SkillSelectionController.ts 10 KB

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