Add trade stations (#23863)

* puters

* Start on fulfillment

* weh

* Smol update

* FTL sound fixes or smth iunno

* Add consoles

* More tweaks

* Make it unanchorable

* final wehs

* weh

* Fix 1 test

* Shrimply bump the distance

* cat
This commit is contained in:
metalgearsloth
2024-01-19 13:02:28 +11:00
committed by GitHub
parent f69f3e0f41
commit a7388e5c05
17 changed files with 2413 additions and 2188 deletions

View File

@@ -1,9 +1,9 @@
<controls:FancyWindow xmlns="https://spacestation14.io" <controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls" xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
SetSize="300 150" SetSize="300 150"
MinSize="300 150"> MinSize="300 150"
<BoxContainer Orientation="Vertical"> Title="{Loc 'cargo-pallet-console-menu-title'}">
<BoxContainer Orientation="Vertical" Margin="5">
<BoxContainer Orientation="Horizontal"> <BoxContainer Orientation="Horizontal">
<Label Text="{Loc 'cargo-pallet-menu-appraisal-label'}" <Label Text="{Loc 'cargo-pallet-menu-appraisal-label'}"
StyleClasses="LabelKeyText" /> StyleClasses="LabelKeyText" />

View File

@@ -17,7 +17,6 @@ public sealed partial class CargoPalletMenu : FancyWindow
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
SellButton.OnPressed += OnSellPressed; SellButton.OnPressed += OnSellPressed;
AppraiseButton.OnPressed += OnAppraisePressed; AppraiseButton.OnPressed += OnAppraisePressed;
Title = Loc.GetString("cargo-pallet-console-menu-title");
} }
public void SetAppraisal(int amount) public void SetAppraisal(int amount)

View File

@@ -67,7 +67,7 @@ namespace Content.IntegrationTests.Tests.Destructible
Assert.That(spawnEntitiesBehavior.Spawn.Values.Single(), Is.EqualTo(new MinMax { Min = 1, Max = 1 })); Assert.That(spawnEntitiesBehavior.Spawn.Values.Single(), Is.EqualTo(new MinMax { Min = 1, Max = 1 }));
}); });
var entitiesInRange = sEntityManager.System<EntityLookupSystem>().GetEntitiesInRange(coordinates, 2); var entitiesInRange = sEntityManager.System<EntityLookupSystem>().GetEntitiesInRange(coordinates, 3, LookupFlags.All | LookupFlags.Approximate);
var found = false; var found = false;
foreach (var entity in entitiesInRange) foreach (var entity in entitiesInRange)
@@ -86,7 +86,7 @@ namespace Content.IntegrationTests.Tests.Destructible
break; break;
} }
Assert.That(found, Is.True); Assert.That(found, Is.True, $"Unable to find {SpawnedEntityId} nearby for destructible test; found {entitiesInRange.Count} entities.");
}); });
await pair.CleanReturnAsync(); await pair.CleanReturnAsync();
} }

View File

@@ -1,5 +1,6 @@
using Content.Shared.Cargo; using Content.Shared.Cargo;
using Content.Shared.Cargo.Prototypes; using Content.Shared.Cargo.Prototypes;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.Cargo.Components; namespace Content.Server.Cargo.Components;
@@ -30,4 +31,10 @@ public sealed partial class StationCargoOrderDatabaseComponent : Component
/// </summary> /// </summary>
[DataField("shuttle")] [DataField("shuttle")]
public EntityUid? Shuttle; public EntityUid? Shuttle;
/// <summary>
/// The paper-type prototype to spawn with the order information.
/// </summary>
[DataField]
public EntProtoId PrinterOutput = "PaperCargoInvoice";
} }

View File

@@ -0,0 +1,10 @@
namespace Content.Server.Cargo.Components;
/// <summary>
/// Target for approved orders to spawn at.
/// </summary>
[RegisterComponent]
public sealed partial class TradeStationComponent : Component
{
}

View File

@@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis;
using Content.Server.Cargo.Components; using Content.Server.Cargo.Components;
using Content.Server.Labels.Components; using Content.Server.Labels.Components;
using Content.Server.Paper; using Content.Server.Paper;
using Content.Server.Station.Components;
using Content.Shared.Cargo; using Content.Shared.Cargo;
using Content.Shared.Cargo.BUI; using Content.Shared.Cargo.BUI;
using Content.Shared.Cargo.Events; using Content.Shared.Cargo.Events;
@@ -10,6 +11,7 @@ using Content.Shared.Database;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Player; using Robust.Shared.Player;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Utility; using Robust.Shared.Utility;
namespace Content.Server.Cargo.Systems namespace Content.Server.Cargo.Systems
@@ -87,10 +89,12 @@ namespace Content.Server.Cargo.Systems
return; return;
} }
var bankAccount = GetBankAccount(uid, component); var station = _station.GetOwningStation(uid);
// No station to deduct from. // No station to deduct from.
if (!TryGetOrderDatabase(uid, out var dbUid, out var orderDatabase, component) || bankAccount == null) if (!TryComp(station, out StationBankAccountComponent? bank) ||
!TryComp(station, out StationDataComponent? stationData) ||
!TryGetOrderDatabase(station, out var orderDatabase))
{ {
ConsolePopup(args.Session, Loc.GetString("cargo-console-station-not-found")); ConsolePopup(args.Session, Loc.GetString("cargo-console-station-not-found"));
PlayDenySound(uid, component); PlayDenySound(uid, component);
@@ -136,32 +140,82 @@ namespace Content.Server.Cargo.Systems
var cost = order.Price * order.OrderQuantity; var cost = order.Price * order.OrderQuantity;
// Not enough balance // Not enough balance
if (cost > bankAccount.Balance) if (cost > bank.Balance)
{ {
ConsolePopup(args.Session, Loc.GetString("cargo-console-insufficient-funds", ("cost", cost))); ConsolePopup(args.Session, Loc.GetString("cargo-console-insufficient-funds", ("cost", cost)));
PlayDenySound(uid, component); PlayDenySound(uid, component);
return; return;
} }
// No slots at the trade station
_listEnts.Clear();
GetTradeStations(stationData, ref _listEnts);
EntityUid? tradeDestination = null;
// Try to fulfill from any station where possible, if the pad is not occupied.
foreach (var trade in _listEnts)
{
var tradePads = GetCargoPallets(trade);
_random.Shuffle(tradePads);
var freePads = GetFreeCargoPallets(trade, tradePads);
foreach (var pad in freePads)
{
var coordinates = new EntityCoordinates(trade, pad.Transform.LocalPosition);
if (FulfillOrder(order, coordinates, orderDatabase.PrinterOutput))
{
tradeDestination = trade;
break;
}
}
if (tradeDestination != null)
break;
}
if (tradeDestination == null)
{
ConsolePopup(args.Session, Loc.GetString("cargo-console-unfulfilled"));
PlayDenySound(uid, component);
return;
}
_idCardSystem.TryFindIdCard(player, out var idCard); _idCardSystem.TryFindIdCard(player, out var idCard);
// ReSharper disable once ConditionalAccessQualifierIsNonNullableAccordingToAPIContract // ReSharper disable once ConditionalAccessQualifierIsNonNullableAccordingToAPIContract
order.SetApproverData(idCard.Comp?.FullName, idCard.Comp?.JobTitle); order.SetApproverData(idCard.Comp?.FullName, idCard.Comp?.JobTitle);
_audio.PlayPvs(_audio.GetSound(component.ConfirmSound), uid); _audio.PlayPvs(component.ConfirmSound, uid);
ConsolePopup(args.Session, Loc.GetString("cargo-console-trade-station", ("destination", MetaData(tradeDestination.Value).EntityName)));
// Log order approval // Log order approval
_adminLogger.Add(LogType.Action, LogImpact.Low, _adminLogger.Add(LogType.Action, LogImpact.Low,
$"{ToPrettyString(player):user} approved order [orderId:{order.OrderId}, quantity:{order.OrderQuantity}, product:{order.ProductId}, requester:{order.Requester}, reason:{order.Reason}] with balance at {bankAccount.Balance}"); $"{ToPrettyString(player):user} approved order [orderId:{order.OrderId}, quantity:{order.OrderQuantity}, product:{order.ProductId}, requester:{order.Requester}, reason:{order.Reason}] with balance at {bank.Balance}");
DeductFunds(bankAccount, cost); DeductFunds(bank, cost);
UpdateOrders(dbUid!.Value, orderDatabase); UpdateOrders(station.Value, orderDatabase);
}
private void GetTradeStations(StationDataComponent data, ref List<EntityUid> ents)
{
foreach (var gridUid in data.Grids)
{
if (!_tradeQuery.HasComponent(gridUid))
continue;
ents.Add(gridUid);
}
} }
private void OnRemoveOrderMessage(EntityUid uid, CargoOrderConsoleComponent component, CargoConsoleRemoveOrderMessage args) private void OnRemoveOrderMessage(EntityUid uid, CargoOrderConsoleComponent component, CargoConsoleRemoveOrderMessage args)
{ {
if (!TryGetOrderDatabase(uid, out var dbUid, out var orderDatabase, component)) var station = _station.GetOwningStation(uid);
if (!TryGetOrderDatabase(station, out var orderDatabase))
return; return;
RemoveOrder(dbUid!.Value, args.OrderId, orderDatabase); RemoveOrder(station.Value, args.OrderId, orderDatabase);
} }
private void OnAddOrderMessage(EntityUid uid, CargoOrderConsoleComponent component, CargoConsoleAddOrderMessage args) private void OnAddOrderMessage(EntityUid uid, CargoOrderConsoleComponent component, CargoConsoleAddOrderMessage args)
@@ -172,11 +226,9 @@ namespace Content.Server.Cargo.Systems
if (args.Amount <= 0) if (args.Amount <= 0)
return; return;
var bank = GetBankAccount(uid, component); var stationUid = _station.GetOwningStation(uid);
if (bank == null)
return;
if (!TryGetOrderDatabase(uid, out var dbUid, out var orderDatabase, component)) if (!TryGetOrderDatabase(stationUid, out var orderDatabase))
return; return;
if (!_protoMan.TryIndex<CargoProductPrototype>(args.CargoProductId, out var product)) if (!_protoMan.TryIndex<CargoProductPrototype>(args.CargoProductId, out var product))
@@ -187,7 +239,7 @@ namespace Content.Server.Cargo.Systems
var data = GetOrderData(args, product, GenerateOrderId(orderDatabase)); var data = GetOrderData(args, product, GenerateOrderId(orderDatabase));
if (!TryAddOrder(dbUid!.Value, data, orderDatabase)) if (!TryAddOrder(stationUid.Value, data, orderDatabase))
{ {
PlayDenySound(uid, component); PlayDenySound(uid, component);
return; return;
@@ -336,10 +388,10 @@ namespace Content.Server.Cargo.Systems
public void ClearOrders(StationCargoOrderDatabaseComponent component) public void ClearOrders(StationCargoOrderDatabaseComponent component)
{ {
if (component.Orders.Count == 0) return; if (component.Orders.Count == 0)
return;
component.Orders.Clear(); component.Orders.Clear();
Dirty(component);
} }
private static bool PopFrontOrder(StationCargoOrderDatabaseComponent orderDB, [NotNullWhen(true)] out CargoOrderData? orderOut) private static bool PopFrontOrder(StationCargoOrderDatabaseComponent orderDB, [NotNullWhen(true)] out CargoOrderData? orderOut)
@@ -362,16 +414,27 @@ namespace Content.Server.Cargo.Systems
return true; return true;
} }
private bool FulfillOrder(StationCargoOrderDatabaseComponent orderDB, EntityCoordinates whereToPutIt, /// <summary>
string? paperPrototypeToPrint) /// Tries to fulfill the next outstanding order.
/// </summary>
private bool FulfillNextOrder(StationCargoOrderDatabaseComponent orderDB, EntityCoordinates spawn, string? paperProto)
{ {
if (PopFrontOrder(orderDB, out var order)) if (!PopFrontOrder(orderDB, out var order))
return false;
return FulfillOrder(order, spawn, paperProto);
}
/// <summary>
/// Fulfills the specified cargo order and spawns paper attached to it.
/// </summary>
private bool FulfillOrder(CargoOrderData order, EntityCoordinates spawn, string? paperProto)
{ {
// Create the item itself // Create the item itself
var item = Spawn(order.ProductId, whereToPutIt); var item = Spawn(order.ProductId, spawn);
// Create a sheet of paper to write the order details on // Create a sheet of paper to write the order details on
var printed = EntityManager.SpawnEntity(paperPrototypeToPrint, whereToPutIt); var printed = EntityManager.SpawnEntity(paperProto, spawn);
if (TryComp<PaperComponent>(printed, out var paper)) if (TryComp<PaperComponent>(printed, out var paper))
{ {
// fill in the order data // fill in the order data
@@ -395,31 +458,19 @@ namespace Content.Server.Cargo.Systems
} }
return true; return true;
}
return false;
} }
private void DeductFunds(StationBankAccountComponent component, int amount) private void DeductFunds(StationBankAccountComponent component, int amount)
{ {
component.Balance = Math.Max(0, component.Balance - amount); component.Balance = Math.Max(0, component.Balance - amount);
Dirty(component);
} }
#region Station #region Station
private StationBankAccountComponent? GetBankAccount(EntityUid uid, CargoOrderConsoleComponent _) private bool TryGetOrderDatabase([NotNullWhen(true)] EntityUid? stationUid, [MaybeNullWhen(false)] out StationCargoOrderDatabaseComponent dbComp)
{ {
var station = _station.GetOwningStation(uid); return TryComp(stationUid, out dbComp);
TryComp<StationBankAccountComponent>(station, out var bankComponent);
return bankComponent;
}
private bool TryGetOrderDatabase(EntityUid uid, [MaybeNullWhen(false)] out EntityUid? dbUid, [MaybeNullWhen(false)] out StationCargoOrderDatabaseComponent dbComp, CargoOrderConsoleComponent _)
{
dbUid = _station.GetOwningStation(uid);
return TryComp(dbUid, out dbComp);
} }
#endregion #endregion

View File

@@ -1,39 +1,27 @@
using System.Linq;
using Content.Server.Cargo.Components; using Content.Server.Cargo.Components;
using Content.Server.GameTicking.Events;
using Content.Server.Shuttles.Components;
using Content.Server.Shuttles.Events;
using Content.Shared.Stacks; using Content.Shared.Stacks;
using Content.Shared.Cargo; using Content.Shared.Cargo;
using Content.Shared.Cargo.BUI; using Content.Shared.Cargo.BUI;
using Content.Shared.Cargo.Components; using Content.Shared.Cargo.Components;
using Content.Shared.Cargo.Events; using Content.Shared.Cargo.Events;
using Content.Shared.CCVar;
using Content.Shared.GameTicking; using Content.Shared.GameTicking;
using Content.Shared.Whitelist;
using Robust.Server.GameObjects;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Utility; using Robust.Shared.Audio;
using Content.Shared.Coordinates;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
namespace Content.Server.Cargo.Systems; namespace Content.Server.Cargo.Systems;
public sealed partial class CargoSystem public sealed partial class CargoSystem
{ {
/* /*
* Handles cargo shuttle mechanics. * Handles cargo shuttle / trade mechanics.
*/ */
public MapId? CargoMap { get; private set; } private static readonly SoundPathSpecifier ApproveSound = new("/Audio/Effects/Cargo/ping.ogg");
private void InitializeShuttle() private void InitializeShuttle()
{ {
SubscribeLocalEvent<CargoShuttleComponent, FTLStartedEvent>(OnCargoFTLStarted); SubscribeLocalEvent<TradeStationComponent, GridSplitEvent>(OnTradeSplit);
SubscribeLocalEvent<CargoShuttleComponent, FTLCompletedEvent>(OnCargoFTLCompleted);
SubscribeLocalEvent<CargoShuttleComponent, FTLTagEvent>(OnCargoFTLTag);
SubscribeLocalEvent<CargoShuttleConsoleComponent, ComponentStartup>(OnCargoShuttleConsoleStartup); SubscribeLocalEvent<CargoShuttleConsoleComponent, ComponentStartup>(OnCargoShuttleConsoleStartup);
@@ -42,32 +30,6 @@ public sealed partial class CargoSystem
SubscribeLocalEvent<CargoPalletConsoleComponent, BoundUIOpenedEvent>(OnPalletUIOpen); SubscribeLocalEvent<CargoPalletConsoleComponent, BoundUIOpenedEvent>(OnPalletUIOpen);
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundRestart); SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundRestart);
SubscribeLocalEvent<RoundStartingEvent>(OnRoundStart);
_cfgManager.OnValueChanged(CCVars.GridFill, SetGridFill);
}
private void ShutdownShuttle()
{
_cfgManager.UnsubValueChanged(CCVars.GridFill, SetGridFill);
}
private void SetGridFill(bool obj)
{
if (obj)
{
SetupCargoShuttle();
}
}
private void OnCargoFTLTag(EntityUid uid, CargoShuttleComponent component, ref FTLTagEvent args)
{
if (args.Handled)
return;
// Just saves mappers forgetting.
args.Handled = true;
args.Tag = "DockCargo";
} }
#region Console #region Console
@@ -156,6 +118,15 @@ public sealed partial class CargoSystem
#endregion #endregion
private void OnTradeSplit(EntityUid uid, TradeStationComponent component, ref GridSplitEvent args)
{
// If the trade station gets bombed it's still a trade station.
foreach (var gridUid in args.NewGrids)
{
EnsureComp<TradeStationComponent>(gridUid);
}
}
#region Shuttle #region Shuttle
/// <summary> /// <summary>
@@ -206,9 +177,9 @@ public sealed partial class CargoSystem
return space; return space;
} }
private List<(EntityUid Entity, CargoPalletComponent Component)> GetCargoPallets(EntityUid gridUid) private List<(EntityUid Entity, CargoPalletComponent Component, TransformComponent PalletXform)> GetCargoPallets(EntityUid gridUid)
{ {
var pads = new List<(EntityUid, CargoPalletComponent)>(); _pads.Clear();
var query = AllEntityQuery<CargoPalletComponent, TransformComponent>(); var query = AllEntityQuery<CargoPalletComponent, TransformComponent>();
while (query.MoveNext(out var uid, out var comp, out var compXform)) while (query.MoveNext(out var uid, out var comp, out var compXform))
@@ -219,23 +190,43 @@ public sealed partial class CargoSystem
continue; continue;
} }
pads.Add((uid, comp)); _pads.Add((uid, comp, compXform));
} }
return pads; return _pads;
}
private IEnumerable<(EntityUid Entity, CargoPalletComponent Component, TransformComponent Transform)>
GetFreeCargoPallets(EntityUid gridUid,
List<(EntityUid Entity, CargoPalletComponent Component, TransformComponent Transform)> pallets)
{
_setEnts.Clear();
foreach (var pallet in pallets)
{
var aabb = _lookup.GetAABBNoContainer(pallet.Entity, pallet.Transform.LocalPosition, pallet.Transform.LocalRotation);
if (_lookup.AnyLocalEntitiesIntersecting(gridUid, aabb, LookupFlags.Dynamic))
continue;
yield return pallet;
}
} }
#endregion #endregion
#region Station #region Station
private void SellPallets(EntityUid gridUid, EntityUid? station, out double amount) private bool SellPallets(EntityUid gridUid, EntityUid? station, out double amount)
{ {
station ??= _station.GetOwningStation(gridUid); station ??= _station.GetOwningStation(gridUid);
GetPalletGoods(gridUid, out var toSell, out amount); GetPalletGoods(gridUid, out var toSell, out amount);
Log.Debug($"Cargo sold {toSell.Count} entities for {amount}"); Log.Debug($"Cargo sold {toSell.Count} entities for {amount}");
if (toSell.Count == 0)
return false;
if (station != null) if (station != null)
{ {
var ev = new EntitySoldEvent(station.Value, toSell); var ev = new EntitySoldEvent(station.Value, toSell);
@@ -246,6 +237,8 @@ public sealed partial class CargoSystem
{ {
Del(ent); Del(ent);
} }
return true;
} }
private void GetPalletGoods(EntityUid gridUid, out HashSet<EntityUid> toSell, out double amount) private void GetPalletGoods(EntityUid gridUid, out HashSet<EntityUid> toSell, out double amount)
@@ -253,10 +246,15 @@ public sealed partial class CargoSystem
amount = 0; amount = 0;
toSell = new HashSet<EntityUid>(); toSell = new HashSet<EntityUid>();
foreach (var (palletUid, _) in GetCargoPallets(gridUid)) foreach (var (palletUid, _, _) in GetCargoPallets(gridUid))
{ {
// Containers should already get the sell price of their children so can skip those. // Containers should already get the sell price of their children so can skip those.
foreach (var ent in _lookup.GetEntitiesIntersecting(palletUid, LookupFlags.Dynamic | LookupFlags.Sundries | LookupFlags.Approximate)) _setEnts.Clear();
_lookup.GetEntitiesIntersecting(palletUid, _setEnts,
LookupFlags.Dynamic | LookupFlags.Sundries);
foreach (var ent in _setEnts)
{ {
// Dont sell: // Dont sell:
// - anything already being sold // - anything already being sold
@@ -304,21 +302,6 @@ public sealed partial class CargoSystem
return true; return true;
} }
private void AddCargoContents(EntityUid shuttleUid, CargoShuttleComponent shuttle, StationCargoOrderDatabaseComponent orderDatabase)
{
var xformQuery = GetEntityQuery<TransformComponent>();
var pads = GetCargoPallets(shuttleUid);
while (pads.Count > 0)
{
var coordinates = new EntityCoordinates(shuttleUid, xformQuery.GetComponent(_random.PickAndTake(pads).Entity).LocalPosition);
if (!FulfillOrder(orderDatabase, coordinates, shuttle.PrinterOutput))
{
break;
}
}
}
private void OnPalletSale(EntityUid uid, CargoPalletConsoleComponent component, CargoPalletSellMessage args) private void OnPalletSale(EntityUid uid, CargoPalletConsoleComponent component, CargoPalletSellMessage args)
{ {
var player = args.Session.AttachedEntity; var player = args.Session.AttachedEntity;
@@ -327,114 +310,29 @@ public sealed partial class CargoSystem
return; return;
var bui = _uiSystem.GetUi(uid, CargoPalletConsoleUiKey.Sale); var bui = _uiSystem.GetUi(uid, CargoPalletConsoleUiKey.Sale);
if (Transform(uid).GridUid is not EntityUid gridUid) var xform = Transform(uid);
if (xform.GridUid is not EntityUid gridUid)
{ {
_uiSystem.SetUiState(bui, _uiSystem.SetUiState(bui,
new CargoPalletConsoleInterfaceState(0, 0, false)); new CargoPalletConsoleInterfaceState(0, 0, false));
return; return;
} }
SellPallets(gridUid, null, out var price); if (!SellPallets(gridUid, null, out var price))
return;
var stackPrototype = _protoMan.Index<StackPrototype>(component.CashType); var stackPrototype = _protoMan.Index<StackPrototype>(component.CashType);
_stack.Spawn((int) price, stackPrototype, uid.ToCoordinates()); _stack.Spawn((int) price, stackPrototype, xform.Coordinates);
_audio.PlayPvs(ApproveSound, uid);
UpdatePalletConsoleInterface(uid); UpdatePalletConsoleInterface(uid);
} }
private void OnCargoFTLStarted(EntityUid uid, CargoShuttleComponent component, ref FTLStartedEvent args)
{
var stationUid = _station.GetOwningStation(uid);
// Called
if (CargoMap == null ||
args.FromMapUid != _mapManager.GetMapEntityId(CargoMap.Value) ||
!TryComp<StationCargoOrderDatabaseComponent>(stationUid, out var orderDatabase))
{
return;
}
AddCargoContents(uid, component, orderDatabase);
UpdateOrders(stationUid!.Value, orderDatabase);
UpdateCargoShuttleConsoles(uid, component);
}
private void OnCargoFTLCompleted(EntityUid uid, CargoShuttleComponent component, ref FTLCompletedEvent args)
{
var xform = Transform(uid);
// Recalled
if (xform.MapID != CargoMap)
return;
var stationUid = _station.GetOwningStation(uid);
if (TryComp<StationBankAccountComponent>(stationUid, out var bank))
{
SellPallets(uid, stationUid, out var amount);
bank.Balance += (int) amount;
}
}
#endregion #endregion
private void OnRoundRestart(RoundRestartCleanupEvent ev) private void OnRoundRestart(RoundRestartCleanupEvent ev)
{ {
Reset(); Reset();
CleanupCargoShuttle();
}
private void OnRoundStart(RoundStartingEvent ev)
{
if (_cfgManager.GetCVar(CCVars.GridFill))
SetupCargoShuttle();
}
private void CleanupCargoShuttle()
{
if (CargoMap == null || !_mapManager.MapExists(CargoMap.Value))
{
CargoMap = null;
DebugTools.Assert(!EntityQuery<CargoShuttleComponent>().Any());
return;
}
_mapManager.DeleteMap(CargoMap.Value);
CargoMap = null;
// Shuttle may not have been in the cargo dimension (e.g. on the station map) so need to delete.
var query = AllEntityQuery<CargoShuttleComponent>();
while (query.MoveNext(out var uid, out var _))
{
if (TryComp<StationCargoOrderDatabaseComponent>(uid, out var station))
{
station.Shuttle = null;
}
QueueDel(uid);
}
}
private void SetupCargoShuttle()
{
if (CargoMap != null && _mapManager.MapExists(CargoMap.Value))
{
return;
}
// It gets mapinit which is okay... buuutt we still want it paused to avoid power draining.
CargoMap = _mapManager.CreateMap();
var mapUid = _mapManager.GetMapEntityId(CargoMap.Value);
var ftl = EnsureComp<FTLDestinationComponent>(_mapManager.GetMapEntityId(CargoMap.Value));
ftl.Whitelist = new EntityWhitelist()
{
Components = new[]
{
_factory.GetComponentName(typeof(CargoShuttleComponent))
}
};
_metaSystem.SetEntityName(mapUid, $"Trading post {_random.Next(1000):000}");
_console.RefreshShuttleConsoles();
} }
} }

View File

@@ -65,10 +65,10 @@ public sealed partial class CargoSystem
} }
var xform = Transform(uid); var xform = Transform(uid);
if (FulfillOrder(orderDatabase, xform.Coordinates, comp.PrinterOutput)) if (FulfillNextOrder(orderDatabase, xform.Coordinates, comp.PrinterOutput))
{ {
_audio.PlayPvs(_audio.GetSound(comp.TeleportSound), uid, AudioParams.Default.WithVolume(-8f)); _audio.PlayPvs(_audio.GetSound(comp.TeleportSound), uid, AudioParams.Default.WithVolume(-8f));
UpdateOrders(station!.Value, orderDatabase); UpdateOrders(station.Value, orderDatabase);
comp.CurrentState = CargoTelepadState.Teleporting; comp.CurrentState = CargoTelepadState.Teleporting;
_appearance.SetData(uid, CargoTelepadVisuals.State, CargoTelepadState.Teleporting, appearance); _appearance.SetData(uid, CargoTelepadVisuals.State, CargoTelepadState.Teleporting, appearance);

View File

@@ -14,8 +14,6 @@ using Content.Shared.Mobs.Components;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
using Robust.Shared.Configuration;
using Robust.Shared.Map;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Robust.Shared.Random; using Robust.Shared.Random;
@@ -25,9 +23,6 @@ namespace Content.Server.Cargo.Systems;
public sealed partial class CargoSystem : SharedCargoSystem public sealed partial class CargoSystem : SharedCargoSystem
{ {
[Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IComponentFactory _factory = default!;
[Dependency] private readonly IConfigurationManager _cfgManager = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IPrototypeManager _protoMan = default!; [Dependency] private readonly IPrototypeManager _protoMan = default!;
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
@@ -41,6 +36,7 @@ public sealed partial class CargoSystem : SharedCargoSystem
[Dependency] private readonly PricingSystem _pricing = default!; [Dependency] private readonly PricingSystem _pricing = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedTransformSystem _xformSystem = default!;
[Dependency] private readonly ShuttleConsoleSystem _console = default!; [Dependency] private readonly ShuttleConsoleSystem _console = default!;
[Dependency] private readonly StackSystem _stack = default!; [Dependency] private readonly StackSystem _stack = default!;
[Dependency] private readonly StationSystem _station = default!; [Dependency] private readonly StationSystem _station = default!;
@@ -50,6 +46,11 @@ public sealed partial class CargoSystem : SharedCargoSystem
private EntityQuery<TransformComponent> _xformQuery; private EntityQuery<TransformComponent> _xformQuery;
private EntityQuery<CargoSellBlacklistComponent> _blacklistQuery; private EntityQuery<CargoSellBlacklistComponent> _blacklistQuery;
private EntityQuery<MobStateComponent> _mobQuery; private EntityQuery<MobStateComponent> _mobQuery;
private EntityQuery<TradeStationComponent> _tradeQuery;
private HashSet<EntityUid> _setEnts = new();
private List<EntityUid> _listEnts = new();
private List<(EntityUid, CargoPalletComponent, TransformComponent)> _pads = new();
public override void Initialize() public override void Initialize()
{ {
@@ -58,6 +59,7 @@ public sealed partial class CargoSystem : SharedCargoSystem
_xformQuery = GetEntityQuery<TransformComponent>(); _xformQuery = GetEntityQuery<TransformComponent>();
_blacklistQuery = GetEntityQuery<CargoSellBlacklistComponent>(); _blacklistQuery = GetEntityQuery<CargoSellBlacklistComponent>();
_mobQuery = GetEntityQuery<MobStateComponent>(); _mobQuery = GetEntityQuery<MobStateComponent>();
_tradeQuery = GetEntityQuery<TradeStationComponent>();
InitializeConsole(); InitializeConsole();
InitializeShuttle(); InitializeShuttle();
@@ -65,13 +67,6 @@ public sealed partial class CargoSystem : SharedCargoSystem
InitializeBounty(); InitializeBounty();
} }
public override void Shutdown()
{
base.Shutdown();
ShutdownShuttle();
CleanupCargoShuttle();
}
public override void Update(float frameTime) public override void Update(float frameTime)
{ {
base.Update(frameTime); base.Update(frameTime);

View File

@@ -1,4 +1,5 @@
using Content.Server.Shuttles.Systems; using Content.Server.Shuttles.Systems;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility; using Robust.Shared.Utility;
namespace Content.Server.Shuttles.Components; namespace Content.Server.Shuttles.Components;
@@ -23,6 +24,11 @@ public record struct GridSpawnGroup
public int MinCount = 1; public int MinCount = 1;
public int MaxCount = 1; public int MaxCount = 1;
/// <summary>
/// Components to be added to any spawned grids.
/// </summary>
public ComponentRegistry AddComponents = new();
/// <summary> /// <summary>
/// Hide the IFF label of the grid. /// Hide the IFF label of the grid.
/// </summary> /// </summary>

View File

@@ -235,6 +235,12 @@ public sealed partial class ShuttleSystem
component.State = FTLState.Starting; component.State = FTLState.Starting;
var audio = _audio.PlayPvs(_startupSound, uid); var audio = _audio.PlayPvs(_startupSound, uid);
audio.Value.Component.Flags |= AudioFlags.GridAudio; audio.Value.Component.Flags |= AudioFlags.GridAudio;
if (_physicsQuery.TryGetComponent(uid, out var gridPhysics))
{
_transform.SetLocalPosition(audio.Value.Entity, gridPhysics.LocalCenter);
}
// Make sure the map is setup before we leave to avoid pop-in (e.g. parallax). // Make sure the map is setup before we leave to avoid pop-in (e.g. parallax).
SetupHyperspace(); SetupHyperspace();
return true; return true;
@@ -260,6 +266,7 @@ public sealed partial class ShuttleSystem
{ {
// Startup time has elapsed and in hyperspace. // Startup time has elapsed and in hyperspace.
case FTLState.Starting: case FTLState.Starting:
{
DoTheDinosaur(xform); DoTheDinosaur(xform);
comp.State = FTLState.Travelling; comp.State = FTLState.Travelling;
@@ -268,7 +275,8 @@ public sealed partial class ShuttleSystem
var fromRotation = _transform.GetWorldRotation(xform); var fromRotation = _transform.GetWorldRotation(xform);
var width = Comp<MapGridComponent>(uid).LocalAABB.Width; var width = Comp<MapGridComponent>(uid).LocalAABB.Width;
xform.Coordinates = new EntityCoordinates(_mapManager.GetMapEntityId(_hyperSpaceMap!.Value), new Vector2(_index + width / 2f, 0f)); xform.Coordinates = new EntityCoordinates(_mapManager.GetMapEntityId(_hyperSpaceMap!.Value),
new Vector2(_index + width / 2f, 0f));
xform.LocalRotation = Angle.Zero; xform.LocalRotation = Angle.Zero;
_index += width + Buffer; _index += width + Buffer;
comp.Accumulator += comp.TravelTime - DefaultArrivalTime; comp.Accumulator += comp.TravelTime - DefaultArrivalTime;
@@ -285,7 +293,9 @@ public sealed partial class ShuttleSystem
SetDockBolts(uid, true); SetDockBolts(uid, true);
_console.RefreshShuttleConsoles(uid); _console.RefreshShuttleConsoles(uid);
var target = comp.TargetUid != null ? new EntityCoordinates(comp.TargetUid.Value, Vector2.Zero) : comp.TargetCoordinates; var target = comp.TargetUid != null
? new EntityCoordinates(comp.TargetUid.Value, Vector2.Zero)
: comp.TargetCoordinates;
var ev = new FTLStartedEvent(uid, target, fromMapUid, fromMatrix, fromRotation); var ev = new FTLStartedEvent(uid, target, fromMapUid, fromMatrix, fromRotation);
RaiseLocalEvent(uid, ref ev, true); RaiseLocalEvent(uid, ref ev, true);
@@ -293,8 +303,15 @@ public sealed partial class ShuttleSystem
var wowdio = _audio.PlayPvs(comp.TravelSound, uid); var wowdio = _audio.PlayPvs(comp.TravelSound, uid);
comp.TravelStream = wowdio?.Entity; comp.TravelStream = wowdio?.Entity;
if (wowdio?.Component != null) if (wowdio?.Component != null)
{
wowdio.Value.Component.Flags |= AudioFlags.GridAudio; wowdio.Value.Component.Flags |= AudioFlags.GridAudio;
if (_physicsQuery.TryGetComponent(uid, out var gridPhysics))
{
_transform.SetLocalPosition(wowdio.Value.Entity, gridPhysics.LocalCenter);
}
}
}
break; break;
// Arriving, play effects // Arriving, play effects
case FTLState.Travelling: case FTLState.Travelling:
@@ -313,6 +330,7 @@ public sealed partial class ShuttleSystem
break; break;
// Arrived // Arrived
case FTLState.Arriving: case FTLState.Arriving:
{
DoTheDinosaur(xform); DoTheDinosaur(xform);
SetDockBolts(uid, false); SetDockBolts(uid, false);
SetDocks(uid, true); SetDocks(uid, true);
@@ -383,6 +401,12 @@ public sealed partial class ShuttleSystem
comp.TravelStream = _audio.Stop(comp.TravelStream); comp.TravelStream = _audio.Stop(comp.TravelStream);
var audio = _audio.PlayPvs(_arrivalSound, uid); var audio = _audio.PlayPvs(_arrivalSound, uid);
audio.Value.Component.Flags |= AudioFlags.GridAudio; audio.Value.Component.Flags |= AudioFlags.GridAudio;
// TODO: Shitcode til engine fix
if (_physicsQuery.TryGetComponent(uid, out var gridPhysics))
{
_transform.SetLocalPosition(audio.Value.Entity, gridPhysics.LocalCenter);
}
if (TryComp<FTLDestinationComponent>(uid, out var dest)) if (TryComp<FTLDestinationComponent>(uid, out var dest))
{ {
@@ -397,6 +421,7 @@ public sealed partial class ShuttleSystem
var ftlEvent = new FTLCompletedEvent(uid, _mapManager.GetMapEntityId(mapId)); var ftlEvent = new FTLCompletedEvent(uid, _mapManager.GetMapEntityId(mapId));
RaiseLocalEvent(uid, ref ftlEvent, true); RaiseLocalEvent(uid, ref ftlEvent, true);
}
break; break;
case FTLState.Cooldown: case FTLState.Cooldown:
RemComp<FTLComponent>(uid); RemComp<FTLComponent>(uid);
@@ -711,6 +736,9 @@ public sealed partial class ShuttleSystem
continue; continue;
var aabb = fixture.Shape.ComputeAABB(transform, 0); var aabb = fixture.Shape.ComputeAABB(transform, 0);
// Shift it slightly
aabb = aabb.Translated(-grid.TileSizeHalfVector);
// Create a small border around it. // Create a small border around it.
aabb = aabb.Enlarged(0.2f); aabb = aabb.Enlarged(0.2f);
aabbs.Add(aabb); aabbs.Add(aabb);

View File

@@ -156,6 +156,17 @@ public sealed partial class ShuttleSystem
var name = path.FilenameWithoutExtension; var name = path.FilenameWithoutExtension;
_metadata.SetEntityName(ent[0], name); _metadata.SetEntityName(ent[0], name);
} }
foreach (var compReg in group.AddComponents.Values)
{
var compType = compReg.Component.GetType();
if (HasComp(ent[0], compType))
continue;
var comp = _factory.GetComponent(compType);
AddComp(ent[0], comp, true);
}
} }
else else
{ {

View File

@@ -25,6 +25,7 @@ namespace Content.Server.Shuttles.Systems;
[UsedImplicitly] [UsedImplicitly]
public sealed partial class ShuttleSystem : SharedShuttleSystem public sealed partial class ShuttleSystem : SharedShuttleSystem
{ {
[Dependency] private readonly IComponentFactory _factory = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!; [Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
@@ -39,7 +40,6 @@ public sealed partial class ShuttleSystem : SharedShuttleSystem
[Dependency] private readonly MapLoaderSystem _loader = default!; [Dependency] private readonly MapLoaderSystem _loader = default!;
[Dependency] private readonly MetaDataSystem _metadata = default!; [Dependency] private readonly MetaDataSystem _metadata = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedMapSystem _maps = default!;
[Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly ShuttleConsoleSystem _console = default!; [Dependency] private readonly ShuttleConsoleSystem _console = default!;

View File

@@ -10,10 +10,7 @@ namespace Content.Shared.Cargo.Components;
[RegisterComponent, NetworkedComponent, Access(typeof(SharedCargoSystem))] [RegisterComponent, NetworkedComponent, Access(typeof(SharedCargoSystem))]
public sealed partial class CargoShuttleComponent : Component public sealed partial class CargoShuttleComponent : Component
{ {
/// <summary> /*
/// The paper-type prototype to spawn with the order information. * Still needed for drone console for now.
/// </summary> */
[DataField("printerOutput", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
public string PrinterOutput = "PaperCargoInvoice";
} }

View File

@@ -28,6 +28,8 @@ cargo-console-invalid-product = Invalid product ID
cargo-console-too-many = Too many approved orders cargo-console-too-many = Too many approved orders
cargo-console-snip-snip = Order trimmed to capacity cargo-console-snip-snip = Order trimmed to capacity
cargo-console-insufficient-funds = Insufficient funds (require {$cost}) cargo-console-insufficient-funds = Insufficient funds (require {$cost})
cargo-console-unfulfilled = No room to fulfill order
cargo-console-trade-station = Sent to {$destination}
cargo-console-paper-print-name = Order #{$orderNumber} cargo-console-paper-print-name = Order #{$orderNumber}
cargo-console-paper-print-text = cargo-console-paper-print-text =

File diff suppressed because it is too large Load Diff

View File

@@ -46,9 +46,16 @@
path: /Maps/Shuttles/cargo.yml path: /Maps/Shuttles/cargo.yml
- type: GridSpawn - type: GridSpawn
groups: groups:
trade:
addComponents:
- type: ProtectedGrid
- type: TradeStation
paths:
- /Maps/Shuttles/trading_outpost.yml
mining: mining:
paths: paths:
- /Maps/Shuttles/mining.yml - /Maps/Shuttles/mining.yml
# Spawn last
ruins: ruins:
hide: true hide: true
nameGrid: true nameGrid: true