import { GAMEPLAY_ATLAS_PATHS } from "../Tool/GameplayAtlasFrames"; import { MiniGameSdk } from "../../Sdk/MiniGameSdk"; import { GameTool } from "../Tool/GameTool"; import BlockAssetManager from "../Tool/BlockAssetManager"; interface GameplayEntryPreparation { startedAt: number; downloads: Promise; release: () => void; } const { ccclass, property } = cc._decorator; @ccclass('GameConfig') export class GameConfig { //所有控制信息都通过GameAppStart内控制 private static _instance: GameConfig = null; static GAME_DATA: any[]; //关卡数据 //用户需要存储在本地的数据, 金币,关卡等级,体力值以及恢复,道具数量。 static CUSTOM_INFO: { moveSpeed: number; //洪峰移动速度 waitTime: number; //洪峰冲击倒计时 fastPath: number; //最短路径 }[]; static TIME_INFO: { waterSpeed: number[]; //洪峰速度 totalTime: number; //游戏总时长 waterTime: number[]; //洪峰来的倒计时 roadSpeed: number; //河道修筑速度 ReinforceSpeed: number; //加固时间速度 Xi_SoilSpeed: number; }; //用于盛放埋点数据上传,每次上传后清空 static CLICK_DATA: { type: number; //上传数据类型 success: boolean; //此局游戏正确与否 round: number; //回合数轮次 getScore: number; //本轮得分,失败为0分 cumulativeScore: number; //本剧游戏总计得分 mapId: string; //地图ID difficulty: number; //地图难度 optimizedSteps: number; //关卡最短步数 usedSteps: number; //玩家修建总步数 stepList: any[]; //U、D、L、R X 息壤 F 加固 startTime: number; //从游戏开始到玩家操作的第一步截止时间 单位毫秒 submitTime: number; //从第一步操作到提交时间,如果未提交为-1 单位毫秒 duration: number; //游戏总用时(截止到提交) 单位毫秒 drawingBack: number; //后退次数 drawingReset: number; //清空次数 timer: number; //游戏倒计时剩余时长 秒 }; static TIP_ERR: string[]; //修筑失败错误提示 static BLOCK_INFO: {}[][]; //static WALL_INFO: ({ id: number; num: number; block: number; color: number; type: number; special: number; length: number; }[] | { id: number; num: number; block: number; color: number; type: number; special: number; }[])[]; static LEVEL_INFO: ({ id: string; map: number[]; risefall?: undefined; gap?: undefined; } | { id: string; map: number[]; risefall: { pos: cc.Vec2; color: number; }[]; gap?: undefined; } | { id: string; map: number[]; gap: cc.Vec2[]; risefall?: undefined; })[]; static WALL_INFO: (({ id: number; num: number; block: number; color: number; special: number; length: number; lock?: undefined; } | { id: number; num: number; block: number; color: number; special: number; length: number; lock: boolean; })[] | ({ id: number; num: number; block: number; color: number; special: number; length: number; freeze?: undefined; } | { id: number; num: number; block: number; color: number; special: number; length: number; freeze: number; })[])[]; static PROP_INFO: ({ pos1: { x: number; y: number; z: number; }; pos2: { x: number; y: number; z: number; }; pos3: { x: number; y: number; z: number; }; pos4: { x: number; y: number; z: number; }; pos5: { x: number; y: number; z: number; }; pos6: { x: number; y: number; z: number; }; } | { pos1: { x: number; y: number; z: number; }; pos2: { x: number; y: number; z: number; }; pos3: { x: number; y: number; z: number; }; pos4: { x: number; y: number; z: number; }; pos5: { x: number; y: number; z: number; }; pos6?: undefined; })[]; private static defaultNewLevel: any[] = []; private static newPlayerNewLevel: any[] = []; private static defaultNewGuide: any[] = []; static get NEW_LEVEL(): any[] { return this.isNewPlayerB() ? this.newPlayerNewLevel : this.defaultNewLevel; } static get NEW_GUIDE(): any[] { return this.isNewPlayerB() ? this.defaultNewGuide.map(item => ({ ...item, level: this.getPropUnlockLevel(item.name) })) : this.defaultNewGuide; } static PASS_CHECK: any; static PASS_CHECK_OLD: any; private static defaultPassRate: any[] = []; private static newPlayerPassRate: any[] = []; static get PASS_RATE(): any[] { return this.isNewPlayerB() ? this.newPlayerPassRate : this.defaultPassRate; } static MESSAGE_MODEL: any; static LOCAL_CACHE_PREFIX = "level_cache_"; static MAX_LOCAL_LEVEL = 100; // 上传 Block 下的 PNG、三张公共图集对应的 plist 和 version.json,不使用 Bundle 构建产物。 static BLOCK_IMAGE_BASE_URL = "https://cdn.pay.nika4games.com/remote/Block/"; // 首次启动且远程版本不可用时的默认值;后续以远程 version.json 为准。 static BLOCK_IMAGE_VERSION = "1.0.0"; static nextEndlessLevel = 0; static locatePositionOnHome = false; // Win 入职按钮返回首页后定位一次。 private static loadedLevelNumber = 0; private static levelLoadRequest = 0; private static sceneLoadRequest = 0; private static homeWarmupRequest = 0; private static cancelBlockLoading: () => void = null; private static gameplayScenePreload: Promise = null; private static gameplaySceneForegroundPreload: Promise = null; private static pendingEntryDownloadRelease: () => void = null; static gameplayEntryStartedAt = 0; // 仅本次启动有效;地图就绪即可完成首次进关,不等待弹窗实例。 static firstGameplayEntryComplete = false; static firstGameplayEntryPending = false; /** 仅合并正在进行的预加载,不能把历史完成标记当作永久有效的场景缓存。 */ static preloadGameplayScene(foreground = false): Promise { const key = foreground ? "gameplaySceneForegroundPreload" : "gameplayScenePreload"; if (this[key]) return this[key]; const request = this.timeGameplayStage(foreground ? "GameScene前台文件准备" : "首页GameScene预取", () => new Promise((resolve, reject) => { const done = (error: Error) => error ? reject(error) : resolve(); const bundle = cc.assetManager.bundles.find(bundle => !!bundle.getSceneInfo("GameScene")); if (!bundle) { reject(new Error("GameScene 所属 Bundle 尚未加载")); return; } // 不等待首页的低优先级任务;引擎复用已下载文件,前台请求使用 scene 调度配置。 bundle.preloadScene("GameScene", { preset: "scene", priority: 10 }, done); })); this[key] = request; const clear = () => { if (this[key] === request) this[key] = null; }; request.then(clear, clear); return request; } static async timeGameplayStage(stage: string, task: () => Promise): Promise { const start = Date.now(); // console.log(`[进关耗时][${stage}] 开始`); try { const result = await task(); // console.log(`[进关耗时][${stage}] ${result === false ? "未就绪" : "完成"},独立耗时=${Date.now() - start}ms`); return result; } catch (error) { // console.log(`[进关耗时][${stage}] 失败,独立耗时=${Date.now() - start}ms`, error); throw error; } } /** 点击进关即开始不依赖关卡 JSON 的任务,所有入口共用原有下载保障。 */ static beginGameplayEntryPreparation(): GameplayEntryPreparation { const startedAt = Date.now(); this.gameplayEntryStartedAt = startedAt; const previousRelease = this.takeEntryDownloadRelease(); const releaseHold = BlockAssetManager.instance.holdBackgroundDownloads(); if (previousRelease) previousRelease(); const release = () => { releaseHold(); if (this.pendingEntryDownloadRelease === release) this.pendingEntryDownloadRelease = null; }; this.pendingEntryDownloadRelease = release; // console.log("[进关耗时] 开始进关准备(各并行阶段耗时不能相加)"); const downloads = this.timeGameplayStage("五个弹窗分包磁盘检查/下载", () => GameTool.ensureGameplayPopupSubpackagesDownloaded()).catch(() => false); // 不把文件预取作为门槛;必要资源就绪后 loadScene 立即正式加载并复用已有文件。 this.preloadGameplayScene(true).catch(error => cc.warn("场景文件预取失败,正式加载时重试", error)); return { startedAt, downloads, release }; } /** 新 Map 接管自己的暂停凭证,旧 Map 销毁不能恢复新进关的后台派发。 */ static takeEntryDownloadRelease(): () => void { const release = this.pendingEntryDownloadRelease; this.pendingEntryDownloadRelease = null; return release; } /** 首页仅用来判断能否启动自动预加载,不暂停或接管任何在途回调。 */ static isGameplayEntryPreparing(): boolean { return !!this.pendingEntryDownloadRelease; } static shouldPrepareFirstGameplayEntry(): boolean { const scene = cc.director.getScene(); return !this.firstGameplayEntryComplete && !!scene && scene.name === "HomeScene" && GameTool.shouldPreloadGameplayPopupInstances(); } static getCurrentLevelBlockDefinitions(): any[] { const blocks = this.BLOCK_INFO && this.BLOCK_INFO[0] ? this.BLOCK_INFO[0] : []; const level: any = this.LEVEL_INFO && this.LEVEL_INFO[0]; return this.collectLevelBlockDefinitions(blocks, level); } private static collectLevelBlockDefinitions(blocks: any[], level: any): any[] { const result = blocks.slice(); const gates = level && Array.isArray(level.spawnGates) ? level.spawnGates : []; for (const gate of gates) { const queue = gate && Array.isArray(gate.spawnQueue) ? gate.spawnQueue : []; for (const item of queue) { if (item && Array.isArray(item.members)) result.push(...item.members); } } return result; } /** HomeScene 空闲预热:低内存 iOS 只落盘,高内存 iOS 和安卓直接创建纹理。 */ static warmupHomeBlockAssets(): Promise { const requestId = ++this.homeWarmupRequest; const scene = cc.director.getScene(); const otherLevel = Number(this.GM_INFO.otherLevel) || 0; const levelNumber = otherLevel > 0 ? otherLevel : Number(this.GM_INFO.level) + 1; const isCurrent = () => requestId === this.homeWarmupRequest && cc.director.getScene() === scene && !!scene && scene.name === "HomeScene"; if (!isCurrent() || levelNumber < 1 || levelNumber > GameTool.MAX_NORMAL_LEVEL) { return Promise.resolve(false); } return this.loadLevelFromBundle(levelNumber, otherLevel === 0).then(jsonData => { if (!isCurrent()) return false; const blockInfo = jsonData && jsonData.BLOCK_INFO && jsonData.BLOCK_INFO[0]; const levelInfo = jsonData && jsonData.LEVEL_INFO && jsonData.LEVEL_INFO[0]; if (!Array.isArray(blockInfo) || !levelInfo) { throw new Error(`关卡${levelNumber}配置无效`); } const blocks = this.collectLevelBlockDefinitions(blockInfo, levelInfo); if (GameTool.shouldPreloadGameplayPopupInstances()) { return BlockAssetManager.instance.prepareWindow(blocks, { currentLevel: levelNumber, stage: "首页内存预热", extraPaths: GAMEPLAY_ATLAS_PATHS, homeWarmup: true, }).then(ready => { if (ready && isCurrent()) { console.log(`[Block图片][首页内存预热] 当前关${levelNumber}完成`); } return ready && isCurrent(); }); } return BlockAssetManager.instance.cacheWindowFiles(blocks, GAMEPLAY_ATLAS_PATHS); }).catch(error => { if (isCurrent()) cc.warn(`[Block图片][首页预热] 当前关${levelNumber}失败`, error); return false; }); } /** 只准备当前关图片,不预读或加载下一关图片。 */ static prepareBlockWindow(onRemoteRequired?: () => void, stage = "进关"): Promise { const blockInfo = this.BLOCK_INFO && this.BLOCK_INFO[0]; const levelInfo = this.LEVEL_INFO && this.LEVEL_INFO[0]; if (!blockInfo || !this.loadedLevelNumber) return Promise.reject(new Error("关卡配置尚未就绪")); const blocks = this.getCurrentLevelBlockDefinitions(); const currentLevel = this.loadedLevelNumber; return BlockAssetManager.instance.prepareWindow(blocks, { onRemoteRequired, currentLevel, stage, extraPaths: GAMEPLAY_ATLAS_PATHS }).then(ready => ready && this.BLOCK_INFO[0] === blockInfo && this.LEVEL_INFO[0] === levelInfo); } /** 创建时不显示;仅远程图片调用 open。失效请求不能打开或关闭后续请求的 Loading。 */ static createBlockLoading(isCurrent: () => boolean): { open: () => void; close: () => void } { if (this.cancelBlockLoading) this.cancelBlockLoading(); const scene = cc.director.getScene(); const canvas = cc.find("Canvas"); const controller: any = canvas && (canvas.getComponent("JiaZai") || canvas.getComponent("SceneManager")); let opened = false; let closed = false; const close = () => { if (closed) return; closed = true; if (opened && controller && cc.isValid(controller, true)) controller.closeLoad(); if (this.cancelBlockLoading === close) this.cancelBlockLoading = null; }; this.cancelBlockLoading = close; return { open: () => { if (!closed && !opened && isCurrent() && cc.director.getScene() === scene && controller && cc.isValid(controller, true)) { opened = true; controller.openLoad(); } }, close }; } /** 所有正式进关入口都在弹窗分包及当前关图片就绪后才切换 GameScene。 */ static enterGameSceneWhenReady(time = 0, preparation?: GameplayEntryPreparation): void { const id = ++this.sceneLoadRequest; const scene = cc.director.getScene(); const blocks = this.BLOCK_INFO; const isCurrent = () => id === this.sceneLoadRequest && cc.director.getScene() === scene && this.BLOCK_INFO === blocks; const loading = this.createBlockLoading(isCurrent); const firstEntry = this.shouldPrepareFirstGameplayEntry(); const entry = preparation || this.beginGameplayEntryPreparation(); const { release, startedAt } = entry; if (firstEntry || !GameTool.areGameplayPopupSubpackagesDownloaded()) loading.open(); // 只等待磁盘分包与当前关图片,不等待后台场景预加载回调。 Promise.all([ entry.downloads, this.timeGameplayStage("当前关方块+公共图集", () => this.prepareBlockWindow(loading.open)), ]).then(([popupReady, ready]) => { if (!isCurrent()) { loading.close(); release(); return; } if (!popupReady) { loading.close(); release(); const missing = GameTool.getMissingGameplayPopupSubpackages(); this.showBlockLoadError( new Error(`游戏弹窗分包下载失败: ${missing.join(", ")}`), () => this.enterGameSceneWhenReady(time), "游戏界面资源下载失败\n请检查网络,点击此处重试" ); return; } if (!ready) { loading.close(); release(); return; } // console.log(`[进关耗时][进关门槛就绪] 从开始累计=${Date.now() - startedAt}ms`); loading.close(); if (firstEntry) { const home = cc.find("Canvas").getComponent("JiaZai"); home.playGameEntryTransition(true); } const transitionStartedAt = Date.now(); setTimeout(() => { if (!isCurrent()) { release(); return; } const retainPopupBundles = !!scene && scene.name === "GameScene" && GameTool.beginGameplayPopupGameSceneReloadRetention(); // console.log(`[进关耗时][转场等待] 独立耗时=${Date.now() - transitionStartedAt}ms`); const sceneStartedAt = Date.now(); // console.log("[进关耗时][GameScene正式加载及激活] 开始"); try { if (firstEntry) this.firstGameplayEntryPending = GameTool.shouldPreloadGameplayPopupInstances(); const accepted = cc.director.loadScene("GameScene", (error: Error) => { if (retainPopupBundles) { GameTool.finishGameplayPopupGameSceneReloadRetention(); } if (error) { // console.log(`[进关耗时][GameScene正式加载及激活] 失败,独立耗时=${Date.now() - sceneStartedAt}ms`, error); release(); if (isCurrent()) { this.firstGameplayEntryPending = false; this.showBlockLoadError(error, () => this.enterGameSceneWhenReady(time)); } } else { // console.log(`[进关耗时][GameScene正式加载及激活] 完成,独立耗时=${Date.now() - sceneStartedAt}ms,从开始累计=${Date.now() - startedAt}ms`); } }); if (accepted === false) { release(); if (retainPopupBundles) GameTool.finishGameplayPopupGameSceneReloadRetention(); this.firstGameplayEntryPending = false; this.showBlockLoadError(new Error("GameScene 切换未启动"), () => this.enterGameSceneWhenReady(time)); } } catch (error) { release(); if (firstEntry) this.firstGameplayEntryPending = false; if (retainPopupBundles) { GameTool.finishGameplayPopupGameSceneReloadRetention(); } this.showBlockLoadError(error, () => this.enterGameSceneWhenReady(time)); } }, firstEntry ? Math.max(time, 1000) : time); }).catch(error => { loading.close(); release(); if (isCurrent()) this.showBlockLoadError(error, () => this.enterGameSceneWhenReady(time)); }); } static getLoadedBlockImagePaths(): string[] { return BlockAssetManager.instance.getLoadedPaths(); } /** 不推进关卡、不无限卡在转场;此提示不依赖任何远程图片。 */ static showBlockLoadError(error: any, retry: () => void, messageText = "方块图片加载失败\n请检查网络,点击此处重试"): void { cc.error(messageText, error); const canvas = cc.find("Canvas"); if (!canvas || canvas.getChildByName("BlockLoadRetry")) return; const overlay = new cc.Node("BlockLoadRetry"); overlay.parent = canvas; overlay.zIndex = cc.macro.MAX_ZINDEX; overlay.setContentSize(canvas.getContentSize()); overlay.addComponent(cc.BlockInputEvents); 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 message = new cc.Node("message"); message.parent = overlay; const label = message.addComponent(cc.Label); label.string = messageText; label.fontSize = 32; label.lineHeight = 48; label.horizontalAlign = cc.Label.HorizontalAlign.CENTER; overlay.once(cc.Node.EventType.TOUCH_END, () => { overlay.removeFromParent(); overlay.destroy(); retry(); }); } // 旧 layer_two 实验以 A 方案结束,旧 B 实现继续保持关闭;layer_2 现用于 Jungle 解锁实验。 static readonly LEGACY_LAYER_TWO_VARIANT_B_ENABLED = false; static readonly JUNGLE_TREASURE_UNLOCK_LEVEL = 40; static getJungleTreasureUnlockLevel(): number { return this.JUNGLE_TREASURE_UNLOCK_LEVEL; } static isNewPlayerB(): boolean { const assignment = this.GM_INFO.abTestAssignments && this.GM_INFO.abTestAssignments.layer_1; return !!assignment && assignment.name === "new1_50" && assignment.enabled !== false && ["B", "2"].includes(String(assignment.group).toUpperCase()); } static getLevelConfigPath(level: number, mainline: boolean = true): string { const folder = mainline && this.isNewPlayerB() && level >= 1 && level <= 50 ? "Json2" : "Json"; return `${folder}/level${level}`; } static getPropUnlockLevel(prop: string): number { if (prop === "hammer") return 8; if (prop === "time") return this.isNewPlayerB() ? 14 : 11; return this.isNewPlayerB() ? 18 : 16; } // 以下功能门槛均使用已通关数。 static getFeatureUnlockLevel(feature: string): number { const levels = this.isNewPlayerB() ? { month: 20, pass: 20, daily: 35, winStreak: 50 } : { month: 16, pass: 21, daily: 0, winStreak: 17 }; return levels[feature]; } static isWinStreakUnlocked(): boolean { return !this.isNewPlayerB() || this.GM_INFO.level >= 50; } static shouldShowWinStreakGuide(): boolean { return !!this.GM_INFO.winStreakFirst && this.isWinStreakUnlocked() && (!this.isNewPlayerB() || this.GM_INFO.winStreak >= 10); } static canSettleCareer(): boolean { return !this.isNewPlayerB() || this.GM_INFO.level > 15; } static TA: any; static GE: any; static CITY: { 河北省: boolean; 山西省: boolean; 辽宁省: boolean; 吉林省: boolean; 黑龙江省: boolean; 江苏省: boolean; 浙江省: boolean; 安徽省: boolean; 福建省: boolean; 江西省: boolean; 山东省: boolean; 河南省: boolean; 湖北省: boolean; 湖南省: boolean; 广东省: boolean; 海南省: boolean; 四川省: boolean; 贵州省: boolean; 云南省: boolean; 陕西省: boolean; 甘肃省: boolean; 青海省: boolean; 港澳台: boolean; 北京: boolean; 天津: boolean; 上海: boolean; 重庆: boolean; 内蒙古: boolean; 广西: boolean; 西藏: boolean; 宁夏: boolean; 新疆: boolean; }; static GM_INFO: { // isEnd: false, error_message: any[]; // 错误信息 mean_Time: number; //平均放箭速度 hp: number; //体力值 review: number; //复活次数 reviewBoom: number; //炸弹复活次数 reviewDoor: number; //门复活次数 reviewRevolving: number; //转盘复活次数 reviewLongAndShort: number; //长短期复活次数 currSeed: number; //用于随机数种子 openid: string; //微信用户唯一id gameId: string; //游戏ID userId: number; //用户ID scode: string; //用户code,从网页后缀获取 username: string; //用户名称 useravatar: string; //用户头像地址 useravatarIcon: string; //用户头像 useravaterkuang: string; //用户头像框 useravatarCat: string; //用户猫 testId: string //测试ID guide: boolean; //是否有引导 url: string; //访问域名 coin: number; //用户金币 film: number; //头像抽卡代币 catArr: any[]; //用户猫数组 isFromWucai: boolean; //本次进入是否来自五彩消消乐 wucai_type: number; //五彩账号迁移状态:-1未参与,0待领奖,1待选择,2/3已完成 wucai_preview: any; //状态1登录时返回的五彩脱敏数据 wucai_current_preview: any; //状态1登录时整理的本游戏进度预览 wucai_reward_popup_pending: boolean; //选择进度后,HomeScene 奖励弹窗待确认 jungleTreasureState?: any; //登录返回的丛林宝藏状态;未触发活动的用户不存在此属性 jungleTreasureServerNow?: number; //登录接口返回状态时的服务器时间 jungleTreasureAvailable?: boolean; //本次服务端读取是否存在可展示的丛林宝藏活动 pendingJungleSettlement?: any; //到期奖励已入账、等待客户端展示并确认的结算 success: boolean; //用户游戏成功与否 matchId: any; //用于埋点上传的ID gameState: boolean; custom: number; //用于测试跳关卡 round: number; //回合数 level: number; //具体游戏内进行到第几步 stepTimeList: number; //整局游戏用时,由于涉及场景切换,数据需要保留 successList: any[]; //整局胜负 first: boolean; //是否首次进入游戏 score: number; //总得分 scale: number; //玩家总计成功点火数 allOutTradeNo: any[]; //订单号数组 min_Time: number; //体力恢复的剩余时间 freezeAmount: number; //冻结道具次数 hammerAmount: number; //锤子道具次数 magicAmount: number; //魔法棒道具次数 musicOpen: boolean; //音乐 effectOpen: boolean; //音效 vibrateOpen: boolean; //震动 coinnum: number; //每局的金币数 paid_user: boolean; //是否是付费用户 version: string; //版本号 shushu_DistinctId: string; //数数访客ID shushu_AccountId: string; //数数账号ID uid: string; //用户和后端唯一id userPowerTime: number; //用户体力恢复时间 freezeFirst: boolean; //冻结道具第一次 hammerFirst: boolean; //锤子道具第一次 magicAFirst: boolean; //魔法棒道具第一次 winStreakFirst: boolean; //每次第一次连胜 hp_Max: number; //体力最大值 doubleCoin: number; //是否双倍 rewardAdCoin: number; //是否双倍 monthTime: number; //月卡时间 revive: number; //复活礼包购买次数 otherUid: string; //其他被帮助用户id otherLevel: number; //其他被帮助用户等级 helpLevel: number; //帮助用户等级 iosShopOrder: string; //ios订单号商城 iosJungleOrder: string; //iOS 客服旧支付的丛林宝藏订单号 iosMonthOrder: string; //ios订单号月卡 iosReviveOrder: string; //ios订单号复活 iosStarterOrder: string; //ios订单号新手 iosPassCheckOrder: string; // ios通行证订单 starter_packTime: number; // 新手礼包截止时间,秒 starterPackServerOffset: number; // 服务端时间与本地时间差,毫秒 starterPackPurchased: boolean; starterPackLastShownAt: number; // 最近一次实际弹出时间,毫秒 starterPackReactivatedDeadline: number; // 本次会话已确认的新周期截止时间,毫秒 wxFriend: boolean; //微信好友信息授权 wxUserInfo: boolean; //微信用户信息授权 winState: boolean; //是否延续连胜状态 getProgressLevel: number; //获取进度关卡 getProgress: number; //获取进度 getItemType: number; popPassCheck: boolean; // 弹窗通行证 passCheckActivate: boolean; winStreak: number; //连胜 tasks: { levelPass: { value: number; target: number; state: number; }; share: { value: number; target: number; state: number; }; useEnergy: { value: number; target: number; state: number; }; useProp: { value: number; target: number; state: number; }; }; addLevel: number; //每日过关数 address: string; //用户城市信息 firstTime: number; //用户注册时间 longitude: string; //用户经度 cityRank: number; //城市排名 sceneValue: string; //当前场景值 shopDouble: { gold_1: boolean; gold_2: boolean; gold_3: boolean; gold_4: boolean; gold_5: boolean; gold_6: boolean; }; randomLevel: number; endLevelNum: number; // 通关后 记录玩家随机游戏关卡数量 GameplayType: number; //游戏类型 canIos: boolean, //ios展示是否可以 iosCanPay: boolean, //ios是否支持支付 ge_ad_id: string, //广告ID ge_creative_id: string, //创意ID abTests: { layer_one: number, layer_two: number, layer_three: number }, //AB测试 abTestAssignments: { [key: string]: any }, // AB测试原始分组信息 }; static bgPosition: number; //游戏内信息 static get Instance() { if (this._instance == null) { this._instance = new GameConfig(); } return this._instance; } //getSeedRandom static init(Authentication) { //@ts-ignore this.TA = null; this.CLICK_init(); this.GM_INFO_init(); this.CITY_init(); this.LEVEL_INFO_init(false, 0, false); this.bgPosition = 0; var self = this; //GAME_DATA 废弃了,暂时不删除以防后面修改回 一整局传一次 this.GAME_DATA = [ ] // if(!Authentication) self.Authentication(); } static generateUUID(): string { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { const r = Math.random() * 16 | 0; const v = c === 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); }); } // 版本比较函数 static compareVersion(v1: string, v2: string): number { const arr1 = v1.split('.'); const arr2 = v2.split('.'); const len = Math.max(arr1.length, arr2.length); for (let i = 0; i < len; i++) { const num1 = parseInt(arr1[i] || '0', 10); const num2 = parseInt(arr2[i] || '0', 10); if (num1 > num2) { return 1; } else if (num1 < num2) { return -1; } } return 0; } static GM_INFO_SET(key, value) { this.GM_INFO[key] = value; } static CLICK_init() { this.CLICK_DATA = { type: 1, //上传数据类型 success: false, //此局游戏正确与否 round: 0, //回合数轮次 getScore: 0, //本轮得分,失败为0分 cumulativeScore: 0, //本剧游戏总计得分 mapId: "", //地图ID difficulty: 0, //地图难度 optimizedSteps: 0, //关卡最短步数 usedSteps: 0, //玩家修建总步数 stepList: [], //U、D、L、R X 息壤 F 加固 startTime: -1, //从游戏开始到玩家操作的第一步截止时间 单位毫秒 submitTime: -1, //从第一步操作到提交时间,如果未提交为-1 单位毫秒 duration: 0, //游戏总用时(截止到提交) 单位毫秒 如果未他移交则为时间上限 drawingBack: 0, //后退次数 drawingReset: 0, //清空次数 timer: 0 //测评倒计时剩余时间 去秒 } } static CLICK_SET(key, value) { this.CLICK_DATA[key] = value; } //数据备用 static GM_INFO_init() { this.GM_INFO = { // isEnd: false, error_message: [], // 错误信息 mean_Time: 0, //平均放箭速度 hp: 5, //体力值 review: 0, //复活次数 reviewBoom: 0, //炸弹复活次数 reviewDoor: 0, //门复活次数 reviewRevolving: 0, //转盘复活次数 reviewLongAndShort: 0, //长短期复活次数 currSeed: 200000, //用于随机数种子 openid: "", //微信用户唯一id gameId: "100001", //游戏ID userId: 0, //用户ID scode: "", //用户code,从网页后缀获取 username: "user", //用户名称 useravatar: "", //用户头像地址 useravatarIcon: "icon_0", //用户头像 useravaterkuang: "kuang_1", //用户头像框 useravatarCat: "cat_1", //用户头像框 guide: true, //是否有引导 url: "https://api.sparkus.cn",//访问域名 coin: 0, //用户金币 film: 0, //头像抽卡代币(以服务端返回值为准) catArr: [1, 2, 3], //已拥有猫咪(登录返回,首页抽猫红点本地判断使用) isFromWucai: false, //本次进入是否来自五彩消消乐 wucai_type: -1, //五彩账号迁移状态 wucai_preview: null, //五彩账号脱敏预览 wucai_current_preview: null,//本游戏进度预览 wucai_reward_popup_pending: false,//五彩迁移奖励弹窗待确认 pendingJungleSettlement: null,//丛林宝藏到期奖励待展示结算 success: false, //用户游戏成功与否 matchId: null, //用于埋点上传的ID gameState: false, custom: 0, //用于测试跳关卡 round: 0, //回合数 level: 0, //具体游戏内进行到第几步 stepTimeList: 0, //整局游戏用时,由于涉及场景切换,数据需要保留 successList: [], //整局胜负 first: false, //是否首次进入游戏 score: 0, //总得分 scale: 1, //玩家总计成功点火数 allOutTradeNo: [], //订单号数组 min_Time: 0, //体力恢复的剩余时间 freezeAmount: 0, //冻结道具次数 hammerAmount: 0, //锤子道具次数 magicAmount: 0, //魔法棒道具次数 musicOpen: true, //音乐 effectOpen: true, //音效 vibrateOpen: true, //震动 coinnum: 0, //每局的金币数 paid_user: false, //是否是付费用户 version: "1.0.56", //版本号 shushu_DistinctId: "", //数数访客ID shushu_AccountId: "", //数数账号ID uid: "", //用户和后端唯一id userPowerTime: 0, //用户体力恢复时间 freezeFirst: false, //冻结道具第一次 hammerFirst: false, //锤子道具第一次 magicAFirst: false, //魔法棒道具第一次 winStreakFirst: false, //每次第一次连胜 hp_Max: 5, //体力最大值 doubleCoin: 1, //是否双倍 rewardAdCoin: 1, //是否双倍 monthTime: 0, //月卡时间 revive: 0, //复活礼包购买次数 otherUid: "", //其他被帮助用户id otherLevel: 0, //其他被帮助用户等级 helpLevel: 0, //帮助用户等级 iosShopOrder: "", //ios订单号商城 iosJungleOrder: "", //iOS 客服旧支付的丛林宝藏订单号 iosMonthOrder: "", //ios订单号月卡 iosReviveOrder: "", //ios订单号复活 iosStarterOrder: "", //ios订单号新手 iosPassCheckOrder: "", // ios通行证订单 starter_packTime: 0, // 新手礼包截止时间 starterPackServerOffset: 0, starterPackPurchased: false, starterPackLastShownAt: 0, starterPackReactivatedDeadline: 0, wxFriend: false, //微信好友信息授权 wxUserInfo: false, //微信用户信息授权 winState: false, //是否延续连胜状态 getProgressLevel: 1, //获取进度关卡 getProgress: 0, //获取进度 getItemType: 2, popPassCheck: false, // 弹窗通行证 passCheckActivate: false, winStreak: 0, //连胜 tasks: { levelPass: { value: 0, target: 0, state: 0 }, share: { value: 0, target: 0, state: 0 }, useEnergy: { value: 0, target: 0, state: 0 }, useProp: { value: 0, target: 0, state: 0 } }, addLevel: 0, //每日过关数 address: "", //用户城市信息 firstTime: 0, //用户注册时间 longitude: "", //用户经度 cityRank: 99, //城市排名 sceneValue: "", //当前场景值 shopDouble: { "gold_1": false, "gold_2": false, "gold_3": false, "gold_4": false, "gold_5": false, "gold_6": false }, randomLevel: 0, endLevelNum: 1, testId: "", //测试ID GameplayType: 0, // 0 正常模式, 1无尽模式 canIos: true, //ios展示是否可以 iosCanPay: false, //ios是否支持支付 ge_ad_id: "", //广告ID ge_creative_id: "", //广告创意ID abTests: { layer_one: 0, layer_two: 0, layer_three: 0 }, //AB测试 abTestAssignments: {}, }; // this.setCode(this.getKey("scode")); // this.GM_INFO.level = 0; cc.resources.load("Json/NEW_LEVEL", (err: any, res: cc.JsonAsset) => { if (err) { console.error("加载 NEW_LEVEL.JSON 失败:", err); return; } const newLevelData = res.json; this.defaultNewLevel = newLevelData.NEW_LEVEL; // console.log("____________________配置", this.NEW_LEVEL); // 可以根据需求对 newLevelData 进行处理 // //console.log("NEW_LEVEL.JSON 加载成功:", newLevelData); }) cc.resources.load("Json/NEW_LEVEL2", (err: any, res: cc.JsonAsset) => { if (err) { console.error("加载 NEW_LEVEL2.JSON 失败:", err); return; } this.newPlayerNewLevel = res.json.NEW_LEVEL; }); cc.resources.load("Json/NEW_GUIDE", (err: any, res: cc.JsonAsset) => { if (err) { console.error("加载 NEW_GUIDE.JSON 失败:", err); return; } const newLevelData = res.json; this.defaultNewGuide = newLevelData.NEW_GUIDE; // 可以根据需求对 newLevelData 进行处理 // //console.log("NEW_GUIDE.JSON 加载成功:", newLevelData); }) cc.resources.load("Json/PASS_CHECK", (err: any, res: cc.JsonAsset) => { if (err) { console.error("加载 PASS_CHECK.JSON 失败:", err); return; } const newLevelData = res.json; cc.resources.load("Json/PASS_CHECK_OLD", (oldError: any, oldRes: cc.JsonAsset) => { if (oldError) { console.error("加载 PASS_CHECK_OLD.JSON 失败:", oldError); return; } this.PASS_CHECK_OLD = oldRes.json.Sheet1; this.PASS_CHECK = newLevelData.Sheet1; }); // 可以根据需求对 newLevelData 进行处理 // //console.log("PASS_CHECK.JSON 加载成功:", newLevelData); }) cc.resources.load("Json/PASS_RATE", (err: any, res: cc.JsonAsset) => { if (err) { console.error("加载 PASS_RATE.JSON 失败:", err); return; } const newLevelData = res.json; this.defaultPassRate = newLevelData; }) cc.resources.load("Json/PASS_RATE2", (err: any, res: cc.JsonAsset) => { if (err) { console.error("加载 PASS_RATE2.JSON 失败:", err); return; } this.newPlayerPassRate = res.json; }); cc.resources.load("Json/MESSAGE_MODEL", (err: any, res: cc.JsonAsset) => { if (err) { console.error("加载 MESSAGE_MODEL.JSON 失败:", err); return; } const newLevelData = res.json; this.MESSAGE_MODEL = newLevelData; }) cc.fx.GameConfig.GM_INFO.testId = "orm8I7N10NSQdAhN4LP04n8UP56I"; } //原有版本 static LEVEL_INFO_init(type, time, otherLevel, endless = false) { // 示例 围墙会根据缺的地块自动生成 // 带缺口 {"id":"1006","map":[10,11],"gap":[cc.v2(4,9),cc.v2(5,9)]}, // 带升降地块 {"id":"1004","map":[7,8],"risefall":[{pos:cc.v2(5,4),color:2},{pos:cc.v2(5,5),color:5}]}, // 如果普通的就 {"id":"1001","map":[6,7]}, 这个样子就可以了 var self = this; const requestId = ++this.levelLoadRequest; this.sceneLoadRequest++; const previousEntryRelease = this.takeEntryDownloadRelease(); if (previousEntryRelease) previousEntryRelease(); if (this.cancelBlockLoading) this.cancelBlockLoading(); let name = null; let levelNumber: number; if (endless) { let level = 0; if (!cc.fx.GameConfig.GM_INFO.randomLevel || cc.fx.GameConfig.GM_INFO.randomLevel <= 0) { cc.fx.GameConfig.GM_INFO.randomLevel = cc.fx.GameTool.getNextLevel(); } level = cc.fx.GameConfig.GM_INFO.randomLevel; levelNumber = level; name = this.getLevelConfigPath(levelNumber, false); console.log("无尽模式关卡", level); cc.fx.GameConfig.GM_INFO.GameplayType = 1; } else { if (cc.fx.GameTool.maxLevel() && otherLevel == false) { // MiniGameSdk.API.showToast("关卡每周更新,敬请期待"); // cc.fx.GameTool.requestSubscribe(); console.log("关卡更新提示") return; } levelNumber = cc.fx.GameConfig.GM_INFO.level + 1; if (cc.fx.GameConfig.GM_INFO.otherLevel > 0) { console.log("进入特殊帮助关卡,可越过自己关卡等级"); levelNumber = cc.fx.GameConfig.GM_INFO.otherLevel; } name = this.getLevelConfigPath(levelNumber, !cc.fx.GameConfig.GM_INFO.otherLevel); } const scene = cc.director.getScene(); const isCurrent = () => requestId === this.levelLoadRequest && (!type || cc.director.getScene() === scene); const firstEntryLoading = type && this.shouldPrepareFirstGameplayEntry() ? this.createBlockLoading(isCurrent) : null; if (firstEntryLoading) firstEntryLoading.open(); const preparation = type ? this.beginGameplayEntryPreparation() : null; const configStartedAt = Date.now(); // console.log(`[进关耗时][${name}/custom分包] 开始`); let bundleName = "custom"; // 替换为实际的分包名称 cc.assetManager.loadBundle(bundleName, { priority: 10 }, (err: Error, bundle: cc.AssetManager.Bundle) => { // console.log(`[进关耗时][${name}/custom分包] ${err ? "失败" : "完成"},独立耗时=${Date.now() - configStartedAt}ms`); if (!isCurrent()) { if (preparation) preparation.release(); return; } if (err) { if (preparation) preparation.release(); if (firstEntryLoading) firstEntryLoading.close(); if (type) this.showBlockLoadError(err, () => this.LEVEL_INFO_init(type, time, otherLevel, endless)); return; } const jsonStartedAt = Date.now(); // console.log(`[进关耗时][${name}/JSON] 开始`); cc.assetManager.loadAny({ path: name, type: cc.JsonAsset, bundle: bundleName }, { priority: 10 }, (err: Error, res: cc.JsonAsset) => { // console.log(`[进关耗时][${name}/JSON] ${err ? "失败" : "完成"},独立耗时=${Date.now() - jsonStartedAt}ms`); if (!isCurrent()) { if (preparation) preparation.release(); return; } if (err) { if (preparation) preparation.release(); if (firstEntryLoading) firstEntryLoading.close(); if (bundle.getInfoWithPath(name, cc.JsonAsset)) { if (type) this.showBlockLoadError(err, () => this.LEVEL_INFO_init(type, time, otherLevel, endless)); return; } console.log("______________没有这个关卡:", name); cc.fx.GameTool.addLevel(null, null, null); if (endless) { cc.fx.GameConfig.GM_INFO.randomLevel = cc.fx.GameConfig.GM_INFO.randomLevel + 1; cc.fx.GameConfig.LEVEL_INFO_init(type, time, false, true); } else { self.LEVEL_INFO_init(type, time, false); } return; } self.applyLevelData(res.json, type, time, levelNumber, preparation); }); }); //道具位置信息 this.PROP_INFO = [ { "pos1": { "x": -66, "y": 60, "z": 0 }, "pos2": { "x": -19, "y": 36, "z": 0 }, "pos3": { "x": -34.362, "y": 13.246, "z": 0 }, "pos4": { "x": -17, "y": 12, "z": 0 }, "pos5": { "x": -85, "y": 70, "z": 0 }, "pos6": { "x": -5, "y": 3, "z": 0 }, }, { "pos1": { "x": -126, "y": 61, "z": 0 }, "pos2": { "x": -25.792, "y": 34.675, "z": 0 }, "pos3": { "x": -94.792, "y": 21.675, "z": 0 }, "pos4": { "x": -20, "y": 13, "z": 0 }, "pos5": { "x": -140, "y": 68, "z": 0 }, "pos6": { "x": -2, "y": 0, "z": 0 }, }, { "pos1": { "x": -67, "y": 122, "z": 0 }, "pos2": { "x": -20, "y": 94, "z": 0 }, "pos3": { "x": -34.792, "y": 23.675, "z": 0 }, "pos4": { "x": -14, "y": 24, "z": 0 }, "pos5": { "x": -84, "y": 128, "z": 0 }, "pos6": { "x": -5, "y": 3, "z": 0 }, }, { "pos1": { "x": -178, "y": 61, "z": 0 }, "pos2": { "x": -34.792, "y": 37.675, "z": 0 }, "pos3": { "x": -150, "y": 23.675, "z": 0 }, "pos4": { "x": -22, "y": 16, "z": 0 }, "pos5": { "x": -200, "y": 67, "z": 0 }, "pos6": { "x": 0, "y": 0, "z": 0 }, }, { "pos1": { "x": -63, "y": 178, "z": 0 }, "pos2": { "x": -17.792, "y": 166.675, "z": 0 }, "pos3": { "x": -31.792, "y": 33.675, "z": 0 }, "pos4": { "x": -14, "y": 37, "z": 0 }, "pos5": { "x": -78, "y": 182, "z": 0 }, "pos6": { "x": 0, "y": 2, "z": 0 }, }, { "pos1": { "x": -123, "y": 124, "z": 0 }, "pos2": { "x": -25.792, "y": 99.675, "z": 0 }, "pos3": { "x": -97.792, "y": 27.675, "z": 0 }, "pos4": { "x": -27, "y": 19, "z": 0 }, "pos5": { "x": -135, "y": 127, "z": 0 }, "pos6": { "x": 0, "y": 0, "z": 0 }, }, { "pos1": { "x": -64, "y": 178, "z": 0 }, "pos2": { "x": -24.792, "y": 275.675, "z": 0 }, "pos3": { "x": -31.792, "y": 35.675, "z": 0 }, "pos4": { "x": -7, "y": 39, "z": 0 }, "pos5": { "x": -77, "y": 188, "z": 0 }, "pos6": { "x": 0, "y": 0, "z": 0 }, }, { "pos1": { "x": -178, "y": 186, "z": 0 }, "pos2": { "x": -31.792, "y": 163.675, "z": 0 }, "pos3": { "x": -33.792, "y": 34.675, "z": 0 }, "pos4": { "x": -18, "y": 30, "z": 0 }, "pos5": { "x": -200, "y": 192, "z": 0 }, "pos6": { "x": 0, "y": 5, "z": 0 }, }, { "pos1": { "x": -186, "y": 178, "z": 0 }, "pos2": { "x": -25.792, "y": 38.675, "z": 0 }, "pos3": { "x": -153.792, "y": 34.675, "z": 0 }, "pos4": { "x": -23, "y": 14, "z": 0 }, "pos5": { "x": -200, "y": 185, "z": 0 }, "pos6": { "x": 0, "y": 0, "z": 0 }, }, { "pos1": { "x": -183, "y": 64, "z": 0 }, "pos2": { "x": -37.792, "y": 37.675, "z": 0 }, "pos3": { "x": -36.792, "y": 26.675, "z": 0 }, "pos4": { "x": -24, "y": 17, "z": 0 }, "pos5": { "x": -212, "y": 74, "z": 0 }, "pos6": { "x": 0, "y": 0, "z": 0 }, }, { "pos1": { "x": -65, "y": 177, "z": 0 }, "pos2": { "x": 93.208, "y": 276.675, "z": 0 }, "pos3": { "x": -33.792, "y": 34.675, "z": 0 }, "pos4": { "x": 3, "y": 31, "z": 0 }, "pos5": { "x": -80, "y": 190, "z": 0 }, "pos6": { "x": 0, "y": 3, "z": 0 }, }, { "pos1": { "x": 58, "y": 179, "z": 0 }, "pos2": { "x": 205.225, "y": 154.034, "z": 0 }, "pos3": { "x": -29.642, "y": 25.691, "z": 0 }, "pos4": { "x": 7.218, "y": 29.765, "z": 0 }, "pos5": { "x": 45, "y": 185, "z": 0 }, "pos6": { "x": 0, "y": 0, "z": 0 }, }, { "pos1": { "x": -68, "y": 182, "z": 0 }, "pos2": { "x": -25.792, "y": 37.675, "z": 0 }, "pos3": { "x": -35.792, "y": 33.675, "z": 0 }, "pos4": { "x": -14, "y": 12, "z": 0 }, "pos5": { "x": -82, "y": 190, "z": 0 }, "pos6": { "x": -2, "y": 1, "z": 0 }, }, { "pos1": { "x": -184, "y": 62, "z": 0 }, "pos2": { "x": -35.792, "y": 36.675, "z": 0 }, "pos3": { "x": -271.792, "y": 24.675, "z": 0 }, "pos4": { "x": -17, "y": 20, "z": 0 }, "pos5": { "x": -200, "y": 72, "z": 0 }, "pos6": { "x": 0, "y": 0, "z": 0 }, }, { "pos1": { "x": -62, "y": 175, "z": 0 }, "pos2": { "x": 87.208, "y": 164.675, "z": 0 }, "pos3": { "x": -31.792, "y": 35.675, "z": 0 }, "pos4": { "x": -6, "y": 24, "z": 0 }, "pos5": { "x": -79, "y": 172, "z": 0 }, "pos6": { "x": 0, "y": 5, "z": 0 }, }, { "pos1": { "x": -184, "y": 72, "z": 0 }, "pos2": { "x": -33.792, "y": 38.675, "z": 0 }, "pos3": { "x": -154.792, "y": 29.675, "z": 0 }, "pos4": { "x": -29, "y": 16, "z": 0 }, "pos5": { "x": -201, "y": 85, "z": 0 }, "pos6": { "x": -1, "y": 3, "z": 0 }, }, { "pos1": { "x": -64, "y": 181, "z": 0 }, "pos2": { "x": 95.208, "y": 155.675, "z": 0 }, "pos3": { "x": -30.792, "y": 36.675, "z": 0 }, "pos4": { "x": -12, "y": 32, "z": 0 }, "pos5": { "x": -78, "y": 190, "z": 0 }, "pos6": { "x": 0, "y": 3, "z": 0 }, }, { "pos1": { "x": -65, "y": 178, "z": 0 }, "pos2": { "x": -21.792, "y": 156.675, "z": 0 }, "pos3": { "x": -31.792, "y": 37.675, "z": 0 }, "pos4": { "x": -12, "y": 32, "z": 0 }, "pos5": { "x": -78, "y": 188, "z": 0 }, "pos6": { "x": 0, "y": 2, "z": 0 }, }, { "pos1": { "x": -62, "y": 183, "z": 0 }, "pos2": { "x": 88.208, "y": 162.675, "z": 0 }, "pos3": { "x": -29.792, "y": 41.675, "z": 0 }, "pos4": { "x": -12, "y": 18, "z": 0 }, "pos5": { "x": -80, "y": 188, "z": 0 }, "pos6": { "x": -4, "y": 2, "z": 0 }, }, { "pos1": { "x": -67, "y": 80, "z": 0 }, "pos2": { "x": -26.792, "y": 32.675, "z": 0 }, "pos3": { "x": -34.792, "y": 23.675, "z": 0 }, "pos4": { "x": -12, "y": 18, "z": 0 }, "pos5": { "x": -84, "y": 90, "z": 0 }, "pos6": { "x": 0, "y": 0, "z": 0 }, }, { "pos1": { "x": -184, "y": 80, "z": 0 }, "pos2": { "x": -25.792, "y": 34.675, "z": 0 }, "pos3": { "x": -155.792, "y": 25.675, "z": 0 }, "pos4": { "x": -18, "y": 26, "z": 0 }, "pos5": { "x": -196, "y": 96, "z": 0 }, "pos6": { "x": 0, "y": 0, "z": 0 }, }, { "pos1": { "x": -62, "y": 160, "z": 0 }, "pos2": { "x": 95.208, "y": 158.675, "z": 0 }, "pos3": { "x": -33.792, "y": 29.675, "z": 0 }, "pos4": { "x": -18, "y": 26, "z": 0 }, "pos5": { "x": -78, "y": 170, "z": 0 }, "pos6": { "x": 0, "y": 2, "z": 0 }, }, { "pos1": { "x": -68, "y": 160, "z": 0 }, "pos2": { "x": -25.792, "y": 156.675, "z": 0 }, "pos3": { "x": -35.792, "y": 27.675, "z": 0 }, "pos4": { "x": -25, "y": 40, "z": 0 }, "pos5": { "x": -82, "y": 170, "z": 0 }, "pos6": { "x": -2, "y": 2, "z": 0 }, } ]; // this.PROP_INFO = [{"pos1": {"x": -60,"y": 60,"z": 0},"pos2": {"x": -16.442,"y": 30,"z": 0},"pos3": {"x": -34.362,"y": 13.246,"z": 0},"pos4": {"x": -17,"y": 12,"z": 0}},{"pos1": {"x": -126,"y": 63,"z": 0},"pos2": {"x": -25.792,"y": 34.675,"z": 0},"pos3": {"x": -94.792,"y": 21.675,"z": 0},"pos4": {"x": -20,"y": 13,"z": 0}},{"pos1": {"x": -60,"y": 125,"z": 0},"pos2": {"x": -12.792,"y": 92.675,"z": 0},"pos3": {"x": -34.792,"y": 23.675,"z": 0},"pos4": {"x": -14,"y": 24,"z": 0}},{"pos1": {"x": -185,"y": 63,"z": 0},"pos2": {"x": -34.792,"y": 37.675,"z": 0},"pos3": {"x": -154.792,"y": 23.675,"z": 0},"pos4": {"x": -22,"y": 16,"z": 0}},{"pos1": {"x": -57,"y": 178,"z": 0},"pos2": {"x": -17.792,"y": 166.675,"z": 0},"pos3": {"x": -31.792,"y": 33.675,"z": 0},"pos4": {"x": -14,"y": 37,"z": 0}},{"pos1": {"x": -123,"y": 127,"z": 0},"pos2": {"x": -25.792,"y": 99.675,"z": 0},"pos3": {"x": -97.792,"y": 27.675,"z": 0},"pos4": {"x": -27,"y": 19,"z": 0}},{"pos1": {"x": -62,"y": 181,"z": 0},"pos2": {"x": -24.792,"y": 275.675,"z": 0},"pos3": {"x": -31.792,"y": 35.675,"z": 0},"pos4": {"x": -7,"y": 39,"z": 0}},{"pos1": {"x": -181,"y": 180,"z": 0},"pos2": {"x": -31.792,"y": 163.675,"z": 0},"pos3": {"x": -33.792,"y": 34.675,"z": 0},"pos4": {"x": -18,"y": 30,"z": 0}},{"pos1": {"x": -182,"y": 182,"z": 0},"pos2": {"x": -25.792,"y": 38.675,"z": 0},"pos3": {"x": -153.792,"y": 34.675,"z": 0},"pos4": {"x": -23,"y": 14,"z": 0}},{"pos1": {"x": -186,"y": 61,"z": 0},"pos2": {"x": -37.792,"y": 37.675,"z": 0},"pos3": {"x": -36.792,"y": 26.675,"z": 0},"pos4": {"x": -24,"y": 17,"z": 0}},{"pos1": {"x": -62,"y": 177,"z": 0},"pos2": {"x": 93.208,"y": 276.675,"z": 0},"pos3": {"x": -33.792,"y": 34.675,"z": 0},"pos4": {"x": 3,"y": 31,"z": 0}},{"pos1": {"x": 58,"y": 179,"z": 0},"pos2": {"x": 205.225,"y": 154.034,"z": 0},"pos3": {"x": -29.642,"y": 25.691,"z": 0},"pos4": {"x": 7.218,"y": 29.765,"z": 0}},{"pos1": {"x": -57,"y": 182,"z": 0},"pos2": {"x": -25.792,"y": 37.675,"z": 0},"pos3": {"x": -35.792,"y": 33.675,"z": 0},"pos4": {"x": -14,"y": 12,"z": 0}},{"pos1": {"x": -180,"y": 62,"z": 0},"pos2": {"x": -35.792,"y": 36.675,"z": 0},"pos3": {"x": -271.792,"y": 24.675,"z": 0},"pos4": {"x": -17,"y": 20,"z": 0}},{"pos1": {"x": -62,"y": 178,"z": 0},"pos2": {"x": 87.208,"y": 164.675,"z": 0},"pos3": {"x": -31.792,"y": 35.675,"z": 0},"pos4": {"x": -6,"y": 24,"z": 0}},{"pos1": {"x": -176,"y": 60,"z": 0},"pos2": {"x": -33.792,"y": 38.675,"z": 0},"pos3": {"x": -154.792,"y": 29.675,"z": 0},"pos4": {"x": -29,"y": 16,"z": 0}},{"pos1": {"x": -60,"y": 181,"z": 0},"pos2": {"x": 95.208,"y": 155.675,"z": 0},"pos3": {"x": -30.792,"y": 36.675,"z": 0},"pos4": {"x": -12,"y": 32,"z": 0}},{"pos1": {"x": -63,"y": 178,"z": 0},"pos2": {"x": -21.792,"y": 156.675,"z": 0},"pos3": {"x": -31.792,"y": 37.675,"z": 0},"pos4": {"x": -12,"y": 32,"z": 0}},{"pos1": {"x": -60,"y": 183,"z": 0},"pos2": {"x": 88.208,"y": 162.675,"z": 0},"pos3": {"x": -29.792,"y": 41.675,"z": 0},"pos4": {"x": -12,"y": 18,"z": 0}},{"pos1": {"x": -64,"y": 58,"z": 0},"pos2": {"x": -26.792,"y": 32.675,"z": 0},"pos3": {"x": -34.792,"y": 23.675,"z": 0},"pos4": {"x": -12,"y": 18,"z": 0}},{"pos1": {"x": -184,"y": 62,"z": 0},"pos2": {"x": -25.792,"y": 34.675,"z": 0},"pos3": {"x": -155.792,"y": 25.675,"z": 0},"pos4": {"x": -18,"y": 26,"z": 0}},{"pos1": {"x": -57,"y": 190,"z": 0},"pos2": {"x": 95.208,"y": 158.675,"z": 0},"pos3": {"x": -33.792,"y": 29.675,"z": 0},"pos4": {"x": -18,"y": 26,"z": 0}},{"pos1": {"x": -71,"y": 184,"z": 0},"pos2": {"x": -25.792,"y": 156.675,"z": 0},"pos3": {"x": -35.792,"y": 27.675,"z": 0},"pos4": {"x": -25,"y": 40,"z": 0}}]; } /** * 检查关卡是否在本地缓存中 */ static isLevelInLocalCache(level: number): boolean { if (level <= this.MAX_LOCAL_LEVEL) { return true; // 前100关在本地bundle中 } // 检查本地存储中是否有缓存 const cacheKey = this.LOCAL_CACHE_PREFIX + level; const cachedData = cc.sys.localStorage.getItem(cacheKey); return cachedData !== null && cachedData !== undefined; } /** * 将关卡数据保存到本地缓存 */ static saveLevelToCache(level: number, jsonData: any): void { if (level <= this.MAX_LOCAL_LEVEL) { return; // 前100关不需要缓存 } const cacheKey = this.LOCAL_CACHE_PREFIX + level; try { cc.sys.localStorage.setItem(cacheKey, JSON.stringify(jsonData)); console.log(`关卡${level}数据已缓存到本地`); } catch (error) { console.error(`缓存关卡${level}数据失败:`, error); } } /** * 从本地缓存获取关卡数据 */ static getLevelFromCache(level: number): any { if (level <= this.MAX_LOCAL_LEVEL) { return null; // 前100关从bundle加载 } const cacheKey = this.LOCAL_CACHE_PREFIX + level; try { const cachedData = cc.sys.localStorage.getItem(cacheKey); if (cachedData) { console.log(`从本地缓存加载关卡${level}`); return JSON.parse(cachedData); } } catch (error) { console.error(`读取关卡${level}缓存失败:`, error); } return null; } /** * 从CDN加载关卡数据 */ static loadLevelFromCDN(level: number): Promise { return new Promise((resolve, reject) => { const cdnBaseUrl = "http://cdn.pay.nika4games.com/resources/json/"; const cdnUrl = cdnBaseUrl + `level${level}.json`; console.log(`从CDN加载关卡${level}:`, cdnUrl); fetch(cdnUrl) .then(response => { if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.json(); }) .then(jsonData => { // 保存到本地缓存 this.saveLevelToCache(level, jsonData); resolve(jsonData); }) .catch(error => { console.error(`CDN加载关卡${level}失败:`, error); reject(error); }); }); } /** * 从本地bundle加载关卡数据 */ static loadLevelFromBundle(level: number, mainline: boolean = true): Promise { return new Promise((resolve, reject) => { const bundleName = "custom"; const name = this.getLevelConfigPath(level, mainline); cc.assetManager.loadBundle(bundleName, { priority: 10 }, (err: Error, bundle: cc.AssetManager.Bundle) => { if (err) { reject(new Error(`加载分包失败: ${err.message}`)); return; } cc.assetManager.loadAny({ path: name, type: cc.JsonAsset, bundle: bundleName }, { priority: 10 }, (err: Error, res: cc.JsonAsset) => { if (err) { reject(new Error(`加载关卡资源失败: ${err.message}`)); return; } resolve(res.json); }); }); }); } //新的CDN版本 // static LEVEL_INFO_init(type, time, otherLevel, endless = false) { // // ... existing code ... // let name = null; // let levelNumber = 0; // if (endless) { // let level = 0; // if (!cc.fx.GameConfig.GM_INFO.randomLevel || cc.fx.GameConfig.GM_INFO.randomLevel <= 0) { // cc.fx.GameConfig.GM_INFO.randomLevel = cc.fx.GameTool.getNextLevel(); // } // level = cc.fx.GameConfig.GM_INFO.randomLevel; // levelNumber = level; // name = "Json/level" + level; // console.log("无尽模式关卡", level); // cc.fx.GameConfig.GM_INFO.GameplayType = 1; // } else { // if (cc.fx.GameTool.maxLevel() && otherLevel == false) { // console.log("关卡更新提示") // return; // } // levelNumber = cc.fx.GameConfig.GM_INFO.level + 1; // if (cc.fx.GameConfig.GM_INFO.otherLevel > 0) { // console.log("进入特殊帮助关卡,可越过自己关卡等级"); // levelNumber = cc.fx.GameConfig.GM_INFO.otherLevel; // } // name = "Json/level" + levelNumber; // } // // 智能加载关卡数据 // this.loadLevelDataSmart(levelNumber, name, type, time, endless, otherLevel); // } /** * 智能加载关卡数据 */ static loadLevelDataSmart(levelNumber: number, name: string, type: any, time: any, endless: boolean, otherLevel: boolean) { const self = this; // 检查关卡数据来源 if (levelNumber <= this.MAX_LOCAL_LEVEL) { // 前100关从本地bundle加载 console.log(`关卡${levelNumber}从本地bundle加载`); this.loadLevelFromBundle(levelNumber) .then(jsonData => { this.applyLevelData(jsonData, type, time, levelNumber); }) .catch(error => { console.error(`本地bundle加载关卡${levelNumber}失败:`, error); this.handleLoadFail(levelNumber, name, type, time, endless, otherLevel); }); } else { // 101关以后,先检查本地缓存 const cachedData = this.getLevelFromCache(levelNumber); if (cachedData) { // 本地缓存存在,直接使用 console.log(`关卡${levelNumber}从本地缓存加载`); this.applyLevelData(cachedData, type, time, levelNumber); } else { // 本地缓存不存在,从CDN加载 console.log(`关卡${levelNumber}从CDN加载`); this.loadLevelFromCDN(levelNumber) .then(jsonData => { this.applyLevelData(jsonData, type, time, levelNumber); }) .catch(error => { console.error(`CDN加载关卡${levelNumber}失败:`, error); this.handleLoadFail(levelNumber, name, type, time, endless, otherLevel); }); } } } /** * 应用关卡数据 */ static applyLevelData(jsonData: any, type: any, time: any, levelNumber = Number(jsonData.LEVEL_INFO[0].id), preparation?: GameplayEntryPreparation) { this.loadedLevelNumber = levelNumber; this.BLOCK_INFO = jsonData["BLOCK_INFO"]; this.LEVEL_INFO = jsonData["LEVEL_INFO"]; this.WALL_INFO = jsonData["WALL_INFO"]; console.log("关卡配置加载完成"); if (type) { this.enterGameSceneWhenReady(time, preparation); } } static handleLoadFail(levelNumber: number, name: string, type: any, time: any, endless: boolean, otherLevel: boolean) { const self = this; // 尝试从本地bundle回退(对于101关以后,可能没有对应的本地资源) console.log(`尝试回退到本地bundle加载: ${name}`); cc.assetManager.loadBundle("custom", (err: Error, bundle: cc.AssetManager.Bundle) => { if (err) { console.log("本地分包加载也失败:", err); cc.fx.GameTool.addLevel(null, null, null); if (endless) { cc.fx.GameConfig.GM_INFO.randomLevel = cc.fx.GameConfig.GM_INFO.randomLevel + 1; cc.fx.GameConfig.LEVEL_INFO_init(type, time, false, true); } else { self.LEVEL_INFO_init(type, time, false); } return; } bundle.load(name, cc.JsonAsset, (err: Error, res: cc.JsonAsset) => { if (err) { console.log("______________没有这个关卡:", name); cc.fx.GameTool.addLevel(null, null, null); if (endless) { cc.fx.GameConfig.GM_INFO.randomLevel = cc.fx.GameConfig.GM_INFO.randomLevel + 1; cc.fx.GameConfig.LEVEL_INFO_init(type, time, false, true); } else { self.LEVEL_INFO_init(type, time, false); } return; } let jsonData: object = res.json!; this.applyLevelData(jsonData, type, time, levelNumber); }); }); } static setCode(code) { this.GM_INFO.scode = code; } static getKey(key) { // var reg = new RegExp('(^|&)' + key + '=([^&]*)(&|$)', 'i'); // var r = window.location.search.substring(1).match(reg); // if (r != null) { // return unescape(r[2]); // } return ""; } static Authentication() { cc.fx.GameTool.Authentication(); } static CITY_init() { this.CITY = { "河北省": true, "山西省": true, "辽宁省": true, "吉林省": true, "黑龙江省": true, "江苏省": true, "浙江省": true, "安徽省": true, "福建省": true, "江西省": true, "山东省": true, "河南省": true, "湖北省": true, "湖南省": true, "广东省": true, "海南省": true, "四川省": true, "贵州省": true, "云南省": true, "陕西省": true, "甘肃省": true, "青海省": true, "港澳台": true, "北京": true, "天津": true, "上海": true, "重庆": true, "内蒙古": true, "广西": true, "西藏": true, "宁夏": true, "新疆": true }; } }