diff --git a/assets/Scene/HomeScene.fire b/assets/Scene/HomeScene.fire index c0ae0d02..c25da831 100644 --- a/assets/Scene/HomeScene.fire +++ b/assets/Scene/HomeScene.fire @@ -4545,7 +4545,7 @@ }, { "__type__": "cc.Node", - "_name": "xinshou", + "_name": "jungle", "_objFlags": 0, "_parent": { "__id__": 31 @@ -4578,8 +4578,8 @@ }, "_contentSize": { "__type__": "cc.Size", - "width": 192, - "height": 157 + "width": 172, + "height": 178 }, "_anchorPoint": { "__type__": "cc.Vec2", @@ -4653,7 +4653,7 @@ "ctor": "Float64Array", "array": [ 0, - -83.788, + -95.567, 0, 0, 0, @@ -4749,7 +4749,7 @@ "ctor": "Float64Array", "array": [ 20.222, - -88.726, + -100.505, 0, 0, 0, @@ -4822,7 +4822,7 @@ "_srcBlendFactor": 770, "_dstBlendFactor": 771, "_spriteFrame": { - "__uuid__": "e304fbd0-7b75-410e-9db8-2a9393f96e80" + "__uuid__": "f8342f92-2d77-4044-9bad-62b5ae3afafc" }, "_type": 0, "_sizeMode": 1, @@ -4919,8 +4919,8 @@ }, "component": "", "_componentId": "1dc93b4fehOrpGgTkihBH4g", - "handler": "openStarter_pack", - "customEventData": "xinshouBtn" + "handler": "openJungleTreasure", + "customEventData": "jungle" }, { "__type__": "cc.Node", @@ -14769,7 +14769,7 @@ "ctor": "Float64Array", "array": [ 0, - -83.788, + -95.023, 0, 0, 0, @@ -14865,7 +14865,7 @@ "ctor": "Float64Array", "array": [ 20.222, - -88.726, + -99.961, 0, 0, 0, @@ -27119,7 +27119,7 @@ "_srcBlendFactor": 770, "_dstBlendFactor": 771, "_spriteFrame": { - "__uuid__": "c4e289ba-3afd-4838-bf0c-571cadce8db2" + "__uuid__": "e8963ffc-77b8-4445-8840-caeb56cc0e41" }, "_type": 0, "_sizeMode": 1, @@ -27132,9 +27132,7 @@ "_fillStart": 0, "_fillRange": 0, "_isTrimmedMode": true, - "_atlas": { - "__uuid__": "c60beb21-ffe5-48e6-bc66-f4ba20a401cc" - }, + "_atlas": null, "_id": "e1aALq05dMl5gd5UQF/y8J" }, { @@ -27299,7 +27297,7 @@ "_srcBlendFactor": 770, "_dstBlendFactor": 771, "_spriteFrame": { - "__uuid__": "2b6c09cd-62b2-4b90-8f3f-498e1a7b4c3f" + "__uuid__": "380f6275-fdcf-4a8d-8f82-f14e474c40e0" }, "_type": 0, "_sizeMode": 1, @@ -27312,9 +27310,7 @@ "_fillStart": 0, "_fillRange": 0, "_isTrimmedMode": true, - "_atlas": { - "__uuid__": "c60beb21-ffe5-48e6-bc66-f4ba20a401cc" - }, + "_atlas": null, "_id": "e9oE+R0LtGFY+Je4h1x/Vl" }, { diff --git a/assets/Script/GameManager.ts b/assets/Script/GameManager.ts index 772f266c..2832df31 100644 --- a/assets/Script/GameManager.ts +++ b/assets/Script/GameManager.ts @@ -329,6 +329,7 @@ export default class GameManager extends cc.Component { if (data.data.token) { cc.fx.GameConfig.GM_INFO.token = data.data.token; } + this.setJungleTreasureLoginState(data.data); // 抽猫红点直接使用登录返回的数据,不额外请求接口。 const loginFilm = data.data.film !== undefined && data.data.film !== null @@ -525,6 +526,36 @@ export default class GameManager extends cc.Component { } // 存储用户数据 } + + /** 只缓存登录结果中已存在的活动状态,不在登录时创建活动数据。 */ + private setJungleTreasureLoginState(userData: any): void { + const gameInfo: any = cc.fx.GameConfig.GM_INFO; + const hasState = userData + && Object.prototype.hasOwnProperty.call(userData, "jungleTreasureState"); + if (!hasState) { + delete gameInfo.jungleTreasureState; + delete gameInfo.jungleTreasureServerNow; + return; + } + + let state = userData.jungleTreasureState; + if (typeof state === "string") { + try { + state = JSON.parse(state); + } + catch (error) { + state = null; + } + } + + if (!state || !Array.isArray(state.tierStates) || !isFinite(Number(state.expiresAt))) { + delete gameInfo.jungleTreasureState; + delete gameInfo.jungleTreasureServerNow; + return; + } + gameInfo.jungleTreasureState = state; + gameInfo.jungleTreasureServerNow = Number(userData.jungleTreasureServerNow) || Date.now(); + } //#region 读取音乐配置 /** 读取音乐配置 */ readMusicConfig() { diff --git a/assets/Script/JiaZai.ts b/assets/Script/JiaZai.ts index d36b400a..5332d984 100644 --- a/assets/Script/JiaZai.ts +++ b/assets/Script/JiaZai.ts @@ -19,6 +19,18 @@ const { ccclass, property } = cc._decorator; @ccclass export default class JiaZai extends cc.Component { + /** HomeScene 的 Loading 始终使用引擎允许的最高层级,并保持为最后一个同级节点。 */ + private ensureLoadingOnTop(): cc.Node { + const loading = this.node && this.node.getChildByName("Loading"); + if (loading) { + loading.zIndex = cc.macro.MAX_ZINDEX; + if (loading.parent) { + loading.setSiblingIndex(loading.parent.childrenCount - 1); + } + } + return loading; + } + /** 仅处理真实按钮事件;振动失败不能阻断按钮原有逻辑。 */ private vibrateButtonClick(event?: cc.Event) { if (!event) { @@ -38,6 +50,24 @@ export default class JiaZai extends cc.Component { // 缓存 shop 节点 private shopNode: cc.Node | null = null; + // 缓存丛林宝藏预制体和界面节点 + private static cachedJungleTreasurePrefab: cc.Prefab | null = null; + private jungleTreasureNode: cc.Node | null = null; + private jungleTreasureLoading: boolean = false; + private jungleTreasureOpenAfterLoad: boolean = false; + private jungleTreasureDataLoading: boolean = false; + private jungleTreasureRequestId: number = 0; + private jungleTreasureServerData: any = null; + private jungleTreasureEndAt: number = 0; + private jungleTreasureCountdownCallback: Function = null; + private jungleTreasureKnownTierStates: any[] = null; + private jungleTreasureUnlockLevel: number = 40; + private jungleTreasureOpenSource: string = "front_page"; + // 每个账号按本地自然日仅自动弹出一次,主动点击入口不受限制。 + private jungleTreasureUseDailyAutoOpenLimit: boolean = true; + private jungleTreasureAutoOpenScheduled: boolean = false; + private jungleTreasureAutoOpenPending: boolean = false; + // 缓存 reward 预制体 private static cachedRewardPrefab: cc.Prefab | null = null; // 缓存 reward 奖励窗口 节点 @@ -180,6 +210,7 @@ export default class JiaZai extends cc.Component { // - 网页版给予大量金币和道具 // ============================================ onLoad() { + this.ensureLoadingOnTop(); //@ts-ignore // wx.getStorageInfo({ // success: (res) => { @@ -241,7 +272,12 @@ export default class JiaZai extends cc.Component { // console.log("进入首页获取的share", cc.fx.GameConfig.GM_INFO.otherUid, cc.fx.GameConfig.GM_INFO.otherLevel); console.log("是否是ios:", cc.fx.GameConfig.GM_INFO.canIos); - this.node.getChildByName("Load").getChildByName("Top").getChildByName("shop").active = cc.fx.GameConfig.GM_INFO.canIos; + const homeTop = this.node.getChildByName("Load").getChildByName("Top"); + homeTop.getChildByName("shop").active = cc.fx.GameConfig.GM_INFO.canIos; + const jungleButton = homeTop.getChildByName("jungle"); + if (jungleButton) { + this.initializeJungleTreasureEntryFromLogin(); + } cc.game.setFrameRate(63); @@ -775,7 +811,7 @@ export default class JiaZai extends cc.Component { // 4. 触摸/滑动结束时再 tween 动画渐显回原 opacity // 何时调用:在 start() 末尾调用 // 涉及节点:passBtn, rank, day, redeemBtn, gacha, gachaGuang, - // xinshou, yicon, shop, hammer + // xinshou, yicon, shop, jungle, hammer // 平台判断:仅在 cc.sys.isMobile 为 true 时才注册事件 // ============================================ initTopBarTouchFade() { @@ -800,6 +836,7 @@ export default class JiaZai extends cc.Component { "xinshou", "yicon", "shop", + "jungle", "hammer", "posBtn", "topBtn" @@ -1038,6 +1075,7 @@ export default class JiaZai extends cc.Component { "yicon", "passBtn", "xinshou", + "jungle", "avatar", "kuang" ] @@ -2359,9 +2397,10 @@ export default class JiaZai extends cc.Component { // 2. 播放loading旋转动画 // ============================================ openLoad() { - this.node.getChildByName("Loading").active = true; - this.node.getChildByName("Loading").getChildByName("load").stopAllActions(); - this.node.getChildByName("Loading").getChildByName("load").runAction(cc.rotateTo(2, 1080).repeatForever()); + const loading = this.ensureLoadingOnTop(); + loading.active = true; + loading.getChildByName("load").stopAllActions(); + loading.getChildByName("load").runAction(cc.rotateTo(2, 1080).repeatForever()); } // ============================================ // 打开加载界面2方法 @@ -2373,9 +2412,10 @@ export default class JiaZai extends cc.Component { // 3. 加载完成后检测是否显示每日任务 // ============================================ openLoad2() { - this.node.getChildByName("Loading").active = true; - this.node.getChildByName("Loading").getChildByName("load").stopAllActions(); - this.node.getChildByName("Loading").getChildByName("load").runAction(cc.rotateTo(2, 1080).repeatForever()); + const loading = this.ensureLoadingOnTop(); + loading.active = true; + loading.getChildByName("load").stopAllActions(); + loading.getChildByName("load").runAction(cc.rotateTo(2, 1080).repeatForever()); setTimeout(() => { if (this.dailyQuestsShow == true) { MiniGameSdk.API.showToast("网络异常,请稍后重试!") @@ -2552,15 +2592,485 @@ export default class JiaZai extends cc.Component { this.actionpNode.active = false; } } - // ============================================ - // 开始通行证倒计时方法 - // 功能:启动通行证时间监控 + + openJungleTreasure(event?: cc.Event, customEventData?: string) { + this.vibrateButtonClick(event); + if (!this.canOpenJungleTreasure()) { + return; + } + this.jungleTreasureOpenSource = customEventData === "shop_page" + ? "shop_page" + : (customEventData === "jump_window" + ? "jump_window" + : "front_page"); + if (this.jungleTreasureOpenSource !== "jump_window") { + this.jungleTreasureAutoOpenPending = false; + } + if (this.jungleTreasureOpenSource === "front_page") { + cc.fx.GameTool.shushu_Track("enter_jungle", { + source: this.jungleTreasureOpenSource, + }); + } + this.openLoad(); + // 每次进入都重新读取服务器状态和剩余时间。 + this.requestJungleTreasureData(true, true); + } + + closeJungleTreasure() { + const jungleNode = this.jungleTreasureNode; + this.jungleTreasureNode = null; + this.jungleTreasureOpenAfterLoad = false; + this.jungleTreasureLoading = false; + JiaZai.cachedJungleTreasurePrefab = null; + + if (jungleNode && cc.isValid(jungleNode)) { + jungleNode.off("jungle-close", this.closeJungleTreasure, this); + jungleNode.off("jungle-state-changed", this.onJungleTreasureStateChanged, this); + jungleNode.active = false; + jungleNode.removeFromParent(false); + jungleNode.destroy(); + } + + // destroy 在当前帧结束时生效,下一帧再释放资源,避免节点仍引用贴图时提前释放。 + this.scheduleOnce(() => { + const bundle = cc.assetManager.getBundle("jungle_treasure"); + if (bundle) { + bundle.releaseAll(); + cc.assetManager.removeBundle(bundle); + } + }, 0); + } + + /** 读取服务器配置和当前玩家的 51 档状态;成功前不加载活动分包。 */ + private requestJungleTreasureData(openAfterLoad: boolean = false, forceRefresh: boolean = false) { + if (openAfterLoad) { + this.jungleTreasureOpenAfterLoad = true; + } + if (this.jungleTreasureServerData && !forceRefresh) { + this.loadJungleTreasurePrefab(); + return; + } + if (this.jungleTreasureDataLoading) { + return; + } + + this.jungleTreasureDataLoading = true; + const requestId = ++this.jungleTreasureRequestId; + const requestTimeout = setTimeout(() => { + if (requestId !== this.jungleTreasureRequestId || !this.jungleTreasureDataLoading) { + return; + } + this.jungleTreasureRequestId++; + this.jungleTreasureDataLoading = false; + this.jungleTreasureOpenAfterLoad = false; + this.setJungleHomeButtonText("重试"); + this.closeLoad(); + cc.warn("获取丛林宝藏数据超时"); + }, 8000); + Utils.getJungleTreasure((response) => { + if (requestId !== this.jungleTreasureRequestId) { + return; + } + clearTimeout(requestTimeout); + this.jungleTreasureDataLoading = false; + const data = response && response.code === 1 ? response.data : null; + const config = data && data.config; + const tierStates = data && data.tierStates; + if (!config || !Array.isArray(config.tiers) || !Array.isArray(tierStates) + || tierStates.length < config.tiers.length) { + this.jungleTreasureOpenAfterLoad = false; + this.setJungleHomeButtonText("重试"); + this.closeLoad(); + cc.warn("获取丛林宝藏配置或玩家状态失败:", response); + return; + } + + this.jungleTreasureServerData = data; + this.cacheJungleTreasureSessionState(data); + const hasRemainingTier = tierStates.some((state) => Number(state) !== 2); + this.jungleTreasureKnownTierStates = tierStates.slice(); + this.startJungleTreasureCountdown(data.expiresAt, data.serverNow); + if (data.config && this.jungleTreasureEndAt > 0) { + // 分包内倒计时同样使用已校正的本地截止时间。 + data.config.endAt = this.jungleTreasureEndAt; + } + const shouldShowEntry = this.refreshJungleTreasureHomeButton(tierStates); + + if (hasRemainingTier && shouldShowEntry) { + this.loadJungleTreasurePrefab(); + } + else { + this.jungleTreasureOpenAfterLoad = false; + if (!hasRemainingTier) { + this.stopJungleTreasureCountdown(); + this.setJungleHomeButtonText("已完成"); + } + this.closeLoad(); + } + }); + } + + private canOpenJungleTreasure(): boolean { + let gameInfo: any = cc.fx.GameConfig.GM_INFO; + let playerLevel = Number(gameInfo.level) || 0; + return !!gameInfo.canIos && playerLevel >= this.jungleTreasureUnlockLevel; + } + + private getJungleTreasureHomeButton(): cc.Node { + let load = this.node && this.node.getChildByName("Load"); + let top = load && load.getChildByName("Top"); + return top && top.getChildByName("jungle"); + } + + private scheduleJungleTreasureAutoOpen(): void { + if (this.jungleTreasureAutoOpenScheduled || !this.canOpenJungleTreasure()) { + return; + } + if (this.jungleTreasureUseDailyAutoOpenLimit + && this.hasJungleTreasureAutoOpenedToday()) { + return; + } + + this.jungleTreasureAutoOpenScheduled = true; + this.scheduleOnce(() => { + this.jungleTreasureAutoOpenScheduled = false; + if (!cc.isValid(this.node) || !this.canOpenJungleTreasure() + || this.jungleTreasureDataLoading || this.jungleTreasureNode) { + return; + } + this.jungleTreasureAutoOpenPending = true; + this.openJungleTreasure(null, "jump_window"); + }, 0); + } + + private hasJungleTreasureAutoOpenedToday(): boolean { + let storedDate = cc.fx.StorageMessage.getStorage(this.getJungleTreasureAutoOpenStorageKey()); + return storedDate === this.getJungleTreasureLocalDate(); + } + + private markJungleTreasureAutoOpened(): void { + if (!this.jungleTreasureAutoOpenPending) { + return; + } + this.jungleTreasureAutoOpenPending = false; + cc.fx.GameTool.shushu_Track("enter_jungle", { + source: "jump_window", + }); + if (this.jungleTreasureUseDailyAutoOpenLimit) { + cc.fx.StorageMessage.setStorage( + this.getJungleTreasureAutoOpenStorageKey(), + this.getJungleTreasureLocalDate(), + ); + } + } + + private getJungleTreasureAutoOpenStorageKey(): string { + let gameInfo: any = cc.fx.GameConfig.GM_INFO; + let userKey = String(gameInfo.uid || gameInfo.userId || "guest"); + return "JUNGLE_TREASURE_AUTO_OPEN_DATE_" + userKey; + } + + private getJungleTreasureLocalDate(): string { + let now = new Date(); + let month = this.padJungleTime(now.getMonth() + 1); + let day = this.padJungleTime(now.getDate()); + return now.getFullYear() + "-" + month + "-" + day; + } + + private cacheJungleTreasureSessionState(data: any): void { + if (!data || !Array.isArray(data.tierStates)) { + return; + } + let gameInfo: any = cc.fx.GameConfig.GM_INFO; + let state = gameInfo.jungleTreasureState + && typeof gameInfo.jungleTreasureState === "object" + ? gameInfo.jungleTreasureState + : {}; + state.tierStates = data.tierStates.slice(); + if (data.expiresAt !== undefined) state.expiresAt = data.expiresAt; + if (data.revision !== undefined) state.revision = data.revision; + if (data.updatedAt !== undefined) state.updatedAt = data.updatedAt; + gameInfo.jungleTreasureState = state; + if (data.serverNow !== undefined) { + gameInfo.jungleTreasureServerNow = data.serverNow; + } + } + + private setJungleHomeButtonText(text: string) { + let button = this.getJungleTreasureHomeButton(); + let time = button && button.getChildByName("time"); + let label = time && time.getComponent(cc.Label); + if (label) { + label.string = text; + } + } + + /** 使用服务器剩余时间建立本地展示倒计时,不使用本地存档重置活动。 */ + private startJungleTreasureCountdown(expiresAtValue: any, serverNowValue: any): boolean { + this.stopJungleTreasureCountdown(); + const expiresAt = Number(expiresAtValue); + const serverNow = Number(serverNowValue); + if (!isFinite(expiresAt) || expiresAt <= 0) { + this.jungleTreasureEndAt = 0; + this.setJungleHomeButtonText("活动中"); + return true; + } + + const normalizedServerNow = isFinite(serverNow) && serverNow > 0 ? serverNow : Date.now(); + this.jungleTreasureEndAt = Date.now() + Math.max(0, expiresAt - normalizedServerNow); + this.jungleTreasureCountdownCallback = () => this.updateJungleTreasureCountdown(); + const remaining = this.updateJungleTreasureCountdown(); + if (remaining > 0) { + this.schedule(this.jungleTreasureCountdownCallback, 1); + return true; + } + return false; + } + + private stopJungleTreasureCountdown(): void { + if (this.jungleTreasureCountdownCallback) { + this.unschedule(this.jungleTreasureCountdownCallback); + this.jungleTreasureCountdownCallback = null; + } + } + + /** 24 小时及以上显示天/时,不足 24 小时显示 HH:MM:SS。 */ + private updateJungleTreasureCountdown(): number { + if (!this.jungleTreasureEndAt) { + return -1; + } + const remaining = Math.max(0, Math.ceil((this.jungleTreasureEndAt - Date.now()) / 1000)); + if (remaining >= 24 * 60 * 60) { + const days = Math.floor(remaining / 86400); + const hours = Math.floor((remaining % 86400) / 3600); + this.setJungleHomeButtonText(days + "天" + hours + "时"); + } + else { + const hours = Math.floor(remaining / 3600); + const minutes = Math.floor((remaining % 3600) / 60); + const seconds = remaining % 60; + this.setJungleHomeButtonText(this.padJungleTime(hours) + + ":" + this.padJungleTime(minutes) + + ":" + this.padJungleTime(seconds)); + } + + if (remaining === 0) { + this.stopJungleTreasureCountdown(); + this.refreshJungleTreasureHomeButton(); + } + return remaining; + } + + private padJungleTime(value: number): string { + return value < 10 ? "0" + value : value.toString(); + } + + /** 进入首页先按登录状态控制入口,符合条件时再按自动弹窗策略读取活动接口。 */ + private initializeJungleTreasureEntryFromLogin(): void { + const gameInfo: any = cc.fx.GameConfig.GM_INFO; + let button = this.getJungleTreasureHomeButton(); + if (!this.canOpenJungleTreasure()) { + if (button) { + button.active = false; + } + return; + } + + const hasState = Object.prototype.hasOwnProperty.call(gameInfo, "jungleTreasureState"); + const state = hasState ? gameInfo.jungleTreasureState : null; + + if (!state || !Array.isArray(state.tierStates)) { + if (button) { + button.active = true; + } + this.setJungleHomeButtonText("活动中"); + this.scheduleJungleTreasureAutoOpen(); + return; + } + + this.jungleTreasureKnownTierStates = state.tierStates.slice(); + this.startJungleTreasureCountdown(state.expiresAt, gameInfo.jungleTreasureServerNow); + let shouldShowEntry = this.refreshJungleTreasureHomeButton(this.jungleTreasureKnownTierStates); + if (shouldShowEntry) { + this.scheduleJungleTreasureAutoOpen(); + } + } + + /** + * 未超时:只要还有未领取档位就保留入口。 + * 已超时:只统计首个未解锁付费档(0)之前的可领取档(1),无可领档时隐藏入口。 + */ + private refreshJungleTreasureHomeButton(tierStatesValue?: any[]): boolean { + const states = Array.isArray(tierStatesValue) + ? tierStatesValue + : this.jungleTreasureKnownTierStates; + let shouldShow = false; + + if (Array.isArray(states)) { + const isExpired = this.jungleTreasureEndAt > 0 && Date.now() >= this.jungleTreasureEndAt; + if (!isExpired) { + shouldShow = states.some((state) => Number(state) !== 2); + } + else { + let claimableCount = 0; + for (let index = 0; index < states.length; index++) { + const state = Number(states[index]); + if (state === 0) { + break; + } + if (state === 1) { + claimableCount++; + } + } + // 只剩一个可领礼包时也必须保留入口。 + shouldShow = claimableCount > 0; + } + } + + let button = this.getJungleTreasureHomeButton(); + if (button) { + button.active = this.canOpenJungleTreasure() && shouldShow; + } + return shouldShow; + } + + /** 预加载丛林宝藏分包;点击发生在加载过程中时,加载完成后自动打开。 */ + private loadJungleTreasurePrefab(openAfterLoad: boolean = false) { + if (openAfterLoad) { + this.jungleTreasureOpenAfterLoad = true; + } + if (!this.jungleTreasureServerData) { + this.requestJungleTreasureData(this.jungleTreasureOpenAfterLoad); + return; + } + if (JiaZai.cachedJungleTreasurePrefab) { + if (this.jungleTreasureOpenAfterLoad) { + this.jungleTreasureOpenAfterLoad = false; + this.showJungleTreasure(); + } + return; + } + if (this.jungleTreasureLoading) { + return; + } + + this.jungleTreasureLoading = true; + cc.assetManager.loadBundle("jungle_treasure", (bundleError: Error, bundle: cc.AssetManager.Bundle) => { + if (bundleError || !bundle) { + this.jungleTreasureLoading = false; + this.jungleTreasureOpenAfterLoad = false; + this.closeLoad(); + cc.error("加载 jungle_treasure 分包失败:", bundleError && (bundleError.message || bundleError)); + return; + } + bundle.load("prefab/jungle", cc.Prefab, (prefabError: Error, prefab: cc.Prefab) => { + this.jungleTreasureLoading = false; + if (prefabError || !prefab) { + this.jungleTreasureOpenAfterLoad = false; + this.closeLoad(); + cc.error("加载 jungle_treasure/prefab/jungle 失败:", prefabError && (prefabError.message || prefabError)); + return; + } + + JiaZai.cachedJungleTreasurePrefab = prefab; + if (this.jungleTreasureOpenAfterLoad) { + this.jungleTreasureOpenAfterLoad = false; + this.showJungleTreasure(); + } + }); + }); + } + + /** 创建或重新显示活动界面。 */ + private showJungleTreasure() { + if (!JiaZai.cachedJungleTreasurePrefab || !this.jungleTreasureServerData) { + this.closeLoad(); + return; + } + try { + if (!this.jungleTreasureNode || !cc.isValid(this.jungleTreasureNode)) { + this.jungleTreasureNode = cc.instantiate(JiaZai.cachedJungleTreasurePrefab); + this.node.addChild(this.jungleTreasureNode); + this.jungleTreasureNode.zIndex = 1001; + this.jungleTreasureNode.on("jungle-close", this.closeJungleTreasure, this); + this.jungleTreasureNode.on("jungle-state-changed", this.onJungleTreasureStateChanged, this); + } + + const manager: any = this.jungleTreasureNode.getComponent("jungle_Manager"); + if (!manager || !manager.setServerData || !manager.openActivity) { + this.jungleTreasureNode.active = false; + cc.error("jungle 预制体缺少 jungle_Manager 或服务器初始化方法"); + return; + } + const initialized = manager.setServerData( + this.jungleTreasureServerData.config, + this.jungleTreasureServerData.tierStates, + this.jungleTreasureOpenSource, + ); + if (initialized) { + manager.openActivity(); + this.markJungleTreasureAutoOpened(); + } + else { + this.jungleTreasureNode.active = false; + cc.error("jungle 服务器配置初始化失败"); + } + } + catch (error) { + if (this.jungleTreasureNode && cc.isValid(this.jungleTreasureNode)) { + this.jungleTreasureNode.active = false; + } + cc.error("jungle 界面初始化异常:", error); + } + finally { + this.closeLoad(); + } + } + // 何时调用:打开通行证界面时调用 // 主要功能: // 1. 先停止之前的计时器 // 2. 每秒检查一次通行证剩余时间 // 3. 到期后停止计时 // ============================================ + /** 接收分包内领取/支付刷新后的服务器状态,立即同步首页入口和下次打开所用缓存。 */ + private onJungleTreasureStateChanged(data: any): void { + if (!data || !Array.isArray(data.tierStates)) { + return; + } + if (!this.jungleTreasureServerData) { + this.jungleTreasureServerData = {}; + } + this.jungleTreasureServerData.tierStates = data.tierStates.slice(); + if (data.config) this.jungleTreasureServerData.config = data.config; + if (data.expiresAt !== undefined) this.jungleTreasureServerData.expiresAt = data.expiresAt; + if (data.serverNow !== undefined) this.jungleTreasureServerData.serverNow = data.serverNow; + if (data.revision !== undefined) this.jungleTreasureServerData.revision = data.revision; + if (data.updatedAt !== undefined) this.jungleTreasureServerData.updatedAt = data.updatedAt; + + this.jungleTreasureKnownTierStates = data.tierStates.slice(); + const gameInfo: any = cc.fx.GameConfig.GM_INFO; + const loginState = gameInfo.jungleTreasureState && typeof gameInfo.jungleTreasureState === "object" + ? gameInfo.jungleTreasureState + : {}; + loginState.tierStates = data.tierStates.slice(); + if (data.expiresAt !== undefined) loginState.expiresAt = data.expiresAt; + if (data.revision !== undefined) loginState.revision = data.revision; + if (data.updatedAt !== undefined) loginState.updatedAt = data.updatedAt; + gameInfo.jungleTreasureState = loginState; + if (data.serverNow !== undefined) { + gameInfo.jungleTreasureServerNow = data.serverNow; + } + + if (data.expiresAt !== undefined) { + this.startJungleTreasureCountdown(data.expiresAt, data.serverNow); + if (this.jungleTreasureServerData.config && this.jungleTreasureEndAt > 0) { + this.jungleTreasureServerData.config.endAt = this.jungleTreasureEndAt; + } + } + this.refreshJungleTreasureHomeButton(this.jungleTreasureKnownTierStates); + } + startStarter_pack() { this.stopStarter_pack(); if (this.scheduleCallback3) { @@ -4247,7 +4757,8 @@ export default class JiaZai extends cc.Component { posBtn: "tututtt", topBtn: "topBtn", redeemBtn: "redeem_btn", - gacha: "gachaBtn" + gacha: "gachaBtn", + jungle: "jungle" }; for (const nodeName in frameNameByNode) { diff --git a/assets/Script/SceneManager.ts b/assets/Script/SceneManager.ts index 405ef0db..cfe2493b 100644 --- a/assets/Script/SceneManager.ts +++ b/assets/Script/SceneManager.ts @@ -15,12 +15,35 @@ const { ccclass, property } = cc._decorator; @ccclass export default class SceneManager extends cc.Component { + /** GameScene 的 Loading 始终使用引擎允许的最高层级,并保持为最后一个同级节点。 */ + private ensureLoadingOnTop(): cc.Node { + const loading = this.node && this.node.getChildByName("Loading"); + if (loading) { + loading.zIndex = cc.macro.MAX_ZINDEX; + if (loading.parent) { + loading.setSiblingIndex(loading.parent.childrenCount - 1); + } + } + return loading; + } + // 缓存 shop 预制体 private static cachedShopPrefab: cc.Prefab | null = null; // 缓存 shop 节点 private shopNode: cc.Node | null = null; + // GameScene 中从商城打开 Jungle Treasure 时使用。 + private static cachedJungleTreasurePrefab: cc.Prefab | null = null; + private jungleTreasureNode: cc.Node | null = null; + private jungleTreasureServerData: any = null; + private jungleTreasureDataLoading: boolean = false; + private jungleTreasureBundleLoading: boolean = false; + private jungleTreasureRequestId: number = 0; + private jungleTreasureRequestTimeout: any = null; + private jungleTreasureUnlockLevel: number = 40; + private jungleTreasureOpenSource: string = "shop_page"; + // 缓存 reward 预制体 private static cachedRewardPrefab: cc.Prefab | null = null; // 缓存 reward 奖励窗口 节点 @@ -143,6 +166,7 @@ export default class SceneManager extends cc.Component { // LIFE-CYCLE CALLBACKS: onLoad() { + this.ensureLoadingOnTop(); this.isDestroying = false; this.node.on(cc.Node.EventType.TOUCH_END, this.onGameButtonTouchEnd, this, true); this.share_time = true; @@ -193,6 +217,7 @@ export default class SceneManager extends cc.Component { this.isDestroying = true; this.node.off(cc.Node.EventType.TOUCH_END, this.onGameButtonTouchEnd, this, true); this.clearFlyCoins(); + this.disposeJungleTreasure(false); } clearFlyCoins() { @@ -616,6 +641,231 @@ export default class SceneManager extends cc.Component { } } + /** GameScene 商城入口打开 Jungle Treasure。 */ + openJungleTreasure(event?: cc.Event, customEventData?: string) { + let level = Number(cc.fx.GameConfig.GM_INFO.level) || 0; + if (!cc.fx.GameConfig.GM_INFO.canIos || level < this.jungleTreasureUnlockLevel + || this.jungleTreasureDataLoading + || this.jungleTreasureBundleLoading || this.jungleTreasureNode) { + return; + } + + this.jungleTreasureOpenSource = customEventData === "jump_window" + ? "jump_window" + : "shop_page"; + + this.openLoad(); + this.jungleTreasureDataLoading = true; + let requestId = ++this.jungleTreasureRequestId; + this.clearJungleTreasureRequestTimeout(); + this.jungleTreasureRequestTimeout = setTimeout(() => { + if (requestId !== this.jungleTreasureRequestId || !this.jungleTreasureDataLoading) { + return; + } + this.jungleTreasureRequestId++; + this.finishJungleTreasureOpenFailure("获取活动数据超时,请稍后重试"); + }, 8000); + + Utils.getJungleTreasure((response) => { + if (requestId !== this.jungleTreasureRequestId || this.isDestroying) { + return; + } + this.clearJungleTreasureRequestTimeout(); + this.jungleTreasureDataLoading = false; + let data = response && response.code === 1 ? response.data : null; + let config = data && data.config; + let tierStates = data && data.tierStates; + if (!config || !Array.isArray(config.tiers) || !Array.isArray(tierStates) + || tierStates.length < config.tiers.length) { + this.finishJungleTreasureOpenFailure( + (response && (response.msg || response.message)) || "获取活动数据失败", + ); + return; + } + + this.jungleTreasureServerData = data; + this.cacheJungleTreasureState(data); + this.loadJungleTreasureBundle(); + }); + } + + closeJungleTreasure() { + this.closeLoad(); + this.disposeJungleTreasure(true); + } + + /** Jungle 领奖后刷新 GameScene 的金币和三种局内道具显示。 */ + refreshJungleRewardDisplay(): void { + if (MapConroler && MapConroler._instance + && MapConroler._instance.node && cc.isValid(MapConroler._instance.node)) { + MapConroler._instance.updateCoin(); + MapConroler._instance.setPropNum(); + } + } + + private loadJungleTreasureBundle(): void { + if (SceneManager.cachedJungleTreasurePrefab) { + this.showJungleTreasure(); + return; + } + + this.jungleTreasureBundleLoading = true; + let existingBundle = cc.assetManager.getBundle("jungle_treasure"); + if (existingBundle) { + this.loadJungleTreasurePrefab(existingBundle); + return; + } + + cc.assetManager.loadBundle("jungle_treasure", (bundleError: Error, bundle: cc.AssetManager.Bundle) => { + if (this.isDestroying) { + return; + } + if (bundleError || !bundle) { + this.jungleTreasureBundleLoading = false; + this.finishJungleTreasureOpenFailure("加载活动资源失败,请稍后重试"); + cc.error("GameScene 加载 jungle_treasure 分包失败:", bundleError); + return; + } + this.loadJungleTreasurePrefab(bundle); + }); + } + + private loadJungleTreasurePrefab(bundle: cc.AssetManager.Bundle): void { + bundle.load("prefab/jungle", cc.Prefab, (prefabError: Error, prefab: cc.Prefab) => { + if (this.isDestroying) { + return; + } + this.jungleTreasureBundleLoading = false; + if (prefabError || !prefab) { + this.finishJungleTreasureOpenFailure("加载活动界面失败,请稍后重试"); + cc.error("GameScene 加载 Jungle prefab 失败:", prefabError); + return; + } + SceneManager.cachedJungleTreasurePrefab = prefab; + this.showJungleTreasure(); + }); + } + + private showJungleTreasure(): void { + if (!SceneManager.cachedJungleTreasurePrefab || !this.jungleTreasureServerData + || this.isDestroying) { + this.finishJungleTreasureOpenFailure("活动界面初始化失败"); + return; + } + + try { + let jungleNode = cc.instantiate(SceneManager.cachedJungleTreasurePrefab); + this.jungleTreasureNode = jungleNode; + this.node.addChild(jungleNode); + jungleNode.zIndex = 1001; + jungleNode.on("jungle-close", this.closeJungleTreasure, this); + jungleNode.on("jungle-state-changed", this.onJungleTreasureStateChanged, this); + this.ensureLoadingOnTop(); + + let manager: any = jungleNode.getComponent("jungle_Manager"); + if (!manager || typeof manager.setServerData !== "function" + || typeof manager.openActivity !== "function") { + throw new Error("Jungle prefab 缺少 jungle_Manager 初始化方法"); + } + let initialized = manager.setServerData( + this.jungleTreasureServerData.config, + this.jungleTreasureServerData.tierStates, + this.jungleTreasureOpenSource, + ); + if (!initialized) { + throw new Error("Jungle 服务器配置初始化失败"); + } + manager.openActivity(); + this.closeLoad(); + } + catch (error) { + cc.error("GameScene Jungle 界面初始化异常:", error); + this.disposeJungleTreasure(true); + this.finishJungleTreasureOpenFailure("活动界面初始化失败"); + } + } + + private onJungleTreasureStateChanged(data: any): void { + if (!data || !Array.isArray(data.tierStates)) { + return; + } + if (!this.jungleTreasureServerData) { + this.jungleTreasureServerData = {}; + } + this.jungleTreasureServerData.tierStates = data.tierStates.slice(); + if (data.config) this.jungleTreasureServerData.config = data.config; + if (data.expiresAt !== undefined) this.jungleTreasureServerData.expiresAt = data.expiresAt; + if (data.serverNow !== undefined) this.jungleTreasureServerData.serverNow = data.serverNow; + if (data.revision !== undefined) this.jungleTreasureServerData.revision = data.revision; + if (data.updatedAt !== undefined) this.jungleTreasureServerData.updatedAt = data.updatedAt; + this.cacheJungleTreasureState(data); + } + + private cacheJungleTreasureState(data: any): void { + if (!data || !Array.isArray(data.tierStates)) { + return; + } + let gameInfo: any = cc.fx.GameConfig.GM_INFO; + let state = gameInfo.jungleTreasureState + && typeof gameInfo.jungleTreasureState === "object" + ? gameInfo.jungleTreasureState + : {}; + state.tierStates = data.tierStates.slice(); + if (data.expiresAt !== undefined) state.expiresAt = data.expiresAt; + if (data.revision !== undefined) state.revision = data.revision; + if (data.updatedAt !== undefined) state.updatedAt = data.updatedAt; + gameInfo.jungleTreasureState = state; + if (data.serverNow !== undefined) { + gameInfo.jungleTreasureServerNow = data.serverNow; + } + } + + private finishJungleTreasureOpenFailure(message: string): void { + this.jungleTreasureDataLoading = false; + this.jungleTreasureBundleLoading = false; + this.clearJungleTreasureRequestTimeout(); + this.closeLoad(); + MiniGameSdk.API.showToast(message); + } + + private clearJungleTreasureRequestTimeout(): void { + if (this.jungleTreasureRequestTimeout) { + clearTimeout(this.jungleTreasureRequestTimeout); + this.jungleTreasureRequestTimeout = null; + } + } + + private disposeJungleTreasure(releaseOnNextFrame: boolean): void { + this.jungleTreasureRequestId++; + this.jungleTreasureDataLoading = false; + this.jungleTreasureBundleLoading = false; + this.clearJungleTreasureRequestTimeout(); + + let jungleNode = this.jungleTreasureNode; + this.jungleTreasureNode = null; + this.jungleTreasureServerData = null; + SceneManager.cachedJungleTreasurePrefab = null; + if (jungleNode && cc.isValid(jungleNode)) { + jungleNode.off("jungle-close", this.closeJungleTreasure, this); + jungleNode.off("jungle-state-changed", this.onJungleTreasureStateChanged, this); + jungleNode.destroy(); + } + + let releaseBundle = () => { + let bundle = cc.assetManager.getBundle("jungle_treasure"); + if (bundle) { + bundle.releaseAll(); + cc.assetManager.removeBundle(bundle); + } + }; + if (releaseOnNextFrame && !this.isDestroying) { + this.scheduleOnce(releaseBundle, 0); + } + else { + releaseBundle(); + } + } + closePause() { cc.fx.AudioManager._instance.playEffect("anniu_little", null); this.pause.getComponent("btnControl").setTouch(true); @@ -895,9 +1145,10 @@ export default class SceneManager extends cc.Component { } openLoad() { - this.node.getChildByName("Loading").active = true; - this.node.getChildByName("Loading").getChildByName("load").stopAllActions(); - this.node.getChildByName("Loading").getChildByName("load").runAction(cc.rotateTo(2, 1080).repeatForever()); + const loading = this.ensureLoadingOnTop(); + loading.active = true; + loading.getChildByName("load").stopAllActions(); + loading.getChildByName("load").runAction(cc.rotateTo(2, 1080).repeatForever()); } closeLoad() { diff --git a/assets/Script/module/Config/GameConfig.ts b/assets/Script/module/Config/GameConfig.ts index 298d4403..a1bcb383 100644 --- a/assets/Script/module/Config/GameConfig.ts +++ b/assets/Script/module/Config/GameConfig.ts @@ -97,6 +97,8 @@ export class GameConfig { coin: number; //用户金币 film: number; //头像抽卡代币 catArr: any[]; //用户猫数组 + jungleTreasureState?: any; //登录返回的丛林宝藏状态;未触发活动的用户不存在此属性 + jungleTreasureServerNow?: number; //登录接口返回状态时的服务器时间 success: boolean; //用户游戏成功与否 matchId: any; //用于埋点上传的ID gameState: boolean; custom: number; //用于测试跳关卡 @@ -135,6 +137,7 @@ export class GameConfig { otherLevel: number; //其他被帮助用户等级 helpLevel: number; //帮助用户等级 iosShopOrder: string; //ios订单号商城 + iosJungleOrder: string; //iOS 客服旧支付的丛林宝藏订单号 iosMonthOrder: string; //ios订单号月卡 iosReviveOrder: string; //ios订单号复活 iosStarterOrder: string; //ios订单号新手 @@ -320,7 +323,7 @@ export class GameConfig { vibrateOpen: true, //震动 coinnum: 0, //每局的金币数 paid_user: false, //是否是付费用户 - version: "1.0.21", //版本号 + version: "1.0.23", //版本号 shushu_DistinctId: "", //数数访客ID shushu_AccountId: "", //数数账号ID uid: "", //用户和后端唯一id @@ -338,6 +341,7 @@ export class GameConfig { otherLevel: 0, //其他被帮助用户等级 helpLevel: 0, //帮助用户等级 iosShopOrder: "", //ios订单号商城 + iosJungleOrder: "", //iOS 客服旧支付的丛林宝藏订单号 iosMonthOrder: "", //ios订单号月卡 iosReviveOrder: "", //ios订单号复活 iosStarterOrder: "", //ios订单号新手 diff --git a/assets/Script/module/Pay/Utils.ts b/assets/Script/module/Pay/Utils.ts index 9bc06bc6..d7992630 100644 --- a/assets/Script/module/Pay/Utils.ts +++ b/assets/Script/module/Pay/Utils.ts @@ -225,6 +225,10 @@ export default class Utils { cc.fx.GameConfig.GM_INFO.iosPassCheckOrder = null; cc.fx.GameConfig.GM_INFO.iosPassCheckOrder = order; } + else if (/^jungle_treasure_\d+$/.test(String(iosPayInfo.payment_name || ""))) { + cc.fx.GameConfig.GM_INFO.iosJungleOrder = null; + cc.fx.GameConfig.GM_INFO.iosJungleOrder = order; + } else { cc.fx.GameConfig.GM_INFO.iosShopOrder = null; cc.fx.GameConfig.GM_INFO.iosShopOrder = order; @@ -1029,6 +1033,46 @@ export default class Utils { makeRequest(); } + //#region 丛林宝藏活动 + /** 获取丛林宝藏配置和玩家档位状态 */ + static getJungleTreasure(callBack) { + //@ts-ignore + if ((typeof wx !== 'undefined' && wx !== null) || (typeof tt !== 'undefined' && tt !== null)) { + let uid = cc.fx.StorageMessage.getStorage("uid"); + if (uid != undefined && uid != "" && uid != null) { + cc.fx.GameConfig.GM_INFO.uid = uid; + } + let setData = { + uid: cc.fx.GameConfig.GM_INFO.uid, + action: 'read', + } + Utils.POST("jungleTreasure", setData, res => { + console.log("获得丛林宝藏数据:", res); + if (callBack) callBack(res); + }); + } + } + + /** 领取丛林宝藏指定档位,tierId 从 1 开始 */ + static claimJungleTreasure(tierId, callBack) { + //@ts-ignore + if ((typeof wx !== 'undefined' && wx !== null) || (typeof tt !== 'undefined' && tt !== null)) { + let uid = cc.fx.StorageMessage.getStorage("uid"); + if (uid != undefined && uid != "" && uid != null) { + cc.fx.GameConfig.GM_INFO.uid = uid; + } + let setData = { + uid: cc.fx.GameConfig.GM_INFO.uid, + action: 'claim', + tierId: tierId, + } + Utils.POST("jungleTreasure", setData, res => { + console.log("领取丛林宝藏数据:", res); + if (callBack) callBack(res); + }); + } + } + //#region 新手礼包相关 //获取新手礼包是否能显示 static getStarter_pack(callBack) { diff --git a/assets/Script/module/Tool/GameTool.ts b/assets/Script/module/Tool/GameTool.ts index 21c1d5e5..0a185942 100644 --- a/assets/Script/module/Tool/GameTool.ts +++ b/assets/Script/module/Tool/GameTool.ts @@ -1071,7 +1071,7 @@ var GameTool = { cc.fx.GameTool.changeCoin(2500); coin = 2500; price = 1000; - this.setUserPowerTime(3600); + this.setUserPowerTime(3600, "unlimited_health_bundle_10"); // MiniGameSdk.API.showToast("获得无限体力小组合包"); rewardData = [ { type: "coin", count: coin }, @@ -1089,7 +1089,7 @@ var GameTool = { "price": price } this.getShopProp(propData, compensate); - this.setUserPowerTime(7200); + this.setUserPowerTime(7200, "unlimited_health_bundle_20"); // MiniGameSdk.API.showToast("获得无限体力大组合包"); rewardData = [ { type: "coin", count: coin }, @@ -1110,7 +1110,7 @@ var GameTool = { "price": price } this.getShopProp(propData2, compensate); - this.setUserPowerTime(14400); + this.setUserPowerTime(14400, "unlimited_health_bundle_30"); // MiniGameSdk.API.showToast("获得无限体力超组合包"); rewardData = [ { type: "coin", count: coin }, @@ -1213,6 +1213,18 @@ var GameTool = { propInfo.freezeAmount = cc.fx.GameConfig.GM_INFO.freezeAmount; propInfo.timestamp = timestamp; + let isGetType = "battlepass"; + if (getType === "jungle") { + isGetType = "jungle"; + } + else if (getType) { + isGetType = "redeem_code"; + } + const isInfiniteHealth = porpId >= 3001 && porpId <= 3005; + const getInfiniteHealthSeconds = (defaultSeconds: number) => { + return getType === "jungle" ? num : defaultSeconds; + }; + switch (porpId) { case 1001: cc.fx.GameTool.changeCoin(num); @@ -1244,28 +1256,34 @@ var GameTool = { break; case 3001: console.log("getPassCheckPorp道具1:", porpId) - this.setUserPowerTime(900); + this.setUserPowerTime(getInfiniteHealthSeconds(900), isGetType); break; case 3002: - this.setUserPowerTime(1800); + this.setUserPowerTime(getInfiniteHealthSeconds(1800), isGetType); break; case 3003: - this.setUserPowerTime(3600); + this.setUserPowerTime(getInfiniteHealthSeconds(3600), isGetType); + break; + case 3004: + this.setUserPowerTime(getInfiniteHealthSeconds(7200), isGetType); + break; + case 3005: + this.setUserPowerTime(getInfiniteHealthSeconds(10800), isGetType); break; } - let isGetType = "battlepass"; - if (getType) { - isGetType = "redeem_code"; + + // 无限体力由 setUserPowerTime 统一发送 id=2005 的 resource_get,避免重复上报。 + if (!isInfiniteHealth) { + const dataTemp = { + change_reason: isGetType, + id: porpId, + num: num, + compensate: compensate + } + console.log("通行证发放奖励", porpId, num) + cc.fx.GameTool.shushu_Track("resource_get", dataTemp); } - const dataTemp = { - change_reason: isGetType, - id: porpId, - num: num, - compensate: compensate - } - console.log("通行证发放奖励", porpId, num) - cc.fx.GameTool.shushu_Track("resource_get", dataTemp); }, formatDate(date: Date): string { @@ -1472,6 +1490,18 @@ var GameTool = { is_paid_bp_usr: data.is_paid_bp_usr,//是否是战令付费档位, boolean } break; + case "enter_jungle": + eventData = { + source: data.source, + } + break; + case "pay_jungle": + case "collect_jungle": + eventData = { + current_block: data.current_block, + source: data.source, + } + break; } if (name == "stage_help") { console.log("准备上报完成", data); @@ -1605,25 +1635,35 @@ var GameTool = { //购买无限体力 - setUserPowerTime(time) { + setUserPowerTime(time, reason, compensate = false) { //@ts-ignore if ((typeof wx !== 'undefined' && wx !== null) || (typeof tt !== 'undefined' && tt !== null)) { + const actualSeconds = Math.max(0, Number(time) || 0); + if (actualSeconds <= 0) { + return; + } let nowTime = Math.floor(Date.now() / 1000); let powerTime = cc.fx.StorageMessage.getStorage("userPowerTime"); console.log("现在时间:", nowTime, "无限体力时间:", powerTime, "增加时间:", time); if (powerTime == "undifend" || powerTime == null || powerTime == "" || powerTime == 0) { - powerTime = Math.floor(Date.now() / 1000) + time; + powerTime = Math.floor(Date.now() / 1000) + actualSeconds; } else { if (nowTime >= powerTime) { - powerTime = nowTime + time; + powerTime = nowTime + actualSeconds; } else { - powerTime = powerTime + time; + powerTime = powerTime + actualSeconds; } } cc.fx.GameConfig.GM_INFO.userPowerTime = powerTime; console.log("最终时间:", powerTime); cc.fx.StorageMessage.setStorage("userPowerTime", powerTime); + cc.fx.GameTool.shushu_Track("resource_get", { + change_reason: reason || "unknown", + id: 2005, + num: actualSeconds, + compensate: compensate || false, + }); Utils.setUserPowerTime(powerTime, (data) => { //console.log("购买无限体力同步:", data); }) diff --git a/assets/UI/UI/start/startUI.plist b/assets/UI/UI/start/startUI.plist index 2eb26927..967c008e 100644 --- a/assets/UI/UI/start/startUI.plist +++ b/assets/UI/UI/start/startUI.plist @@ -7,24 +7,37 @@ gachaBtn.png frame - {{704,335},{172,178}} + {{176,722},{172,178}} offset {0,0} rotated - + sourceColorRect {{3,0},{172,178}} sourceSize {178,178} - meitirw.png + jungle.png frame - {{333,356},{150,156}} + {{176,542},{172,178}} offset {0,0} rotated - + + sourceColorRect + {{0,0},{172,178}} + sourceSize + {172,178} + + meitirw.png + + frame + {{350,678},{150,156}} + offset + {0,0} + rotated + sourceColorRect {{0,0},{150,156}} sourceSize @@ -33,7 +46,7 @@ redeem_btn.png frame - {{530,182},{172,178}} + {{2,722},{172,178}} offset {0,0} rotated @@ -46,7 +59,7 @@ topBtn.png frame - {{174,356},{151,157}} + {{350,525},{151,157}} offset {0,0} rotated @@ -59,11 +72,11 @@ tu01.png frame - {{2,341},{157,170}} + {{350,196},{157,170}} offset {0,0} rotated - + sourceColorRect {{1,1},{157,170}} sourceSize @@ -72,11 +85,11 @@ tu02.png frame - {{350,182},{172,178}} + {{2,542},{172,178}} offset {0,0} rotated - + sourceColorRect {{3,0},{172,178}} sourceSize @@ -85,7 +98,7 @@ tu03.png frame - {{550,2},{172,178}} + {{176,362},{172,178}} offset {0,0} rotated @@ -98,7 +111,7 @@ tu04.png frame - {{176,176},{172,178}} + {{2,362},{172,178}} offset {0,0} rotated @@ -111,11 +124,11 @@ tu05.png frame - {{2,2},{192,157}} + {{350,2},{192,157}} offset {0,0} rotated - + sourceColorRect {{0,0},{192,157}} sourceSize @@ -124,7 +137,7 @@ tu06.png frame - {{376,2},{172,178}} + {{176,182},{172,178}} offset {0,0} rotated @@ -137,11 +150,11 @@ tu07.png frame - {{724,176},{155,157}} + {{350,368},{155,157}} offset {0,0} rotated - + sourceColorRect {{0,0},{155,157}} sourceSize @@ -150,20 +163,20 @@ tututt.png frame - {{724,2},{149,172}} + {{176,2},{172,178}} offset - {-1,-1} + {0,0} rotated sourceColorRect - {{0,2},{149,172}} + {{0,0},{172,178}} sourceSize - {151,174} + {172,178} tututtt.png frame - {{2,161},{172,178}} + {{2,182},{172,178}} offset {0,0} rotated @@ -176,11 +189,11 @@ tutututu.png frame - {{196,2},{172,178}} + {{2,2},{172,178}} offset {0,0} rotated - + sourceColorRect {{3,0},{172,178}} sourceSize @@ -194,9 +207,9 @@ realTextureFileName startUI.png size - {884,510} + {509,902} smartupdate - $TexturePacker:SmartUpdate:6bd70543920415691864496cb1dd5f32$ + $TexturePacker:SmartUpdate:6f86df468e02578b4f942d9e280d5a21$ textureFileName startUI.png diff --git a/assets/UI/UI/start/startUI.plist.meta b/assets/UI/UI/start/startUI.plist.meta index ac8f2eef..29c65196 100644 --- a/assets/UI/UI/start/startUI.plist.meta +++ b/assets/UI/UI/start/startUI.plist.meta @@ -4,8 +4,8 @@ "importer": "asset", "rawTextureUuid": "cadb06f1-d50b-448c-b78b-f2094dbe0f31", "size": { - "width": 884, - "height": 510 + "width": 509, + "height": 902 }, "type": "Texture Packer", "subMetas": { @@ -16,11 +16,11 @@ "rawTextureUuid": "cadb06f1-d50b-448c-b78b-f2094dbe0f31", "trimType": "auto", "trimThreshold": 1, - "rotated": true, + "rotated": false, "offsetX": 0, "offsetY": 0, - "trimX": 704, - "trimY": 335, + "trimX": 176, + "trimY": 722, "width": 172, "height": 178, "rawWidth": 178, @@ -32,6 +32,29 @@ "spriteType": "normal", "subMetas": {} }, + "jungle.png": { + "ver": "1.0.6", + "uuid": "f8342f92-2d77-4044-9bad-62b5ae3afafc", + "importer": "sprite-frame", + "rawTextureUuid": "cadb06f1-d50b-448c-b78b-f2094dbe0f31", + "trimType": "auto", + "trimThreshold": 1, + "rotated": false, + "offsetX": 0, + "offsetY": 0, + "trimX": 176, + "trimY": 542, + "width": 172, + "height": 178, + "rawWidth": 172, + "rawHeight": 178, + "borderTop": 0, + "borderBottom": 0, + "borderLeft": 0, + "borderRight": 0, + "spriteType": "normal", + "subMetas": {} + }, "meitirw.png": { "ver": "1.0.6", "uuid": "438f4e3d-0962-41c5-8b0e-02ff7970a9ad", @@ -39,11 +62,11 @@ "rawTextureUuid": "cadb06f1-d50b-448c-b78b-f2094dbe0f31", "trimType": "auto", "trimThreshold": 1, - "rotated": true, + "rotated": false, "offsetX": 0, "offsetY": 0, - "trimX": 333, - "trimY": 356, + "trimX": 350, + "trimY": 678, "width": 150, "height": 156, "rawWidth": 150, @@ -65,8 +88,8 @@ "rotated": false, "offsetX": 0, "offsetY": 0, - "trimX": 530, - "trimY": 182, + "trimX": 2, + "trimY": 722, "width": 172, "height": 178, "rawWidth": 178, @@ -88,8 +111,8 @@ "rotated": true, "offsetX": 0, "offsetY": 0, - "trimX": 174, - "trimY": 356, + "trimX": 350, + "trimY": 525, "width": 151, "height": 157, "rawWidth": 151, @@ -108,11 +131,11 @@ "rawTextureUuid": "cadb06f1-d50b-448c-b78b-f2094dbe0f31", "trimType": "auto", "trimThreshold": 1, - "rotated": true, + "rotated": false, "offsetX": 0, "offsetY": 0, - "trimX": 2, - "trimY": 341, + "trimX": 350, + "trimY": 196, "width": 157, "height": 170, "rawWidth": 159, @@ -131,11 +154,11 @@ "rawTextureUuid": "cadb06f1-d50b-448c-b78b-f2094dbe0f31", "trimType": "auto", "trimThreshold": 1, - "rotated": true, + "rotated": false, "offsetX": 0, "offsetY": 0, - "trimX": 350, - "trimY": 182, + "trimX": 2, + "trimY": 542, "width": 172, "height": 178, "rawWidth": 178, @@ -157,8 +180,8 @@ "rotated": false, "offsetX": 0, "offsetY": 0, - "trimX": 550, - "trimY": 2, + "trimX": 176, + "trimY": 362, "width": 172, "height": 178, "rawWidth": 178, @@ -180,8 +203,8 @@ "rotated": false, "offsetX": 0, "offsetY": 0, - "trimX": 176, - "trimY": 176, + "trimX": 2, + "trimY": 362, "width": 172, "height": 178, "rawWidth": 178, @@ -200,10 +223,10 @@ "rawTextureUuid": "cadb06f1-d50b-448c-b78b-f2094dbe0f31", "trimType": "auto", "trimThreshold": 1, - "rotated": false, + "rotated": true, "offsetX": 0, "offsetY": 0, - "trimX": 2, + "trimX": 350, "trimY": 2, "width": 192, "height": 157, @@ -226,8 +249,8 @@ "rotated": false, "offsetX": 0, "offsetY": 0, - "trimX": 376, - "trimY": 2, + "trimX": 176, + "trimY": 182, "width": 172, "height": 178, "rawWidth": 172, @@ -246,11 +269,11 @@ "rawTextureUuid": "cadb06f1-d50b-448c-b78b-f2094dbe0f31", "trimType": "auto", "trimThreshold": 1, - "rotated": false, + "rotated": true, "offsetX": 0, "offsetY": 0, - "trimX": 724, - "trimY": 176, + "trimX": 350, + "trimY": 368, "width": 155, "height": 157, "rawWidth": 155, @@ -270,14 +293,14 @@ "trimType": "auto", "trimThreshold": 1, "rotated": false, - "offsetX": -1, - "offsetY": -1, - "trimX": 724, + "offsetX": 0, + "offsetY": 0, + "trimX": 176, "trimY": 2, - "width": 149, - "height": 172, - "rawWidth": 151, - "rawHeight": 174, + "width": 172, + "height": 178, + "rawWidth": 172, + "rawHeight": 178, "borderTop": 0, "borderBottom": 0, "borderLeft": 0, @@ -296,7 +319,7 @@ "offsetX": 0, "offsetY": 0, "trimX": 2, - "trimY": 161, + "trimY": 182, "width": 172, "height": 178, "rawWidth": 178, @@ -315,10 +338,10 @@ "rawTextureUuid": "cadb06f1-d50b-448c-b78b-f2094dbe0f31", "trimType": "auto", "trimThreshold": 1, - "rotated": true, + "rotated": false, "offsetX": 0, "offsetY": 0, - "trimX": 196, + "trimX": 2, "trimY": 2, "width": 172, "height": 178, diff --git a/assets/UI/UI/start/startUI.png b/assets/UI/UI/start/startUI.png index 084ad575..84cd812c 100644 Binary files a/assets/UI/UI/start/startUI.png and b/assets/UI/UI/start/startUI.png differ diff --git a/assets/UI/UI/start/startUI.png.meta b/assets/UI/UI/start/startUI.png.meta index cd039eb1..13523607 100644 --- a/assets/UI/UI/start/startUI.png.meta +++ b/assets/UI/UI/start/startUI.png.meta @@ -8,8 +8,8 @@ "premultiplyAlpha": false, "genMipmaps": false, "packable": true, - "width": 884, - "height": 510, + "width": 509, + "height": 902, "platformSettings": {}, "subMetas": {} } \ No newline at end of file diff --git a/assets/UI/UI/start/startUI2.plist b/assets/UI/UI/start/startUI2.plist index a50e3946..084e8df6 100644 --- a/assets/UI/UI/start/startUI2.plist +++ b/assets/UI/UI/start/startUI2.plist @@ -17,14 +17,27 @@ sourceSize {204,204} - meitirw.png + jungle.png frame - {{2,788},{150,156}} + {{184,392},{188,178}} offset {0,0} rotated - + + sourceColorRect + {{0,0},{188,178}} + sourceSize + {188,178} + + meitirw.png + + frame + {{406,2},{150,156}} + offset + {0,0} + rotated + sourceColorRect {{0,0},{150,156}} sourceSize @@ -46,7 +59,7 @@ topBtn.png frame - {{184,582},{182,180}} + {{374,374},{182,180}} offset {0,-7} rotated @@ -59,11 +72,11 @@ tu01.png frame - {{366,768},{157,170}} + {{2,788},{157,170}} offset {0,0} rotated - + sourceColorRect {{1,1},{157,170}} sourceSize @@ -72,7 +85,7 @@ tu02.png frame - {{184,392},{182,188}} + {{374,184},{182,188}} offset {0,-3} rotated @@ -137,11 +150,11 @@ tu07.png frame - {{374,184},{161,166}} + {{182,768},{161,166}} offset {0,0} rotated - + sourceColorRect {{0,0},{161,166}} sourceSize @@ -150,11 +163,11 @@ tututt.png frame - {{368,392},{190,178}} + {{182,588},{190,178}} offset {-5,-8} rotated - + sourceColorRect {{2,21},{190,178}} sourceSize @@ -163,7 +176,7 @@ tututtt.png frame - {{182,764},{182,178}} + {{374,736},{182,178}} offset {0,-8} rotated @@ -176,11 +189,11 @@ tutututu.png frame - {{368,584},{182,178}} + {{374,556},{182,178}} offset {0,-8} rotated - + sourceColorRect {{11,21},{182,178}} sourceSize @@ -194,9 +207,9 @@ realTextureFileName startUI2.png size - {548,944} + {558,947} smartupdate - $TexturePacker:SmartUpdate:1f8035e43d5ff5b835270172376f4545$ + $TexturePacker:SmartUpdate:a339cad63a250af1abbb35d8c5348e8a$ textureFileName startUI2.png diff --git a/assets/UI/UI/start/startUI2.plist.meta b/assets/UI/UI/start/startUI2.plist.meta index 20d2bc13..576aaa60 100644 --- a/assets/UI/UI/start/startUI2.plist.meta +++ b/assets/UI/UI/start/startUI2.plist.meta @@ -4,8 +4,8 @@ "importer": "asset", "rawTextureUuid": "b45f4eaf-4250-449a-81ea-a7ca1e534a02", "size": { - "width": 548, - "height": 944 + "width": 558, + "height": 947 }, "type": "Texture Packer", "subMetas": { @@ -32,6 +32,29 @@ "spriteType": "normal", "subMetas": {} }, + "jungle.png": { + "ver": "1.0.6", + "uuid": "440f6109-e9b2-4d99-9b8b-c7040bbf38b0", + "importer": "sprite-frame", + "rawTextureUuid": "b45f4eaf-4250-449a-81ea-a7ca1e534a02", + "trimType": "auto", + "trimThreshold": 1, + "rotated": false, + "offsetX": 0, + "offsetY": 0, + "trimX": 184, + "trimY": 392, + "width": 188, + "height": 178, + "rawWidth": 188, + "rawHeight": 178, + "borderTop": 0, + "borderBottom": 0, + "borderLeft": 0, + "borderRight": 0, + "spriteType": "normal", + "subMetas": {} + }, "meitirw.png": { "ver": "1.0.6", "uuid": "5d27437d-7397-4f7b-88b7-447e8864ce39", @@ -39,11 +62,11 @@ "rawTextureUuid": "b45f4eaf-4250-449a-81ea-a7ca1e534a02", "trimType": "auto", "trimThreshold": 1, - "rotated": true, + "rotated": false, "offsetX": 0, "offsetY": 0, - "trimX": 2, - "trimY": 788, + "trimX": 406, + "trimY": 2, "width": 150, "height": 156, "rawWidth": 150, @@ -88,8 +111,8 @@ "rotated": false, "offsetX": 0, "offsetY": -7, - "trimX": 184, - "trimY": 582, + "trimX": 374, + "trimY": 374, "width": 182, "height": 180, "rawWidth": 204, @@ -108,11 +131,11 @@ "rawTextureUuid": "b45f4eaf-4250-449a-81ea-a7ca1e534a02", "trimType": "auto", "trimThreshold": 1, - "rotated": false, + "rotated": true, "offsetX": 0, "offsetY": 0, - "trimX": 366, - "trimY": 768, + "trimX": 2, + "trimY": 788, "width": 157, "height": 170, "rawWidth": 159, @@ -134,8 +157,8 @@ "rotated": false, "offsetX": 0, "offsetY": -3, - "trimX": 184, - "trimY": 392, + "trimX": 374, + "trimY": 184, "width": 182, "height": 188, "rawWidth": 204, @@ -246,11 +269,11 @@ "rawTextureUuid": "b45f4eaf-4250-449a-81ea-a7ca1e534a02", "trimType": "auto", "trimThreshold": 1, - "rotated": false, + "rotated": true, "offsetX": 0, "offsetY": 0, - "trimX": 374, - "trimY": 184, + "trimX": 182, + "trimY": 768, "width": 161, "height": 166, "rawWidth": 161, @@ -269,11 +292,11 @@ "rawTextureUuid": "b45f4eaf-4250-449a-81ea-a7ca1e534a02", "trimType": "auto", "trimThreshold": 1, - "rotated": true, + "rotated": false, "offsetX": -5, "offsetY": -8, - "trimX": 368, - "trimY": 392, + "trimX": 182, + "trimY": 588, "width": 190, "height": 178, "rawWidth": 204, @@ -295,8 +318,8 @@ "rotated": false, "offsetX": 0, "offsetY": -8, - "trimX": 182, - "trimY": 764, + "trimX": 374, + "trimY": 736, "width": 182, "height": 178, "rawWidth": 204, @@ -315,11 +338,11 @@ "rawTextureUuid": "b45f4eaf-4250-449a-81ea-a7ca1e534a02", "trimType": "auto", "trimThreshold": 1, - "rotated": true, + "rotated": false, "offsetX": 0, "offsetY": -8, - "trimX": 368, - "trimY": 584, + "trimX": 374, + "trimY": 556, "width": 182, "height": 178, "rawWidth": 204, diff --git a/assets/UI/UI/start/startUI2.png b/assets/UI/UI/start/startUI2.png index 5289cc3a..2eccd77c 100644 Binary files a/assets/UI/UI/start/startUI2.png and b/assets/UI/UI/start/startUI2.png differ diff --git a/assets/action_bundle/script/DailyQuests.ts b/assets/action_bundle/script/DailyQuests.ts index 46eaee53..6bdd8f60 100644 --- a/assets/action_bundle/script/DailyQuests.ts +++ b/assets/action_bundle/script/DailyQuests.ts @@ -248,7 +248,7 @@ export default class DailyQuests extends cc.Component { } // if (name == "share") { // // console.log("_______获得无限体力时间:", shuju.infinite_health, name); - // cc.fx.GameTool.setUserPowerTime(900); + // cc.fx.GameTool.setUserPowerTime(900, "DailyQuest"); // } const canvasTemp = cc.find("Canvas"); // 假设 Canvas 节点 @@ -355,7 +355,7 @@ export default class DailyQuests extends cc.Component { console.log("领取奖励", data); if (data == "share") { console.log("分享奖励"); - cc.fx.GameTool.setUserPowerTime(900); + cc.fx.GameTool.setUserPowerTime(900, "DailyQuest"); } this.touchStart = false; this.reward = true; @@ -474,15 +474,6 @@ export default class DailyQuests extends cc.Component { } cc.fx.GameTool.shushu_Track("resource_get", data3); } - else if (propData.infinite_health > 0) { - const data4 = { - change_reason: "quests", - id: 2004, - num: propData.infinite_health, - compensate: false - } - cc.fx.GameTool.shushu_Track("resource_get", data4); - } } } diff --git a/assets/custom/Json/level1841.json b/assets/custom/Json/level1841.json index d9248c77..493aebb2 100644 --- a/assets/custom/Json/level1841.json +++ b/assets/custom/Json/level1841.json @@ -395,7 +395,7 @@ "z": 0 }, "floor": 2, - "floorTime": 14, + "floorTime": 16, "floorMove": true, "id": 490 }, @@ -610,4 +610,4 @@ } ] ] -} \ No newline at end of file +} diff --git a/assets/custom/Json/level1845.json b/assets/custom/Json/level1845.json index da6fccce..74ed4a82 100644 --- a/assets/custom/Json/level1845.json +++ b/assets/custom/Json/level1845.json @@ -539,6 +539,20 @@ "longAndShort": 12, "order": true, "colorChange": 9 + }, + { + "id": 1860, + "num": 31, + "color": 7, + "special": 0, + "length": 2 + }, + { + "id": 1861, + "num": 32, + "color": 7, + "special": 0, + "length": 0 } ] ] diff --git a/assets/custom/Json/level1846.json b/assets/custom/Json/level1846.json index 0e1601c2..58c25d02 100644 --- a/assets/custom/Json/level1846.json +++ b/assets/custom/Json/level1846.json @@ -161,7 +161,7 @@ "color": 5, "type": 2, "position": { - "x": 360, + "x": 240, "y": -480, "z": 0 }, @@ -206,7 +206,7 @@ "type": 16, "position": { "x": 480, - "y": -360, + "y": -480, "z": 0 }, "questionTime": 6, @@ -226,16 +226,15 @@ }, { "block": 0, - "color": 3, + "color": 9, "type": 16, "position": { - "x": 0, + "x": 120, "y": -120, "z": 0 }, - "questionTime": 12, - "vertical": true, - "id": 410 + "questionTime": 10, + "id": 400 }, { "block": 4, @@ -291,29 +290,29 @@ "floorMove": true, "id": 430 }, + { + "block": 0, + "color": 3, + "type": 0, + "position": { + "x": 0, + "y": -120, + "z": 0 + }, + "floor": 2, + "floorTime": 10, + "floorMove": true, + "id": 430 + }, { "block": 23, - "color": 1, + "color": 3, "type": 0, "position": { "x": -240, "y": -360, "z": 0 }, - "id": 430 - }, - { - "block": 0, - "color": 9, - "type": 0, - "position": { - "x": 120, - "y": -120, - "z": 0 - }, - "floor": 1, - "floorTime": 10, - "floorMove": true, "id": 440 } ] diff --git a/assets/custom/Json/level1849.json b/assets/custom/Json/level1849.json index ce995fee..f165aa5c 100644 --- a/assets/custom/Json/level1849.json +++ b/assets/custom/Json/level1849.json @@ -137,7 +137,7 @@ }, { "block": 1, - "color": 1, + "color": 2, "type": 14, "position": { "x": -180, diff --git a/assets/custom/Json/level1851.json b/assets/custom/Json/level1851.json index 7912f7ec..1ebbfed9 100644 --- a/assets/custom/Json/level1851.json +++ b/assets/custom/Json/level1851.json @@ -426,17 +426,6 @@ "floorTime": 16, "floorMove": true, "id": 490 - }, - { - "block": 0, - "color": 11, - "type": 0, - "position": { - "x": 60, - "y": -360, - "z": 0 - }, - "id": 490 } ] ], @@ -498,7 +487,7 @@ { "id": 1959, "num": 22, - "color": 6, + "color": 4, "special": 3, "length": 2, "freeze": 15 @@ -506,7 +495,7 @@ { "id": 1960, "num": 26, - "color": 6, + "color": 4, "special": 3, "length": 0, "freeze": 15 diff --git a/assets/custom/Json/level1852.json b/assets/custom/Json/level1852.json index 734b46ae..c831527b 100644 --- a/assets/custom/Json/level1852.json +++ b/assets/custom/Json/level1852.json @@ -35,17 +35,6 @@ }, "id": 220 }, - { - "block": 0, - "color": 9, - "type": 22, - "position": { - "x": 180, - "y": -480, - "z": 0 - }, - "id": 230 - }, { "block": 4, "color": 9, @@ -95,7 +84,7 @@ }, { "block": 0, - "color": 3, + "color": 10, "type": 4, "position": { "x": 60, @@ -230,14 +219,14 @@ }, { "block": 1, - "color": 7, + "color": 2, "type": 1, "position": { "x": 420, "y": -120, "z": 0 }, - "stacking": 2, + "stacking":4 , "colorChange": true, "floor": 8, "floorTime": 2, @@ -258,50 +247,6 @@ "floorMove": true, "id": 410 }, - { - "block": 23, - "color": 2, - "type": 0, - "position": { - "x": 420, - "y": 0, - "z": 0 - }, - "id": 450 - }, - { - "block": 23, - "color": 2, - "type": 0, - "position": { - "x": 300, - "y": 0, - "z": 0 - }, - "id": 460 - }, - { - "block": 23, - "color": 2, - "type": 0, - "position": { - "x": 420, - "y": 120, - "z": 0 - }, - "id": 470 - }, - { - "block": 23, - "color": 2, - "type": 0, - "position": { - "x": 300, - "y": 120, - "z": 0 - }, - "id": 480 - }, { "block": 5, "color": 6, @@ -350,6 +295,50 @@ "floorTime": 14, "floorMove": true, "id": 510 + }, + { + "block": 23, + "color": 1, + "type": 0, + "position": { + "x": 420, + "y": 0, + "z": 0 + }, + "id": 460 + }, + { + "block": 23, + "color": 1, + "type": 0, + "position": { + "x": 420, + "y": 120, + "z": 0 + }, + "id": 470 + }, + { + "block": 23, + "color": 1, + "type": 0, + "position": { + "x": 300, + "y": 0, + "z": 0 + }, + "id": 480 + }, + { + "block": 23, + "color": 1, + "type": 0, + "position": { + "x": 300, + "y": 120, + "z": 0 + }, + "id": 490 } ] ], diff --git a/assets/custom/Json/level1853.json b/assets/custom/Json/level1853.json index 2bfdb2bf..fcbf90a5 100644 --- a/assets/custom/Json/level1853.json +++ b/assets/custom/Json/level1853.json @@ -29,7 +29,7 @@ "color": 6, "type": 0, "position": { - "x": -120, + "x": -240, "y": -480, "z": 0 }, @@ -41,22 +41,11 @@ "type": 0, "position": { "x": 240, - "y": -120, + "y": -240, "z": 0 }, "id": 320 }, - { - "block": 0, - "color": 11, - "type": 0, - "position": { - "x": 240, - "y": -480, - "z": 0 - }, - "id": 340 - }, { "block": 0, "color": 11, @@ -131,8 +120,8 @@ "color": 2, "type": 1, "position": { - "x": 0, - "y": -360, + "x": -240, + "y": -120, "z": 0 }, "stacking": 3, @@ -143,7 +132,7 @@ "color": 4, "type": 1, "position": { - "x": -120, + "x": 120, "y": -120, "z": 0 }, @@ -156,7 +145,7 @@ "type": 17, "position": { "x": -360, - "y": -480, + "y": -360, "z": 0 }, "boomTime": 1, @@ -191,7 +180,7 @@ "color": 6, "type": 16, "position": { - "x": 240, + "x": -120, "y": -240, "z": 0 }, @@ -303,8 +292,8 @@ "color": 11, "type": 0, "position": { - "x": 240, - "y": -480, + "x": -120, + "y": -360, "z": 0 }, "id": 490 diff --git a/assets/custom/Json/level1860.json b/assets/custom/Json/level1860.json index fd40a43e..b22c1af3 100644 --- a/assets/custom/Json/level1860.json +++ b/assets/custom/Json/level1860.json @@ -437,7 +437,7 @@ "length": 3, "longAndShort": 13, "order": false, - "colorChange": 2 + "colorChange": 4 }, { "id": 1969, @@ -447,7 +447,7 @@ "length": 0, "longAndShort": 13, "order": false, - "colorChange": 2 + "colorChange": 4 }, { "id": 1970, @@ -457,7 +457,7 @@ "length": 0, "longAndShort": 13, "order": false, - "colorChange": 2 + "colorChange": 4 }, { "id": 1971, @@ -491,7 +491,7 @@ "length": 2, "longAndShort": 12, "order": true, - "colorChange": 9 + "colorChange": 7 }, { "id": 1975, @@ -501,7 +501,7 @@ "length": 0, "longAndShort": 12, "order": true, - "colorChange": 9 + "colorChange": 7 }, { "id": 1976, diff --git a/assets/jungle_treasure.meta b/assets/jungle_treasure.meta index 9f472779..10f4d7d3 100644 --- a/assets/jungle_treasure.meta +++ b/assets/jungle_treasure.meta @@ -2,12 +2,24 @@ "ver": "1.1.3", "uuid": "9e1d8b29-96ad-4168-8ed8-6545d21984f5", "importer": "folder", - "isBundle": false, + "isBundle": true, "bundleName": "", "priority": 1, - "compressionType": {}, - "optimizeHotUpdate": {}, - "inlineSpriteFrames": {}, - "isRemoteBundle": {}, + "compressionType": { + "wechatgame": "subpackage", + "bytedance": "subpackage" + }, + "optimizeHotUpdate": { + "wechatgame": false, + "bytedance": false + }, + "inlineSpriteFrames": { + "wechatgame": false, + "bytedance": false + }, + "isRemoteBundle": { + "wechatgame": false, + "bytedance": false + }, "subMetas": {} } \ No newline at end of file diff --git a/assets/jungle_treasure/effect.meta b/assets/jungle_treasure/effect.meta new file mode 100644 index 00000000..80e441d6 --- /dev/null +++ b/assets/jungle_treasure/effect.meta @@ -0,0 +1,13 @@ +{ + "ver": "1.1.3", + "uuid": "fbb7e618-55db-40fd-9372-c281114365c2", + "importer": "folder", + "isBundle": false, + "bundleName": "", + "priority": 1, + "compressionType": {}, + "optimizeHotUpdate": {}, + "inlineSpriteFrames": {}, + "isRemoteBundle": {}, + "subMetas": {} +} \ No newline at end of file diff --git a/assets/jungle_treasure/effect/title_shine.effect b/assets/jungle_treasure/effect/title_shine.effect new file mode 100644 index 00000000..1d9ed084 --- /dev/null +++ b/assets/jungle_treasure/effect/title_shine.effect @@ -0,0 +1,91 @@ +CCEffect %{ + techniques: + - passes: + - vert: title-shine-vs + frag: title-shine-fs + blendState: + targets: + - blend: true + blendSrc: src_alpha + blendDst: one_minus_src_alpha + rasterizerState: + cullMode: none + depthStencilState: + depthTest: false + depthWrite: false + properties: + texture: { value: white } + shinePosition: { value: -0.3 } + shineWidth: { value: 0.13 } + shineStrength: { value: 1.8 } + shineAngle: { value: 0.2 } + shineColor: { value: [1.0, 0.95, 0.68, 1.0] } +}% + +CCProgram title-shine-vs %{ + precision highp float; + #include + #include + + in vec3 a_position; + in vec4 a_color; + in vec2 a_uv0; + out vec4 v_color; + out vec2 v_uv0; + + void main () { + vec4 pos = vec4(a_position, 1.0); + #if CC_USE_MODEL + pos = cc_matViewProj * cc_matWorld * pos; + #else + pos = cc_matViewProj * pos; + #endif + gl_Position = pos; + v_color = a_color; + v_uv0 = a_uv0; + } +}% + +CCProgram title-shine-fs %{ + precision highp float; + #include + #include + + in vec4 v_color; + in vec2 v_uv0; + uniform sampler2D texture; + uniform ShineProperties { + vec4 shineColor; + float shinePosition; + float shineWidth; + float shineStrength; + float shineAngle; + }; + + void main () { + vec4 color = vec4(1.0); + CCTexture(texture, v_uv0, color); + color *= v_color; + + float sweepAxis = v_uv0.x + (v_uv0.y - 0.5) * shineAngle; + float bandIn = smoothstep( + shinePosition - shineWidth, + shinePosition, + sweepAxis + ); + float bandOut = 1.0 - smoothstep( + shinePosition, + shinePosition + shineWidth, + sweepAxis + ); + float shine = pow(clamp(bandIn * bandOut, 0.0, 1.0), 1.35); + color.rgb += shineColor.rgb * shine * shineStrength * color.a; + + ALPHA_TEST(color); + #if USE_BGRA + gl_FragColor = color.bgra; + #else + gl_FragColor = color.rgba; + #endif + } +}% diff --git a/assets/jungle_treasure/effect/title_shine.effect.meta b/assets/jungle_treasure/effect/title_shine.effect.meta new file mode 100644 index 00000000..cc2e656c --- /dev/null +++ b/assets/jungle_treasure/effect/title_shine.effect.meta @@ -0,0 +1,18 @@ +{ + "ver": "1.0.27", + "uuid": "80e4a9c8-be05-48f0-bb5d-537db8a95eab", + "importer": "effect", + "compiledShaders": [ + { + "glsl1": { + "vert": "\nprecision highp float;\nuniform mat4 cc_matViewProj;\nuniform mat4 cc_matWorld;\nattribute vec3 a_position;\nattribute vec4 a_color;\nattribute vec2 a_uv0;\nvarying vec4 v_color;\nvarying vec2 v_uv0;\nvoid main () {\n vec4 pos = vec4(a_position, 1.0);\n #if CC_USE_MODEL\n pos = cc_matViewProj * cc_matWorld * pos;\n #else\n pos = cc_matViewProj * pos;\n #endif\n gl_Position = pos;\n v_color = a_color;\n v_uv0 = a_uv0;\n}", + "frag": "\nprecision highp float;\n#if USE_ALPHA_TEST\n uniform float alphaThreshold;\n#endif\nvoid ALPHA_TEST (in vec4 color) {\n #if USE_ALPHA_TEST\n if (color.a < alphaThreshold) discard;\n #endif\n}\nvoid ALPHA_TEST (in float alpha) {\n #if USE_ALPHA_TEST\n if (alpha < alphaThreshold) discard;\n #endif\n}\nvarying vec4 v_color;\nvarying vec2 v_uv0;\nuniform sampler2D texture;\nuniform vec4 shineColor;\nuniform float shinePosition;\nuniform float shineWidth;\nuniform float shineStrength;\nuniform float shineAngle;\nvoid main () {\n vec4 color = vec4(1.0);\n vec4 texture_tmp = texture2D(texture, v_uv0);\n #if CC_USE_ALPHA_ATLAS_texture\n texture_tmp.a *= texture2D(texture, v_uv0 + vec2(0, 0.5)).r;\n #endif\n #if INPUT_IS_GAMMA\n color.rgb *= (texture_tmp.rgb * texture_tmp.rgb);\n color.a *= texture_tmp.a;\n #else\n color *= texture_tmp;\n #endif\n color *= v_color;\n float sweepAxis = v_uv0.x + (v_uv0.y - 0.5) * shineAngle;\n float bandIn = smoothstep(\n shinePosition - shineWidth,\n shinePosition,\n sweepAxis\n );\n float bandOut = 1.0 - smoothstep(\n shinePosition,\n shinePosition + shineWidth,\n sweepAxis\n );\n float shine = pow(clamp(bandIn * bandOut, 0.0, 1.0), 1.35);\n color.rgb += shineColor.rgb * shine * shineStrength * color.a;\n ALPHA_TEST(color);\n #if USE_BGRA\n gl_FragColor = color.bgra;\n #else\n gl_FragColor = color.rgba;\n #endif\n}" + }, + "glsl3": { + "vert": "\nprecision highp float;\nuniform CCGlobal {\n mat4 cc_matView;\n mat4 cc_matViewInv;\n mat4 cc_matProj;\n mat4 cc_matProjInv;\n mat4 cc_matViewProj;\n mat4 cc_matViewProjInv;\n vec4 cc_cameraPos;\n vec4 cc_time;\n mediump vec4 cc_screenSize;\n mediump vec4 cc_screenScale;\n};\nuniform CCLocal {\n mat4 cc_matWorld;\n mat4 cc_matWorldIT;\n};\nin vec3 a_position;\nin vec4 a_color;\nin vec2 a_uv0;\nout vec4 v_color;\nout vec2 v_uv0;\nvoid main () {\n vec4 pos = vec4(a_position, 1.0);\n #if CC_USE_MODEL\n pos = cc_matViewProj * cc_matWorld * pos;\n #else\n pos = cc_matViewProj * pos;\n #endif\n gl_Position = pos;\n v_color = a_color;\n v_uv0 = a_uv0;\n}", + "frag": "\nprecision highp float;\n#if USE_ALPHA_TEST\n uniform ALPHA_TEST {\n float alphaThreshold;\n };\n#endif\nvoid ALPHA_TEST (in vec4 color) {\n #if USE_ALPHA_TEST\n if (color.a < alphaThreshold) discard;\n #endif\n}\nvoid ALPHA_TEST (in float alpha) {\n #if USE_ALPHA_TEST\n if (alpha < alphaThreshold) discard;\n #endif\n}\nin vec4 v_color;\nin vec2 v_uv0;\nuniform sampler2D texture;\nuniform ShineProperties {\n vec4 shineColor;\n float shinePosition;\n float shineWidth;\n float shineStrength;\n float shineAngle;\n};\nvoid main () {\n vec4 color = vec4(1.0);\n vec4 texture_tmp = texture(texture, v_uv0);\n #if CC_USE_ALPHA_ATLAS_texture\n texture_tmp.a *= texture(texture, v_uv0 + vec2(0, 0.5)).r;\n #endif\n #if INPUT_IS_GAMMA\n color.rgb *= (texture_tmp.rgb * texture_tmp.rgb);\n color.a *= texture_tmp.a;\n #else\n color *= texture_tmp;\n #endif\n color *= v_color;\n float sweepAxis = v_uv0.x + (v_uv0.y - 0.5) * shineAngle;\n float bandIn = smoothstep(\n shinePosition - shineWidth,\n shinePosition,\n sweepAxis\n );\n float bandOut = 1.0 - smoothstep(\n shinePosition,\n shinePosition + shineWidth,\n sweepAxis\n );\n float shine = pow(clamp(bandIn * bandOut, 0.0, 1.0), 1.35);\n color.rgb += shineColor.rgb * shine * shineStrength * color.a;\n ALPHA_TEST(color);\n #if USE_BGRA\n gl_FragColor = color.bgra;\n #else\n gl_FragColor = color.rgba;\n #endif\n}" + } + } + ], + "subMetas": {} +} \ No newline at end of file diff --git a/assets/jungle_treasure/font.meta b/assets/jungle_treasure/font.meta new file mode 100644 index 00000000..b7756117 --- /dev/null +++ b/assets/jungle_treasure/font.meta @@ -0,0 +1,13 @@ +{ + "ver": "1.1.3", + "uuid": "64f407da-52a8-406f-a8b2-632beb23dd03", + "importer": "folder", + "isBundle": false, + "bundleName": "", + "priority": 1, + "compressionType": {}, + "optimizeHotUpdate": {}, + "inlineSpriteFrames": {}, + "isRemoteBundle": {}, + "subMetas": {} +} \ No newline at end of file diff --git a/assets/jungle_treasure/font/count.fnt b/assets/jungle_treasure/font/count.fnt new file mode 100644 index 00000000..39f74f05 --- /dev/null +++ b/assets/jungle_treasure/font/count.fnt @@ -0,0 +1,20 @@ +info size=54 unicode=1 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=1,1 outline=0 +common lineHeight=54 base=43 scaleW=256 scaleH=256 pages=1 packed=0 +page id=0 file="count.png" +chars count=16 +char id=43 x=2 y=2 width=48 height=54 xoffset=0 yoffset=0 xadvance=48 +char id=48 x=52 y=2 width=48 height=54 xoffset=0 yoffset=0 xadvance=48 +char id=49 x=102 y=2 width=48 height=54 xoffset=0 yoffset=0 xadvance=48 +char id=50 x=152 y=2 width=48 height=54 xoffset=0 yoffset=0 xadvance=48 +char id=51 x=202 y=2 width=48 height=54 xoffset=0 yoffset=0 xadvance=48 +char id=52 x=2 y=58 width=48 height=54 xoffset=0 yoffset=0 xadvance=48 +char id=53 x=52 y=58 width=48 height=54 xoffset=0 yoffset=0 xadvance=48 +char id=54 x=102 y=58 width=48 height=54 xoffset=0 yoffset=0 xadvance=48 +char id=55 x=152 y=58 width=48 height=54 xoffset=0 yoffset=0 xadvance=48 +char id=56 x=202 y=58 width=48 height=54 xoffset=0 yoffset=0 xadvance=48 +char id=57 x=2 y=114 width=48 height=54 xoffset=0 yoffset=0 xadvance=48 +char id=120 x=52 y=114 width=48 height=54 xoffset=0 yoffset=0 xadvance=48 +char id=20998 x=102 y=114 width=52 height=48 xoffset=0 yoffset=0 xadvance=52 +char id=23567 x=156 y=114 width=51 height=48 xoffset=0 yoffset=0 xadvance=51 +char id=26102 x=2 y=170 width=53 height=49 xoffset=0 yoffset=0 xadvance=53 +char id=38047 x=57 y=170 width=52 height=50 xoffset=0 yoffset=0 xadvance=52 diff --git a/assets/jungle_treasure/font/count.fnt.meta b/assets/jungle_treasure/font/count.fnt.meta new file mode 100644 index 00000000..e9de4e1d --- /dev/null +++ b/assets/jungle_treasure/font/count.fnt.meta @@ -0,0 +1,8 @@ +{ + "ver": "2.1.2", + "uuid": "47f8ba15-3666-48c0-8b10-16c61b4d39ae", + "importer": "bitmap-font", + "textureUuid": "e52c59bd-8062-4446-82c2-013ecd2f0a9c", + "fontSize": 54, + "subMetas": {} +} \ No newline at end of file diff --git a/assets/jungle_treasure/font/count.png b/assets/jungle_treasure/font/count.png new file mode 100644 index 00000000..a1fbff20 Binary files /dev/null and b/assets/jungle_treasure/font/count.png differ diff --git a/assets/jungle_treasure/font/count.png.meta b/assets/jungle_treasure/font/count.png.meta new file mode 100644 index 00000000..de1a2c7d --- /dev/null +++ b/assets/jungle_treasure/font/count.png.meta @@ -0,0 +1,38 @@ +{ + "ver": "2.3.7", + "uuid": "e52c59bd-8062-4446-82c2-013ecd2f0a9c", + "importer": "texture", + "type": "sprite", + "wrapMode": "clamp", + "filterMode": "bilinear", + "premultiplyAlpha": false, + "genMipmaps": false, + "packable": true, + "width": 256, + "height": 256, + "platformSettings": {}, + "subMetas": { + "count": { + "ver": "1.0.6", + "uuid": "6dc14622-3a68-4912-afae-262c50a8d252", + "importer": "sprite-frame", + "rawTextureUuid": "e52c59bd-8062-4446-82c2-013ecd2f0a9c", + "trimType": "auto", + "trimThreshold": 1, + "rotated": false, + "offsetX": -3, + "offsetY": 16, + "trimX": 2, + "trimY": 4, + "width": 246, + "height": 216, + "rawWidth": 256, + "rawHeight": 256, + "borderTop": 0, + "borderBottom": 0, + "borderLeft": 0, + "borderRight": 0, + "subMetas": {} + } + } +} \ No newline at end of file diff --git a/assets/jungle_treasure/font/font.fnt b/assets/jungle_treasure/font/font.fnt new file mode 100644 index 00000000..63f94e1e --- /dev/null +++ b/assets/jungle_treasure/font/font.fnt @@ -0,0 +1,14 @@ +info size=56 unicode=1 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=1,1 outline=0 +common lineHeight=56 base=44 scaleW=256 scaleH=256 pages=1 packed=0 +page id=0 file="font.png" +chars count=10 +char id=48 x=2 y=2 width=51 height=56 xoffset=0 yoffset=0 xadvance=51 +char id=49 x=55 y=2 width=51 height=56 xoffset=0 yoffset=0 xadvance=51 +char id=50 x=108 y=2 width=51 height=56 xoffset=0 yoffset=0 xadvance=51 +char id=51 x=161 y=2 width=51 height=56 xoffset=0 yoffset=0 xadvance=51 +char id=52 x=2 y=60 width=51 height=56 xoffset=0 yoffset=0 xadvance=51 +char id=53 x=55 y=60 width=51 height=56 xoffset=0 yoffset=0 xadvance=51 +char id=54 x=108 y=60 width=51 height=56 xoffset=0 yoffset=0 xadvance=51 +char id=55 x=161 y=60 width=51 height=56 xoffset=0 yoffset=0 xadvance=51 +char id=56 x=2 y=118 width=51 height=56 xoffset=0 yoffset=0 xadvance=51 +char id=57 x=55 y=118 width=51 height=56 xoffset=0 yoffset=0 xadvance=51 diff --git a/assets/jungle_treasure/font/font.fnt.meta b/assets/jungle_treasure/font/font.fnt.meta new file mode 100644 index 00000000..331ea901 --- /dev/null +++ b/assets/jungle_treasure/font/font.fnt.meta @@ -0,0 +1,8 @@ +{ + "ver": "2.1.2", + "uuid": "bbc89572-f0a5-4b86-99a0-600b101843e3", + "importer": "bitmap-font", + "textureUuid": "d11b2ef9-4a7e-49c9-9bc9-57c8addb9e78", + "fontSize": 56, + "subMetas": {} +} \ No newline at end of file diff --git a/assets/jungle_treasure/font/font.png b/assets/jungle_treasure/font/font.png new file mode 100644 index 00000000..e368945d Binary files /dev/null and b/assets/jungle_treasure/font/font.png differ diff --git a/assets/jungle_treasure/font/font.png.meta b/assets/jungle_treasure/font/font.png.meta new file mode 100644 index 00000000..c752f604 --- /dev/null +++ b/assets/jungle_treasure/font/font.png.meta @@ -0,0 +1,38 @@ +{ + "ver": "2.3.7", + "uuid": "d11b2ef9-4a7e-49c9-9bc9-57c8addb9e78", + "importer": "texture", + "type": "sprite", + "wrapMode": "clamp", + "filterMode": "bilinear", + "premultiplyAlpha": false, + "genMipmaps": false, + "packable": true, + "width": 256, + "height": 256, + "platformSettings": {}, + "subMetas": { + "font": { + "ver": "1.0.6", + "uuid": "a2676e00-8583-44a6-be30-b76a4377961a", + "importer": "sprite-frame", + "rawTextureUuid": "d11b2ef9-4a7e-49c9-9bc9-57c8addb9e78", + "trimType": "auto", + "trimThreshold": 1, + "rotated": false, + "offsetX": -21.5, + "offsetY": 39.5, + "trimX": 2, + "trimY": 3, + "width": 209, + "height": 171, + "rawWidth": 256, + "rawHeight": 256, + "borderTop": 0, + "borderBottom": 0, + "borderLeft": 0, + "borderRight": 0, + "subMetas": {} + } + } +} \ No newline at end of file diff --git a/assets/jungle_treasure/img/anniu.png b/assets/jungle_treasure/img/anniu.png index 6c7dc578..6ad3c294 100644 Binary files a/assets/jungle_treasure/img/anniu.png and b/assets/jungle_treasure/img/anniu.png differ diff --git a/assets/jungle_treasure/img/chuizi.png b/assets/jungle_treasure/img/chuizi.png index 1aebb3e0..ef6b3ba0 100644 Binary files a/assets/jungle_treasure/img/chuizi.png and b/assets/jungle_treasure/img/chuizi.png differ diff --git a/assets/jungle_treasure/img/chuizi.png.meta b/assets/jungle_treasure/img/chuizi.png.meta index 9c316769..ccc9efad 100644 --- a/assets/jungle_treasure/img/chuizi.png.meta +++ b/assets/jungle_treasure/img/chuizi.png.meta @@ -8,8 +8,8 @@ "premultiplyAlpha": false, "genMipmaps": false, "packable": true, - "width": 110, - "height": 110, + "width": 316, + "height": 316, "platformSettings": {}, "subMetas": { "chuizi": { @@ -22,12 +22,12 @@ "rotated": false, "offsetX": -0.5, "offsetY": 0, - "trimX": 12, - "trimY": 11, - "width": 85, - "height": 88, - "rawWidth": 110, - "rawHeight": 110, + "trimX": 6, + "trimY": 0, + "width": 303, + "height": 316, + "rawWidth": 316, + "rawHeight": 316, "borderTop": 0, "borderBottom": 0, "borderLeft": 0, diff --git a/assets/jungle_treasure/img/dongjie.png b/assets/jungle_treasure/img/dongjie.png index cd469ec7..31bf597f 100644 Binary files a/assets/jungle_treasure/img/dongjie.png and b/assets/jungle_treasure/img/dongjie.png differ diff --git a/assets/jungle_treasure/img/dongjie.png.meta b/assets/jungle_treasure/img/dongjie.png.meta index 857aa3cf..916da262 100644 --- a/assets/jungle_treasure/img/dongjie.png.meta +++ b/assets/jungle_treasure/img/dongjie.png.meta @@ -8,8 +8,8 @@ "premultiplyAlpha": false, "genMipmaps": false, "packable": true, - "width": 110, - "height": 110, + "width": 316, + "height": 316, "platformSettings": {}, "subMetas": { "dongjie": { @@ -20,14 +20,14 @@ "trimType": "auto", "trimThreshold": 1, "rotated": false, - "offsetX": -0.5, + "offsetX": 0, "offsetY": 0.5, - "trimX": 13, - "trimY": 4, - "width": 83, - "height": 101, - "rawWidth": 110, - "rawHeight": 110, + "trimX": 38, + "trimY": 12, + "width": 240, + "height": 291, + "rawWidth": 316, + "rawHeight": 316, "borderTop": 0, "borderBottom": 0, "borderLeft": 0, diff --git a/assets/jungle_treasure/img/guanbi.png b/assets/jungle_treasure/img/guanbi.png index 9bf9beba..36219834 100644 Binary files a/assets/jungle_treasure/img/guanbi.png and b/assets/jungle_treasure/img/guanbi.png differ diff --git a/assets/jungle_treasure/img/guidao.png b/assets/jungle_treasure/img/guidao.png index 37c18853..adb3cf49 100644 Binary files a/assets/jungle_treasure/img/guidao.png and b/assets/jungle_treasure/img/guidao.png differ diff --git a/assets/jungle_treasure/img/jinbi.png b/assets/jungle_treasure/img/jinbi.png index 33227bfd..6666d646 100644 Binary files a/assets/jungle_treasure/img/jinbi.png and b/assets/jungle_treasure/img/jinbi.png differ diff --git a/assets/jungle_treasure/img/lingqu.png b/assets/jungle_treasure/img/lingqu.png new file mode 100644 index 00000000..8da50b3d Binary files /dev/null and b/assets/jungle_treasure/img/lingqu.png differ diff --git a/assets/shop/img/yueka1.png.meta b/assets/jungle_treasure/img/lingqu.png.meta similarity index 64% rename from assets/shop/img/yueka1.png.meta rename to assets/jungle_treasure/img/lingqu.png.meta index 27de43ba..bf9a2f27 100644 --- a/assets/shop/img/yueka1.png.meta +++ b/assets/jungle_treasure/img/lingqu.png.meta @@ -1,6 +1,6 @@ { "ver": "2.3.7", - "uuid": "91c4090b-98ee-493a-a65f-a0aa980f4e3f", + "uuid": "3c6fd649-03ec-450d-a65f-81eabb74cd2b", "importer": "texture", "type": "sprite", "wrapMode": "clamp", @@ -8,15 +8,15 @@ "premultiplyAlpha": false, "genMipmaps": false, "packable": true, - "width": 914, - "height": 1134, + "width": 116, + "height": 48, "platformSettings": {}, "subMetas": { - "yueka1": { + "lingqu": { "ver": "1.0.6", - "uuid": "e30e1de6-ef28-4077-8f31-a11e73c8c789", + "uuid": "13477134-10b7-420c-a9dc-17818e8303ec", "importer": "sprite-frame", - "rawTextureUuid": "91c4090b-98ee-493a-a65f-a0aa980f4e3f", + "rawTextureUuid": "3c6fd649-03ec-450d-a65f-81eabb74cd2b", "trimType": "auto", "trimThreshold": 1, "rotated": false, @@ -24,10 +24,10 @@ "offsetY": 0, "trimX": 0, "trimY": 0, - "width": 914, - "height": 1134, - "rawWidth": 914, - "rawHeight": 1134, + "width": 116, + "height": 48, + "rawWidth": 116, + "rawHeight": 48, "borderTop": 0, "borderBottom": 0, "borderLeft": 0, diff --git a/assets/jungle_treasure/img/maobao.png b/assets/jungle_treasure/img/maobao.png index fc8bc19e..b1b291eb 100644 Binary files a/assets/jungle_treasure/img/maobao.png and b/assets/jungle_treasure/img/maobao.png differ diff --git a/assets/jungle_treasure/img/mofabang.png b/assets/jungle_treasure/img/mofabang.png index 4a3f735d..c47c55d5 100644 Binary files a/assets/jungle_treasure/img/mofabang.png and b/assets/jungle_treasure/img/mofabang.png differ diff --git a/assets/jungle_treasure/img/mofabang.png.meta b/assets/jungle_treasure/img/mofabang.png.meta index 074361d8..161adb41 100644 --- a/assets/jungle_treasure/img/mofabang.png.meta +++ b/assets/jungle_treasure/img/mofabang.png.meta @@ -8,8 +8,8 @@ "premultiplyAlpha": false, "genMipmaps": false, "packable": true, - "width": 110, - "height": 110, + "width": 316, + "height": 316, "platformSettings": {}, "subMetas": { "mofabang": { @@ -20,14 +20,14 @@ "trimType": "auto", "trimThreshold": 1, "rotated": false, - "offsetX": 0, - "offsetY": 0.5, - "trimX": 13, - "trimY": 10, - "width": 84, - "height": 89, - "rawWidth": 110, - "rawHeight": 110, + "offsetX": 8.5, + "offsetY": -7.5, + "trimX": 25, + "trimY": 17, + "width": 283, + "height": 297, + "rawWidth": 316, + "rawHeight": 316, "borderTop": 0, "borderBottom": 0, "borderLeft": 0, diff --git a/assets/jungle_treasure/img/shizhong.png b/assets/jungle_treasure/img/shizhong.png index a39f4116..7770326c 100644 Binary files a/assets/jungle_treasure/img/shizhong.png and b/assets/jungle_treasure/img/shizhong.png differ diff --git a/assets/jungle_treasure/img/suo.png b/assets/jungle_treasure/img/suo.png index 7f80ad0e..516cf4a0 100644 Binary files a/assets/jungle_treasure/img/suo.png and b/assets/jungle_treasure/img/suo.png differ diff --git a/assets/jungle_treasure/img/title.png.meta b/assets/jungle_treasure/img/title.png.meta index d87220f8..60617d57 100644 --- a/assets/jungle_treasure/img/title.png.meta +++ b/assets/jungle_treasure/img/title.png.meta @@ -7,7 +7,7 @@ "filterMode": "bilinear", "premultiplyAlpha": false, "genMipmaps": false, - "packable": true, + "packable": false, "width": 897, "height": 505, "platformSettings": {}, diff --git a/assets/jungle_treasure/img/unlock.png b/assets/jungle_treasure/img/unlock.png new file mode 100644 index 00000000..c1f2e79e Binary files /dev/null and b/assets/jungle_treasure/img/unlock.png differ diff --git a/assets/jungle_treasure/img/unlock.png.meta b/assets/jungle_treasure/img/unlock.png.meta new file mode 100644 index 00000000..73a8b8e1 --- /dev/null +++ b/assets/jungle_treasure/img/unlock.png.meta @@ -0,0 +1,38 @@ +{ + "ver": "2.3.7", + "uuid": "5f49afb8-18d4-455d-aafb-80a84a4a012b", + "importer": "texture", + "type": "sprite", + "wrapMode": "clamp", + "filterMode": "bilinear", + "premultiplyAlpha": false, + "genMipmaps": false, + "packable": true, + "width": 55, + "height": 71, + "platformSettings": {}, + "subMetas": { + "unlock": { + "ver": "1.0.6", + "uuid": "7052be27-3161-4de4-b558-eb9cfe931983", + "importer": "sprite-frame", + "rawTextureUuid": "5f49afb8-18d4-455d-aafb-80a84a4a012b", + "trimType": "auto", + "trimThreshold": 1, + "rotated": false, + "offsetX": 0, + "offsetY": 0, + "trimX": 0, + "trimY": 0, + "width": 55, + "height": 71, + "rawWidth": 55, + "rawHeight": 71, + "borderTop": 0, + "borderBottom": 0, + "borderLeft": 0, + "borderRight": 0, + "subMetas": {} + } + } +} \ No newline at end of file diff --git a/assets/jungle_treasure/img/wuxiantili.png b/assets/jungle_treasure/img/wuxiantili.png index d6503903..bc0dcb69 100644 Binary files a/assets/jungle_treasure/img/wuxiantili.png and b/assets/jungle_treasure/img/wuxiantili.png differ diff --git a/assets/jungle_treasure/prefab/jungle.prefab b/assets/jungle_treasure/prefab/jungle.prefab index ab9fead5..31a1146a 100644 --- a/assets/jungle_treasure/prefab/jungle.prefab +++ b/assets/jungle_treasure/prefab/jungle.prefab @@ -93,9 +93,27 @@ "buttonFrame": { "__uuid__": "11e37cd7-112f-482d-a4bf-3194ed243343" }, + "freeFrame": { + "__uuid__": "c88f6ff2-53de-4248-8506-e91389105070" + }, + "claimFrame": { + "__uuid__": "13477134-10b7-420c-a9dc-17818e8303ec" + }, + "yuanFrame": { + "__uuid__": "f74c44d3-897f-4e2d-b9d5-4ecc54aca467" + }, + "priceFont": { + "__uuid__": "bbc89572-f0a5-4b86-99a0-600b101843e3" + }, + "countFont": { + "__uuid__": "47f8ba15-3666-48c0-8b10-16c61b4d39ae" + }, "lockFrame": { "__uuid__": "8ff980c0-97df-4a6e-9bfb-e518c9bf44f3" }, + "unlockFrame": { + "__uuid__": "7052be27-3161-4de4-b558-eb9cfe931983" + }, "timerFrame": { "__uuid__": "1d3f2029-c7d6-42b1-8b5a-b46c335e8ea4" }, @@ -117,8 +135,8 @@ "infiniteHealthFrame": { "__uuid__": "6fa6521e-9112-42b6-a12c-e37a948c1e49" }, - "useMockPayment": true, - "previewAnimationOnly": true, + "useMockPayment": false, + "previewAnimationOnly": false, "_id": "" }, { diff --git a/assets/jungle_treasure/script/JungleAnimator.ts b/assets/jungle_treasure/script/JungleAnimator.ts index 10c267f3..7ca4d963 100644 --- a/assets/jungle_treasure/script/JungleAnimator.ts +++ b/assets/jungle_treasure/script/JungleAnimator.ts @@ -4,11 +4,111 @@ export default class JungleAnimator { private host: cc.Component, private contentNode: cc.Node, private cardFrame: cc.SpriteFrame, + private unlockFrame: cc.SpriteFrame, ) { } + /** 档位从锁定变为当前可领取时,播放锁旋转下坠和按钮回弹。 */ + public playUnlockEffect(card: cc.Node, onComplete: () => void): void { + let button = card && card.getChildByName("button"); + let buttonVisual = button && button.getChildByName("button_content"); + let lock = button && button.getChildByName("lock"); + if (!button || !lock) { + onComplete(); + return; + } + + let buttonComponent = button.getComponent(cc.Button); + let lockSprite = lock.getComponent(cc.Sprite); + let originalLockFrame = lockSprite && lockSprite.spriteFrame; + let originalLockSize = lock.getContentSize(); + let buttonLocalPosition = lock.getPosition(); + let originZIndex = lock.zIndex; + let lockWorldPosition = lock.convertToWorldSpaceAR(cc.Vec2.ZERO); + let originPosition = this.contentNode.convertToNodeSpaceAR(lockWorldPosition); + let screenBottomY = cc.visibleRect && cc.visibleRect.bottom + ? cc.visibleRect.bottom.y + : 0; + let dropWorldPosition = cc.v2( + lockWorldPosition.x, + screenBottomY - lock.height * 0.6, + ); + let dropLocalPosition = this.contentNode.convertToNodeSpaceAR(dropWorldPosition); + let originX = originPosition.x; + let originY = originPosition.y; + let dropDistance = Math.max(0, originY - dropLocalPosition.y); + let dropDuration = Math.min(0.42, Math.max(0.22, dropDistance / 2800)); + button.stopAllActions(); + lock.stopAllActions(); + if (buttonVisual) { + buttonVisual.stopAllActions(); + buttonVisual.opacity = 0; + buttonVisual.scale = 0.92; + } + if (buttonComponent) { + buttonComponent.interactable = false; + } + + button.scale = 1; + lock.active = true; + lock.opacity = 255; + lock.scale = 1; + lock.angle = 0; + if (lockSprite && this.unlockFrame) { + lockSprite.spriteFrame = this.unlockFrame; + lock.setContentSize(this.unlockFrame.getOriginalSize()); + } + lock.parent = this.contentNode; + lock.zIndex = 60; + lock.setPosition(originX, originY); + + cc.tween(button) + .to(0.08, { scale: 1.08 }, { easing: "quadOut" }) + .to(0.1, { scale: 1 }, { easing: "backOut" }) + .start(); + + if (buttonVisual) { + cc.tween(buttonVisual) + .to(0.08, { opacity: 255, scale: 1.06 }, { easing: "backOut" }) + .to(0.1, { scale: 1 }, { easing: "sineOut" }) + .start(); + } + + cc.tween(lock) + .to(0.04, { y: originY + 7, angle: -12 }, { easing: "quadOut" }) + .to(0.04, { y: originY + 9, angle: 10 }, { easing: "sineInOut" }) + .to(0.03, { y: originY + 4, angle: -6 }, { easing: "sineInOut" }) + .parallel( + cc.tween().to(dropDuration, { + x: originX - 28, + y: dropLocalPosition.y, + angle: 220, + }, { easing: "quadIn" }), + cc.tween().to(dropDuration, { scale: 0.82 }, { easing: "quadIn" }), + ) + .call(() => { + lock.active = false; + lock.opacity = 255; + lock.scale = 1; + lock.angle = 0; + if (lockSprite && originalLockFrame) { + lockSprite.spriteFrame = originalLockFrame; + lock.setContentSize(originalLockSize); + } + lock.parent = button; + lock.zIndex = originZIndex; + lock.setPosition(buttonLocalPosition); + + if (buttonComponent) { + buttonComponent.interactable = true; + } + onComplete(); + }) + .start(); + } + public playClaimEffect(card: cc.Node, onComplete: () => void): void { const button = card.getChildByName("button"); - const buttonLabelNode = button && button.getChildByName("button_label"); + const buttonVisual = button && button.getChildByName("button_content"); const lock = button && button.getChildByName("lock"); if (button) { button.getComponent(cc.Button).interactable = false; @@ -17,8 +117,8 @@ export default class JungleAnimator { .to(0.1, { scale: 1 }) .start(); } - if (buttonLabelNode) { - buttonLabelNode.opacity = 0; + if (buttonVisual) { + buttonVisual.opacity = 0; } if (lock) { lock.active = false; @@ -150,6 +250,33 @@ export default class JungleAnimator { .start(); } + /** 最前方礼包的短促放大回弹,用于吸引点击。 */ + public playCardAttention(card: cc.Node, onComplete: () => void): void { + if (!card || !cc.isValid(card) || !card.activeInHierarchy) { + onComplete(); + return; + } + + card.scaleX = 1; + card.scaleY = 1; + cc.tween(card) + .to(0.14, { scaleX: 1.06, scaleY: 0.95 }, { easing: "sineInOut" }) + .to(0.17, { scaleX: 0.97, scaleY: 1.06 }, { easing: "sineInOut" }) + .to(0.18, { scaleX: 1.025, scaleY: 0.985 }, { easing: "sineInOut" }) + .to(0.2, { scaleX: 1, scaleY: 1 }, { easing: "sineOut" }) + .call(onComplete) + .start(); + } + + public stopCardAttention(card: cc.Node): void { + if (!card || !cc.isValid(card)) { + return; + } + cc.Tween.stopAllByTarget(card); + card.scaleX = 1; + card.scaleY = 1; + } + public playLockedFeedback(card: cc.Node): void { card.stopAllActions(); const x = card.x; diff --git a/assets/jungle_treasure/script/JungleAnimator.ts.meta b/assets/jungle_treasure/script/JungleAnimator.ts.meta index 901c7237..54f79df8 100644 --- a/assets/jungle_treasure/script/JungleAnimator.ts.meta +++ b/assets/jungle_treasure/script/JungleAnimator.ts.meta @@ -7,4 +7,4 @@ "loadPluginInNative": true, "loadPluginInEditor": false, "subMetas": {} -} +} \ No newline at end of file diff --git a/assets/jungle_treasure/script/JungleConfig.ts.meta b/assets/jungle_treasure/script/JungleConfig.ts.meta index cd0bcca6..b6ed6fa9 100644 --- a/assets/jungle_treasure/script/JungleConfig.ts.meta +++ b/assets/jungle_treasure/script/JungleConfig.ts.meta @@ -7,4 +7,4 @@ "loadPluginInNative": true, "loadPluginInEditor": false, "subMetas": {} -} +} \ No newline at end of file diff --git a/assets/jungle_treasure/script/JungleProgress.ts.meta b/assets/jungle_treasure/script/JungleProgress.ts.meta index aea1f274..02d449da 100644 --- a/assets/jungle_treasure/script/JungleProgress.ts.meta +++ b/assets/jungle_treasure/script/JungleProgress.ts.meta @@ -7,4 +7,4 @@ "loadPluginInNative": true, "loadPluginInEditor": false, "subMetas": {} -} +} \ No newline at end of file diff --git a/assets/jungle_treasure/script/JungleRewardService.ts b/assets/jungle_treasure/script/JungleRewardService.ts index 959c345c..2048cbe3 100644 --- a/assets/jungle_treasure/script/JungleRewardService.ts +++ b/assets/jungle_treasure/script/JungleRewardService.ts @@ -8,26 +8,55 @@ export default class JungleRewardService { rewards.forEach((reward) => { const itemId = this.getItemId(reward); if (gameTool && typeof gameTool.getPassCheckPorp === "function") { - if (reward.type === "infinite_health" - && reward.count !== 900 - && reward.count !== 1800 - && reward.count !== 3600 - && typeof gameTool.setUserPowerTime === "function") { - gameTool.setUserPowerTime(reward.count); - } else { - gameTool.getPassCheckPorp(itemId, reward.count, false); + // 所有 Jungle 奖励统一由公共方法完成缓存、后端同步和 resource_get。 + gameTool.getPassCheckPorp(itemId, reward.count, false, "jungle"); + } + }); + + let canvas = cc.find("Canvas"); + let shopComponents: any[] = []; + if (canvas) { + canvas.children.forEach((child) => { + let shop: any = child.getComponent("shop"); + if (shop) { + shopComponents.push(shop); + } + }); + } + shopComponents.forEach((shop) => { + if (shop && shop.node && shop.node.activeInHierarchy + && typeof shop.refreshJungleRewardDisplay === "function") { + try { + shop.refreshJungleRewardDisplay(); + } + catch (error) { + cc.warn("[JungleTreasure] 刷新商城奖励显示失败", error); } } }); - const canvas = cc.find("Canvas"); - const home = canvas && canvas.getComponent("JiaZai"); + let home: any = canvas && canvas.getComponent("JiaZai"); if (home) { - if (typeof home.updateCoin === "function") { - home.updateCoin(); + try { + if (typeof home.updateCoin === "function") { + home.updateCoin(); + } + if (typeof home.updatePower === "function") { + home.updatePower(); + } } - if (typeof home.updatePower === "function") { - home.updatePower(); + catch (error) { + cc.warn("[JungleTreasure] 刷新 HomeScene 奖励显示失败", error); + } + } + + let game: any = canvas && canvas.getComponent("SceneManager"); + if (game && typeof game.refreshJungleRewardDisplay === "function") { + try { + game.refreshJungleRewardDisplay(); + } + catch (error) { + cc.warn("[JungleTreasure] 刷新 GameScene 奖励显示失败", error); } } @@ -35,6 +64,10 @@ export default class JungleRewardService { } private getItemId(reward: JungleRewardConfig): number { + // Jungle 无限体力统一使用 3004 标识,实际时长由 reward.count 配置。 + if (reward.type === "infinite_health") { + return 3004; + } if (typeof reward.itemId === "number") { return reward.itemId; } @@ -43,11 +76,6 @@ export default class JungleRewardService { case "freeze": return 2001; case "hammer": return 2002; case "magic_wand": return 2003; - case "infinite_health": - if (reward.count === 900) return 3001; - if (reward.count === 1800) return 3002; - if (reward.count === 3600) return 3003; - return 3001; default: return 1001; } } diff --git a/assets/jungle_treasure/script/JungleRewardService.ts.meta b/assets/jungle_treasure/script/JungleRewardService.ts.meta index 1e936c20..fade9d34 100644 --- a/assets/jungle_treasure/script/JungleRewardService.ts.meta +++ b/assets/jungle_treasure/script/JungleRewardService.ts.meta @@ -7,4 +7,4 @@ "loadPluginInNative": true, "loadPluginInEditor": false, "subMetas": {} -} +} \ No newline at end of file diff --git a/assets/jungle_treasure/script/JungleTitleShine.ts b/assets/jungle_treasure/script/JungleTitleShine.ts new file mode 100644 index 00000000..e503ae57 --- /dev/null +++ b/assets/jungle_treasure/script/JungleTitleShine.ts @@ -0,0 +1,80 @@ +const { ccclass, requireComponent } = cc._decorator; + +/** Jungle title 专用的左至右高亮扫光。 */ +@ccclass +@requireComponent(cc.Sprite) +export default class JungleTitleShine extends cc.Component { + private sprite: cc.Sprite = null; + private material: cc.Material = null; + private moveDuration: number = 0.85; + private repeatDelay: number = 1.5; + private startDelay: number = 0.25; + private elapsed: number = 0; + private delayRemaining: number = 0; + private startPosition: number = -0.35; + private endPosition: number = 1.35; + + protected onLoad(): void { + this.sprite = this.getComponent(cc.Sprite); + this.delayRemaining = this.startDelay; + this.loadMaterial(); + } + + protected update(dt: number): void { + if (!this.material) { + return; + } + if (this.delayRemaining > 0) { + this.delayRemaining = Math.max(0, this.delayRemaining - dt); + return; + } + + this.elapsed += dt; + let progress = Math.min(1, this.elapsed / this.moveDuration); + let position = this.startPosition + + (this.endPosition - this.startPosition) * progress; + this.material.setProperty("shinePosition", position); + if (progress >= 1) { + this.elapsed = 0; + this.delayRemaining = this.repeatDelay; + this.material.setProperty("shinePosition", this.startPosition); + } + } + + protected onDestroy(): void { + if (this.material && cc.isValid(this.material)) { + this.material.destroy(); + } + this.material = null; + this.sprite = null; + } + + private loadMaterial(): void { + let bundle = cc.assetManager.getBundle("jungle_treasure"); + if (!bundle || !this.sprite) { + cc.warn("[JungleTreasure] title 扫光材质所在分包未加载"); + return; + } + + bundle.load("effect/title_shine", cc.EffectAsset, (error: Error, effect: cc.EffectAsset) => { + if (error || !effect || !cc.isValid(this.node) || !this.sprite) { + if (error) { + cc.warn("[JungleTreasure] title 扫光 Shader 加载失败", error); + } + return; + } + + let material = new cc.Material(); + // Cocos Creator 2.4 使用 effectAsset 为运行时材质指定 Shader。 + // @ts-ignore + material.effectAsset = effect; + material.setProperty("shinePosition", this.startPosition); + material.setProperty("shineWidth", 0.13); + material.setProperty("shineStrength", 1.8); + material.setProperty("shineAngle", 0.2); + material.setProperty("shineColor", [1.0, 0.95, 0.68, 1.0]); + this.material = material; + this.sprite.setMaterial(0, material); + }); + } +} diff --git a/assets/jungle_treasure/script/JungleTitleShine.ts.meta b/assets/jungle_treasure/script/JungleTitleShine.ts.meta new file mode 100644 index 00000000..32bb1523 --- /dev/null +++ b/assets/jungle_treasure/script/JungleTitleShine.ts.meta @@ -0,0 +1,10 @@ +{ + "ver": "1.1.0", + "uuid": "ed28a489-e107-4b29-aec5-73e87d9044bd", + "importer": "typescript", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/assets/jungle_treasure/script/JungleTypes.ts b/assets/jungle_treasure/script/JungleTypes.ts index 70f6c5b1..1bac5214 100644 --- a/assets/jungle_treasure/script/JungleTypes.ts +++ b/assets/jungle_treasure/script/JungleTypes.ts @@ -1,4 +1,5 @@ export type JungleRewardType = "coin" | "freeze" | "hammer" | "magic_wand" | "infinite_health"; +export type JungleTierState = 0 | 1 | 2; export interface JungleRewardConfig { type: JungleRewardType; @@ -9,13 +10,17 @@ export interface JungleRewardConfig { export interface JungleTierConfig { id: number; price: number; + isPaid?: boolean; productId?: string; + state?: JungleTierState; rewards: JungleRewardConfig[]; } export interface JungleActivityConfig { activityId: string; - endAt: number; + version?: number; + totalTiers?: number; + endAt?: number; tiers: JungleTierConfig[]; } @@ -24,6 +29,11 @@ export interface JungleViewAssets { trackFrame: cc.SpriteFrame; titleFrame: cc.SpriteFrame; buttonFrame: cc.SpriteFrame; + freeFrame: cc.SpriteFrame; + claimFrame: cc.SpriteFrame; + yuanFrame: cc.SpriteFrame; + priceFont: cc.BitmapFont; + countFont: cc.BitmapFont; lockFrame: cc.SpriteFrame; timerFrame: cc.SpriteFrame; closeFrame: cc.SpriteFrame; diff --git a/assets/jungle_treasure/script/JungleTypes.ts.meta b/assets/jungle_treasure/script/JungleTypes.ts.meta index 0e058840..6394201d 100644 --- a/assets/jungle_treasure/script/JungleTypes.ts.meta +++ b/assets/jungle_treasure/script/JungleTypes.ts.meta @@ -7,4 +7,4 @@ "loadPluginInNative": true, "loadPluginInEditor": false, "subMetas": {} -} +} \ No newline at end of file diff --git a/assets/jungle_treasure/script/JungleView.ts b/assets/jungle_treasure/script/JungleView.ts index c6623653..918cbca2 100644 --- a/assets/jungle_treasure/script/JungleView.ts +++ b/assets/jungle_treasure/script/JungleView.ts @@ -7,15 +7,16 @@ import { JungleViewAssets, JungleViewRefs, } from "./JungleTypes"; +import JungleTitleShine from "./JungleTitleShine"; /** 负责 Jungle Treasure 的节点创建、布局和静态渲染。 */ export default class JungleView { private readonly designWidth: number = 1080; private readonly slotCount: number = 6; - private contentNode: cc.Node = null; - private timerLabel: cc.Label = null; - private cards: cc.Node[] = []; - private slotPositions: cc.Vec2[] = []; + public contentNode: cc.Node = null; + public timerLabel: cc.Label = null; + public cards: cc.Node[] = []; + public slotPositions: cc.Vec2[] = []; constructor( private root: cc.Node, @@ -33,10 +34,10 @@ export default class JungleView { } this.stretchToParent(this.root); - const shade = new cc.Node("shade"); + let shade = new cc.Node("shade"); shade.parent = this.root; this.stretchToParent(shade); - const shadeGraphics = shade.addComponent(cc.Graphics); + let shadeGraphics = shade.addComponent(cc.Graphics); shadeGraphics.fillColor = cc.color(35, 25, 20, 205); shadeGraphics.rect(-1600, -2200, 3200, 4400); shadeGraphics.fill(); @@ -44,13 +45,13 @@ export default class JungleView { this.contentNode = new cc.Node("content"); this.contentNode.parent = this.root; this.contentNode.setContentSize(this.designWidth, this.root.height || 1920); - const contentScale = Math.min(1, (this.root.width || this.designWidth) / this.designWidth); + let contentScale = Math.min(1, (this.root.width || this.designWidth) / this.designWidth); this.contentNode.setScale(contentScale); - const contentHeight = (this.root.height || 1920) / contentScale; - const top = contentHeight * 0.5 - 80; - const row1Y = top - 1000; - const rowGap = 445; + let contentHeight = (this.root.height || 1920) / contentScale; + let top = contentHeight * 0.5 - 80; + let row1Y = top - 1000; + let rowGap = 445; this.slotPositions = [ cc.v2(-252, row1Y), cc.v2(252, row1Y), @@ -60,30 +61,40 @@ export default class JungleView { cc.v2(252, row1Y - rowGap * 2), ]; - const track = this.createSpriteNode("track", this.assets.trackFrame); + let track = this.createSpriteNode("track", this.assets.trackFrame); track.parent = this.contentNode; track.setPosition(0, row1Y - rowGap); track.zIndex = 0; - const title = this.createSpriteNode("title", this.assets.titleFrame); + let title = this.createSpriteNode("title", this.assets.titleFrame); title.parent = this.contentNode; title.setPosition(0, top - 515); title.zIndex = 3; + title.addComponent(JungleTitleShine); - const timer = this.createSpriteNode("timer", this.assets.timerFrame); + let timer = this.createSpriteNode("timer", this.assets.timerFrame); timer.parent = this.contentNode; timer.setPosition(-358, top - 235); timer.zIndex = 5; - this.timerLabel = this.createLabel(timer, "00:00:00", 37, cc.color(128, 91, 54), 220, 62); - this.timerLabel.node.setPosition(36, -200); - const close = this.createCloseButton(); + let timeNode = new cc.Node("time"); + timeNode.parent = timer; + timeNode.setContentSize(270, 62); + timeNode.setPosition(38, 0); + timeNode.zIndex = 10; + + this.timerLabel = this.createLabel(timeNode, "00:00:00", 46, cc.color(151, 116, 74), 270, 62); + this.timerLabel.enableBold = false; + this.timerLabel.node.name = "timerLabel"; + + + let close = this.createCloseButton(); close.parent = this.contentNode; close.setPosition(430, top - 245); close.zIndex = 5; for (let i = 0; i < this.slotCount; i++) { - const card = this.createCard(i); + let card = this.createCard(i); card.parent = this.contentNode; card.setPosition(this.slotPositions[i]); card.zIndex = 2; @@ -102,7 +113,7 @@ export default class JungleView { return; } for (let i = 0; i < this.cards.length; i++) { - const card = this.cards[i]; + let card = this.cards[i]; card.stopAllActions(); card.setAnchorPoint(0.5, 0.5); card.setPosition(this.slotPositions[i]); @@ -112,6 +123,26 @@ export default class JungleView { } } + /** 运行时重新绑定领取图,兼容分包预制体未带上新资源引用的情况。 */ + public setClaimFrame(frame: cc.SpriteFrame): void { + if (!frame) { + return; + } + this.assets.claimFrame = frame; + this.cards.forEach((card) => { + let button = card.getChildByName("button"); + let content = button && button.getChildByName("button_content"); + let claimImage = content && content.getChildByName("claim_image"); + let sprite = claimImage && claimImage.getComponent(cc.Sprite); + if (!sprite) { + return; + } + sprite.spriteFrame = frame; + sprite.sizeMode = cc.Sprite.SizeMode.RAW; + claimImage.setContentSize(frame.getOriginalSize()); + }); + } + public renderCard( card: cc.Node, config: JungleActivityConfig, @@ -119,50 +150,67 @@ export default class JungleView { tierIndex: number, slotIndex: number, ): void { - const tier = config.tiers[tierIndex]; + let tier = config.tiers[tierIndex]; card.active = !!tier; if (!tier) { return; } card.name = "reward_card_" + tier.id; - const rewardsNode = card.getChildByName("rewards"); + let rewardsNode = card.getChildByName("rewards"); rewardsNode.removeAllChildren(); this.renderRewards(rewardsNode, tier.rewards); - const button = card.getChildByName("button"); - const buttonLabel = button.getChildByName("button_label").getComponent(cc.Label); - const lock = button.getChildByName("lock"); - const isCurrent = slotIndex === 0 && !state.isPurchasing; - const isExpired = Date.now() >= config.endAt; + let button = card.getChildByName("button"); + let buttonContent = button.getChildByName("button_content"); + let lock = button.getChildByName("lock"); + let isCurrent = slotIndex === 0 && !state.isPurchasing; + let isUnlocked = tier.state !== 0; + let isExpired = !!config.endAt && Date.now() >= config.endAt; + let isPurchaseBlocked = isExpired && tier.price > 0 && tier.state === 0; - buttonLabel.string = this.formatPrice(tier); - buttonLabel.node.opacity = 255; + this.renderButtonContent(buttonContent, tier); + buttonContent.opacity = 255; + buttonContent.scale = 1; button.scale = 1; - lock.active = !isCurrent || isExpired; - button.getComponent(cc.Button).interactable = !state.isMoving && !isExpired; + lock.active = !isCurrent || !isUnlocked || isPurchaseBlocked; + button.getComponent(cc.Button).interactable = !state.isMoving && !isPurchaseBlocked; button.color = isCurrent ? cc.Color.WHITE : cc.color(220, 220, 220); } /** 更新倒计时并返回剩余秒数。 */ public updateTimer(config: JungleActivityConfig): number { + // console.log("更新倒计时", config); + // return; if (!this.timerLabel || !config) { return 0; } - const remaining = Math.max(0, Math.floor((config.endAt - Date.now()) / 1000)); - const days = Math.floor(remaining / 86400); - const hours = Math.floor((remaining % 86400) / 3600); - const minutes = Math.floor((remaining % 3600) / 60); - const seconds = remaining % 60; - this.timerLabel.string = (days > 0 ? days + "天 " : "") - + this.pad(hours) + ":" + this.pad(minutes) + ":" + this.pad(seconds); + let endAt = Number(config.endAt); + if (!endAt || !isFinite(endAt)) { + this.timerLabel.string = "活动中"; + return -1; + } + let remaining = Math.max(0, Math.ceil((endAt - Date.now()) / 1000)); + if (remaining >= 24 * 60 * 60) { + let days = Math.floor(remaining / 86400); + let hours = Math.floor((remaining % 86400) / 3600); + this.timerLabel.string = days + "天" + hours + "时"; + } + else { + let hours = Math.floor(remaining / 3600); + let minutes = Math.floor((remaining % 3600) / 60); + let seconds = remaining % 60; + this.timerLabel.string = this.pad(hours) + + ":" + this.pad(minutes) + ":" + this.pad(seconds); + } + return remaining; } private stretchToParent(node: cc.Node): void { - const parentSize = node.parent ? node.parent.getContentSize() : cc.size(this.designWidth, 1920); + let parentSize = node.parent ? node.parent.getContentSize() : cc.size(this.designWidth, 1920); node.setContentSize(parentSize.width || this.designWidth, parentSize.height || 1920); - const widget = node.addComponent(cc.Widget); + let widget = node.addComponent(cc.Widget); widget.isAlignLeft = true; widget.isAlignRight = true; widget.isAlignTop = true; @@ -171,37 +219,51 @@ export default class JungleView { } private createCloseButton(): cc.Node { - const node = this.createSpriteNode("close", this.assets.closeFrame); + let node = this.createSpriteNode("close", this.assets.closeFrame); node.addComponent(cc.Button); node.on("click", this.onClose, this.eventTarget); return node; } private createCard(index: number): cc.Node { - const card = new cc.Node("reward_card_" + index); + let card = new cc.Node("reward_card_" + index); card.setContentSize(444, 400); - const bg = this.createSpriteNode("card_bg", this.assets.cardFrame); + let bg = this.createSpriteNode("card_bg", this.assets.cardFrame); bg.parent = card; - const rewards = new cc.Node("rewards"); + let rewards = new cc.Node("rewards"); rewards.parent = card; rewards.setContentSize(380, 230); rewards.setPosition(0, 48); - const button = this.createSpriteNode("button", this.assets.buttonFrame); + let button = this.createSpriteNode("button", this.assets.buttonFrame); button.parent = card; button.setPosition(0, -137); button.addComponent(cc.Button); button.on("click", this.onCardClicked, this.eventTarget); - const buttonLabel = this.createLabel(button, "免费", 48, cc.Color.WHITE, 270, 88); - buttonLabel.node.name = "button_label"; - const outline = buttonLabel.node.addComponent(cc.LabelOutline); - outline.color = cc.color(120, 53, 22); - outline.width = 4; + let buttonContent = new cc.Node("button_content"); + buttonContent.parent = button; + buttonContent.setContentSize(270, 88); - const lock = this.createSpriteNode("lock", this.assets.lockFrame); + let freeImage = this.createSpriteNode("free_image", this.assets.freeFrame); + freeImage.parent = buttonContent; + + let priceLabel = this.createLabel(buttonContent, "0", 56, cc.Color.WHITE, 110, 64); + priceLabel.node.name = "price_label"; + priceLabel.lineHeight = 56; + if (this.assets.priceFont) { + priceLabel.font = this.assets.priceFont; + } + + let yuanImage = this.createSpriteNode("yuan_image", this.assets.yuanFrame); + yuanImage.parent = buttonContent; + + let claimImage = this.createSpriteNode("claim_image", this.assets.claimFrame); + claimImage.parent = buttonContent; + + let lock = this.createSpriteNode("lock", this.assets.lockFrame); lock.parent = button; lock.setPosition(120, 18); lock.active = false; @@ -209,51 +271,119 @@ export default class JungleView { } private renderRewards(parent: cc.Node, rewards: JungleRewardConfig[]): void { - const safeRewards = rewards && rewards.length > 0 ? rewards.slice(0, 5) : []; + let safeRewards = rewards && rewards.length > 0 ? rewards.slice(0, 5) : []; if (safeRewards.length === 0) { return; } - const rows: JungleRewardConfig[][] = safeRewards.length <= 3 + let rewardYOffset = -25; + let numberScale = 1.2; + let iconScale = safeRewards.length === 1 + ? 1.1 + : (safeRewards.length === 2 ? 0.85 : 0.6); + let trianglePositions = [ + cc.v2(0, 75), + cc.v2(-90, -35), + cc.v2(90, -35), + ]; + let rows: JungleRewardConfig[][] = safeRewards.length <= 3 ? [safeRewards] : [safeRewards.slice(0, 3), safeRewards.slice(3)]; rows.forEach((row, rowIndex) => { - const frames = row.map((reward) => this.getRewardFrame(reward.type)); - const widths = frames.map((frame) => frame ? frame.getOriginalSize().width : 0); - const gap = 8; - const totalWidth = widths.reduce((sum, width) => sum + width, 0) + gap * Math.max(0, row.length - 1); + let frames = row.map((reward) => this.getRewardFrame(reward.type)); + let iconScales = row.map((reward) => this.isToolReward(reward.type) + ? 0.45 * (safeRewards.length === 1 ? 1 : iconScale) + : iconScale); + let widths = frames.map((frame, index) => frame ? frame.getOriginalSize().width * iconScales[index] : 0); + let gap = safeRewards.length === 1 + ? 8 + : (safeRewards.length === 2 ? 35 : 12); + let totalWidth = widths.reduce((sum, width) => sum + width, 0) + gap * Math.max(0, row.length - 1); let cursorX = -totalWidth * 0.5; + let rowItems: { group: cc.Node; label: cc.Label }[] = []; row.forEach((reward, index) => { - const frame = frames[index]; - const originalSize = frame ? frame.getOriginalSize() : cc.size(0, 0); - const group = new cc.Node("reward_" + reward.type); + let isToolReward = this.isToolReward(reward.type); + let frame = frames[index]; + let originalSize = frame ? frame.getOriginalSize() : cc.size(0, 0); + let group = new cc.Node("reward_" + reward.type); group.parent = parent; - const rowY = rows.length === 1 ? 15 : (rowIndex === 0 ? 65 : -55); - group.setPosition(cursorX + widths[index] * 0.5, rowY); + let rowY = (rows.length === 1 ? 15 : (rowIndex === 0 ? 65 : -55)) + rewardYOffset; + if (safeRewards.length === 3) { + rowY -= 20; + } + else if (safeRewards.length === 1 && isToolReward) { + rowY -= 20; + } + let groupX = cursorX + widths[index] * 0.5; + if (safeRewards.length === 3) { + groupX = trianglePositions[index].x; + rowY += trianglePositions[index].y; + } + group.setPosition(groupX, rowY); cursorX += widths[index] + gap; - const icon = this.createSpriteNode("icon", frame); + let icon = this.createSpriteNode("icon", frame); icon.parent = group; - const labelText = reward.type === "infinite_health" + let rewardIconScale = iconScales[index]; + icon.setScale(rewardIconScale); + let labelText = reward.type === "infinite_health" ? this.formatInfiniteHealth(reward.count) - : "×" + reward.count; - const label = this.createLabel( + : (reward.type === "coin" ? reward.count.toString() : "x" + reward.count); + let baseFontSize = rows.length === 1 ? 36 : 29; + let rewardNumberScale = safeRewards.length === 1 + ? numberScale * 1.1 + : numberScale; + if (isToolReward) { + rewardNumberScale *= 0.95; + } + let scaledWidth = originalSize.width * rewardIconScale; + let scaledHeight = originalSize.height * rewardIconScale; + let labelFontSize = Math.round(baseFontSize * rewardNumberScale); + let labelWidth = isToolReward + ? Math.max(70, labelText.length * labelFontSize) + : Math.max(126, scaledWidth * rewardNumberScale, labelText.length * labelFontSize); + let label = this.createLabel( group, labelText, - rows.length === 1 ? 36 : 29, + labelFontSize, cc.Color.WHITE, - Math.max(105, originalSize.width), - 44, + labelWidth, + Math.round(44 * rewardNumberScale), ); - label.node.setPosition(0, rows.length === 1 ? -originalSize.height * 0.5 - 10 : -originalSize.height * 0.5 + 17); - const outline = label.node.addComponent(cc.LabelOutline); + if (this.assets.countFont) { + label.font = this.assets.countFont; + } + if (isToolReward) { + label.node.setPosition(scaledWidth * 0.34, -scaledHeight * 0.34); + } + else { + label.node.setPosition(0, rows.length === 1 ? -scaledHeight * 0.5 - 10 : -scaledHeight * 0.5 + 17); + } + let outline = label.node.addComponent(cc.LabelOutline); outline.color = cc.color(62, 47, 38); - outline.width = 4; + outline.width = isToolReward ? 3 : 4; + rowItems.push({ group, label }); + }); + + // 通过移动整个奖励组对齐数量文字底边,保持图标和 Label 的相对位置不变。 + // 三角布局只对齐下方两个奖励;上方奖励保持独立高度。 + let alignedItems = safeRewards.length === 3 ? rowItems.slice(1) : rowItems; + let labelBottoms = alignedItems.map((item) => { + return item.group.y + item.label.node.y + - item.label.node.height * item.label.node.anchorY; + }); + let targetBottom = Math.min.apply(null, labelBottoms); + alignedItems.forEach((item, index) => { + item.group.y += targetBottom - labelBottoms[index]; }); }); } + private isToolReward(type: JungleRewardType): boolean { + return type === "freeze" || type === "hammer" || type === "magic_wand"; + } + private getRewardFrame(type: JungleRewardType): cc.SpriteFrame { switch (type) { case "coin": return this.assets.coinFrame; @@ -266,8 +396,8 @@ export default class JungleView { } private createSpriteNode(name: string, frame: cc.SpriteFrame): cc.Node { - const node = new cc.Node(name); - const sprite = node.addComponent(cc.Sprite); + let node = new cc.Node(name); + let sprite = node.addComponent(cc.Sprite); sprite.sizeMode = cc.Sprite.SizeMode.RAW; sprite.spriteFrame = frame; if (frame) { @@ -276,7 +406,7 @@ export default class JungleView { return node; } - private createLabel( + public createLabel( parent: cc.Node, text: string, fontSize: number, @@ -284,29 +414,62 @@ export default class JungleView { width: number, height: number, ): cc.Label { - const node = new cc.Node("label"); + let node = new cc.Node("label"); node.parent = parent; - node.setContentSize(width, height); - const label = node.addComponent(cc.Label); + let label = node.addComponent(cc.Label); + label.overflow = cc.Label.Overflow.SHRINK; label.string = text; label.fontSize = fontSize; label.lineHeight = fontSize + 8; label.horizontalAlign = cc.Label.HorizontalAlign.CENTER; label.verticalAlign = cc.Label.VerticalAlign.CENTER; - label.overflow = cc.Label.Overflow.SHRINK; + node.setContentSize(width, height); node.color = color; return label; } - private formatPrice(tier: JungleTierConfig): string { - return tier.price > 0 ? (Math.round(tier.price * 100) / 100).toString() + "元" : "免费"; + private renderButtonContent(content: cc.Node, tier: JungleTierConfig): void { + let freeImage = content.getChildByName("free_image"); + let priceLabelNode = content.getChildByName("price_label"); + let yuanImage = content.getChildByName("yuan_image"); + let claimImage = content.getChildByName("claim_image"); + let isFree = tier.price <= 0; + let isClaimablePaidTier = tier.price > 0 && tier.state === 1; + + let claimSprite = claimImage.getComponent(cc.Sprite); + if (claimSprite && !claimSprite.spriteFrame && this.assets.claimFrame) { + claimSprite.spriteFrame = this.assets.claimFrame; + claimImage.setContentSize(this.assets.claimFrame.getOriginalSize()); + } + + freeImage.active = isFree; + priceLabelNode.active = !isFree && !isClaimablePaidTier; + yuanImage.active = !isFree && !isClaimablePaidTier; + claimImage.active = isClaimablePaidTier; + if (isFree || isClaimablePaidTier) { + return; + } + + let priceText = Math.max(0, Math.round(Number(tier.price) || 0)).toString(); + let digitWidth = Math.max(51, priceText.length * 51); + let yuanWidth = this.assets.yuanFrame + ? this.assets.yuanFrame.getOriginalSize().width + : 51; + let gap = 5; + let totalWidth = digitWidth + gap + yuanWidth; + let left = -totalWidth * 0.5; + let priceLabel = priceLabelNode.getComponent(cc.Label); + priceLabel.string = priceText; + priceLabelNode.setContentSize(digitWidth, 64); + priceLabelNode.x = left + digitWidth * 0.5; + yuanImage.x = left + digitWidth + gap + yuanWidth * 0.5; } private formatInfiniteHealth(seconds: number): string { if (seconds >= 3600 && seconds % 3600 === 0) { - return "+" + (seconds / 3600) + "小时"; + return `${seconds / 3600}小时`; } - return "+" + Math.max(1, Math.floor(seconds / 60)) + "分钟"; + return `${Math.max(1, Math.floor(seconds / 60))}分钟`; } private pad(value: number): string { diff --git a/assets/jungle_treasure/script/JungleView.ts.meta b/assets/jungle_treasure/script/JungleView.ts.meta index 6a82c09e..2981ec2f 100644 --- a/assets/jungle_treasure/script/JungleView.ts.meta +++ b/assets/jungle_treasure/script/JungleView.ts.meta @@ -7,4 +7,4 @@ "loadPluginInNative": true, "loadPluginInEditor": false, "subMetas": {} -} +} \ No newline at end of file diff --git a/assets/jungle_treasure/script/jungle_Manager.ts b/assets/jungle_treasure/script/jungle_Manager.ts index 43e9a6ea..a63d5daa 100644 --- a/assets/jungle_treasure/script/jungle_Manager.ts +++ b/assets/jungle_treasure/script/jungle_Manager.ts @@ -1,14 +1,16 @@ import JungleAnimator from "./JungleAnimator"; -import { createJungleMockConfig } from "./JungleConfig"; import JungleProgress from "./JungleProgress"; import JungleRewardService from "./JungleRewardService"; import { JungleActivityConfig, JungleRenderState, JungleTierConfig, + JungleTierState, JungleViewAssets, } from "./JungleTypes"; import JungleView from "./JungleView"; +import Utils from "../../Script/module/Pay/Utils"; +import { MiniGameSdk } from "../../Script/Sdk/MiniGameSdk"; const { ccclass, property } = cc._decorator; @@ -30,9 +32,27 @@ export default class JungleManager extends cc.Component { @property(cc.SpriteFrame) buttonFrame: cc.SpriteFrame = null; + @property(cc.SpriteFrame) + freeFrame: cc.SpriteFrame = null; + + @property(cc.SpriteFrame) + claimFrame: cc.SpriteFrame = null; + + @property(cc.SpriteFrame) + yuanFrame: cc.SpriteFrame = null; + + @property(cc.BitmapFont) + priceFont: cc.BitmapFont = null; + + @property(cc.BitmapFont) + countFont: cc.BitmapFont = null; + @property(cc.SpriteFrame) lockFrame: cc.SpriteFrame = null; + @property(cc.SpriteFrame) + unlockFrame: cc.SpriteFrame = null; + @property(cc.SpriteFrame) timerFrame: cc.SpriteFrame = null; @@ -66,6 +86,18 @@ export default class JungleManager extends cc.Component { private slotPositions: cc.Vec2[] = []; private isMoving: boolean = false; private isPurchasing: boolean = false; + private isClaiming: boolean = false; + private isUnlockAnimating: boolean = false; + private isAttentionAnimating: boolean = false; + private hasExpired: boolean = false; + private claimTimeout: any = null; + private paymentTimeout: any = null; + private onShowListener: () => void = null; + private pendingPaymentTierId: number = 0; + private pendingPaymentProductId: string = ""; + private pendingPaymentPrice: number = 0; + private checkingIosOrder: boolean = false; + private entrySource: string = "jump_window"; private view: JungleView = null; private animator: JungleAnimator = null; @@ -75,8 +107,8 @@ export default class JungleManager extends cc.Component { protected onLoad(): void { this.progress = new JungleProgress(); this.rewardService = new JungleRewardService(); - this.activityConfig = createJungleMockConfig(); - this.currentTierIndex = this.progress.read(this.activityConfig, this.previewAnimationOnly); + this.activityConfig = null; + this.currentTierIndex = 0; this.view = new JungleView( this.node, @@ -88,30 +120,92 @@ export default class JungleManager extends cc.Component { const refs = this.view.build(); this.cards = refs.cards; this.slotPositions = refs.slotPositions; - this.animator = new JungleAnimator(this, refs.contentNode, this.cardFrame); + this.loadClaimFrame(); + this.animator = new JungleAnimator( + this, + refs.contentNode, + this.cardFrame, + this.unlockFrame, + ); - this.renderAllCards(); - this.updateTimer(); + this.cards.forEach((card) => card.active = false); this.schedule(this.updateTimer, 1); + this.schedule(this.playFirstCardAttention, 2); + this.registerWechatOnShow(); + } + + /** + * 领取图是后加入的分包资源,运行时再按路径加载一次。 + * 这样即使旧 prefab 缓存没有反序列化 claimFrame,也不会出现付费按钮空白。 + */ + private loadClaimFrame(): void { + let bundle = cc.assetManager.getBundle("jungle_treasure"); + if (!bundle) { + cc.warn("[JungleTreasure] 领取图片所在分包未加载"); + return; + } + bundle.load("img/lingqu", cc.SpriteFrame, (error: Error, frame: cc.SpriteFrame) => { + if (error || !frame || !cc.isValid(this.node) || !this.view) { + if (error) { + cc.warn("[JungleTreasure] 领取图片加载失败", error); + } + return; + } + this.claimFrame = frame; + this.view.setClaimFrame(frame); + this.renderAllCards(); + }); } protected onDestroy(): void { this.unschedule(this.updateTimer); + this.unschedule(this.playFirstCardAttention); + this.clearClaimTimeout(); + this.clearPaymentTimeout(); + this.unregisterWechatOnShow(); } - /** 后续服务器配置到达后调用此方法即可替换模拟表。 */ - public setServerConfig(config: JungleActivityConfig, claimedTierIndex?: number): void { - if (!config || !config.tiers || config.tiers.length === 0) { - cc.warn("[JungleTreasure] ignored empty server config"); - return; + /** 使用后端配置和 0/1/2 状态数组初始化;状态 2 的档位不进入展示队列。 */ + public setServerData( + config: JungleActivityConfig, + tierStates: JungleTierState[], + source?: string, + ): boolean { + if (!config || !Array.isArray(config.tiers) || !Array.isArray(tierStates) + || tierStates.length < config.tiers.length) { + cc.warn("[JungleTreasure] server config or tierStates is invalid"); + return false; + } + if (source !== undefined) { + this.entrySource = source === "front_page" || source === "shop_page" + ? source + : "jump_window"; } - this.activityConfig = config; - this.currentTierIndex = typeof claimedTierIndex === "number" - ? Math.max(0, Math.min(claimedTierIndex, config.tiers.length)) - : this.progress.read(config, this.previewAnimationOnly); + const visibleTiers: JungleTierConfig[] = []; + for (let index = 0; index < config.tiers.length; index++) { + const sourceTier = config.tiers[index]; + const rawState = Number(tierStates[index]); + if (rawState !== 0 && rawState !== 1 && rawState !== 2) { + cc.warn("[JungleTreasure] invalid tier state:", index + 1, tierStates[index]); + return false; + } + if (rawState === 2) { + continue; + } + visibleTiers.push(Object.assign({}, sourceTier, { + state: rawState as JungleTierState, + })); + } + + this.activityConfig = Object.assign({}, config, { + tiers: visibleTiers, + }); + this.currentTierIndex = 0; + this.hasExpired = false; this.renderAllCards(); this.updateTimer(); + return true; } /** 正式支付层完成后回调。支付失败或取消不会移动轨道。 */ @@ -121,7 +215,14 @@ export default class JungleManager extends cc.Component { } this.isPurchasing = false; if (success) { - this.completeCurrentTier(); + const tier = this.activityConfig && this.activityConfig.tiers[this.currentTierIndex]; + if (tier) { + tier.state = 1; + } + this.renderAllCards(); + if (tier) { + this.playTierUnlockAnimation(tier.id); + } } else { this.renderAllCards(); } @@ -129,16 +230,19 @@ export default class JungleManager extends cc.Component { public openActivity(): void { this.node.active = true; + this.registerWechatOnShow(); this.renderAllCards(); this.updateTimer(); } public closeActivity(): void { - if (this.isMoving || this.isPurchasing) { + if (this.isMoving || this.isPurchasing || this.isClaiming || this.isUnlockAnimating) { return; } - this.node.emit("jungle-close"); + this.stopFirstCardAttention(); + this.unregisterWechatOnShow(); this.node.active = false; + this.node.emit("jungle-close"); } /** 仅用于当前模拟配置的联调。 */ @@ -156,7 +260,9 @@ export default class JungleManager extends cc.Component { } private onCardClicked(buttonComponent: cc.Button): void { - if (this.isMoving || this.isPurchasing || !this.activityConfig) { + console.log("点击礼包档位按钮最外层", buttonComponent); + if (this.isMoving || this.isPurchasing || this.isClaiming + || this.isUnlockAnimating || !this.activityConfig) { return; } @@ -169,39 +275,404 @@ export default class JungleManager extends cc.Component { this.animator.playLockedFeedback(card); return; } + this.stopFirstCardAttention(); const tier = this.activityConfig.tiers[this.currentTierIndex]; - if (!tier || Date.now() >= this.activityConfig.endAt) { + if (!tier) { + return; + } + const isPurchaseBlocked = tier.price > 0 + && tier.state === 0 + && !!this.activityConfig.endAt + && Date.now() >= this.activityConfig.endAt; + if (isPurchaseBlocked) { return; } if (this.previewAnimationOnly) { this.completeCurrentTier(); - } else if (tier.price > 0) { + } else if (tier.state === 0) { this.requestPurchase(tier); } else { - this.completeCurrentTier(); + console.log("点击礼包档位按钮准备请求", buttonComponent); + this.requestClaim(tier); } } - private requestPurchase(tier: JungleTierConfig): void { - this.isPurchasing = true; - this.renderAllCards(); - - if (this.useMockPayment) { - this.scheduleOnce(() => this.completePurchase(true), 0.45); + private trackJungleAction(eventName: string, tier: JungleTierConfig): void { + let currentBlock = Number(tier && tier.id); + if (!isFinite(currentBlock) || currentBlock < 1 || currentBlock > 51) { + cc.warn("[JungleTreasure] 埋点格子编号无效:", currentBlock); return; } - - this.node.emit("jungle-purchase-request", { - tierId: tier.id, - price: tier.price, - productId: tier.productId || "", - rewards: tier.rewards, + cc.fx.GameTool.shushu_Track(eventName, { + current_block: currentBlock, + source: this.entrySource, }); } + private requestPurchase(tier: JungleTierConfig): void { + this.trackJungleAction("pay_jungle", tier); + if (!tier.productId) { + this.showToast("礼包商品配置错误"); + return; + } + this.isPurchasing = true; + this.pendingPaymentTierId = tier.id; + this.pendingPaymentProductId = tier.productId; + this.pendingPaymentPrice = Math.round(Number(tier.price) * 100); + this.renderAllCards(); + this.openLoad(); + + if (this.useMockPayment) { + this.scheduleOnce(() => { + this.closeLoad(); + this.completePurchase(true); + }, 0.45); + return; + } + + const systemType = this.getSystemType(); + if (systemType === "ios" && !cc.fx.GameConfig.GM_INFO.iosCanPay) { + this.startOldIosPayment(tier); + return; + } + this.startDirectPayment(tier, systemType); + } + + /** 状态 1 的礼包必须先由后端改成 2;成功后才发奖励并播放轨道动画。 */ + private requestClaim(tier: JungleTierConfig): void { + this.trackJungleAction("collect_jungle", tier); + console.log("点击礼包档位按钮内里面"); + this.isClaiming = true; + this.renderAllCards(); + this.openLoad(); + + let completed = false; + this.clearClaimTimeout(); + this.claimTimeout = setTimeout(() => { + if (completed) { + return; + } + completed = true; + this.claimTimeout = null; + this.finishClaimFailure("领取请求超时,请稍后重试"); + }, 8000); + console.log("点击礼包档位按钮开始请求"); + Utils.claimJungleTreasure(tier.id, (response) => { + console.log("点击礼包档位按钮请求结果", response); + if (completed || !cc.isValid(this.node)) { + return; + } + completed = true; + this.clearClaimTimeout(); + const data = response && response.code === 1 ? response.data : null; + const states = data && data.tierStates; + const claimedTierId = Number(data && data.claimedTierId); + if (!Array.isArray(states) + || claimedTierId !== tier.id + || Number(states[tier.id - 1]) !== 2) { + this.finishClaimFailure((response && (response.msg || response.message)) || "礼包领取失败"); + return; + } + + this.closeLoad(); + this.isClaiming = false; + tier.state = 2; + this.publishServerState(data); + // 后端已确认领取后,才执行客户端道具发放与礼包移动动画。 + this.completeCurrentTier(); + }); + } + + private finishClaimFailure(message: string): void { + this.closeLoad(); + this.isClaiming = false; + this.renderAllCards(); + this.showToast(message); + } + + private startDirectPayment(tier: JungleTierConfig, systemType: string): void { + let callbackReceived = false; + this.clearPaymentTimeout(); + this.paymentTimeout = setTimeout(() => { + if (callbackReceived) { + return; + } + callbackReceived = true; + this.paymentTimeout = null; + this.finishPaymentFailure("支付请求超时,请稍后重试"); + // 包含玩家在微信支付面板中的操作时间,不能按普通接口的 8 秒计算。 + }, 5 * 60 * 1000); + console.log("准备开始调用 购买礼包接口", this.pendingPaymentPrice); + Utils.buyProp( + tier.productId, + 1, + this.pendingPaymentPrice, + systemType, + tier.productId, + (result) => { + console.log("点击礼包档位按钮支付结果", result); + if (!cc.isValid(this.node)) { + console.log("11111点击礼包档位按钮支付结果回调已处理或节点已销毁"); + return; + } + callbackReceived = true; + this.clearPaymentTimeout(); + if (!result) { + this.finishPaymentFailure("支付拉起失败"); + console.log("22222点击礼包档位按钮支付结果回调已处理或节点已销毁"); + return; + } + if (result.err) { + if (Number(result.errCode) === 16) { + this.startOldIosPayment(tier); + console.log("33333点击礼包档位按钮支付结果回调已处理或节点已销毁"); + return; + } + this.finishPaymentFailure(Number(result.errCode) === -2 ? "已取消支付" : "支付拉起失败"); + console.log("44444点击礼包档位按钮支付结果回调已处理或节点已销毁"); + return; + } + console.log("55555点击礼包档位按钮支付结果", tier); + this.pollDirectPaymentResult(tier); + }, + ); + } + + private pollDirectPaymentResult(tier: JungleTierConfig): void { + console.log("拿到礼包档位按钮轮询支付结果之后调用档位是否解锁"); + Utils.getPayInfo((response) => { + console.log("点击礼包档位按钮轮询支付结果", response); + if (!cc.isValid(this.node)) { + return; + } + const payState = Number(response && response.data && response.data.pay_state); + console.log("payState点击礼包档位按钮轮询支付结果", payState); + if (response && response.code === 1 && payState === 2) { + console.log("准备改变礼包状态为可领取", response); + const name = "购买丛林宝藏礼包:" + tier.productId; + MiniGameSdk.API.yinli_Pay(this.pendingPaymentPrice, Utils.outTradeNo, name); + this.refreshAfterPayment(tier.id); + return; + } + this.finishPaymentFailure(payState === 1 ? "已取消支付" : "支付结果获取失败"); + }); + } + + /** iOS 旧支付:跳转客服,回到小游戏时由 wx.onShow 查询订单结果。 */ + private startOldIosPayment(tier: JungleTierConfig): void { + this.clearPaymentTimeout(); + const payInfo = { + price: this.pendingPaymentPrice, + payment_name: tier.productId, + payment_count: 1, + }; + Utils.GoKEFu(payInfo, (result) => { + if (!cc.isValid(this.node)) { + return; + } + if (result !== "success") { + this.finishPaymentFailure("客服支付入口打开失败"); + } + }); + } + + private registerWechatOnShow(): void { + if (this.onShowListener) { + return; + } + // @ts-ignore + if (typeof wx === "undefined" || !wx.onShow) { + return; + } + this.onShowListener = () => this.handleWechatOnShow(); + // @ts-ignore + wx.onShow(this.onShowListener); + } + + private unregisterWechatOnShow(): void { + // @ts-ignore + if (!this.onShowListener || typeof wx === "undefined" || !wx.offShow) { + return; + } + // @ts-ignore + wx.offShow(this.onShowListener); + this.onShowListener = null; + } + + private handleWechatOnShow(): void { + const gameInfo: any = cc.fx.GameConfig.GM_INFO; + const orderNo = gameInfo.iosJungleOrder; + if (!orderNo || this.checkingIosOrder) { + return; + } + this.checkingIosOrder = true; + this.isPurchasing = true; + this.openLoad(); + Utils.getIosPayInfo(orderNo, (response) => { + if (!cc.isValid(this.node)) { + return; + } + this.checkingIosOrder = false; + const data = response && response.data; + const productId = (data && data.payment_name) || this.pendingPaymentProductId; + const price = Number(data && data.goodsPrice) || this.pendingPaymentPrice; + if (response && response.code === 1) { + gameInfo.iosJungleOrder = ""; + const name = "购买丛林宝藏礼包:" + productId; + MiniGameSdk.API.yinli_Pay(price, orderNo, name); + this.refreshAfterPayment(this.resolvePendingTierId(productId)); + return; + } + if (response && response.code === 2) { + this.closeLoad(); + this.isPurchasing = false; + this.renderAllCards(); + this.showToast("支付结果确认超时,稍后返回活动会继续查询"); + return; + } + gameInfo.iosJungleOrder = ""; + this.finishPaymentFailure("支付未成功"); + }); + } + + private refreshAfterPayment(tierId: number): void { + let completed = false; + this.clearPaymentTimeout(); + this.paymentTimeout = setTimeout(() => { + if (completed) { + return; + } + completed = true; + this.paymentTimeout = null; + this.finishPaymentFailure("支付成功,礼包状态刷新超时,请重新进入活动"); + }, 8000); + + const requestUnlockedState = () => { + if (completed || !cc.isValid(this.node)) { + return; + } + Utils.getJungleTreasure((response) => { + if (completed || !cc.isValid(this.node)) { + return; + } + const data = response && response.code === 1 ? response.data : null; + const states = data && data.tierStates; + const refreshedState = Array.isArray(states) && tierId > 0 + ? Number(states[tierId - 1]) + : -1; + + // 微信支付回调会先把订单改为成功,再写玩家的活动解锁状态。 + // 订单成功后第一次读取可能仍为 0,因此在原有 8 秒超时内继续轮询。 + if (!data || !data.config || !Array.isArray(states) + || (tierId > 0 && refreshedState !== 1 && refreshedState !== 2)) { + this.scheduleOnce(requestUnlockedState, 0.4); + return; + } + + completed = true; + this.clearPaymentTimeout(); + this.isPurchasing = false; + this.publishServerState(data); + const refreshed = this.setServerData(data.config, states); + if (refreshed && refreshedState === 1) { + this.playTierUnlockAnimation(tierId); + } + if (!refreshed) { + this.closeLoad(); + this.renderAllCards(); + this.showToast("支付成功,礼包状态刷新失败,请重新进入活动"); + return; + } + // setServerData 已将付费档位从“3元”重新渲染为“领取”,完成后再关闭 Loading。 + this.closeLoad(); + this.showToast("支付成功,礼包已解锁"); + }); + }; + requestUnlockedState(); + } + + private finishPaymentFailure(message: string): void { + this.clearPaymentTimeout(); + this.closeLoad(); + this.isPurchasing = false; + this.checkingIosOrder = false; + this.renderAllCards(); + this.showToast(message); + } + + private resolvePendingTierId(productId: string): number { + if (this.pendingPaymentTierId > 0) { + return this.pendingPaymentTierId; + } + const match = String(productId || "").match(/^jungle_treasure_(\d+)$/); + if (!match) { + return 0; + } + const paidIndex = Number(match[1]); + return paidIndex === 1 ? 4 : 4 + (paidIndex - 1) * 6; + } + + private publishServerState(data: any): void { + this.node.emit("jungle-state-changed", data); + } + + private getSystemType(): string { + try { + // @ts-ignore + const info = typeof wx !== "undefined" && wx.getSystemInfoSync + // @ts-ignore + ? wx.getSystemInfoSync() + : null; + return info && info.platform === "ios" ? "ios" : "Android"; + } catch (error) { + cc.warn("[JungleTreasure] 获取系统类型失败", error); + return "Android"; + } + } + + private getSceneController(): any { + const canvas = cc.find("Canvas"); + return canvas && (canvas.getComponent("JiaZai") || canvas.getComponent("SceneManager")); + } + + private openLoad(): void { + const controller = this.getSceneController(); + if (controller && typeof controller.openLoad === "function") { + controller.openLoad(); + } + } + + private closeLoad(): void { + const controller = this.getSceneController(); + if (controller && typeof controller.closeLoad === "function") { + controller.closeLoad(); + } + } + + private showToast(message: string): void { + if (MiniGameSdk && MiniGameSdk.API && MiniGameSdk.API.showToast) { + MiniGameSdk.API.showToast(message); + } + } + + private clearClaimTimeout(): void { + if (this.claimTimeout) { + clearTimeout(this.claimTimeout); + this.claimTimeout = null; + } + } + + private clearPaymentTimeout(): void { + if (this.paymentTimeout) { + clearTimeout(this.paymentTimeout); + this.paymentTimeout = null; + } + } + private completeCurrentTier(): void { + console.log("点击礼包档位按钮完成当前档位"); const tier = this.activityConfig.tiers[this.currentTierIndex]; if (!tier || this.isMoving) { return; @@ -268,6 +739,12 @@ export default class JungleManager extends cc.Component { this.renderAllCards(); if (this.currentTierIndex >= this.activityConfig.tiers.length) { this.node.emit("jungle-complete"); + return; + } + + let currentTier = this.activityConfig.tiers[this.currentTierIndex]; + if (currentTier && currentTier.state !== 0) { + this.playTierUnlockAnimation(currentTier.id); } } @@ -277,11 +754,63 @@ export default class JungleManager extends cc.Component { } } + /** 无论当前礼包是否解锁,每两秒让最前方礼包整体 duang 一次。 */ + private playFirstCardAttention(): void { + if (!this.node.activeInHierarchy || !this.activityConfig || !this.animator + || this.isAttentionAnimating || this.isMoving || this.isPurchasing + || this.isClaiming || this.isUnlockAnimating) { + return; + } + let firstCard = this.cards[0]; + if (!firstCard || !firstCard.activeInHierarchy) { + return; + } + + this.isAttentionAnimating = true; + this.animator.playCardAttention(firstCard, () => { + if (cc.isValid(this.node)) { + this.isAttentionAnimating = false; + } + }); + } + + private stopFirstCardAttention(): void { + if (!this.isAttentionAnimating || !this.animator) { + return; + } + this.animator.stopCardAttention(this.cards[0]); + this.isAttentionAnimating = false; + } + + private playTierUnlockAnimation(tierId: number): void { + let targetCard: cc.Node = null; + for (let index = 0; index < this.cards.length; index++) { + let card = this.cards[index]; + if (card && card.active && card.name === "reward_card_" + tierId) { + targetCard = card; + break; + } + } + if (!targetCard || !this.animator) { + return; + } + + this.isUnlockAnimating = true; + this.animator.playUnlockEffect(targetCard, () => { + if (!cc.isValid(this.node)) { + return; + } + this.isUnlockAnimating = false; + this.renderAllCards(); + }); + } + private updateTimer(): void { if (!this.view || !this.activityConfig) { return; } - if (this.view.updateTimer(this.activityConfig) === 0) { + if (this.view.updateTimer(this.activityConfig) === 0 && !this.hasExpired) { + this.hasExpired = true; this.renderAllCards(); this.node.emit("jungle-expired"); } @@ -291,7 +820,7 @@ export default class JungleManager extends cc.Component { return { currentTierIndex: this.currentTierIndex, isMoving: this.isMoving, - isPurchasing: this.isPurchasing, + isPurchasing: this.isPurchasing || this.isClaiming || this.isUnlockAnimating, }; } @@ -301,6 +830,11 @@ export default class JungleManager extends cc.Component { trackFrame: this.trackFrame, titleFrame: this.titleFrame, buttonFrame: this.buttonFrame, + freeFrame: this.freeFrame, + claimFrame: this.claimFrame, + yuanFrame: this.yuanFrame, + priceFont: this.priceFont, + countFont: this.countFont, lockFrame: this.lockFrame, timerFrame: this.timerFrame, closeFrame: this.closeFrame, diff --git a/assets/shop/effect.meta b/assets/shop/effect.meta new file mode 100644 index 00000000..20f9a409 --- /dev/null +++ b/assets/shop/effect.meta @@ -0,0 +1,13 @@ +{ + "ver": "1.1.3", + "uuid": "f3e1aa52-2dd0-4efc-9baa-7d6f07cc3c12", + "importer": "folder", + "isBundle": false, + "bundleName": "", + "priority": 1, + "compressionType": {}, + "optimizeHotUpdate": {}, + "inlineSpriteFrames": {}, + "isRemoteBundle": {}, + "subMetas": {} +} \ No newline at end of file diff --git a/assets/shop/effect/jungle_shine.effect b/assets/shop/effect/jungle_shine.effect new file mode 100644 index 00000000..75bb040b --- /dev/null +++ b/assets/shop/effect/jungle_shine.effect @@ -0,0 +1,91 @@ +CCEffect %{ + techniques: + - passes: + - vert: shop-jungle-shine-vs + frag: shop-jungle-shine-fs + blendState: + targets: + - blend: true + blendSrc: src_alpha + blendDst: one_minus_src_alpha + rasterizerState: + cullMode: none + depthStencilState: + depthTest: false + depthWrite: false + properties: + texture: { value: white } + shinePosition: { value: -0.3 } + shineWidth: { value: 0.075 } + shineStrength: { value: 1.8 } + shineAngle: { value: 0.2 } + shineColor: { value: [1.0, 0.95, 0.68, 1.0] } +}% + +CCProgram shop-jungle-shine-vs %{ + precision highp float; + #include + #include + + in vec3 a_position; + in vec4 a_color; + in vec2 a_uv0; + out vec4 v_color; + out vec2 v_uv0; + + void main () { + vec4 pos = vec4(a_position, 1.0); + #if CC_USE_MODEL + pos = cc_matViewProj * cc_matWorld * pos; + #else + pos = cc_matViewProj * pos; + #endif + gl_Position = pos; + v_color = a_color; + v_uv0 = a_uv0; + } +}% + +CCProgram shop-jungle-shine-fs %{ + precision highp float; + #include + #include + + in vec4 v_color; + in vec2 v_uv0; + uniform sampler2D texture; + uniform ShineProperties { + vec4 shineColor; + float shinePosition; + float shineWidth; + float shineStrength; + float shineAngle; + }; + + void main () { + vec4 color = vec4(1.0); + CCTexture(texture, v_uv0, color); + color *= v_color; + + float sweepAxis = v_uv0.x + (v_uv0.y - 0.5) * shineAngle; + float bandIn = smoothstep( + shinePosition - shineWidth, + shinePosition, + sweepAxis + ); + float bandOut = 1.0 - smoothstep( + shinePosition, + shinePosition + shineWidth, + sweepAxis + ); + float shine = pow(clamp(bandIn * bandOut, 0.0, 1.0), 1.35); + color.rgb += shineColor.rgb * shine * shineStrength * color.a; + + ALPHA_TEST(color); + #if USE_BGRA + gl_FragColor = color.bgra; + #else + gl_FragColor = color.rgba; + #endif + } +}% diff --git a/assets/shop/effect/jungle_shine.effect.meta b/assets/shop/effect/jungle_shine.effect.meta new file mode 100644 index 00000000..71666a9b --- /dev/null +++ b/assets/shop/effect/jungle_shine.effect.meta @@ -0,0 +1,18 @@ +{ + "ver": "1.0.27", + "uuid": "2874d4a3-5f10-4d7c-bcaf-40b9d6e6cd30", + "importer": "effect", + "compiledShaders": [ + { + "glsl1": { + "vert": "\nprecision highp float;\nuniform mat4 cc_matViewProj;\nuniform mat4 cc_matWorld;\nattribute vec3 a_position;\nattribute vec4 a_color;\nattribute vec2 a_uv0;\nvarying vec4 v_color;\nvarying vec2 v_uv0;\nvoid main () {\n vec4 pos = vec4(a_position, 1.0);\n #if CC_USE_MODEL\n pos = cc_matViewProj * cc_matWorld * pos;\n #else\n pos = cc_matViewProj * pos;\n #endif\n gl_Position = pos;\n v_color = a_color;\n v_uv0 = a_uv0;\n}", + "frag": "\nprecision highp float;\n#if USE_ALPHA_TEST\n uniform float alphaThreshold;\n#endif\nvoid ALPHA_TEST (in vec4 color) {\n #if USE_ALPHA_TEST\n if (color.a < alphaThreshold) discard;\n #endif\n}\nvoid ALPHA_TEST (in float alpha) {\n #if USE_ALPHA_TEST\n if (alpha < alphaThreshold) discard;\n #endif\n}\nvarying vec4 v_color;\nvarying vec2 v_uv0;\nuniform sampler2D texture;\nuniform vec4 shineColor;\nuniform float shinePosition;\nuniform float shineWidth;\nuniform float shineStrength;\nuniform float shineAngle;\nvoid main () {\n vec4 color = vec4(1.0);\n vec4 texture_tmp = texture2D(texture, v_uv0);\n #if CC_USE_ALPHA_ATLAS_texture\n texture_tmp.a *= texture2D(texture, v_uv0 + vec2(0, 0.5)).r;\n #endif\n #if INPUT_IS_GAMMA\n color.rgb *= (texture_tmp.rgb * texture_tmp.rgb);\n color.a *= texture_tmp.a;\n #else\n color *= texture_tmp;\n #endif\n color *= v_color;\n float sweepAxis = v_uv0.x + (v_uv0.y - 0.5) * shineAngle;\n float bandIn = smoothstep(\n shinePosition - shineWidth,\n shinePosition,\n sweepAxis\n );\n float bandOut = 1.0 - smoothstep(\n shinePosition,\n shinePosition + shineWidth,\n sweepAxis\n );\n float shine = pow(clamp(bandIn * bandOut, 0.0, 1.0), 1.35);\n color.rgb += shineColor.rgb * shine * shineStrength * color.a;\n ALPHA_TEST(color);\n #if USE_BGRA\n gl_FragColor = color.bgra;\n #else\n gl_FragColor = color.rgba;\n #endif\n}" + }, + "glsl3": { + "vert": "\nprecision highp float;\nuniform CCGlobal {\n mat4 cc_matView;\n mat4 cc_matViewInv;\n mat4 cc_matProj;\n mat4 cc_matProjInv;\n mat4 cc_matViewProj;\n mat4 cc_matViewProjInv;\n vec4 cc_cameraPos;\n vec4 cc_time;\n mediump vec4 cc_screenSize;\n mediump vec4 cc_screenScale;\n};\nuniform CCLocal {\n mat4 cc_matWorld;\n mat4 cc_matWorldIT;\n};\nin vec3 a_position;\nin vec4 a_color;\nin vec2 a_uv0;\nout vec4 v_color;\nout vec2 v_uv0;\nvoid main () {\n vec4 pos = vec4(a_position, 1.0);\n #if CC_USE_MODEL\n pos = cc_matViewProj * cc_matWorld * pos;\n #else\n pos = cc_matViewProj * pos;\n #endif\n gl_Position = pos;\n v_color = a_color;\n v_uv0 = a_uv0;\n}", + "frag": "\nprecision highp float;\n#if USE_ALPHA_TEST\n uniform ALPHA_TEST {\n float alphaThreshold;\n };\n#endif\nvoid ALPHA_TEST (in vec4 color) {\n #if USE_ALPHA_TEST\n if (color.a < alphaThreshold) discard;\n #endif\n}\nvoid ALPHA_TEST (in float alpha) {\n #if USE_ALPHA_TEST\n if (alpha < alphaThreshold) discard;\n #endif\n}\nin vec4 v_color;\nin vec2 v_uv0;\nuniform sampler2D texture;\nuniform ShineProperties {\n vec4 shineColor;\n float shinePosition;\n float shineWidth;\n float shineStrength;\n float shineAngle;\n};\nvoid main () {\n vec4 color = vec4(1.0);\n vec4 texture_tmp = texture(texture, v_uv0);\n #if CC_USE_ALPHA_ATLAS_texture\n texture_tmp.a *= texture(texture, v_uv0 + vec2(0, 0.5)).r;\n #endif\n #if INPUT_IS_GAMMA\n color.rgb *= (texture_tmp.rgb * texture_tmp.rgb);\n color.a *= texture_tmp.a;\n #else\n color *= texture_tmp;\n #endif\n color *= v_color;\n float sweepAxis = v_uv0.x + (v_uv0.y - 0.5) * shineAngle;\n float bandIn = smoothstep(\n shinePosition - shineWidth,\n shinePosition,\n sweepAxis\n );\n float bandOut = 1.0 - smoothstep(\n shinePosition,\n shinePosition + shineWidth,\n sweepAxis\n );\n float shine = pow(clamp(bandIn * bandOut, 0.0, 1.0), 1.35);\n color.rgb += shineColor.rgb * shine * shineStrength * color.a;\n ALPHA_TEST(color);\n #if USE_BGRA\n gl_FragColor = color.bgra;\n #else\n gl_FragColor = color.rgba;\n #endif\n}" + } + } + ], + "subMetas": {} +} \ No newline at end of file diff --git a/assets/shop/img/chaozhi.png b/assets/shop/img/chaozhi.png new file mode 100644 index 00000000..16cd0064 Binary files /dev/null and b/assets/shop/img/chaozhi.png differ diff --git a/assets/shop/img/chaozhi.png.meta b/assets/shop/img/chaozhi.png.meta new file mode 100644 index 00000000..c0b0d8c1 --- /dev/null +++ b/assets/shop/img/chaozhi.png.meta @@ -0,0 +1,38 @@ +{ + "ver": "2.3.7", + "uuid": "d93b4f3c-ecfa-480a-92ed-3804685a6260", + "importer": "texture", + "type": "sprite", + "wrapMode": "clamp", + "filterMode": "bilinear", + "premultiplyAlpha": false, + "genMipmaps": false, + "packable": true, + "width": 182, + "height": 113, + "platformSettings": {}, + "subMetas": { + "chaozhi": { + "ver": "1.0.6", + "uuid": "5cfc4f5b-2d0c-45db-ae4e-e1a59a86f78e", + "importer": "sprite-frame", + "rawTextureUuid": "d93b4f3c-ecfa-480a-92ed-3804685a6260", + "trimType": "auto", + "trimThreshold": 1, + "rotated": false, + "offsetX": 0, + "offsetY": 0, + "trimX": 0, + "trimY": 0, + "width": 182, + "height": 113, + "rawWidth": 182, + "rawHeight": 113, + "borderTop": 0, + "borderBottom": 0, + "borderLeft": 0, + "borderRight": 0, + "subMetas": {} + } + } +} \ No newline at end of file diff --git a/assets/shop/img/haisheng.png b/assets/shop/img/haisheng.png new file mode 100644 index 00000000..b99d7996 Binary files /dev/null and b/assets/shop/img/haisheng.png differ diff --git a/assets/shop/img/haisheng.png.meta b/assets/shop/img/haisheng.png.meta new file mode 100644 index 00000000..3d27d70d --- /dev/null +++ b/assets/shop/img/haisheng.png.meta @@ -0,0 +1,38 @@ +{ + "ver": "2.3.7", + "uuid": "eadd1659-4c91-4145-8e84-fd0575efd95e", + "importer": "texture", + "type": "sprite", + "wrapMode": "clamp", + "filterMode": "bilinear", + "premultiplyAlpha": false, + "genMipmaps": false, + "packable": true, + "width": 89, + "height": 42, + "platformSettings": {}, + "subMetas": { + "haisheng": { + "ver": "1.0.6", + "uuid": "e8963ffc-77b8-4445-8840-caeb56cc0e41", + "importer": "sprite-frame", + "rawTextureUuid": "eadd1659-4c91-4145-8e84-fd0575efd95e", + "trimType": "auto", + "trimThreshold": 1, + "rotated": false, + "offsetX": 0, + "offsetY": 0, + "trimX": 0, + "trimY": 0, + "width": 89, + "height": 42, + "rawWidth": 89, + "rawHeight": 42, + "borderTop": 0, + "borderBottom": 0, + "borderLeft": 0, + "borderRight": 0, + "subMetas": {} + } + } +} \ No newline at end of file diff --git a/assets/shop/img/jungleBg.png b/assets/shop/img/jungleBg.png new file mode 100644 index 00000000..298ae354 Binary files /dev/null and b/assets/shop/img/jungleBg.png differ diff --git a/assets/shop/img/jungleBg.png.meta b/assets/shop/img/jungleBg.png.meta new file mode 100644 index 00000000..2613bf22 --- /dev/null +++ b/assets/shop/img/jungleBg.png.meta @@ -0,0 +1,38 @@ +{ + "ver": "2.3.7", + "uuid": "e4c11bc1-0a1e-40ce-909c-90977710fe12", + "importer": "texture", + "type": "sprite", + "wrapMode": "clamp", + "filterMode": "bilinear", + "premultiplyAlpha": false, + "genMipmaps": false, + "packable": true, + "width": 963, + "height": 374, + "platformSettings": {}, + "subMetas": { + "jungleBg": { + "ver": "1.0.6", + "uuid": "c3088e91-abff-46df-85b1-4cdc6b156602", + "importer": "sprite-frame", + "rawTextureUuid": "e4c11bc1-0a1e-40ce-909c-90977710fe12", + "trimType": "auto", + "trimThreshold": 1, + "rotated": false, + "offsetX": 0.5, + "offsetY": -2, + "trimX": 1, + "trimY": 4, + "width": 962, + "height": 370, + "rawWidth": 963, + "rawHeight": 374, + "borderTop": 0, + "borderBottom": 0, + "borderLeft": 0, + "borderRight": 0, + "subMetas": {} + } + } +} \ No newline at end of file diff --git a/assets/shop/img/lingqu.png b/assets/shop/img/lingqu.png new file mode 100644 index 00000000..56f948b3 Binary files /dev/null and b/assets/shop/img/lingqu.png differ diff --git a/assets/shop/img/lingqu.png.meta b/assets/shop/img/lingqu.png.meta new file mode 100644 index 00000000..a42fe422 --- /dev/null +++ b/assets/shop/img/lingqu.png.meta @@ -0,0 +1,38 @@ +{ + "ver": "2.3.7", + "uuid": "25a00495-be3f-4c02-8447-2c4769460e01", + "importer": "texture", + "type": "sprite", + "wrapMode": "clamp", + "filterMode": "bilinear", + "premultiplyAlpha": false, + "genMipmaps": false, + "packable": true, + "width": 315, + "height": 110, + "platformSettings": {}, + "subMetas": { + "lingqu": { + "ver": "1.0.6", + "uuid": "efd7bbd4-0a20-42e3-9019-4037ce7e9a9d", + "importer": "sprite-frame", + "rawTextureUuid": "25a00495-be3f-4c02-8447-2c4769460e01", + "trimType": "auto", + "trimThreshold": 1, + "rotated": false, + "offsetX": 0, + "offsetY": 0, + "trimX": 0, + "trimY": 0, + "width": 315, + "height": 110, + "rawWidth": 315, + "rawHeight": 110, + "borderTop": 0, + "borderBottom": 0, + "borderLeft": 0, + "borderRight": 0, + "subMetas": {} + } + } +} \ No newline at end of file diff --git a/assets/shop/img/lqtian.png b/assets/shop/img/lqtian.png new file mode 100644 index 00000000..c75cf597 Binary files /dev/null and b/assets/shop/img/lqtian.png differ diff --git a/assets/shop/img/lqtian.png.meta b/assets/shop/img/lqtian.png.meta new file mode 100644 index 00000000..a56c8fc6 --- /dev/null +++ b/assets/shop/img/lqtian.png.meta @@ -0,0 +1,38 @@ +{ + "ver": "2.3.7", + "uuid": "c0d066f5-57dc-437e-8b84-dd9948ddcf1e", + "importer": "texture", + "type": "sprite", + "wrapMode": "clamp", + "filterMode": "bilinear", + "premultiplyAlpha": false, + "genMipmaps": false, + "packable": true, + "width": 44, + "height": 40, + "platformSettings": {}, + "subMetas": { + "lqtian": { + "ver": "1.0.6", + "uuid": "380f6275-fdcf-4a8d-8f82-f14e474c40e0", + "importer": "sprite-frame", + "rawTextureUuid": "c0d066f5-57dc-437e-8b84-dd9948ddcf1e", + "trimType": "auto", + "trimThreshold": 1, + "rotated": false, + "offsetX": 0, + "offsetY": 0, + "trimX": 0, + "trimY": 0, + "width": 44, + "height": 40, + "rawWidth": 44, + "rawHeight": 40, + "borderTop": 0, + "borderBottom": 0, + "borderLeft": 0, + "borderRight": 0, + "subMetas": {} + } + } +} \ No newline at end of file diff --git a/assets/shop/img/xianshi.png b/assets/shop/img/xianshi.png new file mode 100644 index 00000000..f29a1e62 Binary files /dev/null and b/assets/shop/img/xianshi.png differ diff --git a/assets/shop/img/xianshi.png.meta b/assets/shop/img/xianshi.png.meta new file mode 100644 index 00000000..b916d331 --- /dev/null +++ b/assets/shop/img/xianshi.png.meta @@ -0,0 +1,38 @@ +{ + "ver": "2.3.7", + "uuid": "6ac9489b-fc6f-48dd-90e3-1a644aa607e7", + "importer": "texture", + "type": "sprite", + "wrapMode": "clamp", + "filterMode": "bilinear", + "premultiplyAlpha": false, + "genMipmaps": false, + "packable": true, + "width": 110, + "height": 108, + "platformSettings": {}, + "subMetas": { + "xianshi": { + "ver": "1.0.6", + "uuid": "83321555-9e19-4355-bf23-15c122d5b758", + "importer": "sprite-frame", + "rawTextureUuid": "6ac9489b-fc6f-48dd-90e3-1a644aa607e7", + "trimType": "auto", + "trimThreshold": 1, + "rotated": false, + "offsetX": 0, + "offsetY": 0, + "trimX": 0, + "trimY": 0, + "width": 110, + "height": 108, + "rawWidth": 110, + "rawHeight": 108, + "borderTop": 0, + "borderBottom": 0, + "borderLeft": 0, + "borderRight": 0, + "subMetas": {} + } + } +} \ No newline at end of file diff --git a/assets/shop/img/yueka.plist b/assets/shop/img/yueka.plist deleted file mode 100644 index 011951e3..00000000 --- a/assets/shop/img/yueka.plist +++ /dev/null @@ -1,296 +0,0 @@ - - - - - frames - - 30yuan.png - - aliases - - spriteOffset - {0,0} - spriteSize - {516,164} - spriteSourceSize - {516,164} - textureRect - {{1328,0},{516,164}} - textureRotated - - - cardm.png - - aliases - - spriteOffset - {0,0} - spriteSize - {123,90} - spriteSourceSize - {123,90} - textureRect - {{1205,0},{123,90}} - textureRotated - - - faguang.png - - aliases - - spriteOffset - {0,0} - spriteSize - {506,506} - spriteSourceSize - {506,506} - textureRect - {{1496,490},{506,506}} - textureRotated - - - fangkuai.png - - aliases - - spriteOffset - {0,0} - spriteSize - {794,210} - spriteSourceSize - {794,210} - textureRect - {{635,164},{794,210}} - textureRotated - - - guanbi.png - - aliases - - spriteOffset - {0,0} - spriteSize - {58,58} - spriteSourceSize - {58,58} - textureRect - {{1147,0},{58,58}} - textureRotated - - - haisheng.png - - aliases - - spriteOffset - {0,0} - spriteSize - {89,42} - spriteSourceSize - {89,42} - textureRect - {{772,0},{89,42}} - textureRotated - - - jiantou.png - - aliases - - spriteOffset - {0,0} - spriteSize - {248,326} - spriteSourceSize - {248,326} - textureRect - {{1749,164},{248,326}} - textureRotated - - - jinbi.png - - aliases - - spriteOffset - {0,0} - spriteSize - {320,320} - spriteSourceSize - {320,320} - textureRect - {{1429,164},{320,320}} - textureRotated - - - lq.png - - aliases - - spriteOffset - {0,0} - spriteSize - {635,173} - spriteSourceSize - {635,173} - textureRect - {{0,164},{635,173}} - textureRotated - - - lqtian.png - - aliases - - spriteOffset - {0,0} - spriteSize - {44,40} - spriteSourceSize - {44,40} - textureRect - {{684,0},{44,40}} - textureRotated - - - shengxiaozhong.png - - aliases - - spriteOffset - {0,0} - spriteSize - {286,42} - spriteSourceSize - {286,42} - textureRect - {{861,0},{286,42}} - textureRotated - - - shengxiaozit.png - - aliases - - spriteOffset - {0,0} - spriteSize - {215,32} - spriteSourceSize - {215,32} - textureRect - {{34,0},{215,32}} - textureRotated - - - shopbaner.png - - aliases - - spriteOffset - {0,0} - spriteSize - {1000,406} - spriteSourceSize - {1000,406} - textureRect - {{496,490},{1000,406}} - textureRotated - - - star1.png - - aliases - - spriteOffset - {0,0} - spriteSize - {248,326} - spriteSourceSize - {248,326} - textureRect - {{0,490},{248,326}} - textureRotated - - - star2.png - - aliases - - spriteOffset - {0,0} - spriteSize - {248,326} - spriteSourceSize - {248,326} - textureRect - {{248,490},{248,326}} - textureRotated - - - tian.png - - aliases - - spriteOffset - {0,0} - spriteSize - {44,40} - spriteSourceSize - {44,40} - textureRect - {{728,0},{44,40}} - textureRotated - - - tt.png - - aliases - - spriteOffset - {0,0} - spriteSize - {34,30} - spriteSourceSize - {34,30} - textureRect - {{0,0},{34,30}} - textureRotated - - - yuekazi.png - - aliases - - spriteOffset - {0,0} - spriteSize - {435,38} - spriteSourceSize - {435,38} - textureRect - {{249,0},{435,38}} - textureRotated - - - - metadata - - format - 3 - pixelFormat - RGBA8888 - premultiplyAlpha - - realTextureFileName - yueka.png - size - {2002,996} - smartupdate - $TexturePacker:SmartUpdate:31468ea6569fed60cde5a27ae2edf1f2:95c923329506deb632b0e819bc5756f8:f9d521de5bc0db19166bcf92821562f3$ - textureFileName - yueka.png - - - diff --git a/assets/shop/img/yueka.plist.meta b/assets/shop/img/yueka.plist.meta deleted file mode 100644 index 3904376b..00000000 --- a/assets/shop/img/yueka.plist.meta +++ /dev/null @@ -1,427 +0,0 @@ -{ - "ver": "1.2.6", - "uuid": "c60beb21-ffe5-48e6-bc66-f4ba20a401cc", - "importer": "asset", - "rawTextureUuid": "b72f8f63-5869-4f82-83a6-70c719b5e4ac", - "size": { - "width": 2002, - "height": 996 - }, - "type": "Texture Packer", - "subMetas": { - "30yuan.png": { - "ver": "1.0.6", - "uuid": "8add4fef-94e5-431a-be6c-2694ddf8a827", - "importer": "sprite-frame", - "rawTextureUuid": "b72f8f63-5869-4f82-83a6-70c719b5e4ac", - "trimType": "auto", - "trimThreshold": 1, - "rotated": false, - "offsetX": 0, - "offsetY": 0, - "trimX": 1328, - "trimY": 0, - "width": 516, - "height": 164, - "rawWidth": 516, - "rawHeight": 164, - "borderTop": 0, - "borderBottom": 0, - "borderLeft": 0, - "borderRight": 0, - "spriteType": "normal", - "subMetas": {} - }, - "cardm.png": { - "ver": "1.0.6", - "uuid": "a4d75412-5080-4584-ace0-87a1ed8b89af", - "importer": "sprite-frame", - "rawTextureUuid": "b72f8f63-5869-4f82-83a6-70c719b5e4ac", - "trimType": "auto", - "trimThreshold": 1, - "rotated": false, - "offsetX": 0, - "offsetY": 0, - "trimX": 1205, - "trimY": 0, - "width": 123, - "height": 90, - "rawWidth": 123, - "rawHeight": 90, - "borderTop": 0, - "borderBottom": 0, - "borderLeft": 0, - "borderRight": 0, - "spriteType": "normal", - "subMetas": {} - }, - "faguang.png": { - "ver": "1.0.6", - "uuid": "7e8f0c9b-9775-4dee-80c5-e42816ec45aa", - "importer": "sprite-frame", - "rawTextureUuid": "b72f8f63-5869-4f82-83a6-70c719b5e4ac", - "trimType": "auto", - "trimThreshold": 1, - "rotated": false, - "offsetX": 0, - "offsetY": 0, - "trimX": 1496, - "trimY": 490, - "width": 506, - "height": 506, - "rawWidth": 506, - "rawHeight": 506, - "borderTop": 0, - "borderBottom": 0, - "borderLeft": 0, - "borderRight": 0, - "spriteType": "normal", - "subMetas": {} - }, - "fangkuai.png": { - "ver": "1.0.6", - "uuid": "d79a5bee-5022-4643-a9d4-e3e8a0ed1957", - "importer": "sprite-frame", - "rawTextureUuid": "b72f8f63-5869-4f82-83a6-70c719b5e4ac", - "trimType": "auto", - "trimThreshold": 1, - "rotated": false, - "offsetX": 0, - "offsetY": 0, - "trimX": 635, - "trimY": 164, - "width": 794, - "height": 210, - "rawWidth": 794, - "rawHeight": 210, - "borderTop": 0, - "borderBottom": 0, - "borderLeft": 0, - "borderRight": 0, - "spriteType": "normal", - "subMetas": {} - }, - "guanbi.png": { - "ver": "1.0.6", - "uuid": "b7ae1806-b45f-4aa8-b7cc-c97fa29a85af", - "importer": "sprite-frame", - "rawTextureUuid": "b72f8f63-5869-4f82-83a6-70c719b5e4ac", - "trimType": "auto", - "trimThreshold": 1, - "rotated": false, - "offsetX": 0, - "offsetY": 0, - "trimX": 1147, - "trimY": 0, - "width": 58, - "height": 58, - "rawWidth": 58, - "rawHeight": 58, - "borderTop": 0, - "borderBottom": 0, - "borderLeft": 0, - "borderRight": 0, - "spriteType": "normal", - "subMetas": {} - }, - "haisheng.png": { - "ver": "1.0.6", - "uuid": "c4e289ba-3afd-4838-bf0c-571cadce8db2", - "importer": "sprite-frame", - "rawTextureUuid": "b72f8f63-5869-4f82-83a6-70c719b5e4ac", - "trimType": "auto", - "trimThreshold": 1, - "rotated": false, - "offsetX": 0, - "offsetY": 0, - "trimX": 772, - "trimY": 0, - "width": 89, - "height": 42, - "rawWidth": 89, - "rawHeight": 42, - "borderTop": 0, - "borderBottom": 0, - "borderLeft": 0, - "borderRight": 0, - "spriteType": "normal", - "subMetas": {} - }, - "jiantou.png": { - "ver": "1.0.6", - "uuid": "506a1673-0424-419b-804a-634729897214", - "importer": "sprite-frame", - "rawTextureUuid": "b72f8f63-5869-4f82-83a6-70c719b5e4ac", - "trimType": "auto", - "trimThreshold": 1, - "rotated": false, - "offsetX": 0, - "offsetY": 0, - "trimX": 1749, - "trimY": 164, - "width": 248, - "height": 326, - "rawWidth": 248, - "rawHeight": 326, - "borderTop": 0, - "borderBottom": 0, - "borderLeft": 0, - "borderRight": 0, - "spriteType": "normal", - "subMetas": {} - }, - "jinbi.png": { - "ver": "1.0.6", - "uuid": "f0ad30c5-b441-4e29-bd57-b74a4ca072a3", - "importer": "sprite-frame", - "rawTextureUuid": "b72f8f63-5869-4f82-83a6-70c719b5e4ac", - "trimType": "auto", - "trimThreshold": 1, - "rotated": false, - "offsetX": 0, - "offsetY": 0, - "trimX": 1429, - "trimY": 164, - "width": 320, - "height": 320, - "rawWidth": 320, - "rawHeight": 320, - "borderTop": 0, - "borderBottom": 0, - "borderLeft": 0, - "borderRight": 0, - "spriteType": "normal", - "subMetas": {} - }, - "lq.png": { - "ver": "1.0.6", - "uuid": "8419f8d6-48b4-4ecd-8228-34d620ae0da9", - "importer": "sprite-frame", - "rawTextureUuid": "b72f8f63-5869-4f82-83a6-70c719b5e4ac", - "trimType": "auto", - "trimThreshold": 1, - "rotated": false, - "offsetX": 0, - "offsetY": 0, - "trimX": 0, - "trimY": 164, - "width": 635, - "height": 173, - "rawWidth": 635, - "rawHeight": 173, - "borderTop": 0, - "borderBottom": 0, - "borderLeft": 0, - "borderRight": 0, - "spriteType": "normal", - "subMetas": {} - }, - "lqtian.png": { - "ver": "1.0.6", - "uuid": "2b6c09cd-62b2-4b90-8f3f-498e1a7b4c3f", - "importer": "sprite-frame", - "rawTextureUuid": "b72f8f63-5869-4f82-83a6-70c719b5e4ac", - "trimType": "auto", - "trimThreshold": 1, - "rotated": false, - "offsetX": 0, - "offsetY": 0, - "trimX": 684, - "trimY": 0, - "width": 44, - "height": 40, - "rawWidth": 44, - "rawHeight": 40, - "borderTop": 0, - "borderBottom": 0, - "borderLeft": 0, - "borderRight": 0, - "spriteType": "normal", - "subMetas": {} - }, - "shengxiaozhong.png": { - "ver": "1.0.6", - "uuid": "3fa8a798-dfb7-403a-93a7-a57a80e07245", - "importer": "sprite-frame", - "rawTextureUuid": "b72f8f63-5869-4f82-83a6-70c719b5e4ac", - "trimType": "auto", - "trimThreshold": 1, - "rotated": false, - "offsetX": 0, - "offsetY": 0, - "trimX": 861, - "trimY": 0, - "width": 286, - "height": 42, - "rawWidth": 286, - "rawHeight": 42, - "borderTop": 0, - "borderBottom": 0, - "borderLeft": 0, - "borderRight": 0, - "spriteType": "normal", - "subMetas": {} - }, - "shengxiaozit.png": { - "ver": "1.0.6", - "uuid": "4f782e47-7a24-4483-86f5-3c5aca38389e", - "importer": "sprite-frame", - "rawTextureUuid": "b72f8f63-5869-4f82-83a6-70c719b5e4ac", - "trimType": "auto", - "trimThreshold": 1, - "rotated": false, - "offsetX": 0, - "offsetY": 0, - "trimX": 34, - "trimY": 0, - "width": 215, - "height": 32, - "rawWidth": 215, - "rawHeight": 32, - "borderTop": 0, - "borderBottom": 0, - "borderLeft": 0, - "borderRight": 0, - "spriteType": "normal", - "subMetas": {} - }, - "shopbaner.png": { - "ver": "1.0.6", - "uuid": "39dbd1cd-c67e-4ff4-b868-dd95054e3fa6", - "importer": "sprite-frame", - "rawTextureUuid": "b72f8f63-5869-4f82-83a6-70c719b5e4ac", - "trimType": "auto", - "trimThreshold": 1, - "rotated": false, - "offsetX": 0, - "offsetY": 0, - "trimX": 496, - "trimY": 490, - "width": 1000, - "height": 406, - "rawWidth": 1000, - "rawHeight": 406, - "borderTop": 0, - "borderBottom": 0, - "borderLeft": 0, - "borderRight": 0, - "spriteType": "normal", - "subMetas": {} - }, - "star1.png": { - "ver": "1.0.6", - "uuid": "caa38b03-cb18-4bad-b47b-0b329f7d3e7a", - "importer": "sprite-frame", - "rawTextureUuid": "b72f8f63-5869-4f82-83a6-70c719b5e4ac", - "trimType": "auto", - "trimThreshold": 1, - "rotated": false, - "offsetX": 0, - "offsetY": 0, - "trimX": 0, - "trimY": 490, - "width": 248, - "height": 326, - "rawWidth": 248, - "rawHeight": 326, - "borderTop": 0, - "borderBottom": 0, - "borderLeft": 0, - "borderRight": 0, - "spriteType": "normal", - "subMetas": {} - }, - "star2.png": { - "ver": "1.0.6", - "uuid": "a914aa35-d735-4678-b506-ff3ab3c7a8e0", - "importer": "sprite-frame", - "rawTextureUuid": "b72f8f63-5869-4f82-83a6-70c719b5e4ac", - "trimType": "auto", - "trimThreshold": 1, - "rotated": false, - "offsetX": 0, - "offsetY": 0, - "trimX": 248, - "trimY": 490, - "width": 248, - "height": 326, - "rawWidth": 248, - "rawHeight": 326, - "borderTop": 0, - "borderBottom": 0, - "borderLeft": 0, - "borderRight": 0, - "spriteType": "normal", - "subMetas": {} - }, - "tian.png": { - "ver": "1.0.6", - "uuid": "134c5e0f-a9ee-4671-a2dd-3c0cf8cf805e", - "importer": "sprite-frame", - "rawTextureUuid": "b72f8f63-5869-4f82-83a6-70c719b5e4ac", - "trimType": "auto", - "trimThreshold": 1, - "rotated": false, - "offsetX": 0, - "offsetY": 0, - "trimX": 728, - "trimY": 0, - "width": 44, - "height": 40, - "rawWidth": 44, - "rawHeight": 40, - "borderTop": 0, - "borderBottom": 0, - "borderLeft": 0, - "borderRight": 0, - "spriteType": "normal", - "subMetas": {} - }, - "tt.png": { - "ver": "1.0.6", - "uuid": "f1560937-7835-4688-9c6b-d7fe8e660b5f", - "importer": "sprite-frame", - "rawTextureUuid": "b72f8f63-5869-4f82-83a6-70c719b5e4ac", - "trimType": "auto", - "trimThreshold": 1, - "rotated": false, - "offsetX": 0, - "offsetY": 0, - "trimX": 0, - "trimY": 0, - "width": 34, - "height": 30, - "rawWidth": 34, - "rawHeight": 30, - "borderTop": 0, - "borderBottom": 0, - "borderLeft": 0, - "borderRight": 0, - "spriteType": "normal", - "subMetas": {} - }, - "yuekazi.png": { - "ver": "1.0.6", - "uuid": "580975aa-0cf8-479f-854f-bd526d0e55bd", - "importer": "sprite-frame", - "rawTextureUuid": "b72f8f63-5869-4f82-83a6-70c719b5e4ac", - "trimType": "auto", - "trimThreshold": 1, - "rotated": false, - "offsetX": 0, - "offsetY": 0, - "trimX": 249, - "trimY": 0, - "width": 435, - "height": 38, - "rawWidth": 435, - "rawHeight": 38, - "borderTop": 0, - "borderBottom": 0, - "borderLeft": 0, - "borderRight": 0, - "spriteType": "normal", - "subMetas": {} - } - } -} \ No newline at end of file diff --git a/assets/shop/img/yueka.png b/assets/shop/img/yueka.png deleted file mode 100644 index ee714ca9..00000000 Binary files a/assets/shop/img/yueka.png and /dev/null differ diff --git a/assets/shop/img/yueka.png.meta b/assets/shop/img/yueka.png.meta deleted file mode 100644 index 391ae8b7..00000000 --- a/assets/shop/img/yueka.png.meta +++ /dev/null @@ -1,15 +0,0 @@ -{ - "ver": "2.3.7", - "uuid": "b72f8f63-5869-4f82-83a6-70c719b5e4ac", - "importer": "texture", - "type": "raw", - "wrapMode": "clamp", - "filterMode": "bilinear", - "premultiplyAlpha": false, - "genMipmaps": false, - "packable": true, - "width": 2002, - "height": 996, - "platformSettings": {}, - "subMetas": {} -} \ No newline at end of file diff --git a/assets/shop/img/yueka1.png b/assets/shop/img/yueka1.png deleted file mode 100644 index 9ee38bb9..00000000 Binary files a/assets/shop/img/yueka1.png and /dev/null differ diff --git a/assets/shop/prefab/shop.prefab b/assets/shop/prefab/shop.prefab index 5a1134be..bb24f33c 100644 --- a/assets/shop/prefab/shop.prefab +++ b/assets/shop/prefab/shop.prefab @@ -27,32 +27,32 @@ "__id__": 11 }, { - "__id__": 444 + "__id__": 466 }, { - "__id__": 526 + "__id__": 548 }, { - "__id__": 529 + "__id__": 551 }, { - "__id__": 537 + "__id__": 559 }, { - "__id__": 550 + "__id__": 572 } ], "_active": true, "_components": [ { - "__id__": 577 + "__id__": 599 }, { - "__id__": 578 + "__id__": 600 } ], "_prefab": { - "__id__": 579 + "__id__": 601 }, "_opacity": 255, "_color": { @@ -411,11 +411,11 @@ "__id__": 17 }, { - "__id__": 442 + "__id__": 464 } ], "_prefab": { - "__id__": 443 + "__id__": 465 }, "_opacity": 255, "_color": { @@ -482,11 +482,11 @@ "__id__": 16 }, { - "__id__": 440 + "__id__": 462 } ], "_prefab": { - "__id__": 441 + "__id__": 463 }, "_opacity": 255, "_color": { @@ -716,16 +716,19 @@ }, { "__id__": 435 + }, + { + "__id__": 438 } ], "_active": true, "_components": [ { - "__id__": 438 + "__id__": 460 } ], "_prefab": { - "__id__": 439 + "__id__": 461 }, "_opacity": 255, "_color": { @@ -935,7 +938,7 @@ "ctor": "Float64Array", "array": [ 0, - -327.441, + -692.346, 0, 0, 0, @@ -2496,7 +2499,7 @@ "ctor": "Float64Array", "array": [ 0.491, - -599.734, + -968.65, 0, 0, 0, @@ -3232,7 +3235,7 @@ "ctor": "Float64Array", "array": [ 0, - 0, + -372.916, 0, 0, 0, @@ -10015,7 +10018,7 @@ "ctor": "Float64Array", "array": [ 0, - -1613.282, + -1986.198, 0, 0, 0, @@ -16196,7 +16199,7 @@ "ctor": "Float64Array", "array": [ 0, - -3550.469, + -3923.385, 0, 0, 0, @@ -16303,7 +16306,7 @@ "ctor": "Float64Array", "array": [ 0, - -4028.521, + -4401.437, 0, 0, 0, @@ -16370,6 +16373,818 @@ "fileId": "f6lUFFE5VOZr7YPoYnwgu9", "sync": false }, + { + "__type__": "cc.Node", + "_name": "jungle", + "_objFlags": 0, + "_parent": { + "__id__": 18 + }, + "_children": [ + { + "__id__": 439 + }, + { + "__id__": 447 + }, + { + "__id__": 450 + }, + { + "__id__": 455 + } + ], + "_active": true, + "_components": [ + { + "__id__": 458 + } + ], + "_prefab": { + "__id__": 459 + }, + "_opacity": 255, + "_color": { + "__type__": "cc.Color", + "r": 255, + "g": 255, + "b": 255, + "a": 255 + }, + "_contentSize": { + "__type__": "cc.Size", + "width": 962, + "height": 370 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.5, + "y": 0.5 + }, + "_trs": { + "__type__": "TypedArray", + "ctor": "Float64Array", + "array": [ + 0, + -272.346, + 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.Node", + "_name": "shengxiaozit", + "_objFlags": 0, + "_parent": { + "__id__": 438 + }, + "_children": [ + { + "__id__": 440 + }, + { + "__id__": 443 + } + ], + "_active": true, + "_components": [ + { + "__id__": 445 + } + ], + "_prefab": { + "__id__": 446 + }, + "_opacity": 255, + "_color": { + "__type__": "cc.Color", + "r": 255, + "g": 255, + "b": 255, + "a": 255 + }, + "_contentSize": { + "__type__": "cc.Size", + "width": 215, + "height": 32 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.5, + "y": 0.5 + }, + "_trs": { + "__type__": "TypedArray", + "ctor": "Float64Array", + "array": [ + 216.908, + -15.498, + 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.Node", + "_name": "tt", + "_objFlags": 0, + "_parent": { + "__id__": 439 + }, + "_children": [], + "_active": true, + "_components": [ + { + "__id__": 441 + } + ], + "_prefab": { + "__id__": 442 + }, + "_opacity": 255, + "_color": { + "__type__": "cc.Color", + "r": 255, + "g": 255, + "b": 255, + "a": 255 + }, + "_contentSize": { + "__type__": "cc.Size", + "width": 34, + "height": 30 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.5, + "y": 0.5 + }, + "_trs": { + "__type__": "TypedArray", + "ctor": "Float64Array", + "array": [ + 206.398, + -0.456, + 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__": 440 + }, + "_enabled": true, + "_materials": [ + { + "__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432" + } + ], + "_srcBlendFactor": 770, + "_dstBlendFactor": 771, + "_spriteFrame": null, + "_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": "13KE47xu1IjoLagLSxZBiV", + "sync": false + }, + { + "__type__": "cc.Node", + "_name": "time", + "_objFlags": 0, + "_parent": { + "__id__": 439 + }, + "_children": [], + "_active": true, + "_components": [], + "_prefab": { + "__id__": 444 + }, + "_opacity": 255, + "_color": { + "__type__": "cc.Color", + "r": 255, + "g": 255, + "b": 255, + "a": 255 + }, + "_contentSize": { + "__type__": "cc.Size", + "width": 0, + "height": 0 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.5, + "y": 0.5 + }, + "_trs": { + "__type__": "TypedArray", + "ctor": "Float64Array", + "array": [ + 165.443, + -1.596, + 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.PrefabInfo", + "root": { + "__id__": 1 + }, + "asset": { + "__id__": 0 + }, + "fileId": "b6NwpGGzpAdaPQ6yxEVDo5", + "sync": false + }, + { + "__type__": "cc.Sprite", + "_name": "", + "_objFlags": 0, + "node": { + "__id__": 439 + }, + "_enabled": true, + "_materials": [ + { + "__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432" + } + ], + "_srcBlendFactor": 770, + "_dstBlendFactor": 771, + "_spriteFrame": null, + "_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": "3e0O2aAH5HGKYsKwZ79wOU", + "sync": false + }, + { + "__type__": "cc.Node", + "_name": "yuekazi", + "_objFlags": 0, + "_parent": { + "__id__": 438 + }, + "_children": [], + "_active": true, + "_components": [ + { + "__id__": 448 + } + ], + "_prefab": { + "__id__": 449 + }, + "_opacity": 255, + "_color": { + "__type__": "cc.Color", + "r": 255, + "g": 255, + "b": 255, + "a": 255 + }, + "_contentSize": { + "__type__": "cc.Size", + "width": 435, + "height": 38 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.5, + "y": 0.5 + }, + "_trs": { + "__type__": "TypedArray", + "ctor": "Float64Array", + "array": [ + 222.837, + 69.969, + 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__": 447 + }, + "_enabled": true, + "_materials": [ + { + "__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432" + } + ], + "_srcBlendFactor": 770, + "_dstBlendFactor": 771, + "_spriteFrame": null, + "_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": "4751GVMwBKZqGP89yZcaxa", + "sync": false + }, + { + "__type__": "cc.Node", + "_name": "lingqu", + "_objFlags": 0, + "_parent": { + "__id__": 438 + }, + "_children": [], + "_active": true, + "_components": [ + { + "__id__": 451 + }, + { + "__id__": 452 + } + ], + "_prefab": { + "__id__": 454 + }, + "_opacity": 255, + "_color": { + "__type__": "cc.Color", + "r": 255, + "g": 255, + "b": 255, + "a": 255 + }, + "_contentSize": { + "__type__": "cc.Size", + "width": 315, + "height": 110 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.5, + "y": 0.5 + }, + "_trs": { + "__type__": "TypedArray", + "ctor": "Float64Array", + "array": [ + 219.493, + -89, + 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__": 450 + }, + "_enabled": true, + "_materials": [ + { + "__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432" + } + ], + "_srcBlendFactor": 770, + "_dstBlendFactor": 771, + "_spriteFrame": { + "__uuid__": "efd7bbd4-0a20-42e3-9019-4037ce7e9a9d" + }, + "_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__": 450 + }, + "_enabled": true, + "_normalMaterial": null, + "_grayMaterial": null, + "duration": 0.1, + "zoomScale": 1.1, + "clickEvents": [ + { + "__id__": 453 + } + ], + "_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": null, + "_id": "" + }, + { + "__type__": "cc.ClickEvent", + "target": { + "__id__": 1 + }, + "component": "", + "_componentId": "48bfeZuYFZE2qmgxbW2IigB", + "handler": "openJungleTreasure", + "customEventData": "" + }, + { + "__type__": "cc.PrefabInfo", + "root": { + "__id__": 1 + }, + "asset": { + "__id__": 0 + }, + "fileId": "452MNwjchGb5G8SXdmCAHF", + "sync": false + }, + { + "__type__": "cc.Node", + "_name": "openTitle", + "_objFlags": 0, + "_parent": { + "__id__": 438 + }, + "_children": [], + "_active": true, + "_components": [ + { + "__id__": 456 + } + ], + "_prefab": { + "__id__": 457 + }, + "_opacity": 255, + "_color": { + "__type__": "cc.Color", + "r": 255, + "g": 255, + "b": 255, + "a": 255 + }, + "_contentSize": { + "__type__": "cc.Size", + "width": 110, + "height": 108 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.5, + "y": 0.5 + }, + "_trs": { + "__type__": "TypedArray", + "ctor": "Float64Array", + "array": [ + 394.602, + -68.706, + 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__": 455 + }, + "_enabled": true, + "_materials": [ + { + "__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432" + } + ], + "_srcBlendFactor": 770, + "_dstBlendFactor": 771, + "_spriteFrame": { + "__uuid__": "83321555-9e19-4355-bf23-15c122d5b758" + }, + "_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": "bfXEURQs1IQZ640Ol1lpOh", + "sync": false + }, + { + "__type__": "cc.Sprite", + "_name": "", + "_objFlags": 0, + "node": { + "__id__": 438 + }, + "_enabled": true, + "_materials": [ + { + "__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432" + } + ], + "_srcBlendFactor": 770, + "_dstBlendFactor": 771, + "_spriteFrame": { + "__uuid__": "c3088e91-abff-46df-85b1-4cdc6b156602" + }, + "_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": "daAEBK+9FHZLdeJAgFvwYN", + "sync": false + }, { "__type__": "cc.Layout", "_name": "", @@ -16500,32 +17315,32 @@ }, "_children": [ { - "__id__": 445 + "__id__": 467 }, { - "__id__": 449 + "__id__": 471 }, { - "__id__": 454 + "__id__": 476 }, { - "__id__": 463 + "__id__": 485 }, { - "__id__": 469 + "__id__": 491 }, { - "__id__": 511 + "__id__": 533 } ], "_active": true, "_components": [ { - "__id__": 524 + "__id__": 546 } ], "_prefab": { - "__id__": 525 + "__id__": 547 }, "_opacity": 255, "_color": { @@ -16579,20 +17394,20 @@ "_name": "shop_1", "_objFlags": 0, "_parent": { - "__id__": 444 + "__id__": 466 }, "_children": [], "_active": true, "_components": [ { - "__id__": 446 + "__id__": 468 }, { - "__id__": 447 + "__id__": 469 } ], "_prefab": { - "__id__": 448 + "__id__": 470 }, "_opacity": 255, "_color": { @@ -16617,7 +17432,7 @@ "ctor": "Float64Array", "array": [ 0, - -556.1390000000001, + -510.86799999999994, 0, 0, 0, @@ -16646,7 +17461,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 445 + "__id__": 467 }, "_enabled": true, "_materials": [ @@ -16678,7 +17493,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 445 + "__id__": 467 }, "_enabled": true, "alignMode": 1, @@ -16688,7 +17503,7 @@ "_alignFlags": 1, "_left": 0, "_right": 0, - "_top": -773.335, + "_top": -818.606, "_bottom": 0, "_verticalCenter": 0, "_horizontalCenter": 0, @@ -16718,20 +17533,20 @@ "_name": "kuang", "_objFlags": 0, "_parent": { - "__id__": 444 + "__id__": 466 }, "_children": [], "_active": true, "_components": [ { - "__id__": 450 + "__id__": 472 }, { - "__id__": 451 + "__id__": 473 } ], "_prefab": { - "__id__": 453 + "__id__": 475 }, "_opacity": 0, "_color": { @@ -16785,7 +17600,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 449 + "__id__": 471 }, "_enabled": true, "_materials": [ @@ -16819,7 +17634,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 449 + "__id__": 471 }, "_enabled": true, "_normalMaterial": null, @@ -16828,7 +17643,7 @@ "zoomScale": 1.2, "clickEvents": [ { - "__id__": 452 + "__id__": 474 } ], "_N$interactable": true, @@ -16884,7 +17699,7 @@ "hoverSprite": null, "_N$disabledSprite": null, "_N$target": { - "__id__": 449 + "__id__": 471 }, "_id": "" }, @@ -16912,24 +17727,24 @@ "_name": "avatar", "_objFlags": 0, "_parent": { - "__id__": 444 + "__id__": 466 }, "_children": [ { - "__id__": 455 + "__id__": 477 }, { - "__id__": 458 + "__id__": 480 } ], "_active": true, "_components": [ { - "__id__": 461 + "__id__": 483 } ], "_prefab": { - "__id__": 462 + "__id__": 484 }, "_opacity": 0, "_color": { @@ -16983,17 +17798,17 @@ "_name": "avatar", "_objFlags": 0, "_parent": { - "__id__": 454 + "__id__": 476 }, "_children": [], "_active": true, "_components": [ { - "__id__": 456 + "__id__": 478 } ], "_prefab": { - "__id__": 457 + "__id__": 479 }, "_opacity": 255, "_color": { @@ -17047,7 +17862,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 455 + "__id__": 477 }, "_enabled": true, "_materials": [ @@ -17088,17 +17903,17 @@ "_name": "sp", "_objFlags": 0, "_parent": { - "__id__": 454 + "__id__": 476 }, "_children": [], "_active": true, "_components": [ { - "__id__": 459 + "__id__": 481 } ], "_prefab": { - "__id__": 460 + "__id__": 482 }, "_opacity": 255, "_color": { @@ -17152,7 +17967,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 458 + "__id__": 480 }, "_enabled": true, "_materials": [ @@ -17203,7 +18018,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 454 + "__id__": 476 }, "_enabled": true, "_materials": [ @@ -17236,23 +18051,23 @@ "_name": "btn1", "_objFlags": 0, "_parent": { - "__id__": 444 + "__id__": 466 }, "_children": [], "_active": true, "_components": [ { - "__id__": 464 + "__id__": 486 }, { - "__id__": 465 + "__id__": 487 }, { - "__id__": 466 + "__id__": 488 } ], "_prefab": { - "__id__": 468 + "__id__": 490 }, "_opacity": 255, "_color": { @@ -17276,8 +18091,8 @@ "__type__": "TypedArray", "ctor": "Float64Array", "array": [ - 447.208, - -98.505, + 483.507, + -67.75400000000002, 0, 0, 0, @@ -17306,7 +18121,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 463 + "__id__": 485 }, "_enabled": true, "_materials": [ @@ -17340,7 +18155,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 463 + "__id__": 485 }, "_enabled": true, "alignMode": 1, @@ -17350,7 +18165,7 @@ "_alignFlags": 1, "_left": 0, "_right": 24.363999999999976, - "_top": 318.03099999999995, + "_top": 287.28, "_bottom": 0, "_verticalCenter": 0, "_horizontalCenter": 0, @@ -17369,7 +18184,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 463 + "__id__": 485 }, "_enabled": true, "_normalMaterial": null, @@ -17378,7 +18193,7 @@ "zoomScale": 1.2, "clickEvents": [ { - "__id__": 467 + "__id__": 489 } ], "_N$interactable": true, @@ -17434,7 +18249,7 @@ "hoverSprite": null, "_N$disabledSprite": null, "_N$target": { - "__id__": 463 + "__id__": 485 }, "_id": "" }, @@ -17464,42 +18279,42 @@ "_name": "Stamina", "_objFlags": 0, "_parent": { - "__id__": 444 + "__id__": 466 }, "_children": [ - { - "__id__": 470 - }, - { - "__id__": 473 - }, - { - "__id__": 476 - }, - { - "__id__": 481 - }, - { - "__id__": 484 - }, - { - "__id__": 487 - }, - { - "__id__": 489 - }, { "__id__": 492 + }, + { + "__id__": 495 + }, + { + "__id__": 498 + }, + { + "__id__": 503 + }, + { + "__id__": 506 + }, + { + "__id__": 509 + }, + { + "__id__": 511 + }, + { + "__id__": 514 } ], "_active": true, "_components": [ { - "__id__": 508 + "__id__": 530 } ], "_prefab": { - "__id__": 510 + "__id__": 532 }, "_opacity": 255, "_color": { @@ -17553,17 +18368,17 @@ "_name": "bg", "_objFlags": 0, "_parent": { - "__id__": 469 + "__id__": 491 }, "_children": [], "_active": true, "_components": [ { - "__id__": 471 + "__id__": 493 } ], "_prefab": { - "__id__": 472 + "__id__": 494 }, "_opacity": 255, "_color": { @@ -17617,7 +18432,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 470 + "__id__": 492 }, "_enabled": true, "_materials": [ @@ -17662,17 +18477,17 @@ "_name": "progresss", "_objFlags": 0, "_parent": { - "__id__": 469 + "__id__": 491 }, "_children": [], "_active": true, "_components": [ { - "__id__": 474 + "__id__": 496 } ], "_prefab": { - "__id__": 475 + "__id__": 497 }, "_opacity": 255, "_color": { @@ -17726,7 +18541,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 473 + "__id__": 495 }, "_enabled": true, "_materials": [ @@ -17771,17 +18586,17 @@ "_name": "man", "_objFlags": 0, "_parent": { - "__id__": 469 + "__id__": 491 }, "_children": [ { - "__id__": 477 + "__id__": 499 } ], "_active": false, "_components": [], "_prefab": { - "__id__": 480 + "__id__": 502 }, "_opacity": 255, "_color": { @@ -17835,17 +18650,17 @@ "_name": "man", "_objFlags": 0, "_parent": { - "__id__": 476 + "__id__": 498 }, "_children": [], "_active": true, "_components": [ { - "__id__": 478 + "__id__": 500 } ], "_prefab": { - "__id__": 479 + "__id__": 501 }, "_opacity": 255, "_color": { @@ -17899,7 +18714,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 477 + "__id__": 499 }, "_enabled": true, "_materials": [ @@ -17955,17 +18770,17 @@ "_name": "New Sprite", "_objFlags": 0, "_parent": { - "__id__": 469 + "__id__": 491 }, "_children": [], "_active": true, "_components": [ { - "__id__": 482 + "__id__": 504 } ], "_prefab": { - "__id__": 483 + "__id__": 505 }, "_opacity": 255, "_color": { @@ -18019,7 +18834,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 481 + "__id__": 503 }, "_enabled": true, "_materials": [ @@ -18062,17 +18877,17 @@ "_name": "New Sprite", "_objFlags": 0, "_parent": { - "__id__": 469 + "__id__": 491 }, "_children": [], "_active": true, "_components": [ { - "__id__": 485 + "__id__": 507 } ], "_prefab": { - "__id__": 486 + "__id__": 508 }, "_opacity": 255, "_color": { @@ -18126,7 +18941,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 484 + "__id__": 506 }, "_enabled": true, "_materials": [ @@ -18169,13 +18984,13 @@ "_name": "health", "_objFlags": 0, "_parent": { - "__id__": 469 + "__id__": 491 }, "_children": [], "_active": true, "_components": [], "_prefab": { - "__id__": 488 + "__id__": 510 }, "_opacity": 255, "_color": { @@ -18240,17 +19055,17 @@ "_name": "time", "_objFlags": 0, "_parent": { - "__id__": 469 + "__id__": 491 }, "_children": [], "_active": false, "_components": [ { - "__id__": 490 + "__id__": 512 } ], "_prefab": { - "__id__": 491 + "__id__": 513 }, "_opacity": 0, "_color": { @@ -18304,7 +19119,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 489 + "__id__": 511 }, "_enabled": true, "_materials": [ @@ -18348,23 +19163,23 @@ "_name": "skyLine", "_objFlags": 0, "_parent": { - "__id__": 469 + "__id__": 491 }, "_children": [ { - "__id__": 493 + "__id__": 515 }, { - "__id__": 496 + "__id__": 518 }, { - "__id__": 499 + "__id__": 521 } ], "_active": false, "_components": [], "_prefab": { - "__id__": 507 + "__id__": 529 }, "_opacity": 255, "_color": { @@ -18418,17 +19233,17 @@ "_name": "New Sprite", "_objFlags": 0, "_parent": { - "__id__": 492 + "__id__": 514 }, "_children": [], "_active": true, "_components": [ { - "__id__": 494 + "__id__": 516 } ], "_prefab": { - "__id__": 495 + "__id__": 517 }, "_opacity": 255, "_color": { @@ -18482,7 +19297,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 493 + "__id__": 515 }, "_enabled": true, "_materials": [ @@ -18525,17 +19340,17 @@ "_name": "skyTime", "_objFlags": 0, "_parent": { - "__id__": 492 + "__id__": 514 }, "_children": [], "_active": true, "_components": [ { - "__id__": 497 + "__id__": 519 } ], "_prefab": { - "__id__": 498 + "__id__": 520 }, "_opacity": 255, "_color": { @@ -18589,7 +19404,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 496 + "__id__": 518 }, "_enabled": true, "_materials": [ @@ -18633,20 +19448,20 @@ "_name": "up", "_objFlags": 0, "_parent": { - "__id__": 492 + "__id__": 514 }, "_children": [ { - "__id__": 500 + "__id__": 522 }, { - "__id__": 503 + "__id__": 525 } ], "_active": true, "_components": [], "_prefab": { - "__id__": 506 + "__id__": 528 }, "_opacity": 255, "_color": { @@ -18700,17 +19515,17 @@ "_name": "tili", "_objFlags": 0, "_parent": { - "__id__": 499 + "__id__": 521 }, "_children": [], "_active": true, "_components": [ { - "__id__": 501 + "__id__": 523 } ], "_prefab": { - "__id__": 502 + "__id__": 524 }, "_opacity": 255, "_color": { @@ -18764,7 +19579,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 500 + "__id__": 522 }, "_enabled": true, "_materials": [ @@ -18817,17 +19632,17 @@ "_name": "light", "_objFlags": 0, "_parent": { - "__id__": 499 + "__id__": 521 }, "_children": [], "_active": true, "_components": [ { - "__id__": 504 + "__id__": 526 } ], "_prefab": { - "__id__": 505 + "__id__": 527 }, "_opacity": 255, "_color": { @@ -18881,7 +19696,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 503 + "__id__": 525 }, "_enabled": true, "_materials": [ @@ -18956,7 +19771,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 469 + "__id__": 491 }, "_enabled": true, "_normalMaterial": null, @@ -18965,7 +19780,7 @@ "zoomScale": 1.1, "clickEvents": [ { - "__id__": 509 + "__id__": 531 } ], "_N$interactable": true, @@ -19021,7 +19836,7 @@ "hoverSprite": null, "_N$disabledSprite": null, "_N$target": { - "__id__": 469 + "__id__": 491 }, "_id": "" }, @@ -19049,26 +19864,26 @@ "_name": "Coin", "_objFlags": 0, "_parent": { - "__id__": 444 + "__id__": 466 }, "_children": [ { - "__id__": 512 + "__id__": 534 }, { - "__id__": 515 + "__id__": 537 }, { - "__id__": 518 + "__id__": 540 }, { - "__id__": 521 + "__id__": 543 } ], "_active": true, "_components": [], "_prefab": { - "__id__": 523 + "__id__": 545 }, "_opacity": 255, "_color": { @@ -19122,17 +19937,17 @@ "_name": "New Sprite", "_objFlags": 0, "_parent": { - "__id__": 511 + "__id__": 533 }, "_children": [], "_active": true, "_components": [ { - "__id__": 513 + "__id__": 535 } ], "_prefab": { - "__id__": 514 + "__id__": 536 }, "_opacity": 255, "_color": { @@ -19186,7 +20001,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 512 + "__id__": 534 }, "_enabled": true, "_materials": [ @@ -19229,17 +20044,17 @@ "_name": "New Sprite", "_objFlags": 0, "_parent": { - "__id__": 511 + "__id__": 533 }, "_children": [], "_active": true, "_components": [ { - "__id__": 516 + "__id__": 538 } ], "_prefab": { - "__id__": 517 + "__id__": 539 }, "_opacity": 255, "_color": { @@ -19293,7 +20108,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 515 + "__id__": 537 }, "_enabled": true, "_materials": [ @@ -19336,17 +20151,17 @@ "_name": "New Sprite", "_objFlags": 0, "_parent": { - "__id__": 511 + "__id__": 533 }, "_children": [], "_active": false, "_components": [ { - "__id__": 519 + "__id__": 541 } ], "_prefab": { - "__id__": 520 + "__id__": 542 }, "_opacity": 255, "_color": { @@ -19400,7 +20215,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 518 + "__id__": 540 }, "_enabled": true, "_materials": [ @@ -19443,13 +20258,13 @@ "_name": "Coin", "_objFlags": 0, "_parent": { - "__id__": 511 + "__id__": 533 }, "_children": [], "_active": true, "_components": [], "_prefab": { - "__id__": 522 + "__id__": 544 }, "_opacity": 255, "_color": { @@ -19525,7 +20340,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 444 + "__id__": 466 }, "_enabled": true, "alignMode": 1, @@ -19571,11 +20386,11 @@ "_active": false, "_components": [ { - "__id__": 527 + "__id__": 549 } ], "_prefab": { - "__id__": 528 + "__id__": 550 }, "_opacity": 255, "_color": { @@ -19629,7 +20444,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 526 + "__id__": 548 }, "_enabled": true, "_materials": [ @@ -19686,17 +20501,17 @@ }, "_children": [ { - "__id__": 530 + "__id__": 552 } ], "_active": false, "_components": [ { - "__id__": 534 + "__id__": 556 } ], "_prefab": { - "__id__": 536 + "__id__": 558 }, "_opacity": 255, "_color": { @@ -19750,20 +20565,20 @@ "_name": "Background", "_objFlags": 512, "_parent": { - "__id__": 529 + "__id__": 551 }, "_children": [], "_active": true, "_components": [ { - "__id__": 531 + "__id__": 553 }, { - "__id__": 532 + "__id__": 554 } ], "_prefab": { - "__id__": 533 + "__id__": 555 }, "_opacity": 255, "_color": { @@ -19817,7 +20632,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 530 + "__id__": 552 }, "_enabled": true, "_materials": [ @@ -19851,7 +20666,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 530 + "__id__": 552 }, "_enabled": true, "alignMode": 0, @@ -19889,7 +20704,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 529 + "__id__": 551 }, "_enabled": true, "_normalMaterial": null, @@ -19898,7 +20713,7 @@ "zoomScale": 1.2, "clickEvents": [ { - "__id__": 535 + "__id__": 557 } ], "_N$interactable": true, @@ -19966,7 +20781,7 @@ "__uuid__": "29158224-f8dd-4661-a796-1ffab537140e" }, "_N$target": { - "__id__": 530 + "__id__": 552 }, "_id": "" }, @@ -20000,23 +20815,23 @@ }, "_children": [ { - "__id__": 538 + "__id__": 560 }, { - "__id__": 542 + "__id__": 564 }, { - "__id__": 545 + "__id__": 567 } ], "_active": false, "_components": [ { - "__id__": 548 + "__id__": 570 } ], "_prefab": { - "__id__": 549 + "__id__": 571 }, "_opacity": 255, "_color": { @@ -20070,20 +20885,20 @@ "_name": "New Sprite(Splash)", "_objFlags": 0, "_parent": { - "__id__": 537 + "__id__": 559 }, "_children": [], "_active": true, "_components": [ { - "__id__": 539 + "__id__": 561 }, { - "__id__": 540 + "__id__": 562 } ], "_prefab": { - "__id__": 541 + "__id__": 563 }, "_opacity": 120, "_color": { @@ -20137,7 +20952,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 538 + "__id__": 560 }, "_enabled": true, "_materials": [ @@ -20169,7 +20984,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 538 + "__id__": 560 }, "_enabled": true, "_id": "" @@ -20190,17 +21005,17 @@ "_name": "load", "_objFlags": 0, "_parent": { - "__id__": 537 + "__id__": 559 }, "_children": [], "_active": true, "_components": [ { - "__id__": 543 + "__id__": 565 } ], "_prefab": { - "__id__": 544 + "__id__": 566 }, "_opacity": 255, "_color": { @@ -20254,7 +21069,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 542 + "__id__": 564 }, "_enabled": true, "_materials": [ @@ -20297,17 +21112,17 @@ "_name": "New Label", "_objFlags": 0, "_parent": { - "__id__": 537 + "__id__": 559 }, "_children": [], "_active": true, "_components": [ { - "__id__": 546 + "__id__": 568 } ], "_prefab": { - "__id__": 547 + "__id__": 569 }, "_opacity": 255, "_color": { @@ -20361,7 +21176,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 545 + "__id__": 567 }, "_enabled": true, "_materials": [ @@ -20405,7 +21220,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 537 + "__id__": 559 }, "_enabled": true, "alignMode": 1, @@ -20447,32 +21262,32 @@ }, "_children": [ { - "__id__": 551 + "__id__": 573 }, { - "__id__": 555 + "__id__": 577 }, { - "__id__": 558 + "__id__": 580 }, { - "__id__": 561 + "__id__": 583 }, { - "__id__": 564 + "__id__": 586 }, { - "__id__": 567 + "__id__": 589 } ], "_active": false, "_components": [ { - "__id__": 575 + "__id__": 597 } ], "_prefab": { - "__id__": 576 + "__id__": 598 }, "_opacity": 255, "_color": { @@ -20526,20 +21341,20 @@ "_name": "New Sprite(Splash)", "_objFlags": 0, "_parent": { - "__id__": 550 + "__id__": 572 }, "_children": [], "_active": true, "_components": [ { - "__id__": 552 + "__id__": 574 }, { - "__id__": 553 + "__id__": 575 } ], "_prefab": { - "__id__": 554 + "__id__": 576 }, "_opacity": 120, "_color": { @@ -20593,7 +21408,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 551 + "__id__": 573 }, "_enabled": true, "_materials": [ @@ -20625,7 +21440,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 551 + "__id__": 573 }, "_enabled": true, "_id": "" @@ -20633,7 +21448,7 @@ { "__type__": "cc.PrefabInfo", "root": { - "__id__": 550 + "__id__": 572 }, "asset": { "__uuid__": "9fdc6671-8422-410b-b9af-36dab20f1488" @@ -20646,17 +21461,17 @@ "_name": "bg", "_objFlags": 0, "_parent": { - "__id__": 550 + "__id__": 572 }, "_children": [], "_active": true, "_components": [ { - "__id__": 556 + "__id__": 578 } ], "_prefab": { - "__id__": 557 + "__id__": 579 }, "_opacity": 255, "_color": { @@ -20710,7 +21525,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 555 + "__id__": 577 }, "_enabled": true, "_materials": [ @@ -20740,7 +21555,7 @@ { "__type__": "cc.PrefabInfo", "root": { - "__id__": 550 + "__id__": 572 }, "asset": { "__uuid__": "9fdc6671-8422-410b-b9af-36dab20f1488" @@ -20753,17 +21568,17 @@ "_name": "title", "_objFlags": 0, "_parent": { - "__id__": 550 + "__id__": 572 }, "_children": [], "_active": true, "_components": [ { - "__id__": 559 + "__id__": 581 } ], "_prefab": { - "__id__": 560 + "__id__": 582 }, "_opacity": 255, "_color": { @@ -20817,7 +21632,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 558 + "__id__": 580 }, "_enabled": true, "_materials": [ @@ -20848,7 +21663,7 @@ { "__type__": "cc.PrefabInfo", "root": { - "__id__": 550 + "__id__": 572 }, "asset": { "__uuid__": "9fdc6671-8422-410b-b9af-36dab20f1488" @@ -20861,17 +21676,17 @@ "_name": "New Label", "_objFlags": 0, "_parent": { - "__id__": 550 + "__id__": 572 }, "_children": [], "_active": false, "_components": [ { - "__id__": 562 + "__id__": 584 } ], "_prefab": { - "__id__": 563 + "__id__": 585 }, "_opacity": 255, "_color": { @@ -20925,7 +21740,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 561 + "__id__": 583 }, "_enabled": true, "_materials": [ @@ -20956,7 +21771,7 @@ { "__type__": "cc.PrefabInfo", "root": { - "__id__": 550 + "__id__": 572 }, "asset": { "__uuid__": "9fdc6671-8422-410b-b9af-36dab20f1488" @@ -20969,17 +21784,17 @@ "_name": "New Label", "_objFlags": 0, "_parent": { - "__id__": 550 + "__id__": 572 }, "_children": [], "_active": true, "_components": [ { - "__id__": 565 + "__id__": 587 } ], "_prefab": { - "__id__": 566 + "__id__": 588 }, "_opacity": 255, "_color": { @@ -21033,7 +21848,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 564 + "__id__": 586 }, "_enabled": true, "_materials": [ @@ -21064,7 +21879,7 @@ { "__type__": "cc.PrefabInfo", "root": { - "__id__": 550 + "__id__": 572 }, "asset": { "__uuid__": "9fdc6671-8422-410b-b9af-36dab20f1488" @@ -21077,24 +21892,24 @@ "_name": "New Sprite", "_objFlags": 0, "_parent": { - "__id__": 550 + "__id__": 572 }, "_children": [ { - "__id__": 568 + "__id__": 590 } ], "_active": true, "_components": [ { - "__id__": 571 + "__id__": 593 }, { - "__id__": 572 + "__id__": 594 } ], "_prefab": { - "__id__": 574 + "__id__": 596 }, "_opacity": 255, "_color": { @@ -21148,17 +21963,17 @@ "_name": "New Label", "_objFlags": 0, "_parent": { - "__id__": 567 + "__id__": 589 }, "_children": [], "_active": true, "_components": [ { - "__id__": 569 + "__id__": 591 } ], "_prefab": { - "__id__": 570 + "__id__": 592 }, "_opacity": 255, "_color": { @@ -21212,7 +22027,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 568 + "__id__": 590 }, "_enabled": true, "_materials": [ @@ -21243,7 +22058,7 @@ { "__type__": "cc.PrefabInfo", "root": { - "__id__": 550 + "__id__": 572 }, "asset": { "__uuid__": "9fdc6671-8422-410b-b9af-36dab20f1488" @@ -21256,7 +22071,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 567 + "__id__": 589 }, "_enabled": true, "_materials": [ @@ -21288,7 +22103,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 567 + "__id__": 589 }, "_enabled": true, "_normalMaterial": null, @@ -21297,7 +22112,7 @@ "zoomScale": 1.1, "clickEvents": [ { - "__id__": 573 + "__id__": 595 } ], "_N$interactable": true, @@ -21353,7 +22168,7 @@ "hoverSprite": null, "_N$disabledSprite": null, "_N$target": { - "__id__": 567 + "__id__": 589 }, "_id": "" }, @@ -21370,7 +22185,7 @@ { "__type__": "cc.PrefabInfo", "root": { - "__id__": 550 + "__id__": 572 }, "asset": { "__uuid__": "9fdc6671-8422-410b-b9af-36dab20f1488" @@ -21383,7 +22198,7 @@ "_name": "", "_objFlags": 0, "node": { - "__id__": 550 + "__id__": 572 }, "_enabled": true, "alignMode": 1, @@ -21408,7 +22223,7 @@ { "__type__": "cc.PrefabInfo", "root": { - "__id__": 550 + "__id__": 572 }, "asset": { "__uuid__": "9fdc6671-8422-410b-b9af-36dab20f1488" @@ -21431,10 +22246,10 @@ "__id__": 18 }, "coin": { - "__id__": 521 + "__id__": 543 }, "Stamina": { - "__id__": 469 + "__id__": 491 }, "monthCardTime": { "__id__": 31 @@ -21443,7 +22258,7 @@ "__uuid__": "fd85df88-648f-4407-bab2-da4f15478fb9" }, "coinAnim": { - "__id__": 526 + "__id__": 548 }, "_id": "" }, diff --git a/assets/shop/script/ShopJungleShine.ts b/assets/shop/script/ShopJungleShine.ts new file mode 100644 index 00000000..255c883d --- /dev/null +++ b/assets/shop/script/ShopJungleShine.ts @@ -0,0 +1,77 @@ +const { ccclass, requireComponent } = cc._decorator; + +/** 商城 Jungle 背景专用的横向扫光。 */ +@ccclass +@requireComponent(cc.Sprite) +export default class ShopJungleShine extends cc.Component { + private sprite: cc.Sprite = null; + private material: cc.Material = null; + private moveDuration: number = 0.72; + private repeatDelay: number = 1.25; + private delayRemaining: number = 0.15; + private elapsed: number = 0; + private startPosition: number = -0.35; + private endPosition: number = 1.35; + + protected onLoad(): void { + this.sprite = this.getComponent(cc.Sprite); + this.loadMaterial(); + } + + protected update(dt: number): void { + if (!this.material) { + return; + } + if (this.delayRemaining > 0) { + this.delayRemaining = Math.max(0, this.delayRemaining - dt); + return; + } + + this.elapsed += dt; + let progress = Math.min(1, this.elapsed / this.moveDuration); + let position = this.startPosition + + (this.endPosition - this.startPosition) * progress; + this.material.setProperty("shinePosition", position); + if (progress >= 1) { + this.elapsed = 0; + this.delayRemaining = this.repeatDelay; + this.material.setProperty("shinePosition", this.startPosition); + } + } + + protected onDestroy(): void { + if (this.material && cc.isValid(this.material)) { + this.material.destroy(); + } + this.material = null; + this.sprite = null; + } + + private loadMaterial(): void { + let bundle = cc.assetManager.getBundle("shop"); + if (!bundle || !this.sprite) { + cc.warn("[ShopJungleShine] 商城分包未加载"); + return; + } + + bundle.load("effect/jungle_shine", cc.EffectAsset, (error: Error, effect: cc.EffectAsset) => { + if (error || !effect || !cc.isValid(this.node) || !this.sprite) { + if (error) { + cc.warn("[ShopJungleShine] 扫光 Shader 加载失败", error); + } + return; + } + + let material = new cc.Material(); + // @ts-ignore Cocos Creator 2.4 运行时通过 effectAsset 创建材质。 + material.effectAsset = effect; + material.setProperty("shinePosition", this.startPosition); + material.setProperty("shineWidth", 0.075); + material.setProperty("shineStrength", 1.8); + material.setProperty("shineAngle", 0.2); + material.setProperty("shineColor", [1.0, 0.95, 0.68, 1.0]); + this.material = material; + this.sprite.setMaterial(0, material); + }); + } +} diff --git a/assets/shop/script/ShopJungleShine.ts.meta b/assets/shop/script/ShopJungleShine.ts.meta new file mode 100644 index 00000000..7c1cca62 --- /dev/null +++ b/assets/shop/script/ShopJungleShine.ts.meta @@ -0,0 +1,10 @@ +{ + "ver": "1.1.0", + "uuid": "aa27c19b-4e7d-43ef-86d2-69c7e0428f4a", + "importer": "typescript", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/assets/shop/script/shop.ts b/assets/shop/script/shop.ts index 34ffb784..c0762ccb 100644 --- a/assets/shop/script/shop.ts +++ b/assets/shop/script/shop.ts @@ -5,6 +5,7 @@ import List from "../../Script/module/RankList/List"; import NumberToImage from "../../Script/NumberToImage"; import SceneManager from "../../Script/SceneManager"; import { MiniGameSdk } from "../../Script/Sdk/MiniGameSdk"; +import ShopJungleShine from "./ShopJungleShine"; const { ccclass, property } = cc._decorator; @@ -53,6 +54,8 @@ export default class NewClass extends cc.Component { reward: boolean = false; private monthCard: boolean = false; doubleInfo: null; + private jungleUnlockLevel: number = 40; + private jungleSiblingOriginalPositions: Array<{ node: cc.Node, y: number }> = []; onLoad() { @@ -69,6 +72,7 @@ export default class NewClass extends cc.Component { init(data) { this.doubleInfo = data; cc.fx.GameConfig.GM_INFO.shopDouble = data; + this.refreshJungleEntryLayout(); this.openShop(); let top = this.node.getChildByName("Top"); top.getChildByName("kuang").opacity = 0; @@ -128,6 +132,49 @@ export default class NewClass extends cc.Component { this.updateIcon(); NumberToImage.numberToImageNodes5(cc.fx.GameConfig.GM_INFO.coin, 30, 15, "Black", this.coin, true); } + + /** 商城 Jungle 入口未解锁或活动已过期时隐藏,并让其他商品补上入口所占高度。 */ + private refreshJungleEntryLayout(): void { + let itemContent = this.node.getChildByName("itemcontent"); + let view = itemContent && itemContent.getChildByName("view"); + let content = view && view.getChildByName("content"); + let jungle = content && content.getChildByName("jungle"); + if (!content || !jungle) { + return; + } + + if (this.jungleSiblingOriginalPositions.length === 0) { + content.children.forEach((child) => { + this.jungleSiblingOriginalPositions.push({ node: child, y: child.y }); + }); + } + + let meetsLevel = (Number(cc.fx.GameConfig.GM_INFO.level) || 0) >= this.jungleUnlockLevel; + let shouldShowJungle = meetsLevel && !this.isJungleTreasureExpired(); + jungle.active = shouldShowJungle; + if (shouldShowJungle && jungle.getComponent(cc.Sprite) && !jungle.getComponent(ShopJungleShine)) { + jungle.addComponent(ShopJungleShine); + } + this.jungleSiblingOriginalPositions.forEach((item) => { + if (cc.isValid(item.node)) { + item.node.y = item.y + (!shouldShowJungle && item.node !== jungle ? 380 : 0); + } + }); + } + + private isJungleTreasureExpired(): boolean { + let state: any = cc.fx.GameConfig.GM_INFO.jungleTreasureState; + if (typeof state === "string") { + try { + state = JSON.parse(state); + } + catch (error) { + return false; + } + } + let expiresAt = Number(state && state.expiresAt); + return isFinite(expiresAt) && expiresAt > 0 && Date.now() >= expiresAt; + } //打开商店界面 openShop() { this.sceneValue = cc.fx.GameConfig.GM_INFO.sceneValue; @@ -284,6 +331,7 @@ export default class NewClass extends cc.Component { onShow() { console.log("从后台进入前台 onShow"); + this.refreshJungleEntryLayout(); let systemInfo = null; if (cc.sys.platform === cc.sys.WECHAT_GAME) //@ts-ignore @@ -751,10 +799,10 @@ export default class NewClass extends cc.Component { //无限体力刷新功能 - updatePower() { + updatePower(refreshGameScene: boolean = true) { console.log("无限体力时间戳:", cc.fx.GameConfig.GM_INFO.userPowerTime); const winCOIN = cc.find("Canvas"); // 假设 Canvas 节点 - if (winCOIN) { + if (refreshGameScene && winCOIN) { const wincoin = winCOIN.getComponent(SceneManager); if (wincoin) { if (MapConroler && MapConroler._instance) { @@ -855,6 +903,37 @@ export default class NewClass extends cc.Component { this.monthCard = true; this.updateIcon() } + + //打开限时活动 + openJungleTreasure(event?: cc.Event) { + let level = Number(cc.fx.GameConfig.GM_INFO.level) || 0; + if (level < this.jungleUnlockLevel || this.isJungleTreasureExpired() + || !this.node || !this.node.parent) { + return; + } + cc.fx.GameTool.shushu_Track("enter_jungle", { + source: "shop_page", + }); + let controller: any = this.node.parent.getComponent("JiaZai") + || this.node.parent.getComponent("SceneManager"); + if (controller && typeof controller.openJungleTreasure === "function") { + controller.openJungleTreasure(event, "shop_page"); + } + else { + cc.error("商城 Jungle 入口未找到当前场景控制器"); + } + } + + /** Jungle 领奖后只刷新商城自身;局内 HUD 由 SceneManager 单独刷新。 */ + refreshJungleRewardDisplay(): void { + this.refreshJungleEntryLayout(); + this.coinAnimating = false; + this.currentCoin = Number(cc.fx.GameConfig.GM_INFO.coin) || 0; + if (this.coin) { + NumberToImage.numberToImageNodes5(this.currentCoin, 30, 15, "Black", this.coin, true); + } + this.updatePower(false); + } //更新图标 updateIcon() { let isExpired = cc.fx.GameTool.checkExpiration(); diff --git a/server/laf-cloud/functions/clearPay.ts b/server/laf-cloud/functions/clearPay.ts index d7324242..682dff16 100644 --- a/server/laf-cloud/functions/clearPay.ts +++ b/server/laf-cloud/functions/clearPay.ts @@ -33,5 +33,13 @@ const propName = { "局内购买冻结": "freeze_in_game", "局内购买锤子": "hammer_in_game", "局内购买魔棒": "wand_in_gamerefill", - "补满体力": "health" + "补满体力": "health", + "宝藏1": "jungle_treasure_1", + "宝藏2": "jungle_treasure_2", + "宝藏3": "jungle_treasure_3", + "宝藏4": "jungle_treasure_4", + "宝藏5": "jungle_treasure_5", + "宝藏6": "jungle_treasure_6", + "宝藏7": "jungle_treasure_7", + "宝藏8": "jungle_treasure_8" }; \ No newline at end of file diff --git a/server/laf-cloud/functions/jungleConfig.ts b/server/laf-cloud/functions/jungleConfig.ts new file mode 100644 index 00000000..8c0ce86e --- /dev/null +++ b/server/laf-cloud/functions/jungleConfig.ts @@ -0,0 +1,439 @@ +export type JungleRewardType = "coin" | "freeze" | "hammer" | "magic_wand" | "infinite_health"; + +export interface JungleRewardConfig { + type: JungleRewardType; + count: number; + itemId?: number; +} + +export interface JungleTierConfig { + id: number; + price: number; + isPaid: boolean; + productId: string; + priceTemporary?: boolean; + rewards: JungleRewardConfig[]; +} + +export interface JungleTreasureConfig { + activityId: string; + version: number; + totalTiers: number; + tiers: JungleTierConfig[]; +} + +/** 0=付费档未解锁,1=已解锁未领取,2=已领取。 */ +export type JungleTierState = 0 | 1 | 2; + +/** 仅服务端存储,paidOrderNos 不返回客户端。 */ +export interface JungleStoredState { + activityId: string; + configVersion: number; + tierStates: JungleTierState[]; + paidOrderNos: Record; + revision: number; + expiresAt: number; + updatedAt: number; +} + +export interface JungleUnlockResult { + ok: boolean; + changed: boolean; + tierId?: number; + state?: JungleStoredState; + message?: string; +} + +export interface JungleClaimResult { + ok: boolean; + tierId?: number; + paidOrderNo?: string; + state?: JungleStoredState; + message?: string; +} + +export const JUNGLE_TREASURE_ACTIVITY_ID = "jungle_treasure_v1"; +export const JUNGLE_TREASURE_CONFIG_VERSION = 2; +export const JUNGLE_TREASURE_TOTAL_TIERS = 51; +export const JUNGLE_TREASURE_DURATION_MS = 7 * 24 * 60 * 60 * 1000; + +/** 后端礼包配置入口。修改数值时只调整此函数中的档位数据。 */ +export function createJungleTreasureConfig(): JungleTreasureConfig { + const tiers: JungleTierConfig[] = [ + createTier(1, 0, [{ type: "coin", count: 100 }]), + createTier(2, 0, [{ type: "hammer", count: 1 }]), + createTier(3, 0, [ + { type: "coin", count: 200 }, + { type: "freeze", count: 1 }, + { type: "magic_wand", count: 1 }, + ]), + createTier(4, 3, [{ type: "coin", count: 700 }], "jungle_treasure_1"), + createTier(5, 0, [{ type: "hammer", count: 1 }]), + createTier(6, 0, [{ type: "infinite_health", count: 900 }]), + createTier(7, 0, [ + { type: "coin", count: 200 }, + { type: "magic_wand", count: 1 }, + ]), + createTier(8, 0, [{ type: "coin", count: 100 }]), + createTier(9, 0, [{ type: "freeze", count: 1 }]), + createTier(10, 6, [ + { type: "coin", count: 1500 }, + { type: "freeze", count: 1 }, + { type: "magic_wand", count: 1 }, + ], "jungle_treasure_2"), + createTier(11, 0, [{ type: "infinite_health", count: 900 }]), + createTier(12, 0, [{ type: "hammer", count: 1 }]), + createTier(13, 0, [{ type: "coin", count: 500 }]), + createTier(14, 0, [{ type: "hammer", count: 1 }]), + createTier(15, 0, [{ type: "magic_wand", count: 1 }]), + createTier(16, 9, [ + { type: "coin", count: 2000 }, + { type: "freeze", count: 1 }, + { type: "magic_wand", count: 1 }, + ], "jungle_treasure_3"), + createTier(17, 0, [{ type: "infinite_health", count: 900 }]), + createTier(18, 0, [ + { type: "hammer", count: 2 }, + { type: "freeze", count: 2 }, + { type: "magic_wand", count: 2 }, + ]), + createTier(19, 0, [{ type: "coin", count: 200 }]), + createTier(20, 0, [{ type: "freeze", count: 1 }]), + createTier(21, 0, [{ type: "magic_wand", count: 1 }]), + createTier(22, 15, [ + { type: "coin", count: 3000 }, + { type: "freeze", count: 3 }, + { type: "magic_wand", count: 3 }, + ], "jungle_treasure_4"), + createTier(23, 0, [ + { type: "hammer", count: 2 }, + { type: "freeze", count: 1 }, + ]), + createTier(24, 0, [ + { type: "infinite_health", count: 1800 }, + { type: "freeze", count: 2 }, + ]), + createTier(25, 0, [{ type: "magic_wand", count: 2 }]), + createTier(26, 0, [{ type: "hammer", count: 1 }]), + createTier(27, 0, [{ type: "magic_wand", count: 1 }]), + createTier(28, 24, [ + { type: "coin", count: 3500 }, + { type: "hammer", count: 2 }, + ], "jungle_treasure_5"), + createTier(29, 0, [ + { type: "hammer", count: 2 }, + { type: "freeze", count: 1 }, + { type: "magic_wand", count: 1 }, + ]), + createTier(30, 0, [ + { type: "freeze", count: 4 }, + { type: "magic_wand", count: 4 }, + ]), + createTier(31, 0, [ + { type: "infinite_health", count: 1800 }, + { type: "freeze", count: 2 }, + ]), + createTier(32, 0, [{ type: "magic_wand", count: 2 }]), + createTier(33, 0, [ + { type: "freeze", count: 1 }, + { type: "magic_wand", count: 1 }, + ]), + createTier(34, 42, [ + { type: "coin", count: 10000 }, + { type: "hammer", count: 3 }, + { type: "freeze", count: 5 }, + ], "jungle_treasure_6"), + createTier(35, 0, [ + { type: "coin", count: 3000 }, + { type: "freeze", count: 2 }, + { type: "magic_wand", count: 2 }, + ]), + createTier(36, 0, [ + { type: "infinite_health", count: 3600 }, + { type: "hammer", count: 2 }, + ]), + createTier(37, 0, [ + { type: "coin", count: 2000 }, + { type: "magic_wand", count: 5 }, + ]), + createTier(38, 0, [{ type: "freeze", count: 3 }]), + createTier(39, 0, [{ type: "magic_wand", count: 3 }]), + createTier(40, 68, [ + { type: "coin", count: 21000 }, + { type: "hammer", count: 10 }, + ], "jungle_treasure_7"), + createTier(41, 0, [ + { type: "infinite_health", count: 7200 }, + { type: "freeze", count: 15 }, + ]), + createTier(42, 0, [{ type: "magic_wand", count: 15 }]), + createTier(43, 0, [{ type: "coin", count: 2500 }]), + createTier(44, 0, [{ type: "freeze", count: 5 }]), + createTier(45, 0, [{ type: "magic_wand", count: 5 }]), + createTier(46, 98, [ + { type: "coin", count: 35000 }, + { type: "freeze", count: 15 }, + { type: "magic_wand", count: 15 }, + ], "jungle_treasure_8"), + createTier(47, 0, [ + { type: "infinite_health", count: 14400 }, + { type: "hammer", count: 10 }, + ]), + createTier(48, 0, [{ type: "hammer", count: 5 }]), + createTier(49, 0, [ + { type: "freeze", count: 10 }, + { type: "magic_wand", count: 5 }, + ]), + createTier(50, 0, [{ type: "coin", count: 2500 }]), + createTier(51, 0, [ + { type: "freeze", count: 5 }, + { type: "magic_wand", count: 10 }, + ]), + ]; + + validateTierOrder(tiers); + return { + activityId: JUNGLE_TREASURE_ACTIVITY_ID, + version: JUNGLE_TREASURE_CONFIG_VERSION, + totalTiers: JUNGLE_TREASURE_TOTAL_TIERS, + tiers, + }; +} + +export const JUNGLE_TREASURE_CONFIG = createJungleTreasureConfig(); + +export function createDefaultJungleState(now = Date.now()): JungleStoredState { + return createStoredState( + JUNGLE_TREASURE_CONFIG.tiers.map((tier) => tier.isPaid ? 0 : 1), + {}, + 0, + now + JUNGLE_TREASURE_DURATION_MS, + now, + ); +} + +/** 只处理当前 JSON 字符串状态;无状态时创建默认 7 天数据。 */ +export function normalizeStoredJungleState(raw: unknown, now = Date.now()): JungleStoredState { + const value = parseUnknownValue(raw); + const defaultState = createDefaultJungleState(now); + if (!value || Array.isArray(value) || !Array.isArray(value.tierStates)) { + return defaultState; + } + + const expiresAt = toPositiveTimestamp(value.expiresAt); + if (!expiresAt) { + return defaultState; + } + + const tierStates = [...defaultState.tierStates]; + for (let index = 0; index < JUNGLE_TREASURE_TOTAL_TIERS; index++) { + const configuredTier = JUNGLE_TREASURE_CONFIG.tiers[index]; + const state = toTierState(value.tierStates[index]); + if (state !== null) { + tierStates[index] = configuredTier.isPaid ? state : (state === 2 ? 2 : 1); + } + else { + tierStates[index] = defaultState.tierStates[index]; + } + } + + return createStoredState( + tierStates, + normalizePaidOrderNos(value), + toNonNegativeInteger(value.revision), + expiresAt, + toNonNegativeInteger(value.updatedAt) || now, + ); +} + +export function isCurrentJungleState(raw: unknown): boolean { + const value = parseUnknownValue(raw); + return Boolean( + value + && !Array.isArray(value) + && value.activityId === JUNGLE_TREASURE_ACTIVITY_ID + && value.configVersion === JUNGLE_TREASURE_CONFIG_VERSION + && Array.isArray(value.tierStates) + && value.tierStates.length === JUNGLE_TREASURE_TOTAL_TIERS + && value.tierStates.every((item: unknown) => toTierState(item) !== null) + && toPositiveTimestamp(value.expiresAt) > 0 + ); +} + +/** 用户表中按 JSON 字符串保存,避免 Laf 数据面板展开 51 项状态数组。 */ +export function serializeJungleState(state: JungleStoredState): string { + return JSON.stringify(state); +} + +export function getJunglePaidTierByProductId(productId: unknown): JungleTierConfig | null { + const normalizedProductId = String(productId || "").trim(); + if (!normalizedProductId) { + return null; + } + return JUNGLE_TREASURE_CONFIG.tiers.find( + (tier) => tier.isPaid && tier.productId === normalizedProductId, + ) || null; +} + +export function unlockJungleTierByProductId( + raw: unknown, + productId: unknown, + outTradeNo = "", + now = Date.now(), +): JungleUnlockResult { + const tier = getJunglePaidTierByProductId(productId); + if (!tier) { + return { ok: false, changed: false, message: "未找到对应的 Jungle 付费档位" }; + } + + // Laf 可能暂时使用共享函数的旧类型缓存,这里按实际返回结构读取新字段。 + const state: any = normalizeStoredJungleState(raw, now); + const stateExpiresAt = Number(state["expiresAt"]); + const tierStates = [...state.tierStates]; + const paidOrderNos = { ...state.paidOrderNos }; + let changed = false; + + if (tierStates[tier.id - 1] === 0) { + tierStates[tier.id - 1] = 1; + changed = true; + } + if (outTradeNo && !paidOrderNos[String(tier.id)]) { + paidOrderNos[String(tier.id)] = outTradeNo; + changed = true; + } + + return { + ok: true, + changed, + tierId: tier.id, + state: changed + ? createStoredState(tierStates, paidOrderNos, state.revision + 1, stateExpiresAt, now) + : state, + }; +} + +/** 按最终需求,不检查前置档位;只检查目标档位自身是否为 1。 */ +export function claimJungleTier(raw: unknown, rawTierId: unknown, now = Date.now()): JungleClaimResult { + const tierId = Number(rawTierId); + if (!Number.isInteger(tierId) || tierId < 1 || tierId > JUNGLE_TREASURE_TOTAL_TIERS) { + return { ok: false, message: "礼包档位编号不合法" }; + } + + const state: any = normalizeStoredJungleState(raw, now); + const stateExpiresAt = Number(state["expiresAt"]); + const currentState = state.tierStates[tierId - 1]; + if (currentState === 0) { + return { ok: false, tierId, message: "该付费档位尚未解锁" }; + } + if (currentState === 2) { + return { ok: false, tierId, message: "该档位已经领取" }; + } + + const tierStates = [...state.tierStates]; + tierStates[tierId - 1] = 2; + return { + ok: true, + tierId, + paidOrderNo: state.paidOrderNos[String(tierId)] || "", + state: createStoredState(tierStates, state.paidOrderNos, state.revision + 1, stateExpiresAt, now), + }; +} + +function createTier( + id: number, + price: number, + rewards: JungleRewardConfig[], + productId = "", + priceTemporary = false, +): JungleTierConfig { + const isPaid = price > 0; + if (isPaid && !productId) { + throw new Error(`[JungleTreasureConfig] 第 ${id} 档为付费档,必须配置 productId`); + } + if (!rewards.length || rewards.length > 5) { + throw new Error(`[JungleTreasureConfig] 第 ${id} 档奖励数量必须为 1 至 5`); + } + return { + id, + price, + isPaid, + productId: isPaid ? productId : "", + ...(priceTemporary ? { priceTemporary: true } : {}), + rewards, + }; +} + +function validateTierOrder(tiers: JungleTierConfig[]): void { + if (tiers.length !== JUNGLE_TREASURE_TOTAL_TIERS) { + throw new Error(`[JungleTreasureConfig] 应配置 ${JUNGLE_TREASURE_TOTAL_TIERS} 档,实际为 ${tiers.length} 档`); + } + tiers.forEach((tier, index) => { + if (tier.id !== index + 1) { + throw new Error(`[JungleTreasureConfig] 第 ${index + 1} 个配置的 id 必须为 ${index + 1}`); + } + }); +} + +function createStoredState( + tierStates: JungleTierState[], + paidOrderNos: Record, + revision: number, + expiresAt: number, + updatedAt: number, +): JungleStoredState { + return { + activityId: JUNGLE_TREASURE_ACTIVITY_ID, + configVersion: JUNGLE_TREASURE_CONFIG_VERSION, + tierStates, + paidOrderNos: { ...paidOrderNos }, + revision, + expiresAt, + updatedAt, + }; +} + +function normalizePaidOrderNos(value: any): Record { + const result: Record = {}; + if (!value || Array.isArray(value) || !value.paidOrderNos || typeof value.paidOrderNos !== "object") { + return result; + } + for (const tier of JUNGLE_TREASURE_CONFIG.tiers) { + if (!tier.isPaid) { + continue; + } + const orderNo = String(value.paidOrderNos[String(tier.id)] || "").trim(); + if (orderNo) { + result[String(tier.id)] = orderNo; + } + } + return result; +} + +function parseUnknownValue(raw: unknown): any { + if (Array.isArray(raw) || (raw && typeof raw === "object")) { + return raw; + } + if (typeof raw !== "string" || !raw.trim()) { + return null; + } + try { + return JSON.parse(raw); + } catch { + return null; + } +} + +function toTierState(value: unknown): JungleTierState | null { + const state = Number(value); + return state === 0 || state === 1 || state === 2 ? state : null; +} + +function toNonNegativeInteger(value: unknown): number { + const numberValue = Number(value); + return Number.isInteger(numberValue) && numberValue >= 0 ? numberValue : 0; +} + +function toPositiveTimestamp(value: unknown): number { + const numberValue = Number(value); + return Number.isFinite(numberValue) && numberValue > 0 ? Math.floor(numberValue) : 0; +} diff --git a/server/laf-cloud/functions/jungleConfig.yaml b/server/laf-cloud/functions/jungleConfig.yaml new file mode 100644 index 00000000..0171b746 --- /dev/null +++ b/server/laf-cloud/functions/jungleConfig.yaml @@ -0,0 +1,7 @@ +name: jungleConfig +desc: "丛林宝藏配置与状态公共模块" +methods: + - GET + - POST +tags: + - activity diff --git a/server/laf-cloud/functions/jungleTreasure.ts b/server/laf-cloud/functions/jungleTreasure.ts new file mode 100644 index 00000000..65dd2109 --- /dev/null +++ b/server/laf-cloud/functions/jungleTreasure.ts @@ -0,0 +1,139 @@ +import cloud from "@lafjs/cloud"; +import Utils from "@/Utils"; +import * as JungleConfigModule from "@/jungleConfig"; + +// Laf 会为共享云函数生成 index 类型声明,该声明可能滞后于实际命名导出。 +const JungleConfig: any = JungleConfigModule as any; + +const db = cloud.database(); +const USER_STATE_FIELD = "jungleTreasureState"; + +/** + * action=read:返回 51 个档位状态及完整配置。 + * action=claim:将指定档位从 1 改为 2,不检查前置档位。 + */ +export default async function jungleTreasure(ctx: FunctionContext) { + const body = ctx.body || {}; + const action = String(body.action || "").toLowerCase(); + const uid = body.uid; + const dbname = body.gameName === "iaa" ? "usersAd" : "users"; + + if (action !== "read" && action !== "claim") { + return fail("无效的操作类型,请使用 read 或 claim"); + } + if (!uid) { + return fail("未获取到uid"); + } + + const userResult = await db.collection(dbname).where({ _id: uid }).getOne(); + const user = userResult.data; + if (!user) { + return fail("未获取到玩家信息"); + } + if (user.token && !Utils.checkToken(body.token, user.token)) { + return fail("token校验失败"); + } + + let storedStateValue: unknown = user[USER_STATE_FIELD]; + let serverState = JungleConfig.normalizeStoredJungleState(storedStateValue); + // 兼容旧对象/数组格式:玩家下次读取时自动改存为 JSON 字符串。 + if (!JungleConfig.isCurrentJungleState(storedStateValue) || typeof storedStateValue !== "string") { + storedStateValue = JungleConfig.serializeJungleState(serverState); + const initialized = await db.collection(dbname).where({ _id: uid }).update({ + [USER_STATE_FIELD]: storedStateValue, + }); + if (isUpdateFailed(initialized)) { + return fail("礼包状态初始化失败"); + } + } + + if (action === "read") { + return readSuccess(serverState); + } + + const claimResult = JungleConfig.claimJungleTier(serverState, body.tierId); + if (!claimResult.ok || !claimResult.state || !claimResult.tierId) { + return fail(claimResult.message || "礼包领取失败"); + } + + // 字符串不能按数组下标查询;比较完整旧值,避免并发请求覆盖彼此或重复领取。 + const updateResult = await db.collection(dbname).where({ + _id: uid, + [USER_STATE_FIELD]: storedStateValue, + }).update({ + [USER_STATE_FIELD]: JungleConfig.serializeJungleState(claimResult.state), + }); + if (isUpdateFailed(updateResult)) { + return fail("礼包状态已变化,请重新读取后再领取"); + } + + // 付费档领取完成后,将支付订单从待发货改为已发货,避免登录补单重复处理。 + if (claimResult.paidOrderNo) { + await db.collection("order").where({ + outTradeNo: claimResult.paidOrderNo, + openid: user.openid, + state: 1, + }).update({ + state: 2, + getTime: new Date(Date.now() + 8 * 3600000), + }); + } + + // 按需求重新读取并返回服务器中的最新状态数组。 + const latestUserResult = await db.collection(dbname).where({ _id: uid }).getOne(); + if (!latestUserResult.data) { + return fail("礼包领取后读取状态失败"); + } + serverState = JungleConfig.normalizeStoredJungleState(latestUserResult.data[USER_STATE_FIELD]); + return claimSuccess(claimResult.tierId, serverState); +} + +function readSuccess(state: any) { + const serverNow = Date.now(); + // Laf 共享函数更新后可能暂时使用旧的 JungleStoredState 类型缓存。 + // 运行时仍由新版 jungleConfig 保证 expiresAt 已存在。 + const stateWithExpiry: any = state; + const expiresAt = stateWithExpiry.expiresAt; + return { + code: 1, + data: { + tierStates: state.tierStates, + revision: state.revision, + expiresAt, + serverNow, + updatedAt: state.updatedAt, + config: { + ...JungleConfig.JUNGLE_TREASURE_CONFIG, + endAt: expiresAt, + }, + }, + msg: "读取成功", + }; +} + +function claimSuccess( + claimedTierId: number, + state: any, +) { + const stateWithExpiry: any = state; + return { + code: 1, + data: { + claimedTierId, + tierStates: state.tierStates, + revision: state.revision, + expiresAt: stateWithExpiry.expiresAt, + serverNow: Date.now(), + updatedAt: state.updatedAt, + }, + msg: "领取成功", + }; +} + +function isUpdateFailed(result: any): boolean { + return Boolean(result && (result.ok === false || result.updated === 0)); +} + +function fail(msg: string) { + return { code: 0, data: null, msg }; +} diff --git a/server/laf-cloud/functions/jungleTreasure.yaml b/server/laf-cloud/functions/jungleTreasure.yaml new file mode 100644 index 00000000..e6b5a0a9 --- /dev/null +++ b/server/laf-cloud/functions/jungleTreasure.yaml @@ -0,0 +1,7 @@ +name: jungleTreasure +desc: "丛林宝藏礼包配置与用户进度读写" +methods: + - GET + - POST +tags: + - activity diff --git a/server/laf-cloud/functions/login.ts b/server/laf-cloud/functions/login.ts index d62f6717..60406bd1 100644 --- a/server/laf-cloud/functions/login.ts +++ b/server/laf-cloud/functions/login.ts @@ -1,5 +1,7 @@ import cloud from '@lafjs/cloud' import { createHash } from 'crypto'; +import * as JungleConfigModule from "@/jungleConfig"; +const JungleConfig: any = JungleConfigModule as any; const db = cloud.database(); import crypto from 'crypto'; const mchid = "1719895437";//商户id @@ -188,6 +190,10 @@ export default async function (ctx: FunctionContext) { } userData.canIos = canIos; userData.version = version; + if (Object.prototype.hasOwnProperty.call(userData, "jungleTreasureState")) { + // 仅随登录返回服务器时间,不在登录阶段创建或修改活动状态。 + userData.jungleTreasureServerNow = Date.now(); + } return { code: 1, data: userData } } function stringToHash(input: string, algorithm: 'sha1' | 'sha256' | 'sha512' = 'sha256'): string { @@ -239,7 +245,15 @@ const propName = { "局内购买冻结": "freeze_in_game", "局内购买锤子": "hammer_in_game", "局内购买魔棒": "wand_in_gamerefill", - "补满体力": "health" + "补满体力": "health", + "宝藏1": "jungle_treasure_1", + "宝藏2": "jungle_treasure_2", + "宝藏3": "jungle_treasure_3", + "宝藏4": "jungle_treasure_4", + "宝藏5": "jungle_treasure_5", + "宝藏6": "jungle_treasure_6", + "宝藏7": "jungle_treasure_7", + "宝藏8": "jungle_treasure_8" }; function getTodayStartTimestamp() { @@ -301,7 +315,20 @@ async function getIosOrder(openid, ctx) { if (ret.trade_state == "SUCCESS") { list.push(res.data[i]); sead(res.data[i], res1.data, getClientIp(ctx)); - switch (res.data[i].itemid) { + const jungleTier = JungleConfig.getJunglePaidTierByProductId(res.data[i].itemid); + if (jungleTier) { + const unlockResult = JungleConfig.unlockJungleTierByProductId( + res1.data.jungleTreasureState, + res.data[i].itemid, + res.data[i].outTradeNo, + ); + const userUpdate: any = { pay_user: true }; + if (unlockResult.ok && unlockResult.changed && unlockResult.state) { + userUpdate.jungleTreasureState = JungleConfig.serializeJungleState(unlockResult.state); + res1.data.jungleTreasureState = unlockResult.state; + } + await db.collection("users").where({ _id: res1.data._id }).update(userUpdate); + } else switch (res.data[i].itemid) { case "month_Card": const latestUser = await db.collection("users").where({ _id: res1.data._id }).getOne(); const now = Date.now(); @@ -322,7 +349,7 @@ async function getIosOrder(openid, ctx) { break; } // 月卡补单确认成功后进入待客户端补发状态,避免下次登录重复累计。 - if (res.data[i].itemid === "month_Card") { + if (res.data[i].itemid === "month_Card" || jungleTier) { await db.collection("order").where({ outTradeNo: res.data[i].outTradeNo }).update({ state: 1 }); } } else { diff --git a/server/laf-cloud/functions/wx/KeFuInfo.ts b/server/laf-cloud/functions/wx/KeFuInfo.ts index 321e71f4..e01b708b 100644 --- a/server/laf-cloud/functions/wx/KeFuInfo.ts +++ b/server/laf-cloud/functions/wx/KeFuInfo.ts @@ -111,7 +111,15 @@ const propName = { freeze_in_game: "局内购买冻结", hammer_in_game: "局内购买锤子", wand_in_gamerefill: "局内购买魔棒", - health: "补满体力" + health: "补满体力", + jungle_treasure_1: "宝藏1", + jungle_treasure_2: "宝藏2", + jungle_treasure_3: "宝藏3", + jungle_treasure_4: "宝藏4", + jungle_treasure_5: "宝藏5", + jungle_treasure_6: "宝藏6", + jungle_treasure_7: "宝藏7", + jungle_treasure_8: "宝藏8" }; async function PostMsg(OpenId, msg) { diff --git a/server/laf-cloud/functions/wx/checkIos.ts b/server/laf-cloud/functions/wx/checkIos.ts index 9501fe0a..0c16950e 100644 --- a/server/laf-cloud/functions/wx/checkIos.ts +++ b/server/laf-cloud/functions/wx/checkIos.ts @@ -1,15 +1,16 @@ import cloud from '@lafjs/cloud' const db = cloud.database(); export default async function (ctx: FunctionContext) { - let openid=ctx.body.openid; - let time=ctx.body.time; + console.log("检查提醒"); + let openid = ctx.body.openid; + let time = ctx.body.time; let outTradeNo = ctx.body.outTradeNo; - if(!openid||!time){ + if (!openid || !time) { return false; } - let user = await db.collection("users").where({ openid:openid}).getOne(); - if(user.data){ - if (user.data.iosTime ==time){ + let user = await db.collection("users").where({ openid: openid }).getOne(); + if (user.data) { + if (user.data.iosTime == time) { let res = await db.collection("iosOrder").where({ outTradeNo: outTradeNo }).getOne(); if (res.data) { await db.collection("order").add({ @@ -19,6 +20,9 @@ export default async function (ctx: FunctionContext) { itemCount: res.data.itemCount, goodsPrice: res.data.goodsPrice, time: res.data.time, + orderTime: res.data.time, + chargeTime: 0, + getTime: 0, type: "ios", state: 0, }); @@ -26,7 +30,7 @@ export default async function (ctx: FunctionContext) { return true; } else { let res = await db.collection("order").where({ outTradeNo: outTradeNo }).getOne(); - if(res.data){ + if (res.data) { console.log("验证成功"); return true; } diff --git a/server/laf-cloud/functions/wx/getOrderReward.ts b/server/laf-cloud/functions/wx/getOrderReward.ts index 659d819c..6ee8cd6c 100644 --- a/server/laf-cloud/functions/wx/getOrderReward.ts +++ b/server/laf-cloud/functions/wx/getOrderReward.ts @@ -2,9 +2,9 @@ import cloud from '@lafjs/cloud' const db = cloud.database(); export default async function (ctx: FunctionContext) { let outTradeNo = ctx.body.outTradeNo; - if(!outTradeNo){ - return { code: 0, data:null, msg: "未获取到outTradeNo" }; + if (!outTradeNo) { + return { code: 0, data: null, msg: "未获取到outTradeNo" }; } - let res=await db.collection("order").where({ outTradeNo: outTradeNo }).update({ state: 2 }); - return { code:1, data:"ok", msg: "成功" }; + let res = await db.collection("order").where({ outTradeNo: outTradeNo }).update({ state: 2, getTime: new Date(Date.now() + 8 * 3600000) }); + return { code: 1, data: "ok", msg: "成功" }; } diff --git a/server/laf-cloud/functions/wx/getPayInfo.ts b/server/laf-cloud/functions/wx/getPayInfo.ts index 93f10740..cf91f12b 100644 --- a/server/laf-cloud/functions/wx/getPayInfo.ts +++ b/server/laf-cloud/functions/wx/getPayInfo.ts @@ -10,6 +10,12 @@ export default async function (ctx: FunctionContext) { return { code: 0, data: null, msg: "未获取到玩家数据" }; } let res = await db.collection("order").where({ outTradeNo: outTradeNo }).getOne(); + if (!res.data) { + return { code: 0, data: null, msg: "未获取到充值订单" }; + } + if (res.data.openid !== user.data.openid) { + return { code: 0, data: null, msg: "充值订单不属于当前玩家" }; + } if (res.data.state == 1) { return { code: 1, data: { @@ -17,6 +23,15 @@ export default async function (ctx: FunctionContext) { }, msg: "成功" }; } else if (res.data.state == 2) { + // Jungle 档位解锁成功后会直接完成订单;前端仍需把它识别为本次支付成功, + // 随后读取 jungleTreasure 状态并把付费按钮刷新为“领取”。 + if (/^jungle_treasure_[1-8]$/.test(String(res.data.itemid || ""))) { + return { + code: 1, data: { + pay_state: 2 + }, msg: "成功" + }; + } return { code: 1, data: { pay_state: 1, @@ -45,6 +60,13 @@ const propName = { "局内购买魔棒": "wand_in_gamerefill", "补满体力": "health", "新手礼包": "starter_pack", + "宝藏1": "jungle_treasure_1", + "宝藏2": "jungle_treasure_2", + "宝藏3": "jungle_treasure_3", + "宝藏4": "jungle_treasure_4", + "宝藏5": "jungle_treasure_5", + "宝藏6": "jungle_treasure_6", + "宝藏7": "jungle_treasure_8" }; function hmacSha256(sessionKey, signData) { const hmac = crypto.createHmac('sha256', sessionKey); diff --git a/server/laf-cloud/functions/wx/iosPayCall.ts b/server/laf-cloud/functions/wx/iosPayCall.ts index 2e5d02eb..42c9753d 100644 --- a/server/laf-cloud/functions/wx/iosPayCall.ts +++ b/server/laf-cloud/functions/wx/iosPayCall.ts @@ -1,5 +1,5 @@ import cloud from '@lafjs/cloud' export default async function (ctx: FunctionContext) { - return { data:'scuess' } + return { data: 'scuess' } } diff --git a/server/laf-cloud/functions/wx/iosgetPayInfo.ts b/server/laf-cloud/functions/wx/iosgetPayInfo.ts index 66561854..0fcf8f5b 100644 --- a/server/laf-cloud/functions/wx/iosgetPayInfo.ts +++ b/server/laf-cloud/functions/wx/iosgetPayInfo.ts @@ -1,5 +1,7 @@ import cloud from '@lafjs/cloud' import crypto from 'crypto'; +import * as JungleConfigModule from "@/jungleConfig"; +const JungleConfig: any = JungleConfigModule as any; const mchid = "1719895437";//商户id const serialNo = "2A97ED6ED441E55600EBC8830B9EC889ED41496E";//证书序列号 const db = cloud.database(); @@ -24,8 +26,13 @@ export default async function (ctx: FunctionContext) { } let payment_name = ret.data?.itemid; if (res) { + let paidUser: any = null; if (uid) { let res1 = await db.collection("users").where({ _id: uid }).getOne(); + if (!res1.data) { + return { code: 0, data: null, msg: "未获取到玩家数据" }; + } + paidUser = res1.data; if (!iosOrder) { iosOrder = {}; } else { @@ -38,6 +45,7 @@ export default async function (ctx: FunctionContext) { } cloud.shared.set("iosOrder", iosOrder); // 设置 wsMap + // 保留原有充值处理流程,Jungle 解锁逻辑在后面独立追加。 switch (payment_name) { case "month_Card": let monthCardTime = res1.data.monthCardTime; @@ -58,7 +66,30 @@ export default async function (ctx: FunctionContext) { break; } } + // 保持原有订单完成逻辑不变,所有商品仍然正常进入 state=2。 await db.collection("order").where({ outTradeNo: outTradeNo }).update({ state: 2, chargeTime: new Date(Date.now() + 8 * 3600000), getTime: new Date(Date.now() + 8 * 3600000) }); + + + // Jungle 属于附加逻辑;异常只记录日志,不影响上面的原充值流程和成功返回。 + if (paidUser) { + try { + const jungleTier = JungleConfig.getJunglePaidTierByProductId(payment_name); + if (jungleTier) { + const unlockResult = JungleConfig.unlockJungleTierByProductId( + paidUser.jungleTreasureState, + payment_name, + outTradeNo, + ); + if (unlockResult.ok && unlockResult.changed && unlockResult.state) { + await db.collection("users").where({ _id: uid }).update({ + jungleTreasureState: JungleConfig.serializeJungleState(unlockResult.state), + }); + } + } + } catch (error) { + console.error("ios Jungle 付费档位解锁失败,不影响原充值结果", error); + } + } return { code: 1, data: { goodsPrice: ret.data?.goodsPrice, payment_name: payment_name }, msg: "充值成功" }; } else { console.log("充值不成功:" + outTradeNo); diff --git a/server/laf-cloud/functions/wx/iosorderPaySig.ts b/server/laf-cloud/functions/wx/iosorderPaySig.ts new file mode 100644 index 00000000..cda264c3 --- /dev/null +++ b/server/laf-cloud/functions/wx/iosorderPaySig.ts @@ -0,0 +1,66 @@ +import cloud from '@lafjs/cloud' +import * as crypto from 'crypto'; +import * as JungleConfigModule from "@/jungleConfig"; +const JungleConfig: any = JungleConfigModule as any; +const db = cloud.database(); +export default async function (ctx: FunctionContext) { + let uid = ctx.body.uid; + let itemid = ctx.body.itemid; + let itemCount = ctx.body.itemCount; + let itemPrice = ctx.body.itemPrice; + let user = await db.collection("users").where({ _id: uid }).getOne(); + if (user.data == null) { + return { code: 0, data: null, msg: "未获取到玩家数据" }; + } + const jungleTier = JungleConfig.getJunglePaidTierByProductId(itemid); + if (jungleTier) { + const state = JungleConfig.normalizeStoredJungleState(user.data.jungleTreasureState); + if (state.tierStates[jungleTier.id - 1] !== 0) { + return { code: 0, data: null, msg: "该 Jungle 付费档位已经解锁或领取" }; + } + itemCount = 1; + itemPrice = Math.round(jungleTier.price * 100); + } + const outTradeNo = `wcx_` + Math.round(Math.random() * 10 ** 13) + Date.now(); + let signData = JSON.stringify({ + "mode": "goods", + "offerId": "1450527458", + "buyQuantity": Number(itemCount),//购买数量 + "env": 0,//正式环境 + "currencyType": "CNY",//人民币 + "platform": "android", + "zoneId": "1",//分区id + "productId": itemid, + "goodsPrice": Number(itemPrice), + "outTradeNo": outTradeNo, + "attach": "购买道具" + }); + let session_key = user.data.session_key; + const signature = hmacSha256(session_key, signData); + let key = "GmsJF53hOi2CVkEeSg2Wlst9V9RDINKG"; + let data = 'requestMidasPaymentGameItem' + '&' + signData; + const paySig = hmacSha256(key, data); + await db.collection("order").add({ + openid: user.data.openid, + outTradeNo: outTradeNo, + itemid: itemid, + itemCount: itemCount, + goodsPrice: Number(itemPrice), + time: Date.now(),//申请订单时间 + orderTime: new Date(Date.now() + 8 * 3600000), + chargeTime: 0, + getTime: 0, + type: "ios", + state: 0, + ...(jungleTier ? { jungleTierId: jungleTier.id } : {}), + }); + console.log("申请订单"); + console.log(outTradeNo); + // 计算哈希值并转换为十六进制字符串 + return { code: 1, data: { paySig: paySig, signature: signature, signData: signData, outTradeNo: outTradeNo }, msg: "成功" } +} +function hmacSha256(sessionKey, signData) { + const hmac = crypto.createHmac('sha256', sessionKey); + hmac.update(signData); + return hmac.digest('hex'); +} diff --git a/server/laf-cloud/functions/wx/orderPaySig.ts b/server/laf-cloud/functions/wx/orderPaySig.ts index 14bc19d0..f41f33bb 100644 --- a/server/laf-cloud/functions/wx/orderPaySig.ts +++ b/server/laf-cloud/functions/wx/orderPaySig.ts @@ -1,5 +1,7 @@ import cloud from '@lafjs/cloud' import * as crypto from 'crypto'; +import * as JungleConfigModule from "@/jungleConfig"; +const JungleConfig: any = JungleConfigModule as any; const db = cloud.database(); export default async function (ctx: FunctionContext) { let uid = ctx.body.uid; @@ -10,10 +12,19 @@ export default async function (ctx: FunctionContext) { if (user.data == null) { return { code: 0, data: null, msg: "未获取到玩家数据" }; } + const jungleTier = JungleConfig.getJunglePaidTierByProductId(itemid); + if (jungleTier) { + const state = JungleConfig.normalizeStoredJungleState(user.data.jungleTreasureState); + if (state.tierStates[jungleTier.id - 1] !== 0) { + return { code: 0, data: null, msg: "该 Jungle 付费档位已经解锁或领取" }; + } + itemCount = 1; + itemPrice = Math.round(jungleTier.price * 100); + } const outTradeNo = `wcx_` + Math.round(Math.random() * 10 ** 13) + Date.now(); let signData = JSON.stringify({ "mode": "goods", - "offerId": "1450390549", + "offerId": "1450527458", "buyQuantity": Number(itemCount),//购买数量 "env": 0,//正式环境 "currencyType": "CNY",//人民币 @@ -26,7 +37,7 @@ export default async function (ctx: FunctionContext) { }); let session_key = user.data.session_key; const signature = hmacSha256(session_key, signData); - let key = "4HfZwqLjVkLb6s4TfIxRGKaTxgvvL1rl"; + let key = "GmsJF53hOi2CVkEeSg2Wlst9V9RDINKG"; let data = 'requestMidasPaymentGameItem' + '&' + signData; const paySig = hmacSha256(key, data); await db.collection("order").add({ @@ -35,9 +46,13 @@ export default async function (ctx: FunctionContext) { itemid: itemid, itemCount: itemCount, goodsPrice: Number(itemPrice), - time: Date.now(), - type:"and", + time: Date.now(),//申请订单时间 + orderTime: new Date(Date.now() + 8 * 3600000), + chargeTime: 0, + getTime: 0, + type: "and", state: 0, + ...(jungleTier ? { jungleTierId: jungleTier.id } : {}), }); // console.log("申请订单"); // console.log(outTradeNo); @@ -48,4 +63,4 @@ function hmacSha256(sessionKey, signData) { const hmac = crypto.createHmac('sha256', sessionKey); hmac.update(signData); return hmac.digest('hex'); -} \ No newline at end of file +} diff --git a/server/laf-cloud/functions/wx/payCallBack.ts b/server/laf-cloud/functions/wx/payCallBack.ts index 117ed4b4..79b640b4 100644 --- a/server/laf-cloud/functions/wx/payCallBack.ts +++ b/server/laf-cloud/functions/wx/payCallBack.ts @@ -1,26 +1,90 @@ import cloud from '@lafjs/cloud'; +import * as JungleConfigModule from "@/jungleConfig"; +const JungleConfig: any = JungleConfigModule as any; const db = cloud.database(); const ThinkingData = require('thinkingdata-node'); export default async function (ctx: FunctionContext) { - let data = JSON.parse(ctx.body.MiniGame.Payload); - let OutTradeNo = data.OutTradeNo; - let res = await db.collection("order").where({ outTradeNo: OutTradeNo }).getOne(); - if (res.data) { - if (res.data.state != 2) { - await db.collection("order").where({ outTradeNo: OutTradeNo }).update({ state: 1, chargeTime: new Date(Date.now() + 8 * 3600000) }); - let user = await db.collection("users").where({ openid: res.data.openid }).getOne(); - sead(res.data, user.data, getClientIp(ctx)); - switch (res.data.itemid) { - case "starter_pack": - db.collection("users").where({ _id: user.data._id }).update({ starter_packState: 1, pay_user: true }); - break; - } - if (!user.data.pay_user) { - db.collection("users").where({ _id: user.data._id }).update({ pay_user: true }); - } + let data: any; + try { + data = JSON.parse(ctx.body?.MiniGame?.Payload || ""); + } catch { + console.error("微信发货回调 Payload 解析失败"); + return { "ErrCode": -1, "ErrMsg": "Invalid Payload" }; + } + + const outTradeNo = String(data.OutTradeNo || ""); + const orderResult = await db.collection("order").where({ outTradeNo }).getOne(); + const order = orderResult.data; + if (!order) { + console.error("微信发货回调未找到订单:" + outTradeNo); + return { "ErrCode": -1, "ErrMsg": "Order Not Found" }; + } + + const jungleTier = JungleConfig.getJunglePaidTierByProductId(order.itemid); + if (jungleTier) { + const callbackProductId = String(data.GoodsInfo?.ProductId || ""); + const callbackQuantity = Number(data.GoodsInfo?.Quantity); + const callbackOriginalPrice = Number(data.GoodsInfo?.OrigPrice); + if ((data.OpenId && data.OpenId !== order.openid) + || (callbackProductId && callbackProductId !== order.itemid) + || (Number.isFinite(callbackQuantity) && callbackQuantity !== Number(order.itemCount)) + || (Number.isFinite(callbackOriginalPrice) && callbackOriginalPrice !== Number(order.goodsPrice))) { + console.error("Jungle 支付回调与订单信息不一致:" + outTradeNo); + return { "ErrCode": -1, "ErrMsg": "Order Mismatch" }; } } + const userResult = await db.collection("users").where({ openid: order.openid }).getOne(); + const user = userResult.data; + if (!user) { + console.error("微信发货回调未找到玩家:" + order.openid); + return { "ErrCode": -1, "ErrMsg": "User Not Found" }; + } + + const firstPaidCallback = order.state === 0; + if (order.state !== 2) { + await db.collection("order").where({ outTradeNo }).update({ + state: 1, + chargeTime: new Date(Date.now() + 8 * 3600000), + }); + } + + const userUpdate: any = {}; + let jungleUnlockedFromLocked = false; + if (jungleTier) { + const storedState = JungleConfig.normalizeStoredJungleState(user.jungleTreasureState); + const wasLocked = Number(storedState?.tierStates?.[jungleTier.id - 1]) === 0; + const unlockResult = JungleConfig.unlockJungleTierByProductId( + user.jungleTreasureState, + order.itemid, + outTradeNo, + ); + if (unlockResult.ok && unlockResult.changed && unlockResult.state) { + userUpdate.jungleTreasureState = JungleConfig.serializeJungleState(unlockResult.state); + jungleUnlockedFromLocked = wasLocked + && Number(unlockResult.state.tierStates?.[jungleTier.id - 1]) === 1; + } + } + + if (order.itemid === "starter_pack") { + userUpdate.starter_packState = 1; + } + if (!user.pay_user) { + userUpdate.pay_user = true; + } + if (Object.keys(userUpdate).length > 0) { + await db.collection("users").where({ _id: user._id }).update(userUpdate); + } + if (firstPaidCallback) { + await sead(order, user, getClientIp(ctx)); + } + if (jungleTier && jungleUnlockedFromLocked) { + await db.collection("order").where({ outTradeNo }).update({ + state: 2, + getTime: new Date(Date.now() + 8 * 3600000), + }); + } + // let query={ // signature: '1c1b5c1c72058436e2f1214e6aeafd258e50acd4', // timestamp: '1751291889', diff --git a/server/laf-cloud/jungleTreasure.README.md b/server/laf-cloud/jungleTreasure.README.md new file mode 100644 index 00000000..ce709ff6 --- /dev/null +++ b/server/laf-cloud/jungleTreasure.README.md @@ -0,0 +1,52 @@ +# Jungle Treasure 后端模块 + +Laf 云函数采用单模块结构: + +- `functions/jungleTreasure.ts`:礼包配置、类型、状态工具、客户端读取和领取接口; +- `functions/jungleTreasure.yaml`:对应的 Laf 云函数定义。 + +其他云函数统一通过下面的方式引用公共模块: + +```ts +import { + getJunglePaidTierByProductId, + unlockJungleTierByProductId, +} from "@/jungleTreasure"; +``` + +不再保留独立的 `jungleConfig`、`jungleState` 和 `types` 模块。 + +## 状态规则 + +- `0`:付费档位未解锁; +- `1`:已解锁未领取,免费档位默认是 `1`; +- `2`:已经领取。 + +状态数组固定为 51 项。领取接口不检查前置档位,只检查目标档位自身是否为 `1`。 + +付费商品依次对应第 `4/10/16/22/28/34/40/46` 档。支付回调根据 `jungleTreasure.ts` 中的 `productId` 查找档位,不在支付代码中重复维护映射。 + +## 接口 + +读取: + +```json +{ + "action": "read", + "uid": "用户 _id", + "token": "登录 token" +} +``` + +领取: + +```json +{ + "action": "claim", + "uid": "用户 _id", + "token": "登录 token", + "tierId": 5 +} +``` + +原来的 `save` 操作已停用。旧版 `claimedTierIds` 数据会在首次读取时自动迁移为三状态数组。 diff --git a/server/laf-cloud/tests/jungle-treasure.test.mjs b/server/laf-cloud/tests/jungle-treasure.test.mjs new file mode 100644 index 00000000..48e544b5 --- /dev/null +++ b/server/laf-cloud/tests/jungle-treasure.test.mjs @@ -0,0 +1,389 @@ +import test from "node:test"; +import assert from "node:assert/strict"; +import { existsSync } from "node:fs"; +import { readFile } from "node:fs/promises"; +import { registerHooks } from "node:module"; + +const functionUrl = new URL("../functions/jungleTreasure.ts", import.meta.url); +const yamlUrl = new URL("../functions/jungleTreasure.yaml", import.meta.url); +const configUrl = new URL("../functions/jungleConfig.ts", import.meta.url); +const configYamlUrl = new URL("../functions/jungleConfig.yaml", import.meta.url); +const payCallbackUrl = new URL("../functions/wx/payCallBack.ts", import.meta.url); +const orderPaySigUrl = new URL("../functions/wx/orderPaySig.ts", import.meta.url); + +const databaseState = { + user: null, + orders: [], + collections: [], + updates: [], + reads: 0, +}; + +globalThis.__jungleCloudMock = { + database() { + return { + collection(name) { + databaseState.collections.push(name); + return { + async add(payload) { + databaseState.updates.push({ collection: name, query: null, payload }); + if (name === "order") { + databaseState.orders.push(payload); + } + return { ok: true }; + }, + where(query) { + return { + async getOne() { + databaseState.reads++; + if (name === "order") { + return { data: databaseState.orders.find((order) => matches(order, query)) || null }; + } + return { data: databaseState.user }; + }, + async update(payload) { + databaseState.updates.push({ collection: name, query, payload }); + if (name === "order") { + const orders = databaseState.orders.filter((order) => matches(order, query)); + orders.forEach((order) => Object.assign(order, payload)); + return { ok: true, updated: orders.length }; + } + if (!databaseState.user || !matches(databaseState.user, query)) { + return { ok: true, updated: 0 }; + } + Object.assign(databaseState.user, payload); + return { ok: true, updated: 1 }; + }, + }; + }, + }; + }, + }; + }, +}; + +globalThis.__jungleUtilsMock = { + checkToken(received, expected) { + return received === expected; + }, +}; + +globalThis.require = () => ({ + initWithBatchMode() { + return { track() {}, close() {} }; + }, +}); + +registerHooks({ + resolve(specifier, context, nextResolve) { + const aliases = { + "@/Utils": "data:text/javascript,export default globalThis.__jungleUtilsMock", + "@/jungleConfig": configUrl.href, + }; + if (specifier === "@lafjs/cloud") { + return { + url: "data:text/javascript,export default globalThis.__jungleCloudMock", + shortCircuit: true, + }; + } + if (aliases[specifier]) { + return { url: aliases[specifier], shortCircuit: true }; + } + return nextResolve(specifier, context); + }, +}); + +const jungleModule = await import(functionUrl); +const configModule = await import(configUrl); +const { JUNGLE_TREASURE_CONFIG } = configModule; +const { + claimJungleTier, + createDefaultJungleState, + unlockJungleTierByProductId, +} = configModule; +const { default: jungleTreasure } = jungleModule; +const { default: payCallback } = await import(payCallbackUrl); +const { default: orderPaySig } = await import(orderPaySigUrl); + +function reset(user, orders = []) { + databaseState.user = user; + databaseState.orders = orders; + databaseState.collections.length = 0; + databaseState.updates.length = 0; + databaseState.reads = 0; +} + +function getStoredJungleState() { + assert.equal(typeof databaseState.user.jungleTreasureState, "string"); + return JSON.parse(databaseState.user.jungleTreasureState); +} + +function matches(target, query) { + return Object.entries(query || {}).every(([key, expected]) => getByPath(target, key) === expected); +} + +function getByPath(target, path) { + return path.split(".").reduce((value, key) => value == null ? undefined : value[key], target); +} + +test("cloud function and yaml configuration exist", async () => { + assert.equal(existsSync(functionUrl), true); + assert.equal(existsSync(yamlUrl), true); + assert.equal(existsSync(configUrl), true); + assert.equal(existsSync(configYamlUrl), true); + const yaml = await readFile(yamlUrl, "utf8"); + const configYaml = await readFile(configYamlUrl, "utf8"); + assert.match(yaml, /^name:\s*jungleTreasure$/m); + assert.match(configYaml, /^name:\s*jungleConfig$/m); +}); + +test("configuration contains 51 tiers and all eight product mappings", () => { + assert.equal(JUNGLE_TREASURE_CONFIG.totalTiers, 51); + assert.equal(JUNGLE_TREASURE_CONFIG.tiers.length, 51); + assert.deepEqual( + JUNGLE_TREASURE_CONFIG.tiers.filter((tier) => tier.isPaid).map((tier) => tier.id), + [4, 10, 16, 22, 28, 34, 40, 46], + ); + assert.deepEqual( + JUNGLE_TREASURE_CONFIG.tiers.filter((tier) => tier.isPaid).map((tier) => tier.productId), + Array.from({ length: 8 }, (_, index) => `jungle_treasure_${index + 1}`), + ); + assert.deepEqual( + JUNGLE_TREASURE_CONFIG.tiers.filter((tier) => tier.isPaid).map((tier) => tier.price), + [3, 6, 9, 15, 24, 42, 68, 98], + ); +}); + +test("configuration rewards match the 51-tier planning table", () => { + const actual = JUNGLE_TREASURE_CONFIG.tiers.map((tier) => + `${tier.id}|${tier.price}|${tier.rewards.map((reward) => `${reward.type}:${reward.count}`).join(",")}` + ); + assert.deepEqual(actual, [ + "1|0|hammer:1", + "2|0|coin:500", + "3|0|freeze:1,magic_wand:1", + "4|3|coin:1000,hammer:1", + "5|0|coin:500,magic_wand:1", + "6|0|freeze:1", + "7|0|freeze:1", + "8|0|magic_wand:1", + "9|0|magic_wand:1", + "10|6|coin:2000,hammer:2,magic_wand:1", + "11|0|coin:500,hammer:1,freeze:1", + "12|0|coin:500", + "13|0|freeze:1", + "14|0|freeze:1,magic_wand:1", + "15|0|magic_wand:1", + "16|9|coin:3000,hammer:2,magic_wand:2", + "17|0|coin:1000,hammer:1,magic_wand:1", + "18|0|coin:500", + "19|0|hammer:1", + "20|0|freeze:2", + "21|0|magic_wand:1,infinite_health:900", + "22|15|coin:4000,hammer:3,magic_wand:2", + "23|0|coin:1500,hammer:1,magic_wand:2", + "24|0|coin:1000", + "25|0|hammer:1,freeze:1", + "26|0|freeze:2,magic_wand:1", + "27|0|hammer:1,freeze:2,magic_wand:1", + "28|24|coin:6500,hammer:5,magic_wand:3", + "29|0|coin:2000,hammer:1,magic_wand:3", + "30|0|coin:1500,infinite_health:3600", + "31|0|hammer:1,freeze:2", + "32|0|freeze:3,magic_wand:2", + "33|0|hammer:1,freeze:3,magic_wand:2", + "34|42|coin:10000,hammer:8,magic_wand:6", + "35|0|coin:3500,hammer:3,magic_wand:4", + "36|0|coin:3000", + "37|0|hammer:2,freeze:2", + "38|0|freeze:4,magic_wand:4", + "39|0|hammer:1,freeze:4,magic_wand:6", + "40|68|coin:18000,hammer:12,magic_wand:10", + "41|0|coin:6000,hammer:4,magic_wand:8", + "42|0|coin:4000,infinite_health:7200", + "43|0|hammer:2,freeze:5", + "44|0|freeze:7,magic_wand:5", + "45|0|hammer:2,freeze:8,magic_wand:7", + "46|98|coin:35000,hammer:18,magic_wand:20", + "47|0|coin:12000,hammer:6,magic_wand:14", + "48|0|coin:8000,infinite_health:10800", + "49|0|hammer:3,freeze:8", + "50|0|freeze:12,magic_wand:8", + "51|0|hammer:3,freeze:15,magic_wand:12", + ]); +}); + +test("default state is 1 for free tiers and 0 for paid tiers", () => { + const state = createDefaultJungleState(100); + assert.equal(state.tierStates.length, 51); + assert.equal(state.expiresAt, 100 + 7 * 24 * 60 * 60 * 1000); + assert.deepEqual(state.tierStates.slice(0, 10), [1, 1, 1, 0, 1, 1, 1, 1, 1, 0]); + assert.deepEqual( + state.tierStates.map((value, index) => value === 0 ? index + 1 : null).filter(Boolean), + [4, 10, 16, 22, 28, 34, 40, 46], + ); +}); + +test("read initializes storage and returns the state array with configuration", async () => { + reset({ _id: "u1", openid: "o1", token: "secret" }); + const result = await jungleTreasure({ + body: { action: "read", uid: "u1", token: "secret" }, + }); + + assert.equal(result.code, 1); + assert.equal(databaseState.collections[0], "users"); + assert.equal(result.data.config.tiers.length, 51); + assert.equal(result.data.config.version, 2); + assert.equal(result.data.config.endAt, result.data.expiresAt); + assert.equal(typeof result.data.serverNow, "number"); + assert.deepEqual(result.data.tierStates.slice(0, 10), [1, 1, 1, 0, 1, 1, 1, 1, 1, 0]); + const storedState = getStoredJungleState(); + assert.equal(storedState.tierStates.length, 51); + assert.equal(storedState.expiresAt, result.data.expiresAt); +}); + +test("claim does not require earlier tiers to be claimed", async () => { + reset({ _id: "u1", openid: "o1", token: "secret" }); + const result = await jungleTreasure({ + body: { action: "claim", uid: "u1", token: "secret", tierId: 5 }, + }); + + assert.equal(result.code, 1); + assert.equal(result.data.claimedTierId, 5); + assert.equal(result.data.tierStates[4], 2); + assert.equal(result.data.tierStates[0], 1); + assert.equal(result.data.tierStates[3], 0); +}); + +test("a locked paid tier cannot be claimed", async () => { + reset({ _id: "u1", openid: "o1", token: "secret" }); + const result = await jungleTreasure({ + body: { action: "claim", uid: "u1", token: "secret", tierId: 4 }, + }); + + assert.equal(result.code, 0); + assert.match(result.msg, /尚未解锁/); +}); + +test("all product IDs unlock their configured paid tier and remember the order", () => { + let state = createDefaultJungleState(100); + const paidTierIds = [4, 10, 16, 22, 28, 34, 40, 46]; + + paidTierIds.forEach((tierId, index) => { + const productId = `jungle_treasure_${index + 1}`; + const result = unlockJungleTierByProductId(state, productId, `order_${index + 1}`, 200 + index); + assert.equal(result.ok, true); + assert.equal(result.tierId, tierId); + assert.equal(result.state.tierStates[tierId - 1], 1); + assert.equal(result.state.paidOrderNos[String(tierId)], `order_${index + 1}`); + state = result.state; + }); +}); + +test("WeChat paid callback unlocks the tier matched by productId", async () => { + const order = { + outTradeNo: "order_1", + openid: "o1", + itemid: "jungle_treasure_1", + itemCount: 1, + goodsPrice: 300, + type: "and", + state: 0, + }; + reset({ _id: "u1", openid: "o1", token: "secret" }, [order]); + + const result = await payCallback({ + body: { + MiniGame: { + Payload: JSON.stringify({ + OpenId: "o1", + OutTradeNo: "order_1", + GoodsInfo: { ProductId: "jungle_treasure_1", Quantity: 1, OrigPrice: 300 }, + }), + }, + }, + headers: {}, + socket: { remoteAddress: "127.0.0.1" }, + }); + + assert.equal(result.ErrCode, 0); + assert.equal(order.state, 1); + const storedState = getStoredJungleState(); + assert.equal(storedState.tierStates[3], 1); + assert.equal(storedState.paidOrderNos["4"], "order_1"); +}); + +test("Jungle order signing uses server product price in cents and quantity one", async () => { + reset({ + _id: "u1", + openid: "o1", + token: "secret", + session_key: "session-key", + }); + + const result = await orderPaySig({ + body: { + uid: "u1", + itemid: "jungle_treasure_1", + itemCount: 99, + itemPrice: 1, + }, + }); + + assert.equal(result.code, 1); + const signData = JSON.parse(result.data.signData); + assert.equal(signData.productId, "jungle_treasure_1"); + assert.equal(signData.buyQuantity, 1); + assert.equal(signData.goodsPrice, 300); + assert.equal(databaseState.orders[0].goodsPrice, 300); + assert.equal(databaseState.orders[0].jungleTierId, 4); +}); + +test("claiming an unlocked paid tier marks its payment order delivered", async () => { + const unlocked = unlockJungleTierByProductId(null, "jungle_treasure_1", "order_1", 100).state; + const order = { outTradeNo: "order_1", openid: "o1", itemid: "jungle_treasure_1", state: 1 }; + reset({ + _id: "u1", + openid: "o1", + token: "secret", + jungleTreasureState: unlocked, + }, [order]); + + const result = await jungleTreasure({ + body: { action: "claim", uid: "u1", token: "secret", tierId: 4 }, + }); + + assert.equal(result.code, 1); + assert.equal(result.data.tierStates[3], 2); + assert.equal(order.state, 2); + assert.ok(order.getTime instanceof Date); +}); + +test("a claimed tier cannot return success again", () => { + const state = createDefaultJungleState(100); + const first = claimJungleTier(state, 5, 200); + const second = claimJungleTier(first.state, 5, 300); + assert.equal(first.ok, true); + assert.equal(second.ok, false); + assert.match(second.message, /已经领取/); +}); + +test("save is disabled and token/usersAd routing follows backend conventions", async () => { + reset({ _id: "u1", openid: "o1", token: "secret" }); + const save = await jungleTreasure({ + body: { action: "save", uid: "u1", token: "secret", tierStates: [] }, + }); + assert.equal(save.code, 0); + + reset({ _id: "u1", openid: "o1", token: "secret" }); + const badToken = await jungleTreasure({ + body: { action: "read", uid: "u1", token: "wrong" }, + }); + assert.equal(badToken.code, 0); + + reset({ _id: "u1", openid: "o1", token: "secret" }); + await jungleTreasure({ + body: { action: "read", uid: "u1", token: "secret", gameName: "iaa" }, + }); + assert.equal(databaseState.collections[0], "usersAd"); +}); diff --git a/server/laf-cloud/tsconfig.json b/server/laf-cloud/tsconfig.json index 6c050871..ec6fec67 100644 --- a/server/laf-cloud/tsconfig.json +++ b/server/laf-cloud/tsconfig.json @@ -41,4 +41,4 @@ "exclude": [ "tests" ] -} \ No newline at end of file +}