← Piratas y marObjeto 3Dfogon/castillo-de-arena
Castillo de arena
Arrastrá para girarlo// «Castillo de arena» · gpt-6-sol (medium)
// A sand castle on the beach: sandy beige walls and four round towers with little crenellations, a small red flag on the main tower, a moat ring of turquoise water and a toy bucket and shovel next to it.
const sand = "#e8c982";
const sandLight = "#f5db99";
const sandDark = "#cda765";
const stone = "#dfbd78";
const stoneLight = "#f3d796";
const stoneDark = "#bd995f";
const water = "#24bfc0";
const waterLight = "#83eee0";
const waterDark = "#168eaa";
const cx = 24, cy = 24;
// Rounded beach, with a slightly uneven sandy edge.
for (let x = 1; x <= 46; x++) {
for (let y = 1; y <= 46; y++) {
const d = Math.hypot(x - cx, y - cy);
if (d > 21.8 + (rand() - 0.5) * 0.7) continue;
set(x, y, 0, sandDark);
if (d < 21.1 + (rand() - 0.5) * 0.6) {
set(x, y, 1, rand() < 0.19 ? sandLight : sand);
}
// A turquoise moat around the raised castle island.
if (d >= 14.0 && d <= 18.6) {
set(x, y, 1, waterDark);
set(x, y, 2, rand() < 0.18 ? waterLight : water);
if (rand() < 0.075) set(x, y, 3, waterLight);
} else if (d < 14.0) {
set(x, y, 2, sandDark);
set(x, y, 3, rand() < 0.22 ? sandLight : sand);
}
}
}
// Sparkles and a pale rim make the moat legible from above.
for (let x = 5; x <= 43; x++) {
for (let y = 5; y <= 43; y++) {
const d = Math.hypot(x - cx, y - cy);
if (d > 17.8 && d < 18.7 && rand() < 0.4) set(x, y, 2, waterLight);
}
}
// Solid castle walls.
box(15, 14, 4, 33, 16, 13, stone);
box(15, 32, 4, 33, 34, 13, stone);
box(14, 16, 4, 16, 32, 13, stone);
box(32, 16, 4, 34, 32, 13, stone);
// Sunlit capstones and chunky alternating crenellations.
for (let x = 18; x <= 30; x++) {
box(x, 14, 14, x, 16, 14, stoneLight);
box(x, 32, 14, x, 34, 14, stoneLight);
if (x % 4 < 2) {
box(x, 14, 15, x, 16, 16, stoneLight);
box(x, 32, 15, x, 34, 16, stoneLight);
}
}
for (let y = 18; y <= 30; y++) {
box(14, y, 14, 16, y, 14, stoneLight);
box(32, y, 14, 34, y, 14, stoneLight);
if (y % 4 < 2) {
box(14, y, 15, 16, y, 16, stoneLight);
box(32, y, 15, 34, y, 16, stoneLight);
}
}
// The gate opens through the wall facing the viewer.
box(22, 13, 4, 25, 16, 9, null);
box(23, 13, 10, 24, 16, 10, null);
box(22, 16, 4, 25, 16, 8, "#765038");
box(23, 16, 9, 24, 16, 10, "#765038");
for (let x = 22; x <= 25; x++) {
box(x, 13, 4, x, 13, x === 22 || x === 25 ? 8 : 10,
x % 2 ? "#98643b" : "#ae7747");
set(x, 12, 5, "#e8b746");
}
box(21, 13, 4, 21, 13, 10, stoneLight);
box(26, 13, 4, 26, 13, 10, stoneLight);
box(22, 13, 11, 25, 13, 11, stoneLight);
// A small central keep rises behind the gate.
box(20, 21, 4, 28, 29, 13, stoneDark);
box(20, 20, 5, 28, 20, 13, stone);
box(21, 20, 14, 27, 28, 14, stoneLight);
for (let x = 20; x <= 28; x += 3) {
box(x, 20, 15, x + 1, 21, 16, stoneLight);
box(x, 28, 15, x + 1, 29, 16, stoneLight);
}
// Deep little arched window.
box(23, 19, 8, 25, 19, 10, "#745b49");
set(24, 19, 11, "#745b49");
box(22, 19, 8, 22, 19, 11, stoneLight);
box(26, 19, 8, 26, 19, 11, stoneLight);
box(23, 19, 12, 25, 19, 12, stoneLight);
// Four round corner towers; the back-right one is the main tower.
function tower(tx, ty, main) {
const top = main ? 23 : 17;
for (let z = 4; z <= top; z++) {
for (let dx = -4; dx <= 4; dx++) {
for (let dy = -4; dy <= 4; dy++) {
const r = Math.hypot(dx, dy);
if (r > 4.25) continue;
let color = dy < -1 ? stoneLight : (dy > 2 ? stoneDark : stone);
if (rand() < 0.065) color = stoneDark;
set(tx + dx, ty + dy, z, color);
}
}
}
// Narrow shadowed arrow slit on the viewer-facing side.
box(tx, ty - 4, 10, tx, ty - 4, 12, "#735b48");
set(tx, ty - 4, 13, stoneLight);
for (let dx = -4; dx <= 4; dx++) {
for (let dy = -4; dy <= 4; dy++) {
const r = Math.hypot(dx, dy);
if (r > 4.35) continue;
set(tx + dx, ty + dy, top + 1, stoneLight);
// Alternating teeth around the circular parapet.
if (r >= 2.7 && Math.floor(
(Math.atan2(dy, dx) + Math.PI) * 6 / Math.PI
) % 2 === 0) {
set(tx + dx, ty + dy, top + 2, stoneLight);
set(tx + dx, ty + dy, top + 3, stone);
}
}
}
}
tower(15, 15, false);
tower(33, 15, false);
tower(15, 33, false);
tower(33, 33, true);
// Red pennant on the tall main tower.
box(33, 33, 25, 33, 33, 34, "#714c34");
set(33, 32, 29, "#f7d46a");
for (let x = 34; x <= 40; x++) {
const width = x <= 37 ? 4 : 41 - x;
for (let z = 30; z < 30 + width; z++) {
set(x, 33, z, z === 33 ? "#ff7665" : "#d93f43");
}
}
// Toy bucket on the near-left beach.
const bx = 9, by = 7;
for (let z = 2; z <= 7; z++) {
for (let dx = -3; dx <= 3; dx++) {
for (let dy = -3; dy <= 3; dy++) {
const r = Math.hypot(dx, dy);
if (r > (z >= 6 ? 3.05 : 2.65)) continue;
const color = z === 7 ? "#ffe8c2"
: z === 4 || z === 5 ? "#fff1d5"
: dy < 0 ? "#fa6555" : "#d94244";
set(bx + dx, by + dy, z, color);
}
}
}
for (let dx = -2; dx <= 2; dx++) {
for (let dy = -2; dy <= 2; dy++) {
if (Math.hypot(dx, dy) < 2.1) set(bx + dx, by + dy, 7, "#ba8053");
}
}
for (let dx = -3; dx <= 3; dx++) {
const hz = 10 - Math.round(Math.abs(dx) * 0.55);
set(bx + dx, by, hz, "#f8d26a");
if (Math.abs(dx) === 3) line(bx + dx, by, 7, bx + dx, by, hz, 0, "#f8d26a");
}
// Bright toy shovel leaning in the sand on the near-right.
ellipsoid(38, 7, 3, 2, 1, 2, "#dc4a46");
box(37, 6, 3, 39, 6, 4, "#ff7660");
line(39, 7, 5, 42, 7, 12, 0, "#b77a42");
line(40, 7, 12, 44, 7, 12, 0, "#e8b05a");
set(42, 7, 13, "#e8b05a");
// A few bright shells scattered on the dry sand.
for (const [x, y] of [[17, 5], [29, 6], [6, 25], [42, 25], [20, 43]]) {
set(x, y, 2, "#fff0ce");
set(x + 1, y, 2, "#f5a98b");
}import { draw } from "./assets/voxel";
import { castilloDeArena } from "./assets/castillo-de-arena";
// En cada cuadro: parado sobre (x, y), el centro de su base.
draw(ctx, castilloDeArena, x, y, { size: 64, angle: 0, t: performance.now() / 1000 });
// size: lo que mide en px su lado más largo
// angle: giro sobre el piso, en radianes
// t: segundos; si se mueve, se anima solo con eso# En una terminal, en la carpeta del juego
fogon assets add castillo-de-arena
# Con las herramientas de Fogón (conector)
add_assets({ slug: "<tu-juego>", ids: ["castillo-de-arena"] })
# Queda en client/assets/castillo-de-arena.ts
# https://fogon.page/object/castillo-de-arena