;; init note (if (> 1 (# itemdb)) (print "Failed to load item database") (print "Item database loaded")) ;; 1a: 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))) ;; 1b: str utils (fn starts-with [s patt] (= (string.sub s 1 (string.len patt)) patt)) (fn un-quote [s] (string.gsub s "\'" "")) ;; 1c: number utils (fn max [a b] (if (> a b) a b)) ;; 1d: fennel 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)) (starts-with a "lt<:") (> (tonumber (string.sub a 5)) (tonumber b)) (starts-with a "gt>:") (< (tonumber (string.sub a 5)) (tonumber b)) (= a b))) ;; 2a: querying (lambda find-items [params ?sort] (let [results (icollect [_ row (ipairs itemdb)] (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 (. results 1)))) ;; 2b: 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] (let [stat-comparison "gt>:%d"] (find-items {4 class 6 subclass 10 invtype 14 (string.format "lte:%d" lv) stat-position (string.format stat-comparison item-stat)} ?sort))) (lambda find-good-item [invtype lv stat-position stat-baseline ?sort] (find-items {10 invtype 14 (string.format "lte:%d" lv) stat-position (string.format "gte:%d" stat-baseline)} ?sort)) ;; 3b: specialized parsing (fn parse-item-id [link] (tonumber (string.sub (string.match link "item:[%d+]+") 6))) ;; 3c: arg parsing (fn parse-opts [msg] (icollect [k v (string.gmatch msg "%w+")] v)) ;; 4a: ui (fn ui-print [msg] (UIErrorsFrame:AddMessage msg)) ;; 4b (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) ;; 5: 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) i-id (if i-link (parse-item-id i-link)) p-level (UnitLevel "player")] (if (not i-link) ;; empty (let [best (. (find-good-item inv-slot (UnitLevel "player") stat 0 (fn [a b] (< (+ (. a 12) (. a stat)) (+ (. b 12) (. b stat))) )) gear-index)] (if best (do (DEFAULT_CHAT_FRAME:AddMessage (string.format "\124Hitem:%d:0:0:0:0:0:0:0:0\124h[%s]\124h\124r" (. best 2) (. best 8))) (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 (max i-stat 0) (fn [a b] (< (+ (. a 12) (. a stat)) (+ (. b 12) (. b stat)) ))) gear-index)] (if best (do (DEFAULT_CHAT_FRAME:AddMessage (string.format "\124Hitem:%d:0:0:0:0:0:0:0:0\124h[%s]\124h\124r" (. best 2) (. best 8))) (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")))))) (print (.. "Item " (un-quote i-name) " not found"))) "ok"))))) ;; unused (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))