diff --git a/html/assets/kenney/iso-dungeon/tiles/planksBroken_S.png b/html/assets/kenney/iso-dungeon/tiles/planksBroken_S.png new file mode 100644 index 0000000..911605c Binary files /dev/null and b/html/assets/kenney/iso-dungeon/tiles/planksBroken_S.png differ diff --git a/html/assets/kenney/iso-dungeon/tiles/stoneTile_S.png b/html/assets/kenney/iso-dungeon/tiles/stoneTile_S.png new file mode 100644 index 0000000..4ca4505 Binary files /dev/null and b/html/assets/kenney/iso-dungeon/tiles/stoneTile_S.png differ diff --git a/html/assets/kenney/iso-dungeon/tiles/stoneUneven_S.png b/html/assets/kenney/iso-dungeon/tiles/stoneUneven_S.png new file mode 100644 index 0000000..c56dbf1 Binary files /dev/null and b/html/assets/kenney/iso-dungeon/tiles/stoneUneven_S.png differ diff --git a/html/assets/kenney/iso-dungeon/tiles/stoneWallCorner_E.png b/html/assets/kenney/iso-dungeon/tiles/stoneWallCorner_E.png new file mode 100644 index 0000000..e7ef38f Binary files /dev/null and b/html/assets/kenney/iso-dungeon/tiles/stoneWallCorner_E.png differ diff --git a/html/assets/kenney/iso-dungeon/tiles/stoneWallCorner_S.png b/html/assets/kenney/iso-dungeon/tiles/stoneWallCorner_S.png new file mode 100644 index 0000000..defb582 Binary files /dev/null and b/html/assets/kenney/iso-dungeon/tiles/stoneWallCorner_S.png differ diff --git a/html/assets/kenney/iso-dungeon/tiles/stoneWallDoorOpen_E.png b/html/assets/kenney/iso-dungeon/tiles/stoneWallDoorOpen_E.png new file mode 100644 index 0000000..d38ce46 Binary files /dev/null and b/html/assets/kenney/iso-dungeon/tiles/stoneWallDoorOpen_E.png differ diff --git a/html/assets/kenney/iso-dungeon/tiles/stoneWallDoorOpen_W.png b/html/assets/kenney/iso-dungeon/tiles/stoneWallDoorOpen_W.png new file mode 100644 index 0000000..22819c2 Binary files /dev/null and b/html/assets/kenney/iso-dungeon/tiles/stoneWallDoorOpen_W.png differ diff --git a/html/assets/kenney/iso-dungeon/tiles/stoneWallWindow_E.png b/html/assets/kenney/iso-dungeon/tiles/stoneWallWindow_E.png new file mode 100644 index 0000000..b633115 Binary files /dev/null and b/html/assets/kenney/iso-dungeon/tiles/stoneWallWindow_E.png differ diff --git a/html/assets/kenney/iso-dungeon/tiles/stoneWallWindow_W.png b/html/assets/kenney/iso-dungeon/tiles/stoneWallWindow_W.png new file mode 100644 index 0000000..0aa4e40 Binary files /dev/null and b/html/assets/kenney/iso-dungeon/tiles/stoneWallWindow_W.png differ diff --git a/html/assets/kenney/iso-dungeon/tiles/stoneWall_E.png b/html/assets/kenney/iso-dungeon/tiles/stoneWall_E.png new file mode 100644 index 0000000..fae9460 Binary files /dev/null and b/html/assets/kenney/iso-dungeon/tiles/stoneWall_E.png differ diff --git a/html/assets/kenney/iso-dungeon/tiles/stoneWall_N.png b/html/assets/kenney/iso-dungeon/tiles/stoneWall_N.png new file mode 100644 index 0000000..4c3c669 Binary files /dev/null and b/html/assets/kenney/iso-dungeon/tiles/stoneWall_N.png differ diff --git a/html/assets/kenney/iso-dungeon/tiles/stoneWall_W.png b/html/assets/kenney/iso-dungeon/tiles/stoneWall_W.png new file mode 100644 index 0000000..664b658 Binary files /dev/null and b/html/assets/kenney/iso-dungeon/tiles/stoneWall_W.png differ diff --git a/html/js/render/assetPacks.js b/html/js/render/assetPacks.js index b9fb7f1..67a2555 100644 --- a/html/js/render/assetPacks.js +++ b/html/js/render/assetPacks.js @@ -293,11 +293,24 @@ floor2: 'assets/kenney/iso-dungeon/tiles/stone_S.png', // stone — dungeon dirt: 'assets/kenney/iso-dungeon/tiles/dirt_S.png', rug: 'assets/kenney/iso-dungeon/tiles/planks_S.png', - // Architecture (full-height tiles). The rich set has real windows/doors/archways. - wall: 'assets/kenney/iso-dungeon/tiles/stoneWall_S.png', - window: 'assets/kenney/iso-dungeon/tiles/stoneWallWindow_S.png', - door: 'assets/kenney/iso-dungeon/tiles/stoneWallDoorOpen_S.png', - archway:'assets/kenney/iso-dungeon/tiles/stoneWallArchway_S.png', + // Architecture (full-height tiles). Iso walls have a facing: a wall running along the + // grid-x axis needs the _S rotation, one running along grid-y needs the perpendicular + // _W rotation, and true corners get the corner tile. The renderer picks per-cell from + // neighbours, choosing `Y` for y-running segments (that's what fixes the "thin + // slabs" look on the top-left / bottom-right edges). + wall: 'assets/kenney/iso-dungeon/tiles/stoneWall_S.png', + wallY: 'assets/kenney/iso-dungeon/tiles/stoneWall_W.png', + wallCorner: 'assets/kenney/iso-dungeon/tiles/stoneWallCorner_S.png', + window: 'assets/kenney/iso-dungeon/tiles/stoneWallWindow_S.png', + windowY: 'assets/kenney/iso-dungeon/tiles/stoneWallWindow_W.png', + door: 'assets/kenney/iso-dungeon/tiles/stoneWallDoorOpen_S.png', + doorY: 'assets/kenney/iso-dungeon/tiles/stoneWallDoorOpen_W.png', + archway: 'assets/kenney/iso-dungeon/tiles/stoneWallArchway_S.png', + // Floor variety — the renderer sprinkles these deterministically so the ground isn't a + // flat sea of identical planks. + floorAlt: 'assets/kenney/iso-dungeon/tiles/planksBroken_S.png', + floor2Alt: 'assets/kenney/iso-dungeon/tiles/stoneUneven_S.png', + stoneTile: 'assets/kenney/iso-dungeon/tiles/stoneTile_S.png', column: 'assets/kenney/iso-dungeon/tiles/stoneColumnWood_S.png', stairs: 'assets/kenney/iso-dungeon/tiles/stairs_S.png', // Furniture props (drawn over a floor base) — each kind now has distinct art. diff --git a/html/js/render/isoRenderer.js b/html/js/render/isoRenderer.js index f9d1b59..1e02438 100644 --- a/html/js/render/isoRenderer.js +++ b/html/js/render/isoRenderer.js @@ -73,6 +73,34 @@ this.ctx.drawImage(img, cx - w / 2, baseY - h, w, h); }; + // Soft contact shadow grounds props/characters so they don't float on the floor. + IsoRenderer.prototype._contactShadow = function (cx, cy, rx, ry) { + var ctx = this.ctx; + ctx.save(); + ctx.globalAlpha = 0.28; + ctx.fillStyle = '#000'; + ctx.beginPath(); + ctx.ellipse(cx, cy, rx, ry, 0, 0, Math.PI * 2); + ctx.fill(); + ctx.restore(); + }; + + // Warm radial vignette — an ambient tavern glow that darkens the edges and lifts the centre, + // the "juice" that makes the flat canvas feel lit rather than pasted. + IsoRenderer.prototype._vignette = function () { + var ctx = this.ctx, w = this.canvas.width, h = this.canvas.height; + var g = ctx.createRadialGradient(w / 2, h * 0.46, Math.min(w, h) * 0.18, + w / 2, h * 0.46, Math.max(w, h) * 0.62); + g.addColorStop(0, 'rgba(255, 214, 150, 0.10)'); + g.addColorStop(0.55, 'rgba(0, 0, 0, 0)'); + g.addColorStop(1, 'rgba(0, 0, 0, 0.42)'); + ctx.save(); + ctx.globalCompositeOperation = 'source-over'; + ctx.fillStyle = g; + ctx.fillRect(0, 0, w, h); + ctx.restore(); + }; + IsoRenderer.prototype._imageBounds = function (img) { if (!img) return null; var key = img.src || img._rkBoundsKey || null; @@ -141,6 +169,44 @@ return tiles[kind] || tiles.fallback || tiles.floor; }; + // Deterministic per-cell hash (no RNG — stable across frames/reloads). + function cellHash(x, y) { + var h = (x * 73856093) ^ (y * 19349663); + h = (h ^ (h >>> 13)) >>> 0; + return h / 4294967295; + } + + // Wall-family kinds share edge-run detection so a window/door in a wall counts as wall. + var WALLISH = { wall: 1, window: 1, door: 1, archway: 1 }; + + IsoRenderer.prototype._isWall = function (scene, x, y) { + if (x < 0 || y < 0 || y >= scene.rows || x >= scene.cols) return false; + return !!WALLISH[scene.grid[y][x]]; + }; + + // Iso walls have a facing. A segment running along grid-x uses the base (_S) rotation; one + // running along grid-y uses the perpendicular (`Y`, _W) rotation; a true corner (both + // axes present) uses the corner tile. This is what makes enclosing walls read as continuous + // faces instead of thin disconnected slabs on the y-running edges. + IsoRenderer.prototype._wallVariant = function (scene, x, y, kind) { + var tiles = this.assets.tiles || {}; + var xRun = this._isWall(scene, x - 1, y) || this._isWall(scene, x + 1, y); + var yRun = this._isWall(scene, x, y - 1) || this._isWall(scene, x, y + 1); + if (xRun && yRun && kind === 'wall' && tiles.wallCorner) return 'wallCorner'; + if (yRun && !xRun && tiles[kind + 'Y']) return kind + 'Y'; + return kind; + }; + + // Sprinkle floor variants deterministically so the ground isn't a flat identical sea. + IsoRenderer.prototype._floorVariant = function (kind, x, y) { + var tiles = this.assets.tiles || {}; + var r = cellHash(x, y); + if (kind === 'floor' && tiles.floorAlt && r > 0.86) return 'floorAlt'; + if (kind === 'floor2' && tiles.stoneTile && r > 0.6) return 'stoneTile'; + if (kind === 'floor2' && tiles.floor2Alt && r > 0.82) return 'floor2Alt'; + return kind; + }; + IsoRenderer.prototype._visualFor = function (e) { var appearance = (e && e.appearance) || { avatar: (e && e.avatar) || 'default' }; if (root.RK && RK.avatarVisuals && RK.avatarVisuals.resolve) { @@ -207,12 +273,16 @@ var it = items[i]; if (it.type === 'tile') { var tileKind = PROP[it.kind] ? 'floor' : it.kind; + if (WALLISH[tileKind]) tileKind = this._wallVariant(scene, it.x, it.y, tileKind); + else tileKind = this._floorVariant(tileKind, it.x, it.y); var rec = this._load(this._tileUrl(tileKind)); if (rec && rec.ready) this._drawImage(rec.img, it.sx, it.sy + this.tileH, this.imageW, this.imageH); } else if (it.type === 'prop') { + this._contactShadow(it.sx, it.sy + this.tileH * 1.4, 22, 9); rec = this._load(this._tileUrl(it.kind)); if (rec && rec.ready) this._drawImage(rec.img, it.sx, it.sy + this.tileH, this.imageW, this.imageH); } else if (it.type === 'entity') { + this._contactShadow(it.sx, it.sy + this.tileH * 1.15, 18, 7); var frame = this._charFrame(it.e, now); rec = this._load(frame && frame.url); if (rec && rec.ready) { @@ -240,6 +310,7 @@ } } } + this._vignette(); if (this._animating) this._invalidate(); }; diff --git a/src/multiplayer/tavernMap.js b/src/multiplayer/tavernMap.js index d49bbbc..7b1c5cc 100644 --- a/src/multiplayer/tavernMap.js +++ b/src/multiplayer/tavernMap.js @@ -51,6 +51,16 @@ function buildDefaultTavern(cols = 28, rows = 15) { // Corner decor — barrels and crates. put(1, 1, 'B'); put(cols - 2, 1, 'C'); put(1, rows - 2, 'C'); put(cols - 2, rows - 2, 'B'); + // Barrels & crates lining the side walls (one in from the wall, clear of the windows/spawn) so + // the room reads lived-in instead of a big empty floor. Alternating B/C for material variety. + const wallDecor = [ + [1, 5, 'B'], [1, 9, 'C'], [1, 12, 'B'], + [cols - 2, 5, 'C'], [cols - 2, 9, 'B'], [cols - 2, 12, 'C'], + [2, rows - 2, 'B'], [cols - 3, rows - 2, 'C'], + [barX0 - 1, barY + 1, 'B'], [barX1 + 1, barY + 1, 'C'] // a barrel/crate tucked beside each keg + ]; + for (const [dx, dy, dch] of wallDecor) put(dx, dy, dch); + // Door + spawn pad at the bottom-centre (two spawn tiles for deterministic adjacency tests). const mid = Math.floor(cols / 2); set(mid, rows - 1, 'D');