核心系统: - combatSystem: 战斗逻辑、伤害计算、战斗状态管理 - skillSystem: 技能系统、技能解锁、经验值、里程碑 - taskSystem: 任务系统、任务类型、任务执行和完成 - eventSystem: 事件系统、随机事件处理 - environmentSystem: 环境系统、时间流逝、区域效果 - levelingSystem: 升级系统、属性成长 - soundSystem: 音效系统 配置文件: - enemies: 敌人配置、掉落表 - events: 事件配置、事件效果 - items: 物品配置、装备属性 - locations: 地点配置、探索事件 - skills: 技能配置、技能树 UI组件: - CraftingDrawer: 制造界面 - InventoryDrawer: 背包界面 - 其他UI优化和动画 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
751 lines
21 KiB
JavaScript
751 lines
21 KiB
JavaScript
/**
|
||
* 任务系统 - 挂机任务管理、互斥检测
|
||
* 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
|
||
|
||
// 检查环境惩罚
|
||
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 effectiveProgress = task.progress * timeMultiplier
|
||
|
||
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}》`, '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 = 2 * elapsedSeconds
|
||
if (playerStore.currentStats.stamina < staminaCost) {
|
||
// 耐力不足,任务结束
|
||
return { completed: true, rewards: {} }
|
||
}
|
||
|
||
playerStore.currentStats.stamina -= staminaCost
|
||
|
||
// 给予技能经验
|
||
const expPerSecond = 1
|
||
const expGain = expPerSecond * elapsedSeconds
|
||
|
||
if (!task.accumulatedExp) {
|
||
task.accumulatedExp = {}
|
||
}
|
||
if (!task.accumulatedExp[skillId]) {
|
||
task.accumulatedExp[skillId] = 0
|
||
}
|
||
task.accumulatedExp[skillId] += expGain
|
||
|
||
// 检查是否完成
|
||
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
|
||
}
|