mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-17 13:55:50 +00:00
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:
@@ -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();
|
||||
|
||||
@@ -34,11 +34,11 @@ const MAX_RECENT = 8
|
||||
/**
|
||||
* Trigger a companion reaction after a query turn.
|
||||
*/
|
||||
export function triggerCompanionReaction(
|
||||
export async function triggerCompanionReaction(
|
||||
messages: Message[],
|
||||
setReaction: (text: string | undefined) => void,
|
||||
): void {
|
||||
const data = loadBuddyData()
|
||||
): Promise<void> {
|
||||
const data = await loadBuddyData()
|
||||
const creature = getActiveCreature(data)
|
||||
if (!creature || getGlobalConfig().companionMuted) return
|
||||
|
||||
|
||||
@@ -17,11 +17,11 @@ A ${species} named ${name} sits beside the user's input box and occasionally com
|
||||
When the user addresses ${name} directly (by name), its bubble will answer. Your job in that moment is to stay out of the way: respond in ONE line or less, or just answer any part of the message meant for you. Don't explain that you're not ${name} — they know. Don't narrate what ${name} might say — the bubble handles that.`
|
||||
}
|
||||
|
||||
export function getCompanionIntroAttachment(
|
||||
export async function getCompanionIntroAttachment(
|
||||
messages: Message[] | undefined,
|
||||
): Attachment[] {
|
||||
): Promise<Attachment[]> {
|
||||
if (!feature('BUDDY')) return []
|
||||
const data = loadBuddyData()
|
||||
const data = await loadBuddyData()
|
||||
const creature = getActiveCreature(data)
|
||||
if (!creature || getGlobalConfig().companionMuted) return []
|
||||
|
||||
|
||||
@@ -37,14 +37,14 @@ import { BuddyPanel } from './BuddyPanel.js'
|
||||
* Load or initialize Pokémon buddy data.
|
||||
* Migrates from legacy buddy system if needed.
|
||||
*/
|
||||
function getOrInitBuddyData(): BuddyData {
|
||||
let data = loadBuddyData()
|
||||
async function getOrInitBuddyData(): Promise<BuddyData> {
|
||||
let data = await loadBuddyData()
|
||||
|
||||
// If no active creature (party empty), check for legacy companion to migrate
|
||||
if (!getActiveCreature(data) || data.creatures.length === 0) {
|
||||
const legacyCompanion = getGlobalConfig().companion
|
||||
if (legacyCompanion) {
|
||||
data = migrateFromLegacy(legacyCompanion)
|
||||
data = await migrateFromLegacy(legacyCompanion)
|
||||
saveBuddyData(data)
|
||||
}
|
||||
}
|
||||
@@ -76,7 +76,7 @@ export async function call(
|
||||
|
||||
// ── /buddy pet — trigger heart animation + XP + egg steps ──
|
||||
if (sub === 'pet') {
|
||||
const data = getOrInitBuddyData()
|
||||
const data = await getOrInitBuddyData()
|
||||
const creature = getActiveCreature(data)
|
||||
if (!creature) {
|
||||
onDone('no companion yet · run /buddy first', { display: 'system' })
|
||||
@@ -100,7 +100,7 @@ export async function call(
|
||||
// Check hatch
|
||||
const readyEgg = data.eggs.find(isEggReadyToHatch)
|
||||
if (readyEgg) {
|
||||
const { buddyData: updatedData, creature: newCreature } = hatchEgg(
|
||||
const { buddyData: updatedData, creature: newCreature } = await hatchEgg(
|
||||
data,
|
||||
readyEgg,
|
||||
)
|
||||
@@ -137,7 +137,7 @@ export async function call(
|
||||
onDone('Usage: /buddy rename <name>', { display: 'system' })
|
||||
return null
|
||||
}
|
||||
const data = getOrInitBuddyData()
|
||||
const data = await getOrInitBuddyData()
|
||||
const creature = getActiveCreature(data)
|
||||
if (!creature) {
|
||||
onDone('no companion yet · run /buddy first', { display: 'system' })
|
||||
@@ -172,10 +172,10 @@ export async function call(
|
||||
return null
|
||||
}
|
||||
|
||||
const data = getOrInitBuddyData()
|
||||
const data = await getOrInitBuddyData()
|
||||
|
||||
// Create the creature
|
||||
const creature = generateCreature(match)
|
||||
const creature = await generateCreature(match)
|
||||
if (levelArg && !isNaN(levelArg) && levelArg >= 1 && levelArg <= 100) {
|
||||
creature.level = levelArg
|
||||
}
|
||||
@@ -212,7 +212,7 @@ export async function call(
|
||||
}
|
||||
|
||||
// ── /buddy (no args) — show unified BuddyPanel ──
|
||||
const data = getOrInitBuddyData()
|
||||
const data = await getOrInitBuddyData()
|
||||
let creature = getActiveCreature(data)
|
||||
|
||||
// Auto-unmute when viewing
|
||||
@@ -224,11 +224,11 @@ export async function call(
|
||||
if (!creature) {
|
||||
const legacyCompanion = getGlobalConfig().companion
|
||||
if (legacyCompanion) {
|
||||
const migrated = migrateFromLegacy(legacyCompanion)
|
||||
const migrated = await migrateFromLegacy(legacyCompanion)
|
||||
saveBuddyData(migrated)
|
||||
creature = getActiveCreature(migrated)!
|
||||
} else {
|
||||
const defaultData = getDefaultBuddyData()
|
||||
const defaultData = await getDefaultBuddyData()
|
||||
saveBuddyData(defaultData)
|
||||
creature = getActiveCreature(defaultData)!
|
||||
}
|
||||
@@ -244,7 +244,7 @@ export async function call(
|
||||
spriteCached?.lines ?? getFallbackSprite(creature.speciesId)
|
||||
|
||||
// Reload data to get latest state after possible initialization
|
||||
const latestData = loadBuddyData()
|
||||
const latestData = await loadBuddyData()
|
||||
|
||||
return React.createElement(BuddyPanel, {
|
||||
buddyData: latestData,
|
||||
|
||||
@@ -3462,7 +3462,7 @@ export function REPL({
|
||||
updateDailyStats: _updateDaily,
|
||||
incrementTurns: _incTurns,
|
||||
} = await import('@claude-code-best/pokemon');
|
||||
const _data = _updateDaily(_incTurns(_load()));
|
||||
const _data = _updateDaily(_incTurns(await _load()));
|
||||
const _creature = _getActive(_data);
|
||||
if (_creature) {
|
||||
// 1. Collect tool names from this turn's messages
|
||||
@@ -3502,7 +3502,7 @@ export function REPL({
|
||||
_data.eggs = _data.eggs.map((e: any) => _advSteps(e, 3));
|
||||
const _readyEgg = _data.eggs.find(_isReady);
|
||||
if (_readyEgg) {
|
||||
const { buddyData: _hatched, creature: _newC } = _hatchEgg(_data, _readyEgg);
|
||||
const { buddyData: _hatched, creature: _newC } = await _hatchEgg(_data, _readyEgg);
|
||||
Object.assign(_data, _hatched);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user