CatStation/Content.Server/Screens/Systems/ScreenSystem.cs
Whatstone 06ebdd462b
Cleanup: Rewrite TextScreenSystem. Timers now wrap the text screen. (#45784)
* Half the way to making the text screen less jank

* quarter-assed timer

* partial update

* fleshing out timing & scrolling in update

* common IGameTiming

* Back to AppearanceData

* partial appearance data system

* funny timer states

* mostly working timer scroll (damned edge pixels)

* screen: scroll offset + hide

* SignalTimer: remove time set on text update

* extend for timer

* TextScreenVisuals: cleanup

* restore old names

* Return old names, write more comments

* fix textscreensystem ref, consistent timer frame

* comments, "TextScreenVisualsComponent"

* TextScreenTimerVisualsComponent, diff reduction

* region

* system order

* no "client-side" comments

* small bugfixes & cleanup with devicenet refactor

* consolidate string formatting

* Restore ETA/ETD text

* No need for DefaultText for regular screen text

* Address Pok's suggestions

Co-authored-by: Pok27 <113675512+Pok27@users.noreply.github.com>

* formatting

* pok suggesto round 2, blyatin-1

also clean up the string vs. display value mess

* TextScreenSystem.GetString -> TimerDisplay.ToStr

* D2 begone!

* constants to component values

* comment update + SignalTimerWindow whitespace

* a touch of numerical safety

fixes a few errors from pasting huge text

* fields aren't properties :^)

* DataDef time

* Update TextScreenSystem.cs

* Update TextScreenTimerVisualsComponent.cs

* return the pause, bunch of comments, missed a comp

---------

Co-authored-by: Pok27 <113675512+Pok27@users.noreply.github.com>
2026-10-02 22:36:32 +00:00

109 lines
4.3 KiB
C#

using Content.Server.Screens.Components;
using Content.Shared.DeviceNetwork.Components;
using Content.Shared.DeviceNetwork.Events;
using Content.Shared.RoundEnd;
using Content.Shared.Screens;
using Content.Shared.TextScreen;
using Robust.Shared.Timing;
namespace Content.Server.Screens.Systems;
/// <summary>
/// Controls the wallmounted screens on stations and shuttles displaying e.g. FTL duration, ETA
/// </summary>
public sealed partial class ScreenSystem : EntitySystem
{
[Dependency] private IGameTiming _gameTiming = default!;
[Dependency] private SharedAppearanceSystem _appearance = default!;
[Dependency] private EntityQuery<AppearanceComponent> _appearanceQuery;
/// <summary>
/// Send a text update to every screen on the same map as the originating device.
/// </summary>
[SubscribeLocalEvent]
private void OnScreenText(Entity<ScreenComponent> ent, ref DeviceNetworkPacketEvent<ScreenTextPayload> args)
{
var text = args.Data.Text;
if (text == null)
return;
if (!_appearanceQuery.TryComp(ent, out var appearance))
return;
// don't allow text updates if there's an active timer
// (and just check here so the server doesn't have to track them)
if (_appearance.TryGetData(ent, TextScreenVisuals.TargetTime, out TimeSpan target, appearance)
&& target > _gameTiming.CurTime)
return;
var screenMap = Transform(ent).MapUid;
if (screenMap == null)
return;
_appearance.SetData(ent, TextScreenVisuals.ScreenText, text, appearance);
_appearance.SetData(ent, TextScreenVisuals.ScreenTextTime, _gameTiming.CurTime, appearance);
}
/// <summary>
/// Determines if/how a timer packet affects this screen.
/// </summary>
/// <remarks>
/// Currently there are 2 broadcast domains: Arrivals, and every other screen.
/// Domain is determined by the <see cref="DeviceNetworkComponent.TransmitFrequencyId"/> on each timer.
/// Each broadcast domain is divided into subnets. Screen MapUid determines subnet.
/// Subnets are the shuttle, source, and dest. Source/dest change each jump.
/// This is required to send different timers to the shuttle/terminal/station.
/// </remarks>
[SubscribeLocalEvent]
private void OnShuttleTimer(Entity<ScreenComponent> ent, ref DeviceNetworkPacketEvent<ScreenShuttlePayload> args)
{
var payload = args.Data;
var timerXform = Transform(ent);
// no false positives.
if (timerXform.MapUid == null)
return;
// Need to have have the AppearanceComponent to do anything.
if (!_appearanceQuery.TryComp(ent, out var appearance))
return;
var text = payload.OverrideText;
TimeSpan time;
switch (timerXform.MapUid)
{
// sometimes the timer transforms on FTL shuttles have a hyperspace mapent, so matching by grid works as a fallback.
case var local when local == payload.Shuttle || timerXform.GridUid == payload.Shuttle:
time = payload.ShuttleTime;
text ??= GetShuttleScreenText(ref args);
break;
case var origin when origin == payload.SourceMap:
time = payload.SourceTime;
text ??= GetShuttleScreenText(ref args);
break;
case var remote when remote == payload.DestinationMap:
time = payload.DestinationTime;
text ??= ShuttleTimerMasks.ETA;
break;
default:
return;
}
_appearance.SetData(ent, TextScreenVisuals.TargetTime, _gameTiming.CurTime + time, appearance);
_appearance.SetData(ent, TextScreenVisuals.ScreenTextTime, _gameTiming.CurTime, appearance);
_appearance.SetData(ent, TextScreenVisuals.ScreenText, text, appearance);
if (payload.OverrideColor != null)
_appearance.SetData(ent, TextScreenVisuals.Color, payload.OverrideColor, appearance);
}
/// <summary>
/// Returns the localization key for ETA or ETD, depending on if the shuttle is currently docked.
/// </summary>
private string GetShuttleScreenText(ref DeviceNetworkPacketEvent<ScreenShuttlePayload> args)
{
return args.Data.Docked ? ShuttleTimerMasks.ETD : ShuttleTimerMasks.ETA;
}
}