← Fantasy3D assetfogon/enano-con-hacha
Enano con hacha
Drag to turn it// «Enano con hacha» · gpt-6-sol (medium)
// A short stocky cartoon dwarf facing front (-y): a huge braided orange-red beard down to the belt, a round steel helmet with two small horns, big nose, chainmail shirt under a brown leather vest, wide iron belt buckle, chunky boots, and a big double-bladed axe held in the right hand.
// Short, stocky storybook dwarf. Front faces -y.
const steel = "#899ba0";
const steelLight = "#bdcbd0";
const steelDark = "#52646c";
const leather = "#70452f";
const leatherLight = "#96613c";
const brass = "#e5b85d";
const beard = "#b94625";
const beardLight = "#e77937";
const beardDark = "#85301f";
// Walking legs: separate parts pivoting at the hips.
function makeLeg(name, x, speed) {
part(name, [x, 23, 12]);
animate(name, { type: "swing", axis: "x", speed, amplitude: 0.3 });
ellipsoid(x, 23, 8, 5, 5, 6, "#465157");
ellipsoid(x, 20, 4, 5, 6, 4, "#543f32");
box(x - 5, 15, 0, x + 5, 25, 3, "#342e30");
ellipsoid(x, 18, 3, 5, 5, 3, "#77533a");
box(x - 5, 15, 4, x + 5, 24, 5, "#a17447");
box(x - 4, 15, 5, x + 4, 16, 5, brass);
}
makeLeg("left leg", 17, 5);
makeLeg("right leg", 29, -5);
part("body");
// Broad chainmail torso and rounded shoulders.
ellipsoid(23, 23, 19, 12, 10, 10, steelDark);
ellipsoid(23, 22, 20, 11, 9, 9, "#75878a");
for (let x = 15; x <= 31; x += 3) {
for (let z = 16; z <= 26; z += 3) {
const y = 14 + Math.round(Math.abs(x - 23) * 0.18);
if (get(x, y, z)) set(x, y - 1, z, (x + z) % 2 ? steelLight : steel);
}
}
// Leather vest panels, edging and brass studs.
ellipsoid(15, 19, 21, 5, 7, 8, leather);
ellipsoid(31, 19, 21, 5, 7, 8, leather);
box(12, 15, 16, 15, 17, 26, leatherLight);
box(31, 15, 16, 34, 17, 26, leatherLight);
for (const x of [14, 32]) {
for (const z of [18, 23]) set(x, 14, z, brass);
}
// Belt and oversized iron buckle.
box(12, 14, 12, 34, 31, 15, "#49362c");
box(13, 13, 14, 33, 15, 15, leatherLight);
box(19, 10, 11, 27, 13, 17, steelDark);
box(20, 9, 12, 26, 10, 16, steelLight);
box(22, 8, 13, 24, 9, 15, "#b68d4b");
set(23, 7, 14, brass);
// Left sleeve and relaxed mitten.
ellipsoid(10, 22, 23, 5, 6, 6, leather);
ellipsoid(9, 19, 18, 4, 5, 6, steelDark);
ellipsoid(9, 16, 14, 4, 4, 4, "#d99468");
box(6, 17, 18, 12, 20, 19, leatherLight);
// Big friendly face.
ellipsoid(23, 21, 30, 11, 9, 9, "#d99468");
ellipsoid(15, 16, 29, 3, 3, 3, "#e5a276");
ellipsoid(31, 16, 29, 3, 3, 3, "#e5a276");
for (const x of [18, 28]) {
ellipsoid(x, 12, 31, 3, 2, 3, "#f4e3b4");
box(x - 1, 9, 30, x, 9, 32, "#263a42");
set(x - 1, 8, 32, "#ffffff");
}
box(15, 10, 34, 20, 12, 35, beardDark);
box(26, 10, 34, 31, 12, 35, beardDark);
// Thick moustache frames the oversized nose.
ellipsoid(19, 10, 26, 6, 3, 3, beard);
ellipsoid(27, 10, 26, 6, 3, 3, beard);
ellipsoid(18, 8, 27, 4, 2, 2, beardLight);
ellipsoid(28, 8, 27, 4, 2, 2, beardLight);
ellipsoid(23, 8, 29, 4, 4, 4, "#eaa77a");
ellipsoid(23, 6, 28, 3, 2, 2, "#f2b58b");
set(18, 7, 27, beardDark);
set(28, 7, 27, beardDark);
// Huge layered beard, tapering to the belt. Three chunky braids sit on top.
ellipsoid(23, 14, 22, 10, 5, 9, beardDark);
ellipsoid(23, 12, 21, 9, 5, 8, beard);
for (const [x, bottom] of [[17, 16], [23, 13], [29, 16]]) {
for (let z = 26; z >= bottom; z -= 3) {
const taper = (26 - z) / (26 - bottom);
const centerX = x + (x < 23 ? taper : x > 23 ? -taper : 0);
ellipsoid(centerX - 1, 8, z, 3, 3, 2, beardLight);
ellipsoid(centerX + 1, 7, z - 1, 3, 3, 2, beard);
set(centerX - 2, 5, z, "#f18b42");
}
box(x - 2, 6, bottom + 1, x + 2, 8, bottom + 2, brass);
ellipsoid(x, 8, bottom, 2, 2, 2, beardDark);
}
for (let i = 0; i < 35; i++) {
const x = 15 + Math.floor(rand() * 17);
const z = 17 + Math.floor(rand() * 11);
if (get(x, 7, z)) set(x, 6, z, rand() < 0.5 ? "#ef8040" : "#9e371f");
}
// Steel helmet dome, raised rim, crest and warm brass rivets.
for (let x = 12; x <= 34; x++) {
for (let y = 12; y <= 31; y++) {
for (let z = 33; z <= 40; z++) {
const d = ((x - 23) / 11) ** 2 +
((y - 22) / 10) ** 2 +
((z - 32) / 8) ** 2;
if (d <= 1) set(x, y, z, z >= 37 ? steelLight : steel);
}
}
}
ellipsoid(23, 20, 33, 12, 10, 2, steelDark);
box(14, 11, 33, 32, 13, 34, steelLight);
box(20, 10, 34, 26, 12, 35, "#a9b9bb");
for (const x of [15, 19, 27, 31]) set(x, 10, 34, brass);
line(23, 15, 36, 23, 20, 40, 1, steelDark);
// Two short, outward-curving ivory horns.
for (const side of [-1, 1]) {
const root = 23 + side * 10;
line(root, 22, 36, root + side * 3, 22, 39, 2, "#d9c7a3");
line(root + side * 3, 22, 39, root + side * 5, 21, 42, 1, "#f5e5c4");
set(root + side * 5, 21, 43, "#fff2d4");
ellipsoid(root, 22, 36, 3, 3, 2, brass);
}
// Axe arm pivots at the right shoulder; the entire weapon follows the chop.
part("axe arm", [33, 22, 25]);
animate("axe arm", { type: "swing", axis: "x", speed: 3, amplitude: 0.5 });
ellipsoid(34, 21, 24, 5, 6, 5, leather);
ellipsoid(36, 18, 21, 4, 5, 5, steelDark);
box(33, 14, 19, 39, 18, 21, leatherLight);
ellipsoid(38, 14, 18, 3, 3, 3, "#d99468");
// Long dark-wood haft, held through the fist.
box(38, 12, 8, 39, 14, 38, "#523a30");
box(38, 11, 10, 38, 11, 37, "#87603b");
box(37, 11, 16, 40, 15, 18, leather);
box(37, 12, 10, 40, 14, 11, brass);
box(37, 12, 27, 40, 14, 28, brass);
// Double-bladed axe head: thick center and broad pale cutting edges.
box(36, 11, 32, 41, 15, 37, steelDark);
ellipsoid(34, 13, 35, 5, 3, 6, steel);
ellipsoid(43, 13, 35, 4, 3, 6, steel);
for (let z = 31; z <= 39; z++) {
const reach = Math.round(3 * Math.sqrt(Math.max(0, 1 - ((z - 35) / 5) ** 2)));
for (let y = 12; y <= 14; y++) {
for (let x = 30; x <= 30 + reach; x++) set(x, y, z, steelLight);
for (let x = 46 - reach; x <= 46; x++) set(x, y, z, steelLight);
}
}
box(37, 10, 33, 40, 11, 37, "#65777c");
set(38, 9, 35, "#58eee0");
set(39, 9, 36, "#80fff0");
set(39, 9, 34, "#58eee0");
box(37, 12, 38, 40, 14, 39, brass);
part("body");import { draw } from "./assets/voxel";
import { enanoConHacha } from "./assets/enano-con-hacha";
// Every frame: standing on (x, y), the centre of its base.
draw(ctx, enanoConHacha, 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 enano-con-hacha
# With Fogón's tools (connector)
add_assets({ slug: "<your-game>", ids: ["enano-con-hacha"] })
# It lands in client/assets/enano-con-hacha.ts
# https://fogon.page/object/enano-con-hacha