You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
920 lines
32 KiB
920 lines
32 KiB
|
1 week ago
|
import { _decorator, assetManager, Button, Camera, clamp01, Component, director, EventKeyboard, Input, input, instantiate, KeyCode, Label, macro, Node, Prefab, Sprite, sys, tween, v3, Vec2, Vec3, view, Widget } from 'cc';
|
||
|
|
|
||
|
|
import { getBattleMapResItem, getBattleMapResItemByChapter, getMainResPakage, getZombieResPakage, ResPkgName, UIConfigs, UIID } from './ConfigRes';
|
||
|
|
import { UILayer } from '../../mx/module/ui/UIManager';
|
||
|
|
import { ItemData } from './ConfigProjectData';
|
||
|
|
import { GEvent } from '../../mx/module/event/GEvent';
|
||
|
|
import MTools from '../../mx/tools/MTools';
|
||
|
|
|
||
|
|
import { OtherDataType, StatisticsType, StatusType } from './GameData';
|
||
|
|
import { sdkConfig } from '../sdk/sdkConfig';
|
||
|
|
import { IUserInfo } from './PlayerRecordReporting';
|
||
|
|
import { BattleType, ChapterDifficulty } from '../manager/ChapterDataManager';
|
||
|
|
import { BattleCore } from '../ui/BattleGame/BattleCore';
|
||
|
|
import { MapAgent } from '../ui/BattleGame/MapAgent';
|
||
|
|
import { BattleDiagnostics } from '../diagnostics/BattleDiagnostics';
|
||
|
|
import { PfDiagnostics } from '../diagnostics/PfDiagnostics';
|
||
|
|
import { TetriStackDiagnostics } from '../tetriMap/TetriStackDiagnostics';
|
||
|
|
import { DEV } from 'cc/env';
|
||
|
|
import { StoryTypeGroup } from '../storyManager/storyManager';
|
||
|
|
import { BattleResLoader } from './BattleResLoader';
|
||
|
|
import { HomeBattlePreload } from './HomeBattlePreload';
|
||
|
|
import { SmartPreload } from './SmartPreload';
|
||
|
|
import { BattlePerformance } from '../ui/BattleGame/BattlePerformance';
|
||
|
|
import { patchSpineSkeletonResetEnums } from '../../mx/module/lang/spineRuntimePatch';
|
||
|
|
import { UIMainUI } from '../ui/MainUI/UIMainUI';
|
||
|
|
|
||
|
|
const { ccclass, property } = _decorator;
|
||
|
|
|
||
|
|
@ccclass('GameController')
|
||
|
|
export class GameController extends Component {
|
||
|
|
|
||
|
|
static instance: GameController = null;
|
||
|
|
/**缓存 BattleSuccess 相机 */
|
||
|
|
private _battleSuccessCamera: Camera | null = null;
|
||
|
|
noGravityWallProbability: number = 0 //无重力方块权重;
|
||
|
|
tetriGameSpeed: number=1 //游戏倍速 最高为3;
|
||
|
|
monsterAttackPercent: number=1; //怪物攻击倍率百分比衰弱
|
||
|
|
monsterHpPercent: number=1; //怪物血量倍率b百分比衰弱
|
||
|
|
isShowBlockDrop: boolean = false;
|
||
|
|
isShowWipeOut: boolean = false;
|
||
|
|
/** 挂机收益领取中(防同界面连点;ishaveLingqu 仅本面板实例有效,关面板/异步 openPanel 时拦不住) */
|
||
|
|
afkRewardClaimLock: boolean = false;
|
||
|
|
//设置关卡不出怪
|
||
|
|
noMonsterShow: boolean = false;
|
||
|
|
|
||
|
|
protected onLoad(): void {
|
||
|
|
GameController.instance = this;
|
||
|
|
}
|
||
|
|
|
||
|
|
private _findNodeByNames(root: Node | null, names: Set<string>): Node | null {
|
||
|
|
if (!root) return null;
|
||
|
|
if (names.has(root.name)) return root;
|
||
|
|
const children = root.children;
|
||
|
|
for (let i = 0; i < children.length; i++) {
|
||
|
|
const found = this._findNodeByNames(children[i], names);
|
||
|
|
if (found) return found;
|
||
|
|
}
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 获取 BattleSuccess 使用的相机(优先专用命名,相机不存在时回退到 GameCamera/UICamera)。
|
||
|
|
*/
|
||
|
|
public getBattleSuccessCamera(): Camera | null {
|
||
|
|
if (this._battleSuccessCamera?.isValid) return this._battleSuccessCamera;
|
||
|
|
|
||
|
|
const scene = this.node?.scene ?? director.getScene();
|
||
|
|
const nameSet = new Set<string>([
|
||
|
|
'BattleSuccessCamera',
|
||
|
|
'battleSuccessCamera',
|
||
|
|
'BattleSuccessCam',
|
||
|
|
'battleSuccessCam',
|
||
|
|
]);
|
||
|
|
const cameraNode = this._findNodeByNames(scene, nameSet);
|
||
|
|
const explicitCam = cameraNode?.getComponent(Camera) ?? null;
|
||
|
|
|
||
|
|
this._battleSuccessCamera = explicitCam ?? gg.ui.GameCamera ?? gg.ui.UICamera ?? null;
|
||
|
|
return this._battleSuccessCamera;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 将 UI 相机下的点击坐标(通常来自 event.getUILocation())转换为 BattleSuccess 相机世界坐标。
|
||
|
|
* @param uiPos UI 相机坐标(x,y);z 可选,默认 0
|
||
|
|
* @returns BattleSuccess 相机世界坐标;相机缺失时返回 null
|
||
|
|
*/
|
||
|
|
public convertUIClickToBattleSuccessWorldPos(uiPos: Vec2 | Vec3): Vec3 | null {
|
||
|
|
const srcCam = gg.ui.UICamera ?? gg.ui.GameCamera;
|
||
|
|
const dstCam = this.getBattleSuccessCamera();
|
||
|
|
if (!srcCam || !dstCam || !uiPos) return null;
|
||
|
|
|
||
|
|
const uiWorld = new Vec3(uiPos.x, uiPos.y, (uiPos as Vec3).z ?? 0);
|
||
|
|
const screenPos = srcCam.worldToScreen(uiWorld);
|
||
|
|
return dstCam.screenToWorld(screenPos);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**游戏总时间 */
|
||
|
|
GameTime: number = 0;
|
||
|
|
|
||
|
|
/**自己的称谓(主角称呼) */
|
||
|
|
SelfName: string = "男主";
|
||
|
|
|
||
|
|
UserInfo: IUserInfo = null;
|
||
|
|
|
||
|
|
/**暂停秒赚 */
|
||
|
|
PauseSecondEarn: boolean = false;
|
||
|
|
|
||
|
|
/**是否正在播放引导剧情 */
|
||
|
|
IsPlayingGuideStory: boolean = false;
|
||
|
|
/**是否正在播放新闻 */
|
||
|
|
IsPlayingNews: boolean = false;
|
||
|
|
/**是否显示障碍物 */
|
||
|
|
isShowObstacle: boolean = true;
|
||
|
|
|
||
|
|
/**当前玩家血量 */
|
||
|
|
playerHp: number = 0;
|
||
|
|
/**当前玩家银币数 */
|
||
|
|
playerCoinNum: number = 0;
|
||
|
|
|
||
|
|
//玩家玩过了游戏到结算
|
||
|
|
IsPlayedGame: boolean = false;
|
||
|
|
playerGameFail: boolean = false;
|
||
|
|
//弹出签到
|
||
|
|
IsShowSignPop: boolean = false;
|
||
|
|
|
||
|
|
/**当前完成后的章节 */
|
||
|
|
finishChapterId: number = -1;
|
||
|
|
/**
|
||
|
|
* 怪物数量倍数
|
||
|
|
*/
|
||
|
|
monsterNumScale: number = 1;
|
||
|
|
|
||
|
|
playerWeaponIdArray = []
|
||
|
|
playerSkillIdArray = []
|
||
|
|
|
||
|
|
playerSkillGroupIdArray = []
|
||
|
|
playerWeaponGroupIdArray = []
|
||
|
|
setPlayerSkillGroupIdArray = false
|
||
|
|
setPlayerWeaponGroupIdArray = false
|
||
|
|
afkTimeOne = 60 * 60
|
||
|
|
|
||
|
|
|
||
|
|
/**武器模式最大挑战次数 */
|
||
|
|
// WeaponCountMax = 5;
|
||
|
|
// /**无尽模式最大挑战次数 */
|
||
|
|
// InfiniteChallengeCountMax = 20;
|
||
|
|
// /**道具模式最大挑战次数 */
|
||
|
|
// PropModeCountMax = 20;
|
||
|
|
// /**宝箱副本最大挑战次数 */
|
||
|
|
// BoxChallengeCountMax = 20;
|
||
|
|
// /**抽卡挑战模式最大挑战次数 */
|
||
|
|
// GachaChallengeCountMax = 20;
|
||
|
|
// /**boss挑战模式最大挑战次数 */
|
||
|
|
// BossChallengeCountMax = 20;
|
||
|
|
|
||
|
|
isInitAllData: boolean = false
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
/**当前选中准备进入的战斗章节id */
|
||
|
|
CurentSelectChapterId: number = 1;
|
||
|
|
/**当前选中准备进入的战斗模式 */
|
||
|
|
CurentSelectBattleType: BattleType = BattleType.Level;
|
||
|
|
/**当前选中准备进入的战斗难度 */
|
||
|
|
CurentSelectBattleDifficulty: ChapterDifficulty = ChapterDifficulty.Normal;
|
||
|
|
/**当前选中的副本 */
|
||
|
|
CurentSelectReplica: ITableReplica = null;
|
||
|
|
/**当前正在进行中的战斗 */
|
||
|
|
CurentBattle: BattleCore = null;
|
||
|
|
/** 正在离开战斗回大厅:局内 update 应直接 return,且暂不置空 CurentBattle */
|
||
|
|
private _battleLeaving = false;
|
||
|
|
|
||
|
|
/** 局内逻辑是否仍有效(离开战斗/清引用过程中为 false,避免刷 null 报错) */
|
||
|
|
isBattleContextActive(): boolean {
|
||
|
|
return !this._battleLeaving && this.CurentBattle != null;
|
||
|
|
}
|
||
|
|
|
||
|
|
guideStoryId: number = 1;
|
||
|
|
|
||
|
|
/** 第4章结束剧情后:角色升级引导完成时再弹丧尸农场入口(原剧情内 guide4) */
|
||
|
|
pendingFarmGuideAfterCh4Role: boolean = false;
|
||
|
|
|
||
|
|
/** 下一次 homeGuideIndex==4 时强制出「点角色」遮罩(第三章后剧情触发,不依赖 doc.chapter 判档) */
|
||
|
|
forceHomeRoleTabGuide: boolean = false;
|
||
|
|
|
||
|
|
guideGreatGift: boolean = false;
|
||
|
|
|
||
|
|
curChooseZombieType: string = 'farm' //农场,工厂,矿产
|
||
|
|
|
||
|
|
/**第三章失败引导角色升级 */
|
||
|
|
thirdChapterFailRoleUpgrade: boolean = false;
|
||
|
|
/**第三章失败引导武器升级 */
|
||
|
|
thirdChapterFailWeaponUpgrade: boolean = false;
|
||
|
|
/***第三章胜利榴莲弹出 */
|
||
|
|
thirdChapterWinGreatGift: boolean = false;
|
||
|
|
|
||
|
|
/**商店首日登录刷新*/
|
||
|
|
isShopFirstLoginRefresh: boolean = false;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 初始化游戏
|
||
|
|
*/
|
||
|
|
async init() {
|
||
|
|
console.log("开始初始化game");
|
||
|
|
patchSpineSkeletonResetEnums();
|
||
|
|
macro.ENABLE_MULTI_TOUCH = false
|
||
|
|
this.GameTime = 0;
|
||
|
|
|
||
|
|
gg.dot.init();
|
||
|
|
|
||
|
|
gg.audio.MuteMusic = gg.data.getStatus(StatusType.MuteMusic) == 1;
|
||
|
|
gg.audio.MuteEffect = gg.data.getStatus(StatusType.MuteEffect) == 1;
|
||
|
|
|
||
|
|
GEvent.Ins.on(GEvent.UpdateItemNum, this.onItemUpdate, this);
|
||
|
|
|
||
|
|
// this.playerWeaponIdArray.push(22);
|
||
|
|
// let obj: IBattleSkill = {
|
||
|
|
// weaponId: 22,
|
||
|
|
// id: 2105,
|
||
|
|
// type: 1,
|
||
|
|
// limit_num: 10,
|
||
|
|
// };
|
||
|
|
// this.playerSkillIdArray.push(obj);
|
||
|
|
// obj = {
|
||
|
|
// weaponId: 22,
|
||
|
|
// id: 2153,
|
||
|
|
// type: 1,
|
||
|
|
// limit_num: 10,
|
||
|
|
// };
|
||
|
|
// this.playerSkillIdArray.push(obj);
|
||
|
|
|
||
|
|
this.unscheduleAllCallbacks();
|
||
|
|
|
||
|
|
//初始化每整秒更新计时器
|
||
|
|
this.schedule(() => {
|
||
|
|
this.onSecond();
|
||
|
|
}, 1);
|
||
|
|
|
||
|
|
// gg.data.project.setHaveWeaponIds([1,2,3,4,5,6,7,8,20]);
|
||
|
|
//首次登录
|
||
|
|
if (gg.data.IsFirstLogin) {
|
||
|
|
console.log('首次登录')
|
||
|
|
gg.sdk.reportDY("inLevel", "进入-首次登录");
|
||
|
|
|
||
|
|
//测试-设置体力金币钻石战力等
|
||
|
|
|
||
|
|
gg.data.setCombatPower(50);
|
||
|
|
gg.data.setMoney(100);
|
||
|
|
//初始化30
|
||
|
|
gg.data.setVigour(30);
|
||
|
|
gg.data.setDiamond(10);
|
||
|
|
|
||
|
|
//测试-设置章节为第2章
|
||
|
|
gg.data.doc.chapter = 1
|
||
|
|
//测试-送8个已装备
|
||
|
|
//测试-送8个已装备
|
||
|
|
gg.data.project.setHaveWeaponIds([1,2,3,4,5,6,7,8]);
|
||
|
|
gg.data.project.setEquipWeaponStatus([1,2,3,4,5,6,7,8]);
|
||
|
|
|
||
|
|
gg.data.setStatistics(StatisticsType.ShopBuyGoldAdCount, 6)
|
||
|
|
gg.data.setStatistics(StatisticsType.ShopBuyItemAdCount, 6)
|
||
|
|
gg.data.setStatistics(StatisticsType.FreeBoxCount, 0)
|
||
|
|
gg.data.setStringData(OtherDataType.SignData, '0&0&1')
|
||
|
|
gg.data.setStatistics(StatisticsType.ActiveRefreshPurpleCount, 2)
|
||
|
|
gg.data.setStatistics(StatisticsType.NoDoublePurpleSkillInterval, 50)
|
||
|
|
gg.data.setStringData(OtherDataType.UserLevelupData, '0&10&0&0&0&0&0')
|
||
|
|
|
||
|
|
gg.data.setStringData(OtherDataType.TaskRewardGetData, '0&0&0&0&0')
|
||
|
|
gg.data.setStringData(OtherDataType.DayTaskRewardGetData, '0&0&0&0&0')
|
||
|
|
gg.data.setStringData(OtherDataType.DayTaskProgressData, '0&0&0&0&0&0&0&0&0&0&0&0&0&0')
|
||
|
|
gg.data.setStringData(OtherDataType.DayActiveGetRewardData, '0&0&0&0&0&0&0&0&0&0&0&0&0&0')
|
||
|
|
// gg.role.setDefaultSkin();
|
||
|
|
gg.data.setStatistics(StatisticsType.NoDoublePurpleSkillCount, 8);
|
||
|
|
gg.data.project.vigourCost = 0;
|
||
|
|
} else {
|
||
|
|
gg.sdk.reportDY("inLevel", "进入-第" + gg.data.LoginDays + "天登录");
|
||
|
|
}
|
||
|
|
|
||
|
|
let arr = gg.data.project.getHaveWeaponIds();
|
||
|
|
if (arr.length == 0) {
|
||
|
|
gg.data.project.setHaveWeaponIds([1, 2, 4, 5, 6, 7, 9, 11]);
|
||
|
|
gg.data.project.setEquipWeaponStatus([1, 2, 4, 5, 6, 7, 9, 11]);
|
||
|
|
}
|
||
|
|
|
||
|
|
//今天首次登录
|
||
|
|
if (gg.data.IsFirstLoginToday) {
|
||
|
|
gg.game.isShopFirstLoginRefresh = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
// let list = gg.storyManager.getStoryComicData(StoryTypeGroup.chushiComic);
|
||
|
|
// let isCanOpen = gg.storyManager.isCanOpen(StoryTypeGroup.chushiComic);
|
||
|
|
if (gg.data.IsFirstLogin) {
|
||
|
|
// gg.audio.playMusic({ name: "主页背景音", path: "bgm/" });
|
||
|
|
// gg.storyManager.storyGuide(StoryTypeGroup.chushiComic)
|
||
|
|
gg.game.CurentSelectReplica = null;
|
||
|
|
gg.game.CurentSelectChapterId = 1;
|
||
|
|
gg.game.CurentSelectBattleType = BattleType.Level;
|
||
|
|
gg.game.CurentSelectBattleDifficulty = ChapterDifficulty.Normal;
|
||
|
|
// 首登流程跑完再标记,避免本会话后续重复发奖;小游戏 restart 依赖存档 status=0 再次进入本分支
|
||
|
|
gg.data.markFirstLoginCompleted();
|
||
|
|
}
|
||
|
|
|
||
|
|
gg.data.saveToServer();
|
||
|
|
|
||
|
|
if (gg.data.doc.chapter == 1) {
|
||
|
|
gg.data.project.vigourCost = 0
|
||
|
|
this.enterBattle()
|
||
|
|
} else {
|
||
|
|
await BattleResLoader.ensureHallBundles();
|
||
|
|
this.enterMain();
|
||
|
|
}
|
||
|
|
|
||
|
|
gg.sdk.reportDY("inLevel", `加载耗时-资源加载完成_${Math.floor(sdkConfig.resLoadFinishTime / 1000)}s`)
|
||
|
|
gg.sdk.reportDY("inLevel", `加载耗时-sdk加载完成_${Math.floor(sdkConfig.sdkLoadFinishTime / 1000)}s`)
|
||
|
|
|
||
|
|
this.isInitAllData = true
|
||
|
|
|
||
|
|
console.log("初始化game完成");
|
||
|
|
|
||
|
|
|
||
|
|
this.scheduleOnce(() => {
|
||
|
|
let getLaunchOptionsSync = gg.sdk.getLaunchOptionsSync_custom()
|
||
|
|
console.log('getLaunchOptionsSync', JSON.stringify(getLaunchOptionsSync))
|
||
|
|
if (getLaunchOptionsSync && getLaunchOptionsSync.scene && String(getLaunchOptionsSync.scene).includes('3041')) {
|
||
|
|
const channel = (getLaunchOptionsSync.query as { feed_game_channel?: string | number } | null | undefined)?.feed_game_channel;
|
||
|
|
if (channel == 1) {
|
||
|
|
gg.sdk.PlatformReportScene()
|
||
|
|
gg.sdk.reportDY("inLevel", `推荐流-复访用户`);
|
||
|
|
//离线收益场景
|
||
|
|
//this.showOutGame()
|
||
|
|
} else if (channel == 2) {
|
||
|
|
gg.sdk.PlatformReportScene()
|
||
|
|
gg.sdk.reportDY("inLevel", `推荐流-获客用户`);
|
||
|
|
}
|
||
|
|
|
||
|
|
} else {
|
||
|
|
//this.showOutGame()
|
||
|
|
}
|
||
|
|
}, 0.2)
|
||
|
|
|
||
|
|
|
||
|
|
gg.targetTaskManager.initCurTargetTaskConfig()
|
||
|
|
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 获取章节id(取服务器端控制的章节id)
|
||
|
|
* @param chapterID 章节ID
|
||
|
|
* @returns 章节数据
|
||
|
|
*/
|
||
|
|
getChapter(chapterID) {
|
||
|
|
if (gg.sdk.levelS.length > chapterID - 1)
|
||
|
|
return gg.sdk.levelS[chapterID - 1];
|
||
|
|
return chapterID;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**获取原始章节id */
|
||
|
|
getOriginChapterID(chapterID) {
|
||
|
|
let idx = gg.sdk.levelS.findIndex((id) => id == chapterID);
|
||
|
|
if (idx >= 0)
|
||
|
|
return idx + 1;
|
||
|
|
return chapterID;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 重置游戏
|
||
|
|
*/
|
||
|
|
reset(tagStr: string = '') {
|
||
|
|
gg.data.reset((res) => {
|
||
|
|
if (res) {
|
||
|
|
if (gg.sdk.pf) {
|
||
|
|
assetManager.cacheManager.clearCache();
|
||
|
|
gg.sdk.pf["restartMiniProgramSync"]();
|
||
|
|
} else {
|
||
|
|
this.playerHp = 0;
|
||
|
|
this.playerCoinNum = 0;
|
||
|
|
this.isShowObstacle = true
|
||
|
|
this.monsterNumScale = 1;
|
||
|
|
|
||
|
|
this.playerWeaponIdArray = []
|
||
|
|
this.playerSkillIdArray = []
|
||
|
|
this.playerSkillGroupIdArray = []
|
||
|
|
this.playerWeaponGroupIdArray = []
|
||
|
|
this.setPlayerSkillGroupIdArray = false
|
||
|
|
this.setPlayerWeaponGroupIdArray = false
|
||
|
|
gg.game.CurentSelectBattleType = 1
|
||
|
|
this.unscheduleAllCallbacks();
|
||
|
|
GEvent.Ins.off(GEvent.UpdateItemNum, this.onItemUpdate, this);
|
||
|
|
gg.ui.closeAllPanel();
|
||
|
|
// gg.task.init();
|
||
|
|
setTimeout(() => {
|
||
|
|
this.init();
|
||
|
|
|
||
|
|
}, 10);
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
gg.ui.showToast("重置游戏失败");
|
||
|
|
}
|
||
|
|
}, tagStr);
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 每帧更新
|
||
|
|
* @param deltaTime
|
||
|
|
*/
|
||
|
|
update(deltaTime: number) {
|
||
|
|
this.GameTime += deltaTime;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 每秒更新逻辑
|
||
|
|
*/
|
||
|
|
onSecond() {
|
||
|
|
//游戏总整秒数
|
||
|
|
let gs = Math.floor(this.GameTime);
|
||
|
|
|
||
|
|
let onlineTime = Math.floor(gg.data.OnlineTimeForAll);
|
||
|
|
|
||
|
|
//每3分钟打点
|
||
|
|
if (Math.floor(onlineTime) % 60 == 0) {
|
||
|
|
let index = Math.floor(onlineTime / 60);
|
||
|
|
if (index == 1 || index == 2) {
|
||
|
|
gg.sdk.reportDY("inLevel", `累积时长-${index}分钟`);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (Math.floor(onlineTime) % 180 == 0) {
|
||
|
|
let index = Math.floor(onlineTime / 60);
|
||
|
|
gg.sdk.reportDY("inLevel", `累积时长-${index}分钟`);
|
||
|
|
}
|
||
|
|
|
||
|
|
//每分钟执行的逻辑
|
||
|
|
if (gs % 60 == 0) {
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
GEvent.Ins.emit(GEvent.SecondUpdate);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 物品更新
|
||
|
|
*/
|
||
|
|
onItemUpdate() {
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 回大厅/主界面时释放战斗阶段累积的静态缓存(与 enterMain 配套)。
|
||
|
|
*/
|
||
|
|
private releaseStaticCachesLeavingBattle(): void {
|
||
|
|
BattleDiagnostics.resetBattle();
|
||
|
|
TetriStackDiagnostics.resetBattle();
|
||
|
|
PfDiagnostics.resetBattleSession();
|
||
|
|
BattlePerformance.resetMemoryPressure();
|
||
|
|
this.CurentBattle = null;
|
||
|
|
MapAgent.clearStaticWallPointPool();
|
||
|
|
MTools.clearBeforeTimesMapForEnterHall();
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 离开战斗:先标记结束并关面板,再清全局引用(避免 update 访问 null) */
|
||
|
|
private leaveBattleToHall(): void {
|
||
|
|
this._battleLeaving = true;
|
||
|
|
const battle = this.CurentBattle;
|
||
|
|
if (battle) {
|
||
|
|
battle.IsGameOver = true;
|
||
|
|
battle.shutdownBattlePhysicsForLeave();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private finishLeaveBattleCleanup(): void {
|
||
|
|
this.releaseStaticCachesLeavingBattle();
|
||
|
|
this._battleLeaving = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 进入主场景
|
||
|
|
*/
|
||
|
|
enterMain(pageeID: UIID = UIID.Home) {
|
||
|
|
gg.ui.clearWaitPopQueue();
|
||
|
|
this.leaveBattleToHall();
|
||
|
|
const hasMainUI = gg.ui.hasPanel(UIID.MainUI);
|
||
|
|
void this._returnToMainUI(pageeID, hasMainUI);
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 离开战斗后统一释放内存并打开大厅(只卸战斗内存,不删磁盘缓存) */
|
||
|
|
private async _returnToMainUI(pageeID: UIID, hasMainUI: boolean): Promise<void> {
|
||
|
|
if (!hasMainUI) {
|
||
|
|
this.showLoading(1);
|
||
|
|
}
|
||
|
|
const startTime = new Date().getTime();
|
||
|
|
await gg.ui.closeAllPanelAsync();
|
||
|
|
gg.ui.clearAnimFlyNodes();
|
||
|
|
this.finishLeaveBattleCleanup();
|
||
|
|
gg.res.clearResPkg(ResPkgName.BattleRes);
|
||
|
|
gg.res.clearNodePool();
|
||
|
|
BattleResLoader.releaseBattleBundles();
|
||
|
|
|
||
|
|
if (!hasMainUI) {
|
||
|
|
await BattleResLoader.ensureHallBundles();
|
||
|
|
await new Promise<void>((resolve) => {
|
||
|
|
gg.res.loadResPakg(getMainResPakage(), (c, t) => {
|
||
|
|
if (DEV) {
|
||
|
|
console.log("加载主场景资源", c, t);
|
||
|
|
}
|
||
|
|
}, () => resolve());
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
await gg.ui.openPanel(UIID.MainUI, { data: pageeID });
|
||
|
|
|
||
|
|
// 智能预载接管:邻接 Tab + 战斗包错峰
|
||
|
|
SmartPreload.onMainTab(pageeID || UIID.Home);
|
||
|
|
|
||
|
|
if (!hasMainUI) {
|
||
|
|
sdkConfig.enterHallTime = new Date().getTime() - startTime;
|
||
|
|
gg.sdk.reportDY("inLevel", `加载耗时-进入大厅_${Math.floor(sdkConfig.enterHallTime / 1000)}s`);
|
||
|
|
this.hideLoading();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 加载战斗分包与核心 prefab,完成后打开战斗界面。
|
||
|
|
* weapon1 单预制体很快,可 await 保证首枪就绪;地图与开界面并行预热。
|
||
|
|
* 体感慢主要来自:回大厅卸分包导致二次冷启动、局内地图 ensure,而不是 weapon1。
|
||
|
|
*/
|
||
|
|
private async loadBattleAndOpen(panelId: UIID, onLoaded?: () => void): Promise<void> {
|
||
|
|
await BattleResLoader.ensureBattleEntry();
|
||
|
|
await BattleResLoader.ensureBattleCoreBundles();
|
||
|
|
|
||
|
|
const mapId = gg.game.CurentBattle?.MapId;
|
||
|
|
const mapItem = mapId ? getBattleMapResItem(mapId) : null;
|
||
|
|
if (mapItem) {
|
||
|
|
await gg.res.ensurePrefab(mapItem.name, mapItem.path, mapItem.bundle);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 单个 weapon1 + hitEffect,耗时通常远小于地图/界面;await 可避免首枪空等
|
||
|
|
await BattleResLoader.preloadWeapon1Bullet();
|
||
|
|
await gg.ui.openPanel(panelId);
|
||
|
|
|
||
|
|
const duration = new Date().getTime() - sdkConfig.enterBattleTime;
|
||
|
|
console.log("章节进入-战斗场景加载完成", duration + "ms");
|
||
|
|
gg.sdk.reportDY("inLevel", `章节进入-战斗场景加载完成`);
|
||
|
|
onLoaded?.();
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 进入战斗场景
|
||
|
|
*/
|
||
|
|
enterBattle(notCheckPower = false) {
|
||
|
|
//检查有没有装备八个武器
|
||
|
|
gg.game.IsPlayedGame = false
|
||
|
|
gg.game.playerGameFail = false
|
||
|
|
let equippedArr = gg.data.project.getEquippedWeaponDataList();
|
||
|
|
if (equippedArr.length < 8) {
|
||
|
|
gg.ui.showToast('需装备8个武器!')
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
//检查体力是否足够
|
||
|
|
if (!gg.data.project.checkVigourEnough(this.CurentSelectBattleType) && gg.data.doc.chapter != 1 ) {
|
||
|
|
gg.ui.openPanel(UIID.buyPower);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!notCheckPower && !gg.data.project.checkPowerEnough(this.CurentSelectChapterId, this.CurentSelectBattleType, this.CurentSelectBattleDifficulty) && !notCheckPower) {
|
||
|
|
//战力不足
|
||
|
|
gg.ui.openPanel(UIID.challengeTip, {
|
||
|
|
data: () => {
|
||
|
|
|
||
|
|
this.enterBattle(true);
|
||
|
|
}
|
||
|
|
})
|
||
|
|
return 2;
|
||
|
|
}
|
||
|
|
|
||
|
|
if(gg.data.doc.chapter == 1){
|
||
|
|
//不扣体力
|
||
|
|
console.log("第1关,不扣体力");
|
||
|
|
}else{
|
||
|
|
gg.data.subVigour(gg.data.project.vigourCost);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
this.showLoading(1);
|
||
|
|
//清除_wait_popIds队列
|
||
|
|
SmartPreload.pause();
|
||
|
|
HomeBattlePreload.pause();
|
||
|
|
|
||
|
|
gg.ui.clearWaitPopQueue();
|
||
|
|
gg.ui.closeAllPanel();
|
||
|
|
gg.ui.clearAnimFlyNodes();
|
||
|
|
sdkConfig.enterBattleTime = new Date().getTime();
|
||
|
|
BattleDiagnostics.resetBattle();
|
||
|
|
TetriStackDiagnostics.resetBattle();
|
||
|
|
PfDiagnostics.resetBattleSession();
|
||
|
|
this.CurentBattle = new BattleCore();
|
||
|
|
this.CurentBattle.init();
|
||
|
|
if(gg.game.CurentSelectBattleType == BattleType.OrangeWeaponMode_Ya){
|
||
|
|
//进入救救我鸭的副本
|
||
|
|
gg.sdk.reportDY("inLevel", `章节进入-战斗场景加载中`);
|
||
|
|
this.scheduleOnce(() => {
|
||
|
|
void this.loadBattleAndOpen(UIID.jiujiuwoyaGame);
|
||
|
|
})
|
||
|
|
|
||
|
|
|
||
|
|
}else{
|
||
|
|
gg.sdk.reportDY("inLevel", `章节进入-战斗场景加载中`);
|
||
|
|
this.scheduleOnce(() => {
|
||
|
|
void this.loadBattleAndOpen(UIID.TetriGame);
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 获取所有秒赚
|
||
|
|
* @returns
|
||
|
|
*/
|
||
|
|
getAllProfit() {
|
||
|
|
let num = 0;
|
||
|
|
return Math.floor(num);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 弹出道具不足弹窗
|
||
|
|
* @param skipUIidArr 需要跳转的UIID数组
|
||
|
|
* @param itemID 道具id
|
||
|
|
* @param itemNum 道具数量
|
||
|
|
* @param showVideoAd 是否显示视频广告获取
|
||
|
|
* @returns
|
||
|
|
*/
|
||
|
|
showSkipPop(itemID = 0, itemNum = 1, showVideoAd = false, skipUIidArr: UIID[] = [], title = "道具不足") {
|
||
|
|
gg.ui.openPanel(UIID.SkipPop, { data: { skipUIidArr: skipUIidArr, itemID: itemID, itemNum: itemNum, showVideoAd: showVideoAd, title: title } });
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
async getUserInfo() {
|
||
|
|
this.UserInfo = (await gg.sdk.getUserInfo()) as IUserInfo;
|
||
|
|
if (this.UserInfo) {
|
||
|
|
gg.data.doc.uid = this.UserInfo.nickName;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async showMsgBox(content: string, title: string = "温馨提示", yesFun: Function = null, noFun: Function = null) {
|
||
|
|
gg.ui.openPanel(UIID.MsgBox, {
|
||
|
|
data: {
|
||
|
|
title: title,
|
||
|
|
content: content,
|
||
|
|
yesFun: yesFun,
|
||
|
|
noFun: noFun
|
||
|
|
}
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**获取最大免费扫荡次数 */
|
||
|
|
getMaxFreeSweepTimes() {
|
||
|
|
let count = 3;
|
||
|
|
//count += gg.purchase.getFreeSweepTimes();
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**根据奖励字符串解析奖励道具(只能解析固定格式的字符串:“道具id,道具数量|道具id,道具数量|...”) */
|
||
|
|
parseRewardStr(rewardStr: string) {
|
||
|
|
let rewardArr = rewardStr.split("|");
|
||
|
|
let rewardList: ItemData[] = [];
|
||
|
|
for (let i = 0; i < rewardArr.length; i++) {
|
||
|
|
let str = rewardArr[i];
|
||
|
|
if (str == "") continue;
|
||
|
|
let [id, num] = rewardArr[i].split(",");
|
||
|
|
let itemData = new ItemData(Number(id), Number(num));
|
||
|
|
itemData.staticData = gg.data.table.getItemData(Number(id));
|
||
|
|
itemData.isFragment = itemData.staticData.type == 8;
|
||
|
|
rewardList.push(itemData);
|
||
|
|
}
|
||
|
|
return rewardList;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**根据道具数组合并同类型道具 */
|
||
|
|
mergeSameTypeItem(itemList: ItemData[]) {
|
||
|
|
let itemMap = new Map<number, ItemData>();
|
||
|
|
for (let i = 0; i < itemList.length; i++) {
|
||
|
|
let item = itemList[i];
|
||
|
|
if (itemMap.has(item.id)) {
|
||
|
|
itemMap.get(item.id).num += item.num;
|
||
|
|
} else {
|
||
|
|
itemMap.set(item.id, item);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return Array.from(itemMap.values());
|
||
|
|
}
|
||
|
|
|
||
|
|
/**根据道具数组自动获得通用碎片转化的道具 */
|
||
|
|
autoMergeFragment(itemList: ItemData[]) {
|
||
|
|
let fragmentList = itemList.filter(x => x.staticData.type == 4);
|
||
|
|
if (fragmentList.length == 0) return itemList;
|
||
|
|
let resultArr = [];
|
||
|
|
//增加碎片,相当于开了个宝箱一样给对应数量的碎片
|
||
|
|
for (let i = 0; i < fragmentList.length; i++) {
|
||
|
|
let fragment = fragmentList[i];
|
||
|
|
let boxRewardArray = gg.data.getFragmentNumArray(fragment.id + ',' + fragment.num)
|
||
|
|
let statsArray = MTools.mergeToStatsArray(boxRewardArray)
|
||
|
|
for (let i = 0; i < statsArray.length; i++) {
|
||
|
|
let array = statsArray[i].split('|')
|
||
|
|
let item = new ItemData(Number(array[0]), Number(array[1]))
|
||
|
|
item.num = Number(array[1])
|
||
|
|
item.id = Number(array[0])
|
||
|
|
resultArr.push(item);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return resultArr;
|
||
|
|
}
|
||
|
|
|
||
|
|
getRoleLv() {
|
||
|
|
let curData = gg.data.getStringData(OtherDataType.UserLevelupData)
|
||
|
|
let curDataArr = curData.split('&')
|
||
|
|
let roleLevel = curDataArr[0]
|
||
|
|
return Number(roleLevel);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**根据奖励字符串显示获得奖励界面 */
|
||
|
|
showGetRewardByReardStr(rewardStr: string, callBack=null) {
|
||
|
|
let rewardsStrArr = rewardStr.split("|");
|
||
|
|
//直接增加
|
||
|
|
let itemsArray = [];
|
||
|
|
for (let i = 0; i < rewardsStrArr.length; i++) {
|
||
|
|
let signReward = rewardsStrArr[i]
|
||
|
|
let [id, num] = signReward.split(",");
|
||
|
|
let itemData = gg.data.table.getItemData(Number(id));
|
||
|
|
|
||
|
|
if (itemData.type == 8) {
|
||
|
|
//给个武器
|
||
|
|
let item = new ItemData(itemData.weapon_type, Number(num))
|
||
|
|
item.num = Number(num)
|
||
|
|
item.id = itemData.weapon_type
|
||
|
|
item.isFragment = true
|
||
|
|
itemsArray.push(item)
|
||
|
|
}
|
||
|
|
else if (itemData.type == 4) {
|
||
|
|
//增加碎片,相当于开了个宝箱一样给对应数量的碎片
|
||
|
|
let boxRewardArray = gg.data.getFragmentNumArray(id + ',' + num)
|
||
|
|
let statsArray = MTools.mergeToStatsArray(boxRewardArray)
|
||
|
|
for (let i = 0; i < statsArray.length; i++) {
|
||
|
|
let array = statsArray[i].split('|')
|
||
|
|
let item = new ItemData(Number(array[0]), Number(array[1]))
|
||
|
|
item.num = Number(array[1])
|
||
|
|
item.id = Number(array[0])
|
||
|
|
itemsArray.push(item);
|
||
|
|
}
|
||
|
|
} else if (itemData.type == 5) {
|
||
|
|
//转换为对应性别的皮肤
|
||
|
|
let itemid = gg.role.getSkinItemIdFromReward(Number(id));
|
||
|
|
if (itemid) {
|
||
|
|
id = itemid.toString();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
let item = new ItemData(id, Number(num))
|
||
|
|
item.num = Number(num);
|
||
|
|
item.staticData = itemData
|
||
|
|
itemsArray.push(item)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if(callBack){
|
||
|
|
gg.ui.openPanel(UIID.GetReward, {
|
||
|
|
data: itemsArray,
|
||
|
|
onClose: callBack
|
||
|
|
});
|
||
|
|
}else{
|
||
|
|
gg.ui.openPanel(UIID.GetReward, {
|
||
|
|
data: itemsArray
|
||
|
|
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
showGetRewardByItemList(items: ItemData[]) {
|
||
|
|
//直接增加
|
||
|
|
let itemsArray = [];
|
||
|
|
for (let i = 0; i < items.length; i++) {
|
||
|
|
let itemData = items[i];
|
||
|
|
if (!itemData.staticData) itemData.staticData = gg.data.table.getItemData(itemData.id);
|
||
|
|
|
||
|
|
if (itemData.staticData && itemData.staticData.type == 8) {
|
||
|
|
//给个武器
|
||
|
|
let item = new ItemData(itemData.staticData.weapon_type, Number(itemData.num))
|
||
|
|
item.num = Number(itemData.num)
|
||
|
|
item.id = itemData.staticData.weapon_type
|
||
|
|
item.isFragment = true
|
||
|
|
itemsArray.push(item)
|
||
|
|
}
|
||
|
|
else if (itemData.staticData && itemData.staticData.type == 4) {
|
||
|
|
//增加碎片,相当于开了个宝箱一样给对应数量的碎片
|
||
|
|
let boxRewardArray = gg.data.getFragmentNumArray(itemData.staticData.id + ',' + itemData.num)
|
||
|
|
let statsArray = MTools.mergeToStatsArray(boxRewardArray)
|
||
|
|
for (let i = 0; i < statsArray.length; i++) {
|
||
|
|
let array = statsArray[i].split('|')
|
||
|
|
let item = new ItemData(Number(array[0]), Number(array[1]))
|
||
|
|
item.num = Number(array[1])
|
||
|
|
item.id = Number(array[0])
|
||
|
|
itemsArray.push(item);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
itemsArray.push(itemData)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
gg.ui.openPanel(UIID.GetReward, {
|
||
|
|
data: itemsArray
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
showLoading(index: number = 2) {
|
||
|
|
GEvent.Ins.emit(GEvent.SHOW_LOADING, index);
|
||
|
|
}
|
||
|
|
|
||
|
|
hideLoading() {
|
||
|
|
GEvent.Ins.emit(GEvent.HIDE_LOADING);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**根据 id 跳转主界面 Tab;已在大厅时仅切页,否则走 enterMain 回厅流程 */
|
||
|
|
gotoPageById(id: UIID): void {
|
||
|
|
const cfg = UIConfigs[id];
|
||
|
|
if (!cfg || cfg.layer !== UILayer.MainUI) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (this.switchMainTabIfInHall(id)) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
this.enterMain(id);
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 任务等统一跳转:主 Tab 切页,弹窗叠层打开,丧尸页先拉资源 */
|
||
|
|
gotoTaskJump(pageID: UIID): void {
|
||
|
|
const cfg = UIConfigs[pageID];
|
||
|
|
if (!cfg) {
|
||
|
|
console.warn('[gotoTaskJump] unknown pageID', pageID);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (cfg.layer === UILayer.MainUI) {
|
||
|
|
this.gotoPageById(pageID);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (pageID === UIID.zombieLayer) {
|
||
|
|
this.showLoading(1);
|
||
|
|
const resPkg = getZombieResPakage();
|
||
|
|
gg.res.loadResPakg(resPkg, null, () => {
|
||
|
|
this.hideLoading();
|
||
|
|
gg.ui.openPanel(UIID.zombieLayer);
|
||
|
|
});
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (gg.ui.hasPanel(UIID.MainUI)) {
|
||
|
|
gg.ui.openPanel(pageID);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
this.enterMain(UIID.Home);
|
||
|
|
this.scheduleOnce(() => gg.ui.openPanel(pageID), 0.5);
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 已打开 MainUI 时切换底部 Tab,避免误走 enterMain 关闭全部面板 */
|
||
|
|
switchMainTabIfInHall(pageID: UIID): boolean {
|
||
|
|
if (!gg.ui.hasPanel(UIID.MainUI)) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
const mainUI = UIMainUI.resolveShell();
|
||
|
|
if (!mainUI?.isValid) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
void mainUI.showPage(pageID);
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
onLoadingShow(c: number, t: number) {
|
||
|
|
this.CurentLoadingProgress = clamp01(c / t);
|
||
|
|
console.log("onLoadingShow", c, t);
|
||
|
|
|
||
|
|
}
|
||
|
|
CurentLoadingProgress: number = 0;
|
||
|
|
|
||
|
|
isPreLoadChapterRes: boolean = false;
|
||
|
|
private _pendingPreLoadChapterId: number = 0;
|
||
|
|
private _runningPreLoadChapterId: number = 0;
|
||
|
|
/**预加载当前章节所需要的资源 */
|
||
|
|
preLoadChapterRes(chapterID: number) {
|
||
|
|
this.scheduleOnce(() => {
|
||
|
|
void (async () => {
|
||
|
|
await BattleResLoader.ensureBattleEntry();
|
||
|
|
const mapItem = getBattleMapResItemByChapter(chapterID);
|
||
|
|
if (!mapItem) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
// 局内/结算叠层时禁止 clearResPkg(BattleRes),否则会释放场上仍在渲染的 prefab/贴图
|
||
|
|
if (gg.game.CurentBattle) {
|
||
|
|
await gg.res.ensurePrefab(mapItem.name, mapItem.path, mapItem.bundle);
|
||
|
|
gg.res.appendToResPkg(ResPkgName.BattleRes, [mapItem]);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
gg.res.loadResPakg({
|
||
|
|
name: ResPkgName.BattleRes,
|
||
|
|
isReleaseable: true,
|
||
|
|
ress: [mapItem],
|
||
|
|
}, null, () => { /* 不主动 triggerGC */ });
|
||
|
|
})();
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
}
|