import { _decorator, Component, EventTouch, Label, Node, Sprite, Vec2 } from 'cc'; import { SpriteLoad } from 'db://assets/mx/components/SpriteLoad'; import { GBundle, UIID } from '../game/ConfigRes'; const { ccclass } = _decorator; const LOG = '[GemGrid]'; /** 超过此位移视为拖拽,不弹信息 */ const TAP_THRESHOLD = 18; /** * 宝石格子:仓库 / 获得弹窗共用。 * bg=宝石框大{quality};icon=Weapon.icon;lbNum=数量。 * 轻点打开 PopGemInfo(拖拽不触发)。 */ @ccclass('GemGrid') export class GemGrid extends Component { /** 是否响应轻点打开信息 */ canClick = true; /** 品质框 */ bg: Node = null!; /** 宝石图标 */ icon: Node = null!; /** 数量 */ lbNum: Label = null!; /** 当前宝石配表 */ data: ITableWeapon | null = null; private _pressing = false; private _pressStart = new Vec2(); protected onLoad(): void { this._bindNodes(); } protected onEnable(): void { this.node.on(Node.EventType.TOUCH_START, this._onTouchStart, this); this.node.on(Node.EventType.TOUCH_END, this._onTouchEnd, this); this.node.on(Node.EventType.TOUCH_CANCEL, this._onTouchCancel, this); } protected onDisable(): void { this.node.off(Node.EventType.TOUCH_START, this._onTouchStart, this); this.node.off(Node.EventType.TOUCH_END, this._onTouchEnd, this); this.node.off(Node.EventType.TOUCH_CANCEL, this._onTouchCancel, this); this._pressing = false; } /** * 打开宝石信息弹窗。 * @param gemId Weapon.id */ static openInfo(gemId: number): void { const id = Math.floor(Number(gemId) || 0); if (id <= 0) return; void gg.ui.openPanel(UIID.PopGemInfo, { data: id }); } /** * 绑定展示数据。 * @param data Weapon 行;null 表示空 * @param count 数量;传负数隐藏数量文案 */ setData(data: ITableWeapon | null, count = 0): void { this._bindNodes(); this.data = data; const quality = data ? Math.floor(Number(data.quality) || 0) : 0; if (!data) { this._setSprite(this.bg, '', false); this._setSprite(this.icon, '', true); if (this.icon?.isValid) this.icon.active = false; } else { if (quality < 1 || quality > 5) console.warn(LOG, '品质无对应宝石框大', data.id, quality); this._setSprite(this.bg, `weapon/宝石框大${quality}`, false); const iconName = String(data.icon || '').trim(); if (!iconName) console.warn(LOG, 'icon 缺失', data.id); this._setSprite(this.icon, iconName ? `weapon/${iconName}` : '', true); if (this.icon?.isValid) this.icon.active = !!iconName; } if (this.lbNum?.isValid) { const showNum = count >= 0; this.lbNum.node.active = showNum; if (showNum) this.lbNum.string = `${Math.max(0, count | 0)}`; } } /** 按下 */ private _onTouchStart(e: EventTouch): void { this._pressing = true; this._pressStart.set(e.getUILocation()); } /** 抬起:位移未超阈值则打开信息 */ private _onTouchEnd(e: EventTouch): void { if (!this._pressing) return; this._pressing = false; if (!this.canClick || !this.data) return; const loc = e.getUILocation(); const dx = loc.x - this._pressStart.x; const dy = loc.y - this._pressStart.y; if (dx * dx + dy * dy >= TAP_THRESHOLD * TAP_THRESHOLD) return; GemGrid.openInfo(Number(this.data.id) || 0); } /** 取消按下 */ private _onTouchCancel(): void { this._pressing = false; } /** 绑定子节点 */ private _bindNodes(): void { if (!this.bg?.isValid) this.bg = this.node.getChildByName('bg')!; if (!this.icon?.isValid) this.icon = this.node.getChildByName('icon')!; if (!this.lbNum?.isValid) { const n = this.node.getChildByName('lbNum'); this.lbNum = n?.getComponent(Label) ?? null!; } if (!this.bg) console.warn(LOG, 'bg 节点缺失', this.node?.name); } /** * icons/weapon 下图。 * @param nativeSize true=按贴图原始尺寸(不拉伸);false=铺满节点 UITransform */ private _setSprite(node: Node | null, path: string, nativeSize: boolean): void { if (!node?.isValid) return; if (!path) { node.active = false; node.getComponent(SpriteLoad)?.clear(); const sp = node.getComponent(Sprite); if (sp) sp.spriteFrame = null; return; } node.active = true; if (!node.getComponent(Sprite)) node.addComponent(Sprite); const sp = node.getComponent(Sprite)!; sp.type = Sprite.Type.SIMPLE; // icon 用 TRIMMED 跟贴图尺寸,避免 CUSTOM 拉伸变形 sp.sizeMode = nativeSize ? Sprite.SizeMode.TRIMMED : Sprite.SizeMode.CUSTOM; node.getOrAddComponent(SpriteLoad).setSprite(path, GBundle.Items, () => { if (!node.isValid || !sp.isValid) return; sp.sizeMode = nativeSize ? Sprite.SizeMode.TRIMMED : Sprite.SizeMode.CUSTOM; }); } }