diff --git a/Moose Development/Moose/Core/MarkerOps_Base.lua b/Moose Development/Moose/Core/MarkerOps_Base.lua index 8e318a7a7..12095c5d9 100644 --- a/Moose Development/Moose/Core/MarkerOps_Base.lua +++ b/Moose Development/Moose/Core/MarkerOps_Base.lua @@ -11,6 +11,7 @@ -- ### Author: **Applevangelist** -- -- Date: 5 May 2021 +-- Last Update: Sep 2022 -- -- === --- @@ -27,6 +28,7 @@ -- @field #string Tag Tag to identify commands. -- @field #table Keywords Table of keywords to recognize. -- @field #string version Version of #MARKEROPS_BASE. +-- @field #boolean Casesensitive Enforce case when identifying the Tag, i.e. tag ~= Tag -- @extends Core.Fsm#FSM --- *Fiat lux.* -- Latin proverb. @@ -42,16 +44,18 @@ MARKEROPS_BASE = { ClassName = "MARKEROPS", Tag = "mytag", Keywords = {}, - version = "0.0.1", + version = "0.1.0", debug = false, + Casesensitive = true, } --- Function to instantiate a new #MARKEROPS_BASE object. -- @param #MARKEROPS_BASE self -- @param #string Tagname Name to identify us from the event text. -- @param #table Keywords Table of keywords recognized from the event text. +-- @param #boolean Casesensitive (Optional) Switch case sensitive identification of Tagname. Defaults to true. -- @return #MARKEROPS_BASE self -function MARKEROPS_BASE:New(Tagname,Keywords) +function MARKEROPS_BASE:New(Tagname,Keywords,Casesensitive) -- Inherit FSM local self=BASE:Inherit(self, FSM:New()) -- #MARKEROPS_BASE @@ -61,6 +65,11 @@ function MARKEROPS_BASE:New(Tagname,Keywords) self.Tag = Tagname or "mytag"-- #string self.Keywords = Keywords or {} -- #table - might want to use lua regex here, too self.debug = false + self.Casesensitive = true + + if Casesensitive and Casesensitive == false then + self.Casesensitive = false + end ----------------------- --- FSM Transitions --- @@ -178,9 +187,16 @@ end -- @return #boolean function MARKEROPS_BASE:_MatchTag(Eventtext) local matches = false - local type = string.lower(self.Tag) -- #string - if string.find(string.lower(Eventtext),type) then - matches = true --event text contains tag + if not self.Casesensitive then + local type = string.lower(self.Tag) -- #string + if string.find(string.lower(Eventtext),type) then + matches = true --event text contains tag + end + else + local type = self.Tag -- #string + if string.find(Eventtext,type) then + matches = true --event text contains tag + end end return matches end diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 92932ca1d..435a114db 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -20,7 +20,7 @@ -- === -- @module Ops.PlayerTask -- @image OPS_PlayerTask.jpg --- @date Last Update August 2022 +-- @date Last Update September 2022 do @@ -264,10 +264,12 @@ end --- [User] Get clients assigned list as table -- @param #PLAYERTASK self -- @return #table clients +-- @return #number clientcount function PLAYERTASK:GetClients() self:T(self.lid.."GetClients") local clientlist = self.Clients:GetIDStackSorted() or {} - return clientlist + local count = self.Clients:Count() + return clientlist, count end --- [User] Count clients @@ -704,6 +706,7 @@ do -- @field #boolean precisionbombing -- @field Ops.FlightGroup#FLIGHTGROUP LasingDrone -- @field Core.MarkerOps_BASE#MARKEROPS_BASE MarkerOps +-- @field #boolean askinfomenu -- @extends Core.Fsm#FSM --- @@ -839,7 +842,7 @@ do -- INTERCEPTTS = "intercept", -- BOMBRUNWAYTTS = "bomb runway", -- HAVEACTIVETASK = "You already have one active task! Complete it first!", --- PILOTJOINEDTASK = "%s, %s joined task %03d", +-- PILOTJOINEDTASK = "%s, %s. You have been assigned %s task %03d", -- TASKNAME = "%s Task ID %03d", -- TASKNAMETTS = "%s Task ID %03d", -- THREATHIGH = "high", @@ -850,7 +853,7 @@ do -- MARKTASK = "%s, %s, copy, task %03d location marked on map!", -- SMOKETASK = "%s, %s, copy, task %03d location smoked!", -- FLARETASK = "%s, %s, copy, task %03d location illuminated!", --- ABORTTASK = "%s, all stations, %s aborted task %03d!", +-- ABORTTASK = "%s, all stations, %s has aborted %s task %03d!", -- UNKNOWN = "Unknown", -- MENUTASKING = " Tasking ", -- MENUACTIVE = "Active Task", @@ -860,6 +863,7 @@ do -- MENUFLARE = "Flare", -- MENUABORT = "Abort", -- MENUJOIN = "Join Task", +-- MENUTASKINFO = Task Info", -- MENUTASKNO = "TaskNo", -- MENUNOTASKS = "Currently no tasks available.", -- TASKCANCELLED = "Task #%03d %s is cancelled!", @@ -875,6 +879,7 @@ do -- PILOTSTTS = ". Pilot(s): ", -- YES = "Yes", -- NO = "No", +-- NONE = "None", -- POINTEROVERTARGET = "%s, %s, pointer in reach for task %03d, lasing!", -- POINTERTARGETREPORT = "\nPointer in reach: %s\nLasing: %s", -- POINTERTARGETLASINGTTS = ". Pointer in reach and lasing.", @@ -984,6 +989,7 @@ PLAYERTASKCONTROLLER = { gettext = nil, locale = "en", precisionbombing = false, + taskinfomenu = false, } --- @@ -1031,7 +1037,8 @@ PLAYERTASKCONTROLLER.Messages = { INTERCEPTTS = "intercept", BOMBRUNWAYTTS = "bomb runway", HAVEACTIVETASK = "You already have one active task! Complete it first!", - PILOTJOINEDTASK = "%s, %s joined task %03d", + --PILOTJOINEDTASK = "%s, %s joined task %03d", + PILOTJOINEDTASK = "%s, %s. You have been assigned %s task %03d", TASKNAME = "%s Task ID %03d", TASKNAMETTS = "%s Task ID %03d", THREATHIGH = "high", @@ -1042,7 +1049,7 @@ PLAYERTASKCONTROLLER.Messages = { MARKTASK = "%s, %s, copy, task %03d location marked on map!", SMOKETASK = "%s, %s, copy, task %03d location smoked!", FLARETASK = "%s, %s, copy, task %03d location illuminated!", - ABORTTASK = "%s, all stations, %s aborted task %03d!", + ABORTTASK = "%s, all stations, %s has aborted %s task %03d!", UNKNOWN = "Unknown", MENUTASKING = " Tasking ", MENUACTIVE = "Active Task", @@ -1052,6 +1059,7 @@ PLAYERTASKCONTROLLER.Messages = { MENUFLARE = "Flare", MENUABORT = "Abort", MENUJOIN = "Join Task", + MENUTASKINFO = "Task Info", MENUTASKNO = "TaskNo", MENUNOTASKS = "Currently no tasks available.", TASKCANCELLED = "Task #%03d %s is cancelled!", @@ -1060,13 +1068,14 @@ PLAYERTASKCONTROLLER.Messages = { TASKSUCCESSTTS = "%s, task %03d %s completed successfully!", TASKFAILED = "Task #%03d %s was a failure!", TASKFAILEDTTS = "%s, task %03d %s was a failure!", - TASKFAILEDREPLAN = "Task #%03d %s was a failure! Replanning!", - TASKFAILEDREPLANTTS = "%s, task %03d %s was a failure! Replanning!", - TASKADDED = "%s has a new task %s available!", + TASKFAILEDREPLAN = "Task #%03d %s available for reassignment!", + TASKFAILEDREPLANTTS = "%s, task %03d %s vailable for reassignment!", + TASKADDED = "%s has a new %s task available!", PILOTS = "\nPilot(s): ", PILOTSTTS = ". Pilot(s): ", YES = "Yes", NO = "No", + NONE = "None", POINTEROVERTARGET = "%s, %s, pointer in reach for task %03d, lasing!", POINTERTARGETREPORT = "\nPointer in reach: %s\nLasing: %s", POINTERTARGETLASINGTTS = ". Pointer in reach and lasing.", @@ -1086,7 +1095,8 @@ PLAYERTASKCONTROLLER.Messages = { INTERCEPTTS = "Abfangen", BOMBRUNWAYTTS = "Startbahn Bombardieren", HAVEACTIVETASK = "Du hast einen aktiven Auftrag! Beende ihn zuerst!", - PILOTJOINEDTASK = "%s, %s hat Auftrag %03d angenommen", + --PILOTJOINEDTASK = "%s, %s. You have been assigned %s task %03d", + PILOTJOINEDTASK = "%s, %s hat Auftrag %s %03d angenommen", TASKNAME = "%s Auftrag ID %03d", TASKNAMETTS = "%s Auftrag ID %03d", THREATHIGH = "hoch", @@ -1097,7 +1107,7 @@ PLAYERTASKCONTROLLER.Messages = { MARKTASK = "%s, %s, verstanden, Zielposition %03d auf der Karte markiert!", SMOKETASK = "%s, %s, verstanden, Zielposition %03d mit Rauch markiert!", FLARETASK = "%s, %s, verstanden, Zielposition %03d beleuchtet!", - ABORTTASK = "%s, an alle, %s hat Auftrag %03d abgebrochen!", + ABORTTASK = "%s, an alle, %s hat Auftrag %s %03d abgebrochen!", UNKNOWN = "Unbekannt", MENUTASKING = " Aufträge ", MENUACTIVE = "Aktiver Auftrag", @@ -1107,6 +1117,7 @@ PLAYERTASKCONTROLLER.Messages = { MENUFLARE = "Leuchtgranate", MENUABORT = "Abbrechen", MENUJOIN = "Auftrag annehmen", + MENUTASKINFO = "Auftrag Briefing", MENUTASKNO = "AuftragsNr", MENUNOTASKS = "Momentan keine Aufträge verfügbar.", TASKCANCELLED = "Auftrag #%03d %s wurde beendet!", @@ -1122,6 +1133,7 @@ PLAYERTASKCONTROLLER.Messages = { PILOTSTTS = ". Pilot(en): ", YES = "Ja", NO = "Nein", + NONE = "Keine", POINTEROVERTARGET = "%s, %s, Marker im Zielbereich für %03d, Laser an!", POINTERTARGETREPORT = "\nMarker im Zielbereich: %s\nLaser an: %s", POINTERTARGETLASINGTTS = ". Marker im Zielbereich, Laser is an.", @@ -1130,7 +1142,7 @@ PLAYERTASKCONTROLLER.Messages = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.27" +PLAYERTASKCONTROLLER.version="0.1.29" --- Constructor -- @param #PLAYERTASKCONTROLLER self @@ -1164,6 +1176,8 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter) self.TasksPerPlayer = FIFO:New() -- Utilities.FiFo#FIFO self.PrecisionTasks = FIFO:New() -- Utilities.FiFo#FIFO self.PlayerMenu = {} -- #table + self.lasttaskcount = 0 + self.taskinfomenu = false self.MenuName = nil @@ -1407,6 +1421,22 @@ function PLAYERTASKCONTROLLER:DisablePrecisionBombing(FlightGroup,LaserCode) return self end +--- [User] Enable extra menu to show task detail information before joining +-- @param #PLAYERTASKCONTROLLER self +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:EnableTaskInfoMenu() + self:T(self.lid.."EnableTaskInfoMenu") + self.taskinfomenu = true +end + +--- [User] Disable extra menu to show task detail information before joining +-- @param #PLAYERTASKCONTROLLER self +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:DisableTaskInfoMenu() + self:T(self.lid.."DisableTaskInfoMenu") + self.taskinfomenu = false +end + --- [Internal] Event handling -- @param #PLAYERTASKCONTROLLER self -- @param Core.Event#EVENTDATA EventData @@ -2080,7 +2110,8 @@ function PLAYERTASKCONTROLLER:_JoinTask(Group, Client, Task) end Task:AddClient(Client) local joined = self.gettext:GetEntry("PILOTJOINEDTASK",self.locale) - local text = string.format(joined, self.MenuName or self.Name, ttsplayername, Task.PlayerTaskNr) + -- PILOTJOINEDTASK = "%s, %s. You have been assigned %s task %03d", + local text = string.format(joined,ttsplayername, self.MenuName or self.Name, Task.TTSType, Task.PlayerTaskNr) self:T(self.lid..text) if not self.NoScreenOutput then local m=MESSAGE:New(text,"10","Tasking"):ToAll() @@ -2090,7 +2121,7 @@ function PLAYERTASKCONTROLLER:_JoinTask(Group, Client, Task) end self.TasksPerPlayer:Push(Task,playername) -- clear menu - self:_BuildMenus(Client) + self:_BuildMenus(Client,true) end if Task.Type == AUFTRAG.Type.PRECISIONBOMBING then if not self.PrecisionTasks:HasUniqueID(Task.PlayerTaskNr) then @@ -2104,14 +2135,15 @@ end -- @param #PLAYERTASKCONTROLLER self -- @param Wrapper.Group#GROUP Group -- @param Wrapper.Client#CLIENT Client +-- @param Ops.PlayerTask#PLAYERTASK Task -- @return #PLAYERTASKCONTROLLER self -function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Group, Client) +function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Group, Client, Task) self:T(self.lid.."_ActiveTaskInfo") local playername, ttsplayername = self:_GetPlayerName(Client) local text = "" - if self.TasksPerPlayer:HasUniqueID(playername) then + if self.TasksPerPlayer:HasUniqueID(playername) or Task then -- TODO: Show multiple? - local task = self.TasksPerPlayer:ReadByID(playername) -- Ops.PlayerTask#PLAYERTASK + local task = Task or self.TasksPerPlayer:ReadByID(playername) -- Ops.PlayerTask#PLAYERTASK local tname = self.gettext:GetEntry("TASKNAME",self.locale) local ttsname = self.gettext:GetEntry("TASKNAMETTS",self.locale) local taskname = string.format(tname,task.Type,task.PlayerTaskNr) @@ -2134,7 +2166,7 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Group, Client) ThreatLevelText = self.gettext:GetEntry("THREATLOW",self.locale) end local targets = task.Target:CountTargets() or 0 - local clientlist = task:GetClients() + local clientlist, clientcount = task:GetClients() local ThreatGraph = "[" .. string.rep( "■", ThreatLevel ) .. string.rep( "□", 10 - ThreatLevel ) .. "]: "..ThreatLevel local ThreatLocaleText = self.gettext:GetEntry("THREATTEXT",self.locale) text = string.format(ThreatLocaleText, taskname, ThreatGraph, targets, CoordText) @@ -2150,14 +2182,19 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Group, Client) end end local clienttxt = self.gettext:GetEntry("PILOTS",self.locale) - for _,_name in pairs(clientlist) do - if string.find(_name,"|") then - -- personalized flight name in player naming - _name = string.match(_name,"| ([%a]+)") + if clientcount > 0 then + for _,_name in pairs(clientlist) do + if string.find(_name,"|") then + -- personalized flight name in player naming + _name = string.match(_name,"| ([%a]+)") + end + clienttxt = clienttxt .. _name .. ", " end - clienttxt = clienttxt .. _name .. ", " + clienttxt=string.gsub(clienttxt,", $",".") + else + local keine = self.gettext:GetEntry("NONE",self.locale) + clienttxt = clienttxt .. keine end - clienttxt=string.gsub(clienttxt,", $",".") text = text .. clienttxt if self.UseSRS then if string.find(CoordText," BR, ") then @@ -2194,7 +2231,7 @@ function PLAYERTASKCONTROLLER:_MarkTask(Group, Client) local text = "" if self.TasksPerPlayer:HasUniqueID(playername) then local task = self.TasksPerPlayer:ReadByID(playername) -- Ops.PlayerTask#PLAYERTASK - local text = string.format("Task ID #%03d | Type: %s | Threat: %d",task.PlayerTaskNr,task.Type,task.Target:GetThreatLevelMax()) + text = string.format("Task ID #%03d | Type: %s | Threat: %d",task.PlayerTaskNr,task.Type,task.Target:GetThreatLevelMax()) task:MarkTargetOnF10Map(text) local textmark = self.gettext:GetEntry("MARKTASK",self.locale) --text = string.format("%s, copy pilot %s, task %03d location marked on map!", self.MenuName or self.Name, playername, task.PlayerTaskNr) @@ -2281,7 +2318,8 @@ function PLAYERTASKCONTROLLER:_AbortTask(Group, Client) local task = self.TasksPerPlayer:PullByID(playername) -- Ops.PlayerTask#PLAYERTASK task:ClientAbort(Client) local textmark = self.gettext:GetEntry("ABORTTASK",self.locale) - text = string.format(textmark, ttsplayername, self.MenuName or self.Name, task.PlayerTaskNr) + -- ABORTTASK = "%s, to all stations, %s has aborted %s task %03d!", + text = string.format(textmark, self.MenuName or self.Name, ttsplayername, task.TTSType, task.PlayerTaskNr) self:T(self.lid..text) --local m=MESSAGE:New(text,"10","Tasking"):ToAll() if self.UseSRS then @@ -2293,19 +2331,21 @@ function PLAYERTASKCONTROLLER:_AbortTask(Group, Client) if not self.NoScreenOutput then local m=MESSAGE:New(text,15,"Tasking"):ToGroup(Group) end - self:_BuildMenus(Client) + self:_BuildMenus(Client,true) return self end --- [Internal] Build client menus -- @param #PLAYERTASKCONTROLLER self -- @param Wrapper.Client#CLIENT Client (optional) build for this client name only +-- @param #boolen enforced -- @return #PLAYERTASKCONTROLLER self -function PLAYERTASKCONTROLLER:_BuildMenus(Client) +function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced) self:T(self.lid.."_BuildMenus") local clients = self.ClientSet:GetAliveSet() if Client then clients = {Client} + enforced = true end for _,_client in pairs(clients) do if _client then @@ -2314,23 +2354,37 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client) local unknown = self.gettext:GetEntry("UNKNOWN",self.locale) local playername = client:GetPlayerName() or unknown if group and client then + --- + -- Conditions for menu rebuild + -- 1) Player has no menu + -- 2) Player has no running task + -- 3) enforced --- -- TOPMENU --- local taskings = self.gettext:GetEntry("MENUTASKING",self.locale) local menuname = self.MenuName or self.Name..taskings..self.Type - local topmenu = MENU_GROUP:New(group,menuname,nil) + local playerhastask = false + + if self:_CheckPlayerHasTask(playername) then playerhastask = true end + local topmenu = nil + + self:T("Playerhastask = "..tostring(playerhastask).." Enforced = "..tostring(enforced)) if self.PlayerMenu[playername] then - self.PlayerMenu[playername]:RemoveSubMenus() + if enforced or not playerhastask then + self.PlayerMenu[playername]:RemoveSubMenus() + end + topmenu = self.PlayerMenu[playername] else + topmenu = MENU_GROUP:New(group,menuname,nil) self.PlayerMenu[playername] = topmenu end --- -- ACTIVE TASK MENU --- - if self:_CheckPlayerHasTask(playername) then + if playerhastask and enforced then local menuactive = self.gettext:GetEntry("MENUACTIVE",self.locale) local menuinfo = self.gettext:GetEntry("MENUINFO",self.locale) @@ -2348,17 +2402,21 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client) local flare = MENU_GROUP_COMMAND:New(group,menuflare,active,self._FlareTask,self,group,client) end local abort = MENU_GROUP_COMMAND:New(group,menuabort,active,self._AbortTask,self,group,client) - elseif self.TaskQueue:Count() > 0 then + + elseif (self.TaskQueue:Count() > 0 and enforced) or (not playerhastask) then --- -- JOIN TASK MENU --- local tasktypes = self:_GetAvailableTaskTypes() local taskpertype = self:_GetTasksPerType() local menujoin = self.gettext:GetEntry("MENUJOIN",self.locale) + local menutaskinfo = self.gettext:GetEntry("MENUTASKINFO",self.locale) local joinmenu = MENU_GROUP:New(group,menujoin,topmenu) local ttypes = {} local taskmenu = {} + local ittypes = {} + local itaskmenu = {} for _tasktype,_data in pairs(tasktypes) do ttypes[_tasktype] = MENU_GROUP:New(group,_tasktype,joinmenu) local tasks = taskpertype[_tasktype] or {} @@ -2386,7 +2444,38 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client) --end end end - else + + if self.taskinfomenu then + local taskinfomenu = MENU_GROUP:New(group,menutaskinfo,topmenu) + for _tasktype,_data in pairs(tasktypes) do + ittypes[_tasktype] = MENU_GROUP:New(group,_tasktype,taskinfomenu) + local tasks = taskpertype[_tasktype] or {} + for _,_task in pairs(tasks) do + _task = _task -- Ops.PlayerTask#PLAYERTASK + local pilotcount = _task:CountClients() + local newtext = "]" + local tnow = timer.getTime() + -- marker for new tasks + if tnow - _task.timestamp < 60 then + newtext = "*]" + end + local menutaskno = self.gettext:GetEntry("MENUTASKNO",self.locale) + local text = string.format("%s %03d [%d%s",menutaskno,_task.PlayerTaskNr,pilotcount,newtext) + if self.UseGroupNames then + local name = _task.Target:GetName() + if name ~= "Unknown" then + text = string.format("%s (%03d) [%d%s",name,_task.PlayerTaskNr,pilotcount,newtext) + end + end + --if _task:GetState() == "Planned" or (not _task:HasPlayerName(playername)) then + local taskentry = MENU_GROUP_COMMAND:New(group,text,ittypes[_tasktype],self._ActiveTaskInfo,self,group,client,_task) + taskentry:SetTag(playername) + itaskmenu[#itaskmenu+1] = taskentry + --end + end + end + end + elseif self.TaskQueue:Count() == 0 then -- no tasks (yet) local menunotasks = self.gettext:GetEntry("MENUNOTASKS",self.locale) local joinmenu = MENU_GROUP:New(group,menunotasks,topmenu) @@ -2638,15 +2727,23 @@ end -- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:onafterStatus(From, Event, To) self:I({From, Event, To}) + self:_CheckTargetQueue() self:_CheckTaskQueue() self:_CheckPrecisionTasks() - self:_BuildMenus() local targetcount = self.TargetQueue:Count() local taskcount = self.TaskQueue:Count() local playercount = self.ClientSet:CountAlive() local assignedtasks = self.TasksPerPlayer:Count() + local enforcedmenu = false + + if taskcount ~= self.lasttaskcount then + self.lasttaskcount = taskcount + enforcedmenu = true + end + + self:_BuildMenus(nil,enforcedmenu) if self.verbose then local text = string.format("New Targets: %02d | Active Tasks: %02d | Active Players: %02d | Assigned Tasks: %02d",targetcount,taskcount,playercount,assignedtasks)