← Basics3D assetfogon/glorieta
Glorieta
Drag to turn it// «Glorieta» · gpt-6-sol (medium)
// Gazebo («Glorieta»).
const C = {
stone: "#dfd6bc", stoneLight: "#f0e8d1", stoneShade: "#bdb49d",
wood: "#d99a5e", woodLight: "#edb577", woodDark: "#a9693e",
tealDark: "#165b59", teal: "#217a75", tealLight: "#32978b",
tealEdge: "#124d4d"
};
function radius(x, y, cx = 24, cy = 24) {
const dx = Math.abs(x - cx), dy = Math.abs(y - cy);
return Math.max(dx, dy, (dx + dy) / 1.48);
}
function octFill(cx, cy, r, z, color) {
for (let x = Math.floor(cx - r); x <= Math.ceil(cx + r); x++)
for (let y = Math.floor(cy - r); y <= Math.ceil(cy + r); y++)
if (radius(x, y, cx, cy) <= r) set(x, y, z, color);
}
function octRim(cx, cy, r, width, z, color) {
for (let x = Math.floor(cx - r); x <= Math.ceil(cx + r); x++)
for (let y = Math.floor(cy - r); y <= Math.ceil(cy + r); y++)
if (radius(x, y, cx, cy) <= r &&
radius(x, y, cx, cy) > r - width)
set(x, y, z, color);
}
// Raised octagonal foundation and plank deck.
octFill(24, 24, 18, 1, C.woodDark);
octFill(24, 24, 18, 2, C.stoneShade);
octFill(24, 24, 18, 3, C.stone);
octRim(24, 24, 18, 1, 3, C.stoneLight);
for (let x = 6; x <= 42; x++) {
for (let y = 6; y <= 42; y++) {
if (radius(x, y) > 17) continue;
const plank = Math.floor((x - 6) / 3);
let color = plank % 3 === 0 ? C.woodLight :
plank % 3 === 1 ? C.wood : "#e5a96c";
if (x % 3 === 0) color = "#ad754a";
else if (rand() < 0.055) color = "#f2bf83";
set(x, y, 4, color);
if (y % 9 === (plank * 4) % 9 && x % 3 !== 0)
set(x, y, 4, "#bd8050");
}
}
octRim(24, 24, 18, 1, 4, "#e9d7ae");
// Broad two-step entrance on the front side.
box(17, 2, 0, 32, 7, 0, "#a87043");
box(17, 2, 1, 32, 7, 1, C.wood);
box(17, 2, 1, 32, 2, 1, C.woodLight);
box(18, 5, 2, 31, 9, 2, "#bc8050");
box(18, 5, 3, 31, 9, 3, C.woodLight);
box(18, 5, 3, 31, 5, 3, "#f1bd7d");
for (let x = 20; x <= 29; x += 3) {
line(x, 3, 1, x, 7, 1, 0, "#b17a4c");
line(x, 6, 3, x, 9, 3, 0, "#c68c56");
}
// Eight-sided railing. The center front bay stays open.
const posts = [
[17, 7], [31, 7], [41, 17], [41, 31],
[31, 41], [17, 41], [7, 31], [7, 17]
];
for (let i = 1; i < posts.length; i++) {
const a = posts[i], b = posts[(i + 1) % posts.length];
const n = Math.max(Math.abs(b[0] - a[0]), Math.abs(b[1] - a[1]));
line(a[0], a[1], 8, b[0], b[1], 8, 0, C.stoneShade);
line(a[0], a[1], 13, b[0], b[1], 13, 0, C.stoneLight);
for (let j = 2; j <= n - 2; j += 3) {
const x = Math.round(a[0] + (b[0] - a[0]) * j / n);
const y = Math.round(a[1] + (b[1] - a[1]) * j / n);
box(x, y, 9, x, y, 12, C.stoneLight);
set(x, y, 10, C.stone);
}
}
// Cream-colored square columns, plinths, capitals, and eave beams.
for (const [x, y] of posts) {
box(x - 2, y - 2, 4, x + 2, y + 2, 5, C.stoneShade);
box(x - 2, y - 2, 6, x + 2, y + 2, 6, C.stoneLight);
box(x - 1, y - 1, 7, x + 1, y + 1, 22, C.stone);
line(x - 1, y - 1, 7, x - 1, y - 1, 22, 0, C.stoneLight);
line(x + 1, y + 1, 7, x + 1, y + 1, 22, 0, C.stoneShade);
box(x - 2, y - 2, 23, x + 2, y + 2, 24, C.stoneLight);
box(x - 2, y - 2, 25, x + 2, y + 2, 25, C.stoneShade);
}
for (let i = 0; i < posts.length; i++) {
const a = posts[i], b = posts[(i + 1) % posts.length];
line(a[0], a[1], 24, b[0], b[1], 24, 1, C.stone);
line(a[0], a[1], 25, b[0], b[1], 25, 1, C.stoneLight);
}
// Dark, projecting underside of the large roof.
octFill(24, 24, 21, 25, C.tealEdge);
octRim(24, 24, 21, 2, 25, C.tealDark);
// Stepped turquoise shingle roof, with concentric courses.
function roofHeight(r) {
return 26 + Math.floor((21 - r) * 0.48);
}
for (let x = 3; x <= 45; x++) {
for (let y = 3; y <= 45; y++) {
const r = radius(x, y);
if (r > 21) continue;
const z = roofHeight(r);
set(x, y, z - 1, C.tealDark);
let color = C.teal;
const course = Math.floor(r);
if (course % 3 === 0) color = C.tealLight;
else if (course % 3 === 2) color = "#206e69";
if ((x + 2 * y) % 7 === 0) color = "#39988b";
if (rand() < 0.045) color = "#125c58";
set(x, y, z, color);
// Raised, slightly irregular edges of the rows of tiles.
if (course % 3 === 0 && r - course < 0.58 &&
(x + y) % 5 !== 0)
set(x, y, z + 1, "#348f85");
}
}
// Eight pronounced hip ridges, radiating to the octagonal eaves.
const hips = [
[-21, -10], [-10, -21], [10, -21], [21, -10],
[21, 10], [10, 21], [-10, 21], [-21, 10]
];
for (const [dx, dy] of hips) {
const steps = Math.max(Math.abs(dx), Math.abs(dy));
for (let j = 6; j <= steps; j++) {
const x = Math.round(24 + dx * j / steps);
const y = Math.round(24 + dy * j / steps);
const z = roofHeight(radius(x, y));
set(x, y, z + 1, j % 4 === 0 ? "#49a99a" : C.tealLight);
}
}
octRim(24, 24, 21, 1, 26, C.tealDark);
// Small open lantern/cupola above the roof.
octFill(24, 24, 5, 37, C.tealDark);
octRim(24, 24, 5, 1, 38, C.stoneLight);
for (const [x, y] of [[20, 22], [22, 20], [26, 20], [28, 22],
[28, 26], [26, 28], [22, 28], [20, 26]]) {
box(x, y, 38, x, y, 41, C.stoneLight);
set(x, y, 39, C.stone);
}
octRim(24, 24, 5, 1, 41, C.stone);
for (let x = 18; x <= 30; x++)
for (let y = 18; y <= 30; y++) {
const r = radius(x, y);
if (r > 6) continue;
const z = 42 + Math.floor((6 - r) / 2.3);
set(x, y, z - 1, C.tealDark);
set(x, y, z, Math.floor(r) % 2 ? C.teal : C.tealLight);
}
octRim(24, 24, 6, 1, 42, C.tealEdge);
box(23, 23, 45, 24, 24, 45, "#a58443");
box(23, 23, 46, 24, 24, 46, "#d4ae5b");
// A small terracotta planter at the entrance.
box(23, 10, 5, 26, 13, 5, "#8b4b37");
box(23, 10, 6, 26, 13, 7, "#ac6243");
box(22, 9, 8, 27, 14, 8, "#c17a53");
box(23, 10, 8, 26, 13, 8, "#514735");
line(24, 11, 9, 24, 11, 12, 0, "#477344");
line(25, 12, 9, 26, 12, 11, 0, "#477344");
for (const [x, y, z] of [[23, 11, 11], [25, 10, 12], [26, 12, 11],
[24, 13, 12], [23, 12, 10]]) {
sphere(x, y, z, 1, z % 2 ? "#3e7950" : "#5b9258");
}import { draw } from "./assets/voxel";
import { glorieta } from "./assets/glorieta";
// Every frame: standing on (x, y), the centre of its base.
draw(ctx, glorieta, 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 glorieta
# With Fogón's tools (connector)
add_assets({ slug: "<your-game>", ids: ["glorieta"] })
# It lands in client/assets/glorieta.ts
# https://fogon.page/object/glorieta