Cargo shuttle changes (#14363)

This commit is contained in:
metalgearsloth
2023-03-23 16:10:49 +11:00
committed by GitHub
parent 7f4bb7fe8a
commit 569f30b721
26 changed files with 523 additions and 628 deletions

View File

@@ -17,20 +17,16 @@ using Content.Shared.Cargo.Prototypes;
using Content.Shared.CCVar;
using Content.Shared.Dataset;
using Content.Shared.GameTicking;
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
using Content.Shared.Whitelist;
using Robust.Server.GameObjects;
using Robust.Server.Maps;
using Robust.Shared.Audio;
using Robust.Shared.Configuration;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Player;
using Robust.Shared.Random;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using Robust.Shared.Prototypes;
using Content.Shared.Coordinates;
using Content.Shared.Mobs.Components;
using Robust.Shared.Map.Components;
namespace Content.Server.Cargo.Systems;
@@ -40,22 +36,19 @@ public sealed partial class CargoSystem
* Handles cargo shuttle mechanics, including cargo shuttle consoles.
*/
[Dependency] private readonly IComponentFactory _factory = default!;
[Dependency] private readonly IConfigurationManager _configManager = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly MapLoaderSystem _map = default!;
[Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly PricingSystem _pricing = default!;
[Dependency] private readonly ShuttleConsoleSystem _console = default!;
[Dependency] private readonly ShuttleSystem _shuttle = default!;
[Dependency] private readonly StackSystem _stack = default!;
public MapId? CargoMap { get; private set; }
private const float CallOffset = 50f;
private int _index;
/// <summary>
@@ -69,10 +62,11 @@ public sealed partial class CargoSystem
// Don't want to immediately call this as shuttles will get setup in the natural course of things.
_configManager.OnValueChanged(CCVars.CargoShuttles, SetCargoShuttleEnabled);
SubscribeLocalEvent<CargoShuttleComponent, MoveEvent>(OnCargoShuttleMove);
SubscribeLocalEvent<CargoShuttleComponent, FTLStartedEvent>(OnCargoFTLStarted);
SubscribeLocalEvent<CargoShuttleComponent, FTLCompletedEvent>(OnCargoFTLCompleted);
SubscribeLocalEvent<CargoShuttleComponent, FTLTagEvent>(OnCargoFTLTag);
SubscribeLocalEvent<CargoShuttleConsoleComponent, ComponentStartup>(OnCargoShuttleConsoleStartup);
SubscribeLocalEvent<CargoShuttleConsoleComponent, CargoCallShuttleMessage>(OnCargoShuttleCall);
SubscribeLocalEvent<CargoShuttleConsoleComponent, CargoRecallShuttleMessage>(RecallCargoShuttle);
SubscribeLocalEvent<CargoPalletConsoleComponent, CargoPalletSellMessage>(OnPalletSale);
SubscribeLocalEvent<CargoPalletConsoleComponent, CargoPalletAppraiseMessage>(OnPalletAppraise);
@@ -87,6 +81,16 @@ public sealed partial class CargoSystem
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundRestart);
}
private void OnCargoFTLTag(EntityUid uid, CargoShuttleComponent component, ref FTLTagEvent args)
{
if (args.Handled)
return;
// Just saves mappers forgetting.
args.Handled = true;
args.Tag = "DockCargo";
}
private void ShutdownShuttle()
{
_configManager.UnsubValueChanged(CCVars.CargoShuttles, SetCargoShuttleEnabled);
@@ -94,7 +98,9 @@ public sealed partial class CargoSystem
private void SetCargoShuttleEnabled(bool value)
{
if (_enabled == value) return;
if (_enabled == value)
return;
_enabled = value;
if (value)
@@ -116,7 +122,7 @@ public sealed partial class CargoSystem
private void OnCargoPilotConsoleOpen(EntityUid uid, CargoPilotConsoleComponent component, AfterActivatableUIOpenEvent args)
{
component.Entity = GetShuttleConsole(component);
component.Entity = GetShuttleConsole(uid);
}
private void OnCargoPilotConsoleClose(EntityUid uid, CargoPilotConsoleComponent component, BoundUIClosedEvent args)
@@ -126,30 +132,50 @@ public sealed partial class CargoSystem
private void OnCargoGetConsole(EntityUid uid, CargoPilotConsoleComponent component, ref ConsoleShuttleEvent args)
{
args.Console = GetShuttleConsole(component);
args.Console = GetShuttleConsole(uid);
}
private EntityUid? GetShuttleConsole(CargoPilotConsoleComponent component)
private EntityUid? GetShuttleConsole(EntityUid uid)
{
var stationUid = _station.GetOwningStation(component.Owner);
var stationUid = _station.GetOwningStation(uid);
if (!TryComp<StationCargoOrderDatabaseComponent>(stationUid, out var orderDatabase) ||
!TryComp<CargoShuttleComponent>(orderDatabase.Shuttle, out var shuttle)) return null;
!TryComp<CargoShuttleComponent>(orderDatabase.Shuttle, out var shuttle))
{
return null;
}
return GetShuttleConsole(shuttle);
return GetShuttleConsole(orderDatabase.Shuttle.Value, shuttle);
}
#endregion
#region Console
private void UpdateShuttleCargoConsoles(CargoShuttleComponent component)
private void UpdateCargoShuttleConsoles(CargoShuttleComponent component)
{
foreach (var console in EntityQuery<CargoShuttleConsoleComponent>(true))
// Update pilot consoles that are already open.
var pilotConsoleQuery = AllEntityQuery<CargoPilotConsoleComponent>();
while (pilotConsoleQuery.MoveNext(out var uid, out var console))
{
var stationUid = _station.GetOwningStation(console.Owner);
if (stationUid != component.Station) continue;
UpdateShuttleState(console, stationUid);
var stationUid = _station.GetOwningStation(uid);
if (stationUid == null || stationUid != component.Station)
continue;
console.Entity = GetShuttleConsole(stationUid.Value);
}
// Update order consoles.
var shuttleConsoleQuery = AllEntityQuery<CargoShuttleConsoleComponent>();
while (shuttleConsoleQuery.MoveNext(out var uid, out var console))
{
var stationUid = _station.GetOwningStation(uid);
if (stationUid != component.Station)
continue;
UpdateShuttleState(uid, console, stationUid);
}
}
@@ -198,10 +224,10 @@ public sealed partial class CargoSystem
private void OnCargoShuttleConsoleStartup(EntityUid uid, CargoShuttleConsoleComponent component, ComponentStartup args)
{
var station = _station.GetOwningStation(uid);
UpdateShuttleState(component, station);
UpdateShuttleState(uid, component, station);
}
private void UpdateShuttleState(CargoShuttleConsoleComponent component, EntityUid? station = null)
private void UpdateShuttleState(EntityUid uid, CargoShuttleConsoleComponent component, EntityUid? station = null)
{
TryComp<StationCargoOrderDatabaseComponent>(station, out var orderDatabase);
TryComp<CargoShuttleComponent>(orderDatabase?.Shuttle, out var shuttle);
@@ -209,12 +235,10 @@ public sealed partial class CargoSystem
var orders = GetProjectedOrders(orderDatabase, shuttle);
var shuttleName = orderDatabase?.Shuttle != null ? MetaData(orderDatabase.Shuttle.Value).EntityName : string.Empty;
_uiSystem.GetUiOrNull(component.Owner, CargoConsoleUiKey.Shuttle)?.SetState(
_uiSystem.GetUiOrNull(uid, CargoConsoleUiKey.Shuttle)?.SetState(
new CargoShuttleConsoleBoundUserInterfaceState(
station != null ? MetaData(station.Value).EntityName : Loc.GetString("cargo-shuttle-console-station-unknown"),
string.IsNullOrEmpty(shuttleName) ? Loc.GetString("cargo-shuttle-console-shuttle-not-found") : shuttleName,
_shuttle.CanFTL(shuttle?.Owner, out _),
shuttle?.NextCall,
orders));
}
@@ -222,32 +246,21 @@ public sealed partial class CargoSystem
#region Shuttle
public EntityUid? GetShuttleConsole(CargoShuttleComponent component)
public EntityUid? GetShuttleConsole(EntityUid uid, CargoShuttleComponent component)
{
foreach (var (comp, xform) in EntityQuery<ShuttleConsoleComponent, TransformComponent>(true))
var query = AllEntityQuery<ShuttleConsoleComponent, TransformComponent>();
while (query.MoveNext(out var cUid, out var comp, out var xform))
{
if (xform.ParentUid != component.Owner) continue;
return comp.Owner;
if (xform.ParentUid != uid)
continue;
return cUid;
}
return null;
}
private void OnCargoShuttleMove(EntityUid uid, CargoShuttleComponent component, ref MoveEvent args)
{
if (component.Station == null) return;
var oldCanRecall = component.CanRecall;
// Check if we can update the recall status.
var canRecall = _shuttle.CanFTL(uid, out _, args.Component);
if (oldCanRecall == canRecall) return;
component.CanRecall = canRecall;
_sawmill.Debug($"Updated CanRecall for {ToPrettyString(uid)}");
UpdateShuttleCargoConsoles(component);
}
/// <summary>
/// Returns the orders that can fit on the cargo shuttle.
/// </summary>
@@ -329,7 +342,10 @@ public sealed partial class CargoSystem
if (CargoMap == null ||
component.Shuttle != null ||
component.CargoShuttleProto == null) return;
component.CargoShuttleProto == null)
{
return;
}
var prototype = _protoMan.Index<CargoShuttlePrototype>(component.CargoShuttleProto);
var possibleNames = _protoMan.Index<DatasetPrototype>(prototype.NameDataset).Values;
@@ -348,11 +364,9 @@ public sealed partial class CargoSystem
xform.LocalPosition += 100 * _index;
var comp = EnsureComp<CargoShuttleComponent>(shuttleUid);
comp.Station = component.Owner;
comp.Coordinates = xform.Coordinates;
component.Shuttle = shuttleUid;
comp.NextCall = _timing.CurTime + TimeSpan.FromSeconds(comp.Cooldown);
UpdateShuttleCargoConsoles(comp);
UpdateCargoShuttleConsoles(comp);
_index++;
_sawmill.Info($"Added cargo shuttle to {ToPrettyString(shuttleUid)}");
}
@@ -400,80 +414,6 @@ public sealed partial class CargoSystem
}
}
private void SendToCargoMap(EntityUid uid, CargoShuttleComponent? component = null)
{
if (!Resolve(uid, ref component))
return;
component.NextCall = _timing.CurTime + TimeSpan.FromSeconds(component.Cooldown);
Transform(uid).Coordinates = component.Coordinates;
DebugTools.Assert(MetaData(uid).EntityPaused);
UpdateShuttleCargoConsoles(component);
_sawmill.Info($"Stashed cargo shuttle {ToPrettyString(uid)}");
}
/// <summary>
/// Retrieves a shuttle for delivery.
/// </summary>
public void CallShuttle(StationCargoOrderDatabaseComponent orderDatabase)
{
if (!TryComp<CargoShuttleComponent>(orderDatabase.Shuttle, out var shuttle))
return;
// Already called / not available
if (shuttle.NextCall == null || _timing.CurTime < shuttle.NextCall)
return;
if (!TryComp<StationDataComponent>(orderDatabase.Owner, out var stationData))
return;
var targetGrid = _station.GetLargestGrid(stationData);
// Nowhere to warp in to.
if (!TryComp<TransformComponent>(targetGrid, out var xform))
return;
shuttle.NextCall = null;
// Find a valid free area nearby to spawn in on
// TODO: Make this use hyperspace now.
var center = new Vector2();
var minRadius = 0f;
Box2? aabb = null;
var xformQuery = GetEntityQuery<TransformComponent>();
foreach (var grid in _mapManager.GetAllMapGrids(xform.MapID))
{
var worldAABB = xformQuery.GetComponent(grid.Owner).WorldMatrix.TransformBox(grid.LocalAABB);
aabb = aabb?.Union(worldAABB) ?? worldAABB;
}
if (aabb != null)
{
center = aabb.Value.Center;
minRadius = MathF.Max(aabb.Value.Width, aabb.Value.Height);
}
var offset = 0f;
if (TryComp<MapGridComponent>(orderDatabase.Shuttle, out var shuttleGrid))
{
var bounds = shuttleGrid.LocalAABB;
offset = MathF.Max(bounds.Width, bounds.Height) / 2f;
}
Transform(shuttle.Owner).Coordinates = new EntityCoordinates(xform.ParentUid,
center + _random.NextVector2(minRadius + offset, minRadius + CallOffset + offset));
DebugTools.Assert(!MetaData(shuttle.Owner).EntityPaused);
AddCargoContents(shuttle, orderDatabase);
UpdateOrders(orderDatabase);
UpdateShuttleCargoConsoles(shuttle);
_console.RefreshShuttleConsoles();
_sawmill.Info($"Retrieved cargo shuttle {ToPrettyString(shuttle.Owner)} from {ToPrettyString(orderDatabase.Owner)}");
}
private void AddCargoContents(CargoShuttleComponent shuttle, StationCargoOrderDatabaseComponent orderDatabase)
{
var xformQuery = GetEntityQuery<TransformComponent>();
@@ -510,74 +450,37 @@ public sealed partial class CargoSystem
UpdatePalletConsoleInterface(uid, component);
}
private void RecallCargoShuttle(EntityUid uid, CargoShuttleConsoleComponent component, CargoRecallShuttleMessage args)
private void OnCargoFTLStarted(EntityUid uid, CargoShuttleComponent component, ref FTLStartedEvent args)
{
var player = args.Session.AttachedEntity;
var xform = Transform(uid);
var stationUid = component.Station;
if (player == null) return;
var stationUid = _station.GetOwningStation(uid);
if (!TryComp<StationCargoOrderDatabaseComponent>(stationUid, out var orderDatabase) ||
!TryComp<StationBankAccountComponent>(stationUid, out var bank)) return;
if (!TryComp<CargoShuttleComponent>(orderDatabase.Shuttle, out var shuttle))
// Called
if (xform.MapID != CargoMap ||
!TryComp<StationCargoOrderDatabaseComponent>(stationUid, out var orderDatabase))
{
_popup.PopupEntity(Loc.GetString("cargo-no-shuttle"), args.Entity, args.Entity);
return;
}
if (!_shuttle.CanFTL(shuttle.Owner, out var reason))
{
_popup.PopupEntity(reason, args.Entity, args.Entity);
AddCargoContents(component, orderDatabase);
UpdateOrders(orderDatabase);
UpdateCargoShuttleConsoles(component);
}
private void OnCargoFTLCompleted(EntityUid uid, CargoShuttleComponent component, ref FTLCompletedEvent args)
{
var xform = Transform(uid);
// Recalled
if (xform.MapID != CargoMap)
return;
}
if (IsBlocked(shuttle))
var stationUid = component.Station;
if (TryComp<StationBankAccountComponent>(stationUid, out var bank))
{
_popup.PopupEntity(Loc.GetString("cargo-shuttle-console-organics"), player.Value, player.Value);
_audio.PlayPvs(_audio.GetSound(component.DenySound), uid);
return;
SellPallets(uid, out var amount);
bank.Balance += (int) amount;
}
SellPallets((EntityUid) orderDatabase.Shuttle, out double price);
bank.Balance += (int) price;
_console.RefreshShuttleConsoles();
SendToCargoMap(orderDatabase.Shuttle.Value);
}
/// <summary>
///
/// </summary>
/// <param name="component"></param>
private bool IsBlocked(CargoShuttleComponent component)
{
// TODO: Would be good to rate-limit this on the console.
var mobQuery = GetEntityQuery<MobStateComponent>();
var xformQuery = GetEntityQuery<TransformComponent>();
return FoundOrganics(component.Owner, mobQuery, xformQuery);
}
public bool FoundOrganics(EntityUid uid, EntityQuery<MobStateComponent> mobQuery, EntityQuery<TransformComponent> xformQuery)
{
var xform = xformQuery.GetComponent(uid);
var childEnumerator = xform.ChildEnumerator;
while (childEnumerator.MoveNext(out var child))
{
if ((mobQuery.TryGetComponent(child.Value, out var mobState) && !_mobState.IsDead(child.Value, mobState))
|| FoundOrganics(child.Value, mobQuery, xformQuery)) return true;
}
return false;
}
private void OnCargoShuttleCall(EntityUid uid, CargoShuttleConsoleComponent component, CargoCallShuttleMessage args)
{
var stationUid = _station.GetOwningStation(args.Entity);
if (!TryComp<StationCargoOrderDatabaseComponent>(stationUid, out var orderDatabase)) return;
CallShuttle(orderDatabase);
}
#endregion
@@ -600,13 +503,15 @@ public sealed partial class CargoSystem
CargoMap = null;
// Shuttle may not have been in the cargo dimension (e.g. on the station map) so need to delete.
foreach (var comp in EntityQuery<CargoShuttleComponent>())
var query = AllEntityQuery<CargoShuttleComponent>();
while (query.MoveNext(out var uid, out var comp))
{
if (TryComp<StationCargoOrderDatabaseComponent>(comp.Station, out var station))
{
station.Shuttle = null;
}
QueueDel(comp.Owner);
QueueDel(uid);
}
}
@@ -619,11 +524,23 @@ public sealed partial class CargoSystem
// It gets mapinit which is okay... buuutt we still want it paused to avoid power draining.
CargoMap = _mapManager.CreateMap();
_mapManager.SetMapPaused(CargoMap!.Value, true);
var mapUid = _mapManager.GetMapEntityId(CargoMap.Value);
var ftl = EnsureComp<FTLDestinationComponent>(_mapManager.GetMapEntityId(CargoMap.Value));
ftl.Whitelist = new EntityWhitelist()
{
Components = new[]
{
_factory.GetComponentName(typeof(CargoShuttleComponent))
}
};
MetaData(mapUid).EntityName = $"Trading post {_random.Next(1000):000}";
foreach (var comp in EntityQuery<StationCargoOrderDatabaseComponent>(true))
{
AddShuttle(comp);
}
_console.RefreshShuttleConsoles();
}
}