← Parties and Christmas3D assetfogon/gorrito-y-cornetas
Gorrito y cornetas
Drag to turn it// «Gorrito y cornetas» · gpt-6-sol (medium)
// Party accessories on a small round table: two cone-shaped party hats with stripes and pompoms (one blue and yellow, one pink and white), two curly paper party horns in red and green with gold tips, and a little pile of confetti in bright colors.
// Small round party table
const cx = 24, cy = 24;
const gold = "#f6c84a";
const colors = ["#ff5a68", "#27c997", "#49b9f2", "#ffe45c", "#c892ee", "#ff92c6"];
function disk(xc, yc, z, r, inside, edge, edgeWidth = 1) {
for (let x = Math.floor(xc - r); x <= Math.ceil(xc + r); x++) {
for (let y = Math.floor(yc - r); y <= Math.ceil(yc + r); y++) {
const d = Math.hypot(x - xc, y - yc);
if (d <= r) set(x, y, z, d > r - edgeWidth ? edge : inside);
}
}
}
// Pedestal and broad, stable foot
disk(cx, cy, 0, 10, "#e8d6b5", "#bb9470");
disk(cx, cy, 1, 10, "#fff0ce", gold);
disk(cx, cy, 2, 8, "#fff1d3", "#dfaf43");
cylinder(cx, cy, 3, 3.5, 9, "#f7dfae");
cylinder(cx, cy, 3, 2.5, 9, "#fff0cf");
disk(cx, cy, 4, 4, "#fce9ba", gold);
disk(cx, cy, 10, 4, "#fce9ba", gold);
// Cream tabletop with a chunky turquoise-and-gold edge
disk(cx, cy, 12, 18, "#b4e5db", "#78c8bd");
disk(cx, cy, 13, 20, "#58bcb3", "#328f91", 2);
disk(cx, cy, 14, 20, "#7bd4c8", "#e5ac3c", 1);
disk(cx, cy, 15, 20, "#fff0d1", "#ffd763", 1.4);
for (let x = 5; x <= 43; x++) {
for (let y = 5; y <= 43; y++) {
if (Math.hypot(x - cx, y - cy) < 17.8 && rand() < 0.075) {
set(x, y, 15, rand() < 0.5 ? "#fff7e4" : "#f8e2bb");
}
}
}
// Tiny alternating jewels around the table's rim
for (let a = 0; a < 360; a += 18) {
const t = a * Math.PI / 180;
set(cx + 19 * Math.cos(t), cy + 19 * Math.sin(t), 15,
a % 36 === 0 ? "#fff5bc" : "#fa9bbd");
}
function partyHat(xc, yc, height, radius, base, stripe, brim, pom) {
disk(xc, yc, 16, radius + 0.9, brim, "#d99b3d");
disk(xc, yc, 17, radius + 0.7, brim, "#fff5c6", 0.8);
for (let z = 18; z <= 17 + height; z++) {
const level = z - 18;
const r = Math.max(0.55, radius * (1 - level / height));
const striped = Math.floor((level + 1) / 3) % 2 === 1;
for (let x = Math.floor(xc - r); x <= Math.ceil(xc + r); x++) {
for (let y = Math.floor(yc - r); y <= Math.ceil(yc + r); y++) {
if (Math.hypot(x - xc, y - yc) <= r) {
let c = striped ? stripe[0] : base[0];
if (y < yc - r * 0.25 && rand() < 0.65) {
c = striped ? stripe[1] : base[1];
} else if (y > yc + r * 0.4 && rand() < 0.38) {
c = striped ? stripe[2] : base[2];
}
set(x, y, z, c);
}
}
}
}
sphere(xc, yc, 19 + height, 2.2, pom[0]);
sphere(xc - 0.5, yc - 0.7, 20 + height, 1.05, pom[1]);
}
partyHat(16, 28, 17, 6.4,
["#258fdd", "#63c8f7", "#1769b1"],
["#ffdb55", "#fff18a", "#e8af31"],
"#f8bf44", ["#ffdf5b", "#fff5a4"]);
partyHat(32, 28, 15, 6.2,
["#f580b2", "#ffc0dc", "#d85591"],
["#fff9ed", "#ffffff", "#e8dce7"],
"#ead5de", ["#f9acd0", "#ffe3f0"]);
// Curled paper blowers lie across the front of the table.
// Their raised curls and gold ends stay readable in front of the hats.
function horn(points, main, light, dark) {
for (let i = 0; i < points.length - 1; i++) {
const a = points[i], b = points[i + 1];
line(...a, ...b, i < 2 ? 1.35 : 1.05,
i % 3 === 0 ? light : main);
}
for (let i = 1; i < points.length - 1; i += 2) {
const p = points[i];
set(p[0], p[1] - 1, p[2], dark);
}
const start = points[0], end = points[points.length - 1];
sphere(...start, 1.6, "#f4c34d");
set(start[0], start[1] - 1, start[2], "#fff0a0");
sphere(...end, 1.5, gold);
set(end[0], end[1] - 1, end[2] + 1, "#fff5ad");
}
horn([
[19, 21, 18], [17, 19, 18], [15, 17, 18], [12, 15, 19],
[10, 13, 20], [9, 12, 21], [9, 12, 23],
[11, 12, 24], [12, 12, 23], [11, 11, 22]
], "#e83749", "#ff6972", "#b72038");
horn([
[29, 21, 18], [31, 19, 18], [33, 17, 18], [36, 15, 19],
[38, 13, 20], [39, 12, 21], [39, 12, 23],
[37, 12, 24], [36, 12, 23], [37, 11, 22]
], "#24ae67", "#62df8c", "#17804e");
// A bright little mound of chunky paper confetti between the accessories
for (let i = 0; i < 105; i++) {
const x = 24 + Math.round((rand() + rand() + rand() - 1.5) * 5.5);
const y = 19 + Math.round((rand() + rand() - 1.5) * 4.5);
const d = Math.hypot(x - 24, y - 19);
if (d > 6 || Math.hypot(x - 16, y - 28) < 7 ||
Math.hypot(x - 32, y - 28) < 7) continue;
const z = 16 + Math.max(0, Math.floor((5 - d) * 0.38 * rand()));
const c = colors[Math.floor(rand() * colors.length)];
set(x, y, z, c);
if (rand() < 0.36) set(x + 1, y, z, c);
if (rand() < 0.12) set(x, y, z + 1, c);
}
// A few stray pieces scattered over the cream tabletop
for (let i = 0; i < 75; i++) {
const x = 6 + Math.floor(rand() * 37);
const y = 6 + Math.floor(rand() * 37);
if (Math.hypot(x - 24, y - 24) > 17 ||
Math.hypot(x - 16, y - 28) < 8 ||
Math.hypot(x - 32, y - 28) < 8 ||
(y > 10 && y < 22 && (x < 20 || x > 29))) continue;
const c = colors[Math.floor(rand() * colors.length)];
set(x, y, 16, c);
if (rand() < 0.28) set(x + 1, y, 16, c);
}import { draw } from "./assets/voxel";
import { gorritoYCornetas } from "./assets/gorrito-y-cornetas";
// Every frame: standing on (x, y), the centre of its base.
draw(ctx, gorritoYCornetas, 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 gorrito-y-cornetas
# With Fogón's tools (connector)
add_assets({ slug: "<your-game>", ids: ["gorrito-y-cornetas"] })
# It lands in client/assets/gorrito-y-cornetas.ts
# https://fogon.page/object/gorrito-y-cornetas