/** * 任务系统 - 挂机任务管理、互斥检测 * Phase 6 核心系统实现 */ import { SKILL_CONFIG } from '@/config/skills.js' import { ITEM_CONFIG } from '@/config/items.js' import { RECIPE_CONFIG } from '@/config/recipes.js' import { addSkillExp, unlockSkill } from './skillSystem.js' /** * 任务类型枚举 */ export const TASK_TYPES = { READING: 'reading', // 阅读 RESTING: 'resting', // 休息 TRAINING: 'training', // 训练 WORKING: 'working', // 工作 COMBAT: 'combat', // 战斗 EXPLORE: 'explore', // 探索 PRAYING: 'praying', // 祈祷 CRAFTING: 'crafting' // 制造 } /** * 任务互斥规则 */ const MUTEX_RULES = { // 完全互斥(不能同时进行) full: [ [TASK_TYPES.COMBAT, TASK_TYPES.TRAINING], // 战斗 vs 训练 [TASK_TYPES.COMBAT, TASK_TYPES.WORKING], // 战斗 vs 工作 [TASK_TYPES.COMBAT, TASK_TYPES.CRAFTING], // 战斗 vs 制造 [TASK_TYPES.TRAINING, TASK_TYPES.WORKING], // 训练 vs 工作 [TASK_TYPES.TRAINING, TASK_TYPES.CRAFTING], // 训练 vs 制造 [TASK_TYPES.EXPLORE, TASK_TYPES.RESTING], // 探索 vs 休息 [TASK_TYPES.EXPLORE, TASK_TYPES.CRAFTING] // 探索 vs 制造 ], // 部分互斥(需要一心多用技能) partial: [ [TASK_TYPES.READING, TASK_TYPES.COMBAT], // 阅读 vs 战斗 [TASK_TYPES.READING, TASK_TYPES.TRAINING], // 阅读 vs 训练 [TASK_TYPES.READING, TASK_TYPES.WORKING], // 阅读 vs 工作 [TASK_TYPES.READING, TASK_TYPES.CRAFTING] // 阅读 vs 制造 ] } /** * 开始任务 * @param {Object} gameStore - 游戏Store * @param {Object} playerStore - 玩家Store * @param {String} taskType - 任务类型 * @param {Object} taskData - 任务数据 * @returns {Object} { success: boolean, message: string, taskId: string|null } */ export function startTask(gameStore, playerStore, taskType, taskData = {}) { // 检查是否可以开始任务 const canStart = canStartTask(gameStore, playerStore, taskType) if (!canStart.canStart) { return { success: false, message: canStart.reason } } // 检查任务特定条件 const taskCheck = checkTaskConditions(playerStore, taskType, taskData) if (!taskCheck.canStart) { return { success: false, message: taskCheck.reason } } // 创建任务 const task = { id: Date.now(), type: taskType, data: taskData, startTime: Date.now(), progress: 0, totalDuration: taskData.duration || 0, lastTickTime: Date.now() } gameStore.activeTasks.push(task) return { success: true, message: '任务已开始', taskId: task.id } } /** * 检查是否可以开始任务 * @param {Object} gameStore - 游戏Store * @param {Object} playerStore - 玩家Store * @param {String} taskType - 任务类型 * @returns {Object} { canStart: boolean, reason: string } */ export function canStartTask(gameStore, playerStore, taskType) { // 检查是否已有相同类型的任务 const existingTask = gameStore.activeTasks.find(t => t.type === taskType) if (existingTask) { return { canStart: false, reason: '已有相同类型的任务正在进行' } } // 检查互斥规则 for (const [type1, type2] of MUTEX_RULES.full) { if (taskType === type1) { const conflictTask = gameStore.activeTasks.find(t => t.type === type2) if (conflictTask) { return { canStart: false, reason: `与${getTaskTypeName(type2)}任务冲突` } } } if (taskType === type2) { const conflictTask = gameStore.activeTasks.find(t => t.type === type1) if (conflictTask) { return { canStart: false, reason: `与${getTaskTypeName(type1)}任务冲突` } } } } // 检查部分互斥(需要一心多用技能) const multitaskingLevel = playerStore.skills.multitasking?.level || 0 const maxConcurrentTasks = 1 + Math.floor(multitaskingLevel / 5) // 每5级一心多用可多1个任务 const activePartialTasks = gameStore.activeTasks.filter(t => MUTEX_RULES.partial.some(pair => pair.includes(t.type)) ).length const isPartialConflict = MUTEX_RULES.partial.some(pair => pair.includes(taskType) && pair.some(type => gameStore.activeTasks.some(t => t.type === type) ) ) if (isPartialConflict && activePartialTasks >= maxConcurrentTasks) { return { canStart: false, reason: `需要一心多用技能Lv.${multitaskingLevel + 1}` } } return { canStart: true, reason: '' } } /** * 检查任务特定条件 */ function checkTaskConditions(playerStore, taskType, taskData) { switch (taskType) { case TASK_TYPES.READING: // 需要书籍 if (!taskData.itemId) { return { canStart: false, reason: '需要选择一本书' } } const bookConfig = ITEM_CONFIG[taskData.itemId] if (!bookConfig || bookConfig.type !== 'book') { return { canStart: false, reason: '这不是一本书' } } // 检查是否已拥有 const hasBook = playerStore.inventory.some(i => i.id === taskData.itemId) if (!hasBook) { return { canStart: false, reason: '你没有这本书' } } break case TASK_TYPES.TRAINING: // 需要耐力 if (playerStore.currentStats.stamina < 10) { return { canStart: false, reason: '耐力不足' } } // 需要武器技能 if (!taskData.skillId) { return { canStart: false, reason: '需要选择训练技能' } } const skill = playerStore.skills[taskData.skillId] if (!skill || !skill.unlocked) { return { canStart: false, reason: '技能未解锁' } } break case TASK_TYPES.RESTING: // 只能在安全区休息 const safeZones = ['camp', 'market', 'blackmarket'] if (!safeZones.includes(playerStore.currentLocation)) { return { canStart: false, reason: '只能在安全区休息' } } break case TASK_TYPES.PRAYING: // 需要圣经 const hasBible = playerStore.inventory.some(i => i.id === 'bible') if (!hasBible) { return { canStart: false, reason: '需要圣经' } } break case TASK_TYPES.CRAFTING: // 需要配方ID if (!taskData.recipeId) { return { canStart: false, reason: '需要选择配方' } } // 需要耐力 if (playerStore.currentStats.stamina < 5) { return { canStart: false, reason: '耐力不足' } } break } return { canStart: true, reason: '' } } /** * 结束任务 * @param {Object} gameStore - 游戏Store * @param {Object} playerStore - 玩家Store * @param {String|Number} taskId - 任务ID * @param {Boolean} completed - 是否完成 * @returns {Object} { success: boolean, rewards: Object } */ export function endTask(gameStore, playerStore, taskId, completed = false) { const taskIndex = gameStore.activeTasks.findIndex(t => t.id === taskId) if (taskIndex === -1) { return { success: false, message: '任务不存在' } } const task = gameStore.activeTasks[taskIndex] const rewards = {} // 如果完成,给予奖励 if (completed) { const taskRewards = getTaskRewards(playerStore, task) Object.assign(rewards, taskRewards) applyTaskRewards(playerStore, task, taskRewards) } // 移除任务 gameStore.activeTasks.splice(taskIndex, 1) return { success: true, message: completed ? '任务完成' : '任务已取消', rewards } } /** * 处理任务tick(每秒调用) * @param {Object} gameStore - 游戏Store * @param {Object} playerStore - 玩家Store * @param {Number} deltaTime - 时间增量(毫秒) * @returns {Array} 完成的任务列表 */ export function processTaskTick(gameStore, playerStore, deltaTime = 1000) { const completedTasks = [] for (const task of gameStore.activeTasks) { const result = processSingleTask(gameStore, playerStore, task, deltaTime) if (result.completed) { completedTasks.push({ task, rewards: result.rewards }) } } // 移除已完成的任务 for (const { task } of completedTasks) { const index = gameStore.activeTasks.findIndex(t => t.id === task.id) if (index !== -1) { gameStore.activeTasks.splice(index, 1) } } return completedTasks } /** * 处理单个任务 */ function processSingleTask(gameStore, playerStore, task, deltaTime) { const now = Date.now() const elapsedSeconds = (now - task.lastTickTime) / 1000 task.lastTickTime = now task.progress += elapsedSeconds switch (task.type) { case TASK_TYPES.READING: return processReadingTask(gameStore, playerStore, task, elapsedSeconds) case TASK_TYPES.RESTING: return processRestingTask(gameStore, playerStore, task, elapsedSeconds) case TASK_TYPES.TRAINING: return processTrainingTask(gameStore, playerStore, task, elapsedSeconds) case TASK_TYPES.WORKING: return processWorkingTask(gameStore, playerStore, task, elapsedSeconds) case TASK_TYPES.PRAYING: return processPrayingTask(gameStore, playerStore, task, elapsedSeconds) case TASK_TYPES.CRAFTING: return processCraftingTask(gameStore, playerStore, task, elapsedSeconds) default: return { completed: false } } } /** * 处理阅读任务 */ function processReadingTask(gameStore, playerStore, task, elapsedSeconds) { const bookConfig = ITEM_CONFIG[task.data.itemId] if (!bookConfig) { return { completed: true, error: '书籍不存在' } } const readingTime = bookConfig.readingTime || 60 // 首次阅读时解锁阅读技能 unlockSkill(playerStore, 'reading') // 检查环境惩罚 const location = playerStore.currentLocation let timeMultiplier = 1.0 // 黑暗区域阅读效率-50% if (location === 'basement') { const darkPenaltyReduce = playerStore.globalBonus?.darkPenaltyReduce || 0 timeMultiplier = 0.5 + (darkPenaltyReduce / 100) * 0.5 } // 阅读技能速度加成 const readingSkillLevel = playerStore.skills.reading?.level || 0 const readingSpeedBonus = playerStore.globalBonus?.readingSpeed || 1 timeMultiplier *= readingSpeedBonus // 计算有效进度 const effectiveProgress = task.progress * timeMultiplier // 给予阅读技能经验(每秒给予经验) const readingExpPerSecond = 1 const readingExpGain = readingExpPerSecond * elapsedSeconds * timeMultiplier const readingResult = addSkillExp(playerStore, 'reading', readingExpGain) // 检查技能是否升级 if (readingResult.leveledUp) { if (gameStore.addLog) { gameStore.addLog(`阅读技能升级到了 Lv.${readingResult.newLevel}!`, 'reward') } } if (effectiveProgress >= readingTime) { // 阅读完成 const rewards = {} // 给予技能经验 if (bookConfig.expReward) { for (const [skillId, exp] of Object.entries(bookConfig.expReward)) { rewards[skillId] = exp } } // 完成奖励 if (bookConfig.completionBonus) { rewards.completionBonus = bookConfig.completionBonus } // 添加日志 if (gameStore.addLog) { gameStore.addLog(`读完了《${bookConfig.name}》,阅读经验+${Math.floor(readingExpGain * task.progress / readingTime)}`, 'reward') } return { completed: true, rewards } } // 阅读进行中,给予其他技能经验 if (bookConfig.expReward) { for (const [skillId, expPerSecond] of Object.entries(bookConfig.expReward)) { const expPerTick = (expPerSecond / readingTime) * elapsedSeconds * timeMultiplier // 累积经验,到完成时一次性给予 if (!task.accumulatedExp) { task.accumulatedExp = {} } if (!task.accumulatedExp[skillId]) { task.accumulatedExp[skillId] = 0 } task.accumulatedExp[skillId] += expPerTick } } return { completed: false } } /** * 处理休息任务 */ function processRestingTask(gameStore, playerStore, task, elapsedSeconds) { // 恢复耐力和生命值 const staminaRecovery = 5 * elapsedSeconds // 每秒恢复5点 const healthRecovery = 2 * elapsedSeconds // 每秒恢复2点 const oldStamina = playerStore.currentStats.stamina const oldHealth = playerStore.currentStats.health playerStore.currentStats.stamina = Math.min( playerStore.currentStats.maxStamina, playerStore.currentStats.stamina + staminaRecovery ) playerStore.currentStats.health = Math.min( playerStore.currentStats.maxHealth, playerStore.currentStats.health + healthRecovery ) // 检查是否完成 if (task.data.duration && task.progress >= task.data.duration) { return { completed: true, rewards: {} } } // 满状态自动完成 if (playerStore.currentStats.stamina >= playerStore.currentStats.maxStamina && playerStore.currentStats.health >= playerStore.currentStats.maxHealth) { return { completed: true, rewards: {} } } return { completed: false } } /** * 处理训练任务 */ function processTrainingTask(gameStore, playerStore, task, elapsedSeconds) { const skillId = task.data.skillId const skillConfig = SKILL_CONFIG[skillId] if (!skillConfig) { return { completed: true, error: '技能不存在' } } // 消耗耐力(降低消耗) const staminaCost = 1 * elapsedSeconds if (playerStore.currentStats.stamina < staminaCost) { // 耐力不足,任务结束 return { completed: true, rewards: {} } } playerStore.currentStats.stamina -= staminaCost // 给予技能经验(提升获取速度) const expPerSecond = 5 // 从1提升到5 const expGain = expPerSecond * elapsedSeconds if (!task.accumulatedExp) { task.accumulatedExp = {} } if (!task.accumulatedExp[skillId]) { task.accumulatedExp[skillId] = 0 } task.accumulatedExp[skillId] += expGain // 检查技能是否升级 const result = addSkillExp(playerStore, skillId, expGain) if (result.leveledUp && gameStore.addLog) { gameStore.addLog(`${skillConfig.name}升级到了 Lv.${result.newLevel}!`, 'reward') } // 检查是否完成 if (task.data.duration && task.progress >= task.data.duration) { const rewards = { [skillId]: task.accumulatedExp[skillId] || 0 } return { completed: true, rewards } } return { completed: false } } /** * 处理工作任务 */ function processWorkingTask(gameStore, playerStore, task, elapsedSeconds) { // 消耗耐力 const staminaCost = 1.5 * elapsedSeconds if (playerStore.currentStats.stamina < staminaCost) { return { completed: true, rewards: {} } } playerStore.currentStats.stamina -= staminaCost // 累积货币 if (!task.accumulatedCurrency) { task.accumulatedCurrency = 0 } task.accumulatedCurrency += 1 * elapsedSeconds // 每秒1铜币 // 给予相关技能经验(如果有) if (task.data.skillId) { if (!task.accumulatedExp) { task.accumulatedExp = {} } if (!task.accumulatedExp[task.data.skillId]) { task.accumulatedExp[task.data.skillId] = 0 } task.accumulatedExp[task.data.skillId] += 0.5 * elapsedSeconds } // 检查是否完成 if (task.data.duration && task.progress >= task.data.duration) { const rewards = { currency: Math.floor(task.accumulatedCurrency || 0), ...task.accumulatedExp } return { completed: true, rewards } } return { completed: false } } /** * 处理祈祷任务 */ function processPrayingTask(gameStore, playerStore, task, elapsedSeconds) { // 祈祷获得信仰经验 if (!task.accumulatedExp) { task.accumulatedExp = {} } if (!task.accumulatedExp.faith) { task.accumulatedExp.faith = 0 } task.accumulatedExp.faith += 0.8 * elapsedSeconds // 恢复精神 const sanityRecovery = 3 * elapsedSeconds playerStore.currentStats.sanity = Math.min( playerStore.currentStats.maxSanity, playerStore.currentStats.sanity + sanityRecovery ) // 检查是否完成 if (task.data.duration && task.progress >= task.data.duration) { const rewards = { faith: task.accumulatedExp.faith || 0 } return { completed: true, rewards } } return { completed: false } } /** * 处理制造任务 */ function processCraftingTask(gameStore, playerStore, task, elapsedSeconds) { const recipeId = task.data.recipeId const recipe = RECIPE_CONFIG[recipeId] if (!recipe) { return { completed: true, error: '配方不存在' } } // 消耗耐力(制造也需要体力) const staminaCost = 1 * elapsedSeconds if (playerStore.currentStats.stamina < staminaCost) { return { completed: true, rewards: {}, failed: true } } playerStore.currentStats.stamina -= staminaCost // 计算实际制造时间(受技能加成影响) const craftingSkill = playerStore.skills[recipe.requiredSkill] let timeMultiplier = 1.0 if (craftingSkill && craftingSkill.level > 0) { // 制造技能每级减少5%时间 timeMultiplier = 1 - (Math.min(craftingSkill.level, 20) * 0.05) } // 累积制造进度(考虑时间加成) const effectiveProgress = task.progress * timeMultiplier // 检查是否完成 if (effectiveProgress >= recipe.baseTime) { // 计算成功率 const successRate = calculateCraftingSuccessRate(playerStore, recipe) // 判定是否成功 const success = Math.random() < successRate if (success) { // 给予制造技能经验 const expGain = recipe.baseTime * 0.5 // 基于制造时间的经验 if (!task.accumulatedExp) { task.accumulatedExp = {} } if (!task.accumulatedExp[recipe.requiredSkill]) { task.accumulatedExp[recipe.requiredSkill] = 0 } task.accumulatedExp[recipe.requiredSkill] += expGain const rewards = { [recipe.requiredSkill]: task.accumulatedExp[recipe.requiredSkill] || 0, craftedItem: recipe.resultItem, craftedCount: recipe.resultCount } // 添加制造物品到背包(这将在任务完成回调中处理) if (gameStore.addLog) { const itemConfig = ITEM_CONFIG[recipe.resultItem] gameStore.addLog(`制造成功:${itemConfig?.name || recipe.resultItem}`, 'reward') } return { completed: true, rewards, success: true } } else { // 制造失败 if (gameStore.addLog) { gameStore.addLog(`制造失败:${recipeId}`, 'warning') } // 失败也给予少量经验 const expGain = recipe.baseTime * 0.1 if (!task.accumulatedExp) { task.accumulatedExp = {} } if (!task.accumulatedExp[recipe.requiredSkill]) { task.accumulatedExp[recipe.requiredSkill] = 0 } task.accumulatedExp[recipe.requiredSkill] += expGain const rewards = { [recipe.requiredSkill]: task.accumulatedExp[recipe.requiredSkill] || 0 } return { completed: true, rewards, success: false } } } // 进行中,给予少量经验 const expPerTick = 0.5 * elapsedSeconds if (!task.accumulatedExp) { task.accumulatedExp = {} } if (!task.accumulatedExp[recipe.requiredSkill]) { task.accumulatedExp[recipe.requiredSkill] = 0 } task.accumulatedExp[recipe.requiredSkill] += expPerTick return { completed: false } } /** * 计算制造成功率 * @param {Object} playerStore - 玩家Store * @param {Object} recipe - 配方对象 * @returns {Number} 成功率 (0-1) */ function calculateCraftingSuccessRate(playerStore, recipe) { let successRate = recipe.baseSuccessRate // 技能等级加成(每级+2%) const skill = playerStore.skills[recipe.requiredSkill] if (skill && skill.level > 0) { successRate += Math.min(skill.level, 20) * 0.02 } // 运气加成 const luck = playerStore.baseStats?.luck || 10 successRate += luck * 0.001 // 检查是否有制造技能的全局加成 if (playerStore.globalBonus?.craftingSuccessRate) { successRate += playerStore.globalBonus.craftingSuccessRate / 100 } return Math.min(0.98, Math.max(0.05, successRate)) } /** * 获取任务奖励 */ function getTaskRewards(playerStore, task) { const rewards = {} if (task.accumulatedExp) { for (const [skillId, exp] of Object.entries(task.accumulatedExp)) { rewards[skillId] = Math.floor(exp) } } if (task.accumulatedCurrency) { rewards.currency = Math.floor(task.accumulatedCurrency) } return rewards } /** * 应用任务奖励 */ function applyTaskRewards(playerStore, task, rewards) { // 应用技能经验 for (const [skillId, exp] of Object.entries(rewards)) { if (skillId === 'currency') { playerStore.currency.copper += exp continue } if (skillId === 'faith') { // 信仰技能经验 - 使用addSkillExp处理 if (!playerStore.skills.faith) { unlockSkill(playerStore, 'faith') } addSkillExp(playerStore, 'faith', exp) continue } // 普通技能经验 - 使用addSkillExp处理升级和里程碑 if (playerStore.skills[skillId]) { addSkillExp(playerStore, skillId, exp) } } } /** * 获取任务类型名称 */ function getTaskTypeName(taskType) { const names = { reading: '阅读', resting: '休息', training: '训练', working: '工作', combat: '战斗', explore: '探索', praying: '祈祷', crafting: '制造' } return names[taskType] || taskType } /** * 获取任务进度信息 * @param {Object} task - 任务对象 * @returns {Object} 进度信息 */ export function getTaskProgress(task) { const progress = { current: task.progress, total: task.totalDuration || 0, percentage: task.totalDuration > 0 ? Math.min(100, (task.progress / task.totalDuration) * 100) : 0 } if (task.type === TASK_TYPES.READING) { const bookConfig = ITEM_CONFIG[task.data.itemId] if (bookConfig) { progress.total = bookConfig.readingTime || 60 progress.percentage = Math.min(100, (task.progress / progress.total) * 100) } } return progress } /** * 获取活动中的任务列表 * @param {Object} gameStore - 游戏Store * @returns {Array} 任务列表 */ export function getActiveTasks(gameStore) { return gameStore.activeTasks.map(task => ({ ...task, progress: getTaskProgress(task), typeName: getTaskTypeName(task.type) })) } /** * 取消所有任务 * @param {Object} gameStore - 游戏Store * @returns {Number} 取消的任务数量 */ export function cancelAllTasks(gameStore) { const count = gameStore.activeTasks.length gameStore.activeTasks = [] return count }