Skip to content

Item drawing: icon and tooltip as 1.14d draws them - #9

Merged
jaenster merged 26 commits into
mainfrom
item-draw
Oct 22, 2026
Merged

jaenster merged 26 commits into
mainfrom
item-draw

Conversation

@jaenster

@jaenster jaenster commented Oct 13, 2026 •

Copy link
Copy Markdown
Owner

Draws an item exactly as Diablo II 1.14d does: the inventory icon, the hover tooltip, and the card that combines them.

Text (d2-item)

  • statdesc.zig: the stat lines (descfuncs 1-28, stat groups, damage pairs, and the unstable descpriority sort).
  • itemdesc.zig: the full card as LoadItemDesc (0x48dd90) builds it, with names per quality, set cards and requirements.

Pictures (new packages/itemdraw), in two stages

  • Layout: a display list in 1.14d pixels (box, lines as colour runs with each glyph's Font16 frame and cell, icon and socket frame references with colour map and draw-mode alpha, tinted cells). JSON-serialisable, plus a plain-text export capped at 1024 bytes for alt text.
  • Raster: draws a layout at any integer scale through glyph and sprite providers. libd2's own providers paint the 1.14d DC6 font and inventory art, byte-identical to the game at scale 1. A glyph provider may paint past its cell (declared pad) and gets the colour's RGB, for vector text with outlines.

One canvas: d2-formats' canvas now has straight-alpha put, fill, scaled remapped blits, baseline blits and compositing; itemdraw and render's object sprites draw through it (byte-identical).

Bindings, generated from the C headers (tools/bindgen, Zig)

  • Checks every header against its package's capi.zig (exports, parameter counts, ABI number), then writes the raw layer for Rust (sys), Go (zz_abi.go, purego), .NET (Native.g.cs) and TypeScript (abi.ts), each with a smoke test.
  • zig build bindings / zig build check-bindings (in CI).
  • The hand wrappers now sit on the generated layer with unchanged public APIs; item drawing with layouts and providers in all four: Rust traits, Go interfaces (purego callbacks), .NET abstract classes (function pointers), npm ItemDrawer + rasterizeLayout (providers in JS).
  • Rust links one combined static library; npm ships items.wasm (item, itemdraw) apart from the map module so map users do not download the item tables.
  • net and util are reachable from every language.

One graphics module: d2-formats' canvas holds the RGBA canvas, palette blits with colour map and alpha, fill, composite, index-space draw and nearest scaling; MMPX and PNG stay in d2-util (domain-free; formats is zero-dep). itemdraw, render's object sprites and tools/sprite use it, their copies are gone, output byte-identical.

Test data: one top-level testdata/ (two engine saves, two kits, the goldens, layout goldens) that every package and language reads. Generated, not committed: the kit character's save (composed in the test) and the binding fixtures (zig build fixtures, run by CI before the language tests).

Generated files carry a "do not edit" header and are linguist-generated in .gitattributes.

Size (lines vs main; the charkit commits come from local main, not yet on origin/main)

part before dedupe after
hand-written code +6056 -17 +9189 -696
generated (bindgen output) 0 +5374
test data (text) +2965 +2679 (of which goldens 2562)
test data (binary) 16 files, 17.8 KB 2 saves, 6.1 KB
charkit +3193 +3195 -2
docs +148 +218

"After" hand-written includes bindgen (1.2k), the binding wrappers (2.5k, net of the 584 hand ABI lines the generator replaced), itemdraw (2.5k) and the item text (2.5k).

Tests

  • 7 descfunc tests; goldens over 132 real items (396 pictures), unchanged through the split.
  • Layout JSON golden, a box glyph provider at scale 2 and 4 on the same layout, C-callback and padded providers.
  • Go, Rust, .NET (docker, linux-arm64), npm: generated smoke tests plus the shared fixtures, layout JSON and a provider at scale 2/4.

Design and the 1.14d functions each part follows: docs/item-draw.md; bindings: docs/BUILDING.md.

Before merge

  • Compare with in-game screenshots, using the list in docs/item-draw.md.
  • EpicAma item 26 shows Enigma's stats under the name "Duress", with runes Shael Um Thul. It could be the test character's data or a naming bug; check which.
  • Not covered yet: shop cost lines, missile potion damage, quest-difficulty red names, stats that change with time of day, fractional scales.

…ority order, descfuncs 1 to 28, stat groups, damage pairs, the undead and Indestructible lines
…order and colours, names per quality, set cards, the numbers from base stats plus the percent and per-level ops, requirements, attack speed
… current durability; the save inspector now shows the durability line, with the max-durability percent and without it on throwing weapons
… second mindam/maxdam is empty, the smite damage is in the first); gem slot lines drop their trailing newline as the engine's affix descriptions do
…colour map, sockets and ethereal look, the hover tooltip box in Font16, and the two together as on hover; C ABI, a CLI, and goldens over real items
…m imports d2-item as a module so both shims link into one wasm; personalised names follow the engine's possessive; the item-draw CLI writes each item's records
…he d2itemdraw C ABI, checked against fixtures the Zig side writes through the same ABI; CI builds and embeds the native
…worn in gold, from Sets.txt, as SKILLDESC_AppendSetBonusTextByStateValue reads them
…ox, lines as positioned glyph cells, icon and socket frame references) and a rasterizer that paints it through glyph and sprite providers; the 1.14d DC6 providers keep every picture byte-identical
…t 1024 bytes, and rasterizing through C callback providers that fall back to the 1.14d art
…res, a lazy module the Zig tests import; the kit character is built from its spec in the test instead of committed, and the copies in itemdraw and the Go package are gone
… remapped blits, baseline blits and compositing; itemdraw's image module and its raster compositing, and render's object compositing, now draw through it with byte-identical output
…d the drawer d2-formats' font table, which now keeps the header height and the Latin glyph lookup
… on every side) and gets the colour's nominal RGB, for vector text with outlines
…the C headers, each header checked against its package's exports and ABI number, with a smoke test per language; zig build bindings / check-bindings; the Go packages now sit on the generated API, and net and util are reachable from Go
… generated layer: drlg on the generated declarations, a safe itemdraw module with layouts and provider traits over the callback struct, all of sys public; util joins the combined wasm and native
…youts, alt text and glyph/sprite providers through function pointers; LibD2.Tests runs the generated smoke test and the shared fixtures, in CI too
…y's ABI is checked on load; item and itemdraw ship as a second module, items.wasm, loaded only by ItemDrawer; ItemDrawer with text, alt text, images, layouts and the default raster, and rasterizeLayout drawing a display list through JavaScript providers; util joins the map module
…tools/sprite drops its own copies (compose and nearest render byte-identical)
…written by zig build fixtures (CI runs it before the language tests) instead of committed, and the layout goldens move to testdata/itemdraw/layouts
…poses the raw layer; docs: layout and raster, providers, the bindings per language, bindgen and the shared test data
@jaenster
jaenster merged commit e36122e into main Oct 22, 2026
6 of 12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant