1981 lines
78 KiB
TypeScript
1981 lines
78 KiB
TypeScript
// 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 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 {
|
||
if (!bundle) return;
|
||
setTimeout(() => {
|
||
if (cc.fx.GameTool && cc.fx.GameTool.shouldRetainGameplayPopupBundlesForGameSceneReload
|
||
&& cc.fx.GameTool.shouldRetainGameplayPopupBundlesForGameSceneReload()) return;
|
||
const active = SceneManager.activeInstance;
|
||
if (active && cc.isValid(active, true)) 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;
|
||
}
|
||
|
||
/** 动态弹窗加载期间使用;层级高于业务弹窗,低于 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;
|
||
|
||
// 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;
|
||
|
||
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) return;
|
||
|
||
const loadedPauseBundle = cc.assetManager.getBundle("pause");
|
||
if (loadedPauseBundle) {
|
||
console.log("[PopupBundle] already loaded: pause (iOS low-memory warmup)");
|
||
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);
|
||
return;
|
||
}
|
||
console.log("[PopupBundle] loadBundle complete: pause (iOS low-memory warmup)");
|
||
});
|
||
}
|
||
|
||
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) 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 (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;
|
||
}
|
||
|
||
isPauseOpen(): boolean {
|
||
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.ensureLoadingOnTop();
|
||
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));
|
||
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 game = this.node.getChildByName("Game");
|
||
const gameNode = game && game.getChildByName("GameNode");
|
||
const top = gameNode && (gameNode.getChildByName("top") || gameNode.getChildByName("Top"));
|
||
const shop = top && top.getChildByName("shop");
|
||
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 GameNode/top/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;
|
||
});
|
||
}
|
||
|
||
start() {
|
||
|
||
}
|
||
|
||
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;
|
||
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);
|
||
}
|
||
|
||
|
||
//打开商店
|
||
openShop(event, customEventData?: string) {
|
||
if (!this.isPopupRuntimeAlive()) return;
|
||
console.log("进入sceneManager openShop");
|
||
if (!cc.fx.GameConfig.GM_INFO.canIos) {
|
||
return;
|
||
}
|
||
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 {
|
||
|
||
}
|
||
})
|
||
}
|
||
|
||
|
||
}
|