docs: update docs
This commit is contained in:
parent
deb8c91239
commit
27e6d52cf3
112
QWEN.md
112
QWEN.md
|
|
@ -17,113 +17,6 @@
|
||||||
`boardgame-core`的内容可以在`framework/node_modules/boardgame-core`找到。
|
`boardgame-core`的内容可以在`framework/node_modules/boardgame-core`找到。
|
||||||
这个文件夹被.gitignore忽略,查看时需要绕开这一限制。
|
这个文件夹被.gitignore忽略,查看时需要绕开这一限制。
|
||||||
|
|
||||||
## 编写GameModule
|
|
||||||
|
|
||||||
游戏逻辑以GameModule的形式定义:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import {createGameCommandRegistry, IGameContext} from "boardgame-core";
|
|
||||||
|
|
||||||
// 创建mutative游戏初始状态
|
|
||||||
export function createInitialState(): GameState {
|
|
||||||
//...
|
|
||||||
}
|
|
||||||
|
|
||||||
// 运行游戏
|
|
||||||
export async function start(game: IGameContext<GameState>) {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
|
|
||||||
// 可选
|
|
||||||
export const registry = createGameCommandRegistry();
|
|
||||||
```
|
|
||||||
|
|
||||||
使用以下步骤创建GameModule:
|
|
||||||
|
|
||||||
### 1. 定义状态
|
|
||||||
|
|
||||||
通常使用一个`regions: Record<string, Region>`和一个`parts: Record<string, Part<TMeta>>`来记录桌游物件的摆放。
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import {Region} from "boardgame-core";
|
|
||||||
|
|
||||||
type GameState = {
|
|
||||||
regions: {
|
|
||||||
white: Region,
|
|
||||||
black: Region,
|
|
||||||
board: Region,
|
|
||||||
},
|
|
||||||
pieces: Record<string, Part<PartsTable['0']>>,
|
|
||||||
currentPlayer: PlayerType,
|
|
||||||
winner: WinnerType,
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
游戏的部件可以从`csv`加载。详情见`boop-game/node_modules/inline-schema/`。
|
|
||||||
```
|
|
||||||
/// parts.csv
|
|
||||||
type, player, count
|
|
||||||
string, string, number
|
|
||||||
cat, white, 8
|
|
||||||
cat, black, 8
|
|
||||||
|
|
||||||
/// parts.csv.d.ts
|
|
||||||
type PartsTable = {
|
|
||||||
type: string;
|
|
||||||
player: string;
|
|
||||||
count: number;
|
|
||||||
}[];
|
|
||||||
|
|
||||||
declare const data: PartsTable;
|
|
||||||
export default data;
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 定义流程
|
|
||||||
|
|
||||||
使用`async function start(game: IGameContext<GameState>)`作为入口。
|
|
||||||
|
|
||||||
使用`game.value`读取游戏当前状态
|
|
||||||
```typescript
|
|
||||||
async function gameEnd(game: IGameContext<GameState>) {
|
|
||||||
return game.value.winner;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
需要修改状态时,使用`game.produce`或`game.produceAsync`。
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
async function start(game: IGameContext<GameState>) {
|
|
||||||
await game.produceAsync(state => {
|
|
||||||
state.currentPlayer = 'white';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
需要等待玩家交互时,使用`await game.prompt(promptDef, validator, player)`。
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import {createPromptDef} from "boardgame-core";
|
|
||||||
|
|
||||||
export const prompts = {
|
|
||||||
play: createPromptDef<[PlayerType, number, number]>('play <player> <row:number> <col:number>'),
|
|
||||||
}
|
|
||||||
|
|
||||||
async function turn(game: IGameContext<GameState>, currentPlayer: PlayerType) {
|
|
||||||
const {player, row, col} = await game.prompt(
|
|
||||||
prompts.play,
|
|
||||||
(player, row, col) => {
|
|
||||||
if (player !== currentPlayer)
|
|
||||||
throw `Wrong player!`
|
|
||||||
return {player, row, col};
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 编写测试
|
|
||||||
|
|
||||||
使用`vitest`编写测试,测试应当使用`GameHost`来模拟游戏环境。
|
|
||||||
|
|
||||||
## 编写Phaser App
|
## 编写Phaser App
|
||||||
|
|
||||||
使用`framework/src/ui/PhaserBridge`来创建Phaser App。
|
使用`framework/src/ui/PhaserBridge`来创建Phaser App。
|
||||||
|
|
@ -136,6 +29,9 @@ async function turn(game: IGameContext<GameState>, currentPlayer: PlayerType) {
|
||||||
export class GameHost<TState extends Record<string, unknown>, TResult=unknown> {
|
export class GameHost<TState extends Record<string, unknown>, TResult=unknown> {
|
||||||
// 获取游戏状态的只读快照
|
// 获取游戏状态的只读快照
|
||||||
get state(): TState{}
|
get state(): TState{}
|
||||||
|
// 获取随机数
|
||||||
|
get rng(): ReadonlyRNG{}
|
||||||
|
|
||||||
// 运行状态
|
// 运行状态
|
||||||
readonly status: ReadonlySignal<GameHostStatus>;
|
readonly status: ReadonlySignal<GameHostStatus>;
|
||||||
|
|
||||||
|
|
@ -151,7 +47,7 @@ export class GameHost<TState extends Record<string, unknown>, TResult=unknown> {
|
||||||
addInterruption(promise: Promise<void>): void {}
|
addInterruption(promise: Promise<void>): void {}
|
||||||
|
|
||||||
// 开始或者重新开始游戏
|
// 开始或者重新开始游戏
|
||||||
start(): Promise<TResult>{}
|
start(seed?: number): Promise<TResult>{}
|
||||||
|
|
||||||
// 销毁
|
// 销毁
|
||||||
dispose(): void {}
|
dispose(): void {}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,107 @@
|
||||||
|
## 编写GameModule
|
||||||
|
|
||||||
|
游戏逻辑以GameModule的形式定义:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import {createGameCommandRegistry, IGameContext} from "boardgame-core";
|
||||||
|
|
||||||
|
// 创建mutative游戏初始状态
|
||||||
|
export function createInitialState(): GameState {
|
||||||
|
//...
|
||||||
|
}
|
||||||
|
|
||||||
|
// 运行游戏
|
||||||
|
export async function start(game: IGameContext<GameState>) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
// 可选
|
||||||
|
export const registry = createGameCommandRegistry();
|
||||||
|
```
|
||||||
|
|
||||||
|
使用以下步骤创建GameModule:
|
||||||
|
|
||||||
|
### 1. 定义状态
|
||||||
|
|
||||||
|
通常使用一个`regions: Record<string, Region>`和一个`parts: Record<string, Part<TMeta>>`来记录桌游物件的摆放。
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import {Region} from "boardgame-core";
|
||||||
|
|
||||||
|
type GameState = {
|
||||||
|
regions: {
|
||||||
|
white: Region,
|
||||||
|
black: Region,
|
||||||
|
board: Region,
|
||||||
|
},
|
||||||
|
pieces: Record<string, Part<PartsTable['0']>>,
|
||||||
|
currentPlayer: PlayerType,
|
||||||
|
winner: WinnerType,
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
游戏的部件可以从`csv`加载。详情见`boop-game/node_modules/inline-schema/`。
|
||||||
|
```
|
||||||
|
/// parts.csv
|
||||||
|
type, player, count
|
||||||
|
string, string, number
|
||||||
|
cat, white, 8
|
||||||
|
cat, black, 8
|
||||||
|
|
||||||
|
/// parts.csv.d.ts
|
||||||
|
type PartsTable = {
|
||||||
|
type: string;
|
||||||
|
player: string;
|
||||||
|
count: number;
|
||||||
|
}[];
|
||||||
|
|
||||||
|
declare const data: PartsTable;
|
||||||
|
export default data;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 定义流程
|
||||||
|
|
||||||
|
使用`async function start(game: IGameContext<GameState>)`作为入口。
|
||||||
|
|
||||||
|
使用`game.value`读取游戏当前状态
|
||||||
|
```typescript
|
||||||
|
async function gameEnd(game: IGameContext<GameState>) {
|
||||||
|
return game.value.winner;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
需要修改状态时,使用`game.produce`或`game.produceAsync`。
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
async function start(game: IGameContext<GameState>) {
|
||||||
|
await game.produceAsync(state => {
|
||||||
|
state.currentPlayer = 'white';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
需要等待玩家交互时,使用`await game.prompt(promptDef, validator, player)`。
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import {createPromptDef} from "boardgame-core";
|
||||||
|
|
||||||
|
export const prompts = {
|
||||||
|
play: createPromptDef<[PlayerType, number, number]>('play <player> <row:number> <col:number>'),
|
||||||
|
}
|
||||||
|
|
||||||
|
async function turn(game: IGameContext<GameState>, currentPlayer: PlayerType) {
|
||||||
|
const {player, row, col} = await game.prompt(
|
||||||
|
prompts.play,
|
||||||
|
(player, row, col) => {
|
||||||
|
if (player !== currentPlayer)
|
||||||
|
throw `Wrong player!`
|
||||||
|
return {player, row, col};
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 编写测试
|
||||||
|
|
||||||
|
使用`vitest`编写测试,测试应当使用`GameHost`来模拟游戏环境。
|
||||||
|
|
||||||
Loading…
Reference in New Issue