@netforge/template-cards 0.0.0-stage → 0.2.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/AGENTS.md +71 -0
- package/CLAUDE.md +11 -0
- package/LICENSE +21 -0
- package/NOTICE +28 -0
- package/README.md +82 -2
- package/client/decor.ts +8 -0
- package/client/index.html +11 -0
- package/client/main.ts +688 -0
- package/client/names.ts +58 -0
- package/llms-full.txt +243 -0
- package/llms.txt +94 -0
- package/package.json +79 -3
- package/src/game.ts +86 -0
- package/src/manifest.ts +44 -0
- package/src/store.ts +24 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Extending INKWRIT
|
|
2
|
+
|
|
3
|
+
INKWRIT is the card battler template of the Net Forge game engine. The template already has the duel, turns and the turn timer, the server-side shuffle, hidden hands, guards, rush, the draft, the AI, netcode, rendering, touch controls, earn and the store. You change the game by changing declarations. Read `llms.txt` for every field, and `llms-full.txt` for the complete worked example.
|
|
4
|
+
|
|
5
|
+
## The one file to edit
|
|
6
|
+
|
|
7
|
+
- `src/game.ts` is the whole game. Change declarations in place.
|
|
8
|
+
- `src/names.json`, next to it, holds the title and every label players read. Cards go in `"weapons"` (`{ name, line }`), plus upgrades, earn rules and store items.
|
|
9
|
+
- Everything else (`src/manifest.ts`, `src/store.ts`, `client/`, `test/`, `scripts/`) is template machinery. Do not edit it.
|
|
10
|
+
|
|
11
|
+
## Imports
|
|
12
|
+
|
|
13
|
+
`src/game.ts` imports only from `@netforge/engine`. The shipped game uses:
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { card, cardTable, earn, game, store, upgrade } from "@netforge/engine";
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Declarations
|
|
20
|
+
|
|
21
|
+
- `game({ template: "cards", style, places, players: { prefab, max } })`, with `players.max` at most 2.
|
|
22
|
+
- `cardTable({ health, startHand, handMax, boardMax, manaMax, turnSeconds, draftSeconds, duels, starter, ai })`, exactly one. `ai` is 1 to 12 tiers of `{ health, smart }`; duel n against the AI uses tier n.
|
|
23
|
+
- `card(id, { model, kind: "unit" | "spell", cost, attack, health, guard?, rush?, effect? })`, 4 to 40 of them. Effects have kind `"damage"`, `"heal"`, `"draw"` or `"buff"` and target `"enemy"`, `"enemy_hero"`, `"all_enemies"`, `"own_hero"`, `"own_unit"` or `"all_own"`.
|
|
24
|
+
- `upgrade(id, { stat, step, maxLevel })` with stat `maxHealth`, `might`, `haste` or `amount` only.
|
|
25
|
+
- `earn.rule(id, { when, n, pay, oncePer: "run" })`. Events: `duel.won` (against a person, never a forfeit), `ai.beaten` and `run.won`.
|
|
26
|
+
- `earn.stake({ min, max, multiple })`, exactly one, on one top-level line.
|
|
27
|
+
- `store.sku(id, { coins, grants? })`.
|
|
28
|
+
|
|
29
|
+
## Rules the build enforces
|
|
30
|
+
|
|
31
|
+
- Ids are lower_snake_case.
|
|
32
|
+
- Every game declares at least one `earn.rule` and exactly one `earn.stake`. `"stake"` is reserved.
|
|
33
|
+
- The starter deck has 8 to 30 cards, names declared cards, and holds at least one unit and cards costing 2 or less.
|
|
34
|
+
- A unit has health 1 or more. A spell has an effect.
|
|
35
|
+
- No `weapon(...)` in a card game.
|
|
36
|
+
- Asset ids are library ids such as `"style.lowpoly.frog"`, never URLs or paths. Keep `game({ style })` as the creator picked it.
|
|
37
|
+
- Two playtest players must decide duels and fire an earn rule within 4 minutes.
|
|
38
|
+
- Keep the SKUs the client is wired to at their prices: `thick_vellum`, `sharp_nib`, `quick_ink`, `spare_page`, `skin_vermilion`, `skin_gold_leaf` and `bound_folio`.
|
|
39
|
+
- Nothing in `src/game.ts` can reveal deck order or another player's hand.
|
|
40
|
+
|
|
41
|
+
## Validate
|
|
42
|
+
|
|
43
|
+
- With the MCP server (`npx -y @netforge/mcp`), call `netforge_validate_game` on `src/game.ts`. It returns one actionable line per problem.
|
|
44
|
+
- In this package, run the tests with vitest: `test/harness.test.ts` and `test/store.test.ts`.
|
|
45
|
+
|
|
46
|
+
## Playtest
|
|
47
|
+
|
|
48
|
+
- With the MCP server, call `netforge_run_playtest`.
|
|
49
|
+
- In code, import the game module and run the headless harness:
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
import { engine } from "@netforge/engine";
|
|
53
|
+
import "./src/game.js";
|
|
54
|
+
|
|
55
|
+
const report = await engine.test.run({ ticks: 30 * 240, bots: 2, seed: 1 });
|
|
56
|
+
console.log(report.kills, report.deaths, report.earned, report.rules?.checks);
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`bots: 2` plays two people; `bots: 1` plays one person against the AI. The report has duels won (kills) and lost (deaths), earn by rule, softlock, ms per tick and the genre's checks under `rules.checks`.
|
|
60
|
+
|
|
61
|
+
## Play locally
|
|
62
|
+
|
|
63
|
+
In a folder with `netnet.json` (`{"template":"cards"}`) and `src/game.ts`, with `@netforge/kit-play` and `@netforge/engine` installed, run `npx kit-play`. It opens http://127.0.0.1:4663 with play money. The MCP tool `netforge_play_locally` gives the same steps.
|
|
64
|
+
|
|
65
|
+
## Do not
|
|
66
|
+
|
|
67
|
+
- Do not write renderers, netcode, physics or AI. The template has them.
|
|
68
|
+
- Do not reference assets by URL or file path.
|
|
69
|
+
- Do not import anything other than `@netforge/engine` in `src/game.ts`.
|
|
70
|
+
- Do not edit `client/`, `src/manifest.ts` or `src/store.ts`.
|
|
71
|
+
- Do not rename or reprice the SKUs the client is wired to.
|
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This package is INKWRIT, a Net Forge game template. Read AGENTS.md before changing this game. It lists the declarations this genre has, the rules the build enforces, and how to validate, playtest and play the game locally.
|
|
4
|
+
|
|
5
|
+
The five hard rules:
|
|
6
|
+
|
|
7
|
+
1. Edit only src/game.ts (and src/names.json for the labels players read).
|
|
8
|
+
2. Import only from @netforge/engine.
|
|
9
|
+
3. Ids are lower_snake_case.
|
|
10
|
+
4. Declare at least one earn.rule and exactly one earn.stake.
|
|
11
|
+
5. Never write renderers, netcode, physics or bot AI, and never reference assets by URL or file path (use library ids such as "style.lowpoly.frog").
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 NetNet Capital Management
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/NOTICE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
@netforge/template-cards (INKWRIT)
|
|
2
|
+
Copyright (c) 2026 NetNet Capital Management
|
|
3
|
+
|
|
4
|
+
An original game. No names, characters, maps, models, sounds or code from
|
|
5
|
+
any other game or franchise.
|
|
6
|
+
|
|
7
|
+
Art and sound
|
|
8
|
+
- Models come from the per-style asset library
|
|
9
|
+
(@netforge/asset-library): CC0 1.0 models by Kenney and
|
|
10
|
+
Quaternius (monsters, animals, props), each pack listed with its
|
|
11
|
+
source URL and licence verification in that package's NOTICE and
|
|
12
|
+
manifest/packs.json. The files are fetched and built, never committed
|
|
13
|
+
here; the build copies only the models this game uses.
|
|
14
|
+
- Fallback shapes (@netforge/tabletop stage.ts) are generated from three.js
|
|
15
|
+
primitives; all textures are drawn on a canvas at load; sounds are the
|
|
16
|
+
library's CC0 samples with WebAudio synth fallbacks.
|
|
17
|
+
|
|
18
|
+
Dependencies (unmodified, via npm)
|
|
19
|
+
- three.js 0.186.1 (https://github.com/mrdoob/three.js), MIT.
|
|
20
|
+
- @netforge/engine and @netforge/engine-sim (this repository);
|
|
21
|
+
see their NOTICE files (bitECS MPL-2.0, Rapier Apache-2.0, Colyseus MIT,
|
|
22
|
+
zod MIT).
|
|
23
|
+
|
|
24
|
+
Patterns (ideas only)
|
|
25
|
+
- The collectible card battler genre's general mechanics (mana that rises
|
|
26
|
+
each turn, units and spells, guards, drafting between matches). Mechanics, not
|
|
27
|
+
expression; no card, rule text, character or art was copied from any
|
|
28
|
+
game.
|
package/README.md
CHANGED
|
@@ -1,3 +1,83 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @netforge/template-cards
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A card battler game template for Net Forge: one against one, a person against a person or against a gauntlet of AI scribes (INKWRIT).
|
|
4
|
+
|
|
5
|
+
Scribes duel in a lamplit study. Every card is an inked creature or a spell written on a page. A run is four duels, and after each one every player drafts one of three new cards into their deck. The room shuffles on the server and keeps hands hidden.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install @netforge/template-cards
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
You rarely import this package directly. kit play (`@netforge/kit-play`) and the Net Forge MCP server use it to build and run a game whose `src/game.ts` you write.
|
|
14
|
+
|
|
15
|
+
## Play it locally
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
mkdir -p my-game/src && cd my-game
|
|
19
|
+
npm init -y && npm install @netforge/kit-play @netforge/engine @netforge/template-cards
|
|
20
|
+
cp node_modules/@netforge/template-cards/src/game.ts src/
|
|
21
|
+
echo '{"template":"cards"}' > netnet.json
|
|
22
|
+
npx kit-play # opens http://127.0.0.1:4663
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Open the link in a second tab to duel yourself, or play alone against the AI. Coins are play money. Real-token earning, stakes and claims run only on the hosted Net Forge server.
|
|
26
|
+
|
|
27
|
+
## Change the game
|
|
28
|
+
|
|
29
|
+
Edit `src/game.ts` and run `npx kit-play` again. For example, give the heroes more health and add a fifth duel to the gauntlet:
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
cardTable({
|
|
33
|
+
health: 25,
|
|
34
|
+
duels: 5,
|
|
35
|
+
// ...the rest of the table as shipped, with a fifth ai tier
|
|
36
|
+
});
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Or add a card and put its label in `src/names.json` under `weapons`:
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
card("mirror_moth", { model: "style.lowpoly.jelly_flyer", kind: "unit", cost: 3, attack: 2, health: 2, effect: { kind: "damage", amount: 1, target: "all_enemies" } });
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## How it plays on the server
|
|
46
|
+
|
|
47
|
+
- A board genre on the engine's rules runtime (`@netforge/engine/rules`), drawn with `@netforge/tabletop`, at 30 Hz.
|
|
48
|
+
- Your quill (the cursor) is predicted. A play, an attack, an end of turn and a draft pick are actions the room validates: whose turn it is, the card in that slot, ink and guards.
|
|
49
|
+
- Hidden information: the room shuffles from its seeded PRNG and keeps the deck order to itself. A hand and a draft offer go only to their owner.
|
|
50
|
+
- A person alone for 3 s gets a seat bot (an AI scribe of the gauntlet's tier), which leaves when a second person joins.
|
|
51
|
+
- Earn: duels won against a person (never a forfeit) and AI scribes beaten. The AI never earns. The room server is the referee.
|
|
52
|
+
|
|
53
|
+
## Files
|
|
54
|
+
|
|
55
|
+
| Path | What |
|
|
56
|
+
|---|---|
|
|
57
|
+
| `src/game.ts` | The game, as declarations only. This is the file you edit. |
|
|
58
|
+
| `src/manifest.ts` | Loads the game; `cardsAdapter()` for the room host and the client (two seats). |
|
|
59
|
+
| `src/store.ts` | Coin prices the client is wired to. |
|
|
60
|
+
| `client/` | The desk, creatures on their cards, piles, quills, the hand, targeting and the draft. |
|
|
61
|
+
| `test/` | Playtest bots through `engine.test.run`, and the store test. |
|
|
62
|
+
| `llms.txt`, `llms-full.txt` | How to change the game, for agents. |
|
|
63
|
+
|
|
64
|
+
## For AI agents
|
|
65
|
+
|
|
66
|
+
Read `AGENTS.md` and `llms.txt` before editing. The Net Forge MCP server gives a coding agent the templates, the API and a playtest loop:
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
npx -y @netforge/mcp
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The package is `@netforge/mcp` and its bin is `netforge-mcp`. Its tools are `netforge_list_templates`, `netforge_create_game`, `netforge_explain_api`, `netforge_search_docs`, `netforge_validate_game`, `netforge_run_playtest` and `netforge_play_locally`.
|
|
73
|
+
|
|
74
|
+
To set up a coding agent in a game project, run `npx netforge setup agent`. It writes AGENTS.md, CLAUDE.md, Cursor rules and `.mcp.json` into the project. In Claude Code you can also add the server directly with `claude mcp add netforge -- npx -y @netforge/mcp`.
|
|
75
|
+
|
|
76
|
+
## Docs
|
|
77
|
+
|
|
78
|
+
- https://forge.netnet.capital/docs/templates/cards
|
|
79
|
+
- `llms.txt` and `llms-full.txt` in this package. The full file includes the complete `src/game.ts` and the validate and playtest steps.
|
|
80
|
+
|
|
81
|
+
## License
|
|
82
|
+
|
|
83
|
+
MIT. See LICENSE and NOTICE.
|
package/client/decor.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Library models the scene draws on its own (not named in game.ts): the
|
|
3
|
+
* study around the table. The build copies these into ./library in every
|
|
4
|
+
* art style (?style= may draw any).
|
|
5
|
+
*/
|
|
6
|
+
export const LAMP_MODEL = "style.lowpoly.campfire";
|
|
7
|
+
export const SHELF_MODELS = ["style.lowpoly.barrels", "style.lowpoly.crate", "style.lowpoly.chest", "style.lowpoly.column"] as const;
|
|
8
|
+
export const SCENE_MODELS: readonly string[] = [LAMP_MODEL, ...SHELF_MODELS];
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover, user-scalable=no" />
|
|
6
|
+
<title>Inkwrit</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<script type="module" src="./main.ts"></script>
|
|
10
|
+
</body>
|
|
11
|
+
</html>
|