Good Intercoms (#17950)

* crystal anomaly

* Good intercoms

* fixes

* fix construction fail

* Revert "crystal anomaly"

This reverts commit 0d9e3f62ff82c79e72f882b9c7f4ca1b9c6e6dd8.

* migration
This commit is contained in:
Nemanja
2023-07-11 19:58:18 -04:00
committed by GitHub
parent f89e85c1e9
commit 6dbfbc52c0
21 changed files with 407 additions and 98 deletions

View File

@@ -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);
}
}

View File

@@ -0,0 +1,31 @@
<controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Title="{Loc 'intercom-menu-title'}"
MinSize="300 170"
SetSize="300 170">
<BoxContainer Orientation="Vertical"
HorizontalExpand="True"
VerticalExpand="True"
Margin="5 0 5 0">
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" VerticalExpand="True">
<BoxContainer Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True" HorizontalAlignment="Center">
<Label Text="{Loc 'intercom-channel-label'}" HorizontalAlignment="Center"/>
<OptionButton Name="ChannelOptions" VerticalExpand="True" MinWidth="125"/>
</BoxContainer>
</BoxContainer>
<Control MinHeight="10"/>
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" HorizontalAlignment="Right" Margin="5 0 5 5">
<Button Name="MicButton" ToggleMode="True" Text="{Loc 'intercom-button-text-mic'}" StyleClasses="OpenRight" MinWidth="70"/>
<Button Name="SpeakerButton" ToggleMode="True" Text="{Loc 'intercom-button-text-speaker'}" StyleClasses="OpenLeft" MinWidth="70"/>
</BoxContainer>
<BoxContainer Orientation="Vertical">
<PanelContainer StyleClasses="LowDivider" />
<BoxContainer Orientation="Horizontal" Margin="10 2 5 0" VerticalAlignment="Bottom">
<Label Text="{Loc 'intercom-flavor-text-left'}" StyleClasses="WindowFooterText"
HorizontalAlignment="Left" HorizontalExpand="True" Margin="0 0 5 0" />
<TextureRect StyleClasses="NTLogoDark" Stretch="KeepAspectCentered"
VerticalAlignment="Center" HorizontalAlignment="Right" SetSize="19 19"/>
</BoxContainer>
</BoxContainer>
</BoxContainer>
</controls:FancyWindow>

View File

@@ -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<bool>? OnMicPressed;
public event Action<bool>? OnSpeakerPressed;
public event Action<string>? OnChannelSelected;
private readonly List<string> _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<RadioChannelPrototype>(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]);
};
}
}

View File

@@ -17,9 +17,6 @@ public sealed class RadioMicrophoneComponent : Component
[DataField("broadcastChannel", customTypeSerializer: typeof(PrototypeIdSerializer<RadioChannelPrototype>))]
public string BroadcastChannel = SharedChatSystem.CommonChannel;
[ViewVariables, DataField("supportedChannels", customTypeSerializer: typeof(PrototypeIdListSerializer<RadioChannelPrototype>))]
public List<string>? 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;
/// <summary>
/// Whether or not interacting with this entity
/// toggles it on or off.
/// </summary>
[DataField("toggleOnInteract")]
public bool ToggleOnInteract = true;
/// <summary>
/// Whether or not the speaker must have an
/// unobstructed path to the radio to speak

View File

@@ -12,6 +12,13 @@ namespace Content.Server.Radio.Components;
[Access(typeof(RadioDeviceSystem))]
public sealed class RadioSpeakerComponent : Component
{
/// <summary>
/// Whether or not interacting with this entity
/// toggles it on or off.
/// </summary>
[DataField("toggleOnInteract")]
public bool ToggleOnInteract = true;
[DataField("channels", customTypeSerializer: typeof(PrototypeIdHashSetSerializer<RadioChannelPrototype>))]
public HashSet<string> Channels = new () { SharedChatSystem.CommonChannel };

View File

@@ -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<RadioMicrophoneComponent, ActivateInWorldEvent>(OnActivateMicrophone);
SubscribeLocalEvent<RadioMicrophoneComponent, ListenEvent>(OnListen);
SubscribeLocalEvent<RadioMicrophoneComponent, ListenAttemptEvent>(OnAttemptListen);
SubscribeLocalEvent<RadioMicrophoneComponent, GetVerbsEvent<Verb>>(OnGetVerbs);
SubscribeLocalEvent<RadioMicrophoneComponent, PowerChangedEvent>(OnPowerChanged);
SubscribeLocalEvent<RadioSpeakerComponent, ComponentInit>(OnSpeakerInit);
SubscribeLocalEvent<RadioSpeakerComponent, ActivateInWorldEvent>(OnActivateSpeaker);
SubscribeLocalEvent<RadioSpeakerComponent, RadioReceiveEvent>(OnReceiveRadio);
SubscribeLocalEvent<IntercomComponent, BeforeActivatableUIOpenEvent>(OnBeforeIntercomUiOpen);
SubscribeLocalEvent<IntercomComponent, ToggleIntercomMicMessage>(OnToggleIntercomMic);
SubscribeLocalEvent<IntercomComponent, ToggleIntercomSpeakerMessage>(OnToggleIntercomSpeaker);
SubscribeLocalEvent<IntercomComponent, SelectIntercomChannelMessage>(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<ActiveListenerComponent>(uid).Range = component.ListenRange;
else
RemCompDeferred<ActiveListenerComponent>(uid);
}
private void OnGetVerbs(EntityUid uid, RadioMicrophoneComponent component, GetVerbsEvent<Verb> 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<RadioChannelPrototype>(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<ActiveListenerComponent>(uid).Range = component.ListenRange;
else
RemCompDeferred<ActiveListenerComponent>(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<ActiveRadioComponent>(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<RadioChannelPrototype>(args.Channel, out _) || !component.SupportedChannels.Contains(args.Channel))
return;
if (TryComp<RadioMicrophoneComponent>(uid, out var mic))
mic.BroadcastChannel = args.Channel;
if (TryComp<RadioSpeakerComponent>(uid, out var speaker))
speaker.Channels = new(){ args.Channel };
UpdateIntercomUi(uid, component);
}
private void UpdateIntercomUi(EntityUid uid, IntercomComponent component)
{
var micComp = CompOrNull<RadioMicrophoneComponent>(uid);
var speakerComp = CompOrNull<RadioSpeakerComponent>(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);
}
}

View File

@@ -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<IntercomComponent>(receiver, out var intercom) && !intercom.SupportedChannels.Contains(channel.ID)))
continue;
if (!channel.LongRange && transform.MapID != sourceMapId && !radio.GlobalReceive)

View File

@@ -0,0 +1,23 @@
using Robust.Shared.GameStates;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
namespace Content.Shared.Radio.Components;
/// <summary>
/// Handles intercom ui and is authoritative on the channels an intercom can access.
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed class IntercomComponent : Component
{
/// <summary>
/// Does this intercom require popwer to function
/// </summary>
[DataField("requiresPower"), ViewVariables(VVAccess.ReadWrite)]
public bool RequiresPower = true;
/// <summary>
/// The list of radio channel prototypes this intercom can choose between.
/// </summary>
[DataField("supportedChannels", customTypeSerializer: typeof(PrototypeIdListSerializer<RadioChannelPrototype>))]
public List<string> SupportedChannels = new();
}

View File

@@ -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
}

View File

@@ -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<string> AvailableChannels;
public string SelectedChannel;
public IntercomBoundUIState(bool micEnabled, bool speakerEnabled, List<string> 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;
}
}

View File

@@ -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

View File

@@ -51,6 +51,8 @@
type: TransferAmountBoundUserInterface
- key: enum.InstrumentUiKey.Key
type: InstrumentBoundUserInterface
- key: enum.IntercomUiKey.Key
type: IntercomBoundUserInterface
- type: Appearance
- type: Item
size: 40

View File

@@ -28,6 +28,8 @@
type: TransferAmountBoundUserInterface
- key: enum.InstrumentUiKey.Key
type: InstrumentBoundUserInterface
- key: enum.IntercomUiKey.Key
type: IntercomBoundUserInterface
- type: Reactive
groups:
Acidic: [Touch]

View File

@@ -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

View File

@@ -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

View File

@@ -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:

View File

@@ -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

View File

@@ -19,6 +19,10 @@
"name": "build",
"directions": 4
},
{
"name": "speaker",
"directions": 4
},
{
"name": "unshaded",
"directions": 4

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 369 B

After

Width:  |  Height:  |  Size: 328 B

View File

@@ -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
ClothingOuterVestKevlar: ClothingOuterArmorBasic
# 2023-07-10
Intercom: IntercomCommon