feat: 实现游戏核心系统和UI组件
核心系统: - 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>
This commit is contained in:
287
utils/levelingSystem.js
Normal file
287
utils/levelingSystem.js
Normal file
@@ -0,0 +1,287 @@
|
||||
/**
|
||||
* 等级系统 - 经验曲线、升级计算
|
||||
* 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
|
||||
}
|
||||
Reference in New Issue
Block a user