The terrain moves in: pits open downward, hedges stand waist-high, ooze is jello
The flat token billboards give way to terrain that inhabits the room. Pits, slime, tacks, and the dimensional warp's rings are painted INTO the flagstones — the home-tile decal pass grows into a general per-cell overlay grid, one texture per cell, alpha-blended texel by texel. Thorn and rose hedges fill their square wall to wall at half height, drawn with a near-bias so the wizard standing among the thorns peeks over the top. The killer ooze rises as a full translucent cube of jello, bricks and bodies visible through it; dust billows; and each dimensional warp mouth throws an additive pillar of violet light up from its floor ring. Billboards learn aspect, translucency, glow, and draw-order bias to carry it all. Nine paintable starters ship — four floor decals under textures/, five standing volumes under terrain3d/ (spec updated for the artist, hedges wide, jello solid, warpglow as light) — shown in the token workshop, rehearsable in place with /?fpv&terrain=thornbush@3,7;jello@4,7. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
a8a63cc27b
commit
1e79f2ffb2
@@ -42,7 +42,7 @@ function brickTexture(base: [number, number, number], rows: number, mortar: stri
|
||||
for (let r = 0; r <= rows; r++) c.fillRect(0, r * rh - 1, TEX, 2);
|
||||
});
|
||||
}
|
||||
export const MATERIALS = ["wall", "rim", "stone", "door", "firewall", "warp", "floor", "ceiling", "home"] as const;
|
||||
export const MATERIALS = ["wall", "rim", "stone", "door", "firewall", "warp", "floor", "ceiling", "home", "pit", "slime", "tacks", "dimwarp"] as const;
|
||||
|
||||
/** The built-in bake: what ships when no painted file overrides it. */
|
||||
export function proceduralTextures(): Record<string, HTMLCanvasElement> {
|
||||
@@ -127,6 +127,67 @@ export function proceduralTextures(): Record<string, HTMLCanvasElement> {
|
||||
c.arc(TEX / 2, TEX / 2, 7, 0, Math.PI * 2);
|
||||
c.fill();
|
||||
}),
|
||||
pit: bake((c) => {
|
||||
// A hole in the world: black heart, ragged broken-flag edge, a rim
|
||||
// highlight on the lit side selling the depth.
|
||||
c.fillStyle = "rgba(0,0,0,0.92)";
|
||||
c.beginPath();
|
||||
c.ellipse(TEX / 2, TEX / 2, TEX * 0.38, TEX * 0.34, 0, 0, Math.PI * 2);
|
||||
c.fill();
|
||||
c.strokeStyle = "rgba(20,16,10,0.9)";
|
||||
c.lineWidth = 4;
|
||||
c.stroke();
|
||||
c.strokeStyle = "rgba(180,168,140,0.5)";
|
||||
c.lineWidth = 2;
|
||||
c.beginPath();
|
||||
c.ellipse(TEX / 2, TEX / 2 - 2, TEX * 0.38, TEX * 0.34, 0, Math.PI * 1.1, Math.PI * 1.9);
|
||||
c.stroke();
|
||||
}),
|
||||
slime: bake((c) => {
|
||||
// A spill of green across the flags, glistening.
|
||||
c.fillStyle = "rgba(70,140,40,0.55)";
|
||||
c.beginPath();
|
||||
c.ellipse(TEX * 0.5, TEX * 0.52, TEX * 0.4, TEX * 0.32, 0.4, 0, Math.PI * 2);
|
||||
c.fill();
|
||||
c.fillStyle = "rgba(110,190,60,0.5)";
|
||||
for (let i = 0; i < 6; i++) {
|
||||
c.beginPath();
|
||||
c.ellipse(10 + h2(i, 2) * 44, 12 + h2(i, 5) * 40, 4 + h2(i, 7) * 5, 3 + h2(i, 9) * 4, 0, 0, Math.PI * 2);
|
||||
c.fill();
|
||||
}
|
||||
c.fillStyle = "rgba(220,255,200,0.35)";
|
||||
c.fillRect(TEX * 0.32, TEX * 0.4, 5, 2);
|
||||
}),
|
||||
tacks: bake((c) => {
|
||||
// A scatter of little iron cruelties.
|
||||
for (let i = 0; i < 14; i++) {
|
||||
const x = 8 + h2(i, 3) * 48, y = 8 + h2(i, 6) * 48;
|
||||
c.fillStyle = "rgba(70,70,78,0.9)";
|
||||
c.beginPath();
|
||||
c.moveTo(x, y - 3);
|
||||
c.lineTo(x + 3, y + 2);
|
||||
c.lineTo(x - 3, y + 2);
|
||||
c.closePath();
|
||||
c.fill();
|
||||
c.fillStyle = "rgba(190,190,200,0.8)";
|
||||
c.fillRect(x - 1, y - 4, 2, 2);
|
||||
}
|
||||
}),
|
||||
dimwarp: bake((c) => {
|
||||
// The dimensional warp's floor mouth: violet rings drawing inward.
|
||||
for (let i = 5; i >= 0; i--) {
|
||||
const a = 0.16 + i * 0.1;
|
||||
c.strokeStyle = `rgba(${140 + i * 12},${80 + i * 14},${220},${a})`;
|
||||
c.lineWidth = 3;
|
||||
c.beginPath();
|
||||
c.arc(TEX / 2, TEX / 2, 5 + i * 5, 0, Math.PI * 2);
|
||||
c.stroke();
|
||||
}
|
||||
c.fillStyle = "rgba(240,225,255,0.8)";
|
||||
c.beginPath();
|
||||
c.arc(TEX / 2, TEX / 2, 3.5, 0, Math.PI * 2);
|
||||
c.fill();
|
||||
}),
|
||||
warp: bake((c) => {
|
||||
const grad = c.createLinearGradient(0, 0, TEX, TEX);
|
||||
grad.addColorStop(0, "#2c1a4e");
|
||||
|
||||
Reference in New Issue
Block a user