claude-code-tamagotchi
Version:
A virtual pet that lives in your Claude Code statusline
149 lines (122 loc) • 5.13 kB
text/typescript
import { PetState } from './StateManager';
import { config, getWeatherEffects } from '../utils/config';
export class DecaySystem {
applyDecay(state: PetState): void {
const now = Date.now();
const timeSinceLastUpdate = now - state.lastUpdate;
const minutesElapsed = timeSinceLastUpdate / 60000;
if (minutesElapsed <= 0) return;
// Don't decay while sleeping (except hunger)
if (state.isAsleep) {
// Restore energy while sleeping
state.energy = Math.min(100, state.energy + (minutesElapsed * 2));
// Still get hungry while sleeping, but slower
state.hunger = Math.max(0, state.hunger - (config.hungerDecayRate * minutesElapsed * 0.5));
return;
}
// Apply base decay rates
state.hunger = Math.max(0, state.hunger - (config.hungerDecayRate * minutesElapsed));
state.happiness = Math.max(0, state.happiness - (config.happinessDecayRate * minutesElapsed));
state.energy = Math.max(0, state.energy - (config.energyDecayRate * minutesElapsed));
state.cleanliness = Math.max(0, state.cleanliness - (config.cleanlinessDecayRate * minutesElapsed));
// Apply weather effects if enabled
if (config.enableWeatherEffects) {
const weatherEffects = getWeatherEffects();
if (weatherEffects.happiness) {
state.happiness = Math.max(0, Math.min(100,
state.happiness + (weatherEffects.happiness * minutesElapsed)));
}
if (weatherEffects.energy) {
state.energy = Math.max(0, Math.min(100,
state.energy + (weatherEffects.energy * minutesElapsed)));
}
if ((weatherEffects as any).cleanliness) {
state.cleanliness = Math.max(0, Math.min(100,
state.cleanliness + ((weatherEffects as any).cleanliness * minutesElapsed)));
}
}
// Health is affected by other stats
this.updateHealth(state);
// Check for sickness
this.checkSickness(state);
// Update age
state.age = Math.floor((now - state.birthTime) / 60000);
// Evolution check
if (config.enableEvolution) {
this.checkEvolution(state);
}
}
private updateHealth(state: PetState): void {
// Health is derived from other stats
const avgStats = (state.happiness + state.hunger + state.energy + state.cleanliness) / 4;
// Health tends toward average of other stats
const healthTarget = avgStats;
const healthDiff = healthTarget - state.health;
// Gradually adjust health
state.health = Math.max(0, Math.min(100,
state.health + (healthDiff * 0.1)));
}
private checkSickness(state: PetState): void {
// Get sick if health is too low or cleanliness is too low
if (state.health < 30 || state.cleanliness < 20) {
state.isSick = true;
} else if (state.isSick && state.health > 60 && state.cleanliness > 50) {
// Recover from sickness
state.isSick = false;
}
}
private checkEvolution(state: PetState): void {
// Evolution stages based on age and care level
const hoursOld = state.age / 60;
const avgCare = (state.happiness + state.hunger + state.energy + state.cleanliness) / 4;
// Calculate care points (good care over time)
if (avgCare > 70) {
state.careLevelPoints += 0.1;
}
// Evolution thresholds
if (state.evolutionStage === 0 && hoursOld > 1 && state.careLevelPoints > 30) {
state.evolutionStage = 1; // Baby -> Child
} else if (state.evolutionStage === 1 && hoursOld > 12 && state.careLevelPoints > 200) {
state.evolutionStage = 2; // Child -> Teen
} else if (state.evolutionStage === 2 && hoursOld > 24 && state.careLevelPoints > 500) {
state.evolutionStage = 3; // Teen -> Adult
}
}
// Apply effects after an action completes
applyActionEffects(state: PetState): void {
if (!state.pendingAction) return;
const action = state.pendingAction;
switch (action.type) {
case 'eating':
state.hunger = Math.min(100, state.hunger + 30);
state.happiness = Math.min(100, state.happiness + 10);
state.totalFeedings++;
if (action.item) {
state.foodEaten.push(action.item);
// Track favorite food
const foodCounts: Record<string, number> = {};
state.foodEaten.forEach(food => {
foodCounts[food] = (foodCounts[food] || 0) + 1;
});
state.favoriteFood = Object.keys(foodCounts).reduce((a, b) =>
foodCounts[a] > foodCounts[b] ? a : b);
}
break;
case 'playing':
state.happiness = Math.min(100, state.happiness + 20);
state.energy = Math.max(0, state.energy - 15);
state.totalPlaySessions++;
break;
case 'sleeping':
state.energy = 100;
state.isAsleep = false;
break;
case 'bathing':
state.cleanliness = 100;
state.happiness = Math.min(100, state.happiness + 5);
break;
}
// Clear the pending action
state.pendingAction = undefined;
}
}