Add Vanilla compatibility layer and installation guide

This commit is contained in:
2026-10-07 19:10:24 +00:00
parent cd6e5653af
commit 4949bfdd89
4 changed files with 1507 additions and 3 deletions
+567
View File
@@ -0,0 +1,567 @@
-- Compat.lua: bridges the WoW 3.3 / Lua 5.1 constructs used by Auctionator 2.6.2 to the 1.12 client
-- (Lua 5.0, vanilla auction API). Loaded first. Everything here is prefixed Atr_ unless noted.
AtrNS = AtrNS or {}
local G = getfenv(0)
----------------------------------------------------------------------
-- small language helpers
----------------------------------------------------------------------
-- strsplit(delim, str): pieces as multiple return values; delim is matched literally
function Atr_strsplit(delim, str, pieces)
if str == nil then return nil end
local t, pos = {}, 1
while true do
local s, e = string.find(str, delim, pos, true)
if (not s) or (pieces and getn(t) >= pieces - 1) then
tinsert(t, string.sub(str, pos))
break
end
tinsert(t, string.sub(str, pos, s - 1))
pos = e + 1
end
return unpack(t)
end
-- Atr_hooksecurefunc(table, "Method", fn) / Atr_hooksecurefunc("Global", fn): runs fn after the original.
-- Auctionator always uses its own copy: 1.12 has no hooksecurefunc, and addons loaded earlier define
-- incompatible globals (Atlas-CFM's only knows the ("Global", fn) form, so every tooltip hook was a no-op).
function Atr_hooksecurefunc(a1, a2, a3)
local tbl, name, fn
if type(a1) == "string" then
tbl, name, fn = G, a1, a2
else
tbl, name, fn = a1, a2, a3
end
local orig = tbl[name]
if type(orig) ~= "function" then return end
tbl[name] = function(p1, p2, p3, p4, p5, p6, p7, p8, p9)
local r1, r2, r3, r4 = orig(p1, p2, p3, p4, p5, p6, p7, p8, p9)
fn(p1, p2, p3, p4, p5, p6, p7, p8, p9)
return r1, r2, r3, r4
end
end
-- Lua errors coming from this addon are also echoed to the chat frame (once per message), so they show up
-- in screenshots even when the error popup is off. The previous handler still runs.
do
local seen = {}
-- errors caught by pcall (tooltip hooks) are reported here, once per message
function Atr_ReportError(msg)
msg = tostring(msg)
if seen[msg] then return end
seen[msg] = true
if DEFAULT_CHAT_FRAME then
DEFAULT_CHAT_FRAME:AddMessage("|cffff3333Auctionator error:|r " .. msg)
end
end
local prev = (G.geterrorhandler and geterrorhandler()) or G._ERRORMESSAGE
if G.seterrorhandler and prev then
seterrorhandler(function(msg)
if type(msg) == "string" and string.find(msg, "Auctionator") and not seen[msg] then
seen[msg] = true
if DEFAULT_CHAT_FRAME then
DEFAULT_CHAT_FRAME:AddMessage("|cffff3333Auctionator error:|r " .. msg)
end
end
return prev(msg)
end)
end
end
function Atr_GetVersion()
return "1.0.0"
end
----------------------------------------------------------------------
-- item info (3.x layout: name, link, rarity, ilevel, minLevel, type, subType, stack, equipLoc, texture, sellPrice)
----------------------------------------------------------------------
local linkByName = {} -- lower(name) -> item link, filled from everything we see
function Atr_RememberItemLink(name, link)
if name and link then linkByName[string.lower(name)] = link end
end
local function ItemIdOf(x)
if type(x) == "number" then return x end
if type(x) ~= "string" then return nil end
local _, _, id = string.find(x, "item:(%d+)")
if id then return tonumber(id) end
local l = linkByName[string.lower(x)]
if l then
local _, _, id2 = string.find(l, "item:(%d+)")
if id2 then return tonumber(id2) end
end
return nil
end
function Atr_ItemIdFromAnything(x)
return ItemIdOf(x)
end
function Atr_GetSellValueById(id)
if id and G.SellValues then
local v = G.SellValues["item:" .. id]
if v then return v end
end
return nil
end
function Atr_GetItemInfo(x)
local id = ItemIdOf(x)
if not id then return nil end
local a, b, c, d, e, f, g, h, i, j = GetItemInfo("item:" .. id .. ":0:0:0")
if not a then return nil end
local name, link, rarity, ilevel, minLevel, itemType, subType, stack, equip, texture
if type(e) == "string" then
-- 1.12 layout: name, link, rarity, minLevel, type, subType, stack, equipLoc, texture
name, link, rarity, minLevel, itemType, subType, stack, equip, texture = a, b, c, d, e, f, g, h, i
else
-- 2.x+ layout (extended clients)
name, link, rarity, ilevel, minLevel, itemType, subType, stack, equip, texture = a, b, c, d, e, f, g, h, i, j
end
if link then Atr_RememberItemLink(name, link) end
return name, link, rarity, ilevel, minLevel, itemType, subType, stack, equip, texture, Atr_GetSellValueById(id)
end
function Atr_GetItemIcon(x)
local _, _, _, _, _, _, _, _, _, texture = Atr_GetItemInfo(x)
return texture
end
----------------------------------------------------------------------
-- cursor / inventory
----------------------------------------------------------------------
function Atr_CursorIsItem()
return CursorHasItem() and true or false
end
local function DropCursorAnywhere()
if not CursorHasItem() then return end
for bag = 0, 4 do
for slot = 1, (GetContainerNumSlots(bag) or 0) do
if not GetContainerItemLink(bag, slot) then
PickupContainerItem(bag, slot)
if not CursorHasItem() then return end
end
end
end
if G.ClearCursor then ClearCursor() end
end
Atr_DropCursorAnywhere = DropCursorAnywhere
local function LinkId(link)
if not link then return nil end
local _, _, id = string.find(link, "item:(%d+)")
return id and tonumber(id) or nil
end
-- link of the item currently sitting in the auction sell slot
function Atr_SellSlotLink()
local name, texture, count, quality, canUse, price, maxStack, link = GetAuctionSellItemInfo()
if not name then return nil end
if link then
Atr_RememberItemLink(name, link)
return link, name, count, maxStack, price
end
local l = linkByName[string.lower(name)]
return l, name, count, maxStack, price
end
-- total of an item (by name) in bags; a stack inside the auction slot is counted once
function Atr_CountItemInBags(itemName)
local total, lockedCounts = 0, {}
local lname = itemName and string.lower(itemName)
for bag = 0, 4 do
for slot = 1, (GetContainerNumSlots(bag) or 0) do
local link = GetContainerItemLink(bag, slot)
if link then
local _, _, nm = string.find(link, "%[(.-)%]")
if nm and string.lower(nm) == lname then
local _, count, locked = GetContainerItemInfo(bag, slot)
total = total + (count or 1)
if locked then lockedCounts[count or 1] = true end
end
end
end
end
local slotLink, slotName, slotCount = Atr_SellSlotLink()
if slotName and string.lower(slotName) == lname and slotCount and not lockedCounts[slotCount] then
total = total + slotCount
end
return total
end
----------------------------------------------------------------------
-- auction house call adapters
----------------------------------------------------------------------
-- 1.12 has no getAll; class/subclass/slot indexes of 0 mean "any" and must be nil
function Atr_QueryAuctionItems(name, minLevel, maxLevel, invType, itemClass, itemSubclass, page, usable, quality)
if minLevel == "" then minLevel = nil end
if maxLevel == "" then maxLevel = nil end
if invType == 0 then invType = nil end
if itemClass == 0 then itemClass = nil end
if itemSubclass == 0 then itemSubclass = nil end
if usable == 0 then usable = nil end
if quality == 0 or quality == -1 then quality = nil end
QueryAuctionItems(name or "", minLevel, maxLevel, invType, itemClass, itemSubclass, page or 0, usable, quality)
end
function Atr_CanSendAuctionQuery()
local can = CanSendAuctionQuery()
return can, false
end
-- durations: 1,2,3 as Auctionator uses them -> minutes understood by StartAuction on this server
AtrNS.durationMinutes = { 120, 480, 1440 }
AtrNS.durationText = { "6 hours", "24 hours", "72 hours" }
----------------------------------------------------------------------
-- multi stack posting (the 3.3 client did this in StartAuction itself)
----------------------------------------------------------------------
local post = nil
local driver = CreateFrame("Frame", "AtrCompatDriver")
local acc = 0
local function FindStack(id, size)
local best, bestCount
for bag = 0, 4 do
for slot = 1, (GetContainerNumSlots(bag) or 0) do
local link = GetContainerItemLink(bag, slot)
if link and LinkId(link) == id then
local _, count, locked = GetContainerItemInfo(bag, slot)
if count and not locked and count >= size then
if count == size then return bag, slot, count end
if not bestCount or count < bestCount then
best = { bag, slot }
bestCount = count
end
end
end
end
end
if best then return best[1], best[2], bestCount end
return nil
end
local function ClearSlot()
if not GetAuctionSellItemInfo() then return end
if CursorHasItem() and G.ClearCursor then ClearCursor() end
ClickAuctionSellItemButton()
DropCursorAnywhere()
end
local function Finish(msg)
local p = post
post = nil
if p and msg and G.Atr_OnAuctionMultiSellFailureMsg then
Atr_OnAuctionMultiSellFailureMsg(msg)
elseif msg and DEFAULT_CHAT_FRAME then
DEFAULT_CHAT_FRAME:AddMessage("|cff00ffffAuctionator:|r " .. msg)
end
ClearSlot()
if p and p.multi and p.done < p.n and G.Atr_OnAuctionMultiSellFailure then
Atr_OnAuctionMultiSellFailure()
end
end
local function Step()
local p = post
if not p then return end
local now = GetTime()
if not AtrNS.ahOpen and AuctionFrame and not AuctionFrame:IsVisible() then
Finish("Auction house closed, posting stopped.")
return
end
if p.state == "prep" then
if now < p.t then return end
local link, name, count = Atr_SellSlotLink()
if name then
if LinkId(link) == p.id and count == p.size then
p.state = "ready"
return
end
ClearSlot()
p.t = now + 0.4
return
end
local bag, slot, count2 = FindStack(p.id, p.size)
if not bag then
Finish("Not enough items to finish posting (" .. p.done .. " of " .. p.n .. " posted).")
return
end
if CursorHasItem() and G.ClearCursor then ClearCursor() end
if count2 == p.size then
PickupContainerItem(bag, slot)
else
SplitContainerItem(bag, slot, p.size)
end
ClickAuctionSellItemButton()
p.state = "placing"
p.t = now
elseif p.state == "placing" then
local link, name, count = Atr_SellSlotLink()
if name and count == p.size then
p.state = "ready"
elseif now - p.t > 3 then
if CursorHasItem() then DropCursorAnywhere() end
Finish("Could not place the item in the auction slot (" .. p.done .. " of " .. p.n .. " posted).")
end
elseif p.state == "ready" then
StartAuction(p.bid, p.buyout, p.minutes)
p.state = "posting"
p.t = now
elseif p.state == "posting" then
local name = GetAuctionSellItemInfo()
if (not name) and now - p.t > 0.3 then
p.done = p.done + 1
if p.multi and G.Atr_OnAuctionMultiSellUpdate then
local sv1, sv2 = G.arg1, G.arg2
G.arg1, G.arg2 = p.done, p.n
Atr_OnAuctionMultiSellUpdate()
G.arg1, G.arg2 = sv1, sv2
end
if p.done >= p.n then
post = nil
else
p.state = "prep"
p.t = now + 0.3
end
elseif now - p.t > 6 then
Finish("The server did not confirm the auction (" .. p.done .. " of " .. p.n .. " posted).")
end
end
end
driver:SetScript("OnUpdate", function()
if not post then return end
acc = acc + arg1
if acc < 0.1 then return end
acc = 0
Step()
end)
driver:RegisterEvent("UI_ERROR_MESSAGE")
driver:RegisterEvent("AUCTION_HOUSE_CLOSED")
driver:SetScript("OnEvent", function()
if not post then return end
if event == "AUCTION_HOUSE_CLOSED" then
post = nil
elseif event == "UI_ERROR_MESSAGE" and (post.state == "placing" or post.state == "ready" or post.state == "posting") then
Finish("Posting stopped: " .. tostring(arg1))
end
end)
function Atr_PostingInProgress()
return post ~= nil
end
-- startingBid / buyout are per stack; duration is 1,2,3
function Atr_StartAuction(startingBid, buyout, duration, stackSize, numStacks)
if post then return end
local link, name, count = Atr_SellSlotLink()
local id = LinkId(link)
stackSize = stackSize or count or 1
numStacks = numStacks or 1
if not name then return end
local minutes = AtrNS.durationMinutes[duration] or duration or 1440
if numStacks == 1 and count == stackSize then
StartAuction(startingBid, buyout, minutes)
return
end
if not id then
StartAuction(startingBid, buyout, minutes)
return
end
post = { id = id, size = stackSize, n = numStacks, done = 0, bid = startingBid, buyout = buyout,
minutes = minutes, state = "prep", t = GetTime(), multi = (numStacks > 1) }
if post.multi and G.Atr_OnAuctionMultiSellStart then Atr_OnAuctionMultiSellStart() end
end
----------------------------------------------------------------------
-- selected row in the lists. The 3.x code locked the button in its PUSHED state; the list buttons have
-- no NormalTexture and the 1.12 client never hid the pushed texture again, so every clicked row stayed
-- yellow. The rows now carry their own selection texture instead (the XML PushedTextures are removed).
----------------------------------------------------------------------
function Atr_SetRowSelected(row, on)
local sel = row.atrSel
if not sel then
sel = row:CreateTexture(nil, "BACKGROUND")
sel:SetTexture("Interface\\HelpFrame\\HelpFrameButton-Highlight")
sel:SetAllPoints(row)
local name = row:GetName() or ""
if string.find(name, "^AuctionatorEntry") then
sel:SetTexCoord(0.25, 0.35, 0.0, 0.6)
else
sel:SetTexCoord(0.15, 0.25, 0.0, 0.6)
if sel.SetBlendMode then sel:SetBlendMode("ADD") end
end
row.atrSel = sel
end
if on then sel:Show() else sel:Hide() end
end
----------------------------------------------------------------------
-- Blizzard coins instead of g/s/c letters (1.12 text cannot hold inline textures, so prices use small
-- money frames: tooltips, the Stack Price column, the deposit line)
----------------------------------------------------------------------
local moneyFrames = 0
function Atr_CreateMoneyFrame(parent, moneyType)
moneyFrames = moneyFrames + 1
local f = CreateFrame("Frame", "AtrMoneyFrame" .. moneyFrames, parent, "SmallMoneyFrameTemplate")
local saved = G.this
G.this = f
f.small = 1
MoneyFrame_SetType(moneyType or "STATIC")
G.this = saved
return f
end
function Atr_SetMoney(frame, money)
MoneyFrame_Update(frame:GetName(), money or 0)
end
-- tooltips: one pooled money frame per priced line, hidden whenever the tooltip is reused or hidden
local function TipHideMoney(tip, resetWidth)
if tip.atrMoney then
for i = 1, getn(tip.atrMoney) do tip.atrMoney[i]:Hide() end
end
tip.atrMoneyUsed = 0
if resetWidth and tip.atrMinWidth then
tip.atrMinWidth = nil
tip:SetMinimumWidth(0)
end
end
local function TipHook(tip)
if tip.atrMoney then return end
tip.atrMoney = {}
local origHide = tip:GetScript("OnHide")
tip:SetScript("OnHide", function()
TipHideMoney(tip, true)
if origHide then origHide() end
end)
Atr_hooksecurefunc(tip, "SetOwner", function(t) TipHideMoney(tip, true) end)
Atr_hooksecurefunc(tip, "ClearLines", function(t) TipHideMoney(tip, true) end)
end
function Atr_TipBeginMoney(tip)
TipHideMoney(tip, false)
end
function Atr_TipAddMoneyLine(tip, label, money)
TipHook(tip)
tip:AddDoubleLine(label, " ")
local n = tip:NumLines()
local used = (tip.atrMoneyUsed or 0) + 1
tip.atrMoneyUsed = used
local mf = tip.atrMoney[used]
if not mf then
mf = Atr_CreateMoneyFrame(tip, "STATIC")
tip.atrMoney[used] = mf
end
Atr_SetMoney(mf, money)
mf:ClearAllPoints()
local tname = tip:GetName()
local left = getglobal(tname .. "TextLeft" .. n)
local right = getglobal(tname .. "TextRight" .. n)
if right then
mf:SetPoint("RIGHT", right, "RIGHT", 13, 0) -- the coins sit 13px inside the frame's right edge
elseif left then
mf:SetPoint("LEFT", left, "RIGHT", 8, 0)
end
mf:Show()
local need = (left and left:GetStringWidth() or 80) + mf:GetWidth() + 30
if need > (tip.atrMinWidth or 0) then
tip.atrMinWidth = need
tip:SetMinimumWidth(need)
end
end
-- Stack Price column of the auction list rows
function Atr_ShowRowStackMoney(row, money)
local mf = row.atrStackMoney
if not money then
if mf then mf:Hide() end
return
end
if not mf then
mf = Atr_CreateMoneyFrame(row, "STATIC")
mf:SetPoint("RIGHT", row, "LEFT", 573, 0) -- right edge of the 120px column at x=440
row.atrStackMoney = mf
end
Atr_SetMoney(mf, money)
mf:Show()
end
-- "Deposit:" line of the Sell tab
function Atr_ShowDepositMoney(money, suffix)
local mf = AtrNS.depositMoney
if not money then
if mf then mf:Hide() end
return
end
if not mf then
mf = Atr_CreateMoneyFrame(Atr_Deposit_Text:GetParent(), "STATIC")
mf:SetPoint("LEFT", Atr_Deposit_Text, "RIGHT", 2, 0)
local fs = mf:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
fs:SetPoint("LEFT", mf, "RIGHT", -8, 0)
mf.atrSuffix = fs
AtrNS.depositMoney = mf
end
Atr_SetMoney(mf, money)
mf.atrSuffix:SetText(suffix or "")
mf:Show()
end
----------------------------------------------------------------------
-- odds and ends the 3.x code takes for granted
----------------------------------------------------------------------
function Atr_ItemStackSize(link)
if not link then return 0 end
local _, _, _, _, _, _, _, stack = Atr_GetItemInfo(link)
return stack or 0
end
-- no sort-clear call on 1.12; Auctionator only used it to start from a known state
function Atr_SortAuctionClearSort(kind)
end
-- the stock helper returns nil until the first scroll
do
local origGetOffset = G.FauxScrollFrame_GetOffset
if origGetOffset then
function FauxScrollFrame_GetOffset(frame)
return origGetOffset(frame) or 0
end
end
end
if not G.ClearCursor then
function ClearCursor()
if CursorHasItem() then Atr_DropCursorAnywhere() end
end
end
-- addon memory reporting does not exist on 1.12 (only used by /atr mem)
if not G.UpdateAddOnMemoryUsage then
function UpdateAddOnMemoryUsage() end
end
if not G.GetAddOnMemoryUsage then
function GetAddOnMemoryUsage() return 0 end
end
if not G.GetAddOnMetadata then
function GetAddOnMetadata() return Atr_GetVersion() end
end
+34
View File
@@ -0,0 +1,34 @@
## Interface: 11200
## Title: OctoAuctionator
## Version: 1.0.0
## Author: John The Baptist (port of Auctionator 2.6.2 by Zirco)
## Notes: Auctionator 2.6.2 ported to the 1.12 client: Buy, Sell and More tabs, shopping lists, Full Scan, tooltip prices
## OptionalDeps: SellValue
## SavedVariablesPerCharacter: AUCTIONATOR_SHOW_ST_PRICE, AUCTIONATOR_ENABLE_ALT, AUCTIONATOR_OPEN_FIRST, AUCTIONATOR_OPEN_BUY, AUCTIONATOR_OPEN_ALL_BAGS, AUCTIONATOR_DEF_DURATION, AUCTIONATOR_SHOW_TIPS, AUCTIONATOR_V_TIPS, AUCTIONATOR_A_TIPS, AUCTIONATOR_D_TIPS, AUCTIONATOR_SHIFT_TIPS, AUCTIONATOR_DE_DETAILS_TIPS, AUCTIONATOR_DEFTAB
## SavedVariables: AUCTIONATOR_SAVEDVARS, AUCTIONATOR_PRICING_HISTORY, AUCTIONATOR_SHOPPING_LISTS, AUCTIONATOR_PRICE_DATABASE, AUCTIONATOR_LAST_SCAN_TIME, AUCTIONATOR_TOONS, AUCTIONATOR_STACKING_PREFS, AUCTIONATOR_SCAN_MINLEVEL
Compat.lua
Locales\enUS.lua
zcUtils.lua
Auctionator.lua
AuctionatorShop.lua
Auctionator.xml
AuctionatorLocalize.lua
AuctionatorPane.lua
AuctionatorScan.lua
AuctionatorQuery.lua
AuctionatorHints.lua
AuctionatorVendor.lua
AuctionatorConflicts.lua
AuctionatorAPI.lua
AuctionatorConfig.lua
AuctionatorBuy.lua
AuctionatorConfig.xml
AuctionatorOptions.lua
+49 -3
View File
@@ -1,3 +1,49 @@
# OctoAuctionator
Classic Auctionator 2.6.2 port for OctoWoW 1.18.1. Buy, Sell and More tabs. Made by John The Baptist; original addon by Zirco.
# OctoAuctionator 1.0.0
The classic **Auctionator 2.6.2** auction house (Buy / Sell / More... tabs) ported to **OctoWoW 1.18.1** (1.12.1 client).
A free addon.
**Made by John The Baptist.**
## What it does
- **Buy:** type a name and press Search. Results fill in while the pages are still coming in, so you can already pick
a row and press Buy (the scan pauses for the purchase and then carries on). Click **Item Price** to flip
cheapest-first / most-expensive-first. Recent searches and your own shopping lists sit on the left.
- **Sell:** drop an item in the slot and it searches that item, shows the current auctions and recommends a buyout
just under the cheapest one. Set the stack size and number of stacks, pick 6 / 24 / 72 hours, Create Auction.
- **More...:** your own items: what you have on the AH right now (check for undercuts) and everything you posted before.
- **Full Scan:** optional. Scans the whole auction house and stores the lowest prices for the tooltips.
- **Tooltips:** auction price of an item (and the vendor price if you use SellValue), shown with the game's own coins.
**About speed:** the server sends 50 auctions per page and only while the auction window is open; the 1.12 client has
no "get all". A normal search is a page or a few; a Full Scan of a big auction house takes a while.
## Install
1. Download **OctoAuctionator-1.0.0.zip** from Releases or the forum attachment.
2. Extract it into your WoW `Interface/AddOns` folder.
3. Restart WoW and enable **OctoAuctionator** at character selection.
The folder must contain `Interface/AddOns/OctoAuctionator/OctoAuctionator.toc`.
Turn off other auction house addons (aux, other Auctionator versions) to avoid two UIs fighting over the window.
**SellValue** is optional (vendor prices in tooltips).
## Usage
- Open the auction house: the **Buy**, **Sell** and **More...** tabs are next to Browse / Bids / Auctions.
- The **Auctionator** button (top right) opens the options; **Full Scan...** starts a full scan.
- `/atr clear fullscandb` clears the stored scan prices, `/atr clear posthistory` clears your posting history.
## Screenshots
![Buy tab](screenshots/octoauctionator-buy.jpg)
![Sell tab](screenshots/octoauctionator-sell.jpg)
## Credits
Original addon: **Auctionator 2.6.2 by Zirco** (WoWInterface). This version keeps his interface and code and adapts
them to the 1.12 client and this server.
*It's not much, but it's honest work.*
+857
View File
@@ -0,0 +1,857 @@
-- Zirco's utilities
-- This module should contain no globals as it is intended to be "linked" in to each of Zirco's addons
local addonName, addonTable = "Auctionator", AtrNS;
local zc = {};
addonTable.zc = zc;
-----------------------------------------
function zc.RGBtoHEX (r,g,b)
local hex = "";
return string.format ("%02x%02x%02x", r * 255, g * 255, b * 255);
end
-----------------------------------------
function zc.EnableDisable (elem, b)
if (b) then
elem:Enable();
else
elem:Disable();
end
end
-----------------------------------------
function zc.ShowHide (elem, b)
if (b) then
elem:Show();
else
elem:Hide();
end
end
-----------------------------------------
function zc.SetTextIf (elem, b, t1, t2)
if (b) then
elem:SetText(t1);
else
elem:SetText(t2);
end
end
-----------------------------------------
function zc.Val (val, ifNilVal)
if (val == nil) then
return ifNilVal;
end
return val;
end
-----------------------------------------
function zc.Min (a, b)
if (a == nil) then
return b;
end
if (b == nil) then
return a;
end
return math.min (tonumber (a), tonumber (b));
end
-----------------------------------------
function zc.Max (a, b)
if (a == nil) then
return b;
end
if (b == nil) then
return a;
end
return math.max (tonumber (a), tonumber (b));
end
-----------------------------------------
function zc.If (b, x, y)
if (b ~= nil and b ~= false) then
return x;
end
return y;
end
-----------------------------------------
function zc.PrintKeysSorted (t)
local ta = {};
for a,v in pairs (t) do
table.insert (ta, a);
end
table.sort (ta, function (a,b) return (string.lower(a) < string.lower(b)); end);
for x = 1, getn(ta) do
zc.msg_pink (x.." "..ta[x]);
end
end
-----------------------------------------
function zc.UTF8_Truncate (s, newlen)
if (string.len (s) <= newlen) then
return s;
end
local x, c;
for x = newlen, 1, -1 do
c = string.byte (s, x+1);
if (c ~= nil and (c < 128 or c >= 192)) then -- the next byte starts a character, so cut here
return string.sub (s, 1, x);
end
end
return string.sub (s, 1, newlen);
end
-----------------------------------------
function zc.GetArrayElemOrFirst (a, x)
if (a and getn(a) > 0) then
if (x == nil or x < 1 or x > getn(a)) then
x = 1;
end
return a[x];
end
return nil;
end
-----------------------------------------
function zc.GetArrayElemOrNil (a, x)
if (a and getn(a) > 0) then
if (x == nil or x < 1 or x > getn(a)) then
return nil;
end
return a[x];
end
return nil;
end
-----------------------------------------
function zc.padstring (s, n, c)
while (string.len (s) < n) do
s = c..s;
end
return s;
end
-----------------------------------------
local encTable = {"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z",
"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z",
"0","1","2","3","4","5","6","7","8","9",
"-", "_" };
local decTable;
-----------------------------------------
local function BuildDecTable()
if (decTable == nil) then
decTable = {};
local i;
for i = 1,64 do
decTable[encTable[i]] = i-1;
end
end
end
-----------------------------------------
function zc.enc64 (n)
if (n == 0) then
return encTable[1];
end
local k = n;
local result = "";
while k > 0 do
local x = mod (k, 64);
result = encTable[x+1]..result;
k = floor (k / 64);
end
return result;
end
-----------------------------------------
function zc.dec64 (s)
if (s == nil or s == "") then
return 0;
end
BuildDecTable();
local result = 0;
local len = string.len (s);
local x;
for x = 1, len do
result = result * 64;
result = result + decTable[string.sub(s,x,x)];
end
return result;
end
-----------------------------------------
function ZF (s)
BuildDecTable();
local s2 = "";
local n;
for n = 1, string.len(s) do
local c = string.sub(s, n,n);
local x = decTable[c];
if (x == nil) then
s2 = s2..c;
else
if (x < 32) then x = x + 32;
else x = x - 32;
end;
s2 = s2..encTable[x+1];
end
end
return s2;
end
-----------------------------------------
function zc.words(str)
local t = {}
local function helper(word) table.insert(t, word) return "" end
local rest = string.gsub (str, "%w+", helper);
if (not string.find (rest, "%S")) then
if (getn(t) == 1) then return t[1]; end;
if (getn(t) == 2) then return t[1],t[2]; end;
if (getn(t) == 3) then return t[1],t[2],t[3]; end;
if (getn(t) == 4) then return t[1],t[2],t[3],t[4]; end;
if (getn(t) == 5) then return t[1],t[2],t[3],t[4],t[5]; end;
return t;
end
end
-----------------------------------------
local gDeferredCalls = {};
-----------------------------------------
function zc.AddDeferredCall (seconds, funcname, param1, param2, tag) -- tag is optional. if present used to overwrite prior call with same tag
local now = time();
local cfdEntry = {};
cfdEntry.funcname = funcname;
cfdEntry.param1 = param1;
cfdEntry.param2 = param2;
cfdEntry.when = now + seconds;
cfdEntry.tag = "";
if (tag) then
cfdEntry.tag = tag;
for i = 1, getn(gDeferredCalls) do
if (gDeferredCalls[i].tag == tag) then
gDeferredCalls[i] = cfdEntry; -- overwrite
return;
end
end
end
table.insert (gDeferredCalls, cfdEntry);
end
-----------------------------------------
function zc.CheckDeferredCall ()
local now = time();
local i;
for i = 1, getn(gDeferredCalls) do
if (gDeferredCalls[i].when < now) then
local fcn = getglobal(gDeferredCalls[i].funcname);
local p1 = gDeferredCalls[i].param1;
local p2 = gDeferredCalls[i].param2;
table.remove (gDeferredCalls, i);
if (type(fcn) == 'function') then
fcn(p1, p2);
end
return; -- only do one
end
end
end
-----------------------------------------
function zc.periodic (elem, name, period, elapsed)
local t = elem[name] or 0;
t = t + elapsed;
if (t > period) then
elem[name] = 0;
return true;
end
elem[name] = t;
return false;
end
-----------------------------------------
function zc.tableIsEmpty (t)
local n, v;
for n, v in pairs (t) do
return false;
end
return true;
end
-----------------------------------------
function zc.PrintTable (t, indent)
if (not indent) then
indent = 0;
end
local x
local padding = "";
for x = 1,indent do
padding = padding.." ";
end
zc.msg ("-------");
for n, v in pairs (t) do
if (type(v) == "table") then
zc.msg (padding..n, "TABLE");
zc.PrintTable(v, indent+1);
elseif (type(v) == "userdata") then
zc.msg (padding..n, "userdata");
else
zc.msg (padding..n, v);
end
end
end
-----------------------------------------
function zc.ItemIDfromLink (itemLink)
if (itemLink == nil) then
return 0,0,0;
end
-- 1.12 item strings are item:id:enchant:suffix:unique
local found, _, itemString = string.find(itemLink, "^|c%x+|H(.+)|h%[.*%]")
if (itemString == nil) then
found, _, itemString = string.find (itemLink, "(item:[%-%d:]+)");
end
if (itemString == nil) then
return 0,0,0;
end
local _, itemId, _, suffixId, uniqueId = Atr_strsplit (":", itemString)
return itemId, suffixId or 0, uniqueId or 0;
end
-----------------------------------------
function zc.BoolToString (b)
if (b) then
return "true";
end
return "false";
end
-----------------------------------------
function zc.BoolToNum (b)
if (b) then
return 1;
end
return 0;
end
-----------------------------------------
function zc.NumToBool (n)
if (n == 0) then
return false;
end
return true;
end
-----------------------------------------
function zc.pluralizeIf (word, count)
if (count and count == 1) then
return word;
else
return zc.pluralize(word);
end
end
-----------------------------------------
function zc.pluralize (word)
return word.."s";
end
-----------------------------------------
function zc.round (v)
return math.floor (v + 0.5);
end
-----------------------------------------
function zc.msg_red (...) zc.msg_color (1, 0, 0, unpack(arg)); end
function zc.msg_pink (...) zc.msg_color (1, .6, .6, unpack(arg)); end
function zc.msg_yellow (...) zc.msg_color (1, 1, 0, unpack(arg)); end
-----------------------------------------
function zc.msg_color (r, g, b, ...)
local options = {};
options.r = r;
options.g = g;
options.b = b;
zc.msg_ex (options, unpack(arg));
end
-----------------------------------------
function zc.msg_str (...)
local options = {};
options.str = true;
return zc.msg_ex (options, unpack(arg));
end
-----------------------------------------
function zc.msg_atr (...)
zc.msg_yellow ("|cff00ffff<Auctionator>|r", unpack(arg));
end
-----------------------------------------
function zc.HSV2RGB (h, s, v)
local r, g, b;
local hi = mod (math.floor(h/60), 6);
local f = h/60 - math.floor(h/60);
local p = v * (1-s);
local q = v * (1-(f*s));
local t = v * (1-((1-f)*s));
if (hi == 0) then return v, t, p; end
if (hi == 1) then return q, v, p; end
if (hi == 2) then return p, v, t; end
if (hi == 3) then return p, q, v; end
if (hi == 4) then return t, p, v; end
if (hi == 5) then return v, p, q; end
return
end
-----------------------------------------
function zc.md (...)
-- developer trace output: disabled in the 1.12 port
end
-----------------------------------------
function zc.msg (...)
local options = {};
zc.msg_ex (options, unpack(arg));
end
-----------------------------------------
function zc.msg_ex (options, ...)
if (not DEFAULT_CHAT_FRAME) then
return;
end
local msg = "";
local i;
local m;
for i = 1, arg.n do
local v = arg[i];
if (type(v) == "boolean") then m = zc.BoolToString(v);
elseif (type(v) == "table") then m = "<table>";
elseif (v == nil) then m = "<nil>";
else m = v;
end
msg = msg.." "..m;
end
if (options.str) then
return msg;
end
if (options.r ~= nil) then
DEFAULT_CHAT_FRAME:AddMessage (msg, options.r, options.g, options.b);
else
DEFAULT_CHAT_FRAME:AddMessage (msg);
end
end
-----------------------------------------
function zc.val2gsc (v)
local rv = zc.round(v)
local g = math.floor (rv/10000);
rv = rv - g*10000;
local s = math.floor (rv/100);
rv = rv - s*100;
local c = rv;
return g, s, c
end
-----------------------------------------
function zc.priceToString (val)
local gold, silver, copper = zc.val2gsc(val);
local st = "";
if (gold ~= 0) then
st = gold.."g ";
end
if (st ~= "") then
st = st..format("%02is ", silver);
elseif (silver ~= 0) then
st = st..silver.."s ";
end
if (st ~= "") then
st = st..format("%02ic", copper);
elseif (copper ~= 0) then
st = st..copper.."c";
end
return st;
end
-----------------------------------------
local goldicon = "|cffffd100g|r"
local silvericon = "|cffc7c7cfs|r"
local coppericon = "|cffeda55fc|r"
-----------------------------------------
function zc.priceToMoneyString (val, noZeroCoppers)
local gold, silver, copper = zc.val2gsc(val);
local st = "";
if (gold ~= 0) then
st = gold..goldicon.." ";
end
if (st ~= "") then
st = st..format("%02i%s ", silver, silvericon);
elseif (silver ~= 0) then
st = st..silver..silvericon.." ";
end
if (noZeroCoppers and copper == 0) then
return st;
end
if (st ~= "") then
st = st..format("%02i%s", copper, coppericon);
elseif (copper ~= 0) then
st = st..copper..coppericon;
end
return st;
end
-----------------------------------------
function zc.StringSame (s1, s2)
if (s1 == nil and s2 == nil) then
return true;
end
if (s1 == nil or s2 == nil) then
return false;
end
if (s1 == s2) then -- maybe will fix german umlaut problem?
return true;
end
return (string.lower (s1) == string.lower (s2));
end
-----------------------------------------
function zc.StringContains (s, sub)
if (sub == nil or sub == "") then
return false;
end
local start, stop = string.find (string.lower(s), string.lower(sub), 1, true);
return (start ~= nil);
end
-----------------------------------------
function zc.StringEndsWith (s, sub)
if (sub == nil or sub == "") then
return false;
end
local i = string.len(s) - string.len(sub);
if (i < 0) then
return false;
end
local sEnd = string.sub (s, i+1);
return (string.lower (sEnd) == string.lower (sub));
end
-----------------------------------------
function zc.StringStartsWith (s, sub)
if (s == nil or sub == nil or sub == "") then
return false;
end
local sublen = string.len (sub);
if (string.len (s) < sublen) then
return false;
end
return (string.lower (string.sub(s, 1, sublen)) == string.lower(sub));
end
-----------------------------------------
function zc.CopyDeep (src)
local result = {};
for n, v in pairs (src) do
if (type(v) == "table") then
result[n] = zc.CopyDeep(v);
else
result[n] = v;
end
end
return result;
end
-----------------------------------------
function zc.printableLink (link)
if (link == nil) then
return "nil";
end
local printable = gsub(link, "\124", "\124\124");
return printable;
end
-----------------------------------------
function zc.printmem ()
local cmem = math.floor(collectgarbage ("count"))
UpdateAddOnMemoryUsage();
local mem = GetAddOnMemoryUsage("OctoAuctionator");
zc.msg_atr (math.floor(mem).." KB (total LUA: "..cmem.." KB)");
end
-----------------------------------------
function zc.printstack (options)
return {}; -- needs debugstack(), which the 1.12 client does not have
end
-----------------------------------------
function zc.tallyAdd (ttable, value)
if (ttable[value]) then
ttable[value] = ttable[value] + 1;
else
ttable[value] = 1;
end
end
-----------------------------------------
function zc.tallyPrint (ttable, options)
local sortedTable = {};
local total = 0;
local n = 1;
for value,count in pairs(ttable) do
sortedTable[n] = {};
sortedTable[n].value = value;
sortedTable[n].count = count;
total = total + count;
n = n + 1;
end
if (options.sortByValue and options.sortDesc) then table.sort (sortedTable, function(x,y) return x.value > y.value; end);
elseif (options.sortByValue and not options.sortDesc) then table.sort (sortedTable, function(x,y) return x.value < y.value; end);
elseif (options.sortDesc) then table.sort (sortedTable, function(x,y) return x.count > y.count; end);
else table.sort (sortedTable, function(x,y) return x.count < y.count; end);
end
for n = 1, getn(sortedTable) do
if (not options.printCount or n < options.printCount) then
zc.msg_pink (sortedTable[n].count.." "..sortedTable[n].value);
end
end
zc.msg_yellow ("Total: "..total);
end