Cargo shuttle changes (#14363)
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user