← Ancient and prehistory3D assetfogon/town-gate
Arco de entrada
Drag to turn it// «Arco de entrada» · propio
// A sandstone town gateway: two thick wall stubs joined by a round arch you can walk through, with a pale band and square merlons along the top.
// A sandstone town gateway: two thick wall stubs joined by a round arch to walk through, a pale band and merlons.
const stone = ["#a37c4b", "#b38c57", "#c19a64", "#cda870", "#d8b57e"];
const footStone = ["#806038", "#8f6d42", "#9c794c"];
const mortar = "#84653c";
const trim = ["#d4bd8c", "#e0cb9c", "#ebd8ad", "#f2e2bd"];
const clampi = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
const X0 = 2, X1 = 45, Y0 = 18, Y1 = 29, TOP = 30;
const CX = 23.5, SPRING = 13, R = 11;
const hash = (a, b) => {
const s = Math.sin(a * 12.9898 + b * 78.233) * 43758.5453;
return s - Math.floor(s);
};
function blockAt(u, z, tones, base) {
const row = Math.floor(z / 4);
const len = 8 + (row % 3);
const shift = (row % 2) * 4 + ((row * 5) % 3);
const k = Math.floor((u + shift) / len);
if (z % 4 === 3 || (u + shift) % len === 0) return mortar;
let tone = base + Math.floor(hash(k, row) * 3);
if (rand() < 0.12) tone--;
return tones[clampi(tone, 0, tones.length - 1)];
}
// Inside the opening?
const open = (x, z) => (z <= SPRING ? Math.abs(x - CX) < R : Math.hypot(x - CX, z - SPRING) < R);
// How far from the opening's edge (for the ring of pale stones around it).
const ring = (x, z) => (z <= SPRING ? Math.abs(x - CX) - R : Math.hypot(x - CX, z - SPRING) - R);
for (let x = X0; x <= X1; x++)
for (let y = Y0; y <= Y1; y++)
for (let z = 0; z <= TOP; z++) {
if (open(x, z)) continue;
const e = ring(x, z);
const face = y === Y0 || y === Y1;
if (e < 2.6 && z > 5) {
// The arch stones: pale, cut like wedges above the spring.
const a = z <= SPRING ? Math.floor(z / 4) : Math.floor((Math.atan2(z - SPRING, x - CX) / Math.PI) * 9);
const joint = z <= SPRING ? z % 4 === 3 : Math.abs((Math.atan2(z - SPRING, x - CX) / Math.PI) * 9 - Math.round((Math.atan2(z - SPRING, x - CX) / Math.PI) * 9)) < 0.12;
set(x, y, z, joint && face ? "#bfa878" : trim[clampi(1 + (a % 2) + (e < 1 ? 0 : 1) - (rand() < 0.12 ? 1 : 0), 0, 3)]);
continue;
}
const u = x === X0 || x === X1 ? y + 2 : x;
set(x, y, z, blockAt(u, z, stone, z > 18 ? 2 : 1));
}
// The foot of each stub: darker, a block wider.
for (let x = X0 - 1; x <= X1 + 1; x++)
for (let y = Y0 - 1; y <= Y1 + 1; y++)
for (let z = 0; z <= 5; z++) {
if (Math.abs(x - CX) < R + 0.6) continue;
const u = x < X0 || x > X1 ? y + 2 : x + 3;
set(x, y, z, z === 5 ? footStone[2] : blockAt(u, z, footStone, 0));
}
// The pale band.
for (let x = X0 - 1; x <= X1 + 1; x++)
for (let y = Y0 - 1; y <= Y1 + 1; y++)
for (let z = TOP - 3; z <= TOP - 2; z++) set(x, y, z, (x + 5) % 11 === 0 ? "#bfa878" : trim[clampi((z === TOP - 2 ? 2 : 1) - (rand() < 0.15 ? 1 : 0), 0, 3)]);
// The top and its merlons, front and back.
for (let x = X0; x <= X1; x++) for (let y = Y0; y <= Y1; y++) set(x, y, TOP, stone[clampi(3 - (rand() < 0.2 ? 1 : 0), 0, 4)]);
for (let x = X0; x <= X1; x++) {
const m = (x - X0) % 8;
if (m > 4) continue;
for (const y of [Y0, Y0 + 1, Y1 - 1, Y1])
for (let z = TOP + 1; z <= TOP + 5; z++) {
let tone = z >= TOP + 4 ? 3 : 2;
if (m === 0 || m === 4) tone--;
if (rand() < 0.12) tone--;
set(x, y, z, z === TOP + 5 ? trim[clampi(tone - 1, 0, 3)] : stone[clampi(tone + 1, 0, 4)]);
}
}
// The keystone, and the paving inside the passage.
for (const y of [Y0 - 1, Y1 + 1]) box(22, y, SPRING + 10, 25, y, SPRING + 13, trim[3]);
for (const y of [Y0 - 1, Y1 + 1]) box(23, y, SPRING + 11, 24, y, SPRING + 12, "#cdb787");
for (let x = 13; x <= 34; x++) for (let y = Y0 - 1; y <= Y1 + 1; y++) set(x, y, 0, (x + (Math.floor(y / 3) % 2) * 2) % 4 === 0 || y % 3 === 0 ? "#9c7d50" : hash(Math.floor(x / 4), Math.floor(y / 3)) > 0.5 ? "#c9a56c" : "#b99660");import { draw } from "./assets/voxel";
import { townGate } from "./assets/town-gate";
// Every frame: standing on (x, y), the centre of its base.
draw(ctx, townGate, x, y, { size: 64, angle: 0, t: performance.now() / 1000 });
// size: its longest side, in px
// angle: turn on the floor, in radians
// t: seconds; if it moves, that animates it# In a terminal, in the game's folder
fogon assets add town-gate
# With Fogón's tools (connector)
add_assets({ slug: "<your-game>", ids: ["town-gate"] })
# It lands in client/assets/town-gate.ts
# https://fogon.gg/object/town-gate