test_comprehensive_defeat_scenarios.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. /**
  2. * 综合失败场景测试
  3. * 模拟实际游戏中的复杂情况,包括UI层级、其他组件干扰等
  4. */
  5. // 模拟事件系统
  6. class MockEventBus {
  7. constructor() {
  8. this.listeners = new Map();
  9. this.eventLog = [];
  10. }
  11. on(event, callback, context) {
  12. if (!this.listeners.has(event)) {
  13. this.listeners.set(event, []);
  14. }
  15. this.listeners.get(event).push({ callback, context });
  16. console.log(`[EventBus] 注册事件监听器: ${event}`);
  17. }
  18. emit(event, ...args) {
  19. console.log(`[EventBus] 触发事件: ${event}`);
  20. this.eventLog.push({ event, timestamp: Date.now(), args });
  21. if (this.listeners.has(event)) {
  22. const listeners = this.listeners.get(event);
  23. listeners.forEach(({ callback, context }) => {
  24. try {
  25. callback.call(context, ...args);
  26. } catch (error) {
  27. console.error(`[EventBus] 事件处理错误 ${event}:`, error);
  28. }
  29. });
  30. }
  31. }
  32. off(event, callback, context) {
  33. if (this.listeners.has(event)) {
  34. const listeners = this.listeners.get(event);
  35. const index = listeners.findIndex(l => l.callback === callback && l.context === context);
  36. if (index !== -1) {
  37. listeners.splice(index, 1);
  38. console.log(`[EventBus] 移除事件监听器: ${event}`);
  39. }
  40. }
  41. }
  42. getEventLog() {
  43. return this.eventLog;
  44. }
  45. }
  46. // 模拟GameEvents
  47. const GameEvents = {
  48. GAME_DEFEAT: 'GAME_DEFEAT',
  49. GAME_RESUME: 'GAME_RESUME',
  50. GAME_START: 'GAME_START',
  51. RESET_UI_STATES: 'RESET_UI_STATES',
  52. CURRENCY_CHANGED: 'CURRENCY_CHANGED'
  53. };
  54. // 模拟Wall组件
  55. class MockWall {
  56. constructor(eventBus) {
  57. this.eventBus = eventBus;
  58. this.currentHealth = 100;
  59. this.maxHealth = 100;
  60. }
  61. takeDamage(damage) {
  62. console.log(`[Wall] 受到伤害: ${damage}, 当前血量: ${this.currentHealth}`);
  63. this.currentHealth -= damage;
  64. if (this.currentHealth <= 0) {
  65. console.log('[Wall] 墙体血量为0,调用onWallDestroyed');
  66. this.onWallDestroyed();
  67. }
  68. }
  69. onWallDestroyed() {
  70. console.log('[Wall] 墙体被摧毁,触发GAME_DEFEAT事件');
  71. this.eventBus.emit(GameEvents.GAME_DEFEAT);
  72. }
  73. }
  74. // 模拟MenuController(修复后的版本)
  75. class MockMenuController {
  76. constructor(eventBus) {
  77. this.eventBus = eventBus;
  78. this.isMenuOpen = false;
  79. this.currentAppState = 'IN_GAME';
  80. }
  81. async onBackButtonClick() {
  82. console.log('[MenuController] 返回按钮被点击');
  83. if (this.currentAppState === 'IN_GAME') {
  84. console.log('[MenuController] 游戏中退出,触发GAME_DEFEAT事件显示游戏失败UI');
  85. // 先触发游戏失败事件,让GameEnd面板显示
  86. this.eventBus.emit(GameEvents.GAME_DEFEAT);
  87. // 关闭菜单(不触发GAME_RESUME事件,避免事件冲突)
  88. await this.closeMenuWithoutResume();
  89. console.log('[MenuController] 菜单退出处理完成,已触发GAME_DEFEAT事件');
  90. }
  91. }
  92. async closeMenuWithoutResume() {
  93. if (!this.isMenuOpen) return;
  94. this.isMenuOpen = false;
  95. console.log('[MenuController] 菜单已关闭(未触发游戏恢复)');
  96. }
  97. }
  98. // 模拟GameEnd组件(完整版本)
  99. class MockGameEnd {
  100. constructor(eventBus) {
  101. this.eventBus = eventBus;
  102. this.animationCount = 0;
  103. this.isVisible = false;
  104. this.hasProcessedGameEnd = false;
  105. this.currentRewards = { money: -1, diamonds: -1 };
  106. this.isGameSuccess = false;
  107. this.hasDoubledReward = false;
  108. this.setupEventListeners();
  109. }
  110. setupEventListeners() {
  111. console.log('[GameEnd] 设置事件监听器');
  112. this.eventBus.on(GameEvents.GAME_DEFEAT, this.onGameDefeat, this);
  113. this.eventBus.on(GameEvents.GAME_START, this.onGameStart, this);
  114. this.eventBus.on(GameEvents.RESET_UI_STATES, this.onResetUI, this);
  115. }
  116. onGameDefeat() {
  117. console.log('[GameEnd] 接收到GAME_DEFEAT事件');
  118. console.log('[GameEnd] 游戏失败事件处理,开始统一处理流程');
  119. this.isGameSuccess = false;
  120. this.calculateAndShowRewards();
  121. }
  122. calculateAndShowRewards() {
  123. if (this.hasProcessedGameEnd) {
  124. console.log('[GameEnd] 游戏结束已处理过,跳过重复计算');
  125. return;
  126. }
  127. this.hasProcessedGameEnd = true;
  128. console.log('[GameEnd] 计算和显示奖励');
  129. // 模拟奖励计算
  130. this.currentRewards = { money: 50, diamonds: 2 };
  131. this.showEndPanelWithAnimation();
  132. }
  133. showEndPanelWithAnimation() {
  134. console.log('[GameEnd] 开始显示结算面板动画');
  135. this.animationCount++;
  136. this.isVisible = true;
  137. console.log(`[GameEnd] GameEnd面板弹出动画第${this.animationCount}次执行`);
  138. }
  139. onGameStart() {
  140. console.log('[GameEnd] 收到游戏开始事件,重置奖励显示');
  141. this.currentRewards = { money: 0, diamonds: 0 };
  142. this.hasProcessedGameEnd = false;
  143. this.hasDoubledReward = false;
  144. this.isGameSuccess = false;
  145. this.animationCount = 0;
  146. this.isVisible = false;
  147. }
  148. onResetUI() {
  149. console.log('[GameEnd] 重置UI状态');
  150. // 重置所有状态,为下一局游戏做准备
  151. this.hasDoubledReward = false;
  152. // 注意:不重置currentRewards,保持奖励显示直到下次游戏开始
  153. this.isGameSuccess = false;
  154. this.hasProcessedGameEnd = false;
  155. console.log('[GameEnd] UI状态重置完成');
  156. }
  157. getStatus() {
  158. return {
  159. animationCount: this.animationCount,
  160. isVisible: this.isVisible,
  161. hasProcessedGameEnd: this.hasProcessedGameEnd,
  162. currentRewards: { ...this.currentRewards },
  163. isGameSuccess: this.isGameSuccess
  164. };
  165. }
  166. }
  167. // 模拟UIStateManager
  168. class MockUIStateManager {
  169. constructor(eventBus) {
  170. this.eventBus = eventBus;
  171. this.setupEventListeners();
  172. }
  173. setupEventListeners() {
  174. this.eventBus.on(GameEvents.RESET_UI_STATES, this.onResetUI, this);
  175. }
  176. onResetUI() {
  177. console.log('[UIStateManager] 接收到RESET_UI_STATES事件');
  178. // 可能会影响其他UI组件
  179. }
  180. }
  181. // 模拟GameManager
  182. class MockGameManager {
  183. constructor(eventBus) {
  184. this.eventBus = eventBus;
  185. this.gameState = 'PLAYING';
  186. this.setupEventListeners();
  187. }
  188. setupEventListeners() {
  189. this.eventBus.on(GameEvents.GAME_DEFEAT, this.onGameDefeat, this);
  190. }
  191. onGameDefeat() {
  192. console.log('[GameManager] 接收到GAME_DEFEAT事件');
  193. this.gameState = 'DEFEAT';
  194. console.log('[GameManager] 游戏状态切换为DEFEAT');
  195. }
  196. onMainMenuClick() {
  197. console.log('[GameManager] 主菜单按钮被点击');
  198. // 可能触发RESET_UI_STATES事件
  199. this.eventBus.emit(GameEvents.RESET_UI_STATES);
  200. }
  201. }
  202. // 模拟MainUIController
  203. class MockMainUIController {
  204. constructor(eventBus) {
  205. this.eventBus = eventBus;
  206. this.setupEventListeners();
  207. }
  208. setupEventListeners() {
  209. this.eventBus.on(GameEvents.CURRENCY_CHANGED, this.onCurrencyChanged, this);
  210. }
  211. onCurrencyChanged() {
  212. console.log('[MainUIController] 货币变化事件处理');
  213. }
  214. onReturnToMainUI() {
  215. console.log('[MainUIController] 返回主界面');
  216. this.eventBus.emit(GameEvents.CURRENCY_CHANGED);
  217. }
  218. }
  219. // 测试函数1:墙体血量为0场景
  220. function testWallDefeatScenario() {
  221. console.log('\n=== 测试1:墙体血量为0失败场景 ===');
  222. const eventBus = new MockEventBus();
  223. const wall = new MockWall(eventBus);
  224. const gameEnd = new MockGameEnd(eventBus);
  225. const gameManager = new MockGameManager(eventBus);
  226. const uiStateManager = new MockUIStateManager(eventBus);
  227. const mainUIController = new MockMainUIController(eventBus);
  228. console.log('\n--- 初始状态 ---');
  229. console.log('墙体血量:', wall.currentHealth);
  230. console.log('GameEnd状态:', gameEnd.getStatus());
  231. console.log('\n--- 模拟墙体受到致命伤害 ---');
  232. wall.takeDamage(100);
  233. console.log('\n--- 检查结果 ---');
  234. const status = gameEnd.getStatus();
  235. console.log('GameEnd状态:', status);
  236. return {
  237. scenario: 'wall_defeat',
  238. success: status.animationCount === 1 && status.isVisible,
  239. status: status,
  240. eventLog: eventBus.getEventLog()
  241. };
  242. }
  243. // 测试函数2:菜单退出场景
  244. function testMenuDefeatScenario() {
  245. console.log('\n=== 测试2:菜单退出失败场景 ===');
  246. const eventBus = new MockEventBus();
  247. const menuController = new MockMenuController(eventBus);
  248. const gameEnd = new MockGameEnd(eventBus);
  249. const gameManager = new MockGameManager(eventBus);
  250. const uiStateManager = new MockUIStateManager(eventBus);
  251. const mainUIController = new MockMainUIController(eventBus);
  252. console.log('\n--- 初始状态 ---');
  253. console.log('GameEnd状态:', gameEnd.getStatus());
  254. console.log('\n--- 模拟菜单退出 ---');
  255. menuController.onBackButtonClick();
  256. console.log('\n--- 检查结果 ---');
  257. const status = gameEnd.getStatus();
  258. console.log('GameEnd状态:', status);
  259. return {
  260. scenario: 'menu_defeat',
  261. success: status.animationCount === 1 && status.isVisible,
  262. status: status,
  263. eventLog: eventBus.getEventLog()
  264. };
  265. }
  266. // 测试函数3:UI重置干扰场景
  267. function testUIResetInterferenceScenario() {
  268. console.log('\n=== 测试3:UI重置干扰场景 ===');
  269. const eventBus = new MockEventBus();
  270. const wall = new MockWall(eventBus);
  271. const gameEnd = new MockGameEnd(eventBus);
  272. const gameManager = new MockGameManager(eventBus);
  273. const uiStateManager = new MockUIStateManager(eventBus);
  274. const mainUIController = new MockMainUIController(eventBus);
  275. console.log('\n--- 初始状态 ---');
  276. console.log('GameEnd状态:', gameEnd.getStatus());
  277. console.log('\n--- 模拟墙体血量为0 ---');
  278. wall.takeDamage(100);
  279. console.log('\n--- 模拟UI重置事件干扰 ---');
  280. gameManager.onMainMenuClick(); // 触发RESET_UI_STATES
  281. console.log('\n--- 检查结果 ---');
  282. const status = gameEnd.getStatus();
  283. console.log('GameEnd状态:', status);
  284. return {
  285. scenario: 'ui_reset_interference',
  286. success: status.animationCount === 1 && status.isVisible && status.currentRewards.money > 0,
  287. status: status,
  288. eventLog: eventBus.getEventLog()
  289. };
  290. }
  291. // 运行所有测试
  292. function runAllTests() {
  293. console.log('\n🧪 开始综合失败场景测试');
  294. const results = [];
  295. // 测试1:墙体血量为0
  296. results.push(testWallDefeatScenario());
  297. // 测试2:菜单退出
  298. results.push(testMenuDefeatScenario());
  299. // 测试3:UI重置干扰
  300. results.push(testUIResetInterferenceScenario());
  301. console.log('\n=== 综合测试结果分析 ===');
  302. results.forEach((result, index) => {
  303. console.log(`\n测试${index + 1} (${result.scenario}):`);
  304. console.log(' 测试通过:', result.success ? '✅' : '❌');
  305. console.log(' 动画执行次数:', result.status.animationCount);
  306. console.log(' 面板可见性:', result.status.isVisible);
  307. console.log(' 奖励数据:', result.status.currentRewards);
  308. console.log(' 处理状态:', result.status.hasProcessedGameEnd);
  309. const defeatEvents = result.eventLog.filter(log => log.event === GameEvents.GAME_DEFEAT);
  310. const resumeEvents = result.eventLog.filter(log => log.event === GameEvents.GAME_RESUME);
  311. const resetEvents = result.eventLog.filter(log => log.event === GameEvents.RESET_UI_STATES);
  312. console.log(' GAME_DEFEAT事件:', defeatEvents.length);
  313. console.log(' GAME_RESUME事件:', resumeEvents.length);
  314. console.log(' RESET_UI_STATES事件:', resetEvents.length);
  315. });
  316. const allPassed = results.every(r => r.success);
  317. console.log('\n=== 最终结果 ===');
  318. if (allPassed) {
  319. console.log('🎉 所有测试通过!失败处理流程工作正常。');
  320. } else {
  321. console.log('⚠️ 部分测试失败,需要进一步调试。');
  322. const failedTests = results.filter(r => !r.success);
  323. console.log('失败的测试场景:');
  324. failedTests.forEach(test => {
  325. console.log(` - ${test.scenario}`);
  326. });
  327. }
  328. return {
  329. allPassed,
  330. results,
  331. summary: {
  332. total: results.length,
  333. passed: results.filter(r => r.success).length,
  334. failed: results.filter(r => !r.success).length
  335. }
  336. };
  337. }
  338. // 运行测试
  339. const testResults = runAllTests();
  340. console.log('\n📊 测试统计:');
  341. console.log('总测试数:', testResults.summary.total);
  342. console.log('通过数:', testResults.summary.passed);
  343. console.log('失败数:', testResults.summary.failed);
  344. console.log('通过率:', Math.round(testResults.summary.passed / testResults.summary.total * 100) + '%');
  345. if (testResults.allPassed) {
  346. console.log('\n✨ 综合测试结论:失败处理流程已完全修复,两种失败场景都能正常工作!');
  347. } else {
  348. console.log('\n🔍 需要进一步调试的问题:');
  349. testResults.results.forEach(result => {
  350. if (!result.success) {
  351. console.log(` - ${result.scenario}: 动画${result.status.animationCount}次,可见${result.status.isVisible}`);
  352. }
  353. });
  354. }