← Basics3D assetfogon/anana-maduro
Ananá maduro
Drag to turn it// «Ananá maduro» · gpt-6-sol (medium)
// Pineapple («Ananá maduro»).
// Pineapple — Ananá maduro
const cx = 24, cy = 24;
function radiusAt(z) {
const profile = [
[0, 5.5], [2, 8], [5, 10], [9, 11.5],
[15, 12], [21, 11.5], [25, 10], [28, 7.5], [29, 4.5]
];
for (let i = 1; i < profile.length; i++) {
if (z <= profile[i][0]) {
const [z0, r0] = profile[i - 1];
const [z1, r1] = profile[i];
return r0 + (r1 - r0) * (z - z0) / (z1 - z0);
}
}
return 0;
}
function edgeDistance(v, period) {
const p = ((v % period) + period) % period;
return Math.min(p, period - p);
}
// Solid, softly rounded golden fruit.
for (let z = 0; z <= 29; z++) {
const r = radiusAt(z);
for (let x = 11; x <= 37; x++) {
for (let y = 11; y <= 37; y++) {
const dx = x - cx, dy = y - cy;
const rr = Math.sqrt(dx * dx + (dy / 0.94) ** 2);
if (rr > r) continue;
const surface = rr > r - 1.35 || z === 29;
let color = z < 6 ? "#b77b22" : z > 23 ? "#d99b29" : "#cb8d24";
if (surface) {
const angle = Math.atan2(dy / 0.94, dx);
const arc = angle * 11.5;
const a = edgeDistance(arc + z * 1.06, 6);
const b = edgeDistance(arc - z * 1.06, 6);
const light = dx < 0 && dy < 2;
// Crossing diagonal seams outline the pineapple's diamond-shaped eyes.
if (a < 0.58 && b < 0.58) color = "#86551c";
else if (a < 0.72 || b < 0.72)
color = light ? "#aa711e" : "#98621c";
else if (a < 1.35 || b < 1.35)
color = light ? "#f0b83c" : "#df9d29";
else
color = light ? "#e9ae35" : "#d09225";
if (rand() < 0.14)
color = light ? "#f4c54b" : "#b97820";
if (z < 5 && rand() < 0.45) color = "#a96d20";
}
set(x, y, z, color);
}
}
}
// Small, raised golden scales catch the light along the fruit's front.
for (let z = 3; z <= 27; z++) {
const r = radiusAt(z);
for (let x = 13; x <= 35; x++) {
const dx = x - cx;
const front = cy - Math.round(0.94 * Math.sqrt(Math.max(0, r * r - dx * dx)));
if (front >= cy || !get(x, front, z)) continue;
const arc = Math.atan2((front - cy) / 0.94, dx) * 11.5;
const a = edgeDistance(arc + z * 1.06, 6);
const b = edgeDistance(arc - z * 1.06, 6);
if (a > 0.9 && a < 1.7 && b > 0.9 && b < 1.8 && rand() < 0.58)
set(x, front - 1, z, rand() < 0.35 ? "#f5c54a" : "#e5aa31");
}
}
// Dense dark-green socket at the base of the crown.
ellipsoid(cx, cy, 29, 5, 5, 3, "#315e26");
ellipsoid(cx, cy, 31, 3, 3, 2, "#427a2d");
function leaf(angle, startR, length, baseZ, lift, width) {
const dx = Math.cos(angle), dy = Math.sin(angle);
const px = -dy, py = dx;
const steps = Math.ceil(length * 1.7);
for (let i = 0; i <= steps; i++) {
const t = i / steps;
const r = startR + length * t;
const x = cx + dx * r, y = cy + dy * r;
const z = baseZ + lift * Math.sin(Math.PI * t * 0.8) - 1.2 * t;
const w = Math.max(0, Math.round(width * (1 - t) - 0.15));
for (let side = -w; side <= w; side++) {
const bx = Math.round(x + px * side);
const by = Math.round(y + py * side);
const bz = Math.round(z);
set(bx, by, bz, side === 0 ? "#346c27" : "#285821");
if (t < 0.9) {
set(bx, by, bz + 1,
side < 0 ? "#66a63b" : side === 0 ? "#4d8d32" : "#39732a");
}
}
}
}
// Long, narrow leaves spread outward in staggered, spiky whorls.
for (let ring = 0; ring < 3; ring++) {
const count = ring === 0 ? 15 : ring === 1 ? 13 : 11;
for (let i = 0; i < count; i++) {
const angle = i * Math.PI * 2 / count + ring * 0.43;
const length = (ring === 0 ? 13 : ring === 1 ? 16 : 11) + rand() * 2;
leaf(angle, ring === 2 ? 1 : 2, length,
ring === 0 ? 30 : ring === 1 ? 32 : 34,
ring === 1 ? 3.5 : 2.5, ring === 1 ? 1.5 : 1);
}
}
// Upright, slightly splayed pointed leaves complete the tall crown.
for (let i = 0; i < 17; i++) {
const angle = i * Math.PI * 2 / 17 + 0.2;
const height = 7 + rand() * 7;
const reach = 2.5 + rand() * 5;
const dx = Math.cos(angle), dy = Math.sin(angle);
const px = -dy, py = dx;
for (let j = 0; j <= Math.ceil(height * 1.5); j++) {
const t = j / Math.ceil(height * 1.5);
const r = 0.5 + reach * t * t;
const x = Math.round(cx + dx * r);
const y = Math.round(cy + dy * r);
const z = Math.round(32 + height * t);
const broad = t < 0.53 && i % 3 !== 0;
set(x, y, z, "#326b29");
if (broad) set(Math.round(x + px), Math.round(y + py), z, "#4c8d34");
if (t > 0.18 && t < 0.9) set(x, y, z + 1, "#65a942");
}
}import { draw } from "./assets/voxel";
import { ananaMaduro } from "./assets/anana-maduro";
// Every frame: standing on (x, y), the centre of its base.
draw(ctx, ananaMaduro, 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 anana-maduro
# With Fogón's tools (connector)
add_assets({ slug: "<your-game>", ids: ["anana-maduro"] })
# It lands in client/assets/anana-maduro.ts
# https://fogon.page/object/anana-maduro