From bc097c7b4dccec9511def4536838e4070b51f0e1 Mon Sep 17 00:00:00 2001 From: Tom Leys Date: Tue, 22 Aug 2023 22:46:50 +1200 Subject: [PATCH] Teleport noobs off Arrivals shuttle to spawn (#17189) --- .../GameTicking/GameTicker.Spawning.cs | 11 ++- .../Components/ArrivalsShuttleComponent.cs | 4 + .../Shuttles/Systems/ArrivalsSystem.cs | 78 ++++++++++++++++++- .../Components/SpawnPointComponent.cs | 5 ++ .../Locale/en-US/game-ticking/game-ticker.ftl | 1 + 5 files changed, 95 insertions(+), 4 deletions(-) diff --git a/Content.Server/GameTicking/GameTicker.Spawning.cs b/Content.Server/GameTicking/GameTicker.Spawning.cs index 808bb9da13..8adbcfdffa 100644 --- a/Content.Server/GameTicking/GameTicker.Spawning.cs +++ b/Content.Server/GameTicking/GameTicker.Spawning.cs @@ -239,7 +239,16 @@ namespace Content.Server.GameTicking // We also want this message last. if (lateJoin && _arrivals.Enabled) { - _chatManager.DispatchServerMessage(player, Loc.GetString("latejoin-arrivals-direction")); + var arrival = _arrivals.NextShuttleArrival(); + if (arrival == null) + { + _chatManager.DispatchServerMessage(player, Loc.GetString("latejoin-arrivals-direction")); + } + else + { + _chatManager.DispatchServerMessage(player, Loc.GetString("latejoin-arrivals-direction-time", + ("time", $"{arrival:mm\\:ss}"))); + } } // We raise this event directed to the mob, but also broadcast it so game rules can do something now. diff --git a/Content.Server/Shuttles/Components/ArrivalsShuttleComponent.cs b/Content.Server/Shuttles/Components/ArrivalsShuttleComponent.cs index 1f6b777963..663b82e7c8 100644 --- a/Content.Server/Shuttles/Components/ArrivalsShuttleComponent.cs +++ b/Content.Server/Shuttles/Components/ArrivalsShuttleComponent.cs @@ -11,4 +11,8 @@ public sealed class ArrivalsShuttleComponent : Component [DataField("nextTransfer", customTypeSerializer:typeof(TimeOffsetSerializer))] public TimeSpan NextTransfer; + + [DataField("nextArrivalsTime", customTypeSerializer:typeof(TimeOffsetSerializer))] + public TimeSpan NextArrivalsTime; + } diff --git a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs index eeb9de8ad8..7bbe6ed108 100644 --- a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs +++ b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs @@ -16,6 +16,7 @@ using Content.Shared.Shuttles.Components; using Content.Shared.Spawners.Components; using Content.Shared.Tiles; using Robust.Server.GameObjects; +using Robust.Shared.Collections; using Robust.Shared.Configuration; using Robust.Shared.Console; using Robust.Shared.Map; @@ -154,6 +155,14 @@ public sealed class ArrivalsSystem : EntitySystem private void OnArrivalsFTL(EntityUid shuttleUid, ArrivalsShuttleComponent component, ref FTLStartedEvent args) { + if (!TryGetArrivals(out EntityUid arrivals)) + return; + + var arrivalsMapUid = Transform(arrivals).MapUid; + // Don't do anything here when leaving arrivals. + if (args.FromMapUid == arrivalsMapUid) + return; + // Any mob then yeet them off the shuttle. if (!_cfgManager.GetCVar(CCVars.ArrivalsReturns) && args.FromMapUid != null) { @@ -166,13 +175,24 @@ public sealed class ArrivalsSystem : EntitySystem var pendingQuery = AllEntityQuery(); - // Clock them in when they FTL + // We're heading from the station back to arrivals (if leaving arrivals, would have returned above). + // Process everyone who holds a PendingClockInComponent + // Note, due to way DumpChildren works, anyone who doesn't have a PendingClockInComponent gets left in space + // and will not warp. This is intended behavior. while (pendingQuery.MoveNext(out var pUid, out _, out var xform)) { - // Cheaper to iterate pending arrivals than all children - if (xform.GridUid != shuttleUid) + if (xform.GridUid == shuttleUid) + { + // Warp all players who are still on this shuttle to a spawn point. This doesn't let them return to + // arrivals. It also ensures noobs, slow players or AFK players safely leave the shuttle. + TryTeleportToMapSpawn(pUid, component.Station, xform); + } + + // Players who have remained at arrives keep their warp coupon (PendingClockInComponent) for now. + if (xform.MapUid == arrivalsMapUid) continue; + // The player has successfully left arrivals and is also not on the shuttle. Remove their warp coupon. RemCompDeferred(pUid); RemCompDeferred(pUid); } @@ -246,6 +266,35 @@ public sealed class ArrivalsSystem : EntitySystem } } + private bool TryTeleportToMapSpawn(EntityUid player, EntityUid stationId, TransformComponent? transform = null) + { + if (!Resolve(player, ref transform)) + return false; + + var points = EntityQueryEnumerator(); + var possiblePositions = new ValueList(32); + + // Find a spawnpoint on the same map as the player is already docked with now. + while ( points.MoveNext(out var uid, out var spawnPoint, out var xform)) + { + if (spawnPoint.SpawnType == SpawnPointType.LateJoin && + _station.GetOwningStation(uid, xform) == stationId) + { + // Add to list of possible spawn locations + possiblePositions.Add(xform.Coordinates); + } + } + + if (possiblePositions.Count > 0) + { + // Move the player to a random late-join spawnpoint. + _transform.SetCoordinates(player, transform, _random.Pick(possiblePositions)); + return true; + } + + return false; + } + private void OnShuttleStartup(EntityUid uid, ArrivalsShuttleComponent component, ComponentStartup args) { EnsureComp(uid); @@ -268,6 +317,20 @@ public sealed class ArrivalsSystem : EntitySystem return false; } + public TimeSpan? NextShuttleArrival() + { + var query = EntityQueryEnumerator(); + var time = TimeSpan.MaxValue; + while (query.MoveNext(out var uid, out var comp)) + { + if (comp.NextArrivalsTime < time) + time = comp.NextArrivalsTime; + } + + var duration = _timing.CurTime; + return (time < duration) ? null : time - duration; + } + public override void Update(float frameTime) { base.Update(frameTime); @@ -291,11 +354,15 @@ public sealed class ArrivalsSystem : EntitySystem if (comp.NextTransfer > curTime || !TryComp(comp.Station, out var data)) continue; + var tripTime = ShuttleSystem.DefaultTravelTime + ShuttleSystem.DefaultStartupTime ; + // Go back to arrivals source if (xform.MapUid != arrivalsXform.MapUid) { if (arrivals.IsValid()) _shuttles.FTLTravel(uid, shuttle, arrivals, dock: true); + + comp.NextArrivalsTime = _timing.CurTime + TimeSpan.FromSeconds(tripTime); } // Go to station else @@ -304,6 +371,11 @@ public sealed class ArrivalsSystem : EntitySystem if (targetGrid != null) _shuttles.FTLTravel(uid, shuttle, targetGrid.Value, dock: true); + + // The ArrivalsCooldown includes the trip there, so we only need to add the time taken for + // the trip back. + comp.NextArrivalsTime = _timing.CurTime + TimeSpan.FromSeconds( + _cfgManager.GetCVar(CCVars.ArrivalsCooldown) + tripTime); } comp.NextTransfer += TimeSpan.FromSeconds(_cfgManager.GetCVar(CCVars.ArrivalsCooldown)); diff --git a/Content.Server/Spawners/Components/SpawnPointComponent.cs b/Content.Server/Spawners/Components/SpawnPointComponent.cs index f520f06f4f..20d74f4ba9 100644 --- a/Content.Server/Spawners/Components/SpawnPointComponent.cs +++ b/Content.Server/Spawners/Components/SpawnPointComponent.cs @@ -17,6 +17,11 @@ public sealed class SpawnPointComponent : Component public SpawnPointType SpawnType { get; } = SpawnPointType.Unset; public JobPrototype? Job => string.IsNullOrEmpty(_jobId) ? null : _prototypeManager.Index(_jobId); + + public override string ToString() + { + return $"{_jobId} {SpawnType}"; + } } public enum SpawnPointType diff --git a/Resources/Locale/en-US/game-ticking/game-ticker.ftl b/Resources/Locale/en-US/game-ticking/game-ticker.ftl index 004b6aaae0..42be7de5f3 100644 --- a/Resources/Locale/en-US/game-ticking/game-ticker.ftl +++ b/Resources/Locale/en-US/game-ticking/game-ticker.ftl @@ -36,3 +36,4 @@ player-leave-message = Player {$name} left. latejoin-arrival-announcement = {$character} ({$job}) has arrived at the station! latejoin-arrival-sender = Station latejoin-arrivals-direction = A shuttle transferring you to your station will arrive shortly. +latejoin-arrivals-direction-time = A shuttle transferring you to your station will arrive in {$time}.