SkillSelectionController.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  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. // 加载技能图标 - 参考UpgradeController的加载方式,在路径后添加'/spriteFrame'
  99. const framePath = `${skillData.icon}/spriteFrame`;
  100. resources.load(framePath, SpriteFrame, (err, spriteFrame) => {
  101. if (!err && spriteFrame && sprite && sprite.isValid) {
  102. sprite.spriteFrame = spriteFrame;
  103. console.log(`技能图标加载成功: ${skillData.icon}`);
  104. } else if (err) {
  105. console.warn(`加载技能图标失败: ${skillData.icon}`, err);
  106. }
  107. });
  108. }
  109. }
  110. // 更新技能名称
  111. const skillName = btnNode.getChildByName('SkillName');
  112. if (skillName) {
  113. const label = skillName.getComponent(Label);
  114. if (label) {
  115. label.string = skillData.name;
  116. }
  117. }
  118. // 更新技能介绍
  119. const skillIntroduce = btnNode.getChildByName('SkillIntroduce');
  120. if (skillIntroduce) {
  121. const label = skillIntroduce.getComponent(Label);
  122. if (label) {
  123. label.string = skillData.description;
  124. }
  125. }
  126. // 设置星级显示 - 从SkillManager获取实际的技能等级
  127. const animator = btnNode.getComponent(SkillButtonAnimator);
  128. if (animator) {
  129. const skillManager = SkillManager.getInstance();
  130. const actualLevel = skillManager ? skillManager.getSkillLevel(skillData.id) : 0;
  131. animator.setSkillLevel(actualLevel);
  132. console.log(`设置技能 ${skillData.name} 星级: ${actualLevel}`);
  133. }
  134. btnNode.active = true;
  135. } else {
  136. btnNode.active = false;
  137. }
  138. });
  139. }
  140. /**
  141. * 玩家选择某技能按钮
  142. */
  143. private onSkillSelected(selectedBtn: Node) {
  144. if (this._clicked) return;
  145. this._clicked = true;
  146. // 获取选中的技能
  147. const btnIndex = this.skillButtons.indexOf(selectedBtn);
  148. if (btnIndex >= 0 && btnIndex < this._currentSkills.length) {
  149. const selectedSkill = this._currentSkills[btnIndex];
  150. // 通过技能管理器升级技能
  151. const skillManager = SkillManager.getInstance();
  152. if (skillManager) {
  153. skillManager.upgradeSkill(selectedSkill.id);
  154. // 立即更新UI显示新的星级
  155. this.updateSkillUI();
  156. // 如果是治疗技能,立即应用治疗效果
  157. if (selectedSkill.id === 'heal') {
  158. this.applyHealEffect(selectedSkill.id);
  159. }
  160. }
  161. }
  162. const otherBtns = this.skillButtons.filter(btn => btn !== selectedBtn);
  163. let finishedOthers = 0;
  164. // 所有其他按钮完成动画后,再收缩选中按钮
  165. const onOtherFinished = () => {
  166. finishedOthers++;
  167. if (finishedOthers >= otherBtns.length) {
  168. // 收缩选中按钮
  169. const selAnim = selectedBtn.getComponent(SkillButtonAnimator);
  170. selAnim?.playShrink(this.shrinkDuration, undefined, () => {
  171. const igm = this.getInGameManager();
  172. if (igm) {
  173. igm.resetEnergyValue();
  174. // 检查是否需要播放diban动画
  175. // 通过InGameManager处理后续逻辑
  176. igm.onSkillSelectionComplete();
  177. }
  178. // 关闭后立刻重置UI
  179. this.resetUI();
  180. });
  181. }
  182. };
  183. // 播放其他按钮动画
  184. const targetPos = selectedBtn.position.clone();
  185. otherBtns.forEach(btn => {
  186. const anim = btn.getComponent(SkillButtonAnimator);
  187. anim?.playShrink(this.shrinkDuration, targetPos, onOtherFinished);
  188. });
  189. }
  190. /**
  191. * 重置 UI 状态,供下次开启时使用
  192. */
  193. public resetUI() {
  194. this._clicked = false;
  195. // 重新启用所有按钮交互
  196. this.skillButtons.forEach(btn => {
  197. // 重置按钮动画状态
  198. const anim = btn.getComponent(SkillButtonAnimator);
  199. anim?.resetState();
  200. });
  201. // 重新随机选择技能
  202. this.randomizeSkills();
  203. }
  204. /**
  205. * 应用治疗技能效果
  206. */
  207. private applyHealEffect(skillId: string) {
  208. const skillManager = SkillManager.getInstance();
  209. if (!skillManager) return;
  210. const skillLevel = skillManager.getSkillLevel(skillId);
  211. if (skillLevel <= 0) return;
  212. // 查找墙体组件
  213. const wall = this.findWallComponent();
  214. if (!wall) {
  215. console.warn('[SkillSelectionController] 未找到墙体组件,无法应用治疗效果');
  216. return;
  217. }
  218. // 计算治疗量:使用SkillManager的计算方法
  219. const maxHealth = wall.getMaxHealth();
  220. const healAmount = SkillManager.calculateInstantHeal(maxHealth, skillLevel);
  221. // 应用治疗效果
  222. wall.heal(healAmount);
  223. // 由于治疗技能会增加最大血量,需要更新血量显示
  224. wall.updateHealthDisplay();
  225. console.log(`[SkillSelectionController] 应用治疗效果: +${healAmount} 血量,技能等级: ${skillLevel}`);
  226. }
  227. /**
  228. * 查找墙体组件
  229. */
  230. private findWallComponent(): Wall | null {
  231. // 尝试从EnemyController获取墙体组件
  232. const enemyController = EnemyController.getInstance();
  233. if (enemyController) {
  234. // 通过topFenceNode或bottomFenceNode查找Wall组件
  235. if (enemyController.topFenceNode) {
  236. const wall = enemyController.topFenceNode.getComponent(Wall);
  237. if (wall) return wall;
  238. }
  239. if (enemyController.bottomFenceNode) {
  240. const wall = enemyController.bottomFenceNode.getComponent(Wall);
  241. if (wall) return wall;
  242. }
  243. }
  244. // 备用方案:直接在场景中查找
  245. const wallNode = find('Canvas/GameLevelUI/GameArea/TopFence') ||
  246. find('Canvas/GameLevelUI/GameArea/BottomFence') ||
  247. find('Canvas/GameLevelUI/Wall') ||
  248. find('Canvas/Wall');
  249. if (wallNode) {
  250. return wallNode.getComponent(Wall);
  251. }
  252. return null;
  253. }
  254. private getGameManager(): GameManager | null {
  255. const gmNode = find('Canvas/GameLevelUI/GameManager');
  256. return gmNode?.getComponent(GameManager) || null;
  257. }
  258. private getInGameManager(): InGameManager | null {
  259. const gm = this.getGameManager();
  260. return gm ? gm.getInGameManager() : null;
  261. }
  262. }