pet_data/core/pet-system.js

287 lines
7.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 寵物系統核心 - 與 API 整合
import { apiService } from './api-service.js'
import { PET_SPECIES } from '../data/pet-species.js'
export class PetSystem {
constructor(api = apiService) {
this.api = api
this.state = null
this.speciesConfig = null
this.tickInterval = null
this.eventCheckInterval = null
}
// 初始化寵物(從 API 載入或創建新寵物)
async initialize(speciesId = 'tinyTigerCat') {
try {
// 從 API 載入現有狀態
this.state = await this.api.getPetState()
if (!this.state) {
// 創建新寵物
this.state = this.createInitialState(speciesId)
await this.api.savePetState(this.state)
}
// 載入種族配置
this.speciesConfig = PET_SPECIES[this.state.speciesId] || PET_SPECIES[speciesId]
return this.state
} catch (error) {
console.error('[PetSystem] 初始化失敗:', error)
// 降級到本地狀態
this.state = this.createInitialState(speciesId)
this.speciesConfig = PET_SPECIES[speciesId]
return this.state
}
}
// 創建初始狀態
createInitialState(speciesId) {
const config = PET_SPECIES[speciesId]
return {
speciesId,
stage: 'baby',
hunger: 100,
happiness: 100,
health: 100,
weight: 500,
ageSeconds: 0,
poopCount: 0,
str: 0,
int: 0,
dex: 0,
luck: config.baseStats.luck || 10,
isSleeping: false,
isSick: false,
isDead: false,
currentDeityId: 'mazu',
deityFavors: {
mazu: 0,
earthgod: 0,
yuelao: 0,
wenchang: 0,
guanyin: 0
},
dailyPrayerCount: 0,
destiny: null,
buffs: [],
inventory: [],
generation: 1,
lastTickTime: Date.now()
}
}
// 更新狀態(同步到 API
async updateState(updates) {
this.state = { ...this.state, ...updates }
try {
await this.api.updatePetState(updates)
} catch (error) {
console.warn('[PetSystem] API 更新失敗,使用本地狀態:', error)
}
return this.state
}
// 獲取當前狀態
getState() {
return { ...this.state }
}
// 刪除寵物
async deletePet() {
try {
// 停止所有循環
this.stopTickLoop()
// 從 API 刪除
await this.api.deletePetState()
// 重置狀態
this.state = null
this.speciesConfig = null
return { success: true, message: '寵物已刪除' }
} catch (error) {
console.error('[PetSystem] 刪除寵物失敗:', error)
// 即使 API 失敗,也清除本地狀態
this.state = null
this.speciesConfig = null
return { success: true, message: '寵物已刪除(本地)' }
}
}
// Tick 循環(每 3 秒)
startTickLoop(callback) {
if (this.tickInterval) this.stopTickLoop()
this.tickInterval = setInterval(async () => {
await this.tick()
if (callback) callback(this.getState())
}, 3000)
}
stopTickLoop() {
if (this.tickInterval) {
clearInterval(this.tickInterval)
this.tickInterval = null
}
}
// 單次 Tick 執行
async tick() {
if (!this.state || this.state.isDead) return
const now = Date.now()
const deltaTime = (now - (this.state.lastTickTime || now)) / 1000
this.state.lastTickTime = now
// 年齡增長
this.state.ageSeconds += deltaTime
// 檢查階段轉換
this.checkStageTransition()
// 基礎數值衰減
const config = this.speciesConfig.baseStats
this.state.hunger = Math.max(0, this.state.hunger - config.hungerDecayPerTick * 100)
this.state.happiness = Math.max(0, this.state.happiness - config.happinessDecayPerTick * 100)
// 便便機率
if (Math.random() < config.poopChancePerTick) {
this.state.poopCount = Math.min(4, this.state.poopCount + 1)
}
// 生病機率
if (!this.state.isSick && Math.random() < config.sicknessChance) {
this.state.isSick = true
await this.updateState({ isSick: true })
}
// 健康檢查
if (this.state.health <= 0) {
this.state.isDead = true
await this.updateState({ isDead: true })
}
// 同步到 API
await this.updateState({
ageSeconds: this.state.ageSeconds,
hunger: this.state.hunger,
happiness: this.state.happiness,
poopCount: this.state.poopCount,
isSick: this.state.isSick,
isDead: this.state.isDead
})
}
// 檢查階段轉換
checkStageTransition() {
const config = this.speciesConfig
const currentStage = this.state.stage
const ageSeconds = this.state.ageSeconds
for (const stageConfig of config.lifecycle) {
if (stageConfig.stage === currentStage) {
// 找到下一個階段
const currentIndex = config.lifecycle.findIndex(s => s.stage === currentStage)
if (currentIndex < config.lifecycle.length - 1) {
const nextStage = config.lifecycle[currentIndex + 1]
if (ageSeconds >= nextStage.durationSeconds && nextStage.durationSeconds > 0) {
this.state.stage = nextStage.stage
this.updateState({ stage: nextStage.stage })
}
}
break
}
}
}
// 餵食
async feed(amount = 20) {
if (this.state.isDead) return { success: false, message: '寵物已死亡' }
const newHunger = Math.min(100, this.state.hunger + amount)
const newWeight = this.state.weight + (amount * 0.5)
await this.updateState({
hunger: newHunger,
weight: newWeight
})
return { success: true, hunger: newHunger, weight: newWeight }
}
// 玩耍
async play(amount = 15) {
if (this.state.isDead || this.state.isSleeping) {
return { success: false, message: '寵物無法玩耍' }
}
const newHappiness = Math.min(100, this.state.happiness + amount)
const newDex = this.state.dex + 0.5
await this.updateState({
happiness: newHappiness,
dex: newDex
})
return { success: true, happiness: newHappiness, dex: newDex }
}
// 清理便便
async cleanPoop() {
if (this.state.poopCount === 0) {
return { success: false, message: '沒有便便需要清理' }
}
const newPoopCount = 0
const newHappiness = Math.min(100, this.state.happiness + 10)
await this.updateState({
poopCount: newPoopCount,
happiness: newHappiness
})
return { success: true, poopCount: newPoopCount, happiness: newHappiness }
}
// 治療
async heal(amount = 20) {
if (this.state.isDead) return { success: false, message: '寵物已死亡' }
const newHealth = Math.min(100, this.state.health + amount)
const wasSick = this.state.isSick
await this.updateState({
health: newHealth,
isSick: false
})
return {
success: true,
health: newHealth,
isSick: false,
cured: wasSick && !this.state.isSick
}
}
// 睡覺/起床
async toggleSleep() {
if (this.state.isDead) return { success: false, message: '寵物已死亡' }
const newIsSleeping = !this.state.isSleeping
const healthChange = newIsSleeping ? 5 : 0
await this.updateState({
isSleeping: newIsSleeping,
health: Math.min(100, this.state.health + healthChange)
})
return { success: true, isSleeping: newIsSleeping }
}
}