You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
287 lines
9.7 KiB
287 lines
9.7 KiB
|
3 weeks ago
|
/**
|
||
|
|
* 解析 Battlebuff.functionpar 中带符号整数。
|
||
|
|
* 支持 `+10`、`-5`、`10`、数值 10。
|
||
|
|
* @returns 整数;无法解析时 null
|
||
|
|
*/
|
||
|
|
export function parseSignedIntFromFunctionPar(raw: string | number | null | undefined): number | null {
|
||
|
|
if (raw == null || raw === '') return null;
|
||
|
|
if (typeof raw === 'number') {
|
||
|
|
return Number.isFinite(raw) ? Math.trunc(raw) : null;
|
||
|
|
}
|
||
|
|
const text = String(raw).trim();
|
||
|
|
if (!text) return null;
|
||
|
|
const m = /^([+-]?)(\d+(?:\.\d+)?)$/.exec(text);
|
||
|
|
if (!m) return null;
|
||
|
|
const sign = m[1] === '-' ? -1 : 1;
|
||
|
|
const n = Number(m[2]);
|
||
|
|
if (!Number.isFinite(n)) return null;
|
||
|
|
return sign * Math.trunc(n);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 解析 functionpar 为逗号分隔数字列表。
|
||
|
|
* @param raw 表字段(如 "60,4000")
|
||
|
|
*/
|
||
|
|
export function parseFunctionParNums(raw: string | number | null | undefined): number[] {
|
||
|
|
if (raw == null || raw === '') return [];
|
||
|
|
if (typeof raw === 'number') {
|
||
|
|
return Number.isFinite(raw) ? [Math.trunc(raw)] : [];
|
||
|
|
}
|
||
|
|
const text = String(raw).trim();
|
||
|
|
if (!text) return [];
|
||
|
|
const parts = text.split(',');
|
||
|
|
const out: number[] = [];
|
||
|
|
for (let i = 0; i < parts.length; i++) {
|
||
|
|
const n = parseSignedIntFromFunctionPar(parts[i].trim());
|
||
|
|
if (n != null) out.push(n);
|
||
|
|
}
|
||
|
|
return out;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 解析体力 buff 增量(functiontype=1)。
|
||
|
|
* @returns 正整数增量;无效时 0
|
||
|
|
*/
|
||
|
|
export function parseBattleBuffVigourDelta(raw: string | number | null | undefined): number {
|
||
|
|
const n = parseSignedIntFromFunctionPar(raw);
|
||
|
|
if (n == null || n <= 0) return 0;
|
||
|
|
return n;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 解析主动「缚仙网」buff 的网住时长(functionpar 第二段,秒)。
|
||
|
|
* @returns 正数秒;无效时 0
|
||
|
|
*/
|
||
|
|
export function parseBattleBuffBindNetDuration(raw: string | number | null | undefined): number {
|
||
|
|
const nums = parseFunctionParNums(raw);
|
||
|
|
if (nums.length < 2) return 0;
|
||
|
|
const sec = nums[1];
|
||
|
|
return Number.isFinite(sec) && sec > 0 ? sec : 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 解析主动「幻魂镜」buff 的迷幻时长(functionpar 第二段,秒)。
|
||
|
|
* @returns 正数秒;无效时 0
|
||
|
|
*/
|
||
|
|
export function parseBattleBuffConfuseHeroDuration(raw: string | number | null | undefined): number {
|
||
|
|
const nums = parseFunctionParNums(raw);
|
||
|
|
if (nums.length < 2) return 0;
|
||
|
|
const sec = nums[1];
|
||
|
|
return Number.isFinite(sec) && sec > 0 ? sec : 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 解析主动「拖到单位升级」buff 的等级增量(functionpar 第二段)。
|
||
|
|
* @returns 正整数;无效时 0
|
||
|
|
*/
|
||
|
|
export function parseBattleBuffUnitLevelDelta(raw: string | number | null | undefined): number {
|
||
|
|
const nums = parseFunctionParNums(raw);
|
||
|
|
if (nums.length < 2) return 0;
|
||
|
|
const delta = Math.floor(nums[1]);
|
||
|
|
return delta > 0 ? delta : 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 解析主动「乾坤袋」buff 的回收蟠桃数量(functionpar 第二段)。
|
||
|
|
* @returns 正整数;无效时 0
|
||
|
|
*/
|
||
|
|
export function parseBattleBuffRecyclePeachCount(raw: string | number | null | undefined): number {
|
||
|
|
const nums = parseFunctionParNums(raw);
|
||
|
|
if (nums.length < 2) return 0;
|
||
|
|
const count = Math.floor(nums[1]);
|
||
|
|
return count > 0 ? count : 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 万分比满值 */
|
||
|
|
const WAN_DENOM = 10000;
|
||
|
|
|
||
|
|
/** 授仙箓(functiontype=7)升级概率配置 */
|
||
|
|
export type BattleBuffRandomLevelConfig = {
|
||
|
|
/** L1→2 … L4→5 万分比,长度 4 */
|
||
|
|
levelUpProbsWan: number[];
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 解析授仙箓 functionpar:CD,L1→2,L2→3,L3→4,L4→5(万分比)。
|
||
|
|
* @returns 配置;参数不足时 null
|
||
|
|
*/
|
||
|
|
export function parseBattleBuffRandomLevelConfig(raw: string | number | null | undefined): BattleBuffRandomLevelConfig | null {
|
||
|
|
const nums = parseFunctionParNums(raw);
|
||
|
|
if (nums.length < 5) {
|
||
|
|
console.log('[BattleBuff] 授仙箓 functionpar 需 5 段');
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
const levelUpProbsWan: number[] = [];
|
||
|
|
for (let i = 1; i <= 4; i++) {
|
||
|
|
const p = Math.floor(Number(nums[i]) || 0);
|
||
|
|
levelUpProbsWan.push(Math.max(0, Math.min(WAN_DENOM, p)));
|
||
|
|
}
|
||
|
|
return { levelUpProbsWan };
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 授仙箓掷骰:成功则 +1,否则 -1(1 级失败不变)。
|
||
|
|
* @param rollWan 0~9999
|
||
|
|
*/
|
||
|
|
/** 攻速符(functiontype=17)配置 */
|
||
|
|
export type BattleBuffAttackSpeedConfig = {
|
||
|
|
/** 冷却秒数 */
|
||
|
|
cdSec: number;
|
||
|
|
/** 攻速加成万分比(4000=+40%) */
|
||
|
|
bonusWan: number;
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 解析攻速符 functionpar:CD,加成系数。
|
||
|
|
* @returns 配置;参数无效时 null
|
||
|
|
*/
|
||
|
|
export function parseBattleBuffAttackSpeedConfig(raw: string | number | null | undefined): BattleBuffAttackSpeedConfig | null {
|
||
|
|
const nums = parseFunctionParNums(raw);
|
||
|
|
if (nums.length < 2) {
|
||
|
|
console.log('[BattleBuff] 攻速符 functionpar 需 2 段');
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
const cdSec = Math.floor(nums[0]);
|
||
|
|
const bonusWan = Math.floor(nums[1]);
|
||
|
|
if (cdSec <= 0 || bonusWan <= 0) return null;
|
||
|
|
return { cdSec, bonusWan };
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 三昧真火(functiontype=24)配置 */
|
||
|
|
export type BattleBuffFireTrapConfig = {
|
||
|
|
/** 冷却秒数 */
|
||
|
|
cdSec: number;
|
||
|
|
/** 单次伤害上限 */
|
||
|
|
maxDamage: number;
|
||
|
|
/** 陷阱持续时间(秒) */
|
||
|
|
durationSec: number;
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 解析三昧真火 functionpar:CD,伤害最大值,陷阱持续时间。
|
||
|
|
* @returns 配置;参数无效时 null
|
||
|
|
*/
|
||
|
|
export function parseBattleBuffFireTrapConfig(raw: string | number | null | undefined): BattleBuffFireTrapConfig | null {
|
||
|
|
const nums = parseFunctionParNums(raw);
|
||
|
|
if (nums.length < 3) {
|
||
|
|
console.log('[BattleBuff] 三昧真火 functionpar 需 3 段');
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
const cdSec = Math.floor(nums[0]);
|
||
|
|
const maxDamage = Math.floor(nums[1]);
|
||
|
|
const durationSec = Math.floor(nums[2]);
|
||
|
|
if (cdSec <= 0 || maxDamage <= 0 || durationSec <= 0) return null;
|
||
|
|
return { cdSec, maxDamage, durationSec };
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 掘宝仙铲(functiontype=10)蟠桃数量区间 */
|
||
|
|
export type BattleBuffTreasureShovelPeachRange = {
|
||
|
|
/** 最小值(含) */
|
||
|
|
min: number;
|
||
|
|
/** 最大值(含) */
|
||
|
|
max: number;
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 解析掘宝仙铲 functionpar:桃子最小值,最大值。
|
||
|
|
* @returns 区间;参数无效时 null
|
||
|
|
*/
|
||
|
|
export function parseBattleBuffTreasureShovelPeachRange(raw: string | number | null | undefined): BattleBuffTreasureShovelPeachRange | null {
|
||
|
|
const nums = parseFunctionParNums(raw);
|
||
|
|
if (nums.length < 2) {
|
||
|
|
console.log('[BattleBuff] 掘宝仙铲 functionpar 需 2 段');
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
const min = Math.floor(nums[0]);
|
||
|
|
const max = Math.floor(nums[1]);
|
||
|
|
if (min <= 0 || max <= 0) return null;
|
||
|
|
return min <= max ? { min, max } : { min: max, max: min };
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 九霄神雷(functiontype=20)被动配置 */
|
||
|
|
export type BattleBuffBaseThunderConfig = {
|
||
|
|
/** 单局最多触发次数 */
|
||
|
|
maxTriggers: number;
|
||
|
|
/** 小怪伤害比例万分比 */
|
||
|
|
smallDmgWan: number;
|
||
|
|
/** Boss 伤害比例万分比 */
|
||
|
|
bossDmgWan: number;
|
||
|
|
/** 触发距离(距终点格数) */
|
||
|
|
triggerCells: number;
|
||
|
|
/** 生效距离(从锚点格沿进路向出怪口后退格数,含锚点格) */
|
||
|
|
effectCells: number;
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 解析九霄神雷 functionpar:单局次数,小怪伤害, Boss伤害,触发距离,生效距离。
|
||
|
|
* @returns 配置;参数无效时 null
|
||
|
|
*/
|
||
|
|
export function parseBattleBuffBaseThunderConfig(raw: string | number | null | undefined): BattleBuffBaseThunderConfig | null {
|
||
|
|
const nums = parseFunctionParNums(raw);
|
||
|
|
if (nums.length < 5) {
|
||
|
|
console.log('[BattleBuff] 九霄神雷 functionpar 需 5 段');
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
const maxTriggers = Math.floor(nums[0]);
|
||
|
|
const smallDmgWan = Math.max(0, Math.min(WAN_DENOM, Math.floor(nums[1])));
|
||
|
|
const bossDmgWan = Math.max(0, Math.min(WAN_DENOM, Math.floor(nums[2])));
|
||
|
|
const triggerCells = Math.floor(nums[3]);
|
||
|
|
const effectCells = Math.floor(nums[4]);
|
||
|
|
if (maxTriggers <= 0 || smallDmgWan <= 0 || bossDmgWan <= 0 || triggerCells <= 0 || effectCells <= 0) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
return { maxTriggers, smallDmgWan, bossDmgWan, triggerCells, effectCells };
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 武财神(functiontype=25)击杀额外蟠桃配置 */
|
||
|
|
export type BattleBuffKillPeachConfig = {
|
||
|
|
/** 触发概率万分比 */
|
||
|
|
probWan: number;
|
||
|
|
/** 成功时额外蟠桃数量 */
|
||
|
|
count: number;
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 解析击杀额外蟠桃 functionpar:概率万分比,数量。
|
||
|
|
* @returns 配置;参数无效时 null
|
||
|
|
*/
|
||
|
|
export function parseBattleBuffKillPeachConfig(raw: string | number | null | undefined): BattleBuffKillPeachConfig | null {
|
||
|
|
const nums = parseFunctionParNums(raw);
|
||
|
|
if (nums.length < 2) {
|
||
|
|
console.log('[BattleBuff] 击杀额外蟠桃 functionpar 需 2 段');
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
const probWan = Math.max(0, Math.min(WAN_DENOM, Math.floor(nums[0])));
|
||
|
|
const count = Math.floor(nums[1]);
|
||
|
|
if (probWan <= 0 || count <= 0) return null;
|
||
|
|
return { probWan, count };
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 解析被动「召神令」number 档偏移(functiontype=8,functionpar 首段)。
|
||
|
|
* @returns 非负整数;无效时 0
|
||
|
|
*/
|
||
|
|
export function parseBattleBuffHeroSummonRateOffset(raw: string | number | null | undefined): number {
|
||
|
|
const nums = parseFunctionParNums(raw);
|
||
|
|
if (nums.length < 1) return 0;
|
||
|
|
const offset = Math.floor(nums[0]);
|
||
|
|
return offset > 0 ? offset : 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
export function rollBattleBuffRandomLevelDelta(
|
||
|
|
currentLevel: number,
|
||
|
|
maxLevel: number,
|
||
|
|
levelUpProbsWan: readonly number[],
|
||
|
|
rollWan: number,
|
||
|
|
): number {
|
||
|
|
const level = Math.max(1, Math.floor(currentLevel) || 1);
|
||
|
|
const max = Math.max(1, Math.floor(maxLevel) || 1);
|
||
|
|
const roll = Math.max(0, Math.min(WAN_DENOM - 1, Math.floor(rollWan) || 0));
|
||
|
|
if (level < max) {
|
||
|
|
const prob = levelUpProbsWan[level - 1] ?? 0;
|
||
|
|
if (roll < prob) return 1;
|
||
|
|
}
|
||
|
|
if (level > 1) return -1;
|
||
|
|
return 0;
|
||
|
|
}
|