Mercurial > wow > askmrrobot
diff ui/ReforgesTab.lua @ 0:ec731d2fe6ba
Version 1.2.12.0
author | Adam tegen <adam.tegen@gmail.com> |
---|---|
date | Tue, 20 May 2014 21:43:23 -0500 |
parents | |
children | ece9167c0d1c |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/ReforgesTab.lua Tue May 20 21:43:23 2014 -0500 @@ -0,0 +1,504 @@ +local _, AskMrRobot = ... + +-------------------------------------------------------------------- +-- Local Reforge Utility Code +-------------------------------------------------------------------- + +StaticPopupDialogs["REFORGE_TAB_PLEASE_OPEN"] = { + text = "You need to open the reforge window for this to work", + button1 = "Ok", + timeout = 0, + whileDead = true, + hideOnEscape = true, + preferredIndex = 3, -- avoid some UI taint, see http://www.wowace.com/announcements/how-to-avoid-some-ui-taint/ +} + +--from LibReforge +local SPI = 1 +local DODGE = 2 +local PARRY = 3 +local HIT = 4 +local CRIT = 5 +local HASTE = 6 +local EXP = 7 +local MASTERY = 8 + +--from LibReforge +local StatNames = { + ITEM_MOD_SPIRIT_SHORT, + ITEM_MOD_DODGE_RATING_SHORT, + ITEM_MOD_PARRY_RATING_SHORT, + ITEM_MOD_HIT_RATING_SHORT, + ITEM_MOD_CRIT_RATING_SHORT, + ITEM_MOD_HASTE_RATING_SHORT, + ITEM_MOD_EXPERTISE_RATING_SHORT, + ITEM_MOD_MASTERY_RATING_SHORT +} +StatNames[0] = NONE +local StatToString = { + "ITEM_MOD_SPIRIT_SHORT", + "ITEM_MOD_DODGE_RATING_SHORT", + "ITEM_MOD_PARRY_RATING_SHORT", + "ITEM_MOD_HIT_RATING_SHORT", + "ITEM_MOD_CRIT_RATING_SHORT", + "ITEM_MOD_HASTE_RATING_SHORT", + "ITEM_MOD_EXPERTISE_RATING_SHORT", + "ITEM_MOD_MASTERY_RATING_SHORT" +} + + +local REFORGE_TABLE_BASE = 112 +local REFORGE_TABLE = { + {1, 2}, {1, 3}, {1, 4}, {1, 5}, {1, 6}, {1, 7}, {1, 8}, + {2, 1}, {2, 3}, {2, 4}, {2, 5}, {2, 6}, {2, 7}, {2, 8}, + {3, 1}, {3, 2}, {3, 4}, {3, 5}, {3, 6}, {3, 7}, {3, 8}, + {4, 1}, {4, 2}, {4, 3}, {4, 5}, {4, 6}, {4, 7}, {4, 8}, + {5, 1}, {5, 2}, {5, 3}, {5, 4}, {5, 6}, {5, 7}, {5, 8}, + {6, 1}, {6, 2}, {6, 3}, {6, 4}, {6, 5}, {6, 7}, {6, 8}, + {7, 1}, {7, 2}, {7, 3}, {7, 4}, {7, 5}, {7, 6}, {7, 8}, + {8, 1}, {8, 2}, {8, 3}, {8, 4}, {8, 5}, {8, 6}, {8, 7}, +} + +--------------- returns the index into the REFORGE_TABLE or nil +-- returns the reforge id or 0 +local function GetReforgeIdForItem(item) + local id = tonumber(item:match("item:%d+:%d+:%d+:%d+:%d+:%d+:%-?%d+:%-?%d+:%d+:(%d+)")) + return (id and id ~= 0 and id or 0) +end + +local function GetReforgeIdFromStats(fromStat, toStat) + if (toStat > fromStat) then + return REFORGE_TABLE_BASE + 7 * (fromStat - 1) + toStat - 1; + else + return REFORGE_TABLE_BASE + 7 * (fromStat - 1) + toStat; + end +end + + +-------------------------------------------------------------------- +-- Initialization +-------------------------------------------------------------------- +AskMrRobot.ReforgesTab = AskMrRobot.inheritsFrom(AskMrRobot.Frame) + +function AskMrRobot.ReforgesTab:new(parent) + + local tab = AskMrRobot.Frame:new(nil, parent) + setmetatable(tab, { __index = AskMrRobot.ReforgesTab }) + tab:SetPoint("TOPLEFT") + tab:SetPoint("BOTTOMRIGHT") + tab:Hide() + + local text = tab:CreateFontString("AmrReforgesHeader", "ARTWORK", "GameFontNormalLarge") + text:SetPoint("TOPLEFT", 0, -5) + text:SetText("Reforges") + + tab.stamp = AskMrRobot.RobotStamp:new(nil, tab) + tab.stamp:Hide() + tab.stamp.smallText:SetText("Your reforges are 100% optimal!") + tab.stamp:SetPoint("TOPLEFT", text, "BOTTOMLEFT", 2, -15) + tab.stamp:SetPoint("RIGHT", tab, "RIGHT", -20, 0) + + tab.reforgeDetails = tab:CreateFontString("AmrReforgeDetails", "ARTWORK", "GameFontWhite") + tab.reforgeDetails:SetPoint("TOPLEFT", text, "BOTTOMLEFT", 0, -15) + tab.reforgeDetails:SetPoint("RIGHT", -30, 0) + tab.reforgeDetails:SetWordWrap(true) + tab.reforgeDetails:SetJustifyH("LEFT") + tab.reforgeDetails:SetText('Open a reforge window, then click the "Reforge!" button to do it automatically.') + tab.reforgeDetails:SetHeight(50) + + tab.reforgeButton = CreateFrame("Button", "AmrReforgeButton", tab, "UIPanelButtonTemplate") + tab.reforgeButton:SetText("Reforge!") + tab.reforgeButton:SetPoint("TOPLEFT", 0, -80) + tab.reforgeButton:SetWidth(140) + tab.reforgeButton:SetHeight(20) + tab.reforgeButton:SetScript("OnClick", function() + tab:OnReforge() + end) + + tab.reforgeCost = tab:CreateFontString(nil, "ARTWORK", "GameFontNormal") + tab.reforgeCost:SetPoint("TOPLEFT", tab.reforgeButton, "TOPRIGHT", 25, 0) + tab.reforgeCost:SetPoint("BOTTOM", tab.reforgeButton, "BOTTOM", 0, 0) + tab.reforgeCost:SetPoint("RIGHT", tab, "RIGHT", -30, 0) + tab.reforgeCost:SetText('') + + tab.slotHeader = tab:CreateFontString(nil, "ARTWORK", "GameFontNormal") + tab.slotHeader:SetText("Slot") + tab.slotHeader:SetPoint("TOPLEFT", tab.reforgeButton, "BOTTOMLEFT", 0, -30) + + tab.reforgeHeader = tab:CreateFontString(nil, "ARTWORK", "GameFontNormal") + tab.reforgeHeader:SetText("Optimal Reforge") + tab.reforgeHeader:SetPoint("TOPLEFT", tab.slotHeader, "TOPLEFT", 100, 0) + + -- pre-allocate a visual element for all possible slots; showBadReforges will set text and show the number that are needed, and hide the rest + tab.slots = {} + tab.optimized = {} + + for i = 1, #AskMrRobot.slotNames do + tab.slots[i] = tab:CreateFontString(nil, "ARTWORK", "GameFontWhite") + tab.slots[i]:SetPoint("TOPLEFT", tab.slotHeader, "TOPLEFT", 0, -20 * i) + tab.slots[i]:Hide() + + tab.optimized[i] = tab:CreateFontString(nil, "ARTWORK", "GameFontWhite") + tab.optimized[i]:SetPoint("TOPLEFT", tab.reforgeHeader, "TOPLEFT", 0, -20 * i) + tab.optimized[i]:Hide() + end + + tab:RegisterEvent("FORGE_MASTER_ITEM_CHANGED") + tab:RegisterEvent("FORGE_MASTER_SET_ITEM") + tab:RegisterEvent("FORGE_MASTER_OPENED") + tab:RegisterEvent("FORGE_MASTER_CLOSED") + + tab:SetScript("OnEvent", function(...) + tab:OnEvent(...) + end) + + + -- initialize stat required for performing the reforges + tab.state = {} + tab:ResetState() + + + return tab +end + + +-------------------------------------------------------------------- +-- Rendering +-------------------------------------------------------------------- + +local function GetReforgeString(fromId, toId) + if toId == 0 then + return "Restore" + end + local pair = REFORGE_TABLE[toId - REFORGE_TABLE_BASE] + + local text = _G[StatToString[pair[1]]] .. ' -> ' .. _G[StatToString[pair[2]]] + --print('from ' .. fromId) + if fromId == 0 then + return text + end + return 'Restore, then ' .. text +end + +-- draw all of the reforges that still need to be performed +function AskMrRobot.ReforgesTab:Render() + + local reforges = AskMrRobot.itemDiffs.reforges + local i = 1 + local cost = 0 + + -- for all the bad items + for slotNum, badReforge in AskMrRobot.sortSlots(reforges) do + + self.optimized[i]:SetText(GetReforgeString(badReforge.current, badReforge.optimized)) + self.optimized[i]:Show() + + self.slots[i]:SetText(_G[strupper(AskMrRobot.slotNames[slotNum])]) + self.slots[i]:Show() + + -- Restore is free, so only add cost for non-restore reforges + if badReforge.optimized > 0 then + local slotId = AskMrRobot.slotIds[slotNum] + local itemLink = GetInventoryItemLink("player", slotId) + cost = cost + (itemLink and select (11, GetItemInfo(itemLink)) or 0) + end + + i = i + 1 + end + + self.reforgeCost:SetText("Total reforge cost: ~" .. math.ceil(cost / 10000) .. " Gold") + + -- hide / show the headers + if i == 1 then + self.reforgeHeader:Hide() + self.slotHeader:Hide() + self.reforgeButton:Hide() + self.reforgeCost:Hide() + self.reforgeDetails:Hide() + self.stamp:Show() + else + self.stamp:Hide() + self.reforgeButton:Show() + self.reforgeCost:Show() + self.reforgeHeader:Show() + self.reforgeDetails:Show() + self.slotHeader:Show() + end + + -- hide the remaining slots + while i <= #self.slots do + self.optimized[i]:Hide() + self.slots[i]:Hide() + i = i + 1 + end +end + +-------------------------------------------------------------------- +-- Reforge Logic +-------------------------------------------------------------------- + +-- reset state for a fresh pass at automatic reforging +function AskMrRobot.ReforgesTab:ResetState() + + self.state.queue = {} -- list of all reforges actions that still need to be performed + self.state.currentItem = nil -- the current item we are trying to reforge + self.state.pendingSlot = nil -- the slot that we have requested to place into the reforger + self.state.currentSlot = nil -- the current slot in the reforger + self.state.pendingReforge = -1 -- the reforge that we have requested to perform on the current item +end + +-- refresh the queue of reforges that still need to be performed +function AskMrRobot.ReforgesTab:RefreshQueue() + + -- clear the queue + self.state.queue = {} + + local reforges = AskMrRobot.itemDiffs.reforges + + -- add all reforges that need updating to the reforge queue + for slotNum, badReforge in AskMrRobot.sortSlots(reforges) do + self:AddToReforgeQueue(slotNum, badReforge.optimized); + end +end + +function AskMrRobot.ReforgesTab:AddToReforgeQueue(itemSlot, reforgeId) + + -- the game's slot id, not the same as the ids that we use on our servers + local gameSlot = AskMrRobot.slotIds[itemSlot] + + local item = GetInventoryItemLink("player", gameSlot) + if item == nil then + --print ('no item') + return + end + + local current = GetReforgeIdForItem(item) + + if current ~= reforgeId then + -- restore first + if current ~= 0 and reforgeId ~= 0 then + tinsert(self.state.queue, { ["slot"] = gameSlot, ["reforge"] = 0 }) + end + + -- then reforge to the specified reforge + tinsert(self.state.queue, { ["slot"] = gameSlot, ["reforge"] = reforgeId }) + end +end + +function AskMrRobot.ReforgesTab:IsQueueEmpty() + return self.state.queue == nil or #self.state.queue == 0 or self.state.queue == {}; +end + +-- returns true if we are waiting on the game to finish a pending async reforge operation +function AskMrRobot.ReforgesTab:HasPendingOperation() + + -- waiting for an item to be placed into the reforger + if self.state.pendingSlot then + return true + end + + -- waiting for a reforge to be completed + if self.state.pendingReforge ~= -1 then + return true + end + + return false +end + +-- put the next item in the reforge queue into the game's reforge UI +function AskMrRobot.ReforgesTab:PutNextItemInForge() + + if self:IsQueueEmpty() or self:HasPendingOperation() then + return + end + + -- get the first action in the queue + local currentAction = self.state.queue[1] + local itemSlot = currentAction.slot + + local item = GetInventoryItemLink("player", itemSlot) + + -- set current item that we are trying to reforge + self.state.currentItem = item + + -- if this item isn't already in the reforger, put it in + if self.state.currentSlot ~= itemSlot then + ClearCursor() -- make sure no item is selected + SetReforgeFromCursorItem() -- pick up the old item (calling this with an item already in the reforger will put it back on the mouse cursor) + ClearCursor() -- clear the cursor to finish removing any current item from the game reforge UI + PickupInventoryItem(itemSlot) -- pick up the right equipped item + + -- pending, listen for an event from the game to complete setting this item into the reforger + self.state.pendingSlot = itemSlot + + SetReforgeFromCursorItem() -- put the item into the reforger, and wait for the FORGE_MASTER_SET_ITEM event, which calls DoReforge + end + +end + +-- an item is in the reforger, ready to be reforged, so do it +function AskMrRobot.ReforgesTab:DoReforge() + + if self:IsQueueEmpty() or self:HasPendingOperation() then + return + end + + local currentAction = self.state.queue[1] + local desiredReforge = currentAction.reforge + + -- the index that needs to be provided to WoW's ReforgeItem method, corresponds to one of the options in the game reforge UI + local reforgeIndex = -1 + + if desiredReforge ~= 0 then + local targetFrom = REFORGE_TABLE[desiredReforge - REFORGE_TABLE_BASE][1]; + local targetTo = REFORGE_TABLE[desiredReforge - REFORGE_TABLE_BASE][2]; + + for i=1, GetNumReforgeOptions() do + local from, _, _, to, _, _ = GetReforgeOptionInfo(i) + --print(i .. ': ' .. from .. " -> " .. to) + if StatNames[targetFrom] == from and StatNames[targetTo] == to then + reforgeIndex = i + break + end + end + else + reforgeIndex = 0 + end + + if reforgeIndex == -1 then + -- we couldn't do this reforge... we either had a bad reforge (wrong stats on an item), or the game had no options in the UI for some reason + + -- remove the item from the reforge window + ClearCursor() + SetReforgeFromCursorItem() + ClearCursor() + + -- reset state and quit reforging (clears the queue) + self:ResetState() + + else + + local currentReforge = GetReforgeIdForItem(self.state.currentItem); + if currentReforge == desiredReforge then + -- we already have this reforge on the item... probably shouldn't ever happen, but if it does, recalculate and start over + tremove(self.state.queue, 1) + + -- remove the item from the reforge window + ClearCursor() + SetReforgeFromCursorItem() + ClearCursor() + + -- update the state of the entire UI, and start again with the next required reforge + AskMrRobot_ReforgeFrame:OnUpdate() + self:OnReforge() + + else + -- we have a reforge (or restore) to do, kick it off and wait for CheckReforge to respond to completion + self:TryReforge(reforgeIndex) + end + + end + +end + +-- wraps WoW API call to ReforgeItem, fires a manual timeout in case the UI does not raise an event +function AskMrRobot.ReforgesTab:TryReforge(reforgeIndex) + + -- we have a reforge (or restore) to do, kick it off and wait for FORGE_MASTER_ITEM_CHANGED, which calls CheckReforge + self.state.pendingReforge = reforgeIndex + ReforgeItem(reforgeIndex) + + -- sometimes the game doesn't send the FORGE_MASTER_ITEM_CHANGED event, so also check after a delay also + AskMrRobot.wait(0.250, AskMrRobot.ReforgesTab.CheckReforge, self) + +end + +-- check that a requested reforge has been completed +function AskMrRobot.ReforgesTab:CheckReforge() + + if self:IsQueueEmpty() or self.state.pendingReforge == -1 then + + -- responding to a reforge that the user has manually performed, update the UI and terminate any automatic process that is going on + AskMrRobot_ReforgeFrame:OnUpdate() + self:ResetState() + + else + -- responding to a reforge that we have initiated + + local currentReforge, icon, name, quality, bound, cost = GetReforgeItemInfo(); + if currentReforge == self.state.pendingReforge then + tremove(self.state.queue, 1) + + -- remove the item from the reforge window + ClearCursor() + SetReforgeFromCursorItem() + ClearCursor() + + -- update the state of the entire UI, and start again with the next required reforge + AskMrRobot_ReforgeFrame:OnUpdate() + self:OnReforge() + else + -- try again + self:TryReforge(self.state.pendingReforge) + end + end + +end + + +-------------------------------------------------------------------- +-- Event Handling +-------------------------------------------------------------------- + +-- event called when the Mr. Robot Reforge button is clicked, kicks off automatic reforge +function AskMrRobot.ReforgesTab:OnReforge() + + -- need to be at a reforger for this to work + if not self.isReforgeOpen then + StaticPopup_Show("REFORGE_TAB_PLEASE_OPEN") + return + end + + -- reset state and refresh the queue of reforges that still need to be done + self:ResetState() + self:RefreshQueue() + + -- get goin, put the first item in the reforger + self:PutNextItemInForge() +end + +function AskMrRobot.ReforgesTab:On_FORGE_MASTER_SET_ITEM() + + if self.state.pendingSlot then + + -- we have successfully finished placing an item into the reforger + self.state.currentSlot = self.state.pendingSlot + + -- indicate that we are no longer waiting for an item + self.state.pendingSlot = nil + + -- now reforge it + self:DoReforge() + end + +end + +function AskMrRobot.ReforgesTab:On_FORGE_MASTER_ITEM_CHANGED() + self:CheckReforge() +end + +function AskMrRobot.ReforgesTab:On_FORGE_MASTER_OPENED() + self.isReforgeOpen = true +end + +function AskMrRobot.ReforgesTab:On_FORGE_MASTER_CLOSED() + self.isReforgeOpen = false +end + +function AskMrRobot.ReforgesTab:OnEvent(frame, event, ...) + --print("EVENT " .. event) + local handler = self["On_" .. event] + if handler then + handler(self, ...) + end +end \ No newline at end of file