feat: 重构卡牌描述渲染系统,实现动态富文本展示

1. 新增SkillDescSet配置商店技能与药水模板,补充6201-6206范围攻击与6502-6511计时强化技能配置
2. 替换原有单/多文本标签为富文本组件,统一使用buildCardDescRich生成描述
3. 简化物品商店与卡牌组件的描述渲染逻辑,移除冗余的getDescription方法
4. 为药水卡片移除硬编码描述,改为从技能模板动态生成,自动高亮数值与持续时间
5. 实现附加概率后缀自动生成,支持暴击/冰冻/击晕/击退概率动态展示
This commit is contained in:
pan
2026-07-31 16:02:47 +08:00
parent f058d02ad9
commit a06c2c0e31
7 changed files with 1480 additions and 1104 deletions

View File

@@ -33,6 +33,7 @@ import { FieldSkillSet, mergeSkillParams, SkillConfig, SkillKind, SkillOverrides
import { FieldDescSet, FieldValueFmt, SkillDescSet } from "./SkillDescSet";
import { BuffList } from "./BuffSet";
import { Attrs } from "./HeroAttrs";
import { CardConfig } from "./CardSet";
/**
* 生成器入参窄接口:只约束实际读取的字段。
@@ -91,6 +92,9 @@ const RICH_NAME_COLOR = "#2E86C1";
/** 技能名「」匹配模式(仅本模块行首/行中技能名片段使用) */
const SKILL_NAME_PATTERN = /「([^」]*)」/g;
/** "持续X秒"匹配模式(卡片描述后处理:把持续时间数值也高亮) */
const DURATION_PATTERN = /持续(\d+)秒/g;
/** 解析目标描述文本(与战斗 SCastSystem 目标选取语义严格一致) */
function buildTargetDesc(skill: SkillConfig): string {
switch (skill.TGroup) {
@@ -374,3 +378,92 @@ export function buildEquipRich(fieldUuids: number[]): string {
}
return lines.length > 0 ? lines.join("\n") : "无词条";
}
/**
* 把"持续X秒"中的 X 也包裹绿色高亮(卡片描述后处理)。
* Why: 计时模板 {dur} 不在 \x01\x02 高亮标记内renderLineRich 仅高亮主数值;
* 持续时间对药水/计时 buff 同样关键,这里补充高亮,保证视觉一致。
*/
function highlightDuration(text: string): string {
return text.replace(DURATION_PATTERN, `持续<color=${RICH_VALUE_COLOR}>$1</color>秒`);
}
/**
* 结构化行 → 富文本串(技能名蓝色、数值绿色)。
* buildEquipRich / buildCardDescRich 共用,保证渲染规则唯一。
*/
function renderLineRich(line: ISkillLine): string {
const prefix = line.prefix.replace(SKILL_NAME_PATTERN, `「<color=${RICH_NAME_COLOR}>$1</color>」`);
return line.value
? `${prefix}<color=${RICH_VALUE_COLOR}>${line.value}</color>${line.suffix}`
: `${prefix}${line.suffix}`;
}
/**
* 扫描合并后技能的附加概率字段crt/frz/stun/bck非零的拼为高亮后缀。
* Why: 模板不再硬编码"有概率击晕"等文案,由实际配置驱动,避免描述与数值漂移。
* @returns 形如 ",30%击晕,20%冰冻";全为 0 时返回 ""
*/
function buildChanceSuffix(skill: SkillConfig): string {
const items: string[] = [];
const push = (val: number | undefined, label: string) => {
if (val && val > 0) items.push(`附加<color=${RICH_VALUE_COLOR}>${val}</color>%${label}`);
};
push(skill.crt, "暴击");
push(skill.frz, "冰冻");
push(skill.stun, "击晕");
push(skill.bck, "击退");
return items.length > 0 ? `,${items.join(",")}` : "";
}
/**
* 商店卡片描述富文本SCardComp / ItemListComp 用BBCode
*
* Why: 卡片 info 为手写文案,与 SkillSet/BuffList 数值易漂移。
* 本函数从 CardConfig.skill + overrides 合并后的实时配置生成描述,
* 数值随档位(如高级药水 buff_value自动同步数值绿色高亮、技能名蓝色。
*
* 描述规则:
* - 药品/技能卡card.skill 有效)→ 查 SkillDescSet 模板渲染(计时模式取 BuffList 默认值兜底);
* 未入表技能回退 SkillSet.info / card.info整行不高亮。
* - 装备卡card.field 有效)→ 复用 buildEquipRich 逐词条渲染。
* - 触发节奏t_times/t_inv融入单行描述定时触发"每M秒释放一次「技能名」xxx共N次";即时触发仅显示效果。
*
* @param card 卡片配置SCardSet 技能卡 / ICardSet 药品卡 / EquipSet 装备卡)
* @returns 多行富文本串;无可描述内容时回退 card.info
*/
export function buildCardDescRich(card: CardConfig): string {
// 装备卡:逐词条渲染驻场光环
if (card.field && card.field.length > 0) {
return buildEquipRich(card.field);
}
// 药品/技能卡:合并覆写后按 SkillDescSet 模板渲染
const base = card.skill ? SkillSet[card.skill] : undefined;
if (base) {
const skill = mergeSkillParams(base, card.overrides);
const line = buildEffectLine("", skill);
// 未入表技能回退链SkillSet.info → card.info
if (!line.value && !SkillDescSet[skill.uuid]) {
return String(skill.info || card.info || "");
}
// 触发节奏融入描述:定时触发"每M秒释放一次「技能名」xxx共N次";即时触发直接描述
const tTimes = card.t_times ?? 1;
const tInv = card.t_inv ?? 0;
const isInstant = card.is_inst || tInv <= 0;
// 附加概率后缀(暴击/冰冻/击晕/击退),由实际配置驱动
const suffix = buildChanceSuffix(skill);
const rich = highlightDuration(renderLineRich(line)) + suffix;
if (isInstant) {
return rich;
}
const rhythm = `每<color=${RICH_VALUE_COLOR}>${tInv}</color>秒释放一次「<color=${RICH_NAME_COLOR}>${skill.name}</color>」`;
const times = `,共<color=${RICH_VALUE_COLOR}>${tTimes}</color>次`;
return `${rhythm}${rich}${times}`;
}
return card.info || "";
}