- 修复战斗胜利后未获得武器技能经验的问题 (initCombat未传递skillExpReward) - 每级武器技能提供5%武器伤害加成(已实现,无需修改) - 实现义体安装/卸载功能,支持NPC对话交互 - StatusPanel添加义体装备槽显示 - MapPanel修复NPC对话import问题 - 新增成就系统框架 - 添加项目文档CLAUDE.md Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
788 lines
23 KiB
JavaScript
788 lines
23 KiB
JavaScript
/**
|
||
* 事件系统 - 事件触发检测、对话管理
|
||
* Phase 6 核心系统实现
|
||
*/
|
||
|
||
import { NPC_CONFIG } from '@/config/npcs.js'
|
||
import { EVENT_CONFIG } from '@/config/events.js'
|
||
import { LOCATION_CONFIG } from '@/config/locations.js'
|
||
import { SKILL_CONFIG } from '@/config/skills.js'
|
||
import { ENEMY_CONFIG } from '@/config/enemies.js'
|
||
import { ITEM_CONFIG } from '@/config/items.js'
|
||
import { unlockSkill } from './skillSystem.js'
|
||
import { addItemToInventory } from './itemSystem.js'
|
||
import { initCombat, getEnvironmentType } from './combatSystem.js'
|
||
import { installProsthetic, removeProsthetic } from './prostheticSystem.js'
|
||
|
||
/**
|
||
* 检查并触发事件
|
||
* @param {Object} gameStore - 游戏Store
|
||
* @param {Object} playerStore - 玩家Store
|
||
* @param {String} triggerType - 触发类型 'enter' | 'action' | 'timer'
|
||
* @param {Object} context - 触发上下文
|
||
* @returns {Object|null} 触发的事件信息
|
||
*/
|
||
export function checkAndTriggerEvent(gameStore, playerStore, triggerType = 'enter', context = {}) {
|
||
// 检查首次进入事件
|
||
if (!playerStore.flags) {
|
||
playerStore.flags = {}
|
||
}
|
||
|
||
// 新游戏初始化事件
|
||
if (!playerStore.flags.introTriggered) {
|
||
playerStore.flags.introTriggered = true
|
||
triggerEvent(gameStore, 'intro', { playerStore })
|
||
return { eventId: 'intro', type: 'story' }
|
||
}
|
||
|
||
// 检查位置事件
|
||
if (triggerType === 'enter') {
|
||
const locationEvent = checkLocationEvent(gameStore, playerStore, context.locationId)
|
||
if (locationEvent) {
|
||
return locationEvent
|
||
}
|
||
}
|
||
|
||
// 检查NPC对话事件
|
||
if (triggerType === 'npc') {
|
||
const npcEvent = checkNPCDialogue(gameStore, playerStore, context.npcId)
|
||
if (npcEvent) {
|
||
return npcEvent
|
||
}
|
||
}
|
||
|
||
// 检查战斗相关事件
|
||
if (triggerType === 'combat') {
|
||
const combatEvent = checkCombatEvent(gameStore, playerStore, context)
|
||
if (combatEvent) {
|
||
return combatEvent
|
||
}
|
||
}
|
||
|
||
// 检查解锁事件
|
||
if (triggerType === 'unlock') {
|
||
const unlockEvent = checkUnlockEvent(gameStore, playerStore, context)
|
||
if (unlockEvent) {
|
||
return unlockEvent
|
||
}
|
||
}
|
||
|
||
return null
|
||
}
|
||
|
||
/**
|
||
* 检查位置事件
|
||
*/
|
||
function checkLocationEvent(gameStore, playerStore, locationId) {
|
||
const location = LOCATION_CONFIG[locationId]
|
||
if (!location) return null
|
||
|
||
// 首次进入地下室警告
|
||
if (locationId === 'basement' && !playerStore.flags.basementFirstEnter) {
|
||
playerStore.flags.basementFirstEnter = true
|
||
triggerEvent(gameStore, 'dark_warning', { locationName: location.name })
|
||
return { eventId: 'dark_warning', type: 'tips' }
|
||
}
|
||
|
||
// 首次进入野外战斗提示
|
||
if (locationId === 'wild1' && !playerStore.flags.wild1FirstEnter) {
|
||
playerStore.flags.wild1FirstEnter = true
|
||
triggerEvent(gameStore, 'first_combat', { locationName: location.name })
|
||
return { eventId: 'first_combat', type: 'tips' }
|
||
}
|
||
|
||
return null
|
||
}
|
||
|
||
/**
|
||
* 检查NPC对话事件
|
||
*/
|
||
function checkNPCDialogue(gameStore, playerStore, npcId) {
|
||
const npc = NPC_CONFIG[npcId]
|
||
if (!npc) return null
|
||
|
||
// 启动NPC对话
|
||
startNPCDialogue(gameStore, npcId, 'first')
|
||
return { eventId: `npc_${npcId}`, type: 'dialogue' }
|
||
}
|
||
|
||
/**
|
||
* 检查战斗事件
|
||
*/
|
||
function checkCombatEvent(gameStore, playerStore, context) {
|
||
// 检查Boss解锁
|
||
if (context.result === 'victory' && context.enemyId === 'wild_dog') {
|
||
const killCount = (playerStore.killCount || {})[context.enemyId] || 0
|
||
playerStore.killCount = playerStore.killCount || {}
|
||
playerStore.killCount[context.enemyId] = killCount + 1
|
||
|
||
// 击杀5只野狗解锁Boss巢
|
||
if (playerStore.killCount[context.enemyId] >= 5 && !playerStore.flags.bossUnlockTriggered) {
|
||
playerStore.flags.bossUnlockTriggered = true
|
||
triggerEvent(gameStore, 'boss_unlock', {})
|
||
return { eventId: 'boss_unlock', type: 'unlock' }
|
||
}
|
||
}
|
||
|
||
// Boss击败事件
|
||
if (context.result === 'victory' && context.enemyId === 'test_boss' && !playerStore.flags.bossDefeatTriggered) {
|
||
playerStore.flags.bossDefeatTriggered = true
|
||
triggerEvent(gameStore, 'boss_defeat', {})
|
||
return { eventId: 'boss_defeat', type: 'story' }
|
||
}
|
||
|
||
return null
|
||
}
|
||
|
||
/**
|
||
* 检查解锁事件
|
||
*/
|
||
function checkUnlockEvent(gameStore, playerStore, context) {
|
||
// 区域解锁事件
|
||
if (context.unlockType === 'location' && context.locationId === 'boss_lair') {
|
||
// 已在战斗事件中处理
|
||
}
|
||
|
||
return null
|
||
}
|
||
|
||
/**
|
||
* 触发事件
|
||
* @param {Object} gameStore - 游戏Store
|
||
* @param {String} eventId - 事件ID
|
||
* @param {Object} context - 事件上下文
|
||
*/
|
||
export function triggerEvent(gameStore, eventId, context = {}) {
|
||
const config = EVENT_CONFIG[eventId]
|
||
if (!config) {
|
||
console.warn(`Event ${eventId} not found`)
|
||
return
|
||
}
|
||
|
||
// 构建事件数据
|
||
const eventData = {
|
||
id: eventId,
|
||
type: config.type,
|
||
title: config.title || '',
|
||
text: config.text || '',
|
||
npc: config.npcId ? NPC_CONFIG[config.npcId] : null,
|
||
choices: config.choices || [],
|
||
actions: config.actions || [],
|
||
context,
|
||
triggers: config.triggers || []
|
||
}
|
||
|
||
// 处理动态文本(替换变量)
|
||
if (config.textTemplate && context) {
|
||
eventData.text = renderTemplate(config.textTemplate, context)
|
||
}
|
||
|
||
// 设置当前事件
|
||
gameStore.currentEvent = eventData
|
||
|
||
// 打开事件抽屉
|
||
gameStore.drawerState.event = true
|
||
}
|
||
|
||
/**
|
||
* 渲染模板文本
|
||
*/
|
||
function renderTemplate(template, context) {
|
||
let text = template
|
||
for (const [key, value] of Object.entries(context)) {
|
||
text = text.replace(new RegExp(`\\{${key}\\}`, 'g'), value)
|
||
}
|
||
return text
|
||
}
|
||
|
||
/**
|
||
* 启动NPC对话
|
||
* @param {Object} gameStore - 游戏Store
|
||
* @param {String} npcId - NPC ID
|
||
* @param {String} dialogueKey - 对话键名
|
||
* @param {Object} playerStore - 玩家Store(用于动态选项生成)
|
||
*/
|
||
export function startNPCDialogue(gameStore, npcId, dialogueKey = 'first', playerStore = null) {
|
||
const npc = NPC_CONFIG[npcId]
|
||
if (!npc) {
|
||
console.warn(`NPC ${npcId} not found`)
|
||
return
|
||
}
|
||
|
||
const dialogue = npc.dialogue[dialogueKey]
|
||
if (!dialogue) {
|
||
console.warn(`Dialogue ${dialogueKey} not found for NPC ${npcId}`)
|
||
return
|
||
}
|
||
|
||
// 处理模板变量
|
||
let text = dialogue.text
|
||
if (playerStore) {
|
||
text = renderDialogueTemplate(text, playerStore, dialogue)
|
||
}
|
||
|
||
// 生成选项列表(包括动态选项)
|
||
let choices = dialogue.choices || []
|
||
if (dialogue.dynamicChoices && playerStore) {
|
||
const dynamicChoices = generateDynamicChoices(dialogue.dynamicChoices, playerStore)
|
||
choices = [...dynamicChoices, ...choices]
|
||
}
|
||
|
||
// 构建事件数据
|
||
const eventData = {
|
||
id: `npc_${npcId}_${dialogueKey}`,
|
||
type: 'dialogue',
|
||
title: npc.name,
|
||
text: text,
|
||
npc: npc,
|
||
choices: choices,
|
||
currentDialogue: dialogueKey,
|
||
npcId
|
||
}
|
||
|
||
gameStore.currentEvent = eventData
|
||
gameStore.drawerState.event = true
|
||
}
|
||
|
||
/**
|
||
* 渲染对话模板(替换变量)
|
||
*/
|
||
function renderDialogueTemplate(template, playerStore, dialogue) {
|
||
let text = template
|
||
|
||
// 替换义体列表
|
||
if (text.includes('{prosthetic_list}')) {
|
||
const prostheticList = getProstheticInventoryList(playerStore)
|
||
text = text.replace('{prosthetic_list}', prostheticList)
|
||
}
|
||
|
||
// 替换已装备的义体
|
||
if (text.includes('{equipped_prosthetic}')) {
|
||
const equipped = playerStore.equipment?.prosthetic
|
||
const equippedText = equipped ? `${equipped.name} (${equipped.description})` : '无'
|
||
text = text.replace('{equipped_prosthetic}', equippedText)
|
||
}
|
||
|
||
return text
|
||
}
|
||
|
||
/**
|
||
* 获取玩家背包中的义体列表
|
||
*/
|
||
function getProstheticInventoryList(playerStore) {
|
||
const prosthetics = playerStore.inventory.filter(item => item.type === 'prosthetic')
|
||
if (prosthetics.length === 0) {
|
||
return '(你没有义体可以安装)'
|
||
}
|
||
return prosthetics.map(p => `• ${p.name}: ${p.description}`).join('\n')
|
||
}
|
||
|
||
/**
|
||
* 生成动态选项
|
||
*/
|
||
function generateDynamicChoices(type, playerStore) {
|
||
switch (type) {
|
||
case 'prosthetic_inventory':
|
||
// 生成义体安装选项
|
||
const prosthetics = playerStore.inventory.filter(item => item.type === 'prosthetic')
|
||
if (prosthetics.length === 0) {
|
||
return []
|
||
}
|
||
return prosthetics.map(p => ({
|
||
text: `安装 ${p.name}`,
|
||
action: 'install_prosthetic',
|
||
actionData: { itemId: p.id }
|
||
}))
|
||
|
||
default:
|
||
return []
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 处理对话选择
|
||
* @param {Object} gameStore - 游戏Store
|
||
* @param {Object} playerStore - 玩家Store
|
||
* @param {Object} choice - 选择项
|
||
* @returns {Object} 处理结果
|
||
*/
|
||
export function handleDialogueChoice(gameStore, playerStore, choice) {
|
||
const result = {
|
||
success: true,
|
||
closeEvent: false,
|
||
message: ''
|
||
}
|
||
|
||
// 处理对话动作
|
||
if (choice.action) {
|
||
const actionResult = processDialogueAction(gameStore, playerStore, choice.action, choice.actionData)
|
||
result.message = actionResult.message
|
||
if (actionResult.closeEvent) {
|
||
result.closeEvent = true
|
||
}
|
||
}
|
||
|
||
// 检查下一步
|
||
if (choice.next === null) {
|
||
// 对话结束
|
||
result.closeEvent = true
|
||
} else if (choice.next) {
|
||
// 继续对话
|
||
const currentEvent = gameStore.currentEvent
|
||
if (currentEvent && currentEvent.npc) {
|
||
startNPCDialogue(gameStore, currentEvent.npc.id, choice.next, playerStore)
|
||
result.closeEvent = false
|
||
}
|
||
} else if (!choice.action) {
|
||
// 没有next也没有action,关闭
|
||
result.closeEvent = true
|
||
}
|
||
|
||
// 如果需要关闭事件
|
||
if (result.closeEvent) {
|
||
gameStore.drawerState.event = false
|
||
gameStore.currentEvent = null
|
||
}
|
||
|
||
return result
|
||
}
|
||
|
||
/**
|
||
* 处理对话动作
|
||
* @param {Object} gameStore - 游戏Store
|
||
* @param {Object} playerStore - 玩家Store
|
||
* @param {String} action - 动作类型
|
||
* @param {Object} actionData - 动作数据
|
||
* @returns {Object} 处理结果
|
||
*/
|
||
export function processDialogueAction(gameStore, playerStore, action, actionData = {}) {
|
||
switch (action) {
|
||
case 'give_stick':
|
||
// 给予木棍
|
||
const stickResult = addItemToInventory(playerStore, 'wooden_stick', 1, 100)
|
||
if (stickResult.success) {
|
||
// 自动解锁木棍精通技能
|
||
unlockSkill(playerStore, 'stick_mastery')
|
||
if (gameStore.addLog) {
|
||
gameStore.addLog('获得了 木棍', 'reward')
|
||
}
|
||
}
|
||
return { success: true, message: '获得了木棍', closeEvent: false }
|
||
|
||
case 'open_shop':
|
||
// 打开商店
|
||
gameStore.drawerState.event = false
|
||
// TODO: 打开商店抽屉
|
||
gameStore.drawerState.shop = true
|
||
return { success: true, message: '', closeEvent: true }
|
||
|
||
case 'give_item':
|
||
// 给予物品
|
||
if (actionData.itemId) {
|
||
const itemResult = addItemToInventory(
|
||
playerStore,
|
||
actionData.itemId,
|
||
actionData.count || 1,
|
||
actionData.quality || null
|
||
)
|
||
if (itemResult.success && gameStore.addLog) {
|
||
gameStore.addLog(`获得了 ${itemResult.item.name}`, 'reward')
|
||
}
|
||
return { success: true, message: `获得了 ${actionData.itemId}`, closeEvent: false }
|
||
}
|
||
return { success: false, message: '物品参数错误', closeEvent: false }
|
||
|
||
case 'unlock_skill':
|
||
// 解锁技能
|
||
if (actionData.skillId) {
|
||
unlockSkill(playerStore, actionData.skillId)
|
||
if (gameStore.addLog) {
|
||
const skillConfig = SKILL_CONFIG[actionData.skillId]
|
||
gameStore.addLog(`解锁了技能: ${skillConfig?.name || actionData.skillId}`, 'system')
|
||
}
|
||
return { success: true, message: `解锁了技能 ${actionData.skillId}`, closeEvent: false }
|
||
}
|
||
return { success: false, message: '技能参数错误', closeEvent: false }
|
||
|
||
case 'heal':
|
||
// 治疗
|
||
if (actionData.amount) {
|
||
playerStore.currentStats.health = Math.min(
|
||
playerStore.currentStats.maxHealth,
|
||
playerStore.currentStats.health + actionData.amount
|
||
)
|
||
return { success: true, message: `恢复了${actionData.amount}生命`, closeEvent: false }
|
||
}
|
||
return { success: false, message: '治疗参数错误', closeEvent: false }
|
||
|
||
case 'restore_stamina':
|
||
// 恢复耐力
|
||
if (actionData.amount) {
|
||
playerStore.currentStats.stamina = Math.min(
|
||
playerStore.currentStats.maxStamina,
|
||
playerStore.currentStats.stamina + actionData.amount
|
||
)
|
||
return { success: true, message: `恢复了${actionData.amount}耐力`, closeEvent: false }
|
||
}
|
||
return { success: false, message: '耐力参数错误', closeEvent: false }
|
||
|
||
case 'teleport':
|
||
// 传送
|
||
if (actionData.locationId) {
|
||
playerStore.currentLocation = actionData.locationId
|
||
if (gameStore.addLog) {
|
||
const location = LOCATION_CONFIG[actionData.locationId]
|
||
gameStore.addLog(`来到了 ${location?.name || actionData.locationId}`, 'system')
|
||
}
|
||
return { success: true, message: `传送到 ${actionData.locationId}`, closeEvent: true }
|
||
}
|
||
return { success: false, message: '位置参数错误', closeEvent: false }
|
||
|
||
case 'add_currency':
|
||
// 添加货币
|
||
if (actionData.amount) {
|
||
playerStore.currency.copper += actionData.amount
|
||
if (gameStore.addLog) {
|
||
gameStore.addLog(`获得了 ${actionData.amount} 铜币`, 'reward')
|
||
}
|
||
return { success: true, message: `获得了${actionData.amount}铜币`, closeEvent: false }
|
||
}
|
||
return { success: false, message: '货币参数错误', closeEvent: false }
|
||
|
||
case 'set_flag':
|
||
// 设置标志
|
||
if (actionData.flag) {
|
||
playerStore.flags = playerStore.flags || {}
|
||
playerStore.flags[actionData.flag] = actionData.value !== undefined ? actionData.value : true
|
||
return { success: true, message: `设置标志 ${actionData.flag}`, closeEvent: false }
|
||
}
|
||
return { success: false, message: '标志参数错误', closeEvent: false }
|
||
|
||
case 'check_flag':
|
||
// 检查标志(返回结果由调用者处理)
|
||
if (actionData.flag) {
|
||
const value = playerStore.flags?.[actionData.flag]
|
||
return {
|
||
success: true,
|
||
message: '',
|
||
closeEvent: false,
|
||
flagValue: value
|
||
}
|
||
}
|
||
return { success: false, message: '标志参数错误', closeEvent: false }
|
||
|
||
case 'start_combat':
|
||
// 开始战斗
|
||
if (actionData.enemyId) {
|
||
gameStore.drawerState.event = false
|
||
// 战斗已在探索事件中初始化,这里只是关闭事件抽屉
|
||
return { success: true, message: '开始战斗', closeEvent: true }
|
||
}
|
||
return { success: false, message: '敌人参数错误', closeEvent: false }
|
||
|
||
case 'flee':
|
||
// 逃跑
|
||
gameStore.drawerState.event = false
|
||
return tryFlee(gameStore, playerStore)
|
||
|
||
case 'close':
|
||
// 直接关闭
|
||
return { success: true, message: '', closeEvent: true }
|
||
|
||
case 'install_prosthetic':
|
||
// 安装义体
|
||
if (actionData.itemId) {
|
||
const item = playerStore.inventory.find(i => i.id === actionData.itemId)
|
||
if (!item) {
|
||
return { success: false, message: '找不到该义体', closeEvent: false }
|
||
}
|
||
const installResult = installProsthetic(playerStore, gameStore, item)
|
||
if (installResult.success) {
|
||
// 从背包移除义体
|
||
const index = playerStore.inventory.findIndex(i => i.id === actionData.itemId)
|
||
if (index > -1) {
|
||
playerStore.inventory.splice(index, 1)
|
||
}
|
||
// 解锁义体技能
|
||
if (item.grantedSkill) {
|
||
unlockSkill(playerStore, item.grantedSkill)
|
||
if (gameStore.addLog) {
|
||
gameStore.addLog(`解锁了技能: ${SKILL_CONFIG[item.grantedSkill]?.name || item.grantedSkill}`, 'system')
|
||
}
|
||
}
|
||
// 更新成就进度
|
||
gameStore.achievementProgress.prostheticEquipped = 1
|
||
gameStore.checkAchievements('equip_prosthetic', { prostheticCount: 1 })
|
||
// 重新显示对话框
|
||
const currentEvent = gameStore.currentEvent
|
||
if (currentEvent && currentEvent.npc) {
|
||
startNPCDialogue(gameStore, currentEvent.npc.id, 'first', playerStore)
|
||
}
|
||
}
|
||
return { success: installResult.success, message: installResult.message, closeEvent: false }
|
||
}
|
||
return { success: false, message: '义体参数错误', closeEvent: false }
|
||
|
||
case 'remove_prosthetic':
|
||
// 卸下义体
|
||
const removeResult = removeProsthetic(playerStore, gameStore)
|
||
if (removeResult.success) {
|
||
// 重新显示对话框
|
||
const currentEvent = gameStore.currentEvent
|
||
if (currentEvent && currentEvent.npc) {
|
||
startNPCDialogue(gameStore, currentEvent.npc.id, 'first', playerStore)
|
||
}
|
||
}
|
||
return { success: removeResult.success, message: removeResult.message, closeEvent: false }
|
||
|
||
case 'refresh_list':
|
||
// 刷新列表(重新显示当前对话框)
|
||
const evt = gameStore.currentEvent
|
||
if (evt && evt.npc && evt.currentDialogue) {
|
||
startNPCDialogue(gameStore, evt.npc.id, evt.currentDialogue, playerStore)
|
||
}
|
||
return { success: true, message: '', closeEvent: false }
|
||
|
||
default:
|
||
console.warn(`Unknown dialogue action: ${action}`)
|
||
return { success: false, message: '未知动作', closeEvent: false }
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 处理事件选择
|
||
* @param {Object} gameStore - 游戏Store
|
||
* @param {Object} playerStore - 玩家Store
|
||
* @param {Number} choiceIndex - 选择索引
|
||
* @returns {Object} 处理结果
|
||
*/
|
||
export function handleEventChoice(gameStore, playerStore, choiceIndex) {
|
||
const currentEvent = gameStore.currentEvent
|
||
if (!currentEvent || !currentEvent.choices || !currentEvent.choices[choiceIndex]) {
|
||
return { success: false, message: '选择无效' }
|
||
}
|
||
|
||
const choice = currentEvent.choices[choiceIndex]
|
||
return handleDialogueChoice(gameStore, playerStore, choice)
|
||
}
|
||
|
||
/**
|
||
* 关闭当前事件
|
||
* @param {Object} gameStore - 游戏Store
|
||
*/
|
||
export function closeEvent(gameStore) {
|
||
gameStore.drawerState.event = false
|
||
gameStore.currentEvent = null
|
||
}
|
||
|
||
/**
|
||
* 获取当前事件信息
|
||
* @param {Object} gameStore - 游戏Store
|
||
* @returns {Object|null} 当前事件
|
||
*/
|
||
export function getCurrentEvent(gameStore) {
|
||
return gameStore.currentEvent
|
||
}
|
||
|
||
/**
|
||
* 检查NPC是否可用
|
||
* @param {String} npcId - NPC ID
|
||
* @param {Object} playerStore - 玩家Store
|
||
* @returns {Boolean}
|
||
*/
|
||
export function isNPCAvailable(npcId, playerStore) {
|
||
const npc = NPC_CONFIG[npcId]
|
||
if (!npc) return false
|
||
|
||
// 检查位置
|
||
if (npc.location && playerStore.currentLocation !== npc.location) {
|
||
return false
|
||
}
|
||
|
||
// 检查解锁条件
|
||
if (npc.unlockCondition) {
|
||
const condition = npc.unlockCondition
|
||
if (condition.flag && !playerStore.flags?.[condition.flag]) {
|
||
return false
|
||
}
|
||
if (condition.item && !playerStore.inventory.some(i => i.id === condition.item)) {
|
||
return false
|
||
}
|
||
}
|
||
|
||
return true
|
||
}
|
||
|
||
/**
|
||
* 获取当前位置可用的NPC列表
|
||
* @param {Object} playerStore - 玩家Store
|
||
* @returns {Array} NPC ID列表
|
||
*/
|
||
export function getAvailableNPCs(playerStore) {
|
||
const location = LOCATION_CONFIG[playerStore.currentLocation]
|
||
if (!location || !location.npcs) {
|
||
return []
|
||
}
|
||
|
||
return location.npcs.filter(npcId => isNPCAvailable(npcId, playerStore))
|
||
}
|
||
|
||
/**
|
||
* 设置定时事件
|
||
* @param {Object} gameStore - 游戏Store
|
||
* @param {String} eventId - 事件ID
|
||
* @param {Number} delayMs - 延迟时间(毫秒)
|
||
*/
|
||
export function scheduleEvent(gameStore, eventId, delayMs) {
|
||
if (!gameStore.scheduledEvents) {
|
||
gameStore.scheduledEvents = []
|
||
}
|
||
|
||
gameStore.scheduledEvents.push({
|
||
eventId,
|
||
triggerTime: Date.now() + delayMs,
|
||
triggered: false
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 检查并触发定时事件
|
||
* @param {Object} gameStore - 游戏Store
|
||
* @param {Object} playerStore - 玩家Store
|
||
* @returns {Array} 触发的事件列表
|
||
*/
|
||
export function checkScheduledEvents(gameStore, playerStore) {
|
||
if (!gameStore.scheduledEvents) {
|
||
return []
|
||
}
|
||
|
||
const now = Date.now()
|
||
const triggeredEvents = []
|
||
|
||
for (const event of gameStore.scheduledEvents) {
|
||
if (!event.triggered && now >= event.triggerTime) {
|
||
event.triggered = true
|
||
triggerEvent(gameStore, event.eventId, { playerStore })
|
||
triggeredEvents.push(event.eventId)
|
||
}
|
||
}
|
||
|
||
// 清理已触发的事件
|
||
gameStore.scheduledEvents = gameStore.scheduledEvents.filter(e => !e.triggered)
|
||
|
||
return triggeredEvents
|
||
}
|
||
|
||
/**
|
||
* 清除所有定时事件
|
||
* @param {Object} gameStore - 游戏Store
|
||
*/
|
||
export function clearScheduledEvents(gameStore) {
|
||
gameStore.scheduledEvents = []
|
||
}
|
||
|
||
/**
|
||
* 触发探索事件
|
||
* @param {Object} gameStore - 游戏Store
|
||
* @param {Object} playerStore - 玩家Store
|
||
* @returns {Object} 探索结果
|
||
*/
|
||
export function triggerExploreEvent(gameStore, playerStore) {
|
||
const location = LOCATION_CONFIG[playerStore.currentLocation]
|
||
if (!location) {
|
||
return { success: false, message: '当前位置错误' }
|
||
}
|
||
|
||
// 检查当前位置是否有敌人配置
|
||
const availableEnemies = location.enemies || []
|
||
if (availableEnemies.length === 0) {
|
||
return { success: false, message: '这里没什么可探索的' }
|
||
}
|
||
|
||
// 随机决定探索结果
|
||
const roll = Math.random()
|
||
|
||
// 40% 没发现
|
||
if (roll < 0.4) {
|
||
triggerEvent(gameStore, 'explore_nothing', {})
|
||
return { success: true, type: 'nothing' }
|
||
}
|
||
|
||
// 25% 发现草药
|
||
if (roll < 0.65) {
|
||
const herbCount = Math.floor(Math.random() * 3) + 1
|
||
const result = addItemToInventory(playerStore, 'healing_herb', herbCount, 100)
|
||
if (result.success) {
|
||
triggerEvent(gameStore, 'explore_find_herb', {})
|
||
return { success: true, type: 'find', item: 'healing_herb', count: herbCount }
|
||
}
|
||
}
|
||
|
||
// 20% 发现铜币 (1-5枚)
|
||
if (roll < 0.85) {
|
||
const coinAmount = Math.floor(Math.random() * 5) + 1
|
||
playerStore.currency.copper += coinAmount
|
||
if (gameStore.addLog) {
|
||
gameStore.addLog(`探索发现了 ${coinAmount} 铜币`, 'reward')
|
||
}
|
||
triggerEvent(gameStore, 'explore_find_coin', { amount: coinAmount })
|
||
return { success: true, type: 'coin', amount: coinAmount }
|
||
}
|
||
|
||
// 15% 遭遇敌人
|
||
const enemyId = availableEnemies[Math.floor(Math.random() * availableEnemies.length)]
|
||
const enemyConfig = ENEMY_CONFIG[enemyId]
|
||
|
||
if (enemyConfig) {
|
||
const environment = getEnvironmentType(playerStore.currentLocation)
|
||
gameStore.combatState = initCombat(enemyId, enemyConfig, environment)
|
||
gameStore.inCombat = true
|
||
|
||
triggerEvent(gameStore, 'explore_encounter', {
|
||
enemyName: enemyConfig.name,
|
||
enemyId
|
||
})
|
||
|
||
return { success: true, type: 'combat', enemyId }
|
||
}
|
||
|
||
// 默认:没发现
|
||
triggerEvent(gameStore, 'explore_nothing', {})
|
||
return { success: true, type: 'nothing' }
|
||
}
|
||
|
||
/**
|
||
* 尝试逃跑
|
||
* @param {Object} gameStore - 游戏Store
|
||
* @param {Object} playerStore - 玩家Store
|
||
* @returns {Object} 逃跑结果
|
||
*/
|
||
export function tryFlee(gameStore, playerStore) {
|
||
// 基础逃跑成功率50%
|
||
const baseFleeChance = 0.5
|
||
|
||
// 敏捷影响逃跑率
|
||
const agilityBonus = (playerStore.baseStats?.dexterity || 0) * 0.01
|
||
|
||
// 装备加成
|
||
const equipmentBonus = (playerStore.globalBonus?.fleeRate || 0)
|
||
|
||
const fleeChance = Math.min(0.9, baseFleeChance + agilityBonus + equipmentBonus)
|
||
|
||
if (Math.random() < fleeChance) {
|
||
// 逃跑成功
|
||
gameStore.inCombat = false
|
||
gameStore.combatState = null
|
||
if (gameStore.addLog) {
|
||
gameStore.addLog('成功逃跑了!', 'system')
|
||
}
|
||
return { success: true, message: '逃跑成功' }
|
||
} else {
|
||
// 逃跑失败,敌人攻击
|
||
if (gameStore.addLog) {
|
||
gameStore.addLog('逃跑失败!', 'combat')
|
||
}
|
||
return { success: false, message: '逃跑失败' }
|
||
}
|
||
}
|