From 6dbfbc52c05ce81c574d8e5498e87fa2bfbcb029 Mon Sep 17 00:00:00 2001
From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com>
Date: Tue, 11 Jul 2023 19:58:18 -0400
Subject: [PATCH] Good Intercoms (#17950)
* crystal anomaly
* Good intercoms
* fixes
* fix construction fail
* Revert "crystal anomaly"
This reverts commit 0d9e3f62ff82c79e72f882b9c7f4ca1b9c6e6dd8.
* migration
---
.../Radio/Ui/IntercomBoundUserInterface.cs | 58 +++++++
Content.Client/Radio/Ui/IntercomMenu.xaml | 31 ++++
Content.Client/Radio/Ui/IntercomMenu.xaml.cs | 55 +++++++
.../Components/RadioMicrophoneComponent.cs | 10 +-
.../Radio/Components/RadioSpeakerComponent.cs | 7 +
.../Radio/EntitySystems/RadioDeviceSystem.cs | 151 +++++++++++-------
.../Radio/EntitySystems/RadioSystem.cs | 2 +-
.../Radio/Components/IntercomComponent.cs | 23 +++
Content.Shared/Radio/RadioVisuals.cs | 6 +-
Content.Shared/Radio/SharedIntercom.cs | 59 +++++++
.../en-US/radio/components/intercom.ftl | 5 +
.../Xenoarchaeology/item_artifacts.yml | 2 +
.../Xenoarchaeology/structure_artifacts.yml | 2 +
.../Structures/Wallmounts/intercom.yml | 71 ++++----
.../Prototypes/Procedural/dungeon_configs.yml | 4 +-
.../Graphs/utilities/intercom.yml | 2 +-
.../XenoArch/Effects/utility_effects.yml | 8 +
.../Wallmounts/intercom.rsi/meta.json | 4 +
.../Wallmounts/intercom.rsi/speaker.png | Bin 0 -> 157 bytes
.../Wallmounts/intercom.rsi/unshaded.png | Bin 369 -> 328 bytes
Resources/migration.yml | 5 +-
21 files changed, 407 insertions(+), 98 deletions(-)
create mode 100644 Content.Client/Radio/Ui/IntercomBoundUserInterface.cs
create mode 100644 Content.Client/Radio/Ui/IntercomMenu.xaml
create mode 100644 Content.Client/Radio/Ui/IntercomMenu.xaml.cs
create mode 100644 Content.Shared/Radio/Components/IntercomComponent.cs
create mode 100644 Content.Shared/Radio/SharedIntercom.cs
create mode 100644 Resources/Locale/en-US/radio/components/intercom.ftl
create mode 100644 Resources/Textures/Structures/Wallmounts/intercom.rsi/speaker.png
diff --git a/Content.Client/Radio/Ui/IntercomBoundUserInterface.cs b/Content.Client/Radio/Ui/IntercomBoundUserInterface.cs
new file mode 100644
index 0000000000..abbb1d58ec
--- /dev/null
+++ b/Content.Client/Radio/Ui/IntercomBoundUserInterface.cs
@@ -0,0 +1,58 @@
+using Content.Shared.Radio;
+using JetBrains.Annotations;
+using Robust.Client.GameObjects;
+
+namespace Content.Client.Radio.Ui;
+
+[UsedImplicitly]
+public sealed class IntercomBoundUserInterface : BoundUserInterface
+{
+ [ViewVariables]
+ private IntercomMenu? _menu;
+
+ public IntercomBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
+ {
+
+ }
+
+ protected override void Open()
+ {
+ base.Open();
+
+ _menu = new();
+
+ _menu.OnMicPressed += enabled =>
+ {
+ SendMessage(new ToggleIntercomMicMessage(enabled));
+ };
+ _menu.OnSpeakerPressed += enabled =>
+ {
+ SendMessage(new ToggleIntercomSpeakerMessage(enabled));
+ };
+ _menu.OnChannelSelected += channel =>
+ {
+ SendMessage(new SelectIntercomChannelMessage(channel));
+ };
+
+ _menu.OnClose += Close;
+ _menu.OpenCentered();
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ base.Dispose(disposing);
+ if (!disposing)
+ return;
+ _menu?.Close();
+ }
+
+ protected override void UpdateState(BoundUserInterfaceState state)
+ {
+ base.UpdateState(state);
+
+ if (state is not IntercomBoundUIState msg)
+ return;
+
+ _menu?.Update(msg);
+ }
+}
diff --git a/Content.Client/Radio/Ui/IntercomMenu.xaml b/Content.Client/Radio/Ui/IntercomMenu.xaml
new file mode 100644
index 0000000000..aed4fc754e
--- /dev/null
+++ b/Content.Client/Radio/Ui/IntercomMenu.xaml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/Radio/Ui/IntercomMenu.xaml.cs b/Content.Client/Radio/Ui/IntercomMenu.xaml.cs
new file mode 100644
index 0000000000..8b4b38753c
--- /dev/null
+++ b/Content.Client/Radio/Ui/IntercomMenu.xaml.cs
@@ -0,0 +1,55 @@
+using Content.Client.UserInterface.Controls;
+using Content.Shared.Radio;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Prototypes;
+
+namespace Content.Client.Radio.Ui;
+
+[GenerateTypedNameReferences]
+public sealed partial class IntercomMenu : FancyWindow
+{
+ [Dependency] private readonly IPrototypeManager _prototype = default!;
+
+ public event Action? OnMicPressed;
+ public event Action? OnSpeakerPressed;
+ public event Action? OnChannelSelected;
+
+ private readonly List _channels = new();
+
+ public IntercomMenu()
+ {
+ RobustXamlLoader.Load(this);
+ IoCManager.InjectDependencies(this);
+
+ MicButton.OnPressed += args => OnMicPressed?.Invoke(args.Button.Pressed);
+ SpeakerButton.OnPressed += args => OnSpeakerPressed?.Invoke(args.Button.Pressed);
+ }
+
+ public void Update(IntercomBoundUIState state)
+ {
+ MicButton.Pressed = state.MicEnabled;
+ SpeakerButton.Pressed = state.SpeakerEnabled;
+
+ ChannelOptions.Clear();
+ _channels.Clear();
+ for (var i = 0; i < state.AvailableChannels.Count; i++)
+ {
+ var channel = state.AvailableChannels[i];
+ if (!_prototype.TryIndex(channel, out var prototype))
+ continue;
+
+ _channels.Add(channel);
+ ChannelOptions.AddItem(Loc.GetString(prototype.Name), i);
+
+ if (channel == state.SelectedChannel)
+ ChannelOptions.Select(i);
+ }
+ ChannelOptions.OnItemSelected += args =>
+ {
+ ChannelOptions.SelectId(args.Id);
+ OnChannelSelected?.Invoke(_channels[args.Id]);
+ };
+ }
+}
+
diff --git a/Content.Server/Radio/Components/RadioMicrophoneComponent.cs b/Content.Server/Radio/Components/RadioMicrophoneComponent.cs
index 5ab2484a9d..162eeb6110 100644
--- a/Content.Server/Radio/Components/RadioMicrophoneComponent.cs
+++ b/Content.Server/Radio/Components/RadioMicrophoneComponent.cs
@@ -17,9 +17,6 @@ public sealed class RadioMicrophoneComponent : Component
[DataField("broadcastChannel", customTypeSerializer: typeof(PrototypeIdSerializer))]
public string BroadcastChannel = SharedChatSystem.CommonChannel;
- [ViewVariables, DataField("supportedChannels", customTypeSerializer: typeof(PrototypeIdListSerializer))]
- public List? SupportedChannels;
-
[ViewVariables(VVAccess.ReadWrite)]
[DataField("listenRange")]
public int ListenRange = 4;
@@ -30,6 +27,13 @@ public sealed class RadioMicrophoneComponent : Component
[DataField("powerRequired")]
public bool PowerRequired = false;
+ ///
+ /// Whether or not interacting with this entity
+ /// toggles it on or off.
+ ///
+ [DataField("toggleOnInteract")]
+ public bool ToggleOnInteract = true;
+
///
/// Whether or not the speaker must have an
/// unobstructed path to the radio to speak
diff --git a/Content.Server/Radio/Components/RadioSpeakerComponent.cs b/Content.Server/Radio/Components/RadioSpeakerComponent.cs
index 22a7578eea..1b842fbd0d 100644
--- a/Content.Server/Radio/Components/RadioSpeakerComponent.cs
+++ b/Content.Server/Radio/Components/RadioSpeakerComponent.cs
@@ -12,6 +12,13 @@ namespace Content.Server.Radio.Components;
[Access(typeof(RadioDeviceSystem))]
public sealed class RadioSpeakerComponent : Component
{
+ ///
+ /// Whether or not interacting with this entity
+ /// toggles it on or off.
+ ///
+ [DataField("toggleOnInteract")]
+ public bool ToggleOnInteract = true;
+
[DataField("channels", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))]
public HashSet Channels = new () { SharedChatSystem.CommonChannel };
diff --git a/Content.Server/Radio/EntitySystems/RadioDeviceSystem.cs b/Content.Server/Radio/EntitySystems/RadioDeviceSystem.cs
index 22a7441ea9..6a6c644fbb 100644
--- a/Content.Server/Radio/EntitySystems/RadioDeviceSystem.cs
+++ b/Content.Server/Radio/EntitySystems/RadioDeviceSystem.cs
@@ -6,12 +6,13 @@ using Content.Server.Power.EntitySystems;
using Content.Server.Radio.Components;
using Content.Server.Speech;
using Content.Server.Speech.Components;
+using Content.Server.UserInterface;
+using Content.Shared.Chat;
using Content.Shared.Examine;
using Content.Shared.Interaction;
using Content.Shared.Radio;
-using Content.Shared.Verbs;
+using Content.Shared.Radio.Components;
using Robust.Server.GameObjects;
-using Robust.Shared.Player;
using Robust.Shared.Prototypes;
namespace Content.Server.Radio.EntitySystems;
@@ -27,6 +28,7 @@ public sealed class RadioDeviceSystem : EntitySystem
[Dependency] private readonly RadioSystem _radio = default!;
[Dependency] private readonly InteractionSystem _interaction = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
+ [Dependency] private readonly UserInterfaceSystem _ui = default!;
// Used to prevent a shitter from using a bunch of radios to spam chat.
private HashSet<(string, EntityUid)> _recentlySent = new();
@@ -39,12 +41,16 @@ public sealed class RadioDeviceSystem : EntitySystem
SubscribeLocalEvent(OnActivateMicrophone);
SubscribeLocalEvent(OnListen);
SubscribeLocalEvent(OnAttemptListen);
- SubscribeLocalEvent>(OnGetVerbs);
SubscribeLocalEvent(OnPowerChanged);
SubscribeLocalEvent(OnSpeakerInit);
SubscribeLocalEvent(OnActivateSpeaker);
SubscribeLocalEvent(OnReceiveRadio);
+
+ SubscribeLocalEvent(OnBeforeIntercomUiOpen);
+ SubscribeLocalEvent(OnToggleIntercomMic);
+ SubscribeLocalEvent(OnToggleIntercomSpeaker);
+ SubscribeLocalEvent(OnSelectIntercomChannel);
}
public override void Update(float frameTime)
@@ -75,12 +81,18 @@ public sealed class RadioDeviceSystem : EntitySystem
#region Toggling
private void OnActivateMicrophone(EntityUid uid, RadioMicrophoneComponent component, ActivateInWorldEvent args)
{
+ if (!component.ToggleOnInteract)
+ return;
+
ToggleRadioMicrophone(uid, args.User, args.Handled, component);
args.Handled = true;
}
private void OnActivateSpeaker(EntityUid uid, RadioSpeakerComponent component, ActivateInWorldEvent args)
{
+ if (!component.ToggleOnInteract)
+ return;
+
ToggleRadioSpeaker(uid, args.User, args.Handled, component);
args.Handled = true;
}
@@ -90,71 +102,38 @@ public sealed class RadioDeviceSystem : EntitySystem
if (!Resolve(uid, ref component))
return;
- if (component.PowerRequired && !this.IsPowered(uid, EntityManager))
- return;
-
- SetMicrophoneEnabled(uid, !component.Enabled, component);
-
- if (!quiet)
- {
- var state = Loc.GetString(component.Enabled ? "handheld-radio-component-on-state" : "handheld-radio-component-off-state");
- var message = Loc.GetString("handheld-radio-component-on-use", ("radioState", state));
- _popup.PopupEntity(message, user, user);
- }
-
- if (component.Enabled)
- EnsureComp(uid).Range = component.ListenRange;
- else
- RemCompDeferred(uid);
- }
-
- private void OnGetVerbs(EntityUid uid, RadioMicrophoneComponent component, GetVerbsEvent args)
- {
- if (!args.CanAccess || !args.CanInteract || args.Hands == null)
- return;
-
- if (component.SupportedChannels == null || component.SupportedChannels.Count <= 1)
- return;
-
- if (component.PowerRequired && !this.IsPowered(uid, EntityManager))
- return;
-
- foreach (var channel in component.SupportedChannels)
- {
- var proto = _protoMan.Index(channel);
-
- var v = new Verb
- {
- Text = proto.LocalizedName,
- Priority = 1,
- Category = VerbCategory.ChannelSelect,
- Disabled = component.BroadcastChannel == channel,
- DoContactInteraction = true,
- Act = () =>
- {
- component.BroadcastChannel = channel;
- _popup.PopupEntity(Loc.GetString("handheld-radio-component-channel-set",
- ("channel", channel)), uid, args.User);
- }
- };
- args.Verbs.Add(v);
- }
+ SetMicrophoneEnabled(uid, user, !component.Enabled, quiet, component);
}
private void OnPowerChanged(EntityUid uid, RadioMicrophoneComponent component, ref PowerChangedEvent args)
{
if (args.Powered)
return;
- SetMicrophoneEnabled(uid, false, component);
+ SetMicrophoneEnabled(uid, null, false, true, component);
}
- public void SetMicrophoneEnabled(EntityUid uid, bool enabled, RadioMicrophoneComponent? component = null)
+ public void SetMicrophoneEnabled(EntityUid uid, EntityUid? user, bool enabled, bool quiet = false, RadioMicrophoneComponent? component = null)
{
if (!Resolve(uid, ref component, false))
return;
+ if (component.PowerRequired && !this.IsPowered(uid, EntityManager))
+ return;
+
component.Enabled = enabled;
+
+ if (!quiet && user != null)
+ {
+ var state = Loc.GetString(component.Enabled ? "handheld-radio-component-on-state" : "handheld-radio-component-off-state");
+ var message = Loc.GetString("handheld-radio-component-on-use", ("radioState", state));
+ _popup.PopupEntity(message, user.Value, user.Value);
+ }
+
_appearance.SetData(uid, RadioDeviceVisuals.Broadcasting, component.Enabled);
+ if (component.Enabled)
+ EnsureComp(uid).Range = component.ListenRange;
+ else
+ RemCompDeferred(uid);
}
public void ToggleRadioSpeaker(EntityUid uid, EntityUid user, bool quiet = false, RadioSpeakerComponent? component = null)
@@ -162,7 +141,15 @@ public sealed class RadioDeviceSystem : EntitySystem
if (!Resolve(uid, ref component))
return;
- component.Enabled = !component.Enabled;
+ SetSpeakerEnabled(uid, user, !component.Enabled, quiet, component);
+ }
+
+ public void SetSpeakerEnabled(EntityUid uid, EntityUid user, bool enabled, bool quiet = false, RadioSpeakerComponent? component = null)
+ {
+ if (!Resolve(uid, ref component))
+ return;
+
+ component.Enabled = enabled;
if (!quiet)
{
@@ -171,6 +158,7 @@ public sealed class RadioDeviceSystem : EntitySystem
_popup.PopupEntity(message, user, user);
}
+ _appearance.SetData(uid, RadioDeviceVisuals.Speaker, component.Enabled);
if (component.Enabled)
EnsureComp(uid).Channels.UnionWith(component.Channels);
else
@@ -215,6 +203,57 @@ public sealed class RadioDeviceSystem : EntitySystem
("originalName", nameEv.Name));
// log to chat so people can identity the speaker/source, but avoid clogging ghost chat if there are many radios
- _chat.TrySendInGameICMessage(uid, args.Message, InGameICChatType.Speak, ChatTransmitRange.GhostRangeLimit, nameOverride: name, checkRadioPrefix: false);
+ _chat.TrySendInGameICMessage(uid, args.Message, InGameICChatType.Whisper, ChatTransmitRange.GhostRangeLimit, nameOverride: name, checkRadioPrefix: false);
+ }
+
+ private void OnBeforeIntercomUiOpen(EntityUid uid, IntercomComponent component, BeforeActivatableUIOpenEvent args)
+ {
+ UpdateIntercomUi(uid, component);
+ }
+
+ private void OnToggleIntercomMic(EntityUid uid, IntercomComponent component, ToggleIntercomMicMessage args)
+ {
+ if (component.RequiresPower && !this.IsPowered(uid, EntityManager) || args.Session.AttachedEntity is not { } user)
+ return;
+
+ SetMicrophoneEnabled(uid, user, args.Enabled, true);
+ UpdateIntercomUi(uid, component);
+ }
+
+ private void OnToggleIntercomSpeaker(EntityUid uid, IntercomComponent component, ToggleIntercomSpeakerMessage args)
+ {
+ if (component.RequiresPower && !this.IsPowered(uid, EntityManager) || args.Session.AttachedEntity is not { } user)
+ return;
+
+ SetSpeakerEnabled(uid, user, args.Enabled, true);
+ UpdateIntercomUi(uid, component);
+ }
+
+ private void OnSelectIntercomChannel(EntityUid uid, IntercomComponent component, SelectIntercomChannelMessage args)
+ {
+ if (component.RequiresPower && !this.IsPowered(uid, EntityManager) || args.Session.AttachedEntity is not { })
+ return;
+
+ if (!_protoMan.TryIndex(args.Channel, out _) || !component.SupportedChannels.Contains(args.Channel))
+ return;
+
+ if (TryComp(uid, out var mic))
+ mic.BroadcastChannel = args.Channel;
+ if (TryComp(uid, out var speaker))
+ speaker.Channels = new(){ args.Channel };
+ UpdateIntercomUi(uid, component);
+ }
+
+ private void UpdateIntercomUi(EntityUid uid, IntercomComponent component)
+ {
+ var micComp = CompOrNull(uid);
+ var speakerComp = CompOrNull(uid);
+
+ var micEnabled = micComp?.Enabled ?? false;
+ var speakerEnabled = speakerComp?.Enabled ?? false;
+ var availableChannels = component.SupportedChannels;
+ var selectedChannel = micComp?.BroadcastChannel ?? SharedChatSystem.CommonChannel;
+ var state = new IntercomBoundUIState(micEnabled, speakerEnabled, availableChannels, selectedChannel);
+ _ui.TrySetUiState(uid, IntercomUiKey.Key, state);
}
}
diff --git a/Content.Server/Radio/EntitySystems/RadioSystem.cs b/Content.Server/Radio/EntitySystems/RadioSystem.cs
index 44963c2863..123a8c57c9 100644
--- a/Content.Server/Radio/EntitySystems/RadioSystem.cs
+++ b/Content.Server/Radio/EntitySystems/RadioSystem.cs
@@ -92,7 +92,7 @@ public sealed class RadioSystem : EntitySystem
var sentAtLeastOnce = false;
while (canSend && radioQuery.MoveNext(out var receiver, out var radio, out var transform))
{
- if (!radio.Channels.Contains(channel.ID))
+ if (!radio.Channels.Contains(channel.ID) || (TryComp(receiver, out var intercom) && !intercom.SupportedChannels.Contains(channel.ID)))
continue;
if (!channel.LongRange && transform.MapID != sourceMapId && !radio.GlobalReceive)
diff --git a/Content.Shared/Radio/Components/IntercomComponent.cs b/Content.Shared/Radio/Components/IntercomComponent.cs
new file mode 100644
index 0000000000..d68a80e174
--- /dev/null
+++ b/Content.Shared/Radio/Components/IntercomComponent.cs
@@ -0,0 +1,23 @@
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
+
+namespace Content.Shared.Radio.Components;
+
+///
+/// Handles intercom ui and is authoritative on the channels an intercom can access.
+///
+[RegisterComponent, NetworkedComponent]
+public sealed class IntercomComponent : Component
+{
+ ///
+ /// Does this intercom require popwer to function
+ ///
+ [DataField("requiresPower"), ViewVariables(VVAccess.ReadWrite)]
+ public bool RequiresPower = true;
+
+ ///
+ /// The list of radio channel prototypes this intercom can choose between.
+ ///
+ [DataField("supportedChannels", customTypeSerializer: typeof(PrototypeIdListSerializer))]
+ public List SupportedChannels = new();
+}
diff --git a/Content.Shared/Radio/RadioVisuals.cs b/Content.Shared/Radio/RadioVisuals.cs
index fc2993dec9..a43828716d 100644
--- a/Content.Shared/Radio/RadioVisuals.cs
+++ b/Content.Shared/Radio/RadioVisuals.cs
@@ -5,11 +5,13 @@ namespace Content.Shared.Radio;
[Serializable, NetSerializable]
public enum RadioDeviceVisuals : byte
{
- Broadcasting
+ Broadcasting,
+ Speaker
}
[Serializable, NetSerializable]
public enum RadioDeviceVisualLayers : byte
{
- Broadcasting
+ Broadcasting,
+ Speaker
}
diff --git a/Content.Shared/Radio/SharedIntercom.cs b/Content.Shared/Radio/SharedIntercom.cs
new file mode 100644
index 0000000000..410843312f
--- /dev/null
+++ b/Content.Shared/Radio/SharedIntercom.cs
@@ -0,0 +1,59 @@
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Radio;
+
+[Serializable, NetSerializable]
+public enum IntercomUiKey
+{
+ Key,
+}
+
+[Serializable, NetSerializable]
+public sealed class IntercomBoundUIState : BoundUserInterfaceState
+{
+ public bool MicEnabled;
+ public bool SpeakerEnabled;
+ public List AvailableChannels;
+ public string SelectedChannel;
+
+ public IntercomBoundUIState(bool micEnabled, bool speakerEnabled, List availableChannels, string selectedChannel)
+ {
+ MicEnabled = micEnabled;
+ SpeakerEnabled = speakerEnabled;
+ AvailableChannels = availableChannels;
+ SelectedChannel = selectedChannel;
+ }
+}
+
+[Serializable, NetSerializable]
+public sealed class ToggleIntercomMicMessage : BoundUserInterfaceMessage
+{
+ public bool Enabled;
+
+ public ToggleIntercomMicMessage(bool enabled)
+ {
+ Enabled = enabled;
+ }
+}
+
+[Serializable, NetSerializable]
+public sealed class ToggleIntercomSpeakerMessage : BoundUserInterfaceMessage
+{
+ public bool Enabled;
+
+ public ToggleIntercomSpeakerMessage(bool enabled)
+ {
+ Enabled = enabled;
+ }
+}
+
+[Serializable, NetSerializable]
+public sealed class SelectIntercomChannelMessage : BoundUserInterfaceMessage
+{
+ public string Channel;
+
+ public SelectIntercomChannelMessage(string channel)
+ {
+ Channel = channel;
+ }
+}
diff --git a/Resources/Locale/en-US/radio/components/intercom.ftl b/Resources/Locale/en-US/radio/components/intercom.ftl
new file mode 100644
index 0000000000..e56e3cd0f7
--- /dev/null
+++ b/Resources/Locale/en-US/radio/components/intercom.ftl
@@ -0,0 +1,5 @@
+intercom-menu-title = Intercom
+intercom-channel-label = Channel:
+intercom-button-text-mic = Mic.
+intercom-button-text-speaker = Speak
+intercom-flavor-text-left = Keep lines free of chatter
diff --git a/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/item_artifacts.yml b/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/item_artifacts.yml
index 5686b5783e..465e1a458c 100644
--- a/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/item_artifacts.yml
+++ b/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/item_artifacts.yml
@@ -51,6 +51,8 @@
type: TransferAmountBoundUserInterface
- key: enum.InstrumentUiKey.Key
type: InstrumentBoundUserInterface
+ - key: enum.IntercomUiKey.Key
+ type: IntercomBoundUserInterface
- type: Appearance
- type: Item
size: 40
diff --git a/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/structure_artifacts.yml b/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/structure_artifacts.yml
index 1a6cc5cd58..eb612a8bc0 100644
--- a/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/structure_artifacts.yml
+++ b/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/structure_artifacts.yml
@@ -28,6 +28,8 @@
type: TransferAmountBoundUserInterface
- key: enum.InstrumentUiKey.Key
type: InstrumentBoundUserInterface
+ - key: enum.IntercomUiKey.Key
+ type: IntercomBoundUserInterface
- type: Reactive
groups:
Acidic: [Touch]
diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/intercom.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/intercom.yml
index 12204bede9..d92dd24844 100644
--- a/Resources/Prototypes/Entities/Structures/Wallmounts/intercom.yml
+++ b/Resources/Prototypes/Entities/Structures/Wallmounts/intercom.yml
@@ -2,6 +2,7 @@
id: Intercom
name: intercom
description: An intercom. For when the station just needs to know something.
+ abstract: true
components:
- type: WallMount
- type: ApcPowerReceiver
@@ -12,6 +13,11 @@
powerRequired: true
unobstructedRequired: true
listenRange: 2
+ toggleOnInteract: false
+ - type: RadioSpeaker
+ toggleOnInteract: false
+ - type: Intercom
+ - type: Speech
- type: ExtensionCableReceiver
- type: Clickable
- type: InteractionOutline
@@ -23,10 +29,15 @@
- state: base
- state: unshaded
map: ["enum.PowerDeviceVisualLayers.Powered"]
+ shader: unshaded
- state: broadcasting
map: ["enum.RadioDeviceVisualLayers.Broadcasting"]
shader: unshaded
visible: false
+ - state: speaker
+ map: ["enum.RadioDeviceVisualLayers.Speaker"]
+ shader: unshaded
+ visible: false
- type: Transform
noRot: false
anchored: true
@@ -34,6 +45,13 @@
- type: Wires
BoardName: "Intercom"
LayoutId: Intercom
+ - type: ActivatableUIRequiresPower
+ - type: ActivatableUI
+ key: enum.IntercomUiKey.Key
+ - type: UserInterface
+ interfaces:
+ - key: enum.IntercomUiKey.Key
+ type: IntercomBoundUserInterface
- type: Construction
graph: Intercom
node: intercom
@@ -67,6 +85,10 @@
enum.RadioDeviceVisualLayers.Broadcasting:
True: { visible: true }
False: { visible: false }
+ enum.RadioDeviceVisuals.Speaker:
+ enum.RadioDeviceVisualLayers.Speaker:
+ True: { visible: true }
+ False: { visible: false }
placement:
mode: SnapgridCenter
snap:
@@ -93,15 +115,21 @@
snap:
- Wallmount
+- type: entity
+ id: IntercomCommon
+ parent: Intercom
+ suffix: Common
+ components:
+ - type: Intercom
+ supportedChannels:
+ - Common
+
- type: entity
id: IntercomCommand
parent: Intercom
suffix: Command
components:
- - type: RadioMicrophone
- powerRequired: true
- unobstructedRequired: true
- listenRange: 2
+ - type: Intercom
supportedChannels:
- Common
- Command
@@ -111,10 +139,7 @@
parent: Intercom
suffix: Engineering
components:
- - type: RadioMicrophone
- powerRequired: true
- unobstructedRequired: true
- listenRange: 2
+ - type: Intercom
supportedChannels:
- Common
- Engineering
@@ -124,10 +149,7 @@
parent: Intercom
suffix: Medical
components:
- - type: RadioMicrophone
- powerRequired: true
- unobstructedRequired: true
- listenRange: 2
+ - type: Intercom
supportedChannels:
- Common
- Medical
@@ -137,10 +159,7 @@
parent: Intercom
suffix: Science
components:
- - type: RadioMicrophone
- powerRequired: true
- unobstructedRequired: true
- listenRange: 2
+ - type: Intercom
supportedChannels:
- Common
- Science
@@ -150,10 +169,7 @@
parent: Intercom
suffix: Security
components:
- - type: RadioMicrophone
- powerRequired: true
- unobstructedRequired: true
- listenRange: 2
+ - type: Intercom
supportedChannels:
- Common
- Security
@@ -163,10 +179,7 @@
parent: Intercom
suffix: Service
components:
- - type: RadioMicrophone
- powerRequired: true
- unobstructedRequired: true
- listenRange: 2
+ - type: Intercom
supportedChannels:
- Common
- Service
@@ -176,10 +189,7 @@
parent: Intercom
suffix: Supply
components:
- - type: RadioMicrophone
- powerRequired: true
- unobstructedRequired: true
- listenRange: 2
+ - type: Intercom
supportedChannels:
- Common
- Supply
@@ -189,10 +199,7 @@
parent: Intercom
suffix: All
components:
- - type: RadioMicrophone
- powerRequired: true
- unobstructedRequired: true
- listenRange: 2
+ - type: Intercom
supportedChannels:
- Common
- Command
diff --git a/Resources/Prototypes/Procedural/dungeon_configs.yml b/Resources/Prototypes/Procedural/dungeon_configs.yml
index 732a5411f2..81931c6cd0 100644
--- a/Resources/Prototypes/Procedural/dungeon_configs.yml
+++ b/Resources/Prototypes/Procedural/dungeon_configs.yml
@@ -41,7 +41,7 @@
- id: RandomPainting
prob: 0.05
orGroup: content
- - id: Intercom
+ - id: IntercomCommon
prob: 0.1
orGroup: content
@@ -124,7 +124,7 @@
- id: RandomPainting
prob: 0.05
orGroup: content
- - id: Intercom
+ - id: IntercomCommon
prob: 0.1
orGroup: content
diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/utilities/intercom.yml b/Resources/Prototypes/Recipes/Construction/Graphs/utilities/intercom.yml
index a1e8e679ce..8da7a2a032 100644
--- a/Resources/Prototypes/Recipes/Construction/Graphs/utilities/intercom.yml
+++ b/Resources/Prototypes/Recipes/Construction/Graphs/utilities/intercom.yml
@@ -57,7 +57,7 @@
doAfter: 2
- node: intercom
- entity: Intercom #TODO: make this work with encryption keys
+ entity: IntercomCommon #TODO: make this work with encryption keys
edges:
- to: wired
conditions:
diff --git a/Resources/Prototypes/XenoArch/Effects/utility_effects.yml b/Resources/Prototypes/XenoArch/Effects/utility_effects.yml
index 0f3a6f4e5a..ee58c1482b 100644
--- a/Resources/Prototypes/XenoArch/Effects/utility_effects.yml
+++ b/Resources/Prototypes/XenoArch/Effects/utility_effects.yml
@@ -7,7 +7,15 @@
permanentComponents:
- type: RadioMicrophone
powerRequired: false
+ toggleOnInteract: false
listenRange: 3
+ - type: Speech
+ - type: RadioSpeaker
+ toggleOnInteract: false
+ - type: ActivatableUI
+ key: enum.IntercomUiKey.Key
+ - type: Intercom
+ requiresPower: false
supportedChannels:
- Common
- CentCom
diff --git a/Resources/Textures/Structures/Wallmounts/intercom.rsi/meta.json b/Resources/Textures/Structures/Wallmounts/intercom.rsi/meta.json
index 5df8b265bf..78fe02a9bb 100644
--- a/Resources/Textures/Structures/Wallmounts/intercom.rsi/meta.json
+++ b/Resources/Textures/Structures/Wallmounts/intercom.rsi/meta.json
@@ -19,6 +19,10 @@
"name": "build",
"directions": 4
},
+ {
+ "name": "speaker",
+ "directions": 4
+ },
{
"name": "unshaded",
"directions": 4
diff --git a/Resources/Textures/Structures/Wallmounts/intercom.rsi/speaker.png b/Resources/Textures/Structures/Wallmounts/intercom.rsi/speaker.png
new file mode 100644
index 0000000000000000000000000000000000000000..eb09c52fc3a078a9f0995ffac3b3d44ebaee7244
GIT binary patch
literal 157
zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|!aZFaLn`LH
zy%orLz<|f~;&GRd5>a1w-mPL>w_h+l=ivBmuEGga_rYCxrfu%LcQ>wGe&+Ftg&8Qs
z!0={89mCFfd^;}RdjaHqoBB)Ux6@Zw9)<@iSTj?2K2_YDGzp~4)78&qol`;+04QiR
AM*si-
literal 0
HcmV?d00001
diff --git a/Resources/Textures/Structures/Wallmounts/intercom.rsi/unshaded.png b/Resources/Textures/Structures/Wallmounts/intercom.rsi/unshaded.png
index b6731da141b67ef8ef410f8a03229c674ac77a81..7b0bb630722bb12089a4a1d54ca9eeeaa7d1b32a 100644
GIT binary patch
delta 288
zcmV+*0pI@d0>}c8F@FL{L_t(|obB4d4T3NffZ-=QaKagkcPB7$09=M8Si&m_D|jRr
zkDm494CQ$+5Y&T$NG<)pqzRCav@g82=|cnn007;kMX9kdO-qYXZTD$3_25NSMeke;
zLeTfc>-q9I
z(^u+LW$2x>DD|{i%YM0#K~uF|jrr#}p#9g{;Hm1uD)7?)xS$Szb_l?v=781VC?X=;
ms%oyi({&X902u%PJaq#ki|m-}=)ctf00004)?H
delta 329
zcmV-P0k;0g0`UTnF@GybL_t(|obB4NZG%7*#_`YWU>bXdD19b~lmW;x9>6V2q(!30
z0x1y@W$L8L499a3frwlrU@$!M|4AnS64K*|cL#ST0RRA?zqBYV388IiQQF)wT}|`w
zGBeo^k4cDz-uV6b_3L~i=_QKJuHUp(mjG}*m1IZ7e#+~p*ncX?Pr!g5s~&m}S>Aqm
zHZ2Lk4p}}p34^}`=dj)Hl+^WE@4DUZW{GDOt1bZm0000004#y`1(1s;MPC(<5y$eS
zMS0QmvuoYmAgi2(O+qm1EJVy5&zSz*2W+N{Vnl~5@Aa0tS{eFFT9o$j^r*M{JB?bZ
z-PM>S_W|8qYeJ)!s)tcv(gb+WB*1zk;HLV3hxba7^jz2N@#^#)1zdBrb8HIUf5ZR)
b0A_UpO|SWh>V4>000000NkvXXu0mjfsEVEZ
diff --git a/Resources/migration.yml b/Resources/migration.yml
index d643ff1f74..8f55788d37 100644
--- a/Resources/migration.yml
+++ b/Resources/migration.yml
@@ -101,4 +101,7 @@ ClothingHeadHelmetHelmetOld: ClothingHeadHelmetBasic
# 2023-07-04
# Bulletproof armor is almost statistically identical to kevlar, however, before this kevlar armor was the closest thing there was to "basic" armor. It makes the most sense to replace it with this.
-ClothingOuterVestKevlar: ClothingOuterArmorBasic
\ No newline at end of file
+ClothingOuterVestKevlar: ClothingOuterArmorBasic
+
+# 2023-07-10
+Intercom: IntercomCommon