From 85c73cb0a59739c0bcaef7eae8fbbbffccf1c979 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 25 Nov 2023 18:28:59 +0100 Subject: [PATCH 01/38] #SPAWN *Link16 fixes * Wrongly created STN's will be replaced with random five digit octals with leading 0 * Voice call sign label will be the callsign's first and last letters, e.g. Enfield = ED. Navy One = NY * Voice call sign number equals callsign minor major, e.g. Enfield 6-1 = ED 61 * Also works for A10CII which has a different entry with a four-digit octal with leading 0 * for fighter aircraft you can use :InitRandomizeCallsign() to give each spawn a random callsign --- Moose Development/Moose/Core/Spawn.lua | 106 ++++++++++++++++++-- Moose Development/Moose/Utilities/Enums.lua | 9 +- Moose Development/Moose/Utilities/Utils.lua | 40 +++++++- 3 files changed, 142 insertions(+), 13 deletions(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 62a24cf8c..6ae5e1bc1 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -320,7 +320,7 @@ function SPAWN:New( SpawnTemplatePrefix ) self.AIOnOff = true -- The AI is on by default when spawning a group. self.SpawnUnControlled = false self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name. - self.DelayOnOff = false -- No intial delay when spawning the first group. + self.DelayOnOff = false -- No initial delay when spawning the first group. self.SpawnGrouping = nil -- No grouping. self.SpawnInitLivery = nil -- No special livery. self.SpawnInitSkill = nil -- No special skill. @@ -332,6 +332,7 @@ function SPAWN:New( SpawnTemplatePrefix ) self.SpawnInitModexPostfix = nil self.SpawnInitAirbase = nil self.TweakedTemplate = false -- Check if the user is using self made template. + self.SpawnRandomCallsign = false self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned. else @@ -1099,6 +1100,14 @@ function SPAWN:InitRandomizeZones( SpawnZoneTable ) return self end +--- [AIR/Fighter only!] This method randomizes the callsign for a new group. +-- @param #SPAWN self +-- @return #SPAWN self +function SPAWN:InitRandomizeCallsign() + self.SpawnRandomCallsign = true + return self +end + --- This method sets a spawn position for the group that is different from the location of the template. -- @param #SPAWN self -- @param Core.Point#COORDINATE Coordinate The position to spawn from @@ -3275,10 +3284,58 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 end -- Callsign + + if self.SpawnRandomCallsign and SpawnTemplate.units[1].callsign then + if type( SpawnTemplate.units[1].callsign ) ~= "number" then + -- change callsign + local min = 1 + local max = 8 + local ctable = CALLSIGN.Aircraft + if string.find(SpawnTemplate.units[1].type, "A-10",1,true) then + max = 12 + end + if string.find(SpawnTemplate.units[1].type, "18",1,true) then + min = 9 + max = 20 + ctable = CALLSIGN.F18 + end + if string.find(SpawnTemplate.units[1].type, "16",1,true) then + min = 9 + max = 20 + ctable = CALLSIGN.F16 + end + if SpawnTemplate.units[1].type == "F-15E" then + min = 9 + max = 18 + ctable = CALLSIGN.F15E + end + local callsignnr = math.random(min,max) + local callsignname = "Enfield" + for name, value in pairs(ctable) do + if value==callsignnr then + callsignname = name + end + end + for UnitID = 1, #SpawnTemplate.units do + SpawnTemplate.units[UnitID].callsign[1] = callsignnr + SpawnTemplate.units[UnitID].callsign[2] = UnitID + SpawnTemplate.units[UnitID].callsign[3] = "1" + SpawnTemplate.units[UnitID].callsign["name"] = tostring(callsignname)..tostring(UnitID).."1" + UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].callsign,1) + end + else + -- Ruskis + for UnitID = 1, #SpawnTemplate.units do + SpawnTemplate.units[UnitID].callsign = math.random(1,999) + end + end + end + for UnitID = 1, #SpawnTemplate.units do local Callsign = SpawnTemplate.units[UnitID].callsign if Callsign then if type( Callsign ) ~= "number" then -- blue callsign + UTILS.PrintTableToLog(Callsign,1) Callsign[2] = ((SpawnIndex - 1) % 10) + 1 local CallsignName = SpawnTemplate.units[UnitID].callsign["name"] -- #string CallsignName = string.match(CallsignName,"^(%a+)") -- 2.8 - only the part w/o numbers @@ -3293,21 +3350,56 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 local AddProps = SpawnTemplate.units[UnitID].AddPropAircraft if AddProps then if SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 then - SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 = SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16+UnitID-1 - if SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 < 10000 then - SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 = string.format("0%d",SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16) + -- 4 digit octal with leading 0 + if tonumber(SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16) ~= nil then + local octal = SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 + local decimal = UTILS.OctalToDecimal(octal)+UnitID-1 + SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 = string.format("%05d",UTILS.DecimalToOctal(decimal)) + else -- ED bug - chars in here + local STN = math.floor(UTILS.RandomGaussian(4088/2,nil,1000,4088)) + STN = STN+UnitID-1 + local OSTN = UTILS.DecimalToOctal(STN) + SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 = string.format("%05d",OSTN) + end + end + -- A10CII + if SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN then + -- 3 digit octal with leading 0 + if tonumber(SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN) ~= nil then + local octal = SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN + local decimal = UTILS.OctalToDecimal(octal)+UnitID-1 + SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN = string.format("%04d",UTILS.DecimalToOctal(decimal)) + else -- ED bug - chars in here + local STN = math.floor(UTILS.RandomGaussian(504/2,nil,100,504)) + STN = STN+UnitID-1 + local OSTN = UTILS.DecimalToOctal(STN) + SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN = string.format("%04d",OSTN) end end -- VoiceCallsignNumber if SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignNumber then - SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignNumber = SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignNumber+UnitID-1 + SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignNumber = SpawnTemplate.units[UnitID].callsign[2] .. SpawnTemplate.units[UnitID].callsign[3] end - --UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].AddPropAircraft,1) + -- VoiceCallsignLabel + if SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignLabel then + local CallsignName = SpawnTemplate.units[UnitID].callsign["name"] -- #string + CallsignName = string.match(CallsignName,"^(%a+)") -- 2.8 - only the part w/o numbers + local label = "NY" -- Navy One exception + if not string.find(CallsignName," ") then + label = string.upper(string.match(CallsignName,"^%a")..string.match(CallsignName,"%a$")) + end + SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignLabel = label + end + UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].AddPropAircraft,1) -- FlightLead if SpawnTemplate.units[UnitID].datalinks and SpawnTemplate.units[UnitID].datalinks.Link16 and SpawnTemplate.units[UnitID].datalinks.Link16.settings then SpawnTemplate.units[UnitID].datalinks.Link16.settings.flightLead = UnitID == 1 and true or false end - --UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].datalinks,1) + -- A10CII + if SpawnTemplate.units[UnitID].datalinks and SpawnTemplate.units[UnitID].datalinks.SADL and SpawnTemplate.units[UnitID].datalinks.SADL.settings then + SpawnTemplate.units[UnitID].datalinks.SADL.settings.flightLead = UnitID == 1 and true or false + end + UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].datalinks,1) end end diff --git a/Moose Development/Moose/Utilities/Enums.lua b/Moose Development/Moose/Utilities/Enums.lua index 9c8ad5150..beab1f0fb 100644 --- a/Moose Development/Moose/Utilities/Enums.lua +++ b/Moose Development/Moose/Utilities/Enums.lua @@ -29,7 +29,6 @@ ENUMS = {} --- Suppress the error box env.setErrorMessageBoxEnabled( false ) - --- Rules of Engagement. -- @type ENUMS.ROE -- @field #number WeaponFree [AIR] AI will engage any enemy group it detects. Target prioritization is based based on the threat of the target. @@ -567,6 +566,14 @@ ENUMS.ReportingName = } } +--- Enums for Link16 transmit power +-- @type ENUMS.Link16Power +ENUMS.Link16Power = { + none = 0, + low = 1, + medium = 2, + high = 3, +} --- Enums for the STORAGE class for stores - which need to be in "" diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 28b608d51..f8fbfe2eb 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -441,19 +441,22 @@ UTILS.BasicSerialize = function(s) end end +--- Print a table to log in a nice format +-- @param #table table The table to print +-- @param #number indent Number of idents function UTILS.PrintTableToLog(table, indent) if not table then - BASE:E("No table passed!") + env.warning("No table passed!") return end if not indent then indent = 0 end for k, v in pairs(table) do if type(v) == "table" then - BASE:I(string.rep(" ", indent) .. tostring(k) .. " = {") + env.info(string.rep(" ", indent) .. tostring(k) .. " = {") UTILS.PrintTableToLog(v, indent + 1) - BASE:I(string.rep(" ", indent) .. "}") + env.info(string.rep(" ", indent) .. "}") else - BASE:I(string.rep(" ", indent) .. tostring(k) .. " = " .. tostring(v)) + env.info(string.rep(" ", indent) .. tostring(k) .. " = " .. tostring(v)) end end end @@ -3325,7 +3328,7 @@ function UTILS.GetZoneProperties(zone_name) for _, property in pairs(zone["properties"]) do return_table[property["key"]] = property["value"] end - return return_table + return return_table else BASE:I(string.format("%s doesn't have any properties", zone_name)) return {} @@ -3599,3 +3602,30 @@ function table.find_key_value_pair(tbl, key, value) return nil end +--- Convert a decimal to octal +-- @param #number Number the number to convert +-- @return #number Octal +function UTILS.DecimalToOctal(Number) + if Number < 8 then return Number end + local number = tonumber(Number) + local octal = "" + local n=1 + while number > 7 do + local number1 = number%8 + octal = string.format("%d",number1)..octal + local number2 = math.abs(number/8) + if number2 < 8 then + octal = string.format("%d",number2)..octal + end + number = number2 + n=n+1 + end + return tonumber(octal) +end + +--- Convert an octal to decimal +-- @param #number Number the number to convert +-- @return #number Decimal +function UTILS.OctalToDecimal(Number) + return tonumber(Number,8) +end From 7c8f212b03f828df2f095283fb0cfd5ec6028b01 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 25 Nov 2023 18:44:21 +0100 Subject: [PATCH 02/38] -- noise --- Moose Development/Moose/Core/Spawn.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 6ae5e1bc1..e562bfbf5 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -3321,7 +3321,7 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 SpawnTemplate.units[UnitID].callsign[2] = UnitID SpawnTemplate.units[UnitID].callsign[3] = "1" SpawnTemplate.units[UnitID].callsign["name"] = tostring(callsignname)..tostring(UnitID).."1" - UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].callsign,1) + -- UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].callsign,1) end else -- Ruskis @@ -3335,7 +3335,7 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 local Callsign = SpawnTemplate.units[UnitID].callsign if Callsign then if type( Callsign ) ~= "number" then -- blue callsign - UTILS.PrintTableToLog(Callsign,1) + --UTILS.PrintTableToLog(Callsign,1) Callsign[2] = ((SpawnIndex - 1) % 10) + 1 local CallsignName = SpawnTemplate.units[UnitID].callsign["name"] -- #string CallsignName = string.match(CallsignName,"^(%a+)") -- 2.8 - only the part w/o numbers @@ -3390,7 +3390,7 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 end SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignLabel = label end - UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].AddPropAircraft,1) + -- UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].AddPropAircraft,1) -- FlightLead if SpawnTemplate.units[UnitID].datalinks and SpawnTemplate.units[UnitID].datalinks.Link16 and SpawnTemplate.units[UnitID].datalinks.Link16.settings then SpawnTemplate.units[UnitID].datalinks.Link16.settings.flightLead = UnitID == 1 and true or false @@ -3399,7 +3399,7 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 if SpawnTemplate.units[UnitID].datalinks and SpawnTemplate.units[UnitID].datalinks.SADL and SpawnTemplate.units[UnitID].datalinks.SADL.settings then SpawnTemplate.units[UnitID].datalinks.SADL.settings.flightLead = UnitID == 1 and true or false end - UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].datalinks,1) + --UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].datalinks,1) end end From 641707f37b670ed53a20f02ea227d8f2c9f80fae Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 26 Nov 2023 16:59:44 +0100 Subject: [PATCH 03/38] #UNIT * Added `GetSTN()` to obtain Link16 info from a unit --- Moose Development/Moose/Wrapper/Unit.lua | 33 ++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 7ad0998ac..0b3e3300f 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -1659,3 +1659,36 @@ function UNIT:GetSkill() local skill = _DATABASE.Templates.Units[name].Template.skill or "Random" return skill end + +--- Get Link16 STN or SADL TN and other datalink info from Unit, if any. +-- @param #UNIT self +-- @return #string STN STN or TN Octal as string, or nil if not set/capable. +-- @return #string VCL Voice Callsign Label or nil if not set/capable. +-- @return #string VCN Voice Callsign Number or nil if not set/capable. +-- @return #string Lead If true, unit is Flight Lead, else false or nil. +function UNIT:GetSTN() + self:F2(self.UnitName) + local STN = nil -- STN/TN + local VCL = nil -- VoiceCallsignLabel + local VCN = nil -- VoiceCallsignNumber + local FGL = false -- FlightGroupLeader + local template = self:GetTemplate() + if template.AddPropAircraft then + if template.AddPropAircraft.STN_L16 then + STN = template.AddPropAircraft.STN_L16 + elseif template.AddPropAircraft.SADL_TN then + STN = template.AddPropAircraft.SADL_TN + end + VCN = template.AddPropAircraft.VoiceCallsignNumber + VCL = template.AddPropAircraft.VoiceCallsignLabel + end + if template.datalinks and template.datalinks.Link16 and template.datalinks.Link16.settings then + FGL = template.datalinks.Link16.settings.flightLead + end + -- A10CII + if template.datalinks and template.datalinks.SADL and template.datalinks.SADL.settings then + FGL = template.datalinks.SADL.settings.flightLead + end + + return STN, VCL, VCN, FGL +end From c489a881061eef407eec48643f691aefa8a4beb5 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 27 Nov 2023 16:49:06 +0100 Subject: [PATCH 04/38] #GROUP * Get Link16 S/TN data from a group --- Moose Development/Moose/Wrapper/Group.lua | 36 ++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index f3d8ccc53..9ee2d2c21 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -2922,7 +2922,7 @@ function GROUP:GetCustomCallSign(ShortCallsign,Keepnumber,CallsignTranslations) return callsign end ---- +--- Set a GROUP to act as recovery tanker -- @param #GROUP self -- @param Wrapper.Group#GROUP CarrierGroup. -- @param #number Speed Speed in knots. @@ -2948,3 +2948,37 @@ function GROUP:SetAsRecoveryTanker(CarrierGroup,Speed,ToKIAS,Altitude,Delay,Last return self end + +--- Get a list of Link16 S/TN data from a GROUP. Can (as of Nov 2023) be obtained from F-18, F-16, F-15E (not the user flyable one) and A-10C-II groups. +-- @param #GROUP self +-- @return #table Table of data entries, indexed by unit name, each entry is a table containing STN, VCL (voice call label), VCN (voice call number), and Lead (#boolean, if true it's the flight lead) +-- @return #string Report Formatted report of all data +function GROUP:GetGroupSTN() + local tSTN = {} -- table + local units = self:GetUnits() + local gname = self:GetName() + gname = string.gsub(gname,"(#%d+)$","") + local report = REPORT:New() + report:Add("Link16 S/TN Report") + report:Add("Group: "..gname) + report:Add("==================") + for _,_unit in pairs(units) do + local unit = _unit -- Wrapper.Unit#UNIT + if unit and unit:IsAlive() then + local STN, VCL, VCN, Lead = unit:GetSTN() + local name = unit:GetName() + tSTN[name] = { + STN=STN, + VCL=VCL, + VCN=VCN, + Lead=Lead, + } + local lead = Lead == true and "(*)" or "" + report:Add(string.format("| %s%s %s %s",tostring(VCL),tostring(VCN),tostring(STN),lead)) + end + end + report:Add("==================") + local text = report:Text() + return tSTN,text +end + From 4b8d120f20b4b695a32595b6aa26f03753a3f1e1 Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 30 Nov 2023 23:29:29 +0100 Subject: [PATCH 05/38] Update Warehouse.lua - Added check that DCS warehouse has enough air assets for selfpropelled assets --- .../Moose/Functional/Warehouse.lua | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index beb60200f..7ac6e2839 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -7404,6 +7404,8 @@ function WAREHOUSE:_CheckRequestNow(request) -- Check if at least one (cargo) asset is available. if _nassets>0 then + + local asset=_assets[1] --#WAREHOUSE.Assetitem -- Get the attibute of the requested asset. _assetattribute=_assets[1].attribute @@ -7414,11 +7416,24 @@ function WAREHOUSE:_CheckRequestNow(request) if _assetcategory==Group.Category.AIRPLANE or _assetcategory==Group.Category.HELICOPTER then if self.airbase and self.airbase:GetCoalition()==self:GetCoalition() then + + -- Check if DCS warehouse of airbase has enough assets + if self.airbase.storage then + local nS=self.airbase.storage:GetAmount(asset.unittype) + local nA=asset.nunits*request.nasset -- Number of units requested + if nS NOT enough to spawn the requested %d asset units (%d groups)", + self.alias, nS, asset.unittype, nA, request.nasset) + self:_InfoMessage(text, 5) + return false + end + end + if self:IsRunwayOperational() or _assetairstart then if _assetairstart then - -- Airstart no need to check parking + -- Airstart no need to check parking else -- Check parking. @@ -7530,6 +7545,9 @@ function WAREHOUSE:_CheckRequestNow(request) self:_InfoMessage(text, 5) return false end + + elseif _assetcategory==Group.Category.AIRPLANE or _assetcategory==Group.Category.HELICOPTER then + end From ae604fd847439b32de420cb26b8fa434f5c04823 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 2 Dec 2023 14:45:42 +0100 Subject: [PATCH 06/38] #AIRBASE * Add'l Normandy Airfields --- Moose Development/Moose/Wrapper/Airbase.lua | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index b877fa004..76da83c94 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -239,6 +239,13 @@ AIRBASE.Nevada = { -- * AIRBASE.Normandy.Broglie -- * AIRBASE.Normandy.Bernay_Saint_Martin -- * AIRBASE.Normandy.Saint_Andre_de_lEure +-- * AIRBASE.Normandy.Biggin_Hill +-- * AIRBASE.Normandy.Manston +-- * AIRBASE.Normandy.Detling +-- * AIRBASE.Normandy.Lympne +-- * AIRBASE.Normandy.Abbeville_Drucat +-- * AIRBASE.Normandy.Merville_Calonne +-- * AIRBASE.Normandy.Saint_Omer_Wizernes -- -- @field Normandy AIRBASE.Normandy = { @@ -311,7 +318,14 @@ AIRBASE.Normandy = { ["Beaumont_le_Roger"] = "Beaumont-le-Roger", ["Broglie"] = "Broglie", ["Bernay_Saint_Martin"] = "Bernay Saint Martin", - ["Saint_Andre_de_lEure"] = "Saint-Andre-de-lEure", + ["Saint_Andre_de_lEure"] = "Saint-Andre-de-lEure", + ["Biggin_Hill"] = "Biggin Hill", + ["Manston"] = "Manston", + ["Detling"] = "Detling", + ["Lympne"] = "Lympne", + ["Abbeville_Drucat"] = "Abbeville Drucat", + ["Merville_Calonne"] = "Merville Calonne", + ["Saint_Omer_Wizernes"] = "Saint-Omer Wizernes", } --- Airbases of the Persion Gulf Map: From 89a902fd57dcabc7f0d7d21c1a9558cd8858cec4 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 2 Dec 2023 15:11:14 +0100 Subject: [PATCH 07/38] #ATIS * make info multi-frequency safe --- Moose Development/Moose/Ops/ATIS.lua | 37 ++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index 143364103..3e0c258f2 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -1594,8 +1594,16 @@ function ATIS:onafterStart( From, Event, To ) end -- Info. - self:I( self.lid .. string.format( "Starting ATIS v%s for airbase %s on %.3f MHz Modulation=%d", ATIS.version, self.airbasename, self.frequency, self.modulation ) ) - + if type(self.frequency) == "table" then + local frequency = table.concat(self.frequency,"/") + local modulation = self.modulation + if type(self.modulation) == "table" then + modulation = table.concat(self.modulation,"/") + end + self:I( self.lid .. string.format( "Starting ATIS v%s for airbase %s on %s MHz Modulation=%s", ATIS.version, self.airbasename, frequency, modulation ) ) + else + self:I( self.lid .. string.format( "Starting ATIS v%s for airbase %s on %.3f MHz Modulation=%d", ATIS.version, self.airbasename, self.frequency, self.modulation ) ) + end -- Start radio queue. if not self.useSRS then self.radioqueue = RADIOQUEUE:New( self.frequency, self.modulation, string.format( "ATIS %s", self.airbasename ) ) @@ -1653,7 +1661,17 @@ function ATIS:onafterStatus( From, Event, To ) end -- Info text. - local text = string.format( "State %s: Freq=%.3f MHz %s", fsmstate, self.frequency, UTILS.GetModulationName( self.modulation ) ) + local text = "" + if type(self.frequency) == "table" then + local frequency = table.concat(self.frequency,"/") + local modulation = self.modulation + if type(self.modulation) == "table" then + modulation = table.concat(self.modulation,"/") + end + text = string.format( "State %s: Freq=%s MHz %s", fsmstate, frequency, modulation ) + else + text = string.format( "State %s: Freq=%.3f MHz %s", fsmstate, self.frequency, UTILS.GetModulationName( self.modulation ) ) + end if self.useSRS then text = text .. string.format( ", SRS path=%s (%s), gender=%s, culture=%s, voice=%s", tostring( self.msrs.path ), tostring( self.msrs.port ), tostring( self.msrs.gender ), tostring( self.msrs.culture ), tostring( self.msrs.voice ) ) else @@ -2919,8 +2937,17 @@ function ATIS:UpdateMarker( information, runact, wind, altimeter, temperature ) if self.markerid then self.airbase:GetCoordinate():RemoveMark( self.markerid ) end - - local text = string.format( "ATIS on %.3f %s, %s:\n", self.frequency, UTILS.GetModulationName( self.modulation ), tostring( information ) ) + local text = "" + if type(self.frequency) == "table" then + local frequency = table.concat(self.frequency,"/") + local modulation = self.modulation + if type(modulation) == "table" then + modulation = table.concat(self.modulation,"/") + end + text = string.format( "ATIS on %s %s, %s:\n", tostring(frequency), tostring(modulation), tostring( information ) ) + else + text = string.format( "ATIS on %.3f %s, %s:\n", self.frequency, UTILS.GetModulationName( self.modulation ), tostring( information ) ) + end text = text .. string.format( "%s\n", tostring( runact ) ) text = text .. string.format( "%s\n", tostring( wind ) ) text = text .. string.format( "%s\n", tostring( altimeter ) ) From afe542cc637c5b8eec85765b486fd9dbe7bab8cf Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Sun, 3 Dec 2023 09:23:42 +0100 Subject: [PATCH 08/38] Update Event.lua Fix for playername in weapon target --- Moose Development/Moose/Core/Event.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index b5122c04e..d3a105631 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -1348,7 +1348,8 @@ function EVENT:onEvent( Event ) Event.Weapon = Event.weapon Event.WeaponName = Event.Weapon:getTypeName() Event.WeaponUNIT = CLIENT:Find( Event.Weapon, '', true ) -- Sometimes, the weapon is a player unit! - Event.WeaponPlayerName = Event.WeaponUNIT and Event.Weapon:getPlayerName() + Event.WeaponPlayerName = Event.WeaponUNIT and Event.Weapon.getPlayerName and Event.Weapon:getPlayerName() + --Event.WeaponPlayerName = Event.WeaponUNIT and Event.Weapon:getPlayerName() Event.WeaponCoalition = Event.WeaponUNIT and Event.Weapon:getCoalition() Event.WeaponCategory = Event.WeaponUNIT and Event.Weapon:getDesc().category Event.WeaponTypeName = Event.WeaponUNIT and Event.Weapon:getTypeName() From cf7d41cd7fe4ac0eaa401b194a673f6ca065f705 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 3 Dec 2023 11:42:53 +0100 Subject: [PATCH 09/38] #ZONE_POLYGON improvements #ZONE_OVAL NEW --- Moose Development/Moose/Core/Zone.lua | 712 +++++++++++++++++++++----- 1 file changed, 597 insertions(+), 115 deletions(-) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index aaff86ce7..6014c7f92 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -46,7 +46,7 @@ -- === -- -- ### Author: **FlightControl** --- ### Contributions: **Applevangelist**, **FunkyFranky** +-- ### Contributions: **Applevangelist**, **FunkyFranky**, **coconutcockpit** -- -- === -- @@ -111,7 +111,7 @@ -- ## A zone might have additional Properties created in the DCS Mission Editor, which can be accessed: -- -- *@{#ZONE_BASE.GetProperty}(): Returns the Value of the zone with the given PropertyName, or nil if no matching property exists. --- *@{#ZONE_BASE.GetAllProperties}(): Returns the zone Properties table. +-- *@{#ZONE_BASE.GetAllProperties}(): Returns the zone Properties table. -- -- @field #ZONE_BASE ZONE_BASE = { @@ -313,7 +313,7 @@ function ZONE_BASE:Get2DDistance(Coordinate) else b.x=Coordinate.x b.y=Coordinate.y - end + end local dist=UTILS.VecDist2D(a,b) return dist end @@ -479,8 +479,12 @@ function ZONE_BASE:UndrawZone(Delay) if Delay and Delay>0 then self:ScheduleOnce(Delay, ZONE_BASE.UndrawZone, self) else - if self.DrawID then + if self.DrawID and type(self.DrawID) ~= "table" then UTILS.RemoveMark(self.DrawID) + else -- DrawID is a table with a collections of mark ids, as used in ZONE_POLYGON + for _, mark_id in pairs(self.DrawID) do + UTILS.RemoveMark(mark_id) + end end end return self @@ -575,17 +579,17 @@ end -- @usage -- -- Create a new zone and start watching it every 5 secs for a defined GROUP entering or leaving -- local triggerzone = ZONE:New("ZonetoWatch"):Trigger(GROUP:FindByName("Aerial-1")) --- +-- -- -- This FSM function will be called when the group enters the zone -- function triggerzone:OnAfterEnteredZone(From,Event,To,Group) -- MESSAGE:New("Group has entered zone!",15):ToAll() -- end --- +-- -- -- This FSM function will be called when the group leaves the zone -- function triggerzone:OnAfterLeftZone(From,Event,To,Group) -- MESSAGE:New("Group has left zone!",15):ToAll() -- end --- +-- -- -- Stop watching the zone after 1 hour -- triggerzone:__TriggerStop(3600) function ZONE_BASE:Trigger(Objects) @@ -606,20 +610,20 @@ function ZONE_BASE:Trigger(Objects) self:_TriggerCheck(true) self:__TriggerRunCheck(self.Checktime) return self - + ------------------------ --- Pseudo Functions --- ------------------------ - + --- Triggers the FSM event "TriggerStop". Stops the ZONE_BASE Trigger. -- @function [parent=#ZONE_BASE] TriggerStop -- @param #ZONE_BASE self - --- Triggers the FSM event "TriggerStop" after a delay. + --- Triggers the FSM event "TriggerStop" after a delay. -- @function [parent=#ZONE_BASE] __TriggerStop -- @param #ZONE_BASE self -- @param #number delay Delay in seconds. - + --- On After "EnteredZone" event. An observed object has entered the zone. -- @function [parent=#ZONE_BASE] OnAfterEnteredZone -- @param #ZONE_BASE self @@ -662,12 +666,12 @@ function ZONE_BASE:_TriggerCheck(fromstart) local obj = _object -- Wrapper.Controllable#CONTROLLABLE if obj and obj:IsAlive() then if not obj.TriggerInZone then - -- has not been tagged previously - wasn't in set! + -- has not been tagged previously - wasn't in set! obj.TriggerInZone = {} end if not obj.TriggerInZone[self.ZoneName] then - -- has not been tagged previously - wasn't in set! - obj.TriggerInZone[self.ZoneName] = false + -- has not been tagged previously - wasn't in set! + obj.TriggerInZone[self.ZoneName] = false end -- is obj in zone? local inzone = self:IsCoordinateInZone(obj:GetCoordinate()) @@ -687,7 +691,7 @@ function ZONE_BASE:_TriggerCheck(fromstart) end end end - end + end return self end @@ -712,7 +716,7 @@ end -- @param #string PropertyName The name of a the TriggerZone Property to be retrieved. -- @return #string The Value of the TriggerZone Property with the given PropertyName, or nil if absent. -- @usage --- +-- -- local PropertiesZone = ZONE:FindByName("Properties Zone") -- local Property = "ExampleProperty" -- local PropertyValue = PropertiesZone:GetProperty(Property) @@ -788,7 +792,7 @@ function ZONE_RADIUS:New( ZoneName, Vec2, Radius, DoNotRegisterZone ) if not DoNotRegisterZone then _EVENTDISPATCHER:CreateEventNewZone(self) end - + --self.Coordinate=COORDINATE:NewFromVec2(Vec2) return self @@ -1426,7 +1430,7 @@ end function ZONE_RADIUS:IsVec2InZone( Vec2 ) self:F2( Vec2 ) - if not Vec2 then return false end + if not Vec2 then return false end local ZoneVec2 = self:GetVec2() @@ -1445,7 +1449,7 @@ end -- @return #boolean true if the point is within the zone. function ZONE_RADIUS:IsVec3InZone( Vec3 ) self:F2( Vec3 ) - if not Vec3 then return false end + if not Vec3 then return false end local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } ) return InZone @@ -1565,7 +1569,7 @@ function ZONE_RADIUS:GetRandomCoordinate(inner, outer, surfacetypes) return Coordinate end ---- Returns a @{Core.Point#COORDINATE} object reflecting a random location within the zone where there are no **map objects** of type "Building". +--- Returns a @{Core.Point#COORDINATE} object reflecting a random location within the zone where there are no **map objects** of type "Building". -- Does not find statics you might have placed there. **Note** This might be quite CPU intensive, use with care. -- @param #ZONE_RADIUS self -- @param #number inner (Optional) Minimal distance from the center of the zone in meters. Default is 0m. @@ -1592,7 +1596,7 @@ function ZONE_RADIUS:GetRandomCoordinateWithoutBuildings(inner,outer,distance,ma local buildings = {} local buildingzones = {} - + if self.ScanData and self.ScanData.BuildingCoordinates then buildings = self.ScanData.BuildingCoordinates buildingzones = self.ScanData.BuildingZones @@ -1619,7 +1623,7 @@ function ZONE_RADIUS:GetRandomCoordinateWithoutBuildings(inner,outer,distance,ma end -- max 1000 tries - local rcoord = nil + local rcoord = nil local found = true local iterations = 0 @@ -1635,21 +1639,21 @@ function ZONE_RADIUS:GetRandomCoordinateWithoutBuildings(inner,outer,distance,ma break end end - if found then + if found then -- we have a winner! if markfinal then MARKER:New(rcoord,"FREE"):ToAll() end - break + break end end - + if not found then -- max 1000 tries - local rcoord = nil + local rcoord = nil local found = true local iterations = 0 - + for i=1,1000 do iterations = iterations + 1 rcoord = self:GetRandomCoordinate(inner,outer) @@ -1661,22 +1665,22 @@ function ZONE_RADIUS:GetRandomCoordinateWithoutBuildings(inner,outer,distance,ma found = false end end - if found then + if found then -- we have a winner! if markfinal then MARKER:New(rcoord,"FREE"):ToAll() end - break + break end end end - + T1=timer.getTime() - + self:T(string.format("Found a coordinate: %s | Iterations: %d | Time: %.3f",tostring(found),iterations,T1-T0)) - + if found then return rcoord else return nil end - + end --- @@ -1994,6 +1998,311 @@ function ZONE_GROUP:GetRandomPointVec2( inner, outer ) end +--- Ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Triangle.lua +--- This triangle "zone" is not really to be used on its own, it only serves as building blocks for +--- ZONE_POLYGON to accurately find a point inside a polygon; as well as getting the correct surface area of +--- a polygon. +-- @type _ZONE_TRIANGLE +-- @extends #BASE + +ZONE_OVAL = { + ClassName = "OVAL", + ZoneName="", + MajorAxis = nil, + MinorAxis = nil, + Angle = 0, + DrawPoly = nil -- let's just use a ZONE_POLYGON to draw the ZONE_OVAL on the map +} + +--- Creates a new ZONE_OVAL from a center point, major axis, minor axis, and angle. +--- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Oval.lua +-- @param #table vec2 The center point of the oval +-- @param #number major_axis The major axis of the oval +-- @param #number minor_axis The minor axis of the oval +-- @param #number angle The angle of the oval +-- @return #ZONE_OVAL The new oval +function ZONE_OVAL:New(name, vec2, major_axis, minor_axis, angle) + self = BASE:Inherit(self, ZONE_BASE:New()) + self.ZoneName = name + self.CenterVec2 = vec2 + self.MajorAxis = major_axis + self.MinorAxis = minor_axis + self.Angle = angle or 0 + + _DATABASE:AddZone(name, self) + + return self +end + +--- Constructor to create a ZONE_OVAL instance, taking the name of a drawing made with the draw tool in the Mission Editor. +--- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Oval.lua +-- @param #ZONE_OVAL self +-- @param #string DrawingName The name of the drawing in the Mission Editor +-- @return #ZONE_OVAL self +function ZONE_OVAL:NewFromDrawing(DrawingName) + self = BASE:Inherit(self, ZONE_BASE:New(DrawingName)) + for _, layer in pairs(env.mission.drawings.layers) do + for _, object in pairs(layer["objects"]) do + if string.find(object["name"], DrawingName, 1, true) then + if object["polygonMode"] == "oval" then + self.CenterVec2 = { x = object["mapX"], y = object["mapY"] } + self.MajorAxis = object["r1"] + self.MinorAxis = object["r2"] + self.Angle = object["angle"] + + end + end + end + end + + _DATABASE:AddZone(DrawingName, self) + + return self +end + +--- Gets the major axis of the oval. +-- @return #number The major axis of the oval +function ZONE_OVAL:GetMajorAxis() + return self.MajorAxis +end + +--- Gets the minor axis of the oval. +-- @return #number The minor axis of the oval +function ZONE_OVAL:GetMinorAxis() + return self.MinorAxis +end + +--- Gets the angle of the oval. +-- @return #number The angle of the oval +function ZONE_OVAL:GetAngle() + return self.Angle +end + +--- Returns a the center point of the oval +-- @return #table The center Vec2 +function ZONE_OVAL:GetVec2() + return self.CenterVec2 +end + +--- Checks if a point is contained within the oval. +-- @param #table point The point to check +-- @return #bool True if the point is contained, false otherwise +function ZONE_OVAL:IsVec2InZone(vec2) + local cos, sin = math.cos, math.sin + local dx = vec2.x - self.CenterVec2.x + local dy = vec2.y - self.CenterVec2.y + local rx = dx * cos(self.Angle) + dy * sin(self.Angle) + local ry = -dx * sin(self.Angle) + dy * cos(self.Angle) + return rx * rx / (self.MajorAxis * self.MajorAxis) + ry * ry / (self.MinorAxis * self.MinorAxis) <= 1 +end + +--- Calculates the bounding box of the oval. The bounding box is the smallest rectangle that contains the oval. +-- @return #table The bounding box of the oval +function ZONE_OVAL:GetBoundingSquare() + local min_x = self.CenterVec2.x - self.MajorAxis + local min_y = self.CenterVec2.y - self.MinorAxis + local max_x = self.CenterVec2.x + self.MajorAxis + local max_y = self.CenterVec2.y + self.MinorAxis + + return { + {x=min_x, y=min_x}, {x=max_x, y=min_y}, {x=max_x, y=max_y}, {x=min_x, y=max_y} + } +end + +--- Find points on the edge of the oval +-- @param #number num_points How many points should be found. More = smoother shape +-- @return #table Points on he edge +function ZONE_OVAL:PointsOnEdge(num_points) + num_points = num_points or 40 + local points = {} + local dtheta = 2 * math.pi / num_points + + for i = 0, num_points - 1 do + local theta = i * dtheta + local x = self.CenterVec2.x + self.MajorAxis * math.cos(theta) * math.cos(self.Angle) - self.MinorAxis * math.sin(theta) * math.sin(self.Angle) + local y = self.CenterVec2.y + self.MajorAxis * math.cos(theta) * math.sin(self.Angle) + self.MinorAxis * math.sin(theta) * math.cos(self.Angle) + table.insert(points, {x = x, y = y}) + end + + return points +end + +--- Returns a random Vec2 within the oval. +-- @return #table The random Vec2 +function ZONE_OVAL:GetRandomVec2() + local theta = math.rad(self.Angle) + + local random_point = math.sqrt(math.random()) --> uniformly + --local random_point = math.random() --> more clumped around center + local phi = math.random() * 2 * math.pi + local x_c = random_point * math.cos(phi) + local y_c = random_point * math.sin(phi) + local x_e = x_c * self.MajorAxis + local y_e = y_c * self.MinorAxis + local rx = (x_e * math.cos(theta) - y_e * math.sin(theta)) + self.CenterVec2.x + local ry = (x_e * math.sin(theta) + y_e * math.cos(theta)) + self.CenterVec2.y + + return {x=rx, y=ry} +end + +--- Define a random @{Core.Point#POINT_VEC2} within the zone. +-- @param #ZONE_OVAL self +-- @return Core.Point#POINT_VEC2 The PointVec2 coordinates. +function ZONE_OVAL:GetRandomPointVec2() + return POINT_VEC2:NewFromVec2(self:GetRandomVec2()) +end + +--- Define a random @{Core.Point#POINT_VEC2} within the zone. +-- @param #ZONE_OVAL self +-- @return Core.Point#POINT_VEC2 The PointVec2 coordinates. +function ZONE_OVAL:GetRandomPointVec3() + return POINT_VEC2:NewFromVec3(self:GetRandomVec2()) +end + +--- Draw the zone on the F10 map. +--- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Oval.lua +-- @param #ZONE_OVAL self +-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All. +-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red. +-- @param #number Alpha Transparency [0,1]. Default 1. +-- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value. -- doesn't seem to work +-- @param #number FillAlpha Transparency [0,1]. Default 0.15. -- doesn't seem to work +-- @param #number LineType Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid. +-- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false. +-- @return #ZONE_OVAL self +function ZONE_OVAL:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType) + Coalition = Coalition or self:GetDrawCoalition() + + -- Set draw coalition. + self:SetDrawCoalition(Coalition) + + Color = Color or self:GetColorRGB() + Alpha = Alpha or 1 + + -- Set color. + self:SetColor(Color, Alpha) + + FillColor = FillColor or self:GetFillColorRGB() + if not FillColor then + UTILS.DeepCopy(Color) + end + FillAlpha = FillAlpha or self:GetFillColorAlpha() + if not FillAlpha then + FillAlpha = 0.15 + end + + LineType = LineType or 1 + + -- Set fill color -----------> has fill color worked in recent versions of DCS? + -- doing something like + -- + -- trigger.action.markupToAll(7, -1, 501, p.Coords[1]:GetVec3(), p.Coords[2]:GetVec3(),p.Coords[3]:GetVec3(),p.Coords[4]:GetVec3(),{1,0,0, 1}, {1,0,0, 1}, 4, false, Text or "") + -- + -- doesn't seem to fill in the shape for an n-sided polygon + self:SetFillColor(FillColor, FillAlpha) + + self.DrawPoly = ZONE_POLYGON:NewFromPointsArray(self.ZoneName, self:PointsOnEdge(80)) + self.DrawPoly:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType) +end + +--- Remove drawing from F10 map +function ZONE_OVAL:UndrawZone() + if self.DrawPoly then + self.DrawPoly:UndrawZone() + end +end + + +--- Ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Triangle.lua +--- This triangle "zone" is not really to be used on its own, it only serves as building blocks for +--- ZONE_POLYGON to accurately find a point inside a polygon; as well as getting the correct surface area of +--- a polygon. +-- @type _ZONE_TRIANGLE +-- @extends #BASE + +_ZONE_TRIANGLE = { + ClassName="ZONE_TRIANGLE", + Points={}, + Coords={}, + CenterVec2={x=0, y=0}, + SurfaceArea=0, + DrawIDs={} +} + +function _ZONE_TRIANGLE:New(p1, p2, p3) + local self = BASE:Inherit(self, ZONE_BASE:New()) + self.Points = {p1, p2, p3} + + local center_x = (p1.x + p2.x + p3.x) / 3 + local center_y = (p1.y + p2.y + p3.y) / 3 + self.CenterVec2 = {x=center_x, y=center_y} + + for _, pt in pairs({p1, p2, p3}) do + table.add(self.Coords, COORDINATE:NewFromVec2(pt)) + end + + self.SurfaceArea = math.abs((p2.x - p1.x) * (p3.y - p1.y) - (p3.x - p1.x) * (p2.y - p1.y)) * 0.5 + + return self +end + +--- Checks if a point is contained within the triangle. +-- @param #table pt The point to check +-- @param #table points (optional) The points of the triangle, or 3 other points if you're just using the TRIANGLE class without an object of it +-- @return #bool True if the point is contained, false otherwise +function _ZONE_TRIANGLE:ContainsPoint(pt, points) + points = points or self.Points + + local function sign(p1, p2, p3) + return (p1.x - p3.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p3.y) + end + + local d1 = sign(pt, self.Points[1], self.Points[2]) + local d2 = sign(pt, self.Points[2], self.Points[3]) + local d3 = sign(pt, self.Points[3], self.Points[1]) + + local has_neg = (d1 < 0) or (d2 < 0) or (d3 < 0) + local has_pos = (d1 > 0) or (d2 > 0) or (d3 > 0) + + return not (has_neg and has_pos) +end + +--- Returns a random Vec2 within the triangle. +-- @param #table points The points of the triangle, or 3 other points if you're just using the TRIANGLE class without an object of it +-- @return #table The random Vec2 +function _ZONE_TRIANGLE:GetRandomVec2(points) + points = points or self.Points + local pt = {math.random(), math.random()} + table.sort(pt) + local s = pt[1] + local t = pt[2] - pt[1] + local u = 1 - pt[2] + + return {x = s * points[1].x + t * points[2].x + u * points[3].x, + y = s * points[1].y + t * points[2].y + u * points[3].y} +end + +--- Draw the triangle +function _ZONE_TRIANGLE:Draw(Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly) + Coalition=Coalition or -1 + + Color=Color or {1, 0, 0 } + Alpha=Alpha or 1 + + FillColor=FillColor or Color + if not FillColor then UTILS.DeepCopy(Color) end + FillAlpha=FillAlpha or Alpha + if not FillAlpha then FillAlpha=1 end + + for i=1, #self.Coords do + local c1 = self.Coords[i] + local c2 = self.Coords[i % #self.Coords + 1] + table.add(self.DrawIDs, c1:LineToAll(c2, Coalition, Color, Alpha, LineType, ReadOnly)) + end + return self.DrawIDs +end + + --- -- @type ZONE_POLYGON_BASE -- @field #ZONE_POLYGON_BASE.ListVec2 Polygon The polygon defined by an array of @{DCS#Vec2}. @@ -2021,7 +2330,10 @@ end -- @field #ZONE_POLYGON_BASE ZONE_POLYGON_BASE = { ClassName="ZONE_POLYGON_BASE", - } + _Triangles={}, -- _ZONE_TRIANGLES + SurfaceArea=0, + DrawID={} -- making a table out of the MarkID so its easier to draw an n-sided polygon, see ZONE_POLYGON_BASE:Draw() +} --- A 2D points array. -- @type ZONE_POLYGON_BASE.ListVec2 @@ -2055,9 +2367,101 @@ function ZONE_POLYGON_BASE:New( ZoneName, PointsArray ) end + -- triangulate the polygon so we can work with it + self._Triangles = self:_Triangulate() + -- set the polygon's surface area + self.SurfaceArea = self:_CalculateSurfaceArea() + return self end +--- Triangulates the polygon. +--- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Polygon.lua +-- @return #table The #_TRIANGLE list that make up +function ZONE_POLYGON_BASE:_Triangulate() + local points = self._.Polygon + local triangles = {} + + local function get_orientation(shape_points) + local sum = 0 + for i = 1, #shape_points do + local j = i % #shape_points + 1 + sum = sum + (shape_points[j].x - shape_points[i].x) * (shape_points[j].y + shape_points[i].y) + end + return sum >= 0 and "clockwise" or "counter-clockwise" -- sum >= 0, return "clockwise", else return "counter-clockwise" + end + + local function ensure_clockwise(shape_points) + local orientation = get_orientation(shape_points) + if orientation == "counter-clockwise" then + -- Reverse the order of shape_points so they're clockwise + local reversed = {} + for i = #shape_points, 1, -1 do + table.insert(reversed, shape_points[i]) + end + return reversed + end + return shape_points + end + + local function is_clockwise(p1, p2, p3) + local cross_product = (p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x) + return cross_product < 0 + end + + local function divide_recursively(shape_points) + if #shape_points == 3 then + table.insert(triangles, _ZONE_TRIANGLE:New(shape_points[1], shape_points[2], shape_points[3])) + elseif #shape_points > 3 then -- find an ear -> a triangle with no other points inside it + for i, p1 in ipairs(shape_points) do + local p2 = shape_points[(i % #shape_points) + 1] + local p3 = shape_points[(i + 1) % #shape_points + 1] + local triangle = _ZONE_TRIANGLE:New(p1, p2, p3) + local is_ear = true + + if not is_clockwise(p1, p2, p3) then + is_ear = false + else + for _, point in ipairs(shape_points) do + if point ~= p1 and point ~= p2 and point ~= p3 and triangle:ContainsPoint(point) then + is_ear = false + break + end + end + end + + if is_ear then + -- Check if any point in the original polygon is inside the ear triangle + local is_valid_triangle = true + for _, point in ipairs(points) do + if point ~= p1 and point ~= p2 and point ~= p3 and triangle:ContainsPoint(point) then + is_valid_triangle = false + break + end + end + if is_valid_triangle then + table.insert(triangles, triangle) + local remaining_points = {} + for j, point in ipairs(shape_points) do + if point ~= p2 then + table.insert(remaining_points, point) + end + end + divide_recursively(remaining_points) + break + end + else + + end + end + end + end + + points = ensure_clockwise(points) + divide_recursively(points) + return triangles +end + --- Update polygon points with an array of @{DCS#Vec2}. -- @param #ZONE_POLYGON_BASE self -- @param #ZONE_POLYGON_BASE.ListVec2 Vec2Array An array of @{DCS#Vec2}, forming a polygon. @@ -2072,6 +2476,10 @@ function ZONE_POLYGON_BASE:UpdateFromVec2(Vec2Array) self._.Polygon[i].y=Vec2Array[i].y end + -- triangulate the polygon so we can work with it + self._Triangles = self:_Triangulate() + -- set the polygon's surface area + self.SurfaceArea = self:_CalculateSurfaceArea() return self end @@ -2089,9 +2497,24 @@ function ZONE_POLYGON_BASE:UpdateFromVec3(Vec3Array) self._.Polygon[i].y=Vec3Array[i].z end + -- triangulate the polygon so we can work with it + self._Triangles = self:_Triangulate() + -- set the polygon's surface area + self.SurfaceArea = self:_CalculateSurfaceArea() return self end +--- Calculates the surface area of the polygon. The surface area is the sum of the areas of the triangles that make up the polygon. +--- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Polygon.lua +-- @return #number The surface area of the polygon +function ZONE_POLYGON_BASE:_CalculateSurfaceArea() + local area = 0 + for _, triangle in pairs(self._Triangles) do + area = area + triangle.SurfaceArea + end + return area +end + --- Returns the center location of the polygon. -- @param #ZONE_POLYGON_BASE self -- @return DCS#Vec2 The location of the zone based on the @{Wrapper.Group} location. @@ -2233,64 +2656,78 @@ function ZONE_POLYGON_BASE:BoundZone( UnBound ) return self end ---- Draw the zone on the F10 map. **NOTE** Currently, only polygons **up to ten points** are supported! +--- Draw the zone on the F10 map. Infinite number of points supported +--- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Polygon.lua -- @param #ZONE_POLYGON_BASE self -- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All. -- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red. -- @param #number Alpha Transparency [0,1]. Default 1. --- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value. --- @param #number FillAlpha Transparency [0,1]. Default 0.15. +-- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value. -- doesn't seem to work +-- @param #number FillAlpha Transparency [0,1]. Default 0.15. -- doesn't seem to work -- @param #number LineType Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid. -- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false. -- @return #ZONE_POLYGON_BASE self -function ZONE_POLYGON_BASE:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly) +function ZONE_POLYGON_BASE:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly, IncludeTriangles) + if self._.Polygon and #self._.Polygon >= 3 then + Coalition = Coalition or self:GetDrawCoalition() - if self._.Polygon and #self._.Polygon>=3 then + -- Set draw coalition. + self:SetDrawCoalition(Coalition) - local coordinate=COORDINATE:NewFromVec2(self._.Polygon[1]) + Color = Color or self:GetColorRGB() + Alpha = Alpha or 1 - Coalition=Coalition or self:GetDrawCoalition() + -- Set color. + self:SetColor(Color, Alpha) - -- Set draw coalition. - self:SetDrawCoalition(Coalition) + FillColor = FillColor or self:GetFillColorRGB() + if not FillColor then + UTILS.DeepCopy(Color) + end + FillAlpha = FillAlpha or self:GetFillColorAlpha() + if not FillAlpha then + FillAlpha = 0.15 + end - Color=Color or self:GetColorRGB() - Alpha=Alpha or 1 + -- Set fill color -----------> has fill color worked in recent versions of DCS? + -- doing something like + -- + -- trigger.action.markupToAll(7, -1, 501, p.Coords[1]:GetVec3(), p.Coords[2]:GetVec3(),p.Coords[3]:GetVec3(),p.Coords[4]:GetVec3(),{1,0,0, 1}, {1,0,0, 1}, 4, false, Text or "") + -- + -- doesn't seem to fill in the shape for an n-sided polygon + self:SetFillColor(FillColor, FillAlpha) - -- Set color. - self:SetColor(Color, Alpha) - - FillColor=FillColor or self:GetFillColorRGB() - if not FillColor then UTILS.DeepCopy(Color) end - FillAlpha=FillAlpha or self:GetFillColorAlpha() - if not FillAlpha then FillAlpha=0.15 end - - -- Set fill color. - self:SetFillColor(FillColor, FillAlpha) - - if #self._.Polygon==4 then - - local Coord2=COORDINATE:NewFromVec2(self._.Polygon[2]) - local Coord3=COORDINATE:NewFromVec2(self._.Polygon[3]) - local Coord4=COORDINATE:NewFromVec2(self._.Polygon[4]) - - self.DrawID=coordinate:QuadToAll(Coord2, Coord3, Coord4, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly) - - else - - local Coordinates=self:GetVerticiesCoordinates() - table.remove(Coordinates, 1) - - self.DrawID=coordinate:MarkupToAllFreeForm(Coordinates, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly) + IncludeTriangles = IncludeTriangles or false + -- just draw the triangles, we get the outline for free + if IncludeTriangles then + for _, triangle in pairs(self._Triangles) do + local draw_ids = triangle:Draw() + table.combine(self.DrawID, draw_ids) + end + -- draw outline only + else + local coords = self:GetVerticiesCoordinates() + for i = 1, #coords do + local c1 = coords[i] + local c2 = coords[i % #coords + 1] + table.add(self.DrawID, c1:LineToAll(c2, Coalition, Color, Alpha, LineType, ReadOnly)) + end + end end - - end - - return self + return self end ---- Get the smallest radius encompassing all points of the polygon zone. +--- Get the surface area of this polygon +-- @param #ZONE_POLYGON_BASE self +-- @return #number Surface area +function ZONE_POLYGON_BASE:GetSurfaceArea() + return self.SurfaceArea +end + + + +--- Get the smallest radius encompassing all points of the polygon zone. -- @param #ZONE_POLYGON_BASE self -- @return #number Radius of the zone in meters. function ZONE_POLYGON_BASE:GetRadius() @@ -2298,22 +2735,22 @@ function ZONE_POLYGON_BASE:GetRadius() local center=self:GetVec2() local radius=0 - + for _,_vec2 in pairs(self._.Polygon) do local vec2=_vec2 --DCS#Vec2 - + local r=UTILS.VecDist2D(center, vec2) - + if r>radius then radius=r end - + end return radius end ---- Get the smallest circular zone encompassing all points of the polygon zone. +--- Get the smallest circular zone encompassing all points of the polygon zone. -- @param #ZONE_POLYGON_BASE self -- @param #string ZoneName (Optional) Name of the zone. Default is the name of the polygon zone. -- @param #boolean DoNotRegisterZone (Optional) If `true`, zone is not registered. @@ -2323,25 +2760,25 @@ function ZONE_POLYGON_BASE:GetZoneRadius(ZoneName, DoNotRegisterZone) local center=self:GetVec2() local radius=self:GetRadius() - + local zone=ZONE_RADIUS:New(ZoneName or self.ZoneName, center, radius, DoNotRegisterZone) return zone end ---- Get the smallest rectangular zone encompassing all points points of the polygon zone. +--- Get the smallest rectangular zone encompassing all points points of the polygon zone. -- @param #ZONE_POLYGON_BASE self -- @param #string ZoneName (Optional) Name of the zone. Default is the name of the polygon zone. -- @param #boolean DoNotRegisterZone (Optional) If `true`, zone is not registered. -- @return #ZONE_POLYGON The rectangular zone. function ZONE_POLYGON_BASE:GetZoneQuad(ZoneName, DoNotRegisterZone) - + local vec1, vec3=self:GetBoundingVec2() - + local vec2={x=vec1.x, y=vec3.y} local vec4={x=vec3.x, y=vec1.y} - + local zone=ZONE_POLYGON_BASE:New(ZoneName or self.ZoneName, {vec1, vec2, vec3, vec4}) return zone @@ -2354,15 +2791,15 @@ end function ZONE_POLYGON_BASE:RemoveJunk(Height) Height=Height or 1000 - + local vec2SW, vec2NE=self:GetBoundingVec2() local vec3SW={x=vec2SW.x, y=-Height, z=vec2SW.y} --DCS#Vec3 local vec3NE={x=vec2NE.x, y= Height, z=vec2NE.y} --DCS#Vec3 - + --local coord1=COORDINATE:NewFromVec3(vec3SW):MarkToAll("SW") --local coord1=COORDINATE:NewFromVec3(vec3NE):MarkToAll("NE") - + local volume = { id = world.VolumeType.BOX, params = { @@ -2371,7 +2808,7 @@ function ZONE_POLYGON_BASE:RemoveJunk(Height) } } - local n=world.removeJunk(volume) + local n=world.removeJunk(volume) return n end @@ -2449,7 +2886,7 @@ end -- @return #boolean true if the location is within the zone. function ZONE_POLYGON_BASE:IsVec2InZone( Vec2 ) self:F2( Vec2 ) - if not Vec2 then return false end + if not Vec2 then return false end local Next local Prev local InPolygon = false @@ -2479,40 +2916,34 @@ end -- @return #boolean true if the point is within the zone. function ZONE_POLYGON_BASE:IsVec3InZone( Vec3 ) self:F2( Vec3 ) - - if not Vec3 then return false end - + + if not Vec3 then return false end + local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } ) return InZone end --- Define a random @{DCS#Vec2} within the zone. +--- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Polygon.lua -- @param #ZONE_POLYGON_BASE self -- @return DCS#Vec2 The Vec2 coordinate. function ZONE_POLYGON_BASE:GetRandomVec2() - - -- It is a bit tricky to find a random point within a polygon. Right now i am doing it the dirty and inefficient way... - - -- Get the bounding square. - local BS = self:GetBoundingSquare() - - local Nmax=1000 ; local n=0 - while n= random_weight then + return triangle:GetRandomVec2() + end + end end --- Return a @{Core.Point#POINT_VEC2} object representing a random 2D point at landheight within the zone. @@ -2597,7 +3028,7 @@ function ZONE_POLYGON_BASE:GetBoundingVec2() y2 = ( y2 < self._.Polygon[i].y ) and self._.Polygon[i].y or y2 end - + local vec1={x=x1, y=y1} local vec2={x=x2, y=y2} @@ -2649,7 +3080,8 @@ end -- @extends #ZONE_POLYGON_BASE ---- The ZONE_POLYGON class defined by a sequence of @{Wrapper.Group#GROUP} waypoints within the Mission Editor, forming a polygon. +--- The ZONE_POLYGON class defined by a sequence of @{Wrapper.Group#GROUP} waypoints within the Mission Editor, forming a polygon, OR by drawings made with the Draw tool +--- in the Mission Editor -- This class implements the inherited functions from @{#ZONE_RADIUS} taking into account the own zone format and properties. -- -- ## Declare a ZONE_POLYGON directly in the DCS mission editor! @@ -2672,6 +3104,13 @@ end -- then SetZone would contain the ZONE_POLYGON object `DefenseZone` as part of the zone collection, -- without much scripting overhead! -- +-- This class now also supports drawings made with the Draw tool in the Mission Editor. Any drawing made with Line > Segments > Closed, Polygon > Rect or Polygon > Free can be +-- made into a ZONE_POLYGON. +-- +-- This class has been updated to use a accurate way of generating random points inside the polygon without having to use trial and error guesses. +-- You can also get the surface area of the polygon now, handy if you want measure which coalition has the largest captured area, for example. + + -- @field #ZONE_POLYGON ZONE_POLYGON = { ClassName="ZONE_POLYGON", @@ -2732,6 +3171,49 @@ function ZONE_POLYGON:NewFromGroupName( GroupName ) return self end +--- Constructor to create a ZONE_POLYGON instance, taking the name of a drawing made with the draw tool in the Mission Editor. +-- @param #ZONE_POLYGON self +-- @param #string DrawingName The name of the drawing in the Mission Editor +-- @return #ZONE_POLYGON self +function ZONE_POLYGON:NewFromDrawing(DrawingName) + local points = {} + for _, layer in pairs(env.mission.drawings.layers) do + for _, object in pairs(layer["objects"]) do + if object["name"] == DrawingName then + if (object["primitiveType"] == "Line" and object["closed"] == true) or (object["polygonMode"] == "free") then + -- points for the drawings are saved in local space, so add the object's map x and y coordinates to get + -- world space points we can use + for _, point in UTILS.spairs(object["points"]) do + local p = {x = object["mapX"] + point["x"], + y = object["mapY"] + point["y"] } + table.add(points, p) + end + elseif object["polygonMode"] == "rect" then + -- the points for a rect are saved as local coordinates with an angle. To get the world space points from this + -- we need to rotate the points around the center of the rects by an angle. UTILS.RotatePointAroundPivot was + -- committed in an earlier commit + local angle = object["angle"] + local half_width = object["width"] / 2 + local half_height = object["height"] / 2 + + local center = { x = object["mapX"], y = object["mapY"] } + local p1 = UTILS.RotatePointAroundPivot({ x = center.x - half_height, y = center.y + half_width }, center, angle) + local p2 = UTILS.RotatePointAroundPivot({ x = center.x + half_height, y = center.y + half_width }, center, angle) + local p3 = UTILS.RotatePointAroundPivot({ x = center.x + half_height, y = center.y - half_width }, center, angle) + local p4 = UTILS.RotatePointAroundPivot({ x = center.x - half_height, y = center.y - half_width }, center, angle) + + points = {p1, p2, p3, p4} + else + -- something else that might be added in the future + end + end + end + end + local self = BASE:Inherit(self, ZONE_POLYGON_BASE:New(DrawingName, points)) + _EVENTDISPATCHER:CreateEventNewZone(self) + return self +end + --- Find a polygon zone in the _DATABASE using the name of the polygon zone. -- @param #ZONE_POLYGON self From 89a9d1d0a40c1ce37b17929e9d41b04f0f91395f Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 3 Dec 2023 12:01:39 +0100 Subject: [PATCH 10/38] #CONTROLLABLE * Fixed ID issue with AA Missile Attack Range option #POINT * Added methdo to get the BULLSEYE as coordinate --- Moose Development/Moose/Core/Point.lua | 16 ++++++++++++++-- Moose Development/Moose/Wrapper/Controllable.lua | 2 +- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 8a811e19f..d120bc35a 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -920,7 +920,7 @@ do -- COORDINATE end - --- Return an angle in radians from the COORDINATE using a direction vector in Vec3 format. + --- Return an angle in radians from the COORDINATE using a **direction vector in Vec3 format**. -- @param #COORDINATE self -- @param DCS#Vec3 DirectionVec3 The direction vector in Vec3 format. -- @return #number DirectionRadians The angle in radians. @@ -933,10 +933,12 @@ do -- COORDINATE return DirectionRadians end - --- Return an angle in degrees from the COORDINATE using a direction vector in Vec3 format. + --- Return an angle in degrees from the COORDINATE using a **direction vector in Vec3 format**. -- @param #COORDINATE self -- @param DCS#Vec3 DirectionVec3 The direction vector in Vec3 format. -- @return #number DirectionRadians The angle in degrees. + -- @usage + -- local directionAngle = currentCoordinate:GetAngleDegrees(currentCoordinate:GetDirectionVec3(sourceCoordinate:GetVec3())) function COORDINATE:GetAngleDegrees( DirectionVec3 ) local AngleRadians = self:GetAngleRadians( DirectionVec3 ) local Angle = UTILS.ToDegree( AngleRadians ) @@ -3021,6 +3023,16 @@ do -- COORDINATE return BRAANATO end + --- Return the BULLSEYE as COORDINATE Object + -- @param #number Coalition Coalition of the bulls eye to return, e.g. coalition.side.BLUE + -- @return #COORDINATE self + -- @usage + -- -- note the dot (.) here,not using the colon (:) + -- local redbulls = COORDINATE.GetBullseyeCoordinate(coalition.side.RED) + function COORDINATE.GetBullseyeCoordinate(Coalition) + return COORDINATE:NewFromVec3( coalition.getMainRefPoint( Coalition ) ) + end + --- Return a BULLS string out of the BULLS of the coalition to the COORDINATE. -- @param #COORDINATE self -- @param DCS#coalition.side Coalition The coalition. diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index ded42ebfd..e7d4ca3c9 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -3974,7 +3974,7 @@ function CONTROLLABLE:OptionAAAttackRange( range ) local Controller = self:_GetController() if Controller then if self:IsAir() then - self:SetOption( AI.Option.Air.val.MISSILE_ATTACK, range ) + self:SetOption( AI.Option.Air.id.MISSILE_ATTACK, range ) end end return self From c97d2ecabaffdb58f2fcaba6b4acec07a9957d0e Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 3 Dec 2023 12:11:22 +0100 Subject: [PATCH 11/38] #ATIS - multi freq example added --- Moose Development/Moose/Ops/ATIS.lua | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index 3e0c258f2..6819ee36c 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -312,10 +312,16 @@ -- -- atis=ATIS:New("Batumi", 305, radio.modulation.AM) -- atis:SetSRS("D:\\DCS\\_SRS\\", "male", "en-US") --- atis:Start() +-- atis:Start() -- -- This uses a male voice with US accent. It requires SRS to be installed in the `D:\DCS\_SRS\` directory. Note that backslashes need to be escaped or simply use slashes (as in linux). -- +-- ### SRS can use multiple frequencies: +-- +-- atis=ATIS:New("Batumi", {305,103.85}, {radio.modulation.AM,radio.modulation.FM}) +-- atis:SetSRS("D:\\DCS\\_SRS\\", "male", "en-US") +-- atis:Start() +-- -- ### SRS Localization -- -- You can localize the SRS output, all you need is to provide a table of translations and set the `locale` of your instance. You need to provide the translations in your script **before you instantiate your ATIS**. @@ -884,13 +890,14 @@ _ATIS = {} --- ATIS class version. -- @field #string version -ATIS.version = "0.10.3" +ATIS.version = "0.10.4" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Correct fog for elevation. +-- DONE: Option to add multiple frequencies for SRS -- DONE: Zulu time --> Zulu in output. -- DONE: Fix for AB not having a runway - Helopost like Naqoura -- DONE: Add new Normandy airfields. @@ -899,7 +906,7 @@ ATIS.version = "0.10.3" -- DONE: Visibility reported twice over SRS -- DONE: Add text report for output. -- DONE: Add stop FMS functions. --- NOGO: Use local time. Not realisitc! +-- NOGO: Use local time. Not realistic! -- DONE: Dew point. Approx. done. -- DONE: Metric units. -- DONE: Set UTC correction. @@ -915,8 +922,8 @@ ATIS.version = "0.10.3" --- Create a new ATIS class object for a specific airbase. -- @param #ATIS self -- @param #string AirbaseName Name of the airbase. --- @param #number Frequency Radio frequency in MHz. Default 143.00 MHz. --- @param #number Modulation Radio modulation: 0=AM, 1=FM. Default 0=AM. See `radio.modulation.AM` and `radio.modulation.FM` enumerators. +-- @param #number Frequency Radio frequency in MHz. Default 143.00 MHz. When using **SRS** this can be passed as a table of multiple frequencies. +-- @param #number Modulation Radio modulation: 0=AM, 1=FM. Default 0=AM. See `radio.modulation.AM` and `radio.modulation.FM` enumerators. When using **SRS** this can be passed as a table of multiple modulations. -- @return #ATIS self function ATIS:New(AirbaseName, Frequency, Modulation) From c22304f2b0b4e6585b0cdbcbc5feb688abc29fff Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 3 Dec 2023 12:25:25 +0100 Subject: [PATCH 12/38] Remove demo links which were empty --- Moose Development/Moose/Core/Point.lua | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index d120bc35a..3911a52f3 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -8,22 +8,6 @@ -- -- === -- --- # Demo Missions --- --- ### [POINT_VEC Demo Missions source code]() --- --- ### [POINT_VEC Demo Missions, only for beta testers]() --- --- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases) --- --- === --- --- # YouTube Channel --- --- ### [POINT_VEC YouTube Channel]() --- --- === --- -- ### Authors: -- -- * FlightControl (Design & Programming) From f739062463177899f91ff8f6ae90f62b5dec98c3 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 3 Dec 2023 12:39:08 +0100 Subject: [PATCH 13/38] #ZONE_OVAL - fix documentation and intellisense --- Moose Development/Moose/Core/Zone.lua | 29 +++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 6014c7f92..cb2648d0d 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -42,6 +42,7 @@ -- * @{#ZONE_UNIT}: The ZONE_UNIT class defines by a zone around a @{Wrapper.Unit#UNIT} with a radius. -- * @{#ZONE_GROUP}: The ZONE_GROUP class defines by a zone around a @{Wrapper.Group#GROUP} with a radius. -- * @{#ZONE_POLYGON}: The ZONE_POLYGON class defines by a sequence of @{Wrapper.Group#GROUP} waypoints within the Mission Editor, forming a polygon. +-- * @{#ZONE_OVAL}: The ZONE_OVAL class isdefined by a center point, major axis, minor axis, and angle. -- -- === -- @@ -1998,13 +1999,17 @@ function ZONE_GROUP:GetRandomPointVec2( inner, outer ) end ---- Ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Triangle.lua ---- This triangle "zone" is not really to be used on its own, it only serves as building blocks for ---- ZONE_POLYGON to accurately find a point inside a polygon; as well as getting the correct surface area of ---- a polygon. --- @type _ZONE_TRIANGLE --- @extends #BASE +--- ZONE_OVAL created from a center point, major axis, minor axis, and angle. +-- Ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Oval.lua +-- @type ZONE_OVAL +-- @extends Core.Zone#ZONE_BASE +--- ## ZONE_OVAL class, extends @{#ZONE_BASE} +-- +-- The ZONE_OVAL class is defined by a center point, major axis, minor axis, and angle. +-- This class implements the inherited functions from @{#ZONE_BASE} taking into account the own zone format and properties. +-- +-- @field #ZONE_OVAL ZONE_OVAL = { ClassName = "OVAL", ZoneName="", @@ -2060,31 +2065,36 @@ function ZONE_OVAL:NewFromDrawing(DrawingName) return self end ---- Gets the major axis of the oval. +--- Gets the major axis of the oval. +-- @param #ZONE_OVAL self -- @return #number The major axis of the oval function ZONE_OVAL:GetMajorAxis() return self.MajorAxis end --- Gets the minor axis of the oval. +-- @param #ZONE_OVAL self -- @return #number The minor axis of the oval function ZONE_OVAL:GetMinorAxis() return self.MinorAxis end --- Gets the angle of the oval. +-- @param #ZONE_OVAL self -- @return #number The angle of the oval function ZONE_OVAL:GetAngle() return self.Angle end --- Returns a the center point of the oval +-- @param #ZONE_OVAL self -- @return #table The center Vec2 function ZONE_OVAL:GetVec2() return self.CenterVec2 end --- Checks if a point is contained within the oval. +-- @param #ZONE_OVAL self -- @param #table point The point to check -- @return #bool True if the point is contained, false otherwise function ZONE_OVAL:IsVec2InZone(vec2) @@ -2097,6 +2107,7 @@ function ZONE_OVAL:IsVec2InZone(vec2) end --- Calculates the bounding box of the oval. The bounding box is the smallest rectangle that contains the oval. +-- @param #ZONE_OVAL self -- @return #table The bounding box of the oval function ZONE_OVAL:GetBoundingSquare() local min_x = self.CenterVec2.x - self.MajorAxis @@ -2110,6 +2121,7 @@ function ZONE_OVAL:GetBoundingSquare() end --- Find points on the edge of the oval +-- @param #ZONE_OVAL self -- @param #number num_points How many points should be found. More = smoother shape -- @return #table Points on he edge function ZONE_OVAL:PointsOnEdge(num_points) @@ -2128,6 +2140,7 @@ function ZONE_OVAL:PointsOnEdge(num_points) end --- Returns a random Vec2 within the oval. +-- @param #ZONE_OVAL self -- @return #table The random Vec2 function ZONE_OVAL:GetRandomVec2() local theta = math.rad(self.Angle) @@ -2206,6 +2219,7 @@ function ZONE_OVAL:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineT end --- Remove drawing from F10 map +-- @param #ZONE_OVAL self function ZONE_OVAL:UndrawZone() if self.DrawPoly then self.DrawPoly:UndrawZone() @@ -2219,7 +2233,6 @@ end --- a polygon. -- @type _ZONE_TRIANGLE -- @extends #BASE - _ZONE_TRIANGLE = { ClassName="ZONE_TRIANGLE", Points={}, From 49191fb144330e7d072941c5f4184eac1d9e7b5d Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 3 Dec 2023 15:34:55 +0100 Subject: [PATCH 14/38] clarifications --- Moose Development/Moose/Core/Set.lua | 10 +++++++++- Moose Development/Moose/Functional/Range.lua | 12 ++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index ee0917406..fec517906 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -1065,8 +1065,15 @@ do self:FilterActive( false ) return self + + --- Filter the set once + -- @function [parent=#SET_GROUP] FilterOnce + -- @param #SET_GROUP self + -- @return #SET_GROUP self + + end - + --- Get a *new* set that only contains alive groups. -- @param #SET_GROUP self -- @return #SET_GROUP Set of alive groups. @@ -1976,6 +1983,7 @@ do --- Get the closest group of the set with respect to a given reference coordinate. Optionally, only groups of given coalitions are considered in the search. -- @param #SET_GROUP self -- @param Core.Point#COORDINATE Coordinate Reference Coordinate from which the closest group is determined. + -- @param #table Coalitions (Optional) Table of coalition #number entries to filter for. -- @return Wrapper.Group#GROUP The closest group (if any). -- @return #number Distance in meters to the closest group. function SET_GROUP:GetClosestGroup(Coordinate, Coalitions) diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index e9005ace0..e238914a9 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -1234,7 +1234,7 @@ function RANGE:SetSRS(PathToSRS, Port, Coalition, Frequency, Modulation, Volume, return self end ---- (SRS) Set range control frequency and voice. +--- (SRS) Set range control frequency and voice. Use `RANGE:SetSRS()` once first before using this function. -- @param #RANGE self -- @param #number frequency Frequency in MHz. Default 256 MHz. -- @param #number modulation Modulation, defaults to radio.modulation.AM. @@ -1244,6 +1244,10 @@ end -- @param #string relayunitname Name of the unit used for transmission location. -- @return #RANGE self function RANGE:SetSRSRangeControl( frequency, modulation, voice, culture, gender, relayunitname ) + if not self.instructmsrs then + self:E(self.lid.."Use myrange:SetSRS() once first before using myrange:SetSRSRangeControl!") + return self + end self.rangecontrolfreq = frequency or 256 self.controlmsrs:SetFrequencies(self.rangecontrolfreq) self.controlmsrs:SetModulations(modulation or radio.modulation.AM) @@ -1259,7 +1263,7 @@ function RANGE:SetSRSRangeControl( frequency, modulation, voice, culture, gender return self end ---- (SRS) Set range instructor frequency and voice. +--- (SRS) Set range instructor frequency and voice. Use `RANGE:SetSRS()` once first before using this function. -- @param #RANGE self -- @param #number frequency Frequency in MHz. Default 305 MHz. -- @param #number modulation Modulation, defaults to radio.modulation.AM. @@ -1269,6 +1273,10 @@ end -- @param #string relayunitname Name of the unit used for transmission location. -- @return #RANGE self function RANGE:SetSRSRangeInstructor( frequency, modulation, voice, culture, gender, relayunitname ) + if not self.instructmsrs then + self:E(self.lid.."Use myrange:SetSRS() once first before using myrange:SetSRSRangeInstructor!") + return self + end self.instructorfreq = frequency or 305 self.instructmsrs:SetFrequencies(self.instructorfreq) self.instructmsrs:SetModulations(modulation or radio.modulation.AM) From fac7a5fdc6b816e07d43d4df33325d56ff0ad0eb Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Mon, 4 Dec 2023 10:40:38 +0100 Subject: [PATCH 15/38] Update CTLD.lua (#2058) Add Remove Crates option --- Moose Development/Moose/Ops/CTLD.lua | 35 ++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 9f0d780bd..07f15ad9d 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -2561,6 +2561,40 @@ function CTLD:_ListCratesNearby( _group, _unit) return self end +-- (Internal) Function to find and Remove nearby crates. +-- @param #CTLD self +-- @param Wrapper.Group#GROUP Group +-- @param Wrapper.Unit#UNIT Unit +-- @return #CTLD self +function CTLD:_RemoveCratesNearby( _group, _unit) + self:T(self.lid .. " _RemoveCratesNearby") + local finddist = self.CrateDistance or 35 + local crates,number = self:_FindCratesNearby(_group,_unit, finddist,true) -- #table + if number > 0 then + local text = REPORT:New("Removing Crates Found Nearby:") + text:Add("------------------------------------------------------------") + for _,_entry in pairs (crates) do + local entry = _entry -- #CTLD_CARGO + local name = entry:GetName() --#string + local dropped = entry:WasDropped() + if dropped then + text:Add(string.format("Crate for %s, %dkg removed",name, entry.PerCrateMass)) + else + text:Add(string.format("Crate for %s, %dkg removed",name, entry.PerCrateMass)) + end + entry:GetPositionable():Destroy(false) + end + if text:GetCount() == 1 then + text:Add(" N O N E") + end + text:Add("------------------------------------------------------------") + self:_SendMessage(text:Text(), 30, true, _group) + else + self:_SendMessage(string.format("No (loadable) crates within %d meters!",finddist), 10, false, _group) + end + return self +end + --- (Internal) Return distance in meters between two coordinates. -- @param #CTLD self -- @param Core.Point#COORDINATE _point1 Coordinate one @@ -3672,6 +3706,7 @@ function CTLD:_RefreshF10Menus() end end listmenu = MENU_GROUP_COMMAND:New(_group,"List crates nearby",topcrates, self._ListCratesNearby, self, _group, _unit) + listmenu = MENU_GROUP_COMMAND:New(_group,"Remove crates nearby",topcrates, self._RemoveCratesNearby, self, _group, _unit) local unloadmenu = MENU_GROUP_COMMAND:New(_group,"Drop crates",topcrates, self._UnloadCrates, self, _group, _unit) if not self.nobuildmenu then local buildmenu = MENU_GROUP_COMMAND:New(_group,"Build crates",topcrates, self._BuildCrates, self, _group, _unit) From e078e488531e5d6e5d62276da17c2d293e4843f2 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 6 Dec 2023 08:42:07 +0100 Subject: [PATCH 16/38] #SPAWN * Fix for a Link16 flight having a non-NATO callsign as number --- Moose Development/Moose/Core/Spawn.lua | 31 ++++++++++++++++++++------ 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index e562bfbf5..388b9aafe 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -3321,10 +3321,10 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 SpawnTemplate.units[UnitID].callsign[2] = UnitID SpawnTemplate.units[UnitID].callsign[3] = "1" SpawnTemplate.units[UnitID].callsign["name"] = tostring(callsignname)..tostring(UnitID).."1" - -- UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].callsign,1) + -- UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].callsign,1) end else - -- Ruskis + -- Russkis for UnitID = 1, #SpawnTemplate.units do SpawnTemplate.units[UnitID].callsign = math.random(1,999) end @@ -3335,7 +3335,7 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 local Callsign = SpawnTemplate.units[UnitID].callsign if Callsign then if type( Callsign ) ~= "number" then -- blue callsign - --UTILS.PrintTableToLog(Callsign,1) + -- UTILS.PrintTableToLog(Callsign,1) Callsign[2] = ((SpawnIndex - 1) % 10) + 1 local CallsignName = SpawnTemplate.units[UnitID].callsign["name"] -- #string CallsignName = string.match(CallsignName,"^(%a+)") -- 2.8 - only the part w/o numbers @@ -3377,11 +3377,11 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 end end -- VoiceCallsignNumber - if SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignNumber then + if SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignNumber and type( Callsign ) ~= "number" then SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignNumber = SpawnTemplate.units[UnitID].callsign[2] .. SpawnTemplate.units[UnitID].callsign[3] end -- VoiceCallsignLabel - if SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignLabel then + if SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignLabel and type( Callsign ) ~= "number" then local CallsignName = SpawnTemplate.units[UnitID].callsign["name"] -- #string CallsignName = string.match(CallsignName,"^(%a+)") -- 2.8 - only the part w/o numbers local label = "NY" -- Navy One exception @@ -3390,7 +3390,7 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 end SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignLabel = label end - -- UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].AddPropAircraft,1) + -- UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].AddPropAircraft,1) -- FlightLead if SpawnTemplate.units[UnitID].datalinks and SpawnTemplate.units[UnitID].datalinks.Link16 and SpawnTemplate.units[UnitID].datalinks.Link16.settings then SpawnTemplate.units[UnitID].datalinks.Link16.settings.flightLead = UnitID == 1 and true or false @@ -3399,11 +3399,28 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 if SpawnTemplate.units[UnitID].datalinks and SpawnTemplate.units[UnitID].datalinks.SADL and SpawnTemplate.units[UnitID].datalinks.SADL.settings then SpawnTemplate.units[UnitID].datalinks.SADL.settings.flightLead = UnitID == 1 and true or false end - --UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].datalinks,1) + -- UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].datalinks,1) + end + end + -- Link16 team members + for UnitID = 1, #SpawnTemplate.units do + if SpawnTemplate.units[UnitID].datalinks and SpawnTemplate.units[UnitID].datalinks.Link16 and SpawnTemplate.units[UnitID].datalinks.Link16.network then + local team = {} + local isF16 = string.find(SpawnTemplate.units[UnitID].type,"F-16",1,true) and true or false + for ID = 1, #SpawnTemplate.units do + local member = {} + member.missionUnitId = ID + if isF16 then + member.TDOA = true + end + table.insert(team,member) + end + SpawnTemplate.units[UnitID].datalinks.Link16.network.teamMembers = team end end self:T3( { "Template:", SpawnTemplate } ) + --UTILS.PrintTableToLog(SpawnTemplate,1) return SpawnTemplate end From 88e1bbd60d9c03e32a48c2ad8c49fda63930a1af Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 7 Dec 2023 11:20:43 +0100 Subject: [PATCH 17/38] #SET * Repaired SET_UNIT:GetCoordinate() --- Moose Development/Moose/Core/Set.lua | 41 ++++++++++++++++------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index fec517906..c9cbc9f3a 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -419,7 +419,11 @@ do -- SET_BASE -- @param #SET_BASE self -- @return Core.Base#BASE function SET_BASE:GetRandom() - local tablemax = table.maxn(self.Index) + local tablemax = 0 + for _,_ind in pairs(self.Index) do + tablemax = tablemax + 1 + end + --local tablemax = table.maxn(self.Index) local RandomItem = self.Set[self.Index[math.random(1,tablemax)]] self:T3( { RandomItem } ) return RandomItem @@ -561,10 +565,12 @@ do -- SET_BASE return self end - --- Iterate the SET_BASE while identifying the nearest object from a @{Core.Point#POINT_VEC2}. + --- Iterate the SET_BASE while identifying the nearest object in the set from a @{Core.Point#POINT_VEC2}. -- @param #SET_BASE self - -- @param Core.Point#POINT_VEC2 PointVec2 A @{Core.Point#POINT_VEC2} object from where to evaluate the closest object in the set. + -- @param Core.Point#POINT_VEC2 PointVec2 A @{Core.Point#COORDINATE} or @{Core.Point#POINT_VEC2} object (but **not** a simple DCS#Vec2!) from where to evaluate the closest object in the set. -- @return Core.Base#BASE The closest object. + -- @usage + -- myset:FindNearestObjectFromPointVec2( ZONE:New("Test Zone"):GetCoordinate() ) function SET_BASE:FindNearestObjectFromPointVec2( PointVec2 ) self:F2( PointVec2 ) @@ -2849,15 +2855,14 @@ do -- SET_UNIT function SET_UNIT:GetCoordinate() local Coordinate = nil - local unit = self:GetRandom() + local unit = self:GetFirst() if self:Count() == 1 and unit then return unit:GetCoordinate() end if unit then - local Coordinate = unit:GetCoordinate() - --self:F({Coordinate:GetVec3()}) - - + Coordinate = unit:GetCoordinate() + self:T2(UTILS.PrintTableToLog(Coordinate:GetVec3())) + local x1 = Coordinate.x local x2 = Coordinate.x local y1 = Coordinate.y @@ -2868,19 +2873,19 @@ do -- SET_UNIT local AvgHeading = nil local MovingCount = 0 - for UnitName, UnitData in pairs( self:GetAliveSet() ) do + for UnitName, UnitData in pairs( self.Set) do local Unit = UnitData -- Wrapper.Unit#UNIT - local Coordinate = Unit:GetCoordinate() + local Coord = Unit:GetCoordinate() - x1 = (Coordinate.x < x1) and Coordinate.x or x1 - x2 = (Coordinate.x > x2) and Coordinate.x or x2 - y1 = (Coordinate.y < y1) and Coordinate.y or y1 - y2 = (Coordinate.y > y2) and Coordinate.y or y2 - z1 = (Coordinate.y < z1) and Coordinate.z or z1 - z2 = (Coordinate.y > z2) and Coordinate.z or z2 + x1 = (Coord.x < x1) and Coord.x or x1 + x2 = (Coord.x > x2) and Coord.x or x2 + y1 = (Coord.y < y1) and Coord.y or y1 + y2 = (Coord.y > y2) and Coord.y or y2 + z1 = (Coord.y < z1) and Coord.z or z1 + z2 = (Coord.y > z2) and Coord.z or z2 - local Velocity = Coordinate:GetVelocity() + local Velocity = Coord:GetVelocity() if Velocity ~= 0 then MaxVelocity = (MaxVelocity < Velocity) and Velocity or MaxVelocity local Heading = Coordinate:GetHeading() @@ -2897,7 +2902,7 @@ do -- SET_UNIT Coordinate:SetHeading( AvgHeading ) Coordinate:SetVelocity( MaxVelocity ) - self:F( { Coordinate = Coordinate } ) + self:T2(UTILS.PrintTableToLog(Coordinate:GetVec3())) end return Coordinate From dd37a42470bff96230cd7257f141fc71f11460ff Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Thu, 7 Dec 2023 12:12:19 +0100 Subject: [PATCH 18/38] Update CTLD.lua (#2060) Changes from @Rey --- Moose Development/Moose/Ops/CTLD.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 07f15ad9d..fac6173bf 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -3671,6 +3671,7 @@ function CTLD:_RefreshF10Menus() local loadmenu = MENU_GROUP_COMMAND:New(_group,"Load crates",topcrates, self._LoadCratesNearby, self, _group, _unit) local cratesmenu = MENU_GROUP:New(_group,"Get Crates",topcrates) local packmenu = MENU_GROUP_COMMAND:New(_group, "Pack crates", topcrates, self._PackCratesNearby, self, _group, _unit) + local removecratesmenu = MENU_GROUP:New(_group, "Remove crates", topcrates) if self.usesubcats then local subcatmenus = {} @@ -3706,7 +3707,7 @@ function CTLD:_RefreshF10Menus() end end listmenu = MENU_GROUP_COMMAND:New(_group,"List crates nearby",topcrates, self._ListCratesNearby, self, _group, _unit) - listmenu = MENU_GROUP_COMMAND:New(_group,"Remove crates nearby",topcrates, self._RemoveCratesNearby, self, _group, _unit) + removecrates = MENU_GROUP_COMMAND:New(_group,"Remove crates nearby",removecratesmenu, self._RemoveCratesNearby, self, _group, _unit) local unloadmenu = MENU_GROUP_COMMAND:New(_group,"Drop crates",topcrates, self._UnloadCrates, self, _group, _unit) if not self.nobuildmenu then local buildmenu = MENU_GROUP_COMMAND:New(_group,"Build crates",topcrates, self._BuildCrates, self, _group, _unit) From 6f473faa924942d2509b27ce5014952c7b87b9f7 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Thu, 7 Dec 2023 12:14:59 +0100 Subject: [PATCH 19/38] Update Message.lua #2059 fixed --- Moose Development/Moose/Core/Message.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Core/Message.lua b/Moose Development/Moose/Core/Message.lua index 0feeecd2b..8b6da3cdd 100644 --- a/Moose Development/Moose/Core/Message.lua +++ b/Moose Development/Moose/Core/Message.lua @@ -98,7 +98,7 @@ function MESSAGE:New( MessageText, MessageDuration, MessageCategory, ClearScreen self.MessageType = nil - -- When no MessageCategory is given, we don't show it as a title... + -- When no MessageCategory is given, we don't show it as a title... if MessageCategory and MessageCategory ~= "" then if MessageCategory:sub( -1 ) ~= "\n" then self.MessageCategory = MessageCategory .. ": " @@ -368,7 +368,7 @@ function MESSAGE:ToCoalition( CoalitionSide, Settings ) if CoalitionSide then if self.MessageDuration ~= 0 then self:T( self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ) .. " / " .. self.MessageDuration ) - trigger.action.outTextForCoalition( CoalitionSide, self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ), self.MessageDuration, self.ClearScreen ) + trigger.action.outTextForCoalition( CoalitionSide, self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ), self.MessageDuration, self.ClearScreen ) end end From 9ce1d360d6c921ad072bd3b077d3f89a772a9c37 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 7 Dec 2023 13:31:40 +0100 Subject: [PATCH 20/38] #CTLD * Spawn dropped troops in a nice circle 5m (hover: 1.5m) left of the he --- Moose Development/Moose/Ops/CTLD.lua | 54 +++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index fac6173bf..095eb28de 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -24,7 +24,7 @@ -- @module Ops.CTLD -- @image OPS_CTLD.jpg --- Last Update November 2023 +-- Last Update December 2023 do @@ -1228,7 +1228,7 @@ CTLD.UnitTypeCapabilities = { --- CTLD class version. -- @field #string version -CTLD.version="1.0.43" +CTLD.version="1.0.44" --- Instantiate a new CTLD. -- @param #CTLD self @@ -3010,6 +3010,35 @@ function CTLD:IsHercules(Unit) end end + +--- (Internal) Function to set troops positions of a template to a nice circle +-- @param #CTLD self +-- @param Core.Point#COORDINATE Coordinate Start coordinate to use +-- @param #number Radius Radius to be used +-- @param #number Heading Heading starting with +-- @param #string Template The group template name +-- @return #table Positions The positions table +function CTLD:_GetUnitPositions(Coordinate,Radius,Heading,Template) + local Positions = {} + local template = _DATABASE:GetGroupTemplate(Template) + UTILS.PrintTableToLog(template) + local numbertroops = #template.units + local newcenter = Coordinate:Translate(Radius,((Heading+270)%360)) + for i=1,360,math.floor(360/numbertroops) do + local phead = ((Heading+270+i)%360) + local post = newcenter:Translate(Radius,phead) + local pos1 = post:GetVec2() + local p1t = { + x = pos1.x, + y = pos1.y, + heading = phead, + } + table.insert(Positions,p1t) + end + UTILS.PrintTableToLog(Positions) + return Positions +end + --- (Internal) Function to unload troops from heli. -- @param #CTLD self -- @param Wrapper.Group#GROUP Group @@ -3061,14 +3090,29 @@ function CTLD:_UnloadTroops(Group, Unit) zoneradius = Unit:GetVelocityMPS() or 100 end local zone = ZONE_GROUP:New(string.format("Unload zone-%s",unitname),Group,zoneradius*factor) - local randomcoord = zone:GetRandomCoordinate(10,30*factor):GetVec2() + local randomcoord = zone:GetRandomCoordinate(10,30*factor) --:GetVec2() + local heading = Group:GetHeading() or 0 + -- Spawn troops left from us, closer when hovering, further off when landed + if hoverunload or grounded then + randomcoord = Group:GetCoordinate() + -- slightly left from us + local Angle = (heading+270)%360 + local offset = hoverunload and 1.5 or 5 + randomcoord:Translate(offset,Angle,nil,true) + end + local tempcount = 0 for _,_template in pairs(temptable) do self.TroopCounter = self.TroopCounter + 1 + tempcount = tempcount+1 local alias = string.format("%s-%d", _template, math.random(1,100000)) + local rad = 2.5+tempcount + local Positions = self:_GetUnitPositions(randomcoord,rad,heading,_template) self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias) - :InitRandomizeUnits(true,20,2) + --:InitRandomizeUnits(true,20,2) + --:InitHeading(heading) :InitDelayOff() - :SpawnFromVec2(randomcoord) + :InitSetUnitAbsolutePositions(Positions) + :SpawnFromVec2(randomcoord:GetVec2()) self:__TroopsDeployed(1, Group, Unit, self.DroppedTroops[self.TroopCounter],type) end -- template loop cargo:SetWasDropped(true) From c770f4cb680c546fd984e7ca72e16f756c4654a8 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 7 Dec 2023 13:46:32 +0100 Subject: [PATCH 21/38] #ZONE Docu fixes --- Moose Development/Moose/Core/Zone.lua | 32 ++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index cb2648d0d..ab2f23949 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -10,7 +10,7 @@ -- * Create moving zones around a unit. -- * Create moving zones around a group. -- * Provide the zone behavior. Some zones are static, while others are moveable. --- * Enquiry if a coordinate is within a zone. +-- * Enquire if a coordinate is within a zone. -- * Smoke zones. -- * Set a zone probability to control zone selection. -- * Get zone coordinates. @@ -42,7 +42,7 @@ -- * @{#ZONE_UNIT}: The ZONE_UNIT class defines by a zone around a @{Wrapper.Unit#UNIT} with a radius. -- * @{#ZONE_GROUP}: The ZONE_GROUP class defines by a zone around a @{Wrapper.Group#GROUP} with a radius. -- * @{#ZONE_POLYGON}: The ZONE_POLYGON class defines by a sequence of @{Wrapper.Group#GROUP} waypoints within the Mission Editor, forming a polygon. --- * @{#ZONE_OVAL}: The ZONE_OVAL class isdefined by a center point, major axis, minor axis, and angle. +-- * @{#ZONE_OVAL}: The ZONE_OVAL class is defined by a center point, major axis, minor axis, and angle. -- -- === -- @@ -2232,7 +2232,14 @@ end --- ZONE_POLYGON to accurately find a point inside a polygon; as well as getting the correct surface area of --- a polygon. -- @type _ZONE_TRIANGLE --- @extends #BASE +-- @extends Core.Zone#ZONE_BASE + +--- ## _ZONE_TRIANGLE class, extends @{#ZONE_BASE} +-- +-- _ZONE_TRIANGLE class is a helper class for ZONE_POLYGON +-- This class implements the inherited functions from @{#ZONE_BASE} taking into account the own zone format and properties. +-- +-- @field #_ZONE_TRIANGLE _ZONE_TRIANGLE = { ClassName="ZONE_TRIANGLE", Points={}, @@ -2241,7 +2248,12 @@ _ZONE_TRIANGLE = { SurfaceArea=0, DrawIDs={} } - +--- +-- @param #_ZONE_TRIANGLE self +-- @param DCS#Vec p1 +-- @param DCS#Vec p2 +-- @param DCS#Vec p3 +-- @return #_ZONE_TRIANGLE self function _ZONE_TRIANGLE:New(p1, p2, p3) local self = BASE:Inherit(self, ZONE_BASE:New()) self.Points = {p1, p2, p3} @@ -2260,6 +2272,7 @@ function _ZONE_TRIANGLE:New(p1, p2, p3) end --- Checks if a point is contained within the triangle. +-- @param #_ZONE_TRIANGLE self -- @param #table pt The point to check -- @param #table points (optional) The points of the triangle, or 3 other points if you're just using the TRIANGLE class without an object of it -- @return #bool True if the point is contained, false otherwise @@ -2281,6 +2294,7 @@ function _ZONE_TRIANGLE:ContainsPoint(pt, points) end --- Returns a random Vec2 within the triangle. +-- @param #_ZONE_TRIANGLE self -- @param #table points The points of the triangle, or 3 other points if you're just using the TRIANGLE class without an object of it -- @return #table The random Vec2 function _ZONE_TRIANGLE:GetRandomVec2(points) @@ -2296,6 +2310,8 @@ function _ZONE_TRIANGLE:GetRandomVec2(points) end --- Draw the triangle +-- @param #_ZONE_TRIANGLE self +-- @return #table of draw IDs function _ZONE_TRIANGLE:Draw(Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly) Coalition=Coalition or -1 @@ -2390,7 +2406,8 @@ end --- Triangulates the polygon. --- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Polygon.lua --- @return #table The #_TRIANGLE list that make up +-- @param #ZONE_POLYGON_BASE self +-- @return #table The #_ZONE_TRIANGLE list that makes up the polygon function ZONE_POLYGON_BASE:_Triangulate() local points = self._.Polygon local triangles = {} @@ -2519,6 +2536,7 @@ end --- Calculates the surface area of the polygon. The surface area is the sum of the areas of the triangles that make up the polygon. --- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Polygon.lua +-- @param #ZONE_POLYGON_BASE self -- @return #number The surface area of the polygon function ZONE_POLYGON_BASE:_CalculateSurfaceArea() local area = 0 @@ -3590,7 +3608,8 @@ end do -- ZONE_ELASTIC - + + --- -- @type ZONE_ELASTIC -- @field #table points Points in 2D. -- @field #table setGroups Set of GROUPs. @@ -3791,6 +3810,7 @@ end do -- ZONE_AIRBASE + --- -- @type ZONE_AIRBASE -- @field #boolean isShip If `true`, airbase is a ship. -- @field #boolean isHelipad If `true`, airbase is a helipad. From ff6704f123ca0517c6ea524607c883cc2a2f2012 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 7 Dec 2023 15:11:53 +0100 Subject: [PATCH 22/38] #ZONE docu fixes --- Moose Development/Moose/Core/Zone.lua | 485 +++++++++++++------------- 1 file changed, 246 insertions(+), 239 deletions(-) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index ab2f23949..09ef67c2f 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -480,12 +480,14 @@ function ZONE_BASE:UndrawZone(Delay) if Delay and Delay>0 then self:ScheduleOnce(Delay, ZONE_BASE.UndrawZone, self) else - if self.DrawID and type(self.DrawID) ~= "table" then - UTILS.RemoveMark(self.DrawID) - else -- DrawID is a table with a collections of mark ids, as used in ZONE_POLYGON + if self.DrawID then + if type(self.DrawID) ~= "table" then + UTILS.RemoveMark(self.DrawID) + else -- DrawID is a table with a collections of mark ids, as used in ZONE_POLYGON for _, mark_id in pairs(self.DrawID) do - UTILS.RemoveMark(mark_id) + UTILS.RemoveMark(mark_id) end + end end end return self @@ -1999,234 +2001,6 @@ function ZONE_GROUP:GetRandomPointVec2( inner, outer ) end ---- ZONE_OVAL created from a center point, major axis, minor axis, and angle. --- Ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Oval.lua --- @type ZONE_OVAL --- @extends Core.Zone#ZONE_BASE - ---- ## ZONE_OVAL class, extends @{#ZONE_BASE} --- --- The ZONE_OVAL class is defined by a center point, major axis, minor axis, and angle. --- This class implements the inherited functions from @{#ZONE_BASE} taking into account the own zone format and properties. --- --- @field #ZONE_OVAL -ZONE_OVAL = { - ClassName = "OVAL", - ZoneName="", - MajorAxis = nil, - MinorAxis = nil, - Angle = 0, - DrawPoly = nil -- let's just use a ZONE_POLYGON to draw the ZONE_OVAL on the map -} - ---- Creates a new ZONE_OVAL from a center point, major axis, minor axis, and angle. ---- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Oval.lua --- @param #table vec2 The center point of the oval --- @param #number major_axis The major axis of the oval --- @param #number minor_axis The minor axis of the oval --- @param #number angle The angle of the oval --- @return #ZONE_OVAL The new oval -function ZONE_OVAL:New(name, vec2, major_axis, minor_axis, angle) - self = BASE:Inherit(self, ZONE_BASE:New()) - self.ZoneName = name - self.CenterVec2 = vec2 - self.MajorAxis = major_axis - self.MinorAxis = minor_axis - self.Angle = angle or 0 - - _DATABASE:AddZone(name, self) - - return self -end - ---- Constructor to create a ZONE_OVAL instance, taking the name of a drawing made with the draw tool in the Mission Editor. ---- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Oval.lua --- @param #ZONE_OVAL self --- @param #string DrawingName The name of the drawing in the Mission Editor --- @return #ZONE_OVAL self -function ZONE_OVAL:NewFromDrawing(DrawingName) - self = BASE:Inherit(self, ZONE_BASE:New(DrawingName)) - for _, layer in pairs(env.mission.drawings.layers) do - for _, object in pairs(layer["objects"]) do - if string.find(object["name"], DrawingName, 1, true) then - if object["polygonMode"] == "oval" then - self.CenterVec2 = { x = object["mapX"], y = object["mapY"] } - self.MajorAxis = object["r1"] - self.MinorAxis = object["r2"] - self.Angle = object["angle"] - - end - end - end - end - - _DATABASE:AddZone(DrawingName, self) - - return self -end - ---- Gets the major axis of the oval. --- @param #ZONE_OVAL self --- @return #number The major axis of the oval -function ZONE_OVAL:GetMajorAxis() - return self.MajorAxis -end - ---- Gets the minor axis of the oval. --- @param #ZONE_OVAL self --- @return #number The minor axis of the oval -function ZONE_OVAL:GetMinorAxis() - return self.MinorAxis -end - ---- Gets the angle of the oval. --- @param #ZONE_OVAL self --- @return #number The angle of the oval -function ZONE_OVAL:GetAngle() - return self.Angle -end - ---- Returns a the center point of the oval --- @param #ZONE_OVAL self --- @return #table The center Vec2 -function ZONE_OVAL:GetVec2() - return self.CenterVec2 -end - ---- Checks if a point is contained within the oval. --- @param #ZONE_OVAL self --- @param #table point The point to check --- @return #bool True if the point is contained, false otherwise -function ZONE_OVAL:IsVec2InZone(vec2) - local cos, sin = math.cos, math.sin - local dx = vec2.x - self.CenterVec2.x - local dy = vec2.y - self.CenterVec2.y - local rx = dx * cos(self.Angle) + dy * sin(self.Angle) - local ry = -dx * sin(self.Angle) + dy * cos(self.Angle) - return rx * rx / (self.MajorAxis * self.MajorAxis) + ry * ry / (self.MinorAxis * self.MinorAxis) <= 1 -end - ---- Calculates the bounding box of the oval. The bounding box is the smallest rectangle that contains the oval. --- @param #ZONE_OVAL self --- @return #table The bounding box of the oval -function ZONE_OVAL:GetBoundingSquare() - local min_x = self.CenterVec2.x - self.MajorAxis - local min_y = self.CenterVec2.y - self.MinorAxis - local max_x = self.CenterVec2.x + self.MajorAxis - local max_y = self.CenterVec2.y + self.MinorAxis - - return { - {x=min_x, y=min_x}, {x=max_x, y=min_y}, {x=max_x, y=max_y}, {x=min_x, y=max_y} - } -end - ---- Find points on the edge of the oval --- @param #ZONE_OVAL self --- @param #number num_points How many points should be found. More = smoother shape --- @return #table Points on he edge -function ZONE_OVAL:PointsOnEdge(num_points) - num_points = num_points or 40 - local points = {} - local dtheta = 2 * math.pi / num_points - - for i = 0, num_points - 1 do - local theta = i * dtheta - local x = self.CenterVec2.x + self.MajorAxis * math.cos(theta) * math.cos(self.Angle) - self.MinorAxis * math.sin(theta) * math.sin(self.Angle) - local y = self.CenterVec2.y + self.MajorAxis * math.cos(theta) * math.sin(self.Angle) + self.MinorAxis * math.sin(theta) * math.cos(self.Angle) - table.insert(points, {x = x, y = y}) - end - - return points -end - ---- Returns a random Vec2 within the oval. --- @param #ZONE_OVAL self --- @return #table The random Vec2 -function ZONE_OVAL:GetRandomVec2() - local theta = math.rad(self.Angle) - - local random_point = math.sqrt(math.random()) --> uniformly - --local random_point = math.random() --> more clumped around center - local phi = math.random() * 2 * math.pi - local x_c = random_point * math.cos(phi) - local y_c = random_point * math.sin(phi) - local x_e = x_c * self.MajorAxis - local y_e = y_c * self.MinorAxis - local rx = (x_e * math.cos(theta) - y_e * math.sin(theta)) + self.CenterVec2.x - local ry = (x_e * math.sin(theta) + y_e * math.cos(theta)) + self.CenterVec2.y - - return {x=rx, y=ry} -end - ---- Define a random @{Core.Point#POINT_VEC2} within the zone. --- @param #ZONE_OVAL self --- @return Core.Point#POINT_VEC2 The PointVec2 coordinates. -function ZONE_OVAL:GetRandomPointVec2() - return POINT_VEC2:NewFromVec2(self:GetRandomVec2()) -end - ---- Define a random @{Core.Point#POINT_VEC2} within the zone. --- @param #ZONE_OVAL self --- @return Core.Point#POINT_VEC2 The PointVec2 coordinates. -function ZONE_OVAL:GetRandomPointVec3() - return POINT_VEC2:NewFromVec3(self:GetRandomVec2()) -end - ---- Draw the zone on the F10 map. ---- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Oval.lua --- @param #ZONE_OVAL self --- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All. --- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red. --- @param #number Alpha Transparency [0,1]. Default 1. --- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value. -- doesn't seem to work --- @param #number FillAlpha Transparency [0,1]. Default 0.15. -- doesn't seem to work --- @param #number LineType Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid. --- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false. --- @return #ZONE_OVAL self -function ZONE_OVAL:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType) - Coalition = Coalition or self:GetDrawCoalition() - - -- Set draw coalition. - self:SetDrawCoalition(Coalition) - - Color = Color or self:GetColorRGB() - Alpha = Alpha or 1 - - -- Set color. - self:SetColor(Color, Alpha) - - FillColor = FillColor or self:GetFillColorRGB() - if not FillColor then - UTILS.DeepCopy(Color) - end - FillAlpha = FillAlpha or self:GetFillColorAlpha() - if not FillAlpha then - FillAlpha = 0.15 - end - - LineType = LineType or 1 - - -- Set fill color -----------> has fill color worked in recent versions of DCS? - -- doing something like - -- - -- trigger.action.markupToAll(7, -1, 501, p.Coords[1]:GetVec3(), p.Coords[2]:GetVec3(),p.Coords[3]:GetVec3(),p.Coords[4]:GetVec3(),{1,0,0, 1}, {1,0,0, 1}, 4, false, Text or "") - -- - -- doesn't seem to fill in the shape for an n-sided polygon - self:SetFillColor(FillColor, FillAlpha) - - self.DrawPoly = ZONE_POLYGON:NewFromPointsArray(self.ZoneName, self:PointsOnEdge(80)) - self.DrawPoly:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType) -end - ---- Remove drawing from F10 map --- @param #ZONE_OVAL self -function ZONE_OVAL:UndrawZone() - if self.DrawPoly then - self.DrawPoly:UndrawZone() - end -end - - --- Ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Triangle.lua --- This triangle "zone" is not really to be used on its own, it only serves as building blocks for --- ZONE_POLYGON to accurately find a point inside a polygon; as well as getting the correct surface area of @@ -2394,13 +2168,13 @@ function ZONE_POLYGON_BASE:New( ZoneName, PointsArray ) self._.Polygon[i].y = PointsArray[i].y end + -- triangulate the polygon so we can work with it + self._Triangles = self:_Triangulate() + -- set the polygon's surface area + self.SurfaceArea = self:_CalculateSurfaceArea() + end - -- triangulate the polygon so we can work with it - self._Triangles = self:_Triangulate() - -- set the polygon's surface area - self.SurfaceArea = self:_CalculateSurfaceArea() - return self end @@ -3106,9 +2880,13 @@ function ZONE_POLYGON_BASE:Boundary(Coalition, Color, Radius, Alpha, Segments, C return self end +do -- Zone_Polygon + + --- -- @type ZONE_POLYGON -- @extends #ZONE_POLYGON_BASE +-- @extends #ZONE_BASE --- The ZONE_POLYGON class defined by a sequence of @{Wrapper.Group#GROUP} waypoints within the Mission Editor, forming a polygon, OR by drawings made with the Draw tool @@ -3140,8 +2918,7 @@ end -- -- This class has been updated to use a accurate way of generating random points inside the polygon without having to use trial and error guesses. -- You can also get the surface area of the polygon now, handy if you want measure which coalition has the largest captured area, for example. - - +-- -- @field #ZONE_POLYGON ZONE_POLYGON = { ClassName="ZONE_POLYGON", @@ -3606,6 +3383,7 @@ function ZONE_POLYGON:IsNoneInZone() return self:CountScannedCoalitions() == 0 end +end do -- ZONE_ELASTIC @@ -3808,6 +3586,235 @@ do -- ZONE_ELASTIC end + + +--- ZONE_OVAL created from a center point, major axis, minor axis, and angle. +-- Ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Oval.lua +-- @type ZONE_OVAL +-- @extends Core.Zone#ZONE_BASE + +--- ## ZONE_OVAL class, extends @{#ZONE_BASE} +-- +-- The ZONE_OVAL class is defined by a center point, major axis, minor axis, and angle. +-- This class implements the inherited functions from @{#ZONE_BASE} taking into account the own zone format and properties. +-- +-- @field #ZONE_OVAL +ZONE_OVAL = { + ClassName = "OVAL", + ZoneName="", + MajorAxis = nil, + MinorAxis = nil, + Angle = 0, + DrawPoly = nil -- let's just use a ZONE_POLYGON to draw the ZONE_OVAL on the map +} + +--- Creates a new ZONE_OVAL from a center point, major axis, minor axis, and angle. +--- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Oval.lua +-- @param #table vec2 The center point of the oval +-- @param #number major_axis The major axis of the oval +-- @param #number minor_axis The minor axis of the oval +-- @param #number angle The angle of the oval +-- @return #ZONE_OVAL The new oval +function ZONE_OVAL:New(name, vec2, major_axis, minor_axis, angle) + self = BASE:Inherit(self, ZONE_BASE:New()) + self.ZoneName = name + self.CenterVec2 = vec2 + self.MajorAxis = major_axis + self.MinorAxis = minor_axis + self.Angle = angle or 0 + + _DATABASE:AddZone(name, self) + + return self +end + +--- Constructor to create a ZONE_OVAL instance, taking the name of a drawing made with the draw tool in the Mission Editor. +--- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Oval.lua +-- @param #ZONE_OVAL self +-- @param #string DrawingName The name of the drawing in the Mission Editor +-- @return #ZONE_OVAL self +function ZONE_OVAL:NewFromDrawing(DrawingName) + self = BASE:Inherit(self, ZONE_BASE:New(DrawingName)) + for _, layer in pairs(env.mission.drawings.layers) do + for _, object in pairs(layer["objects"]) do + if string.find(object["name"], DrawingName, 1, true) then + if object["polygonMode"] == "oval" then + self.CenterVec2 = { x = object["mapX"], y = object["mapY"] } + self.MajorAxis = object["r1"] + self.MinorAxis = object["r2"] + self.Angle = object["angle"] + + end + end + end + end + + _DATABASE:AddZone(DrawingName, self) + + return self +end + +--- Gets the major axis of the oval. +-- @param #ZONE_OVAL self +-- @return #number The major axis of the oval +function ZONE_OVAL:GetMajorAxis() + return self.MajorAxis +end + +--- Gets the minor axis of the oval. +-- @param #ZONE_OVAL self +-- @return #number The minor axis of the oval +function ZONE_OVAL:GetMinorAxis() + return self.MinorAxis +end + +--- Gets the angle of the oval. +-- @param #ZONE_OVAL self +-- @return #number The angle of the oval +function ZONE_OVAL:GetAngle() + return self.Angle +end + +--- Returns a the center point of the oval +-- @param #ZONE_OVAL self +-- @return #table The center Vec2 +function ZONE_OVAL:GetVec2() + return self.CenterVec2 +end + +--- Checks if a point is contained within the oval. +-- @param #ZONE_OVAL self +-- @param #table point The point to check +-- @return #bool True if the point is contained, false otherwise +function ZONE_OVAL:IsVec2InZone(vec2) + local cos, sin = math.cos, math.sin + local dx = vec2.x - self.CenterVec2.x + local dy = vec2.y - self.CenterVec2.y + local rx = dx * cos(self.Angle) + dy * sin(self.Angle) + local ry = -dx * sin(self.Angle) + dy * cos(self.Angle) + return rx * rx / (self.MajorAxis * self.MajorAxis) + ry * ry / (self.MinorAxis * self.MinorAxis) <= 1 +end + +--- Calculates the bounding box of the oval. The bounding box is the smallest rectangle that contains the oval. +-- @param #ZONE_OVAL self +-- @return #table The bounding box of the oval +function ZONE_OVAL:GetBoundingSquare() + local min_x = self.CenterVec2.x - self.MajorAxis + local min_y = self.CenterVec2.y - self.MinorAxis + local max_x = self.CenterVec2.x + self.MajorAxis + local max_y = self.CenterVec2.y + self.MinorAxis + + return { + {x=min_x, y=min_x}, {x=max_x, y=min_y}, {x=max_x, y=max_y}, {x=min_x, y=max_y} + } +end + +--- Find points on the edge of the oval +-- @param #ZONE_OVAL self +-- @param #number num_points How many points should be found. More = smoother shape +-- @return #table Points on he edge +function ZONE_OVAL:PointsOnEdge(num_points) + num_points = num_points or 40 + local points = {} + local dtheta = 2 * math.pi / num_points + + for i = 0, num_points - 1 do + local theta = i * dtheta + local x = self.CenterVec2.x + self.MajorAxis * math.cos(theta) * math.cos(self.Angle) - self.MinorAxis * math.sin(theta) * math.sin(self.Angle) + local y = self.CenterVec2.y + self.MajorAxis * math.cos(theta) * math.sin(self.Angle) + self.MinorAxis * math.sin(theta) * math.cos(self.Angle) + table.insert(points, {x = x, y = y}) + end + + return points +end + +--- Returns a random Vec2 within the oval. +-- @param #ZONE_OVAL self +-- @return #table The random Vec2 +function ZONE_OVAL:GetRandomVec2() + local theta = math.rad(self.Angle) + + local random_point = math.sqrt(math.random()) --> uniformly + --local random_point = math.random() --> more clumped around center + local phi = math.random() * 2 * math.pi + local x_c = random_point * math.cos(phi) + local y_c = random_point * math.sin(phi) + local x_e = x_c * self.MajorAxis + local y_e = y_c * self.MinorAxis + local rx = (x_e * math.cos(theta) - y_e * math.sin(theta)) + self.CenterVec2.x + local ry = (x_e * math.sin(theta) + y_e * math.cos(theta)) + self.CenterVec2.y + + return {x=rx, y=ry} +end + +--- Define a random @{Core.Point#POINT_VEC2} within the zone. +-- @param #ZONE_OVAL self +-- @return Core.Point#POINT_VEC2 The PointVec2 coordinates. +function ZONE_OVAL:GetRandomPointVec2() + return POINT_VEC2:NewFromVec2(self:GetRandomVec2()) +end + +--- Define a random @{Core.Point#POINT_VEC2} within the zone. +-- @param #ZONE_OVAL self +-- @return Core.Point#POINT_VEC2 The PointVec2 coordinates. +function ZONE_OVAL:GetRandomPointVec3() + return POINT_VEC2:NewFromVec3(self:GetRandomVec2()) +end + +--- Draw the zone on the F10 map. +--- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Oval.lua +-- @param #ZONE_OVAL self +-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All. +-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red. +-- @param #number Alpha Transparency [0,1]. Default 1. +-- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value. -- doesn't seem to work +-- @param #number FillAlpha Transparency [0,1]. Default 0.15. -- doesn't seem to work +-- @param #number LineType Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid. +-- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false. +-- @return #ZONE_OVAL self +function ZONE_OVAL:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType) + Coalition = Coalition or self:GetDrawCoalition() + + -- Set draw coalition. + self:SetDrawCoalition(Coalition) + + Color = Color or self:GetColorRGB() + Alpha = Alpha or 1 + + -- Set color. + self:SetColor(Color, Alpha) + + FillColor = FillColor or self:GetFillColorRGB() + if not FillColor then + UTILS.DeepCopy(Color) + end + FillAlpha = FillAlpha or self:GetFillColorAlpha() + if not FillAlpha then + FillAlpha = 0.15 + end + + LineType = LineType or 1 + + -- Set fill color -----------> has fill color worked in recent versions of DCS? + -- doing something like + -- + -- trigger.action.markupToAll(7, -1, 501, p.Coords[1]:GetVec3(), p.Coords[2]:GetVec3(),p.Coords[3]:GetVec3(),p.Coords[4]:GetVec3(),{1,0,0, 1}, {1,0,0, 1}, 4, false, Text or "") + -- + -- doesn't seem to fill in the shape for an n-sided polygon + self:SetFillColor(FillColor, FillAlpha) + + self.DrawPoly = ZONE_POLYGON:NewFromPointsArray(self.ZoneName, self:PointsOnEdge(80)) + self.DrawPoly:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType) +end + +--- Remove drawing from F10 map +-- @param #ZONE_OVAL self +function ZONE_OVAL:UndrawZone() + if self.DrawPoly then + self.DrawPoly:UndrawZone() + end +end + do -- ZONE_AIRBASE --- From 6903e252d2461b7827a5b07df2bfff5937044f52 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 7 Dec 2023 16:08:47 +0100 Subject: [PATCH 23/38] #UTILS * Nicer PrintTableToLog() --- Moose Development/Moose/Utilities/Utils.lua | 23 ++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index f8fbfe2eb..9fe3d9823 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -443,22 +443,35 @@ end --- Print a table to log in a nice format -- @param #table table The table to print --- @param #number indent Number of idents +-- @param #number indent Number of indents +-- @return #string text Text created on the fly of the log output function UTILS.PrintTableToLog(table, indent) + local text = "\n" if not table then env.warning("No table passed!") - return + return nil end if not indent then indent = 0 end for k, v in pairs(table) do + if string.find(k," ") then k='"'..k..'"'end if type(v) == "table" then env.info(string.rep(" ", indent) .. tostring(k) .. " = {") - UTILS.PrintTableToLog(v, indent + 1) - env.info(string.rep(" ", indent) .. "}") + text = text ..string.rep(" ", indent) .. tostring(k) .. " = {\n" + text = text .. tostring(UTILS.PrintTableToLog(v, indent + 1)).."\n" + env.info(string.rep(" ", indent) .. "},") + text = text .. string.rep(" ", indent) .. "},\n" else - env.info(string.rep(" ", indent) .. tostring(k) .. " = " .. tostring(v)) + local value + if tostring(v) == "true" or tostring(v) == "false" or tonumber(v) ~= nil then + value=v + else + value = '"'..tostring(v)..'"' + end + env.info(string.rep(" ", indent) .. tostring(k) .. " = " .. tostring(value)..",\n") + text = text .. string.rep(" ", indent) .. tostring(k) .. " = " .. tostring(value)..",\n" end end + return text end --- Returns table in a easy readable string representation. From b3a006096ce2a83a467697026251489437a2b3a6 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 9 Dec 2023 13:03:34 +0100 Subject: [PATCH 24/38] fixes --- Moose Development/Moose/Core/Set.lua | 88 +++++++++++------------ Moose Development/Moose/Wrapper/Group.lua | 1 + 2 files changed, 41 insertions(+), 48 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index c9cbc9f3a..43fbd8fce 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -2854,58 +2854,50 @@ do -- SET_UNIT -- @return Core.Point#COORDINATE The center coordinate of all the units in the set, including heading in degrees and speed in mps in case of moving units. function SET_UNIT:GetCoordinate() - local Coordinate = nil - local unit = self:GetFirst() - if self:Count() == 1 and unit then - return unit:GetCoordinate() - end - if unit then - Coordinate = unit:GetCoordinate() - self:T2(UTILS.PrintTableToLog(Coordinate:GetVec3())) - - local x1 = Coordinate.x - local x2 = Coordinate.x - local y1 = Coordinate.y - local y2 = Coordinate.y - local z1 = Coordinate.z - local z2 = Coordinate.z - local MaxVelocity = 0 - local AvgHeading = nil - local MovingCount = 0 - - for UnitName, UnitData in pairs( self.Set) do - - local Unit = UnitData -- Wrapper.Unit#UNIT - local Coord = Unit:GetCoordinate() - - x1 = (Coord.x < x1) and Coord.x or x1 - x2 = (Coord.x > x2) and Coord.x or x2 - y1 = (Coord.y < y1) and Coord.y or y1 - y2 = (Coord.y > y2) and Coord.y or y2 - z1 = (Coord.y < z1) and Coord.z or z1 - z2 = (Coord.y > z2) and Coord.z or z2 - - local Velocity = Coord:GetVelocity() - if Velocity ~= 0 then - MaxVelocity = (MaxVelocity < Velocity) and Velocity or MaxVelocity - local Heading = Coordinate:GetHeading() - AvgHeading = AvgHeading and (AvgHeading + Heading) or Heading - MovingCount = MovingCount + 1 + local function GetSetVec3(units) + -- Init. + local x=0 + local y=0 + local z=0 + local n=0 + -- Loop over all units. + for _,unit in pairs(units) do + local vec3=nil --DCS#Vec3 + if unit and unit:IsAlive() then + vec3 = unit:GetVec3() + end + if vec3 then + -- Sum up posits. + x=x+vec3.x + y=y+vec3.y + z=z+vec3.z + -- Increase counter. + n=n+1 end end - - AvgHeading = AvgHeading and (AvgHeading / MovingCount) - - Coordinate.x = (x2 - x1) / 2 + x1 - Coordinate.y = (y2 - y1) / 2 + y1 - Coordinate.z = (z2 - z1) / 2 + z1 - Coordinate:SetHeading( AvgHeading ) - Coordinate:SetVelocity( MaxVelocity ) - - self:T2(UTILS.PrintTableToLog(Coordinate:GetVec3())) + if n>0 then + -- Average. + local Vec3={x=x/n, y=y/n, z=z/n} --DCS#Vec3 + return Vec3 + end + return nil + end + + local Coordinate = nil + local Vec3 = GetSetVec3(self.Set) + if Vec3 then + Coordinate = COORDINATE:NewFromVec3(Vec3) end - return Coordinate + if Coordinate then + local heading = self:GetHeading() or 0 + local velocity = self:GetVelocity() or 0 + Coordinate:SetHeading( heading ) + Coordinate:SetVelocity( velocity ) + self:I(UTILS.PrintTableToLog(Coordinate)) + end + + return Coordinate end --- Get the maximum velocity of the SET_UNIT. diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 9ee2d2c21..fe0833189 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -1157,6 +1157,7 @@ function GROUP:GetAverageCoordinate() local coord = COORDINATE:NewFromVec3(vec3) local Heading = self:GetHeading() coord.Heading = Heading + return coord else BASE:E( { "Cannot GetAverageCoordinate", Group = self, Alive = self:IsAlive() } ) return nil From 6b270916c4b192b9d671801ad3e0abd324e83cd5 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 9 Dec 2023 13:53:27 +0100 Subject: [PATCH 25/38] # DETECTION_BASE * Added `SetRadarBlur(minheight,thresheight,thresblur)` --- .../Moose/Functional/Detection.lua | 68 ++++++++++++++----- 1 file changed, 50 insertions(+), 18 deletions(-) diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index 9fc086cd9..2ec01a349 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -39,7 +39,7 @@ do -- DETECTION_BASE - --- @type DETECTION_BASE + -- @type DETECTION_BASE -- @field Core.Set#SET_GROUP DetectionSetGroup The @{Core.Set} of GROUPs in the Forward Air Controller role. -- @field DCS#Distance DetectionRange The range till which targets are accepted to be detected. -- @field #DETECTION_BASE.DetectedObjects DetectedObjects The list of detected objects. @@ -269,10 +269,10 @@ do -- DETECTION_BASE DetectedItemsByIndex = {}, } - --- @type DETECTION_BASE.DetectedObjects + -- @type DETECTION_BASE.DetectedObjects -- @list <#DETECTION_BASE.DetectedObject> - --- @type DETECTION_BASE.DetectedObject + -- @type DETECTION_BASE.DetectedObject -- @field #string Name -- @field #boolean IsVisible -- @field #boolean KnowType @@ -284,7 +284,7 @@ do -- DETECTION_BASE -- @field #boolean LastPos -- @field #number LastVelocity - --- @type DETECTION_BASE.DetectedItems + -- @type DETECTION_BASE.DetectedItems -- @list <#DETECTION_BASE.DetectedItem> --- Detected item data structure. @@ -522,7 +522,7 @@ do -- DETECTION_BASE do -- State Transition Handling - --- @param #DETECTION_BASE self + -- @param #DETECTION_BASE self -- @param #string From The From State string. -- @param #string Event The Event string. -- @param #string To The To State string. @@ -530,7 +530,7 @@ do -- DETECTION_BASE self:__Detect( 1 ) end - --- @param #DETECTION_BASE self + -- @param #DETECTION_BASE self -- @param #string From The From State string. -- @param #string Event The Event string. -- @param #string To The To State string. @@ -570,7 +570,7 @@ do -- DETECTION_BASE end - --- @param #DETECTION_BASE self + -- @param #DETECTION_BASE self -- @param #number The amount of alive recce. function DETECTION_BASE:CountAliveRecce() @@ -578,7 +578,7 @@ do -- DETECTION_BASE end - --- @param #DETECTION_BASE self + -- @param #DETECTION_BASE self function DETECTION_BASE:ForEachAliveRecce( IteratorFunction, ... ) self:F2( arg ) @@ -587,7 +587,7 @@ do -- DETECTION_BASE return self end - --- @param #DETECTION_BASE self + -- @param #DETECTION_BASE self -- @param #string From The From State string. -- @param #string Event The Event string. -- @param #string To The To State string. @@ -712,6 +712,22 @@ do -- DETECTION_BASE end end + -- Calculate radar blue probability + + if self.RadarBlur then + local minheight = self.RadarBlurMinHeight or 250 -- meters + local thresheight = self.RadarBlurThresHeight or 90 -- 10% chance to find a low flying group + local thresblur = self.RadarBlurThresBlur or 85 -- 25% chance to escape the radar overall + local fheight = math.floor(math.random(1,10000)/100) + local fblur = math.floor(math.random(1,10000)/100) + local unit = UNIT:FindByName(DetectedObjectName) + if unit and unit:IsAlive() then + local AGL = unit:GetAltitude(true) + if AGL <= minheight and fheight > thresheight then DetectionAccepted = false end + if fblur > thresblur then DetectionAccepted = false end + end + end + -- Calculate additional probabilities if not self.DetectedObjects[DetectedObjectName] and TargetIsVisible and self.DistanceProbability then @@ -1011,7 +1027,21 @@ do -- DETECTION_BASE return self end - + + --- Method to make the radar detection less accurate, e.g. for WWII scenarios. + -- @param #DETECTION_BASE self + -- @param #number minheight Minimum flight height to be detected, in meters AGL (above ground) + -- @param #number thresheight Threshold to escape the radar if flying below minheight, defaults to 90 (90% escape chance) + -- @param #number thresblur Threshold to be detected by the radar overall, defaults to 85 (85% chance to be found) + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetRadarBlur(minheight,thresheight,thresblur) + self.RadarBlur = true + self.RadarBlurMinHeight = minheight or 250 -- meters + self.RadarBlurThresHeight = thresheight or 90 -- 10% chance to find a low flying group + self.RadarBlurThresBlur = thresblur or 85 -- 25% chance to escape the radar overall + return self + end + end do @@ -1354,7 +1384,7 @@ do -- DETECTION_BASE } } - --- @param DCS#Unit FoundDCSUnit + -- @param DCS#Unit FoundDCSUnit -- @param Wrapper.Group#GROUP ReportGroup -- @param Core.Set#SET_GROUP ReportSetGroup local FindNearByFriendlies = function( FoundDCSUnit, ReportGroupData ) @@ -1419,7 +1449,7 @@ do -- DETECTION_BASE DetectedItem.PlayersNearBy = nil _DATABASE:ForEachPlayer( - --- @param Wrapper.Unit#UNIT PlayerUnit + -- @param Wrapper.Unit#UNIT PlayerUnit function( PlayerUnitName ) local PlayerUnit = UNIT:FindByName( PlayerUnitName ) @@ -1975,8 +2005,9 @@ do -- DETECTION_BASE end do -- DETECTION_UNITS - - --- @type DETECTION_UNITS + + --- + -- @type DETECTION_UNITS -- @field DCS#Distance DetectionRange The range till which targets are detected. -- @extends Functional.Detection#DETECTION_BASE @@ -2232,7 +2263,7 @@ end do -- DETECTION_TYPES - --- @type DETECTION_TYPES + -- @type DETECTION_TYPES -- @extends Functional.Detection#DETECTION_BASE --- Will detect units within the battle zone. @@ -2434,8 +2465,9 @@ do -- DETECTION_TYPES end do -- DETECTION_AREAS - - --- @type DETECTION_AREAS + + --- + -- @type DETECTION_AREAS -- @field DCS#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. -- @field #DETECTION_BASE.DetectedItems DetectedItems A list of areas containing the set of @{Wrapper.Unit}s, @{Core.Zone}s, the center @{Wrapper.Unit} within the zone, and ID of each area that was detected within a DetectionZoneRange. -- @extends Functional.Detection#DETECTION_BASE @@ -2961,7 +2993,7 @@ do -- DETECTION_AREAS -- DetectedSet:Flush( self ) - DetectedSet:ForEachUnit( --- @param Wrapper.Unit#UNIT DetectedUnit + DetectedSet:ForEachUnit( -- @param Wrapper.Unit#UNIT DetectedUnit function( DetectedUnit ) if DetectedUnit:IsAlive() then -- self:T( "Detected Set #" .. DetectedItem.ID .. ":" .. DetectedUnit:GetName() ) From 0f4162a9a96750c5c1f3c6298ecaabaaa76104dc Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 9 Dec 2023 14:34:41 +0100 Subject: [PATCH 26/38] * fixes --- .../Moose/Functional/Detection.lua | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index 2ec01a349..4ddf6ff9c 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -38,7 +38,8 @@ -- @image Detection.JPG do -- DETECTION_BASE - + + --- -- @type DETECTION_BASE -- @field Core.Set#SET_GROUP DetectionSetGroup The @{Core.Set} of GROUPs in the Forward Air Controller role. -- @field DCS#Distance DetectionRange The range till which targets are accepted to be detected. @@ -91,6 +92,11 @@ do -- DETECTION_BASE -- -- DetectionObject:FilterCategories( { Unit.Category.AIRPLANE, Unit.Category.HELICOPTER } ) -- + -- + -- ## Radar Blur - use to make the radar less exact, e.g. for WWII scenarios + -- + -- * @{DETECTION_BASE.SetRadarBlur}(): Set the radar blur to be used. + -- -- ## **DETECTION_ derived classes** group the detected units into a **DetectedItems[]** list -- -- DETECTION_BASE derived classes build a list called DetectedItems[], which is essentially a first later @@ -268,10 +274,12 @@ do -- DETECTION_BASE DetectedItems = {}, DetectedItemsByIndex = {}, } - + + --- -- @type DETECTION_BASE.DetectedObjects -- @list <#DETECTION_BASE.DetectedObject> + --- -- @type DETECTION_BASE.DetectedObject -- @field #string Name -- @field #boolean IsVisible @@ -283,7 +291,8 @@ do -- DETECTION_BASE -- @field #number LastTime -- @field #boolean LastPos -- @field #number LastVelocity - + + --- -- @type DETECTION_BASE.DetectedItems -- @list <#DETECTION_BASE.DetectedItem> @@ -723,7 +732,7 @@ do -- DETECTION_BASE local unit = UNIT:FindByName(DetectedObjectName) if unit and unit:IsAlive() then local AGL = unit:GetAltitude(true) - if AGL <= minheight and fheight > thresheight then DetectionAccepted = false end + if AGL <= minheight and fheight < thresheight then DetectionAccepted = false end if fblur > thresblur then DetectionAccepted = false end end end @@ -2262,7 +2271,8 @@ do -- DETECTION_UNITS end do -- DETECTION_TYPES - + + --- -- @type DETECTION_TYPES -- @extends Functional.Detection#DETECTION_BASE From bc3f9ed7c0ac72560b83a054d2c6a7f7d5b4fe8a Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 9 Dec 2023 15:51:35 +0100 Subject: [PATCH 27/38] #SPAWN * Added SPAWN:InitCallSign(ID,Name,Minor,Major) --- Moose Development/Moose/Core/Spawn.lua | 33 ++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 388b9aafe..df588f14a 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -1108,6 +1108,22 @@ function SPAWN:InitRandomizeCallsign() return self end +--- [BLUE AIR only!] This method sets a specific callsign for a spawned group. Use for a group with one unit only! +-- @param #SPAWN self +-- @param #number ID ID of the callsign enumerator, e.g. CALLSIGN.Tanker.Texaco - - resulting in e.g. Texaco-2-1 +-- @param #string Name Name of this callsign as it cannot be determined from the ID because of the dependency on the task type of the plane, and the plane type. E.g. "Texaco" +-- @param #number Minor Minor number, i.e. the unit number within the group, e.g 2 - resulting in e.g. Texaco-2-1 +-- @param #number Major Major number, i.e. the group number of this name, e.g. 1 - resulting in e.g. Texaco-2-1 +-- @return #SPAWN self +function SPAWN:InitCallSign(ID,Name,Minor,Major) + self.SpawnInitCallSign = true + self.SpawnInitCallSignID = ID or 1 + self.SpawnInitCallSignMinor = Minor or 1 + self.SpawnInitCallSignMajor = Major or 1 + self.SpawnInitCallSignName = string.lower(Name) or "enfield" + return self +end + --- This method sets a spawn position for the group that is different from the location of the template. -- @param #SPAWN self -- @param Core.Point#COORDINATE Coordinate The position to spawn from @@ -3331,10 +3347,23 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 end end + if self.SpawnInitCallSign then + for UnitID = 1, #SpawnTemplate.units do + local Callsign = SpawnTemplate.units[UnitID].callsign + if Callsign and type( Callsign ) ~= "number" then + SpawnTemplate.units[UnitID].callsign[1] = self.SpawnInitCallSignID + SpawnTemplate.units[UnitID].callsign[2] = self.SpawnInitCallSignMinor + SpawnTemplate.units[UnitID].callsign[3] = self.SpawnInitCallSignMajor + SpawnTemplate.units[UnitID].callsign["name"] = string.format("%s%d%d",self.SpawnInitCallSignName,self.SpawnInitCallSignMinor,self.SpawnInitCallSignMajor) + --UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].callsign,1) + end + end + end + for UnitID = 1, #SpawnTemplate.units do local Callsign = SpawnTemplate.units[UnitID].callsign if Callsign then - if type( Callsign ) ~= "number" then -- blue callsign + if type( Callsign ) ~= "number" and not self.SpawnInitCallSign then -- blue callsign -- UTILS.PrintTableToLog(Callsign,1) Callsign[2] = ((SpawnIndex - 1) % 10) + 1 local CallsignName = SpawnTemplate.units[UnitID].callsign["name"] -- #string @@ -3342,7 +3371,7 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 local CallsignLen = CallsignName:len() SpawnTemplate.units[UnitID].callsign[2] = UnitID SpawnTemplate.units[UnitID].callsign["name"] = CallsignName:sub( 1, CallsignLen ) .. SpawnTemplate.units[UnitID].callsign[2] .. SpawnTemplate.units[UnitID].callsign[3] - else + elseif type( Callsign ) == "number" then SpawnTemplate.units[UnitID].callsign = Callsign + SpawnIndex end end From d2d6fac7df3d3bf5b205fc3dfa90ddffca1fa5bc Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 9 Dec 2023 18:16:29 +0100 Subject: [PATCH 28/38] # DETECTION, logic fix --- Moose Development/Moose/Functional/Detection.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index 4ddf6ff9c..a33e7f7b0 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -724,6 +724,7 @@ do -- DETECTION_BASE -- Calculate radar blue probability if self.RadarBlur then + BASE:I("RadarBlur") local minheight = self.RadarBlurMinHeight or 250 -- meters local thresheight = self.RadarBlurThresHeight or 90 -- 10% chance to find a low flying group local thresblur = self.RadarBlurThresBlur or 85 -- 25% chance to escape the radar overall @@ -732,8 +733,11 @@ do -- DETECTION_BASE local unit = UNIT:FindByName(DetectedObjectName) if unit and unit:IsAlive() then local AGL = unit:GetAltitude(true) - if AGL <= minheight and fheight < thresheight then DetectionAccepted = false end + BASE:I("Unit "..DetectedObjectName.." is at "..AGL.."m.") + BASE:I(string.format("fheight = %d/%d | fblur = %d/%d",fheight,thresheight,fblur,thresblur)) if fblur > thresblur then DetectionAccepted = false end + if AGL <= minheight and fheight < thresheight then DetectionAccepted = false end + BASE:I("Detection Accepted = "..tostring(DetectionAccepted)) end end From 87f1a5ed0d303a4a128abec56fc694e8f35c2d8f Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 10 Dec 2023 11:58:19 +0100 Subject: [PATCH 29/38] # DETECTION * Option to make Radar Blue decision visible in logs (self.debug) and/or screen (self.verbose) --- Moose Development/Moose/Functional/Detection.lua | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index a33e7f7b0..09ad78104 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -46,6 +46,8 @@ do -- DETECTION_BASE -- @field #DETECTION_BASE.DetectedObjects DetectedObjects The list of detected objects. -- @field #table DetectedObjectsIdentified Map of the DetectedObjects identified. -- @field #number DetectionRun + -- @field #boolean debug + -- @field #boolean verbose -- @extends Core.Fsm#FSM --- Defines the core functions to administer detected objects. @@ -273,6 +275,8 @@ do -- DETECTION_BASE DetectedObjectsIdentified = {}, DetectedItems = {}, DetectedItemsByIndex = {}, + debug = false, + verbose = false, } --- @@ -721,10 +725,10 @@ do -- DETECTION_BASE end end - -- Calculate radar blue probability + -- Calculate radar blur probability if self.RadarBlur then - BASE:I("RadarBlur") + MESSAGE:New("Radar Blur",10):ToLogIf(self.debug):ToAllIf(self.verbose) local minheight = self.RadarBlurMinHeight or 250 -- meters local thresheight = self.RadarBlurThresHeight or 90 -- 10% chance to find a low flying group local thresblur = self.RadarBlurThresBlur or 85 -- 25% chance to escape the radar overall @@ -733,11 +737,11 @@ do -- DETECTION_BASE local unit = UNIT:FindByName(DetectedObjectName) if unit and unit:IsAlive() then local AGL = unit:GetAltitude(true) - BASE:I("Unit "..DetectedObjectName.." is at "..AGL.."m.") - BASE:I(string.format("fheight = %d/%d | fblur = %d/%d",fheight,thresheight,fblur,thresblur)) + MESSAGE:New("Unit "..DetectedObjectName.." is at "..math.floor(AGL).."m.",10):ToLogIf(self.debug):ToAllIf(self.verbose) + MESSAGE:New(string.format("fheight = %d/%d | fblur = %d/%d",fheight,thresheight,fblur,thresblur),10):ToLogIf(self.debug):ToAllIf(self.verbose) if fblur > thresblur then DetectionAccepted = false end - if AGL <= minheight and fheight < thresheight then DetectionAccepted = false end - BASE:I("Detection Accepted = "..tostring(DetectionAccepted)) + if AGL <= minheight and fheight < thresheight then DetectionAccepted = false end + MESSAGE:New("Detection Accepted = "..tostring(DetectionAccepted),10):ToLogIf(self.debug):ToAllIf(self.verbose) end end From c089e56060974539e58a346665f7536a2898c4cf Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 10 Dec 2023 14:37:41 +0100 Subject: [PATCH 30/38] # DETECTION * Make the radar blur less effective when under 20km distance --- Moose Development/Moose/Functional/Detection.lua | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index 09ad78104..1acd82ad2 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -46,8 +46,6 @@ do -- DETECTION_BASE -- @field #DETECTION_BASE.DetectedObjects DetectedObjects The list of detected objects. -- @field #table DetectedObjectsIdentified Map of the DetectedObjects identified. -- @field #number DetectionRun - -- @field #boolean debug - -- @field #boolean verbose -- @extends Core.Fsm#FSM --- Defines the core functions to administer detected objects. @@ -275,8 +273,6 @@ do -- DETECTION_BASE DetectedObjectsIdentified = {}, DetectedItems = {}, DetectedItemsByIndex = {}, - debug = false, - verbose = false, } --- @@ -725,19 +721,24 @@ do -- DETECTION_BASE end end - -- Calculate radar blur probability + -- Calculate radar blue probability if self.RadarBlur then MESSAGE:New("Radar Blur",10):ToLogIf(self.debug):ToAllIf(self.verbose) local minheight = self.RadarBlurMinHeight or 250 -- meters local thresheight = self.RadarBlurThresHeight or 90 -- 10% chance to find a low flying group local thresblur = self.RadarBlurThresBlur or 85 -- 25% chance to escape the radar overall + local dist = math.floor(Distance) + if dist <= 20 then + thresheight = (((dist*dist)/400)*thresheight) + thresblur = (((dist*dist)/400)*thresblur) + end local fheight = math.floor(math.random(1,10000)/100) local fblur = math.floor(math.random(1,10000)/100) local unit = UNIT:FindByName(DetectedObjectName) if unit and unit:IsAlive() then local AGL = unit:GetAltitude(true) - MESSAGE:New("Unit "..DetectedObjectName.." is at "..math.floor(AGL).."m.",10):ToLogIf(self.debug):ToAllIf(self.verbose) + MESSAGE:New("Unit "..DetectedObjectName.." is at "..math.floor(AGL).."m. Distance "..math.floor(Distance).."km.",10):ToLogIf(self.debug):ToAllIf(self.verbose) MESSAGE:New(string.format("fheight = %d/%d | fblur = %d/%d",fheight,thresheight,fblur,thresblur),10):ToLogIf(self.debug):ToAllIf(self.verbose) if fblur > thresblur then DetectionAccepted = false end if AGL <= minheight and fheight < thresheight then DetectionAccepted = false end From 230d9d82bfe1ba3cc6f57c2ab1a689d98eb53b0f Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Mon, 11 Dec 2023 11:04:35 +0100 Subject: [PATCH 31/38] Update Detection.lua (#2063) # RadarBlur - make burn-through limit configureable --- Moose Development/Moose/Functional/Detection.lua | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index 1acd82ad2..a71e194ed 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -721,7 +721,7 @@ do -- DETECTION_BASE end end - -- Calculate radar blue probability + -- Calculate radar blur probability if self.RadarBlur then MESSAGE:New("Radar Blur",10):ToLogIf(self.debug):ToAllIf(self.verbose) @@ -729,9 +729,9 @@ do -- DETECTION_BASE local thresheight = self.RadarBlurThresHeight or 90 -- 10% chance to find a low flying group local thresblur = self.RadarBlurThresBlur or 85 -- 25% chance to escape the radar overall local dist = math.floor(Distance) - if dist <= 20 then - thresheight = (((dist*dist)/400)*thresheight) - thresblur = (((dist*dist)/400)*thresblur) + if dist <= self.RadarBlurClosing then + thresheight = (((dist*dist)/self.RadarBlurClosingSquare)*thresheight) + thresblur = (((dist*dist)/self.RadarBlurClosingSquare)*thresblur) end local fheight = math.floor(math.random(1,10000)/100) local fblur = math.floor(math.random(1,10000)/100) @@ -1051,12 +1051,15 @@ do -- DETECTION_BASE -- @param #number minheight Minimum flight height to be detected, in meters AGL (above ground) -- @param #number thresheight Threshold to escape the radar if flying below minheight, defaults to 90 (90% escape chance) -- @param #number thresblur Threshold to be detected by the radar overall, defaults to 85 (85% chance to be found) + -- @param #number closing Closing-in in km - the limit of km from which on it becomes increasingly difficult to escape radar detection if flying towards the radar position. Should be about 1/3 of the radar detection radius in kilometers, defaults to 20. -- @return #DETECTION_BASE self - function DETECTION_BASE:SetRadarBlur(minheight,thresheight,thresblur) + function DETECTION_BASE:SetRadarBlur(minheight,thresheight,thresblur,closing) self.RadarBlur = true self.RadarBlurMinHeight = minheight or 250 -- meters self.RadarBlurThresHeight = thresheight or 90 -- 10% chance to find a low flying group self.RadarBlurThresBlur = thresblur or 85 -- 25% chance to escape the radar overall + self.RadarBlurClosing = closing or 20 -- 20km + self.RadarBlurClosingSquare = self.RadarBlurClosing * self.RadarBlurClosing return self end From f837e9dec7522605ce60b31bbccfcb092c839b8b Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 12 Dec 2023 10:53:37 +0100 Subject: [PATCH 32/38] #COORDINATE * Added functions to create a COORDINATE from MGRS --- Moose Development/Moose/Core/Point.lua | 52 ++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 3911a52f3..9cf13ba3e 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -25,7 +25,7 @@ do -- COORDINATE - --- @type COORDINATE + -- @type COORDINATE -- @field #string ClassName Name of the class -- @field #number x Component of the 3D vector. -- @field #number y Component of the 3D vector. @@ -2551,7 +2551,7 @@ do -- COORDINATE Offset=Offset or 2 - -- Measurement of visibility should not be from the ground, so Adding a hypotethical 2 meters to each Coordinate. + -- Measurement of visibility should not be from the ground, so Adding a hypothetical 2 meters to each Coordinate. local FromVec3 = self:GetVec3() FromVec3.y = FromVec3.y + Offset @@ -2952,10 +2952,10 @@ do -- COORDINATE end -- corrected Track to be direction of travel of bogey (self in this case) - local track = "Maneuver" - - if self.Heading then - track = UTILS.BearingToCardinal(self.Heading) or "North" + local track = "Maneuver" + + if self.Heading then + track = UTILS.BearingToCardinal(self.Heading) or "North" end if rangeNM > 3 then @@ -3100,6 +3100,44 @@ do -- COORDINATE local MGRS = coord.LLtoMGRS( lat, lon ) return "MGRS " .. UTILS.tostringMGRS( MGRS, MGRS_Accuracy ) end + + --- Provides a COORDINATE from an MGRS String + -- @param #COORDINATE self + -- @param #string MGRSString MGRS String, e.g. "MGRS 37T DK 12345 12345" + -- @return #COORDINATE self + function COORDINATE:NewFromMGRSString( MGRSString ) + local myparts = UTILS.Split(MGRSString," ") + UTILS.PrintTableToLog(myparts,1) + local MGRS = { + UTMZone = myparts[2], + MGRSDigraph = myparts[3], + Easting = tonumber(myparts[4]), + Northing = tonumber(myparts[5]), + } + local lat, lon = coord.MGRStoLL(MGRS) + local point = coord.LLtoLO(lat, lon, 0) + local coord = COORDINATE:NewFromVec2({x=point.x,y=point.z}) + return coord + end + + --- Provides a COORDINATE from an MGRS Coordinate + -- @param #COORDINATE self + -- @param #string UTMZone UTM Zone, e.g. "37T" + -- @param #string MGRSDigraph Digraph, e.g. "DK" + -- @param #number Easting Meters easting + -- @param #number Northing Meters northing + -- @return #COORDINATE self + function COORDINATE:NewFromMGRS( UTMZone, MGRSDigraph, Easting, Northing ) + local MGRS = { + UTMZone = UTMZone, + MGRSDigraph = MGRSDigraph, + Easting = Easting, + Northing = Northing, + } + local lat, lon = coord.MGRStoLL(MGRS) + local point = coord.LLtoLO(lat, lon, 0) + local coord = COORDINATE:NewFromVec2({x=point.x,y=point.z}) + end --- Provides a coordinate string of the point, based on a coordinate format system: -- * Uses default settings in COORDINATE. @@ -3613,7 +3651,7 @@ end do -- POINT_VEC2 - --- @type POINT_VEC2 + -- @type POINT_VEC2 -- @field DCS#Distance x The x coordinate in meters. -- @field DCS#Distance y the y coordinate in meters. -- @extends Core.Point#COORDINATE From 8382eb9cd80cc0144b9cb4f407b9383086035cfe Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Wed, 13 Dec 2023 19:21:28 +0100 Subject: [PATCH 33/38] Update Range.lua (#2066) MSRS config compatibility --- Moose Development/Moose/Functional/Range.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index e238914a9..13fb54c04 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -1207,18 +1207,18 @@ end -- @return #RANGE self function RANGE:SetSRS(PathToSRS, Port, Coalition, Frequency, Modulation, Volume, PathToGoogleKey) - if PathToSRS then + if PathToSRS or MSRS.path then self.useSRS=true - self.controlmsrs=MSRS:New(PathToSRS, Frequency or 256, Modulation or radio.modulation.AM, Volume or 1.0) - self.controlmsrs:SetPort(Port) + self.controlmsrs=MSRS:New(PathToSRS or MSRS.path, Frequency or 256, Modulation or radio.modulation.AM, Volume or 1.0) + self.controlmsrs:SetPort(Port or MSRS.port) self.controlmsrs:SetCoalition(Coalition or coalition.side.BLUE) self.controlmsrs:SetLabel("RANGEC") self.controlsrsQ = MSRSQUEUE:New("CONTROL") - self.instructmsrs=MSRS:New(PathToSRS, Frequency or 305, Modulation or radio.modulation.AM, Volume or 1.0) - self.instructmsrs:SetPort(Port) + self.instructmsrs=MSRS:New(PathToSRS or MSRS.path, Frequency or 305, Modulation or radio.modulation.AM, Volume or 1.0) + self.instructmsrs:SetPort(Port or MSRS.port) self.instructmsrs:SetCoalition(Coalition or coalition.side.BLUE) self.instructmsrs:SetLabel("RANGEI") self.instructsrsQ = MSRSQUEUE:New("INSTRUCT") From 68548f45815f682a81cecf4538b242e0f883238e Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 14 Dec 2023 11:12:14 +0100 Subject: [PATCH 34/38] #COORDINATE * Fix for NewFromMGRS for less precise coordinates (below level 5) --- Moose Development/Moose/Core/Point.lua | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 9cf13ba3e..a3c83da1f 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -24,7 +24,8 @@ do -- COORDINATE - + + --- -- @type COORDINATE -- @field #string ClassName Name of the class -- @field #number x Component of the 3D vector. @@ -3107,13 +3108,16 @@ do -- COORDINATE -- @return #COORDINATE self function COORDINATE:NewFromMGRSString( MGRSString ) local myparts = UTILS.Split(MGRSString," ") - UTILS.PrintTableToLog(myparts,1) + local northing = tostring(myparts[5]) or "" + local easting = tostring(myparts[4]) or "" + if string.len(easting) < 5 then easting = easting..string.rep("0",5-string.len(easting)) end + if string.len(northing) < 5 then northing = northing..string.rep("0",5-string.len(northing)) end local MGRS = { UTMZone = myparts[2], MGRSDigraph = myparts[3], - Easting = tonumber(myparts[4]), - Northing = tonumber(myparts[5]), - } + Easting = easting, + Northing = northing, + } local lat, lon = coord.MGRStoLL(MGRS) local point = coord.LLtoLO(lat, lon, 0) local coord = COORDINATE:NewFromVec2({x=point.x,y=point.z}) @@ -3124,10 +3128,12 @@ do -- COORDINATE -- @param #COORDINATE self -- @param #string UTMZone UTM Zone, e.g. "37T" -- @param #string MGRSDigraph Digraph, e.g. "DK" - -- @param #number Easting Meters easting - -- @param #number Northing Meters northing + -- @param #string Easting Meters easting - string in order to allow for leading zeros, e.g. "01234". Should be 5 digits. + -- @param #string Northing Meters northing - string in order to allow for leading zeros, e.g. "12340". Should be 5 digits. -- @return #COORDINATE self function COORDINATE:NewFromMGRS( UTMZone, MGRSDigraph, Easting, Northing ) + if string.len(Easting) < 5 then Easting = Easting..string.rep("0",5-string.len(Easting) )end + if string.len(Northing) < 5 then Northing = Northing..string.rep("0",5-string.len(Northing) )end local MGRS = { UTMZone = UTMZone, MGRSDigraph = MGRSDigraph, From 55ffe37a794e7dba9eeaf3bb5fee618ec2989a60 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 14 Dec 2023 12:42:13 +0100 Subject: [PATCH 35/38] #USERSOUND * Added USERSOUND:ToClient( Client, Delay ) --- Moose Development/Moose/Sound/UserSound.lua | 22 ++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Sound/UserSound.lua b/Moose Development/Moose/Sound/UserSound.lua index cbccffe12..1c753a538 100644 --- a/Moose Development/Moose/Sound/UserSound.lua +++ b/Moose Development/Moose/Sound/UserSound.lua @@ -137,7 +137,7 @@ do -- UserSound return self end - --- Play the usersound to the given @{Wrapper.Unit}. + --- Play the usersound to the given @{Wrapper.Unit}. -- @param #USERSOUND self -- @param Wrapper.Unit#UNIT Unit The @{Wrapper.Unit} to play the usersound to. -- @param #number Delay (Optional) Delay in seconds, before the sound is played. Default 0. @@ -159,4 +159,24 @@ do -- UserSound return self end + + --- Play the usersound to the given @{Wrapper.Unit}. + -- @param #USERSOUND self + -- @param Wrapper.Client#CLIENT The @{Wrapper.Client} to play the usersound to. + -- @param #number Delay (Optional) Delay in seconds, before the sound is played. Default 0. + -- @return #USERSOUND The usersound instance. + -- @usage + -- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" ) + -- local PlayerUnit = CLIENT:FindByPlayerName("Karl Heinz")-- Search for the active client with playername "Karl Heinz", a human player. + -- BlueVictory:ToClient( PlayerUnit ) -- Play the victory sound to the player unit. + -- + function USERSOUND:ToClient( Client, Delay ) + Delay=Delay or 0 + if Delay>0 then + SCHEDULER:New(nil, USERSOUND.ToClient,{self, Client}, Delay) + else + trigger.action.outSoundForUnit( Client:GetID(), self.UserSoundFileName ) + end + return self + end end \ No newline at end of file From e84e16f58bf975dc7bdd9787ad227f286010efe0 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 14 Dec 2023 12:43:36 +0100 Subject: [PATCH 36/38] xx --- Moose Development/Moose/Sound/UserSound.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Sound/UserSound.lua b/Moose Development/Moose/Sound/UserSound.lua index 1c753a538..ceaeeb6e2 100644 --- a/Moose Development/Moose/Sound/UserSound.lua +++ b/Moose Development/Moose/Sound/UserSound.lua @@ -160,7 +160,7 @@ do -- UserSound end - --- Play the usersound to the given @{Wrapper.Unit}. + --- Play the usersound to the given @{Wrapper.Client}. -- @param #USERSOUND self -- @param Wrapper.Client#CLIENT The @{Wrapper.Client} to play the usersound to. -- @param #number Delay (Optional) Delay in seconds, before the sound is played. Default 0. From bda4efc634c804ea01ddebc97163477932a99985 Mon Sep 17 00:00:00 2001 From: kaltokri Date: Fri, 15 Dec 2023 14:08:45 +0100 Subject: [PATCH 37/38] Added page "Create your own Hello world" --- docs/beginner/demo-missions.md | 13 ++ docs/beginner/hello-world-build.md | 169 +++++++++++++++++- .../dcs-triggers-once-conditions-conf.png | Bin 0 -> 11728 bytes 3 files changed, 180 insertions(+), 2 deletions(-) create mode 100644 docs/beginner/demo-missions.md create mode 100644 docs/images/beginner/dcs-triggers-once-conditions-conf.png diff --git a/docs/beginner/demo-missions.md b/docs/beginner/demo-missions.md new file mode 100644 index 000000000..073ae42fd --- /dev/null +++ b/docs/beginner/demo-missions.md @@ -0,0 +1,13 @@ +--- +parent: Beginner +nav_order: 04 +--- + +# Demo missions +{: .no_toc } + +1. Table of contents +{:toc} + +{: .warning } +> THIS DOCUMENT IS STILL WORK IN PROGRESS! diff --git a/docs/beginner/hello-world-build.md b/docs/beginner/hello-world-build.md index fde7e63da..1f7509abd 100644 --- a/docs/beginner/hello-world-build.md +++ b/docs/beginner/hello-world-build.md @@ -4,6 +4,171 @@ nav_order: 03 --- # Create your own Hello world +{: .no_toc } -{: .warning } -> THIS DOCUMENT IS STILL WORK IN PROGRESS! +1. Table of contents +{:toc} + +This page will lead you step by step through the process of creating a mission +with MOOSE. This time we include a simple mission script, which sends only +a "Hello world" message to all players. But the steps are the same to add +another mission script, which will do whatever class(es) you want to use. + +## Create Mission script + +At first we will create the mission script. It is a simple text file and can be +changed with a lot of different tools. Theoretically even the Microsoft Notepad +editor can be used. But it lacks a lot of features, which helps you to avoid +errors. + +For this guide we suggest you to download, install and use [Notepad++]{:target="_blank"}. + +{: .important } +> Windows hides filename extensions by default. So when you create a text file +> and name it `hello-world.lua` it's name is `hello-world.lua.txt` in reality. +> You must activate the display of the file name extension. +> Open a `File Explorer`, switch to menu `View` and find the option +> `File name extensions` in the section `Show/hide`. Activate it. + +- Open a File Explorer. +- Go to the subfolder `Missions` of your [Saved Games folder]{:target="_blank"}. +- Create a new text file and name it `hello-world.lua`. +- Add the following content and save the file: + + `MESSAGE:New( "Hello World! This messages is printed by MOOSE", 35, "INFO" ):ToAll()` + +## Get Moose + +To download Moose click on the following link: + +- [Moose_.lua from develop branch]{:target="_blank"} + +Press `Ctrl + S` to save the file on your hard disk next to your mission script. + +## Create the mission + +- Start DCS. +- In the main menu choose `MISSION EDITOR`. +- Click on `create new mission`. +- In the dialog `NEW MISSION SETTINGS`: + - Choose map `Caucasus`. + - In the drop box upper left choose `Modern` as coalition preset. + - Click on `OK`. +- The mission editor will load with a fresh new and empty mission. +- Click on `File` in the menu bar and `SAVE` or Press `Ctrl + S`. +- Open `My Missions` and save the file with the name `hello-world.miz`. + +## Add Moose to the mission + +- On the left side activate `TRIGGERS`: + + ![dcs-triggers-toolbar.png](../images/beginner/dcs-triggers-toolbar.png) + +- On the right side the `TRIGGERS` dialog opens with a lot of options. +- Click on `NEW`, choose `4 MISSION START` as **TYPE**. +- Give it the `Load MOOSE` as **NAME**. +- Leave the **EVENT** option set to `NO EVENT`. +- Optional: Choose a color for easy recognition (e.g. yellow). + + ![dcs-triggers-mission-start-conf.png](../images/beginner/dcs-triggers-mission-start-conf.png) + +- In the middle part the `CONDITIONS` will be shown. + For this trigger we do not configure any conditions. + + ![dcs-triggers-mission-start-conditions.png](../images/beginner/dcs-triggers-mission-start-conditions.png) + + {: .important } + > The trigger type `4 MISSION START` does not support `CONDITIONS`.
+ > So `CONDITIONS` must left blank when using it.
+ > **If you add a condition the trigger will never be executed!** + + ![dcs-triggers-mission-start-actions-conf.png](../images/beginner/dcs-triggers-mission-start-actions-conf.png) + +- On the right side `ACTIONS` is shown. +- We need to click on `NEW`. +- Choose **ACTION** `Do SCRIPT FILE` and ignore all other actions. +- Click **OPEN** and navigate to the downloaded `Moose_.lua` file. +- The result should look like this: + + ![dcs-triggers-mission-start-actions.png](../images/beginner/dcs-triggers-mission-start-actions.png) + +## Add the mission script + +- Click on `NEW`, choose `1 ONCE` as **TYPE**. +- Give it the `Load Mission Script` as **NAME**. +- Leave the **EVENT** option set to `NO EVENT`. +- Optional: Choose a color for easy recognition (e.g. green). +- The result should look like this: + + ![dcs-triggers-once-conf.png](../images/beginner/dcs-triggers-once-conf.png) + +- Switch to the middle part, the `CONDITIONS` section.
+ For this trigger we add one condition: + + ![dcs-triggers-once-conditions.png](../images/beginner/dcs-triggers-once-conditions-conf.png) + +- The combination of `1 ONCE` with `TIME MORE(1)` will ensure, that the mission + script is executed 1 second after the mission is started. + +- On the right side under `ACTIONS` you need to add the script: +- Click on `NEW`. +- Choose **ACTION** `Do SCRIPT FILE`. +- Click **OPEN** and navigate to the created `hello-world.lua` file. + +{: .important } +> Most important is the fact, that the mission script (`hello-world.lua`) +> is executed **after** `Moose_.lua`, because the mission script needs the +> classes defined in `Moose_.lua`. And they are only available when `Moose_.lua` +> is executed before the mission script. + +## Test the mission + +- Save the mission again. +- Click on the green **Fly mission** cirlce on the left tool side bar. +- It is an empty mission, so skip `BRIEFING` with `START` and then `FLY`. +- You spawn as a spectator. After some seconds you will see this message in + the upper right corner: + + ![dcs-message.jpg](../images/beginner/dcs-message.jpg) + +This is the same result as already seen in the last chapter, but this time you +have create everything on your own. + +{: .note } +> You can use this mission as a template for your own missions. So you don't +> need to do alle these steps again and again. + +## Update mission script + +- Open the `hello-world.lua` with Notepad++ again. +- Change the text a little bit, like `Hello Dude! ...` and save the file. +- Run the mission again. +- The text will not be changed in the mission. Why? + The mission editor copies the script into the mission file when you add it. + Ever change on the script file on your hard disk is not recognized by mission editor. + You have to add the file after each change again. + +- On the left side of the `TRIGGERS` dialog click on `Load Mission Script`. +- On the right side under `ACTIONS` you need to add the script again: + - Click **OPEN** and navigate to the created `hello-world.lua` file. +- Save the mission and test it again. +- Now the new text should be shown. + +## Update Moose + +Moose is constantly being developed so that new functionallity is added or +existing errors are corrected. Also from time to time changes of the DCS +scripting engine comes with a new DCS version. It may therefore be useful or +necessary to update Moose. + +- To update Moose download it again and add it again in the same way you did + with the mission script in the last step. + +## Next step + +Let's move on to the [demo missions]. + +[Notepad++]: https://notepad-plus-plus.org/downloads/ +[Saved Games folder]: tipps-and-tricks.md#find-the-saved-games-folder +[Moose_.lua from develop branch]: https://raw.githubusercontent.com/FlightControl-Master/MOOSE_INCLUDE/develop/Moose_Include_Static/Moose_.lua +[demo missions]: demo-missions.md diff --git a/docs/images/beginner/dcs-triggers-once-conditions-conf.png b/docs/images/beginner/dcs-triggers-once-conditions-conf.png new file mode 100644 index 0000000000000000000000000000000000000000..e751ea40ff51d2066e06b7a5b10522d0d7051005 GIT binary patch literal 11728 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 literal 0 HcmV?d00001 From 0ae9be49daacf38be2efbeef7224d3e3ceace495 Mon Sep 17 00:00:00 2001 From: Frank Date: Sat, 16 Dec 2023 09:31:44 +0100 Subject: [PATCH 38/38] Update Range.lua - Fixed random good by phrase --- Moose Development/Moose/Functional/Range.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index 13fb54c04..8da132f14 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -2187,7 +2187,7 @@ function RANGE:onafterExitRange( From, Event, To, player ) local text = "You left the bombing range zone. " - local r=math.random(2) + local r=math.random(5) if r==1 then text=text.."Have a nice day!"