refactor(hero): 重构技能触发与属性系统,使用运行时解析缓存

1. 重构英雄属性组件,将静态配置与运行时缓存分离,新增runtime_前缀的运行时解析字段
2. 移除旧的evolve进化配置处理逻辑,改为通过等级解析器动态获取当前等级生效的技能与属性
3. 统一怪物与英雄的技能触发逻辑,均使用运行时缓存字段
4. 更新测试模式配置结构,适配新的分组+等级数组配置格式
5. 优化技能描述生成逻辑,支持新的等级配置结构
This commit is contained in:
pan
2026-08-04 16:46:59 +08:00
parent d0971ff72c
commit 57c7934571
10 changed files with 561 additions and 489 deletions

View File

@@ -23,12 +23,12 @@
* 字段结构一致天然都满足。HeroBoxComp 传 model 显示当前等级;卡池/商店传 heroInfo 显示 1 级。
*
* 依赖:
* - heroSet :SkillTriggerName、SkillTriggerType、HeroEvolve
* - heroSet :SkillTriggerName、SkillTriggerType、TriggerGrouped/LvFieldEntry/LvReviveEntry/LvBonusEntry 及 resolver
* - SkillSet :SkillSet、mergeSkillParams、SkillOverrides、SkillKind、TGroup
* - SkillDescSet:结构化描述模板
* - FieldSkillSet驻场技能、BuffList计时 buff 默认值兜底、Attrs属性枚举
*/
import { HeroEvolve, SkillTriggerName, SkillTriggerType } from "./heroSet";
import { SkillTriggerName, SkillTriggerType, TriggerGrouped, LvFieldEntry, LvReviveEntry, LvBonusEntry, resolveTriggerByLv, resolveFieldByLv, resolveReviveByLv } from "./heroSet";
import { FieldSkillSet, mergeSkillParams, SkillConfig, SkillKind, SkillOverrides, SkillSet, TGroup } from "./SkillSet";
import { FieldDescSet, FieldValueFmt, SkillDescSet } from "./SkillDescSet";
import { BuffList } from "./BuffSet";
@@ -37,21 +37,22 @@ import { CardConfig } from "./CardSet";
/**
* 生成器入参窄接口:只约束实际读取的字段。
* heroInfo 与 HeroAttrsComp 的对应字段结构完全一致,天然都满足此接口。
* heroInfo 与 HeroAttrsComp 的对应字段结构完全一致(均为分组 + lv 数组),天然都满足此接口。
*/
export interface ISkillDescSource {
/** 当前等级(用于计算 revive 实际复活次数、evolve 累计加成;静态 heroInfo 恒为 1 */
/** 当前等级(用于 resolve 触发技能/光环/复活取档;静态 heroInfo 恒为 1 */
lv?: number;
call?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[];
dead?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[];
fstart?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[];
fend?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[];
atking?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[];
atked?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[];
field?: number[];
revive?: { s_uuid: number; r_num: number; upr: number };
/** 等级进化配置heroInfo 自带HeroAttrsComp 记录快照),用于累计 ap/hp 额外加成文案 */
evolve?: Record<number, HeroEvolve>;
call?: TriggerGrouped;
dead?: TriggerGrouped;
fstart?: TriggerGrouped;
fend?: TriggerGrouped;
atking?: TriggerGrouped;
atked?: TriggerGrouped;
field?: LvFieldEntry[];
revive?: LvReviveEntry[];
/** 额外攻击/生命加成(按 lv 累加,用于成长文案 */
ap_bonus?: LvBonusEntry[];
hp_bonus?: LvBonusEntry[];
}
/**
@@ -230,11 +231,11 @@ function buildFieldLine(head: string, uuid: number, showName: boolean = true): I
* 复活次数 = r_num + floor((lv - 1) * upr);回血百分比 = SkillSet[s_uuid].ap
*/
function buildReviveLine(source: ISkillDescSource, showName: boolean = true): ISkillLine | null {
const revive = source.revive;
const lv = Math.max(1, source.lv ?? 1);
const revive = resolveReviveByLv(source.revive, lv);
if (!revive) return null;
const base = SkillSet[revive.s_uuid];
if (!base) return null;
const lv = Math.max(1, source.lv ?? 1);
const maxCount = revive.r_num + Math.floor((lv - 1) * revive.upr);
const hpPct = base.ap ?? 0;
const tpl = SkillTriggerName[SkillTriggerType.Revive] ?? "复活";
@@ -247,18 +248,16 @@ function buildReviveLine(source: ISkillDescSource, showName: boolean = true): IS
};
}
/** evolve 额外属性加成ap/hp累计结构化行高亮累计数值 */
/** 额外属性加成ap/hp累计结构化行高亮累计数值 */
function buildEvolveBonusLines(source: ISkillDescSource): ISkillLine[] {
const evolve = source.evolve;
if (!evolve) return [];
const lv = Math.max(1, source.lv ?? 1);
let apTotal = 0;
let hpTotal = 0;
for (let elv = 2; elv <= lv; elv++) {
const evo = evolve[elv];
if (!evo) continue;
apTotal += evo.ap_bonus ?? 0;
hpTotal += evo.hp_bonus ?? 0;
if (source.ap_bonus) {
for (const e of source.ap_bonus) { if (e.lv <= lv) apTotal += e.value; }
}
if (source.hp_bonus) {
for (const e of source.hp_bonus) { if (e.lv <= lv) hpTotal += e.value; }
}
const lines: ISkillLine[] = [];
if (apTotal !== 0) lines.push({ prefix: "成长:额外", value: `+${apTotal}`, suffix: ATTR_NAME[Attrs.ap] ?? "攻击力" });
@@ -267,17 +266,19 @@ function buildEvolveBonusLines(source: ISkillDescSource): ISkillLine[] {
}
/**
* 构建全部能力结构化行(触发技能 + 驻场光环 + 复活 + evolve 加成)。
* 构建全部能力结构化行(触发技能 + 驻场光环 + 复活 + 额外加成)。
* 纯文本与富文本两个公开接口共用此结果,仅渲染阶段不同。
* @param showName 是否在行中包含「技能名」片段(英雄盒面板传 false 隐藏技能名)
*/
function buildSkillLines(source: ISkillDescSource, showName: boolean = true): ISkillLine[] {
const lines: ISkillLine[] = [];
const lv = Math.max(1, source.lv ?? 1);
// ---- 6 种标准触发技能 ----
// ---- 6 种标准触发技能(按当前等级 resolve 后遍历) ----
for (const key of TRIGGER_KEYS) {
const arr = source[key] as { s_uuid: number; t_num: number; overrides?: SkillOverrides }[] | undefined;
if (!arr?.length) continue;
const grouped = source[key] as TriggerGrouped | undefined;
const arr = resolveTriggerByLv(grouped, lv);
if (!arr.length) continue;
// 触发类型简称(召唤/亡语/起手/生息/进击/受击)
const name = SkillTriggerName[key] ?? key;
// 仅进击/受击带触发次数,拼为"×n";其余简称直接用
@@ -293,7 +294,7 @@ function buildSkillLines(source: ISkillDescSource, showName: boolean = true): IS
}
// ---- 驻场光环field查 FieldDescSet 结构化渲染value 数值高亮 ----
const fieldUuids = source[SkillTriggerType.Field] as number[] | undefined;
const fieldUuids = resolveFieldByLv(source[SkillTriggerType.Field] as LvFieldEntry[] | undefined, lv);
if (fieldUuids?.length) {
const tpl = SkillTriggerName[SkillTriggerType.Field] ?? "光环";
for (const uuid of fieldUuids) {