diff --git a/assets/script/game/common/HeroSkillText.ts b/assets/script/game/common/HeroSkillText.ts index 79eff7d7..9a51fc32 100644 --- a/assets/script/game/common/HeroSkillText.ts +++ b/assets/script/game/common/HeroSkillText.ts @@ -14,6 +14,11 @@ * 2. buildHeroSkillTierDesc(heroUuid, skillUuid) —— 指定技能各阶段说明: * slv1 给完整能力 + 激活英雄等级;slv2+ 给相对上一级的提升 + 解锁英雄等级 * + * 触发组口径:每个触发槽位(atking/dead/fstart/atked/call/field)整组视为一个技能, + * 组内多个 s_uuid / 光环词条合并为一个名称(组内首个自定义名)与一套说明统一展示: + * - buildHeroSkillDesc 中每组合并为一行(CHeroComp / HeroBoxComp 等直接消费) + * - buildHeroGroupTierLines(heroUuid, slot) 供弹窗按整组分档渲染(SinfoComp) + * * 占位符规范(模板作者可用,渲染时数值片段自动绿色高亮): * {target} 目标语义(自身/敌方/全体友方/N名队友…,与 SCastSystem 目标选取一致) * {dur} 计时 buff 持续秒数(buff_duration 优先,回退 BuffList 默认) @@ -24,9 +29,9 @@ * 卡牌/道具语境描述见 config/CardSkillText.ts。 */ import { - HeroInfo, heroInfo, SkillTriggerType, SkillTriggerName, HSkillInfo, LvTriggerEntry, + HeroInfo, heroInfo, SkillTriggerType, SkillTriggerName, HSkillInfo, LvTriggerEntry, FieldEntry, TriggerGrouped, resolveSkillOverrides, resolveTriggerByLv, resolveFieldByLv, - getGroupedTriggerName, getFieldSkillName, + getGroupedTriggerName, getFieldSkillName, getTriggerGroupName, getFieldGroupName, } from "./config/heroSet"; import { SkillSet, mergeSkillParams, SkillConfig, SkillKind, TGroup, FieldSkillSet } from "./config/SkillSet"; import { BuffList } from "./config/BuffSet"; @@ -145,8 +150,9 @@ function getHeroDisplayLv(heroUuid: number, configLv: number, fac: FacSet): numb * 输出一:英雄当前局外等级下,全部技能最大生效档的完整说明(富文本)。 * * 覆盖:普攻/大招(skills,resolve ≤lv 最大档)、5 类触发技能、驻场光环(field)、 - * 死亡触发(dead,含复活)。每行 = 「技能名」:(或「技能名」+ 触发语境)+ 模板渲染后的效果文本(数值高亮), - * 不展示技能等级档号(当前档由英雄局外等级隐含决定)。 + * 死亡触发(dead,含复活)。触发槽位整组视为一个技能(组内多技能/词条合并为一行, + * 组名取组内首个自定义名),普攻/大招仍逐技能一行。每行 = 「技能名」:(或「技能名」+ + * 触发语境)+ 模板渲染后的效果文本(数值高亮),不展示技能等级档号(当前档由英雄局外等级隐含决定)。 * * Why 普攻默认隐藏:skills 首位为普攻(全英雄共有基础能力),展示场景通常只关心 * 特色技能(大招/触发/光环),普攻行信息冗余;需要完整展示时传 showNormal=true。 @@ -184,11 +190,16 @@ export function buildHeroSkillDesc(heroUuid: number, showName: boolean = true, s lines.push(head ? `${head}: ${text}` : text); } - // ---- 5 类标准触发技能:resolve 当前等级生效档 ---- + // ---- 5 类触发技能:每个触发槽位整组视为一个技能,组内多技能效果合并为一行 ---- for (const type of TRIGGER_KEYS) { const grouped = hero[type] as TriggerGrouped | undefined; if (!grouped) continue; - for (const item of resolveTriggerByLv(grouped, lv)) { + const items = resolveTriggerByLv(grouped, lv); + if (items.length === 0) continue; + // 组内多个技能时,各效果片段带自身技能名区分 + const headOf = (s_uuid: number) => (items.length > 1 ? skillNameRich(getGroupedTriggerName(grouped, s_uuid)) : ""); + const parts: string[] = []; + for (const item of items) { const base = SkillSet[item.s_uuid]; if (!base) continue; const skill = mergeSkillParams(base, item.overrides); @@ -204,31 +215,40 @@ export function buildHeroSkillDesc(heroUuid: number, showName: boolean = true, s const trig = type === SkillTriggerType.Atking || type === SkillTriggerType.Atked ? `${SkillTriggerName[type]}${valueRich(item.t_num)}次` : SkillTriggerName[type]; - lines.push(`${nameSeg(getGroupedTriggerName(grouped, item.s_uuid))}${trig}, ${text}`); + parts.push(`${headOf(item.s_uuid)}${trig}, ${text}`); } + // dead 组内的复活效果(revive_hp / revive_count)并入同一行 + if (type === SkillTriggerType.Dead) { + for (const item of items) { + const reviveHp = item.overrides?.revive_hp; + const reviveCount = item.overrides?.revive_count ?? 0; + if (reviveHp === undefined || reviveCount <= 0) continue; + const text = `每回合可复活${valueRich(reviveCount)}次, 死亡后以${valueRich(`${Math.round(reviveHp)}%`)}生命原地重生`; + parts.push(`${headOf(item.s_uuid)}${SkillTriggerName[SkillTriggerType.Dead]}, ${text}`); + } + } + if (parts.length === 0) continue; + // 组名取组内首个自定义名(getTriggerGroupName) + lines.push(`${nameSeg(getTriggerGroupName(hero, type))}${parts.join(";")}`); } - // ---- 驻场光环(field):查 FieldDescSet 按 type 渲染,数值实时高亮 ---- - for (const entry of resolveFieldByLv(hero.field, lv)) { + // ---- 驻场光环(field):整组视为一个技能,组内多词条合并为一行 ---- + const fieldItems = resolveFieldByLv(hero.field, lv); + const fieldParts: string[] = []; + for (const entry of fieldItems) { const fs = FieldSkillSet[entry.uuid]; const desc = fs ? FieldDescSet[fs.type] : undefined; if (!fs || !desc) continue; const val = desc.fmt === FieldValueFmt.Percent ? `+${Math.round(entry.value * 100)}%` : desc.fmt === FieldValueFmt.IntMinus ? `-${entry.value}` : `+${entry.value}`; - lines.push(`${nameSeg(getFieldSkillName(hero, entry.uuid))}${desc.verb}${valueRich(val)}`); + // 组内多个词条时带各自词条名区分 + const head = fieldItems.length > 1 ? skillNameRich(entry.name ?? fs.name) : ""; + fieldParts.push(`${head}${desc.verb}${valueRich(val)}`); } - - // ---- 复活(已纳入 dead 触发组):从 dead 配置提取 revive_hp / revive_count ---- - for (const item of resolveTriggerByLv(hero.dead, lv)) { - const reviveHp = item.overrides?.revive_hp; - const reviveCount = item.overrides?.revive_count ?? 0; - if (reviveHp === undefined || reviveCount <= 0) continue; - const base = SkillSet[item.s_uuid]; - if (!base) continue; - const skill = mergeSkillParams(base, item.overrides); - const text = `每回合可复活${valueRich(reviveCount)}次, 死亡后以${valueRich(`${Math.round(reviveHp)}%`)}生命原地重生`; - lines.push(`${nameSeg(getGroupedTriggerName(hero.dead!, item.s_uuid))}${SkillTriggerName[SkillTriggerType.Dead]}, ${text}`); + if (fieldParts.length > 0) { + // 组名取组内首个自定义名(getFieldGroupName) + lines.push(`${nameSeg(getFieldGroupName(hero))}${fieldParts.join(",")}`); } return lines.join("\n"); @@ -552,6 +572,104 @@ export function buildHeroSkillTierLines(heroUuid: number, skillUuid: number, slo return buildTierLines(tiers, triggerLabel); } +/** + * 驻场光环整组分档行(hero.field 全部词条合并展示)。 + * 每档一行:新词条给完整效果(动词 + 数值),已有词条给数值提升; + * 组内仅 1 个词条时与单词条口径一致。档位配置了 desc(LvFieldEntry.desc)时优先采用。 + */ +function collectFieldGroupTierLines(hero: heroInfo): HeroSkillTierLine[] { + const tiers = [...(hero.field ?? [])].sort((a, b) => a.lv - b.lv); + if (tiers.length === 0) return []; + // 组内词条数 >1 时每行带词条名区分 + const uuidSet = new Set(); + for (const t of tiers) { + for (const u of t.uuids) uuidSet.add(u.uuid); + } + const multi = uuidSet.size > 1; + + const lines: HeroSkillTierLine[] = []; + let prev: FieldEntry[] = []; + tiers.forEach((e, i) => { + const parts: string[] = []; + for (const entry of e.uuids) { + const fs = FieldSkillSet[entry.uuid]; + const descCfg = fs ? FieldDescSet[fs.type] : undefined; + if (!fs || !descCfg) continue; + const head = multi ? `${entry.name ?? fs.name}: ` : ""; + const before = prev.find(p => p.uuid === entry.uuid); + if (!before) { + // 新词条:完整效果(动词 + 数值高亮) + parts.push(`${head}${descCfg.verb}${buildFieldValueRich(entry.uuid, entry.value)}`); + } else if (before.value !== entry.value) { + // 已有词条:数值由旧值提升至新值 + parts.push(`${head}${descCfg.verb}${buildFieldValueRich(entry.uuid, before.value)}提升至${buildFieldValueRich(entry.uuid, entry.value)}`); + } + } + lines.push({ sLv: i + 1, heroLv: e.lv, text: e.desc ?? parts.join("\n") }); + prev = e.uuids; + }); + return lines; +} + +/** 多技能档位行按解锁英雄等级合并:同阈值档位文本换行拼接,sLv 取该阈值下最大档 */ +function mergeTierLinesByHeroLv(groups: HeroSkillTierLine[][]): HeroSkillTierLine[] { + if (groups.length === 0) return []; + if (groups.length === 1) return groups[0]; + const byLv = new Map(); + for (const lines of groups) { + for (const line of lines) { + const bucket = byLv.get(line.heroLv); + if (bucket) bucket.push(line); + else byLv.set(line.heroLv, [line]); + } + } + return [...byLv.keys()].sort((a, b) => a - b).map(heroLv => { + const bucket = byLv.get(heroLv)!; + return { + sLv: Math.max(...bucket.map(l => l.sLv)), + heroLv, + text: bucket.map(l => l.text).join("\n"), + }; + }); +} + +/** + * 输出二(触发组整体):指定触发槽位整组的各等级档位说明,按解锁英雄等级升序。 + * + * 每个触发槽位(atking/dead/fstart/atked/call/field)整组视为一个技能: + * 组内各 s_uuid 分别生成分档行后按解锁英雄等级阈值合并(同阈值文本换行拼接), + * 组内多个技能时每行带各自技能名区分;驻场光环按词条维度合并。 + * 每档附带解锁英雄等级,调用方对比 smc.getHeroOuterLv 判定激活状态。 + * + * @param heroUuid 英雄 uuid(HeroInfo key) + * @param slot 触发槽位(field 表示驻场光环整组) + * @returns 合并后的档位行数组;该槽位未配置返回 [] + */ +export function buildHeroGroupTierLines(heroUuid: number, slot: SkillTriggerType): HeroSkillTierLine[] { + const hero = HeroInfo[heroUuid]; + if (!hero) return []; + if (slot === SkillTriggerType.Field) return collectFieldGroupTierLines(hero); + + const grouped = hero[slot] as TriggerGrouped | undefined; + if (!grouped) return []; + const keys = Object.keys(grouped).map(Number); + const lineGroups: HeroSkillTierLine[][] = []; + for (const sUuid of keys) { + const hit = collectTriggerTiers(hero, slot, sUuid); + if (!hit) continue; + const lines = buildTierLines(hit.tiers, hit.label); + // 组内多个技能时每行带各自技能名区分 + if (keys.length > 1) { + const name = getGroupedTriggerName(grouped, sUuid); + for (const line of lines) { + line.text = `${skillNameRich(name)}${line.text}`; + } + } + lineGroups.push(lines); + } + return mergeTierLinesByHeroLv(lineGroups); +} + /** * 输出二:指定技能的各阶段说明(富文本整段)。 * diff --git a/assets/script/game/common/config/heroSet.ts b/assets/script/game/common/config/heroSet.ts index 0160d0b8..595e991d 100644 --- a/assets/script/game/common/config/heroSet.ts +++ b/assets/script/game/common/config/heroSet.ts @@ -1,4 +1,4 @@ -import { v3 } from "cc" +import { v3 } from "cc" import { BoxSet, FacSet, FightSet } from "./GameSet" import { SkillOverrides, TGroup, SkillSet, FieldSkillSet, SkillConfig, SkillKind, FieldSkillConfig, FieldSkillType, mergeSkillParams } from "./SkillSet" import { BuffList, BuffConfig, ModOp } from "./BuffSet" @@ -254,8 +254,10 @@ export enum SkillLv { /** * 触发技能对外命名规则: - * 英雄除普攻外的技能(atked/atking/dead/fstart/revive)命名统一收敛进各英雄 HeroInfo 配置 + * 英雄除普攻外的技能(call/atked/atking/dead/fstart)命名统一收敛进各英雄 HeroInfo 配置 * (HSkillInfo.name 给普攻/主技能命名;LvTriggerEntry.name 给触发技能组命名,写在该组第 1 档)。 + * UI 层将每个触发槽位(atking/dead/fstart/atked/call/field)整组视为一个技能: + * 组内多个 s_uuid / 光环词条合并为一个名称与一套说明统一展示,组名取组内首个自定义名。 * 天赋由 TalentSet 自行命名;SkillSet.name 仅作内部底座代号,不直接对外展示。 */ /** @@ -284,6 +286,44 @@ export function getFieldSkillName(hero: heroInfo | undefined, uuid: number): str return FieldSkillSet[uuid]?.name ?? ""; } +/** + * 查询触发技能组(整个触发槽位)的对外统一展示名(英雄语境)。 + * Why: 每个触发槽位整组视为一个技能,组内多个 s_uuid 合并展示,需要统一组名。 + * @param hero 英雄配置 + * @param type 触发槽位(call/dead/fstart/atking/atked) + * @returns 组内首个配置了 name 的条目名(按 s_uuid 与档位配置顺序扫描); + * 均未配置时回退组内首个 s_uuid 的 SkillSet 底座名;组不存在返回 "" + */ +export function getTriggerGroupName(hero: heroInfo | undefined, type: SkillTriggerType): string { + const grouped = hero?.[type] as TriggerGrouped | undefined; + if (!grouped) return ""; + let firstUuid = 0; + for (const key in grouped) { + if (!firstUuid) firstUuid = Number(key); + const name = grouped[key]?.[0]?.name; + if (name) return name; + } + return firstUuid ? (SkillSet[firstUuid]?.name ?? "") : ""; +} + +/** + * 查询驻场光环组(hero.field 整体)的对外统一展示名(英雄语境)。 + * Why: field 槽位同样整组视为一个技能,组内多个词条合并展示,组名取首个自定义名。 + * @param hero 英雄配置(含 field 词条档位) + * @returns 全部档位词条中首个配置 name 的(按档位 lv 与词条顺序扫描); + * 均未配置时回退首个词条的 FieldSkillSet 底座名;无光环返回 "" + */ +export function getFieldGroupName(hero: heroInfo | undefined): string { + let firstUuid = 0; + for (const tier of hero?.field ?? []) { + for (const entry of tier.uuids) { + if (!firstUuid) firstUuid = entry.uuid; + if (entry.name) return entry.name; + } + } + return firstUuid ? (FieldSkillSet[firstUuid]?.name ?? "") : ""; +} + /** * 复活已纳入 dead 触发组:通过 SkillOverrides.revive_hp / revive_count 配置, * 不再需要独立的 revive 字段和解析函数。 @@ -447,21 +487,23 @@ export const HeroInfo: Record = { 5001: { uuid: 5001, name: "叠甲骑士", path: "hk1", fac: FacSet.HERO, rarity: 1, lv: 1, type: HType.Melee, grow_type: HGrowType.Tank, role: HRole.Tank, hp: HERO_ROLE_BASE[HRole.Tank].hp + 20, ap: HERO_ROLE_BASE[HRole.Tank].ap, def: HERO_ROLE_BASE[HRole.Tank].def + 10, skills: { - 6004: { uuid: 6004, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0, overrides: {"ap":100}, desc: "挥剑对{target}造成攻击力{ap}%伤害", + 6004: { + uuid: 6004, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0, overrides: { "ap": 100 }, desc: "挥剑对{target}造成攻击力{ap}%伤害", lv_entries: [ - { lv: 2, s_lv: SkillLv.Lv2, overrides: {"ap":120} }, + { lv: 2, s_lv: SkillLv.Lv2, overrides: { "ap": 120 } }, ], }, - 6301: { uuid: 6301, name: "魔法盾", lv: 1, cd: 6, ccd: 0, overrides: {"ap":30,"summon_count":1}, desc: "{target}获得生命最大值{ap}%的护盾", + 6301: { + uuid: 6301, name: "魔法盾", lv: 1, cd: 6, ccd: 0, overrides: { "ap": 30, "summon_count": 1 }, desc: "{target}获得生命最大值{ap}%的护盾", lv_entries: [ - { lv: 8, s_lv: SkillLv.Lv2, overrides: {"ap":60,"summon_count":1} }, - { lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":120,"summon_count":2} }, + { lv: 8, s_lv: SkillLv.Lv2, overrides: { "ap": 60, "summon_count": 1 } }, + { lv: 16, s_lv: SkillLv.Lv3, overrides: { "ap": 120, "summon_count": 2 } }, ], }, }, atked: { 6301: [ - { lv: 4, s_lv: SkillLv.Lv1, name: "全体圣盾", t_num: 3, overrides: {"TGroup":null,"ap":10,"summon_count":1}, desc: "全队获得本英雄生命最大值{ap}%的护盾" }, + { lv: 4, s_lv: SkillLv.Lv1, name: "全体圣盾", t_num: 3, overrides: { "TGroup": null, "ap": 10, "summon_count": 1 }, desc: "全队获得本英雄生命最大值{ap}%的护盾" }, ], }, info: "", @@ -470,21 +512,23 @@ export const HeroInfo: Record = { 5002: { uuid: 5002, name: "易伤剑仙", path: "hk3", fac: FacSet.HERO, rarity: 1, lv: 1, type: HType.Melee, grow_type: HGrowType.Warrior, role: HRole.Warrior, hp: HERO_ROLE_BASE[HRole.Warrior].hp + 5, ap: HERO_ROLE_BASE[HRole.Warrior].ap + 20, def: HERO_ROLE_BASE[HRole.Warrior].def + 5, skills: { - 6004: { uuid: 6004, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0, overrides: {"ap":100}, desc: "挥拳对{target}造成攻击力{ap}%伤害", + 6004: { + uuid: 6004, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0, overrides: { "ap": 100 }, desc: "挥拳对{target}造成攻击力{ap}%伤害", lv_entries: [ - { lv: 2, s_lv: SkillLv.Lv2, overrides: {"ap":120} }, + { lv: 2, s_lv: SkillLv.Lv2, overrides: { "ap": 120 } }, ], }, - 6101: { uuid: 6101, name: "气斩", lv: 1, cd: 8, ccd: 0, overrides: {"ap":150,"timed_buff_id":7002,"summon_count":1}, desc: "斩出气刃,对{target}造成攻击力{ap}%伤害,并使目标易伤(受到伤害加深,持续{dur}秒)", + 6101: { + uuid: 6101, name: "气斩", lv: 1, cd: 8, ccd: 0, overrides: { "ap": 150, "timed_buff_id": 7002, "summon_count": 1 }, desc: "斩出气刃,对{target}造成攻击力{ap}%伤害,并使目标易伤(受到伤害加深,持续{dur}秒)", lv_entries: [ - { lv: 8, s_lv: SkillLv.Lv2, overrides: {"buff_value":20,"summon_count":1} }, - { lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":180,"bck":60,"buff_value":40,"summon_count":2}, desc: "斩出气刃,对{target}造成攻击力{ap}%伤害,击退{bck},并使目标易伤(持续{dur}秒)" }, + { lv: 8, s_lv: SkillLv.Lv2, overrides: { "buff_value": 20, "summon_count": 1 } }, + { lv: 16, s_lv: SkillLv.Lv3, overrides: { "ap": 180, "bck": 60, "buff_value": 40, "summon_count": 2 }, desc: "斩出气刃,对{target}造成攻击力{ap}%伤害,击退{bck},并使目标易伤(持续{dur}秒)" }, ], }, }, atking: { 6004: [ - { lv: 4, s_lv: SkillLv.Lv1, name: "奋力一击", t_num: 3, overrides: {"ap":300}, desc: "每三次攻击后立即打击{target},造成攻击力{ap}%伤害" }, + { lv: 4, s_lv: SkillLv.Lv1, name: "奋力一击", t_num: 3, overrides: { "ap": 300 }, desc: "每三次攻击后立即打击{target},造成攻击力{ap}%伤害" }, ], }, info: "", @@ -493,15 +537,17 @@ export const HeroInfo: Record = { 5003: { uuid: 5003, name: "眩晕暴击哥", path: "hk5", fac: FacSet.HERO, rarity: 1, lv: 1, type: HType.Melee, grow_type: HGrowType.Warrior, role: HRole.Warrior, hp: HERO_ROLE_BASE[HRole.Warrior].hp + 5, ap: HERO_ROLE_BASE[HRole.Warrior].ap + 20, def: HERO_ROLE_BASE[HRole.Warrior].def + 5, skills: { - 6004: { uuid: 6004, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0, overrides: {"ap":100}, desc: "挥斧对{target}造成攻击力{ap}%伤害", + 6004: { + uuid: 6004, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0, overrides: { "ap": 100 }, desc: "挥斧对{target}造成攻击力{ap}%伤害", lv_entries: [ - { lv: 2, s_lv: SkillLv.Lv2, overrides: {"stun":10}, desc: "挥斧对{target}造成攻击力{ap}%伤害,{stun}%概率击晕" }, + { lv: 2, s_lv: SkillLv.Lv2, overrides: { "stun": 10 }, desc: "挥斧对{target}造成攻击力{ap}%伤害,{stun}%概率击晕" }, ], }, - 6104: { uuid: 6104, name: "开山斧", lv: 1, cd: 8, ccd: 0, overrides: {"ap":300,"stun":100,"summon_count":1}, desc: "劈出开山斧,对{target}造成攻击力{ap}%伤害,必定击晕", + 6104: { + uuid: 6104, name: "开山斧", lv: 1, cd: 8, ccd: 0, overrides: { "ap": 300, "stun": 100, "summon_count": 1 }, desc: "劈出开山斧,对{target}造成攻击力{ap}%伤害,必定击晕", lv_entries: [ - { lv: 8, s_lv: SkillLv.Lv2, overrides: {"ap":450,"stun":100,"summon_count":1} }, - { lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":450,"stun":100,"summon_count":2} }, + { lv: 8, s_lv: SkillLv.Lv2, overrides: { "ap": 450, "stun": 100, "summon_count": 1 } }, + { lv: 16, s_lv: SkillLv.Lv3, overrides: { "ap": 450, "stun": 100, "summon_count": 2 } }, ], }, }, @@ -514,15 +560,17 @@ export const HeroInfo: Record = { 5004: { uuid: 5004, name: "刀刀暴击", path: "hc1", fac: FacSet.HERO, rarity: 1, lv: 1, type: HType.Mid, grow_type: HGrowType.Assassin, role: HRole.Assassin, hp: HERO_ROLE_BASE[HRole.Assassin].hp, ap: HERO_ROLE_BASE[HRole.Assassin].ap + 30, def: HERO_ROLE_BASE[HRole.Assassin].def, skills: { - 6002: { uuid: 6002, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Fast].cd, ccd: 0, overrides: {"ap":100}, desc: "挥刀对{target}造成攻击力{ap}%伤害", + 6002: { + uuid: 6002, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Fast].cd, ccd: 0, overrides: { "ap": 100 }, desc: "挥刀对{target}造成攻击力{ap}%伤害", lv_entries: [ - { lv: 2, s_lv: SkillLv.Lv2, overrides: {"crt":10}, desc: "挥刀对{target}造成攻击力{ap}%伤害,{crt}%概率暴击" }, + { lv: 2, s_lv: SkillLv.Lv2, overrides: { "crt": 10 }, desc: "挥刀对{target}造成攻击力{ap}%伤害,{crt}%概率暴击" }, ], }, - 6100: { uuid: 6100, name: "光闪", lv: 1, cd: 8, ccd: 0, overrides: {"ap":150,"summon_count":1}, desc: "斩出光闪,对{target}造成攻击力{ap}%伤害", + 6100: { + uuid: 6100, name: "光闪", lv: 1, cd: 8, ccd: 0, overrides: { "ap": 150, "summon_count": 1 }, desc: "斩出光闪,对{target}造成攻击力{ap}%伤害", lv_entries: [ - { lv: 8, s_lv: SkillLv.Lv2, overrides: {"ap":250,"summon_count":1} }, - { lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":350,"summon_count":2} }, + { lv: 8, s_lv: SkillLv.Lv2, overrides: { "ap": 250, "summon_count": 1 } }, + { lv: 16, s_lv: SkillLv.Lv3, overrides: { "ap": 350, "summon_count": 2 } }, ], }, }, @@ -535,15 +583,17 @@ export const HeroInfo: Record = { 5005: { uuid: 5005, name: "出血贼", path: "hc2", fac: FacSet.HERO, rarity: 1, lv: 1, type: HType.Mid, grow_type: HGrowType.Assassin, role: HRole.Assassin, hp: HERO_ROLE_BASE[HRole.Assassin].hp, ap: HERO_ROLE_BASE[HRole.Assassin].ap + 30, def: HERO_ROLE_BASE[HRole.Assassin].def, skills: { - 6002: { uuid: 6002, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Fast].cd, ccd: 0, overrides: {"ap":100}, desc: "挥刀对{target}造成攻击力{ap}%伤害", + 6002: { + uuid: 6002, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Fast].cd, ccd: 0, overrides: { "ap": 100 }, desc: "挥刀对{target}造成攻击力{ap}%伤害", lv_entries: [ - { lv: 2, s_lv: SkillLv.Lv2, overrides: {"timed_buff_id":7007,"buff_value":30,"buff_chance":30}, desc: "挥刀对{target}造成攻击力{ap}%伤害,{buff_chance}%概率使目标出血(每0.5秒受到{buff_value}点伤害,持续{dur}秒)" }, + { lv: 2, s_lv: SkillLv.Lv2, overrides: { "timed_buff_id": 7007, "buff_value": 30, "buff_chance": 30 }, desc: "挥刀对{target}造成攻击力{ap}%伤害,{buff_chance}%概率使目标出血(每0.5秒受到{buff_value}点伤害,持续{dur}秒)" }, ], }, - 6103: { uuid: 6103, name: "闪电斩", lv: 1, cd: 8, ccd: 0, overrides: {"ap":150,"summon_count":1}, desc: "召唤落雷斩击{target},造成攻击力{ap}%伤害", + 6103: { + uuid: 6103, name: "闪电斩", lv: 1, cd: 8, ccd: 0, overrides: { "ap": 150, "summon_count": 1 }, desc: "召唤落雷斩击{target},造成攻击力{ap}%伤害", lv_entries: [ - { lv: 8, s_lv: SkillLv.Lv2, overrides: {"ap":250,"timed_buff_id":7007,"summon_count":1}, desc: "召唤落雷斩击{target},造成攻击力{ap}%伤害,并使目标出血(每0.5秒受到{buff_value}点伤害,持续{dur}秒)" }, - { lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":350,"para":50,"timed_buff_id":7007,"buff_value":60,"summon_count":2}, desc: "召唤落雷斩击{target},造成攻击力{ap}%伤害,{para}%概率麻痹,并使目标出血(每0.5秒受到{buff_value}点伤害,持续{dur}秒)" }, + { lv: 8, s_lv: SkillLv.Lv2, overrides: { "ap": 250, "timed_buff_id": 7007, "summon_count": 1 }, desc: "召唤落雷斩击{target},造成攻击力{ap}%伤害,并使目标出血(每0.5秒受到{buff_value}点伤害,持续{dur}秒)" }, + { lv: 16, s_lv: SkillLv.Lv3, overrides: { "ap": 350, "para": 50, "timed_buff_id": 7007, "buff_value": 60, "summon_count": 2 }, desc: "召唤落雷斩击{target},造成攻击力{ap}%伤害,{para}%概率麻痹,并使目标出血(每0.5秒受到{buff_value}点伤害,持续{dur}秒)" }, ], }, }, @@ -556,15 +606,17 @@ export const HeroInfo: Record = { 5006: { uuid: 5006, name: "冻箭哥", path: "ha1", fac: FacSet.HERO, rarity: 1, lv: 1, type: HType.Long, grow_type: HGrowType.Archer, role: HRole.Archer, hp: HERO_ROLE_BASE[HRole.Archer].hp, ap: HERO_ROLE_BASE[HRole.Archer].ap + 30, def: HERO_ROLE_BASE[HRole.Archer].def, skills: { - 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal].cd, ccd: 0, overrides: {"ap":100}, desc: "射出箭矢,对{target}造成攻击力{ap}%伤害", + 6006: { + uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal].cd, ccd: 0, overrides: { "ap": 100 }, desc: "射出箭矢,对{target}造成攻击力{ap}%伤害", lv_entries: [ - { lv: 2, s_lv: SkillLv.Lv2, overrides: {"bck":10}, desc: "射出箭矢,对{target}造成攻击力{ap}%伤害,击退{bck}" }, + { lv: 2, s_lv: SkillLv.Lv2, overrides: { "bck": 10 }, desc: "射出箭矢,对{target}造成攻击力{ap}%伤害,击退{bck}" }, ], }, - 6150: { uuid: 6150, name: "万箭齐发·冰", lv: 1, cd: 8, ccd: 0, overrides: {"ap":150,"frz":30,"summon_count":1}, desc: "万箭齐发,对{target}造成攻击力{ap}%伤害,{frz}%概率冰冻", + 6150: { + uuid: 6150, name: "万箭齐发·冰", lv: 1, cd: 8, ccd: 0, overrides: { "ap": 150, "frz": 30, "summon_count": 1 }, desc: "万箭齐发,对{target}造成攻击力{ap}%伤害,{frz}%概率冰冻", lv_entries: [ - { lv: 8, s_lv: SkillLv.Lv2, overrides: {"ap":200,"frz":50,"summon_count":1} }, - { lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":250,"frz":70,"summon_count":2} }, + { lv: 8, s_lv: SkillLv.Lv2, overrides: { "ap": 200, "frz": 50, "summon_count": 1 } }, + { lv: 16, s_lv: SkillLv.Lv3, overrides: { "ap": 250, "frz": 70, "summon_count": 2 } }, ], }, }, @@ -577,15 +629,17 @@ export const HeroInfo: Record = { 5007: { uuid: 5007, name: "击退火女", path: "hm1", fac: FacSet.HERO, rarity: 1, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Mage, hp: HERO_ROLE_BASE[HRole.Mage].hp - 5, ap: HERO_ROLE_BASE[HRole.Mage].ap + 10, def: HERO_ROLE_BASE[HRole.Mage].def, skills: { - 6010: { uuid: 6010, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0, overrides: {"ap":100,"bck":30}, desc: "掷出紫魔球,对{target}造成攻击力{ap}%伤害,击退{bck}", + 6010: { + uuid: 6010, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0, overrides: { "ap": 100, "bck": 30 }, desc: "掷出紫魔球,对{target}造成攻击力{ap}%伤害,击退{bck}", lv_entries: [ - { lv: 2, s_lv: SkillLv.Lv2, overrides: {"bck":40} }, + { lv: 2, s_lv: SkillLv.Lv2, overrides: { "bck": 40 } }, ], }, - 6200: { uuid: 6200, name: "火焰喷射", lv: 1, cd: 8, ccd: 0, overrides: {"ap":250,"bck":100,"summon_count":1}, desc: "喷出烈焰风暴,对{target}造成攻击力{ap}%伤害,击退{bck}", + 6200: { + uuid: 6200, name: "火焰喷射", lv: 1, cd: 8, ccd: 0, overrides: { "ap": 250, "bck": 100, "summon_count": 1 }, desc: "喷出烈焰风暴,对{target}造成攻击力{ap}%伤害,击退{bck}", lv_entries: [ - { lv: 8, s_lv: SkillLv.Lv2, overrides: {"ap":350,"bck":100,"summon_count":1} }, - { lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":450,"bck":100,"summon_count":2} }, + { lv: 8, s_lv: SkillLv.Lv2, overrides: { "ap": 350, "bck": 100, "summon_count": 1 } }, + { lv: 16, s_lv: SkillLv.Lv3, overrides: { "ap": 450, "bck": 100, "summon_count": 2 } }, ], }, }, @@ -598,26 +652,28 @@ export const HeroInfo: Record = { 5008: { uuid: 5008, name: "团疗奶爸", path: "hh1", fac: FacSet.HERO, rarity: 1, lv: 1, type: HType.Long, grow_type: HGrowType.Support, role: HRole.Support, hp: HERO_ROLE_BASE[HRole.Support].hp + 10, ap: HERO_ROLE_BASE[HRole.Support].ap + 20, def: HERO_ROLE_BASE[HRole.Support].def, skills: { - 6011: { uuid: 6011, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal].cd, ccd: 0, overrides: {"ap":100}, desc: "挥杖对{target}造成攻击力{ap}%伤害", + 6011: { + uuid: 6011, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal].cd, ccd: 0, overrides: { "ap": 100 }, desc: "挥杖对{target}造成攻击力{ap}%伤害", lv_entries: [ - { lv: 2, s_lv: SkillLv.Lv2, overrides: {"bck":10}, desc: "挥杖对{target}造成攻击力{ap}%伤害,击退{bck}" }, + { lv: 2, s_lv: SkillLv.Lv2, overrides: { "bck": 10 }, desc: "挥杖对{target}造成攻击力{ap}%伤害,击退{bck}" }, ], }, - 6302: { uuid: 6302, name: "治疗", lv: 1, cd: 8, ccd: 0, overrides: {"ap":300,"summon_count":1}, desc: "为{target}回复攻击力{ap}%生命", + 6302: { + uuid: 6302, name: "治疗", lv: 1, cd: 8, ccd: 0, overrides: { "ap": 300, "summon_count": 1 }, desc: "为{target}回复攻击力{ap}%生命", lv_entries: [ - { lv: 8, s_lv: SkillLv.Lv2, overrides: {"ap":400,"summon_count":1} }, - { lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":450,"summon_count":2} }, + { lv: 8, s_lv: SkillLv.Lv2, overrides: { "ap": 400, "summon_count": 1 } }, + { lv: 16, s_lv: SkillLv.Lv3, overrides: { "ap": 450, "summon_count": 2 } }, ], }, }, atking: { 6301: [ - { lv: 4, s_lv: SkillLv.Lv1, name: "圣愈之盾", t_num: 3, overrides: {"ap":30,"summon_count":1}, desc: "{target}获得生命最大值{ap}%的护盾" }, + { lv: 4, s_lv: SkillLv.Lv1, name: "圣愈之盾", t_num: 3, overrides: { "ap": 30, "summon_count": 1 }, desc: "{target}获得生命最大值{ap}%的护盾" }, ], }, info: "", }, -// @hero-setup:heroes-end + // @hero-setup:heroes-end /* *=============怪物配置列表================ diff --git a/assets/script/game/map/HerosListComp.ts b/assets/script/game/map/HerosListComp.ts index b0610c09..f2c4c714 100644 --- a/assets/script/game/map/HerosListComp.ts +++ b/assets/script/game/map/HerosListComp.ts @@ -486,19 +486,21 @@ export class HerosListComp extends CCComp { // ======================== 技能图标列表 ======================== - /** 图鉴面板展示的触发技能类型(heroInfo 触发配置:死亡/战斗开始/光环/攻击后/受击) */ + /** 图鉴面板展示的触发技能槽位(按触发时机排序:召唤→战斗开始→攻击后→受击后→死亡→常驻光环) */ private static readonly PANEL_TRIGGER_TYPES: SkillTriggerType[] = [ - SkillTriggerType.Dead, + SkillTriggerType.Call, SkillTriggerType.FStart, - SkillTriggerType.Field, SkillTriggerType.Atking, SkillTriggerType.Atked, + SkillTriggerType.Dead, + SkillTriggerType.Field, ] /** * 刷新当前英雄的技能图标列表: * 1. 大招:skills[1] 及以后(首个为普攻,跳过)。 - * 2. 触发技能:Dead/FStart/Field/Atking/Atked(复活已纳入 dead)。 + * 2. 触发技能:每个触发槽位(Call/FStart/Atking/Atked/Dead/Field)整组视为一个技能, + * 只出一个图标,组内多技能/词条合并展示(复活已纳入 dead)。 * 以局外等级(getHeroOuterLv)为当前等级,未达解锁档的显示 locked, * 已激活的显示当前等级对应的技能等级。 */ @@ -522,63 +524,76 @@ export class HerosListComp extends CCComp { this.addSkillIcon(hSkill.uuid, sLv, false, false) }) - // ---- 触发技能:按 PANEL_TRIGGER_TYPES 顺序追加 ---- + // ---- 触发技能:每个触发槽位整组一个图标,按 PANEL_TRIGGER_TYPES 顺序追加 ---- for (const type of HerosListComp.PANEL_TRIGGER_TYPES) { if (type === SkillTriggerType.Field) { - this.addFieldIcons(hero[SkillTriggerType.Field], lv) + this.addFieldGroupIcon(hero[SkillTriggerType.Field], lv) } else { - this.addTriggerIcons(hero[type] as TriggerGrouped | undefined, lv, type) + this.addTriggerGroupIcon(hero[type] as TriggerGrouped | undefined, lv, type) } } } /** * 实例化技能小图标并初始化 - * @param s_uuid 技能 UUID(SkillSet 或 FieldSkillSet) - * @param s_lv 当前等级对应的技能等级(locked 时为解锁预览档) - * @param locked 当前等级未激活 → 显示锁定遮罩 - * @param isField true 表示驻场光环词条(图标取 FieldSkillSet) - * @param slot 技能所属槽位(普攻/大招传 undefined;触发/光环传对应类型, - * 同一底座不同槽位视为不同技能,弹窗按槽位精确取档;复活已纳入 dead) + * @param s_uuid 技能 UUID(SkillSet 或 FieldSkillSet;整组模式下取组内首个,仅用于图标) + * @param s_lv 当前等级对应的技能等级(locked 时为解锁预览档) + * @param locked 当前等级未激活 → 显示锁定遮罩 + * @param isField true 表示驻场光环词条(图标取 FieldSkillSet) + * @param slot 技能所属槽位(普攻/大招传 undefined;触发/光环传对应类型, + * 同一底座不同槽位视为不同技能,弹窗按槽位取档;复活已纳入 dead) + * @param isGroup 整组模式:该图标代表整个触发槽位,组名取组内首个自定义名, + * 弹窗按整组合并渲染(组内多技能/词条统一说明) */ - private addSkillIcon(s_uuid: number, s_lv: number, locked: boolean, isField: boolean, slot?: SkillTriggerType) { + private addSkillIcon(s_uuid: number, s_lv: number, locked: boolean, isField: boolean, slot?: SkillTriggerType, isGroup: boolean = false) { const node = instantiate(this.skill_icon_prefab) node.name = `skill_${s_uuid}` const comp = node.getComponent(SSkillBoxComp) || node.addComponent(SSkillBoxComp) // 携带所属英雄与槽位,点击弹窗按英雄技能分档描述渲染 - comp.initHeroSkill(s_uuid, s_lv, locked, isField, this.huuid, slot) + comp.initHeroSkill(s_uuid, s_lv, locked, isField, this.huuid, slot, isGroup) this.skills_node.addChild(node) } /** - * 触发技能(dead/fstart/atking/atked):按 s_uuid 分组,取 ≤lv 最大档判定激活; - * 未激活时预览最低档(与 resolveTriggerByLv 同语义) - * @param type 触发槽位(随图标透传,区分与普攻同底座的触发技能) + * 触发技能槽位(call/fstart/atking/atked/dead)整组一个图标: + * 组内多 s_uuid 合并展示,图标/等级取组内首个 s_uuid 的生效档(未激活预览最低档); + * 整组激活判定:组内任一 s_uuid 存在 ≤lv 生效档即视为已激活(与 resolveTriggerByLv 同语义) + * @param type 触发槽位(随图标透传,弹窗按整组取档) */ - private addTriggerIcons(grouped: TriggerGrouped | undefined, lv: number, type: SkillTriggerType) { + private addTriggerGroupIcon(grouped: TriggerGrouped | undefined, lv: number, type: SkillTriggerType) { if (!grouped) return - for (const key in grouped) { - const entries = grouped[key] - if (!entries?.length) continue - const sorted = [...entries].sort((a, b) => a.lv - b.lv) - let active: LvTriggerEntry | undefined - for (const e of sorted) { if (e.lv <= lv) active = e } - const pick = active ?? sorted[0] - this.addSkillIcon(Number(key), pick.s_lv ?? 1, !active, false, type) + const keys = Object.keys(grouped).map(Number).filter(k => grouped[k]?.length) + if (keys.length === 0) return + // 图标/等级取组内首个 s_uuid 的生效档 + const sorted = [...grouped[keys[0]]].sort((a, b) => a.lv - b.lv) + let active: LvTriggerEntry | undefined + for (const e of sorted) { if (e.lv <= lv) active = e } + // 整组激活判定:组内任一 s_uuid 存在生效档即激活 + let anyActive = !!active + for (const key of keys) { + if (anyActive) break + for (const e of grouped[key] ?? []) { + if (e.lv <= lv) { anyActive = true; break } + } } + const pick = active ?? sorted[0] + this.addSkillIcon(keys[0], pick.s_lv ?? 1, !anyActive, false, type, true) } - /** 驻场光环:未激活取最低档预览,激活取 ≤lv 最大档的词条列表 */ - private addFieldIcons(field: LvFieldEntry[] | undefined, lv: number) { + /** + * 驻场光环槽位整组一个图标:组内多词条合并展示, + * 图标取生效档(未激活预览最低档)的首个词条 + */ + private addFieldGroupIcon(field: LvFieldEntry[] | undefined, lv: number) { if (!field?.length) return const sorted = [...field].sort((a, b) => a.lv - b.lv) let active: LvFieldEntry | undefined for (const e of sorted) { if (e.lv <= lv) active = e } const pick = active ?? sorted[0] - for (const entry of pick.uuids) { - // 词条档位无 s_lv 概念,技能等级恒为 1(等级标签展示技能等级而非解锁的英雄等级) - this.addSkillIcon(entry.uuid, 1, !active, true, SkillTriggerType.Field) - } + const firstUuid = pick.uuids[0]?.uuid + if (!firstUuid) return + // 词条档位无 s_lv 概念,技能等级恒为 1(等级标签展示技能等级而非解锁的英雄等级) + this.addSkillIcon(firstUuid, 1, !active, true, SkillTriggerType.Field, true) } /** 复活技能已纳入 dead 触发组,通过 dead 配置展示(revive_hp / revive_count),无需单独处理 */ diff --git a/assets/script/game/map/SSkillBoxComp.ts b/assets/script/game/map/SSkillBoxComp.ts index 5fd5cfb2..323b4dbb 100644 --- a/assets/script/game/map/SSkillBoxComp.ts +++ b/assets/script/game/map/SSkillBoxComp.ts @@ -6,7 +6,7 @@ import { CardTriggerType } from "../common/config/CardSet"; import { SkillSet, FieldSkillSet } from "../common/config/SkillSet"; import { FieldEntry, HeroInfo, TriggerGrouped, SkillTriggerType, - getGroupedTriggerName, getFieldSkillName, + getGroupedTriggerName, getFieldSkillName, getTriggerGroupName, getFieldGroupName, } from "../common/config/heroSet"; import { UIID } from "../common/config/GameUIConfig"; import { smc } from "../common/SingletonModuleComp"; @@ -45,6 +45,9 @@ export class SSkillBoxComp extends CCComp { /** 技能所属槽位(英雄图鉴模式):同一底座可同时为普攻与触发技能(如 5002 的 6004), * 弹窗按槽位精确取档,避免自动定位命中错误分组 */ private hero_slot?: SkillTriggerType; + /** 整组模式(英雄图鉴):true 时该图标代表整个触发槽位(atking/dead/fstart/atked/call/field), + * 组内多技能/词条合并为一个名称与一套说明展示,弹窗按整组取档 */ + private hero_group: boolean = false; /** 卡牌等级 */ private rarity: number = 1; /** 卡牌自定义图标ID(优先级最高,未定义则按 trigger_type 自动取) */ @@ -84,6 +87,8 @@ export class SSkillBoxComp extends CCComp { hero_uuid: this.hero_uuid || undefined, // 槽位:同一底座不同槽位视为不同技能(激活等级/档位各自独立) hero_slot: this.hero_uuid ? this.hero_slot : undefined, + // 整组模式:弹窗按整个触发槽位合并渲染(组内多技能/词条统一说明) + hero_group: this.hero_uuid ? this.hero_group : undefined, }); } @@ -101,17 +106,21 @@ export class SSkillBoxComp extends CCComp { /** * 初始化英雄技能图标(英雄图鉴面板专用): * 图标按 s_uuid 直接取 SkillSet / FieldSkillSet 配置,不走 EquipPoolList。 - * @param s_uuid 技能 UUID(大招/触发技能指向 SkillSet,光环词条指向 FieldSkillSet) + * @param s_uuid 技能 UUID(大招/触发技能指向 SkillSet,光环词条指向 FieldSkillSet; + * 整组模式下取组内首个 s_uuid/词条,仅用于图标与兜底名) * @param s_lv 当前英雄等级下生效的技能等级(显示在等级标签) * @param locked 当前等级未激活时显示锁定遮罩 * @param isField true 时图标取 FieldSkillSet(驻场光环词条) * @param heroUuid 所属英雄 UUID(点击弹窗按英雄技能分档描述渲染) * @param slot 技能所属槽位(普攻/大招传 undefined;触发/光环传对应类型,复活已纳入 dead) + * @param isGroup 整组模式:该图标代表整个触发槽位,组名取组内首个自定义名, + * 弹窗按整组合并渲染(组内多技能/词条统一说明) */ - initHeroSkill(s_uuid: number, s_lv: number, locked: boolean, isField: boolean = false, heroUuid: number = 0, slot?: SkillTriggerType) { + initHeroSkill(s_uuid: number, s_lv: number, locked: boolean, isField: boolean = false, heroUuid: number = 0, slot?: SkillTriggerType, isGroup: boolean = false) { this.s_uuid = s_uuid; this.hero_uuid = heroUuid; this.hero_slot = slot; + this.hero_group = isGroup; this.rarity = s_lv; this.hero_icon_source = isField ? "field" : "skill"; this.updateUI(); @@ -163,6 +172,7 @@ export class SSkillBoxComp extends CCComp { /** * 英雄图鉴模式:按技能 uuid 在英雄配置中定位,取英雄语境展示名。 + * 整组模式优先:整个触发槽位取统一组名(组内首个自定义名); * 覆盖普攻/大招(skills)、5 类触发技能、驻场光环(field);复活已纳入 dead 触发组。 * 指定槽位时仅在对应槽位查找(同一底座可同时是普攻与触发技能,需精确区分); * 未命中返回空串,由调用方回退底座名。 @@ -170,6 +180,12 @@ export class SSkillBoxComp extends CCComp { private resolveHeroSkillName(): string { const hero = HeroInfo[this.hero_uuid]; if (!hero) return ""; + // 整组模式:整个触发槽位一个技能,取组名(组内首个自定义名) + if (this.hero_group && this.hero_slot !== undefined) { + return this.hero_slot === SkillTriggerType.Field + ? getFieldGroupName(hero) + : getTriggerGroupName(hero, this.hero_slot); + } // 槽位指定:光环/触发组各自取值,未命中直接回退(复活已纳入 dead) if (this.hero_slot !== undefined) { if (this.hero_slot === SkillTriggerType.Field) { diff --git a/assets/script/game/map/SinfoComp.ts b/assets/script/game/map/SinfoComp.ts index 496142b6..79297cae 100644 --- a/assets/script/game/map/SinfoComp.ts +++ b/assets/script/game/map/SinfoComp.ts @@ -24,8 +24,9 @@ * - SkillSet / FieldSkillSet(技能/光环静态配置) * - SkillCardList(卡牌静态配置) * - CardSkillText(buildCardDescRich 富文本描述生成,卡牌模式) - * - HeroSkillText(buildHeroSkillTierLines 技能分档描述,英雄技能模式, - * 覆盖普攻/大招、触发技能、驻场光环词条;复活已纳入 dead 触发组) + * - HeroSkillText(buildHeroSkillTierLines 技能分档描述 / buildHeroGroupTierLines + * 触发组整组分档描述,英雄技能模式;触发槽位整组视为一个技能,组内多技能/词条 + * 合并为一个名称与一套说明统一展示;复活已纳入 dead 触发组) */ import { _decorator, Node, Label, RichText, NodeEventType, Sprite, Color, input, Input, EventTouch, UITransform, Vec3 } from "cc"; import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS"; @@ -36,7 +37,7 @@ import { SkillSet, FieldSkillSet } from "../common/config/SkillSet"; import { SkillCardList } from "../common/config/SCardSet"; import { FieldEntry, SkillTriggerType } from "../common/config/heroSet"; import { buildCardDescRich } from "../common/config/CardSkillText"; -import { buildHeroSkillTierLines, HeroSkillTierLine } from "../common/HeroSkillText"; +import { buildHeroSkillTierLines, buildHeroGroupTierLines, HeroSkillTierLine } from "../common/HeroSkillText"; import { smc } from "../common/SingletonModuleComp"; const { ccclass, property } = _decorator; @@ -60,6 +61,9 @@ export interface SInfoParams { /** 技能所属槽位(英雄图鉴模式):同一底座可同时为普攻与触发技能(如 5002 的 6004), * 指定后仅在该槽位取档,激活等级/档位按槽位独立判定 */ hero_slot?: SkillTriggerType; + /** 触发组整体模式(英雄图鉴):true 时按 hero_slot 整组渲染, + * 组内多技能/词条合并为一套分档说明统一展示(每个触发槽位视为一个技能) */ + hero_group?: boolean; } /** 等级提升信息(用于 2 级 / 3 级面板) */ @@ -243,13 +247,16 @@ export class SinfoComp extends CCComp { /** * 英雄技能模式渲染(文本二:单个技能各阶段说明)。 + * - hero_group:触发槽位整组视为一个技能,组内多技能/词条档位按解锁英雄等级合并展示 * - main_info:技能 slv1 完整描述 * - lv2_desc:slv2 相对 slv1 的提升;lv3_desc:slv3 及以后档(合并展示) * - lv2/lv3_status:按英雄局外等级(smc.getHeroOuterLv)对比档位解锁等级判定激活 * @returns true 已渲染(拦截通用卡牌逻辑);false 技能无分档数据,回退通用卡牌逻辑 */ private renderHeroSkill(p: SInfoParams): boolean { - const tiers = buildHeroSkillTierLines(p.hero_uuid!, p.uuid, p.hero_slot); + const tiers = (p.hero_group && p.hero_slot !== undefined) + ? buildHeroGroupTierLines(p.hero_uuid!, p.hero_slot) + : buildHeroSkillTierLines(p.hero_uuid!, p.uuid, p.hero_slot); if (tiers.length === 0) return false; // 图标 / 名称 / 当前技能等级与通用逻辑一致(skill_lv 传的就是当前生效 s_lv)