← Monstruos y enemigosObjeto 3Dfogon/bruja
Bruja
Arrastrá para girarlo// «Bruja» · gpt-6-sol (medium)
// A cartoon witch facing -y: green skin, a long pointy nose with a wart, a wide grin, wild orange hair under a tall black pointy hat with a purple band, a black dress with a purple belt, striped stockings and pointy boots, holding a gnarled wooden broom.
const C = {
dress: "#191623", dressLight: "#292438", dressShadow: "#100e19",
purple: "#833ab4", purpleLight: "#ac59dd", purpleDark: "#512474",
green: "#75b943", greenLight: "#98db59", greenDark: "#427c35",
orange: "#ed7629", orangeLight: "#ffad3e", orangeDark: "#ad421e",
black: "#17151e", hatLight: "#302a38",
gold: "#ffc957", eye: "#f8ff62", glow: "#fffab0",
wood: "#76431f", woodLight: "#ad6b32", straw: "#d5a34d"
};
// Stockings and legs, each hinged at the hip.
for (const [name, x, speed] of [
["leftLeg", 19, 4],
["rightLeg", 29, -4]
]) {
part(name, [x, 24, 12]);
for (let z = 4; z <= 12; z++) {
box(x - 2, 21, z, x + 2, 26, z,
Math.floor((z - 4) / 2) % 2 ? C.purpleLight : C.greenLight);
}
// Chunky boots with long, upturned toes facing the viewer.
box(x - 3, 18, 1, x + 3, 27, 4, C.black);
box(x - 3, 14, 0, x + 3, 23, 2, C.black);
box(x - 2, 12, 1, x + 2, 17, 3, C.hatLight);
box(x - 1, 11, 2, x + 1, 13, 4, C.black);
box(x - 3, 14, 0, x + 3, 24, 0, "#0b0b11");
box(x - 2, 18, 4, x + 2, 25, 5, C.purpleDark);
animate(name, { type: "swing", axis: "x", speed, amplitude: 0.3 });
}
part("body");
// Flared, solid dress.
for (let z = 9; z <= 27; z++) {
const t = (z - 9) / 18;
const rx = 13 - 7 * t;
const ry = 9 - 4 * t;
for (let x = Math.ceil(24 - rx); x <= Math.floor(24 + rx); x++) {
for (let y = Math.ceil(24 - ry); y <= Math.floor(24 + ry); y++) {
const d = ((x - 24) / rx) ** 2 + ((y - 24) / ry) ** 2;
if (d <= 1) {
const shade = z <= 11 || y >= 28 ? C.dressShadow :
y <= 20 && rand() < 0.24 ? C.dressLight : C.dress;
set(x, y, z, shade);
}
}
}
}
ellipsoid(24, 24, 21, 8, 6, 2, C.purpleDark);
box(18, 18, 21, 30, 19, 22, C.purple);
box(21, 17, 20, 27, 18, 23, C.purpleLight);
box(23, 16, 20, 25, 17, 23, C.gold);
set(24, 16, 21, "#fff2a1");
// Hem and little magical motifs on the front.
for (let x = 13; x <= 35; x++) {
const edge = Math.round(24 - 9 * Math.sqrt(Math.max(0, 1 - ((x - 24) / 13) ** 2)));
set(x, edge - 1, 10, C.purpleDark);
}
for (const [x, y, z] of [[17, 16, 14], [30, 16, 16], [24, 15, 12]]) {
set(x, y, z, C.purpleLight);
set(x - 1, y, z - 1, C.purple);
set(x + 1, y, z - 1, C.purple);
}
// Free arm and sleeve.
line(17, 23, 26, 12, 20, 21, 3, C.dress);
line(12, 20, 21, 10, 18, 19, 2, C.dressLight);
sphere(10, 18, 18, 2, C.green);
set(9, 16, 18, C.greenLight);
// Wild hair behind and around the face.
ellipsoid(24, 26, 32, 9, 8, 7, C.orangeDark);
for (const [x, y, z, dx, dy, dz] of [
[16, 23, 33, -5, -2, -7], [15, 21, 31, -4, 0, -7],
[33, 23, 34, 5, -2, -8], [34, 26, 31, 5, 2, -7],
[20, 29, 31, -3, 3, -8], [29, 29, 31, 3, 3, -8]
]) {
line(x, y, z, x + dx, y + dy, z + dz, 2, C.orange);
set(x + dx, y + dy, z + dz, C.orangeLight);
}
// Big green face and bright cheeks.
ellipsoid(24, 23, 32, 8, 7, 6, C.greenDark);
ellipsoid(24, 22, 32, 7, 7, 5, C.green);
ellipsoid(19, 17, 29, 2, 1, 2, C.greenLight);
ellipsoid(29, 17, 29, 2, 1, 2, C.greenLight);
// Glowing eyes with dark rims and expressive brows.
for (const x of [20, 28]) {
box(x - 2, 15, 32, x + 1, 16, 34, C.black);
box(x - 1, 14, 32, x, 14, 33, C.eye);
set(x, 13, 33, C.glow);
}
line(18, 15, 35, 21, 15, 34, 0, C.orangeDark);
line(27, 15, 34, 30, 15, 35, 0, C.orangeDark);
// Long crooked nose, complete with a prominent wart.
line(24, 16, 32, 24, 11, 30, 1, C.green);
box(23, 10, 29, 25, 12, 30, C.greenLight);
sphere(26, 11, 30, 1, C.greenDark);
set(26, 10, 30, "#bad45a");
// Wide, toothy, friendly grin.
line(19, 16, 29, 22, 15, 27, 0, C.greenDark);
box(21, 15, 27, 27, 15, 28, "#332234");
line(27, 15, 27, 30, 16, 29, 0, C.greenDark);
for (const x of [22, 24, 26]) set(x, 14, 28, "#fff1c8");
set(24, 14, 27, "#f78ca9");
// Curled orange fringe peeking out beneath the hat.
for (const [x, y, z] of [[17, 17, 35], [19, 16, 36], [30, 17, 36], [32, 18, 35]]) {
line(x, y, z, x + (x < 24 ? 1 : -1), y - 1, z - 2, 1, C.orangeLight);
}
// Oversized black brim, purple band, and crooked pointed crown.
ellipsoid(24, 23, 37, 16, 10, 1, C.black);
ellipsoid(24, 23, 38, 15, 9, 1, C.hatLight);
for (let z = 39; z <= 45; z++) {
const t = (z - 39) / 6;
const cx = Math.round(24 - 4 * t * t);
const cy = Math.round(23 - 2 * t);
const rx = Math.max(1, Math.round(9 * (1 - t)));
const ry = Math.max(1, Math.round(7 * (1 - t)));
for (let x = cx - rx; x <= cx + rx; x++) {
for (let y = cy - ry; y <= cy + ry; y++) {
if (((x - cx) / rx) ** 2 + ((y - cy) / ry) ** 2 <= 1)
set(x, y, z, y < cy - 2 ? C.hatLight : C.black);
}
}
}
for (let x = 16; x <= 32; x++) {
for (let y = 17; y <= 29; y++) {
const d = ((x - 24) / 9) ** 2 + ((y - 23) / 7) ** 2;
if (d > 0.72 && d <= 1) {
set(x, y, 39, C.purple);
set(x, y, 40, C.purpleDark);
}
}
}
box(22, 15, 39, 26, 16, 40, C.gold);
set(24, 14, 39, "#fff1a1");
// Shoulder, swinging broom arm, hand, and broom share one pivot.
part("broomArm", [32, 24, 26]);
sphere(32, 23, 26, 3, C.dress);
line(33, 22, 25, 36, 17, 21, 3, C.dress);
line(35, 18, 22, 37, 15, 19, 2, C.dressLight);
// Crooked wooden shaft and broad fan of straw.
line(35, 15, 25, 39, 14, 9, 1, C.wood);
line(36, 14, 23, 39, 13, 12, 0, C.woodLight);
for (const [z, x] of [[21, 36], [17, 37], [13, 38]]) {
set(x, 13, z, C.woodLight);
}
ellipsoid(40, 14, 7, 3, 2, 3, C.straw);
for (let i = 0; i < 19; i++) {
const dx = Math.floor(rand() * 11) - 5;
const dy = Math.floor(rand() * 6) - 3;
const tipZ = Math.floor(rand() * 3);
line(40, 14, 8, 40 + dx, 14 + dy, tipZ, 0,
i % 3 === 0 ? "#f8cd70" : i % 3 === 1 ? C.straw : "#a97236");
}
box(38, 12, 9, 41, 16, 10, C.wood);
box(38, 12, 9, 41, 12, 9, C.gold);
// Green fingers wrap around the handle.
sphere(37, 15, 19, 2, C.green);
box(37, 12, 18, 38, 15, 19, C.greenLight);
animate("broomArm", { type: "swing", axis: "x", speed: 3, amplitude: 0.3 });
part("body");import { draw } from "./assets/voxel";
import { bruja } from "./assets/bruja";
// En cada cuadro: parado sobre (x, y), el centro de su base.
draw(ctx, bruja, 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 bruja
# Con las herramientas de Fogón (conector)
add_assets({ slug: "<tu-juego>", ids: ["bruja"] })
# Queda en client/assets/bruja.ts
# https://fogon.page/object/bruja