Module:ItemBox: Difference between revisions

Vergir (talk | contribs)
Rewrite getFontSize: measured Retail Demo advance-width table + fit logic, output em so name scales with the card (with help from vergir-bot LLM)
Vergir (talk | contribs)
Add variants() for stable per-item texture variety (replaces nth-child); add font-table regen recipe to header (with help from vergir-bot LLM)
Line 4: Line 4:
-- ============================================================================
-- ============================================================================
--  Retail Demo (bold) glyph advance widths, in em of the font size.
--  Retail Demo (bold) glyph advance widths, in em of the font size.
--  unitsPerEm = 1000. Measured 2026-07-20 with fontTools from
--  unitsPerEm = 1000. Measured from File:Retaildemo-bold.woff2
--  File:Retaildemo-bold.woff2  ( /images/d/d5/Retaildemo-bold.woff2 ),
-- ( /images/d/d5/Retaildemo-bold.woff2 ), the bold face used by .itembox-name.
--  the bold face used by .modName.
--
--
--  THIS TABLE IS FONT-SPECIFIC. If that file is re-uploaded with different
--  THIS TABLE IS FONT-SPECIFIC. If that file is re-uploaded with different
--  metrics, name fitting will drift; regenerate the table from the new file's
--  metrics, name fitting drifts. Regenerate the table with:
-- hmtx / unitsPerEm.
--
--    curl -o rd.woff2 https://deadlock.wiki/images/d/d5/Retaildemo-bold.woff2
--    fontforge -lang=py -c 'import fontforge;f=fontforge.open("rd.woff2");f.generate("rd.ttf")'
--    python3 - << 'PY'
--    from fontTools.ttLib import TTFont; import string
--   f=TTFont("rd.ttf"); upm=f["head"].unitsPerEm; hm=f["hmtx"]; uni={}
--    for t in f["cmap"].tables:
--        if t.isUnicode():
--            for cp,gn in t.cmap.items(): uni.setdefault(cp,gn)
--    for c in string.ascii_uppercase+string.ascii_lowercase+string.digits+" ":
--        gn=uni.get(ord(c))
--        if gn: print(f'    ["{c}"]={hm[gn][0]/upm:.3f},')
--    PY
-- ============================================================================
-- ============================================================================
local WIDTH = {
local WIDTH = {
Line 31: Line 42:


-- Card geometry in px at the reference scale where 1em = 10px. The card is
-- Card geometry in px at the reference scale where 1em = 10px. The card is
-- scaled through font-size on .CitadelShopMod (the |size= parameter) and the
-- scaled through font-size on .itembox (the |size= parameter) and the value
-- value returned here is em, so it scales together with everything else.
-- returned here is em, so it scales together with everything else.
local USABLE_W = 72    -- 80px card minus 4px padding on each side
local USABLE_W = 72    -- 80px card minus 4px padding on each side
local NAME_H  = 42    -- vertical room the wrapped name may occupy
local NAME_H  = 42    -- vertical room the wrapped name may occupy
local LINE_H  = 1.2  -- must match .modName line-height
local LINE_H  = 1.2  -- must match .itembox-name line-height
local MAX_PX  = 15    -- full size  (1.50em)
local MAX_PX  = 15    -- full size  (1.50em)
local MIN_PX  = 8    -- hard floor (0.80em)
local MIN_PX  = 8    -- hard floor (0.80em)
Line 107: Line 118:


     return string.format("%.2f", snapped / 10)
     return string.format("%.2f", snapped / 10)
end
-- Stable per-item texture variety, replacing position-dependent :nth-child.
-- A djb2 hash of the (language-independent) code name picks one of each of the
-- icon-mask / wear / paper-texture variants. Same item -> same look everywhere.
local function hash(s)
    local h = 5381
    for i = 1, #s do
        h = (h * 33 + s:byte(i)) % 2147483647
    end
    return h
end
function p.variants(frame)
    local h = hash(frame.args[1] or "")
    local icon = (h % 3) + 1
    local wear = (math.floor(h / 3) % 4) + 1
    local tex  = (math.floor(h / 12) % 3) + 1
    return string.format("itembox-icon-v%d itembox-wear-v%d itembox-texture-v%d",
        icon, wear, tex)
end
end


return p
return p