/** * 游戏循环系统 - 主循环、时间推进、战斗处理、市场刷新 * Phase 7 核心实现 */ import { GAME_CONSTANTS } from '@/config/constants.js' import { ITEM_CONFIG } from '@/config/items.js' import { SKILL_CONFIG } from '@/config/skills.js' import { ENEMY_CONFIG } from '@/config/enemies.js' import { combatTick, initCombat, getEnvironmentType } from './combatSystem.js' import { processTaskTick } from './taskSystem.js' import { processEnvironmentExp, processEnvironmentEffects } from './environmentSystem.js' import { checkScheduledEvents } from './eventSystem.js' import { addSkillExp } from './skillSystem.js' import { addItemToInventory } from './itemSystem.js' // 游戏循环定时器 let gameLoopInterval = null let isLoopRunning = false // 离线时间记录 let lastSaveTime = Date.now() /** * 启动游戏循环 * @param {Object} gameStore - 游戏Store * @param {Object} playerStore - 玩家Store * @returns {boolean} 是否成功启动 */ export function startGameLoop(gameStore, playerStore) { if (isLoopRunning) { console.warn('Game loop is already running') return false } // 每秒执行一次游戏tick gameLoopInterval = setInterval(() => { gameTick(gameStore, playerStore) }, 1000) isLoopRunning = true console.log('Game loop started') return true } /** * 停止游戏循环 * @returns {boolean} 是否成功停止 */ export function stopGameLoop() { if (!isLoopRunning) { return false } if (gameLoopInterval) { clearInterval(gameLoopInterval) gameLoopInterval = null } isLoopRunning = false console.log('Game loop stopped') return true } /** * 检查游戏循环是否运行中 * @returns {boolean} */ export function isGameLoopRunning() { return isLoopRunning } /** * 游戏主tick - 每秒执行一次 * @param {Object} gameStore - 游戏Store * @param {Object} playerStore - 玩家Store */ export function gameTick(gameStore, playerStore) { // 1. 更新游戏时间 updateGameTime(gameStore) // 2. 处理战斗 if (gameStore.inCombat && gameStore.combatState) { handleCombatTick(gameStore, playerStore) } // 3. 处理任务 if (gameStore.activeTasks && gameStore.activeTasks.length > 0) { const completedTasks = processTaskTick(gameStore, playerStore, 1000) // 处理完成的任务奖励 for (const { task, rewards } of completedTasks) { handleTaskCompletion(gameStore, playerStore, task, rewards) } } // 4. 处理环境经验 processEnvironmentExp( gameStore, playerStore, playerStore.currentLocation, 1 ) // 5. 处理环境负面效果 processEnvironmentEffects( playerStore, playerStore.currentLocation, 1 ) // 6. 检查定时事件 checkScheduledEvents(gameStore, playerStore) // 7. 自动保存检查(每分钟保存一次) const currentTime = Date.now() if (currentTime - lastSaveTime >= 60000) { // 触发自动保存(由App.vue处理) lastSaveTime = currentTime if (gameStore.triggerAutoSave) { gameStore.triggerAutoSave() } } } /** * 更新游戏时间 * 现实1秒 = 游戏时间5分钟 * @param {Object} gameStore - 游戏Store */ export function updateGameTime(gameStore) { const { gameTime } = gameStore // 每秒增加5分钟 gameTime.totalMinutes += GAME_CONSTANTS.TIME_SCALE // 计算天、时、分 const minutesPerDay = 24 * 60 const minutesPerHour = 60 gameTime.day = Math.floor(gameTime.totalMinutes / minutesPerDay) + 1 const dayMinutes = gameTime.totalMinutes % minutesPerDay gameTime.hour = Math.floor(dayMinutes / minutesPerHour) gameTime.minute = dayMinutes % minutesPerHour // 检查是否需要刷新市场价格(每天刷新一次) if (gameTime.hour === 0 && gameTime.minute === 0) { refreshMarketPrices(gameStore) } } /** * 处理战斗tick * @param {Object} gameStore - 游戏Store * @param {Object} playerStore - 玩家Store */ function handleCombatTick(gameStore, playerStore) { const result = combatTick(gameStore, playerStore, gameStore.combatState) // 添加战斗日志 if (result.logs && result.logs.length > 0) { for (const log of result.logs) { if (typeof log === 'string') { gameStore.addLog(log, 'combat') } else if (log.message) { gameStore.addLog(log.message, log.type || 'combat') } } } // 处理战斗结果 if (result.victory) { handleCombatVictory(gameStore, playerStore, result) } else if (result.defeat) { handleCombatDefeat(gameStore, playerStore, result) } else if (result.enemy) { // 更新敌人状态 gameStore.combatState.enemy = result.enemy gameStore.combatState.ticks++ } } /** * 处理战斗胜利 * @param {Object} gameStore - 游戏Store * @param {Object} playerStore - 玩家Store * @param {Object} combatResult - 战斗结果 */ export function handleCombatVictory(gameStore, playerStore, combatResult) { const { enemy } = combatResult // 结束战斗状态 gameStore.inCombat = false gameStore.combatState = null // 添加日志 gameStore.addLog(`战胜了 ${enemy.name}!`, 'reward') // 给予经验值 if (enemy.expReward) { playerStore.level.exp += enemy.expReward // 检查升级 const maxExp = playerStore.level.maxExp if (playerStore.level.exp >= maxExp) { playerStore.level.current++ playerStore.level.exp -= maxExp playerStore.level.maxExp = Math.floor(playerStore.level.maxExp * 1.5) gameStore.addLog(`升级了! 等级: ${playerStore.level.current}`, 'reward') // 升级恢复状态 playerStore.currentStats.health = playerStore.currentStats.maxHealth playerStore.currentStats.stamina = playerStore.currentStats.maxStamina playerStore.currentStats.sanity = playerStore.currentStats.maxSanity } } // 给予武器技能经验奖励 if (enemy.skillExpReward && playerStore.equipment.weapon) { const weapon = playerStore.equipment.weapon // 根据武器ID确定技能ID let skillId = null if (weapon.id === 'wooden_stick') { skillId = 'stick_mastery' } else if (weapon.subtype === 'sword') { skillId = 'sword_mastery' } else if (weapon.subtype === 'axe') { skillId = 'axe_mastery' } if (skillId) { const result = addSkillExp(playerStore, skillId, enemy.skillExpReward) if (result.leveledUp) { const skillName = SKILL_CONFIG[skillId]?.name || skillId gameStore.addLog(`${skillName}升级到了 Lv.${result.newLevel}!`, 'reward') } } } // 处理掉落 if (enemy.drops && enemy.drops.length > 0) { processDrops(gameStore, playerStore, enemy.drops) } // 触发战斗胜利事件 gameStore.triggerEvent?.('combat', { result: 'victory', enemyId: enemy.id, enemy }) // 自动战斗模式:战斗结束后继续寻找敌人 if (gameStore.autoCombat) { // 检查玩家状态是否允许继续战斗 const canContinue = playerStore.currentStats.health > 10 && playerStore.currentStats.stamina > 10 if (canContinue) { // 延迟3秒后开始下一场战斗 setTimeout(() => { if (!gameStore.inCombat && gameStore.autoCombat) { startAutoCombat(gameStore, playerStore) } }, 3000) } else { // 状态过低,自动关闭自动战斗 gameStore.autoCombat = false gameStore.addLog('状态过低,自动战斗已停止', 'warning') } } } /** * 启动自动战斗(在当前位置寻找敌人) * @param {Object} gameStore - 游戏Store * @param {Object} playerStore - 玩家Store */ function startAutoCombat(gameStore, playerStore) { // 检查当前位置是否有敌人 const locationId = playerStore.currentLocation const locationEnemies = LOCATION_ENEMIES[locationId] if (!locationEnemies || locationEnemies.length === 0) { gameStore.autoCombat = false gameStore.addLog('当前区域没有敌人,自动战斗已停止', 'info') return } // 随机选择一个敌人 const enemyId = locationEnemies[Math.floor(Math.random() * locationEnemies.length)] const enemyConfig = ENEMY_CONFIG[enemyId] if (!enemyConfig) { gameStore.addLog('敌人配置错误', 'error') return } gameStore.addLog(`自动寻找中...遇到了 ${enemyConfig.name}!`, 'combat') const environment = getEnvironmentType(locationId) gameStore.combatState = initCombat(enemyId, enemyConfig, environment) gameStore.inCombat = true } // 当前区域的敌人配置 const LOCATION_ENEMIES = { wild1: ['wild_dog'], boss_lair: ['test_boss'] } /** * 处理战斗失败 * @param {Object} gameStore - 游戏Store * @param {Object} playerStore - 玩家Store * @param {Object} combatResult - 战斗结果 */ export function handleCombatDefeat(gameStore, playerStore, combatResult) { // 结束战斗状态 gameStore.inCombat = false gameStore.combatState = null // 添加日志 gameStore.addLog('你被击败了...', 'error') // 惩罚:回到营地 playerStore.currentLocation = 'camp' // 恢复部分状态(在营地) playerStore.currentStats.health = Math.floor(playerStore.currentStats.maxHealth * 0.3) playerStore.currentStats.stamina = Math.floor(playerStore.currentStats.maxStamina * 0.5) gameStore.addLog('你在营地醒来,损失了部分状态', 'info') // 触发战斗失败事件 gameStore.triggerEvent?.('combat', { result: 'defeat' }) } /** * 处理掉落物品 * @param {Object} gameStore - 游戏Store * @param {Object} playerStore - 玩家Store * @param {Array} drops - 掉落列表 */ function processDrops(gameStore, playerStore, drops) { for (const drop of drops) { // 检查掉落概率 if (drop.chance && Math.random() > drop.chance) { continue } // 随机数量 const count = drop.count ? drop.count.min + Math.floor(Math.random() * (drop.count.max - drop.count.min + 1)) : 1 // 添加物品 const result = addItemToInventory(playerStore, drop.itemId, count) if (result.success) { const item = result.item gameStore.addLog(`获得了 ${item.name} x${count}`, 'reward') } } } /** * 处理任务完成 * @param {Object} gameStore - 游戏Store * @param {Object} playerStore - 玩家Store * @param {Object} task - 完成的任务 * @param {Object} rewards - 奖励 */ function handleTaskCompletion(gameStore, playerStore, task, rewards) { // 应用货币奖励 if (rewards.currency) { playerStore.currency.copper += rewards.currency } // 应用技能经验 for (const [skillId, exp] of Object.entries(rewards)) { if (skillId !== 'currency' && skillId !== 'completionBonus' && typeof exp === 'number') { addSkillExp(playerStore, skillId, exp) } } // 添加完成日志 const taskNames = { reading: '阅读', resting: '休息', training: '训练', working: '工作', praying: '祈祷' } gameStore.addLog(`${taskNames[task.type]}任务完成`, 'reward') } /** * 刷新市场价格 * @param {Object} gameStore - 游戏Store */ export function refreshMarketPrices(gameStore) { const prices = {} // 为所有可交易物品生成新价格 for (const [itemId, config] of Object.entries(ITEM_CONFIG)) { if (config.baseValue > 0 && config.type !== 'key') { // 价格波动范围:0.5 - 1.5倍 const fluctuation = 0.5 + Math.random() prices[itemId] = { buyRate: fluctuation * 2, // 购买价格倍率 sellRate: fluctuation * 0.3 // 出售价格倍率 } } } gameStore.marketPrices = { lastRefreshDay: gameStore.gameTime?.day || 1, prices } gameStore.addLog('市场价格已更新', 'info') } /** * 获取物品的当前市场价格 * @param {Object} gameStore - 游戏Store * @param {String} itemId - 物品ID * @returns {Object} { buyPrice: number, sellPrice: number } */ export function getMarketPrice(gameStore, itemId) { const config = ITEM_CONFIG[itemId] if (!config || !config.baseValue) { return { buyPrice: 0, sellPrice: 0 } } const marketData = gameStore.marketPrices?.prices?.[itemId] const basePrice = config.baseValue if (marketData) { return { buyPrice: Math.floor(basePrice * marketData.buyRate), sellPrice: Math.floor(basePrice * marketData.sellRate) } } // 默认价格 return { buyPrice: Math.floor(basePrice * 2), sellPrice: Math.floor(basePrice * 0.3) } } /** * 计算离线收益 * @param {Object} gameStore - 游戏Store * @param {Object} playerStore - 玩家Store * @param {Number} offlineSeconds - 离线秒数 * @returns {Object} 离线收益信息 */ export function calculateOfflineEarnings(gameStore, playerStore, offlineSeconds) { const maxOfflineSeconds = 8 * 60 * 60 // 最多计算8小时 const effectiveSeconds = Math.min(offlineSeconds, maxOfflineSeconds) const earnings = { time: effectiveSeconds, timeDisplay: formatOfflineTime(effectiveSeconds), skillExp: {}, currency: 0 } // 简化计算:基于离线时间给予少量技能经验 // 假设玩家在离线期间进行了基础训练 const baseExpRate = 0.1 // 每秒0.1经验 const totalExp = Math.floor(baseExpRate * effectiveSeconds) // 将经验分配给已解锁的技能 const unlockedSkills = Object.keys(playerStore.skills || {}).filter( skillId => playerStore.skills[skillId]?.unlocked ) if (unlockedSkills.length > 0) { const expPerSkill = Math.floor(totalExp / unlockedSkills.length) for (const skillId of unlockedSkills) { earnings.skillExp[skillId] = expPerSkill } } // 离线货币收益(极少) earnings.currency = Math.floor(effectiveSeconds * 0.01) return earnings } /** * 应用离线收益 * @param {Object} gameStore - 游戏Store * @param {Object} playerStore - 玩家Store * @param {Object} earnings - 离线收益 */ export function applyOfflineEarnings(gameStore, playerStore, earnings) { // 应用技能经验 for (const [skillId, exp] of Object.entries(earnings.skillExp)) { addSkillExp(playerStore, skillId, exp) } // 应用货币 if (earnings.currency > 0) { playerStore.currency.copper += earnings.currency } // 添加日志 gameStore.addLog( `离线 ${earnings.timeDisplay},获得了一些经验`, 'info' ) } /** * 格式化离线时间 * @param {Number} seconds - 秒数 * @returns {String} 格式化的时间 */ function formatOfflineTime(seconds) { const hours = Math.floor(seconds / 3600) const minutes = Math.floor((seconds % 3600) / 60) if (hours > 0) { return `${hours}小时${minutes}分钟` } return `${minutes}分钟` } /** * 更新最后保存时间 */ export function updateLastSaveTime() { lastSaveTime = Date.now() } /** * 获取最后保存时间 * @returns {Number} 时间戳 */ export function getLastSaveTime() { return lastSaveTime }