Files
claude-code/docs/plans/buddy-pokemon-plan.md
2026-04-21 19:03:31 +08:00

26 KiB
Raw Blame History

Buddy Pokémon 系统重构计划

Context

现有 buddy 系统(src/buddy/是一个简单的终端宠物18 种物种、固定属性、无成长机制。用户希望将其重构为 Pokémon 风格的收集养成系统,以独立包 packages/pokemon/ 的形式实现,复用原始 151 版本的设计理念。MVP 先做 10 只精灵(御三家 3 条进化线 + 1 只吉祥物)。


Phase 1: 包结构与数据模型

1.1 packages/pokemon/ 目录结构

packages/pokemon/
├── package.json              # name: "@claude-code-best/pokemon"
├── tsconfig.json
├── src/
│   ├── index.ts              # 统一导出
│   ├── types.ts              # 所有类型定义
│   ├── data/
│   │   ├── species.ts        # 10 只物种定义base stats, 进化链, 性别比, 性格)
│   │   ├── evolution.ts      # 进化条件数据
│   │   ├── evMapping.ts      # 工具→EV 映射(可配置 JSON
│   │   ├── xpTable.ts        # 1-100 级经验表(指数曲线)
│   │   └── names.ts          # 默认名、性格文案
│   ├── sprites/
│   │   ├── index.ts          # 精灵渲染入口
│   │   ├── renderer.ts       # ASCII art 渲染器(抖动/眨眼/粒子动画)
│   │   └── fallback.ts       # 网络失败时的简易 ASCII 占位符
│   ├── core/
│   │   ├── creature.ts       # 精灵生成、属性计算、等级判定
│   │   ├── experience.ts     # XP 增减、升级检测、经验曲线
│   │   ├── effort.ts         # EV 计算、工具→EV 映射
│   │   ├── evolution.ts      # 进化条件检测、进化执行
│   │   ├── egg.ts            # 蛋获取条件判定、孵化步数
│   │   ├── gender.ts         # 性别判定(按原始 151 设计)
│   │   ├── spriteCache.ts    # GitHub 拉取 cow → 本地 JSON 缓存
│   │   └── storage.ts        # ~/.claude/buddy-data.json 读写
│   └── ui/
│       ├── CompanionCard.tsx  # 重设计6 属性条 + 等级 + XP
│       ├── PokedexView.tsx    # 图鉴视图
│       ├── EggView.tsx        # 蛋/孵化进度
│       ├── EvolutionAnim.tsx  # 进化闪烁变形动画
│       └── StatBar.tsx        # 属性条组件(复用现有样式)

1.2 核心类型定义

// types.ts

// 6 属性(映射到编程场景)
export type StatName = 'hp' | 'attack' | 'defense' | 'spAtk' | 'spDef' | 'speed'
export const STAT_NAMES: StatName[] = ['hp', 'attack', 'defense', 'spAtk', 'spDef', 'speed']
export const STAT_LABELS: Record<StatName, string> = {
  hp: 'HP', attack: 'ATK', defense: 'DEF',
  spAtk: 'SPA', spDef: 'SPD', speed: 'SPE'
}

// 物种 IDMVP 10 只)
export type SpeciesId =
  | 'bulbasaur' | 'ivysaur' | 'venusaur'      // 御三家·草
  | 'charmander' | 'charmeleon' | 'charizard'  // 御三家·火
  | 'squirtle' | 'wartortle' | 'blastoise'     // 御三家·水
  | 'pikachu'                                   // 吉祥物

// 性别
export type Gender = 'male' | 'female' | 'genderless'

// 进化触发类型
export type EvolutionTrigger = 'level' | 'level_up' | 'item' | 'trade' | 'friendship'

export type EvolutionCondition = {
  trigger: EvolutionTrigger
  level?: number                    // 等级进化:目标等级
  minFriendship?: number            // 亲密度进化
  item?: string                     // 道具进化
  into: SpeciesId                   // 进化为
}

// 物种基础数据
export type SpeciesData = {
  id: SpeciesId
  name: string                      // 英文名
  names: Record<string, string>     // 多语言名 { ja, en, zh }
  dexNumber: number                 // 图鉴编号 (1-10 MVP)
  genderRatio: number               // 雌性概率 (0-1, -1=无性别)
  baseStats: Record<StatName, number>
  types: [string, string?]          // 属性 (grass/poison, fire, water 等)
  personality: string               // 默认性格描述
  evolutionChain?: EvolutionCondition[]
  sprites: string[][]               // ASCII art 帧 (每帧 5 行)
  shinyChance: number               // 闪光概率
}

// 实例化的精灵(存储在 buddy-data.json
export type Creature = {
  id: string                        // 唯一 ID (uuid)
  speciesId: SpeciesId
  nickname?: string                 // 用户自定义名
  gender: Gender
  level: number
  xp: number
  ev: Record<StatName, number>      // 努力值
  iv: Record<StatName, number>      // 个体值 (0-31)
  friendship: number                // 亲密度 (0-255)
  isShiny: boolean
  hatchedAt: number                 // 获得时间戳
}

// 蛋
export type Egg = {
  id: string
  obtainedAt: number
  stepsRemaining: number            // 剩余孵化步数
  speciesId: SpeciesId              // 预决定的物种(保底不重复)
}

// 图鉴条目
export type DexEntry = {
  speciesId: SpeciesId
  discoveredAt: number
  caughtCount: number               // 捕获数量
  bestLevel: number                 // 最高等级记录
}

// buddy-data.json 完整结构
export type BuddyData = {
  version: 1
  activeCreatureId: string | null
  creatures: Creature[]
  eggs: Egg[]
  dex: DexEntry[]
  stats: {
    totalTurns: number
    consecutiveDays: number
    lastActiveDate: string          // ISO date
    totalEggsObtained: number
    totalEvolutions: number
  }
}

1.3 工具→EV 映射(可配置)

// data/evMapping.ts
export const DEFAULT_EV_MAPPING: Record<string, Record<StatName, number>> = {
  "Bash":      { attack: 2, speed: 1 },
  "Edit":      { spAtk: 2, defense: 1 },
  "Write":     { spAtk: 3 },
  "Read":      { defense: 2, hp: 1 },
  "Grep":      { spDef: 2, speed: 1 },
  "Glob":      { spDef: 2, speed: 1 },
  "Agent":     { speed: 2, attack: 1 },
  "WebSearch": { spDef: 2, hp: 1 },
  "WebFetch":  { spDef: 2, hp: 1 },
}
// 不在映射中的工具 → 随机分配 1-2 点 EV

1.4 经验曲线

// data/xpTable.ts
// 指数曲线: level N 需要 totalXP = floor(N^3 * 0.8)
// 等级 1→2: 1 XP, 5→6: ~100 XP, 16→17: ~3000 XP, 36→37: ~37000 XP
export function xpForLevel(level: number): number {
  return Math.floor(Math.pow(level, 3) * 0.8)
}

Phase 2: 数据源 + 核心逻辑

2.0 PokeAPI 数据源

API: https://pokeapi.co/ 免费无需认证速率限制100 请求/分钟/IP

关键端点:

端点 数据 示例
/api/v2/pokemon/{id} base stats, types, height, weight hp:45, atk:49, def:49, spa:65, spd:65, spe:45
/api/v2/pokemon-species/{id} gender_rate, base_happiness, growth_rate, evolution_chain URL, flavor_text gender_rate:1, growth_rate:"medium-slow"
/api/v2/evolution-chain/{id} 完整进化链 + 触发条件 bulbasaur → Lv16 → ivysaur → Lv32 → venusaur

gender_rate 含义: -1=无性别, 0=全雄, 1=12.5%雌, 4=50%雌, 8=全雌。公式: femaleChance = gender_rate / 8

growth_rate 映射到 XP 曲线:

growth_rate 公式 100级总XP
erratic 复杂分段 600,000
fast n^3 * 0.8 800,000
medium-fast n^3 1,000,000
medium-slow 1.2n^3 - 15n^2 + 100n - 140 1,059,860
slow 1.25n^3 1,250,000
fluctuating 复杂分段 1,640,000

MVP 10 只的 growth_rate:

  • Bulbasaur line: medium-slow (链 #1)
  • Charmander line: medium-slow (链 #2)
  • Squirtle line: medium-slow (链 #3)
  • Pikachu: medium-fast (链 #10)

数据获取策略: 构建时调用 PokeAPI 预拉取 10 只精灵数据,生成静态 data/species.ts,运行时无需网络请求。用脚本 scripts/fetch-species.ts 实现:

# 构建时拉取,生成 species.ts
bun run packages/pokemon/scripts/fetch-species.ts

预拉取的数据项:

  • baseStats: 6 项种族值(直接用于属性计算)
  • types: 属性组合grass/poison, fire, water 等)
  • genderRate: 性别比例
  • baseHappiness: 基础亲密度70
  • growthRate: 经验曲线类型
  • evolutionChain: 进化链 + 触发条件level/friendship/item
  • captureRate: 捕获率(影响蛋的稀有度)
  • flavorText: 图鉴描述文本

2.0.1 端点数据示例

GET /api/v2/pokemon/1 (Bulbasaur):

{
  "base_stats": {"hp":45, "attack":49, "defense":49, "special-attack":65, "special-defense":65, "speed":45},
  "types": ["grass", "poison"]
}

GET /api/v2/pokemon-species/1:

{
  "gender_rate": 1,            // 12.5% 雌性
  "base_happiness": 70,
  "growth_rate": {"name": "medium-slow"},
  "capture_rate": 45,
  "evolution_chain": {"url": "https://pokeapi.co/api/v2/evolution-chain/1/"}
}

GET /api/v2/evolution-chain/1:

{
  "chain": {
    "species": {"name": "bulbasaur"},
    "evolves_to": [{
      "species": {"name": "ivysaur"},
      "evolution_details": [{"min_level": 16, "trigger": {"name": "level-up"}}],
      "evolves_to": [{
        "species": {"name": "venusaur"},
        "evolution_details": [{"min_level": 32, "trigger": {"name": "level-up"}}]
      }]
    }]
  }
}

2.1 精灵生成 (core/creature.ts)

  • generateCreature(speciesId, seed?): 创建新精灵
    • IV 随机生成 (0-31),用种子确定性
    • 性别按 speciesData.genderRatio 判定
    • 等级 1XP 0EV 全 0
    • 亲密度 70基础值
  • calculateStats(creature): 计算实际属性值
    • 公式参照宝可梦: stat = floor((2*base + iv + floor(ev/4)) * level / 100) + 5
    • HP 特殊: hp = floor((2*base + iv + floor(ev/4)) * level / 100) + level + 10
  • getCreatureName(creature): 返回 nickname || species name

2.2 经验系统 (core/experience.ts)

  • awardXP(creature, amount): 增加经验,返回是否升级
  • calculateLevel(xp): 根据 totalXP 计算当前等级
  • 来源:
    • 对话轮次完成: +5 XP
    • /buddy pet: +2 XP
    • 工具使用(通过 EV 间接): +1 XP/tool
    • 进化: +50 XP bonus

2.3 努力值系统 (core/effort.ts)

  • awardEV(creature, toolName, count): 根据工具名查映射表,加 EV
  • getEVForTool(toolName): 查映射表,未定义则随机
  • EV 上限: 每项 252总计 510跟宝可梦一致
  • 冷却: 每种工具类型 30 秒内只计算一次 EV

2.4 进化系统 (core/evolution.ts)

  • checkEvolution(creature): 检查是否满足进化条件
    • 等级进化: level >= condition.level
    • 亲密度进化: friendship >= condition.minFriendship
  • evolve(creature): 执行进化
    • 物种 ID 变更为进化目标
    • 属性重新计算base stats 变化)
    • 返回进化动画数据(旧物种 → 新物种)

MVP 进化链(参照原始 151:

Bulbasaur(#1) → Lv16 → Ivysaur(#2) → Lv32 → Venusaur(#3)
Charmander(#4) → Lv16 → Charmeleon(#5) → Lv36 → Charizard(#6)
Squirtle(#7) → Lv16 → Wartortle(#8) → Lv36 → Blastoise(#9)
Pikachu(#10) — MVP 不进化(后续加 Raichu

2.5 蛋系统 (core/egg.ts)

  • checkEggEligibility(buddyData): 判断是否满足获蛋条件
    • consecutiveDays >= 7 && totalTurns % 50 === 0每 50 轮检查一次)
    • 持有蛋数 < 1一次只能有一个蛋
  • generateEgg(buddyData): 生成蛋
    • 物种从「未收集」列表随机选取(保底不重复)
    • 步数 = 2000-5000按稀有度
  • advanceEggSteps(egg, steps): 推进步数
    • pet +5 步,对话轮次 +3 步,任意命令 +1 步
  • tryHatch(egg): 检查步数是否归零,返回新 Creature

2.6 数据持久化 (core/storage.ts)

  • loadBuddyData(): 从 ~/.claude/buddy-data.json 读取
  • saveBuddyData(data): 写入
  • migrateFromLegacy(): 迁移旧 buddy 数据
    • 现有 duck→Bulbasaur, cat→Charmander, turtle→Squirtle 等
    • 保留 nickname 和 personality
    • 等级设为 5奖励老用户

Phase 3: ASCII Art 精灵

3.1 素材来源

彩色像素精灵仓库: https://github.com/HRKings/pokemonsay-newgenerations/tree/master/pokemons

该仓库包含大量 Pokémon .cow 文件Perl cowsay 格式),使用 256 色 ANSI 转义 + Unicode 半块字符▄▀渲染高分辨率彩色像素精灵。MVP 所需的 10 个文件:

001_bulbasaur.cow  → 002_ivysaur.cow  → 003_venusaur.cow
004_charmander.cow → 005_charmeleon.cow → 006_charizard.cow
007_squirtle.cow   → 008_wartortle.cow  → 009_blastoise.cow
025_pikachu.cow

格式特征:

  • Perl heredoc: $the_cow =<<EOC; ... EOC
  • ANSI 256 色: \e[38;5;Nm(前景色)、\e[48;5;Nm(背景色)
  • Unicode 半块字符: \N{U+2584}(▄)、\N{U+2580}(▀)、普通空格
  • 每行含多个色块,组合出像素画效果

转换引擎: 不在代码中硬编码精灵图。运行时从 GitHub 拉取 .cow 文件,转换后缓存到本地。

存储路径: ~/.claude/buddy-sprites/{speciesId}.json

JSON 格式:

{
  "speciesId": "bulbasaur",
  "lines": ["           \e[49m           ...", "...", "...", "...", "..."],
  "width": 36,
  "height": 10,
  "fetchedAt": 1745260800000
}

拉取 + 转换流程 (core/spriteCache.ts):

  1. 获取精灵时(蛋孵化 / 首次获得 / 进化)触发 fetchAndCacheSprite(speciesId)
  2. fetch("https://raw.githubusercontent.com/HRKings/pokemonsay-newgenerations/master/pokemons/{NNN}_{name}.cow")
  3. 解析 .cow → 转换 → 写入 ~/.claude/buddy-sprites/{speciesId}.json
  4. 之后所有渲染从本地缓存读取,不再需要网络
  5. loadSprite(speciesId) — 直接读本地缓存,无网络调用
  6. 首次获取无网络 → 使用内置 fallback 简易 ASCII下次有网时补拉

转换步骤:

  1. 提取 heredoc 内容($the_cow =<<EOC;EOC 之间)
  2. \N{U+XXXX} → 实际 Unicode 字符(String.fromCharCode(0xXXXX)
  3. 保留 ANSI 序列(\e[38;5;Nm / \e[48;5;Nm)— 终端直接渲染彩色
  4. 去除前 4 行 $thoughts 占位行cowsay 对话气泡引导线)
  5. 按目标宽度缩放CompanionCard 宽度约 36 列)
  6. 写入本地 JSON 缓存

动画策略: 每物种只存 1 帧基础图,运行时通过变换生成动画:

  • idle: 原图静态显示(占大部分时间)
  • fidget: 整体右移 1 列 → 回原位,循环 1 次500ms/帧,共 1 秒)
  • blink: 将眼睛字符替换为 1 帧 500ms
  • excited: 快速左右抖动(每 250ms 交替 ±1 列偏移)
  • pet: 在基础图上方叠加心形粒子帧(复用现有 PET_HEARTS

不需要为每个物种单独设计帧。动画循环统一由 sprites/renderer.tsrenderAnimatedSprite(sprite, tick, mode) 处理:

type AnimMode = 'idle' | 'fidget' | 'blink' | 'excited' | 'pet'

function renderAnimatedSprite(lines: string[], tick: number, mode: AnimMode): string[] {
  switch (mode) {
    case 'idle': return lines
    case 'fidget': return shiftLines(lines, tick % 2 === 0 ? 0 : 1)
    case 'blink': return lines.map(l => l.replace(/[·✦×◉@°]/g, '—'))
    case 'excited': return shiftLines(lines, tick % 2 === 0 ? -1 : 1)
    case 'pet': return [...PET_HEARTS[tick % 5], ...lines]
  }
}

IDLE_SEQUENCE(复用现有设计): [idle, idle, idle, idle, fidget, idle, idle, idle, blink, idle, idle, idle, idle]

3.2 渲染适配

复用现有 renderSprite() 的架构,但扩展支持 ANSI 彩色:

  • 彩色模式: 保留原始 ANSI 256 色序列直接输出到终端256 色兼容性 > 99%
  • 单色回退: 剥离 ANSI 序列,用 Ink <Text color> 代替(兼容 16 色终端)
  • 眼睛替换:保留现有 {E} 占位符机制
  • 帽子 slot第 0 行保留空白(可选装饰)
  • 3 帧动画循环500ms tick与现有一致

3.3 进化动画帧

进化时使用闪烁变形效果:

  • 帧 1-3: 旧形态 + 闪烁(间隔显示空白)
  • 帧 4-6: 新旧形态交替
  • 帧 7-8: 新形态 + 粒子
  • 总时长 ~4 秒8 帧 × 500ms

Phase 4: UI 组件

4.1 CompanionCard 重设计

┌──────────────────────────────────┐
│ ★ CHARIZARD #6           Lv.36  │
│ ✨ SHINY ✨                     │
│                                  │
│        ASCII art here            │
│                                  │
│ "Blaze" (nicknamed Ember)       │
│ Type: Fire/Flying  Gender: ♂    │
│                                  │
│ HP  ████████░░  85               │
│ ATK ██████░░░░  62               │
│ DEF █████░░░░░  55               │
│ SPA ███████░░░  78               │
│ SPD ████░░░░░░  48               │
│ SPE ██████░░░░  65               │
│                                  │
│ XP [████████░░░░░░░] 14200/15680 │
│ EV: ATK+42 SPA+28 SPE+18        │
│ Friendship: ████████░░ 180/255   │
│                                  │
│ ── Commands ──                   │
│ /buddy pet   Pet for hearts      │
│ /buddy dex   View Pokédex        │
│ /buddy egg   Check egg progress  │
│ /buddy switch Change buddy       │
└──────────────────────────────────┘

4.2 PokédexView (/buddy dex)

┌───── Pokédex ────────────────────┐
│ Collected: 4/10                  │
│                                  │
│ #001 Bulbasaur  ████████ Lv.12  │
│ #002 Ivysaur    ████████ Lv.24  │
│ #003 Venusaur   ────── ???       │
│ #004 Charmander ████████ Lv.8   │
│ #005 Charmeleon ────── ???       │
│ #006 Charizard  ────── ???       │
│ #007 Squirtle   ████████ Lv.16  │
│ #008 Wartortle  ────── ???       │
│ #009 Blastoise  ────── ???       │
│ #010 Pikachu    ████████ Lv.5   │
│                                  │
│ 🥚 Egg: 1240/3000 steps         │
│ Next egg in: 3 days + 12 turns  │
└──────────────────────────────────┘

4.3 EggView (/buddy egg)

┌───── Egg Status ─────────────────┐
│                                  │
│        .                          │
│       / \                         │
│      |   |                        │
│       \_/                         │
│                                  │
│ Steps: 1240 / 3000               │
│ ████████░░░░░░░░ 41%             │
│                                  │
│ Pet (+5) · Chat (+3) · Cmd (+1) │
│ Hatch: ~588 more interactions    │
└──────────────────────────────────┘

4.4 进化动画 (EvolutionAnim.tsx)

在 REPL 面板右侧区域显示:

  • 设置 AppState.companionEvolving = true
  • 500ms tick 循环:
    • tick 0-3: 旧精灵 + 闪烁(每隔一帧显示空白)
    • tick 4-7: 新旧交替 + 粒子效果
    • tick 8: 新形态稳定显示 + "进化成功!" 文字
  • 用户按任意键结束动画
  • 更新 buddy-data.json 中的物种数据

Phase 5: 集成点

5.1 REPL.tsx 钩子(关键修改文件)

src/screens/REPL.tsx 约 3407 行turn metrics 收集后):

// 现有代码
const toolMs = getTurnToolDurationMs()
const toolCount = getTurnToolCount()

// 新增: EV + XP 奖励
if (feature('BUDDY')) {
  const buddyData = loadBuddyData()
  if (buddyData.activeCreatureId) {
    // 1. 遍历本 turn 的工具调用,计算 EV
    const evResult = awardTurnEV(buddyData, messages)
    // 2. 奖励对话 XP
    const xpResult = awardXP(buddyData, 5 + toolCount)
    // 3. 推进蛋步数
    advanceEggSteps(buddyData, 3)
    // 4. 检查进化
    const evoResult = checkEvolution(getActiveCreature(buddyData))
    if (evoResult) {
      setAppState(prev => ({ ...prev, companionEvolving: evoResult }))
    }
    saveBuddyData(buddyData)
  }
}

5.2 /buddy 命令重构

修改 src/commands/buddy/buddy.ts,子命令:

命令 说明
/buddy 显示 CompanionCard新版
/buddy status 详细属性 + EV 分布
/buddy pet 摸摸 (+5 蛋步数, +2 XP, 心形动画)
/buddy dex 显示 PokédexView
/buddy switch 列出已拥有精灵,选择首发
/buddy egg 显示 EggView
/buddy rename <name> 重命名当前精灵
/buddy on/off 静音/取消静音

5.3 AppState 扩展

src/state/AppStateStore.ts 新增:

companionEvolving?: { from: SpeciesId; to: SpeciesId }  // 进化动画状态
companionEggSteps?: number                                // 蛋步数更新(触发 UI 刷新)

5.4 buddy-data.json 持久化

路径: ~/.claude/buddy-data.json

// core/storage.ts
import { existsSync, readFileSync, writeFileSync } from 'node:fs'
import { join } from 'node:path'
import { homedir } from 'node:os'

const BUDDY_DATA_PATH = join(homedir(), '.claude', 'buddy-data.json')

export function loadBuddyData(): BuddyData {
  if (!existsSync(BUDDY_DATA_PATH)) return getDefaultBuddyData()
  return JSON.parse(readFileSync(BUDDY_DATA_PATH, 'utf-8'))
}

export function saveBuddyData(data: BuddyData): void {
  writeFileSync(BUDDY_DATA_PATH, JSON.stringify(data, null, 2))
}

Phase 6: 迁移方案

6.1 物种映射表

旧物种 新物种 原因
duck Bulbasaur 同为初始伙伴
cat Charmander 独立/判断力 → 火
turtle Squirtle 耐心/稳重 → 水
dragon Pikachu 稀有度最高 → 吉祥物
其他 14 种 随机御三家之一 按稀有度映射

6.2 迁移逻辑

function migrateFromLegacy(storedCompanion: StoredCompanion): BuddyData {
  const speciesMap = { duck: 'bulbasaur', cat: 'charmander', turtle: 'squirtle', dragon: 'pikachu', ... }
  const speciesId = speciesMap[storedCompanion.species] ?? randomStarter()
  const creature = generateCreature(speciesId)
  creature.level = 5          // 奖励老用户
  creature.nickname = storedCompanion.name !== defaultName ? storedCompanion.name : undefined
  creature.friendship = 120   // 已有伙伴基础亲密度
  return {
    version: 1,
    activeCreatureId: creature.id,
    creatures: [creature],
    eggs: [],
    dex: [{ speciesId, discoveredAt: Date.now(), caughtCount: 1, bestLevel: 5 }],
    stats: { totalTurns: 0, consecutiveDays: 0, lastActiveDate: new Date().toISOString(), totalEggsObtained: 0, totalEvolutions: 0 }
  }
}

Phase 7: 实施顺序

Step 1: 包骨架 + 类型

  • 创建 packages/pokemon/ 目录结构
  • 定义所有 TypeScript 类型
  • 配置 package.json、tsconfig.json
  • 在根 package.json 添加 workspace 引用

Step 2: 数据文件PokeAPI 预拉取)

  • 编写 scripts/fetch-species.ts — 调用 PokeAPI 拉取 10 只精灵数据
  • 运行脚本生成 data/species.tsbase stats, types, gender_rate, growth_rate, evolution_chain, capture_rate, flavor_text
  • 手动编写 EV 映射 (data/evMapping.ts)
  • 编写 XP 经验表 (data/xpTable.ts),支持 6 种 growth_rate 曲线

Step 3: ASCII Art 精灵(获取时拉取,永久缓存)

  • 编写 core/spriteCache.ts — 获取精灵时从 GitHub 拉取 .cow → 解析 → 缓存到 ~/.claude/buddy-sprites/
  • loadSprite(speciesId) 纯读本地缓存,无网络调用
  • fetchAndCacheSprite(speciesId) 仅在获得新精灵/进化时触发
  • 编写 sprites/fallback.ts — 网络不可用时的简易占位 ASCII
  • 动画由 renderAnimatedSprite() 运行时变换(抖动/眨眼/心形粒子),每物种只缓存 1 帧

Step 4: 核心逻辑

  • core/creature.ts — 精灵生成、属性计算
  • core/experience.ts — XP/等级系统
  • core/effort.ts — EV 系统
  • core/evolution.ts — 进化检测与执行
  • core/egg.ts — 蛋系统
  • core/storage.ts — 数据持久化
  • core/gender.ts — 性别判定

Step 5: UI 组件

  • 重写 CompanionCard.tsx6 属性 + 等级 + XP
  • 新建 PokedexView.tsx
  • 新建 EggView.tsx
  • 新建 EvolutionAnim.tsx

Step 6: 集成

  • 修改 src/commands/buddy/buddy.ts — 新子命令
  • 修改 src/screens/REPL.tsx — EV/XP 钩子
  • 修改 src/state/AppStateStore.ts — 新状态字段
  • 修改 src/buddy/CompanionSprite.tsx — 使用新精灵系统
  • 迁移逻辑在首次加载时自动执行

Step 7: 测试

  • packages/pokemon/src/__tests__/ 单元测试
  • 覆盖: 属性计算、XP 曲线、EV 映射、进化条件、蛋系统、迁移

关键文件清单

新建文件

  • packages/pokemon/ — 整个包(~20 个文件,不含精灵图)
  • ~/.claude/buddy-data.json — 运行时自动创建
  • ~/.claude/buddy-sprites/ — 运行时从 GitHub 拉取并缓存的精灵 JSON每个物种 1 个文件)

修改文件

  • src/commands/buddy/buddy.ts — 新子命令路由
  • src/commands/buddy/index.ts — 命令注册
  • src/screens/REPL.tsx — EV/XP 钩子(~20 行新增)
  • src/state/AppStateStore.ts — 新状态字段(~3 行)
  • src/buddy/CompanionSprite.tsx — 使用 packages/pokemon 的精灵渲染
  • src/buddy/CompanionCard.tsx — 可能直接替换为 packages/pokemon 的版本

不修改文件

  • src/utils/config.ts — 旧 companion 字段保留,向后兼容
  • src/buddy/companionReact.ts — API 调用层不变,只更新传入的数据结构
  • src/buddy/prompt.ts — 伙伴 intro 逻辑微调

验证方案

  1. 类型检查: bun run typecheck 零错误
  2. 单元测试: bun test packages/pokemon/ 覆盖核心逻辑
  3. 全量测试: bun test 确保 0 失败
  4. 手动验证:
    • bun run dev 启动 → /buddy 显示新卡片
    • /buddy dex 显示图鉴(初始只有 1 只)
    • /buddy pet 心形动画 + XP 增长
    • 模拟工具使用 → EV 增长
    • /buddy egg 显示蛋进度
    • 等级达到 16 → 进化动画触发
    • 旧用户 ~/.claude.json 有 companion → 自动迁移