SkillSelectionController.ts 11 KB

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