feat(skill): 优化技能描述展示逻辑,修复非伤害技能档位显示问题

1. 新增构建触发语境标签函数,统一处理循环触发技能的次数展示
2. 新增技能档位显式配置描述字段,支持直接渲染自定义描述模板
3. 重构技能展示逻辑,优先使用显式配置的描述,避免自动diff语义错位
4. 更新英雄技能配置,补全部分档位的描述文本,修正技能名称和描述文案
5. 完善类型注释和文档说明,规范技能描述配置规则
This commit is contained in:
pan
2026-09-04 14:50:51 +08:00
parent 7bb3dcf6db
commit 16d44e3f5e
3 changed files with 94 additions and 57 deletions

View File

@@ -74,6 +74,20 @@ const TRIGGER_KEYS: SkillTriggerType[] = [
/** 占位符模式:{target}/{dur}/{buff_value}/{技能字段名} */
const PLACEHOLDER_PATTERN = /\{(\w+)\}/g;
/**
* 构建触发语境短语(整组/单技能行首共用)。
* atking/atked 为周期循环触发(运行时按次数取模判定),拼为"每攻击N次"/"每受击N次"
* 表达循环语义(N=1 时退化为"每次攻击"/"每次受击");其余类型直接用触发名(召唤/死亡后/回合开始)。
*/
function buildTriggerLabel(type: SkillTriggerType, tNum: number): string {
if (type === SkillTriggerType.Atking || type === SkillTriggerType.Atked) {
return tNum <= 1
? `每次${SkillTriggerName[type]}`
: `每${SkillTriggerName[type]}${valueRich(tNum)}次`;
}
return SkillTriggerName[type];
}
/**
* 解析目标描述文本(与战斗 SCastSystem 目标选取语义严格一致)
*/
@@ -209,10 +223,8 @@ export function buildHeroSkillDesc(heroUuid: number, showName: boolean = true, s
}
tmpl = tmpl ?? entries[0]?.desc;
const text = tmpl ? renderDesc(tmpl, skill) : fallbackDesc(skill);
// 进击/受击带触发次数,拼为"攻击3次";其余用触发类型简称
const trig = type === SkillTriggerType.Atking || type === SkillTriggerType.Atked
? `${SkillTriggerName[type]}${valueRich(item.t_num)}次`
: SkillTriggerName[type];
// 触发语境:"每攻击N次"循环语义(buildTriggerLabel),逗号衔接效果文本
const trig = buildTriggerLabel(type, item.t_num);
parts.push(`${trig}, ${text}`);
}
// dead 组内的复活效果(revive_hp / revive_count)并入同一行
@@ -261,6 +273,8 @@ interface ISkillTierInfo {
skill: SkillConfig;
/** 该档描述模板(继承链解析后) */
desc?: string;
/** 该档显式配置的描述模板(未走继承链):配置后提升档直接展示该模板,优先于自动 diff */
ownDesc?: string;
/** 触发技能档位的触发次数(普攻无) */
tNum: number;
}
@@ -341,6 +355,7 @@ function collectSkillTiers(hSkill: HSkillInfo): ISkillTierInfo[] {
heroLv: lv1?.lv ?? 1,
skill: mergeSkillParams(base, hSkill.overrides),
desc: lv1?.desc ?? hSkill.desc,
ownDesc: lv1?.desc ?? hSkill.desc,
tNum: 0,
});
// slv2+ = lv_entries 各档(与基础 overrides 浅合并,档位字段优先)
@@ -351,6 +366,7 @@ function collectSkillTiers(hSkill: HSkillInfo): ISkillTierInfo[] {
heroLv: e.lv,
skill: mergeSkillParams(mergeSkillParams(base, hSkill.overrides), e.overrides),
desc: e.desc,
ownDesc: e.desc,
tNum: 0,
});
}
@@ -477,16 +493,15 @@ function collectTriggerTiers(hero: heroInfo, type: SkillTriggerType, skillUuid:
heroLv: e.lv,
skill: mergeSkillParams(base, e.overrides),
desc: e.desc ?? inherit,
ownDesc: e.desc,
tNum: e.t_num,
};
});
const label = (type === SkillTriggerType.Atking || type === SkillTriggerType.Atked)
? `${SkillTriggerName[type]}${valueRich(sorted[0].t_num)}次`
: SkillTriggerName[type];
const label = buildTriggerLabel(type, sorted[0].t_num);
return { tiers, label };
}
/** 档位 → 展示行:slv1 完整能力 + 数值;slv2+ 相对上一级的提升 */
/** 档位 → 展示行:slv1 完整能力 + 数值;slv2+ 显式配置 desc 时直接渲染该模板,否则相对上一级的提升 */
function buildTierLines(tiers: ISkillTierInfo[], triggerLabel: string): HeroSkillTierLine[] {
const lines: HeroSkillTierLine[] = [];
for (let i = 0; i < tiers.length; i++) {
@@ -495,8 +510,11 @@ function buildTierLines(tiers: ISkillTierInfo[], triggerLabel: string): HeroSkil
// 技能 1 级:完整能力 + 数值
const text = t.desc ? renderDesc(t.desc, t.skill) : fallbackDesc(t.skill);
lines.push({ sLv: t.sLv, heroLv: t.heroLv, text: triggerLabel ? `${triggerLabel}, ${text}` : text });
} else if (t.ownDesc) {
// 提升档:该档显式配置了模板(如护盾/治疗等 diff 标签语义不达的技能),策划表述优先
lines.push({ sLv: t.sLv, heroLv: t.heroLv, text: renderDesc(t.ownDesc, t.skill) });
} else {
// 技能 2 级起:相对上一级的提升
// 提升档:未配置模板,给相对上一级的提升
const diff = buildTierDiff(tiers[i - 1], t);
lines.push({ sLv: t.sLv, heroLv: t.heroLv, text: diff.length > 0 ? diff.join(",") : "无变化" });
}