/** * 事件系统 - 事件触发检测、对话管理 * 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 { unlockSkill } from './skillSystem.js' import { addItemToInventory } from './itemSystem.js' import { initCombat, getEnvironmentType } from './combatSystem.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 - 对话键名 */ export function startNPCDialogue(gameStore, npcId, dialogueKey = 'first') { 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 } // 构建事件数据 const eventData = { id: `npc_${npcId}_${dialogueKey}`, type: 'dialogue', title: npc.name, text: dialogue.text, npc: npc, choices: dialogue.choices || [], currentDialogue: dialogueKey, npcId } gameStore.currentEvent = eventData gameStore.drawerState.event = true } /** * 处理对话选择 * @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) 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 } 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: '逃跑失败' } } }