Mercurial > wow > askmrrobot
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 |