← Piezas de nivel 2Objeto 3Dfogon/nivel-canon
Cañón
Arrastrá para girarlo// «Cañón» · gpt-6-sol (medium)
// A level cannon that shoots balls: a squat black iron cannon barrel (radius 9, 26 long) pointing forward (-y) and slightly up, on a chunky wooden carriage with two big wooden wheels with iron rims, a pyramid of three black cannonballs beside it on the floor.
const woodDark = "#77401f";
const wood = "#a9632d";
const woodLight = "#d58a42";
const ironDark = "#202630";
const iron = "#38414b";
const ironLight = "#59636d";
const brass = "#d79a3d";
// Chunky wooden carriage and its long, slightly raised rails.
box(13, 13, 4, 17, 37, 10, woodDark);
box(25, 13, 4, 29, 37, 10, woodDark);
box(13, 13, 10, 17, 35, 11, woodLight);
box(25, 13, 10, 29, 35, 11, woodLight);
box(17, 16, 5, 25, 19, 9, wood);
box(17, 31, 5, 25, 36, 10, wood);
box(14, 36, 6, 28, 38, 10, wood);
box(14, 38, 7, 28, 38, 9, woodLight);
// Grain and iron corner straps.
for (const x of [14, 26]) {
line(x, 15, 11, x, 33, 11, 0, "#e3a458");
for (const y of [16, 33]) {
box(x, y, 5, x + 2, y + 1, 7, ironDark);
set(x + 1, y, 7, ironLight);
}
}
// Uprights cradle the barrel at its trunnions.
for (const x of [13, 26]) {
box(x, 21, 10, x + 3, 28, 17, woodDark);
box(x, 21, 16, x + 3, 27, 17, woodLight);
box(x, 23, 17, x + 3, 26, 18, brass);
}
// Two open-spoked wheels, with thick wooden rims and iron tires.
const wheelY = 24, wheelZ = 10;
for (const [x0, x1] of [[7, 11], [31, 35]]) {
for (let x = x0; x <= x1; x++) {
for (let y = 14; y <= 34; y++) {
for (let z = 0; z <= 20; z++) {
const dy = y - wheelY, dz = z - wheelZ;
const r = Math.hypot(dy, dz);
if (r > 9.5) continue;
const a = Math.atan2(dz, dy);
const spoke = Math.abs(Math.sin(4 * a)) < 0.20;
if (r >= 8.0) {
set(x, y, z, r > 8.9 ? ironDark : ironLight);
} else if (r >= 6.4) {
set(x, y, z, dz > 0 ? woodLight : wood);
} else if (spoke || r <= 2.8) {
set(x, y, z, dz > 1 ? woodLight : woodDark);
}
}
}
}
// Bright side-facing tire edge and a large bolted hub.
const outer = x0 === 7 ? x0 : x1;
for (let y = 14; y <= 34; y++)
for (let z = 0; z <= 20; z++) {
const r = Math.hypot(y - wheelY, z - wheelZ);
if (r >= 8.5 && r <= 9.5 && z >= 10)
set(outer, y, z, ironLight);
}
for (let y = 21; y <= 27; y++)
for (let z = 7; z <= 13; z++)
if ((y - 24) ** 2 + (z - 10) ** 2 <= 9)
set(outer + (outer === 7 ? -1 : 1), y, z, brass);
set(outer + (outer === 7 ? -1 : 1), 24, 10, ironDark);
}
// The main barrel rises gently toward its forward (-y) muzzle.
const barrelX = 21;
const barrelZ = y => 20 + (23 - y) * 0.17;
for (let y = 10; y <= 36; y++) {
const cz = barrelZ(y);
const radius = (y >= 29 && y <= 31) || (y >= 14 && y <= 16) ? 8.8 : 8.1;
for (let x = 11; x <= 31; x++)
for (let z = 8; z <= 32; z++) {
const dx = x - barrelX, dz = z - cz;
if (dx * dx + dz * dz > radius * radius) continue;
let color = dz > 3 || (dz > 0 && dx < -3) ? ironLight
: dz < -4 ? ironDark : iron;
if (radius > 8.1) color = dz > 1 ? "#68717a" : ironDark;
set(x, y, z, color);
}
}
// Rounded breech and its small rear knob.
ellipsoid(21, 36, 18, 7, 3, 7, ironDark);
ellipsoid(21, 38, 18, 4, 2, 4, iron);
sphere(21, 40, 18, 2, ironLight);
// Thick, unmistakable muzzle lip and recessed black bore.
for (let y = 7; y <= 10; y++) {
const cz = barrelZ(y);
for (let x = 11; x <= 31; x++)
for (let z = 12; z <= 33; z++) {
const dx = x - 21, dz = z - cz;
const r2 = dx * dx + dz * dz;
if (r2 > 9.4 * 9.4) continue;
if (r2 < 5.3 * 5.3) {
set(x, y, z, y <= 9 ? "#10151c" : "#1c222a");
} else {
set(x, y, z, dz > 1 ? "#717b83" : iron);
if (r2 < 6.3 * 6.3) set(x, y, z, ironDark);
}
}
}
// A triangular stack of three cannonballs beside the carriage.
function cannonball(cx, cy, cz) {
for (let x = cx - 4; x <= cx + 4; x++)
for (let y = cy - 4; y <= cy + 4; y++)
for (let z = Math.max(0, cz - 4); z <= cz + 4; z++) {
const d2 = (x - cx) ** 2 + (y - cy) ** 2 + (z - cz) ** 2;
if (d2 > 3.5 * 3.5) continue;
set(x, y, z,
z > cz + 1 && x < cx ? ironLight :
z < cz - 1 ? "#191e26" : ironDark);
}
set(cx - 1, cy - 2, cz + 2, "#78818a");
}
cannonball(41, 18, 3);
cannonball(41, 26, 3);
cannonball(41, 22, 9);import { draw } from "./assets/voxel";
import { nivelCanon } from "./assets/nivel-canon";
// En cada cuadro: parado sobre (x, y), el centro de su base.
draw(ctx, nivelCanon, 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 nivel-canon
# Con las herramientas de Fogón (conector)
add_assets({ slug: "<tu-juego>", ids: ["nivel-canon"] })
# Queda en client/assets/nivel-canon.ts
# https://fogon.page/object/nivel-canon