refactor(skill-text): 重构技能文案系统,拆分英雄/卡牌语境
1. 将原HeroSkillDesc拆分为CardSkillText(卡牌/道具通用)和HeroSkillText(英雄专属) 2. 移除废弃的HeroSkillDesc文件与相关引用 3. 统一技能文案生成逻辑,修复数值漂移问题 4. 调整英雄配置中技能解锁等级与分档规则 5. 优化技能弹窗UI与参数传递逻辑
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
* @file HeroSkillText.ts
|
||||
* @description 英雄技能文案生成器(heroInfo 内联描述模板驱动)
|
||||
*
|
||||
* 与 HeroSkillDesc(SkillDescSet 中央模板表)并列的新一代文案层:
|
||||
* 英雄语境描述文案层(原 HeroSkillDesc 已由本模块 + CardSkillText 替代并移除):
|
||||
* - 描述模板直接配置在 heroInfo(HSkillInfo.desc / LvSkillEntry.desc / LvTriggerEntry.desc),
|
||||
* "同一底座技能、不同英雄语境"的表述差异在英雄配置处表达;
|
||||
* - 数值永远从 mergeSkillParams 合并后的实时配置取值(占位符渲染),
|
||||
@@ -21,10 +21,10 @@
|
||||
* {字段名} 合并后技能配置数值(ap/para/frz/stun/crt/bck/hit_count/num/summon_count…)
|
||||
*
|
||||
* 依赖:heroSet(配置与 resolver)、SkillSet/BuffSet/SkillDescSet(底座)、smc(局外等级)。
|
||||
* HeroSkillDesc.ts 暂时保留兼容旧 UI 引用,待迁移完成后移除。
|
||||
* 卡牌/道具语境描述见 config/CardSkillText.ts。
|
||||
*/
|
||||
import {
|
||||
HeroInfo, SkillTriggerType, SkillTriggerName, HSkillInfo, LvTriggerEntry,
|
||||
HeroInfo, heroInfo, SkillTriggerType, SkillTriggerName, HSkillInfo, LvTriggerEntry,
|
||||
TriggerGrouped, resolveSkillOverrides, resolveTriggerByLv, resolveFieldByLv, resolveReviveByLv,
|
||||
getGroupedTriggerName, getFieldSkillName, getReviveSkillName,
|
||||
} from "./config/heroSet";
|
||||
@@ -34,7 +34,7 @@ import { FieldDescSet, FieldValueFmt } from "./config/SkillDescSet";
|
||||
import { FacSet } from "./config/GameSet";
|
||||
import { smc } from "./SingletonModuleComp";
|
||||
|
||||
/* ==================== 富文本片段(与 HeroSkillDesc 同配色,保证新旧面板视觉一致) ==================== */
|
||||
/* ==================== 富文本片段(与 CardSkillText 同配色,保证各面板视觉一致) ==================== */
|
||||
|
||||
/** 富文本数值高亮色(鲜亮绿色) */
|
||||
const RICH_VALUE_COLOR = "#3CE66E";
|
||||
@@ -344,6 +344,84 @@ function collectSkillTiers(hSkill: HSkillInfo): ISkillTierInfo[] {
|
||||
return tiers;
|
||||
}
|
||||
|
||||
/** 驻场光环词条数值展示片段(与 buildHeroSkillDesc 口径一致:比例/整数/减免) */
|
||||
function buildFieldValueRich(fieldUuid: number, value: number): string {
|
||||
const fs = FieldSkillSet[fieldUuid];
|
||||
const desc = fs ? FieldDescSet[fs.type] : undefined;
|
||||
if (!desc) return valueRich(value);
|
||||
const val = desc.fmt === FieldValueFmt.Percent
|
||||
? `+${Math.round(value * 100)}%`
|
||||
: desc.fmt === FieldValueFmt.IntMinus ? `-${value}` : `+${value}`;
|
||||
return valueRich(val);
|
||||
}
|
||||
|
||||
/**
|
||||
* 收集驻场光环词条分档行(hero.field 各英雄等级档,无 s_lv 概念,按档位序号编号)。
|
||||
* slv1 给完整效果(动词 + 数值);slv2+ 给数值提升描述。
|
||||
* 档位配置了 desc 时直接采用(策划自定义表述优先)。
|
||||
*/
|
||||
function collectFieldTierLines(hero: heroInfo, fieldUuid: number): HeroSkillTierLine[] {
|
||||
const matched = (hero.field ?? [])
|
||||
.filter(e => e.uuids.some(u => u.uuid === fieldUuid))
|
||||
.sort((a, b) => a.lv - b.lv);
|
||||
if (matched.length === 0) return [];
|
||||
|
||||
const fs = FieldSkillSet[fieldUuid];
|
||||
const descCfg = fs ? FieldDescSet[fs.type] : undefined;
|
||||
const verb = descCfg?.verb ?? "";
|
||||
const lines: HeroSkillTierLine[] = [];
|
||||
let prevValue: number | undefined;
|
||||
matched.forEach((e, i) => {
|
||||
const entry = e.uuids.find(u => u.uuid === fieldUuid)!;
|
||||
if (e.desc) {
|
||||
// 配置描述优先(策划自定义表述)
|
||||
lines.push({ sLv: i + 1, heroLv: e.lv, text: e.desc });
|
||||
} else if (i === 0 || !descCfg) {
|
||||
// 首档:完整效果(动词 + 数值高亮);未入表类型回退词条 info
|
||||
lines.push({ sLv: i + 1, heroLv: e.lv, text: descCfg ? `${verb}${buildFieldValueRich(fieldUuid, entry.value)}` : (fs?.info ?? "") });
|
||||
} else {
|
||||
// 提升档:数值由旧值提升至新值
|
||||
lines.push({
|
||||
sLv: i + 1, heroLv: e.lv,
|
||||
text: `${verb}${buildFieldValueRich(fieldUuid, prevValue!)}提升至${buildFieldValueRich(fieldUuid, entry.value)}`,
|
||||
});
|
||||
}
|
||||
prevValue = entry.value;
|
||||
});
|
||||
return lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* 收集复活技能分档行(hero.revive 各英雄等级档,无 s_lv 概念,按档位序号编号)。
|
||||
* slv1 给完整效果(复活次数 + 重生血量);slv2+ 给复活次数变化。
|
||||
* 档位配置了 desc 时直接采用。
|
||||
*/
|
||||
function collectReviveTierLines(hero: heroInfo, sUuid: number): HeroSkillTierLine[] {
|
||||
const matched = (hero.revive ?? [])
|
||||
.filter(e => e.s_uuid === sUuid)
|
||||
.sort((a, b) => a.lv - b.lv);
|
||||
if (matched.length === 0) return [];
|
||||
|
||||
const lines: HeroSkillTierLine[] = [];
|
||||
let prevCount: number | undefined;
|
||||
matched.forEach((e, i) => {
|
||||
// 复活次数复刻战斗公式:r_num + floor((英雄等级-1)*upr)
|
||||
const count = e.r_num + Math.floor((e.lv - 1) * e.upr);
|
||||
const hpPct = Math.round(SkillSet[e.s_uuid]?.ap ?? 0);
|
||||
if (e.desc) {
|
||||
lines.push({ sLv: i + 1, heroLv: e.lv, text: e.desc });
|
||||
} else if (i === 0) {
|
||||
lines.push({ sLv: 1, heroLv: e.lv, text: `每回合可复活${valueRich(count)}次, 死亡后以${valueRich(`${hpPct}%`)}生命原地重生` });
|
||||
} else if (prevCount !== undefined && count !== prevCount) {
|
||||
lines.push({ sLv: i + 1, heroLv: e.lv, text: `复活次数${valueRich(prevCount)}次提升至${valueRich(count)}次` });
|
||||
} else {
|
||||
lines.push({ sLv: i + 1, heroLv: e.lv, text: "无变化" });
|
||||
}
|
||||
prevCount = count;
|
||||
});
|
||||
return lines;
|
||||
}
|
||||
|
||||
/** 单个技能等级档位的展示行(结构化,供弹窗逐档渲染激活状态与描述) */
|
||||
export interface HeroSkillTierLine {
|
||||
/** 技能等级(slv) */
|
||||
@@ -360,11 +438,17 @@ export interface HeroSkillTierLine {
|
||||
* slv1 给完整能力 + 数值;slv2+ 给相对上一级的提升。每档附带解锁英雄等级,
|
||||
* 调用方可自行对比英雄当前等级(smc.getHeroOuterLv)判定激活状态。
|
||||
*
|
||||
* 技能定位顺序:hero.skills[skillUuid](普攻/大招)→ 5 类触发技能组 → 未找到返回 []。
|
||||
* 技能定位顺序(覆盖英雄图鉴全部 5 类技能形态):
|
||||
* 1. hero.skills[skillUuid] —— 普攻/大招
|
||||
* 2. 5 类触发技能组 —— call/dead/fstart/atking/atked
|
||||
* 3. hero.field —— 驻场光环词条(FieldSkillSet uuid)
|
||||
* 4. hero.revive —— 复活技能(SkillSet uuid)
|
||||
* 均未命中返回 []。
|
||||
*
|
||||
* Why: 同一底座技能可能被多个英雄以不同 overrides 引用,必须以英雄语境取档。
|
||||
*
|
||||
* @param heroUuid 英雄 uuid(HeroInfo key)
|
||||
* @param skillUuid 技能 uuid(skills key 或触发组 s_uuid)
|
||||
* @param skillUuid 技能 uuid(skills key / 触发组 s_uuid / FieldSkillSet uuid / 复活 SkillSet uuid)
|
||||
* @returns 档位行数组;技能不属于该英雄时返回 []
|
||||
*/
|
||||
export function buildHeroSkillTierLines(heroUuid: number, skillUuid: number): HeroSkillTierLine[] {
|
||||
@@ -404,7 +488,15 @@ export function buildHeroSkillTierLines(heroUuid: number, skillUuid: number): He
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (tiers.length === 0) return [];
|
||||
|
||||
// 光环/复活路径(无 ISkillTierInfo 中间结构,直接生成 HeroSkillTierLine)
|
||||
if (tiers.length === 0) {
|
||||
const fieldLines = collectFieldTierLines(hero, skillUuid);
|
||||
if (fieldLines.length > 0) return fieldLines;
|
||||
const reviveLines = collectReviveTierLines(hero, skillUuid);
|
||||
if (reviveLines.length > 0) return reviveLines;
|
||||
return [];
|
||||
}
|
||||
|
||||
const lines: HeroSkillTierLine[] = [];
|
||||
for (let i = 0; i < tiers.length; i++) {
|
||||
@@ -450,11 +542,19 @@ export function buildHeroSkillTierDesc(heroUuid: number, skillUuid: number, show
|
||||
.join("\n");
|
||||
}
|
||||
|
||||
/** 在英雄触发组中解析技能对外展示名(非触发技能返回底座名) */
|
||||
function resolveTriggerSkillName(hero: typeof HeroInfo[number], skillUuid: number): string {
|
||||
/** 在英雄技能配置中解析技能对外展示名(触发组名 / 光环词条名 / 复活名 / 底座名) */
|
||||
function resolveTriggerSkillName(hero: heroInfo, skillUuid: number): string {
|
||||
for (const type of TRIGGER_KEYS) {
|
||||
const grouped = hero[type] as TriggerGrouped | undefined;
|
||||
if (grouped?.[skillUuid]?.length) return getGroupedTriggerName(grouped, skillUuid);
|
||||
}
|
||||
// 驻场光环:词条名取 FieldSkillSet
|
||||
if (hero.field?.some(e => e.uuids.some(u => u.uuid === skillUuid))) {
|
||||
return getFieldSkillName(skillUuid);
|
||||
}
|
||||
// 复活技能:统一复活称谓
|
||||
if (hero.revive?.some(e => e.s_uuid === skillUuid)) {
|
||||
return getReviveSkillName();
|
||||
}
|
||||
return SkillSet[skillUuid]?.name ?? "";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user