MatchMaster/assets/Script/SceneManager.ts
2026-09-21 18:05:43 +08:00

2181 lines
88 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import type GameplayIntro from "../GameplayIntro/script/GameplayIntro";
import { getLevelGameplay } from "./module/Tool/LevelGameplay";
// Learn TypeScript:
// - https://docs.cocos.com/creator/manual/en/scripting/typescript.html
// Learn Attribute:
// - https://docs.cocos.com/creator/manual/en/scripting/reference/attributes.html
// Learn life-cycle callbacks:
// - https://docs.cocos.com/creator/manual/en/scripting/life-cycle-callbacks.html
import MapConroler from "./Map";
import LoadingCatAnimation from "./LoadingCatAnimation";
import Utils from "./module/Pay/Utils";
import { starterPackRemaining } from "./module/Config/StarterPack";
import NumberToImage from "./NumberToImage";
import { MiniGameSdk } from "./Sdk/MiniGameSdk";
const { ccclass, property } = cc._decorator;
interface DynamicPopupState {
bundleName: string;
prefabPath: string;
root: cc.Node;
bundle: cc.AssetManager.Bundle;
loading: boolean;
requestId: number;
callbacks: Array<(error: Error, root?: cc.Node) => void>;
}
@ccclass
export default class SceneManager extends cc.Component {
private static activeInstance: SceneManager = null;
private isPopupRuntimeAlive(): boolean {
return cc.isValid(this, true)
&& SceneManager.activeInstance === this
&& !this.isDestroying
&& !!this.node
&& cc.isValid(this.node, true);
}
/** 旧场景的异步回调不能释放新 GameScene 正在使用的同名 Bundle。 */
private static releaseOrphanedPopupBundle(bundleName: string, bundle: cc.AssetManager.Bundle): void {
// The gift shares action_bundle with Home; its lifetime is not owned by this popup.
if (bundleName === "action_bundle") return;
if (!bundle) return;
setTimeout(() => {
if (bundleName !== "gameplay_intro" && cc.fx.GameTool && cc.fx.GameTool.shouldRetainGameplayPopupBundlesForGameSceneReload
&& cc.fx.GameTool.shouldRetainGameplayPopupBundlesForGameSceneReload()) return;
const active = SceneManager.activeInstance;
if (active && cc.isValid(active, true)) {
if (bundleName !== "gameplay_intro") return;
// 旧场景晚到的介绍请求,仅在新场景确实接管此包时保留。
if (Object.keys(active.dynamicPopupStates || {}).some(name => {
const state = active.dynamicPopupStates[name];
return state.bundle === bundle || (state.loading && state.bundleName === bundleName);
})) return;
}
if (cc.assetManager.getBundle(bundleName) !== bundle) return;
bundle.releaseAll();
if (cc.assetManager.getBundle(bundleName) === bundle) {
cc.assetManager.removeBundle(bundle);
}
}, 0);
}
private releaseStalePopupBundle(bundleName: string, bundle: cc.AssetManager.Bundle): void {
if (!bundle) return;
if (this.isPopupRuntimeAlive()) {
this.releaseDynamicPopupBundleIfUnused(bundleName, bundle);
} else {
SceneManager.releaseOrphanedPopupBundle(bundleName, bundle);
}
}
/** 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;
}
ensureGameShop(): cc.Node {
const game = this.node && this.node.getChildByName("Game");
if (!game) return null;
let shop = game.getChildByName("shop");
// 编辑器仍使用旧场景时,同步迁移顶部入口,避免新脚本与旧层级不一致。
if (!shop) {
const top = cc.find("GameNode/Top", game);
shop = top && top.getChildByName("shop");
if (!shop) return null;
const topWidget = top.getComponent(cc.Widget);
if (topWidget) topWidget.updateAlignment();
const position = game.convertToNodeSpaceAR(shop.convertToWorldSpaceAR(cc.v2(0, 0)));
shop.parent = game;
shop.setPosition(position);
}
if (!shop.getComponent(cc.Widget)) {
const widget = shop.addComponent(cc.Widget);
widget.isAlignTop = true;
widget.top = game.height * (1 - game.anchorY) - shop.y
- shop.height * (1 - shop.anchorY) * shop.scaleY;
widget.alignMode = cc.Widget.AlignMode.ON_WINDOW_RESIZE;
}
shop.zIndex = cc.macro.MAX_ZINDEX - 1;
return shop;
}
/** 动态弹窗加载期间使用;层级高于业务弹窗,低于 Loading 和失败重试层。 */
private ensureDynamicPopupIntercept(): cc.Node {
if (!this.node || !cc.isValid(this.node)) return null;
const intercept = this.node.getChildByName("intercept");
if (intercept) {
intercept.zIndex = cc.macro.MAX_ZINDEX - 1;
}
return intercept;
}
// 缓存 shop 预制体
private static cachedShopPrefab: cc.Prefab | null = null;
// 缓存 shop 节点
private shopNode: cc.Node | null = null;
private starterPackOpening = false;
// 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 jungleTreasureOpenSource: string = "shop_page";
// 缓存 reward 预制体
private static cachedRewardPrefab: cc.Prefab | null = null;
// 缓存 reward 奖励窗口 节点
private RewardNode: cc.Node | null = null;
//缓存月卡
private static cachedMonthlyCardPrefab: cc.Prefab | null = null;
// 缓存月卡节点
private monthlyCardNode: cc.Node | null = null;
static clearShopPrefabCaches(): void {
SceneManager.cachedShopPrefab = null;
SceneManager.cachedRewardPrefab = null;
SceneManager.cachedMonthlyCardPrefab = null;
}
/** releaseAll 不会同步清空 JS 静态字段;使用前先丢弃已经失效的 Prefab。 */
private static clearInvalidShopPrefabCaches(): void {
if (SceneManager.cachedShopPrefab && !cc.isValid(SceneManager.cachedShopPrefab)) {
SceneManager.cachedShopPrefab = null;
}
if (SceneManager.cachedRewardPrefab && !cc.isValid(SceneManager.cachedRewardPrefab)) {
SceneManager.cachedRewardPrefab = null;
}
if (SceneManager.cachedMonthlyCardPrefab && !cc.isValid(SceneManager.cachedMonthlyCardPrefab)) {
SceneManager.cachedMonthlyCardPrefab = null;
}
}
@property(cc.Label)
label: cc.Label = null;
@property
text: string = 'hello';
@property(cc.Node)
freeze: cc.Node = null;
@property(cc.Node)
hammer: cc.Node = null;
@property(cc.Node)
magic_wand: cc.Node = null;
@property(cc.Node)
pause: cc.Node = null;
@property(cc.Node)
level: cc.Node = null;
//time 弹窗的金币数
@property(cc.Node)
timeCoin: cc.Node = null;
//赢了win金币数
@property(cc.Node)
winCoin: cc.Node = null;
@property(cc.Node)
nextMode: cc.Node = null;
@property({ type: cc.Prefab, tooltip: "旧版 B 方案:结算后飞金币预制体,不配则使用 flyCoinSpriteFrame" })
flyCoinPrefab: cc.Prefab = null;
@property({ type: cc.SpriteFrame, tooltip: "旧版 B 方案:结算后飞金币图片,未配置预制体时使用" })
flyCoinSpriteFrame: cc.SpriteFrame = null;
@property({ tooltip: "旧版 B 方案:结算后飞金币数量" })
flyCoinCount: number = 16;
@property({ tooltip: "旧版 B 方案:结算后飞金币单个飞行时间" })
flyCoinDuration: number = 1;
@property({ tooltip: "旧版 B 方案:结算后飞金币间隔" })
flyCoinInterval: number = 0.05;
@property({ type: [cc.Prefab], tooltip: "方块数组" })
Block_Array: Array<cc.Prefab> = [];
@property({ type: [cc.Prefab], tooltip: "墙壁数组" })
Wall_Prefab: Array<cc.Prefab> = [];
@property(cc.SpriteAtlas)
avatar: cc.SpriteAtlas = null;
@property(cc.Prefab)
messageNode: cc.Prefab = null;
particleEffects: cc.ParticleAsset[];
// @property({type: [cc.ParticleSystem], tooltip:"粒子数组"})
// particleEffects : Array<cc.ParticleSystem> = [];
load1: boolean = false;
load2: boolean = false;
load3: boolean = false;
btnName: string = '';
callBack: any;
share_time: boolean = true;
private flyCoinLayer: cc.Node = null;
private isDestroying: boolean = false;
private winLevelName: number = 0;
private dynamicPopupLoads: { [popupName: string]: boolean } = {};
private dynamicPopupRetryNode: cc.Node = null;
private dynamicPopupRetryName: string = "";
private dynamicPopupRetries: { [popupName: string]: { displayName: string; retry: () => void } } = {};
private dynamicPopupStates: { [popupName: string]: DynamicPopupState } = {};
private popupBundleWarmupStarted: boolean = false;
private gameplayIntroPredownloadScheduled: boolean = false;
/** 地图就绪且弹窗预热结束后调用;持续空闲一秒才派发,只下载,不 loadBundle。 */
scheduleGameplayIntroPredownload() {
//@ts-ignore
if (cc.sys.platform !== cc.sys.WECHAT_GAME || typeof wx === "undefined" || !wx.preDownloadSubpackage) return;
if (this.gameplayIntroPredownloadScheduled || !this.isPopupRuntimeAlive()) return;
this.gameplayIntroPredownloadScheduled = true;
let idle = false;
const check = () => {
if (!this.isPopupRuntimeAlive()) return;
const tool = cc.fx.GameTool;
if (tool.isGameplayIntroDownloaded()) return;
const busy = tool.hasPendingGameplayResourceLoads() || cc.fx.GameConfig.isGameplayEntryPreparing()
|| Object.keys(this.dynamicPopupLoads).length > 0
|| Object.keys(this.dynamicPopupStates).some(name => this.dynamicPopupStates[name].loading);
if (busy || !idle) {
idle = !busy;
this.scheduleOnce(check, 1);
return;
}
tool.predownloadGameplayIntro().then(ready => {
if (!ready && this.isPopupRuntimeAlive()) {
idle = false;
this.scheduleOnce(check, 10);
}
});
};
this.scheduleOnce(check, 1);
}
warmupGameplayPopupBundles(
preloadInstances: boolean = false,
preloadWin?: (onComplete: (error?: Error) => void) => void,
onComplete?: (error?: Error) => void,
) {
if (!this.isPopupRuntimeAlive() || this.popupBundleWarmupStarted) return;
this.popupBundleWarmupStarted = true;
if (preloadInstances) {
const gameRoot = this.node && this.node.getChildByName("Game");
const requests = [
{ popupName: "pause", bundleName: "pause", prefabPath: "prefab/Pause", parent: this.node },
{ popupName: "win", bundleName: "win", prefabPath: "prefab/Win", parent: gameRoot },
{ popupName: "propWindow", bundleName: "propWindow", prefabPath: "prefab/PropWindow", parent: gameRoot },
{ popupName: "newMode", bundleName: "NewMode", prefabPath: "prefab/NewMode", parent: gameRoot },
{ popupName: "lose", bundleName: "lose", prefabPath: "prefab/Lose", parent: gameRoot },
];
let warmupError: Error = null;
const preloadNext = (index: number) => {
if (!this.isPopupRuntimeAlive()) return;
if (!cc.fx.GameTool.shouldPreloadGameplayPopupInstances()) {
this.popupBundleWarmupStarted = false;
if (onComplete) onComplete();
return;
}
if (index >= requests.length) {
if (onComplete) {
if (warmupError) this.popupBundleWarmupStarted = false;
onComplete(warmupError);
}
return;
}
const request = requests[index];
const startedAt = Date.now();
// console.log(`[进关耗时][弹窗预热/${request.bundleName}] 开始(不阻塞进关)`);
const logDone = (error?: Error) => {
// console.log(`[进关耗时][弹窗预热/${request.bundleName}] ${error ? "失败" : "完成"},独立耗时=${Date.now() - startedAt}ms`);
};
// 即使资源全命中内存,也不要在同一帧连续实例化五个弹窗。
const next = () => this.scheduleOnce(() => preloadNext(index + 1), 0);
if (request.popupName === "win") {
if (preloadWin) {
preloadWin(error => {
if (!this.isPopupRuntimeAlive()) return;
logDone(error);
if (error) {
warmupError = error;
cc.warn("Popup instance warmup failed: win", error.message || error);
}
next();
});
} else {
next();
}
return;
}
this.loadDynamicPopup(
request.popupName,
request.bundleName,
request.prefabPath,
request.parent,
(error: Error) => {
if (!this.isPopupRuntimeAlive()) return;
logDone(error);
if (error) {
warmupError = error;
cc.warn(`Popup instance warmup failed: ${request.bundleName}`, error.message || error);
// loadDynamicPopup 已清理失败资源。用户可能正共用此请求,
// 这里不能清除其输入拦截/重试状态。
}
next();
},
false,
);
};
preloadNext(0);
return;
}
// iOS 低内存机型只提前创建 Pause Bundle,不加载 Prefab 和图片。
const shouldWarmupPauseBundle = cc.sys.platform === cc.sys.WECHAT_GAME
&& !!cc.fx.GameTool
&& !!cc.fx.GameTool.isIOSDevice
&& cc.fx.GameTool.isIOSDevice();
if (!shouldWarmupPauseBundle) {
if (onComplete) onComplete();
return;
}
const loadedPauseBundle = cc.assetManager.getBundle("pause");
if (loadedPauseBundle) {
console.log("[PopupBundle] already loaded: pause (iOS low-memory warmup)");
if (onComplete) onComplete();
return;
}
cc.assetManager.loadBundle("pause", (error: Error, bundle: cc.AssetManager.Bundle) => {
if (!this.isPopupRuntimeAlive()) {
if (bundle) SceneManager.releaseOrphanedPopupBundle("pause", bundle);
return;
}
if (error) {
cc.warn("[PopupBundle] loadBundle failed: pause (iOS low-memory warmup)", error.message || error);
if (onComplete) onComplete(error);
return;
}
console.log("[PopupBundle] loadBundle complete: pause (iOS low-memory warmup)");
if (onComplete) onComplete();
});
}
beginDynamicPopupLoad(popupName: string) {
if (!popupName || !this.isPopupRuntimeAlive()) return;
if (!this.dynamicPopupLoads) this.dynamicPopupLoads = {};
this.dynamicPopupLoads[popupName] = true;
const intercept = this.ensureDynamicPopupIntercept();
if (!intercept) {
cc.error("GameScene is missing Canvas/intercept");
return;
}
intercept.active = true;
}
finishDynamicPopupLoad(popupName: string) {
if (!popupName || !this.isPopupRuntimeAlive()) return;
if (!this.dynamicPopupLoads) this.dynamicPopupLoads = {};
if (!this.dynamicPopupRetries) this.dynamicPopupRetries = {};
delete this.dynamicPopupLoads[popupName];
delete this.dynamicPopupRetries[popupName];
if (this.dynamicPopupRetryName === popupName) {
this.clearDynamicPopupRetry();
this.showNextDynamicPopupRetry();
}
if (Object.keys(this.dynamicPopupLoads).length === 0) {
this.dynamicPopupRetries = {};
const intercept = this.ensureDynamicPopupIntercept();
if (intercept) intercept.active = false;
}
}
showDynamicPopupLoadError(popupName: string, displayName: string, retry: () => void) {
if (!this.isPopupRuntimeAlive()) return;
this.beginDynamicPopupLoad(popupName);
this.dynamicPopupRetries[popupName] = { displayName, retry };
if (this.dynamicPopupRetryNode && cc.isValid(this.dynamicPopupRetryNode)
&& this.dynamicPopupRetryName !== popupName) {
return;
}
this.clearDynamicPopupRetry();
this.showDynamicPopupRetry(popupName);
}
private showNextDynamicPopupRetry() {
if (!this.isPopupRuntimeAlive() || this.dynamicPopupRetryNode || !this.dynamicPopupRetries) return;
const popupName = Object.keys(this.dynamicPopupRetries)
.find((name) => !!this.dynamicPopupLoads[name]);
if (popupName) this.showDynamicPopupRetry(popupName);
}
private showDynamicPopupRetry(popupName: string) {
if (!this.isPopupRuntimeAlive() || !this.dynamicPopupRetries) return;
const retryInfo = this.dynamicPopupRetries[popupName];
if (!retryInfo || !this.node || !cc.isValid(this.node)) return;
const displayName = retryInfo.displayName;
const overlay = new cc.Node("DynamicPopupLoadRetry");
overlay.setContentSize(this.node.getContentSize());
overlay.setPosition(0, 0);
overlay.zIndex = cc.macro.MAX_ZINDEX;
overlay.addComponent(cc.BlockInputEvents);
this.node.addChild(overlay);
overlay.setSiblingIndex(this.node.childrenCount - 1);
const background = overlay.addComponent(cc.Graphics);
background.fillColor = cc.color(0, 0, 0, 225);
background.rect(-overlay.width / 2, -overlay.height / 2, overlay.width, overlay.height);
background.fill();
const messageNode = new cc.Node("message");
messageNode.setContentSize(760, 120);
messageNode.parent = overlay;
const label = messageNode.addComponent(cc.Label);
label.string = `${displayName || "界面资源"}加载失败\n点击屏幕重试`;
label.fontSize = 32;
label.lineHeight = 48;
label.horizontalAlign = cc.Label.HorizontalAlign.CENTER;
this.dynamicPopupRetryNode = overlay;
this.dynamicPopupRetryName = popupName;
overlay.once(cc.Node.EventType.TOUCH_END, () => {
if (!this.isPopupRuntimeAlive()) return;
if (this.dynamicPopupRetryNode !== overlay) return;
if (!this.dynamicPopupLoads || !this.dynamicPopupLoads[popupName]) return;
label.string = `${displayName || "界面资源"}正在重新加载\n请稍等`;
try {
retryInfo.retry();
} catch (error) {
cc.error(`${displayName || "界面资源"} retry failed:`, error);
this.showDynamicPopupLoadError(popupName, displayName, retryInfo.retry);
}
});
}
private clearDynamicPopupRetry() {
if (this.dynamicPopupRetryNode && cc.isValid(this.dynamicPopupRetryNode)) {
this.dynamicPopupRetryNode.removeFromParent();
this.dynamicPopupRetryNode.destroy();
}
this.dynamicPopupRetryNode = null;
this.dynamicPopupRetryName = "";
}
loadDynamicPopup(
popupName: string,
bundleName: string,
prefabPath: string,
parent: cc.Node,
callback: (error: Error, root?: cc.Node) => void,
showLoading: boolean = true,
) {
if (!this.isPopupRuntimeAlive()) return;
if (!this.dynamicPopupStates) this.dynamicPopupStates = {};
if (showLoading) this.beginDynamicPopupLoad(popupName);
let state = this.dynamicPopupStates[popupName];
if (!state) {
state = this.dynamicPopupStates[popupName] = {
bundleName,
prefabPath,
root: null,
bundle: null,
loading: false,
requestId: 0,
callbacks: [],
};
}
if (state.root && cc.isValid(state.root)) {
callback(null, state.root);
return;
}
state.root = null;
if (!parent || !cc.isValid(parent)) {
callback(new Error(`${popupName} parent node is missing`));
return;
}
state.callbacks.push(callback);
if (state.loading) return;
state.loading = true;
const requestId = ++state.requestId;
const fail = (error: Error, bundle?: cc.AssetManager.Bundle) => {
if (!this.isDynamicPopupRequestCurrent(popupName, state, requestId)) {
if (bundle) this.releaseStalePopupBundle(bundleName, bundle);
return;
}
if (bundle && state.bundle === bundle) state.bundle = null;
state.loading = false;
if (bundle) this.releaseDynamicPopupBundleIfUnused(bundleName, bundle);
this.finishDynamicPopupAssetLoad(state, error);
};
const loadPrefab = (bundle: cc.AssetManager.Bundle) => {
if (!this.isDynamicPopupRequestCurrent(popupName, state, requestId)) {
this.releaseStalePopupBundle(bundleName, bundle);
return;
}
state.bundle = bundle;
bundle.load(prefabPath, cc.Prefab, (error: Error, prefab: cc.Prefab) => {
if (error) {
fail(error, bundle);
return;
}
if (!this.isDynamicPopupRequestCurrent(popupName, state, requestId)) {
this.releaseStalePopupBundle(bundleName, bundle);
return;
}
let root: cc.Node = null;
try {
root = cc.instantiate(prefab);
root.active = false;
parent.addChild(root);
root.setPosition(0, 0);
root.zIndex = cc.macro.MAX_ZINDEX - 2;
console.log(`[PopupPrefab] instantiated inactive: ${bundleName}/${prefabPath}`);
} catch (instantiateError) {
if (root && cc.isValid(root)) {
root.active = false;
root.removeFromParent();
root.destroy();
}
fail(instantiateError as Error, bundle);
return;
}
state.root = root;
this.finishDynamicPopupAssetLoad(state, null, root);
});
};
const loadedBundle = cc.assetManager.getBundle(bundleName);
if (loadedBundle) {
console.log(`[PopupBundle] already loaded: ${bundleName}`);
loadPrefab(loadedBundle);
return;
}
cc.assetManager.loadBundle(bundleName, (error: Error, bundle: cc.AssetManager.Bundle) => {
if (error) {
fail(error);
return;
}
console.log(`[PopupBundle] loadBundle complete: ${bundleName}`);
loadPrefab(bundle);
});
}
getDynamicPopup(popupName: string): cc.Node {
if (!cc.isValid(this, true) || !this.dynamicPopupStates) return null;
const state = this.dynamicPopupStates[popupName];
return state && state.root && cc.isValid(state.root) ? state.root : null;
}
releaseDynamicPopup(popupName: string, force: boolean = false, retainBundle: boolean = false) {
if (!this.dynamicPopupStates) return;
if (this.isDestroying !== true && !this.isPopupRuntimeAlive()) return;
const state = this.dynamicPopupStates[popupName];
if (state && !force && this.shouldKeepGameplayPopupInstance(popupName)
&& state.root && cc.isValid(state.root) && !state.loading) {
state.root.active = false;
this.finishDynamicPopupLoad(popupName);
return;
}
if (state) {
delete this.dynamicPopupStates[popupName];
state.requestId++;
state.callbacks.length = 0;
if (state.root && cc.isValid(state.root)) {
state.root.active = false;
state.root.removeFromParent();
state.root.destroy();
}
state.root = null;
const bundle = state.bundle || cc.assetManager.getBundle(state.bundleName);
state.bundle = null;
// 介绍只缓存到当前关,不能混入五个常用弹窗的切关保留策略。
if (bundle && (!retainBundle || state.bundleName === "gameplay_intro")) {
this.releaseDynamicPopupBundle(state.bundleName, bundle);
}
}
this.finishDynamicPopupLoad(popupName);
}
private shouldKeepGameplayPopupInstance(popupName: string): boolean {
const cacheablePopups = ["pause", "lose", "propWindow", "newMode"];
return cacheablePopups.indexOf(popupName) >= 0
&& !!cc.fx.GameTool
&& !!cc.fx.GameTool.shouldPreloadGameplayPopupInstances
&& cc.fx.GameTool.shouldPreloadGameplayPopupInstances();
}
private isDynamicPopupRequestCurrent(popupName: string, state: DynamicPopupState, requestId: number) {
return this.isPopupRuntimeAlive()
&& !!this.dynamicPopupStates
&& this.dynamicPopupStates[popupName] === state
&& state.requestId === requestId;
}
private finishDynamicPopupAssetLoad(state: DynamicPopupState, error: Error, root?: cc.Node) {
if (!this.isPopupRuntimeAlive()) {
state.loading = false;
state.callbacks.length = 0;
return;
}
state.loading = false;
const callbacks = state.callbacks.splice(0);
callbacks.forEach((callback) => {
if (!this.isPopupRuntimeAlive()) return;
try {
callback(error, root);
} catch (callbackError) {
cc.error("Dynamic popup callback failed:", callbackError);
}
});
}
private releaseDynamicPopupBundle(bundleName: string, bundle: cc.AssetManager.Bundle) {
if (bundleName === "action_bundle") return;
if (cc.assetManager.getBundle(bundleName) !== bundle) return;
bundle.releaseAll();
if (cc.assetManager.getBundle(bundleName) === bundle) {
cc.assetManager.removeBundle(bundle);
}
}
private releaseDynamicPopupBundleIfUnused(bundleName: string, bundle: cc.AssetManager.Bundle) {
const states = this.dynamicPopupStates;
if (!states) {
SceneManager.releaseOrphanedPopupBundle(bundleName, bundle);
return;
}
const isInUse = Object.keys(states).some((popupName) => {
const state = states[popupName];
return state.bundle === bundle || (state.loading && state.bundleName === bundleName);
});
if (!isInUse) this.releaseDynamicPopupBundle(bundleName, bundle);
}
bindLosePanel(loseRoot: cc.Node) {
const lose = loseRoot && loseRoot.getChildByName("lose");
const time = lose && lose.getChildByName("Time");
const buyBtn = time && time.getChildByName("buyBtn");
const revive = time && time.getChildByName("Revive");
const timeCoin = buyBtn && buyBtn.getChildByName("coinNun");
const panel: any = loseRoot && loseRoot.getComponent("LosePanel");
const map = MapConroler && MapConroler._instance;
if (!lose || !revive || !timeCoin || !panel || !panel.bind || !map) {
cc.error("SceneManager.bindLosePanel failed: Lose prefab node structure changed");
this.unbindLosePanel();
return false;
}
panel.bind({
shareFriend: () => this.shareFriend(),
shareFriend2: () => this.shareFriend2(),
shareClose: () => this.shareClose(),
homeBtn: (event: cc.Event, data: string) => map.homeBtn(event, data),
reviewLevel: (event: cc.Event, data: string) => map.reviewLevel(event, data),
closeWinStreak: () => map.closeWinStreak(),
openHealth: () => map.openHealth(),
returnHome: (event: cc.Event, data: string) => map.returnHome(event, data),
againLevel: () => map.againLevel(),
});
map.revive = revive;
this.timeCoin = timeCoin;
NumberToImage.numberToImageNodes(1000, 25, 15, "button_", this.timeCoin, false);
return true;
}
unbindLosePanel() {
if (MapConroler && MapConroler._instance) {
MapConroler._instance.revive = null;
}
this.timeCoin = null;
}
releaseLosePanel(force: boolean = false) {
this.unbindLosePanel();
this.releaseDynamicPopup("lose", force);
}
private bindPropWindowPanel(propRoot: cc.Node) {
const prop = propRoot && propRoot.getChildByName("prop");
const buyBtn = prop && prop.getChildByName("buy_Btn");
const panel: any = propRoot && propRoot.getComponent("PropWindowPanel");
const map = MapConroler && MapConroler._instance;
if (!prop || !buyBtn || !buyBtn.getComponent("btnControl") || !panel || !panel.bind || !map) {
cc.error("SceneManager.bindPropWindowPanel failed: propWindow prefab node structure changed");
this.unbindPropWindowPanel();
return false;
}
panel.bind({
clickBtn: () => this.clickBtn(),
closePropBuy: (event: cc.Event) => this.closePropBuy(event),
});
map.coinPop = propRoot;
return true;
}
private unbindPropWindowPanel() {
if (MapConroler && MapConroler._instance) {
MapConroler._instance.coinPop = null;
}
}
private releasePropWindowPanel(force: boolean = false) {
this.unbindPropWindowPanel();
this.releaseDynamicPopup("propWindow", force);
}
private bindPausePanel(pauseRoot: cc.Node) {
const pauseContent = pauseRoot && pauseRoot.getChildByName("pause");
const panel: any = pauseRoot && pauseRoot.getComponent("PausePanel");
const setting = pauseRoot && pauseRoot.getComponent("setUi");
const map = MapConroler && MapConroler._instance;
if (!pauseContent || !panel || !panel.bind || !setting || !map) {
cc.error("SceneManager.bindPausePanel failed: Pause prefab node structure changed");
return false;
}
panel.bind({
closePause: () => this.closePause(),
shareFriend: () => this.shareFriend(),
openMessage: () => this.openMessage(),
returnHome: (event: cc.Event, data: string) => map.returnHome(event, data),
});
return true;
}
// 在 Creator 的 Inspector 中绑定场景现有 tipPanel;打开后的预制体挂到这里。
@property(cc.Node)
tipPanel: cc.Node = null;
// 保存已创建的面板控制器,重复打开复用实例;玩法图片仍由面板按需加载/释放。
private gameplayIntro: GameplayIntro = null;
private levelGameplay: any[] = [];
isPauseOpen(): boolean {
// 加载期间及面板显示期间都视为暂停,避免重复打开或其它流程提前恢复游戏。
if (this.dynamicPopupLoads && this.dynamicPopupLoads.gameplayIntro) return true;
if (this.gameplayIntro && cc.isValid(this.gameplayIntro.node) && this.gameplayIntro.node.active) return true;
const pauseRoot = this.getDynamicPopup("pause");
return !!(this.dynamicPopupLoads && this.dynamicPopupLoads.pause)
|| (!!pauseRoot && pauseRoot.active);
}
releasePausePanel(force: boolean = false) {
const pauseControl = this.pause && this.pause.getComponent("btnControl");
if (pauseControl) pauseControl.setTouch(true);
this.releaseDynamicPopup("pause", force);
}
bindWinPanel(winRoot: cc.Node) {
const tween = winRoot && winRoot.getChildByName("tween");
const diguan = tween && tween.getChildByName("diguan");
const tiaodik = tween && tween.getChildByName("tiaodik");
this.level = diguan && diguan.getChildByName("level");
this.winCoin = tiaodik && tiaodik.getChildByName("coin");
this.nextMode = tiaodik && tiaodik.getChildByName("nextMode");
if (!this.level || !this.winCoin || !this.nextMode) {
cc.error("SceneManager.bindWinPanel failed: Win prefab node structure changed");
this.unbindWinPanel();
return false;
}
this.refreshWinPanelInfo();
return true;
}
unbindWinPanel() {
this.level = null;
this.winCoin = null;
this.nextMode = null;
this.clearFlyCoins();
}
private refreshWinPanelInfo() {
if (!this.level || !cc.isValid(this.level)) return;
const levelName = this.winLevelName || this.resolveWinLevelName();
// NumberToImage.numberToImageNodes3(levelName, 43, 15, "level_", this.level, true);
NumberToImage.numberToImageNodes(levelName, 43, 8, "button_", this.level, true);
this.level.children.forEach((child) => child.color = new cc.Color(195, 154, 91));
}
private resolveWinLevelName(): number {
if (cc.fx.GameConfig.GM_INFO.otherLevel > 0) {
return Number(cc.fx.GameConfig.GM_INFO.otherLevel) || 0;
}
if (cc.fx.GameTool.maxLevel() && cc.fx.GameConfig.GM_INFO.GameplayType == 1) {
return Number(cc.fx.GameConfig.GM_INFO.randomLevel) || 0;
}
return (Number(cc.fx.GameConfig.GM_INFO.level) || 0) + 1;
}
/**
* 为 GameScene 中所有可交互按钮统一添加轻微震动。
* 使用根节点捕获事件,以覆盖运行时动态创建的按钮。
*/
private onGameButtonTouchEnd(event: cc.Event.EventTouch) {
let target = event && event.target as cc.Node;
while (target) {
const button = target.getComponent(cc.Button);
if (button) {
// _pressed 为 false 时说明手指已移出按钮,不属于有效点击。
if (button.interactable && button.enabledInHierarchy && (button as any)._pressed !== false) {
try {
cc.fx.GameTool.setVibrate("light", 1);
} catch (error) {
try {
console.warn("GameScene 按钮轻振动调用失败:", error);
} catch (e) { }
}
}
return;
}
if (target === this.node) {
return;
}
target = target.parent;
}
}
// LIFE-CYCLE CALLBACKS:
onLoad() {
SceneManager.activeInstance = this;
this.isDestroying = false;
this.dynamicPopupLoads = {};
this.dynamicPopupRetries = {};
this.dynamicPopupStates = {};
this.popupBundleWarmupStarted = false;
this.refreshGameplayInfo();
this.ensureLoadingOnTop();
if (!this.ensureGameShop()) cc.error("GameScene 缺少顶部商店入口 Game/shop");
const intercept = this.ensureDynamicPopupIntercept();
if (intercept) intercept.active = false;
this.winLevelName = this.resolveWinLevelName();
this.node.on(cc.Node.EventType.TOUCH_END, this.onGameButtonTouchEnd, this, true);
this.share_time = true;
cc.game.setFrameRate(63);
this.changeBg();
// 预加载 shop 预制体
SceneManager.clearInvalidShopPrefabCaches();
if (!SceneManager.cachedShopPrefab
|| !SceneManager.cachedRewardPrefab
|| !SceneManager.cachedMonthlyCardPrefab) {
cc.assetManager.loadBundle('shop', (err: Error, bundle: cc.AssetManager.Bundle) => {
if (err) {
cc.error(err.message || err);
return;
}
if (!SceneManager.cachedShopPrefab) {
bundle.load('prefab/shop', cc.Prefab, (err: Error, prefab: cc.Prefab) => {
if (err) {
cc.error(err.message || err);
return;
}
SceneManager.cachedShopPrefab = prefab;
});
}
if (!SceneManager.cachedRewardPrefab) {
bundle.load('prefab/RewardWindow', cc.Prefab, (err: Error, prefab: cc.Prefab) => {
if (err) {
cc.error(err.message || err);
return;
}
SceneManager.cachedRewardPrefab = prefab;
});
}
if (!SceneManager.cachedMonthlyCardPrefab) {
bundle.load('prefab/monthlyCard', cc.Prefab, (err: Error, prefab: cc.Prefab) => {
if (err) {
cc.error(err.message || err);
return;
}
SceneManager.cachedMonthlyCardPrefab = prefab;
});
}
});
}
if (!cc.fx.GameTool.isLowMemoryIOSDevice()) {
setTimeout(() => {
cc.director.preloadScene("HomeScene", (err) => {
if (err) {
// console.error('预加载 HomeScene 场景失败:', err);
return;
}
// console.log('成功预加载 HomeScene 场景');
});
}, 5000);
}
else {
console.log("[Memory] low-memory iOS: skip HomeScene preload in SceneManager");
}
}
onDestroy() {
this.isDestroying = true;
if (SceneManager.activeInstance === this) SceneManager.activeInstance = null;
this.unbindLosePanel();
this.unbindPropWindowPanel();
const retainPopupBundles = !!cc.fx.GameTool
&& !!cc.fx.GameTool.shouldRetainGameplayPopupBundlesForGameSceneReload
&& cc.fx.GameTool.shouldRetainGameplayPopupBundlesForGameSceneReload();
Object.keys(this.dynamicPopupStates || {}).forEach((popupName) =>
this.releaseDynamicPopup(popupName, true, retainPopupBundles));
const introBundle = cc.assetManager.getBundle("gameplay_intro");
if (introBundle) this.releaseDynamicPopupBundleIfUnused("gameplay_intro", introBundle);
if (!retainPopupBundles) {
["pause", "NewMode", "propWindow", "lose"].forEach((bundleName) => {
const bundle = cc.assetManager.getBundle(bundleName);
if (bundle) this.releaseDynamicPopupBundleIfUnused(bundleName, bundle);
});
}
else {
console.log("[PopupBundle] retained pause/NewMode/propWindow/lose for next GameScene");
}
this.dynamicPopupStates = {};
this.dynamicPopupLoads = {};
this.dynamicPopupRetries = {};
this.clearDynamicPopupRetry();
const intercept = this.node && this.node.getChildByName("intercept");
if (intercept) intercept.active = false;
if (this.node && cc.isValid(this.node, true)) {
this.node.off(cc.Node.EventType.TOUCH_END, this.onGameButtonTouchEnd, this, true);
}
this.clearFlyCoins();
this.disposeJungleTreasure(false);
}
clearFlyCoins() {
if (this.flyCoinLayer && cc.isValid(this.flyCoinLayer)) {
cc.Tween.stopAllByTarget(this.flyCoinLayer);
for (let i = 0; i < this.flyCoinLayer.childrenCount; i++) {
const child = this.flyCoinLayer.children[i];
if (child && cc.isValid(child)) {
cc.Tween.stopAllByTarget(child);
}
}
this.flyCoinLayer.removeAllChildren();
}
}
private updateGameCoinDisplay(displayCoin: number = cc.fx.GameConfig.GM_INFO.coin): void {
if (this.isDestroying) return;
const map = MapConroler && MapConroler._instance;
if (map && cc.isValid(map, true) && map.node && cc.isValid(map.node, true)) {
map.updateCoin(displayCoin);
}
}
getWinCoinFlyTarget() {
if (!this.node || !cc.isValid(this.node)) {
return null;
}
const shop = this.ensureGameShop();
return shop && shop.getChildByName("coins");
}
//更新关卡获得金币
updateWinCoinIconState() {
if (!this.winCoin || !cc.isValid(this.winCoin) || !this.winCoin.parent) {
return;
}
const layerTwoOpen = cc.fx.GameConfig.LEGACY_LAYER_TWO_VARIANT_B_ENABLED;
const coinIcon = this.winCoin.parent.getChildByName("coinIcon");
const meijinbi = this.winCoin.parent.getChildByName("meijinbi");
if (coinIcon) {
coinIcon.active = layerTwoOpen;
}
if (meijinbi) {
meijinbi.active = !layerTwoOpen;
}
}
updateWinCoin() {
if (!this.winCoin || !cc.isValid(this.winCoin)) {
return;
}
console.log("更新关卡获得金币");
this.updateWinCoinIconState();
let winCoin = null;
winCoin = 40;
winCoin = winCoin * cc.fx.GameConfig.GM_INFO.doubleCoin;
if (winCoin > 0) {
NumberToImage.numberToImageNodes(winCoin, 50, 8, "half_black_", this.winCoin, false);
} else {
this.winCoin.active = false;
}
}
//更新下一关获得金币
playWinCoinFly(winRewardCoin: number) {
if (this.isDestroying || !this.node || !cc.isValid(this.node)) {
return;
}
if (!this.winCoin || !cc.isValid(this.winCoin) || (!this.flyCoinPrefab && !this.flyCoinSpriteFrame)) {
console.warn("playWinCoinFly skip: winCoin or fly coin asset missing");
this.updateGameCoinDisplay();
return;
}
const targetNode = this.getWinCoinFlyTarget();
if (!targetNode || !cc.isValid(targetNode)) {
console.warn("playWinCoinFly skip: target Game/shop/coins missing");
this.updateGameCoinDisplay();
return;
}
const coinParent = this.winCoin.parent && this.winCoin.parent.getChildByName("beijing");
if (!coinParent || !cc.isValid(coinParent)) {
console.warn("playWinCoinFly skip: Win/tween/tiaodik/beijing missing");
this.updateGameCoinDisplay();
return;
}
this.clearFlyCoins();
if (!this.flyCoinLayer || !cc.isValid(this.flyCoinLayer)) {
this.flyCoinLayer = coinParent.getChildByName("flyCoinLayer");
}
if (!this.flyCoinLayer || !cc.isValid(this.flyCoinLayer)) {
this.flyCoinLayer = new cc.Node("flyCoinLayer");
}
const layer = this.flyCoinLayer;
layer.parent = coinParent;
layer.setPosition(0, 0);
layer.active = true;
layer.zIndex = 9999;
layer.removeAllChildren();
const startWorld = this.winCoin.convertToWorldSpaceAR(cc.v2(0, 0));
const targetWorld = targetNode.convertToWorldSpaceAR(cc.v2(0, 0));
// 散开偏移沿用 Canvas 坐标,再转换到 beijing 下,避免父节点变换影响路径。
const startPos = this.node.convertToNodeSpaceAR(startWorld);
const targetPos = layer.convertToNodeSpaceAR(targetWorld);
const count = Math.max(1, Math.floor(Number(this.flyCoinCount) || 1));
const speed = 2;
const duration = Math.max(0.1, Number(this.flyCoinDuration) || 0.55) / speed;
const interval = Math.max(0, Number(this.flyCoinInterval) || 0) / speed;
const finalCoin = Math.max(0, Math.floor(Number(cc.fx.GameConfig.GM_INFO.coin) || 0));
const rewardCoin = Math.max(0, Math.floor(Number(winRewardCoin) || 0));
const startCoin = Math.max(0, finalCoin - rewardCoin);
let arrivedCount = 0;
this.updateGameCoinDisplay(startCoin);
console.log("playWinCoinFly start", count, startPos, targetPos);
for (let i = 0; i < count; i++) {
if (this.isDestroying || !cc.isValid(layer)) {
return;
}
const coin = this.createFlyCoinNode();
if (!coin || !cc.isValid(layer)) {
continue;
}
layer.addChild(coin);
coin.active = true;
coin.opacity = 255;
coin.zIndex = 1;
const startX = startPos.x + (Math.random() - 0.5) * 120 + 50;
const startY = startPos.y + (Math.random() - 0.5) * 70 + 150;
coin.setPosition(layer.convertToNodeSpaceAR(this.node.convertToWorldSpaceAR(cc.v2(startX, startY))));
coin.scale = 0;
const midPos = layer.convertToNodeSpaceAR(this.node.convertToWorldSpaceAR(cc.v2(
startX + (Math.random() - 0.5) * 180,
startY + 80 + Math.random() * 80
)));
cc.tween(coin)
.delay(i * interval)
.to(0.12 / speed, { scale: 1 }, { easing: "backOut" })
.to(duration * 0.35, { x: midPos.x, y: midPos.y }, { easing: "sineOut" })
.to(duration * 0.65, { x: targetPos.x, y: targetPos.y, scale: 0.35 }, { easing: "sineIn" })
.call(() => {
arrivedCount++;
const displayCoin = arrivedCount >= count
? finalCoin
: startCoin + Math.floor(rewardCoin * arrivedCount / count);
this.updateGameCoinDisplay(displayCoin);
if (coin && cc.isValid(coin)) {
coin.destroy();
}
})
.start();
}
}
createFlyCoinNode() {
if (this.flyCoinPrefab) {
return cc.instantiate(this.flyCoinPrefab);
}
if (!this.flyCoinSpriteFrame) {
return null;
}
const node = new cc.Node("flyCoin");
const sprite = node.addComponent(cc.Sprite);
sprite.spriteFrame = this.flyCoinSpriteFrame;
return node;
}
updateNextMode() {
if (!this.nextMode || !cc.isValid(this.nextMode) || !this.nextMode.parent) {
return;
}
let nextMode = -1;
this.nextMode.active = false;
this.nextMode.parent.getChildByName("nextLabel").active = false;
const currentLevel = cc.fx.GameConfig.GM_INFO.level + 1;
const newLevelConfig = cc.fx.GameConfig.NEW_LEVEL || [];
for (let i = 0; i < newLevelConfig.length; i++) {
const newLevel = Number(newLevelConfig[i].level);
if (newLevel > currentLevel) {
const diff = newLevel - currentLevel;
if (nextMode < 0 || diff < nextMode) {
nextMode = diff;
}
}
if (newLevel == currentLevel) {
nextMode = 0;
this.nextMode.active = false;
this.nextMode.parent.getChildByName("nextLabel").active = false;
}
}
if (nextMode > 0 && nextMode <= 10) {
this.nextMode.active = true;
this.nextMode.parent.getChildByName("nextLabel").active = true;
NumberToImage.numberToImageNodes(nextMode, 35, 8, "time_", this.nextMode, false);
} else {
this.nextMode.active = false;
this.nextMode.parent.getChildByName("nextLabel").active = false;
}
}
changeBg() {
let number = Math.floor(Math.random() * 8) + 1;
const path = 'bg/bg' + number;
cc.resources.load(path, cc.SpriteFrame, (err, spriteFrame: cc.SpriteFrame) => {
if (err) {
console.error('动态加载背景图失败:', err);
return;
}
this.node.getChildByName("Game").getChildByName("bg").getComponent(cc.Sprite).spriteFrame = spriteFrame;
})
//time金币数量
if (this.timeCoin && cc.isValid(this.timeCoin)) {
NumberToImage.numberToImageNodes(1000, 25, 15, "button_", this.timeCoin, false);
}
this.refreshWinPanelInfo();
}
loadParticleEffects() {
const path = 'Particle';
cc.resources.loadDir(path, cc.ParticleAsset, (err, assets: cc.ParticleAsset[]) => {
if (err) {
console.error('动态加载粒子特效失败:', err);
return;
}
this.particleEffects = assets;
this.setParticleSort();
this.load3 = true;
//console.log('粒子特效加载成功,共加载了', this.particleEffects.length, '个粒子特效');
});
}
setWallPrefabSort() {
const order = ['down', 'downLeft', 'downRight', 'left', 'right', 'up', 'upLeft', 'upRight'];
this.Wall_Prefab.sort((a, b) => {
const indexA = order.indexOf(a.name);
const indexB = order.indexOf(b.name);
if (indexA === -1) return 1;
if (indexB === -1) return -1;
return indexA - indexB;
});
}
setParticleSort() {
const order = ['top', 'bot', 'rig', 'lef'];
this.particleEffects.sort((a, b) => {
// console.log(a.name.substr(0,3),b.name.substr(0,3));
const indexA = order.indexOf(a.name.substr(0, 3));
const indexB = order.indexOf(b.name.substr(0, 3));
if (indexA === -1) return 1;
if (indexB === -1) return -1;
return indexA - indexB;
});
}
setSort() {
this.Block_Array.sort((a, b) => {
// 从名称中提取数字部分
const numberA = parseInt(a.name.match(/\d+/)?.[0] || '0', 10);
const numberB = parseInt(b.name.match(/\d+/)?.[0] || '0', 10);
return numberA - numberB;
});
}
/** 关卡数据在进入场景前已加载;按完整关卡保存列表,消除道具后仍可查阅。 */
private refreshGameplayInfo() {
const button = cc.find("Game/GameNode/Top/tip/GameplayInfoButton", this.node);
const apply = (config: any[]) => {
this.levelGameplay = getLevelGameplay(config, cc.fx.GameConfig);
if (button && cc.isValid(button)) button.active = this.levelGameplay.length > 0;
};
if (cc.fx.GameConfig.NEW_LEVEL) {
apply(cc.fx.GameConfig.NEW_LEVEL);
} else {
// 全局介绍配置是异步读取的,首次进场景时可能仍在加载。
if (button) button.active = false;
cc.resources.load("Json/NEW_LEVEL", cc.JsonAsset, (error, asset: cc.JsonAsset) => {
if (!this.isPopupRuntimeAlive()) return;
if (error) { cc.error("Gameplay introductions failed to load", error); return; }
apply(asset.json.NEW_LEVEL);
});
}
}
/**
* 问号 Button 的点击入口。场景按钮需绑定这个方法,Tip Panel 属性需绑定容器节点。
* 顺序:检查能否打开 → 暂停游戏 → 加载/复用预制体 → 交给 GameplayIntro 显示内容。
*/
openGameplayIntro() {
if (!this.isPopupRuntimeAlive() || this.isPauseOpen()) return;
if (!this.levelGameplay.length) return;
const map = MapConroler._instance;
if (!map || map.gameOver || map.shopPause) return;
if (!this.tipPanel || !cc.isValid(this.tipPanel)) {
cc.error("GameplayIntro: SceneManager.tipPanel is not assigned");
return;
}
// 关闭或加载失败时尝试恢复;仍有冰冻、其它暂停面板、商城或已结束时不能强行恢复。
const resume = () => {
if (!this.isPopupRuntimeAlive()) return;
if (!map.iceTrue() && !this.isPauseOpen() && !map.shopPause && !map.gameOver) {
map.pause = false;
if (map.gameStart) map.startBoom();
}
};
// 暂停游戏,并停止场上炸弹计时,避免阅读玩法期间仍消耗倒计时。
map.pause = true;
map.stopBoom();
this.tipPanel.active = true;
this.tipPanel.zIndex = cc.macro.MAX_ZINDEX - 2;
// 参数依次为:弹窗缓存名、资源包名、包内预制体路径、挂载父节点、完成回调。
// 通用加载器首次会 instantiate 并挂载到 tipPanel,以后返回已经创建的根节点。
this.loadDynamicPopup("gameplayIntro", "gameplay_intro", "prefab/GameplayIntro", this.tipPanel, (error, root) => {
// 无论成功失败都结束加载状态,防止暂停检测一直误认为还在加载。
this.finishDynamicPopupLoad("gameplayIntro");
if (error || !root) {
cc.error("GameplayIntro prefab failed to load", error);
resume();
return;
}
const panel = root.getComponent("GameplayIntro") as GameplayIntro;
if (!panel) {
cc.error("GameplayIntro prefab is missing its controller");
resume();
return;
}
// 同一实例只绑定一次关闭回调,避免重开多次后一次关闭触发多次恢复。
if (this.gameplayIntro !== panel) {
this.gameplayIntro = panel;
root.on("intro-closed", resume);
}
panel.open(this.levelGameplay);
});
}
startGame() {
let jg = cc.fx.GameTool.getAuthorizationAvatar();
if (!jg) {
cc.fx.GameTool.getUserAvatar(() => {
})
}
cc.director.loadScene("HomeScene", (err) => {
if (err) {
console.error('加载 HomeScene 场景失败:', err);
} else {
// console.log('成功切换到 HomeScene 场景');
cc.director.loadScene("HomeScene");
}
});
}
returnHome() {
if (!this.isPopupRuntimeAlive()) return;
const pauseRoot = this.getDynamicPopup("pause");
const pauseContent = pauseRoot && pauseRoot.getChildByName("pause");
const continueBtn = pauseContent && pauseContent.getChildByName("btn");
const continueControl = continueBtn && continueBtn.getComponent("btnControl");
if (continueControl && continueControl._touch && MapConroler && MapConroler._instance) {
continueControl.setTouch(false);
this.closePause();
if (MapConroler._instance.gameStart == true) {
// MiniGameSdk.API.showToast("体力值减少");
if (MapConroler._instance.count_Time) {
let overTime = Date.now();
let count_Time = overTime - MapConroler._instance.count_Time;
let add_Time = MapConroler._instance.add_Time;
let endLevel = false;
if (cc.fx.GameTool.maxLevel() && cc.fx.GameConfig.GM_INFO.GameplayType == 1) {
endLevel = true;
}
let data = {
time: count_Time,
add_Time: add_Time,
is_frenzy: MapConroler._instance.is_frenzy,
result: "give_up",
infinity: endLevel,
total_steps_count: MapConroler._instance.total_steps_count,
}
cc.fx.GameTool.shushu_Track("finish_stage", data);
let data2 = {
is_frenzy: MapConroler._instance.is_frenzy,
count: cc.fx.GameConfig.GM_INFO.winStreak
}
cc.fx.GameTool.shushu_Track("hammer_frenzy", data2);
}
}
cc.fx.AudioManager._instance.playEffect("zhuan1", null);
this.node.getChildByName("zhuanchang").active = true;
this.node.getChildByName("zhuanchang").getComponent(sp.Skeleton).setAnimation(1, "up", false);
cc.director.preloadScene("HomeScene", (err, asset) => {
if (err) {
console.error('动态加载 Prefab 失败:', err);
return;
}
});
setTimeout(() => {
cc.fx.GameConfig.GM_INFO.otherLevel = 0;
cc.fx.GameConfig.GM_INFO.otherUid = "";
let jg = cc.fx.GameTool.getAuthorizationAvatar();
if (!jg) {
cc.fx.GameTool.getUserAvatar(() => {
})
}
cc.director.loadScene("HomeScene");
}, 1200);
}
}
destroyNodesInFrames(nodes: cc.Node[], callback: () => void) {
const BATCH_SIZE = 10; // 每帧销毁的节点数量
let index = 0;
const destroyBatch = () => {
let count = 0;
while (index < nodes.length && count < BATCH_SIZE) {
const node = nodes[index];
if (node) {
node.active = false;
}
index++;
count++;
}
if (index < nodes.length) {
this.scheduleOnce(destroyBatch, 6);
} else {
callback();
}
};
destroyBatch();
}
// 改进后的切换场景方法
switchToEmptyScene() {
const allNodes = cc.director.getScene().children;
this.destroyNodesInFrames(allNodes, () => {
let jg = cc.fx.GameTool.getAuthorizationAvatar();
if (!jg) {
cc.fx.GameTool.getUserAvatar(() => {
})
}
cc.director.loadScene("HomeScene");
});
}
private returningHome: boolean = false;
returnHomeScene(event?: cc.Event, customEventData?: string) {
if (!this.isPopupRuntimeAlive() || this.returningHome) return;
this.returningHome = true;
cc.fx.GameConfig.locatePositionOnHome = customEventData === "ruzhi";
let started = false;
const goHome = () => {
if (started || !this.isPopupRuntimeAlive()) return;
started = true;
const accepted = cc.director.loadScene("HomeScene", (error) => {
if (error && this.isPopupRuntimeAlive()) {
this.returningHome = false;
cc.error("returnHomeScene failed:", error);
}
});
if (!accepted) this.returningHome = false;
};
try {
cc.fx.GameTool.shushu_Track("where_am_i", {});
} catch (error) {
cc.warn("where_am_i tracking failed:", error);
}
try {
if (customEventData === "ruzhi" && !cc.fx.GameTool.getAuthorizationAvatar()) {
// 授权结果不影响返回首页,也不等待头像昵称上传。
cc.fx.GameTool.getUserAvatar(goHome);
} else {
goHome();
}
} catch (error) {
cc.warn("returnHomeScene avatar request failed:", error);
goHome();
}
}
openPause() {
if (!this.isPopupRuntimeAlive()) return;
cc.fx.AudioManager._instance.playEffect("anniu_little", null);
const pauseControl = this.pause && this.pause.getComponent("btnControl");
if (!pauseControl || !pauseControl._touch) return;
pauseControl.setTouch(false);
if (MapConroler && MapConroler._instance) {
MapConroler._instance.pause = true;
MapConroler._instance.stopBoom();
}
this.loadPausePanel();
}
private loadPausePanel() {
if (!this.isPopupRuntimeAlive()) return;
this.loadDynamicPopup("pause", "pause", "prefab/Pause", this.node, (error, pauseRoot) => {
if (!this.isPopupRuntimeAlive()) return;
if (error || !pauseRoot) {
this.showPauseLoadError(error || new Error("Pause prefab is missing"));
return;
}
try {
if (!this.bindPausePanel(pauseRoot)) throw new Error("Pause runtime binding failed");
const pauseContent = pauseRoot.getChildByName("pause");
pauseRoot.active = true;
pauseContent.scale = 0.3;
cc.tween(pauseContent)
.to(0.2, { scale: 1.05 }, { easing: 'backOut' })
.to(0.15, { scale: 1.0 }, { easing: 'sineOut' })
.start();
this.finishDynamicPopupLoad("pause");
} catch (bindError) {
this.releasePausePanel(true);
this.showPauseLoadError(bindError as Error);
}
});
}
private showPauseLoadError(error: Error) {
if (!this.isPopupRuntimeAlive()) return;
cc.error("Pause cannot open:", error && (error.message || error));
this.showDynamicPopupLoadError("pause", "暂停界面资源", () => this.loadPausePanel());
}
closeShop() {
if (this.shopNode) {
this.shopNode.active = false;
if (MapConroler && MapConroler._instance) {
MapConroler._instance.shopPause = false;
var pause = MapConroler._instance.iceTrue();
if (pause == false && !this.isPauseOpen()) {
if (MapConroler && MapConroler._instance) MapConroler._instance.pause = false;
if (MapConroler._instance.gameStart == true) MapConroler._instance.startBoom();
}
}
}
}
/** 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
|| cc.fx.GameConfig.GM_INFO.jungleTreasureAvailable !== true
|| level < cc.fx.GameConfig.getJungleTreasureUnlockLevel()
|| 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;
if (data && data.available === false) {
cc.fx.GameConfig.GM_INFO.jungleTreasureAvailable = false;
this.closeLoad();
return;
}
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;
cc.fx.GameConfig.GM_INFO.jungleTreasureAvailable = true;
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.activityType !== undefined) state.activityType = data.activityType;
if (data.periodKey !== undefined) state.periodKey = data.periodKey;
if (data.startsAt !== undefined) state.startsAt = data.startsAt;
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.releasePausePanel();
if (MapConroler && MapConroler._instance) {
var pause = MapConroler._instance.iceTrue();
if (pause == false && !this.isPauseOpen() && !MapConroler._instance.shopPause) {
if (MapConroler && MapConroler._instance) MapConroler._instance.pause = false;
if (MapConroler._instance.gameStart == true) MapConroler._instance.startBoom();
}
}
}
openPropBuy(name: string) {
if (!this.isPopupRuntimeAlive()) return;
cc.fx.GameTool.tryReactivateStarterPack("level_purchase");
if (MapConroler && MapConroler._instance) {
MapConroler._instance.pause = true;
MapConroler._instance.stopBoom();
}
this.btnName = name;
cc.fx.AudioManager._instance.playEffect("tanchuang", null);
const gameRoot = this.node && this.node.getChildByName("Game");
this.loadDynamicPopup("propWindow", "propWindow", "prefab/PropWindow", gameRoot, (error, propRoot) => {
if (!this.isPopupRuntimeAlive()) return;
if (error || !propRoot) {
this.showPropWindowLoadError(error || new Error("propWindow prefab is missing"), name);
return;
}
try {
if (!this.bindPropWindowPanel(propRoot)) throw new Error("propWindow runtime binding failed");
const prop = propRoot.getChildByName("prop");
const target = prop && prop.getChildByName(name);
if (!target) throw new Error(`propWindow item is missing: ${name}`);
["freeze", "hammer", "magic"].forEach((itemName) => {
prop.getChildByName(itemName).active = itemName === name;
});
prop.getChildByName("buy_Btn").getComponent("btnControl").setTouch(true);
propRoot.active = true;
prop.scale = 0.3;
cc.tween(prop)
.to(0.2, { scale: 1.05 }, { easing: 'backOut' })
.to(0.15, { scale: 1.0 }, { easing: 'sineOut' })
.start();
this.finishDynamicPopupLoad("propWindow");
} catch (bindError) {
this.releasePropWindowPanel(true);
this.showPropWindowLoadError(bindError as Error, name);
}
});
}
private showPropWindowLoadError(error: Error, name: string) {
if (!this.isPopupRuntimeAlive()) return;
cc.error("propWindow cannot open:", error && (error.message || error));
this.showDynamicPopupLoadError(
"propWindow",
"道具购买资源",
() => this.openPropBuy(name)
);
}
clickBtn() {
if (!this.isPopupRuntimeAlive()) return;
cc.fx.AudioManager._instance.playEffect("anniu_Big", null);
const propRoot = this.getDynamicPopup("propWindow");
const prop = propRoot && propRoot.getChildByName("prop");
const buyBtn = prop && prop.getChildByName("buy_Btn");
const buyControl = buyBtn && buyBtn.getComponent("btnControl");
const map = MapConroler && MapConroler._instance;
if (buyControl && buyControl._touch && map && cc.isValid(map, true)) {
buyControl.setTouch(false);
if (this.btnName == "freeze")
map.buyFreeze();
else if (this.btnName == "hammer")
map.buyHammer();
else if (this.btnName == "magic")
map.buyMagic();
}
}
resetBtn() {
if (!this.isPopupRuntimeAlive()) return;
const propRoot = this.getDynamicPopup("propWindow");
const prop = propRoot && propRoot.getChildByName("prop");
const buyBtn = prop && prop.getChildByName("buy_Btn");
const buyControl = buyBtn && buyBtn.getComponent("btnControl");
if (buyControl) buyControl.setTouch(true);
}
/** Reuse the authored gift prefab when reactivation happens inside a level. */
openStarterPack() {
if (!this.isPopupRuntimeAlive() || this.starterPackOpening || starterPackRemaining() <= 0) return;
const existing = this.getDynamicPopup("starterPack");
if (existing && existing.active) return;
const map = MapConroler._instance;
if (!map || map.gameWin) return;
const wasPaused = map.pause;
const wasFrozen = map.iceTrue();
const wasShopPaused = map.shopPause;
map.pause = true;
map.shopPause = true;
map.stopBoom();
this.starterPackOpening = true;
const restore = () => {
if (!this.isPopupRuntimeAlive() || !cc.isValid(map.node)) return;
map.shopPause = wasShopPaused || !!(this.shopNode && this.shopNode.active);
const propWindow = this.getDynamicPopup("propWindow");
if ((!wasPaused || wasFrozen) && !map.shopPause && !map.gameOver && !map.gameWin
&& !(propWindow && propWindow.active)
&& !map.iceTrue() && !this.isPauseOpen()) {
map.pause = false;
if (map.gameStart) map.startBoom();
}
};
this.loadDynamicPopup("starterPack", "action_bundle", "prefab/newbieGift", this.node, (error, root) => {
this.starterPackOpening = false;
this.finishDynamicPopupLoad("starterPack");
if (error || !root || starterPackRemaining() <= 0 || map.gameWin) { restore(); return; }
root.zIndex = cc.macro.MAX_ZINDEX - 1;
root.active = true;
root.once("starter-pack-closed", restore);
root.getComponent("NewbieGift").init(!existing);
cc.fx.GameTool.shushu_Track("interface_exposure", {
interface_id: "NewbieGift", reason: "auto", is_first_exposure: !existing,
entry_point: "reactivate_level",
});
});
}
//打开商店
openShop(event, customEventData?: string) {
if (!this.isPopupRuntimeAlive()) return;
console.log("进入sceneManager openShop");
if (!cc.fx.GameConfig.GM_INFO.canIos) {
return;
}
cc.fx.GameTool.tryReactivateStarterPack("level_purchase");
SceneManager.clearInvalidShopPrefabCaches();
if (!SceneManager.cachedShopPrefab) {
cc.error('Shop prefab is not loaded yet.');
return;
}
Utils.getShopDouble(res => {
if (!this.isPopupRuntimeAlive()) return;
if (res.code === 1) {
let eventName = "";
let btnName = ""
if (customEventData !== undefined) {
btnName = customEventData;
eventName = "click"
} else {
btnName = "shop_level";
eventName = "auto";
}
let dataInfo = {
interface_id: "shop", // 界面名称
reason: eventName, // 事件名称
is_first_exposure: false, // 是否首次曝光
entry_point: btnName, // 按钮名称
}
cc.fx.GameTool.shushu_Track("interface_exposure", dataInfo);
cc.fx.GameConfig.GM_INFO.shopDouble = res.data.shopDouble;
if (MapConroler && MapConroler._instance) {
MapConroler._instance.shopPause = true;
MapConroler._instance.pause = true;
MapConroler._instance.stopBoom();
}
if (!this.shopNode) {
// 第一次使用,创建节点
this.shopNode = cc.instantiate(SceneManager.cachedShopPrefab);
if (this.node && this.shopNode)
this.node.addChild(this.shopNode);
this.shopNode.getComponent("shop").init(res.data.shopDouble);
} else {
// 非第一次使用,直接激活节点
this.shopNode.active = true;
this.shopNode.getComponent("shop").init(res.data.shopDouble);
}
// console.log("shopNode parent:", this.shopNode.parent);
}
});
}
//打开月卡
openMonthlyCard(customEventData?: string) {
SceneManager.clearInvalidShopPrefabCaches();
if (!SceneManager.cachedMonthlyCardPrefab) {
cc.error('MonthlyCard prefab is not loaded yet.');
return;
}
let eventName = "";
let btnName = ""
if (customEventData !== undefined) {
btnName = customEventData;
eventName = "click"
} else {
btnName = "yicon_level";
eventName = "auto";
}
let dataInfo = {
interface_id: "monthlyCard", // 界面名称
reason: eventName, // 事件名称
is_first_exposure: false, // 是否首次曝光
entry_point: btnName, // 按钮名称
}
cc.fx.GameTool.shushu_Track("interface_exposure", dataInfo);
if (!this.monthlyCardNode) {
// 第一次使用,创建节点
this.monthlyCardNode = cc.instantiate(SceneManager.cachedMonthlyCardPrefab);
if (this.node && this.monthlyCardNode)
this.node.addChild(this.monthlyCardNode);
this.monthlyCardNode.getComponent("monthlyCard").init();
this.monthlyCardNode.getComponent("monthlyCard").juwai = false;
} else {
// 非第一次使用,直接激活节点
this.monthlyCardNode.active = true;
this.monthlyCardNode.getComponent("monthlyCard").init();
this.monthlyCardNode.getComponent("monthlyCard").juwai = false;
}
}
updateCoin() {
MapConroler._instance.updateCoin();
}
closePropBuy(type) {
if (!this.isPopupRuntimeAlive()) return;
if (type == true) {
}
else {
if (MapConroler && MapConroler._instance) {
var pause = MapConroler._instance.iceTrue();
if (pause == false && !this.isPauseOpen() && !MapConroler._instance.shopPause) {
if (MapConroler && MapConroler._instance) MapConroler._instance.pause = false;
if (MapConroler._instance.gameStart == true) MapConroler._instance.startBoom();
}
}
}
if (MapConroler && MapConroler._instance) {
let freezeBtn = MapConroler._instance.node.parent.getChildByName("Bottom").getChildByName("timeBtn");
let hammerBtn = MapConroler._instance.node.parent.getChildByName("Bottom").getChildByName("destroyBtn");
let magicBtn = MapConroler._instance.node.parent.getChildByName("Bottom").getChildByName("magicBtn");
if (this.btnName == "freeze") freezeBtn.getComponent("btnControl").setTouch(true);
else if (this.btnName == "hammer") hammerBtn.getComponent("btnControl").setTouch(true);
else if (this.btnName == "magic") magicBtn.getComponent("btnControl").setTouch(true);
}
this.releasePropWindowPanel();
}
openRewardWindow(data, callBack) {
// console.log("_____________________打开奖励弹窗", data);
SceneManager.clearInvalidShopPrefabCaches();
if (!SceneManager.cachedRewardPrefab) {
cc.error('Reward prefab is not loaded yet.');
return;
}
if (!this.RewardNode) {
// 第一次使用,创建节点
this.RewardNode = cc.instantiate(SceneManager.cachedRewardPrefab);
if (this.node && this.RewardNode)
this.node.addChild(this.RewardNode);
this.RewardNode.zIndex = 99;
this.RewardNode.getComponent("Reward").init(data, callBack);
}
else {
this.RewardNode.destroy();
this.RewardNode = null;
this.RewardNode = cc.instantiate(SceneManager.cachedRewardPrefab);
if (this.node && this.RewardNode)
this.node.addChild(this.RewardNode);
this.RewardNode.zIndex = 99;
this.RewardNode.getComponent("Reward").init(data, callBack);
}
this.RewardNode.zIndex = 1001;
}
shareFriend() {
// const loseRoot = this.getDynamicPopup("lose");
// if (loseRoot && loseRoot.active) {
// let shareTips = loseRoot.getChildByName("share_tips");
// // let isCheckboxChecked = cc.sys.localStorage.getItem("share_checkbox_state");
// let isCheckboxChecked = cc.fx.StorageMessage.getStorage("share_checkbox_state");
// if (isCheckboxChecked == 0 || isCheckboxChecked == "" || isCheckboxChecked == null || isCheckboxChecked == undefined) {
// shareTips.active = true;
// } else {
// this.shareFriend2();
// }
// }
// else {
// this.shareFriend2();
// }
}
shareFriend2() {
// const loseRoot = this.getDynamicPopup("lose");
// if (loseRoot && loseRoot.active) {
// let shareTips = loseRoot.getChildByName("share_tips");
// if (shareTips.active) {
// shareTips.active = false;
// let checkbox = shareTips.getChildByName("checkbox");
// let checkboxSelect = 0;
// if (checkbox.getComponent(cc.Toggle).isChecked) {
// checkboxSelect = 1;
// }
// cc.fx.StorageMessage.setStorage("share_checkbox_state", checkboxSelect);
// }
// }
// else {
// }
// if (this.share_time) {
// this.share_time = false;
// console.log("设置分享链接.......");
// let timeStamp = Date.now();
// let otherInfo = {
// timeStamp: timeStamp,
// otherLevel: (cc.fx.GameConfig.GM_INFO.level + 1),
// }
// cc.fx.StorageMessage.setStorage("otherLevel", otherInfo);
// MiniGameSdk.API.shareGame();
// setTimeout(() => {
// this.share_time = true;
// }, 5000);
// }
}
shareClose() {
// const loseRoot = this.getDynamicPopup("lose");
// const shareTips = loseRoot && loseRoot.getChildByName("share_tips");
// if (!shareTips) return;
// if (shareTips.active) {
// shareTips.active = false;
// let checkbox = shareTips.getChildByName("checkbox");
// let checkboxSelect = 0;
// if (checkbox.getComponent(cc.Toggle).isChecked) {
// checkboxSelect = 1;
// }
// cc.fx.StorageMessage.setStorage("share_checkbox_state", checkboxSelect);
// }
}
openLoad() {
const loading = this.ensureLoadingOnTop();
LoadingCatAnimation.play(loading);
}
closeLoad() {
LoadingCatAnimation.stop(this.node.getChildByName("Loading"));
}
openMessage() {
// let reddemCodeNode = cc.instantiate(this.messageNode);
// let messageScrpit = reddemCodeNode.getComponent("Message");
// let nodeType = "commentNode";
// console.log("打开留言界面", nodeType);
// let pos = 1;
// messageScrpit.showNode(nodeType, pos);
// this.node.addChild(reddemCodeNode);
}
openMessage2() {
// 检查 messageNode 是否已经分配了预制体
// if (!this.messageNode) {
// cc.error("messageNode 预制体未分配,请在编辑器中设置");
// return;
// }
// let reddemCodeNode = cc.instantiate(this.messageNode);
// let messageScrpit = reddemCodeNode.getComponent("Message");
// if (!messageScrpit) {
// cc.error("Message 组件未找到,请确保预制体中包含 Message 组件");
// return;
// }
// let nodeType = "likeComment";
// console.log("打开点赞界面", nodeType);
// let pos = 1;
// messageScrpit.showNode(nodeType, pos);
// this.node.addChild(reddemCodeNode);
}
update(dt) {
}
public setPic(url, node) {
cc.assetManager.loadRemote(url, { ext: '.png' }, (err, texture: cc.Texture2D) => {
if (texture && node) {
// node.getChildByName("pic").active = true;
var sprite = node.getComponent(cc.Sprite);
sprite.spriteFrame = new cc.SpriteFrame(texture);
}
else {
}
})
}
}