docs: sync updated boardgame-core
This commit is contained in:
parent
4d4889f825
commit
cbee709a27
|
|
@ -57,6 +57,9 @@ const hand = createRegion('hand', [
|
||||||
**区域操作:**
|
**区域操作:**
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
|
// 注意:parts 现在是 Record<string, Part> 格式
|
||||||
|
const parts: Record<string, Part> = { ... };
|
||||||
|
|
||||||
// 对齐/紧凑排列(根据 axis.align 自动调整位置)
|
// 对齐/紧凑排列(根据 axis.align 自动调整位置)
|
||||||
applyAlign(hand, parts);
|
applyAlign(hand, parts);
|
||||||
|
|
||||||
|
|
@ -78,7 +81,7 @@ removeFromRegion(part, region);
|
||||||
`Part<TMeta>` 表示游戏中的一个部件(棋子、卡牌等)。
|
`Part<TMeta>` 表示游戏中的一个部件(棋子、卡牌等)。
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { createPart, createParts, createPartPool, flip, flipTo, roll } from 'boardgame-core';
|
import { createPart, createParts, createPartPool, flip, flipTo, roll, mergePartPools } from 'boardgame-core';
|
||||||
|
|
||||||
// 创建单个部件
|
// 创建单个部件
|
||||||
const piece = createPart(
|
const piece = createPart(
|
||||||
|
|
@ -86,7 +89,7 @@ const piece = createPart(
|
||||||
'piece-1'
|
'piece-1'
|
||||||
);
|
);
|
||||||
|
|
||||||
// 批量创建(生成 piece-pawn-0, piece-pawn-1, ...)
|
// 批量创建(生成 piece-pawn-1, piece-pawn-2, ...)
|
||||||
const pawns = createParts(
|
const pawns = createParts(
|
||||||
{ regionId: 'supply', position: [0, 0] },
|
{ regionId: 'supply', position: [0, 0] },
|
||||||
8,
|
8,
|
||||||
|
|
@ -141,6 +144,12 @@ roll(dice, rng);
|
||||||
```ts
|
```ts
|
||||||
import { findPartById, isCellOccupied, getPartAtPosition } from 'boardgame-core';
|
import { findPartById, isCellOccupied, getPartAtPosition } from 'boardgame-core';
|
||||||
|
|
||||||
|
// Parts 现在是 Record<string, Part> 格式
|
||||||
|
const parts: Record<string, Part> = {
|
||||||
|
'piece-1': piece1,
|
||||||
|
'piece-2': piece2,
|
||||||
|
};
|
||||||
|
|
||||||
// 按 ID 查找
|
// 按 ID 查找
|
||||||
const piece = findPartById(parts, 'piece-1');
|
const piece = findPartById(parts, 'piece-1');
|
||||||
|
|
||||||
|
|
@ -358,7 +367,7 @@ export function createInitialState() {
|
||||||
{ name: 'x', min: 0, max: BOARD_SIZE - 1 },
|
{ name: 'x', min: 0, max: BOARD_SIZE - 1 },
|
||||||
{ name: 'y', min: 0, max: BOARD_SIZE - 1 },
|
{ name: 'y', min: 0, max: BOARD_SIZE - 1 },
|
||||||
]),
|
]),
|
||||||
parts: [] as TicTacToePart[],
|
parts: {} as Record<string, TicTacToePart>,
|
||||||
currentPlayer: 'X' as PlayerType,
|
currentPlayer: 'X' as PlayerType,
|
||||||
winner: null as PlayerType | 'draw' | null,
|
winner: null as PlayerType | 'draw' | null,
|
||||||
turn: 0,
|
turn: 0,
|
||||||
|
|
@ -411,7 +420,7 @@ registration.add('turn <player> <turn:number>', async function(cmd) {
|
||||||
if (row < 0 || row >= BOARD_SIZE || col < 0 || col >= BOARD_SIZE) {
|
if (row < 0 || row >= BOARD_SIZE || col < 0 || col >= BOARD_SIZE) {
|
||||||
return `Invalid position: (${row}, ${col})`;
|
return `Invalid position: (${row}, ${col})`;
|
||||||
}
|
}
|
||||||
if (isCellOccupied(this.context, row, col)) {
|
if (isCellOccupied(this.context.value.parts, 'board', [row, col])) {
|
||||||
return `Cell (${row}, ${col}) is occupied`;
|
return `Cell (${row}, ${col}) is occupied`;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -426,7 +435,7 @@ registration.add('turn <player> <turn:number>', async function(cmd) {
|
||||||
`piece-${player}-${turnNumber}`
|
`piece-${player}-${turnNumber}`
|
||||||
);
|
);
|
||||||
this.context.produce(state => {
|
this.context.produce(state => {
|
||||||
state.parts.push(piece);
|
state.parts[piece.id] = piece;
|
||||||
state.board.childIds.push(piece.id);
|
state.board.childIds.push(piece.id);
|
||||||
state.board.partMap[`${row},${col}`] = piece.id;
|
state.board.partMap[`${row},${col}`] = piece.id;
|
||||||
});
|
});
|
||||||
|
|
@ -486,12 +495,12 @@ const game = createGameContextFromModule(ticTacToe);
|
||||||
| `PartTemplate<TMeta>` | 部件模板(创建时排除 id) |
|
| `PartTemplate<TMeta>` | 部件模板(创建时排除 id) |
|
||||||
| `PartPool<TMeta>` | 部件池(draw/return/remaining) |
|
| `PartPool<TMeta>` | 部件池(draw/return/remaining) |
|
||||||
| `createPart(template, id)` | 创建单个部件 |
|
| `createPart(template, id)` | 创建单个部件 |
|
||||||
| `createParts(template, count, idPrefix)` | 批量创建部件 |
|
| `createParts(template, count, idPrefix)` | 批量创建部件(ID 从 1 开始:`prefix-1`, `prefix-2`, ...) |
|
||||||
| `createPartPool(template, count, idPrefix)` | 创建部件池 |
|
| `createPartPool(template, count, idPrefix)` | 创建部件池 |
|
||||||
| `mergePartPools(...pools)` | 合并部件池 |
|
| `mergePartPools(...pools)` | 合并部件池 |
|
||||||
| `findPartById(parts, id)` | 按 ID 查找 |
|
| `findPartById(parts, id)` | 按 ID 查找(`parts` 为 `Record<string, Part>`) |
|
||||||
| `isCellOccupied(parts, regionId, position)` | 检查位置占用 |
|
| `isCellOccupied(parts, regionId, position)` | 检查位置占用(`parts` 为 `Record<string, Part>`) |
|
||||||
| `getPartAtPosition(parts, regionId, position)` | 获取位置上的部件 |
|
| `getPartAtPosition(parts, regionId, position)` | 获取位置上的部件(`parts` 为 `Record<string, Part>`) |
|
||||||
| `flip(part)` | 翻面 |
|
| `flip(part)` | 翻面 |
|
||||||
| `flipTo(part, side)` | 翻到指定面 |
|
| `flipTo(part, side)` | 翻到指定面 |
|
||||||
| `roll(part, rng)` | 随机面 |
|
| `roll(part, rng)` | 随机面 |
|
||||||
|
|
@ -503,10 +512,10 @@ const game = createGameContextFromModule(ticTacToe);
|
||||||
| `Region` | 区域类型 |
|
| `Region` | 区域类型 |
|
||||||
| `RegionAxis` | 坐标轴定义 |
|
| `RegionAxis` | 坐标轴定义 |
|
||||||
| `createRegion(id, axes)` | 创建区域 |
|
| `createRegion(id, axes)` | 创建区域 |
|
||||||
| `applyAlign(region, parts)` | 对齐/紧凑排列 |
|
| `applyAlign(region, parts)` | 对齐/紧凑排列(`parts` 为 `Record<string, Part>`) |
|
||||||
| `shuffle(region, parts, rng)` | 随机打乱位置 |
|
| `shuffle(region, parts, rng)` | 随机打乱位置(`parts` 为 `Record<string, Part>`) |
|
||||||
| `moveToRegion(part, sourceRegion?, targetRegion, position?)` | 移动部件 |
|
| `moveToRegion(part, sourceRegion?, targetRegion, position?)` | 移动部件(`sourceRegion` 可选) |
|
||||||
| `moveToRegionAll(parts, sourceRegion?, targetRegion, positions?)` | 批量移动 |
|
| `moveToRegionAll(parts, sourceRegion?, targetRegion, positions?)` | 批量移动(`parts` 为 `Record<string, Part>`,`sourceRegion` 可选) |
|
||||||
| `removeFromRegion(part, region)` | 移除部件 |
|
| `removeFromRegion(part, region)` | 移除部件 |
|
||||||
|
|
||||||
### Command
|
### Command
|
||||||
|
|
@ -520,12 +529,18 @@ const game = createGameContextFromModule(ticTacToe);
|
||||||
| `parseCommandSchema(schema)` | 解析模式字符串 |
|
| `parseCommandSchema(schema)` | 解析模式字符串 |
|
||||||
| `validateCommand(cmd, schema)` | 验证命令 |
|
| `validateCommand(cmd, schema)` | 验证命令 |
|
||||||
| `parseCommandWithSchema(cmd, schema)` | 解析并验证 |
|
| `parseCommandWithSchema(cmd, schema)` | 解析并验证 |
|
||||||
|
| `applyCommandSchema(cmd, schema)` | 应用模式验证并返回验证后的命令 |
|
||||||
| `createCommandRegistry<TContext>()` | 创建命令注册表 |
|
| `createCommandRegistry<TContext>()` | 创建命令注册表 |
|
||||||
| `registerCommand(registry, runner)` | 注册命令 |
|
| `registerCommand(registry, runner)` | 注册命令 |
|
||||||
|
| `unregisterCommand(registry, name)` | 取消注册命令 |
|
||||||
| `hasCommand(registry, name)` | 检查命令是否存在 |
|
| `hasCommand(registry, name)` | 检查命令是否存在 |
|
||||||
|
| `getCommand(registry, name)` | 获取命令处理器 |
|
||||||
| `runCommand(registry, context, input)` | 解析并运行命令 |
|
| `runCommand(registry, context, input)` | 解析并运行命令 |
|
||||||
|
| `runCommandParsed(registry, context, command)` | 运行已解析的命令 |
|
||||||
|
| `createCommandRunnerContext(registry, context)` | 创建命令运行器上下文 |
|
||||||
| `PromptEvent` | Prompt 事件(tryCommit/cancel) |
|
| `PromptEvent` | Prompt 事件(tryCommit/cancel) |
|
||||||
| `CommandRunnerContext<TContext>` | 命令运行器上下文 |
|
| `CommandRunnerContext<TContext>` | 命令运行器上下文 |
|
||||||
|
| `CommandRunnerContextExport<TContext>` | 导出的命令运行器上下文(含 `promptQueue`) |
|
||||||
|
|
||||||
### Utilities
|
### Utilities
|
||||||
|
|
||||||
|
|
@ -541,16 +556,20 @@ const game = createGameContextFromModule(ticTacToe);
|
||||||
|
|
||||||
- **总是使用 `produce()`** 更新状态,不要直接修改 `.value`
|
- **总是使用 `produce()`** 更新状态,不要直接修改 `.value`
|
||||||
- `produce()` 内部是 draft 模式,可以直接修改属性
|
- `produce()` 内部是 draft 模式,可以直接修改属性
|
||||||
|
- **Parts 使用 Record 格式**,通过 ID 作为键访问
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
// ✅ 正确
|
// ✅ 正确
|
||||||
state.produce(draft => {
|
state.produce(draft => {
|
||||||
draft.score += 1;
|
draft.score += 1;
|
||||||
draft.parts.push(newPart);
|
draft.parts[newPart.id] = newPart;
|
||||||
});
|
});
|
||||||
|
|
||||||
// ❌ 错误 — 会破坏响应式
|
// ❌ 错误 — 会破坏响应式
|
||||||
state.value.score = 10;
|
state.value.score = 10;
|
||||||
|
|
||||||
|
// ❌ 错误 — parts 是 Record 不是数组
|
||||||
|
state.parts.push(newPart);
|
||||||
```
|
```
|
||||||
|
|
||||||
### 命令设计
|
### 命令设计
|
||||||
|
|
|
||||||
|
|
@ -39,11 +39,10 @@ export function bindRegion<TState, TMeta>(
|
||||||
container: Phaser.GameObjects.Container,
|
container: Phaser.GameObjects.Container,
|
||||||
): { cleanup: () => void; objects: Map<string, Phaser.GameObjects.GameObject> } {
|
): { cleanup: () => void; objects: Map<string, Phaser.GameObjects.GameObject> } {
|
||||||
const objects = new Map<string, Phaser.GameObjects.GameObject>();
|
const objects = new Map<string, Phaser.GameObjects.GameObject>();
|
||||||
const effects: DisposeFn[] = [];
|
|
||||||
|
|
||||||
const offset = options.offset ?? { x: 0, y: 0 };
|
const offset = options.offset ?? { x: 0, y: 0 };
|
||||||
|
|
||||||
function syncParts() {
|
const dispose = effect(function(this: { dispose: () => void }) {
|
||||||
const parts = partsGetter(state.value);
|
const parts = partsGetter(state.value);
|
||||||
const currentIds = new Set(region.childIds);
|
const currentIds = new Set(region.childIds);
|
||||||
for (const [id, obj] of objects) {
|
for (const [id, obj] of objects) {
|
||||||
|
|
@ -73,14 +72,11 @@ export function bindRegion<TState, TMeta>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
const e = effect(syncParts);
|
|
||||||
effects.push(e);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
cleanup: () => {
|
cleanup: () => {
|
||||||
for (const e of effects) e();
|
dispose();
|
||||||
for (const [, obj] of objects) obj.destroy();
|
for (const [, obj] of objects) obj.destroy();
|
||||||
objects.clear();
|
objects.clear();
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue