pet_data/core/adventure-system.js

289 lines
9.4 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 { 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
}
}