local locale = GetLocale() if locale ~= "enUS" and locale ~= "enGB" then return end ww_EffectHandlers = { ww_stackingEquipEffects, ww_equipStats, ww_sockets, ww_generic, ww_socketBonuses, ww_food, ww_enchant_items, ww_enchants, ww_elixirs, ww_fishing, ww_cooldownUseEffects, ww_useEffects, } function WeightsWatcher.damageRange(textL, textR) local stats local start, _, minimum, maximum = string.find(textL, "^(%d+%.?%d*) %- (%d+%.?%d*) %a* ?damage$") if start then stats = WeightsWatcher.newStatTable() maximum = tonumber(maximum) stats["average melee weapon damage"] = (minimum + maximum) / 2 stats["maximum melee weapon damage"] = maximum else local start, _, damage = string.find(textL, "^(%d+) %a* ?damage$") if start then stats = WeightsWatcher.newStatTable() damage = tonumber(damage) stats["average melee weapon damage"] = damage stats["maximum melee weapon damage"] = damage end end if stats and textR then local start, _, speed = string.find(textR, "^speed (%d+%.?%d*)$") if start then stats["melee weapon speed"] = tonumber(speed) end end return stats end function WeightsWatcher.convertToSeconds(duration) local start, _, hours = string.find(duration, "^(%d+) ho?u?rs?$") if start then return hours * 3600 end local start, _, minutes, seconds = string.find(duration, "^(%d+) minu?t?e?s? (%d+) seco?n?d?s?$") if start then return minutes * 60 + seconds end local start, _, minutes = string.find(duration, "^(%d+) minu?t?e?s?$") if start then return minutes * 60 end local start, _, seconds = string.find(duration, "^(%d+) seco?n?d?s?$") if start then return tonumber(seconds) end end ww_Preprocess = { {"|c%x%x%x%x%x%x%x%x([^|]+)|r", "%1"}, {"(%d+),(%d+)", "%1%2"}, {" +$", ""}, } ww_ignoredInvalidStats = { "all stats", "all resistances", } ww_IgnoredLines = { "^requires ", -- Reputation and materials requirements "^$", "^ [^ ]", "^classes: ", "^%a[%a' -]+ %(%d+/%d+%)$", -- Set names "^\".+\"$", "^\n", "^use: teaches you how to [a-oq-z]%a+ ", -- Profession items "^races: ", "^this item begins a quest$", "^<.*>$", "^mount$", "^major glyph$", "^duration: ", "^%d+ slot ", -- Bags "^thrown$", "^conjured item$", "^minor glyph$", "^use: right click to ", "^upgrade level:", "^ammo$", "^use: teaches you how to p[a-df-z]%a+ ", -- Profession items "^cogwheel$", "^crystal of fear$", "^projectile$", "^already known$", "^use: create", -- TODO: add these for ALL professions "^cooking ingredient$", "^crafting material$", "^locked$", "^prospectable$", -- Zone names "^ahn'kahet: the old kingdom$", "^ahn'qiraj$", "^assault on zan'vess$", "^alterac valley$", "^black temple$", "^blackrock depths$", "^blade's edge mountains$", "^coilfang: serpentshrine cavern$", "^dire maul$", "^drak'tharon keep$", "^ebon hold$", "^gilneas city$", "^grizzly hills$", "^icecrown citadel$", "^icecrown$", "^isle of conquest$", "^isle of thunder$", "^karazhan$", "^kezan$", "^kun%-lai summit$", "^molten front$", "^pursuing the black harvest$", "^ruins of ahn'qiraj$", "^shadowmoon valley", "^sholazar basin$", "^siege of orgrimmar$", "^strand of the ancients$", "^stratholme$", "^tempest keep$", "^the battle for mount hyjal$", "^the black morass$", "^the culling of stratholme$", "^the escape from durnholde$", "^the oculus$", "^the wandering isle$", "^theramore's fall ?%(?h?%)?$", "^timeless isle$", "^townlong steppes$", "^twilight highlands$", "^unga ingoo$", "^utgarde pinnacle$", "^violet hold$", "^wintergrasp$", "^zul'aman$", "^zul'drak$", "^zul'gurub$", } ww_TempIgnoredLines = { "^use: restores %d+ to %d+ %a+ and ", "^use: heals %d+ damage over %d+ sec%.$", } ww_UnweightedLines = { "^%(%d+%) set: ", "^set: ", -- In-game only (set bonus that you have enough pieces for) "chance t?on? ", "cooldown", -- Use effects that have a cooldown "^equip: you", "^use: .*enchants? ", " at the cost of ", } ww_MultipleStatLines = { {"^([^,]+) and ([^,]+)$", WeightsWatcher.twoStats, {"cooldownUseEffect", "elixir", "enchant", "food", "generic", "useEffect"}}, {"^([+-]?%d+ )(%a[%a ]+%a) and (%a[%a ]+%a)$", WeightsWatcher.multipleStatsOneNumber, {"food"}}, {"^([^,]+), ([^,]+),? and ([^,]+)$", function(text, pattern, section) local start, _, stat1, stat2, stat3 = string.find(text, pattern) if start then stat1 = WeightsWatcher.singleStat(stat1, section) stat2 = WeightsWatcher.singleStat(stat2, section) stat3 = WeightsWatcher.singleStat(stat3, section) if stat1 and stat2 and stat3 then return stat1.stats + stat2.stats + stat3.stats end end end, {"elixir", "enchant", "food"} }, {"^(%a+) and (%a+) spell power by (%d+)$", function(text, pattern) local start, _, stat1, stat2, value = string.find(text, pattern) if start then value = tonumber(value) local stats = WeightsWatcher.newStatTable() stats[stat1 .. " spell damage"] = value stats[stat2 .. " spell damage"] = value return stats end end, {"enchant"}, }, {"^(%a[%a ]+) and (%a[%a ]+) by (%d+)$", function(text, pattern, section) local start, _, stat1, stat2, value = string.find(text, pattern) if start then stat1 = WeightsWatcher.singleStat(stat1 .. " by " .. value, section) stat2 = WeightsWatcher.singleStat(stat2 .. " by " .. value, section) if stat1 and stat2 then return stat1.stats + stat2.stats else ww_unparsed_lines[text][pattern].parsedTo = {stat1, stat2} end end end, {"cooldownUseEffect", "elixir", "enchant"}, }, {"^(%a+), (%a+),? and (%a+) spell power by (%d+)$", function(text, pattern) local start, _, stat1, stat2, stat3, value = string.find(text, pattern) if start then value = tonumber(value) local stats = WeightsWatcher.newStatTable() stats[stat1 .. " spell damage"] = value stats[stat2 .. " spell damage"] = value stats[stat3 .. " spell damage"] = value return stats end end, {"elixir"}, }, {"^spell damage caused by (%a+), (%a+),? and (%a+) spells by up to (%d+)$", function(text, pattern) local start, _, stat1, stat2, stat3, value = string.find(text, pattern) if start then value = tonumber(value) local stats = WeightsWatcher.newStatTable() stats[stat1 .. " spell damage"] = value stats[stat2 .. " spell damage"] = value stats[stat3 .. " spell damage"] = value return stats end end, {"elixir"}, }, {"^resistance to (%a+), (%a+), (%a+), (%a+),? and (%a+) spells by (%d+)$", function(text, pattern) local start, _, school1, school2, school3, school4, school5, value = string.find(text, pattern) if start then value = tonumber(value) local stats = WeightsWatcher.newStatTable() stats[school1 .. " resistance"] = value stats[school2 .. " resistance"] = value stats[school3 .. " resistance"] = value stats[school4 .. " resistance"] = value stats[school5 .. " resistance"] = value return stats end end, {"cooldownUseEffect"}, }, -- item 38960 {"^(%a+), (%a+), and (%a+) skills by (%d+)$", function(text, pattern) local start, _, stat1, stat2, stat3, value = string.find(text, pattern) if start then value = tonumber(value) local stats = WeightsWatcher.newStatTable() stats[stat1] = value stats[stat2] = value stats[stat3] = value return stats end end, {"enchant"}, }, {"^chance to resist (%a+) and (%a+) effects by (%d+)%%$", function(text, pattern) local start, _, effect1, effect2, value = string.find(text, pattern) if start then value = tonumber(value) local stats = WeightsWatcher.newStatTable() stats[effect1 .. " resist chance (percent)"] = value stats[effect2 .. " resist chance (percent)"] = value return stats end end, {"equipEffect"}, }, } ww_SingleStatLines = { {"^(item level) (%d+)$", WeightsWatcher.statNameFirst, {"generic"}}, {"^([+-]?%d+) (armor)$", WeightsWatcher.statNumFirst, {"cooldownUseEffect", "elixir", "enchant", "generic"}}, {"^([+-]?%d+) (stamina)$", WeightsWatcher.statNumFirst, {"elixir", "enchant", "food", "generic", "socketBonus", "useEffect"}}, {"^([+-]?%d+) (intellect)$", WeightsWatcher.statNumFirst, {"cooldownUseEffect", "elixir", "enchant", "food", "generic", "socketBonus", "stackingEquipEffect", "useEffect"}}, {"^([+-]?%d+) (spell power)$", WeightsWatcher.statNumFirst, {"cooldownUseEffect", "elixir", "enchant", "equipEffect", "food", "generic", "socketBonus", "stackingEquipEffect", "useEffect"}}, {"^([+-]?%d+) (agility)$", WeightsWatcher.statNumFirst, {"cooldownUseEffect", "elixir", "enchant", "food", "generic", "socketBonus", "stackingEquipEffect", "useEffect"}}, {"^([+-]?%d+) (strength)$", WeightsWatcher.statNumFirst, {"cooldownUseEffect", "elixir", "enchant", "food", "generic", "socketBonus", "stackingEquipEffect", "useEffect"}}, {"^([+-]?%d+) (hit)$", WeightsWatcher.statNumFirst, {"cooldownUseEffect", "elixir", "enchant", "food", "generic", "socketBonus"}}, {"^([+-]?%d+) (critical strike)$", WeightsWatcher.statNumFirst, {"cooldownUseEffect", "elixir", "enchant", "food", "generic", "socketBonus", "useEffect"}}, {"^([+-]?%d+) (mastery)$", WeightsWatcher.statNumFirst, {"cooldownUseEffect", "elixir", "enchant", "food", "generic", "socketBonus"}}, {"^([+-]?%d+) (expertise)$", WeightsWatcher.statNumFirst, {"elixir", "enchant", "food", "generic", "socketBonus"}}, {"^([+-]?%d+) (dodge)$", WeightsWatcher.statNumFirst, {"cooldownUseEffect", "elixir", "enchant", "food", "generic", "socketBonus", "stackingEquipEffect", "useEffect"}}, {"^([+-]?%d+) (parry)$", WeightsWatcher.statNumFirst, {"cooldownUseEffect", "enchant", "food", "generic", "socketBonus"}}, {"^([+-]?%d+) (haste)$", WeightsWatcher.statNumFirst, {"cooldownUseEffect", "elixir", "enchant", "food", "generic", "socketBonus"}}, {"^([+-]?%d+) (pvp power)$", WeightsWatcher.statNumFirst, {"cooldownUseEffect", "elixir", "enchant", "equipEffect", "generic", "socketBonus"}}, {"^([+-]?%d+) (pvp resilience)$", WeightsWatcher.statNumFirst, {"cooldownUseEffect", "elixir", "enchant", "food", "generic", "socketBonus"}}, {"^%(?(%d+%.?%d*) damage per second%)?$", function(text, pattern) return WeightsWatcher.singleStatValueOnly(text, pattern, "melee dps") end, {"generic"}, }, {"^([+-]?%d+) (attack power)$", WeightsWatcher.statNumFirst, {"cooldownUseEffect", "elixir", "enchant", "food", "generic", "socketBonus", "stackingEquipEffect", "useEffect"}}, {"^([+-]?%d+) (spirit)$", WeightsWatcher.statNumFirst, {"cooldownUseEffect", "elixir", "enchant", "food", "generic", "socketBonus", "stackingEquipEffect", "useEffect"}}, {"^([+-]?%d+) (%a+) [ep]v?ery? (%d+) seco?n?d?s?%.?$", function(text, pattern) local start, _, amount, stat, period = text:find(pattern) if start then amount = tonumber(amount) if period ~= "5" then amount = amount * 5 / tonumber(period) end if stat == "mana" then return WeightsWatcher.newStatTable({ mp5 = amount }) elseif stat == "health" then return WeightsWatcher.newStatTable({ hp5 = amount }) end end end, {"cooldownUseEffect", "elixir", "enchant", "equipEffect", "food", "generic", "socketBonus"}, }, {"^([+-]?%d+) (%a+ resistances?)$", WeightsWatcher.statNumFirst, {"cooldownUseEffect", "elixir", "enchant", "equipEffect", "generic", "useEffect"}}, -- random suffix enchants {"^([+-]?%d+) (%a+ spell damage)$", WeightsWatcher.statNumFirst, {"cooldownUseEffect", "generic"}}, {"^minor run speed increase$", function(text, pattern) return WeightsWatcher.newStatTable({["minor run speed"] = 1}) end, {"enchant", "equipEffect", "generic"}, }, {"^([+-]?%d+) (health)$", WeightsWatcher.statNumFirst, {"cooldownUseEffect", "elixir", "enchant", "useEffect"}}, {"^(%d+) to (%d+) (health)$", WeightsWatcher.statRangeFirst, {"useEffect"}}, {"^(fishing) by ([+-]?%d+)$", WeightsWatcher.statNameFirst, {"enchant", "fishing"}}, {"^([+-]?%d+) (all stats)$", WeightsWatcher.statNumFirst, {"cooldownUseEffect", "elixir", "enchant", "food", "generic"}}, {"^(%a+ spell )power by ([+-]?%d+)$", function(text, pattern) local start, _, name, value = string.find(text, pattern) if start then return WeightsWatcher.newStatTable({[name .. "damage"] = tonumber(value)}) end end, {"elixir", "enchant"}, }, {"^deals (%d+) to (%d+) damage every time you block$", function(text, pattern) local start, _, minimum, maximum = text:find(pattern) if start then return WeightsWatcher.newStatTable({["average damage dealt on block"] = (minimum + maximum) / 2}) end end, {"enchant", "equipEffect"}, }, {"^deals (%d+%.?%d*) damage every time you block$", function(text, pattern) local start, _, value = text:find(pattern) if start then return WeightsWatcher.newStatTable({["average damage dealt on block"] = tonumber(value)}) end end, {"enchant", "equipEffect"}, }, {"^([+-]?%d+) (mana)$", WeightsWatcher.statNumFirst, {"elixir", "enchant"}}, {"^([+-]?%d+) ranged damage$", function(text, pattern) return WeightsWatcher.singleStatValueOnly(text, pattern, "average ranged weapon damage") + WeightsWatcher.singleStatValueOnly(text, pattern, "maximum ranged weapon damage") end, {"enchant"}, }, {"^([+-]?%d+) (ranged %a+ ?%a+)$", WeightsWatcher.statNumFirst, {"enchant", "generic"}}, {"^effective stealth level by (%d+)$", function(text, pattern) return WeightsWatcher.singleStatValueOnly(text, pattern, "increased stealth") end, {"equipEffect"}, }, {"^decreases? (%a[%a ]+) by (%d+)$", function(text, pattern, section) local start, _, name, value = string.find(text, pattern) if start then local stats = WeightsWatcher.parseStats("-" .. value .. " " .. name, section) if stats then return stats.stats end end end, {"elixir", "food", "useEffect"}, }, {"^mount speed by ([+-]?%d+)%%$", function(text, pattern) return WeightsWatcher.singleStatValueOnly(text, pattern, "mount speed (percent)") end, {"enchant", "equipEffect"}, }, {"^(herbalism) by ([+-]?%d+)$", WeightsWatcher.statNameFirst, {"enchant", "equipEffect"}}, {"^(mining) by ([+-]?%d+)$", WeightsWatcher.statNameFirst, {"enchant", "equipEffect"}}, {"^(skinning) by ([+-]?%d+)$", WeightsWatcher.statNameFirst, {"enchant"}}, {"^threat caused by (%d+)%%$", function(text, pattern) local start, _, value = string.find(text, pattern) if start then return WeightsWatcher.newStatTable({["threat (percent)"] = value}) end end, {"enchant"}, }, {"^prismatic socket$", function(text, pattern) return WeightsWatcher.newStatTable({[text] = 1}) end, {"enchant"}, }, {"^chance to resist (%a+) effects by (%d+)%%$", function(text, pattern) local start, _, effect, value = string.find(text, pattern) if start then return WeightsWatcher.newStatTable({[effect .. " resist chance (percent)"] = tonumber(value)}) end end, {"equipEffect", "generic"}, }, -- Tends to eat other stats if not last {"^(%a+ ?%a+ ?%a* ?%a+) by ([+-]?%d+%%?)$", function(text, pattern, section) local start, _, name, value = string.find(text, pattern) if start then local stats = WeightsWatcher.parseStats(value .. " " .. name, section) if stats then return stats.stats end end end, {"cooldownUseEffect", "elixir", "enchant", "equipEffect", "food", "stackingEquipEffect", "useEffect"}, }, -- meta/enchant effects {"^(%d+)%% increased armor value from items$", function(text, pattern) return WeightsWatcher.singleStatValueOnly(text, pattern, "armor from items (percent)") end, {"generic"}, }, {"^([+-]?%d+)%% shield block value$", function(text, pattern) return WeightsWatcher.singleStatValueOnly(text, pattern, "block value (percent)") end, {"generic"}, }, {"^chance to increase melee/ranged attack speed$", function(text, pattern) return WeightsWatcher.newStatTable({["chance to increase physical haste"] = 1}) end, {"generic"}, }, {"^chance to increase spell cast speed$", function(text, pattern) return WeightsWatcher.newStatTable({["chance to increase spell haste"] = 1}) end, {"generic"}, }, {"^chance to restore health on hit$", function(text, pattern) return WeightsWatcher.newStatTable({[text] = 1}) end, {"generic"}, }, {"^chance to restore mana on spellcast$", function(text, pattern) return WeightsWatcher.newStatTable({[text] = 1}) end, {"generic"}, }, {"^chance to stun target$", function(text, pattern) return WeightsWatcher.newStatTable({[text] = 1}) end, {"generic"}, }, {"^(%d+)%% increased critical effect$", function(text, pattern) return WeightsWatcher.singleStatValueOnly(text, pattern, "critical effect (percent)") end, {"generic"}, }, {"^(%a+/?%a* duration )reduced by (%d+)%%$", function(text, pattern) local start, _, name, value = string.find(text, pattern) if start then return WeightsWatcher.newStatTable({[name .. "reduction (percent)"] = tonumber(value)}) end end, {"enchant", "equipEffect", "generic"}, }, {"^([+-]?%d+)%% mana$", function(text, pattern) return WeightsWatcher.singleStatValueOnly(text, pattern, "mana (percent)") end, {"generic"}, }, {"^([+-]?%d+) melee damage$", function(text, pattern) return WeightsWatcher.singleStatValueOnly(text, pattern, "average melee weapon damage") + WeightsWatcher.singleStatValueOnly(text, pattern, "maximum melee weapon damage") end, {"enchant", "generic"}, }, {"^([+-]?%d+) physical damage$", function(text, pattern) return WeightsWatcher.singleStatValueOnly(text, pattern, "average melee weapon damage") + WeightsWatcher.singleStatValueOnly(text, pattern, "maximum melee weapon damage") + WeightsWatcher.singleStatValueOnly(text, pattern, "average ranged weapon damage") + WeightsWatcher.singleStatValueOnly(text, pattern, "maximum ranged weapon damage") end, {"enchant"}, }, {"^sometimes heal on your crits$", function(text, pattern) return WeightsWatcher.newStatTable({[text] = 1}) end, {"generic"}, }, {"^reduce spell damage taken by (%d+)%%$", function(text, pattern) return WeightsWatcher.singleStatValueOnly(text, pattern, "spell damage taken reduction (percent)") end, {"generic"}, }, {"^(%d+)%% spell reflect$", function(text, pattern) return WeightsWatcher.singleStatValueOnly(text, pattern, "spell reflect (percent)") end, {"elixir", "generic"}, }, {"^(%d+)%% reduced threat$", function(text, pattern) return WeightsWatcher.singleStatValueOnly(text, pattern, "threat reduction (percent)") end, {"enchant", "generic"}, }, {"^has a (%d+%% chance) per hit of giving you (%d+) damage absorption$", function(text, pattern) local start, _, chance, damage = text:find(pattern) if start then return WeightsWatcher.newStatTable({["damage absorption (" .. chance .. ")"] = tonumber(damage)}) end end, {"enchant"}, }, {"^([+-]?%d+) (%a+ damage to %a+)$", WeightsWatcher.statNumFirst, {"enchant"}}, -- item 38981 {"^([+-]?%d+) (attack power against undead)$", WeightsWatcher.statNumFirst, {"enchant"}}, {"^chance on melee or ranged hit to gain capacitance$", function(text, pattern) return WeightsWatcher.newStatTable({[text] = 1}) end, {"generic"}, }, {"^chance on spell damage to gain 30%% spell haste$", function(text, pattern) return WeightsWatcher.newStatTable({[text] = 1}) end, {"generic"}, }, {"^chance on beneficial spell to make your spells cost no mana for 4 sec%.$", function(text, pattern) return WeightsWatcher.newStatTable({[text] = 1}) end, {"generic"}, }, {"^chance on being hit to gain 20%% reduction to damage taken$", function(text, pattern) return WeightsWatcher.newStatTable({[text] = 1}) end, {"generic"}, }, } ww_ItemInfoLines = { "^binds ", "^unique", "^soulbound$", "^quest item$", "^heroic", "^season ", "^raid finder$", "^thunderforged$", "^elite$", "^flexible$", "^timeless$", "^warforged$", } ww_DoubleSlotLines = { "^chest$", "^legs$", "^hands$", "^head$", "^shoulder$", "^feet$", "^waist$", "^wrist$", "^one%-hand$", "^two%-hand$", "^ranged$", "^off hand$", "^main hand$", } ww_SingleSlotLines = { "^finger$", "^back$", "^trinket$", "^neck$", "^held in off%-hand$", "^shirt$", "^tabard$", }