feat(hero skill): add slot support for hero skill detail panel
1. 新增技能槽位参数,解决同底座技能(如普攻+触发技能)的分档定位问题 2. 新增2级/3级面板节点配置,自动隐藏无对应档位的面板 3. 重构英雄技能档位生成逻辑,支持按槽位精确查找技能配置 4. 优化技能弹窗渲染逻辑,传递槽位参数避免匹配错误分组
This commit is contained in:
@@ -433,71 +433,34 @@ export interface HeroSkillTierLine {
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出二(结构化):指定技能各等级档位说明,按技能等级升序。
|
||||
*
|
||||
* slv1 给完整能力 + 数值;slv2+ 给相对上一级的提升。每档附带解锁英雄等级,
|
||||
* 调用方可自行对比英雄当前等级(smc.getHeroOuterLv)判定激活状态。
|
||||
*
|
||||
* 技能定位顺序(覆盖英雄图鉴全部 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 / FieldSkillSet uuid / 复活 SkillSet uuid)
|
||||
* @returns 档位行数组;技能不属于该英雄时返回 []
|
||||
* 收集指定触发槽位的触发技能档位与触发语境词;未命中(组不存在/无该 s_uuid/无底座)返回 null。
|
||||
*/
|
||||
export function buildHeroSkillTierLines(heroUuid: number, skillUuid: number): HeroSkillTierLine[] {
|
||||
const hero = HeroInfo[heroUuid];
|
||||
if (!hero) return [];
|
||||
|
||||
let tiers: ISkillTierInfo[] = [];
|
||||
let triggerLabel = "";
|
||||
|
||||
const hSkill = hero.skills?.[skillUuid];
|
||||
if (hSkill) {
|
||||
// 普攻/大招路径
|
||||
tiers = collectSkillTiers(hSkill);
|
||||
} else {
|
||||
// 触发技能路径:在 5 类触发组中定位 s_uuid
|
||||
for (const type of TRIGGER_KEYS) {
|
||||
const grouped = hero[type] as TriggerGrouped | undefined;
|
||||
const entries = grouped?.[skillUuid];
|
||||
if (!grouped || !entries?.length) continue;
|
||||
const base = SkillSet[skillUuid];
|
||||
if (!base) return [];
|
||||
const sorted = [...entries].sort((a, b) => (a.s_lv ?? 0) - (b.s_lv ?? 0));
|
||||
let inherit: string | undefined;
|
||||
tiers = sorted.map((e: LvTriggerEntry, i: number) => {
|
||||
if (e.desc) inherit = e.desc;
|
||||
return {
|
||||
sLv: e.s_lv ?? (i + 1),
|
||||
heroLv: e.lv,
|
||||
skill: mergeSkillParams(base, e.overrides),
|
||||
desc: e.desc ?? inherit,
|
||||
tNum: e.t_num,
|
||||
};
|
||||
});
|
||||
triggerLabel = (type === SkillTriggerType.Atking || type === SkillTriggerType.Atked)
|
||||
? `${SkillTriggerName[type]}${valueRich(sorted[0].t_num)}次`
|
||||
: SkillTriggerName[type];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 光环/复活路径(无 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 [];
|
||||
}
|
||||
function collectTriggerTiers(hero: heroInfo, type: SkillTriggerType, skillUuid: number): { tiers: ISkillTierInfo[]; label: string } | null {
|
||||
const grouped = hero[type] as TriggerGrouped | undefined;
|
||||
const entries = grouped?.[skillUuid];
|
||||
if (!grouped || !entries?.length) return null;
|
||||
const base = SkillSet[skillUuid];
|
||||
if (!base) return null;
|
||||
const sorted = [...entries].sort((a, b) => (a.s_lv ?? 0) - (b.s_lv ?? 0));
|
||||
let inherit: string | undefined;
|
||||
const tiers = sorted.map((e: LvTriggerEntry, i: number) => {
|
||||
if (e.desc) inherit = e.desc;
|
||||
return {
|
||||
sLv: e.s_lv ?? (i + 1),
|
||||
heroLv: e.lv,
|
||||
skill: mergeSkillParams(base, e.overrides),
|
||||
desc: e.desc ?? inherit,
|
||||
tNum: e.t_num,
|
||||
};
|
||||
});
|
||||
const label = (type === SkillTriggerType.Atking || type === SkillTriggerType.Atked)
|
||||
? `${SkillTriggerName[type]}${valueRich(sorted[0].t_num)}次`
|
||||
: SkillTriggerName[type];
|
||||
return { tiers, label };
|
||||
}
|
||||
|
||||
/** 档位 → 展示行:slv1 完整能力 + 数值;slv2+ 相对上一级的提升 */
|
||||
function buildTierLines(tiers: ISkillTierInfo[], triggerLabel: string): HeroSkillTierLine[] {
|
||||
const lines: HeroSkillTierLine[] = [];
|
||||
for (let i = 0; i < tiers.length; i++) {
|
||||
const t = tiers[i];
|
||||
@@ -514,6 +477,71 @@ export function buildHeroSkillTierLines(heroUuid: number, skillUuid: number): He
|
||||
return lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出二(结构化):指定技能各等级档位说明,按技能等级升序。
|
||||
*
|
||||
* slv1 给完整能力 + 数值;slv2+ 给相对上一级的提升。每档附带解锁英雄等级,
|
||||
* 调用方可自行对比英雄当前等级(smc.getHeroOuterLv)判定激活状态。
|
||||
*
|
||||
* 技能定位顺序(覆盖英雄图鉴全部 5 类技能形态):
|
||||
* 1. hero.skills[skillUuid] —— 普攻/大招
|
||||
* 2. 5 类触发技能组 —— call/dead/fstart/atking/atked
|
||||
* 3. hero.field —— 驻场光环词条(FieldSkillSet uuid)
|
||||
* 4. hero.revive —— 复活技能(SkillSet uuid)
|
||||
* 均未命中返回 []。
|
||||
*
|
||||
* Why: 同一底座技能可能被多个英雄以不同 overrides 引用,必须以英雄语境取档;
|
||||
* 同一底座还可能同时是普攻与触发技能(如 5002 的 6004),传 slot 指定槽位
|
||||
* 可避免自动定位命中错误分组(激活等级/档位随之错位)。
|
||||
*
|
||||
* @param heroUuid 英雄 uuid(HeroInfo key)
|
||||
* @param skillUuid 技能 uuid(skills key / 触发组 s_uuid / FieldSkillSet uuid / 复活 SkillSet uuid)
|
||||
* @param slot 技能所属槽位(可空):指定时仅在该槽位查找;缺省按上述顺序自动定位
|
||||
* @returns 档位行数组;技能不属于该英雄时返回 []
|
||||
*/
|
||||
export function buildHeroSkillTierLines(heroUuid: number, skillUuid: number, slot?: SkillTriggerType): HeroSkillTierLine[] {
|
||||
const hero = HeroInfo[heroUuid];
|
||||
if (!hero) return [];
|
||||
|
||||
// 槽位指定:光环/复活结构特殊直接生成行;触发组走统一档位结构
|
||||
if (slot === SkillTriggerType.Field) return collectFieldTierLines(hero, skillUuid);
|
||||
if (slot === SkillTriggerType.Revive) return collectReviveTierLines(hero, skillUuid);
|
||||
if (slot !== undefined) {
|
||||
const hit = collectTriggerTiers(hero, slot, skillUuid);
|
||||
return hit ? buildTierLines(hit.tiers, hit.label) : [];
|
||||
}
|
||||
|
||||
// 自动定位:普攻/大招优先,其次 5 类触发组,最后光环/复活
|
||||
let tiers: ISkillTierInfo[] = [];
|
||||
let triggerLabel = "";
|
||||
const hSkill = hero.skills?.[skillUuid];
|
||||
if (hSkill) {
|
||||
// 普攻/大招路径
|
||||
tiers = collectSkillTiers(hSkill);
|
||||
} else {
|
||||
// 触发技能路径:在 5 类触发组中定位 s_uuid
|
||||
for (const type of TRIGGER_KEYS) {
|
||||
const hit = collectTriggerTiers(hero, type, skillUuid);
|
||||
if (hit) {
|
||||
tiers = hit.tiers;
|
||||
triggerLabel = hit.label;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 光环/复活路径(无 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 [];
|
||||
}
|
||||
|
||||
return buildTierLines(tiers, triggerLabel);
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出二:指定技能的各阶段说明(富文本整段)。
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user