diff --git a/assets/Script/Avatar.ts b/assets/Script/Avatar.ts index a35f1a3e..66de45e7 100644 --- a/assets/Script/Avatar.ts +++ b/assets/Script/Avatar.ts @@ -118,11 +118,13 @@ export default class Avatar extends cc.Component { this.content.children[this.avatarData[j] - 4].getChildByName("lock").active = false; } - if (this.catArr.length > 0) { - for (let k = 0; k < this.catArr.length; k++) { - this.content4.children[this.catArr[k] - 1].getChildByName("lock").active = false; - } - } + // 按节点名匹配,避免新增猫 ID 越界;复用窗口时重新计算锁状态。 + const ownedCats = [1, 2, 3].concat(this.catArr.map(id => Number(id))); + this.content4.children.forEach(cat => { + const id = Number(cat.name.replace("cat_", "")); + const lock = cat.getChildByName("lock"); + if (lock) lock.active = ownedCats.indexOf(id) < 0; + }); } @@ -420,11 +422,15 @@ export default class Avatar extends cc.Component { //改变头像框 changeCat(event, catData) { + const cat = this.content4.getChildByName(catData); + if (!cat) { cc.warn("[Avatar] 缺少猫形象节点", catData); return; } + const lock = cat.getChildByName("lock"); + if (event && lock && lock.active) return; this.closeCatKuang(); this.avatarCat = catData; console.log("谁应该为true111", catData); console.log(this.content4.children); - this.content4.getChildByName(catData).getChildByName("select").active = true; + cat.getChildByName("select").active = true; this.changeBtnState("kuang"); } diff --git a/assets/Script/JiaZai.ts b/assets/Script/JiaZai.ts index 621563e7..b941ff5b 100644 --- a/assets/Script/JiaZai.ts +++ b/assets/Script/JiaZai.ts @@ -7,6 +7,8 @@ import MapConroler from "./Map"; +import { DRAWABLE_CAT_IDS, getOwnedDrawableCats, getCatDrawCost } from "./module/Tool/CatDrawRules"; +import HomePopupQueue from "../home_popup_queue/HomePopupQueue"; import NumberToImage from "./NumberToImage"; import { MiniGameSdk } from "./Sdk/MiniGameSdk"; import { LQCollideSystem } from "./lq_collide_system/lq_collide_system"; @@ -1050,23 +1052,12 @@ export default class JiaZai extends cc.Component { if (!redNode) { return; } - const drawableCatIds = [4, 5, 6, 7, 8, 9, 10, 11]; - const filmConsumeTable = [5, 10, 25, 35, 55, 70, 100, 120]; const catArr = Array.isArray(cc.fx.GameConfig.GM_INFO.catArr) ? cc.fx.GameConfig.GM_INFO.catArr : []; - const ownedCatIds: { [catId: number]: boolean } = {}; - - for (let i = 0; i < catArr.length; i++) { - const catId = Number(catArr[i]); - if (drawableCatIds.indexOf(catId) !== -1) { - ownedCatIds[catId] = true; - } - } - - const ownedCount = Object.keys(ownedCatIds).length; - const allCatsOwned = ownedCount >= drawableCatIds.length; - const filmCost = filmConsumeTable[Math.min(ownedCount, filmConsumeTable.length - 1)]; + const ownedCount = getOwnedDrawableCats(catArr).length; + const allCatsOwned = ownedCount >= DRAWABLE_CAT_IDS.length; + const filmCost = getCatDrawCost(ownedCount); const currentFilm = Number(cc.fx.GameConfig.GM_INFO.film) || 0; // drawCat 在 film < filmCost 时判定不足,因此相等时也可以抽取。 @@ -2721,7 +2712,25 @@ export default class JiaZai extends cc.Component { // - customEventData:自定义事件数据 // ============================================ // 打开通行证弹窗 - openStarter_pack(event?: cc.Event, customEventData?: string) { + openStarter_pack(event?: cc.Event, customEventData?: string, fromQueue: boolean = false) { + if (!event && customEventData === undefined && !fromQueue) { + HomePopupQueue.get(this.node).enqueue("starterPack", 40, done => { + const show = () => { + if (!cc.isValid(this.node, true)) { done(); return; } + try { this.openStarter_pack(undefined, undefined, true); } finally { done(); } + }; + if (JiaZai.cachedActionPrefab) { show(); return; } + cc.assetManager.loadBundle("action_bundle", (error, bundle) => { + if (error) { cc.error(error); done(); return; } + bundle.load("prefab/newbieGift", cc.Prefab, (loadError, prefab: cc.Prefab) => { + if (loadError) { cc.error(loadError); done(); return; } + JiaZai.cachedActionPrefab = prefab; + show(); + }); + }); + }); + return; + } this.vibrateButtonClick(event); if (!JiaZai.cachedActionPrefab) { @@ -3841,6 +3850,7 @@ export default class JiaZai extends cc.Component { console.log("未到期已领取"); } if (data.code == 1) { + HomePopupQueue.get(this.node).enqueue("monthlyReward", 20, done => { this.getcard.active = true; this.getcard.zIndex = 999; // 外层 getcard 整体弹入动画(三次回弹) @@ -3869,6 +3879,8 @@ export default class JiaZai extends cc.Component { compensate: false } cc.fx.GameTool.shushu_Track("resource_get", dataTemp); + done(); + }); } }) @@ -3901,8 +3913,11 @@ export default class JiaZai extends cc.Component { if (cachedDateStr !== dateStr) { this.winStreakShow = false; // 更新缓存 - cc.fx.StorageMessage.setStorage("monthlyCardDate", dateStr); - this.openMonthCard(); + HomePopupQueue.get(this.node).enqueue("monthlyCard", 30, done => { + this.openMonthCard(); + if (this.monthlyCardNode && this.monthlyCardNode.active) cc.fx.StorageMessage.setStorage("monthlyCardDate", dateStr); + done(); + }); } else { this.openWin(); @@ -4316,7 +4331,25 @@ export default class JiaZai extends cc.Component { // 功能:创建并显示连胜活动预制体 // 何时调用:点击连胜活动按钮时调用 // ============================================ - openWinStreak(event?: cc.Event, customEventData?: string) { + openWinStreak(event?: cc.Event, customEventData?: string, fromQueue: boolean = false) { + if (!event && customEventData === undefined && !fromQueue) { + HomePopupQueue.get(this.node).enqueue("winStreak", 50, done => { + const show = () => { + if (!cc.isValid(this.node, true)) { done(); return; } + try { this.openWinStreak(undefined, undefined, true); } finally { done(); } + }; + if (JiaZai.cachedWinStreakPrefab) { show(); return; } + cc.assetManager.loadBundle("action_bundle", (error, bundle) => { + if (error) { cc.error(error); done(); return; } + bundle.load("prefab/winStreak", cc.Prefab, (loadError, prefab: cc.Prefab) => { + if (loadError) { cc.error(loadError); done(); return; } + JiaZai.cachedWinStreakPrefab = prefab; + show(); + }); + }); + }); + return; + } this.vibrateButtonClick(event); let eventName = ""; let btnName = "" diff --git a/assets/Script/WinCat.ts b/assets/Script/WinCat.ts index df142cf9..6b93c79e 100644 --- a/assets/Script/WinCat.ts +++ b/assets/Script/WinCat.ts @@ -12,6 +12,7 @@ export default class WinCat extends cc.Component { @property(cc.SpriteAtlas) catUI: cc.SpriteAtlas = null; + @property(cc.SpriteFrame) cat12: cc.SpriteFrame = null; start() { this.refreshUserCat(); @@ -30,7 +31,7 @@ export default class WinCat extends cc.Component { const useravatarCat = cc.fx.GameConfig.GM_INFO.useravatarCat; const catNumber = String(useravatarCat || "").match(/\d+/); const frameName = catNumber ? "cat" + parseInt(catNumber[0], 10) : "cat1"; - const spriteFrame = this.catUI.getSpriteFrame(frameName) + const spriteFrame = (frameName === "cat12" ? this.cat12 : this.catUI.getSpriteFrame(frameName)) || this.catUI.getSpriteFrame("cat1"); if (!spriteFrame) { @@ -57,6 +58,7 @@ export default class WinCat extends cc.Component { cc.warn("WinCat: 找不到猫节点或 Sprite", nodePath); return; } + sprite.sizeMode = cc.Sprite.SizeMode.CUSTOM; sprite.spriteFrame = spriteFrame; } } diff --git a/assets/Script/module/Tool/CatDrawRules.ts b/assets/Script/module/Tool/CatDrawRules.ts new file mode 100644 index 00000000..740a75d9 --- /dev/null +++ b/assets/Script/module/Tool/CatDrawRules.ts @@ -0,0 +1,13 @@ +/** 普通抽猫池;签到等活动猫不参与费用、进度和集齐判断。 */ +export const DRAWABLE_CAT_IDS: ReadonlyArray = [4, 5, 6, 7, 8, 9, 10, 11]; +const FILM_COSTS = [5, 10, 25, 35, 55, 70, 100, 120]; + +export function getOwnedDrawableCats(catArr: any): number[] { + const owned = Array.isArray(catArr) ? catArr.map(id => Number(id)) : []; + return DRAWABLE_CAT_IDS.filter(id => owned.indexOf(id) !== -1); +} + +/** 集齐时保留最后一档展示费用;是否可抽由拥有数量单独判断。 */ +export function getCatDrawCost(ownedCount: number): number { + return FILM_COSTS[Math.min(Math.max(0, ownedCount), FILM_COSTS.length - 1)]; +} diff --git a/assets/Script/module/Tool/CatDrawRules.ts.meta b/assets/Script/module/Tool/CatDrawRules.ts.meta new file mode 100644 index 00000000..3e9d9cb8 --- /dev/null +++ b/assets/Script/module/Tool/CatDrawRules.ts.meta @@ -0,0 +1,10 @@ +{ + "ver": "1.1.0", + "uuid": "4d23795e-6b93-46f0-8ef0-1e0cb8e4f1a6", + "importer": "typescript", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/assets/Script/seven_day_gift/SevenDayGiftBootstrapApi.ts b/assets/Script/seven_day_gift/SevenDayGiftBootstrapApi.ts index 70234062..af8841f1 100644 --- a/assets/Script/seven_day_gift/SevenDayGiftBootstrapApi.ts +++ b/assets/Script/seven_day_gift/SevenDayGiftBootstrapApi.ts @@ -1,5 +1,6 @@ import Utils from "../module/Pay/Utils"; +// 主包用于判断是否展示的活动快照,具体奖励由分包解析。 export interface SevenDayActivityInfo { serverNow: string | number; activityId: string; @@ -17,15 +18,17 @@ export interface SevenDayActivityInfo { interface SevenDayInfoResponse { code: number; - data: SevenDayActivityInfo; + data: SevenDayActivityInfo | false; msg?: string; } +/** 七日活动查询入口,与分包内的资产同步接口分开。 */ export default class SevenDayGiftBootstrapApi { + // 主包只查询活动信息,不提前加载活动资源。 public static getInfo(callback: (response: SevenDayInfoResponse) => void) { const endpoint = "signInActivityInfo"; const params = { uid: this.getUid() }; - let completed = false; + let completed = false; // 超时后到达的响应不再处理。 const timeoutHandle = setTimeout(() => { if (completed) return; @@ -35,6 +38,7 @@ export default class SevenDayGiftBootstrapApi { }, 5500); Utils.POST(endpoint, params, response => { + console.log("[signInActivityInfo] 回复" + (completed ? "(已超时或重复,不再处理)" : ""), JSON.stringify(response, null, 2)); if (completed) return; completed = true; clearTimeout(timeoutHandle); @@ -42,6 +46,7 @@ export default class SevenDayGiftBootstrapApi { }); } + // 优先使用本地登录 UID,并同步到运行时账号信息。 public static getUid(): string { const storedUid = cc.fx.StorageMessage.getStorage("uid"); if (storedUid !== undefined && storedUid !== null && storedUid !== "") { diff --git a/assets/Script/seven_day_gift/SevenDayGiftHost.ts b/assets/Script/seven_day_gift/SevenDayGiftHost.ts index 9aab1d9e..9d231e66 100644 --- a/assets/Script/seven_day_gift/SevenDayGiftHost.ts +++ b/assets/Script/seven_day_gift/SevenDayGiftHost.ts @@ -1,41 +1,79 @@ import SevenDayGiftBootstrapApi, { SevenDayActivityInfo } from "./SevenDayGiftBootstrapApi"; +import HomePopupQueue from "../../home_popup_queue/HomePopupQueue"; const { ccclass } = cc._decorator; const AUTO_OPEN_KEY = "seven_day_gift_auto_open_v1"; const BUNDLE_NAME = "seven_day_gift"; const GIFT_PREFAB_PATH = "prefab/sevenDayGift"; +// 主包通过接口调用分包,避免静态引入活动脚本和资源。 interface SevenDayGiftView { - initializeRuntime(info: SevenDayActivityInfo, closeHandler: () => void): void; + initializeRuntime(info: SevenDayActivityInfo, closeHandler: () => void): boolean; updateRuntimeInfo(info: SevenDayActivityInfo): void; - show(autoClaim?: boolean): void; + show(autoClaim?: boolean): boolean; } +/** 主页入口:查询活动、安排自动展示、管理分包生命周期。 */ @ccclass export default class SevenDayGiftHost extends cc.Component { private giftNode: cc.Node = null; private giftView: SevenDayGiftView = null; private giftBundle: cc.AssetManager.Bundle = null; private giftLoading = false; - private giftLoadId = 0; + private giftLoadId = 0; // 加载批次号,用于忽略过期回调。 private giftLoadCallbacks: Array<(error?: Error) => void> = []; private info: SevenDayActivityInfo = null; private querying = false; private retryCount = 0; + private testPanelNode: cc.Node = null; + // 监听回到前台事件,并在正式环境查询活动。 onLoad() { if (this.node.parent) this.node.setContentSize(this.node.parent.getContentSize()); cc.game.on(cc.game.EVENT_SHOW, this.onGameShow, this); + const gameTool = cc.fx && cc.fx.GameTool; + if (CC_PREVIEW || (gameTool && gameTool.getWechatGameVersion() === "开发版")) this.loadTestPanel(); if (CC_PREVIEW) return; this.queryActivity(true); } + // 移除监听,同时清理弹窗和分包资源。 onDestroy() { + if (this.testPanelNode && cc.isValid(this.testPanelNode)) this.testPanelNode.destroy(); cc.game.off(cc.game.EVENT_SHOW, this.onGameShow, this); this.releaseGiftAssets(true); } + // 开发环境按需加载接口测试面板。 + private loadTestPanel() { + // 主包不静态引用测试脚本;发布构建可以直接排除 testSeven。 + cc.assetManager.loadBundle("testSeven", error => { + if (!cc.isValid(this.node, true)) return; + if (error) { + cc.warn("[SevenDayGiftHost] 测试面板未加载(需要包含 testSeven Bundle)", error); + return; + } + const canvas = cc.find("Canvas"); + if (!canvas) return; + const node = new cc.Node("SevenApiTestPanel"); + canvas.addChild(node); + node.setPosition(0, 0); + node.zIndex = 3000; + this.testPanelNode = node; + const panel: any = node.addComponent("SevenApiPanel"); + panel.testHome = (info: SevenDayActivityInfo) => { + if (this.giftLoading || (this.giftNode && this.giftNode.active)) { + cc.warn("[SevenDayGiftHost] 请先关闭当前七日活动,再测试自动弹窗"); + return; + } + this.info = info; + if (this.shouldAutoOpen()) this.openGift(true); + }; + }); + } + + // 等待登录 UID 就绪后查询,避免并发重复请求。 private queryActivity(allowAutoOpen: boolean) { if (this.querying) return; if (!SevenDayGiftBootstrapApi.getUid()) { @@ -60,8 +98,18 @@ export default class SevenDayGiftHost extends cc.Component { }); } + // 把七日活动加入主页弹窗队列。 private openGift(autoClaim: boolean) { + // 保留旧参数名;现在表示自动展示来源,不会自动领取。 + const canvas = cc.find("Canvas"); + if (!canvas) return; + HomePopupQueue.get(canvas).enqueue("sevenDayGift", 10, done => this.openQueuedGift(autoClaim, done)); + } + + // 轮到本活动时加载并显示,成功显示后才记录当天已弹。 + private openQueuedGift(autoClaim: boolean, done: () => void) { this.ensureGiftLoaded((error?: Error) => { + done(); if (error) { cc.error("[SevenDayGiftHost] 七日活动分包加载失败", error); return; @@ -69,17 +117,23 @@ export default class SevenDayGiftHost extends cc.Component { if (!this.info || !this.giftView) return; if (autoClaim && !this.shouldAutoOpen()) return; - this.initializeGiftView(); + if (!this.initializeGiftView() || !this.giftView.show(autoClaim)) { + cc.error("[SevenDayGiftHost] 弹窗初始化或显示失败,未记录当天已弹"); + this.releaseGiftAssets(); + return; + } if (autoClaim) this.markAutoOpened(); - this.giftView.show(autoClaim); + console.log("[SevenDayGiftHost] 弹窗已打开", { autoClaim }); }); } - private initializeGiftView() { - if (!this.giftView) return; - this.giftView.initializeRuntime(this.info, () => this.releaseGiftAssets()); + // 把活动快照和关闭后的资源清理回调交给分包。 + private initializeGiftView(): boolean { + if (!this.giftView) return false; + return this.giftView.initializeRuntime(this.info, () => this.releaseGiftAssets()); } + // 复用已加载视图;多个加载请求共用一次分包加载。 private ensureGiftLoaded(callback: (error?: Error) => void) { if (this.giftView && this.giftNode && cc.isValid(this.giftNode)) { callback(); @@ -145,12 +199,14 @@ export default class SevenDayGiftHost extends cc.Component { }); } + // 统一通知本次等待加载的调用方。 private finishGiftLoad(error?: Error) { this.giftLoading = false; const callbacks = this.giftLoadCallbacks.splice(0); callbacks.forEach(callback => callback(error)); } + // 使旧加载回调失效,并在节点销毁后释放资源。 private releaseGiftAssets(immediate: boolean = false) { this.giftLoadId++; this.giftLoadCallbacks.length = 0; @@ -176,6 +232,7 @@ export default class SevenDayGiftHost extends cc.Component { else this.scheduleOnce(release, 0); } + // 仅释放当前仍登记在资源管理器中的活动分包。 private releaseBundle(bundle: cc.AssetManager.Bundle) { if (!bundle || cc.assetManager.getBundle(BUNDLE_NAME) !== bundle) return; bundle.releaseAll(); @@ -184,6 +241,7 @@ export default class SevenDayGiftHost extends cc.Component { } } + // 根据触发条件、活动状态和服务端时间判断是否可展示。 private isActivityVisible(): boolean { if (!this.info || !this.info.activityId || !this.info.triggerReached) return false; if (this.info.status === "expired" || this.info.status === "completed") return false; @@ -192,28 +250,44 @@ export default class SevenDayGiftHost extends cc.Component { return !serverNow || !endAt || serverNow < endAt; } + // 今天可领且当前账号当天未弹出过,才自动展示。 private shouldAutoOpen(): boolean { - if (!this.isActivityVisible() || !this.info.canClaim) return false; - return cc.fx.StorageMessage.getStorage(this.getAutoOpenStorageKey()) !== this.getServerDayKey(); + if (!this.isActivityVisible() || !this.info.canClaim || this.info.todayClaimed) { + console.log("[SevenDayGiftHost] 跳过弹窗:活动不可见或今天不可领取"); + return false; + } + const key = this.getAutoOpenStorageKey(); + const recordedDay = cc.fx.StorageMessage.getStorage(key); + const serverDay = this.getServerDayKey(); + const shouldOpen = recordedDay !== serverDay; + console.log("[SevenDayGiftHost] 自动弹窗判断", JSON.stringify({ key, recordedDay, serverDay, shouldOpen })); + return shouldOpen; } + // 只记录已展示,不代表奖励已经领取。 private markAutoOpened() { cc.fx.StorageMessage.setStorage(this.getAutoOpenStorageKey(), this.getServerDayKey()); } + // 按账号和活动隔离自动弹窗记录。 private getAutoOpenStorageKey(): string { return AUTO_OPEN_KEY + ":" + SevenDayGiftBootstrapApi.getUid() + ":" + this.info.activityId; } + // 按北京时间划分服务端日期,避免设备时区影响。 private getServerDayKey(): string { const time = this.toTime(this.info.serverNow) || Date.now(); return Math.floor((time + 8 * 60 * 60 * 1000) / (24 * 60 * 60 * 1000)).toString(); } + // 回到前台时刷新活动;正在展示时继续使用原快照。 private onGameShow() { + // 弹窗正在使用本次查询结果,切前后台不重新查询或重复展示。 + if (this.giftLoading || (this.giftNode && cc.isValid(this.giftNode) && this.giftNode.active)) return; if (!CC_PREVIEW) this.queryActivity(true); } + // 兼容秒、毫秒时间戳和日期字符串。 private toTime(value: string | number): number { if (typeof value === "number") return value < 100000000000 ? value * 1000 : value; if (!value) return 0; diff --git a/assets/seven_day_gift/texture/cat.png b/assets/UI/Cat/cat.png similarity index 100% rename from assets/seven_day_gift/texture/cat.png rename to assets/UI/Cat/cat.png diff --git a/assets/seven_day_gift/texture/cat.png.meta b/assets/UI/Cat/cat.png.meta similarity index 100% rename from assets/seven_day_gift/texture/cat.png.meta rename to assets/UI/Cat/cat.png.meta diff --git a/assets/action_bundle/prefab/Avatar.prefab b/assets/action_bundle/prefab/Avatar.prefab index 8adb821a..b6cf3b38 100644 --- a/assets/action_bundle/prefab/Avatar.prefab +++ b/assets/action_bundle/prefab/Avatar.prefab @@ -26445,6 +26445,9 @@ }, { "__id__": 893 + }, + { + "__id__": 1265 } ], "_active": true, @@ -48643,5 +48646,809 @@ }, "fileId": "", "sync": false + }, + { + "__type__": "cc.Node", + "_name": "cat_12", + "_objFlags": 0, + "_parent": { + "__id__": 682 + }, + "_children": [ + { + "__id__": 1266 + }, + { + "__id__": 1269 + }, + { + "__id__": 1272 + }, + { + "__id__": 1278 + } + ], + "_active": true, + "_components": [ + { + "__id__": 1283 + } + ], + "_prefab": { + "__id__": 1285 + }, + "_opacity": 255, + "_color": { + "__type__": "cc.Color", + "r": 255, + "g": 255, + "b": 255, + "a": 255 + }, + "_contentSize": { + "__type__": "cc.Size", + "width": 200, + "height": 200 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.5, + "y": 0.5 + }, + "_trs": { + "__type__": "TypedArray", + "ctor": "Float64Array", + "array": [ + 300, + -490.497, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0 + ] + }, + "_eulerAngles": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_skewX": 0, + "_skewY": 0, + "_is3DNode": false, + "_groupIndex": 0, + "groupIndex": 0, + "_id": "" + }, + { + "__type__": "cc.Node", + "_name": "kuang_6", + "_objFlags": 0, + "_parent": { + "__id__": 1265 + }, + "_children": [], + "_active": true, + "_components": [ + { + "__id__": 1267 + } + ], + "_prefab": { + "__id__": 1268 + }, + "_opacity": 255, + "_color": { + "__type__": "cc.Color", + "r": 255, + "g": 255, + "b": 255, + "a": 255 + }, + "_contentSize": { + "__type__": "cc.Size", + "width": 160, + "height": 160 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.5, + "y": 0.5 + }, + "_trs": { + "__type__": "TypedArray", + "ctor": "Float64Array", + "array": [ + 0, + 5.39, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1 + ] + }, + "_eulerAngles": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_skewX": 0, + "_skewY": 0, + "_is3DNode": false, + "_groupIndex": 0, + "groupIndex": 0, + "_id": "" + }, + { + "__type__": "cc.Sprite", + "_name": "", + "_objFlags": 0, + "node": { + "__id__": 1266 + }, + "_enabled": true, + "_materials": [ + { + "__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432" + } + ], + "_srcBlendFactor": 770, + "_dstBlendFactor": 771, + "_spriteFrame": { + "__uuid__": "c2b141f2-de71-4481-b9ee-440c893c28b3" + }, + "_type": 0, + "_sizeMode": 1, + "_fillType": 0, + "_fillCenter": { + "__type__": "cc.Vec2", + "x": 0, + "y": 0 + }, + "_fillStart": 0, + "_fillRange": 0, + "_isTrimmedMode": true, + "_atlas": { + "__uuid__": "fd85df88-648f-4407-bab2-da4f15478fb9" + }, + "_id": "" + }, + { + "__type__": "cc.PrefabInfo", + "root": { + "__id__": 1 + }, + "asset": { + "__id__": 0 + }, + "fileId": "9306dfbb-8c2b-4c19-a890-649041d0fac9", + "sync": false + }, + { + "__type__": "cc.Node", + "_name": "icon", + "_objFlags": 0, + "_parent": { + "__id__": 1265 + }, + "_children": [], + "_active": true, + "_components": [ + { + "__id__": 1270 + } + ], + "_prefab": { + "__id__": 1271 + }, + "_opacity": 255, + "_color": { + "__type__": "cc.Color", + "r": 255, + "g": 255, + "b": 255, + "a": 255 + }, + "_contentSize": { + "__type__": "cc.Size", + "width": 186, + "height": 173 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.5, + "y": 0.5 + }, + "_trs": { + "__type__": "TypedArray", + "ctor": "Float64Array", + "array": [ + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0.7526881720430108, + 0.7526881720430108, + 1 + ] + }, + "_eulerAngles": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_skewX": 0, + "_skewY": 0, + "_is3DNode": false, + "_groupIndex": 0, + "groupIndex": 0, + "_id": "" + }, + { + "__type__": "cc.Sprite", + "_name": "", + "_objFlags": 0, + "node": { + "__id__": 1269 + }, + "_enabled": true, + "_materials": [ + { + "__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432" + } + ], + "_srcBlendFactor": 770, + "_dstBlendFactor": 771, + "_spriteFrame": { + "__uuid__": "7d090003-0209-4a00-8b00-000000000209" + }, + "_type": 0, + "_sizeMode": 1, + "_fillType": 0, + "_fillCenter": { + "__type__": "cc.Vec2", + "x": 0, + "y": 0 + }, + "_fillStart": 0, + "_fillRange": 0, + "_isTrimmedMode": true, + "_atlas": null, + "_id": "" + }, + { + "__type__": "cc.PrefabInfo", + "root": { + "__id__": 1 + }, + "asset": { + "__id__": 0 + }, + "fileId": "ddfcf83a-5420-4550-a7ab-0a21c8809fd3", + "sync": false + }, + { + "__type__": "cc.Node", + "_name": "select", + "_objFlags": 0, + "_parent": { + "__id__": 1265 + }, + "_children": [ + { + "__id__": 1273 + } + ], + "_active": false, + "_components": [ + { + "__id__": 1276 + } + ], + "_prefab": { + "__id__": 1277 + }, + "_opacity": 255, + "_color": { + "__type__": "cc.Color", + "r": 255, + "g": 255, + "b": 255, + "a": 255 + }, + "_contentSize": { + "__type__": "cc.Size", + "width": 176, + "height": 176 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.5, + "y": 0.5 + }, + "_trs": { + "__type__": "TypedArray", + "ctor": "Float64Array", + "array": [ + 0, + 5.6, + 0, + 0, + 0, + 0, + 1, + 0.9, + 0.9, + 1 + ] + }, + "_eulerAngles": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_skewX": 0, + "_skewY": 0, + "_is3DNode": false, + "_groupIndex": 0, + "groupIndex": 0, + "_id": "" + }, + { + "__type__": "cc.Node", + "_name": "New Sprite", + "_objFlags": 0, + "_parent": { + "__id__": 1272 + }, + "_children": [], + "_active": true, + "_components": [ + { + "__id__": 1274 + } + ], + "_prefab": { + "__id__": 1275 + }, + "_opacity": 255, + "_color": { + "__type__": "cc.Color", + "r": 255, + "g": 255, + "b": 255, + "a": 255 + }, + "_contentSize": { + "__type__": "cc.Size", + "width": 66, + "height": 66 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.5, + "y": 0.5 + }, + "_trs": { + "__type__": "TypedArray", + "ctor": "Float64Array", + "array": [ + 60, + -60, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1 + ] + }, + "_eulerAngles": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_skewX": 0, + "_skewY": 0, + "_is3DNode": false, + "_groupIndex": 0, + "groupIndex": 0, + "_id": "" + }, + { + "__type__": "cc.Sprite", + "_name": "", + "_objFlags": 0, + "node": { + "__id__": 1273 + }, + "_enabled": true, + "_materials": [ + { + "__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432" + } + ], + "_srcBlendFactor": 770, + "_dstBlendFactor": 771, + "_spriteFrame": { + "__uuid__": "7453479c-fcfb-4019-b1a9-c81ca044b911" + }, + "_type": 0, + "_sizeMode": 1, + "_fillType": 0, + "_fillCenter": { + "__type__": "cc.Vec2", + "x": 0, + "y": 0 + }, + "_fillStart": 0, + "_fillRange": 0, + "_isTrimmedMode": true, + "_atlas": { + "__uuid__": "fd85df88-648f-4407-bab2-da4f15478fb9" + }, + "_id": "" + }, + { + "__type__": "cc.PrefabInfo", + "root": { + "__id__": 1 + }, + "asset": { + "__id__": 0 + }, + "fileId": "1a937bd3-c177-4e84-967c-cb109a00beb8", + "sync": false + }, + { + "__type__": "cc.Sprite", + "_name": "", + "_objFlags": 0, + "node": { + "__id__": 1272 + }, + "_enabled": true, + "_materials": [ + { + "__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432" + } + ], + "_srcBlendFactor": 770, + "_dstBlendFactor": 771, + "_spriteFrame": { + "__uuid__": "949ab4be-d9b5-4dda-a22c-26b8356d1324" + }, + "_type": 0, + "_sizeMode": 1, + "_fillType": 0, + "_fillCenter": { + "__type__": "cc.Vec2", + "x": 0, + "y": 0 + }, + "_fillStart": 0, + "_fillRange": 0, + "_isTrimmedMode": true, + "_atlas": { + "__uuid__": "fd85df88-648f-4407-bab2-da4f15478fb9" + }, + "_id": "" + }, + { + "__type__": "cc.PrefabInfo", + "root": { + "__id__": 1 + }, + "asset": { + "__id__": 0 + }, + "fileId": "738777dc-7255-4aef-9e5e-b052515739ce", + "sync": false + }, + { + "__type__": "cc.Node", + "_name": "lock", + "_objFlags": 0, + "_parent": { + "__id__": 1265 + }, + "_children": [], + "_active": true, + "_components": [ + { + "__id__": 1279 + }, + { + "__id__": 1280 + } + ], + "_prefab": { + "__id__": 1282 + }, + "_opacity": 255, + "_color": { + "__type__": "cc.Color", + "r": 255, + "g": 255, + "b": 255, + "a": 255 + }, + "_contentSize": { + "__type__": "cc.Size", + "width": 160, + "height": 160 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.5, + "y": 0.5 + }, + "_trs": { + "__type__": "TypedArray", + "ctor": "Float64Array", + "array": [ + 0, + 5.897, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0 + ] + }, + "_eulerAngles": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_skewX": 0, + "_skewY": 0, + "_is3DNode": false, + "_groupIndex": 0, + "groupIndex": 0, + "_id": "" + }, + { + "__type__": "cc.Sprite", + "_name": "", + "_objFlags": 0, + "node": { + "__id__": 1278 + }, + "_enabled": true, + "_materials": [ + { + "__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432" + } + ], + "_srcBlendFactor": 770, + "_dstBlendFactor": 771, + "_spriteFrame": { + "__uuid__": "eea1ebab-83ca-474e-bdf5-bc03e7794897" + }, + "_type": 0, + "_sizeMode": 1, + "_fillType": 0, + "_fillCenter": { + "__type__": "cc.Vec2", + "x": 0, + "y": 0 + }, + "_fillStart": 0, + "_fillRange": 0, + "_isTrimmedMode": true, + "_atlas": null, + "_id": "" + }, + { + "__type__": "cc.Button", + "_name": "", + "_objFlags": 0, + "node": { + "__id__": 1278 + }, + "_enabled": true, + "_normalMaterial": null, + "_grayMaterial": null, + "duration": 0.1, + "zoomScale": 1.1, + "clickEvents": [ + { + "__id__": 1281 + } + ], + "_N$interactable": true, + "_N$enableAutoGrayEffect": false, + "_N$transition": 3, + "transition": 3, + "_N$normalColor": { + "__type__": "cc.Color", + "r": 255, + "g": 255, + "b": 255, + "a": 255 + }, + "_N$pressedColor": { + "__type__": "cc.Color", + "r": 211, + "g": 211, + "b": 211, + "a": 255 + }, + "pressedColor": { + "__type__": "cc.Color", + "r": 211, + "g": 211, + "b": 211, + "a": 255 + }, + "_N$hoverColor": { + "__type__": "cc.Color", + "r": 255, + "g": 255, + "b": 255, + "a": 255 + }, + "hoverColor": { + "__type__": "cc.Color", + "r": 255, + "g": 255, + "b": 255, + "a": 255 + }, + "_N$disabledColor": { + "__type__": "cc.Color", + "r": 124, + "g": 124, + "b": 124, + "a": 255 + }, + "_N$normalSprite": null, + "_N$pressedSprite": null, + "pressedSprite": null, + "_N$hoverSprite": null, + "hoverSprite": null, + "_N$disabledSprite": null, + "_N$target": { + "__id__": 1278 + }, + "_id": "" + }, + { + "__type__": "cc.ClickEvent", + "target": { + "__id__": 1 + }, + "component": "", + "_componentId": "7589aoelbpHs5EJQkTTKE6y", + "handler": "openGacha", + "customEventData": "" + }, + { + "__type__": "cc.PrefabInfo", + "root": { + "__id__": 1 + }, + "asset": { + "__id__": 0 + }, + "fileId": "846c69a5-b6f2-4557-aac0-cb8296acfcb7", + "sync": false + }, + { + "__type__": "cc.Button", + "_name": "", + "_objFlags": 0, + "node": { + "__id__": 1265 + }, + "_enabled": true, + "_normalMaterial": null, + "_grayMaterial": null, + "duration": 0.1, + "zoomScale": 1.05, + "clickEvents": [ + { + "__id__": 1284 + } + ], + "_N$interactable": true, + "_N$enableAutoGrayEffect": false, + "_N$transition": 3, + "transition": 3, + "_N$normalColor": { + "__type__": "cc.Color", + "r": 255, + "g": 255, + "b": 255, + "a": 255 + }, + "_N$pressedColor": { + "__type__": "cc.Color", + "r": 211, + "g": 211, + "b": 211, + "a": 255 + }, + "pressedColor": { + "__type__": "cc.Color", + "r": 211, + "g": 211, + "b": 211, + "a": 255 + }, + "_N$hoverColor": { + "__type__": "cc.Color", + "r": 255, + "g": 255, + "b": 255, + "a": 255 + }, + "hoverColor": { + "__type__": "cc.Color", + "r": 255, + "g": 255, + "b": 255, + "a": 255 + }, + "_N$disabledColor": { + "__type__": "cc.Color", + "r": 124, + "g": 124, + "b": 124, + "a": 255 + }, + "_N$normalSprite": null, + "_N$pressedSprite": null, + "pressedSprite": null, + "_N$hoverSprite": null, + "hoverSprite": null, + "_N$disabledSprite": null, + "_N$target": { + "__id__": 1265 + }, + "_id": "" + }, + { + "__type__": "cc.ClickEvent", + "target": { + "__id__": 1 + }, + "component": "", + "_componentId": "7589aoelbpHs5EJQkTTKE6y", + "handler": "changeCat", + "customEventData": "cat_12" + }, + { + "__type__": "cc.PrefabInfo", + "root": { + "__id__": 1 + }, + "asset": { + "__id__": 0 + }, + "fileId": "ccd7ddb0-74b3-4f3a-8661-1cfc8b0257bf", + "sync": false } -] \ No newline at end of file +] diff --git a/assets/career/prefab/rankGame.prefab b/assets/career/prefab/rankGame.prefab index ee210b8d..ea1d2d38 100644 --- a/assets/career/prefab/rankGame.prefab +++ b/assets/career/prefab/rankGame.prefab @@ -30819,7 +30819,10 @@ "__uuid__": "09d077ae-9fce-4041-8f84-7273d7767ef0" }, "iconPrefab": null, - "_id": "" + "_id": "", + "cat12": { + "__uuid__": "7d090003-0209-4a00-8b00-000000000209" + } }, { "__type__": "cc.PrefabInfo", @@ -30999,7 +31002,10 @@ "catUI": { "__uuid__": "09d077ae-9fce-4041-8f84-7273d7767ef0" }, - "_id": "" + "_id": "", + "cat12": { + "__uuid__": "7d090003-0209-4a00-8b00-000000000209" + } }, { "__type__": "cc.PrefabInfo", @@ -31169,4 +31175,4 @@ "fileId": "", "sync": false } -] \ No newline at end of file +] diff --git a/assets/career/script/CareerItem2.ts b/assets/career/script/CareerItem2.ts index 69e852ea..c229380f 100644 --- a/assets/career/script/CareerItem2.ts +++ b/assets/career/script/CareerItem2.ts @@ -23,6 +23,7 @@ export default class CareerItem2 extends cc.Component { randerChildren: any; @property(cc.SpriteAtlas) catUI: cc.SpriteAtlas = null; + @property(cc.SpriteFrame) cat12: cc.SpriteFrame = null; @property(cc.Prefab) @@ -183,9 +184,10 @@ export default class CareerItem2 extends cc.Component { // this.node.children[i].getChildByName("kong").active = false; // this.node.children[i].getChildByName("name").getComponent(cc.Label).string = username + ""; if (this.data.rankingData[i - 8].userCat != undefined && this.data.rankingData[i - 8].userCat != "" && this.data.rankingData[i - 8].userCat != null) { - if (parseInt(this.data.rankingData[i - 8].userCat) < 12 || parseInt(this.data.rankingData[i - 8].userCat) > 0) { + if (parseInt(this.data.rankingData[i - 8].userCat) <= 12 && parseInt(this.data.rankingData[i - 8].userCat) > 0) { child.getChildByName("icon").active = true; - child.getChildByName("icon").getComponent(cc.Sprite).spriteFrame = this.catUI.getSpriteFrame("cat" + this.data.rankingData[i - 8].userCat); + child.getChildByName("icon").getComponent(cc.Sprite).sizeMode = cc.Sprite.SizeMode.CUSTOM; + child.getChildByName("icon").getComponent(cc.Sprite).spriteFrame = (Number(this.data.rankingData[i - 8].userCat) === 12 ? this.cat12 : this.catUI.getSpriteFrame("cat" + this.data.rankingData[i - 8].userCat)); } } if (child.getChildByName("level")) diff --git a/assets/career/script/CareerList2.ts b/assets/career/script/CareerList2.ts index 14c0e143..ab83c254 100644 --- a/assets/career/script/CareerList2.ts +++ b/assets/career/script/CareerList2.ts @@ -138,6 +138,7 @@ export default class CareerList2 extends cc.Component { @property(cc.SpriteAtlas) catUI: cc.SpriteAtlas = null; + @property(cc.SpriteFrame) cat12: cc.SpriteFrame = null; JiaZai: any; onLoad() { @@ -521,7 +522,8 @@ export default class CareerList2 extends cc.Component { } if (this.topData[i - 3].userCat != "" && this.topData[i - 3].userCat != null && this.topData[i - 3].userCat != undefined) { - this.firstRender.children[i].getChildByName("icon").getComponent(cc.Sprite).spriteFrame = this.catUI.getSpriteFrame("cat" + this.topData[i - 3].userCat); + this.firstRender.children[i].getChildByName("icon").getComponent(cc.Sprite).sizeMode = cc.Sprite.SizeMode.CUSTOM; + this.firstRender.children[i].getChildByName("icon").getComponent(cc.Sprite).spriteFrame = (Number(this.topData[i - 3].userCat) === 12 ? this.cat12 : this.catUI.getSpriteFrame("cat" + this.topData[i - 3].userCat)); } diff --git a/assets/gacha_bundle/script/GachaManager.ts b/assets/gacha_bundle/script/GachaManager.ts index d2fc3cee..905edd2e 100644 --- a/assets/gacha_bundle/script/GachaManager.ts +++ b/assets/gacha_bundle/script/GachaManager.ts @@ -1,6 +1,7 @@ import JiaZai from "../../Script/JiaZai"; import LoadingCatAnimation from "../../Script/LoadingCatAnimation"; import Utils from "../../Script/module/Pay/Utils"; +import { getOwnedDrawableCats, getCatDrawCost } from "../../Script/module/Tool/CatDrawRules"; import { MiniGameManager } from "../../Script/Sdk/MiniGameManager"; import { MiniGameSdk } from "../../Script/Sdk/MiniGameSdk"; const { ccclass, property } = cc._decorator; @@ -77,11 +78,8 @@ export default class GachaManager extends cc.Component { // Gacha is the legacy single-phase avatar layout. this.phase = 1; } - this.catArr = []; - if (catArr) { - this.catArr = catArr; - this.cat = this.catArr.length; - } + this.catArr = getOwnedDrawableCats(catArr); + this.cat = this.catArr.length; console.log("222222初始化数据", data, film, catArr); cc.fx.GameConfig.GM_INFO.film = film; if (data) { @@ -115,11 +113,7 @@ export default class GachaManager extends cc.Component { } getCostFilm2() { - let film = [5, 10, 25, 35, 55, 70, 100, 120]; - if (this.cat > film.length) - this.cat = film.length - 1; - let count = film[this.cat]; - return count; + return getCatDrawCost(this.cat); } closeGacha() { diff --git a/assets/gacha_bundle/img/cat.meta b/assets/home_popup_queue.meta similarity index 81% rename from assets/gacha_bundle/img/cat.meta rename to assets/home_popup_queue.meta index b9b84be5..62656e5d 100644 --- a/assets/gacha_bundle/img/cat.meta +++ b/assets/home_popup_queue.meta @@ -1,6 +1,6 @@ { "ver": "1.1.3", - "uuid": "98e24c51-14ad-4467-8071-506d787492ac", + "uuid": "31e1c01e-12e4-4979-b203-990f06809ac7", "importer": "folder", "isBundle": false, "bundleName": "", diff --git a/assets/home_popup_queue/HomePopupQueue.ts b/assets/home_popup_queue/HomePopupQueue.ts new file mode 100644 index 00000000..40acc549 --- /dev/null +++ b/assets/home_popup_queue/HomePopupQueue.ts @@ -0,0 +1,45 @@ +const { ccclass } = cc._decorator; + +/** HomeScene 共用队列。显示中的活动及其奖励窗口关闭后才推进下一项。 */ +@ccclass +export default class HomePopupQueue extends cc.Component { + private tasks: Array<{ key: string; priority: number; run: (done: () => void) => void }> = []; + private running = ""; + + public static get(canvas: cc.Node): HomePopupQueue { + return canvas.getComponent(HomePopupQueue) || canvas.addComponent(HomePopupQueue); + } + + public enqueue(key: string, priority: number, run: (done: () => void) => void) { + if (this.running === key || this.tasks.some(task => task.key === key)) return; + this.tasks.push({ key, priority, run }); + this.tasks.sort((a, b) => a.priority - b.priority); + } + + update() { + if (this.running || !this.tasks.length || this.hasVisiblePopup()) return; + const task = this.tasks.shift(); + this.running = task.key; + let finished = false; + const done = () => { + if (finished) return; + finished = true; + this.running = ""; + }; + try { task.run(done); } + catch (error) { done(); cc.error("[HomePopupQueue] " + task.key, error); } + } + + private hasVisiblePopup(): boolean { + const visible = (node: cc.Node) => node && cc.isValid(node, true) && node.activeInHierarchy; + if (visible(this.node.getChildByName("Transfer"))) return true; + const seven = this.node.getChildByName("sevenDayGift"); + if (seven && seven.children.some(visible)) return true; + const home: any = this.node.getComponent("JiaZai"); + if (!home) return false; + return ["RewardNode", "getcard", "monthlyCardNode", "actionpNode", "winStreakNode", "passCheckNode", "jungleTreasureNode"] + .some(key => visible(home[key])); + } + + onDestroy() { this.tasks.length = 0; this.running = ""; } +} diff --git a/assets/home_popup_queue/HomePopupQueue.ts.meta b/assets/home_popup_queue/HomePopupQueue.ts.meta new file mode 100644 index 00000000..d2b095f7 --- /dev/null +++ b/assets/home_popup_queue/HomePopupQueue.ts.meta @@ -0,0 +1,10 @@ +{ + "ver": "1.1.0", + "uuid": "771d35b0-8e07-4dc7-9041-5571e2c4bb1a", + "importer": "typescript", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/assets/libs/dn-sdk-minigame.meta b/assets/resources/sevenDayicon.meta similarity index 81% rename from assets/libs/dn-sdk-minigame.meta rename to assets/resources/sevenDayicon.meta index 7cedb056..f1598351 100644 --- a/assets/libs/dn-sdk-minigame.meta +++ b/assets/resources/sevenDayicon.meta @@ -1,6 +1,6 @@ { "ver": "1.1.3", - "uuid": "703537c8-6926-4995-9a30-d6ce343e5c63", + "uuid": "0efaaad9-e500-4563-b67f-78e367829b36", "importer": "folder", "isBundle": false, "bundleName": "", diff --git a/assets/pause/texture.meta b/assets/reward_claim_effect.meta similarity index 81% rename from assets/pause/texture.meta rename to assets/reward_claim_effect.meta index b732d0dc..983570d4 100644 --- a/assets/pause/texture.meta +++ b/assets/reward_claim_effect.meta @@ -1,6 +1,6 @@ { "ver": "1.1.3", - "uuid": "9d9d5f8f-add9-4d2b-a1e4-4729e81a8ce8", + "uuid": "41bec397-a5ee-47c7-ac3c-9bfe604f9143", "importer": "folder", "isBundle": false, "bundleName": "", diff --git a/assets/reward_claim_effect/RewardClaimEffect.ts b/assets/reward_claim_effect/RewardClaimEffect.ts new file mode 100644 index 00000000..04dba1f4 --- /dev/null +++ b/assets/reward_claim_effect/RewardClaimEffect.ts @@ -0,0 +1,135 @@ +// 只传展示数据;来源使用世界坐标,兼容卡片缩放和位移。 +export interface RewardClaimVisual { + icon: cc.SpriteFrame; + count: number; + prefix: "" | "×"; + isTime?: boolean; // 时间原值为秒,展示时换算分钟。 + worldPosition: cc.Vec2; + worldPopPosition: cc.Vec2; // 道具中心上方一个道具高度,已包含节点缩放。 +} + +/** 独立领取效果包,只展示,不发奖、不创建奖励弹窗。 */ +export default class RewardClaimEffect { + // 统一创建奖励展示节点,全部动画结束后清理并通知调用方。 + public static play(parent: cc.Node, rewards: RewardClaimVisual[], done: () => void, numberFrames: cc.SpriteFrame[]) { + const visible = rewards.filter(item => item.count > 0); + if (!visible.length) { done(); return; } + const layer = new cc.Node("rewardClaimEffects"); + parent.addChild(layer); + layer.zIndex = 2000; + const growDuration = 0.12; + const returnDuration = 0.025; + const stagger = growDuration + returnDuration; // 第一次放大并回正后,下一个开始生成。 + const entries = visible.map(item => ({ + item, + source: layer.convertToNodeSpaceAR(item.worldPosition), + position: layer.convertToNodeSpaceAR(item.worldPopPosition), + })); + entries.sort((a, b) => a.source.x - b.source.x); + const rowCenterX = (entries[0].position.x + entries[entries.length - 1].position.x) / 2; + const rowY = Math.max(...entries.map(entry => entry.position.y)) + 24; + // 正常尺寸下图标边缘间隔 10px,整排以原道具区域居中。 + const rowSpacing = 88 + 10; + let remaining = visible.length; // 多奖励全部播完后,只回调一次。 + entries.forEach(({ item }, index) => { + const node = new cc.Node("rewardClaim" + index); + layer.addChild(node); + const icon = new cc.Node("icon"); + node.addChild(icon); + icon.setContentSize(88, 88); + const sprite = icon.addComponent(cc.Sprite); + sprite.sizeMode = cc.Sprite.SizeMode.CUSTOM; + sprite.spriteFrame = item.icon; + // 领取图标保持原图比例,最长边不超过 88px。 + sprite.trim = false; + if (item.icon) { + const original = item.icon.getOriginalSize(); + const scale = 88 / Math.max(original.width, original.height, 1); + icon.setContentSize(original.width * scale, original.height * scale); + } + this.createAmount(node, item, numberFrames, 88, 30, icon); + node.setPosition(cc.v2(rowCenterX + (index - (entries.length - 1) / 2) * rowSpacing, rowY)); + node.scale = 0.2; + node.opacity = 0; + // 两次弹到 1.25 倍并快速回正,中间轻微收缩;停留后再上浮渐隐。 + const animation = cc.sequence( + cc.spawn(cc.fadeIn(0.06), cc.scaleTo(growDuration, 1.25).easing(cc.easeSineOut())), + cc.scaleTo(returnDuration, 1), + cc.scaleTo(0.05, 0.9).easing(cc.easeSineOut()), + cc.scaleTo(0.1, 1.25).easing(cc.easeSineOut()), + cc.scaleTo(0.025, 1), + // 首个等最后一个弹完再停 0.25 秒;各自保留同样等待,按生成顺序起飞。 + cc.delayTime((entries.length - 1) * stagger + 0.25), + cc.spawn(cc.moveBy(0.6, cc.v2(0, 150)).easing(cc.easeSineOut()), cc.fadeOut(0.6)), + ); + node.runAction(cc.sequence( + cc.delayTime(index * stagger), + animation, + cc.callFunc(() => { + node.destroy(); + if (--remaining === 0) { layer.destroy(); done(); } + }), + )); + }); + } + + // 卡片和领取效果共用图片数量;仅显示层换算分钟,不改变发奖数值。 + public static createAmount(parent: cc.Node, item: { count: number; prefix: "" | "×"; isTime?: boolean }, + frames: cc.SpriteFrame[], maxWidth: number = 88, height: number = 30, icon: cc.Node = parent) { + const amount = new cc.Node("amount"); + parent.addChild(amount); + // 按贴图实际可见边缘定位,透明留白不参与数量位置计算。 + const frame = icon.getComponent(cc.Sprite).spriteFrame; + const original = frame && frame.getOriginalSize(); + const rect = frame && frame.getRect(); + const offset = frame && frame.getOffset(); + const bottom = frame ? (offset.y - rect.height / 2) * icon.height / original.height : -icon.height / 2; + const right = frame ? (offset.x + rect.width / 2) * icon.width / original.width : icon.width / 2; + amount.setPosition(0, bottom + 4); + const value = item.isTime ? Number((item.count / 60).toFixed(2)) : item.count; + const symbols = (item.prefix + value).split(""); + const glyphs = symbols.map(char => frames && frames[char === "+" ? 10 : char === "×" ? 11 : Number(char)]); + if (item.isTime) glyphs.push(frames && frames[12]); + // 缺图或小数使用文字兜底,保留完整的数量和单位。 + if (!glyphs.every(Boolean) || !/^×?\d+$/.test(symbols.join(""))) { + const fallback = this.addAmountLabel(amount, item.prefix + value + (item.isTime ? "分钟" : ""), height, 0); + const label = fallback.getComponent(cc.Label); + fallback.setContentSize(maxWidth, height + 6); + label.overflow = cc.Label.Overflow.SHRINK; + if (item.prefix === "×") amount.x = right + 4 - maxWidth / 2; + return; + } + const scale = height / Math.max(...glyphs.map(frame => frame.getOriginalSize().height)); + const widths = glyphs.map(frame => frame.getOriginalSize().width * scale); + const totalWidth = widths.reduce((sum, width) => sum + width, 0) + glyphs.length - 1; + const fit = Math.min(1, maxWidth / totalWidth); + // 金币/时间底边居中,道具乘号数量沿右下角对齐。 + if (item.prefix === "×") amount.x = right + 4 - totalWidth * fit / 2; + let x = -totalWidth / 2; + glyphs.forEach((frame, index) => { + const glyph = new cc.Node("digit" + index); + amount.addChild(glyph); + const sprite = glyph.addComponent(cc.Sprite); + sprite.sizeMode = cc.Sprite.SizeMode.CUSTOM; + sprite.trim = false; + sprite.spriteFrame = frame; + glyph.setContentSize(widths[index] * fit, frame.getOriginalSize().height * scale * fit); + glyph.setPosition((x + widths[index] / 2) * fit, 0); + x += widths[index] + 1; + }); + } + + private static addAmountLabel(parent: cc.Node, value: string, size: number, y: number) { + const node = new cc.Node("amountLabel"); + parent.addChild(node); + node.setPosition(0, y); + const label = node.addComponent(cc.Label); + label.string = value; + label.fontSize = size; + label.lineHeight = size + 4; + const outline = node.addComponent(cc.LabelOutline); + outline.color = cc.color(151, 79, 38); + outline.width = 2; + return node; + } +} diff --git a/assets/reward_claim_effect/RewardClaimEffect.ts.meta b/assets/reward_claim_effect/RewardClaimEffect.ts.meta new file mode 100644 index 00000000..fe1d9e36 --- /dev/null +++ b/assets/reward_claim_effect/RewardClaimEffect.ts.meta @@ -0,0 +1,10 @@ +{ + "ver": "1.1.0", + "uuid": "8f295c11-73f6-43eb-bd87-48993a957d86", + "importer": "typescript", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/assets/seven_day_gift/SevenDayGift.ts b/assets/seven_day_gift/SevenDayGift.ts index 5d513276..c3426584 100644 --- a/assets/seven_day_gift/SevenDayGift.ts +++ b/assets/seven_day_gift/SevenDayGift.ts @@ -1,6 +1,8 @@ import { SevenDayActivityInfo, SevenDayReward, SevenDayRewardItem } from "./SevenDayGiftApi"; import SevenDayGiftRuntime from "./SevenDayGiftRuntime"; import ActivityPopupAnimator, { ActivityPopupAnimationType } from "../Script/ActivityPopupAnimator"; +import RewardClaimEffect from "../reward_claim_effect/RewardClaimEffect"; +import SevenDayHitTest from "./SevenDayHitTest"; const { ccclass, property } = cc._decorator; const TOTAL_DAYS = 7; @@ -8,6 +10,7 @@ const TOTAL_DAYS = 7; type ClaimHandler = (callback: (success: boolean) => void) => void; type CloseHandler = () => void; +/** 七日活动视图:卡片展示、点击判定和领取效果。 */ @ccclass export default class SevenDayGift extends cc.Component { @property(cc.SpriteFrame) cardBlue: cc.SpriteFrame = null; @@ -22,6 +25,8 @@ export default class SevenDayGift extends cc.Component { @property(cc.SpriteFrame) freezeIcon: cc.SpriteFrame = null; @property(cc.SpriteFrame) magicWandIcon: cc.SpriteFrame = null; @property(cc.SpriteFrame) infiniteHealthIcon: cc.SpriteFrame = null; + @property({ type: [cc.SpriteFrame], tooltip: "数量图片:依次为 0~9、加号、乘号、分钟" }) + rewardNumberFrames: cc.SpriteFrame[] = []; @property({ type: cc.Enum(ActivityPopupAnimationType), displayName: "弹窗动画", @@ -32,7 +37,8 @@ export default class SevenDayGift extends cc.Component { private info: SevenDayActivityInfo = null; private cards: cc.Node[] = []; private claimHandler: ClaimHandler = null; - private claiming = false; + private claiming = false; // 等待领取回调,阻止重复提交。 + private effectPlaying = false; // 也覆盖测试面板单独预览效果的情况。 private panel: cc.Node = null; private mask: cc.Node = null; private closeNode: cc.Node = null; @@ -40,6 +46,7 @@ export default class SevenDayGift extends cc.Component { private closeHandler: CloseHandler = null; private runtime: SevenDayGiftRuntime = null; + // 缓存节点、绑定点击,再创建正式领取流程。 onLoad() { if (!this.cacheView()) { this.node.active = false; @@ -49,62 +56,84 @@ export default class SevenDayGift extends cc.Component { this.runtime = new SevenDayGiftRuntime(this); } + // 结束业务流程,忽略后续异步返回。 onDestroy() { if (this.runtime) this.runtime.destroy(); this.runtime = null; } - public initializeRuntime(info: SevenDayActivityInfo, closeHandler: CloseHandler) { - if (this.runtime) this.runtime.initialize(info, closeHandler); + // 主页加载分包后,从这里传入正式活动数据。 + public initializeRuntime(info: SevenDayActivityInfo, closeHandler: CloseHandler): boolean { + // Bundle 节点先以 inactive 挂入场景;激活后 onLoad 才会创建 Runtime。 + this.node.active = true; + if (!this.runtime) { + cc.error("[SevenDayGift] Runtime 未初始化,请检查节点激活和 Prefab 结构"); + return false; + } + this.runtime.initialize(info, closeHandler); + return true; } + // 把新的活动查询结果交给业务层。 public updateRuntimeInfo(info: SevenDayActivityInfo) { if (this.runtime) this.runtime.updateInfo(info); } + // 共用视图入口,正式流程和本地模拟都可提供领取回调。 public setData(info: SevenDayActivityInfo, claimHandler: ClaimHandler) { this.info = info; this.claimHandler = claimHandler; this.refreshView(); } + // 登记弹窗关闭后的处理,由主页负责释放资源。 public setCloseHandler(closeHandler: CloseHandler) { this.closeHandler = closeHandler; } - public show(autoClaim: boolean = false) { - if (!this.info) return; - const shouldAutoClaim = autoClaim && !!this.info.canClaim && !this.info.todayClaimed; - this.setCloseEnabled(!shouldAutoClaim); + // 打开时只展示奖励,等待玩家点击领取。 + public show(_autoClaim: boolean = false): boolean { + if (!this.info || !this.panel) return false; + this.node.active = true; + if (!this.node.activeInHierarchy) return false; + this.setCloseEnabled(true); this.node.active = true; this.refreshView(); this.node.opacity = 255; this.node.scale = 1; - ActivityPopupAnimator.open(this.panel, this.mask, this.popupAnimation, () => { - if (shouldAutoClaim) this.requestCurrentReward(); - }); + ActivityPopupAnimator.open(this.panel, this.mask, this.popupAnimation); + return true; } + // 有奖励时本次只领取;无奖励时才关闭,忙碌期间忽略点击。 public close(event?: cc.Event) { if (event) event.stopPropagation(); - if (!this.closeEnabled || this.claiming) return; + if (!this.closeEnabled) return; + if (this.claiming || this.effectPlaying) return; + if (this.info && this.info.canClaim && !this.info.todayClaimed) { + this.requestCurrentReward(); + return; + } + this.setCloseEnabled(false); ActivityPopupAnimator.close(this.panel, this.mask, this.popupAnimation, () => { this.node.active = false; if (this.closeHandler) this.closeHandler(); }); } + // 按 Prefab 约定找到卡片,关闭按钮始终隐藏。 private cacheView(): boolean { this.panel = this.node.getChildByName("panel"); this.mask = this.node.getChildByName("mask"); this.closeNode = this.panel && this.panel.getChildByName("close"); const cards = this.panel && this.panel.getChildByName("cards"); const message = this.panel && this.panel.getChildByName("message"); - if (!this.panel || !this.mask || !this.closeNode || !cards) { + if (!this.panel || !this.mask || !cards) { cc.error("[SevenDayGift] prefab structure is incomplete"); return false; } if (message) message.active = false; + if (this.closeNode) this.closeNode.active = false; for (let day = 1; day <= TOTAL_DAYS; day++) { const card = cards.getChildByName("giftDay" + day); if (!card) { @@ -116,17 +145,38 @@ export default class SevenDayGift extends cc.Component { return true; } + // 区分可见 UI 与外部点击,奖励卡片单独处理领取。 private bindEvents() { - this.closeNode.on(cc.Node.EventType.TOUCH_END, this.close, this); - this.mask.on(cc.Node.EventType.TOUCH_END, this.close, this); - this.cards.forEach((card: cc.Node, index: number) => { - card.on(cc.Node.EventType.TOUCH_END, (event: cc.Event) => { - event.stopPropagation(); - this.onCardClicked(index + 1); - }, this); - }); + // 所有点击共用一次可见性判断,透明处不会被矩形事件范围挡住。 + const blockVisibleUI = (node: cc.Node) => { + if (node.getComponent(cc.Sprite) || node.getComponent(cc.Label)) { + node.on(cc.Node.EventType.TOUCH_END, this.onPanelTouch, this); + } + node.children.forEach(blockVisibleUI); + }; + blockVisibleUI(this.panel); + this.mask.on(cc.Node.EventType.TOUCH_END, this.onPanelTouch, this); } + // 透明处仅执行本次外部点击;命中奖励子图时沿父节点找到所属卡片。 + private onPanelTouch(event: cc.Event.EventTouch) { + event.stopPropagation(); + let hit = SevenDayHitTest.findVisible(this.panel, event.getLocation()); + if (!hit) { + this.close(); + return; + } + while (hit && hit !== this.panel) { + const index = this.cards.indexOf(hit); + if (index >= 0) { + this.onCardClicked(index + 1); + return; + } + hit = hit.parent; + } + } + + // 按最新活动快照刷新七张卡片。 private refreshView() { if (!this.info || this.cards.length !== TOTAL_DAYS) return; for (let day = 1; day <= TOTAL_DAYS; day++) { @@ -134,6 +184,7 @@ export default class SevenDayGift extends cc.Component { } } + // 设置当天高亮、已领印章和奖励图标。 private refreshCard(card: cc.Node, day: number, reward: SevenDayReward) { const claimed = !!(reward && reward.claimed); const available = !!this.info.canClaim && day === Number(this.info.nextRewardDay); @@ -142,7 +193,7 @@ export default class SevenDayGift extends cc.Component { ? this.claimedOverlay : special ? this.cardSpecial : available ? this.cardYellow : this.cardBlue; this.setSprite(card, cardFrame); - // White is the neutral tint: keep the supplied art's original colors. + // 白色不改变贴图颜色,保留美术资源的原色。 card.color = cc.Color.WHITE; card.opacity = 255; @@ -182,6 +233,7 @@ export default class SevenDayGift extends cc.Component { } } + // 只允许领取当前可领天数,其他卡片点击无效。 private onCardClicked(day: number) { const reward = this.findReward(day); if (reward && reward.claimed) { @@ -195,21 +247,45 @@ export default class SevenDayGift extends cc.Component { this.requestCurrentReward(); } + // 统一卡片和外部点击的领取入口,直到回调结束才解锁。 private requestCurrentReward() { - if (this.claiming || !this.claimHandler || !this.info || !this.info.canClaim) return; + if (this.claiming || this.effectPlaying || !this.claimHandler || !this.info || !this.info.canClaim) return; this.claiming = true; this.claimHandler((success: boolean) => { this.claiming = false; this.setCloseEnabled(true); - if (!success) cc.warn("[SevenDayGift] 自动领取未完成,可点击当天奖励重试"); + if (!success) cc.warn("[SevenDayGift] 领取未完成,可点击当天奖励重试"); }); } + // 控制是否允许关闭,不再显示关闭按钮。 private setCloseEnabled(enabled: boolean) { this.closeEnabled = enabled; - if (this.closeNode) this.closeNode.active = enabled; + if (this.closeNode) this.closeNode.active = false; } + // 从对应道具位置生成图标和数量,播放期间锁住交互。 + public playRewardEffect(items: SevenDayRewardItem[], day: number, done: () => void) { + this.effectPlaying = true; + const card = this.cards[day - 1]; + const container = card && card.getChildByName("rewardItems"); + RewardClaimEffect.play(this.node, items.map((item, index) => { + const source = container && container.children[index] || card || this.panel; + return { + icon: this.getRewardIcon(item, true), + count: Math.max(0, Number(item.count !== undefined ? item.count : item.amount) || 0), + prefix: item.type === "infinite_health" || item.type === "coin" ? "" as const : "×" as const, + isTime: item.type === "infinite_health", + worldPosition: source.convertToWorldSpaceAR(cc.v2()), + worldPopPosition: source.convertToWorldSpaceAR(cc.v2(0, source.height)), + }; + }), () => { + this.effectPlaying = false; + done(); + }, this.rewardNumberFrames); + } + + // 按天数寻找奖励,不依赖服务端数组顺序。 private findReward(day: number): SevenDayReward { const rewards = Array.isArray(this.info.rewards) ? this.info.rewards : []; for (let i = 0; i < rewards.length; i++) { @@ -218,6 +294,7 @@ export default class SevenDayGift extends cc.Component { return null; } + // 重建卡片道具图标,多奖励横向重叠显示。 private refreshRewardItems(card: cc.Node, items: SevenDayRewardItem[], special: boolean, unlocked: boolean) { const originalIcon = card.getChildByName("rewardIcon"); if (originalIcon) originalIcon.active = false; @@ -238,29 +315,51 @@ export default class SevenDayGift extends cc.Component { const count = items.length; const availableWidth = special ? 220 : 184; - // 多奖励保持与单奖励相同尺寸,通过横向重叠收进卡片内部。 - const iconSize = special ? 128 : 104; - const step = count === 1 ? 0 : Math.min(iconSize * 0.76, (availableWidth - iconSize) / (count - 1)); + // 图标略微缩小,为下方图片数量留出空间。 + const iconSize = special ? 116 : 92; + const step = count === 1 ? 0 : Math.min(iconSize + 8, (availableWidth - iconSize) / (count - 1)); const totalWidth = iconSize + step * (count - 1); const startX = -totalWidth / 2 + iconSize / 2; - const centerY = special ? 8 : 0; + const centerY = -4; // 道具和数量整体下移 12px。 container.setContentSize(availableWidth, special ? 210 : 130); container.setPosition(0, special ? -8 : -4); items.forEach((item, index) => { const itemNode = new cc.Node("rewardItem" + index); - itemNode.setContentSize(iconSize, iconSize); - itemNode.setPosition(startX + step * index, centerY); + // 第七天突出小猫,爱心缩小;按实际尺寸限制位置,避免超出卡片。 + const baseSize = special && item.type === "cat_skin" ? 140 + : special && item.type === "infinite_health" ? 78 : iconSize; + const size = count === 1 ? Math.round(baseSize * 1.15) : baseSize; + const maxX = (availableWidth - size) / 2; + const x = Math.max(-maxX, Math.min(maxX, startX + step * index)); + itemNode.setContentSize(size, size); + // 爱心略微下移,形成高低层次,让小猫成为视觉重点。 + const y = special && item.type === "infinite_health" ? centerY - 24 : centerY; + itemNode.setPosition(special && item.type === "infinite_health" ? x - 12 : x, y); itemNode.zIndex = index; container.addChild(itemNode); const sprite = itemNode.addComponent(cc.Sprite); sprite.sizeMode = cc.Sprite.SizeMode.CUSTOM; sprite.spriteFrame = this.getRewardIcon(item, unlocked); + // 按原图比例收进目标尺寸,避免非正方形道具被拉伸。 + sprite.trim = false; + if (sprite.spriteFrame) { + const original = sprite.spriteFrame.getOriginalSize(); + const scale = size / Math.max(original.width, original.height, 1); + itemNode.setContentSize(original.width * scale, original.height * scale); + } + // 数量紧跟各自图标;双奖励各占自己的宽度,避免文字相互挤压。 + RewardClaimEffect.createAmount(itemNode, { + count: Math.max(0, Number(item.count !== undefined ? item.count : item.amount) || 0), + prefix: item.type === "coin" || item.type === "infinite_health" ? "" : "×", + isTime: item.type === "infinite_health", + }, this.rewardNumberFrames, count > 1 ? Math.min(88, step) : 100, 26, itemNode); }); } + // 将奖励类型映射为活动图标,猫皮肤区分锁定状态。 private getRewardIcon(item: SevenDayRewardItem, unlocked: boolean): cc.SpriteFrame { switch (item.type) { case "coin": return this.coinIcon; @@ -273,6 +372,7 @@ export default class SevenDayGift extends cc.Component { } } + // 有可用图标时才替换节点图片。 private setSprite(node: cc.Node, frame: cc.SpriteFrame) { if (!node || !frame) return; const sprite = node.getComponent(cc.Sprite); diff --git a/assets/seven_day_gift/SevenDayGiftApi.ts b/assets/seven_day_gift/SevenDayGiftApi.ts index 0f8395ec..a1c75bb7 100644 --- a/assets/seven_day_gift/SevenDayGiftApi.ts +++ b/assets/seven_day_gift/SevenDayGiftApi.ts @@ -1,18 +1,21 @@ import Utils from "../Script/module/Pay/Utils"; +// 单项奖励;优先读取 count,兼容旧数据的 amount;infinite_health 数值单位为秒。 export interface SevenDayRewardItem { type: "coin" | "hammer" | "freeze" | "magic_wand" | "infinite_health" | "cat_skin"; amount?: number; count?: number; - itemId?: number; + itemId?: number; // 猫皮肤奖励对应的猫咪编号。 } +// 一天可以包含多项奖励,claimed 表示这一天已领取。 export interface SevenDayReward { day: number; items: SevenDayRewardItem[]; claimed: boolean; } +// 服务端活动快照,也是弹窗显示和本次领取的数据来源。 export interface SevenDayActivityInfo { serverNow: string | number; activityId: string; @@ -28,7 +31,8 @@ export interface SevenDayActivityInfo { rewards: SevenDayReward[]; } -export interface SevenDayClaimResult { +/** 从活动查询快照生成的本地同步记录,不是独立领取接口的响应。 */ +export interface SevenDayRewardReceipt { claimId: string; rewardDay: number; rewards: SevenDayRewardItem[]; @@ -43,15 +47,9 @@ export interface SevenDayResponse { msg?: string; } +/** 分包内的资产同步接口,不额外请求活动领取接口。 */ export default class SevenDayGiftApi { - public static getInfo(callback: (response: SevenDayResponse) => void) { - this.post("signInActivityInfo", { uid: this.getUid() }, callback); - } - - public static claim(callback: (response: SevenDayResponse) => void) { - this.post("signInClaim", { uid: this.getUid() }, callback); - } - + // 把解锁的猫皮肤同步到当前账号。 public static saveCat(catNum: number, callback: (response: SevenDayResponse<{ catArr: number[] }>) => void) { this.post("setCatArr", { uid: this.getUid(), @@ -60,6 +58,7 @@ export default class SevenDayGiftApi { }, callback); } + // 上传领取后的道具总量,不是本次奖励增量。 public static saveProps(propData: object, callback: (response: SevenDayResponse) => void) { this.post("userProp", { uid: this.getUid(), @@ -69,6 +68,7 @@ export default class SevenDayGiftApi { }, callback); } + // 统一从当前登录信息取得奖励所属账号。 public static getUid(): string { const storedUid = cc.fx.StorageMessage.getStorage("uid"); if (storedUid !== undefined && storedUid !== null && storedUid !== "") { @@ -77,6 +77,7 @@ export default class SevenDayGiftApi { return cc.fx.GameConfig.GM_INFO.uid || ""; } + // 封装同步请求;超时和网络返回最多触发一次回调。 private static post(endpoint: string, params: object, callback: (response: SevenDayResponse) => void) { let completed = false; const timeoutHandle = setTimeout(() => { diff --git a/assets/seven_day_gift/SevenDayGiftReward.ts b/assets/seven_day_gift/SevenDayGiftReward.ts index 82c5dc6c..a9912844 100644 --- a/assets/seven_day_gift/SevenDayGiftReward.ts +++ b/assets/seven_day_gift/SevenDayGiftReward.ts @@ -1,9 +1,11 @@ -import SevenDayGiftApi, { SevenDayClaimResult, SevenDayRewardItem } from "./SevenDayGiftApi"; +import SevenDayGiftApi, { SevenDayRewardReceipt, SevenDayRewardItem } from "./SevenDayGiftApi"; const PROCESSED_CLAIMS_KEY = "seven_day_gift_processed_claims_v1"; +/** 奖励到账与本地防重,只处理资产,不负责播放动画。 */ export default class SevenDayGiftReward { - public static grant(result: SevenDayClaimResult, callback: (success: boolean, message?: string) => void) { + // 汇总奖励并同步资产,全部成功后才写入本地完成记录。 + public static grant(result: SevenDayRewardReceipt, callback: (success: boolean, message?: string) => void) { if (!result || !result.claimId || !Array.isArray(result.rewards)) { callback(false, "服务器奖励数据不完整"); return; @@ -51,6 +53,7 @@ export default class SevenDayGiftReward { }); } + // 更新金币、道具和无限体力;有道具时同步服务端总量。 private static grantCommonRewards( coin: number, hammer: number, @@ -83,6 +86,7 @@ export default class SevenDayGiftReward { } if (infiniteHealth > 0) { + // 服务端奖励值和体力接口都使用秒,原值传入,不做分钟换算。 cc.fx.GameTool.setUserPowerTime(infiniteHealth, "seven_day_gift"); } @@ -106,6 +110,7 @@ export default class SevenDayGiftReward { }); } + // 逐个保存猫皮肤,已保存也视为成功。 private static saveCats(cats: number[], index: number, callback: (success: boolean, message?: string) => void) { if (index >= cats.length) { callback(true); @@ -129,17 +134,20 @@ export default class SevenDayGiftReward { }); } + // 检查本地是否已完成这次奖励同步。 private static hasProcessed(claimId: string): boolean { const records = cc.fx.StorageMessage.getStorage(PROCESSED_CLAIMS_KEY) || {}; return records[this.claimKey(claimId)] === true; } + // 记录本次全部奖励已处理,供后续重复调用跳过。 private static markProcessed(claimId: string) { const records = cc.fx.StorageMessage.getStorage(PROCESSED_CLAIMS_KEY) || {}; records[this.claimKey(claimId)] = true; cc.fx.StorageMessage.setStorage(PROCESSED_CLAIMS_KEY, records); } + // 同一个奖励记录按 UID 隔离,避免账号之间互相影响。 private static claimKey(claimId: string): string { return SevenDayGiftApi.getUid() + ":" + claimId; } diff --git a/assets/seven_day_gift/SevenDayGiftRuntime.ts b/assets/seven_day_gift/SevenDayGiftRuntime.ts index 0880d582..d9e5fcb8 100644 --- a/assets/seven_day_gift/SevenDayGiftRuntime.ts +++ b/assets/seven_day_gift/SevenDayGiftRuntime.ts @@ -1,20 +1,20 @@ -import SevenDayGiftApi, { SevenDayActivityInfo, SevenDayRewardItem } from "./SevenDayGiftApi"; +import { SevenDayActivityInfo, SevenDayRewardItem, SevenDayRewardReceipt } from "./SevenDayGiftApi"; import SevenDayGiftReward from "./SevenDayGiftReward"; -const AUTO_OPEN_KEY = "seven_day_gift_auto_open_v1"; type ClaimHandler = (callback: (success: boolean) => void) => void; export interface SevenDayGiftRuntimeView { - catArt: cc.SpriteFrame; + playRewardEffect(items: SevenDayRewardItem[], day: number, done: () => void): void; setData(info: SevenDayActivityInfo, claimHandler: ClaimHandler): void; setCloseHandler(closeHandler: () => void): void; + // 绑定正式领取回调,再展示活动。 show(autoClaim?: boolean): void; } /** Bundle 内的正式业务逻辑,只处理活动领取、发奖和状态刷新。 */ export default class SevenDayGiftRuntime { private info: SevenDayActivityInfo = null; - private querying = false; + private claiming = false; private loadingSequence = 0; private loadingTimer: any = null; private loadingVisible = false; @@ -22,14 +22,16 @@ export default class SevenDayGiftRuntime { constructor(private view: SevenDayGiftRuntimeView) { } + // 接收主页传入的快照和关闭回调。 public initialize(info: SevenDayActivityInfo, closeHandler: () => void) { if (this.disposed) return; this.view.setCloseHandler(closeHandler); if (info) this.updateInfo(info); } + // 领取期间不覆盖正在使用的活动快照。 public updateInfo(info: SevenDayActivityInfo) { - if (this.disposed || !info) return; + if (this.disposed || this.claiming || !info) return; this.info = info; this.view.setData(this.info, this.claimReward.bind(this)); } @@ -40,68 +42,59 @@ export default class SevenDayGiftRuntime { this.view.show(autoClaim); } + // 销毁后忽略异步结果,并清理加载提示。 public destroy() { if (this.disposed) return; this.disposed = true; this.closeHomeLoading(this.loadingSequence); - this.querying = false; + this.claiming = false; this.info = null; this.view = null; } + // 从当前查询快照生成奖励记录,同步成功后更新状态并播放效果。 private claimReward(callback: (success: boolean) => void) { + if (this.disposed || this.claiming || !this.info || !this.info.canClaim || this.info.todayClaimed) { + callback(false); + return; + } + const rewardDay = Number(this.info.nextRewardDay); + const reward = Array.isArray(this.info.rewards) + ? this.info.rewards.find(item => Number(item.day) === rewardDay) : null; + if (!this.info.activityId || !this.info.startAt || !reward || reward.claimed || !Array.isArray(reward.items) || reward.items.length === 0) { + cc.warn("[SevenDayGift] 活动查询没有返回可领取奖励"); + callback(false); + return; + } + // 消费主页查询的快照,不再次请求活动接口。UID 由 Reward 加入本地防重键。 + const claimedCount = Math.max(Number(this.info.claimedCount) || 0, rewardDay); + const receipt: SevenDayRewardReceipt = { + claimId: JSON.stringify([this.info.activityId, this.info.startAt, rewardDay]), + rewardDay, + rewards: reward.items, + claimedCount, + todayClaimed: true, + completed: claimedCount >= 7, + }; + this.claiming = true; const loadingToken = this.openHomeLoading(7000); - SevenDayGiftApi.claim(response => { + SevenDayGiftReward.grant(receipt, (success: boolean, message?: string) => { if (this.disposed) return; - if (!response || response.code !== 1 || !response.data) { - this.closeHomeLoading(loadingToken); - this.clearAutoOpened(); - cc.warn("[SevenDayGift] 领取失败", response && response.msg); + this.claiming = false; + this.closeHomeLoading(loadingToken); + if (!success) { + cc.warn("[SevenDayGift] 奖励同步失败", message); callback(false); return; } - - SevenDayGiftReward.grant(response.data, (success: boolean, message?: string) => { - if (this.disposed) return; - if (!success) { - this.closeHomeLoading(loadingToken); - this.clearAutoOpened(); - cc.warn("[SevenDayGift] 奖励同步失败", message); - callback(false); - return; - } - - this.applyClaimState(response.data.rewardDay, response.data.claimedCount, response.data.completed); - this.closeHomeLoading(loadingToken); - this.showRewardWindow(response.data.rewards); - this.queryActivity(() => callback(true)); + this.applyClaimState(receipt.rewardDay, receipt.claimedCount, receipt.completed); + this.view.playRewardEffect(receipt.rewards, receipt.rewardDay, () => { + if (!this.disposed) callback(true); }); }); } - private queryActivity(callback?: (success: boolean) => void) { - if (this.querying) { - if (callback) callback(false); - return; - } - if (!SevenDayGiftApi.getUid()) { - if (callback) callback(false); - return; - } - - this.querying = true; - SevenDayGiftApi.getInfo(response => { - if (this.disposed) return; - this.querying = false; - if (!response || response.code !== 1 || !response.data) { - if (callback) callback(false); - return; - } - this.updateInfo(response.data); - if (callback) callback(true); - }); - } - + // 本地标记当天已领,第七天领取后标记活动完成。 private applyClaimState(rewardDay: number, claimedCount: number, completed: boolean) { if (!this.info) return; this.info.claimedCount = Number(claimedCount) || this.info.claimedCount; @@ -116,26 +109,7 @@ export default class SevenDayGiftRuntime { this.view.setData(this.info, this.claimReward.bind(this)); } - private showRewardWindow(rewards: SevenDayRewardItem[]) { - if (!Array.isArray(rewards) || rewards.length === 0) return; - const displayRewards = rewards.map(item => { - const rewardCount = item.count !== undefined ? item.count : item.amount; - return { - type: item.type === "magic_wand" ? "magic" : item.type, - count: Math.max(0, Number(rewardCount) || 0), - spriteFrame: item.type === "cat_skin" ? this.view.catArt : null, - }; - }).filter(item => item.count > 0); - if (displayRewards.length === 0) return; - - const home = this.getHomeController(); - if (!home || typeof home.openRewardWindow !== "function") { - cc.warn("[SevenDayGift] 公共奖励弹窗不可用"); - return; - } - home.openRewardWindow(displayRewards); - } - + // 打开主页加载提示,设置超时以免一直遮挡。 private openHomeLoading(timeoutMs: number): number { const token = ++this.loadingSequence; if (this.loadingTimer !== null) clearTimeout(this.loadingTimer); @@ -148,6 +122,7 @@ export default class SevenDayGiftRuntime { return token; } + // 仅关闭对应请求的提示,避免旧计时器干扰新请求。 private closeHomeLoading(token: number) { if (token !== this.loadingSequence) return; if (this.loadingTimer !== null) { @@ -160,14 +135,10 @@ export default class SevenDayGiftRuntime { if (home && typeof home.closeLoad === "function") home.closeLoad(); } + // 通过主页组件调用现有加载提示。 private getHomeController(): any { const canvas = cc.find("Canvas"); return canvas && canvas.getComponent("JiaZai"); } - private clearAutoOpened() { - if (!this.info || !this.info.activityId) return; - const key = AUTO_OPEN_KEY + ":" + SevenDayGiftApi.getUid() + ":" + this.info.activityId; - cc.fx.StorageMessage.removeStorage(key); - } } diff --git a/assets/seven_day_gift/SevenDayHitMasks.ts b/assets/seven_day_gift/SevenDayHitMasks.ts new file mode 100644 index 00000000..bfa15880 --- /dev/null +++ b/assets/seven_day_gift/SevenDayHitMasks.ts @@ -0,0 +1,4 @@ +// 自动生成的原图 Alpha 命中数据(阈值 16/255),勿手改;更新图片后运行 tools/generate-seven-hit-masks.py。 +export interface SevenDayHitMask { width: number; height: number; rows: number[]; spans: number[][]; } +const masks: { [uuid: string]: SevenDayHitMask } = {"7d090003-0201-4a00-8b00-000000000201":{"width":217,"height":237,"rows":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,13,14,14,15,15,16,17,18,19,19,20,21,21,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,24,24,24,24,25,25,26,26,27,27,28,29,30,31,31,32,33,33,34,35,36,37,38,39,40,41,42,43,44,45],"spans":[[25,189],[21,193],[19,195],[17,197],[15,199],[13,201],[12,202],[11,203],[10,204],[9,205],[8,206],[7,207],[6,208],[5,209],[4,210],[3,211],[2,212],[2,213],[1,213],[1,214],[1,215],[0,215],[0,216],[0,217],[1,217],[2,216],[3,216],[4,215],[5,215],[5,214],[6,214],[7,213],[8,212],[9,211],[10,210],[11,209],[12,208],[13,207],[14,206],[15,205],[17,203],[18,202],[20,200],[22,198],[25,195],[29,191]]},"7d090003-0203-4a00-8b00-000000000203":{"width":264,"height":355,"rows":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,13,14,15,16,17,18,19,19,20,21,22,22,23,24,24,24,24,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,24,24,24,24,22,22,22,20,20,26,26,27,28,28,29,30,31,31,32,33,34,35,36,37,38,39,40,41,42,43],"spans":[[27,231],[24,234],[21,238],[19,241],[17,243],[16,245],[14,246],[13,248],[12,249],[11,250],[10,252],[9,253],[8,254],[7,255],[6,256],[5,257],[5,258],[4,258],[4,259],[3,260],[3,261],[2,261],[2,262],[1,262],[1,263],[0,264],[4,260],[5,259],[6,258],[7,257],[8,256],[9,255],[10,254],[11,253],[12,252],[14,250],[15,249],[16,248],[18,246],[19,245],[21,243],[23,241],[26,238],[30,234]]},"7d090003-0205-4a00-8b00-000000000205":{"width":217,"height":237,"rows":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,13,14,14,15,15,16,17,17,18,18,19,20,20,21,21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,23,23,24,25,25,26,26,27,27,28,29,30,31,31,32,33,33,34,35,36,37,38,39,40,41,42,43,44,45],"spans":[[25,189],[22,192],[19,195],[17,197],[15,199],[13,201],[12,202],[11,203],[10,204],[9,205],[8,206],[7,207],[6,208],[5,209],[4,210],[3,211],[2,212],[2,213],[1,214],[1,215],[0,215],[0,216],[0,217],[1,217],[2,217],[2,216],[3,216],[4,215],[5,215],[5,214],[6,214],[7,213],[8,212],[9,211],[10,210],[11,209],[12,208],[13,207],[14,206],[15,205],[17,203],[18,202],[20,200],[22,198],[25,195],[29,191]]},"7d090003-0207-4a00-8b00-000000000207":{"width":194,"height":181,"rows":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,19,19,20,20,20,21,21,21,21,21,21,22,23,24,25,25,26,27,28,29,30,30,31,31,32,32,32,33,34,35,36,37,38,39,39,40,40,41,42,43,44,44,44,45,44,44,44,43,43,43,46,47,47,47,46,43,43,48,47,47,46,43,49,49,44,50,43,48,51,52,53,54,55,56,57,58,59,59,60,60,60,60,60,61,61,62,62,63,64,64,65,65,66,67,68,69,69,70,70,71,71,71,49,49,46,46,46,72,55,55,55,73,73,54,74,75,76,76,77,78,79,80,81,82,83,84,84,85,86,87,88,89,90,91,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,108,109,110,111,111,112,113,114,115],"spans":[[83,89],[80,90],[69,71,76,91],[66,91,125,126],[65,92,123,129],[64,92,121,130],[64,99,119,131],[64,100,118,131],[63,107,117,132],[63,109,116,132],[64,110,115,137],[64,110,114,139],[44,49,64,112,113,140],[41,52,65,141,148,155],[40,55,62,141,146,157],[38,57,59,142,143,159],[37,160],[37,161],[36,162],[35,163],[34,164],[33,165],[33,166],[31,168],[30,168],[29,169],[28,169],[23,24,28,169],[21,27,28,169],[20,169],[19,170],[18,171],[19,171],[20,171],[22,171,176,179],[23,172,174,181],[20,171,172,182],[18,182],[17,183],[16,183],[15,182],[13,185],[10,187],[8,188],[7,189],[7,190],[8,187],[9,187],[9,188],[7,188],[8,189],[11,187],[13,186],[12,184],[10,183],[8,184],[5,186],[2,188],[1,191],[0,193],[0,194],[1,194],[2,193],[2,192],[3,192],[3,191],[4,191],[4,190],[5,190],[6,189],[6,187],[6,188],[8,186],[9,183],[11,183],[12,182],[13,182],[14,181],[14,180],[15,180],[16,179],[16,178],[16,177],[17,177],[17,176],[18,176],[18,175],[19,175],[21,174],[23,173],[24,172],[25,170],[26,169],[26,168],[27,168],[28,167],[28,166],[29,163],[31,162],[33,161],[35,161],[36,159],[38,158],[40,154,155,156],[42,153],[43,151],[43,150],[43,149],[44,149],[44,148],[45,148],[46,147],[48,72,76,117,120,146],[49,71,80,113,121,145],[50,69,89,101,123,143],[53,66,125,141]]},"7d090003-0211-4a00-8b00-000000000211":{"width":118,"height":99,"rows":[0,1,2,3,4,5,6,7,8,8,9,10,11,12,13,14,14,15,16,17,18,19,20,21,22,22,23,24,24,24,25,26,26,27,27,28,29,30,31,32,33,34,35,35,36,36,37,37,38,39,40,41,42,43,43,43,44,44,44,45,46,46,47,48,48,49,49,50,47,47,47,46,51,51,41,42,52,53,54,55,56,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73],"spans":[[55,64],[52,68],[38,42,51,69],[34,46,49,71],[32,72],[30,73],[29,73],[28,74],[27,75],[26,80],[25,84],[25,86],[24,87],[24,89],[24,90],[23,91],[23,92],[22,92],[20,93],[18,93],[17,93],[16,93],[15,94],[14,94],[13,94],[12,94],[12,95],[12,96],[12,99],[12,100],[12,101],[12,103],[12,106],[13,108],[13,109],[13,110],[14,111],[13,112],[8,113],[5,113],[3,113],[2,114],[2,113],[1,113],[0,113],[0,115],[1,116],[1,117],[0,117],[0,118],[1,118],[2,115],[2,111],[2,101,104,107],[3,99],[3,96],[3,93],[4,92],[4,91],[4,90],[5,89],[6,88],[6,87],[7,86],[9,85],[10,27,30,84],[14,20,32,82],[33,81],[35,79],[37,77],[39,75],[41,73],[44,70],[48,66]]},"7d090003-0213-4a00-8b00-000000000213":{"width":214,"height":232,"rows":[0,1,2,3,4,5,6,7,8,9,10,11,12,12,13,14,14,15,15,16,16,16,17,17,17,17,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,17,17,17,17,16,16,16,15,15,14,14,13,12,12,11,10,9,8,7,6,5,4,3,2,1,0],"spans":[[26,188],[22,192],[19,195],[17,197],[15,199],[14,200],[12,202],[11,203],[10,204],[9,205],[8,206],[7,207],[6,208],[5,209],[4,210],[3,211],[2,212],[1,213],[0,214]]},"7d090003-0235-4a00-8b00-000000000235":{"width":105,"height":116,"rows":[0,1,2,3,4,5,6,7,8,8,9,9,10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,10,10,10,9,9,8,8,8,7,7,7,6,6,5,4,3,12,2,13,14,15],"spans":[[15,90],[12,93],[10,95],[8,97],[7,98],[6,99],[5,100],[4,101],[3,102],[2,103],[1,104],[0,105],[9,96],[11,94],[13,92],[16,89]]},"7d090003-0215-4a00-8b00-000000000215":{"width":158,"height":137,"rows":[0,1,2,2,2,3,3,4,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,30,31,32,33,34,35,36,37,38,39,40,40,41,41,42,43,44,45,46,47,48,49,50,51,52,53,53,54,55,56,56,57,58,58,59,59,60,61,61,62,62,63,64,64,63,62,61,65,66,67,68,69,69,69,70,71,72,73,74,75,75,76,76,76,77,77,77,77,77,77,77,77,77,77,76,76,75,75,74,78,78,79,67,80,57,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97],"spans":[[42,45],[41,46],[41,47],[40,47],[40,48],[39,48,59,82],[39,48,57,84],[39,49,52,87],[38,90],[38,92],[37,93],[36,95],[35,96],[31,97],[27,98],[23,98],[22,99],[22,100],[22,100,105,113],[23,118],[23,121],[30,123],[33,125],[32,126],[32,128],[32,129],[32,130],[31,131],[31,132],[31,133],[31,135],[31,136],[31,137],[31,138],[31,139],[31,140],[31,141],[32,141],[32,142],[32,143],[32,144],[32,145],[31,146],[27,146],[24,147],[21,147],[19,148],[17,148],[15,148],[13,149],[12,149],[11,149],[9,149],[8,149],[7,149],[6,149],[5,149],[4,149],[3,149],[2,149],[2,148],[1,148],[1,147],[1,146],[1,145],[1,149],[1,150],[2,151],[2,152],[2,153],[3,153],[2,154],[1,154],[1,155],[0,155],[0,156],[0,157],[0,158],[0,154],[1,153],[3,150],[5,148],[6,146],[7,145],[9,143],[11,141],[13,138],[15,135],[17,131],[19,125],[21,102,109,117],[23,101],[27,99],[34,46,53,97],[55,95],[58,92],[61,89],[65,85]]},"7d090003-0223-4a00-8b00-000000000223":{"width":240,"height":291,"rows":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,45,46,47,48,49,50,51,51,52,53,53,54,55,55,56,57,57,58,59,60,61,61,62,63,63,64,65,66,67,67,68,68,69,70,70,71,71,72,72,73,73,74,75,75,76,76,77,78,79,79,80,81,81,82,83,83,84,84,85,85,85,86,86,87,88,88,88,89,89,90,90,90,91,91,91,92,92,93,93,94,94,94,95,96,96,96,96,96,97,97,97,97,97,97,97,97,97,98,98,98,98,98,98,98,98,98,98,99,99,99,99,99,100,101,101,101,101,102,103,103,103,104,105,105,106,106,90,89,89,107,107,88,108,108,109,110,111,111,112,113,114,114,115,116,117,117,118,118,119,119,120,121,122,123,123,124,125,126,127,127,128,129,129,130,130,131,132,132,133,134,135,136,136,137,138,138,53,139,139,140,140,141,142,143,143,144,145,146,147,148,149,149,150,151,152,153,154,155,156,157,158,159,160,160,161,162,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,185,186,187,188,189,190,190,191,191,192,193,194,195,196,197],"spans":[[126,134],[125,136],[124,139],[123,141],[121,143],[120,146],[119,148],[117,150],[116,153],[115,155],[114,158],[112,160],[111,163],[110,165],[109,167],[107,170],[106,172],[105,174],[103,177],[102,179],[101,182],[99,185],[93,187],[87,189],[82,192],[78,194],[74,196],[71,199],[68,201],[65,204],[62,206],[59,208],[57,210],[54,211],[52,211],[50,212],[48,212],[46,212],[45,213],[44,213],[42,214],[41,214],[40,214],[40,215],[39,215],[38,216],[37,216],[36,217],[35,217],[35,218],[34,218],[33,219],[32,219],[31,219],[30,220],[29,220],[28,220],[27,220],[26,220],[25,220],[25,221],[24,221],[23,221],[22,221],[21,221],[20,221],[20,222],[19,222],[18,222],[17,222],[16,222],[15,223],[14,223],[13,224],[12,224],[12,225],[11,226],[11,227],[10,227],[10,228],[9,228],[9,229],[9,230],[8,230],[8,231],[7,232],[7,233],[6,233],[6,234],[5,235],[5,236],[4,237],[4,238],[3,238],[3,239],[2,239],[2,240],[1,240],[0,240],[0,239],[1,239],[1,238],[2,238],[2,237],[3,237],[3,236],[4,236],[6,235],[7,234],[8,234],[8,233],[9,233],[10,233],[10,232],[11,232],[12,232],[12,231],[13,231],[14,231],[15,230],[16,230],[17,230],[17,229],[18,229],[19,229],[19,228],[20,228],[21,228],[22,227],[23,227],[24,226],[25,226],[26,225],[27,224],[28,224],[28,223],[29,222],[30,221],[31,220],[32,218],[33,217],[34,216],[34,215],[35,214],[35,213],[36,212],[36,211],[36,210],[37,209],[37,208],[38,207],[38,206],[39,205],[39,204],[40,203],[40,202],[40,201],[41,200],[42,198],[42,197],[43,196],[44,196],[45,195],[46,195],[46,194],[47,194],[48,193],[48,192],[49,191],[50,190],[51,84,89,190],[51,78,93,190],[52,76,95,190],[53,74,98,189],[55,73,99,153,154,188],[56,71,100,149,158,187],[58,69,101,145,159,186],[102,144,160,185],[102,143,161,184],[103,142,162,183],[103,142,163,181],[104,141,164,180],[104,141,165,178],[105,140,167,176],[105,140,169,174],[106,139],[107,138],[108,138],[108,137],[109,136],[110,135],[111,134],[112,133],[113,133],[114,132],[115,131],[117,130],[119,128]]},"7d090003-0225-4a00-8b00-000000000225":{"width":302,"height":315,"rows":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,35,36,37,38,39,40,41,42,43,44,45,45,46,47,48,49,49,50,51,51,52,53,53,54,54,55,55,56,56,57,58,58,59,60,61,62,63,64,65,66,67,68,69,69,70,71,72,72,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,259,260,261,262,263,263,264,264,264,264,264,265,266,267,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293],"spans":[[163,165,166,171],[159,177],[157,181],[155,185],[153,188],[152,189],[150,191],[149,192],[148,195],[147,196],[145,199],[144,200],[143,202],[142,203],[140,205],[139,206],[137,208],[136,209],[135,210],[134,212],[132,213],[131,214],[130,216],[129,216],[128,217],[126,219],[125,220],[124,221],[123,222],[121,222],[120,223],[119,224],[118,225],[117,226],[116,227],[115,228],[114,229],[114,230],[113,231],[113,232],[112,233],[112,234],[111,234],[111,235],[111,236],[110,237],[110,238],[110,239],[110,240],[110,241],[110,242],[110,243],[110,244],[110,245],[110,246],[110,247],[110,248],[111,249],[111,250],[112,250],[112,251],[106,107,112,251],[98,111,113,252],[96,252],[94,253],[92,253],[91,254],[90,254],[89,254],[87,255],[86,255],[85,255],[84,256],[55,56,83,257],[50,64,83,257],[48,70,82,258],[45,73,82,258],[43,75,82,259],[41,77,81,259],[40,79,81,259],[39,260],[38,260],[36,260],[35,260],[34,261],[33,261],[31,261],[30,261],[29,261],[27,261],[26,262],[25,262],[23,262],[22,262],[21,262],[20,262],[18,262],[17,262],[16,262],[15,262],[13,262],[12,262],[11,262],[10,262],[9,262],[8,261],[7,261],[6,261],[5,261],[5,260],[4,260],[3,259],[3,258],[2,258],[2,257],[2,256],[1,255],[1,254],[1,253],[1,252],[0,251],[0,250],[0,248],[0,247],[0,246],[0,245],[0,244],[0,243],[0,242],[0,241],[0,239],[0,238],[0,237],[0,236],[0,235],[1,234],[1,232],[1,231],[1,230],[1,229],[1,228],[1,227],[2,225],[2,224],[2,223],[3,222],[3,220],[4,197,199,218],[4,198,203,213],[4,199],[4,200],[5,201],[5,202],[6,203],[6,204],[7,205],[7,206],[8,207],[8,208],[9,209],[9,210],[10,211],[10,212],[10,213],[11,214],[11,215],[12,215],[12,216],[13,218],[13,219],[14,220],[14,221],[15,221],[16,151,152,222],[16,151,152,223],[17,151,155,224],[17,151,157,225],[18,151,158,226],[19,151,158,227],[19,151,159,228],[20,151,160,229],[21,151,161,230],[21,151,162,231],[22,151,163,232],[23,151,163,233],[23,151,164,234],[24,151,165,234],[25,151,166,235],[26,151,167,236],[27,151,168,237],[28,151,168,238],[28,151,169,239],[29,150,170,240],[30,150,171,241],[31,149,172,242],[32,149,173,243],[33,149,173,244],[33,148,174,245],[34,148,175,246],[35,147,176,246],[36,146,177,247],[37,146,177,248],[38,145,178,249],[40,144,179,250],[40,143,180,251],[41,142,181,252],[43,140,182,253],[44,139,182,254],[45,138,183,255],[46,137,184,256],[47,136,185,257],[49,135,186,258],[50,134,187,258],[51,132,187,259],[53,131,188,260],[54,130,189,261],[56,129,190,262],[57,128,191,263],[59,127,192,264],[60,126,192,265],[62,125,193,266],[64,123,194,267],[65,122,195,268],[68,121,196,268],[69,120,197,269],[70,119,197,270],[72,118,198,271],[74,116,199,272],[76,115,200,273],[78,114,201,274],[80,113,202,277],[83,111,202,278],[86,109,203,280],[90,107,204,280],[93,100,102,103,205,281],[206,282],[206,283],[207,284],[208,285],[209,286],[210,287],[211,288],[211,289],[212,290],[213,291],[214,292],[215,292],[216,293],[216,294],[217,295],[218,296],[219,297],[220,297],[221,297],[221,298],[222,298],[223,298],[224,299],[225,299],[226,299],[227,300],[228,301],[229,301],[230,302],[231,302],[232,302],[233,301],[234,301],[235,300],[236,300],[237,300],[237,299],[238,299],[239,298],[240,298],[241,297],[242,296],[243,296],[243,295],[244,294],[245,293],[246,292],[247,291],[248,290],[248,289],[249,288],[251,287],[252,285],[255,283],[257,282],[258,279],[259,277],[260,274],[264,266,267,268]]},"7d090003-0227-4a00-8b00-000000000227":{"width":137,"height":121,"rows":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,27,27,28,29,29,29,30,31,31,31,31,31,31,31,31,30,29,29,29,29,29,28,27,27,27,26,26,25,32,32,33,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94],"spans":[[97,111],[93,114],[90,117],[88,119],[85,120],[83,122],[82,123],[80,124],[79,125],[77,126],[76,127],[74,128],[25,37,73,128],[20,43,72,129],[16,45,71,130],[14,47,70,130],[12,49,69,131],[11,51,68,131],[9,53,67,132],[8,54,66,132],[7,56,65,133],[6,57,64,133],[5,58,63,133],[4,60,63,134],[4,61,62,134],[3,134],[3,135],[2,135],[2,136],[1,136],[0,136],[0,137],[4,134],[5,133],[6,133],[6,132],[7,132],[7,131],[8,131],[8,130],[9,130],[9,129],[10,129],[10,128],[11,128],[12,127],[12,126],[13,125],[13,124],[13,123],[14,123],[15,122],[16,122],[16,121],[17,121],[18,120],[19,119],[19,118],[20,117],[21,116],[22,116],[23,115],[24,114],[25,113],[26,112],[27,111],[28,110],[29,109],[30,109],[31,108],[32,107],[33,106],[35,105],[36,104],[37,103],[39,102],[40,101],[42,100],[43,99],[45,98],[47,97],[48,96],[50,95],[52,94],[54,93],[56,92],[58,91],[60,89],[62,88],[64,87],[66,86],[68,85],[71,84],[73,83],[76,81]]},"7d090003-0229-4a00-8b00-000000000229":{"width":283,"height":297,"rows":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,27,27,27,27,27,28,28,29,29,29,29,29,29,29,30,31,31,31,31,31,31,32,32,32,32,32,32,32,33,33,33,33,33,33,34,35,35,35,35,35,35,36,36,37,38,39,40,41,42,43,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,79,80,81,81,82,83,84,85,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,205,206,207,208,208,209,210,211,212,212,212,212,212,213,213,214,215,215,216,216,217,218,219,220,221,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,0],"spans":[[],[139,147],[137,149],[134,151],[133,152],[130,153],[128,154],[125,156],[124,156],[122,158],[119,158],[118,159],[116,160],[29,30,32,34,114,161],[28,40,112,161],[27,43,111,161],[26,48,108,161],[25,51,107,161],[25,57,105,161],[25,60,103,162],[25,63,101,162],[24,68,100,163],[24,71,97,164],[24,74,96,164],[24,80,95,165],[24,83,92,165],[24,86,91,165],[25,165],[25,166],[26,166],[27,166],[27,165],[28,165],[29,165],[30,165],[30,164],[31,164],[31,165],[31,166],[31,168],[31,169],[32,170],[32,171],[32,173],[32,175],[31,176],[30,177],[30,178],[29,179],[28,182],[27,182],[27,184],[26,184],[25,186],[25,187],[24,188],[23,189],[23,191],[22,192],[21,193],[21,194],[20,195],[19,197],[19,198],[18,199],[17,200],[16,200],[16,202],[15,202],[14,204],[14,205],[13,206],[13,207],[12,207],[12,208],[11,209],[10,209],[10,210],[9,210],[8,210],[7,210],[6,211],[5,212],[5,213],[4,213],[3,214],[2,214],[2,213],[1,212],[1,211],[0,210],[0,207,208,209],[0,206],[0,204],[0,201],[0,200],[0,197],[0,195],[1,192],[1,190],[1,187],[2,185],[3,182],[3,180],[3,177],[4,175],[5,176],[6,177],[6,178],[8,179],[11,180],[14,181],[16,182],[16,183],[19,184],[21,24,25,185],[28,30,31,186],[37,38,40,187],[46,47,49,52,53,188],[62,64,66,189],[67,190],[68,191],[69,192],[70,193],[70,194],[71,195],[71,196],[72,197],[73,198],[74,199],[75,201],[75,202],[76,203],[77,204],[78,205],[78,206],[79,207],[79,138,139,208],[80,137,139,209],[81,137,140,210],[82,136,141,211],[82,136,142,211],[83,135,143,213],[84,135,143,214],[84,134,144,215],[85,134,145,216],[86,133,146,217],[87,133,147,218],[87,132,148,219],[88,132,148,220],[89,131,149,221],[89,131,150,222],[90,130,151,223],[91,130,152,224],[92,129,152,225],[92,129,153,226],[93,128,154,227],[94,128,155,228],[94,127,155,229],[95,127,156,230],[96,126,157,231],[97,125,158,232],[98,125,159,233],[99,124,160,234],[100,124,161,235],[100,123,161,236],[102,123,162,237],[102,122,163,238],[104,122,164,239],[106,109,110,121,164,240],[110,120,165,241],[111,120,166,242],[112,119,167,243],[113,118,168,244],[114,115,168,245],[170,246],[170,250],[171,252],[172,254],[173,255],[173,256],[174,257],[175,258],[176,259],[176,260],[177,261],[178,262],[179,263],[180,264],[180,265],[182,266],[183,268],[184,268],[185,269],[185,270],[186,271],[187,272],[188,273],[189,274],[189,275],[190,276],[191,276],[192,277],[193,277],[194,278],[195,278],[196,279],[197,279],[198,279],[199,280],[200,281],[201,281],[202,282],[203,283],[203,282],[204,282],[205,282],[206,282],[207,281],[208,281],[209,281],[210,280],[211,279],[212,279],[213,278],[214,277],[215,277],[216,276],[217,275],[218,275],[218,274],[219,273],[220,272],[221,271],[222,270],[223,269],[223,268],[225,267],[226,265],[227,264],[230,262],[233,261],[235,258],[235,256],[238,253],[240,249]]},"a81e0d8f-a8e9-4790-913b-52f8cfd8580f":{"width":246,"height":246,"rows":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,13,14,15,15,16,16,17,17,17,18,18,18,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,18,18,18,17,17,17,16,16,15,15,14,13,13,12,11,10,9,8,7,6,5,4,3,2,1,0],"spans":[[26,220],[23,223],[20,226],[18,228],[16,230],[15,231],[13,233],[12,234],[11,235],[10,236],[9,237],[8,238],[7,239],[6,240],[5,241],[4,242],[3,243],[2,244],[1,245],[0,246]]},"7d090003-0231-4a00-8b00-000000000231":{"width":891,"height":1087,"rows":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,21,22,23,24,25,26,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,40,41,42,43,44,45,45,45,45,46,47,48,49,50,51,52,52,53,54,54,55,55,56,57,58,59,60,60,61,62,63,64,65,66,67,68,68,69,69,70,70,71,71,71,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,73,74,75,76,77,77,78,78,78,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,78,78,78,77,77,76,76,75,75,74,73,73,72,71,71,70,70,69,68,68,67,66,66,65,65,64,63,63,80,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,81,81,81,81,81,82,82,82,83,83,83,84,84,84,85,86,87,88,89,89,90,91,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,109,109,109,109,109,109,109,110,111,112,113,113,111,114,109,109,109,109,115,115,115,115,116,116,116,116,116,117,117,117,118,118,118,119,119,119,120,120,121,121,122,122,123,124,124,125,126,126,127,128,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,150,151,152,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,195,195,195,195,195,195,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,216,217,218,219,220,221,222,223,224,225,226,227,228,229,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,230,231,232,233,234,235,236,237,238,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,195,239,240,241,242,243],"spans":[[47,509,696,706],[42,514,693,740,744,784],[39,517,692,741,742,786],[36,520,691,787],[33,523,691,788],[31,525,691,789],[29,527,690,790],[27,529,690,790],[26,530,690,790],[24,532,690,791],[23,533,690,791],[21,535,690,791],[20,536,691,791],[19,537,691,791],[18,538,691,791],[17,539,691,791],[16,540,691,791],[15,541,691,791],[14,542,691,791],[13,543,691,791],[12,544,691,791],[11,545,691,791],[10,546,691,791],[9,547,691,791],[9,547,691,716,717,791],[8,548,691,716,717,791],[7,549,692,715,717,791],[6,550,693,715,717,791],[6,550,694,714,717,791,826,833],[5,551,695,713,716,791,809,815,825,834],[5,551,702,710,716,791,807,817,825,834],[4,552,716,791,805,818,824,835],[4,552,716,791,805,819,824,835],[4,552,716,791,804,820,824,835],[3,553,716,791,804,820,824,835],[3,553,715,791,804,820,824,835],[3,553,715,791,804,820,825,834],[2,554,715,791,803,820,825,834],[2,554,715,791,804,820,828,831],[2,554,715,791,804,820],[1,555,714,791,804,820],[1,555,714,791,805,819],[1,555,714,791,806,818],[1,555,714,791,807,817],[0,556,713,791,810,814],[0,556,713,791],[0,556,712,790,835,839],[0,556,712,790,804,819,832,844],[0,556,712,790,791,822,831,847],[0,556,712,824,830,849],[0,556,712,825,830,850],[0,556,711,826,830,850],[0,556,711,826,829,851],[0,556,711,827,829,851],[0,556,711,851],[0,556,712,851],[0,556,713,851],[0,556,714,851],[0,556,717,851],[0,556,729,851],[0,851],[0,853],[0,855],[0,857],[0,858],[0,859],[0,860],[0,861],[0,862],[0,863],[0,864],[0,865],[0,866],[0,867],[0,868],[0,869],[0,870],[0,871],[0,872],[0,873],[0,856],[1,855],[2,855],[3,855],[4,855],[5,855],[5,857],[6,858],[6,859],[7,860],[8,861],[9,860],[10,860],[11,859],[11,857],[12,855],[13,855],[14,855],[15,855],[16,855],[17,855],[18,855],[19,855],[20,855],[21,855],[23,855],[24,855],[26,855],[27,855],[29,855],[29,858],[29,859,885,889],[29,859,884,889],[29,859,884,890],[29,858,886,888],[30,855],[31,855],[32,855],[33,855],[34,855],[35,855],[36,855],[37,858],[38,858],[39,855],[40,855],[41,855],[42,855],[43,854],[44,854],[45,854],[46,854],[46,853],[47,853],[48,853],[49,853],[50,852],[51,852],[52,852],[53,851],[54,851],[56,851],[57,850],[58,850],[60,849],[61,849],[63,849],[64,848],[66,848],[68,847],[69,846],[69,845],[69,844],[69,843],[69,842],[70,842],[70,97,98,841],[71,95,100,125,131,840],[71,94,101,125,132,156,163,840],[72,93,101,124,133,155,164,186,194,839],[73,92,103,123,134,154,164,185,196,215,220,838],[74,91,104,122,135,153,165,184,197,214,230,245,246,837],[76,90,105,121,136,152,167,183,197,213,231,243,263,836],[77,89,105,120,137,151,168,182,201,211,234,240,297,835],[80,86,107,119,138,150,170,180,204,206,323,834],[109,117,141,145,349,833],[110,115,375,832],[401,830],[426,829],[111,114,452,827],[108,117,141,146,173,177,478,826],[107,119,138,149,170,180,203,210,234,239,267,270,504,824],[105,121,137,151,168,181,200,212,232,242,264,272,296,302,328,329,331,333,529,823],[104,122,136,153,167,183,198,213,230,243,262,274,294,304,326,334,359,364,391,393,555,821],[103,123,135,154,166,184,197,214,229,244,261,275,292,306,324,336,356,366,388,396,420,427,453,454,581,820],[103,123,134,154,166,185,197,215,229,246,260,276,292,307,323,337,355,367,386,398,419,428,450,458,482,488,607,818],[102,124,133,155,165,185,196,216,229,246,260,277,291,307,322,337,354,368,385,399,418,429,449,459,481,490,513,519,544,550,577,579,609,611,632,815],[101,124,133,155,164,186,196,216,228,247,259,277,290,308,322,338,354,369,385,399,417,429,448,460,480,491,512,521,543,551,575,581,607,612,639,643,658,813],[101,125,132,156,164,186,196,217,227,247,259,278,290,308,321,339,353,369,385,400,417,430,448,460,480,491,511,521,543,552,574,582,606,613,638,644,669,674,684,810],[101,125,132,156,164,186,195,217,227,248,258,278,290,309,321,339,353,369,384,400,416,430,448,460,479,492,511,522,542,552,574,583,606,613,637,644,668,675,701,704,710,807],[101,125,132,156,164,187,195,217,226,248,258,278,290,309,321,339,353,370,384,400,416,431,447,461,479,492,511,522,542,552,574,583,605,614,637,644,668,675,701,704,732,735,736,803],[101,125,132,156,163,187,195,217,226,248,258,279,290,309,321,339,353,370,384,400,416,431,447,461,479,492,511,522,542,552,574,583,605,614,637,644,668,675,701,704,732,735,761,798],[101,125,132,156,163,187,195,217,226,248,258,278,289,309,321,339,353,370,384,400,416,431,447,461,479,491,511,522,543,552,574,583,605,613,637,644,669,674],[101,125,132,156,163,187,195,217,226,247,258,278,290,309,321,339,353,370,385,400,416,430,448,461,480,491,511,522,543,552,574,582,606,613,638,643,669,672],[101,125,132,156,164,186,195,217,227,247,259,278,290,309,322,339,353,369,385,400,417,430,448,460,480,491,512,521,544,551,575,581,607,612,639,641],[101,125,133,156,164,186,195,217,227,247,259,278,290,308,322,338,354,369,385,399,417,429,449,460,480,490,513,521,546,550,577,579],[101,125,133,155,164,186,196,216,228,247,259,277,291,308,322,338,354,369,386,399,418,429,450,459,481,489,514,519],[102,124,133,155,165,186,196,216,228,246,259,277,291,307,323,338,355,368,387,398,419,428,451,458,484,485],[102,124,134,155,166,185,197,216,229,245,260,276,292,306,324,337,356,367,389,396,421,425],[103,123,134,154,167,184,197,215,229,245,261,274,293,305,325,335,358,365,390,391,392,394],[104,123,135,153,168,183,198,214,231,243,262,273,295,304,328,332],[104,122,136,152,169,182,200,213,233,242,265,271,297,300],[106,120,137,150,170,181,202,210,235,240],[108,119,140,148,172,178],[110,116,144,145],[],[144,145],[140,148,173,178],[139,151,170,180,203,211,234,240,266,267,268,271,300,301],[138,152,169,182,202,212,232,243,265,273,297,304,328,334,361,363],[137,153,168,183,200,214,231,244,264,275,295,305,327,335,358,365,390,396,423,426,486,487],[137,153,168,184,199,214,231,245,263,275,294,306,326,336,357,366,389,397,421,428,452,457,484,489,516,518,548,549],[136,154,167,184,199,215,231,246,262,276,294,306,325,337,357,367,388,398,420,428,451,459,482,490,514,520,545,551,577,582],[136,154,167,184,199,215,230,246,262,276,293,306,325,337,356,368,388,398,419,429,451,460,482,490,514,521,545,551,576,582,609,612,639,643],[136,154,167,185,199,215,230,246,262,276,293,307,325,338,356,368,388,399,419,429,450,460,482,491,514,521,544,552,576,582,608,612,639,643],[135,155,167,185,199,216,230,246,261,277,293,307,324,338,356,368,387,399,419,429,450,460,482,491,514,521,544,552,576,582,609,612,639,643],[135,155,167,185,199,216,230,246,261,277,293,307,325,338,356,368,387,399,419,429,450,460,482,490,514,521,545,552,576,582,609,612,640,642],[135,154,167,185,198,216,230,246,261,277,293,307,325,338,356,368,388,399,419,429,451,460,482,490,514,520,546,551,577,582],[136,154,167,185,198,215,230,246,262,276,293,307,325,337,356,368,388,398,419,428,451,459,483,489,514,520,547,549],[136,154,168,185,199,215,231,245,262,276,293,307,326,337,357,368,389,397,420,428,453,458,485,488,516,518],[136,154,168,184,200,215,231,245,263,275,294,306,327,336,358,367,390,396,422,426,455,457],[137,153,169,183,200,214,232,244,263,274,295,305,328,336,359,364,391,393],[137,153,169,183,201,213,233,243,264,273,296,304,329,332],[138,152,170,182,202,212,234,242,267,271],[140,150,171,181,204,209,236,240],[142,149,175,176],[173,179,206,210,237,240],[171,181,203,212,235,242,266,272,298,302,331,332,361,364,392,393],[171,182,202,212,234,244,265,274,297,305,328,335,359,366,391,396,423,426,454,457],[170,182,202,213,233,244,265,274,296,305,328,336,359,367,390,397,421,427,453,458,485,488,517,518,547,550],[170,183,201,213,233,244,264,275,296,305,327,336,358,367,390,398,421,428,453,459,485,489,516,519,547,550],[170,183,201,214,233,245,264,275,295,305,327,336,358,367,390,398,421,428,452,459,485,489,516,520,547,550],[170,183,201,214,233,245,264,275,295,305,327,336,358,367,390,397,421,428,452,459,485,488,516,519,547,550],[170,183,201,214,233,244,264,275,296,305,327,336,358,367,390,397,422,428,453,458],[170,183,201,214,233,244,264,275,296,305,327,336,359,367,391,397,422,428,454,457],[170,183,202,213,234,243,265,274,297,304,328,335,360,366,392,395],[171,182,202,213,234,243,266,273,298,303,330,334,362,364],[172,181,203,212,235,242,267,272],[173,180,204,211],[175,177],[206,210,237,241,269,272],[205,212,236,242,267,274,300,304,331,334,362,365,394,395],[204,212,236,243,267,274,299,305,330,335,360,366,393,396,423,427],[204,213,235,243,266,274,298,305,329,335,360,366,392,396,424,427],[204,213,235,244,266,274,298,305,329,335,360,366,392,396,424,427],[204,213,235,243,266,274,298,305,330,335,361,365,393,395,425,426],[204,213,235,243,267,274,299,304,331,335,362,364],[204,212,236,243,268,273,300,303],[205,211,237,241],[270,272,301,303],[238,241,269,273,300,304],[238,242,269,273,300,304],[238,241,269,272,301,303],[239,240]]},"7d090003-0233-4a00-8b00-000000000233":{"width":709,"height":298,"rows":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,36,36,37,37,38,38,39,39,39,39,39,40,40,39,39,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,62,63,64,65,66,67,68,68,69,70,71,71,71,72,72,73,73,74,74,75,76,76,77,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,91,92,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,219,220,220,221,222,223,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270],"spans":[[156,160],[150,167],[145,171,195,201],[143,173,190,209],[141,174,186,213,264,268],[140,175,184,215,257,272],[139,177,183,217,253,277],[90,96,137,178,182,218,250,280],[86,101,136,179,180,219,249,281],[82,105,135,221,247,283],[78,106,134,222,246,284,296,305],[74,108,134,223,245,285,291,310],[71,109,132,223,244,286,289,315],[68,110,128,228,239,287,288,318],[66,112,123,232,234,319],[63,113,122,320],[59,113,121,322],[56,114,119,323],[53,115,118,324],[50,116,117,325],[46,116,117,326],[42,327],[38,327],[34,330],[29,333],[25,334],[21,335],[18,337],[16,338],[15,339],[14,340],[12,340],[11,341],[11,342],[10,343],[9,343],[8,344],[7,344],[7,345],[6,345],[5,345],[6,344],[6,344,346,347,349,352],[6,356],[6,359],[7,361],[7,363],[7,365],[7,367],[7,369],[8,370],[8,372],[8,373],[9,377],[9,379],[10,381],[11,382],[11,384],[11,385],[11,386],[10,387],[10,389],[10,390],[10,391],[10,392],[8,393],[7,393],[6,393],[5,394],[4,394],[3,395],[2,395],[2,396],[1,396],[1,397],[0,397],[0,398],[0,399],[1,400],[1,400,478,480],[1,400,476,481],[1,401,475,482],[1,401,474,482],[2,401,473,483],[2,401,472,483],[2,401,471,484],[2,402,470,484],[2,402,470,485],[3,402,469,485],[3,402,468,486],[3,403,467,486],[3,402,466,487],[4,402,465,488],[4,402,464,488],[5,401,464,487],[6,401,464,485],[7,401,463,484],[7,401,463,482],[8,400,463,480],[9,400,463,479],[10,399,462,478],[12,398,462,476],[13,398,462,475],[14,397,462,474],[17,396,462,473],[22,395,462,472],[34,394,461,470],[35,393,461,469],[35,392,461,468,588,591],[36,391,461,467,586,591],[36,389,461,466,584,592],[37,388,461,466,583,592],[38,402,461,465,581,592],[39,414,461,464,580,593],[39,424,461,463,578,593],[40,434,460,462,577,593],[36,443,575,594],[34,448,495,504,574,594],[33,220,227,455,488,509,574,594],[32,219,232,459,484,513,574,595],[31,218,241,463,480,514,574,595],[30,217,249,467,477,515,574,595],[30,216,249,471,476,517,574,595],[29,177,178,214,248,472,475,518,574,595],[28,177,179,213,248,519,574,595],[28,177,181,212,247,520,574,594],[28,178,185,207,247,521,534,539,574,593],[28,179,190,203,247,521,529,544,574,592],[28,180,247,522,525,548,574,591],[28,181,246,523,524,552,575,590],[28,182,246,556,575,588],[28,183,246,558,575,587],[28,185,245,559,575,586],[28,186,245,560,575,585],[28,187,245,562,575,584],[29,188,245,563,575,583],[29,189,245,564,575,582],[29,190,244,565,575,581],[29,191,244,566,575,580],[30,192,244,566,576,578],[30,193,244,567],[30,193,243,568],[30,194,243,569],[30,195,243,569],[31,196,243,569],[31,197,242,569],[31,198,242,570],[32,199,242,570],[32,200,241,570],[32,201,241,571],[32,201,241,571,617,619,620,621],[33,202,241,571,616,622],[33,203,241,571,615,622],[34,203,241,571,613,623],[34,204,240,571,612,623],[34,205,240,571,611,623],[34,205,240,571,610,623],[35,206,240,571,609,622],[35,206,240,570,607,622],[36,207,239,570,606,622],[36,207,239,570,585,592,605,622],[36,208,239,570,584,599,601,602,604,622],[37,208,239,570,584,621],[37,209,239,569,584,621],[37,209,238,569,584,621],[37,210,238,569,584,621],[36,210,238,568,585,621],[36,211,238,568,586,620,693,698],[35,211,238,568,586,620,690,699],[35,211,237,567,587,620,688,700],[35,212,237,567,588,620,686,701],[34,212,237,567,588,620,684,701],[34,212,237,567,589,620,680,701],[34,213,237,567,590,621,678,702],[34,213,237,566,590,622,676,702],[33,213,237,566,591,622,674,703],[33,213,237,566,591,623,671,703],[32,213,236,566,592,624,669,704],[32,213,236,565,592,624,667,705],[32,214,236,565,592,625,664,705],[32,214,236,565,592,626,662,706],[31,214,236,564,592,627,660,706],[31,214,237,564,591,627,658,706],[31,214,237,564,591,628,656,707],[31,214,237,564,591,628,654,707],[31,214,237,563,591,629,652,708],[31,214,237,563,591,629,650,709],[30,214,238,563,590,629,648,709],[30,214,238,563,590,609,610,612,613,628,646,709],[30,214,238,562,590,608,621,627,644,707],[30,214,238,562,590,607,642,703],[30,214,239,562,589,605,641,700],[30,214,237,562,589,604,639,696],[30,213,236,561,562,563,589,603,637,693],[30,213,235,568,588,602,635,689],[30,213,233,572,588,601,634,686],[30,213,232,574,588,599,632,681],[30,214,232,576,588,598,630,678],[30,216,231,577,588,597,628,676],[30,218,230,578,589,596,627,671],[30,219,229,580,590,595,625,668],[30,220,229,581,624,664],[30,221,229,582,622,661],[30,222,229,583,620,658],[30,223,228,585,619,654],[30,224,228,586,617,651],[30,224,228,586,616,646],[30,225,228,587,615,643],[31,226,228,588,613,640],[31,226,227,589,612,636],[31,589,610,633],[31,590,609,629],[31,591,607,625],[32,591,606,621],[32,591,605,618],[32,591,604,615],[32,592,602,610],[33,592,602,607],[33,592],[33,228,229,592],[33,228,229,593],[32,228,230,593],[32,227,230,593],[31,227,231,593],[30,226,232,593],[30,226,233,593],[30,89,169,225,233,593],[30,89,170,225,234,592],[29,90,170,224,235,592],[29,90,171,223,235,592],[29,90,171,222,236,592],[29,90,172,221,237,592],[29,90,173,220,238,592],[29,90,174,219,238,592],[29,90,175,217,239,591],[29,90,176,215,240,281,283,591],[30,89,178,207,241,279,284,591],[30,89,180,189,190,206,242,277,286,591],[30,88,192,204,244,275,287,591],[31,88,245,273,288,590],[31,87,246,270,290,590],[32,87,248,267,291,350,352,590],[32,86,253,262,293,349,353,590],[33,85,298,347,355,590],[33,85,303,343,356,589],[34,84,306,341,357,589],[35,83,311,336,359,589],[36,81,320,322,323,325,328,329,363,588],[37,79,368,588],[38,73,373,587],[40,72,383,586],[41,71,394,456,458,585],[44,55,57,68,407,454,459,585],[418,453,459,584],[429,449,460,583],[437,443,461,582],[463,582],[464,506,508,581],[465,504,509,580],[467,503,510,579],[471,502,512,578],[476,499,513,576],[483,494,514,575],[516,574],[518,572],[520,571],[524,568],[529,564],[534,559],[540,554],[]]},"7d090003-0209-4a00-8b00-000000000209":{"width":186,"height":173,"rows":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,19,20,21,21,21,21,21,21,20,20,20,22,23,23,23,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,42,43,44,45,46,45,45,47,47,47,47,48,49,49,49,48,47,47,50,51,52,53,54,55,56,45,57,58,59,60,61,62,62,63,64,65,66,67,68,69,70,70,70,70,71,71,72,72,73,74,75,75,75,76,46,77,55,55,78,78,79,80,81,81,82,83,65,65,65,65,65,84,63,63,63,63,61,85,86,87,87,88,41,89,89,90,42,91,92,93,94,32,32,95,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,109,110,109,108,111,112,113,114,115],"spans":[[79,84],[76,84],[65,67,72,84],[64,83,121,122],[63,83,119,123],[63,83,118,124],[64,83,87,94,117,124],[64,83,86,94,115,124],[64,83,85,94,97,102,114,125],[65,83,84,93,95,102,114,127],[65,93,94,102,113,133],[65,102,103,105,112,134],[40,45,65,101,102,106,111,134],[38,48,66,106,110,133,144,151],[37,50,58,65,66,104,110,131,142,153],[36,52,57,130,140,154],[35,54,57,129,130,131,138,155],[35,55,57,134,136,155],[34,134,135,156],[34,156],[33,156],[33,157],[33,156,158,161],[28,161],[29,160],[19,20,29,160],[18,22,30,159],[18,23,31,163],[19,24,30,163],[20,25,28,163],[22,162],[23,161],[24,163],[26,164],[27,162],[27,161],[24,162,172,175],[21,163,170,175],[16,164,169,175],[15,165,167,174],[16,165,166,172],[16,170],[17,168],[18,171],[9,181],[7,181],[7,182],[8,180],[8,179],[9,179],[9,13,14,180],[13,178],[11,176],[10,177],[9,178],[7,179],[7,180],[10,180],[15,179],[14,173],[14,174],[13,174],[13,175],[12,175],[10,175],[8,176],[6,177],[4,179],[1,181],[0,184],[0,186],[1,186],[2,185],[2,184],[3,184],[3,183],[4,6,8,182],[7,178],[6,179],[6,180],[6,9,10,180],[9,180],[9,175,176,180],[9,175,177,179],[9,176],[13,173],[15,173],[15,172],[15,170],[16,169],[17,169],[18,20,21,168],[21,167],[22,167],[24,166],[25,162],[26,161],[26,160],[27,157,158,159],[28,157],[29,30,32,156],[33,155],[34,154],[35,153],[36,149],[39,148],[41,146],[44,143],[44,141],[43,141],[43,142],[44,68,70,115,117,141],[45,67,72,113,118,140],[46,66,76,82,83,108,119,139],[47,64,86,97,120,138],[50,61,122,136]]}}; +export default masks; diff --git a/assets/seven_day_gift/SevenDayHitMasks.ts.meta b/assets/seven_day_gift/SevenDayHitMasks.ts.meta new file mode 100644 index 00000000..773c8d90 --- /dev/null +++ b/assets/seven_day_gift/SevenDayHitMasks.ts.meta @@ -0,0 +1,10 @@ +{ + "ver": "1.1.0", + "uuid": "a565d062-33fb-4c83-a03f-986b770c8426", + "importer": "typescript", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/assets/seven_day_gift/SevenDayHitTest.ts b/assets/seven_day_gift/SevenDayHitTest.ts new file mode 100644 index 00000000..5ded82df --- /dev/null +++ b/assets/seven_day_gift/SevenDayHitTest.ts @@ -0,0 +1,40 @@ +import masks from "./SevenDayHitMasks"; + +/** 根据原图透明度判断可见 UI,不把贴图矩形内的空白当作弹窗。 */ +export default class SevenDayHitTest { + // 从上层子节点往背景检查,透明处继续寻找下方的可见 UI。 + public static findVisible(node: cc.Node, screenPoint: cc.Vec2): cc.Node { + if (!node.activeInHierarchy || node.opacity === 0) return null; + for (let i = node.children.length - 1; i >= 0; i--) { + const hit = this.findVisible(node.children[i], screenPoint); + if (hit) return hit; + } + const sprite = node.getComponent(cc.Sprite); + const label = node.getComponent(cc.Label); + if ((!sprite || !sprite.enabled || !sprite.spriteFrame) && (!label || !label.enabled)) return null; + const camera = cc.Camera.findCamera(node); + const world = camera ? camera.getScreenToWorldPoint(screenPoint) : screenPoint; + const local = node.convertToNodeSpaceAR(cc.v2(world.x, world.y)); + let u = local.x / node.width + node.anchorX; + let v = 1 - (local.y / node.height + node.anchorY); + if (!isFinite(u) || !isFinite(v) || u < 0 || u >= 1 || v < 0 || v >= 1) return null; + if (!sprite || !sprite.enabled || !sprite.spriteFrame) return node; + // SpriteFrame 的 UUID 不随动态图集打包而变化,命中数据无需读取 GPU。 + const frame = sprite.spriteFrame; + const mask = masks[(frame as any)._uuid]; + if (!mask || sprite.type !== cc.Sprite.Type.SIMPLE) return node; + if (!sprite.trim) { + const size = frame.getOriginalSize(); + const offset = frame.getOffset(); + u = (u * size.width - (size.width - mask.width) / 2 - offset.x) / mask.width; + v = (v * size.height - (size.height - mask.height) / 2 + offset.y) / mask.height; + } + if (u < 0 || u >= 1 || v < 0 || v >= 1) return null; + const x = Math.floor(u * mask.width); + const runs = mask.spans[mask.rows[Math.floor(v * mask.height)]]; + for (let i = 0; i < runs.length; i += 2) { + if (x >= runs[i] && x < runs[i + 1]) return node; + } + return null; + } +} diff --git a/assets/seven_day_gift/SevenDayHitTest.ts.meta b/assets/seven_day_gift/SevenDayHitTest.ts.meta new file mode 100644 index 00000000..6a7c8b45 --- /dev/null +++ b/assets/seven_day_gift/SevenDayHitTest.ts.meta @@ -0,0 +1,10 @@ +{ + "ver": "1.1.0", + "uuid": "bcc43cda-131b-4dc9-bbdf-9a09c1ba0bb6", + "importer": "typescript", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/assets/seven_day_gift/prefab/sevenDayGift.prefab b/assets/seven_day_gift/prefab/sevenDayGift.prefab index 8c206ca6..15aef34a 100644 --- a/assets/seven_day_gift/prefab/sevenDayGift.prefab +++ b/assets/seven_day_gift/prefab/sevenDayGift.prefab @@ -5932,7 +5932,48 @@ "infiniteHealthIcon": { "__uuid__": "7d090003-0227-4a00-8b00-000000000227" }, - "_id": "" + "_id": "", + "rewardNumberFrames": [ + { + "__uuid__": "837772ce-3a48-4bcb-9ba2-2bda8204fd74" + }, + { + "__uuid__": "b07ae691-b024-416b-a59d-a51a7c496270" + }, + { + "__uuid__": "07f23729-1d5a-4a02-83c3-8f8f0d184a54" + }, + { + "__uuid__": "181f0a08-d1b8-4edb-ad3e-3895e5d64d78" + }, + { + "__uuid__": "bafe0f35-0ace-49a4-b035-8fe047e26339" + }, + { + "__uuid__": "f44c5687-d99f-421e-a179-6dd6806fed68" + }, + { + "__uuid__": "c2ed5f1c-0be6-4af7-b945-b78ef8a0c9f5" + }, + { + "__uuid__": "2b2d4cfa-4602-48af-a33b-f8b1f504b668" + }, + { + "__uuid__": "59e86104-9963-4415-9377-e17b2215de5f" + }, + { + "__uuid__": "c647a240-a9d5-46b9-bd73-7d36bf57ed6b" + }, + { + "__uuid__": "5929b033-2102-4f34-972b-1db65e57b0b1" + }, + { + "__uuid__": "6fe4bf3a-562d-4fc9-82bc-0378b0041112" + }, + { + "__uuid__": "f62e9f2e-72b3-4f73-8ecd-d0fd2a2e1b4d" + } + ] }, { "__type__": "cc.PrefabInfo", diff --git a/assets/seven_day_gift/texture/道具数量(中).meta b/assets/seven_day_gift/texture/道具数量(中).meta new file mode 100644 index 00000000..a39c0c51 --- /dev/null +++ b/assets/seven_day_gift/texture/道具数量(中).meta @@ -0,0 +1,13 @@ +{ + "ver": "1.1.3", + "uuid": "e486f6fc-1b96-4dca-82cc-b9f50270f067", + "importer": "folder", + "isBundle": false, + "bundleName": "", + "priority": 1, + "compressionType": {}, + "optimizeHotUpdate": {}, + "inlineSpriteFrames": {}, + "isRemoteBundle": {}, + "subMetas": {} +} \ No newline at end of file diff --git a/assets/seven_day_gift/texture/道具数量(中)/0.png b/assets/seven_day_gift/texture/道具数量(中)/0.png new file mode 100644 index 00000000..8eaad843 Binary files /dev/null and b/assets/seven_day_gift/texture/道具数量(中)/0.png differ diff --git a/assets/seven_day_gift/texture/道具数量(中)/0.png.meta b/assets/seven_day_gift/texture/道具数量(中)/0.png.meta new file mode 100644 index 00000000..8aff1f7c --- /dev/null +++ b/assets/seven_day_gift/texture/道具数量(中)/0.png.meta @@ -0,0 +1,38 @@ +{ + "ver": "2.3.7", + "uuid": "ffc57d0c-40f5-4cae-a612-24c5645097aa", + "importer": "texture", + "type": "sprite", + "wrapMode": "clamp", + "filterMode": "bilinear", + "premultiplyAlpha": false, + "genMipmaps": false, + "packable": true, + "width": 48, + "height": 54, + "platformSettings": {}, + "subMetas": { + "0": { + "ver": "1.0.6", + "uuid": "837772ce-3a48-4bcb-9ba2-2bda8204fd74", + "importer": "sprite-frame", + "rawTextureUuid": "ffc57d0c-40f5-4cae-a612-24c5645097aa", + "trimType": "auto", + "trimThreshold": 1, + "rotated": false, + "offsetX": 0, + "offsetY": 0, + "trimX": 0, + "trimY": 2, + "width": 48, + "height": 50, + "rawWidth": 48, + "rawHeight": 54, + "borderTop": 0, + "borderBottom": 0, + "borderLeft": 0, + "borderRight": 0, + "subMetas": {} + } + } +} \ No newline at end of file diff --git a/assets/seven_day_gift/texture/道具数量(中)/1.png b/assets/seven_day_gift/texture/道具数量(中)/1.png new file mode 100644 index 00000000..5c7dbb1c Binary files /dev/null and b/assets/seven_day_gift/texture/道具数量(中)/1.png differ diff --git a/assets/seven_day_gift/texture/道具数量(中)/1.png.meta b/assets/seven_day_gift/texture/道具数量(中)/1.png.meta new file mode 100644 index 00000000..a49b69eb --- /dev/null +++ b/assets/seven_day_gift/texture/道具数量(中)/1.png.meta @@ -0,0 +1,38 @@ +{ + "ver": "2.3.7", + "uuid": "2e381559-ad49-45da-9aab-a57d163811ca", + "importer": "texture", + "type": "sprite", + "wrapMode": "clamp", + "filterMode": "bilinear", + "premultiplyAlpha": false, + "genMipmaps": false, + "packable": true, + "width": 48, + "height": 54, + "platformSettings": {}, + "subMetas": { + "1": { + "ver": "1.0.6", + "uuid": "b07ae691-b024-416b-a59d-a51a7c496270", + "importer": "sprite-frame", + "rawTextureUuid": "2e381559-ad49-45da-9aab-a57d163811ca", + "trimType": "auto", + "trimThreshold": 1, + "rotated": false, + "offsetX": -0.5, + "offsetY": 0.5, + "trimX": 8, + "trimY": 2, + "width": 31, + "height": 49, + "rawWidth": 48, + "rawHeight": 54, + "borderTop": 0, + "borderBottom": 0, + "borderLeft": 0, + "borderRight": 0, + "subMetas": {} + } + } +} \ No newline at end of file diff --git a/assets/seven_day_gift/texture/道具数量(中)/2.png b/assets/seven_day_gift/texture/道具数量(中)/2.png new file mode 100644 index 00000000..eaebb9d2 Binary files /dev/null and b/assets/seven_day_gift/texture/道具数量(中)/2.png differ diff --git a/assets/seven_day_gift/texture/道具数量(中)/2.png.meta b/assets/seven_day_gift/texture/道具数量(中)/2.png.meta new file mode 100644 index 00000000..26d7c134 --- /dev/null +++ b/assets/seven_day_gift/texture/道具数量(中)/2.png.meta @@ -0,0 +1,38 @@ +{ + "ver": "2.3.7", + "uuid": "b5d4a7a0-1808-422d-bcd7-2d4a490bcbd1", + "importer": "texture", + "type": "sprite", + "wrapMode": "clamp", + "filterMode": "bilinear", + "premultiplyAlpha": false, + "genMipmaps": false, + "packable": true, + "width": 48, + "height": 54, + "platformSettings": {}, + "subMetas": { + "2": { + "ver": "1.0.6", + "uuid": "07f23729-1d5a-4a02-83c3-8f8f0d184a54", + "importer": "sprite-frame", + "rawTextureUuid": "b5d4a7a0-1808-422d-bcd7-2d4a490bcbd1", + "trimType": "auto", + "trimThreshold": 1, + "rotated": false, + "offsetX": -0.5, + "offsetY": 0, + "trimX": 2, + "trimY": 2, + "width": 43, + "height": 50, + "rawWidth": 48, + "rawHeight": 54, + "borderTop": 0, + "borderBottom": 0, + "borderLeft": 0, + "borderRight": 0, + "subMetas": {} + } + } +} \ No newline at end of file diff --git a/assets/seven_day_gift/texture/道具数量(中)/3.png b/assets/seven_day_gift/texture/道具数量(中)/3.png new file mode 100644 index 00000000..5a4bac03 Binary files /dev/null and b/assets/seven_day_gift/texture/道具数量(中)/3.png differ diff --git a/assets/seven_day_gift/texture/道具数量(中)/3.png.meta b/assets/seven_day_gift/texture/道具数量(中)/3.png.meta new file mode 100644 index 00000000..cc357618 --- /dev/null +++ b/assets/seven_day_gift/texture/道具数量(中)/3.png.meta @@ -0,0 +1,38 @@ +{ + "ver": "2.3.7", + "uuid": "d305beff-f9c5-4622-afcf-a9dd213972a7", + "importer": "texture", + "type": "sprite", + "wrapMode": "clamp", + "filterMode": "bilinear", + "premultiplyAlpha": false, + "genMipmaps": false, + "packable": true, + "width": 48, + "height": 54, + "platformSettings": {}, + "subMetas": { + "3": { + "ver": "1.0.6", + "uuid": "181f0a08-d1b8-4edb-ad3e-3895e5d64d78", + "importer": "sprite-frame", + "rawTextureUuid": "d305beff-f9c5-4622-afcf-a9dd213972a7", + "trimType": "auto", + "trimThreshold": 1, + "rotated": false, + "offsetX": 0, + "offsetY": 0, + "trimX": 4, + "trimY": 2, + "width": 40, + "height": 50, + "rawWidth": 48, + "rawHeight": 54, + "borderTop": 0, + "borderBottom": 0, + "borderLeft": 0, + "borderRight": 0, + "subMetas": {} + } + } +} \ No newline at end of file diff --git a/assets/seven_day_gift/texture/道具数量(中)/4.png b/assets/seven_day_gift/texture/道具数量(中)/4.png new file mode 100644 index 00000000..64320fb6 Binary files /dev/null and b/assets/seven_day_gift/texture/道具数量(中)/4.png differ diff --git a/assets/seven_day_gift/texture/道具数量(中)/4.png.meta b/assets/seven_day_gift/texture/道具数量(中)/4.png.meta new file mode 100644 index 00000000..ba7dd028 --- /dev/null +++ b/assets/seven_day_gift/texture/道具数量(中)/4.png.meta @@ -0,0 +1,38 @@ +{ + "ver": "2.3.7", + "uuid": "72909e1f-2743-47d4-964c-6d0a63084300", + "importer": "texture", + "type": "sprite", + "wrapMode": "clamp", + "filterMode": "bilinear", + "premultiplyAlpha": false, + "genMipmaps": false, + "packable": true, + "width": 48, + "height": 54, + "platformSettings": {}, + "subMetas": { + "4": { + "ver": "1.0.6", + "uuid": "bafe0f35-0ace-49a4-b035-8fe047e26339", + "importer": "sprite-frame", + "rawTextureUuid": "72909e1f-2743-47d4-964c-6d0a63084300", + "trimType": "auto", + "trimThreshold": 1, + "rotated": false, + "offsetX": -0.5, + "offsetY": 0.5, + "trimX": 0, + "trimY": 1, + "width": 47, + "height": 51, + "rawWidth": 48, + "rawHeight": 54, + "borderTop": 0, + "borderBottom": 0, + "borderLeft": 0, + "borderRight": 0, + "subMetas": {} + } + } +} \ No newline at end of file diff --git a/assets/seven_day_gift/texture/道具数量(中)/5.png b/assets/seven_day_gift/texture/道具数量(中)/5.png new file mode 100644 index 00000000..8fc0c95c Binary files /dev/null and b/assets/seven_day_gift/texture/道具数量(中)/5.png differ diff --git a/assets/seven_day_gift/texture/道具数量(中)/5.png.meta b/assets/seven_day_gift/texture/道具数量(中)/5.png.meta new file mode 100644 index 00000000..6c710fdd --- /dev/null +++ b/assets/seven_day_gift/texture/道具数量(中)/5.png.meta @@ -0,0 +1,38 @@ +{ + "ver": "2.3.7", + "uuid": "4437f9c4-0bbe-4bdc-b806-249eacfe5520", + "importer": "texture", + "type": "sprite", + "wrapMode": "clamp", + "filterMode": "bilinear", + "premultiplyAlpha": false, + "genMipmaps": false, + "packable": true, + "width": 48, + "height": 54, + "platformSettings": {}, + "subMetas": { + "5": { + "ver": "1.0.6", + "uuid": "f44c5687-d99f-421e-a179-6dd6806fed68", + "importer": "sprite-frame", + "rawTextureUuid": "4437f9c4-0bbe-4bdc-b806-249eacfe5520", + "trimType": "auto", + "trimThreshold": 1, + "rotated": false, + "offsetX": -0.5, + "offsetY": 0, + "trimX": 2, + "trimY": 2, + "width": 43, + "height": 50, + "rawWidth": 48, + "rawHeight": 54, + "borderTop": 0, + "borderBottom": 0, + "borderLeft": 0, + "borderRight": 0, + "subMetas": {} + } + } +} \ No newline at end of file diff --git a/assets/seven_day_gift/texture/道具数量(中)/6.png b/assets/seven_day_gift/texture/道具数量(中)/6.png new file mode 100644 index 00000000..6d99cf20 Binary files /dev/null and b/assets/seven_day_gift/texture/道具数量(中)/6.png differ diff --git a/assets/seven_day_gift/texture/道具数量(中)/6.png.meta b/assets/seven_day_gift/texture/道具数量(中)/6.png.meta new file mode 100644 index 00000000..39279dac --- /dev/null +++ b/assets/seven_day_gift/texture/道具数量(中)/6.png.meta @@ -0,0 +1,38 @@ +{ + "ver": "2.3.7", + "uuid": "e6af91c4-cfe4-4a6d-afee-8d989ed3bd65", + "importer": "texture", + "type": "sprite", + "wrapMode": "clamp", + "filterMode": "bilinear", + "premultiplyAlpha": false, + "genMipmaps": false, + "packable": true, + "width": 48, + "height": 54, + "platformSettings": {}, + "subMetas": { + "6": { + "ver": "1.0.6", + "uuid": "c2ed5f1c-0be6-4af7-b945-b78ef8a0c9f5", + "importer": "sprite-frame", + "rawTextureUuid": "e6af91c4-cfe4-4a6d-afee-8d989ed3bd65", + "trimType": "auto", + "trimThreshold": 1, + "rotated": false, + "offsetX": 0, + "offsetY": 0, + "trimX": 2, + "trimY": 2, + "width": 44, + "height": 50, + "rawWidth": 48, + "rawHeight": 54, + "borderTop": 0, + "borderBottom": 0, + "borderLeft": 0, + "borderRight": 0, + "subMetas": {} + } + } +} \ No newline at end of file diff --git a/assets/seven_day_gift/texture/道具数量(中)/7.png b/assets/seven_day_gift/texture/道具数量(中)/7.png new file mode 100644 index 00000000..a8ab72be Binary files /dev/null and b/assets/seven_day_gift/texture/道具数量(中)/7.png differ diff --git a/assets/seven_day_gift/texture/道具数量(中)/7.png.meta b/assets/seven_day_gift/texture/道具数量(中)/7.png.meta new file mode 100644 index 00000000..4d73efc0 --- /dev/null +++ b/assets/seven_day_gift/texture/道具数量(中)/7.png.meta @@ -0,0 +1,38 @@ +{ + "ver": "2.3.7", + "uuid": "04605f4d-0ef3-4093-baa7-d59f6b51db54", + "importer": "texture", + "type": "sprite", + "wrapMode": "clamp", + "filterMode": "bilinear", + "premultiplyAlpha": false, + "genMipmaps": false, + "packable": true, + "width": 48, + "height": 54, + "platformSettings": {}, + "subMetas": { + "7": { + "ver": "1.0.6", + "uuid": "2b2d4cfa-4602-48af-a33b-f8b1f504b668", + "importer": "sprite-frame", + "rawTextureUuid": "04605f4d-0ef3-4093-baa7-d59f6b51db54", + "trimType": "auto", + "trimThreshold": 1, + "rotated": false, + "offsetX": 0, + "offsetY": 0, + "trimX": 0, + "trimY": 2, + "width": 48, + "height": 50, + "rawWidth": 48, + "rawHeight": 54, + "borderTop": 0, + "borderBottom": 0, + "borderLeft": 0, + "borderRight": 0, + "subMetas": {} + } + } +} \ No newline at end of file diff --git a/assets/seven_day_gift/texture/道具数量(中)/8.png b/assets/seven_day_gift/texture/道具数量(中)/8.png new file mode 100644 index 00000000..aa1b4eae Binary files /dev/null and b/assets/seven_day_gift/texture/道具数量(中)/8.png differ diff --git a/assets/seven_day_gift/texture/道具数量(中)/8.png.meta b/assets/seven_day_gift/texture/道具数量(中)/8.png.meta new file mode 100644 index 00000000..7b038496 --- /dev/null +++ b/assets/seven_day_gift/texture/道具数量(中)/8.png.meta @@ -0,0 +1,38 @@ +{ + "ver": "2.3.7", + "uuid": "3cc1e683-46a5-4e76-bd64-98320a7622e1", + "importer": "texture", + "type": "sprite", + "wrapMode": "clamp", + "filterMode": "bilinear", + "premultiplyAlpha": false, + "genMipmaps": false, + "packable": true, + "width": 48, + "height": 54, + "platformSettings": {}, + "subMetas": { + "8": { + "ver": "1.0.6", + "uuid": "59e86104-9963-4415-9377-e17b2215de5f", + "importer": "sprite-frame", + "rawTextureUuid": "3cc1e683-46a5-4e76-bd64-98320a7622e1", + "trimType": "auto", + "trimThreshold": 1, + "rotated": false, + "offsetX": 0, + "offsetY": 0, + "trimX": 2, + "trimY": 1, + "width": 44, + "height": 52, + "rawWidth": 48, + "rawHeight": 54, + "borderTop": 0, + "borderBottom": 0, + "borderLeft": 0, + "borderRight": 0, + "subMetas": {} + } + } +} \ No newline at end of file diff --git a/assets/seven_day_gift/texture/道具数量(中)/9.png b/assets/seven_day_gift/texture/道具数量(中)/9.png new file mode 100644 index 00000000..12bcbac8 Binary files /dev/null and b/assets/seven_day_gift/texture/道具数量(中)/9.png differ diff --git a/assets/seven_day_gift/texture/道具数量(中)/9.png.meta b/assets/seven_day_gift/texture/道具数量(中)/9.png.meta new file mode 100644 index 00000000..fe46e048 --- /dev/null +++ b/assets/seven_day_gift/texture/道具数量(中)/9.png.meta @@ -0,0 +1,38 @@ +{ + "ver": "2.3.7", + "uuid": "446e0a93-0a2f-4acb-b513-9b487c89cc13", + "importer": "texture", + "type": "sprite", + "wrapMode": "clamp", + "filterMode": "bilinear", + "premultiplyAlpha": false, + "genMipmaps": false, + "packable": true, + "width": 48, + "height": 54, + "platformSettings": {}, + "subMetas": { + "9": { + "ver": "1.0.6", + "uuid": "c647a240-a9d5-46b9-bd73-7d36bf57ed6b", + "importer": "sprite-frame", + "rawTextureUuid": "446e0a93-0a2f-4acb-b513-9b487c89cc13", + "trimType": "auto", + "trimThreshold": 1, + "rotated": false, + "offsetX": 0, + "offsetY": 0, + "trimX": 2, + "trimY": 1, + "width": 44, + "height": 52, + "rawWidth": 48, + "rawHeight": 54, + "borderTop": 0, + "borderBottom": 0, + "borderLeft": 0, + "borderRight": 0, + "subMetas": {} + } + } +} \ No newline at end of file diff --git a/assets/seven_day_gift/texture/道具数量(中)/chenhao.png b/assets/seven_day_gift/texture/道具数量(中)/chenhao.png new file mode 100644 index 00000000..508d8fab Binary files /dev/null and b/assets/seven_day_gift/texture/道具数量(中)/chenhao.png differ diff --git a/assets/seven_day_gift/texture/道具数量(中)/chenhao.png.meta b/assets/seven_day_gift/texture/道具数量(中)/chenhao.png.meta new file mode 100644 index 00000000..bf6e5ea2 --- /dev/null +++ b/assets/seven_day_gift/texture/道具数量(中)/chenhao.png.meta @@ -0,0 +1,38 @@ +{ + "ver": "2.3.7", + "uuid": "5eeb9cc8-7472-40ee-9227-afe5486b5422", + "importer": "texture", + "type": "sprite", + "wrapMode": "clamp", + "filterMode": "bilinear", + "premultiplyAlpha": false, + "genMipmaps": false, + "packable": true, + "width": 31, + "height": 32, + "platformSettings": {}, + "subMetas": { + "chenhao": { + "ver": "1.0.6", + "uuid": "6fe4bf3a-562d-4fc9-82bc-0378b0041112", + "importer": "sprite-frame", + "rawTextureUuid": "5eeb9cc8-7472-40ee-9227-afe5486b5422", + "trimType": "auto", + "trimThreshold": 1, + "rotated": false, + "offsetX": 0, + "offsetY": 0, + "trimX": 0, + "trimY": 0, + "width": 31, + "height": 32, + "rawWidth": 31, + "rawHeight": 32, + "borderTop": 0, + "borderBottom": 0, + "borderLeft": 0, + "borderRight": 0, + "subMetas": {} + } + } +} \ No newline at end of file diff --git a/assets/seven_day_gift/texture/道具数量(中)/jiahao.png b/assets/seven_day_gift/texture/道具数量(中)/jiahao.png new file mode 100644 index 00000000..82740a42 Binary files /dev/null and b/assets/seven_day_gift/texture/道具数量(中)/jiahao.png differ diff --git a/assets/seven_day_gift/texture/道具数量(中)/jiahao.png.meta b/assets/seven_day_gift/texture/道具数量(中)/jiahao.png.meta new file mode 100644 index 00000000..837eed02 --- /dev/null +++ b/assets/seven_day_gift/texture/道具数量(中)/jiahao.png.meta @@ -0,0 +1,38 @@ +{ + "ver": "2.3.7", + "uuid": "d6a6089e-015e-460a-8da9-122935a80111", + "importer": "texture", + "type": "sprite", + "wrapMode": "clamp", + "filterMode": "bilinear", + "premultiplyAlpha": false, + "genMipmaps": false, + "packable": true, + "width": 39, + "height": 39, + "platformSettings": {}, + "subMetas": { + "jiahao": { + "ver": "1.0.6", + "uuid": "5929b033-2102-4f34-972b-1db65e57b0b1", + "importer": "sprite-frame", + "rawTextureUuid": "d6a6089e-015e-460a-8da9-122935a80111", + "trimType": "auto", + "trimThreshold": 1, + "rotated": false, + "offsetX": 0, + "offsetY": 0, + "trimX": 0, + "trimY": 0, + "width": 39, + "height": 39, + "rawWidth": 39, + "rawHeight": 39, + "borderTop": 0, + "borderBottom": 0, + "borderLeft": 0, + "borderRight": 0, + "subMetas": {} + } + } +} \ No newline at end of file diff --git a/assets/seven_day_gift/texture/道具数量(中)/min.png b/assets/seven_day_gift/texture/道具数量(中)/min.png new file mode 100644 index 00000000..27154406 Binary files /dev/null and b/assets/seven_day_gift/texture/道具数量(中)/min.png differ diff --git a/assets/seven_day_gift/texture/道具数量(中)/min.png.meta b/assets/seven_day_gift/texture/道具数量(中)/min.png.meta new file mode 100644 index 00000000..38e8764c --- /dev/null +++ b/assets/seven_day_gift/texture/道具数量(中)/min.png.meta @@ -0,0 +1,38 @@ +{ + "ver": "2.3.7", + "uuid": "fdefa0a7-6a0d-46eb-acf1-a9640e121892", + "importer": "texture", + "type": "sprite", + "wrapMode": "clamp", + "filterMode": "bilinear", + "premultiplyAlpha": false, + "genMipmaps": false, + "packable": true, + "width": 76, + "height": 40, + "platformSettings": {}, + "subMetas": { + "min": { + "ver": "1.0.6", + "uuid": "f62e9f2e-72b3-4f73-8ecd-d0fd2a2e1b4d", + "importer": "sprite-frame", + "rawTextureUuid": "fdefa0a7-6a0d-46eb-acf1-a9640e121892", + "trimType": "auto", + "trimThreshold": 1, + "rotated": false, + "offsetX": 0, + "offsetY": 0, + "trimX": 0, + "trimY": 0, + "width": 76, + "height": 40, + "rawWidth": 76, + "rawHeight": 40, + "borderTop": 0, + "borderBottom": 0, + "borderLeft": 0, + "borderRight": 0, + "subMetas": {} + } + } +} \ No newline at end of file diff --git a/assets/testSeven/SevenApiPanel.ts b/assets/testSeven/SevenApiPanel.ts new file mode 100644 index 00000000..c0739efe --- /dev/null +++ b/assets/testSeven/SevenApiPanel.ts @@ -0,0 +1,221 @@ +import Utils from "../Script/module/Pay/Utils"; + +const { ccclass } = cc._decorator; + +/** 预览和开发版共用的接口面板;独立预览可手动填写登录信息。 */ +@ccclass +export default class SevenApiPanel extends cc.Component { + public preview: (info: any) => void = null; // 独立场景:仅模拟奖励。 + public testHome: (info: any) => void = null; // 开发版主页:进入真实活动流程。 + private panel: cc.Node; + private uid: cc.EditBox; + private token: cc.EditBox; + private output: cc.Label; + private summary: cc.Label; + private scroll: cc.ScrollView; + private request: XMLHttpRequest = null; + private response: any = null; + private responseUid = ""; + + // 仅预览和微信开发版创建面板,提供查询、模拟和主页测试入口。 + start() { + const fx = (cc as any).fx; + if (!CC_PREVIEW && !(fx && fx.GameTool && fx.GameTool.getWechatGameVersion() === "开发版")) return; + this.panel = new cc.Node("SevenApiPanel"); + this.node.addChild(this.panel); + this.panel.zIndex = 2000; + this.panel.setContentSize(1040, 1840); + const background = this.panel.addComponent(cc.Graphics); + background.fillColor = cc.color(22, 29, 43, 255); + background.rect(-520, -920, 1040, 1840); + background.fill(); + this.panel.addComponent(cc.BlockInputEvents); + this.label("七日活动 · 测试服接口", 0, 855, 32); + this.label("POST " + Utils.testHttpip + "signInActivityInfo", 0, 798, 23); + this.label("请求可能更新后端签到记录;预览奖励不增加资产", 0, 749, 24); + this.uid = this.input("测试账号 UID", 660, false); + this.token = this.input("登录 token(不保存)", 562, true); + this.button("读取当前登录", -315, 462, () => this.readLogin()); + this.button("发送查询", 0, 462, () => this.send()); + this.button("关闭面板", 315, 462, () => { this.panel.active = false; }); + this.button(this.testHome ? "测试主页自动弹窗" : "预览返回数据", -250, 380, () => { + if (this.testHome) { + const fx = (cc as any).fx; + const current = fx && fx.GameConfig && fx.GameConfig.GM_INFO; + if (!current || !current.uid || !current.token) return this.message("主页尚未登录完成"); + if (String(current.uid) !== this.uid.string.trim()) return this.message("主页流程使用当前登录账号,请先点读取当前登录"); + if (!this.response || this.responseUid !== String(current.uid)) return this.message("请先发送查询,取得当前登录账号的活动数据"); + if (CC_PREVIEW) return this.message("浏览器预览可查询接口;真实资产同步请在微信开发版 HomeScene 测试"); + this.panel.active = false; + this.testHome(JSON.parse(JSON.stringify(this.response))); + return; + } + if (!this.response || !Array.isArray(this.response.rewards)) return this.message("请先取得有效活动数据"); + if (this.preview) { + this.preview(JSON.parse(JSON.stringify(this.response))); + this.panel.active = false; + } + }); + this.button("清除该账号弹窗记录", 220, 380, () => this.clearPopup()); + this.summary = this.label("输入测试服 UID 和 token 后点击发送查询", 0, 265, 24); + this.summary.node.setContentSize(960, 140); + this.summary.overflow = cc.Label.Overflow.RESIZE_HEIGHT; + + const viewport = new cc.Node("ResponseScroll"); + this.panel.addChild(viewport); + viewport.setPosition(0, -320); + viewport.setContentSize(970, 1050); + viewport.addComponent(cc.Mask); + this.scroll = viewport.addComponent(cc.ScrollView); + this.scroll.horizontal = false; + this.scroll.vertical = true; + const content = new cc.Node("Response"); + viewport.addChild(content); + content.setAnchorPoint(0.5, 1); + content.setPosition(0, 525); + content.setContentSize(950, 1050); + this.scroll.content = content; + this.output = content.addComponent(cc.Label); + this.output.fontSize = 23; + this.output.lineHeight = 30; + this.output.horizontalAlign = cc.Label.HorizontalAlign.LEFT; + this.output.verticalAlign = cc.Label.VerticalAlign.TOP; + this.output.overflow = cc.Label.Overflow.RESIZE_HEIGHT; + this.output.string = "响应 JSON 将显示在这里,可上下滚动。"; + this.readLogin(); + this.panel.active = false; + const toggle = new cc.Node("OpenSevenApi"); + this.node.addChild(toggle); + toggle.zIndex = 1500; + toggle.setPosition(0, 925); + toggle.setContentSize(280, 60); + const title = toggle.addComponent(cc.Label); + title.string = "打开接口测试面板"; + title.fontSize = 28; + toggle.on(cc.Node.EventType.TOUCH_END, () => { this.panel.active = true; this.readLogin(); }); + } + + // 请求测试服并展示原始响应,保留查询时的 UID 供后续校验。 + private send() { + if (this.request) return this.message("请求中,请等待本次返回"); + const uid = this.uid.string.trim(); + const token = this.token.string.trim(); + if (!uid || !token) return this.message("请填写 UID 和 token"); + this.response = null; + this.responseUid = ""; + const xhr = cc.loader.getXMLHttpRequest(); + this.request = xhr; + const started = Date.now(); + let finished = false; + const finish = (error?: string) => { + if (finished) return; + finished = true; + this.request = null; + if (!cc.isValid(this.node, true)) return; + const text = xhr.responseText || ""; + this.output.string = text || error || "空响应"; + if (error) this.message(error); + else { + try { + const result = JSON.parse(text); + this.output.string = JSON.stringify(result, null, 2); + console.log("[testSeven][signInActivityInfo] 回复", this.output.string); + this.responseUid = uid; + this.response = result.code === 1 && result.data && typeof result.data === "object" ? result.data : null; + const data = this.response; + this.message("HTTP " + xhr.status + " | " + (Date.now() - started) + " ms | code=" + result.code + "\n" + + (data ? "status=" + data.status + " canClaim=" + data.canClaim + " todayClaimed=" + data.todayClaimed + "\n" + this.popupRecord() : String(result.msg || "没有活动数据"))); + } catch (_) { this.message("HTTP " + xhr.status + ":响应不是有效 JSON"); } + } + this.scheduleOnce(() => this.scroll.scrollToTop(0), 0); + }; + xhr.onload = () => finish(); + xhr.onerror = () => finish("网络失败,请检查测试服地址、网络或浏览器 CORS 配置"); + xhr.ontimeout = () => finish("请求超时(8 秒),可再次手动请求"); + xhr.onabort = () => finish("请求已取消"); + try { + xhr.open("POST", Utils.testHttpip + "signInActivityInfo", true); + xhr.timeout = 8000; + xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); + this.message("请求中…"); + xhr.send("uid=" + encodeURIComponent(uid) + "&token=" + encodeURIComponent(token) + "&isDebug=false"); + } catch (error) { finish("请求失败:" + error.message); } + } + + // 读取当前登录信息填入输入框,token 不写入存储。 + private readLogin() { + const fx = (cc as any).fx; + const info = fx && fx.GameConfig && fx.GameConfig.GM_INFO; + this.uid.string = info && info.uid || ""; + this.token.string = info && info.token || ""; + if (!this.uid.string) { + try { this.uid.string = String(JSON.parse(cc.sys.localStorage.getItem("uid")) || ""); } catch (_) { } + } + if (this.summary) this.message(this.token.string ? "已读取当前登录信息,请确认该账号属于测试服" : "未取得登录 token,请从测试账号登录结果复制填入"); + } + + // 使用最近一次查询账号和活动生成弹窗记录键。 + private popupKey(): string { + return "seven_day_gift_auto_open_v1:" + this.responseUid + ":" + this.response.activityId; + } + + // 展示当前预览环境保存的自动弹窗记录。 + private popupRecord(): string { + return "本地当天弹窗记录:" + (cc.sys.localStorage.getItem(this.popupKey()) || "无"); + } + + // 仅清除已查询账号的本地弹窗记录,保留奖励和服务端状态。 + private clearPopup() { + if (!this.response || !this.response.activityId || !this.responseUid) return this.message("请先查询该测试账号的活动"); + cc.sys.localStorage.removeItem(this.popupKey()); + this.message("已清除查询账号的本地弹窗记录;未清除资产防重记录或后端签到状态"); + } + + // 更新接口操作提示。 + private message(text: string) { this.summary.string = text; } + + // 创建接口面板中的说明文字。 + private label(text: string, x: number, y: number, size: number): cc.Label { + const node = new cc.Node("Text"); + this.panel.addChild(node); + node.setPosition(x, y); + const label = node.addComponent(cc.Label); + label.fontSize = size; + label.lineHeight = size + 8; + label.string = text; + return label; + } + + // 创建单行输入框,token 使用密码样式。 + private input(placeholder: string, y: number, password: boolean): cc.EditBox { + const node = new cc.Node("Input"); + this.panel.addChild(node); + node.setPosition(0, y); + node.setContentSize(920, 72); + const bg = node.addComponent(cc.Graphics); + bg.fillColor = cc.color(52, 65, 84); + bg.rect(-460, -36, 920, 72); + bg.fill(); + const input = node.addComponent(cc.EditBox); + input.inputMode = cc.EditBox.InputMode.SINGLE_LINE; + input.inputFlag = password ? cc.EditBox.InputFlag.PASSWORD : cc.EditBox.InputFlag.DEFAULT; + input.fontSize = 26; + input.fontColor = cc.Color.WHITE; + input.placeholder = placeholder; + input.placeholderFontSize = 26; + input.maxLength = 4096; + return input; + } + + // 创建接口面板的文字按钮。 + private button(text: string, x: number, y: number, action: () => void) { + const label = this.label(text, x, y, 27); + label.node.color = cc.color(100, 220, 255); + label.node.on(cc.Node.EventType.TOUCH_END, action); + } + + // 销毁面板时取消请求,防止回调访问已销毁节点。 + onDestroy() { + if (this.request) { const xhr = this.request; this.request = null; xhr.onload = xhr.onerror = xhr.ontimeout = xhr.onabort = null; xhr.abort(); } + } +} diff --git a/assets/testSeven/SevenApiPanel.ts.meta b/assets/testSeven/SevenApiPanel.ts.meta new file mode 100644 index 00000000..6d04ebf0 --- /dev/null +++ b/assets/testSeven/SevenApiPanel.ts.meta @@ -0,0 +1,10 @@ +{ + "ver": "1.1.0", + "uuid": "e3a9c35b-0c3f-4609-aa3c-4f719b344e74", + "importer": "typescript", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/assets/testSeven/TestSeven.fire b/assets/testSeven/TestSeven.fire index 5d95a72c..929bc9c4 100644 --- a/assets/testSeven/TestSeven.fire +++ b/assets/testSeven/TestSeven.fire @@ -252,12 +252,6 @@ "__id__": 2 }, "_enabled": true, - "rewardPrefab": { - "__uuid__": "42afc6d4-1976-49c6-9595-91e157447e82" - }, - "rewardFont": { - "__uuid__": "fa9a438e-1f24-47fe-bbcd-b75abcff2ea8" - }, "_id": "f5+OGfobFBVrKoBSWvqhni" } ] \ No newline at end of file diff --git a/assets/testSeven/TestSeven.ts b/assets/testSeven/TestSeven.ts index 304372b9..cc0861e7 100644 --- a/assets/testSeven/TestSeven.ts +++ b/assets/testSeven/TestSeven.ts @@ -1,13 +1,10 @@ -import NumberToImage from "../Script/NumberToImage"; +import SevenApiPanel from "./SevenApiPanel"; -const { ccclass, property } = cc._decorator; +const { ccclass } = 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; @@ -15,15 +12,21 @@ export default class TestSeven extends cc.Component { private busy = false; private failNext = false; + // 仅在编辑器预览加载真实活动 Prefab,并创建测试按钮。 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; + this.button("临时打开 JungleTreasure", 0, 645, () => this.openJunglePreview()); + const apiPanel = this.node.addComponent(SevenApiPanel); + apiPanel.preview = info => { + if (!this.view || this.busy) return this.report("请等待活动资源加载或奖励动画结束"); + this.info = info; + this.open(); + this.report("接口数据模拟 | 点击当天奖励领取,或点击外部只领取,本次不关闭(不增加资产)"); + }; cc.assetManager.loadBundle("seven_day_gift", (error, bundle) => { if (!cc.isValid(this.node, true)) return; if (error) return this.report("分包加载失败:" + error.message); @@ -43,18 +46,80 @@ export default class TestSeven extends cc.Component { 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("点击外部", 330, -755, () => { + if (this.gift.active) this.view.close(); + else this.report("弹窗已关闭,请先重新打开"); + }); this.button("下次失败", -220, -840, () => { this.failNext = true; - this.report("下一次领取将在 1.2 秒后失败,可再次点击重试"); + this.report("下一次领取模拟失败,可再次点击重试"); }); - this.button("重新打开", 0, -840, () => this.open(false)); + this.button("重新打开", 0, -840, () => this.open()); this.button("重置", 220, -840, () => { this.failNext = false; this.select(1); }); + this.button("预览弹出上浮(不领取)", 0, 730, () => { + if (!this.gift.active) this.open(); + const reward = this.info.rewards.find(item => Number(item.day) === Number(this.info.nextRewardDay)); + if (!reward) return; + this.busy = true; + this.view.playRewardEffect(reward.items, reward.day, () => { this.busy = false; }); + }); this.select(1); }); }); } + // 临时查看真实丛林活动界面;使用内存样例和原有动画预览模式。 + private openJunglePreview() { + if (!CC_PREVIEW || this.busy) return; + this.busy = true; + this.report("正在加载 JungleTreasure…"); + cc.assetManager.loadBundle("jungle_treasure", (error, bundle) => { + if (!cc.isValid(this.node, true)) return; + if (error) { this.busy = false; return this.report("丛林分包加载失败:" + error.message); } + bundle.load("prefab/jungle", cc.Prefab, (loadError, prefab: cc.Prefab) => { + if (!cc.isValid(this.node, true)) return; + if (loadError) { this.busy = false; return this.report("丛林界面加载失败:" + loadError.message); } + const node = cc.instantiate(prefab); + node.active = false; + const manager: any = node.getComponent("jungle_Manager"); + if (!manager) { + node.destroy(); + this.busy = false; + return this.report("丛林界面缺少 jungle_Manager 组件"); + } + manager.previewAnimationOnly = true; + // 独立预览场景没有主页音频管理器,当前实例关闭音效。 + manager.anniuMusic = manager.unLockMusic = manager.rewardMusic = null; + this.node.addChild(node); + node.setPosition(0, 0); + node.zIndex = 3000; + const close = () => { + node.destroy(); + this.busy = false; + this.report("已关闭 JungleTreasure,可继续七日活动测试"); + }; + node.on("jungle-close", close, this); + node.on("jungle-complete", close, this); + node.active = true; + const types = ["coin", "freeze", "hammer", "magic_wand", "infinite_health"]; + const config = { + activityId: "testSeven-jungle-preview", endAt: Date.now() + 86400000, + tiers: types.map((type, index) => ({ + id: index + 1, price: index === 2 ? 6 : 0, + rewards: [{ type, count: type === "coin" ? 500 : type === "infinite_health" ? 1800 : 2 }], + })), + }; + if (!manager.setServerData(config, [1, 1, 0, 1, 1])) { + close(); + return this.report("丛林预览数据初始化失败"); + } + manager.openActivity(); + this.report("JungleTreasure 本地界面预览,不发奖、不支付"); + }); + }); + } + + // 生成指定天数和状态的内存样例,不修改账号资产。 private select(day: number, state: string = "active") { if (this.busy) return; const claimedCount = state === "completed" ? 7 : state === "claimed" ? day : day - 1; @@ -63,9 +128,9 @@ export default class TestSeven extends cc.Component { [{ 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 }], + [{ type: "infinite_health", count: 1800 }], // 秒:30 分钟,效果显示 +1800秒。 + [{ type: "hammer", count: 1 }, { type: "freeze", count: 1 }], // 第六天移除魔法棒。 + [{ type: "infinite_health", count: 1800 }, { type: "cat_skin", count: 1, itemId: 1 }], ]; this.info = { serverNow: Date.now(), activityId: "testSeven-memory", status: state === "claimed" ? "active" : state, @@ -75,68 +140,48 @@ export default class TestSeven extends cc.Component { canClaim: state === "active", nextRewardDay: day, rewards: items.map((reward, index) => ({ day: index + 1, items: reward, claimed: index < claimedCount })), }; - this.open(false); + this.open(); } - private open(autoClaim: boolean) { + // 用当前模拟状态重新打开弹窗。 + private open() { if (this.busy || !this.view || !this.info) return; this.bindData(); - this.view.show(autoClaim); + this.view.show(); 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); + const reward = this.info.rewards.find(item => Number(item.day) === Number(this.info.nextRewardDay)); + if (!reward || reward.claimed || !Array.isArray(reward.items) || reward.items.length === 0) { + done(false); + return this.report("当前数据没有可领取奖励,请重新选择模拟天数或查询接口"); + } + if (this.failNext) { + this.failNext = false; + done(false); + return this.report("模拟领取失败,弹窗保留;可点击当天奖励或外部重试"); + } 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); + 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(); + this.view.playRewardEffect(reward.items, reward.day, () => { this.busy = false; done(true); }); + this.report("模拟领取成功(不写入资产):" + reward.items.map(item => item.type + " × " + item.count).join(" / ")); }); } - 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); @@ -149,6 +194,7 @@ export default class TestSeven extends cc.Component { 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); diff --git a/assets/win/prefab/Win.prefab b/assets/win/prefab/Win.prefab index 1ea7eda2..c41e5007 100644 --- a/assets/win/prefab/Win.prefab +++ b/assets/win/prefab/Win.prefab @@ -10648,7 +10648,10 @@ "catUI": { "__uuid__": "ff580ec5-b05e-404f-b9f2-3e6a0fd2863c" }, - "_id": "" + "_id": "", + "cat12": { + "__uuid__": "7d090003-0209-4a00-8b00-000000000209" + } }, { "__type__": "cc.PrefabInfo", @@ -14157,4 +14160,4 @@ "fileId": "", "sync": false } -] \ No newline at end of file +] diff --git a/docs/home-popup-queue.md b/docs/home-popup-queue.md new file mode 100644 index 00000000..a979013b --- /dev/null +++ b/docs/home-popup-queue.md @@ -0,0 +1,13 @@ +# HomeScene 自动弹窗队列 + +脚本目录:`assets/home_popup_queue`。这是主包中的独立代码目录(不是资源 Bundle),供主页和七日活动 Host 共用。不要把它改成远程分包,否则主包静态依赖会破坏按需加载。 + +当前接入的自动展示顺序:七日活动(10)、月卡每日奖励(20)、月卡购买(30)、新手礼包(40)、连胜(50)。数字越小优先级越高。优先级适用于已经进入队列的任务,不抢占正在展示的窗口;接口稍后返回时仍遵守该规则。 + +队列在下一帧选取任务。七日、礼包和连胜资源加载期间占用队列;失败释放占用。展示后检查实际节点状态,活动节点和公共 RewardNode 都关闭才继续。月卡购买的当天记录在节点成功显示后写入。 + +转移窗口显示时暂停队列。已有通行证、丛林宝藏窗口也会阻止队列推进,但它们自己的异步自动打开流程尚未改为入队,因此当前并非所有 HomeScene 弹窗都由本队列调度。分享、订单补发同样仍沿用原流程。后续接入应在资源/接口异步结束时显式结束加载阶段,不能使用固定延时猜测窗口加载完成。 + +手动入口沿用原行为。队列随 Canvas 销毁,未展示任务不会跨场景继续执行。 + +验证:已用引擎桩验证优先级、同键去重、资源加载占用、活动与公共奖励关闭前不推进。需要微信开发版回归:七日与月卡同时满足条件、月卡先打开再返回七日接口、领取后先关闭活动但奖励还在、分包失败、退出 HomeScene。 diff --git a/docs/seven-day-gift-maintenance.md b/docs/seven-day-gift-maintenance.md index 6c2e1458..ebb44121 100644 --- a/docs/seven-day-gift-maintenance.md +++ b/docs/seven-day-gift-maintenance.md @@ -1,3 +1,5 @@ +> 当前交互更新:七日弹窗无关闭按钮;首次外部点击与卡片领取一致,只领取当前奖励,后一次外部点击再关闭。领取及效果期间禁止重复操作。奖励改用 `assets/reward_claim_effect/RewardClaimEffect.ts` 上浮效果;下文旧版自动领取、关闭按钮和公共奖励动画说明仅作历史记录,测试以 `testSeven-local-preview.md` 为准。 + # 七日好礼活动实现与维护说明 本文记录 `feature/seven_day_gift` 分支从 `main` 拉出后完成的七日签到活动相关改动,供后续开发、联调、测试和上线维护使用。 @@ -56,7 +58,7 @@ signInActivityInfo 自动弹窗完成后自动领取当天卡片 ↓ -signInClaim +使用本次查询的 nextRewardDay 对应 items ↓ SevenDayGiftReward.grant ↓ @@ -64,7 +66,7 @@ SevenDayGiftReward.grant ↓ 打开项目公共奖励动画 ↓ -再次查询 signInActivityInfo 刷新最终状态 +在当前快照上更新已领取状态,不重复查询 ``` ## 3. 新增文件 @@ -159,8 +161,7 @@ UI 约定: 七日活动接口封装和 TypeScript 数据契约,负责: -- 查询活动:`signInActivityInfo`。 -- 领取活动:`signInClaim`。 +- 活动查询由主包 `SevenDayGiftBootstrapApi` 调用 `signInActivityInfo`,Bundle 内不再请求活动接口。 - 保存猫皮肤:`setCatArr`。 - 保存普通道具:`userProp`。 - 5.5 秒没有回调时返回统一超时结果,迟到回调不再重复执行业务回调。 @@ -200,16 +201,16 @@ const rewardAmount = item.count !== undefined ? item.count : item.amount; seven_day_gift_processed_claims_v1 ``` -记录键为 `:`。同一个 `claimId` 在同一设备上再次处理时直接视为成功,不重复增加资产。 +记录键为 `:`;这里的 `claimId` 是前端用 `[activityId, startAt, nextRewardDay]` 序列化生成的本地同步标识,接口不需要返回该字段。同一个 `claimId` 在同一设备上再次处理时直接视为成功,不重复增加资产。 -注意:这个记录是客户端本地幂等保护,不能代替服务端 `signInClaim` 的幂等校验。清缓存或换设备后本地记录会消失,后端仍必须保证同一用户、同一天、同一领取不能重复发放。 +注意:这个记录是客户端本地幂等保护,不能代替服务端活动领取的幂等校验。清缓存或换设备后本地记录会消失,后端仍必须保证同一用户、同一天、同一领取不能重复发放。 ### 3.5 `assets/seven_day_gift/SevenDayGiftRuntime.ts` Bundle 内的活动业务控制器,只有 Bundle 加载后才进入内存,负责: -- 领取 `signInClaim`,并交给 `SevenDayGiftReward` 同步本地资产及对应资产接口。 -- 更新领取状态、再次查询活动、调用项目公共奖励动画。 +- 直接消费主页查询结果中的当日奖励,交给 `SevenDayGiftReward` 同步本地资产及对应资产接口。 +- 更新当前快照的领取状态、调用项目公共奖励动画;不再发起额外活动请求。 - 管理主页 Loading、错误提示和领取失败后的重试状态。 - 销毁时清理 Loading 和运行状态。 @@ -259,28 +260,15 @@ Bundle 内的活动业务控制器,只有 Bundle 加载后才进入内存, 活动自动弹窗要求活动处于可见状态、`canClaim === true` 且当天未弹过。当前版本没有手动入口。 -### 4.2 领取活动 `signInClaim` +### 4.2 查询结果直接驱动领取 -请求: +不再使用独立领取接口。`signInActivityInfo` 的请求携带 uid,由公共 POST 层补 token,不传 gameName。未达到 23 关时 `code: 1, data: false`,主页不弹窗。 -```json -{ - "uid": "用户UID" -} -``` +达到条件后,使用查询响应中的 `nextRewardDay` 在 `rewards` 中找到对应 `day` 的 `items`。自动弹窗动画结束后同步这些奖励,完成后本地更新 claimed、claimedCount、todayClaimed、canClaim 和第七天 completed 状态,再播放公共奖励动画。领取过程不重新请求活动接口。 -主要返回字段: +每天自动弹窗记录按 UID、活动 ID 和服务器 UTC+8 日期隔离。同步失败允许在当前弹窗点击当天卡片重试,但不清除当天已弹记录;关闭后当天不会再次自动弹出。弹窗打开期间切回前台不重复查询。 -| 字段 | 含义 | -| --- | --- | -| `claimId` | 本次领取唯一标识,用于客户端和服务端幂等 | -| `rewardDay` | 本次领取第几天 | -| `rewards` | 实际发放的奖励数组 | -| `claimedCount` | 领取后的总进度 | -| `todayClaimed` | 领取后应为真 | -| `completed` | 是否已经领满 7 天 | - -后端必须让同一个领取请求具备幂等性。客户端成功收到领取结果后才执行本地发奖,随后再次查询活动状态。 +当前沿用客户端增加资产并调用资产保存接口的方式;查询响应必须是可消费的领取快照。后端若已经直接增加资产,必须改为读取资产最终值,不能再使用客户端累加。服务器需要维护跨日进度和幂等;前端不会把自己计算的 claimedCount 写回活动接口。 ### 4.3 当前奖励配置 @@ -293,7 +281,7 @@ Bundle 内的活动业务控制器,只有 Bundle 加载后才进入内存, | 3 | 无限体力 1800 秒(30 分钟) | | 4 | 冻结 ×1、魔法棒 ×1 | | 5 | 金币 ×600 | -| 6 | 锤子 ×1、冻结 ×1、魔法棒 ×1 | +| 6 | 锤子 ×1、冻结 ×1(策划调整:删除魔法棒,服务端待同步确认) | | 7 | 无限体力 3600 秒(1 小时)、猫皮肤 `itemId=12` ×1 | 正式奖励完全以服务端 `rewards` 为准,前端不再维护模拟奖励配置。 @@ -384,7 +372,6 @@ Bundle 内的活动业务控制器,只有 Bundle 加载后才进入内存, ```text signInActivityInfo -signInClaim userCoin / userProp / userPower / setCatArr ``` @@ -394,7 +381,7 @@ userCoin / userProp / userPower / setCatArr | --- | --- | | 达到 23 关但没有自动弹 | `signInActivityInfo` 的 `activityId`、`triggerReached`、`status`、`endAt`、`canClaim`;再查服务器 `levelAmount` 和本地当天自动弹记录 | | 每次回主页都重复弹 | 是否每次清缓存;UID/activityId 是否变化;自动弹出本地记录是否被其他逻辑清除 | -| 卡片不能领取 | `canClaim`、`nextRewardDay`、当前卡片 `claimed`;查看 `signInClaim` 返回 | +| 卡片不能领取 | `canClaim`、`nextRewardDay`、当前卡片 `claimed`;查看 `signInActivityInfo` 返回及资产同步错误 | | 奖励数量为 0 | 后端是否返回 `count` 或 `amount`;值是否能转换为数字 | | 猫皮肤动画不显示 | `Reward.ts` 是否收到 `spriteFrame`;活动猫图片 UUID 是否有效 | | 猫皮肤没有保存 | 查看 `setCatArr` 的请求、`itemId` 和返回内容 | @@ -448,7 +435,7 @@ userCoin / userProp / userPower / setCatArr - 同一设备切换两个 UID。 - 后端缺少部分每日任务字段时主页不崩溃。 -其中“后端已经确认领取,但客户端资产同步尚未完成时退出”是需要重点关注的边界。当前流程先完成 `signInClaim`,再更新金币、道具、无限体力和猫皮肤;服务端必须提供可查询、可重试且幂等的最终结果,不能只依赖本地领取标记。 +其中“后端已经确认领取,但客户端资产同步尚未完成时退出”是需要重点关注的边界。当前流程消费 `signInActivityInfo` 返回的奖励快照,再更新金币、道具、无限体力和猫皮肤;服务端必须提供可查询、可重试且幂等的最终结果,不能只依赖本地领取标记。 ## 9. 上线流程 diff --git a/docs/testSeven-local-preview.md b/docs/testSeven-local-preview.md index ddd15fc1..573d85d9 100644 --- a/docs/testSeven-local-preview.md +++ b/docs/testSeven-local-preview.md @@ -1,26 +1,39 @@ # 七日活动本地预览 -使用 Cocos Creator 2.4.15 打开项目,等待资源导入完成,双击 `assets/testSeven/TestSeven.fire`,选择「预览当前场景」。无需登录或测试 UID。 +使用 Cocos Creator 2.4.15 打开 `assets/testSeven/TestSeven.fire`,选择「预览当前场景」。无需登录或测试 UID。 -场景动态加载真实 `seven_day_gift` Bundle 的 `prefab/sevenDayGift`。底部按钮可选择第 1~7 天、今日已领、全部完成、已过期,或重置数据。点击亮起的奖励卡片模拟领取;「自动领取」重新打开当前弹窗并自动领取。「下次失败」可模拟一次失败,再点击奖励重试。模拟响应延迟 1.2 秒,期间可检查连续点击及关闭拦截。关闭后用「重新打开」查看当前状态。 - -模拟数据仅在内存中保存,重启预览即重置;奖励数量为展示用样例。没有调用活动 API、真实发奖或资产存储,也没有连接正式 Runtime。过期和完成状态允许强制展示,以便检查美术布局。 +顶部提供「预览弹出上浮(不领取)」可反复播放当前天数的图标和数量,不改变领取状态。底部可选择第 1~7 天、今日已领、全部完成、已过期、重置、重新打开和下次失败。 建议逐项检查: -- 第 1~7 天高亮、已领取印章、第七天猫皮肤。 -- 单奖励、双奖励、三奖励的尺寸与重叠。 -- 手动领取、自动领取、领取期间关闭拦截、失败后重试。 -- 领取成功使用原有 `shop/prefab/RewardWindow` 和 `Reward` 播放奖励动画(含数量、无限体力时间和猫皮肤),动画结束后点击关闭;失败不播放。 -- 完成或过期时点击卡片不会继续发起领取。 -- 关闭后重新打开,状态保持一致。 +- 没有关闭按钮;点击弹窗背景、标题、卡片等有颜色的 UI 不算点击外部;贴图透明空白算外部,但若下方仍有可见 UI 则由下方 UI 处理。 +- 打开后不自动领取。点击当前奖励卡片或外部,一次只领取当前奖励,成功后保留活动弹窗;之后再次点击外部才关闭并交还后续弹窗判定。 +- 本地领取立即响应,没有模拟请求延迟;播放效果期间连点卡片、实际遮罩都不能重复领取或预约关闭。 +- 「下次失败」后领取失败,保持弹窗且不播放效果,可重试。 +- 上浮:道具位置显示图标和数量,缓慢上移少许后淡出。 +- 第 3 天双奖励、第 6 天锤子+冻结双奖励、第 5 天无限体力秒数(1800 秒)、第 7 天猫皮肤;可重复预览上浮效果。 +- 已领取、完成、过期状态不会再发奖;关闭后重新打开保留内存状态。 -测试场景引用公共奖励 Prefab 和主页同款数字图集,仅在预览时初始化数字显示依赖。七日活动仍动态加载。 +七日活动使用独立 `assets/reward_claim_effect/RewardClaimEffect.ts`,不再使用旧 RewardWindow 或数字图集。正式领取只使用弹出上浮:在道具中心上方一个道具高度处生成(跟随图标缩放),从小弹到 1.25 倍并快速回正,再缩至 0.9 倍、弹到 1.25 倍并快速回正;停留 0.25 秒后,用 0.6 秒上移 150px 并同时淡出。测试仅修改内存,不连接正式 Runtime、不增加资产。主页触发、服务端状态和真实到账仍需测试服验证。 -本地模拟不能证明主页的 22→23 级触发、每日只自动弹一次、服务器日期、接口幂等、真实到账或分包释放正确。上述项目仍需按 `seven-day-gift-maintenance.md` 的测试服流程验证;公共奖励动画也需在测试服回归。 +## 接口测试面板 -## 打包前 +预览 `TestSeven.fire` 后,点击顶部「打开接口测试面板」。输入测试服账号 UID 和登录 token,或点击「读取当前登录」填入当前运行环境里的账号。独立场景没有登录流程,取不到 token 时需要手动填入;token 只保存在输入框内存中。 + +「发送查询」向 `Utils.testHttpip` 的 `signInActivityInfo` 发起 POST,并显示 HTTP 状态、耗时、活动字段和可滚动的完整 JSON;控制台可搜索 `[testSeven][signInActivityInfo]`。查询可能修改后端签到记录。浏览器预览需要测试服允许跨域,网络失败时会提示。 + +「预览返回数据」用真实七日 Prefab 展示响应状态,可在该响应的内存副本上模拟点击领取、奖励动画、点击外部只领取、再次点击才关闭和失败重试,不增加资产,也不提交领取状态。「清除该账号弹窗记录」只清除最近一次成功查询的 UID + activityId 对应的本地自动弹窗记录,不清除奖励防重记录或后端状态。此操作仅影响当前预览环境的存储,不能清除微信开发者工具或真机中的记录。 + +## 打包排除 `testSeven` 是独立测试 Bundle,主场景没有引用它,但这不意味着构建器会自动排除它。打包前把 `assets/testSeven/` 和 `assets/testSeven.meta` 一起移到项目 `assets` 外的本地备份目录,等待 Creator 刷新后构建;测试结束再移回原位并保留 UUID。不要将 TestSeven 选作发布启动场景。脚本额外使用 `CC_PREVIEW` 限制执行,但该限制不能代替构建排除。 确认本地效果后,再使用原有正式场景构建测试服版本。 + +## 透明区域点击回归 + +选择「今日已领」,点击背景和标题的透明边角应关闭;点击有颜色的区域应保持打开。选择第 1 天后点透明处应只领取,效果结束后再次点透明处才关闭。也要检查弹出缩放、卡片呼吸缩放时的边缘判定。 + +命中数据按原图 Alpha ≥ 16/255 生成,忽略几乎透明的柔边。修改七日活动贴图后运行 `python tools/generate-seven-hit-masks.py`(需要 Pillow),再让 Creator 导入更新。当前 16 张图片使用约 44 KB 数据,运行时不读取纹理像素;文字仍使用文本节点范围判定。 + +多奖励新效果排成同一水平行,以原道具区域居中,按左到右每隔 0.145 秒生成(第一次放大并回正的时长)。所有道具弹出完成后停留 0.25 秒,再从左到右每隔 0.145 秒开始上移渐隐。可用第 3 天双奖励、第 6 天锤子+冻结双奖励、第 7 天不同高度图标检查排列和时序。 diff --git a/settings/project.json b/settings/project.json index 0ece24f4..3c844aa5 100644 --- a/settings/project.json +++ b/settings/project.json @@ -1,5 +1,5 @@ { - "last-module-event-record-time": 1788250629444, + "last-module-event-record-time": 1788866731401, "group-list": [ "default", "Map" @@ -53,7 +53,7 @@ "enable": false } }, - "start-scene": "ea96c58a-b731-4349-bcfc-c446571823ad", + "start-scene": "afd5ddb6-1046-4d57-8017-9c13add9b06b", "migrate-history": [ "cloud-function" ], diff --git a/tools/generate-seven-hit-masks.py b/tools/generate-seven-hit-masks.py new file mode 100644 index 00000000..baf9c0da --- /dev/null +++ b/tools/generate-seven-hit-masks.py @@ -0,0 +1,41 @@ +"""原图变更后运行此脚本,更新七日活动的透明区域点击数据。需要 Pillow。""" +import json +from pathlib import Path +from PIL import Image + +ROOT = Path(__file__).resolve().parents[1] +images = sorted((ROOT / "assets/seven_day_gift/texture").glob("*.png")) +images.append(ROOT / "assets/UI/Cat/cat.png") +masks = {} +for path in images: + meta = json.loads(path.with_suffix(".png.meta").read_text(encoding="utf-8")) + alpha = Image.open(path).convert("RGBA").getchannel("A") + for frame in meta["subMetas"].values(): + x, y, w, h = (frame[k] for k in ("trimX", "trimY", "width", "height")) + pixels = alpha.crop((x, y, x + w, y + h)) + rows, spans, lookup = [], [], {} + for row in range(h): + runs, start = [], None + for col in range(w + 1): + solid = col < w and pixels.getpixel((col, row)) >= 16 + if solid and start is None: + start = col + elif not solid and start is not None: + runs.extend((start, col)) + start = None + key = tuple(runs) + if key not in lookup: + lookup[key] = len(spans) + spans.append(runs) + rows.append(lookup[key]) + masks[frame["uuid"]] = dict(width=w, height=h, rows=rows, spans=spans) + +output = ROOT / "assets/seven_day_gift/SevenDayHitMasks.ts" +output.write_text( + "// 自动生成的原图 Alpha 命中数据(阈值 16/255),勿手改;更新图片后运行 tools/generate-seven-hit-masks.py。\n" + "export interface SevenDayHitMask { width: number; height: number; rows: number[]; spans: number[][]; }\n" + "const masks: { [uuid: string]: SevenDayHitMask } = " + + json.dumps(masks, separators=(",", ":")) + ";\nexport default masks;\n", + encoding="utf-8", +) +print(f"Generated {len(masks)} masks, {output.stat().st_size} bytes")