use manual component state for BaseEmitSoundComponent (#35030)

* why

* cursed
This commit is contained in:
Milon
2025-03-30 15:41:11 +02:00
committed by GitHub
parent 47f8aefc25
commit 89e59b391d
13 changed files with 75 additions and 30 deletions

View File

@@ -1,4 +1,6 @@
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
namespace Content.Shared.Sound.Components; namespace Content.Shared.Sound.Components;
@@ -8,10 +10,9 @@ namespace Content.Shared.Sound.Components;
/// </summary> /// </summary>
public abstract partial class BaseEmitSoundComponent : Component public abstract partial class BaseEmitSoundComponent : Component
{ {
public static readonly AudioParams DefaultParams = AudioParams.Default.WithVolume(-2f); /// <summary>
/// The <see cref="SoundSpecifier"/> to play.
[AutoNetworkedField] /// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField(required: true)] [DataField(required: true)]
public SoundSpecifier? Sound; public SoundSpecifier? Sound;
@@ -22,3 +23,15 @@ public abstract partial class BaseEmitSoundComponent : Component
[DataField] [DataField]
public bool Positional; 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;
}

View File

@@ -17,6 +17,6 @@ public sealed partial class EmitSoundOnActivateComponent : BaseEmitSoundComponen
/// otherwise this might enable sound spamming, as use-delays are only initiated if the interaction was /// otherwise this might enable sound spamming, as use-delays are only initiated if the interaction was
/// handled. /// handled.
/// </remarks> /// </remarks>
[DataField("handle")] [DataField]
public bool Handle = true; public bool Handle = true;
} }

View File

@@ -11,13 +11,12 @@ public sealed partial class EmitSoundOnCollideComponent : BaseEmitSoundComponent
/// <summary> /// <summary>
/// Minimum velocity required for the sound to play. /// Minimum velocity required for the sound to play.
/// </summary> /// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField("minVelocity")] [DataField("minVelocity")]
public float MinimumVelocity = 3f; public float MinimumVelocity = 3f;
/// <summary> /// <summary>
/// To avoid sound spam add a cooldown to it. /// To avoid sound spam add a cooldown to it.
/// </summary> /// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField("nextSound", customTypeSerializer: typeof(TimeOffsetSerializer))] [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField]
[AutoPausedField]
public TimeSpan NextSound; public TimeSpan NextSound;
} }

View File

@@ -6,6 +6,4 @@ namespace Content.Shared.Sound.Components;
/// Simple sound emitter that emits sound on entity drop /// Simple sound emitter that emits sound on entity drop
/// </summary> /// </summary>
[RegisterComponent, NetworkedComponent] [RegisterComponent, NetworkedComponent]
public sealed partial class EmitSoundOnDropComponent : BaseEmitSoundComponent public sealed partial class EmitSoundOnDropComponent : BaseEmitSoundComponent;
{
}

View File

@@ -1,5 +1,4 @@
using Content.Shared.Whitelist; using Content.Shared.Whitelist;
using Robust.Shared.Prototypes;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
namespace Content.Shared.Sound.Components; namespace Content.Shared.Sound.Components;
@@ -10,6 +9,9 @@ namespace Content.Shared.Sound.Components;
[RegisterComponent, NetworkedComponent] [RegisterComponent, NetworkedComponent]
public sealed partial class EmitSoundOnInteractUsingComponent : BaseEmitSoundComponent public sealed partial class EmitSoundOnInteractUsingComponent : BaseEmitSoundComponent
{ {
/// <summary>
/// The <see cref="EntityWhitelist"/> for the entities that can use this item.
/// </summary>
[DataField(required: true)] [DataField(required: true)]
public EntityWhitelist Whitelist = new(); public EntityWhitelist Whitelist = new();
} }

View File

@@ -6,6 +6,4 @@ namespace Content.Shared.Sound.Components;
/// Simple sound emitter that emits sound on LandEvent /// Simple sound emitter that emits sound on LandEvent
/// </summary> /// </summary>
[RegisterComponent, NetworkedComponent] [RegisterComponent, NetworkedComponent]
public sealed partial class EmitSoundOnLandComponent : BaseEmitSoundComponent public sealed partial class EmitSoundOnLandComponent : BaseEmitSoundComponent;
{
}

View File

@@ -6,6 +6,4 @@ namespace Content.Shared.Sound.Components;
/// Simple sound emitter that emits sound on entity pickup /// Simple sound emitter that emits sound on entity pickup
/// </summary> /// </summary>
[RegisterComponent, NetworkedComponent] [RegisterComponent, NetworkedComponent]
public sealed partial class EmitSoundOnPickupComponent : BaseEmitSoundComponent public sealed partial class EmitSoundOnPickupComponent : BaseEmitSoundComponent;
{
}

View File

@@ -6,6 +6,4 @@ namespace Content.Shared.Sound.Components;
/// Simple sound emitter that emits sound on entity spawn. /// Simple sound emitter that emits sound on entity spawn.
/// </summary> /// </summary>
[RegisterComponent, NetworkedComponent] [RegisterComponent, NetworkedComponent]
public sealed partial class EmitSoundOnSpawnComponent : BaseEmitSoundComponent public sealed partial class EmitSoundOnSpawnComponent : BaseEmitSoundComponent;
{
}

View File

@@ -6,6 +6,4 @@ namespace Content.Shared.Sound.Components;
/// Simple sound emitter that emits sound on ThrowEvent /// Simple sound emitter that emits sound on ThrowEvent
/// </summary> /// </summary>
[RegisterComponent, NetworkedComponent] [RegisterComponent, NetworkedComponent]
public sealed partial class EmitSoundOnThrowComponent : BaseEmitSoundComponent public sealed partial class EmitSoundOnThrowComponent : BaseEmitSoundComponent;
{
}

View File

@@ -5,7 +5,7 @@ namespace Content.Shared.Sound.Components;
/// <summary> /// <summary>
/// Simple sound emitter that emits sound on UseInHand /// Simple sound emitter that emits sound on UseInHand
/// </summary> /// </summary>
[RegisterComponent] [RegisterComponent, NetworkedComponent]
public sealed partial class EmitSoundOnUseComponent : BaseEmitSoundComponent public sealed partial class EmitSoundOnUseComponent : BaseEmitSoundComponent
{ {
/// <summary> /// <summary>
@@ -17,6 +17,6 @@ public sealed partial class EmitSoundOnUseComponent : BaseEmitSoundComponent
/// otherwise this might enable sound spamming, as use-delays are only initiated if the interaction was /// otherwise this might enable sound spamming, as use-delays are only initiated if the interaction was
/// handled. /// handled.
/// </remarks> /// </remarks>
[DataField("handle")] [DataField]
public bool Handle = true; public bool Handle = true;
} }

View File

@@ -1,4 +1,5 @@
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
namespace Content.Shared.Sound.Components; namespace Content.Shared.Sound.Components;
@@ -12,7 +13,7 @@ public sealed partial class SpamEmitSoundComponent : BaseEmitSoundComponent
/// <summary> /// <summary>
/// The time at which the next sound will play. /// The time at which the next sound will play.
/// </summary> /// </summary>
[DataField, AutoPausedField, AutoNetworkedField] [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField, AutoNetworkedField]
public TimeSpan NextSound; public TimeSpan NextSound;
/// <summary> /// <summary>

View File

@@ -5,6 +5,4 @@ namespace Content.Shared.Sound.Components;
/// on the powered state of the entity. /// on the powered state of the entity.
/// </summary> /// </summary>
[RegisterComponent] [RegisterComponent]
public sealed partial class SpamEmitSoundRequirePowerComponent : Component public sealed partial class SpamEmitSoundRequirePowerComponent : Component;
{
}

View File

@@ -12,6 +12,7 @@ using Content.Shared.Whitelist;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
using Robust.Shared.GameStates;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Map.Components; using Robust.Shared.Map.Components;
using Robust.Shared.Network; using Robust.Shared.Network;
@@ -54,6 +55,47 @@ public abstract class SharedEmitSoundSystem : EntitySystem
SubscribeLocalEvent<EmitSoundOnCollideComponent, StartCollideEvent>(OnEmitSoundOnCollide); SubscribeLocalEvent<EmitSoundOnCollideComponent, StartCollideEvent>(OnEmitSoundOnCollide);
SubscribeLocalEvent<SoundWhileAliveComponent, MobStateChangedEvent>(OnMobState); 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) private void HandleEmitSoundOnUIOpen(EntityUid uid, EmitSoundOnUIOpenComponent component, AfterActivatableUIOpenEvent args)