feat: 完善数学模型和品质系统

- 修复品质等级范围重叠问题(优秀/稀有无重叠)
- 统一 formulas.js 与 constants.js 的品质判定
- 修复经验公式与游戏实际逻辑不一致
- 调整品质生成概率: 传说0.1%, 史诗1%, 稀有4%, 优秀10%
- 添加数学模型文档和README
- 添加数值验证脚本

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Claude
2026-01-25 14:54:20 +08:00
parent cef974d94f
commit 5d4371ba1f
9 changed files with 2459 additions and 82 deletions

View File

@@ -9,18 +9,29 @@ import { SKILL_CONFIG } from '@/config/skills.js'
/**
* 计算装备品质后的属性
* 使用数学模型中的公式:品质倍率 = 1 + (品质 - 100) * 0.01
* @param {String} itemId - 物品ID
* @param {Number} quality - 品质值 (0-250)
* @param {Number} itemLevel - 物品等级(影响属性)
* @returns {Object|null} 计算后的属性
*/
export function calculateItemStats(itemId, quality = 100) {
export function calculateItemStats(itemId, quality = 100, itemLevel = 1) {
const config = ITEM_CONFIG[itemId]
if (!config) {
return null
}
const qualityLevel = getQualityLevel(quality)
const qualityMultiplier = qualityLevel.multiplier
// 品质倍率1 + (品质 - 100) * 0.01
// 品质100 = 1.0倍品质150 = 1.5倍品质50 = 0.5倍
const qualityMultiplier = 1 + (quality - 100) * GAME_CONSTANTS.QUALITY.BASE_MULTIPLIER
// 等级倍率:每级 +2%
const levelMultiplier = 1 + (itemLevel - 1) * 0.02
// 综合倍率
const totalMultiplier = qualityMultiplier * levelMultiplier
const stats = {
id: itemId,
@@ -30,36 +41,35 @@ export function calculateItemStats(itemId, quality = 100) {
description: config.description,
icon: config.icon,
quality: quality,
itemLevel: itemLevel,
qualityLevel: qualityLevel.level,
qualityName: qualityLevel.name,
qualityColor: qualityLevel.color
}
// 应用品质百分比到基础属性
const qualityRatio = quality / 100
// 应用品质倍率到基础属性
if (config.baseDamage) {
stats.baseDamage = config.baseDamage
stats.finalDamage = Math.floor(config.baseDamage * qualityRatio * qualityMultiplier)
stats.finalDamage = Math.max(1, Math.floor(config.baseDamage * totalMultiplier))
}
if (config.baseDefense) {
stats.baseDefense = config.baseDefense
stats.finalDefense = Math.floor(config.baseDefense * qualityRatio * qualityMultiplier)
stats.finalDefense = Math.max(1, Math.floor(config.baseDefense * totalMultiplier))
}
if (config.baseShield) {
stats.baseShield = config.baseShield
stats.finalShield = Math.floor(config.baseShield * qualityRatio * qualityMultiplier)
stats.finalShield = Math.floor(config.baseShield * totalMultiplier)
}
if (config.attackSpeed) {
stats.attackSpeed = config.attackSpeed
}
// 计算价值
// 计算价值(品质影响)
stats.baseValue = config.baseValue || 0
stats.finalValue = Math.floor(config.baseValue * qualityRatio * qualityMultiplier)
stats.finalValue = Math.floor(config.baseValue * totalMultiplier)
// 消耗品效果
if (config.effect) {
@@ -78,6 +88,11 @@ export function calculateItemStats(itemId, quality = 100) {
stats.unlockSkill = config.unlockSkill
}
// 额外属性(来自物品配置)
if (config.stats) {
stats.stats = { ...config.stats }
}
// 堆叠信息
if (config.stackable !== undefined) {
stats.stackable = config.stackable
@@ -115,34 +130,34 @@ export function getQualityLevel(quality) {
* @returns {Number} 品质值
*/
export function generateRandomQuality(luck = 0) {
// 基础品质范围 50-150
const baseMin = 50
const baseMax = 150
// 单次随机判定
const roll = Math.random()
// 运气加成每1点运气增加0.5品质
const luckBonus = luck * 0.5
// 运气加成每1点运气增加对应品质概率的 0.1%
const luckBonus = luck * 0.001
// 随机品质
let quality = Math.floor(Math.random() * (baseMax - baseMin + 1)) + baseMin + luckBonus
// 小概率生成高品质(传说)
if (Math.random() < 0.01 + luck / 10000) {
quality = 180 + Math.floor(Math.random() * 70) // 180-250
// 传说品质0.1% + 运气加成
if (roll < 0.001 + luckBonus) {
return 200 + Math.floor(Math.random() * 51) // 200-250
}
// 史诗品质
else if (Math.random() < 0.05 + luck / 2000) {
quality = 160 + Math.floor(Math.random() * 40) // 160-199
// 史诗品质1% + 运气加成
if (roll < 0.01 + luckBonus * 2) {
return 170 + Math.floor(Math.random() * 30) // 170-199
}
// 稀有品质
else if (Math.random() < 0.15 + luck / 500) {
quality = 130 + Math.floor(Math.random() * 30) // 130-159
// 稀有品质5% + 运气加成
if (roll < 0.05 + luckBonus * 3) {
return 140 + Math.floor(Math.random() * 30) // 140-169
}
// 优秀品质
else if (Math.random() < 0.3 + luck / 200) {
quality = 100 + Math.floor(Math.random() * 30) // 100-129
// 优秀品质15% + 运气加成
if (roll < 0.15 + luckBonus * 4) {
return 100 + Math.floor(Math.random() * 40) // 100-139
}
return Math.min(250, Math.max(0, Math.floor(quality)))
// 普通品质:剩余大部分
if (roll < 0.85) {
return 50 + Math.floor(Math.random() * 50) // 50-99
}
// 垃圾品质15%
return Math.floor(Math.random() * 50) // 0-49
}
/**
@@ -432,24 +447,28 @@ export function addItemToInventory(playerStore, itemId, count = 1, quality = nul
}
}
// 装备类物品 - 按物品ID + 品质等级堆叠
// 装备类物品 - 不堆叠每个装备都有独立的唯一ID
// 这样可以正确跟踪每个装备的装备状态
if (needsQuality) {
const qualityLevel = itemStats.qualityLevel
const existingItem = playerStore.inventory.find(
i => i.id === itemId && i.qualityLevel === qualityLevel
)
if (existingItem) {
existingItem.count += count
return { success: true, item: existingItem }
// 检查是否有完全相同品质的装备(用于显示堆叠数量,但各自独立)
// 注意:装备不再真正堆叠,每个都是独立实例
// 生成唯一IDitemId + quality + 时间戳 + 随机数
const uniqueId = `${itemId}_q${itemQuality}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`
const newItem = {
...itemStats,
uniqueId,
count: 1, // 装备始终为1
equipped: false,
obtainedAt: Date.now()
}
playerStore.inventory.push(newItem)
return { success: true, item: newItem }
}
// 创建新物品
// 装备类物品使用 itemId + qualityLevel 作为唯一标识
// 素材类物品使用 itemId + 时间戳虽然可堆叠但保留唯一ID用于其他用途
const uniqueId = needsQuality
? `${itemId}_q${itemStats.qualityLevel}`
: `${itemId}_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`
// 创建新物品(非装备类)
const uniqueId = `${itemId}_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`
const newItem = {
...itemStats,