vanilla-wow-addons – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | ------------------------------------------------------ |
2 | -- ReagentCost.lua |
||
3 | ------------------------------------------------------ |
||
4 | FRC_VERSION = "11200.1"; |
||
5 | ------------------------------------------------------ |
||
6 | |||
7 | FRC_Config = { }; |
||
8 | FRC_Config.Enabled = true; |
||
9 | FRC_Config.MinProfitRatio = 0; |
||
10 | FRC_Config.MinProfitMoney = nil; |
||
11 | FRC_Config.AutoLoadPriceSource = nil; |
||
12 | |||
13 | FRC_ReagentLinks = { }; |
||
14 | |||
15 | local MIN_SCANS = 35; -- times an item must be seen at auction to be considered a good sample (equates to 100% on our confidence scale) |
||
16 | local MIN_CONFIDENCE = 5; -- cutoff so we don't report items we have little data on as potentially profitable |
||
17 | local MIN_OVERRIDE_CONFIDENCE = 90; -- cutoff for trusting an item's market price versus the price of its components |
||
18 | |||
19 | -- Anti-freeze code borrowed from ReagentInfo (in turn, from Quest-I-On): |
||
20 | -- keeps WoW from locking up if we try to scan the tradeskill window too fast. |
||
21 | FRC_TradeSkillLock = { }; |
||
22 | FRC_TradeSkillLock.NeedScan = false; |
||
23 | FRC_TradeSkillLock.Locked = false; |
||
24 | FRC_TradeSkillLock.EventTimer = 0; |
||
25 | FRC_TradeSkillLock.EventCooldown = 0; |
||
26 | FRC_TradeSkillLock.EventCooldownTime = 1; |
||
27 | FRC_CraftLock = { }; |
||
28 | FRC_CraftLock.NeedScan = false; |
||
29 | FRC_CraftLock.Locked = false; |
||
30 | FRC_CraftLock.EventTimer = 0; |
||
31 | FRC_CraftLock.EventCooldown = 0; |
||
32 | FRC_CraftLock.EventCooldownTime = 1; |
||
33 | |||
34 | function FRC_CraftFrame_SetSelection(id) |
||
35 | FRC_Orig_CraftFrame_SetSelection(id); |
||
36 | |||
37 | if ( not id ) then |
||
38 | return; |
||
39 | end |
||
40 | local name, rank, maxRank = GetCraftDisplaySkillLine(); |
||
41 | if not (name) then |
||
42 | return; |
||
43 | end |
||
44 | local craftName, craftSubSpellName, craftType, numAvailable, isExpanded, trainingPointCost, requiredLevel = GetCraftInfo(id); |
||
45 | if ( trainingPointCost and trainingPointCost > 0 ) then |
||
46 | return; |
||
47 | end |
||
48 | if ( craftType == "header" ) then |
||
49 | return; |
||
50 | end |
||
51 | |||
52 | local costText; |
||
53 | if (FRC_Config.Enabled) then |
||
54 | local itemLink = GetCraftItemLink(id); |
||
55 | if not (itemLink) then |
||
56 | itemLink = craftName; -- Some enchanting formulae produce items (runed rods), most don't. Enchants don't link, items do. |
||
57 | end |
||
58 | itemLink = FRC_NormalizeLink(itemLink); |
||
59 | local materialsTotal, confidenceScore = FRC_MaterialsCost(name, itemLink); |
||
60 | costText = GFWUtils.LtY("(Total cost: "); |
||
61 | if (materialsTotal == nil) then |
||
62 | if (FRC_PriceSource == "Auctioneer" and not IsAddOnLoaded("Auctioneer")) then |
||
63 | costText = costText .. GFWUtils.Gray("[Auctioneer not loaded]"); |
||
64 | else |
||
65 | costText = costText .. GFWUtils.Gray("Unknown [insufficient data]"); |
||
66 | end |
||
67 | else |
||
68 | costText = costText .. GFWUtils.TextGSC(materialsTotal) ..GFWUtils.Gray(" Confidence: "..confidenceScore.."%"); |
||
69 | end |
||
70 | costText = costText ..GFWUtils.LtY(")"); |
||
71 | |||
72 | CraftReagentLabel:SetText(SPELL_REAGENTS.." "..costText); |
||
73 | CraftReagentLabel:Show(); |
||
74 | end |
||
75 | |||
76 | end |
||
77 | |||
78 | function FRC_TradeSkillFrame_SetSelection(id) |
||
79 | FRC_Orig_TradeSkillFrame_SetSelection(id); |
||
80 | |||
81 | if ( not id ) then |
||
82 | return; |
||
83 | end |
||
84 | local skillName, skillType, numAvailable, isExpanded = GetTradeSkillInfo(id); |
||
85 | if ( skillType == "header" ) then |
||
86 | return; |
||
87 | end |
||
88 | local skillLineName, skillLineRank, skillLineMaxRank = GetTradeSkillLine(); |
||
89 | |||
90 | local costText; |
||
91 | if (FRC_Config.Enabled) then |
||
92 | local link = GetTradeSkillItemLink(id); |
||
93 | if (link == nil) then return; end |
||
94 | link = FRC_NormalizeLink(link); |
||
95 | |||
96 | local materialsTotal, confidenceScore = FRC_MaterialsCost(skillLineName, GetTradeSkillItemLink(id)); |
||
97 | costText = GFWUtils.LtY("(Total cost: "); |
||
98 | if (materialsTotal == nil) then |
||
99 | if (FRC_PriceSource == "Auctioneer" and not IsAddOnLoaded("Auctioneer")) then |
||
100 | costText = costText .. GFWUtils.Gray("[Auctioneer not loaded]"); |
||
101 | else |
||
102 | costText = costText .. GFWUtils.Gray("Unknown [insufficient data]"); |
||
103 | end |
||
104 | else |
||
105 | costText = costText .. GFWUtils.TextGSC(materialsTotal) ..GFWUtils.Gray(" Confidence: "..confidenceScore.."%"); |
||
106 | end |
||
107 | costText = costText ..GFWUtils.LtY(")"); |
||
108 | |||
109 | TradeSkillReagentLabel:SetText(SPELL_REAGENTS.." "..costText); |
||
110 | TradeSkillReagentLabel:Show(); |
||
111 | end |
||
112 | |||
113 | end |
||
114 | |||
115 | function FRC_TradeSkillFrame_Update() |
||
116 | FRC_Orig_TradeSkillFrame_Update(); |
||
117 | |||
118 | FRC_ScanTradeSkill(); |
||
119 | end |
||
120 | |||
121 | function FRC_CraftFrame_Update() |
||
122 | FRC_Orig_CraftFrame_Update(); |
||
123 | |||
124 | FRC_ScanCraft(); |
||
125 | end |
||
126 | |||
127 | function FRC_OnLoad() |
||
128 | |||
129 | -- Register Slash Commands |
||
130 | SLASH_FRC1 = "/reagentcost"; |
||
131 | SLASH_FRC2 = "/rc"; |
||
132 | SlashCmdList["FRC"] = function(msg) |
||
133 | FRC_ChatCommandHandler(msg); |
||
134 | end |
||
135 | |||
136 | local name, title, notes, enabled, loadable, reason, security = GetAddOnInfo("Auctioneer"); |
||
137 | if (loadable or IsAddOnLoaded("Auctioneer")) then |
||
138 | FRC_PriceSource = "Auctioneer"; |
||
139 | elseif (KC_Auction ~= nil) then |
||
140 | FRC_PriceSource = "KC_Items"; |
||
141 | elseif (AuctionMatrix_Version ~= nil) then |
||
142 | FRC_PriceSource = "AuctionMatrix"; |
||
143 | elseif (WOWEcon_Enabled ~= nil) then |
||
144 | FRC_PriceSource = "WOWEcon_PriceMod"; |
||
145 | end |
||
146 | |||
147 | this:RegisterEvent("CRAFT_SHOW"); |
||
148 | this:RegisterEvent("CRAFT_UPDATE"); |
||
149 | this:RegisterEvent("TRADE_SKILL_SHOW"); |
||
150 | this:RegisterEvent("TRADE_SKILL_UPDATE"); |
||
151 | this:RegisterEvent("ADDON_LOADED"); |
||
152 | this:RegisterEvent("VARIABLES_LOADED"); |
||
153 | |||
154 | GFWUtils.Print("Fizzwidget Reagent Cost "..FRC_VERSION.." initialized!"); |
||
155 | |||
156 | end |
||
157 | |||
158 | function FRC_OnUpdate(elapsed) |
||
159 | -- If it's been more than a second since our last tradeskill update, |
||
160 | -- we can allow the event to process again. |
||
161 | FRC_TradeSkillLock.EventTimer = FRC_TradeSkillLock.EventTimer + elapsed; |
||
162 | if (FRC_TradeSkillLock.Locked) then |
||
163 | FRC_TradeSkillLock.EventCooldown = FRC_TradeSkillLock.EventCooldown + elapsed; |
||
164 | if (FRC_TradeSkillLock.EventCooldown > FRC_TradeSkillLock.EventCooldownTime) then |
||
165 | |||
166 | FRC_TradeSkillLock.EventCooldown = 0; |
||
167 | FRC_TradeSkillLock.Locked = false; |
||
168 | end |
||
169 | end |
||
170 | FRC_CraftLock.EventTimer = FRC_CraftLock.EventTimer + elapsed; |
||
171 | if (FRC_CraftLock.Locked) then |
||
172 | FRC_CraftLock.EventCooldown = FRC_CraftLock.EventCooldown + elapsed; |
||
173 | if (FRC_CraftLock.EventCooldown > FRC_CraftLock.EventCooldownTime) then |
||
174 | |||
175 | FRC_CraftLock.EventCooldown = 0; |
||
176 | FRC_CraftLock.Locked = false; |
||
177 | end |
||
178 | end |
||
179 | |||
180 | if (FRC_TradeSkillLock.NeedScan) then |
||
181 | FRC_TradeSkillLock.NeedScan = false; |
||
182 | FRC_ScanTradeSkill(); |
||
183 | end |
||
184 | if (FRC_CraftLock.NeedScan) then |
||
185 | FRC_CraftLock.NeedScan = false; |
||
186 | FRC_ScanCraft(); |
||
187 | end |
||
188 | end |
||
189 | |||
190 | function FRC_OnEvent(event) |
||
191 | |||
192 | if (event == "ADDON_LOADED" and (arg1 == "Blizzard_CraftUI" or IsAddOnLoaded("Blizzard_CraftUI"))) then |
||
193 | if (FRC_Orig_CraftFrame_SetSelection == nil) then |
||
194 | -- Overrides for displaying info in CraftFrame |
||
195 | FRC_Orig_CraftFrame_SetSelection = CraftFrame_SetSelection; |
||
196 | CraftFrame_SetSelection = FRC_CraftFrame_SetSelection; |
||
197 | |||
198 | -- And for scanning, since it looks like doing it in event handlers is crashy/unreliable now. |
||
199 | FRC_Orig_CraftFrame_Update = CraftFrame_Update; |
||
200 | CraftFrame_Update = FRC_CraftFrame_Update; |
||
201 | |||
202 | GFWUtils.Print("ReagentCost CraftFrame hooks installed."); |
||
203 | end |
||
204 | end |
||
205 | |||
206 | if (event == "ADDON_LOADED" and (arg1 == "Blizzard_TradeSkillUI" or IsAddOnLoaded("Blizzard_TradeSkillUI"))) then |
||
207 | if (FRC_Orig_TradeSkillFrame_SetSelection == nil) then |
||
208 | -- Overrides for displaying info in TradeSkillFrame |
||
209 | FRC_Orig_TradeSkillFrame_SetSelection = TradeSkillFrame_SetSelection; |
||
210 | TradeSkillFrame_SetSelection = FRC_TradeSkillFrame_SetSelection; |
||
211 | |||
212 | -- And for scanning, since it looks like doing it in event handlers is crashy/unreliable now. |
||
213 | FRC_Orig_TradeSkillFrame_Update = TradeSkillFrame_Update; |
||
214 | TradeSkillFrame_Update = FRC_TradeSkillFrame_Update; |
||
215 | |||
216 | GFWUtils.Print("ReagentCost TradeSkillFrame hooks installed."); |
||
217 | end |
||
218 | end |
||
219 | |||
220 | if ( event == "VARIABLES_LOADED" or event == "ADDON_LOADED" ) then |
||
221 | |||
222 | local name, title, notes, enabled, loadable, reason, security = GetAddOnInfo("Auctioneer"); |
||
223 | if ((loadable or IsAddOnLoaded("Auctioneer")) and FRC_PriceSource == nil) then |
||
224 | FRC_PriceSource = "Auctioneer"; |
||
225 | end |
||
226 | if (AUCTIONEER_VERSION ~= nil) then |
||
227 | local _, _, major, minor = string.find(AUCTIONEER_VERSION, "^(%d+)%.(%d+)"); |
||
228 | major, minor = tonumber(major), tonumber(minor); |
||
229 | if (major ~= nil and major >= 3 and minor ~= nil and minor >= 1) then |
||
230 | FRC_PriceSource = "Auctioneer"; |
||
231 | end |
||
232 | end |
||
233 | return; |
||
234 | end |
||
235 | |||
236 | if ( event == "TRADE_SKILL_SHOW" or event == "CRAFT_SHOW" and FRC_Config.Enabled) then |
||
237 | |||
238 | if (event == "CRAFT_SHOW" and GetCraftDisplaySkillLine() == nil) then |
||
239 | -- Beast Training uses the CraftFrame; we can tell when it's up because it doesn't have a skill-level bar. |
||
240 | -- We don't have anything to do in that case, so let's not try loading Auctioneer and stuff. |
||
241 | return; |
||
242 | end |
||
243 | |||
244 | local name, title, notes, enabled, loadable, reason, security = GetAddOnInfo("Auctioneer"); |
||
245 | if ((loadable or IsAddOnLoaded("Auctioneer")) and FRC_PriceSource == nil) then |
||
246 | FRC_PriceSource = "Auctioneer"; |
||
247 | end |
||
248 | if ( FRC_PriceSource == "Auctioneer" and FRC_Config.AutoLoadPriceSource) then |
||
249 | if (not IsAddOnLoaded("Auctioneer")) then |
||
250 | local loaded, reason = LoadAddOn("Auctioneer"); |
||
251 | if (not loaded) then |
||
252 | GFWUtils.Print("Can't load Auctioneer: "..reason); |
||
253 | return; |
||
254 | end |
||
255 | end |
||
256 | end |
||
257 | if ( FRC_PriceSource == nil) then |
||
258 | GFWUtils.Print("ReagentCost: missing required dependency. Can't find Auctioneer, KC_Items, or AuctionMatrix."); |
||
259 | return; |
||
260 | end |
||
261 | end |
||
262 | |||
263 | if ( event == "TRADE_SKILL_SHOW" or event == "TRADE_SKILL_UPDATE" ) then |
||
264 | |||
265 | FRC_ScanTradeSkill(); |
||
266 | |||
267 | elseif ( event == "CRAFT_SHOW" or event == "CRAFT_UPDATE" ) then |
||
268 | |||
269 | FRC_ScanCraft(); |
||
270 | |||
271 | end |
||
272 | |||
273 | end |
||
274 | |||
275 | function FRC_ChatCommandHandler(msg) |
||
276 | |||
277 | if (FRC_PriceSource == nil) then |
||
278 | GFWUtils.Print("Fizzwidget Reagent Cost is installed but non-functional; can't find Auctioneer, KC_Items (with auction module), or AuctionMatrix."); |
||
279 | return; |
||
280 | end |
||
281 | |||
282 | -- Print Help |
||
283 | if ( msg == "help" ) or ( msg == "" ) then |
||
284 | GFWUtils.Print("Fizzwidget Reagent Cost "..FRC_VERSION..":"); |
||
285 | GFWUtils.Print("/reagentcost (or /rc) <command>"); |
||
286 | GFWUtils.Print("- "..GFWUtils.Hilite("help").." - Print this helplist."); |
||
287 | GFWUtils.Print("- "..GFWUtils.Hilite("status").." - Check current settings."); |
||
288 | GFWUtils.Print("- "..GFWUtils.Hilite("reset").." - Reset to default settings."); |
||
289 | GFWUtils.Print("- "..GFWUtils.Hilite("on").." | "..GFWUtils.Hilite("off").." - Toggle displaying info in tradeskill windows."); |
||
290 | if (FRC_PriceSource == "Auctioneer") then |
||
291 | GFWUtils.Print("- "..GFWUtils.Hilite("autoload on").." | "..GFWUtils.Hilite("off").." - Control whether to automatically load Auctioneer when showing tradeskill windows."); |
||
292 | end |
||
293 | GFWUtils.Print("- "..GFWUtils.Hilite("report [<skillname>]").." - Output a list of the most profitable tradeskill items you can make. (Or only those produced through <skillname>.)"); |
||
294 | GFWUtils.Print("- "..GFWUtils.Hilite("minprofit <number>").." - When reporting, only show items whose estimated profit is <number> or greater. (In copper, so 1g == 10000.)"); |
||
295 | GFWUtils.Print("- "..GFWUtils.Hilite("minprofit <number>%").." - When reporting, only show items whose estimated profit exceeds its cost of materials by <number> percent or more."); |
||
296 | return; |
||
297 | end |
||
298 | |||
299 | if (msg == "version") then |
||
300 | GFWUtils.Print("Fizzwidget Reagent Cost "..FRC_VERSION); |
||
301 | return; |
||
302 | end |
||
303 | |||
304 | -- Check Status |
||
305 | if ( msg == "status" ) then |
||
306 | if (FRC_Config.Enabled) then |
||
307 | GFWUtils.Print("Reagent Cost "..GFWUtils.Hilite("is").." displaying materials cost in tradeskill windows."); |
||
308 | if (FRC_Config.AutoLoadPriceSource) then |
||
309 | GFWUtils.Print("Reagent Cost "..GFWUtils.Hilite("will").." automatically load Auctioneer to show prices in tradeskill windows."); |
||
310 | else |
||
311 | GFWUtils.Print("Reagent Cost "..GFWUtils.Hilite("will not").." automatically load Auctioneer; prices will not be shown in tradeskill windows until Auctioner is loaded some other way."); |
||
312 | end |
||
313 | else |
||
314 | GFWUtils.Print("Reagent Cost "..GFWUtils.Hilite("is not").." displaying materials cost in tradeskill windows."); |
||
315 | end |
||
316 | if (FRC_Config.MinProfitMoney == nil) then |
||
317 | GFWUtils.Print("Reports will only include items whose estimated profit exceeds materials cost by "..GFWUtils.Hilite(FRC_Config.MinProfitRatio.."%").." or more."); |
||
318 | else |
||
319 | GFWUtils.Print("Reports will only include items whose estimated profit is "..GFWUtils.TextGSC(FRC_Config.MinProfitMoney).." or greater."); |
||
320 | end |
||
321 | return; |
||
322 | end |
||
323 | |||
324 | -- Reset Variables |
||
325 | if ( msg == "reset" ) then |
||
326 | FRC_Config.Enabled = true; |
||
327 | FRC_Config.MinProfitRatio = 0; |
||
328 | FRC_Config.MinProfitMoney = nil; |
||
329 | GFWUtils.Print("Reagent Cost configuration reset."); |
||
330 | FRC_ChatCommandHandler("status"); |
||
331 | return; |
||
332 | end |
||
333 | |||
334 | -- Turn trade info gathering on |
||
335 | if ( msg == "on" ) then |
||
336 | FRC_Config.Enabled = true; |
||
337 | GFWUtils.Print("Reagent Cost "..GFWUtils.Hilite("is").." displaying materials cost in tradeskill windows."); |
||
338 | return; |
||
339 | end |
||
340 | |||
341 | -- Turn trade info gathering Off |
||
342 | if ( msg == "off" ) then |
||
343 | FRC_Config.Enabled = false; |
||
344 | GFWUtils.Print("Reagent Cost "..GFWUtils.Hilite("is not").." displaying materials cost in tradeskill windows."); |
||
345 | return; |
||
346 | end |
||
347 | |||
348 | if ( msg == "autoload on" ) then |
||
349 | FRC_Config.AutoLoadPriceSource = true; |
||
350 | GFWUtils.Print("Reagent Cost "..GFWUtils.Hilite("will").." automatically load Auctioneer to show prices in tradeskill windows."); |
||
351 | return; |
||
352 | end |
||
353 | if ( msg == "autoload off" ) then |
||
354 | FRC_Config.AutoLoadPriceSource = nil; |
||
355 | GFWUtils.Print("Reagent Cost "..GFWUtils.Hilite("will not").." automatically load Auctioneer; prices will not be shown in tradeskill windows until Auctioner is loaded some other way."); |
||
356 | return; |
||
357 | end |
||
358 | |||
359 | local _, _, cmd, args = string.find(msg, "(%w+) *(.*)"); |
||
360 | if ( cmd == "minprofit" ) then |
||
361 | |||
362 | local _, _, number, isPercent = string.find(msg, "minprofit (-*%d+)(%%*)"); |
||
363 | if (number == nil) then |
||
364 | GFWUtils.Print("Usage: "..GFWUtils.Hilite("/rc minprofit <number>[%]")); |
||
365 | return; |
||
366 | end |
||
367 | if (isPercent == "%") then |
||
368 | FRC_Config.MinProfitRatio = tonumber(number); |
||
369 | FRC_Config.MinProfitMoney = nil; |
||
370 | GFWUtils.Print("Reports will only include items whose estimated profit exceeds materials cost by "..GFWUtils.Hilite(FRC_Config.MinProfitRatio.."%").." or more."); |
||
371 | else |
||
372 | FRC_Config.MinProfitRatio = nil; |
||
373 | FRC_Config.MinProfitMoney = tonumber(number); |
||
374 | GFWUtils.Print("Reports will only include items whose estimated profit is "..GFWUtils.TextGSC(FRC_Config.MinProfitMoney).." or greater."); |
||
375 | end |
||
376 | return; |
||
377 | end |
||
378 | |||
379 | if ( ( cmd == "reagents" or cmd == "report" ) and FRC_PriceSource == "Auctioneer") then |
||
380 | if (not IsAddOnLoaded("Auctioneer")) then |
||
381 | local loaded, reason = LoadAddOn("Auctioneer"); |
||
382 | if (not loaded) then |
||
383 | GFWUtils.Print("Can't load Auctioneer: "..reason); |
||
384 | return; |
||
385 | end |
||
386 | end |
||
387 | end |
||
388 | |||
389 | if ( cmd == "reagents" or cmd == "report" ) then |
||
390 | |||
391 | -- check second arg |
||
392 | local _, _, arg1, moreArgs = string.find(args, "(%w+) *(.*)"); |
||
393 | local scope = "toon"; |
||
394 | if (arg1 == "all") then |
||
395 | scope = "realm"; |
||
396 | args = moreArgs; |
||
397 | elseif (arg1 == "allrealms") then |
||
398 | scope = "all"; |
||
399 | args = moreArgs; |
||
400 | end |
||
401 | |||
402 | -- parse skill names from args |
||
403 | local mySkills = { }; |
||
404 | if (args ~= nil and args ~= "") then |
||
405 | for word in string.gfind(args, "[^%s]+") do |
||
406 | local niceWord = string.upper(string.sub(word, 1, 1))..string.sub(word, 2); |
||
407 | table.insert(mySkills, niceWord); |
||
408 | end |
||
409 | end |
||
410 | |||
411 | -- if no args, use the skills this character knows |
||
412 | if (table.getn(mySkills) == 0) then |
||
413 | for skillIndex = 1, GetNumSkillLines() do |
||
414 | local skillName, _, _, _, _, _, _, isAbandonable = GetSkillLineInfo(skillIndex); |
||
415 | if (isAbandonable) then |
||
416 | table.insert(mySkills, skillName); |
||
417 | end |
||
418 | end |
||
419 | end |
||
420 | |||
421 | local printList; |
||
422 | if (cmd == "report") then |
||
423 | printList = FRC_ReportForSkill; |
||
424 | elseif (cmd == "reagents") then |
||
425 | printList = FRC_ListAllReagents; |
||
426 | end |
||
427 | for _, skillName in mySkills do |
||
428 | printList(skillName, scope); |
||
429 | end |
||
430 | |||
431 | return; |
||
432 | end |
||
433 | |||
434 | _, _, itemLink = string.find(msg, "(|c%x+|Hitem:%d+:%d+:%d+:%d+|h%[.-%]|h|r)"); |
||
435 | if (itemLink ~= nil and itemLink ~= "") then |
||
436 | itemLink = FRC_NormalizeLink(itemLink); |
||
437 | if (not IsAddOnLoaded("Auctioneer")) then |
||
438 | local loaded, reason = LoadAddOn("Auctioneer"); |
||
439 | if (not loaded) then |
||
440 | GFWUtils.Print("Can't load Auctioneer: "..reason); |
||
441 | return; |
||
442 | end |
||
443 | end |
||
444 | local found = false; |
||
445 | for skillName, skillTable in FRC_ReagentLinks do |
||
446 | if (skillTable[itemLink] ~= nil) then |
||
447 | for recipe, reagentList in skillTable[itemLink] do |
||
448 | if (string.find(itemLink, "%["..recipe.."%]")) then |
||
449 | GFWUtils.Print(itemLink.." ("..skillName.."):"); |
||
450 | else |
||
451 | GFWUtils.Print(itemLink.." ("..skillName.." - "..recipe.."):"); |
||
452 | end |
||
453 | found = true; |
||
454 | for _, reagentInfo in reagentList do |
||
455 | if (type(reagentInfo) == "table") then |
||
456 | local price, confidence, isAdjusted = FRC_AdjustedCost(skillName, reagentInfo.link); |
||
457 | local adjustedText, confidenceText; |
||
458 | if (isAdjusted) then |
||
459 | adjustedText = "(based on component prices)"; |
||
460 | else |
||
461 | adjustedText = ""; |
||
462 | end |
||
463 | if (confidence < 0) then |
||
464 | confidenceText = "from vendor"; |
||
465 | else |
||
466 | confidenceText = confidence.."%" |
||
467 | end |
||
468 | if (price ~= nil) then |
||
469 | GFWUtils.Print(GFWUtils.Hilite(reagentInfo.count.."x ")..reagentInfo.link..": "..GFWUtils.TextGSC(price * reagentInfo.count)..GFWUtils.Gray(" ("..confidenceText..") ")..adjustedText); |
||
470 | else |
||
471 | GFWUtils.Print(GFWUtils.Hilite(reagentInfo.count.."x ")..reagentInfo.link..": No price data"); |
||
472 | end |
||
473 | end |
||
474 | end |
||
475 | |||
476 | local itemPrice, itemConfidence = FRC_TypicalItemPrice(itemLink); |
||
477 | local materialsCost, matsConfidence = FRC_MaterialsCostForRecipe(skillName, itemLink, recipe); |
||
478 | local profit = itemPrice - materialsCost; |
||
479 | local profitText; |
||
480 | if (profit > 0) then |
||
481 | profitText = "profit ".. GFWUtils.TextGSC(profit); |
||
482 | elseif (profit == 0) then |
||
483 | profitText = GFWUtils.Hilite("(break-even)"); |
||
484 | else |
||
485 | profitText = GFWUtils.Red("loss ").. GFWUtils.TextGSC(math.abs(profit)); |
||
486 | end |
||
487 | if (materialsCost ~= nil) then |
||
488 | GFWUtils.Print("Total materials: "..GFWUtils.TextGSC(materialsCost)..GFWUtils.Gray("("..matsConfidence..")")); |
||
489 | else |
||
490 | GFWUtils.Print("Total materials: data not available for one or more reagents"); |
||
491 | end |
||
492 | if (itemPrice ~= nil) then |
||
493 | GFWUtils.Print("Auction price: "..GFWUtils.TextGSC(itemPrice)..GFWUtils.Gray("("..itemConfidence..")").."; "..profitText); |
||
494 | else |
||
495 | GFWUtils.Print("Auction price: data not available"); |
||
496 | end |
||
497 | end |
||
498 | end |
||
499 | end |
||
500 | if (not found) then |
||
501 | GFWUtils.Print(itemLink.." not found in tradeskill data."); |
||
502 | end |
||
503 | return; |
||
504 | end |
||
505 | |||
506 | |||
507 | -- If we get down to here, we got bad input. |
||
508 | FRC_ChatCommandHandler("help"); |
||
509 | end |
||
510 | |||
511 | function FRC_ListAllReagents(skillName, scope) |
||
512 | local itemsTable = FRC_ReagentLinks[skillName]; |
||
513 | if (itemsTable == nil) then |
||
514 | if (ReagentData == nil) then |
||
515 | GFWUtils.Print("Nothing for "..GFWUtils.Hilite(skillName).."."); |
||
516 | elseif (ReagentData['reversegathering'][skillName] ~= nil) then |
||
517 | -- do nothing; don't want to barf errors about gathering skills... |
||
518 | elseif (ReagentData['reverseprofessions'][skillName] ~= nil) then |
||
519 | GFWUtils.Print("ReagentCost doesn't have information on "..GFWUtils.Hilite(skillName)..". Please open your "..GFWUtils.Hilite(skillName).." window before requesting a report."); |
||
520 | else |
||
521 | GFWUtils.Print(GFWUtils.Hilite(skillName).." is not a known profession."); |
||
522 | end |
||
523 | else |
||
524 | local realm = GetRealmName(); |
||
525 | local player = UnitName("player"); |
||
526 | for anItem, recipesTable in itemsTable do |
||
527 | for recipe, reagentList in recipesTable do |
||
528 | local known; |
||
529 | if (scope == "toon") then |
||
530 | if (FRC_KnownRecipes and FRC_KnownRecipes[realm] and FRC_KnownRecipes[realm][player]) then |
||
531 | known = GFWTable.KeyOf(FRC_KnownRecipes[realm][player], anItem); |
||
532 | end |
||
533 | elseif (scope == "realm") then |
||
534 | if (FRC_KnownRecipes and FRC_KnownRecipes[realm]) then |
||
535 | for player, items in FRC_KnownRecipes[realm] do |
||
536 | if (GFWTable.KeyOf(FRC_KnownRecipes[realm][player], anItem)) then |
||
537 | known = true; |
||
538 | break; |
||
539 | end |
||
540 | end |
||
541 | end |
||
542 | else |
||
543 | known = true; |
||
544 | end |
||
545 | |||
546 | if (known) then |
||
547 | local itemString; |
||
548 | if (string.find(anItem, "%["..recipe.."%]")) then |
||
549 | itemString = anItem..": "; |
||
550 | else |
||
551 | itemString = anItem.." ("..recipe.."): "; |
||
552 | end |
||
553 | for _, aReagent in reagentsTable do |
||
554 | itemString = itemString .. aReagent.count .. "x" .. aReagent.link .. ", "; |
||
555 | end |
||
556 | itemString = string.gsub(itemString, ", $", ""); |
||
557 | GFWUtils.Print(itemString); |
||
558 | end |
||
559 | end |
||
560 | end |
||
561 | end |
||
562 | end |
||
563 | |||
564 | function FRC_ReportForSkill(skillName, scope) |
||
565 | local knownItems = 0; |
||
566 | local reliableItems = 0; |
||
567 | local shownItems = 0; |
||
568 | local itemsTable = FRC_ReagentLinks[skillName]; |
||
569 | |||
570 | if (itemsTable == nil) then |
||
571 | if (ReagentData == nil) then |
||
572 | GFWUtils.Print("Nothing for "..GFWUtils.Hilite(skillName).."."); |
||
573 | elseif (ReagentData['reversegathering'][skillName] ~= nil) then |
||
574 | -- do nothing; don't want to barf errors about gathering skills... |
||
575 | if (skillName == ReagentData['gathering']['mining']) then |
||
576 | -- ...except for Mining, which is also a production skill as far as we're concerned. |
||
577 | GFWUtils.Print("ReagentCost doesn't have information on "..GFWUtils.Hilite(skillName)..". Please open your "..GFWUtils.Hilite(skillName).." window before requesting a report."); |
||
578 | end |
||
579 | elseif (ReagentData['reverseprofessions'][skillName] ~= nil) then |
||
580 | GFWUtils.Print("ReagentCost doesn't have information on "..GFWUtils.Hilite(skillName)..". Please open your "..GFWUtils.Hilite(skillName).." window before requesting a report."); |
||
581 | else |
||
582 | GFWUtils.Print(GFWUtils.Hilite(skillName).." is not a known profession."); |
||
583 | end |
||
584 | return; |
||
585 | end |
||
586 | |||
587 | local reportTable = { }; -- separate report for each skill |
||
588 | |||
589 | -- first, build a table that includes current Auctioneer prices for composite items |
||
590 | local realm = GetRealmName(); |
||
591 | local player = UnitName("player"); |
||
592 | for anItem in itemsTable do |
||
593 | local known; |
||
594 | if (scope == "toon") then |
||
595 | if (FRC_KnownRecipes and FRC_KnownRecipes[realm] and FRC_KnownRecipes[realm][player]) then |
||
596 | known = GFWTable.KeyOf(FRC_KnownRecipes[realm][player], anItem); |
||
597 | end |
||
598 | elseif (scope == "realm") then |
||
599 | if (FRC_KnownRecipes and FRC_KnownRecipes[realm]) then |
||
600 | for player, items in FRC_KnownRecipes[realm] do |
||
601 | if (GFWTable.KeyOf(FRC_KnownRecipes[realm][player], anItem)) then |
||
602 | known = true; |
||
603 | break; |
||
604 | end |
||
605 | end |
||
606 | end |
||
607 | else |
||
608 | known = true; |
||
609 | end |
||
610 | |||
611 | if (known) then |
||
612 | -- parse out a link so that we ignore non-auctionable craft recipes (i.e. enchants) |
||
613 | _, _, itemLink = string.find(anItem, "(|c%x+|Hitem:%d+:%d+:%d+:%d+|h%[.-%]|h|r)"); |
||
614 | if (itemLink ~= nil and itemLink ~= "") then |
||
615 | itemLink = FRC_NormalizeLink(itemLink); |
||
616 | for recipe in FRC_ReagentLinks[skillName][itemLink] do |
||
617 | knownItems = knownItems + 1; |
||
618 | local itemPrice, itemConfidence = FRC_TypicalItemPrice(itemLink); |
||
619 | local materialsCost, matsConfidence = FRC_MaterialsCostForRecipe(skillName, itemLink, recipe); |
||
620 | |||
621 | if (itemConfidence == nil) then itemConfidence = 0; end |
||
622 | if (matsConfidence == nil) then matsConfidence = 0; end |
||
623 | |||
624 | if (itemConfidence >= MIN_CONFIDENCE and matsConfidence >= MIN_CONFIDENCE) then |
||
625 | reliableItems = reliableItems + 1; |
||
626 | local profit = itemPrice - materialsCost; |
||
627 | table.insert(reportTable, {link=itemLink, recipe=recipe, matsCost=materialsCost, matsConf=matsConfidence, itemPrice=itemPrice, itemConf=itemConfidence, profit=profit}); |
||
628 | end |
||
629 | end |
||
630 | end |
||
631 | end |
||
632 | end |
||
633 | |||
634 | |||
635 | if (knownItems == 0) then |
||
636 | GFWUtils.Print("ReagentCost doesn't have information on "..GFWUtils.Hilite(skillName)..". Please open your "..GFWUtils.Hilite(skillName).." window before requesting a report."); |
||
637 | return; |
||
638 | end |
||
639 | |||
640 | if (reliableItems == 0) then |
||
641 | GFWUtils.Print("None of the "..GFWUtils.Hilite(knownItems).." items you can make with "..GFWUtils.Hilite(skillName).." have reliable auction price data. (They may not be tradeable.)"); |
||
642 | return; |
||
643 | end |
||
644 | |||
645 | GFWUtils.Print("Most profitable recipes for "..GFWUtils.Hilite(skillName)..":"); |
||
646 | |||
647 | if (reliableItems > 1) then |
||
648 | table.sort(reportTable, FRC_SortProfit); |
||
649 | end |
||
650 | |||
651 | -- and report those that meet our minimum requirements |
||
652 | for _, reportInfo in reportTable do |
||
653 | if (FRC_Config.MinProfitRatio and (reportInfo.profit / reportInfo.matsCost * 100) >= FRC_Config.MinProfitRatio) then |
||
654 | shownItems = shownItems + 1; |
||
655 | FRC_PrintReportLine(reportInfo); |
||
656 | elseif (FRC_Config.MinProfitMoney and reportInfo.profit >= FRC_Config.MinProfitMoney) then |
||
657 | shownItems = shownItems + 1; |
||
658 | FRC_PrintReportLine(reportInfo); |
||
659 | end |
||
660 | end |
||
661 | GFWUtils.Print(GFWUtils.Hilite(knownItems).." recipes known, "..GFWUtils.Hilite(reliableItems).." with auction data, "..GFWUtils.Hilite(shownItems).." above profit threshold."); |
||
662 | |||
663 | end |
||
664 | |||
665 | function FRC_ScanTradeSkill() |
||
666 | if (not TradeSkillFrame or not TradeSkillFrame:IsVisible() or FRC_TradeSkillLock.Locked) then return; end |
||
667 | -- This prevents further update events from being handled if we're already processing one. |
||
668 | -- This is done to prevent the game from freezing under certain conditions. |
||
669 | FRC_TradeSkillLock.Locked = true; |
||
670 | |||
671 | local skillLineName, skillLineRank, skillLineMaxRank = GetTradeSkillLine(); |
||
672 | if not (skillLineName) then |
||
673 | FRC_TradeSkillLock.NeedScan = true; |
||
674 | return; -- apparently sometimes we're called too early, this is nil, and all hell breaks loose. |
||
675 | end |
||
676 | if (FRC_ReagentLinks == nil) then |
||
677 | FRC_ReagentLinks = { }; |
||
678 | end |
||
679 | if (FRC_ReagentLinks[skillLineName] == nil) then |
||
680 | FRC_ReagentLinks[skillLineName] = { }; |
||
681 | end |
||
682 | |||
683 | local realm = GetRealmName(); |
||
684 | local player = UnitName("player"); |
||
685 | if (FRC_KnownRecipes == nil) then |
||
686 | FRC_KnownRecipes = {}; |
||
687 | end |
||
688 | if (FRC_KnownRecipes[realm] == nil) then |
||
689 | FRC_KnownRecipes[realm] = {}; |
||
690 | end |
||
691 | FRC_KnownRecipes[realm][player] = {}; |
||
692 | for id = GetNumTradeSkills(), 1, -1 do |
||
693 | -- loop from the bottom up, since the reagents we make for compound items are usually below the recipes that need them |
||
694 | local skillName, skillType, numAvailable, isExpanded = GetTradeSkillInfo(id); |
||
695 | if ( skillType ~= "header" ) then |
||
696 | local itemLink = GetTradeSkillItemLink(id); |
||
697 | if (itemLink == nil) then |
||
698 | FRC_TradeSkillLock.NeedScan = true; |
||
699 | else |
||
700 | table.insert(FRC_KnownRecipes[realm][player], itemLink); |
||
701 | |||
702 | FRC_ReagentLinks[skillLineName][itemLink] = { }; |
||
703 | FRC_ReagentLinks[skillLineName][itemLink][skillName] = { }; |
||
704 | for i=1, GetTradeSkillNumReagents(id), 1 do |
||
705 | local link = GetTradeSkillReagentItemLink(id, i); |
||
706 | if (link == nil) then |
||
707 | FRC_ReagentLinks[skillLineName][itemLink][skillName] = nil; |
||
708 | FRC_TradeSkillLock.NeedScan = true; |
||
709 | break; |
||
710 | else |
||
711 | local reagentName, reagentTexture, reagentCount, playerReagentCount = GetTradeSkillReagentInfo(id, i); |
||
712 | table.insert(FRC_ReagentLinks[skillLineName][itemLink][skillName], {link=link, count=reagentCount}); |
||
713 | end |
||
714 | end |
||
715 | end |
||
716 | end |
||
717 | end |
||
718 | |||
719 | end |
||
720 | |||
721 | function FRC_ScanCraft() |
||
722 | if (not CraftFrame or not CraftFrame:IsVisible() or FRC_CraftLock.Locked) then return; end |
||
723 | -- This prevents further update events from being handled if we're already processing one. |
||
724 | -- This is done to prevent the game from freezing under certain conditions. |
||
725 | FRC_CraftLock.Locked = true; |
||
726 | |||
727 | -- This is used only for Enchanting |
||
728 | local skillLineName, rank, maxRank = GetCraftDisplaySkillLine(); |
||
729 | if not (skillLineName) then |
||
730 | return; -- Hunters' Beast Training also uses the CraftFrame, but doesn't have a SkillLine. |
||
731 | end |
||
732 | if (FRC_ReagentLinks == nil) then |
||
733 | FRC_ReagentLinks = { }; |
||
734 | end |
||
735 | if (FRC_ReagentLinks[skillLineName] == nil) then |
||
736 | FRC_ReagentLinks[skillLineName] = { }; |
||
737 | end |
||
738 | |||
739 | local realm = GetRealmName(); |
||
740 | local player = UnitName("player"); |
||
741 | if (FRC_KnownRecipes == nil) then |
||
742 | FRC_KnownRecipes = {}; |
||
743 | end |
||
744 | if (FRC_KnownRecipes[realm] == nil) then |
||
745 | FRC_KnownRecipes[realm] = {}; |
||
746 | end |
||
747 | FRC_KnownRecipes[realm][player] = {}; |
||
748 | for id = GetNumCrafts(), 1, -1 do |
||
749 | if ( craftType ~= "header" ) then |
||
750 | craftName, craftSubSpellName, craftType, numAvailable, isExpanded, trainingPointCost, requiredLevel = GetCraftInfo(id); |
||
751 | local itemLink = GetCraftItemLink(id); |
||
752 | if (itemLink == nil) then |
||
753 | itemLink = craftName; -- may be an item, may be a (currently unlinkable) enchant |
||
754 | end |
||
755 | if (itemLink == nil) then |
||
756 | FRC_TradeSkillLock.NeedScan = true; |
||
757 | else |
||
758 | table.insert(FRC_KnownRecipes[realm][player], itemLink); |
||
759 | |||
760 | FRC_ReagentLinks[skillLineName][itemLink] = { }; |
||
761 | FRC_ReagentLinks[skillLineName][itemLink][craftName] = { }; |
||
762 | for i=1, GetCraftNumReagents(id), 1 do |
||
763 | local link = GetCraftReagentItemLink(id, i); |
||
764 | if (link == nil) then |
||
765 | FRC_ReagentLinks[skillLineName][itemLink][craftName] = nil; |
||
766 | FRC_CraftLock.NeedScan = true; |
||
767 | break; |
||
768 | else |
||
769 | local reagentName, reagentTexture, reagentCount, playerReagentCount = GetCraftReagentInfo(id, i); |
||
770 | table.insert(FRC_ReagentLinks[skillLineName][itemLink][craftName], {link=link, count=reagentCount}); |
||
771 | end |
||
772 | end |
||
773 | end |
||
774 | end |
||
775 | end |
||
776 | end |
||
777 | |||
778 | function FRC_SortProfit(a, b) |
||
779 | -- sort by ratio or actual amount based on which we're using as cutoff |
||
780 | if (FRC_Config.MinProfitRatio ~= nil) then |
||
781 | return (a.profit / a.matsCost) > (b.profit / b.matsCost); |
||
782 | else |
||
783 | return a.profit > b.profit; |
||
784 | end |
||
785 | end |
||
786 | |||
787 | function FRC_PrintReportLine(reportInfo) |
||
788 | local reportLine; |
||
789 | if (string.find(reportInfo.link, "%["..reportInfo.recipe.."%]")) then |
||
790 | reportLine = reportInfo.link..": "; |
||
791 | else |
||
792 | reportLine = reportInfo.link.." ("..reportInfo.recipe.."): "; |
||
793 | end |
||
794 | reportLine = reportLine .."materials cost ".. GFWUtils.TextGSC(reportInfo.matsCost) ..GFWUtils.Gray(" ("..reportInfo.matsConf.."%)")..", " |
||
795 | reportLine = reportLine .."auction price ".. GFWUtils.TextGSC(reportInfo.itemPrice) ..GFWUtils.Gray(" ("..reportInfo.itemConf.."%)")..", " |
||
796 | if (reportInfo.profit >= 0) then |
||
797 | reportLine = reportLine .."profit ".. GFWUtils.TextGSC(reportInfo.profit); |
||
798 | else |
||
799 | reportLine = reportLine ..GFWUtils.Red("loss ").. GFWUtils.TextGSC(reportInfo.profit); |
||
800 | end |
||
801 | GFWUtils.Print(reportLine); |
||
802 | end |
||
803 | |||
804 | function FRC_AdjustedCost(skillName, itemLink) |
||
805 | |||
806 | local itemPrice, itemConfidence = FRC_TypicalItemPrice(itemLink); |
||
807 | if (FRC_RecursiveItems == nil) then |
||
808 | FRC_RecursiveItems = {}; |
||
809 | end |
||
810 | if (GFWTable.KeyOf(FRC_RecursiveItems, itemLink)) then |
||
811 | -- avoid infinite recursion |
||
812 | FRC_RecursiveItems = nil; |
||
813 | return itemPrice, itemConfidence, false; |
||
814 | else |
||
815 | table.insert(FRC_RecursiveItems, itemLink); |
||
816 | end |
||
817 | |||
818 | -- don't calculate sub-reagent prices for the likes of alchemical transumutes |
||
819 | -- (recipes that take one reagent also produced by the same skill and produce one other such reagent) |
||
820 | if (FRC_ReagentLinks[skillName] and FRC_ReagentLinks[skillName][itemLink]) then |
||
821 | for recipe, reagentsList in FRC_ReagentLinks[skillName][itemLink] do |
||
822 | if (table.getn(reagentsList) == 1 ) then |
||
823 | local reagentInfo = reagentsList[1]; |
||
824 | if (reagentInfo.count == 1 and FRC_ReagentLinks[skillName][reagentInfo.link]) then |
||
825 | return itemPrice, itemConfidence, false; |
||
826 | end |
||
827 | end |
||
828 | end |
||
829 | end |
||
830 | |||
831 | -- for all other recipes, calculate total cost of reagents which might be produced by the same skill, |
||
832 | -- and use that amount if it's more reliable. |
||
833 | -- (e.g. engineering parts -> base reagents, bolts of cloth -> pieces of cloth) |
||
834 | local subReagentsPrice, subReagentsConfidence = FRC_MaterialsCost(skillName, itemLink); |
||
835 | if (subReagentsPrice and subReagentsConfidence) then |
||
836 | if (not (itemPrice and itemConfidence)) then |
||
837 | return subReagentsPrice, subReagentsConfidence, true; |
||
838 | end |
||
839 | if (subReagentsConfidence >= itemConfidence and itemConfidence < MIN_OVERRIDE_CONFIDENCE and subReagentsPrice < itemPrice) then |
||
840 | return subReagentsPrice, subReagentsConfidence, true; |
||
841 | end |
||
842 | end |
||
843 | return itemPrice, itemConfidence, false; |
||
844 | end |
||
845 | |||
846 | function FRC_MaterialsCost(skillName, itemLink) |
||
847 | if (FRC_ReagentLinks[skillName] == nil) then |
||
848 | return nil, nil; |
||
849 | end |
||
850 | if (FRC_ReagentLinks[skillName][itemLink] == nil) then |
||
851 | return nil, nil; |
||
852 | end |
||
853 | |||
854 | local pricesPerRecipe = {}; |
||
855 | for recipe in FRC_ReagentLinks[skillName][itemLink] do |
||
856 | if (type(recipe) == "string") then |
||
857 | local cost, confidence = FRC_MaterialsCostForRecipe(skillName, itemLink, recipe); |
||
858 | if (cost) then |
||
859 | table.insert(pricesPerRecipe, {cost=cost, confidence=confidence}); |
||
860 | end |
||
861 | end |
||
862 | end |
||
863 | if (table.getn(pricesPerRecipe) == 0) then |
||
864 | return nil, nil; |
||
865 | end |
||
866 | |||
867 | local sortCost = function(a,b) |
||
868 | return a.cost < b.cost; |
||
869 | end |
||
870 | local sortConfidence = function(a,b) |
||
871 | return a.confidence > b.confidence; |
||
872 | end |
||
873 | table.sort(pricesPerRecipe, sortConfidence); |
||
874 | table.sort(pricesPerRecipe, sortCost); |
||
875 | |||
876 | return pricesPerRecipe[1].cost, pricesPerRecipe[1].confidence; |
||
877 | |||
878 | end |
||
879 | |||
880 | function FRC_MaterialsCostForRecipe(skillName, itemLink, recipeName) |
||
881 | local materialsTotal = 0; |
||
882 | local totalConfidence = 0; |
||
883 | local numAuctionReagents = 0; |
||
884 | |||
885 | if (FRC_ReagentLinks[skillName] == nil) then |
||
886 | return nil, nil; |
||
887 | end |
||
888 | if (FRC_ReagentLinks[skillName][itemLink] == nil) then |
||
889 | return nil, nil; |
||
890 | end |
||
891 | if (FRC_ReagentLinks[skillName][itemLink][recipeName] == nil) then |
||
892 | return nil, nil; |
||
893 | end |
||
894 | |||
895 | for _, reagentInfo in FRC_ReagentLinks[skillName][itemLink][recipeName] do |
||
896 | local price, confidence = FRC_AdjustedCost(skillName, reagentInfo.link) |
||
897 | if (price == nil) then |
||
898 | return nil, nil; -- if any of the reagents is missing price info, we can't calculate a total. |
||
899 | end |
||
900 | materialsTotal = materialsTotal + (price * reagentInfo.count); |
||
901 | if (confidence >= 0) then |
||
902 | totalConfidence = totalConfidence + confidence; |
||
903 | numAuctionReagents = numAuctionReagents + 1; |
||
904 | end |
||
905 | end |
||
906 | local confidenceScore = math.floor((totalConfidence / numAuctionReagents) * 100) / 100; |
||
907 | |||
908 | return materialsTotal, confidenceScore; |
||
909 | |||
910 | end |
||
911 | |||
912 | function FRC_TypicalItemPrice(itemLink) |
||
913 | if (FRC_PriceSource == "Auctioneer") then |
||
914 | if (not IsAddOnLoaded("Auctioneer")) then |
||
915 | if (FRC_Config.AutoLoadPriceSource) then |
||
916 | local loaded, reason = LoadAddOn("Auctioneer"); |
||
917 | if (not loaded) then |
||
918 | GFWUtils.Print("Can't load Auctioneer: "..reason); |
||
919 | return nil; |
||
920 | end |
||
921 | else |
||
922 | return nil; |
||
923 | end |
||
924 | end |
||
925 | return FRC_AuctioneerItemPrice(itemLink); |
||
926 | elseif (FRC_PriceSource == "KC_Items") then |
||
927 | return FRC_KCItemPrice(itemLink); |
||
928 | elseif (FRC_PriceSource == "AuctionMatrix") then |
||
929 | return FRC_AuctionMatrixItemPrice(itemLink); |
||
930 | elseif (FRC_PriceSource == "WOWEcon_PriceMod") then |
||
931 | return FRC_WOWEcon_PriceModItemPrice(itemLink); |
||
932 | else |
||
933 | return nil; |
||
934 | end |
||
935 | end |
||
936 | |||
937 | function FRC_AuctioneerItemPrice(itemLink) |
||
938 | local getUsableMedian = Auctioneer_GetUsableMedian; |
||
939 | local getHistoricalMedian = Auctioneer_GetItemHistoricalMedianBuyout; |
||
940 | local getVendorSellPrice = Auctioneer_GetVendorSellPrice; |
||
941 | if (Auctioneer and Auctioneer.Statistic) then |
||
942 | getUsableMedian = Auctioneer.Statistic.GetUsableMedian; |
||
943 | getHistoricalMedian = Auctioneer.Statistic.GetItemHistoricalMedianBuyout; |
||
944 | end |
||
945 | if (Auctioneer and Auctioneer.API) then |
||
946 | getVendorSellPrice = Auctioneer.API.GetVendorSellPrice; |
||
947 | end |
||
948 | if (not (getUsableMedian and getHistoricalMedian)) then |
||
949 | GFWUtils.PrintOnce(GFWUtils.Red("ReagentCost error:").." missing expected Auctioneer API; can't calculate item prices.", 5); |
||
950 | return nil, nil; |
||
951 | end |
||
952 | |||
953 | local _, _, itemID, randomProp, enchant = string.find(itemLink, "item:(%d+):(%d+):(%d+):%d+"); |
||
954 | local itemKey = itemID..":"..(randomProp or 0)..":"..(enchant or 0); |
||
955 | local medianPrice, medianCount = getUsableMedian(itemKey); |
||
956 | if (medianPrice == nil) then |
||
957 | medianPrice, medianCount = getHistoricalMedian(itemKey); |
||
958 | end |
||
959 | if (medianCount == nil) then medianCount = 0 end |
||
960 | |||
961 | itemID = tonumber(itemID) or 0; |
||
962 | local buyFromVendorPrice = 0; |
||
963 | local sellToVendorPrice = 0; |
||
964 | if (FRC_VendorPrices[itemID]) then |
||
965 | buyFromVendorPrice = FRC_VendorPrices[itemID].b; |
||
966 | sellToVendorPrice = FRC_VendorPrices[itemID].s; |
||
967 | end |
||
968 | if (sellToVendorPrice == 0) then |
||
969 | if (getVendorSellPrice) then |
||
970 | sellToVendorPrice = getVendorSellPrice(itemID) or 0; |
||
971 | elseif (Auctioneer_BasePrices ~= nil and Auctioneer_BasePrices[itemID] ~= nil and Auctioneer_BasePrices[itemID].s ~= nil) then |
||
972 | sellToVendorPrice = Auctioneer_BasePrices[itemID].s or 0; |
||
973 | end |
||
974 | end |
||
975 | |||
976 | if (buyFromVendorPrice > 0) then |
||
977 | return buyFromVendorPrice, -1; -- FRC_VendorPrices lists only the primarily-vendor-bought tradeskill items |
||
978 | elseif (medianCount == 0 or medianPrice == nil) then |
||
979 | return sellToVendorPrice * 3, 0; -- generally a good guess for auction price if we don't have real auction data |
||
980 | else |
||
981 | return medianPrice, math.floor((math.min(medianCount, MIN_SCANS) / MIN_SCANS) * 1000) / 10; |
||
982 | end |
||
983 | end |
||
984 | |||
985 | function FRC_KCItemPrice(itemLink) |
||
986 | local itemCode = KC_Common:GetCode(itemLink); |
||
987 | local seen, avgstack, min, bidseen, bid, buyseen, buy = KC_Auction:GetItemData(itemCode); |
||
988 | local _, _, itemID = string.find(itemLink, ".Hitem:(%d+):%d+:%d+:%d+.h%[[^]]+%].h"); |
||
989 | itemID = tonumber(itemID) or 0; |
||
990 | |||
991 | local buyFromVendorPrice = 0; |
||
992 | local sellToVendorPrice = 0; |
||
993 | if (FRC_VendorPrices[itemID]) then |
||
994 | buyFromVendorPrice = FRC_VendorPrices[itemID].b; |
||
995 | sellToVendorPrice = FRC_VendorPrices[itemID].s; |
||
996 | end |
||
997 | if (sellToVendorPrice == 0 and KC_SellValue ~= nil) then |
||
998 | sellToVendorPrice = (KC_Common:GetItemPrices(itemCode) or 0); |
||
999 | end |
||
1000 | |||
1001 | --DevTools_Dump({itemLink=itemLink, itemID=itemID, buy=buy, buyseen=buyseen, buyFromVendorPrice=buyFromVendorPrice, sellToVendorPrice=sellToVendorPrice}); |
||
1002 | |||
1003 | if (buyFromVendorPrice ~= nil and buyFromVendorPrice > 0) then |
||
1004 | return buyFromVendorPrice, -1; -- FRC_VendorPrices lists only the primarily-vendor-bought tradeskill items |
||
1005 | elseif (buy ~= nil and buy > 0) then |
||
1006 | return buy, math.floor((math.min(buyseen, MIN_SCANS) / MIN_SCANS) * 1000) / 10; |
||
1007 | elseif (sellToVendorPrice ~= nil and sellToVendorPrice > 0) then |
||
1008 | return sellToVendorPrice * 3, 0; -- generally a good guess for auction price if we don't have real auction data |
||
1009 | else |
||
1010 | GFWUtils.DebugLog(itemLink.." not found in KC_Auction or vendor-reagent prices list"); |
||
1011 | return nil, 0; |
||
1012 | end |
||
1013 | end |
||
1014 | |||
1015 | function FRC_AuctionMatrixItemPrice(itemLink) |
||
1016 | local _, _, itemID, itemName = string.find(itemLink, ".Hitem:(%d+):%d+:%d+:%d+.h%[([^]]+)%].h"); |
||
1017 | local buyFromVendorPrice = 0; |
||
1018 | local sellToVendorPrice = 0; |
||
1019 | itemID = tonumber(itemID) or 0; |
||
1020 | if (FRC_VendorPrices[itemID]) then |
||
1021 | buyFromVendorPrice = FRC_VendorPrices[itemID].b; |
||
1022 | sellToVendorPrice = FRC_VendorPrices[itemID].s; |
||
1023 | end |
||
1024 | |||
1025 | local buyout, times, storeStack; |
||
1026 | if (itemName ~= nil and itemName ~= "" and AMDB[itemName]) then |
||
1027 | buyout = tonumber(AM_GetMedian(itemName, "abuyout")); |
||
1028 | if (buyout == nil) then |
||
1029 | buyout = tonumber(AuctionMatrix_GetData(itemName, "abuyout")); |
||
1030 | end |
||
1031 | times = tonumber(AuctionMatrix_GetData(itemName, "times")); |
||
1032 | storeStack = tonumber(AuctionMatrix_GetData(itemName, "stack")); |
||
1033 | if (sellToVendorPrice == 0) then |
||
1034 | sellToVendorPrice = tonumber(AuctionMatrix_GetData(itemName, "vendor")); |
||
1035 | end |
||
1036 | end |
||
1037 | |||
1038 | --DevTools_Dump({itemLink=itemLink, buyout=buyout, times=times, buyFromVendorPrice=buyFromVendorPrice, sellToVendorPrice=sellToVendorPrice}); |
||
1039 | |||
1040 | if (buyFromVendorPrice ~= nil and buyFromVendorPrice > 0) then |
||
1041 | return buyFromVendorPrice, -1; -- FRC_VendorPrices lists only the primarily-vendor-bought tradeskill items |
||
1042 | elseif (buyout ~= nil and times ~= nil and buyout > 0) then |
||
1043 | local buyoutForOne = buyout; |
||
1044 | if (storeStack ~= nil and storeStack > 0) then |
||
1045 | buyoutForOne = math.floor(buyout/storeStack); |
||
1046 | end |
||
1047 | return buyoutForOne, math.floor((math.min(times, MIN_SCANS) / MIN_SCANS) * 1000) / 10; |
||
1048 | elseif (sellToVendorPrice ~= nil and sellToVendorPrice > 0) then |
||
1049 | return sellToVendorPrice * 3, 0; -- generally a good guess for auction price if we don't have real auction data |
||
1050 | end |
||
1051 | |||
1052 | GFWUtils.DebugLog(itemLink.." not found in AuctionMatrix or vendor-reagent prices list"); |
||
1053 | return nil, 0; |
||
1054 | end |
||
1055 | |||
1056 | function FRC_WOWEcon_PriceModItemPrice(itemLink) |
||
1057 | local medianPrice, medianCount, serverData = WOWEcon_GetAuctionPrice_ByLink(itemLink); |
||
1058 | if (medianCount == nil) then |
||
1059 | medianCount = 0; |
||
1060 | end |
||
1061 | |||
1062 | local _, _, itemID = string.find(itemLink, ".Hitem:(%d+):%d+:%d+:%d+.h%[[^]]+%].h"); |
||
1063 | itemID = tonumber(itemID) or 0; |
||
1064 | |||
1065 | local buyFromVendorPrice = 0; |
||
1066 | local sellToVendorPrice = 0; |
||
1067 | if (FRC_VendorPrices[itemID]) then |
||
1068 | buyFromVendorPrice = FRC_VendorPrices[itemID].b; |
||
1069 | sellToVendorPrice = FRC_VendorPrices[itemID].s; |
||
1070 | end |
||
1071 | |||
1072 | if (sellToVendorPrice == 0) then |
||
1073 | sellToVendorPrice = WOWEcon_GetVendorPrice_ByLink(itemLink); |
||
1074 | end |
||
1075 | |||
1076 | if (sellToVendorPrice == nil) then sellToVendorPrice = 0 end |
||
1077 | |||
1078 | if (buyFromVendorPrice > 0) then |
||
1079 | return buyFromVendorPrice, -1; -- FRC_VendorPrices lists only the primarily-vendor-bought tradeskill items |
||
1080 | elseif (medianCount == 0 or medianPrice == nil) then |
||
1081 | return sellToVendorPrice * 3, 0; -- generally a good guess for auction price if we don't have real auction data |
||
1082 | else |
||
1083 | return medianPrice, math.floor((math.min(medianCount, MIN_SCANS) / MIN_SCANS) * 1000) / 10; |
||
1084 | end |
||
1085 | end |
||
1086 | |||
1087 | function FRC_NormalizeLink(link) |
||
1088 | -- we don't care about variations in random-property items, enchants, or unique IDs... |
||
1089 | -- discarding them lets us use the link as both a printable link and a reliable index key. |
||
1090 | return string.gsub(link, "|c(%x+)|Hitem:(%d+):%d+:%d+:%d+|h%[(.-)%]|h|r", "|c%1|Hitem:%2:0:0:0|h[%3]|h|r"); |
||
1091 | end |