/** * 等级系统 - 经验曲线、升级计算 * Phase 4 数值调整 - 等级经验曲线 */ /** * 经验曲线类型 */ export const EXP_CURVE_TYPES = { LINEAR: 'linear', // 线性增长: base * level QUADRATIC: 'quadratic', // 二次增长: base * level^2 EXPONENTIAL: 'exponential', // 指数增长: base * multiplier^level HYBRID: 'hybrid' // 混合曲线: 更平滑的体验 } /** * 等级配置 */ export const LEVEL_CONFIG = { // 经验曲线类型 expCurveType: EXP_CURVE_TYPES.HYBRID, // 基础经验值 (1级升级所需) baseExp: 100, // 经验增长倍率 (每级增长) expMultiplier: 1.15, // 最大等级 maxLevel: 50, // 等级属性加成 (每级获得的属性点) statPointsPerLevel: 3, // 初始属性 baseStats: { strength: 10, agility: 8, dexterity: 8, intuition: 10, vitality: 10 } } /** * 计算指定等级所需的总经验 * @param {Number} level - 目标等级 * @returns {Number} 所需经验 */ export function getExpForLevel(level) { if (level <= 1) return 0 const { expCurveType, baseExp, expMultiplier } = LEVEL_CONFIG switch (expCurveType) { case EXP_CURVE_TYPES.LINEAR: // 线性: 100, 200, 300, 400... return baseExp * (level - 1) case EXP_CURVE_TYPES.QUADRATIC: // 二次: 100, 400, 900, 1600... return baseExp * Math.pow(level - 1, 2) case EXP_CURVE_TYPES.EXPONENTIAL: // 指数: 100, 115, 132, 152... return baseExp * Math.pow(expMultiplier, level - 1) case EXP_CURVE_TYPES.HYBRID: default: // 混合曲线 - 平滑的增长体验 // 公式: base * (1.15 ^ (level - 1)) * level / 2 // 结果: Lv2=100, Lv5=700, Lv10=2800, Lv20=15000, Lv30=55000, Lv50=250000 return Math.floor(baseExp * Math.pow(expMultiplier, level - 2) * (level - 1) * 0.8) } } /** * 计算从当前等级到下一等级所需的经验 * @param {Number} currentLevel - 当前等级 * @returns {Number} 升级所需经验 */ export function getExpToNextLevel(currentLevel) { if (currentLevel >= LEVEL_CONFIG.maxLevel) { return 0 // 已达最高等级 } return getExpForLevel(currentLevel + 1) - getExpForLevel(currentLevel) } /** * 检查并处理升级 * @param {Object} playerStore - 玩家Store * @param {Object} gameStore - 游戏Store * @returns {Object} { leveledUp: boolean, newLevel: number, levelsGained: number } */ export function checkLevelUp(playerStore, gameStore) { const currentLevel = playerStore.level.current if (currentLevel >= LEVEL_CONFIG.maxLevel) { return { leveledUp: false, newLevel: currentLevel, levelsGained: 0 } } let levelsGained = 0 let newLevel = currentLevel let remainingExp = playerStore.level.exp // 连续升级检查(支持一次获得大量经验连升多级) while (newLevel < LEVEL_CONFIG.maxLevel) { const expNeeded = getExpToNextLevel(newLevel) if (remainingExp >= expNeeded) { remainingExp -= expNeeded newLevel++ levelsGained++ } else { break } } if (levelsGained > 0) { // 应用升级 const oldLevel = playerStore.level.current playerStore.level.current = newLevel playerStore.level.exp = remainingExp playerStore.level.maxExp = getExpToNextLevel(newLevel) // 应用等级属性加成 applyLevelStats(playerStore, oldLevel, newLevel) // 记录日志 if (levelsGained === 1) { gameStore?.addLog(`升级了! 等级: ${newLevel}`, 'reward') } else { gameStore?.addLog(`连升${levelsGained}级! 等级: ${newLevel}`, 'reward') } // 升级恢复状态 playerStore.currentStats.health = playerStore.currentStats.maxHealth playerStore.currentStats.stamina = playerStore.currentStats.maxStamina playerStore.currentStats.sanity = playerStore.currentStats.maxSanity return { leveledUp: true, newLevel, levelsGained } } return { leveledUp: false, newLevel: currentLevel, levelsGained: 0 } } /** * 应用等级属性加成 * @param {Object} playerStore - 玩家Store * @param {Number} oldLevel - 旧等级 * @param {Number} newLevel - 新等级 */ function applyLevelStats(playerStore, oldLevel, newLevel) { const levelsGained = newLevel - oldLevel const statPoints = levelsGained * LEVEL_CONFIG.statPointsPerLevel // 自动分配属性点 (简化版: 均匀分配) // 实际游戏中可以让玩家手动选择 const stats = ['strength', 'agility', 'dexterity', 'intuition', 'vitality'] const pointsPerStat = Math.floor(statPoints / stats.length) const remainder = statPoints % stats.length stats.forEach((stat, index) => { playerStore.baseStats[stat] += pointsPerStat if (index < remainder) { playerStore.baseStats[stat] += 1 } }) // 更新生命值上限 (体质影响HP) const hpPerVitality = 10 const oldMaxHp = playerStore.currentStats.maxHealth const newMaxHp = 100 + (playerStore.baseStats.vitality - 10) * hpPerVitality if (newMaxHp !== oldMaxHp) { playerStore.currentStats.maxHealth = newMaxHp // 按比例恢复HP const hpRatio = playerStore.currentStats.health / oldMaxHp playerStore.currentStats.health = Math.floor(newMaxHp * hpRatio) } } /** * 添加经验值 * @param {Object} playerStore - 玩家Store * @param {Object} gameStore - 游戏Store * @param {Number} exp - 获得的经验值 * @returns {Object} { leveledUp: boolean, newLevel: number } */ export function addExp(playerStore, gameStore, exp) { playerStore.level.exp += exp return checkLevelUp(playerStore, gameStore) } /** * 获取等级信息 * @param {Number} level - 等级 * @returns {Object} 等级信息 */ export function getLevelInfo(level) { const totalExp = getExpForLevel(level) const toNext = getExpToNextLevel(level) const isMaxLevel = level >= LEVEL_CONFIG.maxLevel return { level, totalExp, toNext, isMaxLevel, maxLevel: LEVEL_CONFIG.maxLevel } } /** * 获取当前经验进度百分比 * @param {Object} playerStore - 玩家Store * @returns {Number} 0-100 */ export function getExpProgress(playerStore) { const currentLevel = playerStore.level.current if (currentLevel >= LEVEL_CONFIG.maxLevel) { return 100 } const toNext = getExpToNextLevel(currentLevel) const current = playerStore.level.exp return Math.min(100, Math.floor((current / toNext) * 100)) } /** * 根据敌人等级计算经验奖励 * @param {Number} enemyLevel - 敌人等级 * @param {Number} playerLevel - 玩家等级 * @param {Number} baseExp - 基础经验值 * @returns {Number} 调整后的经验值 */ export function calculateCombatExp(enemyLevel, playerLevel, baseExp) { const levelDiff = enemyLevel - playerLevel // 等级差异调整 let multiplier = 1.0 if (levelDiff > 5) { // 敌人等级远高于玩家 - 额外奖励 multiplier = 1.5 } else if (levelDiff > 2) { // 敌人等级稍高 - 小幅奖励 multiplier = 1.2 } else if (levelDiff < -5) { // 敌人等级远低于玩家 - 大幅惩罚 multiplier = 0.1 } else if (levelDiff < -2) { // 敌人等级稍低 - 小幅惩罚 multiplier = 0.5 } return Math.floor(baseExp * multiplier) } /** * 获取等级称号 * @param {Number} level - 等级 * @returns {String} 称号 */ export function getLevelTitle(level) { const titles = { 1: '新手', 5: '冒险者', 10: '老手', 15: '精英', 20: '专家', 25: '大师', 30: '宗师', 35: '传奇', 40: '英雄', 45: '神话', 50: '至尊' } let title = '幸存者' for (const [lvl, t] of Object.entries(titles).sort((a, b) => b[0] - a[0])) { if (level >= parseInt(lvl)) { title = t break } } return title }