Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Loud Speakers #155

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
using Robust.Shared.Audio;

namespace Content.Server.SimpleStation14.LoudSpeakers;

[RegisterComponent]
public sealed class LoudSpeakerComponent : Component
{
public IPlayingAudioStream? CurrentPlayingSound;

public TimeSpan NextPlayTime = TimeSpan.Zero;

/// <summary>
/// The port to look for a signal on.
/// </summary>
public string PlaySoundPort = "Trigger";

/// <summary>
/// Whether or not this loudspeaker has ports.
/// </summary>
[DataField("ports")]
public bool Ports = true;

/// <summary>
/// The name of the container the payload is in.
/// This is specified in the construction graph.
/// </summary>
[DataField("containerSlot")]
public string ContainerSlot = "payload";

/// <summary>
/// Can this loudspeaker be triggered by interacting with it?
/// </summary>
[DataField("triggerOnInteract")]
public bool TriggerOnInteract = false;

/// <summary>
/// Should this loudspeaker interrupt already playing sounds if triggered?
/// If false, the sounds will overlap.
/// </summary>
/// <remarks>
/// Warning: If this is false, the speaker will not clean up after itself properly.
/// Since it only saves one sound at a time Use with caution.
/// </remarks>
[DataField("interrupt")]
public bool Interrupt = true;

/// <summary>
/// Cool down time between playing sounds.
/// </summary>
[DataField("cooldown")]
public TimeSpan Cooldown = TimeSpan.FromSeconds(4);

/// <summary>
/// The sound to play if no other sound is specified.
/// </summary>
[DataField("defaultSound")]
public SoundSpecifier DefaultSound = new SoundPathSpecifier("/Audio/SimpleStation14/Effects/metaldink.ogg");

/// <summary>
/// Default variance to be used, if no other variance is specified.
/// Is still subject to <see cref="VarianceMod"/>.
/// </summary>
[DataField("defaultVariance")]
public float DefaultVariance = 0.125f;

/// <summary>
/// The amount to multiply the volume by.
/// </summary>
[DataField("volumeMod")]
public float VolumeMod = 3.5f;

/// <summary>
/// The amount to multiply the range by.
/// </summary>
[DataField("rangeMod")]
public float RangeMod = 3.5f;

/// <summary>
/// The amount to multiply the rolloff by.
/// </summary>
[DataField("rolloffMod")]
public float RolloffMod = 0.3f;

/// <summary>
/// Amount to multiply the variance by, if the sound has one.
/// If the sound has a variance of 0, default variance is used.
/// </summary>
[DataField("varianceMod")]
public float VarianceMod = 1.5f;
}
133 changes: 133 additions & 0 deletions Content.Server/SimpleStation14/Loudspeakers/LoudSpeakerSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
using Content.Server.MachineLinking.Events;
using Content.Server.MachineLinking.System;
using Content.Server.Power.Components;
using Robust.Shared.Audio;
using Robust.Shared.Player;
using Robust.Shared.Containers;
using Content.Server.Sound.Components;
using Content.Shared.Sound.Components;
using Content.Shared.Interaction;
using Robust.Shared.Timing;

namespace Content.Server.SimpleStation14.LoudSpeakers;

public sealed class DoorSignalControlSystem : EntitySystem
{
[Dependency] private readonly SignalLinkerSystem _signal = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly IGameTiming _timing = default!;

public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<LoudSpeakerComponent, ComponentShutdown>(OnShutdown);

SubscribeLocalEvent<LoudSpeakerComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<LoudSpeakerComponent, SignalReceivedEvent>(OnSignalReceived);

SubscribeLocalEvent<LoudSpeakerComponent, InteractHandEvent>(OnInteractHand);
}

private void OnShutdown(EntityUid uid, LoudSpeakerComponent component, ComponentShutdown args)
{
if (component.CurrentPlayingSound != null)
component.CurrentPlayingSound.Stop();
}

private void OnInit(EntityUid uid, LoudSpeakerComponent component, ComponentInit args)
{
if (component.Ports)
_signal.EnsureReceiverPorts(uid, component.PlaySoundPort);
}

/// <summary>
/// Tries to play a loudspeaker.
/// </summary>
/// <param name="uid">The Loudspeaker to play.</param>
/// <param name="component">The Loudspeaker component.</param>
/// <returns>True if the Loudspeaker was played, false otherwise.</returns>
public bool TryPlayLoudSpeaker(EntityUid uid, LoudSpeakerComponent? component = null)
{
if (!Resolve(uid, ref component))
return false;

if (component.NextPlayTime > _timing.CurTime)
return false;

if (TryComp<ApcPowerReceiverComponent>(uid, out var powerComp) && !powerComp.Powered)
return false;

PlayLoudSpeaker(uid, component, GetSpeakerSound(uid, component));

return true;
}

private void PlayLoudSpeaker(EntityUid uid, LoudSpeakerComponent component, SoundSpecifier sound)
{
var newParams = sound.Params
.WithVolume(sound.Params.Volume * component.VolumeMod)
.WithMaxDistance(sound.Params.MaxDistance * component.RangeMod)
.WithRolloffFactor(sound.Params.RolloffFactor * component.RolloffMod)
.WithVariation((sound.Params.Variation !> 0 ? component.DefaultVariance : sound.Params.Variation) * component.VarianceMod);

if (component.Interrupt && component.CurrentPlayingSound != null)
component.CurrentPlayingSound.Stop();

component.NextPlayTime = _timing.CurTime + component.Cooldown;

component.CurrentPlayingSound = _audio.Play(sound, Filter.Pvs(uid, component.RangeMod), uid, true, newParams);
}

private SoundSpecifier GetSpeakerSound(EntityUid uid, LoudSpeakerComponent component)
{
if (!TryComp<ContainerManagerComponent>(uid, out var containerManager) ||
!containerManager.TryGetContainer(component.ContainerSlot, out var container))
return component.DefaultSound;

if (container.ContainedEntities.Count == 0)
return component.DefaultSound;

var entity = container.ContainedEntities[0];

switch (entity)
{
case { } when TryComp<EmitSoundOnTriggerComponent>(entity, out var trigger) && trigger.Sound != null:
return trigger.Sound;

case { } when TryComp<EmitSoundOnActivateComponent>(entity, out var activate) && activate.Sound != null:
return activate.Sound;

case { } when TryComp<EmitSoundOnUseComponent>(entity, out var use) && use.Sound != null:
return use.Sound;

case { } when TryComp<EmitSoundOnDropComponent>(entity, out var drop) && drop.Sound != null:
return drop.Sound;

case { } when TryComp<EmitSoundOnLandComponent>(entity, out var land) && land.Sound != null:
return land.Sound;

default:
return component.DefaultSound;
}
}

private void OnSignalReceived(EntityUid uid, LoudSpeakerComponent component, SignalReceivedEvent args)
{
if (args.Port == component.PlaySoundPort)
{
TryPlayLoudSpeaker(uid, component);
}
}

private void OnInteractHand(EntityUid uid, LoudSpeakerComponent component, InteractHandEvent args)
{
if (!component.TriggerOnInteract)
return;

if (!TryPlayLoudSpeaker(uid, component))
return;

args.Handled = true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Robust.Shared.Serialization.Markdown.Mapping;
using Robust.Shared.Serialization.Markdown.Validation;
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
using Content.Shared.SimpleStation14.Construction.Steps; // Parkstation-LoudSpeakers

namespace Content.Shared.Construction.Steps
{
Expand Down Expand Up @@ -41,6 +42,13 @@ public sealed class ConstructionGraphStepTypeSerializer : ITypeReader<Constructi
return typeof(TemperatureConstructionGraphStep);
}

// Parkstation-LoudSpeakers-Start
if (node.Has("size") || node.Has("compBlacklist") || node.Has("tagBlacklist"))
{
return typeof(FilterConstructionGraphStep);
}
// Parkstation-LoudSpeakers-End

return null;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using Content.Shared.Examine;
using Content.Shared.Item;
using Content.Shared.Construction.Steps;
using System.Collections.Generic;
using Content.Shared.Tag;

namespace Content.Shared.SimpleStation14.Construction.Steps
{
[DataDefinition]
public sealed class FilterConstructionGraphStep : ArbitraryInsertConstructionGraphStep
{
[DataField("size")] public int Size { get; }
[DataField("compBlacklist")] public List<string> CompBlacklist { get; } = new List<string>();
[DataField("tagBlacklist")] public List<string> TagBlacklist { get; } = new List<string>();

public override bool EntityValid(EntityUid uid, IEntityManager entityManager, IComponentFactory compFactory)
{
if (!entityManager.TryGetComponent<ItemComponent>(uid, out var item) || item.Size > Size)
return false;

foreach (var component in CompBlacklist)
{
if (entityManager.HasComponent(uid, compFactory.GetComponent(component).GetType()))
return false;
}

var tagSystem = entityManager.EntitySysManager.GetEntitySystem<TagSystem>();

if (tagSystem.HasAnyTag(uid, TagBlacklist))
return false;

return true;
}

public override void DoExamine(ExaminedEvent examinedEvent)
{
examinedEvent.Message.AddMarkup(string.IsNullOrEmpty(Name)
? Loc.GetString(
"construction-insert-entity-below-size",
("size", Size))
: Loc.GetString(
"construction-insert-exact-entity",
("entityName", Name)));
}
}
}
9 changes: 9 additions & 0 deletions Resources/Audio/SimpleStation14/Effects/attributions.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
- files: ["metaldink.ogg"]
license: "CC-BY-SA-3.0"
copyright: "Recorded and edited by @Pspritechologist#9442"
source: "Parkstation COMMIT HERE"

- files: ["buzzer_one.ogg", "buzzer_two.ogg"]
license: "Pixabay Content License"
copyright: "By Pixabay"
source: "https://pixabay.com/sound-effects/buzz-buzz-95806/"
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Parkstation
# Shown when examining an in-construction object
construction-insert-entity-below-size = Next, insert an entity no larger than {$size}.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
- type: entity
id: LoudSpeakerElectronics
parent: BaseElectronics
name: loud speaker electronics
description: An electronics board used in loud speakers
components:
- type: Sprite
sprite: Objects/Misc/module.rsi
state: id_mod
- type: Tag
tags:
- DroneUsable
- LoudSpeakerElectronics
- type: ReverseEngineering
recipes:
- LoudSpeakerElectronics
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
- type: entity
abstract: true
id: SoundBoxBase
parent: BaseItem
name: sound box
description: A small box designed to play a specific audio. It has no way to play on its own.
components:
- type: Sprite
sprite: Objects/Misc/module.rsi
state: id_mod
- type: Tag
tags:
- SoundBox
- type: EmitSoundOnTrigger
sound: Buzzer

- type: entity
id: SoundBoxBuzzer
parent: SoundBoxBase
name: buzzer sound box
description: A sound box containing a small metal disc that vibrates when electricity is applied to it.
components:
- type: EmitSoundOnTrigger
sound: Buzzer
- type: Sprite
sprite: Objects/Misc/module.rsi
state: id_mod
Loading