pet_data/core/achievement-system.js

375 lines
11 KiB
JavaScript
Raw Permalink 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.

// 成就系統核心
import { apiService } from './api-service.js'
import { ACHIEVEMENTS } from '../data/achievements.js'
export class AchievementSystem {
constructor(petSystem, eventSystem, templeSystem, api = apiService) {
this.petSystem = petSystem
this.eventSystem = eventSystem
this.templeSystem = templeSystem
this.api = api
this.achievements = ACHIEVEMENTS
this.unlockedAchievements = [] // 已解鎖的成就 ID 列表
this.achievementStats = {
// 動作計數
actionCounts: {
feed: 0,
play: 0,
clean: 0,
heal: 0,
sleep: 0,
pray: 0,
drawFortune: 0
},
// 事件計數
eventCount: 0,
eventTypeCounts: {
good: 0,
bad: 0,
weird: 0,
rare: 0
},
// 特殊狀態
recoveredFromDying: false,
perfectStateReached: false
}
}
// 初始化(從 API 載入已解鎖成就)
async initialize() {
try {
const saved = await this.api.getAchievements()
if (saved) {
this.unlockedAchievements = saved.unlocked || []
this.achievementStats = { ...this.achievementStats, ...saved.stats }
}
console.log(`[AchievementSystem] 載入 ${this.unlockedAchievements.length} 個已解鎖成就`)
// 重新應用已解鎖成就的加成
await this.reapplyAchievementBuffs()
} catch (error) {
console.error('[AchievementSystem] 載入成就失敗:', error)
// 降級到本地載入
const saved = localStorage.getItem('achievements')
if (saved) {
const data = JSON.parse(saved)
this.unlockedAchievements = data.unlocked || []
this.achievementStats = { ...this.achievementStats, ...data.stats }
// 重新應用已解鎖成就的加成
await this.reapplyAchievementBuffs()
}
}
}
// 重新應用已解鎖成就的加成(初始化時使用)
async reapplyAchievementBuffs() {
if (!this.petSystem) return
const unlocked = this.achievements.filter(a => this.unlockedAchievements.includes(a.id))
const achievementBuffs = {}
// 累積所有已解鎖成就的加成
for (const achievement of unlocked) {
if (achievement.reward && achievement.reward.buffs) {
for (const [key, value] of Object.entries(achievement.reward.buffs)) {
achievementBuffs[key] = (achievementBuffs[key] || 0) + value
}
}
}
// 更新寵物狀態
if (Object.keys(achievementBuffs).length > 0) {
await this.petSystem.updateState({ achievementBuffs })
// 重新計算戰鬥數值
this.petSystem.calculateCombatStats()
console.log(`[AchievementSystem] 已重新應用 ${unlocked.length} 個成就的加成`)
}
}
// 檢查成就條件
checkCondition(achievement) {
const condition = achievement.condition
const state = this.petSystem.getState()
switch (condition.type) {
case 'age':
return state.ageSeconds >= condition.value
case 'stage':
return state.stage === condition.value
case 'stats':
// 檢查所有指定屬性是否達到要求
for (const [stat, value] of Object.entries(condition.stats)) {
if ((state[stat] || 0) < value) {
return false
}
}
return true
case 'action_count':
return this.achievementStats.actionCounts[condition.action] >= condition.value
case 'deity_favor':
// 檢查任一神明好感度
const favors = state.deityFavors || {}
return Object.values(favors).some(favor => favor >= condition.value)
case 'event_count':
return this.achievementStats.eventCount >= condition.value
case 'event_type_count':
return this.achievementStats.eventTypeCounts[condition.eventType] >= condition.value
case 'recovered_from_dying':
return this.achievementStats.recoveredFromDying === condition.value
case 'perfect_state':
if (condition.value) {
return state.hunger >= 100 &&
state.happiness >= 100 &&
state.health >= 100
}
return false
default:
return false
}
}
// 檢查並解鎖成就
async checkAndUnlockAchievements() {
const newlyUnlocked = []
for (const achievement of this.achievements) {
// 如果已經解鎖,跳過
if (this.unlockedAchievements.includes(achievement.id)) {
continue
}
// 檢查條件
if (this.checkCondition(achievement)) {
await this.unlockAchievement(achievement)
newlyUnlocked.push(achievement)
}
}
return newlyUnlocked
}
// 解鎖成就
async unlockAchievement(achievement) {
if (this.unlockedAchievements.includes(achievement.id)) {
return // 已經解鎖
}
this.unlockedAchievements.push(achievement.id)
// 應用成就獎勵(永久 Buff
if (achievement.reward && achievement.reward.buffs) {
// 將成就加成添加到寵物狀態
const currentState = this.petSystem.getState()
const achievementBuffs = currentState.achievementBuffs || {}
// 合併加成
for (const [key, value] of Object.entries(achievement.reward.buffs)) {
achievementBuffs[key] = (achievementBuffs[key] || 0) + value
}
await this.petSystem.updateState({ achievementBuffs })
// 重新計算戰鬥數值
this.petSystem.calculateCombatStats()
}
// 同步到 API
try {
await this.api.saveAchievements({
unlocked: this.unlockedAchievements,
stats: this.achievementStats
})
} catch (error) {
console.warn('[AchievementSystem] API 同步失敗:', error)
// 降級到 localStorage
localStorage.setItem('achievements', JSON.stringify({
unlocked: this.unlockedAchievements,
stats: this.achievementStats
}))
}
// 顯示成就通知
console.log(`\n${'='.repeat(50)}`)
console.log(`🏆 成就解鎖:${achievement.icon} ${achievement.name}`)
console.log(`📝 ${achievement.description}`)
if (achievement.reward && achievement.reward.buffs) {
console.log(`✨ 獎勵:`)
for (const [key, value] of Object.entries(achievement.reward.buffs)) {
const sign = value > 0 ? '+' : ''
if (typeof value === 'number' && value < 1 && value > 0) {
console.log(` ${key}: ${sign}${(value * 100).toFixed(0)}%`)
} else {
console.log(` ${key}: ${sign}${value}`)
}
}
}
console.log('='.repeat(50) + '\n')
return achievement
}
// 記錄動作
async recordAction(action) {
if (this.achievementStats.actionCounts[action] !== undefined) {
this.achievementStats.actionCounts[action]++
await this.checkAndUnlockAchievements()
}
}
// 記錄事件
async recordEvent(event) {
this.achievementStats.eventCount++
if (event.type && this.achievementStats.eventTypeCounts[event.type] !== undefined) {
this.achievementStats.eventTypeCounts[event.type]++
}
await this.checkAndUnlockAchievements()
}
// 記錄從瀕死恢復
recordRecoveryFromDying() {
if (!this.achievementStats.recoveredFromDying) {
this.achievementStats.recoveredFromDying = true
this.checkAndUnlockAchievements()
}
}
// 檢查完美狀態
checkPerfectState() {
const state = this.petSystem.getState()
if (state.hunger >= 100 &&
state.happiness >= 100 &&
state.health >= 100 &&
!this.achievementStats.perfectStateReached) {
this.achievementStats.perfectStateReached = true
this.checkAndUnlockAchievements()
}
}
// 獲取已解鎖成就
getUnlockedAchievements() {
return this.achievements.filter(a => this.unlockedAchievements.includes(a.id))
}
// 獲取未解鎖成就
getLockedAchievements() {
return this.achievements.filter(a => !this.unlockedAchievements.includes(a.id))
}
// 獲取成就進度
getAchievementProgress(achievement) {
const condition = achievement.condition
const state = this.petSystem.getState()
switch (condition.type) {
case 'age':
return {
current: state.ageSeconds,
target: condition.value,
progress: Math.min(100, (state.ageSeconds / condition.value) * 100)
}
case 'action_count':
return {
current: this.achievementStats.actionCounts[condition.action] || 0,
target: condition.value,
progress: Math.min(100, ((this.achievementStats.actionCounts[condition.action] || 0) / condition.value) * 100)
}
case 'stats':
// 返回最低進度
let minProgress = 100
for (const [stat, value] of Object.entries(condition.stats)) {
const current = state[stat] || 0
const progress = Math.min(100, (current / value) * 100)
minProgress = Math.min(minProgress, progress)
}
return { progress: minProgress }
case 'deity_favor':
const favors = state.deityFavors || {}
const maxFavor = Math.max(...Object.values(favors), 0)
return {
current: maxFavor,
target: condition.value,
progress: Math.min(100, (maxFavor / condition.value) * 100)
}
case 'event_count':
return {
current: this.achievementStats.eventCount,
target: condition.value,
progress: Math.min(100, (this.achievementStats.eventCount / condition.value) * 100)
}
default:
return { progress: 0 }
}
}
// 獲取所有成就加成(用於顯示)
getAllAchievementBonuses() {
const bonuses = {}
const unlocked = this.getUnlockedAchievements()
for (const achievement of unlocked) {
if (achievement.reward && achievement.reward.buffs) {
for (const [key, value] of Object.entries(achievement.reward.buffs)) {
bonuses[key] = (bonuses[key] || 0) + value
}
}
}
return bonuses
}
// 重置成就系統(刪除寵物時使用)
async reset() {
this.unlockedAchievements = []
this.achievementStats = {
actionCounts: {
feed: 0,
play: 0,
clean: 0,
heal: 0,
sleep: 0,
pray: 0,
drawFortune: 0
},
eventCount: 0,
eventTypeCounts: {
good: 0,
bad: 0,
weird: 0,
rare: 0
},
recoveredFromDying: false,
perfectStateReached: false
}
// 同步到 API
try {
await this.api.saveAchievements({
unlocked: this.unlockedAchievements,
stats: this.achievementStats
})
} catch (error) {
console.warn('[AchievementSystem] API 同步失敗:', error)
// 降級到 localStorage
localStorage.removeItem('achievements')
}
console.log('[AchievementSystem] 成就已重置')
}
}