From 7d48ce2a7e9c4a422f0d245bf8498af4a434b538 Mon Sep 17 00:00:00 2001 From: Alex Dcnh <140754794+Wishmaster117@users.noreply.github.com> Date: Wed, 13 May 2026 17:36:52 +0200 Subject: [PATCH] Improve bot quest result frames and add abandon actions --- Locales/MultiBotAceLocale-deDE.lua | 1 + Locales/MultiBotAceLocale-enGB.lua | 1 + Locales/MultiBotAceLocale-enUS.lua | 1 + Locales/MultiBotAceLocale-esES.lua | 1 + Locales/MultiBotAceLocale-frFR.lua | 1 + Locales/MultiBotAceLocale-koKR.lua | 1 + Locales/MultiBotAceLocale-ruRU.lua | 1 + Locales/MultiBotAceLocale-zhCN.lua | 1 + README.md | 2 +- TODO.md | 2 +- UI/MultiBotQuestAllFrame.lua | 10 ++- UI/MultiBotQuestCompletedFrame.lua | 25 +++++++- UI/MultiBotQuestIncompleteFrame.lua | 25 +++++++- UI/MultiBotQuestUIShared.lua | 99 ++++++++++++++++++++++++++++- 14 files changed, 162 insertions(+), 9 deletions(-) diff --git a/Locales/MultiBotAceLocale-deDE.lua b/Locales/MultiBotAceLocale-deDE.lua index fb805ec..6b7da67 100644 --- a/Locales/MultiBotAceLocale-deDE.lua +++ b/Locales/MultiBotAceLocale-deDE.lua @@ -517,6 +517,7 @@ local deDEValues = { ["tips.quests.alllist"] = "Alle Quests des/der Bot(s)", ["tips.quests.compheader"] = "** Abgeschlossene Quests **", ["tips.quests.incompheader"] = "** Unvollständige Quests **", + ["tips.quests.drop"] = "Aufgeben", ["tips.quests.botsword"] = "Bots : ", ["tips.quests.gobsmaster"] = "GameObjects-Menü öffnen\n|cffffffffDieser Button öffnet das GameObjects-Menü.|r\n\n|cffff0000Linksklick zum Öffnen|r\n|cff999999(Ausführungsreihenfolge: System)|r", ["tips.quests.gobenter"] = "GameObject benutzen\n|cffffffffDieser Button öffnet ein Feld zur Eingabe des GameObject-Namens.\n\n|cffff0000Linksklick zum Öffnen des Eingabefeldes|r\n|cff999999(Ausführungsreihenfolge: Bot)|r", diff --git a/Locales/MultiBotAceLocale-enGB.lua b/Locales/MultiBotAceLocale-enGB.lua index 3f76274..84fa412 100644 --- a/Locales/MultiBotAceLocale-enGB.lua +++ b/Locales/MultiBotAceLocale-enGB.lua @@ -519,6 +519,7 @@ local enGBValues = { ["tips.quests.alllist"] = "All Quests for Bot(s)", ["tips.quests.compheader"] = "** Complete Quests **", ["tips.quests.incompheader"] = "** Incomplete Quests **", + ["tips.quests.drop"] = "Abandon", ["tips.quests.botsword"] = "Bots : ", ["tips.quests.gobsmaster"] = "Game Objects Menu\n|cffffffffOpens the Use Game Objects Menu.|r\n\n|cffff0000Left-click to Open|r\n|cff999999(Executed by: System)|r", ["tips.quests.gobenter"] = "Use Game Object\n|cffffffffOpens a prompt to enter Game Object Name.\n\n|cffff0000Left-click to open prompt|r\n|cff999999(Executed by: Bot)|r", diff --git a/Locales/MultiBotAceLocale-enUS.lua b/Locales/MultiBotAceLocale-enUS.lua index 543add3..ee5541a 100644 --- a/Locales/MultiBotAceLocale-enUS.lua +++ b/Locales/MultiBotAceLocale-enUS.lua @@ -519,6 +519,7 @@ local enUSValues = { ["tips.quests.alllist"] = "All Quests for Bot(s)", ["tips.quests.compheader"] = "** Complete Quests **", ["tips.quests.incompheader"] = "** Incomplete Quests **", + ["tips.quests.drop"] = "Abandon", ["tips.quests.botsword"] = "Bots : ", ["tips.quests.gobsmaster"] = "Game Objects Menu\n|cffffffffOpens the Use Game Objects Menu.|r\n\n|cffff0000Left-click to Open|r\n|cff999999(Executed by: System)|r", ["tips.quests.gobenter"] = "Use Game Object\n|cffffffffOpens a prompt to enter Game Object Name.\n\n|cffff0000Left-click to open prompt|r\n|cff999999(Executed by: Bot)|r", diff --git a/Locales/MultiBotAceLocale-esES.lua b/Locales/MultiBotAceLocale-esES.lua index a157bae..f879b93 100644 --- a/Locales/MultiBotAceLocale-esES.lua +++ b/Locales/MultiBotAceLocale-esES.lua @@ -517,6 +517,7 @@ local esESValues = { ["tips.quests.alllist"] = "Todas las misiones del/de los bot(s)", ["tips.quests.compheader"] = "** Misiones completadas **", ["tips.quests.incompheader"] = "** Misiones incompletas **", + ["tips.quests.drop"] = "Abandonar", ["tips.quests.botsword"] = "Bots : ", ["tips.quests.gobsmaster"] = "Menú de Objetos del Juego\n|cffffffffEste botón abre el menú para usar objetos del juego.|r\n\n|cffff0000Clic izquierdo para abrir|r\n|cff999999(Orden de ejecución: Sistema)|r", ["tips.quests.gobenter"] = "Usar objeto del juego\n|cffffffffEste botón abre un cuadro para ingresar el nombre del objeto del juego.\n\n|cffff0000Clic izquierdo para abrir el cuadro|r\n|cff999999(Orden de ejecución: Bot)|r", diff --git a/Locales/MultiBotAceLocale-frFR.lua b/Locales/MultiBotAceLocale-frFR.lua index d67e6ac..19ca5a5 100644 --- a/Locales/MultiBotAceLocale-frFR.lua +++ b/Locales/MultiBotAceLocale-frFR.lua @@ -517,6 +517,7 @@ local frFRValues = { ["tips.quests.alllist"] = "Toutes les quêtes du/des bot(s)", ["tips.quests.compheader"] = "** Quêtes Terminées **", ["tips.quests.incompheader"] = "** Quêtes Incomplètes **", + ["tips.quests.drop"] = "Abandonner", ["tips.quests.botsword"] = "Bots : ", ["tips.quests.gobsmaster"] = "Menu Objets du Jeu\n|cffffffffCe bouton ouvre le menu pour utiliser des objets du jeu.|r\n\n|cffff0000Clic-Gauche pour ouvrir|r\n|cff999999(Ordre d'exécution : Système)|r", ["tips.quests.gobenter"] = "Utiliser un Objet du Jeu\n|cffffffffCe bouton ouvre une fenêtre pour entrer le nom de l'objet du jeu.\n\n|cffff0000Clic-Gauche pour ouvrir la fenêtre|r\n|cff999999(Ordre d'exécution : Bot)|r", diff --git a/Locales/MultiBotAceLocale-koKR.lua b/Locales/MultiBotAceLocale-koKR.lua index 757d1e8..f7d3a3f 100644 --- a/Locales/MultiBotAceLocale-koKR.lua +++ b/Locales/MultiBotAceLocale-koKR.lua @@ -516,6 +516,7 @@ local koKRValues = { ["tips.quests.alllist"] = "모든 봇의 퀘스트 목록", ["tips.quests.compheader"] = "** 완료한 퀘스트 **", ["tips.quests.incompheader"] = "** 미완료 퀘스트 **", + ["tips.quests.drop"] = "포기", ["tips.quests.botsword"] = "봇 : ", ["tips.quests.gobsmaster"] = "게임 오브젝트 메뉴 열기\n|cffffffff이 버튼을 누르면 게임 오브젝트 사용 메뉴를 엽니다.|r\n\n|cffff0000왼쪽 클릭: 열기|r\n|cff999999(실행 순서: 시스템)|r", ["tips.quests.gobenter"] = "게임 오브젝트 사용\n|cffffffff이 버튼을 누르면 게임 오브젝트 이름을 입력할 수 있습니다.\n\n|cffff0000왼쪽 클릭: 입력창 열기|r\n|cff999999(실행 순서: 봇)|r", diff --git a/Locales/MultiBotAceLocale-ruRU.lua b/Locales/MultiBotAceLocale-ruRU.lua index c3ff7ec..bb1f789 100644 --- a/Locales/MultiBotAceLocale-ruRU.lua +++ b/Locales/MultiBotAceLocale-ruRU.lua @@ -517,6 +517,7 @@ local ruRUValues = { ["tips.quests.alllist"] = "Все квесты бота(ов)", ["tips.quests.compheader"] = "** Выполненные квесты **", ["tips.quests.incompheader"] = "** Невыполненные квесты **", + ["tips.quests.drop"] = "Отказаться", ["tips.quests.botsword"] = "Боты : ", ["tips.quests.gobsmaster"] = "Меню игровых объектов\n|cffffffffЭта кнопка открывает меню для использования игровых объектов.|r\n\n|cffff0000Левый клик — открыть|r\n|cff999999(Порядок выполнения: система)|r", ["tips.quests.gobenter"] = "Использовать игровой объект\n|cffffffffЭта кнопка открывает окно для ввода названия игрового объекта.\n\n|cffff0000Левый клик — открыть окно ввода|r\n|cff999999(Порядок выполнения: бот)|r", diff --git a/Locales/MultiBotAceLocale-zhCN.lua b/Locales/MultiBotAceLocale-zhCN.lua index 81b1487..09f701b 100644 --- a/Locales/MultiBotAceLocale-zhCN.lua +++ b/Locales/MultiBotAceLocale-zhCN.lua @@ -517,6 +517,7 @@ local zhCNValues = { ["tips.quests.alllist"] = "机器人所有任务", ["tips.quests.compheader"] = "** 已完成任务 **", ["tips.quests.incompheader"] = "** 未完成任务 **", + ["tips.quests.drop"] = "放弃", ["tips.quests.botsword"] = "机器人:", ["tips.quests.gobsmaster"] = "使用游戏对象菜单\n|cffffffff此按钮打开使用游戏对象的菜单。|r\n\n|cffff0000左键点击打开|r\n|cff999999(执行顺序:系统)|r", ["tips.quests.gobenter"] = "使用游戏对象\n|cffffffff此按钮弹出窗口输入游戏对象名称。\n\n|cffff0000左键点击打开输入窗口|r\n|cff999999(执行顺序:机器人)|r", diff --git a/README.md b/README.md index d9e9683..d063a89 100644 --- a/README.md +++ b/README.md @@ -211,7 +211,7 @@ The goal is to remove automatic UI-refresh spam. Quests - Bridge-first incompleted, completed and all quest lists + Bridge-first incompleted, completed and all quest lists with dark result frames and per-bot abandon buttons Game object search diff --git a/TODO.md b/TODO.md index 40917c2..8ed3657 100644 --- a/TODO.md +++ b/TODO.md @@ -52,7 +52,6 @@ ## Améliorations UI encore ouvertes -* Uniformiser le template des frames de quêtes avec le style d'Itemus. * Uniformiser le template de la frame reward avec le style d'Itemus. * Ajouter une option pour choisir la taille des icônes de la main bar et des Quick Shaman / Quick Hunter. * Voir quelles autres options utiles peuvent être ajoutées à la frame options de MultiBot. @@ -102,6 +101,7 @@ * incompleted ; * completed ; * all. +* Frames de quêtes `all`, `completed` et `incompleted` uniformisées avec fond interne sombre, marges cohérentes et bouton `Abandonner` par quête de bot. * Outfits bridge-first. * Character Info bridge-first. * Réputations bridge-first dans la frame Infos personnage. diff --git a/UI/MultiBotQuestAllFrame.lua b/UI/MultiBotQuestAllFrame.lua index 1688d6e..156cba6 100644 --- a/UI/MultiBotQuestAllFrame.lua +++ b/UI/MultiBotQuestAllFrame.lua @@ -72,6 +72,11 @@ local function createQuestRow(self, questID, text) row:SetFullWidth(true) row:SetLayout("Flow") + local spacer = self.aceGUI:Create("Label") + spacer:SetText("") + spacer:SetWidth(Shared.QUEST_ROW_LEFT_PADDING or 12) + row:AddChild(spacer) + local icon = self.aceGUI:Create("Icon") icon:SetImage(Shared.ICON_BOT_QUEST or "Interface\\Icons\\inv_misc_note_02") icon:SetImageSize(12, 12) @@ -195,6 +200,7 @@ function MultiBot.InitializeQuestAllFrame() window:SetHeight(460) window:EnableResize(false) window:SetLayout("Fill") + Shared.ApplyWindowContentStyle(window, 0.90) local strataLevel = MultiBot.GetGlobalStrataLevel and MultiBot.GetGlobalStrataLevel() if strataLevel then window.frame:SetFrameStrata(strataLevel) @@ -210,6 +216,8 @@ function MultiBot.InitializeQuestAllFrame() content:SetLayout("List") window:AddChild(content) + Shared.AddTopPadding(aceGUI, content) + local summary = aceGUI:Create("Label") summary:SetFullWidth(true) summary:SetText("") @@ -241,4 +249,4 @@ function MultiBot.InitializeQuestAllFrame() MultiBot.tBotAllPopup = window return QuestAllFrame -end \ No newline at end of file +end diff --git a/UI/MultiBotQuestCompletedFrame.lua b/UI/MultiBotQuestCompletedFrame.lua index a867274..d613ee2 100644 --- a/UI/MultiBotQuestCompletedFrame.lua +++ b/UI/MultiBotQuestCompletedFrame.lua @@ -54,6 +54,15 @@ local function resolveBotQuestBucket(store, botName) return EMPTY_TABLE end +local function dropBotQuest(botName, entry) + if not Shared.SendDropQuest(botName, entry) then + return + end + + Shared.RemoveQuestEntryFromBucket(resolveBotQuestBucket(getBotQuestsCompletedStore(), botName), entry) + MultiBot.BuildBotCompletedList(botName) +end + function MultiBot.BuildBotCompletedList(botName) local frame = MultiBot.InitializeQuestCompletedFrame() local completedStore = getBotQuestsCompletedStore() @@ -61,7 +70,13 @@ function MultiBot.BuildBotCompletedList(botName) frame:Show() Shared.RenderQuestEntries(frame, entries, { - summaryText = botName and ("|cff80ff80" .. botName .. "|r") or (MultiBot.L("tips.quests.complist") or ""), + summaryText = botName and ((Shared.QUEST_SUMMARY_PREFIX or " ") .. "|cff80ff80" .. botName .. "|r") or (MultiBot.L("tips.quests.complist") or ""), + rowOptions = botName and { + leftPadding = Shared.QUEST_ROW_LEFT_PADDING or 12, + onDrop = function(entry) + dropBotQuest(botName, entry) + end, + } or nil, }) end @@ -72,6 +87,9 @@ function MultiBot.BuildAggregatedCompletedList() frame:Show() Shared.RenderQuestEntries(frame, entries, { summaryText = "", + rowOptions = { + leftPadding = Shared.QUEST_ROW_LEFT_PADDING or 12, + }, }) end @@ -97,6 +115,7 @@ function MultiBot.InitializeQuestCompletedFrame() window:SetHeight(420) window:EnableResize(false) window:SetLayout("Fill") + Shared.ApplyWindowContentStyle(window, 0.90) local strataLevel = MultiBot.GetGlobalStrataLevel and MultiBot.GetGlobalStrataLevel() if strataLevel then window.frame:SetFrameStrata(strataLevel) @@ -112,6 +131,8 @@ function MultiBot.InitializeQuestCompletedFrame() content:SetLayout("List") window:AddChild(content) + Shared.AddTopPadding(aceGUI, content) + local summary = aceGUI:Create("Label") summary:SetFullWidth(true) summary:SetText("") @@ -138,4 +159,4 @@ function MultiBot.InitializeQuestCompletedFrame() MultiBot.tBotCompPopup = window return QuestCompletedFrame -end \ No newline at end of file +end diff --git a/UI/MultiBotQuestIncompleteFrame.lua b/UI/MultiBotQuestIncompleteFrame.lua index 5238b02..4d0ca82 100644 --- a/UI/MultiBotQuestIncompleteFrame.lua +++ b/UI/MultiBotQuestIncompleteFrame.lua @@ -54,6 +54,15 @@ local function resolveBotQuestBucket(store, botName) return EMPTY_TABLE end +local function dropBotQuest(botName, entry) + if not Shared.SendDropQuest(botName, entry) then + return + end + + Shared.RemoveQuestEntryFromBucket(resolveBotQuestBucket(getBotQuestsIncompletedStore(), botName), entry) + MultiBot.BuildBotQuestList(botName) +end + function MultiBot.BuildBotQuestList(botName) local frame = MultiBot.InitializeQuestIncompleteFrame and MultiBot.InitializeQuestIncompleteFrame() if not frame then @@ -64,7 +73,13 @@ function MultiBot.BuildBotQuestList(botName) frame:Show() Shared.RenderQuestEntries(frame, entries, { - summaryText = botName and ("|cff80ff80" .. botName .. "|r") or (MultiBot.L("tips.quests.incomplist") or ""), + summaryText = botName and ((Shared.QUEST_SUMMARY_PREFIX or " ") .. "|cff80ff80" .. botName .. "|r") or (MultiBot.L("tips.quests.incomplist") or ""), + rowOptions = botName and { + leftPadding = Shared.QUEST_ROW_LEFT_PADDING or 12, + onDrop = function(entry) + dropBotQuest(botName, entry) + end, + } or nil, }) end @@ -75,6 +90,9 @@ function MultiBot.BuildAggregatedQuestList() frame:Show() Shared.RenderQuestEntries(frame, entries, { summaryText = "", + rowOptions = { + leftPadding = Shared.QUEST_ROW_LEFT_PADDING or 12, + }, }) end @@ -100,6 +118,7 @@ function MultiBot.InitializeQuestIncompleteFrame() window:SetHeight(420) window:EnableResize(false) window:SetLayout("Fill") + Shared.ApplyWindowContentStyle(window, 0.90) local strataLevel = MultiBot.GetGlobalStrataLevel and MultiBot.GetGlobalStrataLevel() if strataLevel then window.frame:SetFrameStrata(strataLevel) @@ -115,6 +134,8 @@ function MultiBot.InitializeQuestIncompleteFrame() content:SetLayout("List") window:AddChild(content) + Shared.AddTopPadding(aceGUI, content) + local summary = aceGUI:Create("Label") summary:SetFullWidth(true) summary:SetText("") @@ -141,4 +162,4 @@ function MultiBot.InitializeQuestIncompleteFrame() MultiBot.tBotPopup = window return QuestIncompleteFrame -end \ No newline at end of file +end diff --git a/UI/MultiBotQuestUIShared.lua b/UI/MultiBotQuestUIShared.lua index bcd6669..7d1681b 100644 --- a/UI/MultiBotQuestUIShared.lua +++ b/UI/MultiBotQuestUIShared.lua @@ -7,6 +7,11 @@ Shared.ROW_HEIGHT = 24 Shared.DETAIL_ROW_HEIGHT = 16 Shared.PANEL_ALPHA = 0.90 Shared.SUBPANEL_ALPHA = 0.72 +Shared.DROP_BUTTON_WIDTH = 90 +Shared.DROP_LABEL_WIDTH = 210 +Shared.QUEST_ROW_LEFT_PADDING = 12 +Shared.QUEST_SUMMARY_PREFIX = " " +Shared.QUEST_TOP_PADDING = 16 Shared.ICON_QUEST = "Interface\\Icons\\inv_misc_note_01" Shared.ICON_BOT_QUEST = "Interface\\Icons\\inv_misc_note_02" @@ -32,6 +37,24 @@ function Shared.ApplyPanelStyle(frame, bgAlpha) end end +function Shared.ApplyWindowContentStyle(window, bgAlpha) + if window and window.content then + Shared.ApplyPanelStyle(window.content, bgAlpha or Shared.PANEL_ALPHA) + end +end + +function Shared.AddTopPadding(aceGUI, parent, height) + if not aceGUI or not parent or not parent.AddChild then + return + end + + local spacer = aceGUI:Create("SimpleGroup") + spacer:SetFullWidth(true) + spacer:SetHeight(height or Shared.QUEST_TOP_PADDING) + spacer:SetLayout("Flow") + parent:AddChild(spacer) +end + function Shared.ApplyEditBoxStyle(widget) if not widget or not widget.frame or not widget.editbox then return @@ -60,6 +83,56 @@ function Shared.ApplyEditBoxStyle(widget) end end +function Shared.GetQuestDropButtonText() + return MultiBot.L("tips.quests.drop", ABANDON_QUEST or "Abandon") +end + +function Shared.SendDropQuest(botName, entry) + if type(botName) ~= "string" or botName == "" or type(entry) ~= "table" then + return false + end + + local command = "drop " .. Shared.BuildQuestLink(entry.id, entry.originalName or entry.name) + if MultiBot.ActionToTarget then + return MultiBot.ActionToTarget(command, botName) + end + + if SendChatMessage then + SendChatMessage(command, "WHISPER", nil, botName) + return true + end + + return false +end + +function Shared.RemoveQuestEntryFromBucket(bucket, entry) + if type(bucket) ~= "table" or type(entry) ~= "table" then + return + end + + if entry.id ~= nil then + bucket[entry.id] = nil + bucket[tostring(entry.id)] = nil + end + + local entryName = tostring(entry.originalName or entry.name or "") + if entryName == "" then + return + end + + local removeKeys = {} + for questID, questName in pairs(bucket) do + local storedName = tostring(questName or "") + if storedName == entryName or storedName == tostring(entry.name or "") then + table.insert(removeKeys, questID) + end + end + + for _, questID in ipairs(removeKeys) do + bucket[questID] = nil + end +end + local function setQuestTooltip(widget, questID) if not questID then return @@ -76,11 +149,20 @@ function Shared.CreateQuestEntryRow(self, entry, opts) end opts = opts or {} + local hasDropAction = type(opts.onDrop) == "function" local row = self.aceGUI:Create("SimpleGroup") row:SetFullWidth(true) row:SetLayout("Flow") + local leftPadding = tonumber(opts.leftPadding or 0) or 0 + if leftPadding > 0 then + local spacer = self.aceGUI:Create("Label") + spacer:SetText("") + spacer:SetWidth(leftPadding) + row:AddChild(spacer) + end + local icon = self.aceGUI:Create("Icon") icon:SetImage(opts.iconPath or Shared.ICON_BOT_QUEST or "Interface\\Icons\\inv_misc_note_02") icon:SetImageSize(opts.iconSize or 14, opts.iconSize or 14) @@ -88,7 +170,7 @@ function Shared.CreateQuestEntryRow(self, entry, opts) row:AddChild(icon) local label = self.aceGUI:Create("InteractiveLabel") - label:SetWidth(opts.labelWidth or 320) + label:SetWidth(opts.labelWidth or (hasDropAction and Shared.DROP_LABEL_WIDTH) or 320) label:SetText(Shared.BuildQuestLink(entry.id, entry.name)) label:SetCallback("OnEnter", function(widget) setQuestTooltip(widget, entry.id) @@ -96,6 +178,19 @@ function Shared.CreateQuestEntryRow(self, entry, opts) label:SetCallback("OnLeave", GameTooltip_Hide) row:AddChild(label) + if hasDropAction then + local dropButton = self.aceGUI:Create("Button") + dropButton:SetText(opts.dropButtonText or Shared.GetQuestDropButtonText()) + dropButton:SetWidth(opts.dropButtonWidth or Shared.DROP_BUTTON_WIDTH) + if dropButton.SetHeight then + dropButton:SetHeight(opts.dropButtonHeight or 20) + end + dropButton:SetCallback("OnClick", function(widget) + opts.onDrop(entry, widget) + end) + row:AddChild(dropButton) + end + self.scroll:AddChild(row) if opts.showBots ~= false and entry.bots and #entry.bots > 0 then @@ -262,4 +357,4 @@ function Shared.BuildGameObjectCopyText(bots) end return table.concat(lines, "\n") -end \ No newline at end of file +end