Files
wwdpublic/Content.Server/_Lavaland/Shuttles/Systems/DockingConsoleSystem.cs
Solaris 58d747cdc5 Lavaland Shuttle Fix (#1948)
# Description

The bug in question is the one where if you return from Lavaland the
shuttle would not dock to the station but instead send you approximately
around the station.

I replaced the original function and lookup completely where when the
station and lavaland are initialized it will add them to destinations
and then use a list to warp to the gridUIDs. This allows for more
consistent warping and less time for looking up what the largest grid
is. This fix also fixes the issue of smaller maps like Northway where a
wreck could be much larger than the station and would not warp to it.

Here is the new format for the mining shuttle console

![image](https://github.com/user-attachments/assets/31fa9e54-5033-46ca-90fc-ae916e9638fa)
---

# TODO

- [x] Rework the shuttle, adding destinations to make it more modular.

---

# Changelog

🆑
- fix: NT has finally patched the mining shuttle and now docks to the
station on returning from lavaland.

---------

Co-authored-by: Nathaniel Adams <60526456+Nathaniel-Adams@users.noreply.github.com>
(cherry picked from commit 7492387f78be4c0dcd0150b190d58df9c10b2436)
2025-03-21 16:57:02 +03:00

232 lines
7.7 KiB
C#

using Content.Server._Lavaland.Procedural.Components;
using Content.Server.Shuttles.Components;
using Content.Server.Shuttles.Events;
using Content.Server.Shuttles.Systems;
using Content.Shared._Lavaland.Shuttles;
using Content.Shared._Lavaland.Shuttles.Components;
using Content.Shared._Lavaland.Shuttles.Systems;
using Content.Server.Station.Components;
using Content.Server.Station.Systems;
using Content.Shared.Shuttles.Components;
using Content.Shared.Shuttles.Systems;
using Content.Shared.Timing;
using Content.Shared.Whitelist;
using Robust.Server.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Utility;
using Timer = Robust.Shared.Timing.Timer;
namespace Content.Server._Lavaland.Shuttles.Systems;
public sealed class DockingConsoleSystem : SharedDockingConsoleSystem
{
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
[Dependency] private readonly SharedUserInterfaceSystem _ui = default!;
[Dependency] private readonly ShuttleSystem _shuttle = default!;
[Dependency] private readonly MapLoaderSystem _mapLoader = default!;
[Dependency] private readonly IMapManager _mapMan = default!;
[Dependency] private readonly MapSystem _mapSystem = default!;
[Dependency] private readonly StationSystem _station = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<DockingConsoleComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<DockEvent>(OnDock);
SubscribeLocalEvent<UndockEvent>(OnUndock);
SubscribeLocalEvent<FTLCompletedEvent>(OnFTLCompleted);
Subs.BuiEvents<DockingConsoleComponent>(DockingConsoleUiKey.Key,
subs =>
{
subs.Event<BoundUIOpenedEvent>(OnOpened);
subs.Event<DockingConsoleFTLMessage>(OnFTL);
subs.Event<DockingConsoleShuttleCheckMessage>(OnCallShuttle);
});
}
private void OnMapInit(Entity<DockingConsoleComponent> ent, ref MapInitEvent args)
{
UpdateShuttle(ent);
UpdateUI(ent);
Dirty(ent);
}
private void OnDock(DockEvent args)
{
UpdateConsoles(args.GridAUid, args.GridBUid);
}
private void OnFTLCompleted(ref FTLCompletedEvent args)
{
// Update the state after the cooldown. Shitcode because
// no events are raised on FTL cooldown completion
var ent = args.Entity;
if (!TryComp<FTLComponent>(ent, out var ftl))
return; // how?
Timer.Spawn(ftl.StateTime.Length + TimeSpan.FromSeconds(1), () => UpdateConsolesUsing(ent));
Dirty(ent, ftl);
}
private void OnUndock(UndockEvent args)
{
UpdateConsoles(args.GridAUid, args.GridBUid);
}
private void OnOpened(Entity<DockingConsoleComponent> ent, ref BoundUIOpenedEvent args)
{
if (TerminatingOrDeleted(ent.Comp.Shuttle))
UpdateShuttle(ent);
UpdateUI(ent);
}
private void UpdateConsoles(EntityUid gridA, EntityUid gridB)
{
UpdateConsolesUsing(gridA);
UpdateConsolesUsing(gridB);
}
/// <summary>
/// Update the UI of every console that is using a certain shuttle.
/// </summary>
public void UpdateConsolesUsing(EntityUid shuttle)
{
if (!HasComp<DockingShuttleComponent>(shuttle))
return;
var query = EntityQueryEnumerator<DockingConsoleComponent>();
while (query.MoveNext(out var uid, out var comp))
{
if (comp.Shuttle == shuttle)
UpdateUI((uid, comp));
}
}
public void UpdateUI(Entity<DockingConsoleComponent> ent)
{
if (ent.Comp.Shuttle is not {} shuttle)
return;
var ftlState = FTLState.Available;
StartEndTime ftlTime = default;
List<DockingDestination> destinations = new();
if (TryComp<FTLComponent>(shuttle, out var ftl))
{
ftlState = ftl.State;
ftlTime = _shuttle.GetStateTime(ftl);
}
if (TryComp<DockingShuttleComponent>(shuttle, out var docking))
destinations = docking.Destinations;
var state = new DockingConsoleState(ftlState, ftlTime, destinations);
_ui.SetUiState(ent.Owner, DockingConsoleUiKey.Key, state);
}
private void OnFTL(Entity<DockingConsoleComponent> ent, ref DockingConsoleFTLMessage args)
{
if (ent.Comp.Shuttle is not {} shuttle || !TryComp<DockingShuttleComponent>(shuttle, out var docking))
return;
if (args.Index < 0 || args.Index > docking.Destinations.Count)
return;
var grid = docking.LocationUID[args.Index];
// can't FTL if your already at the grid
if (docking.currentlocation == grid.Id)
return;
RaiseLocalEvent(shuttle, new ShuttleLocationChangeEvent(grid.Id), false);
Log.Debug($"{ToPrettyString(args.Actor):user} is FTL-docking {ToPrettyString(shuttle):shuttle} to {ToPrettyString(grid):grid}");
// Set new current location and FTL!
_shuttle.FTLToDock(shuttle, Comp<ShuttleComponent>(shuttle), grid, priorityTag: docking.DockTag);
}
private const string MiningShuttlePath = "/Maps/_Lavaland/mining.yml";
/// <summary>
/// Load a new mining shuttle if it still doesn't exist
/// </summary>
private void OnCallShuttle(Entity<DockingConsoleComponent> ent, ref DockingConsoleShuttleCheckMessage args)
{
if (ent.Comp.Shuttle != null || UpdateShuttle(ent) || HasComp<DockingShuttleComponent>(ent.Comp.Shuttle))
return;
_mapSystem.CreateMap(out var dummyMap);
_mapLoader.TryLoad(dummyMap, MiningShuttlePath, out _);
// Find the target
var targetMap = Transform(ent).MapID;
if (Transform(ent).GridUid is not {} grid)
return;
// Find the mining shuttle
var shuttle = _mapMan.GetAllGrids(dummyMap).FirstOrNull(x => HasComp<DockingShuttleComponent>(x));
if (!TryComp<DockingShuttleComponent>(shuttle, out var docking))
{
Log.Error("Failed to call Mining shuttle since it failed to load.");
return;
}
// Add the station of the calling console
var shuttleUid = ent.Comp.Shuttle;
if (!TryComp<DockingShuttleComponent>(shuttleUid, out var shuttleComp))
return;
if (shuttleComp.Station == null)
{
var targetUid = Transform(ent).MapUid;
if (targetUid == null)
return;
RaiseLocalEvent(shuttleUid.Value, new ShuttleAddStationEvent(targetUid.Value, targetMap, grid), false);
}
// Finally FTL
_shuttle.FTLToDock(shuttle.Value, Comp<ShuttleComponent>(shuttle.Value), grid, priorityTag: docking.DockTag);
UpdateShuttle(ent);
UpdateUI(ent);
Dirty(ent);
// shitcode because funny
Timer.Spawn(TimeSpan.FromSeconds(15), () => _mapMan.DeleteMap(dummyMap));
}
/// <summary>
/// Tries to connect to some mining shuttle on init.
/// Returns true on success.
/// </summary>
public bool UpdateShuttle(Entity<DockingConsoleComponent> ent)
{
var hadShuttle = ent.Comp.HasShuttle;
ent.Comp.Shuttle = FindShuttle(ent.Comp.ShuttleWhitelist);
ent.Comp.HasShuttle = ent.Comp.Shuttle != null;
if (ent.Comp.HasShuttle != hadShuttle)
Dirty(ent);
return ent.Comp.HasShuttle;
}
private EntityUid? FindShuttle(EntityWhitelist whitelist)
{
var query = EntityQueryEnumerator<DockingShuttleComponent>();
while (query.MoveNext(out var uid, out _))
{
if (_whitelist.IsValid(whitelist, uid))
return uid;
}
return null;
}
}