From d14b7e8f4c486ddadb50ddcf8a3fec497b014723 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 2 Jan 2024 18:12:42 +0100 Subject: [PATCH 01/17] #POINT * added missing COORDINATE:ToStringLL() --- Moose Development/Moose/Core/Point.lua | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 89807b436..b69e7d294 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -181,7 +181,7 @@ do -- COORDINATE -- * @{#COORDINATE.ToStringBR}(): Generates a Bearing & Range text in the format of DDD for DI where DDD is degrees and DI is distance. -- * @{#COORDINATE.ToStringBRA}(): Generates a Bearing, Range & Altitude text. -- * @{#COORDINATE.ToStringBRAANATO}(): Generates a Generates a Bearing, Range, Aspect & Altitude text in NATOPS. - -- * @{#COORDINATE.ToStringLL}(): Generates a Latutide & Longitude text. + -- * @{#COORDINATE.ToStringLL}(): Generates a Latitude & Longitude text. -- * @{#COORDINATE.ToStringLLDMS}(): Generates a Lat, Lon, Degree, Minute, Second text. -- * @{#COORDINATE.ToStringLLDDM}(): Generates a Lat, Lon, Degree, decimal Minute text. -- * @{#COORDINATE.ToStringMGRS}(): Generates a MGRS grid coordinate text. @@ -3071,6 +3071,18 @@ do -- COORDINATE return coord.LOtoLL( self:GetVec3() ) end + --- Get Latitude & Longitude text. + -- @param #COORDINATE self + -- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object. + -- @return #string LLText + function COORDINATE:ToStringLL( Settings ) + + local LL_Accuracy = Settings and Settings.LL_Accuracy or _SETTINGS.LL_Accuracy + local lat, lon = coord.LOtoLL( self:GetVec3() ) + return string.format('%f', lat) .. ' ' .. string.format('%f', lon) + end + + --- Provides a Lat Lon string in Degree Minute Second format. -- @param #COORDINATE self -- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object. From 47f010cb286a79eb03ba5962c3a80c5ad7e6e94e Mon Sep 17 00:00:00 2001 From: kaltokri Date: Tue, 2 Jan 2024 19:03:19 +0100 Subject: [PATCH 02/17] Fixed broken link in AI_Escort.lua --- Moose Development/Moose/AI/AI_Escort.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/AI/AI_Escort.lua b/Moose Development/Moose/AI/AI_Escort.lua index 4eeba1fe3..a063cc31d 100644 --- a/Moose Development/Moose/AI/AI_Escort.lua +++ b/Moose Development/Moose/AI/AI_Escort.lua @@ -19,7 +19,7 @@ -- -- ## Missions: -- --- [ESC - Escorting](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/ESC%20-%20Escorting) +-- [ESC - Escorting](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AI/AI_Escort) -- -- === -- From d6a0fff99347fb254d56d284dba512668c4de67a Mon Sep 17 00:00:00 2001 From: kaltokri Date: Tue, 2 Jan 2024 20:03:18 +0100 Subject: [PATCH 03/17] Fix for broken links in documentation --- Moose Development/Moose/Functional/AICSAR.lua | 6 +++--- Moose Development/Moose/Functional/AmmoTruck.lua | 6 +++--- Moose Development/Moose/Functional/Autolase.lua | 8 ++++---- Moose Development/Moose/Ops/AirWing.lua | 8 ++++---- Moose Development/Moose/Ops/Awacs.lua | 6 +++--- Moose Development/Moose/Ops/Brigade.lua | 2 +- Moose Development/Moose/Ops/Chief.lua | 4 ++-- Moose Development/Moose/Ops/Fleet.lua | 2 +- Moose Development/Moose/Ops/FlightControl.lua | 10 +++++----- Moose Development/Moose/Ops/Operation.lua | 2 +- Moose Development/Moose/Ops/OpsTransport.lua | 2 +- 11 files changed, 28 insertions(+), 28 deletions(-) diff --git a/Moose Development/Moose/Functional/AICSAR.lua b/Moose Development/Moose/Functional/AICSAR.lua index 322959dea..f2bb13c3e 100644 --- a/Moose Development/Moose/Functional/AICSAR.lua +++ b/Moose Development/Moose/Functional/AICSAR.lua @@ -16,9 +16,9 @@ -- === -- -- ## Example Missions: --- --- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/CSR-001%20-%20Basics). --- +-- +-- Demo missions can be found on [GitHub](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Functional/AICSAR). +-- -- === -- -- ### Author: **Applevangelist** diff --git a/Moose Development/Moose/Functional/AmmoTruck.lua b/Moose Development/Moose/Functional/AmmoTruck.lua index 83b177bef..e59ab2872 100644 --- a/Moose Development/Moose/Functional/AmmoTruck.lua +++ b/Moose Development/Moose/Functional/AmmoTruck.lua @@ -8,7 +8,7 @@ -- -- ## Missions: -- --- ### [AmmoTruck](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/AMT%20-%20AmmoTruck/AmmoTruck%20100%20-%20NTTR%20-%20Basic) +-- Demo missions can be found on [GitHub](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Functional/AmmoTruck) -- -- === -- @@ -20,7 +20,7 @@ -- Last update: July 2023 ------------------------------------------------------------------------- ---- **AMMOTRUCK** class, extends Core.FSM#FSM +--- **AMMOTRUCK** class, extends Core.Fsm#FSM -- @type AMMOTRUCK -- @field #string ClassName Class Name -- @field #string lid Lid for log entries @@ -41,7 +41,7 @@ -- @field #number waitingtime Max waiting time in seconds -- @field #boolean routeonroad Route truck on road if true (default) -- @field #number reloads Number of reloads a single truck can do before he must return home --- @extends Core.FSM#FSM +-- @extends Core.Fsm#FSM --- *Amateurs talk about tactics, but professionals study logistics.* - General Robert H Barrow, USMC -- diff --git a/Moose Development/Moose/Functional/Autolase.lua b/Moose Development/Moose/Functional/Autolase.lua index 5bfbab806..e593086b3 100644 --- a/Moose Development/Moose/Functional/Autolase.lua +++ b/Moose Development/Moose/Functional/Autolase.lua @@ -5,11 +5,11 @@ -- **AUOTLASE** - Autolase targets in the field. -- -- === --- +-- -- ## Missions: -- --- ### [Autolase](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/) --- +-- None yet. +-- -- === -- -- **Main Features:** @@ -443,7 +443,7 @@ end -- @param #string Gender (Optional) Defaults to "male" -- @param #string Culture (Optional) Defaults to "en-US" -- @param #number Port (Optional) Defaults to 5002 --- @param #string Voice (Optional) Use a specifc voice with the @{Sound.SRS.SetVoice} function, e.g, `:SetVoice("Microsoft Hedda Desktop")`. +-- @param #string Voice (Optional) Use a specifc voice with the @{Sound.SRS#SetVoice} function, e.g, `:SetVoice("Microsoft Hedda Desktop")`. -- Note that this must be installed on your windows system. Can also be Google voice types, if you are using Google TTS. -- @param #number Volume (Optional) Volume - between 0.0 (silent) and 1.0 (loudest) -- @param #string PathToGoogleKey (Optional) Path to your google key if you want to use google TTS diff --git a/Moose Development/Moose/Ops/AirWing.lua b/Moose Development/Moose/Ops/AirWing.lua index 9d918e5d9..57daa15c6 100644 --- a/Moose Development/Moose/Ops/AirWing.lua +++ b/Moose Development/Moose/Ops/AirWing.lua @@ -10,7 +10,7 @@ -- -- ## Example Missions: -- --- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/OPS%20-%20Airwing). +-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Ops/Airwing). -- -- === -- @@ -1398,9 +1398,9 @@ function AIRWING:GetTankerForFlight(flightgroup) return nil end ---- Add the ability to call back an Ops.Awacs#AWACS object with an FSM call "FlightOnMission(FlightGroup, Mission)". +--- Add the ability to call back an Ops.AWACS#AWACS object with an FSM call "FlightOnMission(FlightGroup, Mission)". -- @param #AIRWING self --- @param Ops.Awacs#AWACS ConnectecdAwacs +-- @param Ops.AWACS#AWACS ConnectecdAwacs -- @return #AIRWING self function AIRWING:SetUsingOpsAwacs(ConnectecdAwacs) self:I(self.lid .. "Added AWACS Object: "..ConnectecdAwacs:GetName() or "unknown") @@ -1409,7 +1409,7 @@ function AIRWING:SetUsingOpsAwacs(ConnectecdAwacs) return self end ---- Remove the ability to call back an Ops.Awacs#AWACS object with an FSM call "FlightOnMission(FlightGroup, Mission)". +--- Remove the ability to call back an Ops.AWACS#AWACS object with an FSM call "FlightOnMission(FlightGroup, Mission)". -- @param #AIRWING self -- @return #AIRWING self function AIRWING:RemoveUsingOpsAwacs() diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index ed435baf9..ab6108775 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -7,9 +7,9 @@ -- === -- -- ## Example Missions: --- --- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/OPS%20-%20Awacs/). --- +-- +-- Demo missions can be found on [GitHub](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Ops/Awacs/). +-- -- ## Videos: -- -- Demo videos can be found on [Youtube](https://www.youtube.com/watch?v=ocdy8QzTNN4&list=PLFxp425SeXnq-oS0DSjam1HtddywH8i_k) diff --git a/Moose Development/Moose/Ops/Brigade.lua b/Moose Development/Moose/Ops/Brigade.lua index 73ae5e043..3242dc389 100644 --- a/Moose Development/Moose/Ops/Brigade.lua +++ b/Moose Development/Moose/Ops/Brigade.lua @@ -10,7 +10,7 @@ -- -- ## Example Missions: -- --- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/OPS%20-%20Brigade). +-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Ops/Brigade). -- -- === -- diff --git a/Moose Development/Moose/Ops/Chief.lua b/Moose Development/Moose/Ops/Chief.lua index 77477e0cc..ada2b1589 100644 --- a/Moose Development/Moose/Ops/Chief.lua +++ b/Moose Development/Moose/Ops/Chief.lua @@ -126,7 +126,7 @@ -- When the chief detects a valid target, he will launch a certain number of selected assets. Only whole groups from SQUADRONs, PLATOONs or FLOTILLAs can be selected. -- In other words, it is not possible to specify the abount of individual *units*. -- --- By default, one group is selected for any detected target. This can, however, be customized with the @{CHIEF.SetResponseOnTarget}() function. The number of min and max +-- By default, one group is selected for any detected target. This can, however, be customized with the @{#CHIEF.SetResponseOnTarget}() function. The number of min and max -- asset groups can be specified depending on threatlevel, category, mission type, number of units, defcon and strategy. -- -- For example: @@ -311,7 +311,7 @@ CHIEF.Strategy = { --- Resource list. -- @type CHIEF.Resources --- @field <#CHIEF.Resource> List of resources. +-- @field #CHIEF.Resource List of resources. --- Resource. -- @type CHIEF.Resource diff --git a/Moose Development/Moose/Ops/Fleet.lua b/Moose Development/Moose/Ops/Fleet.lua index 24bffb79d..050d06c9c 100644 --- a/Moose Development/Moose/Ops/Fleet.lua +++ b/Moose Development/Moose/Ops/Fleet.lua @@ -9,7 +9,7 @@ -- -- ## Example Missions: -- --- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/OPS%20-%20Fleet). +-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Ops/Fleet). -- -- === -- diff --git a/Moose Development/Moose/Ops/FlightControl.lua b/Moose Development/Moose/Ops/FlightControl.lua index 112834e53..651c6dc27 100644 --- a/Moose Development/Moose/Ops/FlightControl.lua +++ b/Moose Development/Moose/Ops/FlightControl.lua @@ -12,7 +12,7 @@ -- -- ## Example Missions: -- --- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/OPS%20-%20FlightControl). +-- Demo missions: None -- -- === -- @@ -207,9 +207,9 @@ -- landing is `21L`. -- -- By default, the runways for landing and takeoff are determined from the wind direction as described above. For cases where this gives wrong results, you can set the active runways manually. This is --- done via @{Wrappper.Airbase#AIRBASE} class. +-- done via @{Wrapper.Airbase#AIRBASE} class. -- --- More specifically, you can use the @{Wrappper.Airbase#AIRBASE.SetActiveRunwayLanding} function to set the landing runway and the @{Wrappper.Airbase#AIRBASE.SetActiveRunwayTakeoff} function to set +-- More specifically, you can use the @{Wrapper.Airbase#AIRBASE.SetActiveRunwayLanding} function to set the landing runway and the @{Wrapper.Airbase#AIRBASE.SetActiveRunwayTakeoff} function to set -- the runway for takeoff. -- -- ## Example for Nellis AFB @@ -223,7 +223,7 @@ -- -- # DCS ATC -- --- You can disable the DCS ATC with the @{Wrappper.Airbase#AIRBASE.SetRadioSilentMode}(*true*). This does not remove the DCS ATC airbase from the F10 menu but makes the ATC unresponsive. +-- You can disable the DCS ATC with the @{Wrapper.Airbase#AIRBASE.SetRadioSilentMode}(*true*). This does not remove the DCS ATC airbase from the F10 menu but makes the ATC unresponsive. -- -- -- # Examples @@ -876,7 +876,7 @@ end --- Set ATIS. -- @param #FLIGHTCONTROL self --- @param Ops.Atis#ATIS Atis ATIS. +-- @param Ops.ATIS#ATIS ATIS ATIS. -- @return #FLIGHTCONTROL self function FLIGHTCONTROL:SetATIS(Atis) self.atis=Atis diff --git a/Moose Development/Moose/Ops/Operation.lua b/Moose Development/Moose/Ops/Operation.lua index c6598acc2..34834439a 100644 --- a/Moose Development/Moose/Ops/Operation.lua +++ b/Moose Development/Moose/Ops/Operation.lua @@ -11,7 +11,7 @@ -- -- ## Example Missions: -- --- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/OPS%20-%20Operation). +-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Ops/Operation). -- -- === -- diff --git a/Moose Development/Moose/Ops/OpsTransport.lua b/Moose Development/Moose/Ops/OpsTransport.lua index 885c0340b..2db528f02 100644 --- a/Moose Development/Moose/Ops/OpsTransport.lua +++ b/Moose Development/Moose/Ops/OpsTransport.lua @@ -12,7 +12,7 @@ -- -- ## Example Missions: -- --- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/OPS%20-%20Transport). +-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Ops/Transport). -- -- === -- From 79b1f1615f8d04288fffe5e53ec741110109c242 Mon Sep 17 00:00:00 2001 From: kaltokri Date: Tue, 2 Jan 2024 22:06:59 +0100 Subject: [PATCH 04/17] Fix broken link in AI_Escort_Request.lua --- Moose Development/Moose/AI/AI_Escort_Request.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/AI/AI_Escort_Request.lua b/Moose Development/Moose/AI/AI_Escort_Request.lua index c5d1782e0..08bee2f64 100644 --- a/Moose Development/Moose/AI/AI_Escort_Request.lua +++ b/Moose Development/Moose/AI/AI_Escort_Request.lua @@ -19,7 +19,7 @@ -- -- ## Missions: -- --- [ESC - Escorting](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/ESC%20-%20Escorting) +-- [ESC - Escorting](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AI/AI_Escort) -- -- === -- From 761d83a68f92c7e4871e9e1827dc9a4426f7d2e4 Mon Sep 17 00:00:00 2001 From: kaltokri Date: Tue, 2 Jan 2024 22:10:08 +0100 Subject: [PATCH 05/17] Fix broken link in Awacs.lua --- Moose Development/Moose/Ops/Awacs.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index ab6108775..de6668415 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -2075,7 +2075,7 @@ end -- @param #string Gender Defaults to "male" -- @param #string Culture Defaults to "en-US" -- @param #number Port Defaults to 5002 --- @param #string Voice (Optional) Use a specifc voice with the @{Sound.SRS.SetVoice} function, e.g, `:SetVoice("Microsoft Hedda Desktop")`. +-- @param #string Voice (Optional) Use a specifc voice with the @{Sound.SRS#SetVoice} function, e.g, `:SetVoice("Microsoft Hedda Desktop")`. -- Note that this must be installed on your windows system. Can also be Google voice types, if you are using Google TTS. -- @param #number Volume Volume - between 0.0 (silent) and 1.0 (loudest) -- @param #string PathToGoogleKey Path to your google key if you want to use google TTS From 4c81333a0a3716cb9058f56b3bf1ec7388ccc3ba Mon Sep 17 00:00:00 2001 From: Rolf Geuenich Date: Wed, 3 Jan 2024 07:19:18 +0100 Subject: [PATCH 06/17] Add the dynamic loading of developer files (#2090) --- .../Moose Templates/Moose_Static_Loader.lua | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/Moose Setup/Moose Templates/Moose_Static_Loader.lua b/Moose Setup/Moose Templates/Moose_Static_Loader.lua index eedc31db7..90098d916 100644 --- a/Moose Setup/Moose Templates/Moose_Static_Loader.lua +++ b/Moose Setup/Moose Templates/Moose_Static_Loader.lua @@ -1,2 +1,43 @@ + +-- Automatic dynamic loading of development files, if they exists. +-- Try to load Moose as individual script files from Date: Wed, 3 Jan 2024 07:53:41 +0100 Subject: [PATCH 07/17] Fixed broken links --- Moose Development/Moose/Core/Database.lua | 4 +-- Moose Development/Moose/Ops/ArmyGroup.lua | 6 ++-- Moose Development/Moose/Ops/Commander.lua | 32 ++++++++++---------- Moose Development/Moose/Ops/EasyGCICAP.lua | 2 +- Moose Development/Moose/Ops/FlightGroup.lua | 2 +- Moose Development/Moose/Ops/Intelligence.lua | 2 +- Moose Development/Moose/Ops/NavyGroup.lua | 6 ++-- Moose Development/Moose/Ops/OpsGroup.lua | 2 +- Moose Development/Moose/Ops/PlayerRecce.lua | 2 +- Moose Development/Moose/Ops/PlayerTask.lua | 6 ++-- 10 files changed, 32 insertions(+), 32 deletions(-) diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index ac290aa2f..009418497 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -1893,7 +1893,7 @@ end --- Add a flight control to the data base. -- @param #DATABASE self --- @param Ops.FlightControl#FLIGHTCONTROL flightcontrol +-- @param OPS.FlightControl#FLIGHTCONTROL flightcontrol function DATABASE:AddFlightControl(flightcontrol) self:F2( { flightcontrol } ) self.FLIGHTCONTROLS[flightcontrol.airbasename]=flightcontrol @@ -1902,7 +1902,7 @@ end --- Get a flight control object from the data base. -- @param #DATABASE self -- @param #string airbasename Name of the associated airbase. --- @return Ops.FlightControl#FLIGHTCONTROL The FLIGHTCONTROL object.s +-- @return OPS.FlightControl#FLIGHTCONTROL The FLIGHTCONTROL object.s function DATABASE:GetFlightControl(airbasename) return self.FLIGHTCONTROLS[airbasename] end diff --git a/Moose Development/Moose/Ops/ArmyGroup.lua b/Moose Development/Moose/Ops/ArmyGroup.lua index d6b54a757..cdd686524 100644 --- a/Moose Development/Moose/Ops/ArmyGroup.lua +++ b/Moose Development/Moose/Ops/ArmyGroup.lua @@ -15,9 +15,9 @@ -- === -- -- ## Example Missions: --- --- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/OPS%20-%20Armygroup). --- +-- +-- Demo missions can be found on [GitHub](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Ops/Armygroup). +-- -- === -- -- ### Author: **funkyfranky** diff --git a/Moose Development/Moose/Ops/Commander.lua b/Moose Development/Moose/Ops/Commander.lua index 4096555e4..046d5ddc0 100644 --- a/Moose Development/Moose/Ops/Commander.lua +++ b/Moose Development/Moose/Ops/Commander.lua @@ -420,7 +420,7 @@ end --- Add an AIRWING to the commander. -- @param #COMMANDER self --- @param Ops.AirWing#AIRWING Airwing The airwing to add. +-- @param Ops.Airwing#AIRWING Airwing The airwing to add. -- @return #COMMANDER self function COMMANDER:AddAirwing(Airwing) @@ -667,10 +667,10 @@ end -- @param #number Speed Orbit speed in KIAS. Default 350 kts. -- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West). -- @param #number Leg Length of race-track in NM. Default 30 NM. --- @return Ops.AirWing#AIRWING.PatrolZone The CAP zone data. +-- @return Ops.Airwing#AIRWING.PatrolZone The CAP zone data. function COMMANDER:AddCapZone(Zone, Altitude, Speed, Heading, Leg) - local patrolzone={} --Ops.AirWing#AIRWING.PatrolZone + local patrolzone={} --Ops.Airwing#AIRWING.PatrolZone patrolzone.zone=Zone patrolzone.altitude=Altitude or 12000 @@ -692,10 +692,10 @@ end -- @param #number Speed Orbit speed in KIAS. Default 350 kts. -- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West). -- @param #number Leg Length of race-track in NM. Default 30 NM. --- @return Ops.AirWing#AIRWING.PatrolZone The CAP zone data. +-- @return Ops.Airwing#AIRWING.PatrolZone The CAP zone data. function COMMANDER:AddGciCapZone(Zone, Altitude, Speed, Heading, Leg) - local patrolzone={} --Ops.AirWing#AIRWING.PatrolZone + local patrolzone={} --Ops.Airwing#AIRWING.PatrolZone patrolzone.zone=Zone patrolzone.altitude=Altitude or 12000 @@ -715,7 +715,7 @@ end -- @param Core.Zone#ZONE Zone Zone, where the flight orbits. function COMMANDER:RemoveGciCapZone(Zone) - local patrolzone={} --Ops.AirWing#AIRWING.PatrolZone + local patrolzone={} --Ops.Airwing#AIRWING.PatrolZone patrolzone.zone=Zone for i,_patrolzone in pairs(self.gcicapZones) do @@ -737,10 +737,10 @@ end -- @param #number Speed Orbit speed in KIAS. Default 350 kts. -- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West). -- @param #number Leg Length of race-track in NM. Default 30 NM. --- @return Ops.AirWing#AIRWING.PatrolZone The AWACS zone data. +-- @return Ops.Airwing#AIRWING.PatrolZone The AWACS zone data. function COMMANDER:AddAwacsZone(Zone, Altitude, Speed, Heading, Leg) - local awacszone={} --Ops.AirWing#AIRWING.PatrolZone + local awacszone={} --Ops.Airwing#AIRWING.PatrolZone awacszone.zone=Zone awacszone.altitude=Altitude or 12000 @@ -760,7 +760,7 @@ end -- @param Core.Zone#ZONE Zone Zone, where the flight orbits. function COMMANDER:RemoveAwacsZone(Zone) - local awacszone={} --Ops.AirWing#AIRWING.PatrolZone + local awacszone={} --Ops.Airwing#AIRWING.PatrolZone awacszone.zone=Zone for i,_awacszone in pairs(self.awacsZones) do @@ -783,10 +783,10 @@ end -- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West). -- @param #number Leg Length of race-track in NM. Default 30 NM. -- @param #number RefuelSystem Refuelling system. --- @return Ops.AirWing#AIRWING.TankerZone The tanker zone data. +-- @return Ops.Airwing#AIRWING.TankerZone The tanker zone data. function COMMANDER:AddTankerZone(Zone, Altitude, Speed, Heading, Leg, RefuelSystem) - local tankerzone={} --Ops.AirWing#AIRWING.TankerZone + local tankerzone={} --Ops.Airwing#AIRWING.TankerZone tankerzone.zone=Zone tankerzone.altitude=Altitude or 12000 @@ -807,7 +807,7 @@ end -- @param Core.Zone#ZONE Zone Zone, where the flight orbits. function COMMANDER:RemoveTankerZone(Zone) - local tankerzone={} --Ops.AirWing#AIRWING.PatrolZone + local tankerzone={} --Ops.Airwing#AIRWING.PatrolZone tankerzone.zone=Zone for i,_tankerzone in pairs(self.tankerZones) do @@ -997,7 +997,7 @@ function COMMANDER:onafterStatus(From, Event, To) -- Check CAP zones. for _,_patrolzone in pairs(self.capZones) do - local patrolzone=_patrolzone --Ops.AirWing#AIRWING.PatrolZone + local patrolzone=_patrolzone --Ops.Airwing#AIRWING.PatrolZone -- Check if mission is nil or over. if (not patrolzone.mission) or patrolzone.mission:IsOver() then local Coordinate=patrolzone.zone:GetCoordinate() @@ -1008,7 +1008,7 @@ function COMMANDER:onafterStatus(From, Event, To) -- Check GCICAP zones. for _,_patrolzone in pairs(self.gcicapZones) do - local patrolzone=_patrolzone --Ops.AirWing#AIRWING.PatrolZone + local patrolzone=_patrolzone --Ops.Airwing#AIRWING.PatrolZone -- Check if mission is nil or over. if (not patrolzone.mission) or patrolzone.mission:IsOver() then local Coordinate=patrolzone.zone:GetCoordinate() @@ -1019,7 +1019,7 @@ function COMMANDER:onafterStatus(From, Event, To) -- Check AWACS zones. for _,_awacszone in pairs(self.awacsZones) do - local awacszone=_awacszone --Ops.AirWing#AIRWING.Patrol + local awacszone=_awacszone --Ops.Airwing#AIRWING.Patrol -- Check if mission is nil or over. if (not awacszone.mission) or awacszone.mission:IsOver() then local Coordinate=awacszone.zone:GetCoordinate() @@ -1030,7 +1030,7 @@ function COMMANDER:onafterStatus(From, Event, To) -- Check Tanker zones. for _,_tankerzone in pairs(self.tankerZones) do - local tankerzone=_tankerzone --Ops.AirWing#AIRWING.TankerZone + local tankerzone=_tankerzone --Ops.Airwing#AIRWING.TankerZone -- Check if mission is nil or over. if (not tankerzone.mission) or tankerzone.mission:IsOver() then local Coordinate=tankerzone.zone:GetCoordinate() diff --git a/Moose Development/Moose/Ops/EasyGCICAP.lua b/Moose Development/Moose/Ops/EasyGCICAP.lua index 34ec1301b..ee1dc3138 100644 --- a/Moose Development/Moose/Ops/EasyGCICAP.lua +++ b/Moose Development/Moose/Ops/EasyGCICAP.lua @@ -41,7 +41,7 @@ -- @field #number coalition -- @field #string alias -- @field #table wings --- @field Ops.Intelligence#INTEL Intel +-- @field Ops.Intel#INTEL Intel -- @field #number resurrection -- @field #number capspeed -- @field #number capalt diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index 25a6b8ef2..ba55abbfb 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -18,7 +18,7 @@ -- -- ## Example Missions: -- --- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/OPS%20-%20Flightgroup). +-- Demo missions can be found on [GitHub](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Ops/Flightgroup). -- -- === -- diff --git a/Moose Development/Moose/Ops/Intelligence.lua b/Moose Development/Moose/Ops/Intelligence.lua index 6e900790f..4e71d65b3 100644 --- a/Moose Development/Moose/Ops/Intelligence.lua +++ b/Moose Development/Moose/Ops/Intelligence.lua @@ -1106,7 +1106,7 @@ function INTEL:CreateDetectedItems(DetectedGroups, DetectedStatics, RecceDetecti return self end ---- (Internal) Return the detected target groups of the controllable as a @{SET_GROUP}. +--- (Internal) Return the detected target groups of the controllable as a @{Core.Set#SET_GROUP}. -- The optional parameters specify the detection methods that can be applied. -- If no detection method is given, the detection will use all the available methods by default. -- @param #INTEL self diff --git a/Moose Development/Moose/Ops/NavyGroup.lua b/Moose Development/Moose/Ops/NavyGroup.lua index 0b7823551..929225ee6 100644 --- a/Moose Development/Moose/Ops/NavyGroup.lua +++ b/Moose Development/Moose/Ops/NavyGroup.lua @@ -20,9 +20,9 @@ -- === -- -- ## Example Missions: --- --- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/OPS%20-%20Navygroup) --- +-- +-- Demo missions can be found on [GitHub](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Ops/Navygroup) +-- -- === -- -- ### Author: **funkyfranky** diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index d7cbe4ab6..1566e6634 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -100,7 +100,7 @@ -- -- @field #OPSGROUP.Radio radio Current radio settings. -- @field #OPSGROUP.Radio radioDefault Default radio settings. --- @field Core.RadioQueue#RADIOQUEUE radioQueue Radio queue. +-- @field Sound.Radio#RADIOQUEUE radioQueue Radio queue. -- -- @field #OPSGROUP.Beacon tacan Current TACAN settings. -- @field #OPSGROUP.Beacon tacanDefault Default TACAN settings. diff --git a/Moose Development/Moose/Ops/PlayerRecce.lua b/Moose Development/Moose/Ops/PlayerRecce.lua index 9c1cb9eb6..2561fa837 100644 --- a/Moose Development/Moose/Ops/PlayerRecce.lua +++ b/Moose Development/Moose/Ops/PlayerRecce.lua @@ -1488,7 +1488,7 @@ end -- @param #string Gender (Optional) Defaults to "male" -- @param #string Culture (Optional) Defaults to "en-US" -- @param #number Port (Optional) Defaults to 5002 --- @param #string Voice (Optional) Use a specifc voice with the @{Sound.SRS.SetVoice} function, e.g, `:SetVoice("Microsoft Hedda Desktop")`. +-- @param #string Voice (Optional) Use a specifc voice with the @{Sound.SRS#SetVoice} function, e.g, `:SetVoice("Microsoft Hedda Desktop")`. -- Note that this must be installed on your windows system. Can also be Google voice types, if you are using Google TTS. -- @param #number Volume (Optional) Volume - between 0.0 (silent) and 1.0 (loudest) -- @param #string PathToGoogleKey (Optional) Path to your google key if you want to use google TTS diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 28110a7ec..650d5a0d6 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -11,7 +11,7 @@ -- -- ## Example Missions: -- --- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/OPS%20-%20PlayerTask). +-- Demo missions can be found on [GitHub](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Ops/PlayerTask). -- -- === -- @@ -974,7 +974,7 @@ do -- @field #string locale -- @field #boolean precisionbombing -- @field Ops.FlightGroup#FLIGHTGROUP LasingDrone --- @field Core.MarkerOps_BASE#MARKEROPS_BASE MarkerOps +-- @field Core.MarkerOps_Base#MARKEROPS_BASE MarkerOps -- @field #boolean taskinfomenu -- @field #boolean MarkerReadOnly -- @field #table FlashPlayer List of player who switched Flashing Direction Info on @@ -1028,7 +1028,7 @@ do -- ## 1 Overview -- -- PLAYERTASKCONTROLLER is used to auto-create (optional) and control tasks for players. It can be set up as Air-to-Ground (A2G, main focus), Air-to-Ship (A2S) or Air-to-Air (A2A) controller. --- For the latter task type, also have a look at the @{Ops.Awacs#AWACS} class which allows for more complex scenarios. +-- For the latter task type, also have a look at the @{Ops.AWACS#AWACS} class which allows for more complex scenarios. -- One task at a time can be joined by the player from the F10 menu. A task can be joined by multiple players. Once joined, task information is available via the F10 menu, the task location -- can be marked on the map and for A2G/S targets, the target can be marked with smoke and flares. -- From 4e50bd213c0656b251907cd8b5f4c8fb359f5da1 Mon Sep 17 00:00:00 2001 From: kaltokri Date: Wed, 3 Jan 2024 09:04:43 +0100 Subject: [PATCH 08/17] No fixes for broken links --- .../Moose/Functional/Warehouse.lua | 2 +- Moose Development/Moose/Ops/Chief.lua | 18 +++++++++--------- Moose Development/Moose/Ops/PlayerTask.lua | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 7ac6e2839..86c1255c7 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -87,7 +87,7 @@ -- @field #number respawndelay Delay before respawn in seconds. -- @field #number runwaydestroyed Time stamp timer.getAbsTime() when the runway was destroyed. -- @field #number runwayrepairtime Time in seconds until runway will be repaired after it was destroyed. Default is 3600 sec (one hour). --- @field Ops.FlightControl#FLIGHTCONTROL flightcontrol Flight control of this warehouse. +-- @field OPS.FlightControl#FLIGHTCONTROL flightcontrol Flight control of this warehouse. -- @extends Core.Fsm#FSM --- Have your assets at the right place at the right time - or not! diff --git a/Moose Development/Moose/Ops/Chief.lua b/Moose Development/Moose/Ops/Chief.lua index ada2b1589..e1027924b 100644 --- a/Moose Development/Moose/Ops/Chief.lua +++ b/Moose Development/Moose/Ops/Chief.lua @@ -34,7 +34,7 @@ -- @field Ops.Commander#COMMANDER commander Commander of assigned legions. -- @field #number Nsuccess Number of successful missions. -- @field #number Nfailure Number of failed mission. --- @extends Ops.Intelligence#INTEL +-- @extends Ops.Intel#INTEL --- *In preparing for battle I have always found that plans are useless, but planning is indispensable* -- Dwight D Eisenhower -- @@ -1096,7 +1096,7 @@ end --- Add an AIRWING to the chief's commander. -- @param #CHIEF self --- @param Ops.AirWing#AIRWING Airwing The airwing to add. +-- @param Ops.Airwing#AIRWING Airwing The airwing to add. -- @return #CHIEF self function CHIEF:AddAirwing(Airwing) @@ -1460,7 +1460,7 @@ end -- @param #number Speed Orbit speed in KIAS. Default 350 kts. -- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West). -- @param #number Leg Length of race-track in NM. Default 30 NM. --- @return Ops.AirWing#AIRWING.PatrolZone The CAP zone data. +-- @return Ops.Airwing#AIRWING.PatrolZone The CAP zone data. function CHIEF:AddCapZone(Zone, Altitude, Speed, Heading, Leg) -- Hand over to commander. @@ -1476,7 +1476,7 @@ end -- @param #number Speed Orbit speed in KIAS. Default 350 kts. -- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West). -- @param #number Leg Length of race-track in NM. Default 30 NM. --- @return Ops.AirWing#AIRWING.PatrolZone The CAP zone data. +-- @return Ops.Airwing#AIRWING.PatrolZone The CAP zone data. function CHIEF:AddGciCapZone(Zone, Altitude, Speed, Heading, Leg) -- Hand over to commander. @@ -1503,7 +1503,7 @@ end -- @param #number Speed Orbit speed in KIAS. Default 350 kts. -- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West). -- @param #number Leg Length of race-track in NM. Default 30 NM. --- @return Ops.AirWing#AIRWING.PatrolZone The AWACS zone data. +-- @return Ops.Airwing#AIRWING.PatrolZone The AWACS zone data. function CHIEF:AddAwacsZone(Zone, Altitude, Speed, Heading, Leg) -- Hand over to commander. @@ -1531,7 +1531,7 @@ end -- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West). -- @param #number Leg Length of race-track in NM. Default 30 NM. -- @param #number RefuelSystem Refuelling system. --- @return Ops.AirWing#AIRWING.TankerZone The tanker zone data. +-- @return Ops.Airwing#AIRWING.TankerZone The tanker zone data. function CHIEF:AddTankerZone(Zone, Altitude, Speed, Heading, Leg, RefuelSystem) -- Hand over to commander. @@ -1785,7 +1785,7 @@ function CHIEF:onafterStatus(From, Event, To) -- Clean up missions where the contact was lost. for _,_contact in pairs(self.ContactsLost) do - local contact=_contact --Ops.Intelligence#INTEL.Contact + local contact=_contact --Ops.Intel#INTEL.Contact if contact.mission and contact.mission:IsNotOver() then @@ -1813,7 +1813,7 @@ function CHIEF:onafterStatus(From, Event, To) -- Create TARGETs for all new contacts. self.Nborder=0 ; self.Nconflict=0 ; self.Nattack=0 for _,_contact in pairs(self.Contacts) do - local contact=_contact --Ops.Intelligence#INTEL.Contact + local contact=_contact --Ops.Intel#INTEL.Contact local group=contact.group --Wrapper.Group#GROUP -- Check if contact inside of our borders. @@ -1964,7 +1964,7 @@ function CHIEF:onafterStatus(From, Event, To) if self.verbose>=2 and #self.Contacts>0 then local text="Contacts:" for i,_contact in pairs(self.Contacts) do - local contact=_contact --Ops.Intelligence#INTEL.Contact + local contact=_contact --Ops.Intel#INTEL.Contact local mtext="N/A" if contact.mission then diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 650d5a0d6..6e1e1079e 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -4001,7 +4001,7 @@ end -- @param #string Gender (Optional) Defaults to "male" -- @param #string Culture (Optional) Defaults to "en-US" -- @param #number Port (Optional) Defaults to 5002 --- @param #string Voice (Optional) Use a specifc voice with the @{Sound.SRS.SetVoice} function, e.g, `:SetVoice("Microsoft Hedda Desktop")`. +-- @param #string Voice (Optional) Use a specifc voice with the @{Sound.SRS#SetVoice} function, e.g, `:SetVoice("Microsoft Hedda Desktop")`. -- Note that this must be installed on your windows system. Can also be Google voice types, if you are using Google TTS. -- @param #number Volume (Optional) Volume - between 0.0 (silent) and 1.0 (loudest) -- @param #string PathToGoogleKey (Optional) Path to your google key if you want to use google TTS From db6dc7b77e0abfa6ba1a2cd7899e1709bb6dbf5f Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 3 Jan 2024 18:05:25 +0100 Subject: [PATCH 09/17] SET: Added GetRandomSurely() --- Moose Development/Moose/Core/Set.lua | 31 ++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index f4e5a0ed7..1ad9bdea6 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -455,7 +455,7 @@ do -- SET_BASE --- Gets a random object from the @{Core.Set#SET_BASE} and derived classes. -- @param #SET_BASE self - -- @return Core.Base#BASE + -- @return Core.Base#BASE or nil if none found or the SET is empty! function SET_BASE:GetRandom() local tablemax = 0 for _,_ind in pairs(self.Index) do @@ -466,6 +466,23 @@ do -- SET_BASE self:T3( { RandomItem } ) return RandomItem end + + --- Gets a random object from the @{Core.Set#SET_BASE} and derived classes. A bit slower than @{#SET_BASE.GetRandom}() but tries to ensure you get an object back if the SET is not empty. + -- @param #SET_BASE self + -- @return Core.Base#BASE or nil if the SET is empty! + function SET_BASE:GetRandomSurely() + local tablemax = 0 + local sorted = {} + for _,_obj in pairs(self.Set) do + tablemax = tablemax + 1 + sorted[tablemax] = _obj + end + --local tablemax = table.maxn(self.Index) + --local RandomItem = self.Set[self.Index[math.random(1,tablemax)]] + local RandomItem = sorted[math.random(1,tablemax)] + self:T3( { RandomItem } ) + return RandomItem + end --- Retrieves the amount of objects in the @{Core.Set#SET_BASE} and derived classes. -- @param #SET_BASE self @@ -8215,9 +8232,15 @@ do -- SET_SCENERY -- @param #SET_SCENERY self -- @return Core.Point#COORDINATE The center coordinate of all the objects in the set. function SET_SCENERY:GetCoordinate() - - local Coordinate = self:GetRandom():GetCoordinate() - + + local Coordinate = COORDINATE:New({0,0,0}) + + local Item = self:GetRandomSurely() + + if Item then + Coordinate:GetCoordinate() + end + local x1 = Coordinate.x local x2 = Coordinate.x local y1 = Coordinate.y From f57beab6d2f0735469ce2b2e95cdcdb9d911d0b6 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 3 Jan 2024 18:14:12 +0100 Subject: [PATCH 10/17] #STRATEGO --- .../Moose/Functional/Stratego.lua | 1174 +++++++++++++++++ Moose Development/Moose/Modules.lua | 1 + Moose Setup/Moose.files | 1 + 3 files changed, 1176 insertions(+) create mode 100644 Moose Development/Moose/Functional/Stratego.lua diff --git a/Moose Development/Moose/Functional/Stratego.lua b/Moose Development/Moose/Functional/Stratego.lua new file mode 100644 index 000000000..4f1cd8320 --- /dev/null +++ b/Moose Development/Moose/Functional/Stratego.lua @@ -0,0 +1,1174 @@ +--- **Functional** - Stratego. +-- +-- **Main Features:** +-- +-- * Helper class for mission designers to support classic capture-the-base scenarios. +-- * Creates a network of possible connections between bases (airbases, FARPs, Ships), Ports (defined as zones) and POIs (defined as zones). +-- * Assigns a strategic value to each of the resulting knots. +-- * Can create a list of targets for your next mission move, both strategic and consolidation targets. +-- * Can be used with budgets to limit the target selection. +-- * Highly configureable. +-- +-- === +-- +-- ### Author: **applevangelist** +-- +-- @module Functional.Stratego +-- @image Stratego.png + + +--- +--- **STRATEGO** class, extends Core.Base#BASE +-- @type STRATEGO +-- @field #string ClassName +-- @field #boolean debug +-- @field #string version +-- @field #number portweight +-- @field #number POIweight +-- @field #number maxrunways +-- @field #number coalition +-- @field #table colors +-- @field #table airbasetable +-- @field #table nonconnectedab +-- @field #table easynames +-- @field #number maxdist +-- @field #table disttable +-- @field #table routexists +-- @field #number routefactor +-- @field #table OpsZones +-- @field #number NeutralBenefit +-- @field #number Budget +-- @field #boolean usebudget +-- @field #number CaptureUnits +-- @field #number CaptureThreatlevel +-- @extends Core.Base#BASE + + +--- *If you see what is right and fail to act on it, you lack courage* --- Confucius +-- +-- === +-- +-- # The STRATEGO Concept +-- +-- STRATEGO is a helper class for mission designers. +-- The basic idea is to create a network of knots (bases) on the map, which each have a number of connections +-- to other knots. The base value of each knot is the number of runways of the base (the bigger the more important), or in the case of Ports and POIs, the assigned value points. +-- The strategic value of each base is determined by the number of routes going in and out of the knot, where connections between more strategic knots add a higher value to the +-- strategic value than connections to less valueable knots. +-- +-- ## Setup +-- +-- Setup is map indepent and works automatically. All airbases, FARPS, and ships on the map are considered. **Note:** Later spawned objects are not considered at the moment. +-- +-- -- Setup and start STRATGEO for the blue side, maximal knot distance is 100km +-- local Bluecher = STRATEGO:New("Bluecher",coalition.side.BLUE,100) +-- -- use budgets +-- Bluecher:SetUsingBudget(true,500) +-- -- draw on the map +-- Bluecher:SetDebug(true,true,true) +-- -- Start +-- Bluecher:Start() +-- +-- ### Helper +-- +-- @{#STRATEGO.SetWeights}(): Set weights for knots and routes to determine their importance. +-- +-- ### Hint +-- +-- Each knot is its own @{Ops.OpsZone#OPSZONE} object to manage the coalition alignment of that knot and how it can be conquered. +-- +-- ### Distance +-- +-- The knot distance factor determines how many connections are there on the map. The smaller the lighter is the resulting net. The higher the thicker it gets, with more strategic options. +-- Play around with the distance to get an optimal map for your scenario. +-- +-- One some maps, e.g. Syria, lower distance factors can create "islands" of unconnected network parts on the map. FARPs and POIs can bridge those gaps, or you can add routes manually. +-- +-- @{#STRATEGO.AddRoutesManually}(): Add a route manually. +-- +-- ## Ports and POIs +-- +-- Ports and POIs are @{Core.Zone#ZONE} objects on the map with specfic values. Zones with the keywords "Port" or "POI" in the name are automatically considered at setup time. +-- +-- ## Get next possible targets +-- +-- There are two types of possible target lists, strategic and consolidation. Targets closer to the start knot are chosen as possible targets. +-- +-- +-- * Strategic targets are of higher or equal base weight from a given start point. Can also be obtained for the whole net. +-- * Consoliation targets are of smaller or equal base weight from a given start point. Can also be obtained for the whole net. +-- +-- +-- @{#STRATEGO.UpdateKnotCoalitions}(): Update alls knot's coalition data before takign a decision. +-- @{#STRATEGO.FindStrategicTargets}(): Find a list of possible strategic targets in the network of the enemy or neutral coalition. +-- @{#STRATEGO.FindConsolidationTargets}(): Find a list of possible strategic targets in the network of the enemy or neutral coalition. +-- @{#STRATEGO.FindAffordableStrategicTarget}(): When using budgets, find **one** strategic target you can afford. +-- @{#STRATEGO.FindAffordableConsolidationTarget}(): When using budgets, find **one** consolidation target you can afford. +-- @{#STRATEGO.FindClosestStrategicTarget}(): Find closest strategic target from a given start point. +-- @{#STRATEGO.FindClosestConsolidationTarget}(): Find closest consolidation target from a given start point. +-- @{#STRATEGO.GetHighestWeightKnots}(): Get a list of the knots with the highest weight. Coalition independent. +-- @{#STRATEGO.GetNextHighestWeightKnots}(): Get a list of the knots a weight less than the give parameter. Coalition independent. +-- +-- +-- **How** you act on these suggestions is again totally up to your mission design. +-- +-- ## Using budgets +-- +-- Set up STRATEGO to use budgets to limit the target selection. **How** your side actually earns budgets is up to your mission design. However, when using budgets, a target will only be selected, +-- when you have more budget points available than the value points of the targeted base. +-- +-- -- use budgets +-- Bluecher:SetUsingBudget(true,500) +-- +-- ## Helpers: +-- +-- +-- @{#STRATEGO.GetBudget}(): Get the current budget points. +-- @{#STRATEGO.AddBudget}(): Add a number of budget points. +-- @{#STRATEGO.SubtractBudget}(): Subtract a number of budget points. +-- @{#STRATEGO.SetNeutralBenefit Set neutral benefit, i.e. how many points it is cheaper to decide for a neutral vs an enemy knot when taking decisions. +-- +-- +-- ## Functions to query a knot's data +-- +-- +-- @{#STRATEGO.GetKnotBaseWeight}(): Get the base weight of a knot by its name. +-- @{#STRATEGO.GetKnotCoalition}(): Get the COALITION of a knot by its name. +-- @{#STRATEGO.GetKnotType}(): Get the TYPE of a knot by its name. +-- @{#STRATEGO.GetKnotZone}(): Get the ZONE of a knot by its name. +-- @{#STRATEGO.GetKnotOpsZone}(): Get the OPSZONE of a knot by its name. +-- @{#STRATEGO.GetKnotCoordinate}(): Get the COORDINATE of a knot by its name. +-- @{#STRATEGO.IsAirbase}(): Check if the TYPE of a knot is AIRBASE. +-- @{#STRATEGO.IsPort}(): Check if the TYPE of a knot is PORT. +-- @{#STRATEGO.IsPOI}(): Check if the TYPE of a knot is POI. +-- @{#STRATEGO.IsFARP}(): Check if the TYPE of a knot is FARP. +-- @{#STRATEGO.IsShip}(): Check if the TYPE of a knot is SHIP. +-- +-- +-- ## Various +-- +-- +-- @{#STRATEGO.FindNeighborKnots}(): Get neighbor knots of a named knot. +-- @{#STRATEGO.FindRoute}(): Find a route between two knots. +-- @{#STRATEGO.SetCaptureOptions}(): Set how many units of which minimum threat level are needed to capture one knot (i.e. the underlying OpsZone). +-- @{#STRATEGO.SetDebug}(): Set debug and draw options. +-- +-- +-- ## Visualisation example code for the Syria map: +-- +-- local Bluecher = STRATEGO:New("Bluecher",coalition.side.BLUE,100) +-- Bluecher:SetDebug(true,true,true) +-- Bluecher:Start() +-- +-- Bluecher:AddRoutesManually(AIRBASE.Syria.Beirut_Rafic_Hariri,AIRBASE.Syria.Larnaca) +-- Bluecher:AddRoutesManually(AIRBASE.Syria.Incirlik,AIRBASE.Syria.Hatay) +-- Bluecher:AddRoutesManually(AIRBASE.Syria.Incirlik,AIRBASE.Syria.Minakh) +-- Bluecher:AddRoutesManually(AIRBASE.Syria.King_Hussein_Air_College,AIRBASE.Syria.H4) +-- Bluecher:AddRoutesManually(AIRBASE.Syria.Sayqal,AIRBASE.Syria.At_Tanf) +-- +-- local route = Bluecher:FindRoute(AIRBASE.Syria.Rosh_Pina,AIRBASE.Syria.Incirlik,5,true) +-- UTILS.PrintTableToLog(route,1) +-- +-- @field #STRATEGO +STRATEGO = { + ClassName = "STRATEGO", + debug = false, + drawzone = false, + markzone = false, + version = "0.1.0", + portweight = 3, + POIweight = 1, + maxrunways = 3, + coalition = nil, + colors = nil, + airbasetable = {}, + nonconnectedab = {}, + easynames = {}, + maxdist = 150, -- km + disttable = {}, + routexists = {}, + routefactor = 5, + OpsZones = {}, + NeutralBenefit = 100, + Budget = 0, + usebudget = false, + CaptureUnits = 3, + CaptureThreatlevel = 1, +} + +--- +-- @type STRATEGO.Data +-- @field #string name +-- @field #number baseweight +-- @field #number weight +-- @field #number coalition +-- @field #boolean port +-- @field Core.Zone#ZONE_RADIUS zone, +-- @field Core.Point#COORDINATRE coord +-- @field #string type +-- @field Ops.OpsZone#OPSZONE opszone + +--- +-- @type STRATEGO.DistData +-- @field #string start +-- @field #string target +-- @field #number dist + +--- +-- @type STRATEGO.Target +-- @field #string name +-- @field #number dist +-- @field #number points +-- @field #number coalition +-- @field #string coalitionname + +--- +-- @type STRATEGO.Type +-- @field #string AIRBASE +-- @field #string PORT +-- @field #string POI +-- @field #string FARP +-- @field #string SHIP +STRATEGO.Type = { + AIRBASE = "AIRBASE", + PORT = "PORT", + POI = "POI", + FARP = "FARP", + SHIP = "SHIP", +} + +--- [USER] Create a new STRATEGO object and start it up. +-- @param #STRATEGO self +-- @param #string Name Name of the Adviser. +-- @param #number Coalition Coalition, e.g. coalition.side.BLUE. +-- @param #number MaxDist Maximum distance of a single route in kilometers, defaults to 150. +-- @return #STRATEGO self +function STRATEGO:New(Name,Coalition,MaxDist) + -- Inherit everything from BASE class. + local self = BASE:Inherit(self, BASE:New()) -- #STRATEGO + + self.coalition = Coalition + self.coalitiontext = UTILS.GetCoalitionName(Coalition) + self.name = Name or "Hannibal" + + self.maxdist = MaxDist or 150 -- km + self.disttable = {} + self.routexists = {} + + self.lid = string.format("STRATEGO %s %s | ",self.name,self.version) + + self.bases = SET_AIRBASE:New():FilterOnce() + self.ports = SET_ZONE:New():FilterPrefixes("Port"):FilterOnce() + self.POIs = SET_ZONE:New():FilterPrefixes("POI"):FilterOnce() + + self.colors = { + [1] = {0,1,0}, -- green + [2] = {1,0,0}, -- red + [3] = {0,0,1}, -- blue + [4] = {1,0.65,0}, -- orange + } + + return self +end + +--- [USER] Do initial setup and get ready. +-- @param #STRATEGO self +-- @return #STRATEGO self +function STRATEGO:Start() + self:T(self.lid.."Start") + self:AnalyseBases() + self:AnalysePOIs(self.ports,self.portweight,"PORT") + self:AnalysePOIs(self.POIs,self.POIweight,"POI") + + for i=self.maxrunways,1,-1 do + self:AnalyseRoutes(i,i*self.routefactor,self.colors[(i%3)+1],i) + --self:AnalyseRoutes(2,2*self.routefactor,self.colors[2],2) + --self:AnalyseRoutes(1,1*self.routefactor,self.colors[3],3) + end + self:AnalyseUnconnected(self.colors[4]) + + self:I(self.lid.."Advisory ready.") + return self +end + +--- [USER] Set up usage of budget and set an initial budget in points. +-- @param #STRATEGO self +-- @param #boolean Usebudget If true, use budget for advisory calculations. +-- @param #number StartBudget Initial budget to be used, defaults to 500. +function STRATEGO:SetUsingBudget(Usebudget,StartBudget) + self:T(self.lid.."SetUsingBudget") + self.usebudget = Usebudget + self.Budget = StartBudget + return self +end + +--- [USER] Set debugging. +-- @param #STRATEGO self +-- @param #boolean Debug If true, switch on debugging. +-- @param #boolean DrawZones If true, draw the OpsZones on the F10 map. +-- @param #boolean MarkZones if true, mark the OpsZones on the F10 map (with further information). +function STRATEGO:SetDebug(Debug,DrawZones,MarkZones) + self.debug = Debug + self.drawzone = DrawZones + self.markzone = MarkZones + return self +end + +--- [USER] Set weights for knots and routes to determine their importance. +-- @param #STRATEGO self +-- @param #number MaxRunways Set the maximum number of runways the big (equals strategic) airbases on the map have. Defaults to 3. The weight of an airbase knot hence equals the number of runways. +-- @param #number PortWeight Set what weight a port knot has. Defaults to 3. +-- @param #number POIWeight Set what weight a POI knot has. Defaults to 1. +-- @param #number RouteFactor Defines which weight each route between two defined knots gets: Weight * RouteFactor. +-- @return #STRATEGO self +function STRATEGO:SetWeights(MaxRunways,PortWeight,POIWeight,RouteFactor) + self.portweight = PortWeight or 3 + self.POIweight = POIWeight or 1 + self.maxrunways = MaxRunways or 3 + self.routefactor = RouteFactor or 5 + return self +end + +--- [USER] Set neutral benefit, i.e. how many points it is cheaper to decide for a neutral vs an enemy knot when taking decisions. +-- @param #STRATEGO self +-- @param #number NeutralBenefit Pointsm defaults to 100. +-- @return #STRATEGO self +function STRATEGO:SetNeutralBenefit(NeutralBenefit) + self.NeutralBenefit = NeutralBenefit or 100 + return self +end + +--- [USER] Set how many units of which minimum threat level are needed to capture one knot (i.e. the underlying OpsZone). +-- @param #STRATEGO self +-- @param #number CaptureUnits Number of units needed, defaults to three. +-- @param #number CaptureThreatlevel Threat level needed, can be 0..10, defaults to one. +-- @return #STRATEGO self +function STRATEGO:SetCaptureOptions(CaptureUnits,CaptureThreatlevel) + self.CaptureUnits = CaptureUnits or 3 + self.CaptureThreatlevel = CaptureThreatlevel or 1 + return self +end + +--- [INTERNAL] Analyse airbase setups +-- @param #STRATEGO self +-- @return #STRATEGO self +function STRATEGO:AnalyseBases() + self:T(self.lid.."AnalyseBases") + local colors = self.colors + local debug = self.debug + local airbasetable = self.airbasetable + local nonconnectedab = self.nonconnectedab + local easynames = self.easynames + + -- find bases with >= 1 runways + self.bases:ForEach( + function(afb) + local ab = afb -- Wrapper.Airbase#AIRBASE + local abname = ab:GetName() + local runways = ab:GetRunways() + local numrwys = #runways + if numrwys >= 1 then numrwys = numrwys * 0.5 end + local abzone = ab:GetZone() + local coa = ab:GetCoalition() + 1 + local abtype = "AIRBASE" + if ab:IsShip() then + numrwys = 1 + abtype = "SHIP" + end + if ab:IsHelipad() then + numrwys = 1 + abtype = "FARP" + end + local coord = abzone:GetCoordinate() + if debug then + abzone:DrawZone(-1,colors[coa],1,colors[coa],0.3,1) + coord:TextToAll(tostring(numrwys),-1,{0,0,0},1,colors[coa],0.3,20) + end + local opszone = self:GetNewOpsZone(abname,coa-1) + local tbl = { + name = abname, + baseweight = numrwys, + weight = 0, + coalition = coa-1, + port = false, + zone = abzone, + coord = coord, + type = abtype, + } + airbasetable[abname] = tbl + nonconnectedab[abname] = true + local name = string.gsub(abname,"[%p%s]",".") + easynames[name]=abname + end + ) + return self +end + +--- [INTERNAL] Update knot coalitions +-- @param #STRATEGO self +-- @return #STRATEGO self +function STRATEGO:UpdateKnotCoalitions() + self:T(self.lid.."UpdateKnotCoalitions") + local newtable = {} + for _id,_data in pairs(self.airbasetable) do + local data = _data -- #STRATEGO.Data + if data.type == "AIRBASE" or data.type == "FARP" then + data.coalition = AIRBASE:FindByName(data.name):GetCoalition() + else + data.coalition = data.opszone:GetOwner() + end + newtable[_id] = _data + end + self.airbasetable = nil + self.airbasetable = newtable + return self +end + +--- [INTERNAL] Get an OpsZone from a Zone object. +-- @param #STRATEGO self +-- @param Core.Zone#ZONE Zone +-- @param #number Coalition +-- @return Ops.OpsZone#OPSZONE OpsZone +function STRATEGO:GetNewOpsZone(Zone,Coalition) + self:T(self.lid.."GetNewOpsZone") + local opszone = OPSZONE:New(Zone,Coalition or 0) + opszone:SetCaptureNunits(self.CaptureUnits) + opszone:SetCaptureThreatlevel(self.CaptureThreatlevel) + opszone:SetDrawZone(self.drawzone) + opszone:SetMarkZone(self.markzone) + opszone:Start() + return opszone +end + +--- [INTERNAL] Analyse POI setups +-- @param #STRATEGO self +-- @return #STRATEGO self +function STRATEGO:AnalysePOIs(Set,Weight,Key) + self:T(self.lid.."AnalysePOIs") + local colors = self.colors + local debug = self.debug + local airbasetable = self.airbasetable + local nonconnectedab = self.nonconnectedab + local easynames = self.easynames + Set:ForEach( + function(port) + local zone = port -- Core.Zone#ZONE_RADIUS + local zname = zone:GetName() + local coord = zone:GetCoordinate() + if debug then + zone:DrawZone(-1,colors[1],1,colors[1],0.3,1) + coord:TextToAll(tostring(Weight),-1,{0,0,0},1,colors[1],0.3,20) + end + local opszone = self:GetNewOpsZone(zone) + local tbl = { -- #STRATEGO.Data + name = zname, + baseweight = Weight, + weight = 0, + coalition = coalition.side.NEUTRAL, + port = true, + zone = zone, + coord = coord, + type = Key, + opszone = opszone, + } + airbasetable[zone:GetName()] = tbl + nonconnectedab[zone:GetName()] = true + local name = string.gsub(zname,"[%p%s]",".") + easynames[name]=zname + end + ) + return self +end + +--- [INTERNAL] Get nice route text +-- @param #STRATEGO self +-- @return #STRATEGO self +function STRATEGO:GetToFrom(StartPoint,EndPoint) + self:T(self.lid.."GetToFrom") + local pstart = string.gsub(StartPoint,"[%p%s]",".") + local pend = string.gsub(EndPoint,"[%p%s]",".") + local fromto = pstart..";"..pend + local tofrom = pend..";"..pstart + return fromto, tofrom +end + +--- [USER] Manually add a route, for e.g. Island hopping or to connect isolated networks. Use **after** STRATEGO has been started! +-- @param #STRATEGO self +-- @param #string Startpoint +-- @param #string Endpoint +-- @param #table Color (Optional) RGB color table {r, g, b}, e.g. {1,0,0} for red. Defaults to lila. +-- @param #number LineType (Optional) Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 5. +-- @param #boolean Draw (Optional) If true, draw route on the F10 map. Defaukt false. +-- @return #STRATEGO self +function STRATEGO:AddRoutesManually(Startpoint,Endpoint,Color,Linetype,Draw) + local fromto,tofrom = self:GetToFrom(Startpoint,Endpoint) + local startcoordinate = self.airbasetable[Startpoint].coord + local targetcoordinate = self.airbasetable[Endpoint].coord + local dist = UTILS.Round(targetcoordinate:Get2DDistance(startcoordinate),-2)/1000 + local color = Color or {136/255,0,1} + local linetype = Linetype or 5 + local data = { + start = Startpoint, + target = Endpoint, + dist = dist, + } + --table.insert(disttable,fromto,data) + self.disttable[fromto] = data + self.disttable[tofrom] = data + --table.insert(disttable,tofrom,data) + table.insert(self.routexists,fromto) + table.insert(self.routexists,tofrom) + self.nonconnectedab[Endpoint] = false + self.nonconnectedab[Startpoint] = false + local factor = self.airbasetable[Startpoint].baseweight*self.routefactor + self.airbasetable[Startpoint].weight = self.airbasetable[Startpoint].weight+factor + self.airbasetable[Endpoint].weight = self.airbasetable[Endpoint].weight+factor + if self.debug or Draw then + startcoordinate:LineToAll(targetcoordinate,-1,color,1,linetype,nil,string.format("%dkm",dist)) + end + return self +end + +--- [INTERNAL] Analyse routes +-- @param #STRATEGO self +-- @return #STRATEGO self +function STRATEGO:AnalyseRoutes(tgtrwys,factor,color,linetype) + self:T(self.lid.."AnalyseRoutes") + for _,_ab in pairs(self.airbasetable) do + if _ab.baseweight >= 1 then + local startpoint = _ab.name + local startcoord = _ab.coord + for _,_data in pairs(self.airbasetable) do + local fromto,tofrom = self:GetToFrom(startpoint,_data.name) + if _data.name == startpoint then + -- sam as we + elseif _data.baseweight == tgtrwys and not (self.routexists[fromto] or self.routexists[tofrom]) then + local tgtc = _data.coord + local dist = UTILS.Round(tgtc:Get2DDistance(startcoord),-2)/1000 + if dist <= self.maxdist then + --local text = string.format("Distance %s to %s is %dkm",startpoint,_data.name,dist) + --MESSAGE:New(text,10):ToLog() + local data = { + start = startpoint, + target = _data.name, + dist = dist, + } + --table.insert(disttable,fromto,data) + self.disttable[fromto] = data + self.disttable[tofrom] = data + --table.insert(disttable,tofrom,data) + table.insert(self.routexists,fromto) + table.insert(self.routexists,tofrom) + self.nonconnectedab[_data.name] = false + self.nonconnectedab[startpoint] = false + self.airbasetable[startpoint].weight = self.airbasetable[startpoint].weight+factor + self.airbasetable[_data.name].weight = self.airbasetable[_data.name].weight+factor + if self.debug then + startcoord:LineToAll(tgtc,-1,color,1,linetype,nil,string.format("%dkm",dist)) + end + end + end + end + end + end + return self +end + +--- [INTERNAL] Analyse non-connected points. +-- @param #STRATEGO self +-- @param #table Color RGB color to be used. +-- @return #STRATEGO self +function STRATEGO:AnalyseUnconnected(Color) + self:T(self.lid.."AnalyseUnconnected") + -- Non connected ones + for _name,_noconnect in pairs(self.nonconnectedab) do + if _noconnect then + -- Find closest connected airbase + local startpoint = _name + local startcoord = self.airbasetable[_name].coord + local shortest = 1000*1000 + local closest = nil + local closestcoord = nil + for _,_data in pairs(self.airbasetable) do + if _name ~= _data.name then + --local tgt = AIRBASE:FindByName(_data.name) + local tgtc = _data.coord + local dist = UTILS.Round(tgtc:Get2DDistance(startcoord),-2)/1000 + if dist < shortest and self.nonconnectedab[_data.name] == false then + --local text = string.format("Distance %s to %s is %dkm",startpoint,_data.name,dist) + shortest = dist + closest = _data.name + closestcoord = tgtc + --MESSAGE:New(text,10):ToLog():ToAll() + end + end + end + if closest then + if self.debug then + startcoord:LineToAll(closestcoord,-1,Color,1,3,nil,string.format("%dkm",shortest)) + end + self.airbasetable[startpoint].weight = self.airbasetable[startpoint].weight+1 + self.airbasetable[closest].weight = self.airbasetable[closest].weight+1 + local data = { + start = startpoint, + target = closest, + dist = shortest, + } + local fromto,tofrom = self:GetToFrom(startpoint,closest) + self.disttable[fromto] = data + self.disttable[tofrom] = data + table.insert(self.routexists,fromto) + table.insert(self.routexists,tofrom) + end + end + end + return self +end + +--- [USER] Get a list of the knots with the highest weight. +-- @param #STRATEGO self +-- @return #table Table of knots. +-- @return #number Weight The consolidated weight associated with the knots. +function STRATEGO:GetHighestWeightKnots() + self:T(self.lid.."GetHighestWeightBases") + local weight = 0 + local airbases = {} + for _name,_data in pairs(self.airbasetable) do + if _data.weight >= weight then + weight = _data.weight + if not airbases[weight] then airbases[weight]={} end + table.insert(airbases[weight],_name) + end + end + return airbases[weight],weight +end + +--- [USER] Get a list of the knots a weight less than the give parameter. +-- @param #STRATEGO self +-- @return #table Table of knots. +-- @return #number Weight The consolidated weight associated with the knots. +function STRATEGO:GetNextHighestWeightKnots(Weight) + self:T(self.lid.."GetNextHighestWeightBases") + local weight = 0 + local airbases = {} + for _name,_data in pairs(self.airbasetable) do + if _data.weight >= weight and _data.weight < Weight then + weight = _data.weight + if not airbases[weight] then airbases[weight]={} end + table.insert(airbases[weight],_name) + end + end + return airbases[weight],weight +end + +--- [USER] Get the aggregated weight of a knot by its name. +-- @param #STRATEGO self +-- @param #string Name. +-- @return #number Weight The weight or 0 if not found. +function STRATEGO:GetKnotWeight(Name) + if Name and self.airbasetable[Name] then + return self.airbasetable[Name].weight or 0 + else + return 0 + end +end + +--- [USER] Get the base weight of a knot by its name. +-- @param #STRATEGO self +-- @param #string Name. +-- @return #number Weight The base weight or 0 if not found. +function STRATEGO:GetKnotBaseWeight(Name) + if Name and self.airbasetable[Name] then + return self.airbasetable[Name].baseweight or 0 + else + return 0 + end +end + +--- [USER] Get the COALITION of a knot by its name. +-- @param #STRATEGO self +-- @param #string Name. +-- @return #number Coalition The coalition. +function STRATEGO:GetKnotCoalition(Name) + if Name and self.airbasetable[Name] then + return self.airbasetable[Name].coalition or coalition.side.NEUTRAL + else + return coalition.side.NEUTRAL + end +end + +--- [USER] Get the TYPE of a knot by its name. +-- @param #STRATEGO self +-- @param #string Name. +-- @return #string Type Type of the knot, e.g. STRATEGO.Type.AIRBASE or nil if not found. +function STRATEGO:GetKnotType(Name) + if Name and self.airbasetable[Name] then + return self.airbasetable[Name].type + else + return nil + end +end + +--- [USER] Get the ZONE of a knot by its name. +-- @param #STRATEGO self +-- @param #string Name. +-- @return Core.Zone#ZONE Zone The Zone of the knot or nil if not found. +function STRATEGO:GetKnotZone(Name) + if Name and self.airbasetable[Name] then + return self.airbasetable[Name].zone + else + return nil + end +end + +--- [USER] Get the OPSZONE of a knot by its name. +-- @param #STRATEGO self +-- @param #string Name. +-- @return Ops.OpsZone#OPSZONE OpsZone The OpsZone of the knot or nil if not found. +function STRATEGO:GetKnotOpsZone(Name) + if Name and self.airbasetable[Name] then + return self.airbasetable[Name].opszone + else + return nil + end +end + +--- [USER] Get the COORDINATE of a knot by its name. +-- @param #STRATEGO self +-- @param #string Name. +-- @return Core.Point#COORDINATE Coordinate The Coordinate of the knot or nil if not found. +function STRATEGO:GetKnotCoordinate(Name) + if Name and self.airbasetable[Name] then + return self.airbasetable[Name].coord + else + return nil + end +end + +--- [USER] Check if the TYPE of a knot is AIRBASE. +-- @param #STRATEGO self +-- @param #string Name. +-- @return #boolean Outcome +function STRATEGO:IsAirbase(Name) + if Name and self.airbasetable[Name] then + return self.airbasetable[Name].type == STRATEGO.Type.AIRBASE + else + return false + end +end + +--- [USER] Check if the TYPE of a knot is PORT. +-- @param #STRATEGO self +-- @param #string Name. +-- @return #boolean Outcome +function STRATEGO:IsPort(Name) + if Name and self.airbasetable[Name] then + return self.airbasetable[Name].type == STRATEGO.Type.PORT + else + return false + end +end + +--- [USER] Check if the TYPE of a knot is POI. +-- @param #STRATEGO self +-- @param #string Name. +-- @return #boolean Outcome +function STRATEGO:IsPOI(Name) + if Name and self.airbasetable[Name] then + return self.airbasetable[Name].type == STRATEGO.Type.POI + else + return false + end +end + +--- [USER] Check if the TYPE of a knot is FARP. +-- @param #STRATEGO self +-- @param #string Name. +-- @return #boolean Outcome +function STRATEGO:IsFARP(Name) + if Name and self.airbasetable[Name] then + return self.airbasetable[Name].type == STRATEGO.Type.FARP + else + return false + end +end + +--- [USER] Check if the TYPE of a knot is SHIP. +-- @param #STRATEGO self +-- @param #string Name. +-- @return #boolean Outcome +function STRATEGO:IsShip(Name) + if Name and self.airbasetable[Name] then + return self.airbasetable[Name].type == STRATEGO.Type.SHIP + else + return false + end +end + +--- [USER] Get the next best consolidation target knot with a lower BaseWeight. +-- @param #STRATEGO self +-- @param #string Startpoint Name of start point. +-- @param #number BaseWeight Base weight of the knot, i.e. the number of runways of an airbase or the weight of ports or POIs. +-- @return #number ShortestDist Shortest distance found. +-- @return #string Name Name of the target knot. +-- @return #number Weight Consolidated weight of the target knot, zero if none found. +-- @return #number Coalition Coaltion of the target. +function STRATEGO:FindClosestConsolidationTarget(Startpoint,BaseWeight) + self:T(self.lid.."FindClosestConsolidationTarget for "..Startpoint.." Weight "..BaseWeight or 0) + -- find existing routes + local shortest = 1000*1000 + local target = nil + local weight = 0 + local coa = nil + if not BaseWeight then BaseWeight = self.maxrunways-1 end + local startpoint = string.gsub(Startpoint,"[%p%s]",".") + for _,_route in pairs(self.routexists) do + if string.find(_route,startpoint,1,true) then + --BASE:I({_route,startpoint}) + local dist = self.disttable[_route].dist + local tname = string.gsub(_route,startpoint,"") + local tname = string.gsub(tname,";","") + local cname = self.easynames[tname] + local targetweight = self.airbasetable[cname].baseweight + coa = self.airbasetable[cname].coalition + if (dist < shortest) and (coa ~= self.coalition) and (BaseWeight >= targetweight) then + shortest = dist + target = cname + weight = self.airbasetable[cname].weight + coa = self.airbasetable[cname].coalition + end + end + end + return shortest,target, weight, coa +end + +--- [USER] Get the next best strategic target knot with same or higher BaseWeight. +-- @param #STRATEGO self +-- @param #string Startpoint Name of start point. +-- @param #number BaseWeight Base weight of the knot, e.g. the number of runways of an airbase or the weight of ports or POIs. +-- @return #number ShortestDist Shortest distance found. +-- @return #string Name Name of the target knot. +-- @return #number Weight Consolidated weight of the target knot, zero if none found. +-- @return #number Coalition Coaltion of the target. +function STRATEGO:FindClosestStrategicTarget(Startpoint,BaseWeight) + self:T(self.lid.."FindClosestStrategicTarget") + -- find existing routes + local shortest = 1000*1000 + local target = nil + local weight = 0 + local coa = nil + if not BaseWeight then BaseWeight = self.maxrunways end + local startpoint = string.gsub(Startpoint,"[%p%s]",".") + for _,_route in pairs(self.routexists) do + if string.find(_route,startpoint,1,true) then + local dist = self.disttable[_route].dist + local tname = string.gsub(_route,startpoint,"") + local tname = string.gsub(tname,";","") + local cname = self.easynames[tname] + if dist < shortest and self.airbasetable[cname].coalition ~= self.coalition and self.airbasetable[cname].baseweight >= BaseWeight then + shortest = dist + target = cname + weight = self.airbasetable[cname].weight + coa = self.airbasetable[cname].coalition + end + end + end + return shortest,target,weight, coa +end + +--- [USER] Get the next best strategic target knots in the network. +-- @param #STRATEGO self +-- @return #table of #STRATEGO.Target data points +function STRATEGO:FindStrategicTargets() + local targets = {} + for _,_data in pairs(self.airbasetable) do + local data = _data -- #STRATEGO.Data + if data.coalition == self.coalition then + local dist, name, points, coa = self:FindClosestStrategicTarget(data.name,self.maxrunways) + if coa == coalition.side.NEUTRAL and points ~= 0 then + local fpoints = points + self.NeutralBenefit + local tries = 1 + while targets[fpoints] or tries < 100 do + fpoints = points + (self.NeutralBenefit+math.random(1,100)) + tries = tries + 1 + end + targets[fpoints] = { + name = name, + dist = dist, + points = fpoints, + coalition = coa, + } + end + local enemycoa = self.coalition == coalition.side.BLUE and coalition.side.RED or coalition.side.BLUE + if coa == enemycoa and points ~= 0 then + local fpoints = points + local tries = 1 + while targets[fpoints] or tries < 100 do + fpoints = points + (math.random(1,100)) + tries = tries + 1 + end + targets[fpoints] = { + name = name, + dist = dist, + points = fpoints, + coalition = coa, + } + end + end + end + return targets +end + +--- [USER] Get the next best consolidation target knots in the network. +-- @param #STRATEGO self +-- @return #table of #STRATEGO.Target data points +function STRATEGO:FindConsolidationTargets() + local targets = {} + for _,_data in pairs(self.airbasetable) do + local data = _data -- #STRATEGO.Data + if data.coalition == self.coalition then + local dist, name, points, coa = self:FindClosestConsolidationTarget(data.name,self.maxrunways-1) + if coa == coalition.side.NEUTRAL and points ~= 0 then + local fpoints = points + self.NeutralBenefit + local tries = 1 + while targets[fpoints] or tries < 100 do + fpoints = points - (self.NeutralBenefit+math.random(1,100)) + tries = tries + 1 + end + targets[fpoints] = { + name = name, + dist = dist, + points = fpoints, + coalition = coa, + coalitionname = UTILS.GetCoalitionName(coa), + } + end + local enemycoa = self.coalition == coalition.side.BLUE and coalition.side.RED or coalition.side.BLUE + if coa == enemycoa and points ~= 0 then + local fpoints = points + local tries = 1 + while targets[fpoints] or tries < 100 do + fpoints = points - (math.random(1,100)) + tries = tries + 1 + end + targets[fpoints] = { + name = name, + dist = dist, + points = fpoints, + coalition = coa, + coalitionname = UTILS.GetCoalitionName(coa), + } + end + end + end + return targets +end + +--- [USER] Get neighbor knots of a named knot. +-- @param #STRATEGO self +-- @param #string Name The name to search the neighbors for. +-- @param #boolean Enemies (optional) If true, find only enemy neighbors. +-- @param #boolean Friends (optional) If true, find only friendly or neutral neighbors. +-- @return #table Neighbors Table of #STRATEGO.DistData entries indexed by neighbor knot names. +function STRATEGO:FindNeighborKnots(Name,Enemies,Friends) + local neighbors = {} + local name = string.gsub(Name,"[%p%s]",".") + for _route,_data in pairs(self.disttable) do + if string.find(_route,name,1,true) then + local dist = self.disttable[_route] -- #STRATEGO.DistData + local tname = string.gsub(_route,name,"") + local tname = string.gsub(tname,";","") + local cname = self.easynames[tname] -- name of target + local encoa = self.coalition == coalition.side.BLUE and coalition.side.RED or coalition.side.BLUE + if Enemies == true then + if self.airbasetable[cname].coalition == encoa then + neighbors[cname] = dist + end + elseif Friends == true then + if self.airbasetable[cname].coalition ~= encoa then + neighbors[cname] = dist + end + else + neighbors[cname] = dist + end + end + end + return neighbors +end + +--- [USER] Find a route between two knots. +-- @param #STRATEGO self +-- @param #string Start The name of the start knot. +-- @param #string End The name of the end knot. +-- @param #number Hops Max iterations to find a route. +-- @param #boolean Draw If true, draw the route on the map. +-- @return #table Route Table of #string name entries of the route +-- @return #boolean Complete If true, the route was found end-to-end. +function STRATEGO:FindRoute(Start,End,Hops,Draw) + self:I({Start,End,Hops}) + --local bases = UTILS.DeepCopy(self.airbasetable) + local Route = {} + local hops = Hops or 4 + local routecomplete = false + + local function Checker(neighbors) + for _name,_data in pairs(neighbors) do + if _name == End then + -- found it + return End + end + end + return nil + end + + local function NextClosest(Start,End) + local ecoord = self.airbasetable[End].coord + local knots = self:FindNeighborKnots(Start) + local closest = nil + local closedist = 1000*1000 + for _name,_dist in pairs(knots) do + local kcoord = self.airbasetable[_name].coord + local dist = math.floor((kcoord:Get2DDistance(ecoord)/1000)+0.5) + if dist < closedist then + closedist = dist + closest = _name + end + end + if closest then + --MESSAGE:New(string.format("Start %s | End %s | Nextclosest %s",Start,End,closest),10,"STRATEGO"):ToLog():ToAll() + return closest + end + end + + local function DrawRoute(Route) + for i=1,#Route-1 do + local p1=Route[i] + local p2=Route[i+1] + local c1 = self.airbasetable[p1].coord -- Core.Point#COORDINATE + local c2 = self.airbasetable[p2].coord -- Core.Point#COORDINATE + c1:LineToAll(c2,-1,{0,0,0},1,6) + end + end + + -- One hop + Route[#Route+1] = Start + local knots = self:FindNeighborKnots(Start) + local endpoint = Checker(knots) + + if endpoint then + Route[#Route+1] = endpoint + routecomplete = true + else + local spoint = Start + for i=1,hops do + local Next = NextClosest(spoint,End) + if Next then + Route[#Route+1] = Next + local knots = self:FindNeighborKnots(Next) + local endpoint = Checker(knots) + if endpoint then + Route[#Route+1] = endpoint + routecomplete = true + break + else + spoint = Next + end + end + end + end + if self.debug or Draw then DrawRoute(Route) end + return Route, routecomplete +end + +--- [USER] Add budget points. +-- @param #STRATEGO self +-- @param #number Number of points to add. +-- @return #STRATEGO self +function STRATEGO:AddBudget(Number) + self.Budget = self.Budget + Number + return self +end + +--- [USER] Subtract budget points. +-- @param #STRATEGO self +-- @param #number Number of points to subtract. +-- @return #STRATEGO self +function STRATEGO:SubtractBudget(Number) + self.Budget = self.Budget - Number + return self +end + +--- [USER] Get budget points. +-- @param #STRATEGO self +-- @return #number budget +function STRATEGO:GetBudget() + return self.Budget +end + +--- [USER] Find **one** affordable strategic target. +-- @param #STRATEGO self +-- @return #table Target Table with #STRATEGO.Target data or nil if none found. +function STRATEGO:FindAffordableStrategicTarget() + local targets = self:FindStrategicTargets() -- #table of #STRATEGO.Target + local budget = self.Budget + --local leftover = self.Budget + local target = nil -- #STRATEGO.Target + local Targets = {} + for _,_data in pairs(targets) do + local data = _data -- #STRATEGO.Target + --if data.points <= budget and budget-data.points < leftover then + if data.points <= budget then + --leftover = budget-data.points + table.insert(Targets,data) + self:T(self.lid.."Affordable strategic target: "..data.name) + end + end + if not targets then + self:T(self.lid.."No suitable target found!") + return nil + end + target = Targets[math.random(1,#Targets)] + if target then + self:T(self.lid.."Final affordable strategic target: "..target.name) + return target + else + return nil + end +end + +--- [USER] Find **one** affordable consolidation target. +-- @param #STRATEGO self +-- @return #table Target Table with #STRATEGO.Target data or nil if none found. +function STRATEGO:FindAffordableConsolidationTarget() + local targets = self:FindConsolidationTargets() -- #table of #STRATEGO.Target + local budget = self.Budget + --local leftover = self.Budget + local target = nil -- #STRATEGO.Target + local Targets = {} + for _,_data in pairs(targets) do + local data = _data -- #STRATEGO.Target + --if data.points <= budget and budget-data.points < leftover then + if data.points <= budget then + --leftover = budget-data.points + table.insert(Targets,data) + self:T(self.lid.."Affordable consolidation target: "..data.name) + end + end + if not targets then + self:T(self.lid.."No suitable target found!") + return nil + end + target = Targets[math.random(1,#Targets)] + if target then + self:T(self.lid.."Final affordable consolidation target: "..target.name) + return target + else + return nil + end +end + +--------------------------------------------------------------------------------------------------------------- +-- +-- End +-- +--------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Modules.lua b/Moose Development/Moose/Modules.lua index 7ab4ad2e8..5fe27008d 100644 --- a/Moose Development/Moose/Modules.lua +++ b/Moose Development/Moose/Modules.lua @@ -84,6 +84,7 @@ __Moose.Include( 'Scripts/Moose/Functional/ZoneGoal.lua' ) __Moose.Include( 'Scripts/Moose/Functional/ZoneGoalCargo.lua' ) __Moose.Include( 'Scripts/Moose/Functional/ZoneGoalCoalition.lua' ) __Moose.Include( 'Scripts/Moose/Functional/Tiresias.lua' ) +__Moose.Include( 'Scripts/Moose/Functional/Stratego.lua' ) __Moose.Include( 'Scripts/Moose/Ops/Airboss.lua' ) __Moose.Include( 'Scripts/Moose/Ops/AirWing.lua' ) diff --git a/Moose Setup/Moose.files b/Moose Setup/Moose.files index a863db719..126021f95 100644 --- a/Moose Setup/Moose.files +++ b/Moose Setup/Moose.files @@ -84,6 +84,7 @@ Functional/AICSAR.lua Functional/AmmoTruck.lua Functional/ZoneGoalCargo.lua Functional/Tiresias.lua +Functional/Stratego.lua Ops/Airboss.lua Ops/RecoveryTanker.lua From de6c7d636b41da498709668f72a767aa22fd93c1 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 3 Jan 2024 18:56:43 +0100 Subject: [PATCH 11/17] docu --- Moose Development/Moose/Functional/Stratego.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Functional/Stratego.lua b/Moose Development/Moose/Functional/Stratego.lua index 4f1cd8320..471391010 100644 --- a/Moose Development/Moose/Functional/Stratego.lua +++ b/Moose Development/Moose/Functional/Stratego.lua @@ -126,7 +126,7 @@ -- @{#STRATEGO.GetBudget}(): Get the current budget points. -- @{#STRATEGO.AddBudget}(): Add a number of budget points. -- @{#STRATEGO.SubtractBudget}(): Subtract a number of budget points. --- @{#STRATEGO.SetNeutralBenefit Set neutral benefit, i.e. how many points it is cheaper to decide for a neutral vs an enemy knot when taking decisions. +-- @{#STRATEGO.SetNeutralBenefit}(): Set neutral benefit, i.e. how many points it is cheaper to decide for a neutral vs an enemy knot when taking decisions. -- -- -- ## Functions to query a knot's data @@ -154,7 +154,7 @@ -- @{#STRATEGO.SetDebug}(): Set debug and draw options. -- -- --- ## Visualisation example code for the Syria map: +-- ## Visualisation example code for the Syria map: -- -- local Bluecher = STRATEGO:New("Bluecher",coalition.side.BLUE,100) -- Bluecher:SetDebug(true,true,true) From 9b95e71d75d17a3d3d5c8a9be326e877a1342b7e Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 3 Jan 2024 22:32:37 +0100 Subject: [PATCH 12/17] Update Airboss.lua - Potential fix for error raised on discord --- Moose Development/Moose/Ops/Airboss.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Moose Development/Moose/Ops/Airboss.lua b/Moose Development/Moose/Ops/Airboss.lua index 10e175f70..d7a467e44 100644 --- a/Moose Development/Moose/Ops/Airboss.lua +++ b/Moose Development/Moose/Ops/Airboss.lua @@ -15606,6 +15606,11 @@ function AIRBOSS:_Number2Radio( radio, number, delay, interval, pilotcall ) Sender = "PilotCall" end + if Sender=="" then + self:E( self.lid .. string.format( "ERROR: Sender unknown!") ) + return + end + -- Split string into characters. local numbers = _split( number ) From 109270d71747d3496871fea2446fc0942e44a062 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 4 Jan 2024 11:27:48 +0100 Subject: [PATCH 13/17] STRATEGO * Renamed knots to nodes to have the correc lingo --- .../Moose/Functional/Stratego.lua | 190 +++++++++--------- 1 file changed, 95 insertions(+), 95 deletions(-) diff --git a/Moose Development/Moose/Functional/Stratego.lua b/Moose Development/Moose/Functional/Stratego.lua index 471391010..2f3f272a0 100644 --- a/Moose Development/Moose/Functional/Stratego.lua +++ b/Moose Development/Moose/Functional/Stratego.lua @@ -4,7 +4,7 @@ -- -- * Helper class for mission designers to support classic capture-the-base scenarios. -- * Creates a network of possible connections between bases (airbases, FARPs, Ships), Ports (defined as zones) and POIs (defined as zones). --- * Assigns a strategic value to each of the resulting knots. +-- * Assigns a strategic value to each of the resulting nodes. -- * Can create a list of targets for your next mission move, both strategic and consolidation targets. -- * Can be used with budgets to limit the target selection. -- * Highly configureable. @@ -51,16 +51,16 @@ -- # The STRATEGO Concept -- -- STRATEGO is a helper class for mission designers. --- The basic idea is to create a network of knots (bases) on the map, which each have a number of connections --- to other knots. The base value of each knot is the number of runways of the base (the bigger the more important), or in the case of Ports and POIs, the assigned value points. --- The strategic value of each base is determined by the number of routes going in and out of the knot, where connections between more strategic knots add a higher value to the --- strategic value than connections to less valueable knots. +-- The basic idea is to create a network of nodes (bases) on the map, which each have a number of connections +-- to other nodes. The base value of each node is the number of runways of the base (the bigger the more important), or in the case of Ports and POIs, the assigned value points. +-- The strategic value of each base is determined by the number of routes going in and out of the node, where connections between more strategic nodes add a higher value to the +-- strategic value than connections to less valueable nodes. -- -- ## Setup -- -- Setup is map indepent and works automatically. All airbases, FARPS, and ships on the map are considered. **Note:** Later spawned objects are not considered at the moment. -- --- -- Setup and start STRATGEO for the blue side, maximal knot distance is 100km +-- -- Setup and start STRATGEO for the blue side, maximal node distance is 100km -- local Bluecher = STRATEGO:New("Bluecher",coalition.side.BLUE,100) -- -- use budgets -- Bluecher:SetUsingBudget(true,500) @@ -71,15 +71,15 @@ -- -- ### Helper -- --- @{#STRATEGO.SetWeights}(): Set weights for knots and routes to determine their importance. +-- @{#STRATEGO.SetWeights}(): Set weights for nodes and routes to determine their importance. -- -- ### Hint -- --- Each knot is its own @{Ops.OpsZone#OPSZONE} object to manage the coalition alignment of that knot and how it can be conquered. +-- Each node is its own @{Ops.OpsZone#OPSZONE} object to manage the coalition alignment of that node and how it can be conquered. -- -- ### Distance -- --- The knot distance factor determines how many connections are there on the map. The smaller the lighter is the resulting net. The higher the thicker it gets, with more strategic options. +-- The node distance factor determines how many connections are there on the map. The smaller the lighter is the resulting net. The higher the thicker it gets, with more strategic options. -- Play around with the distance to get an optimal map for your scenario. -- -- One some maps, e.g. Syria, lower distance factors can create "islands" of unconnected network parts on the map. FARPs and POIs can bridge those gaps, or you can add routes manually. @@ -92,22 +92,22 @@ -- -- ## Get next possible targets -- --- There are two types of possible target lists, strategic and consolidation. Targets closer to the start knot are chosen as possible targets. +-- There are two types of possible target lists, strategic and consolidation. Targets closer to the start node are chosen as possible targets. -- -- -- * Strategic targets are of higher or equal base weight from a given start point. Can also be obtained for the whole net. -- * Consoliation targets are of smaller or equal base weight from a given start point. Can also be obtained for the whole net. -- -- --- @{#STRATEGO.UpdateKnotCoalitions}(): Update alls knot's coalition data before takign a decision. +-- @{#STRATEGO.UpdateNodeCoalitions}(): Update alls node's coalition data before takign a decision. -- @{#STRATEGO.FindStrategicTargets}(): Find a list of possible strategic targets in the network of the enemy or neutral coalition. -- @{#STRATEGO.FindConsolidationTargets}(): Find a list of possible strategic targets in the network of the enemy or neutral coalition. -- @{#STRATEGO.FindAffordableStrategicTarget}(): When using budgets, find **one** strategic target you can afford. -- @{#STRATEGO.FindAffordableConsolidationTarget}(): When using budgets, find **one** consolidation target you can afford. -- @{#STRATEGO.FindClosestStrategicTarget}(): Find closest strategic target from a given start point. -- @{#STRATEGO.FindClosestConsolidationTarget}(): Find closest consolidation target from a given start point. --- @{#STRATEGO.GetHighestWeightKnots}(): Get a list of the knots with the highest weight. Coalition independent. --- @{#STRATEGO.GetNextHighestWeightKnots}(): Get a list of the knots a weight less than the give parameter. Coalition independent. +-- @{#STRATEGO.GetHighestWeightNodes}(): Get a list of the nodes with the highest weight. Coalition independent. +-- @{#STRATEGO.GetNextHighestWeightNodes}(): Get a list of the nodes a weight less than the give parameter. Coalition independent. -- -- -- **How** you act on these suggestions is again totally up to your mission design. @@ -120,41 +120,41 @@ -- -- use budgets -- Bluecher:SetUsingBudget(true,500) -- --- ## Helpers: +-- ### Helpers: -- -- -- @{#STRATEGO.GetBudget}(): Get the current budget points. -- @{#STRATEGO.AddBudget}(): Add a number of budget points. -- @{#STRATEGO.SubtractBudget}(): Subtract a number of budget points. --- @{#STRATEGO.SetNeutralBenefit}(): Set neutral benefit, i.e. how many points it is cheaper to decide for a neutral vs an enemy knot when taking decisions. +-- @{#STRATEGO.SetNeutralBenefit}(): Set neutral benefit, i.e. how many points it is cheaper to decide for a neutral vs an enemy node when taking decisions. -- -- --- ## Functions to query a knot's data +-- ## Functions to query a node's data -- -- --- @{#STRATEGO.GetKnotBaseWeight}(): Get the base weight of a knot by its name. --- @{#STRATEGO.GetKnotCoalition}(): Get the COALITION of a knot by its name. --- @{#STRATEGO.GetKnotType}(): Get the TYPE of a knot by its name. --- @{#STRATEGO.GetKnotZone}(): Get the ZONE of a knot by its name. --- @{#STRATEGO.GetKnotOpsZone}(): Get the OPSZONE of a knot by its name. --- @{#STRATEGO.GetKnotCoordinate}(): Get the COORDINATE of a knot by its name. --- @{#STRATEGO.IsAirbase}(): Check if the TYPE of a knot is AIRBASE. --- @{#STRATEGO.IsPort}(): Check if the TYPE of a knot is PORT. --- @{#STRATEGO.IsPOI}(): Check if the TYPE of a knot is POI. --- @{#STRATEGO.IsFARP}(): Check if the TYPE of a knot is FARP. --- @{#STRATEGO.IsShip}(): Check if the TYPE of a knot is SHIP. +-- @{#STRATEGO.GetNodeBaseWeight}(): Get the base weight of a node by its name. +-- @{#STRATEGO.GetNodeCoalition}(): Get the COALITION of a node by its name. +-- @{#STRATEGO.GetNodeType}(): Get the TYPE of a node by its name. +-- @{#STRATEGO.GetNodeZone}(): Get the ZONE of a node by its name. +-- @{#STRATEGO.GetNodeOpsZone}(): Get the OPSZONE of a node by its name. +-- @{#STRATEGO.GetNodeCoordinate}(): Get the COORDINATE of a node by its name. +-- @{#STRATEGO.IsAirbase}(): Check if the TYPE of a node is AIRBASE. +-- @{#STRATEGO.IsPort}(): Check if the TYPE of a node is PORT. +-- @{#STRATEGO.IsPOI}(): Check if the TYPE of a node is POI. +-- @{#STRATEGO.IsFARP}(): Check if the TYPE of a node is FARP. +-- @{#STRATEGO.IsShip}(): Check if the TYPE of a node is SHIP. -- -- -- ## Various -- -- --- @{#STRATEGO.FindNeighborKnots}(): Get neighbor knots of a named knot. --- @{#STRATEGO.FindRoute}(): Find a route between two knots. --- @{#STRATEGO.SetCaptureOptions}(): Set how many units of which minimum threat level are needed to capture one knot (i.e. the underlying OpsZone). +-- @{#STRATEGO.FindNeighborNodes}(): Get neighbor nodes of a named node. +-- @{#STRATEGO.FindRoute}(): Find a route between two nodes. +-- @{#STRATEGO.SetCaptureOptions}(): Set how many units of which minimum threat level are needed to capture one node (i.e. the underlying OpsZone). -- @{#STRATEGO.SetDebug}(): Set debug and draw options. -- -- --- ## Visualisation example code for the Syria map: +-- ## Visualisation example code for the Syria map: -- -- local Bluecher = STRATEGO:New("Bluecher",coalition.side.BLUE,100) -- Bluecher:SetDebug(true,true,true) @@ -175,7 +175,7 @@ STRATEGO = { debug = false, drawzone = false, markzone = false, - version = "0.1.0", + version = "0.2.1", portweight = 3, POIweight = 1, maxrunways = 3, @@ -314,12 +314,12 @@ function STRATEGO:SetDebug(Debug,DrawZones,MarkZones) return self end ---- [USER] Set weights for knots and routes to determine their importance. +--- [USER] Set weights for nodes and routes to determine their importance. -- @param #STRATEGO self --- @param #number MaxRunways Set the maximum number of runways the big (equals strategic) airbases on the map have. Defaults to 3. The weight of an airbase knot hence equals the number of runways. --- @param #number PortWeight Set what weight a port knot has. Defaults to 3. --- @param #number POIWeight Set what weight a POI knot has. Defaults to 1. --- @param #number RouteFactor Defines which weight each route between two defined knots gets: Weight * RouteFactor. +-- @param #number MaxRunways Set the maximum number of runways the big (equals strategic) airbases on the map have. Defaults to 3. The weight of an airbase node hence equals the number of runways. +-- @param #number PortWeight Set what weight a port node has. Defaults to 3. +-- @param #number POIWeight Set what weight a POI node has. Defaults to 1. +-- @param #number RouteFactor Defines which weight each route between two defined nodes gets: Weight * RouteFactor. -- @return #STRATEGO self function STRATEGO:SetWeights(MaxRunways,PortWeight,POIWeight,RouteFactor) self.portweight = PortWeight or 3 @@ -329,7 +329,7 @@ function STRATEGO:SetWeights(MaxRunways,PortWeight,POIWeight,RouteFactor) return self end ---- [USER] Set neutral benefit, i.e. how many points it is cheaper to decide for a neutral vs an enemy knot when taking decisions. +--- [USER] Set neutral benefit, i.e. how many points it is cheaper to decide for a neutral vs an enemy node when taking decisions. -- @param #STRATEGO self -- @param #number NeutralBenefit Pointsm defaults to 100. -- @return #STRATEGO self @@ -338,7 +338,7 @@ function STRATEGO:SetNeutralBenefit(NeutralBenefit) return self end ---- [USER] Set how many units of which minimum threat level are needed to capture one knot (i.e. the underlying OpsZone). +--- [USER] Set how many units of which minimum threat level are needed to capture one node (i.e. the underlying OpsZone). -- @param #STRATEGO self -- @param #number CaptureUnits Number of units needed, defaults to three. -- @param #number CaptureThreatlevel Threat level needed, can be 0..10, defaults to one. @@ -404,11 +404,11 @@ function STRATEGO:AnalyseBases() return self end ---- [INTERNAL] Update knot coalitions +--- [INTERNAL] Update node coalitions -- @param #STRATEGO self -- @return #STRATEGO self -function STRATEGO:UpdateKnotCoalitions() - self:T(self.lid.."UpdateKnotCoalitions") +function STRATEGO:UpdateNodeCoalitions() + self:T(self.lid.."UpdateNodeCoalitions") local newtable = {} for _id,_data in pairs(self.airbasetable) do local data = _data -- #STRATEGO.Data @@ -625,11 +625,11 @@ function STRATEGO:AnalyseUnconnected(Color) return self end ---- [USER] Get a list of the knots with the highest weight. +--- [USER] Get a list of the nodes with the highest weight. -- @param #STRATEGO self --- @return #table Table of knots. --- @return #number Weight The consolidated weight associated with the knots. -function STRATEGO:GetHighestWeightKnots() +-- @return #table Table of nodes. +-- @return #number Weight The consolidated weight associated with the nodes. +function STRATEGO:GetHighestWeightNodes() self:T(self.lid.."GetHighestWeightBases") local weight = 0 local airbases = {} @@ -643,11 +643,11 @@ function STRATEGO:GetHighestWeightKnots() return airbases[weight],weight end ---- [USER] Get a list of the knots a weight less than the give parameter. +--- [USER] Get a list of the nodes a weight less than the give parameter. -- @param #STRATEGO self --- @return #table Table of knots. --- @return #number Weight The consolidated weight associated with the knots. -function STRATEGO:GetNextHighestWeightKnots(Weight) +-- @return #table Table of nodes. +-- @return #number Weight The consolidated weight associated with the nodes. +function STRATEGO:GetNextHighestWeightNodes(Weight) self:T(self.lid.."GetNextHighestWeightBases") local weight = 0 local airbases = {} @@ -661,11 +661,11 @@ function STRATEGO:GetNextHighestWeightKnots(Weight) return airbases[weight],weight end ---- [USER] Get the aggregated weight of a knot by its name. +--- [USER] Get the aggregated weight of a node by its name. -- @param #STRATEGO self -- @param #string Name. -- @return #number Weight The weight or 0 if not found. -function STRATEGO:GetKnotWeight(Name) +function STRATEGO:GetNodeWeight(Name) if Name and self.airbasetable[Name] then return self.airbasetable[Name].weight or 0 else @@ -673,11 +673,11 @@ function STRATEGO:GetKnotWeight(Name) end end ---- [USER] Get the base weight of a knot by its name. +--- [USER] Get the base weight of a node by its name. -- @param #STRATEGO self -- @param #string Name. -- @return #number Weight The base weight or 0 if not found. -function STRATEGO:GetKnotBaseWeight(Name) +function STRATEGO:GetNodeBaseWeight(Name) if Name and self.airbasetable[Name] then return self.airbasetable[Name].baseweight or 0 else @@ -685,11 +685,11 @@ function STRATEGO:GetKnotBaseWeight(Name) end end ---- [USER] Get the COALITION of a knot by its name. +--- [USER] Get the COALITION of a node by its name. -- @param #STRATEGO self -- @param #string Name. -- @return #number Coalition The coalition. -function STRATEGO:GetKnotCoalition(Name) +function STRATEGO:GetNodeCoalition(Name) if Name and self.airbasetable[Name] then return self.airbasetable[Name].coalition or coalition.side.NEUTRAL else @@ -697,11 +697,11 @@ function STRATEGO:GetKnotCoalition(Name) end end ---- [USER] Get the TYPE of a knot by its name. +--- [USER] Get the TYPE of a node by its name. -- @param #STRATEGO self -- @param #string Name. --- @return #string Type Type of the knot, e.g. STRATEGO.Type.AIRBASE or nil if not found. -function STRATEGO:GetKnotType(Name) +-- @return #string Type Type of the node, e.g. STRATEGO.Type.AIRBASE or nil if not found. +function STRATEGO:GetNodeType(Name) if Name and self.airbasetable[Name] then return self.airbasetable[Name].type else @@ -709,11 +709,11 @@ function STRATEGO:GetKnotType(Name) end end ---- [USER] Get the ZONE of a knot by its name. +--- [USER] Get the ZONE of a node by its name. -- @param #STRATEGO self -- @param #string Name. --- @return Core.Zone#ZONE Zone The Zone of the knot or nil if not found. -function STRATEGO:GetKnotZone(Name) +-- @return Core.Zone#ZONE Zone The Zone of the node or nil if not found. +function STRATEGO:GetNodeZone(Name) if Name and self.airbasetable[Name] then return self.airbasetable[Name].zone else @@ -721,11 +721,11 @@ function STRATEGO:GetKnotZone(Name) end end ---- [USER] Get the OPSZONE of a knot by its name. +--- [USER] Get the OPSZONE of a node by its name. -- @param #STRATEGO self -- @param #string Name. --- @return Ops.OpsZone#OPSZONE OpsZone The OpsZone of the knot or nil if not found. -function STRATEGO:GetKnotOpsZone(Name) +-- @return Ops.OpsZone#OPSZONE OpsZone The OpsZone of the node or nil if not found. +function STRATEGO:GetNodeOpsZone(Name) if Name and self.airbasetable[Name] then return self.airbasetable[Name].opszone else @@ -733,11 +733,11 @@ function STRATEGO:GetKnotOpsZone(Name) end end ---- [USER] Get the COORDINATE of a knot by its name. +--- [USER] Get the COORDINATE of a node by its name. -- @param #STRATEGO self -- @param #string Name. --- @return Core.Point#COORDINATE Coordinate The Coordinate of the knot or nil if not found. -function STRATEGO:GetKnotCoordinate(Name) +-- @return Core.Point#COORDINATE Coordinate The Coordinate of the node or nil if not found. +function STRATEGO:GetNodeCoordinate(Name) if Name and self.airbasetable[Name] then return self.airbasetable[Name].coord else @@ -745,7 +745,7 @@ function STRATEGO:GetKnotCoordinate(Name) end end ---- [USER] Check if the TYPE of a knot is AIRBASE. +--- [USER] Check if the TYPE of a node is AIRBASE. -- @param #STRATEGO self -- @param #string Name. -- @return #boolean Outcome @@ -757,7 +757,7 @@ function STRATEGO:IsAirbase(Name) end end ---- [USER] Check if the TYPE of a knot is PORT. +--- [USER] Check if the TYPE of a node is PORT. -- @param #STRATEGO self -- @param #string Name. -- @return #boolean Outcome @@ -769,7 +769,7 @@ function STRATEGO:IsPort(Name) end end ---- [USER] Check if the TYPE of a knot is POI. +--- [USER] Check if the TYPE of a node is POI. -- @param #STRATEGO self -- @param #string Name. -- @return #boolean Outcome @@ -781,7 +781,7 @@ function STRATEGO:IsPOI(Name) end end ---- [USER] Check if the TYPE of a knot is FARP. +--- [USER] Check if the TYPE of a node is FARP. -- @param #STRATEGO self -- @param #string Name. -- @return #boolean Outcome @@ -793,7 +793,7 @@ function STRATEGO:IsFARP(Name) end end ---- [USER] Check if the TYPE of a knot is SHIP. +--- [USER] Check if the TYPE of a node is SHIP. -- @param #STRATEGO self -- @param #string Name. -- @return #boolean Outcome @@ -805,13 +805,13 @@ function STRATEGO:IsShip(Name) end end ---- [USER] Get the next best consolidation target knot with a lower BaseWeight. +--- [USER] Get the next best consolidation target node with a lower BaseWeight. -- @param #STRATEGO self -- @param #string Startpoint Name of start point. --- @param #number BaseWeight Base weight of the knot, i.e. the number of runways of an airbase or the weight of ports or POIs. +-- @param #number BaseWeight Base weight of the node, i.e. the number of runways of an airbase or the weight of ports or POIs. -- @return #number ShortestDist Shortest distance found. --- @return #string Name Name of the target knot. --- @return #number Weight Consolidated weight of the target knot, zero if none found. +-- @return #string Name Name of the target node. +-- @return #number Weight Consolidated weight of the target node, zero if none found. -- @return #number Coalition Coaltion of the target. function STRATEGO:FindClosestConsolidationTarget(Startpoint,BaseWeight) self:T(self.lid.."FindClosestConsolidationTarget for "..Startpoint.." Weight "..BaseWeight or 0) @@ -842,13 +842,13 @@ function STRATEGO:FindClosestConsolidationTarget(Startpoint,BaseWeight) return shortest,target, weight, coa end ---- [USER] Get the next best strategic target knot with same or higher BaseWeight. +--- [USER] Get the next best strategic target node with same or higher BaseWeight. -- @param #STRATEGO self -- @param #string Startpoint Name of start point. --- @param #number BaseWeight Base weight of the knot, e.g. the number of runways of an airbase or the weight of ports or POIs. +-- @param #number BaseWeight Base weight of the node, e.g. the number of runways of an airbase or the weight of ports or POIs. -- @return #number ShortestDist Shortest distance found. --- @return #string Name Name of the target knot. --- @return #number Weight Consolidated weight of the target knot, zero if none found. +-- @return #string Name Name of the target node. +-- @return #number Weight Consolidated weight of the target node, zero if none found. -- @return #number Coalition Coaltion of the target. function STRATEGO:FindClosestStrategicTarget(Startpoint,BaseWeight) self:T(self.lid.."FindClosestStrategicTarget") @@ -876,7 +876,7 @@ function STRATEGO:FindClosestStrategicTarget(Startpoint,BaseWeight) return shortest,target,weight, coa end ---- [USER] Get the next best strategic target knots in the network. +--- [USER] Get the next best strategic target nodes in the network. -- @param #STRATEGO self -- @return #table of #STRATEGO.Target data points function STRATEGO:FindStrategicTargets() @@ -919,7 +919,7 @@ function STRATEGO:FindStrategicTargets() return targets end ---- [USER] Get the next best consolidation target knots in the network. +--- [USER] Get the next best consolidation target nodes in the network. -- @param #STRATEGO self -- @return #table of #STRATEGO.Target data points function STRATEGO:FindConsolidationTargets() @@ -964,13 +964,13 @@ function STRATEGO:FindConsolidationTargets() return targets end ---- [USER] Get neighbor knots of a named knot. +--- [USER] Get neighbor nodes of a named node. -- @param #STRATEGO self -- @param #string Name The name to search the neighbors for. -- @param #boolean Enemies (optional) If true, find only enemy neighbors. -- @param #boolean Friends (optional) If true, find only friendly or neutral neighbors. --- @return #table Neighbors Table of #STRATEGO.DistData entries indexed by neighbor knot names. -function STRATEGO:FindNeighborKnots(Name,Enemies,Friends) +-- @return #table Neighbors Table of #STRATEGO.DistData entries indexed by neighbor node names. +function STRATEGO:FindNeighborNodes(Name,Enemies,Friends) local neighbors = {} local name = string.gsub(Name,"[%p%s]",".") for _route,_data in pairs(self.disttable) do @@ -996,10 +996,10 @@ function STRATEGO:FindNeighborKnots(Name,Enemies,Friends) return neighbors end ---- [USER] Find a route between two knots. +--- [USER] Find a route between two nodes. -- @param #STRATEGO self --- @param #string Start The name of the start knot. --- @param #string End The name of the end knot. +-- @param #string Start The name of the start node. +-- @param #string End The name of the end node. -- @param #number Hops Max iterations to find a route. -- @param #boolean Draw If true, draw the route on the map. -- @return #table Route Table of #string name entries of the route @@ -1023,10 +1023,10 @@ function STRATEGO:FindRoute(Start,End,Hops,Draw) local function NextClosest(Start,End) local ecoord = self.airbasetable[End].coord - local knots = self:FindNeighborKnots(Start) + local nodes = self:FindNeighborNodes(Start) local closest = nil local closedist = 1000*1000 - for _name,_dist in pairs(knots) do + for _name,_dist in pairs(nodes) do local kcoord = self.airbasetable[_name].coord local dist = math.floor((kcoord:Get2DDistance(ecoord)/1000)+0.5) if dist < closedist then @@ -1052,8 +1052,8 @@ function STRATEGO:FindRoute(Start,End,Hops,Draw) -- One hop Route[#Route+1] = Start - local knots = self:FindNeighborKnots(Start) - local endpoint = Checker(knots) + local nodes = self:FindNeighborNodes(Start) + local endpoint = Checker(nodes) if endpoint then Route[#Route+1] = endpoint @@ -1064,8 +1064,8 @@ function STRATEGO:FindRoute(Start,End,Hops,Draw) local Next = NextClosest(spoint,End) if Next then Route[#Route+1] = Next - local knots = self:FindNeighborKnots(Next) - local endpoint = Checker(knots) + local nodes = self:FindNeighborNodes(Next) + local endpoint = Checker(nodes) if endpoint then Route[#Route+1] = endpoint routecomplete = true From 96da09b9e05c97c27c575e80acc3479aa475a957 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 4 Jan 2024 12:44:43 +0100 Subject: [PATCH 14/17] Picture link --- Moose Development/Moose/Functional/Stratego.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Functional/Stratego.lua b/Moose Development/Moose/Functional/Stratego.lua index 2f3f272a0..af7eb3d6f 100644 --- a/Moose Development/Moose/Functional/Stratego.lua +++ b/Moose Development/Moose/Functional/Stratego.lua @@ -14,7 +14,7 @@ -- ### Author: **applevangelist** -- -- @module Functional.Stratego --- @image Stratego.png +-- @image Functional.Stratego.png --- From d0346c4aa3891c6d351a5c3cc269d5fd20b50cea Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 4 Jan 2024 13:59:41 +0100 Subject: [PATCH 15/17] MSRS line 953 has no self --- Moose Development/Moose/Sound/SRS.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index b8604f36c..559d23c56 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -950,7 +950,7 @@ end -- @param #string Region Region to use. -- @return #MSRS.ProviderOptions Provider optionas table. function MSRS._CreateProviderOptions(Provider, CredentialsFile, AccessKey, SecretKey, Region) - self:F( {Provider, CredentialsFile, AccessKey, SecretKey, Region} ) + BASE:F( {Provider, CredentialsFile, AccessKey, SecretKey, Region} ) local option={} --#MSRS.ProviderOptions option.provider=Provider From 65fb22b6cc1c8d121d7a820f66ac2ed0b5b8f9d3 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 4 Jan 2024 14:00:43 +0100 Subject: [PATCH 16/17] xxx --- Moose Development/Moose/Ops/PlayerTask.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 6e1e1079e..87b9921cb 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -45,8 +45,8 @@ do -- @field Wrapper.Marker#MARKER TargetMarker -- @field #number SmokeColor -- @field #number FlareColor --- @field #table conditionSuccess = {}, --- @field #table conditionFailure = {}, +-- @field #table conditionSuccess +-- @field #table conditionFailure -- @field Ops.PlayerTask#PLAYERTASKCONTROLLER TaskController -- @field #number timestamp -- @field #number lastsmoketime From f5d6d31b105ba3a7271f22af2288ca72124b0b27 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 4 Jan 2024 14:01:12 +0100 Subject: [PATCH 17/17] xxx --- Moose Development/Moose/Sound/SRS.lua | 119 +++++++++++++------------- 1 file changed, 60 insertions(+), 59 deletions(-) diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index c50e7ee76..559d23c56 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -109,10 +109,10 @@ -- -- Use a specific voice with the @{#MSRS.SetVoice} function, e.g, `:SetVoice("Microsoft Hedda Desktop")`. -- Note that this must be installed on your windows system. --- +-- -- Note that you can set voices for each provider via the @{#MSRS.SetVoiceProvider} function. Also shortcuts are available, *i.e.* -- @{#MSRS.SetVoiceWindows}, @{#MSRS.SetVoiceGoogle}, @{#MSRS.SetVoiceAzure} and @{#MSRS.SetVoiceAmazon}. --- +-- -- For voices there are enumerators in this class to help you out on voice names: -- -- MSRS.Voices.Microsoft -- e.g. MSRS.Voices.Microsoft.Hedda - the Microsoft enumerator contains all voices known to work with SRS @@ -134,32 +134,32 @@ -- ## Config file for many variables, auto-loaded by Moose -- -- See @{#MSRS.LoadConfigFile} for details on how to set this up. --- +-- -- ## TTS Providers --- +-- -- The default provider for generating speech from text is the native Windows TTS service. Note that you need to install the voices you want to use. -- -- **Pro-Tip** - use the command line with power shell to call `DCS-SR-ExternalAudio.exe` - it will tell you what is missing -- and also the Google Console error, in case you have missed a step in setting up your Google TTS. -- For example, `.\DCS-SR-ExternalAudio.exe -t "Text Message" -f 255 -m AM -c 2 -s 2 -z -G "Path_To_You_Google.Json"` -- plays a message on 255 MHz AM for the blue coalition in-game. --- +-- -- ### Google -- -- In order to use Google Cloud for TTS you need to use @{#MSRS.SetProvider} and @{#MSRS.SetProviderOptionsGoogle} functions: -- -- msrs:SetProvider(MSRS.Provider.GOOGLE) -- msrs:SetProviderOptionsGoogle(CredentialsFile, AccessKey) --- +-- -- The parameter `CredentialsFile` is used with the default 'DCS-SR-ExternalAudio.exe' backend and must be the full path to the credentials JSON file. -- The `AccessKey` parameter is used with the DCS-gRPC backend (see below). --- +-- -- You can set the voice to use with Google via @{#MSRS.SetVoiceGoogle}. --- +-- -- When using Google it also allows you to utilize SSML in your text for more flexibility. -- 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 --- +-- -- ### Amazon Web Service [Only DCS-gRPC backend] -- -- In order to use Amazon Web Service (AWS) for TTS you need to use @{#MSRS.SetProvider} and @{#MSRS.SetProviderOptionsAmazon} functions: @@ -177,16 +177,16 @@ -- -- msrs:SetProvider(MSRS.Provider.AZURE) -- msrs:SetProviderOptionsAmazon(AccessKey, Region) --- +-- -- The parameter `AccessKey` is your Azure access key. The parameter `Region` is your [Azure region](https://learn.microsoft.com/en-us/azure/cognitive-services/speech-service/regions). --- +-- -- You can set the voice to use with Azure via @{#MSRS.SetVoiceAzure}. --- +-- -- ## Backend --- +-- -- The default interface to SRS is via calling the 'DCS-SR-ExternalAudio.exe'. As noted above, this has the unavoidable drawback that a pop-up briefly appears -- and DCS might be put out of focus. --- +-- -- ## DCS-gRPC as an alternative to 'DCS-SR-ExternalAudio.exe' for TTS -- -- Another interface to SRS is [DCS-gRPC](https://github.com/DCS-gRPC/rust-server). This does not call an exe file and therefore avoids the annoying pop-up window. @@ -776,7 +776,7 @@ end function MSRS:SetVoiceProvider(Voice, Provider) self:F( {Voice=Voice, Provider=Provider} ) self.poptions=self.poptions or {} - + self.poptions[Provider or self:GetProvider()]=Voice return self @@ -834,7 +834,7 @@ end function MSRS:GetVoice(Provider) Provider=Provider or self.provider - + if Provider and self.poptions[Provider] and self.poptions[Provider].voice then return self.poptions[Provider].voice else @@ -863,7 +863,7 @@ function MSRS:SetGoogle(PathToCredentials) if PathToCredentials then self.provider = MSRS.Provider.GOOGLE - + self:SetProviderOptionsGoogle(PathToCredentials, PathToCredentials) end @@ -878,15 +878,15 @@ end function MSRS:SetGoogleAPIKey(APIKey) self:F( {APIKey=APIKey} ) if APIKey then - + self.provider = MSRS.Provider.GOOGLE - + if self.poptions[MSRS.Provider.GOOGLE] then self.poptions[MSRS.Provider.GOOGLE].key=APIKey else self:SetProviderOptionsGoogle(nil ,APIKey) end - + end return self end @@ -894,14 +894,14 @@ end --- Set provider used to generate text-to-speech. -- These options are available: --- +-- -- - `MSRS.Provider.WINDOWS`: Microsoft Windows (default) -- - `MSRS.Provider.GOOGLE`: Google Cloud -- - `MSRS.Provider.AZURE`: Microsoft Azure (only with DCS-gRPC backend) -- - `MSRS.Provier.AMAZON`: Amazone Web Service (only with DCS-gRPC backend) --- +-- -- Note that all providers except Microsoft Windows need as additonal information the credentials of your account. --- +-- -- @param #MSRS self -- @param #string Provider -- @return #MSRS self @@ -950,7 +950,7 @@ end -- @param #string Region Region to use. -- @return #MSRS.ProviderOptions Provider optionas table. function MSRS._CreateProviderOptions(Provider, CredentialsFile, AccessKey, SecretKey, Region) - self:F( {Provider, CredentialsFile, AccessKey, SecretKey, Region} ) + BASE:F( {Provider, CredentialsFile, AccessKey, SecretKey, Region} ) local option={} --#MSRS.ProviderOptions option.provider=Provider @@ -1124,20 +1124,20 @@ function MSRS:PlaySoundText(SoundText, Delay) if Delay and Delay>0 then self:ScheduleOnce(Delay, MSRS.PlaySoundText, self, SoundText, 0) else - + if self.backend==MSRS.Backend.GRPC then self:_DCSgRPCtts(SoundText.text, nil, SoundText.gender, SoundText.culture, SoundText.voice, SoundText.volume, SoundText.label, SoundText.coordinate) else -- Get command. local command=self:_GetCommand(nil, nil, nil, SoundText.gender, SoundText.voice, SoundText.culture, SoundText.volume, SoundText.speed) - + -- Append text. command=command..string.format(" --text=\"%s\"", tostring(SoundText.text)) - + -- Execute command. self:_ExecCommand(command) - + end end @@ -1189,25 +1189,25 @@ function MSRS:PlayTextExt(Text, Delay, Frequencies, Modulations, Gender, Culture if Delay and Delay>0 then self:ScheduleOnce(Delay, MSRS.PlayTextExt, self, Text, 0, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label, Coordinate) else - + Frequencies = Frequencies or self:GetFrequencies() Modulations = Modulations or self:GetModulations() - + if self.backend==MSRS.Backend.SRSEXE then -- Get command line. local command=self:_GetCommand(UTILS.EnsureTable(Frequencies, false), UTILS.EnsureTable(Modulations, false), nil, Gender, Voice, Culture, Volume, nil, nil, Label, Coordinate) - + -- Append text. command=command..string.format(" --text=\"%s\"", tostring(Text)) - + -- Execute command. self:_ExecCommand(command) - + elseif self.backend==MSRS.Backend.GRPC then - + self:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Label, Coordinate) - + end end @@ -1264,8 +1264,8 @@ end --- Get lat, long and alt from coordinate. -- @param #MSRS self -- @param Core.Point#Coordinate Coordinate Coordinate. Can also be a DCS#Vec3. --- @return #number Latitude (or 0 if no input coordinate was given). --- @return #number Longitude (or 0 if no input coordinate was given). +-- @return #number Latitude (or 0 if no input coordinate was given). +-- @return #number Longitude (or 0 if no input coordinate was given). -- @return #number Altitude (or 0 if no input coordinate was given). function MSRS:_GetLatLongAlt(Coordinate) self:F( {Coordinate=Coordinate} ) @@ -1349,7 +1349,7 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp -- Set provider options if self.provider==MSRS.Provider.GOOGLE then - local pops=self:GetProviderOptions() + local pops=self:GetProviderOptions() command=command..string.format(' --ssml -G "%s"', pops.credentials) elseif self.provider==MSRS.Provider.WINDOWS then -- Nothing to do. @@ -1512,15 +1512,15 @@ function MSRS:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Lab self:F({Text, Frequencies, Gender, Culture, Voice, Volume, Label, Coordinate}) local options = {} -- #MSRS.GRPCOptions - + local ssml = Text or '' - + -- Get frequenceies. Frequencies = UTILS.EnsureTable(Frequencies, true) or self:GetFrequencies() - + -- Plain text (not really used. options.plaintext=Text - + -- Name shows as sender. options.srsClientName = Label or self.Label @@ -1528,8 +1528,8 @@ function MSRS:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Lab if self.coordinate then options.position = {} options.position.lat, options.position.lon, options.position.alt = self:_GetLatLongAlt(self.coordinate) - end - + end + -- Coalition (gRPC expects lower case) options.coalition = UTILS.GetCoalitionName(self.coalition):lower() @@ -1540,7 +1540,7 @@ function MSRS:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Lab -- Provider options: voice, credentials options.provider = {} options.provider[provider] = self:GetProviderOptions(provider) - + -- Voice Voice=Voice or self:GetVoice(self.provider) or self.voice @@ -1548,10 +1548,11 @@ function MSRS:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Lab -- We use a specific voice options.provider[provider].voice = Voice else - -- DCS-gRPC doesn't directly support language/gender, but can use SSML - - local preTag, genderProp, langProp, postTag = '', '', '', '' + -- DCS-gRPC doesn't directly support language/gender, but can use SSML + + local preTag, genderProp, langProp, postTag = '', '', '', '' + local gender="" if self.gender then gender=string.format(' gender=\"%s\"', self.gender) @@ -1565,7 +1566,7 @@ function MSRS:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Lab ssml=string.format("%s", gender, language, Text) end end - + for _,freq in pairs(Frequencies) do self:F("Calling GRPC.tts with the following parameter:") self:F({ssml=ssml, freq=freq, options=options}) @@ -1609,7 +1610,7 @@ end -- -- Windows -- win = { -- voice = "Microsoft Hazel Desktop", --- }, +-- }, -- -- Google Cloud -- gcloud = { -- voice = "en-GB-Standard-A", -- The Google Cloud voice to use (see https://cloud.google.com/text-to-speech/docs/voices). @@ -1625,7 +1626,7 @@ end -- }, -- -- Microsoft Azure -- azure = { --- voice="en-US-AriaNeural", --The default Azure voice to use (see https://learn.microsoft.com/azure/cognitive-services/speech-service/language-support). +-- voice="en-US-AriaNeural", --The default Azure voice to use (see https://learn.microsoft.com/azure/cognitive-services/speech-service/language-support). -- key="Your access key", -- Your Azure access key. -- region="westeurope", -- The Azure region to use (see https://learn.microsoft.com/en-us/azure/cognitive-services/speech-service/regions). -- }, @@ -1672,16 +1673,16 @@ function MSRS:LoadConfigFile(Path,Filename) local filexsists = UTILS.FileExists(pathandfile) if filexsists and not MSRS.ConfigLoaded then - + env.info("FF reading config file") - + -- Load global MSRS_Config assert(loadfile(path..file))() - + if MSRS_Config then - + local Self = self or MSRS --#MSRS - + Self.path = MSRS_Config.Path or "C:\\Program Files\\DCS-SimpleRadio-Standalone" Self.port = MSRS_Config.Port or 5002 Self.backend = MSRS_Config.Backend or MSRS.Backend.SRSEXE @@ -1702,13 +1703,13 @@ function MSRS:LoadConfigFile(Path,Filename) Self.poptions[provider]=MSRS_Config[provider] end end - + Self.ConfigLoaded = true - + end env.info("MSRS - Successfully loaded default configuration from disk!",false) end - + if not filexsists then env.info("MSRS - Cannot find default configuration file!",false) return false