← Medieval3D assetfogon/carro-del-verdulero
Carro del verdulero
Drag to turn it// «Carro del verdulero» · gpt-6-sol (medium)
// A small two-wheeled wooden vendor cart with long handles pointing forward (-y): a dark brown plank box piled high with vegetables (orange carrots, green cabbages, red tomatoes, purple turnips) and two big spoked wooden wheels with black iron rims on the sides.
const wood = "#593824", woodLight = "#795034", woodDark = "#39261e";
const iron = "#242629", ironLight = "#46494a";
const gold = "#d5ac55";
// Chassis and two long handles pointing toward the viewer.
box(12, 16, 10, 35, 39, 13, woodDark);
box(14, 3, 11, 16, 20, 13, wood);
box(31, 3, 11, 33, 20, 13, wood);
for (const x of [14, 31]) {
box(x, 3, 13, x + 2, 15, 13, woodLight);
box(x, 3, 10, x + 2, 5, 11, iron);
box(x, 3, 14, x + 2, 4, 14, ironLight);
}
box(15, 17, 10, 32, 18, 12, wood);
box(9, 26, 9, 38, 28, 11, iron);
// Solid plank box, with individually readable horizontal boards.
box(13, 16, 13, 34, 38, 19, woodDark);
for (let z = 14; z <= 20; z += 3) {
for (const x of [11, 34]) {
box(x, 15, z, x + 2, 39, z + 2, wood);
box(x, 15, z + 2, x + 2, 39, z + 2, woodLight);
}
for (const y of [14, 39]) {
box(12, y, z, 35, y + 2, z + 2, wood);
box(12, y, z + 2, 35, y + 2, z + 2, woodLight);
}
}
for (const z of [13, 16, 19]) {
box(11, 14, z, 36, 14, z, woodDark);
box(11, 41, z, 36, 41, z, woodDark);
box(10, 15, z, 10, 40, z, woodDark);
box(37, 15, z, 37, 40, z, woodDark);
}
for (const x of [11, 35]) {
for (const y of [15, 39]) {
box(x, y, 12, x + 1, y + 1, 23, woodDark);
box(x, y, 22, x + 1, y + 1, 23, woodLight);
}
}
box(11, 15, 22, 36, 16, 23, woodLight);
box(11, 39, 22, 36, 40, 23, woodLight);
box(11, 15, 22, 12, 40, 23, woodLight);
box(35, 15, 22, 36, 40, 23, woodLight);
// Iron corner straps and little rivets.
for (const x of [12, 35]) {
for (const y of [14, 41]) {
box(x, y, 14, x, y, 20, iron);
for (const z of [15, 19]) set(x, y - (y === 41 ? 0 : 1), z, ironLight);
}
}
// A small heraldic badge on the front board.
box(20, 13, 16, 27, 13, 21, gold);
box(21, 12, 17, 26, 12, 20, "#8d2931");
box(23, 11, 18, 24, 11, 20, "#314c82");
set(22, 11, 19, gold);
set(25, 11, 19, gold);
// Dark leaves and produce beneath the visible harvest.
ellipsoid(24, 27, 21, 11, 11, 3, "#385533");
for (let i = 0; i < 80; i++) {
const x = 15 + Math.floor(rand() * 18);
const y = 18 + Math.floor(rand() * 19);
const z = 23 + Math.floor(rand() * 3);
set(x, y, z, rand() < 0.5 ? "#537343" : "#709052");
}
// Chunky cabbages, with curved-looking leaf patches.
function cabbage(x, y, z, r) {
sphere(x, y, z, r, "#658b50");
ellipsoid(x, y, z + 1, r - 1, r - 1, r - 1, "#83a76a");
for (let a = 0; a < 7; a++) {
const t = a * Math.PI * 2 / 7;
const px = Math.round(x + Math.cos(t) * (r - 1));
const py = Math.round(y + Math.sin(t) * (r - 1));
line(px, py, z - 1, px, py, z + 1, 0, "#a2b980");
}
set(x, y, z + r, "#b4c68f");
}
cabbage(18, 22, 25, 4);
cabbage(29, 21, 25, 4);
cabbage(24, 30, 26, 4);
cabbage(33, 32, 24, 3);
// Tomatoes and their unmistakable green star-shaped tops.
function tomato(x, y, z, r) {
sphere(x, y, z, r, "#af3f36");
ellipsoid(x - 1, y - 1, z + 1, r - 1, r - 1, r - 1, "#d45a45");
set(x, y, z + r, "#406b36");
for (const [dx, dy] of [[1,0],[-1,0],[0,1],[0,-1]]) {
set(x + dx, y + dy, z + r, "#527d3e");
}
}
tomato(16, 31, 25, 3);
tomato(32, 26, 27, 3);
tomato(20, 36, 25, 3);
tomato(26, 19, 27, 2);
tomato(29, 36, 26, 2);
// Purple turnips with pale roots and leafy crowns.
function turnip(x, y, z) {
ellipsoid(x, y, z - 1, 2, 2, 3, "#e0c69d");
sphere(x, y, z + 1, 3, "#75496f");
ellipsoid(x - 1, y - 1, z + 2, 2, 2, 1, "#a16a97");
line(x, y, z + 3, x, y, z + 5, 0, "#3f713c");
line(x, y, z + 4, x - 2, y, z + 5, 0, "#5c8c48");
line(x, y, z + 4, x + 2, y - 1, z + 5, 0, "#618f49");
}
turnip(22, 24, 27);
turnip(34, 35, 25);
turnip(17, 26, 27);
// Carrots laid diagonally across the overflowing pile.
function carrot(x, y, z, dx, dy) {
sphere(x, y, z, 2, "#d9772e");
line(x, y, z, x + dx * 4, y + dy * 4, z - 2, 1, "#ec9140");
line(x + dx * 4, y + dy * 4, z - 2,
x + dx * 6, y + dy * 6, z - 3, 0, "#bd6027");
for (const [lx, ly] of [[-2, 0], [1, -2], [1, 2]]) {
line(x, y, z + 2, x + lx, y + ly, z + 5, 0, "#548344");
}
}
carrot(27, 25, 29, -1, 0);
carrot(20, 32, 29, 1, 0);
carrot(31, 31, 28, 0, -1);
carrot(16, 19, 26, 0, 1);
// Each wheel is a separate rotating part, built in its own YZ plane.
function wheel(name, cx) {
const cy = 28, cz = 10;
part(name, [cx, cy, cz]);
for (let y = cy - 10; y <= cy + 10; y++) {
for (let z = cz - 10; z <= cz + 10; z++) {
const d = Math.hypot(y - cy, z - cz);
if (d >= 8.9 && d <= 10.25) {
for (let x = cx - 2; x <= cx + 2; x++)
set(x, y, z, x === cx - 2 || x === cx + 2 ? iron : ironLight);
} else if (d >= 7.0 && d <= 8.8) {
for (let x = cx - 1; x <= cx + 1; x++)
set(x, y, z, d > 8.1 ? woodDark : woodLight);
}
}
}
for (let i = 0; i < 8; i++) {
const a = i * Math.PI / 4;
const dy = Math.cos(a), dz = Math.sin(a);
line(cx, cy + Math.round(dy * 2), cz + Math.round(dz * 2),
cx, cy + Math.round(dy * 8), cz + Math.round(dz * 8),
1, i % 2 ? wood : woodLight);
for (const side of [-2, 2])
set(cx + side, cy + Math.round(dy * 9), cz + Math.round(dz * 9), "#788080");
}
cylinder(cx - 2, cy, cz, 3, 5, woodDark, "x");
cylinder(cx - 3, cy, cz, 2, 7, iron, "x");
set(cx - 4, cy, cz, ironLight);
set(cx + 4, cy, cz, ironLight);
animate(name, { type: "spin", axis: "x", speed: 3 });
}
wheel("leftWheel", 7);
wheel("rightWheel", 40);
part("body", [24, 24, 0]);import { draw } from "./assets/voxel";
import { carroDelVerdulero } from "./assets/carro-del-verdulero";
// Every frame: standing on (x, y), the centre of its base.
draw(ctx, carroDelVerdulero, 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 carro-del-verdulero
# With Fogón's tools (connector)
add_assets({ slug: "<your-game>", ids: ["carro-del-verdulero"] })
# It lands in client/assets/carro-del-verdulero.ts
# https://fogon.page/object/carro-del-verdulero