BulletHitEffect.ts 3.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. import { _decorator, Component } from 'cc';
  2. import { HitEffectConfig, HitResult } from '../BulletTypes';
  3. const { ccclass } = _decorator;
  4. /**
  5. * 命中效果(可移植版):纯逻辑整合,不直接与敌人系统耦合
  6. */
  7. @ccclass('BCBulletHitEffect')
  8. export class BulletHitEffect extends Component {
  9. private effects: HitEffectConfig[] = [];
  10. private remainingPierce = 0;
  11. private remainingRicochet = 0;
  12. public init(effects: HitEffectConfig[]) {
  13. this.effects = [...(effects ?? [])].sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0));
  14. // 初始化穿透与弹射计数
  15. const pierce = this.effects.find(e => e.type === 'pierce_damage');
  16. const ricochet = this.effects.find(e => e.type === 'ricochet_damage');
  17. this.remainingPierce = Math.max(0, pierce?.pierceCount ?? 0);
  18. this.remainingRicochet = Math.max(0, ricochet?.ricochetCount ?? 0);
  19. }
  20. /**
  21. * 计算命中后结果(纯数据),由上层去应用到敌人/场景
  22. */
  23. public applyOnHit(): HitResult {
  24. if (!this.effects || this.effects.length === 0) return { didHit: true, damageApplied: 0 };
  25. let damage = 0;
  26. let ricochet = false;
  27. let ricochetAngle: number | undefined = undefined;
  28. let explosion = false;
  29. let explosionRadius: number | undefined = undefined;
  30. let spawnBurn = false;
  31. let burnPayload: HitResult['burn'] = null;
  32. for (const e of this.effects) {
  33. switch (e.type) {
  34. case 'normal_damage':
  35. damage += Math.max(0, e.damage ?? 0);
  36. break;
  37. case 'pierce_damage':
  38. damage += Math.max(0, e.damage ?? 0);
  39. // 穿透次数在命中后交由生命周期或外层减少
  40. break;
  41. case 'ricochet_damage':
  42. damage += Math.max(0, e.damage ?? 0);
  43. if (this.remainingRicochet > 0) {
  44. ricochet = true;
  45. ricochetAngle = e.ricochetAngle ?? ricochetAngle;
  46. }
  47. break;
  48. case 'explosion':
  49. damage += Math.max(0, e.damage ?? 0);
  50. explosion = true;
  51. explosionRadius = e.radius ?? explosionRadius;
  52. break;
  53. case 'ground_burn':
  54. // 地面燃烧不直接叠加伤害(由区域 DOT 管理)
  55. spawnBurn = true;
  56. burnPayload = {
  57. damagePerTick: Math.max(0, e.damage ?? 0),
  58. duration: Math.max(0, e.duration ?? 0),
  59. tickInterval: Math.max(0.05, e.tickInterval ?? 0.5)
  60. };
  61. break;
  62. }
  63. }
  64. const res: HitResult = {
  65. didHit: true,
  66. damageApplied: damage,
  67. ricochet,
  68. ricochetAngle,
  69. pierced: this.remainingPierce > 0,
  70. remainingPierce: this.remainingPierce,
  71. explosion,
  72. explosionRadius,
  73. spawnBurn,
  74. burn: burnPayload
  75. };
  76. return res;
  77. }
  78. /** 在外层确认穿透后调用,减少计数 */
  79. public consumePierce() {
  80. if (this.remainingPierce > 0) this.remainingPierce -= 1;
  81. }
  82. /** 在外层确认弹射后调用,减少计数 */
  83. public consumeRicochet() {
  84. if (this.remainingRicochet > 0) this.remainingRicochet -= 1;
  85. }
  86. }