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');