@ai-rpg-engine/starter-template 2.3.6 → 2.4.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/README.md +15 -15
- package/package.json +35 -35
- package/src/content.ts +80 -80
- package/src/index.ts +5 -5
- package/src/ruleset.ts +65 -65
- package/src/setup.ts +95 -95
- package/src/starter.test.ts +35 -35
- package/tsconfig.json +24 -14
package/README.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
# Starter Template
|
|
2
|
-
|
|
3
|
-
Copy this directory to `packages/starter-yourname/` to create a new game.
|
|
4
|
-
|
|
5
|
-
```bash
|
|
6
|
-
cp -r templates/starter packages/starter-mygame
|
|
7
|
-
```
|
|
8
|
-
|
|
9
|
-
Then edit:
|
|
10
|
-
- `package.json` — name and description
|
|
11
|
-
- `src/ruleset.ts` — your stats, resources, verbs
|
|
12
|
-
- `src/content.ts` — entities and zones
|
|
13
|
-
- `src/setup.ts` — wire your modules alongside `buildCombatStack`
|
|
14
|
-
|
|
15
|
-
See [Chapter 58 — Create Your Own Starter](../../site/src/content/docs/handbook/58-create-your-own-starter.md) for the full walkthrough.
|
|
1
|
+
# Starter Template
|
|
2
|
+
|
|
3
|
+
Copy this directory to `packages/starter-yourname/` to create a new game.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
cp -r templates/starter packages/starter-mygame
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Then edit:
|
|
10
|
+
- `package.json` — name and description
|
|
11
|
+
- `src/ruleset.ts` — your stats, resources, verbs
|
|
12
|
+
- `src/content.ts` — entities and zones
|
|
13
|
+
- `src/setup.ts` — wire your modules alongside `buildCombatStack`
|
|
14
|
+
|
|
15
|
+
See [Chapter 58 — Create Your Own Starter](../../site/src/content/docs/handbook/58-create-your-own-starter.md) for the full walkthrough.
|
package/package.json
CHANGED
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@ai-rpg-engine/starter-template",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "Starter template for ai-rpg-engine — copy, rename, and build your own game",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"files": [
|
|
7
|
-
"src",
|
|
8
|
-
"README.md",
|
|
9
|
-
"tsconfig.json"
|
|
10
|
-
],
|
|
11
|
-
"scripts": {
|
|
12
|
-
"build": "tsc",
|
|
13
|
-
"test": "vitest run"
|
|
14
|
-
},
|
|
15
|
-
"dependencies": {
|
|
16
|
-
"@ai-rpg-engine/core": "*",
|
|
17
|
-
"@ai-rpg-engine/content-schema": "*",
|
|
18
|
-
"@ai-rpg-engine/modules": "*"
|
|
19
|
-
},
|
|
20
|
-
"license": "MIT",
|
|
21
|
-
"author": "mcp-tool-shop",
|
|
22
|
-
"homepage": "https://mcp-tool-shop-org.github.io/ai-rpg-engine/",
|
|
23
|
-
"repository": {
|
|
24
|
-
"type": "git",
|
|
25
|
-
"url": "https://github.com/mcp-tool-shop-org/ai-rpg-engine.git",
|
|
26
|
-
"directory": "templates/starter"
|
|
27
|
-
},
|
|
28
|
-
"keywords": [
|
|
29
|
-
"rpg",
|
|
30
|
-
"game-engine",
|
|
31
|
-
"starter",
|
|
32
|
-
"template",
|
|
33
|
-
"ai-rpg-engine"
|
|
34
|
-
]
|
|
35
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@ai-rpg-engine/starter-template",
|
|
3
|
+
"version": "2.4.0",
|
|
4
|
+
"description": "Starter template for ai-rpg-engine — copy, rename, and build your own game",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"files": [
|
|
7
|
+
"src",
|
|
8
|
+
"README.md",
|
|
9
|
+
"tsconfig.json"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"test": "vitest run"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@ai-rpg-engine/core": "*",
|
|
17
|
+
"@ai-rpg-engine/content-schema": "*",
|
|
18
|
+
"@ai-rpg-engine/modules": "*"
|
|
19
|
+
},
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"author": "mcp-tool-shop",
|
|
22
|
+
"homepage": "https://mcp-tool-shop-org.github.io/ai-rpg-engine/",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "https://github.com/mcp-tool-shop-org/ai-rpg-engine.git",
|
|
26
|
+
"directory": "templates/starter"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"rpg",
|
|
30
|
+
"game-engine",
|
|
31
|
+
"starter",
|
|
32
|
+
"template",
|
|
33
|
+
"ai-rpg-engine"
|
|
34
|
+
]
|
|
35
|
+
}
|
package/src/content.ts
CHANGED
|
@@ -1,80 +1,80 @@
|
|
|
1
|
-
// Starter content — entities, zones, and manifest
|
|
2
|
-
//
|
|
3
|
-
// Replace this with your game's content.
|
|
4
|
-
|
|
5
|
-
import type { EntityState, ZoneState, GameManifest } from '@ai-rpg-engine/core';
|
|
6
|
-
|
|
7
|
-
export const manifest: GameManifest = {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
// ═══════════════════════════════════════════════════════════════════
|
|
18
|
-
// PACK METADATA (used by the CLI pack selector)
|
|
19
|
-
// ═══════════════════════════════════════════════════════════════════
|
|
20
|
-
|
|
21
|
-
export const packMeta = {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
// ═══════════════════════════════════════════════════════════════════
|
|
29
|
-
// PLAYER
|
|
30
|
-
// ═══════════════════════════════════════════════════════════════════
|
|
31
|
-
|
|
32
|
-
export const player: EntityState = {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
// ═══════════════════════════════════════════════════════════════════
|
|
46
|
-
// ENEMIES
|
|
47
|
-
// ═══════════════════════════════════════════════════════════════════
|
|
48
|
-
|
|
49
|
-
export const enemy: EntityState = {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
// ═══════════════════════════════════════════════════════════════════
|
|
62
|
-
// ZONES
|
|
63
|
-
// ═══════════════════════════════════════════════════════════════════
|
|
64
|
-
|
|
65
|
-
export const zones: ZoneState[] = [
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
];
|
|
1
|
+
// Starter content — entities, zones, and manifest
|
|
2
|
+
//
|
|
3
|
+
// Replace this with your game's content.
|
|
4
|
+
|
|
5
|
+
import type { EntityState, ZoneState, GameManifest } from '@ai-rpg-engine/core';
|
|
6
|
+
|
|
7
|
+
export const manifest: GameManifest = {
|
|
8
|
+
id: 'my-game',
|
|
9
|
+
title: 'My Game',
|
|
10
|
+
version: '0.1.0',
|
|
11
|
+
engineVersion: '0.1.0',
|
|
12
|
+
ruleset: 'my-game',
|
|
13
|
+
modules: ['traversal-core', 'status-core', 'combat-core'],
|
|
14
|
+
contentPacks: ['my-game'],
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
18
|
+
// PACK METADATA (used by the CLI pack selector)
|
|
19
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
20
|
+
|
|
21
|
+
export const packMeta = {
|
|
22
|
+
id: 'my-game',
|
|
23
|
+
name: 'My Game',
|
|
24
|
+
description: 'A brief description of your game',
|
|
25
|
+
version: '0.1.0',
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
29
|
+
// PLAYER
|
|
30
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
31
|
+
|
|
32
|
+
export const player: EntityState = {
|
|
33
|
+
id: 'player',
|
|
34
|
+
blueprintId: 'player',
|
|
35
|
+
type: 'player',
|
|
36
|
+
name: 'Hero',
|
|
37
|
+
tags: ['player'],
|
|
38
|
+
stats: { power: 5, speed: 4, grit: 3 },
|
|
39
|
+
resources: { hp: 25, stamina: 10, tension: 0 },
|
|
40
|
+
statuses: [],
|
|
41
|
+
inventory: [],
|
|
42
|
+
zoneId: 'start',
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
46
|
+
// ENEMIES
|
|
47
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
48
|
+
|
|
49
|
+
export const enemy: EntityState = {
|
|
50
|
+
id: 'grunt',
|
|
51
|
+
blueprintId: 'grunt',
|
|
52
|
+
type: 'npc',
|
|
53
|
+
name: 'Grunt',
|
|
54
|
+
tags: ['enemy', 'hostile'],
|
|
55
|
+
stats: { power: 3, speed: 3, grit: 2 },
|
|
56
|
+
resources: { hp: 12 },
|
|
57
|
+
statuses: [],
|
|
58
|
+
zoneId: 'danger-zone',
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
62
|
+
// ZONES
|
|
63
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
64
|
+
|
|
65
|
+
export const zones: ZoneState[] = [
|
|
66
|
+
{
|
|
67
|
+
id: 'start',
|
|
68
|
+
roomId: 'room-1',
|
|
69
|
+
name: 'Starting Area',
|
|
70
|
+
tags: ['safe'],
|
|
71
|
+
neighbors: ['danger-zone'],
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
id: 'danger-zone',
|
|
75
|
+
roomId: 'room-2',
|
|
76
|
+
name: 'The Danger Zone',
|
|
77
|
+
tags: ['hostile'],
|
|
78
|
+
neighbors: ['start'],
|
|
79
|
+
},
|
|
80
|
+
];
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
// @ai-rpg-engine/starter-YOURNAME
|
|
2
|
-
|
|
3
|
-
export { createGame } from './setup.js';
|
|
4
|
-
export { manifest, packMeta } from './content.js';
|
|
5
|
-
export { myRuleset } from './ruleset.js';
|
|
1
|
+
// @ai-rpg-engine/starter-YOURNAME
|
|
2
|
+
|
|
3
|
+
export { createGame } from './setup.js';
|
|
4
|
+
export { manifest, packMeta } from './content.js';
|
|
5
|
+
export { myRuleset } from './ruleset.js';
|
package/src/ruleset.ts
CHANGED
|
@@ -1,65 +1,65 @@
|
|
|
1
|
-
// Ruleset — declarative contract for your game's stats, resources, and verbs
|
|
2
|
-
|
|
3
|
-
import type { RulesetDefinition } from '@ai-rpg-engine/core';
|
|
4
|
-
|
|
5
|
-
export const myRuleset: RulesetDefinition = {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
};
|
|
1
|
+
// Ruleset — declarative contract for your game's stats, resources, and verbs
|
|
2
|
+
|
|
3
|
+
import type { RulesetDefinition } from '@ai-rpg-engine/core';
|
|
4
|
+
|
|
5
|
+
export const myRuleset: RulesetDefinition = {
|
|
6
|
+
id: 'my-game',
|
|
7
|
+
name: 'My Game',
|
|
8
|
+
version: '0.1.0',
|
|
9
|
+
|
|
10
|
+
// ═══════════════════════════════════════════════════════════════
|
|
11
|
+
// STATS — three combat dimensions + any exploration/social stats
|
|
12
|
+
// ═══════════════════════════════════════════════════════════════
|
|
13
|
+
stats: [
|
|
14
|
+
// Combat dimensions (required — mapped in buildCombatStack)
|
|
15
|
+
{ id: 'power', name: 'Power', min: 1, max: 20, default: 5 }, // → attack
|
|
16
|
+
{ id: 'speed', name: 'Speed', min: 1, max: 20, default: 5 }, // → precision
|
|
17
|
+
{ id: 'grit', name: 'Grit', min: 1, max: 20, default: 5 }, // → resolve
|
|
18
|
+
// Add exploration/social stats here if needed
|
|
19
|
+
],
|
|
20
|
+
|
|
21
|
+
// ═══════════════════════════════════════════════════════════════
|
|
22
|
+
// RESOURCES — hp is required; add your starter-specific resource
|
|
23
|
+
// ═══════════════════════════════════════════════════════════════
|
|
24
|
+
resources: [
|
|
25
|
+
{ id: 'hp', name: 'HP', min: 0, max: 100, default: 25 },
|
|
26
|
+
{ id: 'stamina', name: 'Stamina', min: 0, max: 50, default: 10, regenRate: 1 },
|
|
27
|
+
// YOUR RESOURCE: the one thing that makes your game feel different
|
|
28
|
+
{ id: 'tension', name: 'Tension', min: 0, max: 100, default: 0 },
|
|
29
|
+
],
|
|
30
|
+
|
|
31
|
+
// ═══════════════════════════════════════════════════════════════
|
|
32
|
+
// VERBS — actions available to players and AI
|
|
33
|
+
// ═══════════════════════════════════════════════════════════════
|
|
34
|
+
verbs: [
|
|
35
|
+
{ id: 'move', name: 'Move', description: 'Travel to an adjacent zone' },
|
|
36
|
+
{ id: 'inspect', name: 'Inspect', description: 'Examine surroundings' },
|
|
37
|
+
{ id: 'attack', name: 'Attack', tags: ['combat'], description: 'Strike a target' },
|
|
38
|
+
{ id: 'guard', name: 'Guard', tags: ['combat', 'defensive'], description: 'Reduce incoming damage' },
|
|
39
|
+
{ id: 'brace', name: 'Brace', tags: ['combat', 'defensive'], description: 'Stabilize and resist status effects' },
|
|
40
|
+
{ id: 'disengage', name: 'Disengage', tags: ['combat', 'movement'], description: 'Break from combat' },
|
|
41
|
+
{ id: 'reposition', name: 'Reposition', tags: ['combat', 'movement'], description: 'Shift engagement state' },
|
|
42
|
+
{ id: 'use', name: 'Use', description: 'Use an item' },
|
|
43
|
+
{ id: 'speak', name: 'Speak', tags: ['dialogue'], description: 'Talk to an NPC' },
|
|
44
|
+
{ id: 'choose', name: 'Choose', tags: ['dialogue'], description: 'Select a dialogue option' },
|
|
45
|
+
{ id: 'use-ability', name: 'Use Ability', tags: ['ability'], description: 'Activate a special ability' },
|
|
46
|
+
],
|
|
47
|
+
|
|
48
|
+
formulas: [
|
|
49
|
+
{ id: 'hit-chance', name: 'Hit Chance', description: 'Standard: 50 + speed*5 - target.speed*3', inputs: ['attacker.speed', 'target.speed'], output: 'number (0-100)' },
|
|
50
|
+
{ id: 'damage', name: 'Damage', description: 'Standard: attacker.power', inputs: ['attacker.power'], output: 'number' },
|
|
51
|
+
],
|
|
52
|
+
|
|
53
|
+
defaultModules: [
|
|
54
|
+
'traversal-core',
|
|
55
|
+
'combat-core',
|
|
56
|
+
'cognition-core',
|
|
57
|
+
'engagement-core',
|
|
58
|
+
'combat-tactics',
|
|
59
|
+
'combat-intent',
|
|
60
|
+
'combat-recovery',
|
|
61
|
+
'combat-state-narration',
|
|
62
|
+
],
|
|
63
|
+
|
|
64
|
+
progressionModels: [],
|
|
65
|
+
};
|
package/src/setup.ts
CHANGED
|
@@ -1,95 +1,95 @@
|
|
|
1
|
-
// Game setup — wire content + combat stack into the engine
|
|
2
|
-
//
|
|
3
|
-
// ╔══════════════════════════════════════════════════════════════════╗
|
|
4
|
-
// ║ COMPOSITION CONTRACT ║
|
|
5
|
-
// ║ buildCombatStack owns: cognition, engagement, review, ║
|
|
6
|
-
// ║ combatCore, tactics, resources, intent, recovery, narration ║
|
|
7
|
-
// ║ YOU own: traversal, status, inventory, dialogue, progression, ║
|
|
8
|
-
// ║ perception, environment, abilities, and your custom modules ║
|
|
9
|
-
// ╚══════════════════════════════════════════════════════════════════╝
|
|
10
|
-
|
|
11
|
-
import { Engine } from '@ai-rpg-engine/core';
|
|
12
|
-
import type { EngineModule, ResolvedEvent, WorldState } from '@ai-rpg-engine/core';
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
} from '@ai-rpg-engine/modules';
|
|
18
|
-
import { manifest, player, enemy, zones } from './content.js';
|
|
19
|
-
import { myRuleset } from './ruleset.js';
|
|
20
|
-
|
|
21
|
-
// ═══════════════════════════════════════════════════════════════════
|
|
22
|
-
// STARTER-OWNED SYSTEMS
|
|
23
|
-
// Add your game-specific modules here. These are the things that make
|
|
24
|
-
// your starter feel different from other starters.
|
|
25
|
-
// ═══════════════════════════════════════════════════════════════════
|
|
26
|
-
|
|
27
|
-
// Example: a custom module that ticks your "tension" resource each combat round
|
|
28
|
-
function createTensionPressure(): EngineModule {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// ═══════════════════════════════════════════════════════════════════
|
|
44
|
-
// GAME FACTORY
|
|
45
|
-
// ═══════════════════════════════════════════════════════════════════
|
|
46
|
-
|
|
47
|
-
export function createGame(seed?: number): Engine {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
1
|
+
// Game setup — wire content + combat stack into the engine
|
|
2
|
+
//
|
|
3
|
+
// ╔══════════════════════════════════════════════════════════════════╗
|
|
4
|
+
// ║ COMPOSITION CONTRACT ║
|
|
5
|
+
// ║ buildCombatStack owns: cognition, engagement, review, ║
|
|
6
|
+
// ║ combatCore, tactics, resources, intent, recovery, narration ║
|
|
7
|
+
// ║ YOU own: traversal, status, inventory, dialogue, progression, ║
|
|
8
|
+
// ║ perception, environment, abilities, and your custom modules ║
|
|
9
|
+
// ╚══════════════════════════════════════════════════════════════════╝
|
|
10
|
+
|
|
11
|
+
import { Engine } from '@ai-rpg-engine/core';
|
|
12
|
+
import type { EngineModule, ResolvedEvent, WorldState } from '@ai-rpg-engine/core';
|
|
13
|
+
import {
|
|
14
|
+
traversalCore,
|
|
15
|
+
statusCore,
|
|
16
|
+
buildCombatStack,
|
|
17
|
+
} from '@ai-rpg-engine/modules';
|
|
18
|
+
import { manifest, player, enemy, zones } from './content.js';
|
|
19
|
+
import { myRuleset } from './ruleset.js';
|
|
20
|
+
|
|
21
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
22
|
+
// STARTER-OWNED SYSTEMS
|
|
23
|
+
// Add your game-specific modules here. These are the things that make
|
|
24
|
+
// your starter feel different from other starters.
|
|
25
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
26
|
+
|
|
27
|
+
// Example: a custom module that ticks your "tension" resource each combat round
|
|
28
|
+
function createTensionPressure(): EngineModule {
|
|
29
|
+
return {
|
|
30
|
+
id: 'tension-pressure',
|
|
31
|
+
version: '1.0.0',
|
|
32
|
+
register(ctx) {
|
|
33
|
+
ctx.events.on('combat.round.end', (event: ResolvedEvent, world: WorldState) => {
|
|
34
|
+
const p = world.entities['player'];
|
|
35
|
+
if (p && p.resources.tension !== undefined) {
|
|
36
|
+
p.resources.tension = Math.min(100, (p.resources.tension ?? 0) + 5);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
44
|
+
// GAME FACTORY
|
|
45
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
46
|
+
|
|
47
|
+
export function createGame(seed?: number): Engine {
|
|
48
|
+
// Combat stack: map your three stats → the combat dimensions
|
|
49
|
+
const combat = buildCombatStack({
|
|
50
|
+
statMapping: { attack: 'power', precision: 'speed', resolve: 'grit' },
|
|
51
|
+
playerId: 'player',
|
|
52
|
+
// resourceProfile — uncomment and customize for your game's resource pressure:
|
|
53
|
+
// resourceProfile: {
|
|
54
|
+
// spends: [
|
|
55
|
+
// { verbId: 'attack', costStat: 'stamina', amount: 2 },
|
|
56
|
+
// ],
|
|
57
|
+
// },
|
|
58
|
+
// biasTags — entity tags that cognition uses for faction bias:
|
|
59
|
+
// biasTags: ['enemy'],
|
|
60
|
+
// engagement — customize if your game uses ranged/melee distance:
|
|
61
|
+
// engagement: { defaultRange: 'melee' },
|
|
62
|
+
// recovery — safe zone recovery:
|
|
63
|
+
recovery: { safeZoneTags: ['safe'] },
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const engine = new Engine({
|
|
67
|
+
manifest,
|
|
68
|
+
seed: seed ?? 42,
|
|
69
|
+
ruleset: myRuleset,
|
|
70
|
+
modules: [
|
|
71
|
+
traversalCore,
|
|
72
|
+
statusCore,
|
|
73
|
+
...combat.modules, // ← the full combat stack
|
|
74
|
+
// ──────────────────────────────────────────────
|
|
75
|
+
// YOUR MODULES GO HERE
|
|
76
|
+
createTensionPressure(),
|
|
77
|
+
// ──────────────────────────────────────────────
|
|
78
|
+
],
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// Register zones
|
|
82
|
+
for (const zone of zones) {
|
|
83
|
+
engine.store.addZone(zone);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Register entities
|
|
87
|
+
engine.store.addEntity({ ...player });
|
|
88
|
+
engine.store.addEntity({ ...enemy });
|
|
89
|
+
|
|
90
|
+
// Set player context
|
|
91
|
+
engine.store.state.playerId = 'player';
|
|
92
|
+
engine.store.state.locationId = 'start';
|
|
93
|
+
|
|
94
|
+
return engine;
|
|
95
|
+
}
|
package/src/starter.test.ts
CHANGED
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { validateRulesetDefinition } from '@ai-rpg-engine/content-schema';
|
|
3
|
-
import { myRuleset } from './ruleset.js';
|
|
4
|
-
import { createGame } from './setup.js';
|
|
5
|
-
|
|
6
|
-
describe('starter template', () => {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
});
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { validateRulesetDefinition } from '@ai-rpg-engine/content-schema';
|
|
3
|
+
import { myRuleset } from './ruleset.js';
|
|
4
|
+
import { createGame } from './setup.js';
|
|
5
|
+
|
|
6
|
+
describe('starter template', () => {
|
|
7
|
+
it('ruleset validates against schema', () => {
|
|
8
|
+
const r = validateRulesetDefinition(myRuleset);
|
|
9
|
+
expect(r.ok).toBe(true);
|
|
10
|
+
expect(r.errors).toHaveLength(0);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('declares combat stats mapped in buildCombatStack', () => {
|
|
14
|
+
const statIds = myRuleset.stats.map((s) => s.id);
|
|
15
|
+
expect(statIds).toContain('power');
|
|
16
|
+
expect(statIds).toContain('speed');
|
|
17
|
+
expect(statIds).toContain('grit');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('declares a starter-specific resource', () => {
|
|
21
|
+
const resIds = myRuleset.resources.map((r) => r.id);
|
|
22
|
+
expect(resIds).toContain('tension');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('createGame boots without throwing', () => {
|
|
26
|
+
const engine = createGame(1);
|
|
27
|
+
expect(engine).toBeDefined();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('tension pressure module is wired', () => {
|
|
31
|
+
const engine = createGame(1);
|
|
32
|
+
const p = engine.world.entities['player'];
|
|
33
|
+
expect(p?.resources.tension).toBe(0);
|
|
34
|
+
});
|
|
35
|
+
});
|
package/tsconfig.json
CHANGED
|
@@ -1,14 +1,24 @@
|
|
|
1
|
-
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "dist",
|
|
5
|
+
"rootDir": "src"
|
|
6
|
+
},
|
|
7
|
+
"include": [
|
|
8
|
+
"src"
|
|
9
|
+
],
|
|
10
|
+
"references": [
|
|
11
|
+
{
|
|
12
|
+
"path": "../../packages/core"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"path": "../../packages/content-schema"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"path": "../../packages/modules"
|
|
19
|
+
}
|
|
20
|
+
],
|
|
21
|
+
"exclude": [
|
|
22
|
+
"**/*.test.ts"
|
|
23
|
+
]
|
|
24
|
+
}
|