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