mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-08-15 10:47:21 +00:00
Compare commits
168 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
8af3f89c14 | ||
|
|
fe3079caad | ||
|
|
61ac6b4131 | ||
|
|
36cb189512 | ||
|
|
15f9843878 | ||
|
|
67f847dd16 | ||
|
|
8b9143d3f1 | ||
|
|
0388d47f23 | ||
|
|
de9b173d9b | ||
|
|
2cecc526fb | ||
|
|
968d178317 | ||
|
|
3c477b872a | ||
|
|
77e6088114 | ||
|
|
edd6594953 | ||
|
|
f8c05c99d0 | ||
|
|
50f6d98b49 | ||
|
|
147eeb05f6 | ||
|
|
d8cb15a577 | ||
|
|
0daac876ea | ||
|
|
1832125022 | ||
|
|
c311c40b72 | ||
|
|
db516a2077 | ||
|
|
ff8766669c | ||
|
|
06dc9a732e | ||
|
|
50c74d0852 | ||
|
|
1c97eb6f3c | ||
|
|
69449430d1 | ||
|
|
663cd34aa3 | ||
|
|
cfed6f5153 | ||
|
|
2b22d5288c | ||
|
|
a64424ecc8 | ||
|
|
fd1b2ecb86 | ||
|
|
6cae3e62cf | ||
|
|
05ce7e4513 | ||
|
|
136bd19f19 | ||
|
|
8873504daf | ||
|
|
a844a5d697 | ||
|
|
a49f4eaa21 | ||
|
|
e6e2651f8c | ||
|
|
b93ba13644 | ||
|
|
e4a51951b0 | ||
|
|
ad56e39942 | ||
|
|
ea09dc5a6e | ||
|
|
8ecfd913a3 | ||
|
|
53367c786e | ||
|
|
db5797bb4e | ||
|
|
5e8fe97752 | ||
|
|
393fa0bfbb | ||
|
|
4f51884b9d | ||
|
|
4c5c320073 | ||
|
|
9098590568 | ||
|
|
555bb7e68b | ||
|
|
c0a18957f0 | ||
|
|
2cf939560e | ||
|
|
9d3a7aae78 | ||
|
|
f6ed592f92 | ||
|
|
c98757d13c | ||
|
|
17378f509e | ||
|
|
7f18ea0e7a | ||
|
|
5172619cb1 | ||
|
|
3962529698 | ||
|
|
6481d5d41e | ||
|
|
6cc3d73c04 | ||
|
|
e541e39403 | ||
|
|
c7ea45e5fd | ||
|
|
20f28b3d2c | ||
|
|
f3f63ab8aa | ||
|
|
e91090cfff | ||
|
|
1a7fb3c13e | ||
|
|
59857ed79d | ||
|
|
4797665939 | ||
|
|
b89749036d | ||
|
|
c6268488de | ||
|
|
de04369703 | ||
|
|
05b6f19a87 | ||
|
|
2753df8216 |
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
|
||||
@@ -40,8 +40,8 @@
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB.
|
||||
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
||||
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
|
||||
-- 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_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.
|
||||
-- * **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
|
||||
--
|
||||
|
||||
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.
|
||||
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
||||
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
|
||||
-- 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_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.
|
||||
-- * **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
|
||||
--
|
||||
|
||||
@@ -39,8 +39,8 @@
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB.
|
||||
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
||||
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
|
||||
-- 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.
|
||||
-- * **Detect** ( Group ): The AI is detecting targets.
|
||||
-- * **Detected** ( Group ): The AI has detected new targets.
|
||||
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB.
|
||||
-- * **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
|
||||
--
|
||||
@@ -100,8 +100,8 @@
|
||||
-- ## 6. Manage the "out of fuel" in the AI_A2A_PATROL
|
||||
--
|
||||
-- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base.
|
||||
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel treshold is calculated.
|
||||
-- When the fuel treshold is reached, the AI will continue for a given time its patrol task in orbit,
|
||||
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel threshold is calculated.
|
||||
-- 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.
|
||||
-- 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.
|
||||
@@ -109,7 +109,7 @@
|
||||
-- ## 7. Manage "damage" behaviour of the AI in the AI_A2A_PATROL
|
||||
--
|
||||
-- When the AI is damaged, it is required that a new Patrol is started. However, damage cannon be foreseen early on.
|
||||
-- Therefore, when the damage treshold is reached, the AI will return immediately to the home base (RTB).
|
||||
-- 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.
|
||||
--
|
||||
-- ===
|
||||
|
||||
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.
|
||||
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
||||
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
|
||||
-- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
|
||||
@@ -370,11 +370,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.
|
||||
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel treshold is calculated.
|
||||
-- When the fuel treshold is reached, the AI will continue for a given time its patrol task in orbit, while a new AIControllable is targetted to the AI_AIR.
|
||||
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel threshold is calculated.
|
||||
-- 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.
|
||||
-- @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.
|
||||
-- @return #AI_AIR self
|
||||
function AI_AIR:SetFuelThreshold( FuelThresholdPercentage, OutOfFuelOrbitTime )
|
||||
@@ -387,14 +387,14 @@ function AI_AIR:SetFuelThreshold( FuelThresholdPercentage, OutOfFuelOrbitTime )
|
||||
return self
|
||||
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.
|
||||
-- Therefore, when the damage treshold is reached,
|
||||
-- Therefore, when the damage threshold is reached,
|
||||
-- the AI will return immediately to the home base (RTB).
|
||||
-- Note that for groups, the average damage of the complete group will be calculated.
|
||||
-- So, in a group of 4 airplanes, 2 lost and 2 with damage 0.2, the damage treshold will be 0.25.
|
||||
-- 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 #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
|
||||
function AI_AIR:SetDamageThreshold( PatrolDamageThreshold )
|
||||
|
||||
@@ -476,7 +476,7 @@ function AI_AIR:onafterStatus()
|
||||
|
||||
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.
|
||||
if Fuel < self.FuelThresholdPercentage then
|
||||
|
||||
@@ -585,7 +585,7 @@ function AI_AIR:onafterRTB( AIGroup, From, Event, To )
|
||||
|
||||
if AIGroup and AIGroup:IsAlive() then
|
||||
|
||||
self:I( "Group " .. AIGroup:GetName() .. " ... RTB! ( " .. self:GetState() .. " )" )
|
||||
self:T( "Group " .. AIGroup:GetName() .. " ... RTB! ( " .. self:GetState() .. " )" )
|
||||
|
||||
self:ClearTargetDistance()
|
||||
--AIGroup:ClearTasks()
|
||||
|
||||
@@ -10,11 +10,11 @@
|
||||
-- * 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.
|
||||
-- * 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.
|
||||
-- * 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.
|
||||
-- * Add multiple squadrons to different airbases, farps or carriers.
|
||||
-- * Add multiple squadrons to different airbases, FARPs or carriers.
|
||||
-- * Define different ranges to engage upon.
|
||||
-- * Establish an automatic in air refuel process for planes using refuel tankers.
|
||||
-- * 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.
|
||||
--
|
||||
-- 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?
|
||||
@@ -128,7 +128,7 @@
|
||||
-- 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**.
|
||||
-- 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.
|
||||
-- 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
|
||||
-- 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.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
|
||||
-- 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.
|
||||
@@ -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.
|
||||
--
|
||||
-- ## 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 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
|
||||
--
|
||||
@@ -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.
|
||||
--
|
||||
-- ## 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**.
|
||||
--
|
||||
@@ -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.
|
||||
-- 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.
|
||||
--
|
||||
@@ -843,7 +843,7 @@ do -- AI_AIR_DISPATCHER
|
||||
-- A2ADispatcher:SetSquadronCapInterval("Sochi", 2, 30, 600, 1 )
|
||||
-- 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:SetDefaultTanker( "Tanker" )
|
||||
--
|
||||
@@ -882,10 +882,7 @@ do -- AI_AIR_DISPATCHER
|
||||
-- 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
|
||||
-- of the squadron will be destroyed. This to keep consistency of air operations not to confuse the players.
|
||||
--
|
||||
--
|
||||
--
|
||||
--
|
||||
--
|
||||
-- @field #AI_AIR_DISPATCHER
|
||||
AI_AIR_DISPATCHER = {
|
||||
ClassName = "AI_AIR_DISPATCHER",
|
||||
@@ -914,10 +911,10 @@ do -- AI_AIR_DISPATCHER
|
||||
--- Enumerator for spawns at airbases
|
||||
-- @type AI_AIR_DISPATCHER.Takeoff
|
||||
-- @extends Wrapper.Group#GROUP.Takeoff
|
||||
|
||||
|
||||
--- @field #AI_AIR_DISPATCHER.Takeoff Takeoff
|
||||
AI_AIR_DISPATCHER.Takeoff = GROUP.Takeoff
|
||||
|
||||
|
||||
--- Defnes Landing location.
|
||||
-- @field #AI_AIR_DISPATCHER.Landing
|
||||
AI_AIR_DISPATCHER.Landing = {
|
||||
@@ -925,7 +922,7 @@ do -- AI_AIR_DISPATCHER
|
||||
AtRunway = 2,
|
||||
AtEngineShutdown = 3,
|
||||
}
|
||||
|
||||
|
||||
--- A defense queue item description
|
||||
-- @type AI_AIR_DISPATCHER.DefenseQueueItem
|
||||
-- @field Squadron
|
||||
@@ -936,7 +933,7 @@ do -- AI_AIR_DISPATCHER
|
||||
-- @field Functional.Detection#DETECTION_BASE AttackerDetection
|
||||
-- @field DefenderGrouping
|
||||
-- @field #string SquadronName The name of the squadron.
|
||||
|
||||
|
||||
--- 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.
|
||||
-- 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 ...
|
||||
-- @type AI_AIR_DISPATCHER.DefenseQueue
|
||||
-- @list<#AI_AIR_DISPATCHER.DefenseQueueItem> DefenseQueueItem A list of all defenses being queued ...
|
||||
|
||||
|
||||
--- @field #AI_AIR_DISPATCHER.DefenseQueue DefenseQueue
|
||||
AI_AIR_DISPATCHER.DefenseQueue = {}
|
||||
|
||||
|
||||
|
||||
--- Defense approach types
|
||||
-- @type #AI_AIR_DISPATCHER.DefenseApproach
|
||||
AI_AIR_DISPATCHER.DefenseApproach = {
|
||||
Random = 1,
|
||||
Distance = 2,
|
||||
}
|
||||
|
||||
|
||||
--- 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 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 Functional.Detection#DETECTION_BASE Detection The DETECTION object that will detects targets using the the Early Warning Radar network.
|
||||
-- @return #AI_AIR_DISPATCHER self
|
||||
-- @usage
|
||||
--
|
||||
-- -- Setup the Detection, using DETECTION_AREAS.
|
||||
-- -- First define the SET of GROUPs that are defining the EWR network.
|
||||
-- -- Here with prefixes DF CCCP AWACS, DF CCCP EWR.
|
||||
-- DetectionSetGroup = SET_GROUP:New()
|
||||
-- DetectionSetGroup:FilterPrefixes( { "DF CCCP AWACS", "DF CCCP EWR" } )
|
||||
-- DetectionSetGroup:FilterStart()
|
||||
--
|
||||
-- -- Define the DETECTION_AREAS, using the DetectionSetGroup, with a 30km grouping radius.
|
||||
-- Detection = DETECTION_AREAS:New( DetectionSetGroup, 30000 )
|
||||
--
|
||||
-- -- Now Setup the AIR dispatcher, and initialize it using the Detection object.
|
||||
-- AIRDispatcher = AI_AIR_DISPATCHER:New( Detection ) --
|
||||
--
|
||||
--
|
||||
-- -- Setup the Detection, using DETECTION_AREAS.
|
||||
-- -- First define the SET of GROUPs that are defining the EWR network.
|
||||
-- -- Here with prefixes DF CCCP AWACS, DF CCCP EWR.
|
||||
-- DetectionSetGroup = SET_GROUP:New()
|
||||
-- DetectionSetGroup:FilterPrefixes( { "DF CCCP AWACS", "DF CCCP EWR" } )
|
||||
-- DetectionSetGroup:FilterStart()
|
||||
--
|
||||
-- -- Define the DETECTION_AREAS, using the DetectionSetGroup, with a 30km grouping radius.
|
||||
-- Detection = DETECTION_AREAS:New( DetectionSetGroup, 30000 )
|
||||
--
|
||||
-- -- Now Setup the AIR dispatcher, and initialize it using the Detection object.
|
||||
-- AIRDispatcher = AI_AIR_DISPATCHER:New( Detection )
|
||||
--
|
||||
function AI_AIR_DISPATCHER:New( Detection )
|
||||
|
||||
-- Inherits from DETECTION_MANAGER
|
||||
@@ -1435,17 +1431,17 @@ do -- AI_AIR_DISPATCHER
|
||||
end
|
||||
|
||||
|
||||
--- Set the default damage treshold 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.
|
||||
--- Set the default damage threshold when defenders will 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 #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
|
||||
-- @usage
|
||||
--
|
||||
-- -- Now Setup the AIR dispatcher, and initialize it using the Detection object.
|
||||
-- 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.
|
||||
--
|
||||
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.
|
||||
-- @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,
|
||||
-- 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.
|
||||
@@ -2028,7 +2024,7 @@ do -- AI_AIR_DISPATCHER
|
||||
--- Defines the amount of extra planes that will take-off as part of the defense system.
|
||||
-- @param #AI_AIR_DISPATCHER self
|
||||
-- @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,
|
||||
-- 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.
|
||||
@@ -2068,7 +2064,7 @@ do -- AI_AIR_DISPATCHER
|
||||
--- Gets the overhead of planes as part of the defense system, in comparison with the attackers.
|
||||
-- @param #AI_AIR_DISPATCHER self
|
||||
-- @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,
|
||||
-- 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.
|
||||
@@ -2674,17 +2670,17 @@ do -- AI_AIR_DISPATCHER
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set the default fuel treshold 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.
|
||||
--- Set the default fuel threshold when defenders will RTB or Refuel in the air.
|
||||
-- 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 #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
|
||||
-- @usage
|
||||
--
|
||||
-- -- Now Setup the AIR dispatcher, and initialize it using the Detection object.
|
||||
-- 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.
|
||||
--
|
||||
function AI_AIR_DISPATCHER:SetDefaultFuelThreshold( FuelThreshold )
|
||||
@@ -2695,18 +2691,18 @@ do -- AI_AIR_DISPATCHER
|
||||
end
|
||||
|
||||
|
||||
--- Set the fuel treshold 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.
|
||||
--- Set the fuel threshold for the squadron when defenders will RTB or Refuel in the air.
|
||||
-- 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 #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
|
||||
-- @usage
|
||||
--
|
||||
-- -- Now Setup the AIR dispatcher, and initialize it using the Detection object.
|
||||
-- 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.
|
||||
--
|
||||
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.
|
||||
-- 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.
|
||||
--
|
||||
-- -- 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.
|
||||
-- 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.
|
||||
--
|
||||
-- -- Now Setup the squadron tanker.
|
||||
@@ -2847,7 +2843,7 @@ do -- AI_AIR_DISPATCHER
|
||||
-- @param #AI_AIR_DISPATCHER self
|
||||
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 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.
|
||||
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
||||
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
|
||||
-- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
|
||||
@@ -39,8 +39,8 @@
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB.
|
||||
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
||||
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
|
||||
-- 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_AIR_PATROL.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}.
|
||||
-- * **@{#AI_AIR_PATROL.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task.
|
||||
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB.
|
||||
-- * **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
|
||||
--
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
-- 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.
|
||||
-- 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 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_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.
|
||||
-- * **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**
|
||||
--
|
||||
@@ -602,7 +602,7 @@ function AI_BAI_ZONE:onafterEngage( Controllable, From, Event, To,
|
||||
|
||||
self:SetRefreshTimeInterval( 2 )
|
||||
self:SetDetectionActivated()
|
||||
self:__Target( -2 ) -- Start Targetting
|
||||
self:__Target( -2 ) -- Start targeting
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
--
|
||||
-- * Patrol AI airplanes within a given zone.
|
||||
-- * 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.
|
||||
--
|
||||
--
|
||||
@@ -65,8 +65,8 @@
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB.
|
||||
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
||||
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
|
||||
-- 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_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.
|
||||
-- * **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
|
||||
--
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
-- 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.
|
||||
-- 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 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_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.
|
||||
-- * **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.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -520,7 +520,7 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
|
||||
|
||||
self:SetRefreshTimeInterval( 2 )
|
||||
self:SetDetectionActivated()
|
||||
self:__Target( -2 ) -- Start Targetting
|
||||
self:__Target( -2 ) -- Start targeting
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -47,19 +47,21 @@ function AI_CARGO:New( Carrier, CargoSet )
|
||||
|
||||
self:SetStartState( "Unloaded" )
|
||||
|
||||
self:AddTransition( "Unloaded", "Pickup", "*" )
|
||||
self:AddTransition( "Loaded", "Deploy", "*" )
|
||||
-- Board
|
||||
self:AddTransition( "Unloaded", "Pickup", "Unloaded" )
|
||||
self:AddTransition( "*", "Load", "*" )
|
||||
self:AddTransition( "*", "Reload", "*" )
|
||||
self:AddTransition( "*", "Board", "*" )
|
||||
self:AddTransition( "*", "Loaded", "Loaded" )
|
||||
self:AddTransition( "Loaded", "PickedUp", "Loaded" )
|
||||
|
||||
self:AddTransition( "*", "Load", "Boarding" )
|
||||
self:AddTransition( "Boarding", "Board", "Boarding" )
|
||||
self:AddTransition( "Loaded", "Board", "Loaded" )
|
||||
self:AddTransition( "Boarding", "Loaded", "Boarding" )
|
||||
self:AddTransition( "Boarding", "PickedUp", "Loaded" )
|
||||
|
||||
self:AddTransition( "Loaded", "Unload", "Unboarding" )
|
||||
self:AddTransition( "Unboarding", "Unboard", "Unboarding" )
|
||||
self:AddTransition( "Unboarding", "Unloaded", "Unboarding" )
|
||||
self:AddTransition( "Unboarding", "Deployed", "Unloaded" )
|
||||
-- Unload
|
||||
self:AddTransition( "Loaded", "Deploy", "*" )
|
||||
self:AddTransition( "*", "Unload", "*" )
|
||||
self:AddTransition( "*", "Unboard", "*" )
|
||||
self:AddTransition( "*", "Unloaded", "Unloaded" )
|
||||
self:AddTransition( "Unloaded", "Deployed", "Unloaded" )
|
||||
|
||||
|
||||
--- Pickup Handler OnBefore for AI_CARGO
|
||||
-- @function [parent=#AI_CARGO] OnBeforePickup
|
||||
@@ -393,7 +395,7 @@ end
|
||||
function AI_CARGO:onafterBoard( Carrier, From, Event, To, Cargo, CarrierUnit, PickupZone )
|
||||
self:F( { Carrier, From, Event, To, Cargo, CarrierUnit:GetName() } )
|
||||
|
||||
if Carrier and Carrier:IsAlive() and From == "Boarding" then
|
||||
if Carrier and Carrier:IsAlive() then
|
||||
self:F({ IsLoaded = Cargo:IsLoaded(), Cargo:GetName(), Carrier:GetName() } )
|
||||
if not Cargo:IsLoaded() and not Cargo:IsDestroyed() then
|
||||
self:__Board( -10, Cargo, CarrierUnit, PickupZone )
|
||||
@@ -509,7 +511,7 @@ end
|
||||
function AI_CARGO:onafterUnboard( Carrier, From, Event, To, Cargo, CarrierUnit, DeployZone, Defend )
|
||||
self:F( { Carrier, From, Event, To, Cargo:GetName(), DeployZone = DeployZone, Defend = Defend } )
|
||||
|
||||
if Carrier and Carrier:IsAlive() and From == "Unboarding" then
|
||||
if Carrier and Carrier:IsAlive() then
|
||||
if not Cargo:IsUnLoaded() then
|
||||
self:__Unboard( 10, Cargo, CarrierUnit, DeployZone, Defend )
|
||||
return
|
||||
@@ -580,4 +582,3 @@ function AI_CARGO:onafterDeployed( Carrier, From, Event, To, DeployZone, Defend
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -98,7 +98,8 @@ function AI_CARGO_APC:New( APC, CargoSet, CombatRadius )
|
||||
self:AddTransition( "*", "Guard", "Unloaded" )
|
||||
self:AddTransition( "*", "Home", "*" )
|
||||
self:AddTransition( "*", "Reload", "Boarding" )
|
||||
|
||||
self:AddTransition( "*", "Deployed", "*" )
|
||||
self:AddTransition( "*", "PickedUp", "*" )
|
||||
self:AddTransition( "*", "Destroyed", "Destroyed" )
|
||||
|
||||
self:SetCombatRadius( CombatRadius )
|
||||
|
||||
@@ -64,20 +64,24 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet )
|
||||
self.Zone = ZONE_GROUP:New( Helicopter:GetName(), Helicopter, 300 )
|
||||
|
||||
self:SetStartState( "Unloaded" )
|
||||
-- Boarding
|
||||
self:AddTransition( "Unloaded", "Pickup", "Unloaded" )
|
||||
self:AddTransition( "*", "Landed", "*" )
|
||||
self:AddTransition( "*", "Load", "*" )
|
||||
self:AddTransition( "*", "Loaded", "Loaded" )
|
||||
self:AddTransition( "Loaded", "PickedUp", "Loaded" )
|
||||
|
||||
self:AddTransition( "Unloaded", "Pickup", "*" )
|
||||
self:AddTransition( "Loaded", "Deploy", "*" )
|
||||
self:AddTransition( "*", "Loaded", "Loaded" )
|
||||
self:AddTransition( "Unboarding", "Pickup", "Unloaded" )
|
||||
self:AddTransition( "Unloaded", "Unboard", "Unloaded" )
|
||||
self:AddTransition( "Unloaded", "Unloaded", "Unloaded" )
|
||||
self:AddTransition( "*", "PickedUp", "*" )
|
||||
self:AddTransition( "*", "Landed", "*" )
|
||||
self:AddTransition( "*", "Queue", "*" )
|
||||
self:AddTransition( "*", "Orbit" , "*" )
|
||||
-- Unboarding
|
||||
self:AddTransition( "Loaded", "Deploy", "*" )
|
||||
self:AddTransition( "*", "Queue", "*" )
|
||||
self:AddTransition( "*", "Orbit" , "*" )
|
||||
self:AddTransition( "*", "Destroyed", "*" )
|
||||
self:AddTransition( "*", "Unload", "*" )
|
||||
self:AddTransition( "*", "Unloaded", "Unloaded" )
|
||||
self:AddTransition( "Unloaded", "Deployed", "Unloaded" )
|
||||
|
||||
-- RTB
|
||||
self:AddTransition( "*", "Home" , "*" )
|
||||
|
||||
self:AddTransition( "*", "Destroyed", "Destroyed" )
|
||||
|
||||
--- Pickup Handler OnBefore for AI_CARGO_HELICOPTER
|
||||
-- @function [parent=#AI_CARGO_HELICOPTER] OnBeforePickup
|
||||
@@ -207,6 +211,9 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet )
|
||||
|
||||
self:SetCarrier( Helicopter )
|
||||
|
||||
self.landingspeed = 15 -- kph
|
||||
self.landingheight = 5.5 -- meter
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -255,6 +262,25 @@ function AI_CARGO_HELICOPTER:SetCarrier( Helicopter )
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set landingspeed and -height for helicopter landings. Adjust after tracing if your helis get stuck after landing.
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param #number speed Landing speed in kph(!), e.g. 15
|
||||
-- @param #number height Landing height in meters(!), e.g. 5.5
|
||||
-- @return #AI_CARGO_HELICOPTER self
|
||||
-- @usage If your choppers get stuck, add tracing to your script to determine if they hit the right parameters like so:
|
||||
--
|
||||
-- BASE:TraceOn()
|
||||
-- BASE:TraceClass("AI_CARGO_HELICOPTER")
|
||||
--
|
||||
-- Watch the DCS.log for entries stating `Helicopter:<name>, Height = Helicopter:<number>, Velocity = Helicopter:<number>`
|
||||
-- Adjust if necessary.
|
||||
function AI_CARGO_HELICOPTER:SetLandingSpeedAndHeight(speed, height)
|
||||
local _speed = speed or 15
|
||||
local _height = height or 5.5
|
||||
self.landingheight = _height
|
||||
self.landingspeed = _speed
|
||||
return self
|
||||
end
|
||||
|
||||
--- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param Wrapper.Group#GROUP Helicopter
|
||||
@@ -271,13 +297,13 @@ function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To )
|
||||
-- 1 - When the helo lands normally on the ground.
|
||||
-- 2 - when the helo is hit and goes RTB or even when it is destroyed.
|
||||
-- For point 2, this is an issue, the infantry may not unload in this case!
|
||||
-- So we check if the helo is on the ground, and velocity< 5.
|
||||
-- So we check if the helo is on the ground, and velocity< 15.
|
||||
-- Only then the infantry can unload (and load too, for consistency)!
|
||||
|
||||
self:F( { Helicopter:GetName(), Height = Helicopter:GetHeight( true ), Velocity = Helicopter:GetVelocityKMH() } )
|
||||
self:T( { Helicopter:GetName(), Height = Helicopter:GetHeight( true ), Velocity = Helicopter:GetVelocityKMH() } )
|
||||
|
||||
if self.RoutePickup == true then
|
||||
if Helicopter:GetHeight( true ) <= 5.5 and Helicopter:GetVelocityKMH() < 15 then
|
||||
if Helicopter:GetHeight( true ) <= self.landingheight then --and Helicopter:GetVelocityKMH() < self.landingspeed then
|
||||
--self:Load( Helicopter:GetPointVec2() )
|
||||
self:Load( self.PickupZone )
|
||||
self.RoutePickup = false
|
||||
@@ -285,7 +311,7 @@ function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To )
|
||||
end
|
||||
|
||||
if self.RouteDeploy == true then
|
||||
if Helicopter:GetHeight( true ) <= 5.5 and Helicopter:GetVelocityKMH() < 15 then
|
||||
if Helicopter:GetHeight( true ) <= self.landingheight then --and Helicopter:GetVelocityKMH() < self.landingspeed then
|
||||
self:Unload( self.DeployZone )
|
||||
self.RouteDeploy = false
|
||||
end
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
--
|
||||
-- * Patrol AI airplanes within a given zone.
|
||||
-- * Trigger detected events when enemy airplanes are detected.
|
||||
-- * Manage a fuel treshold to RTB on time.
|
||||
-- * Manage a fuel threshold to RTB on time.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -72,8 +72,8 @@
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB.
|
||||
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
||||
-- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
|
||||
-- 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.
|
||||
-- * **Detect** ( Group ): The AI is detecting targets.
|
||||
-- * **Detected** ( Group ): The AI has detected new targets.
|
||||
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB.
|
||||
-- * **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
|
||||
--
|
||||
@@ -133,8 +133,8 @@
|
||||
-- ## 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.
|
||||
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel treshold is calculated.
|
||||
-- When the fuel treshold is reached, the AI will continue for a given time its patrol task in orbit,
|
||||
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel threshold is calculated.
|
||||
-- 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.
|
||||
-- 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.
|
||||
@@ -142,7 +142,7 @@
|
||||
-- ## 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.
|
||||
-- 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.
|
||||
--
|
||||
-- ===
|
||||
@@ -581,11 +581,11 @@ function AI_PATROL_ZONE:ClearDetectedUnits()
|
||||
end
|
||||
|
||||
--- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base.
|
||||
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel treshold is calculated.
|
||||
-- When the fuel treshold is reached, the AI will continue for a given time its patrol task in orbit, while a new AIControllable is targetted to the AI_PATROL_ZONE.
|
||||
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel threshold is calculated.
|
||||
-- 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.
|
||||
-- @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.
|
||||
-- @return #AI_PATROL_ZONE self
|
||||
function AI_PATROL_ZONE:ManageFuel( PatrolFuelThresholdPercentage, PatrolOutOfFuelOrbitTime )
|
||||
@@ -596,14 +596,14 @@ function AI_PATROL_ZONE:ManageFuel( PatrolFuelThresholdPercentage, PatrolOutOfFu
|
||||
return self
|
||||
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.
|
||||
-- Therefore, when the damage treshold is reached,
|
||||
-- Therefore, when the damage threshold is reached,
|
||||
-- the AI will return immediately to the home base (RTB).
|
||||
-- Note that for groups, the average damage of the complete group will be calculated.
|
||||
-- So, in a group of 4 airplanes, 2 lost and 2 with damage 0.2, the damage treshold will be 0.25.
|
||||
-- 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 #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
|
||||
function AI_PATROL_ZONE:ManageDamage( PatrolDamageThreshold )
|
||||
|
||||
|
||||
@@ -194,7 +194,7 @@
|
||||
-- * is of type `Workmaterials`
|
||||
-- * 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 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:
|
||||
--
|
||||
@@ -220,7 +220,7 @@
|
||||
-- * is of type `Workmaterials`
|
||||
-- * 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 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:
|
||||
--
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,9 @@
|
||||
--- **Core** - TACAN and other beacons.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
--
|
||||
-- * Provide beacon functionality to assist pilots.
|
||||
--
|
||||
-- ===
|
||||
@@ -14,34 +14,34 @@
|
||||
-- @image Core_Radio.JPG
|
||||
|
||||
--- *In order for the light to shine so brightly, the darkness must be present.* -- Francis Bacon
|
||||
--
|
||||
--
|
||||
-- After attaching a @{#BEACON} to your @{Wrapper.Positionable#POSITIONABLE}, you need to select the right function to activate the kind of beacon you want.
|
||||
-- There are two types of BEACONs available : the AA TACAN Beacon and the general purpose Radio Beacon.
|
||||
-- Note that in both case, you can set an optional parameter : the `BeaconDuration`. This can be very usefull to simulate the battery time if your BEACON is
|
||||
-- attach to a cargo crate, for exemple.
|
||||
--
|
||||
-- attach to a cargo crate, for exemple.
|
||||
--
|
||||
-- ## AA TACAN Beacon usage
|
||||
--
|
||||
--
|
||||
-- This beacon only works with airborne @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}. Use @{#BEACON:AATACAN}() to set the beacon parameters and start the beacon.
|
||||
-- Use @#BEACON:StopAATACAN}() to stop it.
|
||||
--
|
||||
--
|
||||
-- ## General Purpose Radio Beacon usage
|
||||
--
|
||||
--
|
||||
-- This beacon will work with any @{Wrapper.Positionable#POSITIONABLE}, but **it won't follow the @{Wrapper.Positionable#POSITIONABLE}** ! This means that you should only use it with
|
||||
-- @{Wrapper.Positionable#POSITIONABLE} that don't move, or move very slowly. Use @{#BEACON:RadioBeacon}() to set the beacon parameters and start the beacon.
|
||||
-- Use @{#BEACON:StopRadioBeacon}() to stop it.
|
||||
--
|
||||
--
|
||||
-- @type BEACON
|
||||
-- @field #string ClassName Name of the class "BEACON".
|
||||
-- @field Wrapper.Controllable#CONTROLLABLE Positionable The @{#CONTROLLABLE} that will receive radio capabilities.
|
||||
-- @extends Core.Base#BASE
|
||||
BEACON = {
|
||||
ClassName = "BEACON",
|
||||
ClassName = "BEACON",
|
||||
Positionable = nil,
|
||||
name = nil,
|
||||
name = nil,
|
||||
}
|
||||
|
||||
--- Beacon types supported by DCS.
|
||||
--- Beacon types supported by DCS.
|
||||
-- @type BEACON.Type
|
||||
-- @field #number NULL
|
||||
-- @field #number VOR
|
||||
@@ -65,19 +65,19 @@ BEACON = {
|
||||
-- @field #number ICLS_GLIDESLOPE
|
||||
-- @field #number NAUTICAL_HOMER
|
||||
BEACON.Type={
|
||||
NULL = 0,
|
||||
NULL = 0,
|
||||
VOR = 1,
|
||||
DME = 2,
|
||||
VOR_DME = 3,
|
||||
VOR_DME = 3,
|
||||
TACAN = 4,
|
||||
VORTAC = 5,
|
||||
VORTAC = 5,
|
||||
RSBN = 128,
|
||||
BROADCAST_STATION = 1024,
|
||||
BROADCAST_STATION = 1024,
|
||||
HOMER = 8,
|
||||
AIRPORT_HOMER = 4104,
|
||||
AIRPORT_HOMER_WITH_MARKER = 4136,
|
||||
AIRPORT_HOMER = 4104,
|
||||
AIRPORT_HOMER_WITH_MARKER = 4136,
|
||||
ILS_FAR_HOMER = 16408,
|
||||
ILS_NEAR_HOMER = 16424,
|
||||
ILS_NEAR_HOMER = 16424,
|
||||
ILS_LOCALIZER = 16640,
|
||||
ILS_GLIDESLOPE = 16896,
|
||||
PRMG_LOCALIZER = 33024,
|
||||
@@ -95,11 +95,11 @@ BEACON.Type={
|
||||
-- @field #number TACAN TACtical Air Navigation system on ground.
|
||||
-- @field #number TACAN_TANKER_X TACtical Air Navigation system for tankers on X band.
|
||||
-- @field #number TACAN_TANKER_Y TACtical Air Navigation system for tankers on Y band.
|
||||
-- @field #number VOR Very High Frequency Omni-Directional Range
|
||||
-- @field #number VOR Very High Frequency Omnidirectional Range
|
||||
-- @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_GLIDESLOPE PRGM glideslope.
|
||||
-- @field #number PRGM_GLIDESLOPE PRGM glide slope.
|
||||
-- @field #number BROADCAST_STATION Broadcast station.
|
||||
-- @field #number VORTAC Radio-based navigational aid for aircraft pilots consisting of a co-located VHF omnidirectional range (VOR) beacon and a tactical air navigation system (TACAN) beacon.
|
||||
-- @field #number TACAN_AA_MODE_X TACtical Air Navigation for aircraft on X band.
|
||||
@@ -108,13 +108,13 @@ BEACON.Type={
|
||||
-- @field #number ICLS_LOCALIZER Carrier landing system.
|
||||
-- @field #number ICLS_GLIDESLOPE Carrier landing system.
|
||||
BEACON.System={
|
||||
PAR_10 = 1,
|
||||
RSBN_5 = 2,
|
||||
TACAN = 3,
|
||||
PAR_10 = 1,
|
||||
RSBN_5 = 2,
|
||||
TACAN = 3,
|
||||
TACAN_TANKER_X = 4,
|
||||
TACAN_TANKER_Y = 5,
|
||||
VOR = 6,
|
||||
ILS_LOCALIZER = 7,
|
||||
VOR = 6,
|
||||
ILS_LOCALIZER = 7,
|
||||
ILS_GLIDESLOPE = 8,
|
||||
PRMG_LOCALIZER = 9,
|
||||
PRMG_GLIDESLOPE = 10,
|
||||
@@ -131,28 +131,27 @@ BEACON.System={
|
||||
-- If you want to create a BEACON, you probably should use @{Wrapper.Positionable#POSITIONABLE.GetBeacon}() instead.
|
||||
-- @param #BEACON self
|
||||
-- @param Wrapper.Positionable#POSITIONABLE Positionable The @{Positionable} that will receive radio capabilities.
|
||||
-- @return #BEACON Beacon object or #nil if the positionable is invalid.
|
||||
function BEACON:New(Positionable)
|
||||
-- @return #BEACON Beacon object or #nil if the POSITIONABLE is invalid.
|
||||
function BEACON:New( Positionable )
|
||||
|
||||
-- Inherit BASE.
|
||||
local self=BASE:Inherit(self, BASE:New()) --#BEACON
|
||||
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #BEACON
|
||||
|
||||
-- Debug.
|
||||
self:F(Positionable)
|
||||
|
||||
self:F( Positionable )
|
||||
|
||||
-- Set 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.name=Positionable:GetName()
|
||||
self:I(string.format("New BEACON %s", tostring(self.name)))
|
||||
self.name = Positionable:GetName()
|
||||
self:I( string.format( "New BEACON %s", tostring( self.name ) ) )
|
||||
return self
|
||||
end
|
||||
|
||||
self:E({"The passed positionable is invalid, no BEACON created", Positionable})
|
||||
|
||||
self:E( { "The passed POSITIONABLE is invalid, no BEACON created", Positionable } )
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Activates a TACAN BEACON.
|
||||
-- @param #BEACON self
|
||||
-- @param #number Channel TACAN channel, i.e. the "10" part in "10Y".
|
||||
@@ -162,53 +161,55 @@ end
|
||||
-- @param #number Duration How long will the beacon last in seconds. Omit for forever.
|
||||
-- @return #BEACON self
|
||||
-- @usage
|
||||
--
|
||||
-- -- Let's create a TACAN Beacon for a tanker
|
||||
-- local myUnit = UNIT:FindByName("MyUnit")
|
||||
-- local myUnit = UNIT:FindByName("MyUnit")
|
||||
-- local myBeacon = myUnit:GetBeacon() -- Creates the beacon
|
||||
--
|
||||
--
|
||||
-- myBeacon:ActivateTACAN(20, "Y", "TEXACO", true) -- Activate the beacon
|
||||
function BEACON:ActivateTACAN(Channel, Mode, Message, Bearing, Duration)
|
||||
self:T({channel=Channel, mode=Mode, callsign=Message, bearing=Bearing, duration=Duration})
|
||||
|
||||
--
|
||||
function BEACON:ActivateTACAN( Channel, Mode, Message, Bearing, Duration )
|
||||
self:T( { channel = Channel, mode = Mode, callsign = Message, bearing = Bearing, duration = Duration } )
|
||||
|
||||
-- Get frequency.
|
||||
local Frequency=UTILS.TACANToFrequency(Channel, Mode)
|
||||
|
||||
local Frequency = UTILS.TACANToFrequency( Channel, Mode )
|
||||
|
||||
-- Check.
|
||||
if not Frequency then
|
||||
self:E({"The passed TACAN channel is invalid, the BEACON is not emitting"})
|
||||
if not Frequency then
|
||||
self:E( { "The passed TACAN channel is invalid, the BEACON is not emitting" } )
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
-- Beacon type.
|
||||
local Type=BEACON.Type.TACAN
|
||||
|
||||
local Type = BEACON.Type.TACAN
|
||||
|
||||
-- Beacon system.
|
||||
local System=BEACON.System.TACAN
|
||||
|
||||
local System = BEACON.System.TACAN
|
||||
|
||||
-- Check if unit is an aircraft and set system accordingly.
|
||||
local AA=self.Positionable:IsAir()
|
||||
local AA = self.Positionable:IsAir()
|
||||
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.
|
||||
if Mode~="Y" then
|
||||
self:E({"WARNING: The POSITIONABLE you want to attach the AA Tacan Beacon is an aircraft: Mode should Y !The BEACON is not emitting.", self.Positionable})
|
||||
if Mode ~= "Y" then
|
||||
self:E( { "WARNING: The POSITIONABLE you want to attach the AA TACAN Beacon is an aircraft: Mode should Y! The BEACON is not emitting.", self.Positionable } )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Attached unit.
|
||||
local UnitID=self.Positionable:GetID()
|
||||
|
||||
local UnitID = self.Positionable:GetID()
|
||||
|
||||
-- Debug.
|
||||
self:I({string.format("BEACON Activating TACAN %s: Channel=%d%s, Morse=%s, Bearing=%s, Duration=%s!", tostring(self.name), Channel, Mode, Message, tostring(Bearing), tostring(Duration))})
|
||||
|
||||
self:I( { string.format( "BEACON Activating TACAN %s: Channel=%d%s, Morse=%s, Bearing=%s, Duration=%s!", tostring( self.name ), Channel, Mode, Message, tostring( Bearing ), tostring( Duration ) ) } )
|
||||
|
||||
-- Start beacon.
|
||||
self.Positionable:CommandActivateBeacon(Type, System, Frequency, UnitID, Channel, Mode, AA, Message, Bearing)
|
||||
|
||||
-- Stop sheduler.
|
||||
self.Positionable:CommandActivateBeacon( Type, System, Frequency, UnitID, Channel, Mode, AA, Message, Bearing )
|
||||
|
||||
-- Stop scheduler.
|
||||
if Duration then
|
||||
self.Positionable:DeactivateBeacon(Duration)
|
||||
self.Positionable:DeactivateBeacon( Duration )
|
||||
end
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -218,23 +219,23 @@ end
|
||||
-- @param #string Callsign The Message that is going to be coded in Morse and broadcasted by the beacon.
|
||||
-- @param #number Duration How long will the beacon last in seconds. Omit for forever.
|
||||
-- @return #BEACON self
|
||||
function BEACON:ActivateICLS(Channel, Callsign, Duration)
|
||||
self:F({Channel=Channel, Callsign=Callsign, Duration=Duration})
|
||||
|
||||
function BEACON:ActivateICLS( Channel, Callsign, Duration )
|
||||
self:F( { Channel = Channel, Callsign = Callsign, Duration = Duration } )
|
||||
|
||||
-- Attached unit.
|
||||
local UnitID=self.Positionable:GetID()
|
||||
|
||||
local UnitID = self.Positionable:GetID()
|
||||
|
||||
-- Debug
|
||||
self:T2({"ICLS BEACON started!"})
|
||||
|
||||
self:T2( { "ICLS BEACON started!" } )
|
||||
|
||||
-- Start beacon.
|
||||
self.Positionable:CommandActivateICLS(Channel, UnitID, Callsign)
|
||||
|
||||
-- Stop sheduler
|
||||
self.Positionable:CommandActivateICLS( Channel, UnitID, Callsign )
|
||||
|
||||
-- Stop scheduler
|
||||
if Duration then -- Schedule the stop of the BEACON if asked by the MD
|
||||
self.Positionable:DeactivateBeacon(Duration)
|
||||
self.Positionable:DeactivateBeacon( Duration )
|
||||
end
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -246,27 +247,29 @@ end
|
||||
-- @param #number BeaconDuration How long will the beacon last in seconds. Omit for forever.
|
||||
-- @return #BEACON self
|
||||
-- @usage
|
||||
--
|
||||
-- -- Let's create a TACAN Beacon for a tanker
|
||||
-- local myUnit = UNIT:FindByName("MyUnit")
|
||||
-- local myUnit = UNIT:FindByName("MyUnit")
|
||||
-- local myBeacon = myUnit:GetBeacon() -- Creates the beacon
|
||||
--
|
||||
--
|
||||
-- myBeacon:AATACAN(20, "TEXACO", true) -- Activate the beacon
|
||||
function BEACON:AATACAN(TACANChannel, Message, Bearing, BeaconDuration)
|
||||
self:F({TACANChannel, Message, Bearing, BeaconDuration})
|
||||
|
||||
--
|
||||
function BEACON:AATACAN( TACANChannel, Message, Bearing, BeaconDuration )
|
||||
self:F( { TACANChannel, Message, Bearing, BeaconDuration } )
|
||||
|
||||
local IsValid = true
|
||||
|
||||
|
||||
if not self.Positionable:IsAir() then
|
||||
self:E({"The POSITIONABLE you want to attach the AA Tacan Beacon is not an aircraft ! The BEACON is not emitting", self.Positionable})
|
||||
self:E( { "The POSITIONABLE you want to attach the AA TACAN Beacon is not an aircraft! The BEACON is not emitting", self.Positionable } )
|
||||
IsValid = false
|
||||
end
|
||||
|
||||
local Frequency = self:_TACANToFrequency(TACANChannel, "Y")
|
||||
if not Frequency then
|
||||
self:E({"The passed TACAN channel is invalid, the BEACON is not emitting"})
|
||||
|
||||
local Frequency = self:_TACANToFrequency( TACANChannel, "Y" )
|
||||
if not Frequency then
|
||||
self:E( { "The passed TACAN channel is invalid, the BEACON is not emitting" } )
|
||||
IsValid = false
|
||||
end
|
||||
|
||||
|
||||
-- I'm using the beacon type 4 (BEACON_TYPE_TACAN). For System, I'm using 5 (TACAN_TANKER_MODE_Y) if the bearing shows its bearing
|
||||
-- or 14 (TACAN_AA_MODE_Y) if it does not
|
||||
local System
|
||||
@@ -275,27 +278,26 @@ function BEACON:AATACAN(TACANChannel, Message, Bearing, BeaconDuration)
|
||||
else
|
||||
System = 14
|
||||
end
|
||||
|
||||
|
||||
if IsValid then -- Starts the BEACON
|
||||
self:T2({"AA TACAN BEACON started !"})
|
||||
self.Positionable:SetCommand({
|
||||
self:T2( { "AA TACAN BEACON started !" } )
|
||||
self.Positionable:SetCommand( {
|
||||
id = "ActivateBeacon",
|
||||
params = {
|
||||
type = 4,
|
||||
system = System,
|
||||
callsign = Message,
|
||||
frequency = Frequency,
|
||||
}
|
||||
})
|
||||
|
||||
},
|
||||
} )
|
||||
|
||||
if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD
|
||||
SCHEDULER:New(nil,
|
||||
function()
|
||||
SCHEDULER:New( nil, function()
|
||||
self:StopAATACAN()
|
||||
end, {}, BeaconDuration)
|
||||
end, {}, BeaconDuration )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -305,21 +307,19 @@ end
|
||||
function BEACON:StopAATACAN()
|
||||
self:F()
|
||||
if not self.Positionable then
|
||||
self:E({"Start the beacon first before stoping it !"})
|
||||
self:E( { "Start the beacon first before stopping it!" } )
|
||||
else
|
||||
self.Positionable:SetCommand({
|
||||
id = 'DeactivateBeacon',
|
||||
params = {
|
||||
}
|
||||
})
|
||||
self.Positionable:SetCommand( {
|
||||
id = 'DeactivateBeacon',
|
||||
params = {},
|
||||
} )
|
||||
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.
|
||||
-- Although any frequency could be used, only 2 DCS Modules can home on radio beacons at the time of writing : the Huey and the Mi-8.
|
||||
-- They can home in on these specific frequencies :
|
||||
-- They can home in on these specific frequencies :
|
||||
-- * **Mi8**
|
||||
-- * R-828 -> 20-60MHz
|
||||
-- * ARKUD -> 100-150MHz (canal 1 : 114166, canal 2 : 114333, canal 3 : 114583, canal 4 : 121500, canal 5 : 123100, canal 6 : 124100) AM
|
||||
@@ -342,55 +342,54 @@ end
|
||||
--
|
||||
-- -- Set the beacon and start it
|
||||
-- UnitBeacon:RadioBeacon("MySoundFileSOS.ogg", 40, radio.modulation.FM, 20, 5*60)
|
||||
function BEACON:RadioBeacon(FileName, Frequency, Modulation, Power, BeaconDuration)
|
||||
self:F({FileName, Frequency, Modulation, Power, BeaconDuration})
|
||||
function BEACON:RadioBeacon( FileName, Frequency, Modulation, Power, BeaconDuration )
|
||||
self:F( { FileName, Frequency, Modulation, Power, BeaconDuration } )
|
||||
local IsValid = false
|
||||
|
||||
|
||||
-- Check the filename
|
||||
if type(FileName) == "string" then
|
||||
if FileName:find(".ogg") or FileName:find(".wav") then
|
||||
if not FileName:find("l10n/DEFAULT/") then
|
||||
if type( FileName ) == "string" then
|
||||
if FileName:find( ".ogg" ) or FileName:find( ".wav" ) then
|
||||
if not FileName:find( "l10n/DEFAULT/" ) then
|
||||
FileName = "l10n/DEFAULT/" .. FileName
|
||||
end
|
||||
IsValid = true
|
||||
end
|
||||
end
|
||||
if not IsValid then
|
||||
self:E({"File name invalid. Maybe something wrong with the extension ? ", FileName})
|
||||
self:E( { "File name invalid. Maybe something wrong with the extension? ", FileName } )
|
||||
end
|
||||
|
||||
|
||||
-- Check the Frequency
|
||||
if type(Frequency) ~= "number" and IsValid then
|
||||
self:E({"Frequency invalid. ", Frequency})
|
||||
if type( Frequency ) ~= "number" and IsValid then
|
||||
self:E( { "Frequency invalid. ", Frequency } )
|
||||
IsValid = false
|
||||
end
|
||||
Frequency = Frequency * 1000000 -- Conversion to Hz
|
||||
|
||||
|
||||
-- Check the modulation
|
||||
if Modulation ~= radio.modulation.AM and Modulation ~= radio.modulation.FM and IsValid then --TODO Maybe make this future proof if ED decides to add an other modulation ?
|
||||
self:E({"Modulation is invalid. Use DCS's enum radio.modulation.", Modulation})
|
||||
if Modulation ~= radio.modulation.AM and Modulation ~= radio.modulation.FM and IsValid then -- TODO: Maybe make this future proof if ED decides to add an other modulation ?
|
||||
self:E( { "Modulation is invalid. Use DCS's enum radio.modulation.", Modulation } )
|
||||
IsValid = false
|
||||
end
|
||||
|
||||
|
||||
-- Check the Power
|
||||
if type(Power) ~= "number" and IsValid then
|
||||
self:E({"Power is invalid. ", Power})
|
||||
if type( Power ) ~= "number" and IsValid then
|
||||
self:E( { "Power is invalid. ", Power } )
|
||||
IsValid = false
|
||||
end
|
||||
Power = math.floor(math.abs(Power)) --TODO Find what is the maximum power allowed by DCS and limit power to that
|
||||
|
||||
Power = math.floor( math.abs( Power ) ) -- TODO: Find what is the maximum power allowed by DCS and limit power to that
|
||||
|
||||
if IsValid then
|
||||
self:T2({"Activating Beacon on ", Frequency, Modulation})
|
||||
self:T2( { "Activating Beacon on ", Frequency, Modulation } )
|
||||
-- Note that this is looped. I have to give this transmission a unique name, I use the class ID
|
||||
trigger.action.radioTransmission(FileName, self.Positionable:GetPositionVec3(), Modulation, true, Frequency, Power, tostring(self.ID))
|
||||
|
||||
if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD
|
||||
SCHEDULER:New( nil,
|
||||
function()
|
||||
self:StopRadioBeacon()
|
||||
end, {}, BeaconDuration)
|
||||
end
|
||||
end
|
||||
trigger.action.radioTransmission( FileName, self.Positionable:GetPositionVec3(), Modulation, true, Frequency, Power, tostring( self.ID ) )
|
||||
|
||||
if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD
|
||||
SCHEDULER:New( nil, function()
|
||||
self:StopRadioBeacon()
|
||||
end, {}, BeaconDuration )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Stops the AA TACAN BEACON
|
||||
@@ -399,7 +398,7 @@ end
|
||||
function BEACON:StopRadioBeacon()
|
||||
self:F()
|
||||
-- The unique name of the transmission is the class ID
|
||||
trigger.action.stopRadioTransmission(tostring(self.ID))
|
||||
trigger.action.stopRadioTransmission( tostring( self.ID ) )
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -407,26 +406,26 @@ end
|
||||
-- @param #BEACON self
|
||||
-- @param #number TACANChannel
|
||||
-- @param #string TACANMode
|
||||
-- @return #number Frequecy
|
||||
-- @return #number Frequency
|
||||
-- @return #nil if parameters are invalid
|
||||
function BEACON:_TACANToFrequency(TACANChannel, TACANMode)
|
||||
self:F3({TACANChannel, TACANMode})
|
||||
function BEACON:_TACANToFrequency( TACANChannel, TACANMode )
|
||||
self:F3( { TACANChannel, TACANMode } )
|
||||
|
||||
if type(TACANChannel) ~= "number" then
|
||||
if type( TACANChannel ) ~= "number" then
|
||||
if TACANMode ~= "X" and TACANMode ~= "Y" then
|
||||
return nil -- error in arguments
|
||||
end
|
||||
end
|
||||
|
||||
-- This code is largely based on ED's code, in DCS World\Scripts\World\Radio\BeaconTypes.lua, line 137.
|
||||
-- I have no idea what it does but it seems to work
|
||||
|
||||
-- This code is largely based on ED's code, in DCS World\Scripts\World\Radio\BeaconTypes.lua, line 137.
|
||||
-- I have no idea what it does but it seems to work
|
||||
local A = 1151 -- 'X', channel >= 64
|
||||
local B = 64 -- channel >= 64
|
||||
|
||||
local B = 64 -- channel >= 64
|
||||
|
||||
if TACANChannel < 64 then
|
||||
B = 1
|
||||
end
|
||||
|
||||
|
||||
if TACANMode == 'Y' then
|
||||
A = 1025
|
||||
if TACANChannel < 64 then
|
||||
@@ -437,6 +436,6 @@ function BEACON:_TACANToFrequency(TACANChannel, TACANMode)
|
||||
A = 962
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return (A + TACANChannel - B) * 1000000
|
||||
end
|
||||
end
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -14,7 +14,7 @@
|
||||
-- 
|
||||
--
|
||||
-- Within a running mission, various DCS events occur. Units are dynamically created, crash, die, shoot stuff, get hit etc.
|
||||
-- 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.
|
||||
-- * SET_ derived classes: 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.
|
||||
-- * 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 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.
|
||||
-- * Any other object: Various other objects can subscribe to DCS events. Each DCS event triggered will be published to each subscribed object.
|
||||
--
|
||||
@@ -131,6 +131,8 @@
|
||||
-- * Weapon data: Certain events populate weapon information.
|
||||
-- * Place data: Certain events populate place information.
|
||||
--
|
||||
-- Example code snippet:
|
||||
--
|
||||
-- --- This function is an Event Handling function that will be called when Tank1 is Dead.
|
||||
-- -- EventData is an EVENTDATA structure.
|
||||
-- -- We use the EventData.IniUnit to smoke the tank Green.
|
||||
@@ -141,7 +143,6 @@
|
||||
-- EventData.IniUnit:SmokeGreen()
|
||||
-- end
|
||||
--
|
||||
--
|
||||
-- Find below an overview which events populate which information categories:
|
||||
--
|
||||
-- 
|
||||
@@ -151,6 +152,7 @@
|
||||
-- In case a STATIC object is involved, the documentation indicates which fields will and won't not be populated.
|
||||
-- The fields **IniObjectCategory** and **TgtObjectCategory** contain the indicator which **kind of object is involved** in the event.
|
||||
-- You can use the enumerator **Object.Category.UNIT** and **Object.Category.STATIC** to check on IniObjectCategory and TgtObjectCategory.
|
||||
--
|
||||
-- Example code snippet:
|
||||
--
|
||||
-- if Event.IniObjectCategory == Object.Category.UNIT then
|
||||
@@ -172,7 +174,6 @@
|
||||
-- @module Core.Event
|
||||
-- @image Core_Event.JPG
|
||||
|
||||
|
||||
--- @type EVENT
|
||||
-- @field #EVENT.Events Events
|
||||
-- @extends Core.Base#BASE
|
||||
@@ -194,7 +195,6 @@ world.event.S_EVENT_DELETE_ZONE_GOAL = world.event.S_EVENT_MAX + 1005
|
||||
world.event.S_EVENT_REMOVE_UNIT = world.event.S_EVENT_MAX + 1006
|
||||
world.event.S_EVENT_PLAYER_ENTER_AIRCRAFT = world.event.S_EVENT_MAX + 1007
|
||||
|
||||
|
||||
--- The different types of events supported by MOOSE.
|
||||
-- Use this structure to subscribe to events using the @{Core.Base#BASE.HandleEvent}() method.
|
||||
-- @type EVENTS
|
||||
@@ -227,13 +227,13 @@ EVENTS = {
|
||||
MarkChange = world.event.S_EVENT_MARK_CHANGE,
|
||||
MarkRemoved = world.event.S_EVENT_MARK_REMOVED,
|
||||
-- Moose Events
|
||||
NewCargo = world.event.S_EVENT_NEW_CARGO,
|
||||
DeleteCargo = world.event.S_EVENT_DELETE_CARGO,
|
||||
NewZone = world.event.S_EVENT_NEW_ZONE,
|
||||
DeleteZone = world.event.S_EVENT_DELETE_ZONE,
|
||||
NewZoneGoal = world.event.S_EVENT_NEW_ZONE_GOAL,
|
||||
DeleteZoneGoal = world.event.S_EVENT_DELETE_ZONE_GOAL,
|
||||
RemoveUnit = world.event.S_EVENT_REMOVE_UNIT,
|
||||
NewCargo = world.event.S_EVENT_NEW_CARGO,
|
||||
DeleteCargo = world.event.S_EVENT_DELETE_CARGO,
|
||||
NewZone = world.event.S_EVENT_NEW_ZONE,
|
||||
DeleteZone = world.event.S_EVENT_DELETE_ZONE,
|
||||
NewZoneGoal = world.event.S_EVENT_NEW_ZONE_GOAL,
|
||||
DeleteZoneGoal = world.event.S_EVENT_DELETE_ZONE_GOAL,
|
||||
RemoveUnit = world.event.S_EVENT_REMOVE_UNIT,
|
||||
PlayerEnterAircraft = world.event.S_EVENT_PLAYER_ENTER_AIRCRAFT,
|
||||
-- Added with DCS 2.5.6
|
||||
DetailedFailure = world.event.S_EVENT_DETAILED_FAILURE or -1, --We set this to -1 for backward compatibility to DCS 2.5.5 and earlier
|
||||
@@ -304,8 +304,6 @@ EVENTS = {
|
||||
-- @field Core.ZONE#ZONE Zone The zone object.
|
||||
-- @field #string ZoneName The name of the zone.
|
||||
|
||||
|
||||
|
||||
local _EVENTMETA = {
|
||||
[world.event.S_EVENT_SHOT] = {
|
||||
Order = 1,
|
||||
@@ -562,7 +560,6 @@ local _EVENTMETA = {
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
--- The Events structure
|
||||
-- @type EVENT.Events
|
||||
-- @field #number IniUnit
|
||||
@@ -576,12 +573,11 @@ function EVENT:New()
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
|
||||
-- Add world event handler.
|
||||
self.EventHandler = world.addEventHandler(self)
|
||||
self.EventHandler = world.addEventHandler( self )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Initializes the Events structure for the event.
|
||||
-- @param #EVENT self
|
||||
-- @param DCS#world.event EventID Event ID.
|
||||
@@ -595,7 +591,7 @@ function EVENT:Init( EventID, EventClass )
|
||||
self.Events[EventID] = {}
|
||||
end
|
||||
|
||||
-- Each event has a subtable of EventClasses, ordered by EventPriority.
|
||||
-- Each event has a sub-table of EventClasses, ordered by EventPriority.
|
||||
local EventPriority = EventClass:GetEventPriority()
|
||||
|
||||
if not self.Events[EventID][EventPriority] then
|
||||
@@ -603,7 +599,7 @@ function EVENT:Init( EventID, EventClass )
|
||||
end
|
||||
|
||||
if not self.Events[EventID][EventPriority][EventClass] then
|
||||
self.Events[EventID][EventPriority][EventClass] = {}
|
||||
self.Events[EventID][EventPriority][EventClass] = {}
|
||||
end
|
||||
|
||||
return self.Events[EventID][EventPriority][EventClass]
|
||||
@@ -614,7 +610,7 @@ end
|
||||
-- @param Core.Base#BASE EventClass The self instance of the class for which the event is.
|
||||
-- @param DCS#world.event EventID Event ID.
|
||||
-- @return #EVENT self
|
||||
function EVENT:RemoveEvent( EventClass, EventID )
|
||||
function EVENT:RemoveEvent( EventClass, EventID )
|
||||
|
||||
-- Debug info.
|
||||
self:F2( { "Removing subscription for class: ", EventClass:GetClassNameAndID() } )
|
||||
@@ -638,7 +634,7 @@ end
|
||||
-- @param Core.Base#BASE EventClass The self instance of the class for which the event is.
|
||||
-- @param DCS#world.event EventID Event ID.
|
||||
-- @return #EVENT.Events
|
||||
function EVENT:Reset( EventObject ) --R2.1
|
||||
function EVENT:Reset( EventObject ) -- R2.1
|
||||
|
||||
self:F( { "Resetting subscriptions for class: ", EventObject:GetClassNameAndID() } )
|
||||
|
||||
@@ -657,12 +653,11 @@ function EVENT:Reset( EventObject ) --R2.1
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Clears all event subscriptions for a @{Core.Base#BASE} derived object.
|
||||
-- @param #EVENT self
|
||||
-- @param Core.Base#BASE EventClass The self class object for which the events are removed.
|
||||
-- @return #EVENT self
|
||||
function EVENT:RemoveAll(EventClass)
|
||||
function EVENT:RemoveAll( EventClass )
|
||||
|
||||
local EventClassName = EventClass:GetClassNameAndID()
|
||||
|
||||
@@ -676,8 +671,6 @@ function EVENT:RemoveAll(EventClass)
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Create an OnDead event handler for a group
|
||||
-- @param #EVENT self
|
||||
-- @param #table EventTemplate
|
||||
@@ -709,7 +702,6 @@ function EVENT:OnEventGeneric( EventFunction, EventClass, EventID )
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set a new listener for an `S_EVENT_X` event for a UNIT.
|
||||
-- @param #EVENT self
|
||||
-- @param #string UnitName The name of the UNIT.
|
||||
@@ -797,7 +789,6 @@ do -- OnDead
|
||||
|
||||
end
|
||||
|
||||
|
||||
do -- OnLand
|
||||
|
||||
--- Create an OnLand event handler for a group
|
||||
@@ -864,7 +855,7 @@ do -- Event Creation
|
||||
id = EVENTS.NewCargo,
|
||||
time = timer.getTime(),
|
||||
cargo = Cargo,
|
||||
}
|
||||
}
|
||||
|
||||
world.onEvent( Event )
|
||||
end
|
||||
@@ -879,7 +870,7 @@ do -- Event Creation
|
||||
id = EVENTS.DeleteCargo,
|
||||
time = timer.getTime(),
|
||||
cargo = Cargo,
|
||||
}
|
||||
}
|
||||
|
||||
world.onEvent( Event )
|
||||
end
|
||||
@@ -894,7 +885,7 @@ do -- Event Creation
|
||||
id = EVENTS.NewZone,
|
||||
time = timer.getTime(),
|
||||
zone = Zone,
|
||||
}
|
||||
}
|
||||
|
||||
world.onEvent( Event )
|
||||
end
|
||||
@@ -909,7 +900,7 @@ do -- Event Creation
|
||||
id = EVENTS.DeleteZone,
|
||||
time = timer.getTime(),
|
||||
zone = Zone,
|
||||
}
|
||||
}
|
||||
|
||||
world.onEvent( Event )
|
||||
end
|
||||
@@ -924,12 +915,11 @@ do -- Event Creation
|
||||
id = EVENTS.NewZoneGoal,
|
||||
time = timer.getTime(),
|
||||
ZoneGoal = ZoneGoal,
|
||||
}
|
||||
}
|
||||
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
|
||||
--- Creation of a ZoneGoal Deletion Event.
|
||||
-- @param #EVENT self
|
||||
-- @param Core.ZoneGoal#ZONE_GOAL ZoneGoal The ZoneGoal created.
|
||||
@@ -940,12 +930,11 @@ do -- Event Creation
|
||||
id = EVENTS.DeleteZoneGoal,
|
||||
time = timer.getTime(),
|
||||
ZoneGoal = ZoneGoal,
|
||||
}
|
||||
}
|
||||
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
|
||||
--- Creation of a S_EVENT_PLAYER_ENTER_UNIT Event.
|
||||
-- @param #EVENT self
|
||||
-- @param Wrapper.Unit#UNIT PlayerUnit.
|
||||
@@ -955,8 +944,8 @@ do -- Event Creation
|
||||
local Event = {
|
||||
id = EVENTS.PlayerEnterUnit,
|
||||
time = timer.getTime(),
|
||||
initiator = PlayerUnit:GetDCSObject()
|
||||
}
|
||||
initiator = PlayerUnit:GetDCSObject(),
|
||||
}
|
||||
|
||||
world.onEvent( Event )
|
||||
end
|
||||
@@ -970,8 +959,8 @@ do -- Event Creation
|
||||
local Event = {
|
||||
id = EVENTS.PlayerEnterAircraft,
|
||||
time = timer.getTime(),
|
||||
initiator = PlayerUnit:GetDCSObject()
|
||||
}
|
||||
initiator = PlayerUnit:GetDCSObject(),
|
||||
}
|
||||
|
||||
world.onEvent( Event )
|
||||
end
|
||||
@@ -993,14 +982,13 @@ function EVENT:onEvent( Event )
|
||||
return errmsg
|
||||
end
|
||||
|
||||
|
||||
-- Get event meta data.
|
||||
local EventMeta = _EVENTMETA[Event.id]
|
||||
|
||||
-- Check if this is a known event?
|
||||
if EventMeta then
|
||||
|
||||
if self and self.Events and self.Events[Event.id] and self.MissionEnd==false and (Event.initiator~=nil or (Event.initiator==nil and Event.id~=EVENTS.PlayerLeaveUnit)) then
|
||||
if self and self.Events and self.Events[Event.id] and self.MissionEnd == false and (Event.initiator ~= nil or (Event.initiator == nil and Event.id ~= EVENTS.PlayerLeaveUnit)) then
|
||||
|
||||
if Event.id and Event.id == EVENTS.MissionEnd then
|
||||
self.MissionEnd = true
|
||||
@@ -1024,9 +1012,9 @@ function EVENT:onEvent( Event )
|
||||
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
|
||||
-- if Event.IniGroup then
|
||||
Event.IniGroupName = Event.IniDCSGroupName
|
||||
-- end
|
||||
end
|
||||
Event.IniPlayerName = Event.IniDCSUnit:getPlayerName()
|
||||
Event.IniCoalition = Event.IniDCSUnit:getCoalition()
|
||||
@@ -1036,23 +1024,23 @@ function EVENT:onEvent( Event )
|
||||
|
||||
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.IniDCSUnit = Event.initiator
|
||||
local ID=Event.initiator.id_
|
||||
Event.IniDCSUnitName = string.format("Ejected Pilot ID %s", tostring(ID))
|
||||
local ID = Event.initiator.id_
|
||||
Event.IniDCSUnitName = string.format( "Ejected Pilot ID %s", tostring( ID ) )
|
||||
Event.IniUnitName = Event.IniDCSUnitName
|
||||
Event.IniCoalition = 0
|
||||
Event.IniCategory = 0
|
||||
Event.IniCategory = 0
|
||||
Event.IniTypeName = "Ejected Pilot"
|
||||
elseif Event.id == 33 then -- ejection seat discarded
|
||||
elseif Event.id == 33 then -- ejection seat discarded
|
||||
Event.IniDCSUnit = Event.initiator
|
||||
local ID=Event.initiator.id_
|
||||
Event.IniDCSUnitName = string.format("Ejection Seat ID %s", tostring(ID))
|
||||
local ID = Event.initiator.id_
|
||||
Event.IniDCSUnitName = string.format( "Ejection Seat ID %s", tostring( ID ) )
|
||||
Event.IniUnitName = Event.IniDCSUnitName
|
||||
Event.IniCoalition = 0
|
||||
Event.IniCategory = 0
|
||||
Event.IniCategory = 0
|
||||
Event.IniTypeName = "Ejection Seat"
|
||||
else
|
||||
Event.IniDCSUnit = Event.initiator
|
||||
@@ -1088,7 +1076,7 @@ function EVENT:onEvent( Event )
|
||||
Event.IniDCSUnit = Event.initiator
|
||||
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
|
||||
Event.IniUnitName = Event.IniDCSUnitName
|
||||
Event.IniUnit = AIRBASE:FindByName(Event.IniDCSUnitName)
|
||||
Event.IniUnit = AIRBASE:FindByName( Event.IniDCSUnitName )
|
||||
Event.IniCoalition = Event.IniDCSUnit:getCoalition()
|
||||
Event.IniCategory = Event.IniDCSUnit:getDesc().category
|
||||
Event.IniTypeName = Event.IniDCSUnit:getTypeName()
|
||||
@@ -1109,9 +1097,9 @@ function EVENT:onEvent( Event )
|
||||
if Event.TgtDCSGroup and Event.TgtDCSGroup:isExist() then
|
||||
Event.TgtDCSGroupName = Event.TgtDCSGroup:getName()
|
||||
Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName )
|
||||
--if Event.TgtGroup then
|
||||
Event.TgtGroupName = Event.TgtDCSGroupName
|
||||
--end
|
||||
-- if Event.TgtGroup then
|
||||
Event.TgtGroupName = Event.TgtDCSGroupName
|
||||
-- end
|
||||
end
|
||||
Event.TgtPlayerName = Event.TgtDCSUnit:getPlayerName()
|
||||
Event.TgtCoalition = Event.TgtDCSUnit:getCoalition()
|
||||
@@ -1130,18 +1118,18 @@ function EVENT:onEvent( Event )
|
||||
Event.TgtCategory = Event.TgtDCSUnit:getDesc().category
|
||||
Event.TgtTypeName = Event.TgtDCSUnit:getTypeName()
|
||||
else
|
||||
Event.TgtDCSUnitName = string.format("No target object for Event ID %s", tostring(Event.id))
|
||||
Event.TgtDCSUnitName = string.format( "No target object for Event ID %s", tostring( Event.id ) )
|
||||
Event.TgtUnitName = Event.TgtDCSUnitName
|
||||
Event.TgtUnit = nil
|
||||
Event.TgtCoalition = 0
|
||||
Event.TgtCategory = 0
|
||||
if Event.id == 6 then
|
||||
Event.TgtTypeName = "Ejected Pilot"
|
||||
Event.TgtDCSUnitName = string.format("Ejected Pilot ID %s", tostring(Event.IniDCSUnitName))
|
||||
Event.TgtDCSUnitName = string.format( "Ejected Pilot ID %s", tostring( Event.IniDCSUnitName ) )
|
||||
Event.TgtUnitName = Event.TgtDCSUnitName
|
||||
elseif Event.id == 33 then
|
||||
Event.TgtTypeName = "Ejection Seat"
|
||||
Event.TgtDCSUnitName = string.format("Ejection Seat ID %s", tostring(Event.IniDCSUnitName))
|
||||
Event.TgtDCSUnitName = string.format( "Ejection Seat ID %s", tostring( Event.IniDCSUnitName ) )
|
||||
Event.TgtUnitName = Event.TgtDCSUnitName
|
||||
else
|
||||
Event.TgtTypeName = "Static"
|
||||
@@ -1167,29 +1155,29 @@ function EVENT:onEvent( Event )
|
||||
Event.WeaponCoalition = Event.WeaponUNIT and Event.Weapon:getCoalition()
|
||||
Event.WeaponCategory = Event.WeaponUNIT and Event.Weapon:getDesc().category
|
||||
Event.WeaponTypeName = Event.WeaponUNIT and Event.Weapon:getTypeName()
|
||||
--Event.WeaponTgtDCSUnit = Event.Weapon:getTarget()
|
||||
-- Event.WeaponTgtDCSUnit = Event.Weapon:getTarget()
|
||||
end
|
||||
|
||||
-- Place should be given for takeoff and landing events as well as base captured. It should be a DCS airbase.
|
||||
if Event.place then
|
||||
if Event.id==EVENTS.LandingAfterEjection then
|
||||
if Event.id == EVENTS.LandingAfterEjection then
|
||||
-- Place is here the UNIT of which the pilot ejected.
|
||||
--local name=Event.place:getName() -- This returns a DCS error "Airbase doesn't exit" :(
|
||||
-- local name=Event.place:getName() -- This returns a DCS error "Airbase doesn't exit" :(
|
||||
-- However, this is not a big thing, as the aircraft the pilot ejected from is usually long crashed before the ejected pilot touches the ground.
|
||||
--Event.Place=UNIT:Find(Event.place)
|
||||
-- Event.Place=UNIT:Find(Event.place)
|
||||
else
|
||||
Event.Place=AIRBASE:Find(Event.place)
|
||||
Event.PlaceName=Event.Place:GetName()
|
||||
Event.Place = AIRBASE:Find( Event.place )
|
||||
Event.PlaceName = Event.Place:GetName()
|
||||
end
|
||||
end
|
||||
|
||||
-- Mark points.
|
||||
if Event.idx then
|
||||
Event.MarkID=Event.idx
|
||||
Event.MarkVec3=Event.pos
|
||||
Event.MarkCoordinate=COORDINATE:NewFromVec3(Event.pos)
|
||||
Event.MarkText=Event.text
|
||||
Event.MarkCoalition=Event.coalition
|
||||
Event.MarkID = Event.idx
|
||||
Event.MarkVec3 = Event.pos
|
||||
Event.MarkCoordinate = COORDINATE:NewFromVec3( Event.pos )
|
||||
Event.MarkText = Event.text
|
||||
Event.MarkCoalition = Event.coalition
|
||||
Event.MarkGroupID = Event.groupID
|
||||
end
|
||||
|
||||
@@ -1218,9 +1206,9 @@ function EVENT:onEvent( Event )
|
||||
-- Okay, we got the event from DCS. Now loop the SORTED self.EventSorted[] table for the received Event.id, and for each EventData registered, check if a function needs to be called.
|
||||
for EventClass, EventData in pairs( self.Events[Event.id][EventPriority] ) do
|
||||
|
||||
--if Event.IniObjectCategory ~= Object.Category.STATIC then
|
||||
-- if Event.IniObjectCategory ~= Object.Category.STATIC then
|
||||
-- self:E( { "Evaluating: ", EventClass:GetClassNameAndID() } )
|
||||
--end
|
||||
-- end
|
||||
|
||||
Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName )
|
||||
Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName )
|
||||
@@ -1237,8 +1225,8 @@ function EVENT:onEvent( Event )
|
||||
|
||||
local UnitName = EventClass:GetName()
|
||||
|
||||
if ( EventMeta.Side == "I" and UnitName == Event.IniDCSUnitName ) or
|
||||
( EventMeta.Side == "T" and UnitName == Event.TgtDCSUnitName ) then
|
||||
if (EventMeta.Side == "I" and UnitName == Event.IniDCSUnitName) or
|
||||
(EventMeta.Side == "T" and UnitName == Event.TgtDCSUnitName) then
|
||||
|
||||
-- First test if a EventFunction is Set, otherwise search for the default function
|
||||
if EventData.EventFunction then
|
||||
@@ -1247,15 +1235,14 @@ function EVENT:onEvent( Event )
|
||||
self:F( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } )
|
||||
end
|
||||
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
return EventData.EventFunction( EventClass, Event )
|
||||
end, ErrorHandler )
|
||||
local Result, Value = xpcall( function()
|
||||
return EventData.EventFunction( EventClass, Event )
|
||||
end, ErrorHandler )
|
||||
|
||||
else
|
||||
|
||||
-- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object.
|
||||
local EventFunction = EventClass[ EventMeta.Event ]
|
||||
local EventFunction = EventClass[EventMeta.Event]
|
||||
if EventFunction and type( EventFunction ) == "function" then
|
||||
|
||||
-- Now call the default event function.
|
||||
@@ -1263,10 +1250,9 @@ function EVENT:onEvent( Event )
|
||||
self:F( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
end
|
||||
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
return EventFunction( EventClass, Event )
|
||||
end, ErrorHandler )
|
||||
local Result, Value = xpcall( function()
|
||||
return EventFunction( EventClass, Event )
|
||||
end, ErrorHandler )
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1290,8 +1276,8 @@ function EVENT:onEvent( Event )
|
||||
-- We can get the name of the EventClass, which is now always a GROUP object.
|
||||
local GroupName = EventClass:GetName()
|
||||
|
||||
if ( EventMeta.Side == "I" and GroupName == Event.IniDCSGroupName ) or
|
||||
( EventMeta.Side == "T" and GroupName == Event.TgtDCSGroupName ) then
|
||||
if (EventMeta.Side == "I" and GroupName == Event.IniDCSGroupName) or
|
||||
(EventMeta.Side == "T" and GroupName == Event.TgtDCSGroupName) then
|
||||
|
||||
-- First test if a EventFunction is Set, otherwise search for the default function
|
||||
if EventData.EventFunction then
|
||||
@@ -1300,15 +1286,14 @@ function EVENT:onEvent( Event )
|
||||
self:F( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } )
|
||||
end
|
||||
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
return EventData.EventFunction( EventClass, Event, unpack( EventData.Params ) )
|
||||
end, ErrorHandler )
|
||||
local Result, Value = xpcall( function()
|
||||
return EventData.EventFunction( EventClass, Event, unpack( EventData.Params ) )
|
||||
end, ErrorHandler )
|
||||
|
||||
else
|
||||
|
||||
-- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object.
|
||||
local EventFunction = EventClass[ EventMeta.Event ]
|
||||
local EventFunction = EventClass[EventMeta.Event]
|
||||
if EventFunction and type( EventFunction ) == "function" then
|
||||
|
||||
-- Now call the default event function.
|
||||
@@ -1316,16 +1301,15 @@ function EVENT:onEvent( Event )
|
||||
self:F( { "Calling " .. EventMeta.Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
end
|
||||
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
return EventFunction( EventClass, Event, unpack( EventData.Params ) )
|
||||
end, ErrorHandler )
|
||||
local Result, Value = xpcall( function()
|
||||
return EventFunction( EventClass, Event, unpack( EventData.Params ) )
|
||||
end, ErrorHandler )
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
-- The EventClass is not alive anymore, we remove it from the EventHandlers...
|
||||
--self:RemoveEvent( EventClass, Event.id )
|
||||
-- self:RemoveEvent( EventClass, Event.id )
|
||||
end
|
||||
else
|
||||
|
||||
@@ -1340,14 +1324,13 @@ function EVENT:onEvent( Event )
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:F2( { "Calling EventFunction for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
end
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
return EventData.EventFunction( EventClass, Event )
|
||||
end, ErrorHandler )
|
||||
local Result, Value = xpcall( function()
|
||||
return EventData.EventFunction( EventClass, Event )
|
||||
end, ErrorHandler )
|
||||
else
|
||||
|
||||
-- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object.
|
||||
local EventFunction = EventClass[ EventMeta.Event ]
|
||||
local EventFunction = EventClass[EventMeta.Event]
|
||||
if EventFunction and type( EventFunction ) == "function" then
|
||||
|
||||
-- Now call the default event function.
|
||||
@@ -1355,11 +1338,10 @@ function EVENT:onEvent( Event )
|
||||
self:F2( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
end
|
||||
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
local Result, Value = EventFunction( EventClass, Event )
|
||||
return Result, Value
|
||||
end, ErrorHandler )
|
||||
local Result, Value = xpcall( function()
|
||||
local Result, Value = EventFunction( EventClass, Event )
|
||||
return Result, Value
|
||||
end, ErrorHandler )
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1383,7 +1365,7 @@ function EVENT:onEvent( Event )
|
||||
self:T( { EventMeta.Text, Event } )
|
||||
end
|
||||
else
|
||||
self:E(string.format("WARNING: Could not get EVENTMETA data for event ID=%d! Is this an unknown/new DCS event?", tostring(Event.id)))
|
||||
self:E( string.format( "WARNING: Could not get EVENTMETA data for event ID=%d! Is this an unknown/new DCS event?", tostring( Event.id ) ) )
|
||||
end
|
||||
|
||||
Event = nil
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,89 +1,87 @@
|
||||
--- **Core** - Models the process to achieve goal(s).
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
--
|
||||
-- * Define the goal.
|
||||
-- * Monitor the goal achievement.
|
||||
-- * Manage goal contribution by players.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- Classes that implement a goal achievement, will derive from GOAL to implement the ways how the achievements can be realized.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions: **funkyfranky**
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- @module Core.Goal
|
||||
-- @image Core_Goal.JPG
|
||||
|
||||
|
||||
do -- Goal
|
||||
|
||||
--- @type GOAL
|
||||
-- @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.
|
||||
--
|
||||
--
|
||||
-- # 1. GOAL constructor
|
||||
--
|
||||
--
|
||||
-- * @{#GOAL.New}(): Creates a new GOAL object.
|
||||
--
|
||||
--
|
||||
-- # 2. GOAL is a finite state machine (FSM).
|
||||
--
|
||||
--
|
||||
-- ## 2.1. GOAL States
|
||||
--
|
||||
--
|
||||
-- * **Pending**: The goal object is in progress.
|
||||
-- * **Achieved**: The goal objective is Achieved.
|
||||
--
|
||||
--
|
||||
-- ## 2.2. GOAL Events
|
||||
--
|
||||
--
|
||||
-- * **Achieved**: Set the goal objective to Achieved.
|
||||
--
|
||||
--
|
||||
-- # 3. Player contributions.
|
||||
--
|
||||
--
|
||||
-- Goals are most of the time achieved by players. These player achievements can be registered as part of the goal achievement.
|
||||
-- Use @{#GOAL.AddPlayerContribution}() to add a player contribution to the goal.
|
||||
-- The player contributions are based on a points system, an internal counter per player.
|
||||
-- So once the goal has been achieved, the player contributions can be queried using @{#GOAL.GetPlayerContributions}(),
|
||||
-- So once the goal has been achieved, the player contributions can be queried using @{#GOAL.GetPlayerContributions}(),
|
||||
-- that retrieves all contributions done by the players. For one player, the contribution can be queried using @{#GOAL.GetPlayerContribution}().
|
||||
-- The total amount of player contributions can be queried using @{#GOAL.GetTotalContributions}().
|
||||
--
|
||||
--
|
||||
-- # 4. Goal achievement.
|
||||
--
|
||||
--
|
||||
-- Once the goal is achieved, the mission designer will need to trigger the goal achievement using the **Achieved** event.
|
||||
-- The underlying 2 examples will achieve the goals for the `Goal` object:
|
||||
--
|
||||
--
|
||||
-- Goal:Achieved() -- Achieve the goal immediately.
|
||||
-- Goal:__Achieved( 30 ) -- Achieve the goal within 30 seconds.
|
||||
--
|
||||
--
|
||||
-- # 5. Check goal achievement.
|
||||
--
|
||||
--
|
||||
-- The method @{#GOAL.IsAchieved}() will return true if the goal is achieved (the trigger **Achieved** was executed).
|
||||
-- You can use this method to check asynchronously if a goal has been achieved, for example using a scheduler.
|
||||
--
|
||||
--
|
||||
-- @field #GOAL
|
||||
GOAL = {
|
||||
ClassName = "GOAL",
|
||||
}
|
||||
|
||||
|
||||
--- @field #table GOAL.Players
|
||||
GOAL.Players = {}
|
||||
|
||||
--- @field #number GOAL.TotalContributions
|
||||
GOAL.TotalContributions = 0
|
||||
|
||||
|
||||
--- GOAL Constructor.
|
||||
-- @param #GOAL self
|
||||
-- @return #GOAL
|
||||
function GOAL:New()
|
||||
|
||||
|
||||
local self = BASE:Inherit( self, FSM:New() ) -- #GOAL
|
||||
self:F( {} )
|
||||
|
||||
@@ -104,11 +102,10 @@ do -- Goal
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
|
||||
|
||||
self:SetStartState( "Pending" )
|
||||
self:AddTransition( "*", "Achieved", "Achieved" )
|
||||
|
||||
self:AddTransition( "*", "Achieved", "Achieved" )
|
||||
|
||||
--- Achieved Handler OnBefore for GOAL
|
||||
-- @function [parent=#GOAL] OnBeforeAchieved
|
||||
-- @param #GOAL self
|
||||
@@ -116,47 +113,44 @@ do -- Goal
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
|
||||
--- Achieved Handler OnAfter for GOAL
|
||||
-- @function [parent=#GOAL] OnAfterAchieved
|
||||
-- @param #GOAL self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
|
||||
--- Achieved Trigger for GOAL
|
||||
-- @function [parent=#GOAL] Achieved
|
||||
-- @param #GOAL self
|
||||
|
||||
|
||||
--- Achieved Asynchronous Trigger for GOAL
|
||||
-- @function [parent=#GOAL] __Achieved
|
||||
-- @param #GOAL self
|
||||
-- @param #number Delay
|
||||
|
||||
|
||||
self:SetEventPriority( 5 )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Add a new contribution by a player.
|
||||
-- @param #GOAL self
|
||||
-- @param #string PlayerName The name of the player.
|
||||
function GOAL:AddPlayerContribution( PlayerName )
|
||||
self:F({PlayerName})
|
||||
self:F( { PlayerName } )
|
||||
self.Players[PlayerName] = self.Players[PlayerName] or 0
|
||||
self.Players[PlayerName] = self.Players[PlayerName] + 1
|
||||
self.TotalContributions = self.TotalContributions + 1
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- @param #GOAL self
|
||||
-- @param #number Player contribution.
|
||||
function GOAL:GetPlayerContribution( PlayerName )
|
||||
return self.Players[PlayerName] or 0
|
||||
return self.Players[PlayerName] or 0
|
||||
end
|
||||
|
||||
|
||||
--- Get the players who contributed to achieve the goal.
|
||||
-- The result is a list of players, sorted by the name of the players.
|
||||
-- @param #GOAL self
|
||||
@@ -165,7 +159,6 @@ do -- Goal
|
||||
return self.Players or {}
|
||||
end
|
||||
|
||||
|
||||
--- Gets the total contributions that happened to achieve the goal.
|
||||
-- The result is a number.
|
||||
-- @param #GOAL self
|
||||
@@ -173,9 +166,7 @@ do -- Goal
|
||||
function GOAL:GetTotalContributions()
|
||||
return self.TotalContributions or 0
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
--- Validates if the goal is achieved.
|
||||
-- @param #GOAL self
|
||||
-- @return #boolean true if the goal is achieved.
|
||||
@@ -183,4 +174,4 @@ do -- Goal
|
||||
return self:Is( "Achieved" )
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,18 +1,18 @@
|
||||
--- **Core** - Informs the players using messages during a simulation.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
--
|
||||
-- * A more advanced messaging system using the DCS message system.
|
||||
-- * Time messages.
|
||||
-- * Send messages based on a message type, which has a pre-defined duration that can be tweaked in SETTINGS.
|
||||
-- * Send message to all players.
|
||||
-- * Send messages to a coalition.
|
||||
-- * Send messages to a specific group.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- @module Core.Message
|
||||
-- @image Core_Message.JPG
|
||||
|
||||
@@ -23,14 +23,14 @@
|
||||
--- 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 can contain a category which is indicating the category of the message.
|
||||
--
|
||||
--
|
||||
-- ## MESSAGE construction
|
||||
--
|
||||
--
|
||||
-- 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.
|
||||
--
|
||||
--
|
||||
-- ## Send messages to an audience
|
||||
--
|
||||
--
|
||||
-- Messages are sent:
|
||||
--
|
||||
-- * To a @{Client} using @{#MESSAGE.ToClient}().
|
||||
@@ -39,26 +39,26 @@
|
||||
-- * To the red coalition using @{#MESSAGE.ToRed}().
|
||||
-- * To the blue coalition using @{#MESSAGE.ToBlue}().
|
||||
-- * To all Players using @{#MESSAGE.ToAll}().
|
||||
--
|
||||
--
|
||||
-- ## Send conditionally to an audience
|
||||
--
|
||||
--
|
||||
-- Messages can be sent conditionally to an audience (when a condition is true):
|
||||
--
|
||||
--
|
||||
-- * To all players using @{#MESSAGE.ToAllIf}().
|
||||
-- * To a coalition using @{#MESSAGE.ToCoalitionIf}().
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- @field #MESSAGE
|
||||
MESSAGE = {
|
||||
ClassName = "MESSAGE",
|
||||
MessageCategory = 0,
|
||||
MessageID = 0,
|
||||
ClassName = "MESSAGE",
|
||||
MessageCategory = 0,
|
||||
MessageID = 0,
|
||||
}
|
||||
|
||||
--- Message Types
|
||||
@@ -68,10 +68,9 @@ MESSAGE.Type = {
|
||||
Information = "Information",
|
||||
Briefing = "Briefing 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.
|
||||
-- @param self
|
||||
-- @param #string MessageText is the text of the Message.
|
||||
@@ -80,52 +79,52 @@ MESSAGE.Type = {
|
||||
-- @param #boolean ClearScreen (optional) Clear all previous messages if true.
|
||||
-- @return #MESSAGE
|
||||
-- @usage
|
||||
-- -- Create a series of new Messages.
|
||||
-- -- MessageAll is meant to be sent to all players, for 25 seconds, and is classified as "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" )
|
||||
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", 25, "Penalty" )
|
||||
-- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", 25, "Score" )
|
||||
-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", 25, "Score")
|
||||
--
|
||||
-- -- Create a series of new Messages.
|
||||
-- -- MessageAll is meant to be sent to all players, for 25 seconds, and is classified as "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" )
|
||||
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", 25, "Penalty" )
|
||||
-- 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 )
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
self:F( { MessageText, MessageDuration, MessageCategory } )
|
||||
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
self:F( { MessageText, MessageDuration, MessageCategory } )
|
||||
|
||||
self.MessageType = nil
|
||||
|
||||
|
||||
-- When no MessageCategory is given, we don't show it as a title...
|
||||
if MessageCategory and MessageCategory ~= "" then
|
||||
if MessageCategory:sub(-1) ~= "\n" then
|
||||
if MessageCategory and MessageCategory ~= "" then
|
||||
if MessageCategory:sub( -1 ) ~= "\n" then
|
||||
self.MessageCategory = MessageCategory .. ": "
|
||||
else
|
||||
self.MessageCategory = MessageCategory:sub( 1, -2 ) .. ":\n"
|
||||
self.MessageCategory = MessageCategory:sub( 1, -2 ) .. ":\n"
|
||||
end
|
||||
else
|
||||
self.MessageCategory = ""
|
||||
end
|
||||
|
||||
self.ClearScreen=false
|
||||
if ClearScreen~=nil then
|
||||
self.ClearScreen=ClearScreen
|
||||
|
||||
self.ClearScreen = false
|
||||
if ClearScreen ~= nil then
|
||||
self.ClearScreen = ClearScreen
|
||||
end
|
||||
|
||||
self.MessageDuration = MessageDuration or 5
|
||||
self.MessageTime = timer.getTime()
|
||||
self.MessageText = MessageText:gsub("^\n","",1):gsub("\n$","",1)
|
||||
|
||||
self.MessageSent = false
|
||||
self.MessageGroup = false
|
||||
self.MessageCoalition = false
|
||||
self.MessageDuration = MessageDuration or 5
|
||||
self.MessageTime = timer.getTime()
|
||||
self.MessageText = MessageText:gsub( "^\n", "", 1 ):gsub( "\n$", "", 1 )
|
||||
|
||||
return self
|
||||
self.MessageSent = false
|
||||
self.MessageGroup = false
|
||||
self.MessageCoalition = false
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Creates a new MESSAGE object of a certain type.
|
||||
-- Note that these MESSAGE objects are not yet displayed on the display panel.
|
||||
--- Creates a new MESSAGE object of a certain type.
|
||||
-- Note that these MESSAGE objects are not yet displayed on the display panel.
|
||||
-- You must use the functions @{ToClient} or @{ToCoalition} or @{ToAll} to send these Messages to the respective recipients.
|
||||
-- The message display times are automatically defined based on the timing settings in the @{Settings} menu.
|
||||
-- @param self
|
||||
@@ -134,83 +133,83 @@ end
|
||||
-- @param #boolean ClearScreen (optional) Clear all previous messages.
|
||||
-- @return #MESSAGE
|
||||
-- @usage
|
||||
--
|
||||
-- MessageAll = MESSAGE:NewType( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", MESSAGE.Type.Information )
|
||||
-- MessageRED = MESSAGE:NewType( "To the RED Players: You receive a penalty because you've killed one of your own units", MESSAGE.Type.Information )
|
||||
-- MessageClient1 = MESSAGE:NewType( "Congratulations, you've just hit a target", MESSAGE.Type.Update )
|
||||
-- MessageClient2 = MESSAGE:NewType( "Congratulations, you've just killed a target", MESSAGE.Type.Update )
|
||||
--
|
||||
function MESSAGE:NewType( MessageText, MessageType, ClearScreen )
|
||||
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
self:F( { MessageText } )
|
||||
|
||||
|
||||
self.MessageType = MessageType
|
||||
|
||||
self.ClearScreen=false
|
||||
if ClearScreen~=nil then
|
||||
self.ClearScreen=ClearScreen
|
||||
|
||||
self.ClearScreen = false
|
||||
if ClearScreen ~= nil then
|
||||
self.ClearScreen = ClearScreen
|
||||
end
|
||||
|
||||
self.MessageTime = timer.getTime()
|
||||
self.MessageText = MessageText:gsub("^\n","",1):gsub("\n$","",1)
|
||||
|
||||
self.MessageText = MessageText:gsub( "^\n", "", 1 ):gsub( "\n$", "", 1 )
|
||||
|
||||
return self
|
||||
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
|
||||
-- @return #MESSAGE
|
||||
function MESSAGE:Clear()
|
||||
self:F()
|
||||
self.ClearScreen=true
|
||||
self.ClearScreen = true
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Sends a MESSAGE to a Client Group. Note that the Group needs to be defined within the ME with the skillset "Client" or "Player".
|
||||
-- @param #MESSAGE self
|
||||
-- @param Wrapper.Client#CLIENT Client is the Group of the Client.
|
||||
-- @param Core.Settings#SETTINGS Settings Settings used to display the message.
|
||||
-- @return #MESSAGE
|
||||
-- @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 )
|
||||
-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" ):ToClient( ClientGroup )
|
||||
-- or
|
||||
-- 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 )
|
||||
-- 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 )
|
||||
-- -- 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 )
|
||||
-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" ):ToClient( ClientGroup )
|
||||
-- or
|
||||
-- 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 )
|
||||
-- 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 )
|
||||
self:F( Client )
|
||||
self:F( Client )
|
||||
|
||||
if Client and Client:GetClientGroupID() then
|
||||
if Client and Client:GetClientGroupID() 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.MessageCategory = "" -- self.MessageType .. ": "
|
||||
end
|
||||
|
||||
if self.MessageDuration ~= 0 then
|
||||
local ClientGroupID = Client:GetClientGroupID()
|
||||
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
|
||||
trigger.action.outTextForGroup( ClientGroupID, self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration , self.ClearScreen)
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
local ClientGroupID = Client:GetClientGroupID()
|
||||
self:T( self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ) .. " / " .. self.MessageDuration )
|
||||
trigger.action.outTextForGroup( ClientGroupID, self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ), self.MessageDuration, self.ClearScreen )
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Sends a MESSAGE to a Group.
|
||||
--- Sends a MESSAGE to a Group.
|
||||
-- @param #MESSAGE self
|
||||
-- @param Wrapper.Group#GROUP Group to which the message is displayed.
|
||||
-- @return #MESSAGE Message object.
|
||||
@@ -218,74 +217,80 @@ function MESSAGE:ToGroup( Group, Settings )
|
||||
self:F( Group.GroupName )
|
||||
|
||||
if Group then
|
||||
|
||||
|
||||
if self.MessageType then
|
||||
local Settings = Settings or ( Group and _DATABASE:GetPlayerSettings( Group:GetPlayerName() ) ) or _SETTINGS -- Core.Settings#SETTINGS
|
||||
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 )
|
||||
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 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 )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- 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()
|
||||
--
|
||||
-- -- 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.RED )
|
||||
|
||||
return self
|
||||
self:ToCoalition( coalition.side.BLUE )
|
||||
|
||||
return self
|
||||
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 #DCS.coalition.side CoalitionSide @{#DCS.coalition.side} to which the message is displayed.
|
||||
-- @param Core.Settings#SETTINGS Settings (Optional) Settings for message display.
|
||||
-- @return #MESSAGE Message object.
|
||||
-- @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 )
|
||||
-- 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 )
|
||||
-- or
|
||||
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" )
|
||||
-- MessageRED:ToCoalition( coalition.side.RED )
|
||||
--
|
||||
-- -- 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 )
|
||||
-- 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 )
|
||||
-- or
|
||||
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" )
|
||||
-- MessageRED:ToCoalition( coalition.side.RED )
|
||||
--
|
||||
function MESSAGE:ToCoalition( CoalitionSide, Settings )
|
||||
self:F( CoalitionSide )
|
||||
self:F( CoalitionSide )
|
||||
|
||||
if self.MessageType then
|
||||
local Settings = Settings or _SETTINGS -- Core.Settings#SETTINGS
|
||||
@@ -293,20 +298,20 @@ function MESSAGE:ToCoalition( CoalitionSide, Settings )
|
||||
self.MessageCategory = "" -- self.MessageType .. ": "
|
||||
end
|
||||
|
||||
if CoalitionSide then
|
||||
if CoalitionSide then
|
||||
if self.MessageDuration ~= 0 then
|
||||
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
|
||||
trigger.action.outTextForCoalition( CoalitionSide, self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration, self.ClearScreen )
|
||||
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 )
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
return self
|
||||
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 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
|
||||
function MESSAGE:ToCoalitionIf( CoalitionSide, Condition )
|
||||
self:F( CoalitionSide )
|
||||
@@ -314,7 +319,7 @@ function MESSAGE:ToCoalitionIf( CoalitionSide, Condition )
|
||||
if Condition and Condition == true then
|
||||
self:ToCoalition( CoalitionSide )
|
||||
end
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -323,14 +328,16 @@ end
|
||||
-- @param Core.Settings#Settings Settings (Optional) Settings for message display.
|
||||
-- @return #MESSAGE
|
||||
-- @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()
|
||||
-- or
|
||||
-- MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" ):ToAll()
|
||||
-- or
|
||||
-- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" )
|
||||
-- MessageAll:ToAll()
|
||||
function MESSAGE:ToAll(Settings)
|
||||
--
|
||||
-- -- 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()
|
||||
-- or
|
||||
-- MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" ):ToAll()
|
||||
-- or
|
||||
-- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" )
|
||||
-- MessageAll:ToAll()
|
||||
--
|
||||
function MESSAGE:ToAll( Settings )
|
||||
self:F()
|
||||
|
||||
if self.MessageType then
|
||||
@@ -340,14 +347,13 @@ function MESSAGE:ToAll(Settings)
|
||||
end
|
||||
|
||||
if self.MessageDuration ~= 0 then
|
||||
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
|
||||
trigger.action.outText( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration, self.ClearScreen )
|
||||
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 )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Sends a MESSAGE to all players if the given Condition is true.
|
||||
-- @param #MESSAGE self
|
||||
-- @return #MESSAGE
|
||||
@@ -357,5 +363,5 @@ function MESSAGE:ToAllIf( Condition )
|
||||
self:ToAll()
|
||||
end
|
||||
|
||||
return self
|
||||
return self
|
||||
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.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
--
|
||||
-- * Create text blocks that are formatted.
|
||||
-- * Create automatic indents.
|
||||
-- * Variate the delimiters between reporting lines.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Authors: FlightControl : Design & Programming
|
||||
@@ -15,7 +15,6 @@
|
||||
-- @module Core.Report
|
||||
-- @image Core_Report.JPG
|
||||
|
||||
|
||||
--- @type REPORT
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
@@ -36,7 +35,7 @@ function REPORT:New( Title )
|
||||
|
||||
self.Report = {}
|
||||
|
||||
self:SetTitle( Title or "" )
|
||||
self:SetTitle( Title or "" )
|
||||
self:SetIndent( 3 )
|
||||
|
||||
return self
|
||||
@@ -45,28 +44,26 @@ end
|
||||
--- Has the REPORT Text?
|
||||
-- @param #REPORT self
|
||||
-- @return #boolean
|
||||
function REPORT:HasText() --R2.1
|
||||
|
||||
function REPORT:HasText() -- R2.1
|
||||
|
||||
return #self.Report > 0
|
||||
end
|
||||
|
||||
|
||||
--- Set indent of a REPORT.
|
||||
-- @param #REPORT self
|
||||
-- @param #number Indent
|
||||
-- @return #REPORT
|
||||
function REPORT:SetIndent( Indent ) --R2.1
|
||||
function REPORT:SetIndent( Indent ) -- R2.1
|
||||
self.Indent = Indent
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Add a new line to a REPORT.
|
||||
-- @param #REPORT self
|
||||
-- @param #string Text
|
||||
-- @return #REPORT
|
||||
function REPORT:Add( Text )
|
||||
self.Report[#self.Report+1] = Text
|
||||
self.Report[#self.Report + 1] = Text
|
||||
return self
|
||||
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.
|
||||
-- @return #REPORT
|
||||
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
|
||||
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 #string Delimiter (optional) A delimiter text.
|
||||
-- @return #string The report text.
|
||||
function REPORT:Text( Delimiter )
|
||||
Delimiter = Delimiter or "\n"
|
||||
local ReportText = ( self.Title ~= "" and self.Title .. Delimiter or self.Title ) .. table.concat( self.Report, Delimiter ) or ""
|
||||
local ReportText = (self.Title ~= "" and self.Title .. Delimiter or self.Title) .. table.concat( self.Report, Delimiter ) or ""
|
||||
return ReportText
|
||||
end
|
||||
|
||||
@@ -95,7 +92,7 @@ end
|
||||
-- @param #string Title The title of the report.
|
||||
-- @return #REPORT
|
||||
function REPORT:SetTitle( Title )
|
||||
self.Title = Title
|
||||
self.Title = Title
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
@@ -1,36 +1,36 @@
|
||||
--- **Core** -- SCHEDULEDISPATCHER dispatches the different schedules.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- Takes care of the creation and dispatching of scheduled functions for SCHEDULER objects.
|
||||
--
|
||||
--
|
||||
-- This class is tricky and needs some thorough explanation.
|
||||
-- SCHEDULE classes are used to schedule functions for objects, or as persistent objects.
|
||||
-- The SCHEDULEDISPATCHER class ensures that:
|
||||
--
|
||||
--
|
||||
-- - Scheduled functions are planned 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.
|
||||
--
|
||||
--
|
||||
-- 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* attached to another object, then the SCHEDULER object is _not persistent_ within memory after a garbage collection!
|
||||
--
|
||||
-- The none persistency of SCHEDULERS attached to objects is required to allow SCHEDULER objects to be garbage collectged, when the parent object is also desroyed or nillified and garbage collected.
|
||||
-- Even when there are pending timer scheduled functions to be executed for the SCHEDULER object,
|
||||
--
|
||||
-- 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,
|
||||
-- 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 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.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ### Contributions: -
|
||||
-- ### Authors: FlightControl : Design & Programming
|
||||
--
|
||||
--
|
||||
-- @module Core.ScheduleDispatcher
|
||||
-- @image Core_Schedule_Dispatcher.JPG
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
-- @type SCHEDULEDISPATCHER
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @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 Schedule Meta table setmetatable( {}, { __mode = "k" } ).
|
||||
-- @extends Core.Base#BASE
|
||||
@@ -46,11 +46,11 @@
|
||||
--- The SCHEDULEDISPATCHER structure
|
||||
-- @type SCHEDULEDISPATCHER
|
||||
SCHEDULEDISPATCHER = {
|
||||
ClassName = "SCHEDULEDISPATCHER",
|
||||
CallID = 0,
|
||||
PersistentSchedulers = {},
|
||||
ObjectSchedulers = {},
|
||||
Schedule = nil,
|
||||
ClassName = "SCHEDULEDISPATCHER",
|
||||
CallID = 0,
|
||||
PersistentSchedulers = {},
|
||||
ObjectSchedulers = {},
|
||||
Schedule = nil,
|
||||
}
|
||||
|
||||
--- 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 #table Arguments Schedule function arguments.
|
||||
-- @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 Stop Stop time in seconds.
|
||||
-- @field #number StartTime Time in seconds when the scheduler is created.
|
||||
@@ -77,7 +77,7 @@ end
|
||||
|
||||
--- Add a Schedule to the ScheduleDispatcher.
|
||||
-- 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.
|
||||
-- @param #SCHEDULEDISPATCHER self
|
||||
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
|
||||
@@ -85,7 +85,7 @@ end
|
||||
-- @param #table ScheduleArguments Table of arguments passed to the ScheduleFunction.
|
||||
-- @param #number Start Start time in seconds.
|
||||
-- @param #number Repeat Repeat interval in seconds.
|
||||
-- @param #number Randomize Radomization factor [0,1].
|
||||
-- @param #number Randomize Randomization factor [0,1].
|
||||
-- @param #number Stop Stop time in seconds.
|
||||
-- @param #number TraceLevel Trace level [0,3].
|
||||
-- @param Core.Fsm#FSM Fsm Finite state model.
|
||||
@@ -95,39 +95,38 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
||||
|
||||
-- Increase counter.
|
||||
self.CallID = self.CallID + 1
|
||||
|
||||
|
||||
-- Create ID.
|
||||
local CallID = self.CallID .. "#" .. ( Scheduler.MasterObject and Scheduler.MasterObject.GetClassNameAndID and Scheduler.MasterObject:GetClassNameAndID() or "" ) or ""
|
||||
|
||||
self:T2(string.format("Adding schedule #%d CallID=%s", self.CallID, CallID))
|
||||
local CallID = self.CallID .. "#" .. (Scheduler.MasterObject and Scheduler.MasterObject.GetClassNameAndID and Scheduler.MasterObject:GetClassNameAndID() or "") or ""
|
||||
|
||||
self:T2( string.format( "Adding schedule #%d CallID=%s", self.CallID, CallID ) )
|
||||
|
||||
-- Initialize PersistentSchedulers
|
||||
self.PersistentSchedulers = self.PersistentSchedulers or {}
|
||||
|
||||
-- Initialize the ObjectSchedulers array, which is a weakly coupled table.
|
||||
-- If the object used as the key is nil, then the garbage collector will remove the item from the Functions array.
|
||||
self.ObjectSchedulers = self.ObjectSchedulers or setmetatable( {}, { __mode = "v" } )
|
||||
|
||||
self.ObjectSchedulers = self.ObjectSchedulers or setmetatable( {}, { __mode = "v" } )
|
||||
|
||||
if Scheduler.MasterObject then
|
||||
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
|
||||
self.PersistentSchedulers[CallID] = Scheduler
|
||||
self:F3( { CallID = CallID, PersistentScheduler = self.PersistentSchedulers[CallID] } )
|
||||
end
|
||||
|
||||
|
||||
self.Schedule = self.Schedule or setmetatable( {}, { __mode = "k" } )
|
||||
self.Schedule[Scheduler] = self.Schedule[Scheduler] or {}
|
||||
self.Schedule[Scheduler][CallID] = {} --#SCHEDULEDISPATCHER.ScheduleData
|
||||
self.Schedule[Scheduler][CallID] = {} -- #SCHEDULEDISPATCHER.ScheduleData
|
||||
self.Schedule[Scheduler][CallID].Function = ScheduleFunction
|
||||
self.Schedule[Scheduler][CallID].Arguments = ScheduleArguments
|
||||
self.Schedule[Scheduler][CallID].StartTime = timer.getTime() + ( Start or 0 )
|
||||
self.Schedule[Scheduler][CallID].StartTime = timer.getTime() + (Start or 0)
|
||||
self.Schedule[Scheduler][CallID].Start = Start + 0.1
|
||||
self.Schedule[Scheduler][CallID].Repeat = Repeat or 0
|
||||
self.Schedule[Scheduler][CallID].Randomize = Randomize or 0
|
||||
self.Schedule[Scheduler][CallID].Stop = Stop
|
||||
|
||||
|
||||
|
||||
-- This section handles the tracing of the scheduled calls.
|
||||
-- Because these calls will be executed with a delay, we inspect the place where these scheduled calls are initiated.
|
||||
-- The Info structure contains the output of the debug.getinfo() calls, which inspects the call stack for the function name, line number and source name.
|
||||
@@ -149,10 +148,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.
|
||||
-- To obtain the correct function name for FSM object calls, the function is mentioned in the call stack at a higher stack level.
|
||||
-- So when function name stored in Info.name is nil, then I inspect the function name within the call stack one level higher.
|
||||
-- So this little piece of code does its magic wonderfully, preformance overhead is neglectible, as scheduled calls don't happen that often.
|
||||
-- So this little piece of code does its magic wonderfully, performance overhead is negligible, as scheduled calls don't happen that often.
|
||||
|
||||
local Info = {}
|
||||
|
||||
|
||||
if debug then
|
||||
TraceLevel = TraceLevel or 2
|
||||
Info = debug.getinfo( TraceLevel, "nlS" )
|
||||
@@ -166,7 +165,7 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
||||
|
||||
--- Function passed to the DCS timer.scheduleFunction()
|
||||
self.Schedule[Scheduler][CallID].CallHandler = function( Params )
|
||||
|
||||
|
||||
local CallID = Params.CallID
|
||||
local Info = Params.Info or {}
|
||||
local Source = Info.source or "?"
|
||||
@@ -180,27 +179,27 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
||||
end
|
||||
return errmsg
|
||||
end
|
||||
|
||||
-- Get object or persistant scheduler object.
|
||||
local Scheduler = self.ObjectSchedulers[CallID] --Core.Scheduler#SCHEDULER
|
||||
|
||||
-- Get object or persistent scheduler object.
|
||||
local Scheduler = self.ObjectSchedulers[CallID] -- Core.Scheduler#SCHEDULER
|
||||
if not Scheduler then
|
||||
Scheduler = self.PersistentSchedulers[CallID]
|
||||
end
|
||||
|
||||
--self:T3( { Scheduler = Scheduler } )
|
||||
|
||||
|
||||
-- self:T3( { Scheduler = Scheduler } )
|
||||
|
||||
if Scheduler then
|
||||
|
||||
local MasterObject = tostring(Scheduler.MasterObject)
|
||||
|
||||
-- Schedule object.
|
||||
local Schedule = self.Schedule[Scheduler][CallID] --#SCHEDULEDISPATCHER.ScheduleData
|
||||
|
||||
--self:T3( { Schedule = Schedule } )
|
||||
local MasterObject = tostring( Scheduler.MasterObject )
|
||||
|
||||
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 ScheduleFunction = Schedule.Function
|
||||
local ScheduleArguments = Schedule.Arguments or {}
|
||||
local Start = Schedule.Start
|
||||
@@ -208,18 +207,17 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
||||
local Randomize = Schedule.Randomize or 0
|
||||
local Stop = Schedule.Stop or 0
|
||||
local ScheduleID = Schedule.ScheduleID
|
||||
|
||||
|
||||
local Prefix = ( Repeat == 0 ) and "--->" or "+++>"
|
||||
|
||||
|
||||
local Prefix = (Repeat == 0) and "--->" or "+++>"
|
||||
|
||||
local Status, Result
|
||||
--self:E( { SchedulerObject = SchedulerObject } )
|
||||
-- self:E( { SchedulerObject = SchedulerObject } )
|
||||
if SchedulerObject then
|
||||
local function Timer()
|
||||
if ShowTrace then
|
||||
SchedulerObject:T( Prefix .. Name .. ":" .. Line .. " (" .. Source .. ")" )
|
||||
end
|
||||
return ScheduleFunction( SchedulerObject, unpack( ScheduleArguments ) )
|
||||
return ScheduleFunction( SchedulerObject, unpack( ScheduleArguments ) )
|
||||
end
|
||||
Status, Result = xpcall( Timer, ErrorHandler )
|
||||
else
|
||||
@@ -227,40 +225,39 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
||||
if ShowTrace then
|
||||
self:T( Prefix .. Name .. ":" .. Line .. " (" .. Source .. ")" )
|
||||
end
|
||||
return ScheduleFunction( unpack( ScheduleArguments ) )
|
||||
return ScheduleFunction( unpack( ScheduleArguments ) )
|
||||
end
|
||||
Status, Result = xpcall( Timer, ErrorHandler )
|
||||
end
|
||||
|
||||
|
||||
local CurrentTime = timer.getTime()
|
||||
local StartTime = Schedule.StartTime
|
||||
|
||||
-- Debug info.
|
||||
self:F3( { CallID=CallID, ScheduleID=ScheduleID, Master = MasterObject, CurrentTime = CurrentTime, StartTime = StartTime, Start = Start, Repeat = Repeat, Randomize = Randomize, Stop = Stop } )
|
||||
|
||||
|
||||
if Status and (( Result == nil ) or ( Result and Result ~= false ) ) then
|
||||
|
||||
if Repeat ~= 0 and ( ( Stop == 0 ) or ( Stop ~= 0 and CurrentTime <= StartTime + Stop ) ) then
|
||||
local ScheduleTime = CurrentTime + Repeat + math.random(- ( Randomize * Repeat / 2 ), ( Randomize * Repeat / 2 )) + 0.0001 -- Accuracy
|
||||
--self:T3( { Repeat = CallID, CurrentTime, ScheduleTime, ScheduleArguments } )
|
||||
self:F3( { CallID = CallID, ScheduleID = ScheduleID, Master = MasterObject, CurrentTime = CurrentTime, StartTime = StartTime, Start = Start, Repeat = Repeat, Randomize = Randomize, Stop = Stop } )
|
||||
|
||||
if Status and ((Result == nil) or (Result and Result ~= false)) then
|
||||
|
||||
if Repeat ~= 0 and ((Stop == 0) or (Stop ~= 0 and CurrentTime <= StartTime + Stop)) then
|
||||
local ScheduleTime = CurrentTime + Repeat + math.random( -(Randomize * Repeat / 2), (Randomize * Repeat / 2) ) + 0.0001 -- Accuracy
|
||||
-- self:T3( { Repeat = CallID, CurrentTime, ScheduleTime, ScheduleArguments } )
|
||||
return ScheduleTime -- returns the next time the function needs to be called.
|
||||
else
|
||||
self:Stop( Scheduler, CallID )
|
||||
end
|
||||
|
||||
|
||||
else
|
||||
self:Stop( Scheduler, CallID )
|
||||
end
|
||||
else
|
||||
self:I( "<<<>" .. Name .. ":" .. Line .. " (" .. Source .. ")" )
|
||||
end
|
||||
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
self:Start( Scheduler, CallID, Info )
|
||||
|
||||
|
||||
return CallID
|
||||
end
|
||||
|
||||
@@ -284,33 +281,33 @@ end
|
||||
-- @param #string Info (Optional) Debug info.
|
||||
function SCHEDULEDISPATCHER:Start( Scheduler, CallID, Info )
|
||||
self:F2( { Start = CallID, Scheduler = Scheduler } )
|
||||
|
||||
|
||||
if CallID then
|
||||
|
||||
local Schedule = self.Schedule[Scheduler][CallID] --#SCHEDULEDISPATCHER.ScheduleData
|
||||
|
||||
|
||||
local Schedule = self.Schedule[Scheduler][CallID] -- #SCHEDULEDISPATCHER.ScheduleData
|
||||
|
||||
-- Only start when there is no ScheduleID defined!
|
||||
-- This prevents to "Start" the scheduler twice with the same CallID...
|
||||
if not Schedule.ScheduleID then
|
||||
|
||||
|
||||
-- Current time in seconds.
|
||||
local Tnow=timer.getTime()
|
||||
|
||||
Schedule.StartTime = Tnow -- Set the StartTime field to indicate when the scheduler started.
|
||||
|
||||
local Tnow = timer.getTime()
|
||||
|
||||
Schedule.StartTime = Tnow -- Set the StartTime field to indicate when the scheduler started.
|
||||
|
||||
-- Start DCS schedule function https://wiki.hoggitworld.com/view/DCS_func_scheduleFunction
|
||||
Schedule.ScheduleID = timer.scheduleFunction(Schedule.CallHandler, { CallID = CallID, Info = Info }, Tnow + Schedule.Start)
|
||||
|
||||
self:T(string.format("Starting scheduledispatcher Call ID=%s ==> Schedule ID=%s", tostring(CallID), tostring(Schedule.ScheduleID)))
|
||||
Schedule.ScheduleID = timer.scheduleFunction( Schedule.CallHandler, { CallID = CallID, Info = Info }, Tnow + Schedule.Start )
|
||||
|
||||
self:T( string.format( "Starting SCHEDULEDISPATCHER Call ID=%s ==> Schedule ID=%s", tostring( CallID ), tostring( Schedule.ScheduleID ) ) )
|
||||
end
|
||||
|
||||
|
||||
else
|
||||
|
||||
|
||||
-- Recursive.
|
||||
for CallID, Schedule in pairs( self.Schedule[Scheduler] or {} ) do
|
||||
self:Start( Scheduler, CallID, Info ) -- Recursive
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -322,29 +319,29 @@ function SCHEDULEDISPATCHER:Stop( Scheduler, CallID )
|
||||
self:F2( { Stop = CallID, Scheduler = Scheduler } )
|
||||
|
||||
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.
|
||||
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
|
||||
timer.removeFunction(Schedule.ScheduleID)
|
||||
|
||||
timer.removeFunction( Schedule.ScheduleID )
|
||||
|
||||
Schedule.ScheduleID = nil
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
else
|
||||
|
||||
|
||||
for CallID, Schedule in pairs( self.Schedule[Scheduler] or {} ) do
|
||||
self:Stop( Scheduler, CallID ) -- Recursive
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -359,7 +356,7 @@ function SCHEDULEDISPATCHER:Clear( Scheduler )
|
||||
end
|
||||
end
|
||||
|
||||
--- Shopw tracing info.
|
||||
--- Show tracing info.
|
||||
-- @param #SCHEDULEDISPATCHER self
|
||||
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
|
||||
function SCHEDULEDISPATCHER:ShowTrace( Scheduler )
|
||||
|
||||
@@ -1,41 +1,41 @@
|
||||
--- **Core** - Prepares and handles the execution of functions over scheduled time (intervals).
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
--
|
||||
-- * Schedule functions over time,
|
||||
-- * optionally in an optional specified time interval,
|
||||
-- * optionally **repeating** with a specified time repeat interval,
|
||||
-- * optionally **randomizing** with a specified time interval randomization factor,
|
||||
-- * optionally **stop** the repeating after a specified time interval.
|
||||
-- * optionally in an optional specified time interval,
|
||||
-- * optionally **repeating** with a specified time repeat interval,
|
||||
-- * optionally **randomizing** with a specified time interval randomization factor,
|
||||
-- * optionally **stop** the repeating after a specified time interval.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- # Demo Missions
|
||||
--
|
||||
--
|
||||
-- ### [SCHEDULER Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/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)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # YouTube Channel
|
||||
--
|
||||
-- ### [SCHEDULER YouTube Channel (none)]()
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- # YouTube Channel
|
||||
--
|
||||
-- ### [SCHEDULER YouTube Channel (none)]()
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- * FlightControl : Concept & Testing
|
||||
--
|
||||
-- ### Authors:
|
||||
--
|
||||
--
|
||||
-- ### Authors:
|
||||
--
|
||||
-- * FlightControl : Design & Programming
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module Core.Scheduler
|
||||
@@ -48,62 +48,61 @@
|
||||
-- @field #boolean ShowTrace Trace info if true.
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
|
||||
--- 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**.
|
||||
-- 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}(),
|
||||
-- which can start and stop specific repeating schedules respectively within a SCHEDULER object.
|
||||
--
|
||||
-- ## SCHEDULER constructor
|
||||
--
|
||||
--
|
||||
-- The SCHEDULER class is quite easy to use, but note that the New constructor has variable parameters:
|
||||
--
|
||||
--
|
||||
-- The @{#SCHEDULER.New}() method returns 2 variables:
|
||||
--
|
||||
--
|
||||
-- 1. The SCHEDULER object reference.
|
||||
-- 2. The first schedule planned in the SCHEDULER object.
|
||||
--
|
||||
--
|
||||
-- To clarify the different appliances, lets have a look at the following examples:
|
||||
--
|
||||
--
|
||||
-- ### Construct a SCHEDULER object without a persistent schedule.
|
||||
--
|
||||
--
|
||||
-- * @{#SCHEDULER.New}( nil ): Setup a new SCHEDULER object, which is persistently executed after garbage collection.
|
||||
--
|
||||
--
|
||||
-- MasterObject = SCHEDULER:New()
|
||||
-- SchedulerID = MasterObject:Schedule( nil, ScheduleFunction, {} )
|
||||
--
|
||||
--
|
||||
-- The above example creates a new MasterObject, but does not schedule anything.
|
||||
-- A separate schedule is created by using the MasterObject using the method :Schedule..., which returns a ScheduleID
|
||||
--
|
||||
--
|
||||
-- ### Construct a SCHEDULER object without a volatile schedule, but volatile to the Object existence...
|
||||
--
|
||||
-- * @{#SCHEDULER.New}( Object ): Setup a new SCHEDULER object, which is linked to the Object. When the Object is nillified or destroyed, the SCHEDULER object will also be destroyed and stopped after garbage collection.
|
||||
--
|
||||
--
|
||||
-- * @{#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" )
|
||||
-- MasterObject = SCHEDULER:New( ZoneObject )
|
||||
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {} )
|
||||
-- ...
|
||||
-- ZoneObject = nil
|
||||
-- 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.
|
||||
-- A separate schedule is created by using the MasterObject using the method :Schedule()..., which returns a ScheduleID
|
||||
-- Later in the logic, the ZoneObject is put to nil, and garbage is collected.
|
||||
-- As a result, the MasterObject will cancel any planned schedule.
|
||||
--
|
||||
--
|
||||
-- ### Construct a SCHEDULER object with a persistent schedule.
|
||||
--
|
||||
--
|
||||
-- * @{#SCHEDULER.New}( nil, Function, FunctionArguments, Start, ... ): Setup a new persistent SCHEDULER object, and start a new schedule for the Function with the defined FunctionArguments according the Start and sequent parameters.
|
||||
--
|
||||
--
|
||||
-- MasterObject, SchedulerID = SCHEDULER:New( nil, ScheduleFunction, {} )
|
||||
--
|
||||
--
|
||||
-- The above example creates a new MasterObject, and does schedule the first schedule as part of the call.
|
||||
-- Note that 2 variables are returned here: MasterObject, ScheduleID...
|
||||
--
|
||||
--
|
||||
-- ### Construct a SCHEDULER object without a schedule, but volatile to the Object existence...
|
||||
--
|
||||
--
|
||||
-- * @{#SCHEDULER.New}( Object, Function, FunctionArguments, Start, ... ): Setup a new SCHEDULER object, linked to Object, and start a new schedule for the Function with the defined FunctionArguments according the Start and sequent parameters.
|
||||
--
|
||||
-- ZoneObject = ZONE:New( "ZoneName" )
|
||||
@@ -112,13 +111,13 @@
|
||||
-- ...
|
||||
-- ZoneObject = nil
|
||||
-- garbagecollect()
|
||||
--
|
||||
--
|
||||
-- The above example creates a new MasterObject, and schedules a method call (ScheduleFunction),
|
||||
-- and is bound to the existence of ZoneObject, which is a ZONE object (ZoneObject).
|
||||
-- Both a MasterObject and a SchedulerID variable are returned.
|
||||
-- Later in the logic, the ZoneObject is put to nil, and garbage is collected.
|
||||
-- As a result, the MasterObject will cancel the planned schedule.
|
||||
--
|
||||
--
|
||||
-- ## SCHEDULER timer stopping and (re-)starting.
|
||||
--
|
||||
-- The SCHEDULER can be stopped and restarted with the following methods:
|
||||
@@ -133,70 +132,70 @@
|
||||
-- MasterObject:Stop( SchedulerID )
|
||||
-- ...
|
||||
-- MasterObject:Start( SchedulerID )
|
||||
--
|
||||
--
|
||||
-- The above example creates a new MasterObject, and does schedule the first schedule as part of the call.
|
||||
-- Note that 2 variables are returned here: MasterObject, ScheduleID...
|
||||
-- Later in the logic, the repeating schedule with SchedulerID is stopped.
|
||||
-- A bit later, the repeating schedule with SchedulerId is (re)-started.
|
||||
--
|
||||
-- Note that 2 variables are returned here: MasterObject, ScheduleID...
|
||||
-- Later in the logic, the repeating schedule with SchedulerID is stopped.
|
||||
-- A bit later, the repeating schedule with SchedulerId is (re)-started.
|
||||
--
|
||||
-- ## 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.
|
||||
--
|
||||
--
|
||||
-- Consider the following code fragment of the SCHEDULER object creation.
|
||||
--
|
||||
--
|
||||
-- ZoneObject = ZONE:New( "ZoneName" )
|
||||
-- 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
|
||||
--
|
||||
--
|
||||
-- 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
|
||||
--
|
||||
--
|
||||
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {}, 10 )
|
||||
--
|
||||
--
|
||||
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds ...
|
||||
--
|
||||
--
|
||||
-- ### A schedule with a repeating time interval, planned over time
|
||||
--
|
||||
--
|
||||
-- SchedulerID = 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 ...
|
||||
--
|
||||
--
|
||||
-- ### A schedule with a repeating time interval, planned over time, with time interval randomization
|
||||
--
|
||||
--
|
||||
-- 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 ...
|
||||
-- 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,
|
||||
-- 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,
|
||||
-- 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
|
||||
--
|
||||
--
|
||||
-- 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.
|
||||
-- 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,
|
||||
-- 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,
|
||||
-- which is in this example between **30** and **90** seconds.
|
||||
-- The schedule will stop after **300** seconds.
|
||||
--
|
||||
--
|
||||
-- @field #SCHEDULER
|
||||
SCHEDULER = {
|
||||
ClassName = "SCHEDULER",
|
||||
Schedules = {},
|
||||
MasterObject = nil,
|
||||
ShowTrace = nil,
|
||||
ClassName = "SCHEDULER",
|
||||
Schedules = {},
|
||||
MasterObject = nil,
|
||||
ShowTrace = nil,
|
||||
}
|
||||
|
||||
--- SCHEDULER constructor.
|
||||
@@ -211,15 +210,15 @@ SCHEDULER = {
|
||||
-- @return #SCHEDULER self.
|
||||
-- @return #table The ScheduleID of the planned schedule.
|
||||
function SCHEDULER:New( MasterObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop )
|
||||
|
||||
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #SCHEDULER
|
||||
self:F2( { Start, Repeat, RandomizeFactor, Stop } )
|
||||
|
||||
local ScheduleID = nil
|
||||
|
||||
|
||||
self.MasterObject = MasterObject
|
||||
self.ShowTrace = false
|
||||
|
||||
|
||||
if SchedulerFunction then
|
||||
ScheduleID = self:Schedule( MasterObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop, 3 )
|
||||
end
|
||||
@@ -235,7 +234,7 @@ end
|
||||
-- @param #number Start Specifies the amount of seconds that will be waited before the scheduling is started, and the event function is called.
|
||||
-- @param #number Repeat Specifies the time interval in seconds when the scheduler will call the event function.
|
||||
-- @param #number RandomizeFactor Specifies a randomization factor between 0 and 1 to randomize the Repeat.
|
||||
-- @param #number Stop 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 Core.Fsm#FSM Fsm Finite state model.
|
||||
-- @return #table The ScheduleID of the planned schedule.
|
||||
@@ -245,28 +244,27 @@ function SCHEDULER:Schedule( MasterObject, SchedulerFunction, SchedulerArguments
|
||||
|
||||
-- Debug info.
|
||||
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
|
||||
end
|
||||
self:F3( { "Schedule :", ObjectName, tostring( MasterObject ), Start, Repeat, RandomizeFactor, Stop } )
|
||||
|
||||
self:F3( { "Schedule :", ObjectName, tostring( MasterObject ), Start, Repeat, RandomizeFactor, Stop } )
|
||||
|
||||
-- Set master object.
|
||||
self.MasterObject = MasterObject
|
||||
|
||||
|
||||
-- Add schedule.
|
||||
local ScheduleID = _SCHEDULEDISPATCHER:AddSchedule(
|
||||
self,
|
||||
SchedulerFunction,
|
||||
SchedulerArguments,
|
||||
Start,
|
||||
Repeat,
|
||||
RandomizeFactor,
|
||||
Stop,
|
||||
TraceLevel or 3,
|
||||
Fsm
|
||||
)
|
||||
|
||||
self.Schedules[#self.Schedules+1] = ScheduleID
|
||||
local ScheduleID = _SCHEDULEDISPATCHER:AddSchedule( self,
|
||||
SchedulerFunction,
|
||||
SchedulerArguments,
|
||||
Start,
|
||||
Repeat,
|
||||
RandomizeFactor,
|
||||
Stop,
|
||||
TraceLevel or 3,
|
||||
Fsm
|
||||
)
|
||||
|
||||
self.Schedules[#self.Schedules + 1] = ScheduleID
|
||||
|
||||
return ScheduleID
|
||||
end
|
||||
@@ -276,7 +274,7 @@ end
|
||||
-- @param #string ScheduleID (Optional) The ScheduleID of the planned (repeating) schedule.
|
||||
function SCHEDULER:Start( ScheduleID )
|
||||
self:F3( { ScheduleID } )
|
||||
self:T(string.format("Starting scheduler ID=%s", tostring(ScheduleID)))
|
||||
self:T( string.format( "Starting scheduler ID=%s", tostring( ScheduleID ) ) )
|
||||
_SCHEDULEDISPATCHER:Start( self, ScheduleID )
|
||||
end
|
||||
|
||||
@@ -285,7 +283,7 @@ end
|
||||
-- @param #string ScheduleID (Optional) The ScheduleID of the planned (repeating) schedule.
|
||||
function SCHEDULER:Stop( ScheduleID )
|
||||
self:F3( { ScheduleID } )
|
||||
self:T(string.format("Stopping scheduler ID=%s", tostring(ScheduleID)))
|
||||
self:T( string.format( "Stopping scheduler ID=%s", tostring( ScheduleID ) ) )
|
||||
_SCHEDULEDISPATCHER:Stop( self, ScheduleID )
|
||||
end
|
||||
|
||||
@@ -294,15 +292,15 @@ end
|
||||
-- @param #string ScheduleID (optional) The ScheduleID of the planned (repeating) schedule.
|
||||
function SCHEDULER:Remove( 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 )
|
||||
end
|
||||
|
||||
--- Clears all pending schedules.
|
||||
-- @param #SCHEDULER self
|
||||
function SCHEDULER:Clear()
|
||||
self:F3( )
|
||||
self:T(string.format("Clearing scheduler"))
|
||||
self:F3()
|
||||
self:T( string.format( "Clearing scheduler" ) )
|
||||
_SCHEDULEDISPATCHER:Clear( self )
|
||||
end
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -29,15 +29,14 @@
|
||||
-- @module Core.Settings
|
||||
-- @image Core_Settings.JPG
|
||||
|
||||
|
||||
--- @type SETTINGS
|
||||
-- @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:
|
||||
--
|
||||
-- - **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.
|
||||
-- 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
|
||||
--
|
||||
@@ -69,7 +68,7 @@
|
||||
--
|
||||
-- ## 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.
|
||||
-- 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.
|
||||
@@ -94,8 +93,8 @@
|
||||
--
|
||||
-- - A2G BR: [Bearing Range](https://en.wikipedia.org/wiki/Bearing_(navigation)).
|
||||
-- - A2G MGRS: The [Military Grid Reference System](https://en.wikipedia.org/wiki/Military_Grid_Reference_System). The accuracy can also be adapted.
|
||||
-- - A2G LL DMS: Lattitude Longitude [Degrees Minutes Seconds](https://en.wikipedia.org/wiki/Geographic_coordinate_conversion). The accuracy can also be adapted.
|
||||
-- - A2G LL DDM: Lattitude Longitude [Decimal Degrees Minutes](https://en.wikipedia.org/wiki/Decimal_degrees). The accuracy can also be adapted.
|
||||
-- - 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: 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**
|
||||
--
|
||||
@@ -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.
|
||||
--
|
||||
-- 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.
|
||||
-- Note that **Update** messages can be chosen not to be displayed at all!
|
||||
--
|
||||
@@ -196,7 +195,7 @@
|
||||
--
|
||||
-- ## 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:
|
||||
--
|
||||
-- - **WWII** era: Use for warfare with equipment during the world war II time.
|
||||
@@ -213,8 +212,8 @@
|
||||
SETTINGS = {
|
||||
ClassName = "SETTINGS",
|
||||
ShowPlayerMenu = true,
|
||||
MenuShort = false,
|
||||
MenuStatic = false,
|
||||
MenuShort = false,
|
||||
MenuStatic = false,
|
||||
}
|
||||
|
||||
SETTINGS.__Enum = {}
|
||||
@@ -231,7 +230,6 @@ SETTINGS.__Enum.Era = {
|
||||
Modern = 4,
|
||||
}
|
||||
|
||||
|
||||
do -- SETTINGS
|
||||
|
||||
--- SETTINGS constructor.
|
||||
@@ -268,14 +266,14 @@ do -- SETTINGS
|
||||
-- Short text are better suited for, e.g., VR.
|
||||
-- @param #SETTINGS self
|
||||
-- @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
|
||||
end
|
||||
|
||||
--- Set menu to be static.
|
||||
-- @param #SETTINGS self
|
||||
-- @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
|
||||
end
|
||||
|
||||
@@ -289,7 +287,7 @@ do -- SETTINGS
|
||||
-- @param #SETTINGS self
|
||||
-- @return #boolean true if metric.
|
||||
function SETTINGS:IsMetric()
|
||||
return ( self.Metric ~= nil and self.Metric == true ) or ( self.Metric == nil and _SETTINGS:IsMetric() )
|
||||
return (self.Metric ~= nil and self.Metric == true) or (self.Metric == nil and _SETTINGS:IsMetric())
|
||||
end
|
||||
|
||||
--- Sets the SETTINGS imperial.
|
||||
@@ -302,7 +300,7 @@ do -- SETTINGS
|
||||
-- @param #SETTINGS self
|
||||
-- @return #boolean true if imperial.
|
||||
function SETTINGS:IsImperial()
|
||||
return ( self.Metric ~= nil and self.Metric == false ) or ( self.Metric == nil and _SETTINGS:IsMetric() )
|
||||
return (self.Metric ~= nil and self.Metric == false) or (self.Metric == nil and _SETTINGS:IsMetric())
|
||||
end
|
||||
|
||||
--- Sets the SETTINGS LL accuracy.
|
||||
@@ -344,13 +342,12 @@ do -- SETTINGS
|
||||
self.MessageTypeTimings[MessageType] = MessageTime
|
||||
end
|
||||
|
||||
|
||||
--- Gets the SETTINGS Message Display Timing of a MessageType
|
||||
-- @param #SETTINGS self
|
||||
-- @param Core.Message#MESSAGE MessageType The type of the message.
|
||||
-- @return #number
|
||||
function SETTINGS:GetMessageTime( MessageType )
|
||||
return ( self.MessageTypeTimings and self.MessageTypeTimings[MessageType] ) or _SETTINGS:GetMessageTime( MessageType )
|
||||
return (self.MessageTypeTimings and self.MessageTypeTimings[MessageType]) or _SETTINGS:GetMessageTime( MessageType )
|
||||
end
|
||||
|
||||
--- Sets A2G LL DMS
|
||||
@@ -371,14 +368,14 @@ do -- SETTINGS
|
||||
-- @param #SETTINGS self
|
||||
-- @return #boolean true if LL DMS
|
||||
function SETTINGS:IsA2G_LL_DMS()
|
||||
return ( self.A2GSystem and self.A2GSystem == "LL DMS" ) or ( not self.A2GSystem and _SETTINGS:IsA2G_LL_DMS() )
|
||||
return (self.A2GSystem and self.A2GSystem == "LL DMS") or (not self.A2GSystem and _SETTINGS:IsA2G_LL_DMS())
|
||||
end
|
||||
|
||||
--- Is LL DDM
|
||||
-- @param #SETTINGS self
|
||||
-- @return #boolean true if LL DDM
|
||||
function SETTINGS:IsA2G_LL_DDM()
|
||||
return ( self.A2GSystem and self.A2GSystem == "LL DDM" ) or ( not self.A2GSystem and _SETTINGS:IsA2G_LL_DDM() )
|
||||
return (self.A2GSystem and self.A2GSystem == "LL DDM") or (not self.A2GSystem and _SETTINGS:IsA2G_LL_DDM())
|
||||
end
|
||||
|
||||
--- Sets A2G MGRS
|
||||
@@ -392,7 +389,7 @@ do -- SETTINGS
|
||||
-- @param #SETTINGS self
|
||||
-- @return #boolean true if MGRS
|
||||
function SETTINGS:IsA2G_MGRS()
|
||||
return ( self.A2GSystem and self.A2GSystem == "MGRS" ) or ( not self.A2GSystem and _SETTINGS:IsA2G_MGRS() )
|
||||
return (self.A2GSystem and self.A2GSystem == "MGRS") or (not self.A2GSystem and _SETTINGS:IsA2G_MGRS())
|
||||
end
|
||||
|
||||
--- Sets A2G BRA
|
||||
@@ -406,7 +403,7 @@ do -- SETTINGS
|
||||
-- @param #SETTINGS self
|
||||
-- @return #boolean true if BRA
|
||||
function SETTINGS:IsA2G_BR()
|
||||
return ( self.A2GSystem and self.A2GSystem == "BR" ) or ( not self.A2GSystem and _SETTINGS:IsA2G_BR() )
|
||||
return (self.A2GSystem and self.A2GSystem == "BR") or (not self.A2GSystem and _SETTINGS:IsA2G_BR())
|
||||
end
|
||||
|
||||
--- Sets A2A BRA
|
||||
@@ -420,7 +417,7 @@ do -- SETTINGS
|
||||
-- @param #SETTINGS self
|
||||
-- @return #boolean true if BRA
|
||||
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
|
||||
|
||||
--- Sets A2A BULLS
|
||||
@@ -434,7 +431,7 @@ do -- SETTINGS
|
||||
-- @param #SETTINGS self
|
||||
-- @return #boolean true if BULLS
|
||||
function SETTINGS:IsA2A_BULLS()
|
||||
return ( self.A2ASystem and self.A2ASystem == "BULLS" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_BULLS() )
|
||||
return (self.A2ASystem and self.A2ASystem == "BULLS") or (not self.A2ASystem and _SETTINGS:IsA2A_BULLS())
|
||||
end
|
||||
|
||||
--- Sets A2A LL DMS
|
||||
@@ -455,14 +452,14 @@ do -- SETTINGS
|
||||
-- @param #SETTINGS self
|
||||
-- @return #boolean true if LL DMS
|
||||
function SETTINGS:IsA2A_LL_DMS()
|
||||
return ( self.A2ASystem and self.A2ASystem == "LL DMS" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_LL_DMS() )
|
||||
return (self.A2ASystem and self.A2ASystem == "LL DMS") or (not self.A2ASystem and _SETTINGS:IsA2A_LL_DMS())
|
||||
end
|
||||
|
||||
--- Is LL DDM
|
||||
-- @param #SETTINGS self
|
||||
-- @return #boolean true if LL DDM
|
||||
function SETTINGS:IsA2A_LL_DDM()
|
||||
return ( self.A2ASystem and self.A2ASystem == "LL DDM" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_LL_DDM() )
|
||||
return (self.A2ASystem and self.A2ASystem == "LL DDM") or (not self.A2ASystem and _SETTINGS:IsA2A_LL_DDM())
|
||||
end
|
||||
|
||||
--- Sets A2A MGRS
|
||||
@@ -476,7 +473,7 @@ do -- SETTINGS
|
||||
-- @param #SETTINGS self
|
||||
-- @return #boolean true if MGRS
|
||||
function SETTINGS:IsA2A_MGRS()
|
||||
return ( self.A2ASystem and self.A2ASystem == "MGRS" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_MGRS() )
|
||||
return (self.A2ASystem and self.A2ASystem == "MGRS") or (not self.A2ASystem and _SETTINGS:IsA2A_MGRS())
|
||||
end
|
||||
|
||||
--- @param #SETTINGS self
|
||||
@@ -495,37 +492,37 @@ do -- SETTINGS
|
||||
-- A2G Coordinate System
|
||||
-------
|
||||
|
||||
local text="A2G Coordinate System"
|
||||
local text = "A2G Coordinate System"
|
||||
if _SETTINGS.MenuShort then
|
||||
text="A2G Coordinates"
|
||||
text = "A2G Coordinates"
|
||||
end
|
||||
local A2GCoordinateMenu = MENU_GROUP:New( MenuGroup, text, SettingsMenu ):SetTime( MenuTime )
|
||||
|
||||
-- Set LL DMS
|
||||
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
|
||||
text="LL DMS"
|
||||
text = "LL DMS"
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, text, A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "LL DMS" ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
-- Set LL DDM
|
||||
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
|
||||
text="LL DDM"
|
||||
text = "LL DDM"
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "Lat/Lon Degree Dec Min (LL DDM)", A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "LL DDM" ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
-- Set LL DMS accuracy.
|
||||
if self:IsA2G_LL_DDM() then
|
||||
local text1="LL DDM Accuracy 1"
|
||||
local text2="LL DDM Accuracy 2"
|
||||
local text3="LL DDM Accuracy 3"
|
||||
local text1 = "LL DDM Accuracy 1"
|
||||
local text2 = "LL DDM Accuracy 2"
|
||||
local text3 = "LL DDM Accuracy 3"
|
||||
if _SETTINGS.MenuShort then
|
||||
text1="LL DDM"
|
||||
text1 = "LL DDM"
|
||||
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 2", A2GCoordinateMenu, self.MenuLL_DDM_Accuracy, self, MenuGroup, RootMenu, 2 ):SetTime( MenuTime )
|
||||
@@ -534,18 +531,18 @@ do -- SETTINGS
|
||||
|
||||
-- Set BR.
|
||||
if not self:IsA2G_BR() then
|
||||
local text="Bearing, Range (BR)"
|
||||
local text = "Bearing, Range (BR)"
|
||||
if _SETTINGS.MenuShort then
|
||||
text="BR"
|
||||
text = "BR"
|
||||
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
|
||||
|
||||
-- Set MGRS.
|
||||
if not self:IsA2G_MGRS() then
|
||||
local text="Military Grid (MGRS)"
|
||||
local text = "Military Grid (MGRS)"
|
||||
if _SETTINGS.MenuShort then
|
||||
text="MGRS"
|
||||
text = "MGRS"
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, text, A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "MGRS" ):SetTime( MenuTime )
|
||||
end
|
||||
@@ -563,24 +560,24 @@ do -- SETTINGS
|
||||
-- A2A Coordinate System
|
||||
-------
|
||||
|
||||
local text="A2A Coordinate System"
|
||||
local text = "A2A Coordinate System"
|
||||
if _SETTINGS.MenuShort then
|
||||
text="A2A Coordinates"
|
||||
text = "A2A Coordinates"
|
||||
end
|
||||
local A2ACoordinateMenu = MENU_GROUP:New( MenuGroup, text, SettingsMenu ):SetTime( MenuTime )
|
||||
|
||||
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
|
||||
text="LL DMS"
|
||||
text = "LL DMS"
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "LL DMS" ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
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
|
||||
text="LL DDM"
|
||||
text = "LL DDM"
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "LL DDM" ):SetTime( MenuTime )
|
||||
end
|
||||
@@ -593,25 +590,25 @@ do -- SETTINGS
|
||||
end
|
||||
|
||||
if not self:IsA2A_BULLS() then
|
||||
local text="Bullseye (BULLS)"
|
||||
local text = "Bullseye (BULLS)"
|
||||
if _SETTINGS.MenuShort then
|
||||
text="Bulls"
|
||||
text = "Bulls"
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "BULLS" ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
if not self:IsA2A_BRAA() then
|
||||
local text="Bearing Range Altitude Aspect (BRAA)"
|
||||
local text = "Bearing Range Altitude Aspect (BRAA)"
|
||||
if _SETTINGS.MenuShort then
|
||||
text="BRAA"
|
||||
text = "BRAA"
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "BRAA" ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
if not self:IsA2A_MGRS() then
|
||||
local text="Military Grid (MGRS)"
|
||||
local text = "Military Grid (MGRS)"
|
||||
if _SETTINGS.MenuShort then
|
||||
text="MGRS"
|
||||
text = "MGRS"
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "MGRS" ):SetTime( MenuTime )
|
||||
end
|
||||
@@ -624,31 +621,31 @@ do -- SETTINGS
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 5", A2ACoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 5 ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
local text="Measures and Weights System"
|
||||
local text = "Measures and Weights System"
|
||||
if _SETTINGS.MenuShort then
|
||||
text="Unit System"
|
||||
text = "Unit System"
|
||||
end
|
||||
local MetricsMenu = MENU_GROUP:New( MenuGroup, text, SettingsMenu ):SetTime( MenuTime )
|
||||
|
||||
if self:IsMetric() then
|
||||
local text="Imperial (Miles,Feet)"
|
||||
local text = "Imperial (Miles,Feet)"
|
||||
if _SETTINGS.MenuShort then
|
||||
text="Imperial"
|
||||
text = "Imperial"
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, text, MetricsMenu, self.MenuMWSystem, self, MenuGroup, RootMenu, false ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
if self:IsImperial() then
|
||||
local text="Metric (Kilometers,Meters)"
|
||||
local text = "Metric (Kilometers,Meters)"
|
||||
if _SETTINGS.MenuShort then
|
||||
text="Metric"
|
||||
text = "Metric"
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, text, MetricsMenu, self.MenuMWSystem, self, MenuGroup, RootMenu, true ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
local text="Messages and Reports"
|
||||
local text = "Messages and Reports"
|
||||
if _SETTINGS.MenuShort then
|
||||
text="Messages & Reports"
|
||||
text = "Messages & Reports"
|
||||
end
|
||||
local MessagesMenu = MENU_GROUP:New( MenuGroup, text, SettingsMenu ):SetTime( MenuTime )
|
||||
|
||||
@@ -689,7 +686,6 @@ do -- SETTINGS
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "2 minutes", DetailedReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.DetailedReportsMenu, 120 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "3 minutes", DetailedReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.DetailedReportsMenu, 180 ):SetTime( MenuTime )
|
||||
|
||||
|
||||
SettingsMenu:Remove( MenuTime )
|
||||
|
||||
return self
|
||||
@@ -733,11 +729,11 @@ do -- SETTINGS
|
||||
|
||||
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 )
|
||||
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 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 )
|
||||
@@ -754,40 +750,40 @@ do -- SETTINGS
|
||||
-- A2G Coordinate System
|
||||
------
|
||||
|
||||
local text="A2G Coordinate System"
|
||||
local text = "A2G Coordinate System"
|
||||
if _SETTINGS.MenuShort then
|
||||
text="A2G Coordinates"
|
||||
text = "A2G Coordinates"
|
||||
end
|
||||
local A2GCoordinateMenu = MENU_GROUP:New( PlayerGroup, text, PlayerMenu )
|
||||
|
||||
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
|
||||
text="A2G LL DMS"
|
||||
text = "A2G LL DMS"
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DMS" )
|
||||
end
|
||||
|
||||
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
|
||||
text="A2G LL DDM"
|
||||
text = "A2G LL DDM"
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DDM" )
|
||||
end
|
||||
|
||||
if not self:IsA2G_BR() or _SETTINGS.MenuStatic then
|
||||
local text="Bearing, Range (BR)"
|
||||
local text = "Bearing, Range (BR)"
|
||||
if _SETTINGS.MenuShort then
|
||||
text="A2G BR"
|
||||
text = "A2G BR"
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "BR" )
|
||||
end
|
||||
|
||||
if not self:IsA2G_MGRS() or _SETTINGS.MenuStatic then
|
||||
local text="Military Grid (MGRS)"
|
||||
local text = "Military Grid (MGRS)"
|
||||
if _SETTINGS.MenuShort then
|
||||
text="A2G MGRS"
|
||||
text = "A2G MGRS"
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "MGRS" )
|
||||
end
|
||||
@@ -796,49 +792,48 @@ do -- SETTINGS
|
||||
-- A2A Coordinates Menu
|
||||
------
|
||||
|
||||
local text="A2A Coordinate System"
|
||||
local text = "A2A Coordinate System"
|
||||
if _SETTINGS.MenuShort then
|
||||
text="A2A Coordinates"
|
||||
text = "A2A Coordinates"
|
||||
end
|
||||
local A2ACoordinateMenu = MENU_GROUP:New( PlayerGroup, text, PlayerMenu )
|
||||
|
||||
|
||||
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
|
||||
text="A2A LL DMS"
|
||||
text = "A2A LL DMS"
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DMS" )
|
||||
end
|
||||
|
||||
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
|
||||
text="A2A LL DDM"
|
||||
text = "A2A LL DDM"
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DDM" )
|
||||
end
|
||||
|
||||
if not self:IsA2A_BULLS() or _SETTINGS.MenuStatic then
|
||||
local text="Bullseye (BULLS)"
|
||||
local text = "Bullseye (BULLS)"
|
||||
if _SETTINGS.MenuShort then
|
||||
text="A2A BULLS"
|
||||
text = "A2A BULLS"
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "BULLS" )
|
||||
end
|
||||
|
||||
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
|
||||
text="A2A BRAA"
|
||||
text = "A2A BRAA"
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "BRAA" )
|
||||
end
|
||||
|
||||
if not self:IsA2A_MGRS() or _SETTINGS.MenuStatic then
|
||||
local text="Military Grid (MGRS)"
|
||||
local text = "Military Grid (MGRS)"
|
||||
if _SETTINGS.MenuShort then
|
||||
text="A2A MGRS"
|
||||
text = "A2A MGRS"
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "MGRS" )
|
||||
end
|
||||
@@ -847,24 +842,24 @@ do -- SETTINGS
|
||||
-- Unit system
|
||||
---
|
||||
|
||||
local text="Measures and Weights System"
|
||||
local text = "Measures and Weights System"
|
||||
if _SETTINGS.MenuShort then
|
||||
text="Unit System"
|
||||
text = "Unit System"
|
||||
end
|
||||
local MetricsMenu = MENU_GROUP:New( PlayerGroup, text, PlayerMenu )
|
||||
|
||||
if self:IsMetric() or _SETTINGS.MenuStatic then
|
||||
local text="Imperial (Miles,Feet)"
|
||||
local text = "Imperial (Miles,Feet)"
|
||||
if _SETTINGS.MenuShort then
|
||||
text="Imperial"
|
||||
text = "Imperial"
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, MetricsMenu, self.MenuGroupMWSystem, self, PlayerUnit, PlayerGroup, PlayerName, false )
|
||||
end
|
||||
|
||||
if self:IsImperial() or _SETTINGS.MenuStatic then
|
||||
local text="Metric (Kilometers,Meters)"
|
||||
local text = "Metric (Kilometers,Meters)"
|
||||
if _SETTINGS.MenuShort then
|
||||
text="Metric"
|
||||
text = "Metric"
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, MetricsMenu, self.MenuGroupMWSystem, self, PlayerUnit, PlayerGroup, PlayerName, true )
|
||||
end
|
||||
@@ -873,9 +868,9 @@ do -- SETTINGS
|
||||
-- Messages and Reports
|
||||
---
|
||||
|
||||
local text="Messages and Reports"
|
||||
local text = "Messages and Reports"
|
||||
if _SETTINGS.MenuShort then
|
||||
text="Messages & Reports"
|
||||
text = "Messages & Reports"
|
||||
end
|
||||
local MessagesMenu = MENU_GROUP:New( PlayerGroup, text, PlayerMenu )
|
||||
|
||||
@@ -935,39 +930,38 @@ do -- SETTINGS
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- @param #SETTINGS self
|
||||
function SETTINGS:A2GMenuSystem( MenuGroup, RootMenu, A2GSystem )
|
||||
self.A2GSystem = A2GSystem
|
||||
MESSAGE:New( string.format("Settings: Default A2G coordinate system set to %s for all players!", A2GSystem ), 5 ):ToAll()
|
||||
MESSAGE:New( string.format( "Settings: Default A2G coordinate system set to %s for all players!", A2GSystem ), 5 ):ToAll()
|
||||
self:SetSystemMenu( MenuGroup, RootMenu )
|
||||
end
|
||||
|
||||
--- @param #SETTINGS self
|
||||
function SETTINGS:A2AMenuSystem( MenuGroup, RootMenu, A2ASystem )
|
||||
self.A2ASystem = A2ASystem
|
||||
MESSAGE:New( string.format("Settings: Default A2A coordinate system set to %s for all players!", A2ASystem ), 5 ):ToAll()
|
||||
MESSAGE:New( string.format( "Settings: Default A2A coordinate system set to %s for all players!", A2ASystem ), 5 ):ToAll()
|
||||
self:SetSystemMenu( MenuGroup, RootMenu )
|
||||
end
|
||||
|
||||
--- @param #SETTINGS self
|
||||
function SETTINGS:MenuLL_DDM_Accuracy( MenuGroup, RootMenu, LL_Accuracy )
|
||||
self.LL_Accuracy = LL_Accuracy
|
||||
MESSAGE:New( string.format("Settings: Default LL accuracy set to %s for all players!", LL_Accuracy ), 5 ):ToAll()
|
||||
MESSAGE:New( string.format( "Settings: Default LL accuracy set to %s for all players!", LL_Accuracy ), 5 ):ToAll()
|
||||
self:SetSystemMenu( MenuGroup, RootMenu )
|
||||
end
|
||||
|
||||
--- @param #SETTINGS self
|
||||
function SETTINGS:MenuMGRS_Accuracy( MenuGroup, RootMenu, MGRS_Accuracy )
|
||||
self.MGRS_Accuracy = MGRS_Accuracy
|
||||
MESSAGE:New( string.format("Settings: Default MGRS accuracy set to %s for all players!", MGRS_Accuracy ), 5 ):ToAll()
|
||||
MESSAGE:New( string.format( "Settings: Default MGRS accuracy set to %s for all players!", MGRS_Accuracy ), 5 ):ToAll()
|
||||
self:SetSystemMenu( MenuGroup, RootMenu )
|
||||
end
|
||||
|
||||
--- @param #SETTINGS self
|
||||
function SETTINGS:MenuMWSystem( MenuGroup, RootMenu, MW )
|
||||
self.Metric = MW
|
||||
MESSAGE:New( string.format("Settings: Default measurement format set to %s for all players!", MW and "Metric" or "Imperial" ), 5 ):ToAll()
|
||||
MESSAGE:New( string.format( "Settings: Default measurement format set to %s for all players!", MW and "Metric" or "Imperial" ), 5 ):ToAll()
|
||||
self:SetSystemMenu( MenuGroup, RootMenu )
|
||||
end
|
||||
|
||||
@@ -980,12 +974,12 @@ do -- SETTINGS
|
||||
do
|
||||
--- @param #SETTINGS self
|
||||
function SETTINGS:MenuGroupA2GSystem( PlayerUnit, PlayerGroup, PlayerName, A2GSystem )
|
||||
BASE:E( {self, PlayerUnit:GetName(), A2GSystem} )
|
||||
BASE:E( { self, PlayerUnit:GetName(), A2GSystem } )
|
||||
self.A2GSystem = A2GSystem
|
||||
MESSAGE:New( string.format( "Settings: A2G format set to %s for player %s.", A2GSystem, PlayerName ), 5 ):ToGroup( PlayerGroup )
|
||||
if _SETTINGS.MenuStatic==false then
|
||||
self:RemovePlayerMenu(PlayerUnit)
|
||||
self:SetPlayerMenu(PlayerUnit)
|
||||
if _SETTINGS.MenuStatic == false then
|
||||
self:RemovePlayerMenu( PlayerUnit )
|
||||
self:SetPlayerMenu( PlayerUnit )
|
||||
end
|
||||
end
|
||||
|
||||
@@ -993,9 +987,9 @@ do -- SETTINGS
|
||||
function SETTINGS:MenuGroupA2ASystem( PlayerUnit, PlayerGroup, PlayerName, A2ASystem )
|
||||
self.A2ASystem = A2ASystem
|
||||
MESSAGE:New( string.format( "Settings: A2A format set to %s for player %s.", A2ASystem, PlayerName ), 5 ):ToGroup( PlayerGroup )
|
||||
if _SETTINGS.MenuStatic==false then
|
||||
self:RemovePlayerMenu(PlayerUnit)
|
||||
self:SetPlayerMenu(PlayerUnit)
|
||||
if _SETTINGS.MenuStatic == false then
|
||||
self:RemovePlayerMenu( PlayerUnit )
|
||||
self:SetPlayerMenu( PlayerUnit )
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1003,9 +997,9 @@ do -- SETTINGS
|
||||
function SETTINGS:MenuGroupLL_DDM_AccuracySystem( PlayerUnit, PlayerGroup, PlayerName, 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 )
|
||||
if _SETTINGS.MenuStatic==false then
|
||||
self:RemovePlayerMenu(PlayerUnit)
|
||||
self:SetPlayerMenu(PlayerUnit)
|
||||
if _SETTINGS.MenuStatic == false then
|
||||
self:RemovePlayerMenu( PlayerUnit )
|
||||
self:SetPlayerMenu( PlayerUnit )
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1013,9 +1007,9 @@ do -- SETTINGS
|
||||
function SETTINGS:MenuGroupMGRS_AccuracySystem( PlayerUnit, PlayerGroup, PlayerName, 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 )
|
||||
if _SETTINGS.MenuStatic==false then
|
||||
self:RemovePlayerMenu(PlayerUnit)
|
||||
self:SetPlayerMenu(PlayerUnit)
|
||||
if _SETTINGS.MenuStatic == false then
|
||||
self:RemovePlayerMenu( PlayerUnit )
|
||||
self:SetPlayerMenu( PlayerUnit )
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1023,9 +1017,9 @@ do -- SETTINGS
|
||||
function SETTINGS:MenuGroupMWSystem( PlayerUnit, PlayerGroup, PlayerName, MW )
|
||||
self.Metric = MW
|
||||
MESSAGE:New( string.format( "Settings: Measurement format set to %s for player %s.", MW and "Metric" or "Imperial", PlayerName ), 5 ):ToGroup( PlayerGroup )
|
||||
if _SETTINGS.MenuStatic==false then
|
||||
self:RemovePlayerMenu(PlayerUnit)
|
||||
self:SetPlayerMenu(PlayerUnit)
|
||||
if _SETTINGS.MenuStatic == false then
|
||||
self:RemovePlayerMenu( PlayerUnit )
|
||||
self:SetPlayerMenu( PlayerUnit )
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1055,7 +1049,6 @@ do -- SETTINGS
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Configures the era of the mission to be Cold war.
|
||||
-- @param #SETTINGS self
|
||||
-- @return #SETTINGS self
|
||||
@@ -1065,7 +1058,6 @@ do -- SETTINGS
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Configures the era of the mission to be Modern war.
|
||||
-- @param #SETTINGS self
|
||||
-- @return #SETTINGS self
|
||||
@@ -1075,7 +1067,4 @@ do -- SETTINGS
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -138,24 +138,24 @@ SPAWNSTATIC = {
|
||||
-- @return #SPAWNSTATIC self
|
||||
function SPAWNSTATIC:NewFromStatic(SpawnTemplateName, SpawnCountryID)
|
||||
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC
|
||||
|
||||
local TemplateStatic, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate(SpawnTemplateName)
|
||||
|
||||
if TemplateStatic then
|
||||
self.SpawnTemplatePrefix = SpawnTemplateName
|
||||
self.TemplateStaticUnit = UTILS.DeepCopy(TemplateStatic.units[1])
|
||||
self.CountryID = SpawnCountryID or CountryID
|
||||
self.CategoryID = CategoryID
|
||||
self.CoalitionID = CoalitionID
|
||||
self.SpawnIndex = 0
|
||||
else
|
||||
error( "SPAWNSTATIC:New: There is no static declared in the mission editor with SpawnTemplatePrefix = '" .. tostring(SpawnTemplateName) .. "'" )
|
||||
end
|
||||
local TemplateStatic, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate(SpawnTemplateName)
|
||||
|
||||
if TemplateStatic then
|
||||
self.SpawnTemplatePrefix = SpawnTemplateName
|
||||
self.TemplateStaticUnit = UTILS.DeepCopy(TemplateStatic.units[1])
|
||||
self.CountryID = SpawnCountryID or CountryID
|
||||
self.CategoryID = CategoryID
|
||||
self.CoalitionID = CoalitionID
|
||||
self.SpawnIndex = 0
|
||||
else
|
||||
error( "SPAWNSTATIC:New: There is no static declared in the mission editor with SpawnTemplatePrefix = '" .. tostring(SpawnTemplateName) .. "'" )
|
||||
end
|
||||
|
||||
self:SetEventPriority( 5 )
|
||||
|
||||
return self
|
||||
return self
|
||||
end
|
||||
|
||||
--- Creates the main object to spawn a @{Static} given a template table.
|
||||
@@ -422,7 +422,11 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
|
||||
end
|
||||
|
||||
if self.InitCargo~=nil then
|
||||
Template.isCargo=self.InitCargo
|
||||
Template.canCargo=self.InitCargo
|
||||
end
|
||||
|
||||
if self.InitCargoMass~=nil then
|
||||
Template.mass=self.InitCargoMass
|
||||
end
|
||||
|
||||
if self.InitLinkUnit then
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1053,7 +1053,7 @@ end
|
||||
-- * `AIRBASE.Nevada.Lincoln_County`
|
||||
-- * `AIRBASE.Nevada.McCarran_International_Airport`
|
||||
-- * `AIRBASE.Nevada.Mesquite`
|
||||
-- * `AIRBASE.Nevada.Mina_Airport_3Q0`
|
||||
-- * `AIRBASE.Nevada.Mina_Airport`
|
||||
-- * `AIRBASE.Nevada.Nellis_AFB`
|
||||
-- * `AIRBASE.Nevada.North_Las_Vegas`
|
||||
-- * `AIRBASE.Nevada.Pahute_Mesa_Airstrip`
|
||||
@@ -1288,7 +1288,7 @@ ATC_GROUND_NEVADA = {
|
||||
},
|
||||
},
|
||||
},
|
||||
[AIRBASE.Nevada.Mina_Airport_3Q0] = {
|
||||
[AIRBASE.Nevada.Mina_Airport] = {
|
||||
PointsRunways = {
|
||||
[1] = {
|
||||
[1] = {["y"]=-290054.57371429,["x"]=-160930.02228572,},
|
||||
|
||||
@@ -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 #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 #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 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.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -23,7 +23,6 @@
|
||||
-- @module Functional.FOX
|
||||
-- @image Functional_FOX.png
|
||||
|
||||
|
||||
--- FOX class.
|
||||
-- @type FOX
|
||||
-- @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 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 dt00 Time step [sec] for missile position updates if distance to target < 1 km. Default 0.01 sec.
|
||||
-- @field #boolean
|
||||
-- @field #number dt00 Time step [sec] for missile position updates if distance to target < 1 km. Default 0.01 sec.
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
--- Fox 3!
|
||||
@@ -1707,8 +1705,8 @@ end
|
||||
--- Returns the unit of a player and the player name. If the unit does not belong to a player, nil is returned.
|
||||
-- @param #FOX self
|
||||
-- @param DCS#Weapon weapon The weapon.
|
||||
-- @return #number Notching heading right, i.e. missile heading +90<EFBFBD>
|
||||
-- @return #number Notching heading left, i.e. missile heading -90<EFBFBD>.
|
||||
-- @return #number Notching heading right, i.e. missile heading +90°.
|
||||
-- @return #number Notching heading left, i.e. missile heading -90°.
|
||||
function FOX:_GetNotchingHeadings(weapon)
|
||||
|
||||
if weapon then
|
||||
@@ -1813,4 +1811,4 @@ end
|
||||
|
||||
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,7 @@
|
||||
-- ## Features:
|
||||
--
|
||||
-- * Track the missiles fired at you and other players, providing bearing and range information of the missiles towards the airplanes.
|
||||
-- * Provide alerts of missile launches, including detailed information of the units launching, including bearing, range <EFBFBD>
|
||||
-- * Provide alerts of missile launches, including detailed information of the units launching, including bearing, range °
|
||||
-- * Provide alerts when a missile would have killed your aircraft.
|
||||
-- * Provide alerts when the missile self destructs.
|
||||
-- * Enable / Disable and Configure the Missile Trainer using the various menu options.
|
||||
|
||||
@@ -742,7 +742,7 @@ function PSEUDOATC:ReportWeather(GID, UID, position, location)
|
||||
local T=position:GetTemperature()
|
||||
|
||||
-- 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
|
||||
_T=string.format('%d°C', T)
|
||||
end
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
-- @field #string category Category of aircarft: "plane" or "heli".
|
||||
-- @field #number groupsize Number of aircraft in group.
|
||||
-- @field #string friendly Possible departure/destination airport: all=blue+red+neutral, same=spawn+neutral, spawnonly=spawn, blue=blue+neutral, blueonly=blue, red=red+neutral, redonly=red.
|
||||
-- @field #table ctable Table with the valid coalitons from choice self.friendly.
|
||||
-- @field #table ctable Table with the valid coalitions from choice self.friendly.
|
||||
-- @field #table aircraft Table which holds the basic aircraft properties (speed, range, ...).
|
||||
-- @field #number Vcruisemax Max cruise speed in m/s (250 m/s = 900 km/h = 486 kt) set by user.
|
||||
-- @field #number Vclimb Default climb rate in ft/min.
|
||||
@@ -348,7 +348,7 @@ RAT={
|
||||
category = nil, -- Category of aircarft: "plane" or "heli".
|
||||
groupsize=nil, -- Number of aircraft in the group.
|
||||
friendly = "same", -- Possible departure/destination airport: same=spawn+neutral, spawnonly=spawn, blue=blue+neutral, blueonly=blue, red=red+neutral, redonly=red, neutral.
|
||||
ctable = {}, -- Table with the valid coalitons from choice self.friendly.
|
||||
ctable = {}, -- Table with the valid coalitions from choice self.friendly.
|
||||
aircraft = {}, -- Table which holds the basic aircraft properties (speed, range, ...).
|
||||
Vcruisemax=nil, -- Max cruise speed in set by user.
|
||||
Vclimb=1500, -- Default climb rate in ft/min.
|
||||
@@ -657,7 +657,7 @@ end
|
||||
-- @param #RAT self
|
||||
-- @param #number naircraft (Optional) Number of aircraft to spawn. Default is one aircraft.
|
||||
-- @return #boolean True if spawning was successful or nil if nothing was spawned.
|
||||
-- @usage yak:Spawn(5) will spawn five aircraft. By default aircraft will spawn at neutral and red airports if the template group is part of the red coaliton.
|
||||
-- @usage yak:Spawn(5) will spawn five aircraft. By default aircraft will spawn at neutral and red airports if the template group is part of the red coalition.
|
||||
function RAT:Spawn(naircraft)
|
||||
|
||||
-- Make sure that this function is only been called once per RAT object.
|
||||
@@ -1289,7 +1289,7 @@ end
|
||||
|
||||
--- Include all airports which lie in a zone as possible destinations.
|
||||
-- @param #RAT self
|
||||
-- @param Core.Zone#ZONE zone Zone in which the departure airports lie. Has to be a MOOSE zone.
|
||||
-- @param Core.Zone#ZONE zone Zone in which the destination airports lie. Has to be a MOOSE zone.
|
||||
-- @return #RAT RAT self object.
|
||||
function RAT:SetDestinationsFromZone(zone)
|
||||
self:F2(zone)
|
||||
@@ -1305,7 +1305,7 @@ end
|
||||
|
||||
--- Include all airports which lie in a zone as possible destinations.
|
||||
-- @param #RAT self
|
||||
-- @param Core.Zone#ZONE zone Zone in which the destination airports lie. Has to be a MOOSE zone.
|
||||
-- @param Core.Zone#ZONE zone Zone in which the departure airports lie. Has to be a MOOSE zone.
|
||||
-- @return #RAT RAT self object.
|
||||
function RAT:SetDeparturesFromZone(zone)
|
||||
self:F2(zone)
|
||||
@@ -5371,7 +5371,7 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take
|
||||
|
||||
if spawnonground then
|
||||
|
||||
-- Sh<EFBFBD>ps and FARPS seem to have a build in queue.
|
||||
-- Sh°ps and FARPS seem to have a build in queue.
|
||||
if spawnonship or spawnonfarp or spawnonrunway or automatic then
|
||||
self:T(RAT.id..string.format("RAT group %s spawning at farp, ship or runway %s.", self.alias, departure:GetName()))
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,54 +1,65 @@
|
||||
--- **Functional** -- Make SAM sites execute evasive and defensive behaviour when being fired upon.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
--
|
||||
-- * When SAM sites are being fired upon, the SAMs will take evasive action will reposition themselves when possible.
|
||||
-- * When SAM sites are being fired upon, the SAMs will take defensive action by shutting down their radars.
|
||||
--
|
||||
-- * 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:
|
||||
--
|
||||
--
|
||||
-- [SEV - SEAD Evasion](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SEV%20-%20SEAD%20Evasion)
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ### Authors: **FlightControl**, **applevangelist**
|
||||
--
|
||||
-- Last Update: July 2021
|
||||
--
|
||||
--
|
||||
-- Last Update: Feb 2022
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- @module Functional.Sead
|
||||
-- @image SEAD.JPG
|
||||
|
||||
--- @type SEAD
|
||||
---
|
||||
-- @type SEAD
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
--- Make SAM sites execute evasive and defensive behaviour when being fired upon.
|
||||
--
|
||||
--
|
||||
-- This class is very easy to use. Just setup a SEAD object by using @{#SEAD.New}() and SAMs will evade and take defensive action when being fired upon.
|
||||
-- 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:
|
||||
--
|
||||
--
|
||||
-- Use the @{#SEAD.New}() constructor to create a new SEAD object.
|
||||
--
|
||||
--
|
||||
-- SEAD_RU_SAM_Defenses = SEAD:New( { 'RU SA-6 Kub', 'RU SA-6 Defenses', 'RU MI-26 Troops', 'RU Attack Gori' } )
|
||||
--
|
||||
--
|
||||
-- @field #SEAD
|
||||
SEAD = {
|
||||
ClassName = "SEAD",
|
||||
ClassName = "SEAD",
|
||||
TargetSkill = {
|
||||
Average = { Evade = 30, DelayOn = { 40, 60 } } ,
|
||||
Good = { Evade = 20, DelayOn = { 30, 50 } } ,
|
||||
High = { Evade = 15, DelayOn = { 20, 40 } } ,
|
||||
Excellent = { Evade = 10, DelayOn = { 10, 30 } }
|
||||
},
|
||||
Excellent = { Evade = 10, DelayOn = { 10, 30 } }
|
||||
},
|
||||
SEADGroupPrefixes = {},
|
||||
SuppressedGroups = {},
|
||||
EngagementRange = 75 -- default 75% engagement range Feature Request #1355
|
||||
EngagementRange = 75, -- default 75% engagement range Feature Request #1355
|
||||
Padding = 10,
|
||||
CallBack = nil,
|
||||
UseCallBack = false,
|
||||
debug = false,
|
||||
}
|
||||
|
||||
--- Missile enumerators
|
||||
@@ -59,30 +70,52 @@ SEAD = {
|
||||
["AGM_122"] = "AGM_122",
|
||||
["AGM_84"] = "AGM_84",
|
||||
["AGM_45"] = "AGM_45",
|
||||
["ALARN"] = "ALARM",
|
||||
["ALARM"] = "ALARM",
|
||||
["LD-10"] = "LD-10",
|
||||
["X_58"] = "X_58",
|
||||
["X_28"] = "X_28",
|
||||
["X_25"] = "X_25",
|
||||
["X_31"] = "X_31",
|
||||
["Kh25"] = "Kh25",
|
||||
["BGM_109"] = "BGM_109",
|
||||
["AGM_154"] = "AGM_154",
|
||||
}
|
||||
|
||||
|
||||
--- Missile enumerators - from DCS ME and Wikipedia
|
||||
-- @field HarmData
|
||||
SEAD.HarmData = {
|
||||
-- km and mach
|
||||
["AGM_88"] = { 150, 3},
|
||||
["AGM_45"] = { 12, 2},
|
||||
["AGM_122"] = { 16.5, 2.3},
|
||||
["AGM_84"] = { 280, 0.8},
|
||||
["ALARM"] = { 45, 2},
|
||||
["LD-10"] = { 60, 4},
|
||||
["X_58"] = { 70, 4},
|
||||
["X_28"] = { 80, 2.5},
|
||||
["X_25"] = { 25, 0.76},
|
||||
["X_31"] = {150, 3},
|
||||
["Kh25"] = {25, 0.8},
|
||||
["BGM_109"] = {460, 0.705}, --in-game ~465kn
|
||||
["AGM_154"] = {130, 0.61},
|
||||
}
|
||||
|
||||
--- Creates the main object which is handling defensive actions for SA sites or moving SA vehicles.
|
||||
-- When an anti radiation missile is fired (KH-58, KH-31P, KH-31A, KH-25MPU, HARM missiles), the SA will shut down their radars and will take evasive actions...
|
||||
-- Chances are big that the missile will miss.
|
||||
-- @param #SEAD self
|
||||
-- @param table{string,...}|string SEADGroupPrefixes which is a table of Prefixes of the SA Groups in the DCS mission editor on which evasive actions need to be taken.
|
||||
-- @return SEAD
|
||||
-- @param #table SEADGroupPrefixes Table of #string entries or single #string, which is a table of Prefixes of the SA Groups in the DCS mission editor on which evasive actions need to be taken.
|
||||
-- @param #number Padding (Optional) Extra number of seconds to add to radar switch-back-on time
|
||||
-- @return #SEAD self
|
||||
-- @usage
|
||||
-- -- CCCP SEAD Defenses
|
||||
-- -- Defends the Russian SA installations from SEAD attacks.
|
||||
-- SEAD_RU_SAM_Defenses = SEAD:New( { 'RU SA-6 Kub', 'RU SA-6 Defenses', 'RU MI-26 Troops', 'RU Attack Gori' } )
|
||||
function SEAD:New( SEADGroupPrefixes )
|
||||
function SEAD:New( SEADGroupPrefixes, Padding )
|
||||
|
||||
local self = BASE:Inherit( self, FSM:New() )
|
||||
self:T( SEADGroupPrefixes )
|
||||
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
self:F( SEADGroupPrefixes )
|
||||
|
||||
if type( SEADGroupPrefixes ) == 'table' then
|
||||
for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do
|
||||
self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix
|
||||
@@ -90,20 +123,36 @@ function SEAD:New( SEADGroupPrefixes )
|
||||
else
|
||||
self.SEADGroupPrefixes[SEADGroupPrefixes] = SEADGroupPrefixes
|
||||
end
|
||||
|
||||
local padding = Padding or 10
|
||||
if padding < 10 then padding = 10 end
|
||||
self.Padding = padding
|
||||
self.UseEmissionsOnOff = true
|
||||
|
||||
self.debug = false
|
||||
|
||||
self.CallBack = nil
|
||||
self.UseCallBack = false
|
||||
|
||||
self:HandleEvent( EVENTS.Shot, self.HandleEventShot )
|
||||
self:I("*** SEAD - Started Version 0.2.8")
|
||||
|
||||
-- Start State.
|
||||
self:SetStartState("Running")
|
||||
self:AddTransition("*", "ManageEvasion", "*")
|
||||
self:AddTransition("*", "CalculateHitZone", "*")
|
||||
|
||||
self:I("*** SEAD - Started Version 0.4.3")
|
||||
return self
|
||||
end
|
||||
|
||||
--- Update the active SEAD Set
|
||||
--- Update the active SEAD Set (while running)
|
||||
-- @param #SEAD self
|
||||
-- @param #table SEADGroupPrefixes The prefixes to add, note: can also be a single #string
|
||||
-- @return #SEAD self
|
||||
function SEAD:UpdateSet( SEADGroupPrefixes )
|
||||
|
||||
self:F( SEADGroupPrefixes )
|
||||
|
||||
self:T( SEADGroupPrefixes )
|
||||
|
||||
if type( SEADGroupPrefixes ) == 'table' then
|
||||
for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do
|
||||
self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix
|
||||
@@ -117,10 +166,10 @@ end
|
||||
|
||||
--- Sets the engagement range of the SAMs. Defaults to 75% to make it more deadly. Feature Request #1355
|
||||
-- @param #SEAD self
|
||||
-- @param #number range Set the engagement range in percent, e.g. 50
|
||||
-- @return self
|
||||
-- @param #number range Set the engagement range in percent, e.g. 55 (default 75)
|
||||
-- @return #SEAD self
|
||||
function SEAD:SetEngagementRange(range)
|
||||
self:F( { range } )
|
||||
self:T( { range } )
|
||||
range = range or 75
|
||||
if range < 0 or range > 100 then
|
||||
range = 75
|
||||
@@ -130,100 +179,340 @@ function SEAD:SetEngagementRange(range)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Check if a known HARM was fired
|
||||
-- @param #SEAD self
|
||||
-- @param #string WeaponName
|
||||
-- @return #boolean Returns true for a match
|
||||
function SEAD:_CheckHarms(WeaponName)
|
||||
self:F( { WeaponName } )
|
||||
local hit = false
|
||||
for _,_name in pairs (SEAD.Harms) do
|
||||
if string.find(WeaponName,_name,1) then hit = true end
|
||||
--- Set the padding in seconds, which extends the radar off time calculated by SEAD
|
||||
-- @param #SEAD self
|
||||
-- @param #number Padding Extra number of seconds to add for the switch-on (default 10 seconds)
|
||||
-- @return #SEAD self
|
||||
function SEAD:SetPadding(Padding)
|
||||
self:T( { Padding } )
|
||||
local padding = Padding or 10
|
||||
if padding < 10 then padding = 10 end
|
||||
self.Padding = padding
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set SEAD to use emissions on/off in addition to alarm state.
|
||||
-- @param #SEAD self
|
||||
-- @param #boolean Switch True for on, false for off.
|
||||
-- @return #SEAD self
|
||||
function SEAD:SwitchEmissions(Switch)
|
||||
self:T({Switch})
|
||||
self.UseEmissionsOnOff = Switch
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add an object to call back when going evasive.
|
||||
-- @param #SEAD self
|
||||
-- @param #table Object The object to call. Needs to have object functions as follows:
|
||||
-- `:SeadSuppressionPlanned(Group, Name, SuppressionStartTime, SuppressionEndTime)`
|
||||
-- `:SeadSuppressionStart(Group, Name)`,
|
||||
-- `:SeadSuppressionEnd(Group, Name)`,
|
||||
-- @return #SEAD self
|
||||
function SEAD:AddCallBack(Object)
|
||||
self:T({Class=Object.ClassName})
|
||||
self.CallBack = Object
|
||||
self.UseCallBack = true
|
||||
return self
|
||||
end
|
||||
|
||||
--- (Internal) Check if a known HARM was fired
|
||||
-- @param #SEAD self
|
||||
-- @param #string WeaponName
|
||||
-- @return #boolean Returns true for a match
|
||||
-- @return #string name Name of hit in table
|
||||
function SEAD:_CheckHarms(WeaponName)
|
||||
self:T( { WeaponName } )
|
||||
local hit = false
|
||||
local name = ""
|
||||
for _,_name in pairs (SEAD.Harms) do
|
||||
if string.find(WeaponName,_name,1,true) then
|
||||
hit = true
|
||||
name = _name
|
||||
break
|
||||
end
|
||||
return hit
|
||||
end
|
||||
return hit, name
|
||||
end
|
||||
|
||||
--- (Internal) Return distance in meters between two coordinates or -1 on error.
|
||||
-- @param #SEAD self
|
||||
-- @param Core.Point#COORDINATE _point1 Coordinate one
|
||||
-- @param Core.Point#COORDINATE _point2 Coordinate two
|
||||
-- @return #number Distance in meters
|
||||
function SEAD:_GetDistance(_point1, _point2)
|
||||
self:T("_GetDistance")
|
||||
if _point1 and _point2 then
|
||||
local distance1 = _point1:Get2DDistance(_point2)
|
||||
local distance2 = _point1:DistanceFromPointVec2(_point2)
|
||||
--self:T({dist1=distance1, dist2=distance2})
|
||||
if distance1 and type(distance1) == "number" then
|
||||
return distance1
|
||||
elseif distance2 and type(distance2) == "number" then
|
||||
return distance2
|
||||
else
|
||||
self:E("*****Cannot calculate distance!")
|
||||
self:E({_point1,_point2})
|
||||
return -1
|
||||
end
|
||||
else
|
||||
self:E("******Cannot calculate distance!")
|
||||
self:E({_point1,_point2})
|
||||
return -1
|
||||
end
|
||||
end
|
||||
|
||||
--- Detects if an SAM site was shot with an anti radiation missile. In this case, take evasive actions based on the skill level set within the ME.
|
||||
-- @see SEAD
|
||||
-- @param #SEAD
|
||||
--- (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
|
||||
|
||||
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 = 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
|
||||
-- @return #SEAD self
|
||||
function SEAD:HandleEventShot( EventData )
|
||||
self:T( { EventData } )
|
||||
|
||||
self:T( { EventData.id } )
|
||||
local SEADPlane = EventData.IniUnit -- Wrapper.Unit#UNIT
|
||||
local SEADGroup = EventData.IniGroup -- Wrapper.Group#GROUP
|
||||
local SEADPlanePos = SEADPlane:GetCoordinate() -- Core.Point#COORDINATE
|
||||
local SEADUnit = EventData.IniDCSUnit
|
||||
local SEADUnitName = EventData.IniDCSUnitName
|
||||
local SEADWeapon = EventData.Weapon -- Identify the weapon fired
|
||||
local SEADWeaponName = EventData.WeaponName -- return weapon type
|
||||
|
||||
self:T( "*** SEAD - Missile Launched = " .. SEADWeaponName)
|
||||
self:T({ SEADWeapon })
|
||||
|
||||
--self:T({ SEADWeapon })
|
||||
|
||||
if self:_CheckHarms(SEADWeaponName) then
|
||||
self:T( '*** SEAD - Weapon Match' )
|
||||
local _targetskill = "Random"
|
||||
local _targetMimgroupName = "none"
|
||||
local _evade = math.random (1,100) -- random number for chance of evading action
|
||||
local _targetMim = EventData.Weapon:getTarget() -- Identify target
|
||||
local _targetUnit = UNIT:Find(_targetMim) -- Unit name by DCS Object
|
||||
if _targetUnit and _targetUnit:IsAlive() then
|
||||
local _targetMimgroup = _targetUnit:GetGroup()
|
||||
local _targetMimgroupName = _targetMimgroup:GetName() -- group name
|
||||
--local _targetskill = _DATABASE.Templates.Units[_targetUnit].Template.skill
|
||||
self:T( self.SEADGroupPrefixes )
|
||||
self:T( _targetMimgroupName )
|
||||
local _targetgroupname = "none"
|
||||
local _target = EventData.Weapon:getTarget() -- Identify target
|
||||
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
|
||||
self:T(string.format("*** Targetcat = %d",targetcat))
|
||||
if targetcat == Object.Category.UNIT then -- UNIT
|
||||
self:T("*** Target Category UNIT")
|
||||
_targetUnit = UNIT:Find(_target) -- Wrapper.Unit#UNIT
|
||||
if _targetUnit and _targetUnit:IsAlive() then
|
||||
_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
|
||||
-- see if we are shot at
|
||||
local SEADGroupFound = false
|
||||
for SEADGroupPrefixID, SEADGroupPrefix in pairs( self.SEADGroupPrefixes ) do
|
||||
if string.find( _targetMimgroupName, SEADGroupPrefix, 1, true ) then
|
||||
self:T("Target = ".. _targetgroupname .. " | Prefix = " .. SEADGroupPrefix )
|
||||
if string.find( _targetgroupname, SEADGroupPrefix,1,true ) then
|
||||
SEADGroupFound = true
|
||||
self:T( '*** SEAD - Group Found' )
|
||||
self:T( '*** SEAD - Group Match Found' )
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if SEADGroupFound == true then -- yes we are being attacked
|
||||
if _targetskill == "Random" then -- when skill is random, choose a skill
|
||||
local Skills = { "Average", "Good", "High", "Excellent" }
|
||||
_targetskill = Skills[ math.random(1,4) ]
|
||||
end
|
||||
self:T( _targetskill )
|
||||
if self.TargetSkill[_targetskill] then
|
||||
if (_evade > self.TargetSkill[_targetskill].Evade) then
|
||||
|
||||
self:T( string.format("*** SEAD - Evading, target skill " ..string.format(_targetskill)) )
|
||||
|
||||
local _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon))
|
||||
local _targetMimcont= _targetMimgroup:getController()
|
||||
|
||||
routines.groupRandomDistSelf(_targetMimgroup,300,'Diamond',250,20) -- move randomly
|
||||
|
||||
--tracker ID table to switch groups off and on again
|
||||
local id = {
|
||||
groupName = _targetMimgroup,
|
||||
ctrl = _targetMimcont
|
||||
}
|
||||
|
||||
local function SuppressionEnd(id) --switch group back on
|
||||
local range = self.EngagementRange -- Feature Request #1355
|
||||
self:T(string.format("*** SEAD - Engagement Range is %d", range))
|
||||
id.ctrl:setOption(AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.RED)
|
||||
--id.groupName:enableEmission(true)
|
||||
id.ctrl:setOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION,range) --Feature Request #1355
|
||||
self.SuppressedGroups[id.groupName] = nil --delete group id from table when done
|
||||
end
|
||||
-- randomize switch-on time
|
||||
local delay = math.random(self.TargetSkill[_targetskill].DelayOn[1], self.TargetSkill[_targetskill].DelayOn[2])
|
||||
local SuppressionEndTime = timer.getTime() + delay
|
||||
--create entry
|
||||
if self.SuppressedGroups[id.groupName] == nil then --no timer entry for this group yet
|
||||
self.SuppressedGroups[id.groupName] = {
|
||||
SuppressionEndTime = delay
|
||||
}
|
||||
Controller.setOption(_targetMimcont, AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.GREEN)
|
||||
--_targetMimgroup:enableEmission(false)
|
||||
timer.scheduleFunction(SuppressionEnd, id, SuppressionEndTime) --Schedule the SuppressionEnd() function
|
||||
end
|
||||
end
|
||||
end
|
||||
self:ManageEvasion(_targetskill,_targetgroup,SEADPlanePos,SEADWeaponName,SEADGroup)
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -113,7 +113,7 @@ do
|
||||
["AGM_122"] = "AGM_122",
|
||||
["AGM_84"] = "AGM_84",
|
||||
["AGM_45"] = "AGM_45",
|
||||
["ALARN"] = "ALARM",
|
||||
["ALARM"] = "ALARM",
|
||||
["LD-10"] = "LD-10",
|
||||
["X_58"] = "X_58",
|
||||
["X_28"] = "X_28",
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
-- @field #boolean Report If true, send status messages to coalition.
|
||||
-- @field Wrapper.Static#STATIC warehouse The phyical warehouse structure.
|
||||
-- @field #string alias Alias of the warehouse. Name its called when sending messages.
|
||||
-- @field Core.Zone#ZONE zone Zone around the warehouse. If this zone is captured, the warehouse and all its assets goes to the capturing coaliton.
|
||||
-- @field Core.Zone#ZONE zone Zone around the warehouse. If this zone is captured, the warehouse and all its assets goes to the capturing coalition.
|
||||
-- @field Wrapper.Airbase#AIRBASE airbase Airbase the warehouse belongs to.
|
||||
-- @field #string airbasename Name of the airbase associated to the warehouse.
|
||||
-- @field Core.Point#COORDINATE road Closest point to warehouse on road.
|
||||
@@ -764,7 +764,7 @@
|
||||
-- warehouseBatumi:Load("D:\\My Warehouse Data\\")
|
||||
-- warehouseBatumi:Start()
|
||||
--
|
||||
-- This sequence loads all assets from file. If a warehouse was captured in the last mission, it also respawns the static warehouse structure with the right coaliton.
|
||||
-- This sequence loads all assets from file. If a warehouse was captured in the last mission, it also respawns the static warehouse structure with the right coalition.
|
||||
-- However, it due to DCS limitations it is not possible to set the airbase coalition. This has to be done manually in the mission editor. Or alternatively, one could
|
||||
-- spawn some ground units via a self request and let them capture the airbase.
|
||||
--
|
||||
@@ -1601,7 +1601,7 @@ WAREHOUSE = {
|
||||
-- @field #number range Range of the unit in meters.
|
||||
-- @field #number speedmax Maximum speed in km/h the group can do.
|
||||
-- @field #number size Maximum size in length and with of the asset in meters.
|
||||
-- @field #number weight The weight of the whole asset group in kilo gramms.
|
||||
-- @field #number weight The weight of the whole asset group in kilograms.
|
||||
-- @field DCS#Object.Desc DCSdesc All DCS descriptors.
|
||||
-- @field #WAREHOUSE.Attribute attribute Generalized attribute of the group.
|
||||
-- @field #table cargobay Array of cargo bays of all units in an asset group.
|
||||
@@ -1811,7 +1811,7 @@ WAREHOUSE.version="1.0.2"
|
||||
-- DONE: Add shipping lanes between warehouses.
|
||||
-- DONE: Handle cases with immobile units <== should be handled by dispatcher classes.
|
||||
-- DONE: Handle cases for aircraft carriers and other ships. Place warehouse on carrier possible? On others probably not - exclude them?
|
||||
-- DONE: Add general message function for sending to coaliton or debug.
|
||||
-- DONE: Add general message function for sending to coalition or debug.
|
||||
-- DONE: Fine tune event handlers.
|
||||
-- DONE: Improve generalized attributes.
|
||||
-- DONE: If warehouse is destroyed, all asssets are gone.
|
||||
@@ -3155,7 +3155,7 @@ end
|
||||
-- @param MinAssets (Optional) Minimum number of assets the warehouse should have. Default 0.
|
||||
-- @param #string Descriptor (Optional) Descriptor describing the selected assets which should be in stock. See @{#WAREHOUSE.Descriptor} for possible values.
|
||||
-- @param DescriptorValue (Optional) Descriptor value selecting the type of assets which should be in stock.
|
||||
-- @param DCS#Coalition.side Coalition (Optional) Coalition side of the warehouse. Default is the same coaliton as the present warehouse. Set to false for any coalition.
|
||||
-- @param DCS#Coalition.side Coalition (Optional) Coalition side of the warehouse. Default is the same coalition as the present warehouse. Set to false for any coalition.
|
||||
-- @param Core.Point#COORDINATE RefCoordinate (Optional) Coordinate to which the closest warehouse is searched. Default is the warehouse calling this function.
|
||||
-- @return #WAREHOUSE The the nearest warehouse object. Or nil if no warehouse is found.
|
||||
-- @return #number The distance to the nearest warehouse in meters. Or nil if no warehouse is found.
|
||||
@@ -3267,7 +3267,7 @@ function WAREHOUSE:onafterStart(From, Event, To)
|
||||
|
||||
-- Short info.
|
||||
local text=string.format("Starting warehouse %s alias %s:\n",self.warehouse:GetName(), self.alias)
|
||||
text=text..string.format("Coaliton = %s\n", self:GetCoalitionName())
|
||||
text=text..string.format("Coalition = %s\n", self:GetCoalitionName())
|
||||
text=text..string.format("Country = %s\n", self:GetCountryName())
|
||||
text=text..string.format("Airbase = %s (category=%d)\n", self:GetAirbaseName(), self:GetAirbaseCategory())
|
||||
env.info(text)
|
||||
@@ -8460,7 +8460,7 @@ function WAREHOUSE:_GetStockAssetsText(messagetoall)
|
||||
end
|
||||
|
||||
--- Create or update mark text at warehouse, which is displayed in F10 map showing how many assets of each type are in stock.
|
||||
-- Only the coaliton of the warehouse owner is able to see it.
|
||||
-- Only the coalition of the warehouse owner is able to see it.
|
||||
-- @param #WAREHOUSE self
|
||||
-- @return #string Text about warehouse stock
|
||||
function WAREHOUSE:_UpdateWarehouseMarkText()
|
||||
|
||||
@@ -715,20 +715,21 @@ do -- ZONE_CAPTURE_COALITION
|
||||
|
||||
local UnitHit = EventData.TgtUnit
|
||||
|
||||
if UnitHit.ClassName ~= "SCENERY" then
|
||||
-- Check if unit is inside the capture zone and that it is of the defending coalition.
|
||||
if UnitHit and UnitHit:IsInZone(self) and UnitHit:GetCoalition()==self.Coalition then
|
||||
|
||||
-- Update last hit time.
|
||||
self.HitTimeLast=timer.getTime()
|
||||
|
||||
-- Only trigger attacked event if not already in state "Attacked".
|
||||
if self:GetState()~="Attacked" then
|
||||
self:F2("Hit ==> Attack")
|
||||
self:Attack()
|
||||
end
|
||||
|
||||
if UnitHit and UnitHit:IsInZone(self) and UnitHit:GetCoalition()==self.Coalition then
|
||||
|
||||
-- Update last hit time.
|
||||
self.HitTimeLast=timer.getTime()
|
||||
|
||||
-- Only trigger attacked event if not already in state "Attacked".
|
||||
if self:GetState()~="Attacked" then
|
||||
self:F2("Hit ==> Attack")
|
||||
self:Attack()
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -803,7 +804,7 @@ do -- ZONE_CAPTURE_COALITION
|
||||
return IsEmpty
|
||||
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
|
||||
-- @return #boolean self:IsAllInZoneOfCoalition( self.Coalition )
|
||||
function ZONE_CAPTURE_COALITION:IsGuarded()
|
||||
@@ -825,7 +826,7 @@ do -- ZONE_CAPTURE_COALITION
|
||||
return IsCaptured
|
||||
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
|
||||
-- @return #boolean self:IsSomeInZoneOfCoalition( self.Coalition )
|
||||
function ZONE_CAPTURE_COALITION:IsAttacked()
|
||||
@@ -890,30 +891,31 @@ do -- ZONE_CAPTURE_COALITION
|
||||
end
|
||||
|
||||
-- Status text.
|
||||
local text=string.format("CAPTURE ZONE %s: Owner=%s (Previous=%s): #blue=%d, #red=%d, Status %s", self:GetZoneName(), self:GetCoalitionName(), UTILS.GetCoalitionName(self:GetPreviousCoalition()), nBlue, nRed, State)
|
||||
local NewState = self:GetState()
|
||||
if NewState~=State then
|
||||
text=text..string.format(" --> %s", NewState)
|
||||
if false then
|
||||
local text=string.format("CAPTURE ZONE %s: Owner=%s (Previous=%s): #blue=%d, #red=%d, Status %s", self:GetZoneName(), self:GetCoalitionName(), UTILS.GetCoalitionName(self:GetPreviousCoalition()), nBlue, nRed, State)
|
||||
local NewState = self:GetState()
|
||||
if NewState~=State then
|
||||
text=text..string.format(" --> %s", NewState)
|
||||
end
|
||||
self:I(text)
|
||||
end
|
||||
self:I(text)
|
||||
|
||||
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Misc Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
--- Update Mark on F10 map.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
function ZONE_CAPTURE_COALITION:Mark()
|
||||
|
||||
|
||||
if self.MarkOn then
|
||||
|
||||
|
||||
local Coord = self:GetCoordinate()
|
||||
local ZoneName = self:GetZoneName()
|
||||
local State = self:GetState()
|
||||
|
||||
|
||||
-- Remove marks.
|
||||
if self.MarkRed then
|
||||
Coord:RemoveMark(self.MarkRed)
|
||||
@@ -921,21 +923,21 @@ do -- ZONE_CAPTURE_COALITION
|
||||
if self.MarkBlue then
|
||||
Coord:RemoveMark(self.MarkBlue)
|
||||
end
|
||||
|
||||
-- Create new marks for each coaliton.
|
||||
|
||||
-- Create new marks for each coalition.
|
||||
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 )
|
||||
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 )
|
||||
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 )
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
--- **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.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- @module Functional.ZoneGoalCoalition
|
||||
-- @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.
|
||||
-- @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.
|
||||
--
|
||||
--
|
||||
-- ## 1. ZONE_GOAL_COALITION constructor
|
||||
--
|
||||
--
|
||||
-- * @{#ZONE_GOAL_COALITION.New}(): Creates a new ZONE_GOAL_COALITION object.
|
||||
--
|
||||
--
|
||||
-- ## 2. ZONE_GOAL_COALITION is a finite state machine (FSM).
|
||||
--
|
||||
--
|
||||
-- ### 2.1 ZONE_GOAL_COALITION States
|
||||
--
|
||||
--
|
||||
-- ### 2.2 ZONE_GOAL_COALITION Events
|
||||
--
|
||||
--
|
||||
-- ### 2.3 ZONE_GOAL_COALITION State Machine
|
||||
--
|
||||
--
|
||||
-- @field #ZONE_GOAL_COALITION
|
||||
ZONE_GOAL_COALITION = {
|
||||
ClassName = "ZONE_GOAL_COALITION",
|
||||
Coalition = nil,
|
||||
PreviousCoaliton = nil,
|
||||
UnitCategories = nil,
|
||||
ClassName = "ZONE_GOAL_COALITION",
|
||||
Coalition = nil,
|
||||
PreviousCoalition = nil,
|
||||
UnitCategories = nil,
|
||||
ObjectCategories = nil,
|
||||
}
|
||||
|
||||
|
||||
--- @field #table ZONE_GOAL_COALITION.States
|
||||
ZONE_GOAL_COALITION.States = {}
|
||||
|
||||
|
||||
--- ZONE_GOAL_COALITION Constructor.
|
||||
-- @param #ZONE_GOAL_COALITION self
|
||||
-- @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}.
|
||||
-- @return #ZONE_GOAL_COALITION
|
||||
function ZONE_GOAL_COALITION:New( Zone, Coalition, UnitCategories )
|
||||
|
||||
|
||||
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
|
||||
end
|
||||
|
||||
|
||||
-- Inherit ZONE_GOAL.
|
||||
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.
|
||||
self:SetCoalition( Coalition or coalition.side.NEUTRAL)
|
||||
|
||||
self:SetCoalition( Coalition or coalition.side.NEUTRAL )
|
||||
|
||||
-- Set default unit and object categories for the zone scan.
|
||||
self:SetUnitCategories(UnitCategories)
|
||||
self:SetUnitCategories( UnitCategories )
|
||||
self:SetObjectCategories()
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set the owning coalition of the zone.
|
||||
-- @param #ZONE_GOAL_COALITION self
|
||||
-- @param DCSCoalition.DCSCoalition#coalition Coalition The coalition ID, e.g. *coalition.side.RED*.
|
||||
-- @return #ZONE_GOAL_COALITION
|
||||
function ZONE_GOAL_COALITION:SetCoalition( Coalition )
|
||||
self.PreviousCoalition=self.Coalition or Coalition
|
||||
self.PreviousCoalition = self.Coalition or Coalition
|
||||
self.Coalition = Coalition
|
||||
return self
|
||||
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}.
|
||||
-- @return #ZONE_GOAL_COALITION
|
||||
function ZONE_GOAL_COALITION:SetUnitCategories( UnitCategories )
|
||||
|
||||
if UnitCategories and type(UnitCategories)~="table" then
|
||||
UnitCategories={UnitCategories}
|
||||
|
||||
if UnitCategories and type( UnitCategories ) ~= "table" then
|
||||
UnitCategories = { UnitCategories }
|
||||
end
|
||||
|
||||
self.UnitCategories=UnitCategories or {Unit.Category.GROUND_UNIT}
|
||||
|
||||
|
||||
self.UnitCategories = UnitCategories or { Unit.Category.GROUND_UNIT }
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set the owning coalition of the zone.
|
||||
-- @param #ZONE_GOAL_COALITION self
|
||||
-- @param #table ObjectCategories Table of unit categories. See [DCS Class Object](https://wiki.hoggitworld.com/view/DCS_Class_Object). Default {Object.Category.UNIT, Object.Category.STATIC}, i.e. all UNITS and STATICS.
|
||||
-- @return #ZONE_GOAL_COALITION
|
||||
function ZONE_GOAL_COALITION:SetObjectCategories( ObjectCategories )
|
||||
|
||||
if ObjectCategories and type(ObjectCategories)~="table" then
|
||||
ObjectCategories={ObjectCategories}
|
||||
|
||||
if ObjectCategories and type( ObjectCategories ) ~= "table" then
|
||||
ObjectCategories = { ObjectCategories }
|
||||
end
|
||||
|
||||
self.ObjectCategories=ObjectCategories or {Object.Category.UNIT, Object.Category.STATIC}
|
||||
|
||||
|
||||
self.ObjectCategories = ObjectCategories or { Object.Category.UNIT, Object.Category.STATIC }
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Get the owning coalition of the zone.
|
||||
-- @param #ZONE_GOAL_COALITION self
|
||||
-- @return DCSCoalition.DCSCoalition#coalition Coalition.
|
||||
@@ -127,39 +125,37 @@ do -- ZoneGoal
|
||||
return self.Coalition
|
||||
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
|
||||
-- @return DCSCoalition.DCSCoalition#coalition Coalition.
|
||||
function ZONE_GOAL_COALITION:GetPreviousCoalition()
|
||||
return self.PreviousCoalition
|
||||
end
|
||||
|
||||
|
||||
--- Get the owning coalition name of the zone.
|
||||
-- @param #ZONE_GOAL_COALITION self
|
||||
-- @return #string Coalition name.
|
||||
function ZONE_GOAL_COALITION:GetCoalitionName()
|
||||
return UTILS.GetCoalitionName(self.Coalition)
|
||||
return UTILS.GetCoalitionName( self.Coalition )
|
||||
end
|
||||
|
||||
|
||||
--- Check status Coalition ownership.
|
||||
-- @param #ZONE_GOAL_COALITION self
|
||||
-- @return #ZONE_GOAL_COALITION
|
||||
function ZONE_GOAL_COALITION:StatusZone()
|
||||
|
||||
|
||||
-- Get current state.
|
||||
local State = self:GetState()
|
||||
|
||||
|
||||
-- Debug text.
|
||||
local text=string.format("Zone state=%s, Owner=%s, Scanning...", State, self:GetCoalitionName())
|
||||
self:F(text)
|
||||
|
||||
local text = string.format( "Zone state=%s, Owner=%s, Scanning...", State, self:GetCoalitionName() )
|
||||
self:F( text )
|
||||
|
||||
-- Scan zone.
|
||||
self:Scan( self.ObjectCategories, self.UnitCategories )
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ _DATABASE:_RegisterCargos()
|
||||
|
||||
--- Register zones.
|
||||
_DATABASE:_RegisterZones()
|
||||
_DATABASE:_RegisterAirbases()
|
||||
|
||||
--- Check if os etc is available.
|
||||
BASE:I("Checking de-sanitization of os, io and lfs:")
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,3 @@
|
||||
|
||||
--- **Ops** -- Combat Search and Rescue.
|
||||
--
|
||||
-- ===
|
||||
@@ -7,6 +6,14 @@
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Missions:--- **Ops** -- Combat Search and Rescue.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- **CSAR** - MOOSE based Helicopter CSAR Operations.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Missions:
|
||||
--
|
||||
-- ### [CSAR - Combat Search & Rescue](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/OPS%20-%20CSAR)
|
||||
@@ -23,7 +30,7 @@
|
||||
-- @module Ops.CSAR
|
||||
-- @image OPS_CSAR.jpg
|
||||
|
||||
-- Date: July 2021
|
||||
-- Date: Feb 2022
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
--- **CSAR** class, extends Core.Base#BASE, Core.Fsm#FSM
|
||||
@@ -46,6 +53,7 @@
|
||||
-- * Object oriented refactoring of Ciribob\'s fantastic CSAR script.
|
||||
-- * No need for extra MIST loading.
|
||||
-- * Additional events to tailor your mission.
|
||||
-- * Optional SpawnCASEVAC to create casualties without beacon (e.g. handling dead ground vehicles and create CASVAC requests).
|
||||
--
|
||||
-- ## 0. Prerequisites
|
||||
--
|
||||
@@ -70,6 +78,7 @@
|
||||
--
|
||||
-- self.allowDownedPilotCAcontrol = false -- Set to false if you don\'t want to allow control by Combined Arms.
|
||||
-- self.allowFARPRescue = true -- allows pilots to be rescued by landing at a FARP or Airbase. Else MASH only!
|
||||
-- self.FARPRescueDistance = 1000 -- you need to be this close to a FARP or Airport for the pilot to be rescued.
|
||||
-- self.autosmoke = false -- automatically smoke a downed pilot\'s location when a heli is near.
|
||||
-- self.autosmokedistance = 1000 -- distance for autosmoke
|
||||
-- self.coordtype = 1 -- Use Lat/Long DDM (0), Lat/Long DMS (1), MGRS (2), Bullseye imperial (3) or Bullseye metric (4) for coordinates.
|
||||
@@ -95,7 +104,17 @@
|
||||
-- self.approachdist_far = 5000 -- switch do 10 sec interval approach mode, meters
|
||||
-- self.approachdist_near = 3000 -- switch to 5 sec interval approach mode, meters
|
||||
-- self.pilotmustopendoors = false -- switch to true to enable check of open doors
|
||||
--
|
||||
-- -- (added 0.1.9)
|
||||
-- self.suppressmessages = false -- switch off all messaging if you want to do your own
|
||||
-- -- (added 0.1.11)
|
||||
-- self.rescuehoverheight = 20 -- max height for a hovering rescue in meters
|
||||
-- self.rescuehoverdistance = 10 -- max distance for a hovering rescue in meters
|
||||
-- -- (added 0.1.12)
|
||||
-- -- Country codes for spawned pilots
|
||||
-- self.countryblue= country.id.USA
|
||||
-- self.countryred = country.id.RUSSIA
|
||||
-- self.countryneutral = country.id.UN_PEACEKEEPERS
|
||||
--
|
||||
-- ## 2.1 Experimental Features
|
||||
--
|
||||
-- WARNING - Here\'ll be dragons!
|
||||
@@ -105,7 +124,10 @@
|
||||
-- self.SRSPath = "E:\\Progra~1\\DCS-SimpleRadio-Standalone\\" -- adjust your own path in your SRS installation -- server(!)
|
||||
-- self.SRSchannel = 300 -- radio channel
|
||||
-- self.SRSModulation = radio.modulation.AM -- modulation
|
||||
--
|
||||
-- --
|
||||
-- self.csarUsePara = false -- If set to true, will use the LandingAfterEjection Event instead of Ejection --shagrat
|
||||
-- self.wetfeettemplate = "man in floating thingy" -- if you use a mod to have a pilot in a rescue float, put the template name in here for wet feet spawns. Note: in conjunction with csarUsePara this might create dual ejected pilots in edge cases.
|
||||
--
|
||||
-- ## 3. Results
|
||||
--
|
||||
-- Number of successful landings with save pilots and aggregated number of saved pilots is stored in these variables in the object:
|
||||
@@ -165,6 +187,8 @@
|
||||
-- -- Create downed "Pilot Wagner" in #ZONE "CSAR_Start_1" at a random point for the blue coalition
|
||||
-- my_csar:SpawnCSARAtZone( "CSAR_Start_1", coalition.side.BLUE, "Pilot Wagner", true )
|
||||
--
|
||||
-- --Create a casualty and CASEVAC request from a "Point" (VEC2) for the blue coalition --shagrat
|
||||
-- my_csar:SpawnCASEVAC(Point, coalition.side.BLUE)
|
||||
--
|
||||
-- @field #CSAR
|
||||
CSAR = {
|
||||
@@ -212,28 +236,9 @@ CSAR = {
|
||||
-- @field #number frequency Frequency of the NDB.
|
||||
-- @field #string player Player name if applicable.
|
||||
-- @field Wrapper.Group#GROUP group Spawned group object.
|
||||
-- @field #number timestamp Timestamp for approach process
|
||||
-- @field #boolean alive Group is alive or dead/rescued
|
||||
--
|
||||
--- Updated and sorted list of known NDB beacons (in kHz!) from the available maps.
|
||||
|
||||
--[[ Moved to Utils
|
||||
-- @field #CSAR.SkipFrequencies
|
||||
CSAR.SkipFrequencies = {
|
||||
214,274,291.5,295,297.5,
|
||||
300.5,304,307,309.5,311,312,312.5,316,
|
||||
320,324,328,329,330,336,337,
|
||||
342,343,348,351,352,353,358,
|
||||
363,365,368,372.5,374,
|
||||
380,381,384,389,395,396,
|
||||
414,420,430,432,435,440,450,455,462,470,485,
|
||||
507,515,520,525,528,540,550,560,570,577,580,602,625,641,662,670,680,682,690,
|
||||
705,720,722,730,735,740,745,750,770,795,
|
||||
822,830,862,866,
|
||||
905,907,920,935,942,950,995,
|
||||
1000,1025,1030,1050,1065,1116,1175,1182,1210
|
||||
}
|
||||
--]]
|
||||
-- @field #number timestamp Timestamp for approach process.
|
||||
-- @field #boolean alive Group is alive or dead/rescued.
|
||||
-- @field #boolean wetfeet Group is spawned over (deep) water.
|
||||
|
||||
--- All slot / Limit settings
|
||||
-- @type CSAR.AircraftType
|
||||
@@ -248,10 +253,12 @@ CSAR.AircraftType["Mi-8MTV2"] = 12
|
||||
CSAR.AircraftType["Mi-8MT"] = 12
|
||||
CSAR.AircraftType["Mi-24P"] = 8
|
||||
CSAR.AircraftType["Mi-24V"] = 8
|
||||
CSAR.AircraftType["Bell-47"] = 2
|
||||
CSAR.AircraftType["UH-60L"] = 10
|
||||
|
||||
--- CSAR class version.
|
||||
-- @field #string version
|
||||
CSAR.version="0.1.8r3"
|
||||
CSAR.version="1.0.4c"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- ToDo list
|
||||
@@ -259,7 +266,7 @@ CSAR.version="0.1.8r3"
|
||||
|
||||
-- DONE: SRS Integration (to be tested)
|
||||
-- TODO: Maybe - add option to smoke/flare closest MASH
|
||||
|
||||
-- TODO: shagrat Add cargoWeight to helicopter when pilot boarded
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Constructor
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -360,13 +367,15 @@ function CSAR:New(Coalition, Template, Alias)
|
||||
self.extractDistance = 500 -- Distance the Downed pilot will run to the rescue helicopter
|
||||
self.loadtimemax = 135 -- seconds
|
||||
self.radioSound = "beacon.ogg" -- the name of the sound file to use for the Pilot radio beacons. If this isnt added to the mission BEACONS WONT WORK!
|
||||
self.beaconRefresher = 29 -- seconds
|
||||
self.allowFARPRescue = true --allows pilot to be rescued by landing at a FARP or Airbase
|
||||
self.FARPRescueDistance = 1000 -- you need to be this close to a FARP or Airport for the pilot to be rescued.
|
||||
self.max_units = 6 --max number of pilots that can be carried
|
||||
self.useprefix = true -- Use the Prefixed defined below, Requires Unit have the Prefix defined below
|
||||
self.csarPrefix = { "helicargo", "MEDEVAC"} -- prefixes used for useprefix=true - DON\'T use # in names!
|
||||
self.template = Template or "generic" -- template for downed pilot
|
||||
self.mashprefix = {"MASH"} -- prefixes used to find MASHes
|
||||
self.mash = SET_GROUP:New():FilterCoalitions(self.coalition):FilterPrefixes(self.mashprefix):FilterOnce() -- currently only GROUP objects, maybe support STATICs also?
|
||||
|
||||
self.autosmoke = false -- automatically smoke location when heli is near
|
||||
self.autosmokedistance = 2000 -- distance for autosmoke
|
||||
-- added 0.1.4
|
||||
@@ -378,7 +387,24 @@ function CSAR:New(Coalition, Template, Alias)
|
||||
self.approachdist_far = 5000 -- switch do 10 sec interval approach mode, meters
|
||||
self.approachdist_near = 3000 -- switch to 5 sec interval approach mode, meters
|
||||
self.pilotmustopendoors = false -- switch to true to enable check on open doors
|
||||
|
||||
self.suppressmessages = false
|
||||
|
||||
-- added 0.1.11r1
|
||||
self.rescuehoverheight = 20
|
||||
self.rescuehoverdistance = 10
|
||||
|
||||
-- added 0.1.12
|
||||
self.countryblue= country.id.USA
|
||||
self.countryred = country.id.RUSSIA
|
||||
self.countryneutral = country.id.UN_PEACEKEEPERS
|
||||
|
||||
-- added 0.1.3
|
||||
self.csarUsePara = false -- shagrat set to true, will use the LandingAfterEjection Event instead of Ejection
|
||||
|
||||
-- added 0.1.4
|
||||
self.wetfeettemplate = nil
|
||||
self.usewetfeet = false
|
||||
|
||||
-- WARNING - here\'ll be dragons
|
||||
-- for this to work you need to de-sanitize your mission environment in <DCS root>\Scripts\MissionScripting.lua
|
||||
-- needs SRS => 1.9.6 to work (works on the *server* side)
|
||||
@@ -490,8 +516,9 @@ end
|
||||
-- @param #string Typename Typename of unit.
|
||||
-- @param #number Frequency Frequency of the NDB in Hz
|
||||
-- @param #string Playername Name of Player (if applicable)
|
||||
-- @param #boolean Wetfeet Ejected over water
|
||||
-- @return #CSAR self.
|
||||
function CSAR:_CreateDownedPilotTrack(Group,Groupname,Side,OriginalUnit,Description,Typename,Frequency,Playername)
|
||||
function CSAR:_CreateDownedPilotTrack(Group,Groupname,Side,OriginalUnit,Description,Typename,Frequency,Playername,Wetfeet)
|
||||
self:T({"_CreateDownedPilotTrack",Groupname,Side,OriginalUnit,Description,Typename,Frequency,Playername})
|
||||
|
||||
-- create new entry
|
||||
@@ -507,6 +534,7 @@ function CSAR:_CreateDownedPilotTrack(Group,Groupname,Side,OriginalUnit,Descript
|
||||
DownedPilot.group = Group
|
||||
DownedPilot.timestamp = 0
|
||||
DownedPilot.alive = true
|
||||
DownedPilot.wetfeet = Wetfeet or false
|
||||
|
||||
-- Add Pilot
|
||||
local PilotTable = self.downedPilots
|
||||
@@ -556,16 +584,23 @@ end
|
||||
-- @param #number country Country for template.
|
||||
-- @param Core.Point#COORDINATE point Coordinate to spawn at.
|
||||
-- @param #number frequency Frequency of the pilot's beacon
|
||||
-- @param #boolean wetfeet Spawn is over water
|
||||
-- @return Wrapper.Group#GROUP group The #GROUP object.
|
||||
-- @return #string alias The alias name.
|
||||
function CSAR:_SpawnPilotInField(country,point,frequency)
|
||||
self:T({country,point,frequency})
|
||||
function CSAR:_SpawnPilotInField(country,point,frequency,wetfeet)
|
||||
self:T({country,point,frequency,tostring(wetfeet)})
|
||||
local freq = frequency or 1000
|
||||
local freq = freq / 1000 -- kHz
|
||||
for i=1,10 do
|
||||
math.random(i,10000)
|
||||
end
|
||||
if point:IsSurfaceTypeWater() or wetfeet then
|
||||
point.y = 0
|
||||
end
|
||||
local template = self.template
|
||||
if self.usewetfeet and wetfeet then
|
||||
template = self.wetfeettemplate
|
||||
end
|
||||
local alias = string.format("Pilot %.2fkHz-%d", freq, math.random(1,99))
|
||||
local coalition = self.coalition
|
||||
local pilotcacontrol = self.allowDownedPilotCAcontrol -- Switch AI on/oof - is this really correct for CA?
|
||||
@@ -631,21 +666,31 @@ function CSAR:_AddCsar(_coalition , _country, _point, _typeName, _unitName, _pla
|
||||
self:T({_coalition , _country, _point, _typeName, _unitName, _playerName, _freq, noMessage, _description})
|
||||
|
||||
local template = self.template
|
||||
local wetfeet = false
|
||||
|
||||
local surface = _point:GetSurfaceType()
|
||||
if surface == land.SurfaceType.WATER then
|
||||
wetfeet = true
|
||||
end
|
||||
|
||||
if not _freq then
|
||||
_freq = self:_GenerateADFFrequency()
|
||||
if not _freq then _freq = 333000 end --noob catch
|
||||
end
|
||||
|
||||
local _spawnedGroup, _alias = self:_SpawnPilotInField(_country,_point,_freq)
|
||||
local _spawnedGroup, _alias = self:_SpawnPilotInField(_country,_point,_freq,wetfeet)
|
||||
|
||||
local _typeName = _typeName or "Pilot"
|
||||
|
||||
if not noMessage then
|
||||
self:_DisplayToAllSAR("MAYDAY MAYDAY! " .. _typeName .. " is down. ", self.coalition, 10)
|
||||
if _freq ~= 0 then --shagrat different CASEVAC msg
|
||||
self:_DisplayToAllSAR("MAYDAY MAYDAY! " .. _typeName .. " is down. ", self.coalition, self.messageTime)
|
||||
else
|
||||
self:_DisplayToAllSAR("Troops In Contact. " .. _typeName .. " requests CASEVAC. ", self.coalition, self.messageTime)
|
||||
end
|
||||
end
|
||||
|
||||
if _freq then
|
||||
if (_freq and _freq ~= 0) then --shagrat only add beacon if _freq is NOT 0
|
||||
self:_AddBeaconToGroup(_spawnedGroup, _freq)
|
||||
end
|
||||
|
||||
@@ -654,25 +699,33 @@ function CSAR:_AddCsar(_coalition , _country, _point, _typeName, _unitName, _pla
|
||||
local _text = _description
|
||||
if not forcedesc then
|
||||
if _playerName ~= nil then
|
||||
_text = "Pilot " .. _playerName
|
||||
elseif _unitName ~= nil then
|
||||
_text = "AI Pilot of " .. _unitName
|
||||
if _freq ~= 0 then --shagrat
|
||||
_text = "Pilot " .. _playerName
|
||||
else
|
||||
_text = "TIC - " .. _playerName
|
||||
end
|
||||
elseif _unitName ~= nil then
|
||||
if _freq ~= 0 then --shagrat
|
||||
_text = "AI Pilot of " .. _unitName
|
||||
else
|
||||
_text = "TIC - " .. _unitName
|
||||
end
|
||||
end
|
||||
end
|
||||
self:T({_spawnedGroup, _alias})
|
||||
|
||||
local _GroupName = _spawnedGroup:GetName() or _alias
|
||||
|
||||
self:_CreateDownedPilotTrack(_spawnedGroup,_GroupName,_coalition,_unitName,_text,_typeName,_freq,_playerName)
|
||||
self:_CreateDownedPilotTrack(_spawnedGroup,_GroupName,_coalition,_unitName,_text,_typeName,_freq,_playerName,wetfeet)
|
||||
|
||||
self:_InitSARForPilot(_spawnedGroup, _GroupName, _freq, noMessage)
|
||||
self:_InitSARForPilot(_spawnedGroup, _unitName, _freq, noMessage) --shagrat use unitName to have the aircraft callsign / descriptive "name" etc.
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- (Internal) Function to add a CSAR object into the scene at a zone coordinate. For mission designers wanting to add e.g. PoWs to the scene.
|
||||
-- @param #CSAR self
|
||||
-- @param #string _zone Name of the zone.
|
||||
-- @param #string _zone Name of the zone. Can also be passed as a (normal, round) ZONE object.
|
||||
-- @param #number _coalition Coalition.
|
||||
-- @param #string _description (optional) Description.
|
||||
-- @param #boolean _randomPoint (optional) Random yes or no.
|
||||
@@ -683,7 +736,16 @@ end
|
||||
function CSAR:_SpawnCsarAtZone( _zone, _coalition, _description, _randomPoint, _nomessage, unitname, typename, forcedesc)
|
||||
self:T(self.lid .. " _SpawnCsarAtZone")
|
||||
local freq = self:_GenerateADFFrequency()
|
||||
local _triggerZone = ZONE:New(_zone) -- trigger to use as reference position
|
||||
|
||||
local _triggerZone = nil
|
||||
if type(_zone) == "string" then
|
||||
_triggerZone = ZONE:New(_zone) -- trigger to use as reference position
|
||||
elseif type(_zone) == "table" and _zone.ClassName then
|
||||
if string.find(_zone.ClassName, "ZONE",1) then
|
||||
_triggerZone = _zone -- is already a zone
|
||||
end
|
||||
end
|
||||
|
||||
if _triggerZone == nil then
|
||||
self:E(self.lid.."ERROR: Can\'t find zone called " .. _zone, 10)
|
||||
return
|
||||
@@ -703,11 +765,11 @@ function CSAR:_SpawnCsarAtZone( _zone, _coalition, _description, _randomPoint, _
|
||||
|
||||
local _country = 0
|
||||
if _coalition == coalition.side.BLUE then
|
||||
_country = country.id.USA
|
||||
_country = self.countryblue
|
||||
elseif _coalition == coalition.side.RED then
|
||||
_country = country.id.RUSSIA
|
||||
_country = self.countryred
|
||||
else
|
||||
_country = country.id.UN_PEACEKEEPERS
|
||||
_country = self.countryneutral
|
||||
end
|
||||
|
||||
self:_AddCsar(_coalition, _country, pos, typename, unitname, _description, freq, _nomessage, _description, forcedesc)
|
||||
@@ -717,7 +779,7 @@ end
|
||||
|
||||
--- Function to add a CSAR object into the scene at a zone coordinate. For mission designers wanting to add e.g. PoWs to the scene.
|
||||
-- @param #CSAR self
|
||||
-- @param #string Zone Name of the zone.
|
||||
-- @param #string Zone Name of the zone. Can also be passed as a (normal, round) ZONE object.
|
||||
-- @param #number Coalition Coalition.
|
||||
-- @param #string Description (optional) Description.
|
||||
-- @param #boolean RandomPoint (optional) Random yes or no.
|
||||
@@ -734,7 +796,58 @@ function CSAR:SpawnCSARAtZone(Zone, Coalition, Description, RandomPoint, Nomessa
|
||||
return self
|
||||
end
|
||||
|
||||
-- TODO: Split in functions per Event type
|
||||
--- (Internal) Function to add a CSAR object into the scene at a Point coordinate (VEC_2). For mission designers wanting to add e.g. casualties to the scene, that don't use beacons.
|
||||
-- @param #CSAR self
|
||||
-- @param #string _Point a POINT_VEC2.
|
||||
-- @param #number _coalition Coalition.
|
||||
-- @param #string _description (optional) Description.
|
||||
-- @param #boolean _nomessage (optional) If true, don\'t send a message to SAR.
|
||||
-- @param #string unitname (optional) Name of the lost unit.
|
||||
-- @param #string typename (optional) Type of plane.
|
||||
-- @param #boolean forcedesc (optional) Force to use the description passed only for the pilot track entry. Use to have fully custom names.
|
||||
function CSAR:_SpawnCASEVAC( _Point, _coalition, _description, _nomessage, unitname, typename, forcedesc) --shagrat added internal Function _SpawnCASEVAC
|
||||
self:T(self.lid .. " _SpawnCASEVAC")
|
||||
|
||||
local _description = _description or "CASEVAC"
|
||||
local unitname = unitname or "CASEVAC"
|
||||
local typename = typename or "Ground Commander"
|
||||
|
||||
local pos = {}
|
||||
pos = _Point
|
||||
|
||||
local _country = 0
|
||||
if _coalition == coalition.side.BLUE then
|
||||
_country = self.countryblue
|
||||
elseif _coalition == coalition.side.RED then
|
||||
_country = self.countryred
|
||||
else
|
||||
_country = self.countryneutral
|
||||
end
|
||||
--shagrat set frequency to 0 as "flag" for no beacon
|
||||
self:_AddCsar(_coalition, _country, pos, typename, unitname, _description, 0, _nomessage, _description, forcedesc)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Function to add a CSAR object into the scene at a zone coordinate. For mission designers wanting to add e.g. PoWs to the scene.
|
||||
-- @param #CSAR self
|
||||
-- @param #string Point a POINT_VEC2.
|
||||
-- @param #number Coalition Coalition.
|
||||
-- @param #string Description (optional) Description.
|
||||
-- @param #boolean addBeacon (optional) yes or no.
|
||||
-- @param #boolean Nomessage (optional) If true, don\'t send a message to SAR.
|
||||
-- @param #string Unitname (optional) Name of the lost unit.
|
||||
-- @param #string Typename (optional) Type of plane.
|
||||
-- @param #boolean Forcedesc (optional) Force to use the **description passed only** for the pilot track entry. Use to have fully custom names.
|
||||
-- @usage If missions designers want to spawn downed pilots into the field, e.g. at mission begin, to give the helicopter guys work, they can do this like so:
|
||||
--
|
||||
-- -- Create casualty "CASEVAC" at Point #POINT_VEC2 for the blue coalition.
|
||||
-- my_csar:SpawnCASEVAC( POINT_VEC2, coalition.side.BLUE )
|
||||
function CSAR:SpawnCASEVAC(Point, Coalition, Description, Nomessage, Unitname, Typename, Forcedesc)
|
||||
self:_SpawnCASEVAC(Point, Coalition, Description, Nomessage, Unitname, Typename, Forcedesc)
|
||||
return self
|
||||
end --shagrat end added CASEVAC
|
||||
|
||||
--- (Internal) Event handler.
|
||||
-- @param #CSAR self
|
||||
function CSAR:_EventHandler(EventData)
|
||||
@@ -743,6 +856,11 @@ function CSAR:_EventHandler(EventData)
|
||||
|
||||
local _event = EventData -- Core.Event#EVENTDATA
|
||||
|
||||
-- no Player
|
||||
if self.enableForAI == false and _event.IniPlayerName == nil then
|
||||
return
|
||||
end
|
||||
|
||||
-- no event
|
||||
if _event == nil or _event.initiator == nil then
|
||||
return false
|
||||
@@ -806,8 +924,7 @@ function CSAR:_EventHandler(EventData)
|
||||
if self:_DoubleEjection(_unitname) then
|
||||
return
|
||||
end
|
||||
self:_DisplayToAllSAR("MAYDAY MAYDAY! " .. _unit:GetTypeName() .. " shot down. No Chute!", self.coalition, 10)
|
||||
--local m = MESSAGE:New("MAYDAY MAYDAY! " .. _unit:GetTypeName() .. " shot down. No Chute!",10,"Info"):ToCoalition(self.coalition)
|
||||
|
||||
else
|
||||
self:T(self.lid .. " Pilot has not taken off, ignore")
|
||||
end
|
||||
@@ -823,19 +940,15 @@ function CSAR:_EventHandler(EventData)
|
||||
local _unit = _event.IniUnit
|
||||
local _unitname = _event.IniUnitName
|
||||
local _group = _event.IniGroup
|
||||
|
||||
|
||||
if _unit == nil then
|
||||
return -- error!
|
||||
end
|
||||
|
||||
local _coalition = _unit:GetCoalition()
|
||||
|
||||
local _coalition = _unit:GetCoalition()
|
||||
if _coalition ~= self.coalition then
|
||||
return --ignore!
|
||||
end
|
||||
|
||||
if self.enableForAI == false and _event.IniPlayerName == nil then
|
||||
return
|
||||
end
|
||||
|
||||
if not self.takenOff[_event.IniUnitName] and not _group:IsAirborne() then
|
||||
self:T(self.lid .. " Pilot has not taken off, ignore")
|
||||
@@ -850,12 +963,40 @@ function CSAR:_EventHandler(EventData)
|
||||
if self.limitmaxdownedpilots and self:_ReachedPilotLimit() then
|
||||
return
|
||||
end
|
||||
|
||||
-- all checks passed, get going.
|
||||
|
||||
|
||||
-- TODO: Over water check --- EVENTS.LandingAfterEjection NOT triggered by DCS, so handle csarUsePara = true case
|
||||
-- might create dual pilots in edge cases
|
||||
|
||||
local wetfeet = false
|
||||
|
||||
local surface = _unit:GetCoordinate():GetSurfaceType()
|
||||
if surface == land.SurfaceType.WATER then
|
||||
wetfeet = true
|
||||
end
|
||||
-- all checks passed, get going.
|
||||
if self.csarUsePara == false or (self.csarUsePara and wetfeet ) then --shagrat check parameter LandingAfterEjection, if true don't spawn a Pilot from EJECTION event, wait for the Chute to land
|
||||
local _freq = self:_GenerateADFFrequency()
|
||||
self:_AddCsar(_coalition, _unit:GetCountry(), _unit:GetCoordinate() , _unit:GetTypeName(), _unit:GetName(), _event.IniPlayerName, _freq, false, "none")
|
||||
return true
|
||||
end
|
||||
|
||||
---- shagrat on event LANDING_AFTER_EJECTION spawn pilot at parachute location
|
||||
elseif (_event.id == EVENTS.LandingAfterEjection and self.csarUsePara == true) then
|
||||
self:I({EVENT=_event})
|
||||
local _LandingPos = COORDINATE:NewFromVec3(_event.initiator:getPosition().p)
|
||||
local _unitname = "Aircraft" --_event.initiator:getName() or "Aircraft" --shagrat Optional use of Object name which is unfortunately 'f15_Pilot_Parachute'
|
||||
local _typename = "Ejected Pilot" --_event.Initiator.getTypeName() or "Ejected Pilot"
|
||||
local _country = _event.initiator:getCountry()
|
||||
local _coalition = coalition.getCountryCoalition( _country )
|
||||
if _coalition == self.coalition then
|
||||
local _freq = self:_GenerateADFFrequency()
|
||||
self:_AddCsar(_coalition, _unit:GetCountry(), _unit:GetCoordinate() , _unit:GetTypeName(), _unit:GetName(), _event.IniPlayerName, _freq, false, "none")
|
||||
|
||||
return true
|
||||
self:I({coalition=_coalition,country= _country, coord=_LandingPos, name=_unitname, player=_event.IniPlayerName, freq=_freq})
|
||||
self:_AddCsar(_coalition, _country, _LandingPos, nil, _unitname, _event.IniPlayerName, _freq, false, "none")--shagrat add CSAR at Parachute location.
|
||||
|
||||
Unit.destroy(_event.initiator) -- shagrat remove static Pilot model
|
||||
end
|
||||
return true
|
||||
|
||||
elseif _event.id == EVENTS.Land then
|
||||
self:T(self.lid .. " Landing")
|
||||
@@ -894,11 +1035,7 @@ function CSAR:_EventHandler(EventData)
|
||||
end
|
||||
|
||||
if _place:GetCoalition() == self.coalition or _place:GetCoalition() == coalition.side.NEUTRAL then
|
||||
if self.pilotmustopendoors and not self:_IsLoadingDoorOpen(_event.IniUnitName) then
|
||||
self:_DisplayMessageToSAR(_unit, "Open the door to let me out!", self.messageTime, true)
|
||||
else
|
||||
self:_RescuePilots(_unit)
|
||||
end
|
||||
self:_ScheduledSARFlight(_event.IniUnitName,_event.IniGroupName,true)
|
||||
else
|
||||
self:T(string.format("Airfield %d, Unit %d", _place:GetCoalition(), _unit:GetCoalition()))
|
||||
end
|
||||
@@ -918,15 +1055,19 @@ end
|
||||
function CSAR:_InitSARForPilot(_downedGroup, _GroupName, _freq, _nomessage)
|
||||
self:T(self.lid .. " _InitSARForPilot")
|
||||
local _leader = _downedGroup:GetUnit(1)
|
||||
--local _groupName = _downedGroup:GetName()
|
||||
local _groupName = _GroupName
|
||||
local _freqk = _freq / 1000
|
||||
local _coordinatesText = self:_GetPositionOfWounded(_downedGroup)
|
||||
local _leadername = _leader:GetName()
|
||||
|
||||
if not _nomessage then
|
||||
local _text = string.format("%s requests SAR at %s, beacon at %.2f KHz", _leadername, _coordinatesText, _freqk)
|
||||
if _freq ~= 0 then --shagrat
|
||||
local _text = string.format("%s requests SAR at %s, beacon at %.2f KHz", _groupName, _coordinatesText, _freqk)--shagrat _groupName to prevent 'f15_Pilot_Parachute'
|
||||
self:_DisplayToAllSAR(_text,self.coalition,self.messageTime)
|
||||
else --shagrat CASEVAC msg
|
||||
local _text = string.format("Pickup Zone at %s.", _coordinatesText )
|
||||
self:_DisplayToAllSAR(_text,self.coalition,self.messageTime)
|
||||
end
|
||||
end
|
||||
|
||||
for _,_heliName in pairs(self.csarUnits) do
|
||||
@@ -934,7 +1075,7 @@ function CSAR:_InitSARForPilot(_downedGroup, _GroupName, _freq, _nomessage)
|
||||
end
|
||||
|
||||
-- trigger FSM event
|
||||
self:__PilotDown(2,_downedGroup, _freqk, _leadername, _coordinatesText)
|
||||
self:__PilotDown(2,_downedGroup, _freqk, _groupName, _coordinatesText)
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -1030,9 +1171,9 @@ function CSAR:_CheckWoundedGroupStatus(heliname,woundedgroupname)
|
||||
local dist = UTILS.MetersToNM(self.autosmokedistance)
|
||||
disttext = string.format("%.0fnm",dist)
|
||||
end
|
||||
self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s. I hear you! Damn, that thing is loud!\nI'll pop a smoke when you are %s away.\nLand or hover by the smoke.", _heliName, _pilotName, disttext), self.messageTime,false,true)
|
||||
self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s. I hear you! Finally, that is music in my ears!\nI'll pop a smoke when you are %s away.\nLand or hover by the smoke.", _heliName, _pilotName, disttext), self.messageTime,false,true)
|
||||
else
|
||||
self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s. I hear you! Damn, that thing is loud!\nRequest a flare or smoke if you need.", _heliName, _pilotName), self.messageTime,false,true)
|
||||
self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s. I hear you! Finally, that is music in my ears!\nRequest a flare or smoke if you need.", _heliName, _pilotName), self.messageTime,false,true)
|
||||
end
|
||||
--mark as shown for THIS heli and THIS group
|
||||
self.heliVisibleMessage[_lookupKeyHeli] = true
|
||||
@@ -1064,7 +1205,7 @@ function CSAR:_PopSmokeForGroup(_woundedGroupName, _woundedLeader)
|
||||
if _lastSmoke == nil or timer.getTime() > _lastSmoke then
|
||||
|
||||
local _smokecolor = self.smokecolor
|
||||
local _smokecoord = _woundedLeader:GetCoordinate()
|
||||
local _smokecoord = _woundedLeader:GetCoordinate():Translate( 6, math.random( 1, 360) ) --shagrat place smoke at a random 6 m distance, so smoke does not obscure the pilot
|
||||
_smokecoord:Smoke(_smokecolor)
|
||||
self.smokeMarkers[_woundedGroupName] = timer.getTime() + 300 -- next smoke time
|
||||
end
|
||||
@@ -1104,7 +1245,6 @@ function CSAR:_PickupUnit(_heliUnit, _pilotName, _woundedGroup, _woundedGroupNam
|
||||
local grouptable = downedgrouptable --#CSAR.DownedPilot
|
||||
self.inTransitGroups[_heliName][_woundedGroupName] =
|
||||
{
|
||||
-- DONE: Fix with #CSAR.DownedPilot
|
||||
originalUnit = grouptable.originalUnit,
|
||||
woundedGroup = _woundedGroupName,
|
||||
side = self.coalition,
|
||||
@@ -1112,7 +1252,7 @@ function CSAR:_PickupUnit(_heliUnit, _pilotName, _woundedGroup, _woundedGroupNam
|
||||
player = grouptable.player,
|
||||
}
|
||||
|
||||
_woundedGroup:Destroy()
|
||||
_woundedGroup:Destroy(false)
|
||||
self:_RemoveNameFromDownedPilots(_woundedGroupName,true)
|
||||
|
||||
self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s I\'m in! Get to the MASH ASAP! ", _heliName, _pilotName), self.messageTime,true,true)
|
||||
@@ -1143,39 +1283,7 @@ end
|
||||
-- @return #boolean outcome The outcome.
|
||||
function CSAR:_IsLoadingDoorOpen( unit_name )
|
||||
self:T(self.lid .. " _IsLoadingDoorOpen")
|
||||
local ret_val = false
|
||||
local unit = Unit.getByName(unit_name)
|
||||
if unit ~= nil then
|
||||
local type_name = unit:getTypeName()
|
||||
|
||||
if type_name == "Mi-8MT" and unit:getDrawArgumentValue(86) == 1 or unit:getDrawArgumentValue(250) == 1 then
|
||||
self:T(unit_name .. " Cargo doors are open or cargo door not present")
|
||||
ret_val = true
|
||||
end
|
||||
|
||||
if type_name == "Mi-24P" and unit:getDrawArgumentValue(38) == 1 or unit:getDrawArgumentValue(86) == 1 then
|
||||
self:T(unit_name .. " a side door is open")
|
||||
ret_val = true
|
||||
end
|
||||
|
||||
if type_name == "UH-1H" and unit:getDrawArgumentValue(43) == 1 or unit:getDrawArgumentValue(44) == 1 then
|
||||
self:T(unit_name .. " a side door is open ")
|
||||
ret_val = true
|
||||
end
|
||||
|
||||
if string.find(type_name, "SA342" ) and unit:getDrawArgumentValue(34) == 1 or unit:getDrawArgumentValue(38) == 1 then
|
||||
self:T(unit_name .. " front door(s) are open")
|
||||
ret_val = true
|
||||
end
|
||||
|
||||
if ret_val == false then
|
||||
self:T(unit_name .. " all doors are closed")
|
||||
end
|
||||
return ret_val
|
||||
|
||||
end -- nil
|
||||
|
||||
return false
|
||||
return UTILS.IsLoadingDoorOpen(unit_name)
|
||||
end
|
||||
|
||||
--- (Internal) Function to check if heli is close to group.
|
||||
@@ -1206,14 +1314,12 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG
|
||||
else
|
||||
self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s. You\'re close now! Land in a safe place, I will go there ", _heliName, _pilotName), self.messageTime,false,true)
|
||||
end
|
||||
--mark as shown for THIS heli and THIS group
|
||||
self.heliCloseMessage[_lookupKeyHeli] = true
|
||||
end
|
||||
|
||||
-- have we landed close enough?
|
||||
if not _heliUnit:InAir() then
|
||||
|
||||
-- if you land on them, doesnt matter if they were heading to someone else as you\'re closer, you win! :)
|
||||
if self.pilotRuntoExtractPoint == true then
|
||||
if (_distance < self.extractDistance) then
|
||||
local _time = self.landedStatus[_lookupKeyHeli]
|
||||
@@ -1226,7 +1332,8 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG
|
||||
_time = self.landedStatus[_lookupKeyHeli] - 10
|
||||
self.landedStatus[_lookupKeyHeli] = _time
|
||||
end
|
||||
if _time <= 0 or _distance < self.loadDistance then
|
||||
--if _time <= 0 or _distance < self.loadDistance then
|
||||
if _distance < self.loadDistance + 5 or _distance <= 13 then
|
||||
if self.pilotmustopendoors and not self:_IsLoadingDoorOpen(_heliName) then
|
||||
self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me in!", self.messageTime, true)
|
||||
return true
|
||||
@@ -1257,15 +1364,16 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG
|
||||
end
|
||||
|
||||
if _heliUnit:InAir() and _unitsInHelicopter + 1 <= _maxUnits then
|
||||
|
||||
if _distance < 8.0 then
|
||||
-- TODO - make variable
|
||||
if _distance < self.rescuehoverdistance then
|
||||
|
||||
--check height!
|
||||
local leaderheight = _woundedLeader:GetHeight()
|
||||
if leaderheight < 0 then leaderheight = 0 end
|
||||
local _height = _heliUnit:GetHeight() - leaderheight
|
||||
|
||||
if _height <= 20.0 then
|
||||
|
||||
-- TODO - make variable
|
||||
if _height <= self.rescuehoverheight then
|
||||
|
||||
local _time = self.hoverStatus[_lookupKeyHeli]
|
||||
|
||||
@@ -1314,7 +1422,8 @@ end
|
||||
-- @param #CSAR self
|
||||
-- @param #string heliname Heli name
|
||||
-- @param #string groupname Group name
|
||||
function CSAR:_ScheduledSARFlight(heliname,groupname)
|
||||
-- @param #boolean isairport If true, EVENT.Landing took place at an airport or FARP
|
||||
function CSAR:_ScheduledSARFlight(heliname,groupname, isairport)
|
||||
self:T(self.lid .. " _ScheduledSARFlight")
|
||||
self:T({heliname,groupname})
|
||||
local _heliUnit = self:_GetSARHeli(heliname)
|
||||
@@ -1337,8 +1446,8 @@ function CSAR:_ScheduledSARFlight(heliname,groupname)
|
||||
return
|
||||
end
|
||||
|
||||
if _dist < 200 and _heliUnit:InAir() == false then
|
||||
if self.pilotmustopendoors and not self:_IsLoadingDoorOpen(heliname) then
|
||||
if ( _dist < self.FARPRescueDistance or isairport ) and _heliUnit:InAir() == false then
|
||||
if self.pilotmustopendoors and self:_IsLoadingDoorOpen(heliname) == false then
|
||||
self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me out!", self.messageTime, true)
|
||||
else
|
||||
self:_RescuePilots(_heliUnit)
|
||||
@@ -1347,7 +1456,7 @@ function CSAR:_ScheduledSARFlight(heliname,groupname)
|
||||
end
|
||||
|
||||
--queue up
|
||||
self:__Returning(-5,heliname,_woundedGroupName)
|
||||
self:__Returning(-5,heliname,_woundedGroupName, isairport)
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -1363,8 +1472,7 @@ function CSAR:_RescuePilots(_heliUnit)
|
||||
-- Groups already rescued
|
||||
return
|
||||
end
|
||||
|
||||
-- DONE: count saved units?
|
||||
|
||||
local PilotsSaved = self:_PilotsOnboard(_heliName)
|
||||
|
||||
self.inTransitGroups[_heliName] = nil
|
||||
@@ -1398,12 +1506,15 @@ end
|
||||
-- @param #number _time Message show duration.
|
||||
-- @param #boolean _clear (optional) Clear screen.
|
||||
-- @param #boolean _speak (optional) Speak message via SRS.
|
||||
function CSAR:_DisplayMessageToSAR(_unit, _text, _time, _clear, _speak)
|
||||
-- @param #boolean _override (optional) Override message suppression
|
||||
function CSAR:_DisplayMessageToSAR(_unit, _text, _time, _clear, _speak, _override)
|
||||
self:T(self.lid .. " _DisplayMessageToSAR")
|
||||
local group = _unit:GetGroup()
|
||||
local _clear = _clear or nil
|
||||
local _time = _time or self.messageTime
|
||||
local m = MESSAGE:New(_text,_time,"Info",_clear):ToGroup(group)
|
||||
if _override or not self.suppressmessages then
|
||||
local m = MESSAGE:New(_text,_time,"Info",_clear):ToGroup(group)
|
||||
end
|
||||
-- integrate SRS
|
||||
if _speak and self.useSRS then
|
||||
local srstext = SOUNDTEXT:New(_text)
|
||||
@@ -1458,7 +1569,6 @@ function CSAR:_DisplayActiveSAR(_unitName)
|
||||
local _groupName = _value.name
|
||||
self:T(string.format("Display Active Pilot: %s", tostring(_groupName)))
|
||||
self:T({Table=_value})
|
||||
--local _woundedGroup = GROUP:FindByName(_groupName)
|
||||
local _woundedGroup = _value.group
|
||||
if _woundedGroup and _value.alive then
|
||||
local _coordinatesText = self:_GetPositionOfWounded(_woundedGroup)
|
||||
@@ -1472,7 +1582,11 @@ function CSAR:_DisplayActiveSAR(_unitName)
|
||||
else
|
||||
distancetext = string.format("%.1fkm", _distance/1000.0)
|
||||
end
|
||||
table.insert(_csarList, { dist = _distance, msg = string.format("%s at %s - %.2f KHz ADF - %s ", _value.desc, _coordinatesText, _value.frequency / 1000, distancetext) })
|
||||
if _value.frequency == 0 then--shagrat insert CASEVAC without Frequency
|
||||
table.insert(_csarList, { dist = _distance, msg = string.format("%s at %s - %s ", _value.desc, _coordinatesText, distancetext) })
|
||||
else
|
||||
table.insert(_csarList, { dist = _distance, msg = string.format("%s at %s - %.2f KHz ADF - %s ", _value.desc, _coordinatesText, _value.frequency / 1000, distancetext) })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1486,7 +1600,7 @@ function CSAR:_DisplayActiveSAR(_unitName)
|
||||
_msg = _msg .. "\n" .. _line.msg
|
||||
end
|
||||
|
||||
self:_DisplayMessageToSAR(_heli, _msg, self.messageTime*2)
|
||||
self:_DisplayMessageToSAR(_heli, _msg, self.messageTime*2, false, false, true)
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -1501,10 +1615,17 @@ function CSAR:_GetClosestDownedPilot(_heli)
|
||||
local _shortestDistance = -1
|
||||
local _distance = 0
|
||||
local _closestGroupInfo = nil
|
||||
local _heliCoord = _heli:GetCoordinate()
|
||||
local _heliCoord = _heli:GetCoordinate() or _heli:GetCoordinate()
|
||||
|
||||
if _heliCoord == nil then
|
||||
self:E("****Error obtaining coordinate!")
|
||||
return nil
|
||||
end
|
||||
|
||||
local DownedPilotsTable = self.downedPilots
|
||||
for _, _groupInfo in pairs(DownedPilotsTable) do
|
||||
|
||||
for _, _groupInfo in UTILS.spairs(DownedPilotsTable) do
|
||||
--for _, _groupInfo in pairs(DownedPilotsTable) do
|
||||
local _woundedName = _groupInfo.name
|
||||
local _tempWounded = _groupInfo.group
|
||||
|
||||
@@ -1537,7 +1658,7 @@ function CSAR:_SignalFlare(_unitName)
|
||||
local _closest = self:_GetClosestDownedPilot(_heli)
|
||||
local smokedist = 8000
|
||||
if self.approachdist_far > smokedist then smokedist = self.approachdist_far end
|
||||
if _closest ~= nil and _closest.pilot ~= nil and _closest.distance < smokedist then
|
||||
if _closest ~= nil and _closest.pilot ~= nil and _closest.distance > 0 and _closest.distance < smokedist then
|
||||
|
||||
local _clockDir = self:_GetClockDirection(_heli, _closest.pilot)
|
||||
local _distance = 0
|
||||
@@ -1547,7 +1668,7 @@ function CSAR:_SignalFlare(_unitName)
|
||||
_distance = string.format("%.1fkm",_closest.distance)
|
||||
end
|
||||
local _msg = string.format("%s - Popping signal flare at your %s o\'clock. Distance %s", _unitName, _clockDir, _distance)
|
||||
self:_DisplayMessageToSAR(_heli, _msg, self.messageTime, false, true)
|
||||
self:_DisplayMessageToSAR(_heli, _msg, self.messageTime, false, true, true)
|
||||
|
||||
local _coord = _closest.pilot:GetCoordinate()
|
||||
_coord:FlareRed(_clockDir)
|
||||
@@ -1558,7 +1679,7 @@ function CSAR:_SignalFlare(_unitName)
|
||||
else
|
||||
_distance = string.format("%.1fkm",smokedist/1000)
|
||||
end
|
||||
self:_DisplayMessageToSAR(_heli, string.format("No Pilots within %s",_distance), self.messageTime)
|
||||
self:_DisplayMessageToSAR(_heli, string.format("No Pilots within %s",_distance), self.messageTime, false, false, true)
|
||||
end
|
||||
return self
|
||||
end
|
||||
@@ -1570,12 +1691,11 @@ end
|
||||
-- @param #number _messagetime How long to show.
|
||||
function CSAR:_DisplayToAllSAR(_message, _side, _messagetime)
|
||||
self:T(self.lid .. " _DisplayToAllSAR")
|
||||
local messagetime = _messagetime or self.messageTime
|
||||
for _, _unitName in pairs(self.csarUnits) do
|
||||
local _unit = self:_GetSARHeli(_unitName)
|
||||
if _unit then
|
||||
if not _messagetime then
|
||||
self:_DisplayMessageToSAR(_unit, _message, _messagetime)
|
||||
end
|
||||
if _unit and not self.suppressmessages then
|
||||
self:_DisplayMessageToSAR(_unit, _message, _messagetime)
|
||||
end
|
||||
end
|
||||
return self
|
||||
@@ -1593,16 +1713,16 @@ function CSAR:_Reqsmoke( _unitName )
|
||||
local smokedist = 8000
|
||||
if smokedist < self.approachdist_far then smokedist = self.approachdist_far end
|
||||
local _closest = self:_GetClosestDownedPilot(_heli)
|
||||
if _closest ~= nil and _closest.pilot ~= nil and _closest.distance < smokedist then
|
||||
if _closest ~= nil and _closest.pilot ~= nil and _closest.distance > 0 and _closest.distance < smokedist then
|
||||
local _clockDir = self:_GetClockDirection(_heli, _closest.pilot)
|
||||
local _distance = 0
|
||||
if _SETTINGS:IsImperial() then
|
||||
_distance = string.format("%.1fnm",UTILS.MetersToNM(_closest.distance))
|
||||
else
|
||||
_distance = string.format("%.1fkm",_closest.distance)
|
||||
_distance = string.format("%.1fkm",_closest.distance/1000)
|
||||
end
|
||||
local _msg = string.format("%s - Popping signal smoke at your %s o\'clock. Distance %s", _unitName, _clockDir, _distance)
|
||||
self:_DisplayMessageToSAR(_heli, _msg, self.messageTime, false, true)
|
||||
local _msg = string.format("%s - Popping smoke at your %s o\'clock. Distance %s", _unitName, _clockDir, _distance)
|
||||
self:_DisplayMessageToSAR(_heli, _msg, self.messageTime, false, true, true)
|
||||
local _coord = _closest.pilot:GetCoordinate()
|
||||
local color = self.smokecolor
|
||||
_coord:Smoke(color)
|
||||
@@ -1613,7 +1733,7 @@ function CSAR:_Reqsmoke( _unitName )
|
||||
else
|
||||
_distance = string.format("%.1fkm",smokedist/1000)
|
||||
end
|
||||
self:_DisplayMessageToSAR(_heli, string.format("No Pilots within %s",_distance), self.messageTime)
|
||||
self:_DisplayMessageToSAR(_heli, string.format("No Pilots within %s",_distance), self.messageTime, false, false, true)
|
||||
end
|
||||
return self
|
||||
end
|
||||
@@ -1685,13 +1805,13 @@ function CSAR:_CheckOnboard(_unitName)
|
||||
--list onboard pilots
|
||||
local _inTransit = self.inTransitGroups[_unitName]
|
||||
if _inTransit == nil then
|
||||
self:_DisplayMessageToSAR(_unit, "No Rescued Pilots onboard", self.messageTime)
|
||||
self:_DisplayMessageToSAR(_unit, "No Rescued Pilots onboard", self.messageTime, false, false, true)
|
||||
else
|
||||
local _text = "Onboard - RTB to FARP/Airfield or MASH: "
|
||||
for _, _onboard in pairs(self.inTransitGroups[_unitName]) do
|
||||
_text = _text .. "\n" .. _onboard.desc
|
||||
end
|
||||
self:_DisplayMessageToSAR(_unit, _text, self.messageTime*2)
|
||||
self:_DisplayMessageToSAR(_unit, _text, self.messageTime*2, false, false, true)
|
||||
end
|
||||
return self
|
||||
end
|
||||
@@ -1747,9 +1867,20 @@ end
|
||||
function CSAR:_GetDistance(_point1, _point2)
|
||||
self:T(self.lid .. " _GetDistance")
|
||||
if _point1 and _point2 then
|
||||
local distance = _point1:DistanceFromPointVec2(_point2)
|
||||
return distance
|
||||
local distance1 = _point1:Get2DDistance(_point2)
|
||||
local distance2 = _point1:DistanceFromPointVec2(_point2)
|
||||
if distance1 and type(distance1) == "number" then
|
||||
return distance1
|
||||
elseif distance2 and type(distance2) == "number" then
|
||||
return distance2
|
||||
else
|
||||
self:E("*****Cannot calculate distance!")
|
||||
self:E({_point1,_point2})
|
||||
return -1
|
||||
end
|
||||
else
|
||||
self:E("******Cannot calculate distance!")
|
||||
self:E({_point1,_point2})
|
||||
return -1
|
||||
end
|
||||
end
|
||||
@@ -1758,7 +1889,6 @@ end
|
||||
-- @param #CSAR self
|
||||
function CSAR:_GenerateVHFrequencies()
|
||||
self:T(self.lid .. " _GenerateVHFrequencies")
|
||||
--local _skipFrequencies = self.SkipFrequencies
|
||||
|
||||
local FreeVHFFrequencies = {}
|
||||
FreeVHFFrequencies = UTILS.GenerateVHFrequencies()
|
||||
@@ -1798,7 +1928,6 @@ function CSAR:_GetClockDirection(_heli, _group)
|
||||
if _heading then
|
||||
local Aspect = Angle - _heading
|
||||
if Aspect == 0 then Aspect = 360 end
|
||||
--clock = math.floor(Aspect / 30)
|
||||
clock = math.abs(UTILS.Round((Aspect / 30),0))
|
||||
if clock == 0 then clock = 12 end
|
||||
end
|
||||
@@ -1898,6 +2027,7 @@ function CSAR:onafterStart(From, Event, To)
|
||||
self:HandleEvent(EVENTS.Takeoff, self._EventHandler)
|
||||
self:HandleEvent(EVENTS.Land, self._EventHandler)
|
||||
self:HandleEvent(EVENTS.Ejection, self._EventHandler)
|
||||
self:HandleEvent(EVENTS.LandingAfterEjection, self._EventHandler) --shagrat
|
||||
self:HandleEvent(EVENTS.PlayerEnterAircraft, self._EventHandler)
|
||||
self:HandleEvent(EVENTS.PlayerEnterUnit, self._EventHandler)
|
||||
self:HandleEvent(EVENTS.PilotDead, self._EventHandler)
|
||||
@@ -1907,6 +2037,10 @@ function CSAR:onafterStart(From, Event, To)
|
||||
else
|
||||
self.allheligroupset = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterCategoryHelicopter():FilterStart()
|
||||
end
|
||||
self.mash = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterPrefixes(self.mashprefix):FilterStart() -- currently only GROUP objects, maybe support STATICs also?
|
||||
if self.wetfeettemplate then
|
||||
self.usewetfeet = true
|
||||
end
|
||||
self:__Status(-10)
|
||||
return self
|
||||
end
|
||||
@@ -1915,19 +2049,19 @@ end
|
||||
-- @param #CSAR self
|
||||
function CSAR:_CheckDownedPilotTable()
|
||||
local pilots = self.downedPilots
|
||||
for _,_entry in pairs (pilots) do
|
||||
self:T("Checking for " .. _entry.name)
|
||||
self:T({entry=_entry})
|
||||
local group = _entry.group
|
||||
if not group:IsAlive() then
|
||||
self:T("Group is dead")
|
||||
if _entry.alive == true then
|
||||
self:T("Switching .alive to false")
|
||||
local npilots = {}
|
||||
|
||||
for _ind,_entry in pairs(pilots) do
|
||||
local _group = _entry.group
|
||||
if _group:IsAlive() then
|
||||
npilots[_ind] = _entry
|
||||
else
|
||||
if _entry.alive then
|
||||
self:__KIA(1,_entry.desc)
|
||||
self:_RemoveNameFromDownedPilots(_entry.name,true)
|
||||
end
|
||||
end
|
||||
end
|
||||
self.downedPilots = npilots
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -1940,7 +2074,12 @@ function CSAR:onbeforeStatus(From, Event, To)
|
||||
self:T({From, Event, To})
|
||||
-- housekeeping
|
||||
self:_AddMedevacMenuItem()
|
||||
self:_RefreshRadioBeacons()
|
||||
|
||||
if not self.BeaconTimer or (self.BeaconTimer and not self.BeaconTimer:IsRunning()) then
|
||||
self.BeaconTimer = TIMER:New(self._RefreshRadioBeacons,self)
|
||||
self.BeaconTimer:Start(2,self.beaconRefresher)
|
||||
end
|
||||
|
||||
self:_CheckDownedPilotTable()
|
||||
for _,_sar in pairs (self.csarUnits) do
|
||||
local PilotTable = self.downedPilots
|
||||
@@ -2007,6 +2146,7 @@ function CSAR:onafterStop(From, Event, To)
|
||||
self:UnHandleEvent(EVENTS.Takeoff)
|
||||
self:UnHandleEvent(EVENTS.Land)
|
||||
self:UnHandleEvent(EVENTS.Ejection)
|
||||
self:UnHandleEvent(EVENTS.LandingAfterEjection) -- shagrat
|
||||
self:UnHandleEvent(EVENTS.PlayerEnterUnit)
|
||||
self:UnHandleEvent(EVENTS.PlayerEnterAircraft)
|
||||
self:UnHandleEvent(EVENTS.PilotDead)
|
||||
@@ -2047,9 +2187,10 @@ end
|
||||
-- @param #string To To state.
|
||||
-- @param #string Heliname Name of the helicopter group.
|
||||
-- @param #string Woundedgroupname Name of the downed pilot\'s group.
|
||||
function CSAR:onbeforeReturning(From, Event, To, Heliname, Woundedgroupname)
|
||||
-- @param #boolean IsAirport True if heli has landed on an AFB (from event land).
|
||||
function CSAR:onbeforeReturning(From, Event, To, Heliname, Woundedgroupname, IsAirPort)
|
||||
self:T({From, Event, To, Heliname, Woundedgroupname})
|
||||
self:_ScheduledSARFlight(Heliname,Woundedgroupname)
|
||||
self:_ScheduledSARFlight(Heliname,Woundedgroupname, IsAirPort)
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -802,7 +802,7 @@ function RESCUEHELO:_OnEventCrashOrEject(EventData)
|
||||
-- Debug.
|
||||
local text=string.format("Unit %s crashed or ejected.", unitname)
|
||||
MESSAGE:New(text, 10, "DEBUG"):ToAllIf(self.Debug)
|
||||
self:I(self.lid..text)
|
||||
self:T(self.lid..text)
|
||||
|
||||
-- Get coordinate of unit.
|
||||
local coord=unit:GetCoordinate()
|
||||
|
||||
@@ -157,7 +157,7 @@ end
|
||||
--- Set the frequency for the radio transmission.
|
||||
-- If the transmitting positionable is a unit or group, this also set the command "SetFrequency" with the defined frequency and modulation.
|
||||
-- @param #RADIO self
|
||||
-- @param #number Frequency Frequency in MHz. Ranges allowed for radio transmissions in DCS : 30-87.995 / 108-173.995 / 225-399.975MHz.
|
||||
-- @param #number Frequency Frequency in MHz.
|
||||
-- @return #RADIO self
|
||||
function RADIO:SetFrequency(Frequency)
|
||||
self:F2(Frequency)
|
||||
@@ -165,7 +165,7 @@ function RADIO:SetFrequency(Frequency)
|
||||
if type(Frequency) == "number" then
|
||||
|
||||
-- If frequency is in range
|
||||
if (Frequency >= 30 and Frequency <= 87.995) or (Frequency >= 108 and Frequency <= 173.995) or (Frequency >= 225 and Frequency <= 399.975) then
|
||||
-- if (Frequency >= 30 and Frequency <= 87.995) or (Frequency >= 108 and Frequency <= 173.995) or (Frequency >= 225 and Frequency <= 399.975) then
|
||||
|
||||
-- Convert frequency from MHz to Hz
|
||||
self.Frequency = Frequency * 1000000
|
||||
@@ -186,10 +186,10 @@ function RADIO:SetFrequency(Frequency)
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
-- end
|
||||
end
|
||||
|
||||
self:E({"Frequency is outside of DCS Frequency ranges (30-80, 108-152, 225-400). Frequency unchanged.", Frequency})
|
||||
self:E({"Frequency is not a number. Frequency unchanged.", Frequency})
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
@@ -162,33 +162,32 @@ RADIOSPEECH.Vocabulary.RU = {
|
||||
["8000"] = { "8000", 0.92 },
|
||||
["9000"] = { "9000", 0.87 },
|
||||
|
||||
["Ñ<EFBFBD>тепени"] = { "degrees", 0.5 },
|
||||
["километров"] = { "kilometers", 0.65 },
|
||||
["градусы"] = { "degrees", 0.5 },
|
||||
["километры"] = { "kilometers", 0.65 },
|
||||
["km"] = { "kilometers", 0.65 },
|
||||
["миль"] = { "miles", 0.45 },
|
||||
["мили"] = { "miles", 0.45 },
|
||||
["mi"] = { "miles", 0.45 },
|
||||
["метры"] = { "meters", 0.41 },
|
||||
["метров"] = { "meters", 0.41 },
|
||||
["m"] = { "meters", 0.41 },
|
||||
["ноги"] = { "feet", 0.37 },
|
||||
["ноги"] = { "feet", 0.37 },
|
||||
|
||||
["br"] = { "br", 1.1 },
|
||||
["bra"] = { "bra", 0.3 },
|
||||
|
||||
["возвращение на базу"] = { "returning_to_base", 1.40 },
|
||||
["на пути к наземной цели"] = { "on_route_to_ground_target", 1.45 },
|
||||
["перехват боги"] = { "intercepting_bogeys", 1.22 },
|
||||
["поражение наземной цели"] = { "engaging_ground_target", 1.53 },
|
||||
["привлечение болотных птиц"] = { "engaging_bogeys", 1.68 },
|
||||
["колёса вверх..."] = { "wheels_up", 0.92 },
|
||||
["посадка на базу"] = { "landing at base", 1.04 },
|
||||
["патрулирование"] = { "patrolling", 0.96 },
|
||||
|
||||
["возвращаÑ<EFBFBD>Ñ<EFBFBD>ÑŒ на базу"] = { "returning_to_base", 1.40 },
|
||||
["на пути к наземной цели"] = { "on_route_to_ground_target", 1.45 },
|
||||
["перехват Ñ<>амолетов"] = { "intercepting_bogeys", 1.22 },
|
||||
["поражение наземной цели"] = { "engaging_ground_target", 1.53 },
|
||||
["захватывающие Ñ<>амолеты"] = { "engaging_bogeys", 1.68 },
|
||||
["колеÑ<EFBFBD>а вверх"] = { "wheels_up", 0.92 },
|
||||
["поÑ<EFBFBD>адка на базу"] = { "landing at base", 1.04 },
|
||||
["патрулирующий"] = { "patrolling", 0.96 },
|
||||
|
||||
["за"] = { "for", 0.27 },
|
||||
["и"] = { "and", 0.17 },
|
||||
["в"] = { "at", 0.19 },
|
||||
["dot"] = { "dot", 0.51 },
|
||||
["defender"] = { "defender", 0.45 },
|
||||
["для"] = { "for", 0.27 },
|
||||
["и"] = { "and", 0.17 },
|
||||
["на сайте"] = { "at", 0.19 },
|
||||
["точка"] = { "dot", 0.51 },
|
||||
["защитник"] = { "defender", 0.45 },
|
||||
}
|
||||
|
||||
--- Create a new RADIOSPEECH object for a given radio frequency/modulation.
|
||||
|
||||
@@ -88,10 +88,19 @@
|
||||
--
|
||||
-- Use a specific "culture" with the @{#MSRS.SetCulture} function, e.g. `:SetCulture("en-US")` or `:SetCulture("de-DE")`.
|
||||
--
|
||||
-- ## Set Google
|
||||
--
|
||||
-- Use Google's text-to-speech engine with the @{#MSRS.SetGoogle} function, e.g. ':SetGoogle()'.
|
||||
-- By enabling this it also allows you to utilize SSML in your text for added flexibilty.
|
||||
-- For more information on setting up a cloud account, visit: https://cloud.google.com/text-to-speech
|
||||
-- Google's supported SSML reference: https://cloud.google.com/text-to-speech/docs/ssml
|
||||
--
|
||||
-- ## Set Voice
|
||||
--
|
||||
-- Use a specifc voice with the @{#MSRS.SetVoice} function, e.g, `:SetVoice("Microsoft Hedda Desktop")`.
|
||||
-- Note that this must be installed on your windows system.
|
||||
-- If enabling SetGoogle(), you can use voices provided by Google
|
||||
-- Google's supported voices: https://cloud.google.com/text-to-speech/docs/voices
|
||||
--
|
||||
-- ## Set Coordinate
|
||||
--
|
||||
@@ -678,7 +687,7 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp
|
||||
|
||||
-- Set google.
|
||||
if self.google then
|
||||
command=command..string.format(' -G "%s"', self.google)
|
||||
command=command..string.format(' --ssml -G "%s"', self.google)
|
||||
end
|
||||
|
||||
-- Debug output.
|
||||
|
||||
@@ -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 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 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
|
||||
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,13 +1,13 @@
|
||||
--- **Tasking** - The TASK_A2A models tasks for players in Air to Air engagements.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ### Contributions:
|
||||
--
|
||||
--
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- @module Tasking.Task_A2A
|
||||
-- @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.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.
|
||||
--
|
||||
--
|
||||
-- @field #TASK_A2A
|
||||
TASK_A2A = {
|
||||
ClassName = "TASK_A2A",
|
||||
ClassName = "TASK_A2A"
|
||||
}
|
||||
|
||||
|
||||
--- Instantiates a new TASK_A2A.
|
||||
-- @param #TASK_A2A self
|
||||
-- @param Tasking.Mission#MISSION Mission
|
||||
@@ -54,51 +54,49 @@ do -- TASK_A2A
|
||||
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
|
||||
self:F()
|
||||
|
||||
|
||||
self.TargetSetUnit = TargetSetUnit
|
||||
self.TaskType = TaskType
|
||||
|
||||
local Fsm = self:GetUnitProcess()
|
||||
|
||||
|
||||
Fsm:AddTransition( "Assigned", "RouteToRendezVous", "RoutingToRendezVous" )
|
||||
Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtRendezVous" } )
|
||||
Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtRendezVous" } )
|
||||
|
||||
Fsm:AddProcess( "RoutingToRendezVous", "RouteToRendezVousPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtRendezVous" } )
|
||||
Fsm:AddProcess( "RoutingToRendezVous", "RouteToRendezVousZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtRendezVous" } )
|
||||
|
||||
Fsm:AddTransition( { "Arrived", "RoutingToRendezVous" }, "ArriveAtRendezVous", "ArrivedAtRendezVous" )
|
||||
|
||||
|
||||
Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "Engage", "Engaging" )
|
||||
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:AddProcess( "Engaging", "RouteToTargetZone", ACT_ROUTE_ZONE:New(), {} )
|
||||
Fsm:AddProcess( "Engaging", "RouteToTargetPoint", ACT_ROUTE_POINT:New(), {} )
|
||||
Fsm:AddTransition( "Engaging", "RouteToTargets", "Engaging" )
|
||||
|
||||
-- Fsm:AddTransition( "Accounted", "DestroyedAll", "Accounted" )
|
||||
-- Fsm:AddTransition( "Accounted", "Success", "Success" )
|
||||
|
||||
-- Fsm:AddTransition( "Accounted", "DestroyedAll", "Accounted" )
|
||||
-- Fsm:AddTransition( "Accounted", "Success", "Success" )
|
||||
Fsm:AddTransition( "Rejected", "Reject", "Aborted" )
|
||||
Fsm:AddTransition( "Failed", "Fail", "Failed" )
|
||||
|
||||
|
||||
---- @param #FSM_PROCESS self
|
||||
-- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param #TASK_CARGO Task
|
||||
function Fsm:OnLeaveAssigned( TaskUnit, Task )
|
||||
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
||||
|
||||
|
||||
self:SelectAction()
|
||||
end
|
||||
|
||||
--- Test
|
||||
|
||||
--- Test
|
||||
-- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task_A2A#TASK_A2A Task
|
||||
function Fsm:onafterRouteToRendezVous( TaskUnit, Task )
|
||||
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
||||
-- Determine the first Unit from the self.RendezVousSetUnit
|
||||
|
||||
|
||||
if Task:GetRendezVousZone( TaskUnit ) then
|
||||
self:__RouteToRendezVousZone( 0.1 )
|
||||
else
|
||||
@@ -110,36 +108,36 @@ do -- TASK_A2A
|
||||
end
|
||||
end
|
||||
|
||||
--- Test
|
||||
--- Test
|
||||
-- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task#TASK_A2A Task
|
||||
function Fsm:OnAfterArriveAtRendezVous( TaskUnit, Task )
|
||||
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
||||
-- Determine the first Unit from the self.TargetSetUnit
|
||||
|
||||
self:__Engage( 0.1 )
|
||||
|
||||
self:__Engage( 0.1 )
|
||||
end
|
||||
|
||||
--- Test
|
||||
|
||||
--- Test
|
||||
-- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task#TASK_A2A Task
|
||||
function Fsm:onafterEngage( TaskUnit, Task )
|
||||
self:F( { self } )
|
||||
self:__Account( 0.1 )
|
||||
self:__RouteToTarget(0.1 )
|
||||
self:__RouteToTarget( 0.1 )
|
||||
self:__RouteToTargets( -10 )
|
||||
end
|
||||
|
||||
--- Test
|
||||
|
||||
--- Test
|
||||
-- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task_A2A#TASK_A2A Task
|
||||
function Fsm:onafterRouteToTarget( TaskUnit, Task )
|
||||
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
||||
-- Determine the first Unit from the self.TargetSetUnit
|
||||
|
||||
|
||||
if Task:GetTargetZone( TaskUnit ) then
|
||||
self:__RouteToTargetZone( 0.1 )
|
||||
else
|
||||
@@ -152,8 +150,8 @@ do -- TASK_A2A
|
||||
self:__RouteToTargetPoint( 0.1 )
|
||||
end
|
||||
end
|
||||
|
||||
--- Test
|
||||
|
||||
--- Test
|
||||
-- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task_A2A#TASK_A2A Task
|
||||
@@ -165,20 +163,18 @@ do -- TASK_A2A
|
||||
end
|
||||
self:__RouteToTargets( -10 )
|
||||
end
|
||||
|
||||
|
||||
return self
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- @param #TASK_A2A self
|
||||
-- @param Core.Set#SET_UNIT TargetSetUnit The set of targets.
|
||||
function TASK_A2A:SetTargetSetUnit( TargetSetUnit )
|
||||
|
||||
|
||||
self.TargetSetUnit = TargetSetUnit
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- @param #TASK_A2A self
|
||||
function TASK_A2A:GetPlannedMenuText()
|
||||
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 #number RendezVousRange The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point.
|
||||
-- @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 ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT
|
||||
ActRouteRendezVous:SetCoordinate( RendezVousCoordinate )
|
||||
ActRouteRendezVous:SetRange( RendezVousRange )
|
||||
end
|
||||
|
||||
|
||||
--- @param #TASK_A2A self
|
||||
-- @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 #number The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point.
|
||||
function TASK_A2A:GetRendezVousCoordinate( TaskUnit )
|
||||
|
||||
|
||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||
|
||||
local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT
|
||||
return ActRouteRendezVous:GetCoordinate(), ActRouteRendezVous:GetRange()
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
--- @param #TASK_A2A self
|
||||
-- @param Core.Zone#ZONE_BASE RendezVousZone The Zone object where the RendezVous is located on the map.
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
function TASK_A2A:SetRendezVousZone( RendezVousZone, TaskUnit )
|
||||
|
||||
|
||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||
|
||||
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
|
||||
return ActRouteRendezVous:GetZone()
|
||||
end
|
||||
|
||||
|
||||
--- @param #TASK_A2A self
|
||||
-- @param Core.Point#COORDINATE TargetCoordinate The Coordinate object where the Target is located on the map.
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
function TASK_A2A:SetTargetCoordinate( TargetCoordinate, TaskUnit )
|
||||
|
||||
|
||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||
|
||||
local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT
|
||||
ActRouteTarget:SetCoordinate( TargetCoordinate )
|
||||
end
|
||||
|
||||
|
||||
--- @param #TASK_A2A self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
@@ -256,18 +249,16 @@ do -- TASK_A2A
|
||||
return ActRouteTarget:GetCoordinate()
|
||||
end
|
||||
|
||||
|
||||
--- @param #TASK_A2A self
|
||||
-- @param Core.Zone#ZONE_BASE TargetZone The Zone object where the Target is located on the map.
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
function TASK_A2A:SetTargetZone( TargetZone, Altitude, Heading, TaskUnit )
|
||||
|
||||
|
||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||
|
||||
local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE
|
||||
ActRouteTarget:SetZone( TargetZone, Altitude, Heading )
|
||||
end
|
||||
|
||||
|
||||
--- @param #TASK_A2A self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
@@ -281,45 +272,43 @@ do -- TASK_A2A
|
||||
end
|
||||
|
||||
function TASK_A2A:SetGoalTotal()
|
||||
|
||||
|
||||
self.GoalTotal = self.TargetSetUnit:Count()
|
||||
end
|
||||
|
||||
function TASK_A2A:GetGoalTotal()
|
||||
|
||||
|
||||
return self.GoalTotal
|
||||
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.
|
||||
-- @param #TASK_A2A self
|
||||
function TASK_A2A:ReportOrder( ReportGroup )
|
||||
self:UpdateTaskInfo( self.DetectedItem )
|
||||
|
||||
self:UpdateTaskInfo( self.DetectedItem )
|
||||
|
||||
local Coordinate = self.TaskInfo:GetData( "Coordinate" )
|
||||
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
|
||||
|
||||
|
||||
return Distance
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- This method checks every 10 seconds if the goal has been reached of the task.
|
||||
-- @param #TASK_A2A self
|
||||
function TASK_A2A:onafterGoal( TaskUnit, From, Event, To )
|
||||
local TargetSetUnit = self.TargetSetUnit -- Core.Set#SET_UNIT
|
||||
|
||||
|
||||
if TargetSetUnit:Count() == 0 then
|
||||
self:Success()
|
||||
end
|
||||
|
||||
|
||||
self:__Goal( -10 )
|
||||
end
|
||||
|
||||
|
||||
--- @param #TASK_A2A self
|
||||
function TASK_A2A:UpdateTaskInfo( DetectedItem )
|
||||
|
||||
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:AddCoordinate( TargetCoordinate, 1, "SOD" )
|
||||
|
||||
@@ -343,12 +332,12 @@ do -- TASK_A2A
|
||||
end
|
||||
end
|
||||
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
|
||||
local DetectedItemsCount = self.TargetSetUnit:Count()
|
||||
local DetectedItemsTypes = self.TargetSetUnit:GetTypeNames()
|
||||
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
|
||||
@@ -359,8 +348,8 @@ do -- TASK_A2A
|
||||
-- @param Tasking.CommandCenter#COMMANDCENTER CommandCenter The command center.
|
||||
-- @param Wrapper.Group#GROUP TaskGroup The player group.
|
||||
function TASK_A2A:GetAutoAssignPriority( AutoAssignMethod, CommandCenter, TaskGroup )
|
||||
|
||||
if AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Random then
|
||||
|
||||
if AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Random then
|
||||
return math.random( 1, 9 )
|
||||
elseif AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Distance then
|
||||
local Coordinate = self.TaskInfo:GetData( "Coordinate" )
|
||||
@@ -373,8 +362,7 @@ do -- TASK_A2A
|
||||
return 0
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
do -- TASK_A2A_INTERCEPT
|
||||
|
||||
@@ -384,44 +372,39 @@ do -- TASK_A2A_INTERCEPT
|
||||
-- @extends Tasking.Task#TASK
|
||||
|
||||
--- 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!
|
||||
--
|
||||
-- The TASK_A2A_INTERCEPT is used by the @{Tasking.Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create intercept tasks
|
||||
-- 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
|
||||
-- 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 given a name and a briefing, that is used in the menu structure and in the reporting.
|
||||
--
|
||||
--
|
||||
-- @field #TASK_A2A_INTERCEPT
|
||||
TASK_A2A_INTERCEPT = {
|
||||
ClassName = "TASK_A2A_INTERCEPT",
|
||||
ClassName = "TASK_A2A_INTERCEPT"
|
||||
}
|
||||
|
||||
|
||||
|
||||
--- Instantiates a new TASK_A2A_INTERCEPT.
|
||||
-- @param #TASK_A2A_INTERCEPT self
|
||||
-- @param Tasking.Mission#MISSION Mission
|
||||
-- @param Core.Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned.
|
||||
-- @param #string TaskName The name of the Task.
|
||||
-- @param Core.Set#SET_UNIT TargetSetUnit
|
||||
-- @param Core.Set#SET_UNIT TargetSetUnit
|
||||
-- @param #string TaskBriefing The briefing of the task.
|
||||
-- @return #TASK_A2A_INTERCEPT
|
||||
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
|
||||
self:F()
|
||||
|
||||
|
||||
Mission:AddTask( self )
|
||||
|
||||
self:SetBriefing(
|
||||
TaskBriefing or
|
||||
"Intercept incoming intruders.\n"
|
||||
)
|
||||
|
||||
self:SetBriefing( TaskBriefing or "Intercept incoming intruders.\n" )
|
||||
|
||||
return self
|
||||
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 #string PlayerName The name of the player.
|
||||
-- @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 )
|
||||
|
||||
ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has intercepted a target.", Score )
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -449,7 +432,7 @@ do -- TASK_A2A_INTERCEPT
|
||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||
|
||||
ProcessUnit:AddScore( "Success", "All targets have been successfully intercepted!", Score )
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -465,14 +448,12 @@ do -- TASK_A2A_INTERCEPT
|
||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||
|
||||
ProcessUnit:AddScore( "Failed", "The intercept has failed!", Penalty )
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
do -- TASK_A2A_SWEEP
|
||||
|
||||
--- 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.
|
||||
-- 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.
|
||||
--
|
||||
-- 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.
|
||||
--
|
||||
--
|
||||
-- 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.
|
||||
--
|
||||
--
|
||||
-- @field #TASK_A2A_SWEEP
|
||||
TASK_A2A_SWEEP = {
|
||||
ClassName = "TASK_A2A_SWEEP",
|
||||
ClassName = "TASK_A2A_SWEEP"
|
||||
}
|
||||
|
||||
|
||||
|
||||
--- Instantiates a new TASK_A2A_SWEEP.
|
||||
-- @param #TASK_A2A_SWEEP self
|
||||
-- @param Tasking.Mission#MISSION Mission
|
||||
@@ -509,29 +488,26 @@ do -- TASK_A2A_SWEEP
|
||||
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
|
||||
self:F()
|
||||
|
||||
|
||||
Mission:AddTask( self )
|
||||
|
||||
self:SetBriefing(
|
||||
TaskBriefing or
|
||||
"Perform a fighter sweep. Incoming intruders were detected and could be hiding at the location.\n"
|
||||
)
|
||||
|
||||
self:SetBriefing( TaskBriefing or "Perform a fighter sweep. Incoming intruders were detected and could be hiding at the location.\n" )
|
||||
|
||||
return self
|
||||
end
|
||||
end
|
||||
|
||||
--- @param #TASK_A2A_SWEEP self
|
||||
function TASK_A2A_SWEEP:onafterGoal( TaskUnit, From, Event, To )
|
||||
local TargetSetUnit = self.TargetSetUnit -- Core.Set#SET_UNIT
|
||||
|
||||
|
||||
if TargetSetUnit:Count() == 0 then
|
||||
self:Success()
|
||||
end
|
||||
|
||||
|
||||
self:__Goal( -10 )
|
||||
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 #string PlayerName The name of the player.
|
||||
-- @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 )
|
||||
|
||||
ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has sweeped a target.", Score )
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -559,7 +535,7 @@ do -- TASK_A2A_SWEEP
|
||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||
|
||||
ProcessUnit:AddScore( "Success", "All targets have been successfully sweeped!", Score )
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -575,13 +551,12 @@ do -- TASK_A2A_SWEEP
|
||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||
|
||||
ProcessUnit:AddScore( "Failed", "The sweep has failed!", Penalty )
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
do -- TASK_A2A_ENGAGE
|
||||
|
||||
--- The TASK_A2A_ENGAGE class
|
||||
@@ -591,42 +566,37 @@ do -- TASK_A2A_ENGAGE
|
||||
|
||||
--- 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!
|
||||
--
|
||||
-- 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.
|
||||
--
|
||||
--
|
||||
-- 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.
|
||||
--
|
||||
--
|
||||
-- @field #TASK_A2A_ENGAGE
|
||||
TASK_A2A_ENGAGE = {
|
||||
ClassName = "TASK_A2A_ENGAGE",
|
||||
ClassName = "TASK_A2A_ENGAGE"
|
||||
}
|
||||
|
||||
|
||||
|
||||
--- Instantiates a new TASK_A2A_ENGAGE.
|
||||
-- @param #TASK_A2A_ENGAGE self
|
||||
-- @param Tasking.Mission#MISSION Mission
|
||||
-- @param Core.Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned.
|
||||
-- @param #string TaskName The name of the Task.
|
||||
-- @param Core.Set#SET_UNIT TargetSetUnit
|
||||
-- @param Core.Set#SET_UNIT TargetSetUnit
|
||||
-- @param #string TaskBriefing The briefing of the task.
|
||||
-- @return #TASK_A2A_ENGAGE self
|
||||
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
|
||||
self:F()
|
||||
|
||||
|
||||
Mission:AddTask( self )
|
||||
|
||||
self:SetBriefing(
|
||||
TaskBriefing or
|
||||
"Bogeys are nearby! Players close by are ordered to ENGAGE the intruders!\n"
|
||||
)
|
||||
|
||||
self:SetBriefing( TaskBriefing or "Bogeys are nearby! Players close by are ordered to ENGAGE the intruders!\n" )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Set a score when a target in scope of the A2A attack, has been destroyed .
|
||||
-- @param #TASK_A2A_ENGAGE self
|
||||
-- @param #string PlayerName The name of the player.
|
||||
@@ -639,7 +609,7 @@ do -- TASK_A2A_ENGAGE
|
||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||
|
||||
ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has engaged and destroyed a target.", Score )
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -655,7 +625,7 @@ do -- TASK_A2A_ENGAGE
|
||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||
|
||||
ProcessUnit:AddScore( "Success", "All targets have been successfully engaged!", Score )
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -671,7 +641,7 @@ do -- TASK_A2A_ENGAGE
|
||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||
|
||||
ProcessUnit:AddScore( "Failed", "The target engagement has failed!", Penalty )
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
--- **Tasking** - Dynamically allocates A2A tasks to human players, based on detected airborne targets through an EWR network.
|
||||
--
|
||||
--
|
||||
-- **Features:**
|
||||
--
|
||||
--
|
||||
-- * Dynamically assign tasks to human players based on detected targets.
|
||||
-- * 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.
|
||||
@@ -11,15 +11,15 @@
|
||||
-- * Define different ranges to engage upon intruders.
|
||||
-- * Keep task achievements.
|
||||
-- * Score task achievements.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ### Contributions:
|
||||
--
|
||||
--
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- @module Tasking.Task_A2A_Dispatcher
|
||||
-- @image Task_A2A_Dispatcher.JPG
|
||||
|
||||
@@ -30,72 +30,72 @@ do -- TASK_A2A_DISPATCHER
|
||||
-- @extends Tasking.DetectionManager#DETECTION_MANAGER
|
||||
|
||||
--- 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.
|
||||
-- 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.
|
||||
-- * **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.
|
||||
--
|
||||
--
|
||||
-- ## 1. TASK\_A2A\_DISPATCHER constructor:
|
||||
--
|
||||
--
|
||||
-- The @{#TASK_A2A_DISPATCHER.New}() method creates a new TASK\_A2A\_DISPATCHER instance.
|
||||
--
|
||||
--
|
||||
-- ### 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.
|
||||
--
|
||||
--
|
||||
-- local HQ = GROUP:FindByName( "HQ", "Bravo" )
|
||||
-- local CommandCenter = COMMANDCENTER:New( HQ, "Lima" )
|
||||
-- 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.
|
||||
-- Create the MISSION object, and hook it under the command center.
|
||||
--
|
||||
--
|
||||
-- ### 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.
|
||||
--
|
||||
--
|
||||
-- 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.
|
||||
-- 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**:
|
||||
--
|
||||
--
|
||||
-- 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.
|
||||
--
|
||||
--
|
||||
-- 
|
||||
--
|
||||
--
|
||||
-- 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).
|
||||
-- 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
|
||||
-- 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.
|
||||
-- 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.
|
||||
-- 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.
|
||||
-- It all depends on what the desired effect is.
|
||||
--
|
||||
--
|
||||
-- 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
|
||||
-- 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
|
||||
-- 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.
|
||||
--
|
||||
-- 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.
|
||||
--
|
||||
--
|
||||
-- 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 EWRDetection = DETECTION_AREAS:New( EWRSet, 6000 )
|
||||
@@ -104,50 +104,50 @@ do -- TASK_A2A_DISPATCHER
|
||||
--
|
||||
-- -- Setup the A2A dispatcher, and initialize it.
|
||||
-- A2ADispatcher = TASK_A2A_DISPATCHER:New( Mission, AttackGroups, EWRDetection )
|
||||
--
|
||||
--
|
||||
-- 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 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.
|
||||
--
|
||||
--
|
||||
-- ### 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 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.
|
||||
--
|
||||
--
|
||||
-- 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!
|
||||
--
|
||||
--
|
||||
-- ## 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.
|
||||
--
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- So, if there is a target area detected and reported,
|
||||
-- then any friendlies that are airborne near this target area,
|
||||
--
|
||||
-- So, if there is a target area detected and reported,
|
||||
-- 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).
|
||||
-- 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.
|
||||
-- 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 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**:
|
||||
--
|
||||
--
|
||||
-- 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_,
|
||||
-- when the player reaches certain achievements in the task.
|
||||
--
|
||||
--
|
||||
-- The prototype to handle the **Assign** event needs to be developed as follows:
|
||||
--
|
||||
--
|
||||
-- TaskDispatcher = TASK_A2A_DISPATCHER:New( ... )
|
||||
--
|
||||
--
|
||||
-- --- @param #TaskDispatcher self
|
||||
-- -- @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.
|
||||
@@ -160,22 +160,22 @@ do -- TASK_A2A_DISPATCHER
|
||||
-- Task:SetScoreOnSuccess( PlayerName, 200, TaskUnit )
|
||||
-- Task:SetScoreOnFail( PlayerName, -100, TaskUnit )
|
||||
-- end
|
||||
--
|
||||
--
|
||||
-- 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.
|
||||
-- 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:
|
||||
--
|
||||
--
|
||||
-- * @{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.
|
||||
--
|
||||
-- * @{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**.
|
||||
--
|
||||
-- * @{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!
|
||||
--
|
||||
--
|
||||
-- @field #TASK_A2A_DISPATCHER
|
||||
TASK_A2A_DISPATCHER = {
|
||||
ClassName = "TASK_A2A_DISPATCHER",
|
||||
@@ -184,8 +184,7 @@ do -- TASK_A2A_DISPATCHER
|
||||
Tasks = {},
|
||||
SweepZones = {},
|
||||
}
|
||||
|
||||
|
||||
|
||||
--- TASK_A2A_DISPATCHER constructor.
|
||||
-- @param #TASK_A2A_DISPATCHER self
|
||||
-- @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.
|
||||
-- @return #TASK_A2A_DISPATCHER self
|
||||
function TASK_A2A_DISPATCHER:New( Mission, SetGroup, Detection )
|
||||
|
||||
|
||||
-- Inherits from DETECTION_MANAGER
|
||||
local self = BASE:Inherit( self, DETECTION_MANAGER:New( SetGroup, Detection ) ) -- #TASK_A2A_DISPATCHER
|
||||
|
||||
|
||||
self.Detection = Detection
|
||||
self.Mission = Mission
|
||||
self.FlashNewTask = false
|
||||
|
||||
|
||||
-- TODO: Check detection through radar.
|
||||
self.Detection:FilterCategories( Unit.Category.AIRPLANE, Unit.Category.HELICOPTER )
|
||||
self.Detection:InitDetectRadar( true )
|
||||
self.Detection:SetRefreshTimeInterval( 30 )
|
||||
|
||||
|
||||
self:AddTransition( "Started", "Assign", "Started" )
|
||||
|
||||
|
||||
--- OnAfter Transition Handler for Event Assign.
|
||||
-- @function [parent=#TASK_A2A_DISPATCHER] OnAfterAssign
|
||||
-- @param #TASK_A2A_DISPATCHER self
|
||||
@@ -220,14 +218,13 @@ do -- TASK_A2A_DISPATCHER
|
||||
-- @param #string PlayerName
|
||||
|
||||
self:__Start( 5 )
|
||||
|
||||
|
||||
return self
|
||||
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.
|
||||
-- So, if there is a target area detected and reported,
|
||||
-- then any friendlies that are airborne near this target area,
|
||||
-- So, if there is a target area detected and reported,
|
||||
-- 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).
|
||||
-- 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,
|
||||
@@ -239,27 +236,27 @@ do -- TASK_A2A_DISPATCHER
|
||||
-- @param #number EngageRadius (Optional, Default = 100000) The radius to report friendlies near the target.
|
||||
-- @return #TASK_A2A_DISPATCHER
|
||||
-- @usage
|
||||
--
|
||||
--
|
||||
-- -- Set 50km as the radius to engage any target by airborne friendlies.
|
||||
-- TaskA2ADispatcher:SetEngageRadius( 50000 )
|
||||
--
|
||||
--
|
||||
-- -- Set 100km as the radius to engage any target by airborne friendlies.
|
||||
-- TaskA2ADispatcher:SetEngageRadius() -- 100000 is the default value.
|
||||
--
|
||||
--
|
||||
function TASK_A2A_DISPATCHER:SetEngageRadius( EngageRadius )
|
||||
|
||||
self.Detection:SetFriendliesRange( EngageRadius or 100000 )
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set flashing player messages on or off
|
||||
-- @param #TASK_A2A_DISPATCHER self
|
||||
-- @param #boolean onoff Set messages on (true) or off (false)
|
||||
function TASK_A2A_DISPATCHER:SetSendMessages( onoff )
|
||||
self.FlashNewTask = onoff
|
||||
self.FlashNewTask = onoff
|
||||
end
|
||||
|
||||
|
||||
--- Creates an INTERCEPT task when there are targets for it.
|
||||
-- @param #TASK_A2A_DISPATCHER self
|
||||
-- @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.
|
||||
function TASK_A2A_DISPATCHER:EvaluateINTERCEPT( DetectedItem )
|
||||
self:F( { DetectedItem.ItemID } )
|
||||
|
||||
|
||||
local DetectedSet = DetectedItem.Set
|
||||
local DetectedZone = DetectedItem.Zone
|
||||
|
||||
-- Check if there is at least one UNIT in the DetectedSet is visible.
|
||||
|
||||
|
||||
if DetectedItem.IsDetected == true then
|
||||
|
||||
-- Here we're doing something advanced... We're copying the DetectedSet.
|
||||
local TargetSetUnit = SET_UNIT:New()
|
||||
TargetSetUnit:SetDatabase( DetectedSet )
|
||||
TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection.
|
||||
|
||||
|
||||
return TargetSetUnit
|
||||
end
|
||||
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Creates an SWEEP task when there are targets for it.
|
||||
-- @param #TASK_A2A_DISPATCHER self
|
||||
-- @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.
|
||||
function TASK_A2A_DISPATCHER:EvaluateSWEEP( DetectedItem )
|
||||
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
|
||||
|
||||
@@ -305,14 +300,13 @@ do -- TASK_A2A_DISPATCHER
|
||||
local TargetSetUnit = SET_UNIT:New()
|
||||
TargetSetUnit:SetDatabase( DetectedSet )
|
||||
TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection.
|
||||
|
||||
|
||||
return TargetSetUnit
|
||||
end
|
||||
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Creates an ENGAGE task when there are human friendlies airborne near the targets.
|
||||
-- @param #TASK_A2A_DISPATCHER self
|
||||
-- @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.
|
||||
function TASK_A2A_DISPATCHER:EvaluateENGAGE( DetectedItem )
|
||||
self:F( { DetectedItem.ItemID } )
|
||||
|
||||
|
||||
local DetectedSet = DetectedItem.Set
|
||||
local DetectedZone = DetectedItem.Zone
|
||||
local DetectedZone = DetectedItem.Zone -- TODO: This seems unused, remove?
|
||||
|
||||
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.
|
||||
if PlayersCount > 0 and DetectedItem.IsDetected == true then
|
||||
|
||||
@@ -334,16 +327,13 @@ do -- TASK_A2A_DISPATCHER
|
||||
local TargetSetUnit = SET_UNIT:New()
|
||||
TargetSetUnit:SetDatabase( DetectedSet )
|
||||
TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection.
|
||||
|
||||
|
||||
return TargetSetUnit
|
||||
end
|
||||
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
--- 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".
|
||||
-- @param #TASK_A2A_DISPATCHER self
|
||||
@@ -354,24 +344,24 @@ do -- TASK_A2A_DISPATCHER
|
||||
-- @param #boolean DetectedItemChange
|
||||
-- @return Tasking.Task#TASK
|
||||
function TASK_A2A_DISPATCHER:EvaluateRemoveTask( Mission, Task, Detection, DetectedItem, DetectedItemIndex, DetectedItemChanged )
|
||||
|
||||
|
||||
if Task then
|
||||
|
||||
if Task:IsStatePlanned() then
|
||||
local TaskName = Task:GetName()
|
||||
local TaskType = TaskName:match( "(%u+)%.%d+" )
|
||||
|
||||
|
||||
self:T2( { TaskType = TaskType } )
|
||||
|
||||
|
||||
local Remove = false
|
||||
|
||||
|
||||
local IsPlayers = Detection:IsPlayersNearBy( DetectedItem )
|
||||
if TaskType == "ENGAGE" then
|
||||
if IsPlayers == false then
|
||||
Remove = true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if TaskType == "INTERCEPT" then
|
||||
if IsPlayers == true then
|
||||
Remove = true
|
||||
@@ -380,7 +370,7 @@ do -- TASK_A2A_DISPATCHER
|
||||
Remove = true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if TaskType == "SWEEP" then
|
||||
if DetectedItem.IsDetected == true then
|
||||
Remove = true
|
||||
@@ -388,18 +378,18 @@ do -- TASK_A2A_DISPATCHER
|
||||
end
|
||||
|
||||
local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT
|
||||
--DetectedSet:Flush( self )
|
||||
--self:F( { DetectedSetCount = DetectedSet:Count() } )
|
||||
-- DetectedSet:Flush( self )
|
||||
-- self:F( { DetectedSetCount = DetectedSet:Count() } )
|
||||
if DetectedSet:Count() == 0 then
|
||||
Remove = true
|
||||
end
|
||||
|
||||
|
||||
if DetectedItemChanged == true or Remove then
|
||||
Task = self:RemoveTask( DetectedItemIndex )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return Task
|
||||
end
|
||||
|
||||
@@ -408,10 +398,10 @@ do -- TASK_A2A_DISPATCHER
|
||||
-- @param DetectedItem
|
||||
-- @return #number, Core.CommandCenter#REPORT
|
||||
function TASK_A2A_DISPATCHER:GetFriendliesNearBy( DetectedItem )
|
||||
|
||||
|
||||
local DetectedSet = DetectedItem.Set
|
||||
local FriendlyUnitsNearBy = self.Detection:GetFriendliesNearBy( DetectedItem, Unit.Category.AIRPLANE )
|
||||
|
||||
|
||||
local FriendlyTypes = {}
|
||||
local FriendliesCount = 0
|
||||
|
||||
@@ -423,27 +413,26 @@ do -- TASK_A2A_DISPATCHER
|
||||
local FriendlyUnitThreatLevel = FriendlyUnit:GetThreatLevel()
|
||||
FriendliesCount = FriendliesCount + 1
|
||||
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
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
--self:F( { FriendliesCount = FriendliesCount } )
|
||||
|
||||
-- self:F( { FriendliesCount = FriendliesCount } )
|
||||
|
||||
local FriendlyTypesReport = REPORT:New()
|
||||
|
||||
|
||||
if FriendliesCount > 0 then
|
||||
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
|
||||
else
|
||||
FriendlyTypesReport:Add( "-" )
|
||||
end
|
||||
|
||||
|
||||
|
||||
return FriendliesCount, FriendlyTypesReport
|
||||
end
|
||||
|
||||
@@ -452,10 +441,10 @@ do -- TASK_A2A_DISPATCHER
|
||||
-- @param DetectedItem
|
||||
-- @return #number, Core.CommandCenter#REPORT
|
||||
function TASK_A2A_DISPATCHER:GetPlayerFriendliesNearBy( DetectedItem )
|
||||
|
||||
|
||||
local DetectedSet = DetectedItem.Set
|
||||
local PlayersNearBy = self.Detection:GetPlayersNearBy( DetectedItem )
|
||||
|
||||
|
||||
local PlayerTypes = {}
|
||||
local PlayersCount = 0
|
||||
|
||||
@@ -464,7 +453,7 @@ do -- TASK_A2A_DISPATCHER
|
||||
for PlayerUnitName, PlayerUnitData in pairs( PlayersNearBy ) do
|
||||
local PlayerUnit = PlayerUnitData -- Wrapper.Unit#UNIT
|
||||
local PlayerName = PlayerUnit:GetPlayerName()
|
||||
--self:F( { PlayerName = PlayerName, PlayerUnit = PlayerUnit } )
|
||||
-- self:F( { PlayerName = PlayerName, PlayerUnit = PlayerUnit } )
|
||||
if PlayerUnit:IsAirPlane() and PlayerName ~= nil then
|
||||
local FriendlyUnitThreatLevel = PlayerUnit:GetThreatLevel()
|
||||
PlayersCount = PlayersCount + 1
|
||||
@@ -474,20 +463,19 @@ do -- TASK_A2A_DISPATCHER
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
local PlayerTypesReport = REPORT:New()
|
||||
|
||||
|
||||
if PlayersCount > 0 then
|
||||
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
|
||||
else
|
||||
PlayerTypesReport:Add( "-" )
|
||||
end
|
||||
|
||||
|
||||
|
||||
return PlayersCount, PlayerTypesReport
|
||||
end
|
||||
|
||||
@@ -496,24 +484,23 @@ do -- TASK_A2A_DISPATCHER
|
||||
self.Tasks[TaskIndex] = nil
|
||||
end
|
||||
|
||||
|
||||
--- Assigns tasks in relation to the detected items to the @{Core.Set#SET_GROUP}.
|
||||
-- @param #TASK_A2A_DISPATCHER self
|
||||
-- @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.
|
||||
function TASK_A2A_DISPATCHER:ProcessDetected( Detection )
|
||||
self:F()
|
||||
|
||||
|
||||
local AreaMsg = {}
|
||||
local TaskMsg = {}
|
||||
local ChangeMsg = {}
|
||||
|
||||
|
||||
local Mission = self.Mission
|
||||
|
||||
|
||||
if Mission:IsIDLE() or Mission:IsENGAGED() then
|
||||
|
||||
|
||||
local TaskReport = REPORT:New()
|
||||
|
||||
|
||||
-- Checking the task queue for the dispatcher, and removing any obsolete task!
|
||||
for TaskIndex, TaskData in pairs( self.Tasks ) do
|
||||
local Task = TaskData -- Tasking.Task#TASK
|
||||
@@ -531,18 +518,18 @@ do -- TASK_A2A_DISPATCHER
|
||||
|
||||
-- Now that all obsolete tasks are removed, loop through the detected targets.
|
||||
for DetectedItemID, DetectedItem in pairs( Detection:GetDetectedItems() ) do
|
||||
|
||||
|
||||
local DetectedItem = DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem
|
||||
local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT
|
||||
local DetectedCount = DetectedSet:Count()
|
||||
local DetectedZone = DetectedItem.Zone
|
||||
--self:F( { "Targets in DetectedItem", DetectedItem.ItemID, DetectedSet:Count(), tostring( DetectedItem ) } )
|
||||
--DetectedSet:Flush( self )
|
||||
|
||||
-- self:F( { "Targets in DetectedItem", DetectedItem.ItemID, DetectedSet:Count(), tostring( DetectedItem ) } )
|
||||
-- DetectedSet:Flush( self )
|
||||
|
||||
local DetectedID = DetectedItem.ID
|
||||
local TaskIndex = DetectedItem.Index
|
||||
local DetectedItemChanged = DetectedItem.Changed
|
||||
|
||||
|
||||
local Task = self.Tasks[TaskIndex]
|
||||
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:SetDetection( Detection, DetectedItem )
|
||||
Task:UpdateTaskInfo( DetectedItem )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -582,7 +569,7 @@ do -- TASK_A2A_DISPATCHER
|
||||
function Task.OnEnterCancelled( Task, From, Event, To )
|
||||
self:Cancelled( Task )
|
||||
end
|
||||
|
||||
|
||||
function Task.OnEnterFailed( Task, From, Event, To )
|
||||
self:Failed( Task )
|
||||
end
|
||||
@@ -590,38 +577,38 @@ do -- TASK_A2A_DISPATCHER
|
||||
function Task.OnEnterAborted( Task, From, Event, To )
|
||||
self:Aborted( Task )
|
||||
end
|
||||
|
||||
|
||||
TaskReport:Add( Task:GetName() )
|
||||
else
|
||||
self:F("This should not happen")
|
||||
self:F( "This should not happen" )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if Task then
|
||||
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 )
|
||||
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
|
||||
|
||||
|
||||
-- OK, so the tasking has been done, now delete the changes reported for the area.
|
||||
Detection:AcceptChanges( DetectedItem )
|
||||
end
|
||||
|
||||
|
||||
-- TODO set menus using the HQ coordinator
|
||||
Mission:GetCommandCenter():SetMenu()
|
||||
|
||||
local TaskText = TaskReport:Text(", ")
|
||||
|
||||
local TaskText = TaskReport:Text( ", " )
|
||||
|
||||
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 )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
--- **Tasking** - The TASK_A2G models tasks for players in Air to Ground engagements.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ### Contributions:
|
||||
--
|
||||
--
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- @module Tasking.Task_A2G
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
@@ -18,29 +18,29 @@ do -- TASK_A2G
|
||||
-- @field Core.Set#SET_UNIT TargetSetUnit
|
||||
-- @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}.
|
||||
-- The TASK_A2G is implemented using a @{Core.Fsm#FSM_TASK}, and has the following statuses:
|
||||
--
|
||||
--
|
||||
-- * **None**: Start of the process
|
||||
-- * **Planned**: The A2G task is planned.
|
||||
-- * **Assigned**: The A2G task is assigned to a @{Wrapper.Group#GROUP}.
|
||||
-- * **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.
|
||||
--
|
||||
--
|
||||
-- ## 1) Set the scoring of achievements in an A2G attack.
|
||||
--
|
||||
--
|
||||
-- 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.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.
|
||||
--
|
||||
--
|
||||
-- @field #TASK_A2G
|
||||
TASK_A2G = {
|
||||
ClassName = "TASK_A2G",
|
||||
ClassName = "TASK_A2G"
|
||||
}
|
||||
|
||||
|
||||
--- Instantiates a new TASK_A2G.
|
||||
-- @param #TASK_A2G self
|
||||
-- @param Tasking.Mission#MISSION Mission
|
||||
@@ -54,53 +54,51 @@ do -- TASK_A2G
|
||||
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
|
||||
self:F()
|
||||
|
||||
|
||||
self.TargetSetUnit = TargetSetUnit
|
||||
self.TaskType = TaskType
|
||||
|
||||
|
||||
local Fsm = self:GetUnitProcess()
|
||||
|
||||
|
||||
Fsm:AddTransition( "Assigned", "RouteToRendezVous", "RoutingToRendezVous" )
|
||||
Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtRendezVous" } )
|
||||
Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtRendezVous" } )
|
||||
|
||||
Fsm:AddProcess( "RoutingToRendezVous", "RouteToRendezVousPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtRendezVous" } )
|
||||
Fsm:AddProcess( "RoutingToRendezVous", "RouteToRendezVousZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtRendezVous" } )
|
||||
|
||||
Fsm:AddTransition( { "Arrived", "RoutingToRendezVous" }, "ArriveAtRendezVous", "ArrivedAtRendezVous" )
|
||||
|
||||
|
||||
Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "Engage", "Engaging" )
|
||||
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:AddProcess( "Engaging", "RouteToTargetZone", ACT_ROUTE_ZONE:New(), {} )
|
||||
Fsm:AddProcess( "Engaging", "RouteToTargetPoint", ACT_ROUTE_POINT:New(), {} )
|
||||
Fsm:AddTransition( "Engaging", "RouteToTargets", "Engaging" )
|
||||
|
||||
--Fsm:AddTransition( "Accounted", "DestroyedAll", "Accounted" )
|
||||
--Fsm:AddTransition( "Accounted", "Success", "Success" )
|
||||
|
||||
-- Fsm:AddTransition( "Accounted", "DestroyedAll", "Accounted" )
|
||||
-- Fsm:AddTransition( "Accounted", "Success", "Success" )
|
||||
Fsm:AddTransition( "Rejected", "Reject", "Aborted" )
|
||||
Fsm:AddTransition( "Failed", "Fail", "Failed" )
|
||||
|
||||
|
||||
|
||||
--- Test
|
||||
--- Test
|
||||
-- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task_A2G#TASK_A2G Task
|
||||
function Fsm:onafterAssigned( TaskUnit, Task )
|
||||
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
||||
-- Determine the first Unit from the self.RendezVousSetUnit
|
||||
|
||||
|
||||
self:RouteToRendezVous()
|
||||
end
|
||||
|
||||
--- Test
|
||||
|
||||
--- Test
|
||||
-- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task_A2G#TASK_A2G Task
|
||||
function Fsm:onafterRouteToRendezVous( TaskUnit, Task )
|
||||
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
||||
-- Determine the first Unit from the self.RendezVousSetUnit
|
||||
|
||||
|
||||
if Task:GetRendezVousZone( TaskUnit ) then
|
||||
self:__RouteToRendezVousZone( 0.1 )
|
||||
else
|
||||
@@ -112,36 +110,36 @@ do -- TASK_A2G
|
||||
end
|
||||
end
|
||||
|
||||
--- Test
|
||||
--- Test
|
||||
-- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task#TASK_A2G Task
|
||||
function Fsm:OnAfterArriveAtRendezVous( TaskUnit, Task )
|
||||
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
||||
-- Determine the first Unit from the self.TargetSetUnit
|
||||
|
||||
self:__Engage( 0.1 )
|
||||
|
||||
self:__Engage( 0.1 )
|
||||
end
|
||||
|
||||
--- Test
|
||||
|
||||
--- Test
|
||||
-- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task#TASK_A2G Task
|
||||
function Fsm:onafterEngage( TaskUnit, Task )
|
||||
self:F( { self } )
|
||||
self:__Account( 0.1 )
|
||||
self:__RouteToTarget(0.1 )
|
||||
self:__RouteToTarget( 0.1 )
|
||||
self:__RouteToTargets( -10 )
|
||||
end
|
||||
|
||||
--- Test
|
||||
|
||||
--- Test
|
||||
-- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task_A2G#TASK_A2G Task
|
||||
function Fsm:onafterRouteToTarget( TaskUnit, Task )
|
||||
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
||||
-- Determine the first Unit from the self.TargetSetUnit
|
||||
|
||||
|
||||
if Task:GetTargetZone( TaskUnit ) then
|
||||
self:__RouteToTargetZone( 0.1 )
|
||||
else
|
||||
@@ -154,8 +152,8 @@ do -- TASK_A2G
|
||||
self:__RouteToTargetPoint( 0.1 )
|
||||
end
|
||||
end
|
||||
|
||||
--- Test
|
||||
|
||||
--- Test
|
||||
-- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task_A2G#TASK_A2G Task
|
||||
@@ -167,20 +165,18 @@ do -- TASK_A2G
|
||||
end
|
||||
self:__RouteToTargets( -10 )
|
||||
end
|
||||
|
||||
|
||||
return self
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- @param #TASK_A2G self
|
||||
-- @param Core.Set#SET_UNIT TargetSetUnit The set of targets.
|
||||
function TASK_A2G:SetTargetSetUnit( TargetSetUnit )
|
||||
|
||||
|
||||
self.TargetSetUnit = TargetSetUnit
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- @param #TASK_A2G self
|
||||
function TASK_A2G:GetPlannedMenuText()
|
||||
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 #number RendezVousRange The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point.
|
||||
-- @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 ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT
|
||||
ActRouteRendezVous:SetCoordinate( RendezVousCoordinate )
|
||||
ActRouteRendezVous:SetRange( RendezVousRange )
|
||||
end
|
||||
|
||||
|
||||
--- @param #TASK_A2G self
|
||||
-- @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 #number The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point.
|
||||
function TASK_A2G:GetRendezVousCoordinate( TaskUnit )
|
||||
|
||||
|
||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||
|
||||
local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT
|
||||
return ActRouteRendezVous:GetCoordinate(), ActRouteRendezVous:GetRange()
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
--- @param #TASK_A2G self
|
||||
-- @param Core.Zone#ZONE_BASE RendezVousZone The Zone object where the RendezVous is located on the map.
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
function TASK_A2G:SetRendezVousZone( RendezVousZone, TaskUnit )
|
||||
|
||||
|
||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||
|
||||
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
|
||||
return ActRouteRendezVous:GetZone()
|
||||
end
|
||||
|
||||
|
||||
--- @param #TASK_A2G self
|
||||
-- @param Core.Point#COORDINATE TargetCoordinate The Coordinate object where the Target is located on the map.
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
function TASK_A2G:SetTargetCoordinate( TargetCoordinate, TaskUnit )
|
||||
|
||||
|
||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||
|
||||
local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT
|
||||
ActRouteTarget:SetCoordinate( TargetCoordinate )
|
||||
end
|
||||
|
||||
|
||||
--- @param #TASK_A2G self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
@@ -258,18 +251,16 @@ do -- TASK_A2G
|
||||
return ActRouteTarget:GetCoordinate()
|
||||
end
|
||||
|
||||
|
||||
--- @param #TASK_A2G self
|
||||
-- @param Core.Zone#ZONE_BASE TargetZone The Zone object where the Target is located on the map.
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
function TASK_A2G:SetTargetZone( TargetZone, TaskUnit )
|
||||
|
||||
|
||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||
|
||||
local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE
|
||||
ActRouteTarget:SetZone( TargetZone )
|
||||
end
|
||||
|
||||
|
||||
--- @param #TASK_A2G self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
@@ -283,47 +274,46 @@ do -- TASK_A2G
|
||||
end
|
||||
|
||||
function TASK_A2G:SetGoalTotal()
|
||||
|
||||
|
||||
self.GoalTotal = self.TargetSetUnit:Count()
|
||||
end
|
||||
|
||||
function TASK_A2G:GetGoalTotal()
|
||||
|
||||
|
||||
return self.GoalTotal
|
||||
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.
|
||||
-- @param #TASK_A2G self
|
||||
function TASK_A2G:ReportOrder( ReportGroup )
|
||||
self:UpdateTaskInfo( self.DetectedItem )
|
||||
|
||||
function TASK_A2G:ReportOrder( ReportGroup )
|
||||
self:UpdateTaskInfo( self.DetectedItem )
|
||||
|
||||
local Coordinate = self.TaskInfo:GetData( "Coordinate" )
|
||||
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
|
||||
|
||||
|
||||
return Distance
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- This method checks every 10 seconds if the goal has been reached of the task.
|
||||
-- @param #TASK_A2G self
|
||||
function TASK_A2G:onafterGoal( TaskUnit, From, Event, To )
|
||||
local TargetSetUnit = self.TargetSetUnit -- Core.Set#SET_UNIT
|
||||
|
||||
|
||||
if TargetSetUnit:Count() == 0 then
|
||||
self:Success()
|
||||
end
|
||||
|
||||
|
||||
self:__Goal( -10 )
|
||||
end
|
||||
|
||||
--- @param #TASK_A2G self
|
||||
function TASK_A2G:UpdateTaskInfo( DetectedItem )
|
||||
|
||||
|
||||
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:AddCoordinate( TargetCoordinate, 1, "SOD" )
|
||||
|
||||
|
||||
local ThreatLevel, ThreatText
|
||||
if DetectedItem then
|
||||
ThreatLevel, ThreatText = self.Detection:GetDetectedItemThreatLevel( DetectedItem )
|
||||
@@ -331,7 +321,7 @@ do -- TASK_A2G
|
||||
ThreatLevel, ThreatText = self.TargetSetUnit:CalculateThreatLevelA2G()
|
||||
end
|
||||
self.TaskInfo:AddThreat( ThreatText, ThreatLevel, 10, "MOD", true )
|
||||
|
||||
|
||||
if self.Detection then
|
||||
local DetectedItemsCount = self.TargetSetUnit:Count()
|
||||
local ReportTypes = REPORT:New()
|
||||
@@ -344,33 +334,33 @@ do -- TASK_A2G
|
||||
end
|
||||
end
|
||||
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
|
||||
local DetectedItemsCount = self.TargetSetUnit:Count()
|
||||
local DetectedItemsTypes = self.TargetSetUnit:GetTypeNames()
|
||||
self.TaskInfo:AddTargetCount( DetectedItemsCount, 11, "O", true )
|
||||
self.TaskInfo:AddTargets( DetectedItemsCount, DetectedItemsTypes, 20, "D", true )
|
||||
self.TaskInfo:AddTargets( DetectedItemsCount, DetectedItemsTypes, 20, "D", true )
|
||||
end
|
||||
self.TaskInfo:AddQFEAtCoordinate( TargetCoordinate, 30, "MOD" )
|
||||
self.TaskInfo:AddTemperatureAtCoordinate( TargetCoordinate, 31, "MD" )
|
||||
self.TaskInfo:AddWindAtCoordinate( TargetCoordinate, 32, "MD" )
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- This function is called from the @{Tasking.CommandCenter#COMMANDCENTER} to determine the method of automatic task selection.
|
||||
-- @param #TASK_A2G self
|
||||
-- @param #number AutoAssignMethod The method to be applied to the task.
|
||||
-- @param Tasking.CommandCenter#COMMANDCENTER CommandCenter The command center.
|
||||
-- @param Wrapper.Group#GROUP TaskGroup The player group.
|
||||
function TASK_A2G:GetAutoAssignPriority( AutoAssignMethod, CommandCenter, TaskGroup )
|
||||
|
||||
if AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Random then
|
||||
|
||||
if AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Random then
|
||||
return math.random( 1, 9 )
|
||||
elseif AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Distance then
|
||||
local Coordinate = self.TaskInfo:GetData( "Coordinate" )
|
||||
local Distance = Coordinate:Get2DDistance( CommandCenter:GetPositionable():GetCoordinate() )
|
||||
self:F({Distance=Distance})
|
||||
self:F( { Distance = Distance } )
|
||||
return math.floor( Distance )
|
||||
elseif AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Priority then
|
||||
return 1
|
||||
@@ -379,8 +369,7 @@ do -- TASK_A2G
|
||||
return 0
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
do -- TASK_A2G_SEAD
|
||||
|
||||
@@ -397,9 +386,9 @@ do -- TASK_A2G_SEAD
|
||||
--
|
||||
-- @field #TASK_A2G_SEAD
|
||||
TASK_A2G_SEAD = {
|
||||
ClassName = "TASK_A2G_SEAD",
|
||||
ClassName = "TASK_A2G_SEAD"
|
||||
}
|
||||
|
||||
|
||||
--- Instantiates a new TASK_A2G_SEAD.
|
||||
-- @param #TASK_A2G_SEAD self
|
||||
-- @param Tasking.Mission#MISSION Mission
|
||||
@@ -408,19 +397,16 @@ do -- TASK_A2G_SEAD
|
||||
-- @param Core.Set#SET_UNIT TargetSetUnit
|
||||
-- @param #string TaskBriefing The briefing of the task.
|
||||
-- @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
|
||||
self:F()
|
||||
|
||||
|
||||
Mission:AddTask( self )
|
||||
|
||||
self:SetBriefing(
|
||||
TaskBriefing or
|
||||
"Execute a Suppression of Enemy Air Defenses."
|
||||
)
|
||||
|
||||
self:SetBriefing( TaskBriefing or "Execute a Suppression of Enemy Air Defenses." )
|
||||
|
||||
return self
|
||||
end
|
||||
end
|
||||
|
||||
--- Set a score when a target in scope of the A2G attack, has been destroyed .
|
||||
-- @param #TASK_A2G_SEAD self
|
||||
@@ -434,7 +420,7 @@ do -- TASK_A2G_SEAD
|
||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||
|
||||
ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has SEADed a target.", Score )
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -450,7 +436,7 @@ do -- TASK_A2G_SEAD
|
||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||
|
||||
ProcessUnit:AddScore( "Success", "All radar emitting targets have been successfully SEADed!", Score )
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -466,11 +452,10 @@ do -- TASK_A2G_SEAD
|
||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||
|
||||
ProcessUnit:AddScore( "Failed", "The SEADing has failed!", Penalty )
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
do -- TASK_A2G_BAI
|
||||
@@ -488,10 +473,8 @@ do -- TASK_A2G_BAI
|
||||
-- based on detected enemy ground targets.
|
||||
--
|
||||
-- @field #TASK_A2G_BAI
|
||||
TASK_A2G_BAI = {
|
||||
ClassName = "TASK_A2G_BAI",
|
||||
}
|
||||
|
||||
TASK_A2G_BAI = { ClassName = "TASK_A2G_BAI" }
|
||||
|
||||
--- Instantiates a new TASK_A2G_BAI.
|
||||
-- @param #TASK_A2G_BAI self
|
||||
-- @param Tasking.Mission#MISSION Mission
|
||||
@@ -503,14 +486,11 @@ do -- TASK_A2G_BAI
|
||||
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
|
||||
self:F()
|
||||
|
||||
|
||||
Mission:AddTask( self )
|
||||
|
||||
self:SetBriefing(
|
||||
TaskBriefing or
|
||||
"Execute a Battlefield Air Interdiction of a group of enemy targets."
|
||||
)
|
||||
|
||||
|
||||
self:SetBriefing( TaskBriefing or "Execute a Battlefield Air Interdiction of a group of enemy targets." )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -526,7 +506,7 @@ do -- TASK_A2G_BAI
|
||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||
|
||||
ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has destroyed a target in Battlefield Air Interdiction (BAI).", Score )
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -542,7 +522,7 @@ do -- TASK_A2G_BAI
|
||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||
|
||||
ProcessUnit:AddScore( "Success", "All targets have been successfully destroyed! The Battlefield Air Interdiction (BAI) is a success!", Score )
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -558,15 +538,12 @@ do -- TASK_A2G_BAI
|
||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||
|
||||
ProcessUnit:AddScore( "Failed", "The Battlefield Air Interdiction (BAI) has failed!", Penalty )
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
do -- TASK_A2G_CAS
|
||||
|
||||
--- The TASK_A2G_CAS class
|
||||
@@ -581,10 +558,8 @@ do -- TASK_A2G_CAS
|
||||
-- based on detected enemy ground targets.
|
||||
--
|
||||
-- @field #TASK_A2G_CAS
|
||||
TASK_A2G_CAS = {
|
||||
ClassName = "TASK_A2G_CAS",
|
||||
}
|
||||
|
||||
TASK_A2G_CAS = { ClassName = "TASK_A2G_CAS" }
|
||||
|
||||
--- Instantiates a new TASK_A2G_CAS.
|
||||
-- @param #TASK_A2G_CAS self
|
||||
-- @param Tasking.Mission#MISSION Mission
|
||||
@@ -596,19 +571,13 @@ do -- TASK_A2G_CAS
|
||||
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
|
||||
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
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Set a score when a target in scope of the A2G attack, has been destroyed .
|
||||
-- @param #TASK_A2G_CAS self
|
||||
@@ -622,7 +591,7 @@ do -- TASK_A2G_CAS
|
||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||
|
||||
ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has destroyed a target in Close Air Support (CAS).", Score )
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -638,7 +607,7 @@ do -- TASK_A2G_CAS
|
||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||
|
||||
ProcessUnit:AddScore( "Success", "All targets have been successfully destroyed! The Close Air Support (CAS) was a success!", Score )
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -654,9 +623,8 @@ do -- TASK_A2G_CAS
|
||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||
|
||||
ProcessUnit:AddScore( "Failed", "The Close Air Support (CAS) has failed!", Penalty )
|
||||
|
||||
|
||||
return self
|
||||
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:**
|
||||
--
|
||||
--
|
||||
-- * Dynamically assign tasks to human players based on detected targets.
|
||||
-- * Dynamically change the tasks as the tactical situation evolves during the mission.
|
||||
-- * Dynamically assign (CAS) Close Air Support 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 different ranges to engage upon intruders.
|
||||
-- * Keep task achievements.
|
||||
-- * Score task achievements.--
|
||||
-- * Score task achievements.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ### Contributions:
|
||||
--
|
||||
--
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- @module Tasking.Task_A2G_Dispatcher
|
||||
-- @image Task_A2G_Dispatcher.JPG
|
||||
|
||||
@@ -32,151 +33,151 @@ do -- TASK_A2G_DISPATCHER
|
||||
-- @extends Tasking.DetectionManager#DETECTION_MANAGER
|
||||
|
||||
--- 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 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**.
|
||||
--
|
||||
--
|
||||
-- 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}.
|
||||
--
|
||||
--
|
||||
-- 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.
|
||||
-- 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..
|
||||
-- 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.
|
||||
-- - **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.
|
||||
--
|
||||
-- # 0. Tactical Situations
|
||||
--
|
||||
--
|
||||
-- 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.
|
||||
--
|
||||
--
|
||||
-- 
|
||||
--
|
||||
--
|
||||
-- In the demonstration mission [TAD-A2G-000 - AREAS - Detection test],
|
||||
-- the tactical situation is a demonstration how the A2G detection works.
|
||||
-- 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.
|
||||
--
|
||||
--
|
||||
-- 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
|
||||
-- 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.
|
||||
-- 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.
|
||||
-- For demonstration purposes, the attack airplane is stationed on the ground to explain
|
||||
-- the messages and the menus properly.
|
||||
-- Further test missions demonstrate the A2G task dispatcher from within air.
|
||||
--
|
||||
--
|
||||
-- Depending upon the detection results, the A2G dispatcher will create different tasks.
|
||||
--
|
||||
--
|
||||
-- # 0.1. SEAD Task
|
||||
--
|
||||
--
|
||||
-- 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!
|
||||
-- - 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
|
||||
--
|
||||
--
|
||||
-- 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!
|
||||
-- - Only ground Units are taken into account. Airborne units are ships are not considered friendlies that require Close Air Support.
|
||||
--
|
||||
--
|
||||
-- # 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 may be converted into a CAS task if friendly Ground Units approach within 6 km range!
|
||||
--
|
||||
-- # 1. Player Experience
|
||||
--
|
||||
--
|
||||
-- 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**.
|
||||
--
|
||||
--
|
||||
-- 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:
|
||||
--
|
||||
--
|
||||
-- Radio MENU Structure (F10. Other)
|
||||
--
|
||||
--
|
||||
-- F1. Command Center [Gori]
|
||||
-- F1. Mission "Alpha (Primary)"
|
||||
-- F2. Mission "Beta (Secondary)"
|
||||
-- F3. Mission "Gamma (Tactical)"
|
||||
-- F1. Command Center [Lima]
|
||||
-- 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.
|
||||
--
|
||||
-- ## 1.1. Mission Menu (Under the Command Center Menu)
|
||||
--
|
||||
--
|
||||
-- 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.
|
||||
-- - **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 Mission Reports**: A menu to create various reports on the current mission.
|
||||
--
|
||||
--
|
||||
-- For CC [Lima], Mission "Overlord", the menu structure could look like this:
|
||||
--
|
||||
--
|
||||
-- Radio MENU Structure (F10. Other)
|
||||
--
|
||||
--
|
||||
-- F1. Command Center [Lima]
|
||||
-- F1. Mission "Overlord"
|
||||
-- F1. Mission Briefing
|
||||
-- F2. Mark Task Locations on Map
|
||||
-- F3. Task Reports
|
||||
-- F4. Mission Reports
|
||||
--
|
||||
--
|
||||
-- 
|
||||
--
|
||||
--
|
||||
-- ### 1.1.1. Mission Briefing Menu
|
||||
--
|
||||
--
|
||||
-- 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,
|
||||
-- and explains the mission goals.
|
||||
--
|
||||
--
|
||||
-- and explains the mission goals.
|
||||
--
|
||||
--
|
||||
-- ### 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.
|
||||
-- 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.
|
||||
-- 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
|
||||
--
|
||||
--
|
||||
-- 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.
|
||||
-- - **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.
|
||||
-- - **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.
|
||||
--
|
||||
--
|
||||
-- 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:
|
||||
--
|
||||
--
|
||||
-- Radio MENU Structure (F10. Other)
|
||||
--
|
||||
--
|
||||
-- F1. Command Center [Gori]
|
||||
-- F1. Mission "Alpha"
|
||||
-- F1. Mission Briefing
|
||||
@@ -188,21 +189,21 @@ do -- TASK_A2G_DISPATCHER
|
||||
-- F4. Successful Tasks
|
||||
-- F5. Failed Tasks
|
||||
-- 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.
|
||||
-- (See later).
|
||||
--
|
||||
--
|
||||
-- ### 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:
|
||||
--
|
||||
-- - **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.
|
||||
--
|
||||
--
|
||||
-- For CC |Gori|, Mission "Alpha", the Mission Reports menu structure could look like this:
|
||||
--
|
||||
--
|
||||
-- Radio MENU Structure (F10. Other)
|
||||
--
|
||||
--
|
||||
-- F1. Command Center [Gori]
|
||||
-- F1. Mission "Alpha"
|
||||
-- F1. Mission Briefing
|
||||
@@ -211,25 +212,25 @@ do -- TASK_A2G_DISPATCHER
|
||||
-- F4. Mission Reports
|
||||
-- F1. Report Mission Progress
|
||||
-- F2. Report Players per Task
|
||||
--
|
||||
--
|
||||
--
|
||||
--
|
||||
-- ## 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!**
|
||||
-- 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),
|
||||
-- **the Mission Menu will contain extra Menus to control specific Tasks.**
|
||||
--
|
||||
--
|
||||
-- #### 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:
|
||||
--
|
||||
--
|
||||
-- - 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:
|
||||
--
|
||||
--
|
||||
-- Radio MENU Structure (F10. Other)
|
||||
--
|
||||
--
|
||||
-- F1. Command Center [Gori]
|
||||
-- F1. Mission "Alpha"
|
||||
-- F1. Mission Briefing
|
||||
@@ -237,23 +238,23 @@ do -- TASK_A2G_DISPATCHER
|
||||
-- F3. Task Reports
|
||||
-- F4. Mission Reports
|
||||
-- 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.**
|
||||
--
|
||||
-- #### 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:
|
||||
--
|
||||
--
|
||||
-- - 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.
|
||||
-- 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,
|
||||
-- for CC |Gori|, Mission "Alpha", when a player would have joined Task CAS.001:
|
||||
--
|
||||
--
|
||||
-- Radio MENU Structure (F10. Other)
|
||||
--
|
||||
--
|
||||
-- F1. Command Center [Gori]
|
||||
-- F1. Mission "Alpha"
|
||||
-- F1. Mission Briefing
|
||||
@@ -261,26 +262,25 @@ do -- TASK_A2G_DISPATCHER
|
||||
-- F3. Task Reports
|
||||
-- F4. Mission Reports
|
||||
-- F5. Assigned Task CAS.001
|
||||
--
|
||||
--
|
||||
-- **The F5. Assigned Task __TaskName__ allows the player to control the current Assigned Task and take further actions.**
|
||||
--
|
||||
--
|
||||
--
|
||||
-- ## 1.3. Join Planned Task Menu
|
||||
--
|
||||
--
|
||||
-- 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)**.
|
||||
--
|
||||
-- For example, for CC [Gori], Mission "Alpha",
|
||||
-- The Menu Hierarchy is structuring the Tasks per **Task Type**, and then by **Task Name (ID)**.
|
||||
--
|
||||
-- For example, for CC [Gori], Mission "Alpha",
|
||||
-- if a Mission "ALpha" contains 5 Planned Tasks, which would be:
|
||||
--
|
||||
-- - 2 CAS Tasks
|
||||
--
|
||||
-- - 2 CAS Tasks
|
||||
-- - 1 BAI Task
|
||||
-- - 2 SEAD Tasks
|
||||
--
|
||||
--
|
||||
-- the Join Planned Task Menu Hierarchy could look like this:
|
||||
--
|
||||
--
|
||||
-- Radio MENU Structure (F10. Other)
|
||||
--
|
||||
--
|
||||
-- F1. Command Center [Gori]
|
||||
-- F1. Mission "Alpha"
|
||||
-- F1. Mission Briefing
|
||||
@@ -296,26 +296,26 @@ do -- TASK_A2G_DISPATCHER
|
||||
-- F1. SEAD.003
|
||||
-- F2. SEAD.004
|
||||
-- F3. SEAD.005
|
||||
--
|
||||
--
|
||||
-- An example from within a running simulation:
|
||||
--
|
||||
--
|
||||
-- 
|
||||
--
|
||||
--
|
||||
-- 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**!
|
||||
--
|
||||
-- ### 1.3.1. Planned Task Menu
|
||||
--
|
||||
-- Each Planned Task Menu will allow for the following actions:
|
||||
--
|
||||
--
|
||||
-- - 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.
|
||||
-- - 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)
|
||||
--
|
||||
--
|
||||
-- F1. Command Center |Gori|
|
||||
-- F1. Mission "Alpha"
|
||||
-- F1. Mission Briefing
|
||||
@@ -328,22 +328,22 @@ do -- TASK_A2G_DISPATCHER
|
||||
-- F1. Report Task Details
|
||||
-- F2. Mark Task Location on Map
|
||||
-- F3. Join Task
|
||||
--
|
||||
--
|
||||
-- **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
|
||||
--
|
||||
--
|
||||
-- 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:
|
||||
--
|
||||
--
|
||||
-- - **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.
|
||||
-- - **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:
|
||||
--
|
||||
--
|
||||
-- F1. Command Center |Gori|
|
||||
-- F1. Mission "Alpha"
|
||||
-- F1. Mission Briefing
|
||||
@@ -354,90 +354,89 @@ do -- TASK_A2G_DISPATCHER
|
||||
-- F1. Report Task Details
|
||||
-- F2. Mark Task Location on Map
|
||||
-- F3. Abort Task
|
||||
--
|
||||
--
|
||||
-- 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
|
||||
--
|
||||
--
|
||||
-- During game play, different messages are displayed.
|
||||
-- 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 @{Settings} menu provides additional options to control the timing of the messages.
|
||||
-- There are:
|
||||
--
|
||||
--
|
||||
-- - 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.
|
||||
-- - 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.
|
||||
-- - 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
|
||||
--
|
||||
--
|
||||
-- The @{#TASK_A2G_DISPATCHER.New}() method creates a new TASK\_A2G\_DISPATCHER instance.
|
||||
--
|
||||
-- # 3. Usage
|
||||
--
|
||||
-- To use the TASK\_A2G\_DISPATCHER class, you need:
|
||||
--
|
||||
--
|
||||
-- - A @{CommandCenter} object. The master communication channel.
|
||||
-- - A @{Mission} object. Each task belongs to a Mission.
|
||||
-- - A @{Detection} object. There are several detection grouping methods to choose from.
|
||||
-- - 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.
|
||||
--
|
||||
-- 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
|
||||
-- :FindByName( "HQ", "Bravo HQ" )
|
||||
--
|
||||
-- local CommandCenter = COMMANDCENTER
|
||||
-- :New( HQ, "Lima" )
|
||||
--
|
||||
--
|
||||
-- -- Declare the Mission for the Command Center.
|
||||
-- local Mission = MISSION
|
||||
-- :New( CommandCenter, "Overlord", "High", "Attack Detect Mission Briefing", coalition.side.RED )
|
||||
--
|
||||
--
|
||||
-- -- Define the RecceSet that will detect the enemy.
|
||||
-- local RecceSet = SET_GROUP
|
||||
-- :New()
|
||||
-- :FilterPrefixes( "FAC" )
|
||||
-- :FilterCoalitions("red")
|
||||
-- :FilterStart()
|
||||
--
|
||||
--
|
||||
-- -- Setup the detection. We use DETECTION_AREAS to detect and group the enemies within areas of 3 km radius.
|
||||
-- local DetectionAreas = DETECTION_AREAS
|
||||
-- :New( RecceSet, 3000 ) -- The RecceSet will detect the enemies.
|
||||
--
|
||||
--
|
||||
-- -- 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
|
||||
-- :New() -- Create the SET_GROUP object.
|
||||
-- :FilterCoalitions( "red" ) -- Only incorporate the RED coalitions.
|
||||
-- :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.
|
||||
--
|
||||
--
|
||||
-- -- Now we have everything to setup the main A2G TaskDispatcher.
|
||||
-- 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
|
||||
TASK_A2G_DISPATCHER = {
|
||||
ClassName = "TASK_A2G_DISPATCHER",
|
||||
Mission = nil,
|
||||
Detection = nil,
|
||||
Tasks = {},
|
||||
Tasks = {}
|
||||
}
|
||||
|
||||
|
||||
|
||||
--- TASK_A2G_DISPATCHER constructor.
|
||||
-- @param #TASK_A2G_DISPATCHER self
|
||||
-- @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.
|
||||
-- @return #TASK_A2G_DISPATCHER self
|
||||
function TASK_A2G_DISPATCHER:New( Mission, SetGroup, Detection )
|
||||
|
||||
|
||||
-- Inherits from DETECTION_MANAGER
|
||||
local self = BASE:Inherit( self, DETECTION_MANAGER:New( SetGroup, Detection ) ) -- #TASK_A2G_DISPATCHER
|
||||
|
||||
|
||||
self.Detection = Detection
|
||||
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:AddTransition( "Started", "Assign", "Started" )
|
||||
|
||||
|
||||
--- OnAfter Transition Handler for Event Assign.
|
||||
-- @function [parent=#TASK_A2G_DISPATCHER] OnAfterAssign
|
||||
-- @param #TASK_A2G_DISPATCHER self
|
||||
@@ -466,19 +465,19 @@ do -- TASK_A2G_DISPATCHER
|
||||
-- @param Tasking.Task_A2G#TASK_A2G Task
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param #string PlayerName
|
||||
|
||||
|
||||
self:__Start( 5 )
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set flashing player messages on or off
|
||||
|
||||
--- Set flashing player messages on or off
|
||||
-- @param #TASK_A2G_DISPATCHER self
|
||||
-- @param #boolean onoff Set messages on (true) or off (false)
|
||||
function TASK_A2G_DISPATCHER:SetSendMessages( onoff )
|
||||
self.FlashNewTask = onoff
|
||||
self.FlashNewTask = onoff
|
||||
end
|
||||
|
||||
|
||||
--- Creates a SEAD task when there are targets for it.
|
||||
-- @param #TASK_A2G_DISPATCHER self
|
||||
-- @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.
|
||||
function TASK_A2G_DISPATCHER:EvaluateSEAD( DetectedItem )
|
||||
self:F( { DetectedItem.ItemID } )
|
||||
|
||||
|
||||
local DetectedSet = DetectedItem.Set
|
||||
local DetectedZone = DetectedItem.Zone
|
||||
|
||||
@@ -500,10 +499,10 @@ do -- TASK_A2G_DISPATCHER
|
||||
TargetSetUnit:SetDatabase( DetectedSet )
|
||||
TargetSetUnit:FilterHasSEAD()
|
||||
TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection.
|
||||
|
||||
|
||||
return TargetSetUnit
|
||||
end
|
||||
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
@@ -514,11 +513,10 @@ do -- TASK_A2G_DISPATCHER
|
||||
-- @return #nil If there are no targets to be set.
|
||||
function TASK_A2G_DISPATCHER:EvaluateCAS( DetectedItem )
|
||||
self:F( { DetectedItem.ItemID } )
|
||||
|
||||
|
||||
local DetectedSet = DetectedItem.Set
|
||||
local DetectedZone = DetectedItem.Zone
|
||||
|
||||
|
||||
-- Determine if the set has ground units.
|
||||
-- There should be ground unit friendlies nearby. Airborne units are valid friendlies types.
|
||||
-- And there shouldn't be any radar.
|
||||
@@ -532,13 +530,13 @@ do -- TASK_A2G_DISPATCHER
|
||||
local TargetSetUnit = SET_UNIT:New()
|
||||
TargetSetUnit:SetDatabase( DetectedSet )
|
||||
TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection.
|
||||
|
||||
|
||||
return TargetSetUnit
|
||||
end
|
||||
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Creates a BAI task when there are targets for it.
|
||||
-- @param #TASK_A2G_DISPATCHER self
|
||||
-- @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.
|
||||
function TASK_A2G_DISPATCHER:EvaluateBAI( DetectedItem, FriendlyCoalition )
|
||||
self:F( { DetectedItem.ItemID } )
|
||||
|
||||
|
||||
local DetectedSet = DetectedItem.Set
|
||||
local DetectedZone = DetectedItem.Zone
|
||||
|
||||
|
||||
-- Determine if the set has ground units.
|
||||
-- There shouldn't be any ground unit friendlies nearby.
|
||||
-- And there shouldn't be any radar.
|
||||
@@ -564,19 +561,18 @@ do -- TASK_A2G_DISPATCHER
|
||||
local TargetSetUnit = SET_UNIT:New()
|
||||
TargetSetUnit:SetDatabase( DetectedSet )
|
||||
TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection.
|
||||
|
||||
|
||||
return TargetSetUnit
|
||||
end
|
||||
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
|
||||
function TASK_A2G_DISPATCHER:RemoveTask( TaskIndex )
|
||||
self.Mission:RemoveTask( self.Tasks[TaskIndex] )
|
||||
self.Tasks[TaskIndex] = nil
|
||||
end
|
||||
|
||||
|
||||
--- 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".
|
||||
-- @param #TASK_A2G_DISPATCHER self
|
||||
@@ -586,17 +582,16 @@ do -- TASK_A2G_DISPATCHER
|
||||
-- @param #boolean DetectedItemChange
|
||||
-- @return Tasking.Task#TASK
|
||||
function TASK_A2G_DISPATCHER:EvaluateRemoveTask( Mission, Task, TaskIndex, DetectedItemChanged )
|
||||
|
||||
|
||||
if Task then
|
||||
if ( Task:IsStatePlanned() and DetectedItemChanged == true ) or Task:IsStateCancelled() then
|
||||
--self:F( "Removing Tasking: " .. Task:GetTaskName() )
|
||||
if (Task:IsStatePlanned() and DetectedItemChanged == true) or Task:IsStateCancelled() then
|
||||
-- self:F( "Removing Tasking: " .. Task:GetTaskName() )
|
||||
self:RemoveTask( TaskIndex )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return Task
|
||||
end
|
||||
|
||||
|
||||
--- Assigns tasks in relation to the detected items to the @{Core.Set#SET_GROUP}.
|
||||
-- @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.
|
||||
function TASK_A2G_DISPATCHER:ProcessDetected( Detection )
|
||||
self:F()
|
||||
|
||||
|
||||
local AreaMsg = {}
|
||||
local TaskMsg = {}
|
||||
local ChangeMsg = {}
|
||||
|
||||
|
||||
local Mission = self.Mission
|
||||
|
||||
|
||||
if Mission:IsIDLE() or Mission:IsENGAGED() then
|
||||
|
||||
|
||||
local TaskReport = REPORT:New()
|
||||
|
||||
-- Checking the task queue for the dispatcher, and removing any obsolete task!
|
||||
@@ -634,21 +629,21 @@ do -- TASK_A2G_DISPATCHER
|
||||
|
||||
--- First we need to the detected targets.
|
||||
for DetectedItemID, DetectedItem in pairs( Detection:GetDetectedItems() ) do
|
||||
|
||||
|
||||
local DetectedItem = DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem
|
||||
local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT
|
||||
local DetectedZone = DetectedItem.Zone
|
||||
--self:F( { "Targets in DetectedItem", DetectedItem.ItemID, DetectedSet:Count(), tostring( DetectedItem ) } )
|
||||
--DetectedSet:Flush( self )
|
||||
|
||||
-- self:F( { "Targets in DetectedItem", DetectedItem.ItemID, DetectedSet:Count(), tostring( DetectedItem ) } )
|
||||
-- DetectedSet:Flush( self )
|
||||
|
||||
local DetectedItemID = DetectedItem.ID
|
||||
local TaskIndex = DetectedItem.Index
|
||||
local DetectedItemChanged = DetectedItem.Changed
|
||||
|
||||
|
||||
self:F( { DetectedItemChanged = DetectedItemChanged, DetectedItemID = DetectedItemID, TaskIndex = TaskIndex } )
|
||||
|
||||
|
||||
local Task = self.Tasks[TaskIndex] -- Tasking.Task_A2G#TASK_A2G
|
||||
|
||||
|
||||
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 Task:IsStateAssigned() then
|
||||
@@ -660,7 +655,7 @@ do -- TASK_A2G_DISPATCHER
|
||||
Task:SetTargetSetUnit( TargetSetUnit )
|
||||
Task:SetDetection( Detection, DetectedItem )
|
||||
Task:UpdateTaskInfo( DetectedItem )
|
||||
TargetsReport:Add( Detection:GetChangeText( DetectedItem ) )
|
||||
TargetsReport:Add( Detection:GetChangeText( DetectedItem ) )
|
||||
else
|
||||
Task:Cancel()
|
||||
end
|
||||
@@ -691,18 +686,18 @@ do -- TASK_A2G_DISPATCHER
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Now we send to each group the changes, if any.
|
||||
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
|
||||
local TargetsText = TargetsReport:Text(", ")
|
||||
if ( Mission:IsGroupAssigned(TaskGroup) ) and TargetsText ~= "" and self.FlashNewTask then
|
||||
local TargetsText = TargetsReport:Text( ", " )
|
||||
if (Mission:IsGroupAssigned( TaskGroup )) and TargetsText ~= "" and self.FlashNewTask then
|
||||
Mission:GetCommandCenter():MessageToGroup( string.format( "Task %s has change of targets:\n %s", Task:GetName(), TargetsText ), TaskGroup )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if Task then
|
||||
if Task:IsStatePlanned() then
|
||||
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...
|
||||
if TargetSetUnit then
|
||||
Task = TASK_A2G_SEAD:New( Mission, self.SetGroup, string.format( "SEAD.%03d", DetectedItemID ), TargetSetUnit )
|
||||
DetectedItem.DesignateMenuName = string.format( "SEAD.%03d", DetectedItemID ) --inject a name for DESIGNATE, if using same DETECTION object
|
||||
DetectedItem.DesignateMenuName = string.format( "SEAD.%03d", DetectedItemID ) -- inject a name for DESIGNATE, if using same DETECTION object
|
||||
Task:SetDetection( Detection, DetectedItem )
|
||||
end
|
||||
|
||||
@@ -762,7 +757,7 @@ do -- TASK_A2G_DISPATCHER
|
||||
local TargetSetUnit = self:EvaluateCAS( DetectedItem ) -- Returns a SetUnit if there are targets to be CASed...
|
||||
if TargetSetUnit then
|
||||
Task = TASK_A2G_CAS:New( Mission, self.SetGroup, string.format( "CAS.%03d", DetectedItemID ), TargetSetUnit )
|
||||
DetectedItem.DesignateMenuName = string.format( "CAS.%03d", DetectedItemID ) --inject a name for DESIGNATE, if using same DETECTION object
|
||||
DetectedItem.DesignateMenuName = string.format( "CAS.%03d", DetectedItemID ) -- inject a name for DESIGNATE, if using same DETECTION object
|
||||
Task:SetDetection( Detection, DetectedItem )
|
||||
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...
|
||||
if TargetSetUnit then
|
||||
Task = TASK_A2G_BAI:New( Mission, self.SetGroup, string.format( "BAI.%03d", DetectedItemID ), TargetSetUnit )
|
||||
DetectedItem.DesignateMenuName = string.format( "BAI.%03d", DetectedItemID ) --inject a name for DESIGNATE, if using same DETECTION object
|
||||
DetectedItem.DesignateMenuName = string.format( "BAI.%03d", DetectedItemID ) -- inject a name for DESIGNATE, if using same DETECTION object
|
||||
Task:SetDetection( Detection, DetectedItem )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if Task then
|
||||
self.Tasks[TaskIndex] = Task
|
||||
Task:SetTargetZone( DetectedZone )
|
||||
Task:SetDispatcher( self )
|
||||
Task:UpdateTaskInfo( DetectedItem )
|
||||
Mission:AddTask( Task )
|
||||
|
||||
|
||||
function Task.OnEnterSuccess( Task, From, Event, To )
|
||||
self:Success( Task )
|
||||
end
|
||||
@@ -791,7 +786,7 @@ do -- TASK_A2G_DISPATCHER
|
||||
function Task.OnEnterCancelled( Task, From, Event, To )
|
||||
self:Cancelled( Task )
|
||||
end
|
||||
|
||||
|
||||
function Task.OnEnterFailed( Task, From, Event, To )
|
||||
self:Failed( Task )
|
||||
end
|
||||
@@ -799,31 +794,29 @@ do -- TASK_A2G_DISPATCHER
|
||||
function Task.OnEnterAborted( Task, From, Event, To )
|
||||
self:Aborted( Task )
|
||||
end
|
||||
|
||||
|
||||
|
||||
TaskReport:Add( Task:GetName() )
|
||||
else
|
||||
self:F("This should not happen")
|
||||
self:F( "This should not happen" )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- OK, so the tasking has been done, now delete the changes reported for the area.
|
||||
Detection:AcceptChanges( DetectedItem )
|
||||
end
|
||||
|
||||
|
||||
-- TODO set menus using the HQ coordinator
|
||||
Mission:GetCommandCenter():SetMenu()
|
||||
|
||||
local TaskText = TaskReport:Text(", ")
|
||||
|
||||
local TaskText = TaskReport:Text( ", " )
|
||||
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 )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
--- **Utilities** Enumerators.
|
||||
--
|
||||
--
|
||||
-- An enumerator is a variable that holds a constant value. Enumerators are very useful because they make the code easier to read and to change in general.
|
||||
--
|
||||
--
|
||||
-- For example, instead of using the same value at multiple different places in your code, you should use a variable set to that value.
|
||||
-- If, for whatever reason, the value needs to be changed, you only have to change the variable once and do not have to search through you code and reset
|
||||
-- every value by hand.
|
||||
--
|
||||
--
|
||||
-- Another big advantage is that the LDT intellisense "knows" the enumerators. So you can use the autocompletion feature and do not have to keep all the
|
||||
-- values in your head or look them up in the docs.
|
||||
--
|
||||
-- values in your head or look them up in the docs.
|
||||
--
|
||||
-- DCS itself provides a lot of enumerators for various things. See [Enumerators](https://wiki.hoggitworld.com/view/Category:Enumerators) on Hoggit.
|
||||
--
|
||||
-- Other Moose classe also have enumerators. For example, the AIRBASE class has enumerators for airbase names.
|
||||
--
|
||||
--
|
||||
-- Other Moose classes also have enumerators. For example, the AIRBASE class has enumerators for airbase names.
|
||||
--
|
||||
-- @module ENUMS
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
-- @type ENUMS
|
||||
|
||||
--- Because ENUMS are just better practice.
|
||||
--
|
||||
--
|
||||
-- The ENUMS class adds some handy variables, which help you to make your code better and more general.
|
||||
--
|
||||
-- @field #ENUMS
|
||||
@@ -30,16 +30,16 @@ ENUMS = {}
|
||||
-- @type ENUMS.ROE
|
||||
-- @field #number WeaponFree AI will engage any enemy group it detects. Target prioritization is based based on the threat of the target.
|
||||
-- @field #number OpenFireWeaponFree AI will engage any enemy group it detects, but will prioritize targets specified in the groups tasking.
|
||||
-- @field #number OpenFire AI will engage only targets specified in its taskings.
|
||||
-- @field #number OpenFire AI will engage only targets specified in its tasking.
|
||||
-- @field #number ReturnFire AI will only engage threats that shoot first.
|
||||
-- @field #number WeaponHold AI will hold fire under all circumstances.
|
||||
ENUMS.ROE = {
|
||||
WeaponFree=0,
|
||||
OpenFireWeaponFree=1,
|
||||
OpenFire=2,
|
||||
ReturnFire=3,
|
||||
WeaponHold=4,
|
||||
}
|
||||
WeaponFree = 0,
|
||||
OpenFireWeaponFree = 1,
|
||||
OpenFire = 2,
|
||||
ReturnFire = 3,
|
||||
WeaponHold = 4,
|
||||
}
|
||||
|
||||
--- Reaction On Threat.
|
||||
-- @type ENUMS.ROT
|
||||
@@ -49,11 +49,11 @@ ENUMS.ROE = {
|
||||
-- @field #number BypassAndEscape AI will attempt to avoid enemy threat zones all together. This includes attempting to fly above or around threats.
|
||||
-- @field #number AllowAbortMission If a threat is deemed severe enough the AI will abort its mission and return to base.
|
||||
ENUMS.ROT = {
|
||||
NoReaction=0,
|
||||
PassiveDefense=1,
|
||||
EvadeFire=2,
|
||||
BypassAndEscape=3,
|
||||
AllowAbortMission=4,
|
||||
NoReaction = 0,
|
||||
PassiveDefense = 1,
|
||||
EvadeFire = 2,
|
||||
BypassAndEscape = 3,
|
||||
AllowAbortMission = 4,
|
||||
}
|
||||
|
||||
--- Alarm state.
|
||||
@@ -62,12 +62,12 @@ ENUMS.ROT = {
|
||||
-- @field #number Green Group is not combat ready. Sensors are stowed if possible.
|
||||
-- @field #number Red Group is combat ready and actively searching for targets. Some groups like infantry will not move in this state.
|
||||
ENUMS.AlarmState = {
|
||||
Auto=0,
|
||||
Green=1,
|
||||
Red=2,
|
||||
Auto = 0,
|
||||
Green = 1,
|
||||
Red = 2,
|
||||
}
|
||||
|
||||
--- Weapon types. See the [Weapon Flag](https://wiki.hoggitworld.com/view/DCS_enum_weapon_flag) enumerotor on hoggit wiki.
|
||||
--- Weapon types. See the [Weapon Flag](https://wiki.hoggitworld.com/view/DCS_enum_weapon_flag) enumerator on Hoggit wiki.
|
||||
-- @type ENUMS.WeaponFlag
|
||||
ENUMS.WeaponFlag={
|
||||
-- Bombs
|
||||
@@ -111,7 +111,7 @@ ENUMS.WeaponFlag={
|
||||
--
|
||||
-- Bombs
|
||||
GuidedBomb = 14, -- (LGB + TvGB + SNSGB)
|
||||
AnyUnguidedBomb = 2147485680, -- (HeBomb + Penetrator + NapalmBomb + FAEBomb + ClusterBomb + Dispencer + CandleBomb + ParachuteBomb)
|
||||
AnyUnguidedBomb = 2147485680, -- (HeBomb + Penetrator + NapalmBomb + FAEBomb + ClusterBomb + Dispenser + CandleBomb + ParachuteBomb)
|
||||
AnyBomb = 2147485694, -- (GuidedBomb + AnyUnguidedBomb)
|
||||
--- Rockets
|
||||
AnyRocket = 30720, -- LightRocket + MarkerRocket + CandleRocket + HeavyRocket
|
||||
@@ -123,7 +123,7 @@ ENUMS.WeaponFlag={
|
||||
--- Air-To-Air Missiles
|
||||
AnyAAM = 264241152, -- IR_AAM + SAR_AAM + AR_AAM + SRAAM + MRAAM + LRAAM
|
||||
AnyAutonomousMissile = 36012032, -- IR_AAM + AntiRadarMissile + AntiShipMissile + FireAndForgetASM + CruiseMissile
|
||||
AnyMissile = 268402688, -- AnyASM + AnyAAM
|
||||
AnyMissile = 268402688, -- AnyASM + AnyAAM
|
||||
--- Guns
|
||||
Cannons = 805306368, -- GUN_POD + BuiltInCannon
|
||||
---
|
||||
@@ -133,7 +133,7 @@ ENUMS.WeaponFlag={
|
||||
AnyAG = 2956984318, -- Any Air-To-Ground Weapon
|
||||
AnyAA = 264241152, -- Any Air-To-Air Weapon
|
||||
AnyUnguided = 2952822768, -- Any Unguided Weapon
|
||||
AnyGuided = 268402702, -- Any Guided Weapon
|
||||
AnyGuided = 268402702, -- Any Guided Weapon
|
||||
}
|
||||
|
||||
--- Mission tasks.
|
||||
@@ -173,7 +173,7 @@ ENUMS.MissionTask={
|
||||
TRANSPORT="Transport",
|
||||
}
|
||||
|
||||
--- Formations (new). See the [Formations](https://wiki.hoggitworld.com/view/DCS_enum_formation) on hoggit wiki.
|
||||
--- Formations (new). See the [Formations](https://wiki.hoggitworld.com/view/DCS_enum_formation) on Hoggit wiki.
|
||||
-- @type ENUMS.Formation
|
||||
ENUMS.Formation={}
|
||||
ENUMS.Formation.FixedWing={}
|
||||
@@ -216,23 +216,23 @@ ENUMS.Formation.FixedWing.FighterVic.Close = 917505
|
||||
ENUMS.Formation.FixedWing.FighterVic.Open = 917506
|
||||
ENUMS.Formation.RotaryWing={}
|
||||
ENUMS.Formation.RotaryWing.Column={}
|
||||
ENUMS.Formation.RotaryWing.Column.D70=720896
|
||||
ENUMS.Formation.RotaryWing.Column.D70 = 720896
|
||||
ENUMS.Formation.RotaryWing.Wedge={}
|
||||
ENUMS.Formation.RotaryWing.Wedge.D70=8
|
||||
ENUMS.Formation.RotaryWing.Wedge.D70 = 8
|
||||
ENUMS.Formation.RotaryWing.FrontRight={}
|
||||
ENUMS.Formation.RotaryWing.FrontRight.D300=655361
|
||||
ENUMS.Formation.RotaryWing.FrontRight.D600=655362
|
||||
ENUMS.Formation.RotaryWing.FrontRight.D300 = 655361
|
||||
ENUMS.Formation.RotaryWing.FrontRight.D600 = 655362
|
||||
ENUMS.Formation.RotaryWing.FrontLeft={}
|
||||
ENUMS.Formation.RotaryWing.FrontLeft.D300=655617
|
||||
ENUMS.Formation.RotaryWing.FrontLeft.D600=655618
|
||||
ENUMS.Formation.RotaryWing.FrontLeft.D300 = 655617
|
||||
ENUMS.Formation.RotaryWing.FrontLeft.D600 = 655618
|
||||
ENUMS.Formation.RotaryWing.EchelonRight={}
|
||||
ENUMS.Formation.RotaryWing.EchelonRight.D70 =589825
|
||||
ENUMS.Formation.RotaryWing.EchelonRight.D300=589826
|
||||
ENUMS.Formation.RotaryWing.EchelonRight.D600=589827
|
||||
ENUMS.Formation.RotaryWing.EchelonRight.D70 = 589825
|
||||
ENUMS.Formation.RotaryWing.EchelonRight.D300 = 589826
|
||||
ENUMS.Formation.RotaryWing.EchelonRight.D600 = 589827
|
||||
ENUMS.Formation.RotaryWing.EchelonLeft={}
|
||||
ENUMS.Formation.RotaryWing.EchelonLeft.D70 =590081
|
||||
ENUMS.Formation.RotaryWing.EchelonLeft.D300=590082
|
||||
ENUMS.Formation.RotaryWing.EchelonLeft.D600=590083
|
||||
ENUMS.Formation.RotaryWing.EchelonLeft.D70 = 590081
|
||||
ENUMS.Formation.RotaryWing.EchelonLeft.D300 = 590082
|
||||
ENUMS.Formation.RotaryWing.EchelonLeft.D600 = 590083
|
||||
ENUMS.Formation.Vehicle={}
|
||||
ENUMS.Formation.Vehicle.Vee="Vee"
|
||||
ENUMS.Formation.Vehicle.EchelonRight="EchelonR"
|
||||
@@ -244,34 +244,34 @@ ENUMS.Formation.Vehicle.Cone="Cone"
|
||||
ENUMS.Formation.Vehicle.Diamond="Diamond"
|
||||
|
||||
--- Formations (old). The old format is a simplified version of the new formation enums, which allow more sophisticated settings.
|
||||
-- See the [Formations](https://wiki.hoggitworld.com/view/DCS_enum_formation) on hoggit wiki.
|
||||
-- See the [Formations](https://wiki.hoggitworld.com/view/DCS_enum_formation) on Hoggit wiki.
|
||||
-- @type ENUMS.FormationOld
|
||||
ENUMS.FormationOld={}
|
||||
ENUMS.FormationOld.FixedWing={}
|
||||
ENUMS.FormationOld.FixedWing.LineAbreast=1
|
||||
ENUMS.FormationOld.FixedWing.Trail=2
|
||||
ENUMS.FormationOld.FixedWing.Wedge=3
|
||||
ENUMS.FormationOld.FixedWing.EchelonRight=4
|
||||
ENUMS.FormationOld.FixedWing.EchelonLeft=5
|
||||
ENUMS.FormationOld.FixedWing.FingerFour=6
|
||||
ENUMS.FormationOld.FixedWing.SpreadFour=7
|
||||
ENUMS.FormationOld.FixedWing.BomberElement=12
|
||||
ENUMS.FormationOld.FixedWing.BomberElementHeight=13
|
||||
ENUMS.FormationOld.FixedWing.FighterVic=14
|
||||
ENUMS.FormationOld.FixedWing.LineAbreast = 1
|
||||
ENUMS.FormationOld.FixedWing.Trail = 2
|
||||
ENUMS.FormationOld.FixedWing.Wedge = 3
|
||||
ENUMS.FormationOld.FixedWing.EchelonRight = 4
|
||||
ENUMS.FormationOld.FixedWing.EchelonLeft = 5
|
||||
ENUMS.FormationOld.FixedWing.FingerFour = 6
|
||||
ENUMS.FormationOld.FixedWing.SpreadFour = 7
|
||||
ENUMS.FormationOld.FixedWing.BomberElement = 12
|
||||
ENUMS.FormationOld.FixedWing.BomberElementHeight = 13
|
||||
ENUMS.FormationOld.FixedWing.FighterVic = 14
|
||||
ENUMS.FormationOld.RotaryWing={}
|
||||
ENUMS.FormationOld.RotaryWing.Wedge=8
|
||||
ENUMS.FormationOld.RotaryWing.Echelon=9
|
||||
ENUMS.FormationOld.RotaryWing.Front=10
|
||||
ENUMS.FormationOld.RotaryWing.Column=11
|
||||
ENUMS.FormationOld.RotaryWing.Wedge = 8
|
||||
ENUMS.FormationOld.RotaryWing.Echelon = 9
|
||||
ENUMS.FormationOld.RotaryWing.Front = 10
|
||||
ENUMS.FormationOld.RotaryWing.Column = 11
|
||||
|
||||
|
||||
--- Morse Code. See the [Wikipedia](https://en.wikipedia.org/wiki/Morse_code).
|
||||
--
|
||||
--
|
||||
-- * Short pulse "*"
|
||||
-- * Long pulse "-"
|
||||
--
|
||||
--
|
||||
-- Pulses are separated by a blank character " ".
|
||||
--
|
||||
--
|
||||
-- @type ENUMS.Morse
|
||||
ENUMS.Morse={}
|
||||
ENUMS.Morse.A="* -"
|
||||
@@ -313,9 +313,9 @@ ENUMS.Morse.N0="- - - - -"
|
||||
ENUMS.Morse[" "]=" "
|
||||
|
||||
--- ISO (639-1) 2-letter Language Codes. See the [Wikipedia](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes).
|
||||
--
|
||||
--
|
||||
-- @type ENUMS.ISOLang
|
||||
ENUMS.ISOLang =
|
||||
ENUMS.ISOLang =
|
||||
{
|
||||
Arabic = 'AR',
|
||||
Chinese = 'ZH',
|
||||
@@ -329,7 +329,7 @@ ENUMS.ISOLang =
|
||||
}
|
||||
|
||||
--- Phonetic Alphabet (NATO). See the [Wikipedia](https://en.wikipedia.org/wiki/NATO_phonetic_alphabet).
|
||||
--
|
||||
--
|
||||
-- @type ENUMS.Phonetic
|
||||
ENUMS.Phonetic =
|
||||
{
|
||||
|
||||
@@ -5,11 +5,10 @@
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **TAW CougarNL**, *funkyfranky*
|
||||
--
|
||||
--
|
||||
-- @module Utilities.PROFILER
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
|
||||
--- PROFILER class.
|
||||
-- @type PROFILER
|
||||
-- @field #string ClassName Name of the class.
|
||||
@@ -25,7 +24,6 @@
|
||||
-- @field #number ThreshTtot Total time threshold. Only write output if total function CPU time is more than this value.
|
||||
-- @field #string fileNamePrefix Output file name prefix, e.g. "MooseProfiler".
|
||||
-- @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? *
|
||||
--
|
||||
-- ===
|
||||
@@ -33,60 +31,59 @@
|
||||
-- 
|
||||
--
|
||||
-- # The PROFILER Concept
|
||||
--
|
||||
--
|
||||
-- Profile your lua code. This tells you, which functions are called very often and which consume most real time.
|
||||
-- With this information you can optimize the perfomance of your code.
|
||||
--
|
||||
-- With this information you can optimize the performance of your code.
|
||||
--
|
||||
-- # Prerequisites
|
||||
--
|
||||
-- The modules **os** and **lfs** need to be desanizied.
|
||||
--
|
||||
--
|
||||
--
|
||||
-- The modules **os** and **lfs** need to be de-sanitized.
|
||||
--
|
||||
-- # Start
|
||||
--
|
||||
--
|
||||
-- The profiler can simply be started with the @{#PROFILER.Start}(*Delay, Duration*) function
|
||||
--
|
||||
--
|
||||
-- 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
|
||||
-- stop the profiler after a certain amount of seconds.
|
||||
--
|
||||
--
|
||||
-- # Stop
|
||||
--
|
||||
--
|
||||
-- The profiler automatically stops when the mission ends. But it can be stopped any time with the @{#PROFILER.Stop}(*Delay*) function
|
||||
--
|
||||
--
|
||||
-- PROFILER.Stop()
|
||||
--
|
||||
--
|
||||
-- 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.
|
||||
--
|
||||
--
|
||||
-- # Output
|
||||
--
|
||||
--
|
||||
-- The profiler output is written to a file in your DCS home folder
|
||||
--
|
||||
--
|
||||
-- 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.
|
||||
--
|
||||
--
|
||||
-- ## Data
|
||||
--
|
||||
--
|
||||
-- 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.
|
||||
--
|
||||
--
|
||||
-- If you only want output for functions that are called more than *X* times per second, you can set
|
||||
--
|
||||
--
|
||||
-- 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).
|
||||
--
|
||||
--
|
||||
-- Furthermore, you can limit the output for functions that consumed a certain amount of CPU time in total by
|
||||
--
|
||||
--
|
||||
-- PROFILER.ThreshTtot=0.005
|
||||
--
|
||||
--
|
||||
-- With this setting, which is also the default, only functions which in total used more than 5 milliseconds CPU time.
|
||||
--
|
||||
--
|
||||
-- @field #PROFILER
|
||||
PROFILER = {
|
||||
ClassName = "PROFILER",
|
||||
@@ -117,97 +114,95 @@ PROFILER = {
|
||||
--- Start profiler.
|
||||
-- @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.
|
||||
function PROFILER.Start(Delay, Duration)
|
||||
function PROFILER.Start( Delay, Duration )
|
||||
|
||||
-- Check if os, io and lfs are available.
|
||||
local go=true
|
||||
local go = true
|
||||
if not os then
|
||||
env.error("ERROR: Profiler needs os to be desanitized!")
|
||||
go=false
|
||||
env.error( "ERROR: Profiler needs os to be de-sanitized!" )
|
||||
go = false
|
||||
end
|
||||
if not io then
|
||||
env.error("ERROR: Profiler needs io to be desanitized!")
|
||||
go=false
|
||||
end
|
||||
env.error( "ERROR: Profiler needs io to be de-sanitized!" )
|
||||
go = false
|
||||
end
|
||||
if not lfs then
|
||||
env.error("ERROR: Profiler needs lfs to be desanitized!")
|
||||
go=false
|
||||
end
|
||||
env.error( "ERROR: Profiler needs lfs to be de-sanitized!" )
|
||||
go = false
|
||||
end
|
||||
if not go then
|
||||
return
|
||||
end
|
||||
|
||||
if Delay and Delay>0 then
|
||||
BASE:ScheduleOnce(Delay, PROFILER.Start, 0, Duration)
|
||||
if Delay and Delay > 0 then
|
||||
BASE:ScheduleOnce( Delay, PROFILER.Start, 0, Duration )
|
||||
else
|
||||
|
||||
|
||||
-- Set start time.
|
||||
PROFILER.TstartGame=timer.getTime()
|
||||
PROFILER.TstartOS=os.clock()
|
||||
|
||||
PROFILER.TstartGame = timer.getTime()
|
||||
PROFILER.TstartOS = os.clock()
|
||||
|
||||
-- Add event handler.
|
||||
world.addEventHandler(PROFILER.eventHandler)
|
||||
|
||||
world.addEventHandler( PROFILER.eventHandler )
|
||||
|
||||
-- Info in log.
|
||||
env.info('############################ Profiler Started ############################')
|
||||
env.info( '############################ Profiler Started ############################' )
|
||||
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
|
||||
env.info(string.format("- Will be stopped when mission ends"))
|
||||
env.info( string.format( "- Will be stopped when mission ends" ) )
|
||||
end
|
||||
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("- 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('###############################################################################')
|
||||
|
||||
|
||||
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( "- 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( '###############################################################################' )
|
||||
|
||||
-- Message on screen
|
||||
local duration=Duration or 600
|
||||
trigger.action.outText("### Profiler running ###", duration)
|
||||
|
||||
local duration = Duration or 600
|
||||
trigger.action.outText( "### Profiler running ###", duration )
|
||||
|
||||
-- Set hook.
|
||||
debug.sethook(PROFILER.hook, "cr")
|
||||
|
||||
debug.sethook( PROFILER.hook, "cr" )
|
||||
|
||||
-- Auto stop profiler.
|
||||
if Duration then
|
||||
PROFILER.Stop(Duration)
|
||||
PROFILER.Stop( Duration )
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- Stop profiler.
|
||||
-- @param #number Delay Delay before stop in seconds.
|
||||
function PROFILER.Stop(Delay)
|
||||
function PROFILER.Stop( Delay )
|
||||
|
||||
if Delay and Delay > 0 then
|
||||
|
||||
BASE:ScheduleOnce( Delay, PROFILER.Stop )
|
||||
|
||||
if Delay and Delay>0 then
|
||||
|
||||
BASE:ScheduleOnce(Delay, PROFILER.Stop)
|
||||
|
||||
else
|
||||
|
||||
-- Remove hook.
|
||||
debug.sethook()
|
||||
|
||||
|
||||
|
||||
-- Run time game.
|
||||
local runTimeGame=timer.getTime()-PROFILER.TstartGame
|
||||
|
||||
local runTimeGame = timer.getTime() - PROFILER.TstartGame
|
||||
|
||||
-- Run time real OS.
|
||||
local runTimeOS=os.clock()-PROFILER.TstartOS
|
||||
|
||||
local runTimeOS = os.clock() - PROFILER.TstartOS
|
||||
|
||||
-- Show info.
|
||||
PROFILER.showInfo(runTimeGame, runTimeOS)
|
||||
|
||||
PROFILER.showInfo( runTimeGame, runTimeOS )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Event handler.
|
||||
function PROFILER.eventHandler:onEvent(event)
|
||||
if event.id==world.event.S_EVENT_MISSION_END then
|
||||
function PROFILER.eventHandler:onEvent( event )
|
||||
if event.id == world.event.S_EVENT_MISSION_END then
|
||||
PROFILER.Stop()
|
||||
end
|
||||
end
|
||||
@@ -218,38 +213,38 @@ end
|
||||
|
||||
--- Debug hook.
|
||||
-- @param #table event Event.
|
||||
function PROFILER.hook(event)
|
||||
function PROFILER.hook( event )
|
||||
|
||||
local f=debug.getinfo(2, "f").func
|
||||
|
||||
if event=='call' then
|
||||
|
||||
if PROFILER.Counters[f]==nil then
|
||||
|
||||
PROFILER.Counters[f]=1
|
||||
PROFILER.dInfo[f]=debug.getinfo(2,"Sn")
|
||||
|
||||
if PROFILER.fTimeTotal[f]==nil then
|
||||
PROFILER.fTimeTotal[f]=0
|
||||
local f = debug.getinfo( 2, "f" ).func
|
||||
|
||||
if event == 'call' then
|
||||
|
||||
if PROFILER.Counters[f] == nil then
|
||||
|
||||
PROFILER.Counters[f] = 1
|
||||
PROFILER.dInfo[f] = debug.getinfo( 2, "Sn" )
|
||||
|
||||
if PROFILER.fTimeTotal[f] == nil then
|
||||
PROFILER.fTimeTotal[f] = 0
|
||||
end
|
||||
|
||||
|
||||
else
|
||||
PROFILER.Counters[f]=PROFILER.Counters[f]+1
|
||||
PROFILER.Counters[f] = PROFILER.Counters[f] + 1
|
||||
end
|
||||
|
||||
if PROFILER.fTime[f]==nil then
|
||||
PROFILER.fTime[f]=os.clock()
|
||||
|
||||
if PROFILER.fTime[f] == nil then
|
||||
PROFILER.fTime[f] = os.clock()
|
||||
end
|
||||
|
||||
elseif (event=='return') then
|
||||
|
||||
if PROFILER.fTime[f]~=nil then
|
||||
PROFILER.fTimeTotal[f]=PROFILER.fTimeTotal[f]+(os.clock()-PROFILER.fTime[f])
|
||||
PROFILER.fTime[f]=nil
|
||||
|
||||
elseif (event == 'return') then
|
||||
|
||||
if PROFILER.fTime[f] ~= nil then
|
||||
PROFILER.fTimeTotal[f] = PROFILER.fTimeTotal[f] + (os.clock() - PROFILER.fTime[f])
|
||||
PROFILER.fTime[f] = nil
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -262,105 +257,104 @@ end
|
||||
-- @return #string Source file name.
|
||||
-- @return #string Line number.
|
||||
-- @return #number Function time in seconds.
|
||||
function PROFILER.getData(func)
|
||||
function PROFILER.getData( func )
|
||||
|
||||
local n=PROFILER.dInfo[func]
|
||||
|
||||
if n.what=="C" then
|
||||
local n = PROFILER.dInfo[func]
|
||||
|
||||
if n.what == "C" then
|
||||
return n.name, "?", "?", PROFILER.fTimeTotal[func]
|
||||
end
|
||||
|
||||
return n.name, n.short_src, n.linedefined, PROFILER.fTimeTotal[func]
|
||||
|
||||
return n.name, n.short_src, n.linedefined, PROFILER.fTimeTotal[func]
|
||||
end
|
||||
|
||||
--- Write text to log file.
|
||||
-- @param #function f The file.
|
||||
-- @param #string txt The text.
|
||||
function PROFILER._flog(f, txt)
|
||||
f:write(txt.."\r\n")
|
||||
function PROFILER._flog( f, txt )
|
||||
f:write( txt .. "\r\n" )
|
||||
end
|
||||
|
||||
--- Show table.
|
||||
-- @param #table data Data table.
|
||||
-- @param #function f The file.
|
||||
-- @param #number runTimeGame Game run time in seconds.
|
||||
function PROFILER.showTable(data, f, runTimeGame)
|
||||
function PROFILER.showTable( data, f, runTimeGame )
|
||||
|
||||
-- Loop over data.
|
||||
for i=1, #data do
|
||||
local t=data[i] --#PROFILER.Data
|
||||
|
||||
for i = 1, #data do
|
||||
local t = data[i] -- #PROFILER.Data
|
||||
|
||||
-- Calls per second.
|
||||
local cps=t.count/runTimeGame
|
||||
|
||||
local threshCPS=cps>=PROFILER.ThreshCPS
|
||||
local threshTot=t.tm>=PROFILER.ThreshTtot
|
||||
|
||||
local cps = t.count / runTimeGame
|
||||
|
||||
local threshCPS = cps >= PROFILER.ThreshCPS
|
||||
local threshTot = t.tm >= PROFILER.ThreshTtot
|
||||
|
||||
if threshCPS and threshTot then
|
||||
|
||||
|
||||
-- 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))
|
||||
PROFILER._flog(f, text)
|
||||
|
||||
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 )
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- Print csv file.
|
||||
-- @param #table data Data table.
|
||||
-- @param #number runTimeGame Game run time in seconds.
|
||||
function PROFILER.printCSV(data, runTimeGame)
|
||||
function PROFILER.printCSV( data, runTimeGame )
|
||||
|
||||
-- Output file.
|
||||
local file=PROFILER.getfilename("csv")
|
||||
local g=io.open(file, 'w')
|
||||
local file = PROFILER.getfilename( "csv" )
|
||||
local g = io.open( file, 'w' )
|
||||
|
||||
-- Header.
|
||||
local text="Function,Total Calls,Calls per Sec,Total Time,Total in %,Sec per Call,Source File;Line Number,"
|
||||
g:write(text.."\r\n")
|
||||
|
||||
local text = "Function,Total Calls,Calls per Sec,Total Time,Total in %,Sec per Call,Source File;Line Number,"
|
||||
g:write( text .. "\r\n" )
|
||||
|
||||
-- Loop over data.
|
||||
for i=1, #data do
|
||||
local t=data[i] --#PROFILER.Data
|
||||
|
||||
for i = 1, #data do
|
||||
local t = data[i] -- #PROFILER.Data
|
||||
|
||||
-- Calls per second.
|
||||
local cps=t.count/runTimeGame
|
||||
local cps = t.count / runTimeGame
|
||||
|
||||
-- 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))
|
||||
g:write(txt.."\r\n")
|
||||
|
||||
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" )
|
||||
|
||||
end
|
||||
|
||||
|
||||
-- Close file.
|
||||
g:close()
|
||||
end
|
||||
|
||||
|
||||
--- Write info to output file.
|
||||
-- @param #string ext Extension.
|
||||
-- @return #string File name.
|
||||
function PROFILER.getfilename(ext)
|
||||
function PROFILER.getfilename( ext )
|
||||
|
||||
local dir=lfs.writedir()..[[Logs\]]
|
||||
|
||||
ext=ext or PROFILER.fileNameSuffix
|
||||
|
||||
local file=dir..PROFILER.fileNamePrefix.."."..ext
|
||||
|
||||
if not UTILS.FileExists(file) then
|
||||
local dir = lfs.writedir() .. [[Logs\]]
|
||||
|
||||
ext = ext or PROFILER.fileNameSuffix
|
||||
|
||||
local file = dir .. PROFILER.fileNamePrefix .. "." .. ext
|
||||
|
||||
if not UTILS.FileExists( file ) then
|
||||
return file
|
||||
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
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -368,172 +362,172 @@ end
|
||||
--- Write info to output file.
|
||||
-- @param #number runTimeGame Game time in seconds.
|
||||
-- @param #number runTimeOS OS time in seconds.
|
||||
function PROFILER.showInfo(runTimeGame, runTimeOS)
|
||||
function PROFILER.showInfo( runTimeGame, runTimeOS )
|
||||
|
||||
-- Output file.
|
||||
local file=PROFILER.getfilename(PROFILER.fileNameSuffix)
|
||||
local f=io.open(file, 'w')
|
||||
|
||||
local file = PROFILER.getfilename( PROFILER.fileNameSuffix )
|
||||
local f = io.open( file, 'w' )
|
||||
|
||||
-- Gather data.
|
||||
local Ttot=0
|
||||
local Calls=0
|
||||
|
||||
local t={}
|
||||
|
||||
local tcopy=nil --#PROFILER.Data
|
||||
local tserialize=nil --#PROFILER.Data
|
||||
local tforgen=nil --#PROFILER.Data
|
||||
local tpairs=nil --#PROFILER.Data
|
||||
|
||||
|
||||
for func, count in pairs(PROFILER.Counters) do
|
||||
|
||||
local s,src,line,tm=PROFILER.getData(func)
|
||||
|
||||
if PROFILER.logUnknown==true then
|
||||
if s==nil then s="<Unknown>" end
|
||||
end
|
||||
|
||||
if s~=nil then
|
||||
|
||||
-- Profile data.
|
||||
local T=
|
||||
{ func=s,
|
||||
src=src,
|
||||
line=line,
|
||||
count=count,
|
||||
tm=tm,
|
||||
} --#PROFILER.Data
|
||||
|
||||
-- Collect special cases. Somehow, e.g. "_copy" appears multiple times so we try to gather all data.
|
||||
if s=="_copy" then
|
||||
if tcopy==nil then
|
||||
tcopy=T
|
||||
else
|
||||
tcopy.count=tcopy.count+T.count
|
||||
tcopy.tm=tcopy.tm+T.tm
|
||||
end
|
||||
elseif s=="_Serialize" then
|
||||
if tserialize==nil then
|
||||
tserialize=T
|
||||
else
|
||||
tserialize.count=tserialize.count+T.count
|
||||
tserialize.tm=tserialize.tm+T.tm
|
||||
end
|
||||
elseif s=="(for generator)" then
|
||||
if tforgen==nil then
|
||||
tforgen=T
|
||||
else
|
||||
tforgen.count=tforgen.count+T.count
|
||||
tforgen.tm=tforgen.tm+T.tm
|
||||
end
|
||||
elseif s=="pairs" then
|
||||
if tpairs==nil then
|
||||
tpairs=T
|
||||
else
|
||||
tpairs.count=tpairs.count+T.count
|
||||
tpairs.tm=tpairs.tm+T.tm
|
||||
end
|
||||
else
|
||||
table.insert(t, T)
|
||||
local Ttot = 0
|
||||
local Calls = 0
|
||||
|
||||
local t = {}
|
||||
|
||||
local tcopy = nil -- #PROFILER.Data
|
||||
local tserialize = nil -- #PROFILER.Data
|
||||
local tforgen = nil -- #PROFILER.Data
|
||||
local tpairs = nil -- #PROFILER.Data
|
||||
|
||||
for func, count in pairs( PROFILER.Counters ) do
|
||||
|
||||
local s, src, line, tm = PROFILER.getData( func )
|
||||
|
||||
if PROFILER.logUnknown == true then
|
||||
if s == nil then
|
||||
s = "<Unknown>"
|
||||
end
|
||||
|
||||
-- Total function time.
|
||||
Ttot=Ttot+tm
|
||||
|
||||
-- Total number of calls.
|
||||
Calls=Calls+count
|
||||
|
||||
end
|
||||
|
||||
|
||||
if s ~= nil then
|
||||
|
||||
-- Profile data.
|
||||
local T = { func = s, src = src, line = line, count = count, tm = tm } -- #PROFILER.Data
|
||||
|
||||
-- Collect special cases. Somehow, e.g. "_copy" appears multiple times so we try to gather all data.
|
||||
if s == "_copy" then
|
||||
if tcopy == nil then
|
||||
tcopy = T
|
||||
else
|
||||
tcopy.count = tcopy.count + T.count
|
||||
tcopy.tm = tcopy.tm + T.tm
|
||||
end
|
||||
elseif s == "_Serialize" then
|
||||
if tserialize == nil then
|
||||
tserialize = T
|
||||
else
|
||||
tserialize.count = tserialize.count + T.count
|
||||
tserialize.tm = tserialize.tm + T.tm
|
||||
end
|
||||
elseif s == "(for generator)" then
|
||||
if tforgen == nil then
|
||||
tforgen = T
|
||||
else
|
||||
tforgen.count = tforgen.count + T.count
|
||||
tforgen.tm = tforgen.tm + T.tm
|
||||
end
|
||||
elseif s == "pairs" then
|
||||
if tpairs == nil then
|
||||
tpairs = T
|
||||
else
|
||||
tpairs.count = tpairs.count + T.count
|
||||
tpairs.tm = tpairs.tm + T.tm
|
||||
end
|
||||
else
|
||||
table.insert( t, T )
|
||||
end
|
||||
|
||||
-- Total function time.
|
||||
Ttot = Ttot + tm
|
||||
|
||||
-- Total number of calls.
|
||||
Calls = Calls + count
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- Add special cases.
|
||||
if tcopy then
|
||||
table.insert(t, tcopy)
|
||||
table.insert( t, tcopy )
|
||||
end
|
||||
if tserialize then
|
||||
table.insert(t, tserialize)
|
||||
table.insert( t, tserialize )
|
||||
end
|
||||
if tforgen then
|
||||
table.insert(t, tforgen)
|
||||
table.insert( t, tforgen )
|
||||
end
|
||||
if tpairs then
|
||||
table.insert(t, tpairs)
|
||||
end
|
||||
|
||||
env.info('############################ Profiler Stopped ############################')
|
||||
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("* Function time : %s = %.1f sec (%.1f percent of runtime game)", UTILS.SecondsToClock(Ttot, true), Ttot, Ttot/runTimeGame*100))
|
||||
env.info(string.format("* Total functions : %d", #t))
|
||||
env.info(string.format("* Total func calls : %d", Calls))
|
||||
env.info(string.format("* Writing to file : \"%s\"", file))
|
||||
env.info(string.format("* Writing to file : \"%s\"", PROFILER.getfilename("csv")))
|
||||
env.info("##############################################################################")
|
||||
|
||||
table.insert( t, tpairs )
|
||||
end
|
||||
|
||||
env.info( '############################ Profiler Stopped ############################' )
|
||||
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( "* Function time : %s = %.1f sec (%.1f percent of runtime game)", UTILS.SecondsToClock( Ttot, true ), Ttot, Ttot / runTimeGame * 100 ) )
|
||||
env.info( string.format( "* Total functions : %d", #t ) )
|
||||
env.info( string.format( "* Total func calls : %d", Calls ) )
|
||||
env.info( string.format( "* Writing to file : \"%s\"", file ) )
|
||||
env.info( string.format( "* Writing to file : \"%s\"", PROFILER.getfilename( "csv" ) ) )
|
||||
env.info( "##############################################################################" )
|
||||
|
||||
-- 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.
|
||||
PROFILER._flog(f,"")
|
||||
PROFILER._flog(f,"************************************************************************************************************************")
|
||||
PROFILER._flog(f,"************************************************************************************************************************")
|
||||
PROFILER._flog(f,"************************************************************************************************************************")
|
||||
PROFILER._flog(f,"")
|
||||
PROFILER._flog(f,"-------------------------")
|
||||
PROFILER._flog(f,"---- Profiler Report ----")
|
||||
PROFILER._flog(f,"-------------------------")
|
||||
PROFILER._flog(f,"")
|
||||
PROFILER._flog(f,string.format("* Runtime Game : %s = %.1f sec", UTILS.SecondsToClock(runTimeGame, true), runTimeGame))
|
||||
PROFILER._flog(f,string.format("* Runtime Real : %s = %.1f sec", UTILS.SecondsToClock(runTimeOS, true), runTimeOS))
|
||||
PROFILER._flog(f,string.format("* Function time : %s = %.1f sec (%.1f %% of runtime game)", UTILS.SecondsToClock(Ttot, true), Ttot, Ttot/runTimeGame*100))
|
||||
PROFILER._flog(f,"")
|
||||
PROFILER._flog(f,string.format("* Total functions = %d", #t))
|
||||
PROFILER._flog(f,string.format("* Total func calls = %d", Calls))
|
||||
PROFILER._flog(f,"")
|
||||
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,"")
|
||||
PROFILER._flog(f,"************************************************************************************************************************")
|
||||
PROFILER._flog(f,"")
|
||||
PROFILER.showTable(t, f, runTimeGame)
|
||||
PROFILER._flog( f, "" )
|
||||
PROFILER._flog( f, "************************************************************************************************************************" )
|
||||
PROFILER._flog( f, "************************************************************************************************************************" )
|
||||
PROFILER._flog( f, "************************************************************************************************************************" )
|
||||
PROFILER._flog( f, "" )
|
||||
PROFILER._flog( f, "-------------------------" )
|
||||
PROFILER._flog( f, "---- Profiler Report ----" )
|
||||
PROFILER._flog( f, "-------------------------" )
|
||||
PROFILER._flog( f, "" )
|
||||
PROFILER._flog( f, string.format( "* Runtime Game : %s = %.1f sec", UTILS.SecondsToClock( runTimeGame, true ), runTimeGame ) )
|
||||
PROFILER._flog( f, string.format( "* Runtime Real : %s = %.1f sec", UTILS.SecondsToClock( runTimeOS, true ), runTimeOS ) )
|
||||
PROFILER._flog( f, string.format( "* Function time : %s = %.1f sec (%.1f %% of runtime game)", UTILS.SecondsToClock( Ttot, true ), Ttot, Ttot / runTimeGame * 100 ) )
|
||||
PROFILER._flog( f, "" )
|
||||
PROFILER._flog( f, string.format( "* Total functions = %d", #t ) )
|
||||
PROFILER._flog( f, string.format( "* Total func calls = %d", Calls ) )
|
||||
PROFILER._flog( f, "" )
|
||||
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, "" )
|
||||
PROFILER._flog( f, "************************************************************************************************************************" )
|
||||
PROFILER._flog( f, "" )
|
||||
PROFILER.showTable( t, f, runTimeGame )
|
||||
|
||||
-- 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.
|
||||
PROFILER._flog(f,"")
|
||||
PROFILER._flog(f,"************************************************************************************************************************")
|
||||
PROFILER._flog(f,"")
|
||||
PROFILER._flog(f,"--------------------------------------")
|
||||
PROFILER._flog(f,"---- Data Sorted by Time per Call ----")
|
||||
PROFILER._flog(f,"--------------------------------------")
|
||||
PROFILER._flog(f,"")
|
||||
PROFILER.showTable(t, f, runTimeGame)
|
||||
|
||||
PROFILER._flog( f, "" )
|
||||
PROFILER._flog( f, "************************************************************************************************************************" )
|
||||
PROFILER._flog( f, "" )
|
||||
PROFILER._flog( f, "--------------------------------------" )
|
||||
PROFILER._flog( f, "---- Data Sorted by Time per Call ----" )
|
||||
PROFILER._flog( f, "--------------------------------------" )
|
||||
PROFILER._flog( f, "" )
|
||||
PROFILER.showTable( t, f, runTimeGame )
|
||||
|
||||
-- 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.
|
||||
PROFILER._flog(f,"")
|
||||
PROFILER._flog(f,"************************************************************************************************************************")
|
||||
PROFILER._flog(f,"")
|
||||
PROFILER._flog(f,"------------------------------------")
|
||||
PROFILER._flog(f,"---- Data Sorted by Total Calls ----")
|
||||
PROFILER._flog(f,"------------------------------------")
|
||||
PROFILER._flog(f,"")
|
||||
PROFILER.showTable(t, f, runTimeGame)
|
||||
|
||||
PROFILER._flog( f, "" )
|
||||
PROFILER._flog( f, "************************************************************************************************************************" )
|
||||
PROFILER._flog( f, "" )
|
||||
PROFILER._flog( f, "------------------------------------" )
|
||||
PROFILER._flog( f, "---- Data Sorted by Total Calls ----" )
|
||||
PROFILER._flog( f, "------------------------------------" )
|
||||
PROFILER._flog( f, "" )
|
||||
PROFILER.showTable( t, f, runTimeGame )
|
||||
|
||||
-- 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.
|
||||
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).
|
||||
--
|
||||
--
|
||||
--
|
||||
--
|
||||
--
|
||||
-- @module Utils.STTS
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
@@ -10,29 +9,29 @@
|
||||
-- @field #string DIRECTORY Path of the SRS directory.
|
||||
|
||||
--- Simple Text-To-Speech
|
||||
--
|
||||
--
|
||||
-- Version 0.4 - Compatible with SRS version 1.9.6.0+
|
||||
--
|
||||
--
|
||||
-- # DCS Modification Required
|
||||
--
|
||||
-- You will need to edit MissionScripting.lua in DCS World/Scripts/MissionScripting.lua and remove the 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)"
|
||||
-- Do this without DCS running to allow mission scripts to use os functions.
|
||||
--
|
||||
--
|
||||
-- *You WILL HAVE TO REAPPLY AFTER EVERY DCS UPDATE*
|
||||
--
|
||||
--
|
||||
-- # USAGE:
|
||||
--
|
||||
-- Add this script into the mission as a DO SCRIPT or DO SCRIPT FROM FILE to 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.
|
||||
-- Then its as simple as calling the correct function in LUA as a DO SCRIPT or in your own scripts.
|
||||
--
|
||||
--
|
||||
-- Example calls:
|
||||
--
|
||||
-- STTS.TextToSpeech("Hello DCS WORLD","251","AM","1.0","SRS",2)
|
||||
--
|
||||
--
|
||||
-- Arguments in order are:
|
||||
--
|
||||
--
|
||||
-- * Message to say, make sure not to use a newline (\n) !
|
||||
-- * Frequency in MHz
|
||||
-- * Modulation - AM/FM
|
||||
@@ -43,44 +42,44 @@
|
||||
-- * OPTIONAL - Speed -10 to +10
|
||||
-- * OPTIONAL - Gender male, female or neuter
|
||||
-- * 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
|
||||
--
|
||||
--
|
||||
-- ## Example
|
||||
--
|
||||
-- This example will say the words "Hello DCS WORLD" on 251 MHz AM at maximum volume with a client called SRS and to the Blue coalition only
|
||||
--
|
||||
--
|
||||
-- STTS.TextToSpeech("Hello DCS WORLD","251","AM","1.0","SRS",2,null,-5,"male","en-GB")
|
||||
--
|
||||
--
|
||||
-- ## Example
|
||||
--
|
||||
--This example will say the words "Hello DCS WORLD" on 251 MHz AM at maximum volume with a client called SRS and to the Blue coalition only centered on the position of the Unit called "A UNIT"
|
||||
--
|
||||
-- This example will say the words "Hello DCS WORLD" on 251 MHz AM at maximum volume with a client called SRS and to the Blue coalition only centered on the position of the Unit called "A UNIT"
|
||||
--
|
||||
-- STTS.TextToSpeech("Hello DCS WORLD","251","AM","1.0","SRS",2,Unit.getByName("A UNIT"):getPoint(),-5,"male","en-GB")
|
||||
--
|
||||
--
|
||||
-- Arguments in order are:
|
||||
--
|
||||
--
|
||||
-- * FULL path to the MP3 OR OGG to play
|
||||
-- * Frequency in MHz - to use multiple separate with a comma - Number of frequencies MUST match number of Modulations
|
||||
-- * Modulation - AM/FM - to use multiple
|
||||
-- * Volume - 1.0 max, 0.5 half
|
||||
-- * Name of the transmitter - ATC, RockFM etc
|
||||
-- * Coalition - 0 spectator, 1 red 2 blue
|
||||
--
|
||||
--
|
||||
-- ## Example
|
||||
--
|
||||
--
|
||||
-- This will play that MP3 on 255MHz AM & 31 FM at half volume with a client called "Multiple" and to Spectators only
|
||||
--
|
||||
--
|
||||
-- STTS.PlayMP3("C:\\Users\\Ciaran\\Downloads\\PR-Music.mp3","255,31","AM,FM","0.5","Multiple",0)
|
||||
--
|
||||
--
|
||||
-- @field #STTS
|
||||
STTS={
|
||||
ClassName="STTS",
|
||||
DIRECTORY="",
|
||||
SRS_PORT=5002,
|
||||
GOOGLE_CREDENTIALS="C:\\Users\\Ciaran\\Downloads\\googletts.json",
|
||||
EXECUTABLE="DCS-SR-ExternalAudio.exe",
|
||||
STTS = {
|
||||
ClassName = "STTS",
|
||||
DIRECTORY = "",
|
||||
SRS_PORT = 5002,
|
||||
GOOGLE_CREDENTIALS = "C:\\Users\\Ciaran\\Downloads\\googletts.json",
|
||||
EXECUTABLE = "DCS-SR-ExternalAudio.exe"
|
||||
}
|
||||
|
||||
--- FULL Path to the FOLDER containing DCS-SR-ExternalAudio.exe - EDIT TO CORRECT FOLDER
|
||||
@@ -92,142 +91,144 @@ STTS.SRS_PORT = 5002
|
||||
--- Google credentials file
|
||||
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"
|
||||
|
||||
|
||||
--- Function for UUID.
|
||||
function STTS.uuid()
|
||||
local random = math.random
|
||||
local template ='yxxx-xxxxxxxxxxxx'
|
||||
return string.gsub(template, '[xy]', function (c)
|
||||
local v = (c == 'x') and random(0, 0xf) or random(8, 0xb)
|
||||
return string.format('%x', v)
|
||||
end)
|
||||
local template = 'yxxx-xxxxxxxxxxxx'
|
||||
return string.gsub( template, '[xy]', function( c )
|
||||
local v = (c == 'x') and random( 0, 0xf ) or random( 8, 0xb )
|
||||
return string.format( '%x', v )
|
||||
end )
|
||||
end
|
||||
|
||||
--- Round a number.
|
||||
-- @param #number x Number.
|
||||
-- @param #number n Precision.
|
||||
function STTS.round(x, n)
|
||||
n = math.pow(10, n or 0)
|
||||
function STTS.round( x, n )
|
||||
n = math.pow( 10, n or 0 )
|
||||
x = x * n
|
||||
if x >= 0 then x = math.floor(x + 0.5) else x = math.ceil(x - 0.5) end
|
||||
if x >= 0 then
|
||||
x = math.floor( x + 0.5 )
|
||||
else
|
||||
x = math.ceil( x - 0.5 )
|
||||
end
|
||||
return x / n
|
||||
end
|
||||
|
||||
--- Function returns estimated speech time in seconds.
|
||||
-- Assumptions for time calc: 100 Words per min, 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
|
||||
--
|
||||
-- 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
|
||||
--
|
||||
function STTS.getSpeechTime(length,speed,isGoogle)
|
||||
--
|
||||
function STTS.getSpeechTime( length, speed, isGoogle )
|
||||
|
||||
local maxRateRatio = 3
|
||||
local maxRateRatio = 3
|
||||
|
||||
speed = speed or 1.0
|
||||
isGoogle = isGoogle or false
|
||||
|
||||
local speedFactor = 1.0
|
||||
if isGoogle then
|
||||
speedFactor = speed
|
||||
speedFactor = speed
|
||||
else
|
||||
if speed ~= 0 then
|
||||
speedFactor = math.abs(speed) * (maxRateRatio - 1) / 10 + 1
|
||||
end
|
||||
if speed < 0 then
|
||||
speedFactor = 1/speedFactor
|
||||
end
|
||||
if speed ~= 0 then
|
||||
speedFactor = math.abs( speed ) * (maxRateRatio - 1) / 10 + 1
|
||||
end
|
||||
if speed < 0 then
|
||||
speedFactor = 1 / speedFactor
|
||||
end
|
||||
end
|
||||
|
||||
local wpm = math.ceil(100 * speedFactor)
|
||||
local cps = math.floor((wpm * 5)/60)
|
||||
local wpm = math.ceil( 100 * speedFactor )
|
||||
local cps = math.floor( (wpm * 5) / 60 )
|
||||
|
||||
if type(length) == "string" then
|
||||
length = string.len(length)
|
||||
if type( length ) == "string" then
|
||||
length = string.len( length )
|
||||
end
|
||||
|
||||
return math.ceil(length/cps)
|
||||
return math.ceil( length / cps )
|
||||
end
|
||||
|
||||
--- Text to speech function.
|
||||
function STTS.TextToSpeech(message, freqs, modulations, volume, name, coalition, point, speed, gender, culture, voice, googleTTS)
|
||||
if os == nil or io == nil then
|
||||
env.info("[DCS-STTS] LUA modules os or io are sanitized. skipping. ")
|
||||
return
|
||||
end
|
||||
function STTS.TextToSpeech( message, freqs, modulations, volume, name, coalition, point, speed, gender, culture, voice, googleTTS )
|
||||
if os == nil or io == nil then
|
||||
env.info( "[DCS-STTS] LUA modules os or io are sanitized. skipping. " )
|
||||
return
|
||||
end
|
||||
|
||||
speed = speed or 1
|
||||
gender = gender or "female"
|
||||
culture = culture or ""
|
||||
voice = voice or ""
|
||||
coalition=coalition or "0"
|
||||
name=name or "ROBOT"
|
||||
volume=1
|
||||
speed=1
|
||||
speed = speed or 1
|
||||
gender = gender or "female"
|
||||
culture = culture or ""
|
||||
voice = voice or ""
|
||||
coalition = coalition or "0"
|
||||
name = name or "ROBOT"
|
||||
volume = 1
|
||||
speed = 1
|
||||
|
||||
message = message:gsub( "\"", "\\\"" )
|
||||
|
||||
local cmd = string.format( "start /min \"\" /d \"%s\" /b \"%s\" -f %s -m %s -c %s -p %s -n \"%s\" -h", STTS.DIRECTORY, STTS.EXECUTABLE, freqs or "305", modulations or "AM", coalition, STTS.SRS_PORT, name )
|
||||
|
||||
message = message:gsub("\"","\\\"")
|
||||
|
||||
local cmd = string.format("start /min \"\" /d \"%s\" /b \"%s\" -f %s -m %s -c %s -p %s -n \"%s\" -h", STTS.DIRECTORY, STTS.EXECUTABLE, freqs or "305", modulations or "AM", coalition, STTS.SRS_PORT, name)
|
||||
|
||||
if voice ~= "" then
|
||||
cmd = cmd .. string.format(" -V \"%s\"",voice)
|
||||
cmd = cmd .. string.format( " -V \"%s\"", voice )
|
||||
else
|
||||
|
||||
if culture ~= "" then
|
||||
cmd = cmd .. string.format(" -l %s",culture)
|
||||
end
|
||||
if culture ~= "" then
|
||||
cmd = cmd .. string.format( " -l %s", culture )
|
||||
end
|
||||
|
||||
if gender ~= "" then
|
||||
cmd = cmd .. string.format(" -g %s",gender)
|
||||
end
|
||||
if gender ~= "" then
|
||||
cmd = cmd .. string.format( " -g %s", gender )
|
||||
end
|
||||
end
|
||||
|
||||
if googleTTS == true then
|
||||
cmd = cmd .. string.format(" -G \"%s\"",STTS.GOOGLE_CREDENTIALS)
|
||||
cmd = cmd .. string.format( " -G \"%s\"", STTS.GOOGLE_CREDENTIALS )
|
||||
end
|
||||
|
||||
if speed ~= 1 then
|
||||
cmd = cmd .. string.format(" -s %s",speed)
|
||||
cmd = cmd .. string.format( " -s %s", speed )
|
||||
end
|
||||
|
||||
if volume ~= 1.0 then
|
||||
cmd = cmd .. string.format(" -v %s",volume)
|
||||
cmd = cmd .. string.format( " -v %s", volume )
|
||||
end
|
||||
|
||||
if point and type(point) == "table" and point.x then
|
||||
local lat, lon, alt = coord.LOtoLL(point)
|
||||
if point and type( point ) == "table" and point.x then
|
||||
local lat, lon, alt = coord.LOtoLL( point )
|
||||
|
||||
lat = STTS.round(lat,4)
|
||||
lon = STTS.round(lon,4)
|
||||
alt = math.floor(alt)
|
||||
lat = STTS.round( lat, 4 )
|
||||
lon = STTS.round( lon, 4 )
|
||||
alt = math.floor( alt )
|
||||
|
||||
cmd = cmd .. string.format(" -L %s -O %s -A %s",lat,lon,alt)
|
||||
cmd = cmd .. string.format( " -L %s -O %s -A %s", lat, lon, alt )
|
||||
end
|
||||
|
||||
cmd = cmd ..string.format(" -t \"%s\"",message)
|
||||
cmd = cmd .. string.format( " -t \"%s\"", message )
|
||||
|
||||
if string.len(cmd) > 255 then
|
||||
local filename = os.getenv('TMP') .. "\\DCS_STTS-" .. STTS.uuid() .. ".bat"
|
||||
local script = io.open(filename,"w+")
|
||||
script:write(cmd .. " && exit" )
|
||||
script:close()
|
||||
cmd = string.format("\"%s\"",filename)
|
||||
timer.scheduleFunction(os.remove, filename, timer.getTime() + 1)
|
||||
if string.len( cmd ) > 255 then
|
||||
local filename = os.getenv( 'TMP' ) .. "\\DCS_STTS-" .. STTS.uuid() .. ".bat"
|
||||
local script = io.open( filename, "w+" )
|
||||
script:write( cmd .. " && exit" )
|
||||
script:close()
|
||||
cmd = string.format( "\"%s\"", filename )
|
||||
timer.scheduleFunction( os.remove, filename, timer.getTime() + 1 )
|
||||
end
|
||||
|
||||
if string.len(cmd) > 255 then
|
||||
env.info("[DCS-STTS] - cmd string too long")
|
||||
env.info("[DCS-STTS] TextToSpeech Command :\n" .. cmd.."\n")
|
||||
if string.len( cmd ) > 255 then
|
||||
env.info( "[DCS-STTS] - cmd string too long" )
|
||||
env.info( "[DCS-STTS] TextToSpeech Command :\n" .. cmd .. "\n" )
|
||||
end
|
||||
os.execute(cmd)
|
||||
os.execute( cmd )
|
||||
|
||||
return STTS.getSpeechTime(message,speed,googleTTS)
|
||||
return STTS.getSpeechTime( message, speed, googleTTS )
|
||||
end
|
||||
|
||||
--- Play mp3 function.
|
||||
@@ -235,22 +236,21 @@ end
|
||||
-- @param #string freqs Frequencies, e.g. "305, 256".
|
||||
-- @param #string modulations Modulations, e.g. "AM, FM".
|
||||
-- @param #string volume Volume, e.g. "0.5".
|
||||
function STTS.PlayMP3(pathToMP3, freqs, modulations, volume, name, coalition, point)
|
||||
function STTS.PlayMP3( pathToMP3, freqs, modulations, volume, name, coalition, point )
|
||||
|
||||
local cmd = string.format("start \"\" /d \"%s\" /b /min \"%s\" -i \"%s\" -f %s -m %s -c %s -p %s -n \"%s\" -v %s -h",
|
||||
STTS.DIRECTORY, STTS.EXECUTABLE, pathToMP3, freqs or "305", modulations or "AM", coalition or "0", STTS.SRS_PORT, name or "ROBOT", volume or "1")
|
||||
|
||||
if point and type(point) == "table" and point.x then
|
||||
local lat, lon, alt = coord.LOtoLL(point)
|
||||
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" )
|
||||
|
||||
lat = STTS.round(lat,4)
|
||||
lon = STTS.round(lon,4)
|
||||
alt = math.floor(alt)
|
||||
if point and type( point ) == "table" and point.x then
|
||||
local lat, lon, alt = coord.LOtoLL( point )
|
||||
|
||||
cmd = cmd .. string.format(" -L %s -O %s -A %s",lat,lon,alt)
|
||||
end
|
||||
lat = STTS.round( lat, 4 )
|
||||
lon = STTS.round( lon, 4 )
|
||||
alt = math.floor( alt )
|
||||
|
||||
env.info("[DCS-STTS] MP3/OGG Command :\n" .. cmd.."\n")
|
||||
os.execute(cmd)
|
||||
cmd = cmd .. string.format( " -L %s -O %s -A %s", lat, lon, alt )
|
||||
end
|
||||
|
||||
end
|
||||
env.info( "[DCS-STTS] MP3/OGG Command :\n" .. cmd .. "\n" )
|
||||
os.execute( cmd )
|
||||
|
||||
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
@@ -310,8 +310,7 @@ end
|
||||
|
||||
--- Returns the @{DCS#Position3} position vectors indicating the point and direction vectors in 3D of the POSITIONABLE within the mission.
|
||||
-- @param Wrapper.Positionable#POSITIONABLE self
|
||||
-- @return DCS#Position The 3D position vectors of the POSITIONABLE.
|
||||
-- @return #nil The POSITIONABLE is not existing or alive.
|
||||
-- @return DCS#Position The 3D position vectors of the POSITIONABLE or #nil if the groups not existing or alive.
|
||||
function GROUP:GetPositionVec3() -- Overridden from POSITIONABLE:GetPositionVec3()
|
||||
self:F2( self.PositionableName )
|
||||
|
||||
@@ -339,9 +338,7 @@ end
|
||||
-- If the first @{Wrapper.Unit} of the group is inactive, it will return false.
|
||||
--
|
||||
-- @param #GROUP self
|
||||
-- @return #boolean true if the group is alive and active.
|
||||
-- @return #boolean false if the group is alive but inactive.
|
||||
-- @return #nil if the group does not exist anymore.
|
||||
-- @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.
|
||||
function GROUP:IsAlive()
|
||||
self:F2( self.GroupName )
|
||||
|
||||
@@ -363,8 +360,7 @@ end
|
||||
|
||||
--- Returns if the group is activated.
|
||||
-- @param #GROUP self
|
||||
-- @return #boolean true if group is activated.
|
||||
-- @return #nil The group is not existing or alive.
|
||||
-- @return #boolean `true` if group is activated or `#nil` The group is not existing or alive.
|
||||
function GROUP:IsActive()
|
||||
self:F2( self.GroupName )
|
||||
|
||||
@@ -412,7 +408,6 @@ function GROUP:Destroy( GenerateEvent, delay )
|
||||
self:F2( self.GroupName )
|
||||
|
||||
if delay and delay>0 then
|
||||
--SCHEDULER:New(nil, GROUP.Destroy, {self, GenerateEvent}, delay)
|
||||
self:ScheduleOnce(delay, GROUP.Destroy, self, GenerateEvent)
|
||||
else
|
||||
|
||||
@@ -568,12 +563,12 @@ function GROUP:GetSpeedMax()
|
||||
|
||||
local Units=self:GetUnits()
|
||||
|
||||
local speedmax=nil
|
||||
local speedmax=0
|
||||
|
||||
for _,unit in pairs(Units) do
|
||||
local unit=unit --Wrapper.Unit#UNIT
|
||||
local speed=unit:GetSpeedMax()
|
||||
if speedmax==nil then
|
||||
if speedmax==0 then
|
||||
speedmax=speed
|
||||
elseif speed<speedmax then
|
||||
speedmax=speed
|
||||
@@ -1137,7 +1132,7 @@ end
|
||||
-- @return #number Number of shells left.
|
||||
-- @return #number Number of rockets left.
|
||||
-- @return #number Number of bombs left.
|
||||
-- @return #number Number of missiles left.
|
||||
-- @return #number Number of missiles left.
|
||||
function GROUP:GetAmmunition()
|
||||
self:F( self.ControllableName )
|
||||
|
||||
@@ -1147,6 +1142,7 @@ function GROUP:GetAmmunition()
|
||||
local Nshells=0
|
||||
local Nrockets=0
|
||||
local Nmissiles=0
|
||||
local Nbombs=0
|
||||
|
||||
if DCSControllable then
|
||||
|
||||
@@ -1155,18 +1151,19 @@ function GROUP:GetAmmunition()
|
||||
local Unit = UnitData -- Wrapper.Unit#UNIT
|
||||
|
||||
-- Get ammo of the unit
|
||||
local ntot, nshells, nrockets, nmissiles = Unit:GetAmmunition()
|
||||
local ntot, nshells, nrockets, nbombs, nmissiles = Unit:GetAmmunition()
|
||||
|
||||
Ntot=Ntot+ntot
|
||||
Nshells=Nshells+nshells
|
||||
Nrockets=Nrockets+nrockets
|
||||
Nmissiles=Nmissiles+nmissiles
|
||||
Nmissiles=Nmissiles+nmissiles
|
||||
Nbombs=Nbombs+nbombs
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return Ntot, Nshells, Nrockets, Nmissiles
|
||||
return Ntot, Nshells, Nrockets, Nbombs, Nmissiles
|
||||
end
|
||||
|
||||
|
||||
@@ -2324,41 +2321,44 @@ function GROUP:GetAttribute()
|
||||
local unarmedship=self:HasAttribute("Unarmed ships")
|
||||
|
||||
|
||||
-- Define attribute. Order is important.
|
||||
if transportplane then
|
||||
attribute=GROUP.Attribute.AIR_TRANSPORTPLANE
|
||||
elseif awacs then
|
||||
attribute=GROUP.Attribute.AIR_AWACS
|
||||
elseif fighter then
|
||||
-- Define attribute. Order of attack is important.
|
||||
if fighter then
|
||||
attribute=GROUP.Attribute.AIR_FIGHTER
|
||||
elseif bomber then
|
||||
attribute=GROUP.Attribute.AIR_BOMBER
|
||||
elseif awacs then
|
||||
attribute=GROUP.Attribute.AIR_AWACS
|
||||
elseif transportplane then
|
||||
attribute=GROUP.Attribute.AIR_TRANSPORTPLANE
|
||||
elseif tanker then
|
||||
attribute=GROUP.Attribute.AIR_TANKER
|
||||
-- helos
|
||||
elseif attackhelicopter then
|
||||
attribute=GROUP.Attribute.AIR_ATTACKHELO
|
||||
elseif transporthelo then
|
||||
attribute=GROUP.Attribute.AIR_TRANSPORTHELO
|
||||
elseif attackhelicopter then
|
||||
attribute=GROUP.Attribute.AIR_ATTACKHELO
|
||||
elseif uav then
|
||||
attribute=GROUP.Attribute.AIR_UAV
|
||||
elseif apc then
|
||||
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
|
||||
-- ground - order of attack
|
||||
elseif ewr then
|
||||
attribute=GROUP.Attribute.GROUND_EWR
|
||||
elseif sam then
|
||||
attribute=GROUP.Attribute.GROUND_SAM
|
||||
elseif aaa then
|
||||
attribute=GROUP.Attribute.GROUND_AAA
|
||||
elseif artillery then
|
||||
attribute=GROUP.Attribute.GROUND_ARTILLERY
|
||||
elseif tank then
|
||||
attribute=GROUP.Attribute.GROUND_TANK
|
||||
elseif apc then
|
||||
attribute=GROUP.Attribute.GROUND_APC
|
||||
elseif infantry then
|
||||
attribute=GROUP.Attribute.GROUND_INFANTRY
|
||||
elseif truck then
|
||||
attribute=GROUP.Attribute.GROUND_TRUCK
|
||||
elseif train then
|
||||
attribute=GROUP.Attribute.GROUND_TRAIN
|
||||
-- ships
|
||||
elseif aircraftcarrier then
|
||||
attribute=GROUP.Attribute.NAVAL_AIRCRAFTCARRIER
|
||||
elseif warship then
|
||||
@@ -2576,8 +2576,10 @@ end
|
||||
-- @return #GROUP self
|
||||
function GROUP:SetCommandInvisible(switch)
|
||||
self:F2( self.GroupName )
|
||||
local switch = switch or false
|
||||
local SetInvisible = {id = 'SetInvisible', params = {value = true}}
|
||||
if switch==nil then
|
||||
switch=false
|
||||
end
|
||||
local SetInvisible = {id = 'SetInvisible', params = {value = switch}}
|
||||
self:SetCommand(SetInvisible)
|
||||
return self
|
||||
end
|
||||
@@ -2588,12 +2590,59 @@ end
|
||||
-- @return #GROUP self
|
||||
function GROUP:SetCommandImmortal(switch)
|
||||
self:F2( self.GroupName )
|
||||
local switch = switch or false
|
||||
local SetInvisible = {id = 'SetImmortal', params = {value = true}}
|
||||
self:SetCommand(SetInvisible)
|
||||
if switch==nil then
|
||||
switch=false
|
||||
end
|
||||
local SetImmortal = {id = 'SetImmortal', params = {value = switch}}
|
||||
self:SetCommand(SetImmortal)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get skill from Group. Effectively gets the skill from Unit 1 as the group holds no skill value.
|
||||
-- @param #GROUP self
|
||||
-- @return #string Skill String of skill name.
|
||||
function GROUP:GetSkill()
|
||||
self:F2( self.GroupName )
|
||||
local unit = self:GetUnit(1)
|
||||
local name = unit:GetName()
|
||||
local skill = _DATABASE.Templates.Units[name].Template.skill or "Random"
|
||||
return skill
|
||||
end
|
||||
|
||||
--- 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
|
||||
|
||||
--do -- Smoke
|
||||
--
|
||||
----- Signal a flare at the position of the GROUP.
|
||||
|
||||
@@ -176,7 +176,7 @@ function IDENTIFIABLE:GetCoalitionName()
|
||||
|
||||
if DCSIdentifiable then
|
||||
|
||||
-- Get coaliton ID.
|
||||
-- Get coalition ID.
|
||||
local IdentifiableCoalition = DCSIdentifiable:getCoalition()
|
||||
self:T3( IdentifiableCoalition )
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
-- @module Wrapper.Marker
|
||||
-- @image Wrapper_Marker.png
|
||||
|
||||
|
||||
--- Marker class.
|
||||
-- @type MARKER
|
||||
-- @field #string ClassName Name of the class.
|
||||
@@ -24,7 +23,7 @@
|
||||
-- @field #number mid Marker ID.
|
||||
-- @field Core.Point#COORDINATE coordinate Coordinate of the mark.
|
||||
-- @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 #number coalition Coalition to which the marker is displayed.
|
||||
-- @extends Core.Fsm#FSM
|
||||
@@ -42,29 +41,29 @@
|
||||
-- # Create a Marker
|
||||
--
|
||||
-- -- Create a MARKER object at Batumi with a trivial text.
|
||||
-- local Coordinate=AIRBASE:FindByName("Batumi"):GetCoordinate()
|
||||
-- mymarker=MARKER:New(Coordinate, "I am Batumi Airfield")
|
||||
-- local Coordinate = AIRBASE:FindByName( "Batumi" ):GetCoordinate()
|
||||
-- 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.
|
||||
-- show the marker to everyone, to a speficic coaliton only, or only to a specific group.
|
||||
-- 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 specific coalition only, or only to a specific group.
|
||||
--
|
||||
-- ## For Everyone
|
||||
--
|
||||
-- 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.
|
||||
--
|
||||
-- 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
|
||||
--
|
||||
-- This would show the marker only to the Blue coaliton.
|
||||
-- This would show the marker only to the Blue coalition.
|
||||
--
|
||||
-- ## For a Group
|
||||
--
|
||||
@@ -76,28 +75,28 @@
|
||||
--
|
||||
-- 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.*
|
||||
--
|
||||
-- ## 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
|
||||
--
|
||||
-- mymarker:UpdateText("I am the new text at Batumi", 90)
|
||||
-- mymarker:UpdateText( "I am the new text at Batumi", 90 )
|
||||
--
|
||||
-- ## Update Coordinate
|
||||
--
|
||||
-- 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
|
||||
--
|
||||
-- mymarker:UpdateCoordinate(NewCoordinate, 60)
|
||||
-- mymarker:UpdateCoordinate( NewCoordinate , 60 )
|
||||
--
|
||||
-- # Retrieve Data
|
||||
--
|
||||
@@ -105,18 +104,18 @@
|
||||
--
|
||||
-- ## Text
|
||||
--
|
||||
-- local text=mymarker:GetText()
|
||||
-- env.info("Marker Text = " .. text)
|
||||
-- local text =mymarker:GetText()
|
||||
-- env.info( "Marker Text = " .. text )
|
||||
--
|
||||
-- ## Coordinate
|
||||
--
|
||||
-- local Coordinate=mymarker:GetCoordinate()
|
||||
-- env.info("Marker Coordinate LL DSM = " .. Coordinate:ToStringLLDMS())
|
||||
-- local Coordinate = mymarker:GetCoordinate()
|
||||
-- env.info( "Marker Coordinate LL DSM = " .. Coordinate:ToStringLLDMS() )
|
||||
--
|
||||
--
|
||||
-- # 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.
|
||||
--
|
||||
@@ -133,26 +132,25 @@
|
||||
--
|
||||
-- # Examples
|
||||
--
|
||||
--
|
||||
-- @field #MARKER
|
||||
MARKER = {
|
||||
ClassName = "MARKER",
|
||||
Debug = false,
|
||||
lid = nil,
|
||||
mid = nil,
|
||||
coordinate = nil,
|
||||
text = nil,
|
||||
message = nil,
|
||||
readonly = nil,
|
||||
coalition = nil,
|
||||
ClassName = "MARKER",
|
||||
Debug = false,
|
||||
lid = nil,
|
||||
mid = nil,
|
||||
coordinate = nil,
|
||||
text = nil,
|
||||
message = nil,
|
||||
readonly = nil,
|
||||
coalition = nil,
|
||||
}
|
||||
|
||||
--- Marker ID. Running number.
|
||||
_MARKERID=0
|
||||
_MARKERID = 0
|
||||
|
||||
--- Marker class version.
|
||||
-- @field #string version
|
||||
MARKER.version="0.1.0"
|
||||
MARKER.version = "0.1.0"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
@@ -172,38 +170,38 @@ MARKER.version="0.1.0"
|
||||
-- @param Core.Point#COORDINATE Coordinate Coordinate where to place the marker.
|
||||
-- @param #string Text Text displayed on the mark panel.
|
||||
-- @return #MARKER self
|
||||
function MARKER:New(Coordinate, Text)
|
||||
function MARKER:New( Coordinate, Text )
|
||||
|
||||
-- Inherit everything from FSM class.
|
||||
local self=BASE:Inherit(self, FSM:New()) -- #MARKER
|
||||
local self = BASE:Inherit( self, FSM:New() ) -- #MARKER
|
||||
|
||||
self.coordinate=Coordinate
|
||||
self.coordinate = Coordinate
|
||||
|
||||
self.text=Text
|
||||
self.text = Text
|
||||
|
||||
-- Defaults
|
||||
self.readonly=false
|
||||
self.message=""
|
||||
self.readonly = false
|
||||
self.message = ""
|
||||
|
||||
-- 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.
|
||||
self.lid=string.format("Marker #%d | ", self.myid)
|
||||
self.lid = string.format( "Marker #%d | ", self.myid )
|
||||
|
||||
-- Start State.
|
||||
self:SetStartState("Invisible")
|
||||
self:SetStartState( "Invisible" )
|
||||
|
||||
-- Add FSM transitions.
|
||||
-- From State --> Event --> To State
|
||||
self:AddTransition("Invisible", "Added", "Visible") -- Marker was added.
|
||||
self:AddTransition("Visible", "Removed", "Invisible") -- Marker was removed.
|
||||
self:AddTransition("*", "Changed", "*") -- Marker was changed.
|
||||
self:AddTransition( "Invisible", "Added", "Visible" ) -- Marker was added.
|
||||
self:AddTransition( "Visible", "Removed", "Invisible" ) -- Marker was removed.
|
||||
self:AddTransition( "*", "Changed", "*" ) -- Marker was changed.
|
||||
|
||||
self:AddTransition("*", "TextUpdate", "*") -- Text updated.
|
||||
self:AddTransition("*", "CoordUpdate", "*") -- Coordinates updated.
|
||||
self:AddTransition( "*", "TextUpdate", "*" ) -- Text updated.
|
||||
self:AddTransition( "*", "CoordUpdate", "*" ) -- Coordinates updated.
|
||||
|
||||
--- Triggers the FSM event "Added".
|
||||
-- @function [parent=#MARKER] Added
|
||||
@@ -223,7 +221,6 @@ function MARKER:New(Coordinate, Text)
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Event#EVENTDATA EventData Event data table.
|
||||
|
||||
|
||||
--- Triggers the FSM event "Removed".
|
||||
-- @function [parent=#MARKER] Removed
|
||||
-- @param #MARKER self
|
||||
@@ -242,7 +239,6 @@ function MARKER:New(Coordinate, Text)
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Event#EVENTDATA EventData Event data table.
|
||||
|
||||
|
||||
--- Triggers the FSM event "Changed".
|
||||
-- @function [parent=#MARKER] Changed
|
||||
-- @param #MARKER self
|
||||
@@ -261,7 +257,6 @@ function MARKER:New(Coordinate, Text)
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Event#EVENTDATA EventData Event data table.
|
||||
|
||||
|
||||
--- Triggers the FSM event "TextUpdate".
|
||||
-- @function [parent=#MARKER] TextUpdate
|
||||
-- @param #MARKER self
|
||||
@@ -280,7 +275,6 @@ function MARKER:New(Coordinate, Text)
|
||||
-- @param #string To To state.
|
||||
-- @param #string Text The new text.
|
||||
|
||||
|
||||
--- Triggers the FSM event "CoordUpdate".
|
||||
-- @function [parent=#MARKER] CoordUpdate
|
||||
-- @param #MARKER self
|
||||
@@ -299,11 +293,10 @@ function MARKER:New(Coordinate, Text)
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Point#COORDINATE Coordinate The updated Coordinate.
|
||||
|
||||
|
||||
-- Handle events.
|
||||
self:HandleEvent(EVENTS.MarkAdded)
|
||||
self:HandleEvent(EVENTS.MarkRemoved)
|
||||
self:HandleEvent(EVENTS.MarkChange)
|
||||
self:HandleEvent( EVENTS.MarkAdded )
|
||||
self:HandleEvent( EVENTS.MarkRemoved )
|
||||
self:HandleEvent( EVENTS.MarkChange )
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -317,7 +310,7 @@ end
|
||||
-- @return #MARKER self
|
||||
function MARKER:ReadOnly()
|
||||
|
||||
self.readonly=true
|
||||
self.readonly = true
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -326,9 +319,9 @@ end
|
||||
-- @param #MARKER self
|
||||
-- @param #string Text Message displayed when the marker is added.
|
||||
-- @return #MARKER self
|
||||
function MARKER:Message(Text)
|
||||
function MARKER:Message( Text )
|
||||
|
||||
self.message=Text or ""
|
||||
self.message = Text or ""
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -337,28 +330,28 @@ end
|
||||
-- @param #MARKER self
|
||||
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
|
||||
-- @return #MARKER self
|
||||
function MARKER:ToAll(Delay)
|
||||
function MARKER:ToAll( Delay )
|
||||
|
||||
if Delay and Delay>0 then
|
||||
self:ScheduleOnce(Delay, MARKER.ToAll, self)
|
||||
if Delay and Delay > 0 then
|
||||
self:ScheduleOnce( Delay, MARKER.ToAll, self )
|
||||
else
|
||||
|
||||
self.toall=true
|
||||
self.tocoaliton=nil
|
||||
self.coalition=nil
|
||||
self.togroup=nil
|
||||
self.groupname=nil
|
||||
self.groupid=nil
|
||||
self.toall = true
|
||||
self.tocoalition = nil
|
||||
self.coalition = nil
|
||||
self.togroup = nil
|
||||
self.groupname = nil
|
||||
self.groupid = nil
|
||||
|
||||
-- First remove an existing mark.
|
||||
if self.shown then
|
||||
self:Remove()
|
||||
end
|
||||
|
||||
self.mid=UTILS.GetMarkID()
|
||||
self.mid = UTILS.GetMarkID()
|
||||
|
||||
-- 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
|
||||
|
||||
@@ -367,32 +360,32 @@ end
|
||||
|
||||
--- Place marker visible for a specific coalition only.
|
||||
-- @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.
|
||||
-- @return #MARKER self
|
||||
function MARKER:ToCoalition(Coalition, Delay)
|
||||
function MARKER:ToCoalition( Coalition, Delay )
|
||||
|
||||
if Delay and Delay>0 then
|
||||
self:ScheduleOnce(Delay, MARKER.ToCoalition, self, Coalition)
|
||||
if Delay and Delay > 0 then
|
||||
self:ScheduleOnce( Delay, MARKER.ToCoalition, self, Coalition )
|
||||
else
|
||||
|
||||
self.coalition=Coalition
|
||||
self.coalition = Coalition
|
||||
|
||||
self.tocoaliton=true
|
||||
self.toall=false
|
||||
self.togroup=false
|
||||
self.groupname=nil
|
||||
self.groupid=nil
|
||||
self.tocoalition = true
|
||||
self.toall = false
|
||||
self.togroup = false
|
||||
self.groupname = nil
|
||||
self.groupid = nil
|
||||
|
||||
-- First remove an existing mark.
|
||||
if self.shown then
|
||||
self:Remove()
|
||||
end
|
||||
|
||||
self.mid=UTILS.GetMarkID()
|
||||
self.mid = UTILS.GetMarkID()
|
||||
|
||||
-- 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
|
||||
|
||||
@@ -403,8 +396,8 @@ end
|
||||
-- @param #MARKER self
|
||||
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
|
||||
-- @return #MARKER self
|
||||
function MARKER:ToBlue(Delay)
|
||||
self:ToCoalition(coalition.side.BLUE, Delay)
|
||||
function MARKER:ToBlue( Delay )
|
||||
self:ToCoalition( coalition.side.BLUE, Delay )
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -412,8 +405,8 @@ end
|
||||
-- @param #MARKER self
|
||||
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
|
||||
-- @return #MARKER self
|
||||
function MARKER:ToRed(Delay)
|
||||
self:ToCoalition(coalition.side.RED, Delay)
|
||||
function MARKER:ToRed( Delay )
|
||||
self:ToCoalition( coalition.side.RED, Delay )
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -421,51 +414,50 @@ end
|
||||
-- @param #MARKER self
|
||||
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
|
||||
-- @return #MARKER self
|
||||
function MARKER:ToNeutral(Delay)
|
||||
self:ToCoalition(coalition.side.NEUTRAL, Delay)
|
||||
function MARKER:ToNeutral( Delay )
|
||||
self:ToCoalition( coalition.side.NEUTRAL, Delay )
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Place marker visible for a specific group only.
|
||||
-- @param #MARKER self
|
||||
-- @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.
|
||||
-- @return #MARKER self
|
||||
function MARKER:ToGroup(Group, Delay)
|
||||
function MARKER:ToGroup( Group, Delay )
|
||||
|
||||
if Delay and Delay>0 then
|
||||
self:ScheduleOnce(Delay, MARKER.ToGroup, self, Group)
|
||||
if Delay and Delay > 0 then
|
||||
self:ScheduleOnce( Delay, MARKER.ToGroup, self, Group )
|
||||
else
|
||||
|
||||
-- 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
|
||||
|
||||
self.groupname=Group:GetName()
|
||||
self.groupname = Group:GetName()
|
||||
|
||||
self.togroup=true
|
||||
self.tocoaliton=nil
|
||||
self.coalition=nil
|
||||
self.toall=nil
|
||||
self.togroup = true
|
||||
self.tocoalition = nil
|
||||
self.coalition = nil
|
||||
self.toall = nil
|
||||
|
||||
-- First remove an existing mark.
|
||||
if self.shown then
|
||||
self:Remove()
|
||||
end
|
||||
|
||||
self.mid=UTILS.GetMarkID()
|
||||
self.mid = UTILS.GetMarkID()
|
||||
|
||||
-- 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
|
||||
|
||||
else
|
||||
--TODO: Warning!
|
||||
-- TODO: Warning!
|
||||
end
|
||||
|
||||
end
|
||||
@@ -478,17 +470,17 @@ end
|
||||
-- @param #string Text Updated text.
|
||||
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
|
||||
-- @return #MARKER self
|
||||
function MARKER:UpdateText(Text, Delay)
|
||||
function MARKER:UpdateText( Text, Delay )
|
||||
|
||||
if Delay and Delay>0 then
|
||||
self:ScheduleOnce(Delay, MARKER.UpdateText, self, Text)
|
||||
if Delay and Delay > 0 then
|
||||
self:ScheduleOnce( Delay, MARKER.UpdateText, self, Text )
|
||||
else
|
||||
|
||||
self.text=tostring(Text)
|
||||
self.text = tostring( Text )
|
||||
|
||||
self:Refresh()
|
||||
|
||||
self:TextUpdate(tostring(Text))
|
||||
self:TextUpdate( tostring( Text ) )
|
||||
|
||||
end
|
||||
|
||||
@@ -500,17 +492,17 @@ end
|
||||
-- @param Core.Point#COORDINATE Coordinate The new coordinate.
|
||||
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
|
||||
-- @return #MARKER self
|
||||
function MARKER:UpdateCoordinate(Coordinate, Delay)
|
||||
function MARKER:UpdateCoordinate( Coordinate, Delay )
|
||||
|
||||
if Delay and Delay>0 then
|
||||
self:ScheduleOnce(Delay, MARKER.UpdateCoordinate, self, Coordinate)
|
||||
if Delay and Delay > 0 then
|
||||
self:ScheduleOnce( Delay, MARKER.UpdateCoordinate, self, Coordinate )
|
||||
else
|
||||
|
||||
self.coordinate=Coordinate
|
||||
self.coordinate = Coordinate
|
||||
|
||||
self:Refresh()
|
||||
|
||||
self:CoordUpdate(Coordinate)
|
||||
self:CoordUpdate( Coordinate )
|
||||
|
||||
end
|
||||
|
||||
@@ -521,28 +513,28 @@ end
|
||||
-- @param #MARKER self
|
||||
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
|
||||
-- @return #MARKER self
|
||||
function MARKER:Refresh(Delay)
|
||||
function MARKER:Refresh( Delay )
|
||||
|
||||
if Delay and Delay>0 then
|
||||
self:ScheduleOnce(Delay, MARKER.Refresh, self)
|
||||
if Delay and Delay > 0 then
|
||||
self:ScheduleOnce( Delay, MARKER.Refresh, self )
|
||||
else
|
||||
|
||||
if self.toall then
|
||||
|
||||
self:ToAll()
|
||||
|
||||
elseif self.tocoaliton then
|
||||
elseif self.tocoalition then
|
||||
|
||||
self:ToCoalition(self.coalition)
|
||||
self:ToCoalition( self.coalition )
|
||||
|
||||
elseif self.togroup then
|
||||
|
||||
local group=GROUP:FindByName(self.groupname)
|
||||
local group = GROUP:FindByName( self.groupname )
|
||||
|
||||
self:ToGroup(group)
|
||||
self:ToGroup( group )
|
||||
|
||||
else
|
||||
self:E(self.lid.."ERROR: unknown To in :Refresh()!")
|
||||
self:E( self.lid .. "ERROR: unknown To in :Refresh()!" )
|
||||
end
|
||||
|
||||
end
|
||||
@@ -554,16 +546,16 @@ end
|
||||
-- @param #MARKER self
|
||||
-- @param #number Delay (Optional) Delay in seconds, before the marker is removed.
|
||||
-- @return #MARKER self
|
||||
function MARKER:Remove(Delay)
|
||||
function MARKER:Remove( Delay )
|
||||
|
||||
if Delay and Delay>0 then
|
||||
self:ScheduleOnce(Delay, MARKER.Remove, self)
|
||||
if Delay and Delay > 0 then
|
||||
self:ScheduleOnce( Delay, MARKER.Remove, self )
|
||||
else
|
||||
|
||||
if self.shown then
|
||||
|
||||
-- Call DCS function.
|
||||
trigger.action.removeMark(self.mid)
|
||||
trigger.action.removeMark( self.mid )
|
||||
|
||||
end
|
||||
|
||||
@@ -590,24 +582,23 @@ end
|
||||
-- @param #MARKER self
|
||||
-- @param #string Text Marker text. Default is an empty sting "".
|
||||
-- @return #MARKER self
|
||||
function MARKER:SetText(Text)
|
||||
self.text=Text and tostring(Text) or ""
|
||||
function MARKER:SetText( Text )
|
||||
self.text = Text and tostring( Text ) or ""
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Check if marker is currently visible on the F10 map.
|
||||
-- @param #MARKER self
|
||||
-- @return #boolean True if the marker is currently visible.
|
||||
function MARKER:IsVisible()
|
||||
return self:Is("Visible")
|
||||
return self:Is( "Visible" )
|
||||
end
|
||||
|
||||
--- Check if marker is currently invisible on the F10 map.
|
||||
-- @param #MARKER self
|
||||
-- @return
|
||||
function MARKER:IsInvisible()
|
||||
return self:Is("Invisible")
|
||||
return self:Is( "Invisible" )
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -617,19 +608,19 @@ end
|
||||
--- Event function when a MARKER is added.
|
||||
-- @param #MARKER self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function MARKER:OnEventMarkAdded(EventData)
|
||||
function MARKER:OnEventMarkAdded( EventData )
|
||||
|
||||
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
|
||||
|
||||
@@ -640,19 +631,19 @@ end
|
||||
--- Event function when a MARKER is removed.
|
||||
-- @param #MARKER self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function MARKER:OnEventMarkRemoved(EventData)
|
||||
function MARKER:OnEventMarkRemoved( EventData )
|
||||
|
||||
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=false
|
||||
self.shown = false
|
||||
|
||||
self:Removed(EventData)
|
||||
self:Removed( EventData )
|
||||
|
||||
end
|
||||
|
||||
@@ -663,19 +654,19 @@ end
|
||||
--- Event function when a MARKER changed.
|
||||
-- @param #MARKER self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function MARKER:OnEventMarkChange(EventData)
|
||||
function MARKER:OnEventMarkChange( EventData )
|
||||
|
||||
if EventData and EventData.MarkID 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:Changed( EventData )
|
||||
|
||||
self:TextChanged(tostring(EventData.MarkText))
|
||||
self:TextChanged( tostring( EventData.MarkText ) )
|
||||
|
||||
end
|
||||
|
||||
@@ -693,17 +684,17 @@ end
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Event#EVENTDATA EventData Event data table.
|
||||
function MARKER:onafterAdded(From, Event, To, EventData)
|
||||
function MARKER:onafterAdded( From, Event, To, EventData )
|
||||
|
||||
-- Debug info.
|
||||
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("Coalition = %s\n", tostring(EventData.MarkCoalition))
|
||||
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("Coordinate = %s\n", EventData.MarkCoordinate and EventData.MarkCoordinate:ToStringLLDMS() or "Nowhere")
|
||||
text=text..string.format("Text: \n%s", tostring(EventData.MarkText))
|
||||
self:T2(self.lid..text)
|
||||
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( "Coalition = %s\n", tostring( EventData.MarkCoalition ) )
|
||||
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( "Coordinate = %s\n", EventData.MarkCoordinate and EventData.MarkCoordinate:ToStringLLDMS() or "Nowhere" )
|
||||
text = text .. string.format( "Text: \n%s", tostring( EventData.MarkText ) )
|
||||
self:T2( self.lid .. text )
|
||||
|
||||
end
|
||||
|
||||
@@ -713,17 +704,17 @@ end
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Event#EVENTDATA EventData Event data table.
|
||||
function MARKER:onafterRemoved(From, Event, To, EventData)
|
||||
function MARKER:onafterRemoved( From, Event, To, EventData )
|
||||
|
||||
-- Debug info.
|
||||
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("Coalition = %s\n", tostring(EventData.MarkCoalition))
|
||||
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("Coordinate = %s\n", EventData.MarkCoordinate and EventData.MarkCoordinate:ToStringLLDMS() or "Nowhere")
|
||||
text=text..string.format("Text: \n%s", tostring(EventData.MarkText))
|
||||
self:T2(self.lid..text)
|
||||
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( "Coalition = %s\n", tostring( EventData.MarkCoalition ) )
|
||||
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( "Coordinate = %s\n", EventData.MarkCoordinate and EventData.MarkCoordinate:ToStringLLDMS() or "Nowhere" )
|
||||
text = text .. string.format( "Text: \n%s", tostring( EventData.MarkText ) )
|
||||
self:T2( self.lid .. text )
|
||||
|
||||
end
|
||||
|
||||
@@ -733,17 +724,17 @@ end
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Event#EVENTDATA EventData Event data table.
|
||||
function MARKER:onafterChanged(From, Event, To, EventData)
|
||||
function MARKER:onafterChanged( From, Event, To, EventData )
|
||||
|
||||
-- Debug info.
|
||||
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("Coalition = %s\n", tostring(EventData.MarkCoalition))
|
||||
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("Coordinate = %s\n", EventData.MarkCoordinate and EventData.MarkCoordinate:ToStringLLDMS() or "Nowhere")
|
||||
text=text..string.format("Text: \n%s", tostring(EventData.MarkText))
|
||||
self:T2(self.lid..text)
|
||||
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( "Coalition = %s\n", tostring( EventData.MarkCoalition ) )
|
||||
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( "Coordinate = %s\n", EventData.MarkCoordinate and EventData.MarkCoordinate:ToStringLLDMS() or "Nowhere" )
|
||||
text = text .. string.format( "Text: \n%s", tostring( EventData.MarkText ) )
|
||||
self:T2( self.lid .. text )
|
||||
|
||||
end
|
||||
|
||||
@@ -753,9 +744,9 @@ end
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @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
|
||||
|
||||
@@ -765,12 +756,8 @@ end
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @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
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,52 +1,46 @@
|
||||
--- **Wrapper** -- STATIC wraps the DCS StaticObject class.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
--
|
||||
-- ### Contributions: **funkyfranky**
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- @module Wrapper.Static
|
||||
-- @image Wrapper_Static.JPG
|
||||
|
||||
|
||||
--- @type STATIC
|
||||
-- @extends Wrapper.Positionable#POSITIONABLE
|
||||
|
||||
--- Wrapper class to handle Static objects.
|
||||
--
|
||||
--
|
||||
-- Note that Statics are almost the same as Units, but they don't have a controller.
|
||||
-- The @{Wrapper.Static#STATIC} class is a wrapper class to handle the DCS Static objects:
|
||||
--
|
||||
--
|
||||
-- * Wraps the DCS Static objects.
|
||||
-- * Support all DCS Static APIs.
|
||||
-- * Enhance with Static specific APIs not in the DCS API set.
|
||||
--
|
||||
--
|
||||
-- ## STATIC reference methods
|
||||
--
|
||||
--
|
||||
-- For each DCS Static will have a STATIC wrapper object (instance) within the _@{DATABASE} object.
|
||||
-- This is done at the beginning of the mission (when the mission starts).
|
||||
--
|
||||
--
|
||||
-- The STATIC class does not contain a :New() method, rather it provides :Find() methods to retrieve the object reference
|
||||
-- using the Static Name.
|
||||
--
|
||||
--
|
||||
-- Another thing to know is that STATIC objects do not "contain" the DCS Static object.
|
||||
-- The STATIc methods will reference the DCS Static object by name when it is needed during API execution.
|
||||
-- If the DCS Static object does not exist or is nil, the STATIC methods will return nil and log an exception in the DCS.log file.
|
||||
--
|
||||
--
|
||||
-- The STATIc class provides the following functions to retrieve quickly the relevant STATIC instance:
|
||||
--
|
||||
--
|
||||
-- * @{#STATIC.FindByName}(): Find a STATIC instance from the _DATABASE object using a DCS Static name.
|
||||
--
|
||||
-- IMPORTANT: ONE SHOULD NEVER SANATIZE these STATIC OBJECT REFERENCES! (make the STATIC object references nil).
|
||||
--
|
||||
--
|
||||
-- IMPORTANT: ONE SHOULD NEVER SANITIZE these STATIC OBJECT REFERENCES! (make the STATIC object references nil).
|
||||
--
|
||||
-- @field #STATIC
|
||||
STATIC = {
|
||||
ClassName = "STATIC",
|
||||
}
|
||||
|
||||
STATIC = { ClassName = "STATIC" }
|
||||
|
||||
--- Register a static object.
|
||||
-- @param #STATIC self
|
||||
@@ -58,7 +52,6 @@ function STATIC:Register( StaticName )
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Finds a STATIC from the _DATABASE using a DCSStatic object.
|
||||
-- @param #STATIC self
|
||||
-- @param DCS#StaticObject DCSStatic An existing DCS Static object reference.
|
||||
@@ -83,13 +76,13 @@ function STATIC:FindByName( StaticName, RaiseError )
|
||||
|
||||
-- Set static name.
|
||||
self.StaticName = StaticName
|
||||
|
||||
|
||||
if StaticFound then
|
||||
return StaticFound
|
||||
return StaticFound
|
||||
end
|
||||
|
||||
if RaiseError == nil or RaiseError == true then
|
||||
error( "STATIC not found for: " .. StaticName )
|
||||
|
||||
if RaiseError == nil or RaiseError == true then
|
||||
error( "STATIC not found for: " .. StaticName )
|
||||
end
|
||||
|
||||
return nil
|
||||
@@ -97,38 +90,39 @@ end
|
||||
|
||||
--- Destroys the STATIC.
|
||||
-- @param #STATIC self
|
||||
-- @param #boolean GenerateEvent (Optional) true if you want to generate a crash or dead event for the static.
|
||||
-- @return #nil The DCS StaticObject is not existing or alive.
|
||||
-- @param #boolean GenerateEvent (Optional) true to generate a crash or dead event, false to not generate any event. `nil` (default) creates a remove event.
|
||||
-- @return #nil The DCS StaticObject is not existing or alive.
|
||||
--
|
||||
-- @usage
|
||||
-- -- Air static example: destroy the static Helicopter and generate a S_EVENT_CRASH.
|
||||
-- Helicopter = STATIC:FindByName( "Helicopter" )
|
||||
-- Helicopter:Destroy( true )
|
||||
--
|
||||
--
|
||||
-- @usage
|
||||
-- -- Ground static example: destroy the static Tank and generate a S_EVENT_DEAD.
|
||||
-- Tanks = UNIT:FindByName( "Tank" )
|
||||
-- Tanks:Destroy( true )
|
||||
--
|
||||
--
|
||||
-- @usage
|
||||
-- -- Ship static example: destroy the Ship silently.
|
||||
-- Ship = STATIC:FindByName( "Ship" )
|
||||
-- Ship:Destroy()
|
||||
--
|
||||
--
|
||||
-- @usage
|
||||
-- -- Destroy without event generation example.
|
||||
-- Ship = STATIC:FindByName( "Boat" )
|
||||
-- Ship:Destroy( false ) -- Don't generate an event upon destruction.
|
||||
--
|
||||
-- Ship:Destroy( false ) -- Don't generate any event upon destruction.
|
||||
--
|
||||
function STATIC:Destroy( GenerateEvent )
|
||||
self:F2( self.ObjectName )
|
||||
|
||||
local DCSObject = self:GetDCSObject()
|
||||
|
||||
|
||||
if DCSObject then
|
||||
|
||||
|
||||
local StaticName = DCSObject:getName()
|
||||
self:F( { StaticName = StaticName } )
|
||||
|
||||
|
||||
if GenerateEvent and GenerateEvent == true then
|
||||
if self:IsAir() then
|
||||
self:CreateEventCrash( timer.getTime(), DCSObject )
|
||||
@@ -140,7 +134,7 @@ function STATIC:Destroy( GenerateEvent )
|
||||
else
|
||||
self:CreateEventRemoveUnit( timer.getTime(), DCSObject )
|
||||
end
|
||||
|
||||
|
||||
DCSObject:destroy()
|
||||
return true
|
||||
end
|
||||
@@ -148,17 +142,16 @@ function STATIC:Destroy( GenerateEvent )
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Get DCS object of static of static.
|
||||
-- @param #STATIC self
|
||||
-- @return DCS static object
|
||||
function STATIC:GetDCSObject()
|
||||
local DCSStatic = StaticObject.getByName( self.StaticName )
|
||||
|
||||
|
||||
if DCSStatic then
|
||||
return DCSStatic
|
||||
end
|
||||
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
@@ -170,7 +163,7 @@ function STATIC:GetUnits()
|
||||
local DCSStatic = self:GetDCSObject()
|
||||
|
||||
local Statics = {}
|
||||
|
||||
|
||||
if DCSStatic then
|
||||
Statics[1] = STATIC:Find( DCSStatic )
|
||||
self:T3( Statics )
|
||||
@@ -180,7 +173,6 @@ function STATIC:GetUnits()
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Get threat level of static.
|
||||
-- @param #STATIC self
|
||||
-- @return #number Threat level 1.
|
||||
@@ -194,66 +186,62 @@ end
|
||||
-- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static.
|
||||
-- @param #number Heading The heading of the static respawn in degrees. Default is 0 deg.
|
||||
-- @param #number Delay Delay in seconds before the static is spawned.
|
||||
function STATIC:SpawnAt(Coordinate, Heading, Delay)
|
||||
function STATIC:SpawnAt( Coordinate, Heading, Delay )
|
||||
|
||||
Heading=Heading or 0
|
||||
Heading = Heading or 0
|
||||
|
||||
if Delay and Delay>0 then
|
||||
SCHEDULER:New(nil, self.SpawnAt, {self, Coordinate, Heading}, Delay)
|
||||
if Delay and Delay > 0 then
|
||||
SCHEDULER:New( nil, self.SpawnAt, { self, Coordinate, Heading }, Delay )
|
||||
else
|
||||
|
||||
local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName)
|
||||
|
||||
local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName )
|
||||
|
||||
SpawnStatic:SpawnFromPointVec2( Coordinate, Heading, self.StaticName )
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Respawn the @{Wrapper.Unit} at the same location with the same properties.
|
||||
-- This is useful to respawn a cargo after it has been destroyed.
|
||||
-- @param #STATIC self
|
||||
-- @param DCS#country.id CountryID (Optional) The country ID used for spawning the new static. Default is same as currently.
|
||||
-- @param #number Delay (Optional) Delay in seconds before static is respawned. Default now.
|
||||
function STATIC:ReSpawn(CountryID, Delay)
|
||||
function STATIC:ReSpawn( CountryID, Delay )
|
||||
|
||||
if Delay and Delay>0 then
|
||||
SCHEDULER:New(nil, self.ReSpawn, {self, CountryID}, Delay)
|
||||
if Delay and Delay > 0 then
|
||||
SCHEDULER:New( nil, self.ReSpawn, { self, CountryID }, Delay )
|
||||
else
|
||||
|
||||
CountryID=CountryID or self:GetCountry()
|
||||
CountryID = CountryID or self:GetCountry()
|
||||
|
||||
local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName, CountryID )
|
||||
|
||||
SpawnStatic:Spawn( nil, self.StaticName )
|
||||
|
||||
local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName, CountryID)
|
||||
|
||||
SpawnStatic:Spawn(nil, self.StaticName)
|
||||
|
||||
end
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Respawn the @{Wrapper.Unit} at a defined Coordinate with an optional heading.
|
||||
-- @param #STATIC self
|
||||
-- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static.
|
||||
-- @param #number Heading (Optional) The heading of the static respawn in degrees. Default the current heading.
|
||||
-- @param #number Delay (Optional) Delay in seconds before static is respawned. Default now.
|
||||
function STATIC:ReSpawnAt(Coordinate, Heading, Delay)
|
||||
-- @param #number Heading (Optional) The heading of the static respawn in degrees. Default is the current heading.
|
||||
-- @param #number Delay (Optional) Delay in seconds before static is respawned. Default is now.
|
||||
function STATIC:ReSpawnAt( Coordinate, Heading, Delay )
|
||||
|
||||
--Heading=Heading or 0
|
||||
-- Heading=Heading or 0
|
||||
|
||||
if Delay and Delay>0 then
|
||||
SCHEDULER:New(nil, self.ReSpawnAt, {self, Coordinate, Heading}, Delay)
|
||||
if Delay and Delay > 0 then
|
||||
SCHEDULER:New( nil, self.ReSpawnAt, { self, Coordinate, Heading }, Delay )
|
||||
else
|
||||
|
||||
local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName, self:GetCountry())
|
||||
|
||||
SpawnStatic:SpawnFromCoordinate(Coordinate, Heading, self.StaticName)
|
||||
|
||||
local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName, self:GetCountry() )
|
||||
|
||||
SpawnStatic:SpawnFromCoordinate( Coordinate, Heading, self.StaticName )
|
||||
end
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
@@ -168,9 +168,6 @@ function UNIT:GetDCSObject()
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
--- Respawn the @{Wrapper.Unit} using a (tweaked) template of the parent Group.
|
||||
--
|
||||
-- This function will:
|
||||
@@ -263,8 +260,6 @@ function UNIT:ReSpawnAt( Coordinate, Heading )
|
||||
_DATABASE:Spawn( SpawnGroupTemplate )
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Returns if the unit is activated.
|
||||
-- @param #UNIT self
|
||||
-- @return #boolean `true` if Unit is activated. `nil` The DCS Unit is not existing or alive.
|
||||
@@ -301,8 +296,6 @@ function UNIT:IsAlive()
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Returns the Unit's callsign - the localized string.
|
||||
-- @param #UNIT self
|
||||
-- @return #string The Callsign of the Unit.
|
||||
@@ -442,7 +435,7 @@ function UNIT:GetSpeedMax()
|
||||
return SpeedMax*3.6
|
||||
end
|
||||
|
||||
return nil
|
||||
return 0
|
||||
end
|
||||
|
||||
--- Returns the unit's max range in meters derived from the DCS descriptors.
|
||||
@@ -626,8 +619,8 @@ function UNIT:GetAmmunition()
|
||||
-- Type name of current weapon.
|
||||
local Tammo=ammotable[w]["desc"]["typeName"]
|
||||
|
||||
local _weaponString = UTILS.Split(Tammo,"%.")
|
||||
local _weaponName = _weaponString[#_weaponString]
|
||||
--local _weaponString = UTILS.Split(Tammo,"%.")
|
||||
--local _weaponName = _weaponString[#_weaponString]
|
||||
|
||||
-- Get the weapon category: shell=0, missile=1, rocket=2, bomb=3
|
||||
local Category=ammotable[w].desc.category
|
||||
@@ -655,8 +648,9 @@ function UNIT:GetAmmunition()
|
||||
nbombs=nbombs+Nammo
|
||||
|
||||
elseif Category==Weapon.Category.MISSILE then
|
||||
|
||||
-- Add up all cruise missiles (category 5)
|
||||
|
||||
|
||||
-- Add up all missiles (category 5)
|
||||
if MissileCategory==Weapon.MissileCategory.AAM then
|
||||
nmissiles=nmissiles+Nammo
|
||||
elseif MissileCategory==Weapon.MissileCategory.ANTI_SHIP then
|
||||
@@ -665,6 +659,10 @@ function UNIT:GetAmmunition()
|
||||
nmissiles=nmissiles+Nammo
|
||||
elseif MissileCategory==Weapon.MissileCategory.OTHER then
|
||||
nmissiles=nmissiles+Nammo
|
||||
elseif MissileCategory==Weapon.MissileCategory.SAM then
|
||||
nmissiles=nmissiles+Nammo
|
||||
elseif MissileCategory==Weapon.MissileCategory.CRUISE then
|
||||
nmissiles=nmissiles+Nammo
|
||||
end
|
||||
|
||||
end
|
||||
@@ -956,7 +954,6 @@ end
|
||||
-- @return #string Some text.
|
||||
function UNIT:GetThreatLevel()
|
||||
|
||||
|
||||
local ThreatLevel = 0
|
||||
local ThreatText = ""
|
||||
|
||||
@@ -982,7 +979,6 @@ function UNIT:GetThreatLevel()
|
||||
"LR SAMs"
|
||||
}
|
||||
|
||||
|
||||
if Attributes["LR SAM"] then ThreatLevel = 10
|
||||
elseif Attributes["MR SAM"] then ThreatLevel = 9
|
||||
elseif Attributes["SR SAM"] and
|
||||
@@ -1018,7 +1014,6 @@ function UNIT:GetThreatLevel()
|
||||
"Fighter"
|
||||
}
|
||||
|
||||
|
||||
if Attributes["Fighters"] then ThreatLevel = 10
|
||||
elseif Attributes["Multirole fighters"] then ThreatLevel = 9
|
||||
elseif Attributes["Battleplanes"] then ThreatLevel = 8
|
||||
@@ -1136,12 +1131,6 @@ function UNIT:OtherUnitInRadius( AwaitUnit, Radius )
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
--- Returns if the unit is a friendly unit.
|
||||
-- @param #UNIT self
|
||||
-- @return #boolean IsFriendly evaluation result.
|
||||
@@ -1426,3 +1415,13 @@ function UNIT:EnableEmission(switch)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get skill from Unit.
|
||||
-- @param #UNIT self
|
||||
-- @return #string Skill String of skill name.
|
||||
function UNIT:GetSkill()
|
||||
self:F2( self.UnitName )
|
||||
local name = self.UnitName
|
||||
local skill = _DATABASE.Templates.Units[name].Template.skill or "Random"
|
||||
return skill
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user