Files
space-station-14/Content.Client/Holopad/HolopadWindow.xaml.cs
T

353 lines
13 KiB
C#

using Content.Client.Popups;
using Content.Client.Stylesheets;
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;
namespace Content.Client.Holopad;
[GenerateTypedNameReferences]
public sealed partial class HolopadWindow : FancyWindow
{
[Dependency] private IEntityManager _entManager = default!;
[Dependency] private IPlayerManager _playerManager = default!;
[Dependency] private ILogManager _logManager = default!;
[Dependency] private IGameTiming _timing = default!;
private readonly SharedHolopadSystem _holopadSystem = default!;
private readonly SharedTelephoneSystem _telephoneSystem = default!;
private readonly AccessReaderSystem _accessReaderSystem = default!;
private readonly PopupSystem _popupSystem = default!;
private readonly ISawmill _sawmill = default!;
private EntityUid? _owner = null;
private HolopadUiKey _currentUiKey;
private string _currentSearch = string.Empty;
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;
private TimeSpan _updateDelay = TimeSpan.FromSeconds(0.25f);
private TimeSpan _nextUpdate;
public HolopadWindow()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
_holopadSystem = _entManager.System<SharedHolopadSystem>();
_telephoneSystem = _entManager.System<SharedTelephoneSystem>();
_accessReaderSystem = _entManager.System<AccessReaderSystem>();
_popupSystem = _entManager.System<PopupSystem>();
_sawmill = _logManager.GetSawmill("Holopad");
// 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(StyleClass.Negative);
StartBroadcastButton.AddStyleClass(StyleClass.Negative);
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),
};
}
#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 (!_entManager.TryGetComponent<TelephoneComponent>(_owner, out var telephone))
return;
// Caller and holopad ID text
var callerId = _telephoneSystem.GetFormattedCallerIdForEntity(telephone.LastCallerId?.CallerId, telephone.LastCallerId?.CallerJob, Color.LightGray, "Default", 11);
var holopadId = _telephoneSystem.GetFormattedDeviceIdForEntity(telephone.LastCallerId?.DeviceId, Color.LightGray, "Default", 11);
if (!FormattedMessage.TryFromMarkup(callerId, out var callerIdMsg))
{
callerIdMsg = FormattedMessage.FromMarkupPermissive(callerId);
_sawmill.Error($"CallerId markup text was incorrectly formatted: {callerIdMsg}");
}
if (!FormattedMessage.TryFromMarkup(holopadId, out var holopadIdMsg))
{
holopadIdMsg = FormattedMessage.FromMarkupPermissive(holopadId);
_sawmill.Error($"HolopadId markup text was incorrectly formatted: {holopadIdMsg}");
}
CallerIdText.SetMessage(callerIdMsg);
LockOutIdText.SetMessage(callerIdMsg);
HolopadIdText.SetMessage(holopadIdMsg);
// 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 (!_entManager.TryGetComponent<TelephoneComponent>(_owner, out var telephone))
return;
if (!_entManager.TryGetComponent<HolopadComponent>(_owner, out var holopad))
return;
var broadcastOnCooldown = _holopadSystem.IsHolopadBroadcastOnCoolDown((_owner.Value, holopad));
var localPlayer = _playerManager.LocalSession?.AttachedEntity;
// Update container visibility
ControlsLockOutContainer.Visible = _holopadSystem.IsHolopadControlLocked((_owner.Value, holopad), localPlayer);
ControlsContainer.Visible = !ControlsLockOutContainer.Visible;
// Update contact button visibility
if (SearchLineEdit.Text != _currentSearch)
{
_currentSearch = SearchLineEdit.Text;
foreach (var child in ContactsList.Children)
{
if (child is not HolopadContactButton contactButton)
continue;
var passesFilter = string.IsNullOrEmpty(SearchLineEdit.Text) ||
contactButton.Text?.Contains(SearchLineEdit.Text, StringComparison.CurrentCultureIgnoreCase) == true;
contactButton.Visible = passesFilter;
}
}
// Update timers
if (ControlsContainer.Visible)
{
var cooldown = _holopadSystem.GetHolopadBroadcastCoolDown((_owner.Value, holopad));
var cooldownString = $"{cooldown.Minutes:00}:{cooldown.Seconds:00}";
StartBroadcastButton.Text = broadcastOnCooldown
? Loc.GetString("holopad-window-emergency-broadcast-with-countdown", ("countdown", cooldownString))
: Loc.GetString("holopad-window-emergency-broadcast");
}
if (ControlsLockOutContainer.Visible)
{
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));
}
// Update call status text
switch (telephone.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 (telephone.PreviousState == TelephoneState.Calling || telephone.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 = telephone.CurrentState is not TelephoneState.Ringing;
EndCallButton.Disabled = telephone.CurrentState is TelephoneState.Idle or TelephoneState.EndingCall;
StartBroadcastButton.Disabled = telephone.CurrentState is not TelephoneState.Idle || broadcastOnCooldown;
RequestStationAiButton.Disabled = telephone.CurrentState is not TelephoneState.Idle;
ActivateProjectorButton.Disabled = telephone.CurrentState is not TelephoneState.Idle;
// Update control visibility
FetchingAvailableHolopadsContainer.Visible = ContactsList.ChildCount == 0;
ActiveCallControlsContainer.Visible = telephone.CurrentState is not TelephoneState.Idle || _currentUiKey is HolopadUiKey.AiRequestWindow;
CallPlacementControlsContainer.Visible = !ActiveCallControlsContainer.Visible;
CallerIdContainer.Visible = telephone.CurrentState is TelephoneState.Ringing;
AnswerCallButton.Visible = telephone.CurrentState is TelephoneState.Ringing;
}
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
if (_timing.CurTime < _nextUpdate)
return;
_nextUpdate = _timing.CurTime + _updateDelay;
UpdateAppearance();
}
private sealed class HolopadContactButton : Button
{
public NetEntity NetEntity;
public HolopadContactButton()
{
HorizontalExpand = true;
SetHeight = 32;
Margin = new Thickness(0f, 1f, 0f, 1f);
ReservesSpace = false;
}
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);
}
}