// 冒險系統核心 import { apiService } from './api-service.js' import { ADVENTURES } from '../data/adventures.js' import { ENEMIES } from '../data/enemies.js' export class AdventureSystem { constructor(petSystem, inventorySystem, api = apiService) { this.petSystem = petSystem this.inventorySystem = inventorySystem this.api = api this.currentAdventure = null this.adventureTimer = null this.logs = [] } // 獲取所有冒險區域 getAdventures() { return ADVENTURES } // 開始冒險 async startAdventure(adventureId) { const adventure = ADVENTURES.find(a => a.id === adventureId) if (!adventure) return { success: false, message: '找不到該冒險區域' } const state = this.petSystem.getState() // 1. 檢查條件 if (state.isSick || state.isDead || state.isSleeping) { return { success: false, message: '寵物狀態不適合冒險' } } // 檢查等級/階段要求 (這裡簡化為檢查階段) // 實際應根據 levelRequirement 檢查 // 檢查屬性要求 if (adventure.statsRequirement) { for (const [stat, value] of Object.entries(adventure.statsRequirement)) { if ((state[stat] || 0) < value) { return { success: false, message: `屬性不足:需要 ${stat.toUpperCase()} ${value}` } } } } // 2. 檢查消耗 if (state.hunger < adventure.cost.hunger) { return { success: false, message: '飢餓度不足' } } if (state.happiness < adventure.cost.happiness) { return { success: false, message: '快樂度不足' } } // 3. 扣除消耗 await this.petSystem.updateState({ hunger: state.hunger - adventure.cost.hunger, happiness: state.happiness - adventure.cost.happiness }) // 4. 初始化冒險狀態 this.currentAdventure = { id: adventureId, startTime: Date.now(), duration: adventure.duration * 1000, // 轉為毫秒 endTime: Date.now() + adventure.duration * 1000, logs: [], rewards: { items: [], coins: 0 }, encounters: [] } this.addLog(`出發前往 ${adventure.name}!`) // 5. 啟動計時器 this.startAdventureLoop() return { success: true, adventure: this.currentAdventure } } // 冒險循環 startAdventureLoop() { if (this.adventureTimer) clearInterval(this.adventureTimer) this.adventureTimer = setInterval(async () => { if (!this.currentAdventure) { this.stopAdventureLoop() return } const now = Date.now() const adventure = ADVENTURES.find(a => a.id === this.currentAdventure.id) // 檢查是否結束 if (now >= this.currentAdventure.endTime) { await this.completeAdventure() return } // 隨機遭遇敵人 if (Math.random() < 0.1) { // 每秒 10% 機率遭遇 await this.encounterEnemy(adventure) } }, 1000) } stopAdventureLoop() { if (this.adventureTimer) { clearInterval(this.adventureTimer) this.adventureTimer = null } } // 遭遇敵人 async encounterEnemy(adventure) { if (!adventure.enemyPool || adventure.enemyPool.length === 0) return // 隨機選擇敵人 const enemyId = adventure.enemyPool[Math.floor(Math.random() * adventure.enemyPool.length)] const enemyConfig = ENEMIES[enemyId] if (!enemyConfig) return this.addLog(`遭遇了 ${enemyConfig.name}!準備戰鬥!`) // 執行戰鬥 const combatResult = this.calculateCombat(enemyConfig) // 記錄戰鬥過程 combatResult.logs.forEach(log => this.addLog(log)) if (combatResult.win) { this.addLog(`戰鬥勝利!`) // 計算掉落 if (enemyConfig.drops) { for (const drop of enemyConfig.drops) { if (Math.random() < drop.chance) { // 檢查是否為金幣 if (drop.itemId === 'gold_coin') { // 金幣直接加到獎勵中 if (!this.currentAdventure.rewards.coins) { this.currentAdventure.rewards.coins = 0 } this.currentAdventure.rewards.coins += drop.count || 1 this.addLog(`獲得金幣:${drop.count || 1} 💰`) } else { // 其他道具加到物品列表 this.currentAdventure.rewards.items.push({ itemId: drop.itemId, count: drop.count || 1, name: drop.itemId // 暫時用 ID,實際應查表 }) this.addLog(`獲得戰利品:${drop.itemId} x${drop.count || 1}`) } } } } } else { this.addLog(`戰鬥失敗... 寵物受傷逃跑了。`) // 扣除健康 const state = this.petSystem.getState() await this.petSystem.updateState({ health: Math.max(0, state.health - 10) }) } } // 計算戰鬥 (簡化版回合制) calculateCombat(enemy) { const state = this.petSystem.getState() const petStats = { hp: state.health, // 用健康度當 HP attack: state.attack || 10, defense: state.defense || 0, speed: state.speed || 10 } const enemyStats = { ...enemy.stats } const logs = [] let round = 1 let win = false while (round <= 10) { // 最多 10 回合 // 速度決定先手 const petFirst = petStats.speed >= enemyStats.speed // 雙方攻擊 const attacker = petFirst ? petStats : enemyStats const defender = petFirst ? enemyStats : petStats const attackerName = petFirst ? '你' : enemy.name const defenderName = petFirst ? enemy.name : '你' // 第一擊 let damage = Math.max(1, attacker.attack - defender.defense) // 隨機浮動 0.8 ~ 1.2 damage = Math.floor(damage * (0.8 + Math.random() * 0.4)) defender.hp -= damage logs.push(`[回合${round}] ${attackerName} 對 ${defenderName} 造成 ${damage} 點傷害`) if (defender.hp <= 0) { win = petFirst break } // 第二擊 damage = Math.max(1, defender.attack - attacker.defense) damage = Math.floor(damage * (0.8 + Math.random() * 0.4)) attacker.hp -= damage logs.push(`[回合${round}] ${defenderName} 對 ${attackerName} 造成 ${damage} 點傷害`) if (attacker.hp <= 0) { win = !petFirst break } round++ } if (round > 10) { logs.push('戰鬥超時,雙方平手(視為失敗)') win = false } return { win, logs } } // 完成冒險 async completeAdventure() { this.stopAdventureLoop() const adventure = ADVENTURES.find(a => a.id === this.currentAdventure.id) this.addLog('冒險結束!') // 發放基礎獎勵 if (adventure.rewards) { if (adventure.rewards.items) { for (const reward of adventure.rewards.items) { if (Math.random() < reward.chance) { this.currentAdventure.rewards.items.push({ itemId: reward.itemId, count: 1, name: reward.itemId }) } } } } // 實際發放道具到背包 if (this.inventorySystem) { for (const item of this.currentAdventure.rewards.items) { await this.inventorySystem.addItem(item.itemId, item.count) } } // 發放金幣 if (this.currentAdventure.rewards.coins > 0) { const state = this.petSystem.getState() await this.petSystem.updateState({ coins: (state.coins || 0) + this.currentAdventure.rewards.coins }) this.addLog(`獲得金幣:${this.currentAdventure.rewards.coins} 💰`) } // 觸發完成回調(如果有 UI 監聽) if (this.onAdventureComplete) { this.onAdventureComplete(this.currentAdventure) } this.currentAdventure = null } // 添加日誌 addLog(message) { if (this.currentAdventure) { const time = new Date().toLocaleTimeString() this.currentAdventure.logs.push(`[${time}] ${message}`) // 觸發更新回調 if (this.onLogUpdate) { this.onLogUpdate(this.currentAdventure.logs) } } } // 獲取當前冒險狀態 getCurrentAdventure() { return this.currentAdventure } }