RMCPenetratingProjectileSystem.cs 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. using Content.Shared.Physics;
  2. using Content.Shared.Projectiles;
  3. using Robust.Shared.Physics.Events;
  4. namespace Content.Shared._RMC14.Projectiles.Penetration;
  5. public sealed class RMCPenetratingProjectileSystem : EntitySystem
  6. {
  7. private const int HardCollisionGroup = (int)(CollisionGroup.HighImpassable | CollisionGroup.Impassable);
  8. [Dependency] private readonly SharedTransformSystem _transform = default!;
  9. public override void Initialize()
  10. {
  11. SubscribeLocalEvent<RMCPenetratingProjectileComponent, MapInitEvent>(OnMapInit);
  12. SubscribeLocalEvent<RMCPenetratingProjectileComponent, PreventCollideEvent>(OnPreventCollide);
  13. SubscribeLocalEvent<RMCPenetratingProjectileComponent, StartCollideEvent>(OnStartCollide, after: [typeof(SharedProjectileSystem)]);
  14. SubscribeLocalEvent<RMCPenetratingProjectileComponent, ProjectileHitEvent>(OnProjectileHit);
  15. SubscribeLocalEvent<RMCPenetratingProjectileComponent, AfterProjectileHitEvent>(OnAllowAdditionalHits);
  16. }
  17. /// <summary>
  18. /// Store the coordinates the projectile was shot from.
  19. /// </summary>
  20. private void OnMapInit(Entity<RMCPenetratingProjectileComponent> ent, ref MapInitEvent args)
  21. {
  22. ent.Comp.ShotFrom = _transform.GetMoverCoordinates(ent);
  23. Dirty(ent);
  24. }
  25. /// <summary>
  26. /// Prevent collision with an already hit entity.
  27. /// </summary>
  28. private void OnPreventCollide(Entity<RMCPenetratingProjectileComponent> ent, ref PreventCollideEvent args)
  29. {
  30. if (!ent.Comp.HitTargets.Contains(args.OtherEntity))
  31. return;
  32. args.Cancelled = true;
  33. }
  34. /// <summary>
  35. /// Add the hit target to a list of hit targets that won't be hit another time.
  36. /// </summary>
  37. private void OnProjectileHit(Entity<RMCPenetratingProjectileComponent> ent, ref ProjectileHitEvent args)
  38. {
  39. if (ent.Comp.HitTargets.Contains(args.Target))
  40. {
  41. args.Handled = true;
  42. return;
  43. }
  44. ent.Comp.HitTargets.Add(args.Target);
  45. Dirty(ent);
  46. }
  47. /// <summary>
  48. /// Reduce the projectile damage and range based on what kind of target the projectile is colliding with.
  49. /// </summary>
  50. private void OnStartCollide(Entity<RMCPenetratingProjectileComponent> ent, ref StartCollideEvent args)
  51. {
  52. if (!TryComp(ent, out ProjectileComponent? projectile) || ent.Comp.ShotFrom == null)
  53. return;
  54. var rangeLoss = ent.Comp.RangeLossPerHit;
  55. var damageLoss = ent.Comp.DamageMultiplierLossPerHit;
  56. // Apply damage and range loss multipliers depending on target hit.
  57. if ((args.OtherFixture.CollisionLayer & HardCollisionGroup) != 0)
  58. {
  59. // Thick Membranes have a lower multiplier.
  60. if (TryComp(args.OtherEntity, out OccluderComponent? occluder) &&
  61. !occluder.Enabled)
  62. {
  63. rangeLoss *= ent.Comp.ThickMembraneMultiplier;
  64. damageLoss *= ent.Comp.ThickMembraneMultiplier;
  65. }
  66. else
  67. {
  68. rangeLoss *= ent.Comp.WallMultiplier;
  69. damageLoss *= ent.Comp.WallMultiplier;
  70. }
  71. }
  72. ent.Comp.Range -= rangeLoss;
  73. Dirty(ent);
  74. projectile.Damage *= 1 - damageLoss;
  75. Dirty(ent, projectile);
  76. }
  77. /// <summary>
  78. /// Make sure additional hits are allowed if range is still above 0.
  79. /// </summary>
  80. private void OnAllowAdditionalHits(Entity<RMCPenetratingProjectileComponent> ent, ref AfterProjectileHitEvent args)
  81. {
  82. if (ent.Comp.ShotFrom == null)
  83. return;
  84. var distanceTravelled =
  85. (_transform.GetMoverCoordinates(ent).Position - ent.Comp.ShotFrom.Value.Position).Length();
  86. var range = ent.Comp.Range - distanceTravelled;
  87. ent.Comp.HitTargets.Add(args.Target);
  88. Dirty(ent);
  89. if (range < 0)
  90. return;
  91. args.Projectile.Comp.ProjectileSpent = false;
  92. Dirty(args.Projectile);
  93. }
  94. }
  95. /// <summary>
  96. /// Raised on a projectile after it has hit an entity.
  97. /// </summary>
  98. [ByRefEvent]
  99. public record struct AfterProjectileHitEvent(Entity<ProjectileComponent> Projectile, EntityUid Target);