← AsiáticoObjeto 3Dfogon/carpa-koi-gigante
Carpa koi gigante
Arrastrá para girarlo// «Carpa koi gigante» · gpt-6-sol (medium)
// A big chunky koi fish swimming horizontally along y, raised slightly off the ground: a plump white body with large orange-red patches and a few black spots, big flowing fins, a round mouth with two short whiskers at the front (-y), black dot eyes, a wide fan tail at the back.
const pearl = "#fff2d8";
const warmWhite = "#f3e1bf";
const shadowWhite = "#d6c5a9";
const orange = "#ef722d";
const vermilion = "#c94326";
const deepRed = "#a63426";
const gold = "#f5b957";
const ink = "#242a29";
// Shallow jade-green pool. Place its floor block by block at z=0.
part("pool", [24, 23, 0]);
for (let x = 8; x <= 40; x++) {
for (let y = 6; y <= 40; y++) {
const d = ((x - 24) / 16) ** 2 + ((y - 23) / 17) ** 2;
if (d <= 1) {
set(x, y, 0, d > 0.78 ? "#376b64" : "#579e96");
}
}
}
for (let a = 0; a < Math.PI * 2; a += 0.08) {
const x = Math.round(24 + Math.cos(a) * 11);
const y = Math.round(23 + Math.sin(a) * 12);
if (Math.sin(a * 7) > -0.3) set(x, y, 1, "#9cc9b2");
}
// Plump koi, swimming above the pool.
part("body", [24, 24, 20]);
ellipsoid(24, 22, 20, 12, 16, 10, warmWhite);
ellipsoid(24, 21, 21, 11, 15, 9, pearl);
ellipsoid(24, 9, 20, 7, 5, 6, pearl);
// Shade the body and paint broad koi markings.
forEach((x, y, z, color) => {
if (color !== pearl && color !== warmWhite) return;
let c = z < 16 ? shadowWhite : z > 26 ? "#fff8e9" : pearl;
const patch1 = ((x - 25) ** 2 / 115 + (y - 17) ** 2 / 38 + (z - 29) ** 2 / 85) < 1;
const patch2 = ((x - 21) ** 2 / 120 + (y - 30) ** 2 / 31 + (z - 28) ** 2 / 75) < 1;
const sidePatch = ((x - 13) ** 2 / 28 + (y - 25) ** 2 / 31 + (z - 20) ** 2 / 40) < 1;
const headBlaze = y < 15 && z > 25 && x > 20 && x < 29;
if (patch1 || patch2 || sidePatch || headBlaze) {
c = z > 27 ? orange : vermilion;
if (rand() < 0.12) c = "#db5729";
} else if (rand() < 0.035) {
c = "#e8d5b6";
}
set(x, y, z, c);
});
// Distinct black spots.
ellipsoid(35, 23, 23, 1, 3, 2, ink);
ellipsoid(15, 16, 25, 2, 2, 1, ink);
ellipsoid(27, 34, 26, 2, 2, 1, ink);
// High, swept dorsal fin.
for (let y = 18; y <= 36; y++) {
const peak = Math.max(0, 10 - Math.abs(y - 27) * 0.8);
const base = 28 + Math.max(0, (y - 31) * 0.3);
for (let z = Math.ceil(base); z <= Math.floor(30 + peak); z++) {
const halfWidth = z > 35 ? 1 : 2;
for (let x = 24 - halfWidth; x <= 24 + halfWidth; x++) {
set(x, y, z, z >= 29 + peak ? gold : y % 4 === 0 ? orange : vermilion);
}
}
}
line(24, 18, 29, 24, 27, 40, 1, gold);
// Broad pectoral fins swept back and down.
for (const side of [-1, 1]) {
const rootX = 24 + side * 10;
ellipsoid(rootX, 18, 17, 3, 5, 3, vermilion);
for (let reach = 0; reach <= 9; reach++) {
const x = rootX + side * reach;
const centerY = 19 + reach * 0.65;
const centerZ = 17 - reach * 0.9;
const spread = 2 + reach * 0.45;
for (let y = Math.ceil(centerY - spread); y <= Math.floor(centerY + spread); y++) {
for (let z = Math.ceil(centerZ - 1); z <= Math.floor(centerZ + 1); z++) {
set(x, y, z,
reach > 7 || Math.abs(y - centerY) > spread - 1 ? gold : orange);
}
}
}
line(rootX, 18, 17, rootX + side * 9, 24, 9, 0, deepRed);
}
// Small paired pelvic fins.
for (const side of [-1, 1]) {
line(24 + side * 7, 29, 14, 24 + side * 12, 33, 8, 2, orange);
line(24 + side * 8, 30, 13, 24 + side * 13, 35, 8, 0, gold);
}
// Round, forward-facing mouth with soft white lips.
ellipsoid(24, 6, 19, 5, 3, 5, warmWhite);
for (let x = 19; x <= 29; x++) {
for (let z = 14; z <= 24; z++) {
const d = ((x - 24) / 4.5) ** 2 + ((z - 19) / 4.5) ** 2;
if (d < 0.48) set(x, 3, z, deepRed);
else if (d < 1.05 && d > 0.43) {
set(x, 3, z, z >= 20 ? pearl : warmWhite);
if (d > 0.7) set(x, 4, z, pearl);
}
}
}
set(23, 3, 20, "#682b29");
set(24, 3, 20, "#682b29");
set(25, 3, 20, "#682b29");
// Glossy eyes and short curling whiskers.
for (const side of [-1, 1]) {
const ex = 24 + side * 8;
ellipsoid(ex, 11, 23, 2, 2, 2, gold);
sphere(ex + side, 10, 23, 1, ink);
set(ex + side, 9, 24, "#ffffff");
line(24 + side * 4, 4, 17, 24 + side * 7, 2, 15, 1, warmWhite);
line(24 + side * 7, 2, 15, 24 + side * 9, 2, 13, 0, gold);
}
animate("body", { type: "bob", axis: "z", speed: 1.3, amplitude: 1 });
// Separate swinging fan tail.
part("tail", [24, 37, 20]);
ellipsoid(24, 36, 20, 5, 5, 5, pearl);
for (let y = 38; y <= 45; y++) {
const t = y - 38;
const width = 5 + t * 2;
for (let x = 24 - width; x <= 24 + width; x++) {
const side = Math.abs(x - 24) / width;
const height = (4 + t * 0.95) * Math.sqrt(Math.max(0, 1 - side * side * 0.72));
for (let z = Math.ceil(20 - height); z <= Math.floor(20 + height); z++) {
if (y >= 44 && Math.abs(x - 24) <= y - 43 && z >= 17 && z <= 23) continue;
const edge = Math.abs(z - 20) > height - 1 || side > 0.94;
let color = edge ? gold : x % 5 === 0 ? "#dc592d" : orange;
if (y >= 44 && rand() < 0.12) color = "#f39242";
set(x, y, z, color);
}
}
}
for (const side of [-1, 1]) {
line(24, 38, 20, 24 + side * 17, 45, 27, 0, deepRed);
line(24, 38, 20, 24 + side * 17, 45, 13, 0, deepRed);
}
animate("tail", { type: "swing", axis: "z", speed: 4, amplitude: 0.4 });
animate("tail", { type: "bob", axis: "z", speed: 1.3, amplitude: 1 });import { draw } from "./assets/voxel";
import { carpaKoiGigante } from "./assets/carpa-koi-gigante";
// En cada cuadro: parado sobre (x, y), el centro de su base.
draw(ctx, carpaKoiGigante, x, y, { size: 64, angle: 0, t: performance.now() / 1000 });
// size: lo que mide en px su lado más largo
// angle: giro sobre el piso, en radianes
// t: segundos; si se mueve, se anima solo con eso# En una terminal, en la carpeta del juego
fogon assets add carpa-koi-gigante
# Con las herramientas de Fogón (conector)
add_assets({ slug: "<tu-juego>", ids: ["carpa-koi-gigante"] })
# Queda en client/assets/carpa-koi-gigante.ts
# https://fogon.page/object/carpa-koi-gigante