From 0e9ed36b8568e7deb17fe3ae1a038f983e761e28 Mon Sep 17 00:00:00 2001
From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com>
Date: Sun, 7 Jul 2024 10:19:10 -0400
Subject: [PATCH] Intercom buffs and fixes (#29580)
* Intercom buffs and fixes
* remove unused bui state
* mild sec intercom buff
* reinforce sec intercoms
---
.../Radio/EntitySystems/RadioDeviceSystem.cs | 23 +++
.../Radio/Ui/IntercomBoundUserInterface.cs | 15 +-
Content.Client/Radio/Ui/IntercomMenu.xaml.cs | 37 +++-
.../Radio/EntitySystems/RadioDeviceSystem.cs | 82 ++++----
.../Radio/EntitySystems/RadioSystem.cs | 9 +-
.../Radio/Components/IntercomComponent.cs | 21 +-
.../Components/TelecomExemptComponent.cs | 9 +
.../EntitySystems/EncryptionKeySystem.cs | 14 +-
Content.Shared/Radio/SharedIntercom.cs | 20 +-
Content.Shared/Wires/SharedWiresSystem.cs | 6 +
.../en-US/radio/components/intercom.ftl | 3 +-
.../Entities/Objects/Devices/radio.yml | 3 +-
.../Structures/Wallmounts/intercom.yml | 186 +++++++++++++-----
.../Graphs/utilities/intercom.yml | 41 +++-
.../Recipes/Construction/utilities.yml | 2 +-
.../Wallmounts/intercom.rsi/base.png | Bin 1438 -> 729 bytes
.../Wallmounts/intercom.rsi/broadcasting.png | Bin 171 -> 137 bytes
.../Wallmounts/intercom.rsi/build.png | Bin 1369 -> 656 bytes
.../Wallmounts/intercom.rsi/panel.png | Bin 656 -> 316 bytes
.../Wallmounts/intercom.rsi/speaker.png | Bin 157 -> 134 bytes
.../Wallmounts/intercom.rsi/unshaded.png | Bin 328 -> 217 bytes
Resources/migration.yml | 3 +
22 files changed, 338 insertions(+), 136 deletions(-)
create mode 100644 Content.Client/Radio/EntitySystems/RadioDeviceSystem.cs
create mode 100644 Content.Shared/Radio/Components/TelecomExemptComponent.cs
diff --git a/Content.Client/Radio/EntitySystems/RadioDeviceSystem.cs b/Content.Client/Radio/EntitySystems/RadioDeviceSystem.cs
new file mode 100644
index 0000000000..29d6c635eb
--- /dev/null
+++ b/Content.Client/Radio/EntitySystems/RadioDeviceSystem.cs
@@ -0,0 +1,23 @@
+using Content.Client.Radio.Ui;
+using Content.Shared.Radio;
+using Content.Shared.Radio.Components;
+using Robust.Client.GameObjects;
+
+namespace Content.Client.Radio.EntitySystems;
+
+public sealed class RadioDeviceSystem : EntitySystem
+{
+ [Dependency] private readonly UserInterfaceSystem _ui = default!;
+
+ ///
+ public override void Initialize()
+ {
+ SubscribeLocalEvent(OnAfterHandleState);
+ }
+
+ private void OnAfterHandleState(Entity ent, ref AfterAutoHandleStateEvent args)
+ {
+ if (_ui.TryGetOpenUi(ent.Owner, IntercomUiKey.Key, out var bui))
+ bui.Update(ent);
+ }
+}
diff --git a/Content.Client/Radio/Ui/IntercomBoundUserInterface.cs b/Content.Client/Radio/Ui/IntercomBoundUserInterface.cs
index abbb1d58ec..7b3e39aa08 100644
--- a/Content.Client/Radio/Ui/IntercomBoundUserInterface.cs
+++ b/Content.Client/Radio/Ui/IntercomBoundUserInterface.cs
@@ -1,6 +1,6 @@
using Content.Shared.Radio;
+using Content.Shared.Radio.Components;
using JetBrains.Annotations;
-using Robust.Client.GameObjects;
namespace Content.Client.Radio.Ui;
@@ -19,7 +19,9 @@ public sealed class IntercomBoundUserInterface : BoundUserInterface
{
base.Open();
- _menu = new();
+ var comp = EntMan.GetComponent(Owner);
+
+ _menu = new((Owner, comp));
_menu.OnMicPressed += enabled =>
{
@@ -46,13 +48,8 @@ public sealed class IntercomBoundUserInterface : BoundUserInterface
_menu?.Close();
}
- protected override void UpdateState(BoundUserInterfaceState state)
+ public void Update(Entity ent)
{
- base.UpdateState(state);
-
- if (state is not IntercomBoundUIState msg)
- return;
-
- _menu?.Update(msg);
+ _menu?.Update(ent);
}
}
diff --git a/Content.Client/Radio/Ui/IntercomMenu.xaml.cs b/Content.Client/Radio/Ui/IntercomMenu.xaml.cs
index 8b4b38753c..2e08913051 100644
--- a/Content.Client/Radio/Ui/IntercomMenu.xaml.cs
+++ b/Content.Client/Radio/Ui/IntercomMenu.xaml.cs
@@ -1,8 +1,9 @@
using Content.Client.UserInterface.Controls;
-using Content.Shared.Radio;
+using Content.Shared.Radio.Components;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
+using Robust.Shared.Utility;
namespace Content.Client.Radio.Ui;
@@ -17,38 +18,54 @@ public sealed partial class IntercomMenu : FancyWindow
private readonly List _channels = new();
- public IntercomMenu()
+ public IntercomMenu(Entity entity)
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
MicButton.OnPressed += args => OnMicPressed?.Invoke(args.Button.Pressed);
SpeakerButton.OnPressed += args => OnSpeakerPressed?.Invoke(args.Button.Pressed);
+
+ Update(entity);
}
- public void Update(IntercomBoundUIState state)
+ public void Update(Entity entity)
{
- MicButton.Pressed = state.MicEnabled;
- SpeakerButton.Pressed = state.SpeakerEnabled;
+ MicButton.Pressed = entity.Comp.MicrophoneEnabled;
+ SpeakerButton.Pressed = entity.Comp.SpeakerEnabled;
+
+ MicButton.Disabled = entity.Comp.SupportedChannels.Count == 0;
+ SpeakerButton.Disabled = entity.Comp.SupportedChannels.Count == 0;
+ ChannelOptions.Disabled = entity.Comp.SupportedChannels.Count == 0;
ChannelOptions.Clear();
_channels.Clear();
- for (var i = 0; i < state.AvailableChannels.Count; i++)
+ for (var i = 0; i < entity.Comp.SupportedChannels.Count; i++)
{
- var channel = state.AvailableChannels[i];
- if (!_prototype.TryIndex(channel, out var prototype))
+ var channel = entity.Comp.SupportedChannels[i];
+ if (!_prototype.TryIndex(channel, out var prototype))
continue;
_channels.Add(channel);
ChannelOptions.AddItem(Loc.GetString(prototype.Name), i);
- if (channel == state.SelectedChannel)
+ if (channel == entity.Comp.CurrentChannel)
ChannelOptions.Select(i);
}
+
+ if (entity.Comp.SupportedChannels.Count == 0)
+ {
+ ChannelOptions.AddItem(Loc.GetString("intercom-options-none"), 0);
+ ChannelOptions.Select(0);
+ }
+
ChannelOptions.OnItemSelected += args =>
{
+ if (!_channels.TryGetValue(args.Id, out var proto))
+ return;
+
ChannelOptions.SelectId(args.Id);
- OnChannelSelected?.Invoke(_channels[args.Id]);
+ OnChannelSelected?.Invoke(proto);
};
}
}
diff --git a/Content.Server/Radio/EntitySystems/RadioDeviceSystem.cs b/Content.Server/Radio/EntitySystems/RadioDeviceSystem.cs
index 8484fb2336..1258e0b8c7 100644
--- a/Content.Server/Radio/EntitySystems/RadioDeviceSystem.cs
+++ b/Content.Server/Radio/EntitySystems/RadioDeviceSystem.cs
@@ -1,3 +1,4 @@
+using System.Linq;
using Content.Server.Chat.Systems;
using Content.Server.Interaction;
using Content.Server.Popups;
@@ -6,13 +7,10 @@ using Content.Server.Power.EntitySystems;
using Content.Server.Radio.Components;
using Content.Server.Speech;
using Content.Server.Speech.Components;
-using Content.Shared.UserInterface;
-using Content.Shared.Chat;
using Content.Shared.Examine;
using Content.Shared.Interaction;
using Content.Shared.Radio;
using Content.Shared.Radio.Components;
-using Robust.Server.GameObjects;
using Robust.Shared.Prototypes;
namespace Content.Server.Radio.EntitySystems;
@@ -28,7 +26,6 @@ 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();
@@ -47,7 +44,7 @@ public sealed class RadioDeviceSystem : EntitySystem
SubscribeLocalEvent(OnActivateSpeaker);
SubscribeLocalEvent(OnReceiveRadio);
- SubscribeLocalEvent(OnBeforeIntercomUiOpen);
+ SubscribeLocalEvent(OnIntercomEncryptionChannelsChanged);
SubscribeLocalEvent(OnToggleIntercomMic);
SubscribeLocalEvent(OnToggleIntercomSpeaker);
SubscribeLocalEvent(OnSelectIntercomChannel);
@@ -150,18 +147,18 @@ public sealed class RadioDeviceSystem : EntitySystem
SetSpeakerEnabled(uid, user, !component.Enabled, quiet, component);
}
- public void SetSpeakerEnabled(EntityUid uid, EntityUid user, bool enabled, bool quiet = false, RadioSpeakerComponent? component = null)
+ 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)
+ 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, user);
+ _popup.PopupEntity(message, user.Value, user.Value);
}
_appearance.SetData(uid, RadioDeviceVisuals.Speaker, component.Enabled);
@@ -213,61 +210,74 @@ public sealed class RadioDeviceSystem : EntitySystem
var nameEv = new TransformSpeakerNameEvent(args.MessageSource, Name(args.MessageSource));
RaiseLocalEvent(args.MessageSource, nameEv);
- var name = Loc.GetString("speech-name-relay", ("speaker", Name(uid)),
+ var name = Loc.GetString("speech-name-relay",
+ ("speaker", Name(uid)),
("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.Whisper, ChatTransmitRange.GhostRangeLimit, nameOverride: name, checkRadioPrefix: false);
}
- private void OnBeforeIntercomUiOpen(EntityUid uid, IntercomComponent component, BeforeActivatableUIOpenEvent args)
+ private void OnIntercomEncryptionChannelsChanged(Entity ent, ref EncryptionChannelsChangedEvent args)
{
- UpdateIntercomUi(uid, component);
+ ent.Comp.SupportedChannels = args.Component.Channels.Select(p => new ProtoId(p)).ToList();
+
+ var channel = args.Component.DefaultChannel;
+ if (ent.Comp.CurrentChannel != null && ent.Comp.SupportedChannels.Contains(ent.Comp.CurrentChannel.Value))
+ channel = ent.Comp.CurrentChannel;
+
+ SetIntercomChannel(ent, channel);
}
- private void OnToggleIntercomMic(EntityUid uid, IntercomComponent component, ToggleIntercomMicMessage args)
+ private void OnToggleIntercomMic(Entity ent, ref ToggleIntercomMicMessage args)
{
- if (component.RequiresPower && !this.IsPowered(uid, EntityManager))
+ if (ent.Comp.RequiresPower && !this.IsPowered(ent, EntityManager))
return;
- SetMicrophoneEnabled(uid, args.Actor, args.Enabled, true);
- UpdateIntercomUi(uid, component);
+ SetMicrophoneEnabled(ent, args.Actor, args.Enabled, true);
+ ent.Comp.MicrophoneEnabled = args.Enabled;
+ Dirty(ent);
}
- private void OnToggleIntercomSpeaker(EntityUid uid, IntercomComponent component, ToggleIntercomSpeakerMessage args)
+ private void OnToggleIntercomSpeaker(Entity ent, ref ToggleIntercomSpeakerMessage args)
{
- if (component.RequiresPower && !this.IsPowered(uid, EntityManager))
+ if (ent.Comp.RequiresPower && !this.IsPowered(ent, EntityManager))
return;
- SetSpeakerEnabled(uid, args.Actor, args.Enabled, true);
- UpdateIntercomUi(uid, component);
+ SetSpeakerEnabled(ent, args.Actor, args.Enabled, true);
+ ent.Comp.SpeakerEnabled = args.Enabled;
+ Dirty(ent);
}
- private void OnSelectIntercomChannel(EntityUid uid, IntercomComponent component, SelectIntercomChannelMessage args)
+ private void OnSelectIntercomChannel(Entity ent, ref SelectIntercomChannelMessage args)
{
- if (component.RequiresPower && !this.IsPowered(uid, EntityManager))
+ if (ent.Comp.RequiresPower && !this.IsPowered(ent, EntityManager))
return;
- if (!_protoMan.TryIndex(args.Channel, out _) || !component.SupportedChannels.Contains(args.Channel))
+ if (!_protoMan.HasIndex(args.Channel) || !ent.Comp.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);
+ SetIntercomChannel(ent, args.Channel);
}
- private void UpdateIntercomUi(EntityUid uid, IntercomComponent component)
+ private void SetIntercomChannel(Entity ent, ProtoId? channel)
{
- var micComp = CompOrNull(uid);
- var speakerComp = CompOrNull(uid);
+ ent.Comp.CurrentChannel = channel;
- 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.SetUiState(uid, IntercomUiKey.Key, state);
+ if (channel == null)
+ {
+ SetSpeakerEnabled(ent, null, false);
+ SetMicrophoneEnabled(ent, null, false);
+ ent.Comp.MicrophoneEnabled = false;
+ ent.Comp.SpeakerEnabled = false;
+ Dirty(ent);
+ return;
+ }
+
+ if (TryComp(ent, out var mic))
+ mic.BroadcastChannel = channel;
+ if (TryComp(ent, out var speaker))
+ speaker.Channels = new(){ channel };
+ Dirty(ent);
}
}
diff --git a/Content.Server/Radio/EntitySystems/RadioSystem.cs b/Content.Server/Radio/EntitySystems/RadioSystem.cs
index 4341746aaf..3ad101e62d 100644
--- a/Content.Server/Radio/EntitySystems/RadioSystem.cs
+++ b/Content.Server/Radio/EntitySystems/RadioSystem.cs
@@ -33,11 +33,15 @@ public sealed class RadioSystem : EntitySystem
// set used to prevent radio feedback loops.
private readonly HashSet _messages = new();
+ private EntityQuery _exemptQuery;
+
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent(OnIntrinsicReceive);
SubscribeLocalEvent(OnIntrinsicSpeak);
+
+ _exemptQuery = GetEntityQuery();
}
private void OnIntrinsicSpeak(EntityUid uid, IntrinsicRadioTransmitterComponent component, EntitySpokeEvent args)
@@ -121,9 +125,8 @@ public sealed class RadioSystem : EntitySystem
var sourceMapId = Transform(radioSource).MapID;
var hasActiveServer = HasActiveServer(sourceMapId, channel.ID);
- var hasMicro = HasComp(radioSource);
+ var sourceServerExempt = _exemptQuery.HasComp(radioSource);
- var speakerQuery = GetEntityQuery();
var radioQuery = EntityQueryEnumerator();
while (canSend && radioQuery.MoveNext(out var receiver, out var radio, out var transform))
{
@@ -138,7 +141,7 @@ public sealed class RadioSystem : EntitySystem
continue;
// don't need telecom server for long range channels or handheld radios and intercoms
- var needServer = !channel.LongRange && (!hasMicro || !speakerQuery.HasComponent(receiver));
+ var needServer = !channel.LongRange && !sourceServerExempt;
if (needServer && !hasActiveServer)
continue;
diff --git a/Content.Shared/Radio/Components/IntercomComponent.cs b/Content.Shared/Radio/Components/IntercomComponent.cs
index be2734ff16..8d7b87597b 100644
--- a/Content.Shared/Radio/Components/IntercomComponent.cs
+++ b/Content.Shared/Radio/Components/IntercomComponent.cs
@@ -1,23 +1,32 @@
using Robust.Shared.GameStates;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
+using Robust.Shared.Prototypes;
namespace Content.Shared.Radio.Components;
///
/// Handles intercom ui and is authoritative on the channels an intercom can access.
///
-[RegisterComponent, NetworkedComponent]
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)]
public sealed partial class IntercomComponent : Component
{
///
- /// Does this intercom require popwer to function
+ /// Does this intercom require power to function
///
- [DataField("requiresPower"), ViewVariables(VVAccess.ReadWrite)]
+ [DataField]
public bool RequiresPower = true;
+ [DataField, AutoNetworkedField]
+ public bool SpeakerEnabled;
+
+ [DataField, AutoNetworkedField]
+ public bool MicrophoneEnabled;
+
+ [DataField, AutoNetworkedField]
+ public ProtoId? CurrentChannel;
+
///
/// The list of radio channel prototypes this intercom can choose between.
///
- [DataField("supportedChannels", customTypeSerializer: typeof(PrototypeIdListSerializer))]
- public List SupportedChannels = new();
+ [DataField, AutoNetworkedField]
+ public List> SupportedChannels = new();
}
diff --git a/Content.Shared/Radio/Components/TelecomExemptComponent.cs b/Content.Shared/Radio/Components/TelecomExemptComponent.cs
new file mode 100644
index 0000000000..7af5c1c78c
--- /dev/null
+++ b/Content.Shared/Radio/Components/TelecomExemptComponent.cs
@@ -0,0 +1,9 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Radio.Components;
+
+///
+/// This is used for a radio that doesn't need a telecom server in order to broadcast.
+///
+[RegisterComponent, NetworkedComponent]
+public sealed partial class TelecomExemptComponent : Component;
diff --git a/Content.Shared/Radio/EntitySystems/EncryptionKeySystem.cs b/Content.Shared/Radio/EntitySystems/EncryptionKeySystem.cs
index ea07b5f8a5..cfa553661a 100644
--- a/Content.Shared/Radio/EntitySystems/EncryptionKeySystem.cs
+++ b/Content.Shared/Radio/EntitySystems/EncryptionKeySystem.cs
@@ -31,6 +31,7 @@ public sealed partial class EncryptionKeySystem : EntitySystem
[Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedHandsSystem _hands = default!;
+ [Dependency] private readonly SharedWiresSystem _wires = default!;
public override void Initialize()
{
@@ -150,7 +151,7 @@ public sealed partial class EncryptionKeySystem : EntitySystem
return;
}
- if (TryComp(uid, out var panel) && !panel.Open)
+ if (!_wires.IsPanelOpen(uid))
{
_popup.PopupClient(Loc.GetString("encryption-keys-panel-locked"), uid, args.User);
return;
@@ -184,8 +185,15 @@ public sealed partial class EncryptionKeySystem : EntitySystem
if (component.Channels.Count > 0)
{
- args.PushMarkup(Loc.GetString("examine-encryption-channels-prefix"));
- AddChannelsExamine(component.Channels, component.DefaultChannel, args, _protoManager, "examine-encryption-channel");
+ using (args.PushGroup(nameof(EncryptionKeyComponent)))
+ {
+ args.PushMarkup(Loc.GetString("examine-encryption-channels-prefix"));
+ AddChannelsExamine(component.Channels,
+ component.DefaultChannel,
+ args,
+ _protoManager,
+ "examine-encryption-channel");
+ }
}
}
diff --git a/Content.Shared/Radio/SharedIntercom.cs b/Content.Shared/Radio/SharedIntercom.cs
index 410843312f..f697add8b9 100644
--- a/Content.Shared/Radio/SharedIntercom.cs
+++ b/Content.Shared/Radio/SharedIntercom.cs
@@ -1,4 +1,5 @@
-using Robust.Shared.Serialization;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization;
namespace Content.Shared.Radio;
@@ -8,23 +9,6 @@ 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
{
diff --git a/Content.Shared/Wires/SharedWiresSystem.cs b/Content.Shared/Wires/SharedWiresSystem.cs
index d84766a5fc..7032293eaf 100644
--- a/Content.Shared/Wires/SharedWiresSystem.cs
+++ b/Content.Shared/Wires/SharedWiresSystem.cs
@@ -20,6 +20,7 @@ public abstract class SharedWiresSystem : EntitySystem
{
base.Initialize();
+ SubscribeLocalEvent(OnStartup);
SubscribeLocalEvent(OnPanelDoAfter);
SubscribeLocalEvent(OnInteractUsing);
SubscribeLocalEvent(OnExamine);
@@ -28,6 +29,11 @@ public abstract class SharedWiresSystem : EntitySystem
SubscribeLocalEvent(OnActivatableUIPanelChanged);
}
+ private void OnStartup(Entity ent, ref ComponentStartup args)
+ {
+ UpdateAppearance(ent, ent);
+ }
+
private void OnPanelDoAfter(EntityUid uid, WiresPanelComponent panel, WirePanelDoAfterEvent args)
{
if (args.Cancelled)
diff --git a/Resources/Locale/en-US/radio/components/intercom.ftl b/Resources/Locale/en-US/radio/components/intercom.ftl
index e56e3cd0f7..63303999c2 100644
--- a/Resources/Locale/en-US/radio/components/intercom.ftl
+++ b/Resources/Locale/en-US/radio/components/intercom.ftl
@@ -1,5 +1,6 @@
intercom-menu-title = Intercom
intercom-channel-label = Channel:
intercom-button-text-mic = Mic.
-intercom-button-text-speaker = Speak
+intercom-button-text-speaker = Spkr.
+intercom-options-none = No channels
intercom-flavor-text-left = Keep lines free of chatter
diff --git a/Resources/Prototypes/Entities/Objects/Devices/radio.yml b/Resources/Prototypes/Entities/Objects/Devices/radio.yml
index 43f84fe404..77b6cac2d3 100644
--- a/Resources/Prototypes/Entities/Objects/Devices/radio.yml
+++ b/Resources/Prototypes/Entities/Objects/Devices/radio.yml
@@ -4,6 +4,7 @@
parent: BaseItem
id: RadioHandheld
components:
+ - type: TelecomExempt
- type: RadioMicrophone
broadcastChannel: Handheld
- type: RadioSpeaker
@@ -39,4 +40,4 @@
sprite: Objects/Devices/securityhandy.rsi
- type: Item
sprite: Objects/Devices/securityhandy.rsi
- heldPrefix: walkietalkie
\ No newline at end of file
+ heldPrefix: walkietalkie
diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/intercom.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/intercom.yml
index 2cf77d843c..ca1b1b6c40 100644
--- a/Resources/Prototypes/Entities/Structures/Wallmounts/intercom.yml
+++ b/Resources/Prototypes/Entities/Structures/Wallmounts/intercom.yml
@@ -1,5 +1,5 @@
- type: entity
- id: Intercom
+ id: BaseIntercom
name: intercom
description: An intercom. For when the station just needs to know something.
abstract: true
@@ -9,6 +9,10 @@
- type: Electrified
enabled: false
usesApcPower: true
+ - type: TelecomExempt
+ - type: EncryptionKeyHolder
+ keySlots: 3
+ keysExtractionMethod: Prying
- type: RadioMicrophone
powerRequired: true
unobstructedRequired: true
@@ -24,12 +28,14 @@
- type: InteractionOutline
- type: Appearance
- type: WiresVisuals
+ - type: WiresPanelSecurity
- type: ContainerFill
containers:
board: [ IntercomElectronics ]
- type: ContainerContainer
containers:
board: !type:Container
+ key_slots: !type:Container
- type: Sprite
noRot: false
drawdepth: SmallObjects
@@ -49,7 +55,6 @@
visible: false
- state: panel
map: ["enum.WiresVisualLayers.MaintenancePanel"]
- shader: unshaded
visible: false
- type: Transform
noRot: false
@@ -61,6 +66,7 @@
- type: ActivatableUIRequiresPower
- type: ActivatableUI
key: enum.IntercomUiKey.Key
+ singleUser: true
- type: UserInterface
interfaces:
enum.IntercomUiKey.Key:
@@ -116,7 +122,7 @@
- Wallmount
- type: entity
- id: IntercomAssesmbly
+ id: IntercomAssembly
name: intercom assembly
description: An intercom. It doesn't seem very helpful right now.
components:
@@ -126,7 +132,18 @@
- type: Sprite
drawdepth: SmallObjects
sprite: Structures/Wallmounts/intercom.rsi
- state: build
+ layers:
+ - state: build
+ - state: panel
+ visible: false
+ map: [ "wires" ]
+ - type: Appearance
+ - type: GenericVisualizer
+ visuals:
+ enum.ConstructionVisuals.Layer:
+ wires:
+ 0: { visible: false }
+ 1: { visible: true }
- type: Construction
graph: Intercom
node: assembly
@@ -137,97 +154,176 @@
snap:
- Wallmount
+# this weird inheritance BS exists for construction shitcode
+- type: entity
+ id: IntercomConstructed
+ parent: BaseIntercom
+ suffix: Empty, Panel Open
+ components:
+ - type: Sprite
+ layers:
+ - 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
+ - state: panel
+ map: ["enum.WiresVisualLayers.MaintenancePanel"]
+ visible: true
+ - type: WiresPanel
+ open: true
+
+- type: entity
+ id: Intercom
+ parent: IntercomConstructed
+ suffix: ""
+ components:
+ - type: Sprite
+ layers:
+ - 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
+ - state: panel
+ map: ["enum.WiresVisualLayers.MaintenancePanel"]
+ - type: WiresPanel
+ open: false
+
- type: entity
id: IntercomCommon
parent: Intercom
suffix: Common
components:
- - type: Intercom
- supportedChannels:
- - Common
+ - type: ContainerFill
+ containers:
+ board:
+ - IntercomElectronics
+ key_slots:
+ - EncryptionKeyCommon
- type: entity
id: IntercomCommand
parent: Intercom
suffix: Command
components:
- - type: Intercom
- supportedChannels:
- - Common
- - Command
+ - type: ContainerFill
+ containers:
+ board:
+ - IntercomElectronics
+ key_slots:
+ - EncryptionKeyCommon
+ - EncryptionKeyCommand
- type: entity
id: IntercomEngineering
parent: Intercom
suffix: Engineering
components:
- - type: Intercom
- supportedChannels:
- - Common
- - Engineering
+ - type: ContainerFill
+ containers:
+ board:
+ - IntercomElectronics
+ key_slots:
+ - EncryptionKeyCommon
+ - EncryptionKeyEngineering
- type: entity
id: IntercomMedical
parent: Intercom
suffix: Medical
components:
- - type: Intercom
- supportedChannels:
- - Common
- - Medical
+ - type: ContainerFill
+ containers:
+ board:
+ - IntercomElectronics
+ key_slots:
+ - EncryptionKeyCommon
+ - EncryptionKeyMedical
- type: entity
id: IntercomScience
parent: Intercom
suffix: Science
components:
- - type: Intercom
- supportedChannels:
- - Common
- - Science
+ - type: ContainerFill
+ containers:
+ board:
+ - IntercomElectronics
+ key_slots:
+ - EncryptionKeyCommon
+ - EncryptionKeyScience
- type: entity
id: IntercomSecurity
parent: Intercom
suffix: Security
+ description: An intercom. It's been reinforced with metal from security helmets, making it a bitch-and-a-half to open.
components:
- - type: Intercom
- supportedChannels:
- - Common
- - Security
+ - type: WiresPanel
+ openDelay: 5
+ - type: WiresPanelSecurity
+ examine: wires-panel-component-on-examine-security-level2
+ wiresAccessible: false
+ - type: Construction
+ node: intercomReinforced
+ - type: ContainerFill
+ containers:
+ board:
+ - IntercomElectronics
+ key_slots:
+ - EncryptionKeyCommon
+ - EncryptionKeySecurity
- type: entity
id: IntercomService
parent: Intercom
suffix: Service
components:
- - type: Intercom
- supportedChannels:
- - Common
- - Service
+ - type: ContainerFill
+ containers:
+ board:
+ - IntercomElectronics
+ key_slots:
+ - EncryptionKeyCommon
+ - EncryptionKeyService
- type: entity
id: IntercomSupply
parent: Intercom
suffix: Supply
components:
- - type: Intercom
- supportedChannels:
- - Common
- - Supply
+ - type: ContainerFill
+ containers:
+ board:
+ - IntercomElectronics
+ key_slots:
+ - EncryptionKeyCommon
+ - EncryptionKeyCargo
- type: entity
id: IntercomAll
parent: Intercom
suffix: All
components:
- - type: Intercom
- supportedChannels:
- - Common
- - Command
- - Engineering
- - Medical
- - Science
- - Security
- - Service
- - Supply
+ - type: ContainerFill
+ containers:
+ board:
+ - IntercomElectronics
+ key_slots:
+ - EncryptionKeyCommon
+ - EncryptionKeyStationMaster
diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/utilities/intercom.yml b/Resources/Prototypes/Recipes/Construction/Graphs/utilities/intercom.yml
index 2247860f89..ba29d72539 100644
--- a/Resources/Prototypes/Recipes/Construction/Graphs/utilities/intercom.yml
+++ b/Resources/Prototypes/Recipes/Construction/Graphs/utilities/intercom.yml
@@ -11,13 +11,17 @@
doAfter: 2.0
- node: assembly
- entity: IntercomAssesmbly
+ entity: IntercomAssembly
edges:
- to: wired
steps:
- material: Cable
amount: 2
doAfter: 1
+ completed:
+ - !type:VisualizerDataInt
+ key: "enum.ConstructionVisuals.Layer"
+ data: 1
- to: start
completed:
- !type:GivePrototype
@@ -29,7 +33,7 @@
doAfter: 2
- node: wired
- entity: IntercomAssesmbly
+ entity: IntercomAssembly
edges:
- to: electronics
steps:
@@ -45,6 +49,9 @@
- !type:GivePrototype
prototype: CableApcStack1
amount: 2
+ - !type:VisualizerDataInt
+ key: "enum.ConstructionVisuals.Layer"
+ data: 0
steps:
- tool: Cutting
doAfter: 1
@@ -57,7 +64,11 @@
doAfter: 2
- node: intercom
- entity: IntercomCommon #TODO: make this work with encryption keys
+ entity: IntercomConstructed
+ doNotReplaceInheritingEntities: true
+ actions:
+ - !type:SetWiresPanelSecurity
+ wiresAccessible: true
edges:
- to: wired
conditions:
@@ -72,3 +83,27 @@
steps:
- tool: Prying
doAfter: 1
+ - to: intercomReinforced
+ conditions:
+ - !type:WirePanel
+ steps:
+ - material: Steel
+ amount: 1
+ - tool: Welding
+ doAfter: 1
+
+ - node: intercomReinforced
+ actions:
+ - !type:SetWiresPanelSecurity
+ examine: wires-panel-component-on-examine-security-level2
+ wiresAccessible: false
+ edges:
+ - to: intercom
+ conditions:
+ - !type:WirePanel
+ completed:
+ - !type:GivePrototype
+ prototype: SheetSteel1
+ steps:
+ - tool: Welding
+ doAfter: 5
diff --git a/Resources/Prototypes/Recipes/Construction/utilities.yml b/Resources/Prototypes/Recipes/Construction/utilities.yml
index 19f2fee183..82c16de7b6 100644
--- a/Resources/Prototypes/Recipes/Construction/utilities.yml
+++ b/Resources/Prototypes/Recipes/Construction/utilities.yml
@@ -790,7 +790,7 @@
# INTERCOM
- type: construction
name: intercom
- id: IntercomAssesmbly
+ id: IntercomAssembly
graph: Intercom
startNode: start
targetNode: intercom
diff --git a/Resources/Textures/Structures/Wallmounts/intercom.rsi/base.png b/Resources/Textures/Structures/Wallmounts/intercom.rsi/base.png
index 787af3f53876b0ceabe9599acf922b1e4f2748f6..a85cbfbecc6aa5f549067703687291f9ea7634a6 100644
GIT binary patch
delta 693
zcmbQoeUo*9K|RxCPZ!6KiaBp*1$GMsiX8v%%Cq`wK(3P)^CrjFW~X}=?pCPfe7SJF
z!2|ZF4_m8VDgNSeQ`~Xt*@m{S0n;8%X>psP_bq|nd-Ae-Z#PxHpE=`mwd9`ib>;6K
z-m(0CzMi42)8zRId54zN$jg`ixv#z|wC>}fO!a_PAiv_wfv->Md1ox^{2L{@_#?}9
zqD*m4km|_r5_($biT3EX3g8TOCRrXlU2R$dik^8oX3A=
z+I^IK{M&BQ$&>=thP_3WFY;yU<<8x;|6RR4rY-rl`Z>W@JTqedyyw{wt??kksPX!W
z!$7;59+FmIa&mFzO2&jS
z?dRM7;M3G>?c-}+{m)t;9RC@78mwF5L
z70eSnVqBj;H{SERrX|OwyQGTwu%o;_f8Xo+kN0YAYClOZOxRmI=NC}${=DlG;^N(t
zUaQ@!|DiHR`AW2cobj_;ReR-vR$e(}c>0(7dL}nD+JRT`07pFdJPz?u1J__-#@ntNFCz20Fq*J0IA-=
zTC&10OQ~KYO`31(oMAYMmA0Qg*Xq(9#1ls<>Aed9Uc1aYDXT5^T|o6`#1PQ)jPi
TO!EgO0tN<8S3j3^P619
delta 1407
zcmV-_1%Ue51)d9#Fn;0U}0flVPWyVrO&9NR4Ne+21}iOAb%Q-_Vx7{e&)jY)TkpE
z43-A2*cfrT@pwGET3exTQe?vK*Y8o%sha0x?^w-yFj4eHW2gW?AABa0;qL4l%gf9B
zzOuroYZw6UxUc3JB^d``SKVVV6k;b9I|s45hp7TQ9uL`URzCtPhC)1l@eJ>{k3cY3
z_qlX3&SEIUR(~YYeTlowCuW`5Y!;`}sUHEB!ePCCI~uLHzr4K^4l^}5*-cr`1=AT&
zK6o($?$6C}S}c-I#_^8(YLt*p#u;(Ck-cM=Vg{Hh;MLj+cW39wX0yD@DEjq&n+@4J
zrrUct)jz>b_%~J}Qw0=GiYzZL>+K`$!1Skfq#aN=DSx&pW-VeRDo0gSTrQX4Gxhq-
zRw7dc3|z5s%XhZy$?_PAQoHm4X!(8S=jSW>6Y*Hb3UN5>*k8XvIXvX*gIOLue!|B3
zDw`Wy0L*{Cz_Xv80x&%@b=F_lD6pa1D6{l|O5_nRuf;jp9p^&4;ZPMIE9@-xPkjR-Y
z!-ft@AQ0%v^edbc+1}o6%Ml6$0yXx3q#bZg8ZvywsNYD2o_2);fk3I;^MuCU=T0of
zz!e*AkBrml)IC@u1sIyZKSFox!ggyPa9S+#K7aep(EA*5y6LxloUvl?*=$z#V2v>S
z2lN6{lanlk!;HFy^}&~WpxekjZ59jhq=hyG@18eu8&r=g4F-ywAQf{R#H80(xO3
zvVX9!u&}VOu&}VOu=r1MDYr=GO81Wf}X-TY8D5eXBA!%i-jr*Kl#V*nxwBw_)L
z((|SY2nK_t3BRAI$w{^%5&Z~ffd?VPMbrTte-`7G{R=iXBNc-e8Et@6s(vCz&{RxN
zWO~ap!vME@e(bN`v?+e)yxlw1U8rB7Y>-h^AtKqpo3|zj(%|YnanwQ4c=@D2InI
zB())a=S&Yw>*peQTRsC`t*tbCN7F%35t2?P;}tQus}E+$W=~P8EhfZrcoVGtw&3Ff$|vqHED>A^;I_3S55PU=ZQ$t3Qmi~3Uh6S
zjuIo1{d$cM7l}xn)k~il(esHBGv>cv;P_Yr&P)}~W@^0(mTFp>0oCzyiGQX|K;+*>obGcIrd6hArqJ^H9lfI|6WCSv`0kr|
zcGbNKCrAi!5vZ!l%$vsIXDGc;6y*i{)-UECP%Z&B>-k-vSDM7**LP84aE
zSCyDyx_=j?a-wWr3|>@TRR<;7Er)BBZU$;h1@6^|kcEYXg@r|T_#2w_qxa3rkskm6
N002ovPDHLkV1mjqvT*#WAE}&fA;bybKCFE(g_aIH=VLv@TfG{$A0W
w>8Gz-GcYh@eLMyvfnt~ZjtMEUF)-{%WY)?T;I-A0Ee45uy85}Sb4q9e05)kJdjJ3c
delta 129
zcmeBVT+KMauqxHl#WAE}&f8lXc^ec&SOSugvRw~5m~8Vq5~O#~Y@3(`i_7AD<_DhM
zI{jaf0SMv+z4KP)t+m#;y7uU#-_0N)1_rAhiy9y0E}N%!`j=}1`SoAF@o#!(YO(%w
eha3aL_T3CUlKe&%h9?hz6nnb*xvXK;#J3tWrH3!@SFrl?xRqbOu9vkP_sQLgZ
zwAQO;(7+8KJ!e}%q?Qib{xbOG>TrUj6(t5T22^4X6
zdyDyE0ibvlD5Y?!&d}+c|7iu(S^%Zg)+Pu5%^)b+>;M#Qf<|gBzrF$&-#wdUfH^z-o03U9Q8>0X|+!!~;6Pgb<#*NkBDBKt~;s$Lh
zQ@Alf5ClOG1VP}z+PF}LB^*JFl^3A=bj{aiahq8GD@+-da0D?<>@a0mLLJ075r8r*
zp$=l4d}JX)8J187G1dZ5h9w+9jI{uiVF^bNV=Vw>Si%v+SPMWImT&|y)&fw5B^*Ia
z<)g66u!JLssT^WZh9y3N7zswIc0d`HNDw0kf*=T_d;^<`CUbQETe1KE002ovPDHLk
FV1jWY7&`y}
delta 1338
zcmV-A1;zT11=$LaFn%!$pwi^K0z=5%!~rQ*MYg#01_eXnWKrxv$gzXmgSA_)>)mx7
zlPvB2E;GAh&CEW(H-FxH5D^g(5fSmfWz4Ig-EO04TKl*In17}@Ha6xt%&GI4SA(W$
zZ7CijlgXg#I#N6afDnT3mzM`FMfE&6_vd4<0;lN>x?cKRkZiRupC6QdQMZ
z3&2x1RaI^4x{hU8D2jq*SyU<&gb);##?hX}SqrEe>kVXCW@~FJv^L;r0)Xq|6-5DH
zaZz{L{p#j!!+-TItPG_A0EsJ*d)8!Al054`=rzDs08%_gVc{~CWwBS?rP(?nm($qT
zC=S&5;-XHuYz{4elh}!T1pW*N=h)eK)AzSR)VOu)maF~M_BPGdk>3!3q<5QIqj9RFVSYkX__r)N+U1!MEM@1GFb?Fl*pu(#cXW|Q(YiNoK#
z>(JSDv)Qcc{8B6yhg$^0z~Z8g@u7%x<~sMvCgxHLX{RjqByHCT
z+<$a`yk%LW(`hD^slYx0U1CpGS9!Fu(g}Be==j`h-`n}wSuPzj@Npc>-G)=|^#fL`
zRnqBnU>|`3c(k&@lhsv>^#+AAIt$IFt9>iL>-sUtzQ5qq_o}=9&;Voed0-y{YruE1
z%$H@8@-@lTeptv4G4(O&|>1zJGJq
z^_+Xw?k!yh0F3>xP)1=!3My>&2G=YnAIfSS=
z&)p_4at*Le0Ngo&c52K?W1j=^`2wQe5kj{K45|UXw->*lqNvyS=G*T8`02+rUBz-)EY2KM~&}Px#_DMuRRCt{2+OZOXFbsg<=;^2qjx0Ql
z@BapLaq`rIJb(_aa$Z`}9QOYf1_~FrmTMA30002crcHDJ7Jc8pevL^fnVxBzA5IKa
zmGfl~Rh1znQB_I9=a^%4FJNgbf&6gl(`hU^|IIEpm~LOk^AFmy6389
zfQMDh0G&p;?dLc47000000DwPGT*39M6xXOiFH#fqY?gR8Vwx|CvII&l7V&MC)M&&M|0LzvEUD3m
oY5p5fJOezNB^xwi0040H2AB(}cg~IGwEzGB07*qoM6N<$f(<}`Bme*a
delta 632
zcmV-;0*C#)0+0oeBYy%{NklBjE6vzK^Tgi*28w$-(*@ohRaW*gM61hvi
zKqik`0$Q{OLczNhL%%@3gOd){U?gN=MYeH3I=$cY^ai1K
zdjETO?+zdcf*|&1U>JgeFEO>M56I2~z!(#k!=V@$hGN9%Y{lX!q-A{CWeAb3A9yt`?Zj=ZaJkI6m%fWe4|*HJ?-&003GC
zXkLeys%oYUpzs;6*T-$E?V<-y5Cl<*N$Mph-C9|K{}_hQIxAn6z0b@0z#?nkZ*+i^-Ef#!9Hf6WFw&f#&6=-S?M%~K_B@@rWZMy?BBb@?g${RdN3Eo24&
zK)YWDuyHy~8o6%#odd%Vo3cTgDv*|d$2n}r!Pkq6-6fEuOjRH$0o!rn%sproDC#08
zs|w^Do=CHtJ!lmuE1Rl791K;E~hzhr(peRbtwc(8&sGll0<
T#mz~RK*~H_{an^LB{Ts5O_nYE
diff --git a/Resources/Textures/Structures/Wallmounts/intercom.rsi/unshaded.png b/Resources/Textures/Structures/Wallmounts/intercom.rsi/unshaded.png
index 7b0bb630722bb12089a4a1d54ca9eeeaa7d1b32a..a8fda54fc9332fe037dae35c49093bc5c4650995 100644
GIT binary patch
delta 177
zcmX@Xbdzy{K|RA{PZ!6KiaBp@IdUCV5MVg4cKw_EpE~Q*o(R8Pna^?C*>m9+v81)7
zY&I_(Lm~=dl^I@KHqoz)yBy|ru!%5L0xcm4M5|1_2Kme%zA
z-@4G5fuUhq+rBQjsl8jbSxZmpJ^DbA0R$Yb)N20w8UA9{QLRZ)#qz&=*QIS-GTZ6Q
d1+a96k
delta 289
zcmV++0p9-E0muT7Fn<98Nkl}OyzoZF}khCwnw&_Cz0002prA4W+F-=R0Qf>EXH1*&`RYmVy
z3_{TN#q0UEhC>lPcMNq?624npsppbgj?veF+byiNfC000000084ji}I`EXQ#FQ2J!L~Ha14JRfw?NpCNr*7@7UD7XwPN
zywg|eRAuO$v?%qoS<8O8kU>+mU5)wYIiUU5+Tf|`!Yc670JxwIfOZJLq~?It;V2>^
n+p21=z0-9S02%-Q06cXAB#Z2r?C8JM00000NkvXXu0mjf+scRB
diff --git a/Resources/migration.yml b/Resources/migration.yml
index ef0a5f46b7..bd42de8f2c 100644
--- a/Resources/migration.yml
+++ b/Resources/migration.yml
@@ -358,3 +358,6 @@ FloorTileItemReinforced: PartRodMetal1
#2024-06-25
BookChefGaming: BookHowToCookForFortySpaceman
+
+#2024-06-29
+IntercomAssesmbly: IntercomAssembly