aux auto bid and price heuristics
this commit does two things 1. it implements auto bid just like auto buy 2. it has some minimum pricing heuristics when posting items. this behavoiur actiaves when the post bid price is set to 0, the buyout price behaves like a minimum
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
module 'aux.util.completion'
|
||||
|
||||
local aux = require 'aux'
|
||||
local filter_util = require 'aux.util.filter'
|
||||
|
||||
function M:complete_filter()
|
||||
if IsControlKeyDown() then -- TODO problem is ctrl-v, maybe find a better solution
|
||||
return
|
||||
end
|
||||
|
||||
local filter_string = this:GetText()
|
||||
|
||||
local completed_filter_string = aux.select(3, strfind(filter_string, '([^;]*)/[^/;]*$'))
|
||||
local _, suggestions = filter_util.query(completed_filter_string)
|
||||
|
||||
local start_index, _, current_modifier = strfind(filter_string, '([^/;]*)$')
|
||||
current_modifier = current_modifier or ''
|
||||
|
||||
for _, suggestion in ipairs(suggestions) do
|
||||
if strsub(strupper(suggestion), 1, strlen(current_modifier)) == strupper(current_modifier) then
|
||||
this:SetText(strlower(strsub(filter_string, 1, start_index - 1) .. suggestion))
|
||||
this:HighlightText(strlen(filter_string), -1)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function M.complete(candidates)
|
||||
return function(self)
|
||||
if IsControlKeyDown() then -- TODO problem is ctrl-v, maybe find a better solution
|
||||
return
|
||||
end
|
||||
|
||||
local text = self:GetText()
|
||||
|
||||
local t = candidates()
|
||||
for i = 1, getn(t) do
|
||||
if strsub(strupper(t[i]), 1, strlen(text)) == strupper(text) then
|
||||
self:SetText(strlower(t[i]))
|
||||
self:HighlightText(strlen(text), -1)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,549 @@
|
||||
module 'aux.util.filter'
|
||||
|
||||
local T = require 'T'
|
||||
local aux = require 'aux'
|
||||
local info = require 'aux.util.info'
|
||||
local money = require 'aux.util.money'
|
||||
local filter_util = require 'aux.util.filter'
|
||||
local history = require 'aux.core.history'
|
||||
local disenchant = require 'aux.core.disenchant'
|
||||
|
||||
function default_filter(str)
|
||||
return {
|
||||
input_type = '',
|
||||
validator = function()
|
||||
return function(auction_record)
|
||||
return aux.any(auction_record.tooltip, function(entry)
|
||||
return strfind(strlower(entry.left_text or ''), str, 1, true) or strfind(strlower(entry.right_text or ''), str, 1, true)
|
||||
end)
|
||||
end
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
M.filters = {
|
||||
|
||||
['utilizable'] = {
|
||||
input_type = '',
|
||||
validator = function()
|
||||
return function(auction_record)
|
||||
return auction_record.usable and not info.tooltip_match(ITEM_SPELL_KNOWN, auction_record.tooltip)
|
||||
end
|
||||
end,
|
||||
},
|
||||
|
||||
['tooltip'] = {
|
||||
input_type = 'string',
|
||||
validator = function(str)
|
||||
return default_filter(str).validator()
|
||||
end,
|
||||
},
|
||||
|
||||
['item'] = {
|
||||
input_type = 'string',
|
||||
validator = function(name)
|
||||
return function(auction_record)
|
||||
return strlower(info.item(auction_record.item_id).name) == name
|
||||
end
|
||||
end
|
||||
},
|
||||
|
||||
['left'] = {
|
||||
input_type = T.list('30m', '2h', '8h', '24h'),
|
||||
validator = function(index)
|
||||
return function(auction_record)
|
||||
return auction_record.duration == index
|
||||
end
|
||||
end
|
||||
},
|
||||
|
||||
['rarity'] = {
|
||||
input_type = T.list('poor', 'common', 'uncommon', 'rare', 'epic'),
|
||||
validator = function(index)
|
||||
return function(auction_record)
|
||||
return auction_record.quality == index - 1
|
||||
end
|
||||
end
|
||||
},
|
||||
|
||||
['min-level'] = {
|
||||
input_type = 'number',
|
||||
validator = function(level)
|
||||
return function(auction_record)
|
||||
return auction_record.level >= level
|
||||
end
|
||||
end
|
||||
},
|
||||
|
||||
['max-level'] = {
|
||||
input_type = 'number',
|
||||
validator = function(level)
|
||||
return function(auction_record)
|
||||
return auction_record.level <= level
|
||||
end
|
||||
end
|
||||
},
|
||||
|
||||
['bid-price'] = {
|
||||
input_type = 'money',
|
||||
validator = function(amount)
|
||||
return function(auction_record)
|
||||
return auction_record.unit_bid_price <= amount
|
||||
end
|
||||
end
|
||||
},
|
||||
|
||||
['price'] = {
|
||||
input_type = 'money',
|
||||
validator = function(amount)
|
||||
return function(auction_record)
|
||||
return auction_record.buyout_price > 0 and auction_record.unit_buyout_price <= amount
|
||||
end
|
||||
end
|
||||
},
|
||||
|
||||
['bid-percent'] = {
|
||||
input_type = 'number',
|
||||
validator = function(pct)
|
||||
return function(auction_record)
|
||||
return history.value(auction_record.item_key) and auction_record.unit_bid_price / history.value(auction_record.item_key) * 100 <= pct
|
||||
end
|
||||
end
|
||||
},
|
||||
|
||||
['percent'] = {
|
||||
input_type = 'number',
|
||||
validator = function(pct)
|
||||
return function(auction_record)
|
||||
return auction_record.unit_buyout_price > 0
|
||||
and history.value(auction_record.item_key)
|
||||
and auction_record.unit_buyout_price / history.value(auction_record.item_key) * 100 <= pct
|
||||
end
|
||||
end
|
||||
},
|
||||
|
||||
['bid-profit'] = {
|
||||
input_type = 'money',
|
||||
validator = function(amount)
|
||||
return function(auction_record)
|
||||
return history.value(auction_record.item_key) and history.value(auction_record.item_key) * auction_record.aux_quantity - auction_record.bid_price >= amount
|
||||
end
|
||||
end
|
||||
},
|
||||
|
||||
['profit'] = {
|
||||
input_type = 'money',
|
||||
validator = function(amount)
|
||||
return function(auction_record)
|
||||
return auction_record.buyout_price > 0 and history.value(auction_record.item_key) and history.value(auction_record.item_key) * auction_record.aux_quantity - auction_record.buyout_price >= amount
|
||||
end
|
||||
end
|
||||
},
|
||||
|
||||
['bid-disenchant-profit'] = {
|
||||
input_type = 'money',
|
||||
validator = function(amount)
|
||||
return function(auction_record)
|
||||
local disenchant_value = disenchant.value(auction_record.slot, auction_record.quality, auction_record.level)
|
||||
return disenchant_value and disenchant_value - auction_record.bid_price >= amount
|
||||
end
|
||||
end
|
||||
},
|
||||
|
||||
['disenchant-profit'] = {
|
||||
input_type = 'money',
|
||||
validator = function(amount)
|
||||
return function(auction_record)
|
||||
local disenchant_value = disenchant.value(auction_record.slot, auction_record.quality, auction_record.level)
|
||||
return auction_record.buyout_price > 0 and disenchant_value and disenchant_value - auction_record.buyout_price >= amount
|
||||
end
|
||||
end
|
||||
},
|
||||
|
||||
['bid-vendor-profit'] = {
|
||||
input_type = 'money',
|
||||
validator = function(amount)
|
||||
return function(auction_record)
|
||||
local vendor_price = info.merchant_info(auction_record.item_id)
|
||||
return vendor_price and vendor_price * auction_record.aux_quantity - auction_record.bid_price >= amount
|
||||
end
|
||||
end
|
||||
},
|
||||
|
||||
['vendor-profit'] = {
|
||||
input_type = 'money',
|
||||
validator = function(amount)
|
||||
return function(auction_record)
|
||||
local vendor_price = info.merchant_info(auction_record.item_id)
|
||||
return auction_record.buyout_price > 0 and vendor_price and vendor_price * auction_record.aux_quantity - auction_record.buyout_price >= amount
|
||||
end
|
||||
end
|
||||
},
|
||||
|
||||
['seller'] = {
|
||||
input_type = 'string',
|
||||
validator = function(name)
|
||||
return function(auction_record)
|
||||
return auction_record.owner and strupper(name) == strupper(auction_record.owner)
|
||||
end
|
||||
end
|
||||
},
|
||||
}
|
||||
|
||||
function operator(str)
|
||||
local operator = str == 'not' and T.list('operator', 'not', 1)
|
||||
for name in T.temp-T.set('and', 'or') do
|
||||
local arity = aux.select(3, strfind(str, '^' .. name .. '(%d*)$'))
|
||||
if arity then
|
||||
arity = tonumber(arity)
|
||||
operator = not (arity and arity < 2) and T.list('operator', name, arity)
|
||||
end
|
||||
end
|
||||
return operator or nil
|
||||
end
|
||||
|
||||
do
|
||||
local mt = {
|
||||
__call = function(self, str, i)
|
||||
if not str then
|
||||
self.max_level = self.max_level or self.min_level
|
||||
return self
|
||||
end
|
||||
if self.exact then return end
|
||||
local number = tonumber(aux.select(3, strfind(str, '^(%d+)$')))
|
||||
if number then
|
||||
if number >= 1 and number <= 60 then
|
||||
for _, key in ipairs(T.temp-T.list('min_level', 'max_level')) do
|
||||
if not self[key] then
|
||||
self[key] = T.list(str, number)
|
||||
return T.list('blizzard', key, str, number)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
for _, parser in T.temp-T.list(
|
||||
T.temp-T.list('class', info.item_class_index),
|
||||
T.temp-T.list('subclass', T.vararg-function(arg) return info.item_subclass_index(aux.index(self.class, 2) or 0, unpack(arg)) end),
|
||||
T.temp-T.list('slot', T.vararg-function(arg) return info.item_slot_index(aux.index(self.class, 2) == 2 and 2 or 0, aux.index(self.subclass, 2) or 0, unpack(arg)) end),
|
||||
T.temp-T.list('quality', info.item_quality_index)
|
||||
) do
|
||||
if not self[parser[1]] then
|
||||
tinsert(parser, str)
|
||||
local index, label = parser[2](aux.select(3, unpack(parser)))
|
||||
if index then
|
||||
self[parser[1]] = T.list(label, index)
|
||||
return T.list('blizzard', parser[1], label, index)
|
||||
end
|
||||
end
|
||||
end
|
||||
if not self[str] and (str == 'usable' or str == 'exact' and self.name and aux.size(self) == 1) then
|
||||
self[str] = T.list(str, 1)
|
||||
return T.list('blizzard', str, str, 1)
|
||||
elseif i == 1 and strlen(str) <= 63 then
|
||||
self.name = unquote(str)
|
||||
return T.list('blizzard', 'name', unquote(str), str)
|
||||
-- return nil, 'The name filter must not be longer than 63 characters' TODO
|
||||
end
|
||||
end,
|
||||
}
|
||||
function blizzard_filter_parser()
|
||||
return setmetatable(T.acquire(), mt)
|
||||
end
|
||||
end
|
||||
|
||||
function parse_parameter(input_type, str)
|
||||
if input_type == 'money' then
|
||||
local money = money.from_string(str)
|
||||
return money and money > 0 and money or nil
|
||||
elseif input_type == 'number' then
|
||||
local number = tonumber(str)
|
||||
return number and number > 0 and mod(number, 1) == 0 and number or nil
|
||||
elseif input_type == 'string' then
|
||||
return str ~= '' and str or nil
|
||||
elseif type(input_type) == 'table' then
|
||||
return aux.key(input_type, str)
|
||||
end
|
||||
end
|
||||
|
||||
function M.parse_filter_string(str)
|
||||
local filter, post_filter = T.acquire(), T.acquire()
|
||||
local blizzard_filter_parser = blizzard_filter_parser()
|
||||
|
||||
local parts = str and aux.map(aux.split(str, '/'), function(part) return strlower(aux.trim(part)) end) or T.acquire()
|
||||
|
||||
local i = 1
|
||||
while parts[i] do
|
||||
local operator = operator(parts[i])
|
||||
if operator then
|
||||
tinsert(post_filter, operator)
|
||||
tinsert(filter, operator)
|
||||
elseif filters[parts[i]] then
|
||||
local input_type = filters[parts[i]].input_type
|
||||
if input_type ~= '' then
|
||||
if not parts[i + 1] or not parse_parameter(input_type, parts[i + 1]) then
|
||||
if parts[i] == 'item' then
|
||||
return nil, 'Invalid item name', aux.account_data.auctionable_items
|
||||
elseif type(input_type) == 'table' then
|
||||
return nil, 'Invalid choice for ' .. parts[i], input_type
|
||||
else
|
||||
return nil, 'Invalid input for ' .. parts[i] .. '. Expecting: ' .. input_type
|
||||
end
|
||||
end
|
||||
tinsert(post_filter, T.list('filter', parts[i], parts[i + 1]))
|
||||
i = i + 1
|
||||
else
|
||||
tinsert(post_filter, T.list('filter', parts[i]))
|
||||
end
|
||||
tinsert(filter, post_filter[getn(post_filter)])
|
||||
else
|
||||
local part = blizzard_filter_parser(parts[i], i)
|
||||
if part then
|
||||
tinsert(filter, part)
|
||||
elseif parts[i] ~= '' then
|
||||
tinsert(post_filter, T.list('filter', 'tooltip', parts[i]))
|
||||
tinsert(filter, post_filter[getn(post_filter)])
|
||||
else
|
||||
return nil, 'Empty modifier'
|
||||
end
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
return T.map('components', filter, 'blizzard', blizzard_filter_parser(), 'post', post_filter)
|
||||
end
|
||||
|
||||
function M.query(filter_string)
|
||||
local filter, error, suggestions = parse_filter_string(filter_string)
|
||||
|
||||
if not filter then
|
||||
return nil, suggestions or T.acquire(), error
|
||||
end
|
||||
|
||||
local polish_notation_counter = 0
|
||||
for _, component in ipairs(filter.post) do
|
||||
if component[1] == 'operator' then
|
||||
polish_notation_counter = max(polish_notation_counter, 1)
|
||||
polish_notation_counter = polish_notation_counter + (tonumber(component[2]) or 1) - 1
|
||||
elseif component[1] == 'filter' then
|
||||
polish_notation_counter = polish_notation_counter - 1
|
||||
end
|
||||
end
|
||||
|
||||
if polish_notation_counter > 0 then
|
||||
local suggestions = T.acquire()
|
||||
for key in filters do
|
||||
tinsert(suggestions, strlower(key))
|
||||
end
|
||||
tinsert(suggestions, 'and')
|
||||
tinsert(suggestions, 'or')
|
||||
tinsert(suggestions, 'not')
|
||||
return nil, suggestions, 'Malformed expression'
|
||||
end
|
||||
|
||||
return {
|
||||
blizzard_query = blizzard_query(filter),
|
||||
validator = validator(filter),
|
||||
prettified = prettified_filter_string(filter),
|
||||
}, _M.suggestions(filter)
|
||||
end
|
||||
|
||||
function M.queries(filter_string)
|
||||
local parts = aux.split(filter_string, ';')
|
||||
local queries = T.acquire()
|
||||
for _, str in ipairs(parts) do
|
||||
local query, _, error = query(str)
|
||||
if not query then
|
||||
return nil, error
|
||||
else
|
||||
tinsert(queries, query)
|
||||
end
|
||||
end
|
||||
return queries
|
||||
end
|
||||
|
||||
function suggestions(filter)
|
||||
local suggestions = T.acquire()
|
||||
|
||||
if filter.blizzard.name and aux.size(filter.blizzard) == 1 then tinsert(suggestions, 'exact') end
|
||||
|
||||
tinsert(suggestions, 'and'); tinsert(suggestions, 'or'); tinsert(suggestions, 'not'); tinsert(suggestions, 'tooltip')
|
||||
|
||||
for key in filters do tinsert(suggestions, key) end
|
||||
|
||||
-- classes
|
||||
if not filter.blizzard.class then
|
||||
for _, class in ipairs(T.temp-T.list(GetAuctionItemClasses())) do tinsert(suggestions, class) end
|
||||
end
|
||||
|
||||
-- subclasses
|
||||
if not filter.blizzard.subclass then
|
||||
for _, subclass in ipairs(T.temp-T.list(GetAuctionItemSubClasses(aux.index(filter.blizzard.class, 2) or 0))) do
|
||||
tinsert(suggestions, subclass)
|
||||
end
|
||||
end
|
||||
|
||||
-- slots
|
||||
if not filter.blizzard.slot then
|
||||
for _, invtype in ipairs(T.temp-T.list(GetAuctionInvTypes(aux.index(filter.blizzard.class, 2) == 2 and 2 or 0, aux.index(filter.blizzard.subclass, 2) or 0))) do
|
||||
tinsert(suggestions, _G[invtype])
|
||||
end
|
||||
end
|
||||
|
||||
-- usable
|
||||
if not filter.blizzard.usable then tinsert(suggestions, 'usable') end
|
||||
|
||||
-- rarities
|
||||
if not filter.blizzard.quality then
|
||||
for i = 0, 4 do tinsert(suggestions, _G['ITEM_QUALITY' .. i .. '_DESC']) end
|
||||
end
|
||||
|
||||
-- item names
|
||||
if getn(filter.components) == 0 then
|
||||
for _, name in ipairs(aux.account_data.auctionable_items) do
|
||||
tinsert(suggestions, name .. '/exact')
|
||||
end
|
||||
end
|
||||
|
||||
return suggestions
|
||||
end
|
||||
|
||||
function M.filter_string(components)
|
||||
local query_builder = query_builder()
|
||||
for _, component in ipairs(components) do
|
||||
if component[1] == 'blizzard' then
|
||||
query_builder.append(component[4] or component[3])
|
||||
elseif component[1] == 'operator' then
|
||||
query_builder.append(component[2] .. (component[2] ~= 'not' and tonumber(component[3]) or ''))
|
||||
elseif component[1] == 'filter' then
|
||||
query_builder.append(component[2])
|
||||
local parameter = component[3]
|
||||
if parameter then
|
||||
if filter_util.filters[component[2]].input_type == 'money' then
|
||||
parameter = money.to_string(money.from_string(parameter), nil, true, nil, true)
|
||||
end
|
||||
query_builder.append(parameter)
|
||||
end
|
||||
end
|
||||
end
|
||||
return query_builder.get()
|
||||
end
|
||||
|
||||
function prettified_filter_string(filter)
|
||||
local prettified = query_builder()
|
||||
for i, component in ipairs(filter.components) do
|
||||
if component[1] == 'blizzard' then
|
||||
if component[2] == 'name' then
|
||||
if filter.blizzard.exact then
|
||||
prettified.append(info.display_name(info.item_id(component[3])) or aux.color.orange('[' .. component[3] .. ']'))
|
||||
elseif component[3] ~= '' then
|
||||
prettified.append(aux.color.label.enabled(component[3]))
|
||||
end
|
||||
elseif component[2] ~= 'exact' then
|
||||
prettified.append(aux.color.orange(component[3]))
|
||||
end
|
||||
elseif component[1] == 'operator' then
|
||||
prettified.append(aux.color.orange(component[2] .. (component[2] ~= 'not' and tonumber(component[3]) or '')))
|
||||
elseif component[1] == 'filter' then
|
||||
if i == 1 or component[2] ~= 'tooltip' then
|
||||
prettified.append(aux.color.orange(component[2]))
|
||||
end
|
||||
local parameter = component[3]
|
||||
if parameter then
|
||||
if component[2] == 'item' then
|
||||
prettified.append(info.display_name(info.item_id(parameter)) or aux.color.label.enabled('[' .. parameter .. ']'))
|
||||
else
|
||||
if filters[component[2]].input_type == 'money' then
|
||||
prettified.append(money.to_string(money.from_string(parameter), nil, true, aux.color.label.enabled))
|
||||
else
|
||||
prettified.append(aux.color.label.enabled(parameter))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if prettified.get() == '' then
|
||||
return aux.color.orange'<>'
|
||||
else
|
||||
return prettified.get()
|
||||
end
|
||||
end
|
||||
|
||||
function M.quote(name)
|
||||
return '<' .. name .. '>'
|
||||
end
|
||||
|
||||
function M.unquote(name)
|
||||
return aux.select(3, strfind(name, '^<(.*)>$')) or name
|
||||
end
|
||||
|
||||
function blizzard_query(filter)
|
||||
local filters = filter.blizzard
|
||||
local query = T.map('name', filters.name)
|
||||
local item_id = filters.name and info.item_id(filters.name)
|
||||
local item_info = item_id and info.item(item_id)
|
||||
if filters.exact and item_info then
|
||||
local class_index = info.item_class_index(item_info.class)
|
||||
local subclass_index = info.item_subclass_index(class_index or 0, item_info.subclass)
|
||||
local slot_index = info.item_slot_index(class_index or 0, subclass_index or 0, item_info.slot)
|
||||
query.min_level = item_info.level
|
||||
query.max_level = item_info.level
|
||||
query.usable = item_info.usable
|
||||
query.class = class_index
|
||||
query.subclass = subclass_index
|
||||
query.slot = slot_index
|
||||
query.quality = item_info.quality
|
||||
else
|
||||
for key in T.temp-T.set('min_level', 'max_level', 'class', 'subclass', 'slot', 'usable', 'quality') do
|
||||
query[key] = aux.index(filters[key], 2)
|
||||
end
|
||||
end
|
||||
return query
|
||||
end
|
||||
|
||||
function validator(filter)
|
||||
local validators = T.acquire()
|
||||
for i, component in filter.post do
|
||||
local type, name, param = unpack(component)
|
||||
if type == 'filter' then
|
||||
validators[i] = filters[name].validator(parse_parameter(filters[name].input_type, param))
|
||||
end
|
||||
end
|
||||
return function(record)
|
||||
if filter.blizzard.exact and strlower(info.item(record.item_id).name) ~= filter.blizzard.name then
|
||||
return false
|
||||
end
|
||||
local stack = T.temp-T.acquire()
|
||||
for i = getn(filter.post), 1, -1 do
|
||||
local type, name, param = unpack(filter.post[i])
|
||||
if type == 'operator' then
|
||||
local args = T.temp-T.acquire()
|
||||
while (not param or param > 0) and getn(stack) > 0 do
|
||||
tinsert(args, tremove(stack))
|
||||
param = param and param - 1
|
||||
end
|
||||
if name == 'not' then
|
||||
tinsert(stack, not args[1])
|
||||
elseif name == 'and' then
|
||||
tinsert(stack, aux.all(args))
|
||||
elseif name == 'or' then
|
||||
tinsert(stack, aux.any(args))
|
||||
end
|
||||
elseif type == 'filter' then
|
||||
tinsert(stack, not not validators[i](record))
|
||||
end
|
||||
end
|
||||
return aux.all(stack)
|
||||
end
|
||||
end
|
||||
|
||||
function M.query_builder()
|
||||
local filter
|
||||
return T.map(
|
||||
'append', function(part)
|
||||
filter = not filter and part or filter .. '/' .. part
|
||||
end,
|
||||
'get', function()
|
||||
return filter or ''
|
||||
end
|
||||
)
|
||||
end
|
||||
@@ -0,0 +1,443 @@
|
||||
module 'aux.util.info'
|
||||
|
||||
local T = require 'T'
|
||||
local aux = require 'aux'
|
||||
|
||||
CreateFrame('GameTooltip', 'AuxTooltip', nil, 'GameTooltipTemplate')
|
||||
AuxTooltip:SetScript('OnTooltipAddMoney', function()
|
||||
this.money = arg1
|
||||
end)
|
||||
|
||||
do
|
||||
local inventory_index_map = {
|
||||
INVTYPE_AMMO = {0},
|
||||
INVTYPE_HEAD = {1},
|
||||
INVTYPE_NECK = {2},
|
||||
INVTYPE_SHOULDER = {3},
|
||||
INVTYPE_BODY = {4},
|
||||
INVTYPE_CHEST = {5},
|
||||
INVTYPE_ROBE = {5},
|
||||
INVTYPE_WAIST = {6},
|
||||
INVTYPE_LEGS = {7},
|
||||
INVTYPE_FEET = {8},
|
||||
INVTYPE_WRIST = {9},
|
||||
INVTYPE_HAND = {10},
|
||||
INVTYPE_FINGER = {11, 12},
|
||||
INVTYPE_TRINKET = {13, 14},
|
||||
INVTYPE_CLOAK = {15},
|
||||
INVTYPE_2HWEAPON = {16, 17},
|
||||
INVTYPE_WEAPONMAINHAND = {16, 17},
|
||||
INVTYPE_WEAPON = {16, 17},
|
||||
INVTYPE_WEAPONOFFHAND = {16, 17},
|
||||
INVTYPE_HOLDABLE = {16, 17},
|
||||
INVTYPE_SHIELD = {16, 17},
|
||||
INVTYPE_RANGED = {18},
|
||||
INVTYPE_RANGEDRIGHT = {18},
|
||||
INVTYPE_TABARD = {19},
|
||||
}
|
||||
function M.inventory_index(slot)
|
||||
return unpack(inventory_index_map[slot] or T.temp-T.acquire())
|
||||
end
|
||||
end
|
||||
|
||||
function M.container_item(bag, slot)
|
||||
local link = GetContainerItemLink(bag, slot)
|
||||
if link then
|
||||
local item_id, suffix_id, unique_id, enchant_id = parse_link(link)
|
||||
local item_info = T.temp-item(item_id, suffix_id, unique_id, enchant_id)
|
||||
|
||||
local texture, count, locked, quality, readable, lootable = GetContainerItemInfo(bag, slot) -- quality not working?
|
||||
local tooltip, tooltip_money = tooltip('bag', bag, slot)
|
||||
local max_charges = max_item_charges(item_id)
|
||||
local charges = max_charges and item_charges(tooltip)
|
||||
local aux_quantity = charges or count
|
||||
return T.map(
|
||||
'item_id', item_id,
|
||||
'suffix_id', suffix_id,
|
||||
'unique_id', unique_id,
|
||||
'enchant_id', enchant_id,
|
||||
|
||||
'link', link,
|
||||
'itemstring', item_info.itemstring,
|
||||
'item_key', item_id .. ':' .. suffix_id,
|
||||
|
||||
'name', item_info.name,
|
||||
'texture', texture,
|
||||
'level', item_info.level,
|
||||
'type', item_info.type,
|
||||
'subtype', item_info.subtype,
|
||||
'slot', item_info.slot,
|
||||
'quality', item_info.quality,
|
||||
'max_stack', item_info.max_stack,
|
||||
|
||||
'count', count,
|
||||
'locked', locked,
|
||||
'readable', readable,
|
||||
'lootable', lootable,
|
||||
|
||||
'tooltip', tooltip,
|
||||
'tooltip_money', tooltip_money,
|
||||
'max_charges', max_charges,
|
||||
'charges', charges,
|
||||
'aux_quantity', aux_quantity
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
function M.auction_sell_item()
|
||||
for name, texture, count, quality, usable, vendor_price in GetAuctionSellItemInfo do
|
||||
return T.map(
|
||||
'name', name,
|
||||
'texture', texture,
|
||||
'quality', quality,
|
||||
'count', count,
|
||||
'usable', usable,
|
||||
'vendor_price', vendor_price -- it seems for charge items this is always the price for full charges
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
function M.auction(index, query_type)
|
||||
query_type = query_type or 'list'
|
||||
|
||||
local link = GetAuctionItemLink(query_type, index)
|
||||
if link then
|
||||
local item_id, suffix_id, unique_id, enchant_id = parse_link(link)
|
||||
local item_info = T.temp-item(item_id, suffix_id, unique_id, enchant_id)
|
||||
|
||||
local name, texture, count, quality, usable, level, start_price, min_increment, buyout_price, high_bid, high_bidder, owner, sale_status = GetAuctionItemInfo(query_type, index)
|
||||
|
||||
local duration = GetAuctionItemTimeLeft(query_type, index)
|
||||
local tooltip, tooltip_money = tooltip('auction', query_type, index)
|
||||
local max_charges = max_item_charges(item_id)
|
||||
local charges = max_charges and item_charges(tooltip)
|
||||
local aux_quantity = charges or count
|
||||
local blizzard_bid = high_bid > 0 and high_bid or start_price
|
||||
local bid_price = high_bid > 0 and (high_bid + min_increment) or start_price
|
||||
|
||||
return T.map(
|
||||
'item_id', item_id,
|
||||
'suffix_id', suffix_id,
|
||||
'unique_id', unique_id,
|
||||
'enchant_id', enchant_id,
|
||||
|
||||
'link', link,
|
||||
'itemstring', item_info.itemstring,
|
||||
'item_key', item_id .. ':' .. suffix_id,
|
||||
'search_signature', aux.join(T.temp-T.list(item_id, suffix_id, enchant_id, start_price, buyout_price, bid_price, aux_quantity, duration, query_type == 'owner' and high_bidder or (high_bidder and 1 or 0), aux.account_data.ignore_owner and (is_player(owner) and 0 or 1) or (owner or '?')), ':'),
|
||||
'sniping_signature', aux.join(T.temp-T.list(item_id, suffix_id, enchant_id, start_price, buyout_price, aux_quantity, aux.account_data.ignore_owner and (is_player(owner) and 0 or 1) or (owner or '?')), ':'),
|
||||
|
||||
'name', name,
|
||||
'texture', texture,
|
||||
'level', item_info.level,
|
||||
'type', item_info.type,
|
||||
'subtype', item_info.subtype,
|
||||
'slot', item_info.slot,
|
||||
'quality', quality,
|
||||
'max_stack', item_info.max_stack,
|
||||
|
||||
'count', count,
|
||||
'start_price', start_price,
|
||||
'high_bid', high_bid,
|
||||
'min_increment', min_increment,
|
||||
'blizzard_bid', blizzard_bid,
|
||||
'bid_price', bid_price,
|
||||
'buyout_price', buyout_price,
|
||||
'unit_blizzard_bid', blizzard_bid / aux_quantity,
|
||||
'unit_bid_price', bid_price / aux_quantity,
|
||||
'unit_buyout_price', buyout_price / aux_quantity,
|
||||
'high_bidder', high_bidder,
|
||||
'owner', owner,
|
||||
'sale_status', sale_status,
|
||||
'duration', duration,
|
||||
'usable', usable,
|
||||
|
||||
'tooltip', tooltip,
|
||||
'tooltip_money', tooltip_money,
|
||||
'max_charges', max_charges,
|
||||
'charges', charges,
|
||||
'aux_quantity', aux_quantity
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
function M.bid_update(auction_record)
|
||||
auction_record.high_bid = auction_record.bid_price
|
||||
auction_record.blizzard_bid = auction_record.bid_price
|
||||
auction_record.min_increment = max(1, floor(auction_record.bid_price / 100) * 5)
|
||||
auction_record.bid_price = auction_record.bid_price + auction_record.min_increment
|
||||
auction_record.unit_blizzard_bid = auction_record.blizzard_bid / auction_record.aux_quantity
|
||||
auction_record.unit_bid_price = auction_record.bid_price / auction_record.aux_quantity
|
||||
auction_record.high_bidder = 1
|
||||
auction_record.search_signature = aux.join(T.temp-T.list(auction_record.item_id, auction_record.suffix_id, auction_record.enchant_id, auction_record.start_price, auction_record.buyout_price, auction_record.bid_price, auction_record.aux_quantity, auction_record.duration, 1, aux.account_data.ignore_owner and (is_player(auction_record.owner) and 0 or 1) or (auction_record.owner or '?')), ':')
|
||||
end
|
||||
|
||||
function M.set_tooltip(itemstring, owner, anchor)
|
||||
GameTooltip:SetOwner(owner, anchor)
|
||||
GameTooltip:SetHyperlink(itemstring)
|
||||
end
|
||||
|
||||
function M.set_shopping_tooltip(slot)
|
||||
local index1, index2 = inventory_index(slot)
|
||||
local tooltips = T.temp-T.acquire()
|
||||
if index1 then
|
||||
local tooltip = tooltip('inventory', 'player', index1)
|
||||
if getn(tooltip) > 0 then
|
||||
tinsert(tooltips, tooltip)
|
||||
end
|
||||
end
|
||||
if index2 then
|
||||
local tooltip = tooltip('inventory', 'player', index2)
|
||||
if getn(tooltip) > 0 then
|
||||
tinsert(tooltips, tooltip)
|
||||
end
|
||||
end
|
||||
|
||||
if tooltips[1] then
|
||||
tinsert(tooltips[1], 1, T.temp-T.map('left_text', 'Currently Equipped', 'left_color', T.temp-T.list(.5, .5, .5)))
|
||||
ShoppingTooltip1:SetOwner(GameTooltip, 'ANCHOR_NONE')
|
||||
ShoppingTooltip1:SetPoint('TOPLEFT', GameTooltip, 'TOPRIGHT', 0, -10)
|
||||
load_tooltip(ShoppingTooltip1, tooltips[1])
|
||||
end
|
||||
|
||||
if tooltips[2] then
|
||||
tinsert(tooltips[2], 1, T.temp-T.map('left_text', 'Currently Equipped', 'left_color', T.temp-T.list(.5, .5, .5)))
|
||||
ShoppingTooltip2:SetOwner(ShoppingTooltip1, 'ANCHOR_NONE')
|
||||
ShoppingTooltip2:SetPoint('TOPLEFT', ShoppingTooltip1, 'TOPRIGHT')
|
||||
load_tooltip(ShoppingTooltip2, tooltips[2])
|
||||
end
|
||||
end
|
||||
|
||||
function M.tooltip_match(entry, tooltip)
|
||||
return aux.any(tooltip, function(line)
|
||||
local left_match = line.left_text and strupper(line.left_text) == strupper(entry)
|
||||
local right_match = line.right_text and strupper(line.right_text) == strupper(entry)
|
||||
return left_match or right_match
|
||||
end)
|
||||
end
|
||||
|
||||
function M.tooltip_find(pattern, tooltip)
|
||||
local count = 0
|
||||
for _, line in tooltip do
|
||||
if line.left_text and strfind(line.left_text, pattern) then
|
||||
count = count + 1
|
||||
end
|
||||
if line.right_text and strfind(line.right_text, pattern) then
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
function M.load_tooltip(frame, tooltip)
|
||||
frame:ClearLines()
|
||||
for _, line in ipairs(tooltip) do
|
||||
if line.right_text then
|
||||
frame:AddDoubleLine(line.left_text, line.right_text, line.left_color[1], line.left_color[2], line.left_color[3], line.right_color[1], line.right_color[2], line.right_color[3])
|
||||
else
|
||||
frame:AddLine(line.left_text, line.left_color[1], line.left_color[2], line.left_color[3], true)
|
||||
end
|
||||
end
|
||||
for i = 1, getn(tooltip) do -- TODO why is this needed?
|
||||
_G[frame:GetName() .. 'TextLeft' .. i]:SetJustifyH('LEFT')
|
||||
_G[frame:GetName() .. 'TextRight' .. i]:SetJustifyH('LEFT')
|
||||
end
|
||||
frame:Show()
|
||||
end
|
||||
|
||||
function M.display_name(item_id, no_brackets, no_color)
|
||||
local item_info = item(item_id)
|
||||
if item_info then
|
||||
local name = item_info.name
|
||||
if not no_brackets then
|
||||
name = '[' .. name .. ']'
|
||||
end
|
||||
if not no_color then
|
||||
name = aux.select(4, GetItemQualityColor(item_info.quality)) .. name .. FONT_COLOR_CODE_CLOSE
|
||||
end
|
||||
return name
|
||||
end
|
||||
end
|
||||
|
||||
function M.auctionable(tooltip, quality, strict)
|
||||
local status = tooltip[2] and tooltip[2].left_text
|
||||
local durability, max_durability = durability(tooltip)
|
||||
return (not quality or quality < 6)
|
||||
and status ~= ITEM_BIND_ON_PICKUP
|
||||
and status ~= ITEM_BIND_QUEST
|
||||
and status ~= ITEM_SOULBOUND
|
||||
and (not tooltip_match(ITEM_CONJURED, tooltip) or tooltip_find(ITEM_MIN_LEVEL, tooltip) > 1)
|
||||
and not (strict and durability and durability < max_durability)
|
||||
end
|
||||
|
||||
function M.tooltip(setter, arg1, arg2)
|
||||
AuxTooltip:SetOwner(UIParent, 'ANCHOR_NONE')
|
||||
AuxTooltip.money = 0
|
||||
if setter == 'auction' then
|
||||
AuxTooltip:SetAuctionItem(arg1, arg2)
|
||||
elseif setter == 'bag' then
|
||||
AuxTooltip:SetBagItem(arg1, arg2)
|
||||
elseif setter == 'inventory' then
|
||||
AuxTooltip:SetInventoryItem(arg1, arg2)
|
||||
elseif setter == 'link' then
|
||||
AuxTooltip:SetHyperlink(arg1)
|
||||
end
|
||||
local tooltip = T.acquire()
|
||||
for i = 1, AuxTooltip:NumLines() do
|
||||
tinsert(tooltip, T.map(
|
||||
'left_text', _G['AuxTooltipTextLeft' .. i]:GetText(),
|
||||
'left_color', T.list(_G['AuxTooltipTextLeft' .. i]:GetTextColor()),
|
||||
'right_text', _G['AuxTooltipTextRight' .. i]:IsVisible() and _G['AuxTooltipTextRight' .. i]:GetText(),
|
||||
'right_color', T.list(_G['AuxTooltipTextRight' .. i]:GetTextColor())
|
||||
))
|
||||
end
|
||||
return tooltip, AuxTooltip.money
|
||||
end
|
||||
|
||||
do
|
||||
local pattern = '^' .. gsub(gsub(ITEM_SPELL_CHARGES_P1, '%%d', '(%%d+)'), '%%%d+%$d', '(%%d+)') .. '$'
|
||||
function item_charges(tooltip)
|
||||
for _, line in tooltip do
|
||||
local _, _, left_charges_string = strfind(line.left_text or '', pattern)
|
||||
local _, _, right_charges_string = strfind(line.right_text or '', pattern)
|
||||
|
||||
local charges = tonumber(left_charges_string) or tonumber(right_charges_string)
|
||||
if charges then
|
||||
return max(1, charges) -- TODO kronos bug? should never be 0
|
||||
end
|
||||
end
|
||||
return 1
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local data = {
|
||||
-- wizard oil
|
||||
[20744] = 5,
|
||||
[20746] = 5,
|
||||
[20750] = 5,
|
||||
[20749] = 5,
|
||||
|
||||
-- mana oil
|
||||
[20745] = 5,
|
||||
[20747] = 5,
|
||||
[20748] = 5,
|
||||
|
||||
-- discombobulator
|
||||
[4388] = 5,
|
||||
|
||||
-- recombobulator
|
||||
[4381] = 10,
|
||||
[18637] = 10,
|
||||
|
||||
-- deflector
|
||||
[4376] = 5,
|
||||
[4386] = 5,
|
||||
|
||||
-- ... TODO
|
||||
}
|
||||
function M.max_item_charges(item_id)
|
||||
return data[item_id]
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local pattern = '^' .. gsub(gsub(DURABILITY_TEMPLATE, '%%d', '(%%d+)'), '%%%d+%$d', '(%%d+)') .. '$'
|
||||
function M.durability(tooltip)
|
||||
for _, line in tooltip do
|
||||
local _, _, left_durability_string, left_max_durability_string = strfind(line.left_text or '', pattern)
|
||||
local _, _, right_durability_string, right_max_durability_string = strfind(line.right_text or '', pattern)
|
||||
local durability = tonumber(left_durability_string) or tonumber(right_durability_string)
|
||||
local max_durability = tonumber(left_max_durability_string) or tonumber(right_max_durability_string)
|
||||
if durability then
|
||||
return durability, max_durability
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function M.item_key(link)
|
||||
local item_id, suffix_id = parse_link(link)
|
||||
return item_id .. ':' .. suffix_id
|
||||
end
|
||||
|
||||
function M.parse_link(link)
|
||||
local _, _, item_id, enchant_id, suffix_id, unique_id, name = strfind(link, '|c%x%x%x%x%x%x%x%x|Hitem:(%d*):(%d*):(%d*):(%d*)[:0-9]*|h%[(.-)%]|h|r')
|
||||
return tonumber(item_id) or 0, tonumber(suffix_id) or 0, tonumber(unique_id) or 0, tonumber(enchant_id) or 0, name
|
||||
end
|
||||
|
||||
function M.itemstring(item_id, suffix_id, unique_id, enchant_id)
|
||||
return 'item:' .. (item_id or 0) .. ':' .. (enchant_id or 0) .. ':' .. (suffix_id or 0) .. ':' .. (unique_id or 0)
|
||||
end
|
||||
|
||||
function M.item(item_id, suffix_id)
|
||||
local itemstring = 'item:' .. (item_id or 0) .. ':0:' .. (suffix_id or 0) .. ':0'
|
||||
local name, itemstring, quality, level, class, subclass, max_stack, slot, texture = GetItemInfo(itemstring)
|
||||
return name and T.map(
|
||||
'name', name,
|
||||
'itemstring', itemstring,
|
||||
'quality', quality,
|
||||
'level', level,
|
||||
'class', class,
|
||||
'subclass', subclass,
|
||||
'slot', slot,
|
||||
'max_stack', max_stack,
|
||||
'texture', texture
|
||||
) or item_info(item_id)
|
||||
end
|
||||
|
||||
function M.item_class_index(item_class)
|
||||
for i, class in T.temp-T.list(GetAuctionItemClasses()) do
|
||||
if strupper(class) == strupper(item_class) then
|
||||
return i, class
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function M.item_subclass_index(class_index, item_subclass)
|
||||
for i, subclass in T.temp-T.list(GetAuctionItemSubClasses(class_index)) do
|
||||
if strupper(subclass) == strupper(item_subclass) then
|
||||
return i, subclass
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function M.item_slot_index(class_index, subclass_index, slot_name)
|
||||
for i, slot in T.temp-T.list(GetAuctionInvTypes(class_index, subclass_index)) do
|
||||
if strupper(_G[slot]) == strupper(slot_name) then
|
||||
return i, _G[slot]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function M.item_quality_index(item_quality)
|
||||
for i = 0, 4 do
|
||||
local quality = _G['ITEM_QUALITY' .. i .. '_DESC']
|
||||
if strupper(item_quality) == strupper(quality) then
|
||||
return i, quality
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function M.inventory()
|
||||
local bag, slot = 0, 0
|
||||
return function()
|
||||
if not GetBagName(bag) or slot >= GetContainerNumSlots(bag) then
|
||||
repeat bag = bag + 1 until GetBagName(bag) or bag > 4
|
||||
slot = 1
|
||||
else
|
||||
slot = slot + 1
|
||||
end
|
||||
if bag <= 4 then return T.list(bag, slot), bag_type(bag) end
|
||||
end
|
||||
end
|
||||
|
||||
function M.bag_type(bag)
|
||||
if bag == 0 then return 1 end
|
||||
local link = GetInventoryItemLink('player', ContainerIDToInventoryID(bag))
|
||||
if link then
|
||||
local item_id = parse_link(link)
|
||||
local item_info = item(item_id)
|
||||
return item_subclass_index(3, item_info.subclass)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,131 @@
|
||||
module 'aux.util.money'
|
||||
|
||||
local T = require 'T'
|
||||
local aux = require 'aux'
|
||||
|
||||
M.GOLD_TEXT = '|cffffd70ag|r'
|
||||
M.SILVER_TEXT = '|cffc7c7cfs|r'
|
||||
M.COPPER_TEXT = '|cffeda55fc|r'
|
||||
|
||||
local COPPER_PER_GOLD = 10000
|
||||
local COPPER_PER_SILVER = 100
|
||||
|
||||
function M.to_gsc(money)
|
||||
local gold = floor(money / COPPER_PER_GOLD)
|
||||
local silver = floor(mod(money, COPPER_PER_GOLD) / COPPER_PER_SILVER)
|
||||
local copper = mod(money, COPPER_PER_SILVER)
|
||||
return gold, silver, copper
|
||||
end
|
||||
|
||||
function M.from_gsc(gold, silver, copper)
|
||||
return gold * COPPER_PER_GOLD + silver * COPPER_PER_SILVER + copper
|
||||
end
|
||||
|
||||
function M.to_string2(money, exact, color)
|
||||
color = color or FONT_COLOR_CODE_CLOSE
|
||||
|
||||
local TEXT_NONE = '0'
|
||||
|
||||
local GOLD = 'ffd100'
|
||||
local SILVER = 'e6e6e6'
|
||||
local COPPER = 'c8602c'
|
||||
local START = '|cff%s%d' .. FONT_COLOR_CODE_CLOSE
|
||||
local PART = color .. '.|cff%s%02d' .. FONT_COLOR_CODE_CLOSE
|
||||
local NONE = '|cffa0a0a0' .. TEXT_NONE .. FONT_COLOR_CODE_CLOSE
|
||||
|
||||
if not exact and money >= COPPER_PER_GOLD then
|
||||
money = aux.round(money / COPPER_PER_SILVER) * COPPER_PER_SILVER
|
||||
end
|
||||
local g, s, c = to_gsc(money)
|
||||
|
||||
local str = ''
|
||||
|
||||
local fmt = START
|
||||
if g > 0 then
|
||||
str = str .. format(fmt, GOLD, g)
|
||||
fmt = PART
|
||||
end
|
||||
if s > 0 or c > 0 then
|
||||
str = str .. format(fmt, SILVER, s)
|
||||
fmt = PART
|
||||
end
|
||||
if c > 0 then
|
||||
str = str .. format(fmt, COPPER, c)
|
||||
end
|
||||
if str == '' then
|
||||
str = NONE
|
||||
end
|
||||
return str
|
||||
end
|
||||
|
||||
function M.to_string(money, pad, trim, color, no_color)
|
||||
local is_negative = money < 0
|
||||
money = abs(money)
|
||||
local gold, silver, copper = to_gsc(money)
|
||||
|
||||
local gold_text, silver_text, copper_text
|
||||
if no_color then
|
||||
gold_text, silver_text, copper_text = 'g', 's', 'c'
|
||||
else
|
||||
gold_text, silver_text, copper_text = GOLD_TEXT, SILVER_TEXT, COPPER_TEXT
|
||||
end
|
||||
|
||||
local text
|
||||
if trim then
|
||||
local parts = T.temp-T.acquire()
|
||||
if gold > 0 then
|
||||
tinsert(parts, format_number(gold, false, color) .. gold_text)
|
||||
end
|
||||
if silver > 0 then
|
||||
tinsert(parts, format_number(silver, pad, color) .. silver_text)
|
||||
end
|
||||
if copper > 0 or gold == 0 and silver == 0 then
|
||||
tinsert(parts, format_number(copper, pad, color) .. copper_text)
|
||||
end
|
||||
text = aux.join(parts, ' ')
|
||||
else
|
||||
if gold > 0 then
|
||||
text = format_number(gold, false, color) .. gold_text .. ' ' .. format_number(silver, pad, color) .. silver_text .. ' ' .. format_number(copper, pad, color) .. copper_text
|
||||
elseif silver > 0 then
|
||||
text = format_number(silver, false, color) .. silver_text .. ' ' .. format_number(copper, pad, color) .. copper_text
|
||||
else
|
||||
text = format_number(copper, false, color) .. copper_text
|
||||
end
|
||||
end
|
||||
|
||||
if is_negative then
|
||||
text = (color and color'-' or '-') .. text
|
||||
end
|
||||
|
||||
return text
|
||||
end
|
||||
|
||||
function M.from_string(value)
|
||||
local number = tonumber(value)
|
||||
if number and number >= 0 then
|
||||
return number * COPPER_PER_GOLD
|
||||
end
|
||||
|
||||
value = gsub(gsub(strlower(value), '|c%x%x%x%x%x%x%x%x', ''), FONT_COLOR_CODE_CLOSE, '')
|
||||
|
||||
local gold = tonumber(aux.select(3, strfind(value, '(%d*%.?%d+)g')))
|
||||
local silver = tonumber(aux.select(3, strfind(value, '(%d*%.?%d+)s')))
|
||||
local copper = tonumber(aux.select(3, strfind(value, '(%d*%.?%d+)c')))
|
||||
if not gold and not silver and not copper then return end
|
||||
|
||||
value = gsub(value, '%d*%.?%d+g', '', 1)
|
||||
value = gsub(value, '%d*%.?%d+s', '', 1)
|
||||
value = gsub(value, '%d*%.?%d+c', '', 1)
|
||||
if strfind(value, '%S') then return end
|
||||
|
||||
return from_gsc(gold or 0, silver or 0, copper or 0)
|
||||
end
|
||||
|
||||
function M.format_number(num, pad, color)
|
||||
num = format('%0' .. (pad and 2 or 0) .. 'd', num)
|
||||
if color then
|
||||
return color .. num .. FONT_COLOR_CODE_CLOSE
|
||||
else
|
||||
return num
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,75 @@
|
||||
module 'aux.util.persistence'
|
||||
|
||||
local T = require 'T'
|
||||
local aux = require 'aux'
|
||||
|
||||
function M.read(schema, str)
|
||||
if schema == 'string' then
|
||||
return str
|
||||
elseif schema == 'boolean' then
|
||||
return str == '1'
|
||||
elseif schema == 'number' then
|
||||
return tonumber(str)
|
||||
elseif type(schema) == 'table' and schema[1] == 'list' then
|
||||
return T.temp-read_list(schema, str)
|
||||
elseif type(schema) == 'table' and schema[1] == 'tuple' then
|
||||
return T.temp-read_tuple(schema, str)
|
||||
else
|
||||
error('Invalid schema.', 2)
|
||||
end
|
||||
end
|
||||
|
||||
function M.write(schema, obj)
|
||||
if schema == 'string' then
|
||||
return obj or ''
|
||||
elseif schema == 'boolean' then
|
||||
return obj and '1' or '0'
|
||||
elseif schema == 'number' then
|
||||
return obj and tostring(obj) or ''
|
||||
elseif type(schema) == 'table' and schema[1] == 'list' then
|
||||
return write_list(schema, obj)
|
||||
elseif type(schema) == 'table' and schema[1] == 'tuple' then
|
||||
return write_tuple(schema, obj)
|
||||
else
|
||||
error('Invalid schema.', 2)
|
||||
end
|
||||
end
|
||||
|
||||
function read_list(schema, str)
|
||||
if str == '' then return T.acquire() end
|
||||
local separator = schema[2]
|
||||
local element_type = schema[3]
|
||||
return aux.map(aux.split(str, separator), function(part)
|
||||
return read(element_type, part)
|
||||
end)
|
||||
end
|
||||
|
||||
function write_list(schema, list)
|
||||
local separator = schema[2]
|
||||
local element_type = schema[3]
|
||||
local parts = aux.map(T.temp-aux.copy(list), function(element)
|
||||
return write(element_type, element)
|
||||
end)
|
||||
return aux.join(parts, separator)
|
||||
end
|
||||
|
||||
function read_tuple(schema, str)
|
||||
local separator = schema[2]
|
||||
local tuple = T.acquire()
|
||||
local parts = T.temp-aux.split(str, separator)
|
||||
for i = 3, getn(schema) do
|
||||
local key, type = next(schema[i])
|
||||
tuple[key] = read(type, parts[i - 2])
|
||||
end
|
||||
return tuple
|
||||
end
|
||||
|
||||
function write_tuple(schema, tuple)
|
||||
local separator = schema[2]
|
||||
local parts = T.temp-T.acquire()
|
||||
for i = 3, getn(schema) do
|
||||
local key, type = next(schema[i])
|
||||
tinsert(parts, write(type, tuple[key]))
|
||||
end
|
||||
return aux.join(parts, separator)
|
||||
end
|
||||
@@ -0,0 +1,82 @@
|
||||
module 'aux.util.scan'
|
||||
|
||||
local T = require 'T'
|
||||
local aux = require 'aux'
|
||||
local info = require 'aux.util.info'
|
||||
local filter_util = require 'aux.util.filter'
|
||||
local scan = require 'aux.core.scan'
|
||||
|
||||
function M.test(record, index)
|
||||
local auction_record = T.temp-info.auction(index, record.query_type)
|
||||
return auction_record and auction_record.search_signature == record.search_signature
|
||||
end
|
||||
|
||||
function M.find(auction_record, status_bar, on_abort, on_failure, on_success)
|
||||
|
||||
local queries = T.list(T.acquire())
|
||||
|
||||
if auction_record.blizzard_query then
|
||||
local blizzard_query1 = aux.copy(auction_record.blizzard_query)
|
||||
blizzard_query1.first_page = auction_record.page
|
||||
blizzard_query1.last_page = auction_record.page
|
||||
tinsert(queries, T.map('blizzard_query', blizzard_query1))
|
||||
|
||||
if auction_record.page > 0 then
|
||||
local blizzard_query2 = aux.copy(auction_record.blizzard_query)
|
||||
blizzard_query2.first_page = auction_record.page - 1
|
||||
blizzard_query2.last_page = auction_record.page - 1
|
||||
tinsert(queries, T.map('blizzard_query', blizzard_query2))
|
||||
end
|
||||
|
||||
local item_query = item_query(auction_record.item_id, 0, 0)
|
||||
if not aux.eq(auction_record.blizzard_query, item_query.blizzard_query) then
|
||||
tinsert(queries, item_query)
|
||||
end
|
||||
end
|
||||
|
||||
local found
|
||||
return scan.start{
|
||||
type = auction_record.query_type,
|
||||
queries = queries,
|
||||
on_scan_start = function()
|
||||
status_bar:update_status(0, 0)
|
||||
status_bar:set_text('Searching auction...')
|
||||
end,
|
||||
on_start_query = function(query_index)
|
||||
status_bar:update_status((query_index - 1) / getn(queries), 0)
|
||||
end,
|
||||
on_auction = function(record)
|
||||
if test(auction_record, record.index) then
|
||||
found = true
|
||||
scan.stop()
|
||||
status_bar:update_status(1, 1)
|
||||
status_bar:set_text('Auction found')
|
||||
return on_success(record.index)
|
||||
end
|
||||
end,
|
||||
on_abort = function()
|
||||
if not found then
|
||||
status_bar:update_status(1, 1)
|
||||
status_bar:set_text('Auction not found')
|
||||
return on_abort()
|
||||
end
|
||||
end,
|
||||
on_complete = function()
|
||||
if not found then
|
||||
status_bar:update_status(1, 1)
|
||||
status_bar:set_text('Auction not found')
|
||||
return on_failure()
|
||||
end
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
function M.item_query(item_id, first_page, last_page)
|
||||
local item_info = info.item(item_id)
|
||||
if item_info then
|
||||
local query = filter_util.query(item_info.name .. '/exact')
|
||||
query.blizzard_query.first_page = first_page
|
||||
query.blizzard_query.last_page = last_page
|
||||
return T.map('validator', query.validator, 'blizzard_query', query.blizzard_query)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,30 @@
|
||||
module 'aux.util.sort'
|
||||
|
||||
local T = require 'T'
|
||||
|
||||
M.EQ = 0
|
||||
M.LT = 1
|
||||
M.GT = 2
|
||||
|
||||
function M.compare(a, b, desc)
|
||||
if a < b then
|
||||
return desc and GT or LT
|
||||
elseif a > b then
|
||||
return desc and LT or GT
|
||||
else
|
||||
return EQ
|
||||
end
|
||||
end
|
||||
|
||||
M.multi_lt = T.vararg-function(arg)
|
||||
for i = 1, getn(arg), 2 do
|
||||
if arg[i] and arg[i + 1] and arg[i] ~= arg[i + 1] then
|
||||
return arg[i] < arg[i + 1]
|
||||
elseif not arg[i] and arg[i + 1] then
|
||||
return true
|
||||
elseif not arg[i + 1] then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
Reference in New Issue
Block a user