mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-08-15 10:47:21 +00:00
Compare commits
239 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0971f8ffa7 | ||
|
|
9739f94428 | ||
|
|
450f4eaec0 | ||
|
|
b5186fa328 | ||
|
|
526dc8363d | ||
|
|
242d735d8c | ||
|
|
9fedd32e78 | ||
|
|
a146003a6a | ||
|
|
d17df74191 | ||
|
|
d23403383f | ||
|
|
877b36f8a8 | ||
|
|
299820162b | ||
|
|
5d20a59d92 | ||
|
|
c1ab3b5476 | ||
|
|
0076130483 | ||
|
|
a5194b1999 | ||
|
|
b53b1f083f | ||
|
|
994a36001f | ||
|
|
26b91e3f66 | ||
|
|
f42aedd485 | ||
|
|
a73818a615 | ||
|
|
7c22e9fe69 | ||
|
|
c58e91b956 | ||
|
|
a61059242c | ||
|
|
cea9437e66 | ||
|
|
3f052ef1da | ||
|
|
c137a4b06d | ||
|
|
6c46dafc59 | ||
|
|
1207417894 | ||
|
|
7c717c219c | ||
|
|
a9fad53255 | ||
|
|
372cc021bd | ||
|
|
6820774266 | ||
|
|
a0c189a349 | ||
|
|
1060ff16d8 | ||
|
|
1255f48645 | ||
|
|
7ef69208d4 | ||
|
|
5277cca4e1 | ||
|
|
aaf77815ca | ||
|
|
3f488cc091 | ||
|
|
bdbbdfe60e | ||
|
|
d15c2be2d0 | ||
|
|
73994914eb | ||
|
|
63b0dae794 | ||
|
|
003e865ff7 | ||
|
|
e3c03287b7 | ||
|
|
73493c3a23 | ||
|
|
562a3f6208 | ||
|
|
3c5f3d6c37 | ||
|
|
a37d4214c0 | ||
|
|
636d6ce324 | ||
|
|
eef8b362d2 | ||
|
|
4ae586ebaa | ||
|
|
f6e673c2bb | ||
|
|
183a60159c | ||
|
|
1fdf4f371d | ||
|
|
f50c374d04 | ||
|
|
d59fc331f6 | ||
|
|
b83f478294 | ||
|
|
d5636f4a19 | ||
|
|
196bcf39cf | ||
|
|
afec1c3a5b | ||
|
|
6025339b46 | ||
|
|
40c6cc59d3 | ||
|
|
514e568e04 | ||
|
|
2f34b0a5ed | ||
|
|
708c076885 | ||
|
|
f0e0b918af | ||
|
|
e45f5e1122 | ||
|
|
932015668b | ||
|
|
4011bc3fe6 | ||
|
|
1dcccdc434 | ||
|
|
3380ed9360 | ||
|
|
ed9c14e63d | ||
|
|
4762793adc | ||
|
|
7df3946189 | ||
|
|
d5fb75fe43 | ||
|
|
c0b32a5584 | ||
|
|
e2b1276d7b | ||
|
|
f6aea13fae | ||
|
|
646b113c55 | ||
|
|
58074f499f | ||
|
|
1483ffd7ff | ||
|
|
cdaef851a0 | ||
|
|
04068d7117 | ||
|
|
41e8ddea8c | ||
|
|
cc49791997 | ||
|
|
ba4a8050ba | ||
|
|
7c5067a59a | ||
|
|
69eb920173 | ||
|
|
07d761941a | ||
|
|
ca52585759 | ||
|
|
decc9d09f8 | ||
|
|
466a18447c | ||
|
|
27902ee107 | ||
|
|
8a8b806362 | ||
|
|
40bb181c78 | ||
|
|
8099847e29 | ||
|
|
6e8edd95ec | ||
|
|
5112c9598b | ||
|
|
749158c086 | ||
|
|
b3d4024f21 | ||
|
|
c283b66c1d | ||
|
|
3209843318 | ||
|
|
d35e5cc0f7 | ||
|
|
e5eeb592a2 | ||
|
|
3d38f4d17a | ||
|
|
2d91647e0b | ||
|
|
cac0f30673 | ||
|
|
c5ecba3389 | ||
|
|
e0397dff47 | ||
|
|
e08fb2e972 | ||
|
|
c02ae82003 | ||
|
|
e6fc301b0d | ||
|
|
a385ed57fb | ||
|
|
09dafe4b1d | ||
|
|
ba8505c983 | ||
|
|
fba359d389 | ||
|
|
c56763b68f | ||
|
|
03c2943545 | ||
|
|
98039b9048 | ||
|
|
5065d3b068 | ||
|
|
6828f7e262 | ||
|
|
a685f3ffbd | ||
|
|
e84156d2e9 | ||
|
|
0ce1c31e1c | ||
|
|
f7e14bb60c | ||
|
|
7b907df816 | ||
|
|
7f5be2829c | ||
|
|
fa5afae783 | ||
|
|
b17507d0fa | ||
|
|
5ed43a3190 | ||
|
|
d2a5144a23 | ||
|
|
fb2031d7ca | ||
|
|
4a42571925 | ||
|
|
38413625c2 | ||
|
|
2d544b7a98 | ||
|
|
b1e5e1840e | ||
|
|
e6f9b4a125 | ||
|
|
5260b2b430 | ||
|
|
0213bc7aef | ||
|
|
ca8b0899d0 | ||
|
|
a1f5c0ab9b | ||
|
|
b0e3f82d27 | ||
|
|
327ab4766b | ||
|
|
3aee8a49c1 | ||
|
|
57de0b7351 | ||
|
|
4df1e310a3 | ||
|
|
802a77238a | ||
|
|
85a7e18fae | ||
|
|
26b1fd3487 | ||
|
|
ae7a363012 | ||
|
|
473362af45 | ||
|
|
cde0d09f0a | ||
|
|
94f093826b | ||
|
|
84f231ea08 | ||
|
|
3d9bb14713 | ||
|
|
6c6cdcf763 | ||
|
|
00c8690e61 | ||
|
|
a0d492cd2d | ||
|
|
ba5ccc1021 | ||
|
|
a4163017d5 | ||
|
|
7f4a5c48ec | ||
|
|
9f7588b245 | ||
|
|
63cbc0c55b | ||
|
|
28eb7a678c | ||
|
|
a95c49915a | ||
|
|
b7adc6add6 | ||
|
|
2aeebf280b | ||
|
|
8ac06979f0 | ||
|
|
2d4f90d5eb | ||
|
|
d7a44a639d | ||
|
|
7bfa05f47d | ||
|
|
c7bbb09195 | ||
|
|
41c9c15ae5 | ||
|
|
964831becf | ||
|
|
e847b92cce | ||
|
|
c2ecd86bb4 | ||
|
|
70d922fad6 | ||
|
|
854bee0519 | ||
|
|
d54d991bdd | ||
|
|
a4b600b97d | ||
|
|
d6cfaa5050 | ||
|
|
00d1aec210 | ||
|
|
f62e3391e1 | ||
|
|
4a406604bd | ||
|
|
55cee46a8d | ||
|
|
607c52c0b7 | ||
|
|
2694321256 | ||
|
|
e8e790102a | ||
|
|
9c5561921b | ||
|
|
78fab9ab0c | ||
|
|
058c750bc6 | ||
|
|
f29da39dff | ||
|
|
624a7c70c9 | ||
|
|
0447ee2d9e | ||
|
|
456fcd38d0 | ||
|
|
a3cab7097a | ||
|
|
848e2f1294 | ||
|
|
ef4dc48ea1 | ||
|
|
2138a33292 | ||
|
|
a59343b987 | ||
|
|
a4ca4bdc99 | ||
|
|
a57b9a9081 | ||
|
|
249a6af456 | ||
|
|
18685d1a94 | ||
|
|
493b090534 | ||
|
|
389adab9b8 | ||
|
|
6360b8c58f | ||
|
|
b0818977cf | ||
|
|
32deb160ef | ||
|
|
2ba5215036 | ||
|
|
73ea4c7b32 | ||
|
|
46c37ff06a | ||
|
|
ff4708b624 | ||
|
|
82b2c84f13 | ||
|
|
48e8b1a9b3 | ||
|
|
1ad7c54ace | ||
|
|
9998c86c1f | ||
|
|
01a707ae0a | ||
|
|
570e8388fc | ||
|
|
9c5b5d4633 | ||
|
|
822bf13626 | ||
|
|
c520de0087 | ||
|
|
0e9076efa3 | ||
|
|
74cd5e3387 | ||
|
|
65c92be09e | ||
|
|
8e776cb3ab | ||
|
|
ab6cd2b751 | ||
|
|
18c3d990fc | ||
|
|
19d5cb8ecb | ||
|
|
2b56a78255 | ||
|
|
176d9df476 | ||
|
|
c94275cb8b | ||
|
|
45dbce3677 | ||
|
|
18745158a3 | ||
|
|
98c6c88391 | ||
|
|
2e4fd72781 | ||
|
|
80ced88ef1 |
10
.vs/VSWorkspaceState.json
Normal file
10
.vs/VSWorkspaceState.json
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"ExpandedNodes": [
|
||||||
|
"",
|
||||||
|
"\\Moose Development",
|
||||||
|
"\\Moose Development\\Moose",
|
||||||
|
"\\Moose Development\\Moose\\Ops"
|
||||||
|
],
|
||||||
|
"SelectedNode": "\\Moose Development\\Moose\\Ops\\Airboss.lua",
|
||||||
|
"PreviewInSolutionExplorer": false
|
||||||
|
}
|
||||||
BIN
.vs/slnx.sqlite
Normal file
BIN
.vs/slnx.sqlite
Normal file
Binary file not shown.
33
Moose Development/Moose/.lua-format
Normal file
33
Moose Development/Moose/.lua-format
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# See https://github.com/Koihik/LuaFormatter
|
||||||
|
# Use '-- LuaFormatter off' and '-- LuaFormatter on' around code blocks to inhibit formatting
|
||||||
|
|
||||||
|
column_limit: 500
|
||||||
|
indent_width: 2
|
||||||
|
use_tab: false
|
||||||
|
continuation_indent_width: 2
|
||||||
|
keep_simple_control_block_one_line: false
|
||||||
|
keep_simple_function_one_line: false
|
||||||
|
align_args: true
|
||||||
|
break_after_functioncall_lp: false
|
||||||
|
break_before_functioncall_rp: false
|
||||||
|
align_parameter: true
|
||||||
|
chop_down_parameter: true
|
||||||
|
break_after_functiondef_lp: false
|
||||||
|
break_before_functiondef_rp: false
|
||||||
|
align_table_field: true
|
||||||
|
break_after_table_lb: true
|
||||||
|
break_before_table_rb: true
|
||||||
|
chop_down_table: true
|
||||||
|
chop_down_kv_table: true
|
||||||
|
column_table_limit: 500
|
||||||
|
table_sep: ','
|
||||||
|
extra_sep_at_table_end: true
|
||||||
|
break_after_operator: true
|
||||||
|
single_quote_to_double_quote: false
|
||||||
|
double_quote_to_single_quote: false
|
||||||
|
spaces_before_call: 1
|
||||||
|
spaces_inside_functiondef_parens: true
|
||||||
|
spaces_inside_functioncall_parens: true
|
||||||
|
spaces_inside_table_braces: true
|
||||||
|
spaces_around_equals_in_field: true
|
||||||
|
line_breaks_after_function_body: 1
|
||||||
17
Moose Development/Moose/.vscode/settings.json
vendored
Normal file
17
Moose Development/Moose/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"Lua.workspace.preloadFileSize": 1000,
|
||||||
|
"Lua.diagnostics.disable": [
|
||||||
|
"undefined-doc-name"
|
||||||
|
],
|
||||||
|
"Lua.diagnostics.globals": [
|
||||||
|
"BASE",
|
||||||
|
"lfs",
|
||||||
|
"__Moose",
|
||||||
|
"trigger",
|
||||||
|
"coord",
|
||||||
|
"missionCommands"
|
||||||
|
],
|
||||||
|
"Lua.completion.displayContext": 5,
|
||||||
|
"Lua.runtime.version": "Lua 5.1",
|
||||||
|
"Lua.completion.callSnippet": "Both"
|
||||||
|
}
|
||||||
@@ -40,8 +40,8 @@
|
|||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB.
|
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
|
||||||
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
-- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
@@ -71,7 +71,7 @@
|
|||||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
|
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
|
||||||
-- * **@{#AI_A2A_CAP.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}.
|
-- * **@{#AI_A2A_CAP.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}.
|
||||||
-- * **@{#AI_A2A_CAP.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task.
|
-- * **@{#AI_A2A_CAP.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task.
|
||||||
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB.
|
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
|
||||||
--
|
--
|
||||||
-- ## 3. Set the Range of Engagement
|
-- ## 3. Set the Range of Engagement
|
||||||
--
|
--
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -42,8 +42,8 @@
|
|||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB.
|
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
|
||||||
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
-- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
@@ -73,7 +73,7 @@
|
|||||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
|
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
|
||||||
-- * **@{#AI_A2A_GCI.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}.
|
-- * **@{#AI_A2A_GCI.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}.
|
||||||
-- * **@{#AI_A2A_GCI.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task.
|
-- * **@{#AI_A2A_GCI.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task.
|
||||||
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB.
|
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
|
||||||
--
|
--
|
||||||
-- ## 3. Set the Range of Engagement
|
-- ## 3. Set the Range of Engagement
|
||||||
--
|
--
|
||||||
|
|||||||
@@ -39,8 +39,8 @@
|
|||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB.
|
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
|
||||||
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
-- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
@@ -68,7 +68,7 @@
|
|||||||
-- * **RTB** ( Group ): Route the AI to the home base.
|
-- * **RTB** ( Group ): Route the AI to the home base.
|
||||||
-- * **Detect** ( Group ): The AI is detecting targets.
|
-- * **Detect** ( Group ): The AI is detecting targets.
|
||||||
-- * **Detected** ( Group ): The AI has detected new targets.
|
-- * **Detected** ( Group ): The AI has detected new targets.
|
||||||
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB.
|
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
|
||||||
--
|
--
|
||||||
-- ## 3. Set or Get the AI controllable
|
-- ## 3. Set or Get the AI controllable
|
||||||
--
|
--
|
||||||
@@ -100,8 +100,8 @@
|
|||||||
-- ## 6. Manage the "out of fuel" in the AI_A2A_PATROL
|
-- ## 6. Manage the "out of fuel" in the AI_A2A_PATROL
|
||||||
--
|
--
|
||||||
-- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base.
|
-- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base.
|
||||||
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel treshold is calculated.
|
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel threshold is calculated.
|
||||||
-- When the fuel treshold is reached, the AI will continue for a given time its patrol task in orbit,
|
-- When the fuel threshold is reached, the AI will continue for a given time its patrol task in orbit,
|
||||||
-- while a new AI is targetted to the AI_A2A_PATROL.
|
-- while a new AI is targetted to the AI_A2A_PATROL.
|
||||||
-- Once the time is finished, the old AI will return to the base.
|
-- Once the time is finished, the old AI will return to the base.
|
||||||
-- Use the method @{#AI_A2A_PATROL.ManageFuel}() to have this proces in place.
|
-- Use the method @{#AI_A2A_PATROL.ManageFuel}() to have this proces in place.
|
||||||
@@ -109,7 +109,7 @@
|
|||||||
-- ## 7. Manage "damage" behaviour of the AI in the AI_A2A_PATROL
|
-- ## 7. Manage "damage" behaviour of the AI in the AI_A2A_PATROL
|
||||||
--
|
--
|
||||||
-- When the AI is damaged, it is required that a new Patrol is started. However, damage cannon be foreseen early on.
|
-- When the AI is damaged, it is required that a new Patrol is started. However, damage cannon be foreseen early on.
|
||||||
-- Therefore, when the damage treshold is reached, the AI will return immediately to the home base (RTB).
|
-- Therefore, when the damage threshold is reached, the AI will return immediately to the home base (RTB).
|
||||||
-- Use the method @{#AI_A2A_PATROL.ManageDamage}() to have this proces in place.
|
-- Use the method @{#AI_A2A_PATROL.ManageDamage}() to have this proces in place.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -42,8 +42,8 @@
|
|||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB.
|
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
|
||||||
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
-- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
|
|||||||
@@ -253,6 +253,9 @@ function AI_AIR:New( AIGroup )
|
|||||||
|
|
||||||
self.IdleCount = 0
|
self.IdleCount = 0
|
||||||
|
|
||||||
|
self.RTBSpeedMaxFactor = 0.6
|
||||||
|
self.RTBSpeedMinFactor = 0.5
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -370,11 +373,11 @@ end
|
|||||||
|
|
||||||
|
|
||||||
--- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base.
|
--- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base.
|
||||||
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel treshold is calculated.
|
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel threshold is calculated.
|
||||||
-- When the fuel treshold is reached, the AI will continue for a given time its patrol task in orbit, while a new AIControllable is targetted to the AI_AIR.
|
-- When the fuel threshold is reached, the AI will continue for a given time its patrol task in orbit, while a new AIControllable is targetted to the AI_AIR.
|
||||||
-- Once the time is finished, the old AI will return to the base.
|
-- Once the time is finished, the old AI will return to the base.
|
||||||
-- @param #AI_AIR self
|
-- @param #AI_AIR self
|
||||||
-- @param #number FuelThresholdPercentage The treshold in percentage (between 0 and 1) when the AIControllable is considered to get out of fuel.
|
-- @param #number FuelThresholdPercentage The threshold in percentage (between 0 and 1) when the AIControllable is considered to get out of fuel.
|
||||||
-- @param #number OutOfFuelOrbitTime The amount of seconds the out of fuel AIControllable will orbit before returning to the base.
|
-- @param #number OutOfFuelOrbitTime The amount of seconds the out of fuel AIControllable will orbit before returning to the base.
|
||||||
-- @return #AI_AIR self
|
-- @return #AI_AIR self
|
||||||
function AI_AIR:SetFuelThreshold( FuelThresholdPercentage, OutOfFuelOrbitTime )
|
function AI_AIR:SetFuelThreshold( FuelThresholdPercentage, OutOfFuelOrbitTime )
|
||||||
@@ -387,14 +390,14 @@ function AI_AIR:SetFuelThreshold( FuelThresholdPercentage, OutOfFuelOrbitTime )
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- When the AI is damaged beyond a certain treshold, it is required that the AI returns to the home base.
|
--- When the AI is damaged beyond a certain threshold, it is required that the AI returns to the home base.
|
||||||
-- However, damage cannot be foreseen early on.
|
-- However, damage cannot be foreseen early on.
|
||||||
-- Therefore, when the damage treshold is reached,
|
-- Therefore, when the damage threshold is reached,
|
||||||
-- the AI will return immediately to the home base (RTB).
|
-- the AI will return immediately to the home base (RTB).
|
||||||
-- Note that for groups, the average damage of the complete group will be calculated.
|
-- Note that for groups, the average damage of the complete group will be calculated.
|
||||||
-- So, in a group of 4 airplanes, 2 lost and 2 with damage 0.2, the damage treshold will be 0.25.
|
-- So, in a group of 4 airplanes, 2 lost and 2 with damage 0.2, the damage threshold will be 0.25.
|
||||||
-- @param #AI_AIR self
|
-- @param #AI_AIR self
|
||||||
-- @param #number PatrolDamageThreshold The treshold in percentage (between 0 and 1) when the AI is considered to be damaged.
|
-- @param #number PatrolDamageThreshold The threshold in percentage (between 0 and 1) when the AI is considered to be damaged.
|
||||||
-- @return #AI_AIR self
|
-- @return #AI_AIR self
|
||||||
function AI_AIR:SetDamageThreshold( PatrolDamageThreshold )
|
function AI_AIR:SetDamageThreshold( PatrolDamageThreshold )
|
||||||
|
|
||||||
@@ -476,7 +479,7 @@ function AI_AIR:onafterStatus()
|
|||||||
|
|
||||||
local Fuel = self.Controllable:GetFuelMin()
|
local Fuel = self.Controllable:GetFuelMin()
|
||||||
|
|
||||||
-- If the fuel in the controllable is below the treshold percentage,
|
-- If the fuel in the controllable is below the threshold percentage,
|
||||||
-- then send for refuel in case of a tanker, otherwise RTB.
|
-- then send for refuel in case of a tanker, otherwise RTB.
|
||||||
if Fuel < self.FuelThresholdPercentage then
|
if Fuel < self.FuelThresholdPercentage then
|
||||||
|
|
||||||
@@ -576,6 +579,19 @@ function AI_AIR.RTBHold( AIGroup, Fsm )
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Set the min and max factors on RTB speed. Use this, if your planes are heading back to base too fast. Default values are 0.5 and 0.6.
|
||||||
|
-- The RTB speed is calculated as the max speed of the unit multiplied by MinFactor (lower bracket) and multiplied by MaxFactor (upper bracket).
|
||||||
|
-- A random value in this bracket is then applied in the waypoint routing generation.
|
||||||
|
-- @param #AI_AIR self
|
||||||
|
-- @param #number MinFactor Lower bracket factor. Defaults to 0.5.
|
||||||
|
-- @param #number MaxFactor Upper bracket factor. Defaults to 0.6.
|
||||||
|
-- @return #AI_AIR self
|
||||||
|
function AI_AIR:SetRTBSpeedFactors(MinFactor,MaxFactor)
|
||||||
|
self.RTBSpeedMaxFactor = MaxFactor or 0.6
|
||||||
|
self.RTBSpeedMinFactor = MinFactor or 0.5
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
--- @param #AI_AIR self
|
--- @param #AI_AIR self
|
||||||
-- @param Wrapper.Group#GROUP AIGroup
|
-- @param Wrapper.Group#GROUP AIGroup
|
||||||
@@ -585,11 +601,13 @@ function AI_AIR:onafterRTB( AIGroup, From, Event, To )
|
|||||||
|
|
||||||
if AIGroup and AIGroup:IsAlive() then
|
if AIGroup and AIGroup:IsAlive() then
|
||||||
|
|
||||||
self:I( "Group " .. AIGroup:GetName() .. " ... RTB! ( " .. self:GetState() .. " )" )
|
self:T( "Group " .. AIGroup:GetName() .. " ... RTB! ( " .. self:GetState() .. " )" )
|
||||||
|
|
||||||
self:ClearTargetDistance()
|
self:ClearTargetDistance()
|
||||||
--AIGroup:ClearTasks()
|
--AIGroup:ClearTasks()
|
||||||
|
|
||||||
|
AIGroup:OptionProhibitAfterburner(true)
|
||||||
|
|
||||||
local EngageRoute = {}
|
local EngageRoute = {}
|
||||||
|
|
||||||
--- Calculate the target route point.
|
--- Calculate the target route point.
|
||||||
@@ -597,12 +615,14 @@ function AI_AIR:onafterRTB( AIGroup, From, Event, To )
|
|||||||
local FromCoord = AIGroup:GetCoordinate()
|
local FromCoord = AIGroup:GetCoordinate()
|
||||||
local ToTargetCoord = self.HomeAirbase:GetCoordinate() -- coordinate is on land height(!)
|
local ToTargetCoord = self.HomeAirbase:GetCoordinate() -- coordinate is on land height(!)
|
||||||
local ToTargetVec3 = ToTargetCoord:GetVec3()
|
local ToTargetVec3 = ToTargetCoord:GetVec3()
|
||||||
ToTargetVec3.y = ToTargetCoord:GetLandHeight()+1000 -- let's set this 1000m/3000 feet above ground
|
ToTargetVec3.y = ToTargetCoord:GetLandHeight()+3000 -- let's set this 1000m/3000 feet above ground
|
||||||
local ToTargetCoord2 = COORDINATE:NewFromVec3( ToTargetVec3 )
|
local ToTargetCoord2 = COORDINATE:NewFromVec3( ToTargetVec3 )
|
||||||
|
|
||||||
if not self.RTBMinSpeed or not self.RTBMaxSpeed then
|
if not self.RTBMinSpeed or not self.RTBMaxSpeed then
|
||||||
local RTBSpeedMax = AIGroup:GetSpeedMax()
|
local RTBSpeedMax = AIGroup:GetSpeedMax()
|
||||||
self:SetRTBSpeed( RTBSpeedMax * 0.5, RTBSpeedMax * 0.6 )
|
local RTBSpeedMaxFactor = self.RTBSpeedMaxFactor or 0.6
|
||||||
|
local RTBSpeedMinFactor = self.RTBSpeedMinFactor or 0.5
|
||||||
|
self:SetRTBSpeed( RTBSpeedMax * RTBSpeedMinFactor, RTBSpeedMax * RTBSpeedMaxFactor)
|
||||||
end
|
end
|
||||||
|
|
||||||
local RTBSpeed = math.random( self.RTBMinSpeed, self.RTBMaxSpeed )
|
local RTBSpeed = math.random( self.RTBMinSpeed, self.RTBMaxSpeed )
|
||||||
|
|||||||
@@ -10,11 +10,11 @@
|
|||||||
-- * Setup (CAS) Controlled Air Support squadrons, to attack closeby enemy ground units near friendly installations.
|
-- * Setup (CAS) Controlled Air Support squadrons, to attack closeby enemy ground units near friendly installations.
|
||||||
-- * Setup (BAI) Battleground Air Interdiction squadrons to attack remote enemy ground units and targets.
|
-- * Setup (BAI) Battleground Air Interdiction squadrons to attack remote enemy ground units and targets.
|
||||||
-- * Define and use a detection network controlled by recce.
|
-- * Define and use a detection network controlled by recce.
|
||||||
-- * Define AIR defense squadrons at airbases, farps and carriers.
|
-- * Define AIR defense squadrons at airbases, FARPs and carriers.
|
||||||
-- * Enable airbases for AIR defenses.
|
-- * Enable airbases for AIR defenses.
|
||||||
-- * Add different planes and helicopter templates to squadrons.
|
-- * Add different planes and helicopter templates to squadrons.
|
||||||
-- * Assign squadrons to execute a specific engagement type depending on threat level of the detected ground enemy unit composition.
|
-- * Assign squadrons to execute a specific engagement type depending on threat level of the detected ground enemy unit composition.
|
||||||
-- * Add multiple squadrons to different airbases, farps or carriers.
|
-- * Add multiple squadrons to different airbases, FARPs or carriers.
|
||||||
-- * Define different ranges to engage upon.
|
-- * Define different ranges to engage upon.
|
||||||
-- * Establish an automatic in air refuel process for planes using refuel tankers.
|
-- * Establish an automatic in air refuel process for planes using refuel tankers.
|
||||||
-- * Setup default settings for all squadrons and AIR defenses.
|
-- * Setup default settings for all squadrons and AIR defenses.
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
--
|
--
|
||||||
-- AI_AIR_DISPATCHER is the main AIR defense class that models the AIR defense system.
|
-- AI_AIR_DISPATCHER is the main AIR defense class that models the AIR defense system.
|
||||||
--
|
--
|
||||||
-- Before you start using the AI_AIR_DISPATCHER, ask youself the following questions.
|
-- Before you start using the AI_AIR_DISPATCHER, ask yourself the following questions.
|
||||||
--
|
--
|
||||||
--
|
--
|
||||||
-- ## 1. Which coalition am I modeling an AIR defense system for? blue or red?
|
-- ## 1. Which coalition am I modeling an AIR defense system for? blue or red?
|
||||||
@@ -128,7 +128,7 @@
|
|||||||
-- Depending on the defense type, different payloads will be needed. See further points on squadron definition.
|
-- Depending on the defense type, different payloads will be needed. See further points on squadron definition.
|
||||||
--
|
--
|
||||||
--
|
--
|
||||||
-- ## 7. Where will the Squadrons be located? On Airbases? On Carrier Ships? On Farps?
|
-- ## 7. Where will the Squadrons be located? On Airbases? On Carrier Ships? On FARPs?
|
||||||
--
|
--
|
||||||
-- Squadrons are placed at the **home base** on an **airfield**, **carrier** or **farp**.
|
-- Squadrons are placed at the **home base** on an **airfield**, **carrier** or **farp**.
|
||||||
-- Carefully plan where each Squadron will be located as part of the defense system required for mission effective defenses.
|
-- Carefully plan where each Squadron will be located as part of the defense system required for mission effective defenses.
|
||||||
@@ -354,7 +354,7 @@ do -- AI_AIR_DISPATCHER
|
|||||||
-- **DetectionSetGroup** is then calling `FilterStart()`, which is starting the dynamic filtering or inclusion of these groups.
|
-- **DetectionSetGroup** is then calling `FilterStart()`, which is starting the dynamic filtering or inclusion of these groups.
|
||||||
-- Note that any destroy or new spawn of a group having a name, starting with the above prefix, will be removed or added to the set.
|
-- Note that any destroy or new spawn of a group having a name, starting with the above prefix, will be removed or added to the set.
|
||||||
--
|
--
|
||||||
-- Then a new detection object is created from the class `DETECTION_AREAS`. A grouping radius of 1000 meters (1km) is choosen.
|
-- Then a new detection object is created from the class `DETECTION_AREAS`. A grouping radius of 1000 meters (1km) is chosen.
|
||||||
--
|
--
|
||||||
-- The `Detection` object is then passed to the @{#AI_AIR_DISPATCHER.New}() method to indicate the reconnaissance network
|
-- The `Detection` object is then passed to the @{#AI_AIR_DISPATCHER.New}() method to indicate the reconnaissance network
|
||||||
-- configuration and setup the AIR defense detection mechanism.
|
-- configuration and setup the AIR defense detection mechanism.
|
||||||
@@ -647,7 +647,7 @@ do -- AI_AIR_DISPATCHER
|
|||||||
-- * @{#AI_AIR_DISPATCHER.SetSquadronLandingAtRunway}() will despawn the returning aircraft directly after landing at the runway.
|
-- * @{#AI_AIR_DISPATCHER.SetSquadronLandingAtRunway}() will despawn the returning aircraft directly after landing at the runway.
|
||||||
-- * @{#AI_AIR_DISPATCHER.SetSquadronLandingAtEngineShutdown}() will despawn the returning aircraft when the aircraft has returned to its parking spot and has turned off its engines.
|
-- * @{#AI_AIR_DISPATCHER.SetSquadronLandingAtEngineShutdown}() will despawn the returning aircraft when the aircraft has returned to its parking spot and has turned off its engines.
|
||||||
--
|
--
|
||||||
-- You can use these methods to minimize the airbase coodination overhead and to increase the airbase efficiency.
|
-- You can use these methods to minimize the airbase coordination overhead and to increase the airbase efficiency.
|
||||||
-- When there are lots of aircraft returning for landing, at the same airbase, the takeoff process will be halted, which can cause a complete failure of the
|
-- When there are lots of aircraft returning for landing, at the same airbase, the takeoff process will be halted, which can cause a complete failure of the
|
||||||
-- A2A defense system, as no new CAP or GCI planes can takeoff.
|
-- A2A defense system, as no new CAP or GCI planes can takeoff.
|
||||||
-- Note that the method @{#AI_AIR_DISPATCHER.SetSquadronLandingNearAirbase}() will only work for returning aircraft, not for damaged or out of fuel aircraft.
|
-- Note that the method @{#AI_AIR_DISPATCHER.SetSquadronLandingNearAirbase}() will only work for returning aircraft, not for damaged or out of fuel aircraft.
|
||||||
@@ -724,13 +724,13 @@ do -- AI_AIR_DISPATCHER
|
|||||||
--
|
--
|
||||||
-- Use the method @{#AI_AIR_DISPATCHER.SetSquadronEngageLimit}() to limit the amount of aircraft that will engage with the enemy, per squadron.
|
-- Use the method @{#AI_AIR_DISPATCHER.SetSquadronEngageLimit}() to limit the amount of aircraft that will engage with the enemy, per squadron.
|
||||||
--
|
--
|
||||||
-- ## 4. Set the **fuel treshold**.
|
-- ## 4. Set the **fuel threshold**.
|
||||||
--
|
--
|
||||||
-- When aircraft get **out of fuel** to a certain %-tage, which is by default **15% (0.15)**, there are two possible actions that can be taken:
|
-- When aircraft get **out of fuel** to a certain %, which is by default **15% (0.15)**, there are two possible actions that can be taken:
|
||||||
-- - The aircraft will go RTB, and will be replaced with a new aircraft if possible.
|
-- - The aircraft will go RTB, and will be replaced with a new aircraft if possible.
|
||||||
-- - The aircraft will refuel at a tanker, if a tanker has been specified for the squadron.
|
-- - The aircraft will refuel at a tanker, if a tanker has been specified for the squadron.
|
||||||
--
|
--
|
||||||
-- Use the method @{#AI_AIR_DISPATCHER.SetSquadronFuelThreshold}() to set the **squadron fuel treshold** of the aircraft for all squadrons.
|
-- Use the method @{#AI_AIR_DISPATCHER.SetSquadronFuelThreshold}() to set the **squadron fuel threshold** of the aircraft for all squadrons.
|
||||||
--
|
--
|
||||||
-- ## 6. Other configuration options
|
-- ## 6. Other configuration options
|
||||||
--
|
--
|
||||||
@@ -786,17 +786,17 @@ do -- AI_AIR_DISPATCHER
|
|||||||
--
|
--
|
||||||
-- Use the method @{#AI_AIR_DISPATCHER.SetDefaultGrouping}() to set the **default grouping** of spawned airplanes for all squadrons.
|
-- Use the method @{#AI_AIR_DISPATCHER.SetDefaultGrouping}() to set the **default grouping** of spawned airplanes for all squadrons.
|
||||||
--
|
--
|
||||||
-- ## 10.5. Default RTB fuel treshold.
|
-- ## 10.5. Default RTB fuel threshold.
|
||||||
--
|
--
|
||||||
-- When an airplane gets **out of fuel** to a certain %-tage, which is **15% (0.15)**, it will go RTB, and will be replaced with a new airplane when applicable.
|
-- When an airplane gets **out of fuel** to a certain %, which is **15% (0.15)**, it will go RTB, and will be replaced with a new airplane when applicable.
|
||||||
--
|
--
|
||||||
-- Use the method @{#AI_AIR_DISPATCHER.SetDefaultFuelThreshold}() to set the **default fuel treshold** of spawned airplanes for all squadrons.
|
-- Use the method @{#AI_AIR_DISPATCHER.SetDefaultFuelThreshold}() to set the **default fuel threshold** of spawned airplanes for all squadrons.
|
||||||
--
|
--
|
||||||
-- ## 10.6. Default RTB damage treshold.
|
-- ## 10.6. Default RTB damage threshold.
|
||||||
--
|
--
|
||||||
-- When an airplane is **damaged** to a certain %-tage, which is **40% (0.40)**, it will go RTB, and will be replaced with a new airplane when applicable.
|
-- When an airplane is **damaged** to a certain %, which is **40% (0.40)**, it will go RTB, and will be replaced with a new airplane when applicable.
|
||||||
--
|
--
|
||||||
-- Use the method @{#AI_AIR_DISPATCHER.SetDefaultDamageThreshold}() to set the **default damage treshold** of spawned airplanes for all squadrons.
|
-- Use the method @{#AI_AIR_DISPATCHER.SetDefaultDamageThreshold}() to set the **default damage threshold** of spawned airplanes for all squadrons.
|
||||||
--
|
--
|
||||||
-- ## 10.7. Default settings for **patrol**.
|
-- ## 10.7. Default settings for **patrol**.
|
||||||
--
|
--
|
||||||
@@ -829,7 +829,7 @@ do -- AI_AIR_DISPATCHER
|
|||||||
--
|
--
|
||||||
-- In the mission editor, setup a group with task Refuelling. A tanker unit of the correct coalition will be automatically selected.
|
-- In the mission editor, setup a group with task Refuelling. A tanker unit of the correct coalition will be automatically selected.
|
||||||
-- Then, use the method @{#AI_AIR_DISPATCHER.SetDefaultTanker}() to set the tanker for the dispatcher.
|
-- Then, use the method @{#AI_AIR_DISPATCHER.SetDefaultTanker}() to set the tanker for the dispatcher.
|
||||||
-- Use the method @{#AI_AIR_DISPATCHER.SetDefaultFuelThreshold}() to set the %-tage left in the defender airplane tanks when a refuel action is needed.
|
-- Use the method @{#AI_AIR_DISPATCHER.SetDefaultFuelThreshold}() to set the % left in the defender airplane tanks when a refuel action is needed.
|
||||||
--
|
--
|
||||||
-- When the tanker specified is alive and in the air, the tanker will be used for refuelling.
|
-- When the tanker specified is alive and in the air, the tanker will be used for refuelling.
|
||||||
--
|
--
|
||||||
@@ -843,7 +843,7 @@ do -- AI_AIR_DISPATCHER
|
|||||||
-- A2ADispatcher:SetSquadronCapInterval("Sochi", 2, 30, 600, 1 )
|
-- A2ADispatcher:SetSquadronCapInterval("Sochi", 2, 30, 600, 1 )
|
||||||
-- A2ADispatcher:SetSquadronGci( "Sochi", 900, 1200 )
|
-- A2ADispatcher:SetSquadronGci( "Sochi", 900, 1200 )
|
||||||
--
|
--
|
||||||
-- -- Set the default tanker for refuelling to "Tanker", when the default fuel treshold has reached 90% fuel left.
|
-- -- Set the default tanker for refuelling to "Tanker", when the default fuel threshold has reached 90% fuel left.
|
||||||
-- A2ADispatcher:SetDefaultFuelThreshold( 0.9 )
|
-- A2ADispatcher:SetDefaultFuelThreshold( 0.9 )
|
||||||
-- A2ADispatcher:SetDefaultTanker( "Tanker" )
|
-- A2ADispatcher:SetDefaultTanker( "Tanker" )
|
||||||
--
|
--
|
||||||
@@ -882,10 +882,7 @@ do -- AI_AIR_DISPATCHER
|
|||||||
-- As a result, the GCI and CAP will stop!
|
-- As a result, the GCI and CAP will stop!
|
||||||
-- However, the squadron will still stay alive. Any airplane that is airborne will continue its operations until all airborne airplanes
|
-- However, the squadron will still stay alive. Any airplane that is airborne will continue its operations until all airborne airplanes
|
||||||
-- of the squadron will be destroyed. This to keep consistency of air operations not to confuse the players.
|
-- of the squadron will be destroyed. This to keep consistency of air operations not to confuse the players.
|
||||||
--
|
--
|
||||||
--
|
|
||||||
--
|
|
||||||
--
|
|
||||||
-- @field #AI_AIR_DISPATCHER
|
-- @field #AI_AIR_DISPATCHER
|
||||||
AI_AIR_DISPATCHER = {
|
AI_AIR_DISPATCHER = {
|
||||||
ClassName = "AI_AIR_DISPATCHER",
|
ClassName = "AI_AIR_DISPATCHER",
|
||||||
@@ -914,10 +911,10 @@ do -- AI_AIR_DISPATCHER
|
|||||||
--- Enumerator for spawns at airbases
|
--- Enumerator for spawns at airbases
|
||||||
-- @type AI_AIR_DISPATCHER.Takeoff
|
-- @type AI_AIR_DISPATCHER.Takeoff
|
||||||
-- @extends Wrapper.Group#GROUP.Takeoff
|
-- @extends Wrapper.Group#GROUP.Takeoff
|
||||||
|
|
||||||
--- @field #AI_AIR_DISPATCHER.Takeoff Takeoff
|
--- @field #AI_AIR_DISPATCHER.Takeoff Takeoff
|
||||||
AI_AIR_DISPATCHER.Takeoff = GROUP.Takeoff
|
AI_AIR_DISPATCHER.Takeoff = GROUP.Takeoff
|
||||||
|
|
||||||
--- Defnes Landing location.
|
--- Defnes Landing location.
|
||||||
-- @field #AI_AIR_DISPATCHER.Landing
|
-- @field #AI_AIR_DISPATCHER.Landing
|
||||||
AI_AIR_DISPATCHER.Landing = {
|
AI_AIR_DISPATCHER.Landing = {
|
||||||
@@ -925,7 +922,7 @@ do -- AI_AIR_DISPATCHER
|
|||||||
AtRunway = 2,
|
AtRunway = 2,
|
||||||
AtEngineShutdown = 3,
|
AtEngineShutdown = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
--- A defense queue item description
|
--- A defense queue item description
|
||||||
-- @type AI_AIR_DISPATCHER.DefenseQueueItem
|
-- @type AI_AIR_DISPATCHER.DefenseQueueItem
|
||||||
-- @field Squadron
|
-- @field Squadron
|
||||||
@@ -936,7 +933,7 @@ do -- AI_AIR_DISPATCHER
|
|||||||
-- @field Functional.Detection#DETECTION_BASE AttackerDetection
|
-- @field Functional.Detection#DETECTION_BASE AttackerDetection
|
||||||
-- @field DefenderGrouping
|
-- @field DefenderGrouping
|
||||||
-- @field #string SquadronName The name of the squadron.
|
-- @field #string SquadronName The name of the squadron.
|
||||||
|
|
||||||
--- Queue of planned defenses to be launched.
|
--- Queue of planned defenses to be launched.
|
||||||
-- This queue exists because defenses must be launched on FARPS, or in the air, or on an airbase, or on carriers.
|
-- This queue exists because defenses must be launched on FARPS, or in the air, or on an airbase, or on carriers.
|
||||||
-- And some of these platforms have very limited amount of "launching" platforms.
|
-- And some of these platforms have very limited amount of "launching" platforms.
|
||||||
@@ -945,40 +942,39 @@ do -- AI_AIR_DISPATCHER
|
|||||||
-- This guarantees that launched defenders are also directly existing ...
|
-- This guarantees that launched defenders are also directly existing ...
|
||||||
-- @type AI_AIR_DISPATCHER.DefenseQueue
|
-- @type AI_AIR_DISPATCHER.DefenseQueue
|
||||||
-- @list<#AI_AIR_DISPATCHER.DefenseQueueItem> DefenseQueueItem A list of all defenses being queued ...
|
-- @list<#AI_AIR_DISPATCHER.DefenseQueueItem> DefenseQueueItem A list of all defenses being queued ...
|
||||||
|
|
||||||
--- @field #AI_AIR_DISPATCHER.DefenseQueue DefenseQueue
|
--- @field #AI_AIR_DISPATCHER.DefenseQueue DefenseQueue
|
||||||
AI_AIR_DISPATCHER.DefenseQueue = {}
|
AI_AIR_DISPATCHER.DefenseQueue = {}
|
||||||
|
|
||||||
|
|
||||||
--- Defense approach types
|
--- Defense approach types
|
||||||
-- @type #AI_AIR_DISPATCHER.DefenseApproach
|
-- @type #AI_AIR_DISPATCHER.DefenseApproach
|
||||||
AI_AIR_DISPATCHER.DefenseApproach = {
|
AI_AIR_DISPATCHER.DefenseApproach = {
|
||||||
Random = 1,
|
Random = 1,
|
||||||
Distance = 2,
|
Distance = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
--- AI_AIR_DISPATCHER constructor.
|
--- AI_AIR_DISPATCHER constructor.
|
||||||
-- This is defining the AIR DISPATCHER for one coaliton.
|
-- This is defining the AIR DISPATCHER for one coalition.
|
||||||
-- The Dispatcher works with a @{Functional.Detection#DETECTION_BASE} object that is taking of the detection of targets using the EWR units.
|
-- The Dispatcher works with a @{Functional.Detection#DETECTION_BASE} object that is taking of the detection of targets using the EWR units.
|
||||||
-- The Detection object is polymorphic, depending on the type of detection object choosen, the detection will work differently.
|
-- The Detection object is polymorphic, depending on the type of detection object chosen, the detection will work differently.
|
||||||
-- @param #AI_AIR_DISPATCHER self
|
-- @param #AI_AIR_DISPATCHER self
|
||||||
-- @param Functional.Detection#DETECTION_BASE Detection The DETECTION object that will detects targets using the the Early Warning Radar network.
|
-- @param Functional.Detection#DETECTION_BASE Detection The DETECTION object that will detects targets using the the Early Warning Radar network.
|
||||||
-- @return #AI_AIR_DISPATCHER self
|
-- @return #AI_AIR_DISPATCHER self
|
||||||
-- @usage
|
-- @usage
|
||||||
--
|
--
|
||||||
-- -- Setup the Detection, using DETECTION_AREAS.
|
-- -- Setup the Detection, using DETECTION_AREAS.
|
||||||
-- -- First define the SET of GROUPs that are defining the EWR network.
|
-- -- First define the SET of GROUPs that are defining the EWR network.
|
||||||
-- -- Here with prefixes DF CCCP AWACS, DF CCCP EWR.
|
-- -- Here with prefixes DF CCCP AWACS, DF CCCP EWR.
|
||||||
-- DetectionSetGroup = SET_GROUP:New()
|
-- DetectionSetGroup = SET_GROUP:New()
|
||||||
-- DetectionSetGroup:FilterPrefixes( { "DF CCCP AWACS", "DF CCCP EWR" } )
|
-- DetectionSetGroup:FilterPrefixes( { "DF CCCP AWACS", "DF CCCP EWR" } )
|
||||||
-- DetectionSetGroup:FilterStart()
|
-- DetectionSetGroup:FilterStart()
|
||||||
--
|
--
|
||||||
-- -- Define the DETECTION_AREAS, using the DetectionSetGroup, with a 30km grouping radius.
|
-- -- Define the DETECTION_AREAS, using the DetectionSetGroup, with a 30km grouping radius.
|
||||||
-- Detection = DETECTION_AREAS:New( DetectionSetGroup, 30000 )
|
-- Detection = DETECTION_AREAS:New( DetectionSetGroup, 30000 )
|
||||||
--
|
--
|
||||||
-- -- Now Setup the AIR dispatcher, and initialize it using the Detection object.
|
-- -- Now Setup the AIR dispatcher, and initialize it using the Detection object.
|
||||||
-- AIRDispatcher = AI_AIR_DISPATCHER:New( Detection ) --
|
-- AIRDispatcher = AI_AIR_DISPATCHER:New( Detection )
|
||||||
--
|
--
|
||||||
function AI_AIR_DISPATCHER:New( Detection )
|
function AI_AIR_DISPATCHER:New( Detection )
|
||||||
|
|
||||||
-- Inherits from DETECTION_MANAGER
|
-- Inherits from DETECTION_MANAGER
|
||||||
@@ -1435,17 +1431,17 @@ do -- AI_AIR_DISPATCHER
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Set the default damage treshold when defenders will RTB.
|
--- Set the default damage threshold when defenders will RTB.
|
||||||
-- The default damage treshold is by default set to 40%, which means that when the airplane is 40% damaged, it will go RTB.
|
-- The default damage threshold is by default set to 40%, which means that when the airplane is 40% damaged, it will go RTB.
|
||||||
-- @param #AI_AIR_DISPATCHER self
|
-- @param #AI_AIR_DISPATCHER self
|
||||||
-- @param #number DamageThreshold A decimal number between 0 and 1, that expresses the %-tage of the damage treshold before going RTB.
|
-- @param #number DamageThreshold A decimal number between 0 and 1, that expresses the % of the damage threshold before going RTB.
|
||||||
-- @return #AI_AIR_DISPATCHER
|
-- @return #AI_AIR_DISPATCHER
|
||||||
-- @usage
|
-- @usage
|
||||||
--
|
--
|
||||||
-- -- Now Setup the AIR dispatcher, and initialize it using the Detection object.
|
-- -- Now Setup the AIR dispatcher, and initialize it using the Detection object.
|
||||||
-- AIRDispatcher = AI_AIR_DISPATCHER:New( Detection )
|
-- AIRDispatcher = AI_AIR_DISPATCHER:New( Detection )
|
||||||
--
|
--
|
||||||
-- -- Now Setup the default damage treshold.
|
-- -- Now Setup the default damage threshold.
|
||||||
-- AIRDispatcher:SetDefaultDamageThreshold( 0.90 ) -- Go RTB when the airplane 90% damaged.
|
-- AIRDispatcher:SetDefaultDamageThreshold( 0.90 ) -- Go RTB when the airplane 90% damaged.
|
||||||
--
|
--
|
||||||
function AI_AIR_DISPATCHER:SetDefaultDamageThreshold( DamageThreshold )
|
function AI_AIR_DISPATCHER:SetDefaultDamageThreshold( DamageThreshold )
|
||||||
@@ -1989,7 +1985,7 @@ do -- AI_AIR_DISPATCHER
|
|||||||
|
|
||||||
--- Defines the default amount of extra planes that will take-off as part of the defense system.
|
--- Defines the default amount of extra planes that will take-off as part of the defense system.
|
||||||
-- @param #AI_AIR_DISPATCHER self
|
-- @param #AI_AIR_DISPATCHER self
|
||||||
-- @param #number Overhead The %-tage of Units that dispatching command will allocate to intercept in surplus of detected amount of units.
|
-- @param #number Overhead The % of Units that dispatching command will allocate to intercept in surplus of detected amount of units.
|
||||||
-- The default overhead is 1, so equal balance. The @{#AI_AIR_DISPATCHER.SetOverhead}() method can be used to tweak the defense strength,
|
-- The default overhead is 1, so equal balance. The @{#AI_AIR_DISPATCHER.SetOverhead}() method can be used to tweak the defense strength,
|
||||||
-- taking into account the plane types of the squadron. For example, a MIG-31 with full long-distance AIR missiles payload, may still be less effective than a F-15C with short missiles...
|
-- taking into account the plane types of the squadron. For example, a MIG-31 with full long-distance AIR missiles payload, may still be less effective than a F-15C with short missiles...
|
||||||
-- So in this case, one may want to use the Overhead method to allocate more defending planes as the amount of detected attacking planes.
|
-- So in this case, one may want to use the Overhead method to allocate more defending planes as the amount of detected attacking planes.
|
||||||
@@ -2028,7 +2024,7 @@ do -- AI_AIR_DISPATCHER
|
|||||||
--- Defines the amount of extra planes that will take-off as part of the defense system.
|
--- Defines the amount of extra planes that will take-off as part of the defense system.
|
||||||
-- @param #AI_AIR_DISPATCHER self
|
-- @param #AI_AIR_DISPATCHER self
|
||||||
-- @param #string SquadronName The name of the squadron.
|
-- @param #string SquadronName The name of the squadron.
|
||||||
-- @param #number Overhead The %-tage of Units that dispatching command will allocate to intercept in surplus of detected amount of units.
|
-- @param #number Overhead The % of Units that dispatching command will allocate to intercept in surplus of detected amount of units.
|
||||||
-- The default overhead is 1, so equal balance. The @{#AI_AIR_DISPATCHER.SetOverhead}() method can be used to tweak the defense strength,
|
-- The default overhead is 1, so equal balance. The @{#AI_AIR_DISPATCHER.SetOverhead}() method can be used to tweak the defense strength,
|
||||||
-- taking into account the plane types of the squadron. For example, a MIG-31 with full long-distance AIR missiles payload, may still be less effective than a F-15C with short missiles...
|
-- taking into account the plane types of the squadron. For example, a MIG-31 with full long-distance AIR missiles payload, may still be less effective than a F-15C with short missiles...
|
||||||
-- So in this case, one may want to use the Overhead method to allocate more defending planes as the amount of detected attacking planes.
|
-- So in this case, one may want to use the Overhead method to allocate more defending planes as the amount of detected attacking planes.
|
||||||
@@ -2068,7 +2064,7 @@ do -- AI_AIR_DISPATCHER
|
|||||||
--- Gets the overhead of planes as part of the defense system, in comparison with the attackers.
|
--- Gets the overhead of planes as part of the defense system, in comparison with the attackers.
|
||||||
-- @param #AI_AIR_DISPATCHER self
|
-- @param #AI_AIR_DISPATCHER self
|
||||||
-- @param #string SquadronName The name of the squadron.
|
-- @param #string SquadronName The name of the squadron.
|
||||||
-- @return #number The %-tage of Units that dispatching command will allocate to intercept in surplus of detected amount of units.
|
-- @return #number The % of Units that dispatching command will allocate to intercept in surplus of detected amount of units.
|
||||||
-- The default overhead is 1, so equal balance. The @{#AI_AIR_DISPATCHER.SetOverhead}() method can be used to tweak the defense strength,
|
-- The default overhead is 1, so equal balance. The @{#AI_AIR_DISPATCHER.SetOverhead}() method can be used to tweak the defense strength,
|
||||||
-- taking into account the plane types of the squadron. For example, a MIG-31 with full long-distance AIR missiles payload, may still be less effective than a F-15C with short missiles...
|
-- taking into account the plane types of the squadron. For example, a MIG-31 with full long-distance AIR missiles payload, may still be less effective than a F-15C with short missiles...
|
||||||
-- So in this case, one may want to use the Overhead method to allocate more defending planes as the amount of detected attacking planes.
|
-- So in this case, one may want to use the Overhead method to allocate more defending planes as the amount of detected attacking planes.
|
||||||
@@ -2674,17 +2670,17 @@ do -- AI_AIR_DISPATCHER
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set the default fuel treshold when defenders will RTB or Refuel in the air.
|
--- Set the default fuel threshold when defenders will RTB or Refuel in the air.
|
||||||
-- The fuel treshold is by default set to 15%, which means that an airplane will stay in the air until 15% of its fuel has been consumed.
|
-- The fuel threshold is by default set to 15%, which means that an airplane will stay in the air until 15% of its fuel has been consumed.
|
||||||
-- @param #AI_AIR_DISPATCHER self
|
-- @param #AI_AIR_DISPATCHER self
|
||||||
-- @param #number FuelThreshold A decimal number between 0 and 1, that expresses the %-tage of the treshold of fuel remaining in the tank when the plane will go RTB or Refuel.
|
-- @param #number FuelThreshold A decimal number between 0 and 1, that expresses the % of the threshold of fuel remaining in the tank when the plane will go RTB or Refuel.
|
||||||
-- @return #AI_AIR_DISPATCHER
|
-- @return #AI_AIR_DISPATCHER
|
||||||
-- @usage
|
-- @usage
|
||||||
--
|
--
|
||||||
-- -- Now Setup the AIR dispatcher, and initialize it using the Detection object.
|
-- -- Now Setup the AIR dispatcher, and initialize it using the Detection object.
|
||||||
-- AIRDispatcher = AI_AIR_DISPATCHER:New( Detection )
|
-- AIRDispatcher = AI_AIR_DISPATCHER:New( Detection )
|
||||||
--
|
--
|
||||||
-- -- Now Setup the default fuel treshold.
|
-- -- Now Setup the default fuel threshold.
|
||||||
-- AIRDispatcher:SetDefaultFuelThreshold( 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
|
-- AIRDispatcher:SetDefaultFuelThreshold( 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
|
||||||
--
|
--
|
||||||
function AI_AIR_DISPATCHER:SetDefaultFuelThreshold( FuelThreshold )
|
function AI_AIR_DISPATCHER:SetDefaultFuelThreshold( FuelThreshold )
|
||||||
@@ -2695,18 +2691,18 @@ do -- AI_AIR_DISPATCHER
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Set the fuel treshold for the squadron when defenders will RTB or Refuel in the air.
|
--- Set the fuel threshold for the squadron when defenders will RTB or Refuel in the air.
|
||||||
-- The fuel treshold is by default set to 15%, which means that an airplane will stay in the air until 15% of its fuel has been consumed.
|
-- The fuel threshold is by default set to 15%, which means that an airplane will stay in the air until 15% of its fuel has been consumed.
|
||||||
-- @param #AI_AIR_DISPATCHER self
|
-- @param #AI_AIR_DISPATCHER self
|
||||||
-- @param #string SquadronName The name of the squadron.
|
-- @param #string SquadronName The name of the squadron.
|
||||||
-- @param #number FuelThreshold A decimal number between 0 and 1, that expresses the %-tage of the treshold of fuel remaining in the tank when the plane will go RTB or Refuel.
|
-- @param #number FuelThreshold A decimal number between 0 and 1, that expresses the % of the threshold of fuel remaining in the tank when the plane will go RTB or Refuel.
|
||||||
-- @return #AI_AIR_DISPATCHER
|
-- @return #AI_AIR_DISPATCHER
|
||||||
-- @usage
|
-- @usage
|
||||||
--
|
--
|
||||||
-- -- Now Setup the AIR dispatcher, and initialize it using the Detection object.
|
-- -- Now Setup the AIR dispatcher, and initialize it using the Detection object.
|
||||||
-- AIRDispatcher = AI_AIR_DISPATCHER:New( Detection )
|
-- AIRDispatcher = AI_AIR_DISPATCHER:New( Detection )
|
||||||
--
|
--
|
||||||
-- -- Now Setup the default fuel treshold.
|
-- -- Now Setup the default fuel threshold.
|
||||||
-- AIRDispatcher:SetSquadronRefuelThreshold( "SquadronName", 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
|
-- AIRDispatcher:SetSquadronRefuelThreshold( "SquadronName", 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
|
||||||
--
|
--
|
||||||
function AI_AIR_DISPATCHER:SetSquadronFuelThreshold( SquadronName, FuelThreshold )
|
function AI_AIR_DISPATCHER:SetSquadronFuelThreshold( SquadronName, FuelThreshold )
|
||||||
@@ -2726,7 +2722,7 @@ do -- AI_AIR_DISPATCHER
|
|||||||
-- -- Now Setup the AIR dispatcher, and initialize it using the Detection object.
|
-- -- Now Setup the AIR dispatcher, and initialize it using the Detection object.
|
||||||
-- AIRDispatcher = AI_AIR_DISPATCHER:New( Detection )
|
-- AIRDispatcher = AI_AIR_DISPATCHER:New( Detection )
|
||||||
--
|
--
|
||||||
-- -- Now Setup the default fuel treshold.
|
-- -- Now Setup the default fuel threshold.
|
||||||
-- AIRDispatcher:SetDefaultFuelThreshold( 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
|
-- AIRDispatcher:SetDefaultFuelThreshold( 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
|
||||||
--
|
--
|
||||||
-- -- Now Setup the default tanker.
|
-- -- Now Setup the default tanker.
|
||||||
@@ -2749,7 +2745,7 @@ do -- AI_AIR_DISPATCHER
|
|||||||
-- -- Now Setup the AIR dispatcher, and initialize it using the Detection object.
|
-- -- Now Setup the AIR dispatcher, and initialize it using the Detection object.
|
||||||
-- AIRDispatcher = AI_AIR_DISPATCHER:New( Detection )
|
-- AIRDispatcher = AI_AIR_DISPATCHER:New( Detection )
|
||||||
--
|
--
|
||||||
-- -- Now Setup the squadron fuel treshold.
|
-- -- Now Setup the squadron fuel threshold.
|
||||||
-- AIRDispatcher:SetSquadronRefuelThreshold( "SquadronName", 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
|
-- AIRDispatcher:SetSquadronRefuelThreshold( "SquadronName", 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
|
||||||
--
|
--
|
||||||
-- -- Now Setup the squadron tanker.
|
-- -- Now Setup the squadron tanker.
|
||||||
@@ -2847,7 +2843,7 @@ do -- AI_AIR_DISPATCHER
|
|||||||
-- @param #AI_AIR_DISPATCHER self
|
-- @param #AI_AIR_DISPATCHER self
|
||||||
function AI_AIR_DISPATCHER:CountDefendersEngaged( AttackerDetection, AttackerCount )
|
function AI_AIR_DISPATCHER:CountDefendersEngaged( AttackerDetection, AttackerCount )
|
||||||
|
|
||||||
-- First, count the active AIGroups Units, targetting the DetectedSet
|
-- First, count the active AIGroups Units, targeting the DetectedSet
|
||||||
local DefendersEngaged = 0
|
local DefendersEngaged = 0
|
||||||
local DefendersTotal = 0
|
local DefendersTotal = 0
|
||||||
|
|
||||||
|
|||||||
@@ -42,8 +42,8 @@
|
|||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB.
|
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
|
||||||
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
-- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
@@ -533,6 +533,10 @@ function AI_AIR_ENGAGE:onafterEngage( DefenderGroup, From, Event, To, AttackSetU
|
|||||||
DefenderCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude.
|
DefenderCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude.
|
||||||
|
|
||||||
local TargetCoord = AttackSetUnit:GetFirst():GetPointVec3()
|
local TargetCoord = AttackSetUnit:GetFirst():GetPointVec3()
|
||||||
|
if not TargetCoord then
|
||||||
|
self:Return()
|
||||||
|
return
|
||||||
|
end
|
||||||
TargetCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude.
|
TargetCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude.
|
||||||
|
|
||||||
local TargetDistance = DefenderCoord:Get2DDistance( TargetCoord )
|
local TargetDistance = DefenderCoord:Get2DDistance( TargetCoord )
|
||||||
|
|||||||
@@ -39,8 +39,8 @@
|
|||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB.
|
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
|
||||||
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
-- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
|
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
|
||||||
-- * **@{#AI_AIR_PATROL.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}.
|
-- * **@{#AI_AIR_PATROL.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}.
|
||||||
-- * **@{#AI_AIR_PATROL.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task.
|
-- * **@{#AI_AIR_PATROL.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task.
|
||||||
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB.
|
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
|
||||||
--
|
--
|
||||||
-- ## 3. Set the Range of Engagement
|
-- ## 3. Set the Range of Engagement
|
||||||
--
|
--
|
||||||
|
|||||||
@@ -49,7 +49,7 @@
|
|||||||
-- Upon started, The AI will **Route** itself towards the random 3D point within a patrol zone,
|
-- Upon started, The AI will **Route** itself towards the random 3D point within a patrol zone,
|
||||||
-- using a random speed within the given altitude and speed limits.
|
-- using a random speed within the given altitude and speed limits.
|
||||||
-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.
|
-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.
|
||||||
-- This cycle will continue until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB.
|
-- This cycle will continue until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
|
||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
@@ -87,7 +87,7 @@
|
|||||||
-- It will keep patrolling there, until it is notified to RTB or move to another BOMB Zone.
|
-- It will keep patrolling there, until it is notified to RTB or move to another BOMB Zone.
|
||||||
-- It can be notified to go RTB through the **RTB** event.
|
-- It can be notified to go RTB through the **RTB** event.
|
||||||
--
|
--
|
||||||
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
-- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
@@ -117,7 +117,7 @@
|
|||||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
|
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
|
||||||
-- * **@{#AI_BAI_ZONE.Destroy}**: The AI has destroyed a target @{Wrapper.Unit}.
|
-- * **@{#AI_BAI_ZONE.Destroy}**: The AI has destroyed a target @{Wrapper.Unit}.
|
||||||
-- * **@{#AI_BAI_ZONE.Destroyed}**: The AI has destroyed all target @{Wrapper.Unit}s assigned in the BOMB task.
|
-- * **@{#AI_BAI_ZONE.Destroyed}**: The AI has destroyed all target @{Wrapper.Unit}s assigned in the BOMB task.
|
||||||
-- * **Status**: The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB.
|
-- * **Status**: The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
|
||||||
--
|
--
|
||||||
-- ## 3. Modify the Engage Zone behaviour to pinpoint a **map object** or **scenery object**
|
-- ## 3. Modify the Engage Zone behaviour to pinpoint a **map object** or **scenery object**
|
||||||
--
|
--
|
||||||
@@ -515,8 +515,8 @@ function AI_BAI_ZONE:onafterEngage( Controllable, From, Event, To,
|
|||||||
--- Calculate the current route point.
|
--- Calculate the current route point.
|
||||||
local CurrentVec2 = self.Controllable:GetVec2()
|
local CurrentVec2 = self.Controllable:GetVec2()
|
||||||
|
|
||||||
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1).
|
--DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
|
||||||
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
|
local CurrentAltitude = self.Controllable:GetAltitude()
|
||||||
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||||
local ToEngageZoneSpeed = self.PatrolMaxSpeed
|
local ToEngageZoneSpeed = self.PatrolMaxSpeed
|
||||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||||
@@ -602,7 +602,7 @@ function AI_BAI_ZONE:onafterEngage( Controllable, From, Event, To,
|
|||||||
|
|
||||||
self:SetRefreshTimeInterval( 2 )
|
self:SetRefreshTimeInterval( 2 )
|
||||||
self:SetDetectionActivated()
|
self:SetDetectionActivated()
|
||||||
self:__Target( -2 ) -- Start Targetting
|
self:__Target( -2 ) -- Start targeting
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/AIB%20-%20AI%20Balancing)
|
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AIB%20-%20AI%20Balancing)
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
|
|||||||
@@ -4,13 +4,13 @@
|
|||||||
--
|
--
|
||||||
-- * Patrol AI airplanes within a given zone.
|
-- * Patrol AI airplanes within a given zone.
|
||||||
-- * Trigger detected events when enemy airplanes are detected.
|
-- * Trigger detected events when enemy airplanes are detected.
|
||||||
-- * Manage a fuel treshold to RTB on time.
|
-- * Manage a fuel threshold to RTB on time.
|
||||||
-- * Engage the enemy when detected.
|
-- * Engage the enemy when detected.
|
||||||
--
|
--
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/CAP%20-%20Combat%20Air%20Patrol)
|
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/CAP%20-%20Combat%20Air%20Patrol)
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
@@ -65,8 +65,8 @@
|
|||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB.
|
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
|
||||||
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
-- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
@@ -96,7 +96,7 @@
|
|||||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
|
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
|
||||||
-- * **@{#AI_CAP_ZONE.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}.
|
-- * **@{#AI_CAP_ZONE.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}.
|
||||||
-- * **@{#AI_CAP_ZONE.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task.
|
-- * **@{#AI_CAP_ZONE.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task.
|
||||||
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB.
|
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
|
||||||
--
|
--
|
||||||
-- ## 3. Set the Range of Engagement
|
-- ## 3. Set the Range of Engagement
|
||||||
--
|
--
|
||||||
@@ -428,8 +428,10 @@ function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To )
|
|||||||
--- Calculate the current route point.
|
--- Calculate the current route point.
|
||||||
local CurrentVec2 = self.Controllable:GetVec2()
|
local CurrentVec2 = self.Controllable:GetVec2()
|
||||||
|
|
||||||
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1).
|
if not CurrentVec2 then return self end
|
||||||
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
|
|
||||||
|
--DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
|
||||||
|
local CurrentAltitude = self.Controllable:GetAltitude()
|
||||||
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||||
local ToEngageZoneSpeed = self.PatrolMaxSpeed
|
local ToEngageZoneSpeed = self.PatrolMaxSpeed
|
||||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/CAS%20-%20Close%20Air%20Support)
|
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/CAS%20-%20Close%20Air%20Support)
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
@@ -49,7 +49,7 @@
|
|||||||
-- Upon started, The AI will **Route** itself towards the random 3D point within a patrol zone,
|
-- Upon started, The AI will **Route** itself towards the random 3D point within a patrol zone,
|
||||||
-- using a random speed within the given altitude and speed limits.
|
-- using a random speed within the given altitude and speed limits.
|
||||||
-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.
|
-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.
|
||||||
-- This cycle will continue until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB.
|
-- This cycle will continue until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
|
||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
@@ -87,7 +87,7 @@
|
|||||||
-- It will keep patrolling there, until it is notified to RTB or move to another CAS Zone.
|
-- It will keep patrolling there, until it is notified to RTB or move to another CAS Zone.
|
||||||
-- It can be notified to go RTB through the **RTB** event.
|
-- It can be notified to go RTB through the **RTB** event.
|
||||||
--
|
--
|
||||||
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
-- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
@@ -117,7 +117,7 @@
|
|||||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
|
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
|
||||||
-- * **@{#AI_CAS_ZONE.Destroy}**: The AI has destroyed a target @{Wrapper.Unit}.
|
-- * **@{#AI_CAS_ZONE.Destroy}**: The AI has destroyed a target @{Wrapper.Unit}.
|
||||||
-- * **@{#AI_CAS_ZONE.Destroyed}**: The AI has destroyed all target @{Wrapper.Unit}s assigned in the CAS task.
|
-- * **@{#AI_CAS_ZONE.Destroyed}**: The AI has destroyed all target @{Wrapper.Unit}s assigned in the CAS task.
|
||||||
-- * **Status**: The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB.
|
-- * **Status**: The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
@@ -459,8 +459,8 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
|
|||||||
--- Calculate the current route point.
|
--- Calculate the current route point.
|
||||||
local CurrentVec2 = self.Controllable:GetVec2()
|
local CurrentVec2 = self.Controllable:GetVec2()
|
||||||
|
|
||||||
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1).
|
--DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
|
||||||
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
|
local CurrentAltitude = self.Controllable:GetAltitude()
|
||||||
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||||
local ToEngageZoneSpeed = self.PatrolMaxSpeed
|
local ToEngageZoneSpeed = self.PatrolMaxSpeed
|
||||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||||
@@ -520,7 +520,7 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
|
|||||||
|
|
||||||
self:SetRefreshTimeInterval( 2 )
|
self:SetRefreshTimeInterval( 2 )
|
||||||
self:SetDetectionActivated()
|
self:SetDetectionActivated()
|
||||||
self:__Target( -2 ) -- Start Targetting
|
self:__Target( -2 ) -- Start targeting
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -174,8 +174,8 @@ function AI_CARGO_DISPATCHER_HELICOPTER:New( HelicopterSet, CargoSet, PickupZone
|
|||||||
self:SetPickupSpeed( 350, 150 )
|
self:SetPickupSpeed( 350, 150 )
|
||||||
self:SetDeploySpeed( 350, 150 )
|
self:SetDeploySpeed( 350, 150 )
|
||||||
|
|
||||||
self:SetPickupRadius( 0, 0 )
|
self:SetPickupRadius( 40, 12 )
|
||||||
self:SetDeployRadius( 0, 0 )
|
self:SetDeployRadius( 40, 12 )
|
||||||
|
|
||||||
self:SetPickupHeight( 500, 200 )
|
self:SetPickupHeight( 500, 200 )
|
||||||
self:SetDeployHeight( 500, 200 )
|
self:SetDeployHeight( 500, 200 )
|
||||||
@@ -186,6 +186,9 @@ end
|
|||||||
|
|
||||||
function AI_CARGO_DISPATCHER_HELICOPTER:AICargo( Helicopter, CargoSet )
|
function AI_CARGO_DISPATCHER_HELICOPTER:AICargo( Helicopter, CargoSet )
|
||||||
|
|
||||||
return AI_CARGO_HELICOPTER:New( Helicopter, CargoSet )
|
local dispatcher = AI_CARGO_HELICOPTER:New( Helicopter, CargoSet )
|
||||||
|
dispatcher:SetLandingSpeedAndHeight(27, 6)
|
||||||
|
return dispatcher
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- @module AI.AI_Cargo_Dispatcher_Ship
|
-- @module AI.AI_Cargo_Dispatcher_Ship
|
||||||
-- @image AI_Cargo_Dispatching_For_Ship.JPG
|
-- @image AI_Cargo_Dispatcher.JPG
|
||||||
|
|
||||||
--- @type AI_CARGO_DISPATCHER_SHIP
|
--- @type AI_CARGO_DISPATCHER_SHIP
|
||||||
-- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER
|
-- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- @module AI.AI_Cargo_Ship
|
-- @module AI.AI_Cargo_Ship
|
||||||
-- @image AI_Cargo_Dispatching_For_Ship.JPG
|
-- @image AI_Cargo_Dispatcher.JPG
|
||||||
|
|
||||||
--- @type AI_CARGO_SHIP
|
--- @type AI_CARGO_SHIP
|
||||||
-- @extends AI.AI_Cargo#AI_CARGO
|
-- @extends AI.AI_Cargo#AI_CARGO
|
||||||
|
|||||||
@@ -1140,8 +1140,8 @@ end
|
|||||||
-- @param DCS#Vec3 CV2 Vec3
|
-- @param DCS#Vec3 CV2 Vec3
|
||||||
function AI_FORMATION:FollowMe(FollowGroup, ClientUnit, CT1, CV1, CT2, CV2)
|
function AI_FORMATION:FollowMe(FollowGroup, ClientUnit, CT1, CV1, CT2, CV2)
|
||||||
|
|
||||||
if FollowGroup:GetState( FollowGroup, "Mode" ) == self.__Enum.Mode.Formation then
|
if FollowGroup:GetState( FollowGroup, "Mode" ) == self.__Enum.Mode.Formation and not self:Is("Stopped") then
|
||||||
|
|
||||||
self:T({Mode=FollowGroup:GetState( FollowGroup, "Mode" )})
|
self:T({Mode=FollowGroup:GetState( FollowGroup, "Mode" )})
|
||||||
|
|
||||||
FollowGroup:OptionROTEvadeFire()
|
FollowGroup:OptionROTEvadeFire()
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
--
|
--
|
||||||
-- * Patrol AI airplanes within a given zone.
|
-- * Patrol AI airplanes within a given zone.
|
||||||
-- * Trigger detected events when enemy airplanes are detected.
|
-- * Trigger detected events when enemy airplanes are detected.
|
||||||
-- * Manage a fuel treshold to RTB on time.
|
-- * Manage a fuel threshold to RTB on time.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/PAT%20-%20Patrolling)
|
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/PAT%20-%20Patrolling)
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
@@ -72,8 +72,8 @@
|
|||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB.
|
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
|
||||||
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
-- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
@@ -101,7 +101,7 @@
|
|||||||
-- * **RTB** ( Group ): Route the AI to the home base.
|
-- * **RTB** ( Group ): Route the AI to the home base.
|
||||||
-- * **Detect** ( Group ): The AI is detecting targets.
|
-- * **Detect** ( Group ): The AI is detecting targets.
|
||||||
-- * **Detected** ( Group ): The AI has detected new targets.
|
-- * **Detected** ( Group ): The AI has detected new targets.
|
||||||
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB.
|
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
|
||||||
--
|
--
|
||||||
-- ## 3. Set or Get the AI controllable
|
-- ## 3. Set or Get the AI controllable
|
||||||
--
|
--
|
||||||
@@ -133,8 +133,8 @@
|
|||||||
-- ## 6. Manage the "out of fuel" in the AI_PATROL_ZONE
|
-- ## 6. Manage the "out of fuel" in the AI_PATROL_ZONE
|
||||||
--
|
--
|
||||||
-- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base.
|
-- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base.
|
||||||
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel treshold is calculated.
|
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel threshold is calculated.
|
||||||
-- When the fuel treshold is reached, the AI will continue for a given time its patrol task in orbit,
|
-- When the fuel threshold is reached, the AI will continue for a given time its patrol task in orbit,
|
||||||
-- while a new AI is targetted to the AI_PATROL_ZONE.
|
-- while a new AI is targetted to the AI_PATROL_ZONE.
|
||||||
-- Once the time is finished, the old AI will return to the base.
|
-- Once the time is finished, the old AI will return to the base.
|
||||||
-- Use the method @{#AI_PATROL_ZONE.ManageFuel}() to have this proces in place.
|
-- Use the method @{#AI_PATROL_ZONE.ManageFuel}() to have this proces in place.
|
||||||
@@ -142,7 +142,7 @@
|
|||||||
-- ## 7. Manage "damage" behaviour of the AI in the AI_PATROL_ZONE
|
-- ## 7. Manage "damage" behaviour of the AI in the AI_PATROL_ZONE
|
||||||
--
|
--
|
||||||
-- When the AI is damaged, it is required that a new AIControllable is started. However, damage cannon be foreseen early on.
|
-- When the AI is damaged, it is required that a new AIControllable is started. However, damage cannon be foreseen early on.
|
||||||
-- Therefore, when the damage treshold is reached, the AI will return immediately to the home base (RTB).
|
-- Therefore, when the damage threshold is reached, the AI will return immediately to the home base (RTB).
|
||||||
-- Use the method @{#AI_PATROL_ZONE.ManageDamage}() to have this proces in place.
|
-- Use the method @{#AI_PATROL_ZONE.ManageDamage}() to have this proces in place.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
@@ -581,11 +581,11 @@ function AI_PATROL_ZONE:ClearDetectedUnits()
|
|||||||
end
|
end
|
||||||
|
|
||||||
--- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base.
|
--- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base.
|
||||||
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel treshold is calculated.
|
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel threshold is calculated.
|
||||||
-- When the fuel treshold is reached, the AI will continue for a given time its patrol task in orbit, while a new AIControllable is targetted to the AI_PATROL_ZONE.
|
-- When the fuel threshold is reached, the AI will continue for a given time its patrol task in orbit, while a new AIControllable is targetted to the AI_PATROL_ZONE.
|
||||||
-- Once the time is finished, the old AI will return to the base.
|
-- Once the time is finished, the old AI will return to the base.
|
||||||
-- @param #AI_PATROL_ZONE self
|
-- @param #AI_PATROL_ZONE self
|
||||||
-- @param #number PatrolFuelThresholdPercentage The treshold in percentage (between 0 and 1) when the AIControllable is considered to get out of fuel.
|
-- @param #number PatrolFuelThresholdPercentage The threshold in percentage (between 0 and 1) when the AIControllable is considered to get out of fuel.
|
||||||
-- @param #number PatrolOutOfFuelOrbitTime The amount of seconds the out of fuel AIControllable will orbit before returning to the base.
|
-- @param #number PatrolOutOfFuelOrbitTime The amount of seconds the out of fuel AIControllable will orbit before returning to the base.
|
||||||
-- @return #AI_PATROL_ZONE self
|
-- @return #AI_PATROL_ZONE self
|
||||||
function AI_PATROL_ZONE:ManageFuel( PatrolFuelThresholdPercentage, PatrolOutOfFuelOrbitTime )
|
function AI_PATROL_ZONE:ManageFuel( PatrolFuelThresholdPercentage, PatrolOutOfFuelOrbitTime )
|
||||||
@@ -596,14 +596,14 @@ function AI_PATROL_ZONE:ManageFuel( PatrolFuelThresholdPercentage, PatrolOutOfFu
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- When the AI is damaged beyond a certain treshold, it is required that the AI returns to the home base.
|
--- When the AI is damaged beyond a certain threshold, it is required that the AI returns to the home base.
|
||||||
-- However, damage cannot be foreseen early on.
|
-- However, damage cannot be foreseen early on.
|
||||||
-- Therefore, when the damage treshold is reached,
|
-- Therefore, when the damage threshold is reached,
|
||||||
-- the AI will return immediately to the home base (RTB).
|
-- the AI will return immediately to the home base (RTB).
|
||||||
-- Note that for groups, the average damage of the complete group will be calculated.
|
-- Note that for groups, the average damage of the complete group will be calculated.
|
||||||
-- So, in a group of 4 airplanes, 2 lost and 2 with damage 0.2, the damage treshold will be 0.25.
|
-- So, in a group of 4 airplanes, 2 lost and 2 with damage 0.2, the damage threshold will be 0.25.
|
||||||
-- @param #AI_PATROL_ZONE self
|
-- @param #AI_PATROL_ZONE self
|
||||||
-- @param #number PatrolDamageThreshold The treshold in percentage (between 0 and 1) when the AI is considered to be damaged.
|
-- @param #number PatrolDamageThreshold The threshold in percentage (between 0 and 1) when the AI is considered to be damaged.
|
||||||
-- @return #AI_PATROL_ZONE self
|
-- @return #AI_PATROL_ZONE self
|
||||||
function AI_PATROL_ZONE:ManageDamage( PatrolDamageThreshold )
|
function AI_PATROL_ZONE:ManageDamage( PatrolDamageThreshold )
|
||||||
|
|
||||||
@@ -725,8 +725,8 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local life = self.Controllable:GetLife() or 0
|
||||||
if self.Controllable:IsAlive() then
|
if self.Controllable:IsAlive() and life > 1 then
|
||||||
-- Determine if the AIControllable is within the PatrolZone.
|
-- Determine if the AIControllable is within the PatrolZone.
|
||||||
-- If not, make a waypoint within the to that the AIControllable will fly at maximum speed to that point.
|
-- If not, make a waypoint within the to that the AIControllable will fly at maximum speed to that point.
|
||||||
|
|
||||||
@@ -743,8 +743,9 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
|
|||||||
if self.Controllable:InAir() == false then
|
if self.Controllable:InAir() == false then
|
||||||
self:T( "Not in the air, finding route path within PatrolZone" )
|
self:T( "Not in the air, finding route path within PatrolZone" )
|
||||||
local CurrentVec2 = self.Controllable:GetVec2()
|
local CurrentVec2 = self.Controllable:GetVec2()
|
||||||
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1).
|
if not CurrentVec2 then return end
|
||||||
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
|
--Done: Create GetAltitude function for GROUP, and delete GetUnit(1).
|
||||||
|
local CurrentAltitude = self.Controllable:GetAltitude()
|
||||||
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||||
local ToPatrolZoneSpeed = self.PatrolMaxSpeed
|
local ToPatrolZoneSpeed = self.PatrolMaxSpeed
|
||||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||||
@@ -758,8 +759,9 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
|
|||||||
else
|
else
|
||||||
self:T( "In the air, finding route path within PatrolZone" )
|
self:T( "In the air, finding route path within PatrolZone" )
|
||||||
local CurrentVec2 = self.Controllable:GetVec2()
|
local CurrentVec2 = self.Controllable:GetVec2()
|
||||||
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1).
|
if not CurrentVec2 then return end
|
||||||
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
|
--DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
|
||||||
|
local CurrentAltitude = self.Controllable:GetAltitude()
|
||||||
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||||
local ToPatrolZoneSpeed = self.PatrolMaxSpeed
|
local ToPatrolZoneSpeed = self.PatrolMaxSpeed
|
||||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||||
@@ -870,9 +872,10 @@ function AI_PATROL_ZONE:onafterRTB()
|
|||||||
|
|
||||||
--- Calculate the current route point.
|
--- Calculate the current route point.
|
||||||
local CurrentVec2 = self.Controllable:GetVec2()
|
local CurrentVec2 = self.Controllable:GetVec2()
|
||||||
|
if not CurrentVec2 then return end
|
||||||
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1).
|
--DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
|
||||||
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
|
--local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
|
||||||
|
local CurrentAltitude = self.Controllable:GetAltitude()
|
||||||
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||||
local ToPatrolZoneSpeed = self.PatrolMaxSpeed
|
local ToPatrolZoneSpeed = self.PatrolMaxSpeed
|
||||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||||
|
|||||||
@@ -194,7 +194,7 @@
|
|||||||
-- * is of type `Workmaterials`
|
-- * is of type `Workmaterials`
|
||||||
-- * will report when a carrier is within 500 meters
|
-- * will report when a carrier is within 500 meters
|
||||||
-- * will board to carriers when the carrier is within 500 meters from the cargo object
|
-- * will board to carriers when the carrier is within 500 meters from the cargo object
|
||||||
-- * will dissapear when the cargo is within 25 meters from the carrier during boarding
|
-- * will disappear when the cargo is within 25 meters from the carrier during boarding
|
||||||
--
|
--
|
||||||
-- So the overall syntax of the #CARGO naming tag and arguments are:
|
-- So the overall syntax of the #CARGO naming tag and arguments are:
|
||||||
--
|
--
|
||||||
@@ -206,27 +206,29 @@
|
|||||||
-- * **NR=** Provide the maximum range in meters when the cargo units will be boarded within the carrier during boarding.
|
-- * **NR=** Provide the maximum range in meters when the cargo units will be boarded within the carrier during boarding.
|
||||||
-- Note that this option is optional, so can be omitted. The default value of the RR is 10 meters.
|
-- Note that this option is optional, so can be omitted. The default value of the RR is 10 meters.
|
||||||
--
|
--
|
||||||
-- ## 5.2) The \#CARGO tag to create CARGO_CRATE objects:
|
-- ## 5.2) The \#CARGO tag to create CARGO_CRATE or CARGO_SLINGLOAD objects:
|
||||||
--
|
--
|
||||||
-- You can also use the \#CARGO tag on **static** objects, including **static cargo** objects of the mission editor.
|
-- You can also use the \#CARGO tag on **static** objects, including **static cargo** objects of the mission editor.
|
||||||
--
|
--
|
||||||
-- For example, the following #CARGO naming in the **static name** of the object, will create a CARGO_CRATE object when the mission starts.
|
-- For example, the following #CARGO naming in the **static name** of the object, will create a CARGO_CRATE object when the mission starts.
|
||||||
--
|
--
|
||||||
-- `Static #CARGO(T=Workmaterials,RR=500,NR=25)`
|
-- `Static #CARGO(T=Workmaterials,C=CRATE,RR=500,NR=25)`
|
||||||
--
|
--
|
||||||
-- This will create a CARGO_CRATE object:
|
-- This will create a CARGO_CRATE object:
|
||||||
--
|
--
|
||||||
-- * with the group name `Static #CARGO`
|
-- * with the group name `Static #CARGO`
|
||||||
-- * is of type `Workmaterials`
|
-- * is of type `Workmaterials`
|
||||||
|
-- * is of category `CRATE` (as opposed to `SLING`)
|
||||||
-- * will report when a carrier is within 500 meters
|
-- * will report when a carrier is within 500 meters
|
||||||
-- * will board to carriers when the carrier is within 500 meters from the cargo object
|
-- * will board to carriers when the carrier is within 500 meters from the cargo object
|
||||||
-- * will dissapear when the cargo is within 25 meters from the carrier during boarding
|
-- * will disappear when the cargo is within 25 meters from the carrier during boarding
|
||||||
--
|
--
|
||||||
-- So the overall syntax of the #CARGO naming tag and arguments are:
|
-- So the overall syntax of the #CARGO naming tag and arguments are:
|
||||||
--
|
--
|
||||||
-- `StaticName #CARGO(T=CargoTypeName,RR=Range,NR=Range)`
|
-- `StaticName #CARGO(T=CargoTypeName,C=Category,RR=Range,NR=Range)`
|
||||||
--
|
--
|
||||||
-- * **T=** Provide a text that contains the type name of the cargo object. This type name can be used to filter cargo within a SET_CARGO object.
|
-- * **T=** Provide a text that contains the type name of the cargo object. This type name can be used to filter cargo within a SET_CARGO object.
|
||||||
|
-- * **C=** Provide either `CRATE` or `SLING` to have this static created as a CARGO_CRATE or CARGO_SLINGLOAD respectivly.
|
||||||
-- * **RR=** Provide the minimal range in meters when the report to the carrier, and board to the carrier.
|
-- * **RR=** Provide the minimal range in meters when the report to the carrier, and board to the carrier.
|
||||||
-- Note that this option is optional, so can be omitted. The default value of the RR is 250 meters.
|
-- Note that this option is optional, so can be omitted. The default value of the RR is 250 meters.
|
||||||
-- * **NR=** Provide the maximum range in meters when the cargo units will be boarded within the carrier during boarding.
|
-- * **NR=** Provide the maximum range in meters when the cargo units will be boarded within the carrier during boarding.
|
||||||
|
|||||||
943
Moose Development/Moose/Core/Astar.lua
Normal file
943
Moose Development/Moose/Core/Astar.lua
Normal file
@@ -0,0 +1,943 @@
|
|||||||
|
--- **Core** - A* Pathfinding.
|
||||||
|
--
|
||||||
|
-- **Main Features:**
|
||||||
|
--
|
||||||
|
-- * Find path from A to B.
|
||||||
|
-- * Pre-defined as well as custom valid neighbour functions.
|
||||||
|
-- * Pre-defined as well as custom cost functions.
|
||||||
|
-- * Easy rectangular grid setup.
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ### Author: **funkyfranky**
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
-- @module Core.Astar
|
||||||
|
-- @image CORE_Astar.png
|
||||||
|
|
||||||
|
|
||||||
|
--- ASTAR class.
|
||||||
|
-- @type ASTAR
|
||||||
|
-- @field #string ClassName Name of the class.
|
||||||
|
-- @field #boolean Debug Debug mode. Messages to all about status.
|
||||||
|
-- @field #string lid Class id string for output to DCS log file.
|
||||||
|
-- @field #table nodes Table of nodes.
|
||||||
|
-- @field #number counter Node counter.
|
||||||
|
-- @field #number Nnodes Number of nodes.
|
||||||
|
-- @field #number nvalid Number of nvalid calls.
|
||||||
|
-- @field #number nvalidcache Number of cached valid evals.
|
||||||
|
-- @field #number ncost Number of cost evaluations.
|
||||||
|
-- @field #number ncostcache Number of cached cost evals.
|
||||||
|
-- @field #ASTAR.Node startNode Start node.
|
||||||
|
-- @field #ASTAR.Node endNode End node.
|
||||||
|
-- @field Core.Point#COORDINATE startCoord Start coordinate.
|
||||||
|
-- @field Core.Point#COORDINATE endCoord End coordinate.
|
||||||
|
-- @field #function ValidNeighbourFunc Function to check if a node is valid.
|
||||||
|
-- @field #table ValidNeighbourArg Optional arguments passed to the valid neighbour function.
|
||||||
|
-- @field #function CostFunc Function to calculate the heuristic "cost" to go from one node to another.
|
||||||
|
-- @field #table CostArg Optional arguments passed to the cost function.
|
||||||
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
|
--- *When nothing goes right... Go left!*
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- # The ASTAR Concept
|
||||||
|
--
|
||||||
|
-- Pathfinding algorithm.
|
||||||
|
--
|
||||||
|
--
|
||||||
|
-- # Start and Goal
|
||||||
|
--
|
||||||
|
-- The first thing we need to define is obviously the place where we want to start and where we want to go eventually.
|
||||||
|
--
|
||||||
|
-- ## Start
|
||||||
|
--
|
||||||
|
-- The start
|
||||||
|
--
|
||||||
|
-- ## Goal
|
||||||
|
--
|
||||||
|
--
|
||||||
|
-- # Nodes
|
||||||
|
--
|
||||||
|
-- ## Rectangular Grid
|
||||||
|
--
|
||||||
|
-- A rectangular grid can be created using the @{#ASTAR.CreateGrid}(*ValidSurfaceTypes, BoxHY, SpaceX, deltaX, deltaY, MarkGrid*), where
|
||||||
|
--
|
||||||
|
-- * *ValidSurfaceTypes* is a table of valid surface types. By default all surface types are valid.
|
||||||
|
-- * *BoxXY* is the width of the grid perpendicular the the line between start and end node. Default is 40,000 meters (40 km).
|
||||||
|
-- * *SpaceX* is the additional space behind the start and end nodes. Default is 20,000 meters (20 km).
|
||||||
|
-- * *deltaX* is the grid spacing between nodes in the direction of start and end node. Default is 2,000 meters (2 km).
|
||||||
|
-- * *deltaY* is the grid spacing perpendicular to the direction of start and end node. Default is the same as *deltaX*.
|
||||||
|
-- * *MarkGrid* If set to *true*, this places marker on the F10 map on each grid node. Note that this can stall DCS if too many nodes are created.
|
||||||
|
--
|
||||||
|
-- ## Valid Surfaces
|
||||||
|
--
|
||||||
|
-- Certain unit types can only travel on certain surfaces types, for example
|
||||||
|
--
|
||||||
|
-- * Naval units can only travel on water (that also excludes shallow water in DCS currently),
|
||||||
|
-- * Ground units can only traval on land.
|
||||||
|
--
|
||||||
|
-- By restricting the surface type in the grid construction, we also reduce the number of nodes, which makes the algorithm more efficient.
|
||||||
|
--
|
||||||
|
-- ## Box Width (BoxHY)
|
||||||
|
--
|
||||||
|
-- The box width needs to be large enough to capture all paths you want to consider.
|
||||||
|
--
|
||||||
|
-- ## Space in X
|
||||||
|
--
|
||||||
|
-- The space in X value is important if the algorithm needs to to backwards from the start node or needs to extend even further than the end node.
|
||||||
|
--
|
||||||
|
-- ## Grid Spacing
|
||||||
|
--
|
||||||
|
-- The grid spacing is an important factor as it determines the number of nodes and hence the performance of the algorithm. It should be as large as possible.
|
||||||
|
-- However, if the value is too large, the algorithm might fail to get a valid path.
|
||||||
|
--
|
||||||
|
-- A good estimate of the grid spacing is to set it to be smaller (~ half the size) of the smallest gap you need to path.
|
||||||
|
--
|
||||||
|
-- # Valid Neighbours
|
||||||
|
--
|
||||||
|
-- The A* algorithm needs to know if a transition from one node to another is allowed or not. By default, hopping from one node to another is always possible.
|
||||||
|
--
|
||||||
|
-- ## Line of Sight
|
||||||
|
--
|
||||||
|
-- For naval
|
||||||
|
--
|
||||||
|
--
|
||||||
|
-- # Heuristic Cost
|
||||||
|
--
|
||||||
|
-- In order to determine the optimal path, the pathfinding algorithm needs to know, how costly it is to go from one node to another.
|
||||||
|
-- Often, this can simply be determined by the distance between two nodes. Therefore, the default cost function is set to be the 2D distance between two nodes.
|
||||||
|
--
|
||||||
|
--
|
||||||
|
-- # Calculate the Path
|
||||||
|
--
|
||||||
|
-- Finally, we have to calculate the path. This is done by the @{ASTAR.GetPath}(*ExcludeStart, ExcludeEnd*) function. This function returns a table of nodes, which
|
||||||
|
-- describe the optimal path from the start node to the end node.
|
||||||
|
--
|
||||||
|
-- By default, the start and end node are include in the table that is returned.
|
||||||
|
--
|
||||||
|
-- Note that a valid path must not always exist. So you should check if the function returns *nil*.
|
||||||
|
--
|
||||||
|
-- Common reasons that a path cannot be found are:
|
||||||
|
--
|
||||||
|
-- * The grid is too small ==> increase grid size, e.g. *BoxHY* and/or *SpaceX* if you use a rectangular grid.
|
||||||
|
-- * The grid spacing is too large ==> decrease *deltaX* and/or *deltaY*
|
||||||
|
-- * There simply is no valid path ==> you are screwed :(
|
||||||
|
--
|
||||||
|
--
|
||||||
|
-- # Examples
|
||||||
|
--
|
||||||
|
-- ## Strait of Hormuz
|
||||||
|
--
|
||||||
|
-- Carrier Group finds its way through the Stait of Hormuz.
|
||||||
|
--
|
||||||
|
-- ##
|
||||||
|
--
|
||||||
|
--
|
||||||
|
--
|
||||||
|
-- @field #ASTAR
|
||||||
|
ASTAR = {
|
||||||
|
ClassName = "ASTAR",
|
||||||
|
Debug = nil,
|
||||||
|
lid = nil,
|
||||||
|
nodes = {},
|
||||||
|
counter = 1,
|
||||||
|
Nnodes = 0,
|
||||||
|
ncost = 0,
|
||||||
|
ncostcache = 0,
|
||||||
|
nvalid = 0,
|
||||||
|
nvalidcache = 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Node data.
|
||||||
|
-- @type ASTAR.Node
|
||||||
|
-- @field #number id Node id.
|
||||||
|
-- @field Core.Point#COORDINATE coordinate Coordinate of the node.
|
||||||
|
-- @field #number surfacetype Surface type.
|
||||||
|
-- @field #table valid Cached valid/invalid nodes.
|
||||||
|
-- @field #table cost Cached cost.
|
||||||
|
|
||||||
|
--- ASTAR infinity.
|
||||||
|
-- @field #number INF
|
||||||
|
ASTAR.INF=1/0
|
||||||
|
|
||||||
|
--- ASTAR class version.
|
||||||
|
-- @field #string version
|
||||||
|
ASTAR.version="0.4.0"
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- TODO list
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- TODO: Add more valid neighbour functions.
|
||||||
|
-- TODO: Write docs.
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Constructor
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Create a new ASTAR object.
|
||||||
|
-- @param #ASTAR self
|
||||||
|
-- @return #ASTAR self
|
||||||
|
function ASTAR:New()
|
||||||
|
|
||||||
|
-- Inherit everything from INTEL class.
|
||||||
|
local self=BASE:Inherit(self, BASE:New()) --#ASTAR
|
||||||
|
|
||||||
|
self.lid="ASTAR | "
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- User functions
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Set coordinate from where to start.
|
||||||
|
-- @param #ASTAR self
|
||||||
|
-- @param Core.Point#COORDINATE Coordinate Start coordinate.
|
||||||
|
-- @return #ASTAR self
|
||||||
|
function ASTAR:SetStartCoordinate(Coordinate)
|
||||||
|
|
||||||
|
self.startCoord=Coordinate
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set coordinate where you want to go.
|
||||||
|
-- @param #ASTAR self
|
||||||
|
-- @param Core.Point#COORDINATE Coordinate end coordinate.
|
||||||
|
-- @return #ASTAR self
|
||||||
|
function ASTAR:SetEndCoordinate(Coordinate)
|
||||||
|
|
||||||
|
self.endCoord=Coordinate
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Create a node from a given coordinate.
|
||||||
|
-- @param #ASTAR self
|
||||||
|
-- @param Core.Point#COORDINATE Coordinate The coordinate where to create the node.
|
||||||
|
-- @return #ASTAR.Node The node.
|
||||||
|
function ASTAR:GetNodeFromCoordinate(Coordinate)
|
||||||
|
|
||||||
|
local node={} --#ASTAR.Node
|
||||||
|
|
||||||
|
node.coordinate=Coordinate
|
||||||
|
node.surfacetype=Coordinate:GetSurfaceType()
|
||||||
|
node.id=self.counter
|
||||||
|
|
||||||
|
node.valid={}
|
||||||
|
node.cost={}
|
||||||
|
|
||||||
|
self.counter=self.counter+1
|
||||||
|
|
||||||
|
return node
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Add a node to the table of grid nodes.
|
||||||
|
-- @param #ASTAR self
|
||||||
|
-- @param #ASTAR.Node Node The node to be added.
|
||||||
|
-- @return #ASTAR self
|
||||||
|
function ASTAR:AddNode(Node)
|
||||||
|
|
||||||
|
self.nodes[Node.id]=Node
|
||||||
|
self.Nnodes=self.Nnodes+1
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Add a node to the table of grid nodes specifying its coordinate.
|
||||||
|
-- @param #ASTAR self
|
||||||
|
-- @param Core.Point#COORDINATE Coordinate The coordinate where the node is created.
|
||||||
|
-- @return #ASTAR.Node The node.
|
||||||
|
function ASTAR:AddNodeFromCoordinate(Coordinate)
|
||||||
|
|
||||||
|
local node=self:GetNodeFromCoordinate(Coordinate)
|
||||||
|
|
||||||
|
self:AddNode(node)
|
||||||
|
|
||||||
|
return node
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Check if the coordinate of a node has is at a valid surface type.
|
||||||
|
-- @param #ASTAR self
|
||||||
|
-- @param #ASTAR.Node Node The node to be added.
|
||||||
|
-- @param #table SurfaceTypes Surface types, for example `{land.SurfaceType.WATER}`. By default all surface types are valid.
|
||||||
|
-- @return #boolean If true, surface type of node is valid.
|
||||||
|
function ASTAR:CheckValidSurfaceType(Node, SurfaceTypes)
|
||||||
|
|
||||||
|
if SurfaceTypes then
|
||||||
|
|
||||||
|
if type(SurfaceTypes)~="table" then
|
||||||
|
SurfaceTypes={SurfaceTypes}
|
||||||
|
end
|
||||||
|
|
||||||
|
for _,surface in pairs(SurfaceTypes) do
|
||||||
|
if surface==Node.surfacetype then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
|
||||||
|
else
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Add a function to determine if a neighbour of a node is valid.
|
||||||
|
-- @param #ASTAR self
|
||||||
|
-- @param #function NeighbourFunction Function that needs to return *true* for a neighbour to be valid.
|
||||||
|
-- @param ... Condition function arguments if any.
|
||||||
|
-- @return #ASTAR self
|
||||||
|
function ASTAR:SetValidNeighbourFunction(NeighbourFunction, ...)
|
||||||
|
|
||||||
|
self.ValidNeighbourFunc=NeighbourFunction
|
||||||
|
|
||||||
|
self.ValidNeighbourArg={}
|
||||||
|
if arg then
|
||||||
|
self.ValidNeighbourArg=arg
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Set valid neighbours to require line of sight between two nodes.
|
||||||
|
-- @param #ASTAR self
|
||||||
|
-- @param #number CorridorWidth Width of LoS corridor in meters.
|
||||||
|
-- @return #ASTAR self
|
||||||
|
function ASTAR:SetValidNeighbourLoS(CorridorWidth)
|
||||||
|
|
||||||
|
self:SetValidNeighbourFunction(ASTAR.LoS, CorridorWidth)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set valid neighbours to be in a certain distance.
|
||||||
|
-- @param #ASTAR self
|
||||||
|
-- @param #number MaxDistance Max distance between nodes in meters. Default is 2000 m.
|
||||||
|
-- @return #ASTAR self
|
||||||
|
function ASTAR:SetValidNeighbourDistance(MaxDistance)
|
||||||
|
|
||||||
|
self:SetValidNeighbourFunction(ASTAR.DistMax, MaxDistance)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set valid neighbours to be in a certain distance.
|
||||||
|
-- @param #ASTAR self
|
||||||
|
-- @param #number MaxDistance Max distance between nodes in meters. Default is 2000 m.
|
||||||
|
-- @return #ASTAR self
|
||||||
|
function ASTAR:SetValidNeighbourRoad(MaxDistance)
|
||||||
|
|
||||||
|
self:SetValidNeighbourFunction(ASTAR.Road, MaxDistance)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the function which calculates the "cost" to go from one to another node.
|
||||||
|
-- The first to arguments of this function are always the two nodes under consideration. But you can add optional arguments.
|
||||||
|
-- Very often the distance between nodes is a good measure for the cost.
|
||||||
|
-- @param #ASTAR self
|
||||||
|
-- @param #function CostFunction Function that returns the "cost".
|
||||||
|
-- @param ... Condition function arguments if any.
|
||||||
|
-- @return #ASTAR self
|
||||||
|
function ASTAR:SetCostFunction(CostFunction, ...)
|
||||||
|
|
||||||
|
self.CostFunc=CostFunction
|
||||||
|
|
||||||
|
self.CostArg={}
|
||||||
|
if arg then
|
||||||
|
self.CostArg=arg
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set heuristic cost to go from one node to another to be their 2D distance.
|
||||||
|
-- @param #ASTAR self
|
||||||
|
-- @return #ASTAR self
|
||||||
|
function ASTAR:SetCostDist2D()
|
||||||
|
|
||||||
|
self:SetCostFunction(ASTAR.Dist2D)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set heuristic cost to go from one node to another to be their 3D distance.
|
||||||
|
-- @param #ASTAR self
|
||||||
|
-- @return #ASTAR self
|
||||||
|
function ASTAR:SetCostDist3D()
|
||||||
|
|
||||||
|
self:SetCostFunction(ASTAR.Dist3D)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set heuristic cost to go from one node to another to be their 3D distance.
|
||||||
|
-- @param #ASTAR self
|
||||||
|
-- @return #ASTAR self
|
||||||
|
function ASTAR:SetCostRoad()
|
||||||
|
|
||||||
|
self:SetCostFunction(ASTAR)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Grid functions
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Create a rectangular grid of nodes between star and end coordinate.
|
||||||
|
-- The coordinate system is oriented along the line between start and end point.
|
||||||
|
-- @param #ASTAR self
|
||||||
|
-- @param #table ValidSurfaceTypes Valid surface types. By default is all surfaces are allowed.
|
||||||
|
-- @param #number BoxHY Box "height" in meters along the y-coordinate. Default 40000 meters (40 km).
|
||||||
|
-- @param #number SpaceX Additional space in meters before start and after end coordinate. Default 10000 meters (10 km).
|
||||||
|
-- @param #number deltaX Increment in the direction of start to end coordinate in meters. Default 2000 meters.
|
||||||
|
-- @param #number deltaY Increment perpendicular to the direction of start to end coordinate in meters. Default is same as deltaX.
|
||||||
|
-- @param #boolean MarkGrid If true, create F10 map markers at grid nodes.
|
||||||
|
-- @return #ASTAR self
|
||||||
|
function ASTAR:CreateGrid(ValidSurfaceTypes, BoxHY, SpaceX, deltaX, deltaY, MarkGrid)
|
||||||
|
|
||||||
|
-- Note that internally
|
||||||
|
-- x coordinate is z: x-->z Line from start to end
|
||||||
|
-- y coordinate is x: y-->x Perpendicular
|
||||||
|
|
||||||
|
-- Grid length and width.
|
||||||
|
local Dz=SpaceX or 10000
|
||||||
|
local Dx=BoxHY and BoxHY/2 or 20000
|
||||||
|
|
||||||
|
-- Increments.
|
||||||
|
local dz=deltaX or 2000
|
||||||
|
local dx=deltaY or dz
|
||||||
|
|
||||||
|
-- Heading from start to end coordinate.
|
||||||
|
local angle=self.startCoord:HeadingTo(self.endCoord)
|
||||||
|
|
||||||
|
--Distance between start and end.
|
||||||
|
local dist=self.startCoord:Get2DDistance(self.endCoord)+2*Dz
|
||||||
|
|
||||||
|
-- Origin of map. Needed to translate back to wanted position.
|
||||||
|
local co=COORDINATE:New(0, 0, 0)
|
||||||
|
local do1=co:Get2DDistance(self.startCoord)
|
||||||
|
local ho1=co:HeadingTo(self.startCoord)
|
||||||
|
|
||||||
|
-- Start of grid.
|
||||||
|
local xmin=-Dx
|
||||||
|
local zmin=-Dz
|
||||||
|
|
||||||
|
-- Number of grid points.
|
||||||
|
local nz=dist/dz+1
|
||||||
|
local nx=2*Dx/dx+1
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
local text=string.format("Building grid with nx=%d ny=%d => total=%d nodes", nx, nz, nx*nz)
|
||||||
|
self:T(self.lid..text)
|
||||||
|
|
||||||
|
-- Loop over x and z coordinate to create a 2D grid.
|
||||||
|
for i=1,nx do
|
||||||
|
|
||||||
|
-- x coordinate perpendicular to z.
|
||||||
|
local x=xmin+dx*(i-1)
|
||||||
|
|
||||||
|
for j=1,nz do
|
||||||
|
|
||||||
|
-- z coordinate connecting start and end.
|
||||||
|
local z=zmin+dz*(j-1)
|
||||||
|
|
||||||
|
-- Rotate 2D.
|
||||||
|
local vec3=UTILS.Rotate2D({x=x, y=0, z=z}, angle)
|
||||||
|
|
||||||
|
-- Coordinate of the node.
|
||||||
|
local c=COORDINATE:New(vec3.z, vec3.y, vec3.x):Translate(do1, ho1, true)
|
||||||
|
|
||||||
|
-- Create a node at this coordinate.
|
||||||
|
local node=self:GetNodeFromCoordinate(c)
|
||||||
|
|
||||||
|
-- Check if node has valid surface type.
|
||||||
|
if self:CheckValidSurfaceType(node, ValidSurfaceTypes) then
|
||||||
|
|
||||||
|
if MarkGrid then
|
||||||
|
c:MarkToAll(string.format("i=%d, j=%d surface=%d", i, j, node.surfacetype))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Add node to grid.
|
||||||
|
self:AddNode(node)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
local text=string.format("Done building grid!")
|
||||||
|
self:T2(self.lid..text)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Valid neighbour functions
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Function to check if two nodes have line of sight (LoS).
|
||||||
|
-- @param #ASTAR.Node nodeA First node.
|
||||||
|
-- @param #ASTAR.Node nodeB Other node.
|
||||||
|
-- @param #number corridor (Optional) Width of corridor in meters.
|
||||||
|
-- @return #boolean If true, two nodes have LoS.
|
||||||
|
function ASTAR.LoS(nodeA, nodeB, corridor)
|
||||||
|
|
||||||
|
local offset=1
|
||||||
|
|
||||||
|
local dx=corridor and corridor/2 or nil
|
||||||
|
local dy=dx
|
||||||
|
|
||||||
|
local cA=nodeA.coordinate:GetVec3()
|
||||||
|
local cB=nodeB.coordinate:GetVec3()
|
||||||
|
cA.y=offset
|
||||||
|
cB.y=offset
|
||||||
|
|
||||||
|
local los=land.isVisible(cA, cB)
|
||||||
|
|
||||||
|
if los and corridor then
|
||||||
|
|
||||||
|
-- Heading from A to B.
|
||||||
|
local heading=nodeA.coordinate:HeadingTo(nodeB.coordinate)
|
||||||
|
|
||||||
|
local Ap=UTILS.VecTranslate(cA, dx, heading+90)
|
||||||
|
local Bp=UTILS.VecTranslate(cB, dx, heading+90)
|
||||||
|
|
||||||
|
los=land.isVisible(Ap, Bp)
|
||||||
|
|
||||||
|
if los then
|
||||||
|
|
||||||
|
local Am=UTILS.VecTranslate(cA, dx, heading-90)
|
||||||
|
local Bm=UTILS.VecTranslate(cB, dx, heading-90)
|
||||||
|
|
||||||
|
los=land.isVisible(Am, Bm)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return los
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Function to check if two nodes have a road connection.
|
||||||
|
-- @param #ASTAR.Node nodeA First node.
|
||||||
|
-- @param #ASTAR.Node nodeB Other node.
|
||||||
|
-- @return #boolean If true, two nodes are connected via a road.
|
||||||
|
function ASTAR.Road(nodeA, nodeB)
|
||||||
|
|
||||||
|
local path=land.findPathOnRoads("roads", nodeA.coordinate.x, nodeA.coordinate.z, nodeB.coordinate.x, nodeB.coordinate.z)
|
||||||
|
|
||||||
|
if path then
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Function to check if distance between two nodes is less than a threshold distance.
|
||||||
|
-- @param #ASTAR.Node nodeA First node.
|
||||||
|
-- @param #ASTAR.Node nodeB Other node.
|
||||||
|
-- @param #number distmax Max distance in meters. Default is 2000 m.
|
||||||
|
-- @return #boolean If true, distance between the two nodes is below threshold.
|
||||||
|
function ASTAR.DistMax(nodeA, nodeB, distmax)
|
||||||
|
|
||||||
|
distmax=distmax or 2000
|
||||||
|
|
||||||
|
local dist=nodeA.coordinate:Get2DDistance(nodeB.coordinate)
|
||||||
|
|
||||||
|
return dist<=distmax
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Heuristic cost functions
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Heuristic cost is given by the 2D distance between the nodes.
|
||||||
|
-- @param #ASTAR.Node nodeA First node.
|
||||||
|
-- @param #ASTAR.Node nodeB Other node.
|
||||||
|
-- @return #number Distance between the two nodes.
|
||||||
|
function ASTAR.Dist2D(nodeA, nodeB)
|
||||||
|
local dist=nodeA.coordinate:Get2DDistance(nodeB)
|
||||||
|
return dist
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Heuristic cost is given by the 3D distance between the nodes.
|
||||||
|
-- @param #ASTAR.Node nodeA First node.
|
||||||
|
-- @param #ASTAR.Node nodeB Other node.
|
||||||
|
-- @return #number Distance between the two nodes.
|
||||||
|
function ASTAR.Dist3D(nodeA, nodeB)
|
||||||
|
local dist=nodeA.coordinate:Get3DDistance(nodeB.coordinate)
|
||||||
|
return dist
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Heuristic cost is given by the distance between the nodes on road.
|
||||||
|
-- @param #ASTAR.Node nodeA First node.
|
||||||
|
-- @param #ASTAR.Node nodeB Other node.
|
||||||
|
-- @return #number Distance between the two nodes.
|
||||||
|
function ASTAR.DistRoad(nodeA, nodeB)
|
||||||
|
|
||||||
|
-- Get the path.
|
||||||
|
local path=land.findPathOnRoads("roads", nodeA.coordinate.x, nodeA.coordinate.z, nodeB.coordinate.x, nodeB.coordinate.z)
|
||||||
|
|
||||||
|
if path then
|
||||||
|
|
||||||
|
local dist=0
|
||||||
|
|
||||||
|
for i=2,#path do
|
||||||
|
local b=path[i] --DCS#Vec2
|
||||||
|
local a=path[i-1] --DCS#Vec2
|
||||||
|
|
||||||
|
dist=dist+UTILS.VecDist2D(a,b)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return dist
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
return math.huge
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Misc functions
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Find the closest node from a given coordinate.
|
||||||
|
-- @param #ASTAR self
|
||||||
|
-- @param Core.Point#COORDINATE Coordinate.
|
||||||
|
-- @return #ASTAR.Node Cloest node to the coordinate.
|
||||||
|
-- @return #number Distance to closest node in meters.
|
||||||
|
function ASTAR:FindClosestNode(Coordinate)
|
||||||
|
|
||||||
|
local distMin=math.huge
|
||||||
|
local closeNode=nil
|
||||||
|
|
||||||
|
for _,_node in pairs(self.nodes) do
|
||||||
|
local node=_node --#ASTAR.Node
|
||||||
|
|
||||||
|
local dist=node.coordinate:Get2DDistance(Coordinate)
|
||||||
|
|
||||||
|
if dist<distMin then
|
||||||
|
distMin=dist
|
||||||
|
closeNode=node
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return closeNode, distMin
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Find the start node.
|
||||||
|
-- @param #ASTAR self
|
||||||
|
-- @param #ASTAR.Node Node The node to be added to the nodes table.
|
||||||
|
-- @return #ASTAR self
|
||||||
|
function ASTAR:FindStartNode()
|
||||||
|
|
||||||
|
local node, dist=self:FindClosestNode(self.startCoord)
|
||||||
|
|
||||||
|
self.startNode=node
|
||||||
|
|
||||||
|
if dist>1000 then
|
||||||
|
self:T(self.lid.."Adding start node to node grid!")
|
||||||
|
self:AddNode(node)
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Add a node.
|
||||||
|
-- @param #ASTAR self
|
||||||
|
-- @param #ASTAR.Node Node The node to be added to the nodes table.
|
||||||
|
-- @return #ASTAR self
|
||||||
|
function ASTAR:FindEndNode()
|
||||||
|
|
||||||
|
local node, dist=self:FindClosestNode(self.endCoord)
|
||||||
|
|
||||||
|
self.endNode=node
|
||||||
|
|
||||||
|
if dist>1000 then
|
||||||
|
self:T(self.lid.."Adding end node to node grid!")
|
||||||
|
self:AddNode(node)
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Main A* pathfinding function
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- A* pathfinding function. This seaches the path along nodes between start and end nodes/coordinates.
|
||||||
|
-- @param #ASTAR self
|
||||||
|
-- @param #boolean ExcludeStartNode If *true*, do not include start node in found path. Default is to include it.
|
||||||
|
-- @param #boolean ExcludeEndNode If *true*, do not include end node in found path. Default is to include it.
|
||||||
|
-- @return #table Table of nodes from start to finish.
|
||||||
|
function ASTAR:GetPath(ExcludeStartNode, ExcludeEndNode)
|
||||||
|
|
||||||
|
self:FindStartNode()
|
||||||
|
self:FindEndNode()
|
||||||
|
|
||||||
|
local nodes=self.nodes
|
||||||
|
local start=self.startNode
|
||||||
|
local goal=self.endNode
|
||||||
|
|
||||||
|
-- Sets.
|
||||||
|
local openset = {}
|
||||||
|
local closedset = {}
|
||||||
|
local came_from = {}
|
||||||
|
local g_score = {}
|
||||||
|
local f_score = {}
|
||||||
|
|
||||||
|
openset[start.id]=true
|
||||||
|
local Nopen=1
|
||||||
|
|
||||||
|
-- Initial scores.
|
||||||
|
g_score[start.id]=0
|
||||||
|
f_score[start.id]=g_score[start.id]+self:_HeuristicCost(start, goal)
|
||||||
|
|
||||||
|
-- Set start time.
|
||||||
|
local T0=timer.getAbsTime()
|
||||||
|
|
||||||
|
-- Debug message.
|
||||||
|
local text=string.format("Starting A* pathfinding with %d Nodes", self.Nnodes)
|
||||||
|
self:T(self.lid..text)
|
||||||
|
|
||||||
|
local Tstart=UTILS.GetOSTime()
|
||||||
|
|
||||||
|
-- Loop while we still have an open set.
|
||||||
|
while Nopen > 0 do
|
||||||
|
|
||||||
|
-- Get current node.
|
||||||
|
local current=self:_LowestFscore(openset, f_score)
|
||||||
|
|
||||||
|
-- Check if we are at the end node.
|
||||||
|
if current.id==goal.id then
|
||||||
|
|
||||||
|
local path=self:_UnwindPath({}, came_from, goal)
|
||||||
|
|
||||||
|
if not ExcludeEndNode then
|
||||||
|
table.insert(path, goal)
|
||||||
|
end
|
||||||
|
|
||||||
|
if ExcludeStartNode then
|
||||||
|
table.remove(path, 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
local Tstop=UTILS.GetOSTime()
|
||||||
|
|
||||||
|
local dT=nil
|
||||||
|
if Tstart and Tstop then
|
||||||
|
dT=Tstop-Tstart
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Debug message.
|
||||||
|
local text=string.format("Found path with %d nodes (%d total)", #path, self.Nnodes)
|
||||||
|
if dT then
|
||||||
|
text=text..string.format(", OS Time %.6f sec", dT)
|
||||||
|
end
|
||||||
|
text=text..string.format(", Nvalid=%d [%d cached]", self.nvalid, self.nvalidcache)
|
||||||
|
text=text..string.format(", Ncost=%d [%d cached]", self.ncost, self.ncostcache)
|
||||||
|
self:T(self.lid..text)
|
||||||
|
|
||||||
|
return path
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Move Node from open to closed set.
|
||||||
|
openset[current.id]=nil
|
||||||
|
Nopen=Nopen-1
|
||||||
|
closedset[current.id]=true
|
||||||
|
|
||||||
|
-- Get neighbour nodes.
|
||||||
|
local neighbors=self:_NeighbourNodes(current, nodes)
|
||||||
|
|
||||||
|
-- Loop over neighbours.
|
||||||
|
for _,neighbor in pairs(neighbors) do
|
||||||
|
|
||||||
|
if self:_NotIn(closedset, neighbor.id) then
|
||||||
|
|
||||||
|
local tentative_g_score=g_score[current.id]+self:_DistNodes(current, neighbor)
|
||||||
|
|
||||||
|
if self:_NotIn(openset, neighbor.id) or tentative_g_score < g_score[neighbor.id] then
|
||||||
|
|
||||||
|
came_from[neighbor]=current
|
||||||
|
|
||||||
|
g_score[neighbor.id]=tentative_g_score
|
||||||
|
f_score[neighbor.id]=g_score[neighbor.id]+self:_HeuristicCost(neighbor, goal)
|
||||||
|
|
||||||
|
if self:_NotIn(openset, neighbor.id) then
|
||||||
|
-- Add to open set.
|
||||||
|
openset[neighbor.id]=true
|
||||||
|
Nopen=Nopen+1
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Debug message.
|
||||||
|
local text=string.format("WARNING: Could NOT find valid path!")
|
||||||
|
self:E(self.lid..text)
|
||||||
|
MESSAGE:New(text, 60, "ASTAR"):ToAllIf(self.Debug)
|
||||||
|
|
||||||
|
return nil -- no valid path
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- A* pathfinding helper functions
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Heuristic "cost" function to go from node A to node B. Default is the distance between the nodes.
|
||||||
|
-- @param #ASTAR self
|
||||||
|
-- @param #ASTAR.Node nodeA Node A.
|
||||||
|
-- @param #ASTAR.Node nodeB Node B.
|
||||||
|
-- @return #number "Cost" to go from node A to node B.
|
||||||
|
function ASTAR:_HeuristicCost(nodeA, nodeB)
|
||||||
|
|
||||||
|
-- Counter.
|
||||||
|
self.ncost=self.ncost+1
|
||||||
|
|
||||||
|
-- Get chached cost if available.
|
||||||
|
local cost=nodeA.cost[nodeB.id]
|
||||||
|
if cost~=nil then
|
||||||
|
self.ncostcache=self.ncostcache+1
|
||||||
|
return cost
|
||||||
|
end
|
||||||
|
|
||||||
|
local cost=nil
|
||||||
|
if self.CostFunc then
|
||||||
|
cost=self.CostFunc(nodeA, nodeB, unpack(self.CostArg))
|
||||||
|
else
|
||||||
|
cost=self:_DistNodes(nodeA, nodeB)
|
||||||
|
end
|
||||||
|
|
||||||
|
nodeA.cost[nodeB.id]=cost
|
||||||
|
nodeB.cost[nodeA.id]=cost -- Symmetric problem.
|
||||||
|
|
||||||
|
return cost
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Check if going from a node to a neighbour is possible.
|
||||||
|
-- @param #ASTAR self
|
||||||
|
-- @param #ASTAR.Node node A node.
|
||||||
|
-- @param #ASTAR.Node neighbor Neighbour node.
|
||||||
|
-- @return #boolean If true, transition between nodes is possible.
|
||||||
|
function ASTAR:_IsValidNeighbour(node, neighbor)
|
||||||
|
|
||||||
|
-- Counter.
|
||||||
|
self.nvalid=self.nvalid+1
|
||||||
|
|
||||||
|
local valid=node.valid[neighbor.id]
|
||||||
|
if valid~=nil then
|
||||||
|
--env.info(string.format("Node %d has valid=%s neighbour %d", node.id, tostring(valid), neighbor.id))
|
||||||
|
self.nvalidcache=self.nvalidcache+1
|
||||||
|
return valid
|
||||||
|
end
|
||||||
|
|
||||||
|
local valid=nil
|
||||||
|
if self.ValidNeighbourFunc then
|
||||||
|
valid=self.ValidNeighbourFunc(node, neighbor, unpack(self.ValidNeighbourArg))
|
||||||
|
else
|
||||||
|
valid=true
|
||||||
|
end
|
||||||
|
|
||||||
|
node.valid[neighbor.id]=valid
|
||||||
|
neighbor.valid[node.id]=valid -- Symmetric problem.
|
||||||
|
|
||||||
|
return valid
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Calculate 2D distance between two nodes.
|
||||||
|
-- @param #ASTAR self
|
||||||
|
-- @param #ASTAR.Node nodeA Node A.
|
||||||
|
-- @param #ASTAR.Node nodeB Node B.
|
||||||
|
-- @return #number Distance between nodes in meters.
|
||||||
|
function ASTAR:_DistNodes(nodeA, nodeB)
|
||||||
|
return nodeA.coordinate:Get2DDistance(nodeB.coordinate)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Function that calculates the lowest F score.
|
||||||
|
-- @param #ASTAR self
|
||||||
|
-- @param #table set The set of nodes IDs.
|
||||||
|
-- @param #number f_score F score.
|
||||||
|
-- @return #ASTAR.Node Best node.
|
||||||
|
function ASTAR:_LowestFscore(set, f_score)
|
||||||
|
|
||||||
|
local lowest, bestNode = ASTAR.INF, nil
|
||||||
|
|
||||||
|
for nid,node in pairs(set) do
|
||||||
|
|
||||||
|
local score=f_score[nid]
|
||||||
|
|
||||||
|
if score<lowest then
|
||||||
|
lowest, bestNode = score, nid
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return self.nodes[bestNode]
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Function to get valid neighbours of a node.
|
||||||
|
-- @param #ASTAR self
|
||||||
|
-- @param #ASTAR.Node theNode The node.
|
||||||
|
-- @param #table nodes Possible neighbours.
|
||||||
|
-- @param #table Valid neighbour nodes.
|
||||||
|
function ASTAR:_NeighbourNodes(theNode, nodes)
|
||||||
|
|
||||||
|
local neighbors = {}
|
||||||
|
|
||||||
|
for _,node in pairs(nodes) do
|
||||||
|
|
||||||
|
if theNode.id~=node.id then
|
||||||
|
|
||||||
|
local isvalid=self:_IsValidNeighbour(theNode, node)
|
||||||
|
|
||||||
|
if isvalid then
|
||||||
|
table.insert(neighbors, node)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return neighbors
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Function to check if a node is not in a set.
|
||||||
|
-- @param #ASTAR self
|
||||||
|
-- @param #table set Set of nodes.
|
||||||
|
-- @param #ASTAR.Node theNode The node to check.
|
||||||
|
-- @return #boolean If true, the node is not in the set.
|
||||||
|
function ASTAR:_NotIn(set, theNode)
|
||||||
|
return set[theNode]==nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Unwind path function.
|
||||||
|
-- @param #ASTAR self
|
||||||
|
-- @param #table flat_path Flat path.
|
||||||
|
-- @param #table map Map.
|
||||||
|
-- @param #ASTAR.Node current_node The current node.
|
||||||
|
-- @return #table Unwinded path.
|
||||||
|
function ASTAR:_UnwindPath( flat_path, map, current_node )
|
||||||
|
|
||||||
|
if map [current_node] then
|
||||||
|
table.insert (flat_path, 1, map[current_node])
|
||||||
|
return self:_UnwindPath(flat_path, map, map[current_node])
|
||||||
|
else
|
||||||
|
return flat_path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -16,20 +16,20 @@
|
|||||||
--- *In order for the light to shine so brightly, the darkness must be present.* -- Francis Bacon
|
--- *In order for the light to shine so brightly, the darkness must be present.* -- Francis Bacon
|
||||||
--
|
--
|
||||||
-- After attaching a @{#BEACON} to your @{Wrapper.Positionable#POSITIONABLE}, you need to select the right function to activate the kind of beacon you want.
|
-- After attaching a @{#BEACON} to your @{Wrapper.Positionable#POSITIONABLE}, you need to select the right function to activate the kind of beacon you want.
|
||||||
-- There are two types of BEACONs available : the AA TACAN Beacon and the general purpose Radio Beacon.
|
-- There are two types of BEACONs available : the (aircraft) TACAN Beacon and the general purpose Radio Beacon.
|
||||||
-- Note that in both case, you can set an optional parameter : the `BeaconDuration`. This can be very usefull to simulate the battery time if your BEACON is
|
-- Note that in both case, you can set an optional parameter : the `BeaconDuration`. This can be very usefull to simulate the battery time if your BEACON is
|
||||||
-- attach to a cargo crate, for exemple.
|
-- attach to a cargo crate, for exemple.
|
||||||
--
|
--
|
||||||
-- ## AA TACAN Beacon usage
|
-- ## Aircraft TACAN Beacon usage
|
||||||
--
|
--
|
||||||
-- This beacon only works with airborne @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}. Use @{#BEACON:AATACAN}() to set the beacon parameters and start the beacon.
|
-- This beacon only works with airborne @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}. Use @{#BEACON.ActivateTACAN}() to set the beacon parameters and start the beacon.
|
||||||
-- Use @#BEACON:StopAATACAN}() to stop it.
|
-- Use @{#BEACON.StopRadioBeacon}() to stop it.
|
||||||
--
|
--
|
||||||
-- ## General Purpose Radio Beacon usage
|
-- ## General Purpose Radio Beacon usage
|
||||||
--
|
--
|
||||||
-- This beacon will work with any @{Wrapper.Positionable#POSITIONABLE}, but **it won't follow the @{Wrapper.Positionable#POSITIONABLE}** ! This means that you should only use it with
|
-- This beacon will work with any @{Wrapper.Positionable#POSITIONABLE}, but **it won't follow the @{Wrapper.Positionable#POSITIONABLE}** ! This means that you should only use it with
|
||||||
-- @{Wrapper.Positionable#POSITIONABLE} that don't move, or move very slowly. Use @{#BEACON:RadioBeacon}() to set the beacon parameters and start the beacon.
|
-- @{Wrapper.Positionable#POSITIONABLE} that don't move, or move very slowly. Use @{#BEACON.RadioBeacon}() to set the beacon parameters and start the beacon.
|
||||||
-- Use @{#BEACON:StopRadioBeacon}() to stop it.
|
-- Use @{#BEACON.StopRadioBeacon}() to stop it.
|
||||||
--
|
--
|
||||||
-- @type BEACON
|
-- @type BEACON
|
||||||
-- @field #string ClassName Name of the class "BEACON".
|
-- @field #string ClassName Name of the class "BEACON".
|
||||||
@@ -97,11 +97,11 @@ BEACON.Type={
|
|||||||
-- @field #number TACAN_TANKER_Y TACtical Air Navigation system for tankers on Y band.
|
-- @field #number TACAN_TANKER_Y TACtical Air Navigation system for tankers on Y band.
|
||||||
-- @field #number VOR Very High Frequency Omni-Directional Range
|
-- @field #number VOR Very High Frequency Omni-Directional Range
|
||||||
-- @field #number ILS_LOCALIZER ILS localizer
|
-- @field #number ILS_LOCALIZER ILS localizer
|
||||||
-- @field #number ILS_GLIDESLOPE ILS glideslope.
|
-- @field #number ILS_GLIDESLOPE ILS glide slope.
|
||||||
-- @field #number PRGM_LOCALIZER PRGM localizer.
|
-- @field #number PRGM_LOCALIZER PRGM localizer.
|
||||||
-- @field #number PRGM_GLIDESLOPE PRGM glideslope.
|
-- @field #number PRGM_GLIDESLOPE PRGM glide slope.
|
||||||
-- @field #number BROADCAST_STATION Broadcast station.
|
-- @field #number BROADCAST_STATION Broadcast station.
|
||||||
-- @field #number VORTAC Radio-based navigational aid for aircraft pilots consisting of a co-located VHF omnidirectional range (VOR) beacon and a tactical air navigation system (TACAN) beacon.
|
-- @field #number VORTAC Radio-based navigational aid for aircraft pilots consisting of a co-located VHF omni-directional range (VOR) beacon and a tactical air navigation system (TACAN) beacon.
|
||||||
-- @field #number TACAN_AA_MODE_X TACtical Air Navigation for aircraft on X band.
|
-- @field #number TACAN_AA_MODE_X TACtical Air Navigation for aircraft on X band.
|
||||||
-- @field #number TACAN_AA_MODE_Y TACtical Air Navigation for aircraft on Y band.
|
-- @field #number TACAN_AA_MODE_Y TACtical Air Navigation for aircraft on Y band.
|
||||||
-- @field #number VORDME Radio beacon that combines a VHF omnidirectional range (VOR) with a distance measuring equipment (DME).
|
-- @field #number VORDME Radio beacon that combines a VHF omnidirectional range (VOR) with a distance measuring equipment (DME).
|
||||||
@@ -170,6 +170,8 @@ end
|
|||||||
function BEACON:ActivateTACAN(Channel, Mode, Message, Bearing, Duration)
|
function BEACON:ActivateTACAN(Channel, Mode, Message, Bearing, Duration)
|
||||||
self:T({channel=Channel, mode=Mode, callsign=Message, bearing=Bearing, duration=Duration})
|
self:T({channel=Channel, mode=Mode, callsign=Message, bearing=Bearing, duration=Duration})
|
||||||
|
|
||||||
|
Mode=Mode or "Y"
|
||||||
|
|
||||||
-- Get frequency.
|
-- Get frequency.
|
||||||
local Frequency=UTILS.TACANToFrequency(Channel, Mode)
|
local Frequency=UTILS.TACANToFrequency(Channel, Mode)
|
||||||
|
|
||||||
@@ -187,11 +189,16 @@ function BEACON:ActivateTACAN(Channel, Mode, Message, Bearing, Duration)
|
|||||||
|
|
||||||
-- Check if unit is an aircraft and set system accordingly.
|
-- Check if unit is an aircraft and set system accordingly.
|
||||||
local AA=self.Positionable:IsAir()
|
local AA=self.Positionable:IsAir()
|
||||||
|
|
||||||
|
|
||||||
if AA then
|
if AA then
|
||||||
System=5 --NOTE: 5 is how you cat the correct tanker behaviour! --BEACON.System.TACAN_TANKER
|
System=5 --NOTE: 5 is how you cat the correct tanker behaviour! --BEACON.System.TACAN_TANKER
|
||||||
-- Check if "Y" mode is selected for aircraft.
|
-- Check if "Y" mode is selected for aircraft.
|
||||||
if Mode~="Y" then
|
if Mode=="X" then
|
||||||
self:E({"WARNING: The POSITIONABLE you want to attach the AA Tacan Beacon is an aircraft: Mode should Y !The BEACON is not emitting.", self.Positionable})
|
--self:E({"WARNING: The POSITIONABLE you want to attach the AA Tacan Beacon is an aircraft: Mode should Y!", self.Positionable})
|
||||||
|
System=BEACON.System.TACAN_TANKER_X
|
||||||
|
else
|
||||||
|
System=BEACON.System.TACAN_TANKER_Y
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -204,7 +211,7 @@ function BEACON:ActivateTACAN(Channel, Mode, Message, Bearing, Duration)
|
|||||||
-- Start beacon.
|
-- Start beacon.
|
||||||
self.Positionable:CommandActivateBeacon(Type, System, Frequency, UnitID, Channel, Mode, AA, Message, Bearing)
|
self.Positionable:CommandActivateBeacon(Type, System, Frequency, UnitID, Channel, Mode, AA, Message, Bearing)
|
||||||
|
|
||||||
-- Stop sheduler.
|
-- Stop scheduler.
|
||||||
if Duration then
|
if Duration then
|
||||||
self.Positionable:DeactivateBeacon(Duration)
|
self.Positionable:DeactivateBeacon(Duration)
|
||||||
end
|
end
|
||||||
@@ -230,7 +237,7 @@ function BEACON:ActivateICLS(Channel, Callsign, Duration)
|
|||||||
-- Start beacon.
|
-- Start beacon.
|
||||||
self.Positionable:CommandActivateICLS(Channel, UnitID, Callsign)
|
self.Positionable:CommandActivateICLS(Channel, UnitID, Callsign)
|
||||||
|
|
||||||
-- Stop sheduler
|
-- Stop scheduler
|
||||||
if Duration then -- Schedule the stop of the BEACON if asked by the MD
|
if Duration then -- Schedule the stop of the BEACON if asked by the MD
|
||||||
self.Positionable:DeactivateBeacon(Duration)
|
self.Positionable:DeactivateBeacon(Duration)
|
||||||
end
|
end
|
||||||
@@ -238,7 +245,34 @@ function BEACON:ActivateICLS(Channel, Callsign, Duration)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Activates a TACAN BEACON on an Aircraft.
|
--- Activates a LINK4 BEACON. The unit the BEACON is attached to should be an aircraft carrier supporting this system.
|
||||||
|
-- @param #BEACON self
|
||||||
|
-- @param #number Frequency LINK4 FRequency in MHz, eg 336.
|
||||||
|
-- @param #string Morse The ID that is going to be coded in Morse and broadcasted by the beacon.
|
||||||
|
-- @param #number Duration How long will the beacon last in seconds. Omit for forever.
|
||||||
|
-- @return #BEACON self
|
||||||
|
function BEACON:ActivateLink4(Frequency, Morse, Duration)
|
||||||
|
self:F({Frequency=Frequency, Morse=Morse, Duration=Duration})
|
||||||
|
|
||||||
|
-- Attached unit.
|
||||||
|
local UnitID=self.Positionable:GetID()
|
||||||
|
|
||||||
|
-- Debug
|
||||||
|
self:T2({"LINK4 BEACON started!"})
|
||||||
|
|
||||||
|
-- Start beacon.
|
||||||
|
self.Positionable:CommandActivateLink4(Frequency,UnitID,Morse)
|
||||||
|
|
||||||
|
-- Stop sheduler
|
||||||
|
if Duration then -- Schedule the stop of the BEACON if asked by the MD
|
||||||
|
self.Positionable:CommandDeactivateLink4(Duration)
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- DEPRECATED: Please use @{BEACON:ActivateTACAN}() instead.
|
||||||
|
-- Activates a TACAN BEACON on an Aircraft.
|
||||||
-- @param #BEACON self
|
-- @param #BEACON self
|
||||||
-- @param #number TACANChannel (the "10" part in "10Y"). Note that AA TACAN are only available on Y Channels
|
-- @param #number TACANChannel (the "10" part in "10Y"). Note that AA TACAN are only available on Y Channels
|
||||||
-- @param #string Message The Message that is going to be coded in Morse and broadcasted by the beacon
|
-- @param #string Message The Message that is going to be coded in Morse and broadcasted by the beacon
|
||||||
@@ -267,13 +301,12 @@ function BEACON:AATACAN(TACANChannel, Message, Bearing, BeaconDuration)
|
|||||||
IsValid = false
|
IsValid = false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- I'm using the beacon type 4 (BEACON_TYPE_TACAN). For System, I'm using 5 (TACAN_TANKER_MODE_Y) if the bearing shows its bearing
|
-- I'm using the beacon type 4 (BEACON_TYPE_TACAN). For System, I'm using 5 (TACAN_TANKER_MODE_Y) if the bearing shows its bearing or 14 (TACAN_AA_MODE_Y) if it does not
|
||||||
-- or 14 (TACAN_AA_MODE_Y) if it does not
|
|
||||||
local System
|
local System
|
||||||
if Bearing then
|
if Bearing then
|
||||||
System = 5
|
System = BEACON.System.TACAN_TANKER_Y
|
||||||
else
|
else
|
||||||
System = 14
|
System = BEACON.System.TACAN_AA_MODE_Y
|
||||||
end
|
end
|
||||||
|
|
||||||
if IsValid then -- Starts the BEACON
|
if IsValid then -- Starts the BEACON
|
||||||
@@ -281,10 +314,13 @@ function BEACON:AATACAN(TACANChannel, Message, Bearing, BeaconDuration)
|
|||||||
self.Positionable:SetCommand({
|
self.Positionable:SetCommand({
|
||||||
id = "ActivateBeacon",
|
id = "ActivateBeacon",
|
||||||
params = {
|
params = {
|
||||||
type = 4,
|
type = BEACON.Type.TACAN,
|
||||||
system = System,
|
system = System,
|
||||||
callsign = Message,
|
callsign = Message,
|
||||||
|
AA = true,
|
||||||
frequency = Frequency,
|
frequency = Frequency,
|
||||||
|
bearing = Bearing,
|
||||||
|
modeChannel = "Y",
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -316,10 +352,10 @@ function BEACON:StopAATACAN()
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Activates a general pupose Radio Beacon
|
--- Activates a general purpose Radio Beacon
|
||||||
-- This uses the very generic singleton function "trigger.action.radioTransmission()" provided by DCS to broadcast a sound file on a specific frequency.
|
-- This uses the very generic singleton function "trigger.action.radioTransmission()" provided by DCS to broadcast a sound file on a specific frequency.
|
||||||
-- Although any frequency could be used, only 2 DCS Modules can home on radio beacons at the time of writing : the Huey and the Mi-8.
|
-- Although any frequency could be used, only a few DCS Modules can home on radio beacons at the time of writing, i.e. the Mi-8, Huey, Gazelle etc.
|
||||||
-- They can home in on these specific frequencies :
|
-- The following e.g. can home in on these specific frequencies :
|
||||||
-- * **Mi8**
|
-- * **Mi8**
|
||||||
-- * R-828 -> 20-60MHz
|
-- * R-828 -> 20-60MHz
|
||||||
-- * ARKUD -> 100-150MHz (canal 1 : 114166, canal 2 : 114333, canal 3 : 114583, canal 4 : 121500, canal 5 : 123100, canal 6 : 124100) AM
|
-- * ARKUD -> 100-150MHz (canal 1 : 114166, canal 2 : 114333, canal 3 : 114583, canal 4 : 121500, canal 5 : 123100, canal 6 : 124100) AM
|
||||||
@@ -393,7 +429,7 @@ function BEACON:RadioBeacon(FileName, Frequency, Modulation, Power, BeaconDurati
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Stops the AA TACAN BEACON
|
--- Stops the Radio Beacon
|
||||||
-- @param #BEACON self
|
-- @param #BEACON self
|
||||||
-- @return #BEACON self
|
-- @return #BEACON self
|
||||||
function BEACON:StopRadioBeacon()
|
function BEACON:StopRadioBeacon()
|
||||||
@@ -439,4 +475,4 @@ function BEACON:_TACANToFrequency(TACANChannel, TACANMode)
|
|||||||
end
|
end
|
||||||
|
|
||||||
return (A + TACANChannel - B) * 1000000
|
return (A + TACANChannel - B) * 1000000
|
||||||
end
|
end
|
||||||
|
|||||||
295
Moose Development/Moose/Core/Condition.lua
Normal file
295
Moose Development/Moose/Core/Condition.lua
Normal file
@@ -0,0 +1,295 @@
|
|||||||
|
--- **Core** - Define any or all conditions to be evaluated.
|
||||||
|
--
|
||||||
|
-- **Main Features:**
|
||||||
|
--
|
||||||
|
-- * Add arbitrary numbers of conditon functions
|
||||||
|
-- * Evaluate *any* or *all* conditions
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ## Example Missions:
|
||||||
|
--
|
||||||
|
-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/OPS%20-%20Operation).
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ### Author: **funkyfranky**
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
-- @module Core.Condition
|
||||||
|
-- @image Core_Conditon.png
|
||||||
|
|
||||||
|
--- CONDITON class.
|
||||||
|
-- @type CONDITION
|
||||||
|
-- @field #string ClassName Name of the class.
|
||||||
|
-- @field #string lid Class id string for output to DCS log file.
|
||||||
|
-- @field #boolean isAny General functions are evaluated as any condition.
|
||||||
|
-- @field #boolean negateResult Negeate result of evaluation.
|
||||||
|
-- @field #table functionsGen General condition functions.
|
||||||
|
-- @field #table functionsAny Any condition functions.
|
||||||
|
-- @field #table functionsAll All condition functions.
|
||||||
|
--
|
||||||
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
|
--- *Better three hours too soon than a minute too late.* - William Shakespeare
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- # The CONDITION Concept
|
||||||
|
--
|
||||||
|
--
|
||||||
|
--
|
||||||
|
-- @field #CONDITION
|
||||||
|
CONDITION = {
|
||||||
|
ClassName = "CONDITION",
|
||||||
|
lid = nil,
|
||||||
|
functionsGen = {},
|
||||||
|
functionsAny = {},
|
||||||
|
functionsAll = {},
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Condition function.
|
||||||
|
-- @type CONDITION.Function
|
||||||
|
-- @field #function func Callback function to check for a condition. Should return a `#boolean`.
|
||||||
|
-- @field #table arg (Optional) Arguments passed to the condition callback function if any.
|
||||||
|
|
||||||
|
--- CONDITION class version.
|
||||||
|
-- @field #string version
|
||||||
|
CONDITION.version="0.1.0"
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- TODO list
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- TODO: Make FSM.
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Constructor
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Create a new CONDITION object.
|
||||||
|
-- @param #CONDITION self
|
||||||
|
-- @param #string Name (Optional) Name used in the logs.
|
||||||
|
-- @return #CONDITION self
|
||||||
|
function CONDITION:New(Name)
|
||||||
|
|
||||||
|
-- Inherit BASE.
|
||||||
|
local self=BASE:Inherit(self, BASE:New()) --#CONDITION
|
||||||
|
|
||||||
|
self.name=Name or "Condition X"
|
||||||
|
|
||||||
|
self.lid=string.format("%s | ", self.name)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set that general condition functions return `true` if `any` function returns `true`. Default is that *all* functions must return `true`.
|
||||||
|
-- @param #CONDITION self
|
||||||
|
-- @param #boolean Any If `true`, *any* condition can be true. Else *all* conditions must result `true`.
|
||||||
|
-- @return #CONDITION self
|
||||||
|
function CONDITION:SetAny(Any)
|
||||||
|
self.isAny=Any
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Negate result.
|
||||||
|
-- @param #CONDITION self
|
||||||
|
-- @param #boolean Negate If `true`, result is negated else not.
|
||||||
|
-- @return #CONDITION self
|
||||||
|
function CONDITION:SetNegateResult(Negate)
|
||||||
|
self.negateResult=Negate
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Add a function that is evaluated. It must return a `#boolean` value, *i.e.* either `true` or `false` (or `nil`).
|
||||||
|
-- @param #CONDITION self
|
||||||
|
-- @param #function Function The function to call.
|
||||||
|
-- @param ... (Optional) Parameters passed to the function (if any).
|
||||||
|
--
|
||||||
|
-- @usage
|
||||||
|
-- local function isAequalB(a, b)
|
||||||
|
-- return a==b
|
||||||
|
-- end
|
||||||
|
--
|
||||||
|
-- myCondition:AddFunction(isAequalB, a, b)
|
||||||
|
--
|
||||||
|
-- @return #CONDITION self
|
||||||
|
function CONDITION:AddFunction(Function, ...)
|
||||||
|
|
||||||
|
-- Condition function.
|
||||||
|
local condition=self:_CreateCondition(Function, ...)
|
||||||
|
|
||||||
|
-- Add to table.
|
||||||
|
table.insert(self.functionsGen, condition)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Add a function that is evaluated. It must return a `#boolean` value, *i.e.* either `true` or `false` (or `nil`).
|
||||||
|
-- @param #CONDITION self
|
||||||
|
-- @param #function Function The function to call.
|
||||||
|
-- @param ... (Optional) Parameters passed to the function (if any).
|
||||||
|
-- @return #CONDITION self
|
||||||
|
function CONDITION:AddFunctionAny(Function, ...)
|
||||||
|
|
||||||
|
-- Condition function.
|
||||||
|
local condition=self:_CreateCondition(Function, ...)
|
||||||
|
|
||||||
|
-- Add to table.
|
||||||
|
table.insert(self.functionsAny, condition)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Add a function that is evaluated. It must return a `#boolean` value, *i.e.* either `true` or `false` (or `nil`).
|
||||||
|
-- @param #CONDITION self
|
||||||
|
-- @param #function Function The function to call.
|
||||||
|
-- @param ... (Optional) Parameters passed to the function (if any).
|
||||||
|
-- @return #CONDITION self
|
||||||
|
function CONDITION:AddFunctionAll(Function, ...)
|
||||||
|
|
||||||
|
-- Condition function.
|
||||||
|
local condition=self:_CreateCondition(Function, ...)
|
||||||
|
|
||||||
|
-- Add to table.
|
||||||
|
table.insert(self.functionsAll, condition)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Evaluate conditon functions.
|
||||||
|
-- @param #CONDITION self
|
||||||
|
-- @param #boolean AnyTrue If `true`, evaluation return `true` if *any* condition function returns `true`. By default, *all* condition functions must return true.
|
||||||
|
-- @return #boolean Result of condition functions.
|
||||||
|
function CONDITION:Evaluate(AnyTrue)
|
||||||
|
|
||||||
|
-- Check if at least one function was given.
|
||||||
|
if #self.functionsAll + #self.functionsAny + #self.functionsAll == 0 then
|
||||||
|
if self.negateResult then
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Any condition for gen.
|
||||||
|
local evalAny=self.isAny
|
||||||
|
if AnyTrue~=nil then
|
||||||
|
evalAny=AnyTrue
|
||||||
|
end
|
||||||
|
|
||||||
|
local isGen=nil
|
||||||
|
if evalAny then
|
||||||
|
isGen=self:_EvalConditionsAny(self.functionsGen)
|
||||||
|
else
|
||||||
|
isGen=self:_EvalConditionsAll(self.functionsGen)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Is any?
|
||||||
|
local isAny=self:_EvalConditionsAny(self.functionsAny)
|
||||||
|
|
||||||
|
-- Is all?
|
||||||
|
local isAll=self:_EvalConditionsAll(self.functionsAll)
|
||||||
|
|
||||||
|
-- Result.
|
||||||
|
local result=isGen and isAny and isAll
|
||||||
|
|
||||||
|
-- Negate result.
|
||||||
|
if self.negateResult then
|
||||||
|
result=not result
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Debug message.
|
||||||
|
self:T(self.lid..string.format("Evaluate: isGen=%s, isAny=%s, isAll=%s (negate=%s) ==> result=%s", tostring(isGen), tostring(isAny), tostring(isAll), tostring(self.negateResult), tostring(result)))
|
||||||
|
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Check if all given condition are true.
|
||||||
|
-- @param #CONDITION self
|
||||||
|
-- @param #table functions Functions to evaluate.
|
||||||
|
-- @return #boolean If true, all conditions were true (or functions was empty/nil). Returns false if at least one condition returned false.
|
||||||
|
function CONDITION:_EvalConditionsAll(functions)
|
||||||
|
|
||||||
|
-- At least one condition?
|
||||||
|
local gotone=false
|
||||||
|
|
||||||
|
|
||||||
|
-- Any stop condition must be true.
|
||||||
|
for _,_condition in pairs(functions or {}) do
|
||||||
|
local condition=_condition --#CONDITION.Function
|
||||||
|
|
||||||
|
-- At least one condition was defined.
|
||||||
|
gotone=true
|
||||||
|
|
||||||
|
-- Call function.
|
||||||
|
local istrue=condition.func(unpack(condition.arg))
|
||||||
|
|
||||||
|
-- Any false will return false.
|
||||||
|
if not istrue then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
-- All conditions were true.
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Check if any of the given conditions is true.
|
||||||
|
-- @param #CONDITION self
|
||||||
|
-- @param #table functions Functions to evaluate.
|
||||||
|
-- @return #boolean If true, at least one condition is true (or functions was emtpy/nil).
|
||||||
|
function CONDITION:_EvalConditionsAny(functions)
|
||||||
|
|
||||||
|
-- At least one condition?
|
||||||
|
local gotone=false
|
||||||
|
|
||||||
|
-- Any stop condition must be true.
|
||||||
|
for _,_condition in pairs(functions or {}) do
|
||||||
|
local condition=_condition --#CONDITION.Function
|
||||||
|
|
||||||
|
-- At least one condition was defined.
|
||||||
|
gotone=true
|
||||||
|
|
||||||
|
-- Call function.
|
||||||
|
local istrue=condition.func(unpack(condition.arg))
|
||||||
|
|
||||||
|
-- Any true will return true.
|
||||||
|
if istrue then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
-- No condition was true.
|
||||||
|
if gotone then
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
-- No functions passed.
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Create conditon function object.
|
||||||
|
-- @param #CONDITION self
|
||||||
|
-- @param #function Function The function to call.
|
||||||
|
-- @param ... (Optional) Parameters passed to the function (if any).
|
||||||
|
-- @return #CONDITION.Function Condition function.
|
||||||
|
function CONDITION:_CreateCondition(Function, ...)
|
||||||
|
|
||||||
|
local condition={} --#CONDITION.Function
|
||||||
|
|
||||||
|
condition.func=Function
|
||||||
|
condition.arg={}
|
||||||
|
if arg then
|
||||||
|
condition.arg=arg
|
||||||
|
end
|
||||||
|
|
||||||
|
return condition
|
||||||
|
end
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
@@ -124,6 +124,7 @@ function DATABASE:New()
|
|||||||
self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash )
|
self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash )
|
||||||
self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash )
|
self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash )
|
||||||
self:HandleEvent( EVENTS.RemoveUnit, self._EventOnDeadOrCrash )
|
self:HandleEvent( EVENTS.RemoveUnit, self._EventOnDeadOrCrash )
|
||||||
|
--self:HandleEvent( EVENTS.UnitLost, self._EventOnDeadOrCrash ) -- DCS 2.7.1 for Aerial units no dead event ATM
|
||||||
self:HandleEvent( EVENTS.Hit, self.AccountHits )
|
self:HandleEvent( EVENTS.Hit, self.AccountHits )
|
||||||
self:HandleEvent( EVENTS.NewCargo )
|
self:HandleEvent( EVENTS.NewCargo )
|
||||||
self:HandleEvent( EVENTS.DeleteCargo )
|
self:HandleEvent( EVENTS.DeleteCargo )
|
||||||
@@ -136,8 +137,8 @@ function DATABASE:New()
|
|||||||
self:_RegisterGroupsAndUnits()
|
self:_RegisterGroupsAndUnits()
|
||||||
self:_RegisterClients()
|
self:_RegisterClients()
|
||||||
self:_RegisterStatics()
|
self:_RegisterStatics()
|
||||||
--self:_RegisterAirbases()
|
|
||||||
--self:_RegisterPlayers()
|
--self:_RegisterPlayers()
|
||||||
|
--self:_RegisterAirbases()
|
||||||
|
|
||||||
self.UNITS_Position = 0
|
self.UNITS_Position = 0
|
||||||
|
|
||||||
@@ -162,17 +163,11 @@ end
|
|||||||
function DATABASE:AddUnit( DCSUnitName )
|
function DATABASE:AddUnit( DCSUnitName )
|
||||||
|
|
||||||
if not self.UNITS[DCSUnitName] then
|
if not self.UNITS[DCSUnitName] then
|
||||||
|
|
||||||
-- Debug info.
|
-- Debug info.
|
||||||
self:T( { "Add UNIT:", DCSUnitName } )
|
self:T( { "Add UNIT:", DCSUnitName } )
|
||||||
|
|
||||||
--local UnitRegister = UNIT:Register( DCSUnitName )
|
|
||||||
|
|
||||||
-- Register unit
|
-- Register unit
|
||||||
self.UNITS[DCSUnitName]=UNIT:Register(DCSUnitName)
|
self.UNITS[DCSUnitName]=UNIT:Register(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
|
end
|
||||||
|
|
||||||
return self.UNITS[DCSUnitName]
|
return self.UNITS[DCSUnitName]
|
||||||
@@ -182,7 +177,6 @@ end
|
|||||||
--- Deletes a Unit from the DATABASE based on the Unit Name.
|
--- Deletes a Unit from the DATABASE based on the Unit Name.
|
||||||
-- @param #DATABASE self
|
-- @param #DATABASE self
|
||||||
function DATABASE:DeleteUnit( DCSUnitName )
|
function DATABASE:DeleteUnit( DCSUnitName )
|
||||||
|
|
||||||
self.UNITS[DCSUnitName] = nil
|
self.UNITS[DCSUnitName] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -217,16 +211,6 @@ function DATABASE:FindStatic( StaticName )
|
|||||||
return StaticFound
|
return StaticFound
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Finds a AIRBASE based on the AirbaseName.
|
|
||||||
-- @param #DATABASE self
|
|
||||||
-- @param #string AirbaseName
|
|
||||||
-- @return Wrapper.Airbase#AIRBASE The found AIRBASE.
|
|
||||||
function DATABASE:FindAirbase( AirbaseName )
|
|
||||||
|
|
||||||
local AirbaseFound = self.AIRBASES[AirbaseName]
|
|
||||||
return AirbaseFound
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Adds a Airbase based on the Airbase Name in the DATABASE.
|
--- Adds a Airbase based on the Airbase Name in the DATABASE.
|
||||||
-- @param #DATABASE self
|
-- @param #DATABASE self
|
||||||
-- @param #string AirbaseName The name of the airbase.
|
-- @param #string AirbaseName The name of the airbase.
|
||||||
@@ -300,23 +284,23 @@ do -- Zones
|
|||||||
|
|
||||||
for ZoneID, ZoneData in pairs(env.mission.triggers.zones) do
|
for ZoneID, ZoneData in pairs(env.mission.triggers.zones) do
|
||||||
local ZoneName = ZoneData.name
|
local ZoneName = ZoneData.name
|
||||||
|
|
||||||
-- Color
|
-- Color
|
||||||
local color=ZoneData.color or {1, 0, 0, 0.15}
|
local color=ZoneData.color or {1, 0, 0, 0.15}
|
||||||
|
|
||||||
-- Create new Zone
|
-- Create new Zone
|
||||||
local Zone=nil --Core.Zone#ZONE_BASE
|
local Zone=nil --Core.Zone#ZONE_BASE
|
||||||
|
|
||||||
if ZoneData.type==0 then
|
if ZoneData.type==0 then
|
||||||
|
|
||||||
---
|
---
|
||||||
-- Circular zone
|
-- Circular zone
|
||||||
---
|
---
|
||||||
|
|
||||||
self:I(string.format("Register ZONE: %s (Circular)", ZoneName))
|
self:I(string.format("Register ZONE: %s (Circular)", ZoneName))
|
||||||
|
|
||||||
Zone=ZONE:New(ZoneName)
|
Zone=ZONE:New(ZoneName)
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -324,51 +308,65 @@ do -- Zones
|
|||||||
---
|
---
|
||||||
|
|
||||||
self:I(string.format("Register ZONE: %s (Polygon, Quad)", ZoneName))
|
self:I(string.format("Register ZONE: %s (Polygon, Quad)", ZoneName))
|
||||||
|
|
||||||
Zone=ZONE_POLYGON_BASE:New(ZoneName, ZoneData.verticies)
|
Zone=ZONE_POLYGON_BASE:New(ZoneName, ZoneData.verticies)
|
||||||
|
|
||||||
--for i,vec2 in pairs(ZoneData.verticies) do
|
--for i,vec2 in pairs(ZoneData.verticies) do
|
||||||
-- local coord=COORDINATE:NewFromVec2(vec2)
|
-- local coord=COORDINATE:NewFromVec2(vec2)
|
||||||
-- coord:MarkToAll(string.format("%s Point %d", ZoneName, i))
|
-- coord:MarkToAll(string.format("%s Point %d", ZoneName, i))
|
||||||
--end
|
--end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if Zone then
|
if Zone then
|
||||||
|
|
||||||
-- Store color of zone.
|
-- Store color of zone.
|
||||||
Zone.Color=color
|
Zone.Color=color
|
||||||
|
|
||||||
|
-- Store zone ID.
|
||||||
|
Zone.ZoneID=ZoneData.zoneId
|
||||||
|
|
||||||
|
-- Store zone properties (if any)
|
||||||
|
local ZoneProperties = ZoneData.properties or nil
|
||||||
|
Zone.Properties = {}
|
||||||
|
if ZoneName and ZoneProperties then
|
||||||
|
for _,ZoneProp in ipairs(ZoneProperties) do
|
||||||
|
if ZoneProp.key then
|
||||||
|
Zone.Properties[ZoneProp.key] = ZoneProp.value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- Store in DB.
|
-- Store in DB.
|
||||||
self.ZONENAMES[ZoneName] = ZoneName
|
self.ZONENAMES[ZoneName] = ZoneName
|
||||||
|
|
||||||
-- Add zone.
|
-- Add zone.
|
||||||
self:AddZone(ZoneName, Zone)
|
self:AddZone(ZoneName, Zone)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Polygon zones defined by late activated groups.
|
-- Polygon zones defined by late activated groups.
|
||||||
for ZoneGroupName, ZoneGroup in pairs( self.GROUPS ) do
|
for ZoneGroupName, ZoneGroup in pairs( self.GROUPS ) do
|
||||||
if ZoneGroupName:match("#ZONE_POLYGON") then
|
if ZoneGroupName:match("#ZONE_POLYGON") then
|
||||||
|
|
||||||
local ZoneName1 = ZoneGroupName:match("(.*)#ZONE_POLYGON")
|
local ZoneName1 = ZoneGroupName:match("(.*)#ZONE_POLYGON")
|
||||||
local ZoneName2 = ZoneGroupName:match(".*#ZONE_POLYGON(.*)")
|
local ZoneName2 = ZoneGroupName:match(".*#ZONE_POLYGON(.*)")
|
||||||
local ZoneName = ZoneName1 .. ( ZoneName2 or "" )
|
local ZoneName = ZoneName1 .. ( ZoneName2 or "" )
|
||||||
|
|
||||||
-- Debug output
|
-- Debug output
|
||||||
self:I(string.format("Register ZONE: %s (Polygon)", ZoneName))
|
self:I(string.format("Register ZONE: %s (Polygon)", ZoneName))
|
||||||
|
|
||||||
-- Create a new polygon zone.
|
-- Create a new polygon zone.
|
||||||
local Zone_Polygon = ZONE_POLYGON:New( ZoneName, ZoneGroup )
|
local Zone_Polygon = ZONE_POLYGON:New( ZoneName, ZoneGroup )
|
||||||
|
|
||||||
-- Set color.
|
-- Set color.
|
||||||
Zone_Polygon:SetColor({1, 0, 0}, 0.15)
|
Zone_Polygon:SetColor({1, 0, 0}, 0.15)
|
||||||
|
|
||||||
-- Store name in DB.
|
-- Store name in DB.
|
||||||
self.ZONENAMES[ZoneName] = ZoneName
|
self.ZONENAMES[ZoneName] = ZoneName
|
||||||
|
|
||||||
-- Add zone to DB.
|
-- Add zone to DB.
|
||||||
self:AddZone( ZoneName, Zone_Polygon )
|
self:AddZone( ZoneName, Zone_Polygon )
|
||||||
end
|
end
|
||||||
@@ -778,7 +776,9 @@ function DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, Category
|
|||||||
|
|
||||||
local StaticTemplate = UTILS.DeepCopy( StaticTemplate )
|
local StaticTemplate = UTILS.DeepCopy( StaticTemplate )
|
||||||
|
|
||||||
local StaticTemplateName = env.getValueDictByKey(StaticTemplate.name)
|
local StaticTemplateGroupName = env.getValueDictByKey(StaticTemplate.name)
|
||||||
|
|
||||||
|
local StaticTemplateName=StaticTemplate.units[1].name
|
||||||
|
|
||||||
self.Templates.Statics[StaticTemplateName] = self.Templates.Statics[StaticTemplateName] or {}
|
self.Templates.Statics[StaticTemplateName] = self.Templates.Statics[StaticTemplateName] or {}
|
||||||
|
|
||||||
@@ -786,7 +786,7 @@ function DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, Category
|
|||||||
StaticTemplate.CoalitionID = CoalitionID
|
StaticTemplate.CoalitionID = CoalitionID
|
||||||
StaticTemplate.CountryID = CountryID
|
StaticTemplate.CountryID = CountryID
|
||||||
|
|
||||||
self.Templates.Statics[StaticTemplateName].StaticName = StaticTemplateName
|
self.Templates.Statics[StaticTemplateName].StaticName = StaticTemplateGroupName
|
||||||
self.Templates.Statics[StaticTemplateName].GroupTemplate = StaticTemplate
|
self.Templates.Statics[StaticTemplateName].GroupTemplate = StaticTemplate
|
||||||
self.Templates.Statics[StaticTemplateName].UnitTemplate = StaticTemplate.units[1]
|
self.Templates.Statics[StaticTemplateName].UnitTemplate = StaticTemplate.units[1]
|
||||||
self.Templates.Statics[StaticTemplateName].CategoryID = CategoryID
|
self.Templates.Statics[StaticTemplateName].CategoryID = CategoryID
|
||||||
@@ -830,7 +830,7 @@ function DATABASE:GetStaticUnitTemplate( StaticName )
|
|||||||
return UnitTemplate, self.Templates.Statics[StaticName].CoalitionID, self.Templates.Statics[StaticName].CategoryID, self.Templates.Statics[StaticName].CountryID
|
return UnitTemplate, self.Templates.Statics[StaticName].CoalitionID, self.Templates.Statics[StaticName].CategoryID, self.Templates.Statics[StaticName].CountryID
|
||||||
else
|
else
|
||||||
self:E("ERROR: Static unit template does NOT exist for static "..tostring(StaticName))
|
self:E("ERROR: Static unit template does NOT exist for static "..tostring(StaticName))
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -856,10 +856,24 @@ function DATABASE:GetGroupTemplateFromUnitName( UnitName )
|
|||||||
return self.Templates.Units[UnitName].GroupTemplate
|
return self.Templates.Units[UnitName].GroupTemplate
|
||||||
else
|
else
|
||||||
self:E("ERROR: Unit template does not exist for unit "..tostring(UnitName))
|
self:E("ERROR: Unit template does not exist for unit "..tostring(UnitName))
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get group template from unit name.
|
||||||
|
-- @param #DATABASE self
|
||||||
|
-- @param #string UnitName Name of the unit.
|
||||||
|
-- @return #table Group template.
|
||||||
|
function DATABASE:GetUnitTemplateFromUnitName( UnitName )
|
||||||
|
if self.Templates.Units[UnitName] then
|
||||||
|
return self.Templates.Units[UnitName]
|
||||||
|
else
|
||||||
|
self:E("ERROR: Unit template does not exist for unit "..tostring(UnitName))
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Get coalition ID from client name.
|
--- Get coalition ID from client name.
|
||||||
-- @param #DATABASE self
|
-- @param #DATABASE self
|
||||||
-- @param #string ClientName Name of the Client.
|
-- @param #string ClientName Name of the Client.
|
||||||
@@ -934,13 +948,13 @@ end
|
|||||||
function DATABASE:_RegisterGroupsAndUnits()
|
function DATABASE:_RegisterGroupsAndUnits()
|
||||||
|
|
||||||
local CoalitionsData = { GroupsRed = coalition.getGroups( coalition.side.RED ), GroupsBlue = coalition.getGroups( coalition.side.BLUE ), GroupsNeutral = coalition.getGroups( coalition.side.NEUTRAL ) }
|
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 CoalitionId, CoalitionData in pairs( CoalitionsData ) do
|
||||||
|
|
||||||
for DCSGroupId, DCSGroup in pairs( CoalitionData ) do
|
for DCSGroupId, DCSGroup in pairs( CoalitionData ) do
|
||||||
|
|
||||||
if DCSGroup:isExist() then
|
if DCSGroup:isExist() then
|
||||||
|
|
||||||
-- Group name.
|
-- Group name.
|
||||||
local DCSGroupName = DCSGroup:getName()
|
local DCSGroupName = DCSGroup:getName()
|
||||||
|
|
||||||
@@ -953,11 +967,11 @@ function DATABASE:_RegisterGroupsAndUnits()
|
|||||||
|
|
||||||
-- Get unit name.
|
-- Get unit name.
|
||||||
local DCSUnitName = DCSUnit:getName()
|
local DCSUnitName = DCSUnit:getName()
|
||||||
|
|
||||||
-- Add unit.
|
-- Add unit.
|
||||||
self:I(string.format("Register Unit: %s", tostring(DCSUnitName)))
|
self:I(string.format("Register Unit: %s", tostring(DCSUnitName)))
|
||||||
self:AddUnit( DCSUnitName )
|
self:AddUnit( DCSUnitName )
|
||||||
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
self:E({"Group does not exist: ", DCSGroup})
|
self:E({"Group does not exist: ", DCSGroup})
|
||||||
@@ -976,13 +990,15 @@ function DATABASE:_RegisterClients()
|
|||||||
|
|
||||||
for ClientName, ClientTemplate in pairs( self.Templates.ClientsByName ) do
|
for ClientName, ClientTemplate in pairs( self.Templates.ClientsByName ) do
|
||||||
self:I(string.format("Register Client: %s", tostring(ClientName)))
|
self:I(string.format("Register Client: %s", tostring(ClientName)))
|
||||||
self:AddClient( ClientName )
|
local client=self:AddClient( ClientName )
|
||||||
|
client.SpawnCoord=COORDINATE:New(ClientTemplate.x, ClientTemplate.alt, ClientTemplate.y)
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param #DATABASE self
|
--- Private method that registeres all static objects.
|
||||||
|
-- @param #DATABASE self
|
||||||
function DATABASE:_RegisterStatics()
|
function DATABASE:_RegisterStatics()
|
||||||
|
|
||||||
local CoalitionsData={GroupsRed=coalition.getStaticObjects(coalition.side.RED), GroupsBlue=coalition.getStaticObjects(coalition.side.BLUE), GroupsNeutral=coalition.getStaticObjects(coalition.side.NEUTRAL)}
|
local CoalitionsData={GroupsRed=coalition.getStaticObjects(coalition.side.RED), GroupsBlue=coalition.getStaticObjects(coalition.side.BLUE), GroupsNeutral=coalition.getStaticObjects(coalition.side.NEUTRAL)}
|
||||||
@@ -1010,7 +1026,7 @@ end
|
|||||||
function DATABASE:_RegisterAirbases()
|
function DATABASE:_RegisterAirbases()
|
||||||
|
|
||||||
for DCSAirbaseId, DCSAirbase in pairs(world.getAirbases()) do
|
for DCSAirbaseId, DCSAirbase in pairs(world.getAirbases()) do
|
||||||
|
|
||||||
-- Get the airbase name.
|
-- Get the airbase name.
|
||||||
local DCSAirbaseName = DCSAirbase:getName()
|
local DCSAirbaseName = DCSAirbase:getName()
|
||||||
|
|
||||||
@@ -1019,25 +1035,20 @@ function DATABASE:_RegisterAirbases()
|
|||||||
|
|
||||||
-- Add and register airbase.
|
-- Add and register airbase.
|
||||||
local airbase=self:AddAirbase( DCSAirbaseName )
|
local airbase=self:AddAirbase( DCSAirbaseName )
|
||||||
|
|
||||||
-- Unique ID.
|
-- Unique ID.
|
||||||
local airbaseUID=airbase:GetID(true)
|
local airbaseUID=airbase:GetID(true)
|
||||||
|
|
||||||
-- Debug output.
|
-- Debug output.
|
||||||
local text=string.format("Register %s: %s (ID=%d UID=%d), parking=%d [", AIRBASE.CategoryName[airbase.category], tostring(DCSAirbaseName), airbaseID, airbaseUID, airbase.NparkingTotal)
|
local text=string.format("Register %s: %s (UID=%d), Runways=%d, Parking=%d [", AIRBASE.CategoryName[airbase.category], tostring(DCSAirbaseName), airbaseUID, #airbase.runways, airbase.NparkingTotal)
|
||||||
for _,terminalType in pairs(AIRBASE.TerminalType) do
|
for _,terminalType in pairs(AIRBASE.TerminalType) do
|
||||||
if airbase.NparkingTerminal and airbase.NparkingTerminal[terminalType] then
|
if airbase.NparkingTerminal and airbase.NparkingTerminal[terminalType] then
|
||||||
text=text..string.format("%d=%d ", terminalType, airbase.NparkingTerminal[terminalType])
|
text=text..string.format("%d=%d ", terminalType, airbase.NparkingTerminal[terminalType])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
text=text.."]"
|
text=text.."]"
|
||||||
self:I(text)
|
self:I(text)
|
||||||
|
|
||||||
-- Check for DCS bug IDs.
|
|
||||||
if airbaseID~=airbase:GetID() then
|
|
||||||
--self:E("WARNING: :getID does NOT match :GetID!")
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
@@ -1053,74 +1064,74 @@ function DATABASE:_EventOnBirth( Event )
|
|||||||
self:F( { Event } )
|
self:F( { Event } )
|
||||||
|
|
||||||
if Event.IniDCSUnit then
|
if Event.IniDCSUnit then
|
||||||
|
|
||||||
if Event.IniObjectCategory == 3 then
|
if Event.IniObjectCategory == 3 then
|
||||||
|
|
||||||
self:AddStatic( Event.IniDCSUnitName )
|
self:AddStatic( Event.IniDCSUnitName )
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
if Event.IniObjectCategory == 1 then
|
if Event.IniObjectCategory == 1 then
|
||||||
|
|
||||||
self:AddUnit( Event.IniDCSUnitName )
|
self:AddUnit( Event.IniDCSUnitName )
|
||||||
self:AddGroup( Event.IniDCSGroupName )
|
self:AddGroup( Event.IniDCSGroupName )
|
||||||
|
|
||||||
-- Add airbase if it was spawned later in the mission.
|
-- Add airbase if it was spawned later in the mission.
|
||||||
local DCSAirbase = Airbase.getByName(Event.IniDCSUnitName)
|
local DCSAirbase = Airbase.getByName(Event.IniDCSUnitName)
|
||||||
if DCSAirbase then
|
if DCSAirbase then
|
||||||
self:I(string.format("Adding airbase %s", tostring(Event.IniDCSUnitName)))
|
self:I(string.format("Adding airbase %s", tostring(Event.IniDCSUnitName)))
|
||||||
self:AddAirbase(Event.IniDCSUnitName)
|
self:AddAirbase(Event.IniDCSUnitName)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if Event.IniObjectCategory == 1 then
|
if Event.IniObjectCategory == 1 then
|
||||||
|
|
||||||
Event.IniUnit = self:FindUnit( Event.IniDCSUnitName )
|
Event.IniUnit = self:FindUnit( Event.IniDCSUnitName )
|
||||||
Event.IniGroup = self:FindGroup( Event.IniDCSGroupName )
|
Event.IniGroup = self:FindGroup( Event.IniDCSGroupName )
|
||||||
|
|
||||||
-- Client
|
-- Client
|
||||||
local client=self.CLIENTS[Event.IniDCSUnitName] --Wrapper.Client#CLIENT
|
local client=self.CLIENTS[Event.IniDCSUnitName] --Wrapper.Client#CLIENT
|
||||||
|
|
||||||
if client then
|
if client then
|
||||||
-- TODO: create event ClientAlive
|
-- TODO: create event ClientAlive
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Get player name.
|
-- Get player name.
|
||||||
local PlayerName = Event.IniUnit:GetPlayerName()
|
local PlayerName = Event.IniUnit:GetPlayerName()
|
||||||
|
|
||||||
if PlayerName then
|
if PlayerName then
|
||||||
|
|
||||||
-- Debug info.
|
-- Debug info.
|
||||||
self:I(string.format("Player '%s' joint unit '%s' of group '%s'", tostring(PlayerName), tostring(Event.IniDCSUnitName), tostring(Event.IniDCSGroupName)))
|
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.
|
-- Add client in case it does not exist already.
|
||||||
if not client then
|
if not client then
|
||||||
client=self:AddClient(Event.IniDCSUnitName)
|
client=self:AddClient(Event.IniDCSUnitName)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Add player.
|
-- Add player.
|
||||||
client:AddPlayer(PlayerName)
|
client:AddPlayer(PlayerName)
|
||||||
|
|
||||||
-- Add player.
|
-- Add player.
|
||||||
if not self.PLAYERS[PlayerName] then
|
if not self.PLAYERS[PlayerName] then
|
||||||
self:AddPlayer( Event.IniUnitName, PlayerName )
|
self:AddPlayer( Event.IniUnitName, PlayerName )
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Player settings.
|
-- Player settings.
|
||||||
local Settings = SETTINGS:Set( PlayerName )
|
local Settings = SETTINGS:Set( PlayerName )
|
||||||
Settings:SetPlayerMenu(Event.IniUnit)
|
Settings:SetPlayerMenu(Event.IniUnit)
|
||||||
|
|
||||||
-- Create an event.
|
-- Create an event.
|
||||||
self:CreateEventPlayerEnterAircraft(Event.IniUnit)
|
self:CreateEventPlayerEnterAircraft(Event.IniUnit)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -1130,48 +1141,64 @@ end
|
|||||||
function DATABASE:_EventOnDeadOrCrash( Event )
|
function DATABASE:_EventOnDeadOrCrash( Event )
|
||||||
|
|
||||||
if Event.IniDCSUnit then
|
if Event.IniDCSUnit then
|
||||||
|
|
||||||
local name=Event.IniDCSUnitName
|
local name=Event.IniDCSUnitName
|
||||||
|
|
||||||
if Event.IniObjectCategory == 3 then
|
if Event.IniObjectCategory == 3 then
|
||||||
|
|
||||||
---
|
---
|
||||||
-- STATICS
|
-- STATICS
|
||||||
---
|
---
|
||||||
|
|
||||||
if self.STATICS[Event.IniDCSUnitName] then
|
if self.STATICS[Event.IniDCSUnitName] then
|
||||||
self:DeleteStatic( Event.IniDCSUnitName )
|
self:DeleteStatic( Event.IniDCSUnitName )
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
---
|
||||||
|
-- Maybe a UNIT?
|
||||||
if Event.IniObjectCategory == 1 then
|
---
|
||||||
|
|
||||||
|
-- Delete unit.
|
||||||
|
if self.UNITS[Event.IniDCSUnitName] then
|
||||||
|
self:T("STATIC Event for UNIT "..tostring(Event.IniDCSUnitName))
|
||||||
|
local DCSUnit = _DATABASE:FindUnit( Event.IniDCSUnitName )
|
||||||
|
self:T({DCSUnit})
|
||||||
|
if DCSUnit then
|
||||||
|
--self:I("Creating DEAD Event for UNIT "..tostring(Event.IniDCSUnitName))
|
||||||
|
--DCSUnit:Destroy(true)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
if Event.IniObjectCategory == 1 then
|
||||||
|
|
||||||
---
|
---
|
||||||
-- UNITS
|
-- UNITS
|
||||||
---
|
---
|
||||||
|
|
||||||
-- Delete unit.
|
-- Delete unit.
|
||||||
if self.UNITS[Event.IniDCSUnitName] then
|
if self.UNITS[Event.IniDCSUnitName] then
|
||||||
self:DeleteUnit(Event.IniDCSUnitName)
|
self:DeleteUnit(Event.IniDCSUnitName)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Remove client players.
|
-- Remove client players.
|
||||||
local client=self.CLIENTS[name] --Wrapper.Client#CLIENT
|
local client=self.CLIENTS[name] --Wrapper.Client#CLIENT
|
||||||
|
|
||||||
if client then
|
if client then
|
||||||
client:RemovePlayers()
|
client:RemovePlayers()
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Add airbase if it was spawned later in the mission.
|
-- Add airbase if it was spawned later in the mission.
|
||||||
local airbase=self.AIRBASES[Event.IniDCSUnitName] --Wrapper.Airbase#AIRBASE
|
local airbase=self.AIRBASES[Event.IniDCSUnitName] --Wrapper.Airbase#AIRBASE
|
||||||
if airbase and (airbase:IsHelipad() or airbase:IsShip()) then
|
if airbase and (airbase:IsHelipad() or airbase:IsShip()) then
|
||||||
self:DeleteAirbase(Event.IniDCSUnitName)
|
self:DeleteAirbase(Event.IniDCSUnitName)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Account destroys.
|
-- Account destroys.
|
||||||
@@ -1187,28 +1214,28 @@ function DATABASE:_EventOnPlayerEnterUnit( Event )
|
|||||||
|
|
||||||
if Event.IniDCSUnit then
|
if Event.IniDCSUnit then
|
||||||
if Event.IniObjectCategory == 1 then
|
if Event.IniObjectCategory == 1 then
|
||||||
|
|
||||||
-- Add unit.
|
-- Add unit.
|
||||||
self:AddUnit( Event.IniDCSUnitName )
|
self:AddUnit( Event.IniDCSUnitName )
|
||||||
|
|
||||||
-- Ini unit.
|
-- Ini unit.
|
||||||
Event.IniUnit = self:FindUnit( Event.IniDCSUnitName )
|
Event.IniUnit = self:FindUnit( Event.IniDCSUnitName )
|
||||||
|
|
||||||
-- Add group.
|
-- Add group.
|
||||||
self:AddGroup( Event.IniDCSGroupName )
|
self:AddGroup( Event.IniDCSGroupName )
|
||||||
|
|
||||||
-- Get player unit.
|
-- Get player unit.
|
||||||
local PlayerName = Event.IniDCSUnit:getPlayerName()
|
local PlayerName = Event.IniDCSUnit:getPlayerName()
|
||||||
|
|
||||||
if PlayerName then
|
if PlayerName then
|
||||||
|
|
||||||
if not self.PLAYERS[PlayerName] then
|
if not self.PLAYERS[PlayerName] then
|
||||||
self:AddPlayer( Event.IniDCSUnitName, PlayerName )
|
self:AddPlayer( Event.IniDCSUnitName, PlayerName )
|
||||||
end
|
end
|
||||||
|
|
||||||
local Settings = SETTINGS:Set( PlayerName )
|
local Settings = SETTINGS:Set( PlayerName )
|
||||||
Settings:SetPlayerMenu( Event.IniUnit )
|
Settings:SetPlayerMenu( Event.IniUnit )
|
||||||
|
|
||||||
else
|
else
|
||||||
self:E("ERROR: getPlayerName() returned nil for event PlayerEnterUnit")
|
self:E("ERROR: getPlayerName() returned nil for event PlayerEnterUnit")
|
||||||
end
|
end
|
||||||
@@ -1224,30 +1251,30 @@ function DATABASE:_EventOnPlayerLeaveUnit( Event )
|
|||||||
self:F2( { Event } )
|
self:F2( { Event } )
|
||||||
|
|
||||||
if Event.IniUnit then
|
if Event.IniUnit then
|
||||||
|
|
||||||
if Event.IniObjectCategory == 1 then
|
if Event.IniObjectCategory == 1 then
|
||||||
|
|
||||||
-- Try to get the player name. This can be buggy for multicrew aircraft!
|
-- Try to get the player name. This can be buggy for multicrew aircraft!
|
||||||
local PlayerName = Event.IniUnit:GetPlayerName()
|
local PlayerName = Event.IniUnit:GetPlayerName()
|
||||||
|
|
||||||
if PlayerName then --and self.PLAYERS[PlayerName] then
|
if PlayerName then --and self.PLAYERS[PlayerName] then
|
||||||
|
|
||||||
-- Debug info.
|
-- Debug info.
|
||||||
self:I(string.format("Player '%s' left unit %s", tostring(PlayerName), tostring(Event.IniUnitName)))
|
self:I(string.format("Player '%s' left unit %s", tostring(PlayerName), tostring(Event.IniUnitName)))
|
||||||
|
|
||||||
-- Remove player menu.
|
-- Remove player menu.
|
||||||
local Settings = SETTINGS:Set( PlayerName )
|
local Settings = SETTINGS:Set( PlayerName )
|
||||||
Settings:RemovePlayerMenu(Event.IniUnit)
|
Settings:RemovePlayerMenu(Event.IniUnit)
|
||||||
|
|
||||||
-- Delete player.
|
-- Delete player.
|
||||||
self:DeletePlayer(Event.IniUnit, PlayerName)
|
self:DeletePlayer(Event.IniUnit, PlayerName)
|
||||||
|
|
||||||
-- Client stuff.
|
-- Client stuff.
|
||||||
local client=self.CLIENTS[Event.IniDCSUnitName] --Wrapper.Client#CLIENT
|
local client=self.CLIENTS[Event.IniDCSUnitName] --Wrapper.Client#CLIENT
|
||||||
if client then
|
if client then
|
||||||
client:RemovePlayer(PlayerName)
|
client:RemovePlayer(PlayerName)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -1474,19 +1501,19 @@ function DATABASE:SetPlayerSettings( PlayerName, Settings )
|
|||||||
self.PLAYERSETTINGS[PlayerName] = Settings
|
self.PLAYERSETTINGS[PlayerName] = Settings
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Add a flight group to the data base.
|
--- Add an OPS group (FLIGHTGROUP, ARMYGROUP, NAVYGROUP) to the data base.
|
||||||
-- @param #DATABASE self
|
-- @param #DATABASE self
|
||||||
-- @param Ops.FlightGroup#FLIGHTGROUP flightgroup
|
-- @param Ops.OpsGroup#OPSGROUP opsgroup The OPS group added to the DB.
|
||||||
function DATABASE:AddFlightGroup(flightgroup)
|
function DATABASE:AddOpsGroup(opsgroup)
|
||||||
self:I({NewFlightGroup=flightgroup.groupname})
|
--env.info("Adding OPSGROUP "..tostring(opsgroup.groupname))
|
||||||
self.FLIGHTGROUPS[flightgroup.groupname]=flightgroup
|
self.FLIGHTGROUPS[opsgroup.groupname]=opsgroup
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get a flight group from the data base.
|
--- Get an OPS group (FLIGHTGROUP, ARMYGROUP, NAVYGROUP) from the data base.
|
||||||
-- @param #DATABASE self
|
-- @param #DATABASE self
|
||||||
-- @param #string groupname Group name of the flight group. Can also be passed as GROUP object.
|
-- @param #string groupname Group name of the group. Can also be passed as GROUP object.
|
||||||
-- @return Ops.FlightGroup#FLIGHTGROUP Flight group object.
|
-- @return Ops.OpsGroup#OPSGROUP OPS group object.
|
||||||
function DATABASE:GetFlightGroup(groupname)
|
function DATABASE:GetOpsGroup(groupname)
|
||||||
|
|
||||||
-- Get group and group name.
|
-- Get group and group name.
|
||||||
if type(groupname)=="string" then
|
if type(groupname)=="string" then
|
||||||
@@ -1494,9 +1521,53 @@ function DATABASE:GetFlightGroup(groupname)
|
|||||||
groupname=groupname:GetName()
|
groupname=groupname:GetName()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--env.info("Getting OPSGROUP "..tostring(groupname))
|
||||||
return self.FLIGHTGROUPS[groupname]
|
return self.FLIGHTGROUPS[groupname]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Find an OPSGROUP (FLIGHTGROUP, ARMYGROUP, NAVYGROUP) in the data base.
|
||||||
|
-- @param #DATABASE self
|
||||||
|
-- @param #string groupname Group name of the group. Can also be passed as GROUP object.
|
||||||
|
-- @return Ops.OpsGroup#OPSGROUP OPS group object.
|
||||||
|
function DATABASE:FindOpsGroup(groupname)
|
||||||
|
|
||||||
|
-- Get group and group name.
|
||||||
|
if type(groupname)=="string" then
|
||||||
|
else
|
||||||
|
groupname=groupname:GetName()
|
||||||
|
end
|
||||||
|
|
||||||
|
--env.info("Getting OPSGROUP "..tostring(groupname))
|
||||||
|
return self.FLIGHTGROUPS[groupname]
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Find an OPSGROUP (FLIGHTGROUP, ARMYGROUP, NAVYGROUP) in the data base for a given unit.
|
||||||
|
-- @param #DATABASE self
|
||||||
|
-- @param #string unitname Unit name. Can also be passed as UNIT object.
|
||||||
|
-- @return Ops.OpsGroup#OPSGROUP OPS group object.
|
||||||
|
function DATABASE:FindOpsGroupFromUnit(unitname)
|
||||||
|
|
||||||
|
local unit=nil --Wrapper.Unit#UNIT
|
||||||
|
local groupname
|
||||||
|
|
||||||
|
-- Get group and group name.
|
||||||
|
if type(unitname)=="string" then
|
||||||
|
unit=UNIT:FindByName(unitname)
|
||||||
|
else
|
||||||
|
unit=unitname
|
||||||
|
end
|
||||||
|
|
||||||
|
if unit then
|
||||||
|
groupname=unit:GetGroup():GetName()
|
||||||
|
end
|
||||||
|
|
||||||
|
if groupname then
|
||||||
|
return self.FLIGHTGROUPS[groupname]
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
--- Add a flight control to the data base.
|
--- Add a flight control to the data base.
|
||||||
-- @param #DATABASE self
|
-- @param #DATABASE self
|
||||||
-- @param Ops.FlightControl#FLIGHTCONTROL flightcontrol
|
-- @param Ops.FlightControl#FLIGHTCONTROL flightcontrol
|
||||||
@@ -1580,13 +1651,13 @@ function DATABASE:_RegisterTemplates()
|
|||||||
for group_num, Template in pairs(obj_type_data.group) do
|
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
|
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
|
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 --if GroupTemplate and GroupTemplate.units then
|
||||||
end --for group_num, GroupTemplate in pairs(obj_type_data.group) do
|
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
|
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
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
-- Within a running mission, various DCS events occur. Units are dynamically created, crash, die, shoot stuff, get hit etc.
|
-- Within a running mission, various DCS events occur. Units are dynamically created, crash, die, shoot stuff, get hit etc.
|
||||||
-- This module provides a mechanism to dispatch those events occuring within your running mission, to the different objects orchestrating your mission.
|
-- This module provides a mechanism to dispatch those events occurring within your running mission, to the different objects orchestrating your mission.
|
||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
@@ -32,11 +32,11 @@
|
|||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
-- There are 5 levels of kind of objects that the _EVENTDISPATCHER services:
|
-- There are 5 types/levels of objects that the _EVENTDISPATCHER services:
|
||||||
--
|
--
|
||||||
-- * _DATABASE object: The core of the MOOSE objects. Any object that is created, deleted or updated, is done in this database.
|
-- * _DATABASE object: The core of the MOOSE objects. Any object that is created, deleted or updated, is done in this database.
|
||||||
-- * SET_ derived classes: Subsets of the _DATABASE object. These subsets are updated by the _EVENTDISPATCHER as the second priority.
|
-- * SET_ derived classes: These are subsets of the _DATABASE object. These subsets are updated by the _EVENTDISPATCHER as the second priority.
|
||||||
-- * UNIT objects: UNIT objects can subscribe to DCS events. Each DCS event will be directly published to teh subscribed UNIT object.
|
-- * UNIT objects: UNIT objects can subscribe to DCS events. Each DCS event will be directly published to the subscribed UNIT object.
|
||||||
-- * GROUP objects: GROUP objects can subscribe to DCS events. Each DCS event will be directly published to the subscribed GROUP object.
|
-- * GROUP objects: GROUP objects can subscribe to DCS events. Each DCS event will be directly published to the subscribed GROUP object.
|
||||||
-- * Any other object: Various other objects can subscribe to DCS events. Each DCS event triggered will be published to each subscribed object.
|
-- * Any other object: Various other objects can subscribe to DCS events. Each DCS event triggered will be published to each subscribed object.
|
||||||
--
|
--
|
||||||
@@ -983,13 +983,12 @@ end
|
|||||||
-- @param #EVENTDATA Event Event data table.
|
-- @param #EVENTDATA Event Event data table.
|
||||||
function EVENT:onEvent( Event )
|
function EVENT:onEvent( Event )
|
||||||
|
|
||||||
|
--- Function to handle errors.
|
||||||
local ErrorHandler = function( errmsg )
|
local ErrorHandler = function( errmsg )
|
||||||
|
|
||||||
env.info( "Error in SCHEDULER function:" .. errmsg )
|
env.info( "Error in SCHEDULER function:" .. errmsg )
|
||||||
if BASE.Debug ~= nil then
|
if BASE.Debug ~= nil then
|
||||||
env.info( debug.traceback() )
|
env.info( debug.traceback() )
|
||||||
end
|
end
|
||||||
|
|
||||||
return errmsg
|
return errmsg
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1002,6 +1001,7 @@ function EVENT:onEvent( Event )
|
|||||||
|
|
||||||
if self and self.Events and self.Events[Event.id] and self.MissionEnd==false and (Event.initiator~=nil or (Event.initiator==nil and Event.id~=EVENTS.PlayerLeaveUnit)) then
|
if self and self.Events and self.Events[Event.id] and self.MissionEnd==false and (Event.initiator~=nil or (Event.initiator==nil and Event.id~=EVENTS.PlayerLeaveUnit)) then
|
||||||
|
|
||||||
|
-- Check if mission has ended.
|
||||||
if Event.id and Event.id == EVENTS.MissionEnd then
|
if Event.id and Event.id == EVENTS.MissionEnd then
|
||||||
self.MissionEnd = true
|
self.MissionEnd = true
|
||||||
end
|
end
|
||||||
@@ -1009,35 +1009,12 @@ function EVENT:onEvent( Event )
|
|||||||
if Event.initiator then
|
if Event.initiator then
|
||||||
|
|
||||||
Event.IniObjectCategory = Event.initiator:getCategory()
|
Event.IniObjectCategory = Event.initiator:getCategory()
|
||||||
|
|
||||||
if Event.IniObjectCategory == Object.Category.UNIT then
|
if Event.IniObjectCategory == Object.Category.STATIC then
|
||||||
Event.IniDCSUnit = Event.initiator
|
---
|
||||||
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
|
-- Static
|
||||||
Event.IniUnitName = Event.IniDCSUnitName
|
---
|
||||||
Event.IniDCSGroup = Event.IniDCSUnit:getGroup()
|
|
||||||
Event.IniUnit = UNIT:FindByName( Event.IniDCSUnitName )
|
|
||||||
if not Event.IniUnit then
|
|
||||||
-- Unit can be a CLIENT. Most likely this will be the case ...
|
|
||||||
Event.IniUnit = CLIENT:FindByName( Event.IniDCSUnitName, '', true )
|
|
||||||
end
|
|
||||||
Event.IniDCSGroupName = ""
|
|
||||||
if Event.IniDCSGroup and Event.IniDCSGroup:isExist() then
|
|
||||||
Event.IniDCSGroupName = Event.IniDCSGroup:getName()
|
|
||||||
Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName )
|
|
||||||
--if Event.IniGroup then
|
|
||||||
Event.IniGroupName = Event.IniDCSGroupName
|
|
||||||
--end
|
|
||||||
end
|
|
||||||
Event.IniPlayerName = Event.IniDCSUnit:getPlayerName()
|
|
||||||
Event.IniCoalition = Event.IniDCSUnit:getCoalition()
|
|
||||||
Event.IniTypeName = Event.IniDCSUnit:getTypeName()
|
|
||||||
Event.IniCategory = Event.IniDCSUnit:getDesc().category
|
|
||||||
end
|
|
||||||
|
|
||||||
if Event.IniObjectCategory == Object.Category.STATIC then
|
|
||||||
|
|
||||||
if Event.id==31 then
|
if Event.id==31 then
|
||||||
|
|
||||||
-- Event.initiator is a Static object representing the pilot. But getName() errors due to DCS bug.
|
-- Event.initiator is a Static object representing the pilot. But getName() errors due to DCS bug.
|
||||||
Event.IniDCSUnit = Event.initiator
|
Event.IniDCSUnit = Event.initiator
|
||||||
local ID=Event.initiator.id_
|
local ID=Event.initiator.id_
|
||||||
@@ -1063,9 +1040,47 @@ function EVENT:onEvent( Event )
|
|||||||
Event.IniCategory = Event.IniDCSUnit:getDesc().category
|
Event.IniCategory = Event.IniDCSUnit:getDesc().category
|
||||||
Event.IniTypeName = Event.IniDCSUnit:getTypeName()
|
Event.IniTypeName = Event.IniDCSUnit:getTypeName()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Dead events of units can be delayed and the initiator changed to a static.
|
||||||
|
-- Take care of that.
|
||||||
|
local Unit=UNIT:FindByName(Event.IniDCSUnitName)
|
||||||
|
if Unit then
|
||||||
|
Event.IniObjectCategory = Object.Category.UNIT
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if Event.IniObjectCategory == Object.Category.UNIT then
|
||||||
|
---
|
||||||
|
-- Unit
|
||||||
|
---
|
||||||
|
Event.IniDCSUnit = Event.initiator
|
||||||
|
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
|
||||||
|
Event.IniUnitName = Event.IniDCSUnitName
|
||||||
|
Event.IniDCSGroup = Event.IniDCSUnit:getGroup()
|
||||||
|
Event.IniUnit = UNIT:FindByName( Event.IniDCSUnitName )
|
||||||
|
|
||||||
|
if not Event.IniUnit then
|
||||||
|
-- Unit can be a CLIENT. Most likely this will be the case ...
|
||||||
|
Event.IniUnit = CLIENT:FindByName( Event.IniDCSUnitName, '', true )
|
||||||
|
end
|
||||||
|
|
||||||
|
Event.IniDCSGroupName = Event.IniUnit and Event.IniUnit.GroupName or ""
|
||||||
|
if Event.IniDCSGroup and Event.IniDCSGroup:isExist() then
|
||||||
|
Event.IniDCSGroupName = Event.IniDCSGroup:getName()
|
||||||
|
Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName )
|
||||||
|
Event.IniGroupName = Event.IniDCSGroupName
|
||||||
|
end
|
||||||
|
|
||||||
|
Event.IniPlayerName = Event.IniDCSUnit:getPlayerName()
|
||||||
|
Event.IniCoalition = Event.IniDCSUnit:getCoalition()
|
||||||
|
Event.IniTypeName = Event.IniDCSUnit:getTypeName()
|
||||||
|
Event.IniCategory = Event.IniDCSUnit:getDesc().category
|
||||||
end
|
end
|
||||||
|
|
||||||
if Event.IniObjectCategory == Object.Category.CARGO then
|
if Event.IniObjectCategory == Object.Category.CARGO then
|
||||||
|
---
|
||||||
|
-- Cargo
|
||||||
|
---
|
||||||
Event.IniDCSUnit = Event.initiator
|
Event.IniDCSUnit = Event.initiator
|
||||||
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
|
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
|
||||||
Event.IniUnitName = Event.IniDCSUnitName
|
Event.IniUnitName = Event.IniDCSUnitName
|
||||||
@@ -1076,15 +1091,22 @@ function EVENT:onEvent( Event )
|
|||||||
end
|
end
|
||||||
|
|
||||||
if Event.IniObjectCategory == Object.Category.SCENERY then
|
if Event.IniObjectCategory == Object.Category.SCENERY then
|
||||||
|
---
|
||||||
|
-- Scenery
|
||||||
|
---
|
||||||
|
|
||||||
Event.IniDCSUnit = Event.initiator
|
Event.IniDCSUnit = Event.initiator
|
||||||
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
|
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
|
||||||
Event.IniUnitName = Event.IniDCSUnitName
|
Event.IniUnitName = Event.IniDCSUnitName
|
||||||
Event.IniUnit = SCENERY:Register( Event.IniDCSUnitName, Event.initiator )
|
Event.IniUnit = SCENERY:Register( Event.IniDCSUnitName, Event.initiator )
|
||||||
Event.IniCategory = Event.IniDCSUnit:getDesc().category
|
Event.IniCategory = Event.IniDCSUnit:getDesc().category
|
||||||
Event.IniTypeName = Event.initiator:isExist() and Event.IniDCSUnit:getTypeName() or "SCENERY" -- TODO: Bug fix for 2.1!
|
Event.IniTypeName = Event.initiator:isExist() and Event.IniDCSUnit:getTypeName() or "SCENERY"
|
||||||
end
|
end
|
||||||
|
|
||||||
if Event.IniObjectCategory == Object.Category.BASE then
|
if Event.IniObjectCategory == Object.Category.BASE then
|
||||||
|
---
|
||||||
|
-- Base Object
|
||||||
|
---
|
||||||
Event.IniDCSUnit = Event.initiator
|
Event.IniDCSUnit = Event.initiator
|
||||||
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
|
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
|
||||||
Event.IniUnitName = Event.IniDCSUnitName
|
Event.IniUnitName = Event.IniDCSUnitName
|
||||||
@@ -1096,7 +1118,12 @@ function EVENT:onEvent( Event )
|
|||||||
end
|
end
|
||||||
|
|
||||||
if Event.target then
|
if Event.target then
|
||||||
|
|
||||||
|
---
|
||||||
|
-- TARGET
|
||||||
|
---
|
||||||
|
|
||||||
|
-- Target category.
|
||||||
Event.TgtObjectCategory = Event.target:getCategory()
|
Event.TgtObjectCategory = Event.target:getCategory()
|
||||||
|
|
||||||
if Event.TgtObjectCategory == Object.Category.UNIT then
|
if Event.TgtObjectCategory == Object.Category.UNIT then
|
||||||
@@ -1109,9 +1136,7 @@ function EVENT:onEvent( Event )
|
|||||||
if Event.TgtDCSGroup and Event.TgtDCSGroup:isExist() then
|
if Event.TgtDCSGroup and Event.TgtDCSGroup:isExist() then
|
||||||
Event.TgtDCSGroupName = Event.TgtDCSGroup:getName()
|
Event.TgtDCSGroupName = Event.TgtDCSGroup:getName()
|
||||||
Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName )
|
Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName )
|
||||||
--if Event.TgtGroup then
|
Event.TgtGroupName = Event.TgtDCSGroupName
|
||||||
Event.TgtGroupName = Event.TgtDCSGroupName
|
|
||||||
--end
|
|
||||||
end
|
end
|
||||||
Event.TgtPlayerName = Event.TgtDCSUnit:getPlayerName()
|
Event.TgtPlayerName = Event.TgtDCSUnit:getPlayerName()
|
||||||
Event.TgtCoalition = Event.TgtDCSUnit:getCoalition()
|
Event.TgtCoalition = Event.TgtDCSUnit:getCoalition()
|
||||||
@@ -1159,6 +1184,7 @@ function EVENT:onEvent( Event )
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Weapon.
|
||||||
if Event.weapon then
|
if Event.weapon then
|
||||||
Event.Weapon = Event.weapon
|
Event.Weapon = Event.weapon
|
||||||
Event.WeaponName = Event.Weapon:getTypeName()
|
Event.WeaponName = Event.Weapon:getTypeName()
|
||||||
@@ -1193,23 +1219,22 @@ function EVENT:onEvent( Event )
|
|||||||
Event.MarkGroupID = Event.groupID
|
Event.MarkGroupID = Event.groupID
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Cargo object.
|
||||||
if Event.cargo then
|
if Event.cargo then
|
||||||
Event.Cargo = Event.cargo
|
Event.Cargo = Event.cargo
|
||||||
Event.CargoName = Event.cargo.Name
|
Event.CargoName = Event.cargo.Name
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Zone object.
|
||||||
if Event.zone then
|
if Event.zone then
|
||||||
Event.Zone = Event.zone
|
Event.Zone = Event.zone
|
||||||
Event.ZoneName = Event.zone.ZoneName
|
Event.ZoneName = Event.zone.ZoneName
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Priority order.
|
||||||
local PriorityOrder = EventMeta.Order
|
local PriorityOrder = EventMeta.Order
|
||||||
local PriorityBegin = PriorityOrder == -1 and 5 or 1
|
local PriorityBegin = PriorityOrder == -1 and 5 or 1
|
||||||
local PriorityEnd = PriorityOrder == -1 and 1 or 5
|
local PriorityEnd = PriorityOrder == -1 and 1 or 5
|
||||||
|
|
||||||
if Event.IniObjectCategory ~= Object.Category.STATIC then
|
|
||||||
self:F( { EventMeta.Text, Event, Event.IniDCSUnitName, Event.TgtDCSUnitName, PriorityOrder } )
|
|
||||||
end
|
|
||||||
|
|
||||||
for EventPriority = PriorityBegin, PriorityEnd, PriorityOrder do
|
for EventPriority = PriorityBegin, PriorityEnd, PriorityOrder do
|
||||||
|
|
||||||
@@ -1222,8 +1247,8 @@ function EVENT:onEvent( Event )
|
|||||||
-- self:E( { "Evaluating: ", EventClass:GetClassNameAndID() } )
|
-- self:E( { "Evaluating: ", EventClass:GetClassNameAndID() } )
|
||||||
--end
|
--end
|
||||||
|
|
||||||
Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName )
|
Event.IniGroup = Event.IniGroup or GROUP:FindByName( Event.IniDCSGroupName )
|
||||||
Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName )
|
Event.TgtGroup = Event.TgtGroup or GROUP:FindByName( Event.TgtDCSGroupName )
|
||||||
|
|
||||||
-- If the EventData is for a UNIT, the call directly the EventClass EventFunction for that UNIT.
|
-- If the EventData is for a UNIT, the call directly the EventClass EventFunction for that UNIT.
|
||||||
if EventData.EventUnit then
|
if EventData.EventUnit then
|
||||||
@@ -1233,20 +1258,17 @@ function EVENT:onEvent( Event )
|
|||||||
Event.id == EVENTS.PlayerEnterUnit or
|
Event.id == EVENTS.PlayerEnterUnit or
|
||||||
Event.id == EVENTS.Crash or
|
Event.id == EVENTS.Crash or
|
||||||
Event.id == EVENTS.Dead or
|
Event.id == EVENTS.Dead or
|
||||||
Event.id == EVENTS.RemoveUnit then
|
Event.id == EVENTS.RemoveUnit or
|
||||||
|
Event.id == EVENTS.UnitLost then
|
||||||
|
|
||||||
local UnitName = EventClass:GetName()
|
local UnitName = EventClass:GetName()
|
||||||
|
|
||||||
if ( EventMeta.Side == "I" and UnitName == Event.IniDCSUnitName ) or
|
if ( EventMeta.Side == "I" and UnitName == Event.IniDCSUnitName ) or
|
||||||
( EventMeta.Side == "T" and UnitName == Event.TgtDCSUnitName ) then
|
( EventMeta.Side == "T" and UnitName == Event.TgtDCSUnitName ) then
|
||||||
|
|
||||||
-- First test if a EventFunction is Set, otherwise search for the default function
|
-- First test if a EventFunction is Set, otherwise search for the default function
|
||||||
if EventData.EventFunction then
|
if EventData.EventFunction then
|
||||||
|
|
||||||
if Event.IniObjectCategory ~= 3 then
|
|
||||||
self:F( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } )
|
|
||||||
end
|
|
||||||
|
|
||||||
local Result, Value = xpcall(
|
local Result, Value = xpcall(
|
||||||
function()
|
function()
|
||||||
return EventData.EventFunction( EventClass, Event )
|
return EventData.EventFunction( EventClass, Event )
|
||||||
@@ -1259,15 +1281,12 @@ function EVENT:onEvent( Event )
|
|||||||
if EventFunction and type( EventFunction ) == "function" then
|
if EventFunction and type( EventFunction ) == "function" then
|
||||||
|
|
||||||
-- Now call the default event function.
|
-- Now call the default event function.
|
||||||
if Event.IniObjectCategory ~= 3 then
|
|
||||||
self:F( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
|
||||||
end
|
|
||||||
|
|
||||||
local Result, Value = xpcall(
|
local Result, Value = xpcall(
|
||||||
function()
|
function()
|
||||||
return EventFunction( EventClass, Event )
|
return EventFunction( EventClass, Event )
|
||||||
end, ErrorHandler )
|
end, ErrorHandler )
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@@ -1285,7 +1304,8 @@ function EVENT:onEvent( Event )
|
|||||||
Event.id == EVENTS.PlayerEnterUnit or
|
Event.id == EVENTS.PlayerEnterUnit or
|
||||||
Event.id == EVENTS.Crash or
|
Event.id == EVENTS.Crash or
|
||||||
Event.id == EVENTS.Dead or
|
Event.id == EVENTS.Dead or
|
||||||
Event.id == EVENTS.RemoveUnit then
|
Event.id == EVENTS.RemoveUnit or
|
||||||
|
Event.id == EVENTS.UnitLost then
|
||||||
|
|
||||||
-- We can get the name of the EventClass, which is now always a GROUP object.
|
-- We can get the name of the EventClass, which is now always a GROUP object.
|
||||||
local GroupName = EventClass:GetName()
|
local GroupName = EventClass:GetName()
|
||||||
@@ -1296,10 +1316,6 @@ function EVENT:onEvent( Event )
|
|||||||
-- First test if a EventFunction is Set, otherwise search for the default function
|
-- First test if a EventFunction is Set, otherwise search for the default function
|
||||||
if EventData.EventFunction then
|
if EventData.EventFunction then
|
||||||
|
|
||||||
if Event.IniObjectCategory ~= 3 then
|
|
||||||
self:F( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } )
|
|
||||||
end
|
|
||||||
|
|
||||||
local Result, Value = xpcall(
|
local Result, Value = xpcall(
|
||||||
function()
|
function()
|
||||||
return EventData.EventFunction( EventClass, Event, unpack( EventData.Params ) )
|
return EventData.EventFunction( EventClass, Event, unpack( EventData.Params ) )
|
||||||
@@ -1312,10 +1328,6 @@ function EVENT:onEvent( Event )
|
|||||||
if EventFunction and type( EventFunction ) == "function" then
|
if EventFunction and type( EventFunction ) == "function" then
|
||||||
|
|
||||||
-- Now call the default event function.
|
-- Now call the default event function.
|
||||||
if Event.IniObjectCategory ~= 3 then
|
|
||||||
self:F( { "Calling " .. EventMeta.Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } )
|
|
||||||
end
|
|
||||||
|
|
||||||
local Result, Value = xpcall(
|
local Result, Value = xpcall(
|
||||||
function()
|
function()
|
||||||
return EventFunction( EventClass, Event, unpack( EventData.Params ) )
|
return EventFunction( EventClass, Event, unpack( EventData.Params ) )
|
||||||
@@ -1328,7 +1340,7 @@ function EVENT:onEvent( Event )
|
|||||||
--self:RemoveEvent( EventClass, Event.id )
|
--self:RemoveEvent( EventClass, Event.id )
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
|
||||||
-- If the EventData is not bound to a specific unit, then call the EventClass EventFunction.
|
-- If the EventData is not bound to a specific unit, then call the EventClass EventFunction.
|
||||||
-- Note that here the EventFunction will need to implement and determine the logic for the relevant source- or target unit, or weapon.
|
-- Note that here the EventFunction will need to implement and determine the logic for the relevant source- or target unit, or weapon.
|
||||||
if not EventData.EventUnit then
|
if not EventData.EventUnit then
|
||||||
@@ -1337,9 +1349,6 @@ function EVENT:onEvent( Event )
|
|||||||
if EventData.EventFunction then
|
if EventData.EventFunction then
|
||||||
|
|
||||||
-- There is an EventFunction defined, so call the EventFunction.
|
-- There is an EventFunction defined, so call the EventFunction.
|
||||||
if Event.IniObjectCategory ~= 3 then
|
|
||||||
self:F2( { "Calling EventFunction for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
|
||||||
end
|
|
||||||
local Result, Value = xpcall(
|
local Result, Value = xpcall(
|
||||||
function()
|
function()
|
||||||
return EventData.EventFunction( EventClass, Event )
|
return EventData.EventFunction( EventClass, Event )
|
||||||
@@ -1351,16 +1360,14 @@ function EVENT:onEvent( Event )
|
|||||||
if EventFunction and type( EventFunction ) == "function" then
|
if EventFunction and type( EventFunction ) == "function" then
|
||||||
|
|
||||||
-- Now call the default event function.
|
-- Now call the default event function.
|
||||||
if Event.IniObjectCategory ~= 3 then
|
|
||||||
self:F2( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
|
||||||
end
|
|
||||||
|
|
||||||
local Result, Value = xpcall(
|
local Result, Value = xpcall(
|
||||||
function()
|
function()
|
||||||
local Result, Value = EventFunction( EventClass, Event )
|
local Result, Value = EventFunction( EventClass, Event )
|
||||||
return Result, Value
|
return Result, Value
|
||||||
end, ErrorHandler )
|
end, ErrorHandler )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,89 +1,87 @@
|
|||||||
--- **Core** - Models the process to achieve goal(s).
|
--- **Core** - Models the process to achieve goal(s).
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ## Features:
|
-- ## Features:
|
||||||
--
|
--
|
||||||
-- * Define the goal.
|
-- * Define the goal.
|
||||||
-- * Monitor the goal achievement.
|
-- * Monitor the goal achievement.
|
||||||
-- * Manage goal contribution by players.
|
-- * Manage goal contribution by players.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- Classes that implement a goal achievement, will derive from GOAL to implement the ways how the achievements can be realized.
|
-- Classes that implement a goal achievement, will derive from GOAL to implement the ways how the achievements can be realized.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ### Author: **FlightControl**
|
-- ### Author: **FlightControl**
|
||||||
-- ### Contributions: **funkyfranky**
|
-- ### Contributions: **funkyfranky**
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- @module Core.Goal
|
-- @module Core.Goal
|
||||||
-- @image Core_Goal.JPG
|
-- @image Core_Goal.JPG
|
||||||
|
|
||||||
|
|
||||||
do -- Goal
|
do -- Goal
|
||||||
|
|
||||||
--- @type GOAL
|
--- @type GOAL
|
||||||
-- @extends Core.Fsm#FSM
|
-- @extends Core.Fsm#FSM
|
||||||
|
|
||||||
|
|
||||||
--- Models processes that have an objective with a defined achievement. Derived classes implement the ways how the achievements can be realized.
|
--- Models processes that have an objective with a defined achievement. Derived classes implement the ways how the achievements can be realized.
|
||||||
--
|
--
|
||||||
-- # 1. GOAL constructor
|
-- # 1. GOAL constructor
|
||||||
--
|
--
|
||||||
-- * @{#GOAL.New}(): Creates a new GOAL object.
|
-- * @{#GOAL.New}(): Creates a new GOAL object.
|
||||||
--
|
--
|
||||||
-- # 2. GOAL is a finite state machine (FSM).
|
-- # 2. GOAL is a finite state machine (FSM).
|
||||||
--
|
--
|
||||||
-- ## 2.1. GOAL States
|
-- ## 2.1. GOAL States
|
||||||
--
|
--
|
||||||
-- * **Pending**: The goal object is in progress.
|
-- * **Pending**: The goal object is in progress.
|
||||||
-- * **Achieved**: The goal objective is Achieved.
|
-- * **Achieved**: The goal objective is Achieved.
|
||||||
--
|
--
|
||||||
-- ## 2.2. GOAL Events
|
-- ## 2.2. GOAL Events
|
||||||
--
|
--
|
||||||
-- * **Achieved**: Set the goal objective to Achieved.
|
-- * **Achieved**: Set the goal objective to Achieved.
|
||||||
--
|
--
|
||||||
-- # 3. Player contributions.
|
-- # 3. Player contributions.
|
||||||
--
|
--
|
||||||
-- Goals are most of the time achieved by players. These player achievements can be registered as part of the goal achievement.
|
-- Goals are most of the time achieved by players. These player achievements can be registered as part of the goal achievement.
|
||||||
-- Use @{#GOAL.AddPlayerContribution}() to add a player contribution to the goal.
|
-- Use @{#GOAL.AddPlayerContribution}() to add a player contribution to the goal.
|
||||||
-- The player contributions are based on a points system, an internal counter per player.
|
-- The player contributions are based on a points system, an internal counter per player.
|
||||||
-- So once the goal has been achieved, the player contributions can be queried using @{#GOAL.GetPlayerContributions}(),
|
-- So once the goal has been achieved, the player contributions can be queried using @{#GOAL.GetPlayerContributions}(),
|
||||||
-- that retrieves all contributions done by the players. For one player, the contribution can be queried using @{#GOAL.GetPlayerContribution}().
|
-- that retrieves all contributions done by the players. For one player, the contribution can be queried using @{#GOAL.GetPlayerContribution}().
|
||||||
-- The total amount of player contributions can be queried using @{#GOAL.GetTotalContributions}().
|
-- The total amount of player contributions can be queried using @{#GOAL.GetTotalContributions}().
|
||||||
--
|
--
|
||||||
-- # 4. Goal achievement.
|
-- # 4. Goal achievement.
|
||||||
--
|
--
|
||||||
-- Once the goal is achieved, the mission designer will need to trigger the goal achievement using the **Achieved** event.
|
-- Once the goal is achieved, the mission designer will need to trigger the goal achievement using the **Achieved** event.
|
||||||
-- The underlying 2 examples will achieve the goals for the `Goal` object:
|
-- The underlying 2 examples will achieve the goals for the `Goal` object:
|
||||||
--
|
--
|
||||||
-- Goal:Achieved() -- Achieve the goal immediately.
|
-- Goal:Achieved() -- Achieve the goal immediately.
|
||||||
-- Goal:__Achieved( 30 ) -- Achieve the goal within 30 seconds.
|
-- Goal:__Achieved( 30 ) -- Achieve the goal within 30 seconds.
|
||||||
--
|
--
|
||||||
-- # 5. Check goal achievement.
|
-- # 5. Check goal achievement.
|
||||||
--
|
--
|
||||||
-- The method @{#GOAL.IsAchieved}() will return true if the goal is achieved (the trigger **Achieved** was executed).
|
-- The method @{#GOAL.IsAchieved}() will return true if the goal is achieved (the trigger **Achieved** was executed).
|
||||||
-- You can use this method to check asynchronously if a goal has been achieved, for example using a scheduler.
|
-- You can use this method to check asynchronously if a goal has been achieved, for example using a scheduler.
|
||||||
--
|
--
|
||||||
-- @field #GOAL
|
-- @field #GOAL
|
||||||
GOAL = {
|
GOAL = {
|
||||||
ClassName = "GOAL",
|
ClassName = "GOAL",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- @field #table GOAL.Players
|
--- @field #table GOAL.Players
|
||||||
GOAL.Players = {}
|
GOAL.Players = {}
|
||||||
|
|
||||||
--- @field #number GOAL.TotalContributions
|
--- @field #number GOAL.TotalContributions
|
||||||
GOAL.TotalContributions = 0
|
GOAL.TotalContributions = 0
|
||||||
|
|
||||||
--- GOAL Constructor.
|
--- GOAL Constructor.
|
||||||
-- @param #GOAL self
|
-- @param #GOAL self
|
||||||
-- @return #GOAL
|
-- @return #GOAL
|
||||||
function GOAL:New()
|
function GOAL:New()
|
||||||
|
|
||||||
local self = BASE:Inherit( self, FSM:New() ) -- #GOAL
|
local self = BASE:Inherit( self, FSM:New() ) -- #GOAL
|
||||||
self:F( {} )
|
self:F( {} )
|
||||||
|
|
||||||
@@ -104,11 +102,10 @@ do -- Goal
|
|||||||
-- @param #string From
|
-- @param #string From
|
||||||
-- @param #string Event
|
-- @param #string Event
|
||||||
-- @param #string To
|
-- @param #string To
|
||||||
|
|
||||||
|
|
||||||
self:SetStartState( "Pending" )
|
self:SetStartState( "Pending" )
|
||||||
self:AddTransition( "*", "Achieved", "Achieved" )
|
self:AddTransition( "*", "Achieved", "Achieved" )
|
||||||
|
|
||||||
--- Achieved Handler OnBefore for GOAL
|
--- Achieved Handler OnBefore for GOAL
|
||||||
-- @function [parent=#GOAL] OnBeforeAchieved
|
-- @function [parent=#GOAL] OnBeforeAchieved
|
||||||
-- @param #GOAL self
|
-- @param #GOAL self
|
||||||
@@ -116,47 +113,44 @@ do -- Goal
|
|||||||
-- @param #string Event
|
-- @param #string Event
|
||||||
-- @param #string To
|
-- @param #string To
|
||||||
-- @return #boolean
|
-- @return #boolean
|
||||||
|
|
||||||
--- Achieved Handler OnAfter for GOAL
|
--- Achieved Handler OnAfter for GOAL
|
||||||
-- @function [parent=#GOAL] OnAfterAchieved
|
-- @function [parent=#GOAL] OnAfterAchieved
|
||||||
-- @param #GOAL self
|
-- @param #GOAL self
|
||||||
-- @param #string From
|
-- @param #string From
|
||||||
-- @param #string Event
|
-- @param #string Event
|
||||||
-- @param #string To
|
-- @param #string To
|
||||||
|
|
||||||
--- Achieved Trigger for GOAL
|
--- Achieved Trigger for GOAL
|
||||||
-- @function [parent=#GOAL] Achieved
|
-- @function [parent=#GOAL] Achieved
|
||||||
-- @param #GOAL self
|
-- @param #GOAL self
|
||||||
|
|
||||||
--- Achieved Asynchronous Trigger for GOAL
|
--- Achieved Asynchronous Trigger for GOAL
|
||||||
-- @function [parent=#GOAL] __Achieved
|
-- @function [parent=#GOAL] __Achieved
|
||||||
-- @param #GOAL self
|
-- @param #GOAL self
|
||||||
-- @param #number Delay
|
-- @param #number Delay
|
||||||
|
|
||||||
self:SetEventPriority( 5 )
|
self:SetEventPriority( 5 )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Add a new contribution by a player.
|
--- Add a new contribution by a player.
|
||||||
-- @param #GOAL self
|
-- @param #GOAL self
|
||||||
-- @param #string PlayerName The name of the player.
|
-- @param #string PlayerName The name of the player.
|
||||||
function GOAL:AddPlayerContribution( PlayerName )
|
function GOAL:AddPlayerContribution( PlayerName )
|
||||||
self:F({PlayerName})
|
self:F( { PlayerName } )
|
||||||
self.Players[PlayerName] = self.Players[PlayerName] or 0
|
self.Players[PlayerName] = self.Players[PlayerName] or 0
|
||||||
self.Players[PlayerName] = self.Players[PlayerName] + 1
|
self.Players[PlayerName] = self.Players[PlayerName] + 1
|
||||||
self.TotalContributions = self.TotalContributions + 1
|
self.TotalContributions = self.TotalContributions + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- @param #GOAL self
|
--- @param #GOAL self
|
||||||
-- @param #number Player contribution.
|
-- @param #number Player contribution.
|
||||||
function GOAL:GetPlayerContribution( PlayerName )
|
function GOAL:GetPlayerContribution( PlayerName )
|
||||||
return self.Players[PlayerName] or 0
|
return self.Players[PlayerName] or 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Get the players who contributed to achieve the goal.
|
--- Get the players who contributed to achieve the goal.
|
||||||
-- The result is a list of players, sorted by the name of the players.
|
-- The result is a list of players, sorted by the name of the players.
|
||||||
-- @param #GOAL self
|
-- @param #GOAL self
|
||||||
@@ -165,7 +159,6 @@ do -- Goal
|
|||||||
return self.Players or {}
|
return self.Players or {}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Gets the total contributions that happened to achieve the goal.
|
--- Gets the total contributions that happened to achieve the goal.
|
||||||
-- The result is a number.
|
-- The result is a number.
|
||||||
-- @param #GOAL self
|
-- @param #GOAL self
|
||||||
@@ -173,9 +166,7 @@ do -- Goal
|
|||||||
function GOAL:GetTotalContributions()
|
function GOAL:GetTotalContributions()
|
||||||
return self.TotalContributions or 0
|
return self.TotalContributions or 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- Validates if the goal is achieved.
|
--- Validates if the goal is achieved.
|
||||||
-- @param #GOAL self
|
-- @param #GOAL self
|
||||||
-- @return #boolean true if the goal is achieved.
|
-- @return #boolean true if the goal is achieved.
|
||||||
@@ -183,4 +174,4 @@ do -- Goal
|
|||||||
return self:Is( "Achieved" )
|
return self:Is( "Achieved" )
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
267
Moose Development/Moose/Core/MarkerOps_Base.lua
Normal file
267
Moose Development/Moose/Core/MarkerOps_Base.lua
Normal file
@@ -0,0 +1,267 @@
|
|||||||
|
--- **Core** - MarkerOps_Base.
|
||||||
|
--
|
||||||
|
-- **Main Features:**
|
||||||
|
--
|
||||||
|
-- * Create an easy way to tap into markers added to the F10 map by users.
|
||||||
|
-- * Recognize own tag and list of keywords.
|
||||||
|
-- * Matched keywords are handed down to functions.
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ### Author: **Applevangelist**
|
||||||
|
--
|
||||||
|
-- Date: 5 May 2021
|
||||||
|
-- Last Update: Sep 2022
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
---
|
||||||
|
-- @module Core.MarkerOps_Base
|
||||||
|
-- @image MOOSE_Core.JPG
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------
|
||||||
|
-- MARKEROPS_BASE Class Definition.
|
||||||
|
--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- MARKEROPS_BASE class.
|
||||||
|
-- @type MARKEROPS_BASE
|
||||||
|
-- @field #string ClassName Name of the class.
|
||||||
|
-- @field #string Tag Tag to identify commands.
|
||||||
|
-- @field #table Keywords Table of keywords to recognize.
|
||||||
|
-- @field #string version Version of #MARKEROPS_BASE.
|
||||||
|
-- @field #boolean Casesensitive Enforce case when identifying the Tag, i.e. tag ~= Tag
|
||||||
|
-- @extends Core.Fsm#FSM
|
||||||
|
|
||||||
|
--- *Fiat lux.* -- Latin proverb.
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- # The MARKEROPS_BASE Concept
|
||||||
|
--
|
||||||
|
-- This class enable scripting text-based actions from markers.
|
||||||
|
--
|
||||||
|
-- @field #MARKEROPS_BASE
|
||||||
|
MARKEROPS_BASE = {
|
||||||
|
ClassName = "MARKEROPS",
|
||||||
|
Tag = "mytag",
|
||||||
|
Keywords = {},
|
||||||
|
version = "0.1.0",
|
||||||
|
debug = false,
|
||||||
|
Casesensitive = true,
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Function to instantiate a new #MARKEROPS_BASE object.
|
||||||
|
-- @param #MARKEROPS_BASE self
|
||||||
|
-- @param #string Tagname Name to identify us from the event text.
|
||||||
|
-- @param #table Keywords Table of keywords recognized from the event text.
|
||||||
|
-- @param #boolean Casesensitive (Optional) Switch case sensitive identification of Tagname. Defaults to true.
|
||||||
|
-- @return #MARKEROPS_BASE self
|
||||||
|
function MARKEROPS_BASE:New(Tagname,Keywords,Casesensitive)
|
||||||
|
-- Inherit FSM
|
||||||
|
local self=BASE:Inherit(self, FSM:New()) -- #MARKEROPS_BASE
|
||||||
|
|
||||||
|
-- Set some string id for output to DCS.log file.
|
||||||
|
self.lid=string.format("MARKEROPS_BASE %s | ", tostring(self.version))
|
||||||
|
|
||||||
|
self.Tag = Tagname or "mytag"-- #string
|
||||||
|
self.Keywords = Keywords or {} -- #table - might want to use lua regex here, too
|
||||||
|
self.debug = false
|
||||||
|
self.Casesensitive = true
|
||||||
|
|
||||||
|
if Casesensitive and Casesensitive == false then
|
||||||
|
self.Casesensitive = false
|
||||||
|
end
|
||||||
|
|
||||||
|
-----------------------
|
||||||
|
--- FSM Transitions ---
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
-- Start State.
|
||||||
|
self:SetStartState("Stopped")
|
||||||
|
|
||||||
|
-- Add FSM transitions.
|
||||||
|
-- From State --> Event --> To State
|
||||||
|
self:AddTransition("Stopped", "Start", "Running") -- Start the FSM.
|
||||||
|
self:AddTransition("*", "MarkAdded", "*") -- Start the FSM.
|
||||||
|
self:AddTransition("*", "MarkChanged", "*") -- Start the FSM.
|
||||||
|
self:AddTransition("*", "MarkDeleted", "*") -- Start the FSM.
|
||||||
|
self:AddTransition("Running", "Stop", "Stopped") -- Stop the FSM.
|
||||||
|
|
||||||
|
self:HandleEvent(EVENTS.MarkAdded, self.OnEventMark)
|
||||||
|
self:HandleEvent(EVENTS.MarkChange, self.OnEventMark)
|
||||||
|
self:HandleEvent(EVENTS.MarkRemoved, self.OnEventMark)
|
||||||
|
|
||||||
|
-- start
|
||||||
|
self:I(self.lid..string.format("started for %s",self.Tag))
|
||||||
|
self:__Start(1)
|
||||||
|
return self
|
||||||
|
|
||||||
|
-------------------
|
||||||
|
-- PSEUDO Functions
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
--- On after "MarkAdded" event. Triggered when a Marker is added to the F10 map.
|
||||||
|
-- @function [parent=#MARKEROPS_BASE] OnAfterMarkAdded
|
||||||
|
-- @param #MARKEROPS_BASE self
|
||||||
|
-- @param #string From The From state
|
||||||
|
-- @param #string Event The Event called
|
||||||
|
-- @param #string To The To state
|
||||||
|
-- @param #string Text The text on the marker
|
||||||
|
-- @param #table Keywords Table of matching keywords found in the Event text
|
||||||
|
-- @param Core.Point#COORDINATE Coord Coordinate of the marker.
|
||||||
|
|
||||||
|
--- On after "MarkChanged" event. Triggered when a Marker is changed on the F10 map.
|
||||||
|
-- @function [parent=#MARKEROPS_BASE] OnAfterMarkChanged
|
||||||
|
-- @param #MARKEROPS_BASE self
|
||||||
|
-- @param #string From The From state
|
||||||
|
-- @param #string Event The Event called
|
||||||
|
-- @param #string To The To state
|
||||||
|
-- @param #string Text The text on the marker
|
||||||
|
-- @param #table Keywords Table of matching keywords found in the Event text
|
||||||
|
-- @param Core.Point#COORDINATE Coord Coordinate of the marker.
|
||||||
|
|
||||||
|
--- On after "MarkDeleted" event. Triggered when a Marker is deleted from the F10 map.
|
||||||
|
-- @function [parent=#MARKEROPS_BASE] OnAfterMarkDeleted
|
||||||
|
-- @param #MARKEROPS_BASE self
|
||||||
|
-- @param #string From The From state
|
||||||
|
-- @param #string Event The Event called
|
||||||
|
-- @param #string To The To state
|
||||||
|
|
||||||
|
--- "Stop" trigger. Used to stop the function an unhandle events
|
||||||
|
-- @function [parent=#MARKEROPS_BASE] Stop
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
--- (internal) Handle events.
|
||||||
|
-- @param #MARKEROPS_BASE self
|
||||||
|
-- @param Core.Event#EVENTDATA Event
|
||||||
|
function MARKEROPS_BASE:OnEventMark(Event)
|
||||||
|
self:T({Event})
|
||||||
|
if Event == nil or Event.idx == nil then
|
||||||
|
self:E("Skipping onEvent. Event or Event.idx unknown.")
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
--position
|
||||||
|
local vec3={y=Event.pos.y, x=Event.pos.x, z=Event.pos.z}
|
||||||
|
local coord=COORDINATE:NewFromVec3(vec3)
|
||||||
|
if self.debug then
|
||||||
|
local coordtext = coord:ToStringLLDDM()
|
||||||
|
local text = tostring(Event.text)
|
||||||
|
local m = MESSAGE:New(string.format("Mark added at %s with text: %s",coordtext,text),10,"Info",false):ToAll()
|
||||||
|
end
|
||||||
|
-- decision
|
||||||
|
if Event.id==world.event.S_EVENT_MARK_ADDED then
|
||||||
|
self:T({event="S_EVENT_MARK_ADDED", carrier=self.groupname, vec3=Event.pos})
|
||||||
|
-- Handle event
|
||||||
|
local Eventtext = tostring(Event.text)
|
||||||
|
if Eventtext~=nil then
|
||||||
|
if self:_MatchTag(Eventtext) then
|
||||||
|
local matchtable = self:_MatchKeywords(Eventtext)
|
||||||
|
self:MarkAdded(Eventtext,matchtable,coord)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif Event.id==world.event.S_EVENT_MARK_CHANGE then
|
||||||
|
self:T({event="S_EVENT_MARK_CHANGE", carrier=self.groupname, vec3=Event.pos})
|
||||||
|
-- Handle event.
|
||||||
|
local Eventtext = tostring(Event.text)
|
||||||
|
if Eventtext~=nil then
|
||||||
|
if self:_MatchTag(Eventtext) then
|
||||||
|
local matchtable = self:_MatchKeywords(Eventtext)
|
||||||
|
self:MarkChanged(Eventtext,matchtable,coord)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif Event.id==world.event.S_EVENT_MARK_REMOVED then
|
||||||
|
self:T({event="S_EVENT_MARK_REMOVED", carrier=self.groupname, vec3=Event.pos})
|
||||||
|
-- Hande event.
|
||||||
|
local Eventtext = tostring(Event.text)
|
||||||
|
if Eventtext~=nil then
|
||||||
|
if self:_MatchTag(Eventtext) then
|
||||||
|
self:MarkDeleted()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- (internal) Match tag.
|
||||||
|
-- @param #MARKEROPS_BASE self
|
||||||
|
-- @param #string Eventtext Text added to the marker.
|
||||||
|
-- @return #boolean
|
||||||
|
function MARKEROPS_BASE:_MatchTag(Eventtext)
|
||||||
|
local matches = false
|
||||||
|
if not self.Casesensitive then
|
||||||
|
local type = string.lower(self.Tag) -- #string
|
||||||
|
if string.find(string.lower(Eventtext),type) then
|
||||||
|
matches = true --event text contains tag
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local type = self.Tag -- #string
|
||||||
|
if string.find(Eventtext,type) then
|
||||||
|
matches = true --event text contains tag
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return matches
|
||||||
|
end
|
||||||
|
|
||||||
|
--- (internal) Match keywords table.
|
||||||
|
-- @param #MARKEROPS_BASE self
|
||||||
|
-- @param #string Eventtext Text added to the marker.
|
||||||
|
-- @return #table
|
||||||
|
function MARKEROPS_BASE:_MatchKeywords(Eventtext)
|
||||||
|
local matchtable = {}
|
||||||
|
local keytable = self.Keywords
|
||||||
|
for _index,_word in pairs (keytable) do
|
||||||
|
if string.find(string.lower(Eventtext),string.lower(_word))then
|
||||||
|
table.insert(matchtable,_word)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return matchtable
|
||||||
|
end
|
||||||
|
|
||||||
|
--- On before "MarkAdded" event. Triggered when a Marker is added to the F10 map.
|
||||||
|
-- @param #MARKEROPS_BASE self
|
||||||
|
-- @param #string From The From state
|
||||||
|
-- @param #string Event The Event called
|
||||||
|
-- @param #string To The To state
|
||||||
|
-- @param #string Text The text on the marker
|
||||||
|
-- @param #table Keywords Table of matching keywords found in the Event text
|
||||||
|
-- @param Core.Point#COORDINATE Coord Coordinate of the marker.
|
||||||
|
function MARKEROPS_BASE:onbeforeMarkAdded(From,Event,To,Text,Keywords,Coord)
|
||||||
|
self:T({self.lid,From,Event,To,Text,Keywords,Coord:ToStringLLDDM()})
|
||||||
|
end
|
||||||
|
|
||||||
|
--- On before "MarkChanged" event. Triggered when a Marker is changed on the F10 map.
|
||||||
|
-- @param #MARKEROPS_BASE self
|
||||||
|
-- @param #string From The From state
|
||||||
|
-- @param #string Event The Event called
|
||||||
|
-- @param #string To The To state
|
||||||
|
-- @param #string Text The text on the marker
|
||||||
|
-- @param #table Keywords Table of matching keywords found in the Event text
|
||||||
|
-- @param Core.Point#COORDINATE Coord Coordinate of the marker.
|
||||||
|
function MARKEROPS_BASE:onbeforeMarkChanged(From,Event,To,Text,Keywords,Coord)
|
||||||
|
self:T({self.lid,From,Event,To,Text,Keywords,Coord:ToStringLLDDM()})
|
||||||
|
end
|
||||||
|
|
||||||
|
--- On before "MarkDeleted" event. Triggered when a Marker is removed from the F10 map.
|
||||||
|
-- @param #MARKEROPS_BASE self
|
||||||
|
-- @param #string From The From state
|
||||||
|
-- @param #string Event The Event called
|
||||||
|
-- @param #string To The To state
|
||||||
|
function MARKEROPS_BASE:onbeforeMarkDeleted(From,Event,To)
|
||||||
|
self:T({self.lid,From,Event,To})
|
||||||
|
end
|
||||||
|
|
||||||
|
--- On enter "Stopped" event. Unsubscribe events.
|
||||||
|
-- @param #MARKEROPS_BASE self
|
||||||
|
-- @param #string From The From state
|
||||||
|
-- @param #string Event The Event called
|
||||||
|
-- @param #string To The To state
|
||||||
|
function MARKEROPS_BASE:onenterStopped(From,Event,To)
|
||||||
|
self:T({self.lid,From,Event,To})
|
||||||
|
-- unsubscribe from events
|
||||||
|
self:UnHandleEvent(EVENTS.MarkAdded)
|
||||||
|
self:UnHandleEvent(EVENTS.MarkChange)
|
||||||
|
self:UnHandleEvent(EVENTS.MarkRemoved)
|
||||||
|
end
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------
|
||||||
|
-- MARKEROPS_BASE Class Definition End.
|
||||||
|
--------------------------------------------------------------------------
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
-- * Only create or delete menus when required, and keep existing menus persistent.
|
-- * Only create or delete menus when required, and keep existing menus persistent.
|
||||||
-- * Update menu structures.
|
-- * Update menu structures.
|
||||||
-- * Refresh menu structures intelligently, based on a time stamp of updates.
|
-- * Refresh menu structures intelligently, based on a time stamp of updates.
|
||||||
-- - Delete obscolete menus.
|
-- - Delete obsolete menus.
|
||||||
-- - Create new one where required.
|
-- - Create new one where required.
|
||||||
-- - Don't touch the existing ones.
|
-- - Don't touch the existing ones.
|
||||||
-- * Provide a variable amount of parameters to menus.
|
-- * Provide a variable amount of parameters to menus.
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
-- * Provide a great tool to manage menus in your code.
|
-- * Provide a great tool to manage menus in your code.
|
||||||
--
|
--
|
||||||
-- DCS Menus can be managed using the MENU classes.
|
-- DCS Menus can be managed using the MENU classes.
|
||||||
-- The advantage of using MENU classes is that it hides the complexity of dealing with menu management in more advanced scanerios where you need to
|
-- The advantage of using MENU classes is that it hides the complexity of dealing with menu management in more advanced scenarios where you need to
|
||||||
-- set menus and later remove them, and later set them again. You'll find while using use normal DCS scripting functions, that setting and removing
|
-- set menus and later remove them, and later set them again. You'll find while using use normal DCS scripting functions, that setting and removing
|
||||||
-- menus is not a easy feat if you have complex menu hierarchies defined.
|
-- menus is not a easy feat if you have complex menu hierarchies defined.
|
||||||
-- Using the MOOSE menu classes, the removal and refreshing of menus are nicely being handled within these classes, and becomes much more easy.
|
-- Using the MOOSE menu classes, the removal and refreshing of menus are nicely being handled within these classes, and becomes much more easy.
|
||||||
@@ -53,7 +53,6 @@
|
|||||||
-- @module Core.Menu
|
-- @module Core.Menu
|
||||||
-- @image Core_Menu.JPG
|
-- @image Core_Menu.JPG
|
||||||
|
|
||||||
|
|
||||||
MENU_INDEX = {}
|
MENU_INDEX = {}
|
||||||
MENU_INDEX.MenuMission = {}
|
MENU_INDEX.MenuMission = {}
|
||||||
MENU_INDEX.MenuMission.Menus = {}
|
MENU_INDEX.MenuMission.Menus = {}
|
||||||
@@ -64,10 +63,7 @@ MENU_INDEX.Coalition[coalition.side.RED] = {}
|
|||||||
MENU_INDEX.Coalition[coalition.side.RED].Menus = {}
|
MENU_INDEX.Coalition[coalition.side.RED].Menus = {}
|
||||||
MENU_INDEX.Group = {}
|
MENU_INDEX.Group = {}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function MENU_INDEX:ParentPath( ParentMenu, MenuText )
|
function MENU_INDEX:ParentPath( ParentMenu, MenuText )
|
||||||
|
|
||||||
local Path = ParentMenu and "@" .. table.concat( ParentMenu.MenuPath or {}, "@" ) or ""
|
local Path = ParentMenu and "@" .. table.concat( ParentMenu.MenuPath or {}, "@" ) or ""
|
||||||
if ParentMenu then
|
if ParentMenu then
|
||||||
if ParentMenu:IsInstanceOf( "MENU_GROUP" ) or ParentMenu:IsInstanceOf( "MENU_GROUP_COMMAND" ) then
|
if ParentMenu:IsInstanceOf( "MENU_GROUP" ) or ParentMenu:IsInstanceOf( "MENU_GROUP_COMMAND" ) then
|
||||||
@@ -95,20 +91,16 @@ function MENU_INDEX:ParentPath( ParentMenu, MenuText )
|
|||||||
|
|
||||||
Path = Path .. "@" .. MenuText
|
Path = Path .. "@" .. MenuText
|
||||||
return Path
|
return Path
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function MENU_INDEX:PrepareMission()
|
function MENU_INDEX:PrepareMission()
|
||||||
self.MenuMission.Menus = self.MenuMission.Menus or {}
|
self.MenuMission.Menus = self.MenuMission.Menus or {}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function MENU_INDEX:PrepareCoalition( CoalitionSide )
|
function MENU_INDEX:PrepareCoalition( CoalitionSide )
|
||||||
self.Coalition[CoalitionSide] = self.Coalition[CoalitionSide] or {}
|
self.Coalition[CoalitionSide] = self.Coalition[CoalitionSide] or {}
|
||||||
self.Coalition[CoalitionSide].Menus = self.Coalition[CoalitionSide].Menus or {}
|
self.Coalition[CoalitionSide].Menus = self.Coalition[CoalitionSide].Menus or {}
|
||||||
end
|
end
|
||||||
|
|
||||||
---
|
---
|
||||||
-- @param Wrapper.Group#GROUP Group
|
-- @param Wrapper.Group#GROUP Group
|
||||||
function MENU_INDEX:PrepareGroup( Group )
|
function MENU_INDEX:PrepareGroup( Group )
|
||||||
@@ -119,42 +111,26 @@ function MENU_INDEX:PrepareGroup( Group )
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function MENU_INDEX:HasMissionMenu( Path )
|
function MENU_INDEX:HasMissionMenu( Path )
|
||||||
|
|
||||||
return self.MenuMission.Menus[Path]
|
return self.MenuMission.Menus[Path]
|
||||||
end
|
end
|
||||||
|
|
||||||
function MENU_INDEX:SetMissionMenu( Path, Menu )
|
function MENU_INDEX:SetMissionMenu( Path, Menu )
|
||||||
|
|
||||||
self.MenuMission.Menus[Path] = Menu
|
self.MenuMission.Menus[Path] = Menu
|
||||||
end
|
end
|
||||||
|
|
||||||
function MENU_INDEX:ClearMissionMenu( Path )
|
function MENU_INDEX:ClearMissionMenu( Path )
|
||||||
|
|
||||||
self.MenuMission.Menus[Path] = nil
|
self.MenuMission.Menus[Path] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function MENU_INDEX:HasCoalitionMenu( Coalition, Path )
|
function MENU_INDEX:HasCoalitionMenu( Coalition, Path )
|
||||||
|
|
||||||
return self.Coalition[Coalition].Menus[Path]
|
return self.Coalition[Coalition].Menus[Path]
|
||||||
end
|
end
|
||||||
|
|
||||||
function MENU_INDEX:SetCoalitionMenu( Coalition, Path, Menu )
|
function MENU_INDEX:SetCoalitionMenu( Coalition, Path, Menu )
|
||||||
|
|
||||||
self.Coalition[Coalition].Menus[Path] = Menu
|
self.Coalition[Coalition].Menus[Path] = Menu
|
||||||
end
|
end
|
||||||
|
|
||||||
function MENU_INDEX:ClearCoalitionMenu( Coalition, Path )
|
function MENU_INDEX:ClearCoalitionMenu( Coalition, Path )
|
||||||
|
|
||||||
self.Coalition[Coalition].Menus[Path] = nil
|
self.Coalition[Coalition].Menus[Path] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function MENU_INDEX:HasGroupMenu( Group, Path )
|
function MENU_INDEX:HasGroupMenu( Group, Path )
|
||||||
if Group and Group:IsAlive() then
|
if Group and Group:IsAlive() then
|
||||||
local MenuGroupName = Group:GetName()
|
local MenuGroupName = Group:GetName()
|
||||||
@@ -162,53 +138,36 @@ function MENU_INDEX:HasGroupMenu( Group, Path )
|
|||||||
end
|
end
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
function MENU_INDEX:SetGroupMenu( Group, Path, Menu )
|
function MENU_INDEX:SetGroupMenu( Group, Path, Menu )
|
||||||
|
|
||||||
local MenuGroupName = Group:GetName()
|
local MenuGroupName = Group:GetName()
|
||||||
Group:F({MenuGroupName=MenuGroupName,Path=Path})
|
Group:F({MenuGroupName=MenuGroupName,Path=Path})
|
||||||
self.Group[MenuGroupName].Menus[Path] = Menu
|
self.Group[MenuGroupName].Menus[Path] = Menu
|
||||||
end
|
end
|
||||||
|
|
||||||
function MENU_INDEX:ClearGroupMenu( Group, Path )
|
function MENU_INDEX:ClearGroupMenu( Group, Path )
|
||||||
|
|
||||||
local MenuGroupName = Group:GetName()
|
local MenuGroupName = Group:GetName()
|
||||||
self.Group[MenuGroupName].Menus[Path] = nil
|
self.Group[MenuGroupName].Menus[Path] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
function MENU_INDEX:Refresh( Group )
|
function MENU_INDEX:Refresh( Group )
|
||||||
|
|
||||||
for MenuID, Menu in pairs( self.MenuMission.Menus ) do
|
for MenuID, Menu in pairs( self.MenuMission.Menus ) do
|
||||||
Menu:Refresh()
|
Menu:Refresh()
|
||||||
end
|
end
|
||||||
|
|
||||||
for MenuID, Menu in pairs( self.Coalition[coalition.side.BLUE].Menus ) do
|
for MenuID, Menu in pairs( self.Coalition[coalition.side.BLUE].Menus ) do
|
||||||
Menu:Refresh()
|
Menu:Refresh()
|
||||||
end
|
end
|
||||||
|
|
||||||
for MenuID, Menu in pairs( self.Coalition[coalition.side.RED].Menus ) do
|
for MenuID, Menu in pairs( self.Coalition[coalition.side.RED].Menus ) do
|
||||||
Menu:Refresh()
|
Menu:Refresh()
|
||||||
end
|
end
|
||||||
|
|
||||||
local GroupName = Group:GetName()
|
local GroupName = Group:GetName()
|
||||||
for MenuID, Menu in pairs( self.Group[GroupName].Menus ) do
|
for MenuID, Menu in pairs( self.Group[GroupName].Menus ) do
|
||||||
Menu:Refresh()
|
Menu:Refresh()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
do -- MENU_BASE
|
do -- MENU_BASE
|
||||||
|
|
||||||
--- @type MENU_BASE
|
--- @type MENU_BASE
|
||||||
-- @extends Base#BASE
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
--- Defines the main MENU class where other MENU classes are derived from.
|
--- Defines the main MENU class where other MENU classes are derived from.
|
||||||
-- This is an abstract class, so don't use it.
|
-- This is an abstract class, so don't use it.
|
||||||
-- @field #MENU_BASE
|
-- @field #MENU_BASE
|
||||||
@@ -216,10 +175,10 @@ do -- MENU_BASE
|
|||||||
ClassName = "MENU_BASE",
|
ClassName = "MENU_BASE",
|
||||||
MenuPath = nil,
|
MenuPath = nil,
|
||||||
MenuText = "",
|
MenuText = "",
|
||||||
MenuParentPath = nil
|
MenuParentPath = nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Consructor
|
--- Constructor
|
||||||
-- @param #MENU_BASE
|
-- @param #MENU_BASE
|
||||||
-- @return #MENU_BASE
|
-- @return #MENU_BASE
|
||||||
function MENU_BASE:New( MenuText, ParentMenu )
|
function MENU_BASE:New( MenuText, ParentMenu )
|
||||||
@@ -228,27 +187,25 @@ do -- MENU_BASE
|
|||||||
if ParentMenu ~= nil then
|
if ParentMenu ~= nil then
|
||||||
MenuParentPath = ParentMenu.MenuPath
|
MenuParentPath = ParentMenu.MenuPath
|
||||||
end
|
end
|
||||||
|
local self = BASE:Inherit( self, BASE:New() )
|
||||||
local self = BASE:Inherit( self, BASE:New() )
|
|
||||||
|
|
||||||
self.MenuPath = nil
|
self.MenuPath = nil
|
||||||
self.MenuText = MenuText
|
self.MenuText = MenuText
|
||||||
self.ParentMenu = ParentMenu
|
self.ParentMenu = ParentMenu
|
||||||
self.MenuParentPath = MenuParentPath
|
self.MenuParentPath = MenuParentPath
|
||||||
self.Path = ( self.ParentMenu and "@" .. table.concat( self.MenuParentPath or {}, "@" ) or "" ) .. "@" .. self.MenuText
|
self.Path = ( self.ParentMenu and "@" .. table.concat( self.MenuParentPath or {}, "@" ) or "" ) .. "@" .. self.MenuText
|
||||||
self.Menus = {}
|
self.Menus = {}
|
||||||
self.MenuCount = 0
|
self.MenuCount = 0
|
||||||
self.MenuStamp = timer.getTime()
|
self.MenuStamp = timer.getTime()
|
||||||
self.MenuRemoveParent = false
|
self.MenuRemoveParent = false
|
||||||
|
|
||||||
if self.ParentMenu then
|
if self.ParentMenu then
|
||||||
self.ParentMenu.Menus = self.ParentMenu.Menus or {}
|
self.ParentMenu.Menus = self.ParentMenu.Menus or {}
|
||||||
self.ParentMenu.Menus[MenuText] = self
|
self.ParentMenu.Menus[MenuText] = self
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
function MENU_BASE:SetParentMenu( MenuText, Menu )
|
function MENU_BASE:SetParentMenu( MenuText, Menu )
|
||||||
if self.ParentMenu then
|
if self.ParentMenu then
|
||||||
self.ParentMenu.Menus = self.ParentMenu.Menus or {}
|
self.ParentMenu.Menus = self.ParentMenu.Menus or {}
|
||||||
@@ -256,7 +213,6 @@ do -- MENU_BASE
|
|||||||
self.ParentMenu.MenuCount = self.ParentMenu.MenuCount + 1
|
self.ParentMenu.MenuCount = self.ParentMenu.MenuCount + 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function MENU_BASE:ClearParentMenu( MenuText )
|
function MENU_BASE:ClearParentMenu( MenuText )
|
||||||
if self.ParentMenu and self.ParentMenu.Menus[MenuText] then
|
if self.ParentMenu and self.ParentMenu.Menus[MenuText] then
|
||||||
self.ParentMenu.Menus[MenuText] = nil
|
self.ParentMenu.Menus[MenuText] = nil
|
||||||
@@ -266,7 +222,6 @@ do -- MENU_BASE
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Sets a @{Menu} to remove automatically the parent menu when the menu removed is the last child menu of that parent @{Menu}.
|
--- Sets a @{Menu} to remove automatically the parent menu when the menu removed is the last child menu of that parent @{Menu}.
|
||||||
-- @param #MENU_BASE self
|
-- @param #MENU_BASE self
|
||||||
-- @param #boolean RemoveParent If true, the parent menu is automatically removed when this menu is the last child menu of that parent @{Menu}.
|
-- @param #boolean RemoveParent If true, the parent menu is automatically removed when this menu is the last child menu of that parent @{Menu}.
|
||||||
@@ -276,7 +231,6 @@ do -- MENU_BASE
|
|||||||
self.MenuRemoveParent = RemoveParent
|
self.MenuRemoveParent = RemoveParent
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Gets a @{Menu} from a parent @{Menu}
|
--- Gets a @{Menu} from a parent @{Menu}
|
||||||
-- @param #MENU_BASE self
|
-- @param #MENU_BASE self
|
||||||
@@ -285,7 +239,7 @@ do -- MENU_BASE
|
|||||||
function MENU_BASE:GetMenu( MenuText )
|
function MENU_BASE:GetMenu( MenuText )
|
||||||
return self.Menus[MenuText]
|
return self.Menus[MenuText]
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Sets a menu stamp for later prevention of menu removal.
|
--- Sets a menu stamp for later prevention of menu removal.
|
||||||
-- @param #MENU_BASE self
|
-- @param #MENU_BASE self
|
||||||
-- @param MenuStamp
|
-- @param MenuStamp
|
||||||
@@ -323,9 +277,7 @@ do -- MENU_BASE
|
|||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
do -- MENU_COMMAND_BASE
|
do -- MENU_COMMAND_BASE
|
||||||
|
|
||||||
--- @type MENU_COMMAND_BASE
|
--- @type MENU_COMMAND_BASE
|
||||||
-- @field #function MenuCallHandler
|
-- @field #function MenuCallHandler
|
||||||
-- @extends Core.Menu#MENU_BASE
|
-- @extends Core.Menu#MENU_BASE
|
||||||
@@ -346,8 +298,7 @@ do -- MENU_COMMAND_BASE
|
|||||||
-- @return #MENU_COMMAND_BASE
|
-- @return #MENU_COMMAND_BASE
|
||||||
function MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, CommandMenuArguments )
|
function MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, CommandMenuArguments )
|
||||||
|
|
||||||
local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) -- #MENU_COMMAND_BASE
|
local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) -- #MENU_COMMAND_BASE
|
||||||
|
|
||||||
-- When a menu function goes into error, DCS displays an obscure menu message.
|
-- When a menu function goes into error, DCS displays an obscure menu message.
|
||||||
-- This error handler catches the menu error and displays the full call stack.
|
-- This error handler catches the menu error and displays the full call stack.
|
||||||
local ErrorHandler = function( errmsg )
|
local ErrorHandler = function( errmsg )
|
||||||
@@ -367,7 +318,7 @@ do -- MENU_COMMAND_BASE
|
|||||||
local Status, Result = xpcall( MenuFunction, ErrorHandler )
|
local Status, Result = xpcall( MenuFunction, ErrorHandler )
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- This sets the new command function of a menu,
|
--- This sets the new command function of a menu,
|
||||||
@@ -380,7 +331,6 @@ do -- MENU_COMMAND_BASE
|
|||||||
self.CommandMenuFunction = CommandMenuFunction
|
self.CommandMenuFunction = CommandMenuFunction
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- This sets the new command arguments of a menu,
|
--- This sets the new command arguments of a menu,
|
||||||
-- so that if a menu is regenerated, or if command arguments change,
|
-- so that if a menu is regenerated, or if command arguments change,
|
||||||
-- that the arguments set for the menu are loosely coupled with the menu itself!!!
|
-- that the arguments set for the menu are loosely coupled with the menu itself!!!
|
||||||
@@ -391,35 +341,30 @@ do -- MENU_COMMAND_BASE
|
|||||||
self.CommandMenuArguments = CommandMenuArguments
|
self.CommandMenuArguments = CommandMenuArguments
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
do -- MENU_MISSION
|
do -- MENU_MISSION
|
||||||
|
|
||||||
--- @type MENU_MISSION
|
--- @type MENU_MISSION
|
||||||
-- @extends Core.Menu#MENU_BASE
|
-- @extends Core.Menu#MENU_BASE
|
||||||
|
|
||||||
--- Manages the main menus for a complete mission.
|
--- Manages the main menus for a complete mission.
|
||||||
--
|
--
|
||||||
-- You can add menus with the @{#MENU_MISSION.New} method, which constructs a MENU_MISSION object and returns you the object reference.
|
-- You can add menus with the @{#MENU_MISSION.New} method, which constructs a MENU_MISSION object and returns you the object reference.
|
||||||
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_MISSION.Remove}.
|
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_MISSION.Remove}.
|
||||||
-- @field #MENU_MISSION
|
-- @field #MENU_MISSION
|
||||||
MENU_MISSION = {
|
MENU_MISSION = {
|
||||||
ClassName = "MENU_MISSION"
|
ClassName = "MENU_MISSION",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- MENU_MISSION constructor. Creates a new MENU_MISSION object and creates the menu for a complete mission file.
|
--- MENU_MISSION constructor. Creates a new MENU_MISSION object and creates the menu for a complete mission file.
|
||||||
-- @param #MENU_MISSION self
|
-- @param #MENU_MISSION self
|
||||||
-- @param #string MenuText The text for the menu.
|
-- @param #string MenuText The text for the menu.
|
||||||
-- @param #table ParentMenu The parent menu. This parameter can be ignored if you want the menu to be located at the perent menu of DCS world (under F10 other).
|
-- @param #table ParentMenu The parent menu. This parameter can be ignored if you want the menu to be located at the parent menu of DCS world (under F10 other).
|
||||||
-- @return #MENU_MISSION
|
-- @return #MENU_MISSION
|
||||||
function MENU_MISSION:New( MenuText, ParentMenu )
|
function MENU_MISSION:New( MenuText, ParentMenu )
|
||||||
|
|
||||||
MENU_INDEX:PrepareMission()
|
MENU_INDEX:PrepareMission()
|
||||||
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
|
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
|
||||||
local MissionMenu = MENU_INDEX:HasMissionMenu( Path )
|
local MissionMenu = MENU_INDEX:HasMissionMenu( Path )
|
||||||
|
|
||||||
if MissionMenu then
|
if MissionMenu then
|
||||||
return MissionMenu
|
return MissionMenu
|
||||||
else
|
else
|
||||||
@@ -432,17 +377,16 @@ do -- MENU_MISSION
|
|||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Refreshes a radio item for a mission
|
--- Refreshes a radio item for a mission
|
||||||
-- @param #MENU_MISSION self
|
-- @param #MENU_MISSION self
|
||||||
-- @return #MENU_MISSION
|
-- @return #MENU_MISSION
|
||||||
function MENU_MISSION:Refresh()
|
function MENU_MISSION:Refresh()
|
||||||
|
|
||||||
do
|
do
|
||||||
missionCommands.removeItem( self.MenuPath )
|
missionCommands.removeItem( self.MenuPath )
|
||||||
self.MenuPath = missionCommands.addSubMenu( self.MenuText, self.MenuParentPath )
|
self.MenuPath = missionCommands.addSubMenu( self.MenuText, self.MenuParentPath )
|
||||||
end
|
end
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Removes the sub menus recursively of this MENU_MISSION. Note that the main menu is kept!
|
--- Removes the sub menus recursively of this MENU_MISSION. Note that the main menu is kept!
|
||||||
@@ -466,7 +410,6 @@ do -- MENU_MISSION
|
|||||||
MENU_INDEX:PrepareMission()
|
MENU_INDEX:PrepareMission()
|
||||||
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
|
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
|
||||||
local MissionMenu = MENU_INDEX:HasMissionMenu( Path )
|
local MissionMenu = MENU_INDEX:HasMissionMenu( Path )
|
||||||
|
|
||||||
if MissionMenu == self then
|
if MissionMenu == self then
|
||||||
self:RemoveSubMenus()
|
self:RemoveSubMenus()
|
||||||
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
||||||
@@ -487,10 +430,7 @@ do -- MENU_MISSION
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
do -- MENU_MISSION_COMMAND
|
do -- MENU_MISSION_COMMAND
|
||||||
|
|
||||||
--- @type MENU_MISSION_COMMAND
|
--- @type MENU_MISSION_COMMAND
|
||||||
@@ -503,7 +443,7 @@ do -- MENU_MISSION_COMMAND
|
|||||||
--
|
--
|
||||||
-- @field #MENU_MISSION_COMMAND
|
-- @field #MENU_MISSION_COMMAND
|
||||||
MENU_MISSION_COMMAND = {
|
MENU_MISSION_COMMAND = {
|
||||||
ClassName = "MENU_MISSION_COMMAND"
|
ClassName = "MENU_MISSION_COMMAND",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- MENU_MISSION constructor. Creates a new radio command item for a complete mission file, which can invoke a function with parameters.
|
--- MENU_MISSION constructor. Creates a new radio command item for a complete mission file, which can invoke a function with parameters.
|
||||||
@@ -518,7 +458,6 @@ do -- MENU_MISSION_COMMAND
|
|||||||
MENU_INDEX:PrepareMission()
|
MENU_INDEX:PrepareMission()
|
||||||
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
|
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
|
||||||
local MissionMenu = MENU_INDEX:HasMissionMenu( Path )
|
local MissionMenu = MENU_INDEX:HasMissionMenu( Path )
|
||||||
|
|
||||||
if MissionMenu then
|
if MissionMenu then
|
||||||
MissionMenu:SetCommandMenuFunction( CommandMenuFunction )
|
MissionMenu:SetCommandMenuFunction( CommandMenuFunction )
|
||||||
MissionMenu:SetCommandMenuArguments( arg )
|
MissionMenu:SetCommandMenuArguments( arg )
|
||||||
@@ -532,17 +471,15 @@ do -- MENU_MISSION_COMMAND
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Refreshes a radio item for a mission
|
--- Refreshes a radio item for a mission
|
||||||
-- @param #MENU_MISSION_COMMAND self
|
-- @param #MENU_MISSION_COMMAND self
|
||||||
-- @return #MENU_MISSION_COMMAND
|
-- @return #MENU_MISSION_COMMAND
|
||||||
function MENU_MISSION_COMMAND:Refresh()
|
function MENU_MISSION_COMMAND:Refresh()
|
||||||
|
|
||||||
do
|
do
|
||||||
missionCommands.removeItem( self.MenuPath )
|
missionCommands.removeItem( self.MenuPath )
|
||||||
missionCommands.addCommand( self.MenuText, self.MenuParentPath, self.MenuCallHandler )
|
missionCommands.addCommand( self.MenuText, self.MenuParentPath, self.MenuCallHandler )
|
||||||
end
|
end
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Removes a radio command item for a coalition
|
--- Removes a radio command item for a coalition
|
||||||
@@ -553,7 +490,6 @@ do -- MENU_MISSION_COMMAND
|
|||||||
MENU_INDEX:PrepareMission()
|
MENU_INDEX:PrepareMission()
|
||||||
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
|
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
|
||||||
local MissionMenu = MENU_INDEX:HasMissionMenu( Path )
|
local MissionMenu = MENU_INDEX:HasMissionMenu( Path )
|
||||||
|
|
||||||
if MissionMenu == self then
|
if MissionMenu == self then
|
||||||
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
||||||
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
|
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
|
||||||
@@ -572,13 +508,8 @@ do -- MENU_MISSION_COMMAND
|
|||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
do -- MENU_COALITION
|
do -- MENU_COALITION
|
||||||
|
|
||||||
--- @type MENU_COALITION
|
--- @type MENU_COALITION
|
||||||
-- @extends Core.Menu#MENU_BASE
|
-- @extends Core.Menu#MENU_BASE
|
||||||
|
|
||||||
@@ -634,18 +565,15 @@ do -- MENU_COALITION
|
|||||||
-- @param #MENU_COALITION self
|
-- @param #MENU_COALITION self
|
||||||
-- @param DCS#coalition.side Coalition The coalition owning the menu.
|
-- @param DCS#coalition.side Coalition The coalition owning the menu.
|
||||||
-- @param #string MenuText The text for the menu.
|
-- @param #string MenuText The text for the menu.
|
||||||
-- @param #table ParentMenu The parent menu. This parameter can be ignored if you want the menu to be located at the perent menu of DCS world (under F10 other).
|
-- @param #table ParentMenu The parent menu. This parameter can be ignored if you want the menu to be located at the parent menu of DCS world (under F10 other).
|
||||||
-- @return #MENU_COALITION self
|
-- @return #MENU_COALITION self
|
||||||
function MENU_COALITION:New( Coalition, MenuText, ParentMenu )
|
function MENU_COALITION:New( Coalition, MenuText, ParentMenu )
|
||||||
|
|
||||||
MENU_INDEX:PrepareCoalition( Coalition )
|
MENU_INDEX:PrepareCoalition( Coalition )
|
||||||
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
|
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
|
||||||
local CoalitionMenu = MENU_INDEX:HasCoalitionMenu( Coalition, Path )
|
local CoalitionMenu = MENU_INDEX:HasCoalitionMenu( Coalition, Path )
|
||||||
|
|
||||||
if CoalitionMenu then
|
if CoalitionMenu then
|
||||||
return CoalitionMenu
|
return CoalitionMenu
|
||||||
else
|
else
|
||||||
|
|
||||||
local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) )
|
local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) )
|
||||||
MENU_INDEX:SetCoalitionMenu( Coalition, Path, self )
|
MENU_INDEX:SetCoalitionMenu( Coalition, Path, self )
|
||||||
|
|
||||||
@@ -656,17 +584,15 @@ do -- MENU_COALITION
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Refreshes a radio item for a coalition
|
--- Refreshes a radio item for a coalition
|
||||||
-- @param #MENU_COALITION self
|
-- @param #MENU_COALITION self
|
||||||
-- @return #MENU_COALITION
|
-- @return #MENU_COALITION
|
||||||
function MENU_COALITION:Refresh()
|
function MENU_COALITION:Refresh()
|
||||||
|
|
||||||
do
|
do
|
||||||
missionCommands.removeItemForCoalition( self.Coalition, self.MenuPath )
|
missionCommands.removeItemForCoalition( self.Coalition, self.MenuPath )
|
||||||
missionCommands.addSubMenuForCoalition( self.Coalition, self.MenuText, self.MenuParentPath )
|
missionCommands.addSubMenuForCoalition( self.Coalition, self.MenuText, self.MenuParentPath )
|
||||||
end
|
end
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Removes the sub menus recursively of this MENU_COALITION. Note that the main menu is kept!
|
--- Removes the sub menus recursively of this MENU_COALITION. Note that the main menu is kept!
|
||||||
@@ -689,7 +615,6 @@ do -- MENU_COALITION
|
|||||||
MENU_INDEX:PrepareCoalition( self.Coalition )
|
MENU_INDEX:PrepareCoalition( self.Coalition )
|
||||||
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
|
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
|
||||||
local CoalitionMenu = MENU_INDEX:HasCoalitionMenu( self.Coalition, Path )
|
local CoalitionMenu = MENU_INDEX:HasCoalitionMenu( self.Coalition, Path )
|
||||||
|
|
||||||
if CoalitionMenu == self then
|
if CoalitionMenu == self then
|
||||||
self:RemoveSubMenus()
|
self:RemoveSubMenus()
|
||||||
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
||||||
@@ -709,11 +634,7 @@ do -- MENU_COALITION
|
|||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
do -- MENU_COALITION_COMMAND
|
do -- MENU_COALITION_COMMAND
|
||||||
|
|
||||||
--- @type MENU_COALITION_COMMAND
|
--- @type MENU_COALITION_COMMAND
|
||||||
@@ -742,7 +663,6 @@ do -- MENU_COALITION_COMMAND
|
|||||||
MENU_INDEX:PrepareCoalition( Coalition )
|
MENU_INDEX:PrepareCoalition( Coalition )
|
||||||
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
|
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
|
||||||
local CoalitionMenu = MENU_INDEX:HasCoalitionMenu( Coalition, Path )
|
local CoalitionMenu = MENU_INDEX:HasCoalitionMenu( Coalition, Path )
|
||||||
|
|
||||||
if CoalitionMenu then
|
if CoalitionMenu then
|
||||||
CoalitionMenu:SetCommandMenuFunction( CommandMenuFunction )
|
CoalitionMenu:SetCommandMenuFunction( CommandMenuFunction )
|
||||||
CoalitionMenu:SetCommandMenuArguments( arg )
|
CoalitionMenu:SetCommandMenuArguments( arg )
|
||||||
@@ -757,20 +677,17 @@ do -- MENU_COALITION_COMMAND
|
|||||||
self:SetParentMenu( self.MenuText, self )
|
self:SetParentMenu( self.MenuText, self )
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Refreshes a radio item for a coalition
|
--- Refreshes a radio item for a coalition
|
||||||
-- @param #MENU_COALITION_COMMAND self
|
-- @param #MENU_COALITION_COMMAND self
|
||||||
-- @return #MENU_COALITION_COMMAND
|
-- @return #MENU_COALITION_COMMAND
|
||||||
function MENU_COALITION_COMMAND:Refresh()
|
function MENU_COALITION_COMMAND:Refresh()
|
||||||
|
|
||||||
do
|
do
|
||||||
missionCommands.removeItemForCoalition( self.Coalition, self.MenuPath )
|
missionCommands.removeItemForCoalition( self.Coalition, self.MenuPath )
|
||||||
missionCommands.addCommandForCoalition( self.Coalition, self.MenuText, self.MenuParentPath, self.MenuCallHandler )
|
missionCommands.addCommandForCoalition( self.Coalition, self.MenuText, self.MenuParentPath, self.MenuCallHandler )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Removes a radio command item for a coalition
|
--- Removes a radio command item for a coalition
|
||||||
@@ -781,7 +698,6 @@ do -- MENU_COALITION_COMMAND
|
|||||||
MENU_INDEX:PrepareCoalition( self.Coalition )
|
MENU_INDEX:PrepareCoalition( self.Coalition )
|
||||||
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
|
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
|
||||||
local CoalitionMenu = MENU_INDEX:HasCoalitionMenu( self.Coalition, Path )
|
local CoalitionMenu = MENU_INDEX:HasCoalitionMenu( self.Coalition, Path )
|
||||||
|
|
||||||
if CoalitionMenu == self then
|
if CoalitionMenu == self then
|
||||||
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
||||||
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
|
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
|
||||||
@@ -800,20 +716,16 @@ do -- MENU_COALITION_COMMAND
|
|||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- MENU_GROUP
|
--- MENU_GROUP
|
||||||
|
|
||||||
do
|
do
|
||||||
-- This local variable is used to cache the menus registered under groups.
|
-- This local variable is used to cache the menus registered under groups.
|
||||||
-- Menus don't dissapear when groups for players are destroyed and restarted.
|
-- Menus don't disappear when groups for players are destroyed and restarted.
|
||||||
-- So every menu for a client created must be tracked so that program logic accidentally does not create.
|
-- So every menu for a client created must be tracked so that program logic accidentally does not create.
|
||||||
-- the same menus twice during initialization logic.
|
-- the same menus twice during initialization logic.
|
||||||
-- These menu classes are handling this logic with this variable.
|
-- These menu classes are handling this logic with this variable.
|
||||||
local _MENUGROUPS = {}
|
local _MENUGROUPS = {}
|
||||||
|
|
||||||
--- @type MENU_GROUP
|
--- @type MENU_GROUP
|
||||||
-- @extends Core.Menu#MENU_BASE
|
-- @extends Core.Menu#MENU_BASE
|
||||||
|
|
||||||
@@ -889,16 +801,13 @@ do
|
|||||||
MENU_INDEX:PrepareGroup( Group )
|
MENU_INDEX:PrepareGroup( Group )
|
||||||
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
|
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
|
||||||
local GroupMenu = MENU_INDEX:HasGroupMenu( Group, Path )
|
local GroupMenu = MENU_INDEX:HasGroupMenu( Group, Path )
|
||||||
|
|
||||||
if GroupMenu then
|
if GroupMenu then
|
||||||
return GroupMenu
|
return GroupMenu
|
||||||
else
|
else
|
||||||
self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) )
|
self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) )
|
||||||
MENU_INDEX:SetGroupMenu( Group, Path, self )
|
MENU_INDEX:SetGroupMenu( Group, Path, self )
|
||||||
|
|
||||||
self.Group = Group
|
self.Group = Group
|
||||||
self.GroupID = Group:GetID()
|
self.GroupID = Group:GetID()
|
||||||
|
|
||||||
self.MenuPath = missionCommands.addSubMenuForGroup( self.GroupID, MenuText, self.MenuParentPath )
|
self.MenuPath = missionCommands.addSubMenuForGroup( self.GroupID, MenuText, self.MenuParentPath )
|
||||||
|
|
||||||
self:SetParentMenu( self.MenuText, self )
|
self:SetParentMenu( self.MenuText, self )
|
||||||
@@ -906,12 +815,11 @@ do
|
|||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Refreshes a new radio item for a group and submenus
|
--- Refreshes a new radio item for a group and submenus
|
||||||
-- @param #MENU_GROUP self
|
-- @param #MENU_GROUP self
|
||||||
-- @return #MENU_GROUP
|
-- @return #MENU_GROUP
|
||||||
function MENU_GROUP:Refresh()
|
function MENU_GROUP:Refresh()
|
||||||
|
|
||||||
do
|
do
|
||||||
missionCommands.removeItemForGroup( self.GroupID, self.MenuPath )
|
missionCommands.removeItemForGroup( self.GroupID, self.MenuPath )
|
||||||
missionCommands.addSubMenuForGroup( self.GroupID, self.MenuText, self.MenuParentPath )
|
missionCommands.addSubMenuForGroup( self.GroupID, self.MenuText, self.MenuParentPath )
|
||||||
@@ -920,7 +828,31 @@ do
|
|||||||
Menu:Refresh()
|
Menu:Refresh()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Refreshes a new radio item for a group and submenus, ordering by (numerical) MenuTag
|
||||||
|
-- @param #MENU_GROUP self
|
||||||
|
-- @return #MENU_GROUP
|
||||||
|
function MENU_GROUP:RefreshAndOrderByTag()
|
||||||
|
|
||||||
|
do
|
||||||
|
missionCommands.removeItemForGroup( self.GroupID, self.MenuPath )
|
||||||
|
missionCommands.addSubMenuForGroup( self.GroupID, self.MenuText, self.MenuParentPath )
|
||||||
|
|
||||||
|
local MenuTable = {}
|
||||||
|
for MenuText, Menu in pairs( self.Menus or {} ) do
|
||||||
|
local tag = Menu.MenuTag or math.random(1,10000)
|
||||||
|
MenuTable[#MenuTable+1] = {Tag=tag, Enty=Menu}
|
||||||
|
end
|
||||||
|
table.sort(MenuTable, function (k1, k2) return k1.tag < k2.tag end )
|
||||||
|
for _, Menu in pairs( MenuTable ) do
|
||||||
|
Menu.Entry:Refresh()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Removes the sub menus recursively of this MENU_GROUP.
|
--- Removes the sub menus recursively of this MENU_GROUP.
|
||||||
@@ -929,7 +861,6 @@ do
|
|||||||
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
|
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
|
||||||
-- @return #MENU_GROUP self
|
-- @return #MENU_GROUP self
|
||||||
function MENU_GROUP:RemoveSubMenus( MenuStamp, MenuTag )
|
function MENU_GROUP:RemoveSubMenus( MenuStamp, MenuTag )
|
||||||
|
|
||||||
for MenuText, Menu in pairs( self.Menus or {} ) do
|
for MenuText, Menu in pairs( self.Menus or {} ) do
|
||||||
Menu:Remove( MenuStamp, MenuTag )
|
Menu:Remove( MenuStamp, MenuTag )
|
||||||
end
|
end
|
||||||
@@ -938,18 +869,15 @@ do
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Removes the main menu and sub menus recursively of this MENU_GROUP.
|
--- Removes the main menu and sub menus recursively of this MENU_GROUP.
|
||||||
-- @param #MENU_GROUP self
|
-- @param #MENU_GROUP self
|
||||||
-- @param MenuStamp
|
-- @param MenuStamp
|
||||||
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
|
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
|
||||||
-- @return #nil
|
-- @return #nil
|
||||||
function MENU_GROUP:Remove( MenuStamp, MenuTag )
|
function MENU_GROUP:Remove( MenuStamp, MenuTag )
|
||||||
|
|
||||||
MENU_INDEX:PrepareGroup( self.Group )
|
MENU_INDEX:PrepareGroup( self.Group )
|
||||||
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
|
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
|
||||||
local GroupMenu = MENU_INDEX:HasGroupMenu( self.Group, Path )
|
local GroupMenu = MENU_INDEX:HasGroupMenu( self.Group, Path )
|
||||||
|
|
||||||
if GroupMenu == self then
|
if GroupMenu == self then
|
||||||
self:RemoveSubMenus( MenuStamp, MenuTag )
|
self:RemoveSubMenus( MenuStamp, MenuTag )
|
||||||
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
||||||
@@ -993,18 +921,15 @@ do
|
|||||||
-- @param CommandMenuArgument An argument for the function.
|
-- @param CommandMenuArgument An argument for the function.
|
||||||
-- @return #MENU_GROUP_COMMAND
|
-- @return #MENU_GROUP_COMMAND
|
||||||
function MENU_GROUP_COMMAND:New( Group, MenuText, ParentMenu, CommandMenuFunction, ... )
|
function MENU_GROUP_COMMAND:New( Group, MenuText, ParentMenu, CommandMenuFunction, ... )
|
||||||
|
|
||||||
MENU_INDEX:PrepareGroup( Group )
|
MENU_INDEX:PrepareGroup( Group )
|
||||||
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
|
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
|
||||||
local GroupMenu = MENU_INDEX:HasGroupMenu( Group, Path )
|
local GroupMenu = MENU_INDEX:HasGroupMenu( Group, Path )
|
||||||
|
|
||||||
if GroupMenu then
|
if GroupMenu then
|
||||||
GroupMenu:SetCommandMenuFunction( CommandMenuFunction )
|
GroupMenu:SetCommandMenuFunction( CommandMenuFunction )
|
||||||
GroupMenu:SetCommandMenuArguments( arg )
|
GroupMenu:SetCommandMenuArguments( arg )
|
||||||
return GroupMenu
|
return GroupMenu
|
||||||
else
|
else
|
||||||
self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) )
|
self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) )
|
||||||
|
|
||||||
MENU_INDEX:SetGroupMenu( Group, Path, self )
|
MENU_INDEX:SetGroupMenu( Group, Path, self )
|
||||||
|
|
||||||
self.Group = Group
|
self.Group = Group
|
||||||
@@ -1015,19 +940,17 @@ do
|
|||||||
self:SetParentMenu( self.MenuText, self )
|
self:SetParentMenu( self.MenuText, self )
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Refreshes a radio item for a group
|
--- Refreshes a radio item for a group
|
||||||
-- @param #MENU_GROUP_COMMAND self
|
-- @param #MENU_GROUP_COMMAND self
|
||||||
-- @return #MENU_GROUP_COMMAND
|
-- @return #MENU_GROUP_COMMAND
|
||||||
function MENU_GROUP_COMMAND:Refresh()
|
function MENU_GROUP_COMMAND:Refresh()
|
||||||
|
|
||||||
do
|
do
|
||||||
missionCommands.removeItemForGroup( self.GroupID, self.MenuPath )
|
missionCommands.removeItemForGroup( self.GroupID, self.MenuPath )
|
||||||
missionCommands.addCommandForGroup( self.GroupID, self.MenuText, self.MenuParentPath, self.MenuCallHandler )
|
missionCommands.addCommandForGroup( self.GroupID, self.MenuText, self.MenuParentPath, self.MenuCallHandler )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Removes a menu structure for a group.
|
--- Removes a menu structure for a group.
|
||||||
@@ -1036,11 +959,9 @@ do
|
|||||||
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
|
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
|
||||||
-- @return #nil
|
-- @return #nil
|
||||||
function MENU_GROUP_COMMAND:Remove( MenuStamp, MenuTag )
|
function MENU_GROUP_COMMAND:Remove( MenuStamp, MenuTag )
|
||||||
|
|
||||||
MENU_INDEX:PrepareGroup( self.Group )
|
MENU_INDEX:PrepareGroup( self.Group )
|
||||||
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
|
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
|
||||||
local GroupMenu = MENU_INDEX:HasGroupMenu( self.Group, Path )
|
local GroupMenu = MENU_INDEX:HasGroupMenu( self.Group, Path )
|
||||||
|
|
||||||
if GroupMenu == self then
|
if GroupMenu == self then
|
||||||
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
||||||
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
|
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
|
||||||
@@ -1059,13 +980,9 @@ do
|
|||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- MENU_GROUP_DELAYED
|
--- MENU_GROUP_DELAYED
|
||||||
|
|
||||||
do
|
do
|
||||||
|
|
||||||
--- @type MENU_GROUP_DELAYED
|
--- @type MENU_GROUP_DELAYED
|
||||||
-- @extends Core.Menu#MENU_BASE
|
-- @extends Core.Menu#MENU_BASE
|
||||||
|
|
||||||
@@ -1093,16 +1010,13 @@ do
|
|||||||
MENU_INDEX:PrepareGroup( Group )
|
MENU_INDEX:PrepareGroup( Group )
|
||||||
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
|
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
|
||||||
local GroupMenu = MENU_INDEX:HasGroupMenu( Group, Path )
|
local GroupMenu = MENU_INDEX:HasGroupMenu( Group, Path )
|
||||||
|
|
||||||
if GroupMenu then
|
if GroupMenu then
|
||||||
return GroupMenu
|
return GroupMenu
|
||||||
else
|
else
|
||||||
self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) )
|
self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) )
|
||||||
MENU_INDEX:SetGroupMenu( Group, Path, self )
|
MENU_INDEX:SetGroupMenu( Group, Path, self )
|
||||||
|
|
||||||
self.Group = Group
|
self.Group = Group
|
||||||
self.GroupID = Group:GetID()
|
self.GroupID = Group:GetID()
|
||||||
|
|
||||||
if self.MenuParentPath then
|
if self.MenuParentPath then
|
||||||
self.MenuPath = UTILS.DeepCopy( self.MenuParentPath )
|
self.MenuPath = UTILS.DeepCopy( self.MenuParentPath )
|
||||||
else
|
else
|
||||||
@@ -1116,12 +1030,10 @@ do
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Refreshes a new radio item for a group and submenus
|
--- Refreshes a new radio item for a group and submenus
|
||||||
-- @param #MENU_GROUP_DELAYED self
|
-- @param #MENU_GROUP_DELAYED self
|
||||||
-- @return #MENU_GROUP_DELAYED
|
-- @return #MENU_GROUP_DELAYED
|
||||||
function MENU_GROUP_DELAYED:Set()
|
function MENU_GROUP_DELAYED:Set()
|
||||||
|
|
||||||
do
|
do
|
||||||
if not self.MenuSet then
|
if not self.MenuSet then
|
||||||
missionCommands.addSubMenuForGroup( self.GroupID, self.MenuText, self.MenuParentPath )
|
missionCommands.addSubMenuForGroup( self.GroupID, self.MenuText, self.MenuParentPath )
|
||||||
@@ -1132,15 +1044,12 @@ do
|
|||||||
Menu:Set()
|
Menu:Set()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Refreshes a new radio item for a group and submenus
|
--- Refreshes a new radio item for a group and submenus
|
||||||
-- @param #MENU_GROUP_DELAYED self
|
-- @param #MENU_GROUP_DELAYED self
|
||||||
-- @return #MENU_GROUP_DELAYED
|
-- @return #MENU_GROUP_DELAYED
|
||||||
function MENU_GROUP_DELAYED:Refresh()
|
function MENU_GROUP_DELAYED:Refresh()
|
||||||
|
|
||||||
do
|
do
|
||||||
missionCommands.removeItemForGroup( self.GroupID, self.MenuPath )
|
missionCommands.removeItemForGroup( self.GroupID, self.MenuPath )
|
||||||
missionCommands.addSubMenuForGroup( self.GroupID, self.MenuText, self.MenuParentPath )
|
missionCommands.addSubMenuForGroup( self.GroupID, self.MenuText, self.MenuParentPath )
|
||||||
@@ -1149,7 +1058,8 @@ do
|
|||||||
Menu:Refresh()
|
Menu:Refresh()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Removes the sub menus recursively of this MENU_GROUP_DELAYED.
|
--- Removes the sub menus recursively of this MENU_GROUP_DELAYED.
|
||||||
@@ -1158,7 +1068,6 @@ do
|
|||||||
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
|
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
|
||||||
-- @return #MENU_GROUP_DELAYED self
|
-- @return #MENU_GROUP_DELAYED self
|
||||||
function MENU_GROUP_DELAYED:RemoveSubMenus( MenuStamp, MenuTag )
|
function MENU_GROUP_DELAYED:RemoveSubMenus( MenuStamp, MenuTag )
|
||||||
|
|
||||||
for MenuText, Menu in pairs( self.Menus or {} ) do
|
for MenuText, Menu in pairs( self.Menus or {} ) do
|
||||||
Menu:Remove( MenuStamp, MenuTag )
|
Menu:Remove( MenuStamp, MenuTag )
|
||||||
end
|
end
|
||||||
@@ -1167,18 +1076,15 @@ do
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Removes the main menu and sub menus recursively of this MENU_GROUP.
|
--- Removes the main menu and sub menus recursively of this MENU_GROUP.
|
||||||
-- @param #MENU_GROUP_DELAYED self
|
-- @param #MENU_GROUP_DELAYED self
|
||||||
-- @param MenuStamp
|
-- @param MenuStamp
|
||||||
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
|
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
|
||||||
-- @return #nil
|
-- @return #nil
|
||||||
function MENU_GROUP_DELAYED:Remove( MenuStamp, MenuTag )
|
function MENU_GROUP_DELAYED:Remove( MenuStamp, MenuTag )
|
||||||
|
|
||||||
MENU_INDEX:PrepareGroup( self.Group )
|
MENU_INDEX:PrepareGroup( self.Group )
|
||||||
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
|
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
|
||||||
local GroupMenu = MENU_INDEX:HasGroupMenu( self.Group, Path )
|
local GroupMenu = MENU_INDEX:HasGroupMenu( self.Group, Path )
|
||||||
|
|
||||||
if GroupMenu == self then
|
if GroupMenu == self then
|
||||||
self:RemoveSubMenus( MenuStamp, MenuTag )
|
self:RemoveSubMenus( MenuStamp, MenuTag )
|
||||||
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
||||||
@@ -1223,18 +1129,15 @@ do
|
|||||||
-- @param CommandMenuArgument An argument for the function.
|
-- @param CommandMenuArgument An argument for the function.
|
||||||
-- @return #MENU_GROUP_COMMAND_DELAYED
|
-- @return #MENU_GROUP_COMMAND_DELAYED
|
||||||
function MENU_GROUP_COMMAND_DELAYED:New( Group, MenuText, ParentMenu, CommandMenuFunction, ... )
|
function MENU_GROUP_COMMAND_DELAYED:New( Group, MenuText, ParentMenu, CommandMenuFunction, ... )
|
||||||
|
|
||||||
MENU_INDEX:PrepareGroup( Group )
|
MENU_INDEX:PrepareGroup( Group )
|
||||||
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
|
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
|
||||||
local GroupMenu = MENU_INDEX:HasGroupMenu( Group, Path )
|
local GroupMenu = MENU_INDEX:HasGroupMenu( Group, Path )
|
||||||
|
|
||||||
if GroupMenu then
|
if GroupMenu then
|
||||||
GroupMenu:SetCommandMenuFunction( CommandMenuFunction )
|
GroupMenu:SetCommandMenuFunction( CommandMenuFunction )
|
||||||
GroupMenu:SetCommandMenuArguments( arg )
|
GroupMenu:SetCommandMenuArguments( arg )
|
||||||
return GroupMenu
|
return GroupMenu
|
||||||
else
|
else
|
||||||
self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) )
|
self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) )
|
||||||
|
|
||||||
MENU_INDEX:SetGroupMenu( Group, Path, self )
|
MENU_INDEX:SetGroupMenu( Group, Path, self )
|
||||||
|
|
||||||
self.Group = Group
|
self.Group = Group
|
||||||
@@ -1250,33 +1153,29 @@ do
|
|||||||
self:SetParentMenu( self.MenuText, self )
|
self:SetParentMenu( self.MenuText, self )
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Refreshes a radio item for a group
|
--- Refreshes a radio item for a group
|
||||||
-- @param #MENU_GROUP_COMMAND_DELAYED self
|
-- @param #MENU_GROUP_COMMAND_DELAYED self
|
||||||
-- @return #MENU_GROUP_COMMAND_DELAYED
|
-- @return #MENU_GROUP_COMMAND_DELAYED
|
||||||
function MENU_GROUP_COMMAND_DELAYED:Set()
|
function MENU_GROUP_COMMAND_DELAYED:Set()
|
||||||
|
|
||||||
do
|
do
|
||||||
if not self.MenuSet then
|
if not self.MenuSet then
|
||||||
self.MenuPath = missionCommands.addCommandForGroup( self.GroupID, self.MenuText, self.MenuParentPath, self.MenuCallHandler )
|
self.MenuPath = missionCommands.addCommandForGroup( self.GroupID, self.MenuText, self.MenuParentPath, self.MenuCallHandler )
|
||||||
self.MenuSet = true
|
self.MenuSet = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Refreshes a radio item for a group
|
--- Refreshes a radio item for a group
|
||||||
-- @param #MENU_GROUP_COMMAND_DELAYED self
|
-- @param #MENU_GROUP_COMMAND_DELAYED self
|
||||||
-- @return #MENU_GROUP_COMMAND_DELAYED
|
-- @return #MENU_GROUP_COMMAND_DELAYED
|
||||||
function MENU_GROUP_COMMAND_DELAYED:Refresh()
|
function MENU_GROUP_COMMAND_DELAYED:Refresh()
|
||||||
|
|
||||||
do
|
do
|
||||||
missionCommands.removeItemForGroup( self.GroupID, self.MenuPath )
|
missionCommands.removeItemForGroup( self.GroupID, self.MenuPath )
|
||||||
missionCommands.addCommandForGroup( self.GroupID, self.MenuText, self.MenuParentPath, self.MenuCallHandler )
|
missionCommands.addCommandForGroup( self.GroupID, self.MenuText, self.MenuParentPath, self.MenuCallHandler )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Removes a menu structure for a group.
|
--- Removes a menu structure for a group.
|
||||||
@@ -1285,11 +1184,9 @@ do
|
|||||||
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
|
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
|
||||||
-- @return #nil
|
-- @return #nil
|
||||||
function MENU_GROUP_COMMAND_DELAYED:Remove( MenuStamp, MenuTag )
|
function MENU_GROUP_COMMAND_DELAYED:Remove( MenuStamp, MenuTag )
|
||||||
|
|
||||||
MENU_INDEX:PrepareGroup( self.Group )
|
MENU_INDEX:PrepareGroup( self.Group )
|
||||||
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
|
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
|
||||||
local GroupMenu = MENU_INDEX:HasGroupMenu( self.Group, Path )
|
local GroupMenu = MENU_INDEX:HasGroupMenu( self.Group, Path )
|
||||||
|
|
||||||
if GroupMenu == self then
|
if GroupMenu == self then
|
||||||
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
||||||
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
|
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
|
||||||
@@ -1308,6 +1205,4 @@ do
|
|||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
--- **Core** - Informs the players using messages during a simulation.
|
--- **Core** - Informs the players using messages during a simulation.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ## Features:
|
-- ## Features:
|
||||||
--
|
--
|
||||||
-- * A more advanced messaging system using the DCS message system.
|
-- * A more advanced messaging system using the DCS message system.
|
||||||
-- * Time messages.
|
-- * Time messages.
|
||||||
-- * Send messages based on a message type, which has a pre-defined duration that can be tweaked in SETTINGS.
|
-- * Send messages based on a message type, which has a pre-defined duration that can be tweaked in SETTINGS.
|
||||||
-- * Send message to all players.
|
-- * Send message to all players.
|
||||||
-- * Send messages to a coalition.
|
-- * Send messages to a coalition.
|
||||||
-- * Send messages to a specific group.
|
-- * Send messages to a specific group.
|
||||||
|
-- * Send messages to a specific unit or client.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- @module Core.Message
|
-- @module Core.Message
|
||||||
-- @image Core_Message.JPG
|
-- @image Core_Message.JPG
|
||||||
|
|
||||||
@@ -23,42 +24,43 @@
|
|||||||
--- Message System to display Messages to Clients, Coalitions or All.
|
--- Message System to display Messages to Clients, Coalitions or All.
|
||||||
-- Messages are shown on the display panel for an amount of seconds, and will then disappear.
|
-- Messages are shown on the display panel for an amount of seconds, and will then disappear.
|
||||||
-- Messages can contain a category which is indicating the category of the message.
|
-- Messages can contain a category which is indicating the category of the message.
|
||||||
--
|
--
|
||||||
-- ## MESSAGE construction
|
-- ## MESSAGE construction
|
||||||
--
|
--
|
||||||
-- Messages are created with @{#MESSAGE.New}. Note that when the MESSAGE object is created, no message is sent yet.
|
-- Messages are created with @{#MESSAGE.New}. Note that when the MESSAGE object is created, no message is sent yet.
|
||||||
-- To send messages, you need to use the To functions.
|
-- To send messages, you need to use the To functions.
|
||||||
--
|
--
|
||||||
-- ## Send messages to an audience
|
-- ## Send messages to an audience
|
||||||
--
|
--
|
||||||
-- Messages are sent:
|
-- Messages are sent:
|
||||||
--
|
--
|
||||||
-- * To a @{Client} using @{#MESSAGE.ToClient}().
|
-- * To a @{Client} using @{#MESSAGE.ToClient}().
|
||||||
-- * To a @{Wrapper.Group} using @{#MESSAGE.ToGroup}()
|
-- * To a @{Wrapper.Group} using @{#MESSAGE.ToGroup}()
|
||||||
|
-- * To a @{Wrapper.Unit} using @{#MESSAGE.ToUnit}()
|
||||||
-- * To a coalition using @{#MESSAGE.ToCoalition}().
|
-- * To a coalition using @{#MESSAGE.ToCoalition}().
|
||||||
-- * To the red coalition using @{#MESSAGE.ToRed}().
|
-- * To the red coalition using @{#MESSAGE.ToRed}().
|
||||||
-- * To the blue coalition using @{#MESSAGE.ToBlue}().
|
-- * To the blue coalition using @{#MESSAGE.ToBlue}().
|
||||||
-- * To all Players using @{#MESSAGE.ToAll}().
|
-- * To all Players using @{#MESSAGE.ToAll}().
|
||||||
--
|
--
|
||||||
-- ## Send conditionally to an audience
|
-- ## Send conditionally to an audience
|
||||||
--
|
--
|
||||||
-- Messages can be sent conditionally to an audience (when a condition is true):
|
-- Messages can be sent conditionally to an audience (when a condition is true):
|
||||||
--
|
--
|
||||||
-- * To all players using @{#MESSAGE.ToAllIf}().
|
-- * To all players using @{#MESSAGE.ToAllIf}().
|
||||||
-- * To a coalition using @{#MESSAGE.ToCoalitionIf}().
|
-- * To a coalition using @{#MESSAGE.ToCoalitionIf}().
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ### Author: **FlightControl**
|
-- ### Author: **FlightControl**
|
||||||
-- ### Contributions:
|
-- ### Contributions:
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- @field #MESSAGE
|
-- @field #MESSAGE
|
||||||
MESSAGE = {
|
MESSAGE = {
|
||||||
ClassName = "MESSAGE",
|
ClassName = "MESSAGE",
|
||||||
MessageCategory = 0,
|
MessageCategory = 0,
|
||||||
MessageID = 0,
|
MessageID = 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Message Types
|
--- Message Types
|
||||||
@@ -68,10 +70,9 @@ MESSAGE.Type = {
|
|||||||
Information = "Information",
|
Information = "Information",
|
||||||
Briefing = "Briefing Report",
|
Briefing = "Briefing Report",
|
||||||
Overview = "Overview Report",
|
Overview = "Overview Report",
|
||||||
Detailed = "Detailed Report"
|
Detailed = "Detailed Report",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
--- Creates a new MESSAGE object. Note that these MESSAGE objects are not yet displayed on the display panel. You must use the functions @{ToClient} or @{ToCoalition} or @{ToAll} to send these Messages to the respective recipients.
|
--- Creates a new MESSAGE object. Note that these MESSAGE objects are not yet displayed on the display panel. You must use the functions @{ToClient} or @{ToCoalition} or @{ToAll} to send these Messages to the respective recipients.
|
||||||
-- @param self
|
-- @param self
|
||||||
-- @param #string MessageText is the text of the Message.
|
-- @param #string MessageText is the text of the Message.
|
||||||
@@ -80,52 +81,52 @@ MESSAGE.Type = {
|
|||||||
-- @param #boolean ClearScreen (optional) Clear all previous messages if true.
|
-- @param #boolean ClearScreen (optional) Clear all previous messages if true.
|
||||||
-- @return #MESSAGE
|
-- @return #MESSAGE
|
||||||
-- @usage
|
-- @usage
|
||||||
-- -- Create a series of new Messages.
|
--
|
||||||
-- -- MessageAll is meant to be sent to all players, for 25 seconds, and is classified as "Score".
|
-- -- Create a series of new Messages.
|
||||||
-- -- MessageRED is meant to be sent to the RED players only, for 10 seconds, and is classified as "End of Mission", with ID "Win".
|
-- -- MessageAll is meant to be sent to all players, for 25 seconds, and is classified as "Score".
|
||||||
-- -- MessageClient1 is meant to be sent to a Client, for 25 seconds, and is classified as "Score", with ID "Score".
|
-- -- MessageRED is meant to be sent to the RED players only, for 10 seconds, and is classified as "End of Mission", with ID "Win".
|
||||||
-- -- MessageClient1 is meant to be sent to a Client, for 25 seconds, and is classified as "Score", with ID "Score".
|
-- -- MessageClient1 is meant to be sent to a Client, for 25 seconds, and is classified as "Score", with ID "Score".
|
||||||
-- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", 25, "End of Mission" )
|
-- -- MessageClient1 is meant to be sent to a Client, for 25 seconds, and is classified as "Score", with ID "Score".
|
||||||
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", 25, "Penalty" )
|
-- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", 25, "End of Mission" )
|
||||||
-- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", 25, "Score" )
|
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", 25, "Penalty" )
|
||||||
-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", 25, "Score")
|
-- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", 25, "Score" )
|
||||||
|
-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", 25, "Score")
|
||||||
|
--
|
||||||
function MESSAGE:New( MessageText, MessageDuration, MessageCategory, ClearScreen )
|
function MESSAGE:New( MessageText, MessageDuration, MessageCategory, ClearScreen )
|
||||||
local self = BASE:Inherit( self, BASE:New() )
|
local self = BASE:Inherit( self, BASE:New() )
|
||||||
self:F( { MessageText, MessageDuration, MessageCategory } )
|
self:F( { MessageText, MessageDuration, MessageCategory } )
|
||||||
|
|
||||||
|
|
||||||
self.MessageType = nil
|
self.MessageType = nil
|
||||||
|
|
||||||
-- When no MessageCategory is given, we don't show it as a title...
|
-- When no MessageCategory is given, we don't show it as a title...
|
||||||
if MessageCategory and MessageCategory ~= "" then
|
if MessageCategory and MessageCategory ~= "" then
|
||||||
if MessageCategory:sub(-1) ~= "\n" then
|
if MessageCategory:sub( -1 ) ~= "\n" then
|
||||||
self.MessageCategory = MessageCategory .. ": "
|
self.MessageCategory = MessageCategory .. ": "
|
||||||
else
|
else
|
||||||
self.MessageCategory = MessageCategory:sub( 1, -2 ) .. ":\n"
|
self.MessageCategory = MessageCategory:sub( 1, -2 ) .. ":\n"
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
self.MessageCategory = ""
|
self.MessageCategory = ""
|
||||||
end
|
end
|
||||||
|
|
||||||
self.ClearScreen=false
|
self.ClearScreen = false
|
||||||
if ClearScreen~=nil then
|
if ClearScreen ~= nil then
|
||||||
self.ClearScreen=ClearScreen
|
self.ClearScreen = ClearScreen
|
||||||
end
|
end
|
||||||
|
|
||||||
self.MessageDuration = MessageDuration or 5
|
self.MessageDuration = MessageDuration or 5
|
||||||
self.MessageTime = timer.getTime()
|
self.MessageTime = timer.getTime()
|
||||||
self.MessageText = MessageText:gsub("^\n","",1):gsub("\n$","",1)
|
self.MessageText = MessageText:gsub( "^\n", "", 1 ):gsub( "\n$", "", 1 )
|
||||||
|
|
||||||
self.MessageSent = false
|
|
||||||
self.MessageGroup = false
|
|
||||||
self.MessageCoalition = false
|
|
||||||
|
|
||||||
return self
|
self.MessageSent = false
|
||||||
|
self.MessageGroup = false
|
||||||
|
self.MessageCoalition = false
|
||||||
|
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Creates a new MESSAGE object of a certain type.
|
||||||
--- Creates a new MESSAGE object of a certain type.
|
-- Note that these MESSAGE objects are not yet displayed on the display panel.
|
||||||
-- Note that these MESSAGE objects are not yet displayed on the display panel.
|
|
||||||
-- You must use the functions @{ToClient} or @{ToCoalition} or @{ToAll} to send these Messages to the respective recipients.
|
-- You must use the functions @{ToClient} or @{ToCoalition} or @{ToAll} to send these Messages to the respective recipients.
|
||||||
-- The message display times are automatically defined based on the timing settings in the @{Settings} menu.
|
-- The message display times are automatically defined based on the timing settings in the @{Settings} menu.
|
||||||
-- @param self
|
-- @param self
|
||||||
@@ -134,83 +135,86 @@ end
|
|||||||
-- @param #boolean ClearScreen (optional) Clear all previous messages.
|
-- @param #boolean ClearScreen (optional) Clear all previous messages.
|
||||||
-- @return #MESSAGE
|
-- @return #MESSAGE
|
||||||
-- @usage
|
-- @usage
|
||||||
|
--
|
||||||
-- MessageAll = MESSAGE:NewType( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", MESSAGE.Type.Information )
|
-- MessageAll = MESSAGE:NewType( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", MESSAGE.Type.Information )
|
||||||
-- MessageRED = MESSAGE:NewType( "To the RED Players: You receive a penalty because you've killed one of your own units", MESSAGE.Type.Information )
|
-- MessageRED = MESSAGE:NewType( "To the RED Players: You receive a penalty because you've killed one of your own units", MESSAGE.Type.Information )
|
||||||
-- MessageClient1 = MESSAGE:NewType( "Congratulations, you've just hit a target", MESSAGE.Type.Update )
|
-- MessageClient1 = MESSAGE:NewType( "Congratulations, you've just hit a target", MESSAGE.Type.Update )
|
||||||
-- MessageClient2 = MESSAGE:NewType( "Congratulations, you've just killed a target", MESSAGE.Type.Update )
|
-- MessageClient2 = MESSAGE:NewType( "Congratulations, you've just killed a target", MESSAGE.Type.Update )
|
||||||
|
--
|
||||||
function MESSAGE:NewType( MessageText, MessageType, ClearScreen )
|
function MESSAGE:NewType( MessageText, MessageType, ClearScreen )
|
||||||
|
|
||||||
local self = BASE:Inherit( self, BASE:New() )
|
local self = BASE:Inherit( self, BASE:New() )
|
||||||
self:F( { MessageText } )
|
self:F( { MessageText } )
|
||||||
|
|
||||||
self.MessageType = MessageType
|
self.MessageType = MessageType
|
||||||
|
|
||||||
self.ClearScreen=false
|
self.ClearScreen = false
|
||||||
if ClearScreen~=nil then
|
if ClearScreen ~= nil then
|
||||||
self.ClearScreen=ClearScreen
|
self.ClearScreen = ClearScreen
|
||||||
end
|
end
|
||||||
|
|
||||||
self.MessageTime = timer.getTime()
|
self.MessageTime = timer.getTime()
|
||||||
self.MessageText = MessageText:gsub("^\n","",1):gsub("\n$","",1)
|
self.MessageText = MessageText:gsub( "^\n", "", 1 ):gsub( "\n$", "", 1 )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Clears all previous messages from the screen before the new message is displayed. Not that this must come before all functions starting with ToX(), e.g. ToAll(), ToGroup() etc.
|
||||||
|
|
||||||
--- Clears all previous messages from the screen before the new message is displayed. Not that this must come before all functions starting with ToX(), e.g. ToAll(), ToGroup() etc.
|
|
||||||
-- @param #MESSAGE self
|
-- @param #MESSAGE self
|
||||||
-- @return #MESSAGE
|
-- @return #MESSAGE
|
||||||
function MESSAGE:Clear()
|
function MESSAGE:Clear()
|
||||||
self:F()
|
self:F()
|
||||||
self.ClearScreen=true
|
self.ClearScreen = true
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- Sends a MESSAGE to a Client Group. Note that the Group needs to be defined within the ME with the skillset "Client" or "Player".
|
--- Sends a MESSAGE to a Client Group. Note that the Group needs to be defined within the ME with the skillset "Client" or "Player".
|
||||||
-- @param #MESSAGE self
|
-- @param #MESSAGE self
|
||||||
-- @param Wrapper.Client#CLIENT Client is the Group of the Client.
|
-- @param Wrapper.Client#CLIENT Client is the Group of the Client.
|
||||||
-- @param Core.Settings#SETTINGS Settings Settings used to display the message.
|
-- @param Core.Settings#SETTINGS Settings Settings used to display the message.
|
||||||
-- @return #MESSAGE
|
-- @return #MESSAGE
|
||||||
-- @usage
|
-- @usage
|
||||||
-- -- Send the 2 messages created with the @{New} method to the Client Group.
|
|
||||||
-- -- Note that the Message of MessageClient2 is overwriting the Message of MessageClient1.
|
|
||||||
-- ClientGroup = Group.getByName( "ClientGroup" )
|
|
||||||
--
|
--
|
||||||
-- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" ):ToClient( ClientGroup )
|
-- -- Send the 2 messages created with the @{New} method to the Client Group.
|
||||||
-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" ):ToClient( ClientGroup )
|
-- -- Note that the Message of MessageClient2 is overwriting the Message of MessageClient1.
|
||||||
-- or
|
-- ClientGroup = Group.getByName( "ClientGroup" )
|
||||||
-- MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" ):ToClient( ClientGroup )
|
--
|
||||||
-- MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" ):ToClient( ClientGroup )
|
-- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" ):ToClient( ClientGroup )
|
||||||
-- or
|
-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" ):ToClient( ClientGroup )
|
||||||
-- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" )
|
-- or
|
||||||
-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" )
|
-- MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" ):ToClient( ClientGroup )
|
||||||
-- MessageClient1:ToClient( ClientGroup )
|
-- MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" ):ToClient( ClientGroup )
|
||||||
-- MessageClient2:ToClient( ClientGroup )
|
-- or
|
||||||
|
-- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" )
|
||||||
|
-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" )
|
||||||
|
-- MessageClient1:ToClient( ClientGroup )
|
||||||
|
-- MessageClient2:ToClient( ClientGroup )
|
||||||
|
--
|
||||||
function MESSAGE:ToClient( Client, Settings )
|
function MESSAGE:ToClient( Client, Settings )
|
||||||
self:F( Client )
|
self:F( Client )
|
||||||
|
|
||||||
if Client and Client:GetClientGroupID() then
|
if Client and Client:GetClientGroupID() then
|
||||||
|
|
||||||
if self.MessageType then
|
if self.MessageType then
|
||||||
local Settings = Settings or ( Client and _DATABASE:GetPlayerSettings( Client:GetPlayerName() ) ) or _SETTINGS -- Core.Settings#SETTINGS
|
local Settings = Settings or ( Client and _DATABASE:GetPlayerSettings( Client:GetPlayerName() ) ) or _SETTINGS -- Core.Settings#SETTINGS
|
||||||
self.MessageDuration = Settings:GetMessageTime( self.MessageType )
|
self.MessageDuration = Settings:GetMessageTime( self.MessageType )
|
||||||
self.MessageCategory = "" -- self.MessageType .. ": "
|
self.MessageCategory = "" -- self.MessageType .. ": "
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local Unit = Client:GetClient()
|
||||||
|
|
||||||
if self.MessageDuration ~= 0 then
|
if self.MessageDuration ~= 0 then
|
||||||
local ClientGroupID = Client:GetClientGroupID()
|
local ClientGroupID = Client:GetClientGroupID()
|
||||||
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
|
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
|
||||||
trigger.action.outTextForGroup( ClientGroupID, self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration , self.ClearScreen)
|
--trigger.action.outTextForGroup( ClientGroupID, self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration , self.ClearScreen)
|
||||||
|
trigger.action.outTextForUnit( Unit:GetID(), self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration , self.ClearScreen)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Sends a MESSAGE to a Group.
|
--- Sends a MESSAGE to a Group.
|
||||||
-- @param #MESSAGE self
|
-- @param #MESSAGE self
|
||||||
-- @param Wrapper.Group#GROUP Group to which the message is displayed.
|
-- @param Wrapper.Group#GROUP Group to which the message is displayed.
|
||||||
-- @return #MESSAGE Message object.
|
-- @return #MESSAGE Message object.
|
||||||
@@ -218,74 +222,129 @@ function MESSAGE:ToGroup( Group, Settings )
|
|||||||
self:F( Group.GroupName )
|
self:F( Group.GroupName )
|
||||||
|
|
||||||
if Group then
|
if Group then
|
||||||
|
|
||||||
|
if self.MessageType then
|
||||||
|
local Settings = Settings or (Group and _DATABASE:GetPlayerSettings( Group:GetPlayerName() )) or _SETTINGS -- Core.Settings#SETTINGS
|
||||||
|
self.MessageDuration = Settings:GetMessageTime( self.MessageType )
|
||||||
|
self.MessageCategory = "" -- self.MessageType .. ": "
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.MessageDuration ~= 0 then
|
||||||
|
self:T( self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ) .. " / " .. self.MessageDuration )
|
||||||
|
trigger.action.outTextForGroup( Group:GetID(), self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ), self.MessageDuration, self.ClearScreen )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Sends a MESSAGE to a Unit.
|
||||||
|
-- @param #MESSAGE self
|
||||||
|
-- @param Wrapper.Unit#UNIT Unit to which the message is displayed.
|
||||||
|
-- @return #MESSAGE Message object.
|
||||||
|
function MESSAGE:ToUnit( Unit, Settings )
|
||||||
|
self:F( Unit.IdentifiableName )
|
||||||
|
|
||||||
|
if Unit then
|
||||||
|
|
||||||
if self.MessageType then
|
if self.MessageType then
|
||||||
local Settings = Settings or ( Group and _DATABASE:GetPlayerSettings( Group:GetPlayerName() ) ) or _SETTINGS -- Core.Settings#SETTINGS
|
local Settings = Settings or ( Unit and _DATABASE:GetPlayerSettings( Unit:GetPlayerName() ) ) or _SETTINGS -- Core.Settings#SETTINGS
|
||||||
self.MessageDuration = Settings:GetMessageTime( self.MessageType )
|
self.MessageDuration = Settings:GetMessageTime( self.MessageType )
|
||||||
self.MessageCategory = "" -- self.MessageType .. ": "
|
self.MessageCategory = "" -- self.MessageType .. ": "
|
||||||
end
|
end
|
||||||
|
|
||||||
if self.MessageDuration ~= 0 then
|
if self.MessageDuration ~= 0 then
|
||||||
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
|
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
|
||||||
trigger.action.outTextForGroup( Group:GetID(), self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration, self.ClearScreen )
|
trigger.action.outTextForUnit( Unit:GetID(), self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration, self.ClearScreen )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
--- Sends a MESSAGE to the Blue coalition.
|
|
||||||
-- @param #MESSAGE self
|
|
||||||
-- @return #MESSAGE
|
|
||||||
-- @usage
|
|
||||||
-- -- Send a message created with the @{New} method to the BLUE coalition.
|
|
||||||
-- MessageBLUE = MESSAGE:New( "To the BLUE Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToBlue()
|
|
||||||
-- or
|
|
||||||
-- MESSAGE:New( "To the BLUE Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToBlue()
|
|
||||||
-- or
|
|
||||||
-- MessageBLUE = MESSAGE:New( "To the BLUE Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" )
|
|
||||||
-- MessageBLUE:ToBlue()
|
|
||||||
function MESSAGE:ToBlue()
|
|
||||||
self:F()
|
|
||||||
|
|
||||||
self:ToCoalition( coalition.side.BLUE )
|
--- Sends a MESSAGE to a Unit.
|
||||||
|
-- @param #MESSAGE self
|
||||||
return self
|
-- @param Wrapper.Unit#UNIT Unit to which the message is displayed.
|
||||||
|
-- @return #MESSAGE Message object.
|
||||||
|
function MESSAGE:ToUnit( Unit, Settings )
|
||||||
|
self:F( Unit.IdentifiableName )
|
||||||
|
|
||||||
|
if Unit then
|
||||||
|
|
||||||
|
if self.MessageType then
|
||||||
|
local Settings = Settings or ( Unit and _DATABASE:GetPlayerSettings( Unit:GetPlayerName() ) ) or _SETTINGS -- Core.Settings#SETTINGS
|
||||||
|
self.MessageDuration = Settings:GetMessageTime( self.MessageType )
|
||||||
|
self.MessageCategory = "" -- self.MessageType .. ": "
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.MessageDuration ~= 0 then
|
||||||
|
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
|
||||||
|
trigger.action.outTextForUnit( Unit:GetID(), self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration, self.ClearScreen )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Sends a MESSAGE to the Red Coalition.
|
--- Sends a MESSAGE to the Blue coalition.
|
||||||
-- @param #MESSAGE self
|
-- @param #MESSAGE self
|
||||||
-- @return #MESSAGE
|
-- @return #MESSAGE
|
||||||
-- @usage
|
-- @usage
|
||||||
-- -- Send a message created with the @{New} method to the RED coalition.
|
--
|
||||||
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToRed()
|
-- -- Send a message created with the @{New} method to the BLUE coalition.
|
||||||
-- or
|
-- MessageBLUE = MESSAGE:New( "To the BLUE Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToBlue()
|
||||||
-- MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToRed()
|
-- or
|
||||||
-- or
|
-- MESSAGE:New( "To the BLUE Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToBlue()
|
||||||
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" )
|
-- or
|
||||||
-- MessageRED:ToRed()
|
-- MessageBLUE = MESSAGE:New( "To the BLUE Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" )
|
||||||
function MESSAGE:ToRed( )
|
-- MessageBLUE:ToBlue()
|
||||||
self:F()
|
--
|
||||||
|
function MESSAGE:ToBlue()
|
||||||
|
self:F()
|
||||||
|
|
||||||
self:ToCoalition( coalition.side.RED )
|
self:ToCoalition( coalition.side.BLUE )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Sends a MESSAGE to a Coalition.
|
--- Sends a MESSAGE to the Red Coalition.
|
||||||
|
-- @param #MESSAGE self
|
||||||
|
-- @return #MESSAGE
|
||||||
|
-- @usage
|
||||||
|
--
|
||||||
|
-- -- Send a message created with the @{New} method to the RED coalition.
|
||||||
|
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToRed()
|
||||||
|
-- or
|
||||||
|
-- MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToRed()
|
||||||
|
-- or
|
||||||
|
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" )
|
||||||
|
-- MessageRED:ToRed()
|
||||||
|
--
|
||||||
|
function MESSAGE:ToRed()
|
||||||
|
self:F()
|
||||||
|
|
||||||
|
self:ToCoalition( coalition.side.RED )
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Sends a MESSAGE to a Coalition.
|
||||||
-- @param #MESSAGE self
|
-- @param #MESSAGE self
|
||||||
-- @param #DCS.coalition.side CoalitionSide @{#DCS.coalition.side} to which the message is displayed.
|
-- @param #DCS.coalition.side CoalitionSide @{#DCS.coalition.side} to which the message is displayed.
|
||||||
-- @param Core.Settings#SETTINGS Settings (Optional) Settings for message display.
|
-- @param Core.Settings#SETTINGS Settings (Optional) Settings for message display.
|
||||||
-- @return #MESSAGE Message object.
|
-- @return #MESSAGE Message object.
|
||||||
-- @usage
|
-- @usage
|
||||||
-- -- Send a message created with the @{New} method to the RED coalition.
|
--
|
||||||
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToCoalition( coalition.side.RED )
|
-- -- Send a message created with the @{New} method to the RED coalition.
|
||||||
-- or
|
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToCoalition( coalition.side.RED )
|
||||||
-- MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToCoalition( coalition.side.RED )
|
-- or
|
||||||
-- or
|
-- MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToCoalition( coalition.side.RED )
|
||||||
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" )
|
-- or
|
||||||
-- MessageRED:ToCoalition( coalition.side.RED )
|
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" )
|
||||||
|
-- MessageRED:ToCoalition( coalition.side.RED )
|
||||||
|
--
|
||||||
function MESSAGE:ToCoalition( CoalitionSide, Settings )
|
function MESSAGE:ToCoalition( CoalitionSide, Settings )
|
||||||
self:F( CoalitionSide )
|
self:F( CoalitionSide )
|
||||||
|
|
||||||
if self.MessageType then
|
if self.MessageType then
|
||||||
local Settings = Settings or _SETTINGS -- Core.Settings#SETTINGS
|
local Settings = Settings or _SETTINGS -- Core.Settings#SETTINGS
|
||||||
@@ -293,20 +352,20 @@ function MESSAGE:ToCoalition( CoalitionSide, Settings )
|
|||||||
self.MessageCategory = "" -- self.MessageType .. ": "
|
self.MessageCategory = "" -- self.MessageType .. ": "
|
||||||
end
|
end
|
||||||
|
|
||||||
if CoalitionSide then
|
if CoalitionSide then
|
||||||
if self.MessageDuration ~= 0 then
|
if self.MessageDuration ~= 0 then
|
||||||
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
|
self:T( self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ) .. " / " .. self.MessageDuration )
|
||||||
trigger.action.outTextForCoalition( CoalitionSide, self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration, self.ClearScreen )
|
trigger.action.outTextForCoalition( CoalitionSide, self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ), self.MessageDuration, self.ClearScreen )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Sends a MESSAGE to a Coalition if the given Condition is true.
|
--- Sends a MESSAGE to a Coalition if the given Condition is true.
|
||||||
-- @param #MESSAGE self
|
-- @param #MESSAGE self
|
||||||
-- @param CoalitionSide needs to be filled out by the defined structure of the standard scripting engine @{coalition.side}.
|
-- @param CoalitionSide needs to be filled out by the defined structure of the standard scripting engine @{coalition.side}.
|
||||||
-- @param #boolean Condition Sends the message only if the condition is true.
|
-- @param #boolean Condition Sends the message only if the condition is true.
|
||||||
-- @return #MESSAGE self
|
-- @return #MESSAGE self
|
||||||
function MESSAGE:ToCoalitionIf( CoalitionSide, Condition )
|
function MESSAGE:ToCoalitionIf( CoalitionSide, Condition )
|
||||||
self:F( CoalitionSide )
|
self:F( CoalitionSide )
|
||||||
@@ -314,7 +373,7 @@ function MESSAGE:ToCoalitionIf( CoalitionSide, Condition )
|
|||||||
if Condition and Condition == true then
|
if Condition and Condition == true then
|
||||||
self:ToCoalition( CoalitionSide )
|
self:ToCoalition( CoalitionSide )
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -323,14 +382,16 @@ end
|
|||||||
-- @param Core.Settings#Settings Settings (Optional) Settings for message display.
|
-- @param Core.Settings#Settings Settings (Optional) Settings for message display.
|
||||||
-- @return #MESSAGE
|
-- @return #MESSAGE
|
||||||
-- @usage
|
-- @usage
|
||||||
-- -- Send a message created to all players.
|
--
|
||||||
-- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" ):ToAll()
|
-- -- Send a message created to all players.
|
||||||
-- or
|
-- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" ):ToAll()
|
||||||
-- MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" ):ToAll()
|
-- or
|
||||||
-- or
|
-- MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" ):ToAll()
|
||||||
-- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" )
|
-- or
|
||||||
-- MessageAll:ToAll()
|
-- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" )
|
||||||
function MESSAGE:ToAll(Settings)
|
-- MessageAll:ToAll()
|
||||||
|
--
|
||||||
|
function MESSAGE:ToAll( Settings )
|
||||||
self:F()
|
self:F()
|
||||||
|
|
||||||
if self.MessageType then
|
if self.MessageType then
|
||||||
@@ -340,14 +401,13 @@ function MESSAGE:ToAll(Settings)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if self.MessageDuration ~= 0 then
|
if self.MessageDuration ~= 0 then
|
||||||
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
|
self:T( self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ) .. " / " .. self.MessageDuration )
|
||||||
trigger.action.outText( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration, self.ClearScreen )
|
trigger.action.outText( self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ), self.MessageDuration, self.ClearScreen )
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Sends a MESSAGE to all players if the given Condition is true.
|
--- Sends a MESSAGE to all players if the given Condition is true.
|
||||||
-- @param #MESSAGE self
|
-- @param #MESSAGE self
|
||||||
-- @return #MESSAGE
|
-- @return #MESSAGE
|
||||||
@@ -357,5 +417,5 @@ function MESSAGE:ToAllIf( Condition )
|
|||||||
self:ToAll()
|
self:ToAll()
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,13 @@
|
|||||||
--- **Core** - Provides a handy means to create messages and reports.
|
--- **Core** - Provides a handy means to create messages and reports.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ## Features:
|
-- ## Features:
|
||||||
--
|
--
|
||||||
-- * Create text blocks that are formatted.
|
-- * Create text blocks that are formatted.
|
||||||
-- * Create automatic indents.
|
-- * Create automatic indents.
|
||||||
-- * Variate the delimiters between reporting lines.
|
-- * Variate the delimiters between reporting lines.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ### Authors: FlightControl : Design & Programming
|
-- ### Authors: FlightControl : Design & Programming
|
||||||
@@ -15,7 +15,6 @@
|
|||||||
-- @module Core.Report
|
-- @module Core.Report
|
||||||
-- @image Core_Report.JPG
|
-- @image Core_Report.JPG
|
||||||
|
|
||||||
|
|
||||||
--- @type REPORT
|
--- @type REPORT
|
||||||
-- @extends Core.Base#BASE
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
@@ -36,7 +35,7 @@ function REPORT:New( Title )
|
|||||||
|
|
||||||
self.Report = {}
|
self.Report = {}
|
||||||
|
|
||||||
self:SetTitle( Title or "" )
|
self:SetTitle( Title or "" )
|
||||||
self:SetIndent( 3 )
|
self:SetIndent( 3 )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
@@ -45,28 +44,26 @@ end
|
|||||||
--- Has the REPORT Text?
|
--- Has the REPORT Text?
|
||||||
-- @param #REPORT self
|
-- @param #REPORT self
|
||||||
-- @return #boolean
|
-- @return #boolean
|
||||||
function REPORT:HasText() --R2.1
|
function REPORT:HasText() -- R2.1
|
||||||
|
|
||||||
return #self.Report > 0
|
return #self.Report > 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Set indent of a REPORT.
|
--- Set indent of a REPORT.
|
||||||
-- @param #REPORT self
|
-- @param #REPORT self
|
||||||
-- @param #number Indent
|
-- @param #number Indent
|
||||||
-- @return #REPORT
|
-- @return #REPORT
|
||||||
function REPORT:SetIndent( Indent ) --R2.1
|
function REPORT:SetIndent( Indent ) -- R2.1
|
||||||
self.Indent = Indent
|
self.Indent = Indent
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Add a new line to a REPORT.
|
--- Add a new line to a REPORT.
|
||||||
-- @param #REPORT self
|
-- @param #REPORT self
|
||||||
-- @param #string Text
|
-- @param #string Text
|
||||||
-- @return #REPORT
|
-- @return #REPORT
|
||||||
function REPORT:Add( Text )
|
function REPORT:Add( Text )
|
||||||
self.Report[#self.Report+1] = Text
|
self.Report[#self.Report + 1] = Text
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -76,17 +73,17 @@ end
|
|||||||
-- @param #string Separator (optional) The start of each report line can begin with an optional separator character. This can be a "-", or "#", or "*". You're free to choose what you find the best.
|
-- @param #string Separator (optional) The start of each report line can begin with an optional separator character. This can be a "-", or "#", or "*". You're free to choose what you find the best.
|
||||||
-- @return #REPORT
|
-- @return #REPORT
|
||||||
function REPORT:AddIndent( Text, Separator )
|
function REPORT:AddIndent( Text, Separator )
|
||||||
self.Report[#self.Report+1] = ( ( Separator and Separator .. string.rep( " ", self.Indent - 1 ) ) or string.rep(" ", self.Indent ) ) .. Text:gsub("\n","\n"..string.rep( " ", self.Indent ) )
|
self.Report[#self.Report + 1] = ((Separator and Separator .. string.rep( " ", self.Indent - 1 )) or string.rep( " ", self.Indent )) .. Text:gsub( "\n", "\n" .. string.rep( " ", self.Indent ) )
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Produces the text of the report, taking into account an optional delimeter, which is \n by default.
|
--- Produces the text of the report, taking into account an optional delimiter, which is \n by default.
|
||||||
-- @param #REPORT self
|
-- @param #REPORT self
|
||||||
-- @param #string Delimiter (optional) A delimiter text.
|
-- @param #string Delimiter (optional) A delimiter text.
|
||||||
-- @return #string The report text.
|
-- @return #string The report text.
|
||||||
function REPORT:Text( Delimiter )
|
function REPORT:Text( Delimiter )
|
||||||
Delimiter = Delimiter or "\n"
|
Delimiter = Delimiter or "\n"
|
||||||
local ReportText = ( self.Title ~= "" and self.Title .. Delimiter or self.Title ) .. table.concat( self.Report, Delimiter ) or ""
|
local ReportText = (self.Title ~= "" and self.Title .. Delimiter or self.Title) .. table.concat( self.Report, Delimiter ) or ""
|
||||||
return ReportText
|
return ReportText
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -95,7 +92,7 @@ end
|
|||||||
-- @param #string Title The title of the report.
|
-- @param #string Title The title of the report.
|
||||||
-- @return #REPORT
|
-- @return #REPORT
|
||||||
function REPORT:SetTitle( Title )
|
function REPORT:SetTitle( Title )
|
||||||
self.Title = Title
|
self.Title = Title
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,36 +1,36 @@
|
|||||||
--- **Core** -- SCHEDULEDISPATCHER dispatches the different schedules.
|
--- **Core** -- SCHEDULEDISPATCHER dispatches the different schedules.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- Takes care of the creation and dispatching of scheduled functions for SCHEDULER objects.
|
-- Takes care of the creation and dispatching of scheduled functions for SCHEDULER objects.
|
||||||
--
|
--
|
||||||
-- This class is tricky and needs some thorough explanation.
|
-- This class is tricky and needs some thorough explanation.
|
||||||
-- SCHEDULE classes are used to schedule functions for objects, or as persistent objects.
|
-- SCHEDULE classes are used to schedule functions for objects, or as persistent objects.
|
||||||
-- The SCHEDULEDISPATCHER class ensures that:
|
-- The SCHEDULEDISPATCHER class ensures that:
|
||||||
--
|
--
|
||||||
-- - Scheduled functions are planned according the SCHEDULER object parameters.
|
-- - Scheduled functions are planned according the SCHEDULER object parameters.
|
||||||
-- - Scheduled functions are repeated when requested, according the SCHEDULER object parameters.
|
-- - Scheduled functions are repeated when requested, according the SCHEDULER object parameters.
|
||||||
-- - Scheduled functions are automatically removed when the schedule is finished, according the SCHEDULER object parameters.
|
-- - Scheduled functions are automatically removed when the schedule is finished, according the SCHEDULER object parameters.
|
||||||
--
|
--
|
||||||
-- The SCHEDULEDISPATCHER class will manage SCHEDULER object in memory during garbage collection:
|
-- The SCHEDULEDISPATCHER class will manage SCHEDULER object in memory during garbage collection:
|
||||||
--
|
--
|
||||||
-- - When a SCHEDULER object is not attached to another object (that is, it's first :Schedule() parameter is nil), then the SCHEDULER object is _persistent_ within memory.
|
-- - When a SCHEDULER object is not attached to another object (that is, it's first :Schedule() parameter is nil), then the SCHEDULER object is _persistent_ within memory.
|
||||||
-- - When a SCHEDULER object *is* attached to another object, then the SCHEDULER object is _not persistent_ within memory after a garbage collection!
|
-- - When a SCHEDULER object *is* attached to another object, then the SCHEDULER object is _not persistent_ within memory after a garbage collection!
|
||||||
--
|
--
|
||||||
-- The none persistency of SCHEDULERS attached to objects is required to allow SCHEDULER objects to be garbage collectged, when the parent object is also desroyed or nillified and garbage collected.
|
-- The non-persistency of SCHEDULERS attached to objects is required to allow SCHEDULER objects to be garbage collected when the parent object is destroyed, or set to nil and garbage collected.
|
||||||
-- Even when there are pending timer scheduled functions to be executed for the SCHEDULER object,
|
-- Even when there are pending timer scheduled functions to be executed for the SCHEDULER object,
|
||||||
-- these will not be executed anymore when the SCHEDULER object has been destroyed.
|
-- these will not be executed anymore when the SCHEDULER object has been destroyed.
|
||||||
--
|
--
|
||||||
-- The SCHEDULEDISPATCHER allows multiple scheduled functions to be planned and executed for one SCHEDULER object.
|
-- The SCHEDULEDISPATCHER allows multiple scheduled functions to be planned and executed for one SCHEDULER object.
|
||||||
-- The SCHEDULER object therefore keeps a table of "CallID's", which are returned after each planning of a new scheduled function by the SCHEDULEDISPATCHER.
|
-- The SCHEDULER object therefore keeps a table of "CallID's", which are returned after each planning of a new scheduled function by the SCHEDULEDISPATCHER.
|
||||||
-- The SCHEDULER object plans new scheduled functions through the @{Core.Scheduler#SCHEDULER.Schedule}() method.
|
-- The SCHEDULER object plans new scheduled functions through the @{Core.Scheduler#SCHEDULER.Schedule}() method.
|
||||||
-- The Schedule() method returns the CallID that is the reference ID for each planned schedule.
|
-- The Schedule() method returns the CallID that is the reference ID for each planned schedule.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ### Contributions: -
|
-- ### Contributions: -
|
||||||
-- ### Authors: FlightControl : Design & Programming
|
-- ### Authors: FlightControl : Design & Programming
|
||||||
--
|
--
|
||||||
-- @module Core.ScheduleDispatcher
|
-- @module Core.ScheduleDispatcher
|
||||||
-- @image Core_Schedule_Dispatcher.JPG
|
-- @image Core_Schedule_Dispatcher.JPG
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
-- @type SCHEDULEDISPATCHER
|
-- @type SCHEDULEDISPATCHER
|
||||||
-- @field #string ClassName Name of the class.
|
-- @field #string ClassName Name of the class.
|
||||||
-- @field #number CallID Call ID counter.
|
-- @field #number CallID Call ID counter.
|
||||||
-- @field #table PersistentSchedulers Persistant schedulers.
|
-- @field #table PersistentSchedulers Persistent schedulers.
|
||||||
-- @field #table ObjectSchedulers Schedulers that only exist as long as the master object exists.
|
-- @field #table ObjectSchedulers Schedulers that only exist as long as the master object exists.
|
||||||
-- @field #table Schedule Meta table setmetatable( {}, { __mode = "k" } ).
|
-- @field #table Schedule Meta table setmetatable( {}, { __mode = "k" } ).
|
||||||
-- @extends Core.Base#BASE
|
-- @extends Core.Base#BASE
|
||||||
@@ -46,11 +46,11 @@
|
|||||||
--- The SCHEDULEDISPATCHER structure
|
--- The SCHEDULEDISPATCHER structure
|
||||||
-- @type SCHEDULEDISPATCHER
|
-- @type SCHEDULEDISPATCHER
|
||||||
SCHEDULEDISPATCHER = {
|
SCHEDULEDISPATCHER = {
|
||||||
ClassName = "SCHEDULEDISPATCHER",
|
ClassName = "SCHEDULEDISPATCHER",
|
||||||
CallID = 0,
|
CallID = 0,
|
||||||
PersistentSchedulers = {},
|
PersistentSchedulers = {},
|
||||||
ObjectSchedulers = {},
|
ObjectSchedulers = {},
|
||||||
Schedule = nil,
|
Schedule = nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Player data table holding all important parameters of each player.
|
--- Player data table holding all important parameters of each player.
|
||||||
@@ -58,7 +58,7 @@ SCHEDULEDISPATCHER = {
|
|||||||
-- @field #function Function The schedule function to be called.
|
-- @field #function Function The schedule function to be called.
|
||||||
-- @field #table Arguments Schedule function arguments.
|
-- @field #table Arguments Schedule function arguments.
|
||||||
-- @field #number Start Start time in seconds.
|
-- @field #number Start Start time in seconds.
|
||||||
-- @field #number Repeat Repeat time intervall in seconds.
|
-- @field #number Repeat Repeat time interval in seconds.
|
||||||
-- @field #number Randomize Randomization factor [0,1].
|
-- @field #number Randomize Randomization factor [0,1].
|
||||||
-- @field #number Stop Stop time in seconds.
|
-- @field #number Stop Stop time in seconds.
|
||||||
-- @field #number StartTime Time in seconds when the scheduler is created.
|
-- @field #number StartTime Time in seconds when the scheduler is created.
|
||||||
@@ -77,7 +77,7 @@ end
|
|||||||
|
|
||||||
--- Add a Schedule to the ScheduleDispatcher.
|
--- Add a Schedule to the ScheduleDispatcher.
|
||||||
-- The development of this method was really tidy.
|
-- The development of this method was really tidy.
|
||||||
-- It is constructed as such that a garbage collection is executed on the weak tables, when the Scheduler is nillified.
|
-- It is constructed as such that a garbage collection is executed on the weak tables, when the Scheduler is set to nil.
|
||||||
-- Nothing of this code should be modified without testing it thoroughly.
|
-- Nothing of this code should be modified without testing it thoroughly.
|
||||||
-- @param #SCHEDULEDISPATCHER self
|
-- @param #SCHEDULEDISPATCHER self
|
||||||
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
|
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
|
||||||
@@ -85,7 +85,7 @@ end
|
|||||||
-- @param #table ScheduleArguments Table of arguments passed to the ScheduleFunction.
|
-- @param #table ScheduleArguments Table of arguments passed to the ScheduleFunction.
|
||||||
-- @param #number Start Start time in seconds.
|
-- @param #number Start Start time in seconds.
|
||||||
-- @param #number Repeat Repeat interval in seconds.
|
-- @param #number Repeat Repeat interval in seconds.
|
||||||
-- @param #number Randomize Radomization factor [0,1].
|
-- @param #number Randomize Randomization factor [0,1].
|
||||||
-- @param #number Stop Stop time in seconds.
|
-- @param #number Stop Stop time in seconds.
|
||||||
-- @param #number TraceLevel Trace level [0,3].
|
-- @param #number TraceLevel Trace level [0,3].
|
||||||
-- @param Core.Fsm#FSM Fsm Finite state model.
|
-- @param Core.Fsm#FSM Fsm Finite state model.
|
||||||
@@ -95,39 +95,40 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
|||||||
|
|
||||||
-- Increase counter.
|
-- Increase counter.
|
||||||
self.CallID = self.CallID + 1
|
self.CallID = self.CallID + 1
|
||||||
|
|
||||||
-- Create ID.
|
-- Create ID.
|
||||||
local CallID = self.CallID .. "#" .. ( Scheduler.MasterObject and Scheduler.MasterObject.GetClassNameAndID and Scheduler.MasterObject:GetClassNameAndID() or "" ) or ""
|
local CallID = self.CallID .. "#" .. (Scheduler.MasterObject and Scheduler.MasterObject.GetClassNameAndID and Scheduler.MasterObject:GetClassNameAndID() or "") or ""
|
||||||
|
|
||||||
self:T2(string.format("Adding schedule #%d CallID=%s", self.CallID, CallID))
|
self:T2( string.format( "Adding schedule #%d CallID=%s", self.CallID, CallID ) )
|
||||||
|
|
||||||
-- Initialize PersistentSchedulers
|
-- Initialize PersistentSchedulers
|
||||||
self.PersistentSchedulers = self.PersistentSchedulers or {}
|
self.PersistentSchedulers = self.PersistentSchedulers or {}
|
||||||
|
|
||||||
-- Initialize the ObjectSchedulers array, which is a weakly coupled table.
|
-- Initialize the ObjectSchedulers array, which is a weakly coupled table.
|
||||||
-- If the object used as the key is nil, then the garbage collector will remove the item from the Functions array.
|
-- If the object used as the key is nil, then the garbage collector will remove the item from the Functions array.
|
||||||
self.ObjectSchedulers = self.ObjectSchedulers or setmetatable( {}, { __mode = "v" } )
|
self.ObjectSchedulers = self.ObjectSchedulers or setmetatable( {}, { __mode = "v" } )
|
||||||
|
|
||||||
if Scheduler.MasterObject then
|
if Scheduler.MasterObject then
|
||||||
|
--env.info("FF Object Scheduler")
|
||||||
self.ObjectSchedulers[CallID] = Scheduler
|
self.ObjectSchedulers[CallID] = Scheduler
|
||||||
self:F3( { CallID = CallID, ObjectScheduler = tostring(self.ObjectSchedulers[CallID]), MasterObject = tostring(Scheduler.MasterObject) } )
|
self:F3( { CallID = CallID, ObjectScheduler = tostring( self.ObjectSchedulers[CallID] ), MasterObject = tostring( Scheduler.MasterObject ) } )
|
||||||
else
|
else
|
||||||
|
--env.info("FF Persistent Scheduler")
|
||||||
self.PersistentSchedulers[CallID] = Scheduler
|
self.PersistentSchedulers[CallID] = Scheduler
|
||||||
self:F3( { CallID = CallID, PersistentScheduler = self.PersistentSchedulers[CallID] } )
|
self:F3( { CallID = CallID, PersistentScheduler = self.PersistentSchedulers[CallID] } )
|
||||||
end
|
end
|
||||||
|
|
||||||
self.Schedule = self.Schedule or setmetatable( {}, { __mode = "k" } )
|
self.Schedule = self.Schedule or setmetatable( {}, { __mode = "k" } )
|
||||||
self.Schedule[Scheduler] = self.Schedule[Scheduler] or {}
|
self.Schedule[Scheduler] = self.Schedule[Scheduler] or {}
|
||||||
self.Schedule[Scheduler][CallID] = {} --#SCHEDULEDISPATCHER.ScheduleData
|
self.Schedule[Scheduler][CallID] = {} -- #SCHEDULEDISPATCHER.ScheduleData
|
||||||
self.Schedule[Scheduler][CallID].Function = ScheduleFunction
|
self.Schedule[Scheduler][CallID].Function = ScheduleFunction
|
||||||
self.Schedule[Scheduler][CallID].Arguments = ScheduleArguments
|
self.Schedule[Scheduler][CallID].Arguments = ScheduleArguments
|
||||||
self.Schedule[Scheduler][CallID].StartTime = timer.getTime() + ( Start or 0 )
|
self.Schedule[Scheduler][CallID].StartTime = timer.getTime() + ( Start or 0 )
|
||||||
self.Schedule[Scheduler][CallID].Start = Start + 0.1
|
self.Schedule[Scheduler][CallID].Start = Start + 0.001
|
||||||
self.Schedule[Scheduler][CallID].Repeat = Repeat or 0
|
self.Schedule[Scheduler][CallID].Repeat = Repeat or 0
|
||||||
self.Schedule[Scheduler][CallID].Randomize = Randomize or 0
|
self.Schedule[Scheduler][CallID].Randomize = Randomize or 0
|
||||||
self.Schedule[Scheduler][CallID].Stop = Stop
|
self.Schedule[Scheduler][CallID].Stop = Stop
|
||||||
|
|
||||||
|
|
||||||
-- This section handles the tracing of the scheduled calls.
|
-- This section handles the tracing of the scheduled calls.
|
||||||
-- Because these calls will be executed with a delay, we inspect the place where these scheduled calls are initiated.
|
-- Because these calls will be executed with a delay, we inspect the place where these scheduled calls are initiated.
|
||||||
-- The Info structure contains the output of the debug.getinfo() calls, which inspects the call stack for the function name, line number and source name.
|
-- The Info structure contains the output of the debug.getinfo() calls, which inspects the call stack for the function name, line number and source name.
|
||||||
@@ -149,10 +150,10 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
|||||||
-- Therefore, in the call stack, at the TraceLevel these functions are mentioned as "tail calls", and the Info.name field will be nil as a result.
|
-- Therefore, in the call stack, at the TraceLevel these functions are mentioned as "tail calls", and the Info.name field will be nil as a result.
|
||||||
-- To obtain the correct function name for FSM object calls, the function is mentioned in the call stack at a higher stack level.
|
-- To obtain the correct function name for FSM object calls, the function is mentioned in the call stack at a higher stack level.
|
||||||
-- So when function name stored in Info.name is nil, then I inspect the function name within the call stack one level higher.
|
-- So when function name stored in Info.name is nil, then I inspect the function name within the call stack one level higher.
|
||||||
-- So this little piece of code does its magic wonderfully, preformance overhead is neglectible, as scheduled calls don't happen that often.
|
-- So this little piece of code does its magic wonderfully, performance overhead is negligible, as scheduled calls don't happen that often.
|
||||||
|
|
||||||
local Info = {}
|
local Info = {}
|
||||||
|
|
||||||
if debug then
|
if debug then
|
||||||
TraceLevel = TraceLevel or 2
|
TraceLevel = TraceLevel or 2
|
||||||
Info = debug.getinfo( TraceLevel, "nlS" )
|
Info = debug.getinfo( TraceLevel, "nlS" )
|
||||||
@@ -166,7 +167,7 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
|||||||
|
|
||||||
--- Function passed to the DCS timer.scheduleFunction()
|
--- Function passed to the DCS timer.scheduleFunction()
|
||||||
self.Schedule[Scheduler][CallID].CallHandler = function( Params )
|
self.Schedule[Scheduler][CallID].CallHandler = function( Params )
|
||||||
|
|
||||||
local CallID = Params.CallID
|
local CallID = Params.CallID
|
||||||
local Info = Params.Info or {}
|
local Info = Params.Info or {}
|
||||||
local Source = Info.source or "?"
|
local Source = Info.source or "?"
|
||||||
@@ -180,27 +181,27 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
|||||||
end
|
end
|
||||||
return errmsg
|
return errmsg
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Get object or persistant scheduler object.
|
-- Get object or persistent scheduler object.
|
||||||
local Scheduler = self.ObjectSchedulers[CallID] --Core.Scheduler#SCHEDULER
|
local Scheduler = self.ObjectSchedulers[CallID] -- Core.Scheduler#SCHEDULER
|
||||||
if not Scheduler then
|
if not Scheduler then
|
||||||
Scheduler = self.PersistentSchedulers[CallID]
|
Scheduler = self.PersistentSchedulers[CallID]
|
||||||
end
|
end
|
||||||
|
|
||||||
--self:T3( { Scheduler = Scheduler } )
|
-- self:T3( { Scheduler = Scheduler } )
|
||||||
|
|
||||||
if Scheduler then
|
if Scheduler then
|
||||||
|
|
||||||
local MasterObject = tostring(Scheduler.MasterObject)
|
local MasterObject = tostring( Scheduler.MasterObject )
|
||||||
|
|
||||||
-- Schedule object.
|
|
||||||
local Schedule = self.Schedule[Scheduler][CallID] --#SCHEDULEDISPATCHER.ScheduleData
|
|
||||||
|
|
||||||
--self:T3( { Schedule = Schedule } )
|
|
||||||
|
|
||||||
local SchedulerObject = Scheduler.MasterObject --Scheduler.SchedulerObject Now is this the Maste or Scheduler object?
|
-- Schedule object.
|
||||||
|
local Schedule = self.Schedule[Scheduler][CallID] -- #SCHEDULEDISPATCHER.ScheduleData
|
||||||
|
|
||||||
|
-- self:T3( { Schedule = Schedule } )
|
||||||
|
|
||||||
|
local SchedulerObject = Scheduler.MasterObject -- Scheduler.SchedulerObject Now is this the Master or Scheduler object?
|
||||||
local ShowTrace = Scheduler.ShowTrace
|
local ShowTrace = Scheduler.ShowTrace
|
||||||
|
|
||||||
local ScheduleFunction = Schedule.Function
|
local ScheduleFunction = Schedule.Function
|
||||||
local ScheduleArguments = Schedule.Arguments or {}
|
local ScheduleArguments = Schedule.Arguments or {}
|
||||||
local Start = Schedule.Start
|
local Start = Schedule.Start
|
||||||
@@ -208,17 +209,17 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
|||||||
local Randomize = Schedule.Randomize or 0
|
local Randomize = Schedule.Randomize or 0
|
||||||
local Stop = Schedule.Stop or 0
|
local Stop = Schedule.Stop or 0
|
||||||
local ScheduleID = Schedule.ScheduleID
|
local ScheduleID = Schedule.ScheduleID
|
||||||
|
|
||||||
|
local Prefix = (Repeat == 0) and "--->" or "+++>"
|
||||||
local Prefix = ( Repeat == 0 ) and "--->" or "+++>"
|
|
||||||
|
|
||||||
local Status, Result
|
local Status, Result
|
||||||
--self:E( { SchedulerObject = SchedulerObject } )
|
-- self:E( { SchedulerObject = SchedulerObject } )
|
||||||
if SchedulerObject then
|
if SchedulerObject then
|
||||||
local function Timer()
|
local function Timer()
|
||||||
if ShowTrace then
|
if ShowTrace then
|
||||||
SchedulerObject:T( Prefix .. Name .. ":" .. Line .. " (" .. Source .. ")" )
|
SchedulerObject:T( Prefix .. Name .. ":" .. Line .. " (" .. Source .. ")" )
|
||||||
end
|
end
|
||||||
|
-- The master object is passed as first parameter. A few :Schedule() calls in MOOSE expect this currently. But in principle it should be removed.
|
||||||
return ScheduleFunction( SchedulerObject, unpack( ScheduleArguments ) )
|
return ScheduleFunction( SchedulerObject, unpack( ScheduleArguments ) )
|
||||||
end
|
end
|
||||||
Status, Result = xpcall( Timer, ErrorHandler )
|
Status, Result = xpcall( Timer, ErrorHandler )
|
||||||
@@ -227,40 +228,39 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
|||||||
if ShowTrace then
|
if ShowTrace then
|
||||||
self:T( Prefix .. Name .. ":" .. Line .. " (" .. Source .. ")" )
|
self:T( Prefix .. Name .. ":" .. Line .. " (" .. Source .. ")" )
|
||||||
end
|
end
|
||||||
return ScheduleFunction( unpack( ScheduleArguments ) )
|
return ScheduleFunction( unpack( ScheduleArguments ) )
|
||||||
end
|
end
|
||||||
Status, Result = xpcall( Timer, ErrorHandler )
|
Status, Result = xpcall( Timer, ErrorHandler )
|
||||||
end
|
end
|
||||||
|
|
||||||
local CurrentTime = timer.getTime()
|
local CurrentTime = timer.getTime()
|
||||||
local StartTime = Schedule.StartTime
|
local StartTime = Schedule.StartTime
|
||||||
|
|
||||||
-- Debug info.
|
-- Debug info.
|
||||||
self:F3( { CallID=CallID, ScheduleID=ScheduleID, Master = MasterObject, CurrentTime = CurrentTime, StartTime = StartTime, Start = Start, Repeat = Repeat, Randomize = Randomize, Stop = Stop } )
|
self:F3( { CallID = CallID, ScheduleID = ScheduleID, Master = MasterObject, CurrentTime = CurrentTime, StartTime = StartTime, Start = Start, Repeat = Repeat, Randomize = Randomize, Stop = Stop } )
|
||||||
|
|
||||||
|
if Status and ((Result == nil) or (Result and Result ~= false)) then
|
||||||
if Status and (( Result == nil ) or ( Result and Result ~= false ) ) then
|
|
||||||
|
if Repeat ~= 0 and ((Stop == 0) or (Stop ~= 0 and CurrentTime <= StartTime + Stop)) then
|
||||||
if Repeat ~= 0 and ( ( Stop == 0 ) or ( Stop ~= 0 and CurrentTime <= StartTime + Stop ) ) then
|
local ScheduleTime = CurrentTime + Repeat + math.random( -(Randomize * Repeat / 2), (Randomize * Repeat / 2) ) + 0.0001 -- Accuracy
|
||||||
local ScheduleTime = CurrentTime + Repeat + math.random(- ( Randomize * Repeat / 2 ), ( Randomize * Repeat / 2 )) + 0.0001 -- Accuracy
|
-- self:T3( { Repeat = CallID, CurrentTime, ScheduleTime, ScheduleArguments } )
|
||||||
--self:T3( { Repeat = CallID, CurrentTime, ScheduleTime, ScheduleArguments } )
|
|
||||||
return ScheduleTime -- returns the next time the function needs to be called.
|
return ScheduleTime -- returns the next time the function needs to be called.
|
||||||
else
|
else
|
||||||
self:Stop( Scheduler, CallID )
|
self:Stop( Scheduler, CallID )
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
self:Stop( Scheduler, CallID )
|
self:Stop( Scheduler, CallID )
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
self:I( "<<<>" .. Name .. ":" .. Line .. " (" .. Source .. ")" )
|
self:I( "<<<>" .. Name .. ":" .. Line .. " (" .. Source .. ")" )
|
||||||
end
|
end
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
self:Start( Scheduler, CallID, Info )
|
self:Start( Scheduler, CallID, Info )
|
||||||
|
|
||||||
return CallID
|
return CallID
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -284,67 +284,67 @@ end
|
|||||||
-- @param #string Info (Optional) Debug info.
|
-- @param #string Info (Optional) Debug info.
|
||||||
function SCHEDULEDISPATCHER:Start( Scheduler, CallID, Info )
|
function SCHEDULEDISPATCHER:Start( Scheduler, CallID, Info )
|
||||||
self:F2( { Start = CallID, Scheduler = Scheduler } )
|
self:F2( { Start = CallID, Scheduler = Scheduler } )
|
||||||
|
|
||||||
if CallID then
|
if CallID then
|
||||||
|
|
||||||
local Schedule = self.Schedule[Scheduler][CallID] --#SCHEDULEDISPATCHER.ScheduleData
|
local Schedule = self.Schedule[Scheduler][CallID] -- #SCHEDULEDISPATCHER.ScheduleData
|
||||||
|
|
||||||
-- Only start when there is no ScheduleID defined!
|
-- Only start when there is no ScheduleID defined!
|
||||||
-- This prevents to "Start" the scheduler twice with the same CallID...
|
-- This prevents to "Start" the scheduler twice with the same CallID...
|
||||||
if not Schedule.ScheduleID then
|
if not Schedule.ScheduleID then
|
||||||
|
|
||||||
-- Current time in seconds.
|
-- Current time in seconds.
|
||||||
local Tnow=timer.getTime()
|
local Tnow = timer.getTime()
|
||||||
|
|
||||||
Schedule.StartTime = Tnow -- Set the StartTime field to indicate when the scheduler started.
|
Schedule.StartTime = Tnow -- Set the StartTime field to indicate when the scheduler started.
|
||||||
|
|
||||||
-- Start DCS schedule function https://wiki.hoggitworld.com/view/DCS_func_scheduleFunction
|
-- Start DCS schedule function https://wiki.hoggitworld.com/view/DCS_func_scheduleFunction
|
||||||
Schedule.ScheduleID = timer.scheduleFunction(Schedule.CallHandler, { CallID = CallID, Info = Info }, Tnow + Schedule.Start)
|
Schedule.ScheduleID = timer.scheduleFunction( Schedule.CallHandler, { CallID = CallID, Info = Info }, Tnow + Schedule.Start )
|
||||||
|
|
||||||
self:T(string.format("Starting scheduledispatcher Call ID=%s ==> Schedule ID=%s", tostring(CallID), tostring(Schedule.ScheduleID)))
|
self:T( string.format( "Starting SCHEDULEDISPATCHER Call ID=%s ==> Schedule ID=%s", tostring( CallID ), tostring( Schedule.ScheduleID ) ) )
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
-- Recursive.
|
-- Recursive.
|
||||||
for CallID, Schedule in pairs( self.Schedule[Scheduler] or {} ) do
|
for CallID, Schedule in pairs( self.Schedule[Scheduler] or {} ) do
|
||||||
self:Start( Scheduler, CallID, Info ) -- Recursive
|
self:Start( Scheduler, CallID, Info ) -- Recursive
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Stop dispatcher.
|
--- Stop dispatcher.
|
||||||
-- @param #SCHEDULEDISPATCHER self
|
-- @param #SCHEDULEDISPATCHER self
|
||||||
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
|
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
|
||||||
-- @param #table CallID Call ID.
|
-- @param #string CallID (Optional) Scheduler Call ID. If nil, all pending schedules are stopped recursively.
|
||||||
function SCHEDULEDISPATCHER:Stop( Scheduler, CallID )
|
function SCHEDULEDISPATCHER:Stop( Scheduler, CallID )
|
||||||
self:F2( { Stop = CallID, Scheduler = Scheduler } )
|
self:F2( { Stop = CallID, Scheduler = Scheduler } )
|
||||||
|
|
||||||
if CallID then
|
if CallID then
|
||||||
|
|
||||||
local Schedule = self.Schedule[Scheduler][CallID] --#SCHEDULEDISPATCHER.ScheduleData
|
local Schedule = self.Schedule[Scheduler][CallID] -- #SCHEDULEDISPATCHER.ScheduleData
|
||||||
|
|
||||||
-- Only stop when there is a ScheduleID defined for the CallID. So, when the scheduler was stopped before, do nothing.
|
-- Only stop when there is a ScheduleID defined for the CallID. So, when the scheduler was stopped before, do nothing.
|
||||||
if Schedule.ScheduleID then
|
if Schedule.ScheduleID then
|
||||||
|
|
||||||
self:T(string.format("scheduledispatcher stopping scheduler CallID=%s, ScheduleID=%s", tostring(CallID), tostring(Schedule.ScheduleID)))
|
self:T( string.format( "SCHEDULEDISPATCHER stopping scheduler CallID=%s, ScheduleID=%s", tostring( CallID ), tostring( Schedule.ScheduleID ) ) )
|
||||||
|
|
||||||
-- Remove schedule function https://wiki.hoggitworld.com/view/DCS_func_removeFunction
|
-- Remove schedule function https://wiki.hoggitworld.com/view/DCS_func_removeFunction
|
||||||
timer.removeFunction(Schedule.ScheduleID)
|
timer.removeFunction( Schedule.ScheduleID )
|
||||||
|
|
||||||
Schedule.ScheduleID = nil
|
Schedule.ScheduleID = nil
|
||||||
|
|
||||||
else
|
else
|
||||||
self:T(string.format("Error no ScheduleID for CallID=%s", tostring(CallID)))
|
self:T( string.format( "Error no ScheduleID for CallID=%s", tostring( CallID ) ) )
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
for CallID, Schedule in pairs( self.Schedule[Scheduler] or {} ) do
|
for CallID, Schedule in pairs( self.Schedule[Scheduler] or {} ) do
|
||||||
self:Stop( Scheduler, CallID ) -- Recursive
|
self:Stop( Scheduler, CallID ) -- Recursive
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -359,7 +359,7 @@ function SCHEDULEDISPATCHER:Clear( Scheduler )
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Shopw tracing info.
|
--- Show tracing info.
|
||||||
-- @param #SCHEDULEDISPATCHER self
|
-- @param #SCHEDULEDISPATCHER self
|
||||||
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
|
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
|
||||||
function SCHEDULEDISPATCHER:ShowTrace( Scheduler )
|
function SCHEDULEDISPATCHER:ShowTrace( Scheduler )
|
||||||
|
|||||||
@@ -3,39 +3,39 @@
|
|||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ## Features:
|
-- ## Features:
|
||||||
--
|
--
|
||||||
-- * Schedule functions over time,
|
-- * Schedule functions over time,
|
||||||
-- * optionally in an optional specified time interval,
|
-- * optionally in an optional specified time interval,
|
||||||
-- * optionally **repeating** with a specified time repeat interval,
|
-- * optionally **repeating** with a specified time repeat interval,
|
||||||
-- * optionally **randomizing** with a specified time interval randomization factor,
|
-- * optionally **randomizing** with a specified time interval randomization factor,
|
||||||
-- * optionally **stop** the repeating after a specified time interval.
|
-- * optionally **stop** the repeating after a specified time interval.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- # Demo Missions
|
-- # Demo Missions
|
||||||
--
|
--
|
||||||
-- ### [SCHEDULER Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/SCH%20-%20Scheduler)
|
-- ### [SCHEDULER Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SCH%20-%20Scheduler)
|
||||||
--
|
--
|
||||||
-- ### [SCHEDULER Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SCH%20-%20Scheduler)
|
-- ### [SCHEDULER Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SCH%20-%20Scheduler)
|
||||||
--
|
--
|
||||||
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
|
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
|
||||||
--
|
--
|
||||||
-- ===
|
|
||||||
--
|
|
||||||
-- # YouTube Channel
|
|
||||||
--
|
|
||||||
-- ### [SCHEDULER YouTube Channel (none)]()
|
|
||||||
--
|
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ### Contributions:
|
-- # YouTube Channel
|
||||||
--
|
--
|
||||||
|
-- ### [SCHEDULER YouTube Channel (none)]()
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ### Contributions:
|
||||||
|
--
|
||||||
-- * FlightControl : Concept & Testing
|
-- * FlightControl : Concept & Testing
|
||||||
--
|
--
|
||||||
-- ### Authors:
|
-- ### Authors:
|
||||||
--
|
--
|
||||||
-- * FlightControl : Design & Programming
|
-- * FlightControl : Design & Programming
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- @module Core.Scheduler
|
-- @module Core.Scheduler
|
||||||
@@ -48,62 +48,61 @@
|
|||||||
-- @field #boolean ShowTrace Trace info if true.
|
-- @field #boolean ShowTrace Trace info if true.
|
||||||
-- @extends Core.Base#BASE
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
|
|
||||||
--- Creates and handles schedules over time, which allow to execute code at specific time intervals with randomization.
|
--- Creates and handles schedules over time, which allow to execute code at specific time intervals with randomization.
|
||||||
--
|
--
|
||||||
-- A SCHEDULER can manage **multiple** (repeating) schedules. Each planned or executing schedule has a unique **ScheduleID**.
|
-- A SCHEDULER can manage **multiple** (repeating) schedules. Each planned or executing schedule has a unique **ScheduleID**.
|
||||||
-- The ScheduleID is returned when the method @{#SCHEDULER.Schedule}() is called.
|
-- The ScheduleID is returned when the method @{#SCHEDULER.Schedule}() is called.
|
||||||
-- It is recommended to store the ScheduleID in a variable, as it is used in the methods @{SCHEDULER.Start}() and @{SCHEDULER.Stop}(),
|
-- It is recommended to store the ScheduleID in a variable, as it is used in the methods @{SCHEDULER.Start}() and @{SCHEDULER.Stop}(),
|
||||||
-- which can start and stop specific repeating schedules respectively within a SCHEDULER object.
|
-- which can start and stop specific repeating schedules respectively within a SCHEDULER object.
|
||||||
--
|
--
|
||||||
-- ## SCHEDULER constructor
|
-- ## SCHEDULER constructor
|
||||||
--
|
--
|
||||||
-- The SCHEDULER class is quite easy to use, but note that the New constructor has variable parameters:
|
-- The SCHEDULER class is quite easy to use, but note that the New constructor has variable parameters:
|
||||||
--
|
--
|
||||||
-- The @{#SCHEDULER.New}() method returns 2 variables:
|
-- The @{#SCHEDULER.New}() method returns 2 variables:
|
||||||
--
|
--
|
||||||
-- 1. The SCHEDULER object reference.
|
-- 1. The SCHEDULER object reference.
|
||||||
-- 2. The first schedule planned in the SCHEDULER object.
|
-- 2. The first schedule planned in the SCHEDULER object.
|
||||||
--
|
--
|
||||||
-- To clarify the different appliances, lets have a look at the following examples:
|
-- To clarify the different appliances, lets have a look at the following examples:
|
||||||
--
|
--
|
||||||
-- ### Construct a SCHEDULER object without a persistent schedule.
|
-- ### Construct a SCHEDULER object without a persistent schedule.
|
||||||
--
|
--
|
||||||
-- * @{#SCHEDULER.New}( nil ): Setup a new SCHEDULER object, which is persistently executed after garbage collection.
|
-- * @{#SCHEDULER.New}( nil ): Setup a new SCHEDULER object, which is persistently executed after garbage collection.
|
||||||
--
|
--
|
||||||
-- MasterObject = SCHEDULER:New()
|
-- MasterObject = SCHEDULER:New()
|
||||||
-- SchedulerID = MasterObject:Schedule( nil, ScheduleFunction, {} )
|
-- SchedulerID = MasterObject:Schedule( nil, ScheduleFunction, {} )
|
||||||
--
|
--
|
||||||
-- The above example creates a new MasterObject, but does not schedule anything.
|
-- The above example creates a new MasterObject, but does not schedule anything.
|
||||||
-- A separate schedule is created by using the MasterObject using the method :Schedule..., which returns a ScheduleID
|
-- A separate schedule is created by using the MasterObject using the method :Schedule..., which returns a ScheduleID
|
||||||
--
|
--
|
||||||
-- ### Construct a SCHEDULER object without a volatile schedule, but volatile to the Object existence...
|
-- ### Construct a SCHEDULER object without a volatile schedule, but volatile to the Object existence...
|
||||||
--
|
--
|
||||||
-- * @{#SCHEDULER.New}( Object ): Setup a new SCHEDULER object, which is linked to the Object. When the Object is nillified or destroyed, the SCHEDULER object will also be destroyed and stopped after garbage collection.
|
-- * @{#SCHEDULER.New}( Object ): Setup a new SCHEDULER object, which is linked to the Object. When the Object is set to nil or destroyed, the SCHEDULER object will also be destroyed and stopped after garbage collection.
|
||||||
--
|
--
|
||||||
-- ZoneObject = ZONE:New( "ZoneName" )
|
-- ZoneObject = ZONE:New( "ZoneName" )
|
||||||
-- MasterObject = SCHEDULER:New( ZoneObject )
|
-- MasterObject = SCHEDULER:New( ZoneObject )
|
||||||
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {} )
|
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {} )
|
||||||
-- ...
|
-- ...
|
||||||
-- ZoneObject = nil
|
-- ZoneObject = nil
|
||||||
-- garbagecollect()
|
-- garbagecollect()
|
||||||
--
|
--
|
||||||
-- The above example creates a new MasterObject, but does not schedule anything, and is bound to the existence of ZoneObject, which is a ZONE.
|
-- The above example creates a new MasterObject, but does not schedule anything, and is bound to the existence of ZoneObject, which is a ZONE.
|
||||||
-- A separate schedule is created by using the MasterObject using the method :Schedule()..., which returns a ScheduleID
|
-- A separate schedule is created by using the MasterObject using the method :Schedule()..., which returns a ScheduleID
|
||||||
-- Later in the logic, the ZoneObject is put to nil, and garbage is collected.
|
-- Later in the logic, the ZoneObject is put to nil, and garbage is collected.
|
||||||
-- As a result, the MasterObject will cancel any planned schedule.
|
-- As a result, the MasterObject will cancel any planned schedule.
|
||||||
--
|
--
|
||||||
-- ### Construct a SCHEDULER object with a persistent schedule.
|
-- ### Construct a SCHEDULER object with a persistent schedule.
|
||||||
--
|
--
|
||||||
-- * @{#SCHEDULER.New}( nil, Function, FunctionArguments, Start, ... ): Setup a new persistent SCHEDULER object, and start a new schedule for the Function with the defined FunctionArguments according the Start and sequent parameters.
|
-- * @{#SCHEDULER.New}( nil, Function, FunctionArguments, Start, ... ): Setup a new persistent SCHEDULER object, and start a new schedule for the Function with the defined FunctionArguments according the Start and sequent parameters.
|
||||||
--
|
--
|
||||||
-- MasterObject, SchedulerID = SCHEDULER:New( nil, ScheduleFunction, {} )
|
-- MasterObject, SchedulerID = SCHEDULER:New( nil, ScheduleFunction, {} )
|
||||||
--
|
--
|
||||||
-- The above example creates a new MasterObject, and does schedule the first schedule as part of the call.
|
-- The above example creates a new MasterObject, and does schedule the first schedule as part of the call.
|
||||||
-- Note that 2 variables are returned here: MasterObject, ScheduleID...
|
-- Note that 2 variables are returned here: MasterObject, ScheduleID...
|
||||||
--
|
--
|
||||||
-- ### Construct a SCHEDULER object without a schedule, but volatile to the Object existence...
|
-- ### Construct a SCHEDULER object without a schedule, but volatile to the Object existence...
|
||||||
--
|
--
|
||||||
-- * @{#SCHEDULER.New}( Object, Function, FunctionArguments, Start, ... ): Setup a new SCHEDULER object, linked to Object, and start a new schedule for the Function with the defined FunctionArguments according the Start and sequent parameters.
|
-- * @{#SCHEDULER.New}( Object, Function, FunctionArguments, Start, ... ): Setup a new SCHEDULER object, linked to Object, and start a new schedule for the Function with the defined FunctionArguments according the Start and sequent parameters.
|
||||||
--
|
--
|
||||||
-- ZoneObject = ZONE:New( "ZoneName" )
|
-- ZoneObject = ZONE:New( "ZoneName" )
|
||||||
@@ -112,13 +111,13 @@
|
|||||||
-- ...
|
-- ...
|
||||||
-- ZoneObject = nil
|
-- ZoneObject = nil
|
||||||
-- garbagecollect()
|
-- garbagecollect()
|
||||||
--
|
--
|
||||||
-- The above example creates a new MasterObject, and schedules a method call (ScheduleFunction),
|
-- The above example creates a new MasterObject, and schedules a method call (ScheduleFunction),
|
||||||
-- and is bound to the existence of ZoneObject, which is a ZONE object (ZoneObject).
|
-- and is bound to the existence of ZoneObject, which is a ZONE object (ZoneObject).
|
||||||
-- Both a MasterObject and a SchedulerID variable are returned.
|
-- Both a MasterObject and a SchedulerID variable are returned.
|
||||||
-- Later in the logic, the ZoneObject is put to nil, and garbage is collected.
|
-- Later in the logic, the ZoneObject is put to nil, and garbage is collected.
|
||||||
-- As a result, the MasterObject will cancel the planned schedule.
|
-- As a result, the MasterObject will cancel the planned schedule.
|
||||||
--
|
--
|
||||||
-- ## SCHEDULER timer stopping and (re-)starting.
|
-- ## SCHEDULER timer stopping and (re-)starting.
|
||||||
--
|
--
|
||||||
-- The SCHEDULER can be stopped and restarted with the following methods:
|
-- The SCHEDULER can be stopped and restarted with the following methods:
|
||||||
@@ -133,70 +132,70 @@
|
|||||||
-- MasterObject:Stop( SchedulerID )
|
-- MasterObject:Stop( SchedulerID )
|
||||||
-- ...
|
-- ...
|
||||||
-- MasterObject:Start( SchedulerID )
|
-- MasterObject:Start( SchedulerID )
|
||||||
--
|
--
|
||||||
-- The above example creates a new MasterObject, and does schedule the first schedule as part of the call.
|
-- The above example creates a new MasterObject, and does schedule the first schedule as part of the call.
|
||||||
-- Note that 2 variables are returned here: MasterObject, ScheduleID...
|
-- Note that 2 variables are returned here: MasterObject, ScheduleID...
|
||||||
-- Later in the logic, the repeating schedule with SchedulerID is stopped.
|
-- Later in the logic, the repeating schedule with SchedulerID is stopped.
|
||||||
-- A bit later, the repeating schedule with SchedulerId is (re)-started.
|
-- A bit later, the repeating schedule with SchedulerId is (re)-started.
|
||||||
--
|
--
|
||||||
-- ## Create a new schedule
|
-- ## Create a new schedule
|
||||||
--
|
--
|
||||||
-- With the method @{#SCHEDULER.Schedule}() a new time event can be scheduled.
|
-- With the method @{#SCHEDULER.Schedule}() a new time event can be scheduled.
|
||||||
-- This method is used by the :New() constructor when a new schedule is planned.
|
-- This method is used by the :New() constructor when a new schedule is planned.
|
||||||
--
|
--
|
||||||
-- Consider the following code fragment of the SCHEDULER object creation.
|
-- Consider the following code fragment of the SCHEDULER object creation.
|
||||||
--
|
--
|
||||||
-- ZoneObject = ZONE:New( "ZoneName" )
|
-- ZoneObject = ZONE:New( "ZoneName" )
|
||||||
-- MasterObject = SCHEDULER:New( ZoneObject )
|
-- MasterObject = SCHEDULER:New( ZoneObject )
|
||||||
--
|
--
|
||||||
-- Several parameters can be specified that influence the behaviour of a Schedule.
|
-- Several parameters can be specified that influence the behavior of a Schedule.
|
||||||
--
|
--
|
||||||
-- ### A single schedule, immediately executed
|
-- ### A single schedule, immediately executed
|
||||||
--
|
--
|
||||||
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {} )
|
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {} )
|
||||||
--
|
--
|
||||||
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within milleseconds ...
|
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within milliseconds ...
|
||||||
--
|
--
|
||||||
-- ### A single schedule, planned over time
|
-- ### A single schedule, planned over time
|
||||||
--
|
--
|
||||||
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {}, 10 )
|
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {}, 10 )
|
||||||
--
|
--
|
||||||
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds ...
|
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds ...
|
||||||
--
|
--
|
||||||
-- ### A schedule with a repeating time interval, planned over time
|
-- ### A schedule with a repeating time interval, planned over time
|
||||||
--
|
--
|
||||||
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 60 )
|
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 60 )
|
||||||
--
|
--
|
||||||
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds,
|
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds,
|
||||||
-- and repeating 60 every seconds ...
|
-- and repeating 60 every seconds ...
|
||||||
--
|
--
|
||||||
-- ### A schedule with a repeating time interval, planned over time, with time interval randomization
|
-- ### A schedule with a repeating time interval, planned over time, with time interval randomization
|
||||||
--
|
--
|
||||||
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 60, 0.5 )
|
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 60, 0.5 )
|
||||||
--
|
--
|
||||||
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds,
|
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds,
|
||||||
-- and repeating 60 seconds, with a 50% time interval randomization ...
|
-- and repeating 60 seconds, with a 50% time interval randomization ...
|
||||||
-- So the repeating time interval will be randomized using the **0.5**,
|
-- So the repeating time interval will be randomized using the **0.5**,
|
||||||
-- and will calculate between **60 - ( 60 * 0.5 )** and **60 + ( 60 * 0.5 )** for each repeat,
|
-- and will calculate between **60 - ( 60 * 0.5 )** and **60 + ( 60 * 0.5 )** for each repeat,
|
||||||
-- which is in this example between **30** and **90** seconds.
|
-- which is in this example between **30** and **90** seconds.
|
||||||
--
|
--
|
||||||
-- ### A schedule with a repeating time interval, planned over time, with time interval randomization, and stop after a time interval
|
-- ### A schedule with a repeating time interval, planned over time, with time interval randomization, and stop after a time interval
|
||||||
--
|
--
|
||||||
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 60, 0.5, 300 )
|
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 60, 0.5, 300 )
|
||||||
--
|
--
|
||||||
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds,
|
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds,
|
||||||
-- The schedule will repeat every 60 seconds.
|
-- The schedule will repeat every 60 seconds.
|
||||||
-- So the repeating time interval will be randomized using the **0.5**,
|
-- So the repeating time interval will be randomized using the **0.5**,
|
||||||
-- and will calculate between **60 - ( 60 * 0.5 )** and **60 + ( 60 * 0.5 )** for each repeat,
|
-- and will calculate between **60 - ( 60 * 0.5 )** and **60 + ( 60 * 0.5 )** for each repeat,
|
||||||
-- which is in this example between **30** and **90** seconds.
|
-- which is in this example between **30** and **90** seconds.
|
||||||
-- The schedule will stop after **300** seconds.
|
-- The schedule will stop after **300** seconds.
|
||||||
--
|
--
|
||||||
-- @field #SCHEDULER
|
-- @field #SCHEDULER
|
||||||
SCHEDULER = {
|
SCHEDULER = {
|
||||||
ClassName = "SCHEDULER",
|
ClassName = "SCHEDULER",
|
||||||
Schedules = {},
|
Schedules = {},
|
||||||
MasterObject = nil,
|
MasterObject = nil,
|
||||||
ShowTrace = nil,
|
ShowTrace = nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
--- SCHEDULER constructor.
|
--- SCHEDULER constructor.
|
||||||
@@ -211,15 +210,15 @@ SCHEDULER = {
|
|||||||
-- @return #SCHEDULER self.
|
-- @return #SCHEDULER self.
|
||||||
-- @return #table The ScheduleID of the planned schedule.
|
-- @return #table The ScheduleID of the planned schedule.
|
||||||
function SCHEDULER:New( MasterObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop )
|
function SCHEDULER:New( MasterObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop )
|
||||||
|
|
||||||
local self = BASE:Inherit( self, BASE:New() ) -- #SCHEDULER
|
local self = BASE:Inherit( self, BASE:New() ) -- #SCHEDULER
|
||||||
self:F2( { Start, Repeat, RandomizeFactor, Stop } )
|
self:F2( { Start, Repeat, RandomizeFactor, Stop } )
|
||||||
|
|
||||||
local ScheduleID = nil
|
local ScheduleID = nil
|
||||||
|
|
||||||
self.MasterObject = MasterObject
|
self.MasterObject = MasterObject
|
||||||
self.ShowTrace = false
|
self.ShowTrace = false
|
||||||
|
|
||||||
if SchedulerFunction then
|
if SchedulerFunction then
|
||||||
ScheduleID = self:Schedule( MasterObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop, 3 )
|
ScheduleID = self:Schedule( MasterObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop, 3 )
|
||||||
end
|
end
|
||||||
@@ -235,48 +234,47 @@ end
|
|||||||
-- @param #number Start Specifies the amount of seconds that will be waited before the scheduling is started, and the event function is called.
|
-- @param #number Start Specifies the amount of seconds that will be waited before the scheduling is started, and the event function is called.
|
||||||
-- @param #number Repeat Specifies the time interval in seconds when the scheduler will call the event function.
|
-- @param #number Repeat Specifies the time interval in seconds when the scheduler will call the event function.
|
||||||
-- @param #number RandomizeFactor Specifies a randomization factor between 0 and 1 to randomize the Repeat.
|
-- @param #number RandomizeFactor Specifies a randomization factor between 0 and 1 to randomize the Repeat.
|
||||||
-- @param #number Stop Time interval in seconds after which the scheduler will be stoppe.
|
-- @param #number Stop Time interval in seconds after which the scheduler will be stopped.
|
||||||
-- @param #number TraceLevel Trace level [0,3]. Default 3.
|
-- @param #number TraceLevel Trace level [0,3]. Default 3.
|
||||||
-- @param Core.Fsm#FSM Fsm Finite state model.
|
-- @param Core.Fsm#FSM Fsm Finite state model.
|
||||||
-- @return #table The ScheduleID of the planned schedule.
|
-- @return #string The Schedule ID of the planned schedule.
|
||||||
function SCHEDULER:Schedule( MasterObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop, TraceLevel, Fsm )
|
function SCHEDULER:Schedule( MasterObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop, TraceLevel, Fsm )
|
||||||
self:F2( { Start, Repeat, RandomizeFactor, Stop } )
|
self:F2( { Start, Repeat, RandomizeFactor, Stop } )
|
||||||
self:T3( { SchedulerArguments } )
|
self:T3( { SchedulerArguments } )
|
||||||
|
|
||||||
-- Debug info.
|
-- Debug info.
|
||||||
local ObjectName = "-"
|
local ObjectName = "-"
|
||||||
if MasterObject and MasterObject.ClassName and MasterObject.ClassID then
|
if MasterObject and MasterObject.ClassName and MasterObject.ClassID then
|
||||||
ObjectName = MasterObject.ClassName .. MasterObject.ClassID
|
ObjectName = MasterObject.ClassName .. MasterObject.ClassID
|
||||||
end
|
end
|
||||||
self:F3( { "Schedule :", ObjectName, tostring( MasterObject ), Start, Repeat, RandomizeFactor, Stop } )
|
self:F3( { "Schedule :", ObjectName, tostring( MasterObject ), Start, Repeat, RandomizeFactor, Stop } )
|
||||||
|
|
||||||
-- Set master object.
|
-- Set master object.
|
||||||
self.MasterObject = MasterObject
|
self.MasterObject = MasterObject
|
||||||
|
|
||||||
-- Add schedule.
|
-- Add schedule.
|
||||||
local ScheduleID = _SCHEDULEDISPATCHER:AddSchedule(
|
local ScheduleID = _SCHEDULEDISPATCHER:AddSchedule( self,
|
||||||
self,
|
SchedulerFunction,
|
||||||
SchedulerFunction,
|
SchedulerArguments,
|
||||||
SchedulerArguments,
|
Start,
|
||||||
Start,
|
Repeat,
|
||||||
Repeat,
|
RandomizeFactor,
|
||||||
RandomizeFactor,
|
Stop,
|
||||||
Stop,
|
TraceLevel or 3,
|
||||||
TraceLevel or 3,
|
Fsm
|
||||||
Fsm
|
)
|
||||||
)
|
|
||||||
|
self.Schedules[#self.Schedules + 1] = ScheduleID
|
||||||
self.Schedules[#self.Schedules+1] = ScheduleID
|
|
||||||
|
|
||||||
return ScheduleID
|
return ScheduleID
|
||||||
end
|
end
|
||||||
|
|
||||||
--- (Re-)Starts the schedules or a specific schedule if a valid ScheduleID is provided.
|
--- (Re-)Starts the schedules or a specific schedule if a valid ScheduleID is provided.
|
||||||
-- @param #SCHEDULER self
|
-- @param #SCHEDULER self
|
||||||
-- @param #string ScheduleID (Optional) The ScheduleID of the planned (repeating) schedule.
|
-- @param #string ScheduleID (Optional) The Schedule ID of the planned (repeating) schedule.
|
||||||
function SCHEDULER:Start( ScheduleID )
|
function SCHEDULER:Start( ScheduleID )
|
||||||
self:F3( { ScheduleID } )
|
self:F3( { ScheduleID } )
|
||||||
self:T(string.format("Starting scheduler ID=%s", tostring(ScheduleID)))
|
self:T( string.format( "Starting scheduler ID=%s", tostring( ScheduleID ) ) )
|
||||||
_SCHEDULEDISPATCHER:Start( self, ScheduleID )
|
_SCHEDULEDISPATCHER:Start( self, ScheduleID )
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -285,7 +283,7 @@ end
|
|||||||
-- @param #string ScheduleID (Optional) The ScheduleID of the planned (repeating) schedule.
|
-- @param #string ScheduleID (Optional) The ScheduleID of the planned (repeating) schedule.
|
||||||
function SCHEDULER:Stop( ScheduleID )
|
function SCHEDULER:Stop( ScheduleID )
|
||||||
self:F3( { ScheduleID } )
|
self:F3( { ScheduleID } )
|
||||||
self:T(string.format("Stopping scheduler ID=%s", tostring(ScheduleID)))
|
self:T( string.format( "Stopping scheduler ID=%s", tostring( ScheduleID ) ) )
|
||||||
_SCHEDULEDISPATCHER:Stop( self, ScheduleID )
|
_SCHEDULEDISPATCHER:Stop( self, ScheduleID )
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -294,15 +292,15 @@ end
|
|||||||
-- @param #string ScheduleID (optional) The ScheduleID of the planned (repeating) schedule.
|
-- @param #string ScheduleID (optional) The ScheduleID of the planned (repeating) schedule.
|
||||||
function SCHEDULER:Remove( ScheduleID )
|
function SCHEDULER:Remove( ScheduleID )
|
||||||
self:F3( { ScheduleID } )
|
self:F3( { ScheduleID } )
|
||||||
self:T(string.format("Removing scheduler ID=%s", tostring(ScheduleID)))
|
self:T( string.format( "Removing scheduler ID=%s", tostring( ScheduleID ) ) )
|
||||||
_SCHEDULEDISPATCHER:RemoveSchedule( self, ScheduleID )
|
_SCHEDULEDISPATCHER:RemoveSchedule( self, ScheduleID )
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Clears all pending schedules.
|
--- Clears all pending schedules.
|
||||||
-- @param #SCHEDULER self
|
-- @param #SCHEDULER self
|
||||||
function SCHEDULER:Clear()
|
function SCHEDULER:Clear()
|
||||||
self:F3( )
|
self:F3()
|
||||||
self:T(string.format("Clearing scheduler"))
|
self:T( string.format( "Clearing scheduler" ) )
|
||||||
_SCHEDULEDISPATCHER:Clear( self )
|
_SCHEDULEDISPATCHER:Clear( self )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -29,15 +29,14 @@
|
|||||||
-- @module Core.Settings
|
-- @module Core.Settings
|
||||||
-- @image Core_Settings.JPG
|
-- @image Core_Settings.JPG
|
||||||
|
|
||||||
|
|
||||||
--- @type SETTINGS
|
--- @type SETTINGS
|
||||||
-- @extends Core.Base#BASE
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
--- Takes care of various settings that influence the behaviour of certain functionalities and classes within the MOOSE framework.
|
--- Takes care of various settings that influence the behavior of certain functionalities and classes within the MOOSE framework.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- The SETTINGS class takes care of various settings that influence the behaviour of certain functionalities and classes within the MOOSE framework.
|
-- The SETTINGS class takes care of various settings that influence the behavior of certain functionalities and classes within the MOOSE framework.
|
||||||
-- SETTINGS can work on 2 levels:
|
-- SETTINGS can work on 2 levels:
|
||||||
--
|
--
|
||||||
-- - **Default settings**: A running mission has **Default settings**.
|
-- - **Default settings**: A running mission has **Default settings**.
|
||||||
@@ -59,7 +58,7 @@
|
|||||||
--
|
--
|
||||||
-- A menu is created automatically per Command Center that allows to modify the **Default** settings.
|
-- A menu is created automatically per Command Center that allows to modify the **Default** settings.
|
||||||
-- So, when joining a CC unit, a menu will be available that allows to change the settings parameters **FOR ALL THE PLAYERS**!
|
-- So, when joining a CC unit, a menu will be available that allows to change the settings parameters **FOR ALL THE PLAYERS**!
|
||||||
-- Note that the **Default settings** will only be used when a player has not choosen its own settings.
|
-- Note that the **Default settings** will only be used when a player has not chosen its own settings.
|
||||||
--
|
--
|
||||||
-- ## 2.2) Player settings menu
|
-- ## 2.2) Player settings menu
|
||||||
--
|
--
|
||||||
@@ -69,7 +68,7 @@
|
|||||||
--
|
--
|
||||||
-- ## 2.3) Show or Hide the Player Setting menus
|
-- ## 2.3) Show or Hide the Player Setting menus
|
||||||
--
|
--
|
||||||
-- Of course, it may be requried not to show any setting menus. In this case, a method is available on the **\_SETTINGS object**.
|
-- Of course, it may be required not to show any setting menus. In this case, a method is available on the **\_SETTINGS object**.
|
||||||
-- Use @{#SETTINGS.SetPlayerMenuOff}() to hide the player menus, and use @{#SETTINGS.SetPlayerMenuOn}() show the player menus.
|
-- Use @{#SETTINGS.SetPlayerMenuOff}() to hide the player menus, and use @{#SETTINGS.SetPlayerMenuOn}() show the player menus.
|
||||||
-- Note that when this method is used, any player already in a slot will not have its menus visibility changed.
|
-- Note that when this method is used, any player already in a slot will not have its menus visibility changed.
|
||||||
-- The option will only have effect when a player enters a new slot or changes a slot.
|
-- The option will only have effect when a player enters a new slot or changes a slot.
|
||||||
@@ -94,8 +93,8 @@
|
|||||||
--
|
--
|
||||||
-- - A2G BR: [Bearing Range](https://en.wikipedia.org/wiki/Bearing_(navigation)).
|
-- - A2G BR: [Bearing Range](https://en.wikipedia.org/wiki/Bearing_(navigation)).
|
||||||
-- - A2G MGRS: The [Military Grid Reference System](https://en.wikipedia.org/wiki/Military_Grid_Reference_System). The accuracy can also be adapted.
|
-- - A2G MGRS: The [Military Grid Reference System](https://en.wikipedia.org/wiki/Military_Grid_Reference_System). The accuracy can also be adapted.
|
||||||
-- - A2G LL DMS: Lattitude Longitude [Degrees Minutes Seconds](https://en.wikipedia.org/wiki/Geographic_coordinate_conversion). The accuracy can also be adapted.
|
-- - A2G LL DMS: Latitude Longitude [Degrees Minutes Seconds](https://en.wikipedia.org/wiki/Geographic_coordinate_conversion). The accuracy can also be adapted.
|
||||||
-- - A2G LL DDM: Lattitude Longitude [Decimal Degrees Minutes](https://en.wikipedia.org/wiki/Decimal_degrees). The accuracy can also be adapted.
|
-- - A2G LL DDM: Latitude Longitude [Decimal Degrees Minutes](https://en.wikipedia.org/wiki/Decimal_degrees). The accuracy can also be adapted.
|
||||||
--
|
--
|
||||||
-- ### 3.1.2) A2G coordinates setting **menu**
|
-- ### 3.1.2) A2G coordinates setting **menu**
|
||||||
--
|
--
|
||||||
@@ -183,7 +182,7 @@
|
|||||||
-- The settings can be changed by using the **Default settings menu** on the Command Center or the **Player settings menu** on the Player Slot.
|
-- The settings can be changed by using the **Default settings menu** on the Command Center or the **Player settings menu** on the Player Slot.
|
||||||
--
|
--
|
||||||
-- Each Message Type has specific timings that will be applied when the message is displayed.
|
-- Each Message Type has specific timings that will be applied when the message is displayed.
|
||||||
-- The Settings Menu will provide for each Message Type a selection of proposed durations from which can be choosen.
|
-- The Settings Menu will provide for each Message Type a selection of proposed durations from which can be chosen.
|
||||||
-- So the player can choose its own amount of seconds how long a message should be displayed of a certain type.
|
-- So the player can choose its own amount of seconds how long a message should be displayed of a certain type.
|
||||||
-- Note that **Update** messages can be chosen not to be displayed at all!
|
-- Note that **Update** messages can be chosen not to be displayed at all!
|
||||||
--
|
--
|
||||||
@@ -196,7 +195,7 @@
|
|||||||
--
|
--
|
||||||
-- ## 3.5) **Era** of the battle
|
-- ## 3.5) **Era** of the battle
|
||||||
--
|
--
|
||||||
-- The threat level metric is scaled according the era of the battle. A target that is AAA, will pose a much greather threat in WWII than on modern warfare.
|
-- The threat level metric is scaled according the era of the battle. A target that is AAA, will pose a much greater threat in WWII than on modern warfare.
|
||||||
-- Therefore, there are 4 era that are defined within the settings:
|
-- Therefore, there are 4 era that are defined within the settings:
|
||||||
--
|
--
|
||||||
-- - **WWII** era: Use for warfare with equipment during the world war II time.
|
-- - **WWII** era: Use for warfare with equipment during the world war II time.
|
||||||
@@ -213,8 +212,8 @@
|
|||||||
SETTINGS = {
|
SETTINGS = {
|
||||||
ClassName = "SETTINGS",
|
ClassName = "SETTINGS",
|
||||||
ShowPlayerMenu = true,
|
ShowPlayerMenu = true,
|
||||||
MenuShort = false,
|
MenuShort = false,
|
||||||
MenuStatic = false,
|
MenuStatic = false,
|
||||||
}
|
}
|
||||||
|
|
||||||
SETTINGS.__Enum = {}
|
SETTINGS.__Enum = {}
|
||||||
@@ -231,7 +230,6 @@ SETTINGS.__Enum.Era = {
|
|||||||
Modern = 4,
|
Modern = 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
do -- SETTINGS
|
do -- SETTINGS
|
||||||
|
|
||||||
--- SETTINGS constructor.
|
--- SETTINGS constructor.
|
||||||
@@ -253,6 +251,7 @@ do -- SETTINGS
|
|||||||
self:SetMessageTime( MESSAGE.Type.Overview, 60 )
|
self:SetMessageTime( MESSAGE.Type.Overview, 60 )
|
||||||
self:SetMessageTime( MESSAGE.Type.Update, 15 )
|
self:SetMessageTime( MESSAGE.Type.Update, 15 )
|
||||||
self:SetEraModern()
|
self:SetEraModern()
|
||||||
|
self:SetLocale("en")
|
||||||
return self
|
return self
|
||||||
else
|
else
|
||||||
local Settings = _DATABASE:GetPlayerSettings( PlayerName )
|
local Settings = _DATABASE:GetPlayerSettings( PlayerName )
|
||||||
@@ -268,14 +267,14 @@ do -- SETTINGS
|
|||||||
-- Short text are better suited for, e.g., VR.
|
-- Short text are better suited for, e.g., VR.
|
||||||
-- @param #SETTINGS self
|
-- @param #SETTINGS self
|
||||||
-- @param #boolean onoff If *true* use short menu texts. If *false* long ones (default).
|
-- @param #boolean onoff If *true* use short menu texts. If *false* long ones (default).
|
||||||
function SETTINGS:SetMenutextShort(onoff)
|
function SETTINGS:SetMenutextShort( onoff )
|
||||||
_SETTINGS.MenuShort = onoff
|
_SETTINGS.MenuShort = onoff
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set menu to be static.
|
--- Set menu to be static.
|
||||||
-- @param #SETTINGS self
|
-- @param #SETTINGS self
|
||||||
-- @param #boolean onoff If *true* menu is static. If *false* menu will be updated after changes (default).
|
-- @param #boolean onoff If *true* menu is static. If *false* menu will be updated after changes (default).
|
||||||
function SETTINGS:SetMenuStatic(onoff)
|
function SETTINGS:SetMenuStatic( onoff )
|
||||||
_SETTINGS.MenuStatic = onoff
|
_SETTINGS.MenuStatic = onoff
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -284,12 +283,26 @@ do -- SETTINGS
|
|||||||
function SETTINGS:SetMetric()
|
function SETTINGS:SetMetric()
|
||||||
self.Metric = true
|
self.Metric = true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Sets the SETTINGS default text locale.
|
||||||
|
-- @param #SETTINGS self
|
||||||
|
-- @param #string Locale
|
||||||
|
function SETTINGS:SetLocale(Locale)
|
||||||
|
self.Locale = Locale or "en"
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Gets the SETTINGS text locale.
|
||||||
|
-- @param #SETTINGS self
|
||||||
|
-- @return #string
|
||||||
|
function SETTINGS:GetLocale()
|
||||||
|
return self.Locale or _SETTINGS:GetLocale()
|
||||||
|
end
|
||||||
|
|
||||||
--- Gets if the SETTINGS is metric.
|
--- Gets if the SETTINGS is metric.
|
||||||
-- @param #SETTINGS self
|
-- @param #SETTINGS self
|
||||||
-- @return #boolean true if metric.
|
-- @return #boolean true if metric.
|
||||||
function SETTINGS:IsMetric()
|
function SETTINGS:IsMetric()
|
||||||
return ( self.Metric ~= nil and self.Metric == true ) or ( self.Metric == nil and _SETTINGS:IsMetric() )
|
return (self.Metric ~= nil and self.Metric == true) or (self.Metric == nil and _SETTINGS:IsMetric())
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Sets the SETTINGS imperial.
|
--- Sets the SETTINGS imperial.
|
||||||
@@ -302,7 +315,7 @@ do -- SETTINGS
|
|||||||
-- @param #SETTINGS self
|
-- @param #SETTINGS self
|
||||||
-- @return #boolean true if imperial.
|
-- @return #boolean true if imperial.
|
||||||
function SETTINGS:IsImperial()
|
function SETTINGS:IsImperial()
|
||||||
return ( self.Metric ~= nil and self.Metric == false ) or ( self.Metric == nil and _SETTINGS:IsMetric() )
|
return (self.Metric ~= nil and self.Metric == false) or (self.Metric == nil and _SETTINGS:IsImperial())
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Sets the SETTINGS LL accuracy.
|
--- Sets the SETTINGS LL accuracy.
|
||||||
@@ -344,13 +357,12 @@ do -- SETTINGS
|
|||||||
self.MessageTypeTimings[MessageType] = MessageTime
|
self.MessageTypeTimings[MessageType] = MessageTime
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Gets the SETTINGS Message Display Timing of a MessageType
|
--- Gets the SETTINGS Message Display Timing of a MessageType
|
||||||
-- @param #SETTINGS self
|
-- @param #SETTINGS self
|
||||||
-- @param Core.Message#MESSAGE MessageType The type of the message.
|
-- @param Core.Message#MESSAGE MessageType The type of the message.
|
||||||
-- @return #number
|
-- @return #number
|
||||||
function SETTINGS:GetMessageTime( MessageType )
|
function SETTINGS:GetMessageTime( MessageType )
|
||||||
return ( self.MessageTypeTimings and self.MessageTypeTimings[MessageType] ) or _SETTINGS:GetMessageTime( MessageType )
|
return (self.MessageTypeTimings and self.MessageTypeTimings[MessageType]) or _SETTINGS:GetMessageTime( MessageType )
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Sets A2G LL DMS
|
--- Sets A2G LL DMS
|
||||||
@@ -371,14 +383,14 @@ do -- SETTINGS
|
|||||||
-- @param #SETTINGS self
|
-- @param #SETTINGS self
|
||||||
-- @return #boolean true if LL DMS
|
-- @return #boolean true if LL DMS
|
||||||
function SETTINGS:IsA2G_LL_DMS()
|
function SETTINGS:IsA2G_LL_DMS()
|
||||||
return ( self.A2GSystem and self.A2GSystem == "LL DMS" ) or ( not self.A2GSystem and _SETTINGS:IsA2G_LL_DMS() )
|
return (self.A2GSystem and self.A2GSystem == "LL DMS") or (not self.A2GSystem and _SETTINGS:IsA2G_LL_DMS())
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Is LL DDM
|
--- Is LL DDM
|
||||||
-- @param #SETTINGS self
|
-- @param #SETTINGS self
|
||||||
-- @return #boolean true if LL DDM
|
-- @return #boolean true if LL DDM
|
||||||
function SETTINGS:IsA2G_LL_DDM()
|
function SETTINGS:IsA2G_LL_DDM()
|
||||||
return ( self.A2GSystem and self.A2GSystem == "LL DDM" ) or ( not self.A2GSystem and _SETTINGS:IsA2G_LL_DDM() )
|
return (self.A2GSystem and self.A2GSystem == "LL DDM") or (not self.A2GSystem and _SETTINGS:IsA2G_LL_DDM())
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Sets A2G MGRS
|
--- Sets A2G MGRS
|
||||||
@@ -392,7 +404,7 @@ do -- SETTINGS
|
|||||||
-- @param #SETTINGS self
|
-- @param #SETTINGS self
|
||||||
-- @return #boolean true if MGRS
|
-- @return #boolean true if MGRS
|
||||||
function SETTINGS:IsA2G_MGRS()
|
function SETTINGS:IsA2G_MGRS()
|
||||||
return ( self.A2GSystem and self.A2GSystem == "MGRS" ) or ( not self.A2GSystem and _SETTINGS:IsA2G_MGRS() )
|
return (self.A2GSystem and self.A2GSystem == "MGRS") or (not self.A2GSystem and _SETTINGS:IsA2G_MGRS())
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Sets A2G BRA
|
--- Sets A2G BRA
|
||||||
@@ -406,7 +418,7 @@ do -- SETTINGS
|
|||||||
-- @param #SETTINGS self
|
-- @param #SETTINGS self
|
||||||
-- @return #boolean true if BRA
|
-- @return #boolean true if BRA
|
||||||
function SETTINGS:IsA2G_BR()
|
function SETTINGS:IsA2G_BR()
|
||||||
return ( self.A2GSystem and self.A2GSystem == "BR" ) or ( not self.A2GSystem and _SETTINGS:IsA2G_BR() )
|
return (self.A2GSystem and self.A2GSystem == "BR") or (not self.A2GSystem and _SETTINGS:IsA2G_BR())
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Sets A2A BRA
|
--- Sets A2A BRA
|
||||||
@@ -420,7 +432,7 @@ do -- SETTINGS
|
|||||||
-- @param #SETTINGS self
|
-- @param #SETTINGS self
|
||||||
-- @return #boolean true if BRA
|
-- @return #boolean true if BRA
|
||||||
function SETTINGS:IsA2A_BRAA()
|
function SETTINGS:IsA2A_BRAA()
|
||||||
return ( self.A2ASystem and self.A2ASystem == "BRAA" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_BRAA() )
|
return (self.A2ASystem and self.A2ASystem == "BRAA") or (not self.A2ASystem and _SETTINGS:IsA2A_BRAA())
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Sets A2A BULLS
|
--- Sets A2A BULLS
|
||||||
@@ -434,7 +446,7 @@ do -- SETTINGS
|
|||||||
-- @param #SETTINGS self
|
-- @param #SETTINGS self
|
||||||
-- @return #boolean true if BULLS
|
-- @return #boolean true if BULLS
|
||||||
function SETTINGS:IsA2A_BULLS()
|
function SETTINGS:IsA2A_BULLS()
|
||||||
return ( self.A2ASystem and self.A2ASystem == "BULLS" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_BULLS() )
|
return (self.A2ASystem and self.A2ASystem == "BULLS") or (not self.A2ASystem and _SETTINGS:IsA2A_BULLS())
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Sets A2A LL DMS
|
--- Sets A2A LL DMS
|
||||||
@@ -455,14 +467,14 @@ do -- SETTINGS
|
|||||||
-- @param #SETTINGS self
|
-- @param #SETTINGS self
|
||||||
-- @return #boolean true if LL DMS
|
-- @return #boolean true if LL DMS
|
||||||
function SETTINGS:IsA2A_LL_DMS()
|
function SETTINGS:IsA2A_LL_DMS()
|
||||||
return ( self.A2ASystem and self.A2ASystem == "LL DMS" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_LL_DMS() )
|
return (self.A2ASystem and self.A2ASystem == "LL DMS") or (not self.A2ASystem and _SETTINGS:IsA2A_LL_DMS())
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Is LL DDM
|
--- Is LL DDM
|
||||||
-- @param #SETTINGS self
|
-- @param #SETTINGS self
|
||||||
-- @return #boolean true if LL DDM
|
-- @return #boolean true if LL DDM
|
||||||
function SETTINGS:IsA2A_LL_DDM()
|
function SETTINGS:IsA2A_LL_DDM()
|
||||||
return ( self.A2ASystem and self.A2ASystem == "LL DDM" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_LL_DDM() )
|
return (self.A2ASystem and self.A2ASystem == "LL DDM") or (not self.A2ASystem and _SETTINGS:IsA2A_LL_DDM())
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Sets A2A MGRS
|
--- Sets A2A MGRS
|
||||||
@@ -476,7 +488,7 @@ do -- SETTINGS
|
|||||||
-- @param #SETTINGS self
|
-- @param #SETTINGS self
|
||||||
-- @return #boolean true if MGRS
|
-- @return #boolean true if MGRS
|
||||||
function SETTINGS:IsA2A_MGRS()
|
function SETTINGS:IsA2A_MGRS()
|
||||||
return ( self.A2ASystem and self.A2ASystem == "MGRS" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_MGRS() )
|
return (self.A2ASystem and self.A2ASystem == "MGRS") or (not self.A2ASystem and _SETTINGS:IsA2A_MGRS())
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param #SETTINGS self
|
--- @param #SETTINGS self
|
||||||
@@ -495,37 +507,37 @@ do -- SETTINGS
|
|||||||
-- A2G Coordinate System
|
-- A2G Coordinate System
|
||||||
-------
|
-------
|
||||||
|
|
||||||
local text="A2G Coordinate System"
|
local text = "A2G Coordinate System"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="A2G Coordinates"
|
text = "A2G Coordinates"
|
||||||
end
|
end
|
||||||
local A2GCoordinateMenu = MENU_GROUP:New( MenuGroup, text, SettingsMenu ):SetTime( MenuTime )
|
local A2GCoordinateMenu = MENU_GROUP:New( MenuGroup, text, SettingsMenu ):SetTime( MenuTime )
|
||||||
|
|
||||||
-- Set LL DMS
|
-- Set LL DMS
|
||||||
if not self:IsA2G_LL_DMS() then
|
if not self:IsA2G_LL_DMS() then
|
||||||
local text="Lat/Lon Degree Min Sec (LL DMS)"
|
local text = "Lat/Lon Degree Min Sec (LL DMS)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="LL DMS"
|
text = "LL DMS"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( MenuGroup, text, A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "LL DMS" ):SetTime( MenuTime )
|
MENU_GROUP_COMMAND:New( MenuGroup, text, A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "LL DMS" ):SetTime( MenuTime )
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Set LL DDM
|
-- Set LL DDM
|
||||||
if not self:IsA2G_LL_DDM() then
|
if not self:IsA2G_LL_DDM() then
|
||||||
local text="Lat/Lon Degree Dec Min (LL DDM)"
|
local text = "Lat/Lon Degree Dec Min (LL DDM)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="LL DDM"
|
text = "LL DDM"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( MenuGroup, "Lat/Lon Degree Dec Min (LL DDM)", A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "LL DDM" ):SetTime( MenuTime )
|
MENU_GROUP_COMMAND:New( MenuGroup, "Lat/Lon Degree Dec Min (LL DDM)", A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "LL DDM" ):SetTime( MenuTime )
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Set LL DMS accuracy.
|
-- Set LL DMS accuracy.
|
||||||
if self:IsA2G_LL_DDM() then
|
if self:IsA2G_LL_DDM() then
|
||||||
local text1="LL DDM Accuracy 1"
|
local text1 = "LL DDM Accuracy 1"
|
||||||
local text2="LL DDM Accuracy 2"
|
local text2 = "LL DDM Accuracy 2"
|
||||||
local text3="LL DDM Accuracy 3"
|
local text3 = "LL DDM Accuracy 3"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text1="LL DDM"
|
text1 = "LL DDM"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( MenuGroup, "LL DDM Accuracy 1", A2GCoordinateMenu, self.MenuLL_DDM_Accuracy, self, MenuGroup, RootMenu, 1 ):SetTime( MenuTime )
|
MENU_GROUP_COMMAND:New( MenuGroup, "LL DDM Accuracy 1", A2GCoordinateMenu, self.MenuLL_DDM_Accuracy, self, MenuGroup, RootMenu, 1 ):SetTime( MenuTime )
|
||||||
MENU_GROUP_COMMAND:New( MenuGroup, "LL DDM Accuracy 2", A2GCoordinateMenu, self.MenuLL_DDM_Accuracy, self, MenuGroup, RootMenu, 2 ):SetTime( MenuTime )
|
MENU_GROUP_COMMAND:New( MenuGroup, "LL DDM Accuracy 2", A2GCoordinateMenu, self.MenuLL_DDM_Accuracy, self, MenuGroup, RootMenu, 2 ):SetTime( MenuTime )
|
||||||
@@ -534,18 +546,18 @@ do -- SETTINGS
|
|||||||
|
|
||||||
-- Set BR.
|
-- Set BR.
|
||||||
if not self:IsA2G_BR() then
|
if not self:IsA2G_BR() then
|
||||||
local text="Bearing, Range (BR)"
|
local text = "Bearing, Range (BR)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="BR"
|
text = "BR"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( MenuGroup, text , A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "BR" ):SetTime( MenuTime )
|
MENU_GROUP_COMMAND:New( MenuGroup, text, A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "BR" ):SetTime( MenuTime )
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Set MGRS.
|
-- Set MGRS.
|
||||||
if not self:IsA2G_MGRS() then
|
if not self:IsA2G_MGRS() then
|
||||||
local text="Military Grid (MGRS)"
|
local text = "Military Grid (MGRS)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="MGRS"
|
text = "MGRS"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( MenuGroup, text, A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "MGRS" ):SetTime( MenuTime )
|
MENU_GROUP_COMMAND:New( MenuGroup, text, A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "MGRS" ):SetTime( MenuTime )
|
||||||
end
|
end
|
||||||
@@ -563,24 +575,24 @@ do -- SETTINGS
|
|||||||
-- A2A Coordinate System
|
-- A2A Coordinate System
|
||||||
-------
|
-------
|
||||||
|
|
||||||
local text="A2A Coordinate System"
|
local text = "A2A Coordinate System"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="A2A Coordinates"
|
text = "A2A Coordinates"
|
||||||
end
|
end
|
||||||
local A2ACoordinateMenu = MENU_GROUP:New( MenuGroup, text, SettingsMenu ):SetTime( MenuTime )
|
local A2ACoordinateMenu = MENU_GROUP:New( MenuGroup, text, SettingsMenu ):SetTime( MenuTime )
|
||||||
|
|
||||||
if not self:IsA2A_LL_DMS() then
|
if not self:IsA2A_LL_DMS() then
|
||||||
local text="Lat/Lon Degree Min Sec (LL DMS)"
|
local text = "Lat/Lon Degree Min Sec (LL DMS)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="LL DMS"
|
text = "LL DMS"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "LL DMS" ):SetTime( MenuTime )
|
MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "LL DMS" ):SetTime( MenuTime )
|
||||||
end
|
end
|
||||||
|
|
||||||
if not self:IsA2A_LL_DDM() then
|
if not self:IsA2A_LL_DDM() then
|
||||||
local text="Lat/Lon Degree Dec Min (LL DDM)"
|
local text = "Lat/Lon Degree Dec Min (LL DDM)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="LL DDM"
|
text = "LL DDM"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "LL DDM" ):SetTime( MenuTime )
|
MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "LL DDM" ):SetTime( MenuTime )
|
||||||
end
|
end
|
||||||
@@ -593,25 +605,25 @@ do -- SETTINGS
|
|||||||
end
|
end
|
||||||
|
|
||||||
if not self:IsA2A_BULLS() then
|
if not self:IsA2A_BULLS() then
|
||||||
local text="Bullseye (BULLS)"
|
local text = "Bullseye (BULLS)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="Bulls"
|
text = "Bulls"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "BULLS" ):SetTime( MenuTime )
|
MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "BULLS" ):SetTime( MenuTime )
|
||||||
end
|
end
|
||||||
|
|
||||||
if not self:IsA2A_BRAA() then
|
if not self:IsA2A_BRAA() then
|
||||||
local text="Bearing Range Altitude Aspect (BRAA)"
|
local text = "Bearing Range Altitude Aspect (BRAA)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="BRAA"
|
text = "BRAA"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "BRAA" ):SetTime( MenuTime )
|
MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "BRAA" ):SetTime( MenuTime )
|
||||||
end
|
end
|
||||||
|
|
||||||
if not self:IsA2A_MGRS() then
|
if not self:IsA2A_MGRS() then
|
||||||
local text="Military Grid (MGRS)"
|
local text = "Military Grid (MGRS)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="MGRS"
|
text = "MGRS"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "MGRS" ):SetTime( MenuTime )
|
MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "MGRS" ):SetTime( MenuTime )
|
||||||
end
|
end
|
||||||
@@ -624,31 +636,31 @@ do -- SETTINGS
|
|||||||
MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 5", A2ACoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 5 ):SetTime( MenuTime )
|
MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 5", A2ACoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 5 ):SetTime( MenuTime )
|
||||||
end
|
end
|
||||||
|
|
||||||
local text="Measures and Weights System"
|
local text = "Measures and Weights System"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="Unit System"
|
text = "Unit System"
|
||||||
end
|
end
|
||||||
local MetricsMenu = MENU_GROUP:New( MenuGroup, text, SettingsMenu ):SetTime( MenuTime )
|
local MetricsMenu = MENU_GROUP:New( MenuGroup, text, SettingsMenu ):SetTime( MenuTime )
|
||||||
|
|
||||||
if self:IsMetric() then
|
if self:IsMetric() then
|
||||||
local text="Imperial (Miles,Feet)"
|
local text = "Imperial (Miles,Feet)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="Imperial"
|
text = "Imperial"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( MenuGroup, text, MetricsMenu, self.MenuMWSystem, self, MenuGroup, RootMenu, false ):SetTime( MenuTime )
|
MENU_GROUP_COMMAND:New( MenuGroup, text, MetricsMenu, self.MenuMWSystem, self, MenuGroup, RootMenu, false ):SetTime( MenuTime )
|
||||||
end
|
end
|
||||||
|
|
||||||
if self:IsImperial() then
|
if self:IsImperial() then
|
||||||
local text="Metric (Kilometers,Meters)"
|
local text = "Metric (Kilometers,Meters)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="Metric"
|
text = "Metric"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( MenuGroup, text, MetricsMenu, self.MenuMWSystem, self, MenuGroup, RootMenu, true ):SetTime( MenuTime )
|
MENU_GROUP_COMMAND:New( MenuGroup, text, MetricsMenu, self.MenuMWSystem, self, MenuGroup, RootMenu, true ):SetTime( MenuTime )
|
||||||
end
|
end
|
||||||
|
|
||||||
local text="Messages and Reports"
|
local text = "Messages and Reports"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="Messages & Reports"
|
text = "Messages & Reports"
|
||||||
end
|
end
|
||||||
local MessagesMenu = MENU_GROUP:New( MenuGroup, text, SettingsMenu ):SetTime( MenuTime )
|
local MessagesMenu = MENU_GROUP:New( MenuGroup, text, SettingsMenu ):SetTime( MenuTime )
|
||||||
|
|
||||||
@@ -689,7 +701,6 @@ do -- SETTINGS
|
|||||||
MENU_GROUP_COMMAND:New( MenuGroup, "2 minutes", DetailedReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.DetailedReportsMenu, 120 ):SetTime( MenuTime )
|
MENU_GROUP_COMMAND:New( MenuGroup, "2 minutes", DetailedReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.DetailedReportsMenu, 120 ):SetTime( MenuTime )
|
||||||
MENU_GROUP_COMMAND:New( MenuGroup, "3 minutes", DetailedReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.DetailedReportsMenu, 180 ):SetTime( MenuTime )
|
MENU_GROUP_COMMAND:New( MenuGroup, "3 minutes", DetailedReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.DetailedReportsMenu, 180 ):SetTime( MenuTime )
|
||||||
|
|
||||||
|
|
||||||
SettingsMenu:Remove( MenuTime )
|
SettingsMenu:Remove( MenuTime )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
@@ -733,11 +744,11 @@ do -- SETTINGS
|
|||||||
|
|
||||||
self.PlayerMenu = PlayerMenu
|
self.PlayerMenu = PlayerMenu
|
||||||
|
|
||||||
self:I(string.format("Setting menu for player %s", tostring(PlayerName)))
|
self:I( string.format( "Setting menu for player %s", tostring( PlayerName ) ) )
|
||||||
|
|
||||||
local submenu = MENU_GROUP:New( PlayerGroup, "LL Accuracy", PlayerMenu )
|
local submenu = MENU_GROUP:New( PlayerGroup, "LL Accuracy", PlayerMenu )
|
||||||
MENU_GROUP_COMMAND:New( PlayerGroup, "LL 0 Decimals", submenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 0 )
|
MENU_GROUP_COMMAND:New( PlayerGroup, "LL 0 Decimals", submenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 0 )
|
||||||
MENU_GROUP_COMMAND:New( PlayerGroup, "LL 1 Decimal", submenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 1 )
|
MENU_GROUP_COMMAND:New( PlayerGroup, "LL 1 Decimal", submenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 1 )
|
||||||
MENU_GROUP_COMMAND:New( PlayerGroup, "LL 2 Decimals", submenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 2 )
|
MENU_GROUP_COMMAND:New( PlayerGroup, "LL 2 Decimals", submenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 2 )
|
||||||
MENU_GROUP_COMMAND:New( PlayerGroup, "LL 3 Decimals", submenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 3 )
|
MENU_GROUP_COMMAND:New( PlayerGroup, "LL 3 Decimals", submenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 3 )
|
||||||
MENU_GROUP_COMMAND:New( PlayerGroup, "LL 4 Decimals", submenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 4 )
|
MENU_GROUP_COMMAND:New( PlayerGroup, "LL 4 Decimals", submenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 4 )
|
||||||
@@ -754,40 +765,40 @@ do -- SETTINGS
|
|||||||
-- A2G Coordinate System
|
-- A2G Coordinate System
|
||||||
------
|
------
|
||||||
|
|
||||||
local text="A2G Coordinate System"
|
local text = "A2G Coordinate System"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="A2G Coordinates"
|
text = "A2G Coordinates"
|
||||||
end
|
end
|
||||||
local A2GCoordinateMenu = MENU_GROUP:New( PlayerGroup, text, PlayerMenu )
|
local A2GCoordinateMenu = MENU_GROUP:New( PlayerGroup, text, PlayerMenu )
|
||||||
|
|
||||||
if not self:IsA2G_LL_DMS() or _SETTINGS.MenuStatic then
|
if not self:IsA2G_LL_DMS() or _SETTINGS.MenuStatic then
|
||||||
local text="Lat/Lon Degree Min Sec (LL DMS)"
|
local text = "Lat/Lon Degree Min Sec (LL DMS)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="A2G LL DMS"
|
text = "A2G LL DMS"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DMS" )
|
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DMS" )
|
||||||
end
|
end
|
||||||
|
|
||||||
if not self:IsA2G_LL_DDM() or _SETTINGS.MenuStatic then
|
if not self:IsA2G_LL_DDM() or _SETTINGS.MenuStatic then
|
||||||
local text="Lat/Lon Degree Dec Min (LL DDM)"
|
local text = "Lat/Lon Degree Dec Min (LL DDM)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="A2G LL DDM"
|
text = "A2G LL DDM"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DDM" )
|
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DDM" )
|
||||||
end
|
end
|
||||||
|
|
||||||
if not self:IsA2G_BR() or _SETTINGS.MenuStatic then
|
if not self:IsA2G_BR() or _SETTINGS.MenuStatic then
|
||||||
local text="Bearing, Range (BR)"
|
local text = "Bearing, Range (BR)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="A2G BR"
|
text = "A2G BR"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "BR" )
|
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "BR" )
|
||||||
end
|
end
|
||||||
|
|
||||||
if not self:IsA2G_MGRS() or _SETTINGS.MenuStatic then
|
if not self:IsA2G_MGRS() or _SETTINGS.MenuStatic then
|
||||||
local text="Military Grid (MGRS)"
|
local text = "Military Grid (MGRS)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="A2G MGRS"
|
text = "A2G MGRS"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "MGRS" )
|
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "MGRS" )
|
||||||
end
|
end
|
||||||
@@ -796,49 +807,48 @@ do -- SETTINGS
|
|||||||
-- A2A Coordinates Menu
|
-- A2A Coordinates Menu
|
||||||
------
|
------
|
||||||
|
|
||||||
local text="A2A Coordinate System"
|
local text = "A2A Coordinate System"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="A2A Coordinates"
|
text = "A2A Coordinates"
|
||||||
end
|
end
|
||||||
local A2ACoordinateMenu = MENU_GROUP:New( PlayerGroup, text, PlayerMenu )
|
local A2ACoordinateMenu = MENU_GROUP:New( PlayerGroup, text, PlayerMenu )
|
||||||
|
|
||||||
|
|
||||||
if not self:IsA2A_LL_DMS() or _SETTINGS.MenuStatic then
|
if not self:IsA2A_LL_DMS() or _SETTINGS.MenuStatic then
|
||||||
local text="Lat/Lon Degree Min Sec (LL DMS)"
|
local text = "Lat/Lon Degree Min Sec (LL DMS)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="A2A LL DMS"
|
text = "A2A LL DMS"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DMS" )
|
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DMS" )
|
||||||
end
|
end
|
||||||
|
|
||||||
if not self:IsA2A_LL_DDM() or _SETTINGS.MenuStatic then
|
if not self:IsA2A_LL_DDM() or _SETTINGS.MenuStatic then
|
||||||
local text="Lat/Lon Degree Dec Min (LL DDM)"
|
local text = "Lat/Lon Degree Dec Min (LL DDM)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="A2A LL DDM"
|
text = "A2A LL DDM"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DDM" )
|
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DDM" )
|
||||||
end
|
end
|
||||||
|
|
||||||
if not self:IsA2A_BULLS() or _SETTINGS.MenuStatic then
|
if not self:IsA2A_BULLS() or _SETTINGS.MenuStatic then
|
||||||
local text="Bullseye (BULLS)"
|
local text = "Bullseye (BULLS)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="A2A BULLS"
|
text = "A2A BULLS"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "BULLS" )
|
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "BULLS" )
|
||||||
end
|
end
|
||||||
|
|
||||||
if not self:IsA2A_BRAA() or _SETTINGS.MenuStatic then
|
if not self:IsA2A_BRAA() or _SETTINGS.MenuStatic then
|
||||||
local text="Bearing Range Altitude Aspect (BRAA)"
|
local text = "Bearing Range Altitude Aspect (BRAA)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="A2A BRAA"
|
text = "A2A BRAA"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "BRAA" )
|
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "BRAA" )
|
||||||
end
|
end
|
||||||
|
|
||||||
if not self:IsA2A_MGRS() or _SETTINGS.MenuStatic then
|
if not self:IsA2A_MGRS() or _SETTINGS.MenuStatic then
|
||||||
local text="Military Grid (MGRS)"
|
local text = "Military Grid (MGRS)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="A2A MGRS"
|
text = "A2A MGRS"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "MGRS" )
|
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "MGRS" )
|
||||||
end
|
end
|
||||||
@@ -847,24 +857,24 @@ do -- SETTINGS
|
|||||||
-- Unit system
|
-- Unit system
|
||||||
---
|
---
|
||||||
|
|
||||||
local text="Measures and Weights System"
|
local text = "Measures and Weights System"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="Unit System"
|
text = "Unit System"
|
||||||
end
|
end
|
||||||
local MetricsMenu = MENU_GROUP:New( PlayerGroup, text, PlayerMenu )
|
local MetricsMenu = MENU_GROUP:New( PlayerGroup, text, PlayerMenu )
|
||||||
|
|
||||||
if self:IsMetric() or _SETTINGS.MenuStatic then
|
if self:IsMetric() or _SETTINGS.MenuStatic then
|
||||||
local text="Imperial (Miles,Feet)"
|
local text = "Imperial (Miles,Feet)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="Imperial"
|
text = "Imperial"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, MetricsMenu, self.MenuGroupMWSystem, self, PlayerUnit, PlayerGroup, PlayerName, false )
|
MENU_GROUP_COMMAND:New( PlayerGroup, text, MetricsMenu, self.MenuGroupMWSystem, self, PlayerUnit, PlayerGroup, PlayerName, false )
|
||||||
end
|
end
|
||||||
|
|
||||||
if self:IsImperial() or _SETTINGS.MenuStatic then
|
if self:IsImperial() or _SETTINGS.MenuStatic then
|
||||||
local text="Metric (Kilometers,Meters)"
|
local text = "Metric (Kilometers,Meters)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="Metric"
|
text = "Metric"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, MetricsMenu, self.MenuGroupMWSystem, self, PlayerUnit, PlayerGroup, PlayerName, true )
|
MENU_GROUP_COMMAND:New( PlayerGroup, text, MetricsMenu, self.MenuGroupMWSystem, self, PlayerUnit, PlayerGroup, PlayerName, true )
|
||||||
end
|
end
|
||||||
@@ -873,9 +883,9 @@ do -- SETTINGS
|
|||||||
-- Messages and Reports
|
-- Messages and Reports
|
||||||
---
|
---
|
||||||
|
|
||||||
local text="Messages and Reports"
|
local text = "Messages and Reports"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="Messages & Reports"
|
text = "Messages & Reports"
|
||||||
end
|
end
|
||||||
local MessagesMenu = MENU_GROUP:New( PlayerGroup, text, PlayerMenu )
|
local MessagesMenu = MENU_GROUP:New( PlayerGroup, text, PlayerMenu )
|
||||||
|
|
||||||
@@ -935,39 +945,38 @@ do -- SETTINGS
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- @param #SETTINGS self
|
--- @param #SETTINGS self
|
||||||
function SETTINGS:A2GMenuSystem( MenuGroup, RootMenu, A2GSystem )
|
function SETTINGS:A2GMenuSystem( MenuGroup, RootMenu, A2GSystem )
|
||||||
self.A2GSystem = A2GSystem
|
self.A2GSystem = A2GSystem
|
||||||
MESSAGE:New( string.format("Settings: Default A2G coordinate system set to %s for all players!", A2GSystem ), 5 ):ToAll()
|
MESSAGE:New( string.format( "Settings: Default A2G coordinate system set to %s for all players!", A2GSystem ), 5 ):ToAll()
|
||||||
self:SetSystemMenu( MenuGroup, RootMenu )
|
self:SetSystemMenu( MenuGroup, RootMenu )
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param #SETTINGS self
|
--- @param #SETTINGS self
|
||||||
function SETTINGS:A2AMenuSystem( MenuGroup, RootMenu, A2ASystem )
|
function SETTINGS:A2AMenuSystem( MenuGroup, RootMenu, A2ASystem )
|
||||||
self.A2ASystem = A2ASystem
|
self.A2ASystem = A2ASystem
|
||||||
MESSAGE:New( string.format("Settings: Default A2A coordinate system set to %s for all players!", A2ASystem ), 5 ):ToAll()
|
MESSAGE:New( string.format( "Settings: Default A2A coordinate system set to %s for all players!", A2ASystem ), 5 ):ToAll()
|
||||||
self:SetSystemMenu( MenuGroup, RootMenu )
|
self:SetSystemMenu( MenuGroup, RootMenu )
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param #SETTINGS self
|
--- @param #SETTINGS self
|
||||||
function SETTINGS:MenuLL_DDM_Accuracy( MenuGroup, RootMenu, LL_Accuracy )
|
function SETTINGS:MenuLL_DDM_Accuracy( MenuGroup, RootMenu, LL_Accuracy )
|
||||||
self.LL_Accuracy = LL_Accuracy
|
self.LL_Accuracy = LL_Accuracy
|
||||||
MESSAGE:New( string.format("Settings: Default LL accuracy set to %s for all players!", LL_Accuracy ), 5 ):ToAll()
|
MESSAGE:New( string.format( "Settings: Default LL accuracy set to %s for all players!", LL_Accuracy ), 5 ):ToAll()
|
||||||
self:SetSystemMenu( MenuGroup, RootMenu )
|
self:SetSystemMenu( MenuGroup, RootMenu )
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param #SETTINGS self
|
--- @param #SETTINGS self
|
||||||
function SETTINGS:MenuMGRS_Accuracy( MenuGroup, RootMenu, MGRS_Accuracy )
|
function SETTINGS:MenuMGRS_Accuracy( MenuGroup, RootMenu, MGRS_Accuracy )
|
||||||
self.MGRS_Accuracy = MGRS_Accuracy
|
self.MGRS_Accuracy = MGRS_Accuracy
|
||||||
MESSAGE:New( string.format("Settings: Default MGRS accuracy set to %s for all players!", MGRS_Accuracy ), 5 ):ToAll()
|
MESSAGE:New( string.format( "Settings: Default MGRS accuracy set to %s for all players!", MGRS_Accuracy ), 5 ):ToAll()
|
||||||
self:SetSystemMenu( MenuGroup, RootMenu )
|
self:SetSystemMenu( MenuGroup, RootMenu )
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param #SETTINGS self
|
--- @param #SETTINGS self
|
||||||
function SETTINGS:MenuMWSystem( MenuGroup, RootMenu, MW )
|
function SETTINGS:MenuMWSystem( MenuGroup, RootMenu, MW )
|
||||||
self.Metric = MW
|
self.Metric = MW
|
||||||
MESSAGE:New( string.format("Settings: Default measurement format set to %s for all players!", MW and "Metric" or "Imperial" ), 5 ):ToAll()
|
MESSAGE:New( string.format( "Settings: Default measurement format set to %s for all players!", MW and "Metric" or "Imperial" ), 5 ):ToAll()
|
||||||
self:SetSystemMenu( MenuGroup, RootMenu )
|
self:SetSystemMenu( MenuGroup, RootMenu )
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -980,12 +989,12 @@ do -- SETTINGS
|
|||||||
do
|
do
|
||||||
--- @param #SETTINGS self
|
--- @param #SETTINGS self
|
||||||
function SETTINGS:MenuGroupA2GSystem( PlayerUnit, PlayerGroup, PlayerName, A2GSystem )
|
function SETTINGS:MenuGroupA2GSystem( PlayerUnit, PlayerGroup, PlayerName, A2GSystem )
|
||||||
BASE:E( {self, PlayerUnit:GetName(), A2GSystem} )
|
BASE:E( { self, PlayerUnit:GetName(), A2GSystem } )
|
||||||
self.A2GSystem = A2GSystem
|
self.A2GSystem = A2GSystem
|
||||||
MESSAGE:New( string.format( "Settings: A2G format set to %s for player %s.", A2GSystem, PlayerName ), 5 ):ToGroup( PlayerGroup )
|
MESSAGE:New( string.format( "Settings: A2G format set to %s for player %s.", A2GSystem, PlayerName ), 5 ):ToGroup( PlayerGroup )
|
||||||
if _SETTINGS.MenuStatic==false then
|
if _SETTINGS.MenuStatic == false then
|
||||||
self:RemovePlayerMenu(PlayerUnit)
|
self:RemovePlayerMenu( PlayerUnit )
|
||||||
self:SetPlayerMenu(PlayerUnit)
|
self:SetPlayerMenu( PlayerUnit )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -993,9 +1002,9 @@ do -- SETTINGS
|
|||||||
function SETTINGS:MenuGroupA2ASystem( PlayerUnit, PlayerGroup, PlayerName, A2ASystem )
|
function SETTINGS:MenuGroupA2ASystem( PlayerUnit, PlayerGroup, PlayerName, A2ASystem )
|
||||||
self.A2ASystem = A2ASystem
|
self.A2ASystem = A2ASystem
|
||||||
MESSAGE:New( string.format( "Settings: A2A format set to %s for player %s.", A2ASystem, PlayerName ), 5 ):ToGroup( PlayerGroup )
|
MESSAGE:New( string.format( "Settings: A2A format set to %s for player %s.", A2ASystem, PlayerName ), 5 ):ToGroup( PlayerGroup )
|
||||||
if _SETTINGS.MenuStatic==false then
|
if _SETTINGS.MenuStatic == false then
|
||||||
self:RemovePlayerMenu(PlayerUnit)
|
self:RemovePlayerMenu( PlayerUnit )
|
||||||
self:SetPlayerMenu(PlayerUnit)
|
self:SetPlayerMenu( PlayerUnit )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1003,9 +1012,9 @@ do -- SETTINGS
|
|||||||
function SETTINGS:MenuGroupLL_DDM_AccuracySystem( PlayerUnit, PlayerGroup, PlayerName, LL_Accuracy )
|
function SETTINGS:MenuGroupLL_DDM_AccuracySystem( PlayerUnit, PlayerGroup, PlayerName, LL_Accuracy )
|
||||||
self.LL_Accuracy = LL_Accuracy
|
self.LL_Accuracy = LL_Accuracy
|
||||||
MESSAGE:New( string.format( "Settings: LL format accuracy set to %d decimal places for player %s.", LL_Accuracy, PlayerName ), 5 ):ToGroup( PlayerGroup )
|
MESSAGE:New( string.format( "Settings: LL format accuracy set to %d decimal places for player %s.", LL_Accuracy, PlayerName ), 5 ):ToGroup( PlayerGroup )
|
||||||
if _SETTINGS.MenuStatic==false then
|
if _SETTINGS.MenuStatic == false then
|
||||||
self:RemovePlayerMenu(PlayerUnit)
|
self:RemovePlayerMenu( PlayerUnit )
|
||||||
self:SetPlayerMenu(PlayerUnit)
|
self:SetPlayerMenu( PlayerUnit )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1013,9 +1022,9 @@ do -- SETTINGS
|
|||||||
function SETTINGS:MenuGroupMGRS_AccuracySystem( PlayerUnit, PlayerGroup, PlayerName, MGRS_Accuracy )
|
function SETTINGS:MenuGroupMGRS_AccuracySystem( PlayerUnit, PlayerGroup, PlayerName, MGRS_Accuracy )
|
||||||
self.MGRS_Accuracy = MGRS_Accuracy
|
self.MGRS_Accuracy = MGRS_Accuracy
|
||||||
MESSAGE:New( string.format( "Settings: MGRS format accuracy set to %d for player %s.", MGRS_Accuracy, PlayerName ), 5 ):ToGroup( PlayerGroup )
|
MESSAGE:New( string.format( "Settings: MGRS format accuracy set to %d for player %s.", MGRS_Accuracy, PlayerName ), 5 ):ToGroup( PlayerGroup )
|
||||||
if _SETTINGS.MenuStatic==false then
|
if _SETTINGS.MenuStatic == false then
|
||||||
self:RemovePlayerMenu(PlayerUnit)
|
self:RemovePlayerMenu( PlayerUnit )
|
||||||
self:SetPlayerMenu(PlayerUnit)
|
self:SetPlayerMenu( PlayerUnit )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1023,9 +1032,9 @@ do -- SETTINGS
|
|||||||
function SETTINGS:MenuGroupMWSystem( PlayerUnit, PlayerGroup, PlayerName, MW )
|
function SETTINGS:MenuGroupMWSystem( PlayerUnit, PlayerGroup, PlayerName, MW )
|
||||||
self.Metric = MW
|
self.Metric = MW
|
||||||
MESSAGE:New( string.format( "Settings: Measurement format set to %s for player %s.", MW and "Metric" or "Imperial", PlayerName ), 5 ):ToGroup( PlayerGroup )
|
MESSAGE:New( string.format( "Settings: Measurement format set to %s for player %s.", MW and "Metric" or "Imperial", PlayerName ), 5 ):ToGroup( PlayerGroup )
|
||||||
if _SETTINGS.MenuStatic==false then
|
if _SETTINGS.MenuStatic == false then
|
||||||
self:RemovePlayerMenu(PlayerUnit)
|
self:RemovePlayerMenu( PlayerUnit )
|
||||||
self:SetPlayerMenu(PlayerUnit)
|
self:SetPlayerMenu( PlayerUnit )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1055,7 +1064,6 @@ do -- SETTINGS
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Configures the era of the mission to be Cold war.
|
--- Configures the era of the mission to be Cold war.
|
||||||
-- @param #SETTINGS self
|
-- @param #SETTINGS self
|
||||||
-- @return #SETTINGS self
|
-- @return #SETTINGS self
|
||||||
@@ -1065,7 +1073,6 @@ do -- SETTINGS
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Configures the era of the mission to be Modern war.
|
--- Configures the era of the mission to be Modern war.
|
||||||
-- @param #SETTINGS self
|
-- @param #SETTINGS self
|
||||||
-- @return #SETTINGS self
|
-- @return #SETTINGS self
|
||||||
@@ -1075,7 +1082,4 @@ do -- SETTINGS
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -46,14 +46,14 @@
|
|||||||
-- @field #number InitOffsetAngle Link offset angle in degrees.
|
-- @field #number InitOffsetAngle Link offset angle in degrees.
|
||||||
-- @field #number InitStaticHeading Heading of the static.
|
-- @field #number InitStaticHeading Heading of the static.
|
||||||
-- @field #string InitStaticLivery Livery for aircraft.
|
-- @field #string InitStaticLivery Livery for aircraft.
|
||||||
-- @field #string InitStaticShape Shape of teh static.
|
-- @field #string InitStaticShape Shape of the static.
|
||||||
-- @field #string InitStaticType Type of the static.
|
-- @field #string InitStaticType Type of the static.
|
||||||
-- @field #string InitStaticCategory Categrory of the static.
|
-- @field #string InitStaticCategory Categrory of the static.
|
||||||
-- @field #string InitStaticName Name of the static.
|
-- @field #string InitStaticName Name of the static.
|
||||||
-- @field Core.Point#COORDINATE InitStaticCoordinate Coordinate where to spawn the static.
|
-- @field Core.Point#COORDINATE InitStaticCoordinate Coordinate where to spawn the static.
|
||||||
-- @field #boolean InitDead Set static to be dead if true.
|
-- @field #boolean InitStaticDead Set static to be dead if true.
|
||||||
-- @field #boolean InitCargo If true, static can act as cargo.
|
-- @field #boolean InitStaticCargo If true, static can act as cargo.
|
||||||
-- @field #number InitCargoMass Mass of cargo in kg.
|
-- @field #number InitStaticCargoMass Mass of cargo in kg.
|
||||||
-- @extends Core.Base#BASE
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
|
|
||||||
@@ -260,7 +260,7 @@ end
|
|||||||
-- @param #number Mass Mass of the cargo in kg.
|
-- @param #number Mass Mass of the cargo in kg.
|
||||||
-- @return #SPAWNSTATIC self
|
-- @return #SPAWNSTATIC self
|
||||||
function SPAWNSTATIC:InitCargoMass(Mass)
|
function SPAWNSTATIC:InitCargoMass(Mass)
|
||||||
self.InitCargoMass=Mass
|
self.InitStaticCargoMass=Mass
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -269,7 +269,16 @@ end
|
|||||||
-- @param #boolean IsCargo If true, this static can act as cargo.
|
-- @param #boolean IsCargo If true, this static can act as cargo.
|
||||||
-- @return #SPAWNSTATIC self
|
-- @return #SPAWNSTATIC self
|
||||||
function SPAWNSTATIC:InitCargo(IsCargo)
|
function SPAWNSTATIC:InitCargo(IsCargo)
|
||||||
self.InitCargo=IsCargo
|
self.InitStaticCargo=IsCargo
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Initialize as dead.
|
||||||
|
-- @param #SPAWNSTATIC self
|
||||||
|
-- @param #boolean IsCargo If true, this static is dead.
|
||||||
|
-- @return #SPAWNSTATIC self
|
||||||
|
function SPAWNSTATIC:InitDead(IsDead)
|
||||||
|
self.InitStaticDead=IsDead
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -417,16 +426,16 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
|
|||||||
Template.livery_id=self.InitStaticLivery
|
Template.livery_id=self.InitStaticLivery
|
||||||
end
|
end
|
||||||
|
|
||||||
if self.InitDead~=nil then
|
if self.InitStaticDead~=nil then
|
||||||
Template.dead=self.InitDead
|
Template.dead=self.InitStaticDead
|
||||||
end
|
end
|
||||||
|
|
||||||
if self.InitCargo~=nil then
|
if self.InitStaticCargo~=nil then
|
||||||
Template.canCargo=self.InitCargo
|
Template.canCargo=self.InitStaticCargo
|
||||||
end
|
end
|
||||||
|
|
||||||
if self.InitCargoMass~=nil then
|
if self.InitStaticCargoMass~=nil then
|
||||||
Template.mass=self.InitCargoMass
|
Template.mass=self.InitStaticCargoMass
|
||||||
end
|
end
|
||||||
|
|
||||||
if self.InitLinkUnit then
|
if self.InitLinkUnit then
|
||||||
@@ -479,6 +488,8 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
|
|||||||
-- ED's dirty way to spawn FARPS.
|
-- ED's dirty way to spawn FARPS.
|
||||||
Static=coalition.addGroup(CountryID, -1, TemplateGroup)
|
Static=coalition.addGroup(CountryID, -1, TemplateGroup)
|
||||||
else
|
else
|
||||||
|
self:T("Spawning Static")
|
||||||
|
self:T2({Template=Template})
|
||||||
Static=coalition.addStaticObject(CountryID, Template)
|
Static=coalition.addStaticObject(CountryID, Template)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
202
Moose Development/Moose/Core/TextAndSound.lua
Normal file
202
Moose Development/Moose/Core/TextAndSound.lua
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
--- **Core** - TEXTANDSOUND (MOOSE gettext) system
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ## Main Features:
|
||||||
|
--
|
||||||
|
-- * A GetText for Moose
|
||||||
|
-- * Build a set of localized text entries, alongside their sounds and subtitles
|
||||||
|
-- * Aimed at class developers to offer localizable language support
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ## Example Missions:
|
||||||
|
--
|
||||||
|
-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/).
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ### Author: **applevangelist**
|
||||||
|
-- ## Date: April 2022
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- @module Core.TextAndSound
|
||||||
|
-- @image MOOSE.JPG
|
||||||
|
|
||||||
|
--- Text and Sound class.
|
||||||
|
-- @type TEXTANDSOUND
|
||||||
|
-- @field #string ClassName Name of this class.
|
||||||
|
-- @field #string version Versioning.
|
||||||
|
-- @field #string lid LID for log entries.
|
||||||
|
-- @field #string locale Default locale of this object.
|
||||||
|
-- @field #table entries Table of entries.
|
||||||
|
-- @field #string textclass Name of the class the texts belong to.
|
||||||
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
|
---
|
||||||
|
--
|
||||||
|
-- @field #TEXTANDSOUND
|
||||||
|
TEXTANDSOUND = {
|
||||||
|
ClassName = "TEXTANDSOUND",
|
||||||
|
version = "0.0.1",
|
||||||
|
lid = "",
|
||||||
|
locale = "en",
|
||||||
|
entries = {},
|
||||||
|
textclass = "",
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Text and Sound entry.
|
||||||
|
-- @type TEXTANDSOUND.Entry
|
||||||
|
-- @field #string Classname Name of the class this entry is for.
|
||||||
|
-- @field #string Locale Locale of this entry, defaults to "en".
|
||||||
|
-- @field #table Data The list of entries.
|
||||||
|
|
||||||
|
--- Text and Sound data
|
||||||
|
-- @type TEXTANDSOUND.Data
|
||||||
|
-- @field #string ID ID of this entry for retrieval.
|
||||||
|
-- @field #string Text Text of this entry.
|
||||||
|
-- @field #string Soundfile (optional) Soundfile File name of the corresponding sound file.
|
||||||
|
-- @field #number Soundlength (optional) Length of the sound file in seconds.
|
||||||
|
-- @field #string Subtitle (optional) Subtitle for the sound file.
|
||||||
|
|
||||||
|
--- Instantiate a new object
|
||||||
|
-- @param #TEXTANDSOUND self
|
||||||
|
-- @param #string ClassName Name of the class this instance is providing texts for.
|
||||||
|
-- @param #string Defaultlocale (Optional) Default locale of this instance, defaults to "en".
|
||||||
|
-- @return #TEXTANDSOUND self
|
||||||
|
function TEXTANDSOUND:New(ClassName,Defaultlocale)
|
||||||
|
-- Inherit everything from BASE class.
|
||||||
|
local self=BASE:Inherit(self, BASE:New())
|
||||||
|
-- Set some string id for output to DCS.log file.
|
||||||
|
self.lid=string.format("%s (%s) | ", self.ClassName, self.version)
|
||||||
|
self.locale = Defaultlocale or (_SETTINGS:GetLocale() or "en")
|
||||||
|
self.textclass = ClassName or "none"
|
||||||
|
self.entries = {}
|
||||||
|
local initentry = {} -- #TEXTANDSOUND.Entry
|
||||||
|
initentry.Classname = ClassName
|
||||||
|
initentry.Data = {}
|
||||||
|
initentry.Locale = self.locale
|
||||||
|
self.entries[self.locale] = initentry
|
||||||
|
self:I(self.lid .. "Instantiated.")
|
||||||
|
self:T({self.entries[self.locale]})
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Add an entry
|
||||||
|
-- @param #TEXTANDSOUND self
|
||||||
|
-- @param #string Locale Locale to set for this entry, e.g. "de".
|
||||||
|
-- @param #string ID Unique(!) ID of this entry under this locale (i.e. use the same ID to get localized text for the entry in another language).
|
||||||
|
-- @param #string Text Text for this entry.
|
||||||
|
-- @param #string Soundfile (Optional) Sound file name for this entry.
|
||||||
|
-- @param #number Soundlength (Optional) Length of the sound file in seconds.
|
||||||
|
-- @param #string Subtitle (Optional) Subtitle to be used alongside the sound file.
|
||||||
|
-- @return #TEXTANDSOUND self
|
||||||
|
function TEXTANDSOUND:AddEntry(Locale,ID,Text,Soundfile,Soundlength,Subtitle)
|
||||||
|
self:T(self.lid .. "AddEntry")
|
||||||
|
local locale = Locale or self.locale
|
||||||
|
local dataentry = {} -- #TEXTANDSOUND.Data
|
||||||
|
dataentry.ID = ID or "1"
|
||||||
|
dataentry.Text = Text or "none"
|
||||||
|
dataentry.Soundfile = Soundfile
|
||||||
|
dataentry.Soundlength = Soundlength or 0
|
||||||
|
dataentry.Subtitle = Subtitle
|
||||||
|
if not self.entries[locale] then
|
||||||
|
local initentry = {} -- #TEXTANDSOUND.Entry
|
||||||
|
initentry.Classname = self.textclass -- class name entry
|
||||||
|
initentry.Data = {} -- data array
|
||||||
|
initentry.Locale = locale -- default locale
|
||||||
|
self.entries[locale] = initentry
|
||||||
|
end
|
||||||
|
self.entries[locale].Data[ID] = dataentry
|
||||||
|
self:T({self.entries[locale].Data})
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get an entry
|
||||||
|
-- @param #TEXTANDSOUND self
|
||||||
|
-- @param #string ID The unique ID of the data to be retrieved.
|
||||||
|
-- @param #string Locale (Optional) The locale of the text to be retrieved - defauls to default locale set with `New()`.
|
||||||
|
-- @return #string Text Text or nil if not found and no fallback.
|
||||||
|
-- @return #string Soundfile Filename or nil if not found and no fallback.
|
||||||
|
-- @return #string Soundlength Length of the sound or 0 if not found and no fallback.
|
||||||
|
-- @return #string Subtitle Text for subtitle or nil if not found and no fallback.
|
||||||
|
function TEXTANDSOUND:GetEntry(ID,Locale)
|
||||||
|
self:T(self.lid .. "GetEntry")
|
||||||
|
local locale = Locale or self.locale
|
||||||
|
if not self.entries[locale] then
|
||||||
|
-- fall back to default "en"
|
||||||
|
locale = self.locale
|
||||||
|
end
|
||||||
|
local Text,Soundfile,Soundlength,Subtitle = nil, nil, 0, nil
|
||||||
|
if self.entries[locale] then
|
||||||
|
if self.entries[locale].Data then
|
||||||
|
local data = self.entries[locale].Data[ID] -- #TEXTANDSOUND.Data
|
||||||
|
if data then
|
||||||
|
Text = data.Text
|
||||||
|
Soundfile = data.Soundfile
|
||||||
|
Soundlength = data.Soundlength
|
||||||
|
Subtitle = data.Subtitle
|
||||||
|
elseif self.entries[self.locale].Data[ID] then
|
||||||
|
-- no matching entry, try default
|
||||||
|
local data = self.entries[self.locale].Data[ID]
|
||||||
|
Text = data.Text
|
||||||
|
Soundfile = data.Soundfile
|
||||||
|
Soundlength = data.Soundlength
|
||||||
|
Subtitle = data.Subtitle
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return nil, nil, 0, nil
|
||||||
|
end
|
||||||
|
return Text,Soundfile,Soundlength,Subtitle
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the default locale of this object
|
||||||
|
-- @param #TEXTANDSOUND self
|
||||||
|
-- @return #string locale
|
||||||
|
function TEXTANDSOUND:GetDefaultLocale()
|
||||||
|
self:T(self.lid .. "GetDefaultLocale")
|
||||||
|
return self.locale
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set default locale of this object
|
||||||
|
-- @param #TEXTANDSOUND self
|
||||||
|
-- @param #string locale
|
||||||
|
-- @return #TEXTANDSOUND self
|
||||||
|
function TEXTANDSOUND:SetDefaultLocale(locale)
|
||||||
|
self:T(self.lid .. "SetDefaultLocale")
|
||||||
|
self.locale = locale or "en"
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Check if a locale exists
|
||||||
|
-- @param #TEXTANDSOUND self
|
||||||
|
-- @return #boolean outcome
|
||||||
|
function TEXTANDSOUND:HasLocale(Locale)
|
||||||
|
self:T(self.lid .. "HasLocale")
|
||||||
|
return self.entries[Locale] and true or false
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Flush all entries to the log
|
||||||
|
-- @param #TEXTANDSOUND self
|
||||||
|
-- @return #TEXTANDSOUND self
|
||||||
|
function TEXTANDSOUND:FlushToLog()
|
||||||
|
self:I(self.lid .. "Flushing entries:")
|
||||||
|
local text = string.format("Textclass: %s | Default Locale: %s",self.textclass, self.locale)
|
||||||
|
for _,_entry in pairs(self.entries) do
|
||||||
|
local entry = _entry -- #TEXTANDSOUND.Entry
|
||||||
|
local text = string.format("Textclassname: %s | Locale: %s",entry.Classname, entry.Locale)
|
||||||
|
self:I(text)
|
||||||
|
for _ID,_data in pairs(entry.Data) do
|
||||||
|
local data = _data -- #TEXTANDSOUND.Data
|
||||||
|
local text = string.format("ID: %s\nText: %s\nSoundfile: %s With length: %d\nSubtitle: %s",tostring(_ID), data.Text or "none",data.Soundfile or "none",data.Soundlength or 0,data.Subtitle or "none")
|
||||||
|
self:I(text)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
----------------------------------------------------------------
|
||||||
|
-- End TextAndSound
|
||||||
|
----------------------------------------------------------------
|
||||||
@@ -34,8 +34,6 @@
|
|||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- 
|
|
||||||
--
|
|
||||||
-- # The TIMER Concept
|
-- # The TIMER Concept
|
||||||
--
|
--
|
||||||
-- 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.
|
-- 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.
|
||||||
@@ -107,19 +105,17 @@ TIMER = {
|
|||||||
--- Timer ID.
|
--- Timer ID.
|
||||||
_TIMERID=0
|
_TIMERID=0
|
||||||
|
|
||||||
--- Timer data base.
|
|
||||||
--_TIMERDB={}
|
|
||||||
|
|
||||||
--- TIMER class version.
|
--- TIMER class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
TIMER.version="0.1.1"
|
TIMER.version="0.1.2"
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- TODO list
|
-- TODO list
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
-- TODO: A lot.
|
-- TODO: Randomization.
|
||||||
-- TODO: Write docs.
|
-- TODO: Pause/unpause.
|
||||||
|
-- DONE: Write docs.
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- Constructor
|
-- Constructor
|
||||||
@@ -156,9 +152,6 @@ function TIMER:New(Function, ...)
|
|||||||
-- Log id.
|
-- Log id.
|
||||||
self.lid=string.format("TIMER UID=%d | ", self.uid)
|
self.lid=string.format("TIMER UID=%d | ", self.uid)
|
||||||
|
|
||||||
-- Add to DB.
|
|
||||||
--_TIMERDB[self.uid]=self
|
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -174,7 +167,7 @@ function TIMER:Start(Tstart, dT, Duration)
|
|||||||
local Tnow=timer.getTime()
|
local Tnow=timer.getTime()
|
||||||
|
|
||||||
-- Start time in sec.
|
-- Start time in sec.
|
||||||
self.Tstart=Tstart and Tnow+Tstart or Tnow+0.001 -- one millisecond delay if Tstart=nil
|
self.Tstart=Tstart and Tnow+math.max(Tstart, 0.001) or Tnow+0.001 -- one millisecond delay if Tstart=nil
|
||||||
|
|
||||||
-- Set time interval.
|
-- Set time interval.
|
||||||
self.dT=dT
|
self.dT=dT
|
||||||
@@ -219,10 +212,7 @@ function TIMER:Stop(Delay)
|
|||||||
|
|
||||||
-- Not running any more.
|
-- Not running any more.
|
||||||
self.isrunning=false
|
self.isrunning=false
|
||||||
|
|
||||||
-- Remove DB entry.
|
|
||||||
--_TIMERDB[self.uid]=nil
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -239,6 +229,15 @@ function TIMER:SetMaxFunctionCalls(Nmax)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Set time interval. Can also be set when the timer is already running and is applied after the next function call.
|
||||||
|
-- @param #TIMER self
|
||||||
|
-- @param #number dT Time interval in seconds.
|
||||||
|
-- @return #TIMER self
|
||||||
|
function TIMER:SetTimeInterval(dT)
|
||||||
|
self.dT=dT
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- Check if the timer has been started and was not stopped.
|
--- Check if the timer has been started and was not stopped.
|
||||||
-- @param #TIMER self
|
-- @param #TIMER self
|
||||||
-- @return #boolean If `true`, the timer is running.
|
-- @return #boolean If `true`, the timer is running.
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
-- * Create polygon zones.
|
-- * Create polygon zones.
|
||||||
-- * Create moving zones around a unit.
|
-- * Create moving zones around a unit.
|
||||||
-- * Create moving zones around a group.
|
-- * Create moving zones around a group.
|
||||||
-- * Provide the zone behaviour. Some zones are static, while others are moveable.
|
-- * Provide the zone behavior. Some zones are static, while others are moveable.
|
||||||
-- * Enquiry if a coordinate is within a zone.
|
-- * Enquiry if a coordinate is within a zone.
|
||||||
-- * Smoke zones.
|
-- * Smoke zones.
|
||||||
-- * Set a zone probability to control zone selection.
|
-- * Set a zone probability to control zone selection.
|
||||||
@@ -20,10 +20,10 @@
|
|||||||
-- * Draw zones (circular and polygon) on the F10 map.
|
-- * Draw zones (circular and polygon) on the F10 map.
|
||||||
--
|
--
|
||||||
--
|
--
|
||||||
-- There are essentially two core functions that zones accomodate:
|
-- There are essentially two core functions that zones accommodate:
|
||||||
--
|
--
|
||||||
-- * Test if an object is within the zone boundaries.
|
-- * Test if an object is within the zone boundaries.
|
||||||
-- * Provide the zone behaviour. Some zones are static, while others are moveable.
|
-- * Provide the zone behavior. Some zones are static, while others are moveable.
|
||||||
--
|
--
|
||||||
-- The object classes are using the zone classes to test the zone boundaries, which can take various forms:
|
-- The object classes are using the zone classes to test the zone boundaries, which can take various forms:
|
||||||
--
|
--
|
||||||
@@ -59,6 +59,11 @@
|
|||||||
-- @field #number ZoneProbability A value between 0 and 1. 0 = 0% and 1 = 100% probability.
|
-- @field #number ZoneProbability A value between 0 and 1. 0 = 0% and 1 = 100% probability.
|
||||||
-- @field #number DrawID Unique ID of the drawn zone on the F10 map.
|
-- @field #number DrawID Unique ID of the drawn zone on the F10 map.
|
||||||
-- @field #table Color Table with four entries, e.g. {1, 0, 0, 0.15}. First three are RGB color code. Fourth is the transparency Alpha value.
|
-- @field #table Color Table with four entries, e.g. {1, 0, 0, 0.15}. First three are RGB color code. Fourth is the transparency Alpha value.
|
||||||
|
-- @field #table FillColor Table with four entries, e.g. {1, 0, 0, 0.15}. First three are RGB color code. Fourth is the transparency Alpha value.
|
||||||
|
-- @field #number drawCoalition Draw coalition.
|
||||||
|
-- @field #number ZoneID ID of zone. Only zones defined in the ME have an ID!
|
||||||
|
-- @field #table Table of any trigger zone properties from the ME. The key is the Name of the property, and the value is the property's Value.
|
||||||
|
-- @field #number Surface Type of surface. Only determined at the center of the zone!
|
||||||
-- @extends Core.Fsm#FSM
|
-- @extends Core.Fsm#FSM
|
||||||
|
|
||||||
|
|
||||||
@@ -102,13 +107,21 @@
|
|||||||
-- * @{#ZONE_BASE.SmokeZone}(): Smokes the zone boundaries in a color.
|
-- * @{#ZONE_BASE.SmokeZone}(): Smokes the zone boundaries in a color.
|
||||||
-- * @{#ZONE_BASE.FlareZone}(): Flares the zone boundaries in a color.
|
-- * @{#ZONE_BASE.FlareZone}(): Flares the zone boundaries in a color.
|
||||||
--
|
--
|
||||||
|
-- ## A zone might have additional Properties created in the DCS Mission Editor, which can be accessed:
|
||||||
|
--
|
||||||
|
-- *@{#ZONE_BASE.GetProperty}(): Returns the Value of the zone with the given PropertyName, or nil if no matching property exists.
|
||||||
|
-- *@{#ZONE_BASE.GetAllProperties}(): Returns the zone Properties table.
|
||||||
|
--
|
||||||
-- @field #ZONE_BASE
|
-- @field #ZONE_BASE
|
||||||
ZONE_BASE = {
|
ZONE_BASE = {
|
||||||
ClassName = "ZONE_BASE",
|
ClassName = "ZONE_BASE",
|
||||||
ZoneName = "",
|
ZoneName = "",
|
||||||
ZoneProbability = 1,
|
ZoneProbability = 1,
|
||||||
DrawID=nil,
|
DrawID=nil,
|
||||||
Color={}
|
Color={},
|
||||||
|
ZoneID=nil,
|
||||||
|
Properties={},
|
||||||
|
Sureface=nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -129,7 +142,9 @@ function ZONE_BASE:New( ZoneName )
|
|||||||
self:F( ZoneName )
|
self:F( ZoneName )
|
||||||
|
|
||||||
self.ZoneName = ZoneName
|
self.ZoneName = ZoneName
|
||||||
|
|
||||||
|
--_DATABASE:AddZone(ZoneName,self)
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -170,6 +185,7 @@ end
|
|||||||
-- @param DCS#Vec3 Vec3 The point to test.
|
-- @param DCS#Vec3 Vec3 The point to test.
|
||||||
-- @return #boolean true if the Vec3 is within the zone.
|
-- @return #boolean true if the Vec3 is within the zone.
|
||||||
function ZONE_BASE:IsVec3InZone( Vec3 )
|
function ZONE_BASE:IsVec3InZone( Vec3 )
|
||||||
|
if not Vec3 then return false end
|
||||||
local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } )
|
local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } )
|
||||||
return InZone
|
return InZone
|
||||||
end
|
end
|
||||||
@@ -288,6 +304,23 @@ function ZONE_BASE:GetCoordinate( Height ) --R2.1
|
|||||||
return self.Coordinate
|
return self.Coordinate
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get 2D distance to a coordinate.
|
||||||
|
-- @param #ZONE_BASE self
|
||||||
|
-- @param Core.Point#COORDINATE Coordinate Reference coordinate. Can also be a DCS#Vec2 or DCS#Vec3 object.
|
||||||
|
-- @return #number Distance to the reference coordinate in meters.
|
||||||
|
function ZONE_BASE:Get2DDistance(Coordinate)
|
||||||
|
local a=self:GetVec2()
|
||||||
|
local b={}
|
||||||
|
if Coordinate.z then
|
||||||
|
b.x=Coordinate.x
|
||||||
|
b.y=Coordinate.z
|
||||||
|
else
|
||||||
|
b.x=Coordinate.x
|
||||||
|
b.y=Coordinate.y
|
||||||
|
end
|
||||||
|
local dist=UTILS.VecDist2D(a,b)
|
||||||
|
return dist
|
||||||
|
end
|
||||||
|
|
||||||
--- Define a random @{DCS#Vec2} within the zone.
|
--- Define a random @{DCS#Vec2} within the zone.
|
||||||
-- @param #ZONE_BASE self
|
-- @param #ZONE_BASE self
|
||||||
@@ -314,18 +347,41 @@ end
|
|||||||
-- @param #ZONE_BASE self
|
-- @param #ZONE_BASE self
|
||||||
-- @return #nil The bounding square.
|
-- @return #nil The bounding square.
|
||||||
function ZONE_BASE:GetBoundingSquare()
|
function ZONE_BASE:GetBoundingSquare()
|
||||||
--return { x1 = 0, y1 = 0, x2 = 0, y2 = 0 }
|
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get surface type of the zone.
|
||||||
|
-- @param #ZONE_BASE self
|
||||||
|
-- @return DCS#SurfaceType Type of surface.
|
||||||
|
function ZONE_BASE:GetSurfaceType()
|
||||||
|
local coord=self:GetCoordinate()
|
||||||
|
local surface=coord:GetSurfaceType()
|
||||||
|
return surface
|
||||||
|
end
|
||||||
|
|
||||||
--- Bound the zone boundaries with a tires.
|
--- Bound the zone boundaries with a tires.
|
||||||
-- @param #ZONE_BASE self
|
-- @param #ZONE_BASE self
|
||||||
function ZONE_BASE:BoundZone()
|
function ZONE_BASE:BoundZone()
|
||||||
self:F2()
|
self:F2()
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Set draw coalition of zone.
|
||||||
|
-- @param #ZONE_BASE self
|
||||||
|
-- @param #number Coalition Coalition. Default -1.
|
||||||
|
-- @return #ZONE_BASE self
|
||||||
|
function ZONE_BASE:SetDrawCoalition(Coalition)
|
||||||
|
self.drawCoalition=Coalition or -1
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get draw coalition of zone.
|
||||||
|
-- @param #ZONE_BASE self
|
||||||
|
-- @return #number Draw coaliton.
|
||||||
|
function ZONE_BASE:GetDrawCoalition()
|
||||||
|
return self.drawCoalition or -1
|
||||||
|
end
|
||||||
|
|
||||||
--- Set color of zone.
|
--- Set color of zone.
|
||||||
-- @param #ZONE_BASE self
|
-- @param #ZONE_BASE self
|
||||||
-- @param #table RGBcolor RGB color table. Default `{1, 0, 0}`.
|
-- @param #table RGBcolor RGB color table. Default `{1, 0, 0}`.
|
||||||
@@ -349,7 +405,7 @@ end
|
|||||||
-- @param #ZONE_BASE self
|
-- @param #ZONE_BASE self
|
||||||
-- @return #table Table with four entries, e.g. {1, 0, 0, 0.15}. First three are RGB color code. Fourth is the transparency Alpha value.
|
-- @return #table Table with four entries, e.g. {1, 0, 0, 0.15}. First three are RGB color code. Fourth is the transparency Alpha value.
|
||||||
function ZONE_BASE:GetColor()
|
function ZONE_BASE:GetColor()
|
||||||
return self.Color
|
return self.Color or {1, 0, 0, 0.15}
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get RGB color of zone.
|
--- Get RGB color of zone.
|
||||||
@@ -357,9 +413,10 @@ end
|
|||||||
-- @return #table Table with three entries, e.g. {1, 0, 0}, which is the RGB color code.
|
-- @return #table Table with three entries, e.g. {1, 0, 0}, which is the RGB color code.
|
||||||
function ZONE_BASE:GetColorRGB()
|
function ZONE_BASE:GetColorRGB()
|
||||||
local rgb={}
|
local rgb={}
|
||||||
rgb[1]=self.Color[1]
|
local Color=self:GetColor()
|
||||||
rgb[2]=self.Color[2]
|
rgb[1]=Color[1]
|
||||||
rgb[3]=self.Color[3]
|
rgb[2]=Color[2]
|
||||||
|
rgb[3]=Color[3]
|
||||||
return rgb
|
return rgb
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -367,7 +424,55 @@ end
|
|||||||
-- @param #ZONE_BASE self
|
-- @param #ZONE_BASE self
|
||||||
-- @return #number Alpha value.
|
-- @return #number Alpha value.
|
||||||
function ZONE_BASE:GetColorAlpha()
|
function ZONE_BASE:GetColorAlpha()
|
||||||
local alpha=self.Color[4]
|
local Color=self:GetColor()
|
||||||
|
local alpha=Color[4]
|
||||||
|
return alpha
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set fill color of zone.
|
||||||
|
-- @param #ZONE_BASE self
|
||||||
|
-- @param #table RGBcolor RGB color table. Default `{1, 0, 0}`.
|
||||||
|
-- @param #number Alpha Transparacy between 0 and 1. Default 0.15.
|
||||||
|
-- @return #ZONE_BASE self
|
||||||
|
function ZONE_BASE:SetFillColor(RGBcolor, Alpha)
|
||||||
|
|
||||||
|
RGBcolor=RGBcolor or {1, 0, 0}
|
||||||
|
Alpha=Alpha or 0.15
|
||||||
|
|
||||||
|
self.FillColor={}
|
||||||
|
self.FillColor[1]=RGBcolor[1]
|
||||||
|
self.FillColor[2]=RGBcolor[2]
|
||||||
|
self.FillColor[3]=RGBcolor[3]
|
||||||
|
self.FillColor[4]=Alpha
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get fill color table of the zone.
|
||||||
|
-- @param #ZONE_BASE self
|
||||||
|
-- @return #table Table with four entries, e.g. {1, 0, 0, 0.15}. First three are RGB color code. Fourth is the transparency Alpha value.
|
||||||
|
function ZONE_BASE:GetFillColor()
|
||||||
|
return self.FillColor or {1, 0, 0, 0.15}
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get RGB fill color of zone.
|
||||||
|
-- @param #ZONE_BASE self
|
||||||
|
-- @return #table Table with three entries, e.g. {1, 0, 0}, which is the RGB color code.
|
||||||
|
function ZONE_BASE:GetFillColorRGB()
|
||||||
|
local rgb={}
|
||||||
|
local FillColor=self:GetFillColor()
|
||||||
|
rgb[1]=FillColor[1]
|
||||||
|
rgb[2]=FillColor[2]
|
||||||
|
rgb[3]=FillColor[3]
|
||||||
|
return rgb
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get transperency Alpha fill value of zone.
|
||||||
|
-- @param #ZONE_BASE self
|
||||||
|
-- @return #number Alpha value.
|
||||||
|
function ZONE_BASE:GetFillColorAlpha()
|
||||||
|
local FillColor=self:GetFillColor()
|
||||||
|
local alpha=FillColor[4]
|
||||||
return alpha
|
return alpha
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -459,6 +564,26 @@ function ZONE_BASE:GetZoneMaybe()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Returns the Value of the zone with the given PropertyName, or nil if no matching property exists.
|
||||||
|
-- @param #ZONE_BASE self
|
||||||
|
-- @param #string PropertyName The name of a the TriggerZone Property to be retrieved.
|
||||||
|
-- @return #string The Value of the TriggerZone Property with the given PropertyName, or nil if absent.
|
||||||
|
-- @usage
|
||||||
|
--
|
||||||
|
-- local PropertiesZone = ZONE:FindByName("Properties Zone")
|
||||||
|
-- local Property = "ExampleProperty"
|
||||||
|
-- local PropertyValue = PropertiesZone:GetProperty(Property)
|
||||||
|
--
|
||||||
|
function ZONE_BASE:GetProperty(PropertyName)
|
||||||
|
return self.Properties[PropertyName]
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Returns the zone Properties table.
|
||||||
|
-- @param #ZONE_BASE self
|
||||||
|
-- @return #table The Key:Value table of TriggerZone properties of the zone.
|
||||||
|
function ZONE_BASE:GetAllProperties()
|
||||||
|
return self.Properties
|
||||||
|
end
|
||||||
|
|
||||||
--- The ZONE_RADIUS class, defined by a zone name, a location and a radius.
|
--- The ZONE_RADIUS class, defined by a zone name, a location and a radius.
|
||||||
-- @type ZONE_RADIUS
|
-- @type ZONE_RADIUS
|
||||||
@@ -601,7 +726,7 @@ function ZONE_RADIUS:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, Lin
|
|||||||
|
|
||||||
Color=Color or self:GetColorRGB()
|
Color=Color or self:GetColorRGB()
|
||||||
Alpha=Alpha or 1
|
Alpha=Alpha or 1
|
||||||
FillColor=FillColor or Color
|
FillColor=FillColor or UTILS.DeepCopy(Color)
|
||||||
FillAlpha=FillAlpha or self:GetColorAlpha()
|
FillAlpha=FillAlpha or self:GetColorAlpha()
|
||||||
|
|
||||||
self.DrawID=coordinate:CircleToAll(Radius, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly)
|
self.DrawID=coordinate:CircleToAll(Radius, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly)
|
||||||
@@ -789,7 +914,7 @@ end
|
|||||||
|
|
||||||
|
|
||||||
--- Scan the zone for the presence of units of the given ObjectCategories.
|
--- Scan the zone for the presence of units of the given ObjectCategories.
|
||||||
-- Note that after a zone has been scanned, the zone can be evaluated by:
|
-- Note that **only after** a zone has been scanned, the zone can be evaluated by:
|
||||||
--
|
--
|
||||||
-- * @{ZONE_RADIUS.IsAllInZoneOfCoalition}(): Scan the presence of units in the zone of a coalition.
|
-- * @{ZONE_RADIUS.IsAllInZoneOfCoalition}(): Scan the presence of units in the zone of a coalition.
|
||||||
-- * @{ZONE_RADIUS.IsAllInZoneOfOtherCoalition}(): Scan the presence of units in the zone of an other coalition.
|
-- * @{ZONE_RADIUS.IsAllInZoneOfOtherCoalition}(): Scan the presence of units in the zone of an other coalition.
|
||||||
@@ -798,11 +923,11 @@ end
|
|||||||
-- * @{ZONE_RADIUS.IsNoneInZone}(): Scan if the zone is empty.
|
-- * @{ZONE_RADIUS.IsNoneInZone}(): Scan if the zone is empty.
|
||||||
-- @{#ZONE_RADIUS.
|
-- @{#ZONE_RADIUS.
|
||||||
-- @param #ZONE_RADIUS self
|
-- @param #ZONE_RADIUS self
|
||||||
-- @param ObjectCategories An array of categories of the objects to find in the zone.
|
-- @param ObjectCategories An array of categories of the objects to find in the zone. E.g. `{Object.Category.UNIT}`
|
||||||
-- @param UnitCategories An array of unit categories of the objects to find in the zone.
|
-- @param UnitCategories An array of unit categories of the objects to find in the zone. E.g. `{Unit.Category.GROUND_UNIT,Unit.Category.SHIP}`
|
||||||
-- @usage
|
-- @usage
|
||||||
-- self.Zone:Scan()
|
-- myzone:Scan({Object.Category.UNIT},{Unit.Category.GROUND_UNIT})
|
||||||
-- local IsAttacked = self.Zone:IsSomeInZoneOfCoalition( self.Coalition )
|
-- local IsAttacked = myzone:IsSomeInZoneOfCoalition( self.Coalition )
|
||||||
function ZONE_RADIUS:Scan( ObjectCategories, UnitCategories )
|
function ZONE_RADIUS:Scan( ObjectCategories, UnitCategories )
|
||||||
|
|
||||||
self.ScanData = {}
|
self.ScanData = {}
|
||||||
@@ -872,7 +997,7 @@ function ZONE_RADIUS:Scan( ObjectCategories, UnitCategories )
|
|||||||
local SceneryName = ZoneObject:getName()
|
local SceneryName = ZoneObject:getName()
|
||||||
self.ScanData.Scenery[SceneryType] = self.ScanData.Scenery[SceneryType] or {}
|
self.ScanData.Scenery[SceneryType] = self.ScanData.Scenery[SceneryType] or {}
|
||||||
self.ScanData.Scenery[SceneryType][SceneryName] = SCENERY:Register( SceneryName, ZoneObject )
|
self.ScanData.Scenery[SceneryType][SceneryName] = SCENERY:Register( SceneryName, ZoneObject )
|
||||||
self:F2( { SCENERY = self.ScanData.Scenery[SceneryType][SceneryName] } )
|
self:T( { SCENERY = self.ScanData.Scenery[SceneryType][SceneryName] } )
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -1135,7 +1260,9 @@ end
|
|||||||
-- @return #boolean true if the location is within the zone.
|
-- @return #boolean true if the location is within the zone.
|
||||||
function ZONE_RADIUS:IsVec2InZone( Vec2 )
|
function ZONE_RADIUS:IsVec2InZone( Vec2 )
|
||||||
self:F2( Vec2 )
|
self:F2( Vec2 )
|
||||||
|
|
||||||
|
if not Vec2 then return false end
|
||||||
|
|
||||||
local ZoneVec2 = self:GetVec2()
|
local ZoneVec2 = self:GetVec2()
|
||||||
|
|
||||||
if ZoneVec2 then
|
if ZoneVec2 then
|
||||||
@@ -1153,7 +1280,7 @@ end
|
|||||||
-- @return #boolean true if the point is within the zone.
|
-- @return #boolean true if the point is within the zone.
|
||||||
function ZONE_RADIUS:IsVec3InZone( Vec3 )
|
function ZONE_RADIUS:IsVec3InZone( Vec3 )
|
||||||
self:F2( Vec3 )
|
self:F2( Vec3 )
|
||||||
|
if not Vec3 then return false end
|
||||||
local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } )
|
local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } )
|
||||||
|
|
||||||
return InZone
|
return InZone
|
||||||
@@ -1161,24 +1288,54 @@ end
|
|||||||
|
|
||||||
--- Returns a random Vec2 location within the zone.
|
--- Returns a random Vec2 location within the zone.
|
||||||
-- @param #ZONE_RADIUS self
|
-- @param #ZONE_RADIUS self
|
||||||
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0.
|
-- @param #number inner (Optional) Minimal distance from the center of the zone. Default is 0.
|
||||||
-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
|
-- @param #number outer (Optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
|
||||||
|
-- @param #table surfacetypes (Optional) Table of surface types. Can also be a single surface type. We will try max 100 times to find the right type!
|
||||||
-- @return DCS#Vec2 The random location within the zone.
|
-- @return DCS#Vec2 The random location within the zone.
|
||||||
function ZONE_RADIUS:GetRandomVec2( inner, outer )
|
function ZONE_RADIUS:GetRandomVec2(inner, outer, surfacetypes)
|
||||||
self:F( self.ZoneName, inner, outer )
|
|
||||||
|
|
||||||
local Point = {}
|
local Vec2 = self:GetVec2()
|
||||||
local Vec2 = self:GetVec2()
|
local _inner = inner or 0
|
||||||
local _inner = inner or 0
|
local _outer = outer or self:GetRadius()
|
||||||
local _outer = outer or self:GetRadius()
|
|
||||||
|
|
||||||
local angle = math.random() * math.pi * 2;
|
if surfacetypes and type(surfacetypes)~="table" then
|
||||||
Point.x = Vec2.x + math.cos( angle ) * math.random(_inner, _outer);
|
surfacetypes={surfacetypes}
|
||||||
Point.y = Vec2.y + math.sin( angle ) * math.random(_inner, _outer);
|
end
|
||||||
|
|
||||||
self:T( { Point } )
|
local function _getpoint()
|
||||||
|
local point = {}
|
||||||
|
local angle = math.random() * math.pi * 2
|
||||||
|
point.x = Vec2.x + math.cos(angle) * math.random(_inner, _outer)
|
||||||
|
point.y = Vec2.y + math.sin(angle) * math.random(_inner, _outer)
|
||||||
|
return point
|
||||||
|
end
|
||||||
|
|
||||||
return Point
|
local function _checkSurface(point)
|
||||||
|
local stype=land.getSurfaceType(point)
|
||||||
|
for _,sf in pairs(surfacetypes) do
|
||||||
|
if sf==stype then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local point=_getpoint()
|
||||||
|
|
||||||
|
if surfacetypes then
|
||||||
|
local N=1 ; local Nmax=100 ; local gotit=false
|
||||||
|
while gotit==false and N<=Nmax do
|
||||||
|
gotit=_checkSurface(point)
|
||||||
|
if gotit then
|
||||||
|
--env.info(string.format("Got random coordinate with surface type %d after N=%d/%d iterations", land.getSurfaceType(point), N, Nmax))
|
||||||
|
else
|
||||||
|
point=_getpoint()
|
||||||
|
N=N+1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return point
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone.
|
--- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone.
|
||||||
@@ -1230,20 +1387,99 @@ end
|
|||||||
|
|
||||||
--- Returns a @{Core.Point#COORDINATE} object reflecting a random 3D location within the zone.
|
--- Returns a @{Core.Point#COORDINATE} object reflecting a random 3D location within the zone.
|
||||||
-- @param #ZONE_RADIUS self
|
-- @param #ZONE_RADIUS self
|
||||||
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0.
|
-- @param #number inner (Optional) Minimal distance from the center of the zone in meters. Default is 0 m.
|
||||||
-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
|
-- @param #number outer (Optional) Maximal distance from the outer edge of the zone in meters. Default is the radius of the zone.
|
||||||
-- @return Core.Point#COORDINATE
|
-- @param #table surfacetypes (Optional) Table of surface types. Can also be a single surface type. We will try max 1000 times to find the right type!
|
||||||
function ZONE_RADIUS:GetRandomCoordinate( inner, outer )
|
-- @return Core.Point#COORDINATE The random coordinate.
|
||||||
self:F( self.ZoneName, inner, outer )
|
function ZONE_RADIUS:GetRandomCoordinate(inner, outer, surfacetypes)
|
||||||
|
|
||||||
local Coordinate = COORDINATE:NewFromVec2( self:GetRandomVec2(inner, outer) )
|
local vec2=self:GetRandomVec2(inner, outer, surfacetypes)
|
||||||
|
|
||||||
self:T3( { Coordinate = Coordinate } )
|
local Coordinate = COORDINATE:NewFromVec2(vec2)
|
||||||
|
|
||||||
return Coordinate
|
return Coordinate
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Returns a @{Core.Point#COORDINATE} object reflecting a random location within the zone where there are no **map objects** of type "Building".
|
||||||
|
-- Does not find statics you might have placed there. **Note** This might be quite CPU intensive, use with care.
|
||||||
|
-- @param #ZONE_RADIUS self
|
||||||
|
-- @param #number inner (Optional) Minimal distance from the center of the zone in meters. Default is 0m.
|
||||||
|
-- @param #number outer (Optional) Maximal distance from the outer edge of the zone in meters. Default is the radius of the zone.
|
||||||
|
-- @param #number distance (Optional) Minumum distance from any building coordinate. Defaults to 100m.
|
||||||
|
-- @param #boolean markbuildings (Optional) Place markers on found buildings (if any).
|
||||||
|
-- @param #boolean markfinal (Optional) Place marker on the final coordinate (if any).
|
||||||
|
-- @return Core.Point#COORDINATE The random coordinate or `nil` if cannot be found in 1000 iterations.
|
||||||
|
function ZONE_RADIUS:GetRandomCoordinateWithoutBuildings(inner,outer,distance,markbuildings,markfinal)
|
||||||
|
|
||||||
|
local dist = distance or 100
|
||||||
|
|
||||||
|
local objects = {}
|
||||||
|
|
||||||
|
if self.ScanData and self.ScanData.Scenery then
|
||||||
|
objects = self:GetScannedScenery()
|
||||||
|
else
|
||||||
|
self:Scan({Object.Category.SCENERY})
|
||||||
|
objects = self:GetScannedScenery()
|
||||||
|
end
|
||||||
|
|
||||||
|
local T0 = timer.getTime()
|
||||||
|
local T1 = timer.getTime()
|
||||||
|
|
||||||
|
|
||||||
|
local buildings = {}
|
||||||
|
if self.ScanData and self.ScanData.BuildingCoordinates then
|
||||||
|
buildings = self.ScanData.BuildingCoordinates
|
||||||
|
else
|
||||||
|
-- build table of buildings coordinates
|
||||||
|
for _,_object in pairs (objects) do
|
||||||
|
for _,_scen in pairs (_object) do
|
||||||
|
local scenery = _scen -- Wrapper.Scenery#SCENERY
|
||||||
|
local description=scenery:GetDesc()
|
||||||
|
if description and description.attributes and description.attributes.Buildings then
|
||||||
|
if markbuildings then
|
||||||
|
MARKER:New(scenery:GetCoordinate(),"Building"):ToAll()
|
||||||
|
end
|
||||||
|
buildings[#buildings+1] = scenery:GetCoordinate()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self.ScanData.BuildingCoordinates = buildings
|
||||||
|
end
|
||||||
|
|
||||||
|
-- max 1000 tries
|
||||||
|
local rcoord = nil
|
||||||
|
local found = false
|
||||||
|
local iterations = 0
|
||||||
|
|
||||||
|
for i=1,1000 do
|
||||||
|
iterations = iterations + 1
|
||||||
|
rcoord = self:GetRandomCoordinate(inner,outer)
|
||||||
|
found = false
|
||||||
|
for _,_coord in pairs (buildings) do
|
||||||
|
local coord = _coord -- Core.Point#COORDINATE
|
||||||
|
-- keep >50m dist from buildings
|
||||||
|
if coord:Get2DDistance(rcoord) > dist then
|
||||||
|
found = true
|
||||||
|
else
|
||||||
|
found = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if found then
|
||||||
|
-- we have a winner!
|
||||||
|
if markfinal then
|
||||||
|
MARKER:New(rcoord,"FREE"):ToAll()
|
||||||
|
end
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
T1=timer.getTime()
|
||||||
|
|
||||||
|
self:T(string.format("Found a coordinate: %s | Iterations: %d | Time: %d",tostring(found),iterations,T1-T0))
|
||||||
|
|
||||||
|
if found then return rcoord else return nil end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
--- @type ZONE
|
--- @type ZONE
|
||||||
-- @extends #ZONE_RADIUS
|
-- @extends #ZONE_RADIUS
|
||||||
@@ -1301,7 +1537,7 @@ function ZONE:New( ZoneName )
|
|||||||
|
|
||||||
-- Error!
|
-- Error!
|
||||||
if not Zone then
|
if not Zone then
|
||||||
error( "Zone " .. ZoneName .. " does not exist." )
|
env.error( "ERROR: Zone " .. ZoneName .. " does not exist!" )
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1319,9 +1555,9 @@ function ZONE:New( ZoneName )
|
|||||||
end
|
end
|
||||||
|
|
||||||
--- Find a zone in the _DATABASE using the name of the zone.
|
--- Find a zone in the _DATABASE using the name of the zone.
|
||||||
-- @param #ZONE_BASE self
|
-- @param #ZONE self
|
||||||
-- @param #string ZoneName The name of the zone.
|
-- @param #string ZoneName The name of the zone.
|
||||||
-- @return #ZONE_BASE self
|
-- @return #ZONE self
|
||||||
function ZONE:FindByName( ZoneName )
|
function ZONE:FindByName( ZoneName )
|
||||||
|
|
||||||
local ZoneFound = _DATABASE:FindZone( ZoneName )
|
local ZoneFound = _DATABASE:FindZone( ZoneName )
|
||||||
@@ -1557,7 +1793,7 @@ end
|
|||||||
|
|
||||||
|
|
||||||
--- @type ZONE_POLYGON_BASE
|
--- @type ZONE_POLYGON_BASE
|
||||||
-- --@field #ZONE_POLYGON_BASE.ListVec2 Polygon The polygon defined by an array of @{DCS#Vec2}.
|
-- @field #ZONE_POLYGON_BASE.ListVec2 Polygon The polygon defined by an array of @{DCS#Vec2}.
|
||||||
-- @extends #ZONE_BASE
|
-- @extends #ZONE_BASE
|
||||||
|
|
||||||
|
|
||||||
@@ -1795,7 +2031,7 @@ function ZONE_POLYGON_BASE:BoundZone( UnBound )
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Draw the zone on the F10 map. **NOTE** Currently, only polygons with **exactly four points** are supported!
|
--- Draw the zone on the F10 map. **NOTE** Currently, only polygons **up to ten points** are supported!
|
||||||
-- @param #ZONE_POLYGON_BASE self
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
|
-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
|
||||||
-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red.
|
-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red.
|
||||||
@@ -1807,32 +2043,48 @@ end
|
|||||||
-- @return #ZONE_POLYGON_BASE self
|
-- @return #ZONE_POLYGON_BASE self
|
||||||
function ZONE_POLYGON_BASE:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly)
|
function ZONE_POLYGON_BASE:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly)
|
||||||
|
|
||||||
local coordinate=COORDINATE:NewFromVec2(self._.Polygon[1])
|
if self._.Polygon and #self._.Polygon>=3 then
|
||||||
|
|
||||||
Color=Color or self:GetColorRGB()
|
|
||||||
Alpha=Alpha or 1
|
|
||||||
FillColor=FillColor or Color
|
|
||||||
FillAlpha=FillAlpha or self:GetColorAlpha()
|
|
||||||
|
|
||||||
|
|
||||||
if #self._.Polygon==4 then
|
|
||||||
|
|
||||||
local Coord2=COORDINATE:NewFromVec2(self._.Polygon[2])
|
|
||||||
local Coord3=COORDINATE:NewFromVec2(self._.Polygon[3])
|
|
||||||
local Coord4=COORDINATE:NewFromVec2(self._.Polygon[4])
|
|
||||||
|
|
||||||
self.DrawID=coordinate:QuadToAll(Coord2, Coord3, Coord4, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly)
|
|
||||||
|
|
||||||
else
|
|
||||||
|
|
||||||
local Coordinates=self:GetVerticiesCoordinates()
|
|
||||||
table.remove(Coordinates, 1)
|
|
||||||
|
|
||||||
self.DrawID=coordinate:MarkupToAllFreeForm(Coordinates, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly)
|
|
||||||
|
|
||||||
|
local coordinate=COORDINATE:NewFromVec2(self._.Polygon[1])
|
||||||
|
|
||||||
|
Coalition=Coalition or self:GetDrawCoalition()
|
||||||
|
|
||||||
|
-- Set draw coalition.
|
||||||
|
self:SetDrawCoalition(Coalition)
|
||||||
|
|
||||||
|
Color=Color or self:GetColorRGB()
|
||||||
|
Alpha=Alpha or 1
|
||||||
|
|
||||||
|
-- Set color.
|
||||||
|
self:SetColor(Color, Alpha)
|
||||||
|
|
||||||
|
FillColor=FillColor or self:GetFillColorRGB()
|
||||||
|
if not FillColor then UTILS.DeepCopy(Color) end
|
||||||
|
FillAlpha=FillAlpha or self:GetFillColorAlpha()
|
||||||
|
if not FillAlpha then FillAlpha=0.15 end
|
||||||
|
|
||||||
|
-- Set fill color.
|
||||||
|
self:SetFillColor(FillColor, FillAlpha)
|
||||||
|
|
||||||
|
if #self._.Polygon==4 then
|
||||||
|
|
||||||
|
local Coord2=COORDINATE:NewFromVec2(self._.Polygon[2])
|
||||||
|
local Coord3=COORDINATE:NewFromVec2(self._.Polygon[3])
|
||||||
|
local Coord4=COORDINATE:NewFromVec2(self._.Polygon[4])
|
||||||
|
|
||||||
|
self.DrawID=coordinate:QuadToAll(Coord2, Coord3, Coord4, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly)
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
local Coordinates=self:GetVerticiesCoordinates()
|
||||||
|
table.remove(Coordinates, 1)
|
||||||
|
|
||||||
|
self.DrawID=coordinate:MarkupToAllFreeForm(Coordinates, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1913,7 +2165,7 @@ end
|
|||||||
-- @return #boolean true if the location is within the zone.
|
-- @return #boolean true if the location is within the zone.
|
||||||
function ZONE_POLYGON_BASE:IsVec2InZone( Vec2 )
|
function ZONE_POLYGON_BASE:IsVec2InZone( Vec2 )
|
||||||
self:F2( Vec2 )
|
self:F2( Vec2 )
|
||||||
|
if not Vec2 then return false end
|
||||||
local Next
|
local Next
|
||||||
local Prev
|
local Prev
|
||||||
local InPolygon = false
|
local InPolygon = false
|
||||||
@@ -1937,30 +2189,46 @@ function ZONE_POLYGON_BASE:IsVec2InZone( Vec2 )
|
|||||||
return InPolygon
|
return InPolygon
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Returns if a point is within the zone.
|
||||||
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
|
-- @param DCS#Vec3 Vec3 The point to test.
|
||||||
|
-- @return #boolean true if the point is within the zone.
|
||||||
|
function ZONE_POLYGON_BASE:IsVec3InZone( Vec3 )
|
||||||
|
self:F2( Vec3 )
|
||||||
|
|
||||||
|
if not Vec3 then return false end
|
||||||
|
|
||||||
|
local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } )
|
||||||
|
|
||||||
|
return InZone
|
||||||
|
end
|
||||||
|
|
||||||
--- Define a random @{DCS#Vec2} within the zone.
|
--- Define a random @{DCS#Vec2} within the zone.
|
||||||
-- @param #ZONE_POLYGON_BASE self
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
-- @return DCS#Vec2 The Vec2 coordinate.
|
-- @return DCS#Vec2 The Vec2 coordinate.
|
||||||
function ZONE_POLYGON_BASE:GetRandomVec2()
|
function ZONE_POLYGON_BASE:GetRandomVec2()
|
||||||
self:F2()
|
|
||||||
|
|
||||||
--- It is a bit tricky to find a random point within a polygon. Right now i am doing it the dirty and inefficient way...
|
-- It is a bit tricky to find a random point within a polygon. Right now i am doing it the dirty and inefficient way...
|
||||||
local Vec2Found = false
|
|
||||||
local Vec2
|
-- Get the bounding square.
|
||||||
local BS = self:GetBoundingSquare()
|
local BS = self:GetBoundingSquare()
|
||||||
|
|
||||||
self:T2( BS )
|
local Nmax=1000 ; local n=0
|
||||||
|
while n<Nmax do
|
||||||
|
|
||||||
while Vec2Found == false do
|
-- Random point in the bounding square.
|
||||||
Vec2 = { x = math.random( BS.x1, BS.x2 ), y = math.random( BS.y1, BS.y2 ) }
|
local Vec2={x=math.random(BS.x1, BS.x2), y=math.random(BS.y1, BS.y2)}
|
||||||
self:T2( Vec2 )
|
|
||||||
if self:IsVec2InZone( Vec2 ) then
|
-- Check if this is in the polygon.
|
||||||
Vec2Found = true
|
if self:IsVec2InZone(Vec2) then
|
||||||
|
return Vec2
|
||||||
end
|
end
|
||||||
|
|
||||||
|
n=n+1
|
||||||
end
|
end
|
||||||
|
|
||||||
self:T2( Vec2 )
|
self:E("Could not find a random point in the polygon zone!")
|
||||||
|
return nil
|
||||||
return Vec2
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Return a @{Core.Point#POINT_VEC2} object representing a random 2D point at landheight within the zone.
|
--- Return a @{Core.Point#POINT_VEC2} object representing a random 2D point at landheight within the zone.
|
||||||
@@ -2075,12 +2343,12 @@ end
|
|||||||
--
|
--
|
||||||
-- ## Declare a ZONE_POLYGON directly in the DCS mission editor!
|
-- ## Declare a ZONE_POLYGON directly in the DCS mission editor!
|
||||||
--
|
--
|
||||||
-- You can declare a ZONE_POLYGON using the DCS mission editor by adding the ~ZONE_POLYGON tag in the group name.
|
-- You can declare a ZONE_POLYGON using the DCS mission editor by adding the #ZONE_POLYGON tag in the group name.
|
||||||
--
|
--
|
||||||
-- So, imagine you have a group declared in the mission editor, with group name `DefenseZone~ZONE_POLYGON`.
|
-- So, imagine you have a group declared in the mission editor, with group name `DefenseZone#ZONE_POLYGON`.
|
||||||
-- Then during mission startup, when loading Moose.lua, this group will be detected as a ZONE_POLYGON declaration.
|
-- Then during mission startup, when loading Moose.lua, this group will be detected as a ZONE_POLYGON declaration.
|
||||||
-- Within the background, a ZONE_POLYGON object will be created within the @{Core.Database} using the properties of the group.
|
-- Within the background, a ZONE_POLYGON object will be created within the @{Core.Database} using the properties of the group.
|
||||||
-- The ZONE_POLYGON name will be the group name without the ~ZONE_POLYGON tag.
|
-- The ZONE_POLYGON name will be the group name without the #ZONE_POLYGON tag.
|
||||||
--
|
--
|
||||||
-- So, you can search yourself for the ZONE_POLYGON by using the @{#ZONE_POLYGON.FindByName}() method.
|
-- So, you can search yourself for the ZONE_POLYGON by using the @{#ZONE_POLYGON.FindByName}() method.
|
||||||
-- In this example, `local PolygonZone = ZONE_POLYGON:FindByName( "DefenseZone" )` would return the ZONE_POLYGON object
|
-- In this example, `local PolygonZone = ZONE_POLYGON:FindByName( "DefenseZone" )` would return the ZONE_POLYGON object
|
||||||
@@ -2149,9 +2417,212 @@ function ZONE_POLYGON:FindByName( ZoneName )
|
|||||||
return ZoneFound
|
return ZoneFound
|
||||||
end
|
end
|
||||||
|
|
||||||
|
do -- ZONE_ELASTIC
|
||||||
|
|
||||||
|
--- @type ZONE_ELASTIC
|
||||||
|
-- @field #table points Points in 2D.
|
||||||
|
-- @field #table setGroups Set of GROUPs.
|
||||||
|
-- @field #table setOpsGroups Set of OPSGROUPS.
|
||||||
|
-- @field #table setUnits Set of UNITs.
|
||||||
|
-- @field #number updateID Scheduler ID for updating.
|
||||||
|
-- @extends #ZONE_POLYGON_BASE
|
||||||
|
|
||||||
|
--- The ZONE_ELASTIC class defines a dynamic polygon zone, where only the convex hull is used.
|
||||||
|
--
|
||||||
|
-- @field #ZONE_ELASTIC
|
||||||
|
ZONE_ELASTIC = {
|
||||||
|
ClassName="ZONE_ELASTIC",
|
||||||
|
points={},
|
||||||
|
setGroups={}
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Constructor to create a ZONE_ELASTIC instance.
|
||||||
|
-- @param #ZONE_ELASTIC self
|
||||||
|
-- @param #string ZoneName Name of the zone.
|
||||||
|
-- @param DCS#Vec2 Points (Optional) Fixed points.
|
||||||
|
-- @return #ZONE_ELASTIC self
|
||||||
|
function ZONE_ELASTIC:New(ZoneName, Points)
|
||||||
|
|
||||||
|
local self=BASE:Inherit(self, ZONE_POLYGON_BASE:New(ZoneName, Points)) --#ZONE_ELASTIC
|
||||||
|
|
||||||
|
-- Zone objects are added to the _DATABASE and SET_ZONE objects.
|
||||||
|
_EVENTDISPATCHER:CreateEventNewZone( self )
|
||||||
|
|
||||||
|
if Points then
|
||||||
|
self.points=Points
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Add a vertex (point) to the polygon.
|
||||||
|
-- @param #ZONE_ELASTIC self
|
||||||
|
-- @param DCS#Vec2 Vec2 Point in 2D (with x and y coordinates).
|
||||||
|
-- @return #ZONE_ELASTIC self
|
||||||
|
function ZONE_ELASTIC:AddVertex2D(Vec2)
|
||||||
|
|
||||||
|
-- Add vec2 to points.
|
||||||
|
table.insert(self.points, Vec2)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Add a vertex (point) to the polygon.
|
||||||
|
-- @param #ZONE_ELASTIC self
|
||||||
|
-- @param DCS#Vec3 Vec3 Point in 3D (with x, y and z coordinates). Only the x and z coordinates are used.
|
||||||
|
-- @return #ZONE_ELASTIC self
|
||||||
|
function ZONE_ELASTIC:AddVertex3D(Vec3)
|
||||||
|
|
||||||
|
-- Add vec2 from vec3 to points.
|
||||||
|
table.insert(self.points, {x=Vec3.x, y=Vec3.z})
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Add a set of groups. Positions of the group will be considered as polygon vertices when contructing the convex hull.
|
||||||
|
-- @param #ZONE_ELASTIC self
|
||||||
|
-- @param Core.Set#SET_GROUP SetGroup Set of groups.
|
||||||
|
-- @return #ZONE_ELASTIC self
|
||||||
|
function ZONE_ELASTIC:AddSetGroup(GroupSet)
|
||||||
|
|
||||||
|
-- Add set to table.
|
||||||
|
table.insert(self.setGroups, GroupSet)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Update the convex hull of the polygon.
|
||||||
|
-- This uses the [Graham scan](https://en.wikipedia.org/wiki/Graham_scan).
|
||||||
|
-- @param #ZONE_ELASTIC self
|
||||||
|
-- @param #number Delay Delay in seconds before the zone is updated. Default 0.
|
||||||
|
-- @param #boolean Draw Draw the zone. Default `nil`.
|
||||||
|
-- @return #ZONE_ELASTIC self
|
||||||
|
function ZONE_ELASTIC:Update(Delay, Draw)
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
self:T(string.format("Updating ZONE_ELASTIC %s", tostring(self.ZoneName)))
|
||||||
|
|
||||||
|
-- Copy all points.
|
||||||
|
local points=UTILS.DeepCopy(self.points or {})
|
||||||
|
|
||||||
|
if self.setGroups then
|
||||||
|
for _,_setGroup in pairs(self.setGroups) do
|
||||||
|
local setGroup=_setGroup --Core.Set#SET_GROUP
|
||||||
|
for _,_group in pairs(setGroup.Set) do
|
||||||
|
local group=_group --Wrapper.Group#GROUP
|
||||||
|
if group and group:IsAlive() then
|
||||||
|
table.insert(points, group:GetVec2())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Update polygon verticies from points.
|
||||||
|
self._.Polygon=self:_ConvexHull(points)
|
||||||
|
|
||||||
|
if Draw~=false then
|
||||||
|
if self.DrawID or Draw==true then
|
||||||
|
self:UndrawZone()
|
||||||
|
self:DrawZone()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Start the updating scheduler.
|
||||||
|
-- @param #ZONE_ELASTIC self
|
||||||
|
-- @param #number Tstart Time in seconds before the updating starts.
|
||||||
|
-- @param #number dT Time interval in seconds between updates. Default 60 sec.
|
||||||
|
-- @param #number Tstop Time in seconds after which the updating stops. Default `nil`.
|
||||||
|
-- @param #boolean Draw Draw the zone. Default `nil`.
|
||||||
|
-- @return #ZONE_ELASTIC self
|
||||||
|
function ZONE_ELASTIC:StartUpdate(Tstart, dT, Tstop, Draw)
|
||||||
|
|
||||||
|
self.updateID=self:ScheduleRepeat(Tstart, dT, 0, Tstop, ZONE_ELASTIC.Update, self, 0, Draw)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Stop the updating scheduler.
|
||||||
|
-- @param #ZONE_ELASTIC self
|
||||||
|
-- @param #number Delay Delay in seconds before the scheduler will be stopped. Default 0.
|
||||||
|
-- @return #ZONE_ELASTIC self
|
||||||
|
function ZONE_ELASTIC:StopUpdate(Delay)
|
||||||
|
|
||||||
|
if Delay and Delay>0 then
|
||||||
|
self:ScheduleOnce(Delay, ZONE_ELASTIC.StopUpdate, self)
|
||||||
|
else
|
||||||
|
|
||||||
|
if self.updateID then
|
||||||
|
|
||||||
|
self:ScheduleStop(self.updateID)
|
||||||
|
|
||||||
|
self.updateID=nil
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Create a convec hull.
|
||||||
|
-- @param #ZONE_ELASTIC self
|
||||||
|
-- @param #table pl Points
|
||||||
|
-- @return #table Points
|
||||||
|
function ZONE_ELASTIC:_ConvexHull(pl)
|
||||||
|
|
||||||
|
if #pl == 0 then
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
|
||||||
|
table.sort(pl, function(left,right)
|
||||||
|
return left.x < right.x
|
||||||
|
end)
|
||||||
|
|
||||||
|
local h = {}
|
||||||
|
|
||||||
|
-- Function: ccw > 0 if three points make a counter-clockwise turn, clockwise if ccw < 0, and collinear if ccw = 0.
|
||||||
|
local function ccw(a,b,c)
|
||||||
|
return (b.x - a.x) * (c.y - a.y) > (b.y - a.y) * (c.x - a.x)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- lower hull
|
||||||
|
for i,pt in pairs(pl) do
|
||||||
|
while #h >= 2 and not ccw(h[#h-1], h[#h], pt) do
|
||||||
|
table.remove(h,#h)
|
||||||
|
end
|
||||||
|
table.insert(h,pt)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- upper hull
|
||||||
|
local t = #h + 1
|
||||||
|
for i=#pl, 1, -1 do
|
||||||
|
local pt = pl[i]
|
||||||
|
while #h >= t and not ccw(h[#h-1], h[#h], pt) do
|
||||||
|
table.remove(h, #h)
|
||||||
|
end
|
||||||
|
table.insert(h, pt)
|
||||||
|
end
|
||||||
|
|
||||||
|
table.remove(h, #h)
|
||||||
|
|
||||||
|
return h
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
do -- ZONE_AIRBASE
|
do -- ZONE_AIRBASE
|
||||||
|
|
||||||
--- @type ZONE_AIRBASE
|
--- @type ZONE_AIRBASE
|
||||||
|
-- @field #boolean isShip If `true`, airbase is a ship.
|
||||||
|
-- @field #boolean isHelipad If `true`, airbase is a helipad.
|
||||||
|
-- @field #boolean isAirdrome If `true`, airbase is an airdrome.
|
||||||
-- @extends #ZONE_RADIUS
|
-- @extends #ZONE_RADIUS
|
||||||
|
|
||||||
|
|
||||||
@@ -2180,6 +2651,20 @@ do -- ZONE_AIRBASE
|
|||||||
|
|
||||||
self._.ZoneAirbase = Airbase
|
self._.ZoneAirbase = Airbase
|
||||||
self._.ZoneVec2Cache = self._.ZoneAirbase:GetVec2()
|
self._.ZoneVec2Cache = self._.ZoneAirbase:GetVec2()
|
||||||
|
|
||||||
|
if Airbase:IsShip() then
|
||||||
|
self.isShip=true
|
||||||
|
self.isHelipad=false
|
||||||
|
self.isAirdrome=false
|
||||||
|
elseif Airbase:IsHelipad() then
|
||||||
|
self.isShip=false
|
||||||
|
self.isHelipad=true
|
||||||
|
self.isAirdrome=false
|
||||||
|
elseif Airbase:IsAirdrome() then
|
||||||
|
self.isShip=false
|
||||||
|
self.isHelipad=false
|
||||||
|
self.isAirdrome=true
|
||||||
|
end
|
||||||
|
|
||||||
-- Zone objects are added to the _DATABASE and SET_ZONE objects.
|
-- Zone objects are added to the _DATABASE and SET_ZONE objects.
|
||||||
_EVENTDISPATCHER:CreateEventNewZone( self )
|
_EVENTDISPATCHER:CreateEventNewZone( self )
|
||||||
@@ -2194,9 +2679,9 @@ do -- ZONE_AIRBASE
|
|||||||
return self._.ZoneAirbase
|
return self._.ZoneAirbase
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns the current location of the @{Wrapper.Group}.
|
--- Returns the current location of the AIRBASE.
|
||||||
-- @param #ZONE_AIRBASE self
|
-- @param #ZONE_AIRBASE self
|
||||||
-- @return DCS#Vec2 The location of the zone based on the @{Wrapper.Group} location.
|
-- @return DCS#Vec2 The location of the zone based on the AIRBASE location.
|
||||||
function ZONE_AIRBASE:GetVec2()
|
function ZONE_AIRBASE:GetVec2()
|
||||||
self:F( self.ZoneName )
|
self:F( self.ZoneName )
|
||||||
|
|
||||||
@@ -2214,24 +2699,6 @@ do -- ZONE_AIRBASE
|
|||||||
return ZoneVec2
|
return ZoneVec2
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns a random location within the zone of the @{Wrapper.Group}.
|
|
||||||
-- @param #ZONE_AIRBASE self
|
|
||||||
-- @return DCS#Vec2 The random location of the zone based on the @{Wrapper.Group} location.
|
|
||||||
function ZONE_AIRBASE:GetRandomVec2()
|
|
||||||
self:F( self.ZoneName )
|
|
||||||
|
|
||||||
local Point = {}
|
|
||||||
local Vec2 = self._.ZoneAirbase:GetVec2()
|
|
||||||
|
|
||||||
local angle = math.random() * math.pi*2;
|
|
||||||
Point.x = Vec2.x + math.cos( angle ) * math.random() * self:GetRadius();
|
|
||||||
Point.y = Vec2.y + math.sin( angle ) * math.random() * self:GetRadius();
|
|
||||||
|
|
||||||
self:T( { Point } )
|
|
||||||
|
|
||||||
return Point
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone.
|
--- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone.
|
||||||
-- @param #ZONE_AIRBASE self
|
-- @param #ZONE_AIRBASE self
|
||||||
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0.
|
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0.
|
||||||
|
|||||||
@@ -471,6 +471,22 @@ do -- Types
|
|||||||
--@field #boolean lateActivated
|
--@field #boolean lateActivated
|
||||||
--@field #boolean uncontrolled
|
--@field #boolean uncontrolled
|
||||||
|
|
||||||
|
--- DCS template data structure.
|
||||||
|
-- @type Template
|
||||||
|
-- @field #boolean uncontrolled Aircraft is uncontrolled.
|
||||||
|
-- @field #boolean lateActivation Group is late activated.
|
||||||
|
-- @field #number x 2D Position on x-axis in meters.
|
||||||
|
-- @field #number y 2D Position on y-axis in meters.
|
||||||
|
-- @field #table units Unit list.
|
||||||
|
--
|
||||||
|
|
||||||
|
--- Unit data structure.
|
||||||
|
--@type Template.Unit
|
||||||
|
--@field #string name Name of the unit.
|
||||||
|
--@field #number x
|
||||||
|
--@field #number y
|
||||||
|
--@field #number alt
|
||||||
|
|
||||||
end --
|
end --
|
||||||
|
|
||||||
|
|
||||||
@@ -487,8 +503,9 @@ do -- Object
|
|||||||
-- @field UNIT
|
-- @field UNIT
|
||||||
-- @field WEAPON
|
-- @field WEAPON
|
||||||
-- @field STATIC
|
-- @field STATIC
|
||||||
-- @field SCENERY
|
|
||||||
-- @field BASE
|
-- @field BASE
|
||||||
|
-- @field SCENERY
|
||||||
|
-- @field CARGO
|
||||||
|
|
||||||
--- @type Object.Desc
|
--- @type Object.Desc
|
||||||
-- @extends #Desc
|
-- @extends #Desc
|
||||||
@@ -498,6 +515,10 @@ do -- Object
|
|||||||
--- @function [parent=#Object] isExist
|
--- @function [parent=#Object] isExist
|
||||||
-- @param #Object self
|
-- @param #Object self
|
||||||
-- @return #boolean
|
-- @return #boolean
|
||||||
|
|
||||||
|
--- @function [parent=#Object] isActive
|
||||||
|
-- @param #Object self
|
||||||
|
-- @return #boolean
|
||||||
|
|
||||||
--- @function [parent=#Object] destroy
|
--- @function [parent=#Object] destroy
|
||||||
-- @param #Object self
|
-- @param #Object self
|
||||||
@@ -1130,6 +1151,11 @@ do -- Unit
|
|||||||
-- @function [parent=#Unit] getAmmo
|
-- @function [parent=#Unit] getAmmo
|
||||||
-- @param #Unit self
|
-- @param #Unit self
|
||||||
-- @return #Unit.Ammo
|
-- @return #Unit.Ammo
|
||||||
|
|
||||||
|
--- Returns the number of infantry that can be embark onto the aircraft. Only returns a value if run on airplanes or helicopters. Returns nil if run on ground or ship units.
|
||||||
|
-- @function [parent=#Unit] getDescentCapacity
|
||||||
|
-- @param #Unit self
|
||||||
|
-- @return #number Number of soldiers that embark.
|
||||||
|
|
||||||
--- Returns the unit sensors.
|
--- Returns the unit sensors.
|
||||||
-- @function [parent=#Unit] getSensors
|
-- @function [parent=#Unit] getSensors
|
||||||
@@ -1348,7 +1374,9 @@ do -- AI
|
|||||||
--- @type AI.Option.Ground
|
--- @type AI.Option.Ground
|
||||||
-- @field #AI.Option.Ground.id id
|
-- @field #AI.Option.Ground.id id
|
||||||
-- @field #AI.Option.Ground.val val
|
-- @field #AI.Option.Ground.val val
|
||||||
|
-- @field #AI.Option.Ground.mid mid
|
||||||
|
-- @field #AI.Option.Ground.mval mval
|
||||||
|
--
|
||||||
--- @type AI.Option.Naval
|
--- @type AI.Option.Naval
|
||||||
-- @field #AI.Option.Naval.id id
|
-- @field #AI.Option.Naval.id id
|
||||||
-- @field #AI.Option.Naval.val val
|
-- @field #AI.Option.Naval.val val
|
||||||
@@ -1371,6 +1399,11 @@ do -- AI
|
|||||||
-- @field PROHIBIT_AG
|
-- @field PROHIBIT_AG
|
||||||
-- @field MISSILE_ATTACK
|
-- @field MISSILE_ATTACK
|
||||||
-- @field PROHIBIT_WP_PASS_REPORT
|
-- @field PROHIBIT_WP_PASS_REPORT
|
||||||
|
-- @field OPTION_RADIO_USAGE_CONTACT
|
||||||
|
-- @field OPTION_RADIO_USAGE_ENGAGE
|
||||||
|
-- @field OPTION_RADIO_USAGE_KILL
|
||||||
|
-- @field JETT_TANKS_IF_EMPTY
|
||||||
|
-- @field FORCED_ATTACK
|
||||||
|
|
||||||
--- @type AI.Option.Air.id.FORMATION
|
--- @type AI.Option.Air.id.FORMATION
|
||||||
-- @field LINE_ABREAST
|
-- @field LINE_ABREAST
|
||||||
@@ -1440,19 +1473,35 @@ do -- AI
|
|||||||
--- @type AI.Option.Ground.id
|
--- @type AI.Option.Ground.id
|
||||||
-- @field NO_OPTION
|
-- @field NO_OPTION
|
||||||
-- @field ROE @{#AI.Option.Ground.val.ROE}
|
-- @field ROE @{#AI.Option.Ground.val.ROE}
|
||||||
|
-- @field FORMATION
|
||||||
-- @field DISPERSE_ON_ATTACK true or false
|
-- @field DISPERSE_ON_ATTACK true or false
|
||||||
-- @field ALARM_STATE @{#AI.Option.Ground.val.ALARM_STATE}
|
-- @field ALARM_STATE @{#AI.Option.Ground.val.ALARM_STATE}
|
||||||
-- @field ENGAGE_AIR_WEAPONS
|
-- @field ENGAGE_AIR_WEAPONS
|
||||||
|
-- @field AC_ENGAGEMENT_RANGE_RESTRICTION
|
||||||
|
|
||||||
|
--- @type AI.Option.Ground.mid -- Moose added
|
||||||
|
-- @field RESTRICT_AAA_MIN 27
|
||||||
|
-- @field RESTRICT_AAA_MAX 29
|
||||||
|
-- @field RESTRICT_TARGETS @{#AI.Option.Ground.mval.ENGAGE_TARGETS} 28
|
||||||
|
|
||||||
--- @type AI.Option.Ground.val
|
--- @type AI.Option.Ground.val
|
||||||
-- @field #AI.Option.Ground.val.ROE ROE
|
-- @field #AI.Option.Ground.val.ROE ROE
|
||||||
-- @field #AI.Option.Ground.val.ALARM_STATE ALARM_STATE
|
-- @field #AI.Option.Ground.val.ALARM_STATE ALARM_STATE
|
||||||
|
-- @field #AI.Option.Ground.val.ENGAGE_TARGETS RESTRICT_TARGETS
|
||||||
|
|
||||||
--- @type AI.Option.Ground.val.ROE
|
--- @type AI.Option.Ground.val.ROE
|
||||||
-- @field OPEN_FIRE
|
-- @field OPEN_FIRE
|
||||||
-- @field RETURN_FIRE
|
-- @field RETURN_FIRE
|
||||||
-- @field WEAPON_HOLD
|
-- @field WEAPON_HOLD
|
||||||
|
|
||||||
|
--- @type AI.Option.Ground.mval -- Moose added
|
||||||
|
-- @field #AI.Option.Ground.mval.ENGAGE_TARGETS ENGAGE_TARGETS
|
||||||
|
|
||||||
|
--- @type AI.Option.Ground.mval.ENGAGE_TARGETS -- Moose added
|
||||||
|
-- @field ANY_TARGET -- 0
|
||||||
|
-- @field AIR_UNITS_ONLY -- 1
|
||||||
|
-- @field GROUND_UNITS_ONLY -- 2
|
||||||
|
|
||||||
--- @type AI.Option.Ground.val.ALARM_STATE
|
--- @type AI.Option.Ground.val.ALARM_STATE
|
||||||
-- @field AUTO
|
-- @field AUTO
|
||||||
-- @field GREEN
|
-- @field GREEN
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -73,7 +73,7 @@
|
|||||||
-- @field Core.Point#COORDINATE RearmingPlaceCoord Coordinates of the rearming place. If the place is more than 100 m away from the ARTY group, the group will go there.
|
-- @field Core.Point#COORDINATE RearmingPlaceCoord Coordinates of the rearming place. If the place is more than 100 m away from the ARTY group, the group will go there.
|
||||||
-- @field #boolean RearmingArtyOnRoad If true, ARTY group will move to rearming place using mainly roads. Default false.
|
-- @field #boolean RearmingArtyOnRoad If true, ARTY group will move to rearming place using mainly roads. Default false.
|
||||||
-- @field Core.Point#COORDINATE InitialCoord Initial coordinates of the ARTY group.
|
-- @field Core.Point#COORDINATE InitialCoord Initial coordinates of the ARTY group.
|
||||||
-- @field #boolean report Arty group sends messages about their current state or target to its coaliton.
|
-- @field #boolean report Arty group sends messages about their current state or target to its coalition.
|
||||||
-- @field #table ammoshells Table holding names of the shell types which are included when counting the ammo. Default is {"weapons.shells"} which include most shells.
|
-- @field #table ammoshells Table holding names of the shell types which are included when counting the ammo. Default is {"weapons.shells"} which include most shells.
|
||||||
-- @field #table ammorockets Table holding names of the rocket types which are included when counting the ammo. Default is {"weapons.nurs"} which includes most unguided rockets.
|
-- @field #table ammorockets Table holding names of the rocket types which are included when counting the ammo. Default is {"weapons.nurs"} which includes most unguided rockets.
|
||||||
-- @field #table ammomissiles Table holding names of the missile types which are included when counting the ammo. Default is {"weapons.missiles"} which includes some guided missiles.
|
-- @field #table ammomissiles Table holding names of the missile types which are included when counting the ammo. Default is {"weapons.missiles"} which includes some guided missiles.
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ do -- DESIGNATE
|
|||||||
-- In order to prevent an overflow of designations due to many Detected Targets, there is a
|
-- In order to prevent an overflow of designations due to many Detected Targets, there is a
|
||||||
-- Maximum Designations scope that is set in the DesignationObject.
|
-- Maximum Designations scope that is set in the DesignationObject.
|
||||||
--
|
--
|
||||||
-- The method @{#DESIGNATE.SetMaximumDesignations}() will put a limit on the amount of designations put in scope of the DesignationObject.
|
-- The method @{#DESIGNATE.SetMaximumDesignations}() will put a limit on the amount of designations (target groups) put in scope of the DesignationObject.
|
||||||
-- Using the menu system, the player can "forget" a designation, so that gradually a new designation can be put in scope when detected.
|
-- Using the menu system, the player can "forget" a designation, so that gradually a new designation can be put in scope when detected.
|
||||||
--
|
--
|
||||||
-- # 4. Laser codes
|
-- # 4. Laser codes
|
||||||
@@ -562,7 +562,8 @@ do -- DESIGNATE
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Set the maximum amount of designations.
|
--- Set the maximum amount of designations (target groups). This will put a limit on the amount of designations in scope.
|
||||||
|
-- Using the menu system, the player can "forget" a designation, so that gradually a new designation can be put in scope when detected.
|
||||||
-- @param #DESIGNATE self
|
-- @param #DESIGNATE self
|
||||||
-- @param #number MaximumDesignations
|
-- @param #number MaximumDesignations
|
||||||
-- @return #DESIGNATE
|
-- @return #DESIGNATE
|
||||||
@@ -602,7 +603,7 @@ do -- DESIGNATE
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Set the maximum amount of markings FACs will do, per designated target group.
|
--- Set the maximum amount of markings FACs will do, per designated target group. This will limit the number of parallelly marked units of a target group.
|
||||||
-- @param #DESIGNATE self
|
-- @param #DESIGNATE self
|
||||||
-- @param #number MaximumMarkings Maximum markings FACs will do, per designated target group.
|
-- @param #number MaximumMarkings Maximum markings FACs will do, per designated target group.
|
||||||
-- @return #DESIGNATE
|
-- @return #DESIGNATE
|
||||||
@@ -911,8 +912,8 @@ do -- DESIGNATE
|
|||||||
for DesignateIndex, Designating in pairs( self.Designating ) do
|
for DesignateIndex, Designating in pairs( self.Designating ) do
|
||||||
local DetectedItem = DetectedItems[DesignateIndex]
|
local DetectedItem = DetectedItems[DesignateIndex]
|
||||||
if DetectedItem then
|
if DetectedItem then
|
||||||
local Report = self.Detection:DetectedItemReportSummary( DetectedItem, AttackGroup ):Text( ", " )
|
local Report = self.Detection:DetectedItemReportSummary( DetectedItem, AttackGroup, nil, true ):Text( ", " )
|
||||||
DetectedReport:Add( string.rep( "-", 140 ) )
|
DetectedReport:Add( string.rep( "-", 40 ) )
|
||||||
DetectedReport:Add( " - " .. Report )
|
DetectedReport:Add( " - " .. Report )
|
||||||
if string.find( Designating, "L" ) then
|
if string.find( Designating, "L" ) then
|
||||||
DetectedReport:Add( " - " .. "Lasing Targets" )
|
DetectedReport:Add( " - " .. "Lasing Targets" )
|
||||||
@@ -1192,8 +1193,8 @@ do -- DESIGNATE
|
|||||||
|
|
||||||
local MarkingCount = 0
|
local MarkingCount = 0
|
||||||
local MarkedTypes = {}
|
local MarkedTypes = {}
|
||||||
local ReportTypes = REPORT:New()
|
--local ReportTypes = REPORT:New()
|
||||||
local ReportLaserCodes = REPORT:New()
|
--local ReportLaserCodes = REPORT:New()
|
||||||
|
|
||||||
TargetSetUnit:Flush( self )
|
TargetSetUnit:Flush( self )
|
||||||
|
|
||||||
@@ -1243,8 +1244,8 @@ do -- DESIGNATE
|
|||||||
if not Recce then
|
if not Recce then
|
||||||
|
|
||||||
self:F( "Lasing..." )
|
self:F( "Lasing..." )
|
||||||
self.RecceSet:Flush( self)
|
--self.RecceSet:Flush( self)
|
||||||
|
|
||||||
for RecceGroupID, RecceGroup in pairs( self.RecceSet:GetSet() ) do
|
for RecceGroupID, RecceGroup in pairs( self.RecceSet:GetSet() ) do
|
||||||
for UnitID, UnitData in pairs( RecceGroup:GetUnits() or {} ) do
|
for UnitID, UnitData in pairs( RecceGroup:GetUnits() or {} ) do
|
||||||
|
|
||||||
@@ -1282,13 +1283,13 @@ do -- DESIGNATE
|
|||||||
-- OK. We have assigned for the Recce a TargetUnit. We can exit the function.
|
-- OK. We have assigned for the Recce a TargetUnit. We can exit the function.
|
||||||
MarkingCount = MarkingCount + 1
|
MarkingCount = MarkingCount + 1
|
||||||
local TargetUnitType = TargetUnit:GetTypeName()
|
local TargetUnitType = TargetUnit:GetTypeName()
|
||||||
--RecceUnit:MessageToSetGroup( "Marking " .. TargetUnit:GetTypeName() .. " with laser " .. RecceUnit:GetSpot().LaserCode .. " for " .. Duration .. "s.",
|
RecceUnit:MessageToSetGroup( "Marking " .. TargetUnit:GetTypeName() .. " with laser " .. RecceUnit:GetSpot().LaserCode .. " for " .. Duration .. "s.",
|
||||||
-- 5, self.AttackSet, DesignateName )
|
10, self.AttackSet, DesignateName )
|
||||||
if not MarkedTypes[TargetUnitType] then
|
if not MarkedTypes[TargetUnitType] then
|
||||||
MarkedTypes[TargetUnitType] = true
|
MarkedTypes[TargetUnitType] = true
|
||||||
ReportTypes:Add(TargetUnitType)
|
--ReportTypes:Add(TargetUnitType)
|
||||||
end
|
end
|
||||||
ReportLaserCodes:Add(RecceUnit.LaserCode)
|
--ReportLaserCodes:Add(RecceUnit.LaserCode)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@@ -1303,16 +1304,16 @@ do -- DESIGNATE
|
|||||||
|
|
||||||
if Recce then
|
if Recce then
|
||||||
Recce:LaseOff()
|
Recce:LaseOff()
|
||||||
Recce:MessageToSetGroup( "Target " .. TargetUnit:GetTypeName() "out of LOS. Cancelling lase!", 5, self.AttackSet, self.DesignateName )
|
Recce:MessageToSetGroup( "Target " .. TargetUnit:GetTypeName() "out of LOS. Cancelling lase!", 10, self.AttackSet, self.DesignateName )
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
--MarkingCount = MarkingCount + 1
|
--MarkingCount = MarkingCount + 1
|
||||||
local TargetUnitType = TargetUnit:GetTypeName()
|
local TargetUnitType = TargetUnit:GetTypeName()
|
||||||
if not MarkedTypes[TargetUnitType] then
|
if not MarkedTypes[TargetUnitType] then
|
||||||
MarkedTypes[TargetUnitType] = true
|
MarkedTypes[TargetUnitType] = true
|
||||||
ReportTypes:Add(TargetUnitType)
|
--ReportTypes:Add(TargetUnitType)
|
||||||
end
|
end
|
||||||
ReportLaserCodes:Add(RecceUnit.LaserCode)
|
--ReportLaserCodes:Add(RecceUnit.LaserCode)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -1322,19 +1323,19 @@ do -- DESIGNATE
|
|||||||
local TargetUnitType = TargetUnit:GetTypeName()
|
local TargetUnitType = TargetUnit:GetTypeName()
|
||||||
if not MarkedTypes[TargetUnitType] then
|
if not MarkedTypes[TargetUnitType] then
|
||||||
MarkedTypes[TargetUnitType] = true
|
MarkedTypes[TargetUnitType] = true
|
||||||
ReportTypes:Add(TargetUnitType)
|
--ReportTypes:Add(TargetUnitType)
|
||||||
end
|
end
|
||||||
ReportLaserCodes:Add(Recce.LaserCode)
|
--ReportLaserCodes:Add(Recce.LaserCode)
|
||||||
--Recce:MessageToSetGroup( self.DesignateName .. ": Marking " .. TargetUnit:GetTypeName() .. " with laser " .. Recce.LaserCode .. ".", 5, self.AttackSet )
|
Recce:MessageToSetGroup( self.DesignateName .. ": Marking " .. TargetUnit:GetTypeName() .. " with laser " .. Recce.LaserCode .. ".", 10, self.AttackSet )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
)
|
)
|
||||||
|
|
||||||
local MarkedTypesText = ReportTypes:Text(', ')
|
--local MarkedTypesText = ReportTypes:Text(', ')
|
||||||
local MarkedLaserCodesText = ReportLaserCodes:Text(', ')
|
--local MarkedLaserCodesText = ReportLaserCodes:Text(', ')
|
||||||
self.CC:GetPositionable():MessageToSetGroup( "Marking " .. MarkingCount .. " x " .. MarkedTypesText .. ", code " .. MarkedLaserCodesText .. ".", 5, self.AttackSet, self.DesignateName )
|
--self.CC:GetPositionable():MessageToSetGroup( "Marking " .. MarkingCount .. " x " .. MarkedTypesText .. ", code " .. MarkedLaserCodesText .. ".", 5, self.AttackSet, self.DesignateName )
|
||||||
|
|
||||||
self:__Lasing( -self.LaseDuration, Index, Duration, LaserCodeRequested )
|
self:__Lasing( -self.LaseDuration, Index, Duration, LaserCodeRequested )
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -23,7 +23,6 @@
|
|||||||
-- @module Functional.FOX
|
-- @module Functional.FOX
|
||||||
-- @image Functional_FOX.png
|
-- @image Functional_FOX.png
|
||||||
|
|
||||||
|
|
||||||
--- FOX class.
|
--- FOX class.
|
||||||
-- @type FOX
|
-- @type FOX
|
||||||
-- @field #string ClassName Name of the class.
|
-- @field #string ClassName Name of the class.
|
||||||
@@ -47,8 +46,7 @@
|
|||||||
-- @field #number dt10 Time step [sec] for missile position updates if distance to target > 10 km and < 50 km. Default 1 sec.
|
-- @field #number dt10 Time step [sec] for missile position updates if distance to target > 10 km and < 50 km. Default 1 sec.
|
||||||
-- @field #number dt05 Time step [sec] for missile position updates if distance to target > 5 km and < 10 km. Default 0.5 sec.
|
-- @field #number dt05 Time step [sec] for missile position updates if distance to target > 5 km and < 10 km. Default 0.5 sec.
|
||||||
-- @field #number dt01 Time step [sec] for missile position updates if distance to target > 1 km and < 5 km. Default 0.1 sec.
|
-- @field #number dt01 Time step [sec] for missile position updates if distance to target > 1 km and < 5 km. Default 0.1 sec.
|
||||||
-- @field #number dt00 Time step [sec] for missile position updates if distance to target < 1 km. Default 0.01 sec.
|
-- @field #number dt00 Time step [sec] for missile position updates if distance to target < 1 km. Default 0.01 sec.
|
||||||
-- @field #boolean
|
|
||||||
-- @extends Core.Fsm#FSM
|
-- @extends Core.Fsm#FSM
|
||||||
|
|
||||||
--- Fox 3!
|
--- Fox 3!
|
||||||
@@ -794,7 +792,7 @@ function FOX:onafterMissileLaunch(From, Event, To, missile)
|
|||||||
local text=string.format("Missile launch detected! Distance %.1f NM, bearing %03d°.", UTILS.MetersToNM(distance), bearing)
|
local text=string.format("Missile launch detected! Distance %.1f NM, bearing %03d°.", UTILS.MetersToNM(distance), bearing)
|
||||||
|
|
||||||
-- Say notching headings.
|
-- Say notching headings.
|
||||||
BASE:ScheduleOnce(5, FOX._SayNotchingHeadings, self, player, missile.weapon)
|
self:ScheduleOnce(5, FOX._SayNotchingHeadings, self, player, missile.weapon)
|
||||||
|
|
||||||
--TODO: ALERT or INFO depending on whether this is a direct target.
|
--TODO: ALERT or INFO depending on whether this is a direct target.
|
||||||
--TODO: lauchalertall option.
|
--TODO: lauchalertall option.
|
||||||
@@ -1116,6 +1114,13 @@ end
|
|||||||
-- Event Functions
|
-- Event Functions
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- FOX event handler for event birth.
|
||||||
|
-- @param #FOX self
|
||||||
|
-- @param Core.Event#EVENTDATA EventData
|
||||||
|
function FOX:OnEventPlayerEnterAircraft(EventData)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
--- FOX event handler for event birth.
|
--- FOX event handler for event birth.
|
||||||
-- @param #FOX self
|
-- @param #FOX self
|
||||||
-- @param Core.Event#EVENTDATA EventData
|
-- @param Core.Event#EVENTDATA EventData
|
||||||
@@ -1157,7 +1162,7 @@ function FOX:OnEventBirth(EventData)
|
|||||||
|
|
||||||
-- Add F10 radio menu for player.
|
-- Add F10 radio menu for player.
|
||||||
if not self.menudisabled then
|
if not self.menudisabled then
|
||||||
SCHEDULER:New(nil, self._AddF10Commands, {self,_unitName}, 0.1)
|
self:ScheduleOnce(0.1, FOX._AddF10Commands, self, _unitname)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Player data.
|
-- Player data.
|
||||||
@@ -1813,4 +1818,4 @@ end
|
|||||||
|
|
||||||
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,28 +1,28 @@
|
|||||||
--- **Functional** -- Train missile defence and deflection.
|
--- **Functional** -- Train missile defence and deflection.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ## Features:
|
-- ## Features:
|
||||||
--
|
--
|
||||||
-- * Track the missiles fired at you and other players, providing bearing and range information of the missiles towards the airplanes.
|
-- * Track the missiles fired at you and other players, providing bearing and range information of the missiles towards the airplanes.
|
||||||
-- * Provide alerts of missile launches, including detailed information of the units launching, including bearing, range °
|
-- * Provide alerts of missile launches, including detailed information of the units launching, including bearing, range
|
||||||
-- * Provide alerts when a missile would have killed your aircraft.
|
-- * Provide alerts when a missile would have killed your aircraft.
|
||||||
-- * Provide alerts when the missile self destructs.
|
-- * Provide alerts when the missile self destructs.
|
||||||
-- * Enable / Disable and Configure the Missile Trainer using the various menu options.
|
-- * Enable / Disable and Configure the Missile Trainer using the various menu options.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ## Missions:
|
-- ## Missions:
|
||||||
--
|
--
|
||||||
-- [MIT - Missile Trainer](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/MIT%20-%20Missile%20Trainer)
|
-- [MIT - Missile Trainer](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/MIT%20-%20Missile%20Trainer)
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- Uses the MOOSE messaging system to be alerted of any missiles fired, and when a missile would hit your aircraft,
|
-- Uses the MOOSE messaging system to be alerted of any missiles fired, and when a missile would hit your aircraft,
|
||||||
-- the class will destroy the missile within a certain range, to avoid damage to your aircraft.
|
-- the class will destroy the missile within a certain range, to avoid damage to your aircraft.
|
||||||
--
|
--
|
||||||
-- When running a mission where the missile trainer is used, the following radio menu structure ( 'Radio Menu' -> 'Other (F10)' -> 'MissileTrainer' ) options are available for the players:
|
-- When running a mission where the missile trainer is used, the following radio menu structure ( 'Radio Menu' -> 'Other (F10)' -> 'MissileTrainer' ) options are available for the players:
|
||||||
--
|
--
|
||||||
-- * **Messages**: Menu to configure all messages.
|
-- * **Messages**: Menu to configure all messages.
|
||||||
-- * **Messages On**: Show all messages.
|
-- * **Messages On**: Show all messages.
|
||||||
-- * **Messages Off**: Disable all messages.
|
-- * **Messages Off**: Disable all messages.
|
||||||
@@ -45,23 +45,23 @@
|
|||||||
-- * **Range Off**: Disable range information when a missile is fired to a target.
|
-- * **Range Off**: Disable range information when a missile is fired to a target.
|
||||||
-- * **Bearing On**: Shows bearing information when a missile is fired to a target.
|
-- * **Bearing On**: Shows bearing information when a missile is fired to a target.
|
||||||
-- * **Bearing Off**: Disable bearing information when a missile is fired to a target.
|
-- * **Bearing Off**: Disable bearing information when a missile is fired to a target.
|
||||||
-- * **Distance**: Menu to configure the distance when a missile needs to be destroyed when near to a player, during tracking. This will improve/influence hit calculation accuracy, but has the risk of damaging the aircraft when the missile reaches the aircraft before the distance is measured.
|
-- * **Distance**: Menu to configure the distance when a missile needs to be destroyed when near to a player, during tracking. This will improve/influence hit calculation accuracy, but has the risk of damaging the aircraft when the missile reaches the aircraft before the distance is measured.
|
||||||
-- * **50 meter**: Destroys the missile when the distance to the aircraft is below or equal to 50 meter.
|
-- * **50 meter**: Destroys the missile when the distance to the aircraft is below or equal to 50 meter.
|
||||||
-- * **100 meter**: Destroys the missile when the distance to the aircraft is below or equal to 100 meter.
|
-- * **100 meter**: Destroys the missile when the distance to the aircraft is below or equal to 100 meter.
|
||||||
-- * **150 meter**: Destroys the missile when the distance to the aircraft is below or equal to 150 meter.
|
-- * **150 meter**: Destroys the missile when the distance to the aircraft is below or equal to 150 meter.
|
||||||
-- * **200 meter**: Destroys the missile when the distance to the aircraft is below or equal to 200 meter.
|
-- * **200 meter**: Destroys the missile when the distance to the aircraft is below or equal to 200 meter.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ### Authors: **FlightControl**
|
-- ### Authors: **FlightControl**
|
||||||
--
|
--
|
||||||
-- ### Contributions:
|
-- ### Contributions:
|
||||||
--
|
--
|
||||||
-- * **Stuka (Danny)**: Who you can search on the Eagle Dynamics Forums. Working together with Danny has resulted in the MISSILETRAINER class.
|
-- * **Stuka (Danny)**: Who you can search on the Eagle Dynamics Forums. Working together with Danny has resulted in the MISSILETRAINER class.
|
||||||
-- Danny has shared his ideas and together we made a design.
|
-- Danny has shared his ideas and together we made a design.
|
||||||
-- Together with the **476 virtual team**, we tested the MISSILETRAINER class, and got much positive feedback!
|
-- Together with the **476 virtual team**, we tested the MISSILETRAINER class, and got much positive feedback!
|
||||||
-- * **132nd Squadron**: Testing and optimizing the logic.
|
-- * **132nd Squadron**: Testing and optimizing the logic.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- @module Functional.MissileTrainer
|
-- @module Functional.MissileTrainer
|
||||||
@@ -76,7 +76,7 @@
|
|||||||
---
|
---
|
||||||
--
|
--
|
||||||
-- # Constructor:
|
-- # Constructor:
|
||||||
--
|
--
|
||||||
-- Create a new MISSILETRAINER object with the @{#MISSILETRAINER.New} method:
|
-- Create a new MISSILETRAINER object with the @{#MISSILETRAINER.New} method:
|
||||||
--
|
--
|
||||||
-- * @{#MISSILETRAINER.New}: Creates a new MISSILETRAINER object taking the maximum distance to your aircraft to evaluate when a missile needs to be destroyed.
|
-- * @{#MISSILETRAINER.New}: Creates a new MISSILETRAINER object taking the maximum distance to your aircraft to evaluate when a missile needs to be destroyed.
|
||||||
@@ -84,7 +84,7 @@
|
|||||||
-- MISSILETRAINER will collect each unit declared in the mission with a skill level "Client" and "Player", and will monitor the missiles shot at those.
|
-- MISSILETRAINER will collect each unit declared in the mission with a skill level "Client" and "Player", and will monitor the missiles shot at those.
|
||||||
--
|
--
|
||||||
-- # Initialization:
|
-- # Initialization:
|
||||||
--
|
--
|
||||||
-- A MISSILETRAINER object will behave differently based on the usage of initialization methods:
|
-- A MISSILETRAINER object will behave differently based on the usage of initialization methods:
|
||||||
--
|
--
|
||||||
-- * @{#MISSILETRAINER.InitMessagesOnOff}: Sets by default the display of any message to be ON or OFF.
|
-- * @{#MISSILETRAINER.InitMessagesOnOff}: Sets by default the display of any message to be ON or OFF.
|
||||||
@@ -97,8 +97,8 @@
|
|||||||
-- * @{#MISSILETRAINER.InitRangeOnOff}: Sets by default the display of range information of missiles ON of OFF.
|
-- * @{#MISSILETRAINER.InitRangeOnOff}: Sets by default the display of range information of missiles ON of OFF.
|
||||||
-- * @{#MISSILETRAINER.InitBearingOnOff}: Sets by default the display of bearing information of missiles ON of OFF.
|
-- * @{#MISSILETRAINER.InitBearingOnOff}: Sets by default the display of bearing information of missiles ON of OFF.
|
||||||
-- * @{#MISSILETRAINER.InitMenusOnOff}: Allows to configure the options through the radio menu.
|
-- * @{#MISSILETRAINER.InitMenusOnOff}: Allows to configure the options through the radio menu.
|
||||||
--
|
--
|
||||||
-- @field #MISSILETRAINER
|
-- @field #MISSILETRAINER
|
||||||
MISSILETRAINER = {
|
MISSILETRAINER = {
|
||||||
ClassName = "MISSILETRAINER",
|
ClassName = "MISSILETRAINER",
|
||||||
TrackingMissiles = {},
|
TrackingMissiles = {},
|
||||||
@@ -167,7 +167,7 @@ end
|
|||||||
-- When a missile is fired a SCHEDULER is set off that follows the missile. When near a certain a client player, the missile will be destroyed.
|
-- When a missile is fired a SCHEDULER is set off that follows the missile. When near a certain a client player, the missile will be destroyed.
|
||||||
-- @param #MISSILETRAINER self
|
-- @param #MISSILETRAINER self
|
||||||
-- @param #number Distance The distance in meters when a tracked missile needs to be destroyed when close to a player.
|
-- @param #number Distance The distance in meters when a tracked missile needs to be destroyed when close to a player.
|
||||||
-- @param #string Briefing (Optional) Will show a text to the players when starting their mission. Can be used for briefing purposes.
|
-- @param #string Briefing (Optional) Will show a text to the players when starting their mission. Can be used for briefing purposes.
|
||||||
-- @return #MISSILETRAINER
|
-- @return #MISSILETRAINER
|
||||||
function MISSILETRAINER:New( Distance, Briefing )
|
function MISSILETRAINER:New( Distance, Briefing )
|
||||||
local self = BASE:Inherit( self, BASE:New() )
|
local self = BASE:Inherit( self, BASE:New() )
|
||||||
@@ -194,8 +194,8 @@ function MISSILETRAINER:New( Distance, Briefing )
|
|||||||
-- self:F( "ForEach:" .. Client.UnitName )
|
-- self:F( "ForEach:" .. Client.UnitName )
|
||||||
-- Client:Alive( self._Alive, self )
|
-- Client:Alive( self._Alive, self )
|
||||||
-- end
|
-- end
|
||||||
--
|
--
|
||||||
self.DBClients:ForEachClient(
|
self.DBClients:ForEachClient(
|
||||||
function( Client )
|
function( Client )
|
||||||
self:F( "ForEach:" .. Client.UnitName )
|
self:F( "ForEach:" .. Client.UnitName )
|
||||||
Client:Alive( self._Alive, self )
|
Client:Alive( self._Alive, self )
|
||||||
@@ -207,9 +207,9 @@ function MISSILETRAINER:New( Distance, Briefing )
|
|||||||
-- self.DB:ForEachClient(
|
-- self.DB:ForEachClient(
|
||||||
-- --- @param Wrapper.Client#CLIENT Client
|
-- --- @param Wrapper.Client#CLIENT Client
|
||||||
-- function( Client )
|
-- function( Client )
|
||||||
--
|
--
|
||||||
-- ... actions ...
|
-- ... actions ...
|
||||||
--
|
--
|
||||||
-- end
|
-- end
|
||||||
-- )
|
-- )
|
||||||
|
|
||||||
@@ -225,7 +225,7 @@ function MISSILETRAINER:New( Distance, Briefing )
|
|||||||
|
|
||||||
self.DetailsRangeOnOff = true
|
self.DetailsRangeOnOff = true
|
||||||
self.DetailsBearingOnOff = true
|
self.DetailsBearingOnOff = true
|
||||||
|
|
||||||
self.MenusOnOff = true
|
self.MenusOnOff = true
|
||||||
|
|
||||||
self.TrackingMissiles = {}
|
self.TrackingMissiles = {}
|
||||||
@@ -293,7 +293,7 @@ end
|
|||||||
--- Increases, decreases the missile tracking message display frequency with the provided time interval in seconds.
|
--- Increases, decreases the missile tracking message display frequency with the provided time interval in seconds.
|
||||||
-- The default frequency is a 3 second interval, so the Tracking Frequency parameter specifies the increase or decrease from the default 3 seconds or the last frequency update.
|
-- The default frequency is a 3 second interval, so the Tracking Frequency parameter specifies the increase or decrease from the default 3 seconds or the last frequency update.
|
||||||
-- @param #MISSILETRAINER self
|
-- @param #MISSILETRAINER self
|
||||||
-- @param #number TrackingFrequency Provide a negative or positive value in seconds to incraese or decrease the display frequency.
|
-- @param #number TrackingFrequency Provide a negative or positive value in seconds to incraese or decrease the display frequency.
|
||||||
-- @return #MISSILETRAINER self
|
-- @return #MISSILETRAINER self
|
||||||
function MISSILETRAINER:InitTrackingFrequency( TrackingFrequency )
|
function MISSILETRAINER:InitTrackingFrequency( TrackingFrequency )
|
||||||
self:F( TrackingFrequency )
|
self:F( TrackingFrequency )
|
||||||
@@ -478,30 +478,30 @@ function MISSILETRAINER:OnEventShot( EVentData )
|
|||||||
if TrainerTargetDCSUnit then
|
if TrainerTargetDCSUnit then
|
||||||
local TrainerTargetDCSUnitName = Unit.getName( TrainerTargetDCSUnit )
|
local TrainerTargetDCSUnitName = Unit.getName( TrainerTargetDCSUnit )
|
||||||
local TrainerTargetSkill = _DATABASE.Templates.Units[TrainerTargetDCSUnitName].Template.skill
|
local TrainerTargetSkill = _DATABASE.Templates.Units[TrainerTargetDCSUnitName].Template.skill
|
||||||
|
|
||||||
self:T(TrainerTargetDCSUnitName )
|
self:T(TrainerTargetDCSUnitName )
|
||||||
|
|
||||||
local Client = self.DBClients:FindClient( TrainerTargetDCSUnitName )
|
local Client = self.DBClients:FindClient( TrainerTargetDCSUnitName )
|
||||||
if Client then
|
if Client then
|
||||||
|
|
||||||
local TrainerSourceUnit = UNIT:Find( TrainerSourceDCSUnit )
|
local TrainerSourceUnit = UNIT:Find( TrainerSourceDCSUnit )
|
||||||
local TrainerTargetUnit = UNIT:Find( TrainerTargetDCSUnit )
|
local TrainerTargetUnit = UNIT:Find( TrainerTargetDCSUnit )
|
||||||
|
|
||||||
if self.MessagesOnOff == true and self.AlertsLaunchesOnOff == true then
|
if self.MessagesOnOff == true and self.AlertsLaunchesOnOff == true then
|
||||||
|
|
||||||
local Message = MESSAGE:New(
|
local Message = MESSAGE:New(
|
||||||
string.format( "%s launched a %s",
|
string.format( "%s launched a %s",
|
||||||
TrainerSourceUnit:GetTypeName(),
|
TrainerSourceUnit:GetTypeName(),
|
||||||
TrainerWeaponName
|
TrainerWeaponName
|
||||||
) .. self:_AddRange( Client, TrainerWeapon ) .. self:_AddBearing( Client, TrainerWeapon ), 5, "Launch Alert" )
|
) .. self:_AddRange( Client, TrainerWeapon ) .. self:_AddBearing( Client, TrainerWeapon ), 5, "Launch Alert" )
|
||||||
|
|
||||||
if self.AlertsToAll then
|
if self.AlertsToAll then
|
||||||
Message:ToAll()
|
Message:ToAll()
|
||||||
else
|
else
|
||||||
Message:ToClient( Client )
|
Message:ToClient( Client )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local ClientID = Client:GetID()
|
local ClientID = Client:GetID()
|
||||||
self:T( ClientID )
|
self:T( ClientID )
|
||||||
local MissileData = {}
|
local MissileData = {}
|
||||||
@@ -579,52 +579,52 @@ function MISSILETRAINER:_TrackMissiles()
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- ALERTS PART
|
-- ALERTS PART
|
||||||
|
|
||||||
-- Loop for all Player Clients to check the alerts and deletion of missiles.
|
-- Loop for all Player Clients to check the alerts and deletion of missiles.
|
||||||
for ClientDataID, ClientData in pairs( self.TrackingMissiles ) do
|
for ClientDataID, ClientData in pairs( self.TrackingMissiles ) do
|
||||||
|
|
||||||
local Client = ClientData.Client
|
local Client = ClientData.Client
|
||||||
|
|
||||||
if Client and Client:IsAlive() then
|
if Client and Client:IsAlive() then
|
||||||
|
|
||||||
for MissileDataID, MissileData in pairs( ClientData.MissileData ) do
|
for MissileDataID, MissileData in pairs( ClientData.MissileData ) do
|
||||||
self:T3( MissileDataID )
|
self:T3( MissileDataID )
|
||||||
|
|
||||||
local TrainerSourceUnit = MissileData.TrainerSourceUnit
|
local TrainerSourceUnit = MissileData.TrainerSourceUnit
|
||||||
local TrainerWeapon = MissileData.TrainerWeapon
|
local TrainerWeapon = MissileData.TrainerWeapon
|
||||||
local TrainerTargetUnit = MissileData.TrainerTargetUnit
|
local TrainerTargetUnit = MissileData.TrainerTargetUnit
|
||||||
local TrainerWeaponTypeName = MissileData.TrainerWeaponTypeName
|
local TrainerWeaponTypeName = MissileData.TrainerWeaponTypeName
|
||||||
local TrainerWeaponLaunched = MissileData.TrainerWeaponLaunched
|
local TrainerWeaponLaunched = MissileData.TrainerWeaponLaunched
|
||||||
|
|
||||||
if Client and Client:IsAlive() and TrainerSourceUnit and TrainerSourceUnit:IsAlive() and TrainerWeapon and TrainerWeapon:isExist() and TrainerTargetUnit and TrainerTargetUnit:IsAlive() then
|
if Client and Client:IsAlive() and TrainerSourceUnit and TrainerSourceUnit:IsAlive() and TrainerWeapon and TrainerWeapon:isExist() and TrainerTargetUnit and TrainerTargetUnit:IsAlive() then
|
||||||
local PositionMissile = TrainerWeapon:getPosition().p
|
local PositionMissile = TrainerWeapon:getPosition().p
|
||||||
local TargetVec3 = Client:GetVec3()
|
local TargetVec3 = Client:GetVec3()
|
||||||
|
|
||||||
local Distance = ( ( PositionMissile.x - TargetVec3.x )^2 +
|
local Distance = ( ( PositionMissile.x - TargetVec3.x )^2 +
|
||||||
( PositionMissile.y - TargetVec3.y )^2 +
|
( PositionMissile.y - TargetVec3.y )^2 +
|
||||||
( PositionMissile.z - TargetVec3.z )^2
|
( PositionMissile.z - TargetVec3.z )^2
|
||||||
) ^ 0.5 / 1000
|
) ^ 0.5 / 1000
|
||||||
|
|
||||||
if Distance <= self.Distance then
|
if Distance <= self.Distance then
|
||||||
-- Hit alert
|
-- Hit alert
|
||||||
TrainerWeapon:destroy()
|
TrainerWeapon:destroy()
|
||||||
if self.MessagesOnOff == true and self.AlertsHitsOnOff == true then
|
if self.MessagesOnOff == true and self.AlertsHitsOnOff == true then
|
||||||
|
|
||||||
self:T( "killed" )
|
self:T( "killed" )
|
||||||
|
|
||||||
local Message = MESSAGE:New(
|
local Message = MESSAGE:New(
|
||||||
string.format( "%s launched by %s killed %s",
|
string.format( "%s launched by %s killed %s",
|
||||||
TrainerWeapon:getTypeName(),
|
TrainerWeapon:getTypeName(),
|
||||||
TrainerSourceUnit:GetTypeName(),
|
TrainerSourceUnit:GetTypeName(),
|
||||||
TrainerTargetUnit:GetPlayerName()
|
TrainerTargetUnit:GetPlayerName()
|
||||||
), 15, "Hit Alert" )
|
), 15, "Hit Alert" )
|
||||||
|
|
||||||
if self.AlertsToAll == true then
|
if self.AlertsToAll == true then
|
||||||
Message:ToAll()
|
Message:ToAll()
|
||||||
else
|
else
|
||||||
Message:ToClient( Client )
|
Message:ToClient( Client )
|
||||||
end
|
end
|
||||||
|
|
||||||
MissileData = nil
|
MissileData = nil
|
||||||
table.remove( ClientData.MissileData, MissileDataID )
|
table.remove( ClientData.MissileData, MissileDataID )
|
||||||
self:T(ClientData.MissileData)
|
self:T(ClientData.MissileData)
|
||||||
@@ -639,7 +639,7 @@ function MISSILETRAINER:_TrackMissiles()
|
|||||||
TrainerWeaponTypeName,
|
TrainerWeaponTypeName,
|
||||||
TrainerSourceUnit:GetTypeName()
|
TrainerSourceUnit:GetTypeName()
|
||||||
), 5, "Tracking" )
|
), 5, "Tracking" )
|
||||||
|
|
||||||
if self.AlertsToAll == true then
|
if self.AlertsToAll == true then
|
||||||
Message:ToAll()
|
Message:ToAll()
|
||||||
else
|
else
|
||||||
@@ -660,41 +660,41 @@ function MISSILETRAINER:_TrackMissiles()
|
|||||||
if ShowMessages == true and self.MessagesOnOff == true and self.TrackingOnOff == true then -- Only do this when tracking information needs to be displayed.
|
if ShowMessages == true and self.MessagesOnOff == true and self.TrackingOnOff == true then -- Only do this when tracking information needs to be displayed.
|
||||||
|
|
||||||
-- TRACKING PART
|
-- TRACKING PART
|
||||||
|
|
||||||
-- For the current client, the missile range and bearing details are displayed To the Player Client.
|
-- For the current client, the missile range and bearing details are displayed To the Player Client.
|
||||||
-- For the other clients, the missile range and bearing details are displayed To the other Player Clients.
|
-- For the other clients, the missile range and bearing details are displayed To the other Player Clients.
|
||||||
-- To achieve this, a cross loop is done for each Player Client <-> Other Player Client missile information.
|
-- To achieve this, a cross loop is done for each Player Client <-> Other Player Client missile information.
|
||||||
|
|
||||||
-- Main Player Client loop
|
-- Main Player Client loop
|
||||||
for ClientDataID, ClientData in pairs( self.TrackingMissiles ) do
|
for ClientDataID, ClientData in pairs( self.TrackingMissiles ) do
|
||||||
|
|
||||||
local Client = ClientData.Client
|
local Client = ClientData.Client
|
||||||
--self:T2( { Client:GetName() } )
|
--self:T2( { Client:GetName() } )
|
||||||
|
|
||||||
|
|
||||||
ClientData.MessageToClient = ""
|
ClientData.MessageToClient = ""
|
||||||
ClientData.MessageToAll = ""
|
ClientData.MessageToAll = ""
|
||||||
|
|
||||||
-- Other Players Client loop
|
-- Other Players Client loop
|
||||||
for TrackingDataID, TrackingData in pairs( self.TrackingMissiles ) do
|
for TrackingDataID, TrackingData in pairs( self.TrackingMissiles ) do
|
||||||
|
|
||||||
for MissileDataID, MissileData in pairs( TrackingData.MissileData ) do
|
for MissileDataID, MissileData in pairs( TrackingData.MissileData ) do
|
||||||
--self:T3( MissileDataID )
|
--self:T3( MissileDataID )
|
||||||
|
|
||||||
local TrainerSourceUnit = MissileData.TrainerSourceUnit
|
local TrainerSourceUnit = MissileData.TrainerSourceUnit
|
||||||
local TrainerWeapon = MissileData.TrainerWeapon
|
local TrainerWeapon = MissileData.TrainerWeapon
|
||||||
local TrainerTargetUnit = MissileData.TrainerTargetUnit
|
local TrainerTargetUnit = MissileData.TrainerTargetUnit
|
||||||
local TrainerWeaponTypeName = MissileData.TrainerWeaponTypeName
|
local TrainerWeaponTypeName = MissileData.TrainerWeaponTypeName
|
||||||
local TrainerWeaponLaunched = MissileData.TrainerWeaponLaunched
|
local TrainerWeaponLaunched = MissileData.TrainerWeaponLaunched
|
||||||
|
|
||||||
if Client and Client:IsAlive() and TrainerSourceUnit and TrainerSourceUnit:IsAlive() and TrainerWeapon and TrainerWeapon:isExist() and TrainerTargetUnit and TrainerTargetUnit:IsAlive() then
|
if Client and Client:IsAlive() and TrainerSourceUnit and TrainerSourceUnit:IsAlive() and TrainerWeapon and TrainerWeapon:isExist() and TrainerTargetUnit and TrainerTargetUnit:IsAlive() then
|
||||||
|
|
||||||
if ShowMessages == true then
|
if ShowMessages == true then
|
||||||
local TrackingTo
|
local TrackingTo
|
||||||
TrackingTo = string.format( " -> %s",
|
TrackingTo = string.format( " -> %s",
|
||||||
TrainerWeaponTypeName
|
TrainerWeaponTypeName
|
||||||
)
|
)
|
||||||
|
|
||||||
if ClientDataID == TrackingDataID then
|
if ClientDataID == TrackingDataID then
|
||||||
if ClientData.MessageToClient == "" then
|
if ClientData.MessageToClient == "" then
|
||||||
ClientData.MessageToClient = "Missiles to You:\n"
|
ClientData.MessageToClient = "Missiles to You:\n"
|
||||||
@@ -712,7 +712,7 @@ function MISSILETRAINER:_TrackMissiles()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Once the Player Client and the Other Player Client tracking messages are prepared, show them.
|
-- Once the Player Client and the Other Player Client tracking messages are prepared, show them.
|
||||||
if ClientData.MessageToClient ~= "" or ClientData.MessageToAll ~= "" then
|
if ClientData.MessageToClient ~= "" or ClientData.MessageToAll ~= "" then
|
||||||
local Message = MESSAGE:New( ClientData.MessageToClient .. ClientData.MessageToAll, 1, "Tracking" ):ToClient( Client )
|
local Message = MESSAGE:New( ClientData.MessageToClient .. ClientData.MessageToAll, 1, "Tracking" ):ToClient( Client )
|
||||||
|
|||||||
@@ -742,7 +742,7 @@ function PSEUDOATC:ReportWeather(GID, UID, position, location)
|
|||||||
local T=position:GetTemperature()
|
local T=position:GetTemperature()
|
||||||
|
|
||||||
-- Correct unit system.
|
-- Correct unit system.
|
||||||
local _T=string.format('%d°F', UTILS.CelciusToFarenheit(T))
|
local _T=string.format('%d°F', UTILS.CelsiusToFahrenheit(T))
|
||||||
if settings:IsMetric() then
|
if settings:IsMetric() then
|
||||||
_T=string.format('%d°C', T)
|
_T=string.format('%d°C', T)
|
||||||
end
|
end
|
||||||
@@ -949,11 +949,14 @@ function PSEUDOATC:LocalAirports(GID, UID)
|
|||||||
for _,airbase in pairs(airports) do
|
for _,airbase in pairs(airports) do
|
||||||
|
|
||||||
local name=airbase:getName()
|
local name=airbase:getName()
|
||||||
local q=AIRBASE:FindByName(name):GetCoordinate()
|
local a=AIRBASE:FindByName(name)
|
||||||
local d=q:Get2DDistance(pos)
|
if a then
|
||||||
|
local q=a:GetCoordinate()
|
||||||
|
local d=q:Get2DDistance(pos)
|
||||||
|
|
||||||
-- Add to table.
|
-- Add to table.
|
||||||
table.insert(self.group[GID].player[UID].airports, {distance=d, name=name})
|
table.insert(self.group[GID].player[UID].airports, {distance=d, name=name})
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -1041,6 +1044,3 @@ function PSEUDOATC:_myname(unitname)
|
|||||||
|
|
||||||
return string.format("%s (%s)", csign, pname)
|
return string.format("%s (%s)", csign, pname)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,56 +1,65 @@
|
|||||||
--- **Functional** -- Make SAM sites execute evasive and defensive behaviour when being fired upon.
|
--- **Functional** -- Make SAM sites execute evasive and defensive behaviour when being fired upon.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ## Features:
|
-- ## Features:
|
||||||
--
|
--
|
||||||
-- * When SAM sites are being fired upon, the SAMs will take evasive action will reposition themselves when possible.
|
-- * When SAM sites are being fired upon, the SAMs will take evasive action will reposition themselves when possible.
|
||||||
-- * When SAM sites are being fired upon, the SAMs will take defensive action by shutting down their radars.
|
-- * When SAM sites are being fired upon, the SAMs will take defensive action by shutting down their radars.
|
||||||
--
|
-- * SEAD calculates the time it takes for a HARM to reach the target - and will attempt to minimize the shut-down time.
|
||||||
|
-- * Detection and evasion of shots has a random component based on the skill level of the SAM groups.
|
||||||
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ## Missions:
|
-- ## Missions:
|
||||||
--
|
--
|
||||||
-- [SEV - SEAD Evasion](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SEV%20-%20SEAD%20Evasion)
|
-- [SEV - SEAD Evasion](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SEV%20-%20SEAD%20Evasion)
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ### Authors: **FlightControl**, **applevangelist**
|
-- ### Authors: **FlightControl**, **applevangelist**
|
||||||
--
|
--
|
||||||
-- Last Update: Aug 2021
|
-- Last Update: Feb 2022
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- @module Functional.Sead
|
-- @module Functional.Sead
|
||||||
-- @image SEAD.JPG
|
-- @image SEAD.JPG
|
||||||
|
|
||||||
---
|
---
|
||||||
-- @type SEAD
|
-- @type SEAD
|
||||||
-- @extends Core.Base#BASE
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
--- Make SAM sites execute evasive and defensive behaviour when being fired upon.
|
--- Make SAM sites execute evasive and defensive behaviour when being fired upon.
|
||||||
--
|
--
|
||||||
-- This class is very easy to use. Just setup a SEAD object by using @{#SEAD.New}() and SAMs will evade and take defensive action when being fired upon.
|
-- This class is very easy to use. Just setup a SEAD object by using @{#SEAD.New}() and SAMs will evade and take defensive action when being fired upon.
|
||||||
|
-- Once a HARM attack is detected, SEAD will shut down the radars of the attacked SAM site and take evasive action by moving the SAM
|
||||||
|
-- vehicles around (*if* they are drivable, that is). There's a component of randomness in detection and evasion, which is based on the
|
||||||
|
-- skill set of the SAM set (the higher the skill, the more likely). When a missile is fired from far away, the SAM will stay active for a
|
||||||
|
-- period of time to stay defensive, before it takes evasive actions.
|
||||||
--
|
--
|
||||||
-- # Constructor:
|
-- # Constructor:
|
||||||
--
|
--
|
||||||
-- Use the @{#SEAD.New}() constructor to create a new SEAD object.
|
-- Use the @{#SEAD.New}() constructor to create a new SEAD object.
|
||||||
--
|
--
|
||||||
-- SEAD_RU_SAM_Defenses = SEAD:New( { 'RU SA-6 Kub', 'RU SA-6 Defenses', 'RU MI-26 Troops', 'RU Attack Gori' } )
|
-- SEAD_RU_SAM_Defenses = SEAD:New( { 'RU SA-6 Kub', 'RU SA-6 Defenses', 'RU MI-26 Troops', 'RU Attack Gori' } )
|
||||||
--
|
--
|
||||||
-- @field #SEAD
|
-- @field #SEAD
|
||||||
SEAD = {
|
SEAD = {
|
||||||
ClassName = "SEAD",
|
ClassName = "SEAD",
|
||||||
TargetSkill = {
|
TargetSkill = {
|
||||||
Average = { Evade = 30, DelayOn = { 40, 60 } } ,
|
Average = { Evade = 30, DelayOn = { 40, 60 } } ,
|
||||||
Good = { Evade = 20, DelayOn = { 30, 50 } } ,
|
Good = { Evade = 20, DelayOn = { 30, 50 } } ,
|
||||||
High = { Evade = 15, DelayOn = { 20, 40 } } ,
|
High = { Evade = 15, DelayOn = { 20, 40 } } ,
|
||||||
Excellent = { Evade = 10, DelayOn = { 10, 30 } }
|
Excellent = { Evade = 10, DelayOn = { 10, 30 } }
|
||||||
},
|
},
|
||||||
SEADGroupPrefixes = {},
|
SEADGroupPrefixes = {},
|
||||||
SuppressedGroups = {},
|
SuppressedGroups = {},
|
||||||
EngagementRange = 75, -- default 75% engagement range Feature Request #1355
|
EngagementRange = 75, -- default 75% engagement range Feature Request #1355
|
||||||
Padding = 10,
|
Padding = 10,
|
||||||
|
CallBack = nil,
|
||||||
|
UseCallBack = false,
|
||||||
|
debug = false,
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Missile enumerators
|
--- Missile enumerators
|
||||||
@@ -68,8 +77,11 @@ SEAD = {
|
|||||||
["X_25"] = "X_25",
|
["X_25"] = "X_25",
|
||||||
["X_31"] = "X_31",
|
["X_31"] = "X_31",
|
||||||
["Kh25"] = "Kh25",
|
["Kh25"] = "Kh25",
|
||||||
|
["BGM_109"] = "BGM_109",
|
||||||
|
["AGM_154"] = "AGM_154",
|
||||||
|
["HY-2"] = "HY-2",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Missile enumerators - from DCS ME and Wikipedia
|
--- Missile enumerators - from DCS ME and Wikipedia
|
||||||
-- @field HarmData
|
-- @field HarmData
|
||||||
SEAD.HarmData = {
|
SEAD.HarmData = {
|
||||||
@@ -77,7 +89,7 @@ SEAD = {
|
|||||||
["AGM_88"] = { 150, 3},
|
["AGM_88"] = { 150, 3},
|
||||||
["AGM_45"] = { 12, 2},
|
["AGM_45"] = { 12, 2},
|
||||||
["AGM_122"] = { 16.5, 2.3},
|
["AGM_122"] = { 16.5, 2.3},
|
||||||
["AGM_84"] = { 280, 0.85},
|
["AGM_84"] = { 280, 0.8},
|
||||||
["ALARM"] = { 45, 2},
|
["ALARM"] = { 45, 2},
|
||||||
["LD-10"] = { 60, 4},
|
["LD-10"] = { 60, 4},
|
||||||
["X_58"] = { 70, 4},
|
["X_58"] = { 70, 4},
|
||||||
@@ -85,24 +97,27 @@ SEAD = {
|
|||||||
["X_25"] = { 25, 0.76},
|
["X_25"] = { 25, 0.76},
|
||||||
["X_31"] = {150, 3},
|
["X_31"] = {150, 3},
|
||||||
["Kh25"] = {25, 0.8},
|
["Kh25"] = {25, 0.8},
|
||||||
|
["BGM_109"] = {460, 0.705}, --in-game ~465kn
|
||||||
|
["AGM_154"] = {130, 0.61},
|
||||||
|
["HY-2"] = {90,1},
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Creates the main object which is handling defensive actions for SA sites or moving SA vehicles.
|
--- Creates the main object which is handling defensive actions for SA sites or moving SA vehicles.
|
||||||
-- When an anti radiation missile is fired (KH-58, KH-31P, KH-31A, KH-25MPU, HARM missiles), the SA will shut down their radars and will take evasive actions...
|
-- When an anti radiation missile is fired (KH-58, KH-31P, KH-31A, KH-25MPU, HARM missiles), the SA will shut down their radars and will take evasive actions...
|
||||||
-- Chances are big that the missile will miss.
|
-- Chances are big that the missile will miss.
|
||||||
-- @param #SEAD self
|
-- @param #SEAD self
|
||||||
-- @param #table SEADGroupPrefixes Table of #string entries or single #string, which is a table of Prefixes of the SA Groups in the DCS mission editor on which evasive actions need to be taken.
|
-- @param #table SEADGroupPrefixes Table of #string entries or single #string, which is a table of Prefixes of the SA Groups in the DCS mission editor on which evasive actions need to be taken.
|
||||||
-- @param #number Padding (Optional) Extra number of seconds to add to radar switch-back-on time
|
-- @param #number Padding (Optional) Extra number of seconds to add to radar switch-back-on time
|
||||||
-- @return SEAD
|
-- @return #SEAD self
|
||||||
-- @usage
|
-- @usage
|
||||||
-- -- CCCP SEAD Defenses
|
-- -- CCCP SEAD Defenses
|
||||||
-- -- Defends the Russian SA installations from SEAD attacks.
|
-- -- Defends the Russian SA installations from SEAD attacks.
|
||||||
-- SEAD_RU_SAM_Defenses = SEAD:New( { 'RU SA-6 Kub', 'RU SA-6 Defenses', 'RU MI-26 Troops', 'RU Attack Gori' } )
|
-- SEAD_RU_SAM_Defenses = SEAD:New( { 'RU SA-6 Kub', 'RU SA-6 Defenses', 'RU MI-26 Troops', 'RU Attack Gori' } )
|
||||||
function SEAD:New( SEADGroupPrefixes, Padding )
|
function SEAD:New( SEADGroupPrefixes, Padding )
|
||||||
|
|
||||||
local self = BASE:Inherit( self, BASE:New() )
|
local self = BASE:Inherit( self, FSM:New() )
|
||||||
self:F( SEADGroupPrefixes )
|
self:T( SEADGroupPrefixes )
|
||||||
|
|
||||||
if type( SEADGroupPrefixes ) == 'table' then
|
if type( SEADGroupPrefixes ) == 'table' then
|
||||||
for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do
|
for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do
|
||||||
self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix
|
self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix
|
||||||
@@ -110,25 +125,36 @@ function SEAD:New( SEADGroupPrefixes, Padding )
|
|||||||
else
|
else
|
||||||
self.SEADGroupPrefixes[SEADGroupPrefixes] = SEADGroupPrefixes
|
self.SEADGroupPrefixes[SEADGroupPrefixes] = SEADGroupPrefixes
|
||||||
end
|
end
|
||||||
|
|
||||||
local padding = Padding or 10
|
local padding = Padding or 10
|
||||||
if padding < 10 then padding = 10 end
|
if padding < 10 then padding = 10 end
|
||||||
self.Padding = padding
|
self.Padding = padding
|
||||||
|
self.UseEmissionsOnOff = true
|
||||||
|
|
||||||
|
self.debug = false
|
||||||
|
|
||||||
|
self.CallBack = nil
|
||||||
|
self.UseCallBack = false
|
||||||
|
|
||||||
self:HandleEvent( EVENTS.Shot, self.HandleEventShot )
|
self:HandleEvent( EVENTS.Shot, self.HandleEventShot )
|
||||||
|
|
||||||
self:I("*** SEAD - Started Version 0.3.1")
|
-- Start State.
|
||||||
|
self:SetStartState("Running")
|
||||||
|
self:AddTransition("*", "ManageEvasion", "*")
|
||||||
|
self:AddTransition("*", "CalculateHitZone", "*")
|
||||||
|
|
||||||
|
self:I("*** SEAD - Started Version 0.4.3")
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Update the active SEAD Set
|
--- Update the active SEAD Set (while running)
|
||||||
-- @param #SEAD self
|
-- @param #SEAD self
|
||||||
-- @param #table SEADGroupPrefixes The prefixes to add, note: can also be a single #string
|
-- @param #table SEADGroupPrefixes The prefixes to add, note: can also be a single #string
|
||||||
-- @return #SEAD self
|
-- @return #SEAD self
|
||||||
function SEAD:UpdateSet( SEADGroupPrefixes )
|
function SEAD:UpdateSet( SEADGroupPrefixes )
|
||||||
|
|
||||||
self:T( SEADGroupPrefixes )
|
self:T( SEADGroupPrefixes )
|
||||||
|
|
||||||
if type( SEADGroupPrefixes ) == 'table' then
|
if type( SEADGroupPrefixes ) == 'table' then
|
||||||
for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do
|
for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do
|
||||||
self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix
|
self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix
|
||||||
@@ -142,8 +168,8 @@ end
|
|||||||
|
|
||||||
--- Sets the engagement range of the SAMs. Defaults to 75% to make it more deadly. Feature Request #1355
|
--- Sets the engagement range of the SAMs. Defaults to 75% to make it more deadly. Feature Request #1355
|
||||||
-- @param #SEAD self
|
-- @param #SEAD self
|
||||||
-- @param #number range Set the engagement range in percent, e.g. 50
|
-- @param #number range Set the engagement range in percent, e.g. 55 (default 75)
|
||||||
-- @return self
|
-- @return #SEAD self
|
||||||
function SEAD:SetEngagementRange(range)
|
function SEAD:SetEngagementRange(range)
|
||||||
self:T( { range } )
|
self:T( { range } )
|
||||||
range = range or 75
|
range = range or 75
|
||||||
@@ -157,7 +183,8 @@ end
|
|||||||
|
|
||||||
--- Set the padding in seconds, which extends the radar off time calculated by SEAD
|
--- Set the padding in seconds, which extends the radar off time calculated by SEAD
|
||||||
-- @param #SEAD self
|
-- @param #SEAD self
|
||||||
-- @param #number Padding Extra number of seconds to add for the switch-on
|
-- @param #number Padding Extra number of seconds to add for the switch-on (default 10 seconds)
|
||||||
|
-- @return #SEAD self
|
||||||
function SEAD:SetPadding(Padding)
|
function SEAD:SetPadding(Padding)
|
||||||
self:T( { Padding } )
|
self:T( { Padding } )
|
||||||
local padding = Padding or 10
|
local padding = Padding or 10
|
||||||
@@ -166,59 +193,264 @@ function SEAD:SetPadding(Padding)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Check if a known HARM was fired
|
--- Set SEAD to use emissions on/off in addition to alarm state.
|
||||||
-- @param #SEAD self
|
-- @param #SEAD self
|
||||||
-- @param #string WeaponName
|
-- @param #boolean Switch True for on, false for off.
|
||||||
-- @return #boolean Returns true for a match
|
-- @return #SEAD self
|
||||||
-- @return #string name Name of hit in table
|
function SEAD:SwitchEmissions(Switch)
|
||||||
function SEAD:_CheckHarms(WeaponName)
|
self:T({Switch})
|
||||||
self:T( { WeaponName } )
|
self.UseEmissionsOnOff = Switch
|
||||||
local hit = false
|
return self
|
||||||
local name = ""
|
end
|
||||||
for _,_name in pairs (SEAD.Harms) do
|
|
||||||
if string.find(WeaponName,_name,1) then
|
|
||||||
hit = true
|
|
||||||
name = _name
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return hit, name
|
|
||||||
end
|
|
||||||
|
|
||||||
--- (Internal) Return distance in meters between two coordinates or -1 on error.
|
--- Add an object to call back when going evasive.
|
||||||
-- @param #SEAD self
|
-- @param #SEAD self
|
||||||
-- @param Core.Point#COORDINATE _point1 Coordinate one
|
-- @param #table Object The object to call. Needs to have object functions as follows:
|
||||||
-- @param Core.Point#COORDINATE _point2 Coordinate two
|
-- `:SeadSuppressionPlanned(Group, Name, SuppressionStartTime, SuppressionEndTime)`
|
||||||
-- @return #number Distance in meters
|
-- `:SeadSuppressionStart(Group, Name)`,
|
||||||
function SEAD:_GetDistance(_point1, _point2)
|
-- `:SeadSuppressionEnd(Group, Name)`,
|
||||||
self:T("_GetDistance")
|
-- @return #SEAD self
|
||||||
if _point1 and _point2 then
|
function SEAD:AddCallBack(Object)
|
||||||
local distance1 = _point1:Get2DDistance(_point2)
|
self:T({Class=Object.ClassName})
|
||||||
local distance2 = _point1:DistanceFromPointVec2(_point2)
|
self.CallBack = Object
|
||||||
--self:T({dist1=distance1, dist2=distance2})
|
self.UseCallBack = true
|
||||||
if distance1 and type(distance1) == "number" then
|
return self
|
||||||
return distance1
|
end
|
||||||
elseif distance2 and type(distance2) == "number" then
|
|
||||||
return distance2
|
--- (Internal) Check if a known HARM was fired
|
||||||
else
|
-- @param #SEAD self
|
||||||
self:E("*****Cannot calculate distance!")
|
-- @param #string WeaponName
|
||||||
self:E({_point1,_point2})
|
-- @return #boolean Returns true for a match
|
||||||
return -1
|
-- @return #string name Name of hit in table
|
||||||
|
function SEAD:_CheckHarms(WeaponName)
|
||||||
|
self:T( { WeaponName } )
|
||||||
|
local hit = false
|
||||||
|
local name = ""
|
||||||
|
for _,_name in pairs (SEAD.Harms) do
|
||||||
|
if string.find(WeaponName,_name,1,true) then
|
||||||
|
hit = true
|
||||||
|
name = _name
|
||||||
|
break
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
return hit, name
|
||||||
|
end
|
||||||
|
|
||||||
|
--- (Internal) Return distance in meters between two coordinates or -1 on error.
|
||||||
|
-- @param #SEAD self
|
||||||
|
-- @param Core.Point#COORDINATE _point1 Coordinate one
|
||||||
|
-- @param Core.Point#COORDINATE _point2 Coordinate two
|
||||||
|
-- @return #number Distance in meters
|
||||||
|
function SEAD:_GetDistance(_point1, _point2)
|
||||||
|
self:T("_GetDistance")
|
||||||
|
if _point1 and _point2 then
|
||||||
|
local distance1 = _point1:Get2DDistance(_point2)
|
||||||
|
local distance2 = _point1:DistanceFromPointVec2(_point2)
|
||||||
|
--self:T({dist1=distance1, dist2=distance2})
|
||||||
|
if distance1 and type(distance1) == "number" then
|
||||||
|
return distance1
|
||||||
|
elseif distance2 and type(distance2) == "number" then
|
||||||
|
return distance2
|
||||||
else
|
else
|
||||||
self:E("******Cannot calculate distance!")
|
self:E("*****Cannot calculate distance!")
|
||||||
self:E({_point1,_point2})
|
self:E({_point1,_point2})
|
||||||
return -1
|
return -1
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
self:E("******Cannot calculate distance!")
|
||||||
|
self:E({_point1,_point2})
|
||||||
|
return -1
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- (Internal) Calculate hit zone of an AGM-88
|
||||||
|
-- @param #SEAD self
|
||||||
|
-- @param #table SEADWeapon DCS.Weapon object
|
||||||
|
-- @param Core.Point#COORDINATE pos0 Position of the plane when it fired
|
||||||
|
-- @param #number height Height when the missile was fired
|
||||||
|
-- @param Wrapper.Group#GROUP SEADGroup Attacker group
|
||||||
|
-- @param #string SEADWeaponName Weapon Name
|
||||||
|
-- @return #SEAD self
|
||||||
|
function SEAD:onafterCalculateHitZone(From,Event,To,SEADWeapon,pos0,height,SEADGroup,SEADWeaponName)
|
||||||
|
self:T("**** Calculating hit zone for " .. (SEADWeaponName or "None"))
|
||||||
|
if SEADWeapon and SEADWeapon:isExist() then
|
||||||
|
--local pos = SEADWeapon:getPoint()
|
||||||
|
|
||||||
|
-- postion and height
|
||||||
|
local position = SEADWeapon:getPosition()
|
||||||
|
local mheight = height
|
||||||
|
-- heading
|
||||||
|
local wph = math.atan2(position.x.z, position.x.x)
|
||||||
|
if wph < 0 then
|
||||||
|
wph=wph+2*math.pi
|
||||||
|
end
|
||||||
|
wph=math.deg(wph)
|
||||||
|
|
||||||
|
-- velocity
|
||||||
|
local wpndata = SEAD.HarmData["AGM_88"]
|
||||||
|
if string.find(SEADWeaponName,"154",1) then
|
||||||
|
wpndata = SEAD.HarmData["AGM_154"]
|
||||||
|
end
|
||||||
|
local mveloc = math.floor(wpndata[2] * 340.29)
|
||||||
|
local c1 = (2*mheight*9.81)/(mveloc^2)
|
||||||
|
local c2 = (mveloc^2) / 9.81
|
||||||
|
local Ropt = c2 * math.sqrt(c1+1)
|
||||||
|
if height <= 5000 then
|
||||||
|
Ropt = Ropt * 0.72
|
||||||
|
elseif height <= 7500 then
|
||||||
|
Ropt = Ropt * 0.82
|
||||||
|
elseif height <= 10000 then
|
||||||
|
Ropt = Ropt * 0.87
|
||||||
|
elseif height <= 12500 then
|
||||||
|
Ropt = Ropt * 0.98
|
||||||
|
end
|
||||||
|
|
||||||
|
-- look at a couple of zones across the trajectory
|
||||||
|
for n=1,3 do
|
||||||
|
local dist = Ropt - ((n-1)*20000)
|
||||||
|
local predpos= pos0:Translate(dist,wph)
|
||||||
|
if predpos then
|
||||||
|
|
||||||
--- 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.
|
local targetzone = ZONE_RADIUS:New("Target Zone",predpos:GetVec2(),20000)
|
||||||
-- @see SEAD
|
|
||||||
-- @param #SEAD
|
if self.debug then
|
||||||
|
predpos:MarkToAll(string.format("height=%dm | heading=%d | velocity=%ddeg | Ropt=%dm",mheight,wph,mveloc,Ropt),false)
|
||||||
|
targetzone:DrawZone(coalition.side.BLUE,{0,0,1},0.2,nil,nil,3,true)
|
||||||
|
end
|
||||||
|
|
||||||
|
local seadset = SET_GROUP:New():FilterPrefixes(self.SEADGroupPrefixes):FilterZones({targetzone}):FilterOnce()
|
||||||
|
local tgtcoord = targetzone:GetRandomPointVec2()
|
||||||
|
--if tgtcoord and tgtcoord.ClassName == "COORDINATE" then
|
||||||
|
--local tgtgrp = seadset:FindNearestGroupFromPointVec2(tgtcoord)
|
||||||
|
local tgtgrp = seadset:GetRandom()
|
||||||
|
local _targetgroup = nil
|
||||||
|
local _targetgroupname = "none"
|
||||||
|
local _targetskill = "Random"
|
||||||
|
if tgtgrp and tgtgrp:IsAlive() then
|
||||||
|
_targetgroup = tgtgrp
|
||||||
|
_targetgroupname = tgtgrp:GetName() -- group name
|
||||||
|
_targetskill = tgtgrp:GetUnit(1):GetSkill()
|
||||||
|
self:T("*** Found Target = ".. _targetgroupname)
|
||||||
|
self:ManageEvasion(_targetskill,_targetgroup,pos0,"AGM_88",SEADGroup, 20)
|
||||||
|
end
|
||||||
|
--end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- (Internal) Handle Evasion
|
||||||
|
-- @param #SEAD self
|
||||||
|
-- @param #string _targetskill
|
||||||
|
-- @param Wrapper.Group#GROUP _targetgroup
|
||||||
|
-- @param Core.Point#COORDINATE SEADPlanePos
|
||||||
|
-- @param #string SEADWeaponName
|
||||||
|
-- @param Wrapper.Group#GROUP SEADGroup Attacker Group
|
||||||
|
-- @param #number timeoffset Offset for tti calc
|
||||||
|
-- @return #SEAD self
|
||||||
|
function SEAD:onafterManageEvasion(From,Event,To,_targetskill,_targetgroup,SEADPlanePos,SEADWeaponName,SEADGroup,timeoffset)
|
||||||
|
local timeoffset = timeoffset or 0
|
||||||
|
if _targetskill == "Random" then -- when skill is random, choose a skill
|
||||||
|
local Skills = { "Average", "Good", "High", "Excellent" }
|
||||||
|
_targetskill = Skills[ math.random(1,4) ]
|
||||||
|
end
|
||||||
|
--self:T( _targetskill )
|
||||||
|
if self.TargetSkill[_targetskill] then
|
||||||
|
local _evade = math.random (1,100) -- random number for chance of evading action
|
||||||
|
if (_evade > self.TargetSkill[_targetskill].Evade) then
|
||||||
|
self:T("*** SEAD - Evading")
|
||||||
|
-- calculate distance of attacker
|
||||||
|
local _targetpos = _targetgroup:GetCoordinate()
|
||||||
|
local _distance = self:_GetDistance(SEADPlanePos, _targetpos)
|
||||||
|
-- weapon speed
|
||||||
|
local hit, data = self:_CheckHarms(SEADWeaponName)
|
||||||
|
local wpnspeed = 666 -- ;)
|
||||||
|
local reach = 10
|
||||||
|
if hit then
|
||||||
|
local wpndata = SEAD.HarmData[data]
|
||||||
|
reach = wpndata[1] * 1,1
|
||||||
|
local mach = wpndata[2]
|
||||||
|
wpnspeed = math.floor(mach * 340.29)
|
||||||
|
end
|
||||||
|
-- time to impact
|
||||||
|
local _tti = math.floor(_distance / wpnspeed) - timeoffset -- estimated impact time
|
||||||
|
if _distance > 0 then
|
||||||
|
_distance = math.floor(_distance / 1000) -- km
|
||||||
|
else
|
||||||
|
_distance = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
self:T( string.format("*** SEAD - target skill %s, distance %dkm, reach %dkm, tti %dsec", _targetskill, _distance,reach,_tti ))
|
||||||
|
|
||||||
|
if reach >= _distance then
|
||||||
|
self:T("*** SEAD - Shot in Reach")
|
||||||
|
|
||||||
|
local function SuppressionStart(args)
|
||||||
|
self:T(string.format("*** SEAD - %s Radar Off & Relocating",args[2]))
|
||||||
|
local grp = args[1] -- Wrapper.Group#GROUP
|
||||||
|
local name = args[2] -- #string Group Name
|
||||||
|
local attacker = args[3] -- Wrapper.Group#GROUP
|
||||||
|
if self.UseEmissionsOnOff then
|
||||||
|
grp:EnableEmission(false)
|
||||||
|
end
|
||||||
|
grp:OptionAlarmStateGreen() -- needed else we cannot move around
|
||||||
|
grp:RelocateGroundRandomInRadius(20,300,false,false,"Diamond")
|
||||||
|
if self.UseCallBack then
|
||||||
|
local object = self.CallBack
|
||||||
|
object:SeadSuppressionStart(grp,name,attacker)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function SuppressionStop(args)
|
||||||
|
self:T(string.format("*** SEAD - %s Radar On",args[2]))
|
||||||
|
local grp = args[1] -- Wrapper.Group#GROUP
|
||||||
|
local name = args[2] -- #string Group Nam
|
||||||
|
if self.UseEmissionsOnOff then
|
||||||
|
grp:EnableEmission(true)
|
||||||
|
end
|
||||||
|
grp:OptionAlarmStateRed()
|
||||||
|
grp:OptionEngageRange(self.EngagementRange)
|
||||||
|
self.SuppressedGroups[name] = false
|
||||||
|
if self.UseCallBack then
|
||||||
|
local object = self.CallBack
|
||||||
|
object:SeadSuppressionEnd(grp,name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- randomize switch-on time
|
||||||
|
local delay = math.random(self.TargetSkill[_targetskill].DelayOn[1], self.TargetSkill[_targetskill].DelayOn[2])
|
||||||
|
if delay > _tti then delay = delay / 2 end -- speed up
|
||||||
|
if _tti > 600 then delay = _tti - 90 end -- shot from afar, 600 is default shorad ontime
|
||||||
|
|
||||||
|
local SuppressionStartTime = timer.getTime() + delay
|
||||||
|
local SuppressionEndTime = timer.getTime() + _tti + self.Padding
|
||||||
|
local _targetgroupname = _targetgroup:GetName()
|
||||||
|
if not self.SuppressedGroups[_targetgroupname] then
|
||||||
|
self:T(string.format("*** SEAD - %s | Parameters TTI %ds | Switch-Off in %ds",_targetgroupname,_tti,delay))
|
||||||
|
timer.scheduleFunction(SuppressionStart,{_targetgroup,_targetgroupname, SEADGroup},SuppressionStartTime)
|
||||||
|
timer.scheduleFunction(SuppressionStop,{_targetgroup,_targetgroupname},SuppressionEndTime)
|
||||||
|
self.SuppressedGroups[_targetgroupname] = true
|
||||||
|
if self.UseCallBack then
|
||||||
|
local object = self.CallBack
|
||||||
|
object:SeadSuppressionPlanned(_targetgroup,_targetgroupname,SuppressionStartTime,SuppressionEndTime, SEADGroup)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- (Internal) Detects if an SAM site was shot with an anti radiation missile. In this case, take evasive actions based on the skill level set within the ME.
|
||||||
|
-- @param #SEAD self
|
||||||
-- @param Core.Event#EVENTDATA EventData
|
-- @param Core.Event#EVENTDATA EventData
|
||||||
|
-- @return #SEAD self
|
||||||
function SEAD:HandleEventShot( EventData )
|
function SEAD:HandleEventShot( EventData )
|
||||||
self:T( { EventData.id } )
|
self:T( { EventData.id } )
|
||||||
local SEADPlane = EventData.IniUnit -- Wrapper.Unit#UNIT
|
local SEADPlane = EventData.IniUnit -- Wrapper.Unit#UNIT
|
||||||
|
local SEADGroup = EventData.IniGroup -- Wrapper.Group#GROUP
|
||||||
local SEADPlanePos = SEADPlane:GetCoordinate() -- Core.Point#COORDINATE
|
local SEADPlanePos = SEADPlane:GetCoordinate() -- Core.Point#COORDINATE
|
||||||
local SEADUnit = EventData.IniDCSUnit
|
local SEADUnit = EventData.IniDCSUnit
|
||||||
local SEADUnitName = EventData.IniDCSUnitName
|
local SEADUnitName = EventData.IniDCSUnitName
|
||||||
@@ -227,100 +459,62 @@ function SEAD:HandleEventShot( EventData )
|
|||||||
|
|
||||||
self:T( "*** SEAD - Missile Launched = " .. SEADWeaponName)
|
self:T( "*** SEAD - Missile Launched = " .. SEADWeaponName)
|
||||||
--self:T({ SEADWeapon })
|
--self:T({ SEADWeapon })
|
||||||
|
|
||||||
if self:_CheckHarms(SEADWeaponName) then
|
if self:_CheckHarms(SEADWeaponName) then
|
||||||
self:T( '*** SEAD - Weapon Match' )
|
self:T( '*** SEAD - Weapon Match' )
|
||||||
local _targetskill = "Random"
|
local _targetskill = "Random"
|
||||||
local _targetgroupname = "none"
|
local _targetgroupname = "none"
|
||||||
local _target = EventData.Weapon:getTarget() -- Identify target
|
local _target = EventData.Weapon:getTarget() -- Identify target
|
||||||
local _targetUnit = UNIT:Find(_target) -- Wrapper.Unit#UNIT
|
if not _target or self.debug then -- AGM-88 or 154 w/o target data
|
||||||
|
self:E("***** SEAD - No target data for " .. (SEADWeaponName or "None"))
|
||||||
|
if string.find(SEADWeaponName,"AGM_88",1,true) or string.find(SEADWeaponName,"AGM_154",1,true) then
|
||||||
|
self:I("**** Tracking AGM-88/154 with no target data.")
|
||||||
|
local pos0 = SEADPlane:GetCoordinate()
|
||||||
|
local fheight = SEADPlane:GetHeight()
|
||||||
|
self:__CalculateHitZone(20,SEADWeapon,pos0,fheight,SEADGroup,SEADWeaponName)
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
local targetcat = _target:getCategory() -- Identify category
|
||||||
|
local _targetUnit = nil -- Wrapper.Unit#UNIT
|
||||||
local _targetgroup = nil -- Wrapper.Group#GROUP
|
local _targetgroup = nil -- Wrapper.Group#GROUP
|
||||||
if _targetUnit and _targetUnit:IsAlive() then
|
self:T(string.format("*** Targetcat = %d",targetcat))
|
||||||
_targetgroup = _targetUnit:GetGroup()
|
if targetcat == Object.Category.UNIT then -- UNIT
|
||||||
_targetgroupname = _targetgroup:GetName() -- group name
|
self:T("*** Target Category UNIT")
|
||||||
local _targetUnitName = _targetUnit:GetName()
|
_targetUnit = UNIT:Find(_target) -- Wrapper.Unit#UNIT
|
||||||
_targetUnit:GetSkill()
|
if _targetUnit and _targetUnit:IsAlive() then
|
||||||
_targetskill = _targetUnit:GetSkill()
|
_targetgroup = _targetUnit:GetGroup()
|
||||||
|
_targetgroupname = _targetgroup:GetName() -- group name
|
||||||
|
local _targetUnitName = _targetUnit:GetName()
|
||||||
|
_targetUnit:GetSkill()
|
||||||
|
_targetskill = _targetUnit:GetSkill()
|
||||||
|
end
|
||||||
|
elseif targetcat == Object.Category.STATIC then
|
||||||
|
self:T("*** Target Category STATIC")
|
||||||
|
local seadset = SET_GROUP:New():FilterPrefixes(self.SEADGroupPrefixes):FilterOnce()
|
||||||
|
local targetpoint = _target:getPoint() or {x=0,y=0,z=0}
|
||||||
|
local tgtcoord = COORDINATE:NewFromVec3(targetpoint)
|
||||||
|
local tgtgrp = seadset:FindNearestGroupFromPointVec2(tgtcoord)
|
||||||
|
if tgtgrp and tgtgrp:IsAlive() then
|
||||||
|
_targetgroup = tgtgrp
|
||||||
|
_targetgroupname = tgtgrp:GetName() -- group name
|
||||||
|
_targetskill = tgtgrp:GetUnit(1):GetSkill()
|
||||||
|
self:T("*** Found Target = ".. _targetgroupname)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
-- see if we are shot at
|
-- see if we are shot at
|
||||||
local SEADGroupFound = false
|
local SEADGroupFound = false
|
||||||
for SEADGroupPrefixID, SEADGroupPrefix in pairs( self.SEADGroupPrefixes ) do
|
for SEADGroupPrefixID, SEADGroupPrefix in pairs( self.SEADGroupPrefixes ) do
|
||||||
self:T( SEADGroupPrefix )
|
self:T("Target = ".. _targetgroupname .. " | Prefix = " .. SEADGroupPrefix )
|
||||||
if string.find( _targetgroupname, SEADGroupPrefix, 1, true ) then
|
if string.find( _targetgroupname, SEADGroupPrefix,1,true ) then
|
||||||
SEADGroupFound = true
|
SEADGroupFound = true
|
||||||
self:T( '*** SEAD - Group Match Found' )
|
self:T( '*** SEAD - Group Match Found' )
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if SEADGroupFound == true then -- yes we are being attacked
|
if SEADGroupFound == true then -- yes we are being attacked
|
||||||
if _targetskill == "Random" then -- when skill is random, choose a skill
|
self:ManageEvasion(_targetskill,_targetgroup,SEADPlanePos,SEADWeaponName,SEADGroup)
|
||||||
local Skills = { "Average", "Good", "High", "Excellent" }
|
|
||||||
_targetskill = Skills[ math.random(1,4) ]
|
|
||||||
end
|
|
||||||
--self:T( _targetskill )
|
|
||||||
if self.TargetSkill[_targetskill] then
|
|
||||||
local _evade = math.random (1,100) -- random number for chance of evading action
|
|
||||||
if (_evade > self.TargetSkill[_targetskill].Evade) then
|
|
||||||
self:T("*** SEAD - Evading")
|
|
||||||
-- calculate distance of attacker
|
|
||||||
local _targetpos = _targetgroup:GetCoordinate()
|
|
||||||
local _distance = self:_GetDistance(SEADPlanePos, _targetpos)
|
|
||||||
-- weapon speed
|
|
||||||
local hit, data = self:_CheckHarms(SEADWeaponName)
|
|
||||||
local wpnspeed = 666 -- ;)
|
|
||||||
local reach = 10
|
|
||||||
if hit then
|
|
||||||
local wpndata = SEAD.HarmData[data]
|
|
||||||
reach = wpndata[1] * 1,1
|
|
||||||
local mach = wpndata[2]
|
|
||||||
wpnspeed = math.floor(mach * 340.29)
|
|
||||||
end
|
|
||||||
-- time to impact
|
|
||||||
local _tti = math.floor(_distance / wpnspeed) -- estimated impact time
|
|
||||||
if _distance > 0 then
|
|
||||||
_distance = math.floor(_distance / 1000) -- km
|
|
||||||
else
|
|
||||||
_distance = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
self:T( string.format("*** SEAD - target skill %s, distance %dkm, reach %dkm, tti %dsec", _targetskill, _distance,reach,_tti ))
|
|
||||||
|
|
||||||
if reach >= _distance then
|
|
||||||
self:T("*** SEAD - Shot in Reach")
|
|
||||||
|
|
||||||
local function SuppressionStart(args)
|
|
||||||
self:T(string.format("*** SEAD - %s Radar Off & Relocating",args[2]))
|
|
||||||
local grp = args[1] -- Wrapper.Group#GROUP
|
|
||||||
grp:OptionAlarmStateGreen()
|
|
||||||
grp:RelocateGroundRandomInRadius(20,300,false,false,"Diamond")
|
|
||||||
end
|
|
||||||
|
|
||||||
local function SuppressionStop(args)
|
|
||||||
self:T(string.format("*** SEAD - %s Radar On",args[2]))
|
|
||||||
local grp = args[1] -- Wrapper.Group#GROUP
|
|
||||||
grp:OptionAlarmStateRed()
|
|
||||||
grp:OptionEngageRange(self.EngagementRange)
|
|
||||||
self.SuppressedGroups[args[2]] = false
|
|
||||||
end
|
|
||||||
|
|
||||||
-- randomize switch-on time
|
|
||||||
local delay = math.random(self.TargetSkill[_targetskill].DelayOn[1], self.TargetSkill[_targetskill].DelayOn[2])
|
|
||||||
if delay > _tti then delay = delay / 2 end -- speed up
|
|
||||||
if _tti > (3*delay) then delay = (_tti / 2) * 0.9 end -- shot from afar
|
|
||||||
|
|
||||||
local SuppressionStartTime = timer.getTime() + delay
|
|
||||||
local SuppressionEndTime = timer.getTime() + _tti + self.Padding
|
|
||||||
|
|
||||||
if not self.SuppressedGroups[_targetgroupname] then
|
|
||||||
self:T(string.format("*** SEAD - %s | Parameters TTI %ds | Switch-Off in %ds",_targetgroupname,_tti,delay))
|
|
||||||
timer.scheduleFunction(SuppressionStart,{_targetgroup,_targetgroupname},SuppressionStartTime)
|
|
||||||
timer.scheduleFunction(SuppressionStop,{_targetgroup,_targetgroupname},SuppressionEndTime)
|
|
||||||
self.SuppressedGroups[_targetgroupname] = true
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
-- @module Functional.Shorad
|
-- @module Functional.Shorad
|
||||||
-- @image Functional.Shorad.jpg
|
-- @image Functional.Shorad.jpg
|
||||||
--
|
--
|
||||||
-- Date: July 2021
|
-- Date: Nov 2021
|
||||||
|
|
||||||
-------------------------------------------------------------------------
|
-------------------------------------------------------------------------
|
||||||
--- **SHORAD** class, extends Core.Base#BASE
|
--- **SHORAD** class, extends Core.Base#BASE
|
||||||
@@ -41,6 +41,7 @@
|
|||||||
-- @field #boolean UseEmOnOff Decide if we are using Emission on/off (default) or AlarmState red/green.
|
-- @field #boolean UseEmOnOff Decide if we are using Emission on/off (default) or AlarmState red/green.
|
||||||
-- @extends Core.Base#BASE
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
|
|
||||||
--- *Good friends are worth defending.* Mr Tushman, Wonder (the Movie)
|
--- *Good friends are worth defending.* Mr Tushman, Wonder (the Movie)
|
||||||
--
|
--
|
||||||
-- Simple Class for a more intelligent Short Range Air Defense System
|
-- Simple Class for a more intelligent Short Range Air Defense System
|
||||||
@@ -131,6 +132,7 @@ do
|
|||||||
["Kh29"] = "Kh29",
|
["Kh29"] = "Kh29",
|
||||||
["Kh31"] = "Kh31",
|
["Kh31"] = "Kh31",
|
||||||
["Kh66"] = "Kh66",
|
["Kh66"] = "Kh66",
|
||||||
|
--["BGM_109"] = "BGM_109",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Instantiates a new SHORAD object
|
--- Instantiates a new SHORAD object
|
||||||
@@ -138,13 +140,13 @@ do
|
|||||||
-- @param #string Name Name of this SHORAD
|
-- @param #string Name Name of this SHORAD
|
||||||
-- @param #string ShoradPrefix Filter for the Shorad #SET_GROUP
|
-- @param #string ShoradPrefix Filter for the Shorad #SET_GROUP
|
||||||
-- @param Core.Set#SET_GROUP Samset The #SET_GROUP of SAM sites to defend
|
-- @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 Radius Defense radius in meters, used to switch on SHORAD groups **within** this radius
|
||||||
-- @param #number ActiveTimer Determines how many seconds the systems stay on red alert after wake-up call
|
-- @param #number ActiveTimer Determines how many seconds the systems stay on red alert after wake-up call
|
||||||
-- @param #string Coalition Coalition, i.e. "blue", "red", or "neutral"
|
-- @param #string Coalition Coalition, i.e. "blue", "red", or "neutral"
|
||||||
-- @param #boolean UseEmOnOff Use Emissions On/Off rather than Alarm State Red/Green (default: use Emissions switch)
|
-- @param #boolean UseEmOnOff Use Emissions On/Off rather than Alarm State Red/Green (default: use Emissions switch)
|
||||||
-- @retunr #SHORAD self
|
-- @retunr #SHORAD self
|
||||||
function SHORAD:New(Name, ShoradPrefix, Samset, Radius, ActiveTimer, Coalition, UseEmOnOff)
|
function SHORAD:New(Name, ShoradPrefix, Samset, Radius, ActiveTimer, Coalition, UseEmOnOff)
|
||||||
local self = BASE:Inherit( self, BASE:New() )
|
local self = BASE:Inherit( self, FSM:New() )
|
||||||
self:T({Name, ShoradPrefix, Samset, Radius, ActiveTimer, Coalition})
|
self:T({Name, ShoradPrefix, Samset, Radius, ActiveTimer, Coalition})
|
||||||
|
|
||||||
local GroupSet = SET_GROUP:New():FilterPrefixes(ShoradPrefix):FilterCoalitions(Coalition):FilterCategoryGround():FilterStart()
|
local GroupSet = SET_GROUP:New():FilterPrefixes(ShoradPrefix):FilterCoalitions(Coalition):FilterCategoryGround():FilterStart()
|
||||||
@@ -162,11 +164,17 @@ do
|
|||||||
self.DefenseLowProb = 70 -- probability to detect a missile shot, low margin
|
self.DefenseLowProb = 70 -- probability to detect a missile shot, low margin
|
||||||
self.DefenseHighProb = 90 -- probability to detect a missile shot, high margin
|
self.DefenseHighProb = 90 -- probability to detect a missile shot, high margin
|
||||||
self.UseEmOnOff = UseEmOnOff or false -- Decide if we are using Emission on/off (default) or AlarmState red/green
|
self.UseEmOnOff = UseEmOnOff or false -- Decide if we are using Emission on/off (default) or AlarmState red/green
|
||||||
self:I("*** SHORAD - Started Version 0.2.8")
|
self:I("*** SHORAD - Started Version 0.3.1")
|
||||||
-- Set the string id for output to DCS.log file.
|
-- Set the string id for output to DCS.log file.
|
||||||
self.lid=string.format("SHORAD %s | ", self.name)
|
self.lid=string.format("SHORAD %s | ", self.name)
|
||||||
self:_InitState()
|
self:_InitState()
|
||||||
self:HandleEvent(EVENTS.Shot, self.HandleEventShot)
|
self:HandleEvent(EVENTS.Shot, self.HandleEventShot)
|
||||||
|
|
||||||
|
-- Start State.
|
||||||
|
self:SetStartState("Running")
|
||||||
|
self:AddTransition("*", "WakeUpShorad", "*")
|
||||||
|
self:AddTransition("*", "CalculateHitZone", "*")
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -310,7 +318,7 @@ do
|
|||||||
local hit = false
|
local hit = false
|
||||||
if self.DefendHarms then
|
if self.DefendHarms then
|
||||||
for _,_name in pairs (SHORAD.Harms) do
|
for _,_name in pairs (SHORAD.Harms) do
|
||||||
if string.find(WeaponName,_name,1) then hit = true end
|
if string.find(WeaponName,_name,1,true) then hit = true end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return hit
|
return hit
|
||||||
@@ -326,7 +334,7 @@ do
|
|||||||
local hit = false
|
local hit = false
|
||||||
if self.DefendMavs then
|
if self.DefendMavs then
|
||||||
for _,_name in pairs (SHORAD.Mavs) do
|
for _,_name in pairs (SHORAD.Mavs) do
|
||||||
if string.find(WeaponName,_name,1) then hit = true end
|
if string.find(WeaponName,_name,1,true) then hit = true end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return hit
|
return hit
|
||||||
@@ -367,7 +375,7 @@ do
|
|||||||
local returnname = false
|
local returnname = false
|
||||||
for _,_groups in pairs (shoradset) do
|
for _,_groups in pairs (shoradset) do
|
||||||
local groupname = _groups:GetName()
|
local groupname = _groups:GetName()
|
||||||
if string.find(groupname, tgtgrp, 1) then
|
if string.find(groupname, tgtgrp, 1, true) then
|
||||||
returnname = true
|
returnname = true
|
||||||
--_groups:RelocateGroundRandomInRadius(7,100,false,false) -- be a bit evasive
|
--_groups:RelocateGroundRandomInRadius(7,100,false,false) -- be a bit evasive
|
||||||
end
|
end
|
||||||
@@ -388,7 +396,7 @@ do
|
|||||||
local returnname = false
|
local returnname = false
|
||||||
for _,_groups in pairs (shoradset) do
|
for _,_groups in pairs (shoradset) do
|
||||||
local groupname = _groups:GetName()
|
local groupname = _groups:GetName()
|
||||||
if string.find(groupname, tgtgrp, 1) then
|
if string.find(groupname, tgtgrp, 1, true) then
|
||||||
returnname = true
|
returnname = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -400,6 +408,7 @@ do
|
|||||||
-- @return #boolean Returns true for a detection, else false
|
-- @return #boolean Returns true for a detection, else false
|
||||||
function SHORAD:_ShotIsDetected()
|
function SHORAD:_ShotIsDetected()
|
||||||
self:T(self.lid .. " _ShotIsDetected")
|
self:T(self.lid .. " _ShotIsDetected")
|
||||||
|
if self.debug then return true end
|
||||||
local IsDetected = false
|
local IsDetected = false
|
||||||
local DetectionProb = math.random(self.DefenseLowProb, self.DefenseHighProb) -- reference value
|
local DetectionProb = math.random(self.DefenseLowProb, self.DefenseHighProb) -- reference value
|
||||||
local ActualDetection = math.random(1,100) -- value for this shot
|
local ActualDetection = math.random(1,100) -- value for this shot
|
||||||
@@ -423,7 +432,7 @@ do
|
|||||||
-- mymantis = MANTIS:New("BlueMantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")
|
-- mymantis = MANTIS:New("BlueMantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")
|
||||||
-- mymantis:AddShorad(myshorad,720)
|
-- mymantis:AddShorad(myshorad,720)
|
||||||
-- mymantis:Start()
|
-- mymantis:Start()
|
||||||
function SHORAD:WakeUpShorad(TargetGroup, Radius, ActiveTimer, TargetCat)
|
function SHORAD:onafterWakeUpShorad(From, Event, To, TargetGroup, Radius, ActiveTimer, TargetCat)
|
||||||
self:T(self.lid .. " WakeUpShorad")
|
self:T(self.lid .. " WakeUpShorad")
|
||||||
self:T({TargetGroup, Radius, ActiveTimer, TargetCat})
|
self:T({TargetGroup, Radius, ActiveTimer, TargetCat})
|
||||||
local targetcat = TargetCat or Object.Category.UNIT
|
local targetcat = TargetCat or Object.Category.UNIT
|
||||||
@@ -477,6 +486,76 @@ do
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- (Internal) Calculate hit zone of an AGM-88
|
||||||
|
-- @param #SHORAD self
|
||||||
|
-- @param #table SEADWeapon DCS.Weapon object
|
||||||
|
-- @param Core.Point#COORDINATE pos0 Position of the plane when it fired
|
||||||
|
-- @param #number height Height when the missile was fired
|
||||||
|
-- @param Wrapper.Group#GROUP SEADGroup Attacker group
|
||||||
|
-- @return #SHORAD self
|
||||||
|
function SHORAD:onafterCalculateHitZone(From,Event,To,SEADWeapon,pos0,height,SEADGroup)
|
||||||
|
self:T("**** Calculating hit zone")
|
||||||
|
if SEADWeapon and SEADWeapon:isExist() then
|
||||||
|
--local pos = SEADWeapon:getPoint()
|
||||||
|
|
||||||
|
-- postion and height
|
||||||
|
local position = SEADWeapon:getPosition()
|
||||||
|
local mheight = height
|
||||||
|
-- heading
|
||||||
|
local wph = math.atan2(position.x.z, position.x.x)
|
||||||
|
if wph < 0 then
|
||||||
|
wph=wph+2*math.pi
|
||||||
|
end
|
||||||
|
wph=math.deg(wph)
|
||||||
|
|
||||||
|
-- velocity
|
||||||
|
local wpndata = SEAD.HarmData["AGM_88"]
|
||||||
|
local mveloc = math.floor(wpndata[2] * 340.29)
|
||||||
|
local c1 = (2*mheight*9.81)/(mveloc^2)
|
||||||
|
local c2 = (mveloc^2) / 9.81
|
||||||
|
local Ropt = c2 * math.sqrt(c1+1)
|
||||||
|
if height <= 5000 then
|
||||||
|
Ropt = Ropt * 0.72
|
||||||
|
elseif height <= 7500 then
|
||||||
|
Ropt = Ropt * 0.82
|
||||||
|
elseif height <= 10000 then
|
||||||
|
Ropt = Ropt * 0.87
|
||||||
|
elseif height <= 12500 then
|
||||||
|
Ropt = Ropt * 0.98
|
||||||
|
end
|
||||||
|
|
||||||
|
-- look at a couple of zones across the trajectory
|
||||||
|
for n=1,3 do
|
||||||
|
local dist = Ropt - ((n-1)*20000)
|
||||||
|
local predpos= pos0:Translate(dist,wph)
|
||||||
|
if predpos then
|
||||||
|
|
||||||
|
local targetzone = ZONE_RADIUS:New("Target Zone",predpos:GetVec2(),20000)
|
||||||
|
|
||||||
|
if self.debug then
|
||||||
|
predpos:MarkToAll(string.format("height=%dm | heading=%d | velocity=%ddeg | Ropt=%dm",mheight,wph,mveloc,Ropt),false)
|
||||||
|
targetzone:DrawZone(coalition.side.BLUE,{0,0,1},0.2,nil,nil,3,true)
|
||||||
|
end
|
||||||
|
|
||||||
|
local seadset = self.Groupset
|
||||||
|
local tgtcoord = targetzone:GetRandomPointVec2()
|
||||||
|
local tgtgrp = seadset:FindNearestGroupFromPointVec2(tgtcoord)
|
||||||
|
local _targetgroup = nil
|
||||||
|
local _targetgroupname = "none"
|
||||||
|
local _targetskill = "Random"
|
||||||
|
if tgtgrp and tgtgrp:IsAlive() then
|
||||||
|
_targetgroup = tgtgrp
|
||||||
|
_targetgroupname = tgtgrp:GetName() -- group name
|
||||||
|
_targetskill = tgtgrp:GetUnit(1):GetSkill()
|
||||||
|
self:T("*** Found Target = ".. _targetgroupname)
|
||||||
|
self:WakeUpShorad(_targetgroupname, self.Radius, self.ActiveTimer, Object.Category.UNIT)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- Main function - work on the EventData
|
--- Main function - work on the EventData
|
||||||
-- @param #SHORAD self
|
-- @param #SHORAD self
|
||||||
-- @param Core.Event#EVENTDATA EventData The event details table data set
|
-- @param Core.Event#EVENTDATA EventData The event details table data set
|
||||||
@@ -504,13 +583,48 @@ do
|
|||||||
if (self:_CheckHarms(ShootingWeaponName) or self:_CheckMavs(ShootingWeaponName)) and IsDetected then
|
if (self:_CheckHarms(ShootingWeaponName) or self:_CheckMavs(ShootingWeaponName)) and IsDetected then
|
||||||
-- get target data
|
-- get target data
|
||||||
local targetdata = EventData.Weapon:getTarget() -- Identify target
|
local targetdata = EventData.Weapon:getTarget() -- Identify target
|
||||||
|
-- Is there target data?
|
||||||
|
if not targetdata or self.debug then
|
||||||
|
if string.find(ShootingWeaponName,"AGM_88",1,true) then
|
||||||
|
self:I("**** Tracking AGM-88 with no target data.")
|
||||||
|
local pos0 = EventData.IniUnit:GetCoordinate()
|
||||||
|
local fheight = EventData.IniUnit:GetHeight()
|
||||||
|
self:__CalculateHitZone(20,ShootingWeapon,pos0,fheight,EventData.IniGroup)
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
local targetcat = targetdata:getCategory() -- Identify category
|
local targetcat = targetdata:getCategory() -- Identify category
|
||||||
self:T(string.format("Target Category (3=STATIC, 1=UNIT)= %s",tostring(targetcat)))
|
self:T(string.format("Target Category (3=STATIC, 1=UNIT)= %s",tostring(targetcat)))
|
||||||
|
self:T({targetdata})
|
||||||
local targetunit = nil
|
local targetunit = nil
|
||||||
if targetcat == Object.Category.UNIT then -- UNIT
|
if targetcat == Object.Category.UNIT then -- UNIT
|
||||||
targetunit = UNIT:Find(targetdata)
|
targetunit = UNIT:Find(targetdata)
|
||||||
elseif targetcat == Object.Category.STATIC then -- STATIC
|
elseif targetcat == Object.Category.STATIC then -- STATIC
|
||||||
targetunit = STATIC:Find(targetdata)
|
--self:T("Static Target Data")
|
||||||
|
--self:T({targetdata:isExist()})
|
||||||
|
--self:T({targetdata:getPoint()})
|
||||||
|
local tgtcoord = COORDINATE:NewFromVec3(targetdata:getPoint())
|
||||||
|
--tgtcoord:MarkToAll("Missile Target",true)
|
||||||
|
|
||||||
|
local tgtgrp1 = self.Samset:FindNearestGroupFromPointVec2(tgtcoord)
|
||||||
|
local tgtcoord1 = tgtgrp1:GetCoordinate()
|
||||||
|
--tgtcoord1:MarkToAll("Close target SAM",true)
|
||||||
|
|
||||||
|
local tgtgrp2 = self.Groupset:FindNearestGroupFromPointVec2(tgtcoord)
|
||||||
|
local tgtcoord2 = tgtgrp2:GetCoordinate()
|
||||||
|
--tgtcoord2:MarkToAll("Close target SHORAD",true)
|
||||||
|
|
||||||
|
local dist1 = tgtcoord:Get2DDistance(tgtcoord1)
|
||||||
|
local dist2 = tgtcoord:Get2DDistance(tgtcoord2)
|
||||||
|
|
||||||
|
if dist1 < dist2 then
|
||||||
|
targetunit = tgtgrp1
|
||||||
|
targetcat = Object.Category.UNIT
|
||||||
|
else
|
||||||
|
targetunit = tgtgrp2
|
||||||
|
targetcat = Object.Category.UNIT
|
||||||
|
end
|
||||||
end
|
end
|
||||||
--local targetunitname = Unit.getName(targetdata) -- Unit name
|
--local targetunitname = Unit.getName(targetdata) -- Unit name
|
||||||
if targetunit and targetunit:IsAlive() then
|
if targetunit and targetunit:IsAlive() then
|
||||||
@@ -519,7 +633,11 @@ do
|
|||||||
local targetgroup = nil
|
local targetgroup = nil
|
||||||
local targetgroupname = "none"
|
local targetgroupname = "none"
|
||||||
if targetcat == Object.Category.UNIT then
|
if targetcat == Object.Category.UNIT then
|
||||||
targetgroup = targetunit:GetGroup()
|
if targetunit.ClassName == "UNIT" then
|
||||||
|
targetgroup = targetunit:GetGroup()
|
||||||
|
elseif targetunit.ClassName == "GROUP" then
|
||||||
|
targetgroup = targetunit
|
||||||
|
end
|
||||||
targetgroupname = targetgroup:GetName() -- group name
|
targetgroupname = targetgroup:GetName() -- group name
|
||||||
elseif targetcat == Object.Category.STATIC then
|
elseif targetcat == Object.Category.STATIC then
|
||||||
targetgroup = targetunit
|
targetgroup = targetunit
|
||||||
@@ -540,6 +658,7 @@ do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
--
|
--
|
||||||
end
|
end
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -804,7 +804,7 @@ do -- ZONE_CAPTURE_COALITION
|
|||||||
return IsEmpty
|
return IsEmpty
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Check if zone is "Guarded", i.e. only one (the defending) coaliton is present inside the zone.
|
--- Check if zone is "Guarded", i.e. only one (the defending) coalition is present inside the zone.
|
||||||
-- @param #ZONE_CAPTURE_COALITION self
|
-- @param #ZONE_CAPTURE_COALITION self
|
||||||
-- @return #boolean self:IsAllInZoneOfCoalition( self.Coalition )
|
-- @return #boolean self:IsAllInZoneOfCoalition( self.Coalition )
|
||||||
function ZONE_CAPTURE_COALITION:IsGuarded()
|
function ZONE_CAPTURE_COALITION:IsGuarded()
|
||||||
@@ -826,7 +826,7 @@ do -- ZONE_CAPTURE_COALITION
|
|||||||
return IsCaptured
|
return IsCaptured
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Check if zone is "Attacked", i.e. another coaliton entered the zone.
|
--- Check if zone is "Attacked", i.e. another coalition entered the zone.
|
||||||
-- @param #ZONE_CAPTURE_COALITION self
|
-- @param #ZONE_CAPTURE_COALITION self
|
||||||
-- @return #boolean self:IsSomeInZoneOfCoalition( self.Coalition )
|
-- @return #boolean self:IsSomeInZoneOfCoalition( self.Coalition )
|
||||||
function ZONE_CAPTURE_COALITION:IsAttacked()
|
function ZONE_CAPTURE_COALITION:IsAttacked()
|
||||||
@@ -899,24 +899,23 @@ do -- ZONE_CAPTURE_COALITION
|
|||||||
end
|
end
|
||||||
self:I(text)
|
self:I(text)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- Misc Functions
|
-- Misc Functions
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
--- Update Mark on F10 map.
|
--- Update Mark on F10 map.
|
||||||
-- @param #ZONE_CAPTURE_COALITION self
|
-- @param #ZONE_CAPTURE_COALITION self
|
||||||
function ZONE_CAPTURE_COALITION:Mark()
|
function ZONE_CAPTURE_COALITION:Mark()
|
||||||
|
|
||||||
if self.MarkOn then
|
if self.MarkOn then
|
||||||
|
|
||||||
local Coord = self:GetCoordinate()
|
local Coord = self:GetCoordinate()
|
||||||
local ZoneName = self:GetZoneName()
|
local ZoneName = self:GetZoneName()
|
||||||
local State = self:GetState()
|
local State = self:GetState()
|
||||||
|
|
||||||
-- Remove marks.
|
-- Remove marks.
|
||||||
if self.MarkRed then
|
if self.MarkRed then
|
||||||
Coord:RemoveMark(self.MarkRed)
|
Coord:RemoveMark(self.MarkRed)
|
||||||
@@ -924,21 +923,21 @@ do -- ZONE_CAPTURE_COALITION
|
|||||||
if self.MarkBlue then
|
if self.MarkBlue then
|
||||||
Coord:RemoveMark(self.MarkBlue)
|
Coord:RemoveMark(self.MarkBlue)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Create new marks for each coaliton.
|
-- Create new marks for each coalition.
|
||||||
if self.Coalition == coalition.side.BLUE then
|
if self.Coalition == coalition.side.BLUE then
|
||||||
self.MarkBlue = Coord:MarkToCoalitionBlue( "Coalition: Blue\nGuard Zone: " .. ZoneName .. "\nStatus: " .. State )
|
self.MarkBlue = Coord:MarkToCoalitionBlue( "Coalition: Blue\nGuard Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||||
self.MarkRed = Coord:MarkToCoalitionRed( "Coalition: Blue\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State )
|
self.MarkRed = Coord:MarkToCoalitionRed( "Coalition: Blue\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||||
elseif self.Coalition == coalition.side.RED then
|
elseif self.Coalition == coalition.side.RED then
|
||||||
self.MarkRed = Coord:MarkToCoalitionRed( "Coalition: Red\nGuard Zone: " .. ZoneName .. "\nStatus: " .. State )
|
self.MarkRed = Coord:MarkToCoalitionRed( "Coalition: Red\nGuard Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||||
self.MarkBlue = Coord:MarkToCoalitionBlue( "Coalition: Red\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State )
|
self.MarkBlue = Coord:MarkToCoalitionBlue( "Coalition: Red\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||||
else
|
else
|
||||||
self.MarkRed = Coord:MarkToCoalitionRed( "Coalition: Neutral\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State )
|
self.MarkRed = Coord:MarkToCoalitionRed( "Coalition: Neutral\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||||
self.MarkBlue = Coord:MarkToCoalitionBlue( "Coalition: Neutral\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State )
|
self.MarkBlue = Coord:MarkToCoalitionBlue( "Coalition: Neutral\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
--- **Functional (WIP)** -- Base class that models processes to achieve goals involving a Zone for a Coalition.
|
--- **Functional (WIP)** -- Base class that models processes to achieve goals involving a Zone for a Coalition.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ZONE_GOAL_COALITION models processes that have a Goal with a defined achievement involving a Zone for a Coalition.
|
-- ZONE_GOAL_COALITION models processes that have a Goal with a defined achievement involving a Zone for a Coalition.
|
||||||
-- Derived classes implement the ways how the achievements can be realized.
|
-- Derived classes implement the ways how the achievements can be realized.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ### Author: **FlightControl**
|
-- ### Author: **FlightControl**
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- @module Functional.ZoneGoalCoalition
|
-- @module Functional.ZoneGoalCoalition
|
||||||
-- @image MOOSE.JPG
|
-- @image MOOSE.JPG
|
||||||
|
|
||||||
@@ -24,34 +24,33 @@ do -- ZoneGoal
|
|||||||
-- @field #table ObjectCategories Table of object categories that are able to hold a zone. Default is UNITS and STATICS.
|
-- @field #table ObjectCategories Table of object categories that are able to hold a zone. Default is UNITS and STATICS.
|
||||||
-- @extends Functional.ZoneGoal#ZONE_GOAL
|
-- @extends Functional.ZoneGoal#ZONE_GOAL
|
||||||
|
|
||||||
|
--- ZONE_GOAL_COALITION models processes that have a Goal with a defined achievement involving a Zone for a Coalition.
|
||||||
--- ZONE_GOAL_COALITION models processes that have a Goal with a defined achievement involving a Zone for a Coalition.
|
|
||||||
-- Derived classes implement the ways how the achievements can be realized.
|
-- Derived classes implement the ways how the achievements can be realized.
|
||||||
--
|
--
|
||||||
-- ## 1. ZONE_GOAL_COALITION constructor
|
-- ## 1. ZONE_GOAL_COALITION constructor
|
||||||
--
|
--
|
||||||
-- * @{#ZONE_GOAL_COALITION.New}(): Creates a new ZONE_GOAL_COALITION object.
|
-- * @{#ZONE_GOAL_COALITION.New}(): Creates a new ZONE_GOAL_COALITION object.
|
||||||
--
|
--
|
||||||
-- ## 2. ZONE_GOAL_COALITION is a finite state machine (FSM).
|
-- ## 2. ZONE_GOAL_COALITION is a finite state machine (FSM).
|
||||||
--
|
--
|
||||||
-- ### 2.1 ZONE_GOAL_COALITION States
|
-- ### 2.1 ZONE_GOAL_COALITION States
|
||||||
--
|
--
|
||||||
-- ### 2.2 ZONE_GOAL_COALITION Events
|
-- ### 2.2 ZONE_GOAL_COALITION Events
|
||||||
--
|
--
|
||||||
-- ### 2.3 ZONE_GOAL_COALITION State Machine
|
-- ### 2.3 ZONE_GOAL_COALITION State Machine
|
||||||
--
|
--
|
||||||
-- @field #ZONE_GOAL_COALITION
|
-- @field #ZONE_GOAL_COALITION
|
||||||
ZONE_GOAL_COALITION = {
|
ZONE_GOAL_COALITION = {
|
||||||
ClassName = "ZONE_GOAL_COALITION",
|
ClassName = "ZONE_GOAL_COALITION",
|
||||||
Coalition = nil,
|
Coalition = nil,
|
||||||
PreviousCoaliton = nil,
|
PreviousCoalition = nil,
|
||||||
UnitCategories = nil,
|
UnitCategories = nil,
|
||||||
ObjectCategories = nil,
|
ObjectCategories = nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
--- @field #table ZONE_GOAL_COALITION.States
|
--- @field #table ZONE_GOAL_COALITION.States
|
||||||
ZONE_GOAL_COALITION.States = {}
|
ZONE_GOAL_COALITION.States = {}
|
||||||
|
|
||||||
--- ZONE_GOAL_COALITION Constructor.
|
--- ZONE_GOAL_COALITION Constructor.
|
||||||
-- @param #ZONE_GOAL_COALITION self
|
-- @param #ZONE_GOAL_COALITION self
|
||||||
-- @param Core.Zone#ZONE Zone A @{Zone} object with the goal to be achieved.
|
-- @param Core.Zone#ZONE Zone A @{Zone} object with the goal to be achieved.
|
||||||
@@ -59,33 +58,32 @@ do -- ZoneGoal
|
|||||||
-- @param #table UnitCategories Table of unit categories. See [DCS Class Unit](https://wiki.hoggitworld.com/view/DCS_Class_Unit). Default {Unit.Category.GROUND_UNIT}.
|
-- @param #table UnitCategories Table of unit categories. See [DCS Class Unit](https://wiki.hoggitworld.com/view/DCS_Class_Unit). Default {Unit.Category.GROUND_UNIT}.
|
||||||
-- @return #ZONE_GOAL_COALITION
|
-- @return #ZONE_GOAL_COALITION
|
||||||
function ZONE_GOAL_COALITION:New( Zone, Coalition, UnitCategories )
|
function ZONE_GOAL_COALITION:New( Zone, Coalition, UnitCategories )
|
||||||
|
|
||||||
if not Zone then
|
if not Zone then
|
||||||
BASE:E("ERROR: No Zone specified in ZONE_GOAL_COALITON!")
|
BASE:E( "ERROR: No Zone specified in ZONE_GOAL_COALITION!" )
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Inherit ZONE_GOAL.
|
-- Inherit ZONE_GOAL.
|
||||||
local self = BASE:Inherit( self, ZONE_GOAL:New( Zone ) ) -- #ZONE_GOAL_COALITION
|
local self = BASE:Inherit( self, ZONE_GOAL:New( Zone ) ) -- #ZONE_GOAL_COALITION
|
||||||
self:F( { Zone = Zone, Coalition = Coalition } )
|
self:F( { Zone = Zone, Coalition = Coalition } )
|
||||||
|
|
||||||
-- Set initial owner.
|
-- Set initial owner.
|
||||||
self:SetCoalition( Coalition or coalition.side.NEUTRAL)
|
self:SetCoalition( Coalition or coalition.side.NEUTRAL )
|
||||||
|
|
||||||
-- Set default unit and object categories for the zone scan.
|
-- Set default unit and object categories for the zone scan.
|
||||||
self:SetUnitCategories(UnitCategories)
|
self:SetUnitCategories( UnitCategories )
|
||||||
self:SetObjectCategories()
|
self:SetObjectCategories()
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Set the owning coalition of the zone.
|
--- Set the owning coalition of the zone.
|
||||||
-- @param #ZONE_GOAL_COALITION self
|
-- @param #ZONE_GOAL_COALITION self
|
||||||
-- @param DCSCoalition.DCSCoalition#coalition Coalition The coalition ID, e.g. *coalition.side.RED*.
|
-- @param DCSCoalition.DCSCoalition#coalition Coalition The coalition ID, e.g. *coalition.side.RED*.
|
||||||
-- @return #ZONE_GOAL_COALITION
|
-- @return #ZONE_GOAL_COALITION
|
||||||
function ZONE_GOAL_COALITION:SetCoalition( Coalition )
|
function ZONE_GOAL_COALITION:SetCoalition( Coalition )
|
||||||
self.PreviousCoalition=self.Coalition or Coalition
|
self.PreviousCoalition = self.Coalition or Coalition
|
||||||
self.Coalition = Coalition
|
self.Coalition = Coalition
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@@ -95,31 +93,31 @@ do -- ZoneGoal
|
|||||||
-- @param #table UnitCategories Table of unit categories. See [DCS Class Unit](https://wiki.hoggitworld.com/view/DCS_Class_Unit). Default {Unit.Category.GROUND_UNIT}.
|
-- @param #table UnitCategories Table of unit categories. See [DCS Class Unit](https://wiki.hoggitworld.com/view/DCS_Class_Unit). Default {Unit.Category.GROUND_UNIT}.
|
||||||
-- @return #ZONE_GOAL_COALITION
|
-- @return #ZONE_GOAL_COALITION
|
||||||
function ZONE_GOAL_COALITION:SetUnitCategories( UnitCategories )
|
function ZONE_GOAL_COALITION:SetUnitCategories( UnitCategories )
|
||||||
|
|
||||||
if UnitCategories and type(UnitCategories)~="table" then
|
if UnitCategories and type( UnitCategories ) ~= "table" then
|
||||||
UnitCategories={UnitCategories}
|
UnitCategories = { UnitCategories }
|
||||||
end
|
end
|
||||||
|
|
||||||
self.UnitCategories=UnitCategories or {Unit.Category.GROUND_UNIT}
|
self.UnitCategories = UnitCategories or { Unit.Category.GROUND_UNIT }
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set the owning coalition of the zone.
|
--- Set the owning coalition of the zone.
|
||||||
-- @param #ZONE_GOAL_COALITION self
|
-- @param #ZONE_GOAL_COALITION self
|
||||||
-- @param #table ObjectCategories Table of unit categories. See [DCS Class Object](https://wiki.hoggitworld.com/view/DCS_Class_Object). Default {Object.Category.UNIT, Object.Category.STATIC}, i.e. all UNITS and STATICS.
|
-- @param #table ObjectCategories Table of unit categories. See [DCS Class Object](https://wiki.hoggitworld.com/view/DCS_Class_Object). Default {Object.Category.UNIT, Object.Category.STATIC}, i.e. all UNITS and STATICS.
|
||||||
-- @return #ZONE_GOAL_COALITION
|
-- @return #ZONE_GOAL_COALITION
|
||||||
function ZONE_GOAL_COALITION:SetObjectCategories( ObjectCategories )
|
function ZONE_GOAL_COALITION:SetObjectCategories( ObjectCategories )
|
||||||
|
|
||||||
if ObjectCategories and type(ObjectCategories)~="table" then
|
if ObjectCategories and type( ObjectCategories ) ~= "table" then
|
||||||
ObjectCategories={ObjectCategories}
|
ObjectCategories = { ObjectCategories }
|
||||||
end
|
end
|
||||||
|
|
||||||
self.ObjectCategories=ObjectCategories or {Object.Category.UNIT, Object.Category.STATIC}
|
self.ObjectCategories = ObjectCategories or { Object.Category.UNIT, Object.Category.STATIC }
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get the owning coalition of the zone.
|
--- Get the owning coalition of the zone.
|
||||||
-- @param #ZONE_GOAL_COALITION self
|
-- @param #ZONE_GOAL_COALITION self
|
||||||
-- @return DCSCoalition.DCSCoalition#coalition Coalition.
|
-- @return DCSCoalition.DCSCoalition#coalition Coalition.
|
||||||
@@ -127,39 +125,37 @@ do -- ZoneGoal
|
|||||||
return self.Coalition
|
return self.Coalition
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get the previous coaliton, i.e. the one owning the zone before the current one.
|
--- Get the previous coalition, i.e. the one owning the zone before the current one.
|
||||||
-- @param #ZONE_GOAL_COALITION self
|
-- @param #ZONE_GOAL_COALITION self
|
||||||
-- @return DCSCoalition.DCSCoalition#coalition Coalition.
|
-- @return DCSCoalition.DCSCoalition#coalition Coalition.
|
||||||
function ZONE_GOAL_COALITION:GetPreviousCoalition()
|
function ZONE_GOAL_COALITION:GetPreviousCoalition()
|
||||||
return self.PreviousCoalition
|
return self.PreviousCoalition
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Get the owning coalition name of the zone.
|
--- Get the owning coalition name of the zone.
|
||||||
-- @param #ZONE_GOAL_COALITION self
|
-- @param #ZONE_GOAL_COALITION self
|
||||||
-- @return #string Coalition name.
|
-- @return #string Coalition name.
|
||||||
function ZONE_GOAL_COALITION:GetCoalitionName()
|
function ZONE_GOAL_COALITION:GetCoalitionName()
|
||||||
return UTILS.GetCoalitionName(self.Coalition)
|
return UTILS.GetCoalitionName( self.Coalition )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Check status Coalition ownership.
|
--- Check status Coalition ownership.
|
||||||
-- @param #ZONE_GOAL_COALITION self
|
-- @param #ZONE_GOAL_COALITION self
|
||||||
-- @return #ZONE_GOAL_COALITION
|
-- @return #ZONE_GOAL_COALITION
|
||||||
function ZONE_GOAL_COALITION:StatusZone()
|
function ZONE_GOAL_COALITION:StatusZone()
|
||||||
|
|
||||||
-- Get current state.
|
-- Get current state.
|
||||||
local State = self:GetState()
|
local State = self:GetState()
|
||||||
|
|
||||||
-- Debug text.
|
-- Debug text.
|
||||||
local text=string.format("Zone state=%s, Owner=%s, Scanning...", State, self:GetCoalitionName())
|
local text = string.format( "Zone state=%s, Owner=%s, Scanning...", State, self:GetCoalitionName() )
|
||||||
self:F(text)
|
self:F( text )
|
||||||
|
|
||||||
-- Scan zone.
|
-- Scan zone.
|
||||||
self:Scan( self.ObjectCategories, self.UnitCategories )
|
self:Scan( self.ObjectCategories, self.UnitCategories )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,13 @@ __Moose.Include( 'Scripts/Moose/Utilities/Utils.lua' )
|
|||||||
__Moose.Include( 'Scripts/Moose/Utilities/Profiler.lua' )
|
__Moose.Include( 'Scripts/Moose/Utilities/Profiler.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Utilities/Templates.lua' )
|
__Moose.Include( 'Scripts/Moose/Utilities/Templates.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Utilities/STTS.lua' )
|
__Moose.Include( 'Scripts/Moose/Utilities/STTS.lua' )
|
||||||
|
__Moose.Include( 'Scripts/Moose/Utilities/FiFo.lua' )
|
||||||
|
__Moose.Include( 'Scripts/Moose/Utilities/Socket.lua' )
|
||||||
|
|
||||||
__Moose.Include( 'Scripts/Moose/Core/Base.lua' )
|
__Moose.Include( 'Scripts/Moose/Core/Base.lua' )
|
||||||
|
__Moose.Include( 'Scripts/Moose/Core/Astar.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Core/Beacon.lua' )
|
__Moose.Include( 'Scripts/Moose/Core/Beacon.lua' )
|
||||||
|
__Moose.Include( 'Scripts/Moose/Core/Condition.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Core/UserFlag.lua' )
|
__Moose.Include( 'Scripts/Moose/Core/UserFlag.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Core/Report.lua' )
|
__Moose.Include( 'Scripts/Moose/Core/Report.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Core/Scheduler.lua' )
|
__Moose.Include( 'Scripts/Moose/Core/Scheduler.lua' )
|
||||||
@@ -27,6 +31,8 @@ __Moose.Include( 'Scripts/Moose/Core/SpawnStatic.lua' )
|
|||||||
__Moose.Include( 'Scripts/Moose/Core/Timer.lua' )
|
__Moose.Include( 'Scripts/Moose/Core/Timer.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Core/Goal.lua' )
|
__Moose.Include( 'Scripts/Moose/Core/Goal.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Core/Spot.lua' )
|
__Moose.Include( 'Scripts/Moose/Core/Spot.lua' )
|
||||||
|
__Moose.Include( 'Scripts/Moose/Core/MarkerOps_Base.lua' )
|
||||||
|
__Moose.Include( 'Scripts/Moose/Core/TextAndSound.lua' )
|
||||||
|
|
||||||
__Moose.Include( 'Scripts/Moose/Wrapper/Object.lua' )
|
__Moose.Include( 'Scripts/Moose/Wrapper/Object.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Wrapper/Identifiable.lua' )
|
__Moose.Include( 'Scripts/Moose/Wrapper/Identifiable.lua' )
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -31,7 +31,7 @@
|
|||||||
-- @field #string tankergroupname Name of the late activated tanker template group.
|
-- @field #string tankergroupname Name of the late activated tanker template group.
|
||||||
-- @field Wrapper.Group#GROUP tanker Tanker group.
|
-- @field Wrapper.Group#GROUP tanker Tanker group.
|
||||||
-- @field Wrapper.Airbase#AIRBASE airbase The home airbase object of the tanker. Normally the aircraft carrier.
|
-- @field Wrapper.Airbase#AIRBASE airbase The home airbase object of the tanker. Normally the aircraft carrier.
|
||||||
-- @field Core.Radio#BEACON beacon Tanker TACAN beacon.
|
-- @field Core.Beacon#BEACON beacon Tanker TACAN beacon.
|
||||||
-- @field #number TACANchannel TACAN channel. Default 1.
|
-- @field #number TACANchannel TACAN channel. Default 1.
|
||||||
-- @field #string TACANmode TACAN mode, i.e. "X" or "Y". Default "Y". Use only "Y" for AA TACAN stations!
|
-- @field #string TACANmode TACAN mode, i.e. "X" or "Y". Default "Y". Use only "Y" for AA TACAN stations!
|
||||||
-- @field #string TACANmorse TACAN morse code. Three letters identifying the TACAN station. Default "TKR".
|
-- @field #string TACANmorse TACAN morse code. Three letters identifying the TACAN station. Default "TKR".
|
||||||
@@ -784,10 +784,11 @@ end
|
|||||||
-- @param #RECOVERYTANKER self
|
-- @param #RECOVERYTANKER self
|
||||||
-- @param #number channel TACAN channel. Default 1.
|
-- @param #number channel TACAN channel. Default 1.
|
||||||
-- @param #string morse TACAN morse code identifier. Three letters. Default "TKR".
|
-- @param #string morse TACAN morse code identifier. Three letters. Default "TKR".
|
||||||
|
-- @param #string mode TACAN mode, which can be either "Y" (default) or "X".
|
||||||
-- @return #RECOVERYTANKER self
|
-- @return #RECOVERYTANKER self
|
||||||
function RECOVERYTANKER:SetTACAN(channel, morse)
|
function RECOVERYTANKER:SetTACAN(channel, morse, mode)
|
||||||
self.TACANchannel=channel or 1
|
self.TACANchannel=channel or 1
|
||||||
self.TACANmode="Y"
|
self.TACANmode=mode or "Y"
|
||||||
self.TACANmorse=morse or "TKR"
|
self.TACANmorse=morse or "TKR"
|
||||||
self.TACANon=true
|
self.TACANon=true
|
||||||
return self
|
return self
|
||||||
@@ -1625,7 +1626,6 @@ function RECOVERYTANKER:_ActivateTACAN(delay)
|
|||||||
if delay and delay>0 then
|
if delay and delay>0 then
|
||||||
|
|
||||||
-- Schedule TACAN activation.
|
-- Schedule TACAN activation.
|
||||||
--SCHEDULER:New(nil, self._ActivateTACAN, {self}, delay)
|
|
||||||
self:ScheduleOnce(delay, RECOVERYTANKER._ActivateTACAN, self)
|
self:ScheduleOnce(delay, RECOVERYTANKER._ActivateTACAN, self)
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -802,7 +802,7 @@ function RESCUEHELO:_OnEventCrashOrEject(EventData)
|
|||||||
-- Debug.
|
-- Debug.
|
||||||
local text=string.format("Unit %s crashed or ejected.", unitname)
|
local text=string.format("Unit %s crashed or ejected.", unitname)
|
||||||
MESSAGE:New(text, 10, "DEBUG"):ToAllIf(self.Debug)
|
MESSAGE:New(text, 10, "DEBUG"):ToAllIf(self.Debug)
|
||||||
self:I(self.lid..text)
|
self:T(self.lid..text)
|
||||||
|
|
||||||
-- Get coordinate of unit.
|
-- Get coordinate of unit.
|
||||||
local coord=unit:GetCoordinate()
|
local coord=unit:GetCoordinate()
|
||||||
|
|||||||
@@ -1,28 +1,28 @@
|
|||||||
--- **Sound** - Radio transmissions.
|
--- **Sound** - Radio transmissions.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ## Features:
|
-- ## Features:
|
||||||
--
|
--
|
||||||
-- * Provide radio functionality to broadcast radio transmissions.
|
-- * Provide radio functionality to broadcast radio transmissions.
|
||||||
--
|
--
|
||||||
-- What are radio communications in DCS?
|
-- What are radio communications in DCS?
|
||||||
--
|
--
|
||||||
-- * Radio transmissions consist of **sound files** that are broadcasted on a specific **frequency** (e.g. 115MHz) and **modulation** (e.g. AM),
|
-- * Radio transmissions consist of **sound files** that are broadcasted on a specific **frequency** (e.g. 115MHz) and **modulation** (e.g. AM),
|
||||||
-- * They can be **subtitled** for a specific **duration**, the **power** in Watts of the transmiter's antenna can be set, and the transmission can be **looped**.
|
-- * They can be **subtitled** for a specific **duration**, the **power** in Watts of the transmiter's antenna can be set, and the transmission can be **looped**.
|
||||||
--
|
--
|
||||||
-- How to supply DCS my own Sound Files?
|
-- How to supply DCS my own Sound Files?
|
||||||
--
|
--
|
||||||
-- * Your sound files need to be encoded in **.ogg** or .wav,
|
-- * Your sound files need to be encoded in **.ogg** or .wav,
|
||||||
-- * Your sound files should be **as tiny as possible**. It is suggested you encode in .ogg with low bitrate and sampling settings,
|
-- * Your sound files should be **as tiny as possible**. It is suggested you encode in .ogg with low bitrate and sampling settings,
|
||||||
-- * They need to be added in .\l10n\DEFAULT\ in you .miz file (wich can be decompressed like a .zip file),
|
-- * They need to be added in .\l10n\DEFAULT\ in you .miz file (wich can be decompressed like a .zip file),
|
||||||
-- * For simplicity sake, you can **let DCS' Mission Editor add the file** itself, by creating a new Trigger with the action "Sound to Country", and choosing your sound file and a country you don't use in your mission.
|
-- * For simplicity sake, you can **let DCS' Mission Editor add the file** itself, by creating a new Trigger with the action "Sound to Country", and choosing your sound file and a country you don't use in your mission.
|
||||||
--
|
--
|
||||||
-- Due to weird DCS quirks, **radio communications behave differently** if sent by a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP} or by any other @{Wrapper.Positionable#POSITIONABLE}
|
-- Due to weird DCS quirks, **radio communications behave differently** if sent by a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP} or by any other @{Wrapper.Positionable#POSITIONABLE}
|
||||||
--
|
--
|
||||||
-- * If the transmitter is a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}, DCS will set the power of the transmission automatically,
|
-- * If the transmitter is a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}, DCS will set the power of the transmission automatically,
|
||||||
-- * If the transmitter is any other @{Wrapper.Positionable#POSITIONABLE}, the transmisison can't be subtitled or looped.
|
-- * If the transmitter is any other @{Wrapper.Positionable#POSITIONABLE}, the transmisison can't be subtitled or looped.
|
||||||
--
|
--
|
||||||
-- Note that obviously, the **frequency** and the **modulation** of the transmission are important only if the players are piloting an **Advanced System Modelling** enabled aircraft,
|
-- Note that obviously, the **frequency** and the **modulation** of the transmission are important only if the players are piloting an **Advanced System Modelling** enabled aircraft,
|
||||||
-- like the A10C or the Mirage 2000C. They will **hear the transmission** if they are tuned on the **right frequency and modulation** (and if they are close enough - more on that below).
|
-- like the A10C or the Mirage 2000C. They will **hear the transmission** if they are tuned on the **right frequency and modulation** (and if they are close enough - more on that below).
|
||||||
-- If an FC3 aircraft is used, it will **hear every communication, whatever the frequency and the modulation** is set to. The same is true for TACAN beacons. If your aircraft isn't compatible,
|
-- If an FC3 aircraft is used, it will **hear every communication, whatever the frequency and the modulation** is set to. The same is true for TACAN beacons. If your aircraft isn't compatible,
|
||||||
@@ -37,41 +37,41 @@
|
|||||||
|
|
||||||
|
|
||||||
--- *It's not true I had nothing on, I had the radio on.* -- Marilyn Monroe
|
--- *It's not true I had nothing on, I had the radio on.* -- Marilyn Monroe
|
||||||
--
|
--
|
||||||
-- # RADIO usage
|
-- # RADIO usage
|
||||||
--
|
--
|
||||||
-- There are 3 steps to a successful radio transmission.
|
-- There are 3 steps to a successful radio transmission.
|
||||||
--
|
--
|
||||||
-- * First, you need to **"add a @{#RADIO} object** to your @{Wrapper.Positionable#POSITIONABLE}. This is done using the @{Wrapper.Positionable#POSITIONABLE.GetRadio}() function,
|
-- * First, you need to **"add a @{#RADIO} object** to your @{Wrapper.Positionable#POSITIONABLE}. This is done using the @{Wrapper.Positionable#POSITIONABLE.GetRadio}() function,
|
||||||
-- * Then, you will **set the relevant parameters** to the transmission (see below),
|
-- * Then, you will **set the relevant parameters** to the transmission (see below),
|
||||||
-- * When done, you can actually **broadcast the transmission** (i.e. play the sound) with the @{RADIO.Broadcast}() function.
|
-- * When done, you can actually **broadcast the transmission** (i.e. play the sound) with the @{RADIO.Broadcast}() function.
|
||||||
--
|
--
|
||||||
-- Methods to set relevant parameters for both a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP} or any other @{Wrapper.Positionable#POSITIONABLE}
|
-- Methods to set relevant parameters for both a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP} or any other @{Wrapper.Positionable#POSITIONABLE}
|
||||||
--
|
--
|
||||||
-- * @{#RADIO.SetFileName}() : Sets the file name of your sound file (e.g. "Noise.ogg"),
|
-- * @{#RADIO.SetFileName}() : Sets the file name of your sound file (e.g. "Noise.ogg"),
|
||||||
-- * @{#RADIO.SetFrequency}() : Sets the frequency of your transmission.
|
-- * @{#RADIO.SetFrequency}() : Sets the frequency of your transmission.
|
||||||
-- * @{#RADIO.SetModulation}() : Sets the modulation of your transmission.
|
-- * @{#RADIO.SetModulation}() : Sets the modulation of your transmission.
|
||||||
-- * @{#RADIO.SetLoop}() : Choose if you want the transmission to be looped. If you need your transmission to be looped, you might need a @{#BEACON} instead...
|
-- * @{#RADIO.SetLoop}() : Choose if you want the transmission to be looped. If you need your transmission to be looped, you might need a @{#BEACON} instead...
|
||||||
--
|
--
|
||||||
-- Additional Methods to set relevant parameters if the transmitter is a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}
|
-- Additional Methods to set relevant parameters if the transmitter is a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}
|
||||||
--
|
--
|
||||||
-- * @{#RADIO.SetSubtitle}() : Set both the subtitle and its duration,
|
-- * @{#RADIO.SetSubtitle}() : Set both the subtitle and its duration,
|
||||||
-- * @{#RADIO.NewUnitTransmission}() : Shortcut to set all the relevant parameters in one method call
|
-- * @{#RADIO.NewUnitTransmission}() : Shortcut to set all the relevant parameters in one method call
|
||||||
--
|
--
|
||||||
-- Additional Methods to set relevant parameters if the transmitter is any other @{Wrapper.Positionable#POSITIONABLE}
|
-- Additional Methods to set relevant parameters if the transmitter is any other @{Wrapper.Positionable#POSITIONABLE}
|
||||||
--
|
--
|
||||||
-- * @{#RADIO.SetPower}() : Sets the power of the antenna in Watts
|
-- * @{#RADIO.SetPower}() : Sets the power of the antenna in Watts
|
||||||
-- * @{#RADIO.NewGenericTransmission}() : Shortcut to set all the relevant parameters in one method call
|
-- * @{#RADIO.NewGenericTransmission}() : Shortcut to set all the relevant parameters in one method call
|
||||||
--
|
--
|
||||||
-- What is this power thing?
|
-- What is this power thing?
|
||||||
--
|
--
|
||||||
-- * If your transmission is sent by a @{Wrapper.Positionable#POSITIONABLE} other than a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}, you can set the power of the antenna,
|
-- * If your transmission is sent by a @{Wrapper.Positionable#POSITIONABLE} other than a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}, you can set the power of the antenna,
|
||||||
-- * Otherwise, DCS sets it automatically, depending on what's available on your Unit,
|
-- * Otherwise, DCS sets it automatically, depending on what's available on your Unit,
|
||||||
-- * If the player gets **too far** from the transmitter, or if the antenna is **too weak**, the transmission will **fade** and **become noisyer**,
|
-- * If the player gets **too far** from the transmitter, or if the antenna is **too weak**, the transmission will **fade** and **become noisyer**,
|
||||||
-- * This an automated DCS calculation you have no say on,
|
-- * This an automated DCS calculation you have no say on,
|
||||||
-- * For reference, a standard VOR station has a 100 W antenna, a standard AA TACAN has a 120 W antenna, and civilian ATC's antenna usually range between 300 and 500 W,
|
-- * For reference, a standard VOR station has a 100 W antenna, a standard AA TACAN has a 120 W antenna, and civilian ATC's antenna usually range between 300 and 500 W,
|
||||||
-- * Note that if the transmission has a subtitle, it will be readable, regardless of the quality of the transmission.
|
-- * Note that if the transmission has a subtitle, it will be readable, regardless of the quality of the transmission.
|
||||||
--
|
--
|
||||||
-- @type RADIO
|
-- @type RADIO
|
||||||
-- @field Wrapper.Controllable#CONTROLLABLE Positionable The @{#CONTROLLABLE} that will transmit the radio calls.
|
-- @field Wrapper.Controllable#CONTROLLABLE Positionable The @{#CONTROLLABLE} that will transmit the radio calls.
|
||||||
-- @field #string FileName Name of the sound file played.
|
-- @field #string FileName Name of the sound file played.
|
||||||
@@ -105,12 +105,12 @@ function RADIO:New(Positionable)
|
|||||||
-- Inherit base
|
-- Inherit base
|
||||||
local self = BASE:Inherit( self, BASE:New() ) -- Core.Radio#RADIO
|
local self = BASE:Inherit( self, BASE:New() ) -- Core.Radio#RADIO
|
||||||
self:F(Positionable)
|
self:F(Positionable)
|
||||||
|
|
||||||
if Positionable:GetPointVec2() then -- It's stupid, but the only way I found to make sure positionable is valid
|
if Positionable:GetPointVec2() then -- It's stupid, but the only way I found to make sure positionable is valid
|
||||||
self.Positionable = Positionable
|
self.Positionable = Positionable
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
self:E({error="The passed positionable is invalid, no RADIO created!", positionable=Positionable})
|
self:E({error="The passed positionable is invalid, no RADIO created!", positionable=Positionable})
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
@@ -137,19 +137,19 @@ end
|
|||||||
-- @return #RADIO self
|
-- @return #RADIO self
|
||||||
function RADIO:SetFileName(FileName)
|
function RADIO:SetFileName(FileName)
|
||||||
self:F2(FileName)
|
self:F2(FileName)
|
||||||
|
|
||||||
if type(FileName) == "string" then
|
if type(FileName) == "string" then
|
||||||
|
|
||||||
if FileName:find(".ogg") or FileName:find(".wav") then
|
if FileName:find(".ogg") or FileName:find(".wav") then
|
||||||
if not FileName:find("l10n/DEFAULT/") then
|
if not FileName:find("l10n/DEFAULT/") then
|
||||||
FileName = "l10n/DEFAULT/" .. FileName
|
FileName = "l10n/DEFAULT/" .. FileName
|
||||||
end
|
end
|
||||||
|
|
||||||
self.FileName = FileName
|
self.FileName = FileName
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
self:E({"File name invalid. Maybe something wrong with the extension?", FileName})
|
self:E({"File name invalid. Maybe something wrong with the extension?", FileName})
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@@ -161,34 +161,34 @@ end
|
|||||||
-- @return #RADIO self
|
-- @return #RADIO self
|
||||||
function RADIO:SetFrequency(Frequency)
|
function RADIO:SetFrequency(Frequency)
|
||||||
self:F2(Frequency)
|
self:F2(Frequency)
|
||||||
|
|
||||||
if type(Frequency) == "number" then
|
if type(Frequency) == "number" then
|
||||||
|
|
||||||
-- If frequency is in range
|
-- If frequency is in range
|
||||||
-- if (Frequency >= 30 and Frequency <= 87.995) or (Frequency >= 108 and Frequency <= 173.995) or (Frequency >= 225 and Frequency <= 399.975) then
|
--if (Frequency >= 30 and Frequency <= 87.995) or (Frequency >= 108 and Frequency <= 173.995) or (Frequency >= 225 and Frequency <= 399.975) then
|
||||||
|
|
||||||
-- Convert frequency from MHz to Hz
|
-- Convert frequency from MHz to Hz
|
||||||
self.Frequency = Frequency * 1000000
|
self.Frequency = Frequency * 1000000
|
||||||
|
|
||||||
-- If the RADIO is attached to a UNIT or a GROUP, we need to send the DCS Command "SetFrequency" to change the UNIT or GROUP frequency
|
-- If the RADIO is attached to a UNIT or a GROUP, we need to send the DCS Command "SetFrequency" to change the UNIT or GROUP frequency
|
||||||
if self.Positionable.ClassName == "UNIT" or self.Positionable.ClassName == "GROUP" then
|
if self.Positionable.ClassName == "UNIT" or self.Positionable.ClassName == "GROUP" then
|
||||||
|
|
||||||
local commandSetFrequency={
|
local commandSetFrequency={
|
||||||
id = "SetFrequency",
|
id = "SetFrequency",
|
||||||
params = {
|
params = {
|
||||||
frequency = self.Frequency,
|
frequency = self.Frequency,
|
||||||
modulation = self.Modulation,
|
modulation = self.Modulation,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self:T2(commandSetFrequency)
|
self:T2(commandSetFrequency)
|
||||||
self.Positionable:SetCommand(commandSetFrequency)
|
self.Positionable:SetCommand(commandSetFrequency)
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
-- end
|
--end
|
||||||
end
|
end
|
||||||
|
|
||||||
self:E({"Frequency is not a number. Frequency unchanged.", Frequency})
|
self:E({"Frequency is not a number. Frequency unchanged.", Frequency})
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@@ -215,13 +215,13 @@ end
|
|||||||
-- @return #RADIO self
|
-- @return #RADIO self
|
||||||
function RADIO:SetPower(Power)
|
function RADIO:SetPower(Power)
|
||||||
self:F2(Power)
|
self:F2(Power)
|
||||||
|
|
||||||
if type(Power) == "number" then
|
if type(Power) == "number" then
|
||||||
self.Power = math.floor(math.abs(Power)) --TODO Find what is the maximum power allowed by DCS and limit power to that
|
self.Power = math.floor(math.abs(Power)) --TODO Find what is the maximum power allowed by DCS and limit power to that
|
||||||
else
|
else
|
||||||
self:E({"Power is invalid. Power unchanged.", self.Power})
|
self:E({"Power is invalid. Power unchanged.", self.Power})
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -249,7 +249,7 @@ end
|
|||||||
-- -- create the broadcaster and attaches it a RADIO
|
-- -- create the broadcaster and attaches it a RADIO
|
||||||
-- local MyUnit = UNIT:FindByName("MyUnit")
|
-- local MyUnit = UNIT:FindByName("MyUnit")
|
||||||
-- local MyUnitRadio = MyUnit:GetRadio()
|
-- local MyUnitRadio = MyUnit:GetRadio()
|
||||||
--
|
--
|
||||||
-- -- add a subtitle for the next transmission, which will be up for 10s
|
-- -- add a subtitle for the next transmission, which will be up for 10s
|
||||||
-- MyUnitRadio:SetSubtitle("My Subtitle, 10)
|
-- MyUnitRadio:SetSubtitle("My Subtitle, 10)
|
||||||
function RADIO:SetSubtitle(Subtitle, SubtitleDuration)
|
function RADIO:SetSubtitle(Subtitle, SubtitleDuration)
|
||||||
@@ -264,14 +264,14 @@ function RADIO:SetSubtitle(Subtitle, SubtitleDuration)
|
|||||||
self.SubtitleDuration = SubtitleDuration
|
self.SubtitleDuration = SubtitleDuration
|
||||||
else
|
else
|
||||||
self.SubtitleDuration = 0
|
self.SubtitleDuration = 0
|
||||||
self:E({"SubtitleDuration is invalid. SubtitleDuration reset.", self.SubtitleDuration})
|
self:E({"SubtitleDuration is invalid. SubtitleDuration reset.", self.SubtitleDuration})
|
||||||
end
|
end
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Create a new transmission, that is to say, populate the RADIO with relevant data
|
--- Create a new transmission, that is to say, populate the RADIO with relevant data
|
||||||
-- In this function the data is especially relevant if the broadcaster is anything but a UNIT or a GROUP,
|
-- In this function the data is especially relevant if the broadcaster is anything but a UNIT or a GROUP,
|
||||||
-- but it will work with a UNIT or a GROUP anyway.
|
-- but it will work with a UNIT or a GROUP anyway.
|
||||||
-- Only the #RADIO and the Filename are mandatory
|
-- Only the #RADIO and the Filename are mandatory
|
||||||
-- @param #RADIO self
|
-- @param #RADIO self
|
||||||
-- @param #string FileName Name of the sound file that will be transmitted.
|
-- @param #string FileName Name of the sound file that will be transmitted.
|
||||||
@@ -281,20 +281,20 @@ end
|
|||||||
-- @return #RADIO self
|
-- @return #RADIO self
|
||||||
function RADIO:NewGenericTransmission(FileName, Frequency, Modulation, Power, Loop)
|
function RADIO:NewGenericTransmission(FileName, Frequency, Modulation, Power, Loop)
|
||||||
self:F({FileName, Frequency, Modulation, Power})
|
self:F({FileName, Frequency, Modulation, Power})
|
||||||
|
|
||||||
self:SetFileName(FileName)
|
self:SetFileName(FileName)
|
||||||
if Frequency then self:SetFrequency(Frequency) end
|
if Frequency then self:SetFrequency(Frequency) end
|
||||||
if Modulation then self:SetModulation(Modulation) end
|
if Modulation then self:SetModulation(Modulation) end
|
||||||
if Power then self:SetPower(Power) end
|
if Power then self:SetPower(Power) end
|
||||||
if Loop then self:SetLoop(Loop) end
|
if Loop then self:SetLoop(Loop) end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Create a new transmission, that is to say, populate the RADIO with relevant data
|
--- Create a new transmission, that is to say, populate the RADIO with relevant data
|
||||||
-- In this function the data is especially relevant if the broadcaster is a UNIT or a GROUP,
|
-- In this function the data is especially relevant if the broadcaster is a UNIT or a GROUP,
|
||||||
-- but it will work for any @{Wrapper.Positionable#POSITIONABLE}.
|
-- but it will work for any @{Wrapper.Positionable#POSITIONABLE}.
|
||||||
-- Only the RADIO and the Filename are mandatory.
|
-- Only the RADIO and the Filename are mandatory.
|
||||||
-- @param #RADIO self
|
-- @param #RADIO self
|
||||||
-- @param #string FileName Name of sound file.
|
-- @param #string FileName Name of sound file.
|
||||||
@@ -316,20 +316,20 @@ function RADIO:NewUnitTransmission(FileName, Subtitle, SubtitleDuration, Frequen
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Set frequency.
|
-- Set frequency.
|
||||||
if Frequency then
|
if Frequency then
|
||||||
self:SetFrequency(Frequency)
|
self:SetFrequency(Frequency)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Set subtitle.
|
-- Set subtitle.
|
||||||
if Subtitle then
|
if Subtitle then
|
||||||
self:SetSubtitle(Subtitle, SubtitleDuration or 0)
|
self:SetSubtitle(Subtitle, SubtitleDuration or 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Set Looping.
|
-- Set Looping.
|
||||||
if Loop then
|
if Loop then
|
||||||
self:SetLoop(Loop)
|
self:SetLoop(Loop)
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -346,7 +346,7 @@ end
|
|||||||
-- @return #RADIO self
|
-- @return #RADIO self
|
||||||
function RADIO:Broadcast(viatrigger)
|
function RADIO:Broadcast(viatrigger)
|
||||||
self:F({viatrigger=viatrigger})
|
self:F({viatrigger=viatrigger})
|
||||||
|
|
||||||
-- If the POSITIONABLE is actually a UNIT or a GROUP, use the more complicated DCS command system.
|
-- If the POSITIONABLE is actually a UNIT or a GROUP, use the more complicated DCS command system.
|
||||||
if (self.Positionable.ClassName=="UNIT" or self.Positionable.ClassName=="GROUP") and (not viatrigger) then
|
if (self.Positionable.ClassName=="UNIT" or self.Positionable.ClassName=="GROUP") and (not viatrigger) then
|
||||||
self:T("Broadcasting from a UNIT or a GROUP")
|
self:T("Broadcasting from a UNIT or a GROUP")
|
||||||
@@ -359,7 +359,7 @@ function RADIO:Broadcast(viatrigger)
|
|||||||
subtitle = self.Subtitle,
|
subtitle = self.Subtitle,
|
||||||
loop = self.Loop,
|
loop = self.Loop,
|
||||||
}}
|
}}
|
||||||
|
|
||||||
self:T3(commandTransmitMessage)
|
self:T3(commandTransmitMessage)
|
||||||
self.Positionable:SetCommand(commandTransmitMessage)
|
self.Positionable:SetCommand(commandTransmitMessage)
|
||||||
else
|
else
|
||||||
@@ -368,7 +368,7 @@ function RADIO:Broadcast(viatrigger)
|
|||||||
self:T("Broadcasting from a POSITIONABLE")
|
self:T("Broadcasting from a POSITIONABLE")
|
||||||
trigger.action.radioTransmission(self.FileName, self.Positionable:GetPositionVec3(), self.Modulation, self.Loop, self.Frequency, self.Power, tostring(self.ID))
|
trigger.action.radioTransmission(self.FileName, self.Positionable:GetPositionVec3(), self.Modulation, self.Loop, self.Frequency, self.Power, tostring(self.ID))
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -380,11 +380,11 @@ end
|
|||||||
-- @return #RADIO self
|
-- @return #RADIO self
|
||||||
function RADIO:StopBroadcast()
|
function RADIO:StopBroadcast()
|
||||||
self:F()
|
self:F()
|
||||||
-- If the POSITIONABLE is a UNIT or a GROUP, stop the transmission with the DCS "StopTransmission" command
|
-- If the POSITIONABLE is a UNIT or a GROUP, stop the transmission with the DCS "StopTransmission" command
|
||||||
if self.Positionable.ClassName == "UNIT" or self.Positionable.ClassName == "GROUP" then
|
if self.Positionable.ClassName == "UNIT" or self.Positionable.ClassName == "GROUP" then
|
||||||
|
|
||||||
local commandStopTransmission={id="StopTransmission", params={}}
|
local commandStopTransmission={id="StopTransmission", params={}}
|
||||||
|
|
||||||
self.Positionable:SetCommand(commandStopTransmission)
|
self.Positionable:SetCommand(commandStopTransmission)
|
||||||
else
|
else
|
||||||
-- Else, we use the appropriate singleton funciton
|
-- Else, we use the appropriate singleton funciton
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
--- **Core** - Makes the radio talk.
|
--- **Core** - Makes the radio talk.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ## Features:
|
-- ## Features:
|
||||||
--
|
--
|
||||||
-- * Send text strings using a vocabulary that is converted in spoken language.
|
-- * Send text strings using a vocabulary that is converted in spoken language.
|
||||||
-- * Possiblity to implement multiple language.
|
-- * Possiblity to implement multiple language.
|
||||||
--
|
--
|
||||||
@@ -15,10 +15,10 @@
|
|||||||
-- @image Core_Radio.JPG
|
-- @image Core_Radio.JPG
|
||||||
|
|
||||||
--- Makes the radio speak.
|
--- Makes the radio speak.
|
||||||
--
|
--
|
||||||
-- # RADIOSPEECH usage
|
-- # RADIOSPEECH usage
|
||||||
--
|
--
|
||||||
--
|
--
|
||||||
-- @type RADIOSPEECH
|
-- @type RADIOSPEECH
|
||||||
-- @extends Core.RadioQueue#RADIOQUEUE
|
-- @extends Core.RadioQueue#RADIOQUEUE
|
||||||
RADIOSPEECH = {
|
RADIOSPEECH = {
|
||||||
@@ -59,24 +59,24 @@ RADIOSPEECH.Vocabulary.EN = {
|
|||||||
["70"] = { "70", 0.48 },
|
["70"] = { "70", 0.48 },
|
||||||
["80"] = { "80", 0.26 },
|
["80"] = { "80", 0.26 },
|
||||||
["90"] = { "90", 0.36 },
|
["90"] = { "90", 0.36 },
|
||||||
["100"] = { "100", 0.55 },
|
["100"] = { "100", 0.55 },
|
||||||
["200"] = { "200", 0.55 },
|
["200"] = { "200", 0.55 },
|
||||||
["300"] = { "300", 0.61 },
|
["300"] = { "300", 0.61 },
|
||||||
["400"] = { "400", 0.60 },
|
["400"] = { "400", 0.60 },
|
||||||
["500"] = { "500", 0.61 },
|
["500"] = { "500", 0.61 },
|
||||||
["600"] = { "600", 0.65 },
|
["600"] = { "600", 0.65 },
|
||||||
["700"] = { "700", 0.70 },
|
["700"] = { "700", 0.70 },
|
||||||
["800"] = { "800", 0.54 },
|
["800"] = { "800", 0.54 },
|
||||||
["900"] = { "900", 0.60 },
|
["900"] = { "900", 0.60 },
|
||||||
["1000"] = { "1000", 0.60 },
|
["1000"] = { "1000", 0.60 },
|
||||||
["2000"] = { "2000", 0.61 },
|
["2000"] = { "2000", 0.61 },
|
||||||
["3000"] = { "3000", 0.64 },
|
["3000"] = { "3000", 0.64 },
|
||||||
["4000"] = { "4000", 0.62 },
|
["4000"] = { "4000", 0.62 },
|
||||||
["5000"] = { "5000", 0.69 },
|
["5000"] = { "5000", 0.69 },
|
||||||
["6000"] = { "6000", 0.69 },
|
["6000"] = { "6000", 0.69 },
|
||||||
["7000"] = { "7000", 0.75 },
|
["7000"] = { "7000", 0.75 },
|
||||||
["8000"] = { "8000", 0.59 },
|
["8000"] = { "8000", 0.59 },
|
||||||
["9000"] = { "9000", 0.65 },
|
["9000"] = { "9000", 0.65 },
|
||||||
|
|
||||||
["chevy"] = { "chevy", 0.35 },
|
["chevy"] = { "chevy", 0.35 },
|
||||||
["colt"] = { "colt", 0.35 },
|
["colt"] = { "colt", 0.35 },
|
||||||
@@ -94,10 +94,10 @@ RADIOSPEECH.Vocabulary.EN = {
|
|||||||
["meters"] = { "meters", 0.41 },
|
["meters"] = { "meters", 0.41 },
|
||||||
["mi"] = { "miles", 0.45 },
|
["mi"] = { "miles", 0.45 },
|
||||||
["feet"] = { "feet", 0.29 },
|
["feet"] = { "feet", 0.29 },
|
||||||
|
|
||||||
["br"] = { "br", 1.1 },
|
["br"] = { "br", 1.1 },
|
||||||
["bra"] = { "bra", 0.3 },
|
["bra"] = { "bra", 0.3 },
|
||||||
|
|
||||||
|
|
||||||
["returning to base"] = { "returning_to_base", 0.85 },
|
["returning to base"] = { "returning_to_base", 0.85 },
|
||||||
["on route to ground target"] = { "on_route_to_ground_target", 1.05 },
|
["on route to ground target"] = { "on_route_to_ground_target", 1.05 },
|
||||||
@@ -143,24 +143,24 @@ RADIOSPEECH.Vocabulary.RU = {
|
|||||||
["70"] = { "70", 0.68 },
|
["70"] = { "70", 0.68 },
|
||||||
["80"] = { "80", 0.84 },
|
["80"] = { "80", 0.84 },
|
||||||
["90"] = { "90", 0.71 },
|
["90"] = { "90", 0.71 },
|
||||||
["100"] = { "100", 0.35 },
|
["100"] = { "100", 0.35 },
|
||||||
["200"] = { "200", 0.59 },
|
["200"] = { "200", 0.59 },
|
||||||
["300"] = { "300", 0.53 },
|
["300"] = { "300", 0.53 },
|
||||||
["400"] = { "400", 0.70 },
|
["400"] = { "400", 0.70 },
|
||||||
["500"] = { "500", 0.50 },
|
["500"] = { "500", 0.50 },
|
||||||
["600"] = { "600", 0.58 },
|
["600"] = { "600", 0.58 },
|
||||||
["700"] = { "700", 0.64 },
|
["700"] = { "700", 0.64 },
|
||||||
["800"] = { "800", 0.77 },
|
["800"] = { "800", 0.77 },
|
||||||
["900"] = { "900", 0.75 },
|
["900"] = { "900", 0.75 },
|
||||||
["1000"] = { "1000", 0.87 },
|
["1000"] = { "1000", 0.87 },
|
||||||
["2000"] = { "2000", 0.83 },
|
["2000"] = { "2000", 0.83 },
|
||||||
["3000"] = { "3000", 0.84 },
|
["3000"] = { "3000", 0.84 },
|
||||||
["4000"] = { "4000", 1.00 },
|
["4000"] = { "4000", 1.00 },
|
||||||
["5000"] = { "5000", 0.77 },
|
["5000"] = { "5000", 0.77 },
|
||||||
["6000"] = { "6000", 0.90 },
|
["6000"] = { "6000", 0.90 },
|
||||||
["7000"] = { "7000", 0.77 },
|
["7000"] = { "7000", 0.77 },
|
||||||
["8000"] = { "8000", 0.92 },
|
["8000"] = { "8000", 0.92 },
|
||||||
["9000"] = { "9000", 0.87 },
|
["9000"] = { "9000", 0.87 },
|
||||||
|
|
||||||
["градусы"] = { "degrees", 0.5 },
|
["градусы"] = { "degrees", 0.5 },
|
||||||
["километры"] = { "kilometers", 0.65 },
|
["километры"] = { "kilometers", 0.65 },
|
||||||
@@ -170,10 +170,11 @@ RADIOSPEECH.Vocabulary.RU = {
|
|||||||
["метров"] = { "meters", 0.41 },
|
["метров"] = { "meters", 0.41 },
|
||||||
["m"] = { "meters", 0.41 },
|
["m"] = { "meters", 0.41 },
|
||||||
["ноги"] = { "feet", 0.37 },
|
["ноги"] = { "feet", 0.37 },
|
||||||
|
|
||||||
["br"] = { "br", 1.1 },
|
["br"] = { "br", 1.1 },
|
||||||
["bra"] = { "bra", 0.3 },
|
["bra"] = { "bra", 0.3 },
|
||||||
|
|
||||||
|
|
||||||
["возвращение на базу"] = { "returning_to_base", 1.40 },
|
["возвращение на базу"] = { "returning_to_base", 1.40 },
|
||||||
["на пути к наземной цели"] = { "on_route_to_ground_target", 1.45 },
|
["на пути к наземной цели"] = { "on_route_to_ground_target", 1.45 },
|
||||||
["перехват боги"] = { "intercepting_bogeys", 1.22 },
|
["перехват боги"] = { "intercepting_bogeys", 1.22 },
|
||||||
@@ -199,11 +200,11 @@ function RADIOSPEECH:New(frequency, modulation)
|
|||||||
|
|
||||||
-- Inherit base
|
-- Inherit base
|
||||||
local self = BASE:Inherit( self, RADIOQUEUE:New( frequency, modulation ) ) -- #RADIOSPEECH
|
local self = BASE:Inherit( self, RADIOQUEUE:New( frequency, modulation ) ) -- #RADIOSPEECH
|
||||||
|
|
||||||
self.Language = "EN"
|
self.Language = "EN"
|
||||||
|
|
||||||
self:BuildTree()
|
self:BuildTree()
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -261,7 +262,7 @@ end
|
|||||||
function RADIOSPEECH:BuildTree()
|
function RADIOSPEECH:BuildTree()
|
||||||
|
|
||||||
self.Speech = {}
|
self.Speech = {}
|
||||||
|
|
||||||
for Language, Sentences in pairs( self.Vocabulary ) do
|
for Language, Sentences in pairs( self.Vocabulary ) do
|
||||||
self:I( { Language = Language, Sentences = Sentences })
|
self:I( { Language = Language, Sentences = Sentences })
|
||||||
self.Speech[Language] = {}
|
self.Speech[Language] = {}
|
||||||
@@ -270,7 +271,7 @@ function RADIOSPEECH:BuildTree()
|
|||||||
self:AddSentenceToSpeech( Sentence, self.Speech[Language], Sentence, Data )
|
self:AddSentenceToSpeech( Sentence, self.Speech[Language], Sentence, Data )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
self:I( { Speech = self.Speech } )
|
self:I( { Speech = self.Speech } )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
@@ -289,7 +290,7 @@ function RADIOSPEECH:SpeakWords( Sentence, Speech, Language )
|
|||||||
local Word, RemainderSentence = Sentence:match( "^[., ]*([^ .,]+)(.*)" )
|
local Word, RemainderSentence = Sentence:match( "^[., ]*([^ .,]+)(.*)" )
|
||||||
|
|
||||||
self:I( { Word = Word, Speech = Speech[Word], RemainderSentence = RemainderSentence } )
|
self:I( { Word = Word, Speech = Speech[Word], RemainderSentence = RemainderSentence } )
|
||||||
|
|
||||||
|
|
||||||
if Word then
|
if Word then
|
||||||
if Word ~= "" and tonumber(Word) == nil then
|
if Word ~= "" and tonumber(Word) == nil then
|
||||||
@@ -301,7 +302,7 @@ function RADIOSPEECH:SpeakWords( Sentence, Speech, Language )
|
|||||||
if Speech[Word].Next == nil then
|
if Speech[Word].Next == nil then
|
||||||
self:I( { Sentence = Speech[Word].Sentence, Data = Speech[Word].Data } )
|
self:I( { Sentence = Speech[Word].Sentence, Data = Speech[Word].Data } )
|
||||||
self:NewTransmission( Speech[Word].Data[1] .. ".wav", Speech[Word].Data[2], Language .. "/" )
|
self:NewTransmission( Speech[Word].Data[1] .. ".wav", Speech[Word].Data[2], Language .. "/" )
|
||||||
else
|
else
|
||||||
if RemainderSentence and RemainderSentence ~= "" then
|
if RemainderSentence and RemainderSentence ~= "" then
|
||||||
return self:SpeakWords( RemainderSentence, Speech[Word].Next, Language )
|
return self:SpeakWords( RemainderSentence, Speech[Word].Next, Language )
|
||||||
end
|
end
|
||||||
@@ -309,11 +310,11 @@ function RADIOSPEECH:SpeakWords( Sentence, Speech, Language )
|
|||||||
end
|
end
|
||||||
return RemainderSentence
|
return RemainderSentence
|
||||||
end
|
end
|
||||||
return OriginalSentence
|
return OriginalSentence
|
||||||
else
|
else
|
||||||
return ""
|
return ""
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Speak a sentence.
|
--- Speak a sentence.
|
||||||
@@ -332,7 +333,7 @@ function RADIOSPEECH:SpeakDigits( Sentence, Speech, Langauge )
|
|||||||
|
|
||||||
if Digits then
|
if Digits then
|
||||||
if Digits ~= "" and tonumber( Digits ) ~= nil then
|
if Digits ~= "" and tonumber( Digits ) ~= nil then
|
||||||
|
|
||||||
-- Construct numbers
|
-- Construct numbers
|
||||||
local Number = tonumber( Digits )
|
local Number = tonumber( Digits )
|
||||||
local Multiple = nil
|
local Multiple = nil
|
||||||
@@ -356,7 +357,7 @@ function RADIOSPEECH:SpeakDigits( Sentence, Speech, Langauge )
|
|||||||
end
|
end
|
||||||
return RemainderSentence
|
return RemainderSentence
|
||||||
end
|
end
|
||||||
return OriginalSentence
|
return OriginalSentence
|
||||||
else
|
else
|
||||||
return ""
|
return ""
|
||||||
end
|
end
|
||||||
@@ -373,26 +374,26 @@ function RADIOSPEECH:Speak( Sentence, Language )
|
|||||||
self:I( { Sentence, Language } )
|
self:I( { Sentence, Language } )
|
||||||
|
|
||||||
local Language = Language or "EN"
|
local Language = Language or "EN"
|
||||||
|
|
||||||
self:I( { Language = Language } )
|
self:I( { Language = Language } )
|
||||||
|
|
||||||
-- If there is no node for Speech, then we start at the first nodes of the language.
|
-- If there is no node for Speech, then we start at the first nodes of the language.
|
||||||
local Speech = self.Speech[Language]
|
local Speech = self.Speech[Language]
|
||||||
|
|
||||||
self:I( { Speech = Speech, Language = Language } )
|
self:I( { Speech = Speech, Language = Language } )
|
||||||
|
|
||||||
self:NewTransmission( "_In.wav", 0.52, Language .. "/" )
|
self:NewTransmission( "_In.wav", 0.52, Language .. "/" )
|
||||||
|
|
||||||
repeat
|
repeat
|
||||||
|
|
||||||
Sentence = self:SpeakWords( Sentence, Speech, Language )
|
Sentence = self:SpeakWords( Sentence, Speech, Language )
|
||||||
|
|
||||||
self:I( { Sentence = Sentence } )
|
self:I( { Sentence = Sentence } )
|
||||||
|
|
||||||
Sentence = self:SpeakDigits( Sentence, Speech, Language )
|
Sentence = self:SpeakDigits( Sentence, Speech, Language )
|
||||||
|
|
||||||
self:I( { Sentence = Sentence } )
|
self:I( { Sentence = Sentence } )
|
||||||
|
|
||||||
-- Sentence = self:SpeakSymbols( Sentence, Speech )
|
-- Sentence = self:SpeakSymbols( Sentence, Speech )
|
||||||
--
|
--
|
||||||
-- self:I( { Sentence = Sentence } )
|
-- self:I( { Sentence = Sentence } )
|
||||||
|
|||||||
@@ -45,14 +45,13 @@
|
|||||||
-- @field Core.Point#COORDINATE coordinate Coordinate from where the transmission is send.
|
-- @field Core.Point#COORDINATE coordinate Coordinate from where the transmission is send.
|
||||||
-- @field #string path Path to the SRS exe. This includes the final slash "/".
|
-- @field #string path Path to the SRS exe. This includes the final slash "/".
|
||||||
-- @field #string google Full path google credentials JSON file, e.g. "C:\Users\username\Downloads\service-account-file.json".
|
-- @field #string google Full path google credentials JSON file, e.g. "C:\Users\username\Downloads\service-account-file.json".
|
||||||
|
-- @field #string Label Label showing up on the SRS radio overlay. Default is "ROBOT". No spaces allowed.
|
||||||
-- @extends Core.Base#BASE
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
--- *It is a very sad thing that nowadays there is so little useless information.* - Oscar Wilde
|
--- *It is a very sad thing that nowadays there is so little useless information.* - Oscar Wilde
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- 
|
|
||||||
--
|
|
||||||
-- # The MSRS Concept
|
-- # The MSRS Concept
|
||||||
--
|
--
|
||||||
-- This class allows to broadcast sound files or text via Simple Radio Standalone (SRS).
|
-- This class allows to broadcast sound files or text via Simple Radio Standalone (SRS).
|
||||||
@@ -88,15 +87,40 @@
|
|||||||
--
|
--
|
||||||
-- Use a specific "culture" with the @{#MSRS.SetCulture} function, e.g. `:SetCulture("en-US")` or `:SetCulture("de-DE")`.
|
-- Use a specific "culture" with the @{#MSRS.SetCulture} function, e.g. `:SetCulture("en-US")` or `:SetCulture("de-DE")`.
|
||||||
--
|
--
|
||||||
|
-- ## Set Google
|
||||||
|
--
|
||||||
|
-- Use Google's text-to-speech engine with the @{#MSRS.SetGoogle} function, e.g. ':SetGoogle()'.
|
||||||
|
-- By enabling this it also allows you to utilize SSML in your text for added flexibilty.
|
||||||
|
-- For more information on setting up a cloud account, visit: https://cloud.google.com/text-to-speech
|
||||||
|
-- Google's supported SSML reference: https://cloud.google.com/text-to-speech/docs/ssml
|
||||||
|
--
|
||||||
|
-- **NOTE on using GOOGLE TTS with SRS:** You need to have the C# library installed in your SRS folder for Google to work.
|
||||||
|
-- You can obtain it e.g. here: [NuGet](https://www.nuget.org/packages/Grpc.Core)
|
||||||
|
--
|
||||||
|
-- **Pro-Tipp** - use the command line with power shell to call DCS-SR-ExternalAudio.exe - it will tell you what is missing.
|
||||||
|
-- and also the Google Console error, in case you have missed a step in setting up your Google TTS.
|
||||||
|
-- E.g. `.\DCS-SR-ExternalAudio.exe -t "Text Message" -f 255 -m AM -c 2 -s 2 -z -G "Path_To_You_Google.Json"`
|
||||||
|
-- Plays a message on 255AM for the blue coalition in-game.
|
||||||
|
--
|
||||||
-- ## Set Voice
|
-- ## Set Voice
|
||||||
--
|
--
|
||||||
-- Use a specifc voice with the @{#MSRS.SetVoice} function, e.g, `:SetVoice("Microsoft Hedda Desktop")`.
|
-- Use a specifc voice with the @{#MSRS.SetVoice} function, e.g, `:SetVoice("Microsoft Hedda Desktop")`.
|
||||||
-- Note that this must be installed on your windows system.
|
-- Note that this must be installed on your windows system.
|
||||||
|
-- If enabling SetGoogle(), you can use voices provided by Google
|
||||||
|
-- Google's supported voices: https://cloud.google.com/text-to-speech/docs/voices
|
||||||
--
|
--
|
||||||
-- ## Set Coordinate
|
-- ## Set Coordinate
|
||||||
--
|
--
|
||||||
-- Use @{#MSRS.SetCoordinate} to define the origin from where the transmission is broadcasted.
|
-- Use @{#MSRS.SetCoordinate} to define the origin from where the transmission is broadcasted.
|
||||||
--
|
--
|
||||||
|
-- ## Set SRS Port
|
||||||
|
--
|
||||||
|
-- Use @{#MSRS.SetPort} to define the SRS port. Defaults to 5002.
|
||||||
|
--
|
||||||
|
-- ## Set SRS Volume
|
||||||
|
--
|
||||||
|
-- Use @{#MSRS.SetVolume} to define the SRS volume. Defaults to 1.0. Allowed values are between 0.0 and 1.0, from silent to loudest.
|
||||||
|
--
|
||||||
-- @field #MSRS
|
-- @field #MSRS
|
||||||
MSRS = {
|
MSRS = {
|
||||||
ClassName = "MSRS",
|
ClassName = "MSRS",
|
||||||
@@ -112,11 +136,113 @@ MSRS = {
|
|||||||
volume = 1,
|
volume = 1,
|
||||||
speed = 1,
|
speed = 1,
|
||||||
coordinate = nil,
|
coordinate = nil,
|
||||||
|
Label = "ROBOT",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- MSRS class version.
|
--- MSRS class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
MSRS.version="0.0.3"
|
MSRS.version="0.1.0"
|
||||||
|
|
||||||
|
--- Voices
|
||||||
|
-- @type Voices
|
||||||
|
MSRS.Voices = {
|
||||||
|
Microsoft = {
|
||||||
|
["Hedda"] = "Microsoft Hedda Desktop", -- de-DE
|
||||||
|
["Hazel"] = "Microsoft Hazel Desktop", -- en-GB
|
||||||
|
["David"] = "Microsoft David Desktop", -- en-US
|
||||||
|
["Zira"] = "Microsoft Zira Desktop", -- en-US
|
||||||
|
["Hortense"] = "Microsoft Hortense Desktop", --fr-FR
|
||||||
|
},
|
||||||
|
Google = {
|
||||||
|
Standard = {
|
||||||
|
["en-AU-Standard-A"] = 'en-AU-Standard-A', -- [1] FEMALE
|
||||||
|
["en-AU-Standard-B"] = 'en-AU-Standard-B', -- [2] MALE
|
||||||
|
["en-AU-Standard-C"] = 'en-AU-Standard-C', -- [3] FEMALE
|
||||||
|
["en-AU-Standard-D"] = 'en-AU-Standard-D', -- [4] MALE
|
||||||
|
["en-IN-Standard-A"] = 'en-IN-Standard-A', -- [5] FEMALE
|
||||||
|
["en-IN-Standard-B"] = 'en-IN-Standard-B', -- [6] MALE
|
||||||
|
["en-IN-Standard-C"] = 'en-IN-Standard-C', -- [7] MALE
|
||||||
|
["en-IN-Standard-D"] = 'en-IN-Standard-D', -- [8] FEMALE
|
||||||
|
["en-GB-Standard-A"] = 'en-GB-Standard-A', -- [9] FEMALE
|
||||||
|
["en-GB-Standard-B"] = 'en-GB-Standard-B', -- [10] MALE
|
||||||
|
["en-GB-Standard-C"] = 'en-GB-Standard-C', -- [11] FEMALE
|
||||||
|
["en-GB-Standard-D"] = 'en-GB-Standard-D', -- [12] MALE
|
||||||
|
["en-GB-Standard-F"] = 'en-GB-Standard-F', -- [13] FEMALE
|
||||||
|
["en-US-Standard-A"] = 'en-US-Standard-A', -- [14] MALE
|
||||||
|
["en-US-Standard-B"] = 'en-US-Standard-B', -- [15] MALE
|
||||||
|
["en-US-Standard-C"] = 'en-US-Standard-C', -- [16] FEMALE
|
||||||
|
["en-US-Standard-D"] = 'en-US-Standard-D', -- [17] MALE
|
||||||
|
["en-US-Standard-E"] = 'en-US-Standard-E', -- [18] FEMALE
|
||||||
|
["en-US-Standard-F"] = 'en-US-Standard-F', -- [19] FEMALE
|
||||||
|
["en-US-Standard-G"] = 'en-US-Standard-G', -- [20] FEMALE
|
||||||
|
["en-US-Standard-H"] = 'en-US-Standard-H', -- [21] FEMALE
|
||||||
|
["en-US-Standard-I"] = 'en-US-Standard-I', -- [22] MALE
|
||||||
|
["en-US-Standard-J"] = 'en-US-Standard-J', -- [23] MALE
|
||||||
|
["fr-FR-Standard-A"] = "fr-FR-Standard-A", -- Female
|
||||||
|
["fr-FR-Standard-B"] = "fr-FR-Standard-B", -- Male
|
||||||
|
["fr-FR-Standard-C"] = "fr-FR-Standard-C", -- Female
|
||||||
|
["fr-FR-Standard-D"] = "fr-FR-Standard-D", -- Male
|
||||||
|
["fr-FR-Standard-E"] = "fr-FR-Standard-E", -- Female
|
||||||
|
["de-DE-Standard-A"] = "de-DE-Standard-A", -- Female
|
||||||
|
["de-DE-Standard-B"] = "de-DE-Standard-B", -- Male
|
||||||
|
["de-DE-Standard-C"] = "de-DE-Standard-C", -- Female
|
||||||
|
["de-DE-Standard-D"] = "de-DE-Standard-D", -- Male
|
||||||
|
["de-DE-Standard-E"] = "de-DE-Standard-E", -- Male
|
||||||
|
["de-DE-Standard-F"] = "de-DE-Standard-F", -- Female
|
||||||
|
["es-ES-Standard-A"] = "es-ES-Standard-A", -- Female
|
||||||
|
["es-ES-Standard-B"] = "es-ES-Standard-B", -- Male
|
||||||
|
["es-ES-Standard-C"] = "es-ES-Standard-C", -- Female
|
||||||
|
["es-ES-Standard-D"] = "es-ES-Standard-D", -- Female
|
||||||
|
["it-IT-Standard-A"] = "it-IT-Standard-A", -- Female
|
||||||
|
["it-IT-Standard-B"] = "it-IT-Standard-B", -- Female
|
||||||
|
["it-IT-Standard-C"] = "it-IT-Standard-C", -- Male
|
||||||
|
["it-IT-Standard-D"] = "it-IT-Standard-D", -- Male
|
||||||
|
},
|
||||||
|
Wavenet = {
|
||||||
|
["en-AU-Wavenet-A"] = 'en-AU-Wavenet-A', -- [1] FEMALE
|
||||||
|
["en-AU-Wavenet-B"] = 'en-AU-Wavenet-B', -- [2] MALE
|
||||||
|
["en-AU-Wavenet-C"] = 'en-AU-Wavenet-C', -- [3] FEMALE
|
||||||
|
["en-AU-Wavenet-D"] = 'en-AU-Wavenet-D', -- [4] MALE
|
||||||
|
["en-IN-Wavenet-A"] = 'en-IN-Wavenet-A', -- [5] FEMALE
|
||||||
|
["en-IN-Wavenet-B"] = 'en-IN-Wavenet-B', -- [6] MALE
|
||||||
|
["en-IN-Wavenet-C"] = 'en-IN-Wavenet-C', -- [7] MALE
|
||||||
|
["en-IN-Wavenet-D"] = 'en-IN-Wavenet-D', -- [8] FEMALE
|
||||||
|
["en-GB-Wavenet-A"] = 'en-GB-Wavenet-A', -- [9] FEMALE
|
||||||
|
["en-GB-Wavenet-B"] = 'en-GB-Wavenet-B', -- [10] MALE
|
||||||
|
["en-GB-Wavenet-C"] = 'en-GB-Wavenet-C', -- [11] FEMALE
|
||||||
|
["en-GB-Wavenet-D"] = 'en-GB-Wavenet-D', -- [12] MALE
|
||||||
|
["en-GB-Wavenet-F"] = 'en-GB-Wavenet-F', -- [13] FEMALE
|
||||||
|
["en-US-Wavenet-A"] = 'en-US-Wavenet-A', -- [14] MALE
|
||||||
|
["en-US-Wavenet-B"] = 'en-US-Wavenet-B', -- [15] MALE
|
||||||
|
["en-US-Wavenet-C"] = 'en-US-Wavenet-C', -- [16] FEMALE
|
||||||
|
["en-US-Wavenet-D"] = 'en-US-Wavenet-D', -- [17] MALE
|
||||||
|
["en-US-Wavenet-E"] = 'en-US-Wavenet-E', -- [18] FEMALE
|
||||||
|
["en-US-Wavenet-F"] = 'en-US-Wavenet-F', -- [19] FEMALE
|
||||||
|
["en-US-Wavenet-G"] = 'en-US-Wavenet-G', -- [20] FEMALE
|
||||||
|
["en-US-Wavenet-H"] = 'en-US-Wavenet-H', -- [21] FEMALE
|
||||||
|
["en-US-Wavenet-I"] = 'en-US-Wavenet-I', -- [22] MALE
|
||||||
|
["en-US-Wavenet-J"] = 'en-US-Wavenet-J', -- [23] MALE
|
||||||
|
["fr-FR-Wavenet-A"] = "fr-FR-Wavenet-A", -- Female
|
||||||
|
["fr-FR-Wavenet-B"] = "fr-FR-Wavenet-B", -- Male
|
||||||
|
["fr-FR-Wavenet-C"] = "fr-FR-Wavenet-C", -- Female
|
||||||
|
["fr-FR-Wavenet-D"] = "fr-FR-Wavenet-D", -- Male
|
||||||
|
["fr-FR-Wavenet-E"] = "fr-FR-Wavenet-E", -- Female
|
||||||
|
["de-DE-Wavenet-A"] = "de-DE-Wavenet-A", -- Female
|
||||||
|
["de-DE-Wavenet-B"] = "de-DE-Wavenet-B", -- Male
|
||||||
|
["de-DE-Wavenet-C"] = "de-DE-Wavenet-C", -- Female
|
||||||
|
["de-DE-Wavenet-D"] = "de-DE-Wavenet-D", -- Male
|
||||||
|
["de-DE-Wavenet-E"] = "de-DE-Wavenet-E", -- Male
|
||||||
|
["de-DE-Wavenet-F"] = "de-DE-Wavenet-F", -- Female
|
||||||
|
["es-ES-Wavenet-B"] = "es-ES-Wavenet-B", -- Male
|
||||||
|
["es-ES-Wavenet-C"] = "es-ES-Wavenet-C", -- Female
|
||||||
|
["es-ES-Wavenet-D"] = "es-ES-Wavenet-D", -- Female
|
||||||
|
["it-IT-Wavenet-A"] = "it-IT-Wavenet-A", -- Female
|
||||||
|
["it-IT-Wavenet-B"] = "it-IT-Wavenet-B", -- Female
|
||||||
|
["it-IT-Wavenet-C"] = "it-IT-Wavenet-C", -- Male
|
||||||
|
["it-IT-Wavenet-D"] = "it-IT-Wavenet-D", -- Male
|
||||||
|
} ,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- TODO list
|
-- TODO list
|
||||||
@@ -135,8 +261,9 @@ MSRS.version="0.0.3"
|
|||||||
-- @param #string PathToSRS Path to the directory, where SRS is located.
|
-- @param #string PathToSRS Path to the directory, where SRS is located.
|
||||||
-- @param #number Frequency Radio frequency in MHz. Default 143.00 MHz. Can also be given as a #table of multiple frequencies.
|
-- @param #number Frequency Radio frequency in MHz. Default 143.00 MHz. Can also be given as a #table of multiple frequencies.
|
||||||
-- @param #number Modulation Radio modulation: 0=AM (default), 1=FM. See `radio.modulation.AM` and `radio.modulation.FM` enumerators. Can also be given as a #table of multiple modulations.
|
-- @param #number Modulation Radio modulation: 0=AM (default), 1=FM. See `radio.modulation.AM` and `radio.modulation.FM` enumerators. Can also be given as a #table of multiple modulations.
|
||||||
|
-- @param #number Volume Volume - 1.0 is max, 0.0 is silence
|
||||||
-- @return #MSRS self
|
-- @return #MSRS self
|
||||||
function MSRS:New(PathToSRS, Frequency, Modulation)
|
function MSRS:New(PathToSRS, Frequency, Modulation, Volume)
|
||||||
|
|
||||||
-- Defaults.
|
-- Defaults.
|
||||||
Frequency =Frequency or 143
|
Frequency =Frequency or 143
|
||||||
@@ -151,6 +278,13 @@ function MSRS:New(PathToSRS, Frequency, Modulation)
|
|||||||
self:SetModulations(Modulation)
|
self:SetModulations(Modulation)
|
||||||
self:SetGender()
|
self:SetGender()
|
||||||
self:SetCoalition()
|
self:SetCoalition()
|
||||||
|
self:SetLabel()
|
||||||
|
self:SetVolume()
|
||||||
|
self.lid = string.format("%s-%s | ", self.name, self.version)
|
||||||
|
|
||||||
|
if not io or not os then
|
||||||
|
self:E(self.lid.."***** ERROR - io or os NOT desanitized! MSRS will not work!")
|
||||||
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@@ -193,12 +327,47 @@ function MSRS:GetPath()
|
|||||||
return self.path
|
return self.path
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Set SRS volume.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param #number Volume Volume - 1.0 is max, 0.0 is silence
|
||||||
|
-- @return #MSRS self
|
||||||
|
function MSRS:SetVolume(Volume)
|
||||||
|
local volume = Volume or 1
|
||||||
|
if volume > 1 then volume = 1 elseif volume < 0 then volume = 0 end
|
||||||
|
self.volume = volume
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get SRS volume.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @return #number Volume Volume - 1.0 is max, 0.0 is silence
|
||||||
|
function MSRS:GetVolume()
|
||||||
|
return self.volume
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set label.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param #number Label. Default "ROBOT"
|
||||||
|
-- @return #MSRS self
|
||||||
|
function MSRS:SetLabel(Label)
|
||||||
|
self.Label=Label or "ROBOT"
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get label.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @return #number Label.
|
||||||
|
function MSRS:GetLabel()
|
||||||
|
return self.Label
|
||||||
|
end
|
||||||
|
|
||||||
--- Set port.
|
--- Set port.
|
||||||
-- @param #MSRS self
|
-- @param #MSRS self
|
||||||
-- @param #number Port Port. Default 5002.
|
-- @param #number Port Port. Default 5002.
|
||||||
-- @return #MSRS self
|
-- @return #MSRS self
|
||||||
function MSRS:SetPort(Port)
|
function MSRS:SetPort(Port)
|
||||||
self.port=Port or 5002
|
self.port=Port or 5002
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get port.
|
--- Get port.
|
||||||
@@ -214,6 +383,7 @@ end
|
|||||||
-- @return #MSRS self
|
-- @return #MSRS self
|
||||||
function MSRS:SetCoalition(Coalition)
|
function MSRS:SetCoalition(Coalition)
|
||||||
self.coalition=Coalition or 0
|
self.coalition=Coalition or 0
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get coalition.
|
--- Get coalition.
|
||||||
@@ -382,21 +552,10 @@ function MSRS:PlaySoundFile(Soundfile, Delay)
|
|||||||
local command=self:_GetCommand()
|
local command=self:_GetCommand()
|
||||||
|
|
||||||
-- Append file.
|
-- Append file.
|
||||||
command=command.." --file="..tostring(soundfile)
|
command=command..' --file="'..tostring(soundfile)..'"'
|
||||||
|
|
||||||
|
-- Execute command.
|
||||||
self:_ExecCommand(command)
|
self:_ExecCommand(command)
|
||||||
|
|
||||||
--[[
|
|
||||||
|
|
||||||
command=command.." > bla.txt"
|
|
||||||
|
|
||||||
-- Debug output.
|
|
||||||
self:I(string.format("MSRS PlaySoundfile command=%s", command))
|
|
||||||
|
|
||||||
-- Execute SRS command.
|
|
||||||
local x=os.execute(command)
|
|
||||||
|
|
||||||
]]
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -422,16 +581,6 @@ function MSRS:PlaySoundText(SoundText, Delay)
|
|||||||
|
|
||||||
-- Execute command.
|
-- Execute command.
|
||||||
self:_ExecCommand(command)
|
self:_ExecCommand(command)
|
||||||
|
|
||||||
--[[
|
|
||||||
command=command.." > bla.txt"
|
|
||||||
|
|
||||||
-- Debug putput.
|
|
||||||
self:I(string.format("MSRS PlaySoundfile command=%s", command))
|
|
||||||
|
|
||||||
-- Execute SRS command.
|
|
||||||
local x=os.execute(command)
|
|
||||||
]]
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -458,37 +607,48 @@ function MSRS:PlayText(Text, Delay)
|
|||||||
-- Execute command.
|
-- Execute command.
|
||||||
self:_ExecCommand(command)
|
self:_ExecCommand(command)
|
||||||
|
|
||||||
--[[
|
end
|
||||||
|
|
||||||
-- Check that length of command is max 255 chars or os.execute() will not work!
|
|
||||||
if string.len(command)>255 then
|
|
||||||
|
|
||||||
-- Create a tmp file.
|
|
||||||
local filename = os.getenv('TMP') .. "\\MSRS-"..STTS.uuid()..".bat"
|
|
||||||
|
|
||||||
local script = io.open(filename, "w+")
|
|
||||||
script:write(command.." && exit")
|
|
||||||
script:close()
|
|
||||||
|
|
||||||
-- Play command.
|
|
||||||
command=string.format("\"%s\"", filename)
|
|
||||||
|
|
||||||
-- Play file in 0.05 seconds
|
|
||||||
timer.scheduleFunction(os.execute, command, timer.getTime()+0.05)
|
|
||||||
|
|
||||||
-- Remove file in 1 second.
|
|
||||||
timer.scheduleFunction(os.remove, filename, timer.getTime()+1)
|
|
||||||
else
|
|
||||||
|
|
||||||
-- Debug output.
|
|
||||||
self:I(string.format("MSRS Text command=%s", command))
|
|
||||||
|
|
||||||
-- Execute SRS command.
|
return self
|
||||||
local x=os.execute(command)
|
end
|
||||||
|
|
||||||
|
--- Play text message via STTS with explicitly specified options.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param #string Text Text message.
|
||||||
|
-- @param #number Delay Delay in seconds, before the message is played.
|
||||||
|
-- @param #table Frequencies Radio frequencies.
|
||||||
|
-- @param #table Modulations Radio modulations.
|
||||||
|
-- @param #string Gender Gender.
|
||||||
|
-- @param #string Culture Culture.
|
||||||
|
-- @param #string Voice Voice.
|
||||||
|
-- @param #number Volume Volume.
|
||||||
|
-- @param #string Label Label.
|
||||||
|
-- @return #MSRS self
|
||||||
|
function MSRS:PlayTextExt(Text, Delay, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label)
|
||||||
|
|
||||||
|
if Delay and Delay>0 then
|
||||||
|
self:ScheduleOnce(Delay, MSRS.PlayTextExt, self, Text, 0, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label)
|
||||||
|
else
|
||||||
|
|
||||||
|
-- Ensure table.
|
||||||
|
if Frequencies and type(Frequencies)~="table" then
|
||||||
|
Frequencies={Frequencies}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Ensure table.
|
||||||
|
if Modulations and type(Modulations)~="table" then
|
||||||
|
Modulations={Modulations}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get command line.
|
||||||
|
local command=self:_GetCommand(Frequencies, Modulations, nil, Gender, Voice, Culture, Volume, nil, nil, Label)
|
||||||
|
|
||||||
|
-- Append text.
|
||||||
|
command=command..string.format(" --text=\"%s\"", tostring(Text))
|
||||||
|
|
||||||
end
|
-- Execute command.
|
||||||
|
self:_ExecCommand(command)
|
||||||
|
|
||||||
]]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
@@ -625,8 +785,9 @@ end
|
|||||||
-- @param #number volume Volume.
|
-- @param #number volume Volume.
|
||||||
-- @param #number speed Speed.
|
-- @param #number speed Speed.
|
||||||
-- @param #number port Port.
|
-- @param #number port Port.
|
||||||
|
-- @param #string label Label, defaults to "ROBOT" (displayed sender name in the radio overlay of SRS) - No spaces allowed!
|
||||||
-- @return #string Command.
|
-- @return #string Command.
|
||||||
function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, speed, port)
|
function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, speed, port,label)
|
||||||
|
|
||||||
local path=self:GetPath() or STTS.DIRECTORY
|
local path=self:GetPath() or STTS.DIRECTORY
|
||||||
local exe=STTS.EXECUTABLE or "DCS-SR-ExternalAudio.exe"
|
local exe=STTS.EXECUTABLE or "DCS-SR-ExternalAudio.exe"
|
||||||
@@ -639,6 +800,7 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp
|
|||||||
volume=volume or self.volume
|
volume=volume or self.volume
|
||||||
speed=speed or self.speed
|
speed=speed or self.speed
|
||||||
port=port or self.port
|
port=port or self.port
|
||||||
|
label=label or self.Label
|
||||||
|
|
||||||
-- Replace modulation
|
-- Replace modulation
|
||||||
modus=modus:gsub("0", "AM")
|
modus=modus:gsub("0", "AM")
|
||||||
@@ -648,12 +810,12 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp
|
|||||||
--local command=string.format("%s --freqs=%s --modulations=%s --coalition=%d --port=%d --volume=%.2f --speed=%d", exe, freqs, modus, coal, port, volume, speed)
|
--local command=string.format("%s --freqs=%s --modulations=%s --coalition=%d --port=%d --volume=%.2f --speed=%d", exe, freqs, modus, coal, port, volume, speed)
|
||||||
|
|
||||||
-- Command from orig STTS script. Works better for some unknown reason!
|
-- Command from orig STTS script. Works better for some unknown reason!
|
||||||
local command=string.format("start /min \"\" /d \"%s\" /b \"%s\" -f %s -m %s -c %s -p %s -n \"%s\" -h", path, exe, freqs, modus, coal, port, "ROBOT")
|
--local command=string.format("start /min \"\" /d \"%s\" /b \"%s\" -f %s -m %s -c %s -p %s -n \"%s\" -h", path, exe, freqs, modus, coal, port, "ROBOT")
|
||||||
|
|
||||||
--local command=string.format('start /b "" /d "%s" "%s" -f %s -m %s -c %s -p %s -n "%s" > bla.txt', path, exe, freqs, modus, coal, port, "ROBOT")
|
--local command=string.format('start /b "" /d "%s" "%s" -f %s -m %s -c %s -p %s -n "%s" > bla.txt', path, exe, freqs, modus, coal, port, "ROBOT")
|
||||||
|
|
||||||
-- Command.
|
-- Command.
|
||||||
local command=string.format('%s/%s -f %s -m %s -c %s -p %s -n "%s"', path, exe, freqs, modus, coal, port, "ROBOT")
|
local command=string.format('"%s\\%s" -f %s -m %s -c %s -p %s -n "%s" -v "%.1f"', path, exe, freqs, modus, coal, port, label,volume)
|
||||||
|
|
||||||
-- Set voice or gender/culture.
|
-- Set voice or gender/culture.
|
||||||
if voice then
|
if voice then
|
||||||
@@ -662,7 +824,7 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp
|
|||||||
else
|
else
|
||||||
-- Add gender.
|
-- Add gender.
|
||||||
if gender and gender~="female" then
|
if gender and gender~="female" then
|
||||||
command=command..string.format(" --gender=%s", tostring(gender))
|
command=command..string.format(" -g %s", tostring(gender))
|
||||||
end
|
end
|
||||||
-- Add culture.
|
-- Add culture.
|
||||||
if culture and culture~="en-GB" then
|
if culture and culture~="en-GB" then
|
||||||
@@ -678,7 +840,7 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp
|
|||||||
|
|
||||||
-- Set google.
|
-- Set google.
|
||||||
if self.google then
|
if self.google then
|
||||||
command=command..string.format(' -G "%s"', self.google)
|
command=command..string.format(' --ssml -G "%s"', self.google)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Debug output.
|
-- Debug output.
|
||||||
@@ -687,6 +849,353 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp
|
|||||||
return command
|
return command
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Manages radio transmissions.
|
||||||
|
--
|
||||||
|
-- The purpose of the MSRSQUEUE class is to manage SRS text-to-speech (TTS) messages using the MSRS class.
|
||||||
|
-- This can be used to submit multiple TTS messages and the class takes care that they are transmitted one after the other (and not overlapping).
|
||||||
|
--
|
||||||
|
-- @type MSRSQUEUE
|
||||||
|
-- @field #string ClassName Name of the class "MSRSQUEUE".
|
||||||
|
-- @field #string lid ID for dcs.log.
|
||||||
|
-- @field #table queue The queue of transmissions.
|
||||||
|
-- @field #string alias Name of the radio queue.
|
||||||
|
-- @field #number dt Time interval in seconds for checking the radio queue.
|
||||||
|
-- @field #number Tlast Time (abs) when the last transmission finished.
|
||||||
|
-- @field #boolean checking If `true`, the queue update function is scheduled to be called again.
|
||||||
|
-- @extends Core.Base#BASE
|
||||||
|
MSRSQUEUE = {
|
||||||
|
ClassName = "MSRSQUEUE",
|
||||||
|
Debugmode = nil,
|
||||||
|
lid = nil,
|
||||||
|
queue = {},
|
||||||
|
alias = nil,
|
||||||
|
dt = nil,
|
||||||
|
Tlast = nil,
|
||||||
|
checking = nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Radio queue transmission data.
|
||||||
|
-- @type MSRSQUEUE.Transmission
|
||||||
|
-- @field #string text Text to be transmitted.
|
||||||
|
-- @field Sound.SRS#MSRS msrs MOOSE SRS object.
|
||||||
|
-- @field #number duration Duration in seconds.
|
||||||
|
-- @field #table subgroups Groups to send subtitle to.
|
||||||
|
-- @field #string subtitle Subtitle of the transmission.
|
||||||
|
-- @field #number subduration Duration of the subtitle being displayed.
|
||||||
|
-- @field #number frequency Frequency.
|
||||||
|
-- @field #number modulation Modulation.
|
||||||
|
-- @field #number Tstarted Mission time (abs) in seconds when the transmission started.
|
||||||
|
-- @field #boolean isplaying If true, transmission is currently playing.
|
||||||
|
-- @field #number Tplay Mission time (abs) in seconds when the transmission should be played.
|
||||||
|
-- @field #number interval Interval in seconds before next transmission.
|
||||||
|
|
||||||
|
--- Create a new MSRSQUEUE object for a given radio frequency/modulation.
|
||||||
|
-- @param #MSRSQUEUE self
|
||||||
|
-- @param #string alias (Optional) Name of the radio queue.
|
||||||
|
-- @return #MSRSQUEUE self The MSRSQUEUE object.
|
||||||
|
function MSRSQUEUE:New(alias)
|
||||||
|
|
||||||
|
-- Inherit base
|
||||||
|
local self=BASE:Inherit(self, BASE:New()) --#MSRSQUEUE
|
||||||
|
|
||||||
|
self.alias=alias or "My Radio"
|
||||||
|
|
||||||
|
self.dt=1.0
|
||||||
|
|
||||||
|
self.lid=string.format("MSRSQUEUE %s | ", self.alias)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Clear the radio queue.
|
||||||
|
-- @param #MSRSQUEUE self
|
||||||
|
-- @return #MSRSQUEUE self The MSRSQUEUE object.
|
||||||
|
function MSRSQUEUE:Clear()
|
||||||
|
self:I(self.lid.."Clearning MSRSQUEUE")
|
||||||
|
self.queue={}
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Add a transmission to the radio queue.
|
||||||
|
-- @param #MSRSQUEUE self
|
||||||
|
-- @param #MSRSQUEUE.Transmission transmission The transmission data table.
|
||||||
|
-- @return #MSRSQUEUE self
|
||||||
|
function MSRSQUEUE:AddTransmission(transmission)
|
||||||
|
|
||||||
|
-- Init.
|
||||||
|
transmission.isplaying=false
|
||||||
|
transmission.Tstarted=nil
|
||||||
|
|
||||||
|
-- Add to queue.
|
||||||
|
table.insert(self.queue, transmission)
|
||||||
|
|
||||||
|
-- Start checking.
|
||||||
|
if not self.checking then
|
||||||
|
self:_CheckRadioQueue()
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Create a new transmission and add it to the radio queue.
|
||||||
|
-- @param #MSRSQUEUE self
|
||||||
|
-- @param #string text Text to play.
|
||||||
|
-- @param #number duration Duration in seconds the file lasts. Default is determined by number of characters of the text message.
|
||||||
|
-- @param Sound.SRS#MSRS msrs MOOSE SRS object.
|
||||||
|
-- @param #number tstart Start time (abs) seconds. Default now.
|
||||||
|
-- @param #number interval Interval in seconds after the last transmission finished.
|
||||||
|
-- @param #table subgroups Groups that should receive the subtiltle.
|
||||||
|
-- @param #string subtitle Subtitle displayed when the message is played.
|
||||||
|
-- @param #number subduration Duration [sec] of the subtitle being displayed. Default 5 sec.
|
||||||
|
-- @param #number frequency Radio frequency if other than MSRS default.
|
||||||
|
-- @param #number modulation Radio modulation if other then MSRS default.
|
||||||
|
-- @return #MSRSQUEUE.Transmission Radio transmission table.
|
||||||
|
function MSRSQUEUE:NewTransmission(text, duration, msrs, tstart, interval, subgroups, subtitle, subduration, frequency, modulation)
|
||||||
|
|
||||||
|
-- Sanity checks.
|
||||||
|
if not text then
|
||||||
|
self:E(self.lid.."ERROR: No text specified.")
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
if type(text)~="string" then
|
||||||
|
self:E(self.lid.."ERROR: Text specified is NOT a string.")
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- Create a new transmission object.
|
||||||
|
local transmission={} --#MSRSQUEUE.Transmission
|
||||||
|
transmission.text=text
|
||||||
|
transmission.duration=duration or STTS.getSpeechTime(text)
|
||||||
|
transmission.msrs=msrs
|
||||||
|
transmission.Tplay=tstart or timer.getAbsTime()
|
||||||
|
transmission.subtitle=subtitle
|
||||||
|
transmission.interval=interval or 0
|
||||||
|
transmission.frequency=frequency
|
||||||
|
transmission.modulation=modulation
|
||||||
|
transmission.subgroups=subgroups
|
||||||
|
if transmission.subtitle then
|
||||||
|
transmission.subduration=subduration or transmission.duration
|
||||||
|
else
|
||||||
|
transmission.subduration=0 --nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Add transmission to queue.
|
||||||
|
self:AddTransmission(transmission)
|
||||||
|
|
||||||
|
return transmission
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Broadcast radio message.
|
||||||
|
-- @param #MSRSQUEUE self
|
||||||
|
-- @param #MSRSQUEUE.Transmission transmission The transmission.
|
||||||
|
function MSRSQUEUE:Broadcast(transmission)
|
||||||
|
|
||||||
|
if transmission.frequency then
|
||||||
|
transmission.msrs:PlayTextExt(transmission.text, nil, transmission.frequency, transmission.modulation, Gender, Culture, Voice, Volume, Label)
|
||||||
|
else
|
||||||
|
transmission.msrs:PlayText(transmission.text)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function texttogroup(gid)
|
||||||
|
-- Text to group.
|
||||||
|
trigger.action.outTextForGroup(gid, transmission.subtitle, transmission.subduration, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
if transmission.subgroups and #transmission.subgroups>0 then
|
||||||
|
|
||||||
|
for _,_group in pairs(transmission.subgroups) do
|
||||||
|
local group=_group --Wrapper.Group#GROUP
|
||||||
|
|
||||||
|
if group and group:IsAlive() then
|
||||||
|
local gid=group:GetID()
|
||||||
|
|
||||||
|
self:ScheduleOnce(4, texttogroup, gid)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Calculate total transmission duration of all transmission in the queue.
|
||||||
|
-- @param #MSRSQUEUE self
|
||||||
|
-- @return #number Total transmission duration.
|
||||||
|
function MSRSQUEUE:CalcTransmisstionDuration()
|
||||||
|
|
||||||
|
local Tnow=timer.getAbsTime()
|
||||||
|
|
||||||
|
local T=0
|
||||||
|
for _,_transmission in pairs(self.queue) do
|
||||||
|
local transmission=_transmission --#MSRSQUEUE.Transmission
|
||||||
|
|
||||||
|
if transmission.isplaying then
|
||||||
|
|
||||||
|
-- Playing for dt seconds.
|
||||||
|
local dt=Tnow-transmission.Tstarted
|
||||||
|
|
||||||
|
T=T+transmission.duration-dt
|
||||||
|
|
||||||
|
else
|
||||||
|
T=T+transmission.duration
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return T
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Check radio queue for transmissions to be broadcasted.
|
||||||
|
-- @param #MSRSQUEUE self
|
||||||
|
-- @param #number delay Delay in seconds before checking.
|
||||||
|
function MSRSQUEUE:_CheckRadioQueue(delay)
|
||||||
|
|
||||||
|
-- Transmissions in queue.
|
||||||
|
local N=#self.queue
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
self:T2(self.lid..string.format("Check radio queue %s: delay=%.3f sec, N=%d, checking=%s", self.alias, delay or 0, N, tostring(self.checking)))
|
||||||
|
|
||||||
|
if delay and delay>0 then
|
||||||
|
|
||||||
|
-- Delayed call.
|
||||||
|
self:ScheduleOnce(delay, MSRSQUEUE._CheckRadioQueue, self)
|
||||||
|
|
||||||
|
-- Checking on.
|
||||||
|
self.checking=true
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
-- Check if queue is empty.
|
||||||
|
if N==0 then
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
self:T(self.lid..string.format("Check radio queue %s empty ==> disable checking", self.alias))
|
||||||
|
|
||||||
|
-- Queue is now empty. Nothing to else to do. We start checking again, if a transmission is added.
|
||||||
|
self.checking=false
|
||||||
|
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get current abs time.
|
||||||
|
local time=timer.getAbsTime()
|
||||||
|
|
||||||
|
-- Checking on.
|
||||||
|
self.checking=true
|
||||||
|
|
||||||
|
-- Set dt.
|
||||||
|
local dt=self.dt
|
||||||
|
|
||||||
|
|
||||||
|
local playing=false
|
||||||
|
local next=nil --#MSRSQUEUE.Transmission
|
||||||
|
local remove=nil
|
||||||
|
for i,_transmission in ipairs(self.queue) do
|
||||||
|
local transmission=_transmission --#MSRSQUEUE.Transmission
|
||||||
|
|
||||||
|
-- Check if transmission time has passed.
|
||||||
|
if time>=transmission.Tplay then
|
||||||
|
|
||||||
|
-- Check if transmission is currently playing.
|
||||||
|
if transmission.isplaying then
|
||||||
|
|
||||||
|
-- Check if transmission is finished.
|
||||||
|
if time>=transmission.Tstarted+transmission.duration then
|
||||||
|
|
||||||
|
-- Transmission over.
|
||||||
|
transmission.isplaying=false
|
||||||
|
|
||||||
|
-- Remove ith element in queue.
|
||||||
|
remove=i
|
||||||
|
|
||||||
|
-- Store time last transmission finished.
|
||||||
|
self.Tlast=time
|
||||||
|
|
||||||
|
else -- still playing
|
||||||
|
|
||||||
|
-- Transmission is still playing.
|
||||||
|
playing=true
|
||||||
|
|
||||||
|
dt=transmission.duration-(time-transmission.Tstarted)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
else -- not playing yet
|
||||||
|
|
||||||
|
local Tlast=self.Tlast
|
||||||
|
|
||||||
|
if transmission.interval==nil then
|
||||||
|
|
||||||
|
-- Not playing ==> this will be next.
|
||||||
|
if next==nil then
|
||||||
|
next=transmission
|
||||||
|
end
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
if Tlast==nil or time-Tlast>=transmission.interval then
|
||||||
|
next=transmission
|
||||||
|
else
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- We got a transmission or one with an interval that is not due yet. No need for anything else.
|
||||||
|
if next or Tlast then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
-- Transmission not due yet.
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Found a new transmission.
|
||||||
|
if next~=nil and not playing then
|
||||||
|
-- Debug info.
|
||||||
|
self:T(self.lid..string.format("Broadcasting text=\"%s\" at T=%.3f", next.text, time))
|
||||||
|
|
||||||
|
-- Call SRS.
|
||||||
|
self:Broadcast(next)
|
||||||
|
|
||||||
|
next.isplaying=true
|
||||||
|
next.Tstarted=time
|
||||||
|
dt=next.duration
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Remove completed call from queue.
|
||||||
|
if remove then
|
||||||
|
-- Remove from queue.
|
||||||
|
table.remove(self.queue, remove)
|
||||||
|
N=N-1
|
||||||
|
|
||||||
|
-- Check if queue is empty.
|
||||||
|
if #self.queue==0 then
|
||||||
|
-- Debug info.
|
||||||
|
self:T(self.lid..string.format("Check radio queue %s empty ==> disable checking", self.alias))
|
||||||
|
|
||||||
|
self.checking=false
|
||||||
|
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check queue.
|
||||||
|
self:_CheckRadioQueue(dt)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -183,13 +183,17 @@ do -- Sound File
|
|||||||
|
|
||||||
--- Set path, where the sound file is located.
|
--- Set path, where the sound file is located.
|
||||||
-- @param #SOUNDFILE self
|
-- @param #SOUNDFILE self
|
||||||
-- @param #string Path Path to the directory, where the sound file is located.
|
-- @param #string Path Path to the directory, where the sound file is located. In case this is nil, it defaults to the DCS mission temp directory.
|
||||||
-- @return #SOUNDFILE self
|
-- @return #SOUNDFILE self
|
||||||
function SOUNDFILE:SetPath(Path)
|
function SOUNDFILE:SetPath(Path)
|
||||||
|
|
||||||
-- Init path.
|
-- Init path.
|
||||||
self.path=Path or "l10n/DEFAULT/"
|
self.path=Path or "l10n/DEFAULT/"
|
||||||
|
|
||||||
|
if not Path and self.useSRS then -- use path to mission temp dir
|
||||||
|
self.path = os.getenv('TMP') .. "\\DCS\\Mission\\l10n\\DEFAULT"
|
||||||
|
end
|
||||||
|
|
||||||
-- Remove (back)slashes.
|
-- Remove (back)slashes.
|
||||||
local nmax=1000 ; local n=1
|
local nmax=1000 ; local n=1
|
||||||
while (self.path:sub(-1)=="/" or self.path:sub(-1)==[[\]]) and n<=nmax do
|
while (self.path:sub(-1)=="/" or self.path:sub(-1)==[[\]]) and n<=nmax do
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ do -- UserSound
|
|||||||
-- @param #USERSOUND self
|
-- @param #USERSOUND self
|
||||||
-- @param #string UserSoundFileName The filename of the usersound.
|
-- @param #string UserSoundFileName The filename of the usersound.
|
||||||
-- @return #USERSOUND
|
-- @return #USERSOUND
|
||||||
function USERSOUND:New( UserSoundFileName ) --R2.3
|
function USERSOUND:New( UserSoundFileName )
|
||||||
|
|
||||||
local self = BASE:Inherit( self, BASE:New() ) -- #USERSOUND
|
local self = BASE:Inherit( self, BASE:New() ) -- #USERSOUND
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ do -- UserSound
|
|||||||
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
|
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
|
||||||
-- BlueVictory:SetFileName( "BlueVictoryLoud.ogg" ) -- Set the BlueVictory to change the file name to play a louder sound.
|
-- BlueVictory:SetFileName( "BlueVictoryLoud.ogg" ) -- Set the BlueVictory to change the file name to play a louder sound.
|
||||||
--
|
--
|
||||||
function USERSOUND:SetFileName( UserSoundFileName ) --R2.3
|
function USERSOUND:SetFileName( UserSoundFileName )
|
||||||
|
|
||||||
self.UserSoundFileName = UserSoundFileName
|
self.UserSoundFileName = UserSoundFileName
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@ do -- UserSound
|
|||||||
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
|
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
|
||||||
-- BlueVictory:ToAll() -- Play the sound that Blue has won.
|
-- BlueVictory:ToAll() -- Play the sound that Blue has won.
|
||||||
--
|
--
|
||||||
function USERSOUND:ToAll() --R2.3
|
function USERSOUND:ToAll()
|
||||||
|
|
||||||
trigger.action.outSound( self.UserSoundFileName )
|
trigger.action.outSound( self.UserSoundFileName )
|
||||||
|
|
||||||
@@ -91,7 +91,7 @@ do -- UserSound
|
|||||||
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
|
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
|
||||||
-- BlueVictory:ToCoalition( coalition.side.BLUE ) -- Play the sound that Blue has won to the blue coalition.
|
-- BlueVictory:ToCoalition( coalition.side.BLUE ) -- Play the sound that Blue has won to the blue coalition.
|
||||||
--
|
--
|
||||||
function USERSOUND:ToCoalition( Coalition ) --R2.3
|
function USERSOUND:ToCoalition( Coalition )
|
||||||
|
|
||||||
trigger.action.outSoundForCoalition(Coalition, self.UserSoundFileName )
|
trigger.action.outSoundForCoalition(Coalition, self.UserSoundFileName )
|
||||||
|
|
||||||
@@ -107,7 +107,7 @@ do -- UserSound
|
|||||||
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
|
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
|
||||||
-- BlueVictory:ToCountry( country.id.USA ) -- Play the sound that Blue has won to the USA country.
|
-- BlueVictory:ToCountry( country.id.USA ) -- Play the sound that Blue has won to the USA country.
|
||||||
--
|
--
|
||||||
function USERSOUND:ToCountry( Country ) --R2.3
|
function USERSOUND:ToCountry( Country )
|
||||||
|
|
||||||
trigger.action.outSoundForCountry( Country, self.UserSoundFileName )
|
trigger.action.outSoundForCountry( Country, self.UserSoundFileName )
|
||||||
|
|
||||||
@@ -123,9 +123,9 @@ do -- UserSound
|
|||||||
-- @usage
|
-- @usage
|
||||||
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
|
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
|
||||||
-- local PlayerGroup = GROUP:FindByName( "PlayerGroup" ) -- Search for the active group named "PlayerGroup", that contains a human player.
|
-- local PlayerGroup = GROUP:FindByName( "PlayerGroup" ) -- Search for the active group named "PlayerGroup", that contains a human player.
|
||||||
-- BlueVictory:ToGroup( PlayerGroup ) -- Play the sound that Blue has won to the player group.
|
-- BlueVictory:ToGroup( PlayerGroup ) -- Play the victory sound to the player group.
|
||||||
--
|
--
|
||||||
function USERSOUND:ToGroup( Group, Delay ) --R2.3
|
function USERSOUND:ToGroup( Group, Delay )
|
||||||
|
|
||||||
Delay=Delay or 0
|
Delay=Delay or 0
|
||||||
if Delay>0 then
|
if Delay>0 then
|
||||||
@@ -136,5 +136,27 @@ do -- UserSound
|
|||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Play the usersound to the given @{Wrapper.Unit}.
|
||||||
|
-- @param #USERSOUND self
|
||||||
|
-- @param Wrapper.Unit#UNIT Unit The @{Wrapper.Unit} to play the usersound to.
|
||||||
|
-- @param #number Delay (Optional) Delay in seconds, before the sound is played. Default 0.
|
||||||
|
-- @return #USERSOUND The usersound instance.
|
||||||
|
-- @usage
|
||||||
|
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
|
||||||
|
-- local PlayerUnit = UNIT:FindByName( "PlayerUnit" ) -- Search for the active unit named "PlayerUnit", a human player.
|
||||||
|
-- BlueVictory:ToUnit( PlayerUnit ) -- Play the victory sound to the player unit.
|
||||||
|
--
|
||||||
|
function USERSOUND:ToUnit( Unit, Delay )
|
||||||
|
|
||||||
|
Delay=Delay or 0
|
||||||
|
if Delay>0 then
|
||||||
|
SCHEDULER:New(nil, USERSOUND.ToUnit,{self, Unit}, Delay)
|
||||||
|
else
|
||||||
|
trigger.action.outSoundForUnit( Unit:GetID(), self.UserSoundFileName )
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -216,7 +216,7 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName )
|
|||||||
local MenuReporting = MENU_GROUP:New( EventGroup, "Missions Reports", CommandCenterMenu )
|
local MenuReporting = MENU_GROUP:New( EventGroup, "Missions Reports", CommandCenterMenu )
|
||||||
local MenuMissionsSummary = MENU_GROUP_COMMAND:New( EventGroup, "Missions Status Report", MenuReporting, self.ReportSummary, self, EventGroup )
|
local MenuMissionsSummary = MENU_GROUP_COMMAND:New( EventGroup, "Missions Status Report", MenuReporting, self.ReportSummary, self, EventGroup )
|
||||||
local MenuMissionsDetails = MENU_GROUP_COMMAND:New( EventGroup, "Missions Players Report", MenuReporting, self.ReportMissionsPlayers, self, EventGroup )
|
local MenuMissionsDetails = MENU_GROUP_COMMAND:New( EventGroup, "Missions Players Report", MenuReporting, self.ReportMissionsPlayers, self, EventGroup )
|
||||||
self:ReportSummary( EventGroup )
|
--self:ReportSummary( EventGroup )
|
||||||
local PlayerUnit = EventData.IniUnit
|
local PlayerUnit = EventData.IniUnit
|
||||||
for MissionID, Mission in pairs( self:GetMissions() ) do
|
for MissionID, Mission in pairs( self:GetMissions() ) do
|
||||||
local Mission = Mission -- Tasking.Mission#MISSION
|
local Mission = Mission -- Tasking.Mission#MISSION
|
||||||
@@ -560,9 +560,11 @@ function COMMANDCENTER:SetAutoAssignTasks( AutoAssign )
|
|||||||
self.AutoAssignTasks = AutoAssign or false
|
self.AutoAssignTasks = AutoAssign or false
|
||||||
|
|
||||||
if self.AutoAssignTasks == true then
|
if self.AutoAssignTasks == true then
|
||||||
self:ScheduleRepeat( 10, 30, 0, nil, self.AssignTasks, self )
|
self.autoAssignTasksScheduleID=self:ScheduleRepeat( 10, 30, 0, nil, self.AssignTasks, self )
|
||||||
else
|
else
|
||||||
self:ScheduleStop( self.AssignTasks )
|
self:ScheduleStop()
|
||||||
|
-- FF this is not the schedule ID
|
||||||
|
--self:ScheduleStop( self.AssignTasks )
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ MISSION = {
|
|||||||
-- @param #string MissionName Name of the mission. This name will be used to reference the status of each mission by the players.
|
-- @param #string MissionName Name of the mission. This name will be used to reference the status of each mission by the players.
|
||||||
-- @param #string MissionPriority String indicating the "priority" of the Mission. e.g. "Primary", "Secondary". It is free format and up to the Mission designer to choose. There are no rules behind this field.
|
-- @param #string MissionPriority String indicating the "priority" of the Mission. e.g. "Primary", "Secondary". It is free format and up to the Mission designer to choose. There are no rules behind this field.
|
||||||
-- @param #string MissionBriefing String indicating the mission briefing to be shown when a player joins a @{CLIENT}.
|
-- @param #string MissionBriefing String indicating the mission briefing to be shown when a player joins a @{CLIENT}.
|
||||||
-- @param DCS#coaliton.side MissionCoalition Side of the coalition, i.e. and enumerator @{#DCS.coalition.side} corresponding to RED, BLUE or NEUTRAL.
|
-- @param DCS#coalition.side MissionCoalition Side of the coalition, i.e. and enumerator @{#DCS.coalition.side} corresponding to RED, BLUE or NEUTRAL.
|
||||||
-- @return #MISSION self
|
-- @return #MISSION self
|
||||||
function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefing, MissionCoalition )
|
function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefing, MissionCoalition )
|
||||||
|
|
||||||
|
|||||||
@@ -82,7 +82,7 @@
|
|||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
-- A statistic report on the progress of the mission. Each task achievement will increase the %-tage to 100% as a goal to complete the task.
|
-- A statistic report on the progress of the mission. Each task achievement will increase the % to 100% as a goal to complete the task.
|
||||||
--
|
--
|
||||||
-- ## 1.3) Join a Task.
|
-- ## 1.3) Join a Task.
|
||||||
--
|
--
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
--- **Tasking** - The TASK_A2A models tasks for players in Air to Air engagements.
|
--- **Tasking** - The TASK_A2A models tasks for players in Air to Air engagements.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ### Author: **FlightControl**
|
-- ### Author: **FlightControl**
|
||||||
--
|
--
|
||||||
-- ### Contributions:
|
-- ### Contributions:
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- @module Tasking.Task_A2A
|
-- @module Tasking.Task_A2A
|
||||||
-- @image MOOSE.JPG
|
-- @image MOOSE.JPG
|
||||||
|
|
||||||
@@ -35,12 +35,12 @@ do -- TASK_A2A
|
|||||||
-- * @{#TASK_A2A.SetScoreOnDestroy}(): Set a score when a target in scope of the A2A attack, has been destroyed.
|
-- * @{#TASK_A2A.SetScoreOnDestroy}(): Set a score when a target in scope of the A2A attack, has been destroyed.
|
||||||
-- * @{#TASK_A2A.SetScoreOnSuccess}(): Set a score when all the targets in scope of the A2A attack, have been destroyed.
|
-- * @{#TASK_A2A.SetScoreOnSuccess}(): Set a score when all the targets in scope of the A2A attack, have been destroyed.
|
||||||
-- * @{#TASK_A2A.SetPenaltyOnFailed}(): Set a penalty when the A2A attack has failed.
|
-- * @{#TASK_A2A.SetPenaltyOnFailed}(): Set a penalty when the A2A attack has failed.
|
||||||
--
|
--
|
||||||
-- @field #TASK_A2A
|
-- @field #TASK_A2A
|
||||||
TASK_A2A = {
|
TASK_A2A = {
|
||||||
ClassName = "TASK_A2A",
|
ClassName = "TASK_A2A"
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Instantiates a new TASK_A2A.
|
--- Instantiates a new TASK_A2A.
|
||||||
-- @param #TASK_A2A self
|
-- @param #TASK_A2A self
|
||||||
-- @param Tasking.Mission#MISSION Mission
|
-- @param Tasking.Mission#MISSION Mission
|
||||||
@@ -54,51 +54,49 @@ do -- TASK_A2A
|
|||||||
function TASK_A2A:New( Mission, SetAttack, TaskName, TargetSetUnit, TaskType, TaskBriefing )
|
function TASK_A2A:New( Mission, SetAttack, TaskName, TargetSetUnit, TaskType, TaskBriefing )
|
||||||
local self = BASE:Inherit( self, TASK:New( Mission, SetAttack, TaskName, TaskType, TaskBriefing ) ) -- Tasking.Task#TASK_A2A
|
local self = BASE:Inherit( self, TASK:New( Mission, SetAttack, TaskName, TaskType, TaskBriefing ) ) -- Tasking.Task#TASK_A2A
|
||||||
self:F()
|
self:F()
|
||||||
|
|
||||||
self.TargetSetUnit = TargetSetUnit
|
self.TargetSetUnit = TargetSetUnit
|
||||||
self.TaskType = TaskType
|
self.TaskType = TaskType
|
||||||
|
|
||||||
local Fsm = self:GetUnitProcess()
|
local Fsm = self:GetUnitProcess()
|
||||||
|
|
||||||
|
|
||||||
Fsm:AddTransition( "Assigned", "RouteToRendezVous", "RoutingToRendezVous" )
|
Fsm:AddTransition( "Assigned", "RouteToRendezVous", "RoutingToRendezVous" )
|
||||||
Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtRendezVous" } )
|
Fsm:AddProcess( "RoutingToRendezVous", "RouteToRendezVousPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtRendezVous" } )
|
||||||
Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtRendezVous" } )
|
Fsm:AddProcess( "RoutingToRendezVous", "RouteToRendezVousZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtRendezVous" } )
|
||||||
|
|
||||||
Fsm:AddTransition( { "Arrived", "RoutingToRendezVous" }, "ArriveAtRendezVous", "ArrivedAtRendezVous" )
|
Fsm:AddTransition( { "Arrived", "RoutingToRendezVous" }, "ArriveAtRendezVous", "ArrivedAtRendezVous" )
|
||||||
|
|
||||||
Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "Engage", "Engaging" )
|
Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "Engage", "Engaging" )
|
||||||
Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "HoldAtRendezVous", "HoldingAtRendezVous" )
|
Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "HoldAtRendezVous", "HoldingAtRendezVous" )
|
||||||
|
|
||||||
Fsm:AddProcess ( "Engaging", "Account", ACT_ACCOUNT_DEADS:New(), {} )
|
Fsm:AddProcess( "Engaging", "Account", ACT_ACCOUNT_DEADS:New(), {} )
|
||||||
Fsm:AddTransition( "Engaging", "RouteToTarget", "Engaging" )
|
Fsm:AddTransition( "Engaging", "RouteToTarget", "Engaging" )
|
||||||
Fsm:AddProcess( "Engaging", "RouteToTargetZone", ACT_ROUTE_ZONE:New(), {} )
|
Fsm:AddProcess( "Engaging", "RouteToTargetZone", ACT_ROUTE_ZONE:New(), {} )
|
||||||
Fsm:AddProcess( "Engaging", "RouteToTargetPoint", ACT_ROUTE_POINT:New(), {} )
|
Fsm:AddProcess( "Engaging", "RouteToTargetPoint", ACT_ROUTE_POINT:New(), {} )
|
||||||
Fsm:AddTransition( "Engaging", "RouteToTargets", "Engaging" )
|
Fsm:AddTransition( "Engaging", "RouteToTargets", "Engaging" )
|
||||||
|
|
||||||
-- Fsm:AddTransition( "Accounted", "DestroyedAll", "Accounted" )
|
-- Fsm:AddTransition( "Accounted", "DestroyedAll", "Accounted" )
|
||||||
-- Fsm:AddTransition( "Accounted", "Success", "Success" )
|
-- Fsm:AddTransition( "Accounted", "Success", "Success" )
|
||||||
Fsm:AddTransition( "Rejected", "Reject", "Aborted" )
|
Fsm:AddTransition( "Rejected", "Reject", "Aborted" )
|
||||||
Fsm:AddTransition( "Failed", "Fail", "Failed" )
|
Fsm:AddTransition( "Failed", "Fail", "Failed" )
|
||||||
|
|
||||||
|
|
||||||
---- @param #FSM_PROCESS self
|
-- @param #FSM_PROCESS self
|
||||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||||
-- @param #TASK_CARGO Task
|
-- @param #TASK_CARGO Task
|
||||||
function Fsm:OnLeaveAssigned( TaskUnit, Task )
|
function Fsm:OnLeaveAssigned( TaskUnit, Task )
|
||||||
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
||||||
|
|
||||||
self:SelectAction()
|
self:SelectAction()
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Test
|
--- Test
|
||||||
-- @param #FSM_PROCESS self
|
-- @param #FSM_PROCESS self
|
||||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||||
-- @param Tasking.Task_A2A#TASK_A2A Task
|
-- @param Tasking.Task_A2A#TASK_A2A Task
|
||||||
function Fsm:onafterRouteToRendezVous( TaskUnit, Task )
|
function Fsm:onafterRouteToRendezVous( TaskUnit, Task )
|
||||||
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
||||||
-- Determine the first Unit from the self.RendezVousSetUnit
|
-- Determine the first Unit from the self.RendezVousSetUnit
|
||||||
|
|
||||||
if Task:GetRendezVousZone( TaskUnit ) then
|
if Task:GetRendezVousZone( TaskUnit ) then
|
||||||
self:__RouteToRendezVousZone( 0.1 )
|
self:__RouteToRendezVousZone( 0.1 )
|
||||||
else
|
else
|
||||||
@@ -110,36 +108,36 @@ do -- TASK_A2A
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Test
|
--- Test
|
||||||
-- @param #FSM_PROCESS self
|
-- @param #FSM_PROCESS self
|
||||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||||
-- @param Tasking.Task#TASK_A2A Task
|
-- @param Tasking.Task#TASK_A2A Task
|
||||||
function Fsm:OnAfterArriveAtRendezVous( TaskUnit, Task )
|
function Fsm:OnAfterArriveAtRendezVous( TaskUnit, Task )
|
||||||
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
||||||
-- Determine the first Unit from the self.TargetSetUnit
|
-- Determine the first Unit from the self.TargetSetUnit
|
||||||
|
|
||||||
self:__Engage( 0.1 )
|
self:__Engage( 0.1 )
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Test
|
--- Test
|
||||||
-- @param #FSM_PROCESS self
|
-- @param #FSM_PROCESS self
|
||||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||||
-- @param Tasking.Task#TASK_A2A Task
|
-- @param Tasking.Task#TASK_A2A Task
|
||||||
function Fsm:onafterEngage( TaskUnit, Task )
|
function Fsm:onafterEngage( TaskUnit, Task )
|
||||||
self:F( { self } )
|
self:F( { self } )
|
||||||
self:__Account( 0.1 )
|
self:__Account( 0.1 )
|
||||||
self:__RouteToTarget(0.1 )
|
self:__RouteToTarget( 0.1 )
|
||||||
self:__RouteToTargets( -10 )
|
self:__RouteToTargets( -10 )
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Test
|
--- Test
|
||||||
-- @param #FSM_PROCESS self
|
-- @param #FSM_PROCESS self
|
||||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||||
-- @param Tasking.Task_A2A#TASK_A2A Task
|
-- @param Tasking.Task_A2A#TASK_A2A Task
|
||||||
function Fsm:onafterRouteToTarget( TaskUnit, Task )
|
function Fsm:onafterRouteToTarget( TaskUnit, Task )
|
||||||
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
||||||
-- Determine the first Unit from the self.TargetSetUnit
|
-- Determine the first Unit from the self.TargetSetUnit
|
||||||
|
|
||||||
if Task:GetTargetZone( TaskUnit ) then
|
if Task:GetTargetZone( TaskUnit ) then
|
||||||
self:__RouteToTargetZone( 0.1 )
|
self:__RouteToTargetZone( 0.1 )
|
||||||
else
|
else
|
||||||
@@ -152,8 +150,8 @@ do -- TASK_A2A
|
|||||||
self:__RouteToTargetPoint( 0.1 )
|
self:__RouteToTargetPoint( 0.1 )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Test
|
--- Test
|
||||||
-- @param #FSM_PROCESS self
|
-- @param #FSM_PROCESS self
|
||||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||||
-- @param Tasking.Task_A2A#TASK_A2A Task
|
-- @param Tasking.Task_A2A#TASK_A2A Task
|
||||||
@@ -165,20 +163,18 @@ do -- TASK_A2A
|
|||||||
end
|
end
|
||||||
self:__RouteToTargets( -10 )
|
self:__RouteToTargets( -10 )
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param #TASK_A2A self
|
--- @param #TASK_A2A self
|
||||||
-- @param Core.Set#SET_UNIT TargetSetUnit The set of targets.
|
-- @param Core.Set#SET_UNIT TargetSetUnit The set of targets.
|
||||||
function TASK_A2A:SetTargetSetUnit( TargetSetUnit )
|
function TASK_A2A:SetTargetSetUnit( TargetSetUnit )
|
||||||
|
|
||||||
self.TargetSetUnit = TargetSetUnit
|
self.TargetSetUnit = TargetSetUnit
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- @param #TASK_A2A self
|
--- @param #TASK_A2A self
|
||||||
function TASK_A2A:GetPlannedMenuText()
|
function TASK_A2A:GetPlannedMenuText()
|
||||||
return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )"
|
return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )"
|
||||||
@@ -188,34 +184,32 @@ do -- TASK_A2A
|
|||||||
-- @param Core.Point#COORDINATE RendezVousCoordinate The Coordinate object referencing to the 2D point where the RendezVous point is located on the map.
|
-- @param Core.Point#COORDINATE RendezVousCoordinate The Coordinate object referencing to the 2D point where the RendezVous point is located on the map.
|
||||||
-- @param #number RendezVousRange The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point.
|
-- @param #number RendezVousRange The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point.
|
||||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||||
function TASK_A2A:SetRendezVousCoordinate( RendezVousCoordinate, RendezVousRange, TaskUnit )
|
function TASK_A2A:SetRendezVousCoordinate( RendezVousCoordinate, RendezVousRange, TaskUnit )
|
||||||
|
|
||||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||||
|
|
||||||
local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT
|
local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT
|
||||||
ActRouteRendezVous:SetCoordinate( RendezVousCoordinate )
|
ActRouteRendezVous:SetCoordinate( RendezVousCoordinate )
|
||||||
ActRouteRendezVous:SetRange( RendezVousRange )
|
ActRouteRendezVous:SetRange( RendezVousRange )
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param #TASK_A2A self
|
--- @param #TASK_A2A self
|
||||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||||
-- @return Core.Point#COORDINATE The Coordinate object referencing to the 2D point where the RendezVous point is located on the map.
|
-- @return Core.Point#COORDINATE The Coordinate object referencing to the 2D point where the RendezVous point is located on the map.
|
||||||
-- @return #number The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point.
|
-- @return #number The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point.
|
||||||
function TASK_A2A:GetRendezVousCoordinate( TaskUnit )
|
function TASK_A2A:GetRendezVousCoordinate( TaskUnit )
|
||||||
|
|
||||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||||
|
|
||||||
local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT
|
local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT
|
||||||
return ActRouteRendezVous:GetCoordinate(), ActRouteRendezVous:GetRange()
|
return ActRouteRendezVous:GetCoordinate(), ActRouteRendezVous:GetRange()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- @param #TASK_A2A self
|
--- @param #TASK_A2A self
|
||||||
-- @param Core.Zone#ZONE_BASE RendezVousZone The Zone object where the RendezVous is located on the map.
|
-- @param Core.Zone#ZONE_BASE RendezVousZone The Zone object where the RendezVous is located on the map.
|
||||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||||
function TASK_A2A:SetRendezVousZone( RendezVousZone, TaskUnit )
|
function TASK_A2A:SetRendezVousZone( RendezVousZone, TaskUnit )
|
||||||
|
|
||||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||||
|
|
||||||
local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE
|
local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE
|
||||||
@@ -232,18 +226,17 @@ do -- TASK_A2A
|
|||||||
local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE
|
local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE
|
||||||
return ActRouteRendezVous:GetZone()
|
return ActRouteRendezVous:GetZone()
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param #TASK_A2A self
|
--- @param #TASK_A2A self
|
||||||
-- @param Core.Point#COORDINATE TargetCoordinate The Coordinate object where the Target is located on the map.
|
-- @param Core.Point#COORDINATE TargetCoordinate The Coordinate object where the Target is located on the map.
|
||||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||||
function TASK_A2A:SetTargetCoordinate( TargetCoordinate, TaskUnit )
|
function TASK_A2A:SetTargetCoordinate( TargetCoordinate, TaskUnit )
|
||||||
|
|
||||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||||
|
|
||||||
local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT
|
local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT
|
||||||
ActRouteTarget:SetCoordinate( TargetCoordinate )
|
ActRouteTarget:SetCoordinate( TargetCoordinate )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- @param #TASK_A2A self
|
--- @param #TASK_A2A self
|
||||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||||
@@ -256,18 +249,16 @@ do -- TASK_A2A
|
|||||||
return ActRouteTarget:GetCoordinate()
|
return ActRouteTarget:GetCoordinate()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- @param #TASK_A2A self
|
--- @param #TASK_A2A self
|
||||||
-- @param Core.Zone#ZONE_BASE TargetZone The Zone object where the Target is located on the map.
|
-- @param Core.Zone#ZONE_BASE TargetZone The Zone object where the Target is located on the map.
|
||||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||||
function TASK_A2A:SetTargetZone( TargetZone, Altitude, Heading, TaskUnit )
|
function TASK_A2A:SetTargetZone( TargetZone, Altitude, Heading, TaskUnit )
|
||||||
|
|
||||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||||
|
|
||||||
local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE
|
local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE
|
||||||
ActRouteTarget:SetZone( TargetZone, Altitude, Heading )
|
ActRouteTarget:SetZone( TargetZone, Altitude, Heading )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- @param #TASK_A2A self
|
--- @param #TASK_A2A self
|
||||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||||
@@ -281,45 +272,43 @@ do -- TASK_A2A
|
|||||||
end
|
end
|
||||||
|
|
||||||
function TASK_A2A:SetGoalTotal()
|
function TASK_A2A:SetGoalTotal()
|
||||||
|
|
||||||
self.GoalTotal = self.TargetSetUnit:Count()
|
self.GoalTotal = self.TargetSetUnit:Count()
|
||||||
end
|
end
|
||||||
|
|
||||||
function TASK_A2A:GetGoalTotal()
|
function TASK_A2A:GetGoalTotal()
|
||||||
|
|
||||||
return self.GoalTotal
|
return self.GoalTotal
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Return the relative distance to the target vicinity from the player, in order to sort the targets in the reports per distance from the threats.
|
--- Return the relative distance to the target vicinity from the player, in order to sort the targets in the reports per distance from the threats.
|
||||||
-- @param #TASK_A2A self
|
-- @param #TASK_A2A self
|
||||||
function TASK_A2A:ReportOrder( ReportGroup )
|
function TASK_A2A:ReportOrder( ReportGroup )
|
||||||
self:UpdateTaskInfo( self.DetectedItem )
|
self:UpdateTaskInfo( self.DetectedItem )
|
||||||
|
|
||||||
local Coordinate = self.TaskInfo:GetData( "Coordinate" )
|
local Coordinate = self.TaskInfo:GetData( "Coordinate" )
|
||||||
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
|
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
|
||||||
|
|
||||||
return Distance
|
return Distance
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- This method checks every 10 seconds if the goal has been reached of the task.
|
--- This method checks every 10 seconds if the goal has been reached of the task.
|
||||||
-- @param #TASK_A2A self
|
-- @param #TASK_A2A self
|
||||||
function TASK_A2A:onafterGoal( TaskUnit, From, Event, To )
|
function TASK_A2A:onafterGoal( TaskUnit, From, Event, To )
|
||||||
local TargetSetUnit = self.TargetSetUnit -- Core.Set#SET_UNIT
|
local TargetSetUnit = self.TargetSetUnit -- Core.Set#SET_UNIT
|
||||||
|
|
||||||
if TargetSetUnit:Count() == 0 then
|
if TargetSetUnit:Count() == 0 then
|
||||||
self:Success()
|
self:Success()
|
||||||
end
|
end
|
||||||
|
|
||||||
self:__Goal( -10 )
|
self:__Goal( -10 )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- @param #TASK_A2A self
|
--- @param #TASK_A2A self
|
||||||
function TASK_A2A:UpdateTaskInfo( DetectedItem )
|
function TASK_A2A:UpdateTaskInfo( DetectedItem )
|
||||||
|
|
||||||
if self:IsStatePlanned() or self:IsStateAssigned() then
|
if self:IsStatePlanned() or self:IsStateAssigned() then
|
||||||
local TargetCoordinate = DetectedItem and self.Detection:GetDetectedItemCoordinate( DetectedItem ) or self.TargetSetUnit:GetFirst():GetCoordinate()
|
local TargetCoordinate = DetectedItem and self.Detection:GetDetectedItemCoordinate( DetectedItem ) or self.TargetSetUnit:GetFirst():GetCoordinate()
|
||||||
self.TaskInfo:AddTaskName( 0, "MSOD" )
|
self.TaskInfo:AddTaskName( 0, "MSOD" )
|
||||||
self.TaskInfo:AddCoordinate( TargetCoordinate, 1, "SOD" )
|
self.TaskInfo:AddCoordinate( TargetCoordinate, 1, "SOD" )
|
||||||
|
|
||||||
@@ -343,12 +332,12 @@ do -- TASK_A2A
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
self.TaskInfo:AddTargetCount( DetectedItemsCount, 11, "O", true )
|
self.TaskInfo:AddTargetCount( DetectedItemsCount, 11, "O", true )
|
||||||
self.TaskInfo:AddTargets( DetectedItemsCount, ReportTypes:Text( ", " ), 20, "D", true )
|
self.TaskInfo:AddTargets( DetectedItemsCount, ReportTypes:Text( ", " ), 20, "D", true )
|
||||||
else
|
else
|
||||||
local DetectedItemsCount = self.TargetSetUnit:Count()
|
local DetectedItemsCount = self.TargetSetUnit:Count()
|
||||||
local DetectedItemsTypes = self.TargetSetUnit:GetTypeNames()
|
local DetectedItemsTypes = self.TargetSetUnit:GetTypeNames()
|
||||||
self.TaskInfo:AddTargetCount( DetectedItemsCount, 11, "O", true )
|
self.TaskInfo:AddTargetCount( DetectedItemsCount, 11, "O", true )
|
||||||
self.TaskInfo:AddTargets( DetectedItemsCount, DetectedItemsTypes, 20, "D", true )
|
self.TaskInfo:AddTargets( DetectedItemsCount, DetectedItemsTypes, 20, "D", true )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -359,8 +348,8 @@ do -- TASK_A2A
|
|||||||
-- @param Tasking.CommandCenter#COMMANDCENTER CommandCenter The command center.
|
-- @param Tasking.CommandCenter#COMMANDCENTER CommandCenter The command center.
|
||||||
-- @param Wrapper.Group#GROUP TaskGroup The player group.
|
-- @param Wrapper.Group#GROUP TaskGroup The player group.
|
||||||
function TASK_A2A:GetAutoAssignPriority( AutoAssignMethod, CommandCenter, TaskGroup )
|
function TASK_A2A:GetAutoAssignPriority( AutoAssignMethod, CommandCenter, TaskGroup )
|
||||||
|
|
||||||
if AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Random then
|
if AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Random then
|
||||||
return math.random( 1, 9 )
|
return math.random( 1, 9 )
|
||||||
elseif AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Distance then
|
elseif AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Distance then
|
||||||
local Coordinate = self.TaskInfo:GetData( "Coordinate" )
|
local Coordinate = self.TaskInfo:GetData( "Coordinate" )
|
||||||
@@ -373,8 +362,7 @@ do -- TASK_A2A
|
|||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
do -- TASK_A2A_INTERCEPT
|
do -- TASK_A2A_INTERCEPT
|
||||||
|
|
||||||
@@ -384,44 +372,39 @@ do -- TASK_A2A_INTERCEPT
|
|||||||
-- @extends Tasking.Task#TASK
|
-- @extends Tasking.Task#TASK
|
||||||
|
|
||||||
--- Defines an intercept task for a human player to be executed.
|
--- Defines an intercept task for a human player to be executed.
|
||||||
-- When enemy planes need to be intercepted by human players, use this task type to urgen the players to get out there!
|
-- When enemy planes need to be intercepted by human players, use this task type to urge the players to get out there!
|
||||||
--
|
--
|
||||||
-- The TASK_A2A_INTERCEPT is used by the @{Tasking.Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create intercept tasks
|
-- The TASK_A2A_INTERCEPT is used by the @{Tasking.Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create intercept tasks
|
||||||
-- based on detected airborne enemy targets intruding friendly airspace.
|
-- based on detected airborne enemy targets intruding friendly airspace.
|
||||||
--
|
--
|
||||||
-- The task is defined for a @{Tasking.Mission#MISSION}, where a friendly @{Core.Set#SET_GROUP} consisting of GROUPs with one human players each, is intercepting the targets.
|
-- The task is defined for a @{Tasking.Mission#MISSION}, where a friendly @{Core.Set#SET_GROUP} consisting of GROUPs with one human players each, is intercepting the targets.
|
||||||
-- The task is given a name and a briefing, that is used in the menu structure and in the reporting.
|
-- The task is given a name and a briefing, that is used in the menu structure and in the reporting.
|
||||||
--
|
--
|
||||||
-- @field #TASK_A2A_INTERCEPT
|
-- @field #TASK_A2A_INTERCEPT
|
||||||
TASK_A2A_INTERCEPT = {
|
TASK_A2A_INTERCEPT = {
|
||||||
ClassName = "TASK_A2A_INTERCEPT",
|
ClassName = "TASK_A2A_INTERCEPT"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- Instantiates a new TASK_A2A_INTERCEPT.
|
--- Instantiates a new TASK_A2A_INTERCEPT.
|
||||||
-- @param #TASK_A2A_INTERCEPT self
|
-- @param #TASK_A2A_INTERCEPT self
|
||||||
-- @param Tasking.Mission#MISSION Mission
|
-- @param Tasking.Mission#MISSION Mission
|
||||||
-- @param Core.Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned.
|
-- @param Core.Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned.
|
||||||
-- @param #string TaskName The name of the Task.
|
-- @param #string TaskName The name of the Task.
|
||||||
-- @param Core.Set#SET_UNIT TargetSetUnit
|
-- @param Core.Set#SET_UNIT TargetSetUnit
|
||||||
-- @param #string TaskBriefing The briefing of the task.
|
-- @param #string TaskBriefing The briefing of the task.
|
||||||
-- @return #TASK_A2A_INTERCEPT
|
-- @return #TASK_A2A_INTERCEPT
|
||||||
function TASK_A2A_INTERCEPT:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing )
|
function TASK_A2A_INTERCEPT:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing )
|
||||||
local self = BASE:Inherit( self, TASK_A2A:New( Mission, SetGroup, TaskName, TargetSetUnit, "INTERCEPT", TaskBriefing ) ) -- #TASK_A2A_INTERCEPT
|
local self = BASE:Inherit( self, TASK_A2A:New( Mission, SetGroup, TaskName, TargetSetUnit, "INTERCEPT", TaskBriefing ) ) -- #TASK_A2A_INTERCEPT
|
||||||
self:F()
|
self:F()
|
||||||
|
|
||||||
Mission:AddTask( self )
|
Mission:AddTask( self )
|
||||||
|
|
||||||
self:SetBriefing(
|
self:SetBriefing( TaskBriefing or "Intercept incoming intruders.\n" )
|
||||||
TaskBriefing or
|
|
||||||
"Intercept incoming intruders.\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set a score when a target in scope of the A2A attack, has been destroyed .
|
--- Set a score when a target in scope of the A2A attack, has been destroyed.
|
||||||
-- @param #TASK_A2A_INTERCEPT self
|
-- @param #TASK_A2A_INTERCEPT self
|
||||||
-- @param #string PlayerName The name of the player.
|
-- @param #string PlayerName The name of the player.
|
||||||
-- @param #number Score The score in points to be granted when task process has been achieved.
|
-- @param #number Score The score in points to be granted when task process has been achieved.
|
||||||
@@ -433,7 +416,7 @@ do -- TASK_A2A_INTERCEPT
|
|||||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||||
|
|
||||||
ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has intercepted a target.", Score )
|
ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has intercepted a target.", Score )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -449,7 +432,7 @@ do -- TASK_A2A_INTERCEPT
|
|||||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||||
|
|
||||||
ProcessUnit:AddScore( "Success", "All targets have been successfully intercepted!", Score )
|
ProcessUnit:AddScore( "Success", "All targets have been successfully intercepted!", Score )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -465,14 +448,12 @@ do -- TASK_A2A_INTERCEPT
|
|||||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||||
|
|
||||||
ProcessUnit:AddScore( "Failed", "The intercept has failed!", Penalty )
|
ProcessUnit:AddScore( "Failed", "The intercept has failed!", Penalty )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
do -- TASK_A2A_SWEEP
|
do -- TASK_A2A_SWEEP
|
||||||
|
|
||||||
--- The TASK_A2A_SWEEP class
|
--- The TASK_A2A_SWEEP class
|
||||||
@@ -484,20 +465,18 @@ do -- TASK_A2A_SWEEP
|
|||||||
-- A sweep task needs to be given when targets were detected but somehow the detection was lost.
|
-- A sweep task needs to be given when targets were detected but somehow the detection was lost.
|
||||||
-- Most likely, these enemy planes are hidden in the mountains or are flying under radar.
|
-- Most likely, these enemy planes are hidden in the mountains or are flying under radar.
|
||||||
-- These enemy planes need to be sweeped by human players, and use this task type to urge the players to get out there and find those enemy fighters.
|
-- These enemy planes need to be sweeped by human players, and use this task type to urge the players to get out there and find those enemy fighters.
|
||||||
--
|
--
|
||||||
-- The TASK_A2A_SWEEP is used by the @{Tasking.Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create sweep tasks
|
-- The TASK_A2A_SWEEP is used by the @{Tasking.Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create sweep tasks
|
||||||
-- based on detected airborne enemy targets intruding friendly airspace, for which the detection has been lost for more than 60 seconds.
|
-- based on detected airborne enemy targets intruding friendly airspace, for which the detection has been lost for more than 60 seconds.
|
||||||
--
|
--
|
||||||
-- The task is defined for a @{Tasking.Mission#MISSION}, where a friendly @{Core.Set#SET_GROUP} consisting of GROUPs with one human players each, is sweeping the targets.
|
-- The task is defined for a @{Tasking.Mission#MISSION}, where a friendly @{Core.Set#SET_GROUP} consisting of GROUPs with one human players each, is sweeping the targets.
|
||||||
-- The task is given a name and a briefing, that is used in the menu structure and in the reporting.
|
-- The task is given a name and a briefing, that is used in the menu structure and in the reporting.
|
||||||
--
|
--
|
||||||
-- @field #TASK_A2A_SWEEP
|
-- @field #TASK_A2A_SWEEP
|
||||||
TASK_A2A_SWEEP = {
|
TASK_A2A_SWEEP = {
|
||||||
ClassName = "TASK_A2A_SWEEP",
|
ClassName = "TASK_A2A_SWEEP"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- Instantiates a new TASK_A2A_SWEEP.
|
--- Instantiates a new TASK_A2A_SWEEP.
|
||||||
-- @param #TASK_A2A_SWEEP self
|
-- @param #TASK_A2A_SWEEP self
|
||||||
-- @param Tasking.Mission#MISSION Mission
|
-- @param Tasking.Mission#MISSION Mission
|
||||||
@@ -509,29 +488,26 @@ do -- TASK_A2A_SWEEP
|
|||||||
function TASK_A2A_SWEEP:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing )
|
function TASK_A2A_SWEEP:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing )
|
||||||
local self = BASE:Inherit( self, TASK_A2A:New( Mission, SetGroup, TaskName, TargetSetUnit, "SWEEP", TaskBriefing ) ) -- #TASK_A2A_SWEEP
|
local self = BASE:Inherit( self, TASK_A2A:New( Mission, SetGroup, TaskName, TargetSetUnit, "SWEEP", TaskBriefing ) ) -- #TASK_A2A_SWEEP
|
||||||
self:F()
|
self:F()
|
||||||
|
|
||||||
Mission:AddTask( self )
|
Mission:AddTask( self )
|
||||||
|
|
||||||
self:SetBriefing(
|
self:SetBriefing( TaskBriefing or "Perform a fighter sweep. Incoming intruders were detected and could be hiding at the location.\n" )
|
||||||
TaskBriefing or
|
|
||||||
"Perform a fighter sweep. Incoming intruders were detected and could be hiding at the location.\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param #TASK_A2A_SWEEP self
|
--- @param #TASK_A2A_SWEEP self
|
||||||
function TASK_A2A_SWEEP:onafterGoal( TaskUnit, From, Event, To )
|
function TASK_A2A_SWEEP:onafterGoal( TaskUnit, From, Event, To )
|
||||||
local TargetSetUnit = self.TargetSetUnit -- Core.Set#SET_UNIT
|
local TargetSetUnit = self.TargetSetUnit -- Core.Set#SET_UNIT
|
||||||
|
|
||||||
if TargetSetUnit:Count() == 0 then
|
if TargetSetUnit:Count() == 0 then
|
||||||
self:Success()
|
self:Success()
|
||||||
end
|
end
|
||||||
|
|
||||||
self:__Goal( -10 )
|
self:__Goal( -10 )
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set a score when a target in scope of the A2A attack, has been destroyed .
|
--- Set a score when a target in scope of the A2A attack, has been destroyed.
|
||||||
-- @param #TASK_A2A_SWEEP self
|
-- @param #TASK_A2A_SWEEP self
|
||||||
-- @param #string PlayerName The name of the player.
|
-- @param #string PlayerName The name of the player.
|
||||||
-- @param #number Score The score in points to be granted when task process has been achieved.
|
-- @param #number Score The score in points to be granted when task process has been achieved.
|
||||||
@@ -543,7 +519,7 @@ do -- TASK_A2A_SWEEP
|
|||||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||||
|
|
||||||
ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has sweeped a target.", Score )
|
ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has sweeped a target.", Score )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -559,7 +535,7 @@ do -- TASK_A2A_SWEEP
|
|||||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||||
|
|
||||||
ProcessUnit:AddScore( "Success", "All targets have been successfully sweeped!", Score )
|
ProcessUnit:AddScore( "Success", "All targets have been successfully sweeped!", Score )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -575,13 +551,12 @@ do -- TASK_A2A_SWEEP
|
|||||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||||
|
|
||||||
ProcessUnit:AddScore( "Failed", "The sweep has failed!", Penalty )
|
ProcessUnit:AddScore( "Failed", "The sweep has failed!", Penalty )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
do -- TASK_A2A_ENGAGE
|
do -- TASK_A2A_ENGAGE
|
||||||
|
|
||||||
--- The TASK_A2A_ENGAGE class
|
--- The TASK_A2A_ENGAGE class
|
||||||
@@ -591,42 +566,37 @@ do -- TASK_A2A_ENGAGE
|
|||||||
|
|
||||||
--- Defines an engage task for a human player to be executed.
|
--- Defines an engage task for a human player to be executed.
|
||||||
-- When enemy planes are close to human players, use this task type is used urge the players to get out there!
|
-- When enemy planes are close to human players, use this task type is used urge the players to get out there!
|
||||||
--
|
--
|
||||||
-- The TASK_A2A_ENGAGE is used by the @{Tasking.Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create engage tasks
|
-- The TASK_A2A_ENGAGE is used by the @{Tasking.Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create engage tasks
|
||||||
-- based on detected airborne enemy targets intruding friendly airspace.
|
-- based on detected airborne enemy targets intruding friendly airspace.
|
||||||
--
|
--
|
||||||
-- The task is defined for a @{Tasking.Mission#MISSION}, where a friendly @{Core.Set#SET_GROUP} consisting of GROUPs with one human players each, is engaging the targets.
|
-- The task is defined for a @{Tasking.Mission#MISSION}, where a friendly @{Core.Set#SET_GROUP} consisting of GROUPs with one human players each, is engaging the targets.
|
||||||
-- The task is given a name and a briefing, that is used in the menu structure and in the reporting.
|
-- The task is given a name and a briefing, that is used in the menu structure and in the reporting.
|
||||||
--
|
--
|
||||||
-- @field #TASK_A2A_ENGAGE
|
-- @field #TASK_A2A_ENGAGE
|
||||||
TASK_A2A_ENGAGE = {
|
TASK_A2A_ENGAGE = {
|
||||||
ClassName = "TASK_A2A_ENGAGE",
|
ClassName = "TASK_A2A_ENGAGE"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- Instantiates a new TASK_A2A_ENGAGE.
|
--- Instantiates a new TASK_A2A_ENGAGE.
|
||||||
-- @param #TASK_A2A_ENGAGE self
|
-- @param #TASK_A2A_ENGAGE self
|
||||||
-- @param Tasking.Mission#MISSION Mission
|
-- @param Tasking.Mission#MISSION Mission
|
||||||
-- @param Core.Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned.
|
-- @param Core.Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned.
|
||||||
-- @param #string TaskName The name of the Task.
|
-- @param #string TaskName The name of the Task.
|
||||||
-- @param Core.Set#SET_UNIT TargetSetUnit
|
-- @param Core.Set#SET_UNIT TargetSetUnit
|
||||||
-- @param #string TaskBriefing The briefing of the task.
|
-- @param #string TaskBriefing The briefing of the task.
|
||||||
-- @return #TASK_A2A_ENGAGE self
|
-- @return #TASK_A2A_ENGAGE self
|
||||||
function TASK_A2A_ENGAGE:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing )
|
function TASK_A2A_ENGAGE:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing )
|
||||||
local self = BASE:Inherit( self, TASK_A2A:New( Mission, SetGroup, TaskName, TargetSetUnit, "ENGAGE", TaskBriefing ) ) -- #TASK_A2A_ENGAGE
|
local self = BASE:Inherit( self, TASK_A2A:New( Mission, SetGroup, TaskName, TargetSetUnit, "ENGAGE", TaskBriefing ) ) -- #TASK_A2A_ENGAGE
|
||||||
self:F()
|
self:F()
|
||||||
|
|
||||||
Mission:AddTask( self )
|
Mission:AddTask( self )
|
||||||
|
|
||||||
self:SetBriefing(
|
self:SetBriefing( TaskBriefing or "Bogeys are nearby! Players close by are ordered to ENGAGE the intruders!\n" )
|
||||||
TaskBriefing or
|
|
||||||
"Bogeys are nearby! Players close by are ordered to ENGAGE the intruders!\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set a score when a target in scope of the A2A attack, has been destroyed .
|
--- Set a score when a target in scope of the A2A attack, has been destroyed .
|
||||||
-- @param #TASK_A2A_ENGAGE self
|
-- @param #TASK_A2A_ENGAGE self
|
||||||
-- @param #string PlayerName The name of the player.
|
-- @param #string PlayerName The name of the player.
|
||||||
@@ -639,7 +609,7 @@ do -- TASK_A2A_ENGAGE
|
|||||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||||
|
|
||||||
ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has engaged and destroyed a target.", Score )
|
ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has engaged and destroyed a target.", Score )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -655,7 +625,7 @@ do -- TASK_A2A_ENGAGE
|
|||||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||||
|
|
||||||
ProcessUnit:AddScore( "Success", "All targets have been successfully engaged!", Score )
|
ProcessUnit:AddScore( "Success", "All targets have been successfully engaged!", Score )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -671,7 +641,7 @@ do -- TASK_A2A_ENGAGE
|
|||||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||||
|
|
||||||
ProcessUnit:AddScore( "Failed", "The target engagement has failed!", Penalty )
|
ProcessUnit:AddScore( "Failed", "The target engagement has failed!", Penalty )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
--- **Tasking** - Dynamically allocates A2A tasks to human players, based on detected airborne targets through an EWR network.
|
--- **Tasking** - Dynamically allocates A2A tasks to human players, based on detected airborne targets through an EWR network.
|
||||||
--
|
--
|
||||||
-- **Features:**
|
-- **Features:**
|
||||||
--
|
--
|
||||||
-- * Dynamically assign tasks to human players based on detected targets.
|
-- * Dynamically assign tasks to human players based on detected targets.
|
||||||
-- * Dynamically change the tasks as the tactical situation evolves during the mission.
|
-- * Dynamically change the tasks as the tactical situation evolves during the mission.
|
||||||
-- * Dynamically assign (CAP) Control Air Patrols tasks for human players to perform CAP.
|
-- * Dynamically assign (CAP) Control Air Patrols tasks for human players to perform CAP.
|
||||||
@@ -11,15 +11,15 @@
|
|||||||
-- * Define different ranges to engage upon intruders.
|
-- * Define different ranges to engage upon intruders.
|
||||||
-- * Keep task achievements.
|
-- * Keep task achievements.
|
||||||
-- * Score task achievements.
|
-- * Score task achievements.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ### Author: **FlightControl**
|
-- ### Author: **FlightControl**
|
||||||
--
|
--
|
||||||
-- ### Contributions:
|
-- ### Contributions:
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- @module Tasking.Task_A2A_Dispatcher
|
-- @module Tasking.Task_A2A_Dispatcher
|
||||||
-- @image Task_A2A_Dispatcher.JPG
|
-- @image Task_A2A_Dispatcher.JPG
|
||||||
|
|
||||||
@@ -30,72 +30,72 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
-- @extends Tasking.DetectionManager#DETECTION_MANAGER
|
-- @extends Tasking.DetectionManager#DETECTION_MANAGER
|
||||||
|
|
||||||
--- Orchestrates the dynamic dispatching of tasks upon groups of detected units determined a @{Set} of EWR installation groups.
|
--- Orchestrates the dynamic dispatching of tasks upon groups of detected units determined a @{Set} of EWR installation groups.
|
||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
-- The EWR will detect units, will group them, and will dispatch @{Task}s to groups. Depending on the type of target detected, different tasks will be dispatched.
|
-- The EWR will detect units, will group them, and will dispatch @{Task}s to groups. Depending on the type of target detected, different tasks will be dispatched.
|
||||||
-- Find a summary below describing for which situation a task type is created:
|
-- Find a summary below describing for which situation a task type is created:
|
||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
-- * **INTERCEPT Task**: Is created when the target is known, is detected and within a danger zone, and there is no friendly airborne in range.
|
-- * **INTERCEPT Task**: Is created when the target is known, is detected and within a danger zone, and there is no friendly airborne in range.
|
||||||
-- * **SWEEP Task**: Is created when the target is unknown, was detected and the last position is only known, and within a danger zone, and there is no friendly airborne in range.
|
-- * **SWEEP Task**: Is created when the target is unknown, was detected and the last position is only known, and within a danger zone, and there is no friendly airborne in range.
|
||||||
-- * **ENGAGE Task**: Is created when the target is known, is detected and within a danger zone, and there is a friendly airborne in range, that will receive this task.
|
-- * **ENGAGE Task**: Is created when the target is known, is detected and within a danger zone, and there is a friendly airborne in range, that will receive this task.
|
||||||
--
|
--
|
||||||
-- ## 1. TASK\_A2A\_DISPATCHER constructor:
|
-- ## 1. TASK\_A2A\_DISPATCHER constructor:
|
||||||
--
|
--
|
||||||
-- The @{#TASK_A2A_DISPATCHER.New}() method creates a new TASK\_A2A\_DISPATCHER instance.
|
-- The @{#TASK_A2A_DISPATCHER.New}() method creates a new TASK\_A2A\_DISPATCHER instance.
|
||||||
--
|
--
|
||||||
-- ### 1.1. Define or set the **Mission**:
|
-- ### 1.1. Define or set the **Mission**:
|
||||||
--
|
--
|
||||||
-- Tasking is executed to accomplish missions. Therefore, a MISSION object needs to be given as the first parameter.
|
-- Tasking is executed to accomplish missions. Therefore, a MISSION object needs to be given as the first parameter.
|
||||||
--
|
--
|
||||||
-- local HQ = GROUP:FindByName( "HQ", "Bravo" )
|
-- local HQ = GROUP:FindByName( "HQ", "Bravo" )
|
||||||
-- local CommandCenter = COMMANDCENTER:New( HQ, "Lima" )
|
-- local CommandCenter = COMMANDCENTER:New( HQ, "Lima" )
|
||||||
-- local Mission = MISSION:New( CommandCenter, "A2A Mission", "High", "Watch the air enemy units being detected.", coalition.side.RED )
|
-- local Mission = MISSION:New( CommandCenter, "A2A Mission", "High", "Watch the air enemy units being detected.", coalition.side.RED )
|
||||||
--
|
--
|
||||||
-- Missions are governed by COMMANDCENTERS, so, ensure you have a COMMANDCENTER object installed and setup within your mission.
|
-- Missions are governed by COMMANDCENTERS, so, ensure you have a COMMANDCENTER object installed and setup within your mission.
|
||||||
-- Create the MISSION object, and hook it under the command center.
|
-- Create the MISSION object, and hook it under the command center.
|
||||||
--
|
--
|
||||||
-- ### 1.2. Build a set of the groups seated by human players:
|
-- ### 1.2. Build a set of the groups seated by human players:
|
||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
-- A set or collection of the groups wherein human players can be seated, these can be clients or units that can be joined as a slot or jumping into.
|
-- A set or collection of the groups wherein human players can be seated, these can be clients or units that can be joined as a slot or jumping into.
|
||||||
--
|
--
|
||||||
-- local AttackGroups = SET_GROUP:New():FilterCoalitions( "red" ):FilterPrefixes( "Defender" ):FilterStart()
|
-- local AttackGroups = SET_GROUP:New():FilterCoalitions( "red" ):FilterPrefixes( "Defender" ):FilterStart()
|
||||||
--
|
--
|
||||||
-- The set is built using the SET_GROUP class. Apply any filter criteria to identify the correct groups for your mission.
|
-- The set is built using the SET_GROUP class. Apply any filter criteria to identify the correct groups for your mission.
|
||||||
-- Only these slots or units will be able to execute the mission and will receive tasks for this mission, once available.
|
-- Only these slots or units will be able to execute the mission and will receive tasks for this mission, once available.
|
||||||
--
|
--
|
||||||
-- ### 1.3. Define the **EWR network**:
|
-- ### 1.3. Define the **EWR network**:
|
||||||
--
|
--
|
||||||
-- As part of the TASK\_A2A\_DISPATCHER constructor, an EWR network must be given as the third parameter.
|
-- As part of the TASK\_A2A\_DISPATCHER constructor, an EWR network must be given as the third parameter.
|
||||||
-- An EWR network, or, Early Warning Radar network, is used to early detect potential airborne targets and to understand the position of patrolling targets of the enemy.
|
-- An EWR network, or, Early Warning Radar network, is used to early detect potential airborne targets and to understand the position of patrolling targets of the enemy.
|
||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
-- Typically EWR networks are setup using 55G6 EWR, 1L13 EWR, Hawk sr and Patriot str ground based radar units.
|
-- Typically EWR networks are setup using 55G6 EWR, 1L13 EWR, Hawk sr and Patriot str ground based radar units.
|
||||||
-- These radars have different ranges and 55G6 EWR and 1L13 EWR radars are Eastern Bloc units (eg Russia, Ukraine, Georgia) while the Hawk and Patriot radars are Western (eg US).
|
-- These radars have different ranges and 55G6 EWR and 1L13 EWR radars are Eastern Bloc units (eg Russia, Ukraine, Georgia) while the Hawk and Patriot radars are Western (eg US).
|
||||||
-- Additionally, ANY other radar capable unit can be part of the EWR network! Also AWACS airborne units, planes, helicopters can help to detect targets, as long as they have radar.
|
-- Additionally, ANY other radar capable unit can be part of the EWR network! Also AWACS airborne units, planes, helicopters can help to detect targets, as long as they have radar.
|
||||||
-- The position of these units is very important as they need to provide enough coverage
|
-- The position of these units is very important as they need to provide enough coverage
|
||||||
-- to pick up enemy aircraft as they approach so that CAP and GCI flights can be tasked to intercept them.
|
-- to pick up enemy aircraft as they approach so that CAP and GCI flights can be tasked to intercept them.
|
||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
-- Additionally in a hot war situation where the border is no longer respected the placement of radars has a big effect on how fast the war escalates.
|
-- Additionally in a hot war situation where the border is no longer respected the placement of radars has a big effect on how fast the war escalates.
|
||||||
-- For example if they are a long way forward and can detect enemy planes on the ground and taking off
|
-- For example if they are a long way forward and can detect enemy planes on the ground and taking off
|
||||||
-- they will start to vector CAP and GCI flights to attack them straight away which will immediately draw a response from the other coalition.
|
-- they will start to vector CAP and GCI flights to attack them straight away which will immediately draw a response from the other coalition.
|
||||||
-- Having the radars further back will mean a slower escalation because fewer targets will be detected and
|
-- Having the radars further back will mean a slower escalation because fewer targets will be detected and
|
||||||
-- therefore less CAP and GCI flights will spawn and this will tend to make just the border area active rather than a melee over the whole map.
|
-- therefore less CAP and GCI flights will spawn and this will tend to make just the border area active rather than a melee over the whole map.
|
||||||
-- It all depends on what the desired effect is.
|
-- It all depends on what the desired effect is.
|
||||||
--
|
--
|
||||||
-- EWR networks are **dynamically constructed**, that is, they form part of the @{Functional.Detection#DETECTION_BASE} object that is given as the input parameter of the TASK\_A2A\_DISPATCHER class.
|
-- EWR networks are **dynamically constructed**, that is, they form part of the @{Functional.Detection#DETECTION_BASE} object that is given as the input parameter of the TASK\_A2A\_DISPATCHER class.
|
||||||
-- By defining in a **smart way the names or name prefixes of the groups** with EWR capable units, these groups will be **automatically added or deleted** from the EWR network,
|
-- By defining in a **smart way the names or name prefixes of the groups** with EWR capable units, these groups will be **automatically added or deleted** from the EWR network,
|
||||||
-- increasing or decreasing the radar coverage of the Early Warning System.
|
-- increasing or decreasing the radar coverage of the Early Warning System.
|
||||||
--
|
--
|
||||||
-- See the following example to setup an EWR network containing EWR stations and AWACS.
|
-- See the following example to setup an EWR network containing EWR stations and AWACS.
|
||||||
--
|
--
|
||||||
-- local EWRSet = SET_GROUP:New():FilterPrefixes( "EWR" ):FilterCoalitions("red"):FilterStart()
|
-- local EWRSet = SET_GROUP:New():FilterPrefixes( "EWR" ):FilterCoalitions("red"):FilterStart()
|
||||||
--
|
--
|
||||||
-- local EWRDetection = DETECTION_AREAS:New( EWRSet, 6000 )
|
-- local EWRDetection = DETECTION_AREAS:New( EWRSet, 6000 )
|
||||||
@@ -104,50 +104,50 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
--
|
--
|
||||||
-- -- Setup the A2A dispatcher, and initialize it.
|
-- -- Setup the A2A dispatcher, and initialize it.
|
||||||
-- A2ADispatcher = TASK_A2A_DISPATCHER:New( Mission, AttackGroups, EWRDetection )
|
-- A2ADispatcher = TASK_A2A_DISPATCHER:New( Mission, AttackGroups, EWRDetection )
|
||||||
--
|
--
|
||||||
-- The above example creates a SET_GROUP instance, and stores this in the variable (object) **EWRSet**.
|
-- The above example creates a SET_GROUP instance, and stores this in the variable (object) **EWRSet**.
|
||||||
-- **EWRSet** is then being configured to filter all active groups with a group name starting with **EWR** to be included in the Set.
|
-- **EWRSet** is then being configured to filter all active groups with a group name starting with **EWR** to be included in the Set.
|
||||||
-- **EWRSet** is then being ordered to start the dynamic filtering. Note that any destroy or new spawn of a group with the above names will be removed or added to the Set.
|
-- **EWRSet** is then being ordered to start the dynamic filtering. Note that any destroy or new spawn of a group with the above names will be removed or added to the Set.
|
||||||
-- Then a new **EWRDetection** object is created from the class DETECTION_AREAS. A grouping radius of 6000 is choosen, which is 6km.
|
-- Then a new **EWRDetection** object is created from the class DETECTION_AREAS. A grouping radius of 6000 is chosen, which is 6 km.
|
||||||
-- The **EWRDetection** object is then passed to the @{#TASK_A2A_DISPATCHER.New}() method to indicate the EWR network configuration and setup the A2A tasking and detection mechanism.
|
-- The **EWRDetection** object is then passed to the @{#TASK_A2A_DISPATCHER.New}() method to indicate the EWR network configuration and setup the A2A tasking and detection mechanism.
|
||||||
--
|
--
|
||||||
-- ### 2. Define the detected **target grouping radius**:
|
-- ### 2. Define the detected **target grouping radius**:
|
||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
-- The target grouping radius is a property of the Detection object, that was passed to the AI\_A2A\_DISPATCHER object, but can be changed.
|
-- The target grouping radius is a property of the Detection object, that was passed to the AI\_A2A\_DISPATCHER object, but can be changed.
|
||||||
-- The grouping radius should not be too small, but also depends on the types of planes and the era of the simulation.
|
-- The grouping radius should not be too small, but also depends on the types of planes and the era of the simulation.
|
||||||
-- Fast planes like in the 80s, need a larger radius than WWII planes.
|
-- Fast planes like in the 80s, need a larger radius than WWII planes.
|
||||||
-- Typically I suggest to use 30000 for new generation planes and 10000 for older era aircraft.
|
-- Typically I suggest to use 30000 for new generation planes and 10000 for older era aircraft.
|
||||||
--
|
--
|
||||||
-- Note that detected targets are constantly re-grouped, that is, when certain detected aircraft are moving further than the group radius, then these aircraft will become a separate
|
-- Note that detected targets are constantly re-grouped, that is, when certain detected aircraft are moving further than the group radius, then these aircraft will become a separate
|
||||||
-- group being detected. This may result in additional GCI being started by the dispatcher! So don't make this value too small!
|
-- group being detected. This may result in additional GCI being started by the dispatcher! So don't make this value too small!
|
||||||
--
|
--
|
||||||
-- ## 3. Set the **Engage radius**:
|
-- ## 3. Set the **Engage radius**:
|
||||||
--
|
--
|
||||||
-- Define the radius to engage any target by airborne friendlies, which are executing cap or returning from an intercept mission.
|
-- Define the radius to engage any target by airborne friendlies, which are executing cap or returning from an intercept mission.
|
||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
-- So, if there is a target area detected and reported,
|
-- So, if there is a target area detected and reported,
|
||||||
-- then any friendlies that are airborne near this target area,
|
-- then any friendlies that are airborne near this target area,
|
||||||
-- will be commanded to (re-)engage that target when available (if no other tasks were commanded).
|
-- will be commanded to (re-)engage that target when available (if no other tasks were commanded).
|
||||||
-- For example, if 100000 is given as a value, then any friendly that is airborne within 100km from the detected target,
|
-- For example, if 100000 is given as a value, then any friendly that is airborne within 100km from the detected target,
|
||||||
-- will be considered to receive the command to engage that target area.
|
-- will be considered to receive the command to engage that target area.
|
||||||
-- You need to evaluate the value of this parameter carefully.
|
-- You need to evaluate the value of this parameter carefully.
|
||||||
-- If too small, more intercept missions may be triggered upon detected target areas.
|
-- If too small, more intercept missions may be triggered upon detected target areas.
|
||||||
-- If too large, any airborne cap may not be able to reach the detected target area in time, because it is too far.
|
-- If too large, any airborne cap may not be able to reach the detected target area in time, because it is too far.
|
||||||
--
|
--
|
||||||
-- ## 4. Set **Scoring** and **Messages**:
|
-- ## 4. Set **Scoring** and **Messages**:
|
||||||
--
|
--
|
||||||
-- The TASK\_A2A\_DISPATCHER is a state machine. It triggers the event Assign when a new player joins a @{Task} dispatched by the TASK\_A2A\_DISPATCHER.
|
-- The TASK\_A2A\_DISPATCHER is a state machine. It triggers the event Assign when a new player joins a @{Task} dispatched by the TASK\_A2A\_DISPATCHER.
|
||||||
-- An _event handler_ can be defined to catch the **Assign** event, and add **additional processing** to set _scoring_ and to _define messages_,
|
-- An _event handler_ can be defined to catch the **Assign** event, and add **additional processing** to set _scoring_ and to _define messages_,
|
||||||
-- when the player reaches certain achievements in the task.
|
-- when the player reaches certain achievements in the task.
|
||||||
--
|
--
|
||||||
-- The prototype to handle the **Assign** event needs to be developed as follows:
|
-- The prototype to handle the **Assign** event needs to be developed as follows:
|
||||||
--
|
--
|
||||||
-- TaskDispatcher = TASK_A2A_DISPATCHER:New( ... )
|
-- TaskDispatcher = TASK_A2A_DISPATCHER:New( ... )
|
||||||
--
|
--
|
||||||
-- --- @param #TaskDispatcher self
|
-- --- @param #TaskDispatcher self
|
||||||
-- -- @param #string From Contains the name of the state from where the Event was triggered.
|
-- -- @param #string From Contains the name of the state from where the Event was triggered.
|
||||||
-- -- @param #string Event Contains the name of the event that was triggered. In this case Assign.
|
-- -- @param #string Event Contains the name of the event that was triggered. In this case Assign.
|
||||||
@@ -160,22 +160,22 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
-- Task:SetScoreOnSuccess( PlayerName, 200, TaskUnit )
|
-- Task:SetScoreOnSuccess( PlayerName, 200, TaskUnit )
|
||||||
-- Task:SetScoreOnFail( PlayerName, -100, TaskUnit )
|
-- Task:SetScoreOnFail( PlayerName, -100, TaskUnit )
|
||||||
-- end
|
-- end
|
||||||
--
|
--
|
||||||
-- The **OnAfterAssign** method (function) is added to the TaskDispatcher object.
|
-- The **OnAfterAssign** method (function) is added to the TaskDispatcher object.
|
||||||
-- This method will be called when a new player joins a unit in the set of groups in scope of the dispatcher.
|
-- This method will be called when a new player joins a unit in the set of groups in scope of the dispatcher.
|
||||||
-- So, this method will be called only **ONCE** when a player joins a unit in scope of the task.
|
-- So, this method will be called only **ONCE** when a player joins a unit in scope of the task.
|
||||||
--
|
--
|
||||||
-- The TASK class implements various methods to additional **set scoring** for player achievements:
|
-- The TASK class implements various methods to additional **set scoring** for player achievements:
|
||||||
--
|
--
|
||||||
-- * @{Tasking.Task#TASK.SetScoreOnProgress}() will add additional scores when a player achieves **Progress** while executing the task.
|
-- * @{Tasking.Task#TASK.SetScoreOnProgress}() will add additional scores when a player achieves **Progress** while executing the task.
|
||||||
-- Examples of **task progress** can be destroying units, arriving at zones etc.
|
-- Examples of **task progress** can be destroying units, arriving at zones etc.
|
||||||
--
|
--
|
||||||
-- * @{Tasking.Task#TASK.SetScoreOnSuccess}() will add additional scores when the task goes into **Success** state.
|
-- * @{Tasking.Task#TASK.SetScoreOnSuccess}() will add additional scores when the task goes into **Success** state.
|
||||||
-- This means the **task has been successfully completed**.
|
-- This means the **task has been successfully completed**.
|
||||||
--
|
--
|
||||||
-- * @{Tasking.Task#TASK.SetScoreOnSuccess}() will add additional (negative) scores when the task goes into **Failed** state.
|
-- * @{Tasking.Task#TASK.SetScoreOnSuccess}() will add additional (negative) scores when the task goes into **Failed** state.
|
||||||
-- This means the **task has not been successfully completed**, and the scores must be given with a negative value!
|
-- This means the **task has not been successfully completed**, and the scores must be given with a negative value!
|
||||||
--
|
--
|
||||||
-- @field #TASK_A2A_DISPATCHER
|
-- @field #TASK_A2A_DISPATCHER
|
||||||
TASK_A2A_DISPATCHER = {
|
TASK_A2A_DISPATCHER = {
|
||||||
ClassName = "TASK_A2A_DISPATCHER",
|
ClassName = "TASK_A2A_DISPATCHER",
|
||||||
@@ -184,8 +184,7 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
Tasks = {},
|
Tasks = {},
|
||||||
SweepZones = {},
|
SweepZones = {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
--- TASK_A2A_DISPATCHER constructor.
|
--- TASK_A2A_DISPATCHER constructor.
|
||||||
-- @param #TASK_A2A_DISPATCHER self
|
-- @param #TASK_A2A_DISPATCHER self
|
||||||
-- @param Tasking.Mission#MISSION Mission The mission for which the task dispatching is done.
|
-- @param Tasking.Mission#MISSION Mission The mission for which the task dispatching is done.
|
||||||
@@ -193,22 +192,21 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
-- @param Functional.Detection#DETECTION_BASE Detection The detection results that are used to dynamically assign new tasks to human players.
|
-- @param Functional.Detection#DETECTION_BASE Detection The detection results that are used to dynamically assign new tasks to human players.
|
||||||
-- @return #TASK_A2A_DISPATCHER self
|
-- @return #TASK_A2A_DISPATCHER self
|
||||||
function TASK_A2A_DISPATCHER:New( Mission, SetGroup, Detection )
|
function TASK_A2A_DISPATCHER:New( Mission, SetGroup, Detection )
|
||||||
|
|
||||||
-- Inherits from DETECTION_MANAGER
|
-- Inherits from DETECTION_MANAGER
|
||||||
local self = BASE:Inherit( self, DETECTION_MANAGER:New( SetGroup, Detection ) ) -- #TASK_A2A_DISPATCHER
|
local self = BASE:Inherit( self, DETECTION_MANAGER:New( SetGroup, Detection ) ) -- #TASK_A2A_DISPATCHER
|
||||||
|
|
||||||
self.Detection = Detection
|
self.Detection = Detection
|
||||||
self.Mission = Mission
|
self.Mission = Mission
|
||||||
self.FlashNewTask = false
|
self.FlashNewTask = false
|
||||||
|
|
||||||
-- TODO: Check detection through radar.
|
-- TODO: Check detection through radar.
|
||||||
self.Detection:FilterCategories( Unit.Category.AIRPLANE, Unit.Category.HELICOPTER )
|
self.Detection:FilterCategories( Unit.Category.AIRPLANE, Unit.Category.HELICOPTER )
|
||||||
self.Detection:InitDetectRadar( true )
|
self.Detection:InitDetectRadar( true )
|
||||||
self.Detection:SetRefreshTimeInterval( 30 )
|
self.Detection:SetRefreshTimeInterval( 30 )
|
||||||
|
|
||||||
self:AddTransition( "Started", "Assign", "Started" )
|
self:AddTransition( "Started", "Assign", "Started" )
|
||||||
|
|
||||||
|
|
||||||
--- OnAfter Transition Handler for Event Assign.
|
--- OnAfter Transition Handler for Event Assign.
|
||||||
-- @function [parent=#TASK_A2A_DISPATCHER] OnAfterAssign
|
-- @function [parent=#TASK_A2A_DISPATCHER] OnAfterAssign
|
||||||
-- @param #TASK_A2A_DISPATCHER self
|
-- @param #TASK_A2A_DISPATCHER self
|
||||||
@@ -220,14 +218,13 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
-- @param #string PlayerName
|
-- @param #string PlayerName
|
||||||
|
|
||||||
self:__Start( 5 )
|
self:__Start( 5 )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Define the radius to when an ENGAGE task will be generated for any nearby by airborne friendlies, which are executing cap or returning from an intercept mission.
|
--- Define the radius to when an ENGAGE task will be generated for any nearby by airborne friendlies, which are executing cap or returning from an intercept mission.
|
||||||
-- So, if there is a target area detected and reported,
|
-- So, if there is a target area detected and reported,
|
||||||
-- then any friendlies that are airborne near this target area,
|
-- then any friendlies that are airborne near this target area,
|
||||||
-- will be commanded to (re-)engage that target when available (if no other tasks were commanded).
|
-- will be commanded to (re-)engage that target when available (if no other tasks were commanded).
|
||||||
-- An ENGAGE task will be created for those pilots.
|
-- An ENGAGE task will be created for those pilots.
|
||||||
-- For example, if 100000 is given as a value, then any friendly that is airborne within 100km from the detected target,
|
-- For example, if 100000 is given as a value, then any friendly that is airborne within 100km from the detected target,
|
||||||
@@ -239,27 +236,27 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
-- @param #number EngageRadius (Optional, Default = 100000) The radius to report friendlies near the target.
|
-- @param #number EngageRadius (Optional, Default = 100000) The radius to report friendlies near the target.
|
||||||
-- @return #TASK_A2A_DISPATCHER
|
-- @return #TASK_A2A_DISPATCHER
|
||||||
-- @usage
|
-- @usage
|
||||||
--
|
--
|
||||||
-- -- Set 50km as the radius to engage any target by airborne friendlies.
|
-- -- Set 50km as the radius to engage any target by airborne friendlies.
|
||||||
-- TaskA2ADispatcher:SetEngageRadius( 50000 )
|
-- TaskA2ADispatcher:SetEngageRadius( 50000 )
|
||||||
--
|
--
|
||||||
-- -- Set 100km as the radius to engage any target by airborne friendlies.
|
-- -- Set 100km as the radius to engage any target by airborne friendlies.
|
||||||
-- TaskA2ADispatcher:SetEngageRadius() -- 100000 is the default value.
|
-- TaskA2ADispatcher:SetEngageRadius() -- 100000 is the default value.
|
||||||
--
|
--
|
||||||
function TASK_A2A_DISPATCHER:SetEngageRadius( EngageRadius )
|
function TASK_A2A_DISPATCHER:SetEngageRadius( EngageRadius )
|
||||||
|
|
||||||
self.Detection:SetFriendliesRange( EngageRadius or 100000 )
|
self.Detection:SetFriendliesRange( EngageRadius or 100000 )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set flashing player messages on or off
|
--- Set flashing player messages on or off
|
||||||
-- @param #TASK_A2A_DISPATCHER self
|
-- @param #TASK_A2A_DISPATCHER self
|
||||||
-- @param #boolean onoff Set messages on (true) or off (false)
|
-- @param #boolean onoff Set messages on (true) or off (false)
|
||||||
function TASK_A2A_DISPATCHER:SetSendMessages( onoff )
|
function TASK_A2A_DISPATCHER:SetSendMessages( onoff )
|
||||||
self.FlashNewTask = onoff
|
self.FlashNewTask = onoff
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Creates an INTERCEPT task when there are targets for it.
|
--- Creates an INTERCEPT task when there are targets for it.
|
||||||
-- @param #TASK_A2A_DISPATCHER self
|
-- @param #TASK_A2A_DISPATCHER self
|
||||||
-- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem
|
-- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem
|
||||||
@@ -267,26 +264,25 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
-- @return #nil If there are no targets to be set.
|
-- @return #nil If there are no targets to be set.
|
||||||
function TASK_A2A_DISPATCHER:EvaluateINTERCEPT( DetectedItem )
|
function TASK_A2A_DISPATCHER:EvaluateINTERCEPT( DetectedItem )
|
||||||
self:F( { DetectedItem.ItemID } )
|
self:F( { DetectedItem.ItemID } )
|
||||||
|
|
||||||
local DetectedSet = DetectedItem.Set
|
local DetectedSet = DetectedItem.Set
|
||||||
local DetectedZone = DetectedItem.Zone
|
local DetectedZone = DetectedItem.Zone
|
||||||
|
|
||||||
-- Check if there is at least one UNIT in the DetectedSet is visible.
|
-- Check if there is at least one UNIT in the DetectedSet is visible.
|
||||||
|
|
||||||
if DetectedItem.IsDetected == true then
|
if DetectedItem.IsDetected == true then
|
||||||
|
|
||||||
-- Here we're doing something advanced... We're copying the DetectedSet.
|
-- Here we're doing something advanced... We're copying the DetectedSet.
|
||||||
local TargetSetUnit = SET_UNIT:New()
|
local TargetSetUnit = SET_UNIT:New()
|
||||||
TargetSetUnit:SetDatabase( DetectedSet )
|
TargetSetUnit:SetDatabase( DetectedSet )
|
||||||
TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection.
|
TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection.
|
||||||
|
|
||||||
return TargetSetUnit
|
return TargetSetUnit
|
||||||
end
|
end
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Creates an SWEEP task when there are targets for it.
|
--- Creates an SWEEP task when there are targets for it.
|
||||||
-- @param #TASK_A2A_DISPATCHER self
|
-- @param #TASK_A2A_DISPATCHER self
|
||||||
-- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem
|
-- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem
|
||||||
@@ -294,10 +290,9 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
-- @return #nil If there are no targets to be set.
|
-- @return #nil If there are no targets to be set.
|
||||||
function TASK_A2A_DISPATCHER:EvaluateSWEEP( DetectedItem )
|
function TASK_A2A_DISPATCHER:EvaluateSWEEP( DetectedItem )
|
||||||
self:F( { DetectedItem.ItemID } )
|
self:F( { DetectedItem.ItemID } )
|
||||||
|
|
||||||
local DetectedSet = DetectedItem.Set
|
|
||||||
local DetectedZone = DetectedItem.Zone
|
|
||||||
|
|
||||||
|
local DetectedSet = DetectedItem.Set
|
||||||
|
local DetectedZone = DetectedItem.Zone -- TODO: This seems unused, remove?
|
||||||
|
|
||||||
if DetectedItem.IsDetected == false then
|
if DetectedItem.IsDetected == false then
|
||||||
|
|
||||||
@@ -305,14 +300,13 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
local TargetSetUnit = SET_UNIT:New()
|
local TargetSetUnit = SET_UNIT:New()
|
||||||
TargetSetUnit:SetDatabase( DetectedSet )
|
TargetSetUnit:SetDatabase( DetectedSet )
|
||||||
TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection.
|
TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection.
|
||||||
|
|
||||||
return TargetSetUnit
|
return TargetSetUnit
|
||||||
end
|
end
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Creates an ENGAGE task when there are human friendlies airborne near the targets.
|
--- Creates an ENGAGE task when there are human friendlies airborne near the targets.
|
||||||
-- @param #TASK_A2A_DISPATCHER self
|
-- @param #TASK_A2A_DISPATCHER self
|
||||||
-- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem
|
-- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem
|
||||||
@@ -320,13 +314,12 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
-- @return #nil If there are no targets to be set.
|
-- @return #nil If there are no targets to be set.
|
||||||
function TASK_A2A_DISPATCHER:EvaluateENGAGE( DetectedItem )
|
function TASK_A2A_DISPATCHER:EvaluateENGAGE( DetectedItem )
|
||||||
self:F( { DetectedItem.ItemID } )
|
self:F( { DetectedItem.ItemID } )
|
||||||
|
|
||||||
local DetectedSet = DetectedItem.Set
|
local DetectedSet = DetectedItem.Set
|
||||||
local DetectedZone = DetectedItem.Zone
|
local DetectedZone = DetectedItem.Zone -- TODO: This seems unused, remove?
|
||||||
|
|
||||||
local PlayersCount, PlayersReport = self:GetPlayerFriendliesNearBy( DetectedItem )
|
local PlayersCount, PlayersReport = self:GetPlayerFriendliesNearBy( DetectedItem )
|
||||||
|
|
||||||
|
|
||||||
-- Only allow ENGAGE when there are Players near the zone, and when the Area has detected items since the last run in a 60 seconds time zone.
|
-- Only allow ENGAGE when there are Players near the zone, and when the Area has detected items since the last run in a 60 seconds time zone.
|
||||||
if PlayersCount > 0 and DetectedItem.IsDetected == true then
|
if PlayersCount > 0 and DetectedItem.IsDetected == true then
|
||||||
|
|
||||||
@@ -334,16 +327,13 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
local TargetSetUnit = SET_UNIT:New()
|
local TargetSetUnit = SET_UNIT:New()
|
||||||
TargetSetUnit:SetDatabase( DetectedSet )
|
TargetSetUnit:SetDatabase( DetectedSet )
|
||||||
TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection.
|
TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection.
|
||||||
|
|
||||||
return TargetSetUnit
|
return TargetSetUnit
|
||||||
end
|
end
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- Evaluates the removal of the Task from the Mission.
|
--- Evaluates the removal of the Task from the Mission.
|
||||||
-- Can only occur when the DetectedItem is Changed AND the state of the Task is "Planned".
|
-- Can only occur when the DetectedItem is Changed AND the state of the Task is "Planned".
|
||||||
-- @param #TASK_A2A_DISPATCHER self
|
-- @param #TASK_A2A_DISPATCHER self
|
||||||
@@ -354,24 +344,24 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
-- @param #boolean DetectedItemChange
|
-- @param #boolean DetectedItemChange
|
||||||
-- @return Tasking.Task#TASK
|
-- @return Tasking.Task#TASK
|
||||||
function TASK_A2A_DISPATCHER:EvaluateRemoveTask( Mission, Task, Detection, DetectedItem, DetectedItemIndex, DetectedItemChanged )
|
function TASK_A2A_DISPATCHER:EvaluateRemoveTask( Mission, Task, Detection, DetectedItem, DetectedItemIndex, DetectedItemChanged )
|
||||||
|
|
||||||
if Task then
|
if Task then
|
||||||
|
|
||||||
if Task:IsStatePlanned() then
|
if Task:IsStatePlanned() then
|
||||||
local TaskName = Task:GetName()
|
local TaskName = Task:GetName()
|
||||||
local TaskType = TaskName:match( "(%u+)%.%d+" )
|
local TaskType = TaskName:match( "(%u+)%.%d+" )
|
||||||
|
|
||||||
self:T2( { TaskType = TaskType } )
|
self:T2( { TaskType = TaskType } )
|
||||||
|
|
||||||
local Remove = false
|
local Remove = false
|
||||||
|
|
||||||
local IsPlayers = Detection:IsPlayersNearBy( DetectedItem )
|
local IsPlayers = Detection:IsPlayersNearBy( DetectedItem )
|
||||||
if TaskType == "ENGAGE" then
|
if TaskType == "ENGAGE" then
|
||||||
if IsPlayers == false then
|
if IsPlayers == false then
|
||||||
Remove = true
|
Remove = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if TaskType == "INTERCEPT" then
|
if TaskType == "INTERCEPT" then
|
||||||
if IsPlayers == true then
|
if IsPlayers == true then
|
||||||
Remove = true
|
Remove = true
|
||||||
@@ -380,7 +370,7 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
Remove = true
|
Remove = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if TaskType == "SWEEP" then
|
if TaskType == "SWEEP" then
|
||||||
if DetectedItem.IsDetected == true then
|
if DetectedItem.IsDetected == true then
|
||||||
Remove = true
|
Remove = true
|
||||||
@@ -388,18 +378,18 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
end
|
end
|
||||||
|
|
||||||
local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT
|
local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT
|
||||||
--DetectedSet:Flush( self )
|
-- DetectedSet:Flush( self )
|
||||||
--self:F( { DetectedSetCount = DetectedSet:Count() } )
|
-- self:F( { DetectedSetCount = DetectedSet:Count() } )
|
||||||
if DetectedSet:Count() == 0 then
|
if DetectedSet:Count() == 0 then
|
||||||
Remove = true
|
Remove = true
|
||||||
end
|
end
|
||||||
|
|
||||||
if DetectedItemChanged == true or Remove then
|
if DetectedItemChanged == true or Remove then
|
||||||
Task = self:RemoveTask( DetectedItemIndex )
|
Task = self:RemoveTask( DetectedItemIndex )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return Task
|
return Task
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -408,10 +398,10 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
-- @param DetectedItem
|
-- @param DetectedItem
|
||||||
-- @return #number, Core.CommandCenter#REPORT
|
-- @return #number, Core.CommandCenter#REPORT
|
||||||
function TASK_A2A_DISPATCHER:GetFriendliesNearBy( DetectedItem )
|
function TASK_A2A_DISPATCHER:GetFriendliesNearBy( DetectedItem )
|
||||||
|
|
||||||
local DetectedSet = DetectedItem.Set
|
local DetectedSet = DetectedItem.Set
|
||||||
local FriendlyUnitsNearBy = self.Detection:GetFriendliesNearBy( DetectedItem, Unit.Category.AIRPLANE )
|
local FriendlyUnitsNearBy = self.Detection:GetFriendliesNearBy( DetectedItem, Unit.Category.AIRPLANE )
|
||||||
|
|
||||||
local FriendlyTypes = {}
|
local FriendlyTypes = {}
|
||||||
local FriendliesCount = 0
|
local FriendliesCount = 0
|
||||||
|
|
||||||
@@ -423,27 +413,26 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
local FriendlyUnitThreatLevel = FriendlyUnit:GetThreatLevel()
|
local FriendlyUnitThreatLevel = FriendlyUnit:GetThreatLevel()
|
||||||
FriendliesCount = FriendliesCount + 1
|
FriendliesCount = FriendliesCount + 1
|
||||||
local FriendlyType = FriendlyUnit:GetTypeName()
|
local FriendlyType = FriendlyUnit:GetTypeName()
|
||||||
FriendlyTypes[FriendlyType] = FriendlyTypes[FriendlyType] and ( FriendlyTypes[FriendlyType] + 1 ) or 1
|
FriendlyTypes[FriendlyType] = FriendlyTypes[FriendlyType] and (FriendlyTypes[FriendlyType] + 1) or 1
|
||||||
if DetectedTreatLevel < FriendlyUnitThreatLevel + 2 then
|
if DetectedTreatLevel < FriendlyUnitThreatLevel + 2 then
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--self:F( { FriendliesCount = FriendliesCount } )
|
-- self:F( { FriendliesCount = FriendliesCount } )
|
||||||
|
|
||||||
local FriendlyTypesReport = REPORT:New()
|
local FriendlyTypesReport = REPORT:New()
|
||||||
|
|
||||||
if FriendliesCount > 0 then
|
if FriendliesCount > 0 then
|
||||||
for FriendlyType, FriendlyTypeCount in pairs( FriendlyTypes ) do
|
for FriendlyType, FriendlyTypeCount in pairs( FriendlyTypes ) do
|
||||||
FriendlyTypesReport:Add( string.format("%d of %s", FriendlyTypeCount, FriendlyType ) )
|
FriendlyTypesReport:Add( string.format( "%d of %s", FriendlyTypeCount, FriendlyType ) )
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
FriendlyTypesReport:Add( "-" )
|
FriendlyTypesReport:Add( "-" )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
return FriendliesCount, FriendlyTypesReport
|
return FriendliesCount, FriendlyTypesReport
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -452,10 +441,10 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
-- @param DetectedItem
|
-- @param DetectedItem
|
||||||
-- @return #number, Core.CommandCenter#REPORT
|
-- @return #number, Core.CommandCenter#REPORT
|
||||||
function TASK_A2A_DISPATCHER:GetPlayerFriendliesNearBy( DetectedItem )
|
function TASK_A2A_DISPATCHER:GetPlayerFriendliesNearBy( DetectedItem )
|
||||||
|
|
||||||
local DetectedSet = DetectedItem.Set
|
local DetectedSet = DetectedItem.Set
|
||||||
local PlayersNearBy = self.Detection:GetPlayersNearBy( DetectedItem )
|
local PlayersNearBy = self.Detection:GetPlayersNearBy( DetectedItem )
|
||||||
|
|
||||||
local PlayerTypes = {}
|
local PlayerTypes = {}
|
||||||
local PlayersCount = 0
|
local PlayersCount = 0
|
||||||
|
|
||||||
@@ -464,7 +453,7 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
for PlayerUnitName, PlayerUnitData in pairs( PlayersNearBy ) do
|
for PlayerUnitName, PlayerUnitData in pairs( PlayersNearBy ) do
|
||||||
local PlayerUnit = PlayerUnitData -- Wrapper.Unit#UNIT
|
local PlayerUnit = PlayerUnitData -- Wrapper.Unit#UNIT
|
||||||
local PlayerName = PlayerUnit:GetPlayerName()
|
local PlayerName = PlayerUnit:GetPlayerName()
|
||||||
--self:F( { PlayerName = PlayerName, PlayerUnit = PlayerUnit } )
|
-- self:F( { PlayerName = PlayerName, PlayerUnit = PlayerUnit } )
|
||||||
if PlayerUnit:IsAirPlane() and PlayerName ~= nil then
|
if PlayerUnit:IsAirPlane() and PlayerName ~= nil then
|
||||||
local FriendlyUnitThreatLevel = PlayerUnit:GetThreatLevel()
|
local FriendlyUnitThreatLevel = PlayerUnit:GetThreatLevel()
|
||||||
PlayersCount = PlayersCount + 1
|
PlayersCount = PlayersCount + 1
|
||||||
@@ -474,20 +463,19 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local PlayerTypesReport = REPORT:New()
|
local PlayerTypesReport = REPORT:New()
|
||||||
|
|
||||||
if PlayersCount > 0 then
|
if PlayersCount > 0 then
|
||||||
for PlayerName, PlayerType in pairs( PlayerTypes ) do
|
for PlayerName, PlayerType in pairs( PlayerTypes ) do
|
||||||
PlayerTypesReport:Add( string.format('"%s" in %s', PlayerName, PlayerType ) )
|
PlayerTypesReport:Add( string.format( '"%s" in %s', PlayerName, PlayerType ) )
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
PlayerTypesReport:Add( "-" )
|
PlayerTypesReport:Add( "-" )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
return PlayersCount, PlayerTypesReport
|
return PlayersCount, PlayerTypesReport
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -496,24 +484,23 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
self.Tasks[TaskIndex] = nil
|
self.Tasks[TaskIndex] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Assigns tasks in relation to the detected items to the @{Core.Set#SET_GROUP}.
|
--- Assigns tasks in relation to the detected items to the @{Core.Set#SET_GROUP}.
|
||||||
-- @param #TASK_A2A_DISPATCHER self
|
-- @param #TASK_A2A_DISPATCHER self
|
||||||
-- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Functional.Detection#DETECTION_BASE} derived object.
|
-- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Functional.Detection#DETECTION_BASE} derived object.
|
||||||
-- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop.
|
-- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop.
|
||||||
function TASK_A2A_DISPATCHER:ProcessDetected( Detection )
|
function TASK_A2A_DISPATCHER:ProcessDetected( Detection )
|
||||||
self:F()
|
self:F()
|
||||||
|
|
||||||
local AreaMsg = {}
|
local AreaMsg = {}
|
||||||
local TaskMsg = {}
|
local TaskMsg = {}
|
||||||
local ChangeMsg = {}
|
local ChangeMsg = {}
|
||||||
|
|
||||||
local Mission = self.Mission
|
local Mission = self.Mission
|
||||||
|
|
||||||
if Mission:IsIDLE() or Mission:IsENGAGED() then
|
if Mission:IsIDLE() or Mission:IsENGAGED() then
|
||||||
|
|
||||||
local TaskReport = REPORT:New()
|
local TaskReport = REPORT:New()
|
||||||
|
|
||||||
-- Checking the task queue for the dispatcher, and removing any obsolete task!
|
-- Checking the task queue for the dispatcher, and removing any obsolete task!
|
||||||
for TaskIndex, TaskData in pairs( self.Tasks ) do
|
for TaskIndex, TaskData in pairs( self.Tasks ) do
|
||||||
local Task = TaskData -- Tasking.Task#TASK
|
local Task = TaskData -- Tasking.Task#TASK
|
||||||
@@ -531,18 +518,18 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
|
|
||||||
-- Now that all obsolete tasks are removed, loop through the detected targets.
|
-- Now that all obsolete tasks are removed, loop through the detected targets.
|
||||||
for DetectedItemID, DetectedItem in pairs( Detection:GetDetectedItems() ) do
|
for DetectedItemID, DetectedItem in pairs( Detection:GetDetectedItems() ) do
|
||||||
|
|
||||||
local DetectedItem = DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem
|
local DetectedItem = DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem
|
||||||
local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT
|
local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT
|
||||||
local DetectedCount = DetectedSet:Count()
|
local DetectedCount = DetectedSet:Count()
|
||||||
local DetectedZone = DetectedItem.Zone
|
local DetectedZone = DetectedItem.Zone
|
||||||
--self:F( { "Targets in DetectedItem", DetectedItem.ItemID, DetectedSet:Count(), tostring( DetectedItem ) } )
|
-- self:F( { "Targets in DetectedItem", DetectedItem.ItemID, DetectedSet:Count(), tostring( DetectedItem ) } )
|
||||||
--DetectedSet:Flush( self )
|
-- DetectedSet:Flush( self )
|
||||||
|
|
||||||
local DetectedID = DetectedItem.ID
|
local DetectedID = DetectedItem.ID
|
||||||
local TaskIndex = DetectedItem.Index
|
local TaskIndex = DetectedItem.Index
|
||||||
local DetectedItemChanged = DetectedItem.Changed
|
local DetectedItemChanged = DetectedItem.Changed
|
||||||
|
|
||||||
local Task = self.Tasks[TaskIndex]
|
local Task = self.Tasks[TaskIndex]
|
||||||
Task = self:EvaluateRemoveTask( Mission, Task, Detection, DetectedItem, TaskIndex, DetectedItemChanged ) -- Task will be removed if it is planned and changed.
|
Task = self:EvaluateRemoveTask( Mission, Task, Detection, DetectedItem, TaskIndex, DetectedItemChanged ) -- Task will be removed if it is planned and changed.
|
||||||
|
|
||||||
@@ -565,7 +552,7 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
Task = TASK_A2A_SWEEP:New( Mission, self.SetGroup, string.format( "SWEEP.%03d", DetectedID ), TargetSetUnit )
|
Task = TASK_A2A_SWEEP:New( Mission, self.SetGroup, string.format( "SWEEP.%03d", DetectedID ), TargetSetUnit )
|
||||||
Task:SetDetection( Detection, DetectedItem )
|
Task:SetDetection( Detection, DetectedItem )
|
||||||
Task:UpdateTaskInfo( DetectedItem )
|
Task:UpdateTaskInfo( DetectedItem )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -582,7 +569,7 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
function Task.OnEnterCancelled( Task, From, Event, To )
|
function Task.OnEnterCancelled( Task, From, Event, To )
|
||||||
self:Cancelled( Task )
|
self:Cancelled( Task )
|
||||||
end
|
end
|
||||||
|
|
||||||
function Task.OnEnterFailed( Task, From, Event, To )
|
function Task.OnEnterFailed( Task, From, Event, To )
|
||||||
self:Failed( Task )
|
self:Failed( Task )
|
||||||
end
|
end
|
||||||
@@ -590,38 +577,38 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
function Task.OnEnterAborted( Task, From, Event, To )
|
function Task.OnEnterAborted( Task, From, Event, To )
|
||||||
self:Aborted( Task )
|
self:Aborted( Task )
|
||||||
end
|
end
|
||||||
|
|
||||||
TaskReport:Add( Task:GetName() )
|
TaskReport:Add( Task:GetName() )
|
||||||
else
|
else
|
||||||
self:F("This should not happen")
|
self:F( "This should not happen" )
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if Task then
|
if Task then
|
||||||
local FriendliesCount, FriendliesReport = self:GetFriendliesNearBy( DetectedItem, Unit.Category.AIRPLANE )
|
local FriendliesCount, FriendliesReport = self:GetFriendliesNearBy( DetectedItem, Unit.Category.AIRPLANE )
|
||||||
Task.TaskInfo:AddText( "Friendlies", string.format( "%d ( %s )", FriendliesCount, FriendliesReport:Text( "," ) ), 40, "MOD" )
|
Task.TaskInfo:AddText( "Friendlies", string.format( "%d ( %s )", FriendliesCount, FriendliesReport:Text( "," ) ), 40, "MOD" )
|
||||||
local PlayersCount, PlayersReport = self:GetPlayerFriendliesNearBy( DetectedItem )
|
local PlayersCount, PlayersReport = self:GetPlayerFriendliesNearBy( DetectedItem )
|
||||||
Task.TaskInfo:AddText( "Players", string.format( "%d ( %s )", PlayersCount, PlayersReport:Text( "," ) ), 40, "MOD" )
|
Task.TaskInfo:AddText( "Players", string.format( "%d ( %s )", PlayersCount, PlayersReport:Text( "," ) ), 40, "MOD" )
|
||||||
end
|
end
|
||||||
|
|
||||||
-- OK, so the tasking has been done, now delete the changes reported for the area.
|
-- OK, so the tasking has been done, now delete the changes reported for the area.
|
||||||
Detection:AcceptChanges( DetectedItem )
|
Detection:AcceptChanges( DetectedItem )
|
||||||
end
|
end
|
||||||
|
|
||||||
-- TODO set menus using the HQ coordinator
|
-- TODO set menus using the HQ coordinator
|
||||||
Mission:GetCommandCenter():SetMenu()
|
Mission:GetCommandCenter():SetMenu()
|
||||||
|
|
||||||
local TaskText = TaskReport:Text(", ")
|
local TaskText = TaskReport:Text( ", " )
|
||||||
|
|
||||||
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
|
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
|
||||||
if ( not Mission:IsGroupAssigned(TaskGroup) ) and TaskText ~= "" and (self.FlashNewTask) then
|
if (not Mission:IsGroupAssigned( TaskGroup )) and TaskText ~= "" and (self.FlashNewTask) then
|
||||||
Mission:GetCommandCenter():MessageToGroup( string.format( "%s has tasks %s. Subscribe to a task using the radio menu.", Mission:GetShortText(), TaskText ), TaskGroup )
|
Mission:GetCommandCenter():MessageToGroup( string.format( "%s has tasks %s. Subscribe to a task using the radio menu.", Mission:GetShortText(), TaskText ), TaskGroup )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
--- **Tasking** - The TASK_A2G models tasks for players in Air to Ground engagements.
|
--- **Tasking** - The TASK_A2G models tasks for players in Air to Ground engagements.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ### Author: **FlightControl**
|
-- ### Author: **FlightControl**
|
||||||
--
|
--
|
||||||
-- ### Contributions:
|
-- ### Contributions:
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- @module Tasking.Task_A2G
|
-- @module Tasking.Task_A2G
|
||||||
-- @image MOOSE.JPG
|
-- @image MOOSE.JPG
|
||||||
|
|
||||||
@@ -18,29 +18,29 @@ do -- TASK_A2G
|
|||||||
-- @field Core.Set#SET_UNIT TargetSetUnit
|
-- @field Core.Set#SET_UNIT TargetSetUnit
|
||||||
-- @extends Tasking.Task#TASK
|
-- @extends Tasking.Task#TASK
|
||||||
|
|
||||||
--- The TASK_A2G class defines Air To Ground tasks for a @{Set} of Target Units,
|
--- The TASK_A2G class defines Air To Ground tasks for a @{Set} of Target Units,
|
||||||
-- based on the tasking capabilities defined in @{Tasking.Task#TASK}.
|
-- based on the tasking capabilities defined in @{Tasking.Task#TASK}.
|
||||||
-- The TASK_A2G is implemented using a @{Core.Fsm#FSM_TASK}, and has the following statuses:
|
-- The TASK_A2G is implemented using a @{Core.Fsm#FSM_TASK}, and has the following statuses:
|
||||||
--
|
--
|
||||||
-- * **None**: Start of the process
|
-- * **None**: Start of the process
|
||||||
-- * **Planned**: The A2G task is planned.
|
-- * **Planned**: The A2G task is planned.
|
||||||
-- * **Assigned**: The A2G task is assigned to a @{Wrapper.Group#GROUP}.
|
-- * **Assigned**: The A2G task is assigned to a @{Wrapper.Group#GROUP}.
|
||||||
-- * **Success**: The A2G task is successfully completed.
|
-- * **Success**: The A2G task is successfully completed.
|
||||||
-- * **Failed**: The A2G task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ.
|
-- * **Failed**: The A2G task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ.
|
||||||
--
|
--
|
||||||
-- ## 1) Set the scoring of achievements in an A2G attack.
|
-- ## 1) Set the scoring of achievements in an A2G attack.
|
||||||
--
|
--
|
||||||
-- Scoring or penalties can be given in the following circumstances:
|
-- Scoring or penalties can be given in the following circumstances:
|
||||||
--
|
--
|
||||||
-- * @{#TASK_A2G.SetScoreOnDestroy}(): Set a score when a target in scope of the A2G attack, has been destroyed.
|
-- * @{#TASK_A2G.SetScoreOnDestroy}(): Set a score when a target in scope of the A2G attack, has been destroyed.
|
||||||
-- * @{#TASK_A2G.SetScoreOnSuccess}(): Set a score when all the targets in scope of the A2G attack, have been destroyed.
|
-- * @{#TASK_A2G.SetScoreOnSuccess}(): Set a score when all the targets in scope of the A2G attack, have been destroyed.
|
||||||
-- * @{#TASK_A2G.SetPenaltyOnFailed}(): Set a penalty when the A2G attack has failed.
|
-- * @{#TASK_A2G.SetPenaltyOnFailed}(): Set a penalty when the A2G attack has failed.
|
||||||
--
|
--
|
||||||
-- @field #TASK_A2G
|
-- @field #TASK_A2G
|
||||||
TASK_A2G = {
|
TASK_A2G = {
|
||||||
ClassName = "TASK_A2G",
|
ClassName = "TASK_A2G"
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Instantiates a new TASK_A2G.
|
--- Instantiates a new TASK_A2G.
|
||||||
-- @param #TASK_A2G self
|
-- @param #TASK_A2G self
|
||||||
-- @param Tasking.Mission#MISSION Mission
|
-- @param Tasking.Mission#MISSION Mission
|
||||||
@@ -54,53 +54,51 @@ do -- TASK_A2G
|
|||||||
function TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskType, TaskBriefing )
|
function TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskType, TaskBriefing )
|
||||||
local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, TaskType, TaskBriefing ) ) -- Tasking.Task#TASK_A2G
|
local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, TaskType, TaskBriefing ) ) -- Tasking.Task#TASK_A2G
|
||||||
self:F()
|
self:F()
|
||||||
|
|
||||||
self.TargetSetUnit = TargetSetUnit
|
self.TargetSetUnit = TargetSetUnit
|
||||||
self.TaskType = TaskType
|
self.TaskType = TaskType
|
||||||
|
|
||||||
local Fsm = self:GetUnitProcess()
|
local Fsm = self:GetUnitProcess()
|
||||||
|
|
||||||
Fsm:AddTransition( "Assigned", "RouteToRendezVous", "RoutingToRendezVous" )
|
Fsm:AddTransition( "Assigned", "RouteToRendezVous", "RoutingToRendezVous" )
|
||||||
Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtRendezVous" } )
|
Fsm:AddProcess( "RoutingToRendezVous", "RouteToRendezVousPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtRendezVous" } )
|
||||||
Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtRendezVous" } )
|
Fsm:AddProcess( "RoutingToRendezVous", "RouteToRendezVousZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtRendezVous" } )
|
||||||
|
|
||||||
Fsm:AddTransition( { "Arrived", "RoutingToRendezVous" }, "ArriveAtRendezVous", "ArrivedAtRendezVous" )
|
Fsm:AddTransition( { "Arrived", "RoutingToRendezVous" }, "ArriveAtRendezVous", "ArrivedAtRendezVous" )
|
||||||
|
|
||||||
Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "Engage", "Engaging" )
|
Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "Engage", "Engaging" )
|
||||||
Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "HoldAtRendezVous", "HoldingAtRendezVous" )
|
Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "HoldAtRendezVous", "HoldingAtRendezVous" )
|
||||||
|
|
||||||
Fsm:AddProcess ( "Engaging", "Account", ACT_ACCOUNT_DEADS:New(), {} )
|
Fsm:AddProcess( "Engaging", "Account", ACT_ACCOUNT_DEADS:New(), {} )
|
||||||
Fsm:AddTransition( "Engaging", "RouteToTarget", "Engaging" )
|
Fsm:AddTransition( "Engaging", "RouteToTarget", "Engaging" )
|
||||||
Fsm:AddProcess( "Engaging", "RouteToTargetZone", ACT_ROUTE_ZONE:New(), {} )
|
Fsm:AddProcess( "Engaging", "RouteToTargetZone", ACT_ROUTE_ZONE:New(), {} )
|
||||||
Fsm:AddProcess( "Engaging", "RouteToTargetPoint", ACT_ROUTE_POINT:New(), {} )
|
Fsm:AddProcess( "Engaging", "RouteToTargetPoint", ACT_ROUTE_POINT:New(), {} )
|
||||||
Fsm:AddTransition( "Engaging", "RouteToTargets", "Engaging" )
|
Fsm:AddTransition( "Engaging", "RouteToTargets", "Engaging" )
|
||||||
|
|
||||||
--Fsm:AddTransition( "Accounted", "DestroyedAll", "Accounted" )
|
-- Fsm:AddTransition( "Accounted", "DestroyedAll", "Accounted" )
|
||||||
--Fsm:AddTransition( "Accounted", "Success", "Success" )
|
-- Fsm:AddTransition( "Accounted", "Success", "Success" )
|
||||||
Fsm:AddTransition( "Rejected", "Reject", "Aborted" )
|
Fsm:AddTransition( "Rejected", "Reject", "Aborted" )
|
||||||
Fsm:AddTransition( "Failed", "Fail", "Failed" )
|
Fsm:AddTransition( "Failed", "Fail", "Failed" )
|
||||||
|
|
||||||
|
|
||||||
|
--- Test
|
||||||
--- Test
|
|
||||||
-- @param #FSM_PROCESS self
|
-- @param #FSM_PROCESS self
|
||||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||||
-- @param Tasking.Task_A2G#TASK_A2G Task
|
-- @param Tasking.Task_A2G#TASK_A2G Task
|
||||||
function Fsm:onafterAssigned( TaskUnit, Task )
|
function Fsm:onafterAssigned( TaskUnit, Task )
|
||||||
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
||||||
-- Determine the first Unit from the self.RendezVousSetUnit
|
-- Determine the first Unit from the self.RendezVousSetUnit
|
||||||
|
|
||||||
self:RouteToRendezVous()
|
self:RouteToRendezVous()
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Test
|
--- Test
|
||||||
-- @param #FSM_PROCESS self
|
-- @param #FSM_PROCESS self
|
||||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||||
-- @param Tasking.Task_A2G#TASK_A2G Task
|
-- @param Tasking.Task_A2G#TASK_A2G Task
|
||||||
function Fsm:onafterRouteToRendezVous( TaskUnit, Task )
|
function Fsm:onafterRouteToRendezVous( TaskUnit, Task )
|
||||||
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
||||||
-- Determine the first Unit from the self.RendezVousSetUnit
|
-- Determine the first Unit from the self.RendezVousSetUnit
|
||||||
|
|
||||||
if Task:GetRendezVousZone( TaskUnit ) then
|
if Task:GetRendezVousZone( TaskUnit ) then
|
||||||
self:__RouteToRendezVousZone( 0.1 )
|
self:__RouteToRendezVousZone( 0.1 )
|
||||||
else
|
else
|
||||||
@@ -112,36 +110,36 @@ do -- TASK_A2G
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Test
|
--- Test
|
||||||
-- @param #FSM_PROCESS self
|
-- @param #FSM_PROCESS self
|
||||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||||
-- @param Tasking.Task#TASK_A2G Task
|
-- @param Tasking.Task#TASK_A2G Task
|
||||||
function Fsm:OnAfterArriveAtRendezVous( TaskUnit, Task )
|
function Fsm:OnAfterArriveAtRendezVous( TaskUnit, Task )
|
||||||
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
||||||
-- Determine the first Unit from the self.TargetSetUnit
|
-- Determine the first Unit from the self.TargetSetUnit
|
||||||
|
|
||||||
self:__Engage( 0.1 )
|
self:__Engage( 0.1 )
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Test
|
--- Test
|
||||||
-- @param #FSM_PROCESS self
|
-- @param #FSM_PROCESS self
|
||||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||||
-- @param Tasking.Task#TASK_A2G Task
|
-- @param Tasking.Task#TASK_A2G Task
|
||||||
function Fsm:onafterEngage( TaskUnit, Task )
|
function Fsm:onafterEngage( TaskUnit, Task )
|
||||||
self:F( { self } )
|
self:F( { self } )
|
||||||
self:__Account( 0.1 )
|
self:__Account( 0.1 )
|
||||||
self:__RouteToTarget(0.1 )
|
self:__RouteToTarget( 0.1 )
|
||||||
self:__RouteToTargets( -10 )
|
self:__RouteToTargets( -10 )
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Test
|
--- Test
|
||||||
-- @param #FSM_PROCESS self
|
-- @param #FSM_PROCESS self
|
||||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||||
-- @param Tasking.Task_A2G#TASK_A2G Task
|
-- @param Tasking.Task_A2G#TASK_A2G Task
|
||||||
function Fsm:onafterRouteToTarget( TaskUnit, Task )
|
function Fsm:onafterRouteToTarget( TaskUnit, Task )
|
||||||
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
||||||
-- Determine the first Unit from the self.TargetSetUnit
|
-- Determine the first Unit from the self.TargetSetUnit
|
||||||
|
|
||||||
if Task:GetTargetZone( TaskUnit ) then
|
if Task:GetTargetZone( TaskUnit ) then
|
||||||
self:__RouteToTargetZone( 0.1 )
|
self:__RouteToTargetZone( 0.1 )
|
||||||
else
|
else
|
||||||
@@ -154,8 +152,8 @@ do -- TASK_A2G
|
|||||||
self:__RouteToTargetPoint( 0.1 )
|
self:__RouteToTargetPoint( 0.1 )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Test
|
--- Test
|
||||||
-- @param #FSM_PROCESS self
|
-- @param #FSM_PROCESS self
|
||||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||||
-- @param Tasking.Task_A2G#TASK_A2G Task
|
-- @param Tasking.Task_A2G#TASK_A2G Task
|
||||||
@@ -167,20 +165,18 @@ do -- TASK_A2G
|
|||||||
end
|
end
|
||||||
self:__RouteToTargets( -10 )
|
self:__RouteToTargets( -10 )
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param #TASK_A2G self
|
--- @param #TASK_A2G self
|
||||||
-- @param Core.Set#SET_UNIT TargetSetUnit The set of targets.
|
-- @param Core.Set#SET_UNIT TargetSetUnit The set of targets.
|
||||||
function TASK_A2G:SetTargetSetUnit( TargetSetUnit )
|
function TASK_A2G:SetTargetSetUnit( TargetSetUnit )
|
||||||
|
|
||||||
self.TargetSetUnit = TargetSetUnit
|
self.TargetSetUnit = TargetSetUnit
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- @param #TASK_A2G self
|
--- @param #TASK_A2G self
|
||||||
function TASK_A2G:GetPlannedMenuText()
|
function TASK_A2G:GetPlannedMenuText()
|
||||||
return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )"
|
return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )"
|
||||||
@@ -190,34 +186,32 @@ do -- TASK_A2G
|
|||||||
-- @param Core.Point#COORDINATE RendezVousCoordinate The Coordinate object referencing to the 2D point where the RendezVous point is located on the map.
|
-- @param Core.Point#COORDINATE RendezVousCoordinate The Coordinate object referencing to the 2D point where the RendezVous point is located on the map.
|
||||||
-- @param #number RendezVousRange The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point.
|
-- @param #number RendezVousRange The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point.
|
||||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||||
function TASK_A2G:SetRendezVousCoordinate( RendezVousCoordinate, RendezVousRange, TaskUnit )
|
function TASK_A2G:SetRendezVousCoordinate( RendezVousCoordinate, RendezVousRange, TaskUnit )
|
||||||
|
|
||||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||||
|
|
||||||
local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT
|
local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT
|
||||||
ActRouteRendezVous:SetCoordinate( RendezVousCoordinate )
|
ActRouteRendezVous:SetCoordinate( RendezVousCoordinate )
|
||||||
ActRouteRendezVous:SetRange( RendezVousRange )
|
ActRouteRendezVous:SetRange( RendezVousRange )
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param #TASK_A2G self
|
--- @param #TASK_A2G self
|
||||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||||
-- @return Core.Point#COORDINATE The Coordinate object referencing to the 2D point where the RendezVous point is located on the map.
|
-- @return Core.Point#COORDINATE The Coordinate object referencing to the 2D point where the RendezVous point is located on the map.
|
||||||
-- @return #number The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point.
|
-- @return #number The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point.
|
||||||
function TASK_A2G:GetRendezVousCoordinate( TaskUnit )
|
function TASK_A2G:GetRendezVousCoordinate( TaskUnit )
|
||||||
|
|
||||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||||
|
|
||||||
local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT
|
local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT
|
||||||
return ActRouteRendezVous:GetCoordinate(), ActRouteRendezVous:GetRange()
|
return ActRouteRendezVous:GetCoordinate(), ActRouteRendezVous:GetRange()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- @param #TASK_A2G self
|
--- @param #TASK_A2G self
|
||||||
-- @param Core.Zone#ZONE_BASE RendezVousZone The Zone object where the RendezVous is located on the map.
|
-- @param Core.Zone#ZONE_BASE RendezVousZone The Zone object where the RendezVous is located on the map.
|
||||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||||
function TASK_A2G:SetRendezVousZone( RendezVousZone, TaskUnit )
|
function TASK_A2G:SetRendezVousZone( RendezVousZone, TaskUnit )
|
||||||
|
|
||||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||||
|
|
||||||
local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE
|
local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE
|
||||||
@@ -234,18 +228,17 @@ do -- TASK_A2G
|
|||||||
local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE
|
local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE
|
||||||
return ActRouteRendezVous:GetZone()
|
return ActRouteRendezVous:GetZone()
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param #TASK_A2G self
|
--- @param #TASK_A2G self
|
||||||
-- @param Core.Point#COORDINATE TargetCoordinate The Coordinate object where the Target is located on the map.
|
-- @param Core.Point#COORDINATE TargetCoordinate The Coordinate object where the Target is located on the map.
|
||||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||||
function TASK_A2G:SetTargetCoordinate( TargetCoordinate, TaskUnit )
|
function TASK_A2G:SetTargetCoordinate( TargetCoordinate, TaskUnit )
|
||||||
|
|
||||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||||
|
|
||||||
local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT
|
local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT
|
||||||
ActRouteTarget:SetCoordinate( TargetCoordinate )
|
ActRouteTarget:SetCoordinate( TargetCoordinate )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- @param #TASK_A2G self
|
--- @param #TASK_A2G self
|
||||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||||
@@ -258,18 +251,16 @@ do -- TASK_A2G
|
|||||||
return ActRouteTarget:GetCoordinate()
|
return ActRouteTarget:GetCoordinate()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- @param #TASK_A2G self
|
--- @param #TASK_A2G self
|
||||||
-- @param Core.Zone#ZONE_BASE TargetZone The Zone object where the Target is located on the map.
|
-- @param Core.Zone#ZONE_BASE TargetZone The Zone object where the Target is located on the map.
|
||||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||||
function TASK_A2G:SetTargetZone( TargetZone, TaskUnit )
|
function TASK_A2G:SetTargetZone( TargetZone, TaskUnit )
|
||||||
|
|
||||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||||
|
|
||||||
local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE
|
local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE
|
||||||
ActRouteTarget:SetZone( TargetZone )
|
ActRouteTarget:SetZone( TargetZone )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- @param #TASK_A2G self
|
--- @param #TASK_A2G self
|
||||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||||
@@ -283,47 +274,46 @@ do -- TASK_A2G
|
|||||||
end
|
end
|
||||||
|
|
||||||
function TASK_A2G:SetGoalTotal()
|
function TASK_A2G:SetGoalTotal()
|
||||||
|
|
||||||
self.GoalTotal = self.TargetSetUnit:Count()
|
self.GoalTotal = self.TargetSetUnit:Count()
|
||||||
end
|
end
|
||||||
|
|
||||||
function TASK_A2G:GetGoalTotal()
|
function TASK_A2G:GetGoalTotal()
|
||||||
|
|
||||||
return self.GoalTotal
|
return self.GoalTotal
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Return the relative distance to the target vicinity from the player, in order to sort the targets in the reports per distance from the threats.
|
--- Return the relative distance to the target vicinity from the player, in order to sort the targets in the reports per distance from the threats.
|
||||||
-- @param #TASK_A2G self
|
-- @param #TASK_A2G self
|
||||||
function TASK_A2G:ReportOrder( ReportGroup )
|
function TASK_A2G:ReportOrder( ReportGroup )
|
||||||
self:UpdateTaskInfo( self.DetectedItem )
|
self:UpdateTaskInfo( self.DetectedItem )
|
||||||
|
|
||||||
local Coordinate = self.TaskInfo:GetData( "Coordinate" )
|
local Coordinate = self.TaskInfo:GetData( "Coordinate" )
|
||||||
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
|
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
|
||||||
|
|
||||||
return Distance
|
return Distance
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- This method checks every 10 seconds if the goal has been reached of the task.
|
--- This method checks every 10 seconds if the goal has been reached of the task.
|
||||||
-- @param #TASK_A2G self
|
-- @param #TASK_A2G self
|
||||||
function TASK_A2G:onafterGoal( TaskUnit, From, Event, To )
|
function TASK_A2G:onafterGoal( TaskUnit, From, Event, To )
|
||||||
local TargetSetUnit = self.TargetSetUnit -- Core.Set#SET_UNIT
|
local TargetSetUnit = self.TargetSetUnit -- Core.Set#SET_UNIT
|
||||||
|
|
||||||
if TargetSetUnit:Count() == 0 then
|
if TargetSetUnit:Count() == 0 then
|
||||||
self:Success()
|
self:Success()
|
||||||
end
|
end
|
||||||
|
|
||||||
self:__Goal( -10 )
|
self:__Goal( -10 )
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param #TASK_A2G self
|
--- @param #TASK_A2G self
|
||||||
function TASK_A2G:UpdateTaskInfo( DetectedItem )
|
function TASK_A2G:UpdateTaskInfo( DetectedItem )
|
||||||
|
|
||||||
if self:IsStatePlanned() or self:IsStateAssigned() then
|
if self:IsStatePlanned() or self:IsStateAssigned() then
|
||||||
local TargetCoordinate = DetectedItem and self.Detection:GetDetectedItemCoordinate( DetectedItem ) or self.TargetSetUnit:GetFirst():GetCoordinate()
|
local TargetCoordinate = DetectedItem and self.Detection:GetDetectedItemCoordinate( DetectedItem ) or self.TargetSetUnit:GetFirst():GetCoordinate()
|
||||||
self.TaskInfo:AddTaskName( 0, "MSOD" )
|
self.TaskInfo:AddTaskName( 0, "MSOD" )
|
||||||
self.TaskInfo:AddCoordinate( TargetCoordinate, 1, "SOD" )
|
self.TaskInfo:AddCoordinate( TargetCoordinate, 1, "SOD" )
|
||||||
|
|
||||||
local ThreatLevel, ThreatText
|
local ThreatLevel, ThreatText
|
||||||
if DetectedItem then
|
if DetectedItem then
|
||||||
ThreatLevel, ThreatText = self.Detection:GetDetectedItemThreatLevel( DetectedItem )
|
ThreatLevel, ThreatText = self.Detection:GetDetectedItemThreatLevel( DetectedItem )
|
||||||
@@ -331,7 +321,7 @@ do -- TASK_A2G
|
|||||||
ThreatLevel, ThreatText = self.TargetSetUnit:CalculateThreatLevelA2G()
|
ThreatLevel, ThreatText = self.TargetSetUnit:CalculateThreatLevelA2G()
|
||||||
end
|
end
|
||||||
self.TaskInfo:AddThreat( ThreatText, ThreatLevel, 10, "MOD", true )
|
self.TaskInfo:AddThreat( ThreatText, ThreatLevel, 10, "MOD", true )
|
||||||
|
|
||||||
if self.Detection then
|
if self.Detection then
|
||||||
local DetectedItemsCount = self.TargetSetUnit:Count()
|
local DetectedItemsCount = self.TargetSetUnit:Count()
|
||||||
local ReportTypes = REPORT:New()
|
local ReportTypes = REPORT:New()
|
||||||
@@ -344,33 +334,33 @@ do -- TASK_A2G
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
self.TaskInfo:AddTargetCount( DetectedItemsCount, 11, "O", true )
|
self.TaskInfo:AddTargetCount( DetectedItemsCount, 11, "O", true )
|
||||||
self.TaskInfo:AddTargets( DetectedItemsCount, ReportTypes:Text( ", " ), 20, "D", true )
|
self.TaskInfo:AddTargets( DetectedItemsCount, ReportTypes:Text( ", " ), 20, "D", true )
|
||||||
else
|
else
|
||||||
local DetectedItemsCount = self.TargetSetUnit:Count()
|
local DetectedItemsCount = self.TargetSetUnit:Count()
|
||||||
local DetectedItemsTypes = self.TargetSetUnit:GetTypeNames()
|
local DetectedItemsTypes = self.TargetSetUnit:GetTypeNames()
|
||||||
self.TaskInfo:AddTargetCount( DetectedItemsCount, 11, "O", true )
|
self.TaskInfo:AddTargetCount( DetectedItemsCount, 11, "O", true )
|
||||||
self.TaskInfo:AddTargets( DetectedItemsCount, DetectedItemsTypes, 20, "D", true )
|
self.TaskInfo:AddTargets( DetectedItemsCount, DetectedItemsTypes, 20, "D", true )
|
||||||
end
|
end
|
||||||
self.TaskInfo:AddQFEAtCoordinate( TargetCoordinate, 30, "MOD" )
|
self.TaskInfo:AddQFEAtCoordinate( TargetCoordinate, 30, "MOD" )
|
||||||
self.TaskInfo:AddTemperatureAtCoordinate( TargetCoordinate, 31, "MD" )
|
self.TaskInfo:AddTemperatureAtCoordinate( TargetCoordinate, 31, "MD" )
|
||||||
self.TaskInfo:AddWindAtCoordinate( TargetCoordinate, 32, "MD" )
|
self.TaskInfo:AddWindAtCoordinate( TargetCoordinate, 32, "MD" )
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- This function is called from the @{Tasking.CommandCenter#COMMANDCENTER} to determine the method of automatic task selection.
|
--- This function is called from the @{Tasking.CommandCenter#COMMANDCENTER} to determine the method of automatic task selection.
|
||||||
-- @param #TASK_A2G self
|
-- @param #TASK_A2G self
|
||||||
-- @param #number AutoAssignMethod The method to be applied to the task.
|
-- @param #number AutoAssignMethod The method to be applied to the task.
|
||||||
-- @param Tasking.CommandCenter#COMMANDCENTER CommandCenter The command center.
|
-- @param Tasking.CommandCenter#COMMANDCENTER CommandCenter The command center.
|
||||||
-- @param Wrapper.Group#GROUP TaskGroup The player group.
|
-- @param Wrapper.Group#GROUP TaskGroup The player group.
|
||||||
function TASK_A2G:GetAutoAssignPriority( AutoAssignMethod, CommandCenter, TaskGroup )
|
function TASK_A2G:GetAutoAssignPriority( AutoAssignMethod, CommandCenter, TaskGroup )
|
||||||
|
|
||||||
if AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Random then
|
if AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Random then
|
||||||
return math.random( 1, 9 )
|
return math.random( 1, 9 )
|
||||||
elseif AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Distance then
|
elseif AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Distance then
|
||||||
local Coordinate = self.TaskInfo:GetData( "Coordinate" )
|
local Coordinate = self.TaskInfo:GetData( "Coordinate" )
|
||||||
local Distance = Coordinate:Get2DDistance( CommandCenter:GetPositionable():GetCoordinate() )
|
local Distance = Coordinate:Get2DDistance( CommandCenter:GetPositionable():GetCoordinate() )
|
||||||
self:F({Distance=Distance})
|
self:F( { Distance = Distance } )
|
||||||
return math.floor( Distance )
|
return math.floor( Distance )
|
||||||
elseif AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Priority then
|
elseif AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Priority then
|
||||||
return 1
|
return 1
|
||||||
@@ -379,8 +369,7 @@ do -- TASK_A2G
|
|||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
do -- TASK_A2G_SEAD
|
do -- TASK_A2G_SEAD
|
||||||
|
|
||||||
@@ -397,9 +386,9 @@ do -- TASK_A2G_SEAD
|
|||||||
--
|
--
|
||||||
-- @field #TASK_A2G_SEAD
|
-- @field #TASK_A2G_SEAD
|
||||||
TASK_A2G_SEAD = {
|
TASK_A2G_SEAD = {
|
||||||
ClassName = "TASK_A2G_SEAD",
|
ClassName = "TASK_A2G_SEAD"
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Instantiates a new TASK_A2G_SEAD.
|
--- Instantiates a new TASK_A2G_SEAD.
|
||||||
-- @param #TASK_A2G_SEAD self
|
-- @param #TASK_A2G_SEAD self
|
||||||
-- @param Tasking.Mission#MISSION Mission
|
-- @param Tasking.Mission#MISSION Mission
|
||||||
@@ -408,19 +397,16 @@ do -- TASK_A2G_SEAD
|
|||||||
-- @param Core.Set#SET_UNIT TargetSetUnit
|
-- @param Core.Set#SET_UNIT TargetSetUnit
|
||||||
-- @param #string TaskBriefing The briefing of the task.
|
-- @param #string TaskBriefing The briefing of the task.
|
||||||
-- @return #TASK_A2G_SEAD self
|
-- @return #TASK_A2G_SEAD self
|
||||||
function TASK_A2G_SEAD:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing)
|
function TASK_A2G_SEAD:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing )
|
||||||
local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "SEAD", TaskBriefing ) ) -- #TASK_A2G_SEAD
|
local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "SEAD", TaskBriefing ) ) -- #TASK_A2G_SEAD
|
||||||
self:F()
|
self:F()
|
||||||
|
|
||||||
Mission:AddTask( self )
|
Mission:AddTask( self )
|
||||||
|
|
||||||
self:SetBriefing(
|
self:SetBriefing( TaskBriefing or "Execute a Suppression of Enemy Air Defenses." )
|
||||||
TaskBriefing or
|
|
||||||
"Execute a Suppression of Enemy Air Defenses."
|
|
||||||
)
|
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set a score when a target in scope of the A2G attack, has been destroyed .
|
--- Set a score when a target in scope of the A2G attack, has been destroyed .
|
||||||
-- @param #TASK_A2G_SEAD self
|
-- @param #TASK_A2G_SEAD self
|
||||||
@@ -434,7 +420,7 @@ do -- TASK_A2G_SEAD
|
|||||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||||
|
|
||||||
ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has SEADed a target.", Score )
|
ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has SEADed a target.", Score )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -450,7 +436,7 @@ do -- TASK_A2G_SEAD
|
|||||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||||
|
|
||||||
ProcessUnit:AddScore( "Success", "All radar emitting targets have been successfully SEADed!", Score )
|
ProcessUnit:AddScore( "Success", "All radar emitting targets have been successfully SEADed!", Score )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -466,11 +452,10 @@ do -- TASK_A2G_SEAD
|
|||||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||||
|
|
||||||
ProcessUnit:AddScore( "Failed", "The SEADing has failed!", Penalty )
|
ProcessUnit:AddScore( "Failed", "The SEADing has failed!", Penalty )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
do -- TASK_A2G_BAI
|
do -- TASK_A2G_BAI
|
||||||
@@ -488,10 +473,8 @@ do -- TASK_A2G_BAI
|
|||||||
-- based on detected enemy ground targets.
|
-- based on detected enemy ground targets.
|
||||||
--
|
--
|
||||||
-- @field #TASK_A2G_BAI
|
-- @field #TASK_A2G_BAI
|
||||||
TASK_A2G_BAI = {
|
TASK_A2G_BAI = { ClassName = "TASK_A2G_BAI" }
|
||||||
ClassName = "TASK_A2G_BAI",
|
|
||||||
}
|
|
||||||
|
|
||||||
--- Instantiates a new TASK_A2G_BAI.
|
--- Instantiates a new TASK_A2G_BAI.
|
||||||
-- @param #TASK_A2G_BAI self
|
-- @param #TASK_A2G_BAI self
|
||||||
-- @param Tasking.Mission#MISSION Mission
|
-- @param Tasking.Mission#MISSION Mission
|
||||||
@@ -503,14 +486,11 @@ do -- TASK_A2G_BAI
|
|||||||
function TASK_A2G_BAI:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing )
|
function TASK_A2G_BAI:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing )
|
||||||
local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "BAI", TaskBriefing ) ) -- #TASK_A2G_BAI
|
local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "BAI", TaskBriefing ) ) -- #TASK_A2G_BAI
|
||||||
self:F()
|
self:F()
|
||||||
|
|
||||||
Mission:AddTask( self )
|
Mission:AddTask( self )
|
||||||
|
|
||||||
self:SetBriefing(
|
self:SetBriefing( TaskBriefing or "Execute a Battlefield Air Interdiction of a group of enemy targets." )
|
||||||
TaskBriefing or
|
|
||||||
"Execute a Battlefield Air Interdiction of a group of enemy targets."
|
|
||||||
)
|
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -526,7 +506,7 @@ do -- TASK_A2G_BAI
|
|||||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||||
|
|
||||||
ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has destroyed a target in Battlefield Air Interdiction (BAI).", Score )
|
ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has destroyed a target in Battlefield Air Interdiction (BAI).", Score )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -542,7 +522,7 @@ do -- TASK_A2G_BAI
|
|||||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||||
|
|
||||||
ProcessUnit:AddScore( "Success", "All targets have been successfully destroyed! The Battlefield Air Interdiction (BAI) is a success!", Score )
|
ProcessUnit:AddScore( "Success", "All targets have been successfully destroyed! The Battlefield Air Interdiction (BAI) is a success!", Score )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -558,15 +538,12 @@ do -- TASK_A2G_BAI
|
|||||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||||
|
|
||||||
ProcessUnit:AddScore( "Failed", "The Battlefield Air Interdiction (BAI) has failed!", Penalty )
|
ProcessUnit:AddScore( "Failed", "The Battlefield Air Interdiction (BAI) has failed!", Penalty )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
do -- TASK_A2G_CAS
|
do -- TASK_A2G_CAS
|
||||||
|
|
||||||
--- The TASK_A2G_CAS class
|
--- The TASK_A2G_CAS class
|
||||||
@@ -581,10 +558,8 @@ do -- TASK_A2G_CAS
|
|||||||
-- based on detected enemy ground targets.
|
-- based on detected enemy ground targets.
|
||||||
--
|
--
|
||||||
-- @field #TASK_A2G_CAS
|
-- @field #TASK_A2G_CAS
|
||||||
TASK_A2G_CAS = {
|
TASK_A2G_CAS = { ClassName = "TASK_A2G_CAS" }
|
||||||
ClassName = "TASK_A2G_CAS",
|
|
||||||
}
|
|
||||||
|
|
||||||
--- Instantiates a new TASK_A2G_CAS.
|
--- Instantiates a new TASK_A2G_CAS.
|
||||||
-- @param #TASK_A2G_CAS self
|
-- @param #TASK_A2G_CAS self
|
||||||
-- @param Tasking.Mission#MISSION Mission
|
-- @param Tasking.Mission#MISSION Mission
|
||||||
@@ -596,19 +571,13 @@ do -- TASK_A2G_CAS
|
|||||||
function TASK_A2G_CAS:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing )
|
function TASK_A2G_CAS:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing )
|
||||||
local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "CAS", TaskBriefing ) ) -- #TASK_A2G_CAS
|
local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "CAS", TaskBriefing ) ) -- #TASK_A2G_CAS
|
||||||
self:F()
|
self:F()
|
||||||
|
|
||||||
Mission:AddTask( self )
|
|
||||||
|
|
||||||
self:SetBriefing(
|
|
||||||
TaskBriefing or
|
|
||||||
"Execute a Close Air Support for a group of enemy targets. " ..
|
|
||||||
"Beware of friendlies at the vicinity! "
|
|
||||||
)
|
|
||||||
|
|
||||||
|
Mission:AddTask( self )
|
||||||
|
|
||||||
|
self:SetBriefing( TaskBriefing or ( "Execute a Close Air Support for a group of enemy targets. " .. "Beware of friendlies at the vicinity! " ) )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Set a score when a target in scope of the A2G attack, has been destroyed .
|
--- Set a score when a target in scope of the A2G attack, has been destroyed .
|
||||||
-- @param #TASK_A2G_CAS self
|
-- @param #TASK_A2G_CAS self
|
||||||
@@ -622,7 +591,7 @@ do -- TASK_A2G_CAS
|
|||||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||||
|
|
||||||
ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has destroyed a target in Close Air Support (CAS).", Score )
|
ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has destroyed a target in Close Air Support (CAS).", Score )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -638,7 +607,7 @@ do -- TASK_A2G_CAS
|
|||||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||||
|
|
||||||
ProcessUnit:AddScore( "Success", "All targets have been successfully destroyed! The Close Air Support (CAS) was a success!", Score )
|
ProcessUnit:AddScore( "Success", "All targets have been successfully destroyed! The Close Air Support (CAS) was a success!", Score )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -654,9 +623,8 @@ do -- TASK_A2G_CAS
|
|||||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||||
|
|
||||||
ProcessUnit:AddScore( "Failed", "The Close Air Support (CAS) has failed!", Penalty )
|
ProcessUnit:AddScore( "Failed", "The Close Air Support (CAS) has failed!", Penalty )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,24 +1,25 @@
|
|||||||
--- **Tasking** -- Dynamically allocates A2G tasks to human players, based on detected ground targets through reconnaissance.
|
--- **Tasking** -- Dynamically allocates A2G tasks to human players, based on detected ground targets through reconnaissance.
|
||||||
--
|
--
|
||||||
-- **Features:**
|
-- **Features:**
|
||||||
--
|
--
|
||||||
-- * Dynamically assign tasks to human players based on detected targets.
|
-- * Dynamically assign tasks to human players based on detected targets.
|
||||||
-- * Dynamically change the tasks as the tactical situation evolves during the mission.
|
-- * Dynamically change the tasks as the tactical situation evolves during the mission.
|
||||||
-- * Dynamically assign (CAS) Close Air Support tasks for human players.
|
-- * Dynamically assign (CAS) Close Air Support tasks for human players.
|
||||||
-- * Dynamically assign (BAI) Battlefield Air Interdiction tasks for human players.
|
-- * Dynamically assign (BAI) Battlefield Air Interdiction tasks for human players.
|
||||||
-- * Dynamically assign (SEAD) Supression of Enemy Air Defense tasks for human players to eliminate G2A missile threats.
|
-- * Dynamically assign (SEAD) Suppression of Enemy Air Defense tasks for human players to eliminate G2A missile threats.
|
||||||
-- * Define and use an EWR (Early Warning Radar) network.
|
-- * Define and use an EWR (Early Warning Radar) network.
|
||||||
-- * Define different ranges to engage upon intruders.
|
-- * Define different ranges to engage upon intruders.
|
||||||
-- * Keep task achievements.
|
-- * Keep task achievements.
|
||||||
-- * Score task achievements.--
|
-- * Score task achievements.
|
||||||
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ### Author: **FlightControl**
|
-- ### Author: **FlightControl**
|
||||||
--
|
--
|
||||||
-- ### Contributions:
|
-- ### Contributions:
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- @module Tasking.Task_A2G_Dispatcher
|
-- @module Tasking.Task_A2G_Dispatcher
|
||||||
-- @image Task_A2G_Dispatcher.JPG
|
-- @image Task_A2G_Dispatcher.JPG
|
||||||
|
|
||||||
@@ -32,151 +33,151 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
-- @extends Tasking.DetectionManager#DETECTION_MANAGER
|
-- @extends Tasking.DetectionManager#DETECTION_MANAGER
|
||||||
|
|
||||||
--- Orchestrates dynamic **A2G Task Dispatching** based on the detection results of a linked @{Detection} object.
|
--- Orchestrates dynamic **A2G Task Dispatching** based on the detection results of a linked @{Detection} object.
|
||||||
--
|
--
|
||||||
-- It uses the Tasking System within the MOOSE framework, which is a multi-player Tasking Orchestration system.
|
-- It uses the Tasking System within the MOOSE framework, which is a multi-player Tasking Orchestration system.
|
||||||
-- It provides a truly dynamic battle environment for pilots and ground commanders to engage upon,
|
-- It provides a truly dynamic battle environment for pilots and ground commanders to engage upon,
|
||||||
-- in a true co-operation environment wherein **Multiple Teams** will collaborate in Missions to **achieve a common Mission Goal**.
|
-- in a true co-operation environment wherein **Multiple Teams** will collaborate in Missions to **achieve a common Mission Goal**.
|
||||||
--
|
--
|
||||||
-- The A2G dispatcher will dispatch the A2G Tasks to a defined @{Set} of @{Wrapper.Group}s that will be manned by **Players**.
|
-- The A2G dispatcher will dispatch the A2G Tasks to a defined @{Set} of @{Wrapper.Group}s that will be manned by **Players**.
|
||||||
-- We call this the **AttackSet** of the A2G dispatcher. So, the Players are seated in the @{Client}s of the @{Wrapper.Group} @{Set}.
|
-- We call this the **AttackSet** of the A2G dispatcher. So, the Players are seated in the @{Client}s of the @{Wrapper.Group} @{Set}.
|
||||||
--
|
--
|
||||||
-- Depending on the actions of the enemy, preventive tasks are dispatched to the players to orchestrate the engagement in a true co-operation.
|
-- Depending on the actions of the enemy, preventive tasks are dispatched to the players to orchestrate the engagement in a true co-operation.
|
||||||
-- The detection object will group the detected targets by its grouping method, and integrates a @{Set} of @{Wrapper.Group}s that are Recce vehicles or air units.
|
-- The detection object will group the detected targets by its grouping method, and integrates a @{Set} of @{Wrapper.Group}s that are Recce vehicles or air units.
|
||||||
-- We call this the **RecceSet** of the A2G dispatcher.
|
-- We call this the **RecceSet** of the A2G dispatcher.
|
||||||
--
|
--
|
||||||
-- Depending on the current detected tactical situation, different task types will be dispatched to the Players seated in the AttackSet..
|
-- Depending on the current detected tactical situation, different task types will be dispatched to the Players seated in the AttackSet..
|
||||||
-- There are currently 3 **Task Types** implemented in the TASK\_A2G\_DISPATCHER:
|
-- There are currently 3 **Task Types** implemented in the TASK\_A2G\_DISPATCHER:
|
||||||
--
|
--
|
||||||
-- - **SEAD Task**: Dispatched when there are ground based Radar Emitters detected within an area.
|
-- - **SEAD Task**: Dispatched when there are ground based Radar Emitters detected within an area.
|
||||||
-- - **CAS Task**: Dispatched when there are no ground based Radar Emitters within the area, but there are friendly ground Units within 6 km from the enemy.
|
-- - **CAS Task**: Dispatched when there are no ground based Radar Emitters within the area, but there are friendly ground Units within 6 km from the enemy.
|
||||||
-- - **BAI Task**: Dispatched when there are no ground based Radar Emitters within the area, and there aren't friendly ground Units within 6 km from the enemy.
|
-- - **BAI Task**: Dispatched when there are no ground based Radar Emitters within the area, and there aren't friendly ground Units within 6 km from the enemy.
|
||||||
--
|
--
|
||||||
-- # 0. Tactical Situations
|
-- # 0. Tactical Situations
|
||||||
--
|
--
|
||||||
-- This chapters provides some insights in the tactical situations when certain Task Types are created.
|
-- This chapters provides some insights in the tactical situations when certain Task Types are created.
|
||||||
-- The Task Types are depending on the enemy positions that were detected, and the current location of friendly units.
|
-- The Task Types are depending on the enemy positions that were detected, and the current location of friendly units.
|
||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
-- In the demonstration mission [TAD-A2G-000 - AREAS - Detection test],
|
-- In the demonstration mission [TAD-A2G-000 - AREAS - Detection test],
|
||||||
-- the tactical situation is a demonstration how the A2G detection works.
|
-- the tactical situation is a demonstration how the A2G detection works.
|
||||||
-- This example will be taken further in the explanation in the following chapters.
|
-- This example will be taken further in the explanation in the following chapters.
|
||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
-- The red coalition are the players, the blue coalition is the enemy.
|
-- The red coalition are the players, the blue coalition is the enemy.
|
||||||
--
|
--
|
||||||
-- Red reconnaissance vehicles and airborne units are detecting the targets.
|
-- Red reconnaissance vehicles and airborne units are detecting the targets.
|
||||||
-- We call this the RecceSet as explained above, which is a Set of Groups that
|
-- We call this the RecceSet as explained above, which is a Set of Groups that
|
||||||
-- have a group name starting with `Recce` (configured in the mission script).
|
-- have a group name starting with `Recce` (configured in the mission script).
|
||||||
--
|
--
|
||||||
-- Red attack units are responsible for executing the mission for the command center.
|
-- Red attack units are responsible for executing the mission for the command center.
|
||||||
-- We call this the AttackSet, which is a Set of Groups with a group name starting with `Attack` (configured in the mission script).
|
-- We call this the AttackSet, which is a Set of Groups with a group name starting with `Attack` (configured in the mission script).
|
||||||
-- These units are setup in this demonstration mission to be ground vehicles and airplanes.
|
-- These units are setup in this demonstration mission to be ground vehicles and airplanes.
|
||||||
-- For demonstration purposes, the attack airplane is stationed on the ground to explain
|
-- For demonstration purposes, the attack airplane is stationed on the ground to explain
|
||||||
-- the messages and the menus properly.
|
-- the messages and the menus properly.
|
||||||
-- Further test missions demonstrate the A2G task dispatcher from within air.
|
-- Further test missions demonstrate the A2G task dispatcher from within air.
|
||||||
--
|
--
|
||||||
-- Depending upon the detection results, the A2G dispatcher will create different tasks.
|
-- Depending upon the detection results, the A2G dispatcher will create different tasks.
|
||||||
--
|
--
|
||||||
-- # 0.1. SEAD Task
|
-- # 0.1. SEAD Task
|
||||||
--
|
--
|
||||||
-- A SEAD Task is dispatched when there are ground based Radar Emitters detected within an area.
|
-- A SEAD Task is dispatched when there are ground based Radar Emitters detected within an area.
|
||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
-- - Once all Radar Emitting Units have been destroyed, the Task will convert into a BAI or CAS task!
|
-- - Once all Radar Emitting Units have been destroyed, the Task will convert into a BAI or CAS task!
|
||||||
-- - A CAS and BAI task may be converted into a SEAD task, once a radar has been detected within the area!
|
-- - A CAS and BAI task may be converted into a SEAD task, once a radar has been detected within the area!
|
||||||
--
|
--
|
||||||
-- # 0.2. CAS Task
|
-- # 0.2. CAS Task
|
||||||
--
|
--
|
||||||
-- A CAS Task is dispatched when there are no ground based Radar Emitters within the area, but there are friendly ground Units within 6 km from the enemy.
|
-- A CAS Task is dispatched when there are no ground based Radar Emitters within the area, but there are friendly ground Units within 6 km from the enemy.
|
||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
-- - After the detection of the CAS task, if the friendly Units are destroyed, the CAS task will convert into a BAI task!
|
-- - After the detection of the CAS task, if the friendly Units are destroyed, the CAS task will convert into a BAI task!
|
||||||
-- - Only ground Units are taken into account. Airborne units are ships are not considered friendlies that require Close Air Support.
|
-- - Only ground Units are taken into account. Airborne units are ships are not considered friendlies that require Close Air Support.
|
||||||
--
|
--
|
||||||
-- # 0.3. BAI Task
|
-- # 0.3. BAI Task
|
||||||
--
|
--
|
||||||
-- A BAI Task is dispatched when there are no ground based Radar Emitters within the area, and there aren't friendly ground Units within 6 km from the enemy.
|
-- A BAI Task is dispatched when there are no ground based Radar Emitters within the area, and there aren't friendly ground Units within 6 km from the enemy.
|
||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
-- - A BAI task may be converted into a CAS task if friendly Ground Units approach within 6 km range!
|
-- - A BAI task may be converted into a CAS task if friendly Ground Units approach within 6 km range!
|
||||||
--
|
--
|
||||||
-- # 1. Player Experience
|
-- # 1. Player Experience
|
||||||
--
|
--
|
||||||
-- The A2G dispatcher is residing under a @{CommandCenter}, which is orchestrating a @{Mission}.
|
-- The A2G dispatcher is residing under a @{CommandCenter}, which is orchestrating a @{Mission}.
|
||||||
-- As a result, you'll find for DCS World missions that implement the A2G dispatcher a **Command Center Menu** and under this one or more **Mission Menus**.
|
-- As a result, you'll find for DCS World missions that implement the A2G dispatcher a **Command Center Menu** and under this one or more **Mission Menus**.
|
||||||
--
|
--
|
||||||
-- For example, if there are 2 Command Centers (CC).
|
-- For example, if there are 2 Command Centers (CC).
|
||||||
-- Each CC is controlling a couple of Missions, the Radio Menu Structure could look like this:
|
-- Each CC is controlling a couple of Missions, the Radio Menu Structure could look like this:
|
||||||
--
|
--
|
||||||
-- Radio MENU Structure (F10. Other)
|
-- Radio MENU Structure (F10. Other)
|
||||||
--
|
--
|
||||||
-- F1. Command Center [Gori]
|
-- F1. Command Center [Gori]
|
||||||
-- F1. Mission "Alpha (Primary)"
|
-- F1. Mission "Alpha (Primary)"
|
||||||
-- F2. Mission "Beta (Secondary)"
|
-- F2. Mission "Beta (Secondary)"
|
||||||
-- F3. Mission "Gamma (Tactical)"
|
-- F3. Mission "Gamma (Tactical)"
|
||||||
-- F1. Command Center [Lima]
|
-- F1. Command Center [Lima]
|
||||||
-- F1. Mission "Overlord (High)"
|
-- F1. Mission "Overlord (High)"
|
||||||
--
|
--
|
||||||
-- Command Center [Gori] is controlling Mission "Alpha", "Beta", "Gamma". Alpha is the Primary mission, Beta the Secondary and there is a Tacical mission Gamma.
|
-- Command Center [Gori] is controlling Mission "Alpha", "Beta", "Gamma". Alpha is the Primary mission, Beta the Secondary and there is a Tactical mission Gamma.
|
||||||
-- Command Center [Lima] is controlling Missions "Overlord", which needs to be executed with High priority.
|
-- Command Center [Lima] is controlling Missions "Overlord", which needs to be executed with High priority.
|
||||||
--
|
--
|
||||||
-- ## 1.1. Mission Menu (Under the Command Center Menu)
|
-- ## 1.1. Mission Menu (Under the Command Center Menu)
|
||||||
--
|
--
|
||||||
-- The Mission Menu controls the information of the mission, including the:
|
-- The Mission Menu controls the information of the mission, including the:
|
||||||
--
|
--
|
||||||
-- - **Mission Briefing**: A briefing of the Mission in text, which will be shown as a message.
|
-- - **Mission Briefing**: A briefing of the Mission in text, which will be shown as a message.
|
||||||
-- - **Mark Task Locations**: A summary of each Task will be shown on the map as a marker.
|
-- - **Mark Task Locations**: A summary of each Task will be shown on the map as a marker.
|
||||||
-- - **Create Task Reports**: A menu to create various reports of the current tasks dispatched by the A2G dispatcher.
|
-- - **Create Task Reports**: A menu to create various reports of the current tasks dispatched by the A2G dispatcher.
|
||||||
-- - **Create Mission Reports**: A menu to create various reports on the current mission.
|
-- - **Create Mission Reports**: A menu to create various reports on the current mission.
|
||||||
--
|
--
|
||||||
-- For CC [Lima], Mission "Overlord", the menu structure could look like this:
|
-- For CC [Lima], Mission "Overlord", the menu structure could look like this:
|
||||||
--
|
--
|
||||||
-- Radio MENU Structure (F10. Other)
|
-- Radio MENU Structure (F10. Other)
|
||||||
--
|
--
|
||||||
-- F1. Command Center [Lima]
|
-- F1. Command Center [Lima]
|
||||||
-- F1. Mission "Overlord"
|
-- F1. Mission "Overlord"
|
||||||
-- F1. Mission Briefing
|
-- F1. Mission Briefing
|
||||||
-- F2. Mark Task Locations on Map
|
-- F2. Mark Task Locations on Map
|
||||||
-- F3. Task Reports
|
-- F3. Task Reports
|
||||||
-- F4. Mission Reports
|
-- F4. Mission Reports
|
||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
-- ### 1.1.1. Mission Briefing Menu
|
-- ### 1.1.1. Mission Briefing Menu
|
||||||
--
|
--
|
||||||
-- The Mission Briefing Menu will show in text a summary description of the overall mission objectives and expectations.
|
-- The Mission Briefing Menu will show in text a summary description of the overall mission objectives and expectations.
|
||||||
-- Note that the Mission Briefing is not the briefing of a specific task, but rather provides an overall strategy and tactical situation,
|
-- Note that the Mission Briefing is not the briefing of a specific task, but rather provides an overall strategy and tactical situation,
|
||||||
-- and explains the mission goals.
|
-- and explains the mission goals.
|
||||||
--
|
--
|
||||||
--
|
--
|
||||||
-- ### 1.1.2. Mark Task Locations Menu
|
-- ### 1.1.2. Mark Task Locations Menu
|
||||||
--
|
--
|
||||||
-- The Mark Task Locations Menu will mark the location indications of the Tasks on the map, if this intelligence is known by the Command Center.
|
-- The Mark Task Locations Menu will mark the location indications of the Tasks on the map, if this intelligence is known by the Command Center.
|
||||||
-- For A2G tasks this information will always be know, but it can be that for other tasks a location intelligence will be less relevant.
|
-- For A2G tasks this information will always be know, but it can be that for other tasks a location intelligence will be less relevant.
|
||||||
-- Note that each Planned task and each Engaged task will be marked. Completed, Failed and Cancelled tasks are not marked.
|
-- Note that each Planned task and each Engaged task will be marked. Completed, Failed and Cancelled tasks are not marked.
|
||||||
-- Depending on the task type, a summary information is shown to bring to the player the relevant information for situational awareness.
|
-- Depending on the task type, a summary information is shown to bring to the player the relevant information for situational awareness.
|
||||||
--
|
--
|
||||||
-- ### 1.1.3. Task Reports Menu
|
-- ### 1.1.3. Task Reports Menu
|
||||||
--
|
--
|
||||||
-- The Task Reports Menu is a sub menu, that allows to create various reports:
|
-- The Task Reports Menu is a sub menu, that allows to create various reports:
|
||||||
--
|
--
|
||||||
-- - **Tasks Summary**: This report will list all the Tasks that are or were active within the mission, indicating its status.
|
-- - **Tasks Summary**: This report will list all the Tasks that are or were active within the mission, indicating its status.
|
||||||
-- - **Planned Tasks**: This report will list all the Tasks that are in status Planned, which are Tasks not assigned to any player, and are ready to be executed.
|
-- - **Planned Tasks**: This report will list all the Tasks that are in status Planned, which are Tasks not assigned to any player, and are ready to be executed.
|
||||||
-- - **Assigned Tasks**: This report will list all the Tasks that are in status Assigned, which are Tasks assigned to (a) player(s) and are currently executed.
|
-- - **Assigned Tasks**: This report will list all the Tasks that are in status Assigned, which are Tasks assigned to (a) player(s) and are currently executed.
|
||||||
-- - **Successful Tasks**: This report will list all the Tasks that are in status Success, which are Tasks executed by (a) player(s) and are completed successfully.
|
-- - **Successful Tasks**: This report will list all the Tasks that are in status Success, which are Tasks executed by (a) player(s) and are completed successfully.
|
||||||
-- - **Failed Tasks**: This report will list all the Tasks that are in status Success, which are Tasks executed by (a) player(s) and that have failed.
|
-- - **Failed Tasks**: This report will list all the Tasks that are in status Success, which are Tasks executed by (a) player(s) and that have failed.
|
||||||
--
|
--
|
||||||
-- The information shown of the tasks will vary according the underlying task type, but are self explanatory.
|
-- The information shown of the tasks will vary according the underlying task type, but are self explanatory.
|
||||||
--
|
--
|
||||||
-- For CC [Gori], Mission "Alpha", the Task Reports menu structure could look like this:
|
-- For CC [Gori], Mission "Alpha", the Task Reports menu structure could look like this:
|
||||||
--
|
--
|
||||||
-- Radio MENU Structure (F10. Other)
|
-- Radio MENU Structure (F10. Other)
|
||||||
--
|
--
|
||||||
-- F1. Command Center [Gori]
|
-- F1. Command Center [Gori]
|
||||||
-- F1. Mission "Alpha"
|
-- F1. Mission "Alpha"
|
||||||
-- F1. Mission Briefing
|
-- F1. Mission Briefing
|
||||||
@@ -188,21 +189,21 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
-- F4. Successful Tasks
|
-- F4. Successful Tasks
|
||||||
-- F5. Failed Tasks
|
-- F5. Failed Tasks
|
||||||
-- F4. Mission Reports
|
-- F4. Mission Reports
|
||||||
--
|
--
|
||||||
-- Note that these reports provide an "overview" of the tasks. Detailed information of the task can be retrieved using the Detailed Report on the Task Menu.
|
-- Note that these reports provide an "overview" of the tasks. Detailed information of the task can be retrieved using the Detailed Report on the Task Menu.
|
||||||
-- (See later).
|
-- (See later).
|
||||||
--
|
--
|
||||||
-- ### 1.1.4. Mission Reports Menu
|
-- ### 1.1.4. Mission Reports Menu
|
||||||
--
|
--
|
||||||
-- The Mission Reports Menu is a sub menu, that provides options to retrieve further information on the current Mission:
|
-- The Mission Reports Menu is a sub menu, that provides options to retrieve further information on the current Mission:
|
||||||
--
|
--
|
||||||
-- - **Report Mission Progress**: Shows the progress of the current Mission. Each Task has a %-tage of completion.
|
-- - **Report Mission Progress**: Shows the progress of the current Mission. Each Task has a % of completion.
|
||||||
-- - **Report Players per Task**: Show which players are engaged on which Task within the Mission.
|
-- - **Report Players per Task**: Show which players are engaged on which Task within the Mission.
|
||||||
--
|
--
|
||||||
-- For CC |Gori|, Mission "Alpha", the Mission Reports menu structure could look like this:
|
-- For CC |Gori|, Mission "Alpha", the Mission Reports menu structure could look like this:
|
||||||
--
|
--
|
||||||
-- Radio MENU Structure (F10. Other)
|
-- Radio MENU Structure (F10. Other)
|
||||||
--
|
--
|
||||||
-- F1. Command Center [Gori]
|
-- F1. Command Center [Gori]
|
||||||
-- F1. Mission "Alpha"
|
-- F1. Mission "Alpha"
|
||||||
-- F1. Mission Briefing
|
-- F1. Mission Briefing
|
||||||
@@ -211,25 +212,25 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
-- F4. Mission Reports
|
-- F4. Mission Reports
|
||||||
-- F1. Report Mission Progress
|
-- F1. Report Mission Progress
|
||||||
-- F2. Report Players per Task
|
-- F2. Report Players per Task
|
||||||
--
|
--
|
||||||
--
|
--
|
||||||
-- ## 1.2. Task Management Menus
|
-- ## 1.2. Task Management Menus
|
||||||
--
|
--
|
||||||
-- Very important to remember is: **Multiple Players can be assigned to the same Task, but from the player perspective, the Player can only be assigned to one Task per Mission at the same time!**
|
-- Very important to remember is: **Multiple Players can be assigned to the same Task, but from the player perspective, the Player can only be assigned to one Task per Mission at the same time!**
|
||||||
-- Consider this like the two major modes in which a player can be in. He can be free of tasks or he can be assigned to a Task.
|
-- Consider this like the two major modes in which a player can be in. He can be free of tasks or he can be assigned to a Task.
|
||||||
-- Depending on whether a Task has been Planned or Assigned to a Player (Group),
|
-- Depending on whether a Task has been Planned or Assigned to a Player (Group),
|
||||||
-- **the Mission Menu will contain extra Menus to control specific Tasks.**
|
-- **the Mission Menu will contain extra Menus to control specific Tasks.**
|
||||||
--
|
--
|
||||||
-- #### 1.2.1. Join a Planned Task
|
-- #### 1.2.1. Join a Planned Task
|
||||||
--
|
--
|
||||||
-- If the Player has not yet been assigned to a Task within the Mission, the Mission Menu will contain additionally a:
|
-- If the Player has not yet been assigned to a Task within the Mission, the Mission Menu will contain additionally a:
|
||||||
--
|
--
|
||||||
-- - Join Planned Task Menu: This menu structure allows the player to join a planned task (a Task with status Planned).
|
-- - Join Planned Task Menu: This menu structure allows the player to join a planned task (a Task with status Planned).
|
||||||
--
|
--
|
||||||
-- For CC |Gori|, Mission "Alpha", the menu structure could look like this:
|
-- For CC |Gori|, Mission "Alpha", the menu structure could look like this:
|
||||||
--
|
--
|
||||||
-- Radio MENU Structure (F10. Other)
|
-- Radio MENU Structure (F10. Other)
|
||||||
--
|
--
|
||||||
-- F1. Command Center [Gori]
|
-- F1. Command Center [Gori]
|
||||||
-- F1. Mission "Alpha"
|
-- F1. Mission "Alpha"
|
||||||
-- F1. Mission Briefing
|
-- F1. Mission Briefing
|
||||||
@@ -237,23 +238,23 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
-- F3. Task Reports
|
-- F3. Task Reports
|
||||||
-- F4. Mission Reports
|
-- F4. Mission Reports
|
||||||
-- F5. Join Planned Task
|
-- F5. Join Planned Task
|
||||||
--
|
--
|
||||||
-- **The F5. Join Planned Task allows the player to join a Planned Task and take an engagement in the running Mission.**
|
-- **The F5. Join Planned Task allows the player to join a Planned Task and take an engagement in the running Mission.**
|
||||||
--
|
--
|
||||||
-- #### 1.2.2. Manage an Assigned Task
|
-- #### 1.2.2. Manage an Assigned Task
|
||||||
--
|
--
|
||||||
-- If the Player has been assigned to one Task within the Mission, the Mission Menu will contain an extra:
|
-- If the Player has been assigned to one Task within the Mission, the Mission Menu will contain an extra:
|
||||||
--
|
--
|
||||||
-- - Assigned Task __TaskName__ Menu: This menu structure allows the player to take actions on the currently engaged task.
|
-- - Assigned Task __TaskName__ Menu: This menu structure allows the player to take actions on the currently engaged task.
|
||||||
--
|
--
|
||||||
-- In this example, the Group currently seated by the player is not assigned yet to a Task.
|
-- In this example, the Group currently seated by the player is not assigned yet to a Task.
|
||||||
-- The Player has the option to assign itself to a Planned Task using menu option F5 under the Mission Menu "Alpha".
|
-- The Player has the option to assign itself to a Planned Task using menu option F5 under the Mission Menu "Alpha".
|
||||||
--
|
--
|
||||||
-- This would be an example menu structure,
|
-- This would be an example menu structure,
|
||||||
-- for CC |Gori|, Mission "Alpha", when a player would have joined Task CAS.001:
|
-- for CC |Gori|, Mission "Alpha", when a player would have joined Task CAS.001:
|
||||||
--
|
--
|
||||||
-- Radio MENU Structure (F10. Other)
|
-- Radio MENU Structure (F10. Other)
|
||||||
--
|
--
|
||||||
-- F1. Command Center [Gori]
|
-- F1. Command Center [Gori]
|
||||||
-- F1. Mission "Alpha"
|
-- F1. Mission "Alpha"
|
||||||
-- F1. Mission Briefing
|
-- F1. Mission Briefing
|
||||||
@@ -261,26 +262,25 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
-- F3. Task Reports
|
-- F3. Task Reports
|
||||||
-- F4. Mission Reports
|
-- F4. Mission Reports
|
||||||
-- F5. Assigned Task CAS.001
|
-- F5. Assigned Task CAS.001
|
||||||
--
|
--
|
||||||
-- **The F5. Assigned Task __TaskName__ allows the player to control the current Assigned Task and take further actions.**
|
-- **The F5. Assigned Task __TaskName__ allows the player to control the current Assigned Task and take further actions.**
|
||||||
--
|
--
|
||||||
--
|
|
||||||
-- ## 1.3. Join Planned Task Menu
|
-- ## 1.3. Join Planned Task Menu
|
||||||
--
|
--
|
||||||
-- The Join Planned Task Menu contains the different Planned A2G Tasks **in a structured Menu Hierarchy**.
|
-- The Join Planned Task Menu contains the different Planned A2G Tasks **in a structured Menu Hierarchy**.
|
||||||
-- The Menu Hierarchy is structuring the Tasks per **Task Type**, and then by **Task Name (ID)**.
|
-- The Menu Hierarchy is structuring the Tasks per **Task Type**, and then by **Task Name (ID)**.
|
||||||
--
|
--
|
||||||
-- For example, for CC [Gori], Mission "Alpha",
|
-- For example, for CC [Gori], Mission "Alpha",
|
||||||
-- if a Mission "ALpha" contains 5 Planned Tasks, which would be:
|
-- if a Mission "ALpha" contains 5 Planned Tasks, which would be:
|
||||||
--
|
--
|
||||||
-- - 2 CAS Tasks
|
-- - 2 CAS Tasks
|
||||||
-- - 1 BAI Task
|
-- - 1 BAI Task
|
||||||
-- - 2 SEAD Tasks
|
-- - 2 SEAD Tasks
|
||||||
--
|
--
|
||||||
-- the Join Planned Task Menu Hierarchy could look like this:
|
-- the Join Planned Task Menu Hierarchy could look like this:
|
||||||
--
|
--
|
||||||
-- Radio MENU Structure (F10. Other)
|
-- Radio MENU Structure (F10. Other)
|
||||||
--
|
--
|
||||||
-- F1. Command Center [Gori]
|
-- F1. Command Center [Gori]
|
||||||
-- F1. Mission "Alpha"
|
-- F1. Mission "Alpha"
|
||||||
-- F1. Mission Briefing
|
-- F1. Mission Briefing
|
||||||
@@ -296,26 +296,26 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
-- F1. SEAD.003
|
-- F1. SEAD.003
|
||||||
-- F2. SEAD.004
|
-- F2. SEAD.004
|
||||||
-- F3. SEAD.005
|
-- F3. SEAD.005
|
||||||
--
|
--
|
||||||
-- An example from within a running simulation:
|
-- An example from within a running simulation:
|
||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
-- Each Task Type Menu would have a list of the Task Menus underneath.
|
-- Each Task Type Menu would have a list of the Task Menus underneath.
|
||||||
-- Each Task Menu (eg. `CAS.001`) has a **detailed Task Menu structure to control the specific task**!
|
-- Each Task Menu (eg. `CAS.001`) has a **detailed Task Menu structure to control the specific task**!
|
||||||
--
|
--
|
||||||
-- ### 1.3.1. Planned Task Menu
|
-- ### 1.3.1. Planned Task Menu
|
||||||
--
|
--
|
||||||
-- Each Planned Task Menu will allow for the following actions:
|
-- Each Planned Task Menu will allow for the following actions:
|
||||||
--
|
--
|
||||||
-- - Report Task Details: Provides a detailed report on the Planned Task.
|
-- - Report Task Details: Provides a detailed report on the Planned Task.
|
||||||
-- - Mark Task Location on Map: Mark the approximate location of the Task on the Map, if relevant.
|
-- - Mark Task Location on Map: Mark the approximate location of the Task on the Map, if relevant.
|
||||||
-- - Join Task: Join the Task. This is THE menu option to let a Player join the Task, and to engage within the Mission.
|
-- - Join Task: Join the Task. This is THE menu option to let a Player join the Task, and to engage within the Mission.
|
||||||
--
|
--
|
||||||
-- The Join Planned Task Menu could look like this for for CC |Gori|, Mission "Alpha":
|
-- The Join Planned Task Menu could look like this for for CC |Gori|, Mission "Alpha":
|
||||||
--
|
--
|
||||||
-- Radio MENU Structure (F10. Other)
|
-- Radio MENU Structure (F10. Other)
|
||||||
--
|
--
|
||||||
-- F1. Command Center |Gori|
|
-- F1. Command Center |Gori|
|
||||||
-- F1. Mission "Alpha"
|
-- F1. Mission "Alpha"
|
||||||
-- F1. Mission Briefing
|
-- F1. Mission Briefing
|
||||||
@@ -328,22 +328,22 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
-- F1. Report Task Details
|
-- F1. Report Task Details
|
||||||
-- F2. Mark Task Location on Map
|
-- F2. Mark Task Location on Map
|
||||||
-- F3. Join Task
|
-- F3. Join Task
|
||||||
--
|
--
|
||||||
-- **The Join Task is THE menu option to let a Player join the Task, and to engage within the Mission.**
|
-- **The Join Task is THE menu option to let a Player join the Task, and to engage within the Mission.**
|
||||||
--
|
--
|
||||||
--
|
--
|
||||||
-- ## 1.4. Assigned Task Menu
|
-- ## 1.4. Assigned Task Menu
|
||||||
--
|
--
|
||||||
-- The Assigned Task Menu allows to control the **current assigned task** within the Mission.
|
-- The Assigned Task Menu allows to control the **current assigned task** within the Mission.
|
||||||
--
|
--
|
||||||
-- Depending on the Type of Task, the following menu options will be available:
|
-- Depending on the Type of Task, the following menu options will be available:
|
||||||
--
|
--
|
||||||
-- - **Report Task Details**: Provides a detailed report on the Planned Task.
|
-- - **Report Task Details**: Provides a detailed report on the Planned Task.
|
||||||
-- - **Mark Task Location on Map**: Mark the approximate location of the Task on the Map, if relevant.
|
-- - **Mark Task Location on Map**: Mark the approximate location of the Task on the Map, if relevant.
|
||||||
-- - **Abort Task: Abort the current assigned Task:** This menu option lets the player abort the Task.
|
-- - **Abort Task: Abort the current assigned Task:** This menu option lets the player abort the Task.
|
||||||
--
|
--
|
||||||
-- For example, for CC |Gori|, Mission "Alpha", the Assigned Menu could be:
|
-- For example, for CC |Gori|, Mission "Alpha", the Assigned Menu could be:
|
||||||
--
|
--
|
||||||
-- F1. Command Center |Gori|
|
-- F1. Command Center |Gori|
|
||||||
-- F1. Mission "Alpha"
|
-- F1. Mission "Alpha"
|
||||||
-- F1. Mission Briefing
|
-- F1. Mission Briefing
|
||||||
@@ -354,90 +354,89 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
-- F1. Report Task Details
|
-- F1. Report Task Details
|
||||||
-- F2. Mark Task Location on Map
|
-- F2. Mark Task Location on Map
|
||||||
-- F3. Abort Task
|
-- F3. Abort Task
|
||||||
--
|
--
|
||||||
-- Task abortion will result in the Task to be Cancelled, and the Task **may** be **Replanned**.
|
-- Task abortion will result in the Task to be Cancelled, and the Task **may** be **Replanned**.
|
||||||
-- However, this will depend on the setup of each Mission.
|
-- However, this will depend on the setup of each Mission.
|
||||||
--
|
--
|
||||||
-- ## 1.5. Messages
|
-- ## 1.5. Messages
|
||||||
--
|
--
|
||||||
-- During game play, different messages are displayed.
|
-- During game play, different messages are displayed.
|
||||||
-- These messages provide an update of the achievements made, and the state wherein the task is.
|
-- These messages provide an update of the achievements made, and the state wherein the task is.
|
||||||
--
|
--
|
||||||
-- The various reports can be used also to retrieve the current status of the mission and its tasks.
|
-- The various reports can be used also to retrieve the current status of the mission and its tasks.
|
||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
-- The @{Settings} menu provides additional options to control the timing of the messages.
|
-- The @{Settings} menu provides additional options to control the timing of the messages.
|
||||||
-- There are:
|
-- There are:
|
||||||
--
|
--
|
||||||
-- - Status messages, which are quick status updates. The settings menu allows to switch off these messages.
|
-- - Status messages, which are quick status updates. The settings menu allows to switch off these messages.
|
||||||
-- - Information messages, which are shown a bit longer, as they contain important information.
|
-- - Information messages, which are shown a bit longer, as they contain important information.
|
||||||
-- - Summary reports, which are quick reports showing a high level summary.
|
-- - Summary reports, which are quick reports showing a high level summary.
|
||||||
-- - Overview reports, which are providing the essential information. It provides an overview of a greater thing, and may take a bit of time to read.
|
-- - Overview reports, which are providing the essential information. It provides an overview of a greater thing, and may take a bit of time to read.
|
||||||
-- - Detailed reports, which provide with very detailed information. It takes a bit longer to read those reports, so the display of those could be a bit longer.
|
-- - Detailed reports, which provide with very detailed information. It takes a bit longer to read those reports, so the display of those could be a bit longer.
|
||||||
--
|
--
|
||||||
-- # 2. TASK\_A2G\_DISPATCHER constructor
|
-- # 2. TASK\_A2G\_DISPATCHER constructor
|
||||||
--
|
--
|
||||||
-- The @{#TASK_A2G_DISPATCHER.New}() method creates a new TASK\_A2G\_DISPATCHER instance.
|
-- The @{#TASK_A2G_DISPATCHER.New}() method creates a new TASK\_A2G\_DISPATCHER instance.
|
||||||
--
|
--
|
||||||
-- # 3. Usage
|
-- # 3. Usage
|
||||||
--
|
--
|
||||||
-- To use the TASK\_A2G\_DISPATCHER class, you need:
|
-- To use the TASK\_A2G\_DISPATCHER class, you need:
|
||||||
--
|
--
|
||||||
-- - A @{CommandCenter} object. The master communication channel.
|
-- - A @{CommandCenter} object. The master communication channel.
|
||||||
-- - A @{Mission} object. Each task belongs to a Mission.
|
-- - A @{Mission} object. Each task belongs to a Mission.
|
||||||
-- - A @{Detection} object. There are several detection grouping methods to choose from.
|
-- - A @{Detection} object. There are several detection grouping methods to choose from.
|
||||||
-- - A @{Task_A2G_Dispatcher} object. The master A2G task dispatcher.
|
-- - A @{Task_A2G_Dispatcher} object. The master A2G task dispatcher.
|
||||||
-- - A @{Set} of @{Wrapper.Group} objects that will detect the emeny, the RecceSet. This is attached to the @{Detection} object.
|
-- - A @{Set} of @{Wrapper.Group} objects that will detect the enemy, the RecceSet. This is attached to the @{Detection} object.
|
||||||
-- - A @{Set} ob @{Wrapper.Group} objects that will attack the enemy, the AttackSet. This is attached to the @{Task_A2G_Dispatcher} object.
|
-- - A @{Set} ob @{Wrapper.Group} objects that will attack the enemy, the AttackSet. This is attached to the @{Task_A2G_Dispatcher} object.
|
||||||
--
|
|
||||||
-- Below an example mission declaration that is defines a Task A2G Dispatcher object.
|
|
||||||
--
|
--
|
||||||
-- -- Declare the Command Center
|
-- Below an example mission declaration that is defines a Task A2G Dispatcher object.
|
||||||
|
--
|
||||||
|
-- -- Declare the Command Center
|
||||||
-- local HQ = GROUP
|
-- local HQ = GROUP
|
||||||
-- :FindByName( "HQ", "Bravo HQ" )
|
-- :FindByName( "HQ", "Bravo HQ" )
|
||||||
--
|
--
|
||||||
-- local CommandCenter = COMMANDCENTER
|
-- local CommandCenter = COMMANDCENTER
|
||||||
-- :New( HQ, "Lima" )
|
-- :New( HQ, "Lima" )
|
||||||
--
|
--
|
||||||
-- -- Declare the Mission for the Command Center.
|
-- -- Declare the Mission for the Command Center.
|
||||||
-- local Mission = MISSION
|
-- local Mission = MISSION
|
||||||
-- :New( CommandCenter, "Overlord", "High", "Attack Detect Mission Briefing", coalition.side.RED )
|
-- :New( CommandCenter, "Overlord", "High", "Attack Detect Mission Briefing", coalition.side.RED )
|
||||||
--
|
--
|
||||||
-- -- Define the RecceSet that will detect the enemy.
|
-- -- Define the RecceSet that will detect the enemy.
|
||||||
-- local RecceSet = SET_GROUP
|
-- local RecceSet = SET_GROUP
|
||||||
-- :New()
|
-- :New()
|
||||||
-- :FilterPrefixes( "FAC" )
|
-- :FilterPrefixes( "FAC" )
|
||||||
-- :FilterCoalitions("red")
|
-- :FilterCoalitions("red")
|
||||||
-- :FilterStart()
|
-- :FilterStart()
|
||||||
--
|
--
|
||||||
-- -- Setup the detection. We use DETECTION_AREAS to detect and group the enemies within areas of 3 km radius.
|
-- -- Setup the detection. We use DETECTION_AREAS to detect and group the enemies within areas of 3 km radius.
|
||||||
-- local DetectionAreas = DETECTION_AREAS
|
-- local DetectionAreas = DETECTION_AREAS
|
||||||
-- :New( RecceSet, 3000 ) -- The RecceSet will detect the enemies.
|
-- :New( RecceSet, 3000 ) -- The RecceSet will detect the enemies.
|
||||||
--
|
--
|
||||||
-- -- Setup the AttackSet, which is a SET_GROUP.
|
-- -- Setup the AttackSet, which is a SET_GROUP.
|
||||||
-- -- The SET_GROUP is a dynamic collection of GROUP objects.
|
-- -- The SET_GROUP is a dynamic collection of GROUP objects.
|
||||||
-- local AttackSet = SET_GROUP
|
-- local AttackSet = SET_GROUP
|
||||||
-- :New() -- Create the SET_GROUP object.
|
-- :New() -- Create the SET_GROUP object.
|
||||||
-- :FilterCoalitions( "red" ) -- Only incorporate the RED coalitions.
|
-- :FilterCoalitions( "red" ) -- Only incorporate the RED coalitions.
|
||||||
-- :FilterPrefixes( "Attack" ) -- Only incorporate groups that start with the name Attack.
|
-- :FilterPrefixes( "Attack" ) -- Only incorporate groups that start with the name Attack.
|
||||||
-- :FilterStart() -- Enable the dynamic filtering. From this moment the AttackSet will contain all groups that are red and start with the name Attack.
|
-- :FilterStart() -- Enable the dynamic filtering. From this moment the AttackSet will contain all groups that are red and start with the name Attack.
|
||||||
--
|
--
|
||||||
-- -- Now we have everything to setup the main A2G TaskDispatcher.
|
-- -- Now we have everything to setup the main A2G TaskDispatcher.
|
||||||
-- TaskDispatcher = TASK_A2G_DISPATCHER
|
-- TaskDispatcher = TASK_A2G_DISPATCHER
|
||||||
-- :New( Mission, AttackSet, DetectionAreas ) -- We assign the TaskDispatcher under Mission. The AttackSet will engage the enemy and will recieve the dispatched Tasks. The DetectionAreas will report any detected enemies to the TaskDispatcher.
|
-- :New( Mission, AttackSet, DetectionAreas ) -- We assign the TaskDispatcher under Mission. The AttackSet will engage the enemy and will receive the dispatched Tasks. The DetectionAreas will report any detected enemies to the TaskDispatcher.
|
||||||
--
|
--
|
||||||
--
|
--
|
||||||
--
|
--
|
||||||
-- @field #TASK_A2G_DISPATCHER
|
-- @field #TASK_A2G_DISPATCHER
|
||||||
TASK_A2G_DISPATCHER = {
|
TASK_A2G_DISPATCHER = {
|
||||||
ClassName = "TASK_A2G_DISPATCHER",
|
ClassName = "TASK_A2G_DISPATCHER",
|
||||||
Mission = nil,
|
Mission = nil,
|
||||||
Detection = nil,
|
Detection = nil,
|
||||||
Tasks = {},
|
Tasks = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
--- TASK_A2G_DISPATCHER constructor.
|
--- TASK_A2G_DISPATCHER constructor.
|
||||||
-- @param #TASK_A2G_DISPATCHER self
|
-- @param #TASK_A2G_DISPATCHER self
|
||||||
-- @param Tasking.Mission#MISSION Mission The mission for which the task dispatching is done.
|
-- @param Tasking.Mission#MISSION Mission The mission for which the task dispatching is done.
|
||||||
@@ -445,18 +444,18 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
-- @param Functional.Detection#DETECTION_BASE Detection The detection results that are used to dynamically assign new tasks to human players.
|
-- @param Functional.Detection#DETECTION_BASE Detection The detection results that are used to dynamically assign new tasks to human players.
|
||||||
-- @return #TASK_A2G_DISPATCHER self
|
-- @return #TASK_A2G_DISPATCHER self
|
||||||
function TASK_A2G_DISPATCHER:New( Mission, SetGroup, Detection )
|
function TASK_A2G_DISPATCHER:New( Mission, SetGroup, Detection )
|
||||||
|
|
||||||
-- Inherits from DETECTION_MANAGER
|
-- Inherits from DETECTION_MANAGER
|
||||||
local self = BASE:Inherit( self, DETECTION_MANAGER:New( SetGroup, Detection ) ) -- #TASK_A2G_DISPATCHER
|
local self = BASE:Inherit( self, DETECTION_MANAGER:New( SetGroup, Detection ) ) -- #TASK_A2G_DISPATCHER
|
||||||
|
|
||||||
self.Detection = Detection
|
self.Detection = Detection
|
||||||
self.Mission = Mission
|
self.Mission = Mission
|
||||||
self.FlashNewTask = true --set to false to suppress flash messages
|
self.FlashNewTask = true -- set to false to suppress flash messages
|
||||||
|
|
||||||
self.Detection:FilterCategories( { Unit.Category.GROUND_UNIT } )
|
self.Detection:FilterCategories( { Unit.Category.GROUND_UNIT } )
|
||||||
|
|
||||||
self:AddTransition( "Started", "Assign", "Started" )
|
self:AddTransition( "Started", "Assign", "Started" )
|
||||||
|
|
||||||
--- OnAfter Transition Handler for Event Assign.
|
--- OnAfter Transition Handler for Event Assign.
|
||||||
-- @function [parent=#TASK_A2G_DISPATCHER] OnAfterAssign
|
-- @function [parent=#TASK_A2G_DISPATCHER] OnAfterAssign
|
||||||
-- @param #TASK_A2G_DISPATCHER self
|
-- @param #TASK_A2G_DISPATCHER self
|
||||||
@@ -466,19 +465,19 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
-- @param Tasking.Task_A2G#TASK_A2G Task
|
-- @param Tasking.Task_A2G#TASK_A2G Task
|
||||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||||
-- @param #string PlayerName
|
-- @param #string PlayerName
|
||||||
|
|
||||||
self:__Start( 5 )
|
self:__Start( 5 )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set flashing player messages on or off
|
--- Set flashing player messages on or off
|
||||||
-- @param #TASK_A2G_DISPATCHER self
|
-- @param #TASK_A2G_DISPATCHER self
|
||||||
-- @param #boolean onoff Set messages on (true) or off (false)
|
-- @param #boolean onoff Set messages on (true) or off (false)
|
||||||
function TASK_A2G_DISPATCHER:SetSendMessages( onoff )
|
function TASK_A2G_DISPATCHER:SetSendMessages( onoff )
|
||||||
self.FlashNewTask = onoff
|
self.FlashNewTask = onoff
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Creates a SEAD task when there are targets for it.
|
--- Creates a SEAD task when there are targets for it.
|
||||||
-- @param #TASK_A2G_DISPATCHER self
|
-- @param #TASK_A2G_DISPATCHER self
|
||||||
-- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem
|
-- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem
|
||||||
@@ -486,7 +485,7 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
-- @return #nil If there are no targets to be set.
|
-- @return #nil If there are no targets to be set.
|
||||||
function TASK_A2G_DISPATCHER:EvaluateSEAD( DetectedItem )
|
function TASK_A2G_DISPATCHER:EvaluateSEAD( DetectedItem )
|
||||||
self:F( { DetectedItem.ItemID } )
|
self:F( { DetectedItem.ItemID } )
|
||||||
|
|
||||||
local DetectedSet = DetectedItem.Set
|
local DetectedSet = DetectedItem.Set
|
||||||
local DetectedZone = DetectedItem.Zone
|
local DetectedZone = DetectedItem.Zone
|
||||||
|
|
||||||
@@ -500,10 +499,10 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
TargetSetUnit:SetDatabase( DetectedSet )
|
TargetSetUnit:SetDatabase( DetectedSet )
|
||||||
TargetSetUnit:FilterHasSEAD()
|
TargetSetUnit:FilterHasSEAD()
|
||||||
TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection.
|
TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection.
|
||||||
|
|
||||||
return TargetSetUnit
|
return TargetSetUnit
|
||||||
end
|
end
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -514,11 +513,10 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
-- @return #nil If there are no targets to be set.
|
-- @return #nil If there are no targets to be set.
|
||||||
function TASK_A2G_DISPATCHER:EvaluateCAS( DetectedItem )
|
function TASK_A2G_DISPATCHER:EvaluateCAS( DetectedItem )
|
||||||
self:F( { DetectedItem.ItemID } )
|
self:F( { DetectedItem.ItemID } )
|
||||||
|
|
||||||
local DetectedSet = DetectedItem.Set
|
local DetectedSet = DetectedItem.Set
|
||||||
local DetectedZone = DetectedItem.Zone
|
local DetectedZone = DetectedItem.Zone
|
||||||
|
|
||||||
|
|
||||||
-- Determine if the set has ground units.
|
-- Determine if the set has ground units.
|
||||||
-- There should be ground unit friendlies nearby. Airborne units are valid friendlies types.
|
-- There should be ground unit friendlies nearby. Airborne units are valid friendlies types.
|
||||||
-- And there shouldn't be any radar.
|
-- And there shouldn't be any radar.
|
||||||
@@ -532,13 +530,13 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
local TargetSetUnit = SET_UNIT:New()
|
local TargetSetUnit = SET_UNIT:New()
|
||||||
TargetSetUnit:SetDatabase( DetectedSet )
|
TargetSetUnit:SetDatabase( DetectedSet )
|
||||||
TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection.
|
TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection.
|
||||||
|
|
||||||
return TargetSetUnit
|
return TargetSetUnit
|
||||||
end
|
end
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Creates a BAI task when there are targets for it.
|
--- Creates a BAI task when there are targets for it.
|
||||||
-- @param #TASK_A2G_DISPATCHER self
|
-- @param #TASK_A2G_DISPATCHER self
|
||||||
-- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem
|
-- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem
|
||||||
@@ -546,11 +544,10 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
-- @return #nil If there are no targets to be set.
|
-- @return #nil If there are no targets to be set.
|
||||||
function TASK_A2G_DISPATCHER:EvaluateBAI( DetectedItem, FriendlyCoalition )
|
function TASK_A2G_DISPATCHER:EvaluateBAI( DetectedItem, FriendlyCoalition )
|
||||||
self:F( { DetectedItem.ItemID } )
|
self:F( { DetectedItem.ItemID } )
|
||||||
|
|
||||||
local DetectedSet = DetectedItem.Set
|
local DetectedSet = DetectedItem.Set
|
||||||
local DetectedZone = DetectedItem.Zone
|
local DetectedZone = DetectedItem.Zone
|
||||||
|
|
||||||
|
|
||||||
-- Determine if the set has ground units.
|
-- Determine if the set has ground units.
|
||||||
-- There shouldn't be any ground unit friendlies nearby.
|
-- There shouldn't be any ground unit friendlies nearby.
|
||||||
-- And there shouldn't be any radar.
|
-- And there shouldn't be any radar.
|
||||||
@@ -564,19 +561,18 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
local TargetSetUnit = SET_UNIT:New()
|
local TargetSetUnit = SET_UNIT:New()
|
||||||
TargetSetUnit:SetDatabase( DetectedSet )
|
TargetSetUnit:SetDatabase( DetectedSet )
|
||||||
TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection.
|
TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection.
|
||||||
|
|
||||||
return TargetSetUnit
|
return TargetSetUnit
|
||||||
end
|
end
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function TASK_A2G_DISPATCHER:RemoveTask( TaskIndex )
|
function TASK_A2G_DISPATCHER:RemoveTask( TaskIndex )
|
||||||
self.Mission:RemoveTask( self.Tasks[TaskIndex] )
|
self.Mission:RemoveTask( self.Tasks[TaskIndex] )
|
||||||
self.Tasks[TaskIndex] = nil
|
self.Tasks[TaskIndex] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Evaluates the removal of the Task from the Mission.
|
--- Evaluates the removal of the Task from the Mission.
|
||||||
-- Can only occur when the DetectedItem is Changed AND the state of the Task is "Planned".
|
-- Can only occur when the DetectedItem is Changed AND the state of the Task is "Planned".
|
||||||
-- @param #TASK_A2G_DISPATCHER self
|
-- @param #TASK_A2G_DISPATCHER self
|
||||||
@@ -586,17 +582,16 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
-- @param #boolean DetectedItemChange
|
-- @param #boolean DetectedItemChange
|
||||||
-- @return Tasking.Task#TASK
|
-- @return Tasking.Task#TASK
|
||||||
function TASK_A2G_DISPATCHER:EvaluateRemoveTask( Mission, Task, TaskIndex, DetectedItemChanged )
|
function TASK_A2G_DISPATCHER:EvaluateRemoveTask( Mission, Task, TaskIndex, DetectedItemChanged )
|
||||||
|
|
||||||
if Task then
|
if Task then
|
||||||
if ( Task:IsStatePlanned() and DetectedItemChanged == true ) or Task:IsStateCancelled() then
|
if (Task:IsStatePlanned() and DetectedItemChanged == true) or Task:IsStateCancelled() then
|
||||||
--self:F( "Removing Tasking: " .. Task:GetTaskName() )
|
-- self:F( "Removing Tasking: " .. Task:GetTaskName() )
|
||||||
self:RemoveTask( TaskIndex )
|
self:RemoveTask( TaskIndex )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return Task
|
return Task
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Assigns tasks in relation to the detected items to the @{Core.Set#SET_GROUP}.
|
--- Assigns tasks in relation to the detected items to the @{Core.Set#SET_GROUP}.
|
||||||
-- @param #TASK_A2G_DISPATCHER self
|
-- @param #TASK_A2G_DISPATCHER self
|
||||||
@@ -604,15 +599,15 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
-- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop.
|
-- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop.
|
||||||
function TASK_A2G_DISPATCHER:ProcessDetected( Detection )
|
function TASK_A2G_DISPATCHER:ProcessDetected( Detection )
|
||||||
self:F()
|
self:F()
|
||||||
|
|
||||||
local AreaMsg = {}
|
local AreaMsg = {}
|
||||||
local TaskMsg = {}
|
local TaskMsg = {}
|
||||||
local ChangeMsg = {}
|
local ChangeMsg = {}
|
||||||
|
|
||||||
local Mission = self.Mission
|
local Mission = self.Mission
|
||||||
|
|
||||||
if Mission:IsIDLE() or Mission:IsENGAGED() then
|
if Mission:IsIDLE() or Mission:IsENGAGED() then
|
||||||
|
|
||||||
local TaskReport = REPORT:New()
|
local TaskReport = REPORT:New()
|
||||||
|
|
||||||
-- Checking the task queue for the dispatcher, and removing any obsolete task!
|
-- Checking the task queue for the dispatcher, and removing any obsolete task!
|
||||||
@@ -634,21 +629,21 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
|
|
||||||
--- First we need to the detected targets.
|
--- First we need to the detected targets.
|
||||||
for DetectedItemID, DetectedItem in pairs( Detection:GetDetectedItems() ) do
|
for DetectedItemID, DetectedItem in pairs( Detection:GetDetectedItems() ) do
|
||||||
|
|
||||||
local DetectedItem = DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem
|
local DetectedItem = DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem
|
||||||
local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT
|
local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT
|
||||||
local DetectedZone = DetectedItem.Zone
|
local DetectedZone = DetectedItem.Zone
|
||||||
--self:F( { "Targets in DetectedItem", DetectedItem.ItemID, DetectedSet:Count(), tostring( DetectedItem ) } )
|
-- self:F( { "Targets in DetectedItem", DetectedItem.ItemID, DetectedSet:Count(), tostring( DetectedItem ) } )
|
||||||
--DetectedSet:Flush( self )
|
-- DetectedSet:Flush( self )
|
||||||
|
|
||||||
local DetectedItemID = DetectedItem.ID
|
local DetectedItemID = DetectedItem.ID
|
||||||
local TaskIndex = DetectedItem.Index
|
local TaskIndex = DetectedItem.Index
|
||||||
local DetectedItemChanged = DetectedItem.Changed
|
local DetectedItemChanged = DetectedItem.Changed
|
||||||
|
|
||||||
self:F( { DetectedItemChanged = DetectedItemChanged, DetectedItemID = DetectedItemID, TaskIndex = TaskIndex } )
|
self:F( { DetectedItemChanged = DetectedItemChanged, DetectedItemID = DetectedItemID, TaskIndex = TaskIndex } )
|
||||||
|
|
||||||
local Task = self.Tasks[TaskIndex] -- Tasking.Task_A2G#TASK_A2G
|
local Task = self.Tasks[TaskIndex] -- Tasking.Task_A2G#TASK_A2G
|
||||||
|
|
||||||
if Task then
|
if Task then
|
||||||
-- If there is a Task and the task was assigned, then we check if the task was changed ... If it was, we need to reevaluate the targets.
|
-- If there is a Task and the task was assigned, then we check if the task was changed ... If it was, we need to reevaluate the targets.
|
||||||
if Task:IsStateAssigned() then
|
if Task:IsStateAssigned() then
|
||||||
@@ -660,7 +655,7 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
Task:SetTargetSetUnit( TargetSetUnit )
|
Task:SetTargetSetUnit( TargetSetUnit )
|
||||||
Task:SetDetection( Detection, DetectedItem )
|
Task:SetDetection( Detection, DetectedItem )
|
||||||
Task:UpdateTaskInfo( DetectedItem )
|
Task:UpdateTaskInfo( DetectedItem )
|
||||||
TargetsReport:Add( Detection:GetChangeText( DetectedItem ) )
|
TargetsReport:Add( Detection:GetChangeText( DetectedItem ) )
|
||||||
else
|
else
|
||||||
Task:Cancel()
|
Task:Cancel()
|
||||||
end
|
end
|
||||||
@@ -691,18 +686,18 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Now we send to each group the changes, if any.
|
-- Now we send to each group the changes, if any.
|
||||||
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
|
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
|
||||||
local TargetsText = TargetsReport:Text(", ")
|
local TargetsText = TargetsReport:Text( ", " )
|
||||||
if ( Mission:IsGroupAssigned(TaskGroup) ) and TargetsText ~= "" and self.FlashNewTask then
|
if (Mission:IsGroupAssigned( TaskGroup )) and TargetsText ~= "" and self.FlashNewTask then
|
||||||
Mission:GetCommandCenter():MessageToGroup( string.format( "Task %s has change of targets:\n %s", Task:GetName(), TargetsText ), TaskGroup )
|
Mission:GetCommandCenter():MessageToGroup( string.format( "Task %s has change of targets:\n %s", Task:GetName(), TargetsText ), TaskGroup )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if Task then
|
if Task then
|
||||||
if Task:IsStatePlanned() then
|
if Task:IsStatePlanned() then
|
||||||
if DetectedItemChanged == true then -- The detection has changed, thus a new TargetSet is to be evaluated and set
|
if DetectedItemChanged == true then -- The detection has changed, thus a new TargetSet is to be evaluated and set
|
||||||
@@ -753,7 +748,7 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
local TargetSetUnit = self:EvaluateSEAD( DetectedItem ) -- Returns a SetUnit if there are targets to be SEADed...
|
local TargetSetUnit = self:EvaluateSEAD( DetectedItem ) -- Returns a SetUnit if there are targets to be SEADed...
|
||||||
if TargetSetUnit then
|
if TargetSetUnit then
|
||||||
Task = TASK_A2G_SEAD:New( Mission, self.SetGroup, string.format( "SEAD.%03d", DetectedItemID ), TargetSetUnit )
|
Task = TASK_A2G_SEAD:New( Mission, self.SetGroup, string.format( "SEAD.%03d", DetectedItemID ), TargetSetUnit )
|
||||||
DetectedItem.DesignateMenuName = string.format( "SEAD.%03d", DetectedItemID ) --inject a name for DESIGNATE, if using same DETECTION object
|
DetectedItem.DesignateMenuName = string.format( "SEAD.%03d", DetectedItemID ) -- inject a name for DESIGNATE, if using same DETECTION object
|
||||||
Task:SetDetection( Detection, DetectedItem )
|
Task:SetDetection( Detection, DetectedItem )
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -762,7 +757,7 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
local TargetSetUnit = self:EvaluateCAS( DetectedItem ) -- Returns a SetUnit if there are targets to be CASed...
|
local TargetSetUnit = self:EvaluateCAS( DetectedItem ) -- Returns a SetUnit if there are targets to be CASed...
|
||||||
if TargetSetUnit then
|
if TargetSetUnit then
|
||||||
Task = TASK_A2G_CAS:New( Mission, self.SetGroup, string.format( "CAS.%03d", DetectedItemID ), TargetSetUnit )
|
Task = TASK_A2G_CAS:New( Mission, self.SetGroup, string.format( "CAS.%03d", DetectedItemID ), TargetSetUnit )
|
||||||
DetectedItem.DesignateMenuName = string.format( "CAS.%03d", DetectedItemID ) --inject a name for DESIGNATE, if using same DETECTION object
|
DetectedItem.DesignateMenuName = string.format( "CAS.%03d", DetectedItemID ) -- inject a name for DESIGNATE, if using same DETECTION object
|
||||||
Task:SetDetection( Detection, DetectedItem )
|
Task:SetDetection( Detection, DetectedItem )
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -771,19 +766,19 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
local TargetSetUnit = self:EvaluateBAI( DetectedItem, self.Mission:GetCommandCenter():GetPositionable():GetCoalition() ) -- Returns a SetUnit if there are targets to be BAIed...
|
local TargetSetUnit = self:EvaluateBAI( DetectedItem, self.Mission:GetCommandCenter():GetPositionable():GetCoalition() ) -- Returns a SetUnit if there are targets to be BAIed...
|
||||||
if TargetSetUnit then
|
if TargetSetUnit then
|
||||||
Task = TASK_A2G_BAI:New( Mission, self.SetGroup, string.format( "BAI.%03d", DetectedItemID ), TargetSetUnit )
|
Task = TASK_A2G_BAI:New( Mission, self.SetGroup, string.format( "BAI.%03d", DetectedItemID ), TargetSetUnit )
|
||||||
DetectedItem.DesignateMenuName = string.format( "BAI.%03d", DetectedItemID ) --inject a name for DESIGNATE, if using same DETECTION object
|
DetectedItem.DesignateMenuName = string.format( "BAI.%03d", DetectedItemID ) -- inject a name for DESIGNATE, if using same DETECTION object
|
||||||
Task:SetDetection( Detection, DetectedItem )
|
Task:SetDetection( Detection, DetectedItem )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if Task then
|
if Task then
|
||||||
self.Tasks[TaskIndex] = Task
|
self.Tasks[TaskIndex] = Task
|
||||||
Task:SetTargetZone( DetectedZone )
|
Task:SetTargetZone( DetectedZone )
|
||||||
Task:SetDispatcher( self )
|
Task:SetDispatcher( self )
|
||||||
Task:UpdateTaskInfo( DetectedItem )
|
Task:UpdateTaskInfo( DetectedItem )
|
||||||
Mission:AddTask( Task )
|
Mission:AddTask( Task )
|
||||||
|
|
||||||
function Task.OnEnterSuccess( Task, From, Event, To )
|
function Task.OnEnterSuccess( Task, From, Event, To )
|
||||||
self:Success( Task )
|
self:Success( Task )
|
||||||
end
|
end
|
||||||
@@ -791,7 +786,7 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
function Task.OnEnterCancelled( Task, From, Event, To )
|
function Task.OnEnterCancelled( Task, From, Event, To )
|
||||||
self:Cancelled( Task )
|
self:Cancelled( Task )
|
||||||
end
|
end
|
||||||
|
|
||||||
function Task.OnEnterFailed( Task, From, Event, To )
|
function Task.OnEnterFailed( Task, From, Event, To )
|
||||||
self:Failed( Task )
|
self:Failed( Task )
|
||||||
end
|
end
|
||||||
@@ -799,31 +794,29 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
function Task.OnEnterAborted( Task, From, Event, To )
|
function Task.OnEnterAborted( Task, From, Event, To )
|
||||||
self:Aborted( Task )
|
self:Aborted( Task )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
TaskReport:Add( Task:GetName() )
|
TaskReport:Add( Task:GetName() )
|
||||||
else
|
else
|
||||||
self:F("This should not happen")
|
self:F( "This should not happen" )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- OK, so the tasking has been done, now delete the changes reported for the area.
|
-- OK, so the tasking has been done, now delete the changes reported for the area.
|
||||||
Detection:AcceptChanges( DetectedItem )
|
Detection:AcceptChanges( DetectedItem )
|
||||||
end
|
end
|
||||||
|
|
||||||
-- TODO set menus using the HQ coordinator
|
-- TODO set menus using the HQ coordinator
|
||||||
Mission:GetCommandCenter():SetMenu()
|
Mission:GetCommandCenter():SetMenu()
|
||||||
|
|
||||||
local TaskText = TaskReport:Text(", ")
|
local TaskText = TaskReport:Text( ", " )
|
||||||
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
|
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
|
||||||
if ( not Mission:IsGroupAssigned(TaskGroup) ) and TaskText ~= "" and self.FlashNewTask then
|
if (not Mission:IsGroupAssigned( TaskGroup )) and TaskText ~= "" and self.FlashNewTask then
|
||||||
Mission:GetCommandCenter():MessageToGroup( string.format( "%s has tasks %s. Subscribe to a task using the radio menu.", Mission:GetShortText(), TaskText ), TaskGroup )
|
Mission:GetCommandCenter():MessageToGroup( string.format( "%s has tasks %s. Subscribe to a task using the radio menu.", Mission:GetShortText(), TaskText ), TaskGroup )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -683,6 +683,7 @@ do -- TASK_CARGO_DISPATCHER
|
|||||||
-- If no TaskPrefix is given, then "Transport" will be used as the prefix.
|
-- If no TaskPrefix is given, then "Transport" will be used as the prefix.
|
||||||
-- @param Core.SetCargo#SET_CARGO SetCargo The SetCargo to be transported.
|
-- @param Core.SetCargo#SET_CARGO SetCargo The SetCargo to be transported.
|
||||||
-- @param #string Briefing The briefing of the task transport to be shown to the player.
|
-- @param #string Briefing The briefing of the task transport to be shown to the player.
|
||||||
|
-- @param #boolean Silent If true don't send a message that a new task is available.
|
||||||
-- @return Tasking.Task_Cargo_Transport#TASK_CARGO_TRANSPORT
|
-- @return Tasking.Task_Cargo_Transport#TASK_CARGO_TRANSPORT
|
||||||
-- @usage
|
-- @usage
|
||||||
--
|
--
|
||||||
@@ -705,10 +706,12 @@ do -- TASK_CARGO_DISPATCHER
|
|||||||
-- -- Here we set a TransportDeployZone. We use the WorkplaceTask as the reference, and provide a ZONE object.
|
-- -- Here we set a TransportDeployZone. We use the WorkplaceTask as the reference, and provide a ZONE object.
|
||||||
-- TaskDispatcher:SetTransportDeployZone( WorkplaceTask, ZONE:New( "Workplace" ) )
|
-- TaskDispatcher:SetTransportDeployZone( WorkplaceTask, ZONE:New( "Workplace" ) )
|
||||||
--
|
--
|
||||||
function TASK_CARGO_DISPATCHER:AddTransportTask( TaskPrefix, SetCargo, Briefing )
|
function TASK_CARGO_DISPATCHER:AddTransportTask( TaskPrefix, SetCargo, Briefing, Silent )
|
||||||
|
|
||||||
self.TransportCount = self.TransportCount + 1
|
self.TransportCount = self.TransportCount + 1
|
||||||
|
|
||||||
|
local verbose = Silent or false
|
||||||
|
|
||||||
local TaskName = string.format( ( TaskPrefix or "Transport" ) .. ".%03d", self.TransportCount )
|
local TaskName = string.format( ( TaskPrefix or "Transport" ) .. ".%03d", self.TransportCount )
|
||||||
|
|
||||||
self.Transport[TaskName] = {}
|
self.Transport[TaskName] = {}
|
||||||
@@ -717,7 +720,7 @@ do -- TASK_CARGO_DISPATCHER
|
|||||||
self.Transport[TaskName].Task = nil
|
self.Transport[TaskName].Task = nil
|
||||||
self.Transport[TaskName].TaskPrefix = TaskPrefix
|
self.Transport[TaskName].TaskPrefix = TaskPrefix
|
||||||
|
|
||||||
self:ManageTasks()
|
self:ManageTasks(verbose)
|
||||||
|
|
||||||
return self.Transport[TaskName] and self.Transport[TaskName].Task
|
return self.Transport[TaskName] and self.Transport[TaskName].Task
|
||||||
end
|
end
|
||||||
@@ -785,10 +788,11 @@ do -- TASK_CARGO_DISPATCHER
|
|||||||
|
|
||||||
--- Assigns tasks to the @{Core.Set#SET_GROUP}.
|
--- Assigns tasks to the @{Core.Set#SET_GROUP}.
|
||||||
-- @param #TASK_CARGO_DISPATCHER self
|
-- @param #TASK_CARGO_DISPATCHER self
|
||||||
|
-- @param #boolean Silent Announce new task (nil/false) or not (true).
|
||||||
-- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop.
|
-- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop.
|
||||||
function TASK_CARGO_DISPATCHER:ManageTasks()
|
function TASK_CARGO_DISPATCHER:ManageTasks(Silent)
|
||||||
self:F()
|
self:F()
|
||||||
|
local verbose = Silent and true
|
||||||
local AreaMsg = {}
|
local AreaMsg = {}
|
||||||
local TaskMsg = {}
|
local TaskMsg = {}
|
||||||
local ChangeMsg = {}
|
local ChangeMsg = {}
|
||||||
@@ -897,7 +901,7 @@ do -- TASK_CARGO_DISPATCHER
|
|||||||
local TaskText = TaskReport:Text(", ")
|
local TaskText = TaskReport:Text(", ")
|
||||||
|
|
||||||
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
|
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
|
||||||
if ( not Mission:IsGroupAssigned(TaskGroup) ) and TaskText ~= "" then
|
if ( not Mission:IsGroupAssigned(TaskGroup) ) and TaskText ~= "" and not verbose then
|
||||||
Mission:GetCommandCenter():MessageToGroup( string.format( "%s has tasks %s. Subscribe to a task using the radio menu.", Mission:GetShortText(), TaskText ), TaskGroup )
|
Mission:GetCommandCenter():MessageToGroup( string.format( "%s has tasks %s. Subscribe to a task using the radio menu.", Mission:GetShortText(), TaskText ), TaskGroup )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
--
|
--
|
||||||
-- **Specific features:**
|
-- **Specific features:**
|
||||||
--
|
--
|
||||||
-- * Creates a task to transport @{Cargo.Cargo} to and between deployment zones.
|
-- * Creates a task to transport #Cargo.Cargo to and between deployment zones.
|
||||||
-- * Derived from the TASK_CARGO class, which is derived from the TASK class.
|
-- * Derived from the TASK_CARGO class, which is derived from the TASK class.
|
||||||
-- * Orchestrate the task flow, so go from Planned to Assigned to Success, Failed or Cancelled.
|
-- * Orchestrate the task flow, so go from Planned to Assigned to Success, Failed or Cancelled.
|
||||||
-- * Co-operation tasking, so a player joins a group of players executing the same task.
|
-- * Co-operation tasking, so a player joins a group of players executing the same task.
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- Please read through the @{Tasking.Task_Cargo} process to understand the mechanisms of tasking and cargo tasking and handling.
|
-- Please read through the #Tasking.Task_Cargo process to understand the mechanisms of tasking and cargo tasking and handling.
|
||||||
--
|
--
|
||||||
-- Enjoy!
|
-- Enjoy!
|
||||||
-- FC
|
-- FC
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
|
|
||||||
do -- TASK_CARGO_TRANSPORT
|
do -- TASK_CARGO_TRANSPORT
|
||||||
|
|
||||||
--- @type TASK_CARGO_TRANSPORT
|
-- @type TASK_CARGO_TRANSPORT
|
||||||
-- @extends Tasking.Task_CARGO#TASK_CARGO
|
-- @extends Tasking.Task_CARGO#TASK_CARGO
|
||||||
|
|
||||||
--- Orchestrates the task for players to transport cargo to or between deployment zones.
|
--- Orchestrates the task for players to transport cargo to or between deployment zones.
|
||||||
@@ -76,7 +76,7 @@ do -- TASK_CARGO_TRANSPORT
|
|||||||
--
|
--
|
||||||
-- ## 1.1) Create a command center.
|
-- ## 1.1) Create a command center.
|
||||||
--
|
--
|
||||||
-- First you need to create a command center using the @{Tasking.CommandCenter#COMMANDCENTER.New}() constructor.
|
-- First you need to create a command center using the Tasking.CommandCenter#COMMANDCENTER.New constructor.
|
||||||
--
|
--
|
||||||
-- local CommandCenter = COMMANDCENTER
|
-- local CommandCenter = COMMANDCENTER
|
||||||
-- :New( HQ, "Lima" ) -- Create the CommandCenter.
|
-- :New( HQ, "Lima" ) -- Create the CommandCenter.
|
||||||
@@ -85,7 +85,7 @@ do -- TASK_CARGO_TRANSPORT
|
|||||||
--
|
--
|
||||||
-- Tasks work in a mission, which groups these tasks to achieve a joint mission goal.
|
-- Tasks work in a mission, which groups these tasks to achieve a joint mission goal.
|
||||||
-- A command center can govern multiple missions.
|
-- A command center can govern multiple missions.
|
||||||
-- Create a new mission, using the @{Tasking.Mission#MISSION.New}() constructor.
|
-- Create a new mission, using the Tasking.Mission#MISSION.New constructor.
|
||||||
--
|
--
|
||||||
-- -- Declare the Mission for the Command Center.
|
-- -- Declare the Mission for the Command Center.
|
||||||
-- local Mission = MISSION
|
-- local Mission = MISSION
|
||||||
@@ -99,7 +99,7 @@ do -- TASK_CARGO_TRANSPORT
|
|||||||
-- ## 1.3) Create the transport cargo task.
|
-- ## 1.3) Create the transport cargo task.
|
||||||
--
|
--
|
||||||
-- So, now that we have a command center and a mission, we now create the transport task.
|
-- So, now that we have a command center and a mission, we now create the transport task.
|
||||||
-- We create the transport task using the @{#TASK_CARGO_TRANSPORT.New}() constructor.
|
-- We create the transport task using the #TASK_CARGO_TRANSPORT.New constructor.
|
||||||
--
|
--
|
||||||
-- Because a transport task will not generate the cargo itself, you'll need to create it first.
|
-- Because a transport task will not generate the cargo itself, you'll need to create it first.
|
||||||
-- The cargo in this case will be the downed pilot!
|
-- The cargo in this case will be the downed pilot!
|
||||||
@@ -118,7 +118,7 @@ do -- TASK_CARGO_TRANSPORT
|
|||||||
-- -- The cargoset "CargoSet" will embed all defined cargo of type "Pilots" (prefix) into its set.
|
-- -- The cargoset "CargoSet" will embed all defined cargo of type "Pilots" (prefix) into its set.
|
||||||
-- local CargoGroup = CARGO_GROUP:New( PilotGroup, "Cargo", "Engineer Team 1", 500 )
|
-- local CargoGroup = CARGO_GROUP:New( PilotGroup, "Cargo", "Engineer Team 1", 500 )
|
||||||
--
|
--
|
||||||
-- What is also needed, is to have a set of @{Core.Group}s defined that contains the clients of the players.
|
-- What is also needed, is to have a set of Core.Groups defined that contains the clients of the players.
|
||||||
--
|
--
|
||||||
-- -- Allocate the Transport, which are the helicopter to retrieve the pilot, that can be manned by players.
|
-- -- Allocate the Transport, which are the helicopter to retrieve the pilot, that can be manned by players.
|
||||||
-- local GroupSet = SET_GROUP:New():FilterPrefixes( "Transport" ):FilterStart()
|
-- local GroupSet = SET_GROUP:New():FilterPrefixes( "Transport" ):FilterStart()
|
||||||
@@ -139,48 +139,48 @@ do -- TASK_CARGO_TRANSPORT
|
|||||||
-- By doing this, cargo transport tasking will become a dynamic experience.
|
-- By doing this, cargo transport tasking will become a dynamic experience.
|
||||||
--
|
--
|
||||||
--
|
--
|
||||||
-- # 2) Create a task using the @{Tasking.Task_Cargo_Dispatcher} module.
|
-- # 2) Create a task using the Tasking.Task_Cargo_Dispatcher module.
|
||||||
--
|
--
|
||||||
-- Actually, it is better to **GENERATE** these tasks using the @{Tasking.Task_Cargo_Dispatcher} module.
|
-- Actually, it is better to **GENERATE** these tasks using the Tasking.Task_Cargo_Dispatcher module.
|
||||||
-- Using the dispatcher module, transport tasks can be created much more easy.
|
-- Using the dispatcher module, transport tasks can be created easier.
|
||||||
--
|
--
|
||||||
-- Find below an example how to use the TASK_CARGO_DISPATCHER class:
|
-- Find below an example how to use the TASK_CARGO_DISPATCHER class:
|
||||||
--
|
--
|
||||||
--
|
--
|
||||||
-- -- Find the HQ group.
|
-- -- Find the HQ group.
|
||||||
-- HQ = GROUP:FindByName( "HQ", "Bravo" )
|
-- HQ = GROUP:FindByName( "HQ", "Bravo" )
|
||||||
--
|
--
|
||||||
-- -- Create the command center with the name "Lima".
|
-- -- Create the command center with the name "Lima".
|
||||||
-- CommandCenter = COMMANDCENTER
|
-- CommandCenter = COMMANDCENTER
|
||||||
-- :New( HQ, "Lima" )
|
-- :New( HQ, "Lima" )
|
||||||
--
|
--
|
||||||
-- -- Create the mission, for the command center, with the name "Operation Cargo Fun", a "Tactical" mission, with the mission briefing "Transport Cargo", for the BLUE coalition.
|
-- -- Create the mission, for the command center, with the name "Operation Cargo Fun", a "Tactical" mission, with the mission briefing "Transport Cargo", for the BLUE coalition.
|
||||||
-- Mission = MISSION
|
-- Mission = MISSION
|
||||||
-- :New( CommandCenter, "Operation Cargo Fun", "Tactical", "Transport Cargo", coalition.side.BLUE )
|
-- :New( CommandCenter, "Operation Cargo Fun", "Tactical", "Transport Cargo", coalition.side.BLUE )
|
||||||
--
|
--
|
||||||
-- -- Create the SET of GROUPs containing clients (players) that will transport the cargo.
|
-- -- Create the SET of GROUPs containing clients (players) that will transport the cargo.
|
||||||
-- -- These are have a name that start with "Transport" and are of the "blue" coalition.
|
-- -- These are have a name that start with "Transport" and are of the "blue" coalition.
|
||||||
-- TransportGroups = SET_GROUP:New():FilterCoalitions( "blue" ):FilterPrefixes( "Transport" ):FilterStart()
|
-- TransportGroups = SET_GROUP:New():FilterCoalitions( "blue" ):FilterPrefixes( "Transport" ):FilterStart()
|
||||||
--
|
--
|
||||||
--
|
--
|
||||||
-- -- Here we create the TASK_CARGO_DISPATCHER object! This is where we assign the dispatcher to generate tasks in the Mission for the TransportGroups.
|
-- -- Here we create the TASK_CARGO_DISPATCHER object! This is where we assign the dispatcher to generate tasks in the Mission for the TransportGroups.
|
||||||
-- TaskDispatcher = TASK_CARGO_DISPATCHER:New( Mission, TransportGroups )
|
-- TaskDispatcher = TASK_CARGO_DISPATCHER:New( Mission, TransportGroups )
|
||||||
--
|
--
|
||||||
--
|
--
|
||||||
-- -- Here we declare the SET of CARGOs called "Workmaterials".
|
-- -- Here we declare the SET of CARGOs called "Workmaterials".
|
||||||
-- local CargoSetWorkmaterials = SET_CARGO:New():FilterTypes( "Workmaterials" ):FilterStart()
|
-- local CargoSetWorkmaterials = SET_CARGO:New():FilterTypes( "Workmaterials" ):FilterStart()
|
||||||
--
|
--
|
||||||
-- -- Here we declare (add) CARGO_GROUP objects of various types, that are filtered and added in the CargoSetworkmaterials cargo set.
|
-- -- Here we declare (add) CARGO_GROUP objects of various types, that are filtered and added in the CargoSetworkmaterials cargo set.
|
||||||
-- -- These cargo objects have the type "Workmaterials" which is exactly the type of cargo the CargoSetworkmaterials is filtering on.
|
-- -- These cargo objects have the type "Workmaterials" which is exactly the type of cargo the CargoSetworkmaterials is filtering on.
|
||||||
-- local EngineerCargoGroup = CARGO_GROUP:New( GROUP:FindByName( "Engineers" ), "Workmaterials", "Engineers", 250 )
|
-- local EngineerCargoGroup = CARGO_GROUP:New( GROUP:FindByName( "Engineers" ), "Workmaterials", "Engineers", 250 )
|
||||||
-- local ConcreteCargo = CARGO_SLINGLOAD:New( STATIC:FindByName( "Concrete" ), "Workmaterials", "Concrete", 150, 50 )
|
-- local ConcreteCargo = CARGO_SLINGLOAD:New( STATIC:FindByName( "Concrete" ), "Workmaterials", "Concrete", 150, 50 )
|
||||||
-- local CrateCargo = CARGO_CRATE:New( STATIC:FindByName( "Crate" ), "Workmaterials", "Crate", 150, 50 )
|
-- local CrateCargo = CARGO_CRATE:New( STATIC:FindByName( "Crate" ), "Workmaterials", "Crate", 150, 50 )
|
||||||
-- local EnginesCargo = CARGO_CRATE:New( STATIC:FindByName( "Engines" ), "Workmaterials", "Engines", 150, 50 )
|
-- local EnginesCargo = CARGO_CRATE:New( STATIC:FindByName( "Engines" ), "Workmaterials", "Engines", 150, 50 )
|
||||||
-- local MetalCargo = CARGO_CRATE:New( STATIC:FindByName( "Metal" ), "Workmaterials", "Metal", 150, 50 )
|
-- local MetalCargo = CARGO_CRATE:New( STATIC:FindByName( "Metal" ), "Workmaterials", "Metal", 150, 50 )
|
||||||
--
|
--
|
||||||
-- -- And here we create a new WorkplaceTask, using the :AddTransportTask method of the TaskDispatcher.
|
-- -- And here we create a new WorkplaceTask, using the :AddTransportTask method of the TaskDispatcher.
|
||||||
-- local WorkplaceTask = TaskDispatcher:AddTransportTask( "Build a Workplace", CargoSetWorkmaterials, "Transport the workers, engineers and the equipment near the Workplace." )
|
-- local WorkplaceTask = TaskDispatcher:AddTransportTask( "Build a Workplace", CargoSetWorkmaterials, "Transport the workers, engineers and the equipment near the Workplace." )
|
||||||
-- TaskDispatcher:SetTransportDeployZone( WorkplaceTask, ZONE:New( "Workplace" ) )
|
-- TaskDispatcher:SetTransportDeployZone( WorkplaceTask, ZONE:New( "Workplace" ) )
|
||||||
--
|
--
|
||||||
-- # 3) Handle cargo task events.
|
-- # 3) Handle cargo task events.
|
||||||
--
|
--
|
||||||
@@ -189,7 +189,7 @@ do -- TASK_CARGO_TRANSPORT
|
|||||||
-- In order to properly capture the events and avoid mistakes using the documentation, it is advised that you execute the following actions:
|
-- In order to properly capture the events and avoid mistakes using the documentation, it is advised that you execute the following actions:
|
||||||
--
|
--
|
||||||
-- * **Copy / Paste** the code section into your script.
|
-- * **Copy / Paste** the code section into your script.
|
||||||
-- * **Change** the CLASS literal to the task object name you have in your script.
|
-- * **Change** the "myclass" literal to the task object name you have in your script.
|
||||||
-- * Within the function, you can now **write your own code**!
|
-- * Within the function, you can now **write your own code**!
|
||||||
-- * **IntelliSense** will recognize the type of the variables provided by the function. Note: the From, Event and To variables can be safely ignored,
|
-- * **IntelliSense** will recognize the type of the variables provided by the function. Note: the From, Event and To variables can be safely ignored,
|
||||||
-- but you need to declare them as they are automatically provided by the event handling system of MOOSE.
|
-- but you need to declare them as they are automatically provided by the event handling system of MOOSE.
|
||||||
@@ -210,14 +210,13 @@ do -- TASK_CARGO_TRANSPORT
|
|||||||
-- If you want to code your own event handler, use this code fragment to tailor the event when a player carrier has picked up a cargo object in the CarrierGroup.
|
-- If you want to code your own event handler, use this code fragment to tailor the event when a player carrier has picked up a cargo object in the CarrierGroup.
|
||||||
-- You can use this event handler to post messages to players, or provide status updates etc.
|
-- You can use this event handler to post messages to players, or provide status updates etc.
|
||||||
--
|
--
|
||||||
-- --- CargoPickedUp event handler OnAfter for CLASS.
|
-- --- CargoPickedUp event handler OnAfter for "myclass".
|
||||||
-- -- @param #CLASS self
|
|
||||||
-- -- @param #string From A string that contains the "*from state name*" when the event was triggered.
|
-- -- @param #string From A string that contains the "*from state name*" when the event was triggered.
|
||||||
-- -- @param #string Event A string that contains the "*event name*" when the event was triggered.
|
-- -- @param #string Event A string that contains the "*event name*" when the event was triggered.
|
||||||
-- -- @param #string To A string that contains the "*to state name*" when the event was triggered.
|
-- -- @param #string To A string that contains the "*to state name*" when the event was triggered.
|
||||||
-- -- @param Wrapper.Unit#UNIT TaskUnit The unit (client) of the player that has picked up the cargo.
|
-- -- @param Wrapper.Unit#UNIT TaskUnit The unit (client) of the player that has picked up the cargo.
|
||||||
-- -- @param Cargo.Cargo#CARGO Cargo The cargo object that has been picked up. Note that this can be a CARGO_GROUP, CARGO_CRATE or CARGO_SLINGLOAD object!
|
-- -- @param Cargo.Cargo#CARGO Cargo The cargo object that has been picked up. Note that this can be a CARGO_GROUP, CARGO_CRATE or CARGO_SLINGLOAD object!
|
||||||
-- function CLASS:OnAfterCargoPickedUp( From, Event, To, TaskUnit, Cargo )
|
-- function myclass:OnAfterCargoPickedUp( From, Event, To, TaskUnit, Cargo )
|
||||||
--
|
--
|
||||||
-- -- Write here your own code.
|
-- -- Write here your own code.
|
||||||
--
|
--
|
||||||
@@ -240,15 +239,14 @@ do -- TASK_CARGO_TRANSPORT
|
|||||||
-- You can use this event handler to post messages to players, or provide status updates etc.
|
-- You can use this event handler to post messages to players, or provide status updates etc.
|
||||||
--
|
--
|
||||||
--
|
--
|
||||||
-- --- CargoDeployed event handler OnAfter for CLASS.
|
-- --- CargoDeployed event handler OnAfter foR "myclass".
|
||||||
-- -- @param #CLASS self
|
|
||||||
-- -- @param #string From A string that contains the "*from state name*" when the event was triggered.
|
-- -- @param #string From A string that contains the "*from state name*" when the event was triggered.
|
||||||
-- -- @param #string Event A string that contains the "*event name*" when the event was triggered.
|
-- -- @param #string Event A string that contains the "*event name*" when the event was triggered.
|
||||||
-- -- @param #string To A string that contains the "*to state name*" when the event was triggered.
|
-- -- @param #string To A string that contains the "*to state name*" when the event was triggered.
|
||||||
-- -- @param Wrapper.Unit#UNIT TaskUnit The unit (client) of the player that has deployed the cargo.
|
-- -- @param Wrapper.Unit#UNIT TaskUnit The unit (client) of the player that has deployed the cargo.
|
||||||
-- -- @param Cargo.Cargo#CARGO Cargo The cargo object that has been deployed. Note that this can be a CARGO_GROUP, CARGO_CRATE or CARGO_SLINGLOAD object!
|
-- -- @param Cargo.Cargo#CARGO Cargo The cargo object that has been deployed. Note that this can be a CARGO_GROUP, CARGO_CRATE or CARGO_SLINGLOAD object!
|
||||||
-- -- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
|
-- -- @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 CLASS:OnAfterCargoDeployed( From, Event, To, TaskUnit, Cargo, DeployZone )
|
-- function myclass:OnAfterCargoDeployed( From, Event, To, TaskUnit, Cargo, DeployZone )
|
||||||
--
|
--
|
||||||
-- -- Write here your own code.
|
-- -- Write here your own code.
|
||||||
--
|
--
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
--
|
--
|
||||||
-- DCS itself provides a lot of enumerators for various things. See [Enumerators](https://wiki.hoggitworld.com/view/Category:Enumerators) on Hoggit.
|
-- DCS itself provides a lot of enumerators for various things. See [Enumerators](https://wiki.hoggitworld.com/view/Category:Enumerators) on Hoggit.
|
||||||
--
|
--
|
||||||
-- Other Moose classe also have enumerators. For example, the AIRBASE class has enumerators for airbase names.
|
-- Other Moose classes also have enumerators. For example, the AIRBASE class has enumerators for airbase names.
|
||||||
--
|
--
|
||||||
-- @module ENUMS
|
-- @module ENUMS
|
||||||
-- @image MOOSE.JPG
|
-- @image MOOSE.JPG
|
||||||
@@ -126,6 +126,8 @@ ENUMS.WeaponFlag={
|
|||||||
AnyMissile = 268402688, -- AnyASM + AnyAAM
|
AnyMissile = 268402688, -- AnyASM + AnyAAM
|
||||||
--- Guns
|
--- Guns
|
||||||
Cannons = 805306368, -- GUN_POD + BuiltInCannon
|
Cannons = 805306368, -- GUN_POD + BuiltInCannon
|
||||||
|
--- Torpedo
|
||||||
|
Torpedo = 4294967296,
|
||||||
---
|
---
|
||||||
-- Even More Genral
|
-- Even More Genral
|
||||||
Auto = 3221225470, -- Any Weapon (AnyBomb + AnyRocket + AnyMissile + Cannons)
|
Auto = 3221225470, -- Any Weapon (AnyBomb + AnyRocket + AnyMissile + Cannons)
|
||||||
@@ -136,6 +138,93 @@ ENUMS.WeaponFlag={
|
|||||||
AnyGuided = 268402702, -- Any Guided Weapon
|
AnyGuided = 268402702, -- Any Guided Weapon
|
||||||
}
|
}
|
||||||
|
|
||||||
|
--- Weapon types by category. See the [Weapon Flag](https://wiki.hoggitworld.com/view/DCS_enum_weapon_flag) enumerator on hoggit wiki.
|
||||||
|
-- @type ENUMS.WeaponType
|
||||||
|
-- @field #table Bomb Bombs.
|
||||||
|
-- @field #table Rocket Rocket.
|
||||||
|
-- @field #table Gun Guns.
|
||||||
|
-- @field #table Missile Missiles.
|
||||||
|
-- @field #table AAM Air-to-Air missiles.
|
||||||
|
-- @field #table Torpedo Torpedos.
|
||||||
|
-- @field #table Any Combinations.
|
||||||
|
ENUMS.WeaponType={}
|
||||||
|
ENUMS.WeaponType.Bomb={
|
||||||
|
-- Bombs
|
||||||
|
LGB = 2,
|
||||||
|
TvGB = 4,
|
||||||
|
SNSGB = 8,
|
||||||
|
HEBomb = 16,
|
||||||
|
Penetrator = 32,
|
||||||
|
NapalmBomb = 64,
|
||||||
|
FAEBomb = 128,
|
||||||
|
ClusterBomb = 256,
|
||||||
|
Dispencer = 512,
|
||||||
|
CandleBomb = 1024,
|
||||||
|
ParachuteBomb = 2147483648,
|
||||||
|
-- Combinations
|
||||||
|
GuidedBomb = 14, -- (LGB + TvGB + SNSGB)
|
||||||
|
AnyUnguidedBomb = 2147485680, -- (HeBomb + Penetrator + NapalmBomb + FAEBomb + ClusterBomb + Dispencer + CandleBomb + ParachuteBomb)
|
||||||
|
AnyBomb = 2147485694, -- (GuidedBomb + AnyUnguidedBomb)
|
||||||
|
}
|
||||||
|
ENUMS.WeaponType.Rocket={
|
||||||
|
-- Rockets
|
||||||
|
LightRocket = 2048,
|
||||||
|
MarkerRocket = 4096,
|
||||||
|
CandleRocket = 8192,
|
||||||
|
HeavyRocket = 16384,
|
||||||
|
-- Combinations
|
||||||
|
AnyRocket = 30720, -- LightRocket + MarkerRocket + CandleRocket + HeavyRocket
|
||||||
|
}
|
||||||
|
ENUMS.WeaponType.Gun={
|
||||||
|
-- Guns
|
||||||
|
GunPod = 268435456,
|
||||||
|
BuiltInCannon = 536870912,
|
||||||
|
-- Combinations
|
||||||
|
Cannons = 805306368, -- GUN_POD + BuiltInCannon
|
||||||
|
}
|
||||||
|
ENUMS.WeaponType.Missile={
|
||||||
|
-- Missiles
|
||||||
|
AntiRadarMissile = 32768,
|
||||||
|
AntiShipMissile = 65536,
|
||||||
|
AntiTankMissile = 131072,
|
||||||
|
FireAndForgetASM = 262144,
|
||||||
|
LaserASM = 524288,
|
||||||
|
TeleASM = 1048576,
|
||||||
|
CruiseMissile = 2097152,
|
||||||
|
AntiRadarMissile2 = 1073741824,
|
||||||
|
-- Combinations
|
||||||
|
GuidedASM = 1572864, -- (LaserASM + TeleASM)
|
||||||
|
TacticalASM = 1835008, -- (GuidedASM + FireAndForgetASM)
|
||||||
|
AnyASM = 4161536, -- (AntiRadarMissile + AntiShipMissile + AntiTankMissile + FireAndForgetASM + GuidedASM + CruiseMissile)
|
||||||
|
AnyASM2 = 1077903360, -- 4161536+1073741824,
|
||||||
|
AnyAutonomousMissile = 36012032, -- IR_AAM + AntiRadarMissile + AntiShipMissile + FireAndForgetASM + CruiseMissile
|
||||||
|
AnyMissile = 268402688, -- AnyASM + AnyAAM
|
||||||
|
}
|
||||||
|
ENUMS.WeaponType.AAM={
|
||||||
|
-- Air-To-Air Missiles
|
||||||
|
SRAM = 4194304,
|
||||||
|
MRAAM = 8388608,
|
||||||
|
LRAAM = 16777216,
|
||||||
|
IR_AAM = 33554432,
|
||||||
|
SAR_AAM = 67108864,
|
||||||
|
AR_AAM = 134217728,
|
||||||
|
-- Combinations
|
||||||
|
AnyAAM = 264241152, -- IR_AAM + SAR_AAM + AR_AAM + SRAAM + MRAAM + LRAAM
|
||||||
|
}
|
||||||
|
ENUMS.WeaponType.Torpedo={
|
||||||
|
-- Torpedo
|
||||||
|
Torpedo = 4294967296,
|
||||||
|
}
|
||||||
|
ENUMS.WeaponType.Any={
|
||||||
|
-- General combinations
|
||||||
|
Weapon = 3221225470, -- Any Weapon (AnyBomb + AnyRocket + AnyMissile + Cannons)
|
||||||
|
AG = 2956984318, -- Any Air-To-Ground Weapon
|
||||||
|
AA = 264241152, -- Any Air-To-Air Weapon
|
||||||
|
Unguided = 2952822768, -- Any Unguided Weapon
|
||||||
|
Guided = 268402702, -- Any Guided Weapon
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
--- Mission tasks.
|
--- Mission tasks.
|
||||||
-- @type ENUMS.MissionTask
|
-- @type ENUMS.MissionTask
|
||||||
-- @field #string NOTHING No special task. Group can perform the minimal tasks: Orbit, Refuelling, Follow and Aerobatics.
|
-- @field #string NOTHING No special task. Group can perform the minimal tasks: Orbit, Refuelling, Follow and Aerobatics.
|
||||||
@@ -359,4 +448,114 @@ ENUMS.Phonetic =
|
|||||||
X = 'Xray',
|
X = 'Xray',
|
||||||
Y = 'Yankee',
|
Y = 'Yankee',
|
||||||
Z = 'Zulu',
|
Z = 'Zulu',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
--- Reporting Names (NATO). See the [Wikipedia](https://en.wikipedia.org/wiki/List_of_NATO_reporting_names_for_fighter_aircraft).
|
||||||
|
-- DCS known aircraft types
|
||||||
|
--
|
||||||
|
-- @type ENUMS.ReportingName
|
||||||
|
ENUMS.ReportingName =
|
||||||
|
{
|
||||||
|
NATO = {
|
||||||
|
-- Fighters
|
||||||
|
Dragon = "JF-17", -- China, correctly Fierce Dragon, Thunder for PAC
|
||||||
|
Fagot = "MiG-15",
|
||||||
|
Farmer = "MiG-19", -- Shenyang J-6 and Mikoyan-Gurevich MiG-19
|
||||||
|
Felon = "Su-57",
|
||||||
|
Fencer = "Su-24",
|
||||||
|
Fishbed = "MiG-21",
|
||||||
|
Fitter = "Su-17", -- Sukhoi Su-7 and Su-17/Su-20/Su-22
|
||||||
|
Flogger = "MiG-23", --and MiG-27
|
||||||
|
Flogger_D = "MiG-27", --and MiG-23
|
||||||
|
Flagon = "Su-15",
|
||||||
|
Foxbat = "MiG-25",
|
||||||
|
Fulcrum = "MiG-29",
|
||||||
|
Foxhound = "MiG-31",
|
||||||
|
Flanker = "Su-27", -- Sukhoi Su-27/Su-30/Su-33/Su-35/Su-37 and Shenyang J-11/J-15/J-16
|
||||||
|
Flanker_C = "Su-30",
|
||||||
|
Flanker_E = "Su-35",
|
||||||
|
Flanker_F = "Su-37",
|
||||||
|
Flanker_L = "J-11A",
|
||||||
|
Firebird = "J-10",
|
||||||
|
Sea_Flanker = "Su-33",
|
||||||
|
Fullback = "Su-34", -- also Su-32
|
||||||
|
Frogfoot = "Su-25",
|
||||||
|
Tomcat = "F-14", -- Iran
|
||||||
|
Mirage = "Mirage", -- various non-NATO
|
||||||
|
Codling = "Yak-40",
|
||||||
|
Maya = "L-39",
|
||||||
|
-- Fighters US/NATO
|
||||||
|
Warthog = "A-10",
|
||||||
|
--Mosquito = "A-20",
|
||||||
|
Skyhawk = "A-4E",
|
||||||
|
Viggen = "AJS37",
|
||||||
|
Harrier = "AV-8B",
|
||||||
|
Spirit = "B-2",
|
||||||
|
Aviojet = "C-101",
|
||||||
|
Nighthawk = "F-117A",
|
||||||
|
Eagle = "F-15C",
|
||||||
|
Mudhen = "F-15E",
|
||||||
|
Viper = "F-16",
|
||||||
|
Phantom = "F-4E",
|
||||||
|
Tiger = "F-5", -- was thinkg to name this MiG-25 ;)
|
||||||
|
Sabre = "F-86",
|
||||||
|
Hornet = "A-18", -- avoiding the slash
|
||||||
|
Hawk = "Hawk",
|
||||||
|
Albatros = "L-39",
|
||||||
|
Goshawk = "T-45",
|
||||||
|
Starfighter = "F-104",
|
||||||
|
Tornado = "Tornado",
|
||||||
|
-- Transport / Bomber / Others
|
||||||
|
Atlas = "A400",
|
||||||
|
Lancer = "B1-B",
|
||||||
|
Stratofortress = "B-52H",
|
||||||
|
Hercules = "C-130",
|
||||||
|
Super_Hercules = "Hercules",
|
||||||
|
Globemaster = "C-17",
|
||||||
|
Greyhound = "C-2A",
|
||||||
|
Galaxy = "C-5",
|
||||||
|
Hawkeye = "E-2D",
|
||||||
|
Sentry = "E-3A",
|
||||||
|
Stratotanker = "KC-135",
|
||||||
|
Extender = "KC-10",
|
||||||
|
Orion = "P-3C",
|
||||||
|
Viking = "S-3B",
|
||||||
|
Osprey = "V-22",
|
||||||
|
-- Bomber Rus
|
||||||
|
Badger = "H6-J",
|
||||||
|
Bear_J = "Tu-142", -- also Tu-95
|
||||||
|
Bear = "Tu-95", -- also Tu-142
|
||||||
|
Blinder = "Tu-22",
|
||||||
|
Blackjack = "Tu-160",
|
||||||
|
-- AIC / Transport / Other
|
||||||
|
Clank = "An-30",
|
||||||
|
Curl = "An-26",
|
||||||
|
Candid = "IL-76",
|
||||||
|
Midas = "IL-78",
|
||||||
|
Mainstay = "A-50",
|
||||||
|
Mainring = "KJ-2000", -- A-50 China
|
||||||
|
Yak = "Yak-52",
|
||||||
|
-- Helos
|
||||||
|
Helix = "Ka-27",
|
||||||
|
Shark = "Ka-50",
|
||||||
|
Hind = "Mi-24",
|
||||||
|
Halo = "Mi-26",
|
||||||
|
Hip = "Mi-8",
|
||||||
|
Havoc = "Mi-28",
|
||||||
|
Gazelle = "SA342",
|
||||||
|
-- Helos US
|
||||||
|
Huey = "UH-1H",
|
||||||
|
Cobra = "AH-1",
|
||||||
|
Apache = "AH-64",
|
||||||
|
Chinook = "CH-47",
|
||||||
|
Sea_Stallion = "CH-53",
|
||||||
|
Kiowa = "OH-58",
|
||||||
|
Seahawk = "SH-60",
|
||||||
|
Blackhawk = "UH-60",
|
||||||
|
Sea_King = "S-61",
|
||||||
|
-- Drones
|
||||||
|
UCAV = "WingLoong",
|
||||||
|
Reaper = "MQ-9",
|
||||||
|
Predator = "MQ-1A",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
773
Moose Development/Moose/Utilities/FiFo.lua
Normal file
773
Moose Development/Moose/Utilities/FiFo.lua
Normal file
@@ -0,0 +1,773 @@
|
|||||||
|
--- **UTILS** - ClassicFiFo Stack.
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ## Main Features:
|
||||||
|
--
|
||||||
|
-- * Build a simple multi-purpose FiFo (First-In, First-Out) stack for generic data.
|
||||||
|
-- * [Wikipedia](https://en.wikipedia.org/wiki/FIFO_(computing_and_electronics)
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ### Author: **applevangelist**
|
||||||
|
-- @module Utils.FiFo
|
||||||
|
-- @image MOOSE.JPG
|
||||||
|
|
||||||
|
-- Date: April 2022
|
||||||
|
|
||||||
|
do
|
||||||
|
--- FIFO class.
|
||||||
|
-- @type FIFO
|
||||||
|
-- @field #string ClassName Name of the class.
|
||||||
|
-- @field #string lid Class id string for output to DCS log file.
|
||||||
|
-- @field #string version Version of FiFo.
|
||||||
|
-- @field #number counter Counter.
|
||||||
|
-- @field #number pointer Pointer.
|
||||||
|
-- @field #table stackbypointer Stack by pointer.
|
||||||
|
-- @field #table stackbyid Stack by ID.
|
||||||
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
|
---
|
||||||
|
-- @type FIFO.IDEntry
|
||||||
|
-- @field #number pointer
|
||||||
|
-- @field #table data
|
||||||
|
-- @field #table uniqueID
|
||||||
|
|
||||||
|
---
|
||||||
|
-- @field #FIFO
|
||||||
|
FIFO = {
|
||||||
|
ClassName = "FIFO",
|
||||||
|
lid = "",
|
||||||
|
version = "0.0.5",
|
||||||
|
counter = 0,
|
||||||
|
pointer = 0,
|
||||||
|
stackbypointer = {},
|
||||||
|
stackbyid = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Instantiate a new FIFO Stack.
|
||||||
|
-- @param #FIFO self
|
||||||
|
-- @return #FIFO self
|
||||||
|
function FIFO:New()
|
||||||
|
-- Inherit everything from BASE class.
|
||||||
|
local self=BASE:Inherit(self, BASE:New()) --#FIFO
|
||||||
|
self.pointer = 0
|
||||||
|
self.counter = 0
|
||||||
|
self.stackbypointer = {}
|
||||||
|
self.stackbyid = {}
|
||||||
|
self.uniquecounter = 0
|
||||||
|
-- Set some string id for output to DCS.log file.
|
||||||
|
self.lid=string.format("%s (%s) | ", "FiFo", self.version)
|
||||||
|
self:T(self.lid .."Created.")
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Empty FIFO Stack.
|
||||||
|
-- @param #FIFO self
|
||||||
|
-- @return #FIFO self
|
||||||
|
function FIFO:Clear()
|
||||||
|
self:T(self.lid.."Clear")
|
||||||
|
self.pointer = 0
|
||||||
|
self.counter = 0
|
||||||
|
self.stackbypointer = nil
|
||||||
|
self.stackbyid = nil
|
||||||
|
self.stackbypointer = {}
|
||||||
|
self.stackbyid = {}
|
||||||
|
self.uniquecounter = 0
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- FIFO Push Object to Stack.
|
||||||
|
-- @param #FIFO self
|
||||||
|
-- @param #table Object
|
||||||
|
-- @param #string UniqueID (optional) - will default to current pointer + 1. Note - if you intend to use `FIFO:GetIDStackSorted()` keep the UniqueID numerical!
|
||||||
|
-- @return #FIFO self
|
||||||
|
function FIFO:Push(Object,UniqueID)
|
||||||
|
self:T(self.lid.."Push")
|
||||||
|
self:T({Object,UniqueID})
|
||||||
|
self.pointer = self.pointer + 1
|
||||||
|
self.counter = self.counter + 1
|
||||||
|
local uniID = UniqueID
|
||||||
|
if not UniqueID then
|
||||||
|
self.uniquecounter = self.uniquecounter + 1
|
||||||
|
uniID = self.uniquecounter
|
||||||
|
end
|
||||||
|
self.stackbyid[uniID] = { pointer = self.pointer, data = Object, uniqueID = uniID }
|
||||||
|
self.stackbypointer[self.pointer] = { pointer = self.pointer, data = Object, uniqueID = uniID }
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- FIFO Pull Object from Stack.
|
||||||
|
-- @param #FIFO self
|
||||||
|
-- @return #table Object or nil if stack is empty
|
||||||
|
function FIFO:Pull()
|
||||||
|
self:T(self.lid.."Pull")
|
||||||
|
if self.counter == 0 then return nil end
|
||||||
|
--local object = self.stackbypointer[self.pointer].data
|
||||||
|
--self.stackbypointer[self.pointer] = nil
|
||||||
|
local object = self.stackbypointer[1].data
|
||||||
|
self.stackbypointer[1] = nil
|
||||||
|
self.counter = self.counter - 1
|
||||||
|
--self.pointer = self.pointer - 1
|
||||||
|
self:Flatten()
|
||||||
|
return object
|
||||||
|
end
|
||||||
|
|
||||||
|
--- FIFO Pull Object from Stack by Pointer
|
||||||
|
-- @param #FIFO self
|
||||||
|
-- @param #number Pointer
|
||||||
|
-- @return #table Object or nil if stack is empty
|
||||||
|
function FIFO:PullByPointer(Pointer)
|
||||||
|
self:T(self.lid.."PullByPointer " .. tostring(Pointer))
|
||||||
|
if self.counter == 0 then return nil end
|
||||||
|
local object = self.stackbypointer[Pointer] -- #FIFO.IDEntry
|
||||||
|
self.stackbypointer[Pointer] = nil
|
||||||
|
if object then self.stackbyid[object.uniqueID] = nil end
|
||||||
|
self.counter = self.counter - 1
|
||||||
|
self:Flatten()
|
||||||
|
if object then
|
||||||
|
return object.data
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- FIFO Read, not Pull, Object from Stack by Pointer
|
||||||
|
-- @param #FIFO self
|
||||||
|
-- @param #number Pointer
|
||||||
|
-- @return #table Object or nil if stack is empty or pointer does not exist
|
||||||
|
function FIFO:ReadByPointer(Pointer)
|
||||||
|
self:T(self.lid.."ReadByPointer " .. tostring(Pointer))
|
||||||
|
if self.counter == 0 or not Pointer or not self.stackbypointer[Pointer] then return nil end
|
||||||
|
local object = self.stackbypointer[Pointer] -- #FIFO.IDEntry
|
||||||
|
if object then
|
||||||
|
return object.data
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- FIFO Read, not Pull, Object from Stack by UniqueID
|
||||||
|
-- @param #FIFO self
|
||||||
|
-- @param #number UniqueID
|
||||||
|
-- @return #table Object data or nil if stack is empty or ID does not exist
|
||||||
|
function FIFO:ReadByID(UniqueID)
|
||||||
|
self:T(self.lid.."ReadByID " .. tostring(UniqueID))
|
||||||
|
if self.counter == 0 or not UniqueID or not self.stackbyid[UniqueID] then return nil end
|
||||||
|
local object = self.stackbyid[UniqueID] -- #FIFO.IDEntry
|
||||||
|
if object then
|
||||||
|
return object.data
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- FIFO Pull Object from Stack by UniqueID
|
||||||
|
-- @param #FIFO self
|
||||||
|
-- @param #tableUniqueID
|
||||||
|
-- @return #table Object or nil if stack is empty
|
||||||
|
function FIFO:PullByID(UniqueID)
|
||||||
|
self:T(self.lid.."PullByID " .. tostring(UniqueID))
|
||||||
|
if self.counter == 0 then return nil end
|
||||||
|
local object = self.stackbyid[UniqueID] -- #FIFO.IDEntry
|
||||||
|
--self.stackbyid[UniqueID] = nil
|
||||||
|
if object then
|
||||||
|
return self:PullByPointer(object.pointer)
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- FIFO Housekeeping
|
||||||
|
-- @param #FIFO self
|
||||||
|
-- @return #FIFO self
|
||||||
|
function FIFO:Flatten()
|
||||||
|
self:T(self.lid.."Flatten")
|
||||||
|
-- rebuild stacks
|
||||||
|
local pointerstack = {}
|
||||||
|
local idstack = {}
|
||||||
|
local counter = 0
|
||||||
|
for _ID,_entry in pairs(self.stackbypointer) do
|
||||||
|
counter = counter + 1
|
||||||
|
pointerstack[counter] = { pointer = counter, data = _entry.data, uniqueID = _entry.uniqueID}
|
||||||
|
end
|
||||||
|
for _ID,_entry in pairs(pointerstack) do
|
||||||
|
idstack[_entry.uniqueID] = { pointer = _entry.pointer , data = _entry.data, uniqueID = _entry.uniqueID}
|
||||||
|
end
|
||||||
|
self.stackbypointer = nil
|
||||||
|
self.stackbypointer = pointerstack
|
||||||
|
self.stackbyid = nil
|
||||||
|
self.stackbyid = idstack
|
||||||
|
self.counter = counter
|
||||||
|
self.pointer = counter
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- FIFO Check Stack is empty
|
||||||
|
-- @param #FIFO self
|
||||||
|
-- @return #boolean empty
|
||||||
|
function FIFO:IsEmpty()
|
||||||
|
self:T(self.lid.."IsEmpty")
|
||||||
|
return self.counter == 0 and true or false
|
||||||
|
end
|
||||||
|
|
||||||
|
--- FIFO Get stack size
|
||||||
|
-- @param #FIFO self
|
||||||
|
-- @return #number size
|
||||||
|
function FIFO:GetSize()
|
||||||
|
self:T(self.lid.."GetSize")
|
||||||
|
return self.counter
|
||||||
|
end
|
||||||
|
|
||||||
|
--- FIFO Get stack size
|
||||||
|
-- @param #FIFO self
|
||||||
|
-- @return #number size
|
||||||
|
function FIFO:Count()
|
||||||
|
self:T(self.lid.."Count")
|
||||||
|
return self.counter
|
||||||
|
end
|
||||||
|
|
||||||
|
--- FIFO Check Stack is NOT empty
|
||||||
|
-- @param #FIFO self
|
||||||
|
-- @return #boolean notempty
|
||||||
|
function FIFO:IsNotEmpty()
|
||||||
|
self:T(self.lid.."IsNotEmpty")
|
||||||
|
return not self:IsEmpty()
|
||||||
|
end
|
||||||
|
|
||||||
|
--- FIFO Get the data stack by pointer
|
||||||
|
-- @param #FIFO self
|
||||||
|
-- @return #table Table of #FIFO.IDEntry entries
|
||||||
|
function FIFO:GetPointerStack()
|
||||||
|
self:T(self.lid.."GetPointerStack")
|
||||||
|
return self.stackbypointer
|
||||||
|
end
|
||||||
|
|
||||||
|
--- FIFO Check if a certain UniqeID exists
|
||||||
|
-- @param #FIFO self
|
||||||
|
-- @return #boolean exists
|
||||||
|
function FIFO:HasUniqueID(UniqueID)
|
||||||
|
self:T(self.lid.."HasUniqueID")
|
||||||
|
if self.stackbyid[UniqueID] ~= nil then
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- FIFO Get the data stack by UniqueID
|
||||||
|
-- @param #FIFO self
|
||||||
|
-- @return #table Table of #FIFO.IDEntry entries
|
||||||
|
function FIFO:GetIDStack()
|
||||||
|
self:T(self.lid.."GetIDStack")
|
||||||
|
return self.stackbyid
|
||||||
|
end
|
||||||
|
|
||||||
|
--- FIFO Get table of UniqueIDs sorted smallest to largest
|
||||||
|
-- @param #FIFO self
|
||||||
|
-- @return #table Table with index [1] to [n] of UniqueID entries
|
||||||
|
function FIFO:GetIDStackSorted()
|
||||||
|
self:T(self.lid.."GetIDStackSorted")
|
||||||
|
|
||||||
|
local stack = self:GetIDStack()
|
||||||
|
local idstack = {}
|
||||||
|
for _id,_entry in pairs(stack) do
|
||||||
|
idstack[#idstack+1] = _id
|
||||||
|
|
||||||
|
self:T({"pre",_id})
|
||||||
|
end
|
||||||
|
|
||||||
|
local function sortID(a, b)
|
||||||
|
return a < b
|
||||||
|
end
|
||||||
|
|
||||||
|
table.sort(idstack)
|
||||||
|
|
||||||
|
return idstack
|
||||||
|
end
|
||||||
|
|
||||||
|
--- FIFO Get table of data entries
|
||||||
|
-- @param #FIFO self
|
||||||
|
-- @return #table Raw table indexed [1] to [n] of object entries - might be empty!
|
||||||
|
function FIFO:GetDataTable()
|
||||||
|
self:T(self.lid.."GetDataTable")
|
||||||
|
local datatable = {}
|
||||||
|
for _,_entry in pairs(self.stackbypointer) do
|
||||||
|
datatable[#datatable+1] = _entry.data
|
||||||
|
end
|
||||||
|
return datatable
|
||||||
|
end
|
||||||
|
|
||||||
|
--- FIFO Get sorted table of data entries by UniqueIDs (must be numerical UniqueIDs only!)
|
||||||
|
-- @param #FIFO self
|
||||||
|
-- @return #table Table indexed [1] to [n] of sorted object entries - might be empty!
|
||||||
|
function FIFO:GetSortedDataTable()
|
||||||
|
self:T(self.lid.."GetSortedDataTable")
|
||||||
|
local datatable = {}
|
||||||
|
local idtablesorted = self:GetIDStackSorted()
|
||||||
|
for _,_entry in pairs(idtablesorted) do
|
||||||
|
datatable[#datatable+1] = self:ReadByID(_entry)
|
||||||
|
end
|
||||||
|
return datatable
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Iterate the FIFO and call an iterator function for the given FIFO data, providing the object for each element of the stack and optional parameters.
|
||||||
|
-- @param #FIFO self
|
||||||
|
-- @param #function IteratorFunction The function that will be called.
|
||||||
|
-- @param #table Arg (Optional) Further Arguments of the IteratorFunction.
|
||||||
|
-- @param #function Function (Optional) A function returning a #boolean true/false. Only if true, the IteratorFunction is called.
|
||||||
|
-- @param #table FunctionArguments (Optional) Function arguments.
|
||||||
|
-- @return #FIFO self
|
||||||
|
function FIFO:ForEach( IteratorFunction, Arg, Function, FunctionArguments )
|
||||||
|
self:T(self.lid.."ForEach")
|
||||||
|
|
||||||
|
local Set = self:GetPointerStack() or {}
|
||||||
|
Arg = Arg or {}
|
||||||
|
|
||||||
|
local function CoRoutine()
|
||||||
|
local Count = 0
|
||||||
|
for ObjectID, ObjectData in pairs( Set ) do
|
||||||
|
local Object = ObjectData.data
|
||||||
|
self:T( {Object} )
|
||||||
|
if Function then
|
||||||
|
if Function( unpack( FunctionArguments or {} ), Object ) == true then
|
||||||
|
IteratorFunction( Object, unpack( Arg ) )
|
||||||
|
end
|
||||||
|
else
|
||||||
|
IteratorFunction( Object, unpack( Arg ) )
|
||||||
|
end
|
||||||
|
Count = Count + 1
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
local co = CoRoutine
|
||||||
|
|
||||||
|
local function Schedule()
|
||||||
|
|
||||||
|
local status, res = co()
|
||||||
|
self:T( { status, res } )
|
||||||
|
|
||||||
|
if status == false then
|
||||||
|
error( res )
|
||||||
|
end
|
||||||
|
if res == false then
|
||||||
|
return true -- resume next time the loop
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
Schedule()
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- FIFO Print stacks to dcs.log
|
||||||
|
-- @param #FIFO self
|
||||||
|
-- @return #FIFO self
|
||||||
|
function FIFO:Flush()
|
||||||
|
self:T(self.lid.."FiFo Flush")
|
||||||
|
self:I("FIFO Flushing Stack by Pointer")
|
||||||
|
for _id,_data in pairs (self.stackbypointer) do
|
||||||
|
local data = _data -- #FIFO.IDEntry
|
||||||
|
self:I(string.format("Pointer: %s | Entry: Number = %s Data = %s UniqueID = %s",tostring(_id),tostring(data.pointer),tostring(data.data),tostring(data.uniqueID)))
|
||||||
|
end
|
||||||
|
self:I("FIFO Flushing Stack by ID")
|
||||||
|
for _id,_data in pairs (self.stackbyid) do
|
||||||
|
local data = _data -- #FIFO.IDEntry
|
||||||
|
self:I(string.format("ID: %s | Entry: Number = %s Data = %s UniqueID = %s",tostring(_id),tostring(data.pointer),tostring(data.data),tostring(data.uniqueID)))
|
||||||
|
end
|
||||||
|
self:I("Counter = " .. self.counter)
|
||||||
|
self:I("Pointer = ".. self.pointer)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- End FIFO
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- LIFO
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
do
|
||||||
|
--- **UTILS** - LiFo Stack.
|
||||||
|
--
|
||||||
|
-- **Main Features:**
|
||||||
|
--
|
||||||
|
-- * Build a simple multi-purpose LiFo (Last-In, First-Out) stack for generic data.
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ### Author: **applevangelist**
|
||||||
|
|
||||||
|
--- LIFO class.
|
||||||
|
-- @type LIFO
|
||||||
|
-- @field #string ClassName Name of the class.
|
||||||
|
-- @field #string lid Class id string for output to DCS log file.
|
||||||
|
-- @field #string version Version of LiFo
|
||||||
|
-- @field #number counter
|
||||||
|
-- @field #number pointer
|
||||||
|
-- @field #table stackbypointer
|
||||||
|
-- @field #table stackbyid
|
||||||
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
|
---
|
||||||
|
-- @type LIFO.IDEntry
|
||||||
|
-- @field #number pointer
|
||||||
|
-- @field #table data
|
||||||
|
-- @field #table uniqueID
|
||||||
|
|
||||||
|
---
|
||||||
|
-- @field #LIFO
|
||||||
|
LIFO = {
|
||||||
|
ClassName = "LIFO",
|
||||||
|
lid = "",
|
||||||
|
version = "0.0.5",
|
||||||
|
counter = 0,
|
||||||
|
pointer = 0,
|
||||||
|
stackbypointer = {},
|
||||||
|
stackbyid = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Instantiate a new LIFO Stack
|
||||||
|
-- @param #LIFO self
|
||||||
|
-- @return #LIFO self
|
||||||
|
function LIFO:New()
|
||||||
|
-- Inherit everything from BASE class.
|
||||||
|
local self=BASE:Inherit(self, BASE:New())
|
||||||
|
self.pointer = 0
|
||||||
|
self.counter = 0
|
||||||
|
self.uniquecounter = 0
|
||||||
|
self.stackbypointer = {}
|
||||||
|
self.stackbyid = {}
|
||||||
|
-- Set some string id for output to DCS.log file.
|
||||||
|
self.lid=string.format("%s (%s) | ", "LiFo", self.version)
|
||||||
|
self:T(self.lid .."Created.")
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Empty LIFO Stack
|
||||||
|
-- @param #LIFO self
|
||||||
|
-- @return #LIFO self
|
||||||
|
function LIFO:Clear()
|
||||||
|
self:T(self.lid.."Clear")
|
||||||
|
self.pointer = 0
|
||||||
|
self.counter = 0
|
||||||
|
self.stackbypointer = nil
|
||||||
|
self.stackbyid = nil
|
||||||
|
self.stackbypointer = {}
|
||||||
|
self.stackbyid = {}
|
||||||
|
self.uniquecounter = 0
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- LIFO Push Object to Stack
|
||||||
|
-- @param #LIFO self
|
||||||
|
-- @param #table Object
|
||||||
|
-- @param #string UniqueID (optional) - will default to current pointer + 1
|
||||||
|
-- @return #LIFO self
|
||||||
|
function LIFO:Push(Object,UniqueID)
|
||||||
|
self:T(self.lid.."Push")
|
||||||
|
self:T({Object,UniqueID})
|
||||||
|
self.pointer = self.pointer + 1
|
||||||
|
self.counter = self.counter + 1
|
||||||
|
local uniID = UniqueID
|
||||||
|
if not UniqueID then
|
||||||
|
self.uniquecounter = self.uniquecounter + 1
|
||||||
|
uniID = self.uniquecounter
|
||||||
|
end
|
||||||
|
self.stackbyid[uniID] = { pointer = self.pointer, data = Object, uniqueID = uniID }
|
||||||
|
self.stackbypointer[self.pointer] = { pointer = self.pointer, data = Object, uniqueID = uniID }
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- LIFO Pull Object from Stack
|
||||||
|
-- @param #LIFO self
|
||||||
|
-- @return #table Object or nil if stack is empty
|
||||||
|
function LIFO:Pull()
|
||||||
|
self:T(self.lid.."Pull")
|
||||||
|
if self.counter == 0 then return nil end
|
||||||
|
local object = self.stackbypointer[self.pointer].data
|
||||||
|
self.stackbypointer[self.pointer] = nil
|
||||||
|
--local object = self.stackbypointer[1].data
|
||||||
|
--self.stackbypointer[1] = nil
|
||||||
|
self.counter = self.counter - 1
|
||||||
|
self.pointer = self.pointer - 1
|
||||||
|
self:Flatten()
|
||||||
|
return object
|
||||||
|
end
|
||||||
|
|
||||||
|
--- LIFO Pull Object from Stack by Pointer
|
||||||
|
-- @param #LIFO self
|
||||||
|
-- @param #number Pointer
|
||||||
|
-- @return #table Object or nil if stack is empty
|
||||||
|
function LIFO:PullByPointer(Pointer)
|
||||||
|
self:T(self.lid.."PullByPointer " .. tostring(Pointer))
|
||||||
|
if self.counter == 0 then return nil end
|
||||||
|
local object = self.stackbypointer[Pointer] -- #FIFO.IDEntry
|
||||||
|
self.stackbypointer[Pointer] = nil
|
||||||
|
if object then self.stackbyid[object.uniqueID] = nil end
|
||||||
|
self.counter = self.counter - 1
|
||||||
|
self:Flatten()
|
||||||
|
if object then
|
||||||
|
return object.data
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- LIFO Read, not Pull, Object from Stack by Pointer
|
||||||
|
-- @param #LIFO self
|
||||||
|
-- @param #number Pointer
|
||||||
|
-- @return #table Object or nil if stack is empty or pointer does not exist
|
||||||
|
function LIFO:ReadByPointer(Pointer)
|
||||||
|
self:T(self.lid.."ReadByPointer " .. tostring(Pointer))
|
||||||
|
if self.counter == 0 or not Pointer or not self.stackbypointer[Pointer] then return nil end
|
||||||
|
local object = self.stackbypointer[Pointer] -- #LIFO.IDEntry
|
||||||
|
if object then
|
||||||
|
return object.data
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- LIFO Read, not Pull, Object from Stack by UniqueID
|
||||||
|
-- @param #LIFO self
|
||||||
|
-- @param #number UniqueID
|
||||||
|
-- @return #table Object or nil if stack is empty or ID does not exist
|
||||||
|
function LIFO:ReadByID(UniqueID)
|
||||||
|
self:T(self.lid.."ReadByID " .. tostring(UniqueID))
|
||||||
|
if self.counter == 0 or not UniqueID or not self.stackbyid[UniqueID] then return nil end
|
||||||
|
local object = self.stackbyid[UniqueID] -- #LIFO.IDEntry
|
||||||
|
if object then
|
||||||
|
return object.data
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- LIFO Pull Object from Stack by UniqueID
|
||||||
|
-- @param #LIFO self
|
||||||
|
-- @param #tableUniqueID
|
||||||
|
-- @return #table Object or nil if stack is empty
|
||||||
|
function LIFO:PullByID(UniqueID)
|
||||||
|
self:T(self.lid.."PullByID " .. tostring(UniqueID))
|
||||||
|
if self.counter == 0 then return nil end
|
||||||
|
local object = self.stackbyid[UniqueID] -- #LIFO.IDEntry
|
||||||
|
--self.stackbyid[UniqueID] = nil
|
||||||
|
if object then
|
||||||
|
return self:PullByPointer(object.pointer)
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- LIFO Housekeeping
|
||||||
|
-- @param #LIFO self
|
||||||
|
-- @return #LIFO self
|
||||||
|
function LIFO:Flatten()
|
||||||
|
self:T(self.lid.."Flatten")
|
||||||
|
-- rebuild stacks
|
||||||
|
local pointerstack = {}
|
||||||
|
local idstack = {}
|
||||||
|
local counter = 0
|
||||||
|
for _ID,_entry in pairs(self.stackbypointer) do
|
||||||
|
counter = counter + 1
|
||||||
|
pointerstack[counter] = { pointer = counter, data = _entry.data, uniqueID = _entry.uniqueID}
|
||||||
|
end
|
||||||
|
for _ID,_entry in pairs(pointerstack) do
|
||||||
|
idstack[_entry.uniqueID] = { pointer = _entry.pointer , data = _entry.data, uniqueID = _entry.uniqueID}
|
||||||
|
end
|
||||||
|
self.stackbypointer = nil
|
||||||
|
self.stackbypointer = pointerstack
|
||||||
|
self.stackbyid = nil
|
||||||
|
self.stackbyid = idstack
|
||||||
|
self.counter = counter
|
||||||
|
self.pointer = counter
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- LIFO Check Stack is empty
|
||||||
|
-- @param #LIFO self
|
||||||
|
-- @return #boolean empty
|
||||||
|
function LIFO:IsEmpty()
|
||||||
|
self:T(self.lid.."IsEmpty")
|
||||||
|
return self.counter == 0 and true or false
|
||||||
|
end
|
||||||
|
|
||||||
|
--- LIFO Get stack size
|
||||||
|
-- @param #LIFO self
|
||||||
|
-- @return #number size
|
||||||
|
function LIFO:GetSize()
|
||||||
|
self:T(self.lid.."GetSize")
|
||||||
|
return self.counter
|
||||||
|
end
|
||||||
|
|
||||||
|
--- LIFO Get stack size
|
||||||
|
-- @param #LIFO self
|
||||||
|
-- @return #number size
|
||||||
|
function LIFO:Count()
|
||||||
|
self:T(self.lid.."Count")
|
||||||
|
return self.counter
|
||||||
|
end
|
||||||
|
|
||||||
|
--- LIFO Check Stack is NOT empty
|
||||||
|
-- @param #LIFO self
|
||||||
|
-- @return #boolean notempty
|
||||||
|
function LIFO:IsNotEmpty()
|
||||||
|
self:T(self.lid.."IsNotEmpty")
|
||||||
|
return not self:IsEmpty()
|
||||||
|
end
|
||||||
|
|
||||||
|
--- LIFO Get the data stack by pointer
|
||||||
|
-- @param #LIFO self
|
||||||
|
-- @return #table Table of #LIFO.IDEntry entries
|
||||||
|
function LIFO:GetPointerStack()
|
||||||
|
self:T(self.lid.."GetPointerStack")
|
||||||
|
return self.stackbypointer
|
||||||
|
end
|
||||||
|
|
||||||
|
--- LIFO Get the data stack by UniqueID
|
||||||
|
-- @param #LIFO self
|
||||||
|
-- @return #table Table of #LIFO.IDEntry entries
|
||||||
|
function LIFO:GetIDStack()
|
||||||
|
self:T(self.lid.."GetIDStack")
|
||||||
|
return self.stackbyid
|
||||||
|
end
|
||||||
|
|
||||||
|
--- LIFO Get table of UniqueIDs sorted smallest to largest
|
||||||
|
-- @param #LIFO self
|
||||||
|
-- @return #table Table of #LIFO.IDEntry entries
|
||||||
|
function LIFO:GetIDStackSorted()
|
||||||
|
self:T(self.lid.."GetIDStackSorted")
|
||||||
|
|
||||||
|
local stack = self:GetIDStack()
|
||||||
|
local idstack = {}
|
||||||
|
for _id,_entry in pairs(stack) do
|
||||||
|
idstack[#idstack+1] = _id
|
||||||
|
|
||||||
|
self:T({"pre",_id})
|
||||||
|
end
|
||||||
|
|
||||||
|
local function sortID(a, b)
|
||||||
|
return a < b
|
||||||
|
end
|
||||||
|
|
||||||
|
table.sort(idstack)
|
||||||
|
|
||||||
|
return idstack
|
||||||
|
end
|
||||||
|
|
||||||
|
--- LIFO Check if a certain UniqeID exists
|
||||||
|
-- @param #LIFO self
|
||||||
|
-- @return #boolean exists
|
||||||
|
function LIFO:HasUniqueID(UniqueID)
|
||||||
|
self:T(self.lid.."HasUniqueID")
|
||||||
|
return self.stackbyid[UniqueID] and true or false
|
||||||
|
end
|
||||||
|
|
||||||
|
--- LIFO Print stacks to dcs.log
|
||||||
|
-- @param #LIFO self
|
||||||
|
-- @return #LIFO self
|
||||||
|
function LIFO:Flush()
|
||||||
|
self:T(self.lid.."FiFo Flush")
|
||||||
|
self:I("LIFO Flushing Stack by Pointer")
|
||||||
|
for _id,_data in pairs (self.stackbypointer) do
|
||||||
|
local data = _data -- #LIFO.IDEntry
|
||||||
|
self:I(string.format("Pointer: %s | Entry: Number = %s Data = %s UniqueID = %s",tostring(_id),tostring(data.pointer),tostring(data.data),tostring(data.uniqueID)))
|
||||||
|
end
|
||||||
|
self:I("LIFO Flushing Stack by ID")
|
||||||
|
for _id,_data in pairs (self.stackbyid) do
|
||||||
|
local data = _data -- #LIFO.IDEntry
|
||||||
|
self:I(string.format("ID: %s | Entry: Number = %s Data = %s UniqueID = %s",tostring(_id),tostring(data.pointer),tostring(data.data),tostring(data.uniqueID)))
|
||||||
|
end
|
||||||
|
self:I("Counter = " .. self.counter)
|
||||||
|
self:I("Pointer = ".. self.pointer)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- LIFO Get table of data entries
|
||||||
|
-- @param #LIFO self
|
||||||
|
-- @return #table Raw table indexed [1] to [n] of object entries - might be empty!
|
||||||
|
function LIFO:GetDataTable()
|
||||||
|
self:T(self.lid.."GetDataTable")
|
||||||
|
local datatable = {}
|
||||||
|
for _,_entry in pairs(self.stackbypointer) do
|
||||||
|
datatable[#datatable+1] = _entry.data
|
||||||
|
end
|
||||||
|
return datatable
|
||||||
|
end
|
||||||
|
|
||||||
|
--- LIFO Get sorted table of data entries by UniqueIDs (must be numerical UniqueIDs only!)
|
||||||
|
-- @param #LIFO self
|
||||||
|
-- @return #table Table indexed [1] to [n] of sorted object entries - might be empty!
|
||||||
|
function LIFO:GetSortedDataTable()
|
||||||
|
self:T(self.lid.."GetSortedDataTable")
|
||||||
|
local datatable = {}
|
||||||
|
local idtablesorted = self:GetIDStackSorted()
|
||||||
|
for _,_entry in pairs(idtablesorted) do
|
||||||
|
datatable[#datatable+1] = self:ReadByID(_entry)
|
||||||
|
end
|
||||||
|
return datatable
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Iterate the LIFO and call an iterator function for the given LIFO data, providing the object for each element of the stack and optional parameters.
|
||||||
|
-- @param #LIFO self
|
||||||
|
-- @param #function IteratorFunction The function that will be called.
|
||||||
|
-- @param #table Arg (Optional) Further Arguments of the IteratorFunction.
|
||||||
|
-- @param #function Function (Optional) A function returning a #boolean true/false. Only if true, the IteratorFunction is called.
|
||||||
|
-- @param #table FunctionArguments (Optional) Function arguments.
|
||||||
|
-- @return #LIFO self
|
||||||
|
function LIFO:ForEach( IteratorFunction, Arg, Function, FunctionArguments )
|
||||||
|
self:T(self.lid.."ForEach")
|
||||||
|
|
||||||
|
local Set = self:GetPointerStack() or {}
|
||||||
|
Arg = Arg or {}
|
||||||
|
|
||||||
|
local function CoRoutine()
|
||||||
|
local Count = 0
|
||||||
|
for ObjectID, ObjectData in pairs( Set ) do
|
||||||
|
local Object = ObjectData.data
|
||||||
|
self:T( {Object} )
|
||||||
|
if Function then
|
||||||
|
if Function( unpack( FunctionArguments or {} ), Object ) == true then
|
||||||
|
IteratorFunction( Object, unpack( Arg ) )
|
||||||
|
end
|
||||||
|
else
|
||||||
|
IteratorFunction( Object, unpack( Arg ) )
|
||||||
|
end
|
||||||
|
Count = Count + 1
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
local co = CoRoutine
|
||||||
|
|
||||||
|
local function Schedule()
|
||||||
|
|
||||||
|
local status, res = co()
|
||||||
|
self:T( { status, res } )
|
||||||
|
|
||||||
|
if status == false then
|
||||||
|
error( res )
|
||||||
|
end
|
||||||
|
if res == false then
|
||||||
|
return true -- resume next time the loop
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
Schedule()
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- End LIFO
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
end
|
||||||
@@ -5,10 +5,9 @@
|
|||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ### Author: **TAW CougarNL**, *funkyfranky*
|
-- ### Author: **TAW CougarNL**, *funkyfranky*
|
||||||
--
|
--
|
||||||
-- @module Utilities.PROFILER
|
-- @module Utilities.PROFILER
|
||||||
-- @image MOOSE.JPG
|
-- @image Utils_Profiler.jpg
|
||||||
|
|
||||||
|
|
||||||
--- PROFILER class.
|
--- PROFILER class.
|
||||||
-- @type PROFILER
|
-- @type PROFILER
|
||||||
@@ -26,67 +25,79 @@
|
|||||||
-- @field #string fileNamePrefix Output file name prefix, e.g. "MooseProfiler".
|
-- @field #string fileNamePrefix Output file name prefix, e.g. "MooseProfiler".
|
||||||
-- @field #string fileNameSuffix Output file name prefix, e.g. "txt"
|
-- @field #string fileNameSuffix Output file name prefix, e.g. "txt"
|
||||||
|
|
||||||
--- *The emperor counsels simplicity. First principles. Of each particular thing, ask: What is it in itself, in its own constitution? What is its causal nature? *
|
--- *The emperor counsels simplicity.* *First principles. Of each particular thing, ask: What is it in itself, in its own constitution? What is its causal nature?*
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
-- # The PROFILER Concept
|
-- # The PROFILER Concept
|
||||||
--
|
--
|
||||||
-- Profile your lua code. This tells you, which functions are called very often and which consume most real time.
|
-- Profile your lua code. This tells you, which functions are called very often and which consume most real time.
|
||||||
-- With this information you can optimize the perfomance of your code.
|
-- With this information you can optimize the perfomance of your code.
|
||||||
--
|
--
|
||||||
-- # Prerequisites
|
-- # Prerequisites
|
||||||
--
|
--
|
||||||
-- The modules **os** and **lfs** need to be desanizied.
|
-- The modules **os**, **io** and **lfs** need to be desanizied. Comment out the lines
|
||||||
--
|
--
|
||||||
--
|
-- --sanitizeModule('os')
|
||||||
|
-- --sanitizeModule('io')
|
||||||
|
-- --sanitizeModule('lfs')
|
||||||
|
--
|
||||||
|
-- in your *"DCS World OpenBeta/Scripts/MissionScripting.lua"* file.
|
||||||
|
--
|
||||||
|
-- But be aware that these changes can make you system vulnerable to attacks.
|
||||||
|
--
|
||||||
|
-- # Disclaimer
|
||||||
|
--
|
||||||
|
-- **Profiling itself is CPU expensive!** Don't use this when you want to fly or host a mission.
|
||||||
|
--
|
||||||
|
--
|
||||||
-- # Start
|
-- # Start
|
||||||
--
|
--
|
||||||
-- The profiler can simply be started with the @{#PROFILER.Start}(*Delay, Duration*) function
|
-- The profiler can simply be started with the @{#PROFILER.Start}(*Delay, Duration*) function
|
||||||
--
|
--
|
||||||
-- PROFILER.Start()
|
-- PROFILER.Start()
|
||||||
--
|
--
|
||||||
-- The optional parameter *Delay* can be used to delay the start by a certain amount of seconds and the optional parameter *Duration* can be used to
|
-- The optional parameter *Delay* can be used to delay the start by a certain amount of seconds and the optional parameter *Duration* can be used to
|
||||||
-- stop the profiler after a certain amount of seconds.
|
-- stop the profiler after a certain amount of seconds.
|
||||||
--
|
--
|
||||||
-- # Stop
|
-- # Stop
|
||||||
--
|
--
|
||||||
-- The profiler automatically stops when the mission ends. But it can be stopped any time with the @{#PROFILER.Stop}(*Delay*) function
|
-- The profiler automatically stops when the mission ends. But it can be stopped any time with the @{#PROFILER.Stop}(*Delay*) function
|
||||||
--
|
--
|
||||||
-- PROFILER.Stop()
|
-- PROFILER.Stop()
|
||||||
--
|
--
|
||||||
-- The optional parameter *Delay* can be used to specify a delay after which the profiler is stopped.
|
-- The optional parameter *Delay* can be used to specify a delay after which the profiler is stopped.
|
||||||
--
|
--
|
||||||
-- When the profiler is stopped, the output is written to a file.
|
-- When the profiler is stopped, the output is written to a file.
|
||||||
--
|
--
|
||||||
-- # Output
|
-- # Output
|
||||||
--
|
--
|
||||||
-- The profiler output is written to a file in your DCS home folder
|
-- The profiler output is written to a file in your DCS home folder
|
||||||
--
|
--
|
||||||
-- X:\User\<Your User Name>\Saved Games\DCS OpenBeta\Logs
|
-- X:\User\<Your User Name>\Saved Games\DCS OpenBeta\Logs
|
||||||
--
|
--
|
||||||
-- The default file name is "MooseProfiler.txt". If that file exists, the file name is "MooseProfiler-001.txt" etc.
|
-- The default file name is "MooseProfiler.txt". If that file exists, the file name is "MooseProfiler-001.txt" etc.
|
||||||
--
|
--
|
||||||
-- ## Data
|
-- ## Data
|
||||||
--
|
--
|
||||||
-- The data in the output file provides information on the functions that were called in the mission.
|
-- The data in the output file provides information on the functions that were called in the mission.
|
||||||
--
|
--
|
||||||
-- It will tell you how many times a function was called in total, how many times per second, how much time in total and the percentage of time.
|
-- It will tell you how many times a function was called in total, how many times per second, how much time in total and the percentage of time.
|
||||||
--
|
--
|
||||||
-- If you only want output for functions that are called more than *X* times per second, you can set
|
-- If you only want output for functions that are called more than *X* times per second, you can set
|
||||||
--
|
--
|
||||||
-- PROFILER.ThreshCPS=1.5
|
-- PROFILER.ThreshCPS=1.5
|
||||||
--
|
--
|
||||||
-- With this setting, only functions which are called more than 1.5 times per second are displayed. The default setting is PROFILER.ThreshCPS=0.0 (no threshold).
|
-- With this setting, only functions which are called more than 1.5 times per second are displayed. The default setting is PROFILER.ThreshCPS=0.0 (no threshold).
|
||||||
--
|
--
|
||||||
-- Furthermore, you can limit the output for functions that consumed a certain amount of CPU time in total by
|
-- Furthermore, you can limit the output for functions that consumed a certain amount of CPU time in total by
|
||||||
--
|
--
|
||||||
-- PROFILER.ThreshTtot=0.005
|
-- PROFILER.ThreshTtot=0.005
|
||||||
--
|
--
|
||||||
-- With this setting, which is also the default, only functions which in total used more than 5 milliseconds CPU time.
|
-- With this setting, which is also the default, only functions which in total used more than 5 milliseconds CPU time.
|
||||||
--
|
--
|
||||||
-- @field #PROFILER
|
-- @field #PROFILER
|
||||||
PROFILER = {
|
PROFILER = {
|
||||||
ClassName = "PROFILER",
|
ClassName = "PROFILER",
|
||||||
@@ -117,97 +128,105 @@ PROFILER = {
|
|||||||
--- Start profiler.
|
--- Start profiler.
|
||||||
-- @param #number Delay Delay in seconds before profiler is stated. Default is immediately.
|
-- @param #number Delay Delay in seconds before profiler is stated. Default is immediately.
|
||||||
-- @param #number Duration Duration in (game) seconds before the profiler is stopped. Default is when mission ends.
|
-- @param #number Duration Duration in (game) seconds before the profiler is stopped. Default is when mission ends.
|
||||||
function PROFILER.Start(Delay, Duration)
|
function PROFILER.Start( Delay, Duration )
|
||||||
|
|
||||||
-- Check if os, io and lfs are available.
|
-- Check if os, io and lfs are available.
|
||||||
local go=true
|
local go = true
|
||||||
if not os then
|
if not os then
|
||||||
env.error("ERROR: Profiler needs os to be desanitized!")
|
env.error( "ERROR: Profiler needs os to be de-sanitized!" )
|
||||||
go=false
|
go = false
|
||||||
end
|
end
|
||||||
if not io then
|
if not io then
|
||||||
env.error("ERROR: Profiler needs io to be desanitized!")
|
env.error("ERROR: Profiler needs io to be desanitized!")
|
||||||
go=false
|
go=false
|
||||||
end
|
end
|
||||||
if not lfs then
|
if not lfs then
|
||||||
env.error("ERROR: Profiler needs lfs to be desanitized!")
|
env.error("ERROR: Profiler needs lfs to be desanitized!")
|
||||||
go=false
|
go=false
|
||||||
end
|
end
|
||||||
if not go then
|
if not go then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if Delay and Delay>0 then
|
if Delay and Delay > 0 then
|
||||||
BASE:ScheduleOnce(Delay, PROFILER.Start, 0, Duration)
|
BASE:ScheduleOnce( Delay, PROFILER.Start, 0, Duration )
|
||||||
else
|
else
|
||||||
|
|
||||||
-- Set start time.
|
-- Set start time.
|
||||||
PROFILER.TstartGame=timer.getTime()
|
PROFILER.TstartGame=timer.getTime()
|
||||||
PROFILER.TstartOS=os.clock()
|
PROFILER.TstartOS=os.clock()
|
||||||
|
|
||||||
-- Add event handler.
|
-- Add event handler.
|
||||||
world.addEventHandler(PROFILER.eventHandler)
|
world.addEventHandler(PROFILER.eventHandler)
|
||||||
|
|
||||||
-- Info in log.
|
-- Info in log.
|
||||||
env.info('############################ Profiler Started ############################')
|
env.info( '############################ Profiler Started ############################' )
|
||||||
if Duration then
|
if Duration then
|
||||||
env.info(string.format("- Will be running for %d seconds", Duration))
|
env.info( string.format( "- Will be running for %d seconds", Duration ) )
|
||||||
else
|
else
|
||||||
env.info(string.format("- Will be stopped when mission ends"))
|
env.info( string.format( "- Will be stopped when mission ends" ) )
|
||||||
end
|
end
|
||||||
env.info(string.format("- Calls per second threshold %.3f/sec", PROFILER.ThreshCPS))
|
env.info(string.format("- Calls per second threshold %.3f/sec", PROFILER.ThreshCPS))
|
||||||
env.info(string.format("- Total function time threshold %.3f sec", PROFILER.ThreshTtot))
|
env.info(string.format("- Total function time threshold %.3f sec", PROFILER.ThreshTtot))
|
||||||
env.info(string.format("- Output file \"%s\" in your DCS log file folder", PROFILER.getfilename(PROFILER.fileNameSuffix)))
|
env.info(string.format("- Output file \"%s\" in your DCS log file folder", PROFILER.getfilename(PROFILER.fileNameSuffix)))
|
||||||
env.info(string.format("- Output file \"%s\" in CSV format", PROFILER.getfilename("csv")))
|
env.info(string.format("- Output file \"%s\" in CSV format", PROFILER.getfilename("csv")))
|
||||||
env.info('###############################################################################')
|
env.info('###############################################################################')
|
||||||
|
|
||||||
|
|
||||||
-- Message on screen
|
-- Message on screen
|
||||||
local duration=Duration or 600
|
local duration=Duration or 600
|
||||||
trigger.action.outText("### Profiler running ###", duration)
|
trigger.action.outText("### Profiler running ###", duration)
|
||||||
|
|
||||||
-- Set hook.
|
-- Set hook.
|
||||||
debug.sethook(PROFILER.hook, "cr")
|
debug.sethook(PROFILER.hook, "cr")
|
||||||
|
|
||||||
-- Auto stop profiler.
|
-- Auto stop profiler.
|
||||||
if Duration then
|
if Duration then
|
||||||
PROFILER.Stop(Duration)
|
PROFILER.Stop( Duration )
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Stop profiler.
|
--- Stop profiler.
|
||||||
-- @param #number Delay Delay before stop in seconds.
|
-- @param #number Delay Delay before stop in seconds.
|
||||||
|
function PROFILER.Stop( Delay )
|
||||||
|
|
||||||
|
if Delay and Delay > 0 then
|
||||||
|
|
||||||
|
BASE:ScheduleOnce( Delay, PROFILER.Stop )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function PROFILER.Stop(Delay)
|
function PROFILER.Stop(Delay)
|
||||||
|
|
||||||
if Delay and Delay>0 then
|
if Delay and Delay>0 then
|
||||||
|
|
||||||
BASE:ScheduleOnce(Delay, PROFILER.Stop)
|
BASE:ScheduleOnce(Delay, PROFILER.Stop)
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
-- Remove hook.
|
-- Remove hook.
|
||||||
debug.sethook()
|
debug.sethook()
|
||||||
|
|
||||||
|
|
||||||
-- Run time game.
|
-- Run time game.
|
||||||
local runTimeGame=timer.getTime()-PROFILER.TstartGame
|
local runTimeGame=timer.getTime()-PROFILER.TstartGame
|
||||||
|
|
||||||
-- Run time real OS.
|
-- Run time real OS.
|
||||||
local runTimeOS=os.clock()-PROFILER.TstartOS
|
local runTimeOS=os.clock()-PROFILER.TstartOS
|
||||||
|
|
||||||
-- Show info.
|
-- Show info.
|
||||||
PROFILER.showInfo(runTimeGame, runTimeOS)
|
PROFILER.showInfo(runTimeGame, runTimeOS)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Event handler.
|
--- Event handler.
|
||||||
function PROFILER.eventHandler:onEvent(event)
|
function PROFILER.eventHandler:onEvent( event )
|
||||||
if event.id==world.event.S_EVENT_MISSION_END then
|
if event.id == world.event.S_EVENT_MISSION_END then
|
||||||
PROFILER.Stop()
|
PROFILER.Stop()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -221,35 +240,35 @@ end
|
|||||||
function PROFILER.hook(event)
|
function PROFILER.hook(event)
|
||||||
|
|
||||||
local f=debug.getinfo(2, "f").func
|
local f=debug.getinfo(2, "f").func
|
||||||
|
|
||||||
if event=='call' then
|
if event=='call' then
|
||||||
|
|
||||||
if PROFILER.Counters[f]==nil then
|
if PROFILER.Counters[f]==nil then
|
||||||
|
|
||||||
PROFILER.Counters[f]=1
|
PROFILER.Counters[f]=1
|
||||||
PROFILER.dInfo[f]=debug.getinfo(2,"Sn")
|
PROFILER.dInfo[f]=debug.getinfo(2,"Sn")
|
||||||
|
|
||||||
if PROFILER.fTimeTotal[f]==nil then
|
if PROFILER.fTimeTotal[f]==nil then
|
||||||
PROFILER.fTimeTotal[f]=0
|
PROFILER.fTimeTotal[f]=0
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
PROFILER.Counters[f]=PROFILER.Counters[f]+1
|
PROFILER.Counters[f] = PROFILER.Counters[f] + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
if PROFILER.fTime[f]==nil then
|
if PROFILER.fTime[f]==nil then
|
||||||
PROFILER.fTime[f]=os.clock()
|
PROFILER.fTime[f]=os.clock()
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif (event=='return') then
|
elseif (event=='return') then
|
||||||
|
|
||||||
if PROFILER.fTime[f]~=nil then
|
if PROFILER.fTime[f]~=nil then
|
||||||
PROFILER.fTimeTotal[f]=PROFILER.fTimeTotal[f]+(os.clock()-PROFILER.fTime[f])
|
PROFILER.fTimeTotal[f]=PROFILER.fTimeTotal[f]+(os.clock()-PROFILER.fTime[f])
|
||||||
PROFILER.fTime[f]=nil
|
PROFILER.fTime[f]=nil
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
@@ -262,105 +281,104 @@ end
|
|||||||
-- @return #string Source file name.
|
-- @return #string Source file name.
|
||||||
-- @return #string Line number.
|
-- @return #string Line number.
|
||||||
-- @return #number Function time in seconds.
|
-- @return #number Function time in seconds.
|
||||||
function PROFILER.getData(func)
|
function PROFILER.getData( func )
|
||||||
|
|
||||||
local n=PROFILER.dInfo[func]
|
local n=PROFILER.dInfo[func]
|
||||||
|
|
||||||
if n.what=="C" then
|
if n.what=="C" then
|
||||||
return n.name, "?", "?", PROFILER.fTimeTotal[func]
|
return n.name, "?", "?", PROFILER.fTimeTotal[func]
|
||||||
end
|
end
|
||||||
|
|
||||||
return n.name, n.short_src, n.linedefined, PROFILER.fTimeTotal[func]
|
return n.name, n.short_src, n.linedefined, PROFILER.fTimeTotal[func]
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Write text to log file.
|
--- Write text to log file.
|
||||||
-- @param #function f The file.
|
-- @param #function f The file.
|
||||||
-- @param #string txt The text.
|
-- @param #string txt The text.
|
||||||
function PROFILER._flog(f, txt)
|
function PROFILER._flog( f, txt )
|
||||||
f:write(txt.."\r\n")
|
f:write( txt .. "\r\n" )
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Show table.
|
--- Show table.
|
||||||
-- @param #table data Data table.
|
-- @param #table data Data table.
|
||||||
-- @param #function f The file.
|
-- @param #function f The file.
|
||||||
-- @param #number runTimeGame Game run time in seconds.
|
-- @param #number runTimeGame Game run time in seconds.
|
||||||
function PROFILER.showTable(data, f, runTimeGame)
|
function PROFILER.showTable( data, f, runTimeGame )
|
||||||
|
|
||||||
-- Loop over data.
|
-- Loop over data.
|
||||||
for i=1, #data do
|
for i=1, #data do
|
||||||
local t=data[i] --#PROFILER.Data
|
local t=data[i] --#PROFILER.Data
|
||||||
|
|
||||||
-- Calls per second.
|
-- Calls per second.
|
||||||
local cps=t.count/runTimeGame
|
local cps=t.count/runTimeGame
|
||||||
|
|
||||||
local threshCPS=cps>=PROFILER.ThreshCPS
|
local threshCPS=cps>=PROFILER.ThreshCPS
|
||||||
local threshTot=t.tm>=PROFILER.ThreshTtot
|
local threshTot=t.tm>=PROFILER.ThreshTtot
|
||||||
|
|
||||||
if threshCPS and threshTot then
|
if threshCPS and threshTot then
|
||||||
|
|
||||||
-- Output
|
-- Output
|
||||||
local text=string.format("%30s: %8d calls %8.1f/sec - Time Total %8.3f sec (%.3f %%) %5.3f sec/call %s line %s", t.func, t.count, cps, t.tm, t.tm/runTimeGame*100, t.tm/t.count, tostring(t.src), tostring(t.line))
|
local text=string.format("%30s: %8d calls %8.1f/sec - Time Total %8.3f sec (%.3f %%) %5.3f sec/call %s line %s", t.func, t.count, cps, t.tm, t.tm/runTimeGame*100, t.tm/t.count, tostring(t.src), tostring(t.line))
|
||||||
PROFILER._flog(f, text)
|
PROFILER._flog(f, text)
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Print csv file.
|
--- Print csv file.
|
||||||
-- @param #table data Data table.
|
-- @param #table data Data table.
|
||||||
-- @param #number runTimeGame Game run time in seconds.
|
-- @param #number runTimeGame Game run time in seconds.
|
||||||
function PROFILER.printCSV(data, runTimeGame)
|
function PROFILER.printCSV( data, runTimeGame )
|
||||||
|
|
||||||
-- Output file.
|
-- Output file.
|
||||||
local file=PROFILER.getfilename("csv")
|
local file = PROFILER.getfilename( "csv" )
|
||||||
local g=io.open(file, 'w')
|
local g = io.open( file, 'w' )
|
||||||
|
|
||||||
-- Header.
|
-- Header.
|
||||||
local text="Function,Total Calls,Calls per Sec,Total Time,Total in %,Sec per Call,Source File;Line Number,"
|
local text="Function,Total Calls,Calls per Sec,Total Time,Total in %,Sec per Call,Source File;Line Number,"
|
||||||
g:write(text.."\r\n")
|
g:write(text.."\r\n")
|
||||||
|
|
||||||
-- Loop over data.
|
-- Loop over data.
|
||||||
for i=1, #data do
|
for i=1, #data do
|
||||||
local t=data[i] --#PROFILER.Data
|
local t=data[i] --#PROFILER.Data
|
||||||
|
|
||||||
-- Calls per second.
|
-- Calls per second.
|
||||||
local cps=t.count/runTimeGame
|
local cps = t.count / runTimeGame
|
||||||
|
|
||||||
-- Output
|
-- Output
|
||||||
local txt=string.format("%s,%d,%.1f,%.3f,%.3f,%.3f,%s,%s,", t.func, t.count, cps, t.tm, t.tm/runTimeGame*100, t.tm/t.count, tostring(t.src), tostring(t.line))
|
local txt=string.format("%s,%d,%.1f,%.3f,%.3f,%.3f,%s,%s,", t.func, t.count, cps, t.tm, t.tm/runTimeGame*100, t.tm/t.count, tostring(t.src), tostring(t.line))
|
||||||
g:write(txt.."\r\n")
|
g:write(txt.."\r\n")
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Close file.
|
-- Close file.
|
||||||
g:close()
|
g:close()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Write info to output file.
|
--- Write info to output file.
|
||||||
-- @param #string ext Extension.
|
-- @param #string ext Extension.
|
||||||
-- @return #string File name.
|
-- @return #string File name.
|
||||||
function PROFILER.getfilename(ext)
|
function PROFILER.getfilename(ext)
|
||||||
|
|
||||||
local dir=lfs.writedir()..[[Logs\]]
|
local dir=lfs.writedir()..[[Logs\]]
|
||||||
|
|
||||||
ext=ext or PROFILER.fileNameSuffix
|
ext=ext or PROFILER.fileNameSuffix
|
||||||
|
|
||||||
local file=dir..PROFILER.fileNamePrefix.."."..ext
|
local file=dir..PROFILER.fileNamePrefix.."."..ext
|
||||||
|
|
||||||
if not UTILS.FileExists(file) then
|
if not UTILS.FileExists(file) then
|
||||||
return file
|
return file
|
||||||
end
|
end
|
||||||
|
|
||||||
for i=1,999 do
|
|
||||||
|
|
||||||
local file=string.format("%s%s-%03d.%s", dir,PROFILER.fileNamePrefix, i, ext)
|
|
||||||
|
|
||||||
if not UTILS.FileExists(file) then
|
for i = 1, 999 do
|
||||||
|
|
||||||
|
local file = string.format( "%s%s-%03d.%s", dir, PROFILER.fileNamePrefix, i, ext )
|
||||||
|
|
||||||
|
if not UTILS.FileExists( file ) then
|
||||||
return file
|
return file
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -368,34 +386,34 @@ end
|
|||||||
--- Write info to output file.
|
--- Write info to output file.
|
||||||
-- @param #number runTimeGame Game time in seconds.
|
-- @param #number runTimeGame Game time in seconds.
|
||||||
-- @param #number runTimeOS OS time in seconds.
|
-- @param #number runTimeOS OS time in seconds.
|
||||||
function PROFILER.showInfo(runTimeGame, runTimeOS)
|
function PROFILER.showInfo( runTimeGame, runTimeOS )
|
||||||
|
|
||||||
-- Output file.
|
-- Output file.
|
||||||
local file=PROFILER.getfilename(PROFILER.fileNameSuffix)
|
local file=PROFILER.getfilename(PROFILER.fileNameSuffix)
|
||||||
local f=io.open(file, 'w')
|
local f=io.open(file, 'w')
|
||||||
|
|
||||||
-- Gather data.
|
-- Gather data.
|
||||||
local Ttot=0
|
local Ttot=0
|
||||||
local Calls=0
|
local Calls=0
|
||||||
|
|
||||||
local t={}
|
local t={}
|
||||||
|
|
||||||
local tcopy=nil --#PROFILER.Data
|
local tcopy=nil --#PROFILER.Data
|
||||||
local tserialize=nil --#PROFILER.Data
|
local tserialize=nil --#PROFILER.Data
|
||||||
local tforgen=nil --#PROFILER.Data
|
local tforgen=nil --#PROFILER.Data
|
||||||
local tpairs=nil --#PROFILER.Data
|
local tpairs=nil --#PROFILER.Data
|
||||||
|
|
||||||
|
|
||||||
for func, count in pairs(PROFILER.Counters) do
|
for func, count in pairs(PROFILER.Counters) do
|
||||||
|
|
||||||
local s,src,line,tm=PROFILER.getData(func)
|
local s,src,line,tm=PROFILER.getData(func)
|
||||||
|
|
||||||
if PROFILER.logUnknown==true then
|
if PROFILER.logUnknown==true then
|
||||||
if s==nil then s="<Unknown>" end
|
if s==nil then s="<Unknown>" end
|
||||||
end
|
end
|
||||||
|
|
||||||
if s~=nil then
|
if s~=nil then
|
||||||
|
|
||||||
-- Profile data.
|
-- Profile data.
|
||||||
local T=
|
local T=
|
||||||
{ func=s,
|
{ func=s,
|
||||||
@@ -404,64 +422,64 @@ function PROFILER.showInfo(runTimeGame, runTimeOS)
|
|||||||
count=count,
|
count=count,
|
||||||
tm=tm,
|
tm=tm,
|
||||||
} --#PROFILER.Data
|
} --#PROFILER.Data
|
||||||
|
|
||||||
-- Collect special cases. Somehow, e.g. "_copy" appears multiple times so we try to gather all data.
|
-- Collect special cases. Somehow, e.g. "_copy" appears multiple times so we try to gather all data.
|
||||||
if s=="_copy" then
|
if s == "_copy" then
|
||||||
if tcopy==nil then
|
if tcopy == nil then
|
||||||
tcopy=T
|
tcopy = T
|
||||||
else
|
else
|
||||||
tcopy.count=tcopy.count+T.count
|
tcopy.count = tcopy.count + T.count
|
||||||
tcopy.tm=tcopy.tm+T.tm
|
tcopy.tm = tcopy.tm + T.tm
|
||||||
end
|
end
|
||||||
elseif s=="_Serialize" then
|
elseif s == "_Serialize" then
|
||||||
if tserialize==nil then
|
if tserialize == nil then
|
||||||
tserialize=T
|
tserialize = T
|
||||||
else
|
else
|
||||||
tserialize.count=tserialize.count+T.count
|
tserialize.count=tserialize.count+T.count
|
||||||
tserialize.tm=tserialize.tm+T.tm
|
tserialize.tm=tserialize.tm+T.tm
|
||||||
end
|
end
|
||||||
elseif s=="(for generator)" then
|
elseif s=="(for generator)" then
|
||||||
if tforgen==nil then
|
if tforgen==nil then
|
||||||
tforgen=T
|
tforgen=T
|
||||||
else
|
else
|
||||||
tforgen.count=tforgen.count+T.count
|
tforgen.count=tforgen.count+T.count
|
||||||
tforgen.tm=tforgen.tm+T.tm
|
tforgen.tm=tforgen.tm+T.tm
|
||||||
end
|
end
|
||||||
elseif s=="pairs" then
|
elseif s=="pairs" then
|
||||||
if tpairs==nil then
|
if tpairs==nil then
|
||||||
tpairs=T
|
tpairs=T
|
||||||
else
|
else
|
||||||
tpairs.count=tpairs.count+T.count
|
tpairs.count=tpairs.count+T.count
|
||||||
tpairs.tm=tpairs.tm+T.tm
|
tpairs.tm=tpairs.tm+T.tm
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
table.insert(t, T)
|
table.insert( t, T )
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Total function time.
|
-- Total function time.
|
||||||
Ttot=Ttot+tm
|
Ttot=Ttot+tm
|
||||||
|
|
||||||
-- Total number of calls.
|
-- Total number of calls.
|
||||||
Calls=Calls+count
|
Calls=Calls+count
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Add special cases.
|
-- Add special cases.
|
||||||
if tcopy then
|
if tcopy then
|
||||||
table.insert(t, tcopy)
|
table.insert( t, tcopy )
|
||||||
end
|
end
|
||||||
if tserialize then
|
if tserialize then
|
||||||
table.insert(t, tserialize)
|
table.insert(t, tserialize)
|
||||||
end
|
end
|
||||||
if tforgen then
|
if tforgen then
|
||||||
table.insert(t, tforgen)
|
table.insert( t, tforgen )
|
||||||
end
|
end
|
||||||
if tpairs then
|
if tpairs then
|
||||||
table.insert(t, tpairs)
|
table.insert(t, tpairs)
|
||||||
end
|
end
|
||||||
|
|
||||||
env.info('############################ Profiler Stopped ############################')
|
env.info('############################ Profiler Stopped ############################')
|
||||||
env.info(string.format("* Runtime Game : %s = %d sec", UTILS.SecondsToClock(runTimeGame, true), runTimeGame))
|
env.info(string.format("* Runtime Game : %s = %d sec", UTILS.SecondsToClock(runTimeGame, true), runTimeGame))
|
||||||
env.info(string.format("* Runtime Real : %s = %d sec", UTILS.SecondsToClock(runTimeOS, true), runTimeOS))
|
env.info(string.format("* Runtime Real : %s = %d sec", UTILS.SecondsToClock(runTimeOS, true), runTimeOS))
|
||||||
@@ -470,11 +488,11 @@ function PROFILER.showInfo(runTimeGame, runTimeOS)
|
|||||||
env.info(string.format("* Total func calls : %d", Calls))
|
env.info(string.format("* Total func calls : %d", Calls))
|
||||||
env.info(string.format("* Writing to file : \"%s\"", file))
|
env.info(string.format("* Writing to file : \"%s\"", file))
|
||||||
env.info(string.format("* Writing to file : \"%s\"", PROFILER.getfilename("csv")))
|
env.info(string.format("* Writing to file : \"%s\"", PROFILER.getfilename("csv")))
|
||||||
env.info("##############################################################################")
|
env.info("##############################################################################")
|
||||||
|
|
||||||
-- Sort by total time.
|
-- Sort by total time.
|
||||||
table.sort(t, function(a,b) return a.tm>b.tm end)
|
table.sort(t, function(a,b) return a.tm>b.tm end)
|
||||||
|
|
||||||
-- Write data.
|
-- Write data.
|
||||||
PROFILER._flog(f,"")
|
PROFILER._flog(f,"")
|
||||||
PROFILER._flog(f,"************************************************************************************************************************")
|
PROFILER._flog(f,"************************************************************************************************************************")
|
||||||
@@ -493,7 +511,7 @@ function PROFILER.showInfo(runTimeGame, runTimeOS)
|
|||||||
PROFILER._flog(f,string.format("* Total func calls = %d", Calls))
|
PROFILER._flog(f,string.format("* Total func calls = %d", Calls))
|
||||||
PROFILER._flog(f,"")
|
PROFILER._flog(f,"")
|
||||||
PROFILER._flog(f,string.format("* Calls per second threshold = %.3f/sec", PROFILER.ThreshCPS))
|
PROFILER._flog(f,string.format("* Calls per second threshold = %.3f/sec", PROFILER.ThreshCPS))
|
||||||
PROFILER._flog(f,string.format("* Total func time threshold = %.3f sec", PROFILER.ThreshTtot))
|
PROFILER._flog(f,string.format("* Total func time threshold = %.3f sec", PROFILER.ThreshTtot))
|
||||||
PROFILER._flog(f,"")
|
PROFILER._flog(f,"")
|
||||||
PROFILER._flog(f,"************************************************************************************************************************")
|
PROFILER._flog(f,"************************************************************************************************************************")
|
||||||
PROFILER._flog(f,"")
|
PROFILER._flog(f,"")
|
||||||
@@ -501,7 +519,7 @@ function PROFILER.showInfo(runTimeGame, runTimeOS)
|
|||||||
|
|
||||||
-- Sort by number of calls.
|
-- Sort by number of calls.
|
||||||
table.sort(t, function(a,b) return a.tm/a.count>b.tm/b.count end)
|
table.sort(t, function(a,b) return a.tm/a.count>b.tm/b.count end)
|
||||||
|
|
||||||
-- Detailed data.
|
-- Detailed data.
|
||||||
PROFILER._flog(f,"")
|
PROFILER._flog(f,"")
|
||||||
PROFILER._flog(f,"************************************************************************************************************************")
|
PROFILER._flog(f,"************************************************************************************************************************")
|
||||||
@@ -511,10 +529,10 @@ function PROFILER.showInfo(runTimeGame, runTimeOS)
|
|||||||
PROFILER._flog(f,"--------------------------------------")
|
PROFILER._flog(f,"--------------------------------------")
|
||||||
PROFILER._flog(f,"")
|
PROFILER._flog(f,"")
|
||||||
PROFILER.showTable(t, f, runTimeGame)
|
PROFILER.showTable(t, f, runTimeGame)
|
||||||
|
|
||||||
-- Sort by number of calls.
|
-- Sort by number of calls.
|
||||||
table.sort(t, function(a,b) return a.count>b.count end)
|
table.sort(t, function(a,b) return a.count>b.count end)
|
||||||
|
|
||||||
-- Detailed data.
|
-- Detailed data.
|
||||||
PROFILER._flog(f,"")
|
PROFILER._flog(f,"")
|
||||||
PROFILER._flog(f,"************************************************************************************************************************")
|
PROFILER._flog(f,"************************************************************************************************************************")
|
||||||
@@ -524,16 +542,15 @@ function PROFILER.showInfo(runTimeGame, runTimeOS)
|
|||||||
PROFILER._flog(f,"------------------------------------")
|
PROFILER._flog(f,"------------------------------------")
|
||||||
PROFILER._flog(f,"")
|
PROFILER._flog(f,"")
|
||||||
PROFILER.showTable(t, f, runTimeGame)
|
PROFILER.showTable(t, f, runTimeGame)
|
||||||
|
|
||||||
-- Closing.
|
-- Closing.
|
||||||
PROFILER._flog(f,"")
|
PROFILER._flog( f, "" )
|
||||||
PROFILER._flog(f,"************************************************************************************************************************")
|
PROFILER._flog( f, "************************************************************************************************************************" )
|
||||||
PROFILER._flog(f,"************************************************************************************************************************")
|
PROFILER._flog( f, "************************************************************************************************************************" )
|
||||||
PROFILER._flog(f,"************************************************************************************************************************")
|
PROFILER._flog( f, "************************************************************************************************************************" )
|
||||||
-- Close file.
|
-- Close file.
|
||||||
f:close()
|
f:close()
|
||||||
|
|
||||||
-- Print csv file.
|
|
||||||
PROFILER.printCSV(t, runTimeGame)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
-- Print csv file.
|
||||||
|
PROFILER.printCSV( t, runTimeGame )
|
||||||
|
end
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,6 @@
|
|||||||
--- **Utilities** DCS Simple Text-To-Speech (STTS).
|
--- **Utilities** DCS Simple Text-To-Speech (STTS).
|
||||||
--
|
--
|
||||||
--
|
--
|
||||||
--
|
|
||||||
-- @module Utils.STTS
|
-- @module Utils.STTS
|
||||||
-- @image MOOSE.JPG
|
-- @image MOOSE.JPG
|
||||||
|
|
||||||
@@ -10,29 +9,29 @@
|
|||||||
-- @field #string DIRECTORY Path of the SRS directory.
|
-- @field #string DIRECTORY Path of the SRS directory.
|
||||||
|
|
||||||
--- Simple Text-To-Speech
|
--- Simple Text-To-Speech
|
||||||
--
|
--
|
||||||
-- Version 0.4 - Compatible with SRS version 1.9.6.0+
|
-- Version 0.4 - Compatible with SRS version 1.9.6.0+
|
||||||
--
|
--
|
||||||
-- # DCS Modification Required
|
-- # DCS Modification Required
|
||||||
--
|
--
|
||||||
-- You will need to edit MissionScripting.lua in DCS World/Scripts/MissionScripting.lua and remove the sanitisation.
|
-- You will need to edit MissionScripting.lua in DCS World/Scripts/MissionScripting.lua and remove the sanitization.
|
||||||
-- To do this remove all the code below the comment - the line starts "local function sanitizeModule(name)"
|
-- To do this remove all the code below the comment - the line starts "local function sanitizeModule(name)"
|
||||||
-- Do this without DCS running to allow mission scripts to use os functions.
|
-- Do this without DCS running to allow mission scripts to use os functions.
|
||||||
--
|
--
|
||||||
-- *You WILL HAVE TO REAPPLY AFTER EVERY DCS UPDATE*
|
-- *You WILL HAVE TO REAPPLY AFTER EVERY DCS UPDATE*
|
||||||
--
|
--
|
||||||
-- # USAGE:
|
-- # USAGE:
|
||||||
--
|
--
|
||||||
-- Add this script into the mission as a DO SCRIPT or DO SCRIPT FROM FILE to initialise it
|
-- Add this script into the mission as a DO SCRIPT or DO SCRIPT FROM FILE to initialize it
|
||||||
-- Make sure to edit the STTS.SRS_PORT and STTS.DIRECTORY to the correct values before adding to the mission.
|
-- Make sure to edit the STTS.SRS_PORT and STTS.DIRECTORY to the correct values before adding to the mission.
|
||||||
-- Then its as simple as calling the correct function in LUA as a DO SCRIPT or in your own scripts.
|
-- Then its as simple as calling the correct function in LUA as a DO SCRIPT or in your own scripts.
|
||||||
--
|
--
|
||||||
-- Example calls:
|
-- Example calls:
|
||||||
--
|
--
|
||||||
-- STTS.TextToSpeech("Hello DCS WORLD","251","AM","1.0","SRS",2)
|
-- STTS.TextToSpeech("Hello DCS WORLD","251","AM","1.0","SRS",2)
|
||||||
--
|
--
|
||||||
-- Arguments in order are:
|
-- Arguments in order are:
|
||||||
--
|
--
|
||||||
-- * Message to say, make sure not to use a newline (\n) !
|
-- * Message to say, make sure not to use a newline (\n) !
|
||||||
-- * Frequency in MHz
|
-- * Frequency in MHz
|
||||||
-- * Modulation - AM/FM
|
-- * Modulation - AM/FM
|
||||||
@@ -43,44 +42,44 @@
|
|||||||
-- * OPTIONAL - Speed -10 to +10
|
-- * OPTIONAL - Speed -10 to +10
|
||||||
-- * OPTIONAL - Gender male, female or neuter
|
-- * OPTIONAL - Gender male, female or neuter
|
||||||
-- * OPTIONAL - Culture - en-US, en-GB etc
|
-- * OPTIONAL - Culture - en-US, en-GB etc
|
||||||
-- * OPTIONAL - Voice - a specfic voice by name. Run DCS-SR-ExternalAudio.exe with --help to get the ones you can use on the command line
|
-- * OPTIONAL - Voice - a specific voice by name. Run DCS-SR-ExternalAudio.exe with --help to get the ones you can use on the command line
|
||||||
-- * OPTIONAL - Google TTS - Switch to Google Text To Speech - Requires STTS.GOOGLE_CREDENTIALS path and Google project setup correctly
|
-- * OPTIONAL - Google TTS - Switch to Google Text To Speech - Requires STTS.GOOGLE_CREDENTIALS path and Google project setup correctly
|
||||||
--
|
--
|
||||||
--
|
--
|
||||||
-- ## Example
|
-- ## Example
|
||||||
--
|
--
|
||||||
-- This example will say the words "Hello DCS WORLD" on 251 MHz AM at maximum volume with a client called SRS and to the Blue coalition only
|
-- This example will say the words "Hello DCS WORLD" on 251 MHz AM at maximum volume with a client called SRS and to the Blue coalition only
|
||||||
--
|
--
|
||||||
-- STTS.TextToSpeech("Hello DCS WORLD","251","AM","1.0","SRS",2,null,-5,"male","en-GB")
|
-- STTS.TextToSpeech("Hello DCS WORLD","251","AM","1.0","SRS",2,null,-5,"male","en-GB")
|
||||||
--
|
--
|
||||||
-- ## Example
|
-- ## Example
|
||||||
--
|
--
|
||||||
--This example will say the words "Hello DCS WORLD" on 251 MHz AM at maximum volume with a client called SRS and to the Blue coalition only centered on the position of the Unit called "A UNIT"
|
-- This example will say the words "Hello DCS WORLD" on 251 MHz AM at maximum volume with a client called SRS and to the Blue coalition only centered on the position of the Unit called "A UNIT"
|
||||||
--
|
--
|
||||||
-- STTS.TextToSpeech("Hello DCS WORLD","251","AM","1.0","SRS",2,Unit.getByName("A UNIT"):getPoint(),-5,"male","en-GB")
|
-- STTS.TextToSpeech("Hello DCS WORLD","251","AM","1.0","SRS",2,Unit.getByName("A UNIT"):getPoint(),-5,"male","en-GB")
|
||||||
--
|
--
|
||||||
-- Arguments in order are:
|
-- Arguments in order are:
|
||||||
--
|
--
|
||||||
-- * FULL path to the MP3 OR OGG to play
|
-- * FULL path to the MP3 OR OGG to play
|
||||||
-- * Frequency in MHz - to use multiple separate with a comma - Number of frequencies MUST match number of Modulations
|
-- * Frequency in MHz - to use multiple separate with a comma - Number of frequencies MUST match number of Modulations
|
||||||
-- * Modulation - AM/FM - to use multiple
|
-- * Modulation - AM/FM - to use multiple
|
||||||
-- * Volume - 1.0 max, 0.5 half
|
-- * Volume - 1.0 max, 0.5 half
|
||||||
-- * Name of the transmitter - ATC, RockFM etc
|
-- * Name of the transmitter - ATC, RockFM etc
|
||||||
-- * Coalition - 0 spectator, 1 red 2 blue
|
-- * Coalition - 0 spectator, 1 red 2 blue
|
||||||
--
|
--
|
||||||
-- ## Example
|
-- ## Example
|
||||||
--
|
--
|
||||||
-- This will play that MP3 on 255MHz AM & 31 FM at half volume with a client called "Multiple" and to Spectators only
|
-- This will play that MP3 on 255MHz AM & 31 FM at half volume with a client called "Multiple" and to Spectators only
|
||||||
--
|
--
|
||||||
-- STTS.PlayMP3("C:\\Users\\Ciaran\\Downloads\\PR-Music.mp3","255,31","AM,FM","0.5","Multiple",0)
|
-- STTS.PlayMP3("C:\\Users\\Ciaran\\Downloads\\PR-Music.mp3","255,31","AM,FM","0.5","Multiple",0)
|
||||||
--
|
--
|
||||||
-- @field #STTS
|
-- @field #STTS
|
||||||
STTS={
|
STTS = {
|
||||||
ClassName="STTS",
|
ClassName = "STTS",
|
||||||
DIRECTORY="",
|
DIRECTORY = "",
|
||||||
SRS_PORT=5002,
|
SRS_PORT = 5002,
|
||||||
GOOGLE_CREDENTIALS="C:\\Users\\Ciaran\\Downloads\\googletts.json",
|
GOOGLE_CREDENTIALS = "C:\\Users\\Ciaran\\Downloads\\googletts.json",
|
||||||
EXECUTABLE="DCS-SR-ExternalAudio.exe",
|
EXECUTABLE = "DCS-SR-ExternalAudio.exe"
|
||||||
}
|
}
|
||||||
|
|
||||||
--- FULL Path to the FOLDER containing DCS-SR-ExternalAudio.exe - EDIT TO CORRECT FOLDER
|
--- FULL Path to the FOLDER containing DCS-SR-ExternalAudio.exe - EDIT TO CORRECT FOLDER
|
||||||
@@ -92,142 +91,147 @@ STTS.SRS_PORT = 5002
|
|||||||
--- Google credentials file
|
--- Google credentials file
|
||||||
STTS.GOOGLE_CREDENTIALS = "C:\\Users\\Ciaran\\Downloads\\googletts.json"
|
STTS.GOOGLE_CREDENTIALS = "C:\\Users\\Ciaran\\Downloads\\googletts.json"
|
||||||
|
|
||||||
--- DONT CHANGE THIS UNLESS YOU KNOW WHAT YOU'RE DOING
|
--- DON'T CHANGE THIS UNLESS YOU KNOW WHAT YOU'RE DOING
|
||||||
STTS.EXECUTABLE = "DCS-SR-ExternalAudio.exe"
|
STTS.EXECUTABLE = "DCS-SR-ExternalAudio.exe"
|
||||||
|
|
||||||
|
|
||||||
--- Function for UUID.
|
--- Function for UUID.
|
||||||
function STTS.uuid()
|
function STTS.uuid()
|
||||||
local random = math.random
|
local random = math.random
|
||||||
local template ='yxxx-xxxxxxxxxxxx'
|
local template = 'yxxx-xxxxxxxxxxxx'
|
||||||
return string.gsub(template, '[xy]', function (c)
|
return string.gsub( template, '[xy]', function( c )
|
||||||
local v = (c == 'x') and random(0, 0xf) or random(8, 0xb)
|
local v = (c == 'x') and random( 0, 0xf ) or random( 8, 0xb )
|
||||||
return string.format('%x', v)
|
return string.format( '%x', v )
|
||||||
end)
|
end )
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Round a number.
|
--- Round a number.
|
||||||
-- @param #number x Number.
|
-- @param #number x Number.
|
||||||
-- @param #number n Precision.
|
-- @param #number n Precision.
|
||||||
function STTS.round(x, n)
|
function STTS.round( x, n )
|
||||||
n = math.pow(10, n or 0)
|
n = math.pow( 10, n or 0 )
|
||||||
x = x * n
|
x = x * n
|
||||||
if x >= 0 then x = math.floor(x + 0.5) else x = math.ceil(x - 0.5) end
|
if x >= 0 then
|
||||||
|
x = math.floor( x + 0.5 )
|
||||||
|
else
|
||||||
|
x = math.ceil( x - 0.5 )
|
||||||
|
end
|
||||||
return x / n
|
return x / n
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Function returns estimated speech time in seconds.
|
--- Function returns estimated speech time in seconds.
|
||||||
-- Assumptions for time calc: 100 Words per min, avarage of 5 letters for english word so
|
-- Assumptions for time calc: 100 Words per min, average of 5 letters for english word so
|
||||||
--
|
--
|
||||||
-- * 5 chars * 100wpm = 500 characters per min = 8.3 chars per second
|
-- * 5 chars * 100wpm = 500 characters per min = 8.3 chars per second
|
||||||
--
|
--
|
||||||
-- So lengh of msg / 8.3 = number of seconds needed to read it. rounded down to 8 chars per sec map function:
|
-- So length of msg / 8.3 = number of seconds needed to read it. rounded down to 8 chars per sec map function:
|
||||||
--
|
--
|
||||||
-- * (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
|
-- * (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
|
||||||
--
|
--
|
||||||
|
-- @param #number length can also be passed as #string
|
||||||
|
-- @param #number speed Defaults to 1.0
|
||||||
|
-- @param #boolean isGoogle We're using Google TTS
|
||||||
function STTS.getSpeechTime(length,speed,isGoogle)
|
function STTS.getSpeechTime(length,speed,isGoogle)
|
||||||
|
|
||||||
local maxRateRatio = 3
|
local maxRateRatio = 3
|
||||||
|
|
||||||
speed = speed or 1.0
|
speed = speed or 1.0
|
||||||
isGoogle = isGoogle or false
|
isGoogle = isGoogle or false
|
||||||
|
|
||||||
local speedFactor = 1.0
|
local speedFactor = 1.0
|
||||||
if isGoogle then
|
if isGoogle then
|
||||||
speedFactor = speed
|
speedFactor = speed
|
||||||
else
|
else
|
||||||
if speed ~= 0 then
|
if speed ~= 0 then
|
||||||
speedFactor = math.abs(speed) * (maxRateRatio - 1) / 10 + 1
|
speedFactor = math.abs( speed ) * (maxRateRatio - 1) / 10 + 1
|
||||||
end
|
end
|
||||||
if speed < 0 then
|
if speed < 0 then
|
||||||
speedFactor = 1/speedFactor
|
speedFactor = 1 / speedFactor
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local wpm = math.ceil(100 * speedFactor)
|
local wpm = math.ceil( 100 * speedFactor )
|
||||||
local cps = math.floor((wpm * 5)/60)
|
local cps = math.floor( (wpm * 5) / 60 )
|
||||||
|
|
||||||
if type(length) == "string" then
|
if type( length ) == "string" then
|
||||||
length = string.len(length)
|
length = string.len( length )
|
||||||
end
|
end
|
||||||
|
|
||||||
return math.ceil(length/cps)
|
return length/cps --math.ceil(length/cps)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Text to speech function.
|
--- Text to speech function.
|
||||||
function STTS.TextToSpeech(message, freqs, modulations, volume, name, coalition, point, speed, gender, culture, voice, googleTTS)
|
function STTS.TextToSpeech( message, freqs, modulations, volume, name, coalition, point, speed, gender, culture, voice, googleTTS )
|
||||||
if os == nil or io == nil then
|
if os == nil or io == nil then
|
||||||
env.info("[DCS-STTS] LUA modules os or io are sanitized. skipping. ")
|
env.info( "[DCS-STTS] LUA modules os or io are sanitized. skipping. " )
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
speed = speed or 1
|
speed = speed or 1
|
||||||
gender = gender or "female"
|
gender = gender or "female"
|
||||||
culture = culture or ""
|
culture = culture or ""
|
||||||
voice = voice or ""
|
voice = voice or ""
|
||||||
coalition=coalition or "0"
|
coalition = coalition or "0"
|
||||||
name=name or "ROBOT"
|
name = name or "ROBOT"
|
||||||
volume=1
|
volume = 1
|
||||||
speed=1
|
speed = 1
|
||||||
|
|
||||||
|
message = message:gsub( "\"", "\\\"" )
|
||||||
|
|
||||||
|
local cmd = string.format( "start /min \"\" /d \"%s\" /b \"%s\" -f %s -m %s -c %s -p %s -n \"%s\" -h", STTS.DIRECTORY, STTS.EXECUTABLE, freqs or "305", modulations or "AM", coalition, STTS.SRS_PORT, name )
|
||||||
|
|
||||||
message = message:gsub("\"","\\\"")
|
|
||||||
|
|
||||||
local cmd = string.format("start /min \"\" /d \"%s\" /b \"%s\" -f %s -m %s -c %s -p %s -n \"%s\" -h", STTS.DIRECTORY, STTS.EXECUTABLE, freqs or "305", modulations or "AM", coalition, STTS.SRS_PORT, name)
|
|
||||||
|
|
||||||
if voice ~= "" then
|
if voice ~= "" then
|
||||||
cmd = cmd .. string.format(" -V \"%s\"",voice)
|
cmd = cmd .. string.format( " -V \"%s\"", voice )
|
||||||
else
|
else
|
||||||
|
|
||||||
if culture ~= "" then
|
if culture ~= "" then
|
||||||
cmd = cmd .. string.format(" -l %s",culture)
|
cmd = cmd .. string.format( " -l %s", culture )
|
||||||
end
|
end
|
||||||
|
|
||||||
if gender ~= "" then
|
if gender ~= "" then
|
||||||
cmd = cmd .. string.format(" -g %s",gender)
|
cmd = cmd .. string.format( " -g %s", gender )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if googleTTS == true then
|
if googleTTS == true then
|
||||||
cmd = cmd .. string.format(" -G \"%s\"",STTS.GOOGLE_CREDENTIALS)
|
cmd = cmd .. string.format( " -G \"%s\"", STTS.GOOGLE_CREDENTIALS )
|
||||||
end
|
end
|
||||||
|
|
||||||
if speed ~= 1 then
|
if speed ~= 1 then
|
||||||
cmd = cmd .. string.format(" -s %s",speed)
|
cmd = cmd .. string.format( " -s %s", speed )
|
||||||
end
|
end
|
||||||
|
|
||||||
if volume ~= 1.0 then
|
if volume ~= 1.0 then
|
||||||
cmd = cmd .. string.format(" -v %s",volume)
|
cmd = cmd .. string.format( " -v %s", volume )
|
||||||
end
|
end
|
||||||
|
|
||||||
if point and type(point) == "table" and point.x then
|
if point and type( point ) == "table" and point.x then
|
||||||
local lat, lon, alt = coord.LOtoLL(point)
|
local lat, lon, alt = coord.LOtoLL( point )
|
||||||
|
|
||||||
lat = STTS.round(lat,4)
|
lat = STTS.round( lat, 4 )
|
||||||
lon = STTS.round(lon,4)
|
lon = STTS.round( lon, 4 )
|
||||||
alt = math.floor(alt)
|
alt = math.floor( alt )
|
||||||
|
|
||||||
cmd = cmd .. string.format(" -L %s -O %s -A %s",lat,lon,alt)
|
cmd = cmd .. string.format( " -L %s -O %s -A %s", lat, lon, alt )
|
||||||
end
|
end
|
||||||
|
|
||||||
cmd = cmd ..string.format(" -t \"%s\"",message)
|
cmd = cmd .. string.format( " -t \"%s\"", message )
|
||||||
|
|
||||||
if string.len(cmd) > 255 then
|
if string.len( cmd ) > 255 then
|
||||||
local filename = os.getenv('TMP') .. "\\DCS_STTS-" .. STTS.uuid() .. ".bat"
|
local filename = os.getenv( 'TMP' ) .. "\\DCS_STTS-" .. STTS.uuid() .. ".bat"
|
||||||
local script = io.open(filename,"w+")
|
local script = io.open( filename, "w+" )
|
||||||
script:write(cmd .. " && exit" )
|
script:write( cmd .. " && exit" )
|
||||||
script:close()
|
script:close()
|
||||||
cmd = string.format("\"%s\"",filename)
|
cmd = string.format( "\"%s\"", filename )
|
||||||
timer.scheduleFunction(os.remove, filename, timer.getTime() + 1)
|
timer.scheduleFunction( os.remove, filename, timer.getTime() + 1 )
|
||||||
end
|
end
|
||||||
|
|
||||||
if string.len(cmd) > 255 then
|
if string.len( cmd ) > 255 then
|
||||||
env.info("[DCS-STTS] - cmd string too long")
|
env.info( "[DCS-STTS] - cmd string too long" )
|
||||||
env.info("[DCS-STTS] TextToSpeech Command :\n" .. cmd.."\n")
|
env.info( "[DCS-STTS] TextToSpeech Command :\n" .. cmd .. "\n" )
|
||||||
end
|
end
|
||||||
os.execute(cmd)
|
os.execute( cmd )
|
||||||
|
|
||||||
return STTS.getSpeechTime(message,speed,googleTTS)
|
return STTS.getSpeechTime( message, speed, googleTTS )
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Play mp3 function.
|
--- Play mp3 function.
|
||||||
@@ -235,22 +239,21 @@ end
|
|||||||
-- @param #string freqs Frequencies, e.g. "305, 256".
|
-- @param #string freqs Frequencies, e.g. "305, 256".
|
||||||
-- @param #string modulations Modulations, e.g. "AM, FM".
|
-- @param #string modulations Modulations, e.g. "AM, FM".
|
||||||
-- @param #string volume Volume, e.g. "0.5".
|
-- @param #string volume Volume, e.g. "0.5".
|
||||||
function STTS.PlayMP3(pathToMP3, freqs, modulations, volume, name, coalition, point)
|
function STTS.PlayMP3( pathToMP3, freqs, modulations, volume, name, coalition, point )
|
||||||
|
|
||||||
local cmd = string.format("start \"\" /d \"%s\" /b /min \"%s\" -i \"%s\" -f %s -m %s -c %s -p %s -n \"%s\" -v %s -h",
|
local cmd = string.format( "start \"\" /d \"%s\" /b /min \"%s\" -i \"%s\" -f %s -m %s -c %s -p %s -n \"%s\" -v %s -h", STTS.DIRECTORY, STTS.EXECUTABLE, pathToMP3, freqs or "305", modulations or "AM", coalition or "0", STTS.SRS_PORT, name or "ROBOT", volume or "1" )
|
||||||
STTS.DIRECTORY, STTS.EXECUTABLE, pathToMP3, freqs or "305", modulations or "AM", coalition or "0", STTS.SRS_PORT, name or "ROBOT", volume or "1")
|
|
||||||
|
|
||||||
if point and type(point) == "table" and point.x then
|
|
||||||
local lat, lon, alt = coord.LOtoLL(point)
|
|
||||||
|
|
||||||
lat = STTS.round(lat,4)
|
if point and type( point ) == "table" and point.x then
|
||||||
lon = STTS.round(lon,4)
|
local lat, lon, alt = coord.LOtoLL( point )
|
||||||
alt = math.floor(alt)
|
|
||||||
|
|
||||||
cmd = cmd .. string.format(" -L %s -O %s -A %s",lat,lon,alt)
|
lat = STTS.round( lat, 4 )
|
||||||
end
|
lon = STTS.round( lon, 4 )
|
||||||
|
alt = math.floor( alt )
|
||||||
|
|
||||||
env.info("[DCS-STTS] MP3/OGG Command :\n" .. cmd.."\n")
|
cmd = cmd .. string.format( " -L %s -O %s -A %s", lat, lon, alt )
|
||||||
os.execute(cmd)
|
end
|
||||||
|
|
||||||
end
|
env.info( "[DCS-STTS] MP3/OGG Command :\n" .. cmd .. "\n" )
|
||||||
|
os.execute( cmd )
|
||||||
|
|
||||||
|
end
|
||||||
|
|||||||
152
Moose Development/Moose/Utilities/Socket.lua
Normal file
152
Moose Development/Moose/Utilities/Socket.lua
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
--- **Utilities** - Socket.
|
||||||
|
--
|
||||||
|
-- **Main Features:**
|
||||||
|
--
|
||||||
|
-- * Creates UDP Sockets
|
||||||
|
-- * Send messages to Discord
|
||||||
|
-- * Compatible with [FunkMan](https://github.com/funkyfranky/FunkMan)
|
||||||
|
-- * Compatible with [DCSServerBot](https://github.com/Special-K-s-Flightsim-Bots/DCSServerBot)
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ### Author: **funkyfranky**
|
||||||
|
-- @module Utilities.Socket
|
||||||
|
-- @image Utilities_Socket.png
|
||||||
|
|
||||||
|
|
||||||
|
--- SOCKET class.
|
||||||
|
-- @type SOCKET
|
||||||
|
-- @field #string ClassName Name of the class.
|
||||||
|
-- @field #number verbose Verbosity level.
|
||||||
|
-- @field #string lid Class id string for output to DCS log file.
|
||||||
|
-- @field #table socket The socket.
|
||||||
|
-- @field #number port The port.
|
||||||
|
-- @field #string host The host.
|
||||||
|
-- @field #table json JSON.
|
||||||
|
-- @extends Core.Fsm#FSM
|
||||||
|
|
||||||
|
--- **At times I feel like a socket that remembers its tooth.** -- Saul Bellow
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- # The SOCKET Concept
|
||||||
|
--
|
||||||
|
-- Create a UDP socket server. It enables you to send messages to discord servers via discord bots.
|
||||||
|
--
|
||||||
|
-- **Note** that you have to **de-sanitize** `require` and `package` in your `MissionScripting.lua` file, which is in your `DCS/Scripts` folder.
|
||||||
|
--
|
||||||
|
--
|
||||||
|
-- @field #SOCKET
|
||||||
|
SOCKET = {
|
||||||
|
ClassName = "SOCKET",
|
||||||
|
verbose = 0,
|
||||||
|
lid = nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Data type. This is the keyword the socket listener uses.
|
||||||
|
-- @field #string TEXT Plain text.
|
||||||
|
-- @field #string BOMBRESULT Range bombing.
|
||||||
|
-- @field #string STRAFERESULT Range strafeing result.
|
||||||
|
-- @field #string LSOGRADE Airboss LSO grade.
|
||||||
|
SOCKET.DataType={
|
||||||
|
TEXT="moose_text",
|
||||||
|
BOMBRESULT="moose_bomb_result",
|
||||||
|
STRAFERESULT="moose_strafe_result",
|
||||||
|
LSOGRADE="moose_lso_grade",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
--- SOCKET class version.
|
||||||
|
-- @field #string version
|
||||||
|
SOCKET.version="0.1.0"
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- TODO list
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- TODO: A lot!
|
||||||
|
-- TODO: Messages as spoiler.
|
||||||
|
-- TODO: Send images?
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Constructor
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Create a new SOCKET object.
|
||||||
|
-- @param #SOCKET self
|
||||||
|
-- @param #number Port UDP port. Default `10042`.
|
||||||
|
-- @param #string Host Host. Default `"127.0.0.1"`.
|
||||||
|
-- @return #SOCKET self
|
||||||
|
function SOCKET:New(Port, Host)
|
||||||
|
|
||||||
|
-- Inherit everything from FSM class.
|
||||||
|
local self=BASE:Inherit(self, FSM:New()) --#SOCKET
|
||||||
|
|
||||||
|
package.path = package.path..";.\\LuaSocket\\?.lua;"
|
||||||
|
package.cpath = package.cpath..";.\\LuaSocket\\?.dll;"
|
||||||
|
|
||||||
|
self.socket = require("socket")
|
||||||
|
|
||||||
|
self.port=Port or 10042
|
||||||
|
self.host=Host or "127.0.0.1"
|
||||||
|
|
||||||
|
self.json=loadfile("Scripts\\JSON.lua")()
|
||||||
|
|
||||||
|
self.UDPSendSocket=self.socket.udp()
|
||||||
|
self.UDPSendSocket:settimeout(0)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set port.
|
||||||
|
-- @param #SOCKET self
|
||||||
|
-- @param #number Port Port. Default 10042.
|
||||||
|
-- @return #SOCKET self
|
||||||
|
function SOCKET:SetPort(Port)
|
||||||
|
self.port=Port or 10042
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set host.
|
||||||
|
-- @param #SOCKET self
|
||||||
|
-- @param #string Host Host. Default `"127.0.0.1"`.
|
||||||
|
-- @return #SOCKET self
|
||||||
|
function SOCKET:SetHost(Host)
|
||||||
|
self.host=Host or "127.0.0.1"
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Send a table.
|
||||||
|
-- @param #SOCKET self
|
||||||
|
-- @param #table Table Table to send.
|
||||||
|
-- @return #SOCKET self
|
||||||
|
function SOCKET:SendTable(Table)
|
||||||
|
|
||||||
|
local json= self.json:encode(Table)
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
self:T("Json table:")
|
||||||
|
self:T(json)
|
||||||
|
|
||||||
|
-- Send data.
|
||||||
|
self.socket.try(self.UDPSendSocket:sendto(json, self.host, self.port))
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Send a text message.
|
||||||
|
-- @param #SOCKET self
|
||||||
|
-- @param #string Text Test message.
|
||||||
|
-- @return #SOCKET self
|
||||||
|
function SOCKET:SendText(Text)
|
||||||
|
|
||||||
|
local message={}
|
||||||
|
|
||||||
|
message.command = SOCKET.DataType.TEXT
|
||||||
|
message.text = Text
|
||||||
|
|
||||||
|
self:SendTable(message)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
--- This module contains derived utilities taken from the MIST framework, which are excellent tools to be reused in an OO environment.
|
--- This module contains derived utilities taken from the MIST framework, as well as a lot of added helpers from the MOOSE community.
|
||||||
--
|
--
|
||||||
-- ### Authors:
|
-- ### Authors:
|
||||||
--
|
--
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
-- ### Contributions:
|
-- ### Contributions:
|
||||||
--
|
--
|
||||||
-- * FlightControl : Rework to OO framework.
|
-- * FlightControl : Rework to OO framework.
|
||||||
|
-- * And many more
|
||||||
--
|
--
|
||||||
-- @module Utils
|
-- @module Utils
|
||||||
-- @image MOOSE.JPG
|
-- @image MOOSE.JPG
|
||||||
@@ -51,6 +52,7 @@ BIGSMOKEPRESET = {
|
|||||||
-- @field #string TheChannel The Channel map.
|
-- @field #string TheChannel The Channel map.
|
||||||
-- @field #string Syria Syria map.
|
-- @field #string Syria Syria map.
|
||||||
-- @field #string MarianaIslands Mariana Islands map.
|
-- @field #string MarianaIslands Mariana Islands map.
|
||||||
|
-- @field #string Falklands South Atlantic map.
|
||||||
DCSMAP = {
|
DCSMAP = {
|
||||||
Caucasus="Caucasus",
|
Caucasus="Caucasus",
|
||||||
NTTR="Nevada",
|
NTTR="Nevada",
|
||||||
@@ -58,7 +60,8 @@ DCSMAP = {
|
|||||||
PersianGulf="PersianGulf",
|
PersianGulf="PersianGulf",
|
||||||
TheChannel="TheChannel",
|
TheChannel="TheChannel",
|
||||||
Syria="Syria",
|
Syria="Syria",
|
||||||
MarianaIslands="MarianaIslands"
|
MarianaIslands="MarianaIslands",
|
||||||
|
Falklands="Falklands",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -130,6 +133,62 @@ CALLSIGN={
|
|||||||
Dublin=9,
|
Dublin=9,
|
||||||
Perth=10,
|
Perth=10,
|
||||||
},
|
},
|
||||||
|
F16={
|
||||||
|
Viper=9,
|
||||||
|
Venom=10,
|
||||||
|
Lobo=11,
|
||||||
|
Cowboy=12,
|
||||||
|
Python=13,
|
||||||
|
Rattler=14,
|
||||||
|
Panther=15,
|
||||||
|
Wolf=16,
|
||||||
|
Weasel=17,
|
||||||
|
Wild=18,
|
||||||
|
Ninja=19,
|
||||||
|
Jedi=20,
|
||||||
|
},
|
||||||
|
F18={
|
||||||
|
Hornet=9,
|
||||||
|
Squid=10,
|
||||||
|
Ragin=11,
|
||||||
|
Roman=12,
|
||||||
|
Sting=13,
|
||||||
|
Jury=14,
|
||||||
|
Jokey=15,
|
||||||
|
Ram=16,
|
||||||
|
Hawk=17,
|
||||||
|
Devil=18,
|
||||||
|
Check=19,
|
||||||
|
Snake=20,
|
||||||
|
},
|
||||||
|
F15E={
|
||||||
|
Dude=9,
|
||||||
|
Thud=10,
|
||||||
|
Gunny=11,
|
||||||
|
Trek=12,
|
||||||
|
Sniper=13,
|
||||||
|
Sled=14,
|
||||||
|
Best=15,
|
||||||
|
Jazz=16,
|
||||||
|
Rage=17,
|
||||||
|
Tahoe=18,
|
||||||
|
},
|
||||||
|
B1B={
|
||||||
|
Bone=9,
|
||||||
|
Dark=10,
|
||||||
|
Vader=11
|
||||||
|
},
|
||||||
|
B52={
|
||||||
|
Buff=9,
|
||||||
|
Dump=10,
|
||||||
|
Kenworth=11,
|
||||||
|
},
|
||||||
|
TransportAircraft={
|
||||||
|
Heavy=9,
|
||||||
|
Trash=10,
|
||||||
|
Cargo=11,
|
||||||
|
Ascot=12,
|
||||||
|
},
|
||||||
} --#CALLSIGN
|
} --#CALLSIGN
|
||||||
|
|
||||||
--- Utilities static class.
|
--- Utilities static class.
|
||||||
@@ -409,13 +468,17 @@ end
|
|||||||
-- @param #number knots Speed in knots.
|
-- @param #number knots Speed in knots.
|
||||||
-- @return #number Speed in m/s.
|
-- @return #number Speed in m/s.
|
||||||
UTILS.KnotsToMps = function( knots )
|
UTILS.KnotsToMps = function( knots )
|
||||||
return knots / 1.94384 --* 1852 / 3600
|
if type(knots) == "number" then
|
||||||
|
return knots / 1.94384 --* 1852 / 3600
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Convert temperature from Celsius to Farenheit.
|
--- Convert temperature from Celsius to Fahrenheit.
|
||||||
-- @param #number Celcius Temperature in degrees Celsius.
|
-- @param #number Celcius Temperature in degrees Celsius.
|
||||||
-- @return #number Temperature in degrees Farenheit.
|
-- @return #number Temperature in degrees Fahrenheit.
|
||||||
UTILS.CelciusToFarenheit = function( Celcius )
|
UTILS.CelsiusToFahrenheit = function( Celcius )
|
||||||
return Celcius * 9/5 + 32
|
return Celcius * 9/5 + 32
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -748,7 +811,7 @@ function UTILS.BeaufortScale(speed)
|
|||||||
return bn,bd
|
return bn,bd
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Split string at seperators. C.f. http://stackoverflow.com/questions/1426954/split-string-in-lua
|
--- Split string at seperators. C.f. [split-string-in-lua](http://stackoverflow.com/questions/1426954/split-string-in-lua).
|
||||||
-- @param #string str Sting to split.
|
-- @param #string str Sting to split.
|
||||||
-- @param #string sep Speparator for split.
|
-- @param #string sep Speparator for split.
|
||||||
-- @return #table Split text.
|
-- @return #table Split text.
|
||||||
@@ -970,6 +1033,15 @@ function UTILS.VecDot(a, b)
|
|||||||
return a.x*b.x + a.y*b.y + a.z*b.z
|
return a.x*b.x + a.y*b.y + a.z*b.z
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Calculate the [dot product](https://en.wikipedia.org/wiki/Dot_product) of two 2D vectors. The result is a number.
|
||||||
|
-- @param DCS#Vec2 a Vector in 2D with x, y components.
|
||||||
|
-- @param DCS#Vec2 b Vector in 2D with x, y components.
|
||||||
|
-- @return #number Scalar product of the two vectors a*b.
|
||||||
|
function UTILS.Vec2Dot(a, b)
|
||||||
|
return a.x*b.x + a.y*b.y
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Calculate the [euclidean norm](https://en.wikipedia.org/wiki/Euclidean_distance) (length) of a 3D vector.
|
--- Calculate the [euclidean norm](https://en.wikipedia.org/wiki/Euclidean_distance) (length) of a 3D vector.
|
||||||
-- @param DCS#Vec3 a Vector in 3D with x, y, z components.
|
-- @param DCS#Vec3 a Vector in 3D with x, y, z components.
|
||||||
-- @return #number Norm of the vector.
|
-- @return #number Norm of the vector.
|
||||||
@@ -977,6 +1049,13 @@ function UTILS.VecNorm(a)
|
|||||||
return math.sqrt(UTILS.VecDot(a, a))
|
return math.sqrt(UTILS.VecDot(a, a))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Calculate the [euclidean norm](https://en.wikipedia.org/wiki/Euclidean_distance) (length) of a 2D vector.
|
||||||
|
-- @param DCS#Vec2 a Vector in 2D with x, y components.
|
||||||
|
-- @return #number Norm of the vector.
|
||||||
|
function UTILS.Vec2Norm(a)
|
||||||
|
return math.sqrt(UTILS.Vec2Dot(a, a))
|
||||||
|
end
|
||||||
|
|
||||||
--- Calculate the distance between two 2D vectors.
|
--- Calculate the distance between two 2D vectors.
|
||||||
-- @param DCS#Vec2 a Vector in 3D with x, y components.
|
-- @param DCS#Vec2 a Vector in 3D with x, y components.
|
||||||
-- @param DCS#Vec2 b Vector in 3D with x, y components.
|
-- @param DCS#Vec2 b Vector in 3D with x, y components.
|
||||||
@@ -1020,6 +1099,14 @@ function UTILS.VecSubstract(a, b)
|
|||||||
return {x=a.x-b.x, y=a.y-b.y, z=a.z-b.z}
|
return {x=a.x-b.x, y=a.y-b.y, z=a.z-b.z}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Calculate the difference between two 2D vectors by substracting the x,y components from each other.
|
||||||
|
-- @param DCS#Vec2 a Vector in 2D with x, y components.
|
||||||
|
-- @param DCS#Vec2 b Vector in 2D with x, y components.
|
||||||
|
-- @return DCS#Vec2 Vector c=a-b with c(i)=a(i)-b(i), i=x,y.
|
||||||
|
function UTILS.Vec2Substract(a, b)
|
||||||
|
return {x=a.x-b.x, y=a.y-b.y}
|
||||||
|
end
|
||||||
|
|
||||||
--- Calculate the total vector of two 3D vectors by adding the x,y,z components of each other.
|
--- Calculate the total vector of two 3D vectors by adding the x,y,z components of each other.
|
||||||
-- @param DCS#Vec3 a Vector in 3D with x, y, z components.
|
-- @param DCS#Vec3 a Vector in 3D with x, y, z components.
|
||||||
-- @param DCS#Vec3 b Vector in 3D with x, y, z components.
|
-- @param DCS#Vec3 b Vector in 3D with x, y, z components.
|
||||||
@@ -1028,6 +1115,14 @@ function UTILS.VecAdd(a, b)
|
|||||||
return {x=a.x+b.x, y=a.y+b.y, z=a.z+b.z}
|
return {x=a.x+b.x, y=a.y+b.y, z=a.z+b.z}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Calculate the total vector of two 2D vectors by adding the x,y components of each other.
|
||||||
|
-- @param DCS#Vec2 a Vector in 2D with x, y components.
|
||||||
|
-- @param DCS#Vec2 b Vector in 2D with x, y components.
|
||||||
|
-- @return DCS#Vec2 Vector c=a+b with c(i)=a(i)+b(i), i=x,y.
|
||||||
|
function UTILS.Vec2Add(a, b)
|
||||||
|
return {x=a.x+b.x, y=a.y+b.y}
|
||||||
|
end
|
||||||
|
|
||||||
--- Calculate the angle between two 3D vectors.
|
--- Calculate the angle between two 3D vectors.
|
||||||
-- @param DCS#Vec3 a Vector in 3D with x, y, z components.
|
-- @param DCS#Vec3 a Vector in 3D with x, y, z components.
|
||||||
-- @param DCS#Vec3 b Vector in 3D with x, y, z components.
|
-- @param DCS#Vec3 b Vector in 3D with x, y, z components.
|
||||||
@@ -1059,6 +1154,17 @@ function UTILS.VecHdg(a)
|
|||||||
return h
|
return h
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Calculate "heading" of a 2D vector in the X-Y plane.
|
||||||
|
-- @param DCS#Vec2 a Vector in 2D with x, y components.
|
||||||
|
-- @return #number Heading in degrees in [0,360).
|
||||||
|
function UTILS.Vec2Hdg(a)
|
||||||
|
local h=math.deg(math.atan2(a.y, a.x))
|
||||||
|
if h<0 then
|
||||||
|
h=h+360
|
||||||
|
end
|
||||||
|
return h
|
||||||
|
end
|
||||||
|
|
||||||
--- Calculate the difference between two "heading", i.e. angles in [0,360) deg.
|
--- Calculate the difference between two "heading", i.e. angles in [0,360) deg.
|
||||||
-- @param #number h1 Heading one.
|
-- @param #number h1 Heading one.
|
||||||
-- @param #number h2 Heading two.
|
-- @param #number h2 Heading two.
|
||||||
@@ -1095,6 +1201,22 @@ function UTILS.VecTranslate(a, distance, angle)
|
|||||||
return {x=TX, y=a.y, z=TY}
|
return {x=TX, y=a.y, z=TY}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Translate 2D vector in the 2D (x,z) plane.
|
||||||
|
-- @param DCS#Vec2 a Vector in 2D with x, y components.
|
||||||
|
-- @param #number distance The distance to translate.
|
||||||
|
-- @param #number angle Rotation angle in degrees.
|
||||||
|
-- @return DCS#Vec2 Translated vector.
|
||||||
|
function UTILS.Vec2Translate(a, distance, angle)
|
||||||
|
|
||||||
|
local SX = a.x
|
||||||
|
local SY = a.y
|
||||||
|
local Radians=math.rad(angle or 0)
|
||||||
|
local TX=distance*math.cos(Radians)+SX
|
||||||
|
local TY=distance*math.sin(Radians)+SY
|
||||||
|
|
||||||
|
return {x=TX, y=TY}
|
||||||
|
end
|
||||||
|
|
||||||
--- Rotate 3D vector in the 2D (x,z) plane. y-component (usually altitude) unchanged.
|
--- Rotate 3D vector in the 2D (x,z) plane. y-component (usually altitude) unchanged.
|
||||||
-- @param DCS#Vec3 a Vector in 3D with x, y, z components.
|
-- @param DCS#Vec3 a Vector in 3D with x, y, z components.
|
||||||
-- @param #number angle Rotation angle in degrees.
|
-- @param #number angle Rotation angle in degrees.
|
||||||
@@ -1115,6 +1237,25 @@ function UTILS.Rotate2D(a, angle)
|
|||||||
return A
|
return A
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Rotate 2D vector in the 2D (x,z) plane.
|
||||||
|
-- @param DCS#Vec2 a Vector in 2D with x, y components.
|
||||||
|
-- @param #number angle Rotation angle in degrees.
|
||||||
|
-- @return DCS#Vec2 Vector rotated in the (x,y) plane.
|
||||||
|
function UTILS.Vec2Rotate2D(a, angle)
|
||||||
|
|
||||||
|
local phi=math.rad(angle)
|
||||||
|
|
||||||
|
local x=a.x
|
||||||
|
local y=a.y
|
||||||
|
|
||||||
|
local X=x*math.cos(phi)-y*math.sin(phi)
|
||||||
|
local Y=x*math.sin(phi)+y*math.cos(phi)
|
||||||
|
|
||||||
|
local A={x=X, y=Y}
|
||||||
|
|
||||||
|
return A
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Converts a TACAN Channel/Mode couple into a frequency in Hz.
|
--- Converts a TACAN Channel/Mode couple into a frequency in Hz.
|
||||||
-- @param #number TACANChannel The TACAN channel, i.e. the 10 in "10X".
|
-- @param #number TACANChannel The TACAN channel, i.e. the 10 in "10X".
|
||||||
@@ -1198,26 +1339,6 @@ function UTILS.GetMissionDayOfYear(Time)
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns the current date.
|
|
||||||
-- @return #string Mission date in yyyy/mm/dd format.
|
|
||||||
-- @return #number The year anno domini.
|
|
||||||
-- @return #number The month.
|
|
||||||
-- @return #number The day.
|
|
||||||
function UTILS.GetDate()
|
|
||||||
|
|
||||||
-- Mission start date
|
|
||||||
local date, year, month, day=UTILS.GetDCSMissionDate()
|
|
||||||
|
|
||||||
local time=timer.getAbsTime()
|
|
||||||
|
|
||||||
local clock=UTILS.SecondsToClock(time, false)
|
|
||||||
|
|
||||||
local x=tonumber(UTILS.Split(clock, "+")[2])
|
|
||||||
|
|
||||||
local day=day+x
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Returns the magnetic declination of the map.
|
--- Returns the magnetic declination of the map.
|
||||||
-- Returned values for the current maps are:
|
-- Returned values for the current maps are:
|
||||||
--
|
--
|
||||||
@@ -1228,6 +1349,7 @@ end
|
|||||||
-- * The Cannel Map -10 (West)
|
-- * The Cannel Map -10 (West)
|
||||||
-- * Syria +5 (East)
|
-- * Syria +5 (East)
|
||||||
-- * Mariana Islands +2 (East)
|
-- * Mariana Islands +2 (East)
|
||||||
|
-- * Falklands +12 (East) - note there's a LOT of deviation across the map, as we're closer to the South Pole
|
||||||
-- @param #string map (Optional) Map for which the declination is returned. Default is from env.mission.theatre
|
-- @param #string map (Optional) Map for which the declination is returned. Default is from env.mission.theatre
|
||||||
-- @return #number Declination in degrees.
|
-- @return #number Declination in degrees.
|
||||||
function UTILS.GetMagneticDeclination(map)
|
function UTILS.GetMagneticDeclination(map)
|
||||||
@@ -1250,6 +1372,8 @@ function UTILS.GetMagneticDeclination(map)
|
|||||||
declination=5
|
declination=5
|
||||||
elseif map==DCSMAP.MarianaIslands then
|
elseif map==DCSMAP.MarianaIslands then
|
||||||
declination=2
|
declination=2
|
||||||
|
elseif map==DCSMAP.Falklands then
|
||||||
|
declination=12
|
||||||
else
|
else
|
||||||
declination=0
|
declination=0
|
||||||
end
|
end
|
||||||
@@ -1328,6 +1452,23 @@ function UTILS.GetModulationName(Modulation)
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get the NATO reporting name of a unit type name
|
||||||
|
-- @param #number Typename The type name.
|
||||||
|
-- @return #string The Reporting name or "Bogey".
|
||||||
|
function UTILS.GetReportingName(Typename)
|
||||||
|
|
||||||
|
local typename = string.lower(Typename)
|
||||||
|
|
||||||
|
for name, value in pairs(ENUMS.ReportingName.NATO) do
|
||||||
|
local svalue = string.lower(value)
|
||||||
|
if string.find(typename,svalue,1,true) then
|
||||||
|
return name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return "Bogey"
|
||||||
|
end
|
||||||
|
|
||||||
--- Get the callsign name from its enumerator value
|
--- Get the callsign name from its enumerator value
|
||||||
-- @param #number Callsign The enumerator callsign.
|
-- @param #number Callsign The enumerator callsign.
|
||||||
-- @return #string The callsign name or "Ghostrider".
|
-- @return #string The callsign name or "Ghostrider".
|
||||||
@@ -1356,7 +1497,49 @@ function UTILS.GetCallsignName(Callsign)
|
|||||||
return name
|
return name
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
for name, value in pairs(CALLSIGN.B1B) do
|
||||||
|
if value==Callsign then
|
||||||
|
return name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for name, value in pairs(CALLSIGN.B52) do
|
||||||
|
if value==Callsign then
|
||||||
|
return name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for name, value in pairs(CALLSIGN.F15E) do
|
||||||
|
if value==Callsign then
|
||||||
|
return name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for name, value in pairs(CALLSIGN.F16) do
|
||||||
|
if value==Callsign then
|
||||||
|
return name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for name, value in pairs(CALLSIGN.F18) do
|
||||||
|
if value==Callsign then
|
||||||
|
return name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for name, value in pairs(CALLSIGN.FARP) do
|
||||||
|
if value==Callsign then
|
||||||
|
return name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for name, value in pairs(CALLSIGN.TransportAircraft) do
|
||||||
|
if value==Callsign then
|
||||||
|
return name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return "Ghostrider"
|
return "Ghostrider"
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1542,15 +1725,21 @@ end
|
|||||||
-- @return #number Os time in seconds.
|
-- @return #number Os time in seconds.
|
||||||
function UTILS.GetOSTime()
|
function UTILS.GetOSTime()
|
||||||
if os then
|
if os then
|
||||||
return os.clock()
|
local ts = 0
|
||||||
|
local t = os.date("*t")
|
||||||
|
local s = t.sec
|
||||||
|
local m = t.min * 60
|
||||||
|
local h = t.hour * 3600
|
||||||
|
ts = s+m+h
|
||||||
|
return ts
|
||||||
|
else
|
||||||
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
return nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Shuffle a table accoring to Fisher Yeates algorithm
|
--- Shuffle a table accoring to Fisher Yeates algorithm
|
||||||
--@param #table table to be shuffled
|
--@param #table t Table to be shuffled.
|
||||||
--@return #table
|
--@return #table Shuffled table.
|
||||||
function UTILS.ShuffleTable(t)
|
function UTILS.ShuffleTable(t)
|
||||||
if t == nil or type(t) ~= "table" then
|
if t == nil or type(t) ~= "table" then
|
||||||
BASE:I("Error in ShuffleTable: Missing or wrong type of Argument")
|
BASE:I("Error in ShuffleTable: Missing or wrong type of Argument")
|
||||||
@@ -1568,60 +1757,104 @@ function UTILS.ShuffleTable(t)
|
|||||||
return TempTable
|
return TempTable
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get a random element of a table.
|
||||||
|
--@param #table t Table.
|
||||||
|
--@param #boolean replace If `true`, the drawn element is replaced, i.e. not deleted.
|
||||||
|
--@return #number Table element.
|
||||||
|
function UTILS.GetRandomTableElement(t, replace)
|
||||||
|
|
||||||
|
if t == nil or type(t) ~= "table" then
|
||||||
|
BASE:I("Error in ShuffleTable: Missing or wrong type of Argument")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
math.random()
|
||||||
|
math.random()
|
||||||
|
math.random()
|
||||||
|
|
||||||
|
local r=math.random(#t)
|
||||||
|
|
||||||
|
local element=t[r]
|
||||||
|
|
||||||
|
if not replace then
|
||||||
|
table.remove(t, r)
|
||||||
|
end
|
||||||
|
|
||||||
|
return element
|
||||||
|
end
|
||||||
|
|
||||||
--- (Helicopter) Check if one loading door is open.
|
--- (Helicopter) Check if one loading door is open.
|
||||||
--@param #string unit_name Unit name to be checked
|
--@param #string unit_name Unit name to be checked
|
||||||
--@return #boolean Outcome - true if a (loading door) is open, false if not, nil if none exists.
|
--@return #boolean Outcome - true if a (loading door) is open, false if not, nil if none exists.
|
||||||
function UTILS.IsLoadingDoorOpen( unit_name )
|
function UTILS.IsLoadingDoorOpen( unit_name )
|
||||||
|
|
||||||
local ret_val = false
|
|
||||||
local unit = Unit.getByName(unit_name)
|
local unit = Unit.getByName(unit_name)
|
||||||
|
|
||||||
if unit ~= nil then
|
if unit ~= nil then
|
||||||
local type_name = unit:getTypeName()
|
local type_name = unit:getTypeName()
|
||||||
|
BASE:T("TypeName = ".. type_name)
|
||||||
|
|
||||||
if type_name == "Mi-8MT" and unit:getDrawArgumentValue(38) == 1 or unit:getDrawArgumentValue(86) == 1 or unit:getDrawArgumentValue(250) < 0 then
|
if type_name == "Mi-8MT" and (unit:getDrawArgumentValue(38) == 1 or unit:getDrawArgumentValue(86) == 1 or unit:getDrawArgumentValue(250) < 0) then
|
||||||
BASE:T(unit_name .. " Cargo doors are open or cargo door not present")
|
BASE:T(unit_name .. " Cargo doors are open or cargo door not present")
|
||||||
ret_val = true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
if type_name == "Mi-24P" and unit:getDrawArgumentValue(38) == 1 or unit:getDrawArgumentValue(86) == 1 then
|
if type_name == "Mi-24P" and (unit:getDrawArgumentValue(38) == 1 or unit:getDrawArgumentValue(86) == 1) then
|
||||||
BASE:T(unit_name .. " a side door is open")
|
BASE:T(unit_name .. " a side door is open")
|
||||||
ret_val = true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
if type_name == "UH-1H" and unit:getDrawArgumentValue(43) == 1 or unit:getDrawArgumentValue(44) == 1 then
|
if type_name == "UH-1H" and (unit:getDrawArgumentValue(43) == 1 or unit:getDrawArgumentValue(44) == 1) then
|
||||||
BASE:T(unit_name .. " a side door is open ")
|
BASE:T(unit_name .. " a side door is open ")
|
||||||
ret_val = true
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
if string.find(type_name, "SA342" ) and (unit:getDrawArgumentValue(34) == 1) then
|
||||||
|
BASE:T(unit_name .. " front door(s) are open or doors removed")
|
||||||
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
if string.find(type_name, "SA342" ) and unit:getDrawArgumentValue(34) == 1 or unit:getDrawArgumentValue(38) == 1 then
|
if string.find(type_name, "Hercules") and (unit:getDrawArgumentValue(1215) == 1 and unit:getDrawArgumentValue(1216) == 1) then
|
||||||
BASE:T(unit_name .. " front door(s) are open")
|
|
||||||
ret_val = true
|
|
||||||
end
|
|
||||||
|
|
||||||
if string.find(type_name, "Hercules") and unit:getDrawArgumentValue(1215) == 1 and unit:getDrawArgumentValue(1216) == 1 then
|
|
||||||
BASE:T(unit_name .. " rear doors are open")
|
BASE:T(unit_name .. " rear doors are open")
|
||||||
ret_val = true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
if string.find(type_name, "Hercules") and (unit:getDrawArgumentValue(1220) == 1 or unit:getDrawArgumentValue(1221) == 1) then
|
if string.find(type_name, "Hercules") and (unit:getDrawArgumentValue(1220) == 1 or unit:getDrawArgumentValue(1221) == 1) then
|
||||||
BASE:T(unit_name .. " para doors are open")
|
BASE:T(unit_name .. " para doors are open")
|
||||||
ret_val = true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
if string.find(type_name, "Hercules") and unit:getDrawArgumentValue(1217) == 1 then
|
if string.find(type_name, "Hercules") and (unit:getDrawArgumentValue(1217) == 1) then
|
||||||
BASE:T(unit_name .. " side door is open")
|
BASE:T(unit_name .. " side door is open")
|
||||||
ret_val = true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
if string.find(type_name, "Bell-47") then -- bell aint got no doors so always ready to load injured soldiers
|
if string.find(type_name, "Bell-47") then -- bell aint got no doors so always ready to load injured soldiers
|
||||||
BASE:T(unit_name .. " door is open")
|
BASE:T(unit_name .. " door is open")
|
||||||
ret_val = true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
if ret_val == false then
|
if string.find(type_name, "UH-60L") and (unit:getDrawArgumentValue(401) == 1 or unit:getDrawArgumentValue(402) == 1) then
|
||||||
BASE:T(unit_name .. " all doors are closed")
|
BASE:T(unit_name .. " cargo door is open")
|
||||||
|
return true
|
||||||
end
|
end
|
||||||
return ret_val
|
|
||||||
|
if string.find(type_name, "UH-60L" ) and (unit:getDrawArgumentValue(38) == 1 or unit:getDrawArgumentValue(400) == 1 ) then
|
||||||
|
BASE:T(unit_name .. " front door(s) are open")
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
if type_name == "AH-64D_BLK_II" then
|
||||||
|
BASE:T(unit_name .. " front door(s) are open")
|
||||||
|
return true -- no doors on this one ;)
|
||||||
|
end
|
||||||
|
|
||||||
|
if type_name == "Bronco-OV-10A" then
|
||||||
|
BASE:T(unit_name .. " front door(s) are open")
|
||||||
|
return true -- no doors on this one ;)
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
|
||||||
end -- nil
|
end -- nil
|
||||||
|
|
||||||
@@ -1650,7 +1883,7 @@ function UTILS.GenerateVHFrequencies()
|
|||||||
-- known and sorted map-wise NDBs in kHz
|
-- known and sorted map-wise NDBs in kHz
|
||||||
local _skipFrequencies = {
|
local _skipFrequencies = {
|
||||||
214,274,291.5,295,297.5,
|
214,274,291.5,295,297.5,
|
||||||
300.5,304,307,309.5,311,312,312.5,316,
|
300.5,304,305,307,309.5,311,312,312.5,316,
|
||||||
320,324,328,329,330,332,336,337,
|
320,324,328,329,330,332,336,337,
|
||||||
342,343,348,351,352,353,358,
|
342,343,348,351,352,353,358,
|
||||||
363,365,368,372.5,374,
|
363,365,368,372.5,374,
|
||||||
@@ -1772,3 +2005,472 @@ function UTILS.GenerateLaserCodes()
|
|||||||
end
|
end
|
||||||
return jtacGeneratedLaserCodes
|
return jtacGeneratedLaserCodes
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Function to save an object to a file
|
||||||
|
-- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems.
|
||||||
|
-- @param #string Filename The name of the file. Existing file will be overwritten.
|
||||||
|
-- @param #table Data The LUA data structure to save. This will be e.g. a table of text lines with an \\n at the end of each line.
|
||||||
|
-- @return #boolean outcome True if saving is possible, else false.
|
||||||
|
function UTILS.SaveToFile(Path,Filename,Data)
|
||||||
|
-- Thanks to @FunkyFranky
|
||||||
|
-- Check io module is available.
|
||||||
|
if not io then
|
||||||
|
BASE:E("ERROR: io not desanitized. Can't save current file.")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check default path.
|
||||||
|
if Path==nil and not lfs then
|
||||||
|
BASE:E("WARNING: lfs not desanitized. File will be saved in DCS installation root directory rather than your \"Saved Games\\DCS\" folder.")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set path or default.
|
||||||
|
local path = nil
|
||||||
|
if lfs then
|
||||||
|
path=Path or lfs.writedir()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set file name.
|
||||||
|
local filename=Filename
|
||||||
|
if path~=nil then
|
||||||
|
filename=path.."\\"..filename
|
||||||
|
end
|
||||||
|
|
||||||
|
-- write
|
||||||
|
local f = assert(io.open(filename, "wb"))
|
||||||
|
f:write(Data)
|
||||||
|
f:close()
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Function to save an object to a file
|
||||||
|
-- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems.
|
||||||
|
-- @param #string Filename The name of the file.
|
||||||
|
-- @return #boolean outcome True if reading is possible and successful, else false.
|
||||||
|
-- @return #table data The data read from the filesystem (table of lines of text). Each line is one single #string!
|
||||||
|
function UTILS.LoadFromFile(Path,Filename)
|
||||||
|
-- Thanks to @FunkyFranky
|
||||||
|
-- Check io module is available.
|
||||||
|
if not io then
|
||||||
|
BASE:E("ERROR: io not desanitized. Can't save current state.")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check default path.
|
||||||
|
if Path==nil and not lfs then
|
||||||
|
BASE:E("WARNING: lfs not desanitized. Loading will look into your DCS installation root directory rather than your \"Saved Games\\DCS\" folder.")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set path or default.
|
||||||
|
local path = nil
|
||||||
|
if lfs then
|
||||||
|
path=Path or lfs.writedir()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set file name.
|
||||||
|
local filename=Filename
|
||||||
|
if path~=nil then
|
||||||
|
filename=path.."\\"..filename
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check if file exists.
|
||||||
|
local exists=UTILS.CheckFileExists(Path,Filename)
|
||||||
|
if not exists then
|
||||||
|
BASE:E(string.format("ERROR: File %s does not exist!",filename))
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- read
|
||||||
|
local file=assert(io.open(filename, "rb"))
|
||||||
|
local loadeddata = {}
|
||||||
|
for line in file:lines() do
|
||||||
|
loadeddata[#loadeddata+1] = line
|
||||||
|
end
|
||||||
|
file:close()
|
||||||
|
return true, loadeddata
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Function to check if a file exists.
|
||||||
|
-- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems.
|
||||||
|
-- @param #string Filename The name of the file.
|
||||||
|
-- @return #boolean outcome True if reading is possible, else false.
|
||||||
|
function UTILS.CheckFileExists(Path,Filename)
|
||||||
|
-- Thanks to @FunkyFranky
|
||||||
|
-- Function that check if a file exists.
|
||||||
|
local function _fileexists(name)
|
||||||
|
local f=io.open(name,"r")
|
||||||
|
if f~=nil then
|
||||||
|
io.close(f)
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check io module is available.
|
||||||
|
if not io then
|
||||||
|
BASE:E("ERROR: io not desanitized. Can't save current state.")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check default path.
|
||||||
|
if Path==nil and not lfs then
|
||||||
|
BASE:E("WARNING: lfs not desanitized. Loading will look into your DCS installation root directory rather than your \"Saved Games\\DCS\" folder.")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set path or default.
|
||||||
|
local path = nil
|
||||||
|
if lfs then
|
||||||
|
path=Path or lfs.writedir()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set file name.
|
||||||
|
local filename=Filename
|
||||||
|
if path~=nil then
|
||||||
|
filename=path.."\\"..filename
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check if file exists.
|
||||||
|
local exists=_fileexists(filename)
|
||||||
|
if not exists then
|
||||||
|
BASE:E(string.format("ERROR: File %s does not exist!",filename))
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Function to save the state of a list of groups found by name
|
||||||
|
-- @param #table List Table of strings with groupnames
|
||||||
|
-- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems.
|
||||||
|
-- @param #string Filename The name of the file.
|
||||||
|
-- @return #boolean outcome True if saving is successful, else false.
|
||||||
|
-- @usage
|
||||||
|
-- We will go through the list and find the corresponding group and save the current group size (0 when dead).
|
||||||
|
-- These groups are supposed to be put on the map in the ME and have *not* moved (e.g. stationary SAM sites).
|
||||||
|
-- Position is still saved for your usage.
|
||||||
|
-- The idea is to reduce the number of units when reloading the data again to restart the saved mission.
|
||||||
|
-- The data will be a simple comma separated list of groupname and size, with one header line.
|
||||||
|
function UTILS.SaveStationaryListOfGroups(List,Path,Filename)
|
||||||
|
local filename = Filename or "StateListofGroups"
|
||||||
|
local data = "--Save Stationary List of Groups: "..Filename .."\n"
|
||||||
|
for _,_group in pairs (List) do
|
||||||
|
local group = GROUP:FindByName(_group) -- Wrapper.Group#GROUP
|
||||||
|
if group and group:IsAlive() then
|
||||||
|
local units = group:CountAliveUnits()
|
||||||
|
local position = group:GetVec3()
|
||||||
|
data = string.format("%s%s,%d,%d,%d,%d\n",data,_group,units,position.x,position.y,position.z)
|
||||||
|
else
|
||||||
|
data = string.format("%s%s,0,0,0,0\n",data,_group)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- save the data
|
||||||
|
local outcome = UTILS.SaveToFile(Path,Filename,data)
|
||||||
|
return outcome
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Function to save the state of a set of Wrapper.Group#GROUP objects.
|
||||||
|
-- @param Core.Set#SET_BASE Set of objects to save
|
||||||
|
-- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems.
|
||||||
|
-- @param #string Filename The name of the file.
|
||||||
|
-- @return #boolean outcome True if saving is successful, else false.
|
||||||
|
-- @usage
|
||||||
|
-- We will go through the set and find the corresponding group and save the current group size and current position.
|
||||||
|
-- The idea is to respawn the groups **spawned during an earlier run of the mission** at the given location and reduce
|
||||||
|
-- the number of units in the group when reloading the data again to restart the saved mission. Note that *dead* groups
|
||||||
|
-- cannot be covered with this.
|
||||||
|
-- **Note** Do NOT use dashes or hashes in group template names (-,#)!
|
||||||
|
-- The data will be a simple comma separated list of groupname and size, with one header line.
|
||||||
|
-- The current task/waypoint/etc cannot be restored.
|
||||||
|
function UTILS.SaveSetOfGroups(Set,Path,Filename)
|
||||||
|
local filename = Filename or "SetOfGroups"
|
||||||
|
local data = "--Save SET of groups: "..Filename .."\n"
|
||||||
|
local List = Set:GetSetObjects()
|
||||||
|
for _,_group in pairs (List) do
|
||||||
|
local group = _group -- Wrapper.Group#GROUP
|
||||||
|
if group and group:IsAlive() then
|
||||||
|
local name = group:GetName()
|
||||||
|
local template = string.gsub(name,"-(.+)$","")
|
||||||
|
if string.find(template,"#") then
|
||||||
|
template = string.gsub(name,"#(%d+)$","")
|
||||||
|
end
|
||||||
|
local units = group:CountAliveUnits()
|
||||||
|
local position = group:GetVec3()
|
||||||
|
data = string.format("%s%s,%s,%d,%d,%d,%d\n",data,name,template,units,position.x,position.y,position.z)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- save the data
|
||||||
|
local outcome = UTILS.SaveToFile(Path,Filename,data)
|
||||||
|
return outcome
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Function to save the state of a set of Wrapper.Static#STATIC objects.
|
||||||
|
-- @param Core.Set#SET_BASE Set of objects to save
|
||||||
|
-- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems.
|
||||||
|
-- @param #string Filename The name of the file.
|
||||||
|
-- @return #boolean outcome True if saving is successful, else false.
|
||||||
|
-- @usage
|
||||||
|
-- We will go through the set and find the corresponding static and save the current name and postion when alive.
|
||||||
|
-- The data will be a simple comma separated list of name and state etc, with one header line.
|
||||||
|
function UTILS.SaveSetOfStatics(Set,Path,Filename)
|
||||||
|
local filename = Filename or "SetOfStatics"
|
||||||
|
local data = "--Save SET of statics: "..Filename .."\n"
|
||||||
|
local List = Set:GetSetObjects()
|
||||||
|
for _,_group in pairs (List) do
|
||||||
|
local group = _group -- Wrapper.Static#STATIC
|
||||||
|
if group and group:IsAlive() then
|
||||||
|
local name = group:GetName()
|
||||||
|
local position = group:GetVec3()
|
||||||
|
data = string.format("%s%s,%d,%d,%d\n",data,name,position.x,position.y,position.z)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- save the data
|
||||||
|
local outcome = UTILS.SaveToFile(Path,Filename,data)
|
||||||
|
return outcome
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Function to save the state of a list of statics found by name
|
||||||
|
-- @param #table List Table of strings with statics names
|
||||||
|
-- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems.
|
||||||
|
-- @param #string Filename The name of the file.
|
||||||
|
-- @return #boolean outcome True if saving is successful, else false.
|
||||||
|
-- @usage
|
||||||
|
-- We will go through the list and find the corresponding static and save the current alive state as 1 (0 when dead).
|
||||||
|
-- Position is saved for your usage. **Note** this works on UNIT-name level.
|
||||||
|
-- The idea is to reduce the number of units when reloading the data again to restart the saved mission.
|
||||||
|
-- The data will be a simple comma separated list of name and state etc, with one header line.
|
||||||
|
function UTILS.SaveStationaryListOfStatics(List,Path,Filename)
|
||||||
|
local filename = Filename or "StateListofStatics"
|
||||||
|
local data = "--Save Stationary List of Statics: "..Filename .."\n"
|
||||||
|
for _,_group in pairs (List) do
|
||||||
|
local group = STATIC:FindByName(_group,false) -- Wrapper.Static#STATIC
|
||||||
|
if group and group:IsAlive() then
|
||||||
|
local position = group:GetVec3()
|
||||||
|
data = string.format("%s%s,1,%d,%d,%d\n",data,_group,position.x,position.y,position.z)
|
||||||
|
else
|
||||||
|
data = string.format("%s%s,0,0,0,0\n",data,_group)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- save the data
|
||||||
|
local outcome = UTILS.SaveToFile(Path,Filename,data)
|
||||||
|
return outcome
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Load back a stationary list of groups from file.
|
||||||
|
-- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems.
|
||||||
|
-- @param #string Filename The name of the file.
|
||||||
|
-- @param #boolean Reduce If false, existing loaded groups will not be reduced to fit the saved number.
|
||||||
|
-- @return #table Table of data objects (tables) containing groupname, coordinate and group object. Returns nil when file cannot be read.
|
||||||
|
function UTILS.LoadStationaryListOfGroups(Path,Filename,Reduce)
|
||||||
|
local reduce = true
|
||||||
|
if Reduce == false then reduce = false end
|
||||||
|
local filename = Filename or "StateListofGroups"
|
||||||
|
local datatable = {}
|
||||||
|
if UTILS.CheckFileExists(Path,filename) then
|
||||||
|
local outcome,loadeddata = UTILS.LoadFromFile(Path,Filename)
|
||||||
|
-- remove header
|
||||||
|
table.remove(loadeddata, 1)
|
||||||
|
for _id,_entry in pairs (loadeddata) do
|
||||||
|
local dataset = UTILS.Split(_entry,",")
|
||||||
|
-- groupname,units,position.x,position.y,position.z
|
||||||
|
local groupname = dataset[1]
|
||||||
|
local size = tonumber(dataset[2])
|
||||||
|
local posx = tonumber(dataset[3])
|
||||||
|
local posy = tonumber(dataset[4])
|
||||||
|
local posz = tonumber(dataset[5])
|
||||||
|
local coordinate = COORDINATE:NewFromVec3({x=posx, y=posy, z=posz})
|
||||||
|
local data = { groupname=groupname, size=size, coordinate=coordinate, group=GROUP:FindByName(groupname) }
|
||||||
|
if reduce then
|
||||||
|
local actualgroup = GROUP:FindByName(groupname)
|
||||||
|
if actualgroup and actualgroup:IsAlive() and actualgroup:CountAliveUnits() > size then
|
||||||
|
local reduction = actualgroup:CountAliveUnits() - size
|
||||||
|
BASE:I("Reducing groupsize by ".. reduction .. " units!")
|
||||||
|
-- reduce existing group
|
||||||
|
local units = actualgroup:GetUnits()
|
||||||
|
local units2 = UTILS.ShuffleTable(units) -- randomize table
|
||||||
|
for i=1,reduction do
|
||||||
|
units2[i]:Destroy(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.insert(datatable,data)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
return datatable
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Load back a SET of groups from file.
|
||||||
|
-- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems.
|
||||||
|
-- @param #string Filename The name of the file.
|
||||||
|
-- @param #boolean Spawn If set to false, do not re-spawn the groups loaded in location and reduce to size.
|
||||||
|
-- @return Core.Set#SET_GROUP Set of GROUP objects.
|
||||||
|
-- Returns nil when file cannot be read. Returns a table of data entries if Spawn is false: `{ groupname=groupname, size=size, coordinate=coordinate, template=template }`
|
||||||
|
function UTILS.LoadSetOfGroups(Path,Filename,Spawn)
|
||||||
|
local spawn = true
|
||||||
|
if Spawn == false then spawn = false end
|
||||||
|
BASE:I("Spawn = "..tostring(spawn))
|
||||||
|
local filename = Filename or "SetOfGroups"
|
||||||
|
local setdata = SET_GROUP:New()
|
||||||
|
local datatable = {}
|
||||||
|
if UTILS.CheckFileExists(Path,filename) then
|
||||||
|
local outcome,loadeddata = UTILS.LoadFromFile(Path,Filename)
|
||||||
|
-- remove header
|
||||||
|
table.remove(loadeddata, 1)
|
||||||
|
for _id,_entry in pairs (loadeddata) do
|
||||||
|
local dataset = UTILS.Split(_entry,",")
|
||||||
|
-- groupname,template,units,position.x,position.y,position.z
|
||||||
|
local groupname = dataset[1]
|
||||||
|
local template = dataset[2]
|
||||||
|
local size = tonumber(dataset[3])
|
||||||
|
local posx = tonumber(dataset[4])
|
||||||
|
local posy = tonumber(dataset[5])
|
||||||
|
local posz = tonumber(dataset[6])
|
||||||
|
local coordinate = COORDINATE:NewFromVec3({x=posx, y=posy, z=posz})
|
||||||
|
local group=nil
|
||||||
|
local data = { groupname=groupname, size=size, coordinate=coordinate, template=template }
|
||||||
|
table.insert(datatable,data)
|
||||||
|
if spawn then
|
||||||
|
local group = SPAWN:New(template)
|
||||||
|
:InitDelayOff()
|
||||||
|
:OnSpawnGroup(
|
||||||
|
function(spwndgrp)
|
||||||
|
setdata:AddObject(spwndgrp)
|
||||||
|
local actualsize = spwndgrp:CountAliveUnits()
|
||||||
|
if actualsize > size then
|
||||||
|
local reduction = actualsize-size
|
||||||
|
-- reduce existing group
|
||||||
|
local units = spwndgrp:GetUnits()
|
||||||
|
local units2 = UTILS.ShuffleTable(units) -- randomize table
|
||||||
|
for i=1,reduction do
|
||||||
|
units2[i]:Destroy(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)
|
||||||
|
:SpawnFromCoordinate(coordinate)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
if spawn then
|
||||||
|
return setdata
|
||||||
|
else
|
||||||
|
return datatable
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Load back a SET of statics from file.
|
||||||
|
-- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems.
|
||||||
|
-- @param #string Filename The name of the file.
|
||||||
|
-- @return Core.Set#SET_STATIC Set SET_STATIC containing the static objects.
|
||||||
|
function UTILS.LoadSetOfStatics(Path,Filename)
|
||||||
|
local filename = Filename or "SetOfStatics"
|
||||||
|
local datatable = SET_STATIC:New()
|
||||||
|
if UTILS.CheckFileExists(Path,filename) then
|
||||||
|
local outcome,loadeddata = UTILS.LoadFromFile(Path,Filename)
|
||||||
|
-- remove header
|
||||||
|
table.remove(loadeddata, 1)
|
||||||
|
for _id,_entry in pairs (loadeddata) do
|
||||||
|
local dataset = UTILS.Split(_entry,",")
|
||||||
|
-- staticname,position.x,position.y,position.z
|
||||||
|
local staticname = dataset[1]
|
||||||
|
local posx = tonumber(dataset[2])
|
||||||
|
local posy = tonumber(dataset[3])
|
||||||
|
local posz = tonumber(dataset[4])
|
||||||
|
local coordinate = COORDINATE:NewFromVec3({x=posx, y=posy, z=posz})
|
||||||
|
datatable:AddObject(STATIC:FindByName(staticname,false))
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
return datatable
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Load back a stationary list of statics from file.
|
||||||
|
-- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems.
|
||||||
|
-- @param #string Filename The name of the file.
|
||||||
|
-- @param #boolean Reduce If false, do not destroy the units with size=0.
|
||||||
|
-- @return #table Table of data objects (tables) containing staticname, size (0=dead else 1), coordinate and the static object.
|
||||||
|
-- Returns nil when file cannot be read.
|
||||||
|
function UTILS.LoadStationaryListOfStatics(Path,Filename,Reduce)
|
||||||
|
local reduce = true
|
||||||
|
if Reduce == false then reduce = false end
|
||||||
|
local filename = Filename or "StateListofStatics"
|
||||||
|
local datatable = {}
|
||||||
|
if UTILS.CheckFileExists(Path,filename) then
|
||||||
|
local outcome,loadeddata = UTILS.LoadFromFile(Path,Filename)
|
||||||
|
-- remove header
|
||||||
|
table.remove(loadeddata, 1)
|
||||||
|
for _id,_entry in pairs (loadeddata) do
|
||||||
|
local dataset = UTILS.Split(_entry,",")
|
||||||
|
-- staticname,units(1/0),position.x,position.y,position.z)
|
||||||
|
local staticname = dataset[1]
|
||||||
|
local size = tonumber(dataset[2])
|
||||||
|
local posx = tonumber(dataset[3])
|
||||||
|
local posy = tonumber(dataset[4])
|
||||||
|
local posz = tonumber(dataset[5])
|
||||||
|
local coordinate = COORDINATE:NewFromVec3({x=posx, y=posy, z=posz})
|
||||||
|
local data = { staticname=staticname, size=size, coordinate=coordinate, static=STATIC:FindByName(staticname,false) }
|
||||||
|
table.insert(datatable,data)
|
||||||
|
if size==0 and reduce then
|
||||||
|
local static = STATIC:FindByName(staticname,false)
|
||||||
|
if static then
|
||||||
|
static:Destroy(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
return datatable
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Heading Degrees (0-360) to Cardinal
|
||||||
|
-- @param #number Heading The heading
|
||||||
|
-- @return #string Cardinal, e.g. "NORTH"
|
||||||
|
function UTILS.BearingToCardinal(Heading)
|
||||||
|
if Heading >= 0 and Heading <= 22 then return "North"
|
||||||
|
elseif Heading >= 23 and Heading <= 66 then return "North-East"
|
||||||
|
elseif Heading >= 67 and Heading <= 101 then return "East"
|
||||||
|
elseif Heading >= 102 and Heading <= 146 then return "South-East"
|
||||||
|
elseif Heading >= 147 and Heading <= 201 then return "South"
|
||||||
|
elseif Heading >= 202 and Heading <= 246 then return "South-West"
|
||||||
|
elseif Heading >= 247 and Heading <= 291 then return "West"
|
||||||
|
elseif Heading >= 292 and Heading <= 338 then return "North-West"
|
||||||
|
elseif Heading >= 339 then return "North"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Create a BRAA NATO call string BRAA between two GROUP objects
|
||||||
|
-- @param Wrapper.Group#GROUP FromGrp GROUP object
|
||||||
|
-- @param Wrapper.Group#GROUP ToGrp GROUP object
|
||||||
|
-- @return #string Formatted BRAA NATO call
|
||||||
|
function UTILS.ToStringBRAANATO(FromGrp,ToGrp)
|
||||||
|
local BRAANATO = "Merged."
|
||||||
|
local GroupNumber = FromGrp:GetSize()
|
||||||
|
local GroupWords = "Singleton"
|
||||||
|
if GroupNumber == 2 then GroupWords = "Two-Ship"
|
||||||
|
elseif GroupNumber >= 3 then GroupWords = "Heavy"
|
||||||
|
end
|
||||||
|
local grpLeadUnit = ToGrp:GetUnit(1)
|
||||||
|
local tgtCoord = grpLeadUnit:GetCoordinate()
|
||||||
|
local currentCoord = FromGrp:GetCoordinate()
|
||||||
|
local hdg = UTILS.Round(ToGrp:GetHeading()/100,1)*100
|
||||||
|
local bearing = UTILS.Round(currentCoord:HeadingTo(tgtCoord),0)
|
||||||
|
local rangeMetres = tgtCoord:Get2DDistance(currentCoord)
|
||||||
|
local rangeNM = UTILS.Round( UTILS.MetersToNM(rangeMetres), 0)
|
||||||
|
local aspect = tgtCoord:ToStringAspect(currentCoord)
|
||||||
|
local alt = UTILS.Round(UTILS.MetersToFeet(grpLeadUnit:GetAltitude())/1000,0)--*1000
|
||||||
|
local track = UTILS.BearingToCardinal(hdg)
|
||||||
|
if rangeNM > 3 then
|
||||||
|
if aspect == "" then
|
||||||
|
BRAANATO = string.format("%s, BRA, %03d, %d miles, Angels %d, Track %s",GroupWords,bearing, rangeNM, alt, track)
|
||||||
|
else
|
||||||
|
BRAANATO = string.format("%s, BRAA, %03d, %d miles, Angels %d, %s, Track %s",GroupWords, bearing, rangeNM, alt, aspect, track)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return BRAANATO
|
||||||
|
end
|
||||||
|
|||||||
@@ -25,9 +25,11 @@
|
|||||||
-- @field #boolean isShip Airbase is a ship.
|
-- @field #boolean isShip Airbase is a ship.
|
||||||
-- @field #table parking Parking spot data.
|
-- @field #table parking Parking spot data.
|
||||||
-- @field #table parkingByID Parking spot data table with ID as key.
|
-- @field #table parkingByID Parking spot data table with ID as key.
|
||||||
-- @field #number activerwyno Active runway number (forced).
|
|
||||||
-- @field #table parkingWhitelist List of parking spot terminal IDs considered for spawning.
|
-- @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.
|
-- @field #table parkingBlacklist List of parking spot terminal IDs **not** considered for spawning.
|
||||||
|
-- @field #table runways Runways of airdromes.
|
||||||
|
-- @field #AIRBASE.Runway runwayLanding Runway used for landing.
|
||||||
|
-- @field #AIRBASE.Runway runwayTakeoff Runway used for takeoff.
|
||||||
-- @extends Wrapper.Positionable#POSITIONABLE
|
-- @extends Wrapper.Positionable#POSITIONABLE
|
||||||
|
|
||||||
--- Wrapper class to handle the DCS Airbase objects:
|
--- Wrapper class to handle the DCS Airbase objects:
|
||||||
@@ -52,7 +54,7 @@
|
|||||||
-- * @{#AIRBASE.Find}(): Find a AIRBASE instance from the _DATABASE object using a DCS Airbase object.
|
-- * @{#AIRBASE.Find}(): Find a AIRBASE instance from the _DATABASE object using a DCS Airbase object.
|
||||||
-- * @{#AIRBASE.FindByName}(): Find a AIRBASE instance from the _DATABASE object using a DCS Airbase name.
|
-- * @{#AIRBASE.FindByName}(): Find a AIRBASE instance from the _DATABASE object using a DCS Airbase name.
|
||||||
--
|
--
|
||||||
-- IMPORTANT: ONE SHOULD NEVER SANATIZE these AIRBASE OBJECT REFERENCES! (make the AIRBASE object references nil).
|
-- IMPORTANT: ONE SHOULD NEVER SANITIZE these AIRBASE OBJECT REFERENCES! (make the AIRBASE object references nil).
|
||||||
--
|
--
|
||||||
-- ## DCS Airbase APIs
|
-- ## DCS Airbase APIs
|
||||||
--
|
--
|
||||||
@@ -63,14 +65,14 @@
|
|||||||
--
|
--
|
||||||
-- @field #AIRBASE AIRBASE
|
-- @field #AIRBASE AIRBASE
|
||||||
AIRBASE = {
|
AIRBASE = {
|
||||||
ClassName="AIRBASE",
|
ClassName = "AIRBASE",
|
||||||
CategoryName = {
|
CategoryName = {
|
||||||
[Airbase.Category.AIRDROME] = "Airdrome",
|
[Airbase.Category.AIRDROME] = "Airdrome",
|
||||||
[Airbase.Category.HELIPAD] = "Helipad",
|
[Airbase.Category.HELIPAD] = "Helipad",
|
||||||
[Airbase.Category.SHIP] = "Ship",
|
[Airbase.Category.SHIP] = "Ship",
|
||||||
},
|
},
|
||||||
activerwyno=nil,
|
activerwyno = nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Enumeration to identify the airbases in the Caucasus region.
|
--- Enumeration to identify the airbases in the Caucasus region.
|
||||||
--
|
--
|
||||||
@@ -121,7 +123,7 @@ AIRBASE.Caucasus = {
|
|||||||
["Nalchik"] = "Nalchik",
|
["Nalchik"] = "Nalchik",
|
||||||
["Mozdok"] = "Mozdok",
|
["Mozdok"] = "Mozdok",
|
||||||
["Beslan"] = "Beslan",
|
["Beslan"] = "Beslan",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Airbases of the Nevada map:
|
--- Airbases of the Nevada map:
|
||||||
--
|
--
|
||||||
@@ -137,32 +139,32 @@ AIRBASE.Caucasus = {
|
|||||||
-- * AIRBASE.Nevada.Laughlin_Airport
|
-- * AIRBASE.Nevada.Laughlin_Airport
|
||||||
-- * AIRBASE.Nevada.Lincoln_County
|
-- * AIRBASE.Nevada.Lincoln_County
|
||||||
-- * AIRBASE.Nevada.Mesquite
|
-- * AIRBASE.Nevada.Mesquite
|
||||||
-- * AIRBASE.Nevada.Mina_Airport_3Q0
|
-- * AIRBASE.Nevada.Mina_Airport
|
||||||
-- * AIRBASE.Nevada.North_Las_Vegas
|
-- * AIRBASE.Nevada.North_Las_Vegas
|
||||||
-- * AIRBASE.Nevada.Pahute_Mesa_Airstrip
|
-- * AIRBASE.Nevada.Pahute_Mesa_Airstrip
|
||||||
-- * AIRBASE.Nevada.Tonopah_Airport
|
-- * AIRBASE.Nevada.Tonopah_Airport
|
||||||
-- * AIRBASE.Nevada.Tonopah_Test_Range_Airfield
|
-- * AIRBASE.Nevada.Tonopah_Test_Range_Airfield
|
||||||
--
|
--
|
||||||
-- @field Nevada
|
-- @field Nevada
|
||||||
AIRBASE.Nevada = {
|
AIRBASE.Nevada = {
|
||||||
["Creech_AFB"] = "Creech AFB",
|
["Creech_AFB"] = "Creech",
|
||||||
["Groom_Lake_AFB"] = "Groom Lake AFB",
|
["Groom_Lake_AFB"] = "Groom Lake",
|
||||||
["McCarran_International_Airport"] = "McCarran International Airport",
|
["McCarran_International_Airport"] = "McCarran International",
|
||||||
["Nellis_AFB"] = "Nellis AFB",
|
["Nellis_AFB"] = "Nellis",
|
||||||
["Beatty_Airport"] = "Beatty Airport",
|
["Beatty_Airport"] = "Beatty",
|
||||||
["Boulder_City_Airport"] = "Boulder City Airport",
|
["Boulder_City_Airport"] = "Boulder City",
|
||||||
["Echo_Bay"] = "Echo Bay",
|
["Echo_Bay"] = "Echo Bay",
|
||||||
["Henderson_Executive_Airport"] = "Henderson Executive Airport",
|
["Henderson_Executive_Airport"] = "Henderson Executive",
|
||||||
["Jean_Airport"] = "Jean Airport",
|
["Jean_Airport"] = "Jean",
|
||||||
["Laughlin_Airport"] = "Laughlin Airport",
|
["Laughlin_Airport"] = "Laughlin",
|
||||||
["Lincoln_County"] = "Lincoln County",
|
["Lincoln_County"] = "Lincoln County",
|
||||||
["Mesquite"] = "Mesquite",
|
["Mesquite"] = "Mesquite",
|
||||||
["Mina_Airport_3Q0"] = "Mina Airport 3Q0",
|
["Mina_Airport"] = "Mina",
|
||||||
["North_Las_Vegas"] = "North Las Vegas",
|
["North_Las_Vegas"] = "North Las Vegas",
|
||||||
["Pahute_Mesa_Airstrip"] = "Pahute Mesa Airstrip",
|
["Pahute_Mesa_Airstrip"] = "Pahute Mesa",
|
||||||
["Tonopah_Airport"] = "Tonopah Airport",
|
["Tonopah_Airport"] = "Tonopah",
|
||||||
["Tonopah_Test_Range_Airfield"] = "Tonopah Test Range Airfield",
|
["Tonopah_Test_Range_Airfield"] = "Tonopah Test Range",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Airbases of the Normandy map:
|
--- Airbases of the Normandy map:
|
||||||
--
|
--
|
||||||
@@ -316,7 +318,10 @@ AIRBASE.PersianGulf = {
|
|||||||
-- * AIRBASE.TheChannel.Lympne
|
-- * AIRBASE.TheChannel.Lympne
|
||||||
-- * AIRBASE.TheChannel.Detling
|
-- * AIRBASE.TheChannel.Detling
|
||||||
-- * AIRBASE.TheChannel.High_Halden
|
-- * AIRBASE.TheChannel.High_Halden
|
||||||
--
|
-- * AIRBASE.TheChannel.Biggin_Hill
|
||||||
|
-- * AIRBASE.TheChannel.Eastchurch
|
||||||
|
-- * AIRBASE.TheChannel.Headcorn
|
||||||
|
--
|
||||||
-- @field TheChannel
|
-- @field TheChannel
|
||||||
AIRBASE.TheChannel = {
|
AIRBASE.TheChannel = {
|
||||||
["Abbeville_Drucat"] = "Abbeville Drucat",
|
["Abbeville_Drucat"] = "Abbeville Drucat",
|
||||||
@@ -328,6 +333,9 @@ AIRBASE.TheChannel = {
|
|||||||
["Lympne"] = "Lympne",
|
["Lympne"] = "Lympne",
|
||||||
["Detling"] = "Detling",
|
["Detling"] = "Detling",
|
||||||
["High_Halden"] = "High Halden",
|
["High_Halden"] = "High Halden",
|
||||||
|
["Biggin_Hill"] = "Biggin Hill",
|
||||||
|
["Eastchurch"] = "Eastchurch",
|
||||||
|
["Headcorn"] = "Headcorn",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Airbases of the Syria map:
|
--- Airbases of the Syria map:
|
||||||
@@ -346,7 +354,6 @@ AIRBASE.TheChannel = {
|
|||||||
-- * AIRBASE.Syria.Wujah_Al_Hajar
|
-- * AIRBASE.Syria.Wujah_Al_Hajar
|
||||||
-- * AIRBASE.Syria.Al_Dumayr
|
-- * AIRBASE.Syria.Al_Dumayr
|
||||||
-- * AIRBASE.Syria.Gazipasa
|
-- * AIRBASE.Syria.Gazipasa
|
||||||
-- * AIRBASE.Syria.Ru_Convoy_4
|
|
||||||
-- * AIRBASE.Syria.Hatay
|
-- * AIRBASE.Syria.Hatay
|
||||||
-- * AIRBASE.Syria.Nicosia
|
-- * AIRBASE.Syria.Nicosia
|
||||||
-- * AIRBASE.Syria.Pinarbashi
|
-- * AIRBASE.Syria.Pinarbashi
|
||||||
@@ -364,7 +371,6 @@ AIRBASE.TheChannel = {
|
|||||||
-- * AIRBASE.Syria.Akrotiri
|
-- * AIRBASE.Syria.Akrotiri
|
||||||
-- * AIRBASE.Syria.Naqoura
|
-- * AIRBASE.Syria.Naqoura
|
||||||
-- * AIRBASE.Syria.Gaziantep
|
-- * AIRBASE.Syria.Gaziantep
|
||||||
-- * AIRBASE.Syria.CVN_71
|
|
||||||
-- * AIRBASE.Syria.Sayqal
|
-- * AIRBASE.Syria.Sayqal
|
||||||
-- * AIRBASE.Syria.Tiyas
|
-- * AIRBASE.Syria.Tiyas
|
||||||
-- * AIRBASE.Syria.Shayrat
|
-- * AIRBASE.Syria.Shayrat
|
||||||
@@ -385,6 +391,17 @@ AIRBASE.TheChannel = {
|
|||||||
-- * AIRBASE.Syria.Beirut_Rafic_Hariri
|
-- * AIRBASE.Syria.Beirut_Rafic_Hariri
|
||||||
-- * AIRBASE.Syria.An_Nasiriyah
|
-- * AIRBASE.Syria.An_Nasiriyah
|
||||||
-- * AIRBASE.Syria.Abu_al_Duhur
|
-- * AIRBASE.Syria.Abu_al_Duhur
|
||||||
|
-- * AIRBASE.Syria.At_Tanf
|
||||||
|
-- * AIRBASE.Syria.H3
|
||||||
|
-- * AIRBASE.Syria.H3_Northwest
|
||||||
|
-- * AIRBASE.Syria.H3_Southwest
|
||||||
|
-- * AIRBASE.Syria.Kharab_Ishk
|
||||||
|
-- * AIRBASE.Syria.Raj_al_Issa_East
|
||||||
|
-- * AIRBASE.Syria.Raj_al_Issa_West
|
||||||
|
-- * AIRBASE.Syria.Ruwayshid
|
||||||
|
-- * AIRBASE.Syria.Sanliurfa
|
||||||
|
-- * AIRBASE.Syria.Tal_Siman
|
||||||
|
-- * AIRBASE.Syria.Deir_ez_Zor
|
||||||
--
|
--
|
||||||
--@field Syria
|
--@field Syria
|
||||||
AIRBASE.Syria={
|
AIRBASE.Syria={
|
||||||
@@ -402,7 +419,7 @@ AIRBASE.Syria={
|
|||||||
["Wujah_Al_Hajar"]="Wujah Al Hajar",
|
["Wujah_Al_Hajar"]="Wujah Al Hajar",
|
||||||
["Al_Dumayr"]="Al-Dumayr",
|
["Al_Dumayr"]="Al-Dumayr",
|
||||||
["Gazipasa"]="Gazipasa",
|
["Gazipasa"]="Gazipasa",
|
||||||
["Ru_Convoy_4"]="Ru Convoy-4",
|
--["Ru_Convoy_4"]="Ru Convoy-4",
|
||||||
["Hatay"]="Hatay",
|
["Hatay"]="Hatay",
|
||||||
["Nicosia"]="Nicosia",
|
["Nicosia"]="Nicosia",
|
||||||
["Pinarbashi"]="Pinarbashi",
|
["Pinarbashi"]="Pinarbashi",
|
||||||
@@ -440,10 +457,19 @@ AIRBASE.Syria={
|
|||||||
["Beirut_Rafic_Hariri"]="Beirut-Rafic Hariri",
|
["Beirut_Rafic_Hariri"]="Beirut-Rafic Hariri",
|
||||||
["An_Nasiriyah"]="An Nasiriyah",
|
["An_Nasiriyah"]="An Nasiriyah",
|
||||||
["Abu_al_Duhur"]="Abu al-Duhur",
|
["Abu_al_Duhur"]="Abu al-Duhur",
|
||||||
|
["At_Tanf"]="At Tanf",
|
||||||
|
["H3"]="H3",
|
||||||
|
["H3_Northwest"]="H3 Northwest",
|
||||||
|
["H3_Southwest"]="H3 Southwest",
|
||||||
|
["Kharab_Ishk"]="Kharab Ishk",
|
||||||
|
["Raj_al_Issa_East"]="Raj al Issa East",
|
||||||
|
["Raj_al_Issa_West"]="Raj al Issa West",
|
||||||
|
["Ruwayshid"]="Ruwayshid",
|
||||||
|
["Sanliurfa"]="Sanliurfa",
|
||||||
|
["Tal_Siman"]="Tal Siman",
|
||||||
|
["Deir_ez_Zor"] = "Deir ez-Zor",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- Airbases of the Mariana Islands map:
|
--- Airbases of the Mariana Islands map:
|
||||||
--
|
--
|
||||||
-- * AIRBASE.MarianaIslands.Rota_Intl
|
-- * AIRBASE.MarianaIslands.Rota_Intl
|
||||||
@@ -453,16 +479,48 @@ AIRBASE.Syria={
|
|||||||
-- * AIRBASE.MarianaIslands.Tinian_Intl
|
-- * AIRBASE.MarianaIslands.Tinian_Intl
|
||||||
-- * AIRBASE.MarianaIslands.Olf_Orote
|
-- * AIRBASE.MarianaIslands.Olf_Orote
|
||||||
--
|
--
|
||||||
--@field MarianaIslands
|
-- @field MarianaIslands
|
||||||
AIRBASE.MarianaIslands={
|
AIRBASE.MarianaIslands = {
|
||||||
["Rota_Intl"]="Rota Intl",
|
["Rota_Intl"] = "Rota Intl",
|
||||||
["Andersen_AFB"]="Andersen AFB",
|
["Andersen_AFB"] = "Andersen AFB",
|
||||||
["Antonio_B_Won_Pat_Intl"]="Antonio B. Won Pat Intl",
|
["Antonio_B_Won_Pat_Intl"] = "Antonio B. Won Pat Intl",
|
||||||
["Saipan_Intl"]="Saipan Intl",
|
["Saipan_Intl"] = "Saipan Intl",
|
||||||
["Tinian_Intl"]="Tinian Intl",
|
["Tinian_Intl"] = "Tinian Intl",
|
||||||
["Olf_Orote"]="Olf Orote",
|
["Olf_Orote"] = "Olf Orote",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
--- Airbases of the South Atlantic map:
|
||||||
|
--
|
||||||
|
-- * AIRBASE.SouthAtlantic.Port_Stanley
|
||||||
|
-- * AIRBASE.SouthAtlantic.Mount_Pleasant
|
||||||
|
-- * AIRBASE.SouthAtlantic.San_Carlos_FOB
|
||||||
|
-- * AIRBASE.SouthAtlantic.Rio_Grande
|
||||||
|
-- * AIRBASE.SouthAtlantic.Rio_Gallegos
|
||||||
|
-- * AIRBASE.SouthAtlantic.Ushuaia
|
||||||
|
-- * AIRBASE.SouthAtlantic.Ushuaia_Helo_Port
|
||||||
|
-- * AIRBASE.SouthAtlantic.Punta_Arenas
|
||||||
|
-- * AIRBASE.SouthAtlantic.Pampa_Guanaco
|
||||||
|
-- * AIRBASE.SouthAtlantic.San_Julian
|
||||||
|
-- * AIRBASE.SouthAtlantic.Puerto_Williams
|
||||||
|
-- * AIRBASE.SouthAtlantic.Puerto_Natales
|
||||||
|
-- * AIRBASE.SouthAtlantic.El_Calafate
|
||||||
|
--
|
||||||
|
--@field MarianaIslands
|
||||||
|
AIRBASE.SouthAtlantic={
|
||||||
|
["Port_Stanley"]="Port Stanley",
|
||||||
|
["Mount_Pleasant"]="Mount Pleasant",
|
||||||
|
["San_Carlos_FOB"]="San Carlos FOB",
|
||||||
|
["Rio_Grande"]="Rio Grande",
|
||||||
|
["Rio_Gallegos"]="Rio Gallegos",
|
||||||
|
["Ushuaia"]="Ushuaia",
|
||||||
|
["Ushuaia_Helo_Port"]="Ushuaia Helo Port",
|
||||||
|
["Punta_Arenas"]="Punta Arenas",
|
||||||
|
["Pampa_Guanaco"]="Pampa Guanaco",
|
||||||
|
["San_Julian"]="San Julian",
|
||||||
|
["Puerto_Williams"]="Puerto Williams",
|
||||||
|
["Puerto_Natales"]="Puerto Natales",
|
||||||
|
["El_Calafate"]="El Calafate",
|
||||||
|
}
|
||||||
|
|
||||||
--- AIRBASE.ParkingSpot ".Coordinate, ".TerminalID", ".TerminalType", ".TOAC", ".Free", ".TerminalID0", ".DistToRwy".
|
--- AIRBASE.ParkingSpot ".Coordinate, ".TerminalID", ".TerminalType", ".TOAC", ".Free", ".TerminalID0", ".DistToRwy".
|
||||||
-- @type AIRBASE.ParkingSpot
|
-- @type AIRBASE.ParkingSpot
|
||||||
@@ -473,6 +531,14 @@ AIRBASE.MarianaIslands={
|
|||||||
-- @field #boolean Free This spot is currently free, i.e. there is no alive aircraft on it at the present moment.
|
-- @field #boolean Free This spot is currently free, i.e. there is no alive aircraft on it at the present moment.
|
||||||
-- @field #number TerminalID0 Unknown what this means. If you know, please tell us!
|
-- @field #number TerminalID0 Unknown what this means. If you know, please tell us!
|
||||||
-- @field #number DistToRwy Distance to runway in meters. Currently bugged and giving the same number as the TerminalID.
|
-- @field #number DistToRwy Distance to runway in meters. Currently bugged and giving the same number as the TerminalID.
|
||||||
|
-- @field #string AirbaseName Name of the airbase.
|
||||||
|
-- @field #number MarkerID Numerical ID of marker placed at parking spot.
|
||||||
|
-- @field Wrapper.Marker#MARKER Marker The marker on the F10 map.
|
||||||
|
-- @field #string ClientSpot If `true`, this is a parking spot of a client aircraft.
|
||||||
|
-- @field #string ClientName Client unit name of this spot.
|
||||||
|
-- @field #string Status Status of spot e.g. `AIRBASE.SpotStatus.FREE`.
|
||||||
|
-- @field #string OccupiedBy Name of the aircraft occupying the spot or "unknown". Can be *nil* if spot is not occupied.
|
||||||
|
-- @field #string ReservedBy Name of the aircraft for which this spot is reserved. Can be *nil* if spot is not reserved.
|
||||||
|
|
||||||
--- Terminal Types of parking spots. See also https://wiki.hoggitworld.com/view/DCS_func_getParking
|
--- Terminal Types of parking spots. See also https://wiki.hoggitworld.com/view/DCS_func_getParking
|
||||||
--
|
--
|
||||||
@@ -507,13 +573,30 @@ AIRBASE.TerminalType = {
|
|||||||
FighterAircraft=244,
|
FighterAircraft=244,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
--- Status of a parking spot.
|
||||||
|
-- @type AIRBASE.SpotStatus
|
||||||
|
-- @field #string FREE Spot is free.
|
||||||
|
-- @field #string OCCUPIED Spot is occupied.
|
||||||
|
-- @field #string RESERVED Spot is reserved.
|
||||||
|
AIRBASE.SpotStatus = {
|
||||||
|
FREE="Free",
|
||||||
|
OCCUPIED="Occupied",
|
||||||
|
RESERVED="Reserved",
|
||||||
|
}
|
||||||
|
|
||||||
--- Runway data.
|
--- Runway data.
|
||||||
-- @type AIRBASE.Runway
|
-- @type AIRBASE.Runway
|
||||||
-- @field #number heading Heading of the runway in degrees.
|
-- @field #string name Runway name.
|
||||||
-- @field #string idx Runway ID: heading 070° ==> idx="07".
|
-- @field #string idx Runway ID: heading 070° ==> idx="07".
|
||||||
|
-- @field #number heading True heading of the runway in degrees.
|
||||||
|
-- @field #number magheading Magnetic heading of the runway in degrees. This is what is marked on the runway.
|
||||||
-- @field #number length Length of runway in meters.
|
-- @field #number length Length of runway in meters.
|
||||||
|
-- @field #number width Width of runway in meters.
|
||||||
|
-- @field Core.Zone#ZONE_POLYGON zone Runway zone.
|
||||||
|
-- @field Core.Point#COORDINATE center Center of the runway.
|
||||||
-- @field Core.Point#COORDINATE position Position of runway start.
|
-- @field Core.Point#COORDINATE position Position of runway start.
|
||||||
-- @field Core.Point#COORDINATE endpoint End point of runway.
|
-- @field Core.Point#COORDINATE endpoint End point of runway.
|
||||||
|
-- @field #boolean isLeft If `true`, this is the left of two parallel runways. If `false`, this is the right of two runways. If `nil`, no parallel runway exists.
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- Registration
|
-- Registration
|
||||||
@@ -537,6 +620,9 @@ function AIRBASE:Register(AirbaseName)
|
|||||||
-- Get descriptors.
|
-- Get descriptors.
|
||||||
self.descriptors=self:GetDesc()
|
self.descriptors=self:GetDesc()
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
--self:I({airbase=AirbaseName, descriptors=self.descriptors})
|
||||||
|
|
||||||
-- Category.
|
-- Category.
|
||||||
self.category=self.descriptors and self.descriptors.category or Airbase.Category.AIRDROME
|
self.category=self.descriptors and self.descriptors.category or Airbase.Category.AIRDROME
|
||||||
|
|
||||||
@@ -553,13 +639,23 @@ function AIRBASE:Register(AirbaseName)
|
|||||||
self.isShip=false
|
self.isShip=false
|
||||||
self.category=Airbase.Category.HELIPAD
|
self.category=Airbase.Category.HELIPAD
|
||||||
_DATABASE:AddStatic(AirbaseName)
|
_DATABASE:AddStatic(AirbaseName)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
self:E("ERROR: Unknown airbase category!")
|
self:E("ERROR: Unknown airbase category!")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Init Runways.
|
||||||
|
self:_InitRunways()
|
||||||
|
|
||||||
|
-- Set the active runways based on wind direction.
|
||||||
|
if self.isAirdrome then
|
||||||
|
self:SetActiveRunway()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Init parking spots.
|
||||||
self:_InitParkingSpots()
|
self:_InitParkingSpots()
|
||||||
|
|
||||||
|
-- Get 2D position vector.
|
||||||
local vec2=self:GetVec2()
|
local vec2=self:GetVec2()
|
||||||
|
|
||||||
-- Init coordinate.
|
-- Init coordinate.
|
||||||
@@ -772,6 +868,42 @@ function AIRBASE:SetParkingSpotBlacklist(TerminalIdBlacklist)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Sets the ATC belonging to an airbase object to be silent and unresponsive. This is useful for disabling the award winning ATC behavior in DCS.
|
||||||
|
-- Note that this DOES NOT remove the airbase from the list. It just makes it unresponsive and silent to any radio calls to it.
|
||||||
|
-- @param #AIRBASE self
|
||||||
|
-- @param #boolean Silent If `true`, enable silent mode. If `false` or `nil`, disable silent mode.
|
||||||
|
-- @return #AIRBASE self
|
||||||
|
function AIRBASE:SetRadioSilentMode(Silent)
|
||||||
|
|
||||||
|
-- Get DCS airbase object.
|
||||||
|
local airbase=self:GetDCSObject()
|
||||||
|
|
||||||
|
-- Set mode.
|
||||||
|
if airbase then
|
||||||
|
airbase:setRadioSilentMode(Silent)
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Check whether or not the airbase has been silenced.
|
||||||
|
-- @param #AIRBASE self
|
||||||
|
-- @return #boolean If `true`, silent mode is enabled.
|
||||||
|
function AIRBASE:GetRadioSilentMode()
|
||||||
|
|
||||||
|
-- Is silent?
|
||||||
|
local silent=nil
|
||||||
|
|
||||||
|
-- Get DCS airbase object.
|
||||||
|
local airbase=self:GetDCSObject()
|
||||||
|
|
||||||
|
-- Set mode.
|
||||||
|
if airbase then
|
||||||
|
silent=airbase:getRadioSilentMode()
|
||||||
|
end
|
||||||
|
|
||||||
|
return silent
|
||||||
|
end
|
||||||
|
|
||||||
--- Get category of airbase.
|
--- Get category of airbase.
|
||||||
-- @param #AIRBASE self
|
-- @param #AIRBASE self
|
||||||
@@ -953,6 +1085,21 @@ function AIRBASE:_InitParkingSpots()
|
|||||||
self.NparkingTerminal[terminalType]=0
|
self.NparkingTerminal[terminalType]=0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Get client coordinates.
|
||||||
|
local function isClient(coord)
|
||||||
|
local clients=_DATABASE.CLIENTS
|
||||||
|
for clientname, _client in pairs(clients) do
|
||||||
|
local client=_client --Wrapper.Client#CLIENT
|
||||||
|
if client and client.SpawnCoord then
|
||||||
|
local dist=client.SpawnCoord:Get2DDistance(coord)
|
||||||
|
if dist<2 then
|
||||||
|
return true, clientname
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false, nil
|
||||||
|
end
|
||||||
|
|
||||||
-- Put coordinates of parking spots into table.
|
-- Put coordinates of parking spots into table.
|
||||||
for _,spot in pairs(parkingdata) do
|
for _,spot in pairs(parkingdata) do
|
||||||
|
|
||||||
@@ -966,6 +1113,8 @@ function AIRBASE:_InitParkingSpots()
|
|||||||
park.TerminalID0=spot.Term_Index_0
|
park.TerminalID0=spot.Term_Index_0
|
||||||
park.TerminalType=spot.Term_Type
|
park.TerminalType=spot.Term_Type
|
||||||
park.TOAC=spot.TO_AC
|
park.TOAC=spot.TO_AC
|
||||||
|
park.ClientSpot, park.ClientName=isClient(park.Coordinate)
|
||||||
|
park.AirbaseName=self.AirbaseName
|
||||||
|
|
||||||
self.NparkingTotal=self.NparkingTotal+1
|
self.NparkingTotal=self.NparkingTotal+1
|
||||||
|
|
||||||
@@ -1024,6 +1173,7 @@ function AIRBASE:GetParkingSpotsTable(termtype)
|
|||||||
|
|
||||||
spot.Free=_isfree(_spot) -- updated
|
spot.Free=_isfree(_spot) -- updated
|
||||||
spot.TOAC=_spot.TO_AC -- updated
|
spot.TOAC=_spot.TO_AC -- updated
|
||||||
|
spot.AirbaseName=self.AirbaseName
|
||||||
|
|
||||||
table.insert(spots, spot)
|
table.insert(spots, spot)
|
||||||
|
|
||||||
@@ -1060,6 +1210,7 @@ function AIRBASE:GetFreeParkingSpotsTable(termtype, allowTOAC)
|
|||||||
|
|
||||||
spot.Free=true -- updated
|
spot.Free=true -- updated
|
||||||
spot.TOAC=_spot.TO_AC -- updated
|
spot.TOAC=_spot.TO_AC -- updated
|
||||||
|
spot.AirbaseName=self.AirbaseName
|
||||||
|
|
||||||
table.insert(freespots, spot)
|
table.insert(freespots, spot)
|
||||||
|
|
||||||
@@ -1141,7 +1292,7 @@ end
|
|||||||
-- @param #table parkingdata (Optional) Parking spots data table. If not given it is automatically derived from the GetParkingSpotsTable() function.
|
-- @param #table parkingdata (Optional) Parking spots data table. If not given it is automatically derived from the GetParkingSpotsTable() function.
|
||||||
-- @return #table Table of coordinates and terminal IDs of free parking spots. Each table entry has the elements .Coordinate and .TerminalID.
|
-- @return #table Table of coordinates and terminal IDs of free parking spots. Each table entry has the elements .Coordinate and .TerminalID.
|
||||||
function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nspots, parkingdata)
|
function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nspots, parkingdata)
|
||||||
|
|
||||||
-- Init default
|
-- Init default
|
||||||
scanradius=scanradius or 50
|
scanradius=scanradius or 50
|
||||||
if scanunits==nil then
|
if scanunits==nil then
|
||||||
@@ -1197,7 +1348,7 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius,
|
|||||||
az = 17 -- width
|
az = 17 -- width
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- Number of spots we are looking for. Note that, e.g. grouping can require a number different from the group size!
|
-- Number of spots we are looking for. Note that, e.g. grouping can require a number different from the group size!
|
||||||
local _nspots=nspots or group:GetSize()
|
local _nspots=nspots or group:GetSize()
|
||||||
|
|
||||||
@@ -1304,14 +1455,14 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius,
|
|||||||
|
|
||||||
--_spot:MarkToAll(string.format("Parking spot %d free=%s", parkingspot.TerminalID, tostring(not occupied)))
|
--_spot:MarkToAll(string.format("Parking spot %d free=%s", parkingspot.TerminalID, tostring(not occupied)))
|
||||||
if occupied then
|
if occupied then
|
||||||
self:I(string.format("%s: Parking spot id %d occupied.", airport, _termid))
|
self:T(string.format("%s: Parking spot id %d occupied.", airport, _termid))
|
||||||
else
|
else
|
||||||
self:I(string.format("%s: Parking spot id %d free.", airport, _termid))
|
self:T(string.format("%s: Parking spot id %d free.", airport, _termid))
|
||||||
if nvalid<_nspots then
|
if nvalid<_nspots then
|
||||||
table.insert(validspots, {Coordinate=_spot, TerminalID=_termid})
|
table.insert(validspots, {Coordinate=_spot, TerminalID=_termid})
|
||||||
end
|
end
|
||||||
nvalid=nvalid+1
|
nvalid=nvalid+1
|
||||||
self:I(string.format("%s: Parking spot id %d free. Nfree=%d/%d.", airport, _termid, nvalid,_nspots))
|
self:T(string.format("%s: Parking spot id %d free. Nfree=%d/%d.", airport, _termid, nvalid,_nspots))
|
||||||
end
|
end
|
||||||
|
|
||||||
end -- loop over units
|
end -- loop over units
|
||||||
@@ -1325,7 +1476,7 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius,
|
|||||||
|
|
||||||
-- Retrun spots we found, even if there were not enough.
|
-- Retrun spots we found, even if there were not enough.
|
||||||
return validspots
|
return validspots
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Check black and white lists.
|
--- Check black and white lists.
|
||||||
@@ -1344,7 +1495,7 @@ function AIRBASE:_CheckParkingLists(TerminalID)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- Check if a whitelist was defined.
|
-- Check if a whitelist was defined.
|
||||||
if self.parkingWhitelist and #self.parkingWhitelist>0 then
|
if self.parkingWhitelist and #self.parkingWhitelist>0 then
|
||||||
for _,terminalID in pairs(self.parkingWhitelist or {}) do
|
for _,terminalID in pairs(self.parkingWhitelist or {}) do
|
||||||
@@ -1411,6 +1562,231 @@ end
|
|||||||
-- Runway
|
-- Runway
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Get runways.
|
||||||
|
-- @param #AIRBASE self
|
||||||
|
-- @return #table Runway data.
|
||||||
|
function AIRBASE:GetRunways()
|
||||||
|
return self.runways or {}
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get runway by its name.
|
||||||
|
-- @param #AIRBASE self
|
||||||
|
-- @param #string Name Name of the runway, e.g. "31" or "21L".
|
||||||
|
-- @return #AIRBASE.Runway Runway data.
|
||||||
|
function AIRBASE:GetRunwayByName(Name)
|
||||||
|
|
||||||
|
if Name==nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if Name then
|
||||||
|
for _,_runway in pairs(self.runways) do
|
||||||
|
local runway=_runway --#AIRBASE.Runway
|
||||||
|
|
||||||
|
-- Name including L or R, e.g. "31L".
|
||||||
|
local name=self:GetRunwayName(runway)
|
||||||
|
|
||||||
|
if name==Name:upper() then
|
||||||
|
return runway
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self:E("ERROR: Could not find runway with name "..tostring(Name))
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Init runways.
|
||||||
|
-- @param #AIRBASE self
|
||||||
|
-- @param #boolean IncludeInverse If `true` or `nil`, include inverse runways.
|
||||||
|
-- @return #table Runway data.
|
||||||
|
function AIRBASE:_InitRunways(IncludeInverse)
|
||||||
|
|
||||||
|
-- Default is true.
|
||||||
|
if IncludeInverse==nil then
|
||||||
|
IncludeInverse=true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Runway table.
|
||||||
|
local Runways={}
|
||||||
|
|
||||||
|
if self:GetAirbaseCategory()~=Airbase.Category.AIRDROME then
|
||||||
|
self.runways={}
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Function to create a runway data table.
|
||||||
|
local function _createRunway(name, course, width, length, center)
|
||||||
|
|
||||||
|
-- Bearing in rad.
|
||||||
|
local bearing=-1*course
|
||||||
|
|
||||||
|
-- Heading in degrees.
|
||||||
|
local heading=math.deg(bearing)
|
||||||
|
|
||||||
|
-- Data table.
|
||||||
|
local runway={} --#AIRBASE.Runway
|
||||||
|
runway.name=string.format("%02d", tonumber(name))
|
||||||
|
runway.magheading=tonumber(runway.name)*10
|
||||||
|
runway.heading=heading
|
||||||
|
runway.width=width or 0
|
||||||
|
runway.length=length or 0
|
||||||
|
runway.center=COORDINATE:NewFromVec3(center)
|
||||||
|
|
||||||
|
-- Ensure heading is [0,360]
|
||||||
|
if runway.heading>360 then
|
||||||
|
runway.heading=runway.heading-360
|
||||||
|
elseif runway.heading<0 then
|
||||||
|
runway.heading=runway.heading+360
|
||||||
|
end
|
||||||
|
|
||||||
|
-- For example at Nellis, DCS reports two runways, i.e. 03 and 21, BUT the "course" of both is -0.700 rad = 40 deg!
|
||||||
|
-- As a workaround, I check the difference between the "magnetic" heading derived from the name and the true heading.
|
||||||
|
-- If this is too large then very likely the "inverse" heading is the one we are looking for.
|
||||||
|
if math.abs(runway.heading-runway.magheading)>60 then
|
||||||
|
self:T(string.format("WARNING: Runway %s: heading=%.1f magheading=%.1f", runway.name, runway.heading, runway.magheading))
|
||||||
|
runway.heading=runway.heading-180
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Ensure heading is [0,360]
|
||||||
|
if runway.heading>360 then
|
||||||
|
runway.heading=runway.heading-360
|
||||||
|
elseif runway.heading<0 then
|
||||||
|
runway.heading=runway.heading+360
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Start and endpoint of runway.
|
||||||
|
runway.position=runway.center:Translate(-runway.length/2, runway.heading)
|
||||||
|
runway.endpoint=runway.center:Translate( runway.length/2, runway.heading)
|
||||||
|
|
||||||
|
local init=runway.center:GetVec3()
|
||||||
|
local width = runway.width/2
|
||||||
|
local L2=runway.length/2
|
||||||
|
|
||||||
|
local offset1 = {x = init.x + (math.cos(bearing + math.pi) * L2), y = init.z + (math.sin(bearing + math.pi) * L2)}
|
||||||
|
local offset2 = {x = init.x - (math.cos(bearing + math.pi) * L2), y = init.z - (math.sin(bearing + math.pi) * L2)}
|
||||||
|
|
||||||
|
local points={}
|
||||||
|
points[1] = {x = offset1.x + (math.cos(bearing + (math.pi/2)) * width), y = offset1.y + (math.sin(bearing + (math.pi/2)) * width)}
|
||||||
|
points[2] = {x = offset1.x + (math.cos(bearing - (math.pi/2)) * width), y = offset1.y + (math.sin(bearing - (math.pi/2)) * width)}
|
||||||
|
points[3] = {x = offset2.x + (math.cos(bearing - (math.pi/2)) * width), y = offset2.y + (math.sin(bearing - (math.pi/2)) * width)}
|
||||||
|
points[4] = {x = offset2.x + (math.cos(bearing + (math.pi/2)) * width), y = offset2.y + (math.sin(bearing + (math.pi/2)) * width)}
|
||||||
|
|
||||||
|
-- Runway zone.
|
||||||
|
runway.zone=ZONE_POLYGON_BASE:New(string.format("%s Runway %s", self.AirbaseName, runway.name), points)
|
||||||
|
|
||||||
|
return runway
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- Get DCS object.
|
||||||
|
local airbase=self:GetDCSObject()
|
||||||
|
|
||||||
|
if airbase then
|
||||||
|
|
||||||
|
|
||||||
|
-- Get DCS runways.
|
||||||
|
local runways=airbase:getRunways()
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
self:T2(runways)
|
||||||
|
|
||||||
|
if runways then
|
||||||
|
|
||||||
|
-- Loop over runways.
|
||||||
|
for _,rwy in pairs(runways) do
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
self:T(rwy)
|
||||||
|
|
||||||
|
-- Get runway data.
|
||||||
|
local runway=_createRunway(rwy.Name, rwy.course, rwy.width, rwy.length, rwy.position) --#AIRBASE.Runway
|
||||||
|
|
||||||
|
-- Add to table.
|
||||||
|
table.insert(Runways, runway)
|
||||||
|
|
||||||
|
-- Include "inverse" runway.
|
||||||
|
if IncludeInverse then
|
||||||
|
|
||||||
|
-- Create "inverse".
|
||||||
|
local idx=tonumber(runway.name)
|
||||||
|
local name2=tostring(idx-18)
|
||||||
|
if idx<18 then
|
||||||
|
name2=tostring(idx+18)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Create "inverse" runway.
|
||||||
|
local runway=_createRunway(name2, rwy.course-math.pi, rwy.width, rwy.length, rwy.position) --#AIRBASE.Runway
|
||||||
|
|
||||||
|
-- Add inverse to table.
|
||||||
|
table.insert(Runways, runway)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Look for identical (parallel) runways, e.g. 03L and 03R at Nellis.
|
||||||
|
local rpairs={}
|
||||||
|
for i,_ri in pairs(Runways) do
|
||||||
|
local ri=_ri --#AIRBASE.Runway
|
||||||
|
for j,_rj in pairs(Runways) do
|
||||||
|
local rj=_rj --#AIRBASE.Runway
|
||||||
|
if i<j then
|
||||||
|
if ri.name==rj.name then
|
||||||
|
rpairs[i]=j
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function isLeft(a, b, c)
|
||||||
|
--return ((b.x - a.x)*(c.z - a.z) - (b.z - a.z)*(c.x - a.x)) > 0
|
||||||
|
return ((b.z - a.z)*(c.x - a.x) - (b.x - a.x)*(c.z - a.z)) > 0
|
||||||
|
end
|
||||||
|
|
||||||
|
for i,j in pairs(rpairs) do
|
||||||
|
local ri=Runways[i] --#AIRBASE.Runway
|
||||||
|
local rj=Runways[j] --#AIRBASE.Runway
|
||||||
|
|
||||||
|
-- Draw arrow.
|
||||||
|
--ri.center:ArrowToAll(rj.center)
|
||||||
|
|
||||||
|
local c0=ri.center
|
||||||
|
|
||||||
|
-- Vector in the direction of the runway.
|
||||||
|
local a=UTILS.VecTranslate(c0, 1000, ri.heading)
|
||||||
|
|
||||||
|
-- Vector from runway i to runway j.
|
||||||
|
local b=UTILS.VecSubstract(rj.center, ri.center)
|
||||||
|
b=UTILS.VecAdd(ri.center, b)
|
||||||
|
|
||||||
|
-- Check if rj is left of ri.
|
||||||
|
local left=isLeft(c0, a, b)
|
||||||
|
|
||||||
|
--env.info(string.format("Found pair %s: i=%d, j=%d, left==%s", ri.name, i, j, tostring(left)))
|
||||||
|
|
||||||
|
if left then
|
||||||
|
ri.isLeft=false
|
||||||
|
rj.isLeft=true
|
||||||
|
else
|
||||||
|
ri.isLeft=true
|
||||||
|
rj.isLeft=false
|
||||||
|
end
|
||||||
|
|
||||||
|
--break
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set runways.
|
||||||
|
self.runways=Runways
|
||||||
|
|
||||||
|
return Runways
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Get runways data. Only for airdromes!
|
--- Get runways data. Only for airdromes!
|
||||||
-- @param #AIRBASE self
|
-- @param #AIRBASE self
|
||||||
-- @param #number magvar (Optional) Magnetic variation in degrees.
|
-- @param #number magvar (Optional) Magnetic variation in degrees.
|
||||||
@@ -1454,7 +1830,7 @@ function AIRBASE:GetRunwayData(magvar, mark)
|
|||||||
name==AIRBASE.PersianGulf.Abu_Dhabi_International_Airport or
|
name==AIRBASE.PersianGulf.Abu_Dhabi_International_Airport or
|
||||||
name==AIRBASE.PersianGulf.Dubai_Intl or
|
name==AIRBASE.PersianGulf.Dubai_Intl or
|
||||||
name==AIRBASE.PersianGulf.Shiraz_International_Airport or
|
name==AIRBASE.PersianGulf.Shiraz_International_Airport or
|
||||||
name==AIRBASE.PersianGulf.Kish_International_Airport or
|
name==AIRBASE.PersianGulf.Kish_International_Airport or
|
||||||
name==AIRBASE.MarianaIslands.Andersen_AFB then
|
name==AIRBASE.MarianaIslands.Andersen_AFB then
|
||||||
|
|
||||||
-- 1-->4, 2-->3, 3-->2, 4-->1
|
-- 1-->4, 2-->3, 3-->2, 4-->1
|
||||||
@@ -1578,26 +1954,100 @@ function AIRBASE:GetRunwayData(magvar, mark)
|
|||||||
return runways
|
return runways
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set the active runway in case it cannot be determined by the wind direction.
|
--- Set the active runway for landing and takeoff.
|
||||||
-- @param #AIRBASE self
|
-- @param #AIRBASE self
|
||||||
-- @param #number iactive Number of the active runway in the runway data table.
|
-- @param #string Name Name of the runway, e.g. "31" or "02L" or "90R". If not given, the runway is determined from the wind direction.
|
||||||
function AIRBASE:SetActiveRunway(iactive)
|
-- @param #boolean PreferLeft If `true`, perfer the left runway. If `false`, prefer the right runway. If `nil` (default), do not care about left or right.
|
||||||
self.activerwyno=iactive
|
function AIRBASE:SetActiveRunway(Name, PreferLeft)
|
||||||
|
|
||||||
|
self:SetActiveRunwayTakeoff(Name, PreferLeft)
|
||||||
|
|
||||||
|
self:SetActiveRunwayLanding(Name,PreferLeft)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get the active runway based on current wind direction.
|
--- Set the active runway for landing.
|
||||||
-- @param #AIRBASE self
|
-- @param #AIRBASE self
|
||||||
-- @param #number magvar (Optional) Magnetic variation in degrees.
|
-- @param #string Name Name of the runway, e.g. "31" or "02L" or "90R". If not given, the runway is determined from the wind direction.
|
||||||
-- @return #AIRBASE.Runway Active runway data table.
|
-- @param #boolean PreferLeft If `true`, perfer the left runway. If `false`, prefer the right runway. If `nil` (default), do not care about left or right.
|
||||||
function AIRBASE:GetActiveRunway(magvar)
|
-- @return #AIRBASE.Runway The active runway for landing.
|
||||||
|
function AIRBASE:SetActiveRunwayLanding(Name, PreferLeft)
|
||||||
|
|
||||||
-- Get runways data (initialize if necessary).
|
local runway=self:GetRunwayByName(Name)
|
||||||
local runways=self:GetRunwayData(magvar)
|
|
||||||
|
if not runway then
|
||||||
-- Return user forced active runway if it was set.
|
runway=self:GetRunwayIntoWind(PreferLeft)
|
||||||
if self.activerwyno then
|
|
||||||
return runways[self.activerwyno]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if runway then
|
||||||
|
self:T(string.format("%s: Setting active runway for landing as %s", self.AirbaseName, self:GetRunwayName(runway)))
|
||||||
|
else
|
||||||
|
self:E("ERROR: Could not set the runway for landing!")
|
||||||
|
end
|
||||||
|
|
||||||
|
self.runwayLanding=runway
|
||||||
|
|
||||||
|
return runway
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the active runways.
|
||||||
|
-- @param #AIRBASE self
|
||||||
|
-- @return #AIRBASE.Runway The active runway for landing.
|
||||||
|
-- @return #AIRBASE.Runway The active runway for takeoff.
|
||||||
|
function AIRBASE:GetActiveRunway()
|
||||||
|
return self.runwayLanding, self.runwayTakeoff
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Get the active runway for landing.
|
||||||
|
-- @param #AIRBASE self
|
||||||
|
-- @return #AIRBASE.Runway The active runway for landing.
|
||||||
|
function AIRBASE:GetActiveRunwayLanding()
|
||||||
|
return self.runwayLanding
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the active runway for takeoff.
|
||||||
|
-- @param #AIRBASE self
|
||||||
|
-- @return #AIRBASE.Runway The active runway for takeoff.
|
||||||
|
function AIRBASE:GetActiveRunwayTakeoff()
|
||||||
|
return self.runwayTakeoff
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Set the active runway for takeoff.
|
||||||
|
-- @param #AIRBASE self
|
||||||
|
-- @param #string Name Name of the runway, e.g. "31" or "02L" or "90R". If not given, the runway is determined from the wind direction.
|
||||||
|
-- @param #boolean PreferLeft If `true`, perfer the left runway. If `false`, prefer the right runway. If `nil` (default), do not care about left or right.
|
||||||
|
-- @return #AIRBASE.Runway The active runway for landing.
|
||||||
|
function AIRBASE:SetActiveRunwayTakeoff(Name, PreferLeft)
|
||||||
|
|
||||||
|
local runway=self:GetRunwayByName(Name)
|
||||||
|
|
||||||
|
if not runway then
|
||||||
|
runway=self:GetRunwayIntoWind(PreferLeft)
|
||||||
|
end
|
||||||
|
|
||||||
|
if runway then
|
||||||
|
self:T(string.format("%s: Setting active runway for takeoff as %s", self.AirbaseName, self:GetRunwayName(runway)))
|
||||||
|
else
|
||||||
|
self:E("ERROR: Could not set the runway for takeoff!")
|
||||||
|
end
|
||||||
|
|
||||||
|
self.runwayTakeoff=runway
|
||||||
|
|
||||||
|
return runway
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Get the runway where aircraft would be taking of or landing into the direction of the wind.
|
||||||
|
-- NOTE that this requires the wind to be non-zero as set in the mission editor.
|
||||||
|
-- @param #AIRBASE self
|
||||||
|
-- @param #boolean PreferLeft If `true`, perfer the left runway. If `false`, prefer the right runway. If `nil` (default), do not care about left or right.
|
||||||
|
-- @return #AIRBASE.Runway Active runway data table.
|
||||||
|
function AIRBASE:GetRunwayIntoWind(PreferLeft)
|
||||||
|
|
||||||
|
-- Get runway data.
|
||||||
|
local runways=self:GetRunways()
|
||||||
|
|
||||||
-- Get wind vector.
|
-- Get wind vector.
|
||||||
local Vwind=self:GetCoordinate():GetWindWithTurbulenceVec3()
|
local Vwind=self:GetCoordinate():GetWindWithTurbulenceVec3()
|
||||||
@@ -1618,33 +2068,64 @@ function AIRBASE:GetActiveRunway(magvar)
|
|||||||
local dotmin=nil
|
local dotmin=nil
|
||||||
for i,_runway in pairs(runways) do
|
for i,_runway in pairs(runways) do
|
||||||
local runway=_runway --#AIRBASE.Runway
|
local runway=_runway --#AIRBASE.Runway
|
||||||
|
|
||||||
|
if PreferLeft==nil or PreferLeft==runway.isLeft then
|
||||||
|
|
||||||
-- Angle in rad.
|
-- Angle in rad.
|
||||||
local alpha=math.rad(runway.heading)
|
local alpha=math.rad(runway.heading)
|
||||||
|
|
||||||
-- Runway vector.
|
-- Runway vector.
|
||||||
local Vrunway={x=math.cos(alpha), y=0, z=math.sin(alpha)}
|
local Vrunway={x=math.cos(alpha), y=0, z=math.sin(alpha)}
|
||||||
|
|
||||||
-- Dot product: parallel component of the two vectors.
|
-- Dot product: parallel component of the two vectors.
|
||||||
local dot=UTILS.VecDot(Vwind, Vrunway)
|
local dot=UTILS.VecDot(Vwind, Vrunway)
|
||||||
|
|
||||||
-- Debug.
|
-- New min?
|
||||||
--env.info(string.format("runway=%03d° dot=%.3f", runway.heading, dot))
|
if dotmin==nil or dot<dotmin then
|
||||||
|
dotmin=dot
|
||||||
-- New min?
|
iact=i
|
||||||
if dotmin==nil or dot<dotmin then
|
end
|
||||||
dotmin=dot
|
|
||||||
iact=i
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
self:E("WARNING: Norm of wind is zero! Cannot determine active runway based on wind direction.")
|
self:E("WARNING: Norm of wind is zero! Cannot determine runway based on wind direction")
|
||||||
end
|
end
|
||||||
|
|
||||||
return runways[iact]
|
return runways[iact]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get name of a given runway, e.g. "31L".
|
||||||
|
-- @param #AIRBASE self
|
||||||
|
-- @param #AIRBASE.Runway Runway The runway. Default is the active runway.
|
||||||
|
-- @param #boolean LongLeftRight If `true`, return "Left" or "Right" instead of "L" or "R".
|
||||||
|
-- @return #string Name of the runway or "XX" if it could not be found.
|
||||||
|
function AIRBASE:GetRunwayName(Runway, LongLeftRight)
|
||||||
|
|
||||||
|
Runway=Runway or self:GetActiveRunway()
|
||||||
|
|
||||||
|
local name="XX"
|
||||||
|
if Runway then
|
||||||
|
name=Runway.name
|
||||||
|
if Runway.isLeft==true then
|
||||||
|
if LongLeftRight then
|
||||||
|
name=name.." Left"
|
||||||
|
else
|
||||||
|
name=name.."L"
|
||||||
|
end
|
||||||
|
elseif Runway.isLeft==false then
|
||||||
|
if LongLeftRight then
|
||||||
|
name=name.." Right"
|
||||||
|
else
|
||||||
|
name=name.."R"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return name
|
||||||
|
end
|
||||||
|
|
||||||
--- Function that checks if at leat one unit of a group has been spawned close to a spawn point on the runway.
|
--- Function that checks if at leat one unit of a group has been spawned close to a spawn point on the runway.
|
||||||
-- @param #AIRBASE self
|
-- @param #AIRBASE self
|
||||||
-- @param Wrapper.Group#GROUP group Group to be checked.
|
-- @param Wrapper.Group#GROUP group Group to be checked.
|
||||||
|
|||||||
@@ -13,6 +13,17 @@
|
|||||||
|
|
||||||
--- The CLIENT class
|
--- The CLIENT class
|
||||||
-- @type CLIENT
|
-- @type CLIENT
|
||||||
|
-- @field #string ClassName Name of the class.
|
||||||
|
-- @field #string ClientName Name of the client.
|
||||||
|
-- @field #string ClientBriefing Briefing.
|
||||||
|
-- @field #function ClientCallBack Callback function.
|
||||||
|
-- @field #table ClientParameters Parameters of the callback function.
|
||||||
|
-- @field #number ClientGroupID Group ID of the client.
|
||||||
|
-- @field #string ClientGroupName Group name.
|
||||||
|
-- @field #boolean ClientAlive Client alive.
|
||||||
|
-- @field #boolean ClientAlive2 Client alive 2.
|
||||||
|
-- @field #table Players Player table.
|
||||||
|
-- @field Core.Point#COORDINATE SpawnCoord Spawn coordinate from the template.
|
||||||
-- @extends Wrapper.Unit#UNIT
|
-- @extends Wrapper.Unit#UNIT
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -187,6 +187,7 @@ GROUPTEMPLATE.Takeoff = {
|
|||||||
-- @field #string GROUND_APC Infantry carriers, in particular Amoured Personell Carrier. This can be used to transport other assets.
|
-- @field #string GROUND_APC Infantry carriers, in particular Amoured Personell Carrier. This can be used to transport other assets.
|
||||||
-- @field #string GROUND_TRUCK Unarmed ground vehicles, which has the DCS "Truck" attribute.
|
-- @field #string GROUND_TRUCK Unarmed ground vehicles, which has the DCS "Truck" attribute.
|
||||||
-- @field #string GROUND_INFANTRY Ground infantry assets.
|
-- @field #string GROUND_INFANTRY Ground infantry assets.
|
||||||
|
-- @field #string GROUND_IFV Ground Infantry Fighting Vehicle.
|
||||||
-- @field #string GROUND_ARTILLERY Artillery assets.
|
-- @field #string GROUND_ARTILLERY Artillery assets.
|
||||||
-- @field #string GROUND_TANK Tanks (modern or old).
|
-- @field #string GROUND_TANK Tanks (modern or old).
|
||||||
-- @field #string GROUND_TRAIN Trains. Not that trains are **not** yet properly implemented in DCS and cannot be used currently.
|
-- @field #string GROUND_TRAIN Trains. Not that trains are **not** yet properly implemented in DCS and cannot be used currently.
|
||||||
@@ -213,6 +214,7 @@ GROUP.Attribute = {
|
|||||||
GROUND_APC="Ground_APC",
|
GROUND_APC="Ground_APC",
|
||||||
GROUND_TRUCK="Ground_Truck",
|
GROUND_TRUCK="Ground_Truck",
|
||||||
GROUND_INFANTRY="Ground_Infantry",
|
GROUND_INFANTRY="Ground_Infantry",
|
||||||
|
GROUND_IFV="Ground_IFV",
|
||||||
GROUND_ARTILLERY="Ground_Artillery",
|
GROUND_ARTILLERY="Ground_Artillery",
|
||||||
GROUND_TANK="Ground_Tank",
|
GROUND_TANK="Ground_Tank",
|
||||||
GROUND_TRAIN="Ground_Train",
|
GROUND_TRAIN="Ground_Train",
|
||||||
@@ -310,8 +312,7 @@ end
|
|||||||
|
|
||||||
--- Returns the @{DCS#Position3} position vectors indicating the point and direction vectors in 3D of the POSITIONABLE within the mission.
|
--- Returns the @{DCS#Position3} position vectors indicating the point and direction vectors in 3D of the POSITIONABLE within the mission.
|
||||||
-- @param Wrapper.Positionable#POSITIONABLE self
|
-- @param Wrapper.Positionable#POSITIONABLE self
|
||||||
-- @return DCS#Position The 3D position vectors of the POSITIONABLE.
|
-- @return DCS#Position The 3D position vectors of the POSITIONABLE or #nil if the groups not existing or alive.
|
||||||
-- @return #nil The POSITIONABLE is not existing or alive.
|
|
||||||
function GROUP:GetPositionVec3() -- Overridden from POSITIONABLE:GetPositionVec3()
|
function GROUP:GetPositionVec3() -- Overridden from POSITIONABLE:GetPositionVec3()
|
||||||
self:F2( self.PositionableName )
|
self:F2( self.PositionableName )
|
||||||
|
|
||||||
@@ -339,9 +340,7 @@ end
|
|||||||
-- If the first @{Wrapper.Unit} of the group is inactive, it will return false.
|
-- If the first @{Wrapper.Unit} of the group is inactive, it will return false.
|
||||||
--
|
--
|
||||||
-- @param #GROUP self
|
-- @param #GROUP self
|
||||||
-- @return #boolean true if the group is alive and active.
|
-- @return #boolean `true` if the group is alive *and* active, `false` if the group is alive but inactive or `#nil` if the group does not exist anymore.
|
||||||
-- @return #boolean false if the group is alive but inactive.
|
|
||||||
-- @return #nil if the group does not exist anymore.
|
|
||||||
function GROUP:IsAlive()
|
function GROUP:IsAlive()
|
||||||
self:F2( self.GroupName )
|
self:F2( self.GroupName )
|
||||||
|
|
||||||
@@ -363,8 +362,7 @@ end
|
|||||||
|
|
||||||
--- Returns if the group is activated.
|
--- Returns if the group is activated.
|
||||||
-- @param #GROUP self
|
-- @param #GROUP self
|
||||||
-- @return #boolean true if group is activated.
|
-- @return #boolean `true` if group is activated or `#nil` The group is not existing or alive.
|
||||||
-- @return #nil The group is not existing or alive.
|
|
||||||
function GROUP:IsActive()
|
function GROUP:IsActive()
|
||||||
self:F2( self.GroupName )
|
self:F2( self.GroupName )
|
||||||
|
|
||||||
@@ -388,7 +386,7 @@ end
|
|||||||
-- So all event listeners will catch the destroy event of this group for each unit in the group.
|
-- So all event listeners will catch the destroy event of this group for each unit in the group.
|
||||||
-- To raise these events, provide the `GenerateEvent` parameter.
|
-- To raise these events, provide the `GenerateEvent` parameter.
|
||||||
-- @param #GROUP self
|
-- @param #GROUP self
|
||||||
-- @param #boolean GenerateEvent If true, a crash or dead event for each unit is generated. If false, if no event is triggered. If nil, a RemoveUnit event is triggered.
|
-- @param #boolean GenerateEvent If true, a crash [AIR] or dead [GROUND] event for each unit is generated. If false, if no event is triggered. If nil, a RemoveUnit event is triggered.
|
||||||
-- @param #number delay Delay in seconds before despawning the group.
|
-- @param #number delay Delay in seconds before despawning the group.
|
||||||
-- @usage
|
-- @usage
|
||||||
-- -- Air unit example: destroy the Helicopter and generate a S_EVENT_CRASH for each unit in the Helicopter group.
|
-- -- Air unit example: destroy the Helicopter and generate a S_EVENT_CRASH for each unit in the Helicopter group.
|
||||||
@@ -412,7 +410,6 @@ function GROUP:Destroy( GenerateEvent, delay )
|
|||||||
self:F2( self.GroupName )
|
self:F2( self.GroupName )
|
||||||
|
|
||||||
if delay and delay>0 then
|
if delay and delay>0 then
|
||||||
--SCHEDULER:New(nil, GROUP.Destroy, {self, GenerateEvent}, delay)
|
|
||||||
self:ScheduleOnce(delay, GROUP.Destroy, self, GenerateEvent)
|
self:ScheduleOnce(delay, GROUP.Destroy, self, GenerateEvent)
|
||||||
else
|
else
|
||||||
|
|
||||||
@@ -534,26 +531,32 @@ function GROUP:HasAttribute(attribute, all)
|
|||||||
-- Get all units of the group.
|
-- Get all units of the group.
|
||||||
local _units=self:GetUnits()
|
local _units=self:GetUnits()
|
||||||
|
|
||||||
local _allhave=true
|
if _units then
|
||||||
local _onehas=false
|
|
||||||
|
|
||||||
for _,_unit in pairs(_units) do
|
local _allhave=true
|
||||||
local _unit=_unit --Wrapper.Unit#UNIT
|
local _onehas=false
|
||||||
if _unit then
|
|
||||||
local _hastit=_unit:HasAttribute(attribute)
|
for _,_unit in pairs(_units) do
|
||||||
if _hastit==true then
|
local _unit=_unit --Wrapper.Unit#UNIT
|
||||||
_onehas=true
|
if _unit then
|
||||||
else
|
local _hastit=_unit:HasAttribute(attribute)
|
||||||
_allhave=false
|
if _hastit==true then
|
||||||
end
|
_onehas=true
|
||||||
end
|
else
|
||||||
|
_allhave=false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if all==true then
|
||||||
|
return _allhave
|
||||||
|
else
|
||||||
|
return _onehas
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if all==true then
|
return nil
|
||||||
return _allhave
|
|
||||||
else
|
|
||||||
return _onehas
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns the maximum speed of the group.
|
--- Returns the maximum speed of the group.
|
||||||
@@ -572,12 +575,15 @@ function GROUP:GetSpeedMax()
|
|||||||
|
|
||||||
for _,unit in pairs(Units) do
|
for _,unit in pairs(Units) do
|
||||||
local unit=unit --Wrapper.Unit#UNIT
|
local unit=unit --Wrapper.Unit#UNIT
|
||||||
|
|
||||||
local speed=unit:GetSpeedMax()
|
local speed=unit:GetSpeedMax()
|
||||||
if speedmax==nil then
|
|
||||||
speedmax=speed
|
if speedmax==nil or speed<speedmax then
|
||||||
elseif speed<speedmax then
|
|
||||||
speedmax=speed
|
speedmax=speed
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--env.info(string.format("FF unit %s: speed=%.1f, speedmax=%.1f", unit:GetName(), speed, speedmax))
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return speedmax
|
return speedmax
|
||||||
@@ -627,7 +633,7 @@ function GROUP:GetUnits()
|
|||||||
local DCSGroup = self:GetDCSObject()
|
local DCSGroup = self:GetDCSObject()
|
||||||
|
|
||||||
if DCSGroup then
|
if DCSGroup then
|
||||||
local DCSUnits = DCSGroup:getUnits()
|
local DCSUnits = DCSGroup:getUnits() or {}
|
||||||
local Units = {}
|
local Units = {}
|
||||||
for Index, UnitData in pairs( DCSUnits ) do
|
for Index, UnitData in pairs( DCSUnits ) do
|
||||||
Units[#Units+1] = UNIT:Find( UnitData )
|
Units[#Units+1] = UNIT:Find( UnitData )
|
||||||
@@ -663,6 +669,12 @@ function GROUP:GetPlayerUnits()
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Check if an (air) group is a client or player slot. Information is retrieved from the group template.
|
||||||
|
-- @param #GROUP self
|
||||||
|
-- @return #boolean If true, group is associated with a client or player slot.
|
||||||
|
function GROUP:IsPlayer()
|
||||||
|
return self:GetUnit(1):IsPlayer()
|
||||||
|
end
|
||||||
|
|
||||||
--- Returns the UNIT wrapper class with number UnitNumber.
|
--- Returns the UNIT wrapper class with number UnitNumber.
|
||||||
-- If the underlying DCS Unit does not exist, the method will return nil. .
|
-- If the underlying DCS Unit does not exist, the method will return nil. .
|
||||||
@@ -674,17 +686,29 @@ function GROUP:GetUnit( UnitNumber )
|
|||||||
local DCSGroup = self:GetDCSObject()
|
local DCSGroup = self:GetDCSObject()
|
||||||
|
|
||||||
if DCSGroup then
|
if DCSGroup then
|
||||||
|
|
||||||
local DCSUnit = DCSGroup:getUnit( UnitNumber )
|
|
||||||
|
|
||||||
local UnitFound = UNIT:Find(DCSUnit)
|
local UnitFound = nil
|
||||||
|
-- 2.7.1 dead event bug, return the first alive unit instead
|
||||||
|
local units = DCSGroup:getUnits() or {}
|
||||||
|
|
||||||
|
for _,_unit in pairs(units) do
|
||||||
|
|
||||||
|
local UnitFound = UNIT:Find(_unit)
|
||||||
|
|
||||||
|
if UnitFound then
|
||||||
|
|
||||||
|
return UnitFound
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return UnitFound
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Returns the DCS Unit with number UnitNumber.
|
--- Returns the DCS Unit with number UnitNumber.
|
||||||
-- If the underlying DCS Unit does not exist, the method will return nil. .
|
-- If the underlying DCS Unit does not exist, the method will return nil. .
|
||||||
-- @param #GROUP self
|
-- @param #GROUP self
|
||||||
@@ -692,11 +716,24 @@ end
|
|||||||
-- @return DCS#Unit The DCS Unit.
|
-- @return DCS#Unit The DCS Unit.
|
||||||
function GROUP:GetDCSUnit( UnitNumber )
|
function GROUP:GetDCSUnit( UnitNumber )
|
||||||
|
|
||||||
local DCSGroup=self:GetDCSObject()
|
local DCSGroup = self:GetDCSObject()
|
||||||
|
|
||||||
if DCSGroup then
|
if DCSGroup then
|
||||||
local DCSUnitFound=DCSGroup:getUnit( UnitNumber )
|
|
||||||
return DCSUnitFound
|
if DCSGroup.getUnit and DCSGroup:getUnit( UnitNumber ) then
|
||||||
|
return DCSGroup:getUnit( UnitNumber )
|
||||||
|
else
|
||||||
|
|
||||||
|
local UnitFound = nil
|
||||||
|
-- 2.7.1 dead event bug, return the first alive unit instead
|
||||||
|
local units = DCSGroup:getUnits() or {}
|
||||||
|
|
||||||
|
for _,_unit in pairs(units) do
|
||||||
|
if _unit and _unit:isExist() then
|
||||||
|
return _unit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -770,8 +807,7 @@ end
|
|||||||
|
|
||||||
--- Returns the average velocity Vec3 vector.
|
--- Returns the average velocity Vec3 vector.
|
||||||
-- @param Wrapper.Group#GROUP self
|
-- @param Wrapper.Group#GROUP self
|
||||||
-- @return DCS#Vec3 The velocity Vec3 vector
|
-- @return DCS#Vec3 The velocity Vec3 vector or `#nil` if the GROUP is not existing or alive.
|
||||||
-- @return #nil The GROUP is not existing or alive.
|
|
||||||
function GROUP:GetVelocityVec3()
|
function GROUP:GetVelocityVec3()
|
||||||
self:F2( self.GroupName )
|
self:F2( self.GroupName )
|
||||||
|
|
||||||
@@ -802,11 +838,19 @@ function GROUP:GetVelocityVec3()
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Returns the average group altitude in meters.
|
||||||
|
-- @param Wrapper.Group#GROUP self
|
||||||
|
-- @param #boolean FromGround Measure from the ground or from sea level (ASL). Provide **true** for measuring from the ground (AGL). **false** or **nil** if you measure from sea level.
|
||||||
|
-- @return #number The altitude of the group or nil if is not existing or alive.
|
||||||
|
function GROUP:GetAltitude(FromGround)
|
||||||
|
self:F2( self.GroupName )
|
||||||
|
return self:GetHeight(FromGround)
|
||||||
|
end
|
||||||
|
|
||||||
--- Returns the average group height in meters.
|
--- Returns the average group height in meters.
|
||||||
-- @param Wrapper.Group#GROUP self
|
-- @param Wrapper.Group#GROUP self
|
||||||
-- @param #boolean FromGround Measure from the ground or from sea level. Provide **true** for measuring from the ground. **false** or **nil** if you measure from sea level.
|
-- @param #boolean FromGround Measure from the ground or from sea level (ASL). Provide **true** for measuring from the ground (AGL). **false** or **nil** if you measure from sea level.
|
||||||
-- @return DCS#Vec3 The height of the group or nil if is not existing or alive.
|
-- @return #number The height of the group or nil if is not existing or alive.
|
||||||
function GROUP:GetHeight( FromGround )
|
function GROUP:GetHeight( FromGround )
|
||||||
self:F2( self.GroupName )
|
self:F2( self.GroupName )
|
||||||
|
|
||||||
@@ -906,6 +950,24 @@ function GROUP:GetTypeName()
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- [AIRPLANE] Get the NATO reporting name (platform, e.g. "Flanker") of a GROUP (note - first unit the group). "Bogey" if not found. Currently airplanes only!
|
||||||
|
--@param #GROUP self
|
||||||
|
--@return #string NatoReportingName or "Bogey" if unknown.
|
||||||
|
function GROUP:GetNatoReportingName()
|
||||||
|
self:F2( self.GroupName )
|
||||||
|
|
||||||
|
local DCSGroup = self:GetDCSObject()
|
||||||
|
|
||||||
|
if DCSGroup then
|
||||||
|
local GroupTypeName = DCSGroup:getUnit(1):getTypeName()
|
||||||
|
self:T3( GroupTypeName )
|
||||||
|
return UTILS.GetReportingName(GroupTypeName)
|
||||||
|
end
|
||||||
|
|
||||||
|
return "Bogey"
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
--- Gets the player name of the group.
|
--- Gets the player name of the group.
|
||||||
-- @param #GROUP self
|
-- @param #GROUP self
|
||||||
-- @return #string The player name of the group.
|
-- @return #string The player name of the group.
|
||||||
@@ -998,25 +1060,34 @@ end
|
|||||||
-- @param Wrapper.Group#GROUP self
|
-- @param Wrapper.Group#GROUP self
|
||||||
-- @return Core.Point#COORDINATE The COORDINATE of the GROUP.
|
-- @return Core.Point#COORDINATE The COORDINATE of the GROUP.
|
||||||
function GROUP:GetCoordinate()
|
function GROUP:GetCoordinate()
|
||||||
|
|
||||||
local FirstUnit = self:GetUnit(1)
|
|
||||||
|
local Units = self:GetUnits() or {}
|
||||||
|
|
||||||
if FirstUnit then
|
for _,_unit in pairs(Units) do
|
||||||
local FirstUnitCoordinate = FirstUnit:GetCoordinate()
|
local FirstUnit = _unit -- Wrapper.Unit#UNIT
|
||||||
return FirstUnitCoordinate
|
|
||||||
|
if FirstUnit then
|
||||||
|
|
||||||
|
local FirstUnitCoordinate = FirstUnit:GetCoordinate()
|
||||||
|
|
||||||
|
if FirstUnitCoordinate then
|
||||||
|
local Heading = self:GetHeading()
|
||||||
|
FirstUnitCoordinate.Heading = Heading
|
||||||
|
return FirstUnitCoordinate
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
BASE:E( { "Cannot GetCoordinate", Group = self, Alive = self:IsAlive() } )
|
BASE:E( { "Cannot GetCoordinate", Group = self, Alive = self:IsAlive() } )
|
||||||
|
|
||||||
return nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Returns a random @{DCS#Vec3} vector (point in 3D of the UNIT within the mission) within a range around the first UNIT of the GROUP.
|
--- Returns a random @{DCS#Vec3} vector (point in 3D of the UNIT within the mission) within a range around the first UNIT of the GROUP.
|
||||||
-- @param #GROUP self
|
-- @param #GROUP self
|
||||||
-- @param #number Radius
|
-- @param #number Radius Radius in meters.
|
||||||
-- @return DCS#Vec3 The random 3D point vector around the first UNIT of the GROUP.
|
-- @return DCS#Vec3 The random 3D point vector around the first UNIT of the GROUP or #nil The GROUP is invalid or empty.
|
||||||
-- @return #nil The GROUP is invalid or empty
|
|
||||||
-- @usage
|
-- @usage
|
||||||
-- -- If Radius is ignored, returns the DCS#Vec3 of first UNIT of the GROUP
|
-- -- If Radius is ignored, returns the DCS#Vec3 of first UNIT of the GROUP
|
||||||
function GROUP:GetRandomVec3(Radius)
|
function GROUP:GetRandomVec3(Radius)
|
||||||
@@ -1037,24 +1108,25 @@ end
|
|||||||
|
|
||||||
--- Returns the mean heading of every UNIT in the GROUP in degrees
|
--- Returns the mean heading of every UNIT in the GROUP in degrees
|
||||||
-- @param #GROUP self
|
-- @param #GROUP self
|
||||||
-- @return #number mean heading of the GROUP
|
-- @return #number Mean heading of the GROUP in degrees or #nil The first UNIT is not existing or alive.
|
||||||
-- @return #nil The first UNIT is not existing or alive.
|
|
||||||
function GROUP:GetHeading()
|
function GROUP:GetHeading()
|
||||||
self:F2(self.GroupName)
|
self:F2(self.GroupName)
|
||||||
|
|
||||||
|
self:F2(self.GroupName)
|
||||||
|
|
||||||
local GroupSize = self:GetSize()
|
local GroupSize = self:GetSize()
|
||||||
local HeadingAccumulator = 0
|
local HeadingAccumulator = 0
|
||||||
|
|
||||||
local n=0
|
local n=0
|
||||||
|
local Units = self:GetUnits()
|
||||||
|
|
||||||
if GroupSize then
|
if GroupSize then
|
||||||
for i = 1, GroupSize do
|
for _,unit in pairs(Units) do
|
||||||
local unit=self:GetUnit(i)
|
|
||||||
if unit and unit:IsAlive() then
|
if unit and unit:IsAlive() then
|
||||||
HeadingAccumulator = HeadingAccumulator + unit:GetHeading()
|
HeadingAccumulator = HeadingAccumulator + unit:GetHeading()
|
||||||
n=n+1
|
n=n+1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return math.floor(HeadingAccumulator / n)
|
return math.floor(HeadingAccumulator / n)
|
||||||
end
|
end
|
||||||
|
|
||||||
BASE:E( { "Cannot GetHeading", Group = self, Alive = self:IsAlive() } )
|
BASE:E( { "Cannot GetHeading", Group = self, Alive = self:IsAlive() } )
|
||||||
@@ -1066,8 +1138,8 @@ end
|
|||||||
--- Return the fuel state and unit reference for the unit with the least
|
--- Return the fuel state and unit reference for the unit with the least
|
||||||
-- amount of fuel in the group.
|
-- amount of fuel in the group.
|
||||||
-- @param #GROUP self
|
-- @param #GROUP self
|
||||||
-- @return #number The fuel state of the unit with the least amount of fuel
|
-- @return #number The fuel state of the unit with the least amount of fuel.
|
||||||
-- @return #Unit reference to #Unit object for further processing
|
-- @return Wrapper.Unit#UNIT reference to #Unit object for further processing.
|
||||||
function GROUP:GetFuelMin()
|
function GROUP:GetFuelMin()
|
||||||
self:F3(self.ControllableName)
|
self:F3(self.ControllableName)
|
||||||
|
|
||||||
@@ -1109,7 +1181,7 @@ function GROUP:GetFuelAvg()
|
|||||||
local TotalFuel = 0
|
local TotalFuel = 0
|
||||||
for UnitID, UnitData in pairs( self:GetUnits() ) do
|
for UnitID, UnitData in pairs( self:GetUnits() ) do
|
||||||
local Unit = UnitData -- Wrapper.Unit#UNIT
|
local Unit = UnitData -- Wrapper.Unit#UNIT
|
||||||
local UnitFuel = Unit:GetFuel()
|
local UnitFuel = Unit:GetFuel() or 0
|
||||||
self:F( { Fuel = UnitFuel } )
|
self:F( { Fuel = UnitFuel } )
|
||||||
TotalFuel = TotalFuel + UnitFuel
|
TotalFuel = TotalFuel + UnitFuel
|
||||||
end
|
end
|
||||||
@@ -1137,7 +1209,8 @@ end
|
|||||||
-- @return #number Number of shells left.
|
-- @return #number Number of shells left.
|
||||||
-- @return #number Number of rockets left.
|
-- @return #number Number of rockets left.
|
||||||
-- @return #number Number of bombs left.
|
-- @return #number Number of bombs left.
|
||||||
-- @return #number Number of missiles left.
|
-- @return #number Number of missiles left.
|
||||||
|
-- @return #number Number of artillery shells left (with explosive mass, included in shells; shells can also be machine gun ammo)
|
||||||
function GROUP:GetAmmunition()
|
function GROUP:GetAmmunition()
|
||||||
self:F( self.ControllableName )
|
self:F( self.ControllableName )
|
||||||
|
|
||||||
@@ -1148,6 +1221,7 @@ function GROUP:GetAmmunition()
|
|||||||
local Nrockets=0
|
local Nrockets=0
|
||||||
local Nmissiles=0
|
local Nmissiles=0
|
||||||
local Nbombs=0
|
local Nbombs=0
|
||||||
|
local Narti=0
|
||||||
|
|
||||||
if DCSControllable then
|
if DCSControllable then
|
||||||
|
|
||||||
@@ -1156,19 +1230,19 @@ function GROUP:GetAmmunition()
|
|||||||
local Unit = UnitData -- Wrapper.Unit#UNIT
|
local Unit = UnitData -- Wrapper.Unit#UNIT
|
||||||
|
|
||||||
-- Get ammo of the unit
|
-- Get ammo of the unit
|
||||||
local ntot, nshells, nrockets, nbombs, nmissiles = Unit:GetAmmunition()
|
local ntot, nshells, nrockets, nbombs, nmissiles, narti = Unit:GetAmmunition()
|
||||||
|
|
||||||
Ntot=Ntot+ntot
|
Ntot=Ntot+ntot
|
||||||
Nshells=Nshells+nshells
|
Nshells=Nshells+nshells
|
||||||
Nrockets=Nrockets+nrockets
|
Nrockets=Nrockets+nrockets
|
||||||
Nmissiles=Nmissiles+nmissiles
|
Nmissiles=Nmissiles+nmissiles
|
||||||
Nbombs=Nbombs+nbombs
|
Nbombs=Nbombs+nbombs
|
||||||
|
Narti=Narti+narti
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return Ntot, Nshells, Nrockets, Nbombs, Nmissiles
|
return Ntot, Nshells, Nrockets, Nbombs, Nmissiles, Narti
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -1186,13 +1260,14 @@ function GROUP:IsInZone( Zone )
|
|||||||
for UnitID, UnitData in pairs(self:GetUnits()) do
|
for UnitID, UnitData in pairs(self:GetUnits()) do
|
||||||
local Unit = UnitData -- Wrapper.Unit#UNIT
|
local Unit = UnitData -- Wrapper.Unit#UNIT
|
||||||
|
|
||||||
-- Get 2D vector. That's all we need for the zone check.
|
local vec2 = nil
|
||||||
local vec2=Unit:GetVec2()
|
if Unit then
|
||||||
|
-- Get 2D vector. That's all we need for the zone check.
|
||||||
|
vec2=Unit:GetVec2()
|
||||||
|
end
|
||||||
|
|
||||||
if Zone:IsVec2InZone(vec2) then
|
if vec2 and Zone:IsVec2InZone(vec2) then
|
||||||
return true -- At least one unit is in the zone. That is enough.
|
return true -- At least one unit is in the zone. That is enough.
|
||||||
else
|
|
||||||
-- This one is not but another could be.
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -2305,14 +2380,15 @@ function GROUP:GetAttribute()
|
|||||||
--- Ground ---
|
--- Ground ---
|
||||||
--------------
|
--------------
|
||||||
-- Ground
|
-- Ground
|
||||||
local apc=self:HasAttribute("Infantry carriers")
|
local apc=self:HasAttribute("APC")
|
||||||
local truck=self:HasAttribute("Trucks") and self:GetCategory()==Group.Category.GROUND
|
local truck=self:HasAttribute("Trucks") and self:GetCategory()==Group.Category.GROUND
|
||||||
local infantry=self:HasAttribute("Infantry")
|
local infantry=self:HasAttribute("Infantry")
|
||||||
local artillery=self:HasAttribute("Artillery")
|
local artillery=self:HasAttribute("Artillery")
|
||||||
local tank=self:HasAttribute("Old Tanks") or self:HasAttribute("Modern Tanks")
|
local tank=self:HasAttribute("Old Tanks") or self:HasAttribute("Modern Tanks")
|
||||||
local aaa=self:HasAttribute("AAA")
|
local aaa=self:HasAttribute("AAA") and (not self:HasAttribute("SAM elements"))
|
||||||
local ewr=self:HasAttribute("EWR")
|
local ewr=self:HasAttribute("EWR")
|
||||||
local sam=self:HasAttribute("SAM elements") and (not self:HasAttribute("AAA"))
|
local ifv=self:HasAttribute("IFV")
|
||||||
|
local sam=self:HasAttribute("SAM elements") or self:HasAttribute("Optical Tracker")
|
||||||
-- Train
|
-- Train
|
||||||
local train=self:GetCategory()==Group.Category.TRAIN
|
local train=self:GetCategory()==Group.Category.TRAIN
|
||||||
|
|
||||||
@@ -2326,41 +2402,46 @@ function GROUP:GetAttribute()
|
|||||||
local unarmedship=self:HasAttribute("Unarmed ships")
|
local unarmedship=self:HasAttribute("Unarmed ships")
|
||||||
|
|
||||||
|
|
||||||
-- Define attribute. Order is important.
|
-- Define attribute. Order of attack is important.
|
||||||
if transportplane then
|
if fighter then
|
||||||
attribute=GROUP.Attribute.AIR_TRANSPORTPLANE
|
|
||||||
elseif awacs then
|
|
||||||
attribute=GROUP.Attribute.AIR_AWACS
|
|
||||||
elseif fighter then
|
|
||||||
attribute=GROUP.Attribute.AIR_FIGHTER
|
attribute=GROUP.Attribute.AIR_FIGHTER
|
||||||
elseif bomber then
|
elseif bomber then
|
||||||
attribute=GROUP.Attribute.AIR_BOMBER
|
attribute=GROUP.Attribute.AIR_BOMBER
|
||||||
|
elseif awacs then
|
||||||
|
attribute=GROUP.Attribute.AIR_AWACS
|
||||||
|
elseif transportplane then
|
||||||
|
attribute=GROUP.Attribute.AIR_TRANSPORTPLANE
|
||||||
elseif tanker then
|
elseif tanker then
|
||||||
attribute=GROUP.Attribute.AIR_TANKER
|
attribute=GROUP.Attribute.AIR_TANKER
|
||||||
|
-- helos
|
||||||
|
elseif attackhelicopter then
|
||||||
|
attribute=GROUP.Attribute.AIR_ATTACKHELO
|
||||||
elseif transporthelo then
|
elseif transporthelo then
|
||||||
attribute=GROUP.Attribute.AIR_TRANSPORTHELO
|
attribute=GROUP.Attribute.AIR_TRANSPORTHELO
|
||||||
elseif attackhelicopter then
|
|
||||||
attribute=GROUP.Attribute.AIR_ATTACKHELO
|
|
||||||
elseif uav then
|
elseif uav then
|
||||||
attribute=GROUP.Attribute.AIR_UAV
|
attribute=GROUP.Attribute.AIR_UAV
|
||||||
elseif apc then
|
-- ground - order of attack
|
||||||
attribute=GROUP.Attribute.GROUND_APC
|
|
||||||
elseif infantry then
|
|
||||||
attribute=GROUP.Attribute.GROUND_INFANTRY
|
|
||||||
elseif artillery then
|
|
||||||
attribute=GROUP.Attribute.GROUND_ARTILLERY
|
|
||||||
elseif tank then
|
|
||||||
attribute=GROUP.Attribute.GROUND_TANK
|
|
||||||
elseif aaa then
|
|
||||||
attribute=GROUP.Attribute.GROUND_AAA
|
|
||||||
elseif ewr then
|
elseif ewr then
|
||||||
attribute=GROUP.Attribute.GROUND_EWR
|
attribute=GROUP.Attribute.GROUND_EWR
|
||||||
elseif sam then
|
elseif sam then
|
||||||
attribute=GROUP.Attribute.GROUND_SAM
|
attribute=GROUP.Attribute.GROUND_SAM
|
||||||
|
elseif aaa then
|
||||||
|
attribute=GROUP.Attribute.GROUND_AAA
|
||||||
|
elseif artillery then
|
||||||
|
attribute=GROUP.Attribute.GROUND_ARTILLERY
|
||||||
|
elseif tank then
|
||||||
|
attribute=GROUP.Attribute.GROUND_TANK
|
||||||
|
elseif ifv then
|
||||||
|
attribute=GROUP.Attribute.GROUND_IFV
|
||||||
|
elseif apc then
|
||||||
|
attribute=GROUP.Attribute.GROUND_APC
|
||||||
|
elseif infantry then
|
||||||
|
attribute=GROUP.Attribute.GROUND_INFANTRY
|
||||||
elseif truck then
|
elseif truck then
|
||||||
attribute=GROUP.Attribute.GROUND_TRUCK
|
attribute=GROUP.Attribute.GROUND_TRUCK
|
||||||
elseif train then
|
elseif train then
|
||||||
attribute=GROUP.Attribute.GROUND_TRAIN
|
attribute=GROUP.Attribute.GROUND_TRAIN
|
||||||
|
-- ships
|
||||||
elseif aircraftcarrier then
|
elseif aircraftcarrier then
|
||||||
attribute=GROUP.Attribute.NAVAL_AIRCRAFTCARRIER
|
attribute=GROUP.Attribute.NAVAL_AIRCRAFTCARRIER
|
||||||
elseif warship then
|
elseif warship then
|
||||||
@@ -2611,94 +2692,120 @@ function GROUP:GetSkill()
|
|||||||
return skill
|
return skill
|
||||||
end
|
end
|
||||||
|
|
||||||
--do -- Smoke
|
|
||||||
|
--- Get the unit in the group with the highest threat level, which is still alive.
|
||||||
|
-- @param #GROUP self
|
||||||
|
-- @return Wrapper.Unit#UNIT The most dangerous unit in the group.
|
||||||
|
-- @return #number Threat level of the unit.
|
||||||
|
function GROUP:GetHighestThreat()
|
||||||
|
|
||||||
|
-- Get units of the group.
|
||||||
|
local units=self:GetUnits()
|
||||||
|
|
||||||
|
if units then
|
||||||
|
|
||||||
|
local threat=nil ; local maxtl=0
|
||||||
|
for _,_unit in pairs(units or {}) do
|
||||||
|
local unit=_unit --Wrapper.Unit#UNIT
|
||||||
|
|
||||||
|
if unit and unit:IsAlive() then
|
||||||
|
|
||||||
|
-- Threat level of group.
|
||||||
|
local tl=unit:GetThreatLevel()
|
||||||
|
|
||||||
|
-- Check if greater the current threat.
|
||||||
|
if tl>maxtl then
|
||||||
|
maxtl=tl
|
||||||
|
threat=unit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return threat, maxtl
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get TTS friendly, optionally customized callsign mainly for **player groups**. A customized callsign is taken from the #GROUP name, after an optional '#' sign, e.g. "Aerial 1-1#Ghostrider" resulting in "Ghostrider 9", or,
|
||||||
|
-- if that isn't available, from the playername, as set in the mission editor main screen under Logbook, after an optional '|' sign (actually, more of a personal call sign), e.g. "Apple|Moose" results in "Moose 9 1". Options see below.
|
||||||
|
-- @param #GROUP self
|
||||||
|
-- @param #boolean ShortCallsign Return a shortened customized callsign, i.e. "Ghostrider 9" and not "Ghostrider 9 1"
|
||||||
|
-- @param #boolean Keepnumber (Player only) Return customized callsign, incl optional numbers at the end, e.g. "Aerial 1-1#Ghostrider 109" results in "Ghostrider 109", if you want to e.g. use historical US Navy Callsigns
|
||||||
|
-- @param #table CallsignTranslations Table to translate between DCS standard callsigns and bespoke ones. Does not apply if using customized
|
||||||
|
-- callsigns from playername or group name.
|
||||||
|
-- @return #string Callsign
|
||||||
|
-- @usage
|
||||||
|
-- -- Set Custom CAP Flight Callsigns for use with TTS
|
||||||
|
-- mygroup:GetCustomCallSign(true,false,{
|
||||||
|
-- Devil = 'Bengal',
|
||||||
|
-- Snake = 'Winder',
|
||||||
|
-- Colt = 'Camelot',
|
||||||
|
-- Enfield = 'Victory',
|
||||||
|
-- Uzi = 'Evil Eye'
|
||||||
|
-- })
|
||||||
--
|
--
|
||||||
----- Signal a flare at the position of the GROUP.
|
-- results in this outcome if the group has Callsign "Enfield 9 1" on the 1st #UNIT of the group:
|
||||||
---- @param #GROUP self
|
|
||||||
---- @param Utilities.Utils#FLARECOLOR FlareColor
|
|
||||||
--function GROUP:Flare( FlareColor )
|
|
||||||
-- self:F2()
|
|
||||||
-- trigger.action.signalFlare( self:GetVec3(), FlareColor , 0 )
|
|
||||||
--end
|
|
||||||
--
|
--
|
||||||
----- Signal a white flare at the position of the GROUP.
|
-- 'Victory 9'
|
||||||
---- @param #GROUP self
|
|
||||||
--function GROUP:FlareWhite()
|
|
||||||
-- self:F2()
|
|
||||||
-- trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.White , 0 )
|
|
||||||
--end
|
|
||||||
--
|
--
|
||||||
----- Signal a yellow flare at the position of the GROUP.
|
--
|
||||||
---- @param #GROUP self
|
function GROUP:GetCustomCallSign(ShortCallsign,Keepnumber,CallsignTranslations)
|
||||||
--function GROUP:FlareYellow()
|
--self:I("GetCustomCallSign")
|
||||||
-- self:F2()
|
|
||||||
-- trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.Yellow , 0 )
|
local callsign = "Ghost 1"
|
||||||
--end
|
if self:IsAlive() then
|
||||||
--
|
local IsPlayer = self:IsPlayer()
|
||||||
----- Signal a green flare at the position of the GROUP.
|
local shortcallsign = self:GetCallsign() or "unknown91" -- e.g.Uzi91, but we want Uzi 9 1
|
||||||
---- @param #GROUP self
|
local callsignroot = string.match(shortcallsign, '(%a+)') -- Uzi
|
||||||
--function GROUP:FlareGreen()
|
--self:I("CallSign = " .. callsignroot)
|
||||||
-- self:F2()
|
local groupname = self:GetName()
|
||||||
-- trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.Green , 0 )
|
local callnumber = string.match(shortcallsign, "(%d+)$" ) or "91" -- 91
|
||||||
--end
|
local callnumbermajor = string.char(string.byte(callnumber,1)) -- 9
|
||||||
--
|
local callnumberminor = string.char(string.byte(callnumber,2)) -- 1
|
||||||
----- Signal a red flare at the position of the GROUP.
|
local personalized = false
|
||||||
---- @param #GROUP self
|
if IsPlayer and string.find(groupname,"#") then
|
||||||
--function GROUP:FlareRed()
|
-- personalized flight name in group naming
|
||||||
-- self:F2()
|
if Keepnumber then
|
||||||
-- local Vec3 = self:GetVec3()
|
shortcallsign = string.match(groupname,"#(.+)") -- Ghostrider 219
|
||||||
-- if Vec3 then
|
else
|
||||||
-- trigger.action.signalFlare( Vec3, trigger.flareColor.Red, 0 )
|
shortcallsign = string.match(groupname,"#%s*([%a]+)") -- Ghostrider
|
||||||
-- end
|
end
|
||||||
--end
|
personalized = true
|
||||||
--
|
elseif IsPlayer and string.find(self:GetPlayerName(),"|") then
|
||||||
----- Smoke the GROUP.
|
-- personalized flight name in group naming
|
||||||
---- @param #GROUP self
|
shortcallsign = string.match(self:GetPlayerName(),"|%s*([%a]+)") -- Ghostrider
|
||||||
--function GROUP:Smoke( SmokeColor, Range )
|
personalized = true
|
||||||
-- self:F2()
|
end
|
||||||
-- if Range then
|
|
||||||
-- trigger.action.smoke( self:GetRandomVec3( Range ), SmokeColor )
|
if (not personalized) and CallsignTranslations and CallsignTranslations[callsignroot] then
|
||||||
-- else
|
callsignroot = CallsignTranslations[callsignroot]
|
||||||
-- trigger.action.smoke( self:GetVec3(), SmokeColor )
|
end
|
||||||
-- end
|
|
||||||
--
|
if personalized then
|
||||||
--end
|
-- player personalized callsign
|
||||||
--
|
-- remove trailing/leading spaces
|
||||||
----- Smoke the GROUP Green.
|
shortcallsign=string.gsub(shortcallsign,"^%s*","")
|
||||||
---- @param #GROUP self
|
shortcallsign=string.gsub(shortcallsign,"%s*$","")
|
||||||
--function GROUP:SmokeGreen()
|
if Keepnumber then
|
||||||
-- self:F2()
|
return shortcallsign -- Ghostrider 219
|
||||||
-- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Green )
|
elseif ShortCallsign then
|
||||||
--end
|
callsign = shortcallsign.." "..callnumbermajor -- Ghostrider 9
|
||||||
--
|
else
|
||||||
----- Smoke the GROUP Red.
|
callsign = shortcallsign.." "..callnumbermajor.." "..callnumberminor -- Ghostrider 9 1
|
||||||
---- @param #GROUP self
|
end
|
||||||
--function GROUP:SmokeRed()
|
return callsign
|
||||||
-- self:F2()
|
end
|
||||||
-- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Red )
|
|
||||||
--end
|
-- AI or not personalized
|
||||||
--
|
if ShortCallsign then
|
||||||
----- Smoke the GROUP White.
|
callsign = callsignroot.." "..callnumbermajor -- Uzi/Victory 9
|
||||||
---- @param #GROUP self
|
else
|
||||||
--function GROUP:SmokeWhite()
|
callsign = callsignroot.." "..callnumbermajor.." "..callnumberminor -- Uzi/Victory 9 1
|
||||||
-- self:F2()
|
end
|
||||||
-- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.White )
|
|
||||||
--end
|
--self:I("Generated Callsign = " .. callsign)
|
||||||
--
|
end
|
||||||
----- Smoke the GROUP Orange.
|
|
||||||
---- @param #GROUP self
|
return callsign
|
||||||
--function GROUP:SmokeOrange()
|
end
|
||||||
-- self:F2()
|
|
||||||
-- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Orange )
|
|
||||||
--end
|
|
||||||
--
|
|
||||||
----- Smoke the GROUP Blue.
|
|
||||||
---- @param #GROUP self
|
|
||||||
--function GROUP:SmokeBlue()
|
|
||||||
-- self:F2()
|
|
||||||
-- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Blue )
|
|
||||||
--end
|
|
||||||
--
|
|
||||||
--
|
|
||||||
--
|
|
||||||
--end
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ IDENTIFIABLE = {
|
|||||||
|
|
||||||
local _CategoryName = {
|
local _CategoryName = {
|
||||||
[Unit.Category.AIRPLANE] = "Airplane",
|
[Unit.Category.AIRPLANE] = "Airplane",
|
||||||
[Unit.Category.HELICOPTER] = "Helicoper",
|
[Unit.Category.HELICOPTER] = "Helicopter",
|
||||||
[Unit.Category.GROUND_UNIT] = "Ground Identifiable",
|
[Unit.Category.GROUND_UNIT] = "Ground Identifiable",
|
||||||
[Unit.Category.SHIP] = "Ship",
|
[Unit.Category.SHIP] = "Ship",
|
||||||
[Unit.Category.STRUCTURE] = "Structure",
|
[Unit.Category.STRUCTURE] = "Structure",
|
||||||
@@ -56,8 +56,7 @@ end
|
|||||||
-- If the Identifiable is not alive, nil is returned.
|
-- If the Identifiable is not alive, nil is returned.
|
||||||
-- If the Identifiable is alive, true is returned.
|
-- If the Identifiable is alive, true is returned.
|
||||||
-- @param #IDENTIFIABLE self
|
-- @param #IDENTIFIABLE self
|
||||||
-- @return #boolean true if Identifiable is alive.
|
-- @return #boolean true if Identifiable is alive or `#nil` if the Identifiable is not existing or is not alive.
|
||||||
-- @return #nil if the Identifiable is not existing or is not alive.
|
|
||||||
function IDENTIFIABLE:IsAlive()
|
function IDENTIFIABLE:IsAlive()
|
||||||
self:F3( self.IdentifiableName )
|
self:F3( self.IdentifiableName )
|
||||||
|
|
||||||
@@ -77,11 +76,8 @@ end
|
|||||||
--- Returns DCS Identifiable object name.
|
--- Returns DCS Identifiable object name.
|
||||||
-- The function provides access to non-activated objects too.
|
-- The function provides access to non-activated objects too.
|
||||||
-- @param #IDENTIFIABLE self
|
-- @param #IDENTIFIABLE self
|
||||||
-- @return #string The name of the DCS Identifiable.
|
-- @return #string The name of the DCS Identifiable or `#nil`.
|
||||||
-- @return #nil The DCS Identifiable is not existing or alive.
|
|
||||||
function IDENTIFIABLE:GetName()
|
function IDENTIFIABLE:GetName()
|
||||||
self:F2( self.IdentifiableName )
|
|
||||||
|
|
||||||
local IdentifiableName = self.IdentifiableName
|
local IdentifiableName = self.IdentifiableName
|
||||||
return IdentifiableName
|
return IdentifiableName
|
||||||
end
|
end
|
||||||
@@ -148,8 +144,7 @@ end
|
|||||||
|
|
||||||
--- Returns coalition of the Identifiable.
|
--- Returns coalition of the Identifiable.
|
||||||
-- @param #IDENTIFIABLE self
|
-- @param #IDENTIFIABLE self
|
||||||
-- @return DCS#coalition.side The side of the coalition.
|
-- @return DCS#coalition.side The side of the coalition or `#nil` The DCS Identifiable is not existing or alive.
|
||||||
-- @return #nil The DCS Identifiable is not existing or alive.
|
|
||||||
function IDENTIFIABLE:GetCoalition()
|
function IDENTIFIABLE:GetCoalition()
|
||||||
self:F2( self.IdentifiableName )
|
self:F2( self.IdentifiableName )
|
||||||
|
|
||||||
@@ -176,7 +171,7 @@ function IDENTIFIABLE:GetCoalitionName()
|
|||||||
|
|
||||||
if DCSIdentifiable then
|
if DCSIdentifiable then
|
||||||
|
|
||||||
-- Get coaliton ID.
|
-- Get coalition ID.
|
||||||
local IdentifiableCoalition = DCSIdentifiable:getCoalition()
|
local IdentifiableCoalition = DCSIdentifiable:getCoalition()
|
||||||
self:T3( IdentifiableCoalition )
|
self:T3( IdentifiableCoalition )
|
||||||
|
|
||||||
@@ -190,8 +185,7 @@ end
|
|||||||
|
|
||||||
--- Returns country of the Identifiable.
|
--- Returns country of the Identifiable.
|
||||||
-- @param #IDENTIFIABLE self
|
-- @param #IDENTIFIABLE self
|
||||||
-- @return DCS#country.id The country identifier.
|
-- @return DCS#country.id The country identifier or `#nil` The DCS Identifiable is not existing or alive.
|
||||||
-- @return #nil The DCS Identifiable is not existing or alive.
|
|
||||||
function IDENTIFIABLE:GetCountry()
|
function IDENTIFIABLE:GetCountry()
|
||||||
self:F2( self.IdentifiableName )
|
self:F2( self.IdentifiableName )
|
||||||
|
|
||||||
@@ -222,8 +216,7 @@ end
|
|||||||
|
|
||||||
--- Returns Identifiable descriptor. Descriptor type depends on Identifiable category.
|
--- Returns Identifiable descriptor. Descriptor type depends on Identifiable category.
|
||||||
-- @param #IDENTIFIABLE self
|
-- @param #IDENTIFIABLE self
|
||||||
-- @return DCS#Object.Desc The Identifiable descriptor.
|
-- @return DCS#Object.Desc The Identifiable descriptor or `#nil` The DCS Identifiable is not existing or alive.
|
||||||
-- @return #nil The DCS Identifiable is not existing or alive.
|
|
||||||
function IDENTIFIABLE:GetDesc()
|
function IDENTIFIABLE:GetDesc()
|
||||||
self:F2( self.IdentifiableName )
|
self:F2( self.IdentifiableName )
|
||||||
|
|
||||||
@@ -242,8 +235,7 @@ end
|
|||||||
--- Check if the Object has the attribute.
|
--- Check if the Object has the attribute.
|
||||||
-- @param #IDENTIFIABLE self
|
-- @param #IDENTIFIABLE self
|
||||||
-- @param #string AttributeName The attribute name.
|
-- @param #string AttributeName The attribute name.
|
||||||
-- @return #boolean true if the attribute exists.
|
-- @return #boolean true if the attribute exists or `#nil` The DCS Identifiable is not existing or alive.
|
||||||
-- @return #nil The DCS Identifiable is not existing or alive.
|
|
||||||
function IDENTIFIABLE:HasAttribute( AttributeName )
|
function IDENTIFIABLE:HasAttribute( AttributeName )
|
||||||
self:F2( self.IdentifiableName )
|
self:F2( self.IdentifiableName )
|
||||||
|
|
||||||
@@ -266,8 +258,10 @@ function IDENTIFIABLE:GetCallsign()
|
|||||||
return ''
|
return ''
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Gets the threat level.
|
||||||
|
-- @param #IDENTIFIABLE self
|
||||||
|
-- @return #number Threat level.
|
||||||
|
-- @return #string Type.
|
||||||
function IDENTIFIABLE:GetThreatLevel()
|
function IDENTIFIABLE:GetThreatLevel()
|
||||||
|
|
||||||
return 0, "Scenery"
|
return 0, "Scenery"
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -13,8 +13,7 @@
|
|||||||
--
|
--
|
||||||
-- ### Author: **funkyfranky**
|
-- ### Author: **funkyfranky**
|
||||||
-- @module Wrapper.Marker
|
-- @module Wrapper.Marker
|
||||||
-- @image Wrapper_Marker.png
|
-- @image MOOSE_Core.JPG
|
||||||
|
|
||||||
|
|
||||||
--- Marker class.
|
--- Marker class.
|
||||||
-- @type MARKER
|
-- @type MARKER
|
||||||
@@ -24,7 +23,7 @@
|
|||||||
-- @field #number mid Marker ID.
|
-- @field #number mid Marker ID.
|
||||||
-- @field Core.Point#COORDINATE coordinate Coordinate of the mark.
|
-- @field Core.Point#COORDINATE coordinate Coordinate of the mark.
|
||||||
-- @field #string text Text displayed in the mark panel.
|
-- @field #string text Text displayed in the mark panel.
|
||||||
-- @field #string message Message dispayed when the mark is added.
|
-- @field #string message Message displayed when the mark is added.
|
||||||
-- @field #boolean readonly Marker is read-only.
|
-- @field #boolean readonly Marker is read-only.
|
||||||
-- @field #number coalition Coalition to which the marker is displayed.
|
-- @field #number coalition Coalition to which the marker is displayed.
|
||||||
-- @extends Core.Fsm#FSM
|
-- @extends Core.Fsm#FSM
|
||||||
@@ -42,29 +41,29 @@
|
|||||||
-- # Create a Marker
|
-- # Create a Marker
|
||||||
--
|
--
|
||||||
-- -- Create a MARKER object at Batumi with a trivial text.
|
-- -- Create a MARKER object at Batumi with a trivial text.
|
||||||
-- local Coordinate=AIRBASE:FindByName("Batumi"):GetCoordinate()
|
-- local Coordinate = AIRBASE:FindByName( "Batumi" ):GetCoordinate()
|
||||||
-- mymarker=MARKER:New(Coordinate, "I am Batumi Airfield")
|
-- mymarker = MARKER:New( Coordinate, "I am Batumi Airfield" )
|
||||||
--
|
--
|
||||||
-- Now this does **not** show the marker yet. We still need to specifiy to whom it is shown. There are several options, i.e.
|
-- Now this does **not** show the marker yet. We still need to specify to whom it is shown. There are several options, i.e.
|
||||||
-- show the marker to everyone, to a speficic coaliton only, or only to a specific group.
|
-- show the marker to everyone, to a specific coalition only, or only to a specific group.
|
||||||
--
|
--
|
||||||
-- ## For Everyone
|
-- ## For Everyone
|
||||||
--
|
--
|
||||||
-- If the marker should be visible to everyone, you can use the :ToAll() function.
|
-- If the marker should be visible to everyone, you can use the :ToAll() function.
|
||||||
--
|
--
|
||||||
-- mymarker=MARKER:New(Coordinate, "I am Batumi Airfield"):ToAll()
|
-- mymarker = MARKER:New( Coordinate, "I am Batumi Airfield" ):ToAll()
|
||||||
--
|
--
|
||||||
-- ## For a Coaliton
|
-- ## For a Coalition
|
||||||
--
|
--
|
||||||
-- If the maker should be visible to a specific coalition, you can use the :ToCoalition() function.
|
-- If the maker should be visible to a specific coalition, you can use the :ToCoalition() function.
|
||||||
--
|
--
|
||||||
-- mymarker=MARKER:New(Coordinate, "I am Batumi Airfield"):ToCoaliton(coaliton.side.BLUE)
|
-- mymarker = MARKER:New( Coordinate , "I am Batumi Airfield" ):ToCoalition( coalition.side.BLUE )
|
||||||
--
|
--
|
||||||
-- ### To Blue Coaliton
|
-- ### To Blue Coalition
|
||||||
--
|
--
|
||||||
-- ### To Red Coalition
|
-- ### To Red Coalition
|
||||||
--
|
--
|
||||||
-- This would show the marker only to the Blue coaliton.
|
-- This would show the marker only to the Blue coalition.
|
||||||
--
|
--
|
||||||
-- ## For a Group
|
-- ## For a Group
|
||||||
--
|
--
|
||||||
@@ -76,28 +75,28 @@
|
|||||||
--
|
--
|
||||||
-- The marker text and coordinate can be updated easily as shown below.
|
-- The marker text and coordinate can be updated easily as shown below.
|
||||||
--
|
--
|
||||||
-- However, note that **updateing involves to remove and recreate the marker if either text or its coordinate is changed**.
|
-- However, note that **updating involves to remove and recreate the marker if either text or its coordinate is changed**.
|
||||||
-- *This is a DCS scripting engine limitation.*
|
-- *This is a DCS scripting engine limitation.*
|
||||||
--
|
--
|
||||||
-- ## Update Text
|
-- ## Update Text
|
||||||
--
|
--
|
||||||
-- If you created a marker "mymarker" as shown above, you can update the dispayed test by
|
-- If you created a marker "mymarker" as shown above, you can update the displayed test by
|
||||||
--
|
--
|
||||||
-- mymarker:UpdateText("I am the new text at Batumi")
|
-- mymarker:UpdateText( "I am the new text at Batumi" )
|
||||||
--
|
--
|
||||||
-- The update can also be delayed by, e.g. 90 seconds, using
|
-- The update can also be delayed by, e.g. 90 seconds, using
|
||||||
--
|
--
|
||||||
-- mymarker:UpdateText("I am the new text at Batumi", 90)
|
-- mymarker:UpdateText( "I am the new text at Batumi", 90 )
|
||||||
--
|
--
|
||||||
-- ## Update Coordinate
|
-- ## Update Coordinate
|
||||||
--
|
--
|
||||||
-- If you created a marker "mymarker" as shown above, you can update its coordinate on the F10 map by
|
-- If you created a marker "mymarker" as shown above, you can update its coordinate on the F10 map by
|
||||||
--
|
--
|
||||||
-- mymarker:UpdateCoordinate(NewCoordinate)
|
-- mymarker:UpdateCoordinate( NewCoordinate )
|
||||||
--
|
--
|
||||||
-- The update can also be delayed by, e.g. 60 seconds, using
|
-- The update can also be delayed by, e.g. 60 seconds, using
|
||||||
--
|
--
|
||||||
-- mymarker:UpdateCoordinate(NewCoordinate, 60)
|
-- mymarker:UpdateCoordinate( NewCoordinate , 60 )
|
||||||
--
|
--
|
||||||
-- # Retrieve Data
|
-- # Retrieve Data
|
||||||
--
|
--
|
||||||
@@ -105,18 +104,18 @@
|
|||||||
--
|
--
|
||||||
-- ## Text
|
-- ## Text
|
||||||
--
|
--
|
||||||
-- local text=mymarker:GetText()
|
-- local text =mymarker:GetText()
|
||||||
-- env.info("Marker Text = " .. text)
|
-- env.info( "Marker Text = " .. text )
|
||||||
--
|
--
|
||||||
-- ## Coordinate
|
-- ## Coordinate
|
||||||
--
|
--
|
||||||
-- local Coordinate=mymarker:GetCoordinate()
|
-- local Coordinate = mymarker:GetCoordinate()
|
||||||
-- env.info("Marker Coordinate LL DSM = " .. Coordinate:ToStringLLDMS())
|
-- env.info( "Marker Coordinate LL DSM = " .. Coordinate:ToStringLLDMS() )
|
||||||
--
|
--
|
||||||
--
|
--
|
||||||
-- # FSM Events
|
-- # FSM Events
|
||||||
--
|
--
|
||||||
-- Moose creates addditonal events, so called FSM event, when markers are added, changed, removed, and text or the coordianteis updated.
|
-- Moose creates additional events, so called FSM event, when markers are added, changed, removed, and text or the coordinate is updated.
|
||||||
--
|
--
|
||||||
-- These events can be captured and used for processing via OnAfter functions as shown below.
|
-- These events can be captured and used for processing via OnAfter functions as shown below.
|
||||||
--
|
--
|
||||||
@@ -133,26 +132,25 @@
|
|||||||
--
|
--
|
||||||
-- # Examples
|
-- # Examples
|
||||||
--
|
--
|
||||||
--
|
|
||||||
-- @field #MARKER
|
-- @field #MARKER
|
||||||
MARKER = {
|
MARKER = {
|
||||||
ClassName = "MARKER",
|
ClassName = "MARKER",
|
||||||
Debug = false,
|
Debug = false,
|
||||||
lid = nil,
|
lid = nil,
|
||||||
mid = nil,
|
mid = nil,
|
||||||
coordinate = nil,
|
coordinate = nil,
|
||||||
text = nil,
|
text = nil,
|
||||||
message = nil,
|
message = nil,
|
||||||
readonly = nil,
|
readonly = nil,
|
||||||
coalition = nil,
|
coalition = nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Marker ID. Running number.
|
--- Marker ID. Running number.
|
||||||
_MARKERID=0
|
_MARKERID = 0
|
||||||
|
|
||||||
--- Marker class version.
|
--- Marker class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
MARKER.version="0.1.0"
|
MARKER.version="0.1.1"
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- TODO list
|
-- TODO list
|
||||||
@@ -172,38 +170,40 @@ MARKER.version="0.1.0"
|
|||||||
-- @param Core.Point#COORDINATE Coordinate Coordinate where to place the marker.
|
-- @param Core.Point#COORDINATE Coordinate Coordinate where to place the marker.
|
||||||
-- @param #string Text Text displayed on the mark panel.
|
-- @param #string Text Text displayed on the mark panel.
|
||||||
-- @return #MARKER self
|
-- @return #MARKER self
|
||||||
function MARKER:New(Coordinate, Text)
|
function MARKER:New( Coordinate, Text )
|
||||||
|
|
||||||
-- Inherit everything from FSM class.
|
-- Inherit everything from FSM class.
|
||||||
|
local self = BASE:Inherit( self, FSM:New() ) -- #MARKER
|
||||||
|
|
||||||
local self=BASE:Inherit(self, FSM:New()) -- #MARKER
|
local self=BASE:Inherit(self, FSM:New()) -- #MARKER
|
||||||
|
|
||||||
self.coordinate=Coordinate
|
self.coordinate=UTILS.DeepCopy(Coordinate)
|
||||||
|
|
||||||
self.text=Text
|
self.text = Text
|
||||||
|
|
||||||
-- Defaults
|
-- Defaults
|
||||||
self.readonly=false
|
self.readonly = false
|
||||||
self.message=""
|
self.message = ""
|
||||||
|
|
||||||
-- New marker ID. This is not the one of the actual marker.
|
-- New marker ID. This is not the one of the actual marker.
|
||||||
_MARKERID=_MARKERID+1
|
_MARKERID = _MARKERID + 1
|
||||||
|
|
||||||
self.myid=_MARKERID
|
self.myid = _MARKERID
|
||||||
|
|
||||||
-- Log ID.
|
-- Log ID.
|
||||||
self.lid=string.format("Marker #%d | ", self.myid)
|
self.lid = string.format( "Marker #%d | ", self.myid )
|
||||||
|
|
||||||
-- Start State.
|
-- Start State.
|
||||||
self:SetStartState("Invisible")
|
self:SetStartState( "Invisible" )
|
||||||
|
|
||||||
-- Add FSM transitions.
|
-- Add FSM transitions.
|
||||||
-- From State --> Event --> To State
|
-- From State --> Event --> To State
|
||||||
self:AddTransition("Invisible", "Added", "Visible") -- Marker was added.
|
self:AddTransition( "Invisible", "Added", "Visible" ) -- Marker was added.
|
||||||
self:AddTransition("Visible", "Removed", "Invisible") -- Marker was removed.
|
self:AddTransition( "Visible", "Removed", "Invisible" ) -- Marker was removed.
|
||||||
self:AddTransition("*", "Changed", "*") -- Marker was changed.
|
self:AddTransition( "*", "Changed", "*" ) -- Marker was changed.
|
||||||
|
|
||||||
self:AddTransition("*", "TextUpdate", "*") -- Text updated.
|
self:AddTransition( "*", "TextUpdate", "*" ) -- Text updated.
|
||||||
self:AddTransition("*", "CoordUpdate", "*") -- Coordinates updated.
|
self:AddTransition( "*", "CoordUpdate", "*" ) -- Coordinates updated.
|
||||||
|
|
||||||
--- Triggers the FSM event "Added".
|
--- Triggers the FSM event "Added".
|
||||||
-- @function [parent=#MARKER] Added
|
-- @function [parent=#MARKER] Added
|
||||||
@@ -223,7 +223,6 @@ function MARKER:New(Coordinate, Text)
|
|||||||
-- @param #string To To state.
|
-- @param #string To To state.
|
||||||
-- @param Core.Event#EVENTDATA EventData Event data table.
|
-- @param Core.Event#EVENTDATA EventData Event data table.
|
||||||
|
|
||||||
|
|
||||||
--- Triggers the FSM event "Removed".
|
--- Triggers the FSM event "Removed".
|
||||||
-- @function [parent=#MARKER] Removed
|
-- @function [parent=#MARKER] Removed
|
||||||
-- @param #MARKER self
|
-- @param #MARKER self
|
||||||
@@ -242,7 +241,6 @@ function MARKER:New(Coordinate, Text)
|
|||||||
-- @param #string To To state.
|
-- @param #string To To state.
|
||||||
-- @param Core.Event#EVENTDATA EventData Event data table.
|
-- @param Core.Event#EVENTDATA EventData Event data table.
|
||||||
|
|
||||||
|
|
||||||
--- Triggers the FSM event "Changed".
|
--- Triggers the FSM event "Changed".
|
||||||
-- @function [parent=#MARKER] Changed
|
-- @function [parent=#MARKER] Changed
|
||||||
-- @param #MARKER self
|
-- @param #MARKER self
|
||||||
@@ -261,7 +259,6 @@ function MARKER:New(Coordinate, Text)
|
|||||||
-- @param #string To To state.
|
-- @param #string To To state.
|
||||||
-- @param Core.Event#EVENTDATA EventData Event data table.
|
-- @param Core.Event#EVENTDATA EventData Event data table.
|
||||||
|
|
||||||
|
|
||||||
--- Triggers the FSM event "TextUpdate".
|
--- Triggers the FSM event "TextUpdate".
|
||||||
-- @function [parent=#MARKER] TextUpdate
|
-- @function [parent=#MARKER] TextUpdate
|
||||||
-- @param #MARKER self
|
-- @param #MARKER self
|
||||||
@@ -280,7 +277,6 @@ function MARKER:New(Coordinate, Text)
|
|||||||
-- @param #string To To state.
|
-- @param #string To To state.
|
||||||
-- @param #string Text The new text.
|
-- @param #string Text The new text.
|
||||||
|
|
||||||
|
|
||||||
--- Triggers the FSM event "CoordUpdate".
|
--- Triggers the FSM event "CoordUpdate".
|
||||||
-- @function [parent=#MARKER] CoordUpdate
|
-- @function [parent=#MARKER] CoordUpdate
|
||||||
-- @param #MARKER self
|
-- @param #MARKER self
|
||||||
@@ -299,11 +295,10 @@ function MARKER:New(Coordinate, Text)
|
|||||||
-- @param #string To To state.
|
-- @param #string To To state.
|
||||||
-- @param Core.Point#COORDINATE Coordinate The updated Coordinate.
|
-- @param Core.Point#COORDINATE Coordinate The updated Coordinate.
|
||||||
|
|
||||||
|
|
||||||
-- Handle events.
|
-- Handle events.
|
||||||
self:HandleEvent(EVENTS.MarkAdded)
|
self:HandleEvent( EVENTS.MarkAdded )
|
||||||
self:HandleEvent(EVENTS.MarkRemoved)
|
self:HandleEvent( EVENTS.MarkRemoved )
|
||||||
self:HandleEvent(EVENTS.MarkChange)
|
self:HandleEvent( EVENTS.MarkChange )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@@ -317,7 +312,17 @@ end
|
|||||||
-- @return #MARKER self
|
-- @return #MARKER self
|
||||||
function MARKER:ReadOnly()
|
function MARKER:ReadOnly()
|
||||||
|
|
||||||
self.readonly=true
|
self.readonly = true
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Marker is readonly. Text cannot be changed and marker cannot be removed.
|
||||||
|
-- @param #MARKER self
|
||||||
|
-- @return #MARKER self
|
||||||
|
function MARKER:ReadWrite()
|
||||||
|
|
||||||
|
self.readonly=false
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@@ -326,9 +331,9 @@ end
|
|||||||
-- @param #MARKER self
|
-- @param #MARKER self
|
||||||
-- @param #string Text Message displayed when the marker is added.
|
-- @param #string Text Message displayed when the marker is added.
|
||||||
-- @return #MARKER self
|
-- @return #MARKER self
|
||||||
function MARKER:Message(Text)
|
function MARKER:Message( Text )
|
||||||
|
|
||||||
self.message=Text or ""
|
self.message = Text or ""
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@@ -337,28 +342,28 @@ end
|
|||||||
-- @param #MARKER self
|
-- @param #MARKER self
|
||||||
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
|
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
|
||||||
-- @return #MARKER self
|
-- @return #MARKER self
|
||||||
function MARKER:ToAll(Delay)
|
function MARKER:ToAll( Delay )
|
||||||
|
|
||||||
if Delay and Delay>0 then
|
if Delay and Delay > 0 then
|
||||||
self:ScheduleOnce(Delay, MARKER.ToAll, self)
|
self:ScheduleOnce( Delay, MARKER.ToAll, self )
|
||||||
else
|
else
|
||||||
|
|
||||||
self.toall=true
|
self.toall = true
|
||||||
self.tocoaliton=nil
|
self.tocoalition = nil
|
||||||
self.coalition=nil
|
self.coalition = nil
|
||||||
self.togroup=nil
|
self.togroup = nil
|
||||||
self.groupname=nil
|
self.groupname = nil
|
||||||
self.groupid=nil
|
self.groupid = nil
|
||||||
|
|
||||||
-- First remove an existing mark.
|
-- First remove an existing mark.
|
||||||
if self.shown then
|
if self.shown then
|
||||||
self:Remove()
|
self:Remove()
|
||||||
end
|
end
|
||||||
|
|
||||||
self.mid=UTILS.GetMarkID()
|
self.mid = UTILS.GetMarkID()
|
||||||
|
|
||||||
-- Call DCS function.
|
-- Call DCS function.
|
||||||
trigger.action.markToAll(self.mid, self.text, self.coordinate:GetVec3(), self.readonly, self.message)
|
trigger.action.markToAll( self.mid, self.text, self.coordinate:GetVec3(), self.readonly, self.message )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -367,32 +372,32 @@ end
|
|||||||
|
|
||||||
--- Place marker visible for a specific coalition only.
|
--- Place marker visible for a specific coalition only.
|
||||||
-- @param #MARKER self
|
-- @param #MARKER self
|
||||||
-- @param #number Coalition Coalition 1=Red, 2=Blue, 0=Neutral. See `coaliton.side.RED`.
|
-- @param #number Coalition Coalition 1=Red, 2=Blue, 0=Neutral. See `coalition.side.RED`.
|
||||||
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
|
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
|
||||||
-- @return #MARKER self
|
-- @return #MARKER self
|
||||||
function MARKER:ToCoalition(Coalition, Delay)
|
function MARKER:ToCoalition( Coalition, Delay )
|
||||||
|
|
||||||
if Delay and Delay>0 then
|
if Delay and Delay > 0 then
|
||||||
self:ScheduleOnce(Delay, MARKER.ToCoalition, self, Coalition)
|
self:ScheduleOnce( Delay, MARKER.ToCoalition, self, Coalition )
|
||||||
else
|
else
|
||||||
|
|
||||||
self.coalition=Coalition
|
self.coalition = Coalition
|
||||||
|
|
||||||
self.tocoaliton=true
|
self.tocoalition = true
|
||||||
self.toall=false
|
self.toall = false
|
||||||
self.togroup=false
|
self.togroup = false
|
||||||
self.groupname=nil
|
self.groupname = nil
|
||||||
self.groupid=nil
|
self.groupid = nil
|
||||||
|
|
||||||
-- First remove an existing mark.
|
-- First remove an existing mark.
|
||||||
if self.shown then
|
if self.shown then
|
||||||
self:Remove()
|
self:Remove()
|
||||||
end
|
end
|
||||||
|
|
||||||
self.mid=UTILS.GetMarkID()
|
self.mid = UTILS.GetMarkID()
|
||||||
|
|
||||||
-- Call DCS function.
|
-- Call DCS function.
|
||||||
trigger.action.markToCoalition(self.mid, self.text, self.coordinate:GetVec3(), self.coalition, self.readonly, self.message)
|
trigger.action.markToCoalition( self.mid, self.text, self.coordinate:GetVec3(), self.coalition, self.readonly, self.message )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -403,8 +408,8 @@ end
|
|||||||
-- @param #MARKER self
|
-- @param #MARKER self
|
||||||
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
|
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
|
||||||
-- @return #MARKER self
|
-- @return #MARKER self
|
||||||
function MARKER:ToBlue(Delay)
|
function MARKER:ToBlue( Delay )
|
||||||
self:ToCoalition(coalition.side.BLUE, Delay)
|
self:ToCoalition( coalition.side.BLUE, Delay )
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -412,8 +417,8 @@ end
|
|||||||
-- @param #MARKER self
|
-- @param #MARKER self
|
||||||
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
|
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
|
||||||
-- @return #MARKER self
|
-- @return #MARKER self
|
||||||
function MARKER:ToRed(Delay)
|
function MARKER:ToRed( Delay )
|
||||||
self:ToCoalition(coalition.side.RED, Delay)
|
self:ToCoalition( coalition.side.RED, Delay )
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -421,51 +426,50 @@ end
|
|||||||
-- @param #MARKER self
|
-- @param #MARKER self
|
||||||
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
|
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
|
||||||
-- @return #MARKER self
|
-- @return #MARKER self
|
||||||
function MARKER:ToNeutral(Delay)
|
function MARKER:ToNeutral( Delay )
|
||||||
self:ToCoalition(coalition.side.NEUTRAL, Delay)
|
self:ToCoalition( coalition.side.NEUTRAL, Delay )
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Place marker visible for a specific group only.
|
--- Place marker visible for a specific group only.
|
||||||
-- @param #MARKER self
|
-- @param #MARKER self
|
||||||
-- @param Wrapper.Group#GROUP Group The group to which the marker is displayed.
|
-- @param Wrapper.Group#GROUP Group The group to which the marker is displayed.
|
||||||
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
|
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
|
||||||
-- @return #MARKER self
|
-- @return #MARKER self
|
||||||
function MARKER:ToGroup(Group, Delay)
|
function MARKER:ToGroup( Group, Delay )
|
||||||
|
|
||||||
if Delay and Delay>0 then
|
if Delay and Delay > 0 then
|
||||||
self:ScheduleOnce(Delay, MARKER.ToGroup, self, Group)
|
self:ScheduleOnce( Delay, MARKER.ToGroup, self, Group )
|
||||||
else
|
else
|
||||||
|
|
||||||
-- Check if group exists.
|
-- Check if group exists.
|
||||||
if Group and Group:IsAlive()~=nil then
|
if Group and Group:IsAlive() ~= nil then
|
||||||
|
|
||||||
self.groupid=Group:GetID()
|
self.groupid = Group:GetID()
|
||||||
|
|
||||||
if self.groupid then
|
if self.groupid then
|
||||||
|
|
||||||
self.groupname=Group:GetName()
|
self.groupname = Group:GetName()
|
||||||
|
|
||||||
self.togroup=true
|
self.togroup = true
|
||||||
self.tocoaliton=nil
|
self.tocoalition = nil
|
||||||
self.coalition=nil
|
self.coalition = nil
|
||||||
self.toall=nil
|
self.toall = nil
|
||||||
|
|
||||||
-- First remove an existing mark.
|
-- First remove an existing mark.
|
||||||
if self.shown then
|
if self.shown then
|
||||||
self:Remove()
|
self:Remove()
|
||||||
end
|
end
|
||||||
|
|
||||||
self.mid=UTILS.GetMarkID()
|
self.mid = UTILS.GetMarkID()
|
||||||
|
|
||||||
-- Call DCS function.
|
-- Call DCS function.
|
||||||
trigger.action.markToGroup(self.mid, self.text, self.coordinate:GetVec3(), self.groupid, self.readonly, self.message)
|
trigger.action.markToGroup( self.mid, self.text, self.coordinate:GetVec3(), self.groupid, self.readonly, self.message )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
--TODO: Warning!
|
-- TODO: Warning!
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -478,17 +482,17 @@ end
|
|||||||
-- @param #string Text Updated text.
|
-- @param #string Text Updated text.
|
||||||
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
|
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
|
||||||
-- @return #MARKER self
|
-- @return #MARKER self
|
||||||
function MARKER:UpdateText(Text, Delay)
|
function MARKER:UpdateText( Text, Delay )
|
||||||
|
|
||||||
if Delay and Delay>0 then
|
if Delay and Delay > 0 then
|
||||||
self:ScheduleOnce(Delay, MARKER.UpdateText, self, Text)
|
self:ScheduleOnce( Delay, MARKER.UpdateText, self, Text )
|
||||||
else
|
else
|
||||||
|
|
||||||
self.text=tostring(Text)
|
self.text = tostring( Text )
|
||||||
|
|
||||||
self:Refresh()
|
self:Refresh()
|
||||||
|
|
||||||
self:TextUpdate(tostring(Text))
|
self:TextUpdate( tostring( Text ) )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -500,17 +504,17 @@ end
|
|||||||
-- @param Core.Point#COORDINATE Coordinate The new coordinate.
|
-- @param Core.Point#COORDINATE Coordinate The new coordinate.
|
||||||
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
|
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
|
||||||
-- @return #MARKER self
|
-- @return #MARKER self
|
||||||
function MARKER:UpdateCoordinate(Coordinate, Delay)
|
function MARKER:UpdateCoordinate( Coordinate, Delay )
|
||||||
|
|
||||||
if Delay and Delay>0 then
|
if Delay and Delay > 0 then
|
||||||
self:ScheduleOnce(Delay, MARKER.UpdateCoordinate, self, Coordinate)
|
self:ScheduleOnce( Delay, MARKER.UpdateCoordinate, self, Coordinate )
|
||||||
else
|
else
|
||||||
|
|
||||||
self.coordinate=Coordinate
|
self.coordinate = Coordinate
|
||||||
|
|
||||||
self:Refresh()
|
self:Refresh()
|
||||||
|
|
||||||
self:CoordUpdate(Coordinate)
|
self:CoordUpdate( Coordinate )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -521,28 +525,28 @@ end
|
|||||||
-- @param #MARKER self
|
-- @param #MARKER self
|
||||||
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
|
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
|
||||||
-- @return #MARKER self
|
-- @return #MARKER self
|
||||||
function MARKER:Refresh(Delay)
|
function MARKER:Refresh( Delay )
|
||||||
|
|
||||||
if Delay and Delay>0 then
|
if Delay and Delay > 0 then
|
||||||
self:ScheduleOnce(Delay, MARKER.Refresh, self)
|
self:ScheduleOnce( Delay, MARKER.Refresh, self )
|
||||||
else
|
else
|
||||||
|
|
||||||
if self.toall then
|
if self.toall then
|
||||||
|
|
||||||
self:ToAll()
|
self:ToAll()
|
||||||
|
|
||||||
elseif self.tocoaliton then
|
elseif self.tocoalition then
|
||||||
|
|
||||||
self:ToCoalition(self.coalition)
|
self:ToCoalition( self.coalition )
|
||||||
|
|
||||||
elseif self.togroup then
|
elseif self.togroup then
|
||||||
|
|
||||||
local group=GROUP:FindByName(self.groupname)
|
local group = GROUP:FindByName( self.groupname )
|
||||||
|
|
||||||
self:ToGroup(group)
|
self:ToGroup( group )
|
||||||
|
|
||||||
else
|
else
|
||||||
self:E(self.lid.."ERROR: unknown To in :Refresh()!")
|
self:E( self.lid .. "ERROR: unknown To in :Refresh()!" )
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -554,16 +558,16 @@ end
|
|||||||
-- @param #MARKER self
|
-- @param #MARKER self
|
||||||
-- @param #number Delay (Optional) Delay in seconds, before the marker is removed.
|
-- @param #number Delay (Optional) Delay in seconds, before the marker is removed.
|
||||||
-- @return #MARKER self
|
-- @return #MARKER self
|
||||||
function MARKER:Remove(Delay)
|
function MARKER:Remove( Delay )
|
||||||
|
|
||||||
if Delay and Delay>0 then
|
if Delay and Delay > 0 then
|
||||||
self:ScheduleOnce(Delay, MARKER.Remove, self)
|
self:ScheduleOnce( Delay, MARKER.Remove, self )
|
||||||
else
|
else
|
||||||
|
|
||||||
if self.shown then
|
if self.shown then
|
||||||
|
|
||||||
-- Call DCS function.
|
-- Call DCS function.
|
||||||
trigger.action.removeMark(self.mid)
|
trigger.action.removeMark( self.mid )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -588,26 +592,25 @@ end
|
|||||||
|
|
||||||
--- Set text that is displayed in the marker panel. Note this does not show the marker.
|
--- Set text that is displayed in the marker panel. Note this does not show the marker.
|
||||||
-- @param #MARKER self
|
-- @param #MARKER self
|
||||||
-- @param #string Text Marker text. Default is an empty sting "".
|
-- @param #string Text Marker text. Default is an empty string "".
|
||||||
-- @return #MARKER self
|
-- @return #MARKER self
|
||||||
function MARKER:SetText(Text)
|
function MARKER:SetText( Text )
|
||||||
self.text=Text and tostring(Text) or ""
|
self.text = Text and tostring( Text ) or ""
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Check if marker is currently visible on the F10 map.
|
--- Check if marker is currently visible on the F10 map.
|
||||||
-- @param #MARKER self
|
-- @param #MARKER self
|
||||||
-- @return #boolean True if the marker is currently visible.
|
-- @return #boolean True if the marker is currently visible.
|
||||||
function MARKER:IsVisible()
|
function MARKER:IsVisible()
|
||||||
return self:Is("Visible")
|
return self:Is( "Visible" )
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Check if marker is currently invisible on the F10 map.
|
--- Check if marker is currently invisible on the F10 map.
|
||||||
-- @param #MARKER self
|
-- @param #MARKER self
|
||||||
-- @return
|
-- @return
|
||||||
function MARKER:IsInvisible()
|
function MARKER:IsInvisible()
|
||||||
return self:Is("Invisible")
|
return self:Is( "Invisible" )
|
||||||
end
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
@@ -617,19 +620,19 @@ end
|
|||||||
--- Event function when a MARKER is added.
|
--- Event function when a MARKER is added.
|
||||||
-- @param #MARKER self
|
-- @param #MARKER self
|
||||||
-- @param Core.Event#EVENTDATA EventData
|
-- @param Core.Event#EVENTDATA EventData
|
||||||
function MARKER:OnEventMarkAdded(EventData)
|
function MARKER:OnEventMarkAdded( EventData )
|
||||||
|
|
||||||
if EventData and EventData.MarkID then
|
if EventData and EventData.MarkID then
|
||||||
|
|
||||||
local MarkID=EventData.MarkID
|
local MarkID = EventData.MarkID
|
||||||
|
|
||||||
self:T3(self.lid..string.format("Captured event MarkAdded for Mark ID=%s", tostring(MarkID)))
|
self:T3( self.lid .. string.format( "Captured event MarkAdded for Mark ID=%s", tostring( MarkID ) ) )
|
||||||
|
|
||||||
if MarkID==self.mid then
|
if MarkID == self.mid then
|
||||||
|
|
||||||
self.shown=true
|
self.shown = true
|
||||||
|
|
||||||
self:Added(EventData)
|
self:Added( EventData )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -640,19 +643,21 @@ end
|
|||||||
--- Event function when a MARKER is removed.
|
--- Event function when a MARKER is removed.
|
||||||
-- @param #MARKER self
|
-- @param #MARKER self
|
||||||
-- @param Core.Event#EVENTDATA EventData
|
-- @param Core.Event#EVENTDATA EventData
|
||||||
function MARKER:OnEventMarkRemoved(EventData)
|
function MARKER:OnEventMarkRemoved( EventData )
|
||||||
|
|
||||||
if EventData and EventData.MarkID then
|
if EventData and EventData.MarkID then
|
||||||
|
|
||||||
|
local MarkID = EventData.MarkID
|
||||||
|
|
||||||
local MarkID=EventData.MarkID
|
local MarkID=EventData.MarkID
|
||||||
|
|
||||||
self:T3(self.lid..string.format("Captured event MarkAdded for Mark ID=%s", tostring(MarkID)))
|
self:T3(self.lid..string.format("Captured event MarkRemoved for Mark ID=%s", tostring(MarkID)))
|
||||||
|
|
||||||
if MarkID==self.mid then
|
if MarkID == self.mid then
|
||||||
|
|
||||||
self.shown=false
|
self.shown = false
|
||||||
|
|
||||||
self:Removed(EventData)
|
self:Removed( EventData )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -663,26 +668,32 @@ end
|
|||||||
--- Event function when a MARKER changed.
|
--- Event function when a MARKER changed.
|
||||||
-- @param #MARKER self
|
-- @param #MARKER self
|
||||||
-- @param Core.Event#EVENTDATA EventData
|
-- @param Core.Event#EVENTDATA EventData
|
||||||
function MARKER:OnEventMarkChange(EventData)
|
function MARKER:OnEventMarkChange( EventData )
|
||||||
|
|
||||||
if EventData and EventData.MarkID then
|
if EventData and EventData.MarkID then
|
||||||
|
|
||||||
|
local MarkID = EventData.MarkID
|
||||||
|
|
||||||
|
self:T3( self.lid .. string.format( "Captured event MarkChange for Mark ID=%s", tostring( MarkID ) ) )
|
||||||
|
|
||||||
|
if MarkID == self.mid then
|
||||||
|
|
||||||
local MarkID=EventData.MarkID
|
local MarkID=EventData.MarkID
|
||||||
|
|
||||||
self:T3(self.lid..string.format("Captured event MarkChange for Mark ID=%s", tostring(MarkID)))
|
self:T3(self.lid..string.format("Captured event MarkChange for Mark ID=%s", tostring(MarkID)))
|
||||||
|
|
||||||
if MarkID==self.mid then
|
if MarkID==self.mid then
|
||||||
|
|
||||||
self:Changed(EventData)
|
self.text=tostring(EventData.MarkText)
|
||||||
|
|
||||||
self:TextChanged(tostring(EventData.MarkText))
|
self:Changed(EventData)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- FSM Event Functions
|
-- FSM Event Functions
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
@@ -693,17 +704,17 @@ end
|
|||||||
-- @param #string Event Event.
|
-- @param #string Event Event.
|
||||||
-- @param #string To To state.
|
-- @param #string To To state.
|
||||||
-- @param Core.Event#EVENTDATA EventData Event data table.
|
-- @param Core.Event#EVENTDATA EventData Event data table.
|
||||||
function MARKER:onafterAdded(From, Event, To, EventData)
|
function MARKER:onafterAdded( From, Event, To, EventData )
|
||||||
|
|
||||||
-- Debug info.
|
-- Debug info.
|
||||||
local text=string.format("Captured event MarkAdded for myself:\n")
|
local text = string.format( "Captured event MarkAdded for myself:\n" )
|
||||||
text=text..string.format("Marker ID = %s\n", tostring(EventData.MarkID))
|
text = text .. string.format( "Marker ID = %s\n", tostring( EventData.MarkID ) )
|
||||||
text=text..string.format("Coalition = %s\n", tostring(EventData.MarkCoalition))
|
text = text .. string.format( "Coalition = %s\n", tostring( EventData.MarkCoalition ) )
|
||||||
text=text..string.format("Group ID = %s\n", tostring(EventData.MarkGroupID))
|
text = text .. string.format( "Group ID = %s\n", tostring( EventData.MarkGroupID ) )
|
||||||
text=text..string.format("Initiator = %s\n", EventData.IniUnit and EventData.IniUnit:GetName() or "Nobody")
|
text = text .. string.format( "Initiator = %s\n", EventData.IniUnit and EventData.IniUnit:GetName() or "Nobody" )
|
||||||
text=text..string.format("Coordinate = %s\n", EventData.MarkCoordinate and EventData.MarkCoordinate:ToStringLLDMS() or "Nowhere")
|
text = text .. string.format( "Coordinate = %s\n", EventData.MarkCoordinate and EventData.MarkCoordinate:ToStringLLDMS() or "Nowhere" )
|
||||||
text=text..string.format("Text: \n%s", tostring(EventData.MarkText))
|
text = text .. string.format( "Text: \n%s", tostring( EventData.MarkText ) )
|
||||||
self:T2(self.lid..text)
|
self:T2( self.lid .. text )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -713,17 +724,17 @@ end
|
|||||||
-- @param #string Event Event.
|
-- @param #string Event Event.
|
||||||
-- @param #string To To state.
|
-- @param #string To To state.
|
||||||
-- @param Core.Event#EVENTDATA EventData Event data table.
|
-- @param Core.Event#EVENTDATA EventData Event data table.
|
||||||
function MARKER:onafterRemoved(From, Event, To, EventData)
|
function MARKER:onafterRemoved( From, Event, To, EventData )
|
||||||
|
|
||||||
-- Debug info.
|
-- Debug info.
|
||||||
local text=string.format("Captured event MarkRemoved for myself:\n")
|
local text = string.format( "Captured event MarkRemoved for myself:\n" )
|
||||||
text=text..string.format("Marker ID = %s\n", tostring(EventData.MarkID))
|
text = text .. string.format( "Marker ID = %s\n", tostring( EventData.MarkID ) )
|
||||||
text=text..string.format("Coalition = %s\n", tostring(EventData.MarkCoalition))
|
text = text .. string.format( "Coalition = %s\n", tostring( EventData.MarkCoalition ) )
|
||||||
text=text..string.format("Group ID = %s\n", tostring(EventData.MarkGroupID))
|
text = text .. string.format( "Group ID = %s\n", tostring( EventData.MarkGroupID ) )
|
||||||
text=text..string.format("Initiator = %s\n", EventData.IniUnit and EventData.IniUnit:GetName() or "Nobody")
|
text = text .. string.format( "Initiator = %s\n", EventData.IniUnit and EventData.IniUnit:GetName() or "Nobody" )
|
||||||
text=text..string.format("Coordinate = %s\n", EventData.MarkCoordinate and EventData.MarkCoordinate:ToStringLLDMS() or "Nowhere")
|
text = text .. string.format( "Coordinate = %s\n", EventData.MarkCoordinate and EventData.MarkCoordinate:ToStringLLDMS() or "Nowhere" )
|
||||||
text=text..string.format("Text: \n%s", tostring(EventData.MarkText))
|
text = text .. string.format( "Text: \n%s", tostring( EventData.MarkText ) )
|
||||||
self:T2(self.lid..text)
|
self:T2( self.lid .. text )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -733,17 +744,17 @@ end
|
|||||||
-- @param #string Event Event.
|
-- @param #string Event Event.
|
||||||
-- @param #string To To state.
|
-- @param #string To To state.
|
||||||
-- @param Core.Event#EVENTDATA EventData Event data table.
|
-- @param Core.Event#EVENTDATA EventData Event data table.
|
||||||
function MARKER:onafterChanged(From, Event, To, EventData)
|
function MARKER:onafterChanged( From, Event, To, EventData )
|
||||||
|
|
||||||
-- Debug info.
|
-- Debug info.
|
||||||
local text=string.format("Captured event MarkChange for myself:\n")
|
local text = string.format( "Captured event MarkChange for myself:\n" )
|
||||||
text=text..string.format("Marker ID = %s\n", tostring(EventData.MarkID))
|
text = text .. string.format( "Marker ID = %s\n", tostring( EventData.MarkID ) )
|
||||||
text=text..string.format("Coalition = %s\n", tostring(EventData.MarkCoalition))
|
text = text .. string.format( "Coalition = %s\n", tostring( EventData.MarkCoalition ) )
|
||||||
text=text..string.format("Group ID = %s\n", tostring(EventData.MarkGroupID))
|
text = text .. string.format( "Group ID = %s\n", tostring( EventData.MarkGroupID ) )
|
||||||
text=text..string.format("Initiator = %s\n", EventData.IniUnit and EventData.IniUnit:GetName() or "Nobody")
|
text = text .. string.format( "Initiator = %s\n", EventData.IniUnit and EventData.IniUnit:GetName() or "Nobody" )
|
||||||
text=text..string.format("Coordinate = %s\n", EventData.MarkCoordinate and EventData.MarkCoordinate:ToStringLLDMS() or "Nowhere")
|
text = text .. string.format( "Coordinate = %s\n", EventData.MarkCoordinate and EventData.MarkCoordinate:ToStringLLDMS() or "Nowhere" )
|
||||||
text=text..string.format("Text: \n%s", tostring(EventData.MarkText))
|
text = text .. string.format( "Text: \n%s", tostring( EventData.MarkText ) )
|
||||||
self:T2(self.lid..text)
|
self:T2( self.lid .. text )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -753,9 +764,9 @@ end
|
|||||||
-- @param #string Event Event.
|
-- @param #string Event Event.
|
||||||
-- @param #string To To state.
|
-- @param #string To To state.
|
||||||
-- @param #string Text The updated text, displayed in the mark panel.
|
-- @param #string Text The updated text, displayed in the mark panel.
|
||||||
function MARKER:onafterTextUpdate(From, Event, To, Text)
|
function MARKER:onafterTextUpdate( From, Event, To, Text )
|
||||||
|
|
||||||
self:T(self.lid..string.format("New Marker Text:\n%s", Text))
|
self:T( self.lid .. string.format( "New Marker Text:\n%s", Text ) )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -765,12 +776,8 @@ end
|
|||||||
-- @param #string Event Event.
|
-- @param #string Event Event.
|
||||||
-- @param #string To To state.
|
-- @param #string To To state.
|
||||||
-- @param Core.Point#COORDINATE Coordinate The updated coordinates.
|
-- @param Core.Point#COORDINATE Coordinate The updated coordinates.
|
||||||
function MARKER:onafterCoordUpdate(From, Event, To, Coordinate)
|
function MARKER:onafterCoordUpdate( From, Event, To, Coordinate )
|
||||||
|
|
||||||
self:T(self.lid..string.format("New Marker Coordinate in LL DMS: %s", Coordinate:ToStringLLDMS()))
|
self:T( self.lid .. string.format( "New Marker Coordinate in LL DMS: %s", Coordinate:ToStringLLDMS() ) )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -57,3 +57,40 @@ end
|
|||||||
function SCENERY:GetThreatLevel()
|
function SCENERY:GetThreatLevel()
|
||||||
return 0, "Scenery"
|
return 0, "Scenery"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Find a SCENERY object by it's name/id.
|
||||||
|
--@param #SCENERY self
|
||||||
|
--@param #string name The name/id of the scenery object as taken from the ME. Ex. '595785449'
|
||||||
|
--@return #SCENERY Scenery Object or nil if not found.
|
||||||
|
function SCENERY:FindByName(name)
|
||||||
|
local findAirbase = function ()
|
||||||
|
local airbases = AIRBASE.GetAllAirbases()
|
||||||
|
for index,airbase in pairs(airbases) do
|
||||||
|
local surftype = airbase:GetCoordinate():GetSurfaceType()
|
||||||
|
if surftype ~= land.SurfaceType.SHALLOW_WATER and surftype ~= land.SurfaceType.WATER then
|
||||||
|
return airbase:GetCoordinate()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local sceneryScan = function (scancoord)
|
||||||
|
if scancoord ~= nil then
|
||||||
|
local _,_,sceneryfound,_,_,scenerylist = scancoord:ScanObjects(200, false, false, true)
|
||||||
|
if sceneryfound == true then
|
||||||
|
scenerylist[1].id_ = name
|
||||||
|
SCENERY.SceneryObject = SCENERY:Register(scenerylist[1].id_, scenerylist[1])
|
||||||
|
return SCENERY.SceneryObject
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
if SCENERY.SceneryObject then
|
||||||
|
SCENERY.SceneryObject.SceneryObject.id_ = name
|
||||||
|
SCENERY.SceneryObject.SceneryName = name
|
||||||
|
return SCENERY:Register(SCENERY.SceneryObject.SceneryObject.id_, SCENERY.SceneryObject.SceneryObject)
|
||||||
|
else
|
||||||
|
return sceneryScan(findAirbase())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
--
|
--
|
||||||
-- @field #STATIC
|
-- @field #STATIC
|
||||||
STATIC = {
|
STATIC = {
|
||||||
ClassName = "STATIC",
|
ClassName = "STATIC",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -85,7 +85,7 @@ function STATIC:FindByName( StaticName, RaiseError )
|
|||||||
self.StaticName = StaticName
|
self.StaticName = StaticName
|
||||||
|
|
||||||
if StaticFound then
|
if StaticFound then
|
||||||
return StaticFound
|
return StaticFound
|
||||||
end
|
end
|
||||||
|
|
||||||
if RaiseError == nil or RaiseError == true then
|
if RaiseError == nil or RaiseError == true then
|
||||||
@@ -256,4 +256,3 @@ function STATIC:ReSpawnAt(Coordinate, Heading, Delay)
|
|||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user