create-debuff 0.2.0 → 0.3.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-debuff",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Scaffold a new debuff game: npm create debuff",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"game",
|
|
@@ -58,7 +58,6 @@
|
|
|
58
58
|
"node": ">=22"
|
|
59
59
|
},
|
|
60
60
|
"publishConfig": {
|
|
61
|
-
"access": "public"
|
|
62
|
-
"provenance": false
|
|
61
|
+
"access": "public"
|
|
63
62
|
}
|
|
64
63
|
}
|
|
@@ -7,7 +7,8 @@ This is a 2D browser game made with **debuff** (https://debuff.jp). These notes
|
|
|
7
7
|
| Command | What it does |
|
|
8
8
|
| --- | --- |
|
|
9
9
|
| `npm run dev` | Dev server with hot reload. Press <kbd>`</kbd> in the game for the dev panel |
|
|
10
|
-
| `
|
|
10
|
+
| `npx debuff test` | Runs the logic tests in `tests/`: the real game in Node, no browser, in milliseconds. Run it after every change. `-t <text>` runs matching tests; `--json` for tools |
|
|
11
|
+
| `npm test` | Logic tests, then `scenarios/*.json` in a headless browser with screenshot comparison |
|
|
11
12
|
| `npx debuff fuzz` | Plays the game with random input for a few minutes. Each crash, stuck state or back-and-forth loop is saved as a scenario in `.debuff/fuzz/` that replays it exactly |
|
|
12
13
|
| `npm run build` | Type-checks, builds to `dist/` and prints a size report. Fails if dev-only code leaks |
|
|
13
14
|
| `npm run assets` | Regenerates the asset manifest and lists unused assets |
|
|
@@ -18,7 +19,8 @@ This is a 2D browser game made with **debuff** (https://debuff.jp). These notes
|
|
|
18
19
|
|
|
19
20
|
- `src/main.ts`: the game, which creates `new Game(...)`, scenes (`update`, `render`, `inspect`) and params.
|
|
20
21
|
- `assets/`: source art, audio and data. The manifest and atlases are generated from it, and only files the game references ship. Don't edit generated files.
|
|
21
|
-
- `
|
|
22
|
+
- `tests/`: logic tests (`*.test.ts`, `keiko`). They boot the real game headless, drive input, time and params, and assert on `inspect()` state. Add one for each mechanic you build. Guide: https://debuff.jp/docs/testing
|
|
23
|
+
- `scenarios/`: JSON scripts with screenshot captures, run in a real browser. Add one when a change is visual.
|
|
22
24
|
- `.mcp.json`: connects the debuff MCP server, so you can play and inspect the running game.
|
|
23
25
|
|
|
24
26
|
## Work loop
|
|
@@ -26,7 +28,7 @@ This is a 2D browser game made with **debuff** (https://debuff.jp). These notes
|
|
|
26
28
|
1. **Look before you change.** With the MCP server, call `game_start`. If the human has the game open under `npm run dev`, you attach to it live, so read `game_notes` first: they're moments the human flagged, and `game_replay` with a note's tick saves a scenario that reproduces it. Then use `game_state`, `game_capture` and `game_errors`.
|
|
27
29
|
2. **Tune before you rewrite.** Feel values (speed, gravity, timings) live in `game.params.define(...)`. Change them with `game_set_param` and show the human before editing code. The human can undo your changes in the dev panel.
|
|
28
30
|
3. **Make the smallest change that works.** Keep dev-only code inside `if (import.meta.env.DEV)`.
|
|
29
|
-
4. **Verify.** Run `npm test` and `npm run build`, and check `game_errors` is empty. Before calling a feature done, run `npx debuff fuzz`: to fix a finding,
|
|
31
|
+
4. **Verify.** Run `npm test` and `npm run build`, and check `game_errors` is empty. Before calling a feature done, run `npx debuff fuzz`: to fix a finding, turn its file into a logic test with `game.replay(".debuff/fuzz/<file>")`, watch it fail, fix, and keep the test. Errors come back explained: `kind` says whose bug it is (`your-code`, `debuff-usage`, `debuff-bug`, `outside`) and `where` gives the source line.
|
|
30
32
|
5. **Report** what changed, what you verified, and anything you couldn't check.
|
|
31
33
|
|
|
32
34
|
## Rules
|
|
@@ -7,17 +7,18 @@
|
|
|
7
7
|
"dev": "debuff dev",
|
|
8
8
|
"build": "tsc --noEmit && debuff build",
|
|
9
9
|
"assets": "debuff assets",
|
|
10
|
-
"test": "debuff scenario"
|
|
10
|
+
"test": "debuff test && debuff scenario"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@debuffjp/audio": "0.
|
|
14
|
-
"@debuffjp/core": "0.
|
|
15
|
-
"@debuffjp/input": "0.
|
|
16
|
-
"@debuffjp/juice": "0.
|
|
13
|
+
"@debuffjp/audio": "0.3.0",
|
|
14
|
+
"@debuffjp/core": "0.3.0",
|
|
15
|
+
"@debuffjp/input": "0.3.0",
|
|
16
|
+
"@debuffjp/juice": "0.3.0"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
|
-
"@debuffjp/cli": "0.
|
|
19
|
+
"@debuffjp/cli": "0.3.0",
|
|
20
20
|
"typescript": "^7.0.2",
|
|
21
|
-
"@debuffjp/dev": "0.
|
|
21
|
+
"@debuffjp/dev": "0.3.0",
|
|
22
|
+
"keiko": "0.1.0"
|
|
22
23
|
}
|
|
23
24
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { describe, expect, play, test } from "keiko";
|
|
2
|
+
|
|
3
|
+
/** What the play scene's inspect() returns (src/main.ts). */
|
|
4
|
+
interface State {
|
|
5
|
+
player: { x: number; vx: number };
|
|
6
|
+
pops: number;
|
|
7
|
+
particles: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
describe("movement", () => {
|
|
11
|
+
test("buff starts in the middle, standing still", async () => {
|
|
12
|
+
const game = await play<State>();
|
|
13
|
+
expect(game.scene).toBe("play");
|
|
14
|
+
expect(game.state.player).toEqual({ x: 160, vx: 0 });
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("reaches top speed within a quarter second", async () => {
|
|
18
|
+
const game = await play<State>();
|
|
19
|
+
game.press("ArrowRight");
|
|
20
|
+
const steps = game.until((s) => s.player.vx >= 140, { within: "1s" });
|
|
21
|
+
expect(steps).toBeAtMost(15);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("never leaves the screen", async () => {
|
|
25
|
+
const game = await play<State>();
|
|
26
|
+
game.press("ArrowLeft");
|
|
27
|
+
game.during("3s", (s) => s.player.x >= 8, "buff stays on screen");
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test.sweep("stops in speed ÷ acceleration after letting go", { "player.speed": [80, 140, 300] }, (game) => {
|
|
31
|
+
game.hold("ArrowRight", "0.5s");
|
|
32
|
+
const steps = game.until((s) => s.player.vx === 0, { within: "1s" });
|
|
33
|
+
const expected = Number(game.param("player.speed")) / Number(game.param("player.accel"));
|
|
34
|
+
expect(steps * game.stepSeconds).toBeCloseTo(expected, game.stepSeconds);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe("popping", () => {
|
|
39
|
+
test("Space pops once per press and makes particles", async () => {
|
|
40
|
+
const game = await play<State>();
|
|
41
|
+
game.tap("Space").wait(4);
|
|
42
|
+
expect(game.state.pops).toBe(1);
|
|
43
|
+
expect(game.state.particles).toBeAbove(0);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test.deterministic("five seconds of play are reproducible", (game) => {
|
|
48
|
+
game.hold("ArrowRight", "1s").tap("Space").hold("ArrowLeft", "2s").wait("2s");
|
|
49
|
+
});
|