← Weapons3D assetfogon/arma-hwacha
Hwacha
Drag to turn it// «Hwacha» · propio
// A handheld Korean hwacha: a small wooden rack holding a bundle of fire-arrows with red feathers and gunpowder tubes.
const colors = () => {};
const WOOD_DARK = "#4a2e1a";
const WOOD_MID = "#7a4c28";
const WOOD_LIGHT = "#a97545";
const WOOD_HI = "#c99b63";
const METAL_DARK = "#33353e";
const METAL_MID = "#5c5f6b";
const METAL_HI = "#8e93a3";
const SHAFTS = ["#d9b878", "#c8a566", "#e6cb90"];
const SHAFT_D = "#b8945a";
const TIP_DARK = "#2f3038";
const TIP_MID = "#55586a";
const TIP_HI = "#8b91a3";
const FEATHS = ["#c0392b", "#b8352a", "#cc4433"];
const FEATH_L = "#e05a3c";
const FEATH_D = "#7e221c";
const POWDER = "#2b2b33";
const BRASS = "#c9a13a";
const BAND = "#a82a20";
const CORD = "#b8a066";
part("body");
// =========================================================
// WOODEN SLED / RACK
// =========================================================
box(12, 15, 0, 38, 33, 2, WOOD_DARK); // lower body
box(12, 15, 3, 38, 33, 3, WOOD_MID); // top plate
box(12, 20, 3, 38, 28, 3, "#33200f"); // groove shadow floor
// side walls
box(12, 15, 4, 38, 19, 10, WOOD_MID);
box(12, 29, 4, 38, 33, 10, WOOD_MID);
box(12, 15, 11, 38, 19, 11, WOOD_LIGHT); // caps
box(12, 29, 11, 38, 33, 11, WOOD_LIGHT);
box(12, 19, 4, 38, 19, 10, WOOD_LIGHT); // inner faces catch light
box(12, 29, 4, 38, 29, 10, WOOD_LIGHT);
// wood grain stripes on the outer faces
box(12, 15, 7, 38, 15, 7, WOOD_DARK);
box(12, 33, 5, 38, 33, 5, WOOD_DARK);
box(12, 15, 9, 38, 15, 9, WOOD_HI);
box(12, 33, 9, 38, 33, 9, WOOD_HI);
box(38, 15, 1, 38, 33, 1, WOOD_DARK); // front edge
box(12, 15, 1, 12, 33, 1, WOOD_DARK); // rear edge
// metal reinforcement bands on the outside
box(13, 15, 4, 14, 15, 10, METAL_MID);
box(13, 33, 4, 14, 33, 10, METAL_MID);
box(36, 15, 4, 37, 15, 10, METAL_MID);
box(36, 33, 4, 37, 33, 10, METAL_MID);
// bolts along the wall caps
for (let x = 15; x <= 36; x += 7) {
set(x, 17, 11, METAL_HI);
set(x, 31, 11, METAL_HI);
}
// brass corner caps
box(12, 15, 11, 13, 16, 11, BRASS);
box(12, 32, 11, 13, 33, 11, BRASS);
box(37, 15, 11, 38, 16, 11, BRASS);
box(37, 32, 11, 38, 33, 11, BRASS);
// =========================================================
// BUNDLE OF FIRE-ARROWS (3 x 3)
// =========================================================
const AY = [21, 24, 27];
const AZ = [5, 8, 11];
for (let zi = 0; zi < 3; zi++) {
for (let yi = 0; yi < 3; yi++) {
const ay = AY[yi];
const az = AZ[zi];
const sc = SHAFTS[(yi + zi) % 3];
const fc = FEATHS[(yi + zi) % 3];
// ---- tail feathers : solid block, then a tapering cross ----
box(4, ay - 1, az - 1, 7, ay + 1, az + 1, fc);
box(4, ay - 1, az + 1, 7, ay + 1, az + 1, FEATH_L);
box(4, ay - 1, az - 1, 4, ay + 1, az + 1, FEATH_D);
for (let x = 8; x <= 11; x++) {
set(x, ay, az, FEATH_L);
set(x, ay - 1, az, fc);
set(x, ay + 1, az, fc);
set(x, ay, az - 1, fc);
set(x, ay, az + 1, FEATH_L);
}
// ---- gunpowder tube ----
box(12, ay - 1, az - 1, 19, ay + 1, az + 1, POWDER);
box(12, ay - 1, az - 1, 12, ay + 1, az + 1, BRASS);
box(19, ay - 1, az - 1, 19, ay + 1, az + 1, BRASS);
box(15, ay - 1, az - 1, 16, ay + 1, az + 1, BAND);
// ---- shaft (cross section -> gaps between the arrows) ----
for (let x = 20; x <= 40; x++) {
const c = (x % 8 === 0) ? SHAFT_D : sc;
set(x, ay, az, c);
set(x, ay - 1, az, c);
set(x, ay + 1, az, c);
set(x, ay, az - 1, c);
set(x, ay, az + 1, c);
}
// ---- iron arrowhead ----
box(41, ay - 1, az - 1, 41, ay + 1, az + 1, TIP_MID);
set(41, ay, az + 1, TIP_HI);
set(42, ay, az, TIP_MID);
set(42, ay - 1, az, TIP_DARK);
set(42, ay + 1, az, TIP_DARK);
set(42, ay, az - 1, TIP_MID);
set(42, ay, az + 1, TIP_HI);
set(43, ay, az, TIP_HI);
}
}
// =========================================================
// RETAINING STRAPS OVER THE BUNDLE
// =========================================================
box(20, 15, 12, 22, 33, 13, WOOD_LIGHT);
box(34, 15, 12, 36, 33, 13, WOOD_LIGHT);
box(21, 15, 13, 21, 33, 13, METAL_MID);
box(35, 15, 13, 35, 33, 13, METAL_MID);
// =========================================================
// SLOW MATCH / FUSE
// =========================================================
line(15, 31, 12, 11, 32, 15, 0.55, CORD);
set(14, 31, 12, METAL_MID);
set(15, 31, 12, METAL_MID);
// =========================================================
// BURNING SPARK (bobs + flickers)
// =========================================================
part("spark", [11, 32, 15]);
set(11, 32, 15, "#ffd45e");
set(10, 32, 15, "#ff8a2e");
set(12, 32, 15, "#ff8a2e");
set(11, 32, 16, "#fff0a0");
set(11, 31, 15, "#ffb04e");
animate("spark", { type: "bob", axis: "z", speed: 2.2, amplitude: 0.9 });
colors("spark", {
palette: ["#ffd45e", "#ff7a1e", "#fff3b0", "#ff9a2e"],
pattern: "flicker", speed: 3.0, glow: 1
});
// =========================================================
// FLOATING EMBERS
// =========================================================
part("ember", [12, 32, 18]);
set(12, 32, 18, "#ff9a2e");
set(13, 31, 19, "#ff7a1e");
set(11, 32, 19, "#ffb04e");
animate("ember", { type: "bob", axis: "z", speed: 1.5, amplitude: 1.3 });
colors("ember", {
palette: ["#ff7a1e", "#ffb04e", "#ff3a10"],
pattern: "flicker", speed: 2.0, glow: 1
});
// =========================================================
// FINAL WOOD-GRAIN PASS
// =========================================================
forEach(function (x, y, z, c) {
if (c === WOOD_MID) {
const r = rand();
if (r < 0.10) set(x, y, z, WOOD_DARK);
else if (r < 0.18) set(x, y, z, WOOD_LIGHT);
}
});import { draw } from "./assets/voxel";
import { armaHwacha } from "./assets/arma-hwacha";
// Every frame: standing on (x, y), the centre of its base.
draw(ctx, armaHwacha, 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 arma-hwacha
# With Fogón's tools (connector)
add_assets({ slug: "<your-game>", ids: ["arma-hwacha"] })
# It lands in client/assets/arma-hwacha.ts
# https://fogon.page/object/arma-hwacha