mirror of
https://github.com/babalae/bettergi-scripts-list.git
synced 2026-03-26 05:09:52 +08:00
* Add files via upload * Add files via upload * Add files via upload * Delete repo/js/RoleExperienceCalculation/desktop.ini
271 lines
11 KiB
JavaScript
271 lines
11 KiB
JavaScript
// 角色经验计算 - 计算模块
|
||
|
||
// 经验
|
||
var expCalculator= {
|
||
// 核心数据:1-90级每级升级所需经验值 (来源: 原神WIKI)[citation:5]
|
||
LEVEL_EXP_REQUIREMENTS: [
|
||
0, // Lv1 之前为0
|
||
1000, 1325, 1700, 2150, 2625, // Lv1-5
|
||
3150, 3725, 4350, 5000, 5700, // Lv6-10
|
||
6450, 7225, 8050, 8925, 9825, // Lv11-15
|
||
10750, 11725, 12725, 13775, 14875, // Lv16-20
|
||
16800, 18000, 19250, 20550, 21875, // Lv21-25
|
||
23250, 24650, 26100, 27575, 29100, // Lv26-30
|
||
30650, 32250, 33875, 35550, 37250, // Lv31-35
|
||
38975, 40750, 42575, 44425, 46300, // Lv36-40
|
||
50625, 52700, 54775, 56900, 59075, // Lv41-45
|
||
61275, 63525, 65800, 68125, 70475, // Lv46-50
|
||
76500, 79050, 81650, 84275, 86950, // Lv51-55
|
||
89650, 92400, 95175, 98000, 100875, // Lv56-60
|
||
108950, 112050, 115175, 118325, 121525, // Lv61-65
|
||
124775, 128075, 131400, 134775, 138175, // Lv66-70
|
||
148700, 152375, 156075, 159825, 163600, // Lv71-75
|
||
167425, 171300, 175225, 179175, 183175, // Lv76-80
|
||
216225, 243025, 273100, 306800, 344600, // Lv81-85
|
||
386950, 434425, 487625, 547200, 0 // Lv86-90 (90级为满级)
|
||
],
|
||
|
||
// 经验书定义[citation:2]
|
||
EXP_BOOKS: {
|
||
PURPLE: {name: '大英雄的经验', experience: 20000},
|
||
BLUE: {name: '冒险家的经验', experience: 5000},
|
||
GREEN: {name: '流浪者的经验', experience: 1000}
|
||
},
|
||
|
||
/**
|
||
* 计算从当前等级/经验升级到目标等级所需的总经验值。
|
||
* @param {number} currentLevel - 当前等级 (1-89)
|
||
* @param {number} currentExp - 当前等级下已累积的经验值
|
||
* @param {number} targetLevel - 目标等级 (2-90)
|
||
* @returns {number} 所需总经验值,如果输入无效返回 -1
|
||
*/
|
||
calculateExpRequired: function (currentLevel, currentExp, targetLevel) {
|
||
// 参数校验
|
||
if (currentLevel < 1 || currentLevel >= 90 ||
|
||
targetLevel <= currentLevel || targetLevel > 90 ||
|
||
currentExp < 0) {
|
||
console.error('Invalid input parameters.');
|
||
return -1;
|
||
}
|
||
|
||
let totalExpNeeded = 0;
|
||
|
||
// 1. 减去当前等级已积累的经验
|
||
totalExpNeeded -= currentExp;
|
||
|
||
// 2. 累加从当前等级到目标等级-1所需的每一级经验
|
||
for (let lvl = currentLevel; lvl < targetLevel; lvl++) {
|
||
totalExpNeeded += expCalculator.LEVEL_EXP_REQUIREMENTS[lvl];
|
||
}
|
||
|
||
return totalExpNeeded;
|
||
},
|
||
|
||
/**
|
||
* 将经验值转换为所需的各种经验书数量(优先使用高等级书籍)。
|
||
* @param {number} expRequired - 所需总经验值
|
||
* @returns {Object} 包含三种经验书所需数量的对象
|
||
*/
|
||
convertExpToBooks: function (expRequired) {
|
||
let remainingExp = expRequired;
|
||
|
||
const purpleBooks = Math.floor(remainingExp / expCalculator.EXP_BOOKS.PURPLE.experience);
|
||
remainingExp %= expCalculator.EXP_BOOKS.PURPLE.experience;
|
||
|
||
const blueBooks = Math.floor(remainingExp / expCalculator.EXP_BOOKS.BLUE.experience);
|
||
remainingExp %= expCalculator.EXP_BOOKS.BLUE.experience;
|
||
|
||
const greenBooks = Math.ceil(remainingExp / expCalculator.EXP_BOOKS.GREEN.experience); // 向上取整,因为无法提供不足1000的经验
|
||
|
||
return {
|
||
purple: purpleBooks,
|
||
blue: blueBooks,
|
||
green: greenBooks,
|
||
// 返回一个详细的文本摘要,方便直接使用
|
||
summary: `共需: ${purpleBooks}本[${expCalculator.EXP_BOOKS.PURPLE.name}] + ${blueBooks}本[${expCalculator.EXP_BOOKS.BLUE.name}] + ${greenBooks}本[${expCalculator.EXP_BOOKS.GREEN.name}]`
|
||
};
|
||
}
|
||
}
|
||
|
||
// 树脂
|
||
var resinCalculation = {
|
||
// 世界等级掉落配置:每种经验书的 [最小数量, 最大数量]
|
||
WORLD_LEVEL_DROP_CONFIG : {
|
||
0: { '流浪者的经验': [7, 8], '冒险家的经验': [3, 4] },
|
||
1: { '流浪者的经验': [10, 12], '冒险家的经验': [5, 6] },
|
||
2: { '冒险家的经验': [10, 11] },
|
||
3: { '冒险家的经验': [13, 14] },
|
||
4: { '大英雄的经验': [2, 3], '冒险家的经验': [6, 7] },
|
||
5: { '大英雄的经验': [3, 4], '冒险家的经验': [6, 7] },
|
||
6: { '大英雄的经验': [4, 5], '冒险家的经验': [6, 7] },
|
||
7: { '大英雄的经验': [4, 5], '冒险家的经验': [6, 7] },
|
||
8: { '大英雄的经验': [4, 5], '冒险家的经验': [6, 7] },
|
||
9: { '大英雄的经验': [4, 5], '冒险家的经验': [6, 7] }
|
||
},
|
||
|
||
/**
|
||
* 计算单次刷取的期望经验值和概率分布
|
||
* @param {string|boolean} worldLevel - 世界等级
|
||
* @returns {Object} {expectedExp, minExp, distribution}
|
||
*/
|
||
calculateSingleRunExp: function(worldLevel) {
|
||
// 添加中文到英文的映射
|
||
const BOOK_NAME_MAPPING = {
|
||
'流浪者的经验': 'GREEN',
|
||
'冒险家的经验': 'BLUE',
|
||
'大英雄的经验': 'PURPLE'
|
||
};
|
||
|
||
const config = this.WORLD_LEVEL_DROP_CONFIG[worldLevel];
|
||
if (!config || Object.keys(config).length === 0) {
|
||
return { expectedExp: 0, minExp: 0, distribution: [] };
|
||
}
|
||
|
||
let outcomeDetails = [];
|
||
for (const [chineseBookName, range] of Object.entries(config)) {
|
||
const englishKey = BOOK_NAME_MAPPING[chineseBookName];
|
||
|
||
// 检查映射是否成功
|
||
if (!englishKey) {
|
||
console.warn(`未找到对应的英文键名: ${chineseBookName}`);
|
||
continue;
|
||
}
|
||
|
||
const [min, max] = range;
|
||
const possibilities = [];
|
||
|
||
// 使用英文键名访问 EXP_BOOKS
|
||
const bookConfig = expCalculator.EXP_BOOKS[englishKey];
|
||
if (!bookConfig) {
|
||
console.warn(`在 EXP_BOOKS 中未找到: ${englishKey}`);
|
||
continue;
|
||
}
|
||
|
||
for (let qty = min; qty <= max; qty++) {
|
||
possibilities.push({
|
||
bookKey: englishKey,
|
||
chineseName: chineseBookName, // 可选:保留中文名
|
||
qty,
|
||
experience: bookConfig.experience * qty
|
||
});
|
||
}
|
||
outcomeDetails.push(possibilities);
|
||
}
|
||
|
||
// 如果 outcomeDetails 为空,返回默认值
|
||
if (outcomeDetails.length === 0) {
|
||
console.error('没有有效的掉落配置');
|
||
return { expectedExp: 0, minExp: 0, distribution: [] };
|
||
}
|
||
|
||
// 计算所有组合的概率分布(假设每种数量概率相等)
|
||
const distributionMap = new Map();
|
||
|
||
function dfs(index, currentExp, probability) {
|
||
if (index === outcomeDetails.length) {
|
||
distributionMap.set(currentExp, (distributionMap.get(currentExp) || 0) + probability);
|
||
return;
|
||
}
|
||
for (const outcome of outcomeDetails[index]) {
|
||
dfs(index + 1, currentExp + outcome.experience, probability / outcomeDetails[index].length);
|
||
}
|
||
}
|
||
|
||
dfs(0, 0, 1.0);
|
||
|
||
// 转换为排序后的数组并计算期望值
|
||
let distribution = [];
|
||
let expectedExp = 0;
|
||
let minExp = Infinity;
|
||
|
||
for (const [experience, prob] of distributionMap.entries()) {
|
||
distribution.push({ experience, probability: prob });
|
||
expectedExp += experience * prob;
|
||
if (experience < minExp) minExp = experience;
|
||
}
|
||
|
||
distribution.sort((a, b) => a.experience - b.experience);
|
||
|
||
return {
|
||
expectedExp: Math.round(expectedExp),
|
||
minExp: minExp,
|
||
distribution
|
||
};
|
||
},
|
||
|
||
/**
|
||
* 主计算函数:分别计算使用浓缩树脂和原粹树脂的刷取次数
|
||
* @param {number} requiredExp - 所需经验值
|
||
* @param {string|boolean} worldLevel - 世界等级 (0-9)
|
||
* @param {{}} options - 计算选项
|
||
* @param {boolean} options.useExpected - true:使用期望值计算(平均情况), false:使用保底值计算(最非情况)
|
||
* @param {number} options.resinPerCondensed - 合成一个浓缩树脂所需的原粹树脂,默认为60
|
||
* @returns {Object} 计算结果
|
||
*/
|
||
calculateExpBookRequirements: function(requiredExp, worldLevel, options = {}) {
|
||
const {
|
||
useExpected = true,
|
||
resinPerCondensed = 60
|
||
} = options;
|
||
|
||
// 获取单次刷取的经验数据
|
||
const { expectedExp, minExp, distribution } = this.calculateSingleRunExp(worldLevel);
|
||
|
||
if (expectedExp === 0) {
|
||
return {
|
||
error: `世界等级 ${worldLevel} 的掉落配置不完整,无法计算。请补充 WORLD_LEVEL_DROP_CONFIG 中的数据。`
|
||
};
|
||
}
|
||
|
||
// 选择计算基准:期望值(平均)或保底值(最小)
|
||
const expPerRun = useExpected ? expectedExp : minExp;
|
||
const calcMode = useExpected ? '期望值(平均)' : '保底值(最小)';
|
||
|
||
// 计算总刷取次数
|
||
const totalRuns = Math.ceil(requiredExp / expPerRun);
|
||
|
||
// 计算浓缩树脂相关次数
|
||
const condensedRuns = Math.ceil(totalRuns / (resinPerCondensed / 20)); // 全部使用浓缩树脂的次数
|
||
// 原粹树脂需要刷取的次数(如果没有浓缩树脂)
|
||
const originalResinRuns = totalRuns; // 全部使用原粹树脂的次数
|
||
|
||
// 计算原粹树脂总消耗量
|
||
const totalOriginalResin = totalRuns * 20; // 每次副本固定消耗20原粹树脂
|
||
|
||
// 计算需要合成的浓缩树脂数量
|
||
const condensedResinNeeded = Math.ceil(totalOriginalResin / resinPerCondensed);
|
||
|
||
return {
|
||
// 计算基准信息
|
||
calcMode,
|
||
expPerRunUsed: expPerRun,
|
||
singleRunStats: {
|
||
expectedExp,
|
||
minExp,
|
||
possibleExpRange: distribution.length > 0 ?
|
||
`${distribution[0].experience} ~ ${distribution[distribution.length - 1].experience}` : 'N/A'
|
||
},
|
||
|
||
// 核心结果:分别输出两种树脂的刷取次数
|
||
runs: {
|
||
// 使用浓缩树脂需要刷取的次数
|
||
usingCondensedResin: condensedRuns,
|
||
// 使用原粹树脂需要刷取的次数
|
||
usingOriginalResin: originalResinRuns,
|
||
// 总副本挑战次数(两种方式相同)
|
||
totalChallenges: totalRuns
|
||
},
|
||
|
||
// 资源消耗
|
||
resinCost: {
|
||
// 原粹树脂总消耗量
|
||
totalOriginalResin,
|
||
// 需要合成的浓缩树脂数量
|
||
condensedResinNeeded,
|
||
// 合成浓缩树脂所需原粹树脂总量
|
||
resinForCondensed: condensedResinNeeded * resinPerCondensed
|
||
},
|
||
};
|
||
},
|
||
}
|