SkillSelectionController.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  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. // 禁用所有按钮交互
  163. this.skillButtons.forEach(btn => {
  164. const b = btn.getComponent(Button);
  165. if (b) b.interactable = false;
  166. });
  167. const otherBtns = this.skillButtons.filter(btn => btn !== selectedBtn);
  168. let finishedOthers = 0;
  169. // 所有其他按钮完成动画后,再收缩选中按钮
  170. const onOtherFinished = () => {
  171. finishedOthers++;
  172. if (finishedOthers >= otherBtns.length) {
  173. // 收缩选中按钮
  174. const selAnim = selectedBtn.getComponent(SkillButtonAnimator);
  175. selAnim?.playShrink(this.shrinkDuration, undefined, () => {
  176. const igm = this.getInGameManager();
  177. if (igm) {
  178. igm.resetEnergyValue();
  179. // 检查是否需要播放diban动画
  180. // 通过InGameManager处理后续逻辑
  181. igm.onSkillSelectionComplete();
  182. }
  183. // 关闭后立刻重置UI
  184. this.resetUI();
  185. });
  186. }
  187. };
  188. // 播放其他按钮动画
  189. const targetPos = selectedBtn.position.clone();
  190. otherBtns.forEach(btn => {
  191. const anim = btn.getComponent(SkillButtonAnimator);
  192. anim?.playShrink(this.shrinkDuration, targetPos, onOtherFinished);
  193. });
  194. }
  195. /**
  196. * 重置 UI 状态,供下次开启时使用
  197. */
  198. public resetUI() {
  199. this._clicked = false;
  200. // 重新启用所有按钮交互
  201. this.skillButtons.forEach(btn => {
  202. const b = btn.getComponent(Button);
  203. if (b) b.interactable = true;
  204. // 重置按钮动画状态
  205. const anim = btn.getComponent(SkillButtonAnimator);
  206. anim?.resetState();
  207. });
  208. // 重新随机选择技能
  209. this.randomizeSkills();
  210. }
  211. /**
  212. * 应用治疗技能效果
  213. */
  214. private applyHealEffect(skillId: string) {
  215. const skillManager = SkillManager.getInstance();
  216. if (!skillManager) return;
  217. const skillLevel = skillManager.getSkillLevel(skillId);
  218. if (skillLevel <= 0) return;
  219. // 查找墙体组件
  220. const wall = this.findWallComponent();
  221. if (!wall) {
  222. console.warn('[SkillSelectionController] 未找到墙体组件,无法应用治疗效果');
  223. return;
  224. }
  225. // 计算治疗量:使用SkillManager的计算方法
  226. const maxHealth = wall.getMaxHealth();
  227. const healAmount = SkillManager.calculateInstantHeal(maxHealth, skillLevel);
  228. // 应用治疗效果
  229. wall.heal(healAmount);
  230. // 由于治疗技能会增加最大血量,需要更新血量显示
  231. wall.updateHealthDisplay();
  232. console.log(`[SkillSelectionController] 应用治疗效果: +${healAmount} 血量,技能等级: ${skillLevel}`);
  233. }
  234. /**
  235. * 查找墙体组件
  236. */
  237. private findWallComponent(): Wall | null {
  238. // 尝试从EnemyController获取墙体组件
  239. const enemyController = EnemyController.getInstance();
  240. if (enemyController) {
  241. // 通过topFenceNode或bottomFenceNode查找Wall组件
  242. if (enemyController.topFenceNode) {
  243. const wall = enemyController.topFenceNode.getComponent(Wall);
  244. if (wall) return wall;
  245. }
  246. if (enemyController.bottomFenceNode) {
  247. const wall = enemyController.bottomFenceNode.getComponent(Wall);
  248. if (wall) return wall;
  249. }
  250. }
  251. // 备用方案:直接在场景中查找
  252. const wallNode = find('Canvas/GameLevelUI/GameArea/TopFence') ||
  253. find('Canvas/GameLevelUI/GameArea/BottomFence') ||
  254. find('Canvas/GameLevelUI/Wall') ||
  255. find('Canvas/Wall');
  256. if (wallNode) {
  257. return wallNode.getComponent(Wall);
  258. }
  259. return null;
  260. }
  261. private getGameManager(): GameManager | null {
  262. const gmNode = find('Canvas/GameLevelUI/GameManager');
  263. return gmNode?.getComponent(GameManager) || null;
  264. }
  265. private getInGameManager(): InGameManager | null {
  266. const gm = this.getGameManager();
  267. return gm ? gm.getInGameManager() : null;
  268. }
  269. }