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 // --- Destiny Data --- const DESTINIES = [ { id: 'luck', name: '福運', description: '籤詩好籤率 +10%', rarity: 1 }, { id: 'diligence', name: '勤奮', description: '訓練遊戲獎勵 +20%', rarity: 1 }, { id: 'gluttony', name: '暴食', description: '飢餓下降速度 +30%', rarity: 1 }, { id: 'playful', name: '愛玩', description: 'Happiness 更快下降/更快上升', rarity: 1 }, { id: 'purification', name: '淨化', description: '生病機率 -20%', rarity: 1 }, { id: 'thirdeye', name: '天眼', description: '擲筊出聖筊機率微升', rarity: 2 }, { id: 'medium', name: '冥感', description: '死亡後招魂成功率提升', rarity: 2 } ]; // --- 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: 1, // days (start at day 1) poopCount: 0, // Number of poops on screen // v2 Stats str: 0, // 力量 (Fireball Game) int: 0, // 智力 (Guessing Game) dex: 0, // 敏捷 (Catch Ball) generation: 1, // 輪迴世代 deityFavor: 0, // 神明好感度 destiny: null // 天生命格 (Object) }); const achievements = ref([ { id: 'newbie', name: '新手飼主', desc: '養育超過 1 天', unlocked: false, icon: '🥚' }, { id: 'veteran', name: '資深飼主', desc: '養育超過 7 天', unlocked: false, icon: '🏆' }, { id: 'healthy', name: '健康寶寶', desc: '3歲且健康 > 90', unlocked: false, icon: '💪' }, { id: 'happy', name: '快樂天使', desc: '3歲且快樂 > 90', unlocked: false, icon: '💖' } ]); // --- Internal Timers --- let gameLoopId = null; let tickCount = 0; const TICK_RATE = 3000; // 3 seconds per tick const TICKS_PER_DAY = 20; // For testing: 1 minute = 1 day (usually 28800 for 24h) const isCleaning = ref(false); // --- Actions --- function assignDestiny() { // Simple weighted random or just random for now // Rarity 2 has lower chance const roll = Math.random(); let pool = DESTINIES; // 20% chance for rare destiny if (roll < 0.2) { pool = DESTINIES.filter(d => d.rarity === 2); } else { pool = DESTINIES.filter(d => d.rarity === 1); } const picked = pool[Math.floor(Math.random() * pool.length)]; stats.value.destiny = picked; console.log('Assigned Destiny:', picked); } 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 (降低機率) // Destiny Effect: Gluttony (暴食) might increase poop chance? Or just hunger decay. 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 // Destiny Effect: Gluttony (暴食) - Hunger decreases faster (+30%) let hungerDecay = 0.05; if (stats.value.destiny?.id === 'gluttony') { hungerDecay *= 1.3; } // Destiny Effect: Playful (愛玩) - Happiness decreases faster let happinessDecay = 0.08; if (stats.value.destiny?.id === 'playful') { happinessDecay *= 1.2; // Faster decay } // Destiny Effect: DEX (敏捷) - Hunger decreases slower // DEX 10 = -10% decay, DEX 50 = -50% decay if (stats.value.dex > 0) { const reduction = Math.min(0.5, stats.value.dex * 0.01); // Max 50% reduction hungerDecay *= (1 - reduction); } if (state.value !== 'sleep') { stats.value.hunger = Math.max(0, stats.value.hunger - hungerDecay); stats.value.happiness = Math.max(0, stats.value.happiness - happinessDecay); } else { // Slower decay when sleeping (約 1/3 速度) stats.value.hunger = Math.max(0, stats.value.hunger - (hungerDecay * 0.3)); stats.value.happiness = Math.max(0, stats.value.happiness - (happinessDecay * 0.3)); } // 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 (更低的生病機率) // Destiny Effect: Purification (淨化) - Sickness chance -20% let sickChance = 0.1; if (stats.value.destiny?.id === 'purification') { sickChance *= 0.8; } if (stats.value.health < 30 && state.value !== 'sick') { if (Math.random() < sickChance) { 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 tickCount++; if (tickCount >= TICKS_PER_DAY) { stats.value.age++; tickCount = 0; checkEvolution(); } checkAchievements(); } function checkAchievements() { if (!achievements.value[0].unlocked && stats.value.age >= 1) { unlockAchievement(0); } if (!achievements.value[1].unlocked && stats.value.age >= 7) { unlockAchievement(1); } if (!achievements.value[2].unlocked && stats.value.age >= 3 && stats.value.health >= 90) { unlockAchievement(2); } if (!achievements.value[3].unlocked && stats.value.age >= 3 && stats.value.happiness >= 90) { unlockAchievement(3); } } function unlockAchievement(index) { if (!achievements.value[index].unlocked) { achievements.value[index].unlocked = true; triggerState('happy', 2000); // Celebrate achievement } } function unlockAllAchievements() { achievements.value.forEach(a => a.unlocked = true); triggerState('happy', 2000); } function checkEvolution() { // Simple evolution logic if (stage.value === 'baby' && stats.value.age >= 3) { stage.value = 'child'; triggerState('happy', 2000); // Celebrate } else if (stage.value === 'child' && stats.value.age >= 7) { stage.value = 'adult'; triggerState('happy', 2000); } } // --- 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; // v2: Assign Destiny assignDestiny(); 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: 1, poopCount: 0, // v2 Reset str: 0, int: 0, dex: 0, generation: 1, deityFavor: 0, destiny: null }; tickCount = 0; } function resurrect() { if (state.value !== 'dead') return; state.value = 'idle'; stats.value.health = 50; // Revive with half health stats.value.happiness = 50; stats.value.hunger = 50; // Penalty or Ghost Buff? // For now just a console log, maybe visual effect later console.log('Pet Resurrected!'); } function reincarnate() { // Inherit logic const prevDestiny = stats.value.destiny; const prevFavor = stats.value.deityFavor; const nextGen = (stats.value.generation || 1) + 1; // Reset everything reset(); // Apply Inheritance stats.value.generation = nextGen; // 20% Favor inheritance stats.value.deityFavor = Math.floor(prevFavor * 0.2); // Destiny Inheritance (Optional: Maybe keep if it was a rare one?) // For now, let's say if you had a Rare (2) destiny, you keep it. // Otherwise, you get a new one on hatch. if (prevDestiny && prevDestiny.rarity === 2) { stats.value.destiny = prevDestiny; } console.log('Pet Reincarnated to Gen', nextGen); } // --- Lifecycle --- onMounted(() => { gameLoopId = setInterval(tick, TICK_RATE); }); onUnmounted(() => { if (gameLoopId) clearInterval(gameLoopId); }); return { stage, state, stats, isCleaning, feed, play, clean, sleep, hatchEgg, reset, achievements, unlockAllAchievements, assignDestiny, // Export for debug if needed resurrect, reincarnate }; }