feat: Phase 1 — 数据模型升级 Creature v2 + PCBox/Bag

- 新增 MoveSlot, PCBox, Bag, ItemId 类型
- Creature 扩展 nature/moves/ability/heldItem/pokeball 字段
- BuddyData 升级 v2: 新增 boxes, bag, battlesWon/battlesLost
- 新建 data/learnsets.ts: getDefaultMoveset/getDefaultAbility/getNewLearnableMoves
- storage.ts v1→v2 迁移: 回填 nature/moves/ability,新增 PCBox/Bag
- 新增 PCBox 操作: deposit/withdraw/move/rename/findLocation/release
- 新增 Bag 操作: add/remove/getCount
- generateCreature/loadBuddyData/hatchEgg 改为 async (Dex.learnsets.get 异步)
- 修复 PokedexView: activeCreatureId → party[0]
- 更新测试文件: async/await + v2 BuddyData fixtures

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
claude-code-best
2026-04-22 00:20:08 +08:00
parent 96f3e1b309
commit 12cbb7c4c7
16 changed files with 496 additions and 216 deletions

View File

@@ -26,6 +26,18 @@ const BUBBLE_SHOW = 20; // ticks → ~10s at 500ms
const FADE_WINDOW = 6; // last ~3s the bubble dims so you know it's about to go
const PET_BURST_MS = 2500; // how long hearts float after /buddy pet
// Module-level cache for sync access in render
let _cachedCreature: Creature | null = null;
let _cacheLoadPromise: Promise<void> | null = null;
function ensureCreatureCache(): void {
if (_cachedCreature !== null || _cacheLoadPromise) return;
_cacheLoadPromise = loadBuddyData().then(data => {
_cachedCreature = getActiveCreature(data);
_cacheLoadPromise = null;
}).catch(() => { _cacheLoadPromise = null; });
}
// Hearts float up-and-out over 5 ticks (~2.5s). Prepended above the sprite.
const H = figures.heart;
const PET_HEARTS = [
@@ -105,17 +117,27 @@ function spriteColWidth(nameWidth: number): number {
}
/**
* Get active Pokémon creature, or null if buddy system not initialized.
* Get active Pokémon creature from cache, or null if not loaded yet.
* Triggers async load if cache is empty.
*/
function getPokemonCreature(): Creature | null {
try {
const data = loadBuddyData();
return getActiveCreature(data);
ensureCreatureCache();
return _cachedCreature;
} catch {
return null;
}
}
/**
* Force-refresh the creature cache (call after data changes).
*/
export function refreshCreatureCache(): void {
_cachedCreature = null;
_cacheLoadPromise = null;
ensureCreatureCache();
}
export function companionReservedColumns(terminalColumns: number, speaking: boolean): number {
if (!feature('BUDDY')) return 0;
const creature = getPokemonCreature();