/** * 技能系统 - 技能经验、升级、里程碑、父技能同步 * Phase 6 核心系统实现 */ import { SKILL_CONFIG } from '@/config/skills.js' import { GAME_CONSTANTS } from '@/config/constants.js' /** * 添加技能经验 * @param {Object} playerStore - 玩家Store * @param {String} skillId - 技能ID * @param {Number} amount - 经验值 * @param {Object} options - 选项 { partialSuccess: boolean } * @returns {Object} { leveledUp: boolean, newLevel: number, milestoneReached: number|null } */ export function addSkillExp(playerStore, skillId, amount, options = {}) { const { partialSuccess = false } = options // 检查技能是否存在且已解锁 if (!playerStore.skills || !playerStore.skills[skillId]) { return { leveledUp: false, newLevel: 0, milestoneReached: null } } const skill = playerStore.skills[skillId] if (!skill.unlocked) { return { leveledUp: false, newLevel: 0, milestoneReached: null } } // 部分成功时经验减半 const finalAmount = partialSuccess ? Math.floor(amount * GAME_CONSTANTS.EXP_PARTIAL_SUCCESS) : amount // 检查玩家等级限制 const maxSkillLevel = playerStore.level ? playerStore.level.current * 2 : 2 if (skill.level >= maxSkillLevel) { return { leveledUp: false, newLevel: skill.level, milestoneReached: null, capped: true } } // 获取技能配置 const config = SKILL_CONFIG[skillId] if (!config) { return { leveledUp: false, newLevel: 0, milestoneReached: null } } // 计算有效等级上限(取技能上限和玩家限制的最小值) const effectiveMaxLevel = Math.min(config.maxLevel, maxSkillLevel) // 添加经验 skill.exp += finalAmount let leveledUp = false let milestoneReached = null // 检查升级(可能连续升级) while (skill.level < effectiveMaxLevel && skill.exp >= getNextLevelExp(skillId, skill.level)) { const result = levelUpSkill(playerStore, skillId, skill.level) leveledUp = true if (result.milestone) { milestoneReached = result.milestone } } return { leveledUp, newLevel: skill.level, milestoneReached, capped: skill.level >= effectiveMaxLevel } } /** * 获取下一级所需经验 * @param {String} skillId - 技能ID * @param {Number} currentLevel - 当前等级 * @returns {Number} 所需经验值 */ export function getNextLevelExp(skillId, currentLevel) { const config = SKILL_CONFIG[skillId] if (!config) return 100 // 使用配置中的经验公式,默认为 level * 100 if (typeof config.expPerLevel === 'function') { return config.expPerLevel(currentLevel + 1) } return (currentLevel + 1) * 100 } /** * 技能升级 * @param {Object} playerStore - 玩家Store * @param {String} skillId - 技能ID * @param {Number} currentLevel - 当前等级 * @returns {Object} { milestone: number|null, effects: Object } */ export function levelUpSkill(playerStore, skillId, currentLevel) { const skill = playerStore.skills[skillId] const config = SKILL_CONFIG[skillId] if (!skill || !config) { return { milestone: null, effects: {} } } // 增加等级 skill.level++ const requiredExp = getNextLevelExp(skillId, currentLevel) skill.exp -= requiredExp // 检查里程碑奖励 let milestone = null let effects = {} if (config.milestones && config.milestones[skill.level]) { milestone = skill.level effects = applyMilestoneBonus(playerStore, skillId, skill.level) } // 更新父技能等级 updateParentSkill(playerStore, skillId) return { milestone, effects } } /** * 应用里程碑奖励 * @param {Object} playerStore - 玩家Store * @param {String} skillId - 技能ID * @param {Number} level - 里程碑等级 * @returns {Object} 应用的效果 */ export function applyMilestoneBonus(playerStore, skillId, level) { const config = SKILL_CONFIG[skillId] if (!config || !config.milestones || !config.milestones[level]) { return {} } const milestone = config.milestones[level] const effects = { ...milestone.effect } // 初始化里程碑奖励存储 if (!playerStore.milestoneBonuses) { playerStore.milestoneBonuses = {} } const bonusKey = `${skillId}_${level}` // 如果已经应用过,跳过 if (playerStore.milestoneBonuses[bonusKey]) { return effects } // 标记里程碑已应用 playerStore.milestoneBonuses[bonusKey] = { skillId, level, desc: milestone.desc, effect: effects, appliedAt: Date.now() } // 应用全局属性加成 if (!playerStore.globalBonus) { playerStore.globalBonus = { critRate: 0, attackBonus: 0, expRate: 1, critMult: 0, darkPenaltyReduce: 0, globalExpRate: 0, readingSpeed: 1 } } // 应用具体效果 if (effects.critRate) { playerStore.globalBonus.critRate += effects.critRate } if (effects.attackBonus) { playerStore.globalBonus.attackBonus += effects.attackBonus } if (effects.expRate) { playerStore.globalBonus.expRate *= effects.expRate } if (effects.critMult) { playerStore.globalBonus.critMult += effects.critMult } if (effects.darkPenaltyReduce) { playerStore.globalBonus.darkPenaltyReduce += effects.darkPenaltyReduce } if (effects.globalExpRate) { playerStore.globalBonus.globalExpRate += effects.globalExpRate } if (effects.readingSpeed) { playerStore.globalBonus.readingSpeed *= effects.readingSpeed } return effects } /** * 更新父技能等级 * @param {Object} playerStore - 玩家Store * @param {String} skillId - 子技能ID */ export function updateParentSkill(playerStore, skillId) { const config = SKILL_CONFIG[skillId] if (!config || !config.parentSkill) { return } const parentSkillId = config.parentSkill // 查找所有子技能的最高等级 const childSkills = Object.keys(SKILL_CONFIG).filter(id => SKILL_CONFIG[id].parentSkill === parentSkillId ) let maxChildLevel = 0 for (const childId of childSkills) { const childSkill = playerStore.skills[childId] if (childSkill && childSkill.unlocked) { maxChildLevel = Math.max(maxChildLevel, childSkill.level) } } // 更新父技能等级 if (!playerStore.skills[parentSkillId]) { playerStore.skills[parentSkillId] = { level: 0, exp: 0, unlocked: true } } playerStore.skills[parentSkillId].level = maxChildLevel } /** * 解锁技能 * @param {Object} playerStore - 玩家Store * @param {String} skillId - 技能ID * @returns {boolean} 是否成功解锁 */ export function unlockSkill(playerStore, skillId) { if (!playerStore.skills) { playerStore.skills = {} } const config = SKILL_CONFIG[skillId] if (!config) { return false } // 检查是否已解锁 if (playerStore.skills[skillId] && playerStore.skills[skillId].unlocked) { return true } // 初始化技能数据 playerStore.skills[skillId] = { level: 0, exp: 0, unlocked: true, maxExp: getNextLevelExp(skillId, 0) } return true } /** * 检查技能是否可解锁 * @param {Object} playerStore - 玩家Store * @param {String} skillId - 技能ID * @returns {Object} { canUnlock: boolean, reason: string } */ export function canUnlockSkill(playerStore, skillId) { const config = SKILL_CONFIG[skillId] if (!config) { return { canUnlock: false, reason: '技能不存在' } } // 已解锁 if (playerStore.skills[skillId] && playerStore.skills[skillId].unlocked) { return { canUnlock: true, reason: '已解锁' } } // 检查解锁条件 if (config.unlockCondition) { const condition = config.unlockCondition // 物品条件 if (condition.type === 'item') { const hasItem = playerStore.inventory?.some(i => i.id === condition.item) if (!hasItem) { return { canUnlock: false, reason: '需要特定物品' } } } // 位置条件 if (condition.location) { if (playerStore.currentLocation !== condition.location) { return { canUnlock: false, reason: '需要在特定位置解锁' } } } // 技能等级条件 if (condition.skillLevel) { const requiredSkill = playerStore.skills[condition.skillId] if (!requiredSkill || requiredSkill.level < condition.skillLevel) { return { canUnlock: false, reason: '需要前置技能达到特定等级' } } } } return { canUnlock: true, reason: '' } } /** * 获取技能经验倍率(含父技能加成) * @param {Object} playerStore - 玩家Store * @param {String} skillId - 技能ID * @returns {Number} 经验倍率 */ export function getSkillExpRate(playerStore, skillId) { let rate = 1.0 // 全局经验加成 if (playerStore.globalBonus && playerStore.globalBonus.globalExpRate) { rate *= (1 + playerStore.globalBonus.globalExpRate / 100) } // 父技能加成 const config = SKILL_CONFIG[skillId] if (config && config.parentSkill) { const parentSkill = playerStore.skills[config.parentSkill] if (parentSkill && parentSkill.unlocked) { const parentConfig = SKILL_CONFIG[config.parentSkill] if (parentConfig && parentConfig.milestones) { // 检查父技能里程碑奖励 for (const [level, milestone] of Object.entries(parentConfig.milestones)) { if (parentSkill.level >= parseInt(level) && milestone.effect?.expRate) { rate *= milestone.effect.expRate } } } // 如果子技能等级低于父技能,提供额外加成 if (parentSkill.level > 0 && playerStore.skills[skillId]) { const childLevel = playerStore.skills[skillId].level if (childLevel < parentSkill.level) { rate *= 1.5 // 父技能倍率加成 } } } } return rate } /** * 获取技能显示信息 * @param {Object} playerStore - 玩家Store * @param {String} skillId - 技能ID * @returns {Object|null} 技能显示信息 */ export function getSkillDisplayInfo(playerStore, skillId) { const config = SKILL_CONFIG[skillId] if (!config) { return null } const skillData = playerStore.skills[skillId] || { level: 0, exp: 0, unlocked: false } const maxExp = getNextLevelExp(skillId, skillData.level) const progress = skillData.level >= config.maxLevel ? 1 : skillData.exp / maxExp // 获取已达到的里程碑 const reachedMilestones = [] if (config.milestones && playerStore.milestoneBonuses) { for (const [level, milestone] of Object.entries(config.milestones)) { const bonusKey = `${skillId}_${level}` if (playerStore.milestoneBonuses[bonusKey]) { reachedMilestones.push({ level: parseInt(level), desc: milestone.desc }) } } } return { id: skillId, name: config.name, type: config.type, category: config.category, icon: config.icon, level: skillData.level, maxLevel: config.maxLevel, exp: skillData.exp, maxExp, progress, unlocked: skillData.unlocked, reachedMilestones, nextMilestone: getNextMilestone(config, skillData.level) } } /** * 获取下一个里程碑 * @param {Object} config - 技能配置 * @param {Number} currentLevel - 当前等级 * @returns {Object|null} 下一个里程碑 */ function getNextMilestone(config, currentLevel) { if (!config.milestones) { return null } for (const [level, milestone] of Object.entries(config.milestones)) { if (parseInt(level) > currentLevel) { return { level: parseInt(level), desc: milestone.desc } } } return null } /** * 重置技能系统(用于新游戏) * @param {Object} playerStore - 玩家Store */ export function resetSkillSystem(playerStore) { playerStore.skills = {} playerStore.milestoneBonuses = {} playerStore.globalBonus = { critRate: 0, attackBonus: 0, expRate: 1, critMult: 0, darkPenaltyReduce: 0, globalExpRate: 0, readingSpeed: 1 } }