diff --git a/.luacheckrc b/.luacheckrc index 297cc4e..88285e5 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -35,7 +35,8 @@ globals = { "GameFontNormal", "_G", "SetDesaturation", "WOW_PROJECT_ID", "WOW_PROJECT_MAINLINE", "ColorPickerFrame", "fixstrata", "UIDropDownMenu_HandleGlobalMouseEvent", "EventRegistry", "BackdropTemplateMixin", "TooltipBackdropTemplateMixin", "NORMAL_FONT_COLOR", "HIGHLIGHT_FONT_COLOR", "GameTooltip_SetDefaultAnchor", "ChatFrame1", "UISpecialFrames", "GetMouseFocus", "ShowUIPanel", "tremove", "min", "max", "GetMinimapShape", "GetMinimapShape", "PanelTemplates_TabResize", "GetGuildRosterShowOffline", "SetGuildRosterShowOffline", "IsInGuild", "GetGuildInfo", "SetGuildRosterShowOffline", "PLAYER", "INVENTORY_TOOLTIP", - "BAGSLOT", "UNKNOWN", "UnitIsDead", "ShowPrompt", "_MB_GetOrCreateShamanPos", "ensureHiddenTooltip", "MB_TAB_TITLE_DEFAULT", "SPELLBOOK", "MB_PAGE_DEFAULT" + "BAGSLOT", "UNKNOWN", "UnitIsDead", "ShowPrompt", "_MB_GetOrCreateShamanPos", "ensureHiddenTooltip", "MB_TAB_TITLE_DEFAULT", "SPELLBOOK", "MB_PAGE_DEFAULT", "SPELLBOOK_END_NON_SPELL_STREAK", + "RAID_CLASS_COLORS", "INSPECT" } read_globals = { diff --git a/Core/MultiBotHandler.lua b/Core/MultiBotHandler.lua index fedd482..76e7cc7 100644 --- a/Core/MultiBotHandler.lua +++ b/Core/MultiBotHandler.lua @@ -177,6 +177,14 @@ local function setSavedMainBarValue(key, value) return value end +MultiBot.GetSavedMainBarValue = function(key) + return getSavedMainBarValue(key) +end + +MultiBot.SetSavedMainBarValue = function(key, value) + return setSavedMainBarValue(key, value) +end + local function getLayoutProfileStore() local profile = MultiBot.db and MultiBot.db.profile if not profile then return nil end diff --git a/Core/MultiBotInit.lua b/Core/MultiBotInit.lua index 77bcfc9..33feeab 100644 --- a/Core/MultiBotInit.lua +++ b/Core/MultiBotInit.lua @@ -1509,10 +1509,22 @@ end local tButton = tMain.addButton("Reward", 0, 306, "Interface\\AddOns\\MultiBot\\Icons\\reward.blp", MultiBot.L("tips.main.reward")).setDisable() tButton.doRight = function(pButton) - if(#MultiBot.reward.rewards > 0 and #MultiBot.reward.units > 0) then MultiBot.reward:Show() end + MultiBot.rewardReopenIfAvailable() end + tButton.doLeft = function(pButton) - MultiBot.reward.state = MultiBot.OnOffSwitch(pButton) + local wasSavedEnabled = (MultiBot.GetSavedMainBarValue and MultiBot.GetSavedMainBarValue("Reward") == "true") + local isEnabled = MultiBot.OnOffSwitch(pButton) + + MultiBot.rewardSetEnabled(isEnabled) + + if(MultiBot.SetSavedMainBarValue) then + MultiBot.SetSavedMainBarValue("Reward", MultiBot.IF(isEnabled, "true", "false")) + end + + if(isEnabled and not wasSavedEnabled and MultiBot.rewardShowConfigPopup) then + MultiBot.rewardShowConfigPopup() + end end tMain.addButton("Reset", 0, 340, "inv_misc_tournaments_symbol_gnome", MultiBot.L("tips.main.reset")) @@ -3161,9 +3173,19 @@ MultiBot.inventory.movButton("Move", -406, 849, 34, MultiBot.L("tips.move.invent MultiBot.inventory.wowButton("X", -126, 862, 15, 18, 13) .doLeft = function(pButton) - local tUnits = MultiBot.frames["MultiBar"].frames["Units"] - local tButton = tUnits.frames[MultiBot.inventory.name].buttons["Inventory"] - tButton.doLeft(tButton) + local tUnits = MultiBot.frames and MultiBot.frames["MultiBar"] and MultiBot.frames["MultiBar"].frames and MultiBot.frames["MultiBar"].frames["Units"] + local tName = MultiBot.inventory and MultiBot.inventory.name + if(tUnits == nil or tName == nil or tUnits.buttons == nil or tUnits.buttons[tName] == nil) then + MultiBot.inventory:Hide() + return + end + + local tButton = tUnits.buttons[tName].buttons and tUnits.buttons[tName].buttons["Inventory"] + if(tButton ~= nil and tButton.doLeft ~= nil) then + tButton.doLeft(tButton) + else + MultiBot.inventory:Hide() + end end MultiBot.inventory.addButton("Sell", -94, 806, "inv_misc_coin_16", MultiBot.L("tips.inventory.sell")).setEnable() @@ -3523,285 +3545,8 @@ tFrame:Show() MultiBot.InitializeSpellBookFrame() -- REWARD -- - -MultiBot.reward = MultiBot.newFrame(MultiBot, -754, 238, 28, 384, 512) -MultiBot.reward.rewards = {} -MultiBot.reward.units = {} -MultiBot.reward.from = 1 -MultiBot.reward.max = 1 -MultiBot.reward.now = 1 -MultiBot.reward.to = 12 -MultiBot.reward:SetMovable(true) -MultiBot.reward:Hide() - -MultiBot.reward.doClose = function() - local tOverlay = MultiBot.reward.frames["Overlay"] - for key, value in pairs(MultiBot.reward.units) do if(value.rewarded == false) then return end end - MultiBot.reward:Hide() -end - -local tFrame = MultiBot.reward.addFrame("Icon", -313, 443, 28, 64, 64) -tFrame.addTexture("Interface\\AddOns\\MultiBot\\Textures\\Reward.blp") -tFrame:SetFrameLevel(0) - -local tFrame = MultiBot.reward.addFrame("TopLeft", -128, 256, 28, 256, 256) -tFrame.addTexture("Interface/ItemTextFrame/UI-ItemText-TopLeft") -tFrame:SetFrameLevel(1) - -local tFrame = MultiBot.reward.addFrame("TopRight", -0, 256, 28, 128, 256) -tFrame.addTexture("Interface/Spellbook/UI-SpellbookPanel-TopRight") -tFrame:SetFrameLevel(2) - -local tFrame = MultiBot.reward.addFrame("BottomLeft", -128, 0, 28, 256, 256) -tFrame.addTexture("Interface/ItemTextFrame/UI-ItemText-BotLeft") -tFrame:SetFrameLevel(3) - -local tFrame = MultiBot.reward.addFrame("BottomRight", -0, 0, 28, 128, 256) -tFrame.addTexture("Interface/Spellbook/UI-SpellbookPanel-BotRight") -tFrame:SetFrameLevel(4) - -local tOverlay = MultiBot.reward.addFrame("Overlay", -48, 97, 28, 310, 330) -tOverlay.addText("Title", MultiBot.L("info.reward"), "CENTER", 16, 226, 13) -tOverlay.addText("Pages", MB_PAGE_DEFAULT, "CENTER", 16, 196, 13) -tOverlay:SetFrameLevel(5) - -tOverlay.movButton("Move", -270, 354, 50, MultiBot.L("tips.move.reward"), MultiBot.reward) - -tOverlay.wowButton("<", -182, 351, 15, 18, 13) -.doLeft = function(pButton) - local tOverlay = MultiBot.reward.frames["Overlay"] - local tReward = MultiBot.reward - - tReward.to = tReward.to - 12 - tReward.now = tReward.now - 1 - tReward.from = tReward.from - 12 - tOverlay.setText("Pages", tReward.now .. "/" .. tReward.max) - tOverlay.buttons[">"].doShow() - - if(tReward.now == 1) then pButton.doHide() end - local tIndex = 1 - - for i = tReward.from, tReward.to do - MultiBot.setReward(tIndex, MultiBot.reward.units[i]) - tIndex = tIndex + 1 - end -end - -tOverlay.wowButton(">", -82, 351, 15, 18, 11) -.doLeft = function(pButton) - local tOverlay = MultiBot.reward.frames["Overlay"] - local tReward = MultiBot.reward - - tReward.to = tReward.to + 12 - tReward.now = tReward.now + 1 - tReward.from = tReward.from + 12 - tOverlay.setText("Pages", tReward.now .. "/" .. tReward.max) - tOverlay.buttons["<"].doShow() - - if(tReward.now == tReward.max) then pButton.doHide() end - local tIndex = 1 - - for i = tReward.from, tReward.to do - MultiBot.setReward(tIndex, MultiBot.reward.units[i]) - tIndex = tIndex + 1 - end -end - -tOverlay.wowButton("X", 13, 381, 17, 20, 11) -.doLeft = function(pButton) - MultiBot.reward:Hide() -end - --- GROUP:U01 -- - -local tFrame = tOverlay.addFrame("U01", -156, 282, 23, 154, 48) -tFrame.addText("U01", "|cffffcc00NAME - CLASS|r", "BOTTOMLEFT", 20, 28, 13) -tFrame.addButton("R1", -130, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R2", -104, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R3", -78, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R4", -52, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R5", -26, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R6", -0, 0, "inv_misc_questionmark", "Text") -tFrame.addFrame("Inspector", -137, 26, 16) -.addButton("Inspect", 0, 0, "Interface\\AddOns\\MultiBot\\Icons\\filter_none.blp", "Inspect") -.doLeft = function(pButton) - InspectUnit(pButton.getName()) -end - --- GROUP:U02 -- - -local tFrame = tOverlay.addFrame("U02", 0, 282, 23, 154, 48) -tFrame.addText("U02", "|cffffcc00NAME - CLASS|r", "BOTTOMLEFT", 20, 28, 13) -tFrame.addButton("R1", -130, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R2", -104, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R3", -78, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R4", -52, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R5", -26, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R6", -0, 0, "inv_misc_questionmark", "Text") -tFrame.addFrame("Inspector", -137, 26, 16) -.addButton("Inspect", 0, 0, "Interface\\AddOns\\MultiBot\\Icons\\filter_none.blp", "Inspect") -.doLeft = function(pButton) - InspectUnit(pButton.getName()) -end - --- GROUP:U03 -- - -local tFrame = tOverlay.addFrame("U03", -156, 228, 23, 154, 48) -tFrame.addText("U03", "|cffffcc00NAME - CLASS|r", "BOTTOMLEFT", 20, 28, 13) -tFrame.addButton("R1", -130, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R2", -104, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R3", -78, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R4", -52, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R5", -26, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R6", -0, 0, "inv_misc_questionmark", "Text") -tFrame.addFrame("Inspector", -137, 26, 16) -.addButton("Inspect", 0, 0, "Interface\\AddOns\\MultiBot\\Icons\\filter_none.blp", "Inspect") -.doLeft = function(pButton) - InspectUnit(pButton.getName()) -end - --- GROUP:U04 -- - -local tFrame = tOverlay.addFrame("U04", 0, 228, 23, 154, 48) -tFrame.addText("U04", "|cffffcc00NAME - CLASS|r", "BOTTOMLEFT", 20, 28, 13) -tFrame.addButton("R1", -130, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R2", -104, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R3", -78, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R4", -52, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R5", -26, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R6", -0, 0, "inv_misc_questionmark", "Text") -tFrame.addFrame("Inspector", -137, 26, 16) -.addButton("Inspect", 0, 0, "Interface\\AddOns\\MultiBot\\Icons\\filter_none.blp", "Inspect") -.doLeft = function(pButton) - InspectUnit(pButton.getName()) -end - --- GROUP:U05 -- - -local tFrame = tOverlay.addFrame("U05", -156, 174, 23, 154, 48) -tFrame.addText("U05", "|cffffcc00NAME - CLASS|r", "BOTTOMLEFT", 20, 28, 13) -tFrame.addButton("R1", -130, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R2", -104, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R3", -78, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R4", -52, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R5", -26, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R6", -0, 0, "inv_misc_questionmark", "Text") -tFrame.addFrame("Inspector", -137, 26, 16) -.addButton("Inspect", 0, 0, "Interface\\AddOns\\MultiBot\\Icons\\filter_none.blp", "Inspect") -.doLeft = function(pButton) - InspectUnit(pButton.getName()) -end - --- GROUP:U06 -- - -local tFrame = tOverlay.addFrame("U06", 0, 174, 23, 154, 48) -tFrame.addText("U06", "|cffffcc00NAME - CLASS|r", "BOTTOMLEFT", 20, 28, 13) -tFrame.addButton("R1", -130, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R2", -104, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R3", -78, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R4", -52, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R5", -26, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R6", -0, 0, "inv_misc_questionmark", "Text") -tFrame.addFrame("Inspector", -137, 26, 16) -.addButton("Inspect", 0, 0, "Interface\\AddOns\\MultiBot\\Icons\\filter_none.blp", "Inspect") -.doLeft = function(pButton) - InspectUnit(pButton.getName()) -end - --- GROUP:U07 -- - -local tFrame = tOverlay.addFrame("U07", -156, 120, 23, 154, 48) -tFrame.addText("U07", "|cffffcc00NAME - CLASS|r", "BOTTOMLEFT", 20, 28, 13) -tFrame.addButton("R1", -130, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R2", -104, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R3", -78, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R4", -52, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R5", -26, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R6", -0, 0, "inv_misc_questionmark", "Text") -tFrame.addFrame("Inspector", -137, 26, 16) -.addButton("Inspect", 0, 0, "Interface\\AddOns\\MultiBot\\Icons\\filter_none.blp", "Inspect") -.doLeft = function(pButton) - InspectUnit(pButton.getName()) -end - --- GROUP:U08 -- - -local tFrame = tOverlay.addFrame("U08", 0, 120, 23, 154, 48) -tFrame.addText("U08", "|cffffcc00NAME - CLASS|r", "BOTTOMLEFT", 20, 28, 13) -tFrame.addButton("R1", -130, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R2", -104, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R3", -78, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R4", -52, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R5", -26, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R6", -0, 0, "inv_misc_questionmark", "Text") -tFrame.addFrame("Inspector", -137, 26, 16) -.addButton("Inspect", 0, 0, "Interface\\AddOns\\MultiBot\\Icons\\filter_none.blp", "Inspect") -.doLeft = function(pButton) - InspectUnit(pButton.getName()) -end - --- GROUP:U09 -- - -local tFrame = tOverlay.addFrame("U09", -156, 66, 23, 154, 48) -tFrame.addText("U09", "|cffffcc00NAME - CLASS|r", "BOTTOMLEFT", 20, 28, 13) -tFrame.addButton("R1", -130, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R2", -104, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R3", -78, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R4", -52, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R5", -26, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R6", -0, 0, "inv_misc_questionmark", "Text") -tFrame.addFrame("Inspector", -137, 26, 16) -.addButton("Inspect", 0, 0, "Interface\\AddOns\\MultiBot\\Icons\\filter_none.blp", "Inspect") -.doLeft = function(pButton) - InspectUnit(pButton.getName()) -end - --- GROUP:U10 -- - -local tFrame = tOverlay.addFrame("U10", 0, 66, 23, 154, 48) -tFrame.addText("U10", "|cffffcc00NAME - CLASS|r", "BOTTOMLEFT", 20, 28, 13) -tFrame.addButton("R1", -130, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R2", -104, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R3", -78, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R4", -52, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R5", -26, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R6", -0, 0, "inv_misc_questionmark", "Text") -tFrame.addFrame("Inspector", -137, 26, 16) -.addButton("Inspect", 0, 0, "Interface\\AddOns\\MultiBot\\Icons\\filter_none.blp", "Inspect") -.doLeft = function(pButton) - InspectUnit(pButton.getName()) -end - --- GROUP:U11 -- - -local tFrame = tOverlay.addFrame("U11", -156, 12, 23, 154, 48) -tFrame.addText("U11", "|cffffcc00NAME - CLASS|r", "BOTTOMLEFT", 20, 28, 13) -tFrame.addButton("R1", -130, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R2", -104, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R3", -78, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R4", -52, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R5", -26, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R6", -0, 0, "inv_misc_questionmark", "Text") -tFrame.addFrame("Inspector", -137, 26, 16) -.addButton("Inspect", 0, 0, "Interface\\AddOns\\MultiBot\\Icons\\filter_none.blp", "Inspect") -.doLeft = function(pButton) - InspectUnit(pButton.getName()) -end - --- GROUP:U12 -- - -local tFrame = tOverlay.addFrame("U12", 0, 12, 23, 154, 48) -tFrame.addText("U12", "|cffffcc00NAME - CLASS|r", "BOTTOMLEFT", 20, 28, 13) -tFrame.addButton("R1", -130, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R2", -104, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R3", -78, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R4", -52, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R5", -26, 0, "inv_misc_questionmark", "Text") -tFrame.addButton("R6", -0, 0, "inv_misc_questionmark", "Text") -tFrame.addFrame("Inspector", -137, 26, 16) -.addButton("Inspect", 0, 0, "Interface\\AddOns\\MultiBot\\Icons\\filter_none.blp", "Inspect") -.doLeft = function(pButton) - InspectUnit(pButton.getName()) +if MultiBot.InitializeRewardFrame then + MultiBot.InitializeRewardFrame() end -- TALENT AND GLYPHS FRAME -- diff --git a/Features/MultiBotReward.lua b/Features/MultiBotReward.lua index 7961570..7ab49f9 100644 --- a/Features/MultiBotReward.lua +++ b/Features/MultiBotReward.lua @@ -1,87 +1,349 @@ -MultiBot.setRewards = function() - if(MultiBot.reward.state == false) then return end - local tChoices = MultiBot.IF(GetNumQuestChoices() > 6, 6, GetNumQuestChoices()) - MultiBot.reward.rewards = {} - MultiBot.reward.units = {} +local MB_REWARD_PAGE_SIZE = 12 +local MB_REWARD_MAX_CHOICES = 6 + +local MB_REWARD_CONFIG_POPUP_KEY = "MULTIBOT_REWARD_CONFIG_WARNING" + +local function showRewardConfigPopup() + if(type(StaticPopupDialogs) ~= "table" or type(StaticPopup_Show) ~= "function") then return end + + if(not StaticPopupDialogs[MB_REWARD_CONFIG_POPUP_KEY]) then + StaticPopupDialogs[MB_REWARD_CONFIG_POPUP_KEY] = { + text = "", + button1 = OKAY or "OK", + timeout = 0, + whileDead = 1, + hideOnEscape = 1, + preferredIndex = 3, + } + end + + StaticPopupDialogs[MB_REWARD_CONFIG_POPUP_KEY].text = MultiBot.L("info.reward.popup") + StaticPopup_Show(MB_REWARD_CONFIG_POPUP_KEY) +end + +MultiBot.rewardShowConfigPopup = function() + showRewardConfigPopup() +end + +local function getClassToken(className) + local canon = (MultiBot.toClass and MultiBot.toClass(className)) or className + local tokenMap = { + DeathKnight = "DEATHKNIGHT", + Druid = "DRUID", + Hunter = "HUNTER", + Mage = "MAGE", + Paladin = "PALADIN", + Priest = "PRIEST", + Rogue = "ROGUE", + Shaman = "SHAMAN", + Warlock = "WARLOCK", + Warrior = "WARRIOR", + } + + return tokenMap[canon] +end - for i = 1, tChoices do +local function getWotlkClassHexColor(className) + local token = getClassToken(className) + local colors = (token and RAID_CLASS_COLORS and RAID_CLASS_COLORS[token]) or nil + if(not colors) then return "ffffcc00" end + + local r = math.floor(((colors.r or 1) * 255) + 0.5) + local g = math.floor(((colors.g or 1) * 255) + 0.5) + local b = math.floor(((colors.b or 1) * 255) + 0.5) + return string.format("ff%02x%02x%02x", r, g, b) +end + +local function getClassIconMarkup(className, iconSize) + local token = getClassToken(className) + local coordsByToken = { + WARRIOR = "0:64:0:64", + MAGE = "64:128:0:64", + ROGUE = "128:192:0:64", + DRUID = "192:256:0:64", + HUNTER = "0:64:64:128", + SHAMAN = "64:128:64:128", + PRIEST = "128:192:64:128", + WARLOCK = "192:256:64:128", + PALADIN = "0:64:128:192", + DEATHKNIGHT = "64:128:128:192", + } + + local coords = token and coordsByToken[token] + if(coords == nil) then return "" end + + local size = math.max(12, math.min(24, tonumber(iconSize) or 16)) + return "|TInterface\\Glues\\CharacterCreate\\UI-CharacterCreate-Classes:" .. size .. ":" .. size .. ":0:0:256:256:" .. coords .. "|t" +end + +local function collectQuestChoices() + local tChoices = {} + local tMaxChoices = math.min(MB_REWARD_MAX_CHOICES, GetNumQuestChoices() or 0) + + for i = 1, tMaxChoices do local tLink = GetQuestItemLink("CHOICE", i) local tName, tIcon = GetQuestItemInfo("CHOICE", i) - MultiBot.reward.rewards[i] = { tLink, tName, tIcon } + if(tLink ~= nil and tName ~= nil) then + table.insert(tChoices, { tLink, tName, tIcon or "inv_misc_questionmark" }) + end end - for i = 1, 12 do - local tID = "U" .. MultiBot.IF(i < 10, "0", "") .. i - local tUnit = MultiBot.reward.frames["Overlay"].frames[tID] - for j = 1, 6 do tUnit.buttons["R" .. j]:Hide() end - tUnit:Hide() + return tChoices +end + +local function collectEligibleUnits() + local tUnits = {} + local tPlayerName = UnitName("player") + + local function addIfBot(unitName) + if(unitName == nil) then return end + local tBot = MultiBot.getBot(unitName) + if(tBot ~= nil and tBot.name ~= tPlayerName) then + tBot.rewarded = false + table.insert(tUnits, tBot) + end end if(GetNumRaidMembers() > 0) then - for i = 1, 40 do - local tUnit = UnitName("raid" .. i) - if(tUnit ~= nil) then - local tBot = MultiBot.getBot(tUnit) - if(tBot ~= nil and tBot.name ~= UnitName("player")) then table.insert(MultiBot.reward.units, tBot) end - end - end + for i = 1, 40 do addIfBot(UnitName("raid" .. i)) end elseif(GetNumPartyMembers() > 0) then - for i = 1, 5 do - local tUnit = UnitName("party" .. i) - if(tUnit ~= nil) then - local tBot = MultiBot.getBot(tUnit) - if(tBot ~= nil and tBot.name ~= UnitName("player")) then table.insert(MultiBot.reward.units, tBot) end + for i = 1, 5 do addIfBot(UnitName("party" .. i)) end + end + + return tUnits +end + +local function applyRewardChoice(pButton) + if(pButton == nil or pButton.link == nil) then return end + + pButton.parent:Hide() + SendChatMessage("r " .. pButton.link, "WHISPER", nil, pButton.getName()) + + local tClickedBot = MultiBot.getBot(pButton.getName()) + if(tClickedBot ~= nil) then tClickedBot.rewarded = true end + + MultiBot.rewardTryClose() +end + +MultiBot.rewardSetClassIconSize = function(size) + local tReward = MultiBot.rewardEnsureState() + if(tReward == nil) then return 16 end + + local safeSize = math.max(12, math.min(24, tonumber(size) or tReward.classIconSize or 16)) + tReward.classIconSize = safeSize + + if(MultiBot.rewardHasRenderableData() and tReward:IsVisible()) then + MultiBot.rewardRenderPage() + end + + return safeSize +end + +MultiBot.rewardSyncPageBounds = function() + local tReward = MultiBot.rewardEnsureState() + if(tReward == nil) then return 0, 0 end + + local unitsCount = #tReward.units + local rewardCount = #tReward.rewards + if(unitsCount <= 0) then + tReward.max = 1 + tReward.now = 1 + tReward.from = 1 + tReward.to = tReward.pageSize + return unitsCount, rewardCount + end + + tReward.max = math.max(1, math.ceil(unitsCount / tReward.pageSize)) + tReward.now = math.max(1, math.min(tReward.now or 1, tReward.max)) + tReward.from = ((tReward.now - 1) * tReward.pageSize) + 1 + tReward.to = math.min(tReward.from + tReward.pageSize - 1, unitsCount) + return unitsCount, rewardCount +end + +MultiBot.rewardChangePage = function(delta) + local tReward = MultiBot.rewardEnsureState() + if(tReward == nil or not MultiBot.rewardHasRenderableData()) then return false end + + MultiBot.rewardSyncPageBounds() + local tTarget = math.max(1, math.min((tReward.now or 1) + (delta or 0), tReward.max or 1)) + if(tTarget == tReward.now) then return false end + + tReward.now = tTarget + MultiBot.rewardSyncPageBounds() + MultiBot.rewardRenderPage() + return true +end + +MultiBot.rewardCollectQuestChoices = collectQuestChoices +MultiBot.rewardCollectEligibleUnits = collectEligibleUnits + +MultiBot.rewardTryClose = function() + local tReward = MultiBot.rewardEnsureState() + if(tReward == nil) then return end + + for _, value in pairs(tReward.units) do + if(value ~= nil and value.rewarded == false) then return end + end + + tReward:Hide() +end + +MultiBot.rewardApplyChoice = function(pButton) + applyRewardChoice(pButton) +end + +MultiBot.rewardHasRenderableData = function() + local tReward = MultiBot.rewardEnsureState() + if(tReward == nil) then return false end + return (#tReward.rewards > 0 and #tReward.units > 0) +end + +MultiBot.rewardSetEnabled = function(isEnabled) + local tReward = MultiBot.rewardEnsureState() + + tReward.state = (isEnabled == true) + + if(not tReward.state) then + MultiBot.rewardResetPagination() + MultiBot.rewardClearPage() + tReward:Hide() + end + + return tReward.state +end + +MultiBot.rewardReopenIfAvailable = function() + local tReward = MultiBot.rewardEnsureState() + if(tReward == nil) then return false end + if(not MultiBot.rewardHasRenderableData()) then return false end + + MultiBot.rewardRefreshPager() + MultiBot.rewardRenderPage() + tReward:Show() + return true +end + +MultiBot.rewardEnsureState = function() + if(MultiBot.reward == nil) then return nil end + + MultiBot.reward.rewards = MultiBot.reward.rewards or {} + MultiBot.reward.units = MultiBot.reward.units or {} + MultiBot.reward.pageSize = MultiBot.reward.pageSize or MB_REWARD_PAGE_SIZE + MultiBot.reward.now = MultiBot.reward.now or 1 + MultiBot.reward.max = MultiBot.reward.max or 1 + MultiBot.reward.from = MultiBot.reward.from or 1 + MultiBot.reward.to = MultiBot.reward.to or MultiBot.reward.pageSize + MultiBot.reward.classIconSize = MultiBot.reward.classIconSize or 16 + + return MultiBot.reward +end + +MultiBot.rewardResetPagination = function() + local tReward = MultiBot.rewardEnsureState() + if(tReward == nil) then return end + + tReward.now = 1 + tReward.max = 1 + tReward.from = 1 + tReward.to = tReward.pageSize +end + +MultiBot.rewardClearPage = function() + local tReward = MultiBot.rewardEnsureState() + if(tReward == nil or tReward.rows == nil or tReward.pageLabel == nil or tReward.prevButton == nil or tReward.nextButton == nil) then return end + + for i = 1, tReward.pageSize do + local tUnit = tReward.rows[i] + if(tUnit ~= nil) then + for j = 1, MB_REWARD_MAX_CHOICES do + local tButton = tUnit.buttons["R" .. j] + if(tButton ~= nil) then tButton:Hide() end end + tUnit:Hide() end end +end + +MultiBot.rewardRefreshPager = function() + local tReward = MultiBot.rewardEnsureState() + if(tReward == nil or tReward.rows == nil or tReward.pageLabel == nil or tReward.prevButton == nil or tReward.nextButton == nil) then return end - -- Nombre de bots éligibles à une récompense - local unitsCount = #MultiBot.reward.units + MultiBot.rewardSyncPageBounds() + local tMaxDisplay = math.max(1, tReward.max or 1) + tReward.pageLabel:SetText(tReward.now .. "/" .. tMaxDisplay) + tReward.prevButton:Show() + tReward.nextButton:Show() - if(unitsCount > 0 and tChoices > 0) then - local tOverlay = MultiBot.reward.frames["Overlay"] - local tUnits = unitsCount + if(tReward.now <= 1) then tReward.prevButton:Hide() end + if(tReward.now >= tMaxDisplay) then tReward.nextButton:Hide() end +end - MultiBot.reward.max = math.ceil(tUnits / MultiBot.reward.to) - tOverlay.setText("Pages", MultiBot.reward.now .. "/" .. MultiBot.reward.max) - tOverlay.buttons["<"]:Show() - tOverlay.buttons[">"]:Show() +MultiBot.rewardRenderPage = function() + local tReward = MultiBot.rewardEnsureState() + if(tReward == nil) then return end - if(MultiBot.reward.now == 1) then tOverlay.buttons["<"]:Hide() end - if(MultiBot.reward.now == MultiBot.reward.max) then tOverlay.buttons[">"]:Hide() end + MultiBot.rewardSyncPageBounds() + MultiBot.rewardClearPage() + MultiBot.rewardRefreshPager() - if(tUnits > MultiBot.reward.to) then - tUnits = MultiBot.reward.to - end + if(#tReward.units == 0 or #tReward.rewards == 0) then return end + + for tRow = 1, tReward.pageSize do + local tBot = tReward.units[tReward.from + tRow - 1] + local tUnit = MultiBot.setReward(tRow, tBot) - for i = 1, tUnits do - local tBot = MultiBot.reward.units[i] - local tUnit = MultiBot.setReward(i, tBot, false) - - for j = 1, tChoices do - local tReward = tUnit.buttons["R" .. j] - tReward:Show() - tReward.link = MultiBot.reward.rewards[j][1] - tReward.setButton(MultiBot.reward.rewards[j][3], MultiBot.reward.rewards[j][1]) - tReward.doLeft = function(pButton) - pButton.parent:Hide() - SendChatMessage("r " .. pButton.link, "WHISPER", nil, pButton.getName()) - MultiBot.getBot(pButton.getName()).rewarded = true - MultiBot.reward.doClose() + if(tUnit ~= nil and tBot ~= nil and (not tBot.rewarded)) then + for j = 1, #tReward.rewards do + local tRewardChoice = tReward.rewards[j] + local tButton = tUnit.buttons["R" .. j] + if(tRewardChoice ~= nil and tButton ~= nil) then + tButton:Show() + tButton.link = tRewardChoice[1] + tButton.setButton(tRewardChoice[3], tRewardChoice[1]) + tButton.doLeft = applyRewardChoice end end end + end +end + +MultiBot.setRewards = function() + local tReward = MultiBot.rewardEnsureState() + if(tReward == nil or tReward.state == false) then return end + + tReward.rewards = collectQuestChoices() + tReward.units = collectEligibleUnits() + MultiBot.rewardResetPagination() - MultiBot.reward:Show() + local unitsCount, rewardCount = MultiBot.rewardSyncPageBounds() + if(unitsCount == 0 or rewardCount == 0) then + MultiBot.rewardClearPage() + MultiBot.rewardRefreshPager() + tReward:Hide() + return end + + MultiBot.rewardRenderPage() + tReward:Show() end MultiBot.setReward = function(pIndex, pBot, oRewarded) - local tID = "U" .. MultiBot.IF(pIndex < 10, "0", "") .. pIndex - local tUnit = MultiBot.reward.frames["Overlay"].frames[tID] + local tReward = MultiBot.rewardEnsureState() + if(tReward == nil or tReward.rows == nil) then return nil end + + local tUnit = tReward.rows[pIndex] + if(tUnit == nil) then return nil end + + if(pBot == nil) then + tUnit:Hide() + return tUnit + end + if(oRewarded ~= nil) then pBot.rewarded = oRewarded end if(pBot.rewarded) then tUnit:Hide() else tUnit:Show() end - tUnit.setText(tID, "|cffffcc00" .. pBot.name .. " - " .. pBot.class .. "|r") + local classHex = getWotlkClassHexColor(pBot.class) + local classIcon = getClassIconMarkup(pBot.class, tReward.classIconSize) + tUnit.setText("", "|c" .. classHex .. pBot.name .. "|r " .. classIcon) tUnit.class = pBot.class tUnit.name = pBot.name return tUnit diff --git a/Locales/MultiBotAceLocale-deDE.lua b/Locales/MultiBotAceLocale-deDE.lua index c535eca..29a767f 100644 --- a/Locales/MultiBotAceLocale-deDE.lua +++ b/Locales/MultiBotAceLocale-deDE.lua @@ -55,6 +55,7 @@ local deDEValues = { ["info.combination"] = "Es gibt keine Items für diese Kombination.Ich muss zuerst den Language-Selector aktivieren.", ["info.rights"] = "Ich habe keine GameMaster-Rechte.", ["info.reward"] = "Wähle die Belohnungen", + ["info.reward.popup"] = "Damit diese Funktion funktioniert, muss die playerbot.conf wie folgt konfiguriert werden:\nAiPlayerbot.AutoPickReward = no (erforderlich)\nAiPlayerbot.SyncQuestWithPlayer = 1 (empfohlen)", ["info.shorts.bag"] = "Bag", ["info.shorts.dur"] = "Dur", ["info.shorts.xp"] = "XP", diff --git a/Locales/MultiBotAceLocale-enGB.lua b/Locales/MultiBotAceLocale-enGB.lua index 81839e3..07e8b5b 100644 --- a/Locales/MultiBotAceLocale-enGB.lua +++ b/Locales/MultiBotAceLocale-enGB.lua @@ -55,6 +55,7 @@ local enGBValues = { ["info.combination"] = "I couldn't find any items that match this filter combination.I need to activate the Language-Selector first.", ["info.rights"] = "I have no GameMaster privileges.", ["info.reward"] = "Select Rewards", + ["info.reward.popup"] = "For this feature to work, playerbot.conf must be configured as follows:\nAiPlayerbot.AutoPickReward = no (required)\nAiPlayerbot.SyncQuestWithPlayer = 1 (recommended)", ["info.nothing"] = "Nothing is saved in this Slot.", ["info.shorts.bag"] = "Bag", ["info.shorts.dur"] = "Dur", diff --git a/Locales/MultiBotAceLocale-enUS.lua b/Locales/MultiBotAceLocale-enUS.lua index 844c66d..49ad18e 100644 --- a/Locales/MultiBotAceLocale-enUS.lua +++ b/Locales/MultiBotAceLocale-enUS.lua @@ -55,6 +55,7 @@ local enUSValues = { ["info.combination"] = "I couldn't find any items that match this filter combination.I need to activate the Language-Selector first.", ["info.rights"] = "I have no GameMaster privileges.", ["info.reward"] = "Select Rewards", + ["info.reward.popup"] = "For this feature to work, playerbot.conf must be configured as follows:\nAiPlayerbot.AutoPickReward = no (required)\nAiPlayerbot.SyncQuestWithPlayer = 1 (recommended)", ["info.nothing"] = "Nothing is saved in this Slot.", ["info.shorts.bag"] = "Bag", ["info.shorts.dur"] = "Dur", diff --git a/Locales/MultiBotAceLocale-esES.lua b/Locales/MultiBotAceLocale-esES.lua index 6f9b953..1295283 100644 --- a/Locales/MultiBotAceLocale-esES.lua +++ b/Locales/MultiBotAceLocale-esES.lua @@ -55,6 +55,7 @@ local esESValues = { ["info.combination"] = "No hay elementos para esta combinación.Necesito activar el selector de idioma primero.", ["info.rights"] = "No tengo los GameMaster-Rights.", ["info.reward"] = "Selecciona las recompensas", + ["info.reward.popup"] = "Para que esta función funcione, debes configurar playerbot.conf de la siguiente manera:\nAiPlayerbot.AutoPickReward = no (obligatorio)\nAiPlayerbot.SyncQuestWithPlayer = 1 (recomendado)", ["info.shorts.bag"] = "Bag", ["info.shorts.dur"] = "Dur", ["info.shorts.xp"] = "XP", diff --git a/Locales/MultiBotAceLocale-frFR.lua b/Locales/MultiBotAceLocale-frFR.lua index 000e5d1..da0f2f8 100644 --- a/Locales/MultiBotAceLocale-frFR.lua +++ b/Locales/MultiBotAceLocale-frFR.lua @@ -55,6 +55,7 @@ local frFRValues = { ["info.combination"] = "Il n'y a pas d'objets pour cette combinaison.Je dois d'abord activer le sélecteur de langue.", ["info.rights"] = "Je n'ai pas les droits MJ.", ["info.reward"] = "Sélectionner les récompenses", + ["info.reward.popup"] = "Pour que cette fonctionnalité fonctionne, il faut configurer playerbot.conf de la manière suivante:\nAiPlayerbot.AutoPickReward = no (obligatoire)\nAiPlayerbot.SyncQuestWithPlayer = 1 (recommandé)", ["info.shorts.bag"] = "Sac", ["info.shorts.dur"] = "Dur", ["info.shorts.xp"] = "XP", diff --git a/Locales/MultiBotAceLocale-koKR.lua b/Locales/MultiBotAceLocale-koKR.lua index 115e481..a0285dc 100644 --- a/Locales/MultiBotAceLocale-koKR.lua +++ b/Locales/MultiBotAceLocale-koKR.lua @@ -55,6 +55,7 @@ local koKRValues = { ["info.combination"] = "이 조합에 맞는 항목이 없습니다.먼저 언어 선택기를 활성화해야 했습니다.", ["info.rights"] = "GM 권한이 없습니다.", ["info.reward"] = "보상을 선택하세요.", + ["info.reward.popup"] = "이 기능이 작동하려면 playerbot.conf를 다음과 같이 설정해야 합니다:\nAiPlayerbot.AutoPickReward = no (필수)\nAiPlayerbot.SyncQuestWithPlayer = 1 (권장)", ["info.shorts.bag"] = "배낭", ["info.shorts.dur"] = "내구성", ["info.shorts.xp"] = "경험", diff --git a/Locales/MultiBotAceLocale-ruRU.lua b/Locales/MultiBotAceLocale-ruRU.lua index b6cc6f8..2d54ed9 100644 --- a/Locales/MultiBotAceLocale-ruRU.lua +++ b/Locales/MultiBotAceLocale-ruRU.lua @@ -55,6 +55,7 @@ local ruRUValues = { ["info.combination"] = "Нет предметов для этой комбинации.Мне нужно сначала активировать выбор языка.", ["info.rights"] = "У меня нет прав Гейммастера.", ["info.reward"] = "Выберите награды", + ["info.reward.popup"] = "Чтобы эта функция работала, необходимо настроить playerbot.conf следующим образом:\nAiPlayerbot.AutoPickReward = no (обязательно)\nAiPlayerbot.SyncQuestWithPlayer = 1 (рекомендуется)", ["info.shorts.bag"] = "Сумка", ["info.shorts.dur"] = "Прочн.", ["info.shorts.xp"] = "Опыт", diff --git a/Locales/MultiBotAceLocale-zhCN.lua b/Locales/MultiBotAceLocale-zhCN.lua index 072f8cb..1306af0 100644 --- a/Locales/MultiBotAceLocale-zhCN.lua +++ b/Locales/MultiBotAceLocale-zhCN.lua @@ -55,6 +55,7 @@ local zhCNValues = { ["info.combination"] = "没有适合这种组合的物品。我需要先激活语言选择器。", ["info.rights"] = "我没有GM权限。", ["info.reward"] = "选择奖励。", + ["info.reward.popup"] = "要使此功能正常工作,必须按如下方式配置 playerbot.conf:\nAiPlayerbot.AutoPickReward = no (必需)\nAiPlayerbot.SyncQuestWithPlayer = 1 (推荐)", ["info.shorts.bag"] = "背包", ["info.shorts.dur"] = "耐久度", ["info.shorts.xp"] = "经验值", diff --git a/MultiBot.toc b/MultiBot.toc index 7dd63bf..970bfc3 100644 --- a/MultiBot.toc +++ b/MultiBot.toc @@ -61,6 +61,7 @@ Core\MultiBotEvery.lua UI\MultiBotStats.lua UI\MultiBotSpell.lua UI\MultiBotSpellBookFrame.lua +UI\MultiBotRewardFrame.lua UI\MultiBotItem.lua UI\MultiBotTalentFrame.lua Core\MultiBotInit.lua diff --git a/ROADMAP.md b/ROADMAP.md index 2b5c772..a3c6bc1 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -20,7 +20,7 @@ - AceDB bootstrap/runtime migration is now complete for supported SavedVariables paths; one-way versioned legacy cutovers are in place with guarded legacy creation and post-migration cleanup to avoid stale duplicate persistence. - **Milestone 7 (Minimap/options integration):** Completed. - Minimap hide/angle, global frame strata, options timers/throttle, Spec dropdown positions, Hunter/Shaman quick-bar positions, Hunter pet stance state and Shaman totem choice state now run through AceDB-backed helpers with one-way versioned legacy cutover and guarded legacy fallback (no legacy table creation on pure read paths). -- **Milestone 8 (AceGUI UI refactor):** In progress (Raidus + SpellBook slices completed). +- **Milestone 8 (AceGUI UI refactor):** In progress (Raidus + SpellBook + Reward slices completed). - `UI/MultiBotOptions.lua` panel content has been migrated to AceGUI widgets while preserving category registration and slash/open flows. - `UI/MultiBotPVPUI.lua` migration slice is completed for the targeted controls (bot selector dropdown + tab group with localized fallback compatibility). - `UI/MultiBotSpecUI.lua` migration slice is completed for the spec popup/inspect helper controls (AceGUI window path finalized: close-cross UX, layering fix, compact size, and position persistence on AceDB path). @@ -28,6 +28,7 @@ - `UI/MultiBotTalentFrame.lua` Talents/Glyphs host path is active on AceGUI (`Window` container + host layout + tab context updates), with legacy visual tab chrome intentionally preserved (`ChatFrameTab-*`) by design choice for now. - Milestone 8 Talents/Glyphs remains **GO partiel**: ACE3 hosting path is in place, but full "no legacy dependency" validation is not yet declared closed. - `UI/MultiBotSpellBookFrame.lua` + `UI/MultiBotSpell.lua` SpellBook migration slice is completed (AceGUI window host, dynamic slot/check generation, page-size normalization, and stateful chat-collection parsing/finish flow). + - Reward frame migration slice is completed (`UI/MultiBotRewardFrame.lua` + `Features/MultiBotReward.lua` + main-bar integration): native AceGUI host, deduped module API, saved-state-aware popup trigger, and parity close/paging behavior are in place. - Remaining screens continue screen-by-screen (quest popups and auxiliary prompts still pending). - **Milestone 9 (Localization and text pipeline):** Completed. - Core locale loader + per-locale payload files are integrated (`Core/MultiBotLocale.lua`, `Locales/MultiBotAceLocale-*.lua`). diff --git a/TODO.md b/TODO.md index 4d4a8d5..087050d 100644 --- a/TODO.md +++ b/TODO.md @@ -1,7 +1,8 @@ TODO -* Retravailler l'interface d'affichage des gameobjects pour le clean à la fermeture car si on clique deux fois - sur le télescope il affiche par dessus -* Retravailler l'interface copier/coller +* Essayer de faire disparaitre la barre multibots au bout d'un temps et la faire apparaitre quand on passe la souris dessus. +* Faire en sorte que le réglage strata prenne en compte toutes les frames de multibots +* Mettre un bouton style décursive pour cacher la shaman quick et hunter quick * Quand on deplace ou fait quelque chose dans l'ui il faudrait que ça se sauvegarde tout de suite dans les variables dans deco reco * Raidus doit se rafraichir à l'ouverture et fermeture +* dans la liste des quêtes des fois c'est l'ID de la queête qui apparait et pas le tritre diff --git a/UI/MultiBotRewardFrame.lua b/UI/MultiBotRewardFrame.lua new file mode 100644 index 0000000..9550e68 --- /dev/null +++ b/UI/MultiBotRewardFrame.lua @@ -0,0 +1,297 @@ +-- REWARD FRAME -- + +local MB_REWARD_INSPECT_X_OFFSET = -30 + +local function getRewardAceGUI() + if(MultiBot.GetAceGUI) then + local tAce = MultiBot.GetAceGUI() + if(tAce) then return tAce end + end + + if(type(LibStub) == "table" and LibStub.GetLibrary) then + local ok, aceGUI = pcall(LibStub.GetLibrary, LibStub, "AceGUI-3.0", true) + if(ok) then return aceGUI end + end + + return nil +end + +local function makeIconButton(parent, size) + local button = CreateFrame("Button", nil, parent) + button:SetSize(size, size) + + local icon = button:CreateTexture(nil, "ARTWORK") + icon:SetAllPoints(button) + icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark") + button.icon = icon + + button:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square", "ADD") + + return button +end + +local function requestBotInventory(botName) + if(botName == nil) then return end + + local unitsButtons = MultiBot.frames + and MultiBot.frames["MultiBar"] + and MultiBot.frames["MultiBar"].frames + and MultiBot.frames["MultiBar"].frames["Units"] + and MultiBot.frames["MultiBar"].frames["Units"].buttons + + local botButton = unitsButtons and unitsButtons[botName] or nil + if(botButton ~= nil) then botButton.waitFor = "INVENTORY" end + + SendChatMessage("items", "WHISPER", nil, botName) +end + +local function buildRow(parent, yOffset) + local rowFrame = CreateFrame("Frame", nil, parent) + rowFrame:SetSize(420, 30) + rowFrame:SetPoint("TOPLEFT", parent, "TOPLEFT", 10, yOffset) + + local panelBg = rowFrame:CreateTexture(nil, "BACKGROUND") + panelBg:SetAllPoints(rowFrame) + panelBg:SetTexture("Interface\\Buttons\\WHITE8x8") + panelBg:SetVertexColor(0.07, 0.07, 0.07, 0.45) + + local panelBorder = { + top = rowFrame:CreateTexture(nil, "BORDER"), + bottom = rowFrame:CreateTexture(nil, "BORDER"), + left = rowFrame:CreateTexture(nil, "BORDER"), + right = rowFrame:CreateTexture(nil, "BORDER"), + } + for _, edge in pairs(panelBorder) do + edge:SetTexture("Interface\\Buttons\\WHITE8x8") + edge:SetVertexColor(0.26, 0.26, 0.26, 0.95) + end + + panelBorder.top:SetPoint("TOPLEFT", rowFrame, "TOPLEFT", -1, 1) + panelBorder.top:SetPoint("TOPRIGHT", rowFrame, "TOPRIGHT", 1, 1) + panelBorder.top:SetHeight(1) + + panelBorder.bottom:SetPoint("BOTTOMLEFT", rowFrame, "BOTTOMLEFT", -1, -1) + panelBorder.bottom:SetPoint("BOTTOMRIGHT", rowFrame, "BOTTOMRIGHT", 1, -1) + panelBorder.bottom:SetHeight(1) + + panelBorder.left:SetPoint("TOPLEFT", rowFrame, "TOPLEFT", -1, 1) + panelBorder.left:SetPoint("BOTTOMLEFT", rowFrame, "BOTTOMLEFT", -1, -1) + panelBorder.left:SetWidth(1) + + panelBorder.right:SetPoint("TOPRIGHT", rowFrame, "TOPRIGHT", 1, 1) + panelBorder.right:SetPoint("BOTTOMRIGHT", rowFrame, "BOTTOMRIGHT", 1, -1) + panelBorder.right:SetWidth(1) + + local nameText = rowFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") + nameText:SetPoint("LEFT", rowFrame, "LEFT", 6, 0) + nameText:SetWidth(160) + nameText:SetJustifyH("LEFT") + nameText:SetText("|cffffcc00NAME - CLASS|r") + + local row = { + frame = rowFrame, + buttons = {}, + name = nil, + class = nil, + panelBg = panelBg, + panelBorder = panelBorder, + } + + function row:Show() + self.frame:Show() + if(self.panelBg) then self.panelBg:Show() end + if(self.panelBorder) then + for _, edge in pairs(self.panelBorder) do edge:Show() end + end + end + + function row:Hide() + self.frame:Hide() + if(self.panelBg) then self.panelBg:Hide() end + if(self.panelBorder) then + for _, edge in pairs(self.panelBorder) do edge:Hide() end + end + end + + function row.setText(_, textOrId, value) + nameText:SetText(value or textOrId or "") + end + + local inspectButton = CreateFrame("Button", nil, rowFrame, "UIPanelButtonTemplate") + inspectButton:SetHeight(20) + inspectButton:SetPoint("LEFT", nameText, "RIGHT", MB_REWARD_INSPECT_X_OFFSET, 0) + inspectButton:SetText(INSPECT or "Inspect") + + local inspectText = inspectButton:GetFontString() + local inspectWidth = 50 + if(inspectText ~= nil and inspectText.GetStringWidth) then + inspectWidth = math.max(50, math.ceil((inspectText:GetStringWidth() or 0) + 24)) + end + inspectButton:SetWidth(inspectWidth) + + local inspectProxy = { + parent = row, + doLeft = nil, + } + function inspectProxy.getName() return row.name end + inspectButton:SetScript("OnClick", function() + if(inspectProxy.doLeft) then inspectProxy.doLeft(inspectProxy) end + end) + inspectButton:SetScript("OnEnter", function() + GameTooltip:SetOwner(inspectButton, "ANCHOR_RIGHT") + GameTooltip:SetText(MultiBot.L("tips.creator.inspect") or (INSPECT or "Inspect"), 1, 1, 1, true) + GameTooltip:Show() + end) + inspectButton:SetScript("OnLeave", function() GameTooltip:Hide() end) + inspectProxy.doLeft = function(pButton) + local tName = pButton.getName() + if(tName) then + InspectUnit(tName) + requestBotInventory(tName) + end + end + row.inspect = inspectProxy + + local buttonStartX = math.max(220, 170 + MB_REWARD_INSPECT_X_OFFSET + inspectWidth) + for i = 1, 6 do + local btn = makeIconButton(rowFrame, 20) + btn:SetPoint("LEFT", rowFrame, "LEFT", buttonStartX + ((i - 1) * 24), 0) + + local proxy = { + frame = btn, + parent = row, + link = nil, + doLeft = nil, + } + + function proxy:Show() self.frame:Show() end + function proxy:Hide() self.frame:Hide() end + function proxy.getName() return row.name end + function proxy.setButton(iconPath, link) + proxy.link = link + proxy.frame.icon:SetTexture(iconPath or "Interface\\Icons\\INV_Misc_QuestionMark") + end + + btn:SetScript("OnClick", function() + if(proxy.doLeft) then proxy.doLeft(proxy) end + end) + btn:SetScript("OnEnter", function() + if(proxy.link) then + GameTooltip:SetOwner(btn, "ANCHOR_RIGHT") + GameTooltip:SetHyperlink(proxy.link) + GameTooltip:Show() + end + end) + btn:SetScript("OnLeave", function() GameTooltip:Hide() end) + + row.buttons["R" .. i] = proxy + end + + return row +end + +function MultiBot.InitializeRewardFrame() + local aceGUI = getRewardAceGUI() + if(not aceGUI) then + UIErrorsFrame:AddMessage("AceGUI-3.0 is required for Reward", 1, 0.2, 0.2, 1) + return + end + + local window = aceGUI:Create("Window") + window:SetTitle(MultiBot.L("info.reward")) + window:SetLayout("Manual") + window:SetWidth(460) + window:SetHeight(430) + window.frame:SetClampedToScreen(true) + window.frame:SetMovable(true) + window.frame:EnableMouse(true) + window.frame:SetFrameStrata("HIGH") + window.frame:SetPoint("BOTTOMRIGHT", UIParent, "BOTTOMRIGHT", -754, 238) + window:Hide() + window:SetCallback("OnClose", function(widget) + widget:Hide() + end) + + if(window.EnableResize) then window:EnableResize(false) end + if(window.SetStatusText) then window:SetStatusText("") end + + local content = window.content + content:SetPoint("TOPLEFT", window.frame, "TOPLEFT", 12, -30) + content:SetPoint("BOTTOMRIGHT", window.frame, "BOTTOMRIGHT", -12, 12) + + local pageLabel = content:CreateFontString(nil, "OVERLAY", "GameFontNormal") + pageLabel:SetPoint("TOP", content, "TOP", 0, -2) + pageLabel:SetText(MB_PAGE_DEFAULT) + + local prevButton = CreateFrame("Button", nil, content, "UIPanelButtonTemplate") + prevButton:SetSize(26, 20) + prevButton:SetPoint("TOPLEFT", content, "TOPLEFT", 8, -2) + prevButton:SetText("<") + + local nextButton = CreateFrame("Button", nil, content, "UIPanelButtonTemplate") + nextButton:SetSize(26, 20) + nextButton:SetPoint("LEFT", prevButton, "RIGHT", 6, 0) + nextButton:SetText(">") + + local rows = {} + for i = 1, 12 do + local yOffset = -30 - ((i - 1) * 32) + rows[i] = buildRow(content, yOffset) + rows[i]:Hide() + end + + MultiBot.reward = { + state = false, + rewards = {}, + units = {}, + rows = rows, + window = window, + pageLabel = pageLabel, + prevButton = prevButton, + nextButton = nextButton, + pageSize = 12, + classIconSize = 18, + from = 1, + max = 1, + now = 1, + to = 12, + } + + function MultiBot.reward:Show() + self.window:Show() + end + + function MultiBot.reward:Hide() + self.window:Hide() + end + + function MultiBot.reward:IsVisible() + return self.window.frame and self.window.frame:IsShown() + end + + function MultiBot.reward:GetRight() + return self.window.frame:GetRight() + end + + function MultiBot.reward:GetBottom() + return self.window.frame:GetBottom() + end + + function MultiBot.reward.setPoint(x, y) + if(x == nil or y == nil) then return end + window.frame:ClearAllPoints() + window.frame:SetPoint("BOTTOMRIGHT", UIParent, "BOTTOMRIGHT", x, y) + end + + MultiBot.reward.doClose = function() + MultiBot.rewardTryClose() + end + + prevButton:SetScript("OnClick", function() + MultiBot.rewardChangePage(-1) + end) + + nextButton:SetScript("OnClick", function() + MultiBot.rewardChangePage(1) + end) +end \ No newline at end of file diff --git a/UI/MultiBotSpell.lua b/UI/MultiBotSpell.lua index 665e02f..11c9830 100644 --- a/UI/MultiBotSpell.lua +++ b/UI/MultiBotSpell.lua @@ -43,7 +43,9 @@ local function shouldFinishSpellbookCollection(pLine, pCollectionState) return true end - if(pCollectionState.hasCollectedSpell and pCollectionState.nonSpellStreak >= SPELLBOOK_END_NON_SPELL_STREAK) then + local tNonSpellStreak = tonumber(pCollectionState.nonSpellStreak) or 0 + local tNonSpellThreshold = tonumber(SPELLBOOK_END_NON_SPELL_STREAK) or 4 + if(pCollectionState.hasCollectedSpell and tNonSpellStreak >= tNonSpellThreshold) then return true end @@ -195,7 +197,7 @@ MultiBot.handleSpellbookChatLine = function(pButton, pLine, pSender) return true end - tCollectionState.nonSpellStreak = (tCollectionState.nonSpellStreak or 0) + 1 + tCollectionState.nonSpellStreak = (tonumber(tCollectionState.nonSpellStreak) or 0) + 1 if(shouldFinishSpellbookCollection(pLine, tCollectionState)) then if(MultiBot.finishSpellbookCollection) then MultiBot.finishSpellbookCollection() diff --git a/docs/ace3-expansion-checklist.md b/docs/ace3-expansion-checklist.md index 753e217..efe6c05 100644 --- a/docs/ace3-expansion-checklist.md +++ b/docs/ace3-expansion-checklist.md @@ -23,6 +23,7 @@ Checklist for the full addon-wide ACE3 expansion after M7 completion. - [x] Raidus migration/polish slice completed and finalized (`Features/MultiBotRaidus.lua`): Ace host window path + fallback, close-state sync with main button, score badges, drop feedback animation, and interactive contrast polish. - [x] Universal prompt migration slice completed (`Core/MultiBotInit.lua`: `MBUniversalPrompt`) with AceGUI window/edit/button path and no legacy frame fallback. - [x] SpellBook migration slice completed (`UI/MultiBotSpellBookFrame.lua` + `UI/MultiBotSpell.lua`): AceGUI host window, dynamic slot/check generation, normalized page-size handling, and stateful chat collection end-detection fallback. +- [x] Reward migration slice completed (`UI/MultiBotRewardFrame.lua` + `Features/MultiBotReward.lua`): native AceGUI window host, deduped Reward module exports, localized config popup on intended activation path, and stable multi-bot reward close/paging behavior. - [x] Talents/Glyphs frame migration slice completed (`UI/MultiBotTalentFrame.lua`): AceGUI host integration for the talents/glyphs workflow with preserved tab/copy/apply behavior and custom glyph interactions. - [x] GameObject popup/copy migration slice completed (`Core/MultiBotInit.lua`: `MB_GameObjPopup` + `MB_GameObjCopyBox`) with AceGUI windows/widgets path and no legacy frame fallback. - [x] Quest popup family migration slice completed (`Core/MultiBotInit.lua`: `MB_QuestPopup`, `MB_BotQuestPopup`, `MB_BotQuestCompPopup`, `MB_BotQuestAllPopup`) with AceGUI host windows and no legacy frame fallback. diff --git a/docs/ace3-ui-frame-inventory.md b/docs/ace3-ui-frame-inventory.md index 088db38..82a7d3f 100644 --- a/docs/ace3-ui-frame-inventory.md +++ b/docs/ace3-ui-frame-inventory.md @@ -65,6 +65,10 @@ Inventory of addon UI frame construction points found via `CreateFrame(...)` sca Files: `UI/MultiBotSpellBookFrame.lua`, `UI/MultiBotSpell.lua`, `Core/MultiBotHandler.lua`. References: lines `330`, `294`, `173`, `1517`. +- [x] **Reward window** (`MultiBot.reward`) migrated to a native AceGUI host window path with dedicated Reward module flow, stable close/paging behavior, and localized saved-state-aware config popup on activation. + Files: `UI/MultiBotRewardFrame.lua`, `Features/MultiBotReward.lua`, `Core/MultiBotInit.lua`. + References: `UI/MultiBotRewardFrame.lua:193`, `Features/MultiBotReward.lua:5`, `Core/MultiBotInit.lua:1510`. + - [x] **Talents/Glyphs frame** (`MultiBot.talent`) migrated with AceGUI host integration for the talents/glyphs workflow while preserving tab-state, copy/apply actions, and custom glyph socket interactions. File: `UI/MultiBotTalentFrame.lua`. References: lines `5`, `23`, `119`, `2228`.