//********************* // create by 流云 // time: Sat Aug 15 2026 // desc: 每日商店槽位已满 — 选择替换 //********************* import { _decorator, EventTouch, instantiate, Label, Layout, Node, v3 } from 'cc'; import { Auto_PopReplaceSkill } from './Auto_PopReplaceSkill'; import { DailyShopDataManager } from '../../manager/DailyShopDataManager'; import { DailyShopParsed, DailyShopSlotEntry } from '../../manager/DailyShopTypes'; import { DailyShopLotteryIcon } from '../PopDailyShopLottery/DailyShopLotteryIcon'; const { ccclass } = _decorator; export type PopReplaceSkillParam = { goodsId: number; /** 确认替换:传入选中要卸下的携带 id,购买逻辑由调用方处理 */ onConfirm?: (replaceOwnedId: number) => void; /** 放弃替换:清除待替换订单并刷新界面 */ onCancel?: () => void; }; @ccclass('UIPopReplaceSkill') export class UIPopReplaceSkill extends Auto_PopReplaceSkill { /** 列表项选中时 quality / icon 相对预制缩放的倍率 */ private static readonly ITEM_SELECT_SCALE = 1.1; private _goodsId = 0; private _newGoods: DailyShopParsed | null = null; private _entries: DailyShopSlotEntry[] = []; private _selectedIndex = 0; private _listNodes: Node[] = []; private _onConfirm: ((replaceOwnedId: number) => void) | null = null; private _onCancel: (() => void) | null = null; onLoad(): void { super.onLoad(); if (this.ui_item?.isValid) this.ui_item.active = false; } onInit(): void { const p = (this.prema ?? {}) as PopReplaceSkillParam; this._goodsId = Number(p.goodsId) || 0; this._onConfirm = typeof p.onConfirm === 'function' ? p.onConfirm : null; this._onCancel = typeof p.onCancel === 'function' ? p.onCancel : null; this._selectedIndex = 0; this._listNodes = []; this._newGoods = DailyShopDataManager.getParsed(this._goodsId); if (!this._newGoods) { this.close(2); return; } this._entries = DailyShopDataManager.getOccupiedSlotEntries(this._newGoods.kind); if (this._entries.length <= 0) { this.close(2); return; } if (this.ui_getQuality?.isValid) { this._bindNewGoods(this.ui_getQuality, this._newGoods); } this._renderList(); this._refreshSelectedDesc(); this.showOpenCenterEff(); } /** 绑定待替换的新道具 ui_getQuality */ private _bindNewGoods(root: Node, parsed: DailyShopParsed): void { DailyShopLotteryIcon.bindReplaceNewItem(root, parsed); this._setNameLabel(root.getChildByName('name'), parsed); this._setLabel(root.getChildByName('desc'), DailyShopLotteryIcon.battleBuffDesc(parsed)); } private _renderList(): void { if (!this.ui_content?.isValid || !this.ui_item?.isValid) return; this.ui_content.removeAllChildren(); this._listNodes = []; for (let i = 0; i < this._entries.length; i++) { const item = instantiate(this.ui_item); item.active = true; item.setPosition(v3(0, 0, 0)); item.parent = this.ui_content; this._bindListItem(item, this._entries[i], i); this._listNodes.push(item); } this.ui_content.getComponent(Layout)?.updateLayout(); } private _bindListItem(item: Node, entry: DailyShopSlotEntry, listIndex: number): void { const parsed = entry.parsed; DailyShopLotteryIcon.bindReplaceListItem(item, parsed); this._setNameLabel(item.getChildByName('name'), parsed); this._cacheItemScaleBase(item); this._updateItemSelect(item, listIndex === this._selectedIndex); item['replaceListIndex'] = listIndex; item.targetOff(Node.EventType.TOUCH_END); item.on(Node.EventType.TOUCH_END, this._onClickListItem, this); } /** 缓存列表项 quality / icon 预制缩放,供选中态倍率计算 */ private _cacheItemScaleBase(item: Node): void { const quality = item.getChildByName('quality'); const icon = item.getChildByName('icon') ?? quality?.getChildByName('icon'); item['replaceItemScaleBase'] = { quality: quality?.isValid ? v3(quality.scale.x, quality.scale.y, quality.scale.z) : null, icon: icon?.isValid ? v3(icon.scale.x, icon.scale.y, icon.scale.z) : null, }; } private _updateItemSelect(item: Node, selected: boolean): void { const select = item.getChildByName('select'); if (select?.isValid) select.active = selected; const selectGou = item.getChildByName('selectGou'); if (selectGou?.isValid) selectGou.active = selected; const mul = selected ? UIPopReplaceSkill.ITEM_SELECT_SCALE : 1; this._setItemQualityIconScale(item, mul); } /** 仅缩放 quality / icon,其他节点保持不变 */ private _setItemQualityIconScale(item: Node, mul: number): void { const base = item['replaceItemScaleBase'] as { quality: { x: number; y: number; z: number } | null; icon: { x: number; y: number; z: number } | null; } | undefined; const quality = item.getChildByName('quality'); if (quality?.isValid && base?.quality) { quality.setScale(v3(base.quality.x * mul, base.quality.y * mul, base.quality.z)); } const icon = item.getChildByName('icon') ?? quality?.getChildByName('icon'); if (icon?.isValid && base?.icon) { icon.setScale(v3(base.icon.x * mul, base.icon.y * mul, base.icon.z)); } } private _refreshSelection(): void { for (let i = 0; i < this._listNodes.length; i++) { this._updateItemSelect(this._listNodes[i], i === this._selectedIndex); } this._refreshSelectedDesc(); } /** 面板 ui_desc 显示当前选中携带道具描述 */ private _refreshSelectedDesc(): void { const entry = this._entries[this._selectedIndex]; const desc = entry ? DailyShopLotteryIcon.battleBuffDesc(entry.parsed) : ''; this._setLabel(this.ui_desc, desc); } private _setLabel(node: Node | null | undefined, text: string): void { if (!node?.isValid) return; const lb = node.getComponent(Label); if (lb) lb.string = text; } /** 名称:文本 + 品质描边 */ private _setNameLabel(node: Node | null | undefined, parsed: DailyShopParsed): void { if (!node?.isValid) return; const lb = node.getComponent(Label); if (!lb) return; lb.string = parsed.name; DailyShopLotteryIcon.applyNameOutline(lb, parsed.quality); } private _onClickListItem(event: EventTouch): void { const item = event.currentTarget as Node; const idx = Number(item?.['replaceListIndex']) || 0; if (idx === this._selectedIndex) return; this._selectedIndex = idx; this._refreshSelection(); } click_btnOkReplace(): void { const entry = this._entries[this._selectedIndex]; if (!entry) return; this._onConfirm?.(entry.id); this.close(2); } click_btnCancelReplace(): void { DailyShopDataManager.cancelPendingReplace(); this._onCancel?.(); this.close(2); } /** 点击遮罩不关闭弹窗 */ click_bgMask(): void { } }