SkillSelectionController.ts 10 KB

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