← Piratas y marObjeto 3Dfogon/naufragio
Naufragio
Arrastrá para girarlo// «Naufragio» · gpt-6-sol (medium)
// A shipwreck half-sunk in sand: the broken front half of a wooden ship tilted at an angle, with a snapped mast, torn sail, holes in the hull, seaweed and a few barnacles.
const sand = ["#f5d993", "#efd08a", "#e6c17b"];
const wood = ["#704126", "#84502d", "#995f33", "#ad7140"];
const lightWood = "#c28a50";
const darkWood = "#493022";
const gold = "#f8c843";
const water = ["#38c8c5", "#53d9ce", "#80e7d9"];
function tilt(x, y) {
return Math.round((x - 24) * 0.13 + (y - 20) * 0.055);
}
function hullWidth(y) {
if (y < 8 || y > 37) return 0;
if (y < 18) return 2 + (y - 8) * 1.22;
if (y < 31) return 14;
return 14 - (y - 31) * 0.43;
}
function edgeColor(x, y, z) {
if (z % 3 === 0) return rand() < 0.65 ? lightWood : wood[3];
return wood[Math.min(3, Math.max(0, Math.floor((z - 3) / 5)))] ;
}
// Low, irregular beach.
for (let x = 3; x <= 44; x++) {
for (let y = 3; y <= 44; y++) {
const height = 1 + (rand() < 0.42 ? 1 : 0) +
(x > 12 && x < 39 && y > 11 && y < 39 ? 1 : 0);
for (let z = 0; z <= height; z++)
set(x, y, z, z === height ? sand[Math.floor(rand() * 3)] : "#dcb979");
}
}
// Bright, shallow water curling around the front corners of the beach.
for (let x = 3; x <= 44; x++) {
for (let y = 3; y <= 15; y++) {
if ((x < 12 || x > 38) && y < 12 + Math.sin(x * 0.8) * 2) {
set(x, y, 2, water[Math.floor(rand() * water.length)]);
if (rand() < 0.17) set(x, y, 3, "#b9f4e3");
}
}
}
// The pointed bow is at -y; the far end is an open, jagged break.
for (let y = 8; y <= 37; y++) {
const width = hullWidth(y);
for (let x = Math.ceil(24 - width); x <= Math.floor(24 + width); x++) {
const t = tilt(x, y);
for (let h = 3; h <= 16; h++) {
const half = width * (0.37 + 0.63 * (h - 3) / 13);
if (Math.abs(x - 24) > half) continue;
if (y >= 35 && h > 11 && (x + y * 3) % 5 < 2) continue;
const edge = Math.abs(x - 24) > half - 1.5;
const brokenEnd = y >= 35 && h > 6;
let color = h === 16 ? "#aa7040" :
edge ? edgeColor(x, y, h) : wood[1];
if (h === 16 && x % 3 === 0) color = "#c18a51";
if (brokenEnd && rand() < 0.33) color = darkWood;
set(x, y, h + t, color);
}
}
}
// Long deck-plank seams and a few loose, lifted boards.
for (let x = 13; x <= 35; x += 4) {
for (let y = 15; y <= 33; y++) {
if (Math.abs(x - 24) < hullWidth(y) - 2)
set(x, y, 16 + tilt(x, y), "#754625");
}
}
for (let i = 0; i < 13; i++) {
const x = 15 + Math.floor(rand() * 19);
const y = 17 + Math.floor(rand() * 17);
if (Math.abs(x - 24) < hullWidth(y) - 2)
box(x, y, 17 + tilt(x, y), x + 2, y, 17 + tilt(x, y), lightWood);
}
// Thick raised gunwales, with intermittent damage toward the snapped end.
for (let y = 12; y <= 35; y++) {
const w = hullWidth(y);
for (const side of [-1, 1]) {
const x = Math.round(24 + side * (w - 0.8));
for (let h = 17; h <= 20; h++) {
if (y > 31 && (y + side * 3 + h) % 7 < 3) continue;
set(x, y, h + tilt(x, y), h === 20 ? lightWood : wood[2]);
set(x - side, y, h + tilt(x, y), wood[1]);
}
if (y % 4 === 0 && y < 32)
box(x - side, y, 18 + tilt(x, y),
x - side, y, 21 + tilt(x, y), gold);
}
}
// Missing planks read as dark, ragged holes in the visible hull sides.
for (const hole of [
{ y: 19, h: 10, ry: 2, rz: 2 },
{ y: 28, h: 12, ry: 2, rz: 2 },
{ y: 24, h: 8, ry: 1, rz: 1 }
]) {
for (let y = hole.y - hole.ry; y <= hole.y + hole.ry; y++) {
for (let h = hole.h - hole.rz; h <= hole.h + hole.rz; h++) {
if (((y - hole.y) / (hole.ry + 0.5)) ** 2 +
((h - hole.h) / (hole.rz + 0.5)) ** 2 > 1) continue;
for (const side of [-1, 1]) {
const x = Math.round(24 + side * hullWidth(y) *
(0.37 + 0.63 * (h - 3) / 13));
set(x, y, h + tilt(x, y), "#302822");
set(x - side, y, h + tilt(x, y), "#302822");
}
}
}
}
// Bow timbers, gold-edged prow, and a little curled figurehead.
for (let y = 7; y <= 15; y++) {
const x = 24;
const z = 19 + Math.round((15 - y) * 0.48);
box(x - 1, y, z - 2, x + 1, y, z, wood[2]);
set(x, y, z + 1, gold);
}
line(24, 7, 23, 24, 5, 20, 1, gold);
line(24, 5, 20, 26, 6, 19, 0, gold);
// Exposed ribs and splintered beams sticking out of the broken back.
for (const x of [14, 19, 25, 31, 35]) {
const z = 13 + tilt(x, 35);
line(x, 32, z, x + (x % 3) - 1, 39, z + 2, 1, wood[2]);
line(x, 35, z, x + (x < 24 ? -2 : 2), 38, z + 4, 0, lightWood);
}
for (let x = 17; x <= 32; x += 3)
line(x, 35, 10 + tilt(x, 35), x, 37, 15 + tilt(x, 35), 0, darkWood);
// Snapped, leaning mast and its cross-yard.
line(24, 23, 17, 29, 24, 36, 1.5, wood[1]);
line(25, 22, 21, 29, 24, 35, 0, lightWood);
line(29, 24, 35, 31, 25, 38, 0, "#c68b51");
line(29, 24, 35, 27, 24, 37, 0, darkWood);
line(15, 22, 34, 39, 23, 34, 1, wood[2]);
set(15, 22, 34, gold);
set(39, 23, 34, gold);
// Torn black sail hangs in front of the yard.
for (let z = 26; z <= 34; z++) {
const left = 17 + Math.max(0, 29 - z) * 0.5;
const right = 37 - Math.max(0, 29 - z) * 0.7;
for (let x = Math.ceil(left); x <= Math.floor(right); x++) {
if (z < 29 && ((x >= 31 && (x + z) % 4 < 2) ||
(x <= 21 && (x + z) % 5 < 2))) continue;
if ((x === 20 && z === 29) || (x === 34 && z === 30)) continue;
const shade = (x + z) % 6 === 0 ? "#41404a" :
z > 31 ? "#31313b" : "#242631";
set(x, 20, z, shade);
set(x, 21, z, shade);
}
}
line(17, 21, 34, 19, 20, 26, 0, "#a88767");
line(37, 21, 34, 34, 20, 27, 0, "#a88767");
// Bold white skull and crossed bones on the viewer-facing sail.
for (let x = 25; x <= 30; x++)
for (let z = 30; z <= 32; z++)
if (!((x === 25 || x === 30) && z === 32))
set(x, 19, z, "#fff1d1");
box(26, 19, 29, 29, 19, 30, "#fff1d1");
set(26, 18, 31, "#242631");
set(29, 18, 31, "#242631");
set(27, 18, 30, "#242631");
for (const x of [26, 28]) set(x, 18, 29, "#242631");
line(23, 19, 27, 32, 19, 25, 0, "#fff1d1");
line(23, 19, 25, 32, 19, 27, 0, "#fff1d1");
// Clumps of seaweed at the waterline and caught against the hull.
for (const [x, y, z] of [
[11, 17, 3], [38, 18, 3], [10, 27, 3], [39, 30, 3],
[17, 39, 3], [32, 39, 3], [7, 13, 2], [42, 15, 2]
]) {
line(x, y, z, x + 1, y, z + 4, 0, "#328e55");
line(x, y, z + 1, x - 1, y - 1, z + 3, 0, "#42aa61");
set(x + 1, y, z + 4, "#65bb63");
}
// Pale barnacles scattered across the lower planking.
for (const [y, h, side] of [
[14, 8, -1], [16, 11, 1], [21, 6, -1], [25, 9, 1],
[30, 7, -1], [32, 12, 1], [18, 7, 1], [27, 6, -1]
]) {
const x = Math.round(24 + side * hullWidth(y) *
(0.37 + 0.63 * (h - 3) / 13));
set(x, y, h + tilt(x, y), "#f2e9ca");
set(x, y, h + 1 + tilt(x, y), "#fff8db");
set(x, y + 1, h + tilt(x, y), "#cfceaa");
}
// A few unmistakably shiny pirate coins in the sand.
for (const [x, y] of [[33, 10], [35, 12], [37, 9], [13, 36], [16, 40]]) {
cylinder(x, y, 3, 1, 1, gold);
set(x, y, 4, "#ffe875");
}import { draw } from "./assets/voxel";
import { naufragio } from "./assets/naufragio";
// En cada cuadro: parado sobre (x, y), el centro de su base.
draw(ctx, naufragio, 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 naufragio
# Con las herramientas de Fogón (conector)
add_assets({ slug: "<tu-juego>", ids: ["naufragio"] })
# Queda en client/assets/naufragio.ts
# https://fogon.page/object/naufragio