2025-11-20 09:15:38 +00:00
|
|
|
import { ref, computed, onMounted, onUnmounted } from 'vue';
|
2025-11-22 18:17:13 +00:00
|
|
|
import { api } from '../services/api';
|
2025-11-20 09:15:38 +00:00
|
|
|
|
|
|
|
|
export function usePetSystem() {
|
|
|
|
|
// --- State ---
|
|
|
|
|
const stage = ref('egg'); // egg, baby, adult
|
|
|
|
|
const state = ref('idle'); // idle, sleep, eating, sick, dead, refuse
|
2025-11-22 18:17:13 +00:00
|
|
|
const isLoading = ref(true);
|
|
|
|
|
const error = ref(null);
|
2025-11-20 09:15:38 +00:00
|
|
|
|
2025-11-22 13:25:35 +00:00
|
|
|
// --- 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 }
|
|
|
|
|
];
|
|
|
|
|
|
2025-11-20 09:15:38 +00:00
|
|
|
// --- 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
|
2025-11-22 03:23:02 +00:00
|
|
|
age: 1, // days (start at day 1)
|
2025-11-22 13:25:35 +00:00
|
|
|
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, // 神明好感度
|
2025-11-22 15:50:28 +00:00
|
|
|
destiny: null, // 天生命格 (Object)
|
|
|
|
|
// Deity System
|
|
|
|
|
currentDeity: 'mazu',
|
|
|
|
|
deityFavors: {
|
|
|
|
|
mazu: 0,
|
|
|
|
|
earthgod: 0,
|
|
|
|
|
matchmaker: 0,
|
|
|
|
|
wenchang: 0,
|
|
|
|
|
guanyin: 0
|
|
|
|
|
},
|
|
|
|
|
dailyPrayerCount: 0
|
2025-11-20 09:15:38 +00:00
|
|
|
});
|
|
|
|
|
|
2025-11-22 18:17:13 +00:00
|
|
|
// Game Config (loaded from API)
|
|
|
|
|
const config = ref({
|
|
|
|
|
rates: {
|
|
|
|
|
hungerDecay: 0.05,
|
|
|
|
|
happinessDecay: 0.08,
|
|
|
|
|
poopChance: 0.005,
|
|
|
|
|
sickChance: 0.1
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2025-11-22 03:23:02 +00:00
|
|
|
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: '💖' }
|
|
|
|
|
]);
|
|
|
|
|
|
2025-11-20 09:15:38 +00:00
|
|
|
// --- Internal Timers ---
|
|
|
|
|
let gameLoopId = null;
|
2025-11-22 03:23:02 +00:00
|
|
|
let tickCount = 0;
|
2025-11-20 09:15:38 +00:00
|
|
|
const TICK_RATE = 3000; // 3 seconds per tick
|
2025-11-22 03:23:02 +00:00
|
|
|
const TICKS_PER_DAY = 20; // For testing: 1 minute = 1 day (usually 28800 for 24h)
|
2025-11-20 09:15:38 +00:00
|
|
|
|
|
|
|
|
const isCleaning = ref(false);
|
|
|
|
|
|
2025-11-22 18:17:13 +00:00
|
|
|
// --- Initialization ---
|
|
|
|
|
async function initGame() {
|
|
|
|
|
try {
|
|
|
|
|
console.log('🎮 initGame: Starting...');
|
|
|
|
|
isLoading.value = true;
|
|
|
|
|
|
|
|
|
|
// Load Pet Data
|
|
|
|
|
console.log('🎮 initGame: Calling api.getPetStatus()...');
|
|
|
|
|
const petData = await api.getPetStatus();
|
|
|
|
|
console.log('🎮 initGame: Got petData:', petData);
|
|
|
|
|
|
|
|
|
|
stage.value = petData.stage;
|
|
|
|
|
state.value = petData.state;
|
|
|
|
|
stats.value = { ...stats.value, ...petData.stats }; // Merge defaults
|
|
|
|
|
|
|
|
|
|
// Load Config
|
|
|
|
|
console.log('🎮 initGame: Calling api.getGameConfig()...');
|
|
|
|
|
const configData = await api.getGameConfig();
|
|
|
|
|
console.log('🎮 initGame: Got configData:', configData);
|
|
|
|
|
|
|
|
|
|
if (configData) {
|
|
|
|
|
config.value = configData;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('🎮 initGame: Success! Setting isLoading to false');
|
|
|
|
|
isLoading.value = false;
|
|
|
|
|
console.log('🎮 initGame: isLoading.value is now:', isLoading.value);
|
|
|
|
|
console.log('🎮 initGame: typeof isLoading:', typeof isLoading);
|
|
|
|
|
console.log('🎮 initGame: isLoading object:', isLoading);
|
|
|
|
|
startGameLoop();
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.error("❌ Failed to init game:", err);
|
|
|
|
|
error.value = "Failed to load game data.";
|
|
|
|
|
isLoading.value = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function startGameLoop() {
|
|
|
|
|
if (gameLoopId) clearInterval(gameLoopId);
|
|
|
|
|
gameLoopId = setInterval(tick, TICK_RATE);
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-20 09:15:38 +00:00
|
|
|
// --- Actions ---
|
|
|
|
|
|
2025-11-22 13:25:35 +00:00
|
|
|
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);
|
2025-11-22 18:17:13 +00:00
|
|
|
|
|
|
|
|
// Sync to API
|
|
|
|
|
api.updatePetStatus({ stats: { destiny: picked } });
|
2025-11-22 13:25:35 +00:00
|
|
|
}
|
|
|
|
|
|
2025-11-22 18:17:13 +00:00
|
|
|
async function feed() {
|
2025-11-20 09:15:38 +00:00
|
|
|
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;
|
|
|
|
|
|
2025-11-22 18:17:13 +00:00
|
|
|
// Sync to API
|
|
|
|
|
await api.updatePetStatus({
|
|
|
|
|
state: 'eating',
|
|
|
|
|
stats: {
|
|
|
|
|
hunger: stats.value.hunger,
|
|
|
|
|
weight: stats.value.weight
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Chance to poop after eating
|
|
|
|
|
if (Math.random() < 0.15) {
|
|
|
|
|
setTimeout(async () => {
|
2025-11-20 09:15:38 +00:00
|
|
|
if (stats.value.poopCount < 4) {
|
|
|
|
|
stats.value.poopCount++;
|
2025-11-22 18:17:13 +00:00
|
|
|
await api.updatePetStatus({ stats: { poopCount: stats.value.poopCount } });
|
2025-11-20 09:15:38 +00:00
|
|
|
}
|
|
|
|
|
}, 4000);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-22 18:17:13 +00:00
|
|
|
async function play() {
|
2025-11-20 09:15:38 +00:00
|
|
|
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);
|
2025-11-22 18:17:13 +00:00
|
|
|
|
|
|
|
|
await api.updatePetStatus({
|
|
|
|
|
stats: {
|
|
|
|
|
happiness: stats.value.happiness,
|
|
|
|
|
weight: stats.value.weight,
|
|
|
|
|
hunger: stats.value.hunger
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2025-11-20 09:15:38 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-22 18:17:13 +00:00
|
|
|
async function clean() {
|
2025-11-20 09:15:38 +00:00
|
|
|
if (stats.value.poopCount > 0 && !isCleaning.value) {
|
|
|
|
|
isCleaning.value = true;
|
|
|
|
|
|
|
|
|
|
// Delay removal for animation
|
2025-11-22 18:17:13 +00:00
|
|
|
setTimeout(async () => {
|
2025-11-20 09:15:38 +00:00
|
|
|
stats.value.poopCount = 0;
|
|
|
|
|
stats.value.happiness += 10;
|
|
|
|
|
isCleaning.value = false;
|
2025-11-22 18:17:13 +00:00
|
|
|
|
|
|
|
|
await api.updatePetStatus({
|
|
|
|
|
stats: {
|
|
|
|
|
poopCount: 0,
|
|
|
|
|
happiness: stats.value.happiness
|
|
|
|
|
}
|
|
|
|
|
});
|
2025-11-20 09:15:38 +00:00
|
|
|
}, 2000); // 2 seconds flush animation
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-22 18:17:13 +00:00
|
|
|
async function sleep() {
|
2025-11-20 09:15:38 +00:00
|
|
|
if (isCleaning.value) return;
|
|
|
|
|
|
|
|
|
|
if (state.value === 'idle') {
|
|
|
|
|
state.value = 'sleep';
|
|
|
|
|
} else if (state.value === 'sleep') {
|
|
|
|
|
state.value = 'idle'; // Wake up
|
|
|
|
|
}
|
2025-11-22 18:17:13 +00:00
|
|
|
|
|
|
|
|
await api.updatePetStatus({ state: state.value });
|
2025-11-20 09:15:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- Game Loop ---
|
|
|
|
|
function tick() {
|
|
|
|
|
if (state.value === 'dead' || stage.value === 'egg') return;
|
|
|
|
|
|
2025-11-22 18:17:13 +00:00
|
|
|
// Use rates from config
|
|
|
|
|
const rates = config.value.rates || {
|
|
|
|
|
hungerDecay: 0.05,
|
|
|
|
|
happinessDecay: 0.08,
|
|
|
|
|
poopChance: 0.005,
|
|
|
|
|
sickChance: 0.1
|
|
|
|
|
};
|
|
|
|
|
|
2025-11-20 09:15:38 +00:00
|
|
|
// Decrease stats naturally
|
2025-11-22 18:17:13 +00:00
|
|
|
let hungerDecay = rates.hungerDecay;
|
2025-11-22 13:25:35 +00:00
|
|
|
if (stats.value.destiny?.id === 'gluttony') {
|
|
|
|
|
hungerDecay *= 1.3;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-22 18:17:13 +00:00
|
|
|
let happinessDecay = rates.happinessDecay;
|
2025-11-22 13:25:35 +00:00
|
|
|
if (stats.value.destiny?.id === 'playful') {
|
|
|
|
|
happinessDecay *= 1.2; // Faster decay
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Destiny Effect: DEX (敏捷) - Hunger decreases slower
|
|
|
|
|
if (stats.value.dex > 0) {
|
|
|
|
|
const reduction = Math.min(0.5, stats.value.dex * 0.01); // Max 50% reduction
|
|
|
|
|
hungerDecay *= (1 - reduction);
|
|
|
|
|
}
|
2025-11-20 16:00:13 +00:00
|
|
|
|
2025-11-20 09:15:38 +00:00
|
|
|
if (state.value !== 'sleep') {
|
2025-11-22 13:25:35 +00:00
|
|
|
stats.value.hunger = Math.max(0, stats.value.hunger - hungerDecay);
|
|
|
|
|
stats.value.happiness = Math.max(0, stats.value.happiness - happinessDecay);
|
2025-11-20 09:15:38 +00:00
|
|
|
} else {
|
2025-11-20 16:00:13 +00:00
|
|
|
// Slower decay when sleeping (約 1/3 速度)
|
2025-11-22 13:25:35 +00:00
|
|
|
stats.value.hunger = Math.max(0, stats.value.hunger - (hungerDecay * 0.3));
|
|
|
|
|
stats.value.happiness = Math.max(0, stats.value.happiness - (happinessDecay * 0.3));
|
2025-11-20 09:15:38 +00:00
|
|
|
}
|
|
|
|
|
|
2025-11-22 18:17:13 +00:00
|
|
|
// Random poop generation
|
|
|
|
|
if (state.value !== 'sleep' && Math.random() < rates.poopChance && stats.value.poopCount < 4 && !isCleaning.value) {
|
2025-11-20 09:15:38 +00:00
|
|
|
stats.value.poopCount++;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-22 18:17:13 +00:00
|
|
|
// Health Logic
|
2025-11-20 09:15:38 +00:00
|
|
|
if (stats.value.poopCount > 0) {
|
2025-11-20 16:00:13 +00:00
|
|
|
stats.value.health = Math.max(0, stats.value.health - (0.1 * stats.value.poopCount));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (stats.value.hunger < 20) {
|
2025-11-22 18:17:13 +00:00
|
|
|
const hungerPenalty = (20 - stats.value.hunger) * 0.02;
|
2025-11-20 16:00:13 +00:00
|
|
|
stats.value.health = Math.max(0, stats.value.health - hungerPenalty);
|
2025-11-20 09:15:38 +00:00
|
|
|
}
|
|
|
|
|
|
2025-11-20 16:00:13 +00:00
|
|
|
if (stats.value.happiness < 20) {
|
|
|
|
|
const happinessPenalty = (20 - stats.value.happiness) * 0.01;
|
|
|
|
|
stats.value.health = Math.max(0, stats.value.health - happinessPenalty);
|
2025-11-20 09:15:38 +00:00
|
|
|
}
|
|
|
|
|
|
2025-11-22 18:17:13 +00:00
|
|
|
// Sickness Check
|
|
|
|
|
let sickChance = rates.sickChance;
|
2025-11-22 13:25:35 +00:00
|
|
|
if (stats.value.destiny?.id === 'purification') {
|
|
|
|
|
sickChance *= 0.8;
|
|
|
|
|
}
|
2025-11-22 15:50:28 +00:00
|
|
|
if (stats.value.currentDeity === 'mazu' && stats.value.deityFavors?.mazu > 0) {
|
|
|
|
|
sickChance *= 0.85;
|
|
|
|
|
}
|
2025-11-22 13:25:35 +00:00
|
|
|
|
2025-11-20 09:15:38 +00:00
|
|
|
if (stats.value.health < 30 && state.value !== 'sick') {
|
2025-11-22 13:25:35 +00:00
|
|
|
if (Math.random() < sickChance) {
|
2025-11-20 09:15:38 +00:00
|
|
|
state.value = 'sick';
|
2025-11-22 18:17:13 +00:00
|
|
|
api.updatePetStatus({ state: 'sick' });
|
2025-11-20 09:15:38 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-22 18:17:13 +00:00
|
|
|
// Health Recovery
|
2025-11-22 15:50:28 +00:00
|
|
|
let healthRecovery = 0.05;
|
|
|
|
|
if (stats.value.currentDeity === 'guanyin' && stats.value.deityFavors?.guanyin > 0) {
|
|
|
|
|
healthRecovery *= 1.2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (stats.value.currentDeity === 'matchmaker' && stats.value.deityFavors?.matchmaker > 0) {
|
|
|
|
|
happinessDecay *= 0.75;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-20 16:00:13 +00:00
|
|
|
if (stats.value.poopCount === 0 && stats.value.hunger > 50 && stats.value.happiness > 50 && stats.value.health < 100 && state.value !== 'sick') {
|
2025-11-22 15:50:28 +00:00
|
|
|
stats.value.health = Math.min(100, stats.value.health + healthRecovery);
|
2025-11-20 09:15:38 +00:00
|
|
|
}
|
|
|
|
|
|
2025-11-22 03:23:02 +00:00
|
|
|
// Evolution / Growth
|
|
|
|
|
tickCount++;
|
|
|
|
|
if (tickCount >= TICKS_PER_DAY) {
|
|
|
|
|
stats.value.age++;
|
|
|
|
|
tickCount = 0;
|
|
|
|
|
checkEvolution();
|
2025-11-22 18:17:13 +00:00
|
|
|
|
|
|
|
|
// Sync stats periodically (e.g., every "day")
|
|
|
|
|
api.updatePetStatus({ stats: stats.value });
|
2025-11-22 03:23:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-22 18:17:13 +00:00
|
|
|
async function checkEvolution() {
|
2025-11-22 03:23:02 +00:00
|
|
|
// Simple evolution logic
|
2025-11-22 18:17:13 +00:00
|
|
|
let evolved = false;
|
2025-11-22 03:23:02 +00:00
|
|
|
if (stage.value === 'baby' && stats.value.age >= 3) {
|
|
|
|
|
stage.value = 'child';
|
2025-11-22 18:17:13 +00:00
|
|
|
evolved = true;
|
2025-11-22 03:23:02 +00:00
|
|
|
} else if (stage.value === 'child' && stats.value.age >= 7) {
|
|
|
|
|
stage.value = 'adult';
|
2025-11-22 18:17:13 +00:00
|
|
|
evolved = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (evolved) {
|
|
|
|
|
triggerState('happy', 2000); // Celebrate
|
|
|
|
|
await api.updatePetStatus({ stage: stage.value });
|
2025-11-22 03:23:02 +00:00
|
|
|
}
|
2025-11-20 09:15:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- Helpers ---
|
|
|
|
|
function triggerState(tempState, duration) {
|
|
|
|
|
const previousState = state.value;
|
|
|
|
|
state.value = tempState;
|
2025-11-22 18:17:13 +00:00
|
|
|
setTimeout(async () => {
|
2025-11-20 09:15:38 +00:00
|
|
|
if (state.value === tempState) { // Only revert if state hasn't changed again
|
|
|
|
|
state.value = previousState === 'sleep' ? 'idle' : 'idle';
|
2025-11-22 18:17:13 +00:00
|
|
|
// Sync state revert
|
|
|
|
|
await api.updatePetStatus({ state: state.value });
|
2025-11-20 09:15:38 +00:00
|
|
|
}
|
|
|
|
|
}, duration);
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-22 18:17:13 +00:00
|
|
|
async function hatchEgg() {
|
2025-11-20 09:15:38 +00:00
|
|
|
if (stage.value === 'egg') {
|
2025-11-22 18:17:13 +00:00
|
|
|
stage.value = 'adult'; // Skip to adult for demo
|
2025-11-20 09:15:38 +00:00
|
|
|
state.value = 'idle';
|
|
|
|
|
stats.value.hunger = 50;
|
|
|
|
|
stats.value.happiness = 50;
|
|
|
|
|
stats.value.health = 100;
|
|
|
|
|
stats.value.poopCount = 0;
|
2025-11-22 13:25:35 +00:00
|
|
|
|
|
|
|
|
// v2: Assign Destiny
|
|
|
|
|
assignDestiny();
|
|
|
|
|
|
2025-11-20 09:15:38 +00:00
|
|
|
isCleaning.value = false;
|
2025-11-22 18:17:13 +00:00
|
|
|
|
|
|
|
|
await api.updatePetStatus({
|
|
|
|
|
stage: stage.value,
|
|
|
|
|
state: state.value,
|
|
|
|
|
stats: stats.value
|
|
|
|
|
});
|
2025-11-20 09:15:38 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-22 18:17:13 +00:00
|
|
|
async function reset() {
|
|
|
|
|
await api.resetGame();
|
|
|
|
|
await initGame(); // Reload initial data
|
2025-11-22 03:23:02 +00:00
|
|
|
tickCount = 0;
|
2025-11-20 09:15:38 +00:00
|
|
|
}
|
|
|
|
|
|
2025-11-22 18:17:13 +00:00
|
|
|
async function resurrect() {
|
2025-11-22 13:25:35 +00:00
|
|
|
if (state.value !== 'dead') return;
|
|
|
|
|
|
|
|
|
|
state.value = 'idle';
|
|
|
|
|
stats.value.health = 50; // Revive with half health
|
|
|
|
|
stats.value.happiness = 50;
|
|
|
|
|
stats.value.hunger = 50;
|
|
|
|
|
|
|
|
|
|
console.log('Pet Resurrected!');
|
2025-11-22 18:17:13 +00:00
|
|
|
|
|
|
|
|
await api.updatePetStatus({
|
|
|
|
|
state: state.value,
|
|
|
|
|
stats: stats.value
|
|
|
|
|
});
|
2025-11-22 13:25:35 +00:00
|
|
|
}
|
|
|
|
|
|
2025-11-22 18:17:13 +00:00
|
|
|
async function reincarnate() {
|
2025-11-22 13:25:35 +00:00
|
|
|
// Inherit logic
|
|
|
|
|
const prevDestiny = stats.value.destiny;
|
|
|
|
|
const prevFavor = stats.value.deityFavor;
|
|
|
|
|
const nextGen = (stats.value.generation || 1) + 1;
|
|
|
|
|
|
|
|
|
|
// Reset everything
|
2025-11-22 18:17:13 +00:00
|
|
|
await api.resetGame();
|
|
|
|
|
|
|
|
|
|
// Reload but keep some stats
|
|
|
|
|
const petData = await api.getPetStatus();
|
|
|
|
|
stage.value = petData.stage;
|
|
|
|
|
state.value = petData.state;
|
|
|
|
|
stats.value = { ...stats.value, ...petData.stats };
|
2025-11-22 13:25:35 +00:00
|
|
|
|
|
|
|
|
// Apply Inheritance
|
|
|
|
|
stats.value.generation = nextGen;
|
|
|
|
|
stats.value.deityFavor = Math.floor(prevFavor * 0.2);
|
|
|
|
|
|
|
|
|
|
if (prevDestiny && prevDestiny.rarity === 2) {
|
|
|
|
|
stats.value.destiny = prevDestiny;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('Pet Reincarnated to Gen', nextGen);
|
2025-11-22 18:17:13 +00:00
|
|
|
|
|
|
|
|
await api.updatePetStatus({ stats: stats.value });
|
2025-11-22 13:25:35 +00:00
|
|
|
}
|
|
|
|
|
|
2025-11-20 09:15:38 +00:00
|
|
|
// --- Lifecycle ---
|
|
|
|
|
onMounted(() => {
|
2025-11-22 18:17:13 +00:00
|
|
|
// initGame is called manually or by parent
|
2025-11-20 09:15:38 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
onUnmounted(() => {
|
|
|
|
|
if (gameLoopId) clearInterval(gameLoopId);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
stage,
|
|
|
|
|
state,
|
|
|
|
|
stats,
|
|
|
|
|
isCleaning,
|
2025-11-22 18:17:13 +00:00
|
|
|
isLoading,
|
|
|
|
|
error,
|
|
|
|
|
initGame,
|
2025-11-20 09:15:38 +00:00
|
|
|
feed,
|
|
|
|
|
play,
|
|
|
|
|
clean,
|
|
|
|
|
sleep,
|
|
|
|
|
hatchEgg,
|
2025-11-22 03:23:02 +00:00
|
|
|
reset,
|
|
|
|
|
achievements,
|
2025-11-22 13:25:35 +00:00
|
|
|
unlockAllAchievements,
|
2025-11-22 18:17:13 +00:00
|
|
|
assignDestiny,
|
2025-11-22 13:25:35 +00:00
|
|
|
resurrect,
|
|
|
|
|
reincarnate
|
2025-11-20 09:15:38 +00:00
|
|
|
};
|
|
|
|
|
}
|