@@ -0,0 +1,6 @@ | |||||
-- slash command /better as addon entrypoint | |||||
SLASH_BETTER1 = "/better" | |||||
SlashCmdList["BETTER"] = function(msg) | |||||
main(msg) | |||||
end | |||||
@@ -0,0 +1,10 @@ | |||||
## Interface: 20400 | |||||
## Title: Better | |||||
## Notes: Shows better item for given slot | |||||
## Author: L | |||||
## Version: 0.0.1 | |||||
Items.lua | |||||
main.lua | |||||
Better.lua |
@@ -0,0 +1,6 @@ | |||||
#!make | |||||
.PHONY: default | |||||
default: | |||||
fennel --compile main.fnl > main.lua |
@@ -0,0 +1,65 @@ | |||||
#!/usr/bin/ruby | |||||
require 'sqlite3' | |||||
require 'json' | |||||
db = SQLite3::Database.open '/tmp/tbc.db' | |||||
db.results_as_hash = true | |||||
items = db.execute 'select i.entry, i.class, i.subclass, i.name, i.inventorytype, i.itemlevel, i.requiredlevel, i.dmg_min1, i.dmg_max1, "" as mobs, delay, %s from item_template i;' % [(1..10).to_a.map { |n| "stat_type#{n} as k#{n}, stat_value#{n} as v#{n}" }.join(', ')] | |||||
total = items.length | |||||
items_w_loot = [] | |||||
keys = (1..10).map { |n| "k#{n}" } | |||||
vals = (1..10).map { |n| "v#{n}" } | |||||
skip_keys = keys + ['delay', 'mobs'] | |||||
def truncate(s, len) | |||||
s.length > len ? "#{s[0...len]}..." : s | |||||
end | |||||
items.each_with_index do |item, idx| | |||||
mobs = db.execute 'select c.name as name, cl.ChanceOrQuestChance as chance from creature_loot_template cl left join creature_template c on c.entry=cl.entry where cl.item=%d order by chance desc' % [item['entry']] | |||||
new = [] | |||||
item.each_pair do |k, v| | |||||
if skip_keys.include?(k) or vals.include?(k) | |||||
next | |||||
end | |||||
new.push k | |||||
new.push v | |||||
end | |||||
new.push 'mobs' | |||||
new.push(truncate ((mobs.map {|m| m['name']}.join ',') or ""), 300) | |||||
(0..56).each do |type| | |||||
val = 0 | |||||
keys.each_with_index do |k, kidx| | |||||
if item[k] == type | |||||
val = item[vals[kidx]] | |||||
end | |||||
end | |||||
new.push val | |||||
end | |||||
if item['delay'] | |||||
new.push item['delay'] | |||||
end | |||||
if idx % 100 == 0 | |||||
print "\r%f%%" % [(idx * 100).to_f / total] | |||||
end | |||||
if idx > 50 | |||||
# break | |||||
end | |||||
File.write(File.join(Dir.home, 'items_w_loot.json'), (new.to_json.concat ",\n"), mode: 'a+') | |||||
end |
@@ -0,0 +1,204 @@ | |||||
;; coll utils | |||||
(fn length-of [coll] | |||||
(if coll | |||||
(do | |||||
(var c 0) | |||||
(each [k (pairs coll)] | |||||
(set c (+ c 1))) | |||||
c) | |||||
0)) | |||||
(fn first [coll] | |||||
(let [key (next coll)] | |||||
(. coll key))) | |||||
;; str utils | |||||
(fn starts-with [s patt] | |||||
(= (string.sub s 1 (string.len patt)) patt)) | |||||
(fn un-quote [s] | |||||
(string.gsub s "\'" "")) | |||||
;; lang utils | |||||
(fn matches [a b] | |||||
(if (starts-with a "lte:") | |||||
(>= (tonumber (string.sub a 5)) (tonumber b)) | |||||
(starts-with a "gte:") | |||||
(<= (tonumber (string.sub a 5)) (tonumber b)) | |||||
(= a b))) | |||||
;; querying | |||||
(lambda find-items [params ?sort] | |||||
(let [results (icollect [_ row (ipairs items)] | |||||
(do | |||||
(var hit true) | |||||
(each [key param (pairs params)] | |||||
(do | |||||
(set hit (and hit (matches param (. row key)))))) | |||||
(if hit | |||||
row)))] | |||||
(if (and ?sort results) | |||||
(do | |||||
(table.sort results ?sort) | |||||
results) | |||||
results))) | |||||
(lambda find-item [params ?sort] | |||||
(let [results (find-items params ?sort)] | |||||
(if results | |||||
(first results)))) | |||||
;; specialized querying | |||||
(fn find-item-by-name [name] | |||||
(find-item {8 name})) | |||||
(fn find-next-better-item [class subclass invtype lv ilv stat-position item-stat ?sort] | |||||
(find-items {4 class | |||||
6 subclass | |||||
10 invtype | |||||
14 (string.format "lte:%d" lv) | |||||
14 (string.format "gte:%d" (- lv 4)) | |||||
;; 12 (string.format "gte:%d" ilv) | |||||
stat-position (string.format "gte:%d" item-stat)} | |||||
?sort)) | |||||
(lambda find-good-item [invtype lv stat-position stat-baseline ?sort] | |||||
(find-items {10 invtype | |||||
14 (string.format "lte:%d" lv) | |||||
14 (string.format "gte:%d" (- lv 6)) | |||||
stat-position (string.format "gte:%d" stat-baseline)} | |||||
?sort)) | |||||
;; arg parsing | |||||
(fn parse-opts [msg] | |||||
(icollect [k v (string.gmatch msg "%w+")] v)) | |||||
;; ui | |||||
(fn ui-print [msg] | |||||
(UIErrorsFrame:AddMessage msg)) | |||||
(fn stat-name [n] | |||||
(let [stat-names ["Mana" "Health" "No Visible Effect" | |||||
"Agility" "Strength" "Intellect" | |||||
"Spirit" "Stamina" "No Visible Effect" "No Visible Effect" | |||||
"No Visible Effect" "No Visible Effect*" "Defense Rating" | |||||
"Dodge" "Parry Rating" "Shield Block Rating" | |||||
"Melee Hit Rating" "Ranged Hit Rating" "Spell Hit Rating" | |||||
"Melee Critical Strike" "Ranged Critical Strike" "Spell Critical Strike" | |||||
"Melee Hit Avoidance" "Ranged Hit Avoidance" "Spell Hit Avoidance" | |||||
"Melee Critical Avoidance" "Spell Critical Avoidance" "Melee Haste Rating" | |||||
"Ranged Haste Rating" "Spell Haste Rating" "Hit Avoidance Rating" | |||||
"Hit Rating" "Critical Strike" "Hit Avoidance Rating" | |||||
"Critical Avoidance Rating" "Resilience" "Haste" | |||||
"Expertise" "Attack Power" "Ranged Attack Power" | |||||
"No Visible Effect" "Healing Done By Magical Spells and Effects up to value" "Damage Done By Magical Spells and Effects up to value" | |||||
"Mana Regeneration (Ticks every 5 seconds)" "Armor Penetration Rating" "Spell Power" | |||||
"Health Regeneration (Ticks every 5 seconds)" "Spell Penetration" "Block Value of Shield" | |||||
"Mastery" "Armor" "Fire Resist" | |||||
"Frost Resist" "Shadow Resist" "Nature Resist" | |||||
"Arcane Resist"]] | |||||
(.. (. stat-names (+ n 1)) " (" n ")"))) | |||||
(fn stat-print [item] | |||||
(var val "") | |||||
(for [n 22 56] | |||||
(if (< 0 (. item n)) | |||||
(set val (.. val " " (stat-name (- n 21)) ": " (. item n))))) | |||||
val) | |||||
;; main | |||||
(global | |||||
main | |||||
(fn main [msg] | |||||
(let [opts (icollect [v (string.gmatch msg "%w+")] v) | |||||
[slot stat gear-index] opts | |||||
slot (.. slot "Slot") ;; omit the need to type it out | |||||
stat (+ 21 (tonumber stat)) ;; stats start after field 21, so agility (3) is 22+3, etc | |||||
gear-index (tonumber (or gear-index "1")) | |||||
inv-slot (GetInventorySlotInfo slot) | |||||
i-link (GetInventoryItemLink "player" inv-slot) | |||||
p-level (UnitLevel "player")] | |||||
;; (print (string.format "slot %s stat %s gear-index %s" slot stat gear-index)) | |||||
(if (not i-link) | |||||
;; empty | |||||
(let [best (. (find-good-item inv-slot (UnitLevel "player") stat 0 (fn [a b] (< (. a 12) (. b 12)))) gear-index)] | |||||
(if best | |||||
(do | |||||
(ui-print (.. "next best available: " | |||||
(. best 8) | |||||
" " | |||||
(stat-print best) | |||||
" at " | |||||
(or (. best 20)) "N/A"))))) | |||||
;; exists | |||||
(let [i-name (GetItemInfo i-link) | |||||
stats (find-item-by-name (un-quote i-name))] | |||||
(if stats | |||||
(let [i-class (. stats 4) | |||||
i-subclass (. stats 6) | |||||
i-invtype (. stats 10) | |||||
i-level (. stats 12) | |||||
i-stat (. stats stat) | |||||
best (. (find-next-better-item i-class i-subclass i-invtype p-level i-level stat i-stat (fn [a b] (< (. a stat) (. b stat)))) gear-index)] | |||||
(if best | |||||
(do | |||||
(ui-print (.. "next best available: " | |||||
(. best 8) | |||||
" " | |||||
(stat-print best) | |||||
" at " | |||||
(or (. best 20)) "N/A")) | |||||
(ui-print (.. "currently equipped " i-name " " (or (. stats stat) "N/A"))))))) | |||||
"ok"))))) | |||||
(fn maxxed [] | |||||
(let [slots ["AmmoSlot" | |||||
"BackSlot" | |||||
"Bag0Slot" | |||||
"Bag1Slot" | |||||
"Bag2Slot" | |||||
"Bag3Slot" | |||||
"ChestSlot" | |||||
"FeetSlot" | |||||
"Finger0Slot" | |||||
"Finger1Slot" | |||||
"HandsSlot" | |||||
"HeadSlot" | |||||
"LegsSlot" | |||||
"MainHandSlot" | |||||
"NeckSlot" | |||||
"RangedSlot" | |||||
"SecondaryHandSlot" | |||||
"ShirtSlot" | |||||
"ShoulderSlot" | |||||
"TabardSlot" | |||||
"Trinket0Slot" | |||||
"Trinket1Slot" | |||||
"WaistSlot" | |||||
"WristSlot"]] | |||||
slots)) |
@@ -0,0 +1,182 @@ | |||||
local function length_of(coll) | |||||
if coll then | |||||
local c = 0 | |||||
for k in pairs(coll) do | |||||
c = (c + 1) | |||||
end | |||||
return c | |||||
else | |||||
return 0 | |||||
end | |||||
end | |||||
local function first(coll) | |||||
local key = next(coll) | |||||
return coll[key] | |||||
end | |||||
local function starts_with(s, patt) | |||||
return (string.sub(s, 1, string.len(patt)) == patt) | |||||
end | |||||
local function un_quote(s) | |||||
return string.gsub(s, "'", "") | |||||
end | |||||
local function matches(a, b) | |||||
if starts_with(a, "lte:") then | |||||
return (tonumber(string.sub(a, 5)) >= tonumber(b)) | |||||
elseif starts_with(a, "gte:") then | |||||
return (tonumber(string.sub(a, 5)) <= tonumber(b)) | |||||
else | |||||
return (a == b) | |||||
end | |||||
end | |||||
local function find_items(params, _3fsort) | |||||
_G.assert((nil ~= params), "Missing argument params on main.fnl:37") | |||||
local results | |||||
do | |||||
local tbl_17_auto = {} | |||||
local i_18_auto = #tbl_17_auto | |||||
for _, row in ipairs(items) do | |||||
local val_19_auto | |||||
do | |||||
local hit = true | |||||
for key, param in pairs(params) do | |||||
hit = (hit and matches(param, row[key])) | |||||
end | |||||
if hit then | |||||
val_19_auto = row | |||||
else | |||||
val_19_auto = nil | |||||
end | |||||
end | |||||
if (nil ~= val_19_auto) then | |||||
i_18_auto = (i_18_auto + 1) | |||||
do end (tbl_17_auto)[i_18_auto] = val_19_auto | |||||
else | |||||
end | |||||
end | |||||
results = tbl_17_auto | |||||
end | |||||
if (_3fsort and results) then | |||||
table.sort(results, _3fsort) | |||||
return results | |||||
else | |||||
return results | |||||
end | |||||
end | |||||
local function find_item(params, _3fsort) | |||||
_G.assert((nil ~= params), "Missing argument params on main.fnl:52") | |||||
local results = find_items(params, _3fsort) | |||||
if results then | |||||
return first(results) | |||||
else | |||||
return nil | |||||
end | |||||
end | |||||
local function find_item_by_name(name) | |||||
return find_item({[8] = name}) | |||||
end | |||||
local function find_next_better_item(class, subclass, invtype, lv, ilv, stat_position, item_stat, _3fsort) | |||||
return find_items({[4] = class, [6] = subclass, [10] = invtype, [14] = string.format("gte:%d", (lv - 4)), [stat_position] = string.format("gte:%d", item_stat)}, _3fsort) | |||||
end | |||||
local function find_good_item(invtype, lv, stat_position, stat_baseline, _3fsort) | |||||
_G.assert((nil ~= stat_baseline), "Missing argument stat-baseline on main.fnl:72") | |||||
_G.assert((nil ~= stat_position), "Missing argument stat-position on main.fnl:72") | |||||
_G.assert((nil ~= lv), "Missing argument lv on main.fnl:72") | |||||
_G.assert((nil ~= invtype), "Missing argument invtype on main.fnl:72") | |||||
return find_items({[10] = invtype, [14] = string.format("gte:%d", (lv - 6)), [stat_position] = string.format("gte:%d", stat_baseline)}, _3fsort) | |||||
end | |||||
local function parse_opts(msg) | |||||
local tbl_17_auto = {} | |||||
local i_18_auto = #tbl_17_auto | |||||
for k, v in string.gmatch(msg, "%w+") do | |||||
local val_19_auto = v | |||||
if (nil ~= val_19_auto) then | |||||
i_18_auto = (i_18_auto + 1) | |||||
do end (tbl_17_auto)[i_18_auto] = val_19_auto | |||||
else | |||||
end | |||||
end | |||||
return tbl_17_auto | |||||
end | |||||
local function ui_print(msg) | |||||
return UIErrorsFrame:AddMessage(msg) | |||||
end | |||||
local function stat_name(n) | |||||
local stat_names = {"Mana", "Health", "No Visible Effect", "Agility", "Strength", "Intellect", "Spirit", "Stamina", "No Visible Effect", "No Visible Effect", "No Visible Effect", "No Visible Effect*", "Defense Rating", "Dodge", "Parry Rating", "Shield Block Rating", "Melee Hit Rating", "Ranged Hit Rating", "Spell Hit Rating", "Melee Critical Strike", "Ranged Critical Strike", "Spell Critical Strike", "Melee Hit Avoidance", "Ranged Hit Avoidance", "Spell Hit Avoidance", "Melee Critical Avoidance", "Spell Critical Avoidance", "Melee Haste Rating", "Ranged Haste Rating", "Spell Haste Rating", "Hit Avoidance Rating", "Hit Rating", "Critical Strike", "Hit Avoidance Rating", "Critical Avoidance Rating", "Resilience", "Haste", "Expertise", "Attack Power", "Ranged Attack Power", "No Visible Effect", "Healing Done By Magical Spells and Effects up to value", "Damage Done By Magical Spells and Effects up to value", "Mana Regeneration (Ticks every 5 seconds)", "Armor Penetration Rating", "Spell Power", "Health Regeneration (Ticks every 5 seconds)", "Spell Penetration", "Block Value of Shield", "Mastery", "Armor", "Fire Resist", "Frost Resist", "Shadow Resist", "Nature Resist", "Arcane Resist"} | |||||
return (stat_names[(n + 1)] .. " (" .. n .. ")") | |||||
end | |||||
local function stat_print(item) | |||||
local val = "" | |||||
for n = 22, 56 do | |||||
if (0 < item[n]) then | |||||
val = (val .. " " .. stat_name((n - 21)) .. ": " .. item[n]) | |||||
else | |||||
end | |||||
end | |||||
return val | |||||
end | |||||
local function main0(msg) | |||||
local opts | |||||
do | |||||
local tbl_17_auto = {} | |||||
local i_18_auto = #tbl_17_auto | |||||
for v in string.gmatch(msg, "%w+") do | |||||
local val_19_auto = v | |||||
if (nil ~= val_19_auto) then | |||||
i_18_auto = (i_18_auto + 1) | |||||
do end (tbl_17_auto)[i_18_auto] = val_19_auto | |||||
else | |||||
end | |||||
end | |||||
opts = tbl_17_auto | |||||
end | |||||
local _let_10_ = opts | |||||
local slot = _let_10_[1] | |||||
local stat = _let_10_[2] | |||||
local gear_index = _let_10_[3] | |||||
local slot0 = (slot .. "Slot") | |||||
local stat0 = (21 + tonumber(stat)) | |||||
local gear_index0 = tonumber((gear_index or "1")) | |||||
local inv_slot = GetInventorySlotInfo(slot0) | |||||
local i_link = GetInventoryItemLink("player", inv_slot) | |||||
local p_level = UnitLevel("player") | |||||
if not i_link then | |||||
local best | |||||
local function _11_(a, b) | |||||
return (a[12] < b[12]) | |||||
end | |||||
best = (find_good_item(inv_slot, UnitLevel("player"), stat0, 0, _11_))[gear_index0] | |||||
if best then | |||||
return ui_print(("next best available: " .. best[8] .. " " .. stat_print(best) .. " at " .. best[20] .. "N/A")) | |||||
else | |||||
return nil | |||||
end | |||||
else | |||||
local i_name = GetItemInfo(i_link) | |||||
local stats = find_item_by_name(un_quote(i_name)) | |||||
if stats then | |||||
local i_class = stats[4] | |||||
local i_subclass = stats[6] | |||||
local i_invtype = stats[10] | |||||
local i_level = stats[12] | |||||
local i_stat = stats[stat0] | |||||
local best | |||||
local function _13_(a, b) | |||||
return (a[stat0] < b[stat0]) | |||||
end | |||||
best = (find_next_better_item(i_class, i_subclass, i_invtype, p_level, i_level, stat0, i_stat, _13_))[gear_index0] | |||||
if best then | |||||
ui_print(("next best available: " .. best[8] .. " " .. stat_print(best) .. " at " .. best[20] .. "N/A")) | |||||
ui_print(("currently equipped " .. i_name .. " " .. (stats[stat0] or "N/A"))) | |||||
else | |||||
end | |||||
else | |||||
end | |||||
return "ok" | |||||
end | |||||
end | |||||
main = main0 | |||||
local function maxxed() | |||||
local slots = {"AmmoSlot", "BackSlot", "Bag0Slot", "Bag1Slot", "Bag2Slot", "Bag3Slot", "ChestSlot", "FeetSlot", "Finger0Slot", "Finger1Slot", "HandsSlot", "HeadSlot", "LegsSlot", "MainHandSlot", "NeckSlot", "RangedSlot", "SecondaryHandSlot", "ShirtSlot", "ShoulderSlot", "TabardSlot", "Trinket0Slot", "Trinket1Slot", "WaistSlot", "WristSlot"} | |||||
return slots | |||||
end | |||||
return maxxed |