/** 词条字符串解析(无业务依赖,供 Loadout / Applier 共用,避免循环引用) */ /** 解析词条.base_attr:「类型,值」或「类型,值;类型,值」 */ export function parseEntryBaseAttr(raw: string): Array<{ type: number; value: number }> { if (!raw) return []; const out: Array<{ type: number; value: number }> = []; for (const seg of raw.split(';')) { const part = seg.trim(); if (!part) continue; const [typeStr, valueStr] = part.split(','); const type = Number(typeStr); const value = Number(valueStr); if (!Number.isFinite(type) || !Number.isFinite(value)) continue; out.push({ type, value }); } return out; } /** 解析 base_para:「代号,值[,值...]」多段用 ; */ export function parseEntryBasePara(raw: string): Array<{ code: number; values: number[] }> { if (!raw) return []; const out: Array<{ code: number; values: number[] }> = []; for (const seg of raw.split(';')) { const part = seg.trim(); if (!part) continue; const nums = part.split(',').map(Number); if (!nums.length || !Number.isFinite(nums[0])) continue; out.push({ code: nums[0], values: nums.slice(1) }); } return out; } /** * 解析 Skill.skill_change:修改 base_atk.skill 逗号分隔参数(1-based 下标)。 * * 格式:`第N个参数,增加类型,增加数值`;多段用 `;` 分隔。 * 只修改已有下标上的数值,不追加新参数。 * * 增加类型(op,配表注释): * - 1 增加数值:cur = cur + value * - 2 万分比增加:cur = cur × (1 + value/10000) * - 3 减少数值:cur = cur - value * - 4 万分比减少:cur = cur / (1 + value/10000) * * 同参数位、同 op 的多条修改先汇总 value,再按 1→2→3→4 顺序各算一次(每次基于上一步的 cur,非替换回基线)。 */ export function parseSkillChange(raw: string): Array<{ index: number; op: number; value: number }> { if (!raw) return []; const out: Array<{ index: number; op: number; value: number }> = []; for (const seg of raw.split(';')) { const part = seg.trim(); if (!part) continue; const [i, op, v] = part.split(',').map(Number); if (!Number.isFinite(i) || !Number.isFinite(op) || !Number.isFinite(v)) continue; out.push({ index: i, op, value: v }); } return out; } /** 在 base_atk.skill 基线 parts 上应用 skill_change(不扩展数组长度) */ export function applySkillChangesToParts( baseParts: readonly number[], changes: ReadonlyArray<{ index: number; op: number; value: number }>, ): number[] { const parts = [...baseParts]; if (!changes.length) return parts; const byIndex = new Map>(); for (const ch of changes) { let ops = byIndex.get(ch.index); if (!ops) { ops = new Map(); byIndex.set(ch.index, ops); } ops.set(ch.op, (ops.get(ch.op) ?? 0) + ch.value); } for (const [index, ops] of byIndex) { const i = index - 1; if (i < 0 || i >= parts.length) continue; let cur = baseParts[i]; if (ops.has(1)) cur += ops.get(1)!; if (ops.has(2)) cur *= 1 + ops.get(2)! / 10000; if (ops.has(3)) cur -= ops.get(3)!; if (ops.has(4)) cur /= 1 + ops.get(4)! / 10000; parts[i] = cur; } return parts; }