Files
DopingControl/ui/cellsheet.lua
T
2026-08-30 15:37:17 +02:00

119 lines
4.8 KiB
Lua

-- DopingControl ui/cellsheet.lua
--
-- The matrix draws one small box per cell: a solid background with a 1px
-- border whose color carries the state. Done with SetBackdrop that costs NINE
-- texture regions per cell (measured in the client: bare frame 0, backdrop
-- with bgFile only 1, backdrop with edgeFile 9). At 805 cells that is ~7200
-- regions of pure decoration.
--
-- Instead every box is baked into one sheet and a cell owns a single texture
-- region that picks its box with SetTexCoord. The border is part of the image,
-- so the result is pixel-identical -- the sheet is generated from the same
-- DC.COLORS palette by tools/gen-cellsheet.js.
--
-- Pure Lua on purpose: no WoW API at file scope, so the offline tests can
-- dofile it under lua50.exe.
DC_Cells = {}
DC_Cells.SHEET = {
path = "Interface\\AddOns\\DopingControl\\textures\\cells",
size = 256, -- power of two, mandatory: a 12x12 TGA fails silently
tileW = 64, -- holds the widest sprite (34) with room to spare
tileH = 32, -- holds CELL_H (23)
perRow = 4, -- 256 / 64
}
-- Cell height never varies; only the width does (trinket columns are 34).
DC_Cells.CELL_H = 23
DC_Cells.WIDTHS = { 30, 34 }
-- Every (background, border) pair a CELL can take, in sheet order. Borders
-- given as a number are DC.QUALITY indices, everything else is a DC.COLORS
-- key. Order is the wire format of the sheet: appending is safe, reordering
-- means regenerating the TGA.
DC_Cells.VARIANTS = {
{ name = "HAS", bg = "hatBg", border = "hatBorder" },
{ name = "MISSING", bg = "fehltBg", border = "fehltBorder" },
{ name = "UNKNOWN", bg = "unbekBg", border = "unbekBorder" },
{ name = "SKIP", bg = "skip", border = "skipBorder" },
{ name = "NEUTRAL", bg = "theadBg", border = "line" },
{ name = "ITEM_HAS", bg = "theadBg", border = "hatBorder" },
{ name = "ITEM_MISSING", bg = "theadBg", border = "fehltBorder" },
{ name = "Q_RARE", bg = "theadBg", border = "rareBorder" },
{ name = "Q_EPIC", bg = "theadBg", border = "epicBorder" },
{ name = "Q_0", bg = "theadBg", border = 0 },
{ name = "Q_1", bg = "theadBg", border = 1 },
{ name = "Q_2", bg = "theadBg", border = 2 },
{ name = "Q_5", bg = "theadBg", border = 5 },
{ name = "Q_6", bg = "theadBg", border = 6 },
}
DC_Cells.INDEX = {}
for i = 1, table.getn(DC_Cells.VARIANTS) do
DC_Cells.INDEX[DC_Cells.VARIANTS[i].name] = i
end
-- Tile slot of a variant/width pair: each variant owns two consecutive tiles,
-- one per width, in DC_Cells.WIDTHS order.
local function slotOf(name, w)
local vi = DC_Cells.INDEX[name]
if not vi then
return nil
end
for k = 1, table.getn(DC_Cells.WIDTHS) do
if DC_Cells.WIDTHS[k] == w then
return (vi - 1) * table.getn(DC_Cells.WIDTHS) + (k - 1)
end
end
return nil
end
DC_Cells.SlotOf = slotOf
-- SetTexCoord arguments for one box. Returns four numbers or nil -- nil so a
-- typo shows up as a missing box instead of silently sampling tile 0.
function DC_Cells.Coords(name, w)
local slot = slotOf(name, w)
if not slot then
return nil
end
local S = DC_Cells.SHEET
local col = math.mod(slot, S.perRow)
local row = math.floor(slot / S.perRow)
local x0 = col * S.tileW
local y0 = row * S.tileH
return x0 / S.size,
(x0 + w) / S.size,
y0 / S.size,
(y0 + DC_Cells.CELL_H) / S.size
end
-- State + slot kind + item quality -> variant name. Mirrors exactly what the
-- old SetBackdropG call sites in ui/matrix.lua chose -- which, for a nil
-- quality, differed BY KIND (see ui/matrix.lua's M.PaintItemTile, old lines
-- 1965-1990, confirmed at tools/luatests/test_cellsheet.lua):
-- * trinket: M.qualityColors(item.quality) ran unconditionally, and for
-- quality == nil that resolves to DC.QUALITY[1] (common/white) via the
-- `q or 1` fallback -- state-INDEPENDENT.
-- * ench: quality == nil skipped M.qualityColors and fell back to
-- C.hatBorder/C.fehltBorder/C.line BY STATE -- exactly
-- ITEM_HAS/ITEM_MISSING/NEUTRAL.
-- So trinket's nil-quality case must NOT share ench's state-based fallback.
function DC_Cells.VariantFor(state, kind, quality)
if kind == "ench" or kind == "trinket" then
if quality == 3 then return "Q_RARE" end
if quality == 4 then return "Q_EPIC" end
if quality ~= nil then return "Q_" .. quality end
if kind == "trinket" then
return "Q_1"
end
if state == "HAS" then return "ITEM_HAS" end
if state == "MISSING" then return "ITEM_MISSING" end
return "NEUTRAL"
end
if state == "HAS" then return "HAS" end
if state == "MISSING" then return "MISSING" end
if state == "UNKNOWN" then return "UNKNOWN" end
return "SKIP"
end