/** * 解析 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 表字段 */ 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; } /** 万分比满值 */ const WAN_DENOM = 10000; /** * 解析体力 buff 增量(functiontype=15)。 * @returns 正整数增量;无效时 0 */ export function parseBattleBuffVigourDelta(raw: string | number | null | undefined): number { const n = parseSignedIntFromFunctionPar(raw); if (n == null || n <= 0) return 0; return n; } /** 灾厄盒:Boss 出现几率加成万分比 */ export function parseBattleBuffBossSpawnBonusWan(raw: string | number | null | undefined): number { const nums = parseFunctionParNums(raw); if (nums.length < 1) { console.log('[BattleBuff] 灾厄盒 functionpar 缺少万分比'); return 0; } return Math.max(0, Math.floor(nums[0])); } /** 倍率卡配置 */ export type BattleBuffMultiplierCardConfig = { /** 召唤时产卡概率万分比 */ spawnWan: number; /** +1 级权重 */ plus1Weight: number; /** +2 级权重 */ plus2Weight: number; }; /** 解析倍率卡 functionpar:产卡万分比,+1权重,+2权重 */ export function parseBattleBuffMultiplierCardConfig(raw: string | number | null | undefined): BattleBuffMultiplierCardConfig | null { const nums = parseFunctionParNums(raw); if (nums.length < 3) { console.log('[BattleBuff] 倍率卡 functionpar 需 3 段'); return null; } const spawnWan = Math.max(0, Math.floor(nums[0])); const plus1Weight = Math.max(0, Math.floor(nums[1])); const plus2Weight = Math.max(0, Math.floor(nums[2])); if (spawnWan <= 0 || plus1Weight + plus2Weight <= 0) return null; return { spawnWan, plus1Weight, plus2Weight }; } /** 攻速万分比(单值) */ export function parseBattleBuffAspdWan(raw: string | number | null | undefined): number { const nums = parseFunctionParNums(raw); if (nums.length < 1) { console.log('[BattleBuff] 攻速 functionpar 缺少万分比'); return 0; } return Math.max(0, Math.floor(nums[0])); } /** 鏖战册:我方/对方攻速 */ export type BattleBuffBothAttackSpeedConfig = { /** 我方攻速万分比 */ selfWan: number; /** 对方攻速万分比 */ enemyWan: number; }; /** 解析鏖战册 functionpar:我方,对方 */ export function parseBattleBuffBothAttackSpeedConfig(raw: string | number | null | undefined): BattleBuffBothAttackSpeedConfig | null { const nums = parseFunctionParNums(raw); if (nums.length < 2) { console.log('[BattleBuff] 鏖战册 functionpar 需 2 段'); return null; } const selfWan = Math.max(0, Math.floor(nums[0])); const enemyWan = Math.max(0, Math.floor(nums[1])); if (selfWan <= 0 && enemyWan <= 0) return null; return { selfWan, enemyWan }; } /** 击杀额外金币:概率万分比(成功固定 +1) */ export function parseBattleBuffKillCoinProbWan(raw: string | number | null | undefined): number { const nums = parseFunctionParNums(raw); if (nums.length < 1) { console.log('[BattleBuff] 赏金令 functionpar 缺少万分比'); return 0; } return Math.max(0, Math.min(WAN_DENOM, Math.floor(nums[0]))); } /** 倍增卡 / 属性卡产卡 / 宝石掉落 / 省金 / 滞行:单段万分比 */ export function parseBattleBuffWan(raw: string | number | null | undefined, label: string): number { const nums = parseFunctionParNums(raw); if (nums.length < 1) { console.log(`[BattleBuff] ${label} functionpar 缺少万分比`); return 0; } return Math.max(0, Math.floor(nums[0])); } /** 基地加血整数 */ export function parseBattleBuffBaseHpBonus(raw: string | number | null | undefined): number { const nums = parseFunctionParNums(raw); if (nums.length < 1) { console.log('[BattleBuff] 基地加血 functionpar 缺少数值'); return 0; } return Math.max(0, Math.floor(nums[0])); } /** 双生盾:我方/对方基地血 */ export type BattleBuffBothBaseHpConfig = { /** 我方加血 */ selfHp: number; /** 对方加血 */ enemyHp: number; }; /** 解析双生盾 functionpar:我方,对方 */ export function parseBattleBuffBothBaseHpConfig(raw: string | number | null | undefined): BattleBuffBothBaseHpConfig | null { const nums = parseFunctionParNums(raw); if (nums.length < 2) { console.log('[BattleBuff] 双生盾 functionpar 需 2 段'); return null; } const selfHp = Math.max(0, Math.floor(nums[0])); const enemyHp = Math.max(0, Math.floor(nums[1])); if (selfHp <= 0 && enemyHp <= 0) return null; return { selfHp, enemyHp }; } /** 寻金尺:解锁金币区间 */ export type BattleBuffUnlockCoinConfig = { /** 最小金币(含) */ minCoin: number; /** 最大金币(含) */ maxCoin: number; }; /** 解析寻金尺 functionpar:min,max */ export function parseBattleBuffUnlockCoinConfig(raw: string | number | null | undefined): BattleBuffUnlockCoinConfig | null { const nums = parseFunctionParNums(raw); if (nums.length < 2) { console.log('[BattleBuff] 寻金尺 functionpar 需 2 段'); return null; } const a = Math.max(0, Math.floor(nums[0])); const b = Math.max(0, Math.floor(nums[1])); if (a <= 0 && b <= 0) return null; return { minCoin: Math.min(a, b), maxCoin: Math.max(a, b) }; } /** 御雷珠(同旧九霄神雷 5 段) */ export type BattleBuffBaseThunderConfig = { /** 单局最多触发次数 */ maxTriggers: number; /** 小怪伤害比例万分比 */ smallDmgWan: number; /** Boss 伤害比例万分比 */ bossDmgWan: number; /** 触发距离(距终点格数) */ triggerCells: number; /** 生效距离(从锚点格沿进路向出怪口后退格数,含锚点格) */ effectCells: number; }; /** 解析御雷珠 functionpar:次数,小怪伤,Boss伤,触发格,生效格 */ 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 }; } /** 金矿间隔秒 */ export function parseBattleBuffGoldMineIntervalSec(raw: string | number | null | undefined): number { const nums = parseFunctionParNums(raw); if (nums.length < 1) { console.log('[BattleBuff] 金矿 functionpar 缺少秒数'); return 0; } return Math.max(0, Math.floor(nums[0])); } /** targetpar 类型1:等级区间 */ export interface BattleBuffTargetLevelRange { /** 最小等级(含) */ minLv: number; /** 最大等级(含) */ maxLv: number; } /** targetpar 解析结果 */ export type BattleBuffTargetPar = | { kind: 1; minLv: number; maxLv: number } | { kind: 2; topN: number }; /** functiontype1=107/110:技能 id 列表 + 倍率 */ export interface BattleBuffSkillListMul { /** Weapon/Skill id 列表 */ skillIds: number[]; /** 伤害倍数(如 3) */ mul: number; } /** * 解析 `id|id|…,倍数`。 * @param raw functionpar1 */ export function parseBattleBuffSkillListMul(raw: string | number | null | undefined): BattleBuffSkillListMul | null { const text = String(raw ?? '').trim(); if (!text) return null; const comma = text.lastIndexOf(','); if (comma < 0) { console.warn('[BattleBuff] functionpar1 缺倍数', raw); return null; } const mul = Math.floor(Number(text.slice(comma + 1).trim()) || 0); if (!(mul > 0)) { console.warn('[BattleBuff] functionpar1 倍数非法', raw); return null; } const idPart = text.slice(0, comma).trim(); if (!idPart) { console.warn('[BattleBuff] functionpar1 缺技能 id', raw); return null; } const segs = idPart.split('|'); const skillIds: number[] = []; for (let i = 0; i < segs.length; i++) { const id = Math.floor(Number(segs[i].trim()) || 0); if (!(id > 0)) { console.warn('[BattleBuff] functionpar1 含非法 id', raw, segs[i]); continue; } skillIds.push(id); } if (skillIds.length <= 0) return null; return { skillIds, mul }; } /** * 解析 Battlebuff.targetpar。 * 格式:类型,参数…;类型1=等级区间(min,max);类型2=场上最大 N 个。 */ export class BattleBuffTargetParse { /** * 解析 targetpar。 * @param raw targetpar */ static parse(raw: string | null | undefined): BattleBuffTargetPar | null { const text = String(raw ?? '').trim(); if (!text) return null; const nums = parseFunctionParNums(text); if (nums.length < 1) return null; const kind = Math.floor(nums[0]); if (kind === 1) { if (nums.length < 3) { console.warn('[BattleBuff] targetpar 类型1缺少等级区间', raw); return null; } const minLv = Math.floor(nums[1]); const maxLv = Math.floor(nums[2]); if (minLv <= 0 || maxLv < minLv) { console.warn('[BattleBuff] targetpar 等级区间非法', raw); return null; } return { kind: 1, minLv, maxLv }; } if (kind === 2) { if (nums.length < 2) { console.warn('[BattleBuff] targetpar 类型2缺少数量', raw); return null; } const topN = Math.floor(nums[1]); if (!(topN > 0)) { console.warn('[BattleBuff] targetpar 类型2数量非法', raw); return null; } return { kind: 2, topN }; } console.warn('[BattleBuff] targetpar 未知类型', raw); return null; } /** * 解析等级区间;非类型1 时返回 null。 * @param raw targetpar */ static parseLevelRange(raw: string | null | undefined): BattleBuffTargetLevelRange | null { const p = this.parse(raw); if (!p || p.kind !== 1) return null; return { minLv: p.minLv, maxLv: p.maxLv }; } /** * 数字是否命中 target + targetpar。 * @param target 0/空=不过滤;1~5 五行;99 全五行 * @param targetpar 生效参数 * @param heroType 单位 herotype(1~5) * @param level 单位等级 * @param rankAmongElement 同五行场上等级排名(1=最高);类型2 必填 */ static matches(target: number, targetpar: string | null | undefined, heroType: number, level: number, rankAmongElement = 0): boolean { const t = Math.floor(Number(target) || 0); const ht = Math.floor(Number(heroType) || 0); const lv = Math.floor(Number(level) || 0); if (t === 0) return true; if (t === 99) { if (ht < 1 || ht > 5) return false; } else if (t >= 1 && t <= 5) { if (ht !== t) return false; } else { console.warn('[BattleBuff] target 非法', t); return false; } const text = String(targetpar ?? '').trim(); if (!text) return true; const par = this.parse(text); if (!par) return false; if (par.kind === 1) return lv >= par.minLv && lv <= par.maxLv; if (!(rankAmongElement > 0)) { console.warn('[BattleBuff] targetpar 类型2缺排名', targetpar); return false; } return rankAmongElement <= par.topN; } }