← Pirates and sea3D assetfogon/jaula-de-prisionero
Jaula de prisionero
Drag to turn it// «Jaula de prisionero» · gpt-6-sol (medium)
// A hanging cage: a tall wooden post with an arm from which hangs a round black iron bird-style cage big enough for a person, with a small skeleton hand peeking through the bars.
const wood = "#87502d", woodLight = "#ad7040", woodDark = "#60391f";
const iron = "#202830", ironLight = "#46515a", ironDark = "#11191f";
const gold = "#efbd49", bone = "#f3e4bb";
const cx = 31, cy = 24;
// A little sandy ground around the foot of the gallows.
ellipsoid(12, 24, 0, 8, 7, 1, "#eacb86");
for (let i = 0; i < 85; i++) {
const x = 5 + Math.floor(rand() * 15);
const y = 17 + Math.floor(rand() * 15);
if (get(x, y, 0) === "#eacb86")
set(x, y, 0, rand() < 0.5 ? "#f5da98" : "#dcb976");
}
// Solid, weathered wooden post.
box(10, 21, 0, 16, 27, 2, woodDark);
box(11, 22, 2, 15, 26, 41, wood);
box(11, 22, 3, 11, 26, 40, woodLight);
box(12, 22, 3, 12, 22, 40, "#bd8050");
box(15, 22, 3, 15, 26, 40, woodDark);
box(10, 21, 2, 16, 27, 3, woodLight);
box(10, 21, 37, 17, 27, 39, woodDark);
// Long projecting beam, with a chunky diagonal brace.
box(11, 22, 38, 34, 26, 41, wood);
box(12, 22, 41, 33, 22, 41, "#c18a53");
box(12, 26, 38, 33, 26, 38, woodDark);
line(15, 24, 26, 25, 24, 38, 1.5, woodDark);
line(16, 23, 27, 25, 23, 38, 0.7, woodLight);
box(32, 21, 37, 35, 27, 41, woodDark);
box(33, 22, 40, 35, 26, 41, woodLight);
// Grain and plank seams on visible faces.
for (let z = 5; z < 37; z += 5) {
line(12, 21, z, 12, 21, z + 2, 0, woodLight);
line(14, 21, z + 2, 14, 21, z + 3, 0, woodDark);
}
for (let x = 17; x < 32; x += 5) {
line(x, 21, 39, x + 2, 21, 39, 0, woodLight);
set(x + 1, 21, 38, woodDark);
}
for (const z of [5, 35]) {
box(10, 21, z, 16, 27, z, ironDark);
box(11, 21, z, 13, 21, z, ironLight);
set(12, 20, z, gold);
set(15, 20, z, gold);
}
for (const x of [15, 25, 32]) {
set(x, 21, 39, ironDark);
set(x, 21, 40, ironLight);
}
// All of the suspended ironwork swings together from this pin.
part("cage", [cx, cy, 36]);
animate("cage", { type: "swing", axis: "x", speed: 1.5, amplitude: 0.15 });
box(cx - 1, cy - 1, 35, cx + 1, cy + 1, 36, ironLight);
set(cx, cy - 2, 36, gold);
for (const z of [32, 34]) {
box(cx, cy - 1, z, cx, cy + 1, z + 1, iron);
set(cx, cy - 2, z, ironLight);
}
box(cx - 1, cy - 1, 30, cx + 1, cy + 1, 32, iron);
set(cx, cy - 2, 31, gold);
// Rounded cage profile. The hoops and vertical ribs remain open between bars.
function radius(z) {
const profile = [
[8, 8], [10, 9], [12, 9.8], [16, 10],
[20, 9.4], [23, 8.2], [26, 6.2],
[28, 4.2], [30, 1.8]
];
for (let i = 1; i < profile.length; i++) {
if (z <= profile[i][0]) {
const a = profile[i - 1], b = profile[i];
return a[1] + (b[1] - a[1]) * (z - a[0]) / (b[0] - a[0]);
}
}
return 1.8;
}
function hoop(z, r, color, thickness) {
const n = 84;
for (let i = 0; i < n; i++) {
const a = i * 2 * Math.PI / n;
const b = (i + 1) * 2 * Math.PI / n;
line(cx + r * Math.cos(a), cy + r * Math.sin(a), z,
cx + r * Math.cos(b), cy + r * Math.sin(b), z,
thickness, color);
}
}
// Heavy round base with a visible wooden floor inside.
ellipsoid(cx, cy, 8, 8.5, 8.5, 1, ironDark);
ellipsoid(cx, cy, 9, 7.2, 7.2, 0.5, woodDark);
for (let x = 25; x <= 37; x += 3) {
for (let y = 19; y <= 29; y++)
if (get(x, y, 9) === woodDark) set(x, y, 9, woodLight);
}
hoop(8, 8.3, iron, 0.9);
hoop(10, radius(10), ironLight, 0.65);
hoop(20, radius(20), iron, 0.65);
hoop(26, radius(26), ironLight, 0.7);
hoop(29, radius(29), iron, 0.8);
// Fourteen stout, bowed iron bars, with lighter front-facing edges.
for (let i = 0; i < 14; i++) {
const a = i * Math.PI * 2 / 14;
for (let z = 8; z < 30; z++) {
const r0 = radius(z), r1 = radius(z + 1);
line(cx + r0 * Math.cos(a), cy + r0 * Math.sin(a), z,
cx + r1 * Math.cos(a), cy + r1 * Math.sin(a), z + 1,
0.55, iron);
}
if (Math.sin(a) < -0.35) {
for (let z = 12; z < 25; z += 3) {
const r = radius(z);
set(cx + r * Math.cos(a), cy + r * Math.sin(a) - 0.5,
z, ironLight);
}
}
}
hoop(8, 8.3, ironLight, 0.65);
hoop(20, radius(20), ironLight, 0.45);
hoop(29, radius(29), ironLight, 0.55);
sphere(cx, cy, 30, 2, iron);
set(cx, cy - 2, 30, gold);
// Rivets and a conspicuous little gold door latch on the front.
for (const x of [25, 31, 37]) {
set(x, 15, 10, gold);
set(x, 15, 20, gold);
}
box(36, 14, 15, 37, 14, 17, gold);
set(37, 13, 16, "#fff0a3");
// A skeletal arm reaches out between the bars toward the viewer.
line(34, 19, 15, 34, 13, 16, 0.75, "#d4c69e");
sphere(34, 12, 16, 1.3, bone);
line(33, 11, 16, 32, 10, 18, 0.5, bone);
line(34, 11, 17, 34, 9, 20, 0.5, bone);
line(35, 11, 17, 36, 9, 19, 0.5, bone);
line(35, 11, 16, 37, 10, 17, 0.5, bone);
line(33, 12, 15, 31, 11, 14, 0.5, bone);
for (const [x, y, z] of [
[32, 10, 18], [34, 9, 20], [36, 9, 19],
[37, 10, 17], [31, 11, 14]
]) set(x, y, z, "#fff4d6");
part("body", [0, 0, 0]);import { draw } from "./assets/voxel";
import { jaulaDePrisionero } from "./assets/jaula-de-prisionero";
// Every frame: standing on (x, y), the centre of its base.
draw(ctx, jaulaDePrisionero, 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 jaula-de-prisionero
# With Fogón's tools (connector)
add_assets({ slug: "<your-game>", ids: ["jaula-de-prisionero"] })
# It lands in client/assets/jaula-de-prisionero.ts
# https://fogon.page/object/jaula-de-prisionero