← BásicoObjeto 3Dfogon/anfora
Ánfora
Arrastrá para girarlo// «Ánfora» · gpt-6-sol (medium)
// Amphora («Ánfora»).
const cx = 24, cy = 24;
const clay = ["#a65c31", "#b86c37", "#c77b3e", "#d28a49"];
const shadow = "#493326";
const deep = "#302720";
const darkClay = "#714325";
const gold = "#e7a75b";
const radius = [
5, 7, 8, 8, 7, 5, 5, 6, 7, 9,
11, 12, 13, 14, 15, 15, 16, 16, 16, 16,
16, 16, 15, 15, 14, 13, 12, 11, 9, 8,
7, 7, 7, 7, 7, 7, 7, 8, 9, 9, 8
];
// Turn the amphora as stacked, solid rings. The opening is recessed into the neck.
for (let z = 0; z < radius.length; z++) {
const r = radius[z];
for (let x = cx - r; x <= cx + r; x++) {
for (let y = cy - r; y <= cy + r; y++) {
const d = Math.hypot(x - cx, y - cy);
if (d > r + 0.12) continue;
if (z >= 36 && d < (z >= 38 ? 4.5 : 4.1)) continue;
const edge = d > r - 1.45;
let color = clay[1];
if (z === 0 || (z <= 4 && d > r - 1.2)) color = darkClay;
else if (z === 35 || z === 36 || z === 30 || z === 31)
color = z === 35 ? shadow : darkClay;
else if (z === 37) color = "#694026";
else if (z === 38 || z === 39) color = "#b96e38";
else if (z === 40) color = gold;
else if (z === 26 || z === 27) color = darkClay;
else if (z === 22) color = "#784626";
else if (z === 8) color = "#714027";
else if (z === 3 || z === 4) color = "#d08a48";
if (edge && z !== 40) {
const light = (x < cx ? 1 : 0) + (y < cy ? 1 : 0);
if (color === clay[1])
color = clay[Math.min(3, Math.max(0, light + (rand() < 0.13 ? 1 : 0)))];
if (z >= 11 && z <= 21 && rand() < 0.11)
color = rand() < 0.55 ? "#91512b" : "#df9550";
if (z === 1 || z === 9 || z === 23 || z === 29)
color = rand() < 0.73 ? darkClay : "#a35c2e";
}
set(x, y, z, color);
}
}
}
// A dark bottom in the mouth, with irregular inner ceramic walls.
for (let x = 19; x <= 29; x++)
for (let y = 19; y <= 29; y++) {
const d = Math.hypot(x - cx, y - cy);
if (d < 4.5) set(x, y, 35, d < 3.2 ? deep : "#654027");
}
for (let z = 36; z <= 39; z++)
for (let a = 0; a < Math.PI * 2; a += 0.075) {
const r = z >= 38 ? 4.7 : 4.3;
set(cx + r * Math.cos(a), cy + r * Math.sin(a), z,
z === 39 && a > Math.PI ? "#8a4d29" : "#442c20");
}
function surface(a, z, offset, color) {
const r = radius[z] + offset;
set(cx + r * Math.cos(a), cy + r * Math.sin(a), z, color);
}
// Thick, chipped-looking lip and its bright upper edge.
for (let a = 0; a < Math.PI * 2; a += 0.045) {
surface(a, 37, 0.2, rand() < 0.18 ? "#30271f" : "#593824");
surface(a, 38, 0.3, rand() < 0.26 ? "#edaa59" : "#bd753a");
surface(a, 39, 0.3, rand() < 0.17 ? darkClay : "#d98b45");
surface(a, 40, 0.1, rand() < 0.14 ? "#f5ba69" : gold);
}
// Narrow, dark bands around the neck.
for (let a = 0; a < Math.PI * 2; a += 0.055) {
surface(a, 30, 0.3, "#493025");
surface(a, 31, 0.3, rand() < 0.23 ? "#c0783c" : "#694028");
surface(a, 34, 0.2, rand() < 0.18 ? "#d28843" : "#884b29");
surface(a, 35, 0.3, "#392c23");
}
// Raised decorative cord and jagged painted pattern on the shoulders.
for (let a = 0; a < Math.PI * 2; a += 0.045) {
surface(a, 22, 0.35, rand() < 0.17 ? "#d68c46" : "#553726");
surface(a, 23, 0.3, rand() < 0.18 ? gold : "#b46b35");
const point = Math.cos(a * 11);
const z = point > 0.26 ? 26 : point < -0.26 ? 24 : 25;
surface(a, z, 0.5, point > 0.26 ? shadow : "#513323");
surface(a, z + 1, 0.35, rand() < 0.18 ? gold : "#9c592d");
if (Math.cos(a * 11) > 0.75)
surface(a, 27, 0.55, "#3c2d23");
}
// Short dark flutes, separated by raised amber ribs, around the broad belly.
for (let a = 0; a < Math.PI * 2; a += Math.PI / 8) {
for (let z = 13; z <= 20; z++) {
const taper = z < 15 || z > 19;
surface(a, z, 0.5, taper ? "#784426" : "#503526");
surface(a + 0.065, z, 0.45, "#e0984e");
if (z >= 16 && z <= 19)
surface(a - 0.045, z, 0.45, "#704027");
}
surface(a, 21, 0.5, "#3e2e23");
}
// Stamped marks above the foot.
for (let a = 0; a < Math.PI * 2; a += Math.PI / 9) {
surface(a, 10, 0.45, "#523424");
surface(a + 0.055, 11, 0.35, "#754127");
surface(a, 3, 0.35, "#eda251");
}
// Angular, heavy loop handles. A dark outer contour surrounds warm clay cores.
function handle(points) {
for (let i = 0; i < points.length - 1; i++) {
const p = points[i], q = points[i + 1];
line(...p, ...q, 2.65, deep);
}
for (let i = 0; i < points.length - 1; i++) {
const p = points[i], q = points[i + 1];
line(...p, ...q, 1.8, "#a86132");
line(p[0], p[1] - 0.55, p[2] + 0.4,
q[0], q[1] - 0.55, q[2] + 0.4, 0.8, "#dd9249");
}
}
handle([
[18, 24, 34], [14, 24, 35], [9, 24, 34], [6, 24, 31],
[6, 23, 27], [9, 23, 24], [13, 23, 22], [18, 23, 25]
]);
handle([
[30, 25, 35], [35, 25, 38], [40, 25, 37], [43, 25, 34],
[43, 25, 30], [41, 24, 27], [37, 24, 24], [32, 24, 26]
]);
// Dark attachment collars make the handles feel joined to the vessel.
for (const [x, y, z] of [[18, 24, 34], [30, 25, 35],
[18, 23, 25], [32, 24, 26]]) {
box(x - 1, y - 1, z - 1, x + 1, y + 1, z, "#654027");
set(x, y - 2, z, "#d28a49");
}import { draw } from "./assets/voxel";
import { anfora } from "./assets/anfora";
// En cada cuadro: parado sobre (x, y), el centro de su base.
draw(ctx, anfora, 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 anfora
# Con las herramientas de Fogón (conector)
add_assets({ slug: "<tu-juego>", ids: ["anfora"] })
# Queda en client/assets/anfora.ts
# https://fogon.page/object/anfora