← Nature3D assetfogon/termitero
Termitero
Drag to turn it// «Termitero» · gpt-6-sol (medium)
// A tall termite mound: a lumpy reddish-earth tower with several pointed spires and holes.
const clay = ["#b9633f", "#c97045", "#d87e4d", "#e28d59"];
const darkClay = "#984b35";
const lightClay = "#eb9c62";
function earthColor(z) {
const n = rand();
if (n < 0.09) return darkClay;
if (n < 0.29) return clay[0];
if (n < 0.69) return clay[1];
if (n < 0.91) return clay[2];
return z > 18 ? lightClay : clay[3];
}
function radiusAt(z) {
const levels = [
[0, 17], [4, 18], [9, 17.3], [15, 15.5],
[21, 13.2], [27, 10.3], [32, 7.2],
[35, 4.8], [37, 1.5]
];
for (let i = 1; i < levels.length; i++) {
if (z <= levels[i][0]) {
const a = levels[i - 1], b = levels[i];
return a[1] + (b[1] - a[1]) * (z - a[0]) / (b[0] - a[0]);
}
}
return 0;
}
// A solid, gently twisting mound, with scalloped rather than smooth sides.
for (let z = 0; z <= 37; z++) {
const r = radiusAt(z);
const cx = 24 + 0.8 * Math.sin(z * 0.15);
const cy = 24 + 0.6 * Math.sin(z * 0.11 + 1);
for (let x = 4; x <= 44; x++) {
for (let y = 4; y <= 44; y++) {
const angle = Math.atan2(y - cy, x - cx);
const ripple = 0.65 * Math.sin(7 * angle + z * 0.16)
+ 0.38 * Math.sin(11 * angle - z * 0.21);
const distance = Math.hypot((x - cx) / 1.03, (y - cy) / 0.96);
if (distance <= r + ripple + (rand() - 0.5) * 0.48) {
set(x, y, z, earthColor(z));
}
}
}
}
// Three pointed, uneven earthen peaks grow out of its shoulders.
const peaks = [
{ x: 22, y: 24, bottom: 26, top: 44, radius: 6.3 },
{ x: 14, y: 28, bottom: 24, top: 40, radius: 5.1 },
{ x: 33, y: 27, bottom: 25, top: 41, radius: 5.3 }
];
for (const p of peaks) {
for (let z = p.bottom; z <= p.top; z++) {
const t = (z - p.bottom) / (p.top - p.bottom);
const r = p.radius * Math.pow(1 - t, 0.83) + 0.48;
const cx = p.x + 1.3 * t * Math.sin(p.x);
const cy = p.y - 1.0 * t;
for (let x = Math.floor(cx - r - 1); x <= Math.ceil(cx + r + 1); x++) {
for (let y = Math.floor(cy - r - 1); y <= Math.ceil(cy + r + 1); y++) {
const angle = Math.atan2(y - cy, x - cx);
const edge = r + 0.32 * Math.sin(angle * 5 + z * 0.4);
if (Math.hypot(x - cx, y - cy) <= edge) set(x, y, z, earthColor(z));
}
}
}
}
// Raised, winding clay flutes give the front its termite-built texture.
function frontY(x, z) {
for (let y = 3; y < 45; y++) if (get(x, y, z)) return y;
return -1;
}
for (const startX of [10, 16, 23, 31, 38]) {
for (let z = 2; z <= 32; z++) {
const x = Math.round(
24 + (startX - 24) * (1 - z / 42) +
0.8 * Math.sin(z * 0.35 + startX)
);
const y = frontY(x, z);
if (y > 3) {
set(x, y - 1, z, "#df8652");
if (z % 5 !== 0 && get(x + 1, y, z)) {
set(x + 1, y - 1, z, "#bb623e");
}
}
}
}
// Recessed openings: irregular pale rims, warm shaded throats, dark interiors.
const openings = [
[24, 10, 3.5, 4.0],
[15, 20, 2.8, 3.4],
[30, 24, 2.8, 3.4],
[23, 32, 2.0, 2.6],
[35, 11, 2.4, 2.9],
[11, 9, 1.8, 2.3]
];
for (const [hx, hz, rx, rz] of openings) {
for (let x = Math.floor(hx - rx - 2); x <= Math.ceil(hx + rx + 2); x++) {
for (let z = Math.floor(hz - rz - 2); z <= Math.ceil(hz + rz + 2); z++) {
if (z < 2) continue;
const dx = (x - hx) / rx;
const dz = (z - hz) / rz;
const shape = dx * dx + dz * dz + 0.09 * Math.sin(x * 2.3 + z);
const y = frontY(x, z);
if (y < 4) continue;
if (shape < 0.80) {
set(x, y, z, null);
set(x, y + 1, z, null);
set(x, y + 2, z, null);
set(x, y + 3, z, shape < 0.43 ? "#65382d" : "#81432f");
} else if (shape < 1.30) {
set(x, y - 1, z, dz > 0.25 ? "#a85437" : lightClay);
}
}
}
}
// A few smaller holes face the right-hand side.
function rightX(y, z) {
for (let x = 44; x >= 3; x--) if (get(x, y, z)) return x;
return -1;
}
for (const [hy, hz, ry, rz] of [[23, 17, 2.4, 3], [29, 7, 1.8, 2.2]]) {
for (let y = Math.floor(hy - ry - 2); y <= Math.ceil(hy + ry + 2); y++) {
for (let z = Math.floor(hz - rz - 2); z <= Math.ceil(hz + rz + 2); z++) {
const shape = ((y - hy) / ry) ** 2 + ((z - hz) / rz) ** 2;
const x = rightX(y, z);
if (x < 4) continue;
if (shape < 0.78) {
set(x, y, z, null);
set(x - 1, y, z, null);
set(x - 2, y, z, "#75402f");
} else if (shape < 1.34) {
set(x + 1, y, z, z > hz ? lightClay : "#a85437");
}
}
}
}
// Sparse cheerful moss and tiny grass tufts around the foot.
for (let i = 0; i < 105; i++) {
const a = rand() * Math.PI * 2;
const r = 17.3 + rand() * 1.5;
const x = Math.round(24 + Math.cos(a) * r);
const y = Math.round(24 + Math.sin(a) * r);
if (get(x, y, 0)) {
const green = rand() < 0.5 ? "#78aa48" : "#93be55";
set(x, y, 0, green);
if (rand() < 0.26 && !get(x, y, 1)) set(x, y, 1, "#a8cb65");
}
}import { draw } from "./assets/voxel";
import { termitero } from "./assets/termitero";
// Every frame: standing on (x, y), the centre of its base.
draw(ctx, termitero, 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 termitero
# With Fogón's tools (connector)
add_assets({ slug: "<your-game>", ids: ["termitero"] })
# It lands in client/assets/termitero.ts
# https://fogon.page/object/termitero