import { ref, computed, onMounted, onUnmounted } from 'vue'; export function usePetSystem() { // --- State --- const stage = ref('egg'); // egg, baby, adult const state = ref('idle'); // idle, sleep, eating, sick, dead, refuse // --- Stats --- const stats = ref({ hunger: 100, // 0-100 (0 = Starving) happiness: 100, // 0-100 (0 = Depressed) health: 100, // 0-100 (0 = Sick risk) weight: 500, // grams age: 0, // days poopCount: 0 // Number of poops on screen }); // --- Internal Timers --- let gameLoopId = null; const TICK_RATE = 3000; // 3 seconds per tick const isCleaning = ref(false); // --- Actions --- function feed() { if (state.value === 'sleep' || state.value === 'dead' || stage.value === 'egg' || isCleaning.value) return false; if (state.value === 'sick' || stats.value.hunger >= 90) { // Refuse food if sick or full triggerState('refuse', 2000); return false; } // Eat triggerState('eating', 3000); // Animation duration stats.value.hunger = Math.min(100, stats.value.hunger + 20); stats.value.weight += 50; // Chance to poop after eating (降低機率) if (Math.random() < 0.15) { // 從 0.3 降到 0.15 setTimeout(() => { if (stats.value.poopCount < 4) { stats.value.poopCount++; } }, 4000); } return true; } function play() { if (state.value !== 'idle' || stage.value === 'egg' || isCleaning.value) return false; stats.value.happiness = Math.min(100, stats.value.happiness + 15); stats.value.weight -= 10; // Exercise burns calories stats.value.hunger = Math.max(0, stats.value.hunger - 5); return true; } function clean() { if (stats.value.poopCount > 0 && !isCleaning.value) { isCleaning.value = true; // Delay removal for animation setTimeout(() => { stats.value.poopCount = 0; stats.value.happiness += 10; isCleaning.value = false; }, 2000); // 2 seconds flush animation return true; } return false; } function sleep() { if (isCleaning.value) return; if (state.value === 'idle') { state.value = 'sleep'; } else if (state.value === 'sleep') { state.value = 'idle'; // Wake up } } // --- Game Loop --- function tick() { if (state.value === 'dead' || stage.value === 'egg') return; // Decrease stats naturally // 目標:飢餓值約 30-60 分鐘下降 10%,快樂值約 20-40 分鐘下降 10% // TICK_RATE = 3000ms (3秒), 600 ticks = 30分鐘 // 飢餓值每 tick -0.05 → 600 ticks = -30 (30分鐘下降30%) // 快樂值每 tick -0.08 → 600 ticks = -48 (30分鐘下降48%) if (state.value !== 'sleep') { stats.value.hunger = Math.max(0, stats.value.hunger - 0.05); stats.value.happiness = Math.max(0, stats.value.happiness - 0.08); } else { // Slower decay when sleeping (約 1/3 速度) stats.value.hunger = Math.max(0, stats.value.hunger - 0.015); stats.value.happiness = Math.max(0, stats.value.happiness - 0.025); } // Random poop generation (更低的機率:約 0.5% per tick) // 平均約每 200 ticks = 10 分鐘拉一次 if (state.value !== 'sleep' && Math.random() < 0.005 && stats.value.poopCount < 4 && !isCleaning.value) { stats.value.poopCount++; } // Health Logic (更溫和的健康下降) // 便便影響健康:每個便便每 tick -0.1 health if (stats.value.poopCount > 0) { stats.value.health = Math.max(0, stats.value.health - (0.1 * stats.value.poopCount)); } // 飢餓影響健康:飢餓值低於 20 時開始影響健康 if (stats.value.hunger < 20) { const hungerPenalty = (20 - stats.value.hunger) * 0.02; // 飢餓越嚴重,扣越多 stats.value.health = Math.max(0, stats.value.health - hungerPenalty); } // 不開心影響健康:快樂值低於 20 時開始影響健康(較輕微) if (stats.value.happiness < 20) { const happinessPenalty = (20 - stats.value.happiness) * 0.01; stats.value.health = Math.max(0, stats.value.health - happinessPenalty); } // Sickness Check (更低的生病機率) if (stats.value.health < 30 && state.value !== 'sick') { if (Math.random() < 0.1) { // 從 0.3 降到 0.1 state.value = 'sick'; } } // Health Recovery (健康值可以緩慢恢復) // 如果沒有便便、飢餓值和快樂值都高,健康值會緩慢恢復 if (stats.value.poopCount === 0 && stats.value.hunger > 50 && stats.value.happiness > 50 && stats.value.health < 100 && state.value !== 'sick') { stats.value.health = Math.min(100, stats.value.health + 0.05); } // Death Check (移除死亡機制,依照之前的討論) // if (stats.value.health === 0) { // state.value = 'dead'; // } // Evolution / Growth (Simple Age increment) // In a real game, 1 day might be 24h, here maybe every 100 ticks? // For now, let's just say age increases slowly. } // --- Helpers --- function triggerState(tempState, duration) { const previousState = state.value; state.value = tempState; setTimeout(() => { if (state.value === tempState) { // Only revert if state hasn't changed again state.value = previousState === 'sleep' ? 'idle' : 'idle'; } }, duration); } function hatchEgg() { if (stage.value === 'egg') { stage.value = 'baby'; // or 'adult' for now since we only have that sprite // Let's map 'baby' to our 'adult' sprite for now, or just use 'adult' stage.value = 'adult'; state.value = 'idle'; stats.value.hunger = 50; stats.value.happiness = 50; stats.value.health = 100; stats.value.poopCount = 0; isCleaning.value = false; } } function reset() { stage.value = 'egg'; state.value = 'idle'; isCleaning.value = false; stats.value = { hunger: 100, happiness: 100, health: 100, weight: 500, age: 0, poopCount: 0 }; } // --- Lifecycle --- onMounted(() => { gameLoopId = setInterval(tick, TICK_RATE); }); onUnmounted(() => { if (gameLoopId) clearInterval(gameLoopId); }); return { stage, state, stats, isCleaning, feed, play, clean, sleep, hatchEgg, reset }; }