← Weapons3D assetfogon/arma-ametralladora
Ametralladora
Drag to turn it// «Ametralladora» · propio
// A heavy machine gun with a bipod at the front, perforated barrel cover and a brass ammo belt.
const colors = () => {};
// ===== CHUNKY VOXEL HEAVY MACHINE GUN (lies flat, muzzle -> +x) =====
const GM_D="#1e2227", GM_1="#333b44", GM_2="#4a5560", GM_3="#616e7b", GM_4="#8090a0";
const BR_D="#8a6a14", BR_1="#c8a02a", BR_2="#edcf63";
const WD_D="#2b3027", WD_1="#3d4436", WD_2="#525b48";
// ---------- barrel core ----------
cylinder(29, 24, 10, 2, 19, GM_D, "x");
// ---------- perforated barrel shroud ----------
for (let x = 31; x <= 44; x++) {
for (let y = 19; y <= 29; y++) {
for (let z = 5; z <= 15; z++) {
const dy = y - 24, dz = z - 10;
const r = Math.sqrt(dy*dy + dz*dz);
if (r > 5.2 || r < 3.5) continue;
let hole = false;
for (const xh of [33, 37, 41]) {
if (Math.abs(x - xh) <= 1) {
for (let k = 0; k < 6; k++) {
const a = k * Math.PI / 3 + Math.PI / 6;
const hy = 24 + Math.cos(a) * 4.4;
const hz = 10 + Math.sin(a) * 4.4;
if (Math.hypot(y - hy, z - hz) <= 1.3) hole = true;
}
}
}
if (hole) continue;
let c = GM_2;
if (dz >= 3) c = GM_3; else if (dz <= -3) c = GM_1;
set(x, y, z, c);
}
}
}
// ---------- front collar + muzzle brake ----------
for (let y = 19; y <= 29; y++) for (let z = 5; z <= 15; z++) {
const r = Math.hypot(y - 24, z - 10);
if (r <= 5.2 && r >= 3.1) set(45, y, z, GM_1);
}
for (let x = 46; x <= 47; x++) for (let y = 20; y <= 28; y++) for (let z = 6; z <= 14; z++) {
const r = Math.hypot(y - 24, z - 10);
if (r <= 3.7 && r >= 1.4) set(x, y, z, GM_2);
}
// front sight blade
box(43, 23, 16, 44, 25, 18, GM_2);
box(43, 24, 19, 44, 24, 19, GM_3);
// ---------- receiver body ----------
for (let x = 13; x <= 30; x++) {
for (let y = 18; y <= 30; y++) {
for (let z = 4; z <= 16; z++) {
const dy = Math.abs(y - 24), dz = Math.abs(z - 10);
if (dy > 5 || dz > 5 || (dy === 5 && dz === 5)) continue;
let c = GM_1;
if (z >= 14) c = GM_2; else if (z <= 6) c = GM_D;
set(x, y, z, c);
}
}
}
// raised side panels
box(16, 30, 7, 29, 30, 12, GM_2);
box(16, 18, 7, 29, 18, 12, GM_2);
// ejection port
box(20, 30, 8, 24, 30, 11, GM_D);
// top rib + rear sight
box(17, 22, 15, 27, 26, 15, GM_3);
box(14, 22, 16, 15, 26, 16, GM_1);
box(14, 22, 17, 14, 23, 17, GM_1);
box(14, 25, 17, 14, 26, 17, GM_1);
// small feed latch on the left flank
box(26, 18, 12, 28, 18, 13, GM_3);
// ---------- shoulder stock ----------
for (let x = 3; x <= 12; x++) {
const t = (12 - x) / 9;
const hw = 4 + Math.round(1.6 * t);
const z0 = 6 - Math.round(2 * t);
const z1 = 14 + Math.round(2 * t);
for (let y = 24 - hw; y <= 24 + hw; y++) {
for (let z = z0; z <= z1; z++) {
let c = WD_1;
if (z >= z1 - 1) c = WD_2;
else if (z <= z0 + 1) c = WD_D;
else if (y === 24 + hw || y === 24 - hw) c = WD_D;
set(x, y, z, c);
}
}
}
// butt plate
for (let x = 2; x <= 3; x++) {
for (let y = 18; y <= 30; y++) {
for (let z = 4; z <= 16; z++) {
const dy = Math.abs(y - 24), dz = Math.abs(z - 10);
if (dy > 6 || dz > 6 || (dy === 6 && dz === 6)) continue;
set(x, y, z, (x === 2 && dy >= 5) ? GM_D : GM_1);
}
}
}
// ---------- pistol grip ----------
for (let z = 0; z <= 4; z++) {
const x0 = 15 + Math.round(z * 0.6);
box(x0, 20, z, x0 + 5, 27, z, WD_1);
if (z <= 1) box(x0, 20, z, x0 + 5, 27, z, WD_D);
box(x0, 20, z, x0, 27, z, WD_D);
}
// ---------- trigger guard ----------
box(23, 22, 2, 27, 26, 2, GM_1);
box(26, 22, 2, 27, 26, 4, GM_1);
box(23, 22, 3, 27, 22, 4, GM_1);
box(23, 26, 3, 27, 26, 4, GM_1);
box(24, 23, 2, 24, 25, 3, GM_D);
// ---------- bipod ----------
box(35, 22, 3, 37, 26, 4, GM_1);
line(36, 22, 4, 33, 16, 0, 1.2, GM_2);
line(36, 26, 4, 33, 32, 0, 1.2, GM_2);
box(32, 15, 0, 34, 17, 1, GM_1);
box(32, 31, 0, 34, 33, 1, GM_1);
box(34, 23, 2, 35, 25, 2, GM_3);
// ---------- carry handle ----------
box(19, 23, 16, 20, 25, 19, GM_2);
box(26, 23, 16, 27, 25, 19, GM_2);
box(19, 23, 19, 27, 25, 20, GM_2);
box(21, 23, 19, 25, 25, 20, GM_3);
// ---------- cocking handle ----------
line(25, 30, 11, 28, 33, 11, 1, GM_3);
sphere(28, 33, 11, 1.6, GM_4);
// ---------- subtle surface variation ----------
forEach((x, y, z, c) => {
if (c === GM_1 && rand() < 0.07) set(x, y, z, GM_D);
else if (c === GM_2 && rand() < 0.06) set(x, y, z, GM_1);
else if (c === WD_1 && rand() < 0.12) set(x, y, z, WD_D);
});
// ---------- brass ammo belt (draped, sways gently) ----------
part("belt", [27, 17, 11]);
const beltPts = [[27,17,11],[27,16,8],[28,15,5],[29,14,3],[30,13,1],[33,13,1]];
let linkN = 0;
for (let i = 0; i < beltPts.length - 1; i++) {
const a = beltPts[i], b = beltPts[i + 1];
const d = Math.hypot(b[0]-a[0], b[1]-a[1], b[2]-a[2]);
const steps = Math.max(1, Math.round(d / 1.7));
for (let s = 0; s < steps; s++) {
const t = s / steps;
const x = Math.round(a[0] + (b[0]-a[0]) * t);
const y = Math.round(a[1] + (b[1]-a[1]) * t);
const z = Math.round(a[2] + (b[2]-a[2]) * t);
const c = (linkN % 3 === 2) ? BR_2 : (linkN % 3 === 1 ? BR_D : BR_1);
box(x - 1, y - 1, z, x + 1, y, z + 1, c);
linkN++;
}
}
animate("belt", { type: "swing", axis: "x", speed: 1.5, amplitude: 0.12 });import { draw } from "./assets/voxel";
import { armaAmetralladora } from "./assets/arma-ametralladora";
// Every frame: standing on (x, y), the centre of its base.
draw(ctx, armaAmetralladora, 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-ametralladora
# With Fogón's tools (connector)
add_assets({ slug: "<your-game>", ids: ["arma-ametralladora"] })
# It lands in client/assets/arma-ametralladora.ts
# https://fogon.page/object/arma-ametralladora