import NumberToImage from "../Script/NumberToImage"; const { ccclass, property } = cc._decorator; /** 仅编辑器预览使用。通过真实 Prefab 的视图接口模拟,不调用正式 Runtime。 */ @ccclass export default class TestSeven extends cc.Component { @property(cc.Prefab) rewardPrefab: cc.Prefab = null; @property(cc.SpriteAtlas) rewardFont: cc.SpriteAtlas = null; private previousFont: any = null; private gift: cc.Node = null; private view: any = null; private info: any = null; private status: cc.Label = null; private busy = false; private failNext = false; start() { if (!CC_PREVIEW) { cc.error("[testSeven] 仅允许编辑器预览,请从构建中排除 testSeven。"); return; } this.status = this.label("正在加载 seven_day_gift…", 0, 850, 26); if (!this.rewardPrefab || !this.rewardFont) return this.report("测试场景缺少奖励弹窗或数字图集引用"); this.previousFont = NumberToImage.font; NumberToImage.font = this.rewardFont; cc.assetManager.loadBundle("seven_day_gift", (error, bundle) => { if (!cc.isValid(this.node, true)) return; if (error) return this.report("分包加载失败:" + error.message); bundle.load("prefab/sevenDayGift", cc.Prefab, (loadError, prefab: cc.Prefab) => { if (!cc.isValid(this.node, true)) return; if (loadError) return this.report("Prefab 加载失败:" + loadError.message); this.gift = cc.instantiate(prefab); this.node.addChild(this.gift); this.gift.setPosition(0, 0); this.view = this.gift.getComponent("SevenDayGift"); if (!this.view) return this.report("缺少 SevenDayGift 组件"); this.view.setCloseHandler(() => this.report("已关闭,可选择状态重新打开")); // 控制面板在遮罩上方,使用与活动一致的 1080 × 1920 设计尺寸。 for (let day = 1; day <= 7; day++) { this.button("第" + day + "天", (day - 4) * 140, -670, () => this.select(day)); } this.button("今日已领", -330, -755, () => this.select(this.info.nextRewardDay, "claimed")); this.button("全部完成", -110, -755, () => this.select(7, "completed")); this.button("已过期", 110, -755, () => this.select(3, "expired")); this.button("自动领取", 330, -755, () => this.open(true)); this.button("下次失败", -220, -840, () => { this.failNext = true; this.report("下一次领取将在 1.2 秒后失败,可再次点击重试"); }); this.button("重新打开", 0, -840, () => this.open(false)); this.button("重置", 220, -840, () => { this.failNext = false; this.select(1); }); this.select(1); }); }); } private select(day: number, state: string = "active") { if (this.busy) return; const claimedCount = state === "completed" ? 7 : state === "claimed" ? day : day - 1; const items = [ [{ type: "coin", count: 100 }], [{ type: "hammer", count: 2 }], [{ type: "freeze", count: 2 }, { type: "coin", count: 200 }], [{ type: "magic_wand", count: 2 }], [{ type: "infinite_health", count: 30 }], [{ type: "coin", count: 300 }, { type: "hammer", count: 2 }, { type: "freeze", count: 2 }], [{ type: "cat_skin", count: 1, itemId: 1 }], ]; this.info = { serverNow: Date.now(), activityId: "testSeven-memory", status: state === "claimed" ? "active" : state, triggerLevel: 23, triggerReached: true, startAt: Date.now() - 86400000, endAt: Date.now() + (state === "expired" ? -1 : 7 * 86400000), claimedCount, todayClaimed: state === "claimed" || state === "completed", canClaim: state === "active", nextRewardDay: day, rewards: items.map((reward, index) => ({ day: index + 1, items: reward, claimed: index < claimedCount })), }; this.open(false); } private open(autoClaim: boolean) { if (this.busy || !this.view || !this.info) return; this.bindData(); this.view.show(autoClaim); this.report("本地模拟 | 第" + this.info.nextRewardDay + "天 | " + this.info.status + " | 已领 " + this.info.claimedCount + "/7"); } private bindData() { this.view.setData(this.info, (done: (success: boolean) => void) => { if (this.busy || !this.info.canClaim) return done(false); this.busy = true; this.report("模拟领取中…请检查重复点击和关闭拦截"); this.scheduleOnce(() => { this.busy = false; if (this.failNext) { this.failNext = false; done(false); return this.report("模拟领取失败,可点击当前奖励重试"); } const reward = this.info.rewards[this.info.nextRewardDay - 1]; reward.claimed = true; this.info.claimedCount++; this.info.todayClaimed = true; this.info.canClaim = false; if (this.info.claimedCount === 7) this.info.status = "completed"; // 更新视图仍沿用本地回调,不调用 initializeRuntime。 this.bindData(); done(true); this.showRewardWindow(reward.items); this.report("模拟领取成功(不写入资产):" + reward.items.map(item => item.type + " × " + item.count).join(" / ")); }, 1.2); }); } private showRewardWindow(items: any[]) { const node = cc.instantiate(this.rewardPrefab); this.node.addChild(node); node.setPosition(0, 0); node.zIndex = 1001; const reward = node.getComponent("Reward"); if (!reward) { node.destroy(); this.report("奖励弹窗缺少 Reward 组件"); return; } this.busy = true; reward.init(items.map(item => ({ type: item.type === "magic_wand" ? "magic" : item.type, count: Math.max(0, Number(item.count !== undefined ? item.count : item.amount) || 0), spriteFrame: item.type === "cat_skin" ? this.view.catArt : null, })).filter(item => item.count > 0), () => { this.busy = false; }); } onDestroy() { if (CC_PREVIEW && NumberToImage.font === this.rewardFont) NumberToImage.font = this.previousFont; } private report(message: string) { this.status.string = message; } private label(text: string, x: number, y: number, size: number): cc.Label { const node = new cc.Node("testSevenLabel"); this.node.addChild(node); node.setPosition(x, y); node.zIndex = 1000; const label = node.addComponent(cc.Label); label.string = text; label.fontSize = size; label.lineHeight = size + 6; return label; } private button(text: string, x: number, y: number, action: () => void) { const label = this.label(text, x, y, 28); label.node.color = cc.color(110, 225, 255); label.node.on(cc.Node.EventType.TOUCH_END, (event: cc.Event) => { event.stopPropagation(); if (!this.busy) action(); }); } }