mirror of
https://github.com/space-syndicate/space-station-14.git
synced 2026-06-09 13:26:34 +02:00
353 lines
13 KiB
C#
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);
|
|
}
|
|
}
|