From 46258492bd2404866be1640362ba13d6827751af Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 23 Jul 2023 12:37:57 +0200 Subject: [PATCH 01/22] #AIRBASE, #ATIS * Additions --- Moose Development/Moose/Ops/ATIS.lua | 16 +++++++++++++--- Moose Development/Moose/Wrapper/Airbase.lua | 14 ++++++++++++-- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index 719ad8da7..a7222956b 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -419,6 +419,8 @@ ATIS.RunwayM2T = { TheChannel = -10, Syria = 5, MarianaIslands = 2, + Falklands = 12, + Sinai = 5, } --- Whether ICAO phraseology is used for ATIS broadcasts. @@ -430,6 +432,8 @@ ATIS.RunwayM2T = { -- @field #boolean TheChannel true. -- @field #boolean Syria true. -- @field #boolean MarianaIslands true. +-- @field #boolean Falklands true. +-- @field #boolean Sinai true. ATIS.ICAOPhraseology = { Caucasus = true, Nevada = false, @@ -437,7 +441,9 @@ ATIS.ICAOPhraseology = { PersianGulf = true, TheChannel = true, Syria = true, - MarianaIslands = true + MarianaIslands = true, + Falklands = true, + Sinai = true, } --- Nav point data. @@ -1272,7 +1278,8 @@ end -- @param #string Event Event. -- @param #string To To state. function ATIS:onafterStart( From, Event, To ) - + self:I("Airbase category is "..self.airbase:GetAirbaseCategory()) + -- Check that this is an airdrome. if self.airbase:GetAirbaseCategory() == Airbase.Category.SHIP then self:E( self.lid .. string.format( "ERROR: Cannot start ATIS for airbase %s! Only AIRDROMES are supported but NOT SHIPS.", self.airbasename ) ) @@ -1823,7 +1830,10 @@ function ATIS:onafterBroadcast( From, Event, To ) -- Airbase name subtitle = string.format( "%s", self.airbasename ) - if (not self.ATISforFARPs) and self.airbasename:find( "AFB" ) == nil and self.airbasename:find( "Airport" ) == nil and self.airbasename:find( "Airstrip" ) == nil and self.airbasename:find( "airfield" ) == nil and self.airbasename:find( "AB" ) == nil then + if (not self.ATISforFARPs) and self.airbasename:find( "AFB" ) == nil and self.airbasename:find( "Airport" ) == nil + and self.airbasename:find( "Airstrip" ) == nil and self.airbasename:find( "airfield" ) == nil and self.airbasename:find( "AB" ) == nil + and self.airbasename:find( "Field" ) == nil + then subtitle = subtitle .. " Airport" end if not self.useSRS then diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index 276ec5b8d..dd52825fa 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -11,7 +11,7 @@ -- @module Wrapper.Airbase -- @image Wrapper_Airbase.JPG - +--- -- @type AIRBASE -- @field #string ClassName Name of the class, i.e. "AIRBASE". -- @field #table CategoryName Names of airbase categories. @@ -548,6 +548,8 @@ AIRBASE.Syria={ -- * AIRBASE.MarianaIslands.Saipan_Intl -- * AIRBASE.MarianaIslands.Tinian_Intl -- * AIRBASE.MarianaIslands.Olf_Orote +-- * AIRBASE.MarianaIslands.Pagan_Airstrip +-- * AIRBASE.MarianaIslands.North_West_Field -- -- @field MarianaIslands AIRBASE.MarianaIslands = { @@ -557,6 +559,8 @@ AIRBASE.MarianaIslands = { ["Saipan_Intl"] = "Saipan Intl", ["Tinian_Intl"] = "Tinian Intl", ["Olf_Orote"] = "Olf Orote", + ["Pagan_Airstrip"] = "Pagan Airstrip", + ["North_West_Field"] = "North West Field", } --- Airbases of the South Atlantic map: @@ -787,7 +791,13 @@ function AIRBASE:Register(AirbaseName) -- Category. self.category=self.descriptors and self.descriptors.category or Airbase.Category.AIRDROME - + + -- H2 is bugged + --if self.AirbaseName == "H4" and self.descriptors == nil then + --self:E("***** H4 on Syria map is currently bugged!") + --return nil + --end + -- Set category. if self.category==Airbase.Category.AIRDROME then self.isAirdrome=true From 7d3a08cde2a7f4b7f47205f538ce2de74bec485e Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 23 Jul 2023 12:42:36 +0200 Subject: [PATCH 02/22] #CLIENTMENU, #PLAYERTASK * Fixes for "No current task" not being deleted later on --- Moose Development/Moose/Core/ClientMenu.lua | 5 ++-- Moose Development/Moose/Ops/PlayerTask.lua | 26 ++++++++++++++++++--- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Core/ClientMenu.lua b/Moose Development/Moose/Core/ClientMenu.lua index d58c62f4b..1f11eebaa 100644 --- a/Moose Development/Moose/Core/ClientMenu.lua +++ b/Moose Development/Moose/Core/ClientMenu.lua @@ -381,7 +381,7 @@ end CLIENTMENUMANAGER = { ClassName = "CLIENTMENUMANAGER", lid = "", - version = "0.1.0", + version = "0.1.1", name = nil, clientset = nil, menutree = {}, @@ -670,7 +670,7 @@ end --- Remove the entry and all entries below the given entry from the client's F10 menus. -- @param #CLIENTMENUMANAGER self -- @param #CLIENTMENU Entry The entry to remove --- @param Wrapper.Client#CLIENT Client (optional) If given, make this change only for this client. In this case the generic structure will not be touched. +-- @param Wrapper.Client#CLIENT Client (optional) If given, make this change only for this client. -- @return #CLIENTMENUMANAGER self function CLIENTMENUMANAGER:DeleteF10Entry(Entry,Client) self:T(self.lid.."DeleteF10Entry") @@ -783,3 +783,4 @@ end -- End ClientMenu -- ---------------------------------------------------------------------------------------------------------------- + diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 196e422d0..d44e9c644 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -98,7 +98,7 @@ PLAYERTASK = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASK.version="0.1.17" +PLAYERTASK.version="0.1.18" --- Generic task condition. -- @type PLAYERTASK.Condition @@ -1008,6 +1008,7 @@ do -- @field Core.ClientMenu#CLIENTMENUMANAGER ActiveTaskMenuTemplate -- @field Core.ClientMenu#CLIENTMENU ActiveTopMenu -- @field Core.ClientMenu#CLIENTMENU ActiveInfoMenu +-- @field Core.ClientMenu#CLIENTMENU MenuNoTask -- @extends Core.Fsm#FSM --- @@ -1336,6 +1337,7 @@ PLAYERTASKCONTROLLER = { InfoHasCoordinate = false, UseTypeNames = false, Scoring = nil, + MenuNoTask = nil, } --- @@ -3469,6 +3471,19 @@ function PLAYERTASKCONTROLLER:_UpdateJoinMenuTemplate() local actinfomenu = self.ActiveInfoMenu --local entrynumbers = {} --local existingentries = {} + + if self.TaskQueue:Count() == 0 and self.MenuNoTask == nil then + local menunotasks = self.gettext:GetEntry("MENUNOTASKS",self.locale) + self.MenuNoTask = controller:NewEntry(menunotasks,self.JoinMenu) + controller:AddEntry(self.MenuNoTask) + end + + if self.TaskQueue:Count() > 0 and self.MenuNoTask ~= nil then + controller:DeleteGenericEntry(self.MenuNoTask) + controller:DeleteF10Entry(self.MenuNoTask) + self.MenuNoTask = nil + end + local maxn = self.menuitemlimit -- Generate task type menu items for _type,_ in pairs(taskpertype) do @@ -3641,8 +3656,13 @@ function PLAYERTASKCONTROLLER:_CreateJoinMenuTemplate() self.JoinInfoMenu = JoinTaskMenuTemplate:NewEntry(menutaskinfo,self.JoinTopMenu) end - if self.TaskQueue:Count() == 0 then - JoinTaskMenuTemplate:NewEntry(menunotasks,self.JoinMenu) + if self.TaskQueue:Count() == 0 and self.MenuNoTask == nil then + self.MenuNoTask = JoinTaskMenuTemplate:NewEntry(menunotasks,self.JoinMenu) + end + + if self.TaskQueue:Count() > 0 and self.MenuNoTask ~= nil then + JoinTaskMenuTemplate:DeleteGenericEntry(self.MenuNoTask) + self.MenuNoTask = nil end self.JoinTaskMenuTemplate = JoinTaskMenuTemplate From f31741f934a8d3c66bfe5a1f3ced16a7b1c40e86 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 25 Jul 2023 12:01:18 +0200 Subject: [PATCH 03/22] #AI_ESCORT * Improve documentation --- Moose Development/Moose/AI/AI_Escort.lua | 126 +++++++++++++---------- 1 file changed, 69 insertions(+), 57 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Escort.lua b/Moose Development/Moose/AI/AI_Escort.lua index e18948790..c772940ab 100644 --- a/Moose Development/Moose/AI/AI_Escort.lua +++ b/Moose Development/Moose/AI/AI_Escort.lua @@ -147,8 +147,8 @@ -- @image Escorting.JPG - ---- @type AI_ESCORT +--- +-- @type AI_ESCORT -- @extends AI.AI_Formation#AI_FORMATION @@ -168,11 +168,14 @@ -- -- -- First find the GROUP object and the CLIENT object. -- local EscortUnit = CLIENT:FindByName( "Unit Name" ) -- The Unit Name is the name of the unit flagged with the skill Client in the mission editor. --- local EscortGroup = GROUP:FindByName( "Group Name" ) -- The Group Name is the name of the group that will escort the Escort Client. +-- local EscortGroup = SET_GROUP:New():FilterPrefixes("Escort"):FilterOnce() -- The the group name of the escorts contains "Escort". -- -- -- Now use these 2 objects to construct the new EscortPlanes object. -- EscortPlanes = AI_ESCORT:New( EscortUnit, EscortGroup, "Desert", "Welcome to the mission. You are escorted by a plane with code name 'Desert', which can be instructed through the F10 radio menu." ) --- +-- EscortPlanes:MenusAirplanes() -- create menus for airplanes +-- EscortPlanes:__Start(2) +-- +-- -- @field #AI_ESCORT AI_ESCORT = { ClassName = "AI_ESCORT", @@ -189,7 +192,7 @@ AI_ESCORT = { TaskPoints = {} } ---- @field Functional.Detection#DETECTION_AREAS +-- @field Functional.Detection#DETECTION_AREAS AI_ESCORT.Detection = nil --- MENUPARAM type @@ -211,10 +214,14 @@ AI_ESCORT.Detection = nil -- -- -- First find the GROUP object and the CLIENT object. -- local EscortUnit = CLIENT:FindByName( "Unit Name" ) -- The Unit Name is the name of the unit flagged with the skill Client in the mission editor. --- local EscortGroup = GROUP:FindByName( "Group Name" ) -- The Group Name is the name of the group that will escort the Escort Client. +-- local EscortGroup = SET_GROUP:New():FilterPrefixes("Escort"):FilterOnce() -- The the group name of the escorts contains "Escort". -- -- -- Now use these 2 objects to construct the new EscortPlanes object. -- EscortPlanes = AI_ESCORT:New( EscortUnit, EscortGroup, "Desert", "Welcome to the mission. You are escorted by a plane with code name 'Desert', which can be instructed through the F10 radio menu." ) +-- EscortPlanes:MenusAirplanes() -- create menus for airplanes +-- EscortPlanes:__Start(2) +-- +-- function AI_ESCORT:New( EscortUnit, EscortGroupSet, EscortName, EscortBriefing ) local self = BASE:Inherit( self, AI_FORMATION:New( EscortUnit, EscortGroupSet, EscortName, EscortBriefing ) ) -- #AI_ESCORT @@ -227,10 +234,17 @@ function AI_ESCORT:New( EscortUnit, EscortGroupSet, EscortName, EscortBriefing ) self.EscortGroupSet = EscortGroupSet self.EscortGroupSet:SetSomeIteratorLimit( 8 ) - + self.EscortBriefing = EscortBriefing self.Menu = {} + self.Menu.HoldAtEscortPosition = self.Menu.HoldAtEscortPosition or {} + self.Menu.HoldAtLeaderPosition = self.Menu.HoldAtLeaderPosition or {} + self.Menu.Flare = self.Menu.Flare or {} + self.Menu.Smoke = self.Menu.Smoke or {} + self.Menu.Targets = self.Menu.Targets or {} + self.Menu.ROE = self.Menu.ROE or {} + self.Menu.ROT = self.Menu.ROT or {} -- if not EscortBriefing then -- EscortGroup:MessageToClient( EscortGroup:GetCategoryName() .. " '" .. EscortName .. "' (" .. EscortGroup:GetCallsign() .. ") reporting! " .. @@ -250,7 +264,7 @@ function AI_ESCORT:New( EscortUnit, EscortGroupSet, EscortName, EscortBriefing ) EscortGroupSet:ForEachGroup( - --- @param Core.Group#GROUP EscortGroup + -- @param Core.Group#GROUP EscortGroup function( EscortGroup ) -- Set EscortGroup known at EscortUnit. if not self.PlayerUnit._EscortGroups then @@ -325,14 +339,14 @@ function AI_ESCORT:_InitEscortRoute( EscortGroup ) end ---- @param #AI_ESCORT self +-- @param #AI_ESCORT self -- @param Core.Set#SET_GROUP EscortGroupSet function AI_ESCORT:onafterStart( EscortGroupSet ) self:F() EscortGroupSet:ForEachGroup( - --- @param Core.Group#GROUP EscortGroup + -- @param Core.Group#GROUP EscortGroup function( EscortGroup ) EscortGroup:WayPointInitialize() @@ -370,7 +384,7 @@ function AI_ESCORT:onafterStart( EscortGroupSet ) self:_InitFlightMenus() self.EscortGroupSet:ForSomeGroupAlive( - --- @param Core.Group#GROUP EscortGroup + -- @param Core.Group#GROUP EscortGroup function( EscortGroup ) self:_InitEscortMenus( EscortGroup ) @@ -378,7 +392,7 @@ function AI_ESCORT:onafterStart( EscortGroupSet ) self:SetFlightModeFormation( EscortGroup ) - --- @param #AI_ESCORT self + -- @param #AI_ESCORT self -- @param Core.Event#EVENTDATA EventData function EscortGroup:OnEventDeadOrCrash( EventData ) self:F( { "EventDead", EventData } ) @@ -394,14 +408,14 @@ function AI_ESCORT:onafterStart( EscortGroupSet ) end ---- @param #AI_ESCORT self +-- @param #AI_ESCORT self -- @param Core.Set#SET_GROUP EscortGroupSet function AI_ESCORT:onafterStop( EscortGroupSet ) self:F() EscortGroupSet:ForEachGroup( - --- @param Core.Group#GROUP EscortGroup + -- @param Core.Group#GROUP EscortGroup function( EscortGroup ) EscortGroup:WayPointInitialize() @@ -550,7 +564,7 @@ function AI_ESCORT:SetFlightMenuFormation( Formation ) local MenuFlightFormationID = MENU_GROUP_COMMAND:New( self.PlayerGroup, Formation, FlightMenuFormation, function ( self, Formation, ... ) self.EscortGroupSet:ForSomeGroupAlive( - --- @param Core.Group#GROUP EscortGroup + -- @param Core.Group#GROUP EscortGroup function( EscortGroup, self, Formation, Arguments ) if EscortGroup:IsAir() then self:E({FormationID=FormationID}) @@ -764,7 +778,7 @@ end function AI_ESCORT:SetFlightMenuHoldAtEscortPosition() - for _, MenuHoldAtEscortPosition in pairs( self.Menu.HoldAtEscortPosition ) do + for _, MenuHoldAtEscortPosition in pairs( self.Menu.HoldAtEscortPosition or {} ) do local FlightMenuReportNavigation = MENU_GROUP:New( self.PlayerGroup, "Navigation", self.FlightMenu ) local FlightMenuHoldPosition = MENU_GROUP_COMMAND @@ -785,7 +799,7 @@ end function AI_ESCORT:SetEscortMenuHoldAtEscortPosition( EscortGroup ) - for _, HoldAtEscortPosition in pairs( self.Menu.HoldAtEscortPosition ) do + for _, HoldAtEscortPosition in pairs( self.Menu.HoldAtEscortPosition or {}) do if EscortGroup:IsAir() then local EscortGroupName = EscortGroup:GetName() local EscortMenuReportNavigation = MENU_GROUP:New( self.PlayerGroup, "Navigation", EscortGroup.EscortMenu ) @@ -853,7 +867,7 @@ end function AI_ESCORT:SetFlightMenuHoldAtLeaderPosition() - for _, MenuHoldAtLeaderPosition in pairs( self.Menu.HoldAtLeaderPosition ) do + for _, MenuHoldAtLeaderPosition in pairs( self.Menu.HoldAtLeaderPosition or {}) do local FlightMenuReportNavigation = MENU_GROUP:New( self.PlayerGroup, "Navigation", self.FlightMenu ) local FlightMenuHoldAtLeaderPosition = MENU_GROUP_COMMAND @@ -874,7 +888,7 @@ end function AI_ESCORT:SetEscortMenuHoldAtLeaderPosition( EscortGroup ) - for _, HoldAtLeaderPosition in pairs( self.Menu.HoldAtLeaderPosition ) do + for _, HoldAtLeaderPosition in pairs( self.Menu.HoldAtLeaderPosition or {}) do if EscortGroup:IsAir() then local EscortGroupName = EscortGroup:GetName() @@ -999,7 +1013,7 @@ end function AI_ESCORT:SetFlightMenuFlare() - for _, MenuFlare in pairs( self.Menu.Flare) do + for _, MenuFlare in pairs( self.Menu.Flare or {}) do local FlightMenuReportNavigation = MENU_GROUP:New( self.PlayerGroup, "Navigation", self.FlightMenu ) local FlightMenuFlare = MENU_GROUP:New( self.PlayerGroup, MenuFlare.MenuText, FlightMenuReportNavigation ) @@ -1014,7 +1028,7 @@ end function AI_ESCORT:SetEscortMenuFlare( EscortGroup ) - for _, MenuFlare in pairs( self.Menu.Flare) do + for _, MenuFlare in pairs( self.Menu.Flare or {}) do if EscortGroup:IsAir() then local EscortGroupName = EscortGroup:GetName() @@ -1059,7 +1073,7 @@ end function AI_ESCORT:SetFlightMenuSmoke() - for _, MenuSmoke in pairs( self.Menu.Smoke) do + for _, MenuSmoke in pairs( self.Menu.Smoke or {}) do local FlightMenuReportNavigation = MENU_GROUP:New( self.PlayerGroup, "Navigation", self.FlightMenu ) local FlightMenuSmoke = MENU_GROUP:New( self.PlayerGroup, MenuSmoke.MenuText, FlightMenuReportNavigation ) @@ -1076,7 +1090,7 @@ end function AI_ESCORT:SetEscortMenuSmoke( EscortGroup ) - for _, MenuSmoke in pairs( self.Menu.Smoke) do + for _, MenuSmoke in pairs( self.Menu.Smoke or {}) do if EscortGroup:IsAir() then local EscortGroupName = EscortGroup:GetName() @@ -1169,7 +1183,7 @@ function AI_ESCORT:SetFlightMenuTargets() local FlightMenuAttackNearbyAir = MENU_GROUP_COMMAND:New( self.PlayerGroup, "Attack nearest airborne targets", self.FlightMenuAttack, AI_ESCORT._FlightAttackNearestTarget, self, self.__Enum.ReportType.Air ):SetTag( "Attack" ) local FlightMenuAttackNearbyGround = MENU_GROUP_COMMAND:New( self.PlayerGroup, "Attack nearest ground targets", self.FlightMenuAttack, AI_ESCORT._FlightAttackNearestTarget, self, self.__Enum.ReportType.Ground ):SetTag( "Attack" ) - for _, MenuTargets in pairs( self.Menu.Targets) do + for _, MenuTargets in pairs( self.Menu.Targets or {}) do MenuTargets.FlightReportTargetsScheduler = SCHEDULER:New( self, self._FlightReportTargetsScheduler, {}, MenuTargets.Interval, MenuTargets.Interval ) end @@ -1179,7 +1193,7 @@ end function AI_ESCORT:SetEscortMenuTargets( EscortGroup ) - for _, MenuTargets in pairs( self.Menu.Targets) do + for _, MenuTargets in pairs( self.Menu.Targets or {} or {}) do if EscortGroup:IsAir() then local EscortGroupName = EscortGroup:GetName() --local EscortMenuReportTargets = MENU_GROUP:New( self.PlayerGroup, "Report targets", EscortGroup.EscortMenu ) @@ -1231,7 +1245,7 @@ function AI_ESCORT:MenuAssistedAttack() self:F() self.EscortGroupSet:ForSomeGroupAlive( - --- @param Core.Group#GROUP EscortGroup + -- @param Core.Group#GROUP EscortGroup function( EscortGroup ) if not EscortGroup:IsAir() then -- Request assistance from other escorts. @@ -1246,7 +1260,7 @@ end function AI_ESCORT:SetFlightMenuROE() - for _, MenuROE in pairs( self.Menu.ROE) do + for _, MenuROE in pairs( self.Menu.ROE or {}) do local FlightMenuROE = MENU_GROUP:New( self.PlayerGroup, "Rule Of Engagement", self.FlightMenu ) local FlightMenuROEHoldFire = MENU_GROUP_COMMAND:New( self.PlayerGroup, "Hold fire", FlightMenuROE, AI_ESCORT._FlightROEHoldFire, self, "Holding weapons!" ) @@ -1261,7 +1275,7 @@ end function AI_ESCORT:SetEscortMenuROE( EscortGroup ) - for _, MenuROE in pairs( self.Menu.ROE) do + for _, MenuROE in pairs( self.Menu.ROE or {}) do if EscortGroup:IsAir() then local EscortGroupName = EscortGroup:GetName() @@ -1302,7 +1316,7 @@ end function AI_ESCORT:SetFlightMenuROT() - for _, MenuROT in pairs( self.Menu.ROT) do + for _, MenuROT in pairs( self.Menu.ROT or {}) do local FlightMenuROT = MENU_GROUP:New( self.PlayerGroup, "Reaction On Threat", self.FlightMenu ) local FlightMenuROTNoReaction = MENU_GROUP_COMMAND:New( self.PlayerGroup, "Fight until death", FlightMenuROT, AI_ESCORT._FlightROTNoReaction, self, "Fighting until death!" ) @@ -1317,7 +1331,7 @@ end function AI_ESCORT:SetEscortMenuROT( EscortGroup ) - for _, MenuROT in pairs( self.Menu.ROT) do + for _, MenuROT in pairs( self.Menu.ROT or {}) do if EscortGroup:IsAir() then local EscortGroupName = EscortGroup:GetName() @@ -1375,7 +1389,7 @@ function AI_ESCORT:SetEscortMenuResumeMission( EscortGroup ) end ---- @param #AI_ESCORT self +-- @param #AI_ESCORT self -- @param Wrapper.Group#GROUP OrbitGroup -- @param Wrapper.Group#GROUP EscortGroup -- @param #number OrbitHeight @@ -1419,7 +1433,7 @@ function AI_ESCORT:_HoldPosition( OrbitGroup, EscortGroup, OrbitHeight, OrbitSec end ---- @param #AI_ESCORT self +-- @param #AI_ESCORT self -- @param Wrapper.Group#GROUP OrbitGroup -- @param #number OrbitHeight -- @param #number OrbitSeconds @@ -1428,7 +1442,7 @@ function AI_ESCORT:_FlightHoldPosition( OrbitGroup, OrbitHeight, OrbitSeconds ) local EscortUnit = self.PlayerUnit self.EscortGroupSet:ForEachGroupAlive( - --- @param Core.Group#GROUP EscortGroup + -- @param Core.Group#GROUP EscortGroup function( EscortGroup, OrbitGroup ) if EscortGroup:IsAir() then if OrbitGroup == nil then @@ -1456,7 +1470,7 @@ end function AI_ESCORT:_FlightJoinUp() self.EscortGroupSet:ForEachGroupAlive( - --- @param Core.Group#GROUP EscortGroup + -- @param Core.Group#GROUP EscortGroup function( EscortGroup ) if EscortGroup:IsAir() then self:_JoinUp( EscortGroup ) @@ -1483,7 +1497,7 @@ end function AI_ESCORT:_FlightFormationTrail( XStart, XSpace, YStart ) self.EscortGroupSet:ForEachGroupAlive( - --- @param Core.Group#GROUP EscortGroup + -- @param Core.Group#GROUP EscortGroup function( EscortGroup ) if EscortGroup:IsAir() then self:_EscortFormationTrail( EscortGroup, XStart, XSpace, YStart ) @@ -1510,7 +1524,7 @@ end function AI_ESCORT:_FlightFormationStack( XStart, XSpace, YStart, YSpace ) self.EscortGroupSet:ForEachGroupAlive( - --- @param Core.Group#GROUP EscortGroup + -- @param Core.Group#GROUP EscortGroup function( EscortGroup ) if EscortGroup:IsAir() then self:_EscortFormationStack( EscortGroup, XStart, XSpace, YStart, YSpace ) @@ -1533,7 +1547,7 @@ end function AI_ESCORT:_FlightFlare( Color, Message ) self.EscortGroupSet:ForEachGroupAlive( - --- @param Core.Group#GROUP EscortGroup + -- @param Core.Group#GROUP EscortGroup function( EscortGroup ) if EscortGroup:IsAir() then self:_Flare( EscortGroup, Color, Message ) @@ -1556,7 +1570,7 @@ end function AI_ESCORT:_FlightSmoke( Color, Message ) self.EscortGroupSet:ForEachGroupAlive( - --- @param Core.Group#GROUP EscortGroup + -- @param Core.Group#GROUP EscortGroup function( EscortGroup ) if EscortGroup:IsAir() then self:_Smoke( EscortGroup, Color, Message ) @@ -1587,7 +1601,7 @@ end function AI_ESCORT:_FlightSwitchReportNearbyTargets( ReportTargets ) self.EscortGroupSet:ForEachGroupAlive( - --- @param Core.Group#GROUP EscortGroup + -- @param Core.Group#GROUP EscortGroup function( EscortGroup ) if EscortGroup:IsAir() then self:_EscortSwitchReportNearbyTargets( EscortGroup, ReportTargets ) @@ -1679,7 +1693,7 @@ function AI_ESCORT:_ScanTargets( ScanDuration ) end ---- @param Wrapper.Group#GROUP EscortGroup +-- @param Wrapper.Group#GROUP EscortGroup -- @param #AI_ESCORT self function AI_ESCORT.___Resume( EscortGroup, self ) @@ -1701,7 +1715,7 @@ function AI_ESCORT.___Resume( EscortGroup, self ) end ---- @param #AI_ESCORT self +-- @param #AI_ESCORT self -- @param Wrapper.Group#GROUP EscortGroup -- @param #number WayPoint function AI_ESCORT:_ResumeMission( EscortGroup, WayPoint ) @@ -1723,7 +1737,7 @@ function AI_ESCORT:_ResumeMission( EscortGroup, WayPoint ) end ---- @param #AI_ESCORT self +-- @param #AI_ESCORT self -- @param Wrapper.Group#GROUP EscortGroup The escort group that will attack the detected item. -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem function AI_ESCORT:_AttackTarget( EscortGroup, DetectedItem ) @@ -1743,7 +1757,7 @@ function AI_ESCORT:_AttackTarget( EscortGroup, DetectedItem ) local AttackUnitTasks = {} DetectedSet:ForEachUnit( - --- @param Wrapper.Unit#UNIT DetectedUnit + -- @param Wrapper.Unit#UNIT DetectedUnit function( DetectedUnit, Tasks ) if DetectedUnit:IsAlive() then AttackUnitTasks[#AttackUnitTasks+1] = EscortGroup:TaskAttackUnit( DetectedUnit ) @@ -1767,7 +1781,7 @@ function AI_ESCORT:_AttackTarget( EscortGroup, DetectedItem ) local Tasks = {} DetectedSet:ForEachUnit( - --- @param Wrapper.Unit#UNIT DetectedUnit + -- @param Wrapper.Unit#UNIT DetectedUnit function( DetectedUnit, Tasks ) if DetectedUnit:IsAlive() then Tasks[#Tasks+1] = EscortGroup:TaskFireAtPoint( DetectedUnit:GetVec2(), 50 ) @@ -1795,7 +1809,7 @@ end function AI_ESCORT:_FlightAttackTarget( DetectedItem ) self.EscortGroupSet:ForEachGroupAlive( - --- @param Core.Group#GROUP EscortGroup + -- @param Core.Group#GROUP EscortGroup function( EscortGroup, DetectedItem ) if EscortGroup:IsAir() then self:_AttackTarget( EscortGroup, DetectedItem ) @@ -1842,7 +1856,7 @@ end --- ---- @param #AI_ESCORT self +-- @param #AI_ESCORT self -- @param Wrapper.Group#GROUP EscortGroup The escort group that will attack the detected item. -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem function AI_ESCORT:_AssistTarget( EscortGroup, DetectedItem ) @@ -1854,7 +1868,7 @@ function AI_ESCORT:_AssistTarget( EscortGroup, DetectedItem ) local Tasks = {} DetectedSet:ForEachUnit( - --- @param Wrapper.Unit#UNIT DetectedUnit + -- @param Wrapper.Unit#UNIT DetectedUnit function( DetectedUnit, Tasks ) if DetectedUnit:IsAlive() then Tasks[#Tasks+1] = EscortGroup:TaskFireAtPoint( DetectedUnit:GetVec2(), 50 ) @@ -1881,7 +1895,7 @@ end function AI_ESCORT:_FlightROEHoldFire( EscortROEMessage ) self.EscortGroupSet:ForEachGroupAlive( - --- @param Wrapper.Group#GROUP EscortGroup + -- @param Wrapper.Group#GROUP EscortGroup function( EscortGroup ) self:_ROE( EscortGroup, EscortGroup.OptionROEHoldFire, EscortROEMessage ) end @@ -1890,7 +1904,7 @@ end function AI_ESCORT:_FlightROEOpenFire( EscortROEMessage ) self.EscortGroupSet:ForEachGroupAlive( - --- @param Wrapper.Group#GROUP EscortGroup + -- @param Wrapper.Group#GROUP EscortGroup function( EscortGroup ) self:_ROE( EscortGroup, EscortGroup.OptionROEOpenFire, EscortROEMessage ) end @@ -1899,7 +1913,7 @@ end function AI_ESCORT:_FlightROEReturnFire( EscortROEMessage ) self.EscortGroupSet:ForEachGroupAlive( - --- @param Wrapper.Group#GROUP EscortGroup + -- @param Wrapper.Group#GROUP EscortGroup function( EscortGroup ) self:_ROE( EscortGroup, EscortGroup.OptionROEReturnFire, EscortROEMessage ) end @@ -1908,7 +1922,7 @@ end function AI_ESCORT:_FlightROEWeaponFree( EscortROEMessage ) self.EscortGroupSet:ForEachGroupAlive( - --- @param Wrapper.Group#GROUP EscortGroup + -- @param Wrapper.Group#GROUP EscortGroup function( EscortGroup ) self:_ROE( EscortGroup, EscortGroup.OptionROEWeaponFree, EscortROEMessage ) end @@ -1924,7 +1938,7 @@ end function AI_ESCORT:_FlightROTNoReaction( EscortROTMessage ) self.EscortGroupSet:ForEachGroupAlive( - --- @param Wrapper.Group#GROUP EscortGroup + -- @param Wrapper.Group#GROUP EscortGroup function( EscortGroup ) self:_ROT( EscortGroup, EscortGroup.OptionROTNoReaction, EscortROTMessage ) end @@ -1933,7 +1947,7 @@ end function AI_ESCORT:_FlightROTPassiveDefense( EscortROTMessage ) self.EscortGroupSet:ForEachGroupAlive( - --- @param Wrapper.Group#GROUP EscortGroup + -- @param Wrapper.Group#GROUP EscortGroup function( EscortGroup ) self:_ROT( EscortGroup, EscortGroup.OptionROTPassiveDefense, EscortROTMessage ) end @@ -1942,7 +1956,7 @@ end function AI_ESCORT:_FlightROTEvadeFire( EscortROTMessage ) self.EscortGroupSet:ForEachGroupAlive( - --- @param Wrapper.Group#GROUP EscortGroup + -- @param Wrapper.Group#GROUP EscortGroup function( EscortGroup ) self:_ROT( EscortGroup, EscortGroup.OptionROTEvadeFire, EscortROTMessage ) end @@ -1951,7 +1965,7 @@ end function AI_ESCORT:_FlightROTVertical( EscortROTMessage ) self.EscortGroupSet:ForEachGroupAlive( - --- @param Wrapper.Group#GROUP EscortGroup + -- @param Wrapper.Group#GROUP EscortGroup function( EscortGroup ) self:_ROT( EscortGroup, EscortGroup.OptionROTVertical, EscortROTMessage ) end @@ -2178,5 +2192,3 @@ function AI_ESCORT:_FlightReportTargetsScheduler() return false end - - From 838e11de05c9bdef1b375aefa50017e64933fdd0 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 25 Jul 2023 14:50:29 +0200 Subject: [PATCH 04/22] #AWACS * Added tactical radio option --- Moose Development/Moose/Ops/Awacs.lua | 301 ++++++++++++++++++++++++-- 1 file changed, 285 insertions(+), 16 deletions(-) diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index 7eaf70fa1..c051869b6 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -17,7 +17,7 @@ -- === -- -- ### Author: **applevangelist** --- @date Last Update December 2022 +-- @date Last Update July 2023 -- @module Ops.AWACS -- @image OPS_AWACS.jpg @@ -63,6 +63,7 @@ do -- @field Utilities.FiFo#FIFO RadioQueue -- @field Utilities.FiFo#FIFO PrioRadioQueue -- @field Utilities.FiFo#FIFO CAPAirwings +-- @field Utilities.FiFo#FIFO TacticalQueue -- @field #number AwacsTimeOnStation -- @field #number AwacsTimeStamp -- @field #number EscortsTimeOnStation @@ -113,6 +114,13 @@ do -- @field Wrapper.Group#GROUP GCIGroup EWR group object for GCI ops -- @field #string locale Localization -- @field #boolean IncludeHelicopters +-- @field #boolean TacticalMenu +-- @field #table TacticalFrequencies +-- @field #table TacticalSubscribers +-- @field #number TacticalBaseFreq +-- @field #number TacticalIncrFreq +-- @field #number TacticalModulation +-- @field #number TacticalInterval -- @extends Core.Fsm#FSM @@ -499,7 +507,7 @@ do -- @field #AWACS AWACS = { ClassName = "AWACS", -- #string - version = "0.2.55", -- #string + version = "0.2.56", -- #string lid = "", -- #string coalition = coalition.side.BLUE, -- #number coalitiontxt = "blue", -- #string @@ -532,6 +540,7 @@ AWACS = { ContactsAO = {}, -- Utilities.FiFo#FIFO RadioQueue = {}, -- Utilities.FiFo#FIFO PrioRadioQueue = {}, -- Utilities.FiFo#FIFO + TacticalQueue = {}, -- Utilities.FiFo#FIFO AwacsTimeOnStation = 4, AwacsTimeStamp = 0, EscortsTimeOnStation = 4, @@ -587,6 +596,13 @@ AWACS = { GCIGroup = nil, locale = "en", IncludeHelicopters = false, + TacticalMenu = false, + TacticalFrequencies = {}, + TacticalSubscribers = {}, + TacticalBaseFreq = 130, + TacticalIncrFreq = 0.5, + TacticalModulation = radio.modulation.AM, + TacticalInterval = 120, } --- @@ -917,7 +933,7 @@ AWACS.TaskStatus = { --@field #boolean FromAI ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO-List 0.2.52 +-- TODO-List 0.2.53 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- -- DONE - WIP - Player tasking, VID @@ -1110,6 +1126,7 @@ function AWACS:New(Name,AirWing,Coalition,AirbaseName,AwacsOrbit,OpsZone,Station self.Volume = 1.0 self.RadioQueue = FIFO:New() -- Utilities.FiFo#FIFO self.PrioRadioQueue = FIFO:New() -- Utilities.FiFo#FIFO + self.TacticalQueue = FIFO:New() -- Utilities.FiFo#FIFO self.maxspeakentries = 3 self.GoogleTTSPadding = 1 self.WindowsTTSPadding = 2.5 @@ -1180,6 +1197,15 @@ function AWACS:New(Name,AirWing,Coalition,AirbaseName,AwacsOrbit,OpsZone,Station self.clientmenus = FIFO:New() -- Utilities.FiFo#FIFO + -- Tactical Menu + self.TacticalMenu = false + self.TacticalBaseFreq = 130 + self.TacticalIncrFreq = 0.5 + self.TacticalModulation = radio.modulation.AM + self.acticalFrequencies = {} + self.TacticalSubscribers = {} + self.TacticalInterval = 120 + -- SET for Intel Detection self.DetectionSet=SET_GROUP:New() @@ -1204,6 +1230,7 @@ function AWACS:New(Name,AirWing,Coalition,AirbaseName,AwacsOrbit,OpsZone,Station self:AddTransition("*", "LostCluster", "*") self:AddTransition("*", "LostContact", "*") self:AddTransition("*", "CheckRadioQueue", "*") + self:AddTransition("*", "CheckTacticalQueue", "*") self:AddTransition("*", "EscortShiftChange", "*") self:AddTransition("*", "AwacsShiftChange", "*") self:AddTransition("*", "FlightOnMission", "*") @@ -1354,6 +1381,142 @@ end -- Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- [User] Set the tactical information option, create 10 radio channels groups can subscribe and get Bogey Dope on a specific frequency automatically. +-- @param #AWACS self +-- @param #number BaseFreq Base Frequency to use, defaults to 130. +-- @param #number Increase Increase to use, defaults to 0.5, thus channels created are 130, 130.5, 131 .. etc. +-- @param #number Modulation Modulation to use, defaults to radio.modulation.AM. +-- @param #number Interval Seconds between each update call. +-- @param #number Number Number of Frequencies to create, can be 1..10. +-- @return #AWACS self +function AWACS:SetTacticalRadios(BaseFreq,Increase,Modulation,Interval,Number) + self:T(self.lid.."SetTacticalRadios") + self.TacticalMenu = true + self.TacticalBaseFreq = BaseFreq or 130 + self.TacticalIncrFreq = Increase or 0.5 + self.TacticalModulation = Modulation or radio.modulation.AM + self.TacticalInterval = Interval or 120 + local number = Number or 10 + if number < 1 then number = 1 end + if number > 10 then number = 10 end + for i=1,number do + local freq = self.TacticalBaseFreq + ((i-1)*self.TacticalIncrFreq) + self.TacticalFrequencies[freq] = freq + end + if self.AwacsSRS then + self.TacticalSRS = MSRS:New(self.PathToSRS,self.TacticalBaseFreq,self.TacticalModulation,self.Volume) + self.TacticalSRS:SetCoalition(self.coalition) + self.TacticalSRS:SetGender(self.Gender) + self.TacticalSRS:SetCulture(self.Culture) + self.TacticalSRS:SetVoice(self.Voice) + self.TacticalSRS:SetPort(self.Port) + self.TacticalSRS:SetLabel("AWACS") + if self.PathToGoogleKey then + self.TacticalSRS:SetGoogle(self.PathToGoogleKey) + end + self.TacticalSRSQ = MSRSQUEUE:New("Tactical AWACS") + end + return self +end + +--- TODO +-- [Internal] _RefreshMenuNonSubscribed +-- @param #AWACS self +-- @return #AWACS self +function AWACS:_RefreshMenuNonSubscribed() + self:T(self.lid.."_RefreshMenuNonSubscribed") + local aliveset = self.clientset:GetAliveSet() + + for _,_group in pairs(aliveset) do + -- go through set and re-build the sub-menu + local grp = _group -- Wrapper.Client#CLIENT + local Group = grp:GetGroup() + local gname = nil + if Group and Group:IsAlive() then + gname = Group:GetName() + self:T(gname) + end + local menustr = self.clientmenus:ReadByID(gname) + local menu = menustr.tactical -- Core.Menu#MENU_GROUP + if not self.TacticalSubscribers[gname] and menu then + menu:RemoveSubMenus() + for _,_freq in UTILS.spairs(self.TacticalFrequencies) do + local modu = UTILS.GetModulationName(self.TacticalModulation) + local text = string.format("Subscribe to %.3f %s",_freq,modu) + local entry = MENU_GROUP_COMMAND:New(Group,text,menu,self._SubScribeTactRadio,self,Group,_freq) + end + end + end + return self +end + +--- [Internal] _UnsubScribeTactRadio +-- @param #AWACS self +-- @param Wrapper.Group#GROUP Group +-- @return #AWACS self +function AWACS:_UnsubScribeTactRadio(Group) + self:T(self.lid.."_UnsubScribeTactRadio") + local text = "" + local textScreen = "" + local GID, Outcome = self:_GetManagedGrpID(Group) + local gcallsign = self:_GetCallSign(Group,GID) or "Ghost 1" + local gname = Group:GetName() or "unknown" + + if Outcome and self.TacticalSubscribers[gname] then + -- Pilot is checked in + local Freq = self.TacticalSubscribers[gname] + self.TacticalFrequencies[Freq] = Freq + self.TacticalSubscribers[gname] = nil + local modu = self.TacticalModulation == 0 and "AM" or "FM" + text = string.format("%s, %s, switch back to AWACS main frequency!",gcallsign,self.callsigntxt) + self:_NewRadioEntry(text,text,GID,true,true,true,false,true) + self:_RefreshMenuNonSubscribed() + elseif self.AwacsFG then + -- no, unknown + local nocheckin = self.gettext:GetEntry("NOTCHECKEDIN",self.locale) + text = string.format(nocheckin,self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) + self:_NewRadioEntry(text,text,GID,Outcome,true,true,false) + end + return self +end + +--- [Internal] _SubScribeTactRadio +-- @param #AWACS self +-- @param Wrapper.Group#GROUP Group +-- @param #number Frequency +-- @return #AWACS self +function AWACS:_SubScribeTactRadio(Group,Frequency) + self:T(self.lid.."_SubScribeTactRadio") + local text = "" + local textScreen = "" + local GID, Outcome = self:_GetManagedGrpID(Group) + local gcallsign = self:_GetCallSign(Group,GID) or "Ghost 1" + local gname = Group:GetName() or "unknown" + + if Outcome then + -- Pilot is checked in + self.TacticalSubscribers[gname] = Frequency + self.TacticalFrequencies[Frequency] = nil + local modu = self.TacticalModulation == 0 and "AM" or "FM" + text = string.format("%s, %s, switch to %.3f %s for tactical information!",gcallsign,self.callsigntxt,Frequency,modu) + self:_NewRadioEntry(text,text,GID,true,true,true,false,true) + local menustr = self.clientmenus:ReadByID(gname) + local menu = menustr.tactical -- Core.Menu#MENU_GROUP + if menu then + menu:RemoveSubMenus() + local text = string.format("Unsubscribe %.3f %s",Frequency,modu) + local entry = MENU_GROUP_COMMAND:New(Group,text,menu,self._UnsubScribeTactRadio,self,Group) + end + elseif self.AwacsFG then + -- no, unknown + local nocheckin = self.gettext:GetEntry("NOTCHECKEDIN",self.locale) + text = string.format(nocheckin,self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) + self:_NewRadioEntry(text,text,GID,Outcome,true,true,false) + end + + return self +end + --- [Internal] Init localization -- @param #AWACS self -- @return #AWACS self @@ -1426,8 +1589,9 @@ end -- @param #boolean IsNew New -- @param #boolean FromAI From AI -- @param #boolean IsPrio Priority entry +-- @param #boolean Tactical Is for tactical info -- @return #AWACS self -function AWACS:_NewRadioEntry(TextTTS, TextScreen,GID,IsGroup,ToScreen,IsNew,FromAI,IsPrio) +function AWACS:_NewRadioEntry(TextTTS, TextScreen,GID,IsGroup,ToScreen,IsNew,FromAI,IsPrio,Tactical) self:T(self.lid.."_NewRadioEntry") local RadioEntry = {} -- #AWACS.RadioEntry RadioEntry.IsNew = IsNew @@ -1438,7 +1602,9 @@ function AWACS:_NewRadioEntry(TextTTS, TextScreen,GID,IsGroup,ToScreen,IsNew,Fro RadioEntry.Duration = STTS.getSpeechTime(TextTTS,0.95,false) or 8 RadioEntry.FromAI = FromAI RadioEntry.IsGroup = IsGroup - if IsPrio then + if Tactical then + self.TacticalQueue:Push(RadioEntry) + elseif IsPrio then self.PrioRadioQueue:Push(RadioEntry) else self.RadioQueue:Push(RadioEntry) @@ -2689,8 +2855,9 @@ end -- @param #AWACS self -- @param #string Callsign Callsign to address -- @param #number GID GroupID for comms +-- @param #boolean Tactical Is for tactical info -- @return #AWACS self -function AWACS:_CreateBogeyDope(Callsign,GID) +function AWACS:_CreateBogeyDope(Callsign,GID,Tactical) self:T(self.lid.."_CreateBogeyDope for "..Callsign.." GID "..GID) local managedgroup = self.ManagedGrps[GID] -- #AWACS.ManagedGroup @@ -2716,7 +2883,7 @@ function AWACS:_CreateBogeyDope(Callsign,GID) local tag = contact.TargetGroupNaming local reportingname = contact.ReportingName -- DONE - add tag - self:_AnnounceContact(contact,false,group,true,tag,false,reportingname) + self:_AnnounceContact(contact,false,group,true,tag,false,reportingname,Tactical) end end @@ -2841,8 +3008,9 @@ end --- [Internal] AWACS Menu for Bogey Dope -- @param #AWACS self -- @param Wrapper.Group#GROUP Group Group to use +-- @param #boolean Tactical Check for tactical info -- @return #AWACS self -function AWACS:_BogeyDope(Group) +function AWACS:_BogeyDope(Group,Tactical) self:T(self.lid.."_BogeyDope") local text = "" local textScreen = "" @@ -2853,8 +3021,7 @@ function AWACS:_BogeyDope(Group) -- no intel yet! local clean = self.gettext:GetEntry("CLEAN",self.locale) text = string.format(clean,self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) - self:_NewRadioEntry(text,text,0,false,true,true,false,true) - + self:_NewRadioEntry(text,text,0,false,true,true,false,true,Tactical) return self end @@ -2896,7 +3063,7 @@ function AWACS:_BogeyDope(Group) local clean = self.gettext:GetEntry("CLEAN",self.locale) text = string.format(clean,self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) - self:_NewRadioEntry(text,textScreen,GID,Outcome,Outcome,true,false,true) + self:_NewRadioEntry(text,textScreen,GID,Outcome,Outcome,true,false,true,Tactical) else @@ -2915,9 +3082,9 @@ function AWACS:_BogeyDope(Group) textScreen = string.format("%s%d %s.\n",textScreen,contactsAO,groupstxt) end - self:_NewRadioEntry(text,textScreen,GID,Outcome,true,true,false,true) + self:_NewRadioEntry(text,textScreen,GID,Outcome,true,true,false,true,Tactical) - self:_CreateBogeyDope(self:_GetCallSign(Group,GID) or "Ghost 1",GID) + self:_CreateBogeyDope(self:_GetCallSign(Group,GID) or "Ghost 1",GID,Tactical) end end @@ -2925,7 +3092,7 @@ function AWACS:_BogeyDope(Group) -- no, unknown local nocheckin = self.gettext:GetEntry("NOTCHECKEDIN",self.locale) text = string.format(nocheckin,self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt) - self:_NewRadioEntry(text,text,GID,Outcome,true,true,false) + self:_NewRadioEntry(text,text,GID,Outcome,true,true,false,Tactical) end return self @@ -3614,6 +3781,22 @@ function AWACS:_SetClientMenus() local friendly = MENU_GROUP_COMMAND:New(cgrp,"Friendly",vid,self._VID,self,cgrp,AWACS.IFF.FRIENDLY) end + local tactical + if self.TacticalMenu then + tactical = MENU_GROUP:New(cgrp,"Tactical Radio",basemenu) + if self.TacticalSubscribers[cgrpname] then + -- unsubscribe + local entry = MENU_GROUP_COMMAND:New(cgrp,"Unsubscribe",tactical,self._UnsubScribeTactRadio,self,cgrp) + else + -- subscribe + for _,_freq in UTILS.spairs(self.TacticalFrequencies) do + local modu = UTILS.GetModulationName(self.TacticalModulation) + local text = string.format("Subscribe to %.3f %s",_freq,modu) + local entry = MENU_GROUP_COMMAND:New(cgrp,text,tactical,self._SubScribeTactRadio,self,cgrp,_freq) + end + end + end + local ainfo = MENU_GROUP_COMMAND:New(cgrp,"Awacs Info",basemenu,self._ShowAwacsInfo,self,cgrp) local checkout = MENU_GROUP_COMMAND:New(cgrp,"Check Out",basemenu,self._CheckOut,self,cgrp) @@ -3631,6 +3814,7 @@ function AWACS:_SetClientMenus() unable = unable, abort = abort, commit=commit, + tactical=tactical, } self.clientmenus:PullByID(cgrpname) self.clientmenus:Push(menus,cgrpname) @@ -4812,8 +4996,9 @@ end -- @param #string Tag Tag name for this contact. Alpha, Brave, Charlie ... -- @param #boolean IsPopup This is a pop-up group -- @param #string ReportingName The NATO code reporting name for the contact, e.g. "Foxbat". "Bogey" if unknown. +-- @param #boolean Tactical -- @return #AWACS self -function AWACS:_AnnounceContact(Contact,IsNew,Group,IsBogeyDope,Tag,IsPopup,ReportingName) +function AWACS:_AnnounceContact(Contact,IsNew,Group,IsBogeyDope,Tag,IsPopup,ReportingName,Tactical) self:T(self.lid.."_AnnounceContact") -- do we have a group to talk to? local tag = "" @@ -4940,7 +5125,7 @@ function AWACS:_AnnounceContact(Contact,IsNew,Group,IsBogeyDope,Tag,IsPopup,Repo BRAText = string.gsub(BRAText,"BRA","brah") local prio = IsNew or IsBogeyDope - self:_NewRadioEntry(BRAText,TextScreen,GID,isGroup,true,IsNew,false,prio) + self:_NewRadioEntry(BRAText,TextScreen,GID,isGroup,true,IsNew,false,prio,Tactical) return self end @@ -5328,6 +5513,15 @@ function AWACS:_MergedCall(GID) local merge = self.gettext:GetEntry("MERGED",self.locale) local text = string.format("%s. %s. %s.",self.callsigntxt,pilotcallsign,merge) self:_NewRadioEntry(text,text,GID,true,self.debug,true,false,true) + if GID and GID ~= 0 then + local managedgroup = self.ManagedGrps[GID] -- #AWACS.ManagedGroup + if managedgroup and managedgroup.Group and managedgroup.Group:IsAlive() then + local name = managedgroup.GroupName + if self.TacticalSubscribers[name] then + self:_NewRadioEntry(text,text,GID,true,self.debug,true,false,true,true) + end + end + end return self end @@ -5703,6 +5897,10 @@ function AWACS:onafterStart(From, Event, To) self:__Started(-5) end + if self.TacticalMenu then + self:__CheckTacticalQueue(55) + end + self:__Status(-30) return self end @@ -6316,6 +6514,77 @@ function AWACS:onafterLostCluster(From,Event,To,Cluster,Mission) return self end +--- [Internal] onafterCheckTacticalQueue +-- @param #AWACS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #AWACS self +function AWACS:onafterCheckTacticalQueue(From,Event,To) + self:T({From, Event, To}) + -- do we have messages queued? + + if self.clientset:CountAlive() == 0 then + self:T(self.lid.."No player connected.") + self:__CheckTacticalQueue(-5) + return self + end + + for _name,_freq in pairs(self.TacticalSubscribers) do + local Group = nil + if _name then + Group = GROUP:FindByName(_name) + end + if Group and Group:IsAlive() then + self:_BogeyDope(Group,true) + end + end + + if (self.TacticalQueue:IsNotEmpty()) then + + while self.TacticalQueue:Count() > 0 do + + local RadioEntry = self.TacticalQueue:Pull() -- #AWACS.RadioEntry + self:T({RadioEntry}) + local frequency = self.TacticalBaseFreq + if RadioEntry.GroupID and RadioEntry.GroupID ~= 0 then + local managedgroup = self.ManagedGrps[RadioEntry.GroupID] -- #AWACS.ManagedGroup + if managedgroup and managedgroup.Group and managedgroup.Group:IsAlive() then + local name = managedgroup.GroupName + frequency = self.TacticalSubscribers[name] + end + end + -- AI AWACS Speaking + local gtext = RadioEntry.TextTTS + if self.PathToGoogleKey then + gtext = string.format("%s",gtext) + end + self.TacticalSRSQ:NewTransmission(gtext,nil,self.TacticalSRS,nil,0.5,nil,nil,nil,frequency,self.TacticalModulation,nil,nil,nil,nil,nil) + + self:T(RadioEntry.TextTTS) + + if RadioEntry.ToScreen and RadioEntry.TextScreen and (not self.SuppressScreenOutput) then + if RadioEntry.GroupID and RadioEntry.GroupID ~= 0 then + local managedgroup = self.ManagedGrps[RadioEntry.GroupID] -- #AWACS.ManagedGroup + if managedgroup and managedgroup.Group and managedgroup.Group:IsAlive() then + MESSAGE:New(RadioEntry.TextScreen,20,"AWACS"):ToGroup(managedgroup.Group) + self:T(RadioEntry.TextScreen) + end + else + MESSAGE:New(RadioEntry.TextScreen,20,"AWACS"):ToCoalition(self.coalition) + end + end + end + + end -- end while + + if self:Is("Running") then + self:__CheckTacticalQueue(-self.TacticalInterval) + end + return self +end + + --- [Internal] onafterCheckRadioQueue -- @param #AWACS self -- @param #string From From 63a524f25cb60dbd98bf93feb82ce93115ec923c Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 26 Jul 2023 07:49:35 +0200 Subject: [PATCH 05/22] #AWACS * Remove tactical subscription when client logs out or dies --- Moose Development/Moose/Ops/Awacs.lua | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index c051869b6..3c04848a3 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -507,7 +507,7 @@ do -- @field #AWACS AWACS = { ClassName = "AWACS", -- #string - version = "0.2.56", -- #string + version = "0.2.57", -- #string lid = "", -- #string coalition = coalition.side.BLUE, -- #number coalitiontxt = "blue", -- #string @@ -1784,7 +1784,7 @@ function AWACS:_EventHandler(EventData) if WeaponDesc.category == 1 then Type = "Missile" -- AAM - local guidance = WeaponDesc.guidance -- IR=2, Radar Active=3, Radar Semi Active=4, Radar Passive = 5 + local guidance = WeaponDesc.guidance or 4 -- IR=2, Radar Active=3, Radar Semi Active=4, Radar Passive = 5 if guidance == 2 then warndist = 10 elseif guidance == 3 then @@ -3695,11 +3695,17 @@ function AWACS:_CheckOut(Group,GID,dead) local managedgroup = self.ManagedGrps[GID] -- #AWACS.ManagedGroup local Stack = managedgroup.AnchorStackNo local Angels = managedgroup.AnchorStackAngels + local GroupName = managedgroup.GroupName -- remove menus if managedgroup.IsPlayer then - if self.clientmenus:HasUniqueID(managedgroup.GroupName) then - local menus = self.clientmenus:PullByID(managedgroup.GroupName) --#AWACS.MenuStructure + if self.clientmenus:HasUniqueID(GroupName) then + local menus = self.clientmenus:PullByID(GroupName) --#AWACS.MenuStructure menus.basemenu:Remove() + if self.TacticalSubscribers[GroupName] then + local Freq = self.TacticalSubscribers[GroupName] + self.TacticalFrequencies[Freq] = Freq + self.TacticalSubscribers[GroupName] = nil + end end end -- delete open tasks From 065717b2f5e75f77285e0c1e757b7900bb4acadf Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Wed, 26 Jul 2023 15:36:09 +0200 Subject: [PATCH 06/22] Update FlightGroup.lua coord might be nil in GetParking(airbase) --- Moose Development/Moose/Ops/FlightGroup.lua | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index 0ae8dc116..e4554330e 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -4176,11 +4176,13 @@ function FLIGHTGROUP:GetClosestAirbase() local coord=group:GetCoordinate() local coalition=self:GetCoalition() - - local airbase=coord:GetClosestAirbase() --(nil, coalition) - - return airbase - + + if coord then + local airbase=coord:GetClosestAirbase() --(nil, coalition) + return airbase + else + return nil + end else return nil From 8bf56b8b1a1977c2112627388e4d4fdbcf2c7585 Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 26 Jul 2023 16:36:44 +0200 Subject: [PATCH 07/22] Update Airboss.lua - Fixed AI handling (got lost in some merge with develop branch and was using FLIGHTGROUPS) --- Moose Development/Moose/Ops/Airboss.lua | 61 ++++++++++++++++++++----- 1 file changed, 49 insertions(+), 12 deletions(-) diff --git a/Moose Development/Moose/Ops/Airboss.lua b/Moose Development/Moose/Ops/Airboss.lua index 6a0a032d5..2a17e4c13 100644 --- a/Moose Development/Moose/Ops/Airboss.lua +++ b/Moose Development/Moose/Ops/Airboss.lua @@ -5822,27 +5822,64 @@ function AIRBOSS:_ScanCarrierZone() -- Get aircraft type name. local actype = group:GetTypeName() - -- Create a new flight group if knownflight then + -- Debug output. + self:T2(self.lid..string.format("Known flight group %s of type %s in CCA.", groupname, actype)) + -- Check if flight is AI and if we want to handle it at all. - if knownflight.ai and knownflight.flag == -100 and self.handleai and false then --Disabled AI handling because of incorrect OPSGROUP reference! + if knownflight.ai and self.handleai then - local putintomarshal = false + -- Defines if AI group should be handled by the airboss. + local iscarriersquad=true - -- Get flight group. - local flight = _DATABASE:GetOpsGroup( groupname ) - - if flight and flight:IsInbound() and flight.destbase:GetName() == self.carrier:GetName() then - if flight.ishelo then + -- Check if AI group is part of the group set if a set was defined. + if self.squadsetAI then + local group=self.squadsetAI:FindGroup(groupname) + if group then + iscarriersquad=true else - putintomarshal = true + iscarriersquad=false end - flight.airboss = self end - -- Send AI flight to marshal stack. - if putintomarshal then + -- Check if group was explicitly excluded. + if self.excludesetAI then + local group=self.excludesetAI:FindGroup(groupname) + if group then + iscarriersquad=false + end + + end + + + -- Get distance to carrier. + local dist=knownflight.group:GetCoordinate():Get2DDistance(self:GetCoordinate()) + + -- Close in distance. Is >0 if AC comes closer wrt to first detected distance d0. + local closein=knownflight.dist0-dist + + -- Debug info. + self:T3(self.lid..string.format("Known AI flight group %s closed in by %.1f NM", knownflight.groupname, UTILS.MetersToNM(closein))) + + -- Is this group the tanker? + local istanker=self.tanker and self.tanker.tanker:GetName()==groupname + + -- Is this group the AWACS? + local isawacs=self.awacs and self.awacs.tanker:GetName()==groupname + + -- Send tanker to marshal stack? + local tanker2marshal = istanker and self.tanker:IsReturning() and self.tanker.airbase:GetName()==self.airbase:GetName() and knownflight.flag==-100 and self.tanker.recovery==true + + -- Send AWACS to marhsal stack? + local awacs2marshal = isawacs and self.awacs:IsReturning() and self.awacs.airbase:GetName()==self.airbase:GetName() and knownflight.flag==-100 and self.awacs.recovery==true + + -- Put flight into Marshal. + local putintomarshal=closein>UTILS.NMToMeters(5) and knownflight.flag==-100 and iscarriersquad and istanker==false and isawacs==false + + -- Send AI flight to marshal stack if group closes in more than 5 and has initial flag value. + if putintomarshal or tanker2marshal or awacs2marshal then + -- Get the next free stack for current recovery case. local stack = self:_GetFreeStack( knownflight.ai ) From 80e9cb692cb758373ca551b7aa776e491e627f07 Mon Sep 17 00:00:00 2001 From: phr0gz Date: Sat, 29 Jul 2023 13:37:07 +0200 Subject: [PATCH 08/22] Add FSM OnAfter and OnBefore (#1974) Add FSM Function OnAfter and OnBefore in FlightGroup. --- Moose Development/Moose/Ops/FlightGroup.lua | 317 +++++++++++++++++++- 1 file changed, 316 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index e4554330e..46cd5f2d6 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -325,7 +325,322 @@ function FLIGHTGROUP:New(group) -- @param #FLIGHTGROUP self -- @param #number delay Delay in seconds. - -- TODO: Add pseudo functions. + --- FSM Function OnAfterElementSpawned. + -- @function [parent=#FLIGHTGROUP] OnAfterElementSpawned + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element. + + --- FSM Function OnAfterElementParking. + -- @function [parent=#FLIGHTGROUP] OnAfterElementParking + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element. + -- @param Wrapper.Airbase#AIRBASE.ParkingSpot Spot Parking Spot. + + --- FSM Function OnAfterElementEngineOn. + -- @function [parent=#FLIGHTGROUP] OnAfterElementEngineOn + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element. + + --- FSM Function OnAfterElementTaxiing. + -- @function [parent=#FLIGHTGROUP] OnAfterElementTaxiing + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element. + + --- FSM Function OnAfterElementTakeoff. + -- @function [parent=#FLIGHTGROUP] OnAfterElementTakeoff + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element. + -- @param Wrapper.Airbase#AIRBASE airbase The airbase if applicable or nil. + + --- FSM Function OnAfterElementAirborne. + -- @function [parent=#FLIGHTGROUP] OnAfterElementAirborne + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element. + + --- FSM Function OnAfterElementLanded. + -- @function [parent=#FLIGHTGROUP] OnAfterElementLanded + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element. + -- @param Wrapper.Airbase#AIRBASE airbase The airbase if applicable or nil. + + --- FSM Function OnAfterElementArrived. + -- @function [parent=#FLIGHTGROUP] OnAfterElementArrived + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element. + -- @param Wrapper.Airbase#AIRBASE airbase The airbase, where the element arrived. + -- @param Wrapper.Airbase#AIRBASE.ParkingSpot Parking The Parking spot the element has. + + --- FSM Function OnAfterElementDestroyed. + -- @function [parent=#FLIGHTGROUP] OnAfterElementDestroyed + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element. + + --- FSM Function OnAfterElementDead. + -- @function [parent=#FLIGHTGROUP] OnAfterElementDead + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element. + + --- FSM Function OnAfterSpawned. + -- @function [parent=#FLIGHTGROUP] OnAfterSpawned + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterParking. + -- @function [parent=#FLIGHTGROUP] OnAfterParking + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterTaxiing. + -- @function [parent=#FLIGHTGROUP] OnAfterTaxiing + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterTakeoff. + -- @function [parent=#FLIGHTGROUP] OnAfterTakeoff + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterAirborne. + -- @function [parent=#FLIGHTGROUP] OnAfterAirborne + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterCruise. + -- @function [parent=#FLIGHTGROUP] OnAfterCruise + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterLanding. + -- @function [parent=#FLIGHTGROUP] OnAfterLanding + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterLanded. + -- @function [parent=#FLIGHTGROUP] OnAfterLanded + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Wrapper.Airbase#AIRBASE airbase The airbase the flight landed. + + --- FSM Function OnAfterLandedAt. + -- @function [parent=#FLIGHTGROUP] OnAfterLandedAt + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterArrived. + -- @function [parent=#FLIGHTGROUP] OnAfterArrived + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterDead. + -- @function [parent=#FLIGHTGROUP] OnAfterDead + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterUpdateRoute. + -- @function [parent=#FLIGHTGROUP] OnAfterUpdateRoute + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param #number n Next waypoint index. Default is the one coming after that one that has been passed last. + -- @param #number N Waypoint Max waypoint index to be included in the route. Default is the final waypoint. + + --- FSM Function OnAfterOutOfMissilesAA. + -- @function [parent=#FLIGHTGROUP] OnAfterOutOfMissilesAA + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterOutOfMissilesAG. + -- @function [parent=#FLIGHTGROUP] OnAfterOutOfMissilesAG + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterRTB. + -- @function [parent=#FLIGHTGROUP] OnAfterRTB + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Wrapper.Airbase#AIRBASE airbase The airbase to hold at. + -- @param #number SpeedTo Speed used for traveling from current position to holding point in knots. Default 75% of max speed. + -- @param #number SpeedHold Holding speed in knots. Default 250 kts. + -- @param #number SpeedLand Landing speed in knots. Default 170 kts. + + --- FSM Function OnAfterLandAtAirbase. + -- @function [parent=#FLIGHTGROUP] OnAfterLandAtAirbase + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Wrapper.Airbase#AIRBASE airbase The airbase to hold at. + + --- FSM Function OnAfterWait. + -- @function [parent=#FLIGHTGROUP] OnAfterWait + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param #number Duration Duration how long the group will be waiting in seconds. Default `nil` (=forever). + -- @param #number Altitude Altitude in feet. Default 10,000 ft for airplanes and 1,000 feet for helos. + -- @param #number Speed Speed in knots. Default 250 kts for airplanes and 20 kts for helos. + + --- FSM Function OnAfterRefuel. + -- @function [parent=#FLIGHTGROUP] OnAfterRefuel + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Core.Point#COORDINATE Coordinate The coordinate. + + --- FSM Function OnAfterRefueled. + -- @function [parent=#FLIGHTGROUP] OnAfterRefueled + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterDisengage. + -- @function [parent=#FLIGHTGROUP] OnAfterDisengage + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Core.Set#SET_UNIT TargetUnitSet + + --- FSM Function OnAfterEngageTarget. + -- @function [parent=#FLIGHTGROUP] OnAfterEngageTarget + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param #table Target Target object. Can be a UNIT, STATIC, GROUP, SET_UNIT or SET_GROUP object. + + --- FSM Function OnAfterLandAt. + -- @function [parent=#FLIGHTGROUP] OnAfterLandAt + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Core.Set#SET_UNIT TargetUnitSet + + --- FSM Function OnAfterFuelLow. + -- @function [parent=#FLIGHTGROUP] OnAfterFuelLow + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterFuelCritical. + -- @function [parent=#FLIGHTGROUP] OnAfterFuelCritical + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnBeforeUpdateRoute. + -- @function [parent=#FLIGHTGROUP] OnBeforeUpdateRoute + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param #number n Next waypoint index. Default is the one coming after that one that has been passed last. + -- @param #number N Waypoint Max waypoint index to be included in the route. Default is the final waypoint. + -- @return #boolean Transision allowed? + + --- FSM Function OnBeforeRTB. + -- @function [parent=#FLIGHTGROUP] OnBeforeRTB + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Wrapper.Airbase#AIRBASE airbase The airbase to hold at. + -- @param #number SpeedTo Speed used for travelling from current position to holding point in knots. + -- @param #number SpeedHold Holding speed in knots. + + --- FSM Function OnBeforeLandAtAirbase. + -- @function [parent=#FLIGHTGROUP] OnBeforeLandAtAirbase + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Wrapper.Airbase#AIRBASE airbase The airbase to hold at. + + --- FSM Function OnBeforeWait. + -- @function [parent=#FLIGHTGROUP] OnBeforeWait + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param #number Duration Duration how long the group will be waiting in seconds. Default `nil` (=forever). + -- @param #number Altitude Altitude in feet. Default 10,000 ft for airplanes and 1,000 feet for helos. + -- @param #number Speed Speed in knots. Default 250 kts for airplanes and 20 kts for helos. + + --- FSM Function OnBeforeLandAt. + -- @function [parent=#FLIGHTGROUP] OnBeforeLandAt + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Core.Point#COORDINATE Coordinate The coordinate where to land. Default is current position. + -- @param #number Duration The duration in seconds to remain on ground. Default 600 sec (10 min). + + -- TODO: Add pseudo functions ? Normally done, but should be double check -- Handle events: self:HandleEvent(EVENTS.Birth, self.OnEventBirth) From cbd371b40f89b150dec9e2922eae965810b47ea6 Mon Sep 17 00:00:00 2001 From: phr0gz Date: Sat, 29 Jul 2023 15:44:13 +0200 Subject: [PATCH 09/22] Chief zone removal Tanker, AWACS (#1975) Add the ability to remove a zone for Tanker and AWACS. --- Moose Development/Moose/Ops/Chief.lua | 21 ++++++++++++ Moose Development/Moose/Ops/Commander.lua | 40 +++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/Moose Development/Moose/Ops/Chief.lua b/Moose Development/Moose/Ops/Chief.lua index 0272ec659..503cbfc2b 100644 --- a/Moose Development/Moose/Ops/Chief.lua +++ b/Moose Development/Moose/Ops/Chief.lua @@ -1501,6 +1501,17 @@ function CHIEF:AddAwacsZone(Zone, Altitude, Speed, Heading, Leg) return zone end +--- Remove a AWACS zone. +-- @param #CHIEF self +-- @param Core.Zone#ZONE Zone Zone, where the flight orbits. +function CHIEF:RemoveAwacsZone(Zone) + + -- Hand over to commander. + local zone=self.commander:RemoveAwacsZone(Zone) + + return zone +end + --- Add a refuelling tanker zone. -- @param #CHIEF self -- @param Core.Zone#ZONE Zone Zone. @@ -1518,6 +1529,16 @@ function CHIEF:AddTankerZone(Zone, Altitude, Speed, Heading, Leg, RefuelSystem) return zone end +--- Remove a refuelling tanker zone. +-- @param #CHIEF self +-- @param Core.Zone#ZONE Zone Zone, where the flight orbits. +function CHIEF:RemoveTankerZone(Zone) + + -- Hand over to commander. + local zone=self.commander:RemoveTankerZone(Zone) + + return zone +end --- Set border zone set, defining your territory. -- diff --git a/Moose Development/Moose/Ops/Commander.lua b/Moose Development/Moose/Ops/Commander.lua index 0af40ed2d..b0bd1673e 100644 --- a/Moose Development/Moose/Ops/Commander.lua +++ b/Moose Development/Moose/Ops/Commander.lua @@ -735,6 +735,26 @@ function COMMANDER:AddAwacsZone(Zone, Altitude, Speed, Heading, Leg) return awacszone end +--- Remove a AWACS zone. +-- @param #COMMANDER self +-- @param Core.Zone#ZONE Zone Zone, where the flight orbits. +function COMMANDER:RemoveAwacsZone(Zone) + + local awacszone={} --Ops.AirWing#AIRWING.PatrolZone + + awacszone.zone=Zone + for i,_awacszone in pairs(self.awacsZones) do + if _awacszone.zone == awacszone.zone then + if _awacszone.mission and _awacszone.mission:IsNotOver() then + _awacszone.mission:Cancel() + end + table.remove(self.awacsZones, i) + break + end + end + return awacszone +end + --- Add a refuelling tanker zone. -- @param #COMMANDER self -- @param Core.Zone#ZONE Zone Zone. @@ -762,6 +782,26 @@ function COMMANDER:AddTankerZone(Zone, Altitude, Speed, Heading, Leg, RefuelSyst return tankerzone end +--- Remove a refuelling tanker zone. +-- @param #COMMANDER self +-- @param Core.Zone#ZONE Zone Zone, where the flight orbits. +function COMMANDER:RemoveTankerZone(Zone) + + local tankerzone={} --Ops.AirWing#AIRWING.PatrolZone + + tankerzone.zone=Zone + for i,_tankerzone in pairs(self.tankerZones) do + if _tankerzone.zone == tankerzone.zone then + if _tankerzone.mission and _tankerzone.mission:IsNotOver() then + _tankerzone.mission:Cancel() + end + table.remove(self.tankerZones, i) + break + end + end + return tankerzone +end + --- Check if this mission is already in the queue. -- @param #COMMANDER self -- @param Ops.Auftrag#AUFTRAG Mission The mission. From 4ed2b0610d90e63fcf5f80557ceeabb28a3dd2ce Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 29 Jul 2023 16:44:25 +0200 Subject: [PATCH 10/22] #AWACS * ensure tactical frequencies are handed back #PlayerTask * Add'l check for stale tasks to rebuild menu --- Moose Development/Moose/Ops/Awacs.lua | 19 +++++++++++++++ Moose Development/Moose/Ops/PlayerTask.lua | 28 ++++++++++++++++++---- 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index 3c04848a3..d4e58c12c 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -1517,6 +1517,23 @@ function AWACS:_SubScribeTactRadio(Group,Frequency) return self end +--- [Internal] _CheckSubscribers +-- @param #AWACS self +-- @return #AWACS self +function AWACS:_CheckSubscribers() + self:T(self.lid.."_InitLocalization") + + for _name,_freq in pairs(self.TacticalSubscribers or {}) do + local grp = GROUP:FindByName(_name) + if (not grp) or (not grp:IsAlive()) then + self.TacticalFrequencies[_freq] = _freq + self.TacticalSubscribers[_name] = nil + end + end + + return self +end + --- [Internal] Init localization -- @param #AWACS self -- @return #AWACS self @@ -6180,6 +6197,8 @@ function AWACS:onafterStatus(From, Event, To) self:_CheckMerges() + self:_CheckSubscribers() + local outcome, targets = self:_TargetSelectionProcess(true) self:_CheckTaskQueue() diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index d44e9c644..b589bdd63 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -98,7 +98,7 @@ PLAYERTASK = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASK.version="0.1.18" +PLAYERTASK.version="0.1.19" --- Generic task condition. -- @type PLAYERTASK.Condition @@ -2483,6 +2483,15 @@ function PLAYERTASKCONTROLLER:_CheckTaskQueue() self:T("*****Removing player " .. _id) self.TasksPerPlayer:PullByID(_id) end + local clients=task:GetClientObjects() + for _,client in pairs(clients) do + self:_RemoveMenuEntriesForTask(task,client) + --self:_SwitchMenuForClient(client,"Info") + end + for _,client in pairs(clients) do + -- self:_RemoveMenuEntriesForTask(Task,client) + self:_SwitchMenuForClient(client,"Info",5) + end -- Follow-up tasks? local nexttasks = {} if task.FinalState == "Success" then @@ -3594,28 +3603,28 @@ function PLAYERTASKCONTROLLER:_RemoveMenuEntriesForTask(Task,Client) if Task then if Task.UUIDS and self.JoinTaskMenuTemplate then --self:I("***** JoinTaskMenuTemplate") - UTILS.PrintTableToLog(Task.UUIDS) + --UTILS.PrintTableToLog(Task.UUIDS) local controller = self.JoinTaskMenuTemplate for _,_uuid in pairs(Task.UUIDS) do local Entry = controller:FindEntryByUUID(_uuid) if Entry then controller:DeleteF10Entry(Entry,Client) controller:DeleteGenericEntry(Entry) - UTILS.PrintTableToLog(controller.menutree) + --UTILS.PrintTableToLog(controller.menutree) end end end if Task.AUUIDS and self.ActiveTaskMenuTemplate then --self:I("***** ActiveTaskMenuTemplate") - UTILS.PrintTableToLog(Task.AUUIDS) + --UTILS.PrintTableToLog(Task.AUUIDS) for _,_uuid in pairs(Task.AUUIDS) do local controller = self.ActiveTaskMenuTemplate local Entry = controller:FindEntryByUUID(_uuid) if Entry then controller:DeleteF10Entry(Entry,Client) controller:DeleteGenericEntry(Entry) - UTILS.PrintTableToLog(controller.menutree) + --UTILS.PrintTableToLog(controller.menutree) end end end @@ -4210,6 +4219,15 @@ function PLAYERTASKCONTROLLER:onafterTaskFailed(From, Event, To, Task) taskname = string.format(failtxttts, self.MenuName or self.Name, Task.PlayerTaskNr, tostring(Task.TTSType)) self.SRSQueue:NewTransmission(taskname,nil,self.SRS,nil,2) end + local clients=Task:GetClientObjects() + for _,client in pairs(clients) do + self:_RemoveMenuEntriesForTask(Task,client) + --self:_SwitchMenuForClient(client,"Info") + end + for _,client in pairs(clients) do + -- self:_RemoveMenuEntriesForTask(Task,client) + self:_SwitchMenuForClient(client,"Info",5) + end return self end From f87a676e4bfb84757367655e8fd307c688fb5b32 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Sat, 29 Jul 2023 16:45:00 +0200 Subject: [PATCH 11/22] Update Scoring.lua (#1976) Add report Spaces --- Moose Development/Moose/Functional/Scoring.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Functional/Scoring.lua b/Moose Development/Moose/Functional/Scoring.lua index 3d0c56b9f..ec95dc8a3 100644 --- a/Moose Development/Moose/Functional/Scoring.lua +++ b/Moose Development/Moose/Functional/Scoring.lua @@ -1420,7 +1420,7 @@ function SCORING:ReportDetailedPlayerHits( PlayerName ) Penalty = Penalty + UnitData.Penalty PenaltyHit = UnitData.PenaltyHit end - local ScoreMessageHit = string.format( "%s:%d ", CategoryName, Score - Penalty ) + local ScoreMessageHit = string.format( "%s: %d ", CategoryName, Score - Penalty ) self:T( ScoreMessageHit ) ScoreMessageHits = ScoreMessageHits .. ScoreMessageHit PlayerScore = PlayerScore + Score @@ -1476,7 +1476,7 @@ function SCORING:ReportDetailedPlayerDestroys( PlayerName ) end end - local ScoreMessageDestroy = string.format( " %s:%d ", CategoryName, Score - Penalty ) + local ScoreMessageDestroy = string.format( " %s: %d ", CategoryName, Score - Penalty ) self:T( ScoreMessageDestroy ) ScoreMessageDestroys = ScoreMessageDestroys .. ScoreMessageDestroy From 65703d1092f679da072b47fa0711e4d51855a278 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 29 Jul 2023 17:19:51 +0200 Subject: [PATCH 12/22] Debugs --- Moose Development/Moose/Ops/Awacs.lua | 2 +- Moose Development/Moose/Ops/PlayerTask.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index d4e58c12c..5bff6cc86 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -1613,7 +1613,7 @@ function AWACS:_NewRadioEntry(TextTTS, TextScreen,GID,IsGroup,ToScreen,IsNew,Fro local RadioEntry = {} -- #AWACS.RadioEntry RadioEntry.IsNew = IsNew RadioEntry.TextTTS = TextTTS - RadioEntry.TextScreen = TextScreen + RadioEntry.TextScreen = TextScreen or TextTTS RadioEntry.GroupID = GID RadioEntry.ToScreen = ToScreen RadioEntry.Duration = STTS.getSpeechTime(TextTTS,0.95,false) or 8 diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index b589bdd63..848732a32 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -2177,7 +2177,7 @@ end -- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:_EventHandler(EventData) self:T(self.lid.."_EventHandler: "..EventData.id) - self:T(self.lid.."_EventHandler: "..EventData.IniPlayerName) + --self:T(self.lid.."_EventHandler: "..EventData.IniPlayerName) if EventData.id == EVENTS.PlayerLeaveUnit or EventData.id == EVENTS.Ejection or EventData.id == EVENTS.Crash or EventData.id == EVENTS.PilotDead then if EventData.IniPlayerName then self:T(self.lid.."Event for player: "..EventData.IniPlayerName) From 09d4e155ded1d79140d4ea97263f15ea5c3589c3 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Mon, 31 Jul 2023 14:24:47 +0200 Subject: [PATCH 13/22] Update PlayerTask.lua (#1977) Prevent Events being available before the menu structure has been created --- Moose Development/Moose/Ops/PlayerTask.lua | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 848732a32..cdc693a33 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -1656,13 +1656,6 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter) local starttime = math.random(5,10) self:__Status(starttime) - -- Player leaves - self:HandleEvent(EVENTS.PlayerLeaveUnit, self._EventHandler) - self:HandleEvent(EVENTS.Ejection, self._EventHandler) - self:HandleEvent(EVENTS.Crash, self._EventHandler) - self:HandleEvent(EVENTS.PilotDead, self._EventHandler) - self:HandleEvent(EVENTS.PlayerEnterAircraft, self._EventHandler) - self:I(self.lid..self.version.." Started.") return self @@ -4072,6 +4065,12 @@ function PLAYERTASKCONTROLLER:onafterStart(From, Event, To) self:T(self.lid.."onafterStart") self:_CreateJoinMenuTemplate() self:_CreateActiveTaskMenuTemplate() + -- Player Events + self:HandleEvent(EVENTS.PlayerLeaveUnit, self._EventHandler) + self:HandleEvent(EVENTS.Ejection, self._EventHandler) + self:HandleEvent(EVENTS.Crash, self._EventHandler) + self:HandleEvent(EVENTS.PilotDead, self._EventHandler) + self:HandleEvent(EVENTS.PlayerEnterAircraft, self._EventHandler) return self end From 56fcd8f37f839430acaed9aad85d37177349044a Mon Sep 17 00:00:00 2001 From: phr0gz Date: Tue, 1 Aug 2023 10:00:17 +0200 Subject: [PATCH 14/22] Add RemoveGciCapZone in Chief (#1973) This will add the ability to remove CGI CAP zone from chief. --- Moose Development/Moose/Ops/Chief.lua | 11 +++++++++++ Moose Development/Moose/Ops/Commander.lua | 20 ++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/Moose Development/Moose/Ops/Chief.lua b/Moose Development/Moose/Ops/Chief.lua index 503cbfc2b..77477e0cc 100644 --- a/Moose Development/Moose/Ops/Chief.lua +++ b/Moose Development/Moose/Ops/Chief.lua @@ -1485,6 +1485,17 @@ function CHIEF:AddGciCapZone(Zone, Altitude, Speed, Heading, Leg) return zone end +--- Remove a GCI CAP +-- @param #CHIEF self +-- @param Core.Zone#ZONE Zone Zone, where the flight orbits. +function CHIEF:RemoveGciCapZone(Zone) + + -- Hand over to commander. + local zone=self.commander:RemoveGciCapZone(Zone) + + return zone +end + --- Add an AWACS zone. -- @param #CHIEF self -- @param Core.Zone#ZONE Zone Zone. diff --git a/Moose Development/Moose/Ops/Commander.lua b/Moose Development/Moose/Ops/Commander.lua index b0bd1673e..378df6007 100644 --- a/Moose Development/Moose/Ops/Commander.lua +++ b/Moose Development/Moose/Ops/Commander.lua @@ -710,6 +710,26 @@ function COMMANDER:AddGciCapZone(Zone, Altitude, Speed, Heading, Leg) return patrolzone end +--- Remove a GCI CAP. +-- @param #COMMANDER self +-- @param Core.Zone#ZONE Zone Zone, where the flight orbits. +function COMMANDER:RemoveGciCapZone(Zone) + + local patrolzone={} --Ops.AirWing#AIRWING.PatrolZone + + patrolzone.zone=Zone + for i,_patrolzone in pairs(self.gcicapZones) do + if _patrolzone.zone == patrolzone.zone then + if _patrolzone.mission and _patrolzone.mission:IsNotOver() then + _patrolzone.mission:Cancel() + end + table.remove(self.gcicapZones, i) + break + end + end + return patrolzone +end + --- Add an AWACS zone. -- @param #COMMANDER self -- @param Core.Zone#ZONE Zone Zone. From 1fdc50b0dae7620fcfbe43ba8a548bc697786e87 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Tue, 1 Aug 2023 10:00:42 +0200 Subject: [PATCH 15/22] Update Scenery.lua (#1980) _id might be nil in :FindByZoneName() --- Moose Development/Moose/Wrapper/Scenery.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Wrapper/Scenery.lua b/Moose Development/Moose/Wrapper/Scenery.lua index 6a6f70bd9..24dc45fd6 100644 --- a/Moose Development/Moose/Wrapper/Scenery.lua +++ b/Moose Development/Moose/Wrapper/Scenery.lua @@ -178,7 +178,7 @@ function SCENERY:FindByZoneName( ZoneName ) zone = ZONE:FindByName(ZoneName) end local _id = zone:GetProperty('OBJECT ID') - BASE:T("Object ID ".._id) + --BASE:T("Object ID ".._id) if not _id then -- this zone has no object ID BASE:E("**** Zone without object ID: "..ZoneName.." | Type: "..tostring(zone.ClassName)) From 677a64c3dc0ea339f70feca7c27095879cf1bf79 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Tue, 1 Aug 2023 10:38:39 +0200 Subject: [PATCH 16/22] Update Scenery.lua (#1980) (#1981) _id might be nil in :FindByZoneName() --- Moose Development/Moose/Wrapper/Scenery.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Wrapper/Scenery.lua b/Moose Development/Moose/Wrapper/Scenery.lua index 6a6f70bd9..24dc45fd6 100644 --- a/Moose Development/Moose/Wrapper/Scenery.lua +++ b/Moose Development/Moose/Wrapper/Scenery.lua @@ -178,7 +178,7 @@ function SCENERY:FindByZoneName( ZoneName ) zone = ZONE:FindByName(ZoneName) end local _id = zone:GetProperty('OBJECT ID') - BASE:T("Object ID ".._id) + --BASE:T("Object ID ".._id) if not _id then -- this zone has no object ID BASE:E("**** Zone without object ID: "..ZoneName.." | Type: "..tostring(zone.ClassName)) From 7c4dd8160dd353bdffa6b8cdddfca06b1fa3b785 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Tue, 1 Aug 2023 11:53:00 +0200 Subject: [PATCH 17/22] Update Set.lua (#1982) Intellisense updates --- Moose Development/Moose/Core/Set.lua | 206 ++++++++++++++------------- 1 file changed, 110 insertions(+), 96 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 2ba9d95ac..e3cd263c4 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -46,8 +46,9 @@ -- @image Core_Sets.JPG do -- SET_BASE - - --- @type SET_BASE + + --- + -- @type SET_BASE -- @field #table Filter Table of filters. -- @field #table Set Table of objects. -- @field #table Index Table of indices. @@ -578,8 +579,8 @@ do -- SET_BASE end ----- Private method that registers all alive players in the mission. - ---- @param #SET_BASE self - ---- @return #SET_BASE self + -- @param #SET_BASE self + -- @return #SET_BASE self -- function SET_BASE:_RegisterPlayers() -- -- local CoalitionsData = { AlivePlayersRed = coalition.getPlayers( coalition.side.RED ), AlivePlayersBlue = coalition.getPlayers( coalition.side.BLUE ) } @@ -796,8 +797,8 @@ do -- SET_BASE ----- Iterate the SET_BASE and call an iterator function for each **alive** unit, providing the Unit and optional parameters. - ---- @param #SET_BASE self - ---- @param #function IteratorFunction The function that will be called when there is an alive unit in the SET_BASE. The function needs to accept a UNIT parameter. + -- @param #SET_BASE self + -- @param #function IteratorFunction The function that will be called when there is an alive unit in the SET_BASE. The function needs to accept a UNIT parameter. ---- @return #SET_BASE self -- function SET_BASE:ForEachDCSUnitAlive( IteratorFunction, ... ) -- self:F3( arg ) @@ -808,8 +809,8 @@ do -- SET_BASE -- end -- ----- Iterate the SET_BASE and call an iterator function for each **alive** player, providing the Unit of the player and optional parameters. - ---- @param #SET_BASE self - ---- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_BASE. The function needs to accept a UNIT parameter. + -- @param #SET_BASE self + -- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_BASE. The function needs to accept a UNIT parameter. ---- @return #SET_BASE self -- function SET_BASE:ForEachPlayer( IteratorFunction, ... ) -- self:F3( arg ) @@ -821,8 +822,8 @@ do -- SET_BASE -- -- ----- Iterate the SET_BASE and call an iterator function for each client, providing the Client to the function and optional parameters. - ---- @param #SET_BASE self - ---- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_BASE. The function needs to accept a CLIENT parameter. + -- @param #SET_BASE self + -- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_BASE. The function needs to accept a CLIENT parameter. ---- @return #SET_BASE self -- function SET_BASE:ForEachClient( IteratorFunction, ... ) -- self:F3( arg ) @@ -901,9 +902,12 @@ do -- SET_BASE end -do -- SET_GROUP +do - --- @type SET_GROUP #SET_GROUP + -- SET_GROUP + + --- + -- @type SET_GROUP #SET_GROUP -- @field Core.Timer#TIMER ZoneTimer -- @field #number ZoneTimerInterval -- @extends Core.Set#SET_BASE @@ -1568,7 +1572,7 @@ do -- SET_GROUP self:F2( arg ) self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Group#GROUP GroupObject function( ZoneObject, GroupObject ) if GroupObject:IsCompletelyInZone( ZoneObject ) then @@ -1590,7 +1594,7 @@ do -- SET_GROUP self:F2( arg ) self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Group#GROUP GroupObject function( ZoneObject, GroupObject ) if GroupObject:IsPartlyInZone( ZoneObject ) then @@ -1612,7 +1616,7 @@ do -- SET_GROUP self:F2( arg ) self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Group#GROUP GroupObject function( ZoneObject, GroupObject ) if GroupObject:IsNotInZone( ZoneObject ) then @@ -1659,7 +1663,7 @@ do -- SET_GROUP self:F2( arg ) self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Group#GROUP GroupObject function( ZoneObject, GroupObject ) if GroupObject:IsAnyInZone( ZoneObject ) then @@ -1857,8 +1861,8 @@ do -- SET_GROUP end ----- Iterate the SET_GROUP and call an iterator function for each **alive** player, providing the Group of the player and optional parameters. - ---- @param #SET_GROUP self - ---- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_GROUP. The function needs to accept a GROUP parameter. + -- @param #SET_GROUP self + -- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_GROUP. The function needs to accept a GROUP parameter. ---- @return #SET_GROUP self -- function SET_GROUP:ForEachPlayer( IteratorFunction, ... ) -- self:F2( arg ) @@ -1870,8 +1874,8 @@ do -- SET_GROUP -- -- ----- Iterate the SET_GROUP and call an iterator function for each client, providing the Client to the function and optional parameters. - ---- @param #SET_GROUP self - ---- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_GROUP. The function needs to accept a CLIENT parameter. + -- @param #SET_GROUP self + -- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_GROUP. The function needs to accept a CLIENT parameter. ---- @return #SET_GROUP self -- function SET_GROUP:ForEachClient( IteratorFunction, ... ) -- self:F2( arg ) @@ -2011,8 +2015,9 @@ do -- SET_GROUP end do -- SET_UNIT - - --- @type SET_UNIT + + --- + -- @type SET_UNIT -- @field Core.Timer#TIMER ZoneTimer -- @field #number ZoneTimerInterval -- @extends Core.Set#SET_BASE @@ -2672,7 +2677,7 @@ do -- SET_UNIT self:F2( arg ) self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Unit#UNIT UnitObject function( ZoneObject, UnitObject ) if UnitObject:IsInZone( ZoneObject ) then @@ -2694,7 +2699,7 @@ do -- SET_UNIT self:F2( arg ) self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Unit#UNIT UnitObject function( ZoneObject, UnitObject ) if UnitObject:IsNotInZone( ZoneObject ) then @@ -3011,8 +3016,8 @@ do -- SET_UNIT ----- Iterate the SET_UNIT and call an iterator function for each **alive** player, providing the Unit of the player and optional parameters. - ---- @param #SET_UNIT self - ---- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_UNIT. The function needs to accept a UNIT parameter. + -- @param #SET_UNIT self + -- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_UNIT. The function needs to accept a UNIT parameter. ---- @return #SET_UNIT self -- function SET_UNIT:ForEachPlayer( IteratorFunction, ... ) -- self:F2( arg ) @@ -3024,8 +3029,8 @@ do -- SET_UNIT -- -- ----- Iterate the SET_UNIT and call an iterator function for each client, providing the Client to the function and optional parameters. - ---- @param #SET_UNIT self - ---- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_UNIT. The function needs to accept a CLIENT parameter. + -- @param #SET_UNIT self + -- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_UNIT. The function needs to accept a CLIENT parameter. ---- @return #SET_UNIT self -- function SET_UNIT:ForEachClient( IteratorFunction, ... ) -- self:F2( arg ) @@ -3040,7 +3045,7 @@ do -- SET_UNIT -- @param Wrapper.Unit#UNIT MUnit -- @return #SET_UNIT self function SET_UNIT:IsIncludeObject( MUnit ) - self:F2( MUnit ) + self:F2( {MUnit} ) local MUnitInclude = false @@ -3191,8 +3196,9 @@ do -- SET_UNIT end do -- SET_STATIC - - --- @type SET_STATIC + + --- + -- @type SET_STATIC -- @extends Core.Set#SET_BASE --- Mission designers can use the SET_STATIC class to build sets of Statics belonging to certain: @@ -3612,7 +3618,7 @@ do -- SET_STATIC self:F2( arg ) self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Static#STATIC StaticObject function( ZoneObject, StaticObject ) if StaticObject:IsInZone( ZoneObject ) then @@ -3634,7 +3640,7 @@ do -- SET_STATIC self:F2( arg ) self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Static#STATIC StaticObject function( ZoneObject, StaticObject ) if StaticObject:IsNotInZone( ZoneObject ) then @@ -3945,8 +3951,9 @@ do -- SET_STATIC end do -- SET_CLIENT - - --- @type SET_CLIENT + + --- + -- @type SET_CLIENT -- @field Core.Timer#TIMER ZoneTimer -- @field #number ZoneTimerInterval -- @extends Core.Set#SET_BASE @@ -4008,8 +4015,8 @@ do -- SET_CLIENT Countries = nil, ClientPrefixes = nil, Zones = nil, - Playernames = nil, - Callsigns = nil, + Playernames = nil, + Callsigns = nil, }, FilterMeta = { Coalitions = { @@ -4059,7 +4066,7 @@ do -- SET_CLIENT --- Remove CLIENT(s) from SET_CLIENT. -- @param Core.Set#SET_CLIENT self - -- @param Wrapper.Client#CLIENT RemoveClientNames A single name or an array of CLIENT names. + -- @param Wrapper.Client#CLIENT RemoveClientNames A single object or an array of CLIENT objects. -- @return self function SET_CLIENT:RemoveClientsByName( RemoveClientNames ) @@ -4369,7 +4376,7 @@ do -- SET_CLIENT self:F2( arg ) self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Client#CLIENT ClientObject function( ZoneObject, ClientObject ) if ClientObject:IsInZone( ZoneObject ) then @@ -4391,7 +4398,7 @@ do -- SET_CLIENT self:F2( arg ) self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Client#CLIENT ClientObject function( ZoneObject, ClientObject ) if ClientObject:IsNotInZone( ZoneObject ) then @@ -4526,45 +4533,45 @@ do -- SET_CLIENT MClientInclude = MClientInclude and MClientPrefix end - if self.Filter.Zones then - local MClientZone = false - for ZoneName, Zone in pairs( self.Filter.Zones ) do - self:T3( "Zone:", ZoneName ) - local unit = MClient:GetClientGroupUnit() - if unit and unit:IsInZone(Zone) then - MClientZone = true - end - end - MClientInclude = MClientInclude and MClientZone - end - - if self.Filter.Playernames then - local MClientPlayername = false - local playername = MClient:GetPlayerName() or "Unknown" - --self:I(playername) - for _,_Playername in pairs(self.Filter.Playernames) do - if playername and string.find(playername,_Playername) then - MClientPlayername = true - end - end - self:T( { "Evaluated Playername", MClientPlayername } ) - MClientInclude = MClientInclude and MClientPlayername - end - - if self.Filter.Callsigns then - local MClientCallsigns = false - local callsign = MClient:GetCallsign() - --self:I(callsign) - for _,_Callsign in pairs(self.Filter.Callsigns) do - if callsign and string.find(callsign,_Callsign) then - MClientCallsigns = true - end - end - self:T( { "Evaluated Callsign", MClientCallsigns } ) - MClientInclude = MClientInclude and MClientCallsigns - end - - end + if self.Filter.Zones then + local MClientZone = false + for ZoneName, Zone in pairs( self.Filter.Zones ) do + self:T3( "Zone:", ZoneName ) + local unit = MClient:GetClientGroupUnit() + if unit and unit:IsInZone(Zone) then + MClientZone = true + end + end + MClientInclude = MClientInclude and MClientZone + end + + if self.Filter.Playernames then + local MClientPlayername = false + local playername = MClient:GetPlayerName() or "Unknown" + --self:I(playername) + for _,_Playername in pairs(self.Filter.Playernames) do + if playername and string.find(playername,_Playername) then + MClientPlayername = true + end + end + self:T( { "Evaluated Playername", MClientPlayername } ) + MClientInclude = MClientInclude and MClientPlayername + end + + if self.Filter.Callsigns then + local MClientCallsigns = false + local callsign = MClient:GetCallsign() + --self:I(callsign) + for _,_Callsign in pairs(self.Filter.Callsigns) do + if callsign and string.find(callsign,_Callsign) then + MClientCallsigns = true + end + end + self:T( { "Evaluated Callsign", MClientCallsigns } ) + MClientInclude = MClientInclude and MClientCallsigns + end + + end self:T2( MClientInclude ) return MClientInclude end @@ -4573,7 +4580,8 @@ end do -- SET_PLAYER - --- @type SET_PLAYER + --- + -- @type SET_PLAYER -- @extends Core.Set#SET_BASE --- Mission designers can use the @{Core.Set#SET_PLAYER} class to build sets of units belonging to alive players: @@ -4868,7 +4876,7 @@ do -- SET_PLAYER self:F2( arg ) self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Client#CLIENT ClientObject function( ZoneObject, ClientObject ) if ClientObject:IsInZone( ZoneObject ) then @@ -4890,7 +4898,7 @@ do -- SET_PLAYER self:F2( arg ) self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Client#CLIENT ClientObject function( ZoneObject, ClientObject ) if ClientObject:IsNotInZone( ZoneObject ) then @@ -4998,8 +5006,9 @@ do -- SET_PLAYER end do -- SET_AIRBASE - - --- @type SET_AIRBASE + + --- + -- @type SET_AIRBASE -- @extends Core.Set#SET_BASE --- Mission designers can use the @{Core.Set#SET_AIRBASE} class to build sets of airbases optionally belonging to certain: @@ -5341,8 +5350,9 @@ do -- SET_AIRBASE end do -- SET_CARGO - - --- @type SET_CARGO + + --- + -- @type SET_CARGO -- @extends Core.Set#SET_BASE --- Mission designers can use the @{Core.Set#SET_CARGO} class to build sets of cargos optionally belonging to certain: @@ -5767,8 +5777,9 @@ do -- SET_CARGO end do -- SET_ZONE - - --- @type SET_ZONE + + --- + -- @type SET_ZONE -- @extends Core.Set#SET_BASE --- Mission designers can use the @{Core.Set#SET_ZONE} class to build sets of zones of various types. @@ -6135,8 +6146,9 @@ do -- SET_ZONE end do -- SET_ZONE_GOAL - - --- @type SET_ZONE_GOAL + + --- + -- @type SET_ZONE_GOAL -- @extends Core.Set#SET_BASE --- Mission designers can use the @{Core.Set#SET_ZONE_GOAL} class to build sets of zones of various types. @@ -6446,8 +6458,9 @@ do -- SET_ZONE_GOAL end do -- SET_OPSZONE - - --- @type SET_OPSZONE + + --- + -- @type SET_OPSZONE -- @extends Core.Set#SET_BASE --- Mission designers can use the @{Core.Set#SET_OPSZONE} class to build sets of zones of various types. @@ -6903,8 +6916,9 @@ end do -- SET_OPSGROUP - - --- @type SET_OPSGROUP + + --- + -- @type SET_OPSGROUP -- @extends Core.Set#SET_BASE --- Mission designers can use the @{Core.Set#SET_OPSGROUP} class to build sets of OPS groups belonging to certain: @@ -7617,7 +7631,7 @@ do -- SET_SCENERY --- Add SCENERY(s) to SET_SCENERY. -- @param #SET_SCENERY self - -- @param #string AddScenery A single SCENERY. + -- @param Wrapper.Scenery#SCENERY AddScenery A single SCENERY object. -- @return #SET_SCENERY self function SET_SCENERY:AddScenery( AddScenery ) self:F2( AddScenery:GetName() ) @@ -7843,7 +7857,7 @@ do -- SET_SCENERY function SET_SCENERY:GetRelativeLife() local life = self:GetLife() local life0 = self:GetLife0() - self:T3(string.format("Set Lifepoints: %d life0 | %d life",life0,life)) + self:T2(string.format("Set Lifepoints: %d life0 | %d life",life0,life)) local rlife = math.floor((life / life0) * 100) return rlife end From 2771e7f85650a55ba0025c25d58cae059cc248eb Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 1 Aug 2023 16:18:23 +0200 Subject: [PATCH 18/22] #SCENERY, SET_SCENERY * Reworked COORDINATE scan as this doesn't seem to work * Added SET_SCENERY Filters, and FilterOnce() to apply them --- Moose Development/Moose/Core/Set.lua | 107 ++++++++++++++++++-- Moose Development/Moose/Wrapper/Scenery.lua | 91 ++++++++++++----- 2 files changed, 161 insertions(+), 37 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index e3cd263c4..973090bb7 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -243,7 +243,7 @@ do -- SET_BASE function SET_BASE:Add( ObjectName, Object ) -- Debug info. - self:T( { ObjectName = ObjectName, Object = Object } ) + self:T2( { ObjectName = ObjectName, Object = Object } ) -- Ensure that the existing element is removed from the Set before a new one is inserted to the Set if self.Set[ObjectName] then @@ -258,6 +258,8 @@ do -- SET_BASE -- Trigger Added event. self:Added( ObjectName, Object ) + + return self end --- Adds a @{Core.Base#BASE} object in the @{Core.Set#SET_BASE}, using the Object Name as the index. @@ -1949,7 +1951,7 @@ do if self.Filter.Zones then local MGroupZone = false for ZoneName, Zone in pairs( self.Filter.Zones ) do - --self:I( "Zone:", ZoneName ) + --self:T( "Zone:", ZoneName ) if MGroup:IsInZone(Zone) then MGroupZone = true end @@ -4466,7 +4468,7 @@ do -- SET_CLIENT if self.Filter.Active == false or (self.Filter.Active == true and MClient:IsActive() == true and MClient:IsAlive() == true) then MClientActive = true end - --self:I( { "Evaluated Active", MClientActive } ) + --self:T( { "Evaluated Active", MClientActive } ) MClientInclude = MClientInclude and MClientActive end @@ -4548,7 +4550,7 @@ do -- SET_CLIENT if self.Filter.Playernames then local MClientPlayername = false local playername = MClient:GetPlayerName() or "Unknown" - --self:I(playername) + --self:T(playername) for _,_Playername in pairs(self.Filter.Playernames) do if playername and string.find(playername,_Playername) then MClientPlayername = true @@ -4561,7 +4563,7 @@ do -- SET_CLIENT if self.Filter.Callsigns then local MClientCallsigns = false local callsign = MClient:GetCallsign() - --self:I(callsign) + --self:T(callsign) for _,_Callsign in pairs(self.Filter.Callsigns) do if callsign and string.find(callsign,_Callsign) then MClientCallsigns = true @@ -7585,6 +7587,7 @@ do -- SET_SCENERY Scenerys = {}, Filter = { SceneryPrefixes = nil, + SceneryRoles = nil, Zones = nil, }, } @@ -7607,7 +7610,7 @@ do -- SET_SCENERY if ZoneSet then for _,_zone in pairs(ZoneSet.Set) do - self:T("Zone type handed: "..tostring(_zone.ClassName)) + self:T("Zone type handed: "..tostring(_zone.ClassName)) table.insert(zonenames,_zone:GetName()) end self:AddSceneryByName(zonenames) @@ -7701,6 +7704,7 @@ do -- SET_SCENERY end for _,Zone in pairs( zones ) do local zonename = Zone:GetName() + self:T(zonename) self.Filter.Zones[zonename] = Zone end return self @@ -7719,11 +7723,30 @@ do -- SET_SCENERY Prefixes = { Prefixes } end for PrefixID, Prefix in pairs( Prefixes ) do + --self:T(Prefix) self.Filter.SceneryPrefixes[Prefix] = Prefix end return self end - + + --- Builds a set of SCENERYs that **contain** an exact match of the "ROLE" property. + -- @param #SET_SCENERY self + -- @param #string Role The string pattern(s) that needs to exactly match the scenery "ROLE" property from the ME quad-zone properties. Can also be passed as a `#table` of strings. + -- @return #SET_SCENERY self + function SET_SCENERY:FilterRoles( Role ) + if not self.Filter.SceneryRoles then + self.Filter.SceneryRoles = {} + end + if type( Role ) ~= "table" then + Role = { Role } + end + for PrefixID, Prefix in pairs( Role ) do + --self:T(Prefix) + self.Filter.SceneryRoles[Prefix] = Prefix + end + return self + end + --- Iterate the SET_SCENERY and count how many SCENERYSs are alive. -- @param #SET_SCENERY self -- @return #number The number of SCENERYSs alive. @@ -7816,10 +7839,76 @@ do -- SET_SCENERY -- @param Wrapper.Scenery#SCENERY MScenery -- @return #SET_SCENERY self function SET_SCENERY:IsIncludeObject( MScenery ) - self:F2( MScenery ) - return true + self:T( MScenery.SceneryName ) + + local MSceneryInclude = true + + if MScenery then + local MSceneryName = MScenery:GetName() + + -- Filter Prefixes + if self.Filter.Prefixes then + local MSceneryPrefix = false + for ZonePrefixId, ZonePrefix in pairs( self.Filter.Prefixes ) do + self:T( { "Prefix:", string.find( MSceneryName, ZonePrefix, 1 ), ZonePrefix } ) + if string.find( MSceneryName, ZonePrefix, 1 ) then + MSceneryPrefix = true + end + end + self:T( { "Evaluated Prefix", MSceneryPrefix } ) + MSceneryInclude = MSceneryInclude and MSceneryPrefix + end + + if self.Filter.Zones then + local MSceneryZone = false + for ZoneName, Zone in pairs( self.Filter.Zones ) do + --self:T( "Zone:", ZoneName ) + local coord = MScenery:GetCoordinate() + if coord and Zone:IsCoordinateInZone(coord) then + MSceneryZone = true + end + self:T( { "Evaluated Zone", MSceneryZone } ) + end + MSceneryInclude = MSceneryInclude and MSceneryZone + end + + -- Filter Roles + if self.Filter.SceneryRoles then + local MSceneryRole = false + local Role = MScenery:GetProperty("ROLE") or "none" + for ZoneRoleId, ZoneRole in pairs( self.Filter.SceneryRoles ) do + self:T( { "Role:", ZoneRole, Role } ) + if ZoneRole == Role then + MSceneryRole = true + end + end + self:T( { "Evaluated Role ", MSceneryRole } ) + MSceneryInclude = MSceneryInclude and MSceneryRole + end + end + + self:T2( MSceneryInclude ) + return MSceneryInclude end + --- Filters for the defined collection. + -- @param #SET_SCENERY self + -- @return #SET_SCENERY self + function SET_SCENERY:FilterOnce() + + for ObjectName, Object in pairs( self:GetSet() ) do + self:T(ObjectName) + if self:IsIncludeObject( Object ) then + self:Add( ObjectName, Object ) + else + self:Remove(ObjectName, true) + end + end + + return self --FilteredSet + end + + --- Count overall initial (Life0) lifepoints of the SET objects. -- @param #SET_SCENERY self -- @return #number LIfe0Points diff --git a/Moose Development/Moose/Wrapper/Scenery.lua b/Moose Development/Moose/Wrapper/Scenery.lua index 24dc45fd6..3c697579c 100644 --- a/Moose Development/Moose/Wrapper/Scenery.lua +++ b/Moose Development/Moose/Wrapper/Scenery.lua @@ -9,7 +9,7 @@ -- === -- -- @module Wrapper.Scenery --- @image Wrapper_Scenery.JPG +-- @image Wrapper_Scenery.JPG+ --- SCENERY Class @@ -18,6 +18,7 @@ -- @field #string SceneryName Name of the scenery object. -- @field DCS#Object SceneryObject DCS scenery object. -- @field #number Life0 Initial life points. +-- @field #table Properties -- @extends Wrapper.Positionable#POSITIONABLE @@ -43,7 +44,7 @@ function SCENERY:Register( SceneryName, SceneryObject ) local self = BASE:Inherit( self, POSITIONABLE:New( SceneryName ) ) - self.SceneryName = SceneryName + self.SceneryName = tostring(SceneryName) self.SceneryObject = SceneryObject @@ -52,9 +53,43 @@ function SCENERY:Register( SceneryName, SceneryObject ) else self.Life0 = 0 end + + self.Properties = {} + return self end +--- Returns the Value of the zone with the given PropertyName, or nil if no matching property exists. +-- @param #SCENERY self +-- @param #string PropertyName The name of a the QuadZone Property from the scenery assignment to be retrieved. +-- @return #string The Value of the QuadZone Property from the scenery assignment with the given PropertyName, or nil if absent. +function SCENERY:GetProperty(PropertyName) + return self.Properties[PropertyName] +end + +--- Returns the scenery Properties table. +-- @param #SCENERY self +-- @return #table The Key:Value table of QuadZone properties of the zone from the scenery assignment . +function SCENERY:GetAllProperties() + return self.Properties +end + +--- Set a scenery property +-- @param #SCENERY self +-- @param #string PropertyName +-- @param #string PropertyValue +-- @return #SCENERY self +function SCENERY:SetProperty(PropertyName, PropertyValue) + self.Properties[PropertyName] = PropertyValue + return self +end +--- Obtain object name. +--@param #SCENERY self +--@return #string Name +function SCENERY:GetName() + return self.SceneryName +end + --- Obtain DCS Object from the SCENERY Object. --@param #SCENERY self --@return DCS#Object DCS scenery object. @@ -115,26 +150,28 @@ end --@param Core.Point#COORDINATE Coordinate Where to find the scenery object --@param #number Radius (optional) Search radius around coordinate, defaults to 100 --@return #SCENERY Scenery Object or `nil` if it cannot be found -function SCENERY:FindByName(Name, Coordinate, Radius) - +function SCENERY:FindByName(Name, Coordinate, Radius, Role) + local radius = Radius or 100 local name = Name or "unknown" local scenery = nil - BASE:T({name, radius, Coordinate:GetVec2()}) - --- -- @param Core.Point#COORDINATE coordinate -- @param #number radius -- @param #string name - local function SceneryScan(coordinate, radius, name) - if coordinate ~= nil then - local scenerylist = coordinate:ScanScenery(radius) - local rscenery = nil - for _,_scenery in pairs(scenerylist) do + local function SceneryScan(scoordinate, sradius, sname) + if scoordinate ~= nil then + local Vec2 = scoordinate:GetVec2() + local scanzone = ZONE_RADIUS:New("Zone-"..sname,Vec2,sradius,true) + scanzone:Scan({Object.Category.SCENERY}) + local scanned = scanzone:GetScannedSceneryObjects() + local rscenery = nil -- Wrapper.Scenery#SCENERY + for _,_scenery in pairs(scanned) do local scenery = _scenery -- Wrapper.Scenery#SCENERY - if tostring(scenery.SceneryName) == tostring(name) then + if tostring(scenery.SceneryName) == tostring(sname) then rscenery = scenery + if Role then rscenery:SetProperty("ROLE",Role) end break end end @@ -144,6 +181,7 @@ function SCENERY:FindByName(Name, Coordinate, Radius) end if Coordinate then + --BASE:I("Coordinate Scenery Scan") scenery = SceneryScan(Coordinate, radius, name) end @@ -154,7 +192,7 @@ end -- to find the correct object. --@param #SCENERY self --@param #string Name The name or id of the scenery object as taken from the ME. Ex. '595785449' ---@param Core.Zone#ZONE Zone Where to find the scenery object. Can be handed as zone name. +--@param Core.Zone#ZONE_BASE Zone Where to find the scenery object. Can be handed as zone name. --@param #number Radius (optional) Search radius around coordinate, defaults to 100 --@return #SCENERY Scenery Object or `nil` if it cannot be found function SCENERY:FindByNameInZone(Name, Zone, Radius) @@ -164,7 +202,7 @@ function SCENERY:FindByNameInZone(Name, Zone, Radius) Zone = ZONE:FindByName(Zone) end local coordinate = Zone:GetCoordinate() - return self:FindByName(Name,coordinate,Radius) + return self:FindByName(Name,coordinate,Radius,Zone:GetProperty("ROLE")) end --- Find a SCENERY object from its zone name. Since SCENERY isn't registered in the Moose database (just too many objects per map), we need to do a scan first @@ -173,38 +211,34 @@ end --@param #string ZoneName The name of the scenery zone as created with a right-click on the map in the mission editor and select "assigned to...". Can be handed over as ZONE object. --@return #SCENERY First found Scenery Object or `nil` if it cannot be found function SCENERY:FindByZoneName( ZoneName ) - local zone = ZoneName -- Core.Zone#ZONE + local zone = ZoneName -- Core.Zone#ZONE_BASE if type(ZoneName) == "string" then - zone = ZONE:FindByName(ZoneName) + zone = ZONE:FindByName(ZoneName) -- Core.Zone#ZONE_POLYGON end local _id = zone:GetProperty('OBJECT ID') + --local properties = zone:GetAllProperties() or {} + --BASE:I(string.format("Object ID is: %s",_id or "none")) --BASE:T("Object ID ".._id) if not _id then -- this zone has no object ID BASE:E("**** Zone without object ID: "..ZoneName.." | Type: "..tostring(zone.ClassName)) if string.find(zone.ClassName,"POLYGON") then zone:Scan({Object.Category.SCENERY}) - local scanned = zone:GetScannedScenery() + local scanned = zone:GetScannedSceneryObjects() for _,_scenery in (scanned) do local scenery = _scenery -- Wrapper.Scenery#SCENERY if scenery:IsAlive() then + local role = zone:GetProperty("ROLE") + if role then scenery:SetProperty("ROLE",role) end return scenery end end return nil else - local coordinate = zone:GetCoordinate() - local scanned = coordinate:ScanScenery() - for _,_scenery in (scanned) do - local scenery = _scenery -- Wrapper.Scenery#SCENERY - if scenery:IsAlive() then - return scenery - end - end - return nil + return self:FindByName(_id, zone:GetCoordinate(),nil,zone:GetProperty("ROLE")) end else - return self:FindByName(_id, zone:GetCoordinate()) + return self:FindByName(_id, zone:GetCoordinate(),nil,zone:GetProperty("ROLE")) end end @@ -219,6 +253,7 @@ function SCENERY:FindAllByZoneName( ZoneName ) zone = ZONE:FindByName(ZoneName) end local _id = zone:GetProperty('OBJECT ID') + --local properties = zone:GetAllProperties() or {} if not _id then -- this zone has no object ID --BASE:E("**** Zone without object ID: "..ZoneName.." | Type: "..tostring(zone.ClassName)) @@ -230,7 +265,7 @@ function SCENERY:FindAllByZoneName( ZoneName ) return nil end else - local obj = self:FindByName(_id, zone:GetCoordinate()) + local obj = self:FindByName(_id, zone:GetCoordinate(),nil,zone:GetProperty("ROLE")) if obj then return {obj} else From 6aa468308056758ba59cedbedeb55471e22ab94c Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 1 Aug 2023 16:19:03 +0200 Subject: [PATCH 19/22] #PLAYERTASK --- Moose Development/Moose/Ops/PlayerTask.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index cdc693a33..f91893c7e 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -21,7 +21,7 @@ -- === -- @module Ops.PlayerTask -- @image OPS_PlayerTask.jpg --- @date Last Update June 2023 +-- @date Last Update July 2023 do From 9cc08cb088861fd378386de812af20aa2b0bbfce Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 1 Aug 2023 16:26:29 +0200 Subject: [PATCH 20/22] # --- Moose Development/Moose/Wrapper/Scenery.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Wrapper/Scenery.lua b/Moose Development/Moose/Wrapper/Scenery.lua index 3c697579c..1ef31a039 100644 --- a/Moose Development/Moose/Wrapper/Scenery.lua +++ b/Moose Development/Moose/Wrapper/Scenery.lua @@ -9,7 +9,7 @@ -- === -- -- @module Wrapper.Scenery --- @image Wrapper_Scenery.JPG+ +-- @image Wrapper_Scenery.JPG --- SCENERY Class From c5757ffd222fe703e8f58252e6293296d6f0411b Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 2 Aug 2023 18:02:34 +0200 Subject: [PATCH 21/22] #CONTROLLABLE * Added EnRouteTaskCAP() --- .../Moose/Wrapper/Controllable.lua | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 37067d492..79f794b48 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -1734,6 +1734,27 @@ function CONTROLLABLE:EnRouteTaskSEAD(TargetTypes, Priority) return DCSTask end +--- (AIR) Enroute CAP task. +-- @param #CONTROLLABLE self +-- @param DCS#AttributeNameArray TargetTypes Array of target categories allowed to engage. Default `{"Air"}`. +-- @param #number Priority (Optional) All en-route tasks have the priority parameter. This is a number (less value - higher priority) that determines actions related to what task will be performed first. Default 0. +-- @return DCS#Task The DCS task structure. +function CONTROLLABLE:EnRouteTaskCAP(TargetTypes, Priority) + + local DCSTask = { + id = 'EngageTargets', + key = "CAP", + --auto = true, + enabled = true, + params = { + targetTypes = TargetTypes or {"Air"}, + priority = Priority or 0 + } + } + + return DCSTask +end + --- (AIR) Engaging a controllable. The task does not assign the target controllable to the unit/controllable to attack now; -- it just allows the unit/controllable to engage the target controllable as well as other assigned targets. -- See [hoggit](https://wiki.hoggitworld.com/view/DCS_task_engageGroup). From 29ed630536ef4429f425da52f927f7c972d2a37d Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 13 Aug 2023 17:40:28 +0200 Subject: [PATCH 22/22] COORDINATE - Generalized MarkupToAllFreeForm UTILS - Added some functions from MIST --- Moose Development/Moose/Core/Point.lua | 26 +++- Moose Development/Moose/Utilities/Utils.lua | 160 +++++++++++++++++++- 2 files changed, 181 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index d942e49ba..99e8eb593 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -2389,7 +2389,6 @@ do -- COORDINATE end --- Creates a free form shape on the F10 map. The first point is the current COORDINATE. The remaining points need to be specified. - -- **NOTE**: A free form polygon must have **at least three points** in total and currently only **up to 15 points** in total are supported. -- @param #COORDINATE self -- @param #table Coordinates Table of coordinates of the remaining points of the shape. -- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All. @@ -2463,9 +2462,30 @@ do -- COORDINATE vecs[11], vecs[12], vecs[13], vecs[14], vecs[15], Color, FillColor, LineType, ReadOnly, Text or "") else - self:E("ERROR: Currently a free form polygon can only have 15 points in total!") + -- Unfortunately, unpack(vecs) does not work! So no idea how to generalize this :( - trigger.action.markupToAll(7, Coalition, MarkID, unpack(vecs), Color, FillColor, LineType, ReadOnly, Text or "") + --trigger.action.markupToAll(7, Coalition, MarkID, unpack(vecs), Color, FillColor, LineType, ReadOnly, Text or "") + + -- Write command as string and execute that. Idea by Grimes https://forum.dcs.world/topic/324201-mark-to-all-function/#comment-5273793 + local s=string.format("trigger.action.markupToAll(7, %d, %d,", Coalition, MarkID) + for _,vec in pairs(vecs) do + s=s..string.format("%s,", UTILS._OneLineSerialize(vec)) + end + s=s..string.format("%s, %s, %s, %s", UTILS._OneLineSerialize(Color), UTILS._OneLineSerialize(FillColor), tostring(LineType), tostring(ReadOnly)) + if Text and Text~="" then + s=s..string.format(", \"%s\"", Text) + end + s=s..")" + + + -- Execute string command + local success=UTILS.DoString(s) + + if not success then + self:E("ERROR: Could not draw polygon") + env.info(s) + end + end return MarkID diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 7b0467ed1..cc5f1df8b 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -293,8 +293,9 @@ UTILS.DeepCopy = function(object) end ---- Porting in Slmod's serialize_slmod2. +--- Serialize a given table. -- @param #table tbl Input table. +-- @return #string Table as a string. 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 = {} @@ -371,7 +372,54 @@ UTILS.OneLineSerialize = function( tbl ) -- serialization of a table all on a s return objectreturn end ---porting in Slmod's "safestring" basic serialize +--- Serialize a table to a single line string. +-- @param #table tbl table to serialize. +-- @return #string string containing serialized table. +function UTILS._OneLineSerialize(tbl) + + if type(tbl) == 'table' then --function only works for tables! + + local tbl_str = {} + + tbl_str[#tbl_str + 1] = '{ ' + + for ind,val in pairs(tbl) do -- serialize its fields + if type(ind) == "number" then + tbl_str[#tbl_str + 1] = '[' + tbl_str[#tbl_str + 1] = tostring(ind) + tbl_str[#tbl_str + 1] = '] = ' + else --must be a string + tbl_str[#tbl_str + 1] = '[' + tbl_str[#tbl_str + 1] = UTILS.BasicSerialize(ind) + tbl_str[#tbl_str + 1] = '] = ' + end + + if ((type(val) == 'number') or (type(val) == 'boolean')) then + tbl_str[#tbl_str + 1] = tostring(val) + tbl_str[#tbl_str + 1] = ', ' + elseif type(val) == 'string' then + tbl_str[#tbl_str + 1] = UTILS.BasicSerialize(val) + tbl_str[#tbl_str + 1] = ', ' + elseif type(val) == 'nil' then -- won't ever happen, right? + tbl_str[#tbl_str + 1] = 'nil, ' + elseif type(val) == 'table' then + tbl_str[#tbl_str + 1] = UTILS._OneLineSerialize(val) + tbl_str[#tbl_str + 1] = ', ' --I think this is right, I just added it + else + --log:warn('Unable to serialize value type $1 at index $2', mist.utils.basicSerialize(type(val)), tostring(ind)) + end + + end + + tbl_str[#tbl_str + 1] = '}' + return table.concat(tbl_str) + else + return UTILS.BasicSerialize(tbl) + end +end + +--- Basic serialize (porting in Slmod's "safestring" basic serialize). +-- @param #string s Table to serialize. UTILS.BasicSerialize = function(s) if s == nil then return "\"\"" @@ -402,6 +450,114 @@ function UTILS.PrintTableToLog(table, indent) end end +--- Returns table in a easy readable string representation. +-- @param tbl table to show +-- @param loc +-- @param indent +-- @param tableshow_tbls +-- @return Human readable string representation of given table. +function UTILS.TableShow(tbl, loc, indent, tableshow_tbls) + tableshow_tbls = tableshow_tbls or {} --create table of tables + loc = loc or "" + indent = indent or "" + if type(tbl) == 'table' then --function only works for tables! + tableshow_tbls[tbl] = loc + + local tbl_str = {} + + tbl_str[#tbl_str + 1] = indent .. '{\n' + + for ind,val in pairs(tbl) do -- serialize its fields + if type(ind) == "number" then + tbl_str[#tbl_str + 1] = indent + tbl_str[#tbl_str + 1] = loc .. '[' + tbl_str[#tbl_str + 1] = tostring(ind) + tbl_str[#tbl_str + 1] = '] = ' + else + tbl_str[#tbl_str + 1] = indent + tbl_str[#tbl_str + 1] = loc .. '[' + tbl_str[#tbl_str + 1] = UTILS.BasicSerialize(ind) + tbl_str[#tbl_str + 1] = '] = ' + end + + if ((type(val) == 'number') or (type(val) == 'boolean')) then + tbl_str[#tbl_str + 1] = tostring(val) + tbl_str[#tbl_str + 1] = ',\n' + elseif type(val) == 'string' then + tbl_str[#tbl_str + 1] = UTILS.BasicSerialize(val) + tbl_str[#tbl_str + 1] = ',\n' + elseif type(val) == 'nil' then -- won't ever happen, right? + tbl_str[#tbl_str + 1] = 'nil,\n' + elseif type(val) == 'table' then + if tableshow_tbls[val] then + tbl_str[#tbl_str + 1] = tostring(val) .. ' already defined: ' .. tableshow_tbls[val] .. ',\n' + else + tableshow_tbls[val] = loc .. '[' .. UTILS.BasicSerialize(ind) .. ']' + tbl_str[#tbl_str + 1] = tostring(val) .. ' ' + tbl_str[#tbl_str + 1] = UTILS.TableShow(val, loc .. '[' .. UTILS.BasicSerialize(ind).. ']', indent .. ' ', tableshow_tbls) + tbl_str[#tbl_str + 1] = ',\n' + end + elseif type(val) == 'function' then + if debug and debug.getinfo then + local fcnname = tostring(val) + local info = debug.getinfo(val, "S") + if info.what == "C" then + tbl_str[#tbl_str + 1] = string.format('%q', fcnname .. ', C function') .. ',\n' + else + if (string.sub(info.source, 1, 2) == [[./]]) then + tbl_str[#tbl_str + 1] = string.format('%q', fcnname .. ', defined in (' .. info.linedefined .. '-' .. info.lastlinedefined .. ')' .. info.source) ..',\n' + else + tbl_str[#tbl_str + 1] = string.format('%q', fcnname .. ', defined in (' .. info.linedefined .. '-' .. info.lastlinedefined .. ')') ..',\n' + end + end + + else + tbl_str[#tbl_str + 1] = 'a function,\n' + end + else + tbl_str[#tbl_str + 1] = 'unable to serialize value type ' .. UTILS.BasicSerialize(type(val)) .. ' at index ' .. tostring(ind) + end + end + + tbl_str[#tbl_str + 1] = indent .. '}' + return table.concat(tbl_str) + end +end + +--- Dumps the global table _G. +-- This dumps the global table _G to a file in the DCS\Logs directory. +-- This function requires you to disable script sanitization in $DCS_ROOT\Scripts\MissionScripting.lua to access lfs and io libraries. +-- @param #string fname File name. +function UTILS.Gdump(fname) + if lfs and io then + + local fdir = lfs.writedir() .. [[Logs\]] .. fname + + local f = io.open(fdir, 'w') + + f:write(UTILS.TableShow(_G)) + + f:close() + + env.info(string.format('Wrote debug data to $1', fdir)) + else + env.error("WARNING: lfs and/or io not de-sanitized - cannot dump _G!") + end +end + +--- Executes the given string. +-- borrowed from Slmod +-- @param #string s string containing LUA code. +-- @return #boolean `true` if successfully executed, `false` otherwise. +function UTILS.DoString(s) + local f, err = loadstring(s) + if f then + return true, f() + else + return false, err + end +end + UTILS.ToDegree = function(angle) return angle*180/math.pi