using Robust.Shared.Audio;
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization;
namespace Content.Shared.Sound.Components;
/// </summary>
public abstract partial class BaseEmitSoundComponent : Component
{
- public static readonly AudioParams DefaultParams = AudioParams.Default.WithVolume(-2f);
-
- [AutoNetworkedField]
- [ViewVariables(VVAccess.ReadWrite)]
+ /// <summary>
+ /// The <see cref="SoundSpecifier"/> to play.
+ /// </summary>
[DataField(required: true)]
public SoundSpecifier? Sound;
[DataField]
public bool Positional;
}
+
+/// <summary>
+/// Represents the state of <see cref="BaseEmitSoundComponent"/>.
+/// </summary>
+/// <remarks>This is obviously very cursed, but since the BaseEmitSoundComponent is abstract, we cannot network it.
+/// AutoGenerateComponentState attribute won't work here, and since everything revolves around inheritance for some fucking reason,
+/// there's no better way of doing this.</remarks>
+[Serializable, NetSerializable]
+public struct EmitSoundComponentState(SoundSpecifier? sound) : IComponentState
+{
+ public SoundSpecifier? Sound { get; } = sound;
+}
/// otherwise this might enable sound spamming, as use-delays are only initiated if the interaction was
/// handled.
/// </remarks>
- [DataField("handle")]
+ [DataField]
public bool Handle = true;
}
/// <summary>
/// Minimum velocity required for the sound to play.
/// </summary>
- [ViewVariables(VVAccess.ReadWrite), DataField("minVelocity")]
+ [DataField("minVelocity")]
public float MinimumVelocity = 3f;
/// <summary>
/// To avoid sound spam add a cooldown to it.
/// </summary>
- [ViewVariables(VVAccess.ReadWrite), DataField("nextSound", customTypeSerializer: typeof(TimeOffsetSerializer))]
- [AutoPausedField]
+ [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField]
public TimeSpan NextSound;
}
/// Simple sound emitter that emits sound on entity drop
/// </summary>
[RegisterComponent, NetworkedComponent]
-public sealed partial class EmitSoundOnDropComponent : BaseEmitSoundComponent
-{
-}
+public sealed partial class EmitSoundOnDropComponent : BaseEmitSoundComponent;
using Content.Shared.Whitelist;
-using Robust.Shared.Prototypes;
using Robust.Shared.GameStates;
namespace Content.Shared.Sound.Components;
[RegisterComponent, NetworkedComponent]
public sealed partial class EmitSoundOnInteractUsingComponent : BaseEmitSoundComponent
{
+ /// <summary>
+ /// The <see cref="EntityWhitelist"/> for the entities that can use this item.
+ /// </summary>
[DataField(required: true)]
public EntityWhitelist Whitelist = new();
}
/// Simple sound emitter that emits sound on LandEvent
/// </summary>
[RegisterComponent, NetworkedComponent]
-public sealed partial class EmitSoundOnLandComponent : BaseEmitSoundComponent
-{
-}
+public sealed partial class EmitSoundOnLandComponent : BaseEmitSoundComponent;
/// Simple sound emitter that emits sound on entity pickup
/// </summary>
[RegisterComponent, NetworkedComponent]
-public sealed partial class EmitSoundOnPickupComponent : BaseEmitSoundComponent
-{
-}
+public sealed partial class EmitSoundOnPickupComponent : BaseEmitSoundComponent;
/// Simple sound emitter that emits sound on entity spawn.
/// </summary>
[RegisterComponent, NetworkedComponent]
-public sealed partial class EmitSoundOnSpawnComponent : BaseEmitSoundComponent
-{
-}
+public sealed partial class EmitSoundOnSpawnComponent : BaseEmitSoundComponent;
/// Simple sound emitter that emits sound on ThrowEvent
/// </summary>
[RegisterComponent, NetworkedComponent]
-public sealed partial class EmitSoundOnThrowComponent : BaseEmitSoundComponent
-{
-}
+public sealed partial class EmitSoundOnThrowComponent : BaseEmitSoundComponent;
/// <summary>
/// Simple sound emitter that emits sound on UseInHand
/// </summary>
-[RegisterComponent]
+[RegisterComponent, NetworkedComponent]
public sealed partial class EmitSoundOnUseComponent : BaseEmitSoundComponent
{
/// <summary>
/// otherwise this might enable sound spamming, as use-delays are only initiated if the interaction was
/// handled.
/// </remarks>
- [DataField("handle")]
+ [DataField]
public bool Handle = true;
}
using Robust.Shared.GameStates;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
namespace Content.Shared.Sound.Components;
/// <summary>
/// The time at which the next sound will play.
/// </summary>
- [DataField, AutoPausedField, AutoNetworkedField]
+ [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField, AutoNetworkedField]
public TimeSpan NextSound;
/// <summary>
/// on the powered state of the entity.
/// </summary>
[RegisterComponent]
-public sealed partial class SpamEmitSoundRequirePowerComponent : Component
-{
-}
+public sealed partial class SpamEmitSoundRequirePowerComponent : Component;
using JetBrains.Annotations;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
+using Robust.Shared.GameStates;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Network;
SubscribeLocalEvent<EmitSoundOnCollideComponent, StartCollideEvent>(OnEmitSoundOnCollide);
SubscribeLocalEvent<SoundWhileAliveComponent, MobStateChangedEvent>(OnMobState);
+
+ // We need to handle state manually here
+ // BaseEmitSoundComponent isn't registered so we have to subscribe to each one
+ // TODO: Make it use autonetworking instead of relying on inheritance
+ SubscribeEmitComponent<EmitSoundOnActivateComponent>();
+ SubscribeEmitComponent<EmitSoundOnCollideComponent>();
+ SubscribeEmitComponent<EmitSoundOnDropComponent>();
+ SubscribeEmitComponent<EmitSoundOnInteractUsingComponent>();
+ SubscribeEmitComponent<EmitSoundOnLandComponent>();
+ SubscribeEmitComponent<EmitSoundOnPickupComponent>();
+ SubscribeEmitComponent<EmitSoundOnSpawnComponent>();
+ SubscribeEmitComponent<EmitSoundOnThrowComponent>();
+ SubscribeEmitComponent<EmitSoundOnUIOpenComponent>();
+ SubscribeEmitComponent<EmitSoundOnUseComponent>();
+
+ // Helper method so it's a little less ugly
+ void SubscribeEmitComponent<T>() where T : BaseEmitSoundComponent
+ {
+ SubscribeLocalEvent<T, ComponentGetState>(GetBaseEmitState);
+ SubscribeLocalEvent<T, ComponentHandleState>(HandleBaseEmitState);
+ }
+ }
+
+ private static void GetBaseEmitState<T>(Entity<T> ent, ref ComponentGetState args) where T : BaseEmitSoundComponent
+ {
+ args.State = new EmitSoundComponentState(ent.Comp.Sound);
+ }
+
+ private static void HandleBaseEmitState<T>(Entity<T> ent, ref ComponentHandleState args) where T : BaseEmitSoundComponent
+ {
+ if (args.Current is not EmitSoundComponentState state)
+ return;
+
+ ent.Comp.Sound = state.Sound switch
+ {
+ SoundPathSpecifier pathSpec => new SoundPathSpecifier(pathSpec.Path, pathSpec.Params),
+ SoundCollectionSpecifier collectionSpec => collectionSpec.Collection != null
+ ? new SoundCollectionSpecifier(collectionSpec.Collection, collectionSpec.Params)
+ : null,
+ _ => null,
+ };
}
private void HandleEmitSoundOnUIOpen(EntityUid uid, EmitSoundOnUIOpenComponent component, AfterActivatableUIOpenEvent args)