pet_data/core/event-system.js

400 lines
11 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'
export class EventSystem {
constructor(petSystem, api = apiService, achievementSystem = null, inventorySystem = null) {
this.petSystem = petSystem
this.api = api
this.achievementSystem = achievementSystem
this.inventorySystem = inventorySystem
this.events = []
this.buffManager = new BuffManager()
this.eventHistory = []
this.eventCheckInterval = null
this.lastEventCheckTime = 0
}
// 初始化(從 API 載入事件配置)
async initialize() {
try {
this.events = await this.api.getEvents()
console.log(`[EventSystem] 載入 ${this.events.length} 個事件`)
} catch (error) {
console.error('[EventSystem] 載入事件失敗:', error)
// 降級到本地載入
const { EVENT_CONFIG } = await import('../data/events.js')
this.events = EVENT_CONFIG
}
}
// 啟動事件檢查循環(从配置读取间隔)
startEventCheck() {
if (this.eventCheckInterval) this.stopEventCheck()
// 从配置读取间隔时间
const petConfig = this.petSystem.speciesConfig
const interval = petConfig?.baseStats?.eventCheckInterval || 10000
this.eventCheckInterval = setInterval(() => {
this.checkTriggers()
}, interval)
}
stopEventCheck() {
if (this.eventCheckInterval) {
clearInterval(this.eventCheckInterval)
this.eventCheckInterval = null
}
}
// 隨機選擇事件(依權重)
selectRandomEvent() {
const totalWeight = this.events.reduce((sum, e) => sum + e.weight, 0)
let rand = Math.random() * totalWeight
for (const event of this.events) {
rand -= event.weight
if (rand <= 0) return event
}
return this.events[0] // fallback
}
// 檢查觸發條件10% 機率 + 條件檢查)
async checkTriggers() {
const petState = this.petSystem.getState()
if (petState.isDead) return
// 睡眠時不觸發事件(類似暫停)
if (petState.isSleeping) return
// 10% 機率觸發
if (Math.random() >= 0.1) return
const event = this.selectRandomEvent()
// 檢查條件
if (event.condition && !event.condition(petState)) {
return
}
// 觸發事件
await this.triggerEvent(event.id, petState)
}
// 觸發事件(同步到 API
async triggerEvent(eventId, petState = null) {
const event = this.events.find(e => e.id === eventId)
if (!event) {
console.warn(`[EventSystem] 找不到事件: ${eventId}`)
return null
}
const currentState = petState || this.petSystem.getState()
// 檢查條件
if (event.condition && !event.condition(currentState)) {
console.log(`\n❌ 事件 ${event.id} 觸發失敗`)
console.log(` 條件: ${event.conditionDescription || '未定義'}`)
console.log(` 當前狀態:`)
console.log(` - 飢餓: ${currentState.hunger.toFixed(1)}`)
console.log(` - 快樂: ${currentState.happiness.toFixed(1)}`)
console.log(` - 健康: ${currentState.health.toFixed(1)}`)
console.log(` - 運勢: ${currentState.luck.toFixed(1)}`)
console.log(` - 體重: ${currentState.weight.toFixed(1)}`)
console.log(` - DEX: ${currentState.dex.toFixed(1)}`)
console.log(` - INT: ${currentState.int.toFixed(1)}`)
return null
}
console.log(`\n[事件觸發] ${event.id} (${event.type}): 機率檢查通過!`)
// 執行效果
const results = []
for (const effect of event.effects) {
const result = await this.executeEffect(effect, currentState)
results.push(result)
}
// 記錄歷史
this.eventHistory.push({
timestamp: Date.now(),
eventId: event.id,
eventType: event.type,
effects: results
})
// 通知成就系統(如果存在)
if (this.achievementSystem) {
await this.achievementSystem.recordEvent(event)
}
// 同步到 API
try {
await this.api.triggerEvent(eventId, currentState)
} catch (error) {
console.warn('[EventSystem] API 同步失敗:', error)
}
return { event, results }
}
// 執行效果
async executeEffect(effect, petState) {
switch (effect.type) {
case 'modifyStats':
return await this.modifyStats(effect.payload, petState)
case 'addBuff':
return await this.addBuff(effect.payload)
case 'addItem':
return await this.addItem(effect.payload)
case 'spawnPoop':
return await this.spawnPoop(effect.payload)
case 'templeFavor':
return await this.modifyTempleFavor(effect.payload)
case 'logMessage':
console.log(`[訊息] ${effect.payload}`)
return { type: 'logMessage', message: effect.payload }
default:
console.warn(`[EventSystem] 未知效果類型: ${effect.type}`)
return null
}
}
// 修改屬性
async modifyStats(payload, petState) {
// 獲取最新狀態(因為 petState 可能是舊的快照)
const currentState = this.petSystem.getState()
const updates = {}
for (const [key, value] of Object.entries(payload)) {
if (key === 'isSleeping' || key === 'isSick' || key === 'isDead') {
updates[key] = value
} else {
const current = currentState[key] || 0
let newValue = current
if (value > 0) {
// 增加屬性:如果是自然增長(非道具),上限為 100
// 如果當前已經 >= 100則不再增加除非是道具但這裡是事件系統
// 如果當前 < 100則最多增加到 100
newValue = current < 100 ? Math.min(100, current + value) : current
} else {
// 減少屬性:總是生效,最低為 0
newValue = Math.max(0, current + value)
}
updates[key] = newValue
console.log(`[效果] ${key} ${value > 0 ? '+' : ''}${value} (${current.toFixed(1)}${newValue.toFixed(1)})`)
}
}
await this.petSystem.updateState(updates)
return { type: 'modifyStats', updates }
}
// 添加 Buff
async addBuff(buffData) {
this.buffManager.addBuff(buffData)
// 同步到 API
try {
await this.api.applyBuff(buffData)
} catch (error) {
console.warn('[EventSystem] Buff API 同步失敗:', error)
}
return { type: 'addBuff', buff: buffData }
}
// 生成便便
async spawnPoop(payload) {
const count = payload?.count || 1
const currentPoop = this.petSystem.getState().poopCount
const newPoop = Math.min(4, currentPoop + count)
await this.petSystem.updateState({ poopCount: newPoop })
console.log(`[效果] 生成便便: ${count} 個,總數: ${newPoop}`)
return { type: 'spawnPoop', count, total: newPoop }
}
// 修改神明好感度
async modifyTempleFavor(payload) {
const { deityId, amount } = payload
const currentState = this.petSystem.getState()
const currentFavor = currentState.deityFavors[deityId] || 0
const newFavor = Math.max(0, Math.min(100, currentFavor + amount))
await this.petSystem.updateState({
deityFavors: {
...currentState.deityFavors,
[deityId]: newFavor
}
})
console.log(`[效果] ${deityId} 好感度 ${amount > 0 ? '+' : ''}${amount}${newFavor}`)
return { type: 'templeFavor', deityId, favor: newFavor }
}
// 添加道具效果
async addItem(payload) {
if (!this.inventorySystem) {
console.warn('[EventSystem] 背包系統未初始化,無法添加道具')
return { type: 'addItem', success: false, message: '背包系統未初始化' }
}
const { itemId, count = 1 } = payload
const result = await this.inventorySystem.addItem(itemId, count)
if (result.success) {
console.log(`[事件] 獲得道具: ${result.item.name} x${count}`)
return { type: 'addItem', success: true, itemId, count, item: result.item }
} else {
console.warn(`[事件] 添加道具失敗: ${result.message}`)
return { type: 'addItem', success: false, message: result.message }
}
}
// 應用 Buff 到狀態(每 tick 呼叫)
async applyBuffs() {
const petState = this.petSystem.getState()
const buffs = this.buffManager.getActiveBuffs()
// 計算最終屬性(考慮 Buff
const finalStats = this.calculateFinalStats(petState, buffs)
// 更新狀態
await this.petSystem.updateState(finalStats)
return finalStats
}
// 計算最終屬性Base + Flat + Percent
calculateFinalStats(baseState, buffs) {
const stats = { ...baseState }
// 收集所有 Buff 的 flat 和 percent 加成
const flatMods = {}
const percentMods = {}
buffs.forEach(buff => {
if (buff.flat) {
Object.entries(buff.flat).forEach(([key, value]) => {
flatMods[key] = (flatMods[key] || 0) + value
})
}
if (buff.percent) {
Object.entries(buff.percent).forEach(([key, value]) => {
percentMods[key] = (percentMods[key] || 0) + value
})
}
})
// 應用加成
Object.keys(stats).forEach(key => {
if (typeof stats[key] === 'number' && key !== 'ageSeconds' && key !== 'weight' && key !== 'generation') {
const base = stats[key]
const flat = flatMods[key] || 0
const percent = percentMods[key] || 0
stats[key] = Math.max(0, Math.min(100, (base + flat) * (1 + percent)))
}
})
return stats
}
// 獲取事件歷史
getHistory() {
return [...this.eventHistory]
}
// 獲取 Buff 管理器
getBuffManager() {
return this.buffManager
}
// Debug: 強制觸發事件
async debugTriggerEvent(eventId = null) {
let event
if (eventId) {
event = this.events.find(e => e.id === eventId)
if (!event) {
console.warn(`[Debug] 找不到事件: ${eventId}`)
return
}
} else {
event = this.selectRandomEvent()
}
console.log(`[Debug] 強制觸發事件: ${event.id}`)
await this.triggerEvent(event.id)
return event
}
}
// Buff 管理器
class BuffManager {
constructor() {
this.buffs = []
}
addBuff(buff) {
// 檢查是否可堆疊
const existing = this.buffs.find(b => b.id === buff.id)
if (existing && !buff.stacks) {
// 不可堆疊,重置持續時間
existing.currentTicks = buff.durationTicks
console.log(`[Buff] 重置: ${buff.name} (持續 ${buff.durationTicks} ticks)`)
} else {
// 可堆疊或新 Buff
this.buffs.push({
...buff,
currentTicks: buff.durationTicks,
currentStacks: (existing?.currentStacks || 0) + 1
})
console.log(`[Buff] 新增: ${buff.name} (持續 ${buff.durationTicks} ticks)`)
}
}
tick() {
this.buffs = this.buffs.filter(b => {
if (b.durationTicks === Infinity) return true // 永久 Buff
b.currentTicks--
if (b.currentTicks <= 0) {
console.log(`[Buff] 過期: ${b.name}`)
return false
}
return true
})
}
getActiveBuffs() {
return this.buffs.filter(b => b.currentTicks > 0 || b.durationTicks === Infinity)
}
getFinalModifier(statKey) {
const activeBuffs = this.getActiveBuffs()
let flatTotal = 0
let percentTotal = 0
activeBuffs.forEach(b => {
if (b.flat?.[statKey]) flatTotal += b.flat[statKey]
if (b.percent?.[statKey]) percentTotal += b.percent[statKey]
})
return { flat: flatTotal, percent: percentTotal }
}
}