← ArmasObjeto 3Dfogon/arma-shurikens
Shurikens
Arrastrá para girarlo// «Shurikens» · propio
// Three steel ninja throwing stars (shurikens) fanned out, with a red cloth wrap.
const colors = () => {};
const STEEL = ["#59626f", "#7f8a97", "#a6b1be", "#ccd7e2"];
const CLOTH = ["#6f161d", "#8f1f27", "#ad2b34", "#c93f48", "#e06068"];
// ---- helper: one flat four-pointed shuriken ----
function shuriken(cx, cy, ang) {
const R = 13, W = 5.0;
const ca = Math.cos(ang), sa = Math.sin(ang);
const x0 = Math.max(0, Math.floor(cx - R)), x1 = Math.min(47, Math.ceil(cx + R));
const y0 = Math.max(0, Math.floor(cy - R)), y1 = Math.min(47, Math.ceil(cy + R));
for (let x = x0; x <= x1; x++) {
for (let y = y0; y <= y1; y++) {
const dx = x - cx, dy = y - cy;
const u = dx * ca + dy * sa;
const v = -dx * sa + dy * ca;
const au = Math.abs(u), av = Math.abs(v);
const d = Math.min(au / R + av / W, av / R + au / W);
if (d > 1) continue;
const r2 = au * au + av * av;
if (r2 < 5) continue; // centre hole
let top = 4;
if (d > 0.86) top = 1; // thin blade rim
else if (d > 0.62) top = 3;
let ci;
if (r2 < 11) ci = 0; // dark ring around hole
else if (d > 0.86) ci = 3; // bright sharpened edge
else if (d > 0.62) ci = 2;
else if (d > 0.32) ci = 1;
else ci = 2;
for (let z = 0; z <= top; z++) {
let k = (z === top) ? ci : Math.max(0, ci - 1);
if (z === top && rand() < 0.07) k = Math.min(3, k + 1);
set(x, y, z, STEEL[k]);
}
}
}
}
// ---- three shurikens fanned out, tips toward -y ----
shuriken(18.0, 18.2, -22 * Math.PI / 180);
shuriken(24.0, 17.0, 0);
shuriken(30.0, 18.2, 22 * Math.PI / 180);
// ---- red cloth wrap: main band across the tails ----
for (let x = 11; x <= 37; x++) {
let t = 1;
if (x < 16) t = (x - 10) / 6;
if (x > 32) t = (38 - x) / 6;
if (t <= 0.06) continue;
const ry = 4.8 * t, rz = 8.8 * t;
for (let y = 24; y <= 36; y++) {
for (let z = 0; z <= 13; z++) {
const dy = (y - 30) / ry;
const dz = z / rz;
const q = dy * dy + dz * dz;
if (q > 1) continue;
let idx = q > 0.72 ? 4 : q > 0.42 ? 3 : q > 0.18 ? 2 : 1;
if (x % 5 === 0) idx = Math.max(0, idx - 2);
set(x, y, z, CLOTH[idx]);
}
}
}
// ---- knot sitting on the band ----
for (let x = 19; x <= 29; x++) {
for (let y = 25; y <= 35; y++) {
for (let z = 4; z <= 13; z++) {
const q = ((x - 24) / 4.6) ** 2 + ((y - 30) / 4.6) ** 2 + ((z - 8) / 3.6) ** 2;
if (q > 1) continue;
let idx = q > 0.7 ? 4 : q > 0.42 ? 3 : q > 0.2 ? 2 : 1;
if ((x + y) % 5 === 0) idx = Math.max(0, idx - 2);
set(x, y, z, CLOTH[idx]);
}
}
}
// ---- loose cloth tails, hanging out behind the knot ----
function tail(p0, p1, p2) {
for (let i = 0; i <= 70; i++) {
const t = i / 70;
const x = (1 - t) * (1 - t) * p0[0] + 2 * (1 - t) * t * p1[0] + t * t * p2[0];
const y = (1 - t) * (1 - t) * p0[1] + 2 * (1 - t) * t * p1[1] + t * t * p2[1];
const w = 2.5 - 0.8 * t;
const px = Math.round(x), py = Math.round(y);
for (let dx = -3; dx <= 3; dx++) {
for (let dy = -3; dy <= 3; dy++) {
if (dx * dx + dy * dy > w * w) continue;
for (let z = 0; z <= 2; z++) {
let idx = z === 2 ? 4 : z === 1 ? 3 : 2;
if ((px + dx + 40) % 4 === 0) idx = Math.max(0, idx - 1);
set(px + dx, py + dy, z, CLOTH[idx]);
}
}
}
}
}
part("tail1", [27, 34, 0]);
tail([27, 34], [32, 39], [30, 44]);
part("tail2", [21, 34, 0]);
tail([21, 34], [16, 39], [18, 44]);
animate("tail1", { type: "swing", axis: "z", speed: 1.4, amplitude: 0.14 });
animate("tail2", { type: "swing", axis: "z", speed: 1.1, amplitude: 0.17 });import { draw } from "./assets/voxel";
import { armaShurikens } from "./assets/arma-shurikens";
// En cada cuadro: parado sobre (x, y), el centro de su base.
draw(ctx, armaShurikens, 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 arma-shurikens
# Con las herramientas de Fogón (conector)
add_assets({ slug: "<tu-juego>", ids: ["arma-shurikens"] })
# Queda en client/assets/arma-shurikens.ts
# https://fogon.page/object/arma-shurikens