← Ancient and prehistory3D assetfogon/coliseo
Coliseo
Drag to turn it// «Coliseo» · gpt-6-sol (medium)
// A compact Roman Colosseum: an oval ring of tan-cream stone with three stacked levels of arches, one side taller and the other partly broken down so the inside is visible, showing a sandy arena floor and tiers of seats.
const cx = 24, cy = 24;
const stone = ["#e8d4ae", "#f0dfbd", "#ddc49b", "#d4b88c"];
const trim = "#f6e9cc";
const shadow = "#b99870";
function oval(x, y, rx, ry) {
return ((x - cx) / rx) ** 2 + ((y - cy) / ry) ** 2;
}
function stoneColor(z) {
if (rand() < 0.08) return stone[3];
if (rand() < 0.18) return stone[2];
return z % 10 >= 8 ? stone[1] : stone[0];
}
// Broad, solid foundation.
for (let x = 3; x <= 45; x++)
for (let y = 7; y <= 41; y++) {
const q = oval(x, y, 20.8, 16.7);
if (q <= 1) set(x, y, 0, q > 0.87 ? shadow : "#cfb184");
if (q <= 0.96) set(x, y, 1, q > 0.83 ? "#dec49b" : "#cbb087");
}
// Sandy arena, with a few broad patches of warmer sand.
for (let x = 12; x <= 36; x++)
for (let y = 16; y <= 32; y++) {
if (oval(x, y, 11.2, 7.3) > 1) continue;
const patch = Math.sin(x * 0.63 + y * 0.28) + Math.cos(y * 0.81 - x * 0.19);
set(x, y, 2, rand() < 0.10 ? "#d7ae72" :
patch > 0.7 ? "#f0d29a" : "#e7c58e");
}
// Low retaining lip around the sand.
for (let x = 11; x <= 37; x++)
for (let y = 15; y <= 33; y++) {
if (oval(x, y, 12.0, 8.1) <= 1 &&
oval(x, y, 11.25, 7.35) > 1) {
set(x, y, 2, "#a97858");
set(x, y, 3, "#dfc4a0");
}
}
// Six rising, chunky rings of spectator seating.
for (let row = 0; row < 6; row++) {
const rx = 12.35 + row * 0.91;
const ry = 8.35 + row * 0.82;
const innerX = rx - 0.94, innerY = ry - 0.84;
const top = 4 + row;
for (let x = 6; x <= 42; x++)
for (let y = 10; y <= 38; y++) {
if (oval(x, y, rx, ry) > 1 || oval(x, y, innerX, innerY) <= 1) continue;
const a = Math.atan2((y - cy) / ry, (x - cx) / rx);
const aisle = Math.abs(Math.sin(4 * a)) < 0.055 && row > 1;
for (let z = 2; z <= top; z++) {
set(x, y, z, z === top
? (aisle ? "#ae896e" : rand() < 0.15 ? "#f8e7c8" : "#ebd5b0")
: "#bea17d");
}
if (!aisle && row > 0 && rand() < 0.65)
set(x, y, top + 1, "#f4e1bf");
}
}
// A stepped central aisle climbs the far seating.
for (let y = 31; y <= 37; y++)
for (let x = 23; x <= 25; x++) {
const z = Math.min(10, 4 + Math.floor((y - 30) * 0.85));
set(x, y, z, (y & 1) ? "#b39576" : "#d5b997");
}
// Elliptical arcade: twenty broad arch bays on each of three levels.
const bays = 20;
const step = 2 * Math.PI / bays;
function archFraction(x, y) {
let a = Math.atan2((y - cy) / 16.2, (x - cx) / 20.1);
a = ((a + Math.PI * 2) % (Math.PI * 2));
let t = ((a / step) % 1 + 1) % 1;
return Math.abs(t - 0.5) * 2; // 0 at arch center, 1 at its pillar
}
function wallHeight(x, y) {
if (y >= 22) return 30;
if (y >= 17 && (x <= 12 || x >= 36)) return 29;
if (x <= 13) return 21;
if (x >= 35) return 21;
if (x <= 17 || x >= 31) return 12;
if (x <= 20 || x >= 28) return 7;
return 3; // collapsed front center exposes the arena
}
for (let x = 3; x <= 45; x++)
for (let y = 7; y <= 41; y++) {
const q = Math.sqrt(oval(x, y, 20.0, 16.0));
if (q < 0.865 || q > 1.045) continue;
const f = archFraction(x, y);
let height = wallHeight(x, y);
if (height < 29) {
height += Math.sin(x * 2.4 + y * 1.8) > 0.5 ? 1 : 0;
if (Math.sin(x * 1.7 - y * 2.2) < -0.75) height--;
}
for (let z = 2; z <= height; z++) {
const level = Math.floor((z - 2) / 10);
const within = (z - 2) % 10;
const archedTop = 6 - Math.floor(3 * (f / 0.76) ** 2);
const opening = level < 3 && f < 0.76 &&
within >= 1 && within <= archedTop;
if (opening) continue;
let color = stoneColor(z);
if (z === 10 || z === 11 || z === 20 || z === 21 || z === 29)
color = z % 10 === 0 ? "#d4b995" : trim;
else if (f > 0.78 && within >= 1 && within <= 7)
color = rand() < 0.12 ? "#d3b991" : "#edd9b5";
else if (within === 7 && f < 0.76)
color = "#f3dfbc"; // bright voussoirs above each arch
if (z === height && height < 29) color = "#f0ddbb";
set(x, y, z, color);
}
// Engaged columns project beyond the wall, making every bay legible.
if (q > 0.975 && f > 0.81) {
for (let base of [2, 12, 22]) {
if (height < base + 2) continue;
for (let z = base + 1; z <= Math.min(base + 7, height); z++) {
set(x, y, z, z === base + 1 || z === base + 7
? trim : "#e9d4ae");
}
}
}
}
// Strong horizontal cornices on the surviving upper walls.
for (let x = 3; x <= 45; x++)
for (let y = 7; y <= 41; y++) {
const q = Math.sqrt(oval(x, y, 20.45, 16.4));
if (q < 0.97 || q > 1.055) continue;
const h = wallHeight(x, y);
for (const z of [11, 21, 30]) {
if (h >= z) set(x, y, z, z === 30 ? "#f7e9cc" : "#e9d1aa");
}
}
// Irregular broken stones scattered just inside the missing front wall.
for (let i = 0; i < 35; i++) {
const x = 16 + Math.floor(rand() * 17);
const y = 10 + Math.floor(rand() * 5);
if (oval(x, y, 19.4, 15.4) > 1) continue;
const z = get(x, y, 2) ? 3 : 2;
set(x, y, z, rand() < 0.5 ? "#efdcba" : "#c8a77d");
}
// Two small crimson standards hang against the tall, far inner wall.
for (const bx of [17, 30]) {
box(bx, 36, 17, bx + 2, 36, 20, "#9f3d3d");
box(bx, 36, 20, bx + 2, 36, 20, "#d9a750");
set(bx + 1, 35, 18, "#e9bb64");
set(bx, 36, 16, "#d9a750");
set(bx + 2, 36, 16, "#d9a750");
}import { draw } from "./assets/voxel";
import { coliseo } from "./assets/coliseo";
// Every frame: standing on (x, y), the centre of its base.
draw(ctx, coliseo, 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 coliseo
# With Fogón's tools (connector)
add_assets({ slug: "<your-game>", ids: ["coliseo"] })
# It lands in client/assets/coliseo.ts
# https://fogon.page/object/coliseo