diff --git a/assets/script/game/common/HeroSkillText.ts b/assets/script/game/common/HeroSkillText.ts index 406a47a9..475cf3bb 100644 --- a/assets/script/game/common/HeroSkillText.ts +++ b/assets/script/game/common/HeroSkillText.ts @@ -542,9 +542,11 @@ function buildTierLines(tiers: ISkillTierInfo[], triggerLabel: string): HeroSkil * @param heroUuid 英雄 uuid(HeroInfo key) * @param skillUuid 技能 uuid(skills key / 触发组 s_uuid / FieldSkillSet uuid / 复活 SkillSet uuid) * @param slot 技能所属槽位(可空):指定时仅在该槽位查找;缺省按上述顺序自动定位 + * @param minLv 最低展示档(默认 1):传 2 时裁剪掉 sLv<2 的档,仅保留 lv2+ 档位 + * (普攻强化档作为独立技能展示的场景,如 heroSet 普攻 lv2 档单独出图标) * @returns 档位行数组;技能不属于该英雄时返回 [] */ -export function buildHeroSkillTierLines(heroUuid: number, skillUuid: number, slot?: SkillTriggerType): HeroSkillTierLine[] { +export function buildHeroSkillTierLines(heroUuid: number, skillUuid: number, slot?: SkillTriggerType, minLv: number = 1): HeroSkillTierLine[] { const hero = HeroInfo[heroUuid]; if (!hero) return []; @@ -577,12 +579,13 @@ export function buildHeroSkillTierLines(heroUuid: number, skillUuid: number, slo // 光环路径(无 ISkillTierInfo 中间结构,直接生成 HeroSkillTierLine) if (tiers.length === 0) { const fieldLines = collectFieldTierLines(hero, skillUuid); - if (fieldLines.length > 0) return fieldLines; + if (fieldLines.length > 0) return minLv > 1 ? fieldLines.filter(l => l.sLv >= minLv) : fieldLines; // 复活已纳入 dead 触发组,通过 dead 路径处理 return []; } - return buildTierLines(tiers, triggerLabel); + const lines = buildTierLines(tiers, triggerLabel); + return minLv > 1 ? lines.filter(l => l.sLv >= minLv) : lines; } /** diff --git a/assets/script/game/map/HerosListComp.ts b/assets/script/game/map/HerosListComp.ts index f2c4c714..76426da0 100644 --- a/assets/script/game/map/HerosListComp.ts +++ b/assets/script/game/map/HerosListComp.ts @@ -22,7 +22,7 @@ import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ec import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp"; import { oops } from "db://oops-framework/core/Oops"; import { mLogger } from "../common/Logger"; -import { HeroInfo, HeroList, SkillTriggerType, LvFieldEntry, LvTriggerEntry, TriggerGrouped, getHeroDisplayStats } from "../common/config/heroSet"; +import { HeroInfo, HeroList, SkillTriggerType, LvFieldEntry, LvTriggerEntry, TriggerGrouped, HSkillInfo, getHeroDisplayStats } from "../common/config/heroSet"; import { buildHeroSkillDesc } from "../common/HeroSkillText"; import { GameEvent } from "../common/config/GameEvent"; import { HERO_UPGRADE_NEED_FRAGMENTS, RARITY_BG_COLORS } from "../common/config/GameSet"; @@ -524,6 +524,9 @@ export class HerosListComp extends CCComp { this.addSkillIcon(hSkill.uuid, sLv, false, false) }) + // ---- 普攻 lv2 强化档:作为独立技能图标(名称/图标取 lv1,弹窗只展示 lv2 说明) ---- + this.addNormalLv2Icon(Object.values(hero.skills)[0], lv) + // ---- 触发技能:每个触发槽位整组一个图标,按 PANEL_TRIGGER_TYPES 顺序追加 ---- for (const type of HerosListComp.PANEL_TRIGGER_TYPES) { if (type === SkillTriggerType.Field) { @@ -536,21 +539,23 @@ export class HerosListComp extends CCComp { /** * 实例化技能小图标并初始化 - * @param s_uuid 技能 UUID(SkillSet 或 FieldSkillSet;整组模式下取组内首个,仅用于图标) - * @param s_lv 当前等级对应的技能等级(locked 时为解锁预览档) - * @param locked 当前等级未激活 → 显示锁定遮罩 - * @param isField true 表示驻场光环词条(图标取 FieldSkillSet) - * @param slot 技能所属槽位(普攻/大招传 undefined;触发/光环传对应类型, - * 同一底座不同槽位视为不同技能,弹窗按槽位取档;复活已纳入 dead) - * @param isGroup 整组模式:该图标代表整个触发槽位,组名取组内首个自定义名, - * 弹窗按整组合并渲染(组内多技能/词条统一说明) + * @param s_uuid 技能 UUID(SkillSet 或 FieldSkillSet;整组模式下取组内首个,仅用于图标) + * @param s_lv 当前等级对应的技能等级(locked 时为解锁预览档) + * @param locked 当前等级未激活 → 显示锁定遮罩 + * @param isField true 表示驻场光环词条(图标取 FieldSkillSet) + * @param slot 技能所属槽位(普攻/大招传 undefined;触发/光环传对应类型, + * 同一底座不同槽位视为不同技能,弹窗按槽位取档;复活已纳入 dead) + * @param isGroup 整组模式:该图标代表整个触发槽位,组名取组内首个自定义名, + * 弹窗按整组合并渲染(组内多技能/词条统一说明) + * @param normalLv2 普攻 lv2 专属模式:该图标代表普攻 lv2 强化档,名称/图标取 lv1, + * 弹窗只展示 lv2 档位说明 */ - private addSkillIcon(s_uuid: number, s_lv: number, locked: boolean, isField: boolean, slot?: SkillTriggerType, isGroup: boolean = false) { + private addSkillIcon(s_uuid: number, s_lv: number, locked: boolean, isField: boolean, slot?: SkillTriggerType, isGroup: boolean = false, normalLv2: 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, isGroup) + comp.initHeroSkill(s_uuid, s_lv, locked, isField, this.huuid, slot, isGroup, normalLv2) this.skills_node.addChild(node) } @@ -598,6 +603,22 @@ export class HerosListComp extends CCComp { /** 复活技能已纳入 dead 触发组,通过 dead 配置展示(revive_hp / revive_count),无需单独处理 */ + /** + * 普攻 lv2+ 强化档作为独立技能图标: + * 普攻(skills[0])lv_entries 存在档时单独出图标,名称/图标取 lv1(s_uuid 相同), + * 等级显示偏移一档(配置 Lv2 → Lv1,SSkillBoxComp 统一处理),弹窗按 hero_normal_lv2 + * 模式只展示 lv2+ 档位说明;激活判定按档位的英雄等级(lv 阈值)。 + * Why: 普攻 lv2 档是独立成长机制(如附加暴击/冰冻),需让玩家明确感知其存在与解锁条件。 + */ + private addNormalLv2Icon(normal: HSkillInfo | undefined, lv: number) { + const entry = normal?.lv_entries?.[0] + if (!normal || !entry) return + // 节点命名区分 lv2 档(普攻与触发槽位可能同底座,uuid 重名会互相干扰) + this.addSkillIcon(normal.uuid, entry.s_lv ?? 2, lv < entry.lv, false, undefined, false, true) + const icon = this.skills_node.children[this.skills_node.children.length - 1] + if (icon) icon.name = `skill_${normal.uuid}_lv2` + } + // ======================== 英雄动画 ======================== private updateHeroAnimation(uuid: number) { diff --git a/assets/script/game/map/SSkillBoxComp.ts b/assets/script/game/map/SSkillBoxComp.ts index 323b4dbb..13aa42ec 100644 --- a/assets/script/game/map/SSkillBoxComp.ts +++ b/assets/script/game/map/SSkillBoxComp.ts @@ -48,6 +48,9 @@ export class SSkillBoxComp extends CCComp { /** 整组模式(英雄图鉴):true 时该图标代表整个触发槽位(atking/dead/fstart/atked/call/field), * 组内多技能/词条合并为一个名称与一套说明展示,弹窗按整组取档 */ private hero_group: boolean = false; + /** 普攻 lv2 专属模式(英雄图鉴):true 时该图标代表普攻 lv2 强化档, + * 名称/图标取 lv1,弹窗只展示 lv2 档位说明(裁剪 lv1 档) */ + private hero_normal_lv2: boolean = false; /** 卡牌等级 */ private rarity: number = 1; /** 卡牌自定义图标ID(优先级最高,未定义则按 trigger_type 自动取) */ @@ -89,6 +92,8 @@ export class SSkillBoxComp extends CCComp { hero_slot: this.hero_uuid ? this.hero_slot : undefined, // 整组模式:弹窗按整个触发槽位合并渲染(组内多技能/词条统一说明) hero_group: this.hero_uuid ? this.hero_group : undefined, + // 普攻 lv2 专属模式:弹窗只展示 lv2 档位说明(名称/图标取 lv1) + hero_normal_lv2: this.hero_uuid ? this.hero_normal_lv2 : undefined, }); } @@ -115,13 +120,18 @@ export class SSkillBoxComp extends CCComp { * @param slot 技能所属槽位(普攻/大招传 undefined;触发/光环传对应类型,复活已纳入 dead) * @param isGroup 整组模式:该图标代表整个触发槽位,组名取组内首个自定义名, * 弹窗按整组合并渲染(组内多技能/词条统一说明) - */ - initHeroSkill(s_uuid: number, s_lv: number, locked: boolean, isField: boolean = false, heroUuid: number = 0, slot?: SkillTriggerType, isGroup: boolean = false) { + * @param normalLv2 普攻 lv2+ 强化档专属模式:名称/图标取 lv1,弹窗只展示 lv2+ 档位说明; + * 等级显示偏移一档(配置 Lv2 → 显示 Lv1,Lv3 → 显示 Lv2),强化档从 1 起计 + */ + initHeroSkill(s_uuid: number, s_lv: number, locked: boolean, isField: boolean = false, heroUuid: number = 0, slot?: SkillTriggerType, isGroup: boolean = false, normalLv2: 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_normal_lv2 = normalLv2; + // 普攻 lv2+ 强化档:等级显示偏移一档(配置 Lv2 显示 Lv1,Lv3 显示 Lv2), + // 强化档作为独立于普攻的技能存在,等级从 1 起计 + this.rarity = normalLv2 ? Math.max(1, s_lv - 1) : s_lv; this.hero_icon_source = isField ? "field" : "skill"; this.updateUI(); this.setLocked(locked); diff --git a/assets/script/game/map/SinfoComp.ts b/assets/script/game/map/SinfoComp.ts index 79297cae..e30f2467 100644 --- a/assets/script/game/map/SinfoComp.ts +++ b/assets/script/game/map/SinfoComp.ts @@ -64,6 +64,9 @@ export interface SInfoParams { /** 触发组整体模式(英雄图鉴):true 时按 hero_slot 整组渲染, * 组内多技能/词条合并为一套分档说明统一展示(每个触发槽位视为一个技能) */ hero_group?: boolean; + /** 普攻 lv2+ 强化档专属模式(英雄图鉴):名称/图标取 lv1,只展示 lv2+ 档位说明 + * (裁剪 lv1 档,隐藏 2/3 级面板);等级显示偏移一档(配置 Lv2 → 显示 Lv1,Lv3 → Lv2) */ + hero_normal_lv2?: boolean; } /** 等级提升信息(用于 2 级 / 3 级面板) */ @@ -256,15 +259,16 @@ export class SinfoComp extends CCComp { private renderHeroSkill(p: SInfoParams): boolean { 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); + : buildHeroSkillTierLines(p.hero_uuid!, p.uuid, p.hero_slot, p.hero_normal_lv2 ? 2 : 1); if (tiers.length === 0) return false; - // 图标 / 名称 / 当前技能等级与通用逻辑一致(skill_lv 传的就是当前生效 s_lv) + // 图标 / 名称 / 当前技能等级与通用逻辑一致(skill_lv 传的就是当前生效 s_lv; + // 普攻 lv2+ 强化档等级偏移一档显示:配置 Lv2 → Lv1,Lv3 → Lv2) this.renderIcon(p); this.setLabel(this.name_label, this.resolveName(p)); - this.setLabel(this.lv_label, `Lv.${p.skill_lv}`); + this.setLabel(this.lv_label, `Lv.${p.hero_normal_lv2 ? Math.max(1, p.skill_lv - 1) : p.skill_lv}`); - // 技能 1 级:完整能力描述 + // 主说明:普攻 lv2 专属模式只展示 lv2 档位文本(裁剪后首档即 lv2),否则 slv1 完整能力 if (this.main_info && this.main_info.isValid) { this.main_info.string = tiers[0].text || "暂无描述"; } @@ -272,9 +276,17 @@ export class SinfoComp extends CCComp { // 英雄当前局外等级:激活判定基准 const heroLv = Math.max(1, smc.getHeroOuterLv(p.hero_uuid!)); - // 未激活图标背景:1 级档位未解锁时显示 + // 未激活图标背景:首档未解锁时显示 this.updateInactiveBg(heroLv >= tiers[0].heroLv); + // 普攻 lv2 专属模式:等级/档位面板均由该档接管,隐藏 2/3 级面板(只展示 lv2 描述) + if (p.hero_normal_lv2) { + this.setPanelActive(this.lv2_node, false); + this.setPanelActive(this.lv3_node, false); + this.renderLvPanelFromTier(tiers[0], heroLv, this.main_status, null); + return true; + } + // 技能 1 级:激活状态与 2/3 级面板同一表示方式(描述已在 main_info 展示,此处仅渲染状态) this.renderLvPanelFromTier(tiers[0], heroLv, this.main_status, null);