Wall.ts 11 KB

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