SkillSelectionController.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. import { _decorator, Component, Node, Button, find, Label, Sprite, Vec3, CCFloat, resources, JsonAsset, SpriteFrame } from 'cc';
  2. import { GameManager } from '../../LevelSystem/GameManager';
  3. import { InGameManager } from '../../LevelSystem/IN_game';
  4. import { SkillButtonAnimator } from './SkillButtonAnimator';
  5. // GamePause已通过事件系统解耦,不再需要直接导入
  6. import { SkillManager, SkillData } from './SkillManager';
  7. import { Wall } from '../Wall';
  8. import { EnemyController } from '../EnemyController';
  9. import EventBus, { GameEvents } from '../../Core/EventBus';
  10. const { ccclass, property } = _decorator;
  11. // 移除本地SkillData声明,直接用SkillManager中的SkillData
  12. interface SkillConfig {
  13. skills: SkillData[];
  14. }
  15. /**
  16. * SkillSelectionController
  17. * 放在 Canvas/SelectSkillUI 节点上。
  18. * 负责监听技能按钮点击,随机选择技能显示,并按顺序播放缩小动画,最后关闭整个 SelectSkillUI。
  19. */
  20. @ccclass('SkillSelectionController')
  21. export class SkillSelectionController extends Component {
  22. @property({ type: [Node], tooltip: '技能按钮节点列表,留空则自动从 SkillsContainer 获取' })
  23. public skillButtons: Node[] = [];
  24. @property({ type: CCFloat, tooltip: '收缩动画时长' })
  25. public shrinkDuration: number = 0.3;
  26. // 防止重复点击标记
  27. private _clicked = false;
  28. // 技能配置数据
  29. private _skillConfig: SkillConfig | null = null;
  30. // 当前显示的技能数据
  31. private _currentSkills: SkillData[] = [];
  32. start() {
  33. this.loadSkillConfig();
  34. }
  35. /**
  36. * 加载技能配置
  37. */
  38. private loadSkillConfig() {
  39. resources.load('data/skill', JsonAsset, (err, jsonAsset: JsonAsset) => {
  40. if (err) {
  41. console.error('加载技能配置失败:', err);
  42. return;
  43. }
  44. this._skillConfig = jsonAsset.json as SkillConfig;
  45. // 初始化技能管理器
  46. const skillManager = SkillManager.getInstance();
  47. if (skillManager) {
  48. skillManager.initSkills(this._skillConfig.skills);
  49. }
  50. this.setupSkillButtons();
  51. this.randomizeSkills();
  52. });
  53. }
  54. private setupSkillButtons() {
  55. if (this.skillButtons.length === 0) {
  56. const container = this.node.getChildByName('SkillsContainer');
  57. if (container) {
  58. this.skillButtons = container.children.slice();
  59. }
  60. }
  61. this.skillButtons.forEach(btnNode => {
  62. const btn = btnNode.getComponent(Button);
  63. if (btn) {
  64. btn.node.on(Button.EventType.CLICK, () => this.onSkillSelected(btnNode), this);
  65. }
  66. });
  67. }
  68. /**
  69. * 随机选择3个技能显示
  70. */
  71. private randomizeSkills() {
  72. if (!this._skillConfig || this._skillConfig.skills.length < 3) {
  73. console.error('技能配置不足,无法随机选择');
  74. return;
  75. }
  76. // 从6个技能中随机选择3个
  77. const allSkills = [...this._skillConfig.skills];
  78. this._currentSkills = [];
  79. for (let i = 0; i < 3 && allSkills.length > 0; i++) {
  80. const randomIndex = Math.floor(Math.random() * allSkills.length);
  81. this._currentSkills.push(allSkills.splice(randomIndex, 1)[0]);
  82. }
  83. // 更新UI显示
  84. this.updateSkillUI();
  85. }
  86. /**
  87. * 更新技能UI显示
  88. */
  89. private updateSkillUI() {
  90. this.skillButtons.forEach((btnNode, index) => {
  91. if (index < this._currentSkills.length) {
  92. const skillData = this._currentSkills[index];
  93. // 更新技能图标
  94. const skillSprite = btnNode.getChildByName('SkillSprite');
  95. if (skillSprite) {
  96. const sprite = skillSprite.getComponent(Sprite);
  97. if (sprite) {
  98. // 加载技能图标
  99. resources.load(skillData.icon, SpriteFrame, (err, spriteFrame) => {
  100. if (!err && spriteFrame && sprite) {
  101. sprite.spriteFrame = spriteFrame;
  102. }
  103. });
  104. }
  105. }
  106. // 更新技能名称
  107. const skillName = btnNode.getChildByName('SkillName');
  108. if (skillName) {
  109. const label = skillName.getComponent(Label);
  110. if (label) {
  111. label.string = skillData.name;
  112. }
  113. }
  114. // 更新技能介绍
  115. const skillIntroduce = btnNode.getChildByName('SkillIntroduce');
  116. if (skillIntroduce) {
  117. const label = skillIntroduce.getComponent(Label);
  118. if (label) {
  119. label.string = skillData.description;
  120. }
  121. }
  122. // 设置星级显示
  123. const animator = btnNode.getComponent(SkillButtonAnimator);
  124. if (animator) {
  125. animator.setSkillLevel(skillData.currentLevel);
  126. }
  127. btnNode.active = true;
  128. } else {
  129. btnNode.active = false;
  130. }
  131. });
  132. }
  133. /**
  134. * 玩家选择某技能按钮
  135. */
  136. private onSkillSelected(selectedBtn: Node) {
  137. if (this._clicked) return;
  138. this._clicked = true;
  139. // 获取选中的技能
  140. const btnIndex = this.skillButtons.indexOf(selectedBtn);
  141. if (btnIndex >= 0 && btnIndex < this._currentSkills.length) {
  142. const selectedSkill = this._currentSkills[btnIndex];
  143. // 通过技能管理器升级技能
  144. const skillManager = SkillManager.getInstance();
  145. if (skillManager) {
  146. skillManager.upgradeSkill(selectedSkill.id);
  147. // 如果是治疗技能,立即应用治疗效果
  148. if (selectedSkill.id === 'heal') {
  149. this.applyHealEffect(selectedSkill.id);
  150. }
  151. }
  152. }
  153. // 禁用所有按钮交互
  154. this.skillButtons.forEach(btn => {
  155. const b = btn.getComponent(Button);
  156. if (b) b.interactable = false;
  157. });
  158. const otherBtns = this.skillButtons.filter(btn => btn !== selectedBtn);
  159. let finishedOthers = 0;
  160. // 所有其他按钮完成动画后,再收缩选中按钮
  161. const onOtherFinished = () => {
  162. finishedOthers++;
  163. if (finishedOthers >= otherBtns.length) {
  164. // 收缩选中按钮
  165. const selAnim = selectedBtn.getComponent(SkillButtonAnimator);
  166. selAnim?.playShrink(this.shrinkDuration, undefined, () => {
  167. const igm = this.getInGameManager();
  168. if (igm) {
  169. igm.resetEnergyValue();
  170. // 检查是否需要播放diban动画
  171. // 通过InGameManager处理后续逻辑
  172. igm.onSkillSelectionComplete();
  173. }
  174. // 关闭后立刻重置UI
  175. this.resetUI();
  176. });
  177. }
  178. };
  179. // 播放其他按钮动画
  180. const targetPos = selectedBtn.position.clone();
  181. otherBtns.forEach(btn => {
  182. const anim = btn.getComponent(SkillButtonAnimator);
  183. anim?.playShrink(this.shrinkDuration, targetPos, onOtherFinished);
  184. });
  185. }
  186. /**
  187. * 重置 UI 状态,供下次开启时使用
  188. */
  189. public resetUI() {
  190. this._clicked = false;
  191. // 重新启用所有按钮交互
  192. this.skillButtons.forEach(btn => {
  193. const b = btn.getComponent(Button);
  194. if (b) b.interactable = true;
  195. // 重置按钮动画状态
  196. const anim = btn.getComponent(SkillButtonAnimator);
  197. anim?.resetState();
  198. });
  199. // 重新随机选择技能
  200. this.randomizeSkills();
  201. }
  202. /**
  203. * 应用治疗技能效果
  204. */
  205. private applyHealEffect(skillId: string) {
  206. const skillManager = SkillManager.getInstance();
  207. if (!skillManager) return;
  208. const skillLevel = skillManager.getSkillLevel(skillId);
  209. if (skillLevel <= 0) return;
  210. // 查找墙体组件
  211. const wall = this.findWallComponent();
  212. if (!wall) {
  213. console.warn('[SkillSelectionController] 未找到墙体组件,无法应用治疗效果');
  214. return;
  215. }
  216. // 计算治疗量:使用SkillManager的计算方法
  217. const maxHealth = wall.getMaxHealth();
  218. const healAmount = SkillManager.calculateInstantHeal(maxHealth, skillLevel);
  219. // 应用治疗效果
  220. wall.heal(healAmount);
  221. // 由于治疗技能会增加最大血量,需要更新血量显示
  222. wall.updateHealthDisplay();
  223. console.log(`[SkillSelectionController] 应用治疗效果: +${healAmount} 血量,技能等级: ${skillLevel}`);
  224. }
  225. /**
  226. * 查找墙体组件
  227. */
  228. private findWallComponent(): Wall | null {
  229. // 尝试从EnemyController获取墙体组件
  230. const enemyController = EnemyController.getInstance();
  231. if (enemyController) {
  232. // 通过topFenceNode或bottomFenceNode查找Wall组件
  233. if (enemyController.topFenceNode) {
  234. const wall = enemyController.topFenceNode.getComponent(Wall);
  235. if (wall) return wall;
  236. }
  237. if (enemyController.bottomFenceNode) {
  238. const wall = enemyController.bottomFenceNode.getComponent(Wall);
  239. if (wall) return wall;
  240. }
  241. }
  242. // 备用方案:直接在场景中查找
  243. const wallNode = find('Canvas/GameLevelUI/GameArea/TopFence') ||
  244. find('Canvas/GameLevelUI/GameArea/BottomFence') ||
  245. find('Canvas/GameLevelUI/Wall') ||
  246. find('Canvas/Wall');
  247. if (wallNode) {
  248. return wallNode.getComponent(Wall);
  249. }
  250. return null;
  251. }
  252. private getGameManager(): GameManager | null {
  253. const gmNode = find('Canvas/GameLevelUI/GameManager');
  254. return gmNode?.getComponent(GameManager) || null;
  255. }
  256. private getInGameManager(): InGameManager | null {
  257. const gm = this.getGameManager();
  258. return gm ? gm.getInGameManager() : null;
  259. }
  260. }