← Weapons3D assetfogon/arma-rebotador
Rebotador
Drag to turn it// «Rebotador» · propio
// A futuristic purple energy blaster with glowing violet coils and a crystal at the muzzle.
const colors = () => {};
// ===== palette =====
const DARK = "#1a0f2e";
const BODY = "#2e1c4f";
const BODY2 = "#3d2668";
const BODY3 = "#513589";
const TRIM = "#b48ce8";
const METAL = "#5d5170";
const METAL2 = "#8477a0";
const COIL_D = "#7a2fd0";
const COIL_M = "#b06bff";
const COIL_L = "#e6b8ff";
const CRYS_D = "#8a2be2";
const CRYS_M = "#c060ff";
const CRYS_L = "#f0c8ff";
// ===== helpers =====
function discYZ(x, cy, cz, r, color){
for (let y = Math.floor(cy - r - 0.5); y <= Math.ceil(cy + r + 0.5); y++)
for (let z = Math.floor(cz - r - 0.5); z <= Math.ceil(cz + r + 0.5); z++)
if (Math.hypot(y - cy, z - cz) <= r) set(x, y, z, color);
}
function ringYZ(x, cy, cz, r, th, color){
for (let y = Math.floor(cy - r - 1); y <= Math.ceil(cy + r + 1); y++)
for (let z = Math.floor(cz - r - 1); z <= Math.ceil(cz + r + 1); z++){
const d = Math.hypot(y - cy, z - cz);
if (d <= r && d >= r - th) set(x, y, z, color);
}
}
part("body");
// ===== main housing =====
box(9, 18, 1, 30, 30, 9, BODY);
box(9, 18, 1, 30, 30, 1, DARK);
box(9, 18, 9, 30, 30, 9, BODY2);
// top trim border
box(9, 18, 9, 9, 30, 9, TRIM);
box(30, 18, 9, 30, 30, 9, TRIM);
box(9, 30, 9, 30, 30, 9, TRIM);
box(9, 18, 9, 30, 18, 9, TRIM);
// rear block / back strap shading
box(9, 18, 2, 11, 30, 8, BODY3);
// side vents (far side)
for (let i = 0; i < 4; i++)
box(14 + i * 3, 30, 4, 15 + i * 3, 30, 7, DARK);
// rear sight
box(10, 22, 10, 12, 26, 10, METAL2);
box(10, 23, 11, 12, 25, 11, METAL);
// top frame plate
box(13, 20, 9, 25, 28, 9, METAL);
// ===== collar between body and barrel =====
box(29, 20, 2, 32, 28, 8, BODY);
box(29, 20, 8, 32, 28, 8, BODY2);
box(29, 20, 2, 32, 28, 2, DARK);
// ===== barrel shroud =====
box(32, 22, 3, 40, 26, 7, BODY);
box(32, 22, 7, 40, 26, 7, BODY2);
box(32, 22, 3, 40, 26, 3, DARK);
// barrel tube
cylinder(38, 24, 5, 2, 5, BODY3, "x");
// front sight
box(38, 23, 8, 39, 25, 8, METAL2);
// ===== pistol grip =====
for (let y = 8; y <= 18; y++){
const t = (18 - y) / 10;
const cx = 17 - 2.5 * t;
const hw = 3.6 - 0.6 * t;
const x0 = Math.round(cx - hw);
const x1 = Math.round(cx + hw);
for (let x = x0; x <= x1; x++)
for (let z = 1; z <= 8; z++){
let c = BODY;
if (z === 8) c = BODY2;
if (z === 1) c = DARK;
if (x === x0 || x === x1) c = BODY3;
set(x, y, z, c);
}
}
// grip ridges
for (let y = 9; y <= 16; y += 2)
box(12, y, 2, 20, y, 2, DARK);
// grip butt cap
box(12, 7, 1, 18, 7, 7, METAL);
box(12, 8, 1, 18, 8, 1, METAL2);
// ===== trigger guard =====
for (let a = 200; a <= 340; a += 3){
const rad = a * Math.PI / 180;
for (const rr of [6.5, 5.7]){
const x = Math.round(24 + rr * Math.cos(rad));
const y = Math.round(18 + rr * Math.sin(rad));
for (let z = 2; z <= 5; z++) set(x, y, z, METAL);
}
}
// trigger
box(21, 16, 3, 22, 17, 5, TRIM);
// ===== glowing energy cell =====
part("cell");
box(15, 22, 9, 23, 26, 10, CRYS_M);
box(15, 22, 10, 23, 22, 10, CRYS_L);
part("body");
// ===== violet coils =====
part("coils");
for (const bx of [33, 35, 37]){
for (let xx = bx; xx < bx + 2; xx++)
ringYZ(xx, 24, 5, 3.4, 1.6, COIL_M);
}
part("body");
// ===== muzzle crystal =====
part("crystal");
const crysR = [[41,1.4],[42,2.6],[43,3.0],[44,2.6],[45,1.6],[46,0.7]];
for (const p of crysR) discYZ(p[0], 24, 5, p[1], CRYS_D);
discYZ(43, 24, 5, 1.9, CRYS_M);
discYZ(44, 24, 5, 1.2, CRYS_L);
part("body");
// ===== floating energy orb =====
part("orb");
sphere(44, 24, 12, 2, CRYS_M);
sphere(44, 24, 12, 1, CRYS_L);
// ===== animation =====
colors("coils", { palette: ["#7a2fd0", "#b06bff", "#e6b8ff", "#8a2be2"], pattern: "wave", speed: 1.6, glow: 0.9 });
colors("crystal", { palette: ["#8a2be2", "#c060ff", "#f0c8ff", "#d97bff"], pattern: "swirl", speed: 1.2, glow: 1.0 });
colors("cell", { palette: ["#7a2fd0", "#c060ff", "#f0c8ff"], pattern: "pulse", speed: 2.0, glow: 0.8 });
colors("orb", { palette: ["#c060ff", "#f0c8ff", "#8a2be2"], pattern: "pulse", speed: 1.4, glow: 1.0 });
void ("orb", { type: "bob", axis: "z", speed: 1.3, amplitude: 1.6 });
// Sit it on the floor (the lowest block was at z=1).
const __all = [];
forEach((x, y, z, c) => __all.push([x, y, z, c]));
for (const [x, y, z] of __all) set(x, y, z, null);
for (const [x, y, z, c] of __all) set(x, y, z - 1, c);import { draw } from "./assets/voxel";
import { armaRebotador } from "./assets/arma-rebotador";
// Every frame: standing on (x, y), the centre of its base.
draw(ctx, armaRebotador, 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-rebotador
# With Fogón's tools (connector)
add_assets({ slug: "<your-game>", ids: ["arma-rebotador"] })
# It lands in client/assets/arma-rebotador.ts
# https://fogon.page/object/arma-rebotador