| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356 |
- using System.Linq;
- using Content.Shared.Containers;
- using Content.Shared.Examine;
- using Content.Shared.GameTicking;
- using Content.Shared.Popups;
- using JetBrains.Annotations;
- using Robust.Client.Graphics;
- using Robust.Client.Input;
- using Robust.Client.Player;
- using Robust.Client.UserInterface;
- using Robust.Shared.Collections;
- using Robust.Shared.Configuration;
- using Robust.Shared.Map;
- using Robust.Shared.Player;
- using Robust.Shared.Prototypes;
- using Robust.Shared.Replays;
- using Robust.Shared.Timing;
- namespace Content.Client.Popups
- {
- public sealed class PopupSystem : SharedPopupSystem
- {
- [Dependency] private readonly IConfigurationManager _configManager = default!;
- [Dependency] private readonly IInputManager _inputManager = default!;
- [Dependency] private readonly IOverlayManager _overlay = default!;
- [Dependency] private readonly IPlayerManager _playerManager = default!;
- [Dependency] private readonly IPrototypeManager _prototype = default!;
- [Dependency] private readonly IGameTiming _timing = default!;
- [Dependency] private readonly IUserInterfaceManager _uiManager = default!;
- [Dependency] private readonly IReplayRecordingManager _replayRecording = default!;
- [Dependency] private readonly ExamineSystemShared _examine = default!;
- [Dependency] private readonly SharedTransformSystem _transform = default!;
- public IReadOnlyCollection<WorldPopupLabel> WorldLabels => _aliveWorldLabels.Values;
- public IReadOnlyCollection<CursorPopupLabel> CursorLabels => _aliveCursorLabels.Values;
- private readonly Dictionary<WorldPopupData, WorldPopupLabel> _aliveWorldLabels = new();
- private readonly Dictionary<CursorPopupData, CursorPopupLabel> _aliveCursorLabels = new();
- public const float MinimumPopupLifetime = 0.7f;
- public const float MaximumPopupLifetime = 5f;
- public const float PopupLifetimePerCharacter = 0.04f;
- public override void Initialize()
- {
- SubscribeNetworkEvent<PopupCursorEvent>(OnPopupCursorEvent);
- SubscribeNetworkEvent<PopupCoordinatesEvent>(OnPopupCoordinatesEvent);
- SubscribeNetworkEvent<PopupEntityEvent>(OnPopupEntityEvent);
- SubscribeNetworkEvent<RoundRestartCleanupEvent>(OnRoundRestart);
- _overlay
- .AddOverlay(new PopupOverlay(
- _configManager,
- EntityManager,
- _playerManager,
- _prototype,
- _uiManager,
- _uiManager.GetUIController<PopupUIController>(),
- _examine,
- _transform,
- this));
- }
- public override void Shutdown()
- {
- base.Shutdown();
- _overlay
- .RemoveOverlay<PopupOverlay>();
- }
- private void WrapAndRepeatPopup(PopupLabel existingLabel, string popupMessage)
- {
- existingLabel.TotalTime = 0;
- existingLabel.Repeats += 1;
- existingLabel.Text = Loc.GetString("popup-system-repeated-popup-stacking-wrap",
- ("popup-message", popupMessage),
- ("count", existingLabel.Repeats));
- }
- private void PopupMessage(string? message, PopupType type, EntityCoordinates coordinates, EntityUid? entity, bool recordReplay)
- {
- if (message == null)
- return;
- if (recordReplay && _replayRecording.IsRecording)
- {
- if (entity != null)
- _replayRecording.RecordClientMessage(new PopupEntityEvent(message, type, GetNetEntity(entity.Value)));
- else
- _replayRecording.RecordClientMessage(new PopupCoordinatesEvent(message, type, GetNetCoordinates(coordinates)));
- }
- var popupData = new WorldPopupData(message, type, coordinates, entity);
- if (_aliveWorldLabels.TryGetValue(popupData, out var existingLabel))
- {
- WrapAndRepeatPopup(existingLabel, popupData.Message);
- return;
- }
- var label = new WorldPopupLabel(coordinates)
- {
- Text = message,
- Type = type,
- };
- _aliveWorldLabels.Add(popupData, label);
- }
- #region Abstract Method Implementations
- public override void PopupCoordinates(string? message, EntityCoordinates coordinates, PopupType type = PopupType.Small)
- {
- PopupMessage(message, type, coordinates, null, true);
- }
- public override void PopupCoordinates(string? message, EntityCoordinates coordinates, ICommonSession recipient, PopupType type = PopupType.Small)
- {
- if (_playerManager.LocalSession == recipient)
- PopupMessage(message, type, coordinates, null, true);
- }
- public override void PopupCoordinates(string? message, EntityCoordinates coordinates, EntityUid recipient, PopupType type = PopupType.Small)
- {
- if (_playerManager.LocalEntity == recipient)
- PopupMessage(message, type, coordinates, null, true);
- }
- public override void PopupPredictedCoordinates(string? message, EntityCoordinates coordinates, EntityUid? recipient, PopupType type = PopupType.Small)
- {
- if (recipient != null && _timing.IsFirstTimePredicted)
- PopupCoordinates(message, coordinates, recipient.Value, type);
- }
- private void PopupCursorInternal(string? message, PopupType type, bool recordReplay)
- {
- if (message == null)
- return;
- if (recordReplay && _replayRecording.IsRecording)
- _replayRecording.RecordClientMessage(new PopupCursorEvent(message, type));
- var popupData = new CursorPopupData(message, type);
- if (_aliveCursorLabels.TryGetValue(popupData, out var existingLabel))
- {
- WrapAndRepeatPopup(existingLabel, popupData.Message);
- return;
- }
- var label = new CursorPopupLabel(_inputManager.MouseScreenPosition)
- {
- Text = message,
- Type = type,
- };
- _aliveCursorLabels.Add(popupData, label);
- }
- public override void PopupCursor(string? message, PopupType type = PopupType.Small)
- {
- if (!_timing.IsFirstTimePredicted)
- return;
- PopupCursorInternal(message, type, true);
- }
- public override void PopupCursor(string? message, ICommonSession recipient, PopupType type = PopupType.Small)
- {
- if (_playerManager.LocalSession == recipient)
- PopupCursor(message, type);
- }
- public override void PopupCursor(string? message, EntityUid recipient, PopupType type = PopupType.Small)
- {
- if (_playerManager.LocalEntity == recipient)
- PopupCursor(message, type);
- }
- public override void PopupCoordinates(string? message, EntityCoordinates coordinates, Filter filter, bool replayRecord, PopupType type = PopupType.Small)
- {
- PopupCoordinates(message, coordinates, type);
- }
- public override void PopupEntity(string? message, EntityUid uid, EntityUid recipient, PopupType type = PopupType.Small)
- {
- if (_playerManager.LocalEntity == recipient)
- PopupEntity(message, uid, type);
- }
- public override void PopupEntity(string? message, EntityUid uid, ICommonSession recipient, PopupType type = PopupType.Small)
- {
- if (_playerManager.LocalSession == recipient)
- PopupEntity(message, uid, type);
- }
- public override void PopupEntity(string? message, EntityUid uid, Filter filter, bool recordReplay, PopupType type = PopupType.Small)
- {
- if (!filter.Recipients.Contains(_playerManager.LocalSession))
- return;
- PopupEntity(message, uid, type);
- }
- public override void PopupClient(string? message, EntityUid? recipient, PopupType type = PopupType.Small)
- {
- if (recipient == null)
- return;
- if (_timing.IsFirstTimePredicted)
- PopupCursor(message, recipient.Value, type);
- }
- public override void PopupClient(string? message, EntityUid uid, EntityUid? recipient, PopupType type = PopupType.Small)
- {
- if (recipient == null)
- return;
- if (_timing.IsFirstTimePredicted)
- PopupEntity(message, uid, recipient.Value, type);
- }
- public override void PopupClient(string? message, EntityCoordinates coordinates, EntityUid? recipient, PopupType type = PopupType.Small)
- {
- if (recipient == null)
- return;
- if (_timing.IsFirstTimePredicted)
- PopupCoordinates(message, coordinates, recipient.Value, type);
- }
- public override void PopupEntity(string? message, EntityUid uid, PopupType type = PopupType.Small)
- {
- if (TryComp(uid, out TransformComponent? transform))
- PopupMessage(message, type, transform.Coordinates, uid, true);
- }
- public override void PopupPredicted(string? message, EntityUid uid, EntityUid? recipient, PopupType type = PopupType.Small)
- {
- if (recipient != null && _timing.IsFirstTimePredicted)
- PopupEntity(message, uid, recipient.Value, type);
- }
- public override void PopupPredicted(string? recipientMessage, string? othersMessage, EntityUid uid, EntityUid? recipient, PopupType type = PopupType.Small)
- {
- if (recipient != null && _timing.IsFirstTimePredicted)
- PopupEntity(recipientMessage, uid, recipient.Value, type);
- }
- #endregion
- #region Network Event Handlers
- private void OnPopupCursorEvent(PopupCursorEvent ev)
- {
- PopupCursorInternal(ev.Message, ev.Type, false);
- }
- private void OnPopupCoordinatesEvent(PopupCoordinatesEvent ev)
- {
- PopupMessage(ev.Message, ev.Type, GetCoordinates(ev.Coordinates), null, false);
- }
- private void OnPopupEntityEvent(PopupEntityEvent ev)
- {
- var entity = GetEntity(ev.Uid);
- if (TryComp(entity, out TransformComponent? transform))
- PopupMessage(ev.Message, ev.Type, transform.Coordinates, entity, false);
- }
- private void OnRoundRestart(RoundRestartCleanupEvent ev)
- {
- _aliveCursorLabels.Clear();
- _aliveWorldLabels.Clear();
- }
- #endregion
- public static float GetPopupLifetime(PopupLabel label)
- {
- return Math.Clamp(PopupLifetimePerCharacter * label.Text.Length,
- MinimumPopupLifetime,
- MaximumPopupLifetime);
- }
- public override void FrameUpdate(float frameTime)
- {
- if (_aliveWorldLabels.Count == 0 && _aliveCursorLabels.Count == 0)
- return;
- if (_aliveWorldLabels.Count > 0)
- {
- var aliveWorldToRemove = new ValueList<WorldPopupData>();
- foreach (var (data, label) in _aliveWorldLabels)
- {
- label.TotalTime += frameTime;
- if (label.TotalTime > GetPopupLifetime(label) || Deleted(label.InitialPos.EntityId))
- {
- aliveWorldToRemove.Add(data);
- }
- }
- foreach (var data in aliveWorldToRemove)
- {
- _aliveWorldLabels.Remove(data);
- }
- }
- if (_aliveCursorLabels.Count > 0)
- {
- var aliveCursorToRemove = new ValueList<CursorPopupData>();
- foreach (var (data, label) in _aliveCursorLabels)
- {
- label.TotalTime += frameTime;
- if (label.TotalTime > GetPopupLifetime(label))
- {
- aliveCursorToRemove.Add(data);
- }
- }
- foreach (var data in aliveCursorToRemove)
- {
- _aliveCursorLabels.Remove(data);
- }
- }
- }
- public abstract class PopupLabel
- {
- public PopupType Type = PopupType.Small;
- public string Text { get; set; } = string.Empty;
- public float TotalTime { get; set; }
- public int Repeats = 1;
- }
- public sealed class WorldPopupLabel(EntityCoordinates coordinates) : PopupLabel
- {
- /// <summary>
- /// The original EntityCoordinates of the label.
- /// </summary>
- public EntityCoordinates InitialPos = coordinates;
- }
- public sealed class CursorPopupLabel(ScreenCoordinates screenCoords) : PopupLabel
- {
- public ScreenCoordinates InitialPos = screenCoords;
- }
- [UsedImplicitly]
- private record struct WorldPopupData(
- string Message,
- PopupType Type,
- EntityCoordinates Coordinates,
- EntityUid? Entity);
- [UsedImplicitly]
- private record struct CursorPopupData(
- string Message,
- PopupType Type);
- }
- }
|