Wall.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. import { _decorator, Component, Node, Label, find, JsonAsset } from 'cc';
  2. import { SaveDataManager } from '../LevelSystem/SaveDataManager';
  3. import EventBus, { GameEvents } from '../Core/EventBus';
  4. import { JsonConfigLoader } from '../Core/JsonConfigLoader';
  5. import { SkillManager } from './SkillSelection/SkillManager';
  6. const { ccclass, property } = _decorator;
  7. /**
  8. * 墙体组件
  9. * 负责管理墙体的血量、伤害处理、等级升级等功能
  10. */
  11. @ccclass('Wall')
  12. export class Wall extends Component {
  13. @property({
  14. type: Node,
  15. tooltip: '血量显示节点 (HeartLabel)'
  16. })
  17. public heartLabelNode: Node = null;
  18. // @property({
  19. // type: JsonAsset,
  20. // tooltip: '墙体配置文件'
  21. // })
  22. // public wallConfigAsset: JsonAsset = null; // 已改为动态加载
  23. // === 私有属性 ===
  24. private currentHealth: number = 100;
  25. private heartLabel: Label = null;
  26. private saveDataManager: SaveDataManager = null;
  27. // 墙体配置数据
  28. private wallConfig: any = null;
  29. private wallHpMap: Record<number, number> = {};
  30. async start() {
  31. await this.initializeWall();
  32. }
  33. /**
  34. * 加载墙体配置
  35. */
  36. private async loadWallConfig(): Promise<void> {
  37. try {
  38. this.wallConfig = await JsonConfigLoader.getInstance().loadConfig('wall');
  39. if (this.wallConfig && this.wallConfig.wallConfig && this.wallConfig.wallConfig.healthByLevel) {
  40. // 转换字符串键为数字键
  41. const healthByLevel = this.wallConfig.wallConfig.healthByLevel;
  42. this.wallHpMap = {};
  43. for (const level in healthByLevel) {
  44. this.wallHpMap[parseInt(level)] = healthByLevel[level];
  45. }
  46. console.log('[Wall] 墙体配置加载成功:', this.wallHpMap);
  47. } else {
  48. console.warn('[Wall] 配置文件格式错误,使用默认配置');
  49. this.useDefaultConfig();
  50. }
  51. } catch (error) {
  52. console.error('[Wall] 加载wall.json时出错:', error);
  53. this.useDefaultConfig();
  54. }
  55. }
  56. /**
  57. * 使用默认配置
  58. */
  59. private useDefaultConfig(): void {
  60. this.wallHpMap = {
  61. 1: 100,
  62. 2: 500,
  63. 3: 1200,
  64. 4: 1500,
  65. 5: 2000
  66. };
  67. }
  68. /**
  69. * 初始化墙体
  70. */
  71. private async initializeWall() {
  72. // 初始化存档管理器
  73. this.saveDataManager = SaveDataManager.getInstance();
  74. if (!this.saveDataManager) {
  75. console.error('[Wall] SaveDataManager not found');
  76. return;
  77. }
  78. // 加载墙体配置
  79. await this.loadWallConfig();
  80. // 查找血量显示节点
  81. this.findHeartLabelNode();
  82. // 从存档读取墙体血量
  83. this.loadWallHealthFromSave();
  84. // 初始化血量显示
  85. this.updateHealthDisplay();
  86. // 监听治疗技能变化
  87. this.setupSkillListeners();
  88. // 设置事件监听器
  89. this.setupEventListeners();
  90. }
  91. /**
  92. * 查找血量显示节点
  93. */
  94. private findHeartLabelNode() {
  95. // 查找心血显示节点
  96. if (!this.heartLabelNode) {
  97. this.heartLabelNode = find('Canvas-001/TopArea/HeartNode/HeartLabel') || find('Canvas/GameLevelUI/HeartNode/HeartLabel');
  98. }
  99. if (this.heartLabelNode) {
  100. this.heartLabel = this.heartLabelNode.getComponent(Label);
  101. }
  102. }
  103. /**
  104. * 从存档加载墙体血量
  105. */
  106. private loadWallHealthFromSave() {
  107. // 直接从SaveDataManager获取当前墙体血量
  108. this.currentHealth = this.saveDataManager.getWallHealth();
  109. console.log('[Wall] 从配置加载墙体血量:', this.currentHealth);
  110. // 确保当前血量不超过最大血量(考虑技能加成)
  111. const maxHealth = this.getMaxHealth();
  112. if (this.currentHealth > maxHealth) {
  113. this.currentHealth = maxHealth;
  114. }
  115. }
  116. /**
  117. * 墙体受到伤害
  118. */
  119. public takeDamage(damage: number) {
  120. if (damage <= 0) return;
  121. const previousHealth = this.currentHealth;
  122. this.currentHealth = Math.max(0, this.currentHealth - damage);
  123. // 触发受到伤害事件
  124. const eventBus = EventBus.getInstance();
  125. eventBus.emit(GameEvents.WALL_TAKE_DAMAGE, {
  126. damage: damage,
  127. previousHealth: previousHealth,
  128. currentHealth: this.currentHealth
  129. });
  130. // 触发血量变化事件
  131. eventBus.emit(GameEvents.WALL_HEALTH_CHANGED, {
  132. previousHealth: previousHealth,
  133. currentHealth: this.currentHealth,
  134. maxHealth: this.getMaxHealth()
  135. });
  136. // 更新血量显示
  137. this.updateHealthDisplay();
  138. console.log(`[Wall] 墙体受到伤害: ${damage}, 当前血量: ${this.currentHealth}`);
  139. // 检查墙体是否被摧毁
  140. if (this.currentHealth <= 0) {
  141. this.onWallDestroyed();
  142. }
  143. }
  144. /**
  145. * 墙体被摧毁时的处理
  146. * 统一与菜单退出的失败处理流程,直接触发GAME_DEFEAT事件
  147. */
  148. private onWallDestroyed() {
  149. console.log('[Wall] 墙体被摧毁,触发游戏失败');
  150. // 通过事件系统触发墙体被摧毁事件(保留用于其他监听器)
  151. const eventBus = EventBus.getInstance();
  152. eventBus.emit(GameEvents.WALL_DESTROYED, {
  153. finalHealth: this.currentHealth,
  154. maxHealth: this.getMaxHealth()
  155. });
  156. // 统一失败处理:直接触发GAME_DEFEAT事件,与菜单退出处理保持一致
  157. console.log('[Wall] 直接触发GAME_DEFEAT事件,与菜单退出失败处理流程一致');
  158. eventBus.emit(GameEvents.GAME_DEFEAT);
  159. }
  160. /**
  161. * 更新血量显示
  162. */
  163. public updateHealthDisplay() {
  164. if (this.heartLabel) {
  165. this.heartLabel.string = Math.floor(this.currentHealth).toString();
  166. }
  167. }
  168. /**
  169. * 设置墙体血量
  170. */
  171. public setHealth(health: number) {
  172. const previousHealth = this.currentHealth;
  173. this.currentHealth = Math.max(0, health);
  174. // 如果血量发生变化,触发血量变化事件
  175. if (previousHealth !== this.currentHealth) {
  176. const eventBus = EventBus.getInstance();
  177. eventBus.emit(GameEvents.WALL_HEALTH_CHANGED, {
  178. previousHealth: previousHealth,
  179. currentHealth: this.currentHealth,
  180. maxHealth: this.getMaxHealth()
  181. });
  182. }
  183. this.updateHealthDisplay();
  184. }
  185. /**
  186. * 获取当前墙体血量
  187. */
  188. public getCurrentHealth(): number {
  189. return this.currentHealth;
  190. }
  191. /**
  192. * 获取最大血量(基于当前等级和技能加成)
  193. */
  194. public getMaxHealth(): number {
  195. const currentLevel = this.getCurrentWallLevel();
  196. const baseMaxHealth = this.getWallHealthByLevel(currentLevel);
  197. // 应用治疗技能的最大血量加成
  198. const skillManager = SkillManager.getInstance();
  199. if (skillManager) {
  200. const healSkillLevel = skillManager.getSkillLevel('heal');
  201. const healthBonus = SkillManager.getHealSkillHealthBonus(healSkillLevel);
  202. return Math.floor(baseMaxHealth * (1 + healthBonus));
  203. }
  204. return baseMaxHealth;
  205. }
  206. /**
  207. * 根据等级获取墙体血量
  208. */
  209. public getWallHealthByLevel(level: number): number {
  210. // 使用本地配置
  211. return this.wallHpMap[level] || (100 + (level - 1) * 200);
  212. }
  213. /**
  214. * 获取当前墙壁等级
  215. */
  216. public getCurrentWallLevel(): number {
  217. return this.saveDataManager?.getWallLevel() || 1;
  218. }
  219. /**
  220. * 恢复墙体血量
  221. */
  222. public heal(amount: number) {
  223. const previousHealth = this.currentHealth;
  224. const maxHealth = this.getMaxHealth();
  225. this.currentHealth = Math.min(maxHealth, this.currentHealth + amount);
  226. // 如果血量发生变化,触发血量变化事件
  227. if (previousHealth !== this.currentHealth) {
  228. const eventBus = EventBus.getInstance();
  229. eventBus.emit(GameEvents.WALL_HEALTH_CHANGED, {
  230. previousHealth: previousHealth,
  231. currentHealth: this.currentHealth,
  232. maxHealth: maxHealth,
  233. healAmount: this.currentHealth - previousHealth
  234. });
  235. }
  236. this.updateHealthDisplay();
  237. }
  238. /**
  239. * 重置墙体血量到满血
  240. */
  241. public resetToFullHealth() {
  242. this.currentHealth = this.getMaxHealth();
  243. this.updateHealthDisplay();
  244. }
  245. /**
  246. * 获取血量百分比
  247. */
  248. public getHealthPercentage(): number {
  249. const maxHealth = this.getMaxHealth();
  250. return maxHealth > 0 ? this.currentHealth / maxHealth : 0;
  251. }
  252. /**
  253. * 检查墙体是否存活
  254. */
  255. public isAlive(): boolean {
  256. return this.currentHealth > 0;
  257. }
  258. /**
  259. * 设置事件监听器
  260. */
  261. private setupEventListeners() {
  262. const eventBus = EventBus.getInstance();
  263. // 监听重置墙体血量事件
  264. eventBus.on(GameEvents.RESET_WALL_HEALTH, this.onResetWallHealthEvent, this);
  265. // 监听墙体血量变化事件(用于升级后更新显示)
  266. eventBus.on(GameEvents.WALL_HEALTH_CHANGED, this.onWallHealthChangedEvent, this);
  267. }
  268. /**
  269. * 处理重置墙体血量事件
  270. */
  271. private onResetWallHealthEvent() {
  272. console.log('[Wall] 接收到重置墙体血量事件,重置到满血');
  273. this.resetToFullHealth();
  274. }
  275. /**
  276. * 处理墙体血量变化事件(用于升级后更新)
  277. */
  278. private onWallHealthChangedEvent(eventData?: any) {
  279. // 只有在特定情况下才重新加载存档数据,避免覆盖受伤后的血量
  280. // 如果事件数据包含isUpgrade标志,说明是升级触发的,需要重新加载
  281. if (eventData && eventData.isUpgrade) {
  282. console.log('[Wall] 接收到墙体升级事件,重新加载血量数据');
  283. this.loadWallHealthFromSave();
  284. this.updateHealthDisplay();
  285. }
  286. // 其他情况(如受伤、治疗)不需要重新加载存档,血量已经在相应方法中更新
  287. }
  288. /**
  289. * 设置技能监听器
  290. */
  291. private setupSkillListeners() {
  292. const skillManager = SkillManager.getInstance();
  293. if (skillManager) {
  294. // 监听治疗技能变化
  295. skillManager.onSkillChanged('heal', this.onHealSkillChanged.bind(this));
  296. }
  297. }
  298. /**
  299. * 治疗技能变化回调
  300. */
  301. private onHealSkillChanged(skillId: string, level: number) {
  302. console.log(`[Wall] 治疗技能等级变化: ${level}`);
  303. // 技能升级时,墙体最大血量可能增加,需要更新显示
  304. this.updateHealthDisplay();
  305. // 如果当前血量低于新的最大血量,可以考虑给予一些额外治疗
  306. // 这里暂时不做额外处理,因为SkillSelectionController已经处理了即时治疗
  307. }
  308. /**
  309. * 清理技能监听器
  310. */
  311. private cleanupSkillListeners() {
  312. const skillManager = SkillManager.getInstance();
  313. if (skillManager) {
  314. skillManager.offSkillChanged('heal', this.onHealSkillChanged.bind(this));
  315. }
  316. }
  317. onDestroy() {
  318. // 清理事件监听
  319. const eventBus = EventBus.getInstance();
  320. eventBus.off(GameEvents.RESET_WALL_HEALTH, this.onResetWallHealthEvent, this);
  321. eventBus.off(GameEvents.WALL_HEALTH_CHANGED, this.onWallHealthChangedEvent, this);
  322. this.cleanupSkillListeners();
  323. }
  324. }