← Parties and Christmas3D assetfogon/pinata
Piñata
Drag to turn it// «Piñata» · gpt-6-sol (medium)
// A colorful star-shaped Mexican piñata hanging from a rope on a wooden post: a round center body with seven cones sticking out, covered in fringed paper stripes of pink, yellow, turquoise and orange, with long colorful tassels hanging from the tips of the cones.
// Wooden stand and crossbar
box(2, 21, 0, 13, 31, 2, "#8b5430");
box(3, 22, 2, 12, 30, 3, "#ad7240");
box(5, 24, 3, 9, 28, 45, "#a96535");
box(5, 24, 4, 6, 24, 44, "#c98a4e");
box(8, 27, 4, 9, 28, 43, "#784522");
box(6, 23, 44, 28, 27, 46, "#a96535");
box(7, 23, 46, 27, 24, 46, "#d49a5c");
box(7, 27, 44, 27, 27, 45, "#784522");
for (let z = 6; z < 43; z += 7) {
line(5, 23, z, 5, 23, z + 3, 0, "#dc9c60");
line(8, 24, z + 2, 8, 24, z + 4, 0, "#85502d");
}
box(25, 22, 44, 29, 28, 45, "#e7b84f");
box(26, 22, 45, 28, 23, 45, "#ffe28a");
// The rope and entire piñata swing together from the crossbar.
part("pinata", [27, 23, 44]);
line(27, 23, 44, 27, 23, 40, 1, "#eac66f");
line(26, 22, 43, 26, 22, 41, 0, "#fff0a6");
sphere(27, 23, 40, 1.5, "#f4cc63");
const cx = 27, cy = 23, cz = 21;
const bands = [
["#e9448f", "#ff83bb", "#bc2a69"], // pink
["#ffd74e", "#fff187", "#d4a835"], // lemon
["#25c9c5", "#78eee1", "#159795"], // turquoise
["#ff923e", "#ffc06b", "#d5682d"] // orange
];
// Solid, rounded center with broad bands of fringed tissue paper.
for (let x = cx - 8; x <= cx + 8; x++)
for (let y = cy - 8; y <= cy + 8; y++)
for (let z = cz - 8; z <= cz + 8; z++) {
const d = ((x-cx)/8)**2 + ((y-cy)/8)**2 + ((z-cz)/8)**2;
if (d > 1) continue;
const band = bands[((Math.floor((z - 13) / 3) % 4) + 4) % 4];
const shade = y < cy - 3 || z > cz + 5 ? 1 : y > cy + 3 ? 2 : 0;
set(x, y, z, band[rand() < 0.08 ? (shade + 1) % 3 : shade]);
}
// Little loose paper edges on the viewer-facing side of the round body.
for (let z = 15; z <= 27; z += 3)
for (let x = cx - 7; x <= cx + 7; x++) {
const q = 64 - (x-cx)**2 - (z-cz)**2;
if (q < 5 || rand() < 0.23) continue;
const y = Math.round(cy - Math.sqrt(q));
const color = bands[Math.floor((z - 13) / 3) % 4][1];
set(x, y - 1, z, color);
if (rand() < 0.65) set(x, y - 1, z - 1, color);
}
// Seven striped, tapered points radiating around the center.
for (let i = 0; i < 7; i++) {
const a = Math.PI / 2 + i * 2 * Math.PI / 7;
const ux = Math.cos(a), uz = Math.sin(a);
for (let x = 8; x <= 46; x++)
for (let z = 2; z <= 42; z++) {
const dx = x - cx, dz = z - cz;
const s = dx * ux + dz * uz;
if (s < 6 || s > 19) continue;
const t = -dx * uz + dz * ux;
const r = Math.max(0.6, 5.25 * (19 - s) / 13);
if (Math.abs(t) > r) continue;
for (let y = 17; y <= 29; y++) {
if (t*t + ((y-cy)*1.05)**2 > r*r) continue;
const stripe = Math.floor((s - 6) / 2.15) % 4;
const band = bands[(stripe + i) % 4];
const shade = y < cy - 1 || z > cz + 3 ? 1 : y > cy + 2 ? 2 : 0;
set(x, y, z, band[rand() < 0.045 ? (shade + 1) % 3 : shade]);
}
}
// Short, individually cut fringe along the front of each cone.
for (let s = 8; s <= 17; s += 3)
for (let t = -4; t <= 4; t++) {
const r = Math.max(0.6, 5.25 * (19 - s) / 13);
if (Math.abs(t) >= r - 0.3 || rand() < 0.22) continue;
const x = Math.round(cx + ux*s - uz*t);
const z = Math.round(cz + uz*s + ux*t);
const y = Math.round(cy - Math.sqrt(Math.max(0, r*r - t*t)) / 1.05) - 1;
const color = bands[(Math.floor((s-6)/2.15) + i) % 4][1];
set(x, y, z, color);
if (rand() < 0.7) set(x, y, z - 1, color);
}
const tx = Math.round(cx + 19*ux);
const tz = Math.round(cz + 19*uz);
sphere(tx, cy, tz, 1.35, bands[(i + 2) % 4][1]);
// Long, loose tassel ribbons hang vertically from every point.
const ribbons = ["#ff63aa", "#ffe35e", "#37dfd5", "#ffa24e", "#c995ef"];
for (let k = 0; k < 6; k++) {
const spread = k - 2.5;
const y = cy + Math.round(spread * 0.7);
const x = tx + Math.round(spread * 0.55);
const length = 4 + (k % 3) + (tz < 12 ? 1 : 0);
for (let n = 1; n <= length && tz - n >= 0; n++) {
const flutter = n > 3 ? Math.round(Math.sin(n * 1.4 + k) * 0.8) : 0;
set(x + flutter, y, tz - n, ribbons[(k + i) % ribbons.length]);
if (n < 3) set(x + flutter, y + 1, tz - n, ribbons[(k + i) % ribbons.length]);
}
}
}
animate("pinata", { type: "swing", axis: "x", speed: 2, amplitude: 0.35 });import { draw } from "./assets/voxel";
import { pinata } from "./assets/pinata";
// Every frame: standing on (x, y), the centre of its base.
draw(ctx, pinata, 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 pinata
# With Fogón's tools (connector)
add_assets({ slug: "<your-game>", ids: ["pinata"] })
# It lands in client/assets/pinata.ts
# https://fogon.page/object/pinata