mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-23 00:35:51 +00:00
feat: 第一版可用 pokemon
This commit is contained in:
206
packages/pokemon/src/core/storage.ts
Normal file
206
packages/pokemon/src/core/storage.ts
Normal file
@@ -0,0 +1,206 @@
|
||||
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs'
|
||||
import { join } from 'node:path'
|
||||
import { homedir } from 'node:os'
|
||||
import type { BuddyData, SpeciesId } from '../types'
|
||||
import { ALL_SPECIES_IDS } from '../types'
|
||||
import { generateCreature } from './creature'
|
||||
import { SPECIES_DATA } from '../data/species'
|
||||
|
||||
const BUDDY_DATA_PATH = join(homedir(), '.claude', 'buddy-data.json')
|
||||
const BUDDY_SPRITES_DIR = join(homedir(), '.claude', 'buddy-sprites')
|
||||
|
||||
/**
|
||||
* Load buddy data from disk. Returns default data if file doesn't exist.
|
||||
*/
|
||||
export function loadBuddyData(): BuddyData {
|
||||
if (!existsSync(BUDDY_DATA_PATH)) {
|
||||
return getDefaultBuddyData()
|
||||
}
|
||||
try {
|
||||
const raw = readFileSync(BUDDY_DATA_PATH, 'utf-8')
|
||||
const data = JSON.parse(raw) as BuddyData
|
||||
if (data.version !== 1) {
|
||||
return migrateData(data)
|
||||
}
|
||||
return data
|
||||
} catch {
|
||||
return getDefaultBuddyData()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save buddy data to disk.
|
||||
*/
|
||||
export function saveBuddyData(data: BuddyData): void {
|
||||
// Ensure directory exists
|
||||
const dir = join(BUDDY_DATA_PATH, '..')
|
||||
if (!existsSync(dir)) {
|
||||
mkdirSync(dir, { recursive: true })
|
||||
}
|
||||
writeFileSync(BUDDY_DATA_PATH, JSON.stringify(data, null, 2))
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default buddy data for new users.
|
||||
* Randomly assigns one of the three starters.
|
||||
*/
|
||||
export function getDefaultBuddyData(): BuddyData {
|
||||
const starters: SpeciesId[] = ['bulbasaur', 'charmander', 'squirtle']
|
||||
const randomStarter = starters[Math.floor(Math.random() * starters.length)]
|
||||
const creature = generateCreature(randomStarter)
|
||||
|
||||
return {
|
||||
version: 1,
|
||||
activeCreatureId: creature.id,
|
||||
creatures: [creature],
|
||||
eggs: [],
|
||||
dex: [
|
||||
{
|
||||
speciesId: randomStarter,
|
||||
discoveredAt: Date.now(),
|
||||
caughtCount: 1,
|
||||
bestLevel: 1,
|
||||
},
|
||||
],
|
||||
stats: {
|
||||
totalTurns: 0,
|
||||
consecutiveDays: 0,
|
||||
lastActiveDate: new Date().toISOString().split('T')[0],
|
||||
totalEggsObtained: 0,
|
||||
totalEvolutions: 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sprites cache directory path.
|
||||
*/
|
||||
export function getSpritesDir(): string {
|
||||
if (!existsSync(BUDDY_SPRITES_DIR)) {
|
||||
mkdirSync(BUDDY_SPRITES_DIR, { recursive: true })
|
||||
}
|
||||
return BUDDY_SPRITES_DIR
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate from legacy buddy system.
|
||||
* Accepts legacy companion data and maps to new Pokémon species.
|
||||
* If species cannot be determined, randomly assigns a starter.
|
||||
*/
|
||||
export function migrateFromLegacy(
|
||||
storedCompanion: { name?: string; personality?: string; seed?: string; hatchedAt?: number; species?: string },
|
||||
): BuddyData {
|
||||
const speciesMap: Record<string, SpeciesId> = {
|
||||
duck: 'bulbasaur',
|
||||
goose: 'squirtle',
|
||||
blob: 'bulbasaur',
|
||||
cat: 'charmander',
|
||||
dragon: 'pikachu',
|
||||
octopus: 'squirtle',
|
||||
owl: 'bulbasaur',
|
||||
penguin: 'squirtle',
|
||||
turtle: 'squirtle',
|
||||
snail: 'bulbasaur',
|
||||
ghost: 'pikachu',
|
||||
axolotl: 'squirtle',
|
||||
capybara: 'bulbasaur',
|
||||
cactus: 'charmander',
|
||||
robot: 'charmander',
|
||||
rabbit: 'pikachu',
|
||||
mushroom: 'bulbasaur',
|
||||
chonk: 'charmander',
|
||||
}
|
||||
|
||||
// If species is provided directly, use it; otherwise random starter
|
||||
const mapped = storedCompanion.species ? speciesMap[storedCompanion.species] : undefined
|
||||
const starters: SpeciesId[] = ['bulbasaur', 'charmander', 'squirtle']
|
||||
const speciesId: SpeciesId = mapped ?? starters[Math.floor(Math.random() * starters.length)]!
|
||||
|
||||
const creature = generateCreature(speciesId)
|
||||
creature.level = 5 // Reward for existing users
|
||||
creature.totalXp = 100
|
||||
creature.friendship = 120 // Existing partner bonus
|
||||
|
||||
// Preserve nickname if it's not the default
|
||||
const speciesInfo = SPECIES_DATA[speciesId]
|
||||
if (storedCompanion.name && storedCompanion.name !== speciesInfo.name) {
|
||||
creature.nickname = storedCompanion.name
|
||||
}
|
||||
|
||||
return {
|
||||
version: 1,
|
||||
activeCreatureId: creature.id,
|
||||
creatures: [creature],
|
||||
eggs: [],
|
||||
dex: [
|
||||
{
|
||||
speciesId,
|
||||
discoveredAt: Date.now(),
|
||||
caughtCount: 1,
|
||||
bestLevel: 5,
|
||||
},
|
||||
],
|
||||
stats: {
|
||||
totalTurns: 0,
|
||||
consecutiveDays: 1,
|
||||
lastActiveDate: new Date().toISOString().split('T')[0],
|
||||
totalEggsObtained: 0,
|
||||
totalEvolutions: 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle data migration between versions.
|
||||
*/
|
||||
function migrateData(data: BuddyData): BuddyData {
|
||||
// Currently only version 1 exists
|
||||
if (!data.version || data.version < 1) {
|
||||
return getDefaultBuddyData()
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
/**
|
||||
* Update daily stats (consecutive days, last active date).
|
||||
*/
|
||||
export function updateDailyStats(data: BuddyData): BuddyData {
|
||||
const today = new Date().toISOString().split('T')[0]
|
||||
const lastDate = data.stats.lastActiveDate
|
||||
|
||||
let consecutiveDays = data.stats.consecutiveDays
|
||||
if (lastDate !== today) {
|
||||
// Check if yesterday
|
||||
const yesterday = new Date()
|
||||
yesterday.setDate(yesterday.getDate() - 1)
|
||||
const yesterdayStr = yesterday.toISOString().split('T')[0]
|
||||
|
||||
if (lastDate === yesterdayStr) {
|
||||
consecutiveDays++
|
||||
} else {
|
||||
consecutiveDays = 1
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...data,
|
||||
stats: {
|
||||
...data.stats,
|
||||
consecutiveDays,
|
||||
lastActiveDate: today,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment turn counter.
|
||||
*/
|
||||
export function incrementTurns(data: BuddyData): BuddyData {
|
||||
return {
|
||||
...data,
|
||||
stats: {
|
||||
...data.stats,
|
||||
totalTurns: data.stats.totalTurns + 1,
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user