← Ancient and prehistory3D assetfogon/acueducto
Acueducto
Drag to turn it// «Acueducto» · gpt-6-sol (medium)
// A section of Roman aqueduct crossing the whole grid along x: two stacked rows of tan stone arches (big ones below, smaller ones above) with a water channel on top showing blue water, and some grass at the feet of the pillars.
const stone = ["#d7b984", "#e5c999", "#f1d9aa", "#c7a674"];
const joint = "#b89a70";
const trim = "#f3dfb7";
const shadow = "#a9875f";
const lowerCenters = [10, 24, 38];
const upperCenters = [6, 13, 20, 27, 34, 41];
function opening(x, z, centers, spring, radius, rise) {
for (const c of centers) {
const width = z <= spring
? radius
: radius * Math.sqrt(Math.max(0, 1 - ((z - spring) / rise) ** 2));
if (z < spring + rise && Math.abs(x - c) <= width) return true;
}
return false;
}
function masonry(x, y, z) {
const brick = (Math.floor(x / 4) * 7 + Math.floor(y / 4) * 3 +
Math.floor(z / 4) * 11) % 7;
let color = stone[brick < 2 ? 0 : brick < 5 ? 1 : 2];
// Quiet mortar lines and a little variation across the broad stone faces.
if ((y === 17 || y === 30) && z % 5 === 4) color = joint;
if ((y === 17 || y === 30) &&
(x + Math.floor(z / 5) * 3) % 8 === 0 && z % 5 !== 4)
color = stone[3];
if (rand() < 0.025) color = stone[3];
return color;
}
// Three tall, open arches, cut all the way through the aqueduct.
for (let x = 1; x <= 46; x++)
for (let y = 17; y <= 30; y++)
for (let z = 0; z <= 20; z++) {
if (opening(x, z, lowerCenters, 10, 5.1, 8)) continue;
let color = masonry(x, y, z);
if ((y === 17 || y === 30) &&
(opening(x - 1, z, lowerCenters, 10, 5.1, 8) ||
opening(x + 1, z, lowerCenters, 10, 5.1, 8) ||
opening(x, z - 1, lowerCenters, 10, 5.1, 8)))
color = shadow;
set(x, y, z, color);
}
// Broad course between the two tiers.
box(1, 16, 20, 46, 31, 21, "#cfad7c");
box(1, 16, 22, 46, 31, 22, trim);
box(1, 17, 23, 46, 30, 23, "#d8bb8c");
// A tighter rhythm of six arches above.
for (let x = 1; x <= 46; x++)
for (let y = 17; y <= 30; y++)
for (let z = 24; z <= 35; z++) {
if (opening(x, z, upperCenters, 27, 2.4, 5.8)) continue;
let color = masonry(x, y, z);
if ((y === 17 || y === 30) &&
(opening(x - 1, z, upperCenters, 27, 2.4, 5.8) ||
opening(x + 1, z, upperCenters, 27, 2.4, 5.8) ||
opening(x, z - 1, upperCenters, 27, 2.4, 5.8)))
color = shadow;
set(x, y, z, color);
}
// Pale keystones make the crowns legible at a distance.
for (const c of lowerCenters)
for (const y of [17, 30]) {
box(c - 1, y, 18, c + 1, y, 19, "#f2d5a1");
set(c, y, 17, trim);
}
for (const c of upperCenters)
for (const y of [17, 30]) {
box(c - 1, y, 33, c + 1, y, 34, "#f2d5a1");
}
// Chunky projecting foundations at the four supporting piers.
for (const [a, b] of [[1, 5], [15, 19], [29, 33], [43, 46]]) {
box(a, 15, 0, b, 32, 1, "#b79768");
box(a, 16, 2, b, 31, 2, "#d6b783");
}
// Cornice, solid trough floor, and high stone lips along both sides.
box(1, 16, 36, 46, 31, 36, trim);
box(1, 17, 37, 46, 30, 38, "#d3b184");
box(1, 16, 38, 46, 19, 41, "#e5ca98");
box(1, 28, 38, 46, 31, 41, "#d9ba89");
box(1, 16, 41, 46, 19, 41, trim);
box(1, 28, 41, 46, 31, 41, trim);
box(1, 16, 39, 46, 16, 39, "#c5a373");
box(1, 31, 39, 46, 31, 39, "#b79768");
// Small grass clumps spilling around, not covering, the stone feet.
for (const px of [2, 4, 16, 18, 30, 32, 44, 46])
for (const py of [14, 33]) {
box(px, py, 0, px + (px < 46 ? 1 : 0), py, 0, "#668a45");
set(px, py, 1, "#83aa55");
if (px % 3 !== 0) set(px, py, 2, "#9abb65");
}
for (const [x, y] of [[6, 15], [13, 33], [26, 15], [35, 33], [40, 14]]) {
set(x, y, 0, "#648b49");
set(x, y, 1, "#91b760");
}
// The animated water is its own part; the masonry remains still.
part("water", [24, 24, 40]);
box(1, 20, 39, 46, 27, 40, "#398fc2");
for (let x = 3; x <= 45; x += 7) {
line(x, 21, 41, x + 2, 21, 41, 0, "#9ae8ef");
line(x + 2, 25, 41, x + 4, 25, 41, 0, "#71cddd");
}
animate("water", {
type: "colors",
palette: ["#75d9ed", "#288fc9", "#e2fbf5"],
pattern: "wave",
glow: 0.2
});
part("body", [24, 24, 0]);import { draw } from "./assets/voxel";
import { acueducto } from "./assets/acueducto";
// Every frame: standing on (x, y), the centre of its base.
draw(ctx, acueducto, 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 acueducto
# With Fogón's tools (connector)
add_assets({ slug: "<your-game>", ids: ["acueducto"] })
# It lands in client/assets/acueducto.ts
# https://fogon.page/object/acueducto