@pixelvibing/cli 0.53.0 → 0.55.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/pixel-vibing-cli.js
CHANGED
|
@@ -3428,8 +3428,9 @@ function setPieces(asset) {
|
|
|
3428
3428
|
function namesOf(name, count) {
|
|
3429
3429
|
return count === 1 ? [name] : Array.from({ length: count }, (_, index) => `${name}/${index}`);
|
|
3430
3430
|
}
|
|
3431
|
-
function nameFor(asset, record, given2) {
|
|
3432
|
-
const
|
|
3431
|
+
function nameFor(asset, record, given2, key, hd) {
|
|
3432
|
+
const platform = asset.name ?? SET_NAMES[asset.kind] ?? `${asset.kind}-${asset.id.slice(0, 8)}`;
|
|
3433
|
+
const name = given2 ?? record.assets[key]?.name ?? (hd ? `${platform}-hd` : platform);
|
|
3433
3434
|
const problem = nameProblem(name);
|
|
3434
3435
|
if (problem !== null) throw new CliError(`${name} is not a name a picture can be landed under (${problem}): give one with --as`);
|
|
3435
3436
|
return name;
|
|
@@ -3442,13 +3443,22 @@ async function pullAsset(platform, local, ref, given2, options = {}) {
|
|
|
3442
3443
|
if (asset.kind === "terrain") {
|
|
3443
3444
|
throw new CliError(`${asset.id} is a terrain set: land it from the editor's Library, which cuts a set into the cells its brush paints. Nothing was landed`);
|
|
3444
3445
|
}
|
|
3445
|
-
const record = await readRecord(local);
|
|
3446
|
-
const name = nameFor(asset, record, given2);
|
|
3447
3446
|
const pieces = setPieces(asset);
|
|
3447
|
+
const hd = options.hd === true;
|
|
3448
|
+
const drawnSmooth = asset.data.pixelArt === false;
|
|
3449
|
+
if (hd && pieces === null) throw new CliError(`${asset.id} is a ${asset.kind}: only a UI's pieces and icons have an HD version. Nothing was landed`);
|
|
3450
|
+
if (hd && !drawnSmooth && pieces?.some((piece) => piece.hd === void 0 || asset.attachments?.[piece.hd] === void 0)) {
|
|
3451
|
+
throw new CliError(`${asset.id} was made before its pieces were kept as drawn, so it has no HD version. Nothing was landed`);
|
|
3452
|
+
}
|
|
3453
|
+
const lifted = hd && !drawnSmooth;
|
|
3454
|
+
const key = lifted ? `${asset.id}/hd` : asset.id;
|
|
3455
|
+
const record = await readRecord(local);
|
|
3456
|
+
const name = nameFor(asset, record, given2, key, lifted);
|
|
3448
3457
|
const reference = REFERENCE_KINDS.has(asset.kind);
|
|
3449
3458
|
const names = pieces === null ? namesOf(name, asset.images.length) : pieceNames(name, pieces);
|
|
3450
3459
|
const files = names.map((each) => `${reference ? "ref" : "out"}/${each}.png`);
|
|
3451
|
-
const
|
|
3460
|
+
const version2 = pieces === null ? {} : { version: drawnSmooth || lifted ? "hd" : "pixel-art" };
|
|
3461
|
+
const pulled = { id: asset.id, kind: asset.kind, name, hash: asset.hash, files, ...version2, data: asset.data };
|
|
3452
3462
|
const base = `/projects/${encodeURIComponent(project)}`;
|
|
3453
3463
|
const json2 = async (path, init2) => (await platform.call(path, init2)).json();
|
|
3454
3464
|
const art = await json2(`${base}/art`);
|
|
@@ -3456,7 +3466,9 @@ async function pullAsset(platform, local, ref, given2, options = {}) {
|
|
|
3456
3466
|
const held = (each) => reference ? references.get(each) : art.pictures[each];
|
|
3457
3467
|
const landing = [];
|
|
3458
3468
|
for (const [index, each] of names.entries()) {
|
|
3459
|
-
const
|
|
3469
|
+
const own = asset.images[index];
|
|
3470
|
+
const drawn = lifted ? asset.attachments?.[pieces?.[index]?.hd] : void 0;
|
|
3471
|
+
const image = drawn?.id ?? own.id;
|
|
3460
3472
|
const holds = held(each);
|
|
3461
3473
|
if (holds === image) continue;
|
|
3462
3474
|
if (holds !== void 0) {
|
|
@@ -3464,7 +3476,8 @@ async function pullAsset(platform, local, ref, given2, options = {}) {
|
|
|
3464
3476
|
`the project's art already holds ${reference ? "a reference" : "a picture"} named ${each}, of something else: nothing was landed. Give this one another name with --as`
|
|
3465
3477
|
);
|
|
3466
3478
|
}
|
|
3467
|
-
|
|
3479
|
+
const piece = pieces?.[index] ?? null;
|
|
3480
|
+
landing.push({ name: each, image, piece: piece === null || drawn === void 0 ? piece : asDrawn(piece, drawn.width / own.width) });
|
|
3468
3481
|
}
|
|
3469
3482
|
if (landing.length > 0) {
|
|
3470
3483
|
if (reference) {
|
|
@@ -3478,13 +3491,24 @@ async function pullAsset(platform, local, ref, given2, options = {}) {
|
|
|
3478
3491
|
await json2(`${base}/art/land`, { method: "POST", body: { revision: art.revision, assets } });
|
|
3479
3492
|
}
|
|
3480
3493
|
}
|
|
3481
|
-
await writeRecord(local, { ...record, assets: { ...record.assets, [
|
|
3494
|
+
await writeRecord(local, { ...record, assets: { ...record.assets, [key]: { kind: asset.kind, name, hash: asset.hash, files } } });
|
|
3482
3495
|
await bringFolder(platform, local, { map: false, aside: options.aside === true });
|
|
3483
3496
|
return { ...pulled, current: landing.length === 0 };
|
|
3484
3497
|
}
|
|
3498
|
+
function asDrawn(piece, scale) {
|
|
3499
|
+
if (piece.nineGrid === void 0 || !Number.isFinite(scale) || scale <= 1) return piece;
|
|
3500
|
+
const grown = (inset) => Math.round(inset * scale);
|
|
3501
|
+
const { top, right, bottom, left } = piece.nineGrid;
|
|
3502
|
+
return { ...piece, nineGrid: { top: grown(top), right: grown(right), bottom: grown(bottom), left: grown(left) } };
|
|
3503
|
+
}
|
|
3485
3504
|
function pulledWords(pulled) {
|
|
3486
3505
|
const where = pulled.files.length === 1 ? `pixelvibing/${pulled.files[0]}` : `${pulled.files.length} pictures under pixelvibing/`;
|
|
3487
|
-
|
|
3506
|
+
const said = pulled.current ? `${pulled.name} is current: the project's art holds it, and ${where} is what the platform has.` : `Pulled ${pulled.name}, ${pulled.kind}, into the project's art, and wrote ${where}.`;
|
|
3507
|
+
if (pulled.version === "hd" && pulled.data.pixelArt === false)
|
|
3508
|
+
return `${said} It was drawn without pixel art, so its pieces are HD and there is no pixel-art version.`;
|
|
3509
|
+
if (pulled.version === "hd") return `${said} These are the pieces as drawn (HD).`;
|
|
3510
|
+
if (pulled.version === "pixel-art") return `${said} These are the pixel-art pieces; --hd lands them as drawn.`;
|
|
3511
|
+
return said;
|
|
3488
3512
|
}
|
|
3489
3513
|
const date = (at) => at.slice(0, 10);
|
|
3490
3514
|
function listed(assets) {
|
|
@@ -3518,9 +3542,10 @@ More: pixel-vibing-cli list${kind === void 0 ? "" : ` ${kind}`}${tag2 === null ?
|
|
|
3518
3542
|
const pull = {
|
|
3519
3543
|
name: "pull",
|
|
3520
3544
|
summary: "Land one asset, by its id or its name, in the project's art, and write it into the local project",
|
|
3521
|
-
usage: "<id|name> [--as <name>] [--aside]",
|
|
3545
|
+
usage: "<id|name> [--as <name>] [--aside] [--hd]",
|
|
3522
3546
|
options: {
|
|
3523
3547
|
as: { type: "string", value: "<name>", description: "land it under this name instead of the platform's" },
|
|
3548
|
+
hd: { type: "boolean", description: "land a UI's pieces or icons as drawn (HD), beside the pixel-art ones, instead of read back on the pixel grid" },
|
|
3524
3549
|
aside: { type: "boolean", description: "keep a folder's own map and art under pixelvibing/before-platform/, and write it from the platform" }
|
|
3525
3550
|
},
|
|
3526
3551
|
positionals: [1, 1],
|
|
@@ -3528,7 +3553,10 @@ const pull = {
|
|
|
3528
3553
|
const local = await projectHere(context.io.cwd);
|
|
3529
3554
|
linkedOf(local);
|
|
3530
3555
|
const platform = await signedIn(context);
|
|
3531
|
-
const pulled = await pullAsset(platform, local, args.positionals[0], stringOf(args, "as"), {
|
|
3556
|
+
const pulled = await pullAsset(platform, local, args.positionals[0], stringOf(args, "as"), {
|
|
3557
|
+
aside: flagOf(args, "aside"),
|
|
3558
|
+
hd: flagOf(args, "hd")
|
|
3559
|
+
});
|
|
3532
3560
|
answer(context, pulled, pulledWords(pulled));
|
|
3533
3561
|
}
|
|
3534
3562
|
};
|
|
@@ -4508,33 +4536,58 @@ function shapeProblem(shape, index, canvas) {
|
|
|
4508
4536
|
return null;
|
|
4509
4537
|
}
|
|
4510
4538
|
function sketchProblem(sketch) {
|
|
4511
|
-
|
|
4512
|
-
|
|
4513
|
-
|
|
4539
|
+
const problems = sketchProblems(sketch);
|
|
4540
|
+
return problems.length === 0 ? null : problems.join("; ");
|
|
4541
|
+
}
|
|
4542
|
+
function sketchProblems(sketch) {
|
|
4543
|
+
const problems = [];
|
|
4544
|
+
if (typeof sketch.instruction !== "string" || sketch.instruction.trim() === "") problems.push("the sketch has no description");
|
|
4545
|
+
else if (sketch.instruction.length > MAX_SKETCH_INSTRUCTION)
|
|
4546
|
+
problems.push(`the description is ${sketch.instruction.length} characters; it is at most ${MAX_SKETCH_INSTRUCTION}`);
|
|
4514
4547
|
const { canvas, shapes } = sketch;
|
|
4515
4548
|
if (!SKETCH_CANVASES.some((size) => size.width === canvas.width && size.height === canvas.height)) {
|
|
4516
|
-
|
|
4549
|
+
problems.push(
|
|
4550
|
+
`the canvas is ${canvas.width} × ${canvas.height}; a sketch is ${SKETCH_CANVASES.map((size) => `${size.width} × ${size.height}`).join(", ")}`
|
|
4551
|
+
);
|
|
4552
|
+
return problems;
|
|
4517
4553
|
}
|
|
4518
|
-
if (shapes.length === 0)
|
|
4519
|
-
if (shapes.length > MAX_SKETCH_SHAPES)
|
|
4520
|
-
const labels = /* @__PURE__ */ new
|
|
4554
|
+
if (shapes.length === 0) problems.push("the sketch has no shape");
|
|
4555
|
+
if (shapes.length > MAX_SKETCH_SHAPES) problems.push(`the sketch has ${shapes.length} shapes; it holds at most ${MAX_SKETCH_SHAPES}`);
|
|
4556
|
+
const labels = /* @__PURE__ */ new Set();
|
|
4557
|
+
const twice = /* @__PURE__ */ new Set();
|
|
4558
|
+
const sound = [];
|
|
4521
4559
|
for (const [index, shape] of shapes.entries()) {
|
|
4522
4560
|
const problem = shapeProblem(shape, index, canvas);
|
|
4523
|
-
if (problem !== null)
|
|
4561
|
+
if (problem !== null) {
|
|
4562
|
+
problems.push(problem);
|
|
4563
|
+
continue;
|
|
4564
|
+
}
|
|
4565
|
+
sound.push(index);
|
|
4524
4566
|
const key = shape.label.trim().toLowerCase();
|
|
4525
|
-
if (labels.has(key)
|
|
4526
|
-
|
|
4567
|
+
if (labels.has(key) && !twice.has(key)) {
|
|
4568
|
+
twice.add(key);
|
|
4569
|
+
problems.push(`"${shape.label.trim()}" is the label of two shapes`);
|
|
4570
|
+
}
|
|
4571
|
+
labels.add(key);
|
|
4527
4572
|
}
|
|
4528
|
-
|
|
4573
|
+
const overlapping = /* @__PURE__ */ new Set();
|
|
4574
|
+
for (const index of sound) {
|
|
4575
|
+
const shape = shapes[index];
|
|
4529
4576
|
for (let y = shape.y; y < shape.y + shape.height; y += 1) {
|
|
4530
4577
|
for (let x = shape.x; x < shape.x + shape.width; x += 1) {
|
|
4531
4578
|
if (!inShape(shape, x + 0.5, y + 0.5)) continue;
|
|
4532
|
-
const other
|
|
4533
|
-
|
|
4579
|
+
for (const other of sound) {
|
|
4580
|
+
if (other >= index || !inShape(shapes[other], x + 0.5, y + 0.5)) continue;
|
|
4581
|
+
const pair = `"${shapes[other]?.label.trim()}" and "${shape.label.trim()}" overlap`;
|
|
4582
|
+
if (!overlapping.has(pair)) {
|
|
4583
|
+
overlapping.add(pair);
|
|
4584
|
+
problems.push(pair);
|
|
4585
|
+
}
|
|
4586
|
+
}
|
|
4534
4587
|
}
|
|
4535
4588
|
}
|
|
4536
4589
|
}
|
|
4537
|
-
return
|
|
4590
|
+
return problems;
|
|
4538
4591
|
}
|
|
4539
4592
|
function sentShape(shape) {
|
|
4540
4593
|
const caption = shape.caption?.trim() ?? "";
|
|
@@ -4677,6 +4730,748 @@ function terrainSetPatch(pngs, masks, codec2) {
|
|
|
4677
4730
|
const TERRAIN_CELLS_PER_TILE = [32, 64];
|
|
4678
4731
|
const REFERENCE_ROLES = ["style", "source", "variant"];
|
|
4679
4732
|
const DEFAULT_TERRAIN_CELLS = 32;
|
|
4733
|
+
const VFX_RAMPS = ["arcane", "blood", "electric", "fire", "frost", "holy", "nature", "poison", "shadow", "smoke", "water"];
|
|
4734
|
+
const VFX_PARTS_RANGE = { min: 1, max: 6 };
|
|
4735
|
+
const VFX_COLOURS_RANGE = { min: 2, max: 12 };
|
|
4736
|
+
const VFX_SLIDERS = {
|
|
4737
|
+
size: { min: 0.25, max: 4 },
|
|
4738
|
+
amount: { min: 0.25, max: 3 },
|
|
4739
|
+
speed: { min: 0.25, max: 3 },
|
|
4740
|
+
start: { min: 0, max: 1 },
|
|
4741
|
+
end: { min: 0, max: 1 }
|
|
4742
|
+
};
|
|
4743
|
+
const VFX_PRESETS = [
|
|
4744
|
+
{
|
|
4745
|
+
name: "aura",
|
|
4746
|
+
description: "A flickering aura of power around a standing character: flame tongues behind, sparks rising in front. Seamless loop. Best around a character.",
|
|
4747
|
+
loops: true
|
|
4748
|
+
},
|
|
4749
|
+
{ name: "energy_orb", description: "A crackling ball of energy (projectile head or charge-up) with orbiting sparks. Seamless loop.", loops: true },
|
|
4750
|
+
{ name: "explosion", description: "Fireball explosion: flash, a billowing fire mass that burns out, a shockwave ring, debris. One-shot.", loops: false },
|
|
4751
|
+
{
|
|
4752
|
+
name: "firelight",
|
|
4753
|
+
description: "Warm, flickering firelight from below, as from a campfire at the subject's feet; the far side sinks into the dark. It lights your sprite (shown on a sample asteroid when there is none). Loop.",
|
|
4754
|
+
loops: true
|
|
4755
|
+
},
|
|
4756
|
+
{ name: "flame", description: "A burning flame (campfire, torch, brazier). Seamless loop.", loops: true },
|
|
4757
|
+
{
|
|
4758
|
+
name: "ground_sigil",
|
|
4759
|
+
description: "Summoning circle on the ground, seen from low above, turning, with light rising out of it. Best with a character standing in its middle.",
|
|
4760
|
+
loops: true
|
|
4761
|
+
},
|
|
4762
|
+
{
|
|
4763
|
+
name: "heal_pillar",
|
|
4764
|
+
description: "A soft column of healing light with a ground ring and rising sparkles. Seamless loop; good around a character.",
|
|
4765
|
+
loops: true
|
|
4766
|
+
},
|
|
4767
|
+
{ name: "impact_burst", description: "A hit spark: white flash, rays and a ring punching outward, sparks. One-shot, small.", loops: false },
|
|
4768
|
+
{
|
|
4769
|
+
name: "light_sweep",
|
|
4770
|
+
description: "A light circling the subject: sunlight moving across a rock, a planet turning, an asteroid tumbling past a star. It lights your sprite (shown on a sample asteroid when there is none). Seamless loop.",
|
|
4771
|
+
loops: true
|
|
4772
|
+
},
|
|
4773
|
+
{ name: "lightning_strike", description: "A bolt strikes down from above, flashes, hits the ground with a shockwave and sparks. One-shot.", loops: false },
|
|
4774
|
+
{
|
|
4775
|
+
name: "magic_circle",
|
|
4776
|
+
description: "Front-facing arcane circle: rings, a hexagram and a rune band that turn slowly, twinkling motes. Seamless loop.",
|
|
4777
|
+
loops: true
|
|
4778
|
+
},
|
|
4779
|
+
{ name: "portal", description: "An upright swirling portal: churning core, glowing rim, orbiting motes. Seamless loop.", loops: true },
|
|
4780
|
+
{
|
|
4781
|
+
name: "rim_glow",
|
|
4782
|
+
description: "A pulsing rim of light around the subject's outline, as if something bright stands right behind it (a portal, a halo, an explosion). It lights your sprite (shown on a sample asteroid when there is none). Seamless loop.",
|
|
4783
|
+
loops: true
|
|
4784
|
+
},
|
|
4785
|
+
{
|
|
4786
|
+
name: "shield_bubble",
|
|
4787
|
+
description: "A protective bubble: shimmering rim, glints running round it. Seamless loop. Best around a character.",
|
|
4788
|
+
loops: true
|
|
4789
|
+
},
|
|
4790
|
+
{
|
|
4791
|
+
name: "shockwave",
|
|
4792
|
+
description: "A stomp or landing: a ring of force racing out along the ground, kicking up dust. One-shot, seen from low above.",
|
|
4793
|
+
loops: false
|
|
4794
|
+
},
|
|
4795
|
+
{ name: "slash_arc", description: "A sword slash: a crescent that sweeps open, then fades, with a few glints. One-shot.", loops: false },
|
|
4796
|
+
{ name: "smoke_puff", description: "A puff of smoke that billows up and thins away. One-shot.", loops: false }
|
|
4797
|
+
];
|
|
4798
|
+
const VFX_PARTS = [
|
|
4799
|
+
{
|
|
4800
|
+
name: "alert_pop",
|
|
4801
|
+
description: "A red ! pops up above a character's head with a bounce and burst lines, holds, then vanishes: spotted, surprised, warning.",
|
|
4802
|
+
loops: false,
|
|
4803
|
+
tags: ["alert", "exclamation", "ui", "surprise", "warning", "icon"]
|
|
4804
|
+
},
|
|
4805
|
+
{
|
|
4806
|
+
name: "aura_flames",
|
|
4807
|
+
description: "Flickering tongues of power rising behind a standing character, a ring at the feet, sparks in front.",
|
|
4808
|
+
loops: true,
|
|
4809
|
+
tags: ["aura", "power", "buff", "flames", "charge", "rage"]
|
|
4810
|
+
},
|
|
4811
|
+
{
|
|
4812
|
+
name: "blaster_bolt",
|
|
4813
|
+
description: "A hot capsule of energy with a white core and speed lines behind, flying right (+x): laser, plasma or blaster shots.",
|
|
4814
|
+
loops: true,
|
|
4815
|
+
tags: ["projectile", "laser", "blaster", "energy", "shot", "bolt"]
|
|
4816
|
+
},
|
|
4817
|
+
{
|
|
4818
|
+
name: "blood_splat",
|
|
4819
|
+
description: "A red splat on the hit point with droplets spraying out and falling. Stylised gore; recolour for green ichor or slime.",
|
|
4820
|
+
loops: false,
|
|
4821
|
+
tags: ["blood", "hit", "gore", "splatter", "impact", "droplets"]
|
|
4822
|
+
},
|
|
4823
|
+
{
|
|
4824
|
+
name: "bolt_strike",
|
|
4825
|
+
description: "A bolt strikes down from above onto the anchor, flashes, a shockwave and sparks where it hits.",
|
|
4826
|
+
loops: false,
|
|
4827
|
+
tags: ["lightning", "electric", "bolt", "storm", "strike", "thunder"]
|
|
4828
|
+
},
|
|
4829
|
+
{
|
|
4830
|
+
name: "breath_cone",
|
|
4831
|
+
description: "A billowing cone of flame and sparks pouring right (+x) from the anchor: fire breath, flamethrower; frost or poison gas in other colours.",
|
|
4832
|
+
loops: true,
|
|
4833
|
+
tags: ["breath", "cone", "flamethrower", "spray", "fire", "gas"]
|
|
4834
|
+
},
|
|
4835
|
+
{
|
|
4836
|
+
name: "bubble_shield",
|
|
4837
|
+
description: "A see-through dithered bubble around a character: bright rim, a shine highlight, glints running round: shield, barrier, protect.",
|
|
4838
|
+
loops: true,
|
|
4839
|
+
tags: ["shield", "bubble", "barrier", "protect", "buff"]
|
|
4840
|
+
},
|
|
4841
|
+
{
|
|
4842
|
+
name: "bubbles_rise",
|
|
4843
|
+
description: "Round bubbles of a few sizes rising and wobbling up from the ground: underwater, a bubbling pool, potions, drowning.",
|
|
4844
|
+
loops: true,
|
|
4845
|
+
tags: ["bubbles", "water", "underwater", "rising", "ambient", "potion"]
|
|
4846
|
+
},
|
|
4847
|
+
{
|
|
4848
|
+
name: "buff_arrows",
|
|
4849
|
+
description: "Up-arrows rising from the feet behind a character and fading out: a stat buff, power-up, attack or speed boost.",
|
|
4850
|
+
loops: true,
|
|
4851
|
+
tags: ["buff", "arrows", "status", "powerup", "rising"]
|
|
4852
|
+
},
|
|
4853
|
+
{
|
|
4854
|
+
name: "burn_status",
|
|
4855
|
+
description: "Small flames licking up around and over a character, embers and a hot rim on the sprite: on fire, burn damage over time.",
|
|
4856
|
+
loops: true,
|
|
4857
|
+
tags: ["burn", "fire", "status", "flames", "dot"]
|
|
4858
|
+
},
|
|
4859
|
+
{
|
|
4860
|
+
name: "burning_edge",
|
|
4861
|
+
description: "Sparks flickering up off the sprite's whole outline in a loop: on fire, powered up, cursed, overheating.",
|
|
4862
|
+
loops: true,
|
|
4863
|
+
tags: ["outline", "burn", "aura", "sparks", "loop"]
|
|
4864
|
+
},
|
|
4865
|
+
{
|
|
4866
|
+
name: "campfire",
|
|
4867
|
+
description: "A drawn campfire: five tongues from one hot bed, fanning out and flickering, a few sparks, lighting the scene.",
|
|
4868
|
+
loops: true,
|
|
4869
|
+
tags: ["fire", "campfire", "bonfire", "camp", "loop"]
|
|
4870
|
+
},
|
|
4871
|
+
{
|
|
4872
|
+
name: "candle_flame",
|
|
4873
|
+
description: "A small calm drawn teardrop flame that sways a little, lighting the sprite: candles, lanterns, wicks, a match.",
|
|
4874
|
+
loops: true,
|
|
4875
|
+
tags: ["fire", "candle", "flame", "light", "loop"]
|
|
4876
|
+
},
|
|
4877
|
+
{
|
|
4878
|
+
name: "cartoon_boom",
|
|
4879
|
+
description: "A drawn cartoon explosion: a hot flash, lobed fire clouds thrown out and cooling, then smoke. Bombs, barrels, crits.",
|
|
4880
|
+
loops: false,
|
|
4881
|
+
tags: ["explosion", "fire", "cartoon", "boom", "impact"]
|
|
4882
|
+
},
|
|
4883
|
+
{
|
|
4884
|
+
name: "charge_up",
|
|
4885
|
+
description: "Motes of energy swirling in from all round and gathering at one point, a glow swelling there. Charging a spell.",
|
|
4886
|
+
loops: false,
|
|
4887
|
+
tags: ["charge", "gather", "energy", "magic", "converge"]
|
|
4888
|
+
},
|
|
4889
|
+
{
|
|
4890
|
+
name: "charm_hearts",
|
|
4891
|
+
description: "Three small pink hearts circling the head evenly, passing in front and behind, with twinkles: charmed, infatuated, love status.",
|
|
4892
|
+
loops: true,
|
|
4893
|
+
tags: ["charm", "hearts", "love", "status", "orbit"]
|
|
4894
|
+
},
|
|
4895
|
+
{
|
|
4896
|
+
name: "claw_marks",
|
|
4897
|
+
description: "Three parallel claw scratches ripping diagonally across the target, then crumbling away. Beast attacks, rakes, scratches.",
|
|
4898
|
+
loops: false,
|
|
4899
|
+
tags: ["claw", "scratch", "slash", "beast", "hit", "melee"]
|
|
4900
|
+
},
|
|
4901
|
+
{
|
|
4902
|
+
name: "claw_swipe",
|
|
4903
|
+
description: "Three drawn claw marks raked across in a quick swipe, one a beat after the other, crumbling away. Beasts, monsters.",
|
|
4904
|
+
loops: false,
|
|
4905
|
+
tags: ["claw", "swipe", "beast", "scratch", "hit"]
|
|
4906
|
+
},
|
|
4907
|
+
{
|
|
4908
|
+
name: "cloud_puff",
|
|
4909
|
+
description: "A looping drawn cloud clump wobbling in place, lit from above. Rain or storm clouds over a head, sky decor, dreams.",
|
|
4910
|
+
loops: true,
|
|
4911
|
+
tags: ["cloud", "sky", "weather", "loop", "cartoon"]
|
|
4912
|
+
},
|
|
4913
|
+
{
|
|
4914
|
+
name: "coin_burst",
|
|
4915
|
+
description: "Gold coins pop out of the anchor with a flash ring and glints, arc up and fall away: loot drops, pickups, rewards.",
|
|
4916
|
+
loops: false,
|
|
4917
|
+
tags: ["coins", "loot", "pickup", "gold", "reward", "burst"]
|
|
4918
|
+
},
|
|
4919
|
+
{
|
|
4920
|
+
name: "collect_pop",
|
|
4921
|
+
description: "A quick gold glint and ring popping on the anchor, sparkles drifting up and out: an item, coin or power-up being picked up.",
|
|
4922
|
+
loops: false,
|
|
4923
|
+
tags: ["pickup", "collect", "sparkle", "glint", "item", "reward"]
|
|
4924
|
+
},
|
|
4925
|
+
{
|
|
4926
|
+
name: "comet_trail",
|
|
4927
|
+
description: "A bright head streaking across with a long tapered ribbon tail: comets, magic missiles, wisps; a throw's trail.",
|
|
4928
|
+
loops: false,
|
|
4929
|
+
tags: ["comet", "ribbon", "trail", "missile", "projectile"]
|
|
4930
|
+
},
|
|
4931
|
+
{
|
|
4932
|
+
name: "confetti_pop",
|
|
4933
|
+
description: "A pop of multicoloured paper confetti thrown up from the anchor, flipping as it falls: wins, celebrations, achievements.",
|
|
4934
|
+
loops: false,
|
|
4935
|
+
tags: ["confetti", "celebration", "win", "party", "reward", "burst"]
|
|
4936
|
+
},
|
|
4937
|
+
{
|
|
4938
|
+
name: "crater_dust",
|
|
4939
|
+
description: "A ring of dust clouds billowing out and up from a ground impact, then breaking apart. Heavy landings, slams, boulders.",
|
|
4940
|
+
loops: false,
|
|
4941
|
+
tags: ["dust", "impact", "ground", "slam", "landing", "cloud"]
|
|
4942
|
+
},
|
|
4943
|
+
{
|
|
4944
|
+
name: "curse_skulls",
|
|
4945
|
+
description: "Little pale skulls drifting up around a character's head and fading, with dark motes: cursed, doomed, death mark, hex.",
|
|
4946
|
+
loops: true,
|
|
4947
|
+
tags: ["curse", "skulls", "status", "death", "hex", "debuff"]
|
|
4948
|
+
},
|
|
4949
|
+
{
|
|
4950
|
+
name: "debris_burst",
|
|
4951
|
+
description: "Chunks of rock or wood thrown out of a hit, arcing down under gravity.",
|
|
4952
|
+
loops: false,
|
|
4953
|
+
tags: ["debris", "rocks", "chunks", "impact", "break", "rubble"]
|
|
4954
|
+
},
|
|
4955
|
+
{
|
|
4956
|
+
name: "debuff_arrows",
|
|
4957
|
+
description: "Down-arrows sinking from above the head behind a character and fading: a stat debuff, weaken, slow or armour break.",
|
|
4958
|
+
loops: true,
|
|
4959
|
+
tags: ["debuff", "arrows", "status", "weaken", "curse"]
|
|
4960
|
+
},
|
|
4961
|
+
{
|
|
4962
|
+
name: "dust_kick",
|
|
4963
|
+
description: "Two small dust puffs pushing out sideways along the ground and breaking up: footsteps, landings, skids, dashes.",
|
|
4964
|
+
loops: false,
|
|
4965
|
+
tags: ["dust", "landing", "footstep", "ground", "puff", "jump"]
|
|
4966
|
+
},
|
|
4967
|
+
{
|
|
4968
|
+
name: "dust_puff",
|
|
4969
|
+
description: "A small drawn dust puff kicked up from the ground: footsteps, jumps, an item dropping, a skid start.",
|
|
4970
|
+
loops: false,
|
|
4971
|
+
tags: ["dust", "puff", "ground", "footstep", "cartoon"]
|
|
4972
|
+
},
|
|
4973
|
+
{
|
|
4974
|
+
name: "dust_trail",
|
|
4975
|
+
description: "Dust puffs rolling off the feet and trailing behind (to the left) as they break up. Loop for running, dashing, skidding.",
|
|
4976
|
+
loops: true,
|
|
4977
|
+
tags: ["dust", "trail", "run", "dash", "ground", "loop"]
|
|
4978
|
+
},
|
|
4979
|
+
{
|
|
4980
|
+
name: "ember_rise",
|
|
4981
|
+
description: "Glowing embers drifting up from the ground and winking out: fire, heat, burning.",
|
|
4982
|
+
loops: true,
|
|
4983
|
+
tags: ["embers", "fire", "sparks", "rising", "heat", "ambient"]
|
|
4984
|
+
},
|
|
4985
|
+
{
|
|
4986
|
+
name: "fire_blast",
|
|
4987
|
+
description: "A drawn explosion: a flash, round fire billows burning out into smoke puffs, a pressure ring. Noise version: fireball.",
|
|
4988
|
+
loops: false,
|
|
4989
|
+
tags: ["explosion", "fire", "blast", "boom", "smoke"]
|
|
4990
|
+
},
|
|
4991
|
+
{
|
|
4992
|
+
name: "fire_ring",
|
|
4993
|
+
description: "A ring of flickering flames standing on the ground around the anchor, embers rising: trapped in fire, burning ground, fire wall.",
|
|
4994
|
+
loops: true,
|
|
4995
|
+
tags: ["fire", "ring", "ground", "flames", "trap", "area"]
|
|
4996
|
+
},
|
|
4997
|
+
{
|
|
4998
|
+
name: "fire_wall",
|
|
4999
|
+
description: "A wide wall of drawn flame: seven tongues rising and flickering from one burning line. Fire walls, burning ground.",
|
|
5000
|
+
loops: true,
|
|
5001
|
+
tags: ["fire", "wall", "barrier", "burning", "loop"]
|
|
5002
|
+
},
|
|
5003
|
+
{
|
|
5004
|
+
name: "fireball",
|
|
5005
|
+
description: "An explosion's fire mass: a flash, billowing fire that burns out into smoke, a pressure ring.",
|
|
5006
|
+
loops: false,
|
|
5007
|
+
tags: ["explosion", "fire", "blast", "boom", "smoke"]
|
|
5008
|
+
},
|
|
5009
|
+
{
|
|
5010
|
+
name: "fireflies",
|
|
5011
|
+
description: "Glowing fireflies drifting slowly and blinking on and off around the anchor: night, forests, swamps, calm magic.",
|
|
5012
|
+
loops: true,
|
|
5013
|
+
tags: ["fireflies", "night", "nature", "ambient", "glow", "forest"]
|
|
5014
|
+
},
|
|
5015
|
+
{
|
|
5016
|
+
name: "fireworks",
|
|
5017
|
+
description: "A rocket streaking up and bursting into a falling shower of sparkles. Celebrations, victories, level-ups.",
|
|
5018
|
+
loops: false,
|
|
5019
|
+
tags: ["fireworks", "celebrate", "victory", "sparkle", "burst"]
|
|
5020
|
+
},
|
|
5021
|
+
{
|
|
5022
|
+
name: "flame_aura",
|
|
5023
|
+
description: "Drawn tongues of power rising behind a standing character, a ring at the feet, sparks. Noise version: aura_flames.",
|
|
5024
|
+
loops: true,
|
|
5025
|
+
tags: ["aura", "power", "buff", "flames", "charge", "rage"]
|
|
5026
|
+
},
|
|
5027
|
+
{
|
|
5028
|
+
name: "flame_jet",
|
|
5029
|
+
description: "A roaring jet of fire blasting sideways (to the right) from the anchor, widening into billows, sparks flying: flamethrower, fire breath.",
|
|
5030
|
+
loops: true,
|
|
5031
|
+
tags: ["fire", "jet", "breath", "flamethrower", "stream", "attack"]
|
|
5032
|
+
},
|
|
5033
|
+
{
|
|
5034
|
+
name: "flame_ring",
|
|
5035
|
+
description: "A ring of drawn flame tongues on the ground around the anchor, embers rising: trapped in fire. Noise version: fire_ring.",
|
|
5036
|
+
loops: true,
|
|
5037
|
+
tags: ["fire", "ring", "ground", "flames", "trap", "area"]
|
|
5038
|
+
},
|
|
5039
|
+
{
|
|
5040
|
+
name: "flame_tongue",
|
|
5041
|
+
description: "One big drawn cartoon flame, wide at the root, its tip whipping and curling: fire elementals, a burning spot, a cartoon blaze.",
|
|
5042
|
+
loops: true,
|
|
5043
|
+
tags: ["fire", "cartoon", "flame", "elemental", "loop"]
|
|
5044
|
+
},
|
|
5045
|
+
{
|
|
5046
|
+
name: "flamethrower",
|
|
5047
|
+
description: "A drawn jet of fire blasting right from the anchor, sparks flying: flamethrower, fire breath. Noise version: flame_jet.",
|
|
5048
|
+
loops: true,
|
|
5049
|
+
tags: ["fire", "jet", "breath", "flamethrower", "stream", "attack"]
|
|
5050
|
+
},
|
|
5051
|
+
{
|
|
5052
|
+
name: "flash_burst",
|
|
5053
|
+
description: "A hit flash: a white-hot core, rays and a ring punching outward. The heart of a hit spark.",
|
|
5054
|
+
loops: false,
|
|
5055
|
+
tags: ["hit", "impact", "flash", "spark", "punch", "rays"]
|
|
5056
|
+
},
|
|
5057
|
+
{
|
|
5058
|
+
name: "freeze_status",
|
|
5059
|
+
description: "Icy crust and cold tint on the sprite, ice crystals around the feet and glinting frost specks: frozen, chilled, slowed.",
|
|
5060
|
+
loops: true,
|
|
5061
|
+
tags: ["freeze", "ice", "status", "frost", "cold"]
|
|
5062
|
+
},
|
|
5063
|
+
{
|
|
5064
|
+
name: "frost_mist",
|
|
5065
|
+
description: "Low cold mist drifting sideways in dithered bands around the feet, a few frost glints: winter, swamps, graveyards, spooky.",
|
|
5066
|
+
loops: true,
|
|
5067
|
+
tags: ["mist", "fog", "frost", "cold", "ground", "ambient"]
|
|
5068
|
+
},
|
|
5069
|
+
{
|
|
5070
|
+
name: "grass_cut",
|
|
5071
|
+
description: "Grass clippings bursting up from a cut tuft and falling back around it. Cutting bushes and grass, trampling, foliage hits.",
|
|
5072
|
+
loops: false,
|
|
5073
|
+
tags: ["grass", "leaves", "cut", "bush", "environment", "nature"]
|
|
5074
|
+
},
|
|
5075
|
+
{
|
|
5076
|
+
name: "ground_cracks",
|
|
5077
|
+
description: "Glowing cracks race out along the ground from the impact point, cool down and fade. Ground slams, earth magic, lava.",
|
|
5078
|
+
loops: false,
|
|
5079
|
+
tags: ["cracks", "ground", "slam", "impact", "earth", "lava"]
|
|
5080
|
+
},
|
|
5081
|
+
{
|
|
5082
|
+
name: "heal_column",
|
|
5083
|
+
description: "A soft column of light standing on the ground, a ring at its base and sparkles rising.",
|
|
5084
|
+
loops: true,
|
|
5085
|
+
tags: ["heal", "holy", "buff", "light", "pillar", "column", "restore"]
|
|
5086
|
+
},
|
|
5087
|
+
{
|
|
5088
|
+
name: "heal_plus",
|
|
5089
|
+
description: "Green plus signs and motes float up off a body that flashes a glowing outline: healing received, regen ticks, potions.",
|
|
5090
|
+
loops: false,
|
|
5091
|
+
tags: ["heal", "plus", "regen", "potion", "restore", "ui"]
|
|
5092
|
+
},
|
|
5093
|
+
{
|
|
5094
|
+
name: "hit_star",
|
|
5095
|
+
description: "A sharp star flashing on the hit point with specks flying off. The classic hit spark for punches and blows.",
|
|
5096
|
+
loops: false,
|
|
5097
|
+
tags: ["hit", "impact", "spark", "star", "punch", "flash"]
|
|
5098
|
+
},
|
|
5099
|
+
{
|
|
5100
|
+
name: "holy_aura",
|
|
5101
|
+
description: "A golden radiance with light spikes behind a character, a glowing rim and streaks of light rising: blessed, divine, holy buff.",
|
|
5102
|
+
loops: true,
|
|
5103
|
+
tags: ["holy", "aura", "light", "buff", "divine", "blessing"]
|
|
5104
|
+
},
|
|
5105
|
+
{
|
|
5106
|
+
name: "hurt_flash",
|
|
5107
|
+
description: "The sprite blinks white twice, its outline lit hot: the classic took-damage flash. Recolour red for damage, gold for buffs.",
|
|
5108
|
+
loops: false,
|
|
5109
|
+
tags: ["hit", "hurt", "damage", "flash", "blink", "sprite"]
|
|
5110
|
+
},
|
|
5111
|
+
{
|
|
5112
|
+
name: "ice_shatter",
|
|
5113
|
+
description: "A flash and a ring of cold, then ice shards bursting out and tumbling down. Breaking a freeze, an ice spell's hit.",
|
|
5114
|
+
loops: false,
|
|
5115
|
+
tags: ["ice", "shatter", "frost", "break", "hit"]
|
|
5116
|
+
},
|
|
5117
|
+
{
|
|
5118
|
+
name: "ice_spikes",
|
|
5119
|
+
description: "A cluster of faceted ice spikes erupting from the ground under the anchor, holding, then sinking back: frost attacks, traps.",
|
|
5120
|
+
loops: false,
|
|
5121
|
+
tags: ["ice", "frost", "spikes", "ground", "attack", "freeze"]
|
|
5122
|
+
},
|
|
5123
|
+
{
|
|
5124
|
+
name: "item_glint",
|
|
5125
|
+
description: "A sharp four-point glint flashes now and then on an item's upper right, a smaller one lower left: shiny pickups, gems, loot.",
|
|
5126
|
+
loops: true,
|
|
5127
|
+
tags: ["glint", "shine", "pickup", "gem", "loot", "sparkle"]
|
|
5128
|
+
},
|
|
5129
|
+
{
|
|
5130
|
+
name: "landing_dust",
|
|
5131
|
+
description: "Drawn dust clouds rolling out flat to both sides of a landing and thinning away. Heavy landings, stomps, slides.",
|
|
5132
|
+
loops: false,
|
|
5133
|
+
tags: ["dust", "landing", "ground", "stomp", "cartoon"]
|
|
5134
|
+
},
|
|
5135
|
+
{
|
|
5136
|
+
name: "lava_pool",
|
|
5137
|
+
description: "A slowly churning pool of glowing lava on the ground with bubbles popping and embers: lava, molten ground, fire traps, hell.",
|
|
5138
|
+
loops: true,
|
|
5139
|
+
tags: ["lava", "fire", "pool", "ground", "molten", "hazard"]
|
|
5140
|
+
},
|
|
5141
|
+
{
|
|
5142
|
+
name: "leaf_fall",
|
|
5143
|
+
description: "Autumn leaves drifting down and swaying around the anchor: forests, autumn, wind, nature magic, a shaken tree.",
|
|
5144
|
+
loops: true,
|
|
5145
|
+
tags: ["leaves", "nature", "autumn", "weather", "ambient", "falling"]
|
|
5146
|
+
},
|
|
5147
|
+
{
|
|
5148
|
+
name: "level_up",
|
|
5149
|
+
description: "A pillar of light bursts up from the feet, halos rise up the body, up-arrows and sparkles float away: level up, rank up, power gain.",
|
|
5150
|
+
loops: false,
|
|
5151
|
+
tags: ["levelup", "pillar", "buff", "reward", "holy", "arrows"]
|
|
5152
|
+
},
|
|
5153
|
+
{
|
|
5154
|
+
name: "lightning_arcs",
|
|
5155
|
+
description: "Small jagged arcs of electricity crackling on and off around a body, with spark glints: shocked, charged, electrified.",
|
|
5156
|
+
loops: true,
|
|
5157
|
+
tags: ["lightning", "electric", "shock", "status", "crackle", "charge"]
|
|
5158
|
+
},
|
|
5159
|
+
{
|
|
5160
|
+
name: "magic_orb",
|
|
5161
|
+
description: "A glowing orb on the anchor with a tapered tail and motes streaming left: a magic missile flying right (+x).",
|
|
5162
|
+
loops: true,
|
|
5163
|
+
tags: ["projectile", "orb", "magic", "missile", "spell", "trail"]
|
|
5164
|
+
},
|
|
5165
|
+
{
|
|
5166
|
+
name: "muzzle_flash",
|
|
5167
|
+
description: "A bright three-pronged flash bursting right (+x) from the anchor, then sparks and a smoke wisp: gunshots, cannons, wands.",
|
|
5168
|
+
loops: false,
|
|
5169
|
+
tags: ["muzzle", "flash", "gun", "shot", "fire", "projectile"]
|
|
5170
|
+
},
|
|
5171
|
+
{
|
|
5172
|
+
name: "on_fire",
|
|
5173
|
+
description: "Small drawn flames licking up around a character, embers, a hot rim: on fire, burn damage. Noise version: burn_status.",
|
|
5174
|
+
loops: true,
|
|
5175
|
+
tags: ["burn", "fire", "status", "flames", "dot"]
|
|
5176
|
+
},
|
|
5177
|
+
{
|
|
5178
|
+
name: "orbit_motes",
|
|
5179
|
+
description: "Glowing motes and glints circling a character, passing in front and behind: magic charge, enchantment, wisps, a buff.",
|
|
5180
|
+
loops: true,
|
|
5181
|
+
tags: ["orbit", "motes", "magic", "buff", "arcane", "aura"]
|
|
5182
|
+
},
|
|
5183
|
+
{
|
|
5184
|
+
name: "poison_bubbles",
|
|
5185
|
+
description: "Green bubbles and specks rising off the body and popping, with a sickly pulsing rim on the sprite: poisoned, toxic, sick.",
|
|
5186
|
+
loops: true,
|
|
5187
|
+
tags: ["poison", "status", "bubbles", "toxic", "debuff"]
|
|
5188
|
+
},
|
|
5189
|
+
{
|
|
5190
|
+
name: "portal_swirl",
|
|
5191
|
+
description: "An upright swirling portal: churning core, glowing rim, orbiting motes.",
|
|
5192
|
+
loops: true,
|
|
5193
|
+
tags: ["portal", "void", "gate", "teleport", "magic", "arcane"]
|
|
5194
|
+
},
|
|
5195
|
+
{
|
|
5196
|
+
name: "rain_streaks",
|
|
5197
|
+
description: "Rain falling straight down as thin streaks, near and far, with little splashes on the ground: wet weather, storms, gloom.",
|
|
5198
|
+
loops: true,
|
|
5199
|
+
tags: ["rain", "weather", "water", "storm", "ambient"]
|
|
5200
|
+
},
|
|
5201
|
+
{
|
|
5202
|
+
name: "rocket_exhaust",
|
|
5203
|
+
description: "Round smoke puffs streaming left from an exhaust flame, flying right (+x): rockets, fire trails. Noise version: smoke_trail.",
|
|
5204
|
+
loops: true,
|
|
5205
|
+
tags: ["trail", "smoke", "rocket", "exhaust", "projectile", "missile"]
|
|
5206
|
+
},
|
|
5207
|
+
{
|
|
5208
|
+
name: "rune_circle",
|
|
5209
|
+
description: "A magic circle on the ground: rings, a hexagram and a rune band turning slowly, twinkling motes.",
|
|
5210
|
+
loops: true,
|
|
5211
|
+
tags: ["magic", "circle", "sigil", "summon", "ground", "arcane"]
|
|
5212
|
+
},
|
|
5213
|
+
{
|
|
5214
|
+
name: "rune_orbit",
|
|
5215
|
+
description: "Four glowing rune glyphs evenly circling a character's waist on a faint ring, in front and behind: spellcasting, enchant, arcane buff.",
|
|
5216
|
+
loops: true,
|
|
5217
|
+
tags: ["runes", "orbit", "arcane", "magic", "buff", "enchant"]
|
|
5218
|
+
},
|
|
5219
|
+
{
|
|
5220
|
+
name: "scorch_mark",
|
|
5221
|
+
description: "A charred patch on the ground with embers smouldering and flickering in it. Loop: after explosions, fire spells, burning.",
|
|
5222
|
+
loops: true,
|
|
5223
|
+
tags: ["scorch", "burn", "ground", "fire", "embers", "aftermath"]
|
|
5224
|
+
},
|
|
5225
|
+
{
|
|
5226
|
+
name: "shadow_aura",
|
|
5227
|
+
description: "Dark smoke wisps billowing up behind a character from a shadow pool, pale motes rising: dark power, curse, corruption.",
|
|
5228
|
+
loops: true,
|
|
5229
|
+
tags: ["shadow", "aura", "dark", "curse", "smoke", "debuff"]
|
|
5230
|
+
},
|
|
5231
|
+
{
|
|
5232
|
+
name: "shield_break",
|
|
5233
|
+
description: "A bubble shield flickering, then bursting in a flash into shards that fly out and fall. A broken guard or barrier.",
|
|
5234
|
+
loops: false,
|
|
5235
|
+
tags: ["shield", "break", "barrier", "bubble", "shatter"]
|
|
5236
|
+
},
|
|
5237
|
+
{
|
|
5238
|
+
name: "shockwave_ring",
|
|
5239
|
+
description: "A ring of force racing out along the ground with dust kicked up. A stomp, a landing, an impact.",
|
|
5240
|
+
loops: false,
|
|
5241
|
+
tags: ["impact", "stomp", "landing", "ground", "ring", "dust"]
|
|
5242
|
+
},
|
|
5243
|
+
{
|
|
5244
|
+
name: "sky_beam",
|
|
5245
|
+
description: "A pillar of light slams down onto the anchor, flares wide, rings out on the ground and thins away: smite, blessing, summon, revive.",
|
|
5246
|
+
loops: false,
|
|
5247
|
+
tags: ["beam", "holy", "light", "smite", "summon", "revive"]
|
|
5248
|
+
},
|
|
5249
|
+
{
|
|
5250
|
+
name: "slam_dust",
|
|
5251
|
+
description: "Round dust clouds billowing out and up from a ground impact: landings, slams, boulders. Noise version: crater_dust.",
|
|
5252
|
+
loops: false,
|
|
5253
|
+
tags: ["dust", "impact", "ground", "slam", "landing", "cloud"]
|
|
5254
|
+
},
|
|
5255
|
+
{
|
|
5256
|
+
name: "slash_trail",
|
|
5257
|
+
description: "A weapon swing: a bright crescent sweeping past the sprite, then dithering away. Sword, axe and claw swings.",
|
|
5258
|
+
loops: false,
|
|
5259
|
+
tags: ["slash", "swing", "sword", "melee", "hit", "arc"]
|
|
5260
|
+
},
|
|
5261
|
+
{
|
|
5262
|
+
name: "sleep_zzz",
|
|
5263
|
+
description: "Z letters drifting up and to the right off the head, growing as they rise and fading: asleep, resting, sleep status.",
|
|
5264
|
+
loops: true,
|
|
5265
|
+
tags: ["sleep", "zzz", "status", "rest", "idle"]
|
|
5266
|
+
},
|
|
5267
|
+
{
|
|
5268
|
+
name: "smoke_column",
|
|
5269
|
+
description: "A looping column of drawn smoke puffs rising, swaying, growing and fading. Chimneys, campfires, burning wrecks.",
|
|
5270
|
+
loops: true,
|
|
5271
|
+
tags: ["smoke", "column", "chimney", "fire", "loop"]
|
|
5272
|
+
},
|
|
5273
|
+
{
|
|
5274
|
+
name: "smoke_poof",
|
|
5275
|
+
description: "A drawn cartoon poof: round smoke puffs bursting out all round, rising and burning away. Vanish, teleport (z front hides the sprite).",
|
|
5276
|
+
loops: false,
|
|
5277
|
+
tags: ["smoke", "poof", "cartoon", "vanish", "teleport"]
|
|
5278
|
+
},
|
|
5279
|
+
{
|
|
5280
|
+
name: "smoke_puff",
|
|
5281
|
+
description: "A puff of smoke that billows up and thins away: dust clouds, poofs, after an explosion.",
|
|
5282
|
+
loops: false,
|
|
5283
|
+
tags: ["smoke", "puff", "poof", "dust", "cloud"]
|
|
5284
|
+
},
|
|
5285
|
+
{
|
|
5286
|
+
name: "smoke_trail",
|
|
5287
|
+
description: "A churning smoke plume streaming left from an exhaust flame on the anchor, flying right (+x): rockets; a fire trail in fire colour.",
|
|
5288
|
+
loops: true,
|
|
5289
|
+
tags: ["trail", "smoke", "rocket", "exhaust", "projectile", "missile"]
|
|
5290
|
+
},
|
|
5291
|
+
{
|
|
5292
|
+
name: "snowfall",
|
|
5293
|
+
description: "Snowflakes drifting down around the anchor, near ones as little crystals, far ones as dots.",
|
|
5294
|
+
loops: true,
|
|
5295
|
+
tags: ["snow", "weather", "winter", "cold", "ambient", "ice"]
|
|
5296
|
+
},
|
|
5297
|
+
{
|
|
5298
|
+
name: "soft_glow",
|
|
5299
|
+
description: "A soft pool of light on the ground, gently breathing: under a character, a pickup, a lamp.",
|
|
5300
|
+
loops: true,
|
|
5301
|
+
tags: ["glow", "light", "ground", "pool", "ambient", "aura"]
|
|
5302
|
+
},
|
|
5303
|
+
{
|
|
5304
|
+
name: "spark_burst",
|
|
5305
|
+
description: "Sparks flying out of a point and fading: hits, clashes, sword strikes, electricity.",
|
|
5306
|
+
loops: false,
|
|
5307
|
+
tags: ["sparks", "hit", "clash", "impact", "electric"]
|
|
5308
|
+
},
|
|
5309
|
+
{
|
|
5310
|
+
name: "sparks_shower",
|
|
5311
|
+
description: "Hot sparks spraying out of a point in a cone and arcing down: metal on metal, sword clashes, grinding, welding.",
|
|
5312
|
+
loops: false,
|
|
5313
|
+
tags: ["sparks", "metal", "clash", "hit", "shower", "impact"]
|
|
5314
|
+
},
|
|
5315
|
+
{
|
|
5316
|
+
name: "speed_lines",
|
|
5317
|
+
description: "Pale wind streaks rushing left past the anchor, behind and over a sprite moving right (+x): dashes, sprints, charges.",
|
|
5318
|
+
loops: true,
|
|
5319
|
+
tags: ["speed", "dash", "motion", "wind", "lines", "movement"]
|
|
5320
|
+
},
|
|
5321
|
+
{
|
|
5322
|
+
name: "sprite_dust",
|
|
5323
|
+
description: "The sprite's own pixels lifting off as dust and drifting away: disintegrate, fade out, teleport out (pair with the sprite's dissolve).",
|
|
5324
|
+
loops: false,
|
|
5325
|
+
tags: ["disintegrate", "dust", "teleport", "vanish", "sprite"]
|
|
5326
|
+
},
|
|
5327
|
+
{
|
|
5328
|
+
name: "square_sigil",
|
|
5329
|
+
description: "A ground seal of two counter-turning squares inside two rings, a glowing heart and motes rising: casting, warding, a trap glyph.",
|
|
5330
|
+
loops: true,
|
|
5331
|
+
tags: ["sigil", "magic", "circle", "ground", "ward", "arcane"]
|
|
5332
|
+
},
|
|
5333
|
+
{
|
|
5334
|
+
name: "star_burst",
|
|
5335
|
+
description: "Chunky cartoon stars pop out of the anchor in a ring and hang before blinking away: bonks, KOs, rewards, pickups.",
|
|
5336
|
+
loops: false,
|
|
5337
|
+
tags: ["stars", "cartoon", "burst", "hit", "reward", "pickup"]
|
|
5338
|
+
},
|
|
5339
|
+
{
|
|
5340
|
+
name: "steam_puffs",
|
|
5341
|
+
description: "A steam jet from the ground, round puffs that billow up and dissolve: vents, geysers, kettles. Noise version: steam_vent.",
|
|
5342
|
+
loops: true,
|
|
5343
|
+
tags: ["steam", "smoke", "vent", "geyser", "rising", "ambient"]
|
|
5344
|
+
},
|
|
5345
|
+
{
|
|
5346
|
+
name: "steam_vent",
|
|
5347
|
+
description: "A hissing jet of steam from a point on the ground, puffs that billow up, widen and dissolve: vents, geysers, kettles, hot springs.",
|
|
5348
|
+
loops: true,
|
|
5349
|
+
tags: ["steam", "smoke", "vent", "geyser", "rising", "ambient"]
|
|
5350
|
+
},
|
|
5351
|
+
{
|
|
5352
|
+
name: "stun_stars",
|
|
5353
|
+
description: "Three little stars circling evenly just above the head on a faint ring, passing in front and behind: stunned, dazed, dizzy.",
|
|
5354
|
+
loops: true,
|
|
5355
|
+
tags: ["stun", "dizzy", "stars", "status", "daze"]
|
|
5356
|
+
},
|
|
5357
|
+
{
|
|
5358
|
+
name: "swing_slash",
|
|
5359
|
+
description: "A drawn weapon swing that whips: the edge races round, then is reeled in from behind and crumbles. Swords, axes.",
|
|
5360
|
+
loops: false,
|
|
5361
|
+
tags: ["slash", "sword", "swing", "melee", "hit"]
|
|
5362
|
+
},
|
|
5363
|
+
{
|
|
5364
|
+
name: "teleport_in",
|
|
5365
|
+
description: "Motes swirling in and a column of light narrowing to a flash where the sprite appears. Arrivals, summons, respawns.",
|
|
5366
|
+
loops: false,
|
|
5367
|
+
tags: ["teleport", "arrive", "summon", "beam", "magic"]
|
|
5368
|
+
},
|
|
5369
|
+
{
|
|
5370
|
+
name: "thrust_streak",
|
|
5371
|
+
description: "A straight lance of light stabbing out from the sprite and fading forward, with speed lines. Spear, rapier, dash attacks.",
|
|
5372
|
+
loops: false,
|
|
5373
|
+
tags: ["thrust", "stab", "spear", "melee", "dash", "hit"]
|
|
5374
|
+
},
|
|
5375
|
+
{
|
|
5376
|
+
name: "torch_fire",
|
|
5377
|
+
description: "A drawn torch flame: three teardrop tongues whipping at the tips, a spark or two, lighting the sprite. Torches, braziers.",
|
|
5378
|
+
loops: true,
|
|
5379
|
+
tags: ["fire", "torch", "flame", "brazier", "loop"]
|
|
5380
|
+
},
|
|
5381
|
+
{
|
|
5382
|
+
name: "torch_flame",
|
|
5383
|
+
description: "A small flickering teardrop flame with a licking tongue and a spark or two: torches, candles, braziers, a burning spot.",
|
|
5384
|
+
loops: true,
|
|
5385
|
+
tags: ["fire", "flame", "torch", "light", "ambient"]
|
|
5386
|
+
},
|
|
5387
|
+
{
|
|
5388
|
+
name: "twinkles",
|
|
5389
|
+
description: "Little stars twinkling in and out around the anchor: magic, shine, enchantment, treasure.",
|
|
5390
|
+
loops: true,
|
|
5391
|
+
tags: ["sparkles", "twinkle", "magic", "shine", "stars", "ambient"]
|
|
5392
|
+
},
|
|
5393
|
+
{
|
|
5394
|
+
name: "vine_growth",
|
|
5395
|
+
description: "Twisting green vines sprouting from the ground and curling up around the anchor, leaves popping, then withering: entangle, roots, nature.",
|
|
5396
|
+
loops: false,
|
|
5397
|
+
tags: ["nature", "vines", "roots", "entangle", "growth", "ground"]
|
|
5398
|
+
},
|
|
5399
|
+
{
|
|
5400
|
+
name: "water_drips",
|
|
5401
|
+
description: "Water drops forming on a body and dripping off its lower half, wet glints: soaked, wet status, just out of water, melting.",
|
|
5402
|
+
loops: true,
|
|
5403
|
+
tags: ["water", "drips", "wet", "status", "soaked"]
|
|
5404
|
+
},
|
|
5405
|
+
{
|
|
5406
|
+
name: "water_ripple",
|
|
5407
|
+
description: "Thin rings rippling out on a water surface around the feet, with glints. Loop for wading, standing in water, puddles.",
|
|
5408
|
+
loops: true,
|
|
5409
|
+
tags: ["water", "ripple", "wading", "environment", "ambient", "puddle"]
|
|
5410
|
+
},
|
|
5411
|
+
{
|
|
5412
|
+
name: "water_splash",
|
|
5413
|
+
description: "A crown of droplets thrown up out of water and falling back, with ripples spreading on the surface. Landings, dives, stones.",
|
|
5414
|
+
loops: false,
|
|
5415
|
+
tags: ["water", "splash", "droplets", "ripple", "landing", "environment"]
|
|
5416
|
+
},
|
|
5417
|
+
{
|
|
5418
|
+
name: "wind_gusts",
|
|
5419
|
+
description: "Light streaks of wind sweeping left to right across the anchor with flecks of debris: wind, gales, speed, haste.",
|
|
5420
|
+
loops: true,
|
|
5421
|
+
tags: ["wind", "air", "gust", "weather", "speed", "ambient"]
|
|
5422
|
+
},
|
|
5423
|
+
{
|
|
5424
|
+
name: "zigzag_slash",
|
|
5425
|
+
description: "A slash along a path, not a circle: a zigzag (sharp corners, frantic cuts) or an S (smooth). Reshape it by patching its points.",
|
|
5426
|
+
loops: false,
|
|
5427
|
+
tags: ["slash", "zigzag", "sword", "axe", "melee", "hit", "combo", "path"]
|
|
5428
|
+
}
|
|
5429
|
+
];
|
|
5430
|
+
const PRESET_NAMES = new Set(VFX_PRESETS.map((preset) => preset.name));
|
|
5431
|
+
const PART_NAMES = new Map(VFX_PARTS.map((part) => [part.name, part]));
|
|
5432
|
+
const HEX = /^#[0-9a-f]{6}$/i;
|
|
5433
|
+
const SLIDERS = ["size", "amount", "speed", "start", "end"];
|
|
5434
|
+
function colourProblem(colour) {
|
|
5435
|
+
if (typeof colour === "string") {
|
|
5436
|
+
return VFX_RAMPS.includes(colour) ? null : `"${colour}" is not a colour ramp`;
|
|
5437
|
+
}
|
|
5438
|
+
if (!Array.isArray(colour) || colour.length < VFX_COLOURS_RANGE.min || colour.length > VFX_COLOURS_RANGE.max || !colour.every((one) => typeof one === "string" && HEX.test(one))) {
|
|
5439
|
+
return `a colour is a ramp's name or ${VFX_COLOURS_RANGE.min}–${VFX_COLOURS_RANGE.max} #rrggbb colours`;
|
|
5440
|
+
}
|
|
5441
|
+
return null;
|
|
5442
|
+
}
|
|
5443
|
+
function partProblem(part, at) {
|
|
5444
|
+
if (!PART_NAMES.has(part.part)) return `part ${at}: "${part.part}" is not a part`;
|
|
5445
|
+
if (part.colour !== void 0) {
|
|
5446
|
+
const problem = colourProblem(part.colour);
|
|
5447
|
+
if (problem) return `part ${at}: ${problem}`;
|
|
5448
|
+
}
|
|
5449
|
+
for (const slider of SLIDERS) {
|
|
5450
|
+
const value = part[slider];
|
|
5451
|
+
if (value === void 0) continue;
|
|
5452
|
+
const { min, max } = VFX_SLIDERS[slider];
|
|
5453
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < min || value > max) {
|
|
5454
|
+
return `part ${at}: ${slider} is between ${min} and ${max}`;
|
|
5455
|
+
}
|
|
5456
|
+
}
|
|
5457
|
+
if ((part.start ?? 0) >= (part.end ?? 1)) return `part ${at}: start comes before end`;
|
|
5458
|
+
return null;
|
|
5459
|
+
}
|
|
5460
|
+
function vfxProblem(ask) {
|
|
5461
|
+
const hasPreset = ask.preset !== void 0;
|
|
5462
|
+
const hasParts = ask.parts !== void 0;
|
|
5463
|
+
if (hasPreset === hasParts) return "an effect is a preset or parts, one of the two";
|
|
5464
|
+
if (hasPreset) return PRESET_NAMES.has(ask.preset) ? null : `"${ask.preset}" is not a preset`;
|
|
5465
|
+
const parts = ask.parts;
|
|
5466
|
+
if (parts.length < VFX_PARTS_RANGE.min || parts.length > VFX_PARTS_RANGE.max) {
|
|
5467
|
+
return `an effect has ${VFX_PARTS_RANGE.min} to ${VFX_PARTS_RANGE.max} parts`;
|
|
5468
|
+
}
|
|
5469
|
+
for (const [index, part] of parts.entries()) {
|
|
5470
|
+
const problem = partProblem(part, index + 1);
|
|
5471
|
+
if (problem) return problem;
|
|
5472
|
+
}
|
|
5473
|
+
return null;
|
|
5474
|
+
}
|
|
4680
5475
|
const MORE_KINDS = {
|
|
4681
5476
|
variant: "a variant",
|
|
4682
5477
|
edit: "an edit",
|
|
@@ -4688,7 +5483,8 @@ const MORE_KINDS = {
|
|
|
4688
5483
|
"terrain-animation": "an animated terrain set",
|
|
4689
5484
|
sfx: "a sound effect",
|
|
4690
5485
|
speech: "speech",
|
|
4691
|
-
music: "music"
|
|
5486
|
+
music: "music",
|
|
5487
|
+
vfx: "a visual effect"
|
|
4692
5488
|
};
|
|
4693
5489
|
const MORE_NAMES = { state: "creature-state", "animated-terrain": "terrain-animation" };
|
|
4694
5490
|
const UUID$4 = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
@@ -4725,7 +5521,10 @@ const OWN_OPTIONS = {
|
|
|
4725
5521
|
voice: ["speech"],
|
|
4726
5522
|
seconds: ["sfx"],
|
|
4727
5523
|
loop: ["sfx"],
|
|
4728
|
-
out: ["sfx", "speech", "music"]
|
|
5524
|
+
out: ["sfx", "speech", "music", "vfx"],
|
|
5525
|
+
preset: ["vfx"],
|
|
5526
|
+
part: ["vfx"],
|
|
5527
|
+
subject: ["vfx"]
|
|
4729
5528
|
};
|
|
4730
5529
|
const MORE_OPTIONS = {
|
|
4731
5530
|
source: { type: "string", value: "<image id>", description: "a change's picture: an image of the account's, such as a generation's or a sprite's" },
|
|
@@ -4764,7 +5563,23 @@ const MORE_OPTIONS = {
|
|
|
4764
5563
|
voice: { type: "string", value: "<voice>", description: `the voice speech is said in: ${VOICES.join(", ")}` },
|
|
4765
5564
|
seconds: { type: "string", value: "<seconds>", description: `a sound effect's length, ${SFX_SECONDS.min} to ${SFX_SECONDS.max}` },
|
|
4766
5565
|
loop: { type: "boolean", description: "a sound effect that loops seamlessly" },
|
|
4767
|
-
out: {
|
|
5566
|
+
out: {
|
|
5567
|
+
type: "string",
|
|
5568
|
+
value: "<path>",
|
|
5569
|
+
description: "where a sound is written, pixelvibing/sounds/ unless given; the folder an effect's frames are written in, pixelvibing/effects/ unless given"
|
|
5570
|
+
},
|
|
5571
|
+
preset: { type: "string", value: "<preset>", description: `a ready visual effect: ${VFX_PRESETS.map((preset) => preset.name).join(", ")}` },
|
|
5572
|
+
part: {
|
|
5573
|
+
type: "string",
|
|
5574
|
+
multiple: true,
|
|
5575
|
+
value: "<part>[,colour=<ramp|#rrggbb/#rrggbb…>][,size=<n>][,amount=<n>][,speed=<n>][,start=<0-1>][,end=<0-1>]",
|
|
5576
|
+
description: `a part of a visual effect, given once for each, up to six: ${VFX_PARTS.map((part) => part.name).join(", ")}; colour is a ramp (${VFX_RAMPS.join(", ")}) or colours dark to light`
|
|
5577
|
+
},
|
|
5578
|
+
subject: {
|
|
5579
|
+
type: "string",
|
|
5580
|
+
value: "<image id>",
|
|
5581
|
+
description: "the image a visual effect is drawn around, an image of the account's of at most 256 px a side"
|
|
5582
|
+
}
|
|
4768
5583
|
};
|
|
4769
5584
|
function wholeOf(args, name, least, most) {
|
|
4770
5585
|
const given2 = stringOf(args, name);
|
|
@@ -4780,6 +5595,21 @@ function fractionOf(args, name, least, most) {
|
|
|
4780
5595
|
if (!Number.isFinite(value) || value < least || value > most) throw new CliError(`--${name} is a number from ${least} to ${most}`);
|
|
4781
5596
|
return value;
|
|
4782
5597
|
}
|
|
5598
|
+
function partOf(given2) {
|
|
5599
|
+
const [name, ...settings] = given2.split(",").map((each) => each.trim());
|
|
5600
|
+
const part = { part: name ?? "" };
|
|
5601
|
+
for (const setting of settings) {
|
|
5602
|
+
const [key, value] = setting.split("=").map((each) => each.trim());
|
|
5603
|
+
if (value === void 0 || value === "") throw new CliError(`--part's ${key} is given as ${key}=<value>`);
|
|
5604
|
+
if (key === "colour" || key === "color") part.colour = value.includes("/") || value.startsWith("#") ? value.split("/") : value;
|
|
5605
|
+
else if (key === "size" || key === "amount" || key === "speed" || key === "start" || key === "end") {
|
|
5606
|
+
const number2 = Number(value);
|
|
5607
|
+
if (!Number.isFinite(number2)) throw new CliError(`--part's ${key} is a number`);
|
|
5608
|
+
part[key] = number2;
|
|
5609
|
+
} else throw new CliError(`--part takes colour, size, amount, speed, start and end, not ${key}`);
|
|
5610
|
+
}
|
|
5611
|
+
return part;
|
|
5612
|
+
}
|
|
4783
5613
|
const given = (args, name) => args.values[name] !== void 0;
|
|
4784
5614
|
function ownOnly(args, kind) {
|
|
4785
5615
|
for (const [option, kinds] of Object.entries(OWN_OPTIONS)) {
|
|
@@ -4887,6 +5717,18 @@ async function moreRequest(context, kind, args, instruction, reference) {
|
|
|
4887
5717
|
words: `${words} from ${terrain}${instruction === "" ? "" : `: ${instruction}`}`
|
|
4888
5718
|
};
|
|
4889
5719
|
}
|
|
5720
|
+
if (kind === "vfx") {
|
|
5721
|
+
const preset = stringOf(args, "preset");
|
|
5722
|
+
const given2 = args.values.part;
|
|
5723
|
+
const parts = given2 === void 0 ? void 0 : (Array.isArray(given2) ? given2 : [given2]).map((each) => partOf(String(each)));
|
|
5724
|
+
const ask = { ...preset === void 0 ? {} : { preset }, ...parts === void 0 ? {} : { parts } };
|
|
5725
|
+
const problem = vfxProblem(ask);
|
|
5726
|
+
if (problem !== null) throw new CliError(`${problem}: give --preset, or --part once for each part`);
|
|
5727
|
+
const subject = stringOf(args, "subject");
|
|
5728
|
+
if (subject !== void 0 && !UUID$4.test(subject)) throw new CliError("--subject is an image's id");
|
|
5729
|
+
const what = preset ?? (parts ?? []).map((each) => each.part).join(", ");
|
|
5730
|
+
return { kind, body: { ...ask, ...subject === void 0 ? {} : { subject } }, words: `${words}: ${what.replace(/_/g, " ")}` };
|
|
5731
|
+
}
|
|
4890
5732
|
if (kind === "speech") {
|
|
4891
5733
|
const text2 = stringOf(args, "text")?.trim() ?? "";
|
|
4892
5734
|
const voice = stringOf(args, "voice");
|
|
@@ -4923,7 +5765,7 @@ async function withPatch(platform, project, request) {
|
|
|
4923
5765
|
return { ...request, body: { ...body, upperLabel, lowerLabel }, images: { patch } };
|
|
4924
5766
|
}
|
|
4925
5767
|
function landsInArt(kind, request) {
|
|
4926
|
-
if (SOUNDS.has(kind) || kind === "terrain" || kind === "terrain-animation") return false;
|
|
5768
|
+
if (SOUNDS.has(kind) || kind === "terrain" || kind === "terrain-animation" || kind === "vfx") return false;
|
|
4927
5769
|
return !(KEPT_WITH_A_SPRITE.has(kind) && request.body.into !== void 0);
|
|
4928
5770
|
}
|
|
4929
5771
|
async function finishedMore(context, platform, local, kind, job, args) {
|
|
@@ -4948,6 +5790,33 @@ async function finishedMore(context, platform, local, kind, job, args) {
|
|
|
4948
5790
|
}
|
|
4949
5791
|
return { value: { job: job.id, sounds: written }, words: `Wrote ${written.join(", ")}.` };
|
|
4950
5792
|
}
|
|
5793
|
+
if (kind === "vfx") {
|
|
5794
|
+
const frames = job.result?.images ?? [];
|
|
5795
|
+
if (frames.length === 0) throw new CliError("the job is done, and made no frame");
|
|
5796
|
+
const out = stringOf(args, "out");
|
|
5797
|
+
const folder = out === void 0 ? join(artOf(local), "effects", `vfx-${job.id.slice(0, 8)}`) : resolve(context.io.cwd, out);
|
|
5798
|
+
const data = job.result?.data ?? {};
|
|
5799
|
+
const names = frames.map((_, index) => `frame-${String(index + 1).padStart(2, "0")}.png`);
|
|
5800
|
+
const timings = {
|
|
5801
|
+
frames: names.map((file, index) => ({ file, ms: data.frames?.[index]?.ms ?? null })),
|
|
5802
|
+
width: data.width ?? null,
|
|
5803
|
+
height: data.height ?? null,
|
|
5804
|
+
anchor: data.anchor ?? null,
|
|
5805
|
+
loops: data.loops === true
|
|
5806
|
+
};
|
|
5807
|
+
const files = [];
|
|
5808
|
+
for (const [index, frame] of frames.entries()) files.push([join(folder, names[index]), await platform.web.imageBytes(frame)]);
|
|
5809
|
+
files.push([join(folder, "effect.json"), new TextEncoder().encode(`${JSON.stringify(timings, null, 2)}
|
|
5810
|
+
`)]);
|
|
5811
|
+
for (const [target, bytes] of files) {
|
|
5812
|
+
if (out === void 0) await artBoundary(local).writeFileReplacing(target, bytes);
|
|
5813
|
+
else {
|
|
5814
|
+
await mkdir(dirname(target), { recursive: true });
|
|
5815
|
+
await writeFile(target, bytes);
|
|
5816
|
+
}
|
|
5817
|
+
}
|
|
5818
|
+
return { value: { job: job.id, effect: folder, frames: frames.length }, words: `Wrote ${frames.length} frames and effect.json into ${folder}.` };
|
|
5819
|
+
}
|
|
4951
5820
|
if (kind === "terrain" || kind === "terrain-animation") {
|
|
4952
5821
|
return {
|
|
4953
5822
|
value: { job: job.id },
|
|
@@ -5203,8 +6072,8 @@ async function followed(context, platform, jobId) {
|
|
|
5203
6072
|
}
|
|
5204
6073
|
const generate = {
|
|
5205
6074
|
name: "generate",
|
|
5206
|
-
summary: "Generate an asset, a change of a picture, terrain or a
|
|
5207
|
-
usage: "<object|creature|concept-art|icons|ui|variant|edit|animate|pose|rotate|state|terrain|terrain-animation|sfx|speech|music> [--instruction <text>] [--source <image id>] [--state <state>] [--into <sprite> --name <name>] [--upper <ground> --lower <ground>] [--terrain <asset>] [--text <text> --voice <voice>] [--out <
|
|
6075
|
+
summary: "Generate an asset, a change of a picture, terrain, a sound or a visual effect on the platform, then pull it, keep it or write it",
|
|
6076
|
+
usage: "<object|creature|concept-art|icons|ui|variant|edit|animate|pose|rotate|state|terrain|terrain-animation|sfx|speech|music|vfx> [--instruction <text>] [--source <image id>] [--state <state>] [--into <sprite> --name <name>] [--upper <ground> --lower <ground>] [--terrain <asset>] [--text <text> --voice <voice>] [--preset <preset> | --part <part>…] [--subject <image id>] [--out <path>] [--size <c>x<r>|<w>x<h>|<px>] [--transparent] [--pixel-art] [--tiles <c>x<r>] [--tag <tag>]… [--mask <png>] [--names <list>] [--purpose skill|item] [--sketch <json>] [--reference <png>]… [--no-enrich] [--yes]",
|
|
5208
6077
|
options: {
|
|
5209
6078
|
instruction: {
|
|
5210
6079
|
type: "string",
|
|
@@ -5739,7 +6608,7 @@ const move = {
|
|
|
5739
6608
|
);
|
|
5740
6609
|
}
|
|
5741
6610
|
};
|
|
5742
|
-
const version = "0.
|
|
6611
|
+
const version = "0.55.0";
|
|
5743
6612
|
const manifest = {
|
|
5744
6613
|
version
|
|
5745
6614
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pixelvibing/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.55.0",
|
|
4
4
|
"description": "PixelVibing: pixel art game assets from the command line, and the Skills that teach coding assistants to make them.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://pixelvibing.com",
|
|
@@ -48,6 +48,7 @@ Load the Skill for the kind before you generate it:
|
|
|
48
48
|
| another of a result, a change to it, setting it in motion, a pose, turning it, a creature's state | `pixelvibing-change` |
|
|
49
49
|
| ground that joins everywhere, or ground that moves | `pixelvibing-terrain` |
|
|
50
50
|
| a sound effect, a line of speech, a piece of music | `pixelvibing-sound` |
|
|
51
|
+
| a spell, a hit, an explosion, an aura: an animated visual effect | `pixelvibing-effect` |
|
|
51
52
|
|
|
52
53
|
## What every generation shares
|
|
53
54
|
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pixelvibing-effect
|
|
3
|
+
description: Generate visual effects for this game on PixelVibing — an animated spell, hit, explosion, aura, shield or portal, from a ready preset or built from parts, around one of the game's sprites or on its own — with pixel-vibing-cli or the PixelVibing MCP server, written as frames with their timings. Use when the game needs an effect.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Visual effect
|
|
7
|
+
|
|
8
|
+
Load the `pixelvibing` Skill first: who is signed in, the limit.
|
|
9
|
+
|
|
10
|
+
An effect is drawn from a ready **preset** or from tuned **parts**, never from words: pick the
|
|
11
|
+
ones that fit what the person describes. `pixel-vibing-cli generate --help` lists every preset
|
|
12
|
+
and part by name; read it rather than guessing one.
|
|
13
|
+
|
|
14
|
+
## With the CLI
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pixel-vibing-cli generate vfx --preset <preset> [--subject <image id>] [--out <folder>] --yes --json
|
|
18
|
+
pixel-vibing-cli generate vfx --part <part>[,colour=<ramp>][,size=<n>][,start=<0-1>][,end=<0-1>] --part <part> … [--subject <image id>] --yes --json
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Each writes the effect's frames, `frame-01.png` on, and `effect.json` — each frame's time in
|
|
22
|
+
milliseconds, the effect's size, the point it is anchored at and whether it loops — into
|
|
23
|
+
`pixelvibing/effects/`, or the folder `--out` names, once its job is done.
|
|
24
|
+
|
|
25
|
+
## With the MCP server
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{ "project": "<id>", "preset": "explosion", "max_percent": 30 }
|
|
29
|
+
{ "project": "<id>", "parts": [{ "part": "fire_blast", "colour": "frost" }, { "part": "shockwave_ring", "start": 0.1, "end": 0.6 }], "max_percent": 30 }
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
with the tool `generate_effect`. `get_job` follows the job and, once done, names its frames in
|
|
33
|
+
order, with each frame's time in `data.frames`.
|
|
34
|
+
|
|
35
|
+
## Choosing it
|
|
36
|
+
|
|
37
|
+
- **A preset** is a whole effect: `explosion`, `lightning_strike`, `portal`, `heal_pillar`,
|
|
38
|
+
`magic_circle`, `slash_arc`, `shield_bubble` and more. One preset, nothing else.
|
|
39
|
+
- **Parts** build one: one to six, such as `fire_blast` with `shockwave_ring` and `debris_burst`
|
|
40
|
+
for a big explosion. Each takes:
|
|
41
|
+
- `colour`: a ramp's name (arcane, blood, electric, fire, frost, holy, nature, poison, shadow,
|
|
42
|
+
smoke, water), or 2 to 12 colours dark to light, to match the game's palette; on the CLI,
|
|
43
|
+
colours are joined by `/`, as `colour=#1a0b2e/#7f3ffb/#e8d5ff`.
|
|
44
|
+
- `size`, `amount` and `speed`: 1 is the part as made.
|
|
45
|
+
- `start` and `end`: when it plays, as fractions of a one-shot — a flash at 0, the
|
|
46
|
+
shockwave from 0.1, debris from 0.15.
|
|
47
|
+
- **A subject** is a sprite the effect is drawn around, an image of the account's of at most
|
|
48
|
+
256 px a side: an aura, a shield, a heal on a character.
|
|
49
|
+
- **What it costs.** Every effect costs the same, a preset's or six parts'.
|
|
50
|
+
- **Where it goes.** The game plays the frames at their times, placed at the anchor; copy them
|
|
51
|
+
into the game's own folder when that is where the engine looks. Effects are not part of the
|
|
52
|
+
map, the export or the sealed container.
|
|
@@ -22,7 +22,9 @@ pixel-vibing-cli generate icons --names "<name>,<name>,..." --purpose <skill|ite
|
|
|
22
22
|
"icon_size": 32, "instruction": "<theme>", "enrich": false, "max_percent": 40 }
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
`get_job` follows the job; then `pixel-vibing-cli pull <job id>` lands each icon
|
|
25
|
+
`get_job` follows the job; then `pixel-vibing-cli pull <job id>` lands each icon as pixel art, and
|
|
26
|
+
`pull <job id> --hd` lands them as drawn (HD), beside them; `get_asset` with `"version": "hd"` reads
|
|
27
|
+
them so.
|
|
26
28
|
|
|
27
29
|
## Writing it
|
|
28
30
|
|
|
@@ -30,7 +30,10 @@ The layout goes in the call itself, as `canvas` and `shapes`, with the same fiel
|
|
|
30
30
|
"reference_images": ["<image id>"], "max_percent": 40 }
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
`get_job` follows the job; then `pixel-vibing-cli pull <job id>` lands each piece
|
|
33
|
+
`get_job` follows the job; then `pixel-vibing-cli pull <job id>` lands each piece as pixel art, and
|
|
34
|
+
`pull <job id> --hd` lands them as drawn (HD), beside them; `get_asset` with `"version": "hd"` reads
|
|
35
|
+
them so. A UI drawn with `--no-pixel-art` is HD alone. A shape nothing was drawn for is named in the
|
|
36
|
+
result's `missing`: generate the UI again for it rather than retrying the whole set blind.
|
|
34
37
|
|
|
35
38
|
## Writing it
|
|
36
39
|
|