SkillNodeGenerator.ts 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692
  1. import { _decorator, Component, Node, Prefab, resources, instantiate, Label, Sprite, SpriteFrame, Button, ProgressBar, ScrollView, Vec2, Vec3, UITransform } from 'cc';
  2. import { PersistentSkillManager } from './PersistentSkillManager';
  3. import { SaveDataManager } from '../../LevelSystem/SaveDataManager';
  4. const { ccclass, property } = _decorator;
  5. /**
  6. * 技能节点数据接口
  7. */
  8. interface SkillNodeData {
  9. node: Node;
  10. group: number;
  11. skillIndex: number;
  12. cost: number;
  13. isUnlocked: boolean; // 本地记录的解锁状态
  14. backgroundSprite: Sprite;
  15. iconSprite: Sprite;
  16. }
  17. /**
  18. * 技能节点生成器
  19. * 自动生成12组技能节点,每组包含3个技能类型
  20. * 支持技能点亮状态管理和图片切换
  21. */
  22. @ccclass('SkillNodeGenerator')
  23. export class SkillNodeGenerator extends Component {
  24. @property(Prefab)
  25. skillNode1Prefab: Prefab = null!; // Damage
  26. @property(Prefab)
  27. skillNode2Prefab: Prefab = null!; // Ball Speed
  28. @property(Prefab)
  29. skillNode3Prefab: Prefab = null!; // Crit Damage
  30. // 图片资源
  31. @property(SpriteFrame)
  32. backgroundNormal: SpriteFrame = null!; // an001.png - 未点亮状态
  33. @property(SpriteFrame)
  34. backgroundUnlocked: SpriteFrame = null!; // an000.png - 点亮状态
  35. @property(SpriteFrame)
  36. iconNormal: SpriteFrame = null!; // tb002.png - 未点亮状态
  37. @property(SpriteFrame)
  38. iconUnlocked: SpriteFrame = null!; // tb001.png - 点亮状态
  39. @property(ProgressBar)
  40. progressBar: ProgressBar = null!; // 进度条
  41. @property(ScrollView)
  42. scrollView: ScrollView = null!; // 滚动视图
  43. @property(Node)
  44. diamondNumberNode: Node = null!; // 钻石数量显示节点
  45. // 技能节点数据存储(一维数组)
  46. private skillNodes: SkillNodeData[] = [];
  47. // 当前解锁到的节点索引
  48. private currentUnlockIndex: number = -1;
  49. // 持久化技能管理器
  50. private skillManager: PersistentSkillManager | null = null;
  51. // 存档管理器
  52. private saveDataManager: SaveDataManager | null = null;
  53. start() {
  54. console.log('SkillNodeGenerator 初始化开始');
  55. this.skillManager = PersistentSkillManager.getInstance();
  56. this.saveDataManager = SaveDataManager.getInstance();
  57. // 生成所有技能节点
  58. this.generateSkillNodes();
  59. console.log(`技能节点生成完成,共 ${this.skillNodes.length} 个节点`);
  60. // 加载已解锁的技能状态
  61. this.loadUnlockedSkills();
  62. // 更新进度条
  63. this.updateProgressBar();
  64. // 延迟一帧后滚动到最新解锁的技能节点,确保所有节点都已正确布局
  65. this.scheduleOnce(() => {
  66. this.scrollToLatestUnlockedSkill();
  67. }, 0.1);
  68. console.log('SkillNodeGenerator 初始化完成');
  69. }
  70. /**
  71. * 生成技能节点
  72. */
  73. private generateSkillNodes() {
  74. // 技能节点预制体数组
  75. const prefabs = [
  76. this.skillNode1Prefab, // Damage
  77. this.skillNode2Prefab, // Ball Speed
  78. this.skillNode3Prefab // Crit Damage
  79. ];
  80. // 技能名称模板
  81. const skillNames = [
  82. 'Damage +{}%',
  83. 'Ball Speed +{}%',
  84. 'Crit Damage +{}%'
  85. ];
  86. // 生成12组技能节点,按顺序存储到一维数组中
  87. for (let group = 1; group <= 12; group++) {
  88. const effectPercent = group * 5; // 5%, 10%, 15%, ..., 60%
  89. // 计算技能消耗,每组递增10点
  90. const skillCost = 10 + (group - 1) * 10;
  91. // 每组生成3个技能节点:Damage、Ball Speed、Crit Damage
  92. for (let skillIndex = 0; skillIndex < 3; skillIndex++) {
  93. // 计算节点在一维数组中的索引
  94. const arrayIndex = (group - 1) * 3 + skillIndex;
  95. this.createSkillNode(
  96. prefabs[skillIndex],
  97. skillNames[skillIndex].replace('{}', effectPercent.toString()),
  98. skillCost.toString(),
  99. group,
  100. skillIndex,
  101. arrayIndex
  102. );
  103. }
  104. }
  105. }
  106. /**
  107. * 创建技能节点
  108. */
  109. private createSkillNode(
  110. prefab: Prefab,
  111. skillName: string,
  112. cost: string,
  113. group: number,
  114. skillIndex: number,
  115. totalIndex: number
  116. ) {
  117. if (!prefab) {
  118. console.error(`Prefab is null for skill index: ${skillIndex}`);
  119. return;
  120. }
  121. // 实例化预制体
  122. const skillNode = instantiate(prefab);
  123. // 设置节点名称
  124. skillNode.name = `SkillNode${skillIndex + 1}_Group${group}`;
  125. // 查找并设置NameLabel
  126. const nameLabel = skillNode.getChildByName('NameLabel');
  127. if (nameLabel) {
  128. const labelComponent = nameLabel.getComponent(Label);
  129. if (labelComponent) {
  130. labelComponent.string = skillName;
  131. }
  132. }
  133. // 查找并设置CostLabel
  134. const costLabel = skillNode.getChildByName('CostLabel');
  135. if (costLabel) {
  136. const labelComponent = costLabel.getComponent(Label);
  137. if (labelComponent) {
  138. labelComponent.string = cost;
  139. }
  140. }
  141. // 设置节点位置(从下到上排列)
  142. // 每个节点垂直间距150,从最下方开始排列
  143. const yPosition = -totalIndex * 150;
  144. skillNode.setPosition(0, yPosition, 0);
  145. console.log(`Created skill node at position Y: ${yPosition}, totalIndex: ${totalIndex}`);
  146. // 添加到当前节点(content节点)
  147. this.node.addChild(skillNode);
  148. // 获取Background和Icon的Sprite组件
  149. const backgroundNode = skillNode.getChildByName('Background');
  150. const iconNode = skillNode.getChildByName('Icon');
  151. const backgroundSprite = backgroundNode?.getComponent(Sprite);
  152. const iconSprite = iconNode?.getComponent(Sprite);
  153. if (!backgroundSprite || !iconSprite) {
  154. console.error(`Failed to get sprite components for skill node: ${skillName}`);
  155. return;
  156. }
  157. // 创建技能节点数据
  158. const skillNodeData: SkillNodeData = {
  159. node: skillNode,
  160. group: group,
  161. skillIndex: skillIndex,
  162. cost: parseInt(cost),
  163. isUnlocked: false, // 初始化为未解锁状态
  164. backgroundSprite: backgroundSprite,
  165. iconSprite: iconSprite
  166. };
  167. // 存储技能节点数据
  168. this.skillNodes.push(skillNodeData);
  169. // 添加点击事件
  170. const buttonComponent = skillNode.getChildByName('Background').getComponent(Button);
  171. if (buttonComponent) {
  172. buttonComponent.node.on(Button.EventType.CLICK, () => {
  173. this.onSkillNodeClick(skillNodeData);
  174. }, this);
  175. }
  176. // 初始化为未点亮状态
  177. this.updateSkillNodeVisual(skillNodeData);
  178. console.log(`Created skill node: ${skillName}, Cost: ${cost}, Group: ${group}, Position: (0, ${yPosition}, 0)`);
  179. }
  180. /**
  181. * 技能节点点击事件处理
  182. */
  183. private onSkillNodeClick(skillNodeData: SkillNodeData) {
  184. // 计算当前节点的索引
  185. const nodeIndex = (skillNodeData.group - 1) * 3 + skillNodeData.skillIndex;
  186. // 只允许按顺序解锁,必须是下一个待解锁的节点
  187. if (nodeIndex !== this.currentUnlockIndex + 1) {
  188. console.log(`必须按顺序解锁技能,当前可解锁索引: ${this.currentUnlockIndex + 1}`);
  189. return;
  190. }
  191. // 如果已经点亮,则不处理
  192. if (skillNodeData.isUnlocked) {
  193. console.log(`技能已点亮: ${this.getSkillTypeString(skillNodeData.skillIndex)} Group ${skillNodeData.group}`);
  194. return;
  195. }
  196. // 获取技能类型和效果百分比
  197. const skillInfo = this.getSkillInfo(skillNodeData);
  198. if (!skillInfo) {
  199. console.error('无法获取技能信息');
  200. return;
  201. }
  202. // 检查玩家是否有足够的钻石
  203. const currentDiamonds = this.saveDataManager ? this.saveDataManager.getDiamonds() : 0;
  204. if (currentDiamonds < skillNodeData.cost) {
  205. console.log(`钻石不足Need: ${skillNodeData.cost}, Have: ${currentDiamonds}`);
  206. return;
  207. }
  208. // 验证是否可以解锁(由 SkillNodeGenerator 负责验证)
  209. // 这里已经通过 nodeIndex === this.currentUnlockIndex + 1 验证了顺序
  210. if (!this.skillManager) {
  211. console.error('SkillManager not initialized');
  212. return;
  213. }
  214. // 将解锁数据传递给 PersistentSkillManager
  215. const unlockResult = this.skillManager.unlockSkill(skillInfo.skillType, skillNodeData.group, skillInfo.effectPercent);
  216. if (!unlockResult.success) {
  217. // 这里主要处理技能类型无效或已解锁的情况
  218. console.log(unlockResult.message);
  219. return;
  220. }
  221. // 扣除钻石
  222. if (this.saveDataManager && !this.saveDataManager.spendDiamonds(skillNodeData.cost)) {
  223. return;
  224. }
  225. // 更新解锁索引
  226. this.currentUnlockIndex = nodeIndex;
  227. // 更新UI状态
  228. skillNodeData.isUnlocked = true;
  229. this.updateSkillNodeVisual(skillNodeData);
  230. this.updateProgressBar();
  231. this.updateDiamondUI();
  232. // 输出技能解锁信息,包含数组索引
  233. console.log(`技能已解锁: ${this.getSkillTypeString(skillNodeData.skillIndex)} Group ${skillNodeData.group}, 索引[${nodeIndex}/${this.skillNodes.length-1}]`);
  234. // 输出已解锁技能的数组信息
  235. this.logUnlockedSkills();
  236. // 滚动到最新点亮的技能节点
  237. this.scheduleOnce(() => {
  238. this.scrollToLatestUnlockedSkill();
  239. }, 0.1);
  240. const remainingDiamonds = this.saveDataManager ? this.saveDataManager.getDiamonds() : 0;
  241. console.log(`${unlockResult.message}, Remaining diamonds: ${remainingDiamonds}`);
  242. }
  243. /**
  244. * 更新技能节点的视觉效果
  245. * 使用本地的 isUnlocked 状态来更新节点视觉效果
  246. */
  247. private updateSkillNodeVisual(skillNodeData: SkillNodeData) {
  248. if (skillNodeData.isUnlocked) {
  249. // 点亮状态:使用an000.png和tb001.png
  250. if (this.backgroundUnlocked) {
  251. skillNodeData.backgroundSprite.spriteFrame = this.backgroundUnlocked;
  252. }
  253. if (this.iconUnlocked) {
  254. skillNodeData.iconSprite.spriteFrame = this.iconUnlocked;
  255. }
  256. } else {
  257. // 未点亮状态:使用an001.png和tb002.png
  258. if (this.backgroundNormal) {
  259. skillNodeData.backgroundSprite.spriteFrame = this.backgroundNormal;
  260. }
  261. if (this.iconNormal) {
  262. skillNodeData.iconSprite.spriteFrame = this.iconNormal;
  263. }
  264. }
  265. }
  266. /**
  267. * 获取当前钻石数量
  268. */
  269. public getDiamonds(): number {
  270. return this.saveDataManager ? this.saveDataManager.getDiamonds() : 0;
  271. }
  272. /**
  273. * 设置钻石数量(用于测试或外部系统集成)
  274. */
  275. public setDiamonds(amount: number) {
  276. if (this.saveDataManager) {
  277. const playerData = this.saveDataManager.getPlayerData();
  278. if (playerData) {
  279. playerData.diamonds = amount;
  280. this.saveDataManager.savePlayerData();
  281. this.updateDiamondUI();
  282. }
  283. }
  284. }
  285. /**
  286. * 更新UI显示的钻石数量
  287. */
  288. private updateDiamondUI() {
  289. // 使用装饰器属性更新主界面的钻石显示
  290. if (this.diamondNumberNode) {
  291. const label = this.diamondNumberNode.getComponent(Label);
  292. if (label && this.saveDataManager) {
  293. const diamonds = this.saveDataManager.getDiamonds();
  294. label.string = diamonds >= 1000000 ? (diamonds / 1e6).toFixed(1) + 'M' :
  295. diamonds >= 1000 ? (diamonds / 1e3).toFixed(1) + 'K' :
  296. diamonds.toString();
  297. }
  298. } else {
  299. console.warn('Diamond number node not assigned in inspector');
  300. }
  301. }
  302. /**
  303. * 加载已解锁的技能状态
  304. */
  305. private loadUnlockedSkills() {
  306. if (!this.skillManager) {
  307. console.warn('SkillManager not initialized');
  308. return;
  309. }
  310. // 遍历所有技能节点,按顺序检查解锁状态
  311. let lastUnlockedIndex = -1;
  312. for (let i = 0; i < this.skillNodes.length; i++) {
  313. const skillNodeData = this.skillNodes[i];
  314. const skillInfo = this.getSkillInfo(skillNodeData);
  315. if (!skillInfo) continue;
  316. // 检查该技能是否已解锁
  317. const isUnlocked = this.skillManager.isSkillUnlocked(skillInfo.skillType, skillNodeData.group);
  318. if (isUnlocked) {
  319. // 更新本地解锁状态
  320. skillNodeData.isUnlocked = true;
  321. this.updateSkillNodeVisual(skillNodeData);
  322. lastUnlockedIndex = i;
  323. } else {
  324. // 一旦遇到未解锁的技能,就停止检查
  325. skillNodeData.isUnlocked = false;
  326. break;
  327. }
  328. }
  329. // 更新当前解锁索引
  330. this.currentUnlockIndex = lastUnlockedIndex;
  331. // 更新进度条
  332. this.updateProgressBar();
  333. console.log(`已加载技能解锁状态,当前解锁索引: ${this.currentUnlockIndex}/${this.skillNodes.length-1}`);
  334. // 输出已解锁技能的数组信息
  335. this.logUnlockedSkills();
  336. }
  337. /**
  338. * 重置所有技能节点状态
  339. */
  340. public resetAllSkills() {
  341. // 通过技能管理器重置技能数据
  342. if (this.skillManager) {
  343. this.skillManager.resetAllSkills();
  344. }
  345. // 重置UI状态和本地解锁状态
  346. this.skillNodes.forEach(skillNodeData => {
  347. // 重置本地解锁状态
  348. skillNodeData.isUnlocked = false;
  349. // 通过 skillManager 重置技能状态
  350. if (this.skillManager) {
  351. const skillInfo = this.getSkillInfo(skillNodeData);
  352. if (skillInfo) {
  353. this.skillManager.unlockSkill(skillInfo.skillType, skillNodeData.group, 0); // 将效果值设为0来重置技能
  354. }
  355. }
  356. this.updateSkillNodeVisual(skillNodeData);
  357. });
  358. // 重置当前解锁索引
  359. this.currentUnlockIndex = -1;
  360. // 更新进度条
  361. this.updateProgressBar();
  362. console.log('All skills have been reset');
  363. }
  364. /**
  365. * 清除技能点亮状态存档(包括持久化数据和UI状态)
  366. */
  367. public clearSkillSaveData() {
  368. // 清除持久化技能数据
  369. if (this.skillManager) {
  370. this.skillManager.resetAllSkills();
  371. }
  372. // 重置UI状态和本地解锁状态
  373. for (const skillNodeData of this.skillNodes) {
  374. // 重置本地解锁状态
  375. skillNodeData.isUnlocked = false;
  376. // 通过 skillManager 重置技能状态
  377. const skillInfo = this.getSkillInfo(skillNodeData);
  378. if (skillInfo && this.skillManager) {
  379. this.skillManager.unlockSkill(skillInfo.skillType, skillNodeData.group, 0);
  380. }
  381. this.updateSkillNodeVisual(skillNodeData);
  382. }
  383. // 重置当前解锁索引
  384. this.currentUnlockIndex = -1;
  385. // 更新进度条
  386. this.updateProgressBar();
  387. // 更新钻石UI显示
  388. this.updateDiamondUI();
  389. }
  390. /**
  391. * 更新进度条
  392. */
  393. private updateProgressBar() {
  394. if (!this.progressBar) {
  395. console.warn('ProgressBar not found');
  396. return;
  397. }
  398. // 使用当前解锁索引计算进度
  399. const totalNodes = this.skillNodes.length;
  400. const currentProgress = totalNodes > 0 ? (this.currentUnlockIndex + 1) / totalNodes : 0;
  401. // 更新进度条
  402. this.progressBar.progress = currentProgress;
  403. // 通知连接线组件更新进度
  404. this.updateConnectionLineProgress(currentProgress);
  405. // 输出当前解锁进度信息
  406. if (this.currentUnlockIndex >= 0) {
  407. console.log(`技能解锁进度: ${this.currentUnlockIndex + 1}/${totalNodes} (${Math.round(currentProgress * 100)}%)`);
  408. } else {
  409. console.log(`技能解锁进度: 0/${totalNodes} (0%)`);
  410. }
  411. }
  412. /**
  413. * 更新连接线进度
  414. */
  415. private updateConnectionLineProgress(progress: number) {
  416. // 查找连接线组件
  417. const connectionLine = this.node.parent?.parent?.getChildByName('ConnectionLine');
  418. if (connectionLine) {
  419. const connectionLineComp = connectionLine.getComponent('SkillConnectionLine') as any;
  420. if (connectionLineComp && typeof connectionLineComp.updateProgress === 'function') {
  421. connectionLineComp.updateProgress(progress);
  422. }
  423. }
  424. }
  425. /**
  426. * 滚动到最新解锁的技能节点
  427. * 使用本地的 isUnlocked 状态来查找最新解锁的技能节点
  428. */
  429. private scrollToLatestUnlockedSkill() {
  430. // 找到最新解锁技能的索引
  431. let latestUnlockedIndex = -1;
  432. for (let i = 0; i < this.skillNodes.length; i++) {
  433. const skillNode = this.skillNodes[i];
  434. // 使用本地的 isUnlocked 状态来判断节点是否解锁
  435. if (skillNode.isUnlocked) {
  436. latestUnlockedIndex = i;
  437. }
  438. }
  439. console.log('滚动到最新的未解锁技能节点或最底部');
  440. // 延迟一帧执行,确保节点布局完成
  441. this.scheduleOnce(() => {
  442. // 使用ScrollView的百分比滚动方法
  443. if (this.scrollView) {
  444. if (latestUnlockedIndex >= 0) {
  445. // 根据最新解锁技能的索引计算滚动比例
  446. const totalNodes = this.skillNodes.length;
  447. const scrollProgress = latestUnlockedIndex / (totalNodes - 1);
  448. // 使用ScrollView的scrollToPercentVertical方法设置滚动位置
  449. // 参数:百分比(0-1), 滚动时间(秒), 是否衰减
  450. this.scrollView.scrollToPercentVertical(scrollProgress, 0.3, true);
  451. console.log(`滚动到索引 ${latestUnlockedIndex}/${totalNodes - 1}, 滚动进度: ${scrollProgress.toFixed(2)}`);
  452. } else {
  453. // 如果没有解锁技能,滚动到顶部
  454. this.scrollView.scrollToPercentVertical(0, 0.3, true);
  455. console.log('滚动到顶部');
  456. }
  457. } else {
  458. console.warn('ScrollView not assigned in inspector');
  459. }
  460. }, 0);
  461. }
  462. /**
  463. * 获取最新解锁的节点位置
  464. * @returns 最新解锁节点的Y坐标位置,如果没有解锁节点则返回0
  465. */
  466. public getLatestUnlockedNodePosition(): number {
  467. // 找到最新点亮的技能节点(如果有)
  468. let latestUnlockedNode: SkillNodeData | null = null;
  469. let maxIndex = -1;
  470. for (const skillNode of this.skillNodes) {
  471. const skillInfo = this.getSkillInfo(skillNode);
  472. if (skillInfo && this.skillManager?.isSkillUnlocked(skillInfo.skillType, skillNode.group)) {
  473. const totalIndex = (skillNode.group - 1) * 3 + skillNode.skillIndex;
  474. if (totalIndex > maxIndex) {
  475. maxIndex = totalIndex;
  476. latestUnlockedNode = skillNode;
  477. }
  478. }
  479. }
  480. if (latestUnlockedNode) {
  481. // 返回节点的Y坐标位置(负值)
  482. return -latestUnlockedNode.node.position.y;
  483. }
  484. return 0;
  485. }
  486. /**
  487. * 获取当前解锁进度
  488. * @returns 解锁进度(0-1之间)
  489. */
  490. public getUnlockProgress(): number {
  491. const unlockedCount = this.skillNodes.filter(node => {
  492. // 使用本地的 isUnlocked 状态来判断节点是否解锁
  493. return node.isUnlocked;
  494. }).length;
  495. const totalNodes = this.skillNodes.length;
  496. return totalNodes > 0 ? unlockedCount / totalNodes : 0;
  497. }
  498. /**
  499. * 输出已解锁技能的数组信息
  500. * 使用本地的 isUnlocked 状态来记录解锁的技能
  501. */
  502. private logUnlockedSkills(): void {
  503. // 创建一个表示所有技能节点状态的数组
  504. const skillStatusArray = this.skillNodes.map((node, index) => {
  505. return {
  506. index,
  507. group: node.group,
  508. skillType: this.getSkillTypeString(node.skillIndex),
  509. isUnlocked: node.isUnlocked // 使用本地的 isUnlocked 状态
  510. };
  511. });
  512. // 输出已解锁的技能节点
  513. console.log('已解锁技能节点:');
  514. const unlockedNodes = skillStatusArray.filter(node => node.isUnlocked);
  515. unlockedNodes.forEach(node => {
  516. console.log(`[${node.index}] ${node.skillType} Group ${node.group}`);
  517. });
  518. // 输出下一个待解锁的节点
  519. const nextNode = skillStatusArray.find(node => !node.isUnlocked);
  520. if (nextNode) {
  521. console.log(`下一个待解锁节点: [${nextNode.index}] ${nextNode.skillType} Group ${nextNode.group}`);
  522. } else {
  523. console.log('所有技能节点已解锁!');
  524. }
  525. // 输出解锁进度
  526. console.log(`解锁进度: ${unlockedNodes.length}/${skillStatusArray.length} (${Math.round(this.getUnlockProgress() * 100)}%)`);
  527. }
  528. /**
  529. * 从技能节点数据中提取技能信息
  530. * @param skillNodeData 技能节点数据
  531. * @returns 技能信息 {skillType: string, effectPercent: number}
  532. */
  533. private getSkillInfo(skillNodeData: SkillNodeData): {skillType: string, effectPercent: number} | null {
  534. const nameLabel = skillNodeData.node.getChildByName('NameLabel');
  535. if (!nameLabel) {
  536. console.error('NameLabel not found in skill node');
  537. return null;
  538. }
  539. const labelComponent = nameLabel.getComponent(Label);
  540. if (!labelComponent) {
  541. console.error('Label component not found in NameLabel');
  542. return null;
  543. }
  544. const skillName = labelComponent.string;
  545. // 确定技能类型
  546. let skillType: string;
  547. if (skillName.includes('Crit Damage')) {
  548. skillType = 'crit_damage';
  549. } else if (skillName.includes('Damage')) {
  550. skillType = 'damage';
  551. } else if (skillName.includes('Ball Speed')) {
  552. skillType = 'ball_speed';
  553. } else {
  554. console.error('Unknown skill type in skill name:', skillName);
  555. return null;
  556. }
  557. // 从技能名称中提取效果百分比
  558. const match = skillName.match(/\+(\d+)%/);
  559. if (!match) {
  560. console.error('Could not extract effect percent from skill name:', skillName);
  561. return null;
  562. }
  563. const effectPercent = parseInt(match[1]);
  564. return {skillType, effectPercent};
  565. }
  566. /**
  567. * 根据技能索引获取技能类型字符串
  568. * @param skillIndex 技能索引 (0: Damage, 1: Ball Speed, 2: Crit Damage)
  569. * @returns 技能类型字符串
  570. */
  571. private getSkillTypeString(skillIndex: number): string {
  572. switch (skillIndex) {
  573. case 0: return 'Damage';
  574. case 1: return 'Ball Speed';
  575. case 2: return 'Crit Damage';
  576. default: return '未知技能';
  577. }
  578. }
  579. }