← Basics3D assetfogon/carretilla
Carretilla
Drag to turn it// «Carretilla» · gpt-6-sol (medium)
// Wheelbarrow («Carretilla»).
const clay = ["#803c2a", "#97462d", "#ad5131", "#bc6038"];
const shadowClay = "#693326";
const rimLight = "#d79a5a";
const rimGold = "#bd7840";
const steel = "#344449";
const steelLight = "#506366";
const steelDark = "#27363a";
// The wheelbarrow's single wheel, set ahead of the tray.
const wy = 9, wz = 8;
for (let x = 18; x <= 28; x++) {
for (let y = 0; y <= 18; y++) {
for (let z = 0; z <= 17; z++) {
const dy = y - wy, dz = z - wz;
const r = Math.sqrt(dy * dy + dz * dz);
if (r > 7.8) continue;
const angle = Math.atan2(dz, dy);
const tread = Math.floor((angle + Math.PI) * 13 / Math.PI) % 2;
let c = r > 6.3
? (tread ? "#253033" : "#303b3e")
: r > 4.7 ? "#202a2c" : "#303d40";
if (r > 7.1 && (x === 18 || x === 28)) c = "#1e282a";
if (z > 10 && r > 5 && rand() < 0.15) c = "#3b4648";
set(x, y, z, c);
}
}
}
// Square-cut tread lugs around the circumference.
for (let i = 0; i < 20; i++) {
const a = 2 * Math.PI * i / 20;
const yy = Math.round(wy + 7.3 * Math.cos(a));
const zz = Math.round(wz + 7.3 * Math.sin(a));
for (let x = 19; x <= 27; x += 2) {
set(x, yy, zz, i % 3 === 0 ? "#405052" : "#303d3f");
}
}
// Axle and the visible metal wheel hub.
cylinder(28, wy, wz, 4, 2, "#405154", "x");
cylinder(30, wy, wz, 2.7, 1, "#687879", "x");
box(31, wy - 1, wz - 1, 31, wy + 1, wz + 1, "#8b9995");
set(32, wy, wz, "#405052");
box(16, wy - 1, wz - 1, 18, wy + 1, wz + 1, steelDark);
// Two diverging frame rails run from the axle to the handles.
for (let y = 8; y <= 46; y++) {
const t = (y - 8) / 38;
const z = Math.round(12 + 6 * t);
for (const side of [-1, 1]) {
const x = Math.round(24 + side * (5 + 6 * t));
const wood = y >= 19 && y <= 41;
box(x - 1, y, z - 1, x + 1, y, z + 1,
wood ? "#93602e" : steelDark);
set(x, y, z + 1, wood ? (y % 5 === 0 ? "#e1a65a" : "#bf8540") : steelLight);
if (wood && y % 6 === 0) set(x + 1, y, z, "#e7ad62");
if (y >= 41) {
box(x - 1, y, z - 1, x + 1, y, z + 1, "#29383a");
set(x, y, z + 1, "#47595a");
}
}
}
// Dark crosspieces underneath the tub.
for (const y of [17, 25, 32]) {
box(14, y, 14, 34, y + 1, 15, steelDark);
box(17, y, 16, 31, y, 16, steelLight);
}
// Rear parking legs and their broad feet.
for (const side of [-1, 1]) {
const x = 24 + side * 10;
line(x, 33, 14, x + side, 36, 3, 1, steel);
line(x + side, 36, 3, x + side, 36, 1, 1, steelDark);
box(x + side - 2, 35, 0, x + side + 2, 39, 1, steelDark);
box(x + side - 1, 35, 2, x + side + 1, 37, 3, steelLight);
line(x, 31, 13, x + side, 35, 7, 0, "#607070");
}
// A tapered terracotta tub. Its bottom remains visible through the open top.
for (let z = 17; z <= 27; z++) {
const t = (z - 17) / 10;
const x0 = Math.round(14 - 5 * t), x1 = Math.round(34 + 4 * t);
const y0 = Math.round(18 - 6 * t), y1 = Math.round(30 + 5 * t);
for (let x = x0; x <= x1; x++) {
for (let y = y0; y <= y1; y++) {
const edge = x <= x0 + 1 || x >= x1 - 1 ||
y <= y0 + 1 || y >= y1 - 1;
if (!edge) continue;
const outerFront = y <= y0 + 1;
const upper = z >= 25;
let c = clay[(z + Math.floor(x / 5)) % clay.length];
if (z % 3 === 0) c = upper ? "#ca6b3b" : "#a95030";
if (z % 3 === 1 && !upper) c = "#783b29";
if (outerFront) c = z % 3 === 0 ? "#bb6138" : "#a64c2e";
if (rand() < 0.10) c = upper ? "#d07340" : "#86402b";
set(x, y, z, c);
}
}
}
// Solid bottom and the dark, recessed inside floor.
box(14, 18, 16, 34, 30, 17, "#82402b");
box(16, 19, 18, 32, 29, 18, "#733424");
for (let y = 20; y <= 28; y += 3)
box(17, y, 18, 31, y, 18, "#8f422a");
// Interior horizontal courses: dark mortar beneath warm clay lips.
for (let z = 19; z <= 26; z += 3) {
const t = (z - 17) / 10;
const x0 = Math.round(14 - 5 * t), x1 = Math.round(34 + 4 * t);
const y0 = Math.round(18 - 6 * t), y1 = Math.round(30 + 5 * t);
box(x0 + 2, y0 + 2, z, x1 - 2, y0 + 2, z, "#713322");
box(x0 + 2, y1 - 2, z, x1 - 2, y1 - 2, z, "#d17242");
box(x0 + 2, y0 + 2, z + 1, x0 + 2, y1 - 2, z + 1, "#ba5832");
box(x1 - 2, y0 + 2, z, x1 - 2, y1 - 2, z, "#8c3e29");
}
// Thick, pale wooden lip defining the open rectangular silhouette.
box(8, 11, 28, 39, 13, 29, rimGold);
box(8, 34, 28, 39, 36, 29, rimGold);
box(8, 13, 28, 10, 34, 29, rimGold);
box(37, 13, 28, 39, 34, 29, rimGold);
box(9, 11, 29, 38, 11, 29, rimLight);
box(9, 35, 29, 38, 35, 29, "#e0a364");
box(8, 12, 29, 8, 35, 29, "#d69a59");
box(39, 12, 29, 39, 35, 29, "#ebac68");
for (let x = 12; x <= 37; x += 5) {
set(x, 12, 27, "#e4a360");
set(x, 35, 27, "#ce8144");
}
// Short vertical corner reinforcements and plank seams.
for (const x of [10, 37]) {
for (const y of [13, 34]) {
for (let z = 21; z <= 27; z++) {
if (z % 2 === 0) set(x, y, z, "#dc8450");
}
}
}
for (let x = 13; x <= 36; x += 6) {
for (let z = 20; z <= 26; z += 3)
set(x, 12 + Math.round((27 - z) / 5), z, "#773923");
}
// Small iron maker's plate on the near wall.
box(27, 11, 21, 32, 11, 24, "#755035");
box(28, 10, 21, 31, 10, 23, "#526165");
box(29, 9, 22, 30, 9, 23, "#35474a");
set(27, 10, 22, "#d2a162");
set(32, 10, 22, "#d2a162");import { draw } from "./assets/voxel";
import { carretilla } from "./assets/carretilla";
// Every frame: standing on (x, y), the centre of its base.
draw(ctx, carretilla, 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 carretilla
# With Fogón's tools (connector)
add_assets({ slug: "<your-game>", ids: ["carretilla"] })
# It lands in client/assets/carretilla.ts
# https://fogon.page/object/carretilla