ProjectileGrenadeSystem.cs 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. using Content.Server.Explosion.Components;
  2. using Content.Server.Weapons.Ranged.Systems;
  3. using Robust.Server.GameObjects;
  4. using Robust.Shared.Containers;
  5. using Robust.Shared.Map;
  6. using Robust.Shared.Random;
  7. namespace Content.Server.Explosion.EntitySystems;
  8. public sealed class ProjectileGrenadeSystem : EntitySystem
  9. {
  10. [Dependency] private readonly GunSystem _gun = default!;
  11. [Dependency] private readonly IRobustRandom _random = default!;
  12. [Dependency] private readonly SharedContainerSystem _container = default!;
  13. [Dependency] private readonly TransformSystem _transformSystem = default!;
  14. public override void Initialize()
  15. {
  16. base.Initialize();
  17. SubscribeLocalEvent<ProjectileGrenadeComponent, ComponentInit>(OnFragInit);
  18. SubscribeLocalEvent<ProjectileGrenadeComponent, ComponentStartup>(OnFragStartup);
  19. SubscribeLocalEvent<ProjectileGrenadeComponent, TriggerEvent>(OnFragTrigger);
  20. }
  21. private void OnFragInit(Entity<ProjectileGrenadeComponent> entity, ref ComponentInit args)
  22. {
  23. entity.Comp.Container = _container.EnsureContainer<Container>(entity.Owner, "cluster-payload");
  24. }
  25. /// <summary>
  26. /// Setting the unspawned count based on capacity so we know how many new entities to spawn
  27. /// </summary>
  28. private void OnFragStartup(Entity<ProjectileGrenadeComponent> entity, ref ComponentStartup args)
  29. {
  30. if (entity.Comp.FillPrototype == null)
  31. return;
  32. entity.Comp.UnspawnedCount = Math.Max(0, entity.Comp.Capacity - entity.Comp.Container.ContainedEntities.Count);
  33. }
  34. /// <summary>
  35. /// Can be triggered either by damage or the use in hand timer
  36. /// </summary>
  37. private void OnFragTrigger(Entity<ProjectileGrenadeComponent> entity, ref TriggerEvent args)
  38. {
  39. FragmentIntoProjectiles(entity.Owner, entity.Comp);
  40. args.Handled = true;
  41. }
  42. /// <summary>
  43. /// Spawns projectiles at the coordinates of the grenade upon triggering
  44. /// Can customize the angle and velocity the projectiles come out at
  45. /// </summary>
  46. private void FragmentIntoProjectiles(EntityUid uid, ProjectileGrenadeComponent component)
  47. {
  48. var grenadeCoord = _transformSystem.GetMapCoordinates(uid);
  49. var shootCount = 0;
  50. var totalCount = component.Container.ContainedEntities.Count + component.UnspawnedCount;
  51. var segmentAngle = 360 / totalCount;
  52. while (TrySpawnContents(grenadeCoord, component, out var contentUid))
  53. {
  54. Angle angle;
  55. if (component.RandomAngle)
  56. angle = _random.NextAngle();
  57. else
  58. {
  59. var angleMin = segmentAngle * shootCount;
  60. var angleMax = segmentAngle * (shootCount + 1);
  61. angle = Angle.FromDegrees(_random.Next(angleMin, angleMax));
  62. shootCount++;
  63. }
  64. // velocity is randomized to make the projectiles look
  65. // slightly uneven, doesn't really change much, but it looks better
  66. var direction = angle.ToVec().Normalized();
  67. var velocity = _random.NextVector2(component.MinVelocity, component.MaxVelocity);
  68. _gun.ShootProjectile(contentUid, direction, velocity, uid, null);
  69. }
  70. }
  71. /// <summary>
  72. /// Spawns one instance of the fill prototype or contained entity at the coordinate indicated
  73. /// </summary>
  74. private bool TrySpawnContents(MapCoordinates spawnCoordinates, ProjectileGrenadeComponent component, out EntityUid contentUid)
  75. {
  76. contentUid = default;
  77. if (component.UnspawnedCount > 0)
  78. {
  79. component.UnspawnedCount--;
  80. contentUid = Spawn(component.FillPrototype, spawnCoordinates);
  81. return true;
  82. }
  83. if (component.Container.ContainedEntities.Count > 0)
  84. {
  85. contentUid = component.Container.ContainedEntities[0];
  86. if (!_container.Remove(contentUid, component.Container))
  87. return false;
  88. return true;
  89. }
  90. return false;
  91. }
  92. }