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:
Claude
2026-01-23 16:20:10 +08:00
parent 021f6a54f5
commit 16223c89a5
25 changed files with 2731 additions and 318 deletions

287
utils/levelingSystem.js Normal file
View 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
}