GameFlowManager.ts 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652
  1. import { _decorator, Component, Node, resources, sp, Asset, Button } from 'cc';
  2. import { DataManager } from './DataManager';
  3. import { CharacterManager } from './CharacterManager';
  4. import { QuestionAnswerManager } from './QuestionAnswerManager';
  5. import { AvatarManager } from './AvatarManager';
  6. import { PassCardManager } from './PassCardManager';
  7. import { PhoneManager } from './PhoneManager';
  8. import { RosterManager } from './RosterManager';
  9. import { PersonalInfoManager } from './PersonalInfoManager';
  10. import { LieDetectorManager } from './LieDetectorManager';
  11. import { GameLifeManager } from './GameLifeManager';
  12. import { SummaryManager } from './SummaryManager';
  13. const { ccclass, property } = _decorator;
  14. @ccclass('GameFlowManager')
  15. export class GameFlowManager extends Component {
  16. @property({
  17. type: DataManager,
  18. tooltip: '数据管理器引用'
  19. })
  20. dataManager: DataManager = null;
  21. @property({
  22. type: CharacterManager,
  23. tooltip: '角色管理器引用'
  24. })
  25. characterManager: CharacterManager = null;
  26. @property({
  27. type: QuestionAnswerManager,
  28. tooltip: '问答管理器引用'
  29. })
  30. questionAnswerManager: QuestionAnswerManager = null;
  31. @property({
  32. type: Button,
  33. tooltip: '放行按钮'
  34. })
  35. allowButton: Button = null;
  36. @property({
  37. type: Button,
  38. tooltip: '赶走按钮'
  39. })
  40. dismissButton: Button = null;
  41. @property({
  42. type: AvatarManager,
  43. tooltip: '头像管理器引用'
  44. })
  45. avatarManager: AvatarManager = null;
  46. @property({
  47. type: PassCardManager,
  48. tooltip: '通行证管理器引用'
  49. })
  50. passCardManager: PassCardManager = null;
  51. @property({
  52. type: PhoneManager,
  53. tooltip: '电话管理器引用'
  54. })
  55. phoneManager: PhoneManager = null;
  56. @property({
  57. type: RosterManager,
  58. tooltip: '名单管理器引用'
  59. })
  60. rosterManager: RosterManager = null;
  61. @property({
  62. type: PersonalInfoManager,
  63. tooltip: '个人资料管理器引用'
  64. })
  65. personalInfoManager: PersonalInfoManager = null;
  66. @property({
  67. type: LieDetectorManager,
  68. tooltip: '测谎仪管理器引用'
  69. })
  70. lieDetectorManager: LieDetectorManager = null;
  71. @property({
  72. type: GameLifeManager,
  73. tooltip: '游戏生命值管理器引用'
  74. })
  75. gameLifeManager: GameLifeManager = null;
  76. @property({
  77. type: SummaryManager,
  78. tooltip: '总结管理器引用'
  79. })
  80. summaryManager: SummaryManager = null;
  81. // 当前关卡中的NPC索引
  82. private currentNpcIndex: number = -1;
  83. // 当前关卡的NPC列表
  84. private currentNpcs: any[] = [];
  85. start() {
  86. // 初始化UI管理器
  87. this.initUIManagers();
  88. // 注册按钮事件
  89. this.registerButtonEvents();
  90. // 设置生命值管理器回调
  91. this.setupLifeManagerCallbacks();
  92. // 开始游戏流程
  93. this.startGameFlow();
  94. }
  95. /**
  96. * 初始化UI管理器
  97. */
  98. private initUIManagers(): void {
  99. // 检查通行证管理器是否被正确引用
  100. if (!this.passCardManager) {
  101. console.error('通行证管理器未设置');
  102. }
  103. // 检查电话管理器是否被正确引用
  104. if (!this.phoneManager) {
  105. console.error('电话管理器未设置');
  106. }
  107. // 检查名单管理器是否被正确引用
  108. if (!this.rosterManager) {
  109. console.error('名单管理器未设置');
  110. }
  111. // 检查个人资料管理器是否被正确引用
  112. if (!this.personalInfoManager) {
  113. console.error('个人资料管理器未设置');
  114. }
  115. // 检查测谎仪管理器是否被正确引用
  116. if (!this.lieDetectorManager) {
  117. console.error('测谎仪管理器未设置');
  118. }
  119. // 检查生命值管理器是否被正确引用
  120. if (!this.gameLifeManager) {
  121. console.error('生命值管理器未设置');
  122. }
  123. // 检查总结管理器是否被正确引用
  124. if (!this.summaryManager) {
  125. console.error('总结管理器未设置');
  126. }
  127. console.log('所有UI管理器已初始化');
  128. if (this.summaryManager) {
  129. this.summaryManager.onShowCharacterDetail = (characterId) => {
  130. const npcData = this.dataManager.getNPCById(characterId);
  131. if (!npcData) return;
  132. console.log(`在SummaryManager中显示角色详情: ID=${characterId}, 名称=${npcData.characterName}`);
  133. // 构建完整的角色数据对象
  134. const characterData = this.buildCharacterInfoData(npcData);
  135. this.personalInfoManager.displayCharacterInfo(characterData);
  136. };
  137. }
  138. }
  139. /**
  140. * 注册按钮事件
  141. */
  142. private registerButtonEvents(): void {
  143. // 注册放行按钮点击事件
  144. if (this.allowButton) {
  145. this.allowButton.node.on(Button.EventType.CLICK, this.handleCharacterPassed, this);
  146. } else {
  147. console.error('放行按钮未设置');
  148. }
  149. // 注册赶走按钮点击事件
  150. if (this.dismissButton) {
  151. this.dismissButton.node.on(Button.EventType.CLICK, this.handleCharacterDismissed, this);
  152. } else {
  153. console.error('赶走按钮未设置');
  154. }
  155. }
  156. /**
  157. * 设置生命值管理器回调
  158. */
  159. private setupLifeManagerCallbacks(): void {
  160. if (this.gameLifeManager) {
  161. this.gameLifeManager.setCallbacks(
  162. // 复活后的回调,继续游戏流程
  163. () => {
  164. console.log('玩家已复活,继续游戏');
  165. // 重新启用按钮
  166. this.enableButtons();
  167. },
  168. // 游戏结束的回调,显示当天总结
  169. () => {
  170. console.log('游戏结束,显示当天总结');
  171. // 禁用按钮
  172. this.disableButtons();
  173. }
  174. );
  175. }
  176. }
  177. /**
  178. * 重新开始游戏流程
  179. * 在切换关卡后调用此方法重新开始游戏
  180. */
  181. public restartGameFlow(): void {
  182. console.log('GameFlowManager.restartGameFlow被调用,重新开始游戏流程');
  183. // 重置NPC索引
  184. this.currentNpcIndex = -1;
  185. // 获取新关卡数据
  186. const currentLevel = this.dataManager.getCurrentLevel();
  187. if (!currentLevel) {
  188. console.error('无法获取当前关卡数据');
  189. return;
  190. }
  191. console.log(`开始关卡: ${currentLevel.name}`);
  192. // 从关卡NPC列表中随机选择NPC
  193. this.setupRandomNpcs(currentLevel);
  194. // 如果有生命值管理器,重置状态和生命值
  195. if (this.gameLifeManager) {
  196. // 重置总结面板显示状态
  197. this.gameLifeManager.resetSummaryPanelState();
  198. // 重置生命值
  199. this.gameLifeManager.resetLife();
  200. }
  201. // 显示第一个NPC
  202. this.showNextNpc();
  203. }
  204. /**
  205. * 从关卡NPC列表中随机选择NPC
  206. * @param levelData 关卡数据
  207. */
  208. private setupRandomNpcs(levelData: any): void {
  209. if (!levelData || !levelData.npcs || !Array.isArray(levelData.npcs)) {
  210. console.error('关卡数据中缺少有效的NPC列表');
  211. this.currentNpcs = [];
  212. return;
  213. }
  214. // 获取原始NPC列表的副本
  215. const allNpcs = [...levelData.npcs];
  216. // 打乱NPC顺序
  217. this.shuffleArray(allNpcs);
  218. // 获取要选择的NPC数量
  219. const randomNpcCount = levelData.randomNpcCount || allNpcs.length;
  220. console.log(`本关卡需要随机选择 ${randomNpcCount} 个角色`);
  221. // 选择指定数量的NPC
  222. this.currentNpcs = allNpcs.slice(0, randomNpcCount);
  223. console.log(`已随机选择 ${this.currentNpcs.length} 个角色`);
  224. }
  225. /**
  226. * 随机打乱数组顺序
  227. * @param array 要打乱的数组
  228. */
  229. private shuffleArray(array: any[]): void {
  230. for (let i = array.length - 1; i > 0; i--) {
  231. // 生成随机索引
  232. const j = Math.floor(Math.random() * (i + 1));
  233. // 交换元素
  234. [array[i], array[j]] = [array[j], array[i]];
  235. }
  236. }
  237. /**
  238. * 开始游戏流程
  239. */
  240. private startGameFlow(): void {
  241. // 获取当前关卡数据
  242. const currentLevel = this.dataManager.getCurrentLevel();
  243. if (!currentLevel) {
  244. console.error('无法获取当前关卡数据');
  245. return;
  246. }
  247. console.log(`开始关卡: ${currentLevel.name}`);
  248. // 从关卡NPC列表中随机选择NPC
  249. this.setupRandomNpcs(currentLevel);
  250. // 显示第一个NPC
  251. this.showNextNpc();
  252. }
  253. /**
  254. * 显示下一个NPC
  255. */
  256. public showNextNpc(): void {
  257. // 如果已经显示了总结面板,不再继续处理NPC
  258. if (this.gameLifeManager && this.gameLifeManager.hasSummaryPanelShown()) {
  259. console.log('总结面板已显示,不再继续处理NPC');
  260. return;
  261. }
  262. this.currentNpcIndex++;
  263. // 检查是否还有NPC
  264. if (this.currentNpcIndex >= this.currentNpcs.length) {
  265. console.log('当前关卡所有NPC已处理完毕');
  266. // 显示总结面板
  267. if (this.summaryManager) {
  268. if (this.gameLifeManager) {
  269. this.gameLifeManager.showSummaryPanel();
  270. } else {
  271. this.summaryManager.showSummaryPanel();
  272. }
  273. }
  274. return;
  275. }
  276. // 获取当前NPC数据
  277. const npc = this.currentNpcs[this.currentNpcIndex];
  278. // 配置问答对
  279. this.setupQuestionAnswers(npc.qaPairs);
  280. // 如果有头像管理器,先加载头像
  281. if (this.avatarManager) {
  282. this.avatarManager.loadAvatar(npc.characterId);
  283. }
  284. // 配置角色外观,并在完成后让角色入场
  285. this.characterManager.setupCharacterAppearance(npc.characterId, npc.skinName, () => {
  286. // 让角色入场(只有在外观设置完成后才执行)
  287. this.characterManager.characterEnter();
  288. // 启用按钮
  289. this.enableButtons();
  290. });
  291. }
  292. /**
  293. * 启用决策按钮
  294. */
  295. private enableButtons(): void {
  296. if (this.allowButton) {
  297. this.allowButton.interactable = true;
  298. }
  299. if (this.dismissButton) {
  300. this.dismissButton.interactable = true;
  301. }
  302. }
  303. /**
  304. * 禁用决策按钮
  305. */
  306. private disableButtons(): void {
  307. if (this.allowButton) {
  308. this.allowButton.interactable = false;
  309. }
  310. if (this.dismissButton) {
  311. this.dismissButton.interactable = false;
  312. }
  313. }
  314. /**
  315. * 设置问答对
  316. * @param qaPairs 问答对数组
  317. */
  318. private setupQuestionAnswers(qaPairs: any[]): void {
  319. if (!this.questionAnswerManager) {
  320. console.error('问答管理器未设置');
  321. return;
  322. }
  323. // 替换所有问答对(而不是添加)
  324. this.questionAnswerManager.replaceAllQuestionAnswers(qaPairs);
  325. }
  326. /**
  327. * 处理放行角色
  328. */
  329. public handleCharacterPassed(): void {
  330. // 禁用按钮,防止连续点击
  331. this.disableButtons();
  332. // 获取当前NPC数据
  333. const npcData = this.getCurrentNpcData();
  334. // 记录放行统计
  335. if (this.summaryManager && npcData) {
  336. this.summaryManager.recordPassedNPC(npcData);
  337. }
  338. // 检查是否放行了假角色,如果是则扣除生命值
  339. if (npcData && npcData.type === 'fake') {
  340. console.log('错误:放行了伪装者');
  341. if (this.gameLifeManager) {
  342. this.gameLifeManager.decreaseLife(1);
  343. // 如果生命值为0,则不继续游戏流程,等待失败面板处理
  344. if (this.gameLifeManager.getCurrentLife() <= 0) {
  345. return;
  346. }
  347. }
  348. }
  349. // 让角色向右移动,动画完成后显示下一个NPC
  350. this.characterManager.moveCharacterRight(() => {
  351. this.showNextNpc();
  352. });
  353. }
  354. /**
  355. * 处理赶走角色
  356. */
  357. public handleCharacterDismissed(): void {
  358. // 禁用按钮,防止连续点击
  359. this.disableButtons();
  360. // 获取当前NPC数据
  361. const npcData = this.getCurrentNpcData();
  362. // 记录驱赶统计
  363. if (this.summaryManager && npcData) {
  364. this.summaryManager.recordDismissedNPC(npcData);
  365. }
  366. // 检查是否赶走了真角色,如果是则扣除生命值
  367. if (npcData && npcData.type === 'real') {
  368. console.log('错误:赶走了真实角色');
  369. if (this.gameLifeManager) {
  370. this.gameLifeManager.decreaseLife(1);
  371. // 如果生命值为0,则不继续游戏流程,等待失败面板处理
  372. if (this.gameLifeManager.getCurrentLife() <= 0) {
  373. return;
  374. }
  375. }
  376. }
  377. // 让角色向左移动,动画完成后显示下一个NPC
  378. this.characterManager.moveCharacterLeft(() => {
  379. this.showNextNpc();
  380. });
  381. }
  382. /**
  383. * 获取当前NPC数据
  384. * @returns 当前NPC数据,如果没有则返回null
  385. */
  386. public getCurrentNpcData(): any {
  387. if (this.currentNpcIndex < 0 || this.currentNpcIndex >= this.currentNpcs.length) {
  388. return null;
  389. }
  390. return this.currentNpcs[this.currentNpcIndex];
  391. }
  392. /**
  393. * 获取通行证管理器
  394. * @returns 通行证管理器实例
  395. */
  396. public getPassCardManager(): PassCardManager {
  397. return this.passCardManager;
  398. }
  399. /**
  400. * 获取电话管理器
  401. * @returns 电话管理器实例
  402. */
  403. public getPhoneManager(): PhoneManager {
  404. return this.phoneManager;
  405. }
  406. /**
  407. * 获取名单管理器
  408. * @returns 名单管理器实例
  409. */
  410. public getRosterManager(): RosterManager {
  411. return this.rosterManager;
  412. }
  413. /**
  414. * 获取个人资料管理器
  415. * @returns 个人资料管理器实例
  416. */
  417. public getPersonalInfoManager(): PersonalInfoManager {
  418. return this.personalInfoManager;
  419. }
  420. /**
  421. * 获取测谎仪管理器
  422. * @returns 测谎仪管理器实例
  423. */
  424. public getLieDetectorManager(): LieDetectorManager {
  425. return this.lieDetectorManager;
  426. }
  427. /**
  428. * 显示通行证
  429. * @param passData 通行证数据
  430. */
  431. public showPassCard(passData: any): void {
  432. if (this.passCardManager) {
  433. this.passCardManager.showPassCard(passData);
  434. } else {
  435. console.error('通行证管理器未设置,无法显示通行证');
  436. }
  437. }
  438. /**
  439. * 显示电话对话框
  440. */
  441. public showPhoneUI(): void {
  442. if (this.phoneManager) {
  443. this.phoneManager.showPhonePanel();
  444. } else {
  445. console.error('电话管理器未设置,无法显示电话UI');
  446. }
  447. }
  448. /**
  449. * 显示名单面板
  450. */
  451. public showRosterPanel(): void {
  452. if (this.rosterManager) {
  453. // 获取今日名单数据并传递给RosterManager
  454. // 确保名单数据符合TodayListItem接口 (包含name和avatarPath属性)
  455. const todayList = this.dataManager.getTodayList();
  456. console.log(`获取到今日名单数据,共${todayList.length}条记录`);
  457. // 设置名单数据并显示
  458. this.rosterManager.setTodayList(todayList);
  459. this.rosterManager.showRosterPanel();
  460. } else {
  461. console.error('名单管理器未设置,无法显示名单面板');
  462. }
  463. }
  464. /**
  465. * 获取当前关卡名单数据并显示
  466. * 此方法已弃用,请直接使用showRosterPanel
  467. * @deprecated
  468. */
  469. public showCurrentLevelRoster(): void {
  470. this.showRosterPanel();
  471. }
  472. /**
  473. * 显示当前NPC的个人资料
  474. */
  475. public showCurrentNpcPersonalInfo(): void {
  476. // 获取当前NPC数据
  477. const npcData = this.getCurrentNpcData();
  478. if (!npcData) {
  479. console.error('无法获取当前NPC数据');
  480. return;
  481. }
  482. console.log(`显示当前NPC资料: ID=${npcData.characterId}, 名称=${npcData.characterName}`);
  483. // 确保PersonalInfoManager已设置
  484. if (this.personalInfoManager) {
  485. // 构建完整的角色数据对象
  486. const characterData = this.buildCharacterInfoData(npcData);
  487. // 显示个人资料
  488. this.personalInfoManager.displayCharacterInfo(characterData);
  489. } else {
  490. console.error('个人资料管理器未设置');
  491. }
  492. }
  493. /**
  494. * 构建完整的角色信息数据对象
  495. * @param npcData 原始NPC数据
  496. * @returns 格式化后的角色信息对象
  497. */
  498. private buildCharacterInfoData(npcData: any): any {
  499. const passInfo = npcData.pass || {};
  500. const personalInfo = npcData.personal || {};
  501. // 构建角色数据对象
  502. return {
  503. characterId: npcData.characterId,
  504. characterName: npcData.characterName,
  505. info: personalInfo.info || "无个人资料",
  506. avatarPath: personalInfo.avatarPath || `avatars/${npcData.characterId}/avatar_${npcData.characterId}_5`,
  507. room: passInfo.room || "",
  508. identityId: passInfo.identityId || personalInfo.id || "",
  509. hasRoommate: personalInfo.hasRoommate || false,
  510. roommateInfo: personalInfo.hasRoommate && personalInfo.roommate ?
  511. (personalInfo.roommate.id || '未知') : null
  512. };
  513. }
  514. /**
  515. * 显示测谎仪面板
  516. */
  517. public showLieDetectorPanel(): void {
  518. // 获取当前NPC数据
  519. const npcData = this.getCurrentNpcData();
  520. if (!npcData) {
  521. console.error('无法获取当前NPC数据');
  522. return;
  523. }
  524. // 设置测谎仪状态并显示
  525. if (this.lieDetectorManager) {
  526. // 从NPC数据中获取是否为伪装者的状态
  527. const isFake = npcData.type === 'fake';
  528. // 设置当前角色状态
  529. this.lieDetectorManager.setCharacterStatus(isFake);
  530. // 显示测谎仪面板
  531. this.lieDetectorManager.showLieDetectorPanel();
  532. } else {
  533. console.error('测谎仪管理器未设置');
  534. }
  535. }
  536. /**
  537. * 组件销毁时清理事件监听
  538. */
  539. onDestroy() {
  540. // 移除按钮事件监听
  541. if (this.allowButton) {
  542. this.allowButton.node.off(Button.EventType.CLICK, this.handleCharacterPassed, this);
  543. }
  544. if (this.dismissButton) {
  545. this.dismissButton.node.off(Button.EventType.CLICK, this.handleCharacterDismissed, this);
  546. }
  547. }
  548. }