Compare commits

..

60 Commits
2.7.7 ... 2.7.8

Author SHA1 Message Date
TommyC81
4a406604bd Core modules formatting (#1670)
* Update Fsm.lua

Code formatting and minor typo/documentation fixes.

* Update Goal.lua

Code formatting and minor typo/documentation fixes.

* Update Menu.lua

Code formatting and minor typo/documentation fixes.

* Update Message.lua

Code formatting and minor typo/documentation fixes.

* Update Report.lua

Code formatting and minor typo/documentation fixes.

* Update ScheduleDispatcher.lua

Code formatting and minor typo/documentation fixes.

* Update Scheduler.lua

Code formatting and minor typo/documentation fixes.

* Update Settings.lua

Code formatting and minor typo/documentation fixes.

* Update Spawn.lua

Code formatting and minor typo/documentation fixes.
2021-12-20 12:59:56 +01:00
TommyC81
55cee46a8d Core code formatting and typo fixes. (#1669)
* Update Beacon.lua

Code formatting and minor typo/text fixes.

* Update Database.lua

Code formatting and minor typo/text fixes.

* Update Event.lua

Code formatting and minor typo/text fixes.
2021-12-19 08:47:07 +01:00
TommyC81
607c52c0b7 Update CSAR.lua (#1665)
Code formatting and general typo/documentation fixes.
2021-12-17 09:07:32 +01:00
TommyC81
2694321256 Update CTLD.lua (#1666)
Code formatting. Minor typos and text fixes.
2021-12-17 09:07:24 +01:00
TommyC81
e8e790102a Update Base.lua (#1667)
Code Formatting. General whitespace and spelling.
2021-12-17 09:07:13 +01:00
Applevangelist
9c5561921b Noise reducing measures 2021-12-15 13:46:07 +01:00
Applevangelist
78fab9ab0c CSAR - make beacon length configureable 2021-12-14 09:50:34 +01:00
TommyC81
058c750bc6 Update Set.lua (#1663)
Additional minor code formatting and typo fixes.
2021-12-13 16:52:01 +01:00
Applevangelist
f29da39dff CSAR - override suppressmessages for menu driven information 2021-12-12 19:48:36 +01:00
Applevangelist
624a7c70c9 CTLD - corrected landheight of dropped smoke 2021-12-12 19:30:33 +01:00
TommyC81
0447ee2d9e Update Airboss.lua (#1664)
Code formatting. Typo fixing.
2021-12-12 19:15:30 +01:00
TommyC81
456fcd38d0 Code and documentation tweaks. (#1662)
* Update Point.lua

General code formatting.

* Update Set.lua

General code formatting.

* Update Positionable.lua

Code formatting, and documentation fixes.
2021-12-12 13:53:04 +01:00
Applevangelist
a3cab7097a SET - Typos 2021-12-11 19:41:02 +01:00
Applevangelist
848e2f1294 SET - Added Zone Filter for STATIC 2021-12-11 14:24:20 +01:00
Applevangelist
ef4dc48ea1 CTLD - added option for smoke/flare at position 2021-12-11 14:14:44 +01:00
Applevangelist
2138a33292 CSAR fixed KM message 2021-12-10 12:02:50 +01:00
TommyC81
a59343b987 Code formatting, spelling and documentation fixes. (#1661)
* Update Point.lua

Minor code formatting fix.

* Update Airboss.lua

Minor code formatting and documentation fixes.

* Update Set.lua

Code formatting, spelling and documentation fixes.

* Update ATIS.lua

Code formatting, spelling and documentation fixes.

* Update Task_A2A_Dispatcher.lua

Minor code formatting and documentation fixes. Added TODO re. possible unused variables.
2021-12-09 17:51:38 +01:00
TommyC81
a4ca4bdc99 Code and documentation fixes (#1659)
* Update .lua-format

Adjust for observed coding standards.

* Update ATIS.lua

Correct measurement units and spelling (also changed in Utils.lua).

* Update Utils.lua

Format the file, fix typos, adjust minor text. Rename "celcius" to "celsius". Rename "farenheit" to "fahrenheit".

* Update Warehouse.lua

Adjust measurement unit text.

* Update STTS.lua

Adjust formatting, minor typos, and fix error in documentation (missing blank rows) introduced in previous update.

* Update Range.lua

Adjust minor typos and code formatting. Adjust for celsius/fahrenheit typo correction.

* Update PseudoATC.lua

Adjust for celsius/fahrenheit typo correction in utils.lua.

* Update Point.lua

Code formatting, fix minor typos, adjust for celsius/fahrenheit corrrection in utils.lua.

* Update Range.lua

Minor documentation fix.
2021-12-08 19:52:29 +01:00
TommyC81
a57b9a9081 Update Range.lua (#1658)
Adjust some minor spelling and figure out the quirks of luadocumentor...
2021-12-07 18:13:51 +01:00
Applevangelist
249a6af456 Some false values seem to be in need of being set explicitly 2021-12-06 15:16:01 +01:00
Applevangelist
18685d1a94 Update Utils.lua (#1655)
Small bugfix for UTILS.LoadSetOfGroups
2021-12-06 14:57:49 +01:00
TommyC81
493b090534 LuaFormatter, RANGE formatting, and minor code fixes. (#1653)
* Create .lua-format

* Update Range.lua

Format code.

* Display distance in meters from bombtarget.

All other numbers, including menu lists etc. uses meters. Feet kept in parens.

* Fixed displaying of targetname when bombing.
2021-12-06 14:57:36 +01:00
Applevangelist
389adab9b8 SET - slight change to remove function 2021-12-06 10:26:32 +01:00
TommyC81
6360b8c58f General documentation and code fixes (#1650)
Documentation updates for correctness and clarity.
General code formatting updates.
Ajustment to POSITIONABLE:GetCoord to make use of existing POINT:UpdateFromVec3.
Added comments about clarifying the difference between POSITIONABLE:GetCoordinate() and POSITIONABLE:GetCoord() and to perhaps consider a renaming or merging the functions with an optional flag.
2021-12-04 18:50:05 +01:00
TommyC81
b0818977cf Code formatting. (#1651)
General code formatting and fixes of minor typos.
2021-12-04 18:49:57 +01:00
TommyC81
32deb160ef Formatting and typos (#1652)
* Formatting and typo fixes.

General formatting and typo fixes.

* Update STTS.lua

Keep class table on separate lines.
2021-12-04 18:49:47 +01:00
Applevangelist
2ba5215036 Merge pull request #1649 from TommyC81/RANGE_FIXES
Update Range.lua
2021-12-02 19:25:42 +01:00
Tommy Carlsson
73ea4c7b32 Update Range.lua
Fix documentation - including typos and updates to no longer correct text.
Remove duplicated RANGE.Defaults.goodthitrange value.
DCS seems to use headings 0-359 (i.e. <360), thus also deduct 360 from an exact 360 heading.
2021-12-02 20:49:48 +04:00
Applevangelist
46c37ff06a Merge pull request #1648 from TommyC81/A2G_DISPATCHER_FIXES
Update AI_A2G_Dispatcher.lua
2021-12-01 11:54:56 +01:00
Tommy Carlsson
ff4708b624 Update AI_A2G_Dispatcher.lua
General code formatting fixes, and correction of typos/examples.
2021-12-01 13:41:27 +04:00
Applevangelist
82b2c84f13 Merge pull request #1647 from TommyC81/A2G_DISPATCHER_DOCUMENTATION_2
Update AI_A2G_Dispatcher.lua
2021-12-01 09:17:46 +01:00
Tommy Carlsson
48e8b1a9b3 Update AI_A2G_Dispatcher.lua
More updates and fixes.

Update AI_A2G_Dispatcher.lua

General documentation updates.
2021-12-01 11:34:18 +04:00
Applevangelist
1ad7c54ace Merge pull request #1645 from TommyC81/A2G_Dispatcher_documentation
Update AI_A2G_Dispatcher.lua
2021-11-30 17:02:40 +01:00
Tommy Carlsson
9998c86c1f Update AI_A2G_Dispatcher.lua
Fix typos and incorrect references (leftovers) to A2A/CAP etc.
2021-11-30 19:37:26 +04:00
Applevangelist
01a707ae0a Small changes in GROUP 2021-11-29 07:57:07 +01:00
Applevangelist
570e8388fc Bug fixes 2021-11-27 17:30:25 +01:00
Applevangelist
9c5b5d4633 CSAR - added changes by Shagrat for Casevac
CSAR - don't make usePara default. Added coalition check if using the parachute landing event
2021-11-14 13:34:23 +01:00
Applevangelist
822bf13626 Merge pull request #1625 from FlightControl-Master/Applevangelist-unit-1
Wrapper Unit - fix for missile count
2021-11-14 12:59:51 +01:00
Applevangelist
c520de0087 Wrapper Unit - fix for missile count
Wrapper Unit - fix for missile count, issue #1624
2021-11-14 12:57:00 +01:00
Applevangelist
0e9076efa3 SEAD - align to dev changes, allow callback on SEAD events 2021-11-11 16:02:13 +01:00
Applevangelist
74cd5e3387 MANTIS - added docu and addition os SEAD events 2021-11-11 16:02:13 +01:00
Applevangelist
65c92be09e Merge pull request #1621 from FlightControl-Master/Applevangelist-casevac
Update CSAR.lua
2021-11-06 15:30:15 +01:00
Applevangelist
8e776cb3ab Update CSAR.lua
Adding CASEVAC option by Shagrat
2021-11-06 15:29:09 +01:00
Applevangelist
ab6cd2b751 Fix cleanup exisiting crates 2021-10-31 11:51:31 +01:00
Applevangelist
18c3d990fc Merge pull request #1618 from rollnthndr/master
Enabled SSML when using Google text-to-speech.
2021-10-31 08:15:25 +01:00
Rolln
19d5cb8ecb Added a command line option that will enable
SSML support when using Google text-to-speech.
2021-10-30 23:35:27 -06:00
Applevangelist
2b56a78255 Merge pull request #1617 from Penecruz/master
Airboss V/STOL updates
2021-10-30 22:05:21 +02:00
Penecruz
176d9df476 Merge branch 'FlightControl-Master:master' into master 2021-10-31 06:59:50 +11:00
Penecruz
c94275cb8b Airboss V/STOL updates
-Add additional Airboss V/STOL carrier HMAS Canberra L02
-Add Waveoff for AV-8B
-Add Cut Pass if Land without LSO clearance
-Changes to V/STOL groove timings
-Stabilise call when over the V/STOL landing spot
-larger abeam landing spot margin to allow decelerating to stable abeam and still be cleared to land
-Abeam area now extends further aft to allow LSO clearance 45-90 as per NATOPS
-Minor document changes
2021-10-31 06:41:58 +11:00
Applevangelist
45dbce3677 Speedmax returning 0 not nil 2021-10-30 16:33:18 +02:00
Applevangelist
18745158a3 Speedmax returning 0 not nil 2021-10-30 16:33:14 +02:00
Applevangelist
98c6c88391 Completed GetSetComplement 2021-10-29 18:32:13 +02:00
Frank
2e4fd72781 Update Fox.lua
Removed incomplete line `--@field #boolean`
2021-10-28 10:18:43 +02:00
Applevangelist
80ced88ef1 Fix for docs build 2021-10-28 08:31:14 +02:00
Applevangelist
8af3f89c14 Adjustments for Forrestal by Pene 2021-10-24 14:35:55 +02:00
Applevangelist
fe3079caad Added Bell-47 2021-10-22 17:04:23 +02:00
Applevangelist
61ac6b4131 Added Bell-47 2021-10-22 17:04:19 +02:00
Frank
36cb189512 Merge pull request #1612 from FlightControl-Master/FF/MasterDevel
AIRBOSS v1.2.0
2021-10-20 19:55:19 +02:00
Frank
15f9843878 AIRBOSS v1.2.0
- Added Forrestal carrier CV-59
2021-10-16 12:11:34 +02:00
Frank
67f847dd16 Update Group.lua
- Fixed SetInvisible and SetImmortal functions to acknowledge parameter false.
2021-10-12 22:16:18 +02:00
43 changed files with 23064 additions and 22654 deletions

View 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

File diff suppressed because it is too large Load Diff

View File

@@ -585,7 +585,7 @@ function AI_AIR:onafterRTB( AIGroup, From, Event, To )
if AIGroup and AIGroup:IsAlive() then if AIGroup and AIGroup:IsAlive() then
self:I( "Group " .. AIGroup:GetName() .. " ... RTB! ( " .. self:GetState() .. " )" ) self:T( "Group " .. AIGroup:GetName() .. " ... RTB! ( " .. self:GetState() .. " )" )
self:ClearTargetDistance() self:ClearTargetDistance()
--AIGroup:ClearTasks() --AIGroup:ClearTasks()

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,9 @@
--- **Core** - TACAN and other beacons. --- **Core** - TACAN and other beacons.
-- --
-- === -- ===
-- --
-- ## Features: -- ## Features:
-- --
-- * Provide beacon functionality to assist pilots. -- * Provide beacon functionality to assist pilots.
-- --
-- === -- ===
@@ -14,34 +14,34 @@
-- @image Core_Radio.JPG -- @image Core_Radio.JPG
--- *In order for the light to shine so brightly, the darkness must be present.* -- Francis Bacon --- *In order for the light to shine so brightly, the darkness must be present.* -- Francis Bacon
-- --
-- After attaching a @{#BEACON} to your @{Wrapper.Positionable#POSITIONABLE}, you need to select the right function to activate the kind of beacon you want. -- After attaching a @{#BEACON} to your @{Wrapper.Positionable#POSITIONABLE}, you need to select the right function to activate the kind of beacon you want.
-- There are two types of BEACONs available : the AA TACAN Beacon and the general purpose Radio Beacon. -- There are two types of BEACONs available : the 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 -- 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 -- ## 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. -- 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. -- Use @#BEACON:StopAATACAN}() to stop it.
-- --
-- ## General Purpose Radio Beacon usage -- ## General Purpose Radio Beacon usage
-- --
-- This beacon will work with any @{Wrapper.Positionable#POSITIONABLE}, but **it won't follow the @{Wrapper.Positionable#POSITIONABLE}** ! This means that you should only use it with -- This beacon will work with any @{Wrapper.Positionable#POSITIONABLE}, but **it won't follow the @{Wrapper.Positionable#POSITIONABLE}** ! This means that you should only use it with
-- @{Wrapper.Positionable#POSITIONABLE} that don't move, or move very slowly. Use @{#BEACON:RadioBeacon}() to set the beacon parameters and start the beacon. -- @{Wrapper.Positionable#POSITIONABLE} that don't move, or move very slowly. Use @{#BEACON:RadioBeacon}() to set the beacon parameters and start the beacon.
-- Use @{#BEACON:StopRadioBeacon}() to stop it. -- Use @{#BEACON:StopRadioBeacon}() to stop it.
-- --
-- @type BEACON -- @type BEACON
-- @field #string ClassName Name of the class "BEACON". -- @field #string ClassName Name of the class "BEACON".
-- @field Wrapper.Controllable#CONTROLLABLE Positionable The @{#CONTROLLABLE} that will receive radio capabilities. -- @field Wrapper.Controllable#CONTROLLABLE Positionable The @{#CONTROLLABLE} that will receive radio capabilities.
-- @extends Core.Base#BASE -- @extends Core.Base#BASE
BEACON = { BEACON = {
ClassName = "BEACON", ClassName = "BEACON",
Positionable = nil, Positionable = nil,
name = nil, name = nil,
} }
--- Beacon types supported by DCS. --- Beacon types supported by DCS.
-- @type BEACON.Type -- @type BEACON.Type
-- @field #number NULL -- @field #number NULL
-- @field #number VOR -- @field #number VOR
@@ -65,19 +65,19 @@ BEACON = {
-- @field #number ICLS_GLIDESLOPE -- @field #number ICLS_GLIDESLOPE
-- @field #number NAUTICAL_HOMER -- @field #number NAUTICAL_HOMER
BEACON.Type={ BEACON.Type={
NULL = 0, NULL = 0,
VOR = 1, VOR = 1,
DME = 2, DME = 2,
VOR_DME = 3, VOR_DME = 3,
TACAN = 4, TACAN = 4,
VORTAC = 5, VORTAC = 5,
RSBN = 128, RSBN = 128,
BROADCAST_STATION = 1024, BROADCAST_STATION = 1024,
HOMER = 8, HOMER = 8,
AIRPORT_HOMER = 4104, AIRPORT_HOMER = 4104,
AIRPORT_HOMER_WITH_MARKER = 4136, AIRPORT_HOMER_WITH_MARKER = 4136,
ILS_FAR_HOMER = 16408, ILS_FAR_HOMER = 16408,
ILS_NEAR_HOMER = 16424, ILS_NEAR_HOMER = 16424,
ILS_LOCALIZER = 16640, ILS_LOCALIZER = 16640,
ILS_GLIDESLOPE = 16896, ILS_GLIDESLOPE = 16896,
PRMG_LOCALIZER = 33024, PRMG_LOCALIZER = 33024,
@@ -95,11 +95,11 @@ BEACON.Type={
-- @field #number TACAN TACtical Air Navigation system on ground. -- @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_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 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_LOCALIZER ILS localizer
-- @field #number ILS_GLIDESLOPE ILS glideslope. -- @field #number ILS_GLIDESLOPE ILS glide slope.
-- @field #number PRGM_LOCALIZER PRGM localizer. -- @field #number PRGM_LOCALIZER PRGM localizer.
-- @field #number PRGM_GLIDESLOPE PRGM glideslope. -- @field #number PRGM_GLIDESLOPE PRGM glide slope.
-- @field #number BROADCAST_STATION Broadcast station. -- @field #number BROADCAST_STATION Broadcast station.
-- @field #number VORTAC Radio-based navigational aid for aircraft pilots consisting of a co-located VHF omnidirectional range (VOR) beacon and a tactical air navigation system (TACAN) beacon. -- @field #number VORTAC Radio-based navigational aid for aircraft pilots consisting of a co-located VHF omnidirectional range (VOR) beacon and a tactical air navigation system (TACAN) beacon.
-- @field #number TACAN_AA_MODE_X TACtical Air Navigation for aircraft on X band. -- @field #number TACAN_AA_MODE_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_LOCALIZER Carrier landing system.
-- @field #number ICLS_GLIDESLOPE Carrier landing system. -- @field #number ICLS_GLIDESLOPE Carrier landing system.
BEACON.System={ BEACON.System={
PAR_10 = 1, PAR_10 = 1,
RSBN_5 = 2, RSBN_5 = 2,
TACAN = 3, TACAN = 3,
TACAN_TANKER_X = 4, TACAN_TANKER_X = 4,
TACAN_TANKER_Y = 5, TACAN_TANKER_Y = 5,
VOR = 6, VOR = 6,
ILS_LOCALIZER = 7, ILS_LOCALIZER = 7,
ILS_GLIDESLOPE = 8, ILS_GLIDESLOPE = 8,
PRMG_LOCALIZER = 9, PRMG_LOCALIZER = 9,
PRMG_GLIDESLOPE = 10, 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. -- If you want to create a BEACON, you probably should use @{Wrapper.Positionable#POSITIONABLE.GetBeacon}() instead.
-- @param #BEACON self -- @param #BEACON self
-- @param Wrapper.Positionable#POSITIONABLE Positionable The @{Positionable} that will receive radio capabilities. -- @param Wrapper.Positionable#POSITIONABLE Positionable The @{Positionable} that will receive radio capabilities.
-- @return #BEACON Beacon object or #nil if the positionable is invalid. -- @return #BEACON Beacon object or #nil if the POSITIONABLE is invalid.
function BEACON:New(Positionable) function BEACON:New( Positionable )
-- Inherit BASE. -- Inherit BASE.
local self=BASE:Inherit(self, BASE:New()) --#BEACON local self = BASE:Inherit( self, BASE:New() ) -- #BEACON
-- Debug. -- Debug.
self:F(Positionable) self:F( Positionable )
-- Set 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.Positionable = Positionable
self.name=Positionable:GetName() self.name = Positionable:GetName()
self:I(string.format("New BEACON %s", tostring(self.name))) self:I( string.format( "New BEACON %s", tostring( self.name ) ) )
return self return self
end 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 return nil
end end
--- Activates a TACAN BEACON. --- Activates a TACAN BEACON.
-- @param #BEACON self -- @param #BEACON self
-- @param #number Channel TACAN channel, i.e. the "10" part in "10Y". -- @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. -- @param #number Duration How long will the beacon last in seconds. Omit for forever.
-- @return #BEACON self -- @return #BEACON self
-- @usage -- @usage
--
-- -- Let's create a TACAN Beacon for a tanker -- -- 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 -- local myBeacon = myUnit:GetBeacon() -- Creates the beacon
-- --
-- myBeacon:ActivateTACAN(20, "Y", "TEXACO", true) -- Activate 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. -- Get frequency.
local Frequency=UTILS.TACANToFrequency(Channel, Mode) local Frequency = UTILS.TACANToFrequency( Channel, Mode )
-- Check. -- Check.
if not Frequency then if not Frequency then
self:E({"The passed TACAN channel is invalid, the BEACON is not emitting"}) self:E( { "The passed TACAN channel is invalid, the BEACON is not emitting" } )
return self return self
end end
-- Beacon type. -- Beacon type.
local Type=BEACON.Type.TACAN local Type = BEACON.Type.TACAN
-- Beacon system. -- Beacon system.
local System=BEACON.System.TACAN local System = BEACON.System.TACAN
-- Check if unit is an aircraft and set system accordingly. -- Check if unit is an aircraft and set system accordingly.
local AA=self.Positionable:IsAir() local AA = self.Positionable:IsAir()
if AA then if AA then
System=5 --NOTE: 5 is how you cat the correct tanker behaviour! --BEACON.System.TACAN_TANKER System = 5 -- NOTE: 5 is how you cat the correct tanker behaviour! --BEACON.System.TACAN_TANKER
-- Check if "Y" mode is selected for aircraft. -- Check if "Y" mode is selected for aircraft.
if Mode~="Y" then if Mode ~= "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}) 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
end end
-- Attached unit. -- Attached unit.
local UnitID=self.Positionable:GetID() local UnitID = self.Positionable:GetID()
-- Debug. -- 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. -- Start beacon.
self.Positionable:CommandActivateBeacon(Type, System, Frequency, UnitID, Channel, Mode, AA, Message, Bearing) self.Positionable:CommandActivateBeacon( Type, System, Frequency, UnitID, Channel, Mode, AA, Message, Bearing )
-- Stop sheduler. -- Stop scheduler.
if Duration then if Duration then
self.Positionable:DeactivateBeacon(Duration) self.Positionable:DeactivateBeacon( Duration )
end end
return self return self
end 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 #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. -- @param #number Duration How long will the beacon last in seconds. Omit for forever.
-- @return #BEACON self -- @return #BEACON self
function BEACON:ActivateICLS(Channel, Callsign, Duration) function BEACON:ActivateICLS( Channel, Callsign, Duration )
self:F({Channel=Channel, Callsign=Callsign, Duration=Duration}) self:F( { Channel = Channel, Callsign = Callsign, Duration = Duration } )
-- Attached unit. -- Attached unit.
local UnitID=self.Positionable:GetID() local UnitID = self.Positionable:GetID()
-- Debug -- Debug
self:T2({"ICLS BEACON started!"}) self:T2( { "ICLS BEACON started!" } )
-- Start beacon. -- Start beacon.
self.Positionable:CommandActivateICLS(Channel, UnitID, Callsign) self.Positionable:CommandActivateICLS( Channel, UnitID, Callsign )
-- Stop sheduler -- Stop scheduler
if Duration then -- Schedule the stop of the BEACON if asked by the MD if Duration then -- Schedule the stop of the BEACON if asked by the MD
self.Positionable:DeactivateBeacon(Duration) self.Positionable:DeactivateBeacon( Duration )
end end
return self return self
end end
@@ -246,27 +247,29 @@ end
-- @param #number BeaconDuration How long will the beacon last in seconds. Omit for forever. -- @param #number BeaconDuration How long will the beacon last in seconds. Omit for forever.
-- @return #BEACON self -- @return #BEACON self
-- @usage -- @usage
--
-- -- Let's create a TACAN Beacon for a tanker -- -- 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 -- local myBeacon = myUnit:GetBeacon() -- Creates the beacon
-- --
-- myBeacon:AATACAN(20, "TEXACO", true) -- Activate 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 local IsValid = true
if not self.Positionable:IsAir() then 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 IsValid = false
end end
local Frequency = self:_TACANToFrequency(TACANChannel, "Y") local Frequency = self:_TACANToFrequency( TACANChannel, "Y" )
if not Frequency then if not Frequency then
self:E({"The passed TACAN channel is invalid, the BEACON is not emitting"}) self:E( { "The passed TACAN channel is invalid, the BEACON is not emitting" } )
IsValid = false IsValid = false
end end
-- I'm using the beacon type 4 (BEACON_TYPE_TACAN). For System, I'm using 5 (TACAN_TANKER_MODE_Y) if the bearing shows its bearing -- I'm using the beacon type 4 (BEACON_TYPE_TACAN). For System, I'm using 5 (TACAN_TANKER_MODE_Y) if the bearing shows its bearing
-- or 14 (TACAN_AA_MODE_Y) if it does not -- or 14 (TACAN_AA_MODE_Y) if it does not
local System local System
@@ -275,27 +278,26 @@ function BEACON:AATACAN(TACANChannel, Message, Bearing, BeaconDuration)
else else
System = 14 System = 14
end end
if IsValid then -- Starts the BEACON if IsValid then -- Starts the BEACON
self:T2({"AA TACAN BEACON started !"}) self:T2( { "AA TACAN BEACON started !" } )
self.Positionable:SetCommand({ self.Positionable:SetCommand( {
id = "ActivateBeacon", id = "ActivateBeacon",
params = { params = {
type = 4, type = 4,
system = System, system = System,
callsign = Message, callsign = Message,
frequency = Frequency, frequency = Frequency,
} },
}) } )
if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD
SCHEDULER:New(nil, SCHEDULER:New( nil, function()
function()
self:StopAATACAN() self:StopAATACAN()
end, {}, BeaconDuration) end, {}, BeaconDuration )
end end
end end
return self return self
end end
@@ -305,21 +307,19 @@ end
function BEACON:StopAATACAN() function BEACON:StopAATACAN()
self:F() self:F()
if not self.Positionable then if not self.Positionable then
self:E({"Start the beacon first before stoping it !"}) self:E( { "Start the beacon first before stopping it!" } )
else else
self.Positionable:SetCommand({ self.Positionable:SetCommand( {
id = 'DeactivateBeacon', id = 'DeactivateBeacon',
params = { params = {},
} } )
})
end end
end end
--- Activates a general purpose Radio Beacon
--- Activates a general pupose Radio Beacon
-- This uses the very generic singleton function "trigger.action.radioTransmission()" provided by DCS to broadcast a sound file on a specific frequency. -- This uses the very generic singleton function "trigger.action.radioTransmission()" provided by DCS to broadcast a sound file on a specific frequency.
-- Although any frequency could be used, only 2 DCS Modules can home on radio beacons at the time of writing : the Huey and the Mi-8. -- Although any frequency could be used, only 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** -- * **Mi8**
-- * R-828 -> 20-60MHz -- * R-828 -> 20-60MHz
-- * ARKUD -> 100-150MHz (canal 1 : 114166, canal 2 : 114333, canal 3 : 114583, canal 4 : 121500, canal 5 : 123100, canal 6 : 124100) AM -- * ARKUD -> 100-150MHz (canal 1 : 114166, canal 2 : 114333, canal 3 : 114583, canal 4 : 121500, canal 5 : 123100, canal 6 : 124100) AM
@@ -342,55 +342,54 @@ end
-- --
-- -- Set the beacon and start it -- -- Set the beacon and start it
-- UnitBeacon:RadioBeacon("MySoundFileSOS.ogg", 40, radio.modulation.FM, 20, 5*60) -- UnitBeacon:RadioBeacon("MySoundFileSOS.ogg", 40, radio.modulation.FM, 20, 5*60)
function BEACON:RadioBeacon(FileName, Frequency, Modulation, Power, BeaconDuration) function BEACON:RadioBeacon( FileName, Frequency, Modulation, Power, BeaconDuration )
self:F({FileName, Frequency, Modulation, Power, BeaconDuration}) self:F( { FileName, Frequency, Modulation, Power, BeaconDuration } )
local IsValid = false local IsValid = false
-- Check the filename -- Check the filename
if type(FileName) == "string" then if type( FileName ) == "string" then
if FileName:find(".ogg") or FileName:find(".wav") then if FileName:find( ".ogg" ) or FileName:find( ".wav" ) then
if not FileName:find("l10n/DEFAULT/") then if not FileName:find( "l10n/DEFAULT/" ) then
FileName = "l10n/DEFAULT/" .. FileName FileName = "l10n/DEFAULT/" .. FileName
end end
IsValid = true IsValid = true
end end
end end
if not IsValid then 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 end
-- Check the Frequency -- Check the Frequency
if type(Frequency) ~= "number" and IsValid then if type( Frequency ) ~= "number" and IsValid then
self:E({"Frequency invalid. ", Frequency}) self:E( { "Frequency invalid. ", Frequency } )
IsValid = false IsValid = false
end end
Frequency = Frequency * 1000000 -- Conversion to Hz Frequency = Frequency * 1000000 -- Conversion to Hz
-- Check the modulation -- 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 ? 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}) self:E( { "Modulation is invalid. Use DCS's enum radio.modulation.", Modulation } )
IsValid = false IsValid = false
end end
-- Check the Power -- Check the Power
if type(Power) ~= "number" and IsValid then if type( Power ) ~= "number" and IsValid then
self:E({"Power is invalid. ", Power}) self:E( { "Power is invalid. ", Power } )
IsValid = false IsValid = false
end 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 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 -- 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)) 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 if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD
SCHEDULER:New( nil, SCHEDULER:New( nil, function()
function() self:StopRadioBeacon()
self:StopRadioBeacon() end, {}, BeaconDuration )
end, {}, BeaconDuration) end
end end
end
end end
--- Stops the AA TACAN BEACON --- Stops the AA TACAN BEACON
@@ -399,7 +398,7 @@ end
function BEACON:StopRadioBeacon() function BEACON:StopRadioBeacon()
self:F() self:F()
-- The unique name of the transmission is the class ID -- The unique name of the transmission is the class ID
trigger.action.stopRadioTransmission(tostring(self.ID)) trigger.action.stopRadioTransmission( tostring( self.ID ) )
return self return self
end end
@@ -407,26 +406,26 @@ end
-- @param #BEACON self -- @param #BEACON self
-- @param #number TACANChannel -- @param #number TACANChannel
-- @param #string TACANMode -- @param #string TACANMode
-- @return #number Frequecy -- @return #number Frequency
-- @return #nil if parameters are invalid -- @return #nil if parameters are invalid
function BEACON:_TACANToFrequency(TACANChannel, TACANMode) function BEACON:_TACANToFrequency( TACANChannel, TACANMode )
self:F3({TACANChannel, TACANMode}) self:F3( { TACANChannel, TACANMode } )
if type(TACANChannel) ~= "number" then if type( TACANChannel ) ~= "number" then
if TACANMode ~= "X" and TACANMode ~= "Y" then if TACANMode ~= "X" and TACANMode ~= "Y" then
return nil -- error in arguments return nil -- error in arguments
end end
end end
-- This code is largely based on ED's code, in DCS World\Scripts\World\Radio\BeaconTypes.lua, line 137. -- 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 -- I have no idea what it does but it seems to work
local A = 1151 -- 'X', channel >= 64 local A = 1151 -- 'X', channel >= 64
local B = 64 -- channel >= 64 local B = 64 -- channel >= 64
if TACANChannel < 64 then if TACANChannel < 64 then
B = 1 B = 1
end end
if TACANMode == 'Y' then if TACANMode == 'Y' then
A = 1025 A = 1025
if TACANChannel < 64 then if TACANChannel < 64 then
@@ -437,6 +436,6 @@ function BEACON:_TACANToFrequency(TACANChannel, TACANMode)
A = 962 A = 962
end end
end end
return (A + TACANChannel - B) * 1000000 return (A + TACANChannel - B) * 1000000
end end

File diff suppressed because it is too large Load Diff

View File

@@ -14,7 +14,7 @@
-- ![Objects](..\Presentations\EVENT\Dia2.JPG) -- ![Objects](..\Presentations\EVENT\Dia2.JPG)
-- --
-- Within a running mission, various DCS events occur. Units are dynamically created, crash, die, shoot stuff, get hit etc. -- Within a running mission, various DCS events occur. Units are dynamically created, crash, die, shoot stuff, get hit etc.
-- This module provides a mechanism to dispatch those events occuring within your running mission, to the different objects orchestrating your mission. -- This module provides a mechanism to dispatch those events occurring within your running mission, to the different objects orchestrating your mission.
-- --
-- ![Objects](..\Presentations\EVENT\Dia3.JPG) -- ![Objects](..\Presentations\EVENT\Dia3.JPG)
-- --
@@ -141,7 +141,6 @@
-- EventData.IniUnit:SmokeGreen() -- EventData.IniUnit:SmokeGreen()
-- end -- end
-- --
--
-- Find below an overview which events populate which information categories: -- Find below an overview which events populate which information categories:
-- --
-- ![Objects](..\Presentations\EVENT\Dia14.JPG) -- ![Objects](..\Presentations\EVENT\Dia14.JPG)
@@ -172,7 +171,6 @@
-- @module Core.Event -- @module Core.Event
-- @image Core_Event.JPG -- @image Core_Event.JPG
--- @type EVENT --- @type EVENT
-- @field #EVENT.Events Events -- @field #EVENT.Events Events
-- @extends Core.Base#BASE -- @extends Core.Base#BASE
@@ -194,7 +192,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_REMOVE_UNIT = world.event.S_EVENT_MAX + 1006
world.event.S_EVENT_PLAYER_ENTER_AIRCRAFT = world.event.S_EVENT_MAX + 1007 world.event.S_EVENT_PLAYER_ENTER_AIRCRAFT = world.event.S_EVENT_MAX + 1007
--- The different types of events supported by MOOSE. --- The different types of events supported by MOOSE.
-- Use this structure to subscribe to events using the @{Core.Base#BASE.HandleEvent}() method. -- Use this structure to subscribe to events using the @{Core.Base#BASE.HandleEvent}() method.
-- @type EVENTS -- @type EVENTS
@@ -227,13 +224,13 @@ EVENTS = {
MarkChange = world.event.S_EVENT_MARK_CHANGE, MarkChange = world.event.S_EVENT_MARK_CHANGE,
MarkRemoved = world.event.S_EVENT_MARK_REMOVED, MarkRemoved = world.event.S_EVENT_MARK_REMOVED,
-- Moose Events -- Moose Events
NewCargo = world.event.S_EVENT_NEW_CARGO, NewCargo = world.event.S_EVENT_NEW_CARGO,
DeleteCargo = world.event.S_EVENT_DELETE_CARGO, DeleteCargo = world.event.S_EVENT_DELETE_CARGO,
NewZone = world.event.S_EVENT_NEW_ZONE, NewZone = world.event.S_EVENT_NEW_ZONE,
DeleteZone = world.event.S_EVENT_DELETE_ZONE, DeleteZone = world.event.S_EVENT_DELETE_ZONE,
NewZoneGoal = world.event.S_EVENT_NEW_ZONE_GOAL, NewZoneGoal = world.event.S_EVENT_NEW_ZONE_GOAL,
DeleteZoneGoal = world.event.S_EVENT_DELETE_ZONE_GOAL, DeleteZoneGoal = world.event.S_EVENT_DELETE_ZONE_GOAL,
RemoveUnit = world.event.S_EVENT_REMOVE_UNIT, RemoveUnit = world.event.S_EVENT_REMOVE_UNIT,
PlayerEnterAircraft = world.event.S_EVENT_PLAYER_ENTER_AIRCRAFT, PlayerEnterAircraft = world.event.S_EVENT_PLAYER_ENTER_AIRCRAFT,
-- Added with DCS 2.5.6 -- Added with DCS 2.5.6
DetailedFailure = world.event.S_EVENT_DETAILED_FAILURE or -1, --We set this to -1 for backward compatibility to DCS 2.5.5 and earlier DetailedFailure = world.event.S_EVENT_DETAILED_FAILURE or -1, --We set this to -1 for backward compatibility to DCS 2.5.5 and earlier
@@ -304,8 +301,6 @@ EVENTS = {
-- @field Core.ZONE#ZONE Zone The zone object. -- @field Core.ZONE#ZONE Zone The zone object.
-- @field #string ZoneName The name of the zone. -- @field #string ZoneName The name of the zone.
local _EVENTMETA = { local _EVENTMETA = {
[world.event.S_EVENT_SHOT] = { [world.event.S_EVENT_SHOT] = {
Order = 1, Order = 1,
@@ -562,7 +557,6 @@ local _EVENTMETA = {
}, },
} }
--- The Events structure --- The Events structure
-- @type EVENT.Events -- @type EVENT.Events
-- @field #number IniUnit -- @field #number IniUnit
@@ -576,12 +570,11 @@ function EVENT:New()
local self = BASE:Inherit( self, BASE:New() ) local self = BASE:Inherit( self, BASE:New() )
-- Add world event handler. -- Add world event handler.
self.EventHandler = world.addEventHandler(self) self.EventHandler = world.addEventHandler( self )
return self return self
end end
--- Initializes the Events structure for the event. --- Initializes the Events structure for the event.
-- @param #EVENT self -- @param #EVENT self
-- @param DCS#world.event EventID Event ID. -- @param DCS#world.event EventID Event ID.
@@ -595,7 +588,7 @@ function EVENT:Init( EventID, EventClass )
self.Events[EventID] = {} self.Events[EventID] = {}
end end
-- Each event has a subtable of EventClasses, ordered by EventPriority. -- Each event has a sub-table of EventClasses, ordered by EventPriority.
local EventPriority = EventClass:GetEventPriority() local EventPriority = EventClass:GetEventPriority()
if not self.Events[EventID][EventPriority] then if not self.Events[EventID][EventPriority] then
@@ -603,7 +596,7 @@ function EVENT:Init( EventID, EventClass )
end end
if not self.Events[EventID][EventPriority][EventClass] then if not self.Events[EventID][EventPriority][EventClass] then
self.Events[EventID][EventPriority][EventClass] = {} self.Events[EventID][EventPriority][EventClass] = {}
end end
return self.Events[EventID][EventPriority][EventClass] return self.Events[EventID][EventPriority][EventClass]
@@ -614,7 +607,7 @@ end
-- @param Core.Base#BASE EventClass The self instance of the class for which the event is. -- @param Core.Base#BASE EventClass The self instance of the class for which the event is.
-- @param DCS#world.event EventID Event ID. -- @param DCS#world.event EventID Event ID.
-- @return #EVENT self -- @return #EVENT self
function EVENT:RemoveEvent( EventClass, EventID ) function EVENT:RemoveEvent( EventClass, EventID )
-- Debug info. -- Debug info.
self:F2( { "Removing subscription for class: ", EventClass:GetClassNameAndID() } ) self:F2( { "Removing subscription for class: ", EventClass:GetClassNameAndID() } )
@@ -638,7 +631,7 @@ end
-- @param Core.Base#BASE EventClass The self instance of the class for which the event is. -- @param Core.Base#BASE EventClass The self instance of the class for which the event is.
-- @param DCS#world.event EventID Event ID. -- @param DCS#world.event EventID Event ID.
-- @return #EVENT.Events -- @return #EVENT.Events
function EVENT:Reset( EventObject ) --R2.1 function EVENT:Reset( EventObject ) -- R2.1
self:F( { "Resetting subscriptions for class: ", EventObject:GetClassNameAndID() } ) self:F( { "Resetting subscriptions for class: ", EventObject:GetClassNameAndID() } )
@@ -657,12 +650,11 @@ function EVENT:Reset( EventObject ) --R2.1
end end
end end
--- Clears all event subscriptions for a @{Core.Base#BASE} derived object. --- Clears all event subscriptions for a @{Core.Base#BASE} derived object.
-- @param #EVENT self -- @param #EVENT self
-- @param Core.Base#BASE EventClass The self class object for which the events are removed. -- @param Core.Base#BASE EventClass The self class object for which the events are removed.
-- @return #EVENT self -- @return #EVENT self
function EVENT:RemoveAll(EventClass) function EVENT:RemoveAll( EventClass )
local EventClassName = EventClass:GetClassNameAndID() local EventClassName = EventClass:GetClassNameAndID()
@@ -676,8 +668,6 @@ function EVENT:RemoveAll(EventClass)
return self return self
end end
--- Create an OnDead event handler for a group --- Create an OnDead event handler for a group
-- @param #EVENT self -- @param #EVENT self
-- @param #table EventTemplate -- @param #table EventTemplate
@@ -709,7 +699,6 @@ function EVENT:OnEventGeneric( EventFunction, EventClass, EventID )
return self return self
end end
--- Set a new listener for an `S_EVENT_X` event for a UNIT. --- Set a new listener for an `S_EVENT_X` event for a UNIT.
-- @param #EVENT self -- @param #EVENT self
-- @param #string UnitName The name of the UNIT. -- @param #string UnitName The name of the UNIT.
@@ -797,7 +786,6 @@ do -- OnDead
end end
do -- OnLand do -- OnLand
--- Create an OnLand event handler for a group --- Create an OnLand event handler for a group
@@ -864,7 +852,7 @@ do -- Event Creation
id = EVENTS.NewCargo, id = EVENTS.NewCargo,
time = timer.getTime(), time = timer.getTime(),
cargo = Cargo, cargo = Cargo,
} }
world.onEvent( Event ) world.onEvent( Event )
end end
@@ -879,7 +867,7 @@ do -- Event Creation
id = EVENTS.DeleteCargo, id = EVENTS.DeleteCargo,
time = timer.getTime(), time = timer.getTime(),
cargo = Cargo, cargo = Cargo,
} }
world.onEvent( Event ) world.onEvent( Event )
end end
@@ -894,7 +882,7 @@ do -- Event Creation
id = EVENTS.NewZone, id = EVENTS.NewZone,
time = timer.getTime(), time = timer.getTime(),
zone = Zone, zone = Zone,
} }
world.onEvent( Event ) world.onEvent( Event )
end end
@@ -909,7 +897,7 @@ do -- Event Creation
id = EVENTS.DeleteZone, id = EVENTS.DeleteZone,
time = timer.getTime(), time = timer.getTime(),
zone = Zone, zone = Zone,
} }
world.onEvent( Event ) world.onEvent( Event )
end end
@@ -924,12 +912,11 @@ do -- Event Creation
id = EVENTS.NewZoneGoal, id = EVENTS.NewZoneGoal,
time = timer.getTime(), time = timer.getTime(),
ZoneGoal = ZoneGoal, ZoneGoal = ZoneGoal,
} }
world.onEvent( Event ) world.onEvent( Event )
end end
--- Creation of a ZoneGoal Deletion Event. --- Creation of a ZoneGoal Deletion Event.
-- @param #EVENT self -- @param #EVENT self
-- @param Core.ZoneGoal#ZONE_GOAL ZoneGoal The ZoneGoal created. -- @param Core.ZoneGoal#ZONE_GOAL ZoneGoal The ZoneGoal created.
@@ -940,12 +927,11 @@ do -- Event Creation
id = EVENTS.DeleteZoneGoal, id = EVENTS.DeleteZoneGoal,
time = timer.getTime(), time = timer.getTime(),
ZoneGoal = ZoneGoal, ZoneGoal = ZoneGoal,
} }
world.onEvent( Event ) world.onEvent( Event )
end end
--- Creation of a S_EVENT_PLAYER_ENTER_UNIT Event. --- Creation of a S_EVENT_PLAYER_ENTER_UNIT Event.
-- @param #EVENT self -- @param #EVENT self
-- @param Wrapper.Unit#UNIT PlayerUnit. -- @param Wrapper.Unit#UNIT PlayerUnit.
@@ -955,8 +941,8 @@ do -- Event Creation
local Event = { local Event = {
id = EVENTS.PlayerEnterUnit, id = EVENTS.PlayerEnterUnit,
time = timer.getTime(), time = timer.getTime(),
initiator = PlayerUnit:GetDCSObject() initiator = PlayerUnit:GetDCSObject(),
} }
world.onEvent( Event ) world.onEvent( Event )
end end
@@ -970,8 +956,8 @@ do -- Event Creation
local Event = { local Event = {
id = EVENTS.PlayerEnterAircraft, id = EVENTS.PlayerEnterAircraft,
time = timer.getTime(), time = timer.getTime(),
initiator = PlayerUnit:GetDCSObject() initiator = PlayerUnit:GetDCSObject(),
} }
world.onEvent( Event ) world.onEvent( Event )
end end
@@ -993,14 +979,13 @@ function EVENT:onEvent( Event )
return errmsg return errmsg
end end
-- Get event meta data. -- Get event meta data.
local EventMeta = _EVENTMETA[Event.id] local EventMeta = _EVENTMETA[Event.id]
-- Check if this is a known event? -- Check if this is a known event?
if EventMeta then if EventMeta then
if self and 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 if Event.id and Event.id == EVENTS.MissionEnd then
self.MissionEnd = true self.MissionEnd = true
@@ -1024,9 +1009,9 @@ function EVENT:onEvent( Event )
if Event.IniDCSGroup and Event.IniDCSGroup:isExist() then if Event.IniDCSGroup and Event.IniDCSGroup:isExist() then
Event.IniDCSGroupName = Event.IniDCSGroup:getName() Event.IniDCSGroupName = Event.IniDCSGroup:getName()
Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName ) Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName )
--if Event.IniGroup then -- if Event.IniGroup then
Event.IniGroupName = Event.IniDCSGroupName Event.IniGroupName = Event.IniDCSGroupName
--end -- end
end end
Event.IniPlayerName = Event.IniDCSUnit:getPlayerName() Event.IniPlayerName = Event.IniDCSUnit:getPlayerName()
Event.IniCoalition = Event.IniDCSUnit:getCoalition() Event.IniCoalition = Event.IniDCSUnit:getCoalition()
@@ -1036,23 +1021,23 @@ function EVENT:onEvent( Event )
if Event.IniObjectCategory == Object.Category.STATIC then if Event.IniObjectCategory == Object.Category.STATIC then
if Event.id==31 then if Event.id == 31 then
-- Event.initiator is a Static object representing the pilot. But getName() errors due to DCS bug. -- Event.initiator is a Static object representing the pilot. But getName() errors due to DCS bug.
Event.IniDCSUnit = Event.initiator Event.IniDCSUnit = Event.initiator
local ID=Event.initiator.id_ local ID = Event.initiator.id_
Event.IniDCSUnitName = string.format("Ejected Pilot ID %s", tostring(ID)) Event.IniDCSUnitName = string.format( "Ejected Pilot ID %s", tostring( ID ) )
Event.IniUnitName = Event.IniDCSUnitName Event.IniUnitName = Event.IniDCSUnitName
Event.IniCoalition = 0 Event.IniCoalition = 0
Event.IniCategory = 0 Event.IniCategory = 0
Event.IniTypeName = "Ejected Pilot" Event.IniTypeName = "Ejected Pilot"
elseif Event.id == 33 then -- ejection seat discarded elseif Event.id == 33 then -- ejection seat discarded
Event.IniDCSUnit = Event.initiator Event.IniDCSUnit = Event.initiator
local ID=Event.initiator.id_ local ID = Event.initiator.id_
Event.IniDCSUnitName = string.format("Ejection Seat ID %s", tostring(ID)) Event.IniDCSUnitName = string.format( "Ejection Seat ID %s", tostring( ID ) )
Event.IniUnitName = Event.IniDCSUnitName Event.IniUnitName = Event.IniDCSUnitName
Event.IniCoalition = 0 Event.IniCoalition = 0
Event.IniCategory = 0 Event.IniCategory = 0
Event.IniTypeName = "Ejection Seat" Event.IniTypeName = "Ejection Seat"
else else
Event.IniDCSUnit = Event.initiator Event.IniDCSUnit = Event.initiator
@@ -1088,7 +1073,7 @@ function EVENT:onEvent( Event )
Event.IniDCSUnit = Event.initiator Event.IniDCSUnit = Event.initiator
Event.IniDCSUnitName = Event.IniDCSUnit:getName() Event.IniDCSUnitName = Event.IniDCSUnit:getName()
Event.IniUnitName = Event.IniDCSUnitName Event.IniUnitName = Event.IniDCSUnitName
Event.IniUnit = AIRBASE:FindByName(Event.IniDCSUnitName) Event.IniUnit = AIRBASE:FindByName( Event.IniDCSUnitName )
Event.IniCoalition = Event.IniDCSUnit:getCoalition() Event.IniCoalition = Event.IniDCSUnit:getCoalition()
Event.IniCategory = Event.IniDCSUnit:getDesc().category Event.IniCategory = Event.IniDCSUnit:getDesc().category
Event.IniTypeName = Event.IniDCSUnit:getTypeName() Event.IniTypeName = Event.IniDCSUnit:getTypeName()
@@ -1109,9 +1094,9 @@ function EVENT:onEvent( Event )
if Event.TgtDCSGroup and Event.TgtDCSGroup:isExist() then if Event.TgtDCSGroup and Event.TgtDCSGroup:isExist() then
Event.TgtDCSGroupName = Event.TgtDCSGroup:getName() Event.TgtDCSGroupName = Event.TgtDCSGroup:getName()
Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName ) Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName )
--if Event.TgtGroup then -- if Event.TgtGroup then
Event.TgtGroupName = Event.TgtDCSGroupName Event.TgtGroupName = Event.TgtDCSGroupName
--end -- end
end end
Event.TgtPlayerName = Event.TgtDCSUnit:getPlayerName() Event.TgtPlayerName = Event.TgtDCSUnit:getPlayerName()
Event.TgtCoalition = Event.TgtDCSUnit:getCoalition() Event.TgtCoalition = Event.TgtDCSUnit:getCoalition()
@@ -1130,18 +1115,18 @@ function EVENT:onEvent( Event )
Event.TgtCategory = Event.TgtDCSUnit:getDesc().category Event.TgtCategory = Event.TgtDCSUnit:getDesc().category
Event.TgtTypeName = Event.TgtDCSUnit:getTypeName() Event.TgtTypeName = Event.TgtDCSUnit:getTypeName()
else 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.TgtUnitName = Event.TgtDCSUnitName
Event.TgtUnit = nil Event.TgtUnit = nil
Event.TgtCoalition = 0 Event.TgtCoalition = 0
Event.TgtCategory = 0 Event.TgtCategory = 0
if Event.id == 6 then if Event.id == 6 then
Event.TgtTypeName = "Ejected Pilot" 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 Event.TgtUnitName = Event.TgtDCSUnitName
elseif Event.id == 33 then elseif Event.id == 33 then
Event.TgtTypeName = "Ejection Seat" 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 Event.TgtUnitName = Event.TgtDCSUnitName
else else
Event.TgtTypeName = "Static" Event.TgtTypeName = "Static"
@@ -1167,29 +1152,29 @@ function EVENT:onEvent( Event )
Event.WeaponCoalition = Event.WeaponUNIT and Event.Weapon:getCoalition() Event.WeaponCoalition = Event.WeaponUNIT and Event.Weapon:getCoalition()
Event.WeaponCategory = Event.WeaponUNIT and Event.Weapon:getDesc().category Event.WeaponCategory = Event.WeaponUNIT and Event.Weapon:getDesc().category
Event.WeaponTypeName = Event.WeaponUNIT and Event.Weapon:getTypeName() Event.WeaponTypeName = Event.WeaponUNIT and Event.Weapon:getTypeName()
--Event.WeaponTgtDCSUnit = Event.Weapon:getTarget() -- Event.WeaponTgtDCSUnit = Event.Weapon:getTarget()
end end
-- Place should be given for takeoff and landing events as well as base captured. It should be a DCS airbase. -- Place should be given for takeoff and landing events as well as base captured. It should be a DCS airbase.
if Event.place then if Event.place then
if Event.id==EVENTS.LandingAfterEjection then if Event.id == EVENTS.LandingAfterEjection then
-- Place is here the UNIT of which the pilot ejected. -- Place is here the UNIT of which the pilot ejected.
--local name=Event.place:getName() -- This returns a DCS error "Airbase doesn't exit" :( -- local name=Event.place:getName() -- This returns a DCS error "Airbase doesn't exit" :(
-- However, this is not a big thing, as the aircraft the pilot ejected from is usually long crashed before the ejected pilot touches the ground. -- However, this is not a big thing, as the aircraft the pilot ejected from is usually long crashed before the ejected pilot touches the ground.
--Event.Place=UNIT:Find(Event.place) -- Event.Place=UNIT:Find(Event.place)
else else
Event.Place=AIRBASE:Find(Event.place) Event.Place = AIRBASE:Find( Event.place )
Event.PlaceName=Event.Place:GetName() Event.PlaceName = Event.Place:GetName()
end end
end end
-- Mark points. -- Mark points.
if Event.idx then if Event.idx then
Event.MarkID=Event.idx Event.MarkID = Event.idx
Event.MarkVec3=Event.pos Event.MarkVec3 = Event.pos
Event.MarkCoordinate=COORDINATE:NewFromVec3(Event.pos) Event.MarkCoordinate = COORDINATE:NewFromVec3( Event.pos )
Event.MarkText=Event.text Event.MarkText = Event.text
Event.MarkCoalition=Event.coalition Event.MarkCoalition = Event.coalition
Event.MarkGroupID = Event.groupID Event.MarkGroupID = Event.groupID
end end
@@ -1218,9 +1203,9 @@ function EVENT:onEvent( Event )
-- Okay, we got the event from DCS. Now loop the SORTED self.EventSorted[] table for the received Event.id, and for each EventData registered, check if a function needs to be called. -- Okay, we got the event from DCS. Now loop the SORTED self.EventSorted[] table for the received Event.id, and for each EventData registered, check if a function needs to be called.
for EventClass, EventData in pairs( self.Events[Event.id][EventPriority] ) do for EventClass, EventData in pairs( self.Events[Event.id][EventPriority] ) do
--if Event.IniObjectCategory ~= Object.Category.STATIC then -- if Event.IniObjectCategory ~= Object.Category.STATIC then
-- self:E( { "Evaluating: ", EventClass:GetClassNameAndID() } ) -- self:E( { "Evaluating: ", EventClass:GetClassNameAndID() } )
--end -- end
Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName ) Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName )
Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName ) Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName )
@@ -1237,8 +1222,8 @@ function EVENT:onEvent( Event )
local UnitName = EventClass:GetName() local UnitName = EventClass:GetName()
if ( EventMeta.Side == "I" and UnitName == Event.IniDCSUnitName ) or if (EventMeta.Side == "I" and UnitName == Event.IniDCSUnitName) or
( EventMeta.Side == "T" and UnitName == Event.TgtDCSUnitName ) then (EventMeta.Side == "T" and UnitName == Event.TgtDCSUnitName) then
-- First test if a EventFunction is Set, otherwise search for the default function -- First test if a EventFunction is Set, otherwise search for the default function
if EventData.EventFunction then if EventData.EventFunction then
@@ -1247,15 +1232,14 @@ function EVENT:onEvent( Event )
self:F( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } ) self:F( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } )
end end
local Result, Value = xpcall( local Result, Value = xpcall( function()
function() return EventData.EventFunction( EventClass, Event )
return EventData.EventFunction( EventClass, Event ) end, ErrorHandler )
end, ErrorHandler )
else else
-- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object. -- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object.
local EventFunction = EventClass[ EventMeta.Event ] local EventFunction = EventClass[EventMeta.Event]
if EventFunction and type( EventFunction ) == "function" then if EventFunction and type( EventFunction ) == "function" then
-- Now call the default event function. -- Now call the default event function.
@@ -1263,10 +1247,9 @@ function EVENT:onEvent( Event )
self:F( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) self:F( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
end end
local Result, Value = xpcall( local Result, Value = xpcall( function()
function() return EventFunction( EventClass, Event )
return EventFunction( EventClass, Event ) end, ErrorHandler )
end, ErrorHandler )
end end
end end
end end
@@ -1290,8 +1273,8 @@ function EVENT:onEvent( Event )
-- We can get the name of the EventClass, which is now always a GROUP object. -- We can get the name of the EventClass, which is now always a GROUP object.
local GroupName = EventClass:GetName() local GroupName = EventClass:GetName()
if ( EventMeta.Side == "I" and GroupName == Event.IniDCSGroupName ) or if (EventMeta.Side == "I" and GroupName == Event.IniDCSGroupName) or
( EventMeta.Side == "T" and GroupName == Event.TgtDCSGroupName ) then (EventMeta.Side == "T" and GroupName == Event.TgtDCSGroupName) then
-- First test if a EventFunction is Set, otherwise search for the default function -- First test if a EventFunction is Set, otherwise search for the default function
if EventData.EventFunction then if EventData.EventFunction then
@@ -1300,15 +1283,14 @@ function EVENT:onEvent( Event )
self:F( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } ) self:F( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } )
end end
local Result, Value = xpcall( local Result, Value = xpcall( function()
function() return EventData.EventFunction( EventClass, Event, unpack( EventData.Params ) )
return EventData.EventFunction( EventClass, Event, unpack( EventData.Params ) ) end, ErrorHandler )
end, ErrorHandler )
else else
-- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object. -- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object.
local EventFunction = EventClass[ EventMeta.Event ] local EventFunction = EventClass[EventMeta.Event]
if EventFunction and type( EventFunction ) == "function" then if EventFunction and type( EventFunction ) == "function" then
-- Now call the default event function. -- Now call the default event function.
@@ -1316,16 +1298,15 @@ function EVENT:onEvent( Event )
self:F( { "Calling " .. EventMeta.Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } ) self:F( { "Calling " .. EventMeta.Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } )
end end
local Result, Value = xpcall( local Result, Value = xpcall( function()
function() return EventFunction( EventClass, Event, unpack( EventData.Params ) )
return EventFunction( EventClass, Event, unpack( EventData.Params ) ) end, ErrorHandler )
end, ErrorHandler )
end end
end end
end end
else else
-- The EventClass is not alive anymore, we remove it from the EventHandlers... -- The EventClass is not alive anymore, we remove it from the EventHandlers...
--self:RemoveEvent( EventClass, Event.id ) -- self:RemoveEvent( EventClass, Event.id )
end end
else else
@@ -1340,14 +1321,13 @@ function EVENT:onEvent( Event )
if Event.IniObjectCategory ~= 3 then if Event.IniObjectCategory ~= 3 then
self:F2( { "Calling EventFunction for Class ", EventClass:GetClassNameAndID(), EventPriority } ) self:F2( { "Calling EventFunction for Class ", EventClass:GetClassNameAndID(), EventPriority } )
end end
local Result, Value = xpcall( local Result, Value = xpcall( function()
function() return EventData.EventFunction( EventClass, Event )
return EventData.EventFunction( EventClass, Event ) end, ErrorHandler )
end, ErrorHandler )
else else
-- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object. -- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object.
local EventFunction = EventClass[ EventMeta.Event ] local EventFunction = EventClass[EventMeta.Event]
if EventFunction and type( EventFunction ) == "function" then if EventFunction and type( EventFunction ) == "function" then
-- Now call the default event function. -- Now call the default event function.
@@ -1355,11 +1335,10 @@ function EVENT:onEvent( Event )
self:F2( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) self:F2( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
end end
local Result, Value = xpcall( local Result, Value = xpcall( function()
function() local Result, Value = EventFunction( EventClass, Event )
local Result, Value = EventFunction( EventClass, Event ) return Result, Value
return Result, Value end, ErrorHandler )
end, ErrorHandler )
end end
end end
@@ -1383,7 +1362,7 @@ function EVENT:onEvent( Event )
self:T( { EventMeta.Text, Event } ) self:T( { EventMeta.Text, Event } )
end end
else else
self:E(string.format("WARNING: Could not get EVENTMETA data for event ID=%d! Is this an unknown/new DCS event?", tostring(Event.id))) self:E( string.format( "WARNING: Could not get EVENTMETA data for event ID=%d! Is this an unknown/new DCS event?", tostring( Event.id ) ) )
end end
Event = nil Event = nil

File diff suppressed because it is too large Load Diff

View File

@@ -1,89 +1,87 @@
--- **Core** - Models the process to achieve goal(s). --- **Core** - Models the process to achieve goal(s).
-- --
-- === -- ===
-- --
-- ## Features: -- ## Features:
-- --
-- * Define the goal. -- * Define the goal.
-- * Monitor the goal achievement. -- * Monitor the goal achievement.
-- * Manage goal contribution by players. -- * Manage goal contribution by players.
-- --
-- === -- ===
-- --
-- Classes that implement a goal achievement, will derive from GOAL to implement the ways how the achievements can be realized. -- Classes that implement a goal achievement, will derive from GOAL to implement the ways how the achievements can be realized.
-- --
-- === -- ===
-- --
-- ### Author: **FlightControl** -- ### Author: **FlightControl**
-- ### Contributions: **funkyfranky** -- ### Contributions: **funkyfranky**
-- --
-- === -- ===
-- --
-- @module Core.Goal -- @module Core.Goal
-- @image Core_Goal.JPG -- @image Core_Goal.JPG
do -- Goal do -- Goal
--- @type GOAL --- @type GOAL
-- @extends Core.Fsm#FSM -- @extends Core.Fsm#FSM
--- Models processes that have an objective with a defined achievement. Derived classes implement the ways how the achievements can be realized. --- Models processes that have an objective with a defined achievement. Derived classes implement the ways how the achievements can be realized.
-- --
-- # 1. GOAL constructor -- # 1. GOAL constructor
-- --
-- * @{#GOAL.New}(): Creates a new GOAL object. -- * @{#GOAL.New}(): Creates a new GOAL object.
-- --
-- # 2. GOAL is a finite state machine (FSM). -- # 2. GOAL is a finite state machine (FSM).
-- --
-- ## 2.1. GOAL States -- ## 2.1. GOAL States
-- --
-- * **Pending**: The goal object is in progress. -- * **Pending**: The goal object is in progress.
-- * **Achieved**: The goal objective is Achieved. -- * **Achieved**: The goal objective is Achieved.
-- --
-- ## 2.2. GOAL Events -- ## 2.2. GOAL Events
-- --
-- * **Achieved**: Set the goal objective to Achieved. -- * **Achieved**: Set the goal objective to Achieved.
-- --
-- # 3. Player contributions. -- # 3. Player contributions.
-- --
-- Goals are most of the time achieved by players. These player achievements can be registered as part of the goal achievement. -- Goals are most of the time achieved by players. These player achievements can be registered as part of the goal achievement.
-- Use @{#GOAL.AddPlayerContribution}() to add a player contribution to the goal. -- Use @{#GOAL.AddPlayerContribution}() to add a player contribution to the goal.
-- The player contributions are based on a points system, an internal counter per player. -- The player contributions are based on a points system, an internal counter per player.
-- So once the goal has been achieved, the player contributions can be queried using @{#GOAL.GetPlayerContributions}(), -- So once the goal has been achieved, the player contributions can be queried using @{#GOAL.GetPlayerContributions}(),
-- that retrieves all contributions done by the players. For one player, the contribution can be queried using @{#GOAL.GetPlayerContribution}(). -- that retrieves all contributions done by the players. For one player, the contribution can be queried using @{#GOAL.GetPlayerContribution}().
-- The total amount of player contributions can be queried using @{#GOAL.GetTotalContributions}(). -- The total amount of player contributions can be queried using @{#GOAL.GetTotalContributions}().
-- --
-- # 4. Goal achievement. -- # 4. Goal achievement.
-- --
-- Once the goal is achieved, the mission designer will need to trigger the goal achievement using the **Achieved** event. -- Once the goal is achieved, the mission designer will need to trigger the goal achievement using the **Achieved** event.
-- The underlying 2 examples will achieve the goals for the `Goal` object: -- The underlying 2 examples will achieve the goals for the `Goal` object:
-- --
-- Goal:Achieved() -- Achieve the goal immediately. -- Goal:Achieved() -- Achieve the goal immediately.
-- Goal:__Achieved( 30 ) -- Achieve the goal within 30 seconds. -- Goal:__Achieved( 30 ) -- Achieve the goal within 30 seconds.
-- --
-- # 5. Check goal achievement. -- # 5. Check goal achievement.
-- --
-- The method @{#GOAL.IsAchieved}() will return true if the goal is achieved (the trigger **Achieved** was executed). -- The method @{#GOAL.IsAchieved}() will return true if the goal is achieved (the trigger **Achieved** was executed).
-- You can use this method to check asynchronously if a goal has been achieved, for example using a scheduler. -- You can use this method to check asynchronously if a goal has been achieved, for example using a scheduler.
-- --
-- @field #GOAL -- @field #GOAL
GOAL = { GOAL = {
ClassName = "GOAL", ClassName = "GOAL",
} }
--- @field #table GOAL.Players --- @field #table GOAL.Players
GOAL.Players = {} GOAL.Players = {}
--- @field #number GOAL.TotalContributions --- @field #number GOAL.TotalContributions
GOAL.TotalContributions = 0 GOAL.TotalContributions = 0
--- GOAL Constructor. --- GOAL Constructor.
-- @param #GOAL self -- @param #GOAL self
-- @return #GOAL -- @return #GOAL
function GOAL:New() function GOAL:New()
local self = BASE:Inherit( self, FSM:New() ) -- #GOAL local self = BASE:Inherit( self, FSM:New() ) -- #GOAL
self:F( {} ) self:F( {} )
@@ -104,11 +102,10 @@ do -- Goal
-- @param #string From -- @param #string From
-- @param #string Event -- @param #string Event
-- @param #string To -- @param #string To
self:SetStartState( "Pending" ) self:SetStartState( "Pending" )
self:AddTransition( "*", "Achieved", "Achieved" ) self:AddTransition( "*", "Achieved", "Achieved" )
--- Achieved Handler OnBefore for GOAL --- Achieved Handler OnBefore for GOAL
-- @function [parent=#GOAL] OnBeforeAchieved -- @function [parent=#GOAL] OnBeforeAchieved
-- @param #GOAL self -- @param #GOAL self
@@ -116,47 +113,44 @@ do -- Goal
-- @param #string Event -- @param #string Event
-- @param #string To -- @param #string To
-- @return #boolean -- @return #boolean
--- Achieved Handler OnAfter for GOAL --- Achieved Handler OnAfter for GOAL
-- @function [parent=#GOAL] OnAfterAchieved -- @function [parent=#GOAL] OnAfterAchieved
-- @param #GOAL self -- @param #GOAL self
-- @param #string From -- @param #string From
-- @param #string Event -- @param #string Event
-- @param #string To -- @param #string To
--- Achieved Trigger for GOAL --- Achieved Trigger for GOAL
-- @function [parent=#GOAL] Achieved -- @function [parent=#GOAL] Achieved
-- @param #GOAL self -- @param #GOAL self
--- Achieved Asynchronous Trigger for GOAL --- Achieved Asynchronous Trigger for GOAL
-- @function [parent=#GOAL] __Achieved -- @function [parent=#GOAL] __Achieved
-- @param #GOAL self -- @param #GOAL self
-- @param #number Delay -- @param #number Delay
self:SetEventPriority( 5 ) self:SetEventPriority( 5 )
return self return self
end end
--- Add a new contribution by a player. --- Add a new contribution by a player.
-- @param #GOAL self -- @param #GOAL self
-- @param #string PlayerName The name of the player. -- @param #string PlayerName The name of the player.
function GOAL:AddPlayerContribution( PlayerName ) function GOAL:AddPlayerContribution( PlayerName )
self:F({PlayerName}) self:F( { PlayerName } )
self.Players[PlayerName] = self.Players[PlayerName] or 0 self.Players[PlayerName] = self.Players[PlayerName] or 0
self.Players[PlayerName] = self.Players[PlayerName] + 1 self.Players[PlayerName] = self.Players[PlayerName] + 1
self.TotalContributions = self.TotalContributions + 1 self.TotalContributions = self.TotalContributions + 1
end end
--- @param #GOAL self --- @param #GOAL self
-- @param #number Player contribution. -- @param #number Player contribution.
function GOAL:GetPlayerContribution( PlayerName ) function GOAL:GetPlayerContribution( PlayerName )
return self.Players[PlayerName] or 0 return self.Players[PlayerName] or 0
end end
--- Get the players who contributed to achieve the goal. --- Get the players who contributed to achieve the goal.
-- The result is a list of players, sorted by the name of the players. -- The result is a list of players, sorted by the name of the players.
-- @param #GOAL self -- @param #GOAL self
@@ -165,7 +159,6 @@ do -- Goal
return self.Players or {} return self.Players or {}
end end
--- Gets the total contributions that happened to achieve the goal. --- Gets the total contributions that happened to achieve the goal.
-- The result is a number. -- The result is a number.
-- @param #GOAL self -- @param #GOAL self
@@ -173,9 +166,7 @@ do -- Goal
function GOAL:GetTotalContributions() function GOAL:GetTotalContributions()
return self.TotalContributions or 0 return self.TotalContributions or 0
end end
--- Validates if the goal is achieved. --- Validates if the goal is achieved.
-- @param #GOAL self -- @param #GOAL self
-- @return #boolean true if the goal is achieved. -- @return #boolean true if the goal is achieved.
@@ -183,4 +174,4 @@ do -- Goal
return self:Is( "Achieved" ) return self:Is( "Achieved" )
end end
end end

File diff suppressed because it is too large Load Diff

View File

@@ -1,18 +1,18 @@
--- **Core** - Informs the players using messages during a simulation. --- **Core** - Informs the players using messages during a simulation.
-- --
-- === -- ===
-- --
-- ## Features: -- ## Features:
-- --
-- * A more advanced messaging system using the DCS message system. -- * A more advanced messaging system using the DCS message system.
-- * Time messages. -- * Time messages.
-- * Send messages based on a message type, which has a pre-defined duration that can be tweaked in SETTINGS. -- * Send messages based on a message type, which has a pre-defined duration that can be tweaked in SETTINGS.
-- * Send message to all players. -- * Send message to all players.
-- * Send messages to a coalition. -- * Send messages to a coalition.
-- * Send messages to a specific group. -- * Send messages to a specific group.
-- --
-- === -- ===
-- --
-- @module Core.Message -- @module Core.Message
-- @image Core_Message.JPG -- @image Core_Message.JPG
@@ -23,14 +23,14 @@
--- Message System to display Messages to Clients, Coalitions or All. --- Message System to display Messages to Clients, Coalitions or All.
-- Messages are shown on the display panel for an amount of seconds, and will then disappear. -- Messages are shown on the display panel for an amount of seconds, and will then disappear.
-- Messages can contain a category which is indicating the category of the message. -- Messages can contain a category which is indicating the category of the message.
-- --
-- ## MESSAGE construction -- ## MESSAGE construction
-- --
-- Messages are created with @{#MESSAGE.New}. Note that when the MESSAGE object is created, no message is sent yet. -- Messages are created with @{#MESSAGE.New}. Note that when the MESSAGE object is created, no message is sent yet.
-- To send messages, you need to use the To functions. -- To send messages, you need to use the To functions.
-- --
-- ## Send messages to an audience -- ## Send messages to an audience
-- --
-- Messages are sent: -- Messages are sent:
-- --
-- * To a @{Client} using @{#MESSAGE.ToClient}(). -- * To a @{Client} using @{#MESSAGE.ToClient}().
@@ -39,26 +39,26 @@
-- * To the red coalition using @{#MESSAGE.ToRed}(). -- * To the red coalition using @{#MESSAGE.ToRed}().
-- * To the blue coalition using @{#MESSAGE.ToBlue}(). -- * To the blue coalition using @{#MESSAGE.ToBlue}().
-- * To all Players using @{#MESSAGE.ToAll}(). -- * To all Players using @{#MESSAGE.ToAll}().
-- --
-- ## Send conditionally to an audience -- ## Send conditionally to an audience
-- --
-- Messages can be sent conditionally to an audience (when a condition is true): -- Messages can be sent conditionally to an audience (when a condition is true):
-- --
-- * To all players using @{#MESSAGE.ToAllIf}(). -- * To all players using @{#MESSAGE.ToAllIf}().
-- * To a coalition using @{#MESSAGE.ToCoalitionIf}(). -- * To a coalition using @{#MESSAGE.ToCoalitionIf}().
-- --
-- === -- ===
-- --
-- ### Author: **FlightControl** -- ### Author: **FlightControl**
-- ### Contributions: -- ### Contributions:
-- --
-- === -- ===
-- --
-- @field #MESSAGE -- @field #MESSAGE
MESSAGE = { MESSAGE = {
ClassName = "MESSAGE", ClassName = "MESSAGE",
MessageCategory = 0, MessageCategory = 0,
MessageID = 0, MessageID = 0,
} }
--- Message Types --- Message Types
@@ -68,10 +68,9 @@ MESSAGE.Type = {
Information = "Information", Information = "Information",
Briefing = "Briefing Report", Briefing = "Briefing Report",
Overview = "Overview Report", Overview = "Overview Report",
Detailed = "Detailed Report" Detailed = "Detailed Report",
} }
--- Creates a new MESSAGE object. Note that these MESSAGE objects are not yet displayed on the display panel. You must use the functions @{ToClient} or @{ToCoalition} or @{ToAll} to send these Messages to the respective recipients. --- Creates a new MESSAGE object. Note that these MESSAGE objects are not yet displayed on the display panel. You must use the functions @{ToClient} or @{ToCoalition} or @{ToAll} to send these Messages to the respective recipients.
-- @param self -- @param self
-- @param #string MessageText is the text of the Message. -- @param #string MessageText is the text of the Message.
@@ -80,52 +79,52 @@ MESSAGE.Type = {
-- @param #boolean ClearScreen (optional) Clear all previous messages if true. -- @param #boolean ClearScreen (optional) Clear all previous messages if true.
-- @return #MESSAGE -- @return #MESSAGE
-- @usage -- @usage
-- -- Create a series of new Messages. --
-- -- MessageAll is meant to be sent to all players, for 25 seconds, and is classified as "Score". -- -- Create a series of new Messages.
-- -- MessageRED is meant to be sent to the RED players only, for 10 seconds, and is classified as "End of Mission", with ID "Win". -- -- MessageAll is meant to be sent to all players, for 25 seconds, and is classified as "Score".
-- -- MessageClient1 is meant to be sent to a Client, for 25 seconds, and is classified as "Score", with ID "Score". -- -- MessageRED is meant to be sent to the RED players only, for 10 seconds, and is classified as "End of Mission", with ID "Win".
-- -- MessageClient1 is meant to be sent to a Client, for 25 seconds, and is classified as "Score", with ID "Score". -- -- MessageClient1 is meant to be sent to a Client, for 25 seconds, and is classified as "Score", with ID "Score".
-- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", 25, "End of Mission" ) -- -- MessageClient1 is meant to be sent to a Client, for 25 seconds, and is classified as "Score", with ID "Score".
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", 25, "Penalty" ) -- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", 25, "End of Mission" )
-- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", 25, "Score" ) -- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", 25, "Penalty" )
-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", 25, "Score") -- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", 25, "Score" )
-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", 25, "Score")
--
function MESSAGE:New( MessageText, MessageDuration, MessageCategory, ClearScreen ) function MESSAGE:New( MessageText, MessageDuration, MessageCategory, ClearScreen )
local self = BASE:Inherit( self, BASE:New() ) local self = BASE:Inherit( self, BASE:New() )
self:F( { MessageText, MessageDuration, MessageCategory } ) self:F( { MessageText, MessageDuration, MessageCategory } )
self.MessageType = nil self.MessageType = nil
-- When no MessageCategory is given, we don't show it as a title... -- When no MessageCategory is given, we don't show it as a title...
if MessageCategory and MessageCategory ~= "" then if MessageCategory and MessageCategory ~= "" then
if MessageCategory:sub(-1) ~= "\n" then if MessageCategory:sub( -1 ) ~= "\n" then
self.MessageCategory = MessageCategory .. ": " self.MessageCategory = MessageCategory .. ": "
else else
self.MessageCategory = MessageCategory:sub( 1, -2 ) .. ":\n" self.MessageCategory = MessageCategory:sub( 1, -2 ) .. ":\n"
end end
else else
self.MessageCategory = "" self.MessageCategory = ""
end end
self.ClearScreen=false self.ClearScreen = false
if ClearScreen~=nil then if ClearScreen ~= nil then
self.ClearScreen=ClearScreen self.ClearScreen = ClearScreen
end end
self.MessageDuration = MessageDuration or 5 self.MessageDuration = MessageDuration or 5
self.MessageTime = timer.getTime() self.MessageTime = timer.getTime()
self.MessageText = MessageText:gsub("^\n","",1):gsub("\n$","",1) self.MessageText = MessageText:gsub( "^\n", "", 1 ):gsub( "\n$", "", 1 )
self.MessageSent = false
self.MessageGroup = false
self.MessageCoalition = false
return self self.MessageSent = false
self.MessageGroup = false
self.MessageCoalition = false
return self
end end
--- Creates a new MESSAGE object of a certain type.
--- Creates a new MESSAGE object of a certain type. -- Note that these MESSAGE objects are not yet displayed on the display panel.
-- Note that these MESSAGE objects are not yet displayed on the display panel.
-- You must use the functions @{ToClient} or @{ToCoalition} or @{ToAll} to send these Messages to the respective recipients. -- You must use the functions @{ToClient} or @{ToCoalition} or @{ToAll} to send these Messages to the respective recipients.
-- The message display times are automatically defined based on the timing settings in the @{Settings} menu. -- The message display times are automatically defined based on the timing settings in the @{Settings} menu.
-- @param self -- @param self
@@ -134,83 +133,83 @@ end
-- @param #boolean ClearScreen (optional) Clear all previous messages. -- @param #boolean ClearScreen (optional) Clear all previous messages.
-- @return #MESSAGE -- @return #MESSAGE
-- @usage -- @usage
--
-- MessageAll = MESSAGE:NewType( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", MESSAGE.Type.Information ) -- MessageAll = MESSAGE:NewType( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", MESSAGE.Type.Information )
-- MessageRED = MESSAGE:NewType( "To the RED Players: You receive a penalty because you've killed one of your own units", MESSAGE.Type.Information ) -- MessageRED = MESSAGE:NewType( "To the RED Players: You receive a penalty because you've killed one of your own units", MESSAGE.Type.Information )
-- MessageClient1 = MESSAGE:NewType( "Congratulations, you've just hit a target", MESSAGE.Type.Update ) -- MessageClient1 = MESSAGE:NewType( "Congratulations, you've just hit a target", MESSAGE.Type.Update )
-- MessageClient2 = MESSAGE:NewType( "Congratulations, you've just killed a target", MESSAGE.Type.Update ) -- MessageClient2 = MESSAGE:NewType( "Congratulations, you've just killed a target", MESSAGE.Type.Update )
--
function MESSAGE:NewType( MessageText, MessageType, ClearScreen ) function MESSAGE:NewType( MessageText, MessageType, ClearScreen )
local self = BASE:Inherit( self, BASE:New() ) local self = BASE:Inherit( self, BASE:New() )
self:F( { MessageText } ) self:F( { MessageText } )
self.MessageType = MessageType self.MessageType = MessageType
self.ClearScreen=false self.ClearScreen = false
if ClearScreen~=nil then if ClearScreen ~= nil then
self.ClearScreen=ClearScreen self.ClearScreen = ClearScreen
end end
self.MessageTime = timer.getTime() self.MessageTime = timer.getTime()
self.MessageText = MessageText:gsub("^\n","",1):gsub("\n$","",1) self.MessageText = MessageText:gsub( "^\n", "", 1 ):gsub( "\n$", "", 1 )
return self return self
end end
--- Clears all previous messages from the screen before the new message is displayed. Not that this must come before all functions starting with ToX(), e.g. ToAll(), ToGroup() etc.
--- Clears all previous messages from the screen before the new message is displayed. Not that this must come before all functions starting with ToX(), e.g. ToAll(), ToGroup() etc.
-- @param #MESSAGE self -- @param #MESSAGE self
-- @return #MESSAGE -- @return #MESSAGE
function MESSAGE:Clear() function MESSAGE:Clear()
self:F() self:F()
self.ClearScreen=true self.ClearScreen = true
return self return self
end end
--- Sends a MESSAGE to a Client Group. Note that the Group needs to be defined within the ME with the skillset "Client" or "Player". --- Sends a MESSAGE to a Client Group. Note that the Group needs to be defined within the ME with the skillset "Client" or "Player".
-- @param #MESSAGE self -- @param #MESSAGE self
-- @param Wrapper.Client#CLIENT Client is the Group of the Client. -- @param Wrapper.Client#CLIENT Client is the Group of the Client.
-- @param Core.Settings#SETTINGS Settings Settings used to display the message. -- @param Core.Settings#SETTINGS Settings Settings used to display the message.
-- @return #MESSAGE -- @return #MESSAGE
-- @usage -- @usage
-- -- Send the 2 messages created with the @{New} method to the Client Group.
-- -- Note that the Message of MessageClient2 is overwriting the Message of MessageClient1.
-- ClientGroup = Group.getByName( "ClientGroup" )
-- --
-- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" ):ToClient( ClientGroup ) -- -- Send the 2 messages created with the @{New} method to the Client Group.
-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" ):ToClient( ClientGroup ) -- -- Note that the Message of MessageClient2 is overwriting the Message of MessageClient1.
-- or -- ClientGroup = Group.getByName( "ClientGroup" )
-- MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" ):ToClient( ClientGroup ) --
-- MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" ):ToClient( ClientGroup ) -- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" ):ToClient( ClientGroup )
-- or -- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" ):ToClient( ClientGroup )
-- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" ) -- or
-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" ) -- MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" ):ToClient( ClientGroup )
-- MessageClient1:ToClient( ClientGroup ) -- MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" ):ToClient( ClientGroup )
-- MessageClient2:ToClient( ClientGroup ) -- or
-- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" )
-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" )
-- MessageClient1:ToClient( ClientGroup )
-- MessageClient2:ToClient( ClientGroup )
--
function MESSAGE:ToClient( Client, Settings ) function MESSAGE:ToClient( Client, Settings )
self:F( Client ) self:F( Client )
if Client and Client:GetClientGroupID() then if Client and Client:GetClientGroupID() then
if self.MessageType then if self.MessageType then
local Settings = Settings or ( Client and _DATABASE:GetPlayerSettings( Client:GetPlayerName() ) ) or _SETTINGS -- Core.Settings#SETTINGS local Settings = Settings or (Client and _DATABASE:GetPlayerSettings( Client:GetPlayerName() )) or _SETTINGS -- Core.Settings#SETTINGS
self.MessageDuration = Settings:GetMessageTime( self.MessageType ) self.MessageDuration = Settings:GetMessageTime( self.MessageType )
self.MessageCategory = "" -- self.MessageType .. ": " self.MessageCategory = "" -- self.MessageType .. ": "
end end
if self.MessageDuration ~= 0 then if self.MessageDuration ~= 0 then
local ClientGroupID = Client:GetClientGroupID() local ClientGroupID = Client:GetClientGroupID()
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration ) self:T( self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ) .. " / " .. self.MessageDuration )
trigger.action.outTextForGroup( ClientGroupID, self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration , self.ClearScreen) trigger.action.outTextForGroup( ClientGroupID, self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ), self.MessageDuration, self.ClearScreen )
end end
end end
return self return self
end end
--- Sends a MESSAGE to a Group. --- Sends a MESSAGE to a Group.
-- @param #MESSAGE self -- @param #MESSAGE self
-- @param Wrapper.Group#GROUP Group to which the message is displayed. -- @param Wrapper.Group#GROUP Group to which the message is displayed.
-- @return #MESSAGE Message object. -- @return #MESSAGE Message object.
@@ -218,74 +217,80 @@ function MESSAGE:ToGroup( Group, Settings )
self:F( Group.GroupName ) self:F( Group.GroupName )
if Group then if Group then
if self.MessageType then if self.MessageType then
local Settings = Settings or ( Group and _DATABASE:GetPlayerSettings( Group:GetPlayerName() ) ) or _SETTINGS -- Core.Settings#SETTINGS local Settings = Settings or (Group and _DATABASE:GetPlayerSettings( Group:GetPlayerName() )) or _SETTINGS -- Core.Settings#SETTINGS
self.MessageDuration = Settings:GetMessageTime( self.MessageType ) self.MessageDuration = Settings:GetMessageTime( self.MessageType )
self.MessageCategory = "" -- self.MessageType .. ": " self.MessageCategory = "" -- self.MessageType .. ": "
end end
if self.MessageDuration ~= 0 then if self.MessageDuration ~= 0 then
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration ) self:T( self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ) .. " / " .. self.MessageDuration )
trigger.action.outTextForGroup( Group:GetID(), self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration, self.ClearScreen ) trigger.action.outTextForGroup( Group:GetID(), self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ), self.MessageDuration, self.ClearScreen )
end end
end end
return self return self
end end
--- Sends a MESSAGE to the Blue coalition. --- 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 -- @param #MESSAGE self
-- @return #MESSAGE -- @return #MESSAGE
-- @usage -- @usage
-- -- Send a message created with the @{New} method to the RED coalition. --
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToRed() -- -- Send a message created with the @{New} method to the BLUE coalition.
-- or -- MessageBLUE = MESSAGE:New( "To the BLUE Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToBlue()
-- MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToRed() -- or
-- or -- MESSAGE:New( "To the BLUE Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToBlue()
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ) -- or
-- MessageRED:ToRed() -- MessageBLUE = MESSAGE:New( "To the BLUE Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" )
function MESSAGE:ToRed( ) -- MessageBLUE:ToBlue()
self:F() --
function MESSAGE:ToBlue()
self:F()
self:ToCoalition( coalition.side.RED ) self:ToCoalition( coalition.side.BLUE )
return self return self
end end
--- Sends a MESSAGE to a Coalition. --- Sends a MESSAGE to the Red Coalition.
-- @param #MESSAGE self
-- @return #MESSAGE
-- @usage
--
-- -- Send a message created with the @{New} method to the RED coalition.
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToRed()
-- or
-- MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToRed()
-- or
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" )
-- MessageRED:ToRed()
--
function MESSAGE:ToRed()
self:F()
self:ToCoalition( coalition.side.RED )
return self
end
--- Sends a MESSAGE to a Coalition.
-- @param #MESSAGE self -- @param #MESSAGE self
-- @param #DCS.coalition.side CoalitionSide @{#DCS.coalition.side} to which the message is displayed. -- @param #DCS.coalition.side CoalitionSide @{#DCS.coalition.side} to which the message is displayed.
-- @param Core.Settings#SETTINGS Settings (Optional) Settings for message display. -- @param Core.Settings#SETTINGS Settings (Optional) Settings for message display.
-- @return #MESSAGE Message object. -- @return #MESSAGE Message object.
-- @usage -- @usage
-- -- Send a message created with the @{New} method to the RED coalition. --
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToCoalition( coalition.side.RED ) -- -- Send a message created with the @{New} method to the RED coalition.
-- or -- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToCoalition( coalition.side.RED )
-- MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToCoalition( coalition.side.RED ) -- or
-- or -- MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToCoalition( coalition.side.RED )
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ) -- or
-- MessageRED:ToCoalition( coalition.side.RED ) -- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" )
-- MessageRED:ToCoalition( coalition.side.RED )
--
function MESSAGE:ToCoalition( CoalitionSide, Settings ) function MESSAGE:ToCoalition( CoalitionSide, Settings )
self:F( CoalitionSide ) self:F( CoalitionSide )
if self.MessageType then if self.MessageType then
local Settings = Settings or _SETTINGS -- Core.Settings#SETTINGS local Settings = Settings or _SETTINGS -- Core.Settings#SETTINGS
@@ -293,20 +298,20 @@ function MESSAGE:ToCoalition( CoalitionSide, Settings )
self.MessageCategory = "" -- self.MessageType .. ": " self.MessageCategory = "" -- self.MessageType .. ": "
end end
if CoalitionSide then if CoalitionSide then
if self.MessageDuration ~= 0 then if self.MessageDuration ~= 0 then
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration ) self:T( self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ) .. " / " .. self.MessageDuration )
trigger.action.outTextForCoalition( CoalitionSide, self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration, self.ClearScreen ) trigger.action.outTextForCoalition( CoalitionSide, self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ), self.MessageDuration, self.ClearScreen )
end end
end end
return self return self
end end
--- Sends a MESSAGE to a Coalition if the given Condition is true. --- Sends a MESSAGE to a Coalition if the given Condition is true.
-- @param #MESSAGE self -- @param #MESSAGE self
-- @param CoalitionSide needs to be filled out by the defined structure of the standard scripting engine @{coalition.side}. -- @param CoalitionSide needs to be filled out by the defined structure of the standard scripting engine @{coalition.side}.
-- @param #boolean Condition Sends the message only if the condition is true. -- @param #boolean Condition Sends the message only if the condition is true.
-- @return #MESSAGE self -- @return #MESSAGE self
function MESSAGE:ToCoalitionIf( CoalitionSide, Condition ) function MESSAGE:ToCoalitionIf( CoalitionSide, Condition )
self:F( CoalitionSide ) self:F( CoalitionSide )
@@ -314,7 +319,7 @@ function MESSAGE:ToCoalitionIf( CoalitionSide, Condition )
if Condition and Condition == true then if Condition and Condition == true then
self:ToCoalition( CoalitionSide ) self:ToCoalition( CoalitionSide )
end end
return self return self
end end
@@ -323,14 +328,16 @@ end
-- @param Core.Settings#Settings Settings (Optional) Settings for message display. -- @param Core.Settings#Settings Settings (Optional) Settings for message display.
-- @return #MESSAGE -- @return #MESSAGE
-- @usage -- @usage
-- -- Send a message created to all players. --
-- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" ):ToAll() -- -- Send a message created to all players.
-- or -- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" ):ToAll()
-- MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" ):ToAll() -- or
-- or -- MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" ):ToAll()
-- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" ) -- or
-- MessageAll:ToAll() -- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" )
function MESSAGE:ToAll(Settings) -- MessageAll:ToAll()
--
function MESSAGE:ToAll( Settings )
self:F() self:F()
if self.MessageType then if self.MessageType then
@@ -340,14 +347,13 @@ function MESSAGE:ToAll(Settings)
end end
if self.MessageDuration ~= 0 then if self.MessageDuration ~= 0 then
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration ) self:T( self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ) .. " / " .. self.MessageDuration )
trigger.action.outText( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration, self.ClearScreen ) trigger.action.outText( self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ), self.MessageDuration, self.ClearScreen )
end end
return self return self
end end
--- Sends a MESSAGE to all players if the given Condition is true. --- Sends a MESSAGE to all players if the given Condition is true.
-- @param #MESSAGE self -- @param #MESSAGE self
-- @return #MESSAGE -- @return #MESSAGE
@@ -357,5 +363,5 @@ function MESSAGE:ToAllIf( Condition )
self:ToAll() self:ToAll()
end end
return self return self
end end

File diff suppressed because it is too large Load Diff

View File

@@ -1,13 +1,13 @@
--- **Core** - Provides a handy means to create messages and reports. --- **Core** - Provides a handy means to create messages and reports.
-- --
-- === -- ===
-- --
-- ## Features: -- ## Features:
-- --
-- * Create text blocks that are formatted. -- * Create text blocks that are formatted.
-- * Create automatic indents. -- * Create automatic indents.
-- * Variate the delimiters between reporting lines. -- * Variate the delimiters between reporting lines.
-- --
-- === -- ===
-- --
-- ### Authors: FlightControl : Design & Programming -- ### Authors: FlightControl : Design & Programming
@@ -15,7 +15,6 @@
-- @module Core.Report -- @module Core.Report
-- @image Core_Report.JPG -- @image Core_Report.JPG
--- @type REPORT --- @type REPORT
-- @extends Core.Base#BASE -- @extends Core.Base#BASE
@@ -36,7 +35,7 @@ function REPORT:New( Title )
self.Report = {} self.Report = {}
self:SetTitle( Title or "" ) self:SetTitle( Title or "" )
self:SetIndent( 3 ) self:SetIndent( 3 )
return self return self
@@ -45,28 +44,26 @@ end
--- Has the REPORT Text? --- Has the REPORT Text?
-- @param #REPORT self -- @param #REPORT self
-- @return #boolean -- @return #boolean
function REPORT:HasText() --R2.1 function REPORT:HasText() -- R2.1
return #self.Report > 0 return #self.Report > 0
end end
--- Set indent of a REPORT. --- Set indent of a REPORT.
-- @param #REPORT self -- @param #REPORT self
-- @param #number Indent -- @param #number Indent
-- @return #REPORT -- @return #REPORT
function REPORT:SetIndent( Indent ) --R2.1 function REPORT:SetIndent( Indent ) -- R2.1
self.Indent = Indent self.Indent = Indent
return self return self
end end
--- Add a new line to a REPORT. --- Add a new line to a REPORT.
-- @param #REPORT self -- @param #REPORT self
-- @param #string Text -- @param #string Text
-- @return #REPORT -- @return #REPORT
function REPORT:Add( Text ) function REPORT:Add( Text )
self.Report[#self.Report+1] = Text self.Report[#self.Report + 1] = Text
return self return self
end end
@@ -76,17 +73,17 @@ end
-- @param #string Separator (optional) The start of each report line can begin with an optional separator character. This can be a "-", or "#", or "*". You're free to choose what you find the best. -- @param #string Separator (optional) The start of each report line can begin with an optional separator character. This can be a "-", or "#", or "*". You're free to choose what you find the best.
-- @return #REPORT -- @return #REPORT
function REPORT:AddIndent( Text, Separator ) function REPORT:AddIndent( Text, Separator )
self.Report[#self.Report+1] = ( ( Separator and Separator .. string.rep( " ", self.Indent - 1 ) ) or string.rep(" ", self.Indent ) ) .. Text:gsub("\n","\n"..string.rep( " ", self.Indent ) ) self.Report[#self.Report + 1] = ((Separator and Separator .. string.rep( " ", self.Indent - 1 )) or string.rep( " ", self.Indent )) .. Text:gsub( "\n", "\n" .. string.rep( " ", self.Indent ) )
return self return self
end end
--- Produces the text of the report, taking into account an optional delimeter, which is \n by default. --- Produces the text of the report, taking into account an optional delimiter, which is \n by default.
-- @param #REPORT self -- @param #REPORT self
-- @param #string Delimiter (optional) A delimiter text. -- @param #string Delimiter (optional) A delimiter text.
-- @return #string The report text. -- @return #string The report text.
function REPORT:Text( Delimiter ) function REPORT:Text( Delimiter )
Delimiter = Delimiter or "\n" Delimiter = Delimiter or "\n"
local ReportText = ( self.Title ~= "" and self.Title .. Delimiter or self.Title ) .. table.concat( self.Report, Delimiter ) or "" local ReportText = (self.Title ~= "" and self.Title .. Delimiter or self.Title) .. table.concat( self.Report, Delimiter ) or ""
return ReportText return ReportText
end end
@@ -95,7 +92,7 @@ end
-- @param #string Title The title of the report. -- @param #string Title The title of the report.
-- @return #REPORT -- @return #REPORT
function REPORT:SetTitle( Title ) function REPORT:SetTitle( Title )
self.Title = Title self.Title = Title
return self return self
end end

View File

@@ -1,36 +1,36 @@
--- **Core** -- SCHEDULEDISPATCHER dispatches the different schedules. --- **Core** -- SCHEDULEDISPATCHER dispatches the different schedules.
-- --
-- === -- ===
-- --
-- Takes care of the creation and dispatching of scheduled functions for SCHEDULER objects. -- Takes care of the creation and dispatching of scheduled functions for SCHEDULER objects.
-- --
-- This class is tricky and needs some thorough explanation. -- This class is tricky and needs some thorough explanation.
-- SCHEDULE classes are used to schedule functions for objects, or as persistent objects. -- SCHEDULE classes are used to schedule functions for objects, or as persistent objects.
-- The SCHEDULEDISPATCHER class ensures that: -- The SCHEDULEDISPATCHER class ensures that:
-- --
-- - Scheduled functions are planned according the SCHEDULER object parameters. -- - Scheduled functions are planned according the SCHEDULER object parameters.
-- - Scheduled functions are repeated when requested, according the SCHEDULER object parameters. -- - Scheduled functions are repeated when requested, according the SCHEDULER object parameters.
-- - Scheduled functions are automatically removed when the schedule is finished, according the SCHEDULER object parameters. -- - Scheduled functions are automatically removed when the schedule is finished, according the SCHEDULER object parameters.
-- --
-- The SCHEDULEDISPATCHER class will manage SCHEDULER object in memory during garbage collection: -- The SCHEDULEDISPATCHER class will manage SCHEDULER object in memory during garbage collection:
-- --
-- - When a SCHEDULER object is not attached to another object (that is, it's first :Schedule() parameter is nil), then the SCHEDULER object is _persistent_ within memory. -- - When a SCHEDULER object is not attached to another object (that is, it's first :Schedule() parameter is nil), then the SCHEDULER object is _persistent_ within memory.
-- - When a SCHEDULER object *is* attached to another object, then the SCHEDULER object is _not persistent_ within memory after a garbage collection! -- - When a SCHEDULER object *is* attached to another object, then the SCHEDULER object is _not persistent_ within memory after a garbage collection!
-- --
-- The none persistency of SCHEDULERS attached to objects is required to allow SCHEDULER objects to be garbage collectged, when the parent object is also desroyed or nillified and garbage collected. -- The non-persistency of SCHEDULERS attached to objects is required to allow SCHEDULER objects to be garbage collected when the parent object is destroyed, or set to nil and garbage collected.
-- Even when there are pending timer scheduled functions to be executed for the SCHEDULER object, -- Even when there are pending timer scheduled functions to be executed for the SCHEDULER object,
-- these will not be executed anymore when the SCHEDULER object has been destroyed. -- these will not be executed anymore when the SCHEDULER object has been destroyed.
-- --
-- The SCHEDULEDISPATCHER allows multiple scheduled functions to be planned and executed for one SCHEDULER object. -- The SCHEDULEDISPATCHER allows multiple scheduled functions to be planned and executed for one SCHEDULER object.
-- The SCHEDULER object therefore keeps a table of "CallID's", which are returned after each planning of a new scheduled function by the SCHEDULEDISPATCHER. -- The SCHEDULER object therefore keeps a table of "CallID's", which are returned after each planning of a new scheduled function by the SCHEDULEDISPATCHER.
-- The SCHEDULER object plans new scheduled functions through the @{Core.Scheduler#SCHEDULER.Schedule}() method. -- The SCHEDULER object plans new scheduled functions through the @{Core.Scheduler#SCHEDULER.Schedule}() method.
-- The Schedule() method returns the CallID that is the reference ID for each planned schedule. -- The Schedule() method returns the CallID that is the reference ID for each planned schedule.
-- --
-- === -- ===
-- --
-- ### Contributions: - -- ### Contributions: -
-- ### Authors: FlightControl : Design & Programming -- ### Authors: FlightControl : Design & Programming
-- --
-- @module Core.ScheduleDispatcher -- @module Core.ScheduleDispatcher
-- @image Core_Schedule_Dispatcher.JPG -- @image Core_Schedule_Dispatcher.JPG
@@ -38,7 +38,7 @@
-- @type SCHEDULEDISPATCHER -- @type SCHEDULEDISPATCHER
-- @field #string ClassName Name of the class. -- @field #string ClassName Name of the class.
-- @field #number CallID Call ID counter. -- @field #number CallID Call ID counter.
-- @field #table PersistentSchedulers Persistant schedulers. -- @field #table PersistentSchedulers Persistent schedulers.
-- @field #table ObjectSchedulers Schedulers that only exist as long as the master object exists. -- @field #table ObjectSchedulers Schedulers that only exist as long as the master object exists.
-- @field #table Schedule Meta table setmetatable( {}, { __mode = "k" } ). -- @field #table Schedule Meta table setmetatable( {}, { __mode = "k" } ).
-- @extends Core.Base#BASE -- @extends Core.Base#BASE
@@ -46,11 +46,11 @@
--- The SCHEDULEDISPATCHER structure --- The SCHEDULEDISPATCHER structure
-- @type SCHEDULEDISPATCHER -- @type SCHEDULEDISPATCHER
SCHEDULEDISPATCHER = { SCHEDULEDISPATCHER = {
ClassName = "SCHEDULEDISPATCHER", ClassName = "SCHEDULEDISPATCHER",
CallID = 0, CallID = 0,
PersistentSchedulers = {}, PersistentSchedulers = {},
ObjectSchedulers = {}, ObjectSchedulers = {},
Schedule = nil, Schedule = nil,
} }
--- Player data table holding all important parameters of each player. --- Player data table holding all important parameters of each player.
@@ -58,7 +58,7 @@ SCHEDULEDISPATCHER = {
-- @field #function Function The schedule function to be called. -- @field #function Function The schedule function to be called.
-- @field #table Arguments Schedule function arguments. -- @field #table Arguments Schedule function arguments.
-- @field #number Start Start time in seconds. -- @field #number Start Start time in seconds.
-- @field #number Repeat Repeat time intervall in seconds. -- @field #number Repeat Repeat time interval in seconds.
-- @field #number Randomize Randomization factor [0,1]. -- @field #number Randomize Randomization factor [0,1].
-- @field #number Stop Stop time in seconds. -- @field #number Stop Stop time in seconds.
-- @field #number StartTime Time in seconds when the scheduler is created. -- @field #number StartTime Time in seconds when the scheduler is created.
@@ -77,7 +77,7 @@ end
--- Add a Schedule to the ScheduleDispatcher. --- Add a Schedule to the ScheduleDispatcher.
-- The development of this method was really tidy. -- The development of this method was really tidy.
-- It is constructed as such that a garbage collection is executed on the weak tables, when the Scheduler is nillified. -- It is constructed as such that a garbage collection is executed on the weak tables, when the Scheduler is set to nil.
-- Nothing of this code should be modified without testing it thoroughly. -- Nothing of this code should be modified without testing it thoroughly.
-- @param #SCHEDULEDISPATCHER self -- @param #SCHEDULEDISPATCHER self
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object. -- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
@@ -85,7 +85,7 @@ end
-- @param #table ScheduleArguments Table of arguments passed to the ScheduleFunction. -- @param #table ScheduleArguments Table of arguments passed to the ScheduleFunction.
-- @param #number Start Start time in seconds. -- @param #number Start Start time in seconds.
-- @param #number Repeat Repeat interval in seconds. -- @param #number Repeat Repeat interval in seconds.
-- @param #number Randomize Radomization factor [0,1]. -- @param #number Randomize Randomization factor [0,1].
-- @param #number Stop Stop time in seconds. -- @param #number Stop Stop time in seconds.
-- @param #number TraceLevel Trace level [0,3]. -- @param #number TraceLevel Trace level [0,3].
-- @param Core.Fsm#FSM Fsm Finite state model. -- @param Core.Fsm#FSM Fsm Finite state model.
@@ -95,39 +95,38 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
-- Increase counter. -- Increase counter.
self.CallID = self.CallID + 1 self.CallID = self.CallID + 1
-- Create ID. -- Create ID.
local CallID = self.CallID .. "#" .. ( Scheduler.MasterObject and Scheduler.MasterObject.GetClassNameAndID and Scheduler.MasterObject:GetClassNameAndID() or "" ) or "" local CallID = self.CallID .. "#" .. (Scheduler.MasterObject and Scheduler.MasterObject.GetClassNameAndID and Scheduler.MasterObject:GetClassNameAndID() or "") or ""
self:T2(string.format("Adding schedule #%d CallID=%s", self.CallID, CallID)) self:T2( string.format( "Adding schedule #%d CallID=%s", self.CallID, CallID ) )
-- Initialize PersistentSchedulers -- Initialize PersistentSchedulers
self.PersistentSchedulers = self.PersistentSchedulers or {} self.PersistentSchedulers = self.PersistentSchedulers or {}
-- Initialize the ObjectSchedulers array, which is a weakly coupled table. -- Initialize the ObjectSchedulers array, which is a weakly coupled table.
-- If the object used as the key is nil, then the garbage collector will remove the item from the Functions array. -- If the object used as the key is nil, then the garbage collector will remove the item from the Functions array.
self.ObjectSchedulers = self.ObjectSchedulers or setmetatable( {}, { __mode = "v" } ) self.ObjectSchedulers = self.ObjectSchedulers or setmetatable( {}, { __mode = "v" } )
if Scheduler.MasterObject then if Scheduler.MasterObject then
self.ObjectSchedulers[CallID] = Scheduler self.ObjectSchedulers[CallID] = Scheduler
self:F3( { CallID = CallID, ObjectScheduler = tostring(self.ObjectSchedulers[CallID]), MasterObject = tostring(Scheduler.MasterObject) } ) self:F3( { CallID = CallID, ObjectScheduler = tostring( self.ObjectSchedulers[CallID] ), MasterObject = tostring( Scheduler.MasterObject ) } )
else else
self.PersistentSchedulers[CallID] = Scheduler self.PersistentSchedulers[CallID] = Scheduler
self:F3( { CallID = CallID, PersistentScheduler = self.PersistentSchedulers[CallID] } ) self:F3( { CallID = CallID, PersistentScheduler = self.PersistentSchedulers[CallID] } )
end end
self.Schedule = self.Schedule or setmetatable( {}, { __mode = "k" } ) self.Schedule = self.Schedule or setmetatable( {}, { __mode = "k" } )
self.Schedule[Scheduler] = self.Schedule[Scheduler] or {} self.Schedule[Scheduler] = self.Schedule[Scheduler] or {}
self.Schedule[Scheduler][CallID] = {} --#SCHEDULEDISPATCHER.ScheduleData self.Schedule[Scheduler][CallID] = {} -- #SCHEDULEDISPATCHER.ScheduleData
self.Schedule[Scheduler][CallID].Function = ScheduleFunction self.Schedule[Scheduler][CallID].Function = ScheduleFunction
self.Schedule[Scheduler][CallID].Arguments = ScheduleArguments self.Schedule[Scheduler][CallID].Arguments = ScheduleArguments
self.Schedule[Scheduler][CallID].StartTime = timer.getTime() + ( Start or 0 ) self.Schedule[Scheduler][CallID].StartTime = timer.getTime() + (Start or 0)
self.Schedule[Scheduler][CallID].Start = Start + 0.1 self.Schedule[Scheduler][CallID].Start = Start + 0.1
self.Schedule[Scheduler][CallID].Repeat = Repeat or 0 self.Schedule[Scheduler][CallID].Repeat = Repeat or 0
self.Schedule[Scheduler][CallID].Randomize = Randomize or 0 self.Schedule[Scheduler][CallID].Randomize = Randomize or 0
self.Schedule[Scheduler][CallID].Stop = Stop self.Schedule[Scheduler][CallID].Stop = Stop
-- This section handles the tracing of the scheduled calls. -- This section handles the tracing of the scheduled calls.
-- Because these calls will be executed with a delay, we inspect the place where these scheduled calls are initiated. -- Because these calls will be executed with a delay, we inspect the place where these scheduled calls are initiated.
-- The Info structure contains the output of the debug.getinfo() calls, which inspects the call stack for the function name, line number and source name. -- The Info structure contains the output of the debug.getinfo() calls, which inspects the call stack for the function name, line number and source name.
@@ -149,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. -- Therefore, in the call stack, at the TraceLevel these functions are mentioned as "tail calls", and the Info.name field will be nil as a result.
-- To obtain the correct function name for FSM object calls, the function is mentioned in the call stack at a higher stack level. -- To obtain the correct function name for FSM object calls, the function is mentioned in the call stack at a higher stack level.
-- So when function name stored in Info.name is nil, then I inspect the function name within the call stack one level higher. -- So when function name stored in Info.name is nil, then I inspect the function name within the call stack one level higher.
-- So this little piece of code does its magic wonderfully, preformance overhead is neglectible, as scheduled calls don't happen that often. -- So this little piece of code does its magic wonderfully, performance overhead is negligible, as scheduled calls don't happen that often.
local Info = {} local Info = {}
if debug then if debug then
TraceLevel = TraceLevel or 2 TraceLevel = TraceLevel or 2
Info = debug.getinfo( TraceLevel, "nlS" ) Info = debug.getinfo( TraceLevel, "nlS" )
@@ -166,7 +165,7 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
--- Function passed to the DCS timer.scheduleFunction() --- Function passed to the DCS timer.scheduleFunction()
self.Schedule[Scheduler][CallID].CallHandler = function( Params ) self.Schedule[Scheduler][CallID].CallHandler = function( Params )
local CallID = Params.CallID local CallID = Params.CallID
local Info = Params.Info or {} local Info = Params.Info or {}
local Source = Info.source or "?" local Source = Info.source or "?"
@@ -180,27 +179,27 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
end end
return errmsg return errmsg
end end
-- Get object or persistant scheduler object. -- Get object or persistent scheduler object.
local Scheduler = self.ObjectSchedulers[CallID] --Core.Scheduler#SCHEDULER local Scheduler = self.ObjectSchedulers[CallID] -- Core.Scheduler#SCHEDULER
if not Scheduler then if not Scheduler then
Scheduler = self.PersistentSchedulers[CallID] Scheduler = self.PersistentSchedulers[CallID]
end end
--self:T3( { Scheduler = Scheduler } ) -- self:T3( { Scheduler = Scheduler } )
if Scheduler then if Scheduler then
local MasterObject = tostring(Scheduler.MasterObject) local MasterObject = tostring( Scheduler.MasterObject )
-- Schedule object.
local Schedule = self.Schedule[Scheduler][CallID] --#SCHEDULEDISPATCHER.ScheduleData
--self:T3( { Schedule = Schedule } )
local SchedulerObject = Scheduler.MasterObject --Scheduler.SchedulerObject Now is this the Maste or Scheduler object? -- Schedule object.
local Schedule = self.Schedule[Scheduler][CallID] -- #SCHEDULEDISPATCHER.ScheduleData
-- self:T3( { Schedule = Schedule } )
local SchedulerObject = Scheduler.MasterObject -- Scheduler.SchedulerObject Now is this the Master or Scheduler object?
local ShowTrace = Scheduler.ShowTrace local ShowTrace = Scheduler.ShowTrace
local ScheduleFunction = Schedule.Function local ScheduleFunction = Schedule.Function
local ScheduleArguments = Schedule.Arguments or {} local ScheduleArguments = Schedule.Arguments or {}
local Start = Schedule.Start local Start = Schedule.Start
@@ -208,18 +207,17 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
local Randomize = Schedule.Randomize or 0 local Randomize = Schedule.Randomize or 0
local Stop = Schedule.Stop or 0 local Stop = Schedule.Stop or 0
local ScheduleID = Schedule.ScheduleID local ScheduleID = Schedule.ScheduleID
local Prefix = (Repeat == 0) and "--->" or "+++>"
local Prefix = ( Repeat == 0 ) and "--->" or "+++>"
local Status, Result local Status, Result
--self:E( { SchedulerObject = SchedulerObject } ) -- self:E( { SchedulerObject = SchedulerObject } )
if SchedulerObject then if SchedulerObject then
local function Timer() local function Timer()
if ShowTrace then if ShowTrace then
SchedulerObject:T( Prefix .. Name .. ":" .. Line .. " (" .. Source .. ")" ) SchedulerObject:T( Prefix .. Name .. ":" .. Line .. " (" .. Source .. ")" )
end end
return ScheduleFunction( SchedulerObject, unpack( ScheduleArguments ) ) return ScheduleFunction( SchedulerObject, unpack( ScheduleArguments ) )
end end
Status, Result = xpcall( Timer, ErrorHandler ) Status, Result = xpcall( Timer, ErrorHandler )
else else
@@ -227,40 +225,39 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
if ShowTrace then if ShowTrace then
self:T( Prefix .. Name .. ":" .. Line .. " (" .. Source .. ")" ) self:T( Prefix .. Name .. ":" .. Line .. " (" .. Source .. ")" )
end end
return ScheduleFunction( unpack( ScheduleArguments ) ) return ScheduleFunction( unpack( ScheduleArguments ) )
end end
Status, Result = xpcall( Timer, ErrorHandler ) Status, Result = xpcall( Timer, ErrorHandler )
end end
local CurrentTime = timer.getTime() local CurrentTime = timer.getTime()
local StartTime = Schedule.StartTime local StartTime = Schedule.StartTime
-- Debug info. -- Debug info.
self:F3( { CallID=CallID, ScheduleID=ScheduleID, Master = MasterObject, CurrentTime = CurrentTime, StartTime = StartTime, Start = Start, Repeat = Repeat, Randomize = Randomize, Stop = Stop } ) self:F3( { CallID = CallID, ScheduleID = ScheduleID, Master = MasterObject, CurrentTime = CurrentTime, StartTime = StartTime, Start = Start, Repeat = Repeat, Randomize = Randomize, Stop = Stop } )
if Status and ((Result == nil) or (Result and Result ~= false)) then
if Status and (( Result == nil ) or ( Result and Result ~= false ) ) then
if Repeat ~= 0 and ((Stop == 0) or (Stop ~= 0 and CurrentTime <= StartTime + Stop)) then
if Repeat ~= 0 and ( ( Stop == 0 ) or ( Stop ~= 0 and CurrentTime <= StartTime + Stop ) ) then local ScheduleTime = CurrentTime + Repeat + math.random( -(Randomize * Repeat / 2), (Randomize * Repeat / 2) ) + 0.0001 -- Accuracy
local ScheduleTime = CurrentTime + Repeat + math.random(- ( Randomize * Repeat / 2 ), ( Randomize * Repeat / 2 )) + 0.0001 -- Accuracy -- self:T3( { Repeat = CallID, CurrentTime, ScheduleTime, ScheduleArguments } )
--self:T3( { Repeat = CallID, CurrentTime, ScheduleTime, ScheduleArguments } )
return ScheduleTime -- returns the next time the function needs to be called. return ScheduleTime -- returns the next time the function needs to be called.
else else
self:Stop( Scheduler, CallID ) self:Stop( Scheduler, CallID )
end end
else else
self:Stop( Scheduler, CallID ) self:Stop( Scheduler, CallID )
end end
else else
self:I( "<<<>" .. Name .. ":" .. Line .. " (" .. Source .. ")" ) self:I( "<<<>" .. Name .. ":" .. Line .. " (" .. Source .. ")" )
end end
return nil return nil
end end
self:Start( Scheduler, CallID, Info ) self:Start( Scheduler, CallID, Info )
return CallID return CallID
end end
@@ -284,33 +281,33 @@ end
-- @param #string Info (Optional) Debug info. -- @param #string Info (Optional) Debug info.
function SCHEDULEDISPATCHER:Start( Scheduler, CallID, Info ) function SCHEDULEDISPATCHER:Start( Scheduler, CallID, Info )
self:F2( { Start = CallID, Scheduler = Scheduler } ) self:F2( { Start = CallID, Scheduler = Scheduler } )
if CallID then if CallID then
local Schedule = self.Schedule[Scheduler][CallID] --#SCHEDULEDISPATCHER.ScheduleData local Schedule = self.Schedule[Scheduler][CallID] -- #SCHEDULEDISPATCHER.ScheduleData
-- Only start when there is no ScheduleID defined! -- Only start when there is no ScheduleID defined!
-- This prevents to "Start" the scheduler twice with the same CallID... -- This prevents to "Start" the scheduler twice with the same CallID...
if not Schedule.ScheduleID then if not Schedule.ScheduleID then
-- Current time in seconds. -- Current time in seconds.
local Tnow=timer.getTime() local Tnow = timer.getTime()
Schedule.StartTime = Tnow -- Set the StartTime field to indicate when the scheduler started. Schedule.StartTime = Tnow -- Set the StartTime field to indicate when the scheduler started.
-- Start DCS schedule function https://wiki.hoggitworld.com/view/DCS_func_scheduleFunction -- Start DCS schedule function https://wiki.hoggitworld.com/view/DCS_func_scheduleFunction
Schedule.ScheduleID = timer.scheduleFunction(Schedule.CallHandler, { CallID = CallID, Info = Info }, Tnow + Schedule.Start) Schedule.ScheduleID = timer.scheduleFunction( Schedule.CallHandler, { CallID = CallID, Info = Info }, Tnow + Schedule.Start )
self:T(string.format("Starting scheduledispatcher Call ID=%s ==> Schedule ID=%s", tostring(CallID), tostring(Schedule.ScheduleID))) self:T( string.format( "Starting SCHEDULEDISPATCHER Call ID=%s ==> Schedule ID=%s", tostring( CallID ), tostring( Schedule.ScheduleID ) ) )
end end
else else
-- Recursive. -- Recursive.
for CallID, Schedule in pairs( self.Schedule[Scheduler] or {} ) do for CallID, Schedule in pairs( self.Schedule[Scheduler] or {} ) do
self:Start( Scheduler, CallID, Info ) -- Recursive self:Start( Scheduler, CallID, Info ) -- Recursive
end end
end end
end end
@@ -322,29 +319,29 @@ function SCHEDULEDISPATCHER:Stop( Scheduler, CallID )
self:F2( { Stop = CallID, Scheduler = Scheduler } ) self:F2( { Stop = CallID, Scheduler = Scheduler } )
if CallID then if CallID then
local Schedule = self.Schedule[Scheduler][CallID] --#SCHEDULEDISPATCHER.ScheduleData local Schedule = self.Schedule[Scheduler][CallID] -- #SCHEDULEDISPATCHER.ScheduleData
-- Only stop when there is a ScheduleID defined for the CallID. So, when the scheduler was stopped before, do nothing. -- Only stop when there is a ScheduleID defined for the CallID. So, when the scheduler was stopped before, do nothing.
if Schedule.ScheduleID then if Schedule.ScheduleID then
self:T(string.format("scheduledispatcher stopping scheduler CallID=%s, ScheduleID=%s", tostring(CallID), tostring(Schedule.ScheduleID))) self:T( string.format( "SCHEDULEDISPATCHER stopping scheduler CallID=%s, ScheduleID=%s", tostring( CallID ), tostring( Schedule.ScheduleID ) ) )
-- Remove schedule function https://wiki.hoggitworld.com/view/DCS_func_removeFunction -- Remove schedule function https://wiki.hoggitworld.com/view/DCS_func_removeFunction
timer.removeFunction(Schedule.ScheduleID) timer.removeFunction( Schedule.ScheduleID )
Schedule.ScheduleID = nil Schedule.ScheduleID = nil
else else
self:T(string.format("Error no ScheduleID for CallID=%s", tostring(CallID))) self:T( string.format( "Error no ScheduleID for CallID=%s", tostring( CallID ) ) )
end end
else else
for CallID, Schedule in pairs( self.Schedule[Scheduler] or {} ) do for CallID, Schedule in pairs( self.Schedule[Scheduler] or {} ) do
self:Stop( Scheduler, CallID ) -- Recursive self:Stop( Scheduler, CallID ) -- Recursive
end end
end end
end end
@@ -359,7 +356,7 @@ function SCHEDULEDISPATCHER:Clear( Scheduler )
end end
end end
--- Shopw tracing info. --- Show tracing info.
-- @param #SCHEDULEDISPATCHER self -- @param #SCHEDULEDISPATCHER self
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object. -- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
function SCHEDULEDISPATCHER:ShowTrace( Scheduler ) function SCHEDULEDISPATCHER:ShowTrace( Scheduler )

View File

@@ -1,41 +1,41 @@
--- **Core** - Prepares and handles the execution of functions over scheduled time (intervals). --- **Core** - Prepares and handles the execution of functions over scheduled time (intervals).
-- --
-- === -- ===
-- --
-- ## Features: -- ## Features:
-- --
-- * Schedule functions over time, -- * Schedule functions over time,
-- * optionally in an optional specified time interval, -- * optionally in an optional specified time interval,
-- * optionally **repeating** with a specified time repeat interval, -- * optionally **repeating** with a specified time repeat interval,
-- * optionally **randomizing** with a specified time interval randomization factor, -- * optionally **randomizing** with a specified time interval randomization factor,
-- * optionally **stop** the repeating after a specified time interval. -- * optionally **stop** the repeating after a specified time interval.
-- --
-- === -- ===
-- --
-- # Demo Missions -- # Demo Missions
-- --
-- ### [SCHEDULER Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/SCH%20-%20Scheduler) -- ### [SCHEDULER Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/SCH%20-%20Scheduler)
-- --
-- ### [SCHEDULER Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SCH%20-%20Scheduler) -- ### [SCHEDULER Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SCH%20-%20Scheduler)
-- --
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases) -- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
-- --
-- ===
--
-- # YouTube Channel
--
-- ### [SCHEDULER YouTube Channel (none)]()
--
-- === -- ===
-- --
-- ### Contributions: -- # YouTube Channel
-- --
-- ### [SCHEDULER YouTube Channel (none)]()
--
-- ===
--
-- ### Contributions:
--
-- * FlightControl : Concept & Testing -- * FlightControl : Concept & Testing
-- --
-- ### Authors: -- ### Authors:
-- --
-- * FlightControl : Design & Programming -- * FlightControl : Design & Programming
-- --
-- === -- ===
-- --
-- @module Core.Scheduler -- @module Core.Scheduler
@@ -48,62 +48,61 @@
-- @field #boolean ShowTrace Trace info if true. -- @field #boolean ShowTrace Trace info if true.
-- @extends Core.Base#BASE -- @extends Core.Base#BASE
--- Creates and handles schedules over time, which allow to execute code at specific time intervals with randomization. --- Creates and handles schedules over time, which allow to execute code at specific time intervals with randomization.
-- --
-- A SCHEDULER can manage **multiple** (repeating) schedules. Each planned or executing schedule has a unique **ScheduleID**. -- A SCHEDULER can manage **multiple** (repeating) schedules. Each planned or executing schedule has a unique **ScheduleID**.
-- The ScheduleID is returned when the method @{#SCHEDULER.Schedule}() is called. -- The ScheduleID is returned when the method @{#SCHEDULER.Schedule}() is called.
-- It is recommended to store the ScheduleID in a variable, as it is used in the methods @{SCHEDULER.Start}() and @{SCHEDULER.Stop}(), -- It is recommended to store the ScheduleID in a variable, as it is used in the methods @{SCHEDULER.Start}() and @{SCHEDULER.Stop}(),
-- which can start and stop specific repeating schedules respectively within a SCHEDULER object. -- which can start and stop specific repeating schedules respectively within a SCHEDULER object.
-- --
-- ## SCHEDULER constructor -- ## SCHEDULER constructor
-- --
-- The SCHEDULER class is quite easy to use, but note that the New constructor has variable parameters: -- The SCHEDULER class is quite easy to use, but note that the New constructor has variable parameters:
-- --
-- The @{#SCHEDULER.New}() method returns 2 variables: -- The @{#SCHEDULER.New}() method returns 2 variables:
-- --
-- 1. The SCHEDULER object reference. -- 1. The SCHEDULER object reference.
-- 2. The first schedule planned in the SCHEDULER object. -- 2. The first schedule planned in the SCHEDULER object.
-- --
-- To clarify the different appliances, lets have a look at the following examples: -- To clarify the different appliances, lets have a look at the following examples:
-- --
-- ### Construct a SCHEDULER object without a persistent schedule. -- ### Construct a SCHEDULER object without a persistent schedule.
-- --
-- * @{#SCHEDULER.New}( nil ): Setup a new SCHEDULER object, which is persistently executed after garbage collection. -- * @{#SCHEDULER.New}( nil ): Setup a new SCHEDULER object, which is persistently executed after garbage collection.
-- --
-- MasterObject = SCHEDULER:New() -- MasterObject = SCHEDULER:New()
-- SchedulerID = MasterObject:Schedule( nil, ScheduleFunction, {} ) -- SchedulerID = MasterObject:Schedule( nil, ScheduleFunction, {} )
-- --
-- The above example creates a new MasterObject, but does not schedule anything. -- The above example creates a new MasterObject, but does not schedule anything.
-- A separate schedule is created by using the MasterObject using the method :Schedule..., which returns a ScheduleID -- A separate schedule is created by using the MasterObject using the method :Schedule..., which returns a ScheduleID
-- --
-- ### Construct a SCHEDULER object without a volatile schedule, but volatile to the Object existence... -- ### Construct a SCHEDULER object without a volatile schedule, but volatile to the Object existence...
-- --
-- * @{#SCHEDULER.New}( Object ): Setup a new SCHEDULER object, which is linked to the Object. When the Object is nillified or destroyed, the SCHEDULER object will also be destroyed and stopped after garbage collection. -- * @{#SCHEDULER.New}( Object ): Setup a new SCHEDULER object, which is linked to the Object. When the Object is set to nil or destroyed, the SCHEDULER object will also be destroyed and stopped after garbage collection.
-- --
-- ZoneObject = ZONE:New( "ZoneName" ) -- ZoneObject = ZONE:New( "ZoneName" )
-- MasterObject = SCHEDULER:New( ZoneObject ) -- MasterObject = SCHEDULER:New( ZoneObject )
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {} ) -- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {} )
-- ... -- ...
-- ZoneObject = nil -- ZoneObject = nil
-- garbagecollect() -- garbagecollect()
-- --
-- The above example creates a new MasterObject, but does not schedule anything, and is bound to the existence of ZoneObject, which is a ZONE. -- The above example creates a new MasterObject, but does not schedule anything, and is bound to the existence of ZoneObject, which is a ZONE.
-- A separate schedule is created by using the MasterObject using the method :Schedule()..., which returns a ScheduleID -- A separate schedule is created by using the MasterObject using the method :Schedule()..., which returns a ScheduleID
-- Later in the logic, the ZoneObject is put to nil, and garbage is collected. -- Later in the logic, the ZoneObject is put to nil, and garbage is collected.
-- As a result, the MasterObject will cancel any planned schedule. -- As a result, the MasterObject will cancel any planned schedule.
-- --
-- ### Construct a SCHEDULER object with a persistent schedule. -- ### Construct a SCHEDULER object with a persistent schedule.
-- --
-- * @{#SCHEDULER.New}( nil, Function, FunctionArguments, Start, ... ): Setup a new persistent SCHEDULER object, and start a new schedule for the Function with the defined FunctionArguments according the Start and sequent parameters. -- * @{#SCHEDULER.New}( nil, Function, FunctionArguments, Start, ... ): Setup a new persistent SCHEDULER object, and start a new schedule for the Function with the defined FunctionArguments according the Start and sequent parameters.
-- --
-- MasterObject, SchedulerID = SCHEDULER:New( nil, ScheduleFunction, {} ) -- MasterObject, SchedulerID = SCHEDULER:New( nil, ScheduleFunction, {} )
-- --
-- The above example creates a new MasterObject, and does schedule the first schedule as part of the call. -- The above example creates a new MasterObject, and does schedule the first schedule as part of the call.
-- Note that 2 variables are returned here: MasterObject, ScheduleID... -- Note that 2 variables are returned here: MasterObject, ScheduleID...
-- --
-- ### Construct a SCHEDULER object without a schedule, but volatile to the Object existence... -- ### Construct a SCHEDULER object without a schedule, but volatile to the Object existence...
-- --
-- * @{#SCHEDULER.New}( Object, Function, FunctionArguments, Start, ... ): Setup a new SCHEDULER object, linked to Object, and start a new schedule for the Function with the defined FunctionArguments according the Start and sequent parameters. -- * @{#SCHEDULER.New}( Object, Function, FunctionArguments, Start, ... ): Setup a new SCHEDULER object, linked to Object, and start a new schedule for the Function with the defined FunctionArguments according the Start and sequent parameters.
-- --
-- ZoneObject = ZONE:New( "ZoneName" ) -- ZoneObject = ZONE:New( "ZoneName" )
@@ -112,13 +111,13 @@
-- ... -- ...
-- ZoneObject = nil -- ZoneObject = nil
-- garbagecollect() -- garbagecollect()
-- --
-- The above example creates a new MasterObject, and schedules a method call (ScheduleFunction), -- The above example creates a new MasterObject, and schedules a method call (ScheduleFunction),
-- and is bound to the existence of ZoneObject, which is a ZONE object (ZoneObject). -- and is bound to the existence of ZoneObject, which is a ZONE object (ZoneObject).
-- Both a MasterObject and a SchedulerID variable are returned. -- Both a MasterObject and a SchedulerID variable are returned.
-- Later in the logic, the ZoneObject is put to nil, and garbage is collected. -- Later in the logic, the ZoneObject is put to nil, and garbage is collected.
-- As a result, the MasterObject will cancel the planned schedule. -- As a result, the MasterObject will cancel the planned schedule.
-- --
-- ## SCHEDULER timer stopping and (re-)starting. -- ## SCHEDULER timer stopping and (re-)starting.
-- --
-- The SCHEDULER can be stopped and restarted with the following methods: -- The SCHEDULER can be stopped and restarted with the following methods:
@@ -133,70 +132,70 @@
-- MasterObject:Stop( SchedulerID ) -- MasterObject:Stop( SchedulerID )
-- ... -- ...
-- MasterObject:Start( SchedulerID ) -- MasterObject:Start( SchedulerID )
-- --
-- The above example creates a new MasterObject, and does schedule the first schedule as part of the call. -- The above example creates a new MasterObject, and does schedule the first schedule as part of the call.
-- Note that 2 variables are returned here: MasterObject, ScheduleID... -- Note that 2 variables are returned here: MasterObject, ScheduleID...
-- Later in the logic, the repeating schedule with SchedulerID is stopped. -- Later in the logic, the repeating schedule with SchedulerID is stopped.
-- A bit later, the repeating schedule with SchedulerId is (re)-started. -- A bit later, the repeating schedule with SchedulerId is (re)-started.
-- --
-- ## Create a new schedule -- ## Create a new schedule
-- --
-- With the method @{#SCHEDULER.Schedule}() a new time event can be scheduled. -- With the method @{#SCHEDULER.Schedule}() a new time event can be scheduled.
-- This method is used by the :New() constructor when a new schedule is planned. -- This method is used by the :New() constructor when a new schedule is planned.
-- --
-- Consider the following code fragment of the SCHEDULER object creation. -- Consider the following code fragment of the SCHEDULER object creation.
-- --
-- ZoneObject = ZONE:New( "ZoneName" ) -- ZoneObject = ZONE:New( "ZoneName" )
-- MasterObject = SCHEDULER:New( ZoneObject ) -- MasterObject = SCHEDULER:New( ZoneObject )
-- --
-- Several parameters can be specified that influence the behaviour of a Schedule. -- Several parameters can be specified that influence the behavior of a Schedule.
-- --
-- ### A single schedule, immediately executed -- ### A single schedule, immediately executed
-- --
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {} ) -- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {} )
-- --
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within milleseconds ... -- The above example schedules a new ScheduleFunction call to be executed asynchronously, within milliseconds ...
-- --
-- ### A single schedule, planned over time -- ### A single schedule, planned over time
-- --
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {}, 10 ) -- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {}, 10 )
-- --
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds ... -- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds ...
-- --
-- ### A schedule with a repeating time interval, planned over time -- ### A schedule with a repeating time interval, planned over time
-- --
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 60 ) -- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 60 )
-- --
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds, -- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds,
-- and repeating 60 every seconds ... -- and repeating 60 every seconds ...
-- --
-- ### A schedule with a repeating time interval, planned over time, with time interval randomization -- ### A schedule with a repeating time interval, planned over time, with time interval randomization
-- --
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 60, 0.5 ) -- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 60, 0.5 )
-- --
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds, -- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds,
-- and repeating 60 seconds, with a 50% time interval randomization ... -- and repeating 60 seconds, with a 50% time interval randomization ...
-- So the repeating time interval will be randomized using the **0.5**, -- So the repeating time interval will be randomized using the **0.5**,
-- and will calculate between **60 - ( 60 * 0.5 )** and **60 + ( 60 * 0.5 )** for each repeat, -- and will calculate between **60 - ( 60 * 0.5 )** and **60 + ( 60 * 0.5 )** for each repeat,
-- which is in this example between **30** and **90** seconds. -- which is in this example between **30** and **90** seconds.
-- --
-- ### A schedule with a repeating time interval, planned over time, with time interval randomization, and stop after a time interval -- ### A schedule with a repeating time interval, planned over time, with time interval randomization, and stop after a time interval
-- --
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 60, 0.5, 300 ) -- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 60, 0.5, 300 )
-- --
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds, -- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds,
-- The schedule will repeat every 60 seconds. -- The schedule will repeat every 60 seconds.
-- So the repeating time interval will be randomized using the **0.5**, -- So the repeating time interval will be randomized using the **0.5**,
-- and will calculate between **60 - ( 60 * 0.5 )** and **60 + ( 60 * 0.5 )** for each repeat, -- and will calculate between **60 - ( 60 * 0.5 )** and **60 + ( 60 * 0.5 )** for each repeat,
-- which is in this example between **30** and **90** seconds. -- which is in this example between **30** and **90** seconds.
-- The schedule will stop after **300** seconds. -- The schedule will stop after **300** seconds.
-- --
-- @field #SCHEDULER -- @field #SCHEDULER
SCHEDULER = { SCHEDULER = {
ClassName = "SCHEDULER", ClassName = "SCHEDULER",
Schedules = {}, Schedules = {},
MasterObject = nil, MasterObject = nil,
ShowTrace = nil, ShowTrace = nil,
} }
--- SCHEDULER constructor. --- SCHEDULER constructor.
@@ -211,15 +210,15 @@ SCHEDULER = {
-- @return #SCHEDULER self. -- @return #SCHEDULER self.
-- @return #table The ScheduleID of the planned schedule. -- @return #table The ScheduleID of the planned schedule.
function SCHEDULER:New( MasterObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop ) function SCHEDULER:New( MasterObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop )
local self = BASE:Inherit( self, BASE:New() ) -- #SCHEDULER local self = BASE:Inherit( self, BASE:New() ) -- #SCHEDULER
self:F2( { Start, Repeat, RandomizeFactor, Stop } ) self:F2( { Start, Repeat, RandomizeFactor, Stop } )
local ScheduleID = nil local ScheduleID = nil
self.MasterObject = MasterObject self.MasterObject = MasterObject
self.ShowTrace = false self.ShowTrace = false
if SchedulerFunction then if SchedulerFunction then
ScheduleID = self:Schedule( MasterObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop, 3 ) ScheduleID = self:Schedule( MasterObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop, 3 )
end end
@@ -235,7 +234,7 @@ end
-- @param #number Start Specifies the amount of seconds that will be waited before the scheduling is started, and the event function is called. -- @param #number Start Specifies the amount of seconds that will be waited before the scheduling is started, and the event function is called.
-- @param #number Repeat Specifies the time interval in seconds when the scheduler will call the event function. -- @param #number Repeat Specifies the time interval in seconds when the scheduler will call the event function.
-- @param #number RandomizeFactor Specifies a randomization factor between 0 and 1 to randomize the Repeat. -- @param #number RandomizeFactor Specifies a randomization factor between 0 and 1 to randomize the Repeat.
-- @param #number Stop Time interval in seconds after which the scheduler will be stoppe. -- @param #number Stop Time interval in seconds after which the scheduler will be stopped.
-- @param #number TraceLevel Trace level [0,3]. Default 3. -- @param #number TraceLevel Trace level [0,3]. Default 3.
-- @param Core.Fsm#FSM Fsm Finite state model. -- @param Core.Fsm#FSM Fsm Finite state model.
-- @return #table The ScheduleID of the planned schedule. -- @return #table The ScheduleID of the planned schedule.
@@ -245,28 +244,27 @@ function SCHEDULER:Schedule( MasterObject, SchedulerFunction, SchedulerArguments
-- Debug info. -- Debug info.
local ObjectName = "-" local ObjectName = "-"
if MasterObject and MasterObject.ClassName and MasterObject.ClassID then if MasterObject and MasterObject.ClassName and MasterObject.ClassID then
ObjectName = MasterObject.ClassName .. MasterObject.ClassID ObjectName = MasterObject.ClassName .. MasterObject.ClassID
end end
self:F3( { "Schedule :", ObjectName, tostring( MasterObject ), Start, Repeat, RandomizeFactor, Stop } ) self:F3( { "Schedule :", ObjectName, tostring( MasterObject ), Start, Repeat, RandomizeFactor, Stop } )
-- Set master object. -- Set master object.
self.MasterObject = MasterObject self.MasterObject = MasterObject
-- Add schedule. -- Add schedule.
local ScheduleID = _SCHEDULEDISPATCHER:AddSchedule( local ScheduleID = _SCHEDULEDISPATCHER:AddSchedule( self,
self, SchedulerFunction,
SchedulerFunction, SchedulerArguments,
SchedulerArguments, Start,
Start, Repeat,
Repeat, RandomizeFactor,
RandomizeFactor, Stop,
Stop, TraceLevel or 3,
TraceLevel or 3, Fsm
Fsm )
)
self.Schedules[#self.Schedules + 1] = ScheduleID
self.Schedules[#self.Schedules+1] = ScheduleID
return ScheduleID return ScheduleID
end end
@@ -276,7 +274,7 @@ end
-- @param #string ScheduleID (Optional) The ScheduleID of the planned (repeating) schedule. -- @param #string ScheduleID (Optional) The ScheduleID of the planned (repeating) schedule.
function SCHEDULER:Start( ScheduleID ) function SCHEDULER:Start( ScheduleID )
self:F3( { ScheduleID } ) self:F3( { ScheduleID } )
self:T(string.format("Starting scheduler ID=%s", tostring(ScheduleID))) self:T( string.format( "Starting scheduler ID=%s", tostring( ScheduleID ) ) )
_SCHEDULEDISPATCHER:Start( self, ScheduleID ) _SCHEDULEDISPATCHER:Start( self, ScheduleID )
end end
@@ -285,7 +283,7 @@ end
-- @param #string ScheduleID (Optional) The ScheduleID of the planned (repeating) schedule. -- @param #string ScheduleID (Optional) The ScheduleID of the planned (repeating) schedule.
function SCHEDULER:Stop( ScheduleID ) function SCHEDULER:Stop( ScheduleID )
self:F3( { ScheduleID } ) self:F3( { ScheduleID } )
self:T(string.format("Stopping scheduler ID=%s", tostring(ScheduleID))) self:T( string.format( "Stopping scheduler ID=%s", tostring( ScheduleID ) ) )
_SCHEDULEDISPATCHER:Stop( self, ScheduleID ) _SCHEDULEDISPATCHER:Stop( self, ScheduleID )
end end
@@ -294,15 +292,15 @@ end
-- @param #string ScheduleID (optional) The ScheduleID of the planned (repeating) schedule. -- @param #string ScheduleID (optional) The ScheduleID of the planned (repeating) schedule.
function SCHEDULER:Remove( ScheduleID ) function SCHEDULER:Remove( ScheduleID )
self:F3( { ScheduleID } ) self:F3( { ScheduleID } )
self:T(string.format("Removing scheduler ID=%s", tostring(ScheduleID))) self:T( string.format( "Removing scheduler ID=%s", tostring( ScheduleID ) ) )
_SCHEDULEDISPATCHER:RemoveSchedule( self, ScheduleID ) _SCHEDULEDISPATCHER:RemoveSchedule( self, ScheduleID )
end end
--- Clears all pending schedules. --- Clears all pending schedules.
-- @param #SCHEDULER self -- @param #SCHEDULER self
function SCHEDULER:Clear() function SCHEDULER:Clear()
self:F3( ) self:F3()
self:T(string.format("Clearing scheduler")) self:T( string.format( "Clearing scheduler" ) )
_SCHEDULEDISPATCHER:Clear( self ) _SCHEDULEDISPATCHER:Clear( self )
end end

File diff suppressed because it is too large Load Diff

View File

@@ -29,15 +29,14 @@
-- @module Core.Settings -- @module Core.Settings
-- @image Core_Settings.JPG -- @image Core_Settings.JPG
--- @type SETTINGS --- @type SETTINGS
-- @extends Core.Base#BASE -- @extends Core.Base#BASE
--- Takes care of various settings that influence the behaviour of certain functionalities and classes within the MOOSE framework. --- Takes care of various settings that influence the behavior of certain functionalities and classes within the MOOSE framework.
-- --
-- === -- ===
-- --
-- The SETTINGS class takes care of various settings that influence the behaviour of certain functionalities and classes within the MOOSE framework. -- The SETTINGS class takes care of various settings that influence the behavior of certain functionalities and classes within the MOOSE framework.
-- SETTINGS can work on 2 levels: -- SETTINGS can work on 2 levels:
-- --
-- - **Default settings**: A running mission has **Default settings**. -- - **Default settings**: A running mission has **Default settings**.
@@ -59,7 +58,7 @@
-- --
-- A menu is created automatically per Command Center that allows to modify the **Default** settings. -- A menu is created automatically per Command Center that allows to modify the **Default** settings.
-- So, when joining a CC unit, a menu will be available that allows to change the settings parameters **FOR ALL THE PLAYERS**! -- So, when joining a CC unit, a menu will be available that allows to change the settings parameters **FOR ALL THE PLAYERS**!
-- Note that the **Default settings** will only be used when a player has not choosen its own settings. -- Note that the **Default settings** will only be used when a player has not chosen its own settings.
-- --
-- ## 2.2) Player settings menu -- ## 2.2) Player settings menu
-- --
@@ -69,7 +68,7 @@
-- --
-- ## 2.3) Show or Hide the Player Setting menus -- ## 2.3) Show or Hide the Player Setting menus
-- --
-- Of course, it may be requried not to show any setting menus. In this case, a method is available on the **\_SETTINGS object**. -- Of course, it may be required not to show any setting menus. In this case, a method is available on the **\_SETTINGS object**.
-- Use @{#SETTINGS.SetPlayerMenuOff}() to hide the player menus, and use @{#SETTINGS.SetPlayerMenuOn}() show the player menus. -- Use @{#SETTINGS.SetPlayerMenuOff}() to hide the player menus, and use @{#SETTINGS.SetPlayerMenuOn}() show the player menus.
-- Note that when this method is used, any player already in a slot will not have its menus visibility changed. -- Note that when this method is used, any player already in a slot will not have its menus visibility changed.
-- The option will only have effect when a player enters a new slot or changes a slot. -- The option will only have effect when a player enters a new slot or changes a slot.
@@ -94,8 +93,8 @@
-- --
-- - A2G BR: [Bearing Range](https://en.wikipedia.org/wiki/Bearing_(navigation)). -- - A2G BR: [Bearing Range](https://en.wikipedia.org/wiki/Bearing_(navigation)).
-- - A2G MGRS: The [Military Grid Reference System](https://en.wikipedia.org/wiki/Military_Grid_Reference_System). The accuracy can also be adapted. -- - A2G MGRS: The [Military Grid Reference System](https://en.wikipedia.org/wiki/Military_Grid_Reference_System). The accuracy can also be adapted.
-- - A2G LL DMS: Lattitude Longitude [Degrees Minutes Seconds](https://en.wikipedia.org/wiki/Geographic_coordinate_conversion). The accuracy can also be adapted. -- - A2G LL DMS: Latitude Longitude [Degrees Minutes Seconds](https://en.wikipedia.org/wiki/Geographic_coordinate_conversion). The accuracy can also be adapted.
-- - A2G LL DDM: Lattitude Longitude [Decimal Degrees Minutes](https://en.wikipedia.org/wiki/Decimal_degrees). The accuracy can also be adapted. -- - A2G LL DDM: Latitude Longitude [Decimal Degrees Minutes](https://en.wikipedia.org/wiki/Decimal_degrees). The accuracy can also be adapted.
-- --
-- ### 3.1.2) A2G coordinates setting **menu** -- ### 3.1.2) A2G coordinates setting **menu**
-- --
@@ -183,7 +182,7 @@
-- The settings can be changed by using the **Default settings menu** on the Command Center or the **Player settings menu** on the Player Slot. -- The settings can be changed by using the **Default settings menu** on the Command Center or the **Player settings menu** on the Player Slot.
-- --
-- Each Message Type has specific timings that will be applied when the message is displayed. -- Each Message Type has specific timings that will be applied when the message is displayed.
-- The Settings Menu will provide for each Message Type a selection of proposed durations from which can be choosen. -- The Settings Menu will provide for each Message Type a selection of proposed durations from which can be chosen.
-- So the player can choose its own amount of seconds how long a message should be displayed of a certain type. -- So the player can choose its own amount of seconds how long a message should be displayed of a certain type.
-- Note that **Update** messages can be chosen not to be displayed at all! -- Note that **Update** messages can be chosen not to be displayed at all!
-- --
@@ -196,7 +195,7 @@
-- --
-- ## 3.5) **Era** of the battle -- ## 3.5) **Era** of the battle
-- --
-- The threat level metric is scaled according the era of the battle. A target that is AAA, will pose a much greather threat in WWII than on modern warfare. -- The threat level metric is scaled according the era of the battle. A target that is AAA, will pose a much greater threat in WWII than on modern warfare.
-- Therefore, there are 4 era that are defined within the settings: -- Therefore, there are 4 era that are defined within the settings:
-- --
-- - **WWII** era: Use for warfare with equipment during the world war II time. -- - **WWII** era: Use for warfare with equipment during the world war II time.
@@ -213,8 +212,8 @@
SETTINGS = { SETTINGS = {
ClassName = "SETTINGS", ClassName = "SETTINGS",
ShowPlayerMenu = true, ShowPlayerMenu = true,
MenuShort = false, MenuShort = false,
MenuStatic = false, MenuStatic = false,
} }
SETTINGS.__Enum = {} SETTINGS.__Enum = {}
@@ -231,7 +230,6 @@ SETTINGS.__Enum.Era = {
Modern = 4, Modern = 4,
} }
do -- SETTINGS do -- SETTINGS
--- SETTINGS constructor. --- SETTINGS constructor.
@@ -268,14 +266,14 @@ do -- SETTINGS
-- Short text are better suited for, e.g., VR. -- Short text are better suited for, e.g., VR.
-- @param #SETTINGS self -- @param #SETTINGS self
-- @param #boolean onoff If *true* use short menu texts. If *false* long ones (default). -- @param #boolean onoff If *true* use short menu texts. If *false* long ones (default).
function SETTINGS:SetMenutextShort(onoff) function SETTINGS:SetMenutextShort( onoff )
_SETTINGS.MenuShort = onoff _SETTINGS.MenuShort = onoff
end end
--- Set menu to be static. --- Set menu to be static.
-- @param #SETTINGS self -- @param #SETTINGS self
-- @param #boolean onoff If *true* menu is static. If *false* menu will be updated after changes (default). -- @param #boolean onoff If *true* menu is static. If *false* menu will be updated after changes (default).
function SETTINGS:SetMenuStatic(onoff) function SETTINGS:SetMenuStatic( onoff )
_SETTINGS.MenuStatic = onoff _SETTINGS.MenuStatic = onoff
end end
@@ -289,7 +287,7 @@ do -- SETTINGS
-- @param #SETTINGS self -- @param #SETTINGS self
-- @return #boolean true if metric. -- @return #boolean true if metric.
function SETTINGS:IsMetric() function SETTINGS:IsMetric()
return ( self.Metric ~= nil and self.Metric == true ) or ( self.Metric == nil and _SETTINGS:IsMetric() ) return (self.Metric ~= nil and self.Metric == true) or (self.Metric == nil and _SETTINGS:IsMetric())
end end
--- Sets the SETTINGS imperial. --- Sets the SETTINGS imperial.
@@ -302,7 +300,7 @@ do -- SETTINGS
-- @param #SETTINGS self -- @param #SETTINGS self
-- @return #boolean true if imperial. -- @return #boolean true if imperial.
function SETTINGS:IsImperial() function SETTINGS:IsImperial()
return ( self.Metric ~= nil and self.Metric == false ) or ( self.Metric == nil and _SETTINGS:IsMetric() ) return (self.Metric ~= nil and self.Metric == false) or (self.Metric == nil and _SETTINGS:IsMetric())
end end
--- Sets the SETTINGS LL accuracy. --- Sets the SETTINGS LL accuracy.
@@ -344,13 +342,12 @@ do -- SETTINGS
self.MessageTypeTimings[MessageType] = MessageTime self.MessageTypeTimings[MessageType] = MessageTime
end end
--- Gets the SETTINGS Message Display Timing of a MessageType --- Gets the SETTINGS Message Display Timing of a MessageType
-- @param #SETTINGS self -- @param #SETTINGS self
-- @param Core.Message#MESSAGE MessageType The type of the message. -- @param Core.Message#MESSAGE MessageType The type of the message.
-- @return #number -- @return #number
function SETTINGS:GetMessageTime( MessageType ) function SETTINGS:GetMessageTime( MessageType )
return ( self.MessageTypeTimings and self.MessageTypeTimings[MessageType] ) or _SETTINGS:GetMessageTime( MessageType ) return (self.MessageTypeTimings and self.MessageTypeTimings[MessageType]) or _SETTINGS:GetMessageTime( MessageType )
end end
--- Sets A2G LL DMS --- Sets A2G LL DMS
@@ -371,14 +368,14 @@ do -- SETTINGS
-- @param #SETTINGS self -- @param #SETTINGS self
-- @return #boolean true if LL DMS -- @return #boolean true if LL DMS
function SETTINGS:IsA2G_LL_DMS() function SETTINGS:IsA2G_LL_DMS()
return ( self.A2GSystem and self.A2GSystem == "LL DMS" ) or ( not self.A2GSystem and _SETTINGS:IsA2G_LL_DMS() ) return (self.A2GSystem and self.A2GSystem == "LL DMS") or (not self.A2GSystem and _SETTINGS:IsA2G_LL_DMS())
end end
--- Is LL DDM --- Is LL DDM
-- @param #SETTINGS self -- @param #SETTINGS self
-- @return #boolean true if LL DDM -- @return #boolean true if LL DDM
function SETTINGS:IsA2G_LL_DDM() function SETTINGS:IsA2G_LL_DDM()
return ( self.A2GSystem and self.A2GSystem == "LL DDM" ) or ( not self.A2GSystem and _SETTINGS:IsA2G_LL_DDM() ) return (self.A2GSystem and self.A2GSystem == "LL DDM") or (not self.A2GSystem and _SETTINGS:IsA2G_LL_DDM())
end end
--- Sets A2G MGRS --- Sets A2G MGRS
@@ -392,7 +389,7 @@ do -- SETTINGS
-- @param #SETTINGS self -- @param #SETTINGS self
-- @return #boolean true if MGRS -- @return #boolean true if MGRS
function SETTINGS:IsA2G_MGRS() function SETTINGS:IsA2G_MGRS()
return ( self.A2GSystem and self.A2GSystem == "MGRS" ) or ( not self.A2GSystem and _SETTINGS:IsA2G_MGRS() ) return (self.A2GSystem and self.A2GSystem == "MGRS") or (not self.A2GSystem and _SETTINGS:IsA2G_MGRS())
end end
--- Sets A2G BRA --- Sets A2G BRA
@@ -406,7 +403,7 @@ do -- SETTINGS
-- @param #SETTINGS self -- @param #SETTINGS self
-- @return #boolean true if BRA -- @return #boolean true if BRA
function SETTINGS:IsA2G_BR() function SETTINGS:IsA2G_BR()
return ( self.A2GSystem and self.A2GSystem == "BR" ) or ( not self.A2GSystem and _SETTINGS:IsA2G_BR() ) return (self.A2GSystem and self.A2GSystem == "BR") or (not self.A2GSystem and _SETTINGS:IsA2G_BR())
end end
--- Sets A2A BRA --- Sets A2A BRA
@@ -420,7 +417,7 @@ do -- SETTINGS
-- @param #SETTINGS self -- @param #SETTINGS self
-- @return #boolean true if BRA -- @return #boolean true if BRA
function SETTINGS:IsA2A_BRAA() function SETTINGS:IsA2A_BRAA()
return ( self.A2ASystem and self.A2ASystem == "BRAA" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_BRAA() ) return (self.A2ASystem and self.A2ASystem == "BRAA") or (not self.A2ASystem and _SETTINGS:IsA2A_BRAA())
end end
--- Sets A2A BULLS --- Sets A2A BULLS
@@ -434,7 +431,7 @@ do -- SETTINGS
-- @param #SETTINGS self -- @param #SETTINGS self
-- @return #boolean true if BULLS -- @return #boolean true if BULLS
function SETTINGS:IsA2A_BULLS() function SETTINGS:IsA2A_BULLS()
return ( self.A2ASystem and self.A2ASystem == "BULLS" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_BULLS() ) return (self.A2ASystem and self.A2ASystem == "BULLS") or (not self.A2ASystem and _SETTINGS:IsA2A_BULLS())
end end
--- Sets A2A LL DMS --- Sets A2A LL DMS
@@ -455,14 +452,14 @@ do -- SETTINGS
-- @param #SETTINGS self -- @param #SETTINGS self
-- @return #boolean true if LL DMS -- @return #boolean true if LL DMS
function SETTINGS:IsA2A_LL_DMS() function SETTINGS:IsA2A_LL_DMS()
return ( self.A2ASystem and self.A2ASystem == "LL DMS" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_LL_DMS() ) return (self.A2ASystem and self.A2ASystem == "LL DMS") or (not self.A2ASystem and _SETTINGS:IsA2A_LL_DMS())
end end
--- Is LL DDM --- Is LL DDM
-- @param #SETTINGS self -- @param #SETTINGS self
-- @return #boolean true if LL DDM -- @return #boolean true if LL DDM
function SETTINGS:IsA2A_LL_DDM() function SETTINGS:IsA2A_LL_DDM()
return ( self.A2ASystem and self.A2ASystem == "LL DDM" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_LL_DDM() ) return (self.A2ASystem and self.A2ASystem == "LL DDM") or (not self.A2ASystem and _SETTINGS:IsA2A_LL_DDM())
end end
--- Sets A2A MGRS --- Sets A2A MGRS
@@ -476,7 +473,7 @@ do -- SETTINGS
-- @param #SETTINGS self -- @param #SETTINGS self
-- @return #boolean true if MGRS -- @return #boolean true if MGRS
function SETTINGS:IsA2A_MGRS() function SETTINGS:IsA2A_MGRS()
return ( self.A2ASystem and self.A2ASystem == "MGRS" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_MGRS() ) return (self.A2ASystem and self.A2ASystem == "MGRS") or (not self.A2ASystem and _SETTINGS:IsA2A_MGRS())
end end
--- @param #SETTINGS self --- @param #SETTINGS self
@@ -495,37 +492,37 @@ do -- SETTINGS
-- A2G Coordinate System -- A2G Coordinate System
------- -------
local text="A2G Coordinate System" local text = "A2G Coordinate System"
if _SETTINGS.MenuShort then if _SETTINGS.MenuShort then
text="A2G Coordinates" text = "A2G Coordinates"
end end
local A2GCoordinateMenu = MENU_GROUP:New( MenuGroup, text, SettingsMenu ):SetTime( MenuTime ) local A2GCoordinateMenu = MENU_GROUP:New( MenuGroup, text, SettingsMenu ):SetTime( MenuTime )
-- Set LL DMS -- Set LL DMS
if not self:IsA2G_LL_DMS() then if not self:IsA2G_LL_DMS() then
local text="Lat/Lon Degree Min Sec (LL DMS)" local text = "Lat/Lon Degree Min Sec (LL DMS)"
if _SETTINGS.MenuShort then if _SETTINGS.MenuShort then
text="LL DMS" text = "LL DMS"
end end
MENU_GROUP_COMMAND:New( MenuGroup, text, A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "LL DMS" ):SetTime( MenuTime ) MENU_GROUP_COMMAND:New( MenuGroup, text, A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "LL DMS" ):SetTime( MenuTime )
end end
-- Set LL DDM -- Set LL DDM
if not self:IsA2G_LL_DDM() then if not self:IsA2G_LL_DDM() then
local text="Lat/Lon Degree Dec Min (LL DDM)" local text = "Lat/Lon Degree Dec Min (LL DDM)"
if _SETTINGS.MenuShort then if _SETTINGS.MenuShort then
text="LL DDM" text = "LL DDM"
end end
MENU_GROUP_COMMAND:New( MenuGroup, "Lat/Lon Degree Dec Min (LL DDM)", A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "LL DDM" ):SetTime( MenuTime ) MENU_GROUP_COMMAND:New( MenuGroup, "Lat/Lon Degree Dec Min (LL DDM)", A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "LL DDM" ):SetTime( MenuTime )
end end
-- Set LL DMS accuracy. -- Set LL DMS accuracy.
if self:IsA2G_LL_DDM() then if self:IsA2G_LL_DDM() then
local text1="LL DDM Accuracy 1" local text1 = "LL DDM Accuracy 1"
local text2="LL DDM Accuracy 2" local text2 = "LL DDM Accuracy 2"
local text3="LL DDM Accuracy 3" local text3 = "LL DDM Accuracy 3"
if _SETTINGS.MenuShort then if _SETTINGS.MenuShort then
text1="LL DDM" text1 = "LL DDM"
end end
MENU_GROUP_COMMAND:New( MenuGroup, "LL DDM Accuracy 1", A2GCoordinateMenu, self.MenuLL_DDM_Accuracy, self, MenuGroup, RootMenu, 1 ):SetTime( MenuTime ) MENU_GROUP_COMMAND:New( MenuGroup, "LL DDM Accuracy 1", A2GCoordinateMenu, self.MenuLL_DDM_Accuracy, self, MenuGroup, RootMenu, 1 ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "LL DDM Accuracy 2", A2GCoordinateMenu, self.MenuLL_DDM_Accuracy, self, MenuGroup, RootMenu, 2 ):SetTime( MenuTime ) MENU_GROUP_COMMAND:New( MenuGroup, "LL DDM Accuracy 2", A2GCoordinateMenu, self.MenuLL_DDM_Accuracy, self, MenuGroup, RootMenu, 2 ):SetTime( MenuTime )
@@ -534,18 +531,18 @@ do -- SETTINGS
-- Set BR. -- Set BR.
if not self:IsA2G_BR() then if not self:IsA2G_BR() then
local text="Bearing, Range (BR)" local text = "Bearing, Range (BR)"
if _SETTINGS.MenuShort then if _SETTINGS.MenuShort then
text="BR" text = "BR"
end end
MENU_GROUP_COMMAND:New( MenuGroup, text , A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "BR" ):SetTime( MenuTime ) MENU_GROUP_COMMAND:New( MenuGroup, text, A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "BR" ):SetTime( MenuTime )
end end
-- Set MGRS. -- Set MGRS.
if not self:IsA2G_MGRS() then if not self:IsA2G_MGRS() then
local text="Military Grid (MGRS)" local text = "Military Grid (MGRS)"
if _SETTINGS.MenuShort then if _SETTINGS.MenuShort then
text="MGRS" text = "MGRS"
end end
MENU_GROUP_COMMAND:New( MenuGroup, text, A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "MGRS" ):SetTime( MenuTime ) MENU_GROUP_COMMAND:New( MenuGroup, text, A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "MGRS" ):SetTime( MenuTime )
end end
@@ -563,24 +560,24 @@ do -- SETTINGS
-- A2A Coordinate System -- A2A Coordinate System
------- -------
local text="A2A Coordinate System" local text = "A2A Coordinate System"
if _SETTINGS.MenuShort then if _SETTINGS.MenuShort then
text="A2A Coordinates" text = "A2A Coordinates"
end end
local A2ACoordinateMenu = MENU_GROUP:New( MenuGroup, text, SettingsMenu ):SetTime( MenuTime ) local A2ACoordinateMenu = MENU_GROUP:New( MenuGroup, text, SettingsMenu ):SetTime( MenuTime )
if not self:IsA2A_LL_DMS() then if not self:IsA2A_LL_DMS() then
local text="Lat/Lon Degree Min Sec (LL DMS)" local text = "Lat/Lon Degree Min Sec (LL DMS)"
if _SETTINGS.MenuShort then if _SETTINGS.MenuShort then
text="LL DMS" text = "LL DMS"
end end
MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "LL DMS" ):SetTime( MenuTime ) MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "LL DMS" ):SetTime( MenuTime )
end end
if not self:IsA2A_LL_DDM() then if not self:IsA2A_LL_DDM() then
local text="Lat/Lon Degree Dec Min (LL DDM)" local text = "Lat/Lon Degree Dec Min (LL DDM)"
if _SETTINGS.MenuShort then if _SETTINGS.MenuShort then
text="LL DDM" text = "LL DDM"
end end
MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "LL DDM" ):SetTime( MenuTime ) MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "LL DDM" ):SetTime( MenuTime )
end end
@@ -593,25 +590,25 @@ do -- SETTINGS
end end
if not self:IsA2A_BULLS() then if not self:IsA2A_BULLS() then
local text="Bullseye (BULLS)" local text = "Bullseye (BULLS)"
if _SETTINGS.MenuShort then if _SETTINGS.MenuShort then
text="Bulls" text = "Bulls"
end end
MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "BULLS" ):SetTime( MenuTime ) MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "BULLS" ):SetTime( MenuTime )
end end
if not self:IsA2A_BRAA() then if not self:IsA2A_BRAA() then
local text="Bearing Range Altitude Aspect (BRAA)" local text = "Bearing Range Altitude Aspect (BRAA)"
if _SETTINGS.MenuShort then if _SETTINGS.MenuShort then
text="BRAA" text = "BRAA"
end end
MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "BRAA" ):SetTime( MenuTime ) MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "BRAA" ):SetTime( MenuTime )
end end
if not self:IsA2A_MGRS() then if not self:IsA2A_MGRS() then
local text="Military Grid (MGRS)" local text = "Military Grid (MGRS)"
if _SETTINGS.MenuShort then if _SETTINGS.MenuShort then
text="MGRS" text = "MGRS"
end end
MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "MGRS" ):SetTime( MenuTime ) MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "MGRS" ):SetTime( MenuTime )
end end
@@ -624,31 +621,31 @@ do -- SETTINGS
MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 5", A2ACoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 5 ):SetTime( MenuTime ) MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 5", A2ACoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 5 ):SetTime( MenuTime )
end end
local text="Measures and Weights System" local text = "Measures and Weights System"
if _SETTINGS.MenuShort then if _SETTINGS.MenuShort then
text="Unit System" text = "Unit System"
end end
local MetricsMenu = MENU_GROUP:New( MenuGroup, text, SettingsMenu ):SetTime( MenuTime ) local MetricsMenu = MENU_GROUP:New( MenuGroup, text, SettingsMenu ):SetTime( MenuTime )
if self:IsMetric() then if self:IsMetric() then
local text="Imperial (Miles,Feet)" local text = "Imperial (Miles,Feet)"
if _SETTINGS.MenuShort then if _SETTINGS.MenuShort then
text="Imperial" text = "Imperial"
end end
MENU_GROUP_COMMAND:New( MenuGroup, text, MetricsMenu, self.MenuMWSystem, self, MenuGroup, RootMenu, false ):SetTime( MenuTime ) MENU_GROUP_COMMAND:New( MenuGroup, text, MetricsMenu, self.MenuMWSystem, self, MenuGroup, RootMenu, false ):SetTime( MenuTime )
end end
if self:IsImperial() then if self:IsImperial() then
local text="Metric (Kilometers,Meters)" local text = "Metric (Kilometers,Meters)"
if _SETTINGS.MenuShort then if _SETTINGS.MenuShort then
text="Metric" text = "Metric"
end end
MENU_GROUP_COMMAND:New( MenuGroup, text, MetricsMenu, self.MenuMWSystem, self, MenuGroup, RootMenu, true ):SetTime( MenuTime ) MENU_GROUP_COMMAND:New( MenuGroup, text, MetricsMenu, self.MenuMWSystem, self, MenuGroup, RootMenu, true ):SetTime( MenuTime )
end end
local text="Messages and Reports" local text = "Messages and Reports"
if _SETTINGS.MenuShort then if _SETTINGS.MenuShort then
text="Messages & Reports" text = "Messages & Reports"
end end
local MessagesMenu = MENU_GROUP:New( MenuGroup, text, SettingsMenu ):SetTime( MenuTime ) local MessagesMenu = MENU_GROUP:New( MenuGroup, text, SettingsMenu ):SetTime( MenuTime )
@@ -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, "2 minutes", DetailedReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.DetailedReportsMenu, 120 ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "3 minutes", DetailedReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.DetailedReportsMenu, 180 ):SetTime( MenuTime ) MENU_GROUP_COMMAND:New( MenuGroup, "3 minutes", DetailedReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.DetailedReportsMenu, 180 ):SetTime( MenuTime )
SettingsMenu:Remove( MenuTime ) SettingsMenu:Remove( MenuTime )
return self return self
@@ -733,11 +729,11 @@ do -- SETTINGS
self.PlayerMenu = PlayerMenu self.PlayerMenu = PlayerMenu
self:I(string.format("Setting menu for player %s", tostring(PlayerName))) self:I( string.format( "Setting menu for player %s", tostring( PlayerName ) ) )
local submenu = MENU_GROUP:New( PlayerGroup, "LL Accuracy", PlayerMenu ) local submenu = MENU_GROUP:New( PlayerGroup, "LL Accuracy", PlayerMenu )
MENU_GROUP_COMMAND:New( PlayerGroup, "LL 0 Decimals", submenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 0 ) MENU_GROUP_COMMAND:New( PlayerGroup, "LL 0 Decimals", submenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 0 )
MENU_GROUP_COMMAND:New( PlayerGroup, "LL 1 Decimal", submenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 1 ) MENU_GROUP_COMMAND:New( PlayerGroup, "LL 1 Decimal", submenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 1 )
MENU_GROUP_COMMAND:New( PlayerGroup, "LL 2 Decimals", submenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 2 ) MENU_GROUP_COMMAND:New( PlayerGroup, "LL 2 Decimals", submenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 2 )
MENU_GROUP_COMMAND:New( PlayerGroup, "LL 3 Decimals", submenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 3 ) MENU_GROUP_COMMAND:New( PlayerGroup, "LL 3 Decimals", submenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 3 )
MENU_GROUP_COMMAND:New( PlayerGroup, "LL 4 Decimals", submenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 4 ) MENU_GROUP_COMMAND:New( PlayerGroup, "LL 4 Decimals", submenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 4 )
@@ -754,40 +750,40 @@ do -- SETTINGS
-- A2G Coordinate System -- A2G Coordinate System
------ ------
local text="A2G Coordinate System" local text = "A2G Coordinate System"
if _SETTINGS.MenuShort then if _SETTINGS.MenuShort then
text="A2G Coordinates" text = "A2G Coordinates"
end end
local A2GCoordinateMenu = MENU_GROUP:New( PlayerGroup, text, PlayerMenu ) local A2GCoordinateMenu = MENU_GROUP:New( PlayerGroup, text, PlayerMenu )
if not self:IsA2G_LL_DMS() or _SETTINGS.MenuStatic then if not self:IsA2G_LL_DMS() or _SETTINGS.MenuStatic then
local text="Lat/Lon Degree Min Sec (LL DMS)" local text = "Lat/Lon Degree Min Sec (LL DMS)"
if _SETTINGS.MenuShort then if _SETTINGS.MenuShort then
text="A2G LL DMS" text = "A2G LL DMS"
end end
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DMS" ) MENU_GROUP_COMMAND:New( PlayerGroup, text, A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DMS" )
end end
if not self:IsA2G_LL_DDM() or _SETTINGS.MenuStatic then if not self:IsA2G_LL_DDM() or _SETTINGS.MenuStatic then
local text="Lat/Lon Degree Dec Min (LL DDM)" local text = "Lat/Lon Degree Dec Min (LL DDM)"
if _SETTINGS.MenuShort then if _SETTINGS.MenuShort then
text="A2G LL DDM" text = "A2G LL DDM"
end end
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DDM" ) MENU_GROUP_COMMAND:New( PlayerGroup, text, A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DDM" )
end end
if not self:IsA2G_BR() or _SETTINGS.MenuStatic then if not self:IsA2G_BR() or _SETTINGS.MenuStatic then
local text="Bearing, Range (BR)" local text = "Bearing, Range (BR)"
if _SETTINGS.MenuShort then if _SETTINGS.MenuShort then
text="A2G BR" text = "A2G BR"
end end
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "BR" ) MENU_GROUP_COMMAND:New( PlayerGroup, text, A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "BR" )
end end
if not self:IsA2G_MGRS() or _SETTINGS.MenuStatic then if not self:IsA2G_MGRS() or _SETTINGS.MenuStatic then
local text="Military Grid (MGRS)" local text = "Military Grid (MGRS)"
if _SETTINGS.MenuShort then if _SETTINGS.MenuShort then
text="A2G MGRS" text = "A2G MGRS"
end end
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "MGRS" ) MENU_GROUP_COMMAND:New( PlayerGroup, text, A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "MGRS" )
end end
@@ -796,49 +792,48 @@ do -- SETTINGS
-- A2A Coordinates Menu -- A2A Coordinates Menu
------ ------
local text="A2A Coordinate System" local text = "A2A Coordinate System"
if _SETTINGS.MenuShort then if _SETTINGS.MenuShort then
text="A2A Coordinates" text = "A2A Coordinates"
end end
local A2ACoordinateMenu = MENU_GROUP:New( PlayerGroup, text, PlayerMenu ) local A2ACoordinateMenu = MENU_GROUP:New( PlayerGroup, text, PlayerMenu )
if not self:IsA2A_LL_DMS() or _SETTINGS.MenuStatic then if not self:IsA2A_LL_DMS() or _SETTINGS.MenuStatic then
local text="Lat/Lon Degree Min Sec (LL DMS)" local text = "Lat/Lon Degree Min Sec (LL DMS)"
if _SETTINGS.MenuShort then if _SETTINGS.MenuShort then
text="A2A LL DMS" text = "A2A LL DMS"
end end
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DMS" ) MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DMS" )
end end
if not self:IsA2A_LL_DDM() or _SETTINGS.MenuStatic then if not self:IsA2A_LL_DDM() or _SETTINGS.MenuStatic then
local text="Lat/Lon Degree Dec Min (LL DDM)" local text = "Lat/Lon Degree Dec Min (LL DDM)"
if _SETTINGS.MenuShort then if _SETTINGS.MenuShort then
text="A2A LL DDM" text = "A2A LL DDM"
end end
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DDM" ) MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DDM" )
end end
if not self:IsA2A_BULLS() or _SETTINGS.MenuStatic then if not self:IsA2A_BULLS() or _SETTINGS.MenuStatic then
local text="Bullseye (BULLS)" local text = "Bullseye (BULLS)"
if _SETTINGS.MenuShort then if _SETTINGS.MenuShort then
text="A2A BULLS" text = "A2A BULLS"
end end
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "BULLS" ) MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "BULLS" )
end end
if not self:IsA2A_BRAA() or _SETTINGS.MenuStatic then if not self:IsA2A_BRAA() or _SETTINGS.MenuStatic then
local text="Bearing Range Altitude Aspect (BRAA)" local text = "Bearing Range Altitude Aspect (BRAA)"
if _SETTINGS.MenuShort then if _SETTINGS.MenuShort then
text="A2A BRAA" text = "A2A BRAA"
end end
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "BRAA" ) MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "BRAA" )
end end
if not self:IsA2A_MGRS() or _SETTINGS.MenuStatic then if not self:IsA2A_MGRS() or _SETTINGS.MenuStatic then
local text="Military Grid (MGRS)" local text = "Military Grid (MGRS)"
if _SETTINGS.MenuShort then if _SETTINGS.MenuShort then
text="A2A MGRS" text = "A2A MGRS"
end end
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "MGRS" ) MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "MGRS" )
end end
@@ -847,24 +842,24 @@ do -- SETTINGS
-- Unit system -- Unit system
--- ---
local text="Measures and Weights System" local text = "Measures and Weights System"
if _SETTINGS.MenuShort then if _SETTINGS.MenuShort then
text="Unit System" text = "Unit System"
end end
local MetricsMenu = MENU_GROUP:New( PlayerGroup, text, PlayerMenu ) local MetricsMenu = MENU_GROUP:New( PlayerGroup, text, PlayerMenu )
if self:IsMetric() or _SETTINGS.MenuStatic then if self:IsMetric() or _SETTINGS.MenuStatic then
local text="Imperial (Miles,Feet)" local text = "Imperial (Miles,Feet)"
if _SETTINGS.MenuShort then if _SETTINGS.MenuShort then
text="Imperial" text = "Imperial"
end end
MENU_GROUP_COMMAND:New( PlayerGroup, text, MetricsMenu, self.MenuGroupMWSystem, self, PlayerUnit, PlayerGroup, PlayerName, false ) MENU_GROUP_COMMAND:New( PlayerGroup, text, MetricsMenu, self.MenuGroupMWSystem, self, PlayerUnit, PlayerGroup, PlayerName, false )
end end
if self:IsImperial() or _SETTINGS.MenuStatic then if self:IsImperial() or _SETTINGS.MenuStatic then
local text="Metric (Kilometers,Meters)" local text = "Metric (Kilometers,Meters)"
if _SETTINGS.MenuShort then if _SETTINGS.MenuShort then
text="Metric" text = "Metric"
end end
MENU_GROUP_COMMAND:New( PlayerGroup, text, MetricsMenu, self.MenuGroupMWSystem, self, PlayerUnit, PlayerGroup, PlayerName, true ) MENU_GROUP_COMMAND:New( PlayerGroup, text, MetricsMenu, self.MenuGroupMWSystem, self, PlayerUnit, PlayerGroup, PlayerName, true )
end end
@@ -873,9 +868,9 @@ do -- SETTINGS
-- Messages and Reports -- Messages and Reports
--- ---
local text="Messages and Reports" local text = "Messages and Reports"
if _SETTINGS.MenuShort then if _SETTINGS.MenuShort then
text="Messages & Reports" text = "Messages & Reports"
end end
local MessagesMenu = MENU_GROUP:New( PlayerGroup, text, PlayerMenu ) local MessagesMenu = MENU_GROUP:New( PlayerGroup, text, PlayerMenu )
@@ -935,39 +930,38 @@ do -- SETTINGS
return self return self
end end
--- @param #SETTINGS self --- @param #SETTINGS self
function SETTINGS:A2GMenuSystem( MenuGroup, RootMenu, A2GSystem ) function SETTINGS:A2GMenuSystem( MenuGroup, RootMenu, A2GSystem )
self.A2GSystem = A2GSystem self.A2GSystem = A2GSystem
MESSAGE:New( string.format("Settings: Default A2G coordinate system set to %s for all players!", A2GSystem ), 5 ):ToAll() MESSAGE:New( string.format( "Settings: Default A2G coordinate system set to %s for all players!", A2GSystem ), 5 ):ToAll()
self:SetSystemMenu( MenuGroup, RootMenu ) self:SetSystemMenu( MenuGroup, RootMenu )
end end
--- @param #SETTINGS self --- @param #SETTINGS self
function SETTINGS:A2AMenuSystem( MenuGroup, RootMenu, A2ASystem ) function SETTINGS:A2AMenuSystem( MenuGroup, RootMenu, A2ASystem )
self.A2ASystem = A2ASystem self.A2ASystem = A2ASystem
MESSAGE:New( string.format("Settings: Default A2A coordinate system set to %s for all players!", A2ASystem ), 5 ):ToAll() MESSAGE:New( string.format( "Settings: Default A2A coordinate system set to %s for all players!", A2ASystem ), 5 ):ToAll()
self:SetSystemMenu( MenuGroup, RootMenu ) self:SetSystemMenu( MenuGroup, RootMenu )
end end
--- @param #SETTINGS self --- @param #SETTINGS self
function SETTINGS:MenuLL_DDM_Accuracy( MenuGroup, RootMenu, LL_Accuracy ) function SETTINGS:MenuLL_DDM_Accuracy( MenuGroup, RootMenu, LL_Accuracy )
self.LL_Accuracy = LL_Accuracy self.LL_Accuracy = LL_Accuracy
MESSAGE:New( string.format("Settings: Default LL accuracy set to %s for all players!", LL_Accuracy ), 5 ):ToAll() MESSAGE:New( string.format( "Settings: Default LL accuracy set to %s for all players!", LL_Accuracy ), 5 ):ToAll()
self:SetSystemMenu( MenuGroup, RootMenu ) self:SetSystemMenu( MenuGroup, RootMenu )
end end
--- @param #SETTINGS self --- @param #SETTINGS self
function SETTINGS:MenuMGRS_Accuracy( MenuGroup, RootMenu, MGRS_Accuracy ) function SETTINGS:MenuMGRS_Accuracy( MenuGroup, RootMenu, MGRS_Accuracy )
self.MGRS_Accuracy = MGRS_Accuracy self.MGRS_Accuracy = MGRS_Accuracy
MESSAGE:New( string.format("Settings: Default MGRS accuracy set to %s for all players!", MGRS_Accuracy ), 5 ):ToAll() MESSAGE:New( string.format( "Settings: Default MGRS accuracy set to %s for all players!", MGRS_Accuracy ), 5 ):ToAll()
self:SetSystemMenu( MenuGroup, RootMenu ) self:SetSystemMenu( MenuGroup, RootMenu )
end end
--- @param #SETTINGS self --- @param #SETTINGS self
function SETTINGS:MenuMWSystem( MenuGroup, RootMenu, MW ) function SETTINGS:MenuMWSystem( MenuGroup, RootMenu, MW )
self.Metric = MW self.Metric = MW
MESSAGE:New( string.format("Settings: Default measurement format set to %s for all players!", MW and "Metric" or "Imperial" ), 5 ):ToAll() MESSAGE:New( string.format( "Settings: Default measurement format set to %s for all players!", MW and "Metric" or "Imperial" ), 5 ):ToAll()
self:SetSystemMenu( MenuGroup, RootMenu ) self:SetSystemMenu( MenuGroup, RootMenu )
end end
@@ -980,12 +974,12 @@ do -- SETTINGS
do do
--- @param #SETTINGS self --- @param #SETTINGS self
function SETTINGS:MenuGroupA2GSystem( PlayerUnit, PlayerGroup, PlayerName, A2GSystem ) function SETTINGS:MenuGroupA2GSystem( PlayerUnit, PlayerGroup, PlayerName, A2GSystem )
BASE:E( {self, PlayerUnit:GetName(), A2GSystem} ) BASE:E( { self, PlayerUnit:GetName(), A2GSystem } )
self.A2GSystem = A2GSystem self.A2GSystem = A2GSystem
MESSAGE:New( string.format( "Settings: A2G format set to %s for player %s.", A2GSystem, PlayerName ), 5 ):ToGroup( PlayerGroup ) MESSAGE:New( string.format( "Settings: A2G format set to %s for player %s.", A2GSystem, PlayerName ), 5 ):ToGroup( PlayerGroup )
if _SETTINGS.MenuStatic==false then if _SETTINGS.MenuStatic == false then
self:RemovePlayerMenu(PlayerUnit) self:RemovePlayerMenu( PlayerUnit )
self:SetPlayerMenu(PlayerUnit) self:SetPlayerMenu( PlayerUnit )
end end
end end
@@ -993,9 +987,9 @@ do -- SETTINGS
function SETTINGS:MenuGroupA2ASystem( PlayerUnit, PlayerGroup, PlayerName, A2ASystem ) function SETTINGS:MenuGroupA2ASystem( PlayerUnit, PlayerGroup, PlayerName, A2ASystem )
self.A2ASystem = A2ASystem self.A2ASystem = A2ASystem
MESSAGE:New( string.format( "Settings: A2A format set to %s for player %s.", A2ASystem, PlayerName ), 5 ):ToGroup( PlayerGroup ) MESSAGE:New( string.format( "Settings: A2A format set to %s for player %s.", A2ASystem, PlayerName ), 5 ):ToGroup( PlayerGroup )
if _SETTINGS.MenuStatic==false then if _SETTINGS.MenuStatic == false then
self:RemovePlayerMenu(PlayerUnit) self:RemovePlayerMenu( PlayerUnit )
self:SetPlayerMenu(PlayerUnit) self:SetPlayerMenu( PlayerUnit )
end end
end end
@@ -1003,9 +997,9 @@ do -- SETTINGS
function SETTINGS:MenuGroupLL_DDM_AccuracySystem( PlayerUnit, PlayerGroup, PlayerName, LL_Accuracy ) function SETTINGS:MenuGroupLL_DDM_AccuracySystem( PlayerUnit, PlayerGroup, PlayerName, LL_Accuracy )
self.LL_Accuracy = LL_Accuracy self.LL_Accuracy = LL_Accuracy
MESSAGE:New( string.format( "Settings: LL format accuracy set to %d decimal places for player %s.", LL_Accuracy, PlayerName ), 5 ):ToGroup( PlayerGroup ) MESSAGE:New( string.format( "Settings: LL format accuracy set to %d decimal places for player %s.", LL_Accuracy, PlayerName ), 5 ):ToGroup( PlayerGroup )
if _SETTINGS.MenuStatic==false then if _SETTINGS.MenuStatic == false then
self:RemovePlayerMenu(PlayerUnit) self:RemovePlayerMenu( PlayerUnit )
self:SetPlayerMenu(PlayerUnit) self:SetPlayerMenu( PlayerUnit )
end end
end end
@@ -1013,9 +1007,9 @@ do -- SETTINGS
function SETTINGS:MenuGroupMGRS_AccuracySystem( PlayerUnit, PlayerGroup, PlayerName, MGRS_Accuracy ) function SETTINGS:MenuGroupMGRS_AccuracySystem( PlayerUnit, PlayerGroup, PlayerName, MGRS_Accuracy )
self.MGRS_Accuracy = MGRS_Accuracy self.MGRS_Accuracy = MGRS_Accuracy
MESSAGE:New( string.format( "Settings: MGRS format accuracy set to %d for player %s.", MGRS_Accuracy, PlayerName ), 5 ):ToGroup( PlayerGroup ) MESSAGE:New( string.format( "Settings: MGRS format accuracy set to %d for player %s.", MGRS_Accuracy, PlayerName ), 5 ):ToGroup( PlayerGroup )
if _SETTINGS.MenuStatic==false then if _SETTINGS.MenuStatic == false then
self:RemovePlayerMenu(PlayerUnit) self:RemovePlayerMenu( PlayerUnit )
self:SetPlayerMenu(PlayerUnit) self:SetPlayerMenu( PlayerUnit )
end end
end end
@@ -1023,9 +1017,9 @@ do -- SETTINGS
function SETTINGS:MenuGroupMWSystem( PlayerUnit, PlayerGroup, PlayerName, MW ) function SETTINGS:MenuGroupMWSystem( PlayerUnit, PlayerGroup, PlayerName, MW )
self.Metric = MW self.Metric = MW
MESSAGE:New( string.format( "Settings: Measurement format set to %s for player %s.", MW and "Metric" or "Imperial", PlayerName ), 5 ):ToGroup( PlayerGroup ) MESSAGE:New( string.format( "Settings: Measurement format set to %s for player %s.", MW and "Metric" or "Imperial", PlayerName ), 5 ):ToGroup( PlayerGroup )
if _SETTINGS.MenuStatic==false then if _SETTINGS.MenuStatic == false then
self:RemovePlayerMenu(PlayerUnit) self:RemovePlayerMenu( PlayerUnit )
self:SetPlayerMenu(PlayerUnit) self:SetPlayerMenu( PlayerUnit )
end end
end end
@@ -1055,7 +1049,6 @@ do -- SETTINGS
end end
--- Configures the era of the mission to be Cold war. --- Configures the era of the mission to be Cold war.
-- @param #SETTINGS self -- @param #SETTINGS self
-- @return #SETTINGS self -- @return #SETTINGS self
@@ -1065,7 +1058,6 @@ do -- SETTINGS
end end
--- Configures the era of the mission to be Modern war. --- Configures the era of the mission to be Modern war.
-- @param #SETTINGS self -- @param #SETTINGS self
-- @return #SETTINGS self -- @return #SETTINGS self
@@ -1075,7 +1067,4 @@ do -- SETTINGS
end end
end end

File diff suppressed because it is too large Load Diff

View File

@@ -23,7 +23,6 @@
-- @module Functional.FOX -- @module Functional.FOX
-- @image Functional_FOX.png -- @image Functional_FOX.png
--- FOX class. --- FOX class.
-- @type FOX -- @type FOX
-- @field #string ClassName Name of the class. -- @field #string ClassName Name of the class.
@@ -47,8 +46,7 @@
-- @field #number dt10 Time step [sec] for missile position updates if distance to target > 10 km and < 50 km. Default 1 sec. -- @field #number dt10 Time step [sec] for missile position updates if distance to target > 10 km and < 50 km. Default 1 sec.
-- @field #number dt05 Time step [sec] for missile position updates if distance to target > 5 km and < 10 km. Default 0.5 sec. -- @field #number dt05 Time step [sec] for missile position updates if distance to target > 5 km and < 10 km. Default 0.5 sec.
-- @field #number dt01 Time step [sec] for missile position updates if distance to target > 1 km and < 5 km. Default 0.1 sec. -- @field #number dt01 Time step [sec] for missile position updates if distance to target > 1 km and < 5 km. Default 0.1 sec.
-- @field #number dt00 Time step [sec] for missile position updates if distance to target < 1 km. Default 0.01 sec. -- @field #number dt00 Time step [sec] for missile position updates if distance to target < 1 km. Default 0.01 sec.
-- @field #boolean
-- @extends Core.Fsm#FSM -- @extends Core.Fsm#FSM
--- Fox 3! --- Fox 3!
@@ -1813,4 +1811,4 @@ end
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

File diff suppressed because it is too large Load Diff

View File

@@ -742,7 +742,7 @@ function PSEUDOATC:ReportWeather(GID, UID, position, location)
local T=position:GetTemperature() local T=position:GetTemperature()
-- Correct unit system. -- Correct unit system.
local _T=string.format('%d°F', UTILS.CelciusToFarenheit(T)) local _T=string.format('%d°F', UTILS.CelsiusToFahrenheit(T))
if settings:IsMetric() then if settings:IsMetric() then
_T=string.format('%d°C', T) _T=string.format('%d°C', T)
end end

File diff suppressed because it is too large Load Diff

View File

@@ -1,56 +1,64 @@
--- **Functional** -- Make SAM sites execute evasive and defensive behaviour when being fired upon. --- **Functional** -- Make SAM sites execute evasive and defensive behaviour when being fired upon.
-- --
-- === -- ===
-- --
-- ## Features: -- ## Features:
-- --
-- * When SAM sites are being fired upon, the SAMs will take evasive action will reposition themselves when possible. -- * When SAM sites are being fired upon, the SAMs will take evasive action will reposition themselves when possible.
-- * When SAM sites are being fired upon, the SAMs will take defensive action by shutting down their radars. -- * When SAM sites are being fired upon, the SAMs will take defensive action by shutting down their radars.
-- -- * SEAD calculates the time it takes for a HARM to reach the target - and will attempt to minimize the shut-down time.
-- * Detection and evasion of shots has a random component based on the skill level of the SAM groups.
--
-- === -- ===
-- --
-- ## Missions: -- ## Missions:
-- --
-- [SEV - SEAD Evasion](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SEV%20-%20SEAD%20Evasion) -- [SEV - SEAD Evasion](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SEV%20-%20SEAD%20Evasion)
-- --
-- === -- ===
-- --
-- ### Authors: **FlightControl**, **applevangelist** -- ### Authors: **FlightControl**, **applevangelist**
-- --
-- Last Update: Aug 2021 -- Last Update: Nov 2021
-- --
-- === -- ===
-- --
-- @module Functional.Sead -- @module Functional.Sead
-- @image SEAD.JPG -- @image SEAD.JPG
--- ---
-- @type SEAD -- @type SEAD
-- @extends Core.Base#BASE -- @extends Core.Base#BASE
--- Make SAM sites execute evasive and defensive behaviour when being fired upon. --- Make SAM sites execute evasive and defensive behaviour when being fired upon.
-- --
-- This class is very easy to use. Just setup a SEAD object by using @{#SEAD.New}() and SAMs will evade and take defensive action when being fired upon. -- This class is very easy to use. Just setup a SEAD object by using @{#SEAD.New}() and SAMs will evade and take defensive action when being fired upon.
-- Once a HARM attack is detected, SEAD will shut down the radars of the attacked SAM site and take evasive action by moving the SAM
-- vehicles around (*if* they are drivable, that is). There's a component of randomness in detection and evasion, which is based on the
-- skill set of the SAM set (the higher the skill, the more likely). When a missile is fired from far away, the SAM will stay active for a
-- period of time to stay defensive, before it takes evasive actions.
-- --
-- # Constructor: -- # Constructor:
-- --
-- Use the @{#SEAD.New}() constructor to create a new SEAD object. -- Use the @{#SEAD.New}() constructor to create a new SEAD object.
-- --
-- SEAD_RU_SAM_Defenses = SEAD:New( { 'RU SA-6 Kub', 'RU SA-6 Defenses', 'RU MI-26 Troops', 'RU Attack Gori' } ) -- SEAD_RU_SAM_Defenses = SEAD:New( { 'RU SA-6 Kub', 'RU SA-6 Defenses', 'RU MI-26 Troops', 'RU Attack Gori' } )
-- --
-- @field #SEAD -- @field #SEAD
SEAD = { SEAD = {
ClassName = "SEAD", ClassName = "SEAD",
TargetSkill = { TargetSkill = {
Average = { Evade = 30, DelayOn = { 40, 60 } } , Average = { Evade = 30, DelayOn = { 40, 60 } } ,
Good = { Evade = 20, DelayOn = { 30, 50 } } , Good = { Evade = 20, DelayOn = { 30, 50 } } ,
High = { Evade = 15, DelayOn = { 20, 40 } } , High = { Evade = 15, DelayOn = { 20, 40 } } ,
Excellent = { Evade = 10, DelayOn = { 10, 30 } } Excellent = { Evade = 10, DelayOn = { 10, 30 } }
}, },
SEADGroupPrefixes = {}, SEADGroupPrefixes = {},
SuppressedGroups = {}, SuppressedGroups = {},
EngagementRange = 75, -- default 75% engagement range Feature Request #1355 EngagementRange = 75, -- default 75% engagement range Feature Request #1355
Padding = 10, Padding = 10,
CallBack = nil,
UseCallBack = false,
} }
--- Missile enumerators --- Missile enumerators
@@ -69,7 +77,7 @@ SEAD = {
["X_31"] = "X_31", ["X_31"] = "X_31",
["Kh25"] = "Kh25", ["Kh25"] = "Kh25",
} }
--- Missile enumerators - from DCS ME and Wikipedia --- Missile enumerators - from DCS ME and Wikipedia
-- @field HarmData -- @field HarmData
SEAD.HarmData = { SEAD.HarmData = {
@@ -86,14 +94,14 @@ SEAD = {
["X_31"] = {150, 3}, ["X_31"] = {150, 3},
["Kh25"] = {25, 0.8}, ["Kh25"] = {25, 0.8},
} }
--- Creates the main object which is handling defensive actions for SA sites or moving SA vehicles. --- Creates the main object which is handling defensive actions for SA sites or moving SA vehicles.
-- When an anti radiation missile is fired (KH-58, KH-31P, KH-31A, KH-25MPU, HARM missiles), the SA will shut down their radars and will take evasive actions... -- When an anti radiation missile is fired (KH-58, KH-31P, KH-31A, KH-25MPU, HARM missiles), the SA will shut down their radars and will take evasive actions...
-- Chances are big that the missile will miss. -- Chances are big that the missile will miss.
-- @param #SEAD self -- @param #SEAD self
-- @param #table SEADGroupPrefixes Table of #string entries or single #string, which is a table of Prefixes of the SA Groups in the DCS mission editor on which evasive actions need to be taken. -- @param #table SEADGroupPrefixes Table of #string entries or single #string, which is a table of Prefixes of the SA Groups in the DCS mission editor on which evasive actions need to be taken.
-- @param #number Padding (Optional) Extra number of seconds to add to radar switch-back-on time -- @param #number Padding (Optional) Extra number of seconds to add to radar switch-back-on time
-- @return SEAD -- @return #SEAD self
-- @usage -- @usage
-- -- CCCP SEAD Defenses -- -- CCCP SEAD Defenses
-- -- Defends the Russian SA installations from SEAD attacks. -- -- Defends the Russian SA installations from SEAD attacks.
@@ -102,7 +110,7 @@ function SEAD:New( SEADGroupPrefixes, Padding )
local self = BASE:Inherit( self, BASE:New() ) local self = BASE:Inherit( self, BASE:New() )
self:F( SEADGroupPrefixes ) self:F( SEADGroupPrefixes )
if type( SEADGroupPrefixes ) == 'table' then if type( SEADGroupPrefixes ) == 'table' then
for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do
self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix
@@ -110,25 +118,29 @@ function SEAD:New( SEADGroupPrefixes, Padding )
else else
self.SEADGroupPrefixes[SEADGroupPrefixes] = SEADGroupPrefixes self.SEADGroupPrefixes[SEADGroupPrefixes] = SEADGroupPrefixes
end end
local padding = Padding or 10 local padding = Padding or 10
if padding < 10 then padding = 10 end if padding < 10 then padding = 10 end
self.Padding = padding self.Padding = padding
self.UseEmissionsOnOff = false
self.CallBack = nil
self.UseCallBack = false
self:HandleEvent( EVENTS.Shot, self.HandleEventShot ) self:HandleEvent( EVENTS.Shot, self.HandleEventShot )
self:I("*** SEAD - Started Version 0.3.1") self:I("*** SEAD - Started Version 0.3.3")
return self return self
end end
--- Update the active SEAD Set --- Update the active SEAD Set (while running)
-- @param #SEAD self -- @param #SEAD self
-- @param #table SEADGroupPrefixes The prefixes to add, note: can also be a single #string -- @param #table SEADGroupPrefixes The prefixes to add, note: can also be a single #string
-- @return #SEAD self -- @return #SEAD self
function SEAD:UpdateSet( SEADGroupPrefixes ) function SEAD:UpdateSet( SEADGroupPrefixes )
self:T( SEADGroupPrefixes ) self:T( SEADGroupPrefixes )
if type( SEADGroupPrefixes ) == 'table' then if type( SEADGroupPrefixes ) == 'table' then
for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do
self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix
@@ -142,8 +154,8 @@ end
--- Sets the engagement range of the SAMs. Defaults to 75% to make it more deadly. Feature Request #1355 --- Sets the engagement range of the SAMs. Defaults to 75% to make it more deadly. Feature Request #1355
-- @param #SEAD self -- @param #SEAD self
-- @param #number range Set the engagement range in percent, e.g. 50 -- @param #number range Set the engagement range in percent, e.g. 55 (default 75)
-- @return self -- @return #SEAD self
function SEAD:SetEngagementRange(range) function SEAD:SetEngagementRange(range)
self:T( { range } ) self:T( { range } )
range = range or 75 range = range or 75
@@ -157,7 +169,8 @@ end
--- Set the padding in seconds, which extends the radar off time calculated by SEAD --- Set the padding in seconds, which extends the radar off time calculated by SEAD
-- @param #SEAD self -- @param #SEAD self
-- @param #number Padding Extra number of seconds to add for the switch-on -- @param #number Padding Extra number of seconds to add for the switch-on (default 10 seconds)
-- @return #SEAD self
function SEAD:SetPadding(Padding) function SEAD:SetPadding(Padding)
self:T( { Padding } ) self:T( { Padding } )
local padding = Padding or 10 local padding = Padding or 10
@@ -166,56 +179,80 @@ function SEAD:SetPadding(Padding)
return self return self
end end
--- Check if a known HARM was fired --- Set SEAD to use emissions on/off in addition to alarm state.
-- @param #SEAD self -- @param #SEAD self
-- @param #string WeaponName -- @param #boolean Switch True for on, false for off.
-- @return #boolean Returns true for a match -- @return #SEAD self
-- @return #string name Name of hit in table function SEAD:SwitchEmissions(Switch)
function SEAD:_CheckHarms(WeaponName) self:T({Switch})
self:T( { WeaponName } ) self.UseEmissionsOnOff = Switch
local hit = false return self
local name = "" end
for _,_name in pairs (SEAD.Harms) do
if string.find(WeaponName,_name,1) then
hit = true
name = _name
break
end
end
return hit, name
end
--- (Internal) Return distance in meters between two coordinates or -1 on error. --- Add an object to call back when going evasive.
-- @param #SEAD self -- @param #SEAD self
-- @param Core.Point#COORDINATE _point1 Coordinate one -- @param #table Object The object to call. Needs to have object functions as follows:
-- @param Core.Point#COORDINATE _point2 Coordinate two -- `:SeadSuppressionPlanned(Group, Name, SuppressionStartTime, SuppressionEndTime)`
-- @return #number Distance in meters -- `:SeadSuppressionStart(Group, Name)`,
function SEAD:_GetDistance(_point1, _point2) -- `:SeadSuppressionEnd(Group, Name)`,
self:T("_GetDistance") -- @return #SEAD self
if _point1 and _point2 then function SEAD:AddCallBack(Object)
local distance1 = _point1:Get2DDistance(_point2) self:T({Class=Object.ClassName})
local distance2 = _point1:DistanceFromPointVec2(_point2) self.CallBack = Object
--self:T({dist1=distance1, dist2=distance2}) self.UseCallBack = true
if distance1 and type(distance1) == "number" then return self
return distance1 end
elseif distance2 and type(distance2) == "number" then
return distance2 --- (Internal) Check if a known HARM was fired
else -- @param #SEAD self
self:E("*****Cannot calculate distance!") -- @param #string WeaponName
self:E({_point1,_point2}) -- @return #boolean Returns true for a match
return -1 -- @return #string name Name of hit in table
function SEAD:_CheckHarms(WeaponName)
self:T( { WeaponName } )
local hit = false
local name = ""
for _,_name in pairs (SEAD.Harms) do
if string.find(WeaponName,_name,1) then
hit = true
name = _name
break
end end
end
return hit, name
end
--- (Internal) Return distance in meters between two coordinates or -1 on error.
-- @param #SEAD self
-- @param Core.Point#COORDINATE _point1 Coordinate one
-- @param Core.Point#COORDINATE _point2 Coordinate two
-- @return #number Distance in meters
function SEAD:_GetDistance(_point1, _point2)
self:T("_GetDistance")
if _point1 and _point2 then
local distance1 = _point1:Get2DDistance(_point2)
local distance2 = _point1:DistanceFromPointVec2(_point2)
--self:T({dist1=distance1, dist2=distance2})
if distance1 and type(distance1) == "number" then
return distance1
elseif distance2 and type(distance2) == "number" then
return distance2
else else
self:E("******Cannot calculate distance!") self:E("*****Cannot calculate distance!")
self:E({_point1,_point2}) self:E({_point1,_point2})
return -1 return -1
end end
else
self:E("******Cannot calculate distance!")
self:E({_point1,_point2})
return -1
end end
end
--- 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 --- (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 -- @param #SEAD self
-- @param Core.Event#EVENTDATA EventData -- @param Core.Event#EVENTDATA EventData
-- @return #SEAD self
function SEAD:HandleEventShot( EventData ) function SEAD:HandleEventShot( EventData )
self:T( { EventData.id } ) self:T( { EventData.id } )
local SEADPlane = EventData.IniUnit -- Wrapper.Unit#UNIT local SEADPlane = EventData.IniUnit -- Wrapper.Unit#UNIT
@@ -227,7 +264,7 @@ function SEAD:HandleEventShot( EventData )
self:T( "*** SEAD - Missile Launched = " .. SEADWeaponName) self:T( "*** SEAD - Missile Launched = " .. SEADWeaponName)
--self:T({ SEADWeapon }) --self:T({ SEADWeapon })
if self:_CheckHarms(SEADWeaponName) then if self:_CheckHarms(SEADWeaponName) then
self:T( '*** SEAD - Weapon Match' ) self:T( '*** SEAD - Weapon Match' )
local _targetskill = "Random" local _targetskill = "Random"
@@ -245,13 +282,13 @@ function SEAD:HandleEventShot( EventData )
-- see if we are shot at -- see if we are shot at
local SEADGroupFound = false local SEADGroupFound = false
for SEADGroupPrefixID, SEADGroupPrefix in pairs( self.SEADGroupPrefixes ) do for SEADGroupPrefixID, SEADGroupPrefix in pairs( self.SEADGroupPrefixes ) do
self:T( SEADGroupPrefix ) self:T( _targetgroupname, SEADGroupPrefix )
if string.find( _targetgroupname, SEADGroupPrefix, 1, true ) then if string.find( _targetgroupname, SEADGroupPrefix, 1, true ) then
SEADGroupFound = true SEADGroupFound = true
self:T( '*** SEAD - Group Match Found' ) self:T( '*** SEAD - Group Match Found' )
break break
end end
end end
if SEADGroupFound == true then -- yes we are being attacked if SEADGroupFound == true then -- yes we are being attacked
if _targetskill == "Random" then -- when skill is random, choose a skill if _targetskill == "Random" then -- when skill is random, choose a skill
local Skills = { "Average", "Good", "High", "Excellent" } local Skills = { "Average", "Good", "High", "Excellent" }
@@ -282,45 +319,66 @@ function SEAD:HandleEventShot( EventData )
else else
_distance = 0 _distance = 0
end end
self:T( string.format("*** SEAD - target skill %s, distance %dkm, reach %dkm, tti %dsec", _targetskill, _distance,reach,_tti )) self:T( string.format("*** SEAD - target skill %s, distance %dkm, reach %dkm, tti %dsec", _targetskill, _distance,reach,_tti ))
if reach >= _distance then if reach >= _distance then
self:T("*** SEAD - Shot in Reach") self:T("*** SEAD - Shot in Reach")
local function SuppressionStart(args) local function SuppressionStart(args)
self:T(string.format("*** SEAD - %s Radar Off & Relocating",args[2])) self:T(string.format("*** SEAD - %s Radar Off & Relocating",args[2]))
local grp = args[1] -- Wrapper.Group#GROUP local grp = args[1] -- Wrapper.Group#GROUP
grp:OptionAlarmStateGreen() local name = args[2] -- #string Group Name
if self.UseEmissionsOnOff then
grp:EnableEmission(false)
end
grp:OptionAlarmStateGreen() -- needed else we cannot move around
grp:RelocateGroundRandomInRadius(20,300,false,false,"Diamond") grp:RelocateGroundRandomInRadius(20,300,false,false,"Diamond")
if self.UseCallBack then
local object = self.CallBack
object:SeadSuppressionStart(grp,name)
end
end end
local function SuppressionStop(args) local function SuppressionStop(args)
self:T(string.format("*** SEAD - %s Radar On",args[2])) self:T(string.format("*** SEAD - %s Radar On",args[2]))
local grp = args[1] -- Wrapper.Group#GROUP local grp = args[1] -- Wrapper.Group#GROUP
grp:OptionAlarmStateRed() local name = args[2] -- #string Group Nam
if self.UseEmissionsOnOff then
grp:EnableEmission(true)
end
grp:OptionAlarmStateAuto()
grp:OptionEngageRange(self.EngagementRange) grp:OptionEngageRange(self.EngagementRange)
self.SuppressedGroups[args[2]] = false self.SuppressedGroups[name] = false
if self.UseCallBack then
local object = self.CallBack
object:SeadSuppressionEnd(grp,name)
end
end end
-- randomize switch-on time -- randomize switch-on time
local delay = math.random(self.TargetSkill[_targetskill].DelayOn[1], self.TargetSkill[_targetskill].DelayOn[2]) 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 delay > _tti then delay = delay / 2 end -- speed up
if _tti > (3*delay) then delay = (_tti / 2) * 0.9 end -- shot from afar if _tti > (3*delay) then delay = (_tti / 2) * 0.9 end -- shot from afar
local SuppressionStartTime = timer.getTime() + delay local SuppressionStartTime = timer.getTime() + delay
local SuppressionEndTime = timer.getTime() + _tti + self.Padding local SuppressionEndTime = timer.getTime() + _tti + self.Padding
if not self.SuppressedGroups[_targetgroupname] then if not self.SuppressedGroups[_targetgroupname] then
self:T(string.format("*** SEAD - %s | Parameters TTI %ds | Switch-Off in %ds",_targetgroupname,_tti,delay)) self:T(string.format("*** SEAD - %s | Parameters TTI %ds | Switch-Off in %ds",_targetgroupname,_tti,delay))
timer.scheduleFunction(SuppressionStart,{_targetgroup,_targetgroupname},SuppressionStartTime) timer.scheduleFunction(SuppressionStart,{_targetgroup,_targetgroupname},SuppressionStartTime)
timer.scheduleFunction(SuppressionStop,{_targetgroup,_targetgroupname},SuppressionEndTime) timer.scheduleFunction(SuppressionStop,{_targetgroup,_targetgroupname},SuppressionEndTime)
self.SuppressedGroups[_targetgroupname] = true self.SuppressedGroups[_targetgroupname] = true
if self.UseCallBack then
local object = self.CallBack
object:SeadSuppressionPlanned(_targetgroup,_targetgroupname,SuppressionStartTime,SuppressionEndTime)
end
end end
end end
end end
end end
end end
end end
return self
end end

View File

@@ -1601,7 +1601,7 @@ WAREHOUSE = {
-- @field #number range Range of the unit in meters. -- @field #number range Range of the unit in meters.
-- @field #number speedmax Maximum speed in km/h the group can do. -- @field #number speedmax Maximum speed in km/h the group can do.
-- @field #number size Maximum size in length and with of the asset in meters. -- @field #number size Maximum size in length and with of the asset in meters.
-- @field #number weight The weight of the whole asset group in kilo gramms. -- @field #number weight The weight of the whole asset group in kilograms.
-- @field DCS#Object.Desc DCSdesc All DCS descriptors. -- @field DCS#Object.Desc DCSdesc All DCS descriptors.
-- @field #WAREHOUSE.Attribute attribute Generalized attribute of the group. -- @field #WAREHOUSE.Attribute attribute Generalized attribute of the group.
-- @field #table cargobay Array of cargo bays of all units in an asset group. -- @field #table cargobay Array of cargo bays of all units in an asset group.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -802,7 +802,7 @@ function RESCUEHELO:_OnEventCrashOrEject(EventData)
-- Debug. -- Debug.
local text=string.format("Unit %s crashed or ejected.", unitname) local text=string.format("Unit %s crashed or ejected.", unitname)
MESSAGE:New(text, 10, "DEBUG"):ToAllIf(self.Debug) MESSAGE:New(text, 10, "DEBUG"):ToAllIf(self.Debug)
self:I(self.lid..text) self:T(self.lid..text)
-- Get coordinate of unit. -- Get coordinate of unit.
local coord=unit:GetCoordinate() local coord=unit:GetCoordinate()

View File

@@ -88,10 +88,19 @@
-- --
-- Use a specific "culture" with the @{#MSRS.SetCulture} function, e.g. `:SetCulture("en-US")` or `:SetCulture("de-DE")`. -- Use a specific "culture" with the @{#MSRS.SetCulture} function, e.g. `:SetCulture("en-US")` or `:SetCulture("de-DE")`.
-- --
-- ## Set Google
--
-- Use Google's text-to-speech engine with the @{#MSRS.SetGoogle} function, e.g. ':SetGoogle()'.
-- By enabling this it also allows you to utilize SSML in your text for added flexibilty.
-- For more information on setting up a cloud account, visit: https://cloud.google.com/text-to-speech
-- Google's supported SSML reference: https://cloud.google.com/text-to-speech/docs/ssml
--
-- ## Set Voice -- ## Set Voice
-- --
-- Use a specifc voice with the @{#MSRS.SetVoice} function, e.g, `:SetVoice("Microsoft Hedda Desktop")`. -- Use a specifc voice with the @{#MSRS.SetVoice} function, e.g, `:SetVoice("Microsoft Hedda Desktop")`.
-- Note that this must be installed on your windows system. -- Note that this must be installed on your windows system.
-- If enabling SetGoogle(), you can use voices provided by Google
-- Google's supported voices: https://cloud.google.com/text-to-speech/docs/voices
-- --
-- ## Set Coordinate -- ## Set Coordinate
-- --
@@ -678,7 +687,7 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp
-- Set google. -- Set google.
if self.google then if self.google then
command=command..string.format(' -G "%s"', self.google) command=command..string.format(' --ssml -G "%s"', self.google)
end end
-- Debug output. -- Debug output.

View File

@@ -1,13 +1,13 @@
--- **Tasking** - The TASK_A2A models tasks for players in Air to Air engagements. --- **Tasking** - The TASK_A2A models tasks for players in Air to Air engagements.
-- --
-- === -- ===
-- --
-- ### Author: **FlightControl** -- ### Author: **FlightControl**
-- --
-- ### Contributions: -- ### Contributions:
-- --
-- === -- ===
-- --
-- @module Tasking.Task_A2A -- @module Tasking.Task_A2A
-- @image MOOSE.JPG -- @image MOOSE.JPG
@@ -35,12 +35,12 @@ do -- TASK_A2A
-- * @{#TASK_A2A.SetScoreOnDestroy}(): Set a score when a target in scope of the A2A attack, has been destroyed. -- * @{#TASK_A2A.SetScoreOnDestroy}(): Set a score when a target in scope of the A2A attack, has been destroyed.
-- * @{#TASK_A2A.SetScoreOnSuccess}(): Set a score when all the targets in scope of the A2A attack, have been destroyed. -- * @{#TASK_A2A.SetScoreOnSuccess}(): Set a score when all the targets in scope of the A2A attack, have been destroyed.
-- * @{#TASK_A2A.SetPenaltyOnFailed}(): Set a penalty when the A2A attack has failed. -- * @{#TASK_A2A.SetPenaltyOnFailed}(): Set a penalty when the A2A attack has failed.
-- --
-- @field #TASK_A2A -- @field #TASK_A2A
TASK_A2A = { TASK_A2A = {
ClassName = "TASK_A2A", ClassName = "TASK_A2A"
} }
--- Instantiates a new TASK_A2A. --- Instantiates a new TASK_A2A.
-- @param #TASK_A2A self -- @param #TASK_A2A self
-- @param Tasking.Mission#MISSION Mission -- @param Tasking.Mission#MISSION Mission
@@ -54,51 +54,49 @@ do -- TASK_A2A
function TASK_A2A:New( Mission, SetAttack, TaskName, TargetSetUnit, TaskType, TaskBriefing ) function TASK_A2A:New( Mission, SetAttack, TaskName, TargetSetUnit, TaskType, TaskBriefing )
local self = BASE:Inherit( self, TASK:New( Mission, SetAttack, TaskName, TaskType, TaskBriefing ) ) -- Tasking.Task#TASK_A2A local self = BASE:Inherit( self, TASK:New( Mission, SetAttack, TaskName, TaskType, TaskBriefing ) ) -- Tasking.Task#TASK_A2A
self:F() self:F()
self.TargetSetUnit = TargetSetUnit self.TargetSetUnit = TargetSetUnit
self.TaskType = TaskType self.TaskType = TaskType
local Fsm = self:GetUnitProcess() local Fsm = self:GetUnitProcess()
Fsm:AddTransition( "Assigned", "RouteToRendezVous", "RoutingToRendezVous" ) Fsm:AddTransition( "Assigned", "RouteToRendezVous", "RoutingToRendezVous" )
Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtRendezVous" } ) Fsm:AddProcess( "RoutingToRendezVous", "RouteToRendezVousPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtRendezVous" } )
Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtRendezVous" } ) Fsm:AddProcess( "RoutingToRendezVous", "RouteToRendezVousZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtRendezVous" } )
Fsm:AddTransition( { "Arrived", "RoutingToRendezVous" }, "ArriveAtRendezVous", "ArrivedAtRendezVous" ) Fsm:AddTransition( { "Arrived", "RoutingToRendezVous" }, "ArriveAtRendezVous", "ArrivedAtRendezVous" )
Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "Engage", "Engaging" ) Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "Engage", "Engaging" )
Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "HoldAtRendezVous", "HoldingAtRendezVous" ) Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "HoldAtRendezVous", "HoldingAtRendezVous" )
Fsm:AddProcess ( "Engaging", "Account", ACT_ACCOUNT_DEADS:New(), {} ) Fsm:AddProcess( "Engaging", "Account", ACT_ACCOUNT_DEADS:New(), {} )
Fsm:AddTransition( "Engaging", "RouteToTarget", "Engaging" ) Fsm:AddTransition( "Engaging", "RouteToTarget", "Engaging" )
Fsm:AddProcess( "Engaging", "RouteToTargetZone", ACT_ROUTE_ZONE:New(), {} ) Fsm:AddProcess( "Engaging", "RouteToTargetZone", ACT_ROUTE_ZONE:New(), {} )
Fsm:AddProcess( "Engaging", "RouteToTargetPoint", ACT_ROUTE_POINT:New(), {} ) Fsm:AddProcess( "Engaging", "RouteToTargetPoint", ACT_ROUTE_POINT:New(), {} )
Fsm:AddTransition( "Engaging", "RouteToTargets", "Engaging" ) Fsm:AddTransition( "Engaging", "RouteToTargets", "Engaging" )
-- Fsm:AddTransition( "Accounted", "DestroyedAll", "Accounted" ) -- Fsm:AddTransition( "Accounted", "DestroyedAll", "Accounted" )
-- Fsm:AddTransition( "Accounted", "Success", "Success" ) -- Fsm:AddTransition( "Accounted", "Success", "Success" )
Fsm:AddTransition( "Rejected", "Reject", "Aborted" ) Fsm:AddTransition( "Rejected", "Reject", "Aborted" )
Fsm:AddTransition( "Failed", "Fail", "Failed" ) Fsm:AddTransition( "Failed", "Fail", "Failed" )
---- @param #FSM_PROCESS self -- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
-- @param #TASK_CARGO Task -- @param #TASK_CARGO Task
function Fsm:OnLeaveAssigned( TaskUnit, Task ) function Fsm:OnLeaveAssigned( TaskUnit, Task )
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
self:SelectAction() self:SelectAction()
end end
--- Test --- Test
-- @param #FSM_PROCESS self -- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_A2A#TASK_A2A Task -- @param Tasking.Task_A2A#TASK_A2A Task
function Fsm:onafterRouteToRendezVous( TaskUnit, Task ) function Fsm:onafterRouteToRendezVous( TaskUnit, Task )
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
-- Determine the first Unit from the self.RendezVousSetUnit -- Determine the first Unit from the self.RendezVousSetUnit
if Task:GetRendezVousZone( TaskUnit ) then if Task:GetRendezVousZone( TaskUnit ) then
self:__RouteToRendezVousZone( 0.1 ) self:__RouteToRendezVousZone( 0.1 )
else else
@@ -110,36 +108,36 @@ do -- TASK_A2A
end end
end end
--- Test --- Test
-- @param #FSM_PROCESS self -- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task#TASK_A2A Task -- @param Tasking.Task#TASK_A2A Task
function Fsm:OnAfterArriveAtRendezVous( TaskUnit, Task ) function Fsm:OnAfterArriveAtRendezVous( TaskUnit, Task )
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
-- Determine the first Unit from the self.TargetSetUnit -- Determine the first Unit from the self.TargetSetUnit
self:__Engage( 0.1 ) self:__Engage( 0.1 )
end end
--- Test --- Test
-- @param #FSM_PROCESS self -- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task#TASK_A2A Task -- @param Tasking.Task#TASK_A2A Task
function Fsm:onafterEngage( TaskUnit, Task ) function Fsm:onafterEngage( TaskUnit, Task )
self:F( { self } ) self:F( { self } )
self:__Account( 0.1 ) self:__Account( 0.1 )
self:__RouteToTarget(0.1 ) self:__RouteToTarget( 0.1 )
self:__RouteToTargets( -10 ) self:__RouteToTargets( -10 )
end end
--- Test --- Test
-- @param #FSM_PROCESS self -- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_A2A#TASK_A2A Task -- @param Tasking.Task_A2A#TASK_A2A Task
function Fsm:onafterRouteToTarget( TaskUnit, Task ) function Fsm:onafterRouteToTarget( TaskUnit, Task )
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
-- Determine the first Unit from the self.TargetSetUnit -- Determine the first Unit from the self.TargetSetUnit
if Task:GetTargetZone( TaskUnit ) then if Task:GetTargetZone( TaskUnit ) then
self:__RouteToTargetZone( 0.1 ) self:__RouteToTargetZone( 0.1 )
else else
@@ -152,8 +150,8 @@ do -- TASK_A2A
self:__RouteToTargetPoint( 0.1 ) self:__RouteToTargetPoint( 0.1 )
end end
end end
--- Test --- Test
-- @param #FSM_PROCESS self -- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_A2A#TASK_A2A Task -- @param Tasking.Task_A2A#TASK_A2A Task
@@ -165,20 +163,18 @@ do -- TASK_A2A
end end
self:__RouteToTargets( -10 ) self:__RouteToTargets( -10 )
end end
return self return self
end end
--- @param #TASK_A2A self --- @param #TASK_A2A self
-- @param Core.Set#SET_UNIT TargetSetUnit The set of targets. -- @param Core.Set#SET_UNIT TargetSetUnit The set of targets.
function TASK_A2A:SetTargetSetUnit( TargetSetUnit ) function TASK_A2A:SetTargetSetUnit( TargetSetUnit )
self.TargetSetUnit = TargetSetUnit self.TargetSetUnit = TargetSetUnit
end end
--- @param #TASK_A2A self --- @param #TASK_A2A self
function TASK_A2A:GetPlannedMenuText() function TASK_A2A:GetPlannedMenuText()
return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )" return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )"
@@ -188,34 +184,32 @@ do -- TASK_A2A
-- @param Core.Point#COORDINATE RendezVousCoordinate The Coordinate object referencing to the 2D point where the RendezVous point is located on the map. -- @param Core.Point#COORDINATE RendezVousCoordinate The Coordinate object referencing to the 2D point where the RendezVous point is located on the map.
-- @param #number RendezVousRange The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point. -- @param #number RendezVousRange The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point.
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
function TASK_A2A:SetRendezVousCoordinate( RendezVousCoordinate, RendezVousRange, TaskUnit ) function TASK_A2A:SetRendezVousCoordinate( RendezVousCoordinate, RendezVousRange, TaskUnit )
local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ProcessUnit = self:GetUnitProcess( TaskUnit )
local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT
ActRouteRendezVous:SetCoordinate( RendezVousCoordinate ) ActRouteRendezVous:SetCoordinate( RendezVousCoordinate )
ActRouteRendezVous:SetRange( RendezVousRange ) ActRouteRendezVous:SetRange( RendezVousRange )
end end
--- @param #TASK_A2A self --- @param #TASK_A2A self
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
-- @return Core.Point#COORDINATE The Coordinate object referencing to the 2D point where the RendezVous point is located on the map. -- @return Core.Point#COORDINATE The Coordinate object referencing to the 2D point where the RendezVous point is located on the map.
-- @return #number The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point. -- @return #number The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point.
function TASK_A2A:GetRendezVousCoordinate( TaskUnit ) function TASK_A2A:GetRendezVousCoordinate( TaskUnit )
local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ProcessUnit = self:GetUnitProcess( TaskUnit )
local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT
return ActRouteRendezVous:GetCoordinate(), ActRouteRendezVous:GetRange() return ActRouteRendezVous:GetCoordinate(), ActRouteRendezVous:GetRange()
end end
--- @param #TASK_A2A self --- @param #TASK_A2A self
-- @param Core.Zone#ZONE_BASE RendezVousZone The Zone object where the RendezVous is located on the map. -- @param Core.Zone#ZONE_BASE RendezVousZone The Zone object where the RendezVous is located on the map.
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
function TASK_A2A:SetRendezVousZone( RendezVousZone, TaskUnit ) function TASK_A2A:SetRendezVousZone( RendezVousZone, TaskUnit )
local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ProcessUnit = self:GetUnitProcess( TaskUnit )
local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE
@@ -232,18 +226,17 @@ do -- TASK_A2A
local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE
return ActRouteRendezVous:GetZone() return ActRouteRendezVous:GetZone()
end end
--- @param #TASK_A2A self --- @param #TASK_A2A self
-- @param Core.Point#COORDINATE TargetCoordinate The Coordinate object where the Target is located on the map. -- @param Core.Point#COORDINATE TargetCoordinate The Coordinate object where the Target is located on the map.
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
function TASK_A2A:SetTargetCoordinate( TargetCoordinate, TaskUnit ) function TASK_A2A:SetTargetCoordinate( TargetCoordinate, TaskUnit )
local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ProcessUnit = self:GetUnitProcess( TaskUnit )
local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT
ActRouteTarget:SetCoordinate( TargetCoordinate ) ActRouteTarget:SetCoordinate( TargetCoordinate )
end end
--- @param #TASK_A2A self --- @param #TASK_A2A self
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
@@ -256,18 +249,16 @@ do -- TASK_A2A
return ActRouteTarget:GetCoordinate() return ActRouteTarget:GetCoordinate()
end end
--- @param #TASK_A2A self --- @param #TASK_A2A self
-- @param Core.Zone#ZONE_BASE TargetZone The Zone object where the Target is located on the map. -- @param Core.Zone#ZONE_BASE TargetZone The Zone object where the Target is located on the map.
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
function TASK_A2A:SetTargetZone( TargetZone, Altitude, Heading, TaskUnit ) function TASK_A2A:SetTargetZone( TargetZone, Altitude, Heading, TaskUnit )
local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ProcessUnit = self:GetUnitProcess( TaskUnit )
local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE
ActRouteTarget:SetZone( TargetZone, Altitude, Heading ) ActRouteTarget:SetZone( TargetZone, Altitude, Heading )
end end
--- @param #TASK_A2A self --- @param #TASK_A2A self
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
@@ -281,45 +272,43 @@ do -- TASK_A2A
end end
function TASK_A2A:SetGoalTotal() function TASK_A2A:SetGoalTotal()
self.GoalTotal = self.TargetSetUnit:Count() self.GoalTotal = self.TargetSetUnit:Count()
end end
function TASK_A2A:GetGoalTotal() function TASK_A2A:GetGoalTotal()
return self.GoalTotal return self.GoalTotal
end end
--- Return the relative distance to the target vicinity from the player, in order to sort the targets in the reports per distance from the threats. --- Return the relative distance to the target vicinity from the player, in order to sort the targets in the reports per distance from the threats.
-- @param #TASK_A2A self -- @param #TASK_A2A self
function TASK_A2A:ReportOrder( ReportGroup ) function TASK_A2A:ReportOrder( ReportGroup )
self:UpdateTaskInfo( self.DetectedItem ) self:UpdateTaskInfo( self.DetectedItem )
local Coordinate = self.TaskInfo:GetData( "Coordinate" ) local Coordinate = self.TaskInfo:GetData( "Coordinate" )
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate ) local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
return Distance return Distance
end end
--- This method checks every 10 seconds if the goal has been reached of the task. --- This method checks every 10 seconds if the goal has been reached of the task.
-- @param #TASK_A2A self -- @param #TASK_A2A self
function TASK_A2A:onafterGoal( TaskUnit, From, Event, To ) function TASK_A2A:onafterGoal( TaskUnit, From, Event, To )
local TargetSetUnit = self.TargetSetUnit -- Core.Set#SET_UNIT local TargetSetUnit = self.TargetSetUnit -- Core.Set#SET_UNIT
if TargetSetUnit:Count() == 0 then if TargetSetUnit:Count() == 0 then
self:Success() self:Success()
end end
self:__Goal( -10 ) self:__Goal( -10 )
end end
--- @param #TASK_A2A self --- @param #TASK_A2A self
function TASK_A2A:UpdateTaskInfo( DetectedItem ) function TASK_A2A:UpdateTaskInfo( DetectedItem )
if self:IsStatePlanned() or self:IsStateAssigned() then if self:IsStatePlanned() or self:IsStateAssigned() then
local TargetCoordinate = DetectedItem and self.Detection:GetDetectedItemCoordinate( DetectedItem ) or self.TargetSetUnit:GetFirst():GetCoordinate() local TargetCoordinate = DetectedItem and self.Detection:GetDetectedItemCoordinate( DetectedItem ) or self.TargetSetUnit:GetFirst():GetCoordinate()
self.TaskInfo:AddTaskName( 0, "MSOD" ) self.TaskInfo:AddTaskName( 0, "MSOD" )
self.TaskInfo:AddCoordinate( TargetCoordinate, 1, "SOD" ) self.TaskInfo:AddCoordinate( TargetCoordinate, 1, "SOD" )
@@ -343,12 +332,12 @@ do -- TASK_A2A
end end
end end
self.TaskInfo:AddTargetCount( DetectedItemsCount, 11, "O", true ) self.TaskInfo:AddTargetCount( DetectedItemsCount, 11, "O", true )
self.TaskInfo:AddTargets( DetectedItemsCount, ReportTypes:Text( ", " ), 20, "D", true ) self.TaskInfo:AddTargets( DetectedItemsCount, ReportTypes:Text( ", " ), 20, "D", true )
else else
local DetectedItemsCount = self.TargetSetUnit:Count() local DetectedItemsCount = self.TargetSetUnit:Count()
local DetectedItemsTypes = self.TargetSetUnit:GetTypeNames() local DetectedItemsTypes = self.TargetSetUnit:GetTypeNames()
self.TaskInfo:AddTargetCount( DetectedItemsCount, 11, "O", true ) self.TaskInfo:AddTargetCount( DetectedItemsCount, 11, "O", true )
self.TaskInfo:AddTargets( DetectedItemsCount, DetectedItemsTypes, 20, "D", true ) self.TaskInfo:AddTargets( DetectedItemsCount, DetectedItemsTypes, 20, "D", true )
end end
end end
end end
@@ -359,8 +348,8 @@ do -- TASK_A2A
-- @param Tasking.CommandCenter#COMMANDCENTER CommandCenter The command center. -- @param Tasking.CommandCenter#COMMANDCENTER CommandCenter The command center.
-- @param Wrapper.Group#GROUP TaskGroup The player group. -- @param Wrapper.Group#GROUP TaskGroup The player group.
function TASK_A2A:GetAutoAssignPriority( AutoAssignMethod, CommandCenter, TaskGroup ) function TASK_A2A:GetAutoAssignPriority( AutoAssignMethod, CommandCenter, TaskGroup )
if AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Random then if AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Random then
return math.random( 1, 9 ) return math.random( 1, 9 )
elseif AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Distance then elseif AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Distance then
local Coordinate = self.TaskInfo:GetData( "Coordinate" ) local Coordinate = self.TaskInfo:GetData( "Coordinate" )
@@ -373,8 +362,7 @@ do -- TASK_A2A
return 0 return 0
end end
end end
do -- TASK_A2A_INTERCEPT do -- TASK_A2A_INTERCEPT
@@ -384,44 +372,39 @@ do -- TASK_A2A_INTERCEPT
-- @extends Tasking.Task#TASK -- @extends Tasking.Task#TASK
--- Defines an intercept task for a human player to be executed. --- Defines an intercept task for a human player to be executed.
-- When enemy planes need to be intercepted by human players, use this task type to urgen the players to get out there! -- When enemy planes need to be intercepted by human players, use this task type to urge the players to get out there!
-- --
-- The TASK_A2A_INTERCEPT is used by the @{Tasking.Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create intercept tasks -- The TASK_A2A_INTERCEPT is used by the @{Tasking.Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create intercept tasks
-- based on detected airborne enemy targets intruding friendly airspace. -- based on detected airborne enemy targets intruding friendly airspace.
-- --
-- The task is defined for a @{Tasking.Mission#MISSION}, where a friendly @{Core.Set#SET_GROUP} consisting of GROUPs with one human players each, is intercepting the targets. -- The task is defined for a @{Tasking.Mission#MISSION}, where a friendly @{Core.Set#SET_GROUP} consisting of GROUPs with one human players each, is intercepting the targets.
-- The task is given a name and a briefing, that is used in the menu structure and in the reporting. -- The task is given a name and a briefing, that is used in the menu structure and in the reporting.
-- --
-- @field #TASK_A2A_INTERCEPT -- @field #TASK_A2A_INTERCEPT
TASK_A2A_INTERCEPT = { TASK_A2A_INTERCEPT = {
ClassName = "TASK_A2A_INTERCEPT", ClassName = "TASK_A2A_INTERCEPT"
} }
--- Instantiates a new TASK_A2A_INTERCEPT. --- Instantiates a new TASK_A2A_INTERCEPT.
-- @param #TASK_A2A_INTERCEPT self -- @param #TASK_A2A_INTERCEPT self
-- @param Tasking.Mission#MISSION Mission -- @param Tasking.Mission#MISSION Mission
-- @param Core.Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. -- @param Core.Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned.
-- @param #string TaskName The name of the Task. -- @param #string TaskName The name of the Task.
-- @param Core.Set#SET_UNIT TargetSetUnit -- @param Core.Set#SET_UNIT TargetSetUnit
-- @param #string TaskBriefing The briefing of the task. -- @param #string TaskBriefing The briefing of the task.
-- @return #TASK_A2A_INTERCEPT -- @return #TASK_A2A_INTERCEPT
function TASK_A2A_INTERCEPT:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing ) function TASK_A2A_INTERCEPT:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing )
local self = BASE:Inherit( self, TASK_A2A:New( Mission, SetGroup, TaskName, TargetSetUnit, "INTERCEPT", TaskBriefing ) ) -- #TASK_A2A_INTERCEPT local self = BASE:Inherit( self, TASK_A2A:New( Mission, SetGroup, TaskName, TargetSetUnit, "INTERCEPT", TaskBriefing ) ) -- #TASK_A2A_INTERCEPT
self:F() self:F()
Mission:AddTask( self ) Mission:AddTask( self )
self:SetBriefing( self:SetBriefing( TaskBriefing or "Intercept incoming intruders.\n" )
TaskBriefing or
"Intercept incoming intruders.\n"
)
return self return self
end end
--- Set a score when a target in scope of the A2A attack, has been destroyed . --- Set a score when a target in scope of the A2A attack, has been destroyed.
-- @param #TASK_A2A_INTERCEPT self -- @param #TASK_A2A_INTERCEPT self
-- @param #string PlayerName The name of the player. -- @param #string PlayerName The name of the player.
-- @param #number Score The score in points to be granted when task process has been achieved. -- @param #number Score The score in points to be granted when task process has been achieved.
@@ -433,7 +416,7 @@ do -- TASK_A2A_INTERCEPT
local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has intercepted a target.", Score ) ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has intercepted a target.", Score )
return self return self
end end
@@ -449,7 +432,7 @@ do -- TASK_A2A_INTERCEPT
local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScore( "Success", "All targets have been successfully intercepted!", Score ) ProcessUnit:AddScore( "Success", "All targets have been successfully intercepted!", Score )
return self return self
end end
@@ -465,14 +448,12 @@ do -- TASK_A2A_INTERCEPT
local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScore( "Failed", "The intercept has failed!", Penalty ) ProcessUnit:AddScore( "Failed", "The intercept has failed!", Penalty )
return self return self
end end
end end
do -- TASK_A2A_SWEEP do -- TASK_A2A_SWEEP
--- The TASK_A2A_SWEEP class --- The TASK_A2A_SWEEP class
@@ -484,20 +465,18 @@ do -- TASK_A2A_SWEEP
-- A sweep task needs to be given when targets were detected but somehow the detection was lost. -- A sweep task needs to be given when targets were detected but somehow the detection was lost.
-- Most likely, these enemy planes are hidden in the mountains or are flying under radar. -- Most likely, these enemy planes are hidden in the mountains or are flying under radar.
-- These enemy planes need to be sweeped by human players, and use this task type to urge the players to get out there and find those enemy fighters. -- These enemy planes need to be sweeped by human players, and use this task type to urge the players to get out there and find those enemy fighters.
-- --
-- The TASK_A2A_SWEEP is used by the @{Tasking.Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create sweep tasks -- The TASK_A2A_SWEEP is used by the @{Tasking.Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create sweep tasks
-- based on detected airborne enemy targets intruding friendly airspace, for which the detection has been lost for more than 60 seconds. -- based on detected airborne enemy targets intruding friendly airspace, for which the detection has been lost for more than 60 seconds.
-- --
-- The task is defined for a @{Tasking.Mission#MISSION}, where a friendly @{Core.Set#SET_GROUP} consisting of GROUPs with one human players each, is sweeping the targets. -- The task is defined for a @{Tasking.Mission#MISSION}, where a friendly @{Core.Set#SET_GROUP} consisting of GROUPs with one human players each, is sweeping the targets.
-- The task is given a name and a briefing, that is used in the menu structure and in the reporting. -- The task is given a name and a briefing, that is used in the menu structure and in the reporting.
-- --
-- @field #TASK_A2A_SWEEP -- @field #TASK_A2A_SWEEP
TASK_A2A_SWEEP = { TASK_A2A_SWEEP = {
ClassName = "TASK_A2A_SWEEP", ClassName = "TASK_A2A_SWEEP"
} }
--- Instantiates a new TASK_A2A_SWEEP. --- Instantiates a new TASK_A2A_SWEEP.
-- @param #TASK_A2A_SWEEP self -- @param #TASK_A2A_SWEEP self
-- @param Tasking.Mission#MISSION Mission -- @param Tasking.Mission#MISSION Mission
@@ -509,29 +488,26 @@ do -- TASK_A2A_SWEEP
function TASK_A2A_SWEEP:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing ) function TASK_A2A_SWEEP:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing )
local self = BASE:Inherit( self, TASK_A2A:New( Mission, SetGroup, TaskName, TargetSetUnit, "SWEEP", TaskBriefing ) ) -- #TASK_A2A_SWEEP local self = BASE:Inherit( self, TASK_A2A:New( Mission, SetGroup, TaskName, TargetSetUnit, "SWEEP", TaskBriefing ) ) -- #TASK_A2A_SWEEP
self:F() self:F()
Mission:AddTask( self ) Mission:AddTask( self )
self:SetBriefing( self:SetBriefing( TaskBriefing or "Perform a fighter sweep. Incoming intruders were detected and could be hiding at the location.\n" )
TaskBriefing or
"Perform a fighter sweep. Incoming intruders were detected and could be hiding at the location.\n"
)
return self return self
end end
--- @param #TASK_A2A_SWEEP self --- @param #TASK_A2A_SWEEP self
function TASK_A2A_SWEEP:onafterGoal( TaskUnit, From, Event, To ) function TASK_A2A_SWEEP:onafterGoal( TaskUnit, From, Event, To )
local TargetSetUnit = self.TargetSetUnit -- Core.Set#SET_UNIT local TargetSetUnit = self.TargetSetUnit -- Core.Set#SET_UNIT
if TargetSetUnit:Count() == 0 then if TargetSetUnit:Count() == 0 then
self:Success() self:Success()
end end
self:__Goal( -10 ) self:__Goal( -10 )
end end
--- Set a score when a target in scope of the A2A attack, has been destroyed . --- Set a score when a target in scope of the A2A attack, has been destroyed.
-- @param #TASK_A2A_SWEEP self -- @param #TASK_A2A_SWEEP self
-- @param #string PlayerName The name of the player. -- @param #string PlayerName The name of the player.
-- @param #number Score The score in points to be granted when task process has been achieved. -- @param #number Score The score in points to be granted when task process has been achieved.
@@ -543,7 +519,7 @@ do -- TASK_A2A_SWEEP
local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has sweeped a target.", Score ) ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has sweeped a target.", Score )
return self return self
end end
@@ -559,7 +535,7 @@ do -- TASK_A2A_SWEEP
local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScore( "Success", "All targets have been successfully sweeped!", Score ) ProcessUnit:AddScore( "Success", "All targets have been successfully sweeped!", Score )
return self return self
end end
@@ -575,13 +551,12 @@ do -- TASK_A2A_SWEEP
local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScore( "Failed", "The sweep has failed!", Penalty ) ProcessUnit:AddScore( "Failed", "The sweep has failed!", Penalty )
return self return self
end end
end end
do -- TASK_A2A_ENGAGE do -- TASK_A2A_ENGAGE
--- The TASK_A2A_ENGAGE class --- The TASK_A2A_ENGAGE class
@@ -591,42 +566,37 @@ do -- TASK_A2A_ENGAGE
--- Defines an engage task for a human player to be executed. --- Defines an engage task for a human player to be executed.
-- When enemy planes are close to human players, use this task type is used urge the players to get out there! -- When enemy planes are close to human players, use this task type is used urge the players to get out there!
-- --
-- The TASK_A2A_ENGAGE is used by the @{Tasking.Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create engage tasks -- The TASK_A2A_ENGAGE is used by the @{Tasking.Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create engage tasks
-- based on detected airborne enemy targets intruding friendly airspace. -- based on detected airborne enemy targets intruding friendly airspace.
-- --
-- The task is defined for a @{Tasking.Mission#MISSION}, where a friendly @{Core.Set#SET_GROUP} consisting of GROUPs with one human players each, is engaging the targets. -- The task is defined for a @{Tasking.Mission#MISSION}, where a friendly @{Core.Set#SET_GROUP} consisting of GROUPs with one human players each, is engaging the targets.
-- The task is given a name and a briefing, that is used in the menu structure and in the reporting. -- The task is given a name and a briefing, that is used in the menu structure and in the reporting.
-- --
-- @field #TASK_A2A_ENGAGE -- @field #TASK_A2A_ENGAGE
TASK_A2A_ENGAGE = { TASK_A2A_ENGAGE = {
ClassName = "TASK_A2A_ENGAGE", ClassName = "TASK_A2A_ENGAGE"
} }
--- Instantiates a new TASK_A2A_ENGAGE. --- Instantiates a new TASK_A2A_ENGAGE.
-- @param #TASK_A2A_ENGAGE self -- @param #TASK_A2A_ENGAGE self
-- @param Tasking.Mission#MISSION Mission -- @param Tasking.Mission#MISSION Mission
-- @param Core.Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. -- @param Core.Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned.
-- @param #string TaskName The name of the Task. -- @param #string TaskName The name of the Task.
-- @param Core.Set#SET_UNIT TargetSetUnit -- @param Core.Set#SET_UNIT TargetSetUnit
-- @param #string TaskBriefing The briefing of the task. -- @param #string TaskBriefing The briefing of the task.
-- @return #TASK_A2A_ENGAGE self -- @return #TASK_A2A_ENGAGE self
function TASK_A2A_ENGAGE:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing ) function TASK_A2A_ENGAGE:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing )
local self = BASE:Inherit( self, TASK_A2A:New( Mission, SetGroup, TaskName, TargetSetUnit, "ENGAGE", TaskBriefing ) ) -- #TASK_A2A_ENGAGE local self = BASE:Inherit( self, TASK_A2A:New( Mission, SetGroup, TaskName, TargetSetUnit, "ENGAGE", TaskBriefing ) ) -- #TASK_A2A_ENGAGE
self:F() self:F()
Mission:AddTask( self ) Mission:AddTask( self )
self:SetBriefing( self:SetBriefing( TaskBriefing or "Bogeys are nearby! Players close by are ordered to ENGAGE the intruders!\n" )
TaskBriefing or
"Bogeys are nearby! Players close by are ordered to ENGAGE the intruders!\n"
)
return self return self
end end
--- Set a score when a target in scope of the A2A attack, has been destroyed . --- Set a score when a target in scope of the A2A attack, has been destroyed .
-- @param #TASK_A2A_ENGAGE self -- @param #TASK_A2A_ENGAGE self
-- @param #string PlayerName The name of the player. -- @param #string PlayerName The name of the player.
@@ -639,7 +609,7 @@ do -- TASK_A2A_ENGAGE
local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has engaged and destroyed a target.", Score ) ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has engaged and destroyed a target.", Score )
return self return self
end end
@@ -655,7 +625,7 @@ do -- TASK_A2A_ENGAGE
local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScore( "Success", "All targets have been successfully engaged!", Score ) ProcessUnit:AddScore( "Success", "All targets have been successfully engaged!", Score )
return self return self
end end
@@ -671,7 +641,7 @@ do -- TASK_A2A_ENGAGE
local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScore( "Failed", "The target engagement has failed!", Penalty ) ProcessUnit:AddScore( "Failed", "The target engagement has failed!", Penalty )
return self return self
end end

View File

@@ -1,7 +1,7 @@
--- **Tasking** - Dynamically allocates A2A tasks to human players, based on detected airborne targets through an EWR network. --- **Tasking** - Dynamically allocates A2A tasks to human players, based on detected airborne targets through an EWR network.
-- --
-- **Features:** -- **Features:**
-- --
-- * Dynamically assign tasks to human players based on detected targets. -- * Dynamically assign tasks to human players based on detected targets.
-- * Dynamically change the tasks as the tactical situation evolves during the mission. -- * Dynamically change the tasks as the tactical situation evolves during the mission.
-- * Dynamically assign (CAP) Control Air Patrols tasks for human players to perform CAP. -- * Dynamically assign (CAP) Control Air Patrols tasks for human players to perform CAP.
@@ -11,15 +11,15 @@
-- * Define different ranges to engage upon intruders. -- * Define different ranges to engage upon intruders.
-- * Keep task achievements. -- * Keep task achievements.
-- * Score task achievements. -- * Score task achievements.
-- --
-- === -- ===
-- --
-- ### Author: **FlightControl** -- ### Author: **FlightControl**
-- --
-- ### Contributions: -- ### Contributions:
-- --
-- === -- ===
-- --
-- @module Tasking.Task_A2A_Dispatcher -- @module Tasking.Task_A2A_Dispatcher
-- @image Task_A2A_Dispatcher.JPG -- @image Task_A2A_Dispatcher.JPG
@@ -30,72 +30,72 @@ do -- TASK_A2A_DISPATCHER
-- @extends Tasking.DetectionManager#DETECTION_MANAGER -- @extends Tasking.DetectionManager#DETECTION_MANAGER
--- Orchestrates the dynamic dispatching of tasks upon groups of detected units determined a @{Set} of EWR installation groups. --- Orchestrates the dynamic dispatching of tasks upon groups of detected units determined a @{Set} of EWR installation groups.
-- --
-- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia3.JPG) -- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia3.JPG)
-- --
-- The EWR will detect units, will group them, and will dispatch @{Task}s to groups. Depending on the type of target detected, different tasks will be dispatched. -- The EWR will detect units, will group them, and will dispatch @{Task}s to groups. Depending on the type of target detected, different tasks will be dispatched.
-- Find a summary below describing for which situation a task type is created: -- Find a summary below describing for which situation a task type is created:
-- --
-- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia9.JPG) -- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia9.JPG)
-- --
-- * **INTERCEPT Task**: Is created when the target is known, is detected and within a danger zone, and there is no friendly airborne in range. -- * **INTERCEPT Task**: Is created when the target is known, is detected and within a danger zone, and there is no friendly airborne in range.
-- * **SWEEP Task**: Is created when the target is unknown, was detected and the last position is only known, and within a danger zone, and there is no friendly airborne in range. -- * **SWEEP Task**: Is created when the target is unknown, was detected and the last position is only known, and within a danger zone, and there is no friendly airborne in range.
-- * **ENGAGE Task**: Is created when the target is known, is detected and within a danger zone, and there is a friendly airborne in range, that will receive this task. -- * **ENGAGE Task**: Is created when the target is known, is detected and within a danger zone, and there is a friendly airborne in range, that will receive this task.
-- --
-- ## 1. TASK\_A2A\_DISPATCHER constructor: -- ## 1. TASK\_A2A\_DISPATCHER constructor:
-- --
-- The @{#TASK_A2A_DISPATCHER.New}() method creates a new TASK\_A2A\_DISPATCHER instance. -- The @{#TASK_A2A_DISPATCHER.New}() method creates a new TASK\_A2A\_DISPATCHER instance.
-- --
-- ### 1.1. Define or set the **Mission**: -- ### 1.1. Define or set the **Mission**:
-- --
-- Tasking is executed to accomplish missions. Therefore, a MISSION object needs to be given as the first parameter. -- Tasking is executed to accomplish missions. Therefore, a MISSION object needs to be given as the first parameter.
-- --
-- local HQ = GROUP:FindByName( "HQ", "Bravo" ) -- local HQ = GROUP:FindByName( "HQ", "Bravo" )
-- local CommandCenter = COMMANDCENTER:New( HQ, "Lima" ) -- local CommandCenter = COMMANDCENTER:New( HQ, "Lima" )
-- local Mission = MISSION:New( CommandCenter, "A2A Mission", "High", "Watch the air enemy units being detected.", coalition.side.RED ) -- local Mission = MISSION:New( CommandCenter, "A2A Mission", "High", "Watch the air enemy units being detected.", coalition.side.RED )
-- --
-- Missions are governed by COMMANDCENTERS, so, ensure you have a COMMANDCENTER object installed and setup within your mission. -- Missions are governed by COMMANDCENTERS, so, ensure you have a COMMANDCENTER object installed and setup within your mission.
-- Create the MISSION object, and hook it under the command center. -- Create the MISSION object, and hook it under the command center.
-- --
-- ### 1.2. Build a set of the groups seated by human players: -- ### 1.2. Build a set of the groups seated by human players:
-- --
-- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia6.JPG) -- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia6.JPG)
-- --
-- A set or collection of the groups wherein human players can be seated, these can be clients or units that can be joined as a slot or jumping into. -- A set or collection of the groups wherein human players can be seated, these can be clients or units that can be joined as a slot or jumping into.
-- --
-- local AttackGroups = SET_GROUP:New():FilterCoalitions( "red" ):FilterPrefixes( "Defender" ):FilterStart() -- local AttackGroups = SET_GROUP:New():FilterCoalitions( "red" ):FilterPrefixes( "Defender" ):FilterStart()
-- --
-- The set is built using the SET_GROUP class. Apply any filter criteria to identify the correct groups for your mission. -- The set is built using the SET_GROUP class. Apply any filter criteria to identify the correct groups for your mission.
-- Only these slots or units will be able to execute the mission and will receive tasks for this mission, once available. -- Only these slots or units will be able to execute the mission and will receive tasks for this mission, once available.
-- --
-- ### 1.3. Define the **EWR network**: -- ### 1.3. Define the **EWR network**:
-- --
-- As part of the TASK\_A2A\_DISPATCHER constructor, an EWR network must be given as the third parameter. -- As part of the TASK\_A2A\_DISPATCHER constructor, an EWR network must be given as the third parameter.
-- An EWR network, or, Early Warning Radar network, is used to early detect potential airborne targets and to understand the position of patrolling targets of the enemy. -- An EWR network, or, Early Warning Radar network, is used to early detect potential airborne targets and to understand the position of patrolling targets of the enemy.
-- --
-- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia5.JPG) -- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia5.JPG)
-- --
-- Typically EWR networks are setup using 55G6 EWR, 1L13 EWR, Hawk sr and Patriot str ground based radar units. -- Typically EWR networks are setup using 55G6 EWR, 1L13 EWR, Hawk sr and Patriot str ground based radar units.
-- These radars have different ranges and 55G6 EWR and 1L13 EWR radars are Eastern Bloc units (eg Russia, Ukraine, Georgia) while the Hawk and Patriot radars are Western (eg US). -- These radars have different ranges and 55G6 EWR and 1L13 EWR radars are Eastern Bloc units (eg Russia, Ukraine, Georgia) while the Hawk and Patriot radars are Western (eg US).
-- Additionally, ANY other radar capable unit can be part of the EWR network! Also AWACS airborne units, planes, helicopters can help to detect targets, as long as they have radar. -- Additionally, ANY other radar capable unit can be part of the EWR network! Also AWACS airborne units, planes, helicopters can help to detect targets, as long as they have radar.
-- The position of these units is very important as they need to provide enough coverage -- The position of these units is very important as they need to provide enough coverage
-- to pick up enemy aircraft as they approach so that CAP and GCI flights can be tasked to intercept them. -- to pick up enemy aircraft as they approach so that CAP and GCI flights can be tasked to intercept them.
-- --
-- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia7.JPG) -- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia7.JPG)
-- --
-- Additionally in a hot war situation where the border is no longer respected the placement of radars has a big effect on how fast the war escalates. -- Additionally in a hot war situation where the border is no longer respected the placement of radars has a big effect on how fast the war escalates.
-- For example if they are a long way forward and can detect enemy planes on the ground and taking off -- For example if they are a long way forward and can detect enemy planes on the ground and taking off
-- they will start to vector CAP and GCI flights to attack them straight away which will immediately draw a response from the other coalition. -- they will start to vector CAP and GCI flights to attack them straight away which will immediately draw a response from the other coalition.
-- Having the radars further back will mean a slower escalation because fewer targets will be detected and -- Having the radars further back will mean a slower escalation because fewer targets will be detected and
-- therefore less CAP and GCI flights will spawn and this will tend to make just the border area active rather than a melee over the whole map. -- therefore less CAP and GCI flights will spawn and this will tend to make just the border area active rather than a melee over the whole map.
-- It all depends on what the desired effect is. -- It all depends on what the desired effect is.
-- --
-- EWR networks are **dynamically constructed**, that is, they form part of the @{Functional.Detection#DETECTION_BASE} object that is given as the input parameter of the TASK\_A2A\_DISPATCHER class. -- EWR networks are **dynamically constructed**, that is, they form part of the @{Functional.Detection#DETECTION_BASE} object that is given as the input parameter of the TASK\_A2A\_DISPATCHER class.
-- By defining in a **smart way the names or name prefixes of the groups** with EWR capable units, these groups will be **automatically added or deleted** from the EWR network, -- By defining in a **smart way the names or name prefixes of the groups** with EWR capable units, these groups will be **automatically added or deleted** from the EWR network,
-- increasing or decreasing the radar coverage of the Early Warning System. -- increasing or decreasing the radar coverage of the Early Warning System.
-- --
-- See the following example to setup an EWR network containing EWR stations and AWACS. -- See the following example to setup an EWR network containing EWR stations and AWACS.
-- --
-- local EWRSet = SET_GROUP:New():FilterPrefixes( "EWR" ):FilterCoalitions("red"):FilterStart() -- local EWRSet = SET_GROUP:New():FilterPrefixes( "EWR" ):FilterCoalitions("red"):FilterStart()
-- --
-- local EWRDetection = DETECTION_AREAS:New( EWRSet, 6000 ) -- local EWRDetection = DETECTION_AREAS:New( EWRSet, 6000 )
@@ -104,50 +104,50 @@ do -- TASK_A2A_DISPATCHER
-- --
-- -- Setup the A2A dispatcher, and initialize it. -- -- Setup the A2A dispatcher, and initialize it.
-- A2ADispatcher = TASK_A2A_DISPATCHER:New( Mission, AttackGroups, EWRDetection ) -- A2ADispatcher = TASK_A2A_DISPATCHER:New( Mission, AttackGroups, EWRDetection )
-- --
-- The above example creates a SET_GROUP instance, and stores this in the variable (object) **EWRSet**. -- The above example creates a SET_GROUP instance, and stores this in the variable (object) **EWRSet**.
-- **EWRSet** is then being configured to filter all active groups with a group name starting with **EWR** to be included in the Set. -- **EWRSet** is then being configured to filter all active groups with a group name starting with **EWR** to be included in the Set.
-- **EWRSet** is then being ordered to start the dynamic filtering. Note that any destroy or new spawn of a group with the above names will be removed or added to the Set. -- **EWRSet** is then being ordered to start the dynamic filtering. Note that any destroy or new spawn of a group with the above names will be removed or added to the Set.
-- Then a new **EWRDetection** object is created from the class DETECTION_AREAS. A grouping radius of 6000 is choosen, which is 6km. -- Then a new **EWRDetection** object is created from the class DETECTION_AREAS. A grouping radius of 6000 is chosen, which is 6 km.
-- The **EWRDetection** object is then passed to the @{#TASK_A2A_DISPATCHER.New}() method to indicate the EWR network configuration and setup the A2A tasking and detection mechanism. -- The **EWRDetection** object is then passed to the @{#TASK_A2A_DISPATCHER.New}() method to indicate the EWR network configuration and setup the A2A tasking and detection mechanism.
-- --
-- ### 2. Define the detected **target grouping radius**: -- ### 2. Define the detected **target grouping radius**:
-- --
-- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia8.JPG) -- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia8.JPG)
-- --
-- The target grouping radius is a property of the Detection object, that was passed to the AI\_A2A\_DISPATCHER object, but can be changed. -- The target grouping radius is a property of the Detection object, that was passed to the AI\_A2A\_DISPATCHER object, but can be changed.
-- The grouping radius should not be too small, but also depends on the types of planes and the era of the simulation. -- The grouping radius should not be too small, but also depends on the types of planes and the era of the simulation.
-- Fast planes like in the 80s, need a larger radius than WWII planes. -- Fast planes like in the 80s, need a larger radius than WWII planes.
-- Typically I suggest to use 30000 for new generation planes and 10000 for older era aircraft. -- Typically I suggest to use 30000 for new generation planes and 10000 for older era aircraft.
-- --
-- Note that detected targets are constantly re-grouped, that is, when certain detected aircraft are moving further than the group radius, then these aircraft will become a separate -- Note that detected targets are constantly re-grouped, that is, when certain detected aircraft are moving further than the group radius, then these aircraft will become a separate
-- group being detected. This may result in additional GCI being started by the dispatcher! So don't make this value too small! -- group being detected. This may result in additional GCI being started by the dispatcher! So don't make this value too small!
-- --
-- ## 3. Set the **Engage radius**: -- ## 3. Set the **Engage radius**:
-- --
-- Define the radius to engage any target by airborne friendlies, which are executing cap or returning from an intercept mission. -- Define the radius to engage any target by airborne friendlies, which are executing cap or returning from an intercept mission.
-- --
-- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia11.JPG) -- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia11.JPG)
-- --
-- So, if there is a target area detected and reported, -- So, if there is a target area detected and reported,
-- then any friendlies that are airborne near this target area, -- then any friendlies that are airborne near this target area,
-- will be commanded to (re-)engage that target when available (if no other tasks were commanded). -- will be commanded to (re-)engage that target when available (if no other tasks were commanded).
-- For example, if 100000 is given as a value, then any friendly that is airborne within 100km from the detected target, -- For example, if 100000 is given as a value, then any friendly that is airborne within 100km from the detected target,
-- will be considered to receive the command to engage that target area. -- will be considered to receive the command to engage that target area.
-- You need to evaluate the value of this parameter carefully. -- You need to evaluate the value of this parameter carefully.
-- If too small, more intercept missions may be triggered upon detected target areas. -- If too small, more intercept missions may be triggered upon detected target areas.
-- If too large, any airborne cap may not be able to reach the detected target area in time, because it is too far. -- If too large, any airborne cap may not be able to reach the detected target area in time, because it is too far.
-- --
-- ## 4. Set **Scoring** and **Messages**: -- ## 4. Set **Scoring** and **Messages**:
-- --
-- The TASK\_A2A\_DISPATCHER is a state machine. It triggers the event Assign when a new player joins a @{Task} dispatched by the TASK\_A2A\_DISPATCHER. -- The TASK\_A2A\_DISPATCHER is a state machine. It triggers the event Assign when a new player joins a @{Task} dispatched by the TASK\_A2A\_DISPATCHER.
-- An _event handler_ can be defined to catch the **Assign** event, and add **additional processing** to set _scoring_ and to _define messages_, -- An _event handler_ can be defined to catch the **Assign** event, and add **additional processing** to set _scoring_ and to _define messages_,
-- when the player reaches certain achievements in the task. -- when the player reaches certain achievements in the task.
-- --
-- The prototype to handle the **Assign** event needs to be developed as follows: -- The prototype to handle the **Assign** event needs to be developed as follows:
-- --
-- TaskDispatcher = TASK_A2A_DISPATCHER:New( ... ) -- TaskDispatcher = TASK_A2A_DISPATCHER:New( ... )
-- --
-- --- @param #TaskDispatcher self -- --- @param #TaskDispatcher self
-- -- @param #string From Contains the name of the state from where the Event was triggered. -- -- @param #string From Contains the name of the state from where the Event was triggered.
-- -- @param #string Event Contains the name of the event that was triggered. In this case Assign. -- -- @param #string Event Contains the name of the event that was triggered. In this case Assign.
@@ -160,22 +160,22 @@ do -- TASK_A2A_DISPATCHER
-- Task:SetScoreOnSuccess( PlayerName, 200, TaskUnit ) -- Task:SetScoreOnSuccess( PlayerName, 200, TaskUnit )
-- Task:SetScoreOnFail( PlayerName, -100, TaskUnit ) -- Task:SetScoreOnFail( PlayerName, -100, TaskUnit )
-- end -- end
-- --
-- The **OnAfterAssign** method (function) is added to the TaskDispatcher object. -- The **OnAfterAssign** method (function) is added to the TaskDispatcher object.
-- This method will be called when a new player joins a unit in the set of groups in scope of the dispatcher. -- This method will be called when a new player joins a unit in the set of groups in scope of the dispatcher.
-- So, this method will be called only **ONCE** when a player joins a unit in scope of the task. -- So, this method will be called only **ONCE** when a player joins a unit in scope of the task.
-- --
-- The TASK class implements various methods to additional **set scoring** for player achievements: -- The TASK class implements various methods to additional **set scoring** for player achievements:
-- --
-- * @{Tasking.Task#TASK.SetScoreOnProgress}() will add additional scores when a player achieves **Progress** while executing the task. -- * @{Tasking.Task#TASK.SetScoreOnProgress}() will add additional scores when a player achieves **Progress** while executing the task.
-- Examples of **task progress** can be destroying units, arriving at zones etc. -- Examples of **task progress** can be destroying units, arriving at zones etc.
-- --
-- * @{Tasking.Task#TASK.SetScoreOnSuccess}() will add additional scores when the task goes into **Success** state. -- * @{Tasking.Task#TASK.SetScoreOnSuccess}() will add additional scores when the task goes into **Success** state.
-- This means the **task has been successfully completed**. -- This means the **task has been successfully completed**.
-- --
-- * @{Tasking.Task#TASK.SetScoreOnSuccess}() will add additional (negative) scores when the task goes into **Failed** state. -- * @{Tasking.Task#TASK.SetScoreOnSuccess}() will add additional (negative) scores when the task goes into **Failed** state.
-- This means the **task has not been successfully completed**, and the scores must be given with a negative value! -- This means the **task has not been successfully completed**, and the scores must be given with a negative value!
-- --
-- @field #TASK_A2A_DISPATCHER -- @field #TASK_A2A_DISPATCHER
TASK_A2A_DISPATCHER = { TASK_A2A_DISPATCHER = {
ClassName = "TASK_A2A_DISPATCHER", ClassName = "TASK_A2A_DISPATCHER",
@@ -184,8 +184,7 @@ do -- TASK_A2A_DISPATCHER
Tasks = {}, Tasks = {},
SweepZones = {}, SweepZones = {},
} }
--- TASK_A2A_DISPATCHER constructor. --- TASK_A2A_DISPATCHER constructor.
-- @param #TASK_A2A_DISPATCHER self -- @param #TASK_A2A_DISPATCHER self
-- @param Tasking.Mission#MISSION Mission The mission for which the task dispatching is done. -- @param Tasking.Mission#MISSION Mission The mission for which the task dispatching is done.
@@ -193,22 +192,21 @@ do -- TASK_A2A_DISPATCHER
-- @param Functional.Detection#DETECTION_BASE Detection The detection results that are used to dynamically assign new tasks to human players. -- @param Functional.Detection#DETECTION_BASE Detection The detection results that are used to dynamically assign new tasks to human players.
-- @return #TASK_A2A_DISPATCHER self -- @return #TASK_A2A_DISPATCHER self
function TASK_A2A_DISPATCHER:New( Mission, SetGroup, Detection ) function TASK_A2A_DISPATCHER:New( Mission, SetGroup, Detection )
-- Inherits from DETECTION_MANAGER -- Inherits from DETECTION_MANAGER
local self = BASE:Inherit( self, DETECTION_MANAGER:New( SetGroup, Detection ) ) -- #TASK_A2A_DISPATCHER local self = BASE:Inherit( self, DETECTION_MANAGER:New( SetGroup, Detection ) ) -- #TASK_A2A_DISPATCHER
self.Detection = Detection self.Detection = Detection
self.Mission = Mission self.Mission = Mission
self.FlashNewTask = false self.FlashNewTask = false
-- TODO: Check detection through radar. -- TODO: Check detection through radar.
self.Detection:FilterCategories( Unit.Category.AIRPLANE, Unit.Category.HELICOPTER ) self.Detection:FilterCategories( Unit.Category.AIRPLANE, Unit.Category.HELICOPTER )
self.Detection:InitDetectRadar( true ) self.Detection:InitDetectRadar( true )
self.Detection:SetRefreshTimeInterval( 30 ) self.Detection:SetRefreshTimeInterval( 30 )
self:AddTransition( "Started", "Assign", "Started" ) self:AddTransition( "Started", "Assign", "Started" )
--- OnAfter Transition Handler for Event Assign. --- OnAfter Transition Handler for Event Assign.
-- @function [parent=#TASK_A2A_DISPATCHER] OnAfterAssign -- @function [parent=#TASK_A2A_DISPATCHER] OnAfterAssign
-- @param #TASK_A2A_DISPATCHER self -- @param #TASK_A2A_DISPATCHER self
@@ -220,14 +218,13 @@ do -- TASK_A2A_DISPATCHER
-- @param #string PlayerName -- @param #string PlayerName
self:__Start( 5 ) self:__Start( 5 )
return self return self
end end
--- Define the radius to when an ENGAGE task will be generated for any nearby by airborne friendlies, which are executing cap or returning from an intercept mission. --- Define the radius to when an ENGAGE task will be generated for any nearby by airborne friendlies, which are executing cap or returning from an intercept mission.
-- So, if there is a target area detected and reported, -- So, if there is a target area detected and reported,
-- then any friendlies that are airborne near this target area, -- then any friendlies that are airborne near this target area,
-- will be commanded to (re-)engage that target when available (if no other tasks were commanded). -- will be commanded to (re-)engage that target when available (if no other tasks were commanded).
-- An ENGAGE task will be created for those pilots. -- An ENGAGE task will be created for those pilots.
-- For example, if 100000 is given as a value, then any friendly that is airborne within 100km from the detected target, -- For example, if 100000 is given as a value, then any friendly that is airborne within 100km from the detected target,
@@ -239,27 +236,27 @@ do -- TASK_A2A_DISPATCHER
-- @param #number EngageRadius (Optional, Default = 100000) The radius to report friendlies near the target. -- @param #number EngageRadius (Optional, Default = 100000) The radius to report friendlies near the target.
-- @return #TASK_A2A_DISPATCHER -- @return #TASK_A2A_DISPATCHER
-- @usage -- @usage
-- --
-- -- Set 50km as the radius to engage any target by airborne friendlies. -- -- Set 50km as the radius to engage any target by airborne friendlies.
-- TaskA2ADispatcher:SetEngageRadius( 50000 ) -- TaskA2ADispatcher:SetEngageRadius( 50000 )
-- --
-- -- Set 100km as the radius to engage any target by airborne friendlies. -- -- Set 100km as the radius to engage any target by airborne friendlies.
-- TaskA2ADispatcher:SetEngageRadius() -- 100000 is the default value. -- TaskA2ADispatcher:SetEngageRadius() -- 100000 is the default value.
-- --
function TASK_A2A_DISPATCHER:SetEngageRadius( EngageRadius ) function TASK_A2A_DISPATCHER:SetEngageRadius( EngageRadius )
self.Detection:SetFriendliesRange( EngageRadius or 100000 ) self.Detection:SetFriendliesRange( EngageRadius or 100000 )
return self return self
end end
--- Set flashing player messages on or off --- Set flashing player messages on or off
-- @param #TASK_A2A_DISPATCHER self -- @param #TASK_A2A_DISPATCHER self
-- @param #boolean onoff Set messages on (true) or off (false) -- @param #boolean onoff Set messages on (true) or off (false)
function TASK_A2A_DISPATCHER:SetSendMessages( onoff ) function TASK_A2A_DISPATCHER:SetSendMessages( onoff )
self.FlashNewTask = onoff self.FlashNewTask = onoff
end end
--- Creates an INTERCEPT task when there are targets for it. --- Creates an INTERCEPT task when there are targets for it.
-- @param #TASK_A2A_DISPATCHER self -- @param #TASK_A2A_DISPATCHER self
-- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem
@@ -267,26 +264,25 @@ do -- TASK_A2A_DISPATCHER
-- @return #nil If there are no targets to be set. -- @return #nil If there are no targets to be set.
function TASK_A2A_DISPATCHER:EvaluateINTERCEPT( DetectedItem ) function TASK_A2A_DISPATCHER:EvaluateINTERCEPT( DetectedItem )
self:F( { DetectedItem.ItemID } ) self:F( { DetectedItem.ItemID } )
local DetectedSet = DetectedItem.Set local DetectedSet = DetectedItem.Set
local DetectedZone = DetectedItem.Zone local DetectedZone = DetectedItem.Zone
-- Check if there is at least one UNIT in the DetectedSet is visible. -- Check if there is at least one UNIT in the DetectedSet is visible.
if DetectedItem.IsDetected == true then if DetectedItem.IsDetected == true then
-- Here we're doing something advanced... We're copying the DetectedSet. -- Here we're doing something advanced... We're copying the DetectedSet.
local TargetSetUnit = SET_UNIT:New() local TargetSetUnit = SET_UNIT:New()
TargetSetUnit:SetDatabase( DetectedSet ) TargetSetUnit:SetDatabase( DetectedSet )
TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection. TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection.
return TargetSetUnit return TargetSetUnit
end end
return nil return nil
end end
--- Creates an SWEEP task when there are targets for it. --- Creates an SWEEP task when there are targets for it.
-- @param #TASK_A2A_DISPATCHER self -- @param #TASK_A2A_DISPATCHER self
-- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem
@@ -294,10 +290,9 @@ do -- TASK_A2A_DISPATCHER
-- @return #nil If there are no targets to be set. -- @return #nil If there are no targets to be set.
function TASK_A2A_DISPATCHER:EvaluateSWEEP( DetectedItem ) function TASK_A2A_DISPATCHER:EvaluateSWEEP( DetectedItem )
self:F( { DetectedItem.ItemID } ) self:F( { DetectedItem.ItemID } )
local DetectedSet = DetectedItem.Set
local DetectedZone = DetectedItem.Zone
local DetectedSet = DetectedItem.Set
local DetectedZone = DetectedItem.Zone -- TODO: This seems unused, remove?
if DetectedItem.IsDetected == false then if DetectedItem.IsDetected == false then
@@ -305,14 +300,13 @@ do -- TASK_A2A_DISPATCHER
local TargetSetUnit = SET_UNIT:New() local TargetSetUnit = SET_UNIT:New()
TargetSetUnit:SetDatabase( DetectedSet ) TargetSetUnit:SetDatabase( DetectedSet )
TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection. TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection.
return TargetSetUnit return TargetSetUnit
end end
return nil return nil
end end
--- Creates an ENGAGE task when there are human friendlies airborne near the targets. --- Creates an ENGAGE task when there are human friendlies airborne near the targets.
-- @param #TASK_A2A_DISPATCHER self -- @param #TASK_A2A_DISPATCHER self
-- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem
@@ -320,13 +314,12 @@ do -- TASK_A2A_DISPATCHER
-- @return #nil If there are no targets to be set. -- @return #nil If there are no targets to be set.
function TASK_A2A_DISPATCHER:EvaluateENGAGE( DetectedItem ) function TASK_A2A_DISPATCHER:EvaluateENGAGE( DetectedItem )
self:F( { DetectedItem.ItemID } ) self:F( { DetectedItem.ItemID } )
local DetectedSet = DetectedItem.Set local DetectedSet = DetectedItem.Set
local DetectedZone = DetectedItem.Zone local DetectedZone = DetectedItem.Zone -- TODO: This seems unused, remove?
local PlayersCount, PlayersReport = self:GetPlayerFriendliesNearBy( DetectedItem ) local PlayersCount, PlayersReport = self:GetPlayerFriendliesNearBy( DetectedItem )
-- Only allow ENGAGE when there are Players near the zone, and when the Area has detected items since the last run in a 60 seconds time zone. -- Only allow ENGAGE when there are Players near the zone, and when the Area has detected items since the last run in a 60 seconds time zone.
if PlayersCount > 0 and DetectedItem.IsDetected == true then if PlayersCount > 0 and DetectedItem.IsDetected == true then
@@ -334,16 +327,13 @@ do -- TASK_A2A_DISPATCHER
local TargetSetUnit = SET_UNIT:New() local TargetSetUnit = SET_UNIT:New()
TargetSetUnit:SetDatabase( DetectedSet ) TargetSetUnit:SetDatabase( DetectedSet )
TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection. TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection.
return TargetSetUnit return TargetSetUnit
end end
return nil return nil
end end
--- Evaluates the removal of the Task from the Mission. --- Evaluates the removal of the Task from the Mission.
-- Can only occur when the DetectedItem is Changed AND the state of the Task is "Planned". -- Can only occur when the DetectedItem is Changed AND the state of the Task is "Planned".
-- @param #TASK_A2A_DISPATCHER self -- @param #TASK_A2A_DISPATCHER self
@@ -354,24 +344,24 @@ do -- TASK_A2A_DISPATCHER
-- @param #boolean DetectedItemChange -- @param #boolean DetectedItemChange
-- @return Tasking.Task#TASK -- @return Tasking.Task#TASK
function TASK_A2A_DISPATCHER:EvaluateRemoveTask( Mission, Task, Detection, DetectedItem, DetectedItemIndex, DetectedItemChanged ) function TASK_A2A_DISPATCHER:EvaluateRemoveTask( Mission, Task, Detection, DetectedItem, DetectedItemIndex, DetectedItemChanged )
if Task then if Task then
if Task:IsStatePlanned() then if Task:IsStatePlanned() then
local TaskName = Task:GetName() local TaskName = Task:GetName()
local TaskType = TaskName:match( "(%u+)%.%d+" ) local TaskType = TaskName:match( "(%u+)%.%d+" )
self:T2( { TaskType = TaskType } ) self:T2( { TaskType = TaskType } )
local Remove = false local Remove = false
local IsPlayers = Detection:IsPlayersNearBy( DetectedItem ) local IsPlayers = Detection:IsPlayersNearBy( DetectedItem )
if TaskType == "ENGAGE" then if TaskType == "ENGAGE" then
if IsPlayers == false then if IsPlayers == false then
Remove = true Remove = true
end end
end end
if TaskType == "INTERCEPT" then if TaskType == "INTERCEPT" then
if IsPlayers == true then if IsPlayers == true then
Remove = true Remove = true
@@ -380,7 +370,7 @@ do -- TASK_A2A_DISPATCHER
Remove = true Remove = true
end end
end end
if TaskType == "SWEEP" then if TaskType == "SWEEP" then
if DetectedItem.IsDetected == true then if DetectedItem.IsDetected == true then
Remove = true Remove = true
@@ -388,18 +378,18 @@ do -- TASK_A2A_DISPATCHER
end end
local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT
--DetectedSet:Flush( self ) -- DetectedSet:Flush( self )
--self:F( { DetectedSetCount = DetectedSet:Count() } ) -- self:F( { DetectedSetCount = DetectedSet:Count() } )
if DetectedSet:Count() == 0 then if DetectedSet:Count() == 0 then
Remove = true Remove = true
end end
if DetectedItemChanged == true or Remove then if DetectedItemChanged == true or Remove then
Task = self:RemoveTask( DetectedItemIndex ) Task = self:RemoveTask( DetectedItemIndex )
end end
end end
end end
return Task return Task
end end
@@ -408,10 +398,10 @@ do -- TASK_A2A_DISPATCHER
-- @param DetectedItem -- @param DetectedItem
-- @return #number, Core.CommandCenter#REPORT -- @return #number, Core.CommandCenter#REPORT
function TASK_A2A_DISPATCHER:GetFriendliesNearBy( DetectedItem ) function TASK_A2A_DISPATCHER:GetFriendliesNearBy( DetectedItem )
local DetectedSet = DetectedItem.Set local DetectedSet = DetectedItem.Set
local FriendlyUnitsNearBy = self.Detection:GetFriendliesNearBy( DetectedItem, Unit.Category.AIRPLANE ) local FriendlyUnitsNearBy = self.Detection:GetFriendliesNearBy( DetectedItem, Unit.Category.AIRPLANE )
local FriendlyTypes = {} local FriendlyTypes = {}
local FriendliesCount = 0 local FriendliesCount = 0
@@ -423,27 +413,26 @@ do -- TASK_A2A_DISPATCHER
local FriendlyUnitThreatLevel = FriendlyUnit:GetThreatLevel() local FriendlyUnitThreatLevel = FriendlyUnit:GetThreatLevel()
FriendliesCount = FriendliesCount + 1 FriendliesCount = FriendliesCount + 1
local FriendlyType = FriendlyUnit:GetTypeName() local FriendlyType = FriendlyUnit:GetTypeName()
FriendlyTypes[FriendlyType] = FriendlyTypes[FriendlyType] and ( FriendlyTypes[FriendlyType] + 1 ) or 1 FriendlyTypes[FriendlyType] = FriendlyTypes[FriendlyType] and (FriendlyTypes[FriendlyType] + 1) or 1
if DetectedTreatLevel < FriendlyUnitThreatLevel + 2 then if DetectedTreatLevel < FriendlyUnitThreatLevel + 2 then
end end
end end
end end
end end
--self:F( { FriendliesCount = FriendliesCount } ) -- self:F( { FriendliesCount = FriendliesCount } )
local FriendlyTypesReport = REPORT:New() local FriendlyTypesReport = REPORT:New()
if FriendliesCount > 0 then if FriendliesCount > 0 then
for FriendlyType, FriendlyTypeCount in pairs( FriendlyTypes ) do for FriendlyType, FriendlyTypeCount in pairs( FriendlyTypes ) do
FriendlyTypesReport:Add( string.format("%d of %s", FriendlyTypeCount, FriendlyType ) ) FriendlyTypesReport:Add( string.format( "%d of %s", FriendlyTypeCount, FriendlyType ) )
end end
else else
FriendlyTypesReport:Add( "-" ) FriendlyTypesReport:Add( "-" )
end end
return FriendliesCount, FriendlyTypesReport return FriendliesCount, FriendlyTypesReport
end end
@@ -452,10 +441,10 @@ do -- TASK_A2A_DISPATCHER
-- @param DetectedItem -- @param DetectedItem
-- @return #number, Core.CommandCenter#REPORT -- @return #number, Core.CommandCenter#REPORT
function TASK_A2A_DISPATCHER:GetPlayerFriendliesNearBy( DetectedItem ) function TASK_A2A_DISPATCHER:GetPlayerFriendliesNearBy( DetectedItem )
local DetectedSet = DetectedItem.Set local DetectedSet = DetectedItem.Set
local PlayersNearBy = self.Detection:GetPlayersNearBy( DetectedItem ) local PlayersNearBy = self.Detection:GetPlayersNearBy( DetectedItem )
local PlayerTypes = {} local PlayerTypes = {}
local PlayersCount = 0 local PlayersCount = 0
@@ -464,7 +453,7 @@ do -- TASK_A2A_DISPATCHER
for PlayerUnitName, PlayerUnitData in pairs( PlayersNearBy ) do for PlayerUnitName, PlayerUnitData in pairs( PlayersNearBy ) do
local PlayerUnit = PlayerUnitData -- Wrapper.Unit#UNIT local PlayerUnit = PlayerUnitData -- Wrapper.Unit#UNIT
local PlayerName = PlayerUnit:GetPlayerName() local PlayerName = PlayerUnit:GetPlayerName()
--self:F( { PlayerName = PlayerName, PlayerUnit = PlayerUnit } ) -- self:F( { PlayerName = PlayerName, PlayerUnit = PlayerUnit } )
if PlayerUnit:IsAirPlane() and PlayerName ~= nil then if PlayerUnit:IsAirPlane() and PlayerName ~= nil then
local FriendlyUnitThreatLevel = PlayerUnit:GetThreatLevel() local FriendlyUnitThreatLevel = PlayerUnit:GetThreatLevel()
PlayersCount = PlayersCount + 1 PlayersCount = PlayersCount + 1
@@ -474,20 +463,19 @@ do -- TASK_A2A_DISPATCHER
end end
end end
end end
end end
local PlayerTypesReport = REPORT:New() local PlayerTypesReport = REPORT:New()
if PlayersCount > 0 then if PlayersCount > 0 then
for PlayerName, PlayerType in pairs( PlayerTypes ) do for PlayerName, PlayerType in pairs( PlayerTypes ) do
PlayerTypesReport:Add( string.format('"%s" in %s', PlayerName, PlayerType ) ) PlayerTypesReport:Add( string.format( '"%s" in %s', PlayerName, PlayerType ) )
end end
else else
PlayerTypesReport:Add( "-" ) PlayerTypesReport:Add( "-" )
end end
return PlayersCount, PlayerTypesReport return PlayersCount, PlayerTypesReport
end end
@@ -496,24 +484,23 @@ do -- TASK_A2A_DISPATCHER
self.Tasks[TaskIndex] = nil self.Tasks[TaskIndex] = nil
end end
--- Assigns tasks in relation to the detected items to the @{Core.Set#SET_GROUP}. --- Assigns tasks in relation to the detected items to the @{Core.Set#SET_GROUP}.
-- @param #TASK_A2A_DISPATCHER self -- @param #TASK_A2A_DISPATCHER self
-- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Functional.Detection#DETECTION_BASE} derived object. -- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Functional.Detection#DETECTION_BASE} derived object.
-- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop. -- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop.
function TASK_A2A_DISPATCHER:ProcessDetected( Detection ) function TASK_A2A_DISPATCHER:ProcessDetected( Detection )
self:F() self:F()
local AreaMsg = {} local AreaMsg = {}
local TaskMsg = {} local TaskMsg = {}
local ChangeMsg = {} local ChangeMsg = {}
local Mission = self.Mission local Mission = self.Mission
if Mission:IsIDLE() or Mission:IsENGAGED() then if Mission:IsIDLE() or Mission:IsENGAGED() then
local TaskReport = REPORT:New() local TaskReport = REPORT:New()
-- Checking the task queue for the dispatcher, and removing any obsolete task! -- Checking the task queue for the dispatcher, and removing any obsolete task!
for TaskIndex, TaskData in pairs( self.Tasks ) do for TaskIndex, TaskData in pairs( self.Tasks ) do
local Task = TaskData -- Tasking.Task#TASK local Task = TaskData -- Tasking.Task#TASK
@@ -531,18 +518,18 @@ do -- TASK_A2A_DISPATCHER
-- Now that all obsolete tasks are removed, loop through the detected targets. -- Now that all obsolete tasks are removed, loop through the detected targets.
for DetectedItemID, DetectedItem in pairs( Detection:GetDetectedItems() ) do for DetectedItemID, DetectedItem in pairs( Detection:GetDetectedItems() ) do
local DetectedItem = DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem local DetectedItem = DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem
local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT
local DetectedCount = DetectedSet:Count() local DetectedCount = DetectedSet:Count()
local DetectedZone = DetectedItem.Zone local DetectedZone = DetectedItem.Zone
--self:F( { "Targets in DetectedItem", DetectedItem.ItemID, DetectedSet:Count(), tostring( DetectedItem ) } ) -- self:F( { "Targets in DetectedItem", DetectedItem.ItemID, DetectedSet:Count(), tostring( DetectedItem ) } )
--DetectedSet:Flush( self ) -- DetectedSet:Flush( self )
local DetectedID = DetectedItem.ID local DetectedID = DetectedItem.ID
local TaskIndex = DetectedItem.Index local TaskIndex = DetectedItem.Index
local DetectedItemChanged = DetectedItem.Changed local DetectedItemChanged = DetectedItem.Changed
local Task = self.Tasks[TaskIndex] local Task = self.Tasks[TaskIndex]
Task = self:EvaluateRemoveTask( Mission, Task, Detection, DetectedItem, TaskIndex, DetectedItemChanged ) -- Task will be removed if it is planned and changed. Task = self:EvaluateRemoveTask( Mission, Task, Detection, DetectedItem, TaskIndex, DetectedItemChanged ) -- Task will be removed if it is planned and changed.
@@ -565,7 +552,7 @@ do -- TASK_A2A_DISPATCHER
Task = TASK_A2A_SWEEP:New( Mission, self.SetGroup, string.format( "SWEEP.%03d", DetectedID ), TargetSetUnit ) Task = TASK_A2A_SWEEP:New( Mission, self.SetGroup, string.format( "SWEEP.%03d", DetectedID ), TargetSetUnit )
Task:SetDetection( Detection, DetectedItem ) Task:SetDetection( Detection, DetectedItem )
Task:UpdateTaskInfo( DetectedItem ) Task:UpdateTaskInfo( DetectedItem )
end end
end end
end end
@@ -582,7 +569,7 @@ do -- TASK_A2A_DISPATCHER
function Task.OnEnterCancelled( Task, From, Event, To ) function Task.OnEnterCancelled( Task, From, Event, To )
self:Cancelled( Task ) self:Cancelled( Task )
end end
function Task.OnEnterFailed( Task, From, Event, To ) function Task.OnEnterFailed( Task, From, Event, To )
self:Failed( Task ) self:Failed( Task )
end end
@@ -590,38 +577,38 @@ do -- TASK_A2A_DISPATCHER
function Task.OnEnterAborted( Task, From, Event, To ) function Task.OnEnterAborted( Task, From, Event, To )
self:Aborted( Task ) self:Aborted( Task )
end end
TaskReport:Add( Task:GetName() ) TaskReport:Add( Task:GetName() )
else else
self:F("This should not happen") self:F( "This should not happen" )
end end
end end
if Task then if Task then
local FriendliesCount, FriendliesReport = self:GetFriendliesNearBy( DetectedItem, Unit.Category.AIRPLANE ) local FriendliesCount, FriendliesReport = self:GetFriendliesNearBy( DetectedItem, Unit.Category.AIRPLANE )
Task.TaskInfo:AddText( "Friendlies", string.format( "%d ( %s )", FriendliesCount, FriendliesReport:Text( "," ) ), 40, "MOD" ) Task.TaskInfo:AddText( "Friendlies", string.format( "%d ( %s )", FriendliesCount, FriendliesReport:Text( "," ) ), 40, "MOD" )
local PlayersCount, PlayersReport = self:GetPlayerFriendliesNearBy( DetectedItem ) local PlayersCount, PlayersReport = self:GetPlayerFriendliesNearBy( DetectedItem )
Task.TaskInfo:AddText( "Players", string.format( "%d ( %s )", PlayersCount, PlayersReport:Text( "," ) ), 40, "MOD" ) Task.TaskInfo:AddText( "Players", string.format( "%d ( %s )", PlayersCount, PlayersReport:Text( "," ) ), 40, "MOD" )
end end
-- OK, so the tasking has been done, now delete the changes reported for the area. -- OK, so the tasking has been done, now delete the changes reported for the area.
Detection:AcceptChanges( DetectedItem ) Detection:AcceptChanges( DetectedItem )
end end
-- TODO set menus using the HQ coordinator -- TODO set menus using the HQ coordinator
Mission:GetCommandCenter():SetMenu() Mission:GetCommandCenter():SetMenu()
local TaskText = TaskReport:Text(", ") local TaskText = TaskReport:Text( ", " )
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
if ( not Mission:IsGroupAssigned(TaskGroup) ) and TaskText ~= "" and (self.FlashNewTask) then if (not Mission:IsGroupAssigned( TaskGroup )) and TaskText ~= "" and (self.FlashNewTask) then
Mission:GetCommandCenter():MessageToGroup( string.format( "%s has tasks %s. Subscribe to a task using the radio menu.", Mission:GetShortText(), TaskText ), TaskGroup ) Mission:GetCommandCenter():MessageToGroup( string.format( "%s has tasks %s. Subscribe to a task using the radio menu.", Mission:GetShortText(), TaskText ), TaskGroup )
end end
end end
end end
return true return true
end end

View File

@@ -1,13 +1,13 @@
--- **Tasking** - The TASK_A2G models tasks for players in Air to Ground engagements. --- **Tasking** - The TASK_A2G models tasks for players in Air to Ground engagements.
-- --
-- === -- ===
-- --
-- ### Author: **FlightControl** -- ### Author: **FlightControl**
-- --
-- ### Contributions: -- ### Contributions:
-- --
-- === -- ===
-- --
-- @module Tasking.Task_A2G -- @module Tasking.Task_A2G
-- @image MOOSE.JPG -- @image MOOSE.JPG
@@ -18,29 +18,29 @@ do -- TASK_A2G
-- @field Core.Set#SET_UNIT TargetSetUnit -- @field Core.Set#SET_UNIT TargetSetUnit
-- @extends Tasking.Task#TASK -- @extends Tasking.Task#TASK
--- The TASK_A2G class defines Air To Ground tasks for a @{Set} of Target Units, --- The TASK_A2G class defines Air To Ground tasks for a @{Set} of Target Units,
-- based on the tasking capabilities defined in @{Tasking.Task#TASK}. -- based on the tasking capabilities defined in @{Tasking.Task#TASK}.
-- The TASK_A2G is implemented using a @{Core.Fsm#FSM_TASK}, and has the following statuses: -- The TASK_A2G is implemented using a @{Core.Fsm#FSM_TASK}, and has the following statuses:
-- --
-- * **None**: Start of the process -- * **None**: Start of the process
-- * **Planned**: The A2G task is planned. -- * **Planned**: The A2G task is planned.
-- * **Assigned**: The A2G task is assigned to a @{Wrapper.Group#GROUP}. -- * **Assigned**: The A2G task is assigned to a @{Wrapper.Group#GROUP}.
-- * **Success**: The A2G task is successfully completed. -- * **Success**: The A2G task is successfully completed.
-- * **Failed**: The A2G task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ. -- * **Failed**: The A2G task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ.
-- --
-- ## 1) Set the scoring of achievements in an A2G attack. -- ## 1) Set the scoring of achievements in an A2G attack.
-- --
-- Scoring or penalties can be given in the following circumstances: -- Scoring or penalties can be given in the following circumstances:
-- --
-- * @{#TASK_A2G.SetScoreOnDestroy}(): Set a score when a target in scope of the A2G attack, has been destroyed. -- * @{#TASK_A2G.SetScoreOnDestroy}(): Set a score when a target in scope of the A2G attack, has been destroyed.
-- * @{#TASK_A2G.SetScoreOnSuccess}(): Set a score when all the targets in scope of the A2G attack, have been destroyed. -- * @{#TASK_A2G.SetScoreOnSuccess}(): Set a score when all the targets in scope of the A2G attack, have been destroyed.
-- * @{#TASK_A2G.SetPenaltyOnFailed}(): Set a penalty when the A2G attack has failed. -- * @{#TASK_A2G.SetPenaltyOnFailed}(): Set a penalty when the A2G attack has failed.
-- --
-- @field #TASK_A2G -- @field #TASK_A2G
TASK_A2G = { TASK_A2G = {
ClassName = "TASK_A2G", ClassName = "TASK_A2G"
} }
--- Instantiates a new TASK_A2G. --- Instantiates a new TASK_A2G.
-- @param #TASK_A2G self -- @param #TASK_A2G self
-- @param Tasking.Mission#MISSION Mission -- @param Tasking.Mission#MISSION Mission
@@ -54,53 +54,51 @@ do -- TASK_A2G
function TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskType, TaskBriefing ) function TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskType, TaskBriefing )
local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, TaskType, TaskBriefing ) ) -- Tasking.Task#TASK_A2G local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, TaskType, TaskBriefing ) ) -- Tasking.Task#TASK_A2G
self:F() self:F()
self.TargetSetUnit = TargetSetUnit self.TargetSetUnit = TargetSetUnit
self.TaskType = TaskType self.TaskType = TaskType
local Fsm = self:GetUnitProcess() local Fsm = self:GetUnitProcess()
Fsm:AddTransition( "Assigned", "RouteToRendezVous", "RoutingToRendezVous" ) Fsm:AddTransition( "Assigned", "RouteToRendezVous", "RoutingToRendezVous" )
Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtRendezVous" } ) Fsm:AddProcess( "RoutingToRendezVous", "RouteToRendezVousPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtRendezVous" } )
Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtRendezVous" } ) Fsm:AddProcess( "RoutingToRendezVous", "RouteToRendezVousZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtRendezVous" } )
Fsm:AddTransition( { "Arrived", "RoutingToRendezVous" }, "ArriveAtRendezVous", "ArrivedAtRendezVous" ) Fsm:AddTransition( { "Arrived", "RoutingToRendezVous" }, "ArriveAtRendezVous", "ArrivedAtRendezVous" )
Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "Engage", "Engaging" ) Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "Engage", "Engaging" )
Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "HoldAtRendezVous", "HoldingAtRendezVous" ) Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "HoldAtRendezVous", "HoldingAtRendezVous" )
Fsm:AddProcess ( "Engaging", "Account", ACT_ACCOUNT_DEADS:New(), {} ) Fsm:AddProcess( "Engaging", "Account", ACT_ACCOUNT_DEADS:New(), {} )
Fsm:AddTransition( "Engaging", "RouteToTarget", "Engaging" ) Fsm:AddTransition( "Engaging", "RouteToTarget", "Engaging" )
Fsm:AddProcess( "Engaging", "RouteToTargetZone", ACT_ROUTE_ZONE:New(), {} ) Fsm:AddProcess( "Engaging", "RouteToTargetZone", ACT_ROUTE_ZONE:New(), {} )
Fsm:AddProcess( "Engaging", "RouteToTargetPoint", ACT_ROUTE_POINT:New(), {} ) Fsm:AddProcess( "Engaging", "RouteToTargetPoint", ACT_ROUTE_POINT:New(), {} )
Fsm:AddTransition( "Engaging", "RouteToTargets", "Engaging" ) Fsm:AddTransition( "Engaging", "RouteToTargets", "Engaging" )
--Fsm:AddTransition( "Accounted", "DestroyedAll", "Accounted" ) -- Fsm:AddTransition( "Accounted", "DestroyedAll", "Accounted" )
--Fsm:AddTransition( "Accounted", "Success", "Success" ) -- Fsm:AddTransition( "Accounted", "Success", "Success" )
Fsm:AddTransition( "Rejected", "Reject", "Aborted" ) Fsm:AddTransition( "Rejected", "Reject", "Aborted" )
Fsm:AddTransition( "Failed", "Fail", "Failed" ) Fsm:AddTransition( "Failed", "Fail", "Failed" )
--- Test
--- Test
-- @param #FSM_PROCESS self -- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_A2G#TASK_A2G Task -- @param Tasking.Task_A2G#TASK_A2G Task
function Fsm:onafterAssigned( TaskUnit, Task ) function Fsm:onafterAssigned( TaskUnit, Task )
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
-- Determine the first Unit from the self.RendezVousSetUnit -- Determine the first Unit from the self.RendezVousSetUnit
self:RouteToRendezVous() self:RouteToRendezVous()
end end
--- Test --- Test
-- @param #FSM_PROCESS self -- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_A2G#TASK_A2G Task -- @param Tasking.Task_A2G#TASK_A2G Task
function Fsm:onafterRouteToRendezVous( TaskUnit, Task ) function Fsm:onafterRouteToRendezVous( TaskUnit, Task )
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
-- Determine the first Unit from the self.RendezVousSetUnit -- Determine the first Unit from the self.RendezVousSetUnit
if Task:GetRendezVousZone( TaskUnit ) then if Task:GetRendezVousZone( TaskUnit ) then
self:__RouteToRendezVousZone( 0.1 ) self:__RouteToRendezVousZone( 0.1 )
else else
@@ -112,36 +110,36 @@ do -- TASK_A2G
end end
end end
--- Test --- Test
-- @param #FSM_PROCESS self -- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task#TASK_A2G Task -- @param Tasking.Task#TASK_A2G Task
function Fsm:OnAfterArriveAtRendezVous( TaskUnit, Task ) function Fsm:OnAfterArriveAtRendezVous( TaskUnit, Task )
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
-- Determine the first Unit from the self.TargetSetUnit -- Determine the first Unit from the self.TargetSetUnit
self:__Engage( 0.1 ) self:__Engage( 0.1 )
end end
--- Test --- Test
-- @param #FSM_PROCESS self -- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task#TASK_A2G Task -- @param Tasking.Task#TASK_A2G Task
function Fsm:onafterEngage( TaskUnit, Task ) function Fsm:onafterEngage( TaskUnit, Task )
self:F( { self } ) self:F( { self } )
self:__Account( 0.1 ) self:__Account( 0.1 )
self:__RouteToTarget(0.1 ) self:__RouteToTarget( 0.1 )
self:__RouteToTargets( -10 ) self:__RouteToTargets( -10 )
end end
--- Test --- Test
-- @param #FSM_PROCESS self -- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_A2G#TASK_A2G Task -- @param Tasking.Task_A2G#TASK_A2G Task
function Fsm:onafterRouteToTarget( TaskUnit, Task ) function Fsm:onafterRouteToTarget( TaskUnit, Task )
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
-- Determine the first Unit from the self.TargetSetUnit -- Determine the first Unit from the self.TargetSetUnit
if Task:GetTargetZone( TaskUnit ) then if Task:GetTargetZone( TaskUnit ) then
self:__RouteToTargetZone( 0.1 ) self:__RouteToTargetZone( 0.1 )
else else
@@ -154,8 +152,8 @@ do -- TASK_A2G
self:__RouteToTargetPoint( 0.1 ) self:__RouteToTargetPoint( 0.1 )
end end
end end
--- Test --- Test
-- @param #FSM_PROCESS self -- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_A2G#TASK_A2G Task -- @param Tasking.Task_A2G#TASK_A2G Task
@@ -167,20 +165,18 @@ do -- TASK_A2G
end end
self:__RouteToTargets( -10 ) self:__RouteToTargets( -10 )
end end
return self return self
end end
--- @param #TASK_A2G self --- @param #TASK_A2G self
-- @param Core.Set#SET_UNIT TargetSetUnit The set of targets. -- @param Core.Set#SET_UNIT TargetSetUnit The set of targets.
function TASK_A2G:SetTargetSetUnit( TargetSetUnit ) function TASK_A2G:SetTargetSetUnit( TargetSetUnit )
self.TargetSetUnit = TargetSetUnit self.TargetSetUnit = TargetSetUnit
end end
--- @param #TASK_A2G self --- @param #TASK_A2G self
function TASK_A2G:GetPlannedMenuText() function TASK_A2G:GetPlannedMenuText()
return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )" return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )"
@@ -190,34 +186,32 @@ do -- TASK_A2G
-- @param Core.Point#COORDINATE RendezVousCoordinate The Coordinate object referencing to the 2D point where the RendezVous point is located on the map. -- @param Core.Point#COORDINATE RendezVousCoordinate The Coordinate object referencing to the 2D point where the RendezVous point is located on the map.
-- @param #number RendezVousRange The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point. -- @param #number RendezVousRange The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point.
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
function TASK_A2G:SetRendezVousCoordinate( RendezVousCoordinate, RendezVousRange, TaskUnit ) function TASK_A2G:SetRendezVousCoordinate( RendezVousCoordinate, RendezVousRange, TaskUnit )
local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ProcessUnit = self:GetUnitProcess( TaskUnit )
local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT
ActRouteRendezVous:SetCoordinate( RendezVousCoordinate ) ActRouteRendezVous:SetCoordinate( RendezVousCoordinate )
ActRouteRendezVous:SetRange( RendezVousRange ) ActRouteRendezVous:SetRange( RendezVousRange )
end end
--- @param #TASK_A2G self --- @param #TASK_A2G self
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
-- @return Core.Point#COORDINATE The Coordinate object referencing to the 2D point where the RendezVous point is located on the map. -- @return Core.Point#COORDINATE The Coordinate object referencing to the 2D point where the RendezVous point is located on the map.
-- @return #number The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point. -- @return #number The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point.
function TASK_A2G:GetRendezVousCoordinate( TaskUnit ) function TASK_A2G:GetRendezVousCoordinate( TaskUnit )
local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ProcessUnit = self:GetUnitProcess( TaskUnit )
local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT
return ActRouteRendezVous:GetCoordinate(), ActRouteRendezVous:GetRange() return ActRouteRendezVous:GetCoordinate(), ActRouteRendezVous:GetRange()
end end
--- @param #TASK_A2G self --- @param #TASK_A2G self
-- @param Core.Zone#ZONE_BASE RendezVousZone The Zone object where the RendezVous is located on the map. -- @param Core.Zone#ZONE_BASE RendezVousZone The Zone object where the RendezVous is located on the map.
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
function TASK_A2G:SetRendezVousZone( RendezVousZone, TaskUnit ) function TASK_A2G:SetRendezVousZone( RendezVousZone, TaskUnit )
local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ProcessUnit = self:GetUnitProcess( TaskUnit )
local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE
@@ -234,18 +228,17 @@ do -- TASK_A2G
local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE
return ActRouteRendezVous:GetZone() return ActRouteRendezVous:GetZone()
end end
--- @param #TASK_A2G self --- @param #TASK_A2G self
-- @param Core.Point#COORDINATE TargetCoordinate The Coordinate object where the Target is located on the map. -- @param Core.Point#COORDINATE TargetCoordinate The Coordinate object where the Target is located on the map.
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
function TASK_A2G:SetTargetCoordinate( TargetCoordinate, TaskUnit ) function TASK_A2G:SetTargetCoordinate( TargetCoordinate, TaskUnit )
local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ProcessUnit = self:GetUnitProcess( TaskUnit )
local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT
ActRouteTarget:SetCoordinate( TargetCoordinate ) ActRouteTarget:SetCoordinate( TargetCoordinate )
end end
--- @param #TASK_A2G self --- @param #TASK_A2G self
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
@@ -258,18 +251,16 @@ do -- TASK_A2G
return ActRouteTarget:GetCoordinate() return ActRouteTarget:GetCoordinate()
end end
--- @param #TASK_A2G self --- @param #TASK_A2G self
-- @param Core.Zone#ZONE_BASE TargetZone The Zone object where the Target is located on the map. -- @param Core.Zone#ZONE_BASE TargetZone The Zone object where the Target is located on the map.
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
function TASK_A2G:SetTargetZone( TargetZone, TaskUnit ) function TASK_A2G:SetTargetZone( TargetZone, TaskUnit )
local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ProcessUnit = self:GetUnitProcess( TaskUnit )
local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE
ActRouteTarget:SetZone( TargetZone ) ActRouteTarget:SetZone( TargetZone )
end end
--- @param #TASK_A2G self --- @param #TASK_A2G self
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
@@ -283,47 +274,46 @@ do -- TASK_A2G
end end
function TASK_A2G:SetGoalTotal() function TASK_A2G:SetGoalTotal()
self.GoalTotal = self.TargetSetUnit:Count() self.GoalTotal = self.TargetSetUnit:Count()
end end
function TASK_A2G:GetGoalTotal() function TASK_A2G:GetGoalTotal()
return self.GoalTotal return self.GoalTotal
end end
--- Return the relative distance to the target vicinity from the player, in order to sort the targets in the reports per distance from the threats. --- Return the relative distance to the target vicinity from the player, in order to sort the targets in the reports per distance from the threats.
-- @param #TASK_A2G self -- @param #TASK_A2G self
function TASK_A2G:ReportOrder( ReportGroup ) function TASK_A2G:ReportOrder( ReportGroup )
self:UpdateTaskInfo( self.DetectedItem ) self:UpdateTaskInfo( self.DetectedItem )
local Coordinate = self.TaskInfo:GetData( "Coordinate" ) local Coordinate = self.TaskInfo:GetData( "Coordinate" )
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate ) local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
return Distance return Distance
end end
--- This method checks every 10 seconds if the goal has been reached of the task. --- This method checks every 10 seconds if the goal has been reached of the task.
-- @param #TASK_A2G self -- @param #TASK_A2G self
function TASK_A2G:onafterGoal( TaskUnit, From, Event, To ) function TASK_A2G:onafterGoal( TaskUnit, From, Event, To )
local TargetSetUnit = self.TargetSetUnit -- Core.Set#SET_UNIT local TargetSetUnit = self.TargetSetUnit -- Core.Set#SET_UNIT
if TargetSetUnit:Count() == 0 then if TargetSetUnit:Count() == 0 then
self:Success() self:Success()
end end
self:__Goal( -10 ) self:__Goal( -10 )
end end
--- @param #TASK_A2G self --- @param #TASK_A2G self
function TASK_A2G:UpdateTaskInfo( DetectedItem ) function TASK_A2G:UpdateTaskInfo( DetectedItem )
if self:IsStatePlanned() or self:IsStateAssigned() then if self:IsStatePlanned() or self:IsStateAssigned() then
local TargetCoordinate = DetectedItem and self.Detection:GetDetectedItemCoordinate( DetectedItem ) or self.TargetSetUnit:GetFirst():GetCoordinate() local TargetCoordinate = DetectedItem and self.Detection:GetDetectedItemCoordinate( DetectedItem ) or self.TargetSetUnit:GetFirst():GetCoordinate()
self.TaskInfo:AddTaskName( 0, "MSOD" ) self.TaskInfo:AddTaskName( 0, "MSOD" )
self.TaskInfo:AddCoordinate( TargetCoordinate, 1, "SOD" ) self.TaskInfo:AddCoordinate( TargetCoordinate, 1, "SOD" )
local ThreatLevel, ThreatText local ThreatLevel, ThreatText
if DetectedItem then if DetectedItem then
ThreatLevel, ThreatText = self.Detection:GetDetectedItemThreatLevel( DetectedItem ) ThreatLevel, ThreatText = self.Detection:GetDetectedItemThreatLevel( DetectedItem )
@@ -331,7 +321,7 @@ do -- TASK_A2G
ThreatLevel, ThreatText = self.TargetSetUnit:CalculateThreatLevelA2G() ThreatLevel, ThreatText = self.TargetSetUnit:CalculateThreatLevelA2G()
end end
self.TaskInfo:AddThreat( ThreatText, ThreatLevel, 10, "MOD", true ) self.TaskInfo:AddThreat( ThreatText, ThreatLevel, 10, "MOD", true )
if self.Detection then if self.Detection then
local DetectedItemsCount = self.TargetSetUnit:Count() local DetectedItemsCount = self.TargetSetUnit:Count()
local ReportTypes = REPORT:New() local ReportTypes = REPORT:New()
@@ -344,33 +334,33 @@ do -- TASK_A2G
end end
end end
self.TaskInfo:AddTargetCount( DetectedItemsCount, 11, "O", true ) self.TaskInfo:AddTargetCount( DetectedItemsCount, 11, "O", true )
self.TaskInfo:AddTargets( DetectedItemsCount, ReportTypes:Text( ", " ), 20, "D", true ) self.TaskInfo:AddTargets( DetectedItemsCount, ReportTypes:Text( ", " ), 20, "D", true )
else else
local DetectedItemsCount = self.TargetSetUnit:Count() local DetectedItemsCount = self.TargetSetUnit:Count()
local DetectedItemsTypes = self.TargetSetUnit:GetTypeNames() local DetectedItemsTypes = self.TargetSetUnit:GetTypeNames()
self.TaskInfo:AddTargetCount( DetectedItemsCount, 11, "O", true ) self.TaskInfo:AddTargetCount( DetectedItemsCount, 11, "O", true )
self.TaskInfo:AddTargets( DetectedItemsCount, DetectedItemsTypes, 20, "D", true ) self.TaskInfo:AddTargets( DetectedItemsCount, DetectedItemsTypes, 20, "D", true )
end end
self.TaskInfo:AddQFEAtCoordinate( TargetCoordinate, 30, "MOD" ) self.TaskInfo:AddQFEAtCoordinate( TargetCoordinate, 30, "MOD" )
self.TaskInfo:AddTemperatureAtCoordinate( TargetCoordinate, 31, "MD" ) self.TaskInfo:AddTemperatureAtCoordinate( TargetCoordinate, 31, "MD" )
self.TaskInfo:AddWindAtCoordinate( TargetCoordinate, 32, "MD" ) self.TaskInfo:AddWindAtCoordinate( TargetCoordinate, 32, "MD" )
end end
end end
--- This function is called from the @{Tasking.CommandCenter#COMMANDCENTER} to determine the method of automatic task selection. --- This function is called from the @{Tasking.CommandCenter#COMMANDCENTER} to determine the method of automatic task selection.
-- @param #TASK_A2G self -- @param #TASK_A2G self
-- @param #number AutoAssignMethod The method to be applied to the task. -- @param #number AutoAssignMethod The method to be applied to the task.
-- @param Tasking.CommandCenter#COMMANDCENTER CommandCenter The command center. -- @param Tasking.CommandCenter#COMMANDCENTER CommandCenter The command center.
-- @param Wrapper.Group#GROUP TaskGroup The player group. -- @param Wrapper.Group#GROUP TaskGroup The player group.
function TASK_A2G:GetAutoAssignPriority( AutoAssignMethod, CommandCenter, TaskGroup ) function TASK_A2G:GetAutoAssignPriority( AutoAssignMethod, CommandCenter, TaskGroup )
if AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Random then if AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Random then
return math.random( 1, 9 ) return math.random( 1, 9 )
elseif AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Distance then elseif AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Distance then
local Coordinate = self.TaskInfo:GetData( "Coordinate" ) local Coordinate = self.TaskInfo:GetData( "Coordinate" )
local Distance = Coordinate:Get2DDistance( CommandCenter:GetPositionable():GetCoordinate() ) local Distance = Coordinate:Get2DDistance( CommandCenter:GetPositionable():GetCoordinate() )
self:F({Distance=Distance}) self:F( { Distance = Distance } )
return math.floor( Distance ) return math.floor( Distance )
elseif AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Priority then elseif AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Priority then
return 1 return 1
@@ -379,8 +369,7 @@ do -- TASK_A2G
return 0 return 0
end end
end end
do -- TASK_A2G_SEAD do -- TASK_A2G_SEAD
@@ -397,9 +386,9 @@ do -- TASK_A2G_SEAD
-- --
-- @field #TASK_A2G_SEAD -- @field #TASK_A2G_SEAD
TASK_A2G_SEAD = { TASK_A2G_SEAD = {
ClassName = "TASK_A2G_SEAD", ClassName = "TASK_A2G_SEAD"
} }
--- Instantiates a new TASK_A2G_SEAD. --- Instantiates a new TASK_A2G_SEAD.
-- @param #TASK_A2G_SEAD self -- @param #TASK_A2G_SEAD self
-- @param Tasking.Mission#MISSION Mission -- @param Tasking.Mission#MISSION Mission
@@ -408,19 +397,16 @@ do -- TASK_A2G_SEAD
-- @param Core.Set#SET_UNIT TargetSetUnit -- @param Core.Set#SET_UNIT TargetSetUnit
-- @param #string TaskBriefing The briefing of the task. -- @param #string TaskBriefing The briefing of the task.
-- @return #TASK_A2G_SEAD self -- @return #TASK_A2G_SEAD self
function TASK_A2G_SEAD:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing) function TASK_A2G_SEAD:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing )
local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "SEAD", TaskBriefing ) ) -- #TASK_A2G_SEAD local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "SEAD", TaskBriefing ) ) -- #TASK_A2G_SEAD
self:F() self:F()
Mission:AddTask( self ) Mission:AddTask( self )
self:SetBriefing( self:SetBriefing( TaskBriefing or "Execute a Suppression of Enemy Air Defenses." )
TaskBriefing or
"Execute a Suppression of Enemy Air Defenses."
)
return self return self
end end
--- Set a score when a target in scope of the A2G attack, has been destroyed . --- Set a score when a target in scope of the A2G attack, has been destroyed .
-- @param #TASK_A2G_SEAD self -- @param #TASK_A2G_SEAD self
@@ -434,7 +420,7 @@ do -- TASK_A2G_SEAD
local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has SEADed a target.", Score ) ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has SEADed a target.", Score )
return self return self
end end
@@ -450,7 +436,7 @@ do -- TASK_A2G_SEAD
local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScore( "Success", "All radar emitting targets have been successfully SEADed!", Score ) ProcessUnit:AddScore( "Success", "All radar emitting targets have been successfully SEADed!", Score )
return self return self
end end
@@ -466,11 +452,10 @@ do -- TASK_A2G_SEAD
local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScore( "Failed", "The SEADing has failed!", Penalty ) ProcessUnit:AddScore( "Failed", "The SEADing has failed!", Penalty )
return self return self
end end
end end
do -- TASK_A2G_BAI do -- TASK_A2G_BAI
@@ -488,10 +473,8 @@ do -- TASK_A2G_BAI
-- based on detected enemy ground targets. -- based on detected enemy ground targets.
-- --
-- @field #TASK_A2G_BAI -- @field #TASK_A2G_BAI
TASK_A2G_BAI = { TASK_A2G_BAI = { ClassName = "TASK_A2G_BAI" }
ClassName = "TASK_A2G_BAI",
}
--- Instantiates a new TASK_A2G_BAI. --- Instantiates a new TASK_A2G_BAI.
-- @param #TASK_A2G_BAI self -- @param #TASK_A2G_BAI self
-- @param Tasking.Mission#MISSION Mission -- @param Tasking.Mission#MISSION Mission
@@ -503,14 +486,11 @@ do -- TASK_A2G_BAI
function TASK_A2G_BAI:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing ) function TASK_A2G_BAI:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing )
local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "BAI", TaskBriefing ) ) -- #TASK_A2G_BAI local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "BAI", TaskBriefing ) ) -- #TASK_A2G_BAI
self:F() self:F()
Mission:AddTask( self ) Mission:AddTask( self )
self:SetBriefing( self:SetBriefing( TaskBriefing or "Execute a Battlefield Air Interdiction of a group of enemy targets." )
TaskBriefing or
"Execute a Battlefield Air Interdiction of a group of enemy targets."
)
return self return self
end end
@@ -526,7 +506,7 @@ do -- TASK_A2G_BAI
local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has destroyed a target in Battlefield Air Interdiction (BAI).", Score ) ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has destroyed a target in Battlefield Air Interdiction (BAI).", Score )
return self return self
end end
@@ -542,7 +522,7 @@ do -- TASK_A2G_BAI
local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScore( "Success", "All targets have been successfully destroyed! The Battlefield Air Interdiction (BAI) is a success!", Score ) ProcessUnit:AddScore( "Success", "All targets have been successfully destroyed! The Battlefield Air Interdiction (BAI) is a success!", Score )
return self return self
end end
@@ -558,15 +538,12 @@ do -- TASK_A2G_BAI
local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScore( "Failed", "The Battlefield Air Interdiction (BAI) has failed!", Penalty ) ProcessUnit:AddScore( "Failed", "The Battlefield Air Interdiction (BAI) has failed!", Penalty )
return self return self
end end
end end
do -- TASK_A2G_CAS do -- TASK_A2G_CAS
--- The TASK_A2G_CAS class --- The TASK_A2G_CAS class
@@ -581,10 +558,8 @@ do -- TASK_A2G_CAS
-- based on detected enemy ground targets. -- based on detected enemy ground targets.
-- --
-- @field #TASK_A2G_CAS -- @field #TASK_A2G_CAS
TASK_A2G_CAS = { TASK_A2G_CAS = { ClassName = "TASK_A2G_CAS" }
ClassName = "TASK_A2G_CAS",
}
--- Instantiates a new TASK_A2G_CAS. --- Instantiates a new TASK_A2G_CAS.
-- @param #TASK_A2G_CAS self -- @param #TASK_A2G_CAS self
-- @param Tasking.Mission#MISSION Mission -- @param Tasking.Mission#MISSION Mission
@@ -596,19 +571,13 @@ do -- TASK_A2G_CAS
function TASK_A2G_CAS:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing ) function TASK_A2G_CAS:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing )
local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "CAS", TaskBriefing ) ) -- #TASK_A2G_CAS local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "CAS", TaskBriefing ) ) -- #TASK_A2G_CAS
self:F() self:F()
Mission:AddTask( self )
self:SetBriefing(
TaskBriefing or
"Execute a Close Air Support for a group of enemy targets. " ..
"Beware of friendlies at the vicinity! "
)
Mission:AddTask( self )
self:SetBriefing( TaskBriefing or ( "Execute a Close Air Support for a group of enemy targets. " .. "Beware of friendlies at the vicinity! " ) )
return self return self
end end
--- Set a score when a target in scope of the A2G attack, has been destroyed . --- Set a score when a target in scope of the A2G attack, has been destroyed .
-- @param #TASK_A2G_CAS self -- @param #TASK_A2G_CAS self
@@ -622,7 +591,7 @@ do -- TASK_A2G_CAS
local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has destroyed a target in Close Air Support (CAS).", Score ) ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has destroyed a target in Close Air Support (CAS).", Score )
return self return self
end end
@@ -638,7 +607,7 @@ do -- TASK_A2G_CAS
local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScore( "Success", "All targets have been successfully destroyed! The Close Air Support (CAS) was a success!", Score ) ProcessUnit:AddScore( "Success", "All targets have been successfully destroyed! The Close Air Support (CAS) was a success!", Score )
return self return self
end end
@@ -654,9 +623,8 @@ do -- TASK_A2G_CAS
local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScore( "Failed", "The Close Air Support (CAS) has failed!", Penalty ) ProcessUnit:AddScore( "Failed", "The Close Air Support (CAS) has failed!", Penalty )
return self return self
end end
end end

View File

@@ -1,24 +1,25 @@
--- **Tasking** -- Dynamically allocates A2G tasks to human players, based on detected ground targets through reconnaissance. --- **Tasking** -- Dynamically allocates A2G tasks to human players, based on detected ground targets through reconnaissance.
-- --
-- **Features:** -- **Features:**
-- --
-- * Dynamically assign tasks to human players based on detected targets. -- * Dynamically assign tasks to human players based on detected targets.
-- * Dynamically change the tasks as the tactical situation evolves during the mission. -- * Dynamically change the tasks as the tactical situation evolves during the mission.
-- * Dynamically assign (CAS) Close Air Support tasks for human players. -- * Dynamically assign (CAS) Close Air Support tasks for human players.
-- * Dynamically assign (BAI) Battlefield Air Interdiction tasks for human players. -- * Dynamically assign (BAI) Battlefield Air Interdiction tasks for human players.
-- * Dynamically assign (SEAD) Supression of Enemy Air Defense tasks for human players to eliminate G2A missile threats. -- * Dynamically assign (SEAD) Suppression of Enemy Air Defense tasks for human players to eliminate G2A missile threats.
-- * Define and use an EWR (Early Warning Radar) network. -- * Define and use an EWR (Early Warning Radar) network.
-- * Define different ranges to engage upon intruders. -- * Define different ranges to engage upon intruders.
-- * Keep task achievements. -- * Keep task achievements.
-- * Score task achievements.-- -- * Score task achievements.
--
-- === -- ===
-- --
-- ### Author: **FlightControl** -- ### Author: **FlightControl**
-- --
-- ### Contributions: -- ### Contributions:
-- --
-- === -- ===
-- --
-- @module Tasking.Task_A2G_Dispatcher -- @module Tasking.Task_A2G_Dispatcher
-- @image Task_A2G_Dispatcher.JPG -- @image Task_A2G_Dispatcher.JPG
@@ -32,151 +33,151 @@ do -- TASK_A2G_DISPATCHER
-- @extends Tasking.DetectionManager#DETECTION_MANAGER -- @extends Tasking.DetectionManager#DETECTION_MANAGER
--- Orchestrates dynamic **A2G Task Dispatching** based on the detection results of a linked @{Detection} object. --- Orchestrates dynamic **A2G Task Dispatching** based on the detection results of a linked @{Detection} object.
-- --
-- It uses the Tasking System within the MOOSE framework, which is a multi-player Tasking Orchestration system. -- It uses the Tasking System within the MOOSE framework, which is a multi-player Tasking Orchestration system.
-- It provides a truly dynamic battle environment for pilots and ground commanders to engage upon, -- It provides a truly dynamic battle environment for pilots and ground commanders to engage upon,
-- in a true co-operation environment wherein **Multiple Teams** will collaborate in Missions to **achieve a common Mission Goal**. -- in a true co-operation environment wherein **Multiple Teams** will collaborate in Missions to **achieve a common Mission Goal**.
-- --
-- The A2G dispatcher will dispatch the A2G Tasks to a defined @{Set} of @{Wrapper.Group}s that will be manned by **Players**. -- The A2G dispatcher will dispatch the A2G Tasks to a defined @{Set} of @{Wrapper.Group}s that will be manned by **Players**.
-- We call this the **AttackSet** of the A2G dispatcher. So, the Players are seated in the @{Client}s of the @{Wrapper.Group} @{Set}. -- We call this the **AttackSet** of the A2G dispatcher. So, the Players are seated in the @{Client}s of the @{Wrapper.Group} @{Set}.
-- --
-- Depending on the actions of the enemy, preventive tasks are dispatched to the players to orchestrate the engagement in a true co-operation. -- Depending on the actions of the enemy, preventive tasks are dispatched to the players to orchestrate the engagement in a true co-operation.
-- The detection object will group the detected targets by its grouping method, and integrates a @{Set} of @{Wrapper.Group}s that are Recce vehicles or air units. -- The detection object will group the detected targets by its grouping method, and integrates a @{Set} of @{Wrapper.Group}s that are Recce vehicles or air units.
-- We call this the **RecceSet** of the A2G dispatcher. -- We call this the **RecceSet** of the A2G dispatcher.
-- --
-- Depending on the current detected tactical situation, different task types will be dispatched to the Players seated in the AttackSet.. -- Depending on the current detected tactical situation, different task types will be dispatched to the Players seated in the AttackSet..
-- There are currently 3 **Task Types** implemented in the TASK\_A2G\_DISPATCHER: -- There are currently 3 **Task Types** implemented in the TASK\_A2G\_DISPATCHER:
-- --
-- - **SEAD Task**: Dispatched when there are ground based Radar Emitters detected within an area. -- - **SEAD Task**: Dispatched when there are ground based Radar Emitters detected within an area.
-- - **CAS Task**: Dispatched when there are no ground based Radar Emitters within the area, but there are friendly ground Units within 6 km from the enemy. -- - **CAS Task**: Dispatched when there are no ground based Radar Emitters within the area, but there are friendly ground Units within 6 km from the enemy.
-- - **BAI Task**: Dispatched when there are no ground based Radar Emitters within the area, and there aren't friendly ground Units within 6 km from the enemy. -- - **BAI Task**: Dispatched when there are no ground based Radar Emitters within the area, and there aren't friendly ground Units within 6 km from the enemy.
-- --
-- # 0. Tactical Situations -- # 0. Tactical Situations
-- --
-- This chapters provides some insights in the tactical situations when certain Task Types are created. -- This chapters provides some insights in the tactical situations when certain Task Types are created.
-- The Task Types are depending on the enemy positions that were detected, and the current location of friendly units. -- The Task Types are depending on the enemy positions that were detected, and the current location of friendly units.
-- --
-- ![](..\Presentations\TASK_A2G_DISPATCHER\Dia3.JPG) -- ![](..\Presentations\TASK_A2G_DISPATCHER\Dia3.JPG)
-- --
-- In the demonstration mission [TAD-A2G-000 - AREAS - Detection test], -- In the demonstration mission [TAD-A2G-000 - AREAS - Detection test],
-- the tactical situation is a demonstration how the A2G detection works. -- the tactical situation is a demonstration how the A2G detection works.
-- This example will be taken further in the explanation in the following chapters. -- This example will be taken further in the explanation in the following chapters.
-- --
-- ![](..\Presentations\TASK_A2G_DISPATCHER\Dia4.JPG) -- ![](..\Presentations\TASK_A2G_DISPATCHER\Dia4.JPG)
-- --
-- The red coalition are the players, the blue coalition is the enemy. -- The red coalition are the players, the blue coalition is the enemy.
-- --
-- Red reconnaissance vehicles and airborne units are detecting the targets. -- Red reconnaissance vehicles and airborne units are detecting the targets.
-- We call this the RecceSet as explained above, which is a Set of Groups that -- We call this the RecceSet as explained above, which is a Set of Groups that
-- have a group name starting with `Recce` (configured in the mission script). -- have a group name starting with `Recce` (configured in the mission script).
-- --
-- Red attack units are responsible for executing the mission for the command center. -- Red attack units are responsible for executing the mission for the command center.
-- We call this the AttackSet, which is a Set of Groups with a group name starting with `Attack` (configured in the mission script). -- We call this the AttackSet, which is a Set of Groups with a group name starting with `Attack` (configured in the mission script).
-- These units are setup in this demonstration mission to be ground vehicles and airplanes. -- These units are setup in this demonstration mission to be ground vehicles and airplanes.
-- For demonstration purposes, the attack airplane is stationed on the ground to explain -- For demonstration purposes, the attack airplane is stationed on the ground to explain
-- the messages and the menus properly. -- the messages and the menus properly.
-- Further test missions demonstrate the A2G task dispatcher from within air. -- Further test missions demonstrate the A2G task dispatcher from within air.
-- --
-- Depending upon the detection results, the A2G dispatcher will create different tasks. -- Depending upon the detection results, the A2G dispatcher will create different tasks.
-- --
-- # 0.1. SEAD Task -- # 0.1. SEAD Task
-- --
-- A SEAD Task is dispatched when there are ground based Radar Emitters detected within an area. -- A SEAD Task is dispatched when there are ground based Radar Emitters detected within an area.
-- --
-- ![](..\Presentations\TASK_A2G_DISPATCHER\Dia9.JPG) -- ![](..\Presentations\TASK_A2G_DISPATCHER\Dia9.JPG)
-- --
-- - Once all Radar Emitting Units have been destroyed, the Task will convert into a BAI or CAS task! -- - Once all Radar Emitting Units have been destroyed, the Task will convert into a BAI or CAS task!
-- - A CAS and BAI task may be converted into a SEAD task, once a radar has been detected within the area! -- - A CAS and BAI task may be converted into a SEAD task, once a radar has been detected within the area!
-- --
-- # 0.2. CAS Task -- # 0.2. CAS Task
-- --
-- A CAS Task is dispatched when there are no ground based Radar Emitters within the area, but there are friendly ground Units within 6 km from the enemy. -- A CAS Task is dispatched when there are no ground based Radar Emitters within the area, but there are friendly ground Units within 6 km from the enemy.
-- --
-- ![](..\Presentations\TASK_A2G_DISPATCHER\Dia10.JPG) -- ![](..\Presentations\TASK_A2G_DISPATCHER\Dia10.JPG)
-- --
-- - After the detection of the CAS task, if the friendly Units are destroyed, the CAS task will convert into a BAI task! -- - After the detection of the CAS task, if the friendly Units are destroyed, the CAS task will convert into a BAI task!
-- - Only ground Units are taken into account. Airborne units are ships are not considered friendlies that require Close Air Support. -- - Only ground Units are taken into account. Airborne units are ships are not considered friendlies that require Close Air Support.
-- --
-- # 0.3. BAI Task -- # 0.3. BAI Task
-- --
-- A BAI Task is dispatched when there are no ground based Radar Emitters within the area, and there aren't friendly ground Units within 6 km from the enemy. -- A BAI Task is dispatched when there are no ground based Radar Emitters within the area, and there aren't friendly ground Units within 6 km from the enemy.
-- --
-- ![](..\Presentations\TASK_A2G_DISPATCHER\Dia11.JPG) -- ![](..\Presentations\TASK_A2G_DISPATCHER\Dia11.JPG)
-- --
-- - A BAI task may be converted into a CAS task if friendly Ground Units approach within 6 km range! -- - A BAI task may be converted into a CAS task if friendly Ground Units approach within 6 km range!
-- --
-- # 1. Player Experience -- # 1. Player Experience
-- --
-- The A2G dispatcher is residing under a @{CommandCenter}, which is orchestrating a @{Mission}. -- The A2G dispatcher is residing under a @{CommandCenter}, which is orchestrating a @{Mission}.
-- As a result, you'll find for DCS World missions that implement the A2G dispatcher a **Command Center Menu** and under this one or more **Mission Menus**. -- As a result, you'll find for DCS World missions that implement the A2G dispatcher a **Command Center Menu** and under this one or more **Mission Menus**.
-- --
-- For example, if there are 2 Command Centers (CC). -- For example, if there are 2 Command Centers (CC).
-- Each CC is controlling a couple of Missions, the Radio Menu Structure could look like this: -- Each CC is controlling a couple of Missions, the Radio Menu Structure could look like this:
-- --
-- Radio MENU Structure (F10. Other) -- Radio MENU Structure (F10. Other)
-- --
-- F1. Command Center [Gori] -- F1. Command Center [Gori]
-- F1. Mission "Alpha (Primary)" -- F1. Mission "Alpha (Primary)"
-- F2. Mission "Beta (Secondary)" -- F2. Mission "Beta (Secondary)"
-- F3. Mission "Gamma (Tactical)" -- F3. Mission "Gamma (Tactical)"
-- F1. Command Center [Lima] -- F1. Command Center [Lima]
-- F1. Mission "Overlord (High)" -- F1. Mission "Overlord (High)"
-- --
-- Command Center [Gori] is controlling Mission "Alpha", "Beta", "Gamma". Alpha is the Primary mission, Beta the Secondary and there is a Tacical mission Gamma. -- Command Center [Gori] is controlling Mission "Alpha", "Beta", "Gamma". Alpha is the Primary mission, Beta the Secondary and there is a Tactical mission Gamma.
-- Command Center [Lima] is controlling Missions "Overlord", which needs to be executed with High priority. -- Command Center [Lima] is controlling Missions "Overlord", which needs to be executed with High priority.
-- --
-- ## 1.1. Mission Menu (Under the Command Center Menu) -- ## 1.1. Mission Menu (Under the Command Center Menu)
-- --
-- The Mission Menu controls the information of the mission, including the: -- The Mission Menu controls the information of the mission, including the:
-- --
-- - **Mission Briefing**: A briefing of the Mission in text, which will be shown as a message. -- - **Mission Briefing**: A briefing of the Mission in text, which will be shown as a message.
-- - **Mark Task Locations**: A summary of each Task will be shown on the map as a marker. -- - **Mark Task Locations**: A summary of each Task will be shown on the map as a marker.
-- - **Create Task Reports**: A menu to create various reports of the current tasks dispatched by the A2G dispatcher. -- - **Create Task Reports**: A menu to create various reports of the current tasks dispatched by the A2G dispatcher.
-- - **Create Mission Reports**: A menu to create various reports on the current mission. -- - **Create Mission Reports**: A menu to create various reports on the current mission.
-- --
-- For CC [Lima], Mission "Overlord", the menu structure could look like this: -- For CC [Lima], Mission "Overlord", the menu structure could look like this:
-- --
-- Radio MENU Structure (F10. Other) -- Radio MENU Structure (F10. Other)
-- --
-- F1. Command Center [Lima] -- F1. Command Center [Lima]
-- F1. Mission "Overlord" -- F1. Mission "Overlord"
-- F1. Mission Briefing -- F1. Mission Briefing
-- F2. Mark Task Locations on Map -- F2. Mark Task Locations on Map
-- F3. Task Reports -- F3. Task Reports
-- F4. Mission Reports -- F4. Mission Reports
-- --
-- ![](..\Presentations\TASK_A2G_DISPATCHER\Dia5.JPG) -- ![](..\Presentations\TASK_A2G_DISPATCHER\Dia5.JPG)
-- --
-- ### 1.1.1. Mission Briefing Menu -- ### 1.1.1. Mission Briefing Menu
-- --
-- The Mission Briefing Menu will show in text a summary description of the overall mission objectives and expectations. -- The Mission Briefing Menu will show in text a summary description of the overall mission objectives and expectations.
-- Note that the Mission Briefing is not the briefing of a specific task, but rather provides an overall strategy and tactical situation, -- Note that the Mission Briefing is not the briefing of a specific task, but rather provides an overall strategy and tactical situation,
-- and explains the mission goals. -- and explains the mission goals.
-- --
-- --
-- ### 1.1.2. Mark Task Locations Menu -- ### 1.1.2. Mark Task Locations Menu
-- --
-- The Mark Task Locations Menu will mark the location indications of the Tasks on the map, if this intelligence is known by the Command Center. -- The Mark Task Locations Menu will mark the location indications of the Tasks on the map, if this intelligence is known by the Command Center.
-- For A2G tasks this information will always be know, but it can be that for other tasks a location intelligence will be less relevant. -- For A2G tasks this information will always be know, but it can be that for other tasks a location intelligence will be less relevant.
-- Note that each Planned task and each Engaged task will be marked. Completed, Failed and Cancelled tasks are not marked. -- Note that each Planned task and each Engaged task will be marked. Completed, Failed and Cancelled tasks are not marked.
-- Depending on the task type, a summary information is shown to bring to the player the relevant information for situational awareness. -- Depending on the task type, a summary information is shown to bring to the player the relevant information for situational awareness.
-- --
-- ### 1.1.3. Task Reports Menu -- ### 1.1.3. Task Reports Menu
-- --
-- The Task Reports Menu is a sub menu, that allows to create various reports: -- The Task Reports Menu is a sub menu, that allows to create various reports:
-- --
-- - **Tasks Summary**: This report will list all the Tasks that are or were active within the mission, indicating its status. -- - **Tasks Summary**: This report will list all the Tasks that are or were active within the mission, indicating its status.
-- - **Planned Tasks**: This report will list all the Tasks that are in status Planned, which are Tasks not assigned to any player, and are ready to be executed. -- - **Planned Tasks**: This report will list all the Tasks that are in status Planned, which are Tasks not assigned to any player, and are ready to be executed.
-- - **Assigned Tasks**: This report will list all the Tasks that are in status Assigned, which are Tasks assigned to (a) player(s) and are currently executed. -- - **Assigned Tasks**: This report will list all the Tasks that are in status Assigned, which are Tasks assigned to (a) player(s) and are currently executed.
-- - **Successful Tasks**: This report will list all the Tasks that are in status Success, which are Tasks executed by (a) player(s) and are completed successfully. -- - **Successful Tasks**: This report will list all the Tasks that are in status Success, which are Tasks executed by (a) player(s) and are completed successfully.
-- - **Failed Tasks**: This report will list all the Tasks that are in status Success, which are Tasks executed by (a) player(s) and that have failed. -- - **Failed Tasks**: This report will list all the Tasks that are in status Success, which are Tasks executed by (a) player(s) and that have failed.
-- --
-- The information shown of the tasks will vary according the underlying task type, but are self explanatory. -- The information shown of the tasks will vary according the underlying task type, but are self explanatory.
-- --
-- For CC [Gori], Mission "Alpha", the Task Reports menu structure could look like this: -- For CC [Gori], Mission "Alpha", the Task Reports menu structure could look like this:
-- --
-- Radio MENU Structure (F10. Other) -- Radio MENU Structure (F10. Other)
-- --
-- F1. Command Center [Gori] -- F1. Command Center [Gori]
-- F1. Mission "Alpha" -- F1. Mission "Alpha"
-- F1. Mission Briefing -- F1. Mission Briefing
@@ -188,21 +189,21 @@ do -- TASK_A2G_DISPATCHER
-- F4. Successful Tasks -- F4. Successful Tasks
-- F5. Failed Tasks -- F5. Failed Tasks
-- F4. Mission Reports -- F4. Mission Reports
-- --
-- Note that these reports provide an "overview" of the tasks. Detailed information of the task can be retrieved using the Detailed Report on the Task Menu. -- Note that these reports provide an "overview" of the tasks. Detailed information of the task can be retrieved using the Detailed Report on the Task Menu.
-- (See later). -- (See later).
-- --
-- ### 1.1.4. Mission Reports Menu -- ### 1.1.4. Mission Reports Menu
-- --
-- The Mission Reports Menu is a sub menu, that provides options to retrieve further information on the current Mission: -- The Mission Reports Menu is a sub menu, that provides options to retrieve further information on the current Mission:
-- --
-- - **Report Mission Progress**: Shows the progress of the current Mission. Each Task has a %-tage of completion. -- - **Report Mission Progress**: Shows the progress of the current Mission. Each Task has a % of completion.
-- - **Report Players per Task**: Show which players are engaged on which Task within the Mission. -- - **Report Players per Task**: Show which players are engaged on which Task within the Mission.
-- --
-- For CC |Gori|, Mission "Alpha", the Mission Reports menu structure could look like this: -- For CC |Gori|, Mission "Alpha", the Mission Reports menu structure could look like this:
-- --
-- Radio MENU Structure (F10. Other) -- Radio MENU Structure (F10. Other)
-- --
-- F1. Command Center [Gori] -- F1. Command Center [Gori]
-- F1. Mission "Alpha" -- F1. Mission "Alpha"
-- F1. Mission Briefing -- F1. Mission Briefing
@@ -211,25 +212,25 @@ do -- TASK_A2G_DISPATCHER
-- F4. Mission Reports -- F4. Mission Reports
-- F1. Report Mission Progress -- F1. Report Mission Progress
-- F2. Report Players per Task -- F2. Report Players per Task
-- --
-- --
-- ## 1.2. Task Management Menus -- ## 1.2. Task Management Menus
-- --
-- Very important to remember is: **Multiple Players can be assigned to the same Task, but from the player perspective, the Player can only be assigned to one Task per Mission at the same time!** -- Very important to remember is: **Multiple Players can be assigned to the same Task, but from the player perspective, the Player can only be assigned to one Task per Mission at the same time!**
-- Consider this like the two major modes in which a player can be in. He can be free of tasks or he can be assigned to a Task. -- Consider this like the two major modes in which a player can be in. He can be free of tasks or he can be assigned to a Task.
-- Depending on whether a Task has been Planned or Assigned to a Player (Group), -- Depending on whether a Task has been Planned or Assigned to a Player (Group),
-- **the Mission Menu will contain extra Menus to control specific Tasks.** -- **the Mission Menu will contain extra Menus to control specific Tasks.**
-- --
-- #### 1.2.1. Join a Planned Task -- #### 1.2.1. Join a Planned Task
-- --
-- If the Player has not yet been assigned to a Task within the Mission, the Mission Menu will contain additionally a: -- If the Player has not yet been assigned to a Task within the Mission, the Mission Menu will contain additionally a:
-- --
-- - Join Planned Task Menu: This menu structure allows the player to join a planned task (a Task with status Planned). -- - Join Planned Task Menu: This menu structure allows the player to join a planned task (a Task with status Planned).
-- --
-- For CC |Gori|, Mission "Alpha", the menu structure could look like this: -- For CC |Gori|, Mission "Alpha", the menu structure could look like this:
-- --
-- Radio MENU Structure (F10. Other) -- Radio MENU Structure (F10. Other)
-- --
-- F1. Command Center [Gori] -- F1. Command Center [Gori]
-- F1. Mission "Alpha" -- F1. Mission "Alpha"
-- F1. Mission Briefing -- F1. Mission Briefing
@@ -237,23 +238,23 @@ do -- TASK_A2G_DISPATCHER
-- F3. Task Reports -- F3. Task Reports
-- F4. Mission Reports -- F4. Mission Reports
-- F5. Join Planned Task -- F5. Join Planned Task
-- --
-- **The F5. Join Planned Task allows the player to join a Planned Task and take an engagement in the running Mission.** -- **The F5. Join Planned Task allows the player to join a Planned Task and take an engagement in the running Mission.**
-- --
-- #### 1.2.2. Manage an Assigned Task -- #### 1.2.2. Manage an Assigned Task
-- --
-- If the Player has been assigned to one Task within the Mission, the Mission Menu will contain an extra: -- If the Player has been assigned to one Task within the Mission, the Mission Menu will contain an extra:
-- --
-- - Assigned Task __TaskName__ Menu: This menu structure allows the player to take actions on the currently engaged task. -- - Assigned Task __TaskName__ Menu: This menu structure allows the player to take actions on the currently engaged task.
-- --
-- In this example, the Group currently seated by the player is not assigned yet to a Task. -- In this example, the Group currently seated by the player is not assigned yet to a Task.
-- The Player has the option to assign itself to a Planned Task using menu option F5 under the Mission Menu "Alpha". -- The Player has the option to assign itself to a Planned Task using menu option F5 under the Mission Menu "Alpha".
-- --
-- This would be an example menu structure, -- This would be an example menu structure,
-- for CC |Gori|, Mission "Alpha", when a player would have joined Task CAS.001: -- for CC |Gori|, Mission "Alpha", when a player would have joined Task CAS.001:
-- --
-- Radio MENU Structure (F10. Other) -- Radio MENU Structure (F10. Other)
-- --
-- F1. Command Center [Gori] -- F1. Command Center [Gori]
-- F1. Mission "Alpha" -- F1. Mission "Alpha"
-- F1. Mission Briefing -- F1. Mission Briefing
@@ -261,26 +262,25 @@ do -- TASK_A2G_DISPATCHER
-- F3. Task Reports -- F3. Task Reports
-- F4. Mission Reports -- F4. Mission Reports
-- F5. Assigned Task CAS.001 -- F5. Assigned Task CAS.001
-- --
-- **The F5. Assigned Task __TaskName__ allows the player to control the current Assigned Task and take further actions.** -- **The F5. Assigned Task __TaskName__ allows the player to control the current Assigned Task and take further actions.**
-- --
--
-- ## 1.3. Join Planned Task Menu -- ## 1.3. Join Planned Task Menu
-- --
-- The Join Planned Task Menu contains the different Planned A2G Tasks **in a structured Menu Hierarchy**. -- The Join Planned Task Menu contains the different Planned A2G Tasks **in a structured Menu Hierarchy**.
-- The Menu Hierarchy is structuring the Tasks per **Task Type**, and then by **Task Name (ID)**. -- The Menu Hierarchy is structuring the Tasks per **Task Type**, and then by **Task Name (ID)**.
-- --
-- For example, for CC [Gori], Mission "Alpha", -- For example, for CC [Gori], Mission "Alpha",
-- if a Mission "ALpha" contains 5 Planned Tasks, which would be: -- if a Mission "ALpha" contains 5 Planned Tasks, which would be:
-- --
-- - 2 CAS Tasks -- - 2 CAS Tasks
-- - 1 BAI Task -- - 1 BAI Task
-- - 2 SEAD Tasks -- - 2 SEAD Tasks
-- --
-- the Join Planned Task Menu Hierarchy could look like this: -- the Join Planned Task Menu Hierarchy could look like this:
-- --
-- Radio MENU Structure (F10. Other) -- Radio MENU Structure (F10. Other)
-- --
-- F1. Command Center [Gori] -- F1. Command Center [Gori]
-- F1. Mission "Alpha" -- F1. Mission "Alpha"
-- F1. Mission Briefing -- F1. Mission Briefing
@@ -296,26 +296,26 @@ do -- TASK_A2G_DISPATCHER
-- F1. SEAD.003 -- F1. SEAD.003
-- F2. SEAD.004 -- F2. SEAD.004
-- F3. SEAD.005 -- F3. SEAD.005
-- --
-- An example from within a running simulation: -- An example from within a running simulation:
-- --
-- ![](..\Presentations\TASK_A2G_DISPATCHER\Dia6.JPG) -- ![](..\Presentations\TASK_A2G_DISPATCHER\Dia6.JPG)
-- --
-- Each Task Type Menu would have a list of the Task Menus underneath. -- Each Task Type Menu would have a list of the Task Menus underneath.
-- Each Task Menu (eg. `CAS.001`) has a **detailed Task Menu structure to control the specific task**! -- Each Task Menu (eg. `CAS.001`) has a **detailed Task Menu structure to control the specific task**!
-- --
-- ### 1.3.1. Planned Task Menu -- ### 1.3.1. Planned Task Menu
-- --
-- Each Planned Task Menu will allow for the following actions: -- Each Planned Task Menu will allow for the following actions:
-- --
-- - Report Task Details: Provides a detailed report on the Planned Task. -- - Report Task Details: Provides a detailed report on the Planned Task.
-- - Mark Task Location on Map: Mark the approximate location of the Task on the Map, if relevant. -- - Mark Task Location on Map: Mark the approximate location of the Task on the Map, if relevant.
-- - Join Task: Join the Task. This is THE menu option to let a Player join the Task, and to engage within the Mission. -- - Join Task: Join the Task. This is THE menu option to let a Player join the Task, and to engage within the Mission.
-- --
-- The Join Planned Task Menu could look like this for for CC |Gori|, Mission "Alpha": -- The Join Planned Task Menu could look like this for for CC |Gori|, Mission "Alpha":
-- --
-- Radio MENU Structure (F10. Other) -- Radio MENU Structure (F10. Other)
-- --
-- F1. Command Center |Gori| -- F1. Command Center |Gori|
-- F1. Mission "Alpha" -- F1. Mission "Alpha"
-- F1. Mission Briefing -- F1. Mission Briefing
@@ -328,22 +328,22 @@ do -- TASK_A2G_DISPATCHER
-- F1. Report Task Details -- F1. Report Task Details
-- F2. Mark Task Location on Map -- F2. Mark Task Location on Map
-- F3. Join Task -- F3. Join Task
-- --
-- **The Join Task is THE menu option to let a Player join the Task, and to engage within the Mission.** -- **The Join Task is THE menu option to let a Player join the Task, and to engage within the Mission.**
-- --
-- --
-- ## 1.4. Assigned Task Menu -- ## 1.4. Assigned Task Menu
-- --
-- The Assigned Task Menu allows to control the **current assigned task** within the Mission. -- The Assigned Task Menu allows to control the **current assigned task** within the Mission.
-- --
-- Depending on the Type of Task, the following menu options will be available: -- Depending on the Type of Task, the following menu options will be available:
-- --
-- - **Report Task Details**: Provides a detailed report on the Planned Task. -- - **Report Task Details**: Provides a detailed report on the Planned Task.
-- - **Mark Task Location on Map**: Mark the approximate location of the Task on the Map, if relevant. -- - **Mark Task Location on Map**: Mark the approximate location of the Task on the Map, if relevant.
-- - **Abort Task: Abort the current assigned Task:** This menu option lets the player abort the Task. -- - **Abort Task: Abort the current assigned Task:** This menu option lets the player abort the Task.
-- --
-- For example, for CC |Gori|, Mission "Alpha", the Assigned Menu could be: -- For example, for CC |Gori|, Mission "Alpha", the Assigned Menu could be:
-- --
-- F1. Command Center |Gori| -- F1. Command Center |Gori|
-- F1. Mission "Alpha" -- F1. Mission "Alpha"
-- F1. Mission Briefing -- F1. Mission Briefing
@@ -354,90 +354,89 @@ do -- TASK_A2G_DISPATCHER
-- F1. Report Task Details -- F1. Report Task Details
-- F2. Mark Task Location on Map -- F2. Mark Task Location on Map
-- F3. Abort Task -- F3. Abort Task
-- --
-- Task abortion will result in the Task to be Cancelled, and the Task **may** be **Replanned**. -- Task abortion will result in the Task to be Cancelled, and the Task **may** be **Replanned**.
-- However, this will depend on the setup of each Mission. -- However, this will depend on the setup of each Mission.
-- --
-- ## 1.5. Messages -- ## 1.5. Messages
-- --
-- During game play, different messages are displayed. -- During game play, different messages are displayed.
-- These messages provide an update of the achievements made, and the state wherein the task is. -- These messages provide an update of the achievements made, and the state wherein the task is.
-- --
-- The various reports can be used also to retrieve the current status of the mission and its tasks. -- The various reports can be used also to retrieve the current status of the mission and its tasks.
-- --
-- ![](..\Presentations\TASK_A2G_DISPATCHER\Dia7.JPG) -- ![](..\Presentations\TASK_A2G_DISPATCHER\Dia7.JPG)
-- --
-- The @{Settings} menu provides additional options to control the timing of the messages. -- The @{Settings} menu provides additional options to control the timing of the messages.
-- There are: -- There are:
-- --
-- - Status messages, which are quick status updates. The settings menu allows to switch off these messages. -- - Status messages, which are quick status updates. The settings menu allows to switch off these messages.
-- - Information messages, which are shown a bit longer, as they contain important information. -- - Information messages, which are shown a bit longer, as they contain important information.
-- - Summary reports, which are quick reports showing a high level summary. -- - Summary reports, which are quick reports showing a high level summary.
-- - Overview reports, which are providing the essential information. It provides an overview of a greater thing, and may take a bit of time to read. -- - Overview reports, which are providing the essential information. It provides an overview of a greater thing, and may take a bit of time to read.
-- - Detailed reports, which provide with very detailed information. It takes a bit longer to read those reports, so the display of those could be a bit longer. -- - Detailed reports, which provide with very detailed information. It takes a bit longer to read those reports, so the display of those could be a bit longer.
-- --
-- # 2. TASK\_A2G\_DISPATCHER constructor -- # 2. TASK\_A2G\_DISPATCHER constructor
-- --
-- The @{#TASK_A2G_DISPATCHER.New}() method creates a new TASK\_A2G\_DISPATCHER instance. -- The @{#TASK_A2G_DISPATCHER.New}() method creates a new TASK\_A2G\_DISPATCHER instance.
-- --
-- # 3. Usage -- # 3. Usage
-- --
-- To use the TASK\_A2G\_DISPATCHER class, you need: -- To use the TASK\_A2G\_DISPATCHER class, you need:
-- --
-- - A @{CommandCenter} object. The master communication channel. -- - A @{CommandCenter} object. The master communication channel.
-- - A @{Mission} object. Each task belongs to a Mission. -- - A @{Mission} object. Each task belongs to a Mission.
-- - A @{Detection} object. There are several detection grouping methods to choose from. -- - A @{Detection} object. There are several detection grouping methods to choose from.
-- - A @{Task_A2G_Dispatcher} object. The master A2G task dispatcher. -- - A @{Task_A2G_Dispatcher} object. The master A2G task dispatcher.
-- - A @{Set} of @{Wrapper.Group} objects that will detect the emeny, the RecceSet. This is attached to the @{Detection} object. -- - A @{Set} of @{Wrapper.Group} objects that will detect the enemy, the RecceSet. This is attached to the @{Detection} object.
-- - A @{Set} ob @{Wrapper.Group} objects that will attack the enemy, the AttackSet. This is attached to the @{Task_A2G_Dispatcher} object. -- - A @{Set} ob @{Wrapper.Group} objects that will attack the enemy, the AttackSet. This is attached to the @{Task_A2G_Dispatcher} object.
--
-- Below an example mission declaration that is defines a Task A2G Dispatcher object.
-- --
-- -- Declare the Command Center -- Below an example mission declaration that is defines a Task A2G Dispatcher object.
--
-- -- Declare the Command Center
-- local HQ = GROUP -- local HQ = GROUP
-- :FindByName( "HQ", "Bravo HQ" ) -- :FindByName( "HQ", "Bravo HQ" )
-- --
-- local CommandCenter = COMMANDCENTER -- local CommandCenter = COMMANDCENTER
-- :New( HQ, "Lima" ) -- :New( HQ, "Lima" )
-- --
-- -- Declare the Mission for the Command Center. -- -- Declare the Mission for the Command Center.
-- local Mission = MISSION -- local Mission = MISSION
-- :New( CommandCenter, "Overlord", "High", "Attack Detect Mission Briefing", coalition.side.RED ) -- :New( CommandCenter, "Overlord", "High", "Attack Detect Mission Briefing", coalition.side.RED )
-- --
-- -- Define the RecceSet that will detect the enemy. -- -- Define the RecceSet that will detect the enemy.
-- local RecceSet = SET_GROUP -- local RecceSet = SET_GROUP
-- :New() -- :New()
-- :FilterPrefixes( "FAC" ) -- :FilterPrefixes( "FAC" )
-- :FilterCoalitions("red") -- :FilterCoalitions("red")
-- :FilterStart() -- :FilterStart()
-- --
-- -- Setup the detection. We use DETECTION_AREAS to detect and group the enemies within areas of 3 km radius. -- -- Setup the detection. We use DETECTION_AREAS to detect and group the enemies within areas of 3 km radius.
-- local DetectionAreas = DETECTION_AREAS -- local DetectionAreas = DETECTION_AREAS
-- :New( RecceSet, 3000 ) -- The RecceSet will detect the enemies. -- :New( RecceSet, 3000 ) -- The RecceSet will detect the enemies.
-- --
-- -- Setup the AttackSet, which is a SET_GROUP. -- -- Setup the AttackSet, which is a SET_GROUP.
-- -- The SET_GROUP is a dynamic collection of GROUP objects. -- -- The SET_GROUP is a dynamic collection of GROUP objects.
-- local AttackSet = SET_GROUP -- local AttackSet = SET_GROUP
-- :New() -- Create the SET_GROUP object. -- :New() -- Create the SET_GROUP object.
-- :FilterCoalitions( "red" ) -- Only incorporate the RED coalitions. -- :FilterCoalitions( "red" ) -- Only incorporate the RED coalitions.
-- :FilterPrefixes( "Attack" ) -- Only incorporate groups that start with the name Attack. -- :FilterPrefixes( "Attack" ) -- Only incorporate groups that start with the name Attack.
-- :FilterStart() -- Enable the dynamic filtering. From this moment the AttackSet will contain all groups that are red and start with the name Attack. -- :FilterStart() -- Enable the dynamic filtering. From this moment the AttackSet will contain all groups that are red and start with the name Attack.
-- --
-- -- Now we have everything to setup the main A2G TaskDispatcher. -- -- Now we have everything to setup the main A2G TaskDispatcher.
-- TaskDispatcher = TASK_A2G_DISPATCHER -- TaskDispatcher = TASK_A2G_DISPATCHER
-- :New( Mission, AttackSet, DetectionAreas ) -- We assign the TaskDispatcher under Mission. The AttackSet will engage the enemy and will recieve the dispatched Tasks. The DetectionAreas will report any detected enemies to the TaskDispatcher. -- :New( Mission, AttackSet, DetectionAreas ) -- We assign the TaskDispatcher under Mission. The AttackSet will engage the enemy and will receive the dispatched Tasks. The DetectionAreas will report any detected enemies to the TaskDispatcher.
-- --
-- --
-- --
-- @field #TASK_A2G_DISPATCHER -- @field #TASK_A2G_DISPATCHER
TASK_A2G_DISPATCHER = { TASK_A2G_DISPATCHER = {
ClassName = "TASK_A2G_DISPATCHER", ClassName = "TASK_A2G_DISPATCHER",
Mission = nil, Mission = nil,
Detection = nil, Detection = nil,
Tasks = {}, Tasks = {}
} }
--- TASK_A2G_DISPATCHER constructor. --- TASK_A2G_DISPATCHER constructor.
-- @param #TASK_A2G_DISPATCHER self -- @param #TASK_A2G_DISPATCHER self
-- @param Tasking.Mission#MISSION Mission The mission for which the task dispatching is done. -- @param Tasking.Mission#MISSION Mission The mission for which the task dispatching is done.
@@ -445,18 +444,18 @@ do -- TASK_A2G_DISPATCHER
-- @param Functional.Detection#DETECTION_BASE Detection The detection results that are used to dynamically assign new tasks to human players. -- @param Functional.Detection#DETECTION_BASE Detection The detection results that are used to dynamically assign new tasks to human players.
-- @return #TASK_A2G_DISPATCHER self -- @return #TASK_A2G_DISPATCHER self
function TASK_A2G_DISPATCHER:New( Mission, SetGroup, Detection ) function TASK_A2G_DISPATCHER:New( Mission, SetGroup, Detection )
-- Inherits from DETECTION_MANAGER -- Inherits from DETECTION_MANAGER
local self = BASE:Inherit( self, DETECTION_MANAGER:New( SetGroup, Detection ) ) -- #TASK_A2G_DISPATCHER local self = BASE:Inherit( self, DETECTION_MANAGER:New( SetGroup, Detection ) ) -- #TASK_A2G_DISPATCHER
self.Detection = Detection self.Detection = Detection
self.Mission = Mission self.Mission = Mission
self.FlashNewTask = true --set to false to suppress flash messages self.FlashNewTask = true -- set to false to suppress flash messages
self.Detection:FilterCategories( { Unit.Category.GROUND_UNIT } ) self.Detection:FilterCategories( { Unit.Category.GROUND_UNIT } )
self:AddTransition( "Started", "Assign", "Started" ) self:AddTransition( "Started", "Assign", "Started" )
--- OnAfter Transition Handler for Event Assign. --- OnAfter Transition Handler for Event Assign.
-- @function [parent=#TASK_A2G_DISPATCHER] OnAfterAssign -- @function [parent=#TASK_A2G_DISPATCHER] OnAfterAssign
-- @param #TASK_A2G_DISPATCHER self -- @param #TASK_A2G_DISPATCHER self
@@ -466,19 +465,19 @@ do -- TASK_A2G_DISPATCHER
-- @param Tasking.Task_A2G#TASK_A2G Task -- @param Tasking.Task_A2G#TASK_A2G Task
-- @param Wrapper.Unit#UNIT TaskUnit -- @param Wrapper.Unit#UNIT TaskUnit
-- @param #string PlayerName -- @param #string PlayerName
self:__Start( 5 ) self:__Start( 5 )
return self return self
end end
--- Set flashing player messages on or off --- Set flashing player messages on or off
-- @param #TASK_A2G_DISPATCHER self -- @param #TASK_A2G_DISPATCHER self
-- @param #boolean onoff Set messages on (true) or off (false) -- @param #boolean onoff Set messages on (true) or off (false)
function TASK_A2G_DISPATCHER:SetSendMessages( onoff ) function TASK_A2G_DISPATCHER:SetSendMessages( onoff )
self.FlashNewTask = onoff self.FlashNewTask = onoff
end end
--- Creates a SEAD task when there are targets for it. --- Creates a SEAD task when there are targets for it.
-- @param #TASK_A2G_DISPATCHER self -- @param #TASK_A2G_DISPATCHER self
-- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem -- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem
@@ -486,7 +485,7 @@ do -- TASK_A2G_DISPATCHER
-- @return #nil If there are no targets to be set. -- @return #nil If there are no targets to be set.
function TASK_A2G_DISPATCHER:EvaluateSEAD( DetectedItem ) function TASK_A2G_DISPATCHER:EvaluateSEAD( DetectedItem )
self:F( { DetectedItem.ItemID } ) self:F( { DetectedItem.ItemID } )
local DetectedSet = DetectedItem.Set local DetectedSet = DetectedItem.Set
local DetectedZone = DetectedItem.Zone local DetectedZone = DetectedItem.Zone
@@ -500,10 +499,10 @@ do -- TASK_A2G_DISPATCHER
TargetSetUnit:SetDatabase( DetectedSet ) TargetSetUnit:SetDatabase( DetectedSet )
TargetSetUnit:FilterHasSEAD() TargetSetUnit:FilterHasSEAD()
TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection. TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection.
return TargetSetUnit return TargetSetUnit
end end
return nil return nil
end end
@@ -514,11 +513,10 @@ do -- TASK_A2G_DISPATCHER
-- @return #nil If there are no targets to be set. -- @return #nil If there are no targets to be set.
function TASK_A2G_DISPATCHER:EvaluateCAS( DetectedItem ) function TASK_A2G_DISPATCHER:EvaluateCAS( DetectedItem )
self:F( { DetectedItem.ItemID } ) self:F( { DetectedItem.ItemID } )
local DetectedSet = DetectedItem.Set local DetectedSet = DetectedItem.Set
local DetectedZone = DetectedItem.Zone local DetectedZone = DetectedItem.Zone
-- Determine if the set has ground units. -- Determine if the set has ground units.
-- There should be ground unit friendlies nearby. Airborne units are valid friendlies types. -- There should be ground unit friendlies nearby. Airborne units are valid friendlies types.
-- And there shouldn't be any radar. -- And there shouldn't be any radar.
@@ -532,13 +530,13 @@ do -- TASK_A2G_DISPATCHER
local TargetSetUnit = SET_UNIT:New() local TargetSetUnit = SET_UNIT:New()
TargetSetUnit:SetDatabase( DetectedSet ) TargetSetUnit:SetDatabase( DetectedSet )
TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection. TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection.
return TargetSetUnit return TargetSetUnit
end end
return nil return nil
end end
--- Creates a BAI task when there are targets for it. --- Creates a BAI task when there are targets for it.
-- @param #TASK_A2G_DISPATCHER self -- @param #TASK_A2G_DISPATCHER self
-- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem -- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem
@@ -546,11 +544,10 @@ do -- TASK_A2G_DISPATCHER
-- @return #nil If there are no targets to be set. -- @return #nil If there are no targets to be set.
function TASK_A2G_DISPATCHER:EvaluateBAI( DetectedItem, FriendlyCoalition ) function TASK_A2G_DISPATCHER:EvaluateBAI( DetectedItem, FriendlyCoalition )
self:F( { DetectedItem.ItemID } ) self:F( { DetectedItem.ItemID } )
local DetectedSet = DetectedItem.Set local DetectedSet = DetectedItem.Set
local DetectedZone = DetectedItem.Zone local DetectedZone = DetectedItem.Zone
-- Determine if the set has ground units. -- Determine if the set has ground units.
-- There shouldn't be any ground unit friendlies nearby. -- There shouldn't be any ground unit friendlies nearby.
-- And there shouldn't be any radar. -- And there shouldn't be any radar.
@@ -564,19 +561,18 @@ do -- TASK_A2G_DISPATCHER
local TargetSetUnit = SET_UNIT:New() local TargetSetUnit = SET_UNIT:New()
TargetSetUnit:SetDatabase( DetectedSet ) TargetSetUnit:SetDatabase( DetectedSet )
TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection. TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection.
return TargetSetUnit return TargetSetUnit
end end
return nil return nil
end end
function TASK_A2G_DISPATCHER:RemoveTask( TaskIndex ) function TASK_A2G_DISPATCHER:RemoveTask( TaskIndex )
self.Mission:RemoveTask( self.Tasks[TaskIndex] ) self.Mission:RemoveTask( self.Tasks[TaskIndex] )
self.Tasks[TaskIndex] = nil self.Tasks[TaskIndex] = nil
end end
--- Evaluates the removal of the Task from the Mission. --- Evaluates the removal of the Task from the Mission.
-- Can only occur when the DetectedItem is Changed AND the state of the Task is "Planned". -- Can only occur when the DetectedItem is Changed AND the state of the Task is "Planned".
-- @param #TASK_A2G_DISPATCHER self -- @param #TASK_A2G_DISPATCHER self
@@ -586,17 +582,16 @@ do -- TASK_A2G_DISPATCHER
-- @param #boolean DetectedItemChange -- @param #boolean DetectedItemChange
-- @return Tasking.Task#TASK -- @return Tasking.Task#TASK
function TASK_A2G_DISPATCHER:EvaluateRemoveTask( Mission, Task, TaskIndex, DetectedItemChanged ) function TASK_A2G_DISPATCHER:EvaluateRemoveTask( Mission, Task, TaskIndex, DetectedItemChanged )
if Task then if Task then
if ( Task:IsStatePlanned() and DetectedItemChanged == true ) or Task:IsStateCancelled() then if (Task:IsStatePlanned() and DetectedItemChanged == true) or Task:IsStateCancelled() then
--self:F( "Removing Tasking: " .. Task:GetTaskName() ) -- self:F( "Removing Tasking: " .. Task:GetTaskName() )
self:RemoveTask( TaskIndex ) self:RemoveTask( TaskIndex )
end end
end end
return Task return Task
end end
--- Assigns tasks in relation to the detected items to the @{Core.Set#SET_GROUP}. --- Assigns tasks in relation to the detected items to the @{Core.Set#SET_GROUP}.
-- @param #TASK_A2G_DISPATCHER self -- @param #TASK_A2G_DISPATCHER self
@@ -604,15 +599,15 @@ do -- TASK_A2G_DISPATCHER
-- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop. -- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop.
function TASK_A2G_DISPATCHER:ProcessDetected( Detection ) function TASK_A2G_DISPATCHER:ProcessDetected( Detection )
self:F() self:F()
local AreaMsg = {} local AreaMsg = {}
local TaskMsg = {} local TaskMsg = {}
local ChangeMsg = {} local ChangeMsg = {}
local Mission = self.Mission local Mission = self.Mission
if Mission:IsIDLE() or Mission:IsENGAGED() then if Mission:IsIDLE() or Mission:IsENGAGED() then
local TaskReport = REPORT:New() local TaskReport = REPORT:New()
-- Checking the task queue for the dispatcher, and removing any obsolete task! -- Checking the task queue for the dispatcher, and removing any obsolete task!
@@ -634,21 +629,21 @@ do -- TASK_A2G_DISPATCHER
--- First we need to the detected targets. --- First we need to the detected targets.
for DetectedItemID, DetectedItem in pairs( Detection:GetDetectedItems() ) do for DetectedItemID, DetectedItem in pairs( Detection:GetDetectedItems() ) do
local DetectedItem = DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem local DetectedItem = DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem
local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT
local DetectedZone = DetectedItem.Zone local DetectedZone = DetectedItem.Zone
--self:F( { "Targets in DetectedItem", DetectedItem.ItemID, DetectedSet:Count(), tostring( DetectedItem ) } ) -- self:F( { "Targets in DetectedItem", DetectedItem.ItemID, DetectedSet:Count(), tostring( DetectedItem ) } )
--DetectedSet:Flush( self ) -- DetectedSet:Flush( self )
local DetectedItemID = DetectedItem.ID local DetectedItemID = DetectedItem.ID
local TaskIndex = DetectedItem.Index local TaskIndex = DetectedItem.Index
local DetectedItemChanged = DetectedItem.Changed local DetectedItemChanged = DetectedItem.Changed
self:F( { DetectedItemChanged = DetectedItemChanged, DetectedItemID = DetectedItemID, TaskIndex = TaskIndex } ) self:F( { DetectedItemChanged = DetectedItemChanged, DetectedItemID = DetectedItemID, TaskIndex = TaskIndex } )
local Task = self.Tasks[TaskIndex] -- Tasking.Task_A2G#TASK_A2G local Task = self.Tasks[TaskIndex] -- Tasking.Task_A2G#TASK_A2G
if Task then if Task then
-- If there is a Task and the task was assigned, then we check if the task was changed ... If it was, we need to reevaluate the targets. -- If there is a Task and the task was assigned, then we check if the task was changed ... If it was, we need to reevaluate the targets.
if Task:IsStateAssigned() then if Task:IsStateAssigned() then
@@ -660,7 +655,7 @@ do -- TASK_A2G_DISPATCHER
Task:SetTargetSetUnit( TargetSetUnit ) Task:SetTargetSetUnit( TargetSetUnit )
Task:SetDetection( Detection, DetectedItem ) Task:SetDetection( Detection, DetectedItem )
Task:UpdateTaskInfo( DetectedItem ) Task:UpdateTaskInfo( DetectedItem )
TargetsReport:Add( Detection:GetChangeText( DetectedItem ) ) TargetsReport:Add( Detection:GetChangeText( DetectedItem ) )
else else
Task:Cancel() Task:Cancel()
end end
@@ -691,18 +686,18 @@ do -- TASK_A2G_DISPATCHER
end end
end end
end end
-- Now we send to each group the changes, if any. -- Now we send to each group the changes, if any.
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
local TargetsText = TargetsReport:Text(", ") local TargetsText = TargetsReport:Text( ", " )
if ( Mission:IsGroupAssigned(TaskGroup) ) and TargetsText ~= "" and self.FlashNewTask then if (Mission:IsGroupAssigned( TaskGroup )) and TargetsText ~= "" and self.FlashNewTask then
Mission:GetCommandCenter():MessageToGroup( string.format( "Task %s has change of targets:\n %s", Task:GetName(), TargetsText ), TaskGroup ) Mission:GetCommandCenter():MessageToGroup( string.format( "Task %s has change of targets:\n %s", Task:GetName(), TargetsText ), TaskGroup )
end end
end end
end end
end end
end end
if Task then if Task then
if Task:IsStatePlanned() then if Task:IsStatePlanned() then
if DetectedItemChanged == true then -- The detection has changed, thus a new TargetSet is to be evaluated and set if DetectedItemChanged == true then -- The detection has changed, thus a new TargetSet is to be evaluated and set
@@ -753,7 +748,7 @@ do -- TASK_A2G_DISPATCHER
local TargetSetUnit = self:EvaluateSEAD( DetectedItem ) -- Returns a SetUnit if there are targets to be SEADed... local TargetSetUnit = self:EvaluateSEAD( DetectedItem ) -- Returns a SetUnit if there are targets to be SEADed...
if TargetSetUnit then if TargetSetUnit then
Task = TASK_A2G_SEAD:New( Mission, self.SetGroup, string.format( "SEAD.%03d", DetectedItemID ), TargetSetUnit ) Task = TASK_A2G_SEAD:New( Mission, self.SetGroup, string.format( "SEAD.%03d", DetectedItemID ), TargetSetUnit )
DetectedItem.DesignateMenuName = string.format( "SEAD.%03d", DetectedItemID ) --inject a name for DESIGNATE, if using same DETECTION object DetectedItem.DesignateMenuName = string.format( "SEAD.%03d", DetectedItemID ) -- inject a name for DESIGNATE, if using same DETECTION object
Task:SetDetection( Detection, DetectedItem ) Task:SetDetection( Detection, DetectedItem )
end end
@@ -762,7 +757,7 @@ do -- TASK_A2G_DISPATCHER
local TargetSetUnit = self:EvaluateCAS( DetectedItem ) -- Returns a SetUnit if there are targets to be CASed... local TargetSetUnit = self:EvaluateCAS( DetectedItem ) -- Returns a SetUnit if there are targets to be CASed...
if TargetSetUnit then if TargetSetUnit then
Task = TASK_A2G_CAS:New( Mission, self.SetGroup, string.format( "CAS.%03d", DetectedItemID ), TargetSetUnit ) Task = TASK_A2G_CAS:New( Mission, self.SetGroup, string.format( "CAS.%03d", DetectedItemID ), TargetSetUnit )
DetectedItem.DesignateMenuName = string.format( "CAS.%03d", DetectedItemID ) --inject a name for DESIGNATE, if using same DETECTION object DetectedItem.DesignateMenuName = string.format( "CAS.%03d", DetectedItemID ) -- inject a name for DESIGNATE, if using same DETECTION object
Task:SetDetection( Detection, DetectedItem ) Task:SetDetection( Detection, DetectedItem )
end end
@@ -771,19 +766,19 @@ do -- TASK_A2G_DISPATCHER
local TargetSetUnit = self:EvaluateBAI( DetectedItem, self.Mission:GetCommandCenter():GetPositionable():GetCoalition() ) -- Returns a SetUnit if there are targets to be BAIed... local TargetSetUnit = self:EvaluateBAI( DetectedItem, self.Mission:GetCommandCenter():GetPositionable():GetCoalition() ) -- Returns a SetUnit if there are targets to be BAIed...
if TargetSetUnit then if TargetSetUnit then
Task = TASK_A2G_BAI:New( Mission, self.SetGroup, string.format( "BAI.%03d", DetectedItemID ), TargetSetUnit ) Task = TASK_A2G_BAI:New( Mission, self.SetGroup, string.format( "BAI.%03d", DetectedItemID ), TargetSetUnit )
DetectedItem.DesignateMenuName = string.format( "BAI.%03d", DetectedItemID ) --inject a name for DESIGNATE, if using same DETECTION object DetectedItem.DesignateMenuName = string.format( "BAI.%03d", DetectedItemID ) -- inject a name for DESIGNATE, if using same DETECTION object
Task:SetDetection( Detection, DetectedItem ) Task:SetDetection( Detection, DetectedItem )
end end
end end
end end
if Task then if Task then
self.Tasks[TaskIndex] = Task self.Tasks[TaskIndex] = Task
Task:SetTargetZone( DetectedZone ) Task:SetTargetZone( DetectedZone )
Task:SetDispatcher( self ) Task:SetDispatcher( self )
Task:UpdateTaskInfo( DetectedItem ) Task:UpdateTaskInfo( DetectedItem )
Mission:AddTask( Task ) Mission:AddTask( Task )
function Task.OnEnterSuccess( Task, From, Event, To ) function Task.OnEnterSuccess( Task, From, Event, To )
self:Success( Task ) self:Success( Task )
end end
@@ -791,7 +786,7 @@ do -- TASK_A2G_DISPATCHER
function Task.OnEnterCancelled( Task, From, Event, To ) function Task.OnEnterCancelled( Task, From, Event, To )
self:Cancelled( Task ) self:Cancelled( Task )
end end
function Task.OnEnterFailed( Task, From, Event, To ) function Task.OnEnterFailed( Task, From, Event, To )
self:Failed( Task ) self:Failed( Task )
end end
@@ -799,31 +794,29 @@ do -- TASK_A2G_DISPATCHER
function Task.OnEnterAborted( Task, From, Event, To ) function Task.OnEnterAborted( Task, From, Event, To )
self:Aborted( Task ) self:Aborted( Task )
end end
TaskReport:Add( Task:GetName() ) TaskReport:Add( Task:GetName() )
else else
self:F("This should not happen") self:F( "This should not happen" )
end end
end end
-- OK, so the tasking has been done, now delete the changes reported for the area. -- OK, so the tasking has been done, now delete the changes reported for the area.
Detection:AcceptChanges( DetectedItem ) Detection:AcceptChanges( DetectedItem )
end end
-- TODO set menus using the HQ coordinator -- TODO set menus using the HQ coordinator
Mission:GetCommandCenter():SetMenu() Mission:GetCommandCenter():SetMenu()
local TaskText = TaskReport:Text(", ") local TaskText = TaskReport:Text( ", " )
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
if ( not Mission:IsGroupAssigned(TaskGroup) ) and TaskText ~= "" and self.FlashNewTask then if (not Mission:IsGroupAssigned( TaskGroup )) and TaskText ~= "" and self.FlashNewTask then
Mission:GetCommandCenter():MessageToGroup( string.format( "%s has tasks %s. Subscribe to a task using the radio menu.", Mission:GetShortText(), TaskText ), TaskGroup ) Mission:GetCommandCenter():MessageToGroup( string.format( "%s has tasks %s. Subscribe to a task using the radio menu.", Mission:GetShortText(), TaskText ), TaskGroup )
end end
end end
end end
return true return true
end end

View File

@@ -1,18 +1,18 @@
--- **Utilities** Enumerators. --- **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. -- 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. -- 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 -- 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. -- 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 -- 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. -- DCS itself provides a lot of enumerators for various things. See [Enumerators](https://wiki.hoggitworld.com/view/Category:Enumerators) on Hoggit.
-- --
-- Other Moose classe also have enumerators. For example, the AIRBASE class has enumerators for airbase names. -- Other Moose classes also have enumerators. For example, the AIRBASE class has enumerators for airbase names.
-- --
-- @module ENUMS -- @module ENUMS
-- @image MOOSE.JPG -- @image MOOSE.JPG
@@ -20,7 +20,7 @@
-- @type ENUMS -- @type ENUMS
--- Because ENUMS are just better practice. --- Because ENUMS are just better practice.
-- --
-- The ENUMS class adds some handy variables, which help you to make your code better and more general. -- The ENUMS class adds some handy variables, which help you to make your code better and more general.
-- --
-- @field #ENUMS -- @field #ENUMS
@@ -30,16 +30,16 @@ ENUMS = {}
-- @type ENUMS.ROE -- @type ENUMS.ROE
-- @field #number WeaponFree AI will engage any enemy group it detects. Target prioritization is based based on the threat of the target. -- @field #number WeaponFree AI will engage any enemy group it detects. Target prioritization is based based on the threat of the target.
-- @field #number OpenFireWeaponFree AI will engage any enemy group it detects, but will prioritize targets specified in the groups tasking. -- @field #number OpenFireWeaponFree AI will engage any enemy group it detects, but will prioritize targets specified in the groups tasking.
-- @field #number OpenFire AI will engage only targets specified in its taskings. -- @field #number OpenFire AI will engage only targets specified in its tasking.
-- @field #number ReturnFire AI will only engage threats that shoot first. -- @field #number ReturnFire AI will only engage threats that shoot first.
-- @field #number WeaponHold AI will hold fire under all circumstances. -- @field #number WeaponHold AI will hold fire under all circumstances.
ENUMS.ROE = { ENUMS.ROE = {
WeaponFree=0, WeaponFree = 0,
OpenFireWeaponFree=1, OpenFireWeaponFree = 1,
OpenFire=2, OpenFire = 2,
ReturnFire=3, ReturnFire = 3,
WeaponHold=4, WeaponHold = 4,
} }
--- Reaction On Threat. --- Reaction On Threat.
-- @type ENUMS.ROT -- @type ENUMS.ROT
@@ -49,11 +49,11 @@ ENUMS.ROE = {
-- @field #number BypassAndEscape AI will attempt to avoid enemy threat zones all together. This includes attempting to fly above or around threats. -- @field #number BypassAndEscape AI will attempt to avoid enemy threat zones all together. This includes attempting to fly above or around threats.
-- @field #number AllowAbortMission If a threat is deemed severe enough the AI will abort its mission and return to base. -- @field #number AllowAbortMission If a threat is deemed severe enough the AI will abort its mission and return to base.
ENUMS.ROT = { ENUMS.ROT = {
NoReaction=0, NoReaction = 0,
PassiveDefense=1, PassiveDefense = 1,
EvadeFire=2, EvadeFire = 2,
BypassAndEscape=3, BypassAndEscape = 3,
AllowAbortMission=4, AllowAbortMission = 4,
} }
--- Alarm state. --- Alarm state.
@@ -62,12 +62,12 @@ ENUMS.ROT = {
-- @field #number Green Group is not combat ready. Sensors are stowed if possible. -- @field #number Green Group is not combat ready. Sensors are stowed if possible.
-- @field #number Red Group is combat ready and actively searching for targets. Some groups like infantry will not move in this state. -- @field #number Red Group is combat ready and actively searching for targets. Some groups like infantry will not move in this state.
ENUMS.AlarmState = { ENUMS.AlarmState = {
Auto=0, Auto = 0,
Green=1, Green = 1,
Red=2, Red = 2,
} }
--- Weapon types. See the [Weapon Flag](https://wiki.hoggitworld.com/view/DCS_enum_weapon_flag) enumerotor on hoggit wiki. --- Weapon types. See the [Weapon Flag](https://wiki.hoggitworld.com/view/DCS_enum_weapon_flag) enumerator on Hoggit wiki.
-- @type ENUMS.WeaponFlag -- @type ENUMS.WeaponFlag
ENUMS.WeaponFlag={ ENUMS.WeaponFlag={
-- Bombs -- Bombs
@@ -111,7 +111,7 @@ ENUMS.WeaponFlag={
-- --
-- Bombs -- Bombs
GuidedBomb = 14, -- (LGB + TvGB + SNSGB) GuidedBomb = 14, -- (LGB + TvGB + SNSGB)
AnyUnguidedBomb = 2147485680, -- (HeBomb + Penetrator + NapalmBomb + FAEBomb + ClusterBomb + Dispencer + CandleBomb + ParachuteBomb) AnyUnguidedBomb = 2147485680, -- (HeBomb + Penetrator + NapalmBomb + FAEBomb + ClusterBomb + Dispenser + CandleBomb + ParachuteBomb)
AnyBomb = 2147485694, -- (GuidedBomb + AnyUnguidedBomb) AnyBomb = 2147485694, -- (GuidedBomb + AnyUnguidedBomb)
--- Rockets --- Rockets
AnyRocket = 30720, -- LightRocket + MarkerRocket + CandleRocket + HeavyRocket AnyRocket = 30720, -- LightRocket + MarkerRocket + CandleRocket + HeavyRocket
@@ -123,7 +123,7 @@ ENUMS.WeaponFlag={
--- Air-To-Air Missiles --- Air-To-Air Missiles
AnyAAM = 264241152, -- IR_AAM + SAR_AAM + AR_AAM + SRAAM + MRAAM + LRAAM AnyAAM = 264241152, -- IR_AAM + SAR_AAM + AR_AAM + SRAAM + MRAAM + LRAAM
AnyAutonomousMissile = 36012032, -- IR_AAM + AntiRadarMissile + AntiShipMissile + FireAndForgetASM + CruiseMissile AnyAutonomousMissile = 36012032, -- IR_AAM + AntiRadarMissile + AntiShipMissile + FireAndForgetASM + CruiseMissile
AnyMissile = 268402688, -- AnyASM + AnyAAM AnyMissile = 268402688, -- AnyASM + AnyAAM
--- Guns --- Guns
Cannons = 805306368, -- GUN_POD + BuiltInCannon Cannons = 805306368, -- GUN_POD + BuiltInCannon
--- ---
@@ -133,7 +133,7 @@ ENUMS.WeaponFlag={
AnyAG = 2956984318, -- Any Air-To-Ground Weapon AnyAG = 2956984318, -- Any Air-To-Ground Weapon
AnyAA = 264241152, -- Any Air-To-Air Weapon AnyAA = 264241152, -- Any Air-To-Air Weapon
AnyUnguided = 2952822768, -- Any Unguided Weapon AnyUnguided = 2952822768, -- Any Unguided Weapon
AnyGuided = 268402702, -- Any Guided Weapon AnyGuided = 268402702, -- Any Guided Weapon
} }
--- Mission tasks. --- Mission tasks.
@@ -173,7 +173,7 @@ ENUMS.MissionTask={
TRANSPORT="Transport", TRANSPORT="Transport",
} }
--- Formations (new). See the [Formations](https://wiki.hoggitworld.com/view/DCS_enum_formation) on hoggit wiki. --- Formations (new). See the [Formations](https://wiki.hoggitworld.com/view/DCS_enum_formation) on Hoggit wiki.
-- @type ENUMS.Formation -- @type ENUMS.Formation
ENUMS.Formation={} ENUMS.Formation={}
ENUMS.Formation.FixedWing={} ENUMS.Formation.FixedWing={}
@@ -216,23 +216,23 @@ ENUMS.Formation.FixedWing.FighterVic.Close = 917505
ENUMS.Formation.FixedWing.FighterVic.Open = 917506 ENUMS.Formation.FixedWing.FighterVic.Open = 917506
ENUMS.Formation.RotaryWing={} ENUMS.Formation.RotaryWing={}
ENUMS.Formation.RotaryWing.Column={} ENUMS.Formation.RotaryWing.Column={}
ENUMS.Formation.RotaryWing.Column.D70=720896 ENUMS.Formation.RotaryWing.Column.D70 = 720896
ENUMS.Formation.RotaryWing.Wedge={} ENUMS.Formation.RotaryWing.Wedge={}
ENUMS.Formation.RotaryWing.Wedge.D70=8 ENUMS.Formation.RotaryWing.Wedge.D70 = 8
ENUMS.Formation.RotaryWing.FrontRight={} ENUMS.Formation.RotaryWing.FrontRight={}
ENUMS.Formation.RotaryWing.FrontRight.D300=655361 ENUMS.Formation.RotaryWing.FrontRight.D300 = 655361
ENUMS.Formation.RotaryWing.FrontRight.D600=655362 ENUMS.Formation.RotaryWing.FrontRight.D600 = 655362
ENUMS.Formation.RotaryWing.FrontLeft={} ENUMS.Formation.RotaryWing.FrontLeft={}
ENUMS.Formation.RotaryWing.FrontLeft.D300=655617 ENUMS.Formation.RotaryWing.FrontLeft.D300 = 655617
ENUMS.Formation.RotaryWing.FrontLeft.D600=655618 ENUMS.Formation.RotaryWing.FrontLeft.D600 = 655618
ENUMS.Formation.RotaryWing.EchelonRight={} ENUMS.Formation.RotaryWing.EchelonRight={}
ENUMS.Formation.RotaryWing.EchelonRight.D70 =589825 ENUMS.Formation.RotaryWing.EchelonRight.D70 = 589825
ENUMS.Formation.RotaryWing.EchelonRight.D300=589826 ENUMS.Formation.RotaryWing.EchelonRight.D300 = 589826
ENUMS.Formation.RotaryWing.EchelonRight.D600=589827 ENUMS.Formation.RotaryWing.EchelonRight.D600 = 589827
ENUMS.Formation.RotaryWing.EchelonLeft={} ENUMS.Formation.RotaryWing.EchelonLeft={}
ENUMS.Formation.RotaryWing.EchelonLeft.D70 =590081 ENUMS.Formation.RotaryWing.EchelonLeft.D70 = 590081
ENUMS.Formation.RotaryWing.EchelonLeft.D300=590082 ENUMS.Formation.RotaryWing.EchelonLeft.D300 = 590082
ENUMS.Formation.RotaryWing.EchelonLeft.D600=590083 ENUMS.Formation.RotaryWing.EchelonLeft.D600 = 590083
ENUMS.Formation.Vehicle={} ENUMS.Formation.Vehicle={}
ENUMS.Formation.Vehicle.Vee="Vee" ENUMS.Formation.Vehicle.Vee="Vee"
ENUMS.Formation.Vehicle.EchelonRight="EchelonR" ENUMS.Formation.Vehicle.EchelonRight="EchelonR"
@@ -244,34 +244,34 @@ ENUMS.Formation.Vehicle.Cone="Cone"
ENUMS.Formation.Vehicle.Diamond="Diamond" ENUMS.Formation.Vehicle.Diamond="Diamond"
--- Formations (old). The old format is a simplified version of the new formation enums, which allow more sophisticated settings. --- Formations (old). The old format is a simplified version of the new formation enums, which allow more sophisticated settings.
-- See the [Formations](https://wiki.hoggitworld.com/view/DCS_enum_formation) on hoggit wiki. -- See the [Formations](https://wiki.hoggitworld.com/view/DCS_enum_formation) on Hoggit wiki.
-- @type ENUMS.FormationOld -- @type ENUMS.FormationOld
ENUMS.FormationOld={} ENUMS.FormationOld={}
ENUMS.FormationOld.FixedWing={} ENUMS.FormationOld.FixedWing={}
ENUMS.FormationOld.FixedWing.LineAbreast=1 ENUMS.FormationOld.FixedWing.LineAbreast = 1
ENUMS.FormationOld.FixedWing.Trail=2 ENUMS.FormationOld.FixedWing.Trail = 2
ENUMS.FormationOld.FixedWing.Wedge=3 ENUMS.FormationOld.FixedWing.Wedge = 3
ENUMS.FormationOld.FixedWing.EchelonRight=4 ENUMS.FormationOld.FixedWing.EchelonRight = 4
ENUMS.FormationOld.FixedWing.EchelonLeft=5 ENUMS.FormationOld.FixedWing.EchelonLeft = 5
ENUMS.FormationOld.FixedWing.FingerFour=6 ENUMS.FormationOld.FixedWing.FingerFour = 6
ENUMS.FormationOld.FixedWing.SpreadFour=7 ENUMS.FormationOld.FixedWing.SpreadFour = 7
ENUMS.FormationOld.FixedWing.BomberElement=12 ENUMS.FormationOld.FixedWing.BomberElement = 12
ENUMS.FormationOld.FixedWing.BomberElementHeight=13 ENUMS.FormationOld.FixedWing.BomberElementHeight = 13
ENUMS.FormationOld.FixedWing.FighterVic=14 ENUMS.FormationOld.FixedWing.FighterVic = 14
ENUMS.FormationOld.RotaryWing={} ENUMS.FormationOld.RotaryWing={}
ENUMS.FormationOld.RotaryWing.Wedge=8 ENUMS.FormationOld.RotaryWing.Wedge = 8
ENUMS.FormationOld.RotaryWing.Echelon=9 ENUMS.FormationOld.RotaryWing.Echelon = 9
ENUMS.FormationOld.RotaryWing.Front=10 ENUMS.FormationOld.RotaryWing.Front = 10
ENUMS.FormationOld.RotaryWing.Column=11 ENUMS.FormationOld.RotaryWing.Column = 11
--- Morse Code. See the [Wikipedia](https://en.wikipedia.org/wiki/Morse_code). --- Morse Code. See the [Wikipedia](https://en.wikipedia.org/wiki/Morse_code).
-- --
-- * Short pulse "*" -- * Short pulse "*"
-- * Long pulse "-" -- * Long pulse "-"
-- --
-- Pulses are separated by a blank character " ". -- Pulses are separated by a blank character " ".
-- --
-- @type ENUMS.Morse -- @type ENUMS.Morse
ENUMS.Morse={} ENUMS.Morse={}
ENUMS.Morse.A="* -" ENUMS.Morse.A="* -"
@@ -313,9 +313,9 @@ ENUMS.Morse.N0="- - - - -"
ENUMS.Morse[" "]=" " ENUMS.Morse[" "]=" "
--- ISO (639-1) 2-letter Language Codes. See the [Wikipedia](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes). --- ISO (639-1) 2-letter Language Codes. See the [Wikipedia](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes).
-- --
-- @type ENUMS.ISOLang -- @type ENUMS.ISOLang
ENUMS.ISOLang = ENUMS.ISOLang =
{ {
Arabic = 'AR', Arabic = 'AR',
Chinese = 'ZH', Chinese = 'ZH',
@@ -329,7 +329,7 @@ ENUMS.ISOLang =
} }
--- Phonetic Alphabet (NATO). See the [Wikipedia](https://en.wikipedia.org/wiki/NATO_phonetic_alphabet). --- Phonetic Alphabet (NATO). See the [Wikipedia](https://en.wikipedia.org/wiki/NATO_phonetic_alphabet).
-- --
-- @type ENUMS.Phonetic -- @type ENUMS.Phonetic
ENUMS.Phonetic = ENUMS.Phonetic =
{ {

View File

@@ -5,11 +5,10 @@
-- === -- ===
-- --
-- ### Author: **TAW CougarNL**, *funkyfranky* -- ### Author: **TAW CougarNL**, *funkyfranky*
-- --
-- @module Utilities.PROFILER -- @module Utilities.PROFILER
-- @image MOOSE.JPG -- @image MOOSE.JPG
--- PROFILER class. --- PROFILER class.
-- @type PROFILER -- @type PROFILER
-- @field #string ClassName Name of the class. -- @field #string ClassName Name of the class.
@@ -25,7 +24,6 @@
-- @field #number ThreshTtot Total time threshold. Only write output if total function CPU time is more than this value. -- @field #number ThreshTtot Total time threshold. Only write output if total function CPU time is more than this value.
-- @field #string fileNamePrefix Output file name prefix, e.g. "MooseProfiler". -- @field #string fileNamePrefix Output file name prefix, e.g. "MooseProfiler".
-- @field #string fileNameSuffix Output file name prefix, e.g. "txt" -- @field #string fileNameSuffix Output file name prefix, e.g. "txt"
--- *The emperor counsels simplicity. First principles. Of each particular thing, ask: What is it in itself, in its own constitution? What is its causal nature? * --- *The emperor counsels simplicity. First principles. Of each particular thing, ask: What is it in itself, in its own constitution? What is its causal nature? *
-- --
-- === -- ===
@@ -33,60 +31,59 @@
-- ![Banner Image](..\Presentations\Utilities\PROFILER_Main.jpg) -- ![Banner Image](..\Presentations\Utilities\PROFILER_Main.jpg)
-- --
-- # The PROFILER Concept -- # The PROFILER Concept
-- --
-- Profile your lua code. This tells you, which functions are called very often and which consume most real time. -- Profile your lua code. This tells you, which functions are called very often and which consume most real time.
-- With this information you can optimize the perfomance of your code. -- With this information you can optimize the performance of your code.
-- --
-- # Prerequisites -- # Prerequisites
-- --
-- The modules **os** and **lfs** need to be desanizied. -- The modules **os** and **lfs** need to be de-sanitized.
-- --
--
-- # Start -- # Start
-- --
-- The profiler can simply be started with the @{#PROFILER.Start}(*Delay, Duration*) function -- The profiler can simply be started with the @{#PROFILER.Start}(*Delay, Duration*) function
-- --
-- PROFILER.Start() -- PROFILER.Start()
-- --
-- The optional parameter *Delay* can be used to delay the start by a certain amount of seconds and the optional parameter *Duration* can be used to -- The optional parameter *Delay* can be used to delay the start by a certain amount of seconds and the optional parameter *Duration* can be used to
-- stop the profiler after a certain amount of seconds. -- stop the profiler after a certain amount of seconds.
-- --
-- # Stop -- # Stop
-- --
-- The profiler automatically stops when the mission ends. But it can be stopped any time with the @{#PROFILER.Stop}(*Delay*) function -- The profiler automatically stops when the mission ends. But it can be stopped any time with the @{#PROFILER.Stop}(*Delay*) function
-- --
-- PROFILER.Stop() -- PROFILER.Stop()
-- --
-- The optional parameter *Delay* can be used to specify a delay after which the profiler is stopped. -- The optional parameter *Delay* can be used to specify a delay after which the profiler is stopped.
-- --
-- When the profiler is stopped, the output is written to a file. -- When the profiler is stopped, the output is written to a file.
-- --
-- # Output -- # Output
-- --
-- The profiler output is written to a file in your DCS home folder -- The profiler output is written to a file in your DCS home folder
-- --
-- X:\User\<Your User Name>\Saved Games\DCS OpenBeta\Logs -- X:\User\<Your User Name>\Saved Games\DCS OpenBeta\Logs
-- --
-- The default file name is "MooseProfiler.txt". If that file exists, the file name is "MooseProfiler-001.txt" etc. -- The default file name is "MooseProfiler.txt". If that file exists, the file name is "MooseProfiler-001.txt" etc.
-- --
-- ## Data -- ## Data
-- --
-- The data in the output file provides information on the functions that were called in the mission. -- The data in the output file provides information on the functions that were called in the mission.
-- --
-- It will tell you how many times a function was called in total, how many times per second, how much time in total and the percentage of time. -- It will tell you how many times a function was called in total, how many times per second, how much time in total and the percentage of time.
-- --
-- If you only want output for functions that are called more than *X* times per second, you can set -- If you only want output for functions that are called more than *X* times per second, you can set
-- --
-- PROFILER.ThreshCPS=1.5 -- PROFILER.ThreshCPS=1.5
-- --
-- With this setting, only functions which are called more than 1.5 times per second are displayed. The default setting is PROFILER.ThreshCPS=0.0 (no threshold). -- With this setting, only functions which are called more than 1.5 times per second are displayed. The default setting is PROFILER.ThreshCPS=0.0 (no threshold).
-- --
-- Furthermore, you can limit the output for functions that consumed a certain amount of CPU time in total by -- Furthermore, you can limit the output for functions that consumed a certain amount of CPU time in total by
-- --
-- PROFILER.ThreshTtot=0.005 -- PROFILER.ThreshTtot=0.005
-- --
-- With this setting, which is also the default, only functions which in total used more than 5 milliseconds CPU time. -- With this setting, which is also the default, only functions which in total used more than 5 milliseconds CPU time.
-- --
-- @field #PROFILER -- @field #PROFILER
PROFILER = { PROFILER = {
ClassName = "PROFILER", ClassName = "PROFILER",
@@ -117,97 +114,95 @@ PROFILER = {
--- Start profiler. --- Start profiler.
-- @param #number Delay Delay in seconds before profiler is stated. Default is immediately. -- @param #number Delay Delay in seconds before profiler is stated. Default is immediately.
-- @param #number Duration Duration in (game) seconds before the profiler is stopped. Default is when mission ends. -- @param #number Duration Duration in (game) seconds before the profiler is stopped. Default is when mission ends.
function PROFILER.Start(Delay, Duration) function PROFILER.Start( Delay, Duration )
-- Check if os, io and lfs are available. -- Check if os, io and lfs are available.
local go=true local go = true
if not os then if not os then
env.error("ERROR: Profiler needs os to be desanitized!") env.error( "ERROR: Profiler needs os to be de-sanitized!" )
go=false go = false
end end
if not io then if not io then
env.error("ERROR: Profiler needs io to be desanitized!") env.error( "ERROR: Profiler needs io to be de-sanitized!" )
go=false go = false
end end
if not lfs then if not lfs then
env.error("ERROR: Profiler needs lfs to be desanitized!") env.error( "ERROR: Profiler needs lfs to be de-sanitized!" )
go=false go = false
end end
if not go then if not go then
return return
end end
if Delay and Delay>0 then if Delay and Delay > 0 then
BASE:ScheduleOnce(Delay, PROFILER.Start, 0, Duration) BASE:ScheduleOnce( Delay, PROFILER.Start, 0, Duration )
else else
-- Set start time. -- Set start time.
PROFILER.TstartGame=timer.getTime() PROFILER.TstartGame = timer.getTime()
PROFILER.TstartOS=os.clock() PROFILER.TstartOS = os.clock()
-- Add event handler. -- Add event handler.
world.addEventHandler(PROFILER.eventHandler) world.addEventHandler( PROFILER.eventHandler )
-- Info in log. -- Info in log.
env.info('############################ Profiler Started ############################') env.info( '############################ Profiler Started ############################' )
if Duration then if Duration then
env.info(string.format("- Will be running for %d seconds", Duration)) env.info( string.format( "- Will be running for %d seconds", Duration ) )
else else
env.info(string.format("- Will be stopped when mission ends")) env.info( string.format( "- Will be stopped when mission ends" ) )
end end
env.info(string.format("- Calls per second threshold %.3f/sec", PROFILER.ThreshCPS)) env.info( string.format( "- Calls per second threshold %.3f/sec", PROFILER.ThreshCPS ) )
env.info(string.format("- Total function time threshold %.3f sec", PROFILER.ThreshTtot)) env.info( string.format( "- Total function time threshold %.3f sec", PROFILER.ThreshTtot ) )
env.info(string.format("- Output file \"%s\" in your DCS log file folder", PROFILER.getfilename(PROFILER.fileNameSuffix))) env.info( string.format( "- Output file \"%s\" in your DCS log file folder", PROFILER.getfilename( PROFILER.fileNameSuffix ) ) )
env.info(string.format("- Output file \"%s\" in CSV format", PROFILER.getfilename("csv"))) env.info( string.format( "- Output file \"%s\" in CSV format", PROFILER.getfilename( "csv" ) ) )
env.info('###############################################################################') env.info( '###############################################################################' )
-- Message on screen -- Message on screen
local duration=Duration or 600 local duration = Duration or 600
trigger.action.outText("### Profiler running ###", duration) trigger.action.outText( "### Profiler running ###", duration )
-- Set hook. -- Set hook.
debug.sethook(PROFILER.hook, "cr") debug.sethook( PROFILER.hook, "cr" )
-- Auto stop profiler. -- Auto stop profiler.
if Duration then if Duration then
PROFILER.Stop(Duration) PROFILER.Stop( Duration )
end end
end end
end end
--- Stop profiler. --- Stop profiler.
-- @param #number Delay Delay before stop in seconds. -- @param #number Delay Delay before stop in seconds.
function PROFILER.Stop(Delay) 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 else
-- Remove hook. -- Remove hook.
debug.sethook() debug.sethook()
-- Run time game. -- Run time game.
local runTimeGame=timer.getTime()-PROFILER.TstartGame local runTimeGame = timer.getTime() - PROFILER.TstartGame
-- Run time real OS. -- Run time real OS.
local runTimeOS=os.clock()-PROFILER.TstartOS local runTimeOS = os.clock() - PROFILER.TstartOS
-- Show info. -- Show info.
PROFILER.showInfo(runTimeGame, runTimeOS) PROFILER.showInfo( runTimeGame, runTimeOS )
end end
end end
--- Event handler. --- Event handler.
function PROFILER.eventHandler:onEvent(event) function PROFILER.eventHandler:onEvent( event )
if event.id==world.event.S_EVENT_MISSION_END then if event.id == world.event.S_EVENT_MISSION_END then
PROFILER.Stop() PROFILER.Stop()
end end
end end
@@ -218,38 +213,38 @@ end
--- Debug hook. --- Debug hook.
-- @param #table event Event. -- @param #table event Event.
function PROFILER.hook(event) function PROFILER.hook( event )
local f=debug.getinfo(2, "f").func local f = debug.getinfo( 2, "f" ).func
if event=='call' then if event == 'call' then
if PROFILER.Counters[f]==nil then if PROFILER.Counters[f] == nil then
PROFILER.Counters[f]=1 PROFILER.Counters[f] = 1
PROFILER.dInfo[f]=debug.getinfo(2,"Sn") PROFILER.dInfo[f] = debug.getinfo( 2, "Sn" )
if PROFILER.fTimeTotal[f]==nil then if PROFILER.fTimeTotal[f] == nil then
PROFILER.fTimeTotal[f]=0 PROFILER.fTimeTotal[f] = 0
end end
else else
PROFILER.Counters[f]=PROFILER.Counters[f]+1 PROFILER.Counters[f] = PROFILER.Counters[f] + 1
end end
if PROFILER.fTime[f]==nil then if PROFILER.fTime[f] == nil then
PROFILER.fTime[f]=os.clock() PROFILER.fTime[f] = os.clock()
end end
elseif (event=='return') then elseif (event == 'return') then
if PROFILER.fTime[f]~=nil then if PROFILER.fTime[f] ~= nil then
PROFILER.fTimeTotal[f]=PROFILER.fTimeTotal[f]+(os.clock()-PROFILER.fTime[f]) PROFILER.fTimeTotal[f] = PROFILER.fTimeTotal[f] + (os.clock() - PROFILER.fTime[f])
PROFILER.fTime[f]=nil PROFILER.fTime[f] = nil
end end
end end
end end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -262,105 +257,104 @@ end
-- @return #string Source file name. -- @return #string Source file name.
-- @return #string Line number. -- @return #string Line number.
-- @return #number Function time in seconds. -- @return #number Function time in seconds.
function PROFILER.getData(func) function PROFILER.getData( func )
local n=PROFILER.dInfo[func] local n = PROFILER.dInfo[func]
if n.what=="C" then if n.what == "C" then
return n.name, "?", "?", PROFILER.fTimeTotal[func] return n.name, "?", "?", PROFILER.fTimeTotal[func]
end end
return n.name, n.short_src, n.linedefined, PROFILER.fTimeTotal[func] return n.name, n.short_src, n.linedefined, PROFILER.fTimeTotal[func]
end end
--- Write text to log file. --- Write text to log file.
-- @param #function f The file. -- @param #function f The file.
-- @param #string txt The text. -- @param #string txt The text.
function PROFILER._flog(f, txt) function PROFILER._flog( f, txt )
f:write(txt.."\r\n") f:write( txt .. "\r\n" )
end end
--- Show table. --- Show table.
-- @param #table data Data table. -- @param #table data Data table.
-- @param #function f The file. -- @param #function f The file.
-- @param #number runTimeGame Game run time in seconds. -- @param #number runTimeGame Game run time in seconds.
function PROFILER.showTable(data, f, runTimeGame) function PROFILER.showTable( data, f, runTimeGame )
-- Loop over data. -- Loop over data.
for i=1, #data do for i = 1, #data do
local t=data[i] --#PROFILER.Data local t = data[i] -- #PROFILER.Data
-- Calls per second. -- Calls per second.
local cps=t.count/runTimeGame local cps = t.count / runTimeGame
local threshCPS=cps>=PROFILER.ThreshCPS local threshCPS = cps >= PROFILER.ThreshCPS
local threshTot=t.tm>=PROFILER.ThreshTtot local threshTot = t.tm >= PROFILER.ThreshTtot
if threshCPS and threshTot then if threshCPS and threshTot then
-- Output -- Output
local text=string.format("%30s: %8d calls %8.1f/sec - Time Total %8.3f sec (%.3f %%) %5.3f sec/call %s line %s", t.func, t.count, cps, t.tm, t.tm/runTimeGame*100, t.tm/t.count, tostring(t.src), tostring(t.line)) local text = string.format( "%30s: %8d calls %8.1f/sec - Time Total %8.3f sec (%.3f %%) %5.3f sec/call %s line %s", t.func, t.count, cps, t.tm, t.tm / runTimeGame * 100, t.tm / t.count, tostring( t.src ), tostring( t.line ) )
PROFILER._flog(f, text) PROFILER._flog( f, text )
end end
end end
end end
--- Print csv file. --- Print csv file.
-- @param #table data Data table. -- @param #table data Data table.
-- @param #number runTimeGame Game run time in seconds. -- @param #number runTimeGame Game run time in seconds.
function PROFILER.printCSV(data, runTimeGame) function PROFILER.printCSV( data, runTimeGame )
-- Output file. -- Output file.
local file=PROFILER.getfilename("csv") local file = PROFILER.getfilename( "csv" )
local g=io.open(file, 'w') local g = io.open( file, 'w' )
-- Header. -- Header.
local text="Function,Total Calls,Calls per Sec,Total Time,Total in %,Sec per Call,Source File;Line Number," local text = "Function,Total Calls,Calls per Sec,Total Time,Total in %,Sec per Call,Source File;Line Number,"
g:write(text.."\r\n") g:write( text .. "\r\n" )
-- Loop over data. -- Loop over data.
for i=1, #data do for i = 1, #data do
local t=data[i] --#PROFILER.Data local t = data[i] -- #PROFILER.Data
-- Calls per second. -- Calls per second.
local cps=t.count/runTimeGame local cps = t.count / runTimeGame
-- Output -- Output
local txt=string.format("%s,%d,%.1f,%.3f,%.3f,%.3f,%s,%s,", t.func, t.count, cps, t.tm, t.tm/runTimeGame*100, t.tm/t.count, tostring(t.src), tostring(t.line)) local txt = string.format( "%s,%d,%.1f,%.3f,%.3f,%.3f,%s,%s,", t.func, t.count, cps, t.tm, t.tm / runTimeGame * 100, t.tm / t.count, tostring( t.src ), tostring( t.line ) )
g:write(txt.."\r\n") g:write( txt .. "\r\n" )
end end
-- Close file. -- Close file.
g:close() g:close()
end end
--- Write info to output file. --- Write info to output file.
-- @param #string ext Extension. -- @param #string ext Extension.
-- @return #string File name. -- @return #string File name.
function PROFILER.getfilename(ext) function PROFILER.getfilename( ext )
local dir=lfs.writedir()..[[Logs\]] local dir = lfs.writedir() .. [[Logs\]]
ext=ext or PROFILER.fileNameSuffix ext = ext or PROFILER.fileNameSuffix
local file=dir..PROFILER.fileNamePrefix.."."..ext local file = dir .. PROFILER.fileNamePrefix .. "." .. ext
if not UTILS.FileExists(file) then if not UTILS.FileExists( file ) then
return file return file
end end
for i=1,999 do
local file=string.format("%s%s-%03d.%s", dir,PROFILER.fileNamePrefix, i, ext)
if not UTILS.FileExists(file) then for i = 1, 999 do
local file = string.format( "%s%s-%03d.%s", dir, PROFILER.fileNamePrefix, i, ext )
if not UTILS.FileExists( file ) then
return file return file
end end
end end
end end
@@ -368,172 +362,172 @@ end
--- Write info to output file. --- Write info to output file.
-- @param #number runTimeGame Game time in seconds. -- @param #number runTimeGame Game time in seconds.
-- @param #number runTimeOS OS time in seconds. -- @param #number runTimeOS OS time in seconds.
function PROFILER.showInfo(runTimeGame, runTimeOS) function PROFILER.showInfo( runTimeGame, runTimeOS )
-- Output file. -- Output file.
local file=PROFILER.getfilename(PROFILER.fileNameSuffix) local file = PROFILER.getfilename( PROFILER.fileNameSuffix )
local f=io.open(file, 'w') local f = io.open( file, 'w' )
-- Gather data. -- Gather data.
local Ttot=0 local Ttot = 0
local Calls=0 local Calls = 0
local t={} local t = {}
local tcopy=nil --#PROFILER.Data local tcopy = nil -- #PROFILER.Data
local tserialize=nil --#PROFILER.Data local tserialize = nil -- #PROFILER.Data
local tforgen=nil --#PROFILER.Data local tforgen = nil -- #PROFILER.Data
local tpairs=nil --#PROFILER.Data local tpairs = nil -- #PROFILER.Data
for func, count in pairs( PROFILER.Counters ) do
for func, count in pairs(PROFILER.Counters) do
local s, src, line, tm = PROFILER.getData( func )
local s,src,line,tm=PROFILER.getData(func)
if PROFILER.logUnknown == true then
if PROFILER.logUnknown==true then if s == nil then
if s==nil then s="<Unknown>" end s = "<Unknown>"
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 end
-- Total function time.
Ttot=Ttot+tm
-- Total number of calls.
Calls=Calls+count
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 )
end
-- Total function time.
Ttot = Ttot + tm
-- Total number of calls.
Calls = Calls + count
end
end end
-- Add special cases. -- Add special cases.
if tcopy then if tcopy then
table.insert(t, tcopy) table.insert( t, tcopy )
end end
if tserialize then if tserialize then
table.insert(t, tserialize) table.insert( t, tserialize )
end end
if tforgen then if tforgen then
table.insert(t, tforgen) table.insert( t, tforgen )
end end
if tpairs then if tpairs then
table.insert(t, tpairs) table.insert( t, tpairs )
end end
env.info('############################ Profiler Stopped ############################') env.info( '############################ Profiler Stopped ############################' )
env.info(string.format("* Runtime Game : %s = %d sec", UTILS.SecondsToClock(runTimeGame, true), runTimeGame)) env.info( string.format( "* Runtime Game : %s = %d sec", UTILS.SecondsToClock( runTimeGame, true ), runTimeGame ) )
env.info(string.format("* Runtime Real : %s = %d sec", UTILS.SecondsToClock(runTimeOS, true), runTimeOS)) env.info( string.format( "* Runtime Real : %s = %d sec", UTILS.SecondsToClock( runTimeOS, true ), runTimeOS ) )
env.info(string.format("* Function time : %s = %.1f sec (%.1f percent of runtime game)", UTILS.SecondsToClock(Ttot, true), Ttot, Ttot/runTimeGame*100)) env.info( string.format( "* Function time : %s = %.1f sec (%.1f percent of runtime game)", UTILS.SecondsToClock( Ttot, true ), Ttot, Ttot / runTimeGame * 100 ) )
env.info(string.format("* Total functions : %d", #t)) env.info( string.format( "* Total functions : %d", #t ) )
env.info(string.format("* Total func calls : %d", Calls)) env.info( string.format( "* Total func calls : %d", Calls ) )
env.info(string.format("* Writing to file : \"%s\"", file)) env.info( string.format( "* Writing to file : \"%s\"", file ) )
env.info(string.format("* Writing to file : \"%s\"", PROFILER.getfilename("csv"))) env.info( string.format( "* Writing to file : \"%s\"", PROFILER.getfilename( "csv" ) ) )
env.info("##############################################################################") env.info( "##############################################################################" )
-- Sort by total time. -- Sort by total time.
table.sort(t, function(a,b) return a.tm>b.tm end) table.sort( t, function( a, b )
return a.tm > b.tm
end )
-- Write data. -- Write data.
PROFILER._flog(f,"") PROFILER._flog( f, "" )
PROFILER._flog(f,"************************************************************************************************************************") PROFILER._flog( f, "************************************************************************************************************************" )
PROFILER._flog(f,"************************************************************************************************************************") PROFILER._flog( f, "************************************************************************************************************************" )
PROFILER._flog(f,"************************************************************************************************************************") PROFILER._flog( f, "************************************************************************************************************************" )
PROFILER._flog(f,"") PROFILER._flog( f, "" )
PROFILER._flog(f,"-------------------------") PROFILER._flog( f, "-------------------------" )
PROFILER._flog(f,"---- Profiler Report ----") PROFILER._flog( f, "---- Profiler Report ----" )
PROFILER._flog(f,"-------------------------") PROFILER._flog( f, "-------------------------" )
PROFILER._flog(f,"") PROFILER._flog( f, "" )
PROFILER._flog(f,string.format("* Runtime Game : %s = %.1f sec", UTILS.SecondsToClock(runTimeGame, true), runTimeGame)) PROFILER._flog( f, string.format( "* Runtime Game : %s = %.1f sec", UTILS.SecondsToClock( runTimeGame, true ), runTimeGame ) )
PROFILER._flog(f,string.format("* Runtime Real : %s = %.1f sec", UTILS.SecondsToClock(runTimeOS, true), runTimeOS)) PROFILER._flog( f, string.format( "* Runtime Real : %s = %.1f sec", UTILS.SecondsToClock( runTimeOS, true ), runTimeOS ) )
PROFILER._flog(f,string.format("* Function time : %s = %.1f sec (%.1f %% of runtime game)", UTILS.SecondsToClock(Ttot, true), Ttot, Ttot/runTimeGame*100)) PROFILER._flog( f, string.format( "* Function time : %s = %.1f sec (%.1f %% of runtime game)", UTILS.SecondsToClock( Ttot, true ), Ttot, Ttot / runTimeGame * 100 ) )
PROFILER._flog(f,"") PROFILER._flog( f, "" )
PROFILER._flog(f,string.format("* Total functions = %d", #t)) PROFILER._flog( f, string.format( "* Total functions = %d", #t ) )
PROFILER._flog(f,string.format("* Total func calls = %d", Calls)) PROFILER._flog( f, string.format( "* Total func calls = %d", Calls ) )
PROFILER._flog(f,"") PROFILER._flog( f, "" )
PROFILER._flog(f,string.format("* Calls per second threshold = %.3f/sec", PROFILER.ThreshCPS)) PROFILER._flog( f, string.format( "* Calls per second threshold = %.3f/sec", PROFILER.ThreshCPS ) )
PROFILER._flog(f,string.format("* Total func time threshold = %.3f sec", PROFILER.ThreshTtot)) PROFILER._flog( f, string.format( "* Total func time threshold = %.3f sec", PROFILER.ThreshTtot ) )
PROFILER._flog(f,"") PROFILER._flog( f, "" )
PROFILER._flog(f,"************************************************************************************************************************") PROFILER._flog( f, "************************************************************************************************************************" )
PROFILER._flog(f,"") PROFILER._flog( f, "" )
PROFILER.showTable(t, f, runTimeGame) PROFILER.showTable( t, f, runTimeGame )
-- Sort by number of calls. -- Sort by number of calls.
table.sort(t, function(a,b) return a.tm/a.count>b.tm/b.count end) table.sort( t, function( a, b )
return a.tm / a.count > b.tm / b.count
end )
-- Detailed data. -- Detailed data.
PROFILER._flog(f,"") PROFILER._flog( f, "" )
PROFILER._flog(f,"************************************************************************************************************************") PROFILER._flog( f, "************************************************************************************************************************" )
PROFILER._flog(f,"") PROFILER._flog( f, "" )
PROFILER._flog(f,"--------------------------------------") PROFILER._flog( f, "--------------------------------------" )
PROFILER._flog(f,"---- Data Sorted by Time per Call ----") PROFILER._flog( f, "---- Data Sorted by Time per Call ----" )
PROFILER._flog(f,"--------------------------------------") PROFILER._flog( f, "--------------------------------------" )
PROFILER._flog(f,"") PROFILER._flog( f, "" )
PROFILER.showTable(t, f, runTimeGame) PROFILER.showTable( t, f, runTimeGame )
-- Sort by number of calls. -- Sort by number of calls.
table.sort(t, function(a,b) return a.count>b.count end) table.sort( t, function( a, b )
return a.count > b.count
end )
-- Detailed data. -- Detailed data.
PROFILER._flog(f,"") PROFILER._flog( f, "" )
PROFILER._flog(f,"************************************************************************************************************************") PROFILER._flog( f, "************************************************************************************************************************" )
PROFILER._flog(f,"") PROFILER._flog( f, "" )
PROFILER._flog(f,"------------------------------------") PROFILER._flog( f, "------------------------------------" )
PROFILER._flog(f,"---- Data Sorted by Total Calls ----") PROFILER._flog( f, "---- Data Sorted by Total Calls ----" )
PROFILER._flog(f,"------------------------------------") PROFILER._flog( f, "------------------------------------" )
PROFILER._flog(f,"") PROFILER._flog( f, "" )
PROFILER.showTable(t, f, runTimeGame) PROFILER.showTable( t, f, runTimeGame )
-- Closing. -- Closing.
PROFILER._flog(f,"") PROFILER._flog( f, "" )
PROFILER._flog(f,"************************************************************************************************************************") PROFILER._flog( f, "************************************************************************************************************************" )
PROFILER._flog(f,"************************************************************************************************************************") PROFILER._flog( f, "************************************************************************************************************************" )
PROFILER._flog(f,"************************************************************************************************************************") PROFILER._flog( f, "************************************************************************************************************************" )
-- Close file. -- Close file.
f:close() f:close()
-- Print csv file.
PROFILER.printCSV(t, runTimeGame)
end
-- Print csv file.
PROFILER.printCSV( t, runTimeGame )
end

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,6 @@
--- **Utilities** DCS Simple Text-To-Speech (STTS). --- **Utilities** DCS Simple Text-To-Speech (STTS).
-- --
-- --
--
-- @module Utils.STTS -- @module Utils.STTS
-- @image MOOSE.JPG -- @image MOOSE.JPG
@@ -10,29 +9,29 @@
-- @field #string DIRECTORY Path of the SRS directory. -- @field #string DIRECTORY Path of the SRS directory.
--- Simple Text-To-Speech --- Simple Text-To-Speech
-- --
-- Version 0.4 - Compatible with SRS version 1.9.6.0+ -- Version 0.4 - Compatible with SRS version 1.9.6.0+
-- --
-- # DCS Modification Required -- # DCS Modification Required
-- --
-- You will need to edit MissionScripting.lua in DCS World/Scripts/MissionScripting.lua and remove the sanitisation. -- You will need to edit MissionScripting.lua in DCS World/Scripts/MissionScripting.lua and remove the sanitization.
-- To do this remove all the code below the comment - the line starts "local function sanitizeModule(name)" -- To do this remove all the code below the comment - the line starts "local function sanitizeModule(name)"
-- Do this without DCS running to allow mission scripts to use os functions. -- Do this without DCS running to allow mission scripts to use os functions.
-- --
-- *You WILL HAVE TO REAPPLY AFTER EVERY DCS UPDATE* -- *You WILL HAVE TO REAPPLY AFTER EVERY DCS UPDATE*
-- --
-- # USAGE: -- # USAGE:
-- --
-- Add this script into the mission as a DO SCRIPT or DO SCRIPT FROM FILE to initialise it -- Add this script into the mission as a DO SCRIPT or DO SCRIPT FROM FILE to initialize it
-- Make sure to edit the STTS.SRS_PORT and STTS.DIRECTORY to the correct values before adding to the mission. -- Make sure to edit the STTS.SRS_PORT and STTS.DIRECTORY to the correct values before adding to the mission.
-- Then its as simple as calling the correct function in LUA as a DO SCRIPT or in your own scripts. -- Then its as simple as calling the correct function in LUA as a DO SCRIPT or in your own scripts.
-- --
-- Example calls: -- Example calls:
-- --
-- STTS.TextToSpeech("Hello DCS WORLD","251","AM","1.0","SRS",2) -- STTS.TextToSpeech("Hello DCS WORLD","251","AM","1.0","SRS",2)
-- --
-- Arguments in order are: -- Arguments in order are:
-- --
-- * Message to say, make sure not to use a newline (\n) ! -- * Message to say, make sure not to use a newline (\n) !
-- * Frequency in MHz -- * Frequency in MHz
-- * Modulation - AM/FM -- * Modulation - AM/FM
@@ -43,44 +42,44 @@
-- * OPTIONAL - Speed -10 to +10 -- * OPTIONAL - Speed -10 to +10
-- * OPTIONAL - Gender male, female or neuter -- * OPTIONAL - Gender male, female or neuter
-- * OPTIONAL - Culture - en-US, en-GB etc -- * OPTIONAL - Culture - en-US, en-GB etc
-- * OPTIONAL - Voice - a specfic voice by name. Run DCS-SR-ExternalAudio.exe with --help to get the ones you can use on the command line -- * OPTIONAL - Voice - a specific voice by name. Run DCS-SR-ExternalAudio.exe with --help to get the ones you can use on the command line
-- * OPTIONAL - Google TTS - Switch to Google Text To Speech - Requires STTS.GOOGLE_CREDENTIALS path and Google project setup correctly -- * OPTIONAL - Google TTS - Switch to Google Text To Speech - Requires STTS.GOOGLE_CREDENTIALS path and Google project setup correctly
-- --
-- --
-- ## Example -- ## Example
-- --
-- This example will say the words "Hello DCS WORLD" on 251 MHz AM at maximum volume with a client called SRS and to the Blue coalition only -- This example will say the words "Hello DCS WORLD" on 251 MHz AM at maximum volume with a client called SRS and to the Blue coalition only
-- --
-- STTS.TextToSpeech("Hello DCS WORLD","251","AM","1.0","SRS",2,null,-5,"male","en-GB") -- STTS.TextToSpeech("Hello DCS WORLD","251","AM","1.0","SRS",2,null,-5,"male","en-GB")
-- --
-- ## Example -- ## Example
-- --
--This example will say the words "Hello DCS WORLD" on 251 MHz AM at maximum volume with a client called SRS and to the Blue coalition only centered on the position of the Unit called "A UNIT" -- This example will say the words "Hello DCS WORLD" on 251 MHz AM at maximum volume with a client called SRS and to the Blue coalition only centered on the position of the Unit called "A UNIT"
-- --
-- STTS.TextToSpeech("Hello DCS WORLD","251","AM","1.0","SRS",2,Unit.getByName("A UNIT"):getPoint(),-5,"male","en-GB") -- STTS.TextToSpeech("Hello DCS WORLD","251","AM","1.0","SRS",2,Unit.getByName("A UNIT"):getPoint(),-5,"male","en-GB")
-- --
-- Arguments in order are: -- Arguments in order are:
-- --
-- * FULL path to the MP3 OR OGG to play -- * FULL path to the MP3 OR OGG to play
-- * Frequency in MHz - to use multiple separate with a comma - Number of frequencies MUST match number of Modulations -- * Frequency in MHz - to use multiple separate with a comma - Number of frequencies MUST match number of Modulations
-- * Modulation - AM/FM - to use multiple -- * Modulation - AM/FM - to use multiple
-- * Volume - 1.0 max, 0.5 half -- * Volume - 1.0 max, 0.5 half
-- * Name of the transmitter - ATC, RockFM etc -- * Name of the transmitter - ATC, RockFM etc
-- * Coalition - 0 spectator, 1 red 2 blue -- * Coalition - 0 spectator, 1 red 2 blue
-- --
-- ## Example -- ## Example
-- --
-- This will play that MP3 on 255MHz AM & 31 FM at half volume with a client called "Multiple" and to Spectators only -- This will play that MP3 on 255MHz AM & 31 FM at half volume with a client called "Multiple" and to Spectators only
-- --
-- STTS.PlayMP3("C:\\Users\\Ciaran\\Downloads\\PR-Music.mp3","255,31","AM,FM","0.5","Multiple",0) -- STTS.PlayMP3("C:\\Users\\Ciaran\\Downloads\\PR-Music.mp3","255,31","AM,FM","0.5","Multiple",0)
-- --
-- @field #STTS -- @field #STTS
STTS={ STTS = {
ClassName="STTS", ClassName = "STTS",
DIRECTORY="", DIRECTORY = "",
SRS_PORT=5002, SRS_PORT = 5002,
GOOGLE_CREDENTIALS="C:\\Users\\Ciaran\\Downloads\\googletts.json", GOOGLE_CREDENTIALS = "C:\\Users\\Ciaran\\Downloads\\googletts.json",
EXECUTABLE="DCS-SR-ExternalAudio.exe", EXECUTABLE = "DCS-SR-ExternalAudio.exe"
} }
--- FULL Path to the FOLDER containing DCS-SR-ExternalAudio.exe - EDIT TO CORRECT FOLDER --- FULL Path to the FOLDER containing DCS-SR-ExternalAudio.exe - EDIT TO CORRECT FOLDER
@@ -92,142 +91,144 @@ STTS.SRS_PORT = 5002
--- Google credentials file --- Google credentials file
STTS.GOOGLE_CREDENTIALS = "C:\\Users\\Ciaran\\Downloads\\googletts.json" STTS.GOOGLE_CREDENTIALS = "C:\\Users\\Ciaran\\Downloads\\googletts.json"
--- DONT CHANGE THIS UNLESS YOU KNOW WHAT YOU'RE DOING --- DON'T CHANGE THIS UNLESS YOU KNOW WHAT YOU'RE DOING
STTS.EXECUTABLE = "DCS-SR-ExternalAudio.exe" STTS.EXECUTABLE = "DCS-SR-ExternalAudio.exe"
--- Function for UUID. --- Function for UUID.
function STTS.uuid() function STTS.uuid()
local random = math.random local random = math.random
local template ='yxxx-xxxxxxxxxxxx' local template = 'yxxx-xxxxxxxxxxxx'
return string.gsub(template, '[xy]', function (c) return string.gsub( template, '[xy]', function( c )
local v = (c == 'x') and random(0, 0xf) or random(8, 0xb) local v = (c == 'x') and random( 0, 0xf ) or random( 8, 0xb )
return string.format('%x', v) return string.format( '%x', v )
end) end )
end end
--- Round a number. --- Round a number.
-- @param #number x Number. -- @param #number x Number.
-- @param #number n Precision. -- @param #number n Precision.
function STTS.round(x, n) function STTS.round( x, n )
n = math.pow(10, n or 0) n = math.pow( 10, n or 0 )
x = x * n x = x * n
if x >= 0 then x = math.floor(x + 0.5) else x = math.ceil(x - 0.5) end if x >= 0 then
x = math.floor( x + 0.5 )
else
x = math.ceil( x - 0.5 )
end
return x / n return x / n
end end
--- Function returns estimated speech time in seconds. --- Function returns estimated speech time in seconds.
-- Assumptions for time calc: 100 Words per min, avarage of 5 letters for english word so -- Assumptions for time calc: 100 Words per min, average of 5 letters for english word so
-- --
-- * 5 chars * 100wpm = 500 characters per min = 8.3 chars per second -- * 5 chars * 100wpm = 500 characters per min = 8.3 chars per second
-- --
-- So lengh of msg / 8.3 = number of seconds needed to read it. rounded down to 8 chars per sec map function: -- So length of msg / 8.3 = number of seconds needed to read it. rounded down to 8 chars per sec map function:
-- --
-- * (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min -- * (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
-- --
function STTS.getSpeechTime(length,speed,isGoogle) function STTS.getSpeechTime( length, speed, isGoogle )
local maxRateRatio = 3 local maxRateRatio = 3
speed = speed or 1.0 speed = speed or 1.0
isGoogle = isGoogle or false isGoogle = isGoogle or false
local speedFactor = 1.0 local speedFactor = 1.0
if isGoogle then if isGoogle then
speedFactor = speed speedFactor = speed
else else
if speed ~= 0 then if speed ~= 0 then
speedFactor = math.abs(speed) * (maxRateRatio - 1) / 10 + 1 speedFactor = math.abs( speed ) * (maxRateRatio - 1) / 10 + 1
end end
if speed < 0 then if speed < 0 then
speedFactor = 1/speedFactor speedFactor = 1 / speedFactor
end end
end end
local wpm = math.ceil(100 * speedFactor) local wpm = math.ceil( 100 * speedFactor )
local cps = math.floor((wpm * 5)/60) local cps = math.floor( (wpm * 5) / 60 )
if type(length) == "string" then if type( length ) == "string" then
length = string.len(length) length = string.len( length )
end end
return math.ceil(length/cps) return math.ceil( length / cps )
end end
--- Text to speech function. --- Text to speech function.
function STTS.TextToSpeech(message, freqs, modulations, volume, name, coalition, point, speed, gender, culture, voice, googleTTS) function STTS.TextToSpeech( message, freqs, modulations, volume, name, coalition, point, speed, gender, culture, voice, googleTTS )
if os == nil or io == nil then if os == nil or io == nil then
env.info("[DCS-STTS] LUA modules os or io are sanitized. skipping. ") env.info( "[DCS-STTS] LUA modules os or io are sanitized. skipping. " )
return return
end end
speed = speed or 1 speed = speed or 1
gender = gender or "female" gender = gender or "female"
culture = culture or "" culture = culture or ""
voice = voice or "" voice = voice or ""
coalition=coalition or "0" coalition = coalition or "0"
name=name or "ROBOT" name = name or "ROBOT"
volume=1 volume = 1
speed=1 speed = 1
message = message:gsub( "\"", "\\\"" )
local cmd = string.format( "start /min \"\" /d \"%s\" /b \"%s\" -f %s -m %s -c %s -p %s -n \"%s\" -h", STTS.DIRECTORY, STTS.EXECUTABLE, freqs or "305", modulations or "AM", coalition, STTS.SRS_PORT, name )
message = message:gsub("\"","\\\"")
local cmd = string.format("start /min \"\" /d \"%s\" /b \"%s\" -f %s -m %s -c %s -p %s -n \"%s\" -h", STTS.DIRECTORY, STTS.EXECUTABLE, freqs or "305", modulations or "AM", coalition, STTS.SRS_PORT, name)
if voice ~= "" then if voice ~= "" then
cmd = cmd .. string.format(" -V \"%s\"",voice) cmd = cmd .. string.format( " -V \"%s\"", voice )
else else
if culture ~= "" then if culture ~= "" then
cmd = cmd .. string.format(" -l %s",culture) cmd = cmd .. string.format( " -l %s", culture )
end end
if gender ~= "" then if gender ~= "" then
cmd = cmd .. string.format(" -g %s",gender) cmd = cmd .. string.format( " -g %s", gender )
end end
end end
if googleTTS == true then if googleTTS == true then
cmd = cmd .. string.format(" -G \"%s\"",STTS.GOOGLE_CREDENTIALS) cmd = cmd .. string.format( " -G \"%s\"", STTS.GOOGLE_CREDENTIALS )
end end
if speed ~= 1 then if speed ~= 1 then
cmd = cmd .. string.format(" -s %s",speed) cmd = cmd .. string.format( " -s %s", speed )
end end
if volume ~= 1.0 then if volume ~= 1.0 then
cmd = cmd .. string.format(" -v %s",volume) cmd = cmd .. string.format( " -v %s", volume )
end end
if point and type(point) == "table" and point.x then if point and type( point ) == "table" and point.x then
local lat, lon, alt = coord.LOtoLL(point) local lat, lon, alt = coord.LOtoLL( point )
lat = STTS.round(lat,4) lat = STTS.round( lat, 4 )
lon = STTS.round(lon,4) lon = STTS.round( lon, 4 )
alt = math.floor(alt) alt = math.floor( alt )
cmd = cmd .. string.format(" -L %s -O %s -A %s",lat,lon,alt) cmd = cmd .. string.format( " -L %s -O %s -A %s", lat, lon, alt )
end end
cmd = cmd ..string.format(" -t \"%s\"",message) cmd = cmd .. string.format( " -t \"%s\"", message )
if string.len(cmd) > 255 then if string.len( cmd ) > 255 then
local filename = os.getenv('TMP') .. "\\DCS_STTS-" .. STTS.uuid() .. ".bat" local filename = os.getenv( 'TMP' ) .. "\\DCS_STTS-" .. STTS.uuid() .. ".bat"
local script = io.open(filename,"w+") local script = io.open( filename, "w+" )
script:write(cmd .. " && exit" ) script:write( cmd .. " && exit" )
script:close() script:close()
cmd = string.format("\"%s\"",filename) cmd = string.format( "\"%s\"", filename )
timer.scheduleFunction(os.remove, filename, timer.getTime() + 1) timer.scheduleFunction( os.remove, filename, timer.getTime() + 1 )
end end
if string.len(cmd) > 255 then if string.len( cmd ) > 255 then
env.info("[DCS-STTS] - cmd string too long") env.info( "[DCS-STTS] - cmd string too long" )
env.info("[DCS-STTS] TextToSpeech Command :\n" .. cmd.."\n") env.info( "[DCS-STTS] TextToSpeech Command :\n" .. cmd .. "\n" )
end end
os.execute(cmd) os.execute( cmd )
return STTS.getSpeechTime(message,speed,googleTTS) return STTS.getSpeechTime( message, speed, googleTTS )
end end
--- Play mp3 function. --- Play mp3 function.
@@ -235,22 +236,21 @@ end
-- @param #string freqs Frequencies, e.g. "305, 256". -- @param #string freqs Frequencies, e.g. "305, 256".
-- @param #string modulations Modulations, e.g. "AM, FM". -- @param #string modulations Modulations, e.g. "AM, FM".
-- @param #string volume Volume, e.g. "0.5". -- @param #string volume Volume, e.g. "0.5".
function STTS.PlayMP3(pathToMP3, freqs, modulations, volume, name, coalition, point) function STTS.PlayMP3( pathToMP3, freqs, modulations, volume, name, coalition, point )
local cmd = string.format("start \"\" /d \"%s\" /b /min \"%s\" -i \"%s\" -f %s -m %s -c %s -p %s -n \"%s\" -v %s -h", local cmd = string.format( "start \"\" /d \"%s\" /b /min \"%s\" -i \"%s\" -f %s -m %s -c %s -p %s -n \"%s\" -v %s -h", STTS.DIRECTORY, STTS.EXECUTABLE, pathToMP3, freqs or "305", modulations or "AM", coalition or "0", STTS.SRS_PORT, name or "ROBOT", volume or "1" )
STTS.DIRECTORY, STTS.EXECUTABLE, pathToMP3, freqs or "305", modulations or "AM", coalition or "0", STTS.SRS_PORT, name or "ROBOT", volume or "1")
if point and type(point) == "table" and point.x then
local lat, lon, alt = coord.LOtoLL(point)
lat = STTS.round(lat,4) if point and type( point ) == "table" and point.x then
lon = STTS.round(lon,4) local lat, lon, alt = coord.LOtoLL( point )
alt = math.floor(alt)
cmd = cmd .. string.format(" -L %s -O %s -A %s",lat,lon,alt) lat = STTS.round( lat, 4 )
end lon = STTS.round( lon, 4 )
alt = math.floor( alt )
env.info("[DCS-STTS] MP3/OGG Command :\n" .. cmd.."\n") cmd = cmd .. string.format( " -L %s -O %s -A %s", lat, lon, alt )
os.execute(cmd) end
end env.info( "[DCS-STTS] MP3/OGG Command :\n" .. cmd .. "\n" )
os.execute( cmd )
end

File diff suppressed because it is too large Load Diff

View File

@@ -310,8 +310,7 @@ end
--- Returns the @{DCS#Position3} position vectors indicating the point and direction vectors in 3D of the POSITIONABLE within the mission. --- Returns the @{DCS#Position3} position vectors indicating the point and direction vectors in 3D of the POSITIONABLE within the mission.
-- @param Wrapper.Positionable#POSITIONABLE self -- @param Wrapper.Positionable#POSITIONABLE self
-- @return DCS#Position The 3D position vectors of the POSITIONABLE. -- @return DCS#Position The 3D position vectors of the POSITIONABLE or #nil if the groups not existing or alive.
-- @return #nil The POSITIONABLE is not existing or alive.
function GROUP:GetPositionVec3() -- Overridden from POSITIONABLE:GetPositionVec3() function GROUP:GetPositionVec3() -- Overridden from POSITIONABLE:GetPositionVec3()
self:F2( self.PositionableName ) self:F2( self.PositionableName )
@@ -339,9 +338,7 @@ end
-- If the first @{Wrapper.Unit} of the group is inactive, it will return false. -- If the first @{Wrapper.Unit} of the group is inactive, it will return false.
-- --
-- @param #GROUP self -- @param #GROUP self
-- @return #boolean true if the group is alive and active. -- @return #boolean `true` if the group is alive *and* active, `false` if the group is alive but inactive or `#nil` if the group does not exist anymore.
-- @return #boolean false if the group is alive but inactive.
-- @return #nil if the group does not exist anymore.
function GROUP:IsAlive() function GROUP:IsAlive()
self:F2( self.GroupName ) self:F2( self.GroupName )
@@ -363,8 +360,7 @@ end
--- Returns if the group is activated. --- Returns if the group is activated.
-- @param #GROUP self -- @param #GROUP self
-- @return #boolean true if group is activated. -- @return #boolean `true` if group is activated or `#nil` The group is not existing or alive.
-- @return #nil The group is not existing or alive.
function GROUP:IsActive() function GROUP:IsActive()
self:F2( self.GroupName ) self:F2( self.GroupName )
@@ -412,7 +408,6 @@ function GROUP:Destroy( GenerateEvent, delay )
self:F2( self.GroupName ) self:F2( self.GroupName )
if delay and delay>0 then if delay and delay>0 then
--SCHEDULER:New(nil, GROUP.Destroy, {self, GenerateEvent}, delay)
self:ScheduleOnce(delay, GROUP.Destroy, self, GenerateEvent) self:ScheduleOnce(delay, GROUP.Destroy, self, GenerateEvent)
else else
@@ -568,12 +563,12 @@ function GROUP:GetSpeedMax()
local Units=self:GetUnits() local Units=self:GetUnits()
local speedmax=nil local speedmax=0
for _,unit in pairs(Units) do for _,unit in pairs(Units) do
local unit=unit --Wrapper.Unit#UNIT local unit=unit --Wrapper.Unit#UNIT
local speed=unit:GetSpeedMax() local speed=unit:GetSpeedMax()
if speedmax==nil then if speedmax==0 then
speedmax=speed speedmax=speed
elseif speed<speedmax then elseif speed<speedmax then
speedmax=speed speedmax=speed
@@ -2326,41 +2321,44 @@ function GROUP:GetAttribute()
local unarmedship=self:HasAttribute("Unarmed ships") local unarmedship=self:HasAttribute("Unarmed ships")
-- Define attribute. Order is important. -- Define attribute. Order of attack is important.
if transportplane then if fighter then
attribute=GROUP.Attribute.AIR_TRANSPORTPLANE
elseif awacs then
attribute=GROUP.Attribute.AIR_AWACS
elseif fighter then
attribute=GROUP.Attribute.AIR_FIGHTER attribute=GROUP.Attribute.AIR_FIGHTER
elseif bomber then elseif bomber then
attribute=GROUP.Attribute.AIR_BOMBER attribute=GROUP.Attribute.AIR_BOMBER
elseif awacs then
attribute=GROUP.Attribute.AIR_AWACS
elseif transportplane then
attribute=GROUP.Attribute.AIR_TRANSPORTPLANE
elseif tanker then elseif tanker then
attribute=GROUP.Attribute.AIR_TANKER attribute=GROUP.Attribute.AIR_TANKER
-- helos
elseif attackhelicopter then
attribute=GROUP.Attribute.AIR_ATTACKHELO
elseif transporthelo then elseif transporthelo then
attribute=GROUP.Attribute.AIR_TRANSPORTHELO attribute=GROUP.Attribute.AIR_TRANSPORTHELO
elseif attackhelicopter then
attribute=GROUP.Attribute.AIR_ATTACKHELO
elseif uav then elseif uav then
attribute=GROUP.Attribute.AIR_UAV attribute=GROUP.Attribute.AIR_UAV
elseif apc then -- ground - order of attack
attribute=GROUP.Attribute.GROUND_APC
elseif infantry then
attribute=GROUP.Attribute.GROUND_INFANTRY
elseif artillery then
attribute=GROUP.Attribute.GROUND_ARTILLERY
elseif tank then
attribute=GROUP.Attribute.GROUND_TANK
elseif aaa then
attribute=GROUP.Attribute.GROUND_AAA
elseif ewr then elseif ewr then
attribute=GROUP.Attribute.GROUND_EWR attribute=GROUP.Attribute.GROUND_EWR
elseif sam then elseif sam then
attribute=GROUP.Attribute.GROUND_SAM attribute=GROUP.Attribute.GROUND_SAM
elseif aaa then
attribute=GROUP.Attribute.GROUND_AAA
elseif artillery then
attribute=GROUP.Attribute.GROUND_ARTILLERY
elseif tank then
attribute=GROUP.Attribute.GROUND_TANK
elseif apc then
attribute=GROUP.Attribute.GROUND_APC
elseif infantry then
attribute=GROUP.Attribute.GROUND_INFANTRY
elseif truck then elseif truck then
attribute=GROUP.Attribute.GROUND_TRUCK attribute=GROUP.Attribute.GROUND_TRUCK
elseif train then elseif train then
attribute=GROUP.Attribute.GROUND_TRAIN attribute=GROUP.Attribute.GROUND_TRAIN
-- ships
elseif aircraftcarrier then elseif aircraftcarrier then
attribute=GROUP.Attribute.NAVAL_AIRCRAFTCARRIER attribute=GROUP.Attribute.NAVAL_AIRCRAFTCARRIER
elseif warship then elseif warship then
@@ -2578,8 +2576,10 @@ end
-- @return #GROUP self -- @return #GROUP self
function GROUP:SetCommandInvisible(switch) function GROUP:SetCommandInvisible(switch)
self:F2( self.GroupName ) self:F2( self.GroupName )
local switch = switch or false if switch==nil then
local SetInvisible = {id = 'SetInvisible', params = {value = true}} switch=false
end
local SetInvisible = {id = 'SetInvisible', params = {value = switch}}
self:SetCommand(SetInvisible) self:SetCommand(SetInvisible)
return self return self
end end
@@ -2590,9 +2590,11 @@ end
-- @return #GROUP self -- @return #GROUP self
function GROUP:SetCommandImmortal(switch) function GROUP:SetCommandImmortal(switch)
self:F2( self.GroupName ) self:F2( self.GroupName )
local switch = switch or false if switch==nil then
local SetInvisible = {id = 'SetImmortal', params = {value = true}} switch=false
self:SetCommand(SetInvisible) end
local SetImmortal = {id = 'SetImmortal', params = {value = switch}}
self:SetCommand(SetImmortal)
return self return self
end end

File diff suppressed because it is too large Load Diff

View File

@@ -1,52 +1,46 @@
--- **Wrapper** -- STATIC wraps the DCS StaticObject class. --- **Wrapper** -- STATIC wraps the DCS StaticObject class.
-- --
-- === -- ===
-- --
-- ### Author: **FlightControl** -- ### Author: **FlightControl**
-- --
-- ### Contributions: **funkyfranky** -- ### Contributions: **funkyfranky**
-- --
-- === -- ===
-- --
-- @module Wrapper.Static -- @module Wrapper.Static
-- @image Wrapper_Static.JPG -- @image Wrapper_Static.JPG
--- @type STATIC --- @type STATIC
-- @extends Wrapper.Positionable#POSITIONABLE -- @extends Wrapper.Positionable#POSITIONABLE
--- Wrapper class to handle Static objects. --- Wrapper class to handle Static objects.
-- --
-- Note that Statics are almost the same as Units, but they don't have a controller. -- Note that Statics are almost the same as Units, but they don't have a controller.
-- The @{Wrapper.Static#STATIC} class is a wrapper class to handle the DCS Static objects: -- The @{Wrapper.Static#STATIC} class is a wrapper class to handle the DCS Static objects:
-- --
-- * Wraps the DCS Static objects. -- * Wraps the DCS Static objects.
-- * Support all DCS Static APIs. -- * Support all DCS Static APIs.
-- * Enhance with Static specific APIs not in the DCS API set. -- * Enhance with Static specific APIs not in the DCS API set.
-- --
-- ## STATIC reference methods -- ## STATIC reference methods
-- --
-- For each DCS Static will have a STATIC wrapper object (instance) within the _@{DATABASE} object. -- 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). -- 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 -- The STATIC class does not contain a :New() method, rather it provides :Find() methods to retrieve the object reference
-- using the Static Name. -- using the Static Name.
-- --
-- Another thing to know is that STATIC objects do not "contain" the DCS Static object. -- 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. -- 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. -- 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: -- 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. -- * @{#STATIC.FindByName}(): Find a STATIC instance from the _DATABASE object using a DCS Static name.
-- --
-- IMPORTANT: ONE SHOULD NEVER SANATIZE these STATIC OBJECT REFERENCES! (make the STATIC object references nil). -- IMPORTANT: ONE SHOULD NEVER SANITIZE these STATIC OBJECT REFERENCES! (make the STATIC object references nil).
-- --
-- @field #STATIC -- @field #STATIC
STATIC = { STATIC = { ClassName = "STATIC" }
ClassName = "STATIC",
}
--- Register a static object. --- Register a static object.
-- @param #STATIC self -- @param #STATIC self
@@ -58,7 +52,6 @@ function STATIC:Register( StaticName )
return self return self
end end
--- Finds a STATIC from the _DATABASE using a DCSStatic object. --- Finds a STATIC from the _DATABASE using a DCSStatic object.
-- @param #STATIC self -- @param #STATIC self
-- @param DCS#StaticObject DCSStatic An existing DCS Static object reference. -- @param DCS#StaticObject DCSStatic An existing DCS Static object reference.
@@ -83,13 +76,13 @@ function STATIC:FindByName( StaticName, RaiseError )
-- Set static name. -- Set static name.
self.StaticName = StaticName self.StaticName = StaticName
if StaticFound then if StaticFound then
return StaticFound return StaticFound
end end
if RaiseError == nil or RaiseError == true then if RaiseError == nil or RaiseError == true then
error( "STATIC not found for: " .. StaticName ) error( "STATIC not found for: " .. StaticName )
end end
return nil return nil
@@ -97,38 +90,39 @@ end
--- Destroys the STATIC. --- Destroys the STATIC.
-- @param #STATIC self -- @param #STATIC self
-- @param #boolean GenerateEvent (Optional) true if you want to generate a crash or dead event for the static. -- @param #boolean GenerateEvent (Optional) true to generate a crash or dead event, false to not generate any event. `nil` (default) creates a remove event.
-- @return #nil The DCS StaticObject is not existing or alive. -- @return #nil The DCS StaticObject is not existing or alive.
--
-- @usage -- @usage
-- -- Air static example: destroy the static Helicopter and generate a S_EVENT_CRASH. -- -- Air static example: destroy the static Helicopter and generate a S_EVENT_CRASH.
-- Helicopter = STATIC:FindByName( "Helicopter" ) -- Helicopter = STATIC:FindByName( "Helicopter" )
-- Helicopter:Destroy( true ) -- Helicopter:Destroy( true )
-- --
-- @usage -- @usage
-- -- Ground static example: destroy the static Tank and generate a S_EVENT_DEAD. -- -- Ground static example: destroy the static Tank and generate a S_EVENT_DEAD.
-- Tanks = UNIT:FindByName( "Tank" ) -- Tanks = UNIT:FindByName( "Tank" )
-- Tanks:Destroy( true ) -- Tanks:Destroy( true )
-- --
-- @usage -- @usage
-- -- Ship static example: destroy the Ship silently. -- -- Ship static example: destroy the Ship silently.
-- Ship = STATIC:FindByName( "Ship" ) -- Ship = STATIC:FindByName( "Ship" )
-- Ship:Destroy() -- Ship:Destroy()
-- --
-- @usage -- @usage
-- -- Destroy without event generation example. -- -- Destroy without event generation example.
-- Ship = STATIC:FindByName( "Boat" ) -- Ship = STATIC:FindByName( "Boat" )
-- Ship:Destroy( false ) -- Don't generate an event upon destruction. -- Ship:Destroy( false ) -- Don't generate any event upon destruction.
-- --
function STATIC:Destroy( GenerateEvent ) function STATIC:Destroy( GenerateEvent )
self:F2( self.ObjectName ) self:F2( self.ObjectName )
local DCSObject = self:GetDCSObject() local DCSObject = self:GetDCSObject()
if DCSObject then if DCSObject then
local StaticName = DCSObject:getName() local StaticName = DCSObject:getName()
self:F( { StaticName = StaticName } ) self:F( { StaticName = StaticName } )
if GenerateEvent and GenerateEvent == true then if GenerateEvent and GenerateEvent == true then
if self:IsAir() then if self:IsAir() then
self:CreateEventCrash( timer.getTime(), DCSObject ) self:CreateEventCrash( timer.getTime(), DCSObject )
@@ -140,7 +134,7 @@ function STATIC:Destroy( GenerateEvent )
else else
self:CreateEventRemoveUnit( timer.getTime(), DCSObject ) self:CreateEventRemoveUnit( timer.getTime(), DCSObject )
end end
DCSObject:destroy() DCSObject:destroy()
return true return true
end end
@@ -148,17 +142,16 @@ function STATIC:Destroy( GenerateEvent )
return nil return nil
end end
--- Get DCS object of static of static. --- Get DCS object of static of static.
-- @param #STATIC self -- @param #STATIC self
-- @return DCS static object -- @return DCS static object
function STATIC:GetDCSObject() function STATIC:GetDCSObject()
local DCSStatic = StaticObject.getByName( self.StaticName ) local DCSStatic = StaticObject.getByName( self.StaticName )
if DCSStatic then if DCSStatic then
return DCSStatic return DCSStatic
end end
return nil return nil
end end
@@ -170,7 +163,7 @@ function STATIC:GetUnits()
local DCSStatic = self:GetDCSObject() local DCSStatic = self:GetDCSObject()
local Statics = {} local Statics = {}
if DCSStatic then if DCSStatic then
Statics[1] = STATIC:Find( DCSStatic ) Statics[1] = STATIC:Find( DCSStatic )
self:T3( Statics ) self:T3( Statics )
@@ -180,7 +173,6 @@ function STATIC:GetUnits()
return nil return nil
end end
--- Get threat level of static. --- Get threat level of static.
-- @param #STATIC self -- @param #STATIC self
-- @return #number Threat level 1. -- @return #number Threat level 1.
@@ -194,66 +186,62 @@ end
-- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static. -- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static.
-- @param #number Heading The heading of the static respawn in degrees. Default is 0 deg. -- @param #number Heading The heading of the static respawn in degrees. Default is 0 deg.
-- @param #number Delay Delay in seconds before the static is spawned. -- @param #number Delay Delay in seconds before the static is spawned.
function STATIC:SpawnAt(Coordinate, Heading, Delay) function STATIC:SpawnAt( Coordinate, Heading, Delay )
Heading=Heading or 0 Heading = Heading or 0
if Delay and Delay>0 then if Delay and Delay > 0 then
SCHEDULER:New(nil, self.SpawnAt, {self, Coordinate, Heading}, Delay) SCHEDULER:New( nil, self.SpawnAt, { self, Coordinate, Heading }, Delay )
else else
local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName) local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName )
SpawnStatic:SpawnFromPointVec2( Coordinate, Heading, self.StaticName ) SpawnStatic:SpawnFromPointVec2( Coordinate, Heading, self.StaticName )
end end
return self return self
end end
--- Respawn the @{Wrapper.Unit} at the same location with the same properties. --- Respawn the @{Wrapper.Unit} at the same location with the same properties.
-- This is useful to respawn a cargo after it has been destroyed. -- This is useful to respawn a cargo after it has been destroyed.
-- @param #STATIC self -- @param #STATIC self
-- @param DCS#country.id CountryID (Optional) The country ID used for spawning the new static. Default is same as currently. -- @param DCS#country.id CountryID (Optional) The country ID used for spawning the new static. Default is same as currently.
-- @param #number Delay (Optional) Delay in seconds before static is respawned. Default now. -- @param #number Delay (Optional) Delay in seconds before static is respawned. Default now.
function STATIC:ReSpawn(CountryID, Delay) function STATIC:ReSpawn( CountryID, Delay )
if Delay and Delay>0 then if Delay and Delay > 0 then
SCHEDULER:New(nil, self.ReSpawn, {self, CountryID}, Delay) SCHEDULER:New( nil, self.ReSpawn, { self, CountryID }, Delay )
else else
CountryID=CountryID or self:GetCountry() CountryID = CountryID or self:GetCountry()
local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName, CountryID )
SpawnStatic:Spawn( nil, self.StaticName )
local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName, CountryID)
SpawnStatic:Spawn(nil, self.StaticName)
end end
return self return self
end end
--- Respawn the @{Wrapper.Unit} at a defined Coordinate with an optional heading. --- Respawn the @{Wrapper.Unit} at a defined Coordinate with an optional heading.
-- @param #STATIC self -- @param #STATIC self
-- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static. -- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static.
-- @param #number Heading (Optional) The heading of the static respawn in degrees. Default the current heading. -- @param #number Heading (Optional) The heading of the static respawn in degrees. Default is the current heading.
-- @param #number Delay (Optional) Delay in seconds before static is respawned. Default now. -- @param #number Delay (Optional) Delay in seconds before static is respawned. Default is now.
function STATIC:ReSpawnAt(Coordinate, Heading, Delay) function STATIC:ReSpawnAt( Coordinate, Heading, Delay )
--Heading=Heading or 0 -- Heading=Heading or 0
if Delay and Delay>0 then if Delay and Delay > 0 then
SCHEDULER:New(nil, self.ReSpawnAt, {self, Coordinate, Heading}, Delay) SCHEDULER:New( nil, self.ReSpawnAt, { self, Coordinate, Heading }, Delay )
else else
local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName, self:GetCountry() )
local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName, self:GetCountry())
SpawnStatic:SpawnFromCoordinate( Coordinate, Heading, self.StaticName )
SpawnStatic:SpawnFromCoordinate(Coordinate, Heading, self.StaticName)
end end
return self return self
end end

View File

@@ -168,9 +168,6 @@ function UNIT:GetDCSObject()
return nil return nil
end end
--- Respawn the @{Wrapper.Unit} using a (tweaked) template of the parent Group. --- Respawn the @{Wrapper.Unit} using a (tweaked) template of the parent Group.
-- --
-- This function will: -- This function will:
@@ -263,8 +260,6 @@ function UNIT:ReSpawnAt( Coordinate, Heading )
_DATABASE:Spawn( SpawnGroupTemplate ) _DATABASE:Spawn( SpawnGroupTemplate )
end end
--- Returns if the unit is activated. --- Returns if the unit is activated.
-- @param #UNIT self -- @param #UNIT self
-- @return #boolean `true` if Unit is activated. `nil` The DCS Unit is not existing or alive. -- @return #boolean `true` if Unit is activated. `nil` The DCS Unit is not existing or alive.
@@ -301,8 +296,6 @@ function UNIT:IsAlive()
return nil return nil
end end
--- Returns the Unit's callsign - the localized string. --- Returns the Unit's callsign - the localized string.
-- @param #UNIT self -- @param #UNIT self
-- @return #string The Callsign of the Unit. -- @return #string The Callsign of the Unit.
@@ -442,7 +435,7 @@ function UNIT:GetSpeedMax()
return SpeedMax*3.6 return SpeedMax*3.6
end end
return nil return 0
end end
--- Returns the unit's max range in meters derived from the DCS descriptors. --- Returns the unit's max range in meters derived from the DCS descriptors.
@@ -626,8 +619,8 @@ function UNIT:GetAmmunition()
-- Type name of current weapon. -- Type name of current weapon.
local Tammo=ammotable[w]["desc"]["typeName"] local Tammo=ammotable[w]["desc"]["typeName"]
local _weaponString = UTILS.Split(Tammo,"%.") --local _weaponString = UTILS.Split(Tammo,"%.")
local _weaponName = _weaponString[#_weaponString] --local _weaponName = _weaponString[#_weaponString]
-- Get the weapon category: shell=0, missile=1, rocket=2, bomb=3 -- Get the weapon category: shell=0, missile=1, rocket=2, bomb=3
local Category=ammotable[w].desc.category local Category=ammotable[w].desc.category
@@ -655,8 +648,9 @@ function UNIT:GetAmmunition()
nbombs=nbombs+Nammo nbombs=nbombs+Nammo
elseif Category==Weapon.Category.MISSILE then elseif Category==Weapon.Category.MISSILE then
-- Add up all cruise missiles (category 5)
-- Add up all missiles (category 5)
if MissileCategory==Weapon.MissileCategory.AAM then if MissileCategory==Weapon.MissileCategory.AAM then
nmissiles=nmissiles+Nammo nmissiles=nmissiles+Nammo
elseif MissileCategory==Weapon.MissileCategory.ANTI_SHIP then elseif MissileCategory==Weapon.MissileCategory.ANTI_SHIP then
@@ -665,6 +659,10 @@ function UNIT:GetAmmunition()
nmissiles=nmissiles+Nammo nmissiles=nmissiles+Nammo
elseif MissileCategory==Weapon.MissileCategory.OTHER then elseif MissileCategory==Weapon.MissileCategory.OTHER then
nmissiles=nmissiles+Nammo nmissiles=nmissiles+Nammo
elseif MissileCategory==Weapon.MissileCategory.SAM then
nmissiles=nmissiles+Nammo
elseif MissileCategory==Weapon.MissileCategory.CRUISE then
nmissiles=nmissiles+Nammo
end end
end end
@@ -956,7 +954,6 @@ end
-- @return #string Some text. -- @return #string Some text.
function UNIT:GetThreatLevel() function UNIT:GetThreatLevel()
local ThreatLevel = 0 local ThreatLevel = 0
local ThreatText = "" local ThreatText = ""
@@ -982,7 +979,6 @@ function UNIT:GetThreatLevel()
"LR SAMs" "LR SAMs"
} }
if Attributes["LR SAM"] then ThreatLevel = 10 if Attributes["LR SAM"] then ThreatLevel = 10
elseif Attributes["MR SAM"] then ThreatLevel = 9 elseif Attributes["MR SAM"] then ThreatLevel = 9
elseif Attributes["SR SAM"] and elseif Attributes["SR SAM"] and
@@ -1018,7 +1014,6 @@ function UNIT:GetThreatLevel()
"Fighter" "Fighter"
} }
if Attributes["Fighters"] then ThreatLevel = 10 if Attributes["Fighters"] then ThreatLevel = 10
elseif Attributes["Multirole fighters"] then ThreatLevel = 9 elseif Attributes["Multirole fighters"] then ThreatLevel = 9
elseif Attributes["Battleplanes"] then ThreatLevel = 8 elseif Attributes["Battleplanes"] then ThreatLevel = 8
@@ -1136,12 +1131,6 @@ function UNIT:OtherUnitInRadius( AwaitUnit, Radius )
return nil return nil
end end
--- Returns if the unit is a friendly unit. --- Returns if the unit is a friendly unit.
-- @param #UNIT self -- @param #UNIT self
-- @return #boolean IsFriendly evaluation result. -- @return #boolean IsFriendly evaluation result.