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.
90 lines
3.4 KiB
90 lines
3.4 KiB
|
3 weeks ago
|
/** 词条字符串解析(无业务依赖,供 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<number, Map<number, number>>();
|
||
|
|
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;
|
||
|
|
}
|