Vanilla 1.1.4 is a product of Lussumo. More Information: Documentation, Community Support.
-- "Disencrafting" baserule
--
-- This baserule looks for cheap mats for crafting disenchantable items,
-- taking into account the expected cost of the other mats
--
-- Includes a table with a selection of "good value" tailoring recipes - could
-- maybe hook into Fizzwidget's ReagentCost to avoid all that?
local version = 1.0
local dc = BtmScan.Disencraft
if (not dc or dc.version ~= version) then
dc = { version = version }
BtmScan.Disencraft = dc
function dc.getDirectCost(itemID)
local itemInfo = Informant.GetItem(itemID)
if itemInfo and itemInfo.vendors and itemInfo.buy then
return itemInfo.buy
else
local _, itemLink = GetItemInfo(itemID)
if not itemLink then return nil end
return Auctioneer.Statistic.GetUsableMedian(Auctioneer.ItemDB.CreateItemKeyFromLink(itemLink))
end
end
function dc.getDisenchantValue(itemID)
local _, itemLink = GetItemInfo(itemID)
if not itemLink then return 0 end
if not Enchantrix then return 0 end
return Enchantrix.Storage.GetItemDisenchantTotals(itemLink) or 0
end
function dc.getMinCost(itemID)
local minCost = dc.getDirectCost(itemID) or 10000000
if recipeTable[itemID] then
local matsCost = 0
for _, mat in pairs(dc.recipeTable[itemID]) do
local matID, matCount = mat[1], mat[2]
matsCost = matsCost + dc.getMinCost(matID) * matCount
end
minCost = math.min(minCost, matsCost)
end
return minCost
end
function dc.getMaxYield(itemID)
local maxYield = 0
for recipeID, recipeMats in pairs(dc.recipeTable) do
local usedCount = 0
local otherMatsCost = 0
for _, mat in pairs(recipeMats) do
local matID, matCount = mat[1], mat[2]
if matID == itemID then
usedCount = usedCount + matCount
else
otherMatsCost = otherMatsCost + getMinCost(matID) * matCount
end
end
if usedCount > 0 then
local disenchantValue = getDisenchantValue(recipeID)
local recipeValue
if disenchantValue > 0 then
recipeValue = disenchantValue
else
recipeValue = dc.getMaxYield(recipeID)
end
local remainingValue = recipeValue - otherMatsCost
maxYield = math.max(maxYield, remainingValue / usedCount)
end
end
return maxYield
end
----------------------------------------
dc.recipeTable = {
-- Raw materials
[4305] = { { 4306, 4 } },
[4339] = { { 4338, 5 } },
[14048] = { { 14047, 5 } },
[21840] = { { 21877, 6 } },
-- Azure Silk Cloak (30)
[7053] = { { 4305, 3 }, { 6260, 2 }, { 2321, 2 }, },
-- Green Silken Shoulders (31)
[7057] = { { 4305, 5 }, { 4291, 2 }, },
-- Black Mageweave Leggings (36)
[9999] = { { 4339, 2 }, { 4291, 3 }, },
-- White Bandit Mask (38) - recipe is a drop
--[10008] = { { 4339, 1 }, { 2324, 1 }, { 8343, 1 }, },
-- Black Mageweave Headband (41)
[10024] = { { 4339, 3 }, { 8343, 2 }, },
-- Runecloth Belt (46)
[13856] = { { 14048, 3 }, { 14341, 1 }, },
-- Cindercloth Pants (51) - recipe is a drop
--[14045] = { { 14048, 6 }, { 14341, 1 }, { 7078, 1 }, },
-- Frostweave Pants (51) - recipe is a drop
--[13871] = { { 14048, 6 }, { 14341, 1 }, { 7080, 1 }, },
-- Runecloth Boots (51)
[13864] = { { 14048, 4 }, { 14341, 1 }, { 14227, 2 }, { 8170, 4 }, },
-- Runecloth Shoulders (56)
[13867] = { { 14048, 7 }, { 14341, 1 }, { 14227, 2 }, { 8170, 4 }, },
-- Netherweave Belt (61)
[21850] = { { 21840, 3 }, { 14341, 1 }, },
-- Netherweave Pants (64)
[21852] = { { 21840, 6 }, { 14341, 1 }, },
}
----------------------------------------
end
basePrice = dc.getMaxYield(itemID) * itemCountPosted By: NorgannaIf I may make a (slight) performance suggestion, put the functions into a permanent structure just the first time it runs.
-- "Disencrafting" baserule
--
-- Looks for cheap enchanting mats, cheap items to disenchant, and (deep breath)
-- cheap mats for disenchantable crafted items taking into account the expected
-- cost of the other mats.
--
-- Collects available crafting recipes when tradeskill window is open. Recipe
-- data is stored in BtmScanData (persists across sessions)
--------------------------------------------------------------------------------
local ruleVersion = 2
local dataVersion = 2
local minProfit = 0.25
local minProfitAbs = 2500
--------------------------------------------------------------------------------
-- init recipe/ingredient data
local data = BtmScanData.Disencraft
if not data or data.version ~= dataVersion then
local data = {
version = dataVersion,
recipeTable = {},
ingredientTable = {}
}
BtmScanData.Disencraft = data
end
-- init rule logic
local rule = BtmScan.Disencraft
if not rule or rule.version ~= ruleVersion then
rule = { version = ruleVersion }
local data = BtmScanData.Disencraft
-- get value of enchanting mat
rule.getMatValue = function(itemID)
if not rule.priceCapTable[itemID] then return 0 end
return math.min(Enchantrix.Util.GetReagentPrice(itemID) or 0, rule.priceCapTable[itemID])
end
-- get disenchant value for item
rule.getDisenchantValue = function(itemID)
local _, itemLink = GetItemInfo(itemID)
if not itemLink then return 0 end
if not Enchantrix then return 0 end
local data = Enchantrix.Storage.GetItemDisenchants(itemLink)
if not data then return 0 end
local total, totalValue = data.total, 0
if total and total[1] > 0 then
local totalNumber, totalQuantity = unpack(total)
for result, resData in pairs(data) do
if result ~= "total" then
local resNumber, resQuantity = unpack(resData)
local resProb, resCount = resNumber / totalNumber, resQuantity / resNumber
local resYield = resProb * resCount
local resValuePer = rule.getMatValue(result)
local resValue = resValuePer * resYield
totalValue = totalValue + resValue
end
end
end
return totalValue
end
-- get cost of buying item directly, from vendor or ah
rule.getDirectCost = function(itemID)
local itemInfo = Informant.GetItem(itemID)
if itemInfo and itemInfo.vendors and itemInfo.buy then
return itemInfo.buy
else
local _, itemLink = GetItemInfo(itemID)
if not itemLink then return nil end
local itemKey = Auctioneer.ItemDB.CreateItemKeyFromLink(itemLink)
return Auctioneer.Statistic.GetUsableMedian(itemKey)
end
end
-- get minimum cost of obtaining item, from vendor, ah or crafting
rule.getMinCost = function(itemID)
local minCost = rule.getDirectCost(itemID) or 10000000
if data.recipeTable[itemID] then
local matsCost = 0
for _, mat in pairs(data.recipeTable[itemID]) do
local matID, matCount = mat[1], mat[2]
matsCost = matsCost + rule.getMinCost(matID) * matCount
end
minCost = math.min(minCost, matsCost)
end
return minCost
end
-- get best crafting value for recipe ingredient
rule.getCraftingValue = function(itemID)
if not data.ingredientTable[itemID] then
return 0, "None"
end
local maxYield, maxName = 0, "None"
for recipeID in pairs(data.ingredientTable[itemID]) do
local recipeMats = data.recipeTable[recipeID]
local usedCount = 0
local otherMatsCost = 0
for _, mat in pairs(recipeMats) do
local matID, matCount = mat[1], mat[2]
if matID == itemID then
usedCount = usedCount + matCount
else
otherMatsCost = otherMatsCost + rule.getMinCost(matID) * matCount
end
end
local disenchantValue = rule.getDisenchantValue(recipeID)
local recipeValue, recipeName
if disenchantValue > 0 then
recipeValue, recipeName = disenchantValue, GetItemInfo(recipeID) or "GetItemInfo error"
else
recipeValue, recipeName = rule.getCraftingValue(recipeID)
end
local recipeYield = (recipeValue - otherMatsCost) / usedCount
if recipeYield > maxYield then
maxYield = recipeYield
maxName = recipeName
end
end
return maxYield, maxName
end
-- decide whether to buy based on value and price
rule.shouldBuy = function(value, price)
local profit = value - price
if price > 0 and profit / price >= rule.minProfit and profit >= rule.minProfitAbs then
return true
end
end
------------------------------------------------------------------------------
-- main rule logic
rule.RuleMain = function(itemID, itemCount, bidPrice, buyPrice)
local value, action, reason = 0, "none", "none"
local matValue = rule.getMatValue(itemID) * itemCount
if matValue > value then
value = matValue
reason = "Mat"
end
local deValue = rule.getDisenchantValue(itemID)
if deValue > value then
value = deValue
reason = "Disenchant"
end
local craftValue, recipeName = rule.getCraftingValue(itemID)
craftValue = craftValue * itemCount
if craftValue > value then
value = craftValue
reason = recipeName
end
if rule.shouldBuy(value, buyPrice) then
action = "buy"
elseif rule.shouldBuy(value, bidPrice) then
action = "bid"
end
return value, action, reason
end
------------------------------------------------------------------------------
-- utility function to get itemID from itemLink
rule.getItemID = function(itemLink)
local itemID = BtmScan.BreakLink(itemLink)
return itemID
end
-- debug spam
rule.spam = function(message)
getglobal("ChatFrame1"):AddMessage("Disencraft: "..message, 0.25, 0.25, 1.0)
end
-- hook to scan tradeskill window when it opens
rule.tradeUiHook = function()
local recipeTable = data.recipeTable
local ingredientTable = data.ingredientTable
local addedCount = 0
for recipeIdx = 1, GetNumTradeSkills() do
local name, type = GetTradeSkillInfo(recipeIdx)
if name and type ~= "header" then
local recipeID = rule.getItemID(GetTradeSkillItemLink(recipeIdx))
if not recipeTable[recipeID] then
recipeTable[recipeID] = {}
local reagentCount = GetTradeSkillNumReagents(recipeIdx)
for reagentIdx = 1, reagentCount do
local reagentID = rule.getItemID(GetTradeSkillReagentItemLink(recipeIdx, reagentIdx))
local _, _, reagentUsedCount = GetTradeSkillReagentInfo(recipeIdx, reagentIdx)
if not ingredientTable[reagentID] then ingredientTable[reagentID] = {} end
ingredientTable[reagentID][recipeID] = true
recipeTable[recipeID][reagentIdx] = { reagentID, reagentUsedCount }
end
addedCount = addedCount + 1
end
end
end
if addedCount > 0 then rule.spam("Added " .. addedCount .. " recipes") end
data.ingredientTable = ingredientTable
data.recipeTable = recipeTable
end
Stubby.UnregisterEventHook("TRADE_SKILL_SHOW", "BtmScan.Disencraft")
Stubby.RegisterEventHook("TRADE_SKILL_SHOW", "BtmScan.Disencraft", rule.tradeUiHook)
-- maximum prices for enchanting reagents (server-specific!)
rule.priceCapTable = {
[22450] = 500000, -- Void Crystal
[20725] = 50000, -- Nexus Crystal
[22449] = 200000, -- Large Prismatic Shard
[14344] = 120000, -- Large Brilliant Shard
[11178] = 75000, -- Large Radiant Shard
[11139] = 25000, -- Large Glowing Shard
[11084] = 7500, -- Large Glimmering Shard
[22448] = 60000, -- Small Prismatic Shard
[14343] = 30000, -- Small Brilliant Shard
[11177] = 60000, -- Small Radiant Shard
[11138] = 5000, -- Small Glowing Shard
[10978] = 2500, -- Small Glimmering Shard
[22446] = 75000, -- Greater Planar Essence
[16203] = 120000, -- Greater Eternal Essence
[11175] = 60000, -- Greater Nether Essence
[11135] = 7500, -- Greater Mystic Essence
[11082] = 6000, -- Greater Astral Essence
[10939] = 4500, -- Greater Magic Essence
[22447] = 25000, -- Lesser Planar Essence
[16202] = 40000, -- Lesser Eternal Essence
[11174] = 20000, -- Lesser Nether Essence
[11134] = 2500, -- Lesser Mystic Essence
[10998] = 2000, -- Lesser Astral Essence
[10938] = 1500, -- Lesser Magic Essence
[22445] = 20000, -- Arcane Dust
[16204] = 20000, -- Illusion Dust
[11176] = 5000, -- Dream Dust
[11137] = 4500, -- Vision Dust
[11083] = 2000, -- Soul Dust
[10940] = 1000, -- Strange Dust
}
BtmScan.Disencraft = rule
end
--------------------------------------------------------------------------------
BtmScan.Disencraft.minProfit = minProfit
BtmScan.Disencraft.minProfitAbs = minProfitAbs
basePrice, action, reason = BtmScan.Disencraft.RuleMain(itemID, itemCount, bidPrice, buyPrice)


Posted By: sabot7726Chardonnay do you use just 1 baserule that has all this stuff in it or do you swap them in and out depending on what you're looking for?
diff -p prepatch.EvalDisencraft.lua /cygdrive/c/World\ of\ Warcraft/Interface/AddOns/btmscan/EvalDisencraft.lua
*** EvalDisencraft.lua Wed Sep 12 08:11:15 2007
--- /cygdrive/c/World of Warcraft/Interface/AddOns/btmscan/EvalDisencraft.lua Wed Sep 12 08:10:13 2007
*************** define(lcName..'.enable', true)
*** 348,356 ****
define(lcName..'.profit.min', 4500)
define(lcName..'.profit.pct', 45)
function lib:setup(gui)
! id = gui.AddTab(libName)
! gui.AddControl(id, "Subhead", 0, libName.." Settings")
! gui.AddControl(id, "Checkbox", 0, 1, lcName..".enable", "Enable purchasing for "..lcName)
! gui.AddControl(id, "MoneyFramePinned", 0, 1, lcName..".profit.min", 1, 99999999, "Minimum Profit")
! gui.AddControl(id, "WideSlider", 0, 1, lcName..".profit.pct", 1, 100, 0.5, "Percent Profit: %0.01f%%")
end
--- 348,356 ----
define(lcName..'.profit.min', 4500)
define(lcName..'.profit.pct', 45)
function lib:setup(gui)
! id = gui:AddTab(libName)
! gui:AddControl(id, "Subhead", 0, libName.." Settings")
! gui:AddControl(id, "Checkbox", 0, 1, lcName..".enable", "Enable purchasing for "..lcName)
! gui:AddControl(id, "MoneyFramePinned", 0, 1, lcName..".profit.min", 1, 99999999, "Minimum Profit")
! gui:AddControl(id, "WideSlider", 0, 1, lcName..".profit.pct", 1, 100, 0.5, "Percent Profit: %0.01f%%")
end1509c1509
< item.itemconfig.ignoreModuleList=item.itemconfig.ignoreModuleList..";"..item.reason
---
> item.itemconfig.ignoreModuleList=item.itemconfig.ignoreModuleList..","..item.reasonPosted By: dineshi haven't seen Chard for a few weeks now, to be honest.
Date: 2008-04-20 10:36:39
ID: 1
Error occured in: Global
Count: 1
Message: ..\AddOns\BtmScan\EvalDisencraft.lua line 299:
table index is nil
Debug:
[C]: ?
BtmScan\EvalDisencraft.lua:299: f()
Stubby\Stubby.lua:734: EventWatcher()
Stubby\Stubby.lua:957: Events()
[string "*:OnEvent"]:1:
[string "*:OnEvent"]:1
[C]: UseAction()
..\FrameXML\SecureTemplates.lua:266: SecureActionButton_OnClick()
[string "*:OnClick"]:1:
[string "*:OnClick"]:1
1 to 27 of 27