SharedEmitSoundSystem.cs 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. using Content.Shared.Audio;
  2. using Content.Shared.Hands;
  3. using Content.Shared.Interaction;
  4. using Content.Shared.Interaction.Events;
  5. using Content.Shared.Maps;
  6. using Content.Shared.Mobs;
  7. using Content.Shared.Popups;
  8. using Content.Shared.Sound.Components;
  9. using Content.Shared.Throwing;
  10. using Content.Shared.UserInterface;
  11. using Content.Shared.Whitelist;
  12. using JetBrains.Annotations;
  13. using Robust.Shared.Audio;
  14. using Robust.Shared.Audio.Systems;
  15. using Robust.Shared.Map;
  16. using Robust.Shared.Map.Components;
  17. using Robust.Shared.Network;
  18. using Robust.Shared.Physics.Components;
  19. using Robust.Shared.Physics.Events;
  20. using Robust.Shared.Random;
  21. using Robust.Shared.Timing;
  22. namespace Content.Shared.Sound;
  23. /// <summary>
  24. /// Will play a sound on various events if the affected entity has a component derived from BaseEmitSoundComponent
  25. /// </summary>
  26. [UsedImplicitly]
  27. public abstract class SharedEmitSoundSystem : EntitySystem
  28. {
  29. [Dependency] protected readonly IGameTiming Timing = default!;
  30. [Dependency] private readonly INetManager _netMan = default!;
  31. [Dependency] private readonly ITileDefinitionManager _tileDefMan = default!;
  32. [Dependency] protected readonly IRobustRandom Random = default!;
  33. [Dependency] private readonly SharedAmbientSoundSystem _ambient = default!;
  34. [Dependency] private readonly SharedAudioSystem _audioSystem = default!;
  35. [Dependency] protected readonly SharedPopupSystem Popup = default!;
  36. [Dependency] private readonly SharedMapSystem _map = default!;
  37. [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
  38. public override void Initialize()
  39. {
  40. base.Initialize();
  41. SubscribeLocalEvent<EmitSoundOnSpawnComponent, MapInitEvent>(OnEmitSpawnOnInit);
  42. SubscribeLocalEvent<EmitSoundOnLandComponent, LandEvent>(OnEmitSoundOnLand);
  43. SubscribeLocalEvent<EmitSoundOnUseComponent, UseInHandEvent>(OnEmitSoundOnUseInHand);
  44. SubscribeLocalEvent<EmitSoundOnThrowComponent, ThrownEvent>(OnEmitSoundOnThrown);
  45. SubscribeLocalEvent<EmitSoundOnActivateComponent, ActivateInWorldEvent>(OnEmitSoundOnActivateInWorld);
  46. SubscribeLocalEvent<EmitSoundOnPickupComponent, GotEquippedHandEvent>(OnEmitSoundOnPickup);
  47. SubscribeLocalEvent<EmitSoundOnDropComponent, DroppedEvent>(OnEmitSoundOnDrop);
  48. SubscribeLocalEvent<EmitSoundOnInteractUsingComponent, InteractUsingEvent>(OnEmitSoundOnInteractUsing);
  49. SubscribeLocalEvent<EmitSoundOnUIOpenComponent, AfterActivatableUIOpenEvent>(HandleEmitSoundOnUIOpen);
  50. SubscribeLocalEvent<EmitSoundOnCollideComponent, StartCollideEvent>(OnEmitSoundOnCollide);
  51. SubscribeLocalEvent<SoundWhileAliveComponent, MobStateChangedEvent>(OnMobState);
  52. }
  53. private void HandleEmitSoundOnUIOpen(EntityUid uid, EmitSoundOnUIOpenComponent component, AfterActivatableUIOpenEvent args)
  54. {
  55. if (_whitelistSystem.IsBlacklistFail(component.Blacklist, args.User))
  56. {
  57. TryEmitSound(uid, component, args.User);
  58. }
  59. }
  60. private void OnMobState(Entity<SoundWhileAliveComponent> entity, ref MobStateChangedEvent args)
  61. {
  62. // Disable this component rather than removing it because it can be brought back to life.
  63. if (TryComp<SpamEmitSoundComponent>(entity, out var comp))
  64. {
  65. comp.Enabled = args.NewMobState == MobState.Alive;
  66. Dirty(entity.Owner, comp);
  67. }
  68. _ambient.SetAmbience(entity.Owner, args.NewMobState != MobState.Dead);
  69. }
  70. private void OnEmitSpawnOnInit(EntityUid uid, EmitSoundOnSpawnComponent component, MapInitEvent args)
  71. {
  72. TryEmitSound(uid, component, predict: false);
  73. }
  74. private void OnEmitSoundOnLand(EntityUid uid, BaseEmitSoundComponent component, ref LandEvent args)
  75. {
  76. if (!args.PlaySound ||
  77. !TryComp(uid, out TransformComponent? xform) ||
  78. !TryComp<MapGridComponent>(xform.GridUid, out var grid))
  79. {
  80. return;
  81. }
  82. var tile = _map.GetTileRef(xform.GridUid.Value, grid, xform.Coordinates);
  83. // Handle maps being grids (we'll still emit the sound).
  84. if (xform.GridUid != xform.MapUid && tile.IsSpace(_tileDefMan))
  85. return;
  86. // hand throwing not predicted sadly
  87. TryEmitSound(uid, component, args.User, false);
  88. }
  89. private void OnEmitSoundOnUseInHand(EntityUid uid, EmitSoundOnUseComponent component, UseInHandEvent args)
  90. {
  91. // Intentionally not checking whether the interaction has already been handled.
  92. TryEmitSound(uid, component, args.User);
  93. if (component.Handle)
  94. args.Handled = true;
  95. }
  96. private void OnEmitSoundOnThrown(EntityUid uid, BaseEmitSoundComponent component, ref ThrownEvent args)
  97. {
  98. TryEmitSound(uid, component, args.User, false);
  99. }
  100. private void OnEmitSoundOnActivateInWorld(EntityUid uid, EmitSoundOnActivateComponent component, ActivateInWorldEvent args)
  101. {
  102. // Intentionally not checking whether the interaction has already been handled.
  103. TryEmitSound(uid, component, args.User);
  104. if (component.Handle)
  105. args.Handled = true;
  106. }
  107. private void OnEmitSoundOnPickup(EntityUid uid, EmitSoundOnPickupComponent component, GotEquippedHandEvent args)
  108. {
  109. TryEmitSound(uid, component, args.User);
  110. }
  111. private void OnEmitSoundOnDrop(EntityUid uid, EmitSoundOnDropComponent component, DroppedEvent args)
  112. {
  113. TryEmitSound(uid, component, args.User);
  114. }
  115. private void OnEmitSoundOnInteractUsing(Entity<EmitSoundOnInteractUsingComponent> ent, ref InteractUsingEvent args)
  116. {
  117. if (_whitelistSystem.IsWhitelistPass(ent.Comp.Whitelist, args.Used))
  118. {
  119. TryEmitSound(ent, ent.Comp, args.User);
  120. }
  121. }
  122. protected void TryEmitSound(EntityUid uid, BaseEmitSoundComponent component, EntityUid? user=null, bool predict=true)
  123. {
  124. if (component.Sound == null)
  125. return;
  126. if (component.Positional)
  127. {
  128. var coords = Transform(uid).Coordinates;
  129. if (predict)
  130. _audioSystem.PlayPredicted(component.Sound, coords, user);
  131. else if (_netMan.IsServer)
  132. // don't predict sounds that client couldn't have played already
  133. _audioSystem.PlayPvs(component.Sound, coords);
  134. }
  135. else
  136. {
  137. if (predict)
  138. _audioSystem.PlayPredicted(component.Sound, uid, user);
  139. else if (_netMan.IsServer)
  140. // don't predict sounds that client couldn't have played already
  141. _audioSystem.PlayPvs(component.Sound, uid);
  142. }
  143. }
  144. private void OnEmitSoundOnCollide(EntityUid uid, EmitSoundOnCollideComponent component, ref StartCollideEvent args)
  145. {
  146. if (!args.OurFixture.Hard ||
  147. !args.OtherFixture.Hard ||
  148. !TryComp<PhysicsComponent>(uid, out var physics) ||
  149. physics.LinearVelocity.Length() < component.MinimumVelocity ||
  150. Timing.CurTime < component.NextSound ||
  151. MetaData(uid).EntityPaused)
  152. {
  153. return;
  154. }
  155. const float MaxVolumeVelocity = 10f;
  156. const float MinVolume = -10f;
  157. const float MaxVolume = 2f;
  158. var fraction = MathF.Min(1f, (physics.LinearVelocity.Length() - component.MinimumVelocity) / MaxVolumeVelocity);
  159. var volume = MinVolume + (MaxVolume - MinVolume) * fraction;
  160. component.NextSound = Timing.CurTime + EmitSoundOnCollideComponent.CollideCooldown;
  161. var sound = component.Sound;
  162. if (_netMan.IsServer && sound != null)
  163. {
  164. _audioSystem.PlayPvs(_audioSystem.ResolveSound(sound), uid, AudioParams.Default.WithVolume(volume));
  165. }
  166. }
  167. public virtual void SetEnabled(Entity<SpamEmitSoundComponent?> entity, bool enabled)
  168. {
  169. }
  170. }