← Basics3D assetfogon/lampara-faro
Lámpara faro
Drag to turn it// «Lámpara faro» · gpt-6-sol (medium)
// Lighthouse lamp («Lámpara faro»).
const C = { x: 24, y: 24 };
const dist = (x, y) => Math.hypot(x - C.x, y - C.y);
function disk(z, r, color) {
for (let x = Math.floor(C.x - r); x <= Math.ceil(C.x + r); x++)
for (let y = Math.floor(C.y - r); y <= Math.ceil(C.y + r); y++)
if (dist(x, y) <= r) set(x, y, z, color);
}
function ring(z, inner, outer, color) {
for (let x = Math.floor(C.x - outer); x <= Math.ceil(C.x + outer); x++)
for (let y = Math.floor(C.y - outer); y <= Math.ceil(C.y + outer); y++)
if (dist(x, y) >= inner && dist(x, y) <= outer)
set(x, y, z, color);
}
// Broad, low plinth: deep blue sides, brass edging, and a tiled deck.
for (const [z, r] of [[0, 17.3], [1, 19], [2, 20], [3, 20], [4, 18.8]])
disk(z, r, z === 0 ? "#263f51" : z < 3 ? "#304e60" : "#345568");
ring(0, 15.7, 17.3, "#1b3547");
ring(1, 17.5, 19, "#203c50");
ring(2, 18.4, 20, "#d7ae59");
ring(3, 18.3, 20, "#284a5e");
ring(4, 16.8, 18.8, "#e7bb66");
disk(5, 16.5, "#e3d5aa");
ring(5, 14.7, 16.5, "#315669");
ring(5, 15.4, 16.2, "#e7bf70");
disk(6, 13.6, "#d5d1b7");
ring(6, 11.8, 13.6, "#f0e7cb");
for (let i = 0; i < 24; i++) {
const a = i * Math.PI / 12;
const x = Math.round(24 + 19.2 * Math.cos(a));
const y = Math.round(24 + 19.2 * Math.sin(a));
set(x, y, 2, i % 3 ? "#e3b961" : "#f3d783");
if (i % 3 === 0) set(x, y, 1, "#142e40");
}
for (let i = 0; i < 20; i++) {
const a = i * Math.PI / 10;
const x = Math.round(24 + 15.6 * Math.cos(a));
const y = Math.round(24 + 15.6 * Math.sin(a));
set(x, y, 6, i % 2 ? "#f7e5a9" : "#456777");
}
// Stepped limestone footing.
disk(7, 11.3, "#bfc3af");
ring(7, 10, 11.3, "#f1e9d1");
disk(8, 10.3, "#d6d5bd");
ring(8, 9.1, 10.3, "#f7efdb");
for (let z = 9; z <= 27; z++) {
const r = z <= 12 ? 8.9 : z <= 19 ? 8.2 : z <= 22 ? 8.5 : 8.3;
for (let x = 15; x <= 33; x++)
for (let y = 15; y <= 33; y++) {
const d = dist(x, y);
if (d > r) continue;
let color;
if (z <= 12) color = d > r - 1.4 ? "#d5d0bc" : "#b9b8aa";
else if (z <= 14) color = d > r - 1.2 ? "#e9e3cb" : "#c7c3af";
else if (z <= 19) color = "#ad5149";
else if (z <= 22) color = "#d7d0bb";
else if (z <= 25) color = "#bb5b50";
else color = z === 26 ? "#e3d9bd" : "#a44942";
if (z >= 15 && z <= 19 || z >= 23 && z <= 25) {
if ((z + Math.floor(x / 3) + Math.floor(y / 3)) % 5 === 0)
color = "#ca6658";
if (d > r - 1.2 && y > 24 && rand() < 0.22)
color = "#853f3e";
}
if (d > r - 1.3 && y < 24 && z <= 14 && rand() < 0.15)
color = "#f5eed9";
set(x, y, z, color);
}
}
// Pale vertical fluting around the lower masonry and the white middle band.
for (let i = 0; i < 12; i++) {
const a = i * Math.PI / 6;
for (const [lo, hi, radius] of [[9, 14, 8.7], [20, 23, 8.25]]) {
const x = Math.round(24 + radius * Math.cos(a));
const y = Math.round(24 + radius * Math.sin(a));
for (let z = lo; z <= hi; z++) {
set(x, y, z, (i + z) % 5 === 0 ? "#f9eed5" : "#e5dfc9");
if (z === lo || z === hi) set(x, y, z, "#faf0d8");
}
}
}
ring(14, 7.4, 8.5, "#e9e0c8");
ring(19, 7.2, 8.4, "#85433f");
ring(22, 7.4, 8.5, "#efe5ce");
ring(26, 7.3, 9.1, "#efe4c8");
ring(27, 8, 9.6, "#ab5149");
// Recessed, glowing doorway on the viewer-facing side.
box(26, 14, 9, 30, 15, 16, "#183446");
box(27, 13, 10, 29, 13, 15, "#172b3c");
box(27, 13, 12, 28, 13, 15, "#f6db79");
box(27, 13, 10, 29, 13, 11, "#334e57");
box(25, 14, 9, 25, 15, 16, "#edcb78");
box(31, 14, 9, 31, 16, 16, "#d3a95c");
box(26, 13, 17, 30, 15, 17, "#f4dc9c");
box(26, 14, 18, 30, 15, 18, "#9a4c42");
set(30, 12, 12, "#f6df96");
// Blue-and-gold balcony beneath the lantern.
disk(28, 11.5, "#27485b");
ring(28, 9.2, 11.5, "#d3a657");
disk(29, 12.2, "#28475a");
ring(29, 10, 12.2, "#e3bd6e");
disk(30, 10.7, "#213b4e");
ring(30, 9.6, 10.7, "#d7ad61");
// Lantern chamber. Gold-lit panes sit between chunky navy uprights.
for (let z = 31; z <= 35; z++) {
disk(z, 10.2, "#203749");
for (let x = 13; x <= 35; x++)
for (let y = 13; y <= 35; y++) {
const d = dist(x, y);
if (d < 8.7 || d > 10.5) continue;
const a = Math.atan2(y - 24, x - 24);
const sector = Math.abs(Math.sin(a * 6));
let color = sector < 0.24 ? "#294a5c" : "#b68d4b";
if (sector >= 0.24 && z >= 32 && z <= 34)
color = sector > 0.75 ? "#efd075" : "#d5a957";
if (z === 31 || z === 35) color = "#25465b";
set(x, y, z, color);
}
}
for (let i = 0; i < 12; i++) {
const a = i * Math.PI / 6;
const x = Math.round(24 + 10.1 * Math.cos(a));
const y = Math.round(24 + 10.1 * Math.sin(a));
for (let z = 30; z <= 35; z++)
set(x, y, z, z === 30 || z === 35 ? "#ecc977" : "#2b5063");
set(x, y, 33, "#e5b85f");
}
ring(32, 9.7, 10.8, "#d6aa5b");
ring(35, 9.4, 11, "#e4bd70");
ring(36, 9.2, 11.8, "#253e50");
// Wide, layered red tile roof, narrowing to a little finial.
const roof = [
[37, 13.3], [38, 13.6], [39, 12.4], [40, 10.8],
[41, 8.6], [42, 6.3], [43, 3.3]
];
for (const [z, r] of roof) {
for (let x = Math.floor(24 - r); x <= Math.ceil(24 + r); x++)
for (let y = Math.floor(24 - r); y <= Math.ceil(24 + r); y++) {
const d = dist(x, y);
if (d > r) continue;
const row = (Math.floor(x / 3) + Math.floor(y / 3) + z) % 4;
let color = ["#bc514b", "#d46558", "#e47766", "#c75a51"][row];
if (d > r - 1.35) color = z % 2 ? "#ad4543" : "#e07160";
if (y > 25 && d > r - 2 && rand() < 0.38) color = "#a94341";
if (y < 23 && rand() < 0.12) color = "#f0836d";
set(x, y, z, color);
}
}
ring(37, 11.5, 13.3, "#a94743");
ring(38, 11.8, 13.6, "#d26557");
ring(40, 9.4, 10.8, "#ad4945");
ring(42, 5.1, 6.3, "#aa4240");
box(23, 23, 44, 25, 25, 44, "#743c3a");
set(24, 24, 45, "#e2bf67");import { draw } from "./assets/voxel";
import { lamparaFaro } from "./assets/lampara-faro";
// Every frame: standing on (x, y), the centre of its base.
draw(ctx, lamparaFaro, 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 lampara-faro
# With Fogón's tools (connector)
add_assets({ slug: "<your-game>", ids: ["lampara-faro"] })
# It lands in client/assets/lampara-faro.ts
# https://fogon.page/object/lampara-faro