comparison 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
comparison
equal deleted inserted replaced
-1:000000000000 0:ec731d2fe6ba
1 local _, AskMrRobot = ...
2
3 --------------------------------------------------------------------
4 -- Local Reforge Utility Code
5 --------------------------------------------------------------------
6
7 StaticPopupDialogs["REFORGE_TAB_PLEASE_OPEN"] = {
8 text = "You need to open the reforge window for this to work",
9 button1 = "Ok",
10 timeout = 0,
11 whileDead = true,
12 hideOnEscape = true,
13 preferredIndex = 3, -- avoid some UI taint, see http://www.wowace.com/announcements/how-to-avoid-some-ui-taint/
14 }
15
16 --from LibReforge
17 local SPI = 1
18 local DODGE = 2
19 local PARRY = 3
20 local HIT = 4
21 local CRIT = 5
22 local HASTE = 6
23 local EXP = 7
24 local MASTERY = 8
25
26 --from LibReforge
27 local StatNames = {
28 ITEM_MOD_SPIRIT_SHORT,
29 ITEM_MOD_DODGE_RATING_SHORT,
30 ITEM_MOD_PARRY_RATING_SHORT,
31 ITEM_MOD_HIT_RATING_SHORT,
32 ITEM_MOD_CRIT_RATING_SHORT,
33 ITEM_MOD_HASTE_RATING_SHORT,
34 ITEM_MOD_EXPERTISE_RATING_SHORT,
35 ITEM_MOD_MASTERY_RATING_SHORT
36 }
37 StatNames[0] = NONE
38 local StatToString = {
39 "ITEM_MOD_SPIRIT_SHORT",
40 "ITEM_MOD_DODGE_RATING_SHORT",
41 "ITEM_MOD_PARRY_RATING_SHORT",
42 "ITEM_MOD_HIT_RATING_SHORT",
43 "ITEM_MOD_CRIT_RATING_SHORT",
44 "ITEM_MOD_HASTE_RATING_SHORT",
45 "ITEM_MOD_EXPERTISE_RATING_SHORT",
46 "ITEM_MOD_MASTERY_RATING_SHORT"
47 }
48
49
50 local REFORGE_TABLE_BASE = 112
51 local REFORGE_TABLE = {
52 {1, 2}, {1, 3}, {1, 4}, {1, 5}, {1, 6}, {1, 7}, {1, 8},
53 {2, 1}, {2, 3}, {2, 4}, {2, 5}, {2, 6}, {2, 7}, {2, 8},
54 {3, 1}, {3, 2}, {3, 4}, {3, 5}, {3, 6}, {3, 7}, {3, 8},
55 {4, 1}, {4, 2}, {4, 3}, {4, 5}, {4, 6}, {4, 7}, {4, 8},
56 {5, 1}, {5, 2}, {5, 3}, {5, 4}, {5, 6}, {5, 7}, {5, 8},
57 {6, 1}, {6, 2}, {6, 3}, {6, 4}, {6, 5}, {6, 7}, {6, 8},
58 {7, 1}, {7, 2}, {7, 3}, {7, 4}, {7, 5}, {7, 6}, {7, 8},
59 {8, 1}, {8, 2}, {8, 3}, {8, 4}, {8, 5}, {8, 6}, {8, 7},
60 }
61
62 --------------- returns the index into the REFORGE_TABLE or nil
63 -- returns the reforge id or 0
64 local function GetReforgeIdForItem(item)
65 local id = tonumber(item:match("item:%d+:%d+:%d+:%d+:%d+:%d+:%-?%d+:%-?%d+:%d+:(%d+)"))
66 return (id and id ~= 0 and id or 0)
67 end
68
69 local function GetReforgeIdFromStats(fromStat, toStat)
70 if (toStat > fromStat) then
71 return REFORGE_TABLE_BASE + 7 * (fromStat - 1) + toStat - 1;
72 else
73 return REFORGE_TABLE_BASE + 7 * (fromStat - 1) + toStat;
74 end
75 end
76
77
78 --------------------------------------------------------------------
79 -- Initialization
80 --------------------------------------------------------------------
81 AskMrRobot.ReforgesTab = AskMrRobot.inheritsFrom(AskMrRobot.Frame)
82
83 function AskMrRobot.ReforgesTab:new(parent)
84
85 local tab = AskMrRobot.Frame:new(nil, parent)
86 setmetatable(tab, { __index = AskMrRobot.ReforgesTab })
87 tab:SetPoint("TOPLEFT")
88 tab:SetPoint("BOTTOMRIGHT")
89 tab:Hide()
90
91 local text = tab:CreateFontString("AmrReforgesHeader", "ARTWORK", "GameFontNormalLarge")
92 text:SetPoint("TOPLEFT", 0, -5)
93 text:SetText("Reforges")
94
95 tab.stamp = AskMrRobot.RobotStamp:new(nil, tab)
96 tab.stamp:Hide()
97 tab.stamp.smallText:SetText("Your reforges are 100% optimal!")
98 tab.stamp:SetPoint("TOPLEFT", text, "BOTTOMLEFT", 2, -15)
99 tab.stamp:SetPoint("RIGHT", tab, "RIGHT", -20, 0)
100
101 tab.reforgeDetails = tab:CreateFontString("AmrReforgeDetails", "ARTWORK", "GameFontWhite")
102 tab.reforgeDetails:SetPoint("TOPLEFT", text, "BOTTOMLEFT", 0, -15)
103 tab.reforgeDetails:SetPoint("RIGHT", -30, 0)
104 tab.reforgeDetails:SetWordWrap(true)
105 tab.reforgeDetails:SetJustifyH("LEFT")
106 tab.reforgeDetails:SetText('Open a reforge window, then click the "Reforge!" button to do it automatically.')
107 tab.reforgeDetails:SetHeight(50)
108
109 tab.reforgeButton = CreateFrame("Button", "AmrReforgeButton", tab, "UIPanelButtonTemplate")
110 tab.reforgeButton:SetText("Reforge!")
111 tab.reforgeButton:SetPoint("TOPLEFT", 0, -80)
112 tab.reforgeButton:SetWidth(140)
113 tab.reforgeButton:SetHeight(20)
114 tab.reforgeButton:SetScript("OnClick", function()
115 tab:OnReforge()
116 end)
117
118 tab.reforgeCost = tab:CreateFontString(nil, "ARTWORK", "GameFontNormal")
119 tab.reforgeCost:SetPoint("TOPLEFT", tab.reforgeButton, "TOPRIGHT", 25, 0)
120 tab.reforgeCost:SetPoint("BOTTOM", tab.reforgeButton, "BOTTOM", 0, 0)
121 tab.reforgeCost:SetPoint("RIGHT", tab, "RIGHT", -30, 0)
122 tab.reforgeCost:SetText('')
123
124 tab.slotHeader = tab:CreateFontString(nil, "ARTWORK", "GameFontNormal")
125 tab.slotHeader:SetText("Slot")
126 tab.slotHeader:SetPoint("TOPLEFT", tab.reforgeButton, "BOTTOMLEFT", 0, -30)
127
128 tab.reforgeHeader = tab:CreateFontString(nil, "ARTWORK", "GameFontNormal")
129 tab.reforgeHeader:SetText("Optimal Reforge")
130 tab.reforgeHeader:SetPoint("TOPLEFT", tab.slotHeader, "TOPLEFT", 100, 0)
131
132 -- pre-allocate a visual element for all possible slots; showBadReforges will set text and show the number that are needed, and hide the rest
133 tab.slots = {}
134 tab.optimized = {}
135
136 for i = 1, #AskMrRobot.slotNames do
137 tab.slots[i] = tab:CreateFontString(nil, "ARTWORK", "GameFontWhite")
138 tab.slots[i]:SetPoint("TOPLEFT", tab.slotHeader, "TOPLEFT", 0, -20 * i)
139 tab.slots[i]:Hide()
140
141 tab.optimized[i] = tab:CreateFontString(nil, "ARTWORK", "GameFontWhite")
142 tab.optimized[i]:SetPoint("TOPLEFT", tab.reforgeHeader, "TOPLEFT", 0, -20 * i)
143 tab.optimized[i]:Hide()
144 end
145
146 tab:RegisterEvent("FORGE_MASTER_ITEM_CHANGED")
147 tab:RegisterEvent("FORGE_MASTER_SET_ITEM")
148 tab:RegisterEvent("FORGE_MASTER_OPENED")
149 tab:RegisterEvent("FORGE_MASTER_CLOSED")
150
151 tab:SetScript("OnEvent", function(...)
152 tab:OnEvent(...)
153 end)
154
155
156 -- initialize stat required for performing the reforges
157 tab.state = {}
158 tab:ResetState()
159
160
161 return tab
162 end
163
164
165 --------------------------------------------------------------------
166 -- Rendering
167 --------------------------------------------------------------------
168
169 local function GetReforgeString(fromId, toId)
170 if toId == 0 then
171 return "Restore"
172 end
173 local pair = REFORGE_TABLE[toId - REFORGE_TABLE_BASE]
174
175 local text = _G[StatToString[pair[1]]] .. ' -> ' .. _G[StatToString[pair[2]]]
176 --print('from ' .. fromId)
177 if fromId == 0 then
178 return text
179 end
180 return 'Restore, then ' .. text
181 end
182
183 -- draw all of the reforges that still need to be performed
184 function AskMrRobot.ReforgesTab:Render()
185
186 local reforges = AskMrRobot.itemDiffs.reforges
187 local i = 1
188 local cost = 0
189
190 -- for all the bad items
191 for slotNum, badReforge in AskMrRobot.sortSlots(reforges) do
192
193 self.optimized[i]:SetText(GetReforgeString(badReforge.current, badReforge.optimized))
194 self.optimized[i]:Show()
195
196 self.slots[i]:SetText(_G[strupper(AskMrRobot.slotNames[slotNum])])
197 self.slots[i]:Show()
198
199 -- Restore is free, so only add cost for non-restore reforges
200 if badReforge.optimized > 0 then
201 local slotId = AskMrRobot.slotIds[slotNum]
202 local itemLink = GetInventoryItemLink("player", slotId)
203 cost = cost + (itemLink and select (11, GetItemInfo(itemLink)) or 0)
204 end
205
206 i = i + 1
207 end
208
209 self.reforgeCost:SetText("Total reforge cost: ~" .. math.ceil(cost / 10000) .. " Gold")
210
211 -- hide / show the headers
212 if i == 1 then
213 self.reforgeHeader:Hide()
214 self.slotHeader:Hide()
215 self.reforgeButton:Hide()
216 self.reforgeCost:Hide()
217 self.reforgeDetails:Hide()
218 self.stamp:Show()
219 else
220 self.stamp:Hide()
221 self.reforgeButton:Show()
222 self.reforgeCost:Show()
223 self.reforgeHeader:Show()
224 self.reforgeDetails:Show()
225 self.slotHeader:Show()
226 end
227
228 -- hide the remaining slots
229 while i <= #self.slots do
230 self.optimized[i]:Hide()
231 self.slots[i]:Hide()
232 i = i + 1
233 end
234 end
235
236 --------------------------------------------------------------------
237 -- Reforge Logic
238 --------------------------------------------------------------------
239
240 -- reset state for a fresh pass at automatic reforging
241 function AskMrRobot.ReforgesTab:ResetState()
242
243 self.state.queue = {} -- list of all reforges actions that still need to be performed
244 self.state.currentItem = nil -- the current item we are trying to reforge
245 self.state.pendingSlot = nil -- the slot that we have requested to place into the reforger
246 self.state.currentSlot = nil -- the current slot in the reforger
247 self.state.pendingReforge = -1 -- the reforge that we have requested to perform on the current item
248 end
249
250 -- refresh the queue of reforges that still need to be performed
251 function AskMrRobot.ReforgesTab:RefreshQueue()
252
253 -- clear the queue
254 self.state.queue = {}
255
256 local reforges = AskMrRobot.itemDiffs.reforges
257
258 -- add all reforges that need updating to the reforge queue
259 for slotNum, badReforge in AskMrRobot.sortSlots(reforges) do
260 self:AddToReforgeQueue(slotNum, badReforge.optimized);
261 end
262 end
263
264 function AskMrRobot.ReforgesTab:AddToReforgeQueue(itemSlot, reforgeId)
265
266 -- the game's slot id, not the same as the ids that we use on our servers
267 local gameSlot = AskMrRobot.slotIds[itemSlot]
268
269 local item = GetInventoryItemLink("player", gameSlot)
270 if item == nil then
271 --print ('no item')
272 return
273 end
274
275 local current = GetReforgeIdForItem(item)
276
277 if current ~= reforgeId then
278 -- restore first
279 if current ~= 0 and reforgeId ~= 0 then
280 tinsert(self.state.queue, { ["slot"] = gameSlot, ["reforge"] = 0 })
281 end
282
283 -- then reforge to the specified reforge
284 tinsert(self.state.queue, { ["slot"] = gameSlot, ["reforge"] = reforgeId })
285 end
286 end
287
288 function AskMrRobot.ReforgesTab:IsQueueEmpty()
289 return self.state.queue == nil or #self.state.queue == 0 or self.state.queue == {};
290 end
291
292 -- returns true if we are waiting on the game to finish a pending async reforge operation
293 function AskMrRobot.ReforgesTab:HasPendingOperation()
294
295 -- waiting for an item to be placed into the reforger
296 if self.state.pendingSlot then
297 return true
298 end
299
300 -- waiting for a reforge to be completed
301 if self.state.pendingReforge ~= -1 then
302 return true
303 end
304
305 return false
306 end
307
308 -- put the next item in the reforge queue into the game's reforge UI
309 function AskMrRobot.ReforgesTab:PutNextItemInForge()
310
311 if self:IsQueueEmpty() or self:HasPendingOperation() then
312 return
313 end
314
315 -- get the first action in the queue
316 local currentAction = self.state.queue[1]
317 local itemSlot = currentAction.slot
318
319 local item = GetInventoryItemLink("player", itemSlot)
320
321 -- set current item that we are trying to reforge
322 self.state.currentItem = item
323
324 -- if this item isn't already in the reforger, put it in
325 if self.state.currentSlot ~= itemSlot then
326 ClearCursor() -- make sure no item is selected
327 SetReforgeFromCursorItem() -- pick up the old item (calling this with an item already in the reforger will put it back on the mouse cursor)
328 ClearCursor() -- clear the cursor to finish removing any current item from the game reforge UI
329 PickupInventoryItem(itemSlot) -- pick up the right equipped item
330
331 -- pending, listen for an event from the game to complete setting this item into the reforger
332 self.state.pendingSlot = itemSlot
333
334 SetReforgeFromCursorItem() -- put the item into the reforger, and wait for the FORGE_MASTER_SET_ITEM event, which calls DoReforge
335 end
336
337 end
338
339 -- an item is in the reforger, ready to be reforged, so do it
340 function AskMrRobot.ReforgesTab:DoReforge()
341
342 if self:IsQueueEmpty() or self:HasPendingOperation() then
343 return
344 end
345
346 local currentAction = self.state.queue[1]
347 local desiredReforge = currentAction.reforge
348
349 -- the index that needs to be provided to WoW's ReforgeItem method, corresponds to one of the options in the game reforge UI
350 local reforgeIndex = -1
351
352 if desiredReforge ~= 0 then
353 local targetFrom = REFORGE_TABLE[desiredReforge - REFORGE_TABLE_BASE][1];
354 local targetTo = REFORGE_TABLE[desiredReforge - REFORGE_TABLE_BASE][2];
355
356 for i=1, GetNumReforgeOptions() do
357 local from, _, _, to, _, _ = GetReforgeOptionInfo(i)
358 --print(i .. ': ' .. from .. " -> " .. to)
359 if StatNames[targetFrom] == from and StatNames[targetTo] == to then
360 reforgeIndex = i
361 break
362 end
363 end
364 else
365 reforgeIndex = 0
366 end
367
368 if reforgeIndex == -1 then
369 -- 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
370
371 -- remove the item from the reforge window
372 ClearCursor()
373 SetReforgeFromCursorItem()
374 ClearCursor()
375
376 -- reset state and quit reforging (clears the queue)
377 self:ResetState()
378
379 else
380
381 local currentReforge = GetReforgeIdForItem(self.state.currentItem);
382 if currentReforge == desiredReforge then
383 -- we already have this reforge on the item... probably shouldn't ever happen, but if it does, recalculate and start over
384 tremove(self.state.queue, 1)
385
386 -- remove the item from the reforge window
387 ClearCursor()
388 SetReforgeFromCursorItem()
389 ClearCursor()
390
391 -- update the state of the entire UI, and start again with the next required reforge
392 AskMrRobot_ReforgeFrame:OnUpdate()
393 self:OnReforge()
394
395 else
396 -- we have a reforge (or restore) to do, kick it off and wait for CheckReforge to respond to completion
397 self:TryReforge(reforgeIndex)
398 end
399
400 end
401
402 end
403
404 -- wraps WoW API call to ReforgeItem, fires a manual timeout in case the UI does not raise an event
405 function AskMrRobot.ReforgesTab:TryReforge(reforgeIndex)
406
407 -- we have a reforge (or restore) to do, kick it off and wait for FORGE_MASTER_ITEM_CHANGED, which calls CheckReforge
408 self.state.pendingReforge = reforgeIndex
409 ReforgeItem(reforgeIndex)
410
411 -- sometimes the game doesn't send the FORGE_MASTER_ITEM_CHANGED event, so also check after a delay also
412 AskMrRobot.wait(0.250, AskMrRobot.ReforgesTab.CheckReforge, self)
413
414 end
415
416 -- check that a requested reforge has been completed
417 function AskMrRobot.ReforgesTab:CheckReforge()
418
419 if self:IsQueueEmpty() or self.state.pendingReforge == -1 then
420
421 -- responding to a reforge that the user has manually performed, update the UI and terminate any automatic process that is going on
422 AskMrRobot_ReforgeFrame:OnUpdate()
423 self:ResetState()
424
425 else
426 -- responding to a reforge that we have initiated
427
428 local currentReforge, icon, name, quality, bound, cost = GetReforgeItemInfo();
429 if currentReforge == self.state.pendingReforge then
430 tremove(self.state.queue, 1)
431
432 -- remove the item from the reforge window
433 ClearCursor()
434 SetReforgeFromCursorItem()
435 ClearCursor()
436
437 -- update the state of the entire UI, and start again with the next required reforge
438 AskMrRobot_ReforgeFrame:OnUpdate()
439 self:OnReforge()
440 else
441 -- try again
442 self:TryReforge(self.state.pendingReforge)
443 end
444 end
445
446 end
447
448
449 --------------------------------------------------------------------
450 -- Event Handling
451 --------------------------------------------------------------------
452
453 -- event called when the Mr. Robot Reforge button is clicked, kicks off automatic reforge
454 function AskMrRobot.ReforgesTab:OnReforge()
455
456 -- need to be at a reforger for this to work
457 if not self.isReforgeOpen then
458 StaticPopup_Show("REFORGE_TAB_PLEASE_OPEN")
459 return
460 end
461
462 -- reset state and refresh the queue of reforges that still need to be done
463 self:ResetState()
464 self:RefreshQueue()
465
466 -- get goin, put the first item in the reforger
467 self:PutNextItemInForge()
468 end
469
470 function AskMrRobot.ReforgesTab:On_FORGE_MASTER_SET_ITEM()
471
472 if self.state.pendingSlot then
473
474 -- we have successfully finished placing an item into the reforger
475 self.state.currentSlot = self.state.pendingSlot
476
477 -- indicate that we are no longer waiting for an item
478 self.state.pendingSlot = nil
479
480 -- now reforge it
481 self:DoReforge()
482 end
483
484 end
485
486 function AskMrRobot.ReforgesTab:On_FORGE_MASTER_ITEM_CHANGED()
487 self:CheckReforge()
488 end
489
490 function AskMrRobot.ReforgesTab:On_FORGE_MASTER_OPENED()
491 self.isReforgeOpen = true
492 end
493
494 function AskMrRobot.ReforgesTab:On_FORGE_MASTER_CLOSED()
495 self.isReforgeOpen = false
496 end
497
498 function AskMrRobot.ReforgesTab:OnEvent(frame, event, ...)
499 --print("EVENT " .. event)
500 local handler = self["On_" .. event]
501 if handler then
502 handler(self, ...)
503 end
504 end