* Initial resources commit * Initial code commit * Added additional resources * Continuing to build holopad and telephone systems * Added hologram shader * Added hologram system and entity * Holo calls now have a hologram of the user appear on them * Initial implementation of holopads transmitting nearby chatter * Added support for linking across multiple telephones/holopads/entities * Fixed a bunch of bugs * Tried simplifying holopad entity dependence, added support for mid-call user switching * Replaced PVS expansion with manually networked sprite states * Adjusted volume of ring tone * Added machine board * Minor features and tweaks * Resolving merge conflict * Recommit audio attributions * Telephone chat adjustments * Added support for AI interactions with holopads * Building the holopad UI * Holopad UI finished * Further UI tweaks * Station AI can hear local chatter when being projected from a holopad * Minor bug fixes * Added wire panels to holopads * Basic broadcasting * Start of emergency broadcasting code * Fixing issues with broadcasting * More work on emergency broadcasting * Updated holopad visuals * Added cooldown text to emergency broadcast and control lock out screen * Code clean up * Fixed issue with timing * Broadcasting now requires command access * Fixed some bugs * Added multiple holopad prototypes with different ranges * The AI no longer requires power to interact with holopads * Fixed some additional issues * Addressing more issues * Added emote support for holograms * Changed the broadcast lockout durations to their proper values * Added AI vision wire to holopads * Bug fixes * AI vision and interaction wires can be added to the same wire panel * Fixed error * More bug fixes * Fixed test fail * Embellished the emergency call lock out window * Holopads play borg sounds when speaking * Borg and AI names are listed as the caller ID on the holopad * Borg chassis can now be seen on holopad holograms * Holopad returns to a machine frame when badly damaged * Clarified some text * Fix merge conflict * Fixed merge conflict * Fixing merge conflict * Fixing merge conflict * Fixing merge conflict * Offset menu on open * AI can alt click on holopads to activate the projector * Bug fixes for intellicard interactions * Fixed speech issue with intellicards * The UI automatically opens for the AI when it alt-clicks on the holopad * Simplified shader math * Telephones will auto hang up 60 seconds after the last person on a call stops speaking * Added better support for AI requests when multiple AI cores are on the station * The call controls pop up for the AI when they accept a summons from a holopad * Compatibility mode fix for the hologram shader * Further shader fixes for compatibility mode * File clean up * More cleaning up * Removed access requirements from quantum holopads so they can used by nukies * The title of the holopad window now reflects the name of the device * Linked telephones will lose their connection if both move out of range of each other
339 lines
13 KiB
C#
339 lines
13 KiB
C#
using Content.Client.Popups;
|
|
using Content.Client.UserInterface.Controls;
|
|
using Content.Shared.Access.Systems;
|
|
using Content.Shared.Holopad;
|
|
using Content.Shared.Telephone;
|
|
using Robust.Client.AutoGenerated;
|
|
using Robust.Client.Graphics;
|
|
using Robust.Client.Player;
|
|
using Robust.Client.UserInterface.Controls;
|
|
using Robust.Client.UserInterface.XAML;
|
|
using Robust.Shared.Timing;
|
|
using Robust.Shared.Utility;
|
|
using System.Linq;
|
|
|
|
namespace Content.Client.Holopad;
|
|
|
|
[GenerateTypedNameReferences]
|
|
public sealed partial class HolopadWindow : FancyWindow
|
|
{
|
|
[Dependency] private readonly IEntityManager _entManager = default!;
|
|
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
|
[Dependency] private readonly IGameTiming _timing = default!;
|
|
|
|
private readonly SharedHolopadSystem _holopadSystem = default!;
|
|
private readonly SharedTelephoneSystem _telephoneSystem = default!;
|
|
private readonly AccessReaderSystem _accessReaderSystem = default!;
|
|
private readonly PopupSystem _popupSystem = default!;
|
|
|
|
private EntityUid? _owner = null;
|
|
private HolopadUiKey _currentUiKey;
|
|
private TelephoneState _currentState;
|
|
private TelephoneState _previousState;
|
|
private TimeSpan _buttonUnlockTime;
|
|
private float _updateTimer = 0.25f;
|
|
|
|
private const float UpdateTime = 0.25f;
|
|
private TimeSpan _buttonUnlockDelay = TimeSpan.FromSeconds(0.5f);
|
|
|
|
public event Action<NetEntity>? SendHolopadStartNewCallMessageAction;
|
|
public event Action? SendHolopadAnswerCallMessageAction;
|
|
public event Action? SendHolopadEndCallMessageAction;
|
|
public event Action? SendHolopadStartBroadcastMessageAction;
|
|
public event Action? SendHolopadActivateProjectorMessageAction;
|
|
public event Action? SendHolopadRequestStationAiMessageAction;
|
|
|
|
public HolopadWindow()
|
|
{
|
|
RobustXamlLoader.Load(this);
|
|
IoCManager.InjectDependencies(this);
|
|
|
|
_holopadSystem = _entManager.System<SharedHolopadSystem>();
|
|
_telephoneSystem = _entManager.System<SharedTelephoneSystem>();
|
|
_accessReaderSystem = _entManager.System<AccessReaderSystem>();
|
|
_popupSystem = _entManager.System<PopupSystem>();
|
|
|
|
_buttonUnlockTime = _timing.CurTime + _buttonUnlockDelay;
|
|
|
|
// Assign button actions
|
|
AnswerCallButton.OnPressed += args => { OnHolopadAnswerCallMessage(); };
|
|
EndCallButton.OnPressed += args => { OnHolopadEndCallMessage(); };
|
|
StartBroadcastButton.OnPressed += args => { OnHolopadStartBroadcastMessage(); };
|
|
ActivateProjectorButton.OnPressed += args => { OnHolopadActivateProjectorMessage(); };
|
|
RequestStationAiButton.OnPressed += args => { OnHolopadRequestStationAiMessage(); };
|
|
|
|
// XML formatting
|
|
AnswerCallButton.AddStyleClass("ButtonAccept");
|
|
EndCallButton.AddStyleClass("Caution");
|
|
StartBroadcastButton.AddStyleClass("Caution");
|
|
|
|
HolopadContactListPanel.PanelOverride = new StyleBoxFlat
|
|
{
|
|
BackgroundColor = new Color(47, 47, 59) * Color.DarkGray,
|
|
BorderColor = new Color(82, 82, 82), //new Color(70, 73, 102),
|
|
BorderThickness = new Thickness(2),
|
|
};
|
|
|
|
HolopadContactListHeaderPanel.PanelOverride = new StyleBoxFlat
|
|
{
|
|
BackgroundColor = new Color(82, 82, 82),
|
|
};
|
|
|
|
EmergencyBroadcastText.SetMessage(FormattedMessage.FromMarkupOrThrow(Loc.GetString("holopad-window-emergency-broadcast-in-progress")));
|
|
SubtitleText.SetMessage(FormattedMessage.FromMarkupOrThrow(Loc.GetString("holopad-window-subtitle")));
|
|
OptionsText.SetMessage(FormattedMessage.FromMarkupOrThrow(Loc.GetString("holopad-window-options")));
|
|
}
|
|
|
|
#region: Button actions
|
|
|
|
private void OnSendHolopadStartNewCallMessage(NetEntity receiver)
|
|
{
|
|
SendHolopadStartNewCallMessageAction?.Invoke(receiver);
|
|
}
|
|
|
|
private void OnHolopadAnswerCallMessage()
|
|
{
|
|
SendHolopadAnswerCallMessageAction?.Invoke();
|
|
}
|
|
|
|
private void OnHolopadEndCallMessage()
|
|
{
|
|
SendHolopadEndCallMessageAction?.Invoke();
|
|
|
|
if (_currentUiKey == HolopadUiKey.AiRequestWindow)
|
|
Close();
|
|
}
|
|
|
|
private void OnHolopadStartBroadcastMessage()
|
|
{
|
|
if (_playerManager.LocalSession?.AttachedEntity == null || _owner == null)
|
|
return;
|
|
|
|
var player = _playerManager.LocalSession.AttachedEntity;
|
|
|
|
if (!_accessReaderSystem.IsAllowed(player.Value, _owner.Value))
|
|
{
|
|
_popupSystem.PopupClient(Loc.GetString("holopad-window-access-denied"), _owner.Value, player.Value);
|
|
return;
|
|
}
|
|
|
|
SendHolopadStartBroadcastMessageAction?.Invoke();
|
|
}
|
|
|
|
private void OnHolopadActivateProjectorMessage()
|
|
{
|
|
SendHolopadActivateProjectorMessageAction?.Invoke();
|
|
}
|
|
|
|
private void OnHolopadRequestStationAiMessage()
|
|
{
|
|
SendHolopadRequestStationAiMessageAction?.Invoke();
|
|
}
|
|
|
|
#endregion
|
|
|
|
public void SetState(EntityUid owner, HolopadUiKey uiKey)
|
|
{
|
|
_owner = owner;
|
|
_currentUiKey = uiKey;
|
|
|
|
// Determines what UI containers are available to the user.
|
|
// Components of these will be toggled on and off when
|
|
// UpdateAppearance() is called
|
|
|
|
switch (uiKey)
|
|
{
|
|
case HolopadUiKey.InteractionWindow:
|
|
RequestStationAiContainer.Visible = true;
|
|
HolopadContactListContainer.Visible = true;
|
|
StartBroadcastContainer.Visible = true;
|
|
break;
|
|
|
|
case HolopadUiKey.InteractionWindowForAi:
|
|
ActivateProjectorContainer.Visible = true;
|
|
StartBroadcastContainer.Visible = true;
|
|
break;
|
|
|
|
case HolopadUiKey.AiActionWindow:
|
|
HolopadContactListContainer.Visible = true;
|
|
StartBroadcastContainer.Visible = true;
|
|
break;
|
|
|
|
case HolopadUiKey.AiRequestWindow:
|
|
break;
|
|
}
|
|
}
|
|
|
|
public void UpdateState(Dictionary<NetEntity, string> holopads)
|
|
{
|
|
if (_owner == null || !_entManager.TryGetComponent<TelephoneComponent>(_owner.Value, out var telephone))
|
|
return;
|
|
|
|
// Caller ID text
|
|
var callerId = _telephoneSystem.GetFormattedCallerIdForEntity(telephone.LastCallerId.Item1, telephone.LastCallerId.Item2, Color.LightGray, "Default", 11);
|
|
|
|
CallerIdText.SetMessage(FormattedMessage.FromMarkupOrThrow(callerId));
|
|
LockOutIdText.SetMessage(FormattedMessage.FromMarkupOrThrow(callerId));
|
|
|
|
// Sort holopads alphabetically
|
|
var holopadArray = holopads.ToArray();
|
|
Array.Sort(holopadArray, AlphabeticalSort);
|
|
|
|
// Clear excess children from the contact list
|
|
while (ContactsList.ChildCount > holopadArray.Length)
|
|
ContactsList.RemoveChild(ContactsList.GetChild(ContactsList.ChildCount - 1));
|
|
|
|
// Make / update required children
|
|
for (int i = 0; i < holopadArray.Length; i++)
|
|
{
|
|
var (netEntity, label) = holopadArray[i];
|
|
|
|
if (i >= ContactsList.ChildCount)
|
|
{
|
|
var newContactButton = new HolopadContactButton();
|
|
newContactButton.OnPressed += args => { OnSendHolopadStartNewCallMessage(newContactButton.NetEntity); };
|
|
|
|
ContactsList.AddChild(newContactButton);
|
|
}
|
|
|
|
var child = ContactsList.GetChild(i);
|
|
|
|
if (child is not HolopadContactButton)
|
|
continue;
|
|
|
|
var contactButton = (HolopadContactButton)child;
|
|
contactButton.UpdateValues(netEntity, label);
|
|
}
|
|
|
|
// Update buttons
|
|
UpdateAppearance();
|
|
}
|
|
|
|
private void UpdateAppearance()
|
|
{
|
|
if (_owner == null || !_entManager.TryGetComponent<TelephoneComponent>(_owner.Value, out var telephone))
|
|
return;
|
|
|
|
if (_owner == null || !_entManager.TryGetComponent<HolopadComponent>(_owner.Value, out var holopad))
|
|
return;
|
|
|
|
var hasBroadcastAccess = !_holopadSystem.IsHolopadBroadcastOnCoolDown((_owner.Value, holopad));
|
|
var localPlayer = _playerManager.LocalSession?.AttachedEntity;
|
|
|
|
ControlsLockOutContainer.Visible = _holopadSystem.IsHolopadControlLocked((_owner.Value, holopad), localPlayer);
|
|
ControlsContainer.Visible = !ControlsLockOutContainer.Visible;
|
|
|
|
// Temporarily disable the interface buttons when the call state changes to prevent any misclicks
|
|
if (_currentState != telephone.CurrentState)
|
|
{
|
|
_previousState = _currentState;
|
|
_currentState = telephone.CurrentState;
|
|
_buttonUnlockTime = _timing.CurTime + _buttonUnlockDelay;
|
|
}
|
|
|
|
var lockButtons = _timing.CurTime < _buttonUnlockTime;
|
|
|
|
// Make / update required children
|
|
foreach (var child in ContactsList.Children)
|
|
{
|
|
if (child is not HolopadContactButton)
|
|
continue;
|
|
|
|
var contactButton = (HolopadContactButton)child;
|
|
contactButton.Disabled = (_currentState != TelephoneState.Idle || lockButtons);
|
|
}
|
|
|
|
// Update control text
|
|
var cooldown = _holopadSystem.GetHolopadBroadcastCoolDown((_owner.Value, holopad));
|
|
var cooldownString = $"{cooldown.Minutes:00}:{cooldown.Seconds:00}";
|
|
|
|
StartBroadcastButton.Text = _holopadSystem.IsHolopadBroadcastOnCoolDown((_owner.Value, holopad)) ?
|
|
Loc.GetString("holopad-window-emergency-broadcast-with-countdown", ("countdown", cooldownString)) :
|
|
Loc.GetString("holopad-window-emergency-broadcast");
|
|
|
|
var lockout = _holopadSystem.GetHolopadControlLockedPeriod((_owner.Value, holopad));
|
|
var lockoutString = $"{lockout.Minutes:00}:{lockout.Seconds:00}";
|
|
|
|
LockOutCountDownText.Text = Loc.GetString("holopad-window-controls-unlock-countdown", ("countdown", lockoutString));
|
|
|
|
switch (_currentState)
|
|
{
|
|
case TelephoneState.Idle:
|
|
CallStatusText.Text = Loc.GetString("holopad-window-no-calls-in-progress"); break;
|
|
|
|
case TelephoneState.Calling:
|
|
CallStatusText.Text = Loc.GetString("holopad-window-outgoing-call"); break;
|
|
|
|
case TelephoneState.Ringing:
|
|
CallStatusText.Text = (_currentUiKey == HolopadUiKey.AiRequestWindow) ?
|
|
Loc.GetString("holopad-window-ai-request") : Loc.GetString("holopad-window-incoming-call"); break;
|
|
|
|
case TelephoneState.InCall:
|
|
CallStatusText.Text = Loc.GetString("holopad-window-call-in-progress"); break;
|
|
|
|
case TelephoneState.EndingCall:
|
|
if (_previousState == TelephoneState.Calling || _previousState == TelephoneState.Idle)
|
|
CallStatusText.Text = Loc.GetString("holopad-window-call-rejected");
|
|
else
|
|
CallStatusText.Text = Loc.GetString("holopad-window-call-ending");
|
|
break;
|
|
}
|
|
|
|
// Update control disability
|
|
AnswerCallButton.Disabled = (_currentState != TelephoneState.Ringing || lockButtons);
|
|
EndCallButton.Disabled = (_currentState == TelephoneState.Idle || _currentState == TelephoneState.EndingCall || lockButtons);
|
|
StartBroadcastButton.Disabled = (_currentState != TelephoneState.Idle || !hasBroadcastAccess || lockButtons);
|
|
RequestStationAiButton.Disabled = (_currentState != TelephoneState.Idle || lockButtons);
|
|
ActivateProjectorButton.Disabled = (_currentState != TelephoneState.Idle || lockButtons);
|
|
|
|
// Update control visibility
|
|
FetchingAvailableHolopadsContainer.Visible = (ContactsList.ChildCount == 0);
|
|
ActiveCallControlsContainer.Visible = (_currentState != TelephoneState.Idle || _currentUiKey == HolopadUiKey.AiRequestWindow);
|
|
CallPlacementControlsContainer.Visible = !ActiveCallControlsContainer.Visible;
|
|
CallerIdText.Visible = (_currentState == TelephoneState.Ringing);
|
|
AnswerCallButton.Visible = (_currentState == TelephoneState.Ringing);
|
|
}
|
|
|
|
protected override void FrameUpdate(FrameEventArgs args)
|
|
{
|
|
base.FrameUpdate(args);
|
|
|
|
_updateTimer += args.DeltaSeconds;
|
|
|
|
if (_updateTimer >= UpdateTime)
|
|
{
|
|
_updateTimer -= UpdateTime;
|
|
UpdateAppearance();
|
|
}
|
|
}
|
|
|
|
private sealed class HolopadContactButton : Button
|
|
{
|
|
public NetEntity NetEntity;
|
|
|
|
public HolopadContactButton()
|
|
{
|
|
HorizontalExpand = true;
|
|
SetHeight = 32;
|
|
Margin = new Thickness(0f, 1f, 0f, 1f);
|
|
}
|
|
|
|
public void UpdateValues(NetEntity netEntity, string label)
|
|
{
|
|
NetEntity = netEntity;
|
|
Text = Loc.GetString("holopad-window-contact-label", ("label", label));
|
|
}
|
|
}
|
|
|
|
private int AlphabeticalSort(KeyValuePair<NetEntity, string> x, KeyValuePair<NetEntity, string> y)
|
|
{
|
|
if (string.IsNullOrEmpty(x.Value))
|
|
return -1;
|
|
|
|
if (string.IsNullOrEmpty(y.Value))
|
|
return 1;
|
|
|
|
return x.Value.CompareTo(y.Value);
|
|
}
|
|
}
|