mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-17 13:55:50 +00:00
6.4 KiB
6.4 KiB
Phase 2: 战斗引擎 — @pkmn 薄适配层
前置
Phase 1 完成(Creature 含 moves/ability/nature/heldItem,PCBox/Bag 就位)
目标
安装 @pkmn/protocol、@pkmn/client、@pkmn/view,构建战斗引擎作为 @pkmn 的薄适配层。纯逻辑层,不涉及 UI。
安装依赖
cd packages/pokemon && bun add @pkmn/protocol @pkmn/client @pkmn/view
战斗设计原则
- 战后自动恢复:每场战斗结束后 HP/PP 自动恢复满值,Creature 不存 currentHp
- 不可逃跑:野生战斗必须打到一方倒下
- 状态不持久化:异常状态、能力阶级仅存在于 BattleState
- 支持换人:战斗中可切换 party 其他精灵(消耗回合)
- 支持道具:战斗中使用背包道具(消耗回合)
- 特性/物品自动处理:@pkmn/sim 内置
核心架构
@pkmn/sim Battle ──(log)──→ @pkmn/protocol ──(Handler)──→ BattleEvent[]
──(protocol)──→ @pkmn/client Battle ──(投影)──→ BattleState
新建文件
packages/pokemon/src/battle/
├── types.ts # BattleState, BattleEvent, BattleResult, PlayerAction, BattlePokemon
├── adapter.ts # Creature → PokemonSet 转换(stat key + gender 映射)
├── engine.ts # createBattle(), executeTurn() — 薄封装 @pkmn/sim Battle
├── handler.ts # @pkmn/protocol Handler → BattleEvent 转换
├── ai.ts # AI 决策(随机合法招式)
├── settlement.ts # 战后结算(XP/EV/升级/进化/招式学习/背包扣减)
└── index.ts # 导出
详细实现
1. battle/types.ts — 战斗类型
export type StatusCondition = 'poison' | 'bad_poison' | 'burn' | 'paralysis' | 'freeze' | 'sleep' | 'none'
export type BattlePokemon = {
id: string // creature ID
speciesId: string
name: string
level: number
hp: number // 战斗中当前 HP
maxHp: number
types: string[]
moves: MoveOption[]
ability: string
heldItem: string | null
status: StatusCondition
statStages: Record<string, number> // -6 到 +6
}
export type MoveOption = {
id: string; name: string; type: string
pp: number; maxPp: number; disabled: boolean
}
export type PlayerAction =
| { type: 'move'; moveIndex: number }
| { type: 'switch'; creatureId: string }
| { type: 'item'; itemId: string }
export type BattleEvent =
| { type: 'move'; side: 'player' | 'opponent'; move: string; user: string }
| { type: 'damage'; side: 'player' | 'opponent'; amount: number; percentage: number }
| { type: 'heal'; side: 'player' | 'opponent'; amount: number; percentage: number }
| { type: 'faint'; side: 'player' | 'opponent'; speciesId: string }
| { type: 'switch'; side: 'player' | 'opponent'; speciesId: string; name: string }
| { type: 'effectiveness'; multiplier: number }
| { type: 'crit' }
| { type: 'miss' }
| { type: 'status'; side: 'player' | 'opponent'; status: StatusCondition }
| { type: 'statChange'; side: 'player' | 'opponent'; stat: string; stages: number }
| { type: 'ability'; side: 'player' | 'opponent'; ability: string }
| { type: 'item'; side: 'player' | 'opponent'; item: string }
| { type: 'fail'; reason: string }
| { type: 'turn'; number: number }
export type BattleResult = {
winner: 'player' | 'opponent'
turns: number
xpGained: number
evGained: Record<string, number>
participantIds: string[]
}
export type BattleState = {
_sim: any // @pkmn/sim Battle 实例(内部)
_client: any // @pkmn/client Battle 实例(内部)
playerPokemon: BattlePokemon // 从 _client.p1.active[0] 投影
opponentPokemon: BattlePokemon // 从 _client.p2.active[0] 投影
playerParty: BattlePokemon[] // 从 _client.p1.team 投影
turn: number
events: BattleEvent[]
finished: boolean
result?: BattleResult
usableItems: { id: string; name: string; count: number }[]
}
2. battle/adapter.ts — 格式转换
creatureToSet(creature)→PokemonSet(stat key 映射 attack→atk, spAtk→spa 等)wildPokemonToSet(speciesId, level)→ 野生对手PokemonSet- 复用
TO_DEX_STAT映射(来自data/pkmn.ts)
3. battle/engine.ts — 核心引擎
createBattle(partyCreatures, opponentSpeciesId, opponentLevel, bagItems?)→ BattleState- 创建
@pkmn/sim.Battle实例(gen9customgame格式) - 创建
@pkmn/client.Battle实例追踪状态
- 创建
executeTurn(state, action)→ BattleState- 构建 choice 字符串(
move 1/switch 2) - AI 选招(随机合法招式)
battle.makeChoices(p1, p2)- 新 log →
@pkmn/protocolHandler → BattleEvent[] - 从
_client投影 BattleState
- 构建 choice 字符串(
4. battle/handler.ts — 协议处理
- 实现
Protocol.Handler接口 - 每个
|move|、|-damage|、|faint|等回调产出BattleEvent - 替代手写 switch-case 解析器
5. battle/ai.ts — AI 决策
初期实现:从对手可用招式中随机选一个(PP > 0)。后续可增加属性克制优先。
6. battle/settlement.ts — 战后结算
settleBattle(data, battleState) → {
data: BuddyData
learnableMoves: { creatureId, moveId, moveName }[]
pendingEvolutions: { creatureId, from, to }[]
}
applyMoveLearn(data, creatureId, newMoveId, replaceIndex) → BuddyData
applyEvolution(data, creatureId) → BuddyData
结算流程:XP → EV → 升级 → 新招式检测 → 进化检测 → 背包扣减 → 统计更新。
使用 @pkmn 包
@pkmn/sim(Battle 类、Dex.teams.pack)@pkmn/protocol(Protocol.Handler、Protocol.parse)@pkmn/client(Battle 状态追踪)@pkmn/sets(PokemonSet 类型)@pkmn/view(LogFormatter,可选)@pkmn/data(stats.calc,Phase 0 已集成)
测试
新文件: packages/pokemon/src/__tests__/battle.test.ts
- 创建战斗:BattleState 初始化正确
- 选招回合:HP 更新 + 事件生成
- 异常状态:状态附加/恢复
- 换人:正确切换 + 对手出招
- 道具:HP 恢复 + 背包扣减
- 特性/物品:事件生成
- 击倒 → 结束 → XP/EV 奖励
- 战后不写入 Creature 持久化数据
验证
bun test packages/pokemon/src/__tests__/battle.test.ts通过- 可在 Node REPL 中完成完整战斗流程
bun run typecheck零错误
代码量
新增 ~300 行(适配层),避免 ~500 行手写引擎