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
+}