feat(talent): 重构天赋升级交互,新增确认弹窗逻辑

1.  移除天赋预制件上的+号节点和旧的背景组件,改为按阶位切换颜色背景
2.  天赋点击逻辑改为先弹出确认弹窗再执行升级
3.  简化天赋项组件的参数传递和状态判断逻辑
4.  更新注释文档与组件职责描述
This commit is contained in:
panFD
2026-08-30 17:18:36 +08:00
parent 0ceff6fb05
commit 69aa1863b7
7 changed files with 2604 additions and 1316 deletions

View File

@@ -5,29 +5,29 @@
* 职责:
* 1. 接收 TalentsComp 下发的天赋配置 + 当前等级 + 阶位解锁态,渲染迷你点选卡。
* 2. 卡面自包含:图标 / 名称 / 当前已生效数值(Lv0 显示每级数值引导)/ 右下「当前/满级」角标。
* 3. 状态机:可升级(右上 + 徽标)/ 满级(MAX 金角标)/ 未激活(左上锁角标,点击锁/卡体可升级激活)。
* 4. 点击回调上抛(含 + 徽标与卡体),升级校验与 toast 统一由 TalentsComp 处理(单一决策点)。
* 3. 阶位背景:普通背景节点下挂 blue / red / yellow 三个颜色子节点,按阶位激活其一(Ⅰ蓝 / Ⅱ红 / Ⅲ金)。
* 4. 状态机:满级(MAX 金角标)/ 未激活(左上锁角标 + 背景与图标灰阶 Grayscale)。
* 5. 点击回调上抛卡体点击,加点确认弹窗与升级校验统一由 TalentsComp 处理(单一决策点)。
*
* 依赖:
* - TalentSet.TalentConfig / TalentTier / getTalentDisplayText —— 配置与卡面文案
* - CardBgComp —— 阶位品质底框(Ⅰ蓝 / Ⅱ紫 / Ⅲ金)
* - 背景三颜色子节点(blue / red / yellow,编辑器侧搭建)
* - smc.uiconsAtlas —— 图标解析(缺省回退资源目录)
*/
import { _decorator, Label, Node, NodeEventType, Sprite, SpriteFrame } from "cc";
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
import { TalentConfig, TalentTier, getTalentDisplayText } from "../common/config/TalentSet";
import { CardBgComp } from "./CardBgComp";
import { smc } from "../common/SingletonModuleComp";
import { oops } from "db://oops-framework/core/Oops";
const { ccclass, property } = _decorator;
/** 阶位 → 品质底框 card_lv(CARD_LV_BG_COLORS:2=blue、3=purple、4=yellow) */
const TIER_TO_BG_LV: Record<number, number> = {
[TalentTier.Basic]: 2,
[TalentTier.Specialized]: 3,
[TalentTier.Super]: 4,
/** 阶位 → 背景节点下的颜色子节点名(Ⅰ蓝 / Ⅱ红 / Ⅲ金) */
const TIER_BG_CHILD_NAMES: Record<number, string> = {
[TalentTier.Basic]: "blue",
[TalentTier.Specialized]: "red",
[TalentTier.Super]: "yellow",
};
/** TalentItemComp —— 天赋迷你升级卡组件 */
@@ -47,11 +47,8 @@ export class TalentItemComp extends CCComp {
@property({ type: Sprite, tooltip: "天赋图标" })
icon: Sprite = null!;
@property({ type: CardBgComp, tooltip: "卡牌背景组件(阶位品质色)" })
bg: CardBgComp = null!;
@property({ type: Node, tooltip: "右上 + 升级徽标(可升级时显示)" })
node_plus: Node = null!;
@property({ type: Node, tooltip: "卡牌背景节点(下挂 blue / red / yellow 三颜色子节点,按阶位激活其一)" })
bg: Node = null!;
@property({ type: Node, tooltip: "MAX 满级角标" })
node_max: Node = null!;
@@ -78,9 +75,8 @@ export class TalentItemComp extends CCComp {
* @param cfg 天赋配置(TalentSet 条目)
* @param lv 当前等级
* @param tierUnlocked 所属阶位是否已解锁
* @param canUp 父级预判的"可升级"(阶位解锁 + 未满级 + 点数足够)
*/
public updateItem(cfg: TalentConfig, lv: number, tierUnlocked: boolean, canUp: boolean): void {
public updateItem(cfg: TalentConfig, lv: number, tierUnlocked: boolean): void {
if (!cfg) return;
this.curUuid = cfg.uuid;
@@ -89,8 +85,11 @@ export class TalentItemComp extends CCComp {
if (this.lbl_info) this.lbl_info.string = getTalentDisplayText(cfg, lv);
if (this.lbl_lv) this.lbl_lv.string = `${lv}/${cfg.max_lv}`;
// 阶位品质底框:Ⅰ蓝 / Ⅱ紫 / Ⅲ金
if (this.bg) this.bg.apply(TIER_TO_BG_LV[cfg.tier] ?? 1);
// 未激活(Lv0 或阶位未解锁):背景与图标灰阶
const inactive = lv <= 0 || !tierUnlocked;
// 阶位背景:激活对应颜色子节点(Ⅰ蓝 / Ⅱ红 / Ⅲ金)
if (this.bg) this.applyTierBg(cfg.tier, inactive);
// 图标:uiconsAtlas 优先,资源目录回退(与 TalentItemComp 旧版/SCardComp 口径一致)
if (this.icon && cfg.icon) {
@@ -102,16 +101,28 @@ export class TalentItemComp extends CCComp {
frame = oops.res.get("game/heros/cards/" + cfg.icon, SpriteFrame) as SpriteFrame;
}
if (frame) this.icon.spriteFrame = frame;
this.icon.grayscale = inactive;
}
// 状态徽标:满级 MAX / 可升级 + / 未激活锁角标(未加点或阶位未解锁,置于左上角)
const maxed = lv >= cfg.max_lv;
if (this.node_max) this.node_max.active = maxed;
if (this.node_plus) this.node_plus.active = !maxed && tierUnlocked && canUp;
if (this.node_lock) this.node_lock.active = lv <= 0 || !tierUnlocked;
// 状态角标:满级 MAX / 未激活锁角标(未加点或阶位未解锁,置于左上角)
if (this.node_max) this.node_max.active = lv >= cfg.max_lv;
if (this.node_lock) this.node_lock.active = inactive;
}
/** 卡体/+ 徽标/锁角标点击:触摸事件冒泡至卡体统一上抛 uuid,升级校验与提示由 TalentsComp 处理 */
/** 按阶位激活 bg 下对应颜色子节点,其余隐藏;未激活时全部开启灰阶 */
private applyTierBg(tier: TalentTier, inactive: boolean): void {
const curName = TIER_BG_CHILD_NAMES[tier];
for (const [, name] of Object.entries(TIER_BG_CHILD_NAMES)) {
const child = this.bg.getChildByName(name);
if (!child) continue;
child.active = name === curName;
// Sprite 内置灰阶材质,未激活天赋的背景置灰
const render = child.getComponent(Sprite);
if (render) render.grayscale = inactive;
}
}
/** 卡体点击上抛 uuid,加点确认与升级校验由 TalentsComp 处理 */
private onTap(): void {
this.onTalentTap?.(this.curUuid);
}