GameManager.ts 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359
  1. import { _decorator, Component, Node, Prefab, instantiate, Vec3, find, director, Canvas, UITransform, Button, Label, ProgressBar, EPhysics2DDrawFlags, sys } from 'cc';
  2. import { LevelManager } from './LevelManager';
  3. import { LevelConfigManager } from './LevelConfigManager';
  4. import { SaveDataManager } from './SaveDataManager';
  5. // import { ShopManager } from '../ShopSystem/ShopManager';
  6. import { ConfigManager } from '../Core/ConfigManager';
  7. import { EnemyController } from '../CombatSystem/EnemyController';
  8. import EventBus, { GameEvents } from '../Core/EventBus';
  9. import { PhysicsManager } from '../Core/PhysicsManager';
  10. import { BallController } from '../CombatSystem/BallController';
  11. import { BlockManager } from '../CombatSystem/BlockManager';
  12. import { LevelSessionManager } from '../Core/LevelSessionManager';
  13. import { GameStartMove } from '../Animations/GameStartMove';
  14. import { GameBlockSelection } from '../CombatSystem/BlockSelection/GameBlockSelection';
  15. import { BlockTag } from '../CombatSystem/BlockSelection/BlockTag';
  16. import { GamePause } from '../CombatSystem/GamePause';
  17. const { ccclass, property } = _decorator;
  18. /**
  19. * 游戏状态枚举
  20. */
  21. enum GameState {
  22. PLAYING = 'playing',
  23. SUCCESS = 'success',
  24. DEFEAT = 'defeat',
  25. PAUSED = 'paused',
  26. BLOCK_SELECTION = 'block_selection'
  27. }
  28. /**
  29. * 增强版游戏管理器
  30. * 整合了游戏启动、状态管理、UI控制等功能
  31. */
  32. @ccclass('GameManager')
  33. export class GameManager extends Component {
  34. // === 原GameManager属性 ===
  35. @property({
  36. type: Node,
  37. tooltip: '拖拽BallController节点到这里'
  38. })
  39. public ballController: Node = null;
  40. @property({
  41. type: Node,
  42. tooltip: '拖拽GameBlockSelection节点到这里'
  43. })
  44. public gameBlockSelection: Node = null;
  45. @property({
  46. type: Node,
  47. tooltip: '拖拽GameArea节点到这里'
  48. })
  49. public gameArea: Node = null;
  50. @property({
  51. type: Node,
  52. tooltip: '拖拽EnemyController节点到这里'
  53. })
  54. public enemyManager: Node = null;
  55. // === 游戏状态管理属性 ===
  56. @property({
  57. type: Node,
  58. tooltip: '血量显示节点 (HeartLabeld)'
  59. })
  60. public heartLabelNode: Node = null;
  61. @property({
  62. type: Node,
  63. tooltip: '游戏成功UI节点 (GameSuccess)'
  64. })
  65. public gameSuccessUI: Node = null;
  66. @property({
  67. type: Node,
  68. tooltip: '游戏失败UI节点 (GameDefeat)'
  69. })
  70. public gameDefeatUI: Node = null;
  71. // === 能量与技能选择 UI ===
  72. @property({
  73. type: Node,
  74. tooltip: '拖拽 EnergyBar (ProgressBar) 节点到这里'
  75. })
  76. public energyBarNode: Node = null;
  77. @property({
  78. type: Node,
  79. tooltip: '拖拽 SelectSkillUI 节点到这里'
  80. })
  81. public selectSkillUI: Node = null;
  82. // === 游戏配置属性 ===
  83. // 墙体基础血量由存档决定,不再通过属性面板设置
  84. private wallHealth: number = 100;
  85. @property({
  86. tooltip: '初始血量'
  87. })
  88. public initialHealth: number = 100;
  89. @property({
  90. tooltip: '状态检查间隔(秒)'
  91. })
  92. public checkInterval: number = 1.0;
  93. // === 私有属性 ===
  94. private gameStarted: boolean = false;
  95. private currentHealth: number = 100;
  96. private currentState: GameState = GameState.PLAYING;
  97. private checkTimer: number = 0;
  98. private heartLabel: Label = null;
  99. private enemyController: EnemyController = null;
  100. private levelManager: LevelManager = null;
  101. private levelConfigManager: LevelConfigManager = null;
  102. private saveDataManager: SaveDataManager = null;
  103. // private shopManager: ShopManager = null;
  104. private configManager: ConfigManager = null;
  105. private enemySpawningStarted: boolean = false;
  106. private totalEnemiesSpawned: number = 0;
  107. private currentWave: number = 1;
  108. private currentWaveEnemyCount: number = 0;
  109. private currentWaveTotalEnemies: number = 0; // 当前波次总敌人数
  110. private levelWaves: any[] = []; // 关卡波次配置
  111. private levelTotalEnemies: number = 0; // 本关卡总敌人数
  112. private enemiesKilled: number = 0; // 已击杀敌人数量
  113. // 游戏计时器
  114. private gameStartTime: number = 0;
  115. private gameEndTime: number = 0;
  116. // 游戏区域的边界
  117. private gameBounds = {
  118. left: 0,
  119. right: 0,
  120. top: 0,
  121. bottom: 0
  122. };
  123. private preparingNextWave = false;
  124. // 能量系统
  125. private energyPoints: number = 0;
  126. private readonly ENERGY_MAX: number = 5;
  127. private energyBar: ProgressBar = null;
  128. // GameBlockSelection组件
  129. private blockSelectionComponent: GameBlockSelection = null;
  130. private pendingSkillSelection: boolean = false;
  131. start() {
  132. // 初始化物理系统
  133. this.initPhysicsSystem();
  134. // 初始化管理器
  135. this.initializeManagers();
  136. // 提前初始化本局数据,确保 BlockManager 在 start 时能拿到正确金币
  137. if (!LevelSessionManager.inst.runtime) {
  138. LevelSessionManager.inst.initialize(
  139. this.saveDataManager?.getCurrentLevel() || 1,
  140. this.wallHealth
  141. );
  142. }
  143. // 计算游戏区域边界
  144. this.calculateGameBounds();
  145. // 初始化游戏状态
  146. this.initializeGameState();
  147. // 查找UI节点
  148. this.findUINodes();
  149. // 查找敌人控制器
  150. this.findEnemyController();
  151. // 初始化墙体血量显示
  152. this.initWallHealthDisplay();
  153. // 设置敌人控制器
  154. this.setupEnemyController();
  155. // 设置UI按钮
  156. this.setupUIButtons();
  157. // 初始化GameBlockSelection组件
  158. this.initGameBlockSelection();
  159. // 加载当前关卡配置
  160. this.loadCurrentLevelConfig();
  161. // 监听GamePause状态变化事件
  162. this.setupGamePauseEventListeners();
  163. }
  164. /**
  165. * 设置GamePause事件监听器
  166. */
  167. private setupGamePauseEventListeners() {
  168. const eventBus = EventBus.getInstance();
  169. // 监听游戏成功事件
  170. eventBus.on(GameEvents.GAME_SUCCESS, this.onGameSuccessEvent, this);
  171. // 监听游戏失败事件
  172. eventBus.on(GameEvents.GAME_DEFEAT, this.onGameDefeatEvent, this);
  173. // 监听敌人被击杀事件
  174. eventBus.on('ENEMY_KILLED', this.onEnemyKilledEvent, this);
  175. }
  176. /**
  177. * 处理游戏成功事件
  178. */
  179. private onGameSuccessEvent() {
  180. console.log('[GameManager] 接收到游戏成功事件,执行成功处理');
  181. // 显示游戏成功UI
  182. if (this.gameSuccessUI) {
  183. this.gameSuccessUI.active = true;
  184. }
  185. // 执行游戏成功逻辑
  186. this.onGameSuccess();
  187. }
  188. /**
  189. * 处理游戏失败事件
  190. */
  191. private onGameDefeatEvent() {
  192. console.log('[GameManager] 接收到游戏失败事件,执行失败处理');
  193. // 显示游戏失败UI
  194. if (this.gameDefeatUI) {
  195. this.gameDefeatUI.active = true;
  196. }
  197. // 执行游戏失败逻辑
  198. this.onGameDefeat();
  199. }
  200. /**
  201. * 处理敌人被击杀事件
  202. */
  203. private onEnemyKilledEvent() {
  204. // 如果游戏已经结束,不执行后续逻辑
  205. const gamePause = GamePause.getInstance();
  206. if (gamePause.isGameOver()) {
  207. console.warn('[GameManager] 游戏已结束状态下onEnemyKilledEvent被调用!当前状态:', gamePause.getCurrentState());
  208. return;
  209. }
  210. this.enemiesKilled++;
  211. // 当前波击杀 +1
  212. this.currentWaveEnemyCount++;
  213. const remaining = this.currentWaveTotalEnemies - this.currentWaveEnemyCount;
  214. console.log(`[GameManager] 敌人被消灭,当前波剩余敌人: ${remaining}/${this.currentWaveTotalEnemies}, 总击杀: ${this.enemiesKilled}`);
  215. // 先检查是否波次结束,如果结束则先设置状态
  216. if (remaining <= 0) {
  217. // 当前波结束
  218. if (this.currentWave < (this.levelWaves?.length || 1)) {
  219. // 还有下一波,先设置状态为方块选择,防止能量满时直接弹出技能选择
  220. this.currentState = GameState.BLOCK_SELECTION;
  221. console.log(`[GameManager] 当前波结束,准备显示下一波(${this.currentWave + 1}/${this.levelWaves?.length || 1})选择UI`);
  222. this.showNextWavePrompt();
  223. } else {
  224. // 最后一波也结束
  225. console.log('[GameManager] 最后一波结束,触发游戏胜利');
  226. this.triggerGameSuccess();
  227. }
  228. }
  229. // 增加能量点(在状态设置之后)
  230. this.incrementEnergy();
  231. }
  232. // === 暂停游戏 ===
  233. private pauseGame() {
  234. // 使用GamePause统一管理暂停逻辑
  235. const gamePause = GamePause.getInstance();
  236. if (gamePause) {
  237. gamePause.pauseGame();
  238. }
  239. }
  240. // === 恢复游戏 ===
  241. public resumeGame() {
  242. // 使用GamePause统一管理恢复逻辑
  243. const gamePause = GamePause.getInstance();
  244. if (gamePause) {
  245. gamePause.resumeGame();
  246. }
  247. if (this.gameSuccessUI) {
  248. this.gameSuccessUI.active = false;
  249. }
  250. if (this.gameDefeatUI) {
  251. this.gameDefeatUI.active = false;
  252. }
  253. }
  254. update(deltaTime: number) {
  255. if (this.currentState !== GameState.PLAYING) {
  256. return;
  257. }
  258. // 更新检查计时器
  259. this.checkTimer += deltaTime;
  260. if (this.checkTimer >= this.checkInterval) {
  261. this.checkTimer = 0;
  262. this.checkGameState();
  263. }
  264. // 检查自动保存
  265. if (this.saveDataManager) {
  266. this.saveDataManager.checkAutoSave();
  267. }
  268. }
  269. // === 物理系统初始化 ===
  270. private initPhysicsSystem() {
  271. // 确保 PhysicsManager 单例存在
  272. let pm = PhysicsManager.getInstance();
  273. if (!pm) {
  274. const physicsNode = new Node('PhysicsManager');
  275. director.getScene()?.addChild(physicsNode);
  276. pm = physicsNode.addComponent(PhysicsManager);
  277. }
  278. }
  279. // === 管理器初始化 ===
  280. private initializeManagers() {
  281. this.levelManager = LevelManager.getInstance();
  282. // this.shopManager = ShopManager.getInstance();
  283. this.configManager = ConfigManager.getInstance();
  284. this.levelConfigManager = LevelConfigManager.getInstance();
  285. this.enemyController = EnemyController.getInstance() || null;
  286. // 初始化存档管理器
  287. this.saveDataManager = SaveDataManager.getInstance();
  288. this.saveDataManager.initialize();
  289. // 从存档读取墙体基础血量
  290. const pd = this.saveDataManager.getPlayerData();
  291. if (pd && typeof pd.wallBaseHealth === 'number') {
  292. this.wallHealth = pd.wallBaseHealth;
  293. }
  294. }
  295. // === 游戏状态初始化 ===
  296. private initializeGameState() {
  297. this.currentHealth = this.initialHealth;
  298. this.currentState = GameState.PLAYING;
  299. this.checkTimer = 0;
  300. this.enemySpawningStarted = false;
  301. this.totalEnemiesSpawned = 0;
  302. this.currentWave = 1;
  303. this.currentWaveEnemyCount = 0;
  304. this.currentWaveTotalEnemies = 0; // 当前波次总敌人数
  305. // UI 初始化移交给 EnemyController
  306. }
  307. // === 计算游戏区域边界 ===
  308. private calculateGameBounds() {
  309. const canvas = find('Canvas');
  310. if (!canvas) {
  311. return;
  312. }
  313. const canvasUI = canvas.getComponent(UITransform);
  314. if (!canvasUI) {
  315. return;
  316. }
  317. const screenWidth = canvasUI.width;
  318. const screenHeight = canvasUI.height;
  319. const worldPos = canvas.worldPosition;
  320. this.gameBounds.left = worldPos.x - screenWidth / 2;
  321. this.gameBounds.right = worldPos.x + screenWidth / 2;
  322. this.gameBounds.bottom = worldPos.y - screenHeight / 2;
  323. this.gameBounds.top = worldPos.y + screenHeight / 2;
  324. }
  325. // === 查找UI节点 ===
  326. private findUINodes() {
  327. // 查找血量显示节点
  328. if (!this.heartLabelNode) {
  329. this.heartLabelNode = find('Canvas/GameLevelUI/HeartNode/HeartLabeld');
  330. }
  331. if (this.heartLabelNode) {
  332. this.heartLabel = this.heartLabelNode.getComponent(Label);
  333. }
  334. // 查找游戏成功UI
  335. if (!this.gameSuccessUI) {
  336. this.gameSuccessUI = find('Canvas/GameSuccess');
  337. }
  338. if (this.gameSuccessUI) {
  339. this.gameSuccessUI.active = false;
  340. }
  341. // 查找游戏失败UI
  342. if (!this.gameDefeatUI) {
  343. this.gameDefeatUI = find('Canvas/GameDefeat');
  344. }
  345. if (this.gameDefeatUI) {
  346. this.gameDefeatUI.active = false;
  347. }
  348. // 查找能量条
  349. if (!this.energyBarNode) {
  350. this.energyBarNode = find('Canvas/GameLevelUI/EnergyBar');
  351. }
  352. if (this.energyBarNode) {
  353. this.energyBar = this.energyBarNode.getComponent(ProgressBar);
  354. if (this.energyBar) {
  355. this.energyBar.progress = 0;
  356. }
  357. }
  358. // 查找技能选择 UI
  359. if (!this.selectSkillUI) {
  360. this.selectSkillUI = find('Canvas/SelectSkillUI');
  361. }
  362. if (this.selectSkillUI) {
  363. this.selectSkillUI.active = false;
  364. }
  365. }
  366. // === 查找敌人控制器 ===
  367. private findEnemyController() {
  368. if (this.enemyManager) {
  369. this.enemyController = this.enemyManager.getComponent(EnemyController);
  370. }
  371. if (!this.enemyController) {
  372. const enemyNode = find('Canvas/GameLevelUI/EnemyController');
  373. if (enemyNode) {
  374. this.enemyController = enemyNode.getComponent(EnemyController);
  375. }
  376. }
  377. }
  378. // === 初始化墙体血量显示 ===
  379. private initWallHealthDisplay() {
  380. if (this.heartLabelNode && this.heartLabel) {
  381. this.heartLabel.string = this.wallHealth.toString();
  382. }
  383. // 让 EnemyController 自行查找/刷新血量 UI
  384. if (this.enemyController.initWallHealthDisplay) {
  385. this.enemyController.initWallHealthDisplay();
  386. }
  387. }
  388. // === 设置敌人控制器 ===
  389. private setupEnemyController() {
  390. if (!this.enemyManager) {
  391. const gameLevelUI = find('Canvas/GameLevelUI');
  392. if (!gameLevelUI) {
  393. console.error('找不到GameLevelUI节点,无法创建EnemyController');
  394. return;
  395. }
  396. this.enemyManager = new Node('EnemyController');
  397. gameLevelUI.addChild(this.enemyManager);
  398. }
  399. if (!this.enemyController) {
  400. this.enemyController = this.enemyManager.addComponent(EnemyController);
  401. }
  402. // 无论 EnemyController 是否新建,都注入墙体血量
  403. this.enemyController.wallHealth = this.wallHealth;
  404. this.enemyController.updateWallHealthDisplay?.();
  405. }
  406. // === 游戏状态检查 ===
  407. private checkGameState() {
  408. // 更新血量
  409. this.updateHealthFromUI();
  410. if (this.currentHealth <= 0) {
  411. this.triggerGameDefeat();
  412. return;
  413. }
  414. // 检查是否全部击败
  415. if (this.checkAllEnemiesDefeated()) {
  416. this.triggerGameSuccess();
  417. return;
  418. }
  419. }
  420. // === 从UI更新血量 ===
  421. private updateHealthFromUI() {
  422. if (this.heartLabel) {
  423. const healthText = this.heartLabel.string;
  424. const healthMatch = healthText.match(/\d+/);
  425. if (healthMatch) {
  426. const newHealth = parseInt(healthMatch[0]);
  427. if (newHealth !== this.currentHealth) {
  428. this.currentHealth = newHealth;
  429. }
  430. }
  431. }
  432. }
  433. // === 检查所有敌人是否被击败 ===
  434. private checkAllEnemiesDefeated(): boolean {
  435. if (!this.enemyController) {
  436. return false;
  437. }
  438. // 检查敌人是否已开始生成(避免开局就胜利)
  439. if (!this.enemySpawningStarted) {
  440. if (this.enemyController.isGameStarted && this.enemyController.isGameStarted()) {
  441. this.enemySpawningStarted = true;
  442. } else {
  443. return false;
  444. }
  445. }
  446. // 获取当前敌人数量
  447. const currentEnemyCount = this.enemyController.getCurrentEnemyCount ?
  448. this.enemyController.getCurrentEnemyCount() : 0;
  449. // 如果关卡总敌人数已知,以击杀数为准判定胜利
  450. if (this.levelTotalEnemies > 0) {
  451. // 当击杀数达到或超过总敌数且场上没有存活敌人时胜利
  452. return this.enemiesKilled >= this.levelTotalEnemies && currentEnemyCount === 0;
  453. }
  454. // 否则退化到旧逻辑:依赖于是否曾经生成过敌人
  455. // 更新已生成敌人总数(记录曾经达到的最大值)
  456. if (currentEnemyCount > this.totalEnemiesSpawned) {
  457. this.totalEnemiesSpawned = currentEnemyCount;
  458. }
  459. const shouldCheckVictory = this.enemySpawningStarted &&
  460. currentEnemyCount === 0 &&
  461. this.totalEnemiesSpawned > 0;
  462. return shouldCheckVictory;
  463. }
  464. // === 触发游戏失败 ===
  465. private triggerGameDefeat() {
  466. // 使用GamePause统一管理游戏状态
  467. const gamePause = GamePause.getInstance();
  468. gamePause.triggerGameDefeat();
  469. }
  470. // === 触发游戏成功 ===
  471. private triggerGameSuccess() {
  472. // 使用GamePause统一管理游戏状态
  473. const gamePause = GamePause.getInstance();
  474. gamePause.triggerGameSuccess();
  475. }
  476. // === 游戏失败回调 ===
  477. private onGameDefeat() {
  478. this.gameEndTime = Date.now();
  479. // 记录游戏失败到存档
  480. if (this.saveDataManager) {
  481. const currentLevel = this.saveDataManager.getCurrentLevel();
  482. this.saveDataManager.failLevel(currentLevel);
  483. // 更新统计数据
  484. this.saveDataManager.updateStatistic('totalTimePlayed', this.getGameDuration());
  485. }
  486. }
  487. // === 游戏成功回调 ===
  488. private onGameSuccess() {
  489. this.gameEndTime = Date.now();
  490. this.giveReward();
  491. this.onLevelComplete();
  492. }
  493. // === 给予奖励 ===
  494. private giveReward() {
  495. if (!this.saveDataManager) return;
  496. const currentLevel = this.saveDataManager.getCurrentLevel();
  497. const baseReward = currentLevel * 50;
  498. const healthBonus = Math.floor(this.currentHealth * 0.1);
  499. const timeBonus = this.calculateTimeBonus();
  500. const totalCoins = baseReward + healthBonus + timeBonus;
  501. // 给予金币奖励
  502. this.saveDataManager.addCoins(totalCoins, `level_${currentLevel}_complete`);
  503. // 如果是首次完成,给予额外奖励
  504. if (!this.saveDataManager.isLevelCompleted(currentLevel)) {
  505. const firstClearBonus = currentLevel * 25;
  506. this.saveDataManager.addCoins(firstClearBonus, `level_${currentLevel}_first_clear`);
  507. }
  508. }
  509. // === 处理关卡完成 ===
  510. private onLevelComplete(score: number = 0, stars: number = 1) {
  511. if (!this.saveDataManager) return;
  512. const currentLevel = this.saveDataManager.getCurrentLevel();
  513. const gameTime = this.getGameDuration();
  514. // 计算得分(基于剩余血量、用时等)
  515. const calculatedScore = this.calculateScore();
  516. const finalScore = Math.max(score, calculatedScore);
  517. // 计算星级(基于表现)
  518. const calculatedStars = this.calculateStars();
  519. const finalStars = Math.max(stars, calculatedStars);
  520. // 记录关卡完成到存档
  521. this.saveDataManager.completeLevel(currentLevel, finalScore, gameTime, finalStars);
  522. // 更新统计数据
  523. this.saveDataManager.updateStatistic('totalTimePlayed', gameTime);
  524. this.saveDataManager.updateStatistic('totalEnemiesDefeated', this.totalEnemiesSpawned);
  525. // 兼容原有的LevelManager
  526. if (this.levelManager) {
  527. this.levelManager.completeLevel(currentLevel, finalScore, finalStars);
  528. }
  529. }
  530. // === 计算游戏时长 ===
  531. private getGameDuration(): number {
  532. if (this.gameStartTime === 0) return 0;
  533. const endTime = this.gameEndTime || Date.now();
  534. return Math.floor((endTime - this.gameStartTime) / 1000);
  535. }
  536. // === 计算时间奖励 ===
  537. private calculateTimeBonus(): number {
  538. const gameTime = this.getGameDuration();
  539. if (gameTime === 0) return 0;
  540. // 时间越短奖励越多,最多额外50%奖励
  541. const maxTime = 300; // 5分钟
  542. const timeRatio = Math.max(0, (maxTime - gameTime) / maxTime);
  543. const baseReward = this.saveDataManager.getCurrentLevel() * 50;
  544. return Math.floor(baseReward * timeRatio * 0.5);
  545. }
  546. // === 计算得分 ===
  547. private calculateScore(): number {
  548. const currentLevel = this.saveDataManager?.getCurrentLevel() || 1;
  549. const baseScore = currentLevel * 1000;
  550. const healthScore = this.currentHealth * 10;
  551. const enemyScore = this.totalEnemiesSpawned * 50;
  552. const timeScore = this.calculateTimeBonus();
  553. return baseScore + healthScore + enemyScore + timeScore;
  554. }
  555. // === 计算星级 ===
  556. private calculateStars(): number {
  557. const healthRatio = this.currentHealth / this.initialHealth;
  558. const gameTime = this.getGameDuration();
  559. // 基于血量剩余和用时计算星级
  560. if (healthRatio >= 0.8 && gameTime <= 120) {
  561. return 3; // 3星:血量80%以上,2分钟内完成
  562. } else if (healthRatio >= 0.5 && gameTime <= 300) {
  563. return 2; // 2星:血量50%以上,5分钟内完成
  564. } else if (healthRatio > 0) {
  565. return 1; // 1星:只要完成就有1星
  566. }
  567. return 1;
  568. }
  569. // === 设置UI按钮 ===
  570. private setupUIButtons() {
  571. this.setupSuccessUIButtons();
  572. this.setupDefeatUIButtons();
  573. }
  574. // === 设置成功界面按钮 ===
  575. private setupSuccessUIButtons() {
  576. if (this.gameSuccessUI) {
  577. const nextLevelBtn = this.gameSuccessUI.getChildByName('NextLevelBtn');
  578. const restartBtn = this.gameSuccessUI.getChildByName('RestartBtn');
  579. const mainMenuBtn = this.gameSuccessUI.getChildByName('MainMenuBtn');
  580. const shopBtn = this.gameSuccessUI.getChildByName('ShopBtn');
  581. if (nextLevelBtn) {
  582. const button = nextLevelBtn.getComponent(Button);
  583. if (button) {
  584. button.node.on(Button.EventType.CLICK, this.onRestartClick, this);
  585. }
  586. }
  587. if (restartBtn) {
  588. const button = restartBtn.getComponent(Button);
  589. if (button) {
  590. button.node.on(Button.EventType.CLICK, this.onRestartClick, this);
  591. }
  592. }
  593. if (mainMenuBtn) {
  594. const button = mainMenuBtn.getComponent(Button);
  595. if (button) {
  596. button.node.on(Button.EventType.CLICK, this.onMainMenuClick, this);
  597. }
  598. }
  599. if (shopBtn) {
  600. const button = shopBtn.getComponent(Button);
  601. if (button) {
  602. button.node.on(Button.EventType.CLICK, this.onShopClick, this);
  603. }
  604. }
  605. }
  606. }
  607. // === 设置失败界面按钮 ===
  608. private setupDefeatUIButtons() {
  609. if (this.gameDefeatUI) {
  610. const restartBtn = this.gameDefeatUI.getChildByName('RestartBtn');
  611. const mainMenuBtn = this.gameDefeatUI.getChildByName('MainMenuBtn');
  612. const shopBtn = this.gameDefeatUI.getChildByName('ShopBtn');
  613. const reviveBtn = this.gameDefeatUI.getChildByName('ReviveBtn');
  614. if (restartBtn) {
  615. const button = restartBtn.getComponent(Button);
  616. if (button) {
  617. button.node.on(Button.EventType.CLICK, this.onRestartClick, this);
  618. }
  619. }
  620. if (mainMenuBtn) {
  621. const button = mainMenuBtn.getComponent(Button);
  622. if (button) {
  623. button.node.on(Button.EventType.CLICK, this.onMainMenuClick, this);
  624. }
  625. }
  626. if (shopBtn) {
  627. const button = shopBtn.getComponent(Button);
  628. if (button) {
  629. button.node.on(Button.EventType.CLICK, this.onShopClick, this);
  630. }
  631. }
  632. if (reviveBtn) {
  633. const button = reviveBtn.getComponent(Button);
  634. if (button) {
  635. button.node.on(Button.EventType.CLICK, this.onReviveClick, this);
  636. }
  637. }
  638. }
  639. }
  640. // === 按钮点击事件处理 ===
  641. private onRestartClick() {
  642. this.restartGame();
  643. }
  644. private onMainMenuClick() {
  645. // 隐藏游戏界面,显示主界面
  646. const gameLevelUI = find('Canvas/GameLevelUI');
  647. const mainUI = find('Canvas/MainUI');
  648. if (gameLevelUI) gameLevelUI.active = false;
  649. if (mainUI) mainUI.active = true;
  650. // 更新主界面
  651. const mainUIController = mainUI?.getComponent('MainUIController' as any);
  652. if (mainUIController && typeof (mainUIController as any).updateUI === 'function') {
  653. (mainUIController as any).updateUI();
  654. }
  655. }
  656. private onShopClick() {
  657. // 打开商店界面
  658. const gameLevelUI = find('Canvas/GameLevelUI');
  659. const shopUI = find('Canvas/ShopUI');
  660. if (gameLevelUI) gameLevelUI.active = false;
  661. if (shopUI) shopUI.active = true;
  662. }
  663. private onReviveClick() {
  664. const reviveCost = 10; // 复活消耗的钻石数量
  665. if (this.saveDataManager && this.saveDataManager.spendDiamonds(reviveCost)) {
  666. this.revivePlayer();
  667. }
  668. }
  669. // === 复活玩家 ===
  670. private revivePlayer() {
  671. this.setHealth(50);
  672. this.restartGame();
  673. }
  674. // === 重新开始当前关卡 ===
  675. private restartCurrentLevel() {
  676. this.restartGame();
  677. }
  678. // === 原GameManager方法 ===
  679. // onConfirmButtonClicked方法已移除,逻辑已整合到handleConfirmAction中
  680. private preservePlacedBlocks() {
  681. const blockController = find('Canvas/GameLevelUI/BlockController');
  682. if (blockController) {
  683. const blockManager = blockController.getComponent('BlockManager') as any;
  684. if (blockManager) {
  685. blockManager.onGameStart();
  686. }
  687. }
  688. }
  689. public startGame() {
  690. if (this.gameStarted) return;
  691. this.gameStarted = true;
  692. this.gameStartTime = Date.now();
  693. this.currentState = GameState.PLAYING;
  694. // 开始生成球
  695. this.spawnBall();
  696. // 启动状态检查
  697. this.checkTimer = 0;
  698. // 设置UI按钮事件
  699. this.setupUIButtons();
  700. // 第一波提示UI后再开始生成敌人
  701. if (this.enemyController && this.enemyController.showStartWavePromptUI) {
  702. this.enemyController.showStartWavePromptUI();
  703. } else {
  704. this.forceStartEnemySpawning();
  705. }
  706. LevelSessionManager.inst.initialize(
  707. SaveDataManager.getInstance().getCurrentLevel(),
  708. this.wallHealth
  709. );
  710. }
  711. private spawnBall() {
  712. if (!this.ballController) return;
  713. const ballControllerComponent = this.ballController.getComponent(BallController);
  714. if (ballControllerComponent) {
  715. ballControllerComponent.startBall();
  716. }
  717. }
  718. public gameOver() {
  719. this.triggerGameDefeat();
  720. }
  721. // === 公共方法 ===
  722. public setHealth(health: number) {
  723. this.currentHealth = Math.max(0, health);
  724. }
  725. public takeDamage(damage: number) {
  726. this.currentHealth = Math.max(0, this.currentHealth - damage);
  727. if (this.currentHealth <= 0) {
  728. this.triggerGameDefeat();
  729. }
  730. }
  731. public getCurrentState(): GameState {
  732. return this.currentState;
  733. }
  734. public restartGame() {
  735. this.currentState = GameState.PLAYING;
  736. this.gameStarted = false;
  737. this.gameStartTime = 0;
  738. this.gameEndTime = 0;
  739. this.currentHealth = this.initialHealth;
  740. this.totalEnemiesSpawned = 0;
  741. this.enemiesKilled = 0;
  742. this.currentWave = 1;
  743. this.currentWaveEnemyCount = 0;
  744. // 重置GamePause状态
  745. const gamePause = GamePause.getInstance();
  746. gamePause.resetGameState();
  747. // 重置能量条
  748. this.energyPoints = 0;
  749. if (this.energyBar) {
  750. this.energyBar.progress = 0;
  751. }
  752. if (this.selectSkillUI) {
  753. this.selectSkillUI.active = false;
  754. }
  755. // 关闭胜利/失败界面,确保重新进入时是正常状态
  756. if (this.gameSuccessUI) {
  757. this.gameSuccessUI.active = false;
  758. }
  759. if (this.gameDefeatUI) {
  760. this.gameDefeatUI.active = false;
  761. }
  762. // 通知BlockManager游戏重置
  763. const blockMgrNode = find('Canvas/GameLevelUI/BlockController');
  764. const blockManager = blockMgrNode?.getComponent(BlockManager);
  765. if (blockManager) {
  766. blockManager.onGameReset?.();
  767. }
  768. // 清空关卡剩余敌人,不触发敌人死亡事件
  769. if (this.enemyController && this.enemyController.clearAllEnemies) {
  770. this.enemyController.clearAllEnemies(false);
  771. }
  772. // 重置墙体血量显示
  773. this.initWallHealthDisplay();
  774. // 初始化本局数据(金币45等)
  775. LevelSessionManager.inst.clear();
  776. LevelSessionManager.inst.initialize(
  777. this.saveDataManager.getCurrentLevel(),
  778. this.wallHealth
  779. );
  780. // 刷新方块金币显示(如果 BlockManager 已存在)
  781. if (blockManager) {
  782. blockManager.updateCoinDisplay?.();
  783. }
  784. }
  785. public isGameOver(): boolean {
  786. // 统一使用GamePause来判断游戏是否结束
  787. const gamePause = GamePause.getInstance();
  788. return gamePause.isGameOver();
  789. }
  790. public forceGameSuccess() {
  791. this.triggerGameSuccess();
  792. }
  793. public forceGameDefeat() {
  794. this.triggerGameDefeat();
  795. }
  796. // === 获取EnemyController组件 ===
  797. public getEnemyController() {
  798. return this.enemyController;
  799. }
  800. // === 调试方法 ===
  801. public getEnemyStatus() {
  802. if (!this.enemyController) return;
  803. const currentCount = this.enemyController.getCurrentEnemyCount();
  804. const gameStarted = this.enemyController.isGameStarted();
  805. const activeEnemies = this.enemyController.getActiveEnemies?.() || [];
  806. if (activeEnemies.length > 0) {
  807. for (let index = 0; index < activeEnemies.length; index++) {
  808. const enemy = activeEnemies[index];
  809. if (enemy?.isValid) {
  810. // 查看敌人状态
  811. } else {
  812. // 无效敌人节点
  813. }
  814. }
  815. }
  816. }
  817. public forceStartEnemySpawning() {
  818. if (this.enemyController) {
  819. this.enemyController.startGame();
  820. }
  821. }
  822. public setTotalEnemiesSpawned(count: number) {
  823. this.totalEnemiesSpawned = count;
  824. }
  825. public testEnemyDetection() {
  826. // 测试敌人检测功能
  827. this.getEnemyStatus();
  828. }
  829. public testComponentAccess() {
  830. // 测试组件访问
  831. if (this.enemyController) {
  832. // 组件访问正常
  833. }
  834. }
  835. public testEnemyAttackWall() {
  836. if (!this.enemyController) return;
  837. const currentHealth = this.enemyController.getCurrentWallHealth();
  838. const testDamage = 50;
  839. this.enemyController.damageWall(testDamage);
  840. const newHealth = this.enemyController.getCurrentWallHealth();
  841. }
  842. onDestroy() {
  843. // 清理GamePause事件监听
  844. const eventBus = EventBus.getInstance();
  845. eventBus.off(GameEvents.GAME_SUCCESS, this.onGameSuccessEvent, this);
  846. eventBus.off(GameEvents.GAME_DEFEAT, this.onGameDefeatEvent, this);
  847. eventBus.off('ENEMY_KILLED', this.onEnemyKilledEvent, this);
  848. // 清理按钮事件监听
  849. if (this.gameSuccessUI) {
  850. const buttons = this.gameSuccessUI.getComponentsInChildren(Button);
  851. buttons.forEach(button => {
  852. button.node.off(Button.EventType.CLICK);
  853. });
  854. }
  855. if (this.gameDefeatUI) {
  856. const buttons = this.gameDefeatUI.getComponentsInChildren(Button);
  857. buttons.forEach(button => {
  858. button.node.off(Button.EventType.CLICK);
  859. });
  860. }
  861. }
  862. // === 加载当前关卡配置 ===
  863. public async loadCurrentLevelConfig() {
  864. if (!this.saveDataManager || !this.levelConfigManager) return;
  865. const currentLevel = this.saveDataManager.getCurrentLevel();
  866. try {
  867. const levelConfig = await this.levelConfigManager.getLevelConfig(currentLevel);
  868. if (levelConfig) {
  869. this.applyLevelConfig(levelConfig);
  870. } else {
  871. console.warn(`关卡 ${currentLevel} 配置加载失败`);
  872. }
  873. } catch (error) {
  874. console.error(`关卡 ${currentLevel} 配置加载错误:`, error);
  875. }
  876. }
  877. private applyLevelConfig(levelConfig: any) {
  878. // 应用关卡配置
  879. // 如果有武器配置,应用武器
  880. if (levelConfig.weapons && Array.isArray(levelConfig.weapons)) {
  881. // 应用武器配置
  882. }
  883. // 如果有波次配置,设置敌人波次
  884. if (levelConfig.waves && Array.isArray(levelConfig.waves)) {
  885. this.levelWaves = levelConfig.waves;
  886. this.currentWave = 1;
  887. // 计算本关卡总敌人数
  888. this.levelTotalEnemies = 0;
  889. for (const wave of this.levelWaves) {
  890. for (const enemy of wave.enemies || []) {
  891. this.levelTotalEnemies += enemy.count || 0;
  892. }
  893. }
  894. // 通知 EnemyController 初始化第一波数据及 UI
  895. if (this.enemyController) {
  896. const totalWaves = this.levelWaves.length;
  897. const firstWaveEnemies = this.levelWaves.length > 0 && this.levelWaves[0].enemies ?
  898. this.levelWaves[0].enemies.reduce((t: number, g: any) => t + (g.count || 0), 0) : 0;
  899. this.enemyController.startWave(1, totalWaves, firstWaveEnemies);
  900. // 同步 GameManager 当前波敌人数,避免剩余敌人数计算出错
  901. this.setCurrentWave(1, firstWaveEnemies);
  902. }
  903. }
  904. }
  905. // === 获取当前关卡信息 ===
  906. public async getCurrentLevelInfo() {
  907. const currentLevel = this.saveDataManager ?
  908. this.saveDataManager.getCurrentLevel() :
  909. (this.levelManager ? this.levelManager.getCurrentLevel() : 1);
  910. const levelProgress = this.saveDataManager ?
  911. this.saveDataManager.getLevelProgress(currentLevel) : null;
  912. const levelData = this.levelManager ?
  913. this.levelManager.getLevelData(currentLevel) : null;
  914. const levelConfig = await this.loadCurrentLevelConfig();
  915. return {
  916. level: currentLevel,
  917. maxUnlockedLevel: this.saveDataManager ?
  918. this.saveDataManager.getMaxUnlockedLevel() :
  919. (this.levelManager ? this.levelManager.getMaxUnlockedLevel() : 1),
  920. progress: levelProgress,
  921. data: levelData,
  922. config: levelConfig,
  923. playerData: this.saveDataManager ? {
  924. coins: this.saveDataManager.getCoins(),
  925. diamonds: this.saveDataManager.getDiamonds(),
  926. gems: this.saveDataManager.getGems(),
  927. playerLevel: this.saveDataManager.getPlayerLevel()
  928. } : null
  929. };
  930. }
  931. // === 更新波次显示 ===
  932. private updateWaveDisplay() {
  933. // UI 更新交由 EnemyController 处理
  934. }
  935. // === 更新敌人数量显示 ===
  936. private updateEnemyCountDisplay() {
  937. // UI 更新交由 EnemyController 处理
  938. }
  939. // === 设置当前波次 ===
  940. public setCurrentWave(wave: number, enemyCount: number = 0) {
  941. this.currentWave = wave;
  942. this.currentWaveEnemyCount = 0; // 重置当前击杀数
  943. this.currentWaveTotalEnemies = enemyCount; // 设置该波次总敌人数
  944. if (this.enemyController) {
  945. const totalWaves = this.levelWaves?.length || 1;
  946. this.enemyController.startWave(wave, totalWaves, enemyCount);
  947. }
  948. }
  949. // === 更新当前波次敌人数量 ===
  950. public updateCurrentWaveEnemyCount(count: number) {
  951. this.currentWaveEnemyCount = count;
  952. }
  953. // === 获取当前波次 ===
  954. public getCurrentWave(): number {
  955. return this.currentWave;
  956. }
  957. // === 获取当前波次敌人数量 ===
  958. public getCurrentWaveEnemyCount(): number {
  959. return this.currentWaveEnemyCount;
  960. }
  961. // === 获取当前波次总敌人数量 ===
  962. public getCurrentWaveTotalEnemies(): number {
  963. return this.currentWaveTotalEnemies;
  964. }
  965. // === 进入下一波 ===
  966. public nextWave() {
  967. this.currentWave++;
  968. // 根据关卡配置获取下一波敌人数
  969. let enemyTotal = 0;
  970. if (this.levelWaves && this.levelWaves.length >= this.currentWave) {
  971. const waveCfg = this.levelWaves[this.currentWave - 1];
  972. if (waveCfg && waveCfg.enemies) {
  973. enemyTotal = waveCfg.enemies.reduce((t: number, g: any) => t + (g.count || 0), 0);
  974. }
  975. }
  976. this.setCurrentWave(this.currentWave, enemyTotal);
  977. }
  978. /** 显示下一波提示并在短暂延迟后开始下一波 */
  979. private showNextWavePrompt() {
  980. this.showBlockSelectionForNextWave(); // 使用新的方法
  981. }
  982. // 显示方块选择UI用于下一波
  983. private showBlockSelectionForNextWave() {
  984. // 如果游戏已经结束,不显示方块选择UI
  985. if (this.isGameOver()) {
  986. const gamePause = GamePause.getInstance();
  987. console.warn('[GameManager] 游戏已经结束(胜利或失败),不显示下一波方块选择UI!当前状态:', gamePause.getCurrentState());
  988. return;
  989. }
  990. if (this.blockSelectionComponent) {
  991. this.blockSelectionComponent.showBlockSelection(true);
  992. this.preparingNextWave = true;
  993. // 状态已在onEnemyKilledEvent中设置,这里不需要重复设置
  994. // this.currentState = GameState.BLOCK_SELECTION;
  995. // 使用GamePause来暂停游戏
  996. const gamePause = GamePause.getInstance();
  997. if (gamePause) {
  998. gamePause.pauseGame();
  999. }
  1000. }
  1001. }
  1002. /** 敌人被消灭时由 EnemyController 调用 */
  1003. public onEnemyKilled() {
  1004. // 直接调用GamePause来处理
  1005. const gamePause = GamePause.getInstance();
  1006. gamePause.onEnemyKilled();
  1007. }
  1008. /** 每击杀敌人调用,能量 +1,并更新进度条。满值时弹出技能选择界面 */
  1009. private incrementEnergy() {
  1010. this.energyPoints = Math.min(this.energyPoints + 1, this.ENERGY_MAX);
  1011. this.updateEnergyBar();
  1012. if (this.energyPoints >= this.ENERGY_MAX) {
  1013. this.onEnergyFull();
  1014. }
  1015. }
  1016. /** 更新能量条显示 */
  1017. private updateEnergyBar() {
  1018. if (this.energyBar) {
  1019. this.energyBar.progress = this.energyPoints / this.ENERGY_MAX;
  1020. }
  1021. }
  1022. /** 能量满时触发 */
  1023. private onEnergyFull() {
  1024. console.log(`[GameManager] onEnergyFull 被调用,当前状态: ${this.currentState}, pendingSkillSelection: ${this.pendingSkillSelection}`);
  1025. if (this.currentState === GameState.BLOCK_SELECTION) {
  1026. this.pendingSkillSelection = true;
  1027. console.log('[GameManager] 当前处于方块选择状态,暂存技能选择请求');
  1028. return;
  1029. }
  1030. if (this.selectSkillUI && !this.selectSkillUI.active) {
  1031. console.log('[GameManager] 直接弹出技能选择UI');
  1032. // 暂停游戏后再弹出 UI
  1033. this.pauseGame();
  1034. this.selectSkillUI.active = true;
  1035. }
  1036. }
  1037. /** 供外部调用:重置能量值并刷新显示 */
  1038. public resetEnergy() {
  1039. this.energyPoints = 0;
  1040. this.updateEnergyBar();
  1041. }
  1042. /* ========= 墙体血量 / 等级相关 ========= */
  1043. private wallHpMap: Record<number, number> = {
  1044. 1: 100,
  1045. 2: 1000,
  1046. 3: 1200,
  1047. 4: 1500,
  1048. 5: 2000
  1049. };
  1050. /** 根据等级获取墙体血量 */
  1051. public getWallHealthByLevel(level: number): number {
  1052. return this.wallHpMap[level] || (100 + (level - 1) * 200);
  1053. }
  1054. /** 获取当前墙壁等级 */
  1055. public getCurrentWallLevel(): number {
  1056. return this.saveDataManager?.getPlayerData().playerLevel || 1;
  1057. }
  1058. /** 获取当前墙体血量 */
  1059. public getCurrentWallHealth(): number {
  1060. return this.saveDataManager?.getPlayerData().wallBaseHealth || this.getWallHealthByLevel(1);
  1061. }
  1062. /** 升级墙体等级,返回升级后信息,失败返回null */
  1063. public upgradeWallLevel(): { currentLevel: number; currentHp: number; nextLevel: number; nextHp: number } | null {
  1064. if (!this.saveDataManager) return null;
  1065. const pd = this.saveDataManager.getPlayerData();
  1066. const curLvl = pd.playerLevel || 1;
  1067. if (curLvl >= 5) return null; // 已达最高级
  1068. const newLvl = curLvl + 1;
  1069. const newHp = this.getWallHealthByLevel(newLvl);
  1070. pd.playerLevel = newLvl;
  1071. pd.wallBaseHealth = newHp;
  1072. this.saveDataManager.savePlayerData(true);
  1073. // 更新内存中的数值
  1074. this.wallHealth = newHp;
  1075. if (this.enemyController) {
  1076. this.enemyController.wallHealth = newHp;
  1077. this.enemyController.updateWallHealthDisplay?.();
  1078. }
  1079. return {
  1080. currentLevel: newLvl,
  1081. currentHp: newHp,
  1082. nextLevel: newLvl + 1,
  1083. nextHp: this.getWallHealthByLevel(newLvl + 1)
  1084. };
  1085. }
  1086. // 初始化GameBlockSelection组件
  1087. private initGameBlockSelection() {
  1088. if (this.gameBlockSelection) {
  1089. this.blockSelectionComponent = this.gameBlockSelection.getComponent(GameBlockSelection);
  1090. if (this.blockSelectionComponent) {
  1091. // 设置确认回调
  1092. this.blockSelectionComponent.setConfirmCallback(() => {
  1093. this.handleConfirmAction();
  1094. });
  1095. }
  1096. }
  1097. }
  1098. // 处理确认操作(替代原来的onConfirmButtonClicked)
  1099. private handleConfirmAction() {
  1100. if (this.preparingNextWave) {
  1101. this.preparingNextWave = false;
  1102. this.currentState = GameState.PLAYING;
  1103. this.enemyController.showStartWavePromptUI(); // 弹 startWaveUI 后自动 startGame()
  1104. this.nextWave(); // 更新 wave 计数 & total
  1105. // 方块选择结束后,如果有待弹出技能选择,则弹出
  1106. if (this.pendingSkillSelection) {
  1107. this.pendingSkillSelection = false;
  1108. console.log('[GameManager] 检测到待弹出技能选择,现在弹出技能选择UI');
  1109. if (this.selectSkillUI && !this.selectSkillUI.active) {
  1110. this.pauseGame();
  1111. this.selectSkillUI.active = true;
  1112. }
  1113. }
  1114. return;
  1115. }
  1116. // ---------- 首波逻辑(原有代码) ----------
  1117. this.startGame();
  1118. }
  1119. }