diff --git a/assets/resources/gui/element/heros.prefab b/assets/resources/gui/element/heros.prefab index dbb098ee..b3290596 100644 --- a/assets/resources/gui/element/heros.prefab +++ b/assets/resources/gui/element/heros.prefab @@ -1403,8 +1403,8 @@ }, "_lscale": { "__type__": "cc.Vec3", - "x": -2.5, - "y": 2.5, + "x": -2, + "y": 2, "z": 1 }, "_mobility": 0, @@ -1976,6 +1976,8 @@ "__id__": 0 }, "fileId": "28YQfbOz1OCrINhHQ3NA1a", + "instance": null, + "targetOverrides": null, "nestedPrefabInstanceRoots": null }, { @@ -12693,8 +12695,8 @@ "__uuid__": "b8313fa7-28e5-4d92-9d64-a1e0ecb040a8", "__expectedType__": "cc.Prefab" }, - "lv_node": { - "__id__": 125 + "lv_label": { + "__id__": 128 }, "type_node": { "__id__": 131 diff --git a/assets/script/game/map/CardLiteComp.ts b/assets/script/game/map/CardLiteComp.ts index 5cd74285..9ace9534 100644 --- a/assets/script/game/map/CardLiteComp.ts +++ b/assets/script/game/map/CardLiteComp.ts @@ -3,41 +3,34 @@ * @description 英雄图鉴卡预制体组件(UI 视图层) * * 职责: - * 1. 管理英雄图鉴中单张卡牌的 **显示**(名称、图标动画、费用、卡池等级、等级)。 - * 2. 接收 CardConfig 数据并渲染对应卡面(英雄 / 技能 / 特殊卡)。 + * 1. 管理英雄图鉴中单张英雄卡牌的 **显示**(名称、图标动画、卡池等级、英雄等级)。 + * 2. 接收英雄 UUID 并渲染对应卡面。 * 3. 点击卡牌时通过事件通知父组件(HerosListComp)切换选中英雄详情。 * * 关键设计: - * - 轻量版 CardComp,无拖拽使用、无锁定、无费用扣除等交互。 - * - 英雄卡图标加载 idle 动画剪辑并提取首帧静态展示;非英雄卡使用静态图标。 + * - 轻量版 CardComp,仅用于英雄展示,无拖拽使用、无锁定、无费用扣除等交互。 + * - 英雄卡图标加载 idle 动画剪辑并提取首帧静态展示。 * - 通过 iconVisualToken 防止异步加载竞态。 * * 依赖: - * - CardConfig / CardType / CKind 等卡牌数据结构(CardSet) * - HeroInfo(heroSet)—— 用于渲染英雄卡面信息 - * - SkillSet(SkillSet)—— 用于渲染技能卡图标 - * - smc —— 全局单例,访问图标图集缓存 + * - smc —— 全局单例,访问图标图集缓存与英雄局外等级 */ import { mLogger } from "../common/Logger"; import { _decorator, AnimationClip, EventTouch, Label, Node, NodeEventType, Sprite, SpriteFrame, Tween, tween, UIOpacity, Vec3, resources } 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 { CardConfig, CardType, SpecialRefreshCardList, SpecialUpgradeCardList, CKind, CardPoolList } from "../common/config/CardSet"; -import { SkillCardList } from "../common/config/SCardSet"; import { HeroInfo } from "../common/config/heroSet"; -import { SkillSet } from "../common/config/SkillSet"; import { getLvColor, CARD_LV_BG_COLORS } from "../common/config/GameSet"; import { smc } from "../common/SingletonModuleComp"; const { ccclass, property } = _decorator; -/** card_lv → BG_node 子节点(颜色)名映射已移至 GameSet.CARD_LV_BG_COLORS */ - /** - * CardLiteComp —— 单张卡牌简单视图组件 + * CardLiteComp —— 单张英雄卡简单视图组件 * * 挂载在英雄图鉴卡预制体上,由 HerosListComp 实例化并管理。 - * 负责单卡的渲染与点击选择。 + * 负责单张英雄卡的渲染与点击选择。 */ @ccclass('CardLiteComp') @ecs.register('CardLiteComp', false) @@ -61,10 +54,7 @@ export class CardLiteComp extends CCComp { // ======================== 运行时状态 ======================== - card_cost: number = 0 - card_type: CardType = CardType.Hero card_uuid: number = 0 - private cardData: CardConfig | null = null; private iconVisualToken: number = 0; private opacityComp: UIOpacity | null = null; @@ -77,9 +67,6 @@ export class CardLiteComp extends CCComp { this.bindEvents(); this.opacityComp = this.node.getComponent(UIOpacity) || this.node.addComponent(UIOpacity); this.opacityComp.opacity = 255; - if (!this.cardData) { - this.applyEmptyUI(); - } } onDestroy() { @@ -94,61 +81,29 @@ export class CardLiteComp extends CCComp { // ======================== 对外接口 ======================== /** - * 设置卡牌数据并渲染。 - * @param data 卡牌配置数据 - */ - setData(data: CardConfig) { - if (!data) return; - this.cardData = data; - this.card_uuid = data.uuid; - this.card_type = data.type; - this.card_cost = data.cost ?? 0; - this.node.active = true; - this.applyCardUI(); - mLogger.log(this.debugMode, "CardLiteComp", "setData", { - uuid: this.card_uuid, - type: this.card_type, - cost: this.card_cost - }); - } - - /** - * 通过英雄 UUID 直接设置英雄卡(纯展示场景)。 - * 从 HeroInfo 读取静态配置,不再依赖 CardPoolList。 + * 通过英雄 UUID 渲染英雄卡(纯展示场景)。 + * 从 HeroInfo 读取静态配置,从 smc 读取局外等级。 * @param uuid 英雄 UUID */ setHeroByUuid(uuid: number) { const hero = HeroInfo[uuid]; if (!hero) return; - this.setData({ - uuid: hero.uuid, - type: CardType.Hero, - cost: 0, - weight: 0, - card_lv: hero.card_lv ?? 1, - kind: CKind.Hero, - hero_lv: smc.getHeroOuterLv(hero.uuid) - }); + this.card_uuid = uuid; + this.node.active = true; + this.applyHeroUI(hero); + mLogger.log(this.debugMode, "CardLiteComp", "setHeroByUuid", { uuid }); } - /** 查询当前是否有卡牌数据 */ + /** 查询当前是否有英雄数据 */ hasCard(): boolean { - return !!this.cardData; - } - - /** 获取当前卡牌数据 */ - getCardData(): CardConfig | null { - return this.cardData; + return this.card_uuid > 0; } /** - * 清空卡牌数据并重置 UI。 + * 清空英雄数据并重置 UI。 */ clear() { - this.cardData = null; this.card_uuid = 0; - this.card_cost = 0; - this.card_type = CardType.Hero; this.applyEmptyUI(); this.node.active = false; } @@ -176,128 +131,63 @@ export class CardLiteComp extends CCComp { /** 点击卡牌:触发回调通知父组件 */ private onCardClick(event: EventTouch) { - if (!this.cardData) return; + if (!this.hasCard()) return; event.propagationStopped = true; if (this.onClickCallback) { this.onClickCallback(this); } - mLogger.log(this.debugMode, "CardLiteComp", "card clicked", { - uuid: this.card_uuid, - type: this.card_type - }); + mLogger.log(this.debugMode, "CardLiteComp", "card clicked", { uuid: this.card_uuid }); } // ======================== UI 渲染 ======================== /** - * 根据当前 cardData 渲染卡面。 + * 渲染英雄卡面。 * - * 渲染逻辑根据卡牌类型分三路: - * 1. 英雄卡:显示英雄名、等级、idle 动画。 - * 2. 技能卡:显示技能名、静态图标。 - * 3. 特殊卡:显示卡名、静态图标。 - * - * 同时根据 card_lv 切换背景底框,根据 kind 切换种类标识。 + * 1. 背景:按 card_lv 切换颜色底框(green/blue/purple/yellow/red)。 + * 2. 名称:英雄名。 + * 3. 等级:局外成长等级(Lv.X),颜色按 getLvColor。 + * 4. 图标:idle 动画首帧静态展示。 */ - private applyCardUI() { - if (!this.cardData) { - this.applyEmptyUI(); - return; - } - + private applyHeroUI(hero: typeof HeroInfo[number]) { this.iconVisualToken += 1; if (this.opacityComp) this.opacityComp.opacity = 255; - mLogger.log(this.debugMode, "CardLiteComp", "applyCardUI nodes", { - uuid: this.card_uuid, - hasName: !!this.name_node, - hasIcon: !!this.icon_node, - hasCost: !!this.cost_node, - hasBG: !!this.BG_node, - hasLvl: !!this.lvl_node, - nodeActive: this.node.active, - nodeScale: this.node.scale.toString(), - nodePos: this.node.position.toString(), - parentName: this.node.parent?.name || "none", - }); - - // 背景:按 card_lv 切换颜色底框(BG_node 下为 green/blue/purple/yellow/red 子节点) + // 背景:按 card_lv 切换颜色底框 if (this.BG_node) { - const bgLv = Math.min(Math.max(this.cardData.base_card_lv ?? this.cardData.card_lv ?? 1, 1), 5); + const bgLv = Math.min(Math.max(hero.card_lv ?? 1, 1), 5); const color = CARD_LV_BG_COLORS[bgLv - 1]; this.BG_node.children.forEach(child => child.active = (child.name === color)); } - if (this.card_type === CardType.Hero) { - const hero = HeroInfo[this.card_uuid]; + // 名称 + this.setLabel(this.name_node, `${hero?.name || ""}`); + + // 等级:局外成长等级 + if (this.lvl_node) { const heroLv = Math.max(1, smc.getHeroOuterLv(this.card_uuid) || hero?.lv || 1); - this.setLabel(this.name_node, `${hero?.name || ""}`); - if (this.lvl_node) { - this.lvl_node.string = `Lv.${heroLv}`; - this.lvl_node.color = getLvColor(heroLv); - this.lvl_node.node.active = true; - } - } else if (this.card_type === CardType.Skill) { - if (this.lvl_node) this.lvl_node.node.active = false; - const skill = SkillSet[this.card_uuid]; - const skillCard = SkillCardList.find(c => c.uuid === this.card_uuid) ?? CardPoolList.find(c => c.uuid === this.card_uuid); - const card_lv = Math.max(1, Math.floor(this.cardData.card_lv ?? 1)); - const spSuffix = card_lv >= 2 ? "★".repeat(card_lv - 1) : ""; - this.setLabel(this.name_node, `${spSuffix}${skillCard?.name || skill?.name || ""}${spSuffix}`); - } else { - if (this.lvl_node) this.lvl_node.node.active = false; - // 动态升级卡:显示对应英雄名 + "升级"后缀(注意:升级卡绑定的是 target_hero_eid 而非 uuid, - // 此轻量列表场景通常不展示升级卡;为保持渲染健壮性,回退到本卡 uuid 查 HeroInfo) - if (this.card_type === CardType.SpecialUpgrade && this.cardData.target_hero_eid) { - const targetHero = HeroInfo[this.card_uuid]; - this.setLabel(this.name_node, `${targetHero?.name || ""} 升级`); - } else { - const specialCard = this.card_type === CardType.SpecialUpgrade - ? SpecialUpgradeCardList[this.card_uuid] - : SpecialRefreshCardList[this.card_uuid]; - const card_lv = Math.max(1, Math.floor(this.cardData.card_lv ?? 1)); - const spSuffix = card_lv >= 2 ? "★".repeat(card_lv - 1) : ""; - this.setLabel(this.name_node, `${spSuffix}${specialCard?.name || ""}${spSuffix}`); - } + this.lvl_node.string = `Lv.${heroLv}`; + this.lvl_node.node.active = true; } + // 费用:英雄卡不显示 if (this.cost_node) { - this.cost_node.active = true; - const numNode = this.cost_node.getChildByName("num"); - if (numNode) { - this.setLabel(numNode, `${this.card_cost}`); - } + this.cost_node.active = false; } + // 图标:idle 动画首帧静态展示(镜像翻转,原图朝右,卡面需朝左) if (this.icon_node) { - const iconNode = this.icon_node; - if (this.card_type === CardType.Hero) { - iconNode.setScale(new Vec3(-1.5, 1.5, 1)); - this.updateHeroAnimation(iconNode, this.card_uuid, this.iconVisualToken); - return; - } - iconNode.setScale(new Vec3(1, 1, 1)); - const iconId = this.resolveCardIconId(this.card_type, this.card_uuid); - if (iconId) { - this.updateIcon(iconNode, iconId); - } else { - const sprite = iconNode?.getComponent(Sprite) || iconNode?.getComponentInChildren(Sprite); - if (sprite) sprite.spriteFrame = null; - } + this.updateHeroAnimation(this.icon_node, this.card_uuid, this.iconVisualToken); } } /** - * 渲染空槽状态:清空名称、费用、等级、图标等。 + * 渲染空槽状态:清空名称、等级、背景、图标等。 */ private applyEmptyUI() { this.iconVisualToken += 1; this.setLabel(this.name_node, ""); if (this.cost_node) { - const numNode = this.cost_node.getChildByName("num"); - if (numNode) { - this.setLabel(numNode, ""); - } this.cost_node.active = false; } if (this.lvl_node) this.lvl_node.node.active = false; @@ -305,7 +195,6 @@ export class CardLiteComp extends CCComp { this.BG_node.children.forEach(child => child.active = false); } if (this.icon_node) { - this.icon_node.setScale(new Vec3(1, 1, 1)); const sprite = this.icon_node?.getComponent(Sprite) || this.icon_node?.getComponentInChildren(Sprite); if (sprite) sprite.spriteFrame = null; } @@ -337,33 +226,7 @@ export class CardLiteComp extends CCComp { } /** - * 更新图标:从全局缓存图集中获取对应帧。 - */ - private updateIcon(node: Node, iconId: string) { - if (!node || !iconId) return; - const sprite = node.getComponent(Sprite) || node.getComponentInChildren(Sprite); - if (!sprite) return; - if (smc.uiconsAtlas) { - const frame = smc.uiconsAtlas.getSpriteFrame(iconId); - sprite.spriteFrame = frame || null; - } - } - - /** - * 根据卡牌类型和 UUID 解析出图标 ID(在 SpriteAtlas 中的帧名)。 - */ - private resolveCardIconId(type: CardType, uuid: number): string { - if (type === CardType.Skill) { - return SkillSet[uuid]?.icon || `${uuid}`; - } - if (type === CardType.Hero) { - return HeroInfo[uuid]?.icon || `${uuid}`; - } - return `${uuid}`; - } - - /** - * 为英雄图标加载 idle 动画剪辑,提取首帧 SpriteFrame 静止展示(参考 CHeroComp)。 + * 为英雄图标加载 idle 动画剪辑,提取首帧 SpriteFrame 静止展示。 * 使用 token 做竞态保护,确保异步回调时不会覆盖已更新的图标。 */ private updateHeroAnimation(node: Node, uuid: number, token: number) {