/** * 解析 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=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; }