Wall.ts 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  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 { 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. @property({
  18. type: JsonAsset,
  19. tooltip: '墙体配置文件'
  20. })
  21. public wallConfigAsset: JsonAsset = null;
  22. // === 私有属性 ===
  23. private currentHealth: number = 100;
  24. private heartLabel: Label = null;
  25. private saveDataManager: SaveDataManager = null;
  26. // 墙体配置数据
  27. private wallConfig: any = null;
  28. private wallHpMap: Record<number, number> = {};
  29. start() {
  30. this.initializeWall();
  31. }
  32. /**
  33. * 加载墙体配置
  34. */
  35. private loadWallConfig(): void {
  36. if (this.wallConfigAsset) {
  37. try {
  38. this.wallConfig = this.wallConfigAsset.json;
  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 (parseErr) {
  52. console.error('[Wall] 解析墙体配置失败:', parseErr);
  53. this.useDefaultConfig();
  54. }
  55. } else {
  56. console.warn('[Wall] 未挂载墙体配置文件,使用默认配置');
  57. this.useDefaultConfig();
  58. }
  59. }
  60. /**
  61. * 使用默认配置
  62. */
  63. private useDefaultConfig(): void {
  64. this.wallHpMap = {
  65. 1: 100,
  66. 2: 500,
  67. 3: 1200,
  68. 4: 1500,
  69. 5: 2000
  70. };
  71. }
  72. /**
  73. * 初始化墙体
  74. */
  75. private initializeWall() {
  76. // 初始化存档管理器
  77. this.saveDataManager = SaveDataManager.getInstance();
  78. if (!this.saveDataManager) {
  79. console.error('[Wall] SaveDataManager not found');
  80. return;
  81. }
  82. // 加载墙体配置
  83. this.loadWallConfig();
  84. // 查找血量显示节点
  85. this.findHeartLabelNode();
  86. // 从存档读取墙体血量
  87. this.loadWallHealthFromSave();
  88. // 初始化血量显示
  89. this.updateHealthDisplay();
  90. // 监听治疗技能变化
  91. this.setupSkillListeners();
  92. // 设置事件监听器
  93. this.setupEventListeners();
  94. }
  95. /**
  96. * 查找血量显示节点
  97. */
  98. private findHeartLabelNode() {
  99. // 查找心血显示节点
  100. if (!this.heartLabelNode) {
  101. this.heartLabelNode = find('Canvas-001/TopArea/HeartNode/HeartLabel') || find('Canvas/GameLevelUI/HeartNode/HeartLabel');
  102. }
  103. if (this.heartLabelNode) {
  104. this.heartLabel = this.heartLabelNode.getComponent(Label);
  105. }
  106. }
  107. /**
  108. * 从存档加载墙体血量
  109. */
  110. private loadWallHealthFromSave() {
  111. const pd = this.saveDataManager.getPlayerData();
  112. if (pd && typeof pd.wallBaseHealth === 'number') {
  113. this.currentHealth = pd.wallBaseHealth;
  114. } else {
  115. // 如果没有存档数据,使用默认血量
  116. this.currentHealth = this.getWallHealthByLevel(1);
  117. }
  118. // 确保当前血量不超过最大血量(考虑技能加成)
  119. const maxHealth = this.getMaxHealth();
  120. if (this.currentHealth > maxHealth) {
  121. this.currentHealth = maxHealth;
  122. }
  123. }
  124. /**
  125. * 墙体受到伤害
  126. */
  127. public takeDamage(damage: number) {
  128. if (damage <= 0) return;
  129. const previousHealth = this.currentHealth;
  130. this.currentHealth = Math.max(0, this.currentHealth - damage);
  131. // 触发受到伤害事件
  132. const eventBus = EventBus.getInstance();
  133. eventBus.emit(GameEvents.WALL_TAKE_DAMAGE, {
  134. damage: damage,
  135. previousHealth: previousHealth,
  136. currentHealth: this.currentHealth
  137. });
  138. // 触发血量变化事件
  139. eventBus.emit(GameEvents.WALL_HEALTH_CHANGED, {
  140. previousHealth: previousHealth,
  141. currentHealth: this.currentHealth,
  142. maxHealth: this.getMaxHealth()
  143. });
  144. // 更新血量显示
  145. this.updateHealthDisplay();
  146. console.log(`[Wall] 墙体受到伤害: ${damage}, 当前血量: ${this.currentHealth}`);
  147. // 检查墙体是否被摧毁
  148. if (this.currentHealth <= 0) {
  149. this.onWallDestroyed();
  150. }
  151. }
  152. /**
  153. * 墙体被摧毁时的处理
  154. */
  155. private onWallDestroyed() {
  156. console.log('[Wall] 墙体被摧毁,触发游戏失败');
  157. // 通过事件系统触发墙体被摧毁事件
  158. const eventBus = EventBus.getInstance();
  159. eventBus.emit(GameEvents.WALL_DESTROYED, {
  160. finalHealth: this.currentHealth,
  161. maxHealth: this.getMaxHealth()
  162. });
  163. // 触发游戏失败
  164. eventBus.emit(GameEvents.GAME_DEFEAT);
  165. }
  166. /**
  167. * 更新血量显示
  168. */
  169. public updateHealthDisplay() {
  170. if (this.heartLabel) {
  171. this.heartLabel.string = Math.floor(this.currentHealth).toString();
  172. }
  173. }
  174. /**
  175. * 设置墙体血量
  176. */
  177. public setHealth(health: number) {
  178. const previousHealth = this.currentHealth;
  179. this.currentHealth = Math.max(0, health);
  180. // 如果血量发生变化,触发血量变化事件
  181. if (previousHealth !== this.currentHealth) {
  182. const eventBus = EventBus.getInstance();
  183. eventBus.emit(GameEvents.WALL_HEALTH_CHANGED, {
  184. previousHealth: previousHealth,
  185. currentHealth: this.currentHealth,
  186. maxHealth: this.getMaxHealth()
  187. });
  188. }
  189. this.updateHealthDisplay();
  190. }
  191. /**
  192. * 获取当前墙体血量
  193. */
  194. public getCurrentHealth(): number {
  195. return this.currentHealth;
  196. }
  197. /**
  198. * 获取最大血量(基于当前等级和技能加成)
  199. */
  200. public getMaxHealth(): number {
  201. const currentLevel = this.getCurrentWallLevel();
  202. const baseMaxHealth = this.getWallHealthByLevel(currentLevel);
  203. // 应用治疗技能的最大血量加成
  204. const skillManager = SkillManager.getInstance();
  205. if (skillManager) {
  206. const healSkillLevel = skillManager.getSkillLevel('heal');
  207. const healthBonus = SkillManager.getHealSkillHealthBonus(healSkillLevel);
  208. return Math.floor(baseMaxHealth * (1 + healthBonus));
  209. }
  210. return baseMaxHealth;
  211. }
  212. /**
  213. * 根据等级获取墙体血量
  214. */
  215. public getWallHealthByLevel(level: number): number {
  216. // 优先使用SaveDataManager的方法,保持配置一致性
  217. if (this.saveDataManager) {
  218. return this.saveDataManager.getWallHealthByLevel(level);
  219. }
  220. // 备用方案:使用本地配置
  221. return this.wallHpMap[level] || (100 + (level - 1) * 200);
  222. }
  223. /**
  224. * 获取当前墙壁等级
  225. */
  226. public getCurrentWallLevel(): number {
  227. return this.saveDataManager?.getWallLevel() || 1;
  228. }
  229. /**
  230. * 升级墙体等级
  231. * 返回升级后信息,失败返回null
  232. */
  233. public upgradeWallLevel(): { currentLevel: number; currentHp: number; nextLevel: number; nextHp: number } | null {
  234. if (!this.saveDataManager) return null;
  235. // 检查是否可以升级
  236. if (!this.saveDataManager.canUpgradeWall()) return null;
  237. // 获取升级费用并扣除钞票
  238. const cost = this.saveDataManager.getWallUpgradeCost();
  239. if (!this.saveDataManager.spendMoney(cost)) return null;
  240. // 执行升级
  241. if (!this.saveDataManager.upgradeWallLevel()) return null;
  242. const newLvl = this.saveDataManager.getWallLevel();
  243. const baseNewHp = this.getWallHealthByLevel(newLvl);
  244. const actualNewHp = this.getWallHealthByLevel(newLvl); // 使用当前等级的基础血量
  245. // 更新内存中的数值为当前等级的基础血量
  246. const previousHealth = this.currentHealth;
  247. this.currentHealth = actualNewHp;
  248. // 触发墙体血量变化事件,标记为升级事件
  249. const eventBus = EventBus.getInstance();
  250. eventBus.emit(GameEvents.WALL_HEALTH_CHANGED, {
  251. previousHealth: previousHealth,
  252. currentHealth: this.currentHealth,
  253. maxHealth: this.getMaxHealth(),
  254. isUpgrade: true
  255. });
  256. this.updateHealthDisplay();
  257. return {
  258. currentLevel: newLvl,
  259. currentHp: actualNewHp,
  260. nextLevel: newLvl + 1,
  261. nextHp: this.getWallHealthByLevel(newLvl + 1) // 这里返回基础血量用于显示
  262. };
  263. }
  264. /**
  265. * 恢复墙体血量
  266. */
  267. public heal(amount: number) {
  268. const previousHealth = this.currentHealth;
  269. const maxHealth = this.getMaxHealth();
  270. this.currentHealth = Math.min(maxHealth, this.currentHealth + amount);
  271. // 如果血量发生变化,触发血量变化事件
  272. if (previousHealth !== this.currentHealth) {
  273. const eventBus = EventBus.getInstance();
  274. eventBus.emit(GameEvents.WALL_HEALTH_CHANGED, {
  275. previousHealth: previousHealth,
  276. currentHealth: this.currentHealth,
  277. maxHealth: maxHealth,
  278. healAmount: this.currentHealth - previousHealth
  279. });
  280. }
  281. this.updateHealthDisplay();
  282. }
  283. /**
  284. * 重置墙体血量到满血
  285. */
  286. public resetToFullHealth() {
  287. this.currentHealth = this.getMaxHealth();
  288. this.updateHealthDisplay();
  289. }
  290. /**
  291. * 获取血量百分比
  292. */
  293. public getHealthPercentage(): number {
  294. const maxHealth = this.getMaxHealth();
  295. return maxHealth > 0 ? this.currentHealth / maxHealth : 0;
  296. }
  297. /**
  298. * 检查墙体是否存活
  299. */
  300. public isAlive(): boolean {
  301. return this.currentHealth > 0;
  302. }
  303. /**
  304. * 设置事件监听器
  305. */
  306. private setupEventListeners() {
  307. const eventBus = EventBus.getInstance();
  308. // 监听重置墙体血量事件
  309. eventBus.on(GameEvents.RESET_WALL_HEALTH, this.onResetWallHealthEvent, this);
  310. // 监听墙体血量变化事件(用于升级后更新显示)
  311. eventBus.on(GameEvents.WALL_HEALTH_CHANGED, this.onWallHealthChangedEvent, this);
  312. }
  313. /**
  314. * 处理重置墙体血量事件
  315. */
  316. private onResetWallHealthEvent() {
  317. console.log('[Wall] 接收到重置墙体血量事件,重置到满血');
  318. this.resetToFullHealth();
  319. }
  320. /**
  321. * 处理墙体血量变化事件(用于升级后更新)
  322. */
  323. private onWallHealthChangedEvent(eventData?: any) {
  324. // 只有在特定情况下才重新加载存档数据,避免覆盖受伤后的血量
  325. // 如果事件数据包含isUpgrade标志,说明是升级触发的,需要重新加载
  326. if (eventData && eventData.isUpgrade) {
  327. console.log('[Wall] 接收到墙体升级事件,重新加载血量数据');
  328. this.loadWallHealthFromSave();
  329. this.updateHealthDisplay();
  330. }
  331. // 其他情况(如受伤、治疗)不需要重新加载存档,血量已经在相应方法中更新
  332. }
  333. /**
  334. * 设置技能监听器
  335. */
  336. private setupSkillListeners() {
  337. const skillManager = SkillManager.getInstance();
  338. if (skillManager) {
  339. // 监听治疗技能变化
  340. skillManager.onSkillChanged('heal', this.onHealSkillChanged.bind(this));
  341. }
  342. }
  343. /**
  344. * 治疗技能变化回调
  345. */
  346. private onHealSkillChanged(skillId: string, level: number) {
  347. console.log(`[Wall] 治疗技能等级变化: ${level}`);
  348. // 技能升级时,墙体最大血量可能增加,需要更新显示
  349. this.updateHealthDisplay();
  350. // 如果当前血量低于新的最大血量,可以考虑给予一些额外治疗
  351. // 这里暂时不做额外处理,因为SkillSelectionController已经处理了即时治疗
  352. }
  353. /**
  354. * 清理技能监听器
  355. */
  356. private cleanupSkillListeners() {
  357. const skillManager = SkillManager.getInstance();
  358. if (skillManager) {
  359. skillManager.offSkillChanged('heal', this.onHealSkillChanged.bind(this));
  360. }
  361. }
  362. onDestroy() {
  363. // 清理事件监听
  364. const eventBus = EventBus.getInstance();
  365. eventBus.off(GameEvents.RESET_WALL_HEALTH, this.onResetWallHealthEvent, this);
  366. eventBus.off(GameEvents.WALL_HEALTH_CHANGED, this.onWallHealthChangedEvent, this);
  367. this.cleanupSkillListeners();
  368. }
  369. }