diff --git a/Moose Development/Moose/Core/ClientMenu.lua b/Moose Development/Moose/Core/ClientMenu.lua index 61e37bb73..c8d9c3833 100644 --- a/Moose Development/Moose/Core/ClientMenu.lua +++ b/Moose Development/Moose/Core/ClientMenu.lua @@ -37,9 +37,10 @@ -- @field #table parentpath -- @field #CLIENTMENU Parent -- @field Wrapper.Client#CLIENT client --- @field #number GID --- @field #number ID +-- @field #number GroupID Group ID +-- @field #number ID Entry ID -- @field Wrapper.Group#GROUP group +-- @field #string UUID Unique ID based on path+name -- @field #string Function -- @field #table Functionargs -- @field #table Children @@ -54,12 +55,12 @@ CLIENTMENU = { ClassName = "CLIENTMENUE", lid = "", - version = "0.0.1", + version = "0.0.2", name = nil, path = nil, group = nil, client = nil, - GID = nil, + GroupID = nil, Children = {}, Once = false, Generic = false, @@ -87,7 +88,7 @@ function CLIENTMENU:NewEntry(Client,Text,Parent,Function,...) if Client then self.group = Client:GetGroup() self.client = Client - self.GID = self.group:GetID() + self.GroupID = self.group:GetID() else self.Generic = true end @@ -98,7 +99,9 @@ function CLIENTMENU:NewEntry(Client,Text,Parent,Function,...) end self.Parent = Parent self.Function = Function - self.Functionargs = arg + self.Functionargs = arg or {} + table.insert(self.Functionargs,self.group) + table.insert(self.Functionargs,self.client) if self.Functionargs and self.debug then self:I({"Functionargs",self.Functionargs}) end @@ -120,9 +123,9 @@ function CLIENTMENU:NewEntry(Client,Text,Parent,Function,...) self:Clear() end end - self.path = missionCommands.addCommandForGroup(self.GID,Text,self.parentpath, self.CallHandler) + self.path = missionCommands.addCommandForGroup(self.GroupID,Text,self.parentpath, self.CallHandler) else - self.path = missionCommands.addSubMenuForGroup(self.GID,Text,self.parentpath) + self.path = missionCommands.addSubMenuForGroup(self.GroupID,Text,self.parentpath) end else if self.parentpath then @@ -131,8 +134,9 @@ function CLIENTMENU:NewEntry(Client,Text,Parent,Function,...) self.path = {} end self.path[#self.path+1] = Text - self:T({self.path}) end + self.UUID = table.concat(self.path,";") + self:I({self.UUID}) self.Once = false -- Log id. self.lid=string.format("CLIENTMENU %s | %s | ", self.ID, self.name) @@ -162,9 +166,10 @@ end -- @param #CLIENTMENU self -- @return #CLIENTMENU self function CLIENTMENU:RemoveF10() - self:T(self.lid.."RemoveF10") + self:I(self.lid.."RemoveF10") if not self.Generic then - missionCommands.removeItemForGroup(self.GID , self.path ) + self:I(self.lid.."Removing") + missionCommands.removeItemForGroup(self.GroupID , self.path ) end return self end @@ -177,6 +182,14 @@ function CLIENTMENU:GetPath() return self.path end +--- Get the UUID. +-- @param #CLIENTMENU self +-- @return #string UUID +function CLIENTMENU:GetUUID() + self:T(self.lid.."GetUUID") + return self.UUID +end + --- Link a child entry. -- @param #CLIENTMENU self -- @param #CLIENTMENU Child The entry to link as a child. @@ -201,20 +214,20 @@ end -- @param #CLIENTMENU self -- @return #CLIENTMENU self function CLIENTMENU:RemoveSubEntries() - self:T(self.lid.."RemoveSubEntries") - --self:T({self.Children}) + self:I(self.lid.."RemoveSubEntries") + self:I({self.Children}) for _id,_entry in pairs(self.Children) do - self:T("Removing ".._id) + self:I("Removing ".._id) if _entry then _entry:RemoveSubEntries() _entry:RemoveF10() if _entry.Parent then _entry.Parent:RemoveChild(self) end - if self.Controller then - self.Controller:_RemoveByID(_entry.ID) - end - _entry = nil + --if self.Controller then + --self.Controller:_RemoveByID(_entry.ID) + --end + --_entry = nil end end return self @@ -235,9 +248,9 @@ function CLIENTMENU:Clear() if self.Parent then self.Parent:RemoveChild(self) end - if self.Controller then - self.Controller:_RemoveByID(self.ID) - end + --if self.Controller then + --self.Controller:_RemoveByID(self.ID) + --end return self end @@ -256,9 +269,9 @@ end -- @field #string version Version string -- @field #string name Name -- @field Core.Set#SET_CLIENT clientset The set of clients this menu manager is for --- @field #table structure --- @field #table replacementstructure +-- @field #table flattree -- @field #table rootentries +-- @field #table menutree -- @field #number entrycount -- @field #boolean debug -- @extends Core.Base#BASE @@ -310,27 +323,14 @@ end -- -- ## Remove a single entry and also it's subtree -- --- menumgr:Clear(mymenu_lv3a) +-- menumgr:DeleteEntry(mymenu_lv3a) -- -- ## Add a single entry -- -- local baimenu = menumgr:NewEntry("BAI",mymenu_lv1b) -- menumgr:AddEntry(baimenu) -- --- ## Prepare and push a partial replacement in the tree --- --- menumgr:PrepareNewReplacementStructure() --- local submenu = menumgr:NewReplacementEntry("New Level 2 ba",mymenu_lv2a) --- menumgr:NewReplacementEntry("New Level 2 bb",mymenu_lv2a) --- menumgr:NewReplacementEntry("Deleted",mymenu_lv2a) --- menumgr:NewReplacementEntry("New Level 2 bd",mymenu_lv2a) --- menumgr:NewReplacementEntry("SubLevel 3 baa",submenu) --- menumgr:NewReplacementEntry("SubLevel 3 bab",submenu) --- menumgr:NewReplacementEntry("SubLevel 3 bac",submenu) --- menumgr:NewReplacementEntry("SubLevel 3 bad",submenu) --- menumgr:ReplaceEntries(mymenu_lv2a) --- --- ## Change the text of an entry in the menu tree +-- ## Change the text of a leaf entry in the menu tree -- -- menumgr:ChangeEntryTextForAll(mymenu_lv1b,"Attack") -- @@ -346,31 +346,17 @@ end CLIENTMENUMANAGER = { ClassName = "CLIENTMENUMANAGER", lid = "", - version = "0.0.1", + version = "0.0.4", name = nil, clientset = nil, - --- - -- @field #CLIENTMENUMANAGER.Structure - structure = { - generic = {}, - IDs = {}, - }, - --- - -- #CLIENTMENUMANAGER.ReplacementStructure - replacementstructure = { - generic = {}, - IDs = {}, - }, + menutree = {}, + flattree = {}, + playertree = {}, entrycount = 0, rootentries = {}, debug = true, } ---- --- @type CLIENTMENUMANAGER.Structure --- @field #table generic --- @field #table IDs - --- Create a new ClientManager instance. -- @param #CLIENTMENUMANAGER self -- @param Core.Set#SET_CLIENT ClientSet The set of clients to manage. @@ -400,271 +386,119 @@ function CLIENTMENUMANAGER:NewEntry(Text,Parent,Function,...) self:T(self.lid.."NewEntry "..Text or "None") self.entrycount = self.entrycount + 1 local entry = CLIENTMENU:NewEntry(nil,Text,Parent,Function,unpack(arg)) - self.structure.generic[self.entrycount] = entry - self.structure.IDs[entry.ID] = self.entrycount if not Parent then - self.rootentries[self.entrycount] = self.entrycount + self.rootentries[self.entrycount] = entry end + local depth = #entry.path + if not self.menutree[depth] then self.menutree[depth] = {} end + table.insert(self.menutree[depth],entry.UUID) + self.flattree[entry.UUID] = entry return entry end ---- Find **first** matching entry in the generic structure by the menu text. +--- Find matching entry in the generic structure by UUID. -- @param #CLIENTMENUMANAGER self --- @param #string Text Text of the F10 menu entry. +-- @param #string UUID UUID of the menu entry. -- @return #CLIENTMENU Entry The #CLIENTMENU object found or nil. --- @return #number GID GID The GID found or nil. -function CLIENTMENUMANAGER:FindEntryByText(Text) - self:T(self.lid.."FindEntryByText "..Text or "None") +function CLIENTMENUMANAGER:FindEntryByUUID(UUID) + self:I(self.lid.."FindEntryByUUID "..UUID or "None") local entry = nil - local gid = nil - for _gid,_entry in UTILS.spairs(self.structure.generic) do + for _gid,_entry in pairs(self.flattree) do local Entry = _entry -- #CLIENTMENU - if Entry and Entry.name == Text then + if Entry and Entry.UUID == UUID then entry = Entry - gid = _gid end end - return entry, gid -end - ---- Find first matching entry in the generic structure by the GID. --- @param #CLIENTMENUMANAGER self --- @param #number GID The GID of the entry to find. --- @return #CLIENTMENU Entry The #CLIENTMENU object found or nil. -function CLIENTMENUMANAGER:GetEntryByGID(GID) - self:T(self.lid.."GetEntryByGID "..GID or "None") - if GID and type(GID) == "number" then - return self.structure.generic[GID] - else - return nil - end -end - ---- Alter the text of an entry in the generic structure and push to all clients. --- @param #CLIENTMENUMANAGER self --- @param #CLIENTMENU Entry The menu entry. --- @param #string Text Text of the F10 menu entry. --- @return #CLIENTMENUMANAGER self -function CLIENTMENUMANAGER:ChangeEntryTextForAll(Entry,Text) - self:T(self.lid.."ChangeEntryTextForAll "..Text or "None") - for _,_client in pairs(self.clientset.Set) do - local client = _client -- Wrapper.Client#CLIENT - if client and client:IsAlive() then - self:ChangeEntryText(Entry,Text, client) - end - end - return self -end - ---- Alter the text of an entry in the generic structure and push to one specific client. --- @param #CLIENTMENUMANAGER self --- @param #CLIENTMENU Entry The menu entry. --- @param #string Text Text of the F10 menu entry. --- @param Wrapper.Client#CLIENT Client The client for whom to alter the entry --- @return #CLIENTMENUMANAGER self -function CLIENTMENUMANAGER:ChangeEntryText(Entry,Text, Client) - self:T(self.lid.."ChangeEntryText "..Text or "None") - - local text = Text or "none" - local oldtext = Entry.name - Entry.name = text - - local newstructure = {} - local changed = 0 - - local function ChangePath(path,oldtext,newtext) - local newpath = {} - for _id,_text in UTILS.spairs(path) do - local txt = _text - if _text == oldtext then - txt = newtext - end - newpath[_id] = txt - end - return newpath - end - - local function AlterPath(children) - for _,_entry in pairs(children) do - local entry = _entry -- #CLIENTMENU - local newpath = ChangePath(entry.path,oldtext,text) - local newparentpath = ChangePath(entry.parentpath,oldtext,text) - entry.path = nil - entry.parentpath = nil - entry.path = newpath - entry.parentpath = newparentpath - self:T({entry.ID}) - --self:T({entry.parentpath}) - newstructure[entry.ID] = UTILS.DeepCopy(entry) - changed = changed + 1 - if entry.Children and #entry.Children > 0 then - AlterPath(entry.Children) - end - end - end - - -- get the entry - local ID = Entry.ID - local GID = self.structure.IDs[ID] - local playername = Client:GetPlayerName() - local children = self.structure[playername][GID].Children - AlterPath(children) - - self:T("Changed entries: "..changed) - - local NewParent = self:NewEntry(Entry.name,Entry.Parent,Entry.Function,unpack(Entry.Functionargs)) - - for _,_entry in pairs(children) do - self:T("Changed parent for ".._entry.ID.." | GID ".._entry.GID) - local entry = _entry -- #CLIENTMENU - entry.Parent = NewParent - end - - self:PrepareNewReplacementStructure() - - for _,_entry in pairs(newstructure) do - self:T("Changed entry: ".._entry.ID.." | GID ".._entry.GID) - local entry = _entry -- #CLIENTMENU - self:NewReplacementEntry(entry.name,entry.Parent,entry.Function,unpack(entry.Functionargs)) - end - - - self:AddEntry(NewParent) - self:ReplaceEntries(NewParent) - - self:Clear(Entry) - - return self -end - ---- Create a new entry in the replacement structure. --- @param #CLIENTMENUMANAGER self --- @param #string Text Text of the F10 menu entry. --- @param #CLIENTMENU Parent The parent menu entry. --- @param #string Function (optional) Function to call when the entry is used. --- @param ... (optional) Arguments for the Function, comma separated --- @return #CLIENTMENU Entry -function CLIENTMENUMANAGER:NewReplacementEntry(Text,Parent,Function,...) - self:T(self.lid.."NewReplacementEntry "..Text or "None") - self.entrycount = self.entrycount + 1 - local entry = CLIENTMENU:NewEntry(nil,Text,Parent,Function,unpack(arg)) - self.replacementstructure.generic[self.entrycount] = entry - self.replacementstructure.IDs[entry.ID] = self.entrycount - local pID = Parent and Parent.ID or "none" - if self.debug then - self:I("Entry ID = "..self.entrycount.." | Parent ID = "..tostring(pID)) - end - if not Parent then - self.rootentries[self.entrycount] = self.entrycount - end return entry end ---- Prepare a new replacement structure. Deletes the previous one. +--- Find matching entry by text in the generic structure by UUID. -- @param #CLIENTMENUMANAGER self --- @return #CLIENTMENUMANAGER self -function CLIENTMENUMANAGER:PrepareNewReplacementStructure() - self:T(self.lid.."PrepareNewReplacementStructure") - self.replacementstructure = nil -- #CLIENTMENUMANAGER.Structure - self.replacementstructure = { - generic = {}, - IDs = {}, - } - return self -end - ---- [Internal] Merge the replacement structure into the generic structure. --- @param #CLIENTMENUMANAGER self --- @return #CLIENTMENUMANAGER self -function CLIENTMENUMANAGER:_MergeReplacementData() - self:T(self.lid.."_MergeReplacementData") - for _id,_entry in pairs(self.replacementstructure.generic) do - self.structure.generic[_id] = _entry - end - for _id,_entry in pairs(self.replacementstructure.IDs) do - self.structure.IDs[_id] = _entry - end - self:_CleanUpPlayerStructure() - return self -end - ---- Replace entries under the Parent entry with the Replacement structure created prior for all clients. --- @param #CLIENTMENUMANAGER self --- @param #CLIENTMENU Parent The parent entry under which to replace with the new structure. --- @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. --- @return #CLIENTMENUMANAGER self -function CLIENTMENUMANAGER:ReplaceEntries(Parent,Client) - self:T(self.lid.."ReplaceEntries") - -- clear Parent substructure - local Set = self.clientset.Set - if Client then - Set = {Client} - else - self:RemoveSubEntries(Parent) - end - for _,_client in pairs(Set) do - local client = _client -- Wrapper.Client#CLIENT - if client and client:IsAlive() then - local playername = client:GetPlayerName() - --self.structure[playername] = {} - for _id,_entry in UTILS.spairs(self.replacementstructure.generic) do - local entry = _entry -- #CLIENTMENU - local parent = Parent - self:T("Posted Parent = "..Parent.ID) - if entry.Parent and entry.Parent.name then - parent = self:_GetParentEntry(self.replacementstructure.generic,entry.Parent.name) or Parent - self:T("Found Parent = "..parent.ID) - end - self.structure[playername][_id] = CLIENTMENU:NewEntry(client,entry.name,parent,entry.Function,unpack(entry.Functionargs)) - self.structure[playername][_id].Once = entry.Once - end +-- @param #string Text Text or partial text of the menu entry to find +-- @return #table Table of matching UUIDs of #CLIENTMENU objects +-- @return #table Table of matching #CLIENTMENU objects +function CLIENTMENUMANAGER:FindUUIDsByText(Text) + self:I(self.lid.."FindUUIDsByText "..Text or "None") + local matches = {} + local entries = {} + for _uuid,_entry in pairs(self.flattree) do + local Entry = _entry -- #CLIENTMENU + if Entry and string.find(Entry.name,Text) then + table.insert(matches,_uuid) + table.insert(entries,Entry ) end end - self:_MergeReplacementData() + return matches, entries +end + +--- Find matching entries in the generic structure by the menu text. +-- @param #CLIENTMENUMANAGER self +-- @param #string Text Text or partial text of the F10 menu entry. +-- @return #table Table of matching #CLIENTMENU objects. +function CLIENTMENUMANAGER:FindEntriesByText(Text) + self:I(self.lid.."FindEntriesByText "..Text or "None") + local matches, objects = self:FindUUIDsByText(Text) + return objects +end + +--- Alter the text of a leaf entry in the generic structure and push to one specific client's F10 menu. +-- @param #CLIENTMENUMANAGER self +-- @param #CLIENTMENU Entry The menu entry. +-- @param #string Text New Text of the F10 menu entry. +-- @param Wrapper.Client#CLIENT Client (optional) The client for whom to alter the entry, if nil done for all clients. +-- @return #CLIENTMENUMANAGER self +function CLIENTMENUMANAGER:ChangeEntryText(Entry, Text, Client) + self:T(self.lid.."ChangeEntryText "..Text or "None") + local newentry = CLIENTMENU:NewEntry(nil,Text,Entry.Parent,Entry.Function,unpack(Entry.Functionargs)) + self:DeleteF10Entry(Entry,Client) + self:DeleteGenericEntry(Entry) + if not Entry.Parent then + self.rootentries[self.entrycount] = newentry + end + local depth = #newentry.path + if not self.menutree[depth] then self.menutree[depth] = {} end + table.insert(self.menutree[depth],newentry.UUID) + self.flattree[newentry.UUID] = newentry + self:AddEntry(newentry,Client) return self end ---- [Internal] Find a parent entry in a given structure by name. --- @param #CLIENTMENUMANAGER self --- @param #table Structure Table of entries. --- @param #string Name Name to find. --- @return #CLIENTMENU Entry -function CLIENTMENUMANAGER:_GetParentEntry(Structure,Name) - self:T(self.lid.."_GetParentEntry") - local found = nil - for _,_entry in pairs(Structure) do - local entry = _entry -- #CLIENTMENU - if entry.name == Name then - found = entry - break - end - end - return found -end - ---- Push the complete menu structure to each of the clients in the set. +--- Push the complete menu structure to each of the clients in the set - refresh the menu tree of the clients. -- @param #CLIENTMENUMANAGER self -- @param Wrapper.Client#CLIENT Client (optional) If given, propagate only for this client. -- @return #CLIENTMENU Entry function CLIENTMENUMANAGER:Propagate(Client) - self:T(self.lid.."Propagate") + self:I(self.lid.."Propagate") + self:I(Client) local Set = self.clientset.Set if Client then - Set = {Set} + Set = {Client} end + self:ResetMenu(Client) for _,_client in pairs(Set) do local client = _client -- Wrapper.Client#CLIENT if client and client:IsAlive() then local playername = client:GetPlayerName() - self.structure[playername] = {} - for _id,_entry in pairs(self.structure.generic) do - local entry = _entry -- #CLIENTMENU - local parent = nil - if entry.Parent and entry.Parent.name then - parent = self:_GetParentEntry(self.structure[playername],entry.Parent.name) + if not self.playertree[playername] then + self.playertree[playername] = {} + end + for level,branch in pairs (self.menutree) do + self:I("Building branch:" .. level) + for _,leaf in pairs(branch) do + self:I("Building leaf:" .. leaf) + local entry = self:FindEntryByUUID(leaf) + if entry then + self:I("Found generic entry:" .. entry.UUID) + local parent = nil + if entry.Parent and entry.Parent.UUID then + parent = self.playertree[playername][entry.Parent.UUID] or self:FindEntryByUUID(entry.Parent.UUID) + end + self.playertree[playername][entry.UUID] = CLIENTMENU:NewEntry(client,entry.name,parent,entry.Function,unpack(entry.Functionargs)) + self.playertree[playername][entry.UUID].Once = entry.Once + else + self:I("NO generic entry for:" .. leaf) + end end - self.structure[playername][_id] = CLIENTMENU:NewEntry(client,entry.name,parent,entry.Function,unpack(entry.Functionargs)) - self.structure[playername][_id].Once = entry.Once end end end @@ -686,85 +520,75 @@ function CLIENTMENUMANAGER:AddEntry(Entry,Client) local client = _client -- Wrapper.Client#CLIENT if client and client:IsAlive() then local playername = client:GetPlayerName() - local entry = Entry -- #CLIENTMENU - local parent = nil - if entry.Parent and entry.Parent.name then - parent = self:_GetParentEntry(self.structure[playername],entry.Parent.name) - end - self.structure[playername][Entry.ID] = CLIENTMENU:NewEntry(client,entry.name,parent,entry.Function,unpack(entry.Functionargs)) - self.structure[playername][Entry.ID].Once = entry.Once + if Entry then + self:I("Adding generic entry:" .. Entry.UUID) + local parent = nil + if Entry.Parent and Entry.Parent.UUID then + parent = self.playertree[playername][Entry.Parent.UUID] or self:FindEntryByUUID(Entry.Parent.UUID) + end + self.playertree[playername][Entry.UUID] = CLIENTMENU:NewEntry(client,Entry.name,parent,Entry.Function,unpack(Entry.Functionargs)) + self.playertree[playername][Entry.UUID].Once = Entry.Once + else + self:I("NO generic entry given") + end end end return self end ---- Blank out the menu - remove **all root entries** and all entries below from the client's menus, leaving the generic structure untouched. +--- Blank out the menu - remove **all root entries** and all entries below from the client's F10 menus, leaving the generic structure untouched. -- @param #CLIENTMENUMANAGER self --- @param Wrapper.Client#CLIENT Client +-- @param Wrapper.Client#CLIENT Client (optional) If given, remove only for this client. -- @return #CLIENTMENUMANAGER self function CLIENTMENUMANAGER:ResetMenu(Client) self:T(self.lid.."ResetMenu") for _,_entry in pairs(self.rootentries) do - local RootEntry = self.structure.generic[_entry] - if RootEntry then - self:Clear(RootEntry,Client) + --local RootEntry = self.structure.generic[_entry] + if _entry then + self:DeleteF10Entry(_entry,Client) end end return self end ---- Blank out the menu - remove **all root entries** and all entries below from all clients' menus, and **delete** the generic structure. +--- Blank out the menu - remove **all root entries** and all entries below from all clients' F10 menus, and **delete** the generic structure. -- @param #CLIENTMENUMANAGER self -- @return #CLIENTMENUMANAGER self function CLIENTMENUMANAGER:ResetMenuComplete() self:T(self.lid.."ResetMenuComplete") for _,_entry in pairs(self.rootentries) do - local RootEntry = self.structure.generic[_entry] - if RootEntry then - self:Clear(RootEntry) + --local RootEntry = self.structure.generic[_entry] + if _entry then + self:DeleteF10Entry(_entry) end end - self.structure = nil - self.structure = { - generic = {}, - IDs = {}, - } + self.playertree = nil + self.playertree = {} self.rootentries = nil self.rootentries = {} + self.menutree = nil + self.menutree = {} return self end ---- Remove the entry and all entries below the given entry from the client's menus and the generic structure. +--- 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. -- @return #CLIENTMENUMANAGER self -function CLIENTMENUMANAGER:Clear(Entry,Client) - self:T(self.lid.."Clear") - local rid = self.structure.IDs[Entry.ID] - if rid then - local generic = self.structure.generic[rid] - local Set = self.clientset.Set - if Client then - Set = {Client} - end - for _,_client in pairs(Set) do - local client = _client -- Wrapper.Client#CLIENT - if client and client:IsAlive() then - local playername = client:GetPlayerName() - local entry = self.structure[playername][rid] -- #CLIENTMENU - if entry then - entry:Clear() - self.structure[playername][rid] = nil - end - end - end - if not Client then - for _id,_entry in pairs(self.structure.generic) do - local entry = _entry -- #CLIENTMENU - if entry and entry.Parent and entry.Parent.ID and entry.Parent.ID == rid then - self.structure.IDs[entry.ID] = nil - entry = nil +function CLIENTMENUMANAGER:DeleteF10Entry(Entry,Client) + self:I(self.lid.."DeleteF10Entry") + local Set = self.clientset.Set + if Client then + Set = {Client} + end + for _,_client in pairs(Set) do + if _client and _client:IsAlive() then + local playername = _client:GetPlayerName() + if self.playertree[playername] then + local centry = self.playertree[playername][Entry.UUID] -- #CLIENTMENU + if centry then + centry:Clear() end end end @@ -772,115 +596,87 @@ function CLIENTMENUMANAGER:Clear(Entry,Client) return self end ---- [Internal] Clean up player shadow structure +--- Remove the entry and all entries below the given entry from the generic tree. -- @param #CLIENTMENUMANAGER self +-- @param #CLIENTMENU Entry The entry to remove -- @return #CLIENTMENUMANAGER self -function CLIENTMENUMANAGER:_CleanUpPlayerStructure() - self:T(self.lid.."_CleanUpPlayerStructure") - for _,_client in pairs(self.clientset.Set) do - local client = _client -- Wrapper.Client#CLIENT - if client and client:IsAlive() then - local playername = client:GetPlayerName() - local newstructure = {} - for _id, _entry in UTILS.spairs(self.structure[playername]) do - if self.structure.generic[_id] then - newstructure[_id] = _entry - end - end - self.structure[playername] = nil - self.structure[playername] = newstructure +function CLIENTMENUMANAGER:DeleteGenericEntry(Entry) + self:I(self.lid.."DeleteGenericEntry") + + if Entry.Children and #Entry.Children > 0 then + self:RemoveGenericSubEntries(Entry) + end + + local depth = #Entry.path + local uuid = Entry.UUID + + local tbl = UTILS.DeepCopy(self.menutree) + + if tbl[depth] then + for i=depth,#tbl do + self:I("Level = "..i) + for _id,_uuid in pairs(tbl[i]) do + self:I(_uuid) + if string.find(_uuid,uuid) then + self:I("Match for ".._uuid) + self.menutree[i][_id] = nil + self.flattree[_uuid] = nil + end + end end end + return self end ---- Remove all entries below the given entry from the clients' menus and the generic structure. +--- Remove all entries below the given entry from the generic tree. -- @param #CLIENTMENUMANAGER self --- @param #CLIENTMENU Entry The menu entry +-- @param #CLIENTMENU Entry The entry where to start. This entry stays. +-- @return #CLIENTMENUMANAGER self +function CLIENTMENUMANAGER:RemoveGenericSubEntries(Entry) + self:I(self.lid.."RemoveGenericSubEntries") + + local depth = #Entry.path + 1 + local uuid = Entry.UUID + + local tbl = UTILS.DeepCopy(self.menutree) + + if tbl[depth] then + for i=depth,#tbl do + self:I("Level = "..i) + for _id,_uuid in pairs(tbl[i]) do + self:I(_uuid) + if string.find(_uuid,uuid) then + self:I("Match for ".._uuid) + self.menutree[i][_id] = nil + self.flattree[_uuid] = nil + end + end + end + end + return self +end + + +--- Remove all entries below the given entry from the client's F10 menus. +-- @param #CLIENTMENUMANAGER self +-- @param #CLIENTMENU Entry The entry where to start. This entry stays. -- @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. -- @return #CLIENTMENUMANAGER self -function CLIENTMENUMANAGER:RemoveSubEntries(Entry,Client) - self:T(self.lid.."RemoveSubEntries") - local rid = self.structure.IDs[Entry.ID] - if rid then - local Set = self.clientset.Set - if Client then - Set = {Client} - end - for _,_client in pairs(Set) do - local client = _client -- Wrapper.Client#CLIENT - if client and client:IsAlive() then - local playername = client:GetPlayerName() - local entry = self.structure[playername][rid] -- #CLIENTMENU - if entry then - entry:RemoveSubEntries() - end +function CLIENTMENUMANAGER:RemoveF10SubEntries(Entry,Client) + self:I(self.lid.."RemoveSubEntries") + local Set = self.clientset.Set + if Client then + Set = {Client} + end + for _,_client in pairs(Set) do + if _client and _client:IsAlive() then + local playername = _client:GetPlayerName() + if self.playertree[playername] then + local centry = self.playertree[playername][Entry.UUID] -- #CLIENTMENU + centry:RemoveSubEntries() end end - if not Client then - for _id,_entry in pairs(self.structure.generic) do - local entry = _entry -- #CLIENTMENU - if entry and entry.Parent and entry.Parent.ID and entry.Parent.ID == rid then - self.structure.IDs[entry.ID] = nil - self.structure.generic[_id] = nil - end - end - end - end - self:_CleanUpPlayerStructure() - return self -end - ---- Remove a specific entry by ID from the generic structure --- @param #CLIENTMENUMANAGER self --- @param #number ID --- @return #CLIENTMENUMANAGER self -function CLIENTMENUMANAGER:_RemoveByID(ID) - self:T(self.lid.."_RemoveByID "..ID or "none") - if ID then - local gid = self.structure.IDs[ID] - if gid then - self.structure.generic[gid] = nil - self.structure.IDs[ID] = nil - end - end - return self -end - ---- [Internal] Dump structures to log for debug --- @param #CLIENTMENUMANAGER self --- @param #string Playername --- @return #CLIENTMENUMANAGER self -function CLIENTMENUMANAGER:_CheckStructures(Playername) - self:T(self.lid.."CheckStructures") - self:I("Generic Structure") - self:I("-----------------") - for _id,_entry in UTILS.spairs(self.structure.generic) do - local ID = "none" - if _entry and _entry.ID then - ID = _entry.ID - end - self:I("ID= ".._id.." | EntryID = "..ID) - if _id > 10 and _id < 14 then - self:I(_entry.name) - end - end - self:I("Reverse Structure") - self:I("-----------------") - for _id,_entry in UTILS.spairs(self.structure.IDs) do - self:I("EntryID= ".._id.." | ID = ".._entry) - end - if Playername then - self:I("Player Structure") - self:I("-----------------") - for _id,_entry in UTILS.spairs(self.structure[Playername]) do - local ID = "none" - if _entry and _entry.ID then - ID = _entry.ID - end - local _lid = _id or "none" - self:I("ID= ".._lid.." | EntryID = "..ID) - end end return self end diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 94f1b35c8..c7669ce73 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -988,6 +988,7 @@ do -- @field #table PlayerJoinMenu -- @field #table PlayerInfoMenu -- @field #boolean noflaresmokemenu +-- @field #boolean illumenu -- @field #boolean TransmitOnlyWithPlayers -- @field #boolean buddylasing -- @field Ops.PlayerRecce#PLAYERRECCE PlayerRecce @@ -999,6 +1000,13 @@ do -- @field #table PlayerMenuTag -- @field #boolean UseTypeNames -- @field Functional.Scoring#SCORING Scoring +-- @field Core.ClientMenu#CLIENTMENUMANAGER JoinTaskMenuTemplate +-- @field Core.ClientMenu#CLIENTMENU JoinMenu +-- @field Core.ClientMenu#CLIENTMENU JoinTopMenu +-- @field Core.ClientMenu#CLIENTMENU JoinInfoMenu +-- @field Core.ClientMenu#CLIENTMENUMANAGER ActiveTaskMenuTemplate +-- @field Core.ClientMenu#CLIENTMENU ActiveTopMenu +-- @field Core.ClientMenu#CLIENTMENU ActiveInfoMenu -- @extends Core.Fsm#FSM --- @@ -1155,6 +1163,7 @@ do -- MENUMARK = "Mark on map", -- MENUSMOKE = "Smoke", -- MENUFLARE = "Flare", +-- MENUILLU = "Illuminate", -- MENUABORT = "Abort", -- MENUJOIN = "Join Task", -- MENUTASKINFO = Task Info", @@ -1315,6 +1324,7 @@ PLAYERTASKCONTROLLER = { PlayerInfoMenu = {}, PlayerMenuTag = {}, noflaresmokemenu = false, + illumenu = false, TransmitOnlyWithPlayers = true, buddylasing = false, PlayerRecce = nil, @@ -1408,6 +1418,7 @@ PLAYERTASKCONTROLLER.Messages = { MENUMARK = "Mark on map", MENUSMOKE = "Smoke", MENUFLARE = "Flare", + MENUILLU = "Illuminate", MENUABORT = "Abort", MENUJOIN = "Join Task", MENUTASKINFO = "Task Info", @@ -1487,6 +1498,7 @@ PLAYERTASKCONTROLLER.Messages = { MENUMARK = "Kartenmarkierung", MENUSMOKE = "Rauchgranate", MENUFLARE = "Leuchtgranate", + MENUILLU = "Feldbeleuchtung", MENUABORT = "Abbrechen", MENUJOIN = "Auftrag annehmen", MENUTASKINFO = "Auftrag Briefing", @@ -1592,6 +1604,7 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter) self.CallsignTranslations = nil self.noflaresmokemenu = false + self.illumenu = false self.ShowMagnetic = true @@ -1849,6 +1862,24 @@ function PLAYERTASKCONTROLLER:SetEnableSmokeFlareTask() return self end +--- [User] Show menu entries to illuminate targets. Needs smoke/flare enabled. +-- @param #PLAYERTASKCONTROLLER self +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:SetEnableIlluminateTask() + self:T(self.lid.."SetEnableSmokeFlareTask") + self.illumenu = true + return self +end + +--- [User] Do not show menu entries to illuminate targets. +-- @param #PLAYERTASKCONTROLLER self +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:SetDisableIlluminateTask() + self:T(self.lid.."SetDisableIlluminateTask") + self.illumenu = false + return self +end + --- [User] Show info text on screen with a coordinate info in any case (OFF by default) -- @param #PLAYERTASKCONTROLLER self -- @param #boolean OnOff Switch on = true or off = false @@ -2140,7 +2171,7 @@ end -- @param Core.Event#EVENTDATA EventData -- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:_EventHandler(EventData) - self:T(self.lid.."_EventHandler: "..EventData.id) + self:I(self.lid.."_EventHandler: "..EventData.id) 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) @@ -2168,7 +2199,7 @@ function PLAYERTASKCONTROLLER:_EventHandler(EventData) if self.ClientSet:IsNotInSet(CLIENT:FindByName(EventData.IniUnitName)) then return self end - self:T(self.lid.."Event for player: "..EventData.IniPlayerName) + self:I(self.lid.."Event for player: "..EventData.IniPlayerName) local frequency = self.Frequency local freqtext = "" if type(frequency) == "table" then @@ -2198,7 +2229,11 @@ function PLAYERTASKCONTROLLER:_EventHandler(EventData) if EventData.IniPlayerName then self.PlayerMenu[EventData.IniPlayerName] = nil --self:_BuildMenus(CLIENT:FindByName(EventData.IniUnitName)) - self:_BuildMenus(CLIENT:FindByPlayerName(EventData.IniPlayerName)) + --self:_BuildMenus(CLIENT:FindByPlayerName(EventData.IniPlayerName)) + local player = CLIENT:FindByName(EventData.IniUnitName) + self:I({player}) + local controller = self.JoinTaskMenuTemplate + controller:Propagate(player) end end end @@ -2308,7 +2343,7 @@ function PLAYERTASKCONTROLLER:_GetTasksPerType() self:T(self.lid.."_GetTasksPerType") local tasktypes = self:_GetAvailableTaskTypes() - --self:T({tasktypes}) + -- self:I({tasktypes}) -- Sort tasks per threat level first local datatable = self.TaskQueue:GetDataTable() @@ -2965,12 +3000,12 @@ end --- [Internal] Join a player to a task -- @param #PLAYERTASKCONTROLLER self --- @param Wrapper.Group#GROUP Group --- @param Wrapper.Client#CLIENT Client -- @param Ops.PlayerTask#PLAYERTASK Task -- @param #boolean Force Assign task even if client already has one +-- @param Wrapper.Group#GROUP Group +-- @param Wrapper.Client#CLIENT Client -- @return #PLAYERTASKCONTROLLER self -function PLAYERTASKCONTROLLER:_JoinTask(Group, Client, Task, Force) +function PLAYERTASKCONTROLLER:_JoinTask(Task, Force, Group, Client) self:T(self.lid.."_JoinTask") local playername, ttsplayername = self:_GetPlayerName(Client) if self.TasksPerPlayer:HasUniqueID(playername) and not Force then @@ -3004,7 +3039,7 @@ function PLAYERTASKCONTROLLER:_JoinTask(Group, Client, Task, Force) self.TasksPerPlayer:Push(Task,playername) self:__PlayerJoinedTask(1, Group, Client, Task) -- clear menu - self:_BuildMenus(Client,true) + --self:_BuildMenus(Client,true) end if Task.Type == AUFTRAG.Type.PRECISIONBOMBING then if not self.PrecisionTasks:HasUniqueID(Task.PlayerTaskNr) then @@ -3065,11 +3100,11 @@ end --- [Internal] Show active task info -- @param #PLAYERTASKCONTROLLER self +-- @param Ops.PlayerTask#PLAYERTASK Task -- @param Wrapper.Group#GROUP Group -- @param Wrapper.Client#CLIENT Client --- @param Ops.PlayerTask#PLAYERTASK Task -- @return #PLAYERTASKCONTROLLER self -function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Group, Client, Task) +function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Task, Group, Client) self:T(self.lid.."_ActiveTaskInfo") local playername, ttsplayername = self:_GetPlayerName(Client) local text = "" @@ -3389,7 +3424,7 @@ function PLAYERTASKCONTROLLER:_AbortTask(Group, Client) if not self.NoScreenOutput then local m=MESSAGE:New(text,15,"Tasking"):ToClient(Client) end - self:_BuildMenus(Client,true) + --self:_BuildMenus(Client,true) return self end @@ -3454,6 +3489,150 @@ function PLAYERTASKCONTROLLER:_BuildTaskInfoMenu(group,client,playername,topmenu return taskinfomenu end +-- TODO - New Menu Manager + +function PLAYERTASKCONTROLLER:_UpdateJoinMenuTemplate() + self:I("_UpdateJoinMenuTemplate") + if self.TaskQueue:Count() > 0 then + local taskpertype = self:_GetTasksPerType() + local JoinMenu = self.JoinMenu -- Core.ClientMenu#CLIENTMENU + self:I(JoinMenu.path) + local controller = self.JoinTaskMenuTemplate -- Core.ClientMenu#CLIENTMENUMANAGER + local actcontroller = self.ActiveTaskMenuTemplate -- Core.ClientMenu#CLIENTMENUMANAGER + local entrynumbers = {} + local existingentries = {} + local maxn = self.menuitemlimit + -- Generate task type menu items + for _type,_ in pairs(taskpertype) do + --local found = controller:FindEntryByText(_type) + local found, text = controller:FindEntryUnderParentByText(JoinMenu,_type) + self:I(text) + if not found then + local newentry = controller:NewEntry(_type,JoinMenu) + controller:AddEntry(newentry) + if self.JoinInfoMenu then + local newentry = controller:NewEntry(_type,self.JoinInfoMenu) + controller:AddEntry(newentry) + end + entrynumbers[_type] = 0 + existingentries[_type] = {} + else + entrynumbers[_type] = #found.Children + if #taskpertype[_type] > 0 and entrynumbers[_type] < maxn then + existingentries[_type] = {} + for _,_entry in pairs(found.Children) do + local entry = _entry -- Core.ClientMenu#CLIENTMENU + table.insert(existingentries[_type],_entry.name) + end + if self.verbose then + UTILS.PrintTableToLog(existingentries) + end + end + end + end + + local function FindInList(List,Text) + local found = false + for _,_name in pairs(List) do + if string.match(_name,Text) then + found = true + break + end + end + return found + end + + end + return self +end + +function PLAYERTASKCONTROLLER:_CreateJoinMenuTemplate() + self:I("_CreateActiveTaskMenuTemplate") + + local menujoin = self.gettext:GetEntry("MENUJOIN",self.locale) + local menunotasks = self.gettext:GetEntry("MENUNOTASKS",self.locale) + local flashtext = self.gettext:GetEntry("FLASHMENU",self.locale) + + local JoinTaskMenuTemplate = CLIENTMENUMANAGER:New(self.ClientSet,"JoinTask") + + if not self.JoinTopMenu then + local taskings = self.gettext:GetEntry("MENUTASKING",self.locale) + local longname = self.Name..taskings..self.Type + local menuname = self.MenuName or longname + self.JoinTopMenu = JoinTaskMenuTemplate:NewEntry(menuname,self.MenuParent) + end + + if self.AllowFlash then + JoinTaskMenuTemplate:NewEntry(flashtext,self.JoinTopMenu,self._SwitchFlashing,self,group,client) + end + + self.JoinMenu = JoinTaskMenuTemplate:NewEntry(menujoin,self.JoinTopMenu) + + if self.taskinfomenu then + local menutaskinfo = self.gettext:GetEntry("MENUTASKINFO",self.locale) + self.JoinInfoMenu = JoinTaskMenuTemplate:NewEntry(menutaskinfo,self.JoinTopMenu) + end + + if self.TaskQueue:Count() == 0 then + JoinTaskMenuTemplate:NewEntry(menunotasks,self.JoinMenu) + end + + self.JoinTaskMenuTemplate = JoinTaskMenuTemplate + + return self +end + + +function PLAYERTASKCONTROLLER:_CreateActiveTaskMenuTemplate() + self:I("_CreateActiveTaskMenuTemplate") + + local menuactive = self.gettext:GetEntry("MENUACTIVE",self.locale) + local menuinfo = self.gettext:GetEntry("MENUINFO",self.locale) + local menumark = self.gettext:GetEntry("MENUMARK",self.locale) + local menusmoke = self.gettext:GetEntry("MENUSMOKE",self.locale) + local menuflare = self.gettext:GetEntry("MENUFLARE",self.locale) + local menuillu = self.gettext:GetEntry("MENUILLU",self.locale) + local menuabort = self.gettext:GetEntry("MENUABORT",self.locale) + + local ActiveTaskMenuTemplate = CLIENTMENUMANAGER:New(self.ClientSet,"ActiveTask") + + if not self.ActiveTopMenu then + local taskings = self.gettext:GetEntry("MENUTASKING",self.locale) + local longname = self.Name..taskings..self.Type + local menuname = self.MenuName or longname + self.ActiveTopMenu = ActiveTaskMenuTemplate:NewEntry(menuname,self.MenuParent) + end + + if self.AllowFlash then + local flashtext = self.gettext:GetEntry("FLASHMENU",self.locale) + ActiveTaskMenuTemplate:NewEntry(flashtext,self.ActiveTopMenu,self._SwitchFlashing,self,group,client) + end + + local active = ActiveTaskMenuTemplate:NewEntry(menuactive,self.ActiveTopMenu) + ActiveTaskMenuTemplate:NewEntry(menuinfo,active,self._ActiveTaskInfo,self,nil) + ActiveTaskMenuTemplate:NewEntry(menumark,active,self._MarkTask,self,nil) + + if self.Type ~= PLAYERTASKCONTROLLER.Type.A2A and self.noflaresmokemenu ~= true then + ActiveTaskMenuTemplate:NewEntry(menusmoke,active,self._SmokeTask,self,group,client) + ActiveTaskMenuTemplate:NewEntry(menuflare,active,self._FlareTask,self,group,client) + + if self.illumenu then + ActiveTaskMenuTemplate:NewEntry(menuillu,active,self._IlluminateTask,self,group,client) + end + + end + + ActiveTaskMenuTemplate:NewEntry(menuabort,active,self._AbortTask,self,group,client) + self.ActiveTaskMenuTemplate = ActiveTaskMenuTemplate + + if self.taskinfomenu and self.activehasinfomenu then + local menutaskinfo = self.gettext:GetEntry("MENUTASKINFO",self.locale) + self.ActiveInfoMenu = ActiveTaskMenuTemplate:NewEntry(menutaskinfo,self.ActiveTopMenu) + end + + return self +end + --- [Internal] Build client menus -- @param #PLAYERTASKCONTROLLER self -- @param Wrapper.Client#CLIENT Client (optional) build for this client name only @@ -3965,6 +4144,20 @@ end -- TODO: FSM Functions PLAYERTASKCONTROLLER ------------------------------------------------------------------------------------------------------------------- +--- [Internal] On after start call +-- @param #PLAYERTASKCONTROLLER self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:onafterStart(From, Event, To) + self:I({From, Event, To}) + self:I(self.lid.."onafterStart") + self:_CreateJoinMenuTemplate() + self:_CreateActiveTaskMenuTemplate() + return self +end + --- [Internal] On after Status call -- @param #PLAYERTASKCONTROLLER self -- @param #string From @@ -3994,7 +4187,9 @@ function PLAYERTASKCONTROLLER:onafterStatus(From, Event, To) end end - self:_BuildMenus(nil,enforcedmenu) + --self:_BuildMenus(nil,enforcedmenu) + + self:_UpdateJoinMenuTemplate() if self.verbose then local text = string.format("%s | New Targets: %02d | Active Tasks: %02d | Active Players: %02d | Assigned Tasks: %02d",self.MenuName, targetcount,taskcount,playercount,assignedtasks) @@ -4067,7 +4262,7 @@ function PLAYERTASKCONTROLLER:onafterTaskSuccess(From, Event, To, Task) end local clients=Task:GetClientObjects() for _,client in pairs(clients) do - self:_BuildMenus(client,true,true) + --self:_BuildMenus(client,true,true) end return self end