核心系统: - 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>
360 lines
9.0 KiB
JavaScript
360 lines
9.0 KiB
JavaScript
/**
|
|
* 技能系统 - 技能经验、升级、里程碑、父技能同步
|
|
* 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|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
|
|
}
|
|
}
|