restructuring

This commit is contained in:
Manuel Simon Hirsig
2016-07-12 09:01:34 +02:00
parent d56476ebe1
commit 62ef563afe
33 changed files with 38 additions and 32 deletions
+42
View File
@@ -0,0 +1,42 @@
local m = {}
Aux.completion = m
function m:complete()
if IsControlKeyDown() then -- TODO problem is ctrl-v, maybe find a better solution
return
end
local filter_string = this:GetText()
local completed_filter_string = ({strfind(filter_string, '([^;]*)/[^/;]*$')})[3]
local _, suggestions = Aux.scan_util.filter_from_string(completed_filter_string or '')
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.completor(options)
return function(self)
if IsControlKeyDown() then -- TODO problem is ctrl-v, maybe find a better solution
return
end
local text = self:GetText()
for _, item_name in ipairs(options()) do
if strsub(strupper(item_name), 1, strlen(text)) == strupper(text) then
self:SetText(strlower(item_name))
self:HighlightText(strlen(text), -1)
return
end
end
end
end
+8
View File
@@ -0,0 +1,8 @@
<Ui xsi:schemaLocation="http://www.blizzard.com/wow/ui/">
<Script file="completion.lua"/>
<Frame name="AuxCompletionDropDown" inherits="UIDropDownMenuTemplate" hidden="true">
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="555" y="0"/></Offset></Anchor></Anchors>
</Frame>
</Ui>
+406
View File
@@ -0,0 +1,406 @@
local TOOLTIP_LENGTH = 30
local private, public = {}, {}
Aux.info = public
function public.inventory_index(slot)
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},
}
return unpack(inventory_index_map[slot] or {})
end
function public.container_item(bag, slot)
local hyperlink = GetContainerItemLink(bag, slot)
if not hyperlink then
return
end
local item_id, suffix_id, unique_id, enchant_id = public.parse_hyperlink(hyperlink)
local item_info = public.item(item_id, suffix_id, unique_id, enchant_id)
local texture, count, locked, quality, readable, lootable = GetContainerItemInfo(bag, slot) -- quality not working?
local tooltip = public.tooltip(function(tt) tt:SetBagItem(bag, slot) end)
local max_charges = private.max_item_charges(item_id)
local charges = max_charges and private.item_charges(tooltip)
local aux_quantity = charges or count
return {
item_id = item_id,
suffix_id = suffix_id,
unique_id = unique_id,
enchant_id = enchant_id,
hyperlink = hyperlink,
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,
max_charges = max_charges,
charges = charges,
aux_quantity = aux_quantity,
}
end
function public.auction_sell_item()
local name, texture, count, quality, usable, vendor_price = GetAuctionSellItemInfo()
if name then
local unit_vendor_price = vendor_price / count
local tooltip = public.tooltip(function(tt) tt:SetAuctionSellItem() end)
local charges = private.item_charges(tooltip)
local aux_quantity = charges > 1 and charges or count
return {
name = name,
texture = texture,
quality = quality,
stack_size = count,
usable = usable,
vendor_price = vendor_price,
unit_vendor_price = unit_vendor_price,
tooltip = tooltip,
aux_quantity = aux_quantity,
}
end
end
function public.auction(index, query_type)
query_type = query_type or 'list'
local hyperlink = GetAuctionItemLink(query_type, index)
if not hyperlink then
return
end
local item_id, suffix_id, unique_id, enchant_id = public.parse_hyperlink(hyperlink)
local item_info = public.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 = public.tooltip(function(tt) tt:SetAuctionItem(query_type, index) end)
local max_charges = private.max_item_charges(item_id)
local charges = max_charges and private.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 {
item_id = item_id,
suffix_id = suffix_id,
unique_id = unique_id,
enchant_id = enchant_id,
hyperlink = hyperlink,
itemstring = item_info.itemstring,
item_key = item_id..':'..suffix_id,
search_signature = Aux.util.join({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_ignore_owner and (Aux.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,
max_charges = max_charges,
charges = charges,
aux_quantity = aux_quantity,
}
end
function public.bid_update(auction_record)
auction_record.high_bid = auction_record.bid_price
auction_record.blizzard_bid = auction_record.bid_price
auction_record.min_increment = Aux.min_bid_increment(auction_record.bid_price)
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.util.join({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_ignore_owner and (Aux.is_player(auction_record.owner) and 0 or 1) or (auction_record.owner or '?')}, ':')
end
function public.set_tooltip(itemstring, owner, anchor)
GameTooltip:SetOwner(owner, anchor)
GameTooltip:SetHyperlink(itemstring)
end
function public.set_shopping_tooltip(slot)
local index1, index2 = public.inventory_index(slot)
local tooltips = {}
if index1 then
local tooltip = public.tooltip(function(tt) tt:SetInventoryItem('player', index1) end)
if getn(tooltip) > 0 then
tinsert(tooltips, tooltip)
end
end
if index2 then
local tooltip = public.tooltip(function(tt) tt:SetInventoryItem('player', index2) end)
if getn(tooltip) > 0 then
tinsert(tooltips, tooltip)
end
end
if tooltips[1] then
tinsert(tooltips[1], 1, { left_text = 'Currently Equipped', left_color = { 0.5, 0.5, 0.5 } })
ShoppingTooltip1:SetOwner(GameTooltip, 'ANCHOR_BOTTOMRIGHT')
public.load_tooltip(ShoppingTooltip1, tooltips[1])
ShoppingTooltip1:Show()
ShoppingTooltip1:SetPoint('TOPLEFT', GameTooltip, 'TOPRIGHT', 0, -10)
end
if tooltips[2] then
tinsert(tooltips[2], 1, { left_text = 'Currently Equipped', left_color = { 0.5, 0.5, 0.5 } })
ShoppingTooltip2:SetOwner(ShoppingTooltip1, 'ANCHOR_BOTTOMRIGHT')
public.load_tooltip(ShoppingTooltip2, tooltips[2])
ShoppingTooltip2:Show()
ShoppingTooltip2:SetPoint('TOPLEFT', ShoppingTooltip1, 'TOPRIGHT')
end
end
function public.tooltip_match(entry, tooltip)
return Aux.util.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 public.tooltip_find(pattern, tooltip)
local count = 0
for _, line in ipairs(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 public.load_tooltip(frame, tooltip)
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,TOOLTIP_LENGTH do -- TODO why is this needed?
getglobal(frame:GetName()..'TextLeft'..i):SetJustifyH('LEFT')
getglobal(frame:GetName()..'TextRight'..i):SetJustifyH('LEFT')
end
end
function public.display_name(item_id, plain, uncolored)
local item_info = Aux.info.item(item_id)
if item_info then
local name = item_info.name
if not plain then
name = '['..name..']'
end
if not uncolored then
name = ({GetItemQualityColor(item_info.quality)})[4]..name..FONT_COLOR_CODE_CLOSE
end
return name
end
end
function public.auctionable(tooltip, quality, lootable)
local durability, max_durability = public.durability(tooltip)
return not lootable
and (not quality or quality < 6)
and not public.tooltip_match(ITEM_BIND_ON_PICKUP, tooltip)
and not public.tooltip_match(ITEM_BIND_QUEST, tooltip)
and not public.tooltip_match(ITEM_SOULBOUND, tooltip)
and (not public.tooltip_match(ITEM_CONJURED, tooltip) or public.tooltip_find(ITEM_MIN_LEVEL, tooltip) > 1)
and not (durability and durability < max_durability)
end
function public.tooltip(setter)
for i = 1, TOOLTIP_LENGTH do
getglobal('AuxTooltipTextLeft'..i):SetText()
getglobal('AuxTooltipTextRight'..i):SetText()
end
AuxTooltip:SetOwner(UIParent, 'ANCHOR_NONE')
AuxTooltip.money = 0
setter(AuxTooltip)
AuxTooltip:Show()
local tooltip = { money=AuxTooltip.money }
for i = 1, TOOLTIP_LENGTH do
local left_text = getglobal('AuxTooltipTextLeft'..i):GetText()
local left_color = { getglobal('AuxTooltipTextLeft'..i):GetTextColor() }
local right_text = getglobal('AuxTooltipTextRight'..i):GetText()
local right_color = { getglobal('AuxTooltipTextRight'..i):GetTextColor() }
if left_text or right_text then
tinsert(tooltip, {
left_text = left_text,
left_color = left_color,
right_text = right_text,
right_color = right_color,
})
end
end
for i = 1, TOOLTIP_LENGTH do
getglobal('AuxTooltipTextLeft'..i):SetText()
getglobal('AuxTooltipTextRight'..i):SetText()
end
return tooltip
end
function private.item_charges(tooltip)
for _, line in ipairs(tooltip) do
local pattern = '^'..gsub(gsub(ITEM_SPELL_CHARGES_P1, '%%d', '(%%d+)'), '%%%d+%$d', '(%%d+)')..'$'
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 charges
end
end
return 1
end
function private.max_item_charges(item_id)
return ({
-- 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,
-- ... TODO
})[item_id]
end
function public.durability(tooltip)
for _, line in ipairs(tooltip) do
local pattern = '^'..gsub(gsub(DURABILITY_TEMPLATE, '%%d', '(%%d+)'), '%%%d+%$d', '(%%d+)')..'$'
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
function public.item_key(hyperlink)
local item_id, suffix_id = public.parse_hyperlink(hyperlink)
return item_id..':'..suffix_id
end
function public.parse_hyperlink(hyperlink)
local _, _, item_id, enchant_id, suffix_id, unique_id, name = strfind(hyperlink, '|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 public.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 public.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 {
name = name,
itemstring = itemstring,
quality = quality,
level = level,
class = class,
subclass = subclass,
slot = slot,
max_stack = max_stack,
texture = texture,
} or Aux.cache.item_info(item_id)
end
+107
View File
@@ -0,0 +1,107 @@
local m = {}
Aux.money = m
local GOLD_TEXT = '|cffffd70ag|r'
local SILVER_TEXT = '|cffc7c7cfs|r'
local COPPER_TEXT = '|cffeda55fc|r'
do
local COPPER_PER_SILVER = 100
local COPPER_PER_GOLD = 10000
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
end
function m.to_string(money, pad, trim, decimal_points, color, no_color)
local is_negative = money < 0
money = abs(money)
local gold, silver, copper = m.to_GSC(money)
-- rounding
if decimal_points then
copper = tonumber(format('%.'..decimal_points..'f', copper))
end
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 = {}
if gold > 0 then
tinsert(parts, m.format_number(gold, false, nil, color)..gold_text)
end
if silver > 0 then
tinsert(parts, m.format_number(silver, pad, nil, color)..silver_text)
end
if copper > 0 or gold == 0 and silver == 0 then
tinsert(parts, m.format_number(copper, pad, decimal_points, color)..copper_text)
end
text = Aux.util.join(parts, ' ')
else
if gold > 0 then
text = m.format_number(gold, false, nil, color)..gold_text..' '..m.format_number(silver, pad, nil, color)..silver_text..' '..m.format_number(copper, pad, decimal_points, color)..copper_text
elseif silver > 0 then
text = m.format_number(silver, false, nil, color)..silver_text..' '..m.format_number(copper, pad, decimal_points, color)..copper_text
else
text = m.format_number(copper, false, decimal_points, color)..copper_text
end
end
if is_negative then
if color then
text = color..'-|r'..text
else
text = '-'..text
end
end
return text
end
function m.from_string(value)
value = strlower(value)
-- remove any colors
value = gsub(gsub(value, '\124c([0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])', ''), '\124r', '')
-- extract gold/silver/copper values
local gold = tonumber(({strfind(value, '(%d*%.?%d+)g')})[3])
local silver = tonumber(({strfind(value, '(%d*%.?%d+)s')})[3])
local copper = tonumber(({strfind(value, '(%d*%.?%d+)c')})[3])
if not gold and not silver and not copper then return end
-- test that there are no extra characters (other than spaces)
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 m.from_GSC(gold or 0, silver or 0, copper or 0)
end
function m.format_number(num, pad, decimal_padding, color)
local padding = pad and 2 + (decimal_padding and decimal_padding + 1 or 0) or 0
num = format('%0'..padding..'.0'..(decimal_padding or 0)..'f', num)
if color then
return color..num..'|r'
else
return num
end
end
+144
View File
@@ -0,0 +1,144 @@
local private, public = {}, {}
Aux.persistence = public
aux_datasets = {}
function public.on_load()
Aux.control.as_soon_as(function() private.faction = UnitFactionGroup('player') return private.faction end, Aux.util.pass)
private.realm = GetCVar('realmName')
end
function private.get_dataset_key()
return private.realm..'|'..private.faction
end
function public.load_dataset()
local dataset_key = private.get_dataset_key()
aux_datasets[dataset_key] = aux_datasets[dataset_key] or {}
return aux_datasets[dataset_key]
end
function public.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 public.read_list(schema, str)
elseif type(schema) == 'table' and schema[1] == 'record' then
return public.read_record(schema, str)
else
error('Unknown schema.')
end
end
function public.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 public.write_list(schema, obj)
elseif type(schema) == 'table' and schema[1] == 'record' then
return public.write_record(schema, obj)
else
error('Unknown schema.')
end
end
function public.read_list(schema, str)
if str == '' then
return {}
end
local separator = schema[2]
local element_type = schema[3]
local parts = Aux.util.split(str, separator)
return Aux.util.map(parts, function(part)
return public.read(element_type, part)
end)
end
function public.write_list(schema, list)
local separator = schema[2]
local element_type = schema[3]
local parts = Aux.util.map(list, function(element)
return public.write(element_type, element)
end)
return Aux.util.join(parts, separator)
end
function public.read_record(schema, str)
local separator = schema[2]
local record = {}
local parts = Aux.util.split(str, separator)
for i=3,getn(schema) do
local key, type = next(schema[i])
record[key] = public.read(type, parts[i - 2])
end
return record
end
function public.write_record(schema, record)
local separator = schema[2]
local parts = {}
for i=3,getn(schema) do
local key, type = next(schema[i])
tinsert(parts, public.write(type, record[key]))
end
return Aux.util.join(parts, separator)
end
function public.serialize(data, separator, compactor)
local data_string = ''
local i = 1
while i <= getn(data) do
local element, count = data[i], 1
while compactor and data[i + 1] == element do
count = count + 1
i = i + 1
end
local part = count > 1 and element..compactor..count or element
data_string = data_string..(i == 1 and '' or separator)..part
i = i + 1
end
return data_string
end
function public.deserialize(data_string, separator, compactor)
if not data_string or data_string == '' then
return {}
end
local data = {}
while true do
local start_index, _ = strfind(data_string, separator, 1, true)
local part
if start_index then
part = strsub(data_string, 1, start_index - 1)
data_string = strsub(data_string, start_index + 1)
else
part = strsub(data_string, 1)
end
if compactor and strfind(part, compactor, 1, true) then
local compactor_index, _ = strfind(part, compactor, 1, true)
for i=1, tonumber(strsub(part, compactor_index + 1)) do
tinsert(data, strsub(part, 1, compactor_index - 1))
end
else
tinsert(data, part)
end
if not start_index then
return data
end
end
end
+735
View File
@@ -0,0 +1,735 @@
local m = {}
Aux.scan_util = m
function m.default_filter(str)
return {
arity = 0,
test = function()
return function(auction_record)
return Aux.util.any(auction_record.tooltip, function(entry)
return strfind(strupper(entry.left_text or ''), strupper(str or ''), 1, true) or strfind(strupper(entry.right_text or ''), strupper(str or ''), 1, true)
end)
end
end,
}
end
m.filters = {
['utilizable'] = {
arity = 0,
test = function()
return function(auction_record)
return auction_record.usable and not Aux.info.tooltip_match(ITEM_SPELL_KNOWN, auction_record.tooltip)
end
end,
},
['tooltip'] = {
arity = 1,
test = function(str)
if str then
return m.default_filter(str).test()
else
return false, {}, 'Erroneous tooltip modifier'
end
end,
},
['item'] = {
arity = 1,
test = function(name)
if not name then
return false, aux_auctionable_items, 'Erroneous item modifier'
end
return function(auction_record)
return strlower(Aux.info.item(auction_record.item_id).name) == name
end
end
},
['left'] = {
arity = 1,
test = function(duration)
if not duration then
return false, {'30m', '2h', '8h', '24h'}, 'Erroneous time left modifier'
end
local code = ({
['30m'] = 1,
['2h'] = 2,
['8h'] = 3,
['24h'] = 4,
})[duration or '']
if not code then
return false, {}, 'Erroneous time left modifier'
end
return function(auction_record)
return auction_record.duration == code
end
end
},
['rarity'] = {
arity = 1,
test = function(rarity)
if not rarity then
return false, {'poor', 'common', 'uncommon', 'rare', 'epic'}, 'Erroneous rarity modifier'
end
local code = ({
['poor'] = 0,
['common'] = 1,
['uncommon'] = 2,
['rare'] = 3,
['epic'] = 4,
})[rarity or '']
if not code then
return false, {}, 'Erroneous rarity modifier'
end
return function(auction_record)
return auction_record.quality == code
end
end
},
['min-lvl'] = {
arity = 1,
test = function(level)
level = tonumber(level or '')
if level then
return function(auction_record)
return auction_record.level >= level
end
else
return false, {}, 'Erroneous min level modifier'
end
end
},
['max-lvl'] = {
arity = 1,
test = function(level)
level = tonumber(level or '')
if level then
return function(auction_record)
return auction_record.level <= level
end
else
return false, {}, 'Erroneous max level modifier'
end
end
},
['min-unit-bid'] = {
arity = 1,
test = function(amount)
amount = Aux.money.from_string(amount or '') or 0
if amount > 0 then
return function(auction_record)
return auction_record.unit_bid_price >= amount
end
else
return false, {}, 'Erroneous min bid modifier'
end
end
},
['min-unit-buy'] = {
arity = 1,
test = function(amount)
amount = Aux.money.from_string(amount or '') or 0
if amount > 0 then
return function(auction_record)
return auction_record.unit_buyout_price >= amount
end
else
return false, {}, 'Erroneous min buyout modifier'
end
end
},
['max-unit-bid'] = {
arity = 1,
test = function(amount)
amount = Aux.money.from_string(amount or '') or 0
if amount > 0 then
return function(auction_record)
return auction_record.unit_bid_price <= amount
end
else
return false, {}, 'Erroneous max bid modifier'
end
end
},
['max-unit-buy'] = {
arity = 1,
test = function(amount)
amount = Aux.money.from_string(amount or '') or 0
if amount > 0 then
return function(auction_record)
return auction_record.buyout_price > 0 and auction_record.unit_buyout_price <= amount
end
else
return false, {}, 'Erroneous max buyout modifier'
end
end
},
['bid-pct'] = {
arity = 1,
test = function(pct)
pct = tonumber(pct)
if pct then
return function(auction_record)
return auction_record.unit_buyout_price > 0
and Aux.history.value(auction_record.item_key)
and auction_record.unit_buyout_price / Aux.history.value(auction_record.item_key) * 100 <= pct
end
else
return false, {}, 'Erroneous bid percentage modifier'
end
end
},
['buy-pct'] = {
arity = 1,
test = function(pct)
pct = tonumber(pct)
if pct then
return function(auction_record)
return auction_record.unit_buyout_price > 0
and Aux.history.value(auction_record.item_key)
and auction_record.unit_buyout_price / Aux.history.value(auction_record.item_key) * 100 <= pct
end
else
return false, {}, 'Erroneous buyout percentage modifier'
end
end
},
['bid-profit'] = {
arity = 1,
test = function(amount)
amount = Aux.money.from_string(amount or '') or 0
if amount > 0 then
return function(auction_record)
return Aux.history.value(auction_record.item_key) and Aux.history.value(auction_record.item_key) * auction_record.aux_quantity - auction_record.bid_price >= amount
end
else
return false, {}, 'Erroneous bid profit modifier'
end
end
},
['buy-profit'] = {
arity = 1,
test = function(amount)
amount = Aux.money.from_string(amount or '') or 0
if amount > 0 then
return function(auction_record)
return auction_record.buyout_price > 0 and Aux.history.value(auction_record.item_key) and Aux.history.value(auction_record.item_key) * auction_record.aux_quantity - auction_record.buyout_price >= amount
end
else
return false, {}, 'Erroneous buyout profit modifier'
end
end
},
['bid-dis-profit'] = {
arity = 1,
test = function(amount)
amount = Aux.money.from_string(amount or '') or 0
if amount > 0 then
return function(auction_record)
local disenchant_value = Aux.disenchant.value(auction_record.slot, auction_record.quality, auction_record.level)
return disenchant_value and disenchant_value - auction_record.bid_price >= amount
end
else
return false, {}, 'Erroneous bid disenchant profit modifier'
end
end
},
['buy-dis-profit'] = {
arity = 1,
test = function(amount)
amount = Aux.money.from_string(amount or '') or 0
if amount > 0 then
return function(auction_record)
local disenchant_value = Aux.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
else
return false, {}, 'Erroneous buyout disenchant profit modifier'
end
end
},
['bid-vend-profit'] = {
arity = 1,
test = function(amount)
amount = Aux.money.from_string(amount or '') or 0
if amount > 0 then
return function(auction_record)
local vendor_price = Aux.cache.merchant_info(auction_record.item_id)
return vendor_price and vendor_price * auction_record.aux_quantity - auction_record.bid_price >= amount
end
else
return false, {}, 'Erroneous bid vendor profit Modifier'
end
end
},
['buy-vend-profit'] = {
arity = 1,
test = function(amount)
amount = Aux.money.from_string(amount or '') or 0
if amount > 0 then
return function(auction_record)
local vendor_price = Aux.cache.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
else
return false, {}, 'Erroneous buyout vendor profit Modifier'
end
end
},
['discard'] = {
arity = 0,
test = function()
return function()
return false
end
end
},
}
function m.find(auction_record, status_bar, on_abort, on_failure, on_success)
local function test(index)
local auction_info = Aux.info.auction(index, auction_record.query_type)
return auction_info and auction_info.search_signature == auction_record.search_signature
end
local queries = {}
tinsert(queries, {})
if auction_record.blizzard_query then
local blizzard_query1 = Aux.util.copy_table(auction_record.blizzard_query)
blizzard_query1.first_page = auction_record.page
blizzard_query1.last_page = auction_record.page
tinsert(queries, {
blizzard_query = blizzard_query1,
})
if auction_record.page > 0 then
local blizzard_query2 = Aux.util.copy_table(auction_record.blizzard_query)
blizzard_query2.first_page = auction_record.page - 1
blizzard_query2.last_page = auction_record.page - 1
tinsert(queries, {
blizzard_query = blizzard_query1,
})
end
local item_query = m.item_query(auction_record.item_id, 1, 1)
if not Aux.util.table_eq(auction_record.blizzard_query, item_query.blizzard_query) then
tinsert(queries, item_query)
end
end
local found
return Aux.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) * 100, 0)
end,
on_auction = function(auction_record, ctrl)
if test(auction_record.index) then
found = true
ctrl.suspend()
status_bar:update_status(100, 100)
status_bar:set_text('Auction found')
return on_success(auction_record.index)
end
end,
on_abort = function()
if not found then
status_bar:update_status(100, 100)
status_bar:set_text('Auction not found')
return on_abort()
end
end,
on_complete = function()
status_bar:update_status(100, 100)
status_bar:set_text('Auction not found')
return on_failure()
end,
}
end
function m.filter_builder()
local filter = ''
return {
append = function(self, modifier)
modifier = modifier
filter = filter == '' and modifier or filter..'/'..modifier
end,
prepend = function(self, modifier)
modifier = modifier
filter = filter == '' and modifier or modifier..'/'..filter
end,
get = function(self)
return filter
end
}
end
function m.item_query(item_id, first_page, last_page)
local item_info = Aux.info.item(item_id)
if item_info then
local filter = m.filter_from_string(item_info.name..'/exact/'..(first_page or '')..':'..(last_page or ''))
return {
validator = filter.validator,
blizzard_query = filter.blizzard_query,
}
end
end
function m.parse_filter_string(filter_string)
local parts = Aux.util.split(filter_string, ';')
local filters = {}
for _, str in ipairs(parts) do
str = Aux.util.trim(str)
local filter, _, error = m.filter_from_string(str)
if not filter then
Aux.log('Invalid filter: '..error)
return
elseif filter.name and strlen(filter.name) > 63 then
else
tinsert(filters, filter)
end
end
return filters
end
function m.filter_from_string(filter_term)
local parts = Aux.util.map(Aux.util.split(filter_term, '/'), function(part) return strlower(Aux.util.trim(part)) end)
local blizzard_filter = {}
local post_filter = {}
local prettified = m.filter_builder()
local polish_notation_counter = 0
local i = 1
local function non_blizzard_modifier(str)
local filter = m.filters[str]
if filter then
prettified:append('|cffffff00'..str..'|r')
else
filter = filter or m.default_filter(str)
prettified:append('|cffff9218'..str..'|r')
end
local args = {}
for j=1, filter.arity do
local arg = parts[i - 1 + j]
if arg then
tinsert(args, arg)
prettified:append('|cffff9218'..arg..'|r')
end
end
i = i + filter.arity
local test, suggestions, error = filter.test(unpack(args))
if test then
tinsert(post_filter, test)
else
return error, i > getn(parts) and suggestions or {}
end
end
while i <= getn(parts) do
local str = parts[i]
i = i + 1
if polish_notation_counter > 0 or str == 'and' or str == 'or' or str == 'not' then
polish_notation_counter = polish_notation_counter == 0 and polish_notation_counter + 1 or polish_notation_counter
if str == 'and' or str == 'or' then
polish_notation_counter = polish_notation_counter + 1
tinsert(post_filter, str)
prettified:append('|cffffff00'..str..'|r')
elseif str == 'not' then
tinsert(post_filter, str)
prettified:append('|cffffff00'..str..'|r')
elseif str ~= '' then
polish_notation_counter = polish_notation_counter - 1
local error, suggestions = non_blizzard_modifier(str)
if error then
return false, suggestions, error
end
end
elseif strfind(str, '^%d*:%d*$') then
if not blizzard_filter.first_page and not blizzard_filter.last_page then
local _, _, first_page, last_page = strfind(str, '^(%d*):(%d*)$')
if tonumber(first_page) and tonumber(first_page) < 1 or tonumber(last_page) and tonumber(last_page) < 1 then
return false, {}, 'Erroneous page range modifier'
end
blizzard_filter.first_page = tonumber(first_page)
blizzard_filter.last_page = tonumber(last_page)
prettified:append(Aux.gui.inline_color({216, 225, 211, 1})..str..'|r')
else
return false, {}, 'Erroneous page range modifier'
end
elseif tonumber(str) then
if tonumber(str) < 1 or tonumber(str) > 60 then
return false, {}, 'Erroneous level range modifier'
end
if not blizzard_filter.min_level then
blizzard_filter.min_level = tonumber(str)
prettified:append(Aux.gui.inline_color({216, 225, 211, 1})..str..'|r')
elseif not blizzard_filter.max_level and tonumber(str) >= blizzard_filter.min_level then
blizzard_filter.max_level = tonumber(str)
prettified:append(Aux.gui.inline_color({216, 225, 211, 1})..str..'|r')
else
return false, {}, 'Erroneous level range modifier'
end
elseif Aux.item_class_index(str) and not (blizzard_filter.class and not blizzard_filter.subclass and str == strlower(({ GetAuctionItemClasses() })[10])) then
if not blizzard_filter.class then
blizzard_filter.class = Aux.item_class_index(str)
prettified:append(Aux.gui.inline_color({216, 225, 211, 1})..str..'|r')
else
return false, {}, 'Erroneous item class modifier'
end
elseif blizzard_filter.class and Aux.item_subclass_index(blizzard_filter.class, str) then
if not blizzard_filter.subclass then
blizzard_filter.subclass = Aux.item_subclass_index(blizzard_filter.class, str)
prettified:append(Aux.gui.inline_color({216, 225, 211, 1})..str..'|r')
else
return false, {}, 'Erroneous item subclass modifier'
end
elseif blizzard_filter.subclass and Aux.item_slot_index(blizzard_filter.class, blizzard_filter.subclass, str) then
if not blizzard_filter.slot then
blizzard_filter.slot = Aux.item_slot_index(blizzard_filter.class, blizzard_filter.subclass, str)
prettified:append(Aux.gui.inline_color({216, 225, 211, 1})..str..'|r')
else
return false, {}, 'Erroneous item slot modifier'
end
elseif Aux.item_quality_index(str) then
if not blizzard_filter.quality then
blizzard_filter.quality = Aux.item_quality_index(str)
prettified:append(Aux.gui.inline_color({216, 225, 211, 1})..str..'|r')
else
return false, {}, 'Erroneous rarity modifier'
end
elseif str == 'usable' then
if not blizzard_filter.usable then
blizzard_filter.usable = true
prettified:append(Aux.gui.inline_color({216, 225, 211, 1})..str..'|r')
else
return false, {}, 'Erroneous usable only modifier'
end
elseif str == 'exact' then
if not blizzard_filter.exact then
blizzard_filter.exact = true
else
return false, {}, 'Erroneous exact only modifier'
end
elseif i == 2 and not m.filters[str] then
blizzard_filter.name = str
elseif str ~= '' then
local error, suggestions = non_blizzard_modifier(str)
if error then
return false, suggestions, error
end
else
return false, {}, 'Empty modifier'
end
end
if polish_notation_counter ~= 0 then
local suggestions = {}
for filter, _ in pairs(m.filters) do
tinsert(suggestions, strlower(filter))
tinsert(suggestions, 'and')
tinsert(suggestions, 'or')
tinsert(suggestions, 'not')
end
return false, i > getn(parts) and suggestions, 'Malformed expression'
end
if blizzard_filter.exact then
if blizzard_filter.min_level
or blizzard_filter.max_level
or blizzard_filter.class
or blizzard_filter.subclass
or blizzard_filter.slot
or blizzard_filter.quality
or blizzard_filter.usable
or not blizzard_filter.name
then
return false, {}, 'Erroneous exact only modifier'
else
prettified:prepend(Aux.info.display_name(Aux.cache.item_id(blizzard_filter.name)) or Aux.gui.inline_color({216, 225, 211, 1})..'['..blizzard_filter.name..']|r')
end
elseif blizzard_filter.name then
if blizzard_filter.name == '' then
prettified:prepend('|cffff0000'..'No Filter'..'|r')
else
prettified:prepend(Aux.gui.inline_color({216, 225, 211, 1})..blizzard_filter.name..'|r')
end
end
return {
blizzard_query = m.blizzard_query(blizzard_filter),
validator = m.validator(blizzard_filter, post_filter),
prettified = prettified:get(),
}, m.suggestions(blizzard_filter, getn(parts))
end
function m.suggestions(blizzard_filter, num_parts)
local suggestions = {}
if blizzard_filter.name
and not blizzard_filter.min_level
and not blizzard_filter.max_level
and not blizzard_filter.class
and not blizzard_filter.subclass
and not blizzard_filter.slot
and not blizzard_filter.quality
and not blizzard_filter.usable
then
tinsert(suggestions, 'exact')
end
tinsert(suggestions, 'and')
tinsert(suggestions, 'or')
tinsert(suggestions, 'not')
tinsert(suggestions, 'tt')
for filter, _ in pairs(m.filters) do
tinsert(suggestions, strlower(filter))
end
-- classes
if not blizzard_filter.class then
for _, class in ipairs({ GetAuctionItemClasses() }) do
tinsert(suggestions, class)
end
end
-- subclasses
if blizzard_filter.class and not blizzard_filter.subclass then
for _, subclass in ipairs({ GetAuctionItemSubClasses(blizzard_filter.class) }) do
tinsert(suggestions, subclass)
end
end
-- slots
if blizzard_filter.class and blizzard_filter.subclass and not blizzard_filter.slot then
for _, invtype in ipairs({ GetAuctionInvTypes(blizzard_filter.class, blizzard_filter.subclass) }) do
tinsert(suggestions, getglobal(invtype))
end
end
-- usable
if not blizzard_filter.usable then
tinsert(suggestions, 'usable')
end
-- rarities
if not blizzard_filter.quality then
for i=0,4 do
tinsert(suggestions, getglobal('ITEM_QUALITY'..i..'_DESC'))
end
end
-- item names
if num_parts == 1 and blizzard_filter.name == '' then
for _, name in aux_auctionable_items do
tinsert(suggestions, name..'/exact')
end
end
return suggestions
end
function m.blizzard_query(filter)
local item_info, class_index, subclass_index, slot_index
if filter.exact then
local item_id = Aux.cache.item_id(filter.name)
item_info = Aux.info.item(item_id)
class_index = item_info and Aux.item_class_index(item_info.class)
subclass_index = class_index and item_info.subclass and Aux.item_subclass_index(class_index, item_info.subclass)
slot_index = subclass_index and item_info.slot and Aux.item_slot_index(class_index, subclass_index, item_info.slot)
end
return {
first_page = filter.first_page and filter.first_page - 1,
last_page = filter.last_page and filter.last_page - 1,
name = filter.name,
min_level = item_info and item_info.level or filter.min_level,
max_level = item_info and item_info.level or filter.max_level,
class = item_info and class_index or filter.class,
subclass = item_info and subclass_index or filter.subclass,
slot = item_info and item_info.class and slot_index or filter.slot,
usable = item_info and item_info.usable or filter.usable and 1 or 0,
quality = item_info and item_info.quality or filter.quality,
}
end
function m.validator(blizzard_filter, post_filter)
return function(record)
if blizzard_filter.exact and strlower(Aux.info.item(record.item_id).name) ~= blizzard_filter.name then
return
end
if blizzard_filter.min_level and record.level < blizzard_filter.min_level then
return
end
if blizzard_filter.max_level and record.level > blizzard_filter.max_level then
return
end
if getn(post_filter) > 0 then
local stack = {}
for i=getn(post_filter),1,-1 do
local op = post_filter[i]
if op == 'and' then
local a, b = tremove(stack), tremove(stack)
tinsert(stack, a and b)
elseif op == 'or' then
local a, b = tremove(stack), tremove(stack)
tinsert(stack, a or b)
elseif op == 'not' then
tinsert(stack, not tremove(stack))
else
tinsert(stack, op(record) and true or false)
end
end
return Aux.util.all(stack, Aux.util.id)
end
return true
end
end
+86
View File
@@ -0,0 +1,86 @@
local private, public = {}, {}
Aux.sort = public
public.LT = {}
public.EQ = {}
public.GT = {}
-- stable sorting
function public.merge_sort(A, comp)
local n = getn(A)
local B = {}
local width = 1
while width <= n do
for i=1, n, 2 * width do
private.merge(A, i, min(i + width, n), min(i + 2 * width - 1, n), B, comp)
end
private.copy_array(B, A, n)
width = 2 * width
end
end
function private.merge(A, start1, start2, last, B, comp)
local i1 = start1
local i2 = start2
for i=start1,last do
if i1 < start2 and (i2 > last or comp(A[i1], A[i2]) == public.LT or comp(A[i1], A[i2]) == public.EQ) then
B[i] = A[i1]
i1 = i1 + 1
else
B[i] = A[i2]
i2 = i2 + 1
end
end
end
function private.copy_array(A, B, n)
for i=1,n do
B[i] = A[i]
end
end
function public.invert_order(ordering)
if ordering == public.LT then
return public.GT
elseif ordering == public.GT then
return public.LT
else
return public.EQ
end
end
function public.compare(a, b, desc)
if a < b then
return desc and public.GT or public.LT
elseif a > b then
return desc and public.LT or public.GT
else
return public.EQ
end
end
function public.compare_from_lt(lt)
return function(a, b)
if lt(a, b) then
return public.LT
elseif lt(b, a) then
return public.GT
else
return public.EQ
end
end
end
function public.multi_lt(...)
for i=1,arg.n-1,2 do
if arg[i] ~= arg[i+1] then
return arg[i] < arg[i+1]
end
end
return false
end
+399
View File
@@ -0,0 +1,399 @@
local private, public = {}, {}
Aux.util = public
function Aux.util.pass()
end
function public.id(object)
return object
end
function public.table_eq(t1, t2)
if not t1 or not t2 then
return false
end
for key, value in t1 do
if t2[key] ~= value then
return false
end
end
return true
end
function public.wipe(table)
while getn(table) > 0 do
tremove(table)
end
for k, _ in pairs(table) do
table[k] = nil
end
end
function public.copy_table(table)
local copy = {}
for k, v in pairs(table) do
copy[k] = v
end
return copy
end
function public.cons(element, list)
local new_list = public.copy_table(list)
tinsert(new_list, 1, element)
return new_list
end
function public.trim(string)
string = gsub(string, '^%s*', '')
string = gsub(string, '%s*$', '')
return string
end
function public.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 { bag, slot }, public.bag_type(bag)
end
end
end
do
local bag_types = { GetAuctionItemSubClasses(3) }
function public.bag_type(bag)
if bag == 0 then
return 1
end
local link = GetInventoryItemLink('player', ContainerIDToInventoryID(bag))
if link then
local item_id = Aux.info.parse_hyperlink(GetInventoryItemLink('player', ContainerIDToInventoryID(bag)))
local item_info = Aux.info.item(item_id)
return Aux.item_subclass_index(3, item_info.subclass)
end
end
end
function public.safe_index(...)
local target = arg[1]
for i=2,arg.n do
if not target then
return
end
target = target[arg[i]]
end
return target
end
function Aux_PluralizeIf(word, count)
if count and count == 1 then
return word
else
return word..'s'
end
end
function Aux.util.without_errors(f)
local orig = UIErrorsFrame.AddMessage
UIErrorsFrame.AddMessage = Aux.util.pass
f()
UIErrorsFrame.AddMessage = orig
end
function Aux.util.without_sound(f)
local orig = GetCVar('MasterSoundEffects')
SetCVar('MasterSoundEffects', 0)
f()
SetCVar('MasterSoundEffects', orig)
end
function Aux.util.iter(array)
local with_index = ipairs(array)
return function()
local _, value = with_index
return value
end
end
function Aux.util.set_add(set, key)
set[key] = true
end
function Aux.util.set_remove(set, key)
set[key] = nil
end
function Aux.util.set_contains(set, key)
return set[key] ~= nil
end
function Aux.util.set_size(set)
local size = 0
for _,_ in pairs(set) do
size = size + 1
end
return size
end
function Aux.util.set_to_array(set)
local array = {}
for element, _ in pairs(set) do
tinsert(array, element)
end
return array
end
function Aux.util.any(xs, p)
holds = false
for _, x in ipairs(xs) do
holds = holds or p(x)
end
return holds
end
function Aux.util.all(xs, p)
holds = true
for _, x in ipairs(xs) do
holds = holds and p(x)
end
return holds
end
function Aux.util.set_filter(xs, p)
ys = {}
for x, _ in pairs(xs) do
if p(x) then
Aux.util.set_add(ys, x)
end
end
return ys
end
function Aux.util.filter(xs, p)
ys = {}
for _, x in ipairs(xs) do
if p(x) then
tinsert(ys, x)
end
end
return ys
end
function Aux.util.map(xs, f)
ys = {}
for _, x in ipairs(xs) do
tinsert(ys, f(x))
end
return ys
end
function Aux.util.take(n, xs)
ys = {}
for i=1,n do
if xs[i] then
tinsert(ys, xs[i])
end
end
return ys
end
function Aux.util.index_of(value, array)
for i, item in ipairs(array) do
if item == value then
return i
end
end
end
local GSC_GOLD = "ffd100"
local GSC_SILVER = "e6e6e6"
local GSC_COPPER = "c8602c"
local GSC_RED = "ff0000"
local GSC_3 = "|cff"..GSC_GOLD.."%d|cff000000.|cff"..GSC_SILVER.."%02d|cff000000.|cff"..GSC_COPPER.."%02d|r"
local GSC_2 = "|cff"..GSC_SILVER.."%d|cff000000.|cff"..GSC_COPPER.."%02d|r"
local GSC_1 = "|cff"..GSC_COPPER.."%d|r"
local GSC_3N = "|cff"..GSC_RED.."(|cff"..GSC_GOLD.."%d|cff000000.|cff"..GSC_SILVER.."%02d|cff000000.|cff"..GSC_COPPER.."%02d|cff"..GSC_RED..")|r"
local GSC_2N = "|cff"..GSC_RED.."(|cff"..GSC_SILVER.."%d|cff000000.|cff"..GSC_COPPER.."%02d|cff"..GSC_RED..")|r"
local GSC_1N = "|cff"..GSC_RED.."(|cff"..GSC_COPPER.."%d|cff"..GSC_RED..")|r"
function Aux.util.money_string(money)
money = floor(tonumber(money) or 0)
local negative = money < 0
money = abs(money)
local g = floor(money / 10000)
money = money - g * 10000
local s = floor(money / 100)
money = money - s * 100
local c = money
if g > 0 then
if negative then
return format(GSC_3N, g, s, c)
else
return format(GSC_3, g, s, c)
end
elseif s > 0 then
if negative then
return format(GSC_2N, s, c)
else
return format(GSC_2, s, c)
end
else
if negative then
return format(GSC_1N, c)
else
return format(GSC_1, c)
end
end
end
function Aux.util.group_by(tables, equal)
local groups = {}
for _, table in ipairs(tables) do
local found_group
for _, group in ipairs(groups) do
if equal(table, group[1]) then
tinsert(group, table)
found_group = true
end
end
if not found_group then
tinsert(groups, { table })
end
end
return groups
end
function Aux.util.set()
local self = {}
local data = {}
function self:add(value)
data[value] = true
end
function self:add_all(values)
for _, value in ipairs(values) do
self:add(value)
end
end
function self:remove(value)
data[value] = nil
end
function self:remove_all(values)
for _, value in ipairs(values) do
self:remove(value)
end
end
function self:contains(value)
return data[value] ~= nil
end
function self:values()
local values = {}
for value, _ in pairs(data) do
tinsert(values, value)
end
return values
end
return self
end
function public.join(array, separator)
local str = ''
for i, element in ipairs(array) do
if i > 1 then
str = str..separator
end
str = str..element
end
return str
end
function public.tokenize(str)
local tokens = {}
for token in string.gfind(str, '%S+') do
tinsert(tokens, token)
end
return tokens
end
function public.split(str, separator)
local array = {}
while true do
local start_index, _ = strfind(str, separator, 1, true)
if start_index then
local part = strsub(str, 1, start_index - 1)
tinsert(array, part)
str = strsub(str, start_index + 1)
else
local part = strsub(str, 1)
tinsert(array, part)
return array
end
end
end
function public.format_money(money, exact, color)
color = color or '|r'
local TEXT_NONE = '0'
local GSC_GOLD = 'ffd100'
local GSC_SILVER = 'e6e6e6'
local GSC_COPPER = 'c8602c'
local GSC_START = '|cff%s%d|r'
local GSC_PART = color..'.|cff%s%02d|r'
local GSC_NONE = '|cffa0a0a0'..TEXT_NONE..'|r'
if not exact and money >= 10000 then
-- Round to nearest silver
money = math.floor(money / 100 + 0.5) * 100
end
local g, s, c = Aux.money.to_GSC(money)
local gsc = ''
local fmt = GSC_START
if g > 0 then
gsc = gsc..string.format(fmt, GSC_GOLD, g)
fmt = GSC_PART
end
if s > 0 or c > 0 then
gsc = gsc..string.format(fmt, GSC_SILVER, s)
fmt = GSC_PART
end
if c > 0 then
gsc = gsc..string.format(fmt, GSC_COPPER, c)
end
if gsc == '' then
gsc = GSC_NONE
end
return gsc
end