From cdd379c5e4a24d3f88cef60cf647a9ebc325093c Mon Sep 17 00:00:00 2001 From: jwinterm Date: Sat, 11 Jul 2026 07:07:55 -0400 Subject: [PATCH] feat(render): composite object tiles over a base tile (pipeline) + bigger cat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The black boxes around furniture/windows were transparent object tiles drawn straight onto the dark canvas. Fix at the pipeline level, not as a tavern one-off: - tileRenderer: a tile kind whose legend declares `over` (the ground it sits on) now draws that base tile FIRST, then the object on top — so the object's transparent areas show the floor/wall. Works for any scene, atlas or flat-colour fallback. Wall-grounding shadow strip now only applies to real walls, not objects. - sceneModel TAVERN_LEGEND: declares over='floor' for bar/keg/shelf/table/chair/ barrel/crate/rug and over='wall' for windows. - catSprites: the cat occupies ~40% of its 50x50 frame, so scale to ~2x the cell and centre it — it now reads about tile-sized instead of tiny. Client-only. Suite 57 suites / 413 tests green. --- html/js/render/catSprites.js | 6 ++++-- html/js/render/sceneModel.js | 23 +++++++++++++---------- html/js/render/tileRenderer.js | 17 +++++++++++++++-- 3 files changed, 32 insertions(+), 14 deletions(-) diff --git a/html/js/render/catSprites.js b/html/js/render/catSprites.js index a7b2b39..d49304b 100644 --- a/html/js/render/catSprites.js +++ b/html/js/render/catSprites.js @@ -27,9 +27,11 @@ if (!im.complete || !im.naturalWidth) return false; var frame = Math.floor((now || 0) / FRAME_MS) % FRAMES; var sx = frame * FW; - var size = cell * 1.25; // a touch bigger than a tile + // The cat occupies only ~40% of its 50x50 frame (lots of transparent padding), so scale + // the frame to ~2x the cell and centre it — the visible cat then reads about tile-sized. + var size = cell * 2.0; var dx = e.x * cell + (cell - size) / 2; - var dy = e.y * cell + cell - size + cell * 0.12; + var dy = e.y * cell + (cell - size) / 2 + cell * 0.2; // centred, nudged down onto the tile ctx.save(); ctx.imageSmoothingEnabled = false; if (e.facing === 'left') { diff --git a/html/js/render/sceneModel.js b/html/js/render/sceneModel.js index af0eee6..56f814a 100644 --- a/html/js/render/sceneModel.js +++ b/html/js/render/sceneModel.js @@ -7,19 +7,22 @@ 'use strict'; // Tile kinds and how each renderer treats them (glyph for ASCII, colour for tiled/fancy). + // `over` names the ground tile an object sits on, so the renderer composites the object over it + // (its transparent areas show the floor/wall, not the dark canvas). Ground tiles (wall/floor/rug) + // have no `over` — they fill the cell. var TAVERN_LEGEND = { wall: { char: '#', color: '#3a3f4b', solid: true }, - window: { char: 'W', color: '#4a6a8a', solid: true }, + window: { char: 'W', color: '#4a6a8a', solid: true, over: 'wall' }, floor: { char: '·', color: '#6b4d33', solid: false }, // warm wood, not the old dark green - rug: { char: '=', color: '#7a3b39', solid: false }, - bar: { char: '=', color: '#6a4a2a', solid: true }, - keg: { char: 'k', color: '#5a4028', solid: true }, - shelf: { char: 'h', color: '#4a3a28', solid: true }, - table: { char: 'T', color: '#6a4a2a', solid: true }, - chair: { char: 'c', color: '#5a4028', solid: false }, // walkable — stand on it to "sit" - barrel: { char: 'B', color: '#5a4028', solid: true }, - crate: { char: 'C', color: '#7a5a38', solid: true }, - door: { char: 'D', color: '#3fb950', solid: false } + rug: { char: 'r', color: '#7a3b39', solid: false, over: 'floor' }, + bar: { char: '=', color: '#6a4a2a', solid: true, over: 'floor' }, + keg: { char: 'k', color: '#5a4028', solid: true, over: 'floor' }, + shelf: { char: 'h', color: '#4a3a28', solid: true, over: 'floor' }, + table: { char: 'T', color: '#6a4a2a', solid: true, over: 'floor' }, + chair: { char: 'c', color: '#5a4028', solid: false, over: 'floor' }, // walkable — stand on it to "sit" + barrel: { char: 'B', color: '#5a4028', solid: true, over: 'floor' }, + crate: { char: 'C', color: '#7a5a38', solid: true, over: 'floor' }, + door: { char: 'D', color: '#3fb950', solid: false, over: 'floor' } }; // Layout character -> tile kind. diff --git a/html/js/render/tileRenderer.js b/html/js/render/tileRenderer.js index bf5f78b..a1398d9 100644 --- a/html/js/render/tileRenderer.js +++ b/html/js/render/tileRenderer.js @@ -107,9 +107,18 @@ var def = scene.legend[kind] || { color: '#333' }; var coord = useAtlas ? pickVariant(tmap[kind], x, y) : null; if (coord) { + // Object tiles (furniture, windows, torches, treasure…) are transparent + // around the object. Legend `over` names the ground they sit on (floor/wall); + // draw that first so the transparency shows the room, not the dark canvas. + // Pipeline-level: any scene can declare it, not a tavern one-off. + if (def.over && tmap[def.over]) { + var base = pickVariant(tmap[def.over], x, y); + if (base) atlas.draw(ctx, base[0], base[1], px, py, cell); + } atlas.draw(ctx, coord[0], coord[1], px, py, cell); - // Dark bottom-edge strip grounds a solid wall against the floor below. - if (def.solid) { + // Dark bottom-edge strip grounds a solid WALL against the floor below (not + // objects, which already sit on their own base tile). + if (def.solid && !def.over) { var strip = Math.max(2, cell * 0.16); var sg = ctx.createLinearGradient(0, py + cell - strip, 0, py + cell); sg.addColorStop(0, 'rgba(0,0,0,0)'); @@ -121,6 +130,10 @@ continue; } // Flat-colour fallback for any unmapped kind (or atlas not yet loaded). + if (def.over && scene.legend[def.over]) { + ctx.fillStyle = scene.legend[def.over].color; // base colour under the object + ctx.fillRect(px, py, cell, cell); + } ctx.fillStyle = def.color; ctx.fillRect(px, py, cell, cell); ctx.strokeStyle = 'rgba(0,0,0,0.25)';