feat(hero): 优化英雄详情页技能展示逻辑

1. 新增带等级和升级预览的技能描述渲染函数
2. 为技能档位添加专属命名,支持按触发语境显示技能名
3. 给所有技能档位补充技能等级标注,优化面板展示
4. 新增200个游戏技能名称配置文件
This commit is contained in:
panFD
2026-08-08 23:27:47 +08:00
parent 6cfe734724
commit ac14b8563c
4 changed files with 522 additions and 102 deletions

View File

@@ -28,7 +28,7 @@
* - SkillDescSet:结构化描述模板
* - FieldSkillSet驻场技能、BuffList计时 buff 默认值兜底、Attrs属性枚举
*/
import { SkillTriggerName, SkillTriggerType, TriggerGrouped, LvTriggerEntry, LvFieldEntry, LvReviveEntry, LvBonusEntry, resolveTriggerByLv, resolveFieldByLv, resolveReviveByLv } from "./heroSet";
import { SkillTriggerName, SkillTriggerType, TriggerGrouped, LvTriggerEntry, LvFieldEntry, LvReviveEntry, LvBonusEntry, resolveTriggerByLv, resolveFieldByLv, resolveReviveByLv, getTriggerSkillName } from "./heroSet";
import { FieldSkillSet, mergeSkillParams, SkillConfig, SkillKind, SkillOverrides, SkillSet, TGroup } from "./SkillSet";
import { FieldDescSet, FieldValueFmt, SkillDescSet } from "./SkillDescSet";
import { BuffList } from "./BuffSet";
@@ -347,7 +347,8 @@ function buildSkillLines(source: ISkillDescSource, showName: boolean = true, sho
if (!base) continue;
const skill = mergeSkillParams(base, item.overrides);
const trigger = needCount ? `${name}×${item.t_num}` : name;
const nameSeg = showName ? `${base.name}` : "";
// 触发语境专属名称(如 atked.6301=不屈壁垒),未配置回退通用名
const nameSeg = showName ? `${getTriggerSkillName(key, item.s_uuid)}` : "";
lines.push(buildEffectLine(`${trigger}:${nameSeg}`, skill));
}
continue;
@@ -360,7 +361,7 @@ function buildSkillLines(source: ISkillDescSource, showName: boolean = true, sho
const base = SkillSet[Number(sKey)];
if (!base) continue;
const sorted = [...entries].sort((a, b) => a.lv - b.lv);
const nameSeg = showName ? `${base.name}` : "";
const nameSeg = showName ? `${getTriggerSkillName(key, Number(sKey))}` : "";
const headOf = (e: LvTriggerEntry) => `Lv${e.lv}: ${needCount ? `${name}×${e.t_num}` : name} ${nameSeg}`;
// 取当前等级生效档≤lv 的最大档)
@@ -504,6 +505,92 @@ export function buildEquipRich(fieldUuids: number[], showName: boolean = false):
return lines.length > 0 ? lines.join("\n") : "无词条";
}
/**
* 英雄信息弹窗技能描述富文本HeroMoreCom 用)。
*
* 格式:
* 「技能名」 Lv.当前技能等级:当前档技能效果
* 升级提示Lv.当前 → Lv.下一级:下一级技能效果(金色,仅有更高档时出现)
*
* 数据源为运行时 HeroAttrsComplv 取当前英雄等级),逐触发类型逐技能渲染。
* 技能等级取 LvTriggerEntry.s_lv未标注时按档位序号1/2/3...)兜底。
*
* @param source 英雄运行时数据源(含 lv 与触发技能分组)
* @returns 多行富文本串,\n 分隔
*/
export function buildSkillDescMoreRich(source: ISkillDescSource): string {
const lv = Math.max(1, source.lv ?? 1);
const lines: string[] = [];
// 单条效果 → 富文本(技能名蓝色、数值绿色高亮、持续秒数高亮)
const effectRich = (line: ISkillLine): string => {
const prefix = line.prefix.replace(SKILL_NAME_PATTERN, `「<color=${RICH_NAME_COLOR}>$1</color>」`);
const text = line.value
? `${prefix}<color=${RICH_VALUE_COLOR}>${line.value}</color>${line.suffix}`
: `${prefix}${line.suffix}`;
return highlightDuration(text);
};
for (const key of TRIGGER_KEYS) {
const grouped = source[key] as TriggerGrouped | undefined;
if (!grouped) continue;
const trigName = SkillTriggerName[key] ?? key;
const needCount = key === SkillTriggerType.Atking || key === SkillTriggerType.Atked;
for (const sKey in grouped) {
const entries = grouped[sKey];
if (!entries?.length) continue;
const sUuid = Number(sKey);
const base = SkillSet[sUuid];
if (!base) continue;
const sorted = [...entries].sort((a, b) => a.lv - b.lv);
// 技能名走触发语境专属命名(如 atked.6301=不屈壁垒)
const skillName = getTriggerSkillName(key, sUuid);
// 当前生效档≤lv 最大档),未激活则用最低档
let activeIdx = -1;
for (let i = 0; i < sorted.length; i++) {
if (sorted[i].lv <= lv) activeIdx = i;
}
const curIdx = activeIdx >= 0 ? activeIdx : 0;
const cur = sorted[curIdx];
const curSLv = cur.s_lv ?? (curIdx + 1);
const curTrig = needCount ? `${trigName}×${cur.t_num}` : trigName;
const curHead = `${skillName}」 Lv.${curSLv}: ${curTrig}`;
lines.push(effectRich(buildEffectLine(curHead, mergeSkillParams(base, cur.overrides))));
// 升级提示:存在更高档时,金色预览下一档
if (curIdx + 1 < sorted.length) {
const next = sorted[curIdx + 1];
const nextSLv = next.s_lv ?? (curIdx + 2);
const nextTrig = needCount ? `${trigName}×${next.t_num}` : trigName;
const nextLine = buildEffectLine(`${nextTrig}`, mergeSkillParams(base, next.overrides));
const nextText = `${nextLine.prefix}${nextLine.value}${nextLine.suffix}`;
lines.push(`<color=${RICH_UPGRADE_COLOR}>升级 Lv.${curSLv}→Lv.${nextSLv}: ${nextText}</color>`);
}
}
}
// 驻场光环field
const fieldEntries = source[SkillTriggerType.Field] as LvFieldEntry[] | undefined;
if (fieldEntries?.length) {
const tpl = SkillTriggerName[SkillTriggerType.Field] ?? "光环";
for (const uuid of resolveFieldByLv(fieldEntries, lv)) {
const line = buildFieldLine(`${tpl}: `, uuid, true);
if (line) lines.push(effectRich(line));
}
}
// 复活revive
const revive = resolveReviveByLv(source[SkillTriggerType.Revive] as LvReviveEntry[] | undefined, lv);
if (revive) {
const reviveName = getTriggerSkillName(SkillTriggerType.Revive, revive.s_uuid);
lines.push(`「<color=${RICH_NAME_COLOR}>${reviveName}</color>」: 死亡后以<color=${RICH_VALUE_COLOR}>${Math.round(revive.upr * 100)}%</color>血量复活<color=${RICH_VALUE_COLOR}>${revive.r_num}</color>次`);
}
return lines.join("\n");
}
/**
* 把"持续X秒"中的 X 也包裹绿色高亮(卡片描述后处理)。
* Why: 计时模板 {dur} 不在 \x01\x02 高亮标记内renderLineRich 仅高亮主数值;
@@ -513,6 +600,9 @@ function highlightDuration(text: string): string {
return text.replace(DURATION_PATTERN, `持续<color=${RICH_VALUE_COLOR}>$1</color>秒`);
}
/** 富文本升级提示色(金色,标识"升级后"的下一档预览,与未激活灰区分) */
const RICH_UPGRADE_COLOR = "#E8A33D";
/**
* 结构化行 → 富文本串(技能名蓝色、数值绿色)。
* buildEquipRich / buildCardDescRich / buildSkillDescRich 共用,保证渲染规则唯一。