From 50f6d98b490135b19dfb49896945ab80133926da Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 29 Sep 2021 09:25:19 +0200 Subject: [PATCH 01/37] push for a new build --- Moose Development/Moose/Wrapper/Airbase.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index 0e3a157bd..95c3e75af 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -1184,7 +1184,7 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, parkingdata=parkingdata or self:GetParkingSpotsTable(terminaltype) -- Get the aircraft size, i.e. it's longest side of x,z. - local aircraft = nil + local aircraft = nil -- fix local problem below local _aircraftsize, ax,ay,az if group and group.ClassName == "GROUP" then aircraft=group:GetUnit(1) From 3377459df56d1eb135e92b27ea5754de079ef347 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 29 Sep 2021 16:57:39 +0200 Subject: [PATCH 02/37] Added option for silent mission addition --- .../Moose/Tasking/Task_Cargo_Dispatcher.lua | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua index 6833f197c..e1018bdf0 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua @@ -683,6 +683,7 @@ do -- TASK_CARGO_DISPATCHER -- If no TaskPrefix is given, then "Transport" will be used as the prefix. -- @param Core.SetCargo#SET_CARGO SetCargo The SetCargo to be transported. -- @param #string Briefing The briefing of the task transport to be shown to the player. + -- @param #boolean Silent If true don't send a message that a new task is available. -- @return Tasking.Task_Cargo_Transport#TASK_CARGO_TRANSPORT -- @usage -- @@ -705,10 +706,12 @@ do -- TASK_CARGO_DISPATCHER -- -- Here we set a TransportDeployZone. We use the WorkplaceTask as the reference, and provide a ZONE object. -- TaskDispatcher:SetTransportDeployZone( WorkplaceTask, ZONE:New( "Workplace" ) ) -- - function TASK_CARGO_DISPATCHER:AddTransportTask( TaskPrefix, SetCargo, Briefing ) + function TASK_CARGO_DISPATCHER:AddTransportTask( TaskPrefix, SetCargo, Briefing, Silent ) self.TransportCount = self.TransportCount + 1 + local verbose = Silent and true + local TaskName = string.format( ( TaskPrefix or "Transport" ) .. ".%03d", self.TransportCount ) self.Transport[TaskName] = {} @@ -717,7 +720,7 @@ do -- TASK_CARGO_DISPATCHER self.Transport[TaskName].Task = nil self.Transport[TaskName].TaskPrefix = TaskPrefix - self:ManageTasks() + self:ManageTasks(verbose) return self.Transport[TaskName] and self.Transport[TaskName].Task end @@ -785,10 +788,11 @@ do -- TASK_CARGO_DISPATCHER --- Assigns tasks to the @{Core.Set#SET_GROUP}. -- @param #TASK_CARGO_DISPATCHER self + -- @param #boolean Silent Announce new task (nil/false) or not (true). -- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop. - function TASK_CARGO_DISPATCHER:ManageTasks() + function TASK_CARGO_DISPATCHER:ManageTasks(Silent) self:F() - + local verbose = Silent and true local AreaMsg = {} local TaskMsg = {} local ChangeMsg = {} @@ -897,7 +901,7 @@ do -- TASK_CARGO_DISPATCHER local TaskText = TaskReport:Text(", ") for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do - if ( not Mission:IsGroupAssigned(TaskGroup) ) and TaskText ~= "" then + if ( not Mission:IsGroupAssigned(TaskGroup) ) and TaskText ~= "" and not verbose then Mission:GetCommandCenter():MessageToGroup( string.format( "%s has tasks %s. Subscribe to a task using the radio menu.", Mission:GetShortText(), TaskText ), TaskGroup ) end end From f8c05c99d0f81340fffa8b8cef151fc5b129101b Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 30 Sep 2021 08:07:34 +0200 Subject: [PATCH 03/37] RADIO - delete frequency check --- Moose Development/Moose/Sound/Radio.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Sound/Radio.lua b/Moose Development/Moose/Sound/Radio.lua index 872ad6e26..6eb3ee2e2 100644 --- a/Moose Development/Moose/Sound/Radio.lua +++ b/Moose Development/Moose/Sound/Radio.lua @@ -157,7 +157,7 @@ end --- Set the frequency for the radio transmission. -- If the transmitting positionable is a unit or group, this also set the command "SetFrequency" with the defined frequency and modulation. -- @param #RADIO self --- @param #number Frequency Frequency in MHz. Ranges allowed for radio transmissions in DCS : 30-87.995 / 108-173.995 / 225-399.975MHz. +-- @param #number Frequency Frequency in MHz. -- @return #RADIO self function RADIO:SetFrequency(Frequency) self:F2(Frequency) @@ -165,7 +165,7 @@ function RADIO:SetFrequency(Frequency) if type(Frequency) == "number" then -- If frequency is in range - if (Frequency >= 30 and Frequency <= 87.995) or (Frequency >= 108 and Frequency <= 173.995) or (Frequency >= 225 and Frequency <= 399.975) then + -- if (Frequency >= 30 and Frequency <= 87.995) or (Frequency >= 108 and Frequency <= 173.995) or (Frequency >= 225 and Frequency <= 399.975) then -- Convert frequency from MHz to Hz self.Frequency = Frequency * 1000000 @@ -186,10 +186,10 @@ function RADIO:SetFrequency(Frequency) end return self - end + -- end end - self:E({"Frequency is outside of DCS Frequency ranges (30-80, 108-152, 225-400). Frequency unchanged.", Frequency}) + self:E({"Frequency is not a number. Frequency unchanged.", Frequency}) return self end From 359e18eb58f62fb4dc5aa56903740b4936256f73 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 30 Sep 2021 08:07:42 +0200 Subject: [PATCH 04/37] RADIO - delete frequency check --- Moose Development/Moose/Sound/Radio.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Sound/Radio.lua b/Moose Development/Moose/Sound/Radio.lua index 872ad6e26..d98a4cc6c 100644 --- a/Moose Development/Moose/Sound/Radio.lua +++ b/Moose Development/Moose/Sound/Radio.lua @@ -157,7 +157,7 @@ end --- Set the frequency for the radio transmission. -- If the transmitting positionable is a unit or group, this also set the command "SetFrequency" with the defined frequency and modulation. -- @param #RADIO self --- @param #number Frequency Frequency in MHz. Ranges allowed for radio transmissions in DCS : 30-87.995 / 108-173.995 / 225-399.975MHz. +-- @param #number Frequency Frequency in MHz. -- @return #RADIO self function RADIO:SetFrequency(Frequency) self:F2(Frequency) @@ -165,7 +165,7 @@ function RADIO:SetFrequency(Frequency) if type(Frequency) == "number" then -- If frequency is in range - if (Frequency >= 30 and Frequency <= 87.995) or (Frequency >= 108 and Frequency <= 173.995) or (Frequency >= 225 and Frequency <= 399.975) then + --if (Frequency >= 30 and Frequency <= 87.995) or (Frequency >= 108 and Frequency <= 173.995) or (Frequency >= 225 and Frequency <= 399.975) then -- Convert frequency from MHz to Hz self.Frequency = Frequency * 1000000 @@ -186,10 +186,10 @@ function RADIO:SetFrequency(Frequency) end return self - end + --end end - self:E({"Frequency is outside of DCS Frequency ranges (30-80, 108-152, 225-400). Frequency unchanged.", Frequency}) + self:E({"Frequency is not a number. Frequency unchanged.", Frequency}) return self end From edd6594953df48298ccf40658b02ac44d7fe0dd8 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 1 Oct 2021 10:43:17 +0200 Subject: [PATCH 05/37] CTLD: added user-friendly function to inject static cargos: CTLD:InjectStaticFromTemplate(Zone, Template, Mass) --- Moose Development/Moose/Ops/CTLD.lua | 41 +++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 97b33e734..090eb8073 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -987,7 +987,7 @@ CTLD.UnitTypes = { --- CTLD class version. -- @field #string version -CTLD.version="0.2.2a4" +CTLD.version="0.2.3" --- Instantiate a new CTLD. -- @param #CTLD self @@ -1887,14 +1887,18 @@ function CTLD:_GetCrates(Group, Unit, Cargo, number, drop) return self end ---- Inject crates and static cargo objects. +--- (Internal) Inject crates and static cargo objects. -- @param #CTLD self -- @param Core.Zone#ZONE Zone Zone to spawn in. -- @param #CTLD_CARGO Cargo The cargo type to spawn. +-- @param #boolean RandomCoord Randomize coordinate. -- @return #CTLD self -function CTLD:InjectStatics(Zone, Cargo) +function CTLD:InjectStatics(Zone, Cargo, RandomCoord) self:T(self.lid .. " InjectStatics") local cratecoord = Zone:GetCoordinate() + if RandomCoord then + cratecoord = Zone:GetRandomCoordinate(5,20) + end local surface = cratecoord:GetSurfaceType() if surface == land.SurfaceType.WATER then return self @@ -1930,6 +1934,19 @@ function CTLD:InjectStatics(Zone, Cargo) return self end +--- (User) Inject static cargo objects. +-- @param #CTLD self +-- @param Core.Zone#ZONE Zone Zone to spawn in. Will be a somewhat random coordinate. +-- @param #string Template Unit(!) name of the static cargo object to be used as template. +-- @param #number Mass Mass of the static in kg. +-- @return #CTLD self +function CTLD:InjectStaticFromTemplate(Zone, Template, Mass) + self:T(self.lid .. " InjectStaticFromTemplate") + local cargotype = self:GetStaticsCargoFromTemplate(Template,Mass) -- #CTLD_CARGO + self:InjectStatics(Zone,cargotype,true) + return self +end + --- (Internal) Function to find and list nearby crates. -- @param #CTLD self -- @param Wrapper.Group#GROUP Group @@ -2961,7 +2978,7 @@ end --- User function - Add *generic* static-type loadable as cargo. This type will create cargo that needs to be loaded, moved and dropped. -- @param #CTLD self --- @param #string Name Unique name of this type of cargo as set in the mission editor (not: UNIT name!), e.g. "Ammunition-1". +-- @param #string Name Unique name of this type of cargo as set in the mission editor (note: UNIT name!), e.g. "Ammunition-1". -- @param #number Mass Mass in kg of each static in kg, e.g. 100. -- @param #number Stock Number of groups in stock. Nil for unlimited. function CTLD:AddStaticsCargo(Name,Mass,Stock) @@ -2975,6 +2992,22 @@ function CTLD:AddStaticsCargo(Name,Mass,Stock) return self end +--- User function - Get a *generic* static-type loadable as #CTLD_CARGO object. +-- @param #CTLD self +-- @param #string Name Unique Unit(!) name of this type of cargo as set in the mission editor (not: GROUP name!), e.g. "Ammunition-1". +-- @param #number Mass Mass in kg of each static in kg, e.g. 100. +-- @return #CTLD_CARGO Cargo object +function CTLD:GetStaticsCargoFromTemplate(Name,Mass) + self:T(self.lid .. " GetStaticsCargoFromTemplate") + self.CargoCounter = self.CargoCounter + 1 + local type = CTLD_CARGO.Enum.STATIC + local template = STATIC:FindByName(Name,true):GetTypeName() + -- Crates are not directly loadable + local cargo = CTLD_CARGO:New(self.CargoCounter,Name,template,type,false,false,1,nil,nil,Mass,1) + --table.insert(self.Cargo_Statics,cargo) + return cargo +end + --- User function - Add *generic* repair crates loadable as cargo. This type will create crates that need to be loaded, moved, dropped and built. -- @param #CTLD self -- @param #string Name Unique name of this type of cargo. E.g. "Humvee". From 53e98e70aa03c2f0d88863e6305c59a51b476e80 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 1 Oct 2021 10:43:49 +0200 Subject: [PATCH 06/37] CTLD: added user-friendly function to inject static cargos: CTLD:InjectStaticFromTemplate(Zone, Template, Mass) --- Moose Development/Moose/Ops/CTLD.lua | 41 +++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 97b33e734..090eb8073 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -987,7 +987,7 @@ CTLD.UnitTypes = { --- CTLD class version. -- @field #string version -CTLD.version="0.2.2a4" +CTLD.version="0.2.3" --- Instantiate a new CTLD. -- @param #CTLD self @@ -1887,14 +1887,18 @@ function CTLD:_GetCrates(Group, Unit, Cargo, number, drop) return self end ---- Inject crates and static cargo objects. +--- (Internal) Inject crates and static cargo objects. -- @param #CTLD self -- @param Core.Zone#ZONE Zone Zone to spawn in. -- @param #CTLD_CARGO Cargo The cargo type to spawn. +-- @param #boolean RandomCoord Randomize coordinate. -- @return #CTLD self -function CTLD:InjectStatics(Zone, Cargo) +function CTLD:InjectStatics(Zone, Cargo, RandomCoord) self:T(self.lid .. " InjectStatics") local cratecoord = Zone:GetCoordinate() + if RandomCoord then + cratecoord = Zone:GetRandomCoordinate(5,20) + end local surface = cratecoord:GetSurfaceType() if surface == land.SurfaceType.WATER then return self @@ -1930,6 +1934,19 @@ function CTLD:InjectStatics(Zone, Cargo) return self end +--- (User) Inject static cargo objects. +-- @param #CTLD self +-- @param Core.Zone#ZONE Zone Zone to spawn in. Will be a somewhat random coordinate. +-- @param #string Template Unit(!) name of the static cargo object to be used as template. +-- @param #number Mass Mass of the static in kg. +-- @return #CTLD self +function CTLD:InjectStaticFromTemplate(Zone, Template, Mass) + self:T(self.lid .. " InjectStaticFromTemplate") + local cargotype = self:GetStaticsCargoFromTemplate(Template,Mass) -- #CTLD_CARGO + self:InjectStatics(Zone,cargotype,true) + return self +end + --- (Internal) Function to find and list nearby crates. -- @param #CTLD self -- @param Wrapper.Group#GROUP Group @@ -2961,7 +2978,7 @@ end --- User function - Add *generic* static-type loadable as cargo. This type will create cargo that needs to be loaded, moved and dropped. -- @param #CTLD self --- @param #string Name Unique name of this type of cargo as set in the mission editor (not: UNIT name!), e.g. "Ammunition-1". +-- @param #string Name Unique name of this type of cargo as set in the mission editor (note: UNIT name!), e.g. "Ammunition-1". -- @param #number Mass Mass in kg of each static in kg, e.g. 100. -- @param #number Stock Number of groups in stock. Nil for unlimited. function CTLD:AddStaticsCargo(Name,Mass,Stock) @@ -2975,6 +2992,22 @@ function CTLD:AddStaticsCargo(Name,Mass,Stock) return self end +--- User function - Get a *generic* static-type loadable as #CTLD_CARGO object. +-- @param #CTLD self +-- @param #string Name Unique Unit(!) name of this type of cargo as set in the mission editor (not: GROUP name!), e.g. "Ammunition-1". +-- @param #number Mass Mass in kg of each static in kg, e.g. 100. +-- @return #CTLD_CARGO Cargo object +function CTLD:GetStaticsCargoFromTemplate(Name,Mass) + self:T(self.lid .. " GetStaticsCargoFromTemplate") + self.CargoCounter = self.CargoCounter + 1 + local type = CTLD_CARGO.Enum.STATIC + local template = STATIC:FindByName(Name,true):GetTypeName() + -- Crates are not directly loadable + local cargo = CTLD_CARGO:New(self.CargoCounter,Name,template,type,false,false,1,nil,nil,Mass,1) + --table.insert(self.Cargo_Statics,cargo) + return cargo +end + --- User function - Add *generic* repair crates loadable as cargo. This type will create crates that need to be loaded, moved, dropped and built. -- @param #CTLD self -- @param #string Name Unique name of this type of cargo. E.g. "Humvee". From 17ba35e23791adc9679dd35ab05bd594c9fabef3 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 1 Oct 2021 10:44:25 +0200 Subject: [PATCH 07/37] INTEL - also set isship and isground attributes on contacts --- Moose Development/Moose/Ops/Intelligence.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/Intelligence.lua b/Moose Development/Moose/Ops/Intelligence.lua index aee683f96..c8eeee3f4 100644 --- a/Moose Development/Moose/Ops/Intelligence.lua +++ b/Moose Development/Moose/Ops/Intelligence.lua @@ -748,6 +748,8 @@ function INTEL:CreateDetectedItems(DetectedGroups, RecceDetecting) item.velocity=group:GetVelocityVec3() item.speed=group:GetVelocityMPS() item.recce=RecceDetecting[groupname] + item.isground = group:IsGround() or false + item.isship = group:IsShip() or false self:T(string.format("%s group detect by %s/%s", groupname, RecceDetecting[groupname] or "unknown", item.recce or "unknown")) -- Add contact to table. self:AddContact(item) @@ -792,8 +794,8 @@ end function INTEL:GetDetectedUnits(Unit, DetectedUnits, RecceDetecting, DetectVisual, DetectOptical, DetectRadar, DetectIRST, DetectRWR, DetectDLINK) -- Get detected DCS units. - local detectedtargets=Unit:GetDetectedTargets(DetectVisual, DetectOptical, DetectRadar, DetectIRST, DetectRWR, DetectDLINK) local reccename = Unit:GetName() + local detectedtargets=Unit:GetDetectedTargets(DetectVisual, DetectOptical, DetectRadar, DetectIRST, DetectRWR, DetectDLINK) for DetectionObjectID, Detection in pairs(detectedtargets or {}) do local DetectedObject=Detection.object -- DCS#Object From 5c06584676b468f0e7d870f1636282e4939fcb2c Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 1 Oct 2021 14:52:00 +0200 Subject: [PATCH 08/37] UTILS - correction door check MI-8 --- Moose Development/Moose/Utilities/Utils.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 06d6affc3..bdb6a43ff 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -1578,7 +1578,7 @@ function UTILS.IsLoadingDoorOpen( unit_name ) if unit ~= nil then local type_name = unit:getTypeName() - if type_name == "Mi-8MT" and unit:getDrawArgumentValue(38) == 1 or unit:getDrawArgumentValue(86) == 1 or unit:getDrawArgumentValue(250) == 1 then + if type_name == "Mi-8MT" and unit:getDrawArgumentValue(38) == 1 or unit:getDrawArgumentValue(86) == 1 or unit:getDrawArgumentValue(250) < 0 then BASE:T(unit_name .. " Cargo doors are open or cargo door not present") ret_val = true end From b76486ef5fe0166218f2fb9a5ce130ce5bd7a575 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 1 Oct 2021 14:52:25 +0200 Subject: [PATCH 09/37] CSAR - added parameters for hovering rescue --- Moose Development/Moose/Ops/CSAR.lua | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Ops/CSAR.lua b/Moose Development/Moose/Ops/CSAR.lua index 6817db0e1..014efe0a0 100644 --- a/Moose Development/Moose/Ops/CSAR.lua +++ b/Moose Development/Moose/Ops/CSAR.lua @@ -97,6 +97,9 @@ -- self.pilotmustopendoors = false -- switch to true to enable check of open doors -- -- (added 0.1.9) -- self.suppressmessages = false -- switch off all messaging if you want to do your own +-- -- (added 0.1.11) +-- self.rescuehoverheight = 20 -- max height for a hovering rescue in meters +-- self.rescuehoverdistance = 10 -- max distance for a hovering rescue in meters -- -- ## 2.1 Experimental Features -- @@ -233,7 +236,7 @@ CSAR.AircraftType["Mi-24V"] = 8 --- CSAR class version. -- @field #string version -CSAR.version="0.1.10r5" +CSAR.version="0.1.11r1" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list @@ -362,6 +365,10 @@ function CSAR:New(Coalition, Template, Alias) self.approachdist_near = 3000 -- switch to 5 sec interval approach mode, meters self.pilotmustopendoors = false -- switch to true to enable check on open doors self.suppressmessages = false + + -- added 0.1.11r1 + self.rescuehoverheight = 20 + self.rescuehoverdistance = 10 -- WARNING - here\'ll be dragons -- for this to work you need to de-sanitize your mission environment in \Scripts\MissionScripting.lua @@ -1200,15 +1207,16 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG end if _heliUnit:InAir() and _unitsInHelicopter + 1 <= _maxUnits then - - if _distance < 8.0 then + -- TODO - make variable + if _distance < self.rescuehoverdistance then --check height! local leaderheight = _woundedLeader:GetHeight() if leaderheight < 0 then leaderheight = 0 end local _height = _heliUnit:GetHeight() - leaderheight - - if _height <= 20.0 then + + -- TODO - make variable + if _height <= self.rescuehoverheight then local _time = self.hoverStatus[_lookupKeyHeli] From 77e60881147275d07d0bf0e57d0956a080356cb7 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 1 Oct 2021 14:54:16 +0200 Subject: [PATCH 10/37] UTILS - corrected open door check MI-8 --- Moose Development/Moose/Utilities/Utils.lua | 323 +++++++++++--------- 1 file changed, 183 insertions(+), 140 deletions(-) diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 71c3d524a..bdb6a43ff 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -1,13 +1,13 @@ --- This module contains derived utilities taken from the MIST framework, which are excellent tools to be reused in an OO environment. --- --- ### Authors: --- +-- +-- ### Authors: +-- -- * Grimes : Design & Programming of the MIST framework. --- +-- -- ### Contributions: --- --- * FlightControl : Rework to OO framework --- +-- +-- * FlightControl : Rework to OO framework. +-- -- @module Utils -- @image MOOSE.JPG @@ -18,7 +18,7 @@ -- @field White -- @field Orange -- @field Blue - + SMOKECOLOR = trigger.smokeColor -- #SMOKECOLOR --- @type FLARECOLOR @@ -94,7 +94,7 @@ CALLSIGN={ Texaco=1, Arco=2, Shell=3, - }, + }, -- JTAC JTAC={ Axeman=1, @@ -163,31 +163,31 @@ UTILS = { UTILS.IsInstanceOf = function( object, className ) -- Is className NOT a string ? if not type( className ) == 'string' then - + -- Is className a Moose class ? if type( className ) == 'table' and className.IsInstanceOf ~= nil then - + -- Get the name of the Moose class as a string className = className.ClassName - + -- className is neither a string nor a Moose class, throw an error else - + -- I'm not sure if this should take advantage of MOOSE logging function, or throw an error for pcall local err_str = 'className parameter should be a string; parameter received: '..type( className ) return false -- error( err_str ) - + end end - + -- Is the object a Moose class instance ? if type( object ) == 'table' and object.IsInstanceOf ~= nil then - + -- Use the IsInstanceOf method of the BASE class return object:IsInstanceOf( className ) else - + -- If the object is not an instance of a Moose class, evaluate against lua basic data types local basicDataTypes = { 'string', 'number', 'function', 'boolean', 'nil', 'table' } for _, basicDataType in ipairs( basicDataTypes ) do @@ -196,7 +196,7 @@ UTILS.IsInstanceOf = function( object, className ) end end end - + -- Check failed return false end @@ -208,7 +208,7 @@ end UTILS.DeepCopy = function(object) local lookup_table = {} - + -- Copy function. local function _copy(object) if type(object) ~= "table" then @@ -216,20 +216,20 @@ UTILS.DeepCopy = function(object) elseif lookup_table[object] then return lookup_table[object] end - + local new_table = {} - + lookup_table[object] = new_table - + for index, value in pairs(object) do new_table[_copy(index)] = _copy(value) end - + return setmetatable(new_table, getmetatable(object)) end - + local objectreturn = _copy(object) - + return objectreturn end @@ -239,19 +239,19 @@ end UTILS.OneLineSerialize = function( tbl ) -- serialization of a table all on a single line, no comments, made to replace old get_table_string function lookup_table = {} - + local function _Serialize( tbl ) if type(tbl) == 'table' then --function only works for tables! - + if lookup_table[tbl] then return lookup_table[object] end local tbl_str = {} - + lookup_table[tbl] = tbl_str - + tbl_str[#tbl_str + 1] = '{' for ind,val in pairs(tbl) do -- serialize its fields @@ -299,7 +299,7 @@ UTILS.OneLineSerialize = function( tbl ) -- serialization of a table all on a s env.info('unable to serialize value type ' .. routines.utils.basicSerialize(type(val)) .. ' at index ' .. tostring(ind)) env.info( debug.traceback() ) end - + end tbl_str[#tbl_str + 1] = '}' return table.concat(tbl_str) @@ -307,7 +307,7 @@ UTILS.OneLineSerialize = function( tbl ) -- serialization of a table all on a s return tostring(tbl) end end - + local objectreturn = _Serialize(tbl) return objectreturn end @@ -339,18 +339,34 @@ UTILS.MetersToNM = function(meters) return meters/1852 end +UTILS.KiloMetersToNM = function(kilometers) + return kilometers/1852*1000 +end + UTILS.MetersToSM = function(meters) return meters/1609.34 end +UTILS.KiloMetersToSM = function(kilometers) + return kilometers/1609.34*1000 +end + UTILS.MetersToFeet = function(meters) return meters/0.3048 end +UTILS.KiloMetersToFeet = function(kilometers) + return kilometers/0.3048*1000 +end + UTILS.NMToMeters = function(NM) return NM*1852 end +UTILS.NMToKiloMeters = function(NM) + return NM*1852/1000 +end + UTILS.FeetToMeters = function(feet) return feet*0.3048 end @@ -400,7 +416,7 @@ end -- @param #number Celcius Temperature in degrees Celsius. -- @return #number Temperature in degrees Farenheit. UTILS.CelciusToFarenheit = function( Celcius ) - return Celcius * 9/5 + 32 + return Celcius * 9/5 + 32 end --- Convert pressure from hecto Pascal (hPa) to inches of mercury (inHg). @@ -415,7 +431,7 @@ end -- @param #number altitude Altitude in feet -- @return #number Corrected KIAS UTILS.KnotsToAltKIAS = function( knots, altitude ) - return (knots * 0.018 * (altitude / 1000)) + knots + return (knots * 0.018 * (altitude / 1000)) + knots end --- Convert pressure from hecto Pascal (hPa) to millimeters of mercury (mmHg). @@ -534,23 +550,23 @@ UTILS.tostringMGRS = function(MGRS, acc) --R2.1 -- Test if Easting/Northing have less than 4 digits. --MGRS.Easting=123 -- should be 00123 --MGRS.Northing=5432 -- should be 05432 - + -- Truncate rather than round MGRS grid! local Easting=tostring(MGRS.Easting) local Northing=tostring(MGRS.Northing) - + -- Count number of missing digits. Easting/Northing should have 5 digits. However, it is passed as a number. Therefore, any leading zeros would not be displayed by lua. - local nE=5-string.len(Easting) + local nE=5-string.len(Easting) local nN=5-string.len(Northing) - + -- Get leading zeros (if any). for i=1,nE do Easting="0"..Easting end for i=1,nN do Northing="0"..Northing end - + -- Return MGRS string. return string.format("%s %s %s %s", MGRS.UTMZone, MGRS.MGRSDigraph, string.sub(Easting, 1, acc), string.sub(Northing, 1, acc)) end - + end @@ -578,7 +594,7 @@ function UTILS.spairs( t, order ) for k in pairs(t) do keys[#keys+1] = k end -- if order function given, sort by it by passing the table and keys a, b, - -- otherwise just sort the keys + -- otherwise just sort the keys if order then table.sort(keys, function(a,b) return order(t, a, b) end) else @@ -604,7 +620,7 @@ function UTILS.kpairs( t, getkey, order ) for k, o in pairs(t) do keys[#keys+1] = k keyso[#keyso+1] = getkey( o ) end -- if order function given, sort by it by passing the table and keys a, b, - -- otherwise just sort the keys + -- otherwise just sort the keys if order then table.sort(keys, function(a,b) return order(t, a, b) end) else @@ -624,7 +640,7 @@ end -- Here is a customized version of pairs, which I called rpairs because it iterates over the table in a random order. function UTILS.rpairs( t ) -- collect the keys - + local keys = {} for k in pairs(t) do keys[#keys+1] = k end @@ -635,7 +651,7 @@ function UTILS.rpairs( t ) random[i] = keys[k] table.remove( keys, k ) end - + -- return the iterator function local i = 0 return function() @@ -751,12 +767,12 @@ end function UTILS.GetCharacters(str) local chars={} - + for i=1,#str do local c=str:sub(i,i) table.insert(chars, c) end - + return chars end @@ -765,15 +781,15 @@ end -- @param #boolean short (Optional) If true, use short output, i.e. (HH:)MM:SS without day. -- @return #string Time in format Hours:Minutes:Seconds+Days (HH:MM:SS+D). function UTILS.SecondsToClock(seconds, short) - + -- Nil check. if seconds==nil then return nil end - + -- Seconds local seconds = tonumber(seconds) - + -- Seconds of this day. local _seconds=seconds%(60*60*24) @@ -803,10 +819,10 @@ function UTILS.SecondsOfToday() -- Time in seconds. local time=timer.getAbsTime() - + -- Short format without days since mission start. local clock=UTILS.SecondsToClock(time, true) - + -- Time is now the seconds passed since last midnight. return UTILS.ClockToSeconds(clock) end @@ -821,24 +837,24 @@ end -- @param #string clock String of clock time. E.g., "06:12:35" or "5:1:30+1". Format is (H)H:(M)M:((S)S)(+D) H=Hours, M=Minutes, S=Seconds, D=Days. -- @return #number Seconds. Corresponds to what you cet from timer.getAbsTime() function. function UTILS.ClockToSeconds(clock) - + -- Nil check. if clock==nil then return nil end - + -- Seconds init. local seconds=0 - + -- Split additional days. local dsplit=UTILS.Split(clock, "+") - + -- Convert days to seconds. if #dsplit>1 then seconds=seconds+tonumber(dsplit[2])*60*60*24 end - -- Split hours, minutes, seconds + -- Split hours, minutes, seconds local tsplit=UTILS.Split(dsplit[1], ":") -- Get time in seconds @@ -856,7 +872,7 @@ function UTILS.ClockToSeconds(clock) end i=i+1 end - + return seconds end @@ -868,12 +884,12 @@ function UTILS.DisplayMissionTime(duration) local mission_time=Tnow-timer.getTime0() local mission_time_minutes=mission_time/60 local mission_time_seconds=mission_time%60 - local local_time=UTILS.SecondsToClock(Tnow) + local local_time=UTILS.SecondsToClock(Tnow) local text=string.format("Time: %s - %02d:%02d", local_time, mission_time_minutes, mission_time_seconds) MESSAGE:New(text, duration):ToAll() end ---- Replace illegal characters [<>|/?*:\\] in a string. +--- Replace illegal characters [<>|/?*:\\] in a string. -- @param #string Text Input text. -- @param #string ReplaceBy Replace illegal characters by this character or string. Default underscore "_". -- @return #string The input text with illegal chars replaced. @@ -894,28 +910,28 @@ function UTILS.RandomGaussian(x0, sigma, xmin, xmax, imax) -- Standard deviation. Default 10 if not given. sigma=sigma or 10 - + -- Max attempts. imax=imax or 100 - + local r local gotit=false local i=0 while not gotit do - + -- Uniform numbers in [0,1). We need two. local x1=math.random() local x2=math.random() - - -- Transform to Gaussian exp(-(x-x0)²/(2*sigma²). + + -- Transform to Gaussian exp(-(x-x0)°/(2*sigma°). r = math.sqrt(-2*sigma*sigma * math.log(x1)) * math.cos(2*math.pi * x2) + x0 - + i=i+1 if (r>=xmin and r<=xmax) or i>imax then gotit=true end end - + return r end @@ -940,9 +956,9 @@ function UTILS.Randomize(value, fac, lower, upper) else max=value+value*fac end - + local r=math.random(min, max) - + return r end @@ -961,6 +977,33 @@ function UTILS.VecNorm(a) return math.sqrt(UTILS.VecDot(a, a)) end +--- Calculate the distance between two 2D vectors. +-- @param DCS#Vec2 a Vector in 3D with x, y components. +-- @param DCS#Vec2 b Vector in 3D with x, y components. +-- @return #number Distance between the vectors. +function UTILS.VecDist2D(a, b) + + local c={x=b.x-a.x, y=b.y-a.y} + + local d=math.sqrt(c.x*c.x+c.y*c.y) + + return d +end + + +--- Calculate the distance between two 3D vectors. +-- @param DCS#Vec3 a Vector in 3D with x, y, z components. +-- @param DCS#Vec3 b Vector in 3D with x, y, z components. +-- @return #number Distance between the vectors. +function UTILS.VecDist3D(a, b) + + local c={x=b.x-a.x, y=b.y-a.y, z=b.z-a.z} + + local d=math.sqrt(UTILS.VecDot(c, c)) + + return d +end + --- Calculate the [cross product](https://en.wikipedia.org/wiki/Cross_product) of two 3D vectors. The result is a 3D vector. -- @param DCS#Vec3 a Vector in 3D with x, y, z components. -- @param DCS#Vec3 b Vector in 3D with x, y, z components. @@ -969,7 +1012,7 @@ function UTILS.VecCross(a, b) return {x=a.y*b.z - a.z*b.y, y=a.z*b.x - a.x*b.z, z=a.x*b.y - a.y*b.x} end ---- Calculate the difference between two 3D vectors by substracting the x,y,z components from each other. +--- Calculate the difference between two 3D vectors by substracting the x,y,z components from each other. -- @param DCS#Vec3 a Vector in 3D with x, y, z components. -- @param DCS#Vec3 b Vector in 3D with x, y, z components. -- @return DCS#Vec3 Vector c=a-b with c(i)=a(i)-b(i), i=x,y,z. @@ -977,7 +1020,7 @@ function UTILS.VecSubstract(a, b) return {x=a.x-b.x, y=a.y-b.y, z=a.z-b.z} end ---- Calculate the total vector of two 3D vectors by adding the x,y,z components of each other. +--- Calculate the total vector of two 3D vectors by adding the x,y,z components of each other. -- @param DCS#Vec3 a Vector in 3D with x, y, z components. -- @param DCS#Vec3 b Vector in 3D with x, y, z components. -- @return DCS#Vec3 Vector c=a+b with c(i)=a(i)+b(i), i=x,y,z. @@ -985,14 +1028,14 @@ function UTILS.VecAdd(a, b) return {x=a.x+b.x, y=a.y+b.y, z=a.z+b.z} end ---- Calculate the angle between two 3D vectors. +--- Calculate the angle between two 3D vectors. -- @param DCS#Vec3 a Vector in 3D with x, y, z components. -- @param DCS#Vec3 b Vector in 3D with x, y, z components. --- @return #number Angle alpha between and b in degrees. alpha=acos(a*b)/(|a||b|), (* denotes the dot product). +-- @return #number Angle alpha between and b in degrees. alpha=acos(a*b)/(|a||b|), (* denotes the dot product). function UTILS.VecAngle(a, b) local cosalpha=UTILS.VecDot(a,b)/(UTILS.VecNorm(a)*UTILS.VecNorm(b)) - + local alpha=0 if cosalpha>=0.9999999999 then --acos(1) is not defined. alpha=0 @@ -1000,8 +1043,8 @@ function UTILS.VecAngle(a, b) alpha=math.pi else alpha=math.acos(cosalpha) - end - + end + return math.deg(alpha) end @@ -1025,18 +1068,18 @@ function UTILS.HdgDiff(h1, h2) -- Angle in rad. local alpha= math.rad(tonumber(h1)) local beta = math.rad(tonumber(h2)) - + -- Runway vector. local v1={x=math.cos(alpha), y=0, z=math.sin(alpha)} local v2={x=math.cos(beta), y=0, z=math.sin(beta)} local delta=UTILS.VecAngle(v1, v2) - + return math.abs(delta) end ---- Translate 3D vector in the 2D (x,z) plane. y-component (usually altitude) unchanged. +--- Translate 3D vector in the 2D (x,z) plane. y-component (usually altitude) unchanged. -- @param DCS#Vec3 a Vector in 3D with x, y, z components. -- @param #number distance The distance to translate. -- @param #number angle Rotation angle in degrees. @@ -1052,21 +1095,21 @@ function UTILS.VecTranslate(a, distance, angle) return {x=TX, y=a.y, z=TY} end ---- Rotate 3D vector in the 2D (x,z) plane. y-component (usually altitude) unchanged. +--- Rotate 3D vector in the 2D (x,z) plane. y-component (usually altitude) unchanged. -- @param DCS#Vec3 a Vector in 3D with x, y, z components. -- @param #number angle Rotation angle in degrees. -- @return DCS#Vec3 Vector rotated in the (x,z) plane. function UTILS.Rotate2D(a, angle) local phi=math.rad(angle) - + local x=a.z local y=a.x - + local Z=x*math.cos(phi)-y*math.sin(phi) local X=x*math.sin(phi)+y*math.cos(phi) local Y=a.y - + local A={x=X, y=Y, z=Z} return A @@ -1084,17 +1127,17 @@ function UTILS.TACANToFrequency(TACANChannel, TACANMode) end if TACANMode ~= "X" and TACANMode ~= "Y" then return nil -- error in arguments - end - + end + -- This code is largely based on ED's code, in DCS World\Scripts\World\Radio\BeaconTypes.lua, line 137. -- I have no idea what it does but it seems to work local A = 1151 -- 'X', channel >= 64 local B = 64 -- channel >= 64 - + if TACANChannel < 64 then B = 1 end - + if TACANMode == 'Y' then A = 1025 if TACANChannel < 64 then @@ -1105,7 +1148,7 @@ function UTILS.TACANToFrequency(TACANChannel, TACANMode) A = 962 end end - + return (A + TACANChannel - B) * 1000000 end @@ -1132,13 +1175,13 @@ end -- @param #number Time (Optional) Abs. time in seconds. Default now, i.e. the value return from timer.getAbsTime(). -- @return #number Day of the mission. Mission starts on day 0. function UTILS.GetMissionDay(Time) - + Time=Time or timer.getAbsTime() - + local clock=UTILS.SecondsToClock(Time, false) - + local x=tonumber(UTILS.Split(clock, "+")[2]) - + return x end @@ -1148,11 +1191,11 @@ end function UTILS.GetMissionDayOfYear(Time) local Date, Year, Month, Day=UTILS.GetDCSMissionDate() - + local d=UTILS.GetMissionDay(Time) - + return UTILS.GetDayOfYear(Year, Month, Day)+d - + end --- Returns the current date. @@ -1164,20 +1207,20 @@ function UTILS.GetDate() -- Mission start date local date, year, month, day=UTILS.GetDCSMissionDate() - + local time=timer.getAbsTime() - + local clock=UTILS.SecondsToClock(time, false) - + local x=tonumber(UTILS.Split(clock, "+")[2]) - + local day=day+x end --- Returns the magnetic declination of the map. -- Returned values for the current maps are: --- +-- -- * Caucasus +6 (East), year ~ 2011 -- * NTTR +12 (East), year ~ 2011 -- * Normandy -10 (West), year ~ 1944 @@ -1191,7 +1234,7 @@ function UTILS.GetMagneticDeclination(map) -- Map. map=map or UTILS.GetDCSMap() - + local declination=0 if map==DCSMAP.Caucasus then declination=6 @@ -1228,12 +1271,12 @@ function UTILS.FileExists(file) end else return nil - end + end end --- Checks the current memory usage collectgarbage("count"). Info is printed to the DCS log file. Time stamp is the current mission runtime. --- @param #boolean output If true, print to DCS log file. --- @return #number Memory usage in kByte. +-- @param #boolean output If true, print to DCS log file. +-- @return #number Memory usage in kByte. function UTILS.CheckMemory(output) local time=timer.getTime() local clock=UTILS.SecondsToClock(time) @@ -1263,7 +1306,7 @@ function UTILS.GetCoalitionName(Coalition) else return "Unknown" end - + end --- Get the modulation name from its numerical value. @@ -1282,7 +1325,7 @@ function UTILS.GetModulationName(Modulation) else return "Unknown" end - + end --- Get the callsign name from its enumerator value @@ -1295,7 +1338,7 @@ function UTILS.GetCallsignName(Callsign) return name end end - + for name, value in pairs(CALLSIGN.AWACS) do if value==Callsign then return name @@ -1307,7 +1350,7 @@ function UTILS.GetCallsignName(Callsign) return name end end - + for name, value in pairs(CALLSIGN.Tanker) do if value==Callsign then return name @@ -1336,7 +1379,7 @@ function UTILS.GMTToLocalTimeDifference() elseif theatre==DCSMAP.Syria then return 3 -- Damascus is UTC+3 hours elseif theatre==DCSMAP.MarianaIslands then - return 10 -- Guam is UTC+10 hours. + return 10 -- Guam is UTC+10 hours. else BASE:E(string.format("ERROR: Unknown Map %s in UTILS.GMTToLocal function. Returning 0", tostring(theatre))) return 0 @@ -1353,11 +1396,11 @@ end function UTILS.GetDayOfYear(Year, Month, Day) local floor = math.floor - + local n1 = floor(275 * Month / 9) local n2 = floor((Month + 9) / 12) local n3 = (1 + floor((Year - 4 * floor(Year / 4) + 2) / 3)) - + return n1 - (n2 * n3) + Day - 30 end @@ -1370,14 +1413,14 @@ end -- @return #number Sun rise/set in seconds of the day. function UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, Rising, Tlocal) - -- Defaults + -- Defaults local zenith=90.83 local latitude=Latitude local longitude=Longitude local rising=Rising local n=DayOfYear Tlocal=Tlocal or 0 - + -- Short cuts. local rad = math.rad @@ -1404,47 +1447,47 @@ function UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, Rising, Tlocal) return val end end - + -- Convert the longitude to hour value and calculate an approximate time local lng_hour = longitude / 15 - + local t if rising then -- Rising time is desired t = n + ((6 - lng_hour) / 24) else -- Setting time is desired t = n + ((18 - lng_hour) / 24) end - + -- Calculate the Sun's mean anomaly local M = (0.9856 * t) - 3.289 - + -- Calculate the Sun's true longitude local L = fit_into_range(M + (1.916 * sin(M)) + (0.020 * sin(2 * M)) + 282.634, 0, 360) - + -- Calculate the Sun's right ascension local RA = fit_into_range(atan(0.91764 * tan(L)), 0, 360) - + -- Right ascension value needs to be in the same quadrant as L local Lquadrant = floor(L / 90) * 90 local RAquadrant = floor(RA / 90) * 90 RA = RA + Lquadrant - RAquadrant - + -- Right ascension value needs to be converted into hours RA = RA / 15 - + -- Calculate the Sun's declination local sinDec = 0.39782 * sin(L) local cosDec = cos(asin(sinDec)) - + -- Calculate the Sun's local hour angle local cosH = (cos(zenith) - (sinDec * sin(latitude))) / (cosDec * cos(latitude)) - + if rising and cosH > 1 then return "N/R" -- The sun never rises on this location on the specified date elseif cosH < -1 then return "N/S" -- The sun never sets on this location on the specified date end - + -- Finish calculating H and convert into hours local H if rising then @@ -1453,13 +1496,13 @@ function UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, Rising, Tlocal) H = acos(cosH) end H = H / 15 - + -- Calculate local mean time of rising/setting local T = H + RA - (0.06571 * t) - 6.622 -- Adjust back to UTC local UT = fit_into_range(T - lng_hour +Tlocal, 0, 24) - + return floor(UT)*60*60+frac(UT)*60*60--+Tlocal*60*60 end @@ -1534,17 +1577,17 @@ function UTILS.IsLoadingDoorOpen( unit_name ) local unit = Unit.getByName(unit_name) if unit ~= nil then local type_name = unit:getTypeName() - - if type_name == "Mi-8MT" and unit:getDrawArgumentValue(38) == 1 or unit:getDrawArgumentValue(86) == 1 or unit:getDrawArgumentValue(250) == 1 then - BASE:T(unit_name .. " Cargo doors are open or cargo door not present") + + if type_name == "Mi-8MT" and unit:getDrawArgumentValue(38) == 1 or unit:getDrawArgumentValue(86) == 1 or unit:getDrawArgumentValue(250) < 0 then + BASE:T(unit_name .. " Cargo doors are open or cargo door not present") ret_val = true end - + if type_name == "Mi-24P" and unit:getDrawArgumentValue(38) == 1 or unit:getDrawArgumentValue(86) == 1 then BASE:T(unit_name .. " a side door is open") ret_val = true end - + if type_name == "UH-1H" and unit:getDrawArgumentValue(43) == 1 or unit:getDrawArgumentValue(44) == 1 then BASE:T(unit_name .. " a side door is open ") ret_val = true @@ -1559,9 +1602,9 @@ function UTILS.IsLoadingDoorOpen( unit_name ) BASE:T(unit_name .. " all doors are closed") end return ret_val - + end -- nil - + return nil end @@ -1588,10 +1631,10 @@ function UTILS.GenerateVHFrequencies() local _skipFrequencies = { 214,274,291.5,295,297.5, 300.5,304,307,309.5,311,312,312.5,316, - 320,324,328,329,330,336,337, + 320,324,328,329,330,332,336,337, 342,343,348,351,352,353,358, 363,365,368,372.5,374, - 380,381,384,389,395,396, + 380,381,384,385,389,395,396, 414,420,430,432,435,440,450,455,462,470,485, 507,515,520,525,528,540,550,560,570,577,580, 602,625,641,662,670,680,682,690, @@ -1600,13 +1643,13 @@ function UTILS.GenerateVHFrequencies() 905,907,920,935,942,950,995, 1000,1025,1030,1050,1065,1116,1175,1182,1210 } - + local FreeVHFFrequencies = {} - + -- first range local _start = 200000 while _start < 400000 do - + -- skip existing NDB frequencies# local _found = false for _, value in pairs(_skipFrequencies) do @@ -1620,7 +1663,7 @@ function UTILS.GenerateVHFrequencies() end _start = _start + 10000 end - + -- second range _start = 400000 while _start < 850000 do @@ -1637,7 +1680,7 @@ function UTILS.GenerateVHFrequencies() end _start = _start + 10000 end - + -- third range _start = 850000 while _start <= 999000 do -- adjusted for Gazelle @@ -1677,7 +1720,7 @@ end -- @return #table Laser Codes. function UTILS.GenerateLaserCodes() local jtacGeneratedLaserCodes = {} - + -- helper function local function ContainsDigit(_number, _numberToFind) local _thisNumber = _number @@ -1691,7 +1734,7 @@ function UTILS.GenerateLaserCodes() end return false end - + -- generate list of laser codes local _code = 1111 local _count = 1 From 3c477b872af532c6c04a2c58d6b88f63cbca164f Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 1 Oct 2021 14:54:31 +0200 Subject: [PATCH 11/37] CSAR - hovering rescued parameters added --- Moose Development/Moose/Ops/CSAR.lua | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Ops/CSAR.lua b/Moose Development/Moose/Ops/CSAR.lua index 6817db0e1..014efe0a0 100644 --- a/Moose Development/Moose/Ops/CSAR.lua +++ b/Moose Development/Moose/Ops/CSAR.lua @@ -97,6 +97,9 @@ -- self.pilotmustopendoors = false -- switch to true to enable check of open doors -- -- (added 0.1.9) -- self.suppressmessages = false -- switch off all messaging if you want to do your own +-- -- (added 0.1.11) +-- self.rescuehoverheight = 20 -- max height for a hovering rescue in meters +-- self.rescuehoverdistance = 10 -- max distance for a hovering rescue in meters -- -- ## 2.1 Experimental Features -- @@ -233,7 +236,7 @@ CSAR.AircraftType["Mi-24V"] = 8 --- CSAR class version. -- @field #string version -CSAR.version="0.1.10r5" +CSAR.version="0.1.11r1" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list @@ -362,6 +365,10 @@ function CSAR:New(Coalition, Template, Alias) self.approachdist_near = 3000 -- switch to 5 sec interval approach mode, meters self.pilotmustopendoors = false -- switch to true to enable check on open doors self.suppressmessages = false + + -- added 0.1.11r1 + self.rescuehoverheight = 20 + self.rescuehoverdistance = 10 -- WARNING - here\'ll be dragons -- for this to work you need to de-sanitize your mission environment in \Scripts\MissionScripting.lua @@ -1200,15 +1207,16 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG end if _heliUnit:InAir() and _unitsInHelicopter + 1 <= _maxUnits then - - if _distance < 8.0 then + -- TODO - make variable + if _distance < self.rescuehoverdistance then --check height! local leaderheight = _woundedLeader:GetHeight() if leaderheight < 0 then leaderheight = 0 end local _height = _heliUnit:GetHeight() - leaderheight - - if _height <= 20.0 then + + -- TODO - make variable + if _height <= self.rescuehoverheight then local _time = self.hoverStatus[_lookupKeyHeli] From e7fd5db2c21814f5ecd49e67846d29341165f6f0 Mon Sep 17 00:00:00 2001 From: Applevangelist <72444570+Applevangelist@users.noreply.github.com> Date: Fri, 1 Oct 2021 17:20:17 +0200 Subject: [PATCH 12/37] Create Autolase.lua Added Class AUTOLASE --- .../Moose/Functional/Autolase.lua | 679 ++++++++++++++++++ 1 file changed, 679 insertions(+) create mode 100644 Moose Development/Moose/Functional/Autolase.lua diff --git a/Moose Development/Moose/Functional/Autolase.lua b/Moose Development/Moose/Functional/Autolase.lua new file mode 100644 index 000000000..f826b07ac --- /dev/null +++ b/Moose Development/Moose/Functional/Autolase.lua @@ -0,0 +1,679 @@ +--- **Functional** - Autolase targets in the field. +-- +-- **Main Features:** +-- +-- * Detect and lase contacts automaticallyt +-- * Targets are lased by threat priority order +-- * Use FSM events to link functionality into your scripts +-- * Easy setup +-- +-- === +-- +-- ### Author: **applevangelist** +-- @module Functional.Autolase +-- @image Designation.JPG +-- +-- Date: Oct 2021 + +--- +--- Class AUTOLASE +-- @type AUTOLASE +-- @field #string ClassName +-- @field #string lid +-- @field #number verbose +-- @field #string alias +-- @field #boolean debug +-- @field #string version +-- @extends Ops.Intel#INTEL + +--- Spot on! +-- +-- === +-- +-- # 1 Autolase concept +-- +-- * Detect and lase contacts automatically +-- * Targets are lased by threat priority order +-- * Use FSM events to link functionality into your scripts +-- * Easy set-up +-- * Targets are lased by threat priority order +-- +-- # 2 Basic usage +-- +-- ## 2.2 Set up a group of Recce Units: +-- +-- local FoxSet = SET_GROUP:New():FilterPrefixes("Recce"):FilterCoalitions("blue"):FilterStart() +-- +-- ## 2.3 (Optional) Set up a group of pilots, this will drive who sees the F10 menu entry: +-- +-- local Pilotset = SET_CLIENT:New():FilterCoalitions("blue"):FilterActive(true):FilterStart() +-- +-- ## 2.4 Set up and start Autolase: +-- +-- local autolaser = AUTOLASE:New(FoxSet,coalition.side.BLUE,"Wolfpack",Pilotset) +-- +-- ## 2.5 Example - Using a fixed laser code for a specific Recce unit: +-- +-- local recce = SPAWN:New("Reaper") +-- :InitDelayOff() +-- :OnSpawnGroup( +-- function (group) +-- local name = group:GetName() +-- autolaser:SetRecceLaserCode(name,1688) +-- end +-- ) +-- :InitCleanUp(60) +-- :InitLimit(1,0) +-- :SpawnScheduled(30,0.5) +-- +-- ## 2.6 Example - Inform pilots about a new target: +-- +-- function autolaser:OnAfterLasing(From,Event,To,LaserSpot) +-- local laserspot = LaserSpot -- #AUTOLASE.LaserSpot +-- local text = string.format("%s is lasing %s code %d\nat %s",laserspot.reccename,laserspot.unittype,laserspot.lasercode,laserspot.location) +-- local m = MESSAGE:New(text,15,"Autolase"):ToAll() +-- return self +-- end +-- +-- @field #AUTOLASE +AUTOLASE = { + ClassName = "AUTOLASE", + lid = "", + verbose = 2, + alias = "", + debug = false, +} + +---Laser spot info +-- @type AUTOLASE.LaserSpot +-- @field Core.Spot#SPOT laserspot +-- @field Wrapper.Unit#UNIT lasedunit +-- @field Wrapper.Unit#UNIT lasingunit +-- @field #number lasercode +-- @field #string location +-- @field #number timestamp +-- @field #string unitname +-- @field #string reccename +-- @field #string unittype + +--- AUTOLASE class version. +-- @field #string version +AUTOLASE.version = "0.0.1" + +------------------------------------------------------------------- +-- Begin Functional.Autolase.lua +------------------------------------------------------------------- + +--- Constructor for a new Autolase instance. +-- @param #AUTOLASE self +-- @param Core.Set#SET_GROUP RecceSet Set of detecting and lasing units +-- @param #number Coalition Coalition side. Can also be passed as a string "red", "blue" or "neutral". +-- @param #string Alias (Optional) An alias how this object is called in the logs etc. +-- @param Core.Set#SET_CLIENT PilotSet (Optional) Set of clients for precision bombing, steering menu creation. Leave nil for a coalition-wide F10 entry and display. +-- @return #AUTOLASE self +function AUTOLASE:New(RecceSet, Coalition, Alias, PilotSet) + BASE:T({RecceSet, Coalition, Alias, PilotSet}) + + -- Inherit everything from BASE class. + local self=BASE:Inherit(self, BASE:New()) -- #AUTOLASE + + if Coalition and type(Coalition)=="string" then + if Coalition=="blue" then + self.coalition=coalition.side.BLUE + elseif Coalition=="red" then + self.coalition=coalition.side.RED + elseif Coalition=="neutral" then + self.coalition=coalition.side.NEUTRAL + else + self:E("ERROR: Unknown coalition in AUTOLASE!") + end + end + + -- Set alias. + if Alias then + self.alias=tostring(Alias) + else + self.alias="Lion" + if self.coalition then + if self.coalition==coalition.side.RED then + self.alias="Wolf" + elseif self.coalition==coalition.side.BLUE then + self.alias="Fox" + end + end + end + + -- inherit from INTEL + local self=BASE:Inherit(self, INTEL:New(RecceSet, Coalition, Alias)) -- #AUTOLASE + + self.DetectVisual = true + self.DetectOptical = true + self.DetectRadar = false + self.DetectIRST = true + self.DetectRWR = false + self.DetectDLINK = true + self.LaserCodes = UTILS.GenerateLaserCodes() + self.LaseDistance = 4000 + self.LaseDuration = 120 + self.GroupsByThreat = {} + self.UnitsByThreat = {} + self.RecceNames = {} + self.RecceLaserCode = {} + self.RecceUnitNames= {} + self.maxlasing = 4 + self.CurrentLasing = {} + self.lasingindex = 0 + self.deadunitnotes = {} + self.usepilotset = false + self.reporttimeshort = 10 + self.reporttimelong = 30 + self.smoketargets = false + self.smokecolor = SMOKECOLOR.Red + + -- Set some string id for output to DCS.log file. + self.lid=string.format("AUTOLASE %s (%s) | ", self.alias, self.coalition and UTILS.GetCoalitionName(self.coalition) or "unknown") + + -- Add FSM transitions. + -- From State --> Event --> To State + self:AddTransition("*", "Monitor", "*") -- Start FSM + self:AddTransition("*", "Lasing", "*") -- Lasing target + self:AddTransition("*", "TargetLost", "*") -- Lost target + self:AddTransition("*", "TargetDestroyed", "*") -- Target destroyed + self:AddTransition("*", "RecceKIA", "*") -- Recce KIA + self:AddTransition("*", "LaserTimeout", "*") -- Laser timed out + + -- Menu Entry + if not PilotSet then + self.Menu = MENU_COALITION_COMMAND:New(self.coalition,"Autolase",nil,self.ShowStatus,self) + else + self.usepilotset = true + self.pilotset = PilotSet + self:HandleEvent(EVENTS.PlayerEnterAircraft) + self:SetPilotMenu() + end + + self:SetClusterAnalysis(false, false) + + self:__Start(2) + self:__Monitor(-5) + + return self + + ------------------------ + --- Pseudo Functions --- + ------------------------ + + --- Triggers the FSM event "Monitor". + -- @function [parent=#INTEL] Status + -- @param #INTEL self + + --- Triggers the FSM event "Monitor" after a delay. + -- @function [parent=#INTEL] __Status + -- @param #INTEL self + -- @param #number delay Delay in seconds. + + --- On After "RecceKIA" event. + -- @function [parent=#AUTOLASE] OnAfterRecceKIA + -- @param #AUTOLASE self + -- @param #string From The from state + -- @param #string Event The event + -- @param #string To The to state + -- @param #string RecceName The lost Recce + + --- On After "TargetDestroyed" event. + -- @function [parent=#AUTOLASE] OnAfterTargetDestroyed + -- @param #AUTOLASE self + -- @param #string From The from state + -- @param #string Event The event + -- @param #string To The to state + -- @param #string UnitName The destroyed unit\'s name + -- @param #string RecceName The Recce name lasing + + --- On After "TargetLost" event. + -- @function [parent=#AUTOLASE] OnAfterTargetLost + -- @param #AUTOLASE self + -- @param #string From The from state + -- @param #string Event The event + -- @param #string To The to state + -- @param #string UnitName The lost unit\'s name + -- @param #string RecceName The Recce name lasing + + --- On After "LaserTimeout" event. + -- @function [parent=#AUTOLASE] OnAfterLaserTimeout + -- @param #AUTOLASE self + -- @param #string From The from state + -- @param #string Event The event + -- @param #string To The to state + -- @param #string UnitName The lost unit\'s name + -- @param #string RecceName The Recce name lasing + + --- On After "Lasing" event. + -- @function [parent=#AUTOLASE] OnAfterLasing + -- @param #AUTOLASE self + -- @param #string From The from state + -- @param #string Event The event + -- @param #string To The to state + -- @param Functional.Autolase#AUTOLASE.LaserSpot LaserSpot The LaserSpot data table + +end + +------------------------------------------------------------------- +-- Helper Functions +------------------------------------------------------------------- + +--- (Internal) Function to set pilot menu. +-- @param #AUTOLASE self +-- @return #AUTOLASE self +function AUTOLASE:SetPilotMenu() + local pilottable = self.pilotset:GetSetObjects() or {} + for _,_unit in pairs (pilottable) do + local Unit = _unit -- Wrapper.Unit#UNIT + if Unit and Unit:IsAlive() then + local Group = Unit:GetGroup() + local lasemenu = MENU_GROUP_COMMAND:New(Group,"Autolase",nil,self.ShowStatus,self,Group) + lasemenu:Refresh() + end + end + return self +end + +--- (Internal) Event function for new pilots. +-- @param #AUTOLASE self +-- @param Core.Event#EVENTDATA EventData +-- @return #AUTOLASE self +function AUTOLASE:OnEventPlayerEnterAircraft(EventData) + self:SetPilotMenu() + return self +end + +--- Function to get a laser code by recce name +-- @param #AUTOLASE self +-- @param #string RecceName Name of the Recce +-- @return #AUTOLASE self +function AUTOLASE:GetLaserCode(RecceName) + local code = 1688 + if self.RecceLaserCode[RecceName] == nil then + code = self.LaserCodes[math.random(#self.LaserCodes)] + self.RecceLaserCode[RecceName] = code + else + code = self.RecceLaserCode[RecceName] + end + return code +end + +--- Function set max lasing targets +-- @param #AUTOLASE self +-- @param #number Number Max number of targets to lase at once +-- @return #AUTOLASE self +function AUTOLASE:SetMaxLasingTargets(Number) + self.maxlasing = Number or 4 + return self +end + +--- (User) Function to set a specific code to a Recce. +-- @param #AUTOLASE self +-- @param #string RecceName Name of the Recce +-- @param #number Code The lase code +-- @return #AUTOLASE self +function AUTOLASE:SetRecceLaserCode(RecceName, Code) + local code = Code or 1688 + self.RecceLaserCode[RecceName] = code + return self +end + +--- (User) Function to set message show times. +-- @param #AUTOLASE self +-- @param #number long Longer show time +-- @param #number short Shorter show time +-- @return #AUTOLASE self +function AUTOLASE:SetReportingTimes(long, short) + self.reporttimeshort = short or 10 + self.reporttimelong = long or 30 + return self +end + +--- (User) Function to set lasing distance in meters and duration in seconds +-- @param #AUTOLASE self +-- @param #number Distance (Max) distance for lasing in meters +-- @param #number Duration (Max) duration for lasing in seconds +-- @return #AUTOLASE self +function AUTOLASE:SetLasingParameters(Distance, Duration) + self.LaseDistance = distance or 4000 + self.LaseDuration = duration or 120 + return self +end + +--- (User) Function to set smoking of targets. +-- @param #AUTOLASE self +-- @param #boolean OnOff Switch smoking on or off +-- @param #number Color Smokecolor, e.g. SMOKECOLOR.Red +-- @return #AUTOLASE self +function AUTOLASE:SetSmokeTargets(OnOff,Color) + self.smoketargets = OnOff + self.smokecolor = Color or SMOKECOLOR.Red + return self +end + +--- (Internal) Function to check on lased targets. +-- @param #AUTOLASE self +-- @return #AUTOLASE self +function AUTOLASE:CleanCurrentLasing() + local lasingtable = self.CurrentLasing + local newtable = {} + local lasing = 0 + + for _ind,_entry in pairs(lasingtable) do + local entry = _entry -- #AUTOLASE.LaserSpot + local valid = 0 + local reccedead = false + local unitdead = false + local lostsight = false + local Tnow = timer.getAbsTime() + -- check recce dead + local recce = entry.lasingunit + if recce and recce:IsAlive() then + valid = valid + 1 + else + reccedead = true + --local text = string.format("Recce %s KIA!",entry.reccename) + --local m = MESSAGE:New(text,15,"Autolase"):ToAll() + self:__RecceKIA(2,entry.reccename) + end + -- check entry dead + local unit = entry.lasedunit + if unit and unit:IsAlive() == true then + valid = valid + 1 + else + unitdead = true + if not self.deadunitnotes[entry.unitname] then + --local text = string.format("Unit %s destroyed! Good job!",entry.unitname) + --local m = MESSAGE:New(text,15,"Autolase"):ToAll() + self.deadunitnotes[entry.unitname] = true + self:__TargetDestroyed(2,entry.unitname,entry.reccename) + end + end + -- check entry out of sight + if not reccedead and not unitdead then + local coord = unit:GetCoordinate() -- Core.Point#COORDINATE + local coord2 = recce:GetCoordinate() -- Core.Point#COORDINATE + local dist = coord2:Get2DDistance(coord) + if dist <= self.LaseDistance then + valid = valid + 1 + else + lostsight = true + entry.laserspot:LaseOff() + --local text = string.format("Lost sight of unit %s.",entry.unitname) + --local m = MESSAGE:New(text,15,"Autolase"):ToAll() + self:__TargetLost(2,entry.unitname,entry.reccename) + end + end + -- check timed out + local timestamp = entry.timestamp + if Tnow - timestamp < self.LaseDuration then + valid = valid + 1 + else + lostsight = true + entry.laserspot:LaseOff() + --local text = string.format("Lost sight of unit %s.",entry.unitname) + --local m = MESSAGE:New(text,15,"Autolase"):ToAll() + self:__LaserTimeout(2,entry.unitname,entry.reccename) + end + if valid == 4 then + self.lasingindex = self.lasingindex + 1 + newtable[self.lasingindex] = entry + lasing = lasing + 1 + end + end + self.CurrentLasing = newtable + return lasing +end + +--- (Internal) Function to show status. +-- @param #AUTOLASE self +-- @param Wrapper.Group#GROUP Group (Optional) show to a certain group +-- @return #AUTOLASE self +function AUTOLASE:ShowStatus(Group) + local report = REPORT:New("Autolase") + local lines = 0 + for _ind,_entry in pairs(self.CurrentLasing) do + local entry = _entry -- #AUTOLASE.LaserSpot + local reccename = entry.reccename + local typename = entry.unittype + local code = entry.lasercode + local locationstring = entry.location + local text = string.format("%s lasing %s code %d\nat %s",reccename,typename,code,locationstring) + report:AddIndent(text,"|") + lines = lines + 1 + end + if lines == 0 then + report:AddIndent("No targets!","|") + end + local reporttime = self.reporttimelong + if lines == 0 then reporttime = self.reporttimeshort end + if Group and Group:IsAlive() then + local m = MESSAGE:New(report:Text(),reporttime,"Info"):ToGroup(Group) + else + local m = MESSAGE:New(report:Text(),reporttime,"Info"):ToCoalition(self.coalition) + end + return self +end + +------------------------------------------------------------------- +-- FSM Functions +------------------------------------------------------------------- + +--- (Internal) FSM Function for monitoring +-- @param #AUTOLASE self +-- @param #string From The from state +-- @param #string Event The event +-- @param #string To The to state +-- @return #AUTOLASE self +function AUTOLASE:onafterMonitor(From, Event, To) + self:T({From, Event, To}) + + -- Housekeeping + local countlases = self:CleanCurrentLasing() + + local detecteditems = self.Contacts or {} -- #table of Ops.Intelligence#INTEL.Contact + local groupsbythreat = {} + --self:T("Detected Items:") + --self:T({detecteditems}) + local report = REPORT:New("Detections") + local lines = 0 + for _,_contact in pairs(detecteditems) do + local contact = _contact -- Ops.Intelligence#INTEL.Contact + local grp = contact.group + local coord = contact.position + local reccename = contact.recce + local reccegrp = UNIT:FindByName(reccename) + local reccecoord = reccegrp:GetCoordinate() + local distance = math.floor(reccecoord:Get2DDistance(coord)) + local text = string.format("%s of %s | Distance %d km | Threatlevel %d",contact.attribute, contact.groupname, distance/ 1000, contact.threatlevel) + report:Add(text) + self:T(text) + lines = lines + 1 + -- sort out groups beyond sight + if distance <= self.LaseDistance then + table.insert(groupsbythreat,{contact.group,contact.threatlevel}) + self.RecceNames[contact.groupname] = contact.recce + end + end + + self.GroupsByThreat = groupsbythreat + + if self.verbose > 2 and lines > 0 then + local m=MESSAGE:New(report:Text(),self.reporttimeshort,"Autolase"):ToAll() + end + + table.sort(self.GroupsByThreat, function(a,b) + local aNum = a[2] -- Coin value of a + local bNum = b[2] -- Coin value of b + return aNum > bNum -- Return their comparisons, < for ascending, > for descending + end) + + --self:T("Groups by Threat") + --self:T({self.GroupsByThreat}) + + -- build table of Units + local unitsbythreat = {} + for _,_entry in ipairs(self.GroupsByThreat) do + local group = _entry[1] -- Wrapper.Group#GROUP + if group and group:IsAlive() then + local units = group:GetUnits() + local reccename = self.RecceNames[group:GetName()] + for _,_unit in pairs(units) do + local unit = _unit -- Wrapper.Unit#UNIT + if unit and unit:IsAlive() then + local threat = unit:GetThreatLevel() + if threat > 0 then + local unitname = unit:GetName() + table.insert(unitsbythreat,{unit,threat}) + self.RecceUnitNames[unitname] = reccename + end + end + end + end + end + + self.UnitsByThreat = unitsbythreat + + table.sort(self.UnitsByThreat, function(a,b) + local aNum = a[2] -- Coin value of a + local bNum = b[2] -- Coin value of b + return aNum > bNum -- Return their comparisons, < for ascending, > for descending + end) + + local unitreport = REPORT:New("Detected Units") + + local lines = 0 + for _,_entry in ipairs(self.UnitsByThreat) do + local threat = _entry[2] + local unit = _entry[1] + local unitname = unit:GetName() + local text = string.format("Unit %s | Threatlevel %d | Detected by %s",unitname,threat,self.RecceUnitNames[unitname]) + unitreport:Add(text) + lines = lines + 1 + self:T(text) + end + + if self.verbose > 2 and lines > 0 then + local m=MESSAGE:New(unitreport:Text(),self.reporttimeshort,"Autolase"):ToAll() + end + + -- lase targets + local targets = countlases or 0 + for _,_entry in pairs(self.UnitsByThreat) do + local unit = _entry[1] -- Wrapper.Unit#UNIT + local unitname = unit:GetName() + local reccename = self.RecceUnitNames[unitname] + local recce = UNIT:FindByName(reccename) + if targets < self.maxlasing and unit:IsAlive() == true then + targets = targets + 1 + local code = self:GetLaserCode(reccename) + local spot = SPOT:New(recce) + spot:LaseOn(unit,code,self.LaseDuration) + local locationstring = unit:GetCoordinate():ToStringLLDDM() + --local text = string.format("%s is lasing %s code %d\nat %s",reccename,unit:GetTypeName(),code,locationstring) + --local m = MESSAGE:New(text,15,"Autolase"):ToAllIf(self.debug) + local laserspot = { -- #AUTOLASE.LaserSpot + laserspot = spot, + lasedunit = unit, + lasingunit = recce, + lasercode = code, + location = locationstring, + timestamp = timer.getAbsTime(), + unitname = unitname, + reccename = reccename, + unittype = unit:GetTypeName(), + } + if self.smoketargets then + local coord = unit:GetCoordinate() + coord:Smoke(self.smokecolor) + end + self.lasingindex = self.lasingindex + 1 + self.CurrentLasing[self.lasingindex] = laserspot + self:__Lasing(2,laserspot) + end + end + + self:__Monitor(-20) + return self +end + +--- (Internal) FSM Function onbeforeRecceKIA +-- @param #AUTOLASE self +-- @param #string From The from state +-- @param #string Event The event +-- @param #string To The to state +-- @param #string RecceName The lost Recce +-- @return #AUTOLASE self +function AUTOLASE:onbeforeRecceKIA(From,Event,To,RecceName) + self:T({From, Event, To, RecceName}) + local text = string.format("Recce %s KIA!",RecceName) + local m = MESSAGE:New(text,self.reporttimeshort,"Autolase"):ToAllIf(self.debug) + return self +end + +--- (Internal) FSM Function onbeforeTargetDestroyed +-- @param #AUTOLASE self +-- @param #string From The from state +-- @param #string Event The event +-- @param #string To The to state +-- @param #string UnitName The destroyed unit\'s name +-- @param #string RecceName The Recce name lasing +-- @return #AUTOLASE self +function AUTOLASE:onbeforeTargetDestroyed(From,Event,To,UnitName,RecceName) + self:T({From, Event, To, UnitName, RecceName}) + local text = string.format("Unit %s destroyed! Good job!",UnitName) + local m = MESSAGE:New(text,self.reporttimeshort,"Autolase"):ToAllIf(self.debug) + return self +end + +--- (Internal) FSM Function onbeforeTargetLost +-- @param #AUTOLASE self +-- @param #string From The from state +-- @param #string Event The event +-- @param #string To The to state +-- @param #string UnitName The lost unit\'s name +-- @param #string RecceName The Recce name lasing +-- @return #AUTOLASE self +function AUTOLASE:onbeforeTargetLost(From,Event,To,UnitName,RecceName) + self:T({From, Event, To, UnitName,RecceName}) + local text = string.format("%s lost sight of unit %s.",RecceName,UnitName) + local m = MESSAGE:New(text,self.reporttimeshort,"Autolase"):ToAllIf(self.debug) + return self +end + +--- (Internal) FSM Function onbeforeLaserTimeout +-- @param #AUTOLASE self +-- @param #string From The from state +-- @param #string Event The event +-- @param #string To The to state +-- @param #string UnitName The lost unit\'s name +-- @param #string RecceName The Recce name lasing +-- @return #AUTOLASE self +function AUTOLASE:onbeforeLaserTimeout(From,Event,To,UnitName,RecceName) + self:T({From, Event, To, UnitName,RecceName}) + local text = string.format("%s laser timeout on unit %s.",RecceName,UnitName) + local m = MESSAGE:New(text,self.reporttimeshort,"Autolase"):ToAllIf(self.debug) + return self +end + +--- (Internal) FSM Function onbeforeLasing +-- @param #AUTOLASE self +-- @param #string From The from state +-- @param #string Event The event +-- @param #string To The to state +-- @param Functional.Autolase#AUTOLASE.LaserSpot LaserSpot The LaserSpot data table +-- @return #AUTOLASE self +function AUTOLASE:onbeforeLasing(From,Event,To,LaserSpot) + self:T({From, Event, To, LaserSpot.unittype}) + local laserspot = LaserSpot -- #AUTOLASE.LaserSpot + local text = string.format("%s is lasing %s code %d\nat %s",laserspot.reccename,laserspot.unittype,laserspot.lasercode,laserspot.location) + local m = MESSAGE:New(text,self.reporttimeshort,"Autolase"):ToAllIf(self.debug) + return self +end + +------------------------------------------------------------------- +-- End Functional.Autolase.lua +------------------------------------------------------------------- From c57d8399271158de24cba9bb238bea6677e76ce0 Mon Sep 17 00:00:00 2001 From: Applevangelist <72444570+Applevangelist@users.noreply.github.com> Date: Fri, 1 Oct 2021 17:22:58 +0200 Subject: [PATCH 13/37] Update Moose.files Add Autolase.lua --- Moose Setup/Moose.files | 1 + 1 file changed, 1 insertion(+) diff --git a/Moose Setup/Moose.files b/Moose Setup/Moose.files index 077e75168..4f0e34d30 100644 --- a/Moose Setup/Moose.files +++ b/Moose Setup/Moose.files @@ -69,6 +69,7 @@ Functional/Warehouse.lua Functional/Fox.lua Functional/Mantis.lua Functional/Shorad.lua +Functional/Autolase.lua Ops/Airboss.lua Ops/RecoveryTanker.lua From 920646ea74a231bba7ef89e273d647cd4fd3a2f2 Mon Sep 17 00:00:00 2001 From: Applevangelist <72444570+Applevangelist@users.noreply.github.com> Date: Fri, 1 Oct 2021 17:24:25 +0200 Subject: [PATCH 14/37] Update Modules.lua added autolase --- Moose Development/Moose/Modules.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/Moose Development/Moose/Modules.lua b/Moose Development/Moose/Modules.lua index 7ad1b1829..1c4bd1c6a 100644 --- a/Moose Development/Moose/Modules.lua +++ b/Moose Development/Moose/Modules.lua @@ -70,6 +70,7 @@ __Moose.Include( 'Scripts/Moose/Functional/Warehouse.lua' ) __Moose.Include( 'Scripts/Moose/Functional/Fox.lua' ) __Moose.Include( 'Scripts/Moose/Functional/Mantis.lua' ) __Moose.Include( 'Scripts/Moose/Functional/Shorad.lua' ) +__Moose.Include( 'Scripts/Moose/Functional/Autolase.lua' ) __Moose.Include( 'Scripts/Moose/Ops/Airboss.lua' ) __Moose.Include( 'Scripts/Moose/Ops/RecoveryTanker.lua' ) From 4c3a97e2b2fa1e4a62a86d2740d2cea12dfa2503 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 1 Oct 2021 17:29:22 +0200 Subject: [PATCH 15/37] Autolase! --- Moose Development/Moose/Functional/Autolase.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Functional/Autolase.lua b/Moose Development/Moose/Functional/Autolase.lua index f826b07ac..2b247ab90 100644 --- a/Moose Development/Moose/Functional/Autolase.lua +++ b/Moose Development/Moose/Functional/Autolase.lua @@ -2,7 +2,7 @@ -- -- **Main Features:** -- --- * Detect and lase contacts automaticallyt +-- * Detect and lase contacts automatically -- * Targets are lased by threat priority order -- * Use FSM events to link functionality into your scripts -- * Easy setup @@ -13,7 +13,7 @@ -- @module Functional.Autolase -- @image Designation.JPG -- --- Date: Oct 2021 +-- Date: 01 Oct 2021 --- --- Class AUTOLASE From 968d1783174d678f740f8aacfac48951accaea74 Mon Sep 17 00:00:00 2001 From: Applevangelist <72444570+Applevangelist@users.noreply.github.com> Date: Sat, 2 Oct 2021 10:18:13 +0200 Subject: [PATCH 16/37] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b622326a8..5a1979522 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,6 @@ MOOSE has a living (chat and video) community of users, beta testers and contrib Kind regards, -FlightControl (FC) +The Moose Team From cd79c57a271ce87b8a976880aacd5884a493d995 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 2 Oct 2021 14:45:37 +0200 Subject: [PATCH 17/37] AUTOLASE - added Cancel FSM to stop, docu changes, option to notify pilots in the set only --- .../Moose/Functional/Autolase.lua | 135 ++++++++++++++---- 1 file changed, 105 insertions(+), 30 deletions(-) diff --git a/Moose Development/Moose/Functional/Autolase.lua b/Moose Development/Moose/Functional/Autolase.lua index 2b247ab90..51b2856cd 100644 --- a/Moose Development/Moose/Functional/Autolase.lua +++ b/Moose Development/Moose/Functional/Autolase.lua @@ -1,19 +1,31 @@ --- **Functional** - Autolase targets in the field. -- +-- === +-- +-- **AUOTLASE** - Autolase targets in the field. +-- +-- === +-- +-- ## Missions: +-- +-- ### [Autolase](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/) +-- +-- === +-- -- **Main Features:** -- -- * Detect and lase contacts automatically -- * Targets are lased by threat priority order -- * Use FSM events to link functionality into your scripts --- * Easy setup +-- * Easy setup -- -- === -- -- ### Author: **applevangelist** -- @module Functional.Autolase -- @image Designation.JPG --- --- Date: 01 Oct 2021 + +-- Date: Oct 2021 --- --- Class AUTOLASE @@ -66,20 +78,16 @@ -- :InitLimit(1,0) -- :SpawnScheduled(30,0.5) -- --- ## 2.6 Example - Inform pilots about a new target: +-- ## 2.6 Example - Inform pilots about events: -- --- function autolaser:OnAfterLasing(From,Event,To,LaserSpot) --- local laserspot = LaserSpot -- #AUTOLASE.LaserSpot --- local text = string.format("%s is lasing %s code %d\nat %s",laserspot.reccename,laserspot.unittype,laserspot.lasercode,laserspot.location) --- local m = MESSAGE:New(text,15,"Autolase"):ToAll() --- return self --- end +-- autolaser:SetNotifyPilots(true) -- defaults to true, also shown if debug == true +-- -- Note - message are shown to pilots in the #SET_CLIENT only if using the pilotset option, else to the coalition. -- -- @field #AUTOLASE AUTOLASE = { ClassName = "AUTOLASE", lid = "", - verbose = 2, + verbose = 0, alias = "", debug = false, } @@ -98,7 +106,7 @@ AUTOLASE = { --- AUTOLASE class version. -- @field #string version -AUTOLASE.version = "0.0.1" +AUTOLASE.version = "0.0.2" ------------------------------------------------------------------- -- Begin Functional.Autolase.lua @@ -169,6 +177,7 @@ function AUTOLASE:New(RecceSet, Coalition, Alias, PilotSet) self.reporttimelong = 30 self.smoketargets = false self.smokecolor = SMOKECOLOR.Red + self.notifypilots = true -- Set some string id for output to DCS.log file. self.lid=string.format("AUTOLASE %s (%s) | ", self.alias, self.coalition and UTILS.GetCoalitionName(self.coalition) or "unknown") @@ -181,6 +190,7 @@ function AUTOLASE:New(RecceSet, Coalition, Alias, PilotSet) self:AddTransition("*", "TargetDestroyed", "*") -- Target destroyed self:AddTransition("*", "RecceKIA", "*") -- Recce KIA self:AddTransition("*", "LaserTimeout", "*") -- Laser timed out + self:AddTransition("*", "Cancel", "*") -- Stop Autolase -- Menu Entry if not PilotSet then @@ -195,7 +205,7 @@ function AUTOLASE:New(RecceSet, Coalition, Alias, PilotSet) self:SetClusterAnalysis(false, false) self:__Start(2) - self:__Monitor(-5) + self:__Monitor(math.random(5,10)) return self @@ -204,12 +214,21 @@ function AUTOLASE:New(RecceSet, Coalition, Alias, PilotSet) ------------------------ --- Triggers the FSM event "Monitor". - -- @function [parent=#INTEL] Status - -- @param #INTEL self + -- @function [parent=#AUTOLASE] Status + -- @param #AUTOLASE self --- Triggers the FSM event "Monitor" after a delay. - -- @function [parent=#INTEL] __Status - -- @param #INTEL self + -- @function [parent=#AUTOLASE] __Status + -- @param #AUTOLASE self + -- @param #number delay Delay in seconds. + + --- Triggers the FSM event "Cancel". + -- @function [parent=#AUTOLASE] Cancel + -- @param #AUTOLASE self + + --- Triggers the FSM event "Cancel" after a delay. + -- @function [parent=#AUTOLASE] __Cancel + -- @param #AUTOLASE self -- @param #number delay Delay in seconds. --- On After "RecceKIA" event. @@ -270,7 +289,7 @@ function AUTOLASE:SetPilotMenu() local Unit = _unit -- Wrapper.Unit#UNIT if Unit and Unit:IsAlive() then local Group = Unit:GetGroup() - local lasemenu = MENU_GROUP_COMMAND:New(Group,"Autolase",nil,self.ShowStatus,self,Group) + local lasemenu = MENU_GROUP_COMMAND:New(Group,"Autolase Status",nil,self.ShowStatus,self,Group) lasemenu:Refresh() end end @@ -310,6 +329,15 @@ function AUTOLASE:SetMaxLasingTargets(Number) return self end +--- Function set notify pilots on events +-- @param #AUTOLASE self +-- @param #boolean OnOff Switch messaging on (true) or off (false) +-- @return #AUTOLASE self +function AUTOLASE:SetNotifyPilots(OnOff) + self.notifypilots = OnOff and true + return self +end + --- (User) Function to set a specific code to a Recce. -- @param #AUTOLASE self -- @param #string RecceName Name of the Recce @@ -458,6 +486,29 @@ function AUTOLASE:ShowStatus(Group) return self end +--- (Internal) Function to show messages. +-- @param #AUTOLASE self +-- @param #string Message The message to be sent +-- @param #number Duration Duration in seconds +-- @return #AUTOLASE self +function AUTOLASE:NotifyPilots(Message,Duration) + if self.usepilotset then + local pilotset = self.pilotset:GetSetObjects() --#table + for _,_pilot in pairs(pilotset) do + local pilot = _pilot -- Wrapper.Unit#UNIT + if pilot and pilot:IsAlive() then + local Group = pilot:GetGroup() + local m = MESSAGE:New(Message,Duration,"Autolase"):ToGroup(Group) + end + end + elseif not self.debug then + local m = MESSAGE:New(Message,Duration,"Autolase"):ToCoalition(self.coalition) + else + local m = MESSAGE:New(Message,Duration,"Autolase"):ToAll() + end + return self +end + ------------------------------------------------------------------- -- FSM Functions ------------------------------------------------------------------- @@ -474,6 +525,8 @@ function AUTOLASE:onafterMonitor(From, Event, To) -- Housekeeping local countlases = self:CleanCurrentLasing() + self:SetPilotMenu() + local detecteditems = self.Contacts or {} -- #table of Ops.Intelligence#INTEL.Contact local groupsbythreat = {} --self:T("Detected Items:") @@ -596,7 +649,7 @@ function AUTOLASE:onafterMonitor(From, Event, To) end end - self:__Monitor(-20) + self:__Monitor(-30) return self end @@ -609,8 +662,10 @@ end -- @return #AUTOLASE self function AUTOLASE:onbeforeRecceKIA(From,Event,To,RecceName) self:T({From, Event, To, RecceName}) - local text = string.format("Recce %s KIA!",RecceName) - local m = MESSAGE:New(text,self.reporttimeshort,"Autolase"):ToAllIf(self.debug) + if self.notifypilots or self.debug then + local text = string.format("Recce %s KIA!",RecceName) + self:NotifyPilots(text,self.reporttimeshort) + end return self end @@ -624,8 +679,10 @@ end -- @return #AUTOLASE self function AUTOLASE:onbeforeTargetDestroyed(From,Event,To,UnitName,RecceName) self:T({From, Event, To, UnitName, RecceName}) - local text = string.format("Unit %s destroyed! Good job!",UnitName) - local m = MESSAGE:New(text,self.reporttimeshort,"Autolase"):ToAllIf(self.debug) + if self.notifypilots or self.debug then + local text = string.format("Unit %s destroyed! Good job!",UnitName) + self:NotifyPilots(text,self.reporttimeshort) + end return self end @@ -639,8 +696,10 @@ end -- @return #AUTOLASE self function AUTOLASE:onbeforeTargetLost(From,Event,To,UnitName,RecceName) self:T({From, Event, To, UnitName,RecceName}) - local text = string.format("%s lost sight of unit %s.",RecceName,UnitName) - local m = MESSAGE:New(text,self.reporttimeshort,"Autolase"):ToAllIf(self.debug) + if self.notifypilots or self.debug then + local text = string.format("%s lost sight of unit %s.",RecceName,UnitName) + self:NotifyPilots(text,self.reporttimeshort) + end return self end @@ -654,8 +713,10 @@ end -- @return #AUTOLASE self function AUTOLASE:onbeforeLaserTimeout(From,Event,To,UnitName,RecceName) self:T({From, Event, To, UnitName,RecceName}) - local text = string.format("%s laser timeout on unit %s.",RecceName,UnitName) - local m = MESSAGE:New(text,self.reporttimeshort,"Autolase"):ToAllIf(self.debug) + if self.notifypilots or self.debug then + local text = string.format("%s laser timeout on unit %s.",RecceName,UnitName) + self:NotifyPilots(text,self.reporttimeshort) + end return self end @@ -668,9 +729,23 @@ end -- @return #AUTOLASE self function AUTOLASE:onbeforeLasing(From,Event,To,LaserSpot) self:T({From, Event, To, LaserSpot.unittype}) - local laserspot = LaserSpot -- #AUTOLASE.LaserSpot - local text = string.format("%s is lasing %s code %d\nat %s",laserspot.reccename,laserspot.unittype,laserspot.lasercode,laserspot.location) - local m = MESSAGE:New(text,self.reporttimeshort,"Autolase"):ToAllIf(self.debug) + if self.notifypilots or self.debug then + local laserspot = LaserSpot -- #AUTOLASE.LaserSpot + local text = string.format("%s is lasing %s code %d\nat %s",laserspot.reccename,laserspot.unittype,laserspot.lasercode,laserspot.location) + self:NotifyPilots(text,self.reporttimeshort) + end + return self +end + +--- (Internal) FSM Function onbeforeCancel +-- @param #AUTOLASE self +-- @param #string From The from state +-- @param #string Event The event +-- @param #string To The to state +-- @return #AUTOLASE self +function AUTOLASE:onbeforeCancel(From,Event,To) + self:UnHandleEvent(EVENTS.PlayerEnterAircraft) + self:__Stop(2) return self end From aa7f11185d7e641c08482dbc9915add245b08245 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 5 Oct 2021 19:06:30 +0200 Subject: [PATCH 18/37] Small update --- .../Moose/Functional/Autolase.lua | 49 ++++++++++--------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/Moose Development/Moose/Functional/Autolase.lua b/Moose Development/Moose/Functional/Autolase.lua index 51b2856cd..c3fce644f 100644 --- a/Moose Development/Moose/Functional/Autolase.lua +++ b/Moose Development/Moose/Functional/Autolase.lua @@ -20,24 +20,7 @@ -- * Easy setup -- -- === --- --- ### Author: **applevangelist** --- @module Functional.Autolase --- @image Designation.JPG - --- Date: Oct 2021 - ---- ---- Class AUTOLASE --- @type AUTOLASE --- @field #string ClassName --- @field #string lid --- @field #number verbose --- @field #string alias --- @field #boolean debug --- @field #string version --- @extends Ops.Intel#INTEL - +-- --- Spot on! -- -- === @@ -83,6 +66,24 @@ -- autolaser:SetNotifyPilots(true) -- defaults to true, also shown if debug == true -- -- Note - message are shown to pilots in the #SET_CLIENT only if using the pilotset option, else to the coalition. -- +-- +-- ### Author: **applevangelist** +-- @module Functional.Autolase +-- @image Designation.JPG + +-- Date: Oct 2021 + +--- Class AUTOLASE +-- @type AUTOLASE +-- @field #string ClassName +-- @field #string lid +-- @field #number verbose +-- @field #string alias +-- @field #boolean debug +-- @field #string version +-- @extends Ops.Intel#INTEL + +--- -- @field #AUTOLASE AUTOLASE = { ClassName = "AUTOLASE", @@ -92,7 +93,7 @@ AUTOLASE = { debug = false, } ----Laser spot info +--- Laser spot info -- @type AUTOLASE.LaserSpot -- @field Core.Spot#SPOT laserspot -- @field Wrapper.Unit#UNIT lasedunit @@ -106,7 +107,7 @@ AUTOLASE = { --- AUTOLASE class version. -- @field #string version -AUTOLASE.version = "0.0.2" +AUTOLASE.version = "0.0.3" ------------------------------------------------------------------- -- Begin Functional.Autolase.lua @@ -156,13 +157,13 @@ function AUTOLASE:New(RecceSet, Coalition, Alias, PilotSet) self.DetectVisual = true self.DetectOptical = true - self.DetectRadar = false + self.DetectRadar = true self.DetectIRST = true - self.DetectRWR = false + self.DetectRWR = true self.DetectDLINK = true self.LaserCodes = UTILS.GenerateLaserCodes() - self.LaseDistance = 4000 - self.LaseDuration = 120 + self.LaseDistance = 5000 + self.LaseDuration = 300 self.GroupsByThreat = {} self.UnitsByThreat = {} self.RecceNames = {} From c34f30e4a3da926b88e4f223fbbabcb98c5f66f9 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 5 Oct 2021 19:06:40 +0200 Subject: [PATCH 19/37] Docu fixes --- Moose Development/Moose/Ops/CTLD.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 090eb8073..ebc2c6c6d 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -3046,7 +3046,7 @@ end --- User function - Activate Name #CTLD.CargoZone.Type ZoneType for this CTLD instance. -- @param #CTLD self -- @param #string Name Name of the zone to change in the ME. --- @param #CTLD.CargoZoneTyp ZoneType Type of zone this belongs to. +-- @param #CTLD.CargoZoneType ZoneType Type of zone this belongs to. -- @param #boolean NewState (Optional) Set to true to activate, false to switch off. function CTLD:ActivateZone(Name,ZoneType,NewState) self:T(self.lid .. " AddZone") @@ -3082,7 +3082,7 @@ end --- User function - Deactivate Name #CTLD.CargoZoneType ZoneType for this CTLD instance. -- @param #CTLD self -- @param #string Name Name of the zone to change in the ME. --- @param #CTLD.CargoZoneTyp ZoneType Type of zone this belongs to. +-- @param #CTLD.CargoZoneType ZoneType Type of zone this belongs to. function CTLD:DeactivateZone(Name,ZoneType) self:T(self.lid .. " AddZone") self:ActivateZone(Name,ZoneType,false) From b28b4db7fd5d0fac4e35e46c4c37c7ddccdfecca Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 5 Oct 2021 19:06:49 +0200 Subject: [PATCH 20/37] docu fixes --- Moose Development/Moose/Utilities/Utils.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index bdb6a43ff..85832d732 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -1549,7 +1549,7 @@ function UTILS.GetOSTime() end --- Shuffle a table accoring to Fisher Yeates algorithm ---@param #table table to be shuffled +--@param #table t Table to be shuffled --@return #table function UTILS.ShuffleTable(t) if t == nil or type(t) ~= "table" then From f7e7e2e41c829fd7e68cbf263b6417f842c45f45 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 5 Oct 2021 19:07:59 +0200 Subject: [PATCH 21/37] ZONE_CAPTURE_COALITION - fix an error when monitoring hits but the Event Function delivers SCENERY as a hit UNIT --- .../Moose/Functional/ZoneCaptureCoalition.lua | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua b/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua index 07eea7e21..d90dfcca7 100644 --- a/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua +++ b/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua @@ -715,20 +715,21 @@ do -- ZONE_CAPTURE_COALITION local UnitHit = EventData.TgtUnit + if UnitHit.ClassName ~= "SCENERY" then -- Check if unit is inside the capture zone and that it is of the defending coalition. - if UnitHit and UnitHit:IsInZone(self) and UnitHit:GetCoalition()==self.Coalition then - - -- Update last hit time. - self.HitTimeLast=timer.getTime() - - -- Only trigger attacked event if not already in state "Attacked". - if self:GetState()~="Attacked" then - self:F2("Hit ==> Attack") - self:Attack() - end - + if UnitHit and UnitHit:IsInZone(self) and UnitHit:GetCoalition()==self.Coalition then + + -- Update last hit time. + self.HitTimeLast=timer.getTime() + + -- Only trigger attacked event if not already in state "Attacked". + if self:GetState()~="Attacked" then + self:F2("Hit ==> Attack") + self:Attack() + end + + end end - end end From 2cecc526fb66238c3076bbe1e920857e7918bbf2 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 5 Oct 2021 19:10:15 +0200 Subject: [PATCH 22/37] ZONE_CAPTURE_COALITION - fixed an issue when monitoring hits and SCENERY was delivered as hit UNIT --- .../Moose/Functional/ZoneCaptureCoalition.lua | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua b/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua index e6a07e323..d90dfcca7 100644 --- a/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua +++ b/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua @@ -715,20 +715,21 @@ do -- ZONE_CAPTURE_COALITION local UnitHit = EventData.TgtUnit + if UnitHit.ClassName ~= "SCENERY" then -- Check if unit is inside the capture zone and that it is of the defending coalition. - if UnitHit and UnitHit:IsInZone(self) and UnitHit:GetCoalition()==self.Coalition then - - -- Update last hit time. - self.HitTimeLast=timer.getTime() - - -- Only trigger attacked event if not already in state "Attacked". - if self:GetState()~="Attacked" then - self:F2("Hit ==> Attack") - self:Attack() - end - + if UnitHit and UnitHit:IsInZone(self) and UnitHit:GetCoalition()==self.Coalition then + + -- Update last hit time. + self.HitTimeLast=timer.getTime() + + -- Only trigger attacked event if not already in state "Attacked". + if self:GetState()~="Attacked" then + self:F2("Hit ==> Attack") + self:Attack() + end + + end end - end end @@ -890,12 +891,14 @@ do -- ZONE_CAPTURE_COALITION end -- Status text. - local text=string.format("CAPTURE ZONE %s: Owner=%s (Previous=%s): #blue=%d, #red=%d, Status %s", self:GetZoneName(), self:GetCoalitionName(), UTILS.GetCoalitionName(self:GetPreviousCoalition()), nBlue, nRed, State) - local NewState = self:GetState() - if NewState~=State then - text=text..string.format(" --> %s", NewState) + if false then + local text=string.format("CAPTURE ZONE %s: Owner=%s (Previous=%s): #blue=%d, #red=%d, Status %s", self:GetZoneName(), self:GetCoalitionName(), UTILS.GetCoalitionName(self:GetPreviousCoalition()), nBlue, nRed, State) + local NewState = self:GetState() + if NewState~=State then + text=text..string.format(" --> %s", NewState) + end + self:I(text) end - self:I(text) end From 554f063defa34e0b16a8c93b793204003014ed47 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 7 Oct 2021 09:46:06 +0200 Subject: [PATCH 23/37] INTEL - allow variable update times --- Moose Development/Moose/Ops/Intelligence.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Ops/Intelligence.lua b/Moose Development/Moose/Ops/Intelligence.lua index c8eeee3f4..f1d14a23b 100644 --- a/Moose Development/Moose/Ops/Intelligence.lua +++ b/Moose Development/Moose/Ops/Intelligence.lua @@ -135,7 +135,7 @@ INTEL = { --- INTEL class version. -- @field #string version -INTEL.version="0.2.6" +INTEL.version="0.2.7" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list @@ -212,6 +212,8 @@ function INTEL:New(DetectionSet, Coalition, Alias) self.DetectRWR = true self.DetectDLINK = true + self.statusupdate = -60 + -- Set some string id for output to DCS.log file. self.lid=string.format("INTEL %s (%s) | ", self.alias, self.coalition and UTILS.GetCoalitionName(self.coalition) or "unknown") @@ -585,7 +587,7 @@ function INTEL:onafterStatus(From, Event, To) self:I(self.lid..text) end - self:__Status(-60) + self:__Status(self.statusupdate) end From d112ffaf6a6a9bd69286015df26f6bd76303bf0d Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 7 Oct 2021 09:49:06 +0200 Subject: [PATCH 24/37] Autolase - better calculation of LoS for lasing, somewhat faster detection --- .../Moose/Functional/Autolase.lua | 107 +++++++++++++++--- 1 file changed, 91 insertions(+), 16 deletions(-) diff --git a/Moose Development/Moose/Functional/Autolase.lua b/Moose Development/Moose/Functional/Autolase.lua index c3fce644f..4ca4959c3 100644 --- a/Moose Development/Moose/Functional/Autolase.lua +++ b/Moose Development/Moose/Functional/Autolase.lua @@ -53,7 +53,8 @@ -- :InitDelayOff() -- :OnSpawnGroup( -- function (group) --- local name = group:GetName() +-- local unit = group:GetUnit(1) +-- local name = unit:GetName() -- autolaser:SetRecceLaserCode(name,1688) -- end -- ) @@ -107,7 +108,7 @@ AUTOLASE = { --- AUTOLASE class version. -- @field #string version -AUTOLASE.version = "0.0.3" +AUTOLASE.version = "0.0.4" ------------------------------------------------------------------- -- Begin Functional.Autolase.lua @@ -179,6 +180,8 @@ function AUTOLASE:New(RecceSet, Coalition, Alias, PilotSet) self.smoketargets = false self.smokecolor = SMOKECOLOR.Red self.notifypilots = true + --self.statusupdate = -28 -- for #INTEL + self.targetsperrecce = {} -- Set some string id for output to DCS.log file. self.lid=string.format("AUTOLASE %s (%s) | ", self.alias, self.coalition and UTILS.GetCoalitionName(self.coalition) or "unknown") @@ -308,7 +311,7 @@ end --- Function to get a laser code by recce name -- @param #AUTOLASE self --- @param #string RecceName Name of the Recce +-- @param #string RecceName Unit(!) name of the Recce -- @return #AUTOLASE self function AUTOLASE:GetLaserCode(RecceName) local code = 1688 @@ -341,7 +344,7 @@ end --- (User) Function to set a specific code to a Recce. -- @param #AUTOLASE self --- @param #string RecceName Name of the Recce +-- @param #string RecceName (Unit!) Name of the Recce -- @param #number Code The lase code -- @return #AUTOLASE self function AUTOLASE:SetRecceLaserCode(RecceName, Code) @@ -367,8 +370,8 @@ end -- @param #number Duration (Max) duration for lasing in seconds -- @return #AUTOLASE self function AUTOLASE:SetLasingParameters(Distance, Duration) - self.LaseDistance = distance or 4000 - self.LaseDuration = duration or 120 + self.LaseDistance = Distance or 5000 + self.LaseDuration = Duration or 300 return self end @@ -383,14 +386,40 @@ function AUTOLASE:SetSmokeTargets(OnOff,Color) return self end +--- (Internal) Function to calculate line of sight. +-- @param #AUTOLASE self +-- @param Wrapper.Unit#UNIT Unit +-- @return #number LOS Line of sight in meters +function AUTOLASE:GetLosFromUnit(Unit) + local lasedistance = self.LaseDistance + local unitheight = Unit:GetHeight() + local coord = Unit:GetCoordinate() + local landheight = coord:GetLandHeight() + local asl = unitheight - landheight + if asl > 100 then + local absquare = lasedistance^2+asl^2 + lasedistance = math.sqrt(absquare) + end + --self:I({lasedistance=lasedistance}) + return lasedistance +end + --- (Internal) Function to check on lased targets. -- @param #AUTOLASE self -- @return #AUTOLASE self function AUTOLASE:CleanCurrentLasing() local lasingtable = self.CurrentLasing local newtable = {} + local newreccecount = {} local lasing = 0 + for _ind,_entry in pairs(lasingtable) do + local entry = _entry -- #AUTOLASE.LaserSpot + if not newreccecount[entry.reccename] then + newreccecount[entry.reccename] = 0 + end + end + for _ind,_entry in pairs(lasingtable) do local entry = _entry -- #AUTOLASE.LaserSpot local valid = 0 @@ -425,8 +454,9 @@ function AUTOLASE:CleanCurrentLasing() if not reccedead and not unitdead then local coord = unit:GetCoordinate() -- Core.Point#COORDINATE local coord2 = recce:GetCoordinate() -- Core.Point#COORDINATE - local dist = coord2:Get2DDistance(coord) - if dist <= self.LaseDistance then + local dist = coord2:Get3DDistance(coord) + local lasedistance = self:GetLosFromUnit(recce) + if dist <= lasedistance then valid = valid + 1 else lostsight = true @@ -450,10 +480,13 @@ function AUTOLASE:CleanCurrentLasing() if valid == 4 then self.lasingindex = self.lasingindex + 1 newtable[self.lasingindex] = entry + newreccecount[entry.reccename] = newreccecount[entry.reccename] + 1 lasing = lasing + 1 end end self.CurrentLasing = newtable + self.targetsperrecce = newreccecount + --self:I({newreccecount}) return lasing end @@ -507,13 +540,43 @@ function AUTOLASE:NotifyPilots(Message,Duration) else local m = MESSAGE:New(Message,Duration,"Autolase"):ToAll() end + if self.debug then self:I(Message) end return self end +--- (Internal) Function to check if a unit is already lased. +-- @param #AUTOLASE self +-- @param #string unitname Name of the unit to check +-- @return #boolean outcome True or false +function AUTOLASE:CheckIsLased(unitname) + local outcome = false + for _,_laserspot in pairs(self.CurrentLasing) do + local spot = _laserspot -- #AUTOLASE.LaserSpot + if spot.unitname == unitname then + outcome = true + break + end + end + return outcome +end + ------------------------------------------------------------------- -- FSM Functions ------------------------------------------------------------------- +--- (Internal) FSM Function for monitoring +-- @param #AUTOLASE self +-- @param #string From The from state +-- @param #string Event The event +-- @param #string To The to state +-- @return #AUTOLASE self +function AUTOLASE:onbeforeMonitor(From, Event, To) + self:T({From, Event, To}) + -- Check if group has detected any units. + self:UpdateIntel() + return self +end + --- (Internal) FSM Function for monitoring -- @param #AUTOLASE self -- @param #string From The from state @@ -522,7 +585,7 @@ end -- @return #AUTOLASE self function AUTOLASE:onafterMonitor(From, Event, To) self:T({From, Event, To}) - + -- Housekeeping local countlases = self:CleanCurrentLasing() @@ -541,13 +604,15 @@ function AUTOLASE:onafterMonitor(From, Event, To) local reccename = contact.recce local reccegrp = UNIT:FindByName(reccename) local reccecoord = reccegrp:GetCoordinate() - local distance = math.floor(reccecoord:Get2DDistance(coord)) - local text = string.format("%s of %s | Distance %d km | Threatlevel %d",contact.attribute, contact.groupname, distance/ 1000, contact.threatlevel) + local distance = math.floor(reccecoord:Get3DDistance(coord)) + local text = string.format("%s of %s | Distance %d km | Threatlevel %d",contact.attribute, contact.groupname, math.floor(distance/1000), contact.threatlevel) report:Add(text) self:T(text) + if self.debug then self:I(text) end lines = lines + 1 -- sort out groups beyond sight - if distance <= self.LaseDistance then + local lasedistance = self:GetLosFromUnit(reccegrp) + if grp:IsGround() and lasedistance >= distance then table.insert(groupsbythreat,{contact.group,contact.threatlevel}) self.RecceNames[contact.groupname] = contact.recce end @@ -565,20 +630,24 @@ function AUTOLASE:onafterMonitor(From, Event, To) return aNum > bNum -- Return their comparisons, < for ascending, > for descending end) - --self:T("Groups by Threat") + -- self:T("Groups by Threat") --self:T({self.GroupsByThreat}) -- build table of Units local unitsbythreat = {} - for _,_entry in ipairs(self.GroupsByThreat) do + for _,_entry in pairs(self.GroupsByThreat) do local group = _entry[1] -- Wrapper.Group#GROUP if group and group:IsAlive() then local units = group:GetUnits() local reccename = self.RecceNames[group:GetName()] + --local recceunit UNIT:FindByName(reccename) + --local reccecoord = recceunit:GetCoordinate() for _,_unit in pairs(units) do local unit = _unit -- Wrapper.Unit#UNIT if unit and unit:IsAlive() then local threat = unit:GetThreatLevel() + local coord = unit:GetCoordinate() + --local distance = math.floor(reccecoord:Get3DDistance(coord)) if threat > 0 then local unitname = unit:GetName() table.insert(unitsbythreat,{unit,threat}) @@ -597,10 +666,13 @@ function AUTOLASE:onafterMonitor(From, Event, To) return aNum > bNum -- Return their comparisons, < for ascending, > for descending end) + -- self:I("Units by Threat") + -- self:I({self.UnitsByThreat}) + local unitreport = REPORT:New("Detected Units") local lines = 0 - for _,_entry in ipairs(self.UnitsByThreat) do + for _,_entry in pairs(self.UnitsByThreat) do local threat = _entry[2] local unit = _entry[1] local unitname = unit:GetName() @@ -608,6 +680,7 @@ function AUTOLASE:onafterMonitor(From, Event, To) unitreport:Add(text) lines = lines + 1 self:T(text) + if self.debug then self:I(text) end end if self.verbose > 2 and lines > 0 then @@ -621,8 +694,10 @@ function AUTOLASE:onafterMonitor(From, Event, To) local unitname = unit:GetName() local reccename = self.RecceUnitNames[unitname] local recce = UNIT:FindByName(reccename) - if targets < self.maxlasing and unit:IsAlive() == true then + local reccecount = self.targetsperrecce[reccename] or 0 + if (targets < self.maxlasing or reccecount < targets) and not self:CheckIsLased(unitname) and unit:IsAlive() == true then targets = targets + 1 + self.targetsperrecce[reccename] = reccecount + 1 local code = self:GetLaserCode(reccename) local spot = SPOT:New(recce) spot:LaseOn(unit,code,self.LaseDuration) From de9b173d9bcef905426b8539276662cb295e05a0 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 7 Oct 2021 18:14:29 +0200 Subject: [PATCH 25/37] UTILS - added door check for Hercules --- Moose Development/Moose/Utilities/Utils.lua | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index bdb6a43ff..9a8e7e62e 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -1598,6 +1598,21 @@ function UTILS.IsLoadingDoorOpen( unit_name ) ret_val = true end + if string.find(type_name, "Hercules") and unit:getDrawArgumentValue(1215) == 1 and unit:getDrawArgumentValue(1216) == 1 then + BASE:T(unit_name .. " rear doors are open") + ret_val = true + end + + if string.find(type_name, "Hercules") and (unit:getDrawArgumentValue(1220) == 1 or unit:getDrawArgumentValue(1221) == 1) then + BASE:T(unit_name .. " para doors are open") + ret_val = true + end + + if string.find(type_name, "Hercules") and unit:getDrawArgumentValue(1217) == 1 then + BASE:T(unit_name .. " side door is open") + ret_val = true + end + if ret_val == false then BASE:T(unit_name .. " all doors are closed") end From 9b74a58dde336e671c4da85fd81c1538055ec095 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 7 Oct 2021 18:14:50 +0200 Subject: [PATCH 26/37] UTILS - added door check for Hercules --- Moose Development/Moose/Utilities/Utils.lua | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 85832d732..4c02c09f3 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -1598,6 +1598,21 @@ function UTILS.IsLoadingDoorOpen( unit_name ) ret_val = true end + if string.find(type_name, "Hercules") and unit:getDrawArgumentValue(1215) == 1 and unit:getDrawArgumentValue(1216) == 1 then + BASE:T(unit_name .. " rear doors are open") + ret_val = true + end + + if string.find(type_name, "Hercules") and (unit:getDrawArgumentValue(1220) == 1 or unit:getDrawArgumentValue(1221) == 1) then + BASE:T(unit_name .. " para door(s) are open") + ret_val = true + end + + if string.find(type_name, "Hercules") and unit:getDrawArgumentValue(1217) == 1 then + BASE:T(unit_name .. " side door is open") + ret_val = true + end + if ret_val == false then BASE:T(unit_name .. " all doors are closed") end From b05c321306bdfa5a20054724f9730b70edadadff Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 7 Oct 2021 18:15:13 +0200 Subject: [PATCH 27/37] CTLD - added door check for troops --- Moose Development/Moose/Ops/CTLD.lua | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index ebc2c6c6d..447fb42c5 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -22,7 +22,7 @@ -- @module Ops.CTLD -- @image OPS_CTLD.jpg --- Date: Sep 2021 +-- Date: Oct 2021 do ------------------------------------------------------ @@ -669,6 +669,7 @@ do -- my_ctld.cratecountry = country.id.GERMANY -- ID of crates. Will default to country.id.RUSSIA for RED coalition setups. -- my_ctld.allowcratepickupagain = true -- allow re-pickup crates that were dropped. -- my_ctld.enableslingload = false -- allow cargos to be slingloaded - might not work for all cargo types +-- my_ctld.pilotmustopendoors = false -- -- force opening of doors -- -- ## 2.1 User functions -- @@ -987,7 +988,7 @@ CTLD.UnitTypes = { --- CTLD class version. -- @field #string version -CTLD.version="0.2.3" +CTLD.version="0.2.4" --- Instantiate a new CTLD. -- @param #CTLD self @@ -1131,6 +1132,9 @@ function CTLD:New(Coalition, Prefixes, Alias) -- country of crates spawned self.cratecountry = country.id.GERMANY + -- for opening doors + self.pilotmustopendoors = false + if self.coalition == coalition.side.RED then self.cratecountry = country.id.RUSSIA end @@ -1436,6 +1440,7 @@ function CTLD:_LoadTroops(Group, Unit, Cargotype) -- landed or hovering over load zone? local grounded = not self:IsUnitInAir(Unit) local hoverload = self:CanHoverLoad(Unit) + local dooropen = UTILS.IsLoadingDoorOpen(Unit:GetName()) and self.pilotmustopendoors -- check if we are in LOAD zone local inzone, zonename, zone, distance = self:IsUnitInZone(Unit,CTLD.CargoZoneType.LOAD) if not inzone then @@ -1447,6 +1452,9 @@ function CTLD:_LoadTroops(Group, Unit, Cargotype) elseif not grounded and not hoverload then self:_SendMessage("You need to land or hover in position to load!", 10, false, Group) if not self.debug then return self end + elseif not dooropen then + self:_SendMessage("You need to open the door(s) to load troops!", 10, false, Group) + if not self.debug then return self end end -- load troops into heli local group = Group -- Wrapper.Group#GROUP @@ -1618,6 +1626,10 @@ end self:_SendMessage("You need to land or hover in position to load!", 10, false, Group) if not self.debug then return self end end + if self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen(Unit:GetName()) then + self:_SendMessage("You need to open the door(s) to extract troops!", 10, false, Group) + if not self.debug then return self end + end -- load troops into heli local unit = Unit -- Wrapper.Unit#UNIT local unitname = unit:GetName() @@ -2360,6 +2372,11 @@ function CTLD:_UnloadTroops(Group, Unit) self:T(self.lid .. " _UnloadTroops") -- check if we are in LOAD zone local droppingatbase = false + local canunload = true + if self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen(Unit:GetName()) then + self:_SendMessage("You need to open the door(s) to unload troops!", 10, false, Group) + if not self.debug then return self end + end local inzone, zonename, zone, distance = self:IsUnitInZone(Unit,CTLD.CargoZoneType.LOAD) if not inzone then inzone, zonename, zone, distance = self:IsUnitInZone(Unit,CTLD.CargoZoneType.SHIP) From 69862ab67d3ef7f9e59b2dc895872ee369d83e45 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 7 Oct 2021 18:15:50 +0200 Subject: [PATCH 28/37] CSAR - try to avoid spawning under water --- Moose Development/Moose/Ops/CSAR.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/CSAR.lua b/Moose Development/Moose/Ops/CSAR.lua index 014efe0a0..60ed053a7 100644 --- a/Moose Development/Moose/Ops/CSAR.lua +++ b/Moose Development/Moose/Ops/CSAR.lua @@ -556,6 +556,7 @@ function CSAR:_SpawnPilotInField(country,point,frequency) for i=1,10 do math.random(i,10000) end + if point:IsSurfaceTypeWater() then point.y = 0 end local template = self.template local alias = string.format("Pilot %.2fkHz-%d", freq, math.random(1,99)) local coalition = self.coalition @@ -1127,7 +1128,6 @@ end function CSAR:_IsLoadingDoorOpen( unit_name ) self:T(self.lid .. " _IsLoadingDoorOpen") return UTILS.IsLoadingDoorOpen(unit_name) - end --- (Internal) Function to check if heli is close to group. From 3b1c8c3deb0f098756932d458c204364f27b7ec7 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 7 Oct 2021 18:16:40 +0200 Subject: [PATCH 29/37] AUTOLASE - added LOS check, maxlasingunits valid per lasing Recce --- .../Moose/Functional/Autolase.lua | 107 +++++++++++++++--- 1 file changed, 89 insertions(+), 18 deletions(-) diff --git a/Moose Development/Moose/Functional/Autolase.lua b/Moose Development/Moose/Functional/Autolase.lua index 4ca4959c3..dc9185f27 100644 --- a/Moose Development/Moose/Functional/Autolase.lua +++ b/Moose Development/Moose/Functional/Autolase.lua @@ -108,7 +108,7 @@ AUTOLASE = { --- AUTOLASE class version. -- @field #string version -AUTOLASE.version = "0.0.4" +AUTOLASE.version = "0.0.6" ------------------------------------------------------------------- -- Begin Functional.Autolase.lua @@ -156,6 +156,7 @@ function AUTOLASE:New(RecceSet, Coalition, Alias, PilotSet) -- inherit from INTEL local self=BASE:Inherit(self, INTEL:New(RecceSet, Coalition, Alias)) -- #AUTOLASE + self.RecceSet = RecceSet self.DetectVisual = true self.DetectOptical = true self.DetectRadar = true @@ -182,6 +183,9 @@ function AUTOLASE:New(RecceSet, Coalition, Alias, PilotSet) self.notifypilots = true --self.statusupdate = -28 -- for #INTEL self.targetsperrecce = {} + self.RecceUnits = {} + self.forcecooldown = true + self.cooldowntime = 60 -- Set some string id for output to DCS.log file. self.lid=string.format("AUTOLASE %s (%s) | ", self.alias, self.coalition and UTILS.GetCoalitionName(self.coalition) or "unknown") @@ -353,6 +357,17 @@ function AUTOLASE:SetRecceLaserCode(RecceName, Code) return self end +--- (User) Function to force laser cooldown and cool down time +-- @param #AUTOLASE self +-- @param #boolean OnOff Switch cool down on (true) or off (false) - defaults to true +-- @param #number Seconds Number of seconds for cooldown - dafaults to 60 seconds +-- @return #AUTOLASE self +function AUTOLASE:SetLaserCoolDown(OnOff, Seconds) + self.forcecooldown = OnOff and true + self.cooldowntime = Seconds or 60 + return self +end + --- (User) Function to set message show times. -- @param #AUTOLASE self -- @param #number long Longer show time @@ -366,8 +381,8 @@ end --- (User) Function to set lasing distance in meters and duration in seconds -- @param #AUTOLASE self --- @param #number Distance (Max) distance for lasing in meters --- @param #number Duration (Max) duration for lasing in seconds +-- @param #number Distance (Max) distance for lasing in meters - default 5000 meters +-- @param #number Duration (Max) duration for lasing in seconds - default 300 secs -- @return #AUTOLASE self function AUTOLASE:SetLasingParameters(Distance, Duration) self.LaseDistance = Distance or 5000 @@ -420,12 +435,24 @@ function AUTOLASE:CleanCurrentLasing() end end + for _,_recce in pairs (self.RecceSet:GetSetObjects()) do + local recce = _recce --Wrapper.Group#GROUP + if recce and recce:IsAlive() then + local unit = recce:GetUnit(1) + local name = unit:GetName() + if not self.RecceUnits[name] then + self.RecceUnits[name] = { name=name, unit=unit, cooldown = false, timestamp = timer.getAbsTime() } + end + end + end + for _ind,_entry in pairs(lasingtable) do local entry = _entry -- #AUTOLASE.LaserSpot local valid = 0 local reccedead = false local unitdead = false local lostsight = false + local timeout = false local Tnow = timer.getAbsTime() -- check recce dead local recce = entry.lasingunit @@ -452,11 +479,7 @@ function AUTOLASE:CleanCurrentLasing() end -- check entry out of sight if not reccedead and not unitdead then - local coord = unit:GetCoordinate() -- Core.Point#COORDINATE - local coord2 = recce:GetCoordinate() -- Core.Point#COORDINATE - local dist = coord2:Get3DDistance(coord) - local lasedistance = self:GetLosFromUnit(recce) - if dist <= lasedistance then + if self:CanLase(recce,unit) then valid = valid + 1 else lostsight = true @@ -468,14 +491,20 @@ function AUTOLASE:CleanCurrentLasing() end -- check timed out local timestamp = entry.timestamp - if Tnow - timestamp < self.LaseDuration then + if Tnow - timestamp < self.LaseDuration and not lostsight then valid = valid + 1 else - lostsight = true + timeout = true entry.laserspot:LaseOff() --local text = string.format("Lost sight of unit %s.",entry.unitname) --local m = MESSAGE:New(text,15,"Autolase"):ToAll() - self:__LaserTimeout(2,entry.unitname,entry.reccename) + + self.RecceUnits[entry.reccename].cooldown = true + self.RecceUnits[entry.reccename].timestamp = timer.getAbsTime() + + if not lostsight then + self:__LaserTimeout(2,entry.unitname,entry.reccename) + end end if valid == 4 then self.lasingindex = self.lasingindex + 1 @@ -560,6 +589,37 @@ function AUTOLASE:CheckIsLased(unitname) return outcome end +--- (Internal) Function to check if a unit can be lased. +-- @param #AUTOLASE self +-- @param Wrapper.Unit#UNIT Recce The Recce #UNIT +-- @param Wrapper.Unit#UNIT Unit The lased #UNIT +-- @return #boolean outcome True or false +function AUTOLASE:CanLase(Recce,Unit) + local canlase = false + -- cooldown? + local name = Recce:GetName() + local cooldown = self.RecceUnits[name].cooldown and self.forcecooldown + if cooldown then + local Tdiff = timer.getAbsTime() - self.RecceUnits[name].timestamp + if Tdiff < self.cooldowntime then + return false + else + self.RecceUnits[name].cooldown = false + end + end + -- calculate LOS + local reccecoord = Recce:GetCoordinate() + local unitcoord = Unit:GetCoordinate() + local islos = reccecoord:IsLOS(unitcoord,2.5) + -- calculate distance + local distance = math.floor(reccecoord:Get3DDistance(unitcoord)) + local lasedistance = self:GetLosFromUnit(Recce) + if distance <= lasedistance and islos then + canlase = true + end + return canlase +end + ------------------------------------------------------------------- -- FSM Functions ------------------------------------------------------------------- @@ -688,16 +748,26 @@ function AUTOLASE:onafterMonitor(From, Event, To) end -- lase targets - local targets = countlases or 0 + --local maxtargets = targets * self.RecceSet:CountAlive() + + for _,_detectingunit in pairs(self.RecceUnits) do + + local reccename = _detectingunit.name + local recce = _detectingunit.unit + local reccecount = self.targetsperrecce[reccename] or 0 + --self:I("*****Run for: "..reccename) + local targets = 0 for _,_entry in pairs(self.UnitsByThreat) do local unit = _entry[1] -- Wrapper.Unit#UNIT local unitname = unit:GetName() - local reccename = self.RecceUnitNames[unitname] - local recce = UNIT:FindByName(reccename) - local reccecount = self.targetsperrecce[reccename] or 0 - if (targets < self.maxlasing or reccecount < targets) and not self:CheckIsLased(unitname) and unit:IsAlive() == true then + --local unithreat = _entry[2] + --local text = string.format("Recce %s Checking %s Threat %d",reccename, unitname,unithreat) + --local m=MESSAGE:New(text,10,"Autolase"):ToAll() + --self:I(text) + local canlase = self:CanLase(recce,unit) + if targets+reccecount < self.maxlasing and not self:CheckIsLased(unitname) and unit:IsAlive() and canlase then targets = targets + 1 - self.targetsperrecce[reccename] = reccecount + 1 + --self.targetsperrecce[reccename] = reccecount + 1 local code = self:GetLaserCode(reccename) local spot = SPOT:New(recce) spot:LaseOn(unit,code,self.LaseDuration) @@ -724,7 +794,8 @@ function AUTOLASE:onafterMonitor(From, Event, To) self:__Lasing(2,laserspot) end end - + end + self:__Monitor(-30) return self end From 921024035cf4f5bfdca3319833ab8b7c05878b0f Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 8 Oct 2021 12:33:33 +0200 Subject: [PATCH 30/37] AUTOLASE - cleanup, added SRS option --- .../Moose/Functional/Autolase.lua | 100 ++++++++++-------- 1 file changed, 58 insertions(+), 42 deletions(-) diff --git a/Moose Development/Moose/Functional/Autolase.lua b/Moose Development/Moose/Functional/Autolase.lua index dc9185f27..14a725fa2 100644 --- a/Moose Development/Moose/Functional/Autolase.lua +++ b/Moose Development/Moose/Functional/Autolase.lua @@ -31,7 +31,6 @@ -- * Targets are lased by threat priority order -- * Use FSM events to link functionality into your scripts -- * Easy set-up --- * Targets are lased by threat priority order -- -- # 2 Basic usage -- @@ -71,9 +70,9 @@ -- ### Author: **applevangelist** -- @module Functional.Autolase -- @image Designation.JPG - +-- -- Date: Oct 2021 - +-- --- Class AUTOLASE -- @type AUTOLASE -- @field #string ClassName @@ -108,7 +107,7 @@ AUTOLASE = { --- AUTOLASE class version. -- @field #string version -AUTOLASE.version = "0.0.6" +AUTOLASE.version = "0.0.7" ------------------------------------------------------------------- -- Begin Functional.Autolase.lua @@ -181,11 +180,14 @@ function AUTOLASE:New(RecceSet, Coalition, Alias, PilotSet) self.smoketargets = false self.smokecolor = SMOKECOLOR.Red self.notifypilots = true - --self.statusupdate = -28 -- for #INTEL self.targetsperrecce = {} self.RecceUnits = {} self.forcecooldown = true self.cooldowntime = 60 + self.useSRS = false + self.SRSPath = "" + self.SRSFreq = 251 + self.SRSMod = radio.modulation.AM -- Set some string id for output to DCS.log file. self.lid=string.format("AUTOLASE %s (%s) | ", self.alias, self.coalition and UTILS.GetCoalitionName(self.coalition) or "unknown") @@ -313,7 +315,7 @@ function AUTOLASE:OnEventPlayerEnterAircraft(EventData) return self end ---- Function to get a laser code by recce name +--- (Internal) Function to get a laser code by recce name -- @param #AUTOLASE self -- @param #string RecceName Unit(!) name of the Recce -- @return #AUTOLASE self @@ -328,7 +330,22 @@ function AUTOLASE:GetLaserCode(RecceName) return code end ---- Function set max lasing targets +--- (User) Function enable sending messages via SRS. +-- @param #AUTOLASE self +-- @param #boolean OnOff Switch usage on and off +-- @param #string Path Path to SRS directory, e.g. C:\\Program Files\\DCS-SimpleRadio-Standalon +-- @param #number Frequency Frequency to send, e.g. 243 +-- @param #number Modulation Modulation i.e. radio.modulation.AM or radio.modulation.FM +-- @return #AUTOLASE self +function AUTOLASE:SetUsingSRS(OnOff,Path,Frequency,Modulation) + self.useSRS = OnOff or true + self.SRSPath = Path or "E:\\Program Files\\DCS-SimpleRadio-Standalone" + self.SRSFreq = Frequency or 271 + self.SRSMod = Modulation or radio.modulation.AM + return self +end + +--- (User) Function set max lasing targets -- @param #AUTOLASE self -- @param #number Number Max number of targets to lase at once -- @return #AUTOLASE self @@ -337,7 +354,7 @@ function AUTOLASE:SetMaxLasingTargets(Number) return self end ---- Function set notify pilots on events +--- (Internal) Function set notify pilots on events -- @param #AUTOLASE self -- @param #boolean OnOff Switch messaging on (true) or off (false) -- @return #AUTOLASE self @@ -415,7 +432,6 @@ function AUTOLASE:GetLosFromUnit(Unit) local absquare = lasedistance^2+asl^2 lasedistance = math.sqrt(absquare) end - --self:I({lasedistance=lasedistance}) return lasedistance end @@ -460,8 +476,6 @@ function AUTOLASE:CleanCurrentLasing() valid = valid + 1 else reccedead = true - --local text = string.format("Recce %s KIA!",entry.reccename) - --local m = MESSAGE:New(text,15,"Autolase"):ToAll() self:__RecceKIA(2,entry.reccename) end -- check entry dead @@ -471,8 +485,6 @@ function AUTOLASE:CleanCurrentLasing() else unitdead = true if not self.deadunitnotes[entry.unitname] then - --local text = string.format("Unit %s destroyed! Good job!",entry.unitname) - --local m = MESSAGE:New(text,15,"Autolase"):ToAll() self.deadunitnotes[entry.unitname] = true self:__TargetDestroyed(2,entry.unitname,entry.reccename) end @@ -484,8 +496,6 @@ function AUTOLASE:CleanCurrentLasing() else lostsight = true entry.laserspot:LaseOff() - --local text = string.format("Lost sight of unit %s.",entry.unitname) - --local m = MESSAGE:New(text,15,"Autolase"):ToAll() self:__TargetLost(2,entry.unitname,entry.reccename) end end @@ -495,9 +505,7 @@ function AUTOLASE:CleanCurrentLasing() valid = valid + 1 else timeout = true - entry.laserspot:LaseOff() - --local text = string.format("Lost sight of unit %s.",entry.unitname) - --local m = MESSAGE:New(text,15,"Autolase"):ToAll() + entry.laserspot:LaseOff()() self.RecceUnits[entry.reccename].cooldown = true self.RecceUnits[entry.reccename].timestamp = timer.getAbsTime() @@ -515,7 +523,6 @@ function AUTOLASE:CleanCurrentLasing() end self.CurrentLasing = newtable self.targetsperrecce = newreccecount - --self:I({newreccecount}) return lasing end @@ -573,6 +580,37 @@ function AUTOLASE:NotifyPilots(Message,Duration) return self end +--- (User) Send messages via SRS. +-- @param #AUTOLASE self +-- @param #string Message The (short!) message to be sent, e.g. "Lasing target!" +-- @return #AUTOLASE self +-- @usage Step 1 - set up the radio basics **once** with +-- my_autolase:SetUsingSRS(true,"C:\\path\\SRS-Folder",251,radio.modulation.AM) +-- Step 2 - send a message, e.g. +-- function my_autolase:OnAfterLasing(From, Event, To, LaserSpot) +-- my_autolase:NotifyPilotsWithSRS("Reaper lasing new target!") +-- end +function AUTOLASE:NotifyPilotsWithSRS(Message) + if self.useSRS then + -- Create a SOUNDTEXT object. + if self.debug then + BASE:TraceOn() + BASE:TraceClass("SOUNDTEXT") + BASE:TraceClass("MSRS") + end + local path = self.SRSPath or "C:\\Program Files\\DCS-SimpleRadio-Standalone" + local freq = self.SRSFreq or 271 + local mod = self.SRSMod or radio.modulation.AM + local text=SOUNDTEXT:New(Message) + -- MOOSE SRS + local msrs=MSRS:New(path, freq, mod) + -- Text-to speech with default voice after 2 seconds. + msrs:PlaySoundText(text, 2) + end + if self.debug then self:I(Message) end + return self +end + --- (Internal) Function to check if a unit is already lased. -- @param #AUTOLASE self -- @param #string unitname Name of the unit to check @@ -653,8 +691,6 @@ function AUTOLASE:onafterMonitor(From, Event, To) local detecteditems = self.Contacts or {} -- #table of Ops.Intelligence#INTEL.Contact local groupsbythreat = {} - --self:T("Detected Items:") - --self:T({detecteditems}) local report = REPORT:New("Detections") local lines = 0 for _,_contact in pairs(detecteditems) do @@ -690,9 +726,6 @@ function AUTOLASE:onafterMonitor(From, Event, To) return aNum > bNum -- Return their comparisons, < for ascending, > for descending end) - -- self:T("Groups by Threat") - --self:T({self.GroupsByThreat}) - -- build table of Units local unitsbythreat = {} for _,_entry in pairs(self.GroupsByThreat) do @@ -700,14 +733,11 @@ function AUTOLASE:onafterMonitor(From, Event, To) if group and group:IsAlive() then local units = group:GetUnits() local reccename = self.RecceNames[group:GetName()] - --local recceunit UNIT:FindByName(reccename) - --local reccecoord = recceunit:GetCoordinate() for _,_unit in pairs(units) do local unit = _unit -- Wrapper.Unit#UNIT if unit and unit:IsAlive() then local threat = unit:GetThreatLevel() local coord = unit:GetCoordinate() - --local distance = math.floor(reccecoord:Get3DDistance(coord)) if threat > 0 then local unitname = unit:GetName() table.insert(unitsbythreat,{unit,threat}) @@ -726,9 +756,6 @@ function AUTOLASE:onafterMonitor(From, Event, To) return aNum > bNum -- Return their comparisons, < for ascending, > for descending end) - -- self:I("Units by Threat") - -- self:I({self.UnitsByThreat}) - local unitreport = REPORT:New("Detected Units") local lines = 0 @@ -747,33 +774,22 @@ function AUTOLASE:onafterMonitor(From, Event, To) local m=MESSAGE:New(unitreport:Text(),self.reporttimeshort,"Autolase"):ToAll() end - -- lase targets - --local maxtargets = targets * self.RecceSet:CountAlive() - for _,_detectingunit in pairs(self.RecceUnits) do local reccename = _detectingunit.name local recce = _detectingunit.unit local reccecount = self.targetsperrecce[reccename] or 0 - --self:I("*****Run for: "..reccename) local targets = 0 for _,_entry in pairs(self.UnitsByThreat) do local unit = _entry[1] -- Wrapper.Unit#UNIT local unitname = unit:GetName() - --local unithreat = _entry[2] - --local text = string.format("Recce %s Checking %s Threat %d",reccename, unitname,unithreat) - --local m=MESSAGE:New(text,10,"Autolase"):ToAll() - --self:I(text) local canlase = self:CanLase(recce,unit) if targets+reccecount < self.maxlasing and not self:CheckIsLased(unitname) and unit:IsAlive() and canlase then targets = targets + 1 - --self.targetsperrecce[reccename] = reccecount + 1 local code = self:GetLaserCode(reccename) local spot = SPOT:New(recce) spot:LaseOn(unit,code,self.LaseDuration) local locationstring = unit:GetCoordinate():ToStringLLDDM() - --local text = string.format("%s is lasing %s code %d\nat %s",reccename,unit:GetTypeName(),code,locationstring) - --local m = MESSAGE:New(text,15,"Autolase"):ToAllIf(self.debug) local laserspot = { -- #AUTOLASE.LaserSpot laserspot = spot, lasedunit = unit, @@ -879,7 +895,7 @@ function AUTOLASE:onbeforeLasing(From,Event,To,LaserSpot) if self.notifypilots or self.debug then local laserspot = LaserSpot -- #AUTOLASE.LaserSpot local text = string.format("%s is lasing %s code %d\nat %s",laserspot.reccename,laserspot.unittype,laserspot.lasercode,laserspot.location) - self:NotifyPilots(text,self.reporttimeshort) + self:NotifyPilots(text,self.reporttimeshort+5) end return self end From d06db9909cc9d0270c8d5b98503eb912579c3e7f Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 11 Oct 2021 14:11:58 +0200 Subject: [PATCH 31/37] AUTOLASE Small update --- Moose Development/Moose/Functional/Autolase.lua | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Functional/Autolase.lua b/Moose Development/Moose/Functional/Autolase.lua index 14a725fa2..a1085f1b6 100644 --- a/Moose Development/Moose/Functional/Autolase.lua +++ b/Moose Development/Moose/Functional/Autolase.lua @@ -107,7 +107,7 @@ AUTOLASE = { --- AUTOLASE class version. -- @field #string version -AUTOLASE.version = "0.0.7" +AUTOLASE.version = "0.0.8" ------------------------------------------------------------------- -- Begin Functional.Autolase.lua @@ -505,7 +505,7 @@ function AUTOLASE:CleanCurrentLasing() valid = valid + 1 else timeout = true - entry.laserspot:LaseOff()() + entry.laserspot:LaseOff() self.RecceUnits[entry.reccename].cooldown = true self.RecceUnits[entry.reccename].timestamp = timer.getAbsTime() @@ -532,6 +532,15 @@ end -- @return #AUTOLASE self function AUTOLASE:ShowStatus(Group) local report = REPORT:New("Autolase") + local reccetable = self.RecceSet:GetSetObjects() + for _,_recce in pairs(reccetable) do + if _recce and _recce:IsAlive() then + local unit = _recce:GetUnit(1) + local name = unit:GetName() + local code = self:GetLaserCode(name) + report:Add(string.format("Recce %s has code %d",name,code)) + end + end local lines = 0 for _ind,_entry in pairs(self.CurrentLasing) do local entry = _entry -- #AUTOLASE.LaserSpot @@ -540,11 +549,11 @@ function AUTOLASE:ShowStatus(Group) local code = entry.lasercode local locationstring = entry.location local text = string.format("%s lasing %s code %d\nat %s",reccename,typename,code,locationstring) - report:AddIndent(text,"|") + report:Add(text) lines = lines + 1 end if lines == 0 then - report:AddIndent("No targets!","|") + report:Add("No targets!") end local reporttime = self.reporttimelong if lines == 0 then reporttime = self.reporttimeshort end From 1b752fcaf6fe1c0d6e9b2b3fc3e6ef5d2937e5cc Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 11 Oct 2021 18:47:39 +0200 Subject: [PATCH 32/37] CTLD - bug fix for open doors --- Moose Development/Moose/Ops/CTLD.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 447fb42c5..8cdfbcdd6 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -1440,7 +1440,7 @@ function CTLD:_LoadTroops(Group, Unit, Cargotype) -- landed or hovering over load zone? local grounded = not self:IsUnitInAir(Unit) local hoverload = self:CanHoverLoad(Unit) - local dooropen = UTILS.IsLoadingDoorOpen(Unit:GetName()) and self.pilotmustopendoors + --local dooropen = UTILS.IsLoadingDoorOpen(Unit:GetName()) and self.pilotmustopendoors -- check if we are in LOAD zone local inzone, zonename, zone, distance = self:IsUnitInZone(Unit,CTLD.CargoZoneType.LOAD) if not inzone then @@ -1452,7 +1452,7 @@ function CTLD:_LoadTroops(Group, Unit, Cargotype) elseif not grounded and not hoverload then self:_SendMessage("You need to land or hover in position to load!", 10, false, Group) if not self.debug then return self end - elseif not dooropen then + elseif self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen(Unit:GetName()) then self:_SendMessage("You need to open the door(s) to load troops!", 10, false, Group) if not self.debug then return self end end From c48128d92e5c4f2aa139babee7e15efac49865e4 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 12 Oct 2021 08:30:44 +0200 Subject: [PATCH 33/37] CSAR - Added country options for spawned pilots --- Moose Development/Moose/Ops/CSAR.lua | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Ops/CSAR.lua b/Moose Development/Moose/Ops/CSAR.lua index 60ed053a7..49a5ebb90 100644 --- a/Moose Development/Moose/Ops/CSAR.lua +++ b/Moose Development/Moose/Ops/CSAR.lua @@ -22,7 +22,7 @@ -- @module Ops.CSAR -- @image OPS_CSAR.jpg --- Date: Sep 2021 +-- Date: Oct 2021 ------------------------------------------------------------------------- --- **CSAR** class, extends Core.Base#BASE, Core.Fsm#FSM @@ -100,6 +100,11 @@ -- -- (added 0.1.11) -- self.rescuehoverheight = 20 -- max height for a hovering rescue in meters -- self.rescuehoverdistance = 10 -- max distance for a hovering rescue in meters +-- -- (added 0.1.12) +-- -- Country codes for spawned pilots +-- self.countryblue= country.id.USA +-- self.countryred = country.id.RUSSIA +-- self.countryneutral = country.id.UN_PEACEKEEPERS -- -- ## 2.1 Experimental Features -- @@ -369,6 +374,11 @@ function CSAR:New(Coalition, Template, Alias) -- added 0.1.11r1 self.rescuehoverheight = 20 self.rescuehoverdistance = 10 + + -- added 0.1.12 + self.countryblue= country.id.USA + self.countryred = country.id.RUSSIA + self.countryneutral = country.id.UN_PEACEKEEPERS -- WARNING - here\'ll be dragons -- for this to work you need to de-sanitize your mission environment in \Scripts\MissionScripting.lua @@ -695,11 +705,11 @@ function CSAR:_SpawnCsarAtZone( _zone, _coalition, _description, _randomPoint, _ local _country = 0 if _coalition == coalition.side.BLUE then - _country = country.id.USA + _country = self.countryblue elseif _coalition == coalition.side.RED then - _country = country.id.RUSSIA + _country = self.countryred else - _country = country.id.UN_PEACEKEEPERS + _country = self.countryneutral end self:_AddCsar(_coalition, _country, pos, typename, unitname, _description, freq, _nomessage, _description, forcedesc) From 0388d47f235cd8f86a8d5fd26e8bde41b087b559 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 12 Oct 2021 08:31:55 +0200 Subject: [PATCH 34/37] CSAR - Added country options for spawned pilots --- Moose Development/Moose/Ops/CSAR.lua | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Ops/CSAR.lua b/Moose Development/Moose/Ops/CSAR.lua index 014efe0a0..49a5ebb90 100644 --- a/Moose Development/Moose/Ops/CSAR.lua +++ b/Moose Development/Moose/Ops/CSAR.lua @@ -22,7 +22,7 @@ -- @module Ops.CSAR -- @image OPS_CSAR.jpg --- Date: Sep 2021 +-- Date: Oct 2021 ------------------------------------------------------------------------- --- **CSAR** class, extends Core.Base#BASE, Core.Fsm#FSM @@ -100,6 +100,11 @@ -- -- (added 0.1.11) -- self.rescuehoverheight = 20 -- max height for a hovering rescue in meters -- self.rescuehoverdistance = 10 -- max distance for a hovering rescue in meters +-- -- (added 0.1.12) +-- -- Country codes for spawned pilots +-- self.countryblue= country.id.USA +-- self.countryred = country.id.RUSSIA +-- self.countryneutral = country.id.UN_PEACEKEEPERS -- -- ## 2.1 Experimental Features -- @@ -369,6 +374,11 @@ function CSAR:New(Coalition, Template, Alias) -- added 0.1.11r1 self.rescuehoverheight = 20 self.rescuehoverdistance = 10 + + -- added 0.1.12 + self.countryblue= country.id.USA + self.countryred = country.id.RUSSIA + self.countryneutral = country.id.UN_PEACEKEEPERS -- WARNING - here\'ll be dragons -- for this to work you need to de-sanitize your mission environment in \Scripts\MissionScripting.lua @@ -556,6 +566,7 @@ function CSAR:_SpawnPilotInField(country,point,frequency) for i=1,10 do math.random(i,10000) end + if point:IsSurfaceTypeWater() then point.y = 0 end local template = self.template local alias = string.format("Pilot %.2fkHz-%d", freq, math.random(1,99)) local coalition = self.coalition @@ -694,11 +705,11 @@ function CSAR:_SpawnCsarAtZone( _zone, _coalition, _description, _randomPoint, _ local _country = 0 if _coalition == coalition.side.BLUE then - _country = country.id.USA + _country = self.countryblue elseif _coalition == coalition.side.RED then - _country = country.id.RUSSIA + _country = self.countryred else - _country = country.id.UN_PEACEKEEPERS + _country = self.countryneutral end self:_AddCsar(_coalition, _country, pos, typename, unitname, _description, freq, _nomessage, _description, forcedesc) @@ -1127,7 +1138,6 @@ end function CSAR:_IsLoadingDoorOpen( unit_name ) self:T(self.lid .. " _IsLoadingDoorOpen") return UTILS.IsLoadingDoorOpen(unit_name) - end --- (Internal) Function to check if heli is close to group. From 8b9143d3f180099fe5c772c4c2d8af6732d304d0 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 12 Oct 2021 08:32:34 +0200 Subject: [PATCH 35/37] CTLD - added option to force opening of doors --- Moose Development/Moose/Ops/CTLD.lua | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 090eb8073..8cdfbcdd6 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -22,7 +22,7 @@ -- @module Ops.CTLD -- @image OPS_CTLD.jpg --- Date: Sep 2021 +-- Date: Oct 2021 do ------------------------------------------------------ @@ -669,6 +669,7 @@ do -- my_ctld.cratecountry = country.id.GERMANY -- ID of crates. Will default to country.id.RUSSIA for RED coalition setups. -- my_ctld.allowcratepickupagain = true -- allow re-pickup crates that were dropped. -- my_ctld.enableslingload = false -- allow cargos to be slingloaded - might not work for all cargo types +-- my_ctld.pilotmustopendoors = false -- -- force opening of doors -- -- ## 2.1 User functions -- @@ -987,7 +988,7 @@ CTLD.UnitTypes = { --- CTLD class version. -- @field #string version -CTLD.version="0.2.3" +CTLD.version="0.2.4" --- Instantiate a new CTLD. -- @param #CTLD self @@ -1131,6 +1132,9 @@ function CTLD:New(Coalition, Prefixes, Alias) -- country of crates spawned self.cratecountry = country.id.GERMANY + -- for opening doors + self.pilotmustopendoors = false + if self.coalition == coalition.side.RED then self.cratecountry = country.id.RUSSIA end @@ -1436,6 +1440,7 @@ function CTLD:_LoadTroops(Group, Unit, Cargotype) -- landed or hovering over load zone? local grounded = not self:IsUnitInAir(Unit) local hoverload = self:CanHoverLoad(Unit) + --local dooropen = UTILS.IsLoadingDoorOpen(Unit:GetName()) and self.pilotmustopendoors -- check if we are in LOAD zone local inzone, zonename, zone, distance = self:IsUnitInZone(Unit,CTLD.CargoZoneType.LOAD) if not inzone then @@ -1447,6 +1452,9 @@ function CTLD:_LoadTroops(Group, Unit, Cargotype) elseif not grounded and not hoverload then self:_SendMessage("You need to land or hover in position to load!", 10, false, Group) if not self.debug then return self end + elseif self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen(Unit:GetName()) then + self:_SendMessage("You need to open the door(s) to load troops!", 10, false, Group) + if not self.debug then return self end end -- load troops into heli local group = Group -- Wrapper.Group#GROUP @@ -1618,6 +1626,10 @@ end self:_SendMessage("You need to land or hover in position to load!", 10, false, Group) if not self.debug then return self end end + if self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen(Unit:GetName()) then + self:_SendMessage("You need to open the door(s) to extract troops!", 10, false, Group) + if not self.debug then return self end + end -- load troops into heli local unit = Unit -- Wrapper.Unit#UNIT local unitname = unit:GetName() @@ -2360,6 +2372,11 @@ function CTLD:_UnloadTroops(Group, Unit) self:T(self.lid .. " _UnloadTroops") -- check if we are in LOAD zone local droppingatbase = false + local canunload = true + if self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen(Unit:GetName()) then + self:_SendMessage("You need to open the door(s) to unload troops!", 10, false, Group) + if not self.debug then return self end + end local inzone, zonename, zone, distance = self:IsUnitInZone(Unit,CTLD.CargoZoneType.LOAD) if not inzone then inzone, zonename, zone, distance = self:IsUnitInZone(Unit,CTLD.CargoZoneType.SHIP) @@ -3046,7 +3063,7 @@ end --- User function - Activate Name #CTLD.CargoZone.Type ZoneType for this CTLD instance. -- @param #CTLD self -- @param #string Name Name of the zone to change in the ME. --- @param #CTLD.CargoZoneTyp ZoneType Type of zone this belongs to. +-- @param #CTLD.CargoZoneType ZoneType Type of zone this belongs to. -- @param #boolean NewState (Optional) Set to true to activate, false to switch off. function CTLD:ActivateZone(Name,ZoneType,NewState) self:T(self.lid .. " AddZone") @@ -3082,7 +3099,7 @@ end --- User function - Deactivate Name #CTLD.CargoZoneType ZoneType for this CTLD instance. -- @param #CTLD self -- @param #string Name Name of the zone to change in the ME. --- @param #CTLD.CargoZoneTyp ZoneType Type of zone this belongs to. +-- @param #CTLD.CargoZoneType ZoneType Type of zone this belongs to. function CTLD:DeactivateZone(Name,ZoneType) self:T(self.lid .. " AddZone") self:ActivateZone(Name,ZoneType,false) From 67f847dd16c1467af77c01458fd537b05ceca64c Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 12 Oct 2021 22:16:18 +0200 Subject: [PATCH 36/37] Update Group.lua - Fixed SetInvisible and SetImmortal functions to acknowledge parameter false. --- Moose Development/Moose/Wrapper/Group.lua | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index fd1a3f600..f1443b2d0 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -2578,8 +2578,10 @@ end -- @return #GROUP self function GROUP:SetCommandInvisible(switch) self:F2( self.GroupName ) - local switch = switch or false - local SetInvisible = {id = 'SetInvisible', params = {value = true}} + if switch==nil then + switch=false + end + local SetInvisible = {id = 'SetInvisible', params = {value = switch}} self:SetCommand(SetInvisible) return self end @@ -2590,9 +2592,11 @@ end -- @return #GROUP self function GROUP:SetCommandImmortal(switch) self:F2( self.GroupName ) - local switch = switch or false - local SetInvisible = {id = 'SetImmortal', params = {value = true}} - self:SetCommand(SetInvisible) + if switch==nil then + switch=false + end + local SetImmortal = {id = 'SetImmortal', params = {value = switch}} + self:SetCommand(SetImmortal) return self end From 15f98438786d50a4fc393a32c5bc2c2d2eddd9e7 Mon Sep 17 00:00:00 2001 From: Frank Date: Sat, 16 Oct 2021 12:11:34 +0200 Subject: [PATCH 37/37] AIRBOSS v1.2.0 - Added Forrestal carrier CV-59 --- Moose Development/Moose/Ops/Airboss.lua | 65 +++++++++++++++++++------ 1 file changed, 51 insertions(+), 14 deletions(-) diff --git a/Moose Development/Moose/Ops/Airboss.lua b/Moose Development/Moose/Ops/Airboss.lua index de432cd0f..25ba1d265 100644 --- a/Moose Development/Moose/Ops/Airboss.lua +++ b/Moose Development/Moose/Ops/Airboss.lua @@ -31,6 +31,7 @@ -- * [USS Abraham Lincoln](https://en.wikipedia.org/wiki/USS_Abraham_Lincoln_(CVN-72)) (CVN-72) [Super Carrier Module] -- * [USS George Washington](https://en.wikipedia.org/wiki/USS_George_Washington_(CVN-73)) (CVN-73) [Super Carrier Module] -- * [USS Harry S. Truman](https://en.wikipedia.org/wiki/USS_Harry_S._Truman) (CVN-75) [Super Carrier Module] +-- * [USS Forrestal](https://en.wikipedia.org/wiki/USS_Forrestal_(CV-59)) (CV-59) [Heatblur Carrier Module] -- * [USS Tarawa](https://en.wikipedia.org/wiki/USS_Tarawa_(LHA-1)) (LHA-1) [**WIP**] -- * [USS America](https://en.wikipedia.org/wiki/USS_America_(LHA-6)) (LHA-6) [**WIP**] -- * [Juan Carlos I](https://en.wikipedia.org/wiki/Spanish_amphibious_assault_ship_Juan_Carlos_I) (L61) [**WIP**] @@ -1295,6 +1296,7 @@ AIRBOSS.AircraftCarrier={ -- @field #string WASHINGTON USS George Washington (CVN-73) [Super Carrier Module] -- @field #string STENNIS USS John C. Stennis (CVN-74) -- @field #string TRUMAN USS Harry S. Truman (CVN-75) [Super Carrier Module] +-- @field #string FORRESTAL USS Forrestal (CV-59) [Heatblur Carrier Module] -- @field #string VINSON USS Carl Vinson (CVN-70) [Obsolete] -- @field #string TARAWA USS Tarawa (LHA-1) -- @field #string AMERICA USS America (LHA-6) @@ -1306,6 +1308,7 @@ AIRBOSS.CarrierType={ WASHINGTON="CVN_73", TRUMAN="CVN_75", STENNIS="Stennis", + FORRESTAL="Forrestal", VINSON="VINSON", TARAWA="LHA_Tarawa", AMERICA="USS America LHA-6", @@ -1723,7 +1726,7 @@ AIRBOSS.MenuF10Root=nil --- Airboss class version. -- @field #string version -AIRBOSS.version="1.1.6" +AIRBOSS.version="1.2.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -1974,6 +1977,8 @@ function AIRBOSS:New(carriername, alias) self:_InitNimitz() elseif self.carriertype==AIRBOSS.CarrierType.TRUMAN then self:_InitNimitz() + elseif self.carriertype==AIRBOSS.CarrierType.FORRESTAL then + self:_InitForrestal() elseif self.carriertype==AIRBOSS.CarrierType.VINSON then -- TODO: Carl Vinson parameters. self:_InitStennis() @@ -2041,7 +2046,7 @@ function AIRBOSS:New(carriername, alias) local stern=self:_GetSternCoord() -- Bow pos. - local bow=stern:Translate(self.carrierparam.totlength, hdg) + local bow=stern:Translate(self.carrierparam.totlength, hdg, true) -- End of rwy. local rwy=stern:Translate(self.carrierparam.rwylength, FB, true) @@ -2059,31 +2064,31 @@ function AIRBOSS:New(carriername, alias) bow:FlareYellow() -- Runway half width = 10 m. - local r1=stern:Translate(self.carrierparam.rwywidth*0.5, FB+90) - local r2=stern:Translate(self.carrierparam.rwywidth*0.5, FB-90) - r1:FlareWhite() - r2:FlareWhite() + local r1=stern:Translate(self.carrierparam.rwywidth*0.5, FB+90, true) + local r2=stern:Translate(self.carrierparam.rwywidth*0.5, FB-90, true) + --r1:FlareWhite() + --r2:FlareWhite() -- End of runway. rwy:FlareRed() -- Right 30 meters from stern. - local cR=stern:Translate(self.carrierparam.totwidthstarboard, hdg+90) - cR:FlareYellow() + local cR=stern:Translate(self.carrierparam.totwidthstarboard, hdg+90, true) + --cR:FlareYellow() -- Left 40 meters from stern. - local cL=stern:Translate(self.carrierparam.totwidthport, hdg-90) - cL:FlareYellow() + local cL=stern:Translate(self.carrierparam.totwidthport, hdg-90, true) + --cL:FlareYellow() -- Carrier specific. if self.carrier:GetTypeName()~=AIRBOSS.CarrierType.TARAWA or self.carrier:GetTypeName()~=AIRBOSS.CarrierType.AMERICA or self.carrier:GetTypeName()~=AIRBOSS.CarrierType.JCARLOS then -- Flare wires. - local w1=stern:Translate(self.carrierparam.wire1, FB) - local w2=stern:Translate(self.carrierparam.wire2, FB) - local w3=stern:Translate(self.carrierparam.wire3, FB) - local w4=stern:Translate(self.carrierparam.wire4, FB) + local w1=stern:Translate(self.carrierparam.wire1, FB, true) + local w2=stern:Translate(self.carrierparam.wire2, FB, true) + local w3=stern:Translate(self.carrierparam.wire3, FB, true) + local w4=stern:Translate(self.carrierparam.wire4, FB, true) w1:FlareWhite() w2:FlareYellow() w3:FlareWhite() @@ -4377,6 +4382,35 @@ function AIRBOSS:_InitNimitz() end +--- Init parameters for Forrestal class super carriers. +-- @param #AIRBOSS self +function AIRBOSS:_InitForrestal() + + -- Init Nimitz as default. + self:_InitNimitz() + + -- Carrier Parameters. + self.carrierparam.sterndist =-135.5 + self.carrierparam.deckheight = 20 --20.1494 --DCS World OpenBeta\CoreMods\tech\USS_Nimitz\Database\USS_CVN_7X.lua + + -- Total size of the carrier (approx as rectangle). + self.carrierparam.totlength=315 -- Wiki says 325 meters overall length. + self.carrierparam.totwidthport=45 -- Wiki says 73 meters overall beam. + self.carrierparam.totwidthstarboard=35 + + -- Landing runway. + self.carrierparam.rwyangle = -9.1359 --DCS World OpenBeta\CoreMods\tech\USS_Nimitz\scripts\USS_Nimitz_RunwaysAndRoutes.lua + self.carrierparam.rwylength = 212 + self.carrierparam.rwywidth = 25 + + -- Wires. + self.carrierparam.wire1 = 42 -- Distance from stern to first wire. + self.carrierparam.wire2 = 51.5 + self.carrierparam.wire3 = 62 + self.carrierparam.wire4 = 72.5 + +end + --- Init parameters for LHA-1 Tarawa carrier. -- @param #AIRBOSS self function AIRBOSS:_InitTarawa() @@ -10544,6 +10578,9 @@ function AIRBOSS:_GetSternCoord() elseif self.carriertype==AIRBOSS.CarrierType.STENNIS then -- Stennis: translate 7 meters starboard wrt Final bearing. self.sterncoord:Translate(self.carrierparam.sterndist, hdg, true, true):Translate(7, FB+90, true, true) + elseif self.carriertype==AIRBOSS.CarrierType.FORRESTAL then + -- Forrestal + self.sterncoord:Translate(self.carrierparam.sterndist, hdg, true, true):Translate(7.5, FB+90, true, true) else -- Nimitz SC: translate 8 meters starboard wrt Final bearing. self.sterncoord:Translate(self.carrierparam.sterndist, hdg, true, true):Translate(9.5, FB+90, true, true)