← Basics3D assetfogon/velero
Velero
Drag to turn it// «Velero» · gpt-6-sol (medium)
// Sailboat («Velero»).
// Water: a low, irregular turquoise patch beneath the boat.
for (let x = 2; x <= 46; x++) {
for (let y = 6; y <= 42; y++) {
const dx = (x - 24) / 22;
const dy = (y - 24) / 16;
const edge = dx * dx + dy * dy;
const ripple = 0.055 * Math.sin(x * 1.7 + y * 0.8)
+ 0.035 * Math.sin(y * 2.3 - x);
if (edge < 1 + ripple) {
set(x, y, 0, rand() < 0.23 ? "#176a79" : "#217c88");
if (edge < 0.88 + ripple) {
set(x, y, 1, rand() < 0.17 ? "#2394a2" : "#32a7b3");
if (edge < 0.72 && rand() < 0.14)
set(x, y, 2, "#45b6c0");
}
}
}
}
// Short, broken highlights on the waves.
for (const [x, y, n] of [
[5, 27, 3], [8, 35, 4], [14, 39, 3], [27, 38, 4],
[36, 35, 3], [40, 29, 4], [40, 16, 3], [12, 11, 3]
]) {
for (let i = 0; i < n; i++)
set(x + i, y, 2, i % 3 ? "#83d5d5" : "#b5e4dc");
}
// A broad stern, rounded middle and pointed bow.
function beam(x) {
if (x < 10) return 8 + (x - 7) * 0.5;
if (x <= 32) return 9.5;
return Math.max(0.5, 9.5 * (44 - x) / 12);
}
function limits(x, inset = 0) {
const b = Math.max(0, beam(x) - inset);
return [Math.ceil(24 - b), Math.floor(24 + b)];
}
for (let z = 4; z <= 12; z++) {
const inset = z <= 5 ? 2.5 : z <= 7 ? 1.3 : 0;
for (let x = 7; x <= 43; x++) {
const [lo, hi] = limits(x, inset);
for (let y = lo; y <= hi; y++) {
let color;
if (z === 12) {
color = (y === lo || y === hi || x === 7 || x === 43)
? "#f0dfad"
: (x % 4 === 0 ? "#ba8250" : "#d8a265");
} else if (z === 11) color = "#f0dbad";
else if (z === 10) color = "#d4a05c";
else if (z === 9) color = "#efd09a";
else if (z === 8) color = "#273e4a";
else if (z >= 6) color = rand() < 0.12 ? "#253e4b" : "#172e3c";
else color = "#142b38";
set(x, y, z, color);
}
}
}
// Dark waterline, then the long golden stripe on the visible side.
for (let x = 9; x <= 40; x++) {
const [front, back] = limits(x);
if (x % 5 !== 0) set(x, front, 7, "#122636");
set(x, front, 9, "#d3a062");
set(x, back, 9, "#bb8954");
if (x % 8 === 2 && x < 35) {
set(x, front, 8, "#526677");
set(x + 1, front, 8, "#d8d6c5");
}
}
// Visible deck planks and a dark central hatch.
for (let x = 10; x <= 37; x++) {
const [lo, hi] = limits(x, 2);
for (let y = lo; y <= hi; y++) {
if (y % 4 === 0 && rand() < 0.75) set(x, y, 13, "#b7834d");
else if (rand() < 0.10) set(x, y, 13, "#edbd78");
}
}
box(12, 21, 13, 17, 26, 13, "#8b5934");
box(13, 22, 14, 16, 25, 14, "#b98248");
box(14, 23, 14, 15, 24, 14, "#4f4334");
// Low cream gunwale, with individual rail posts and open gaps.
for (let x = 8; x <= 41; x++) {
const [front, back] = limits(x);
set(x, front, 13, "#eee1bd");
set(x, back, 13, "#e2ca98");
if (x % 4 === 1 && x < 40) {
box(x, front, 14, x, front, 16, "#f4e7c8");
box(x, back, 14, x, back, 15, "#ddc697");
}
if (x >= 10 && x <= 37) {
set(x, front, 16, x % 5 === 0 ? "#f7ebce" : "#d5b983");
set(x, back, 15, "#e6d4ad");
}
}
for (let y = 17; y <= 31; y++) {
box(8, y, 13, 8, y, 14, "#ebd4a4");
if (y % 4 === 1) set(8, y, 15, "#f5e7c8");
}
// Small raised companionway on the forward deck.
box(26, 19, 13, 33, 27, 15, "#c88f50");
box(26, 19, 16, 33, 27, 16, "#f4e5bc");
box(28, 18, 13, 31, 19, 15, "#e6c58c");
box(29, 18, 14, 30, 18, 15, "#665641");
for (let x = 27; x <= 32; x += 2)
set(x, 19, 15, "#fff1cd");
// The smaller, rearward cream sail. Its stepped edge remains distinct
// from the larger sail on the other side of the mast.
for (let z = 16; z <= 36; z++) {
const left = Math.round(19 - (36 - z) * 0.60);
for (let x = left; x <= 19; x++) {
for (let y = 20; y <= 22; y++) {
let c = y === 20 ? "#eee7d3" : "#c9c2ac";
if (x === left) c = "#c7bd9f";
else if (z % 6 === 0 && rand() < 0.35) c = "#faf3dd";
else if (rand() < 0.065) c = "#d5cfbc";
set(x, y, z, c);
}
}
}
for (let z = 17; z <= 34; z++) {
const left = Math.round(19 - (36 - z) * 0.60);
if (z % 3 === 0) set(left + 1, 19, z, "#fbf4df");
}
line(7, 20, 16, 18, 20, 35, 0, "#a9a997");
// Dark-blue stitched emblem on the front sail.
for (const [x, z] of [
[15, 28], [16, 28], [14, 27], [17, 27],
[14, 26], [17, 26], [15, 25], [16, 25],
[13, 23], [14, 24], [17, 24], [18, 23],
[14, 22], [15, 22], [16, 22], [17, 22],
[15, 21], [16, 21]
]) set(x, 19, z, "#254965");
set(15, 19, 26, "#376884");
set(16, 19, 23, "#376884");
// Large triangular foresail, set a little farther back.
for (let z = 16; z <= 37; z++) {
const right = Math.min(41, 22 + Math.round((37 - z) * 0.91));
for (let x = 22; x <= right; x++) {
for (let y = 25; y <= 27; y++) {
let c = y === 25 ? "#eee8d7" : "#c8c6b5";
if (x === right) c = "#b8b7a9";
else if (rand() < 0.06) c = "#d7d4c6";
else if (y === 25 && rand() < 0.11) c = "#fcf4df";
set(x, y, z, c);
}
}
if (right > 24) set(right - 1, 24, z, "#f9f1dc");
}
// Narrow navy bands running across the cream canvas.
for (const z of [21, 26]) {
const right = 22 + Math.round((37 - z) * 0.91);
for (let x = 23; x <= right - 1; x++)
set(x, 24, z, x % 7 === 0 ? "#3a6279" : "#294a61");
}
line(23, 24, 17, 40, 24, 17, 0, "#a48b68");
// Mast, boom and their warm wooden fittings.
box(20, 23, 13, 21, 24, 40, "#806342");
for (let z = 16; z <= 39; z++) {
set(20, 22, z, z % 6 === 0 ? "#d0a568" : "#ae8250");
set(21, 22, z, "#d1a46c");
}
line(8, 22, 16, 22, 22, 16, 0, "#775838");
line(22, 24, 16, 40, 24, 16, 0, "#b58c58");
box(19, 22, 14, 22, 25, 16, "#a77742");
// Pennant flying to the right from the masthead.
box(20, 22, 41, 21, 24, 41, "#bd965e");
box(22, 22, 40, 25, 23, 41, "#ba553c");
box(26, 22, 41, 28, 23, 41, "#c65740");
set(28, 22, 40, "#a84335");
set(22, 21, 41, "#e88a57");
// A little red-and-blue ensign at the stern.
box(9, 23, 14, 9, 23, 19, "#805b3a");
box(6, 22, 18, 8, 23, 19, "#b54a3d");
box(5, 22, 17, 8, 23, 17, "#315575");
set(6, 21, 19, "#d77357");import { draw } from "./assets/voxel";
import { velero } from "./assets/velero";
// Every frame: standing on (x, y), the centre of its base.
draw(ctx, velero, 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 velero
# With Fogón's tools (connector)
add_assets({ slug: "<your-game>", ids: ["velero"] })
# It lands in client/assets/velero.ts
# https://fogon.page/object/velero