← BásicoObjeto 3Dfogon/torre-de-castillo
Torre de castillo
Arrastrá para girarlo// «Torre de castillo» · gpt-6-sol (medium)
// Castle turret («Torre de castillo»).
const cx = 24, cy = 24;
const oct = (x, y) => {
const a = Math.abs(x - cx), b = Math.abs(y - cy);
return Math.max(a, b) + 0.38 * Math.min(a, b);
};
const stone = ["#a9ab9b", "#babbaa", "#c8c8b4", "#d5d3bd"];
const trim = ["#b9b8a3", "#ceccb4", "#e0ddc4"];
function shade(x, y, z, top = false) {
let i = y < 24 ? 2 : 1;
if (x > 25) i++;
if (z % 5 === 0) i--;
if (rand() < 0.12) i += rand() < 0.5 ? -1 : 1;
if (top) i++;
return stone[Math.max(0, Math.min(3, i))];
}
// Uneven grassy footprint.
for (let x = 5; x <= 43; x++)
for (let y = 6; y <= 42; y++) {
const d = oct(x, y);
if (d < 18.2 || (d < 19.4 && rand() > 0.43)) {
set(x, y, 0, rand() < 0.3 ? "#596f42" : "#708551");
if (d > 15.2 || rand() < 0.13)
set(x, y, 1, ["#729149", "#91a95b", "#5f8144"][Math.floor(rand() * 3)]);
}
}
// Stepped stone foundation, with grass showing around it.
for (let z = 1; z <= 5; z++) {
const radius = z <= 2 ? 16.4 : z === 3 ? 15.7 : 13.3;
for (let x = 7; x <= 41; x++)
for (let y = 7; y <= 41; y++)
if (oct(x, y) <= radius)
set(x, y, z, z === 3 ? trim[1 + (rand() < 0.15 ? 1 : 0)] : shade(x, y, z, z === 3));
}
// Solid, slightly faceted lower tower and its stone courses.
for (let z = 5; z <= 30; z++)
for (let x = 10; x <= 38; x++)
for (let y = 10; y <= 38; y++) {
const d = oct(x, y);
if (d > 12.1) continue;
let c = shade(x, y, z);
if (d > 10.7) {
if (z === 10 || z === 17 || z === 24) c = "#898f83";
else if (z === 11 || z === 18 || z === 25) c = "#c9c8b2";
// Sparse vertical mortar lines, staggered between courses.
else if ((Math.floor((x + (Math.floor(z / 5) % 2) * 3) / 6) * 6 ===
x + (Math.floor(z / 5) % 2) * 3) && rand() < 0.35)
c = "#999e91";
}
set(x, y, z, c);
}
// Slim raised stone ribs make the cylindrical walls read at this scale.
for (let x of [13, 18, 23, 28, 33, 37])
for (let z = 7; z <= 27; z++) {
for (let y = 9; y <= 24; y++) {
if (oct(x, y) >= 11.6 && oct(x, y) <= 13.0) {
set(x, y, z, z % 7 === 0 ? "#a5a99a" : "#c1c3af");
break;
}
}
}
// Dark recessed arrow slits on the front.
for (const [x, bottom, height] of [[17, 15, 9], [23, 20, 6]]) {
for (let y = 9; y <= 16; y++)
if (oct(x, y) >= 10.4 && oct(x, y) <= 13.1) {
box(x, y, bottom, x + 1, y, bottom + height, "#555b55");
set(x - 1, y, bottom + height + 1, "#dedac1");
set(x + 2, y, bottom + height + 1, "#dedac1");
break;
}
}
// A slit on the right-hand side.
box(36, 22, 17, 38, 23, 24, "#555c57");
box(38, 21, 25, 38, 24, 25, "#d2d0ba");
// Projecting cornice beneath the battlements.
for (let z = 28; z <= 33; z++) {
const radius = z === 28 ? 13.2 : z <= 30 ? 14.8 : 15.3;
for (let x = 7; x <= 41; x++)
for (let y = 7; y <= 41; y++)
if (oct(x, y) <= radius) {
let c = shade(x, y, z, z === 31);
if (z === 28 || z === 30) c = "#8f9588";
if (z === 31) c = trim[Math.floor(rand() * 3)];
set(x, y, z, c);
}
}
// Open upper platform: thick pale parapet, darker floor within.
for (let z = 34; z <= 37; z++)
for (let x = 7; x <= 41; x++)
for (let y = 7; y <= 41; y++) {
const d = oct(x, y);
if (d <= 15.1 && d >= 10.7)
set(x, y, z, z === 37 ? "#dfdcc3" : shade(x, y, z, true));
else if (z === 34 && d < 10.7)
set(x, y, z, "#81877c");
}
// Alternating high merlons and low gaps around the battlement ring.
for (let x = 8; x <= 40; x++)
for (let y = 8; y <= 40; y++) {
const d = oct(x, y);
if (d < 11.1 || d > 15.1) continue;
const angle = Math.atan2(y - cy, x - cx);
const segment = Math.floor((angle + Math.PI) / (Math.PI / 8));
const high = segment % 2 === 0;
const top = high ? 42 : 38;
for (let z = 38; z <= top; z++)
set(x, y, z, z === top ? "#e9e4ca" : shade(x, y, z, true));
}
// Slightly taller squared corner-like posts at the front of the crown.
for (const [x, y] of [[12, 13], [25, 9], [37, 14], [39, 27], [27, 39]]) {
box(x - 1, y - 1, 37, x + 1, y + 1, 41, "#d3d0b8");
box(x - 1, y - 1, 42, x + 1, y + 1, 42, "#e8e3c9");
}
// Front entrance: a deep dark doorway cut into the tower.
for (let x = 27; x <= 33; x++)
for (let z = 4; z <= 15; z++) {
const opening = z <= 11 || (z <= 13 && x >= 28 && x <= 32) ||
(z <= 15 && x >= 29 && x <= 31);
if (opening)
for (let y = 9; y <= 16; y++) set(x, y, z, null);
}
box(28, 16, 4, 32, 16, 11, "#454b45");
box(29, 16, 12, 31, 16, 14, "#444a44");
// Chunky projecting arch surround and piers.
box(25, 10, 4, 27, 15, 13, "#c9c7ae");
box(33, 10, 4, 35, 15, 13, "#bdbda9");
box(26, 9, 12, 28, 15, 15, "#ded9bf");
box(32, 9, 12, 34, 15, 15, "#d0cbb3");
box(28, 9, 14, 32, 15, 16, "#dfdac0");
box(29, 8, 15, 31, 11, 16, "#e8e1c5");
for (let z of [6, 10]) {
box(25, 9, z, 27, 10, z, "#a8ab9a");
box(33, 9, z, 35, 10, z, "#a6a999");
}
box(27, 8, 3, 33, 10, 3, "#e0dcc4");
// A few small mossy patches along the base of the wall.
for (let i = 0; i < 95; i++) {
const x = 8 + Math.floor(rand() * 33);
const y = 8 + Math.floor(rand() * 33);
const z = 2 + Math.floor(rand() * 5);
if (oct(x, y) > 12 && oct(x, y) < 17 && get(x, y, z))
set(x, y, z, rand() < 0.5 ? "#758558" : "#879668");
}
// Flagpole rising from the center of the open roof.
box(23, 23, 34, 24, 24, 46, "#796749");
box(23, 22, 43, 24, 22, 46, "#aa8952");
box(22, 22, 46, 25, 24, 46, "#d8ba70");
// Windblown red banner, with a ragged flying end.
for (let x = 25; x <= 36; x++) {
const tip = x >= 34;
const upper = x <= 30 ? 45 : 44;
for (let z = tip ? 44 : 43; z <= upper; z++)
set(x, 22, z, rand() < 0.25 ? "#a74243" : "#c75450");
}
box(26, 21, 44, 29, 21, 45, "#e27863");
box(30, 22, 43, 32, 22, 43, "#913d42");
set(37, 22, 44, "#a94747");import { draw } from "./assets/voxel";
import { torreDeCastillo } from "./assets/torre-de-castillo";
// En cada cuadro: parado sobre (x, y), el centro de su base.
draw(ctx, torreDeCastillo, 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 torre-de-castillo
# Con las herramientas de Fogón (conector)
add_assets({ slug: "<tu-juego>", ids: ["torre-de-castillo"] })
# Queda en client/assets/torre-de-castillo.ts
# https://fogon.page/object/torre-de-castillo