← Pirates and sea3D assetfogon/balandra
Balandra pirata
Drag to turn it// «Balandra pirata» · gpt-6-sol (medium)
// A small single-mast pirate sloop, hull along y with the bow at -y: warm brown wooden hull with a red and white striped band, one tall mast with a big triangular cream sail patched in places and a small black flag on top, a little wheel at the back. Sits on a thin turquoise water band at z=0.
const wood = ["#713b25", "#88482b", "#a45d32", "#ba7540"];
const cream = "#f5e6b9";
const red = "#c93637";
const white = "#fff2d8";
const gold = "#f4c34d";
// A thin, sunlit patch of sea.
for (let x = 4; x <= 44; x++) {
for (let y = 2; y <= 45; y++) {
const d = ((x - 24) / 20) ** 2 + ((y - 24) / 22) ** 2;
if (d < 1 && rand() > 0.025) {
set(x, y, 0, rand() < 0.17 ? "#68d9cf" : "#27bdbb");
if (d > 0.75 && rand() < 0.18) set(x, y, 0, "#a4ede0");
}
}
}
for (let y = 9; y <= 39; y += 5) {
for (const side of [-1, 1]) {
line(24 + side * 13, y, 0, 24 + side * 16, y + 2, 0, 0, "#b6f1df");
}
}
part("boat", [24, 24, 2]);
animate("boat", { type: "swing", axis: "y", speed: 1.8, amplitude: 0.08 });
function beam(y) {
if (y < 7 || y > 42) return 0;
if (y <= 15) return Math.round(1 + (y - 7) * 1.25);
if (y <= 34) return 11;
return Math.round(11 - (y - 34) * 0.5);
}
function hullWidth(y, z) {
const taper = [0, 0, 0.22, 0.38, 0.55, 0.71, 0.83, 0.92, 0.97, 1, 1, 1, 1, 0.96][z] || 0;
return Math.max(0, Math.round(beam(y) * taper));
}
// Solid, pointed hull with warmer upper planking.
for (let y = 7; y <= 42; y++) {
for (let z = 3; z <= 13; z++) {
const w = hullWidth(y, z);
for (let x = 24 - w; x <= 24 + w; x++) {
const outer = Math.abs(x - 24) >= w - 1;
let color = z < 6 ? wood[0] : z < 9 ? wood[1] : wood[2];
if (outer && rand() < 0.13) color = wood[Math.min(3, z < 7 ? 1 : 3)];
if (outer && z === 9) color = red;
if (outer && z === 10) color = white;
if (outer && z === 11 && rand() < 0.22) color = "#c7874c";
set(x, y, z, color);
}
}
}
// Deck boards and an unmistakable raised gunwale.
for (let y = 10; y <= 40; y++) {
const w = beam(y);
for (let x = 24 - w + 2; x <= 24 + w - 2; x++) {
set(x, y, 14, y % 4 === 0 ? "#bf8650" : "#a66d3b");
}
for (const side of [-1, 1]) {
const edge = 24 + side * w;
box(edge - (side < 0 ? 0 : 1), y, 14, edge + (side > 0 ? 0 : 1), y, 15, "#704127");
set(edge, y, 16, y % 5 === 0 ? gold : "#c38a4e");
if (y % 5 === 0) box(edge, y, 15, edge, y, 17, "#89512e");
// Tiny black gunports set into each flank.
if ([18, 25, 32].includes(y)) {
set(edge, y, 7, "#322b29");
set(edge, y, 8, "#242728");
set(edge, y + 1, 8, gold);
}
}
}
line(24, 7, 14, 24, 11, 17, 1, "#b47b43");
box(19, 41, 14, 29, 42, 16, "#95552f");
line(19, 42, 16, 29, 42, 16, 0, gold);
// Small stern cabin with bright windows.
box(19, 35, 15, 29, 40, 18, "#90552e");
box(18, 34, 19, 30, 41, 20, "#6e3d29");
box(19, 34, 20, 29, 40, 20, red);
for (const x of [21, 26]) {
box(x, 34, 16, x + 1, 34, 18, "#26383a");
set(x, 34, 18, "#91e1d6");
set(x + 1, 34, 16, gold);
}
line(18, 34, 19, 30, 34, 19, 0, gold);
// Mast and its chunky boom.
box(23, 23, 15, 25, 25, 44, "#79482c");
box(24, 23, 17, 24, 23, 43, "#bc8043");
line(10, 23, 18, 38, 23, 18, 1, "#87502f");
set(24, 24, 45, gold);
// Broad triangular sail, presented toward the front of the ship.
for (let z = 19; z <= 39; z++) {
const span = Math.floor((39 - z) * 13 / 20);
for (let x = 24 - span; x <= 24 + span; x++) {
const edge = Math.abs(x - 24) >= span - 1;
let color = edge ? "#ddc99c" : cream;
if (!edge && z % 6 === 0) color = "#ead5a8";
if (!edge && rand() < 0.035) color = "#e2d0a7";
box(x, 21, z, x, 22, z, color);
}
}
// Stitched-on, irregular repair patches.
for (const [x, z] of [[18, 23], [20, 24], [30, 25], [29, 26], [23, 31]]) {
set(x, 20, z, "#c9aa7d");
set(x + 1, 20, z, "#d4b78a");
set(x, 20, z + 1, "#d4b78a");
set(x + 1, 20, z + 1, "#c9aa7d");
set(x, 20, z + 2, "#b8956c");
}
// Mast visible down the face of the canvas.
box(24, 20, 18, 24, 20, 38, "#aa7040");
for (let z = 21; z <= 36; z += 5) set(24, 19, z, gold);
// A little ship's wheel behind the sail, toward the stern.
box(32, 32, 15, 32, 32, 18, "#71432b");
for (let i = 0; i < 12; i++) {
const a = i * Math.PI / 6;
set(32 + Math.round(3 * Math.cos(a)), 31, 20 + Math.round(3 * Math.sin(a)), "#a76534");
if (i % 2 === 0) {
set(32 + Math.round(4 * Math.cos(a)), 31, 20 + Math.round(4 * Math.sin(a)), gold);
line(32, 31, 20,
32 + Math.round(2 * Math.cos(a)), 31, 20 + Math.round(2 * Math.sin(a)),
0, "#a76534");
}
}
set(32, 30, 20, gold);
// A couple of cheerful coins lying on the foredeck.
for (const [x, y] of [[20, 15], [22, 16], [27, 14]]) {
set(x, y, 15, gold);
set(x + 1, y, 15, "#ffdf72");
}
// Flag has its own hinge at the masthead.
part("flag", [24, 24, 43]);
animate("flag", { type: "swing", axis: "z", speed: 4, amplitude: 0.3 });
for (let x = 25; x <= 34; x++) {
const bottom = x >= 32 ? 42 : 41;
const top = x >= 33 ? 43 : 44;
box(x, 23, bottom, x, 23, top, "#24282d");
if (x < 32) set(x, 22, 44, "#36383b");
}
// Tiny white skull and crossed bones.
box(28, 22, 43, 30, 22, 44, white);
set(28, 21, 43, "#24282d");
set(30, 21, 43, "#24282d");
set(29, 21, 42, white);
set(27, 22, 41, white);
set(31, 22, 41, white);import { draw } from "./assets/voxel";
import { balandra } from "./assets/balandra";
// Every frame: standing on (x, y), the centre of its base.
draw(ctx, balandra, 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 balandra
# With Fogón's tools (connector)
add_assets({ slug: "<your-game>", ids: ["balandra"] })
# It lands in client/assets/balandra.ts
# https://fogon.page/object/balandra