/** * @file ItemListComp.ts * @description 商店列表项组件(UI 视图层 + 购买逻辑) * * 职责: * 1. 渲染单个商品/装备卡的显示信息(名称、等级、属性词条、图标)。 * 2. 处理购买点击 → 扣费 → 根据卡牌类型派发对应事件触发购买效果。 * * 支持类型: * - 装备卡(trigger_type=Field)→ 派发 UseEquipCard,由 MissEquipComp 处理。 * - 商品/药品卡(kind=Potion)→ 派发 UseItemCard,由 MissSkillsComp 处理(一次性 buff)。 * * 设计说明: * 本组件同时服务于装备商店和商品商店,通过 cardData 的 trigger_type / kind 自动判断类型。 */ import { mLogger } from "../common/Logger"; import { _decorator, Node, Sprite, Label, NodeEventType } 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, CardTriggerType, CKind } from "../common/config/CardSet"; import { EquipPoolList } from "../common/config/EquipSet"; import { FieldSkillSet, SkillSet } from "../common/config/SkillSet"; import { oops } from "db://oops-framework/core/Oops"; import { GameEvent } from "../common/config/GameEvent"; import { smc } from "../common/SingletonModuleComp"; import { MissionEconomy } from "./MissionEconomy"; const { ccclass, property } = _decorator; /** * ItemListComp —— 商店列表项(装备/商品通用) * * 由 MissionCardComp.populateEquipments() / populateShopItems() 实例化。 * 以图标 + 名称 + 描述/属性词条 + 购买按钮的形式呈现。 */ @ccclass('ItemListComp') @ecs.register('ItemListComp', false) export class ItemListComp extends CCComp { private debugMode: boolean = false; // ======================== 编辑器绑定节点 ======================== /** 商品图标节点 */ @property({ type: Node }) private icon_node: Node = null; /** 背景节点 */ @property({ type: Node }) private bg_node: Node = null; /** 商品名称 */ @property(Label) private name_label: Label = null; /** 描述/属性词条 1 */ @property(Label) private fied1: Label = null; /** 描述/属性词条 2 */ @property(Label) private fied2: Label = null; /** 描述/属性词条 3 */ @property(Label) private fied3: Label = null; /** 等级标签 */ @property(Label) private level_label: Label = null; /** 购买按钮节点 */ @property({ type: Node }) private buy_node: Node = null; // ======================== 运行时状态 ======================== /** 当前商品的卡牌配置 */ private cardData: CardConfig | null = null; /** 是否已购买 */ private isPurchased: boolean = false; // ======================== 生命周期 ======================== onLoad() { this.buy_node?.on(NodeEventType.TOUCH_END, this.onBuyClick, this); } onDestroy() { super.onDestroy(); if (this.buy_node && this.buy_node.isValid) { this.buy_node.off(NodeEventType.TOUCH_END, this.onBuyClick, this); } } // ======================== 数据初始化 ======================== /** * 初始化商品卡数据并渲染 UI * * @param data 商品/装备卡配置 */ applyCardData(data: CardConfig): void { if (!data) return; this.cardData = data; this.isPurchased = false; this.updateUI(); } // ======================== UI 渲染 ======================== updateUI() { if (!this.cardData) return; const isEquip = this.cardData.trigger_type === CardTriggerType.Field; const isPotion = this.cardData.kind === CKind.Potion; // 名称:装备从 EquipPoolList 查找,商品直接用 cardData.name if (this.name_label) { if (isEquip) { const equipConfig = EquipPoolList.find(c => c.uuid === this.cardData!.uuid); this.name_label.string = equipConfig?.name || this.cardData.name || ""; } else { this.name_label.string = this.cardData.name || ""; } } // 等级标签(★ 表示卡牌等级) if (this.level_label) { const lv = this.cardData.card_lv ?? 1; this.level_label.string = lv >= 2 ? "★".repeat(lv - 1) : ""; } // 描述/属性词条(最多显示 3 条) const fieldLabels = [this.fied1, this.fied2, this.fied3]; if (isEquip) { // 装备:显示 field 词条 const fields = this.cardData.field || []; for (let i = 0; i < fieldLabels.length; i++) { if (!fieldLabels[i]) continue; if (i < fields.length) { const fieldConfig = FieldSkillSet[fields[i]]; fieldLabels[i].string = fieldConfig?.info || ""; fieldLabels[i].node.active = true; } else { fieldLabels[i].string = ""; fieldLabels[i].node.active = false; } } } else if (isPotion) { // 商品:显示技能描述 + info const skillConfig = this.cardData.skill ? SkillSet[this.cardData.skill] : null; const skillInfo = skillConfig?.info || ""; const cardInfo = this.cardData.info || ""; if (fieldLabels[0]) { fieldLabels[0].string = cardInfo; fieldLabels[0].node.active = !!cardInfo; } if (fieldLabels[1]) { fieldLabels[1].string = skillInfo; fieldLabels[1].node.active = !!skillInfo && skillInfo !== cardInfo; } if (fieldLabels[2]) { fieldLabels[2].string = ""; fieldLabels[2].node.active = false; } } else { // 其他类型:显示 info for (let i = 0; i < fieldLabels.length; i++) { if (!fieldLabels[i]) continue; fieldLabels[i].string = i === 0 ? (this.cardData.info || "") : ""; fieldLabels[i].node.active = i === 0 && !!this.cardData.info; } } // 图标:装备取第一个 field 词条图标,商品取技能图标 if (this.icon_node) { let iconId = ""; if (isEquip) { const fields = this.cardData.field || []; if (fields.length > 0) { iconId = FieldSkillSet[fields[0]]?.icon || ""; } } else if (isPotion && this.cardData.skill) { iconId = SkillSet[this.cardData.skill]?.icon || ""; } if (iconId) { this.updateIcon(this.icon_node, iconId); } } // 购买按钮费用显示 if (this.buy_node) { this.buy_node.active = true; const costLabel = this.buy_node.getChildByName("num")?.getComponent(Label) || this.buy_node.getComponentInChildren(Label); if (costLabel) { costLabel.string = `${this.cardData.cost ?? 0}`; } } } /** 更新图标精灵 */ 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; } } // ======================== 购买逻辑 ======================== /** * 购买点击处理: * 1. 扣除金币(失败则提示并中止)。 * 2. 根据卡牌类型派发对应事件: * - 装备卡(trigger_type=Field)→ UseEquipCard → MissEquipComp * - 商品/药品卡(kind=Potion)→ UseItemCard → MissSkillsComp(一次性 buff 技能) * 3. 标记已购买,隐藏购买按钮。 */ private onBuyClick() { if (!this.cardData || this.isPurchased) return; oops.audio.playEffect("music/button"); // 扣费 const cost = this.cardData.cost ?? 0; const success = MissionEconomy.spendCoin(cost); if (!success) { oops.message.dispatchEvent(GameEvent.ShowSmallTip, "buy_coin"); mLogger.log(this.debugMode, "ItemListComp", "purchase failed: not enough coin", { uuid: this.cardData.uuid, cost }); return; } // 根据类型派发对应事件 const isEquip = this.cardData.trigger_type === CardTriggerType.Field; const isPotion = this.cardData.kind === CKind.Potion; if (isEquip) { // 装备:被动驻场,由 MissEquipComp 处理 oops.message.dispatchEvent(GameEvent.UseEquipCard, this.cardData); mLogger.log(this.debugMode, "ItemListComp", "equipment purchased", { uuid: this.cardData.uuid, cost }); } else if (isPotion) { // 商品/药品:一次性 buff 技能,由 MissSkillsComp 处理 oops.message.dispatchEvent(GameEvent.UseItemCard, this.cardData); mLogger.log(this.debugMode, "ItemListComp", "item purchased", { uuid: this.cardData.uuid, skill: this.cardData.skill, cost }); } else { // 默认走技能卡流程 oops.message.dispatchEvent(GameEvent.UseSkillCard, this.cardData); mLogger.log(this.debugMode, "ItemListComp", "card purchased", { uuid: this.cardData.uuid, cost }); } // 标记已购买 this.isPurchased = true; if (this.buy_node) { this.buy_node.active = false; } } /** 标记为已购买状态(用于跨波次保持购买记录) */ setPurchased(): void { this.isPurchased = true; if (this.buy_node) { this.buy_node.active = false; } } /** ECS 组件移除时销毁节点 */ reset() { if (this.node && this.node.isValid) { this.node.destroy(); } } }