diff --git a/Content.Server/Cargo/Components/StationCargoOrderDatabaseComponent.cs b/Content.Server/Cargo/Components/StationCargoOrderDatabaseComponent.cs index c30db08bbe..2e3b2c2115 100644 --- a/Content.Server/Cargo/Components/StationCargoOrderDatabaseComponent.cs +++ b/Content.Server/Cargo/Components/StationCargoOrderDatabaseComponent.cs @@ -1,4 +1,6 @@ +using Content.Server.Station.Components; using Content.Shared.Cargo; +using Content.Shared.Cargo.Components; using Content.Shared.Cargo.Prototypes; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; @@ -38,3 +40,17 @@ public sealed partial class StationCargoOrderDatabaseComponent : Component [DataField] public EntProtoId PrinterOutput = "PaperCargoInvoice"; } + +/// +/// Event broadcast before a cargo order is fulfilled, allowing alternate systems to fulfill the order. +/// +[ByRefEvent] +public record struct FulfillCargoOrderEvent(Entity Station, CargoOrderData Order, Entity OrderConsole) +{ + public Entity OrderConsole = OrderConsole; + public Entity Station = Station; + public CargoOrderData Order = Order; + + public EntityUid? FulfillmentEntity; + public bool Handled = false; +} diff --git a/Content.Server/Cargo/Systems/CargoSystem.Orders.cs b/Content.Server/Cargo/Systems/CargoSystem.Orders.cs index 7a81e1a424..13a1d3d565 100644 --- a/Content.Server/Cargo/Systems/CargoSystem.Orders.cs +++ b/Content.Server/Cargo/Systems/CargoSystem.Orders.cs @@ -170,13 +170,20 @@ namespace Content.Server.Cargo.Systems return; } - var tradeDestination = TryFulfillOrder(stationData, order, orderDatabase); + var ev = new FulfillCargoOrderEvent((station.Value, stationData), order, (uid, component)); + RaiseLocalEvent(ref ev); + ev.FulfillmentEntity ??= station.Value; - if (tradeDestination == null) + if (!ev.Handled) { - ConsolePopup(args.Session, Loc.GetString("cargo-console-unfulfilled")); - PlayDenySound(uid, component); - return; + ev.FulfillmentEntity = TryFulfillOrder((station.Value, stationData), order, orderDatabase); + + if (ev.FulfillmentEntity == null) + { + ConsolePopup(args.Session, Loc.GetString("cargo-console-unfulfilled")); + PlayDenySound(uid, component); + return; + } } _idCardSystem.TryFindIdCard(player, out var idCard); @@ -186,14 +193,14 @@ namespace Content.Server.Cargo.Systems var approverName = idCard.Comp?.FullName ?? Loc.GetString("access-reader-unknown-id"); var approverJob = idCard.Comp?.JobTitle ?? Loc.GetString("access-reader-unknown-id"); - var message = Loc.GetString("cargo-console-unlock-approved-order-broadcast", + var message = Loc.GetString("cargo-console-unlock-approved-order-broadcast", ("productName", Loc.GetString(order.ProductName)), ("orderAmount", order.OrderQuantity), ("approverName", approverName), ("approverJob", approverJob), ("cost", cost)); _radio.SendRadioMessage(uid, message, component.AnnouncementChannel, uid, escapeMarkup: false); - ConsolePopup(args.Session, Loc.GetString("cargo-console-trade-station", ("destination", MetaData(tradeDestination.Value).EntityName))); + ConsolePopup(args.Session, Loc.GetString("cargo-console-trade-station", ("destination", MetaData(ev.FulfillmentEntity.Value).EntityName))); // Log order approval _adminLogger.Add(LogType.Action, LogImpact.Low, @@ -201,10 +208,10 @@ namespace Content.Server.Cargo.Systems orderDatabase.Orders.Remove(order); DeductFunds(bank, cost); - UpdateOrders(station.Value, orderDatabase); + UpdateOrders(station.Value); } - private EntityUid? TryFulfillOrder(StationDataComponent stationData, CargoOrderData order, StationCargoOrderDatabaseComponent orderDatabase) + private EntityUid? TryFulfillOrder(Entity stationData, CargoOrderData order, StationCargoOrderDatabaseComponent orderDatabase) { // No slots at the trade station _listEnts.Clear(); @@ -357,7 +364,7 @@ namespace Content.Server.Cargo.Systems /// Updates all of the cargo-related consoles for a particular station. /// This should be called whenever orders change. /// - private void UpdateOrders(EntityUid dbUid, StationCargoOrderDatabaseComponent _) + private void UpdateOrders(EntityUid dbUid) { // Order added so all consoles need updating. var orderQuery = AllEntityQuery(); @@ -392,7 +399,7 @@ namespace Content.Server.Cargo.Systems string description, string dest, StationCargoOrderDatabaseComponent component, - StationDataComponent stationData + Entity stationData ) { DebugTools.Assert(_protoMan.HasIndex(spawnId)); @@ -414,7 +421,7 @@ namespace Content.Server.Cargo.Systems private bool TryAddOrder(EntityUid dbUid, CargoOrderData data, StationCargoOrderDatabaseComponent component) { component.Orders.Add(data); - UpdateOrders(dbUid, component); + UpdateOrders(dbUid); return true; } @@ -432,7 +439,7 @@ namespace Content.Server.Cargo.Systems { orderDB.Orders.RemoveAt(sequenceIdx); } - UpdateOrders(dbUid, orderDB); + UpdateOrders(dbUid); } public void ClearOrders(StationCargoOrderDatabaseComponent component) diff --git a/Content.Server/Cargo/Systems/CargoSystem.Telepad.cs b/Content.Server/Cargo/Systems/CargoSystem.Telepad.cs index 42aabf2578..f83ec1a512 100644 --- a/Content.Server/Cargo/Systems/CargoSystem.Telepad.cs +++ b/Content.Server/Cargo/Systems/CargoSystem.Telepad.cs @@ -1,9 +1,13 @@ +using System.Linq; using Content.Server.Cargo.Components; using Content.Server.Power.Components; +using Content.Server.Power.EntitySystems; +using Content.Server.Station.Components; using Content.Shared.Cargo; using Content.Shared.Cargo.Components; using Content.Shared.DeviceLinking; using Robust.Shared.Audio; +using Robust.Shared.Random; using Robust.Shared.Utility; namespace Content.Server.Cargo.Systems; @@ -13,10 +17,44 @@ public sealed partial class CargoSystem private void InitializeTelepad() { SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnShutdown); SubscribeLocalEvent(OnTelepadPowerChange); // Shouldn't need re-anchored event SubscribeLocalEvent(OnTelepadAnchorChange); + SubscribeLocalEvent(OnTelepadFulfillCargoOrder); } + + private void OnTelepadFulfillCargoOrder(ref FulfillCargoOrderEvent args) + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var tele, out var xform)) + { + if (tele.CurrentState != CargoTelepadState.Idle) + continue; + + if (!this.IsPowered(uid, EntityManager)) + continue; + + if (_station.GetOwningStation(uid, xform) != args.Station) + continue; + + // todo cannot be fucking asked to figure out device linking rn but this shouldn't just default to the first port. + if (!TryComp(uid, out var sinkComponent) || + sinkComponent.LinkedSources.FirstOrNull() is not { } console || + console != args.OrderConsole.Owner) + continue; + + for (var i = 0; i < args.Order.OrderQuantity; i++) + { + tele.CurrentOrders.Add(args.Order); + } + tele.Accumulator = tele.Delay; + args.Handled = true; + args.FulfillmentEntity = uid; + return; + } + } + private void UpdateTelepad(float frameTime) { var query = EntityQueryEnumerator(); @@ -33,14 +71,6 @@ public sealed partial class CargoSystem continue; } - if (!TryComp(uid, out var sinkComponent) || - sinkComponent.LinkedSources.FirstOrNull() is not { } console || - !HasComp(console)) - { - comp.Accumulator = comp.Delay; - continue; - } - comp.Accumulator -= frameTime; // Uhh listen teleporting takes time and I just want the 1 float. @@ -51,21 +81,22 @@ public sealed partial class CargoSystem continue; } - var station = _station.GetOwningStation(console); - - if (!TryComp(station, out var orderDatabase) || - orderDatabase.Orders.Count == 0) + if (comp.CurrentOrders.Count == 0) { comp.Accumulator += comp.Delay; continue; } var xform = Transform(uid); - if (FulfillNextOrder(orderDatabase, xform.Coordinates, comp.PrinterOutput)) + var currentOrder = comp.CurrentOrders.First(); + if (FulfillOrder(currentOrder, xform.Coordinates, comp.PrinterOutput)) { _audio.PlayPvs(_audio.GetSound(comp.TeleportSound), uid, AudioParams.Default.WithVolume(-8f)); - UpdateOrders(station.Value, orderDatabase); + if (_station.GetOwningStation(uid) is { } station) + UpdateOrders(station); + + comp.CurrentOrders.Remove(currentOrder); comp.CurrentState = CargoTelepadState.Teleporting; _appearance.SetData(uid, CargoTelepadVisuals.State, CargoTelepadState.Teleporting, appearance); } @@ -79,6 +110,29 @@ public sealed partial class CargoSystem _linker.EnsureSinkPorts(uid, telepad.ReceiverPort); } + private void OnShutdown(Entity ent, ref ComponentShutdown args) + { + if (ent.Comp.CurrentOrders.Count == 0) + return; + + if (_station.GetStations().Count == 0) + return; + + if (_station.GetOwningStation(ent) is not { } station) + { + station = _random.Pick(_station.GetStations().Where(HasComp).ToList()); + } + + if (!TryComp(station, out var db) || + !TryComp(station, out var data)) + return; + + foreach (var order in ent.Comp.CurrentOrders) + { + TryFulfillOrder((station, data), order, db); + } + } + private void SetEnabled(EntityUid uid, CargoTelepadComponent component, ApcPowerReceiverComponent? receiver = null, TransformComponent? xform = null) { diff --git a/Content.Server/StationEvents/Events/CargoGiftsRule.cs b/Content.Server/StationEvents/Events/CargoGiftsRule.cs index 194786fca7..0c8c9b6dc5 100644 --- a/Content.Server/StationEvents/Events/CargoGiftsRule.cs +++ b/Content.Server/StationEvents/Events/CargoGiftsRule.cs @@ -69,7 +69,7 @@ public sealed class CargoGiftsRule : StationEventSystem Loc.GetString(component.Description), Loc.GetString(component.Dest), cargoDb, - stationData! + (station.Value, stationData) )) { break; diff --git a/Content.Shared/Cargo/CargoOrderData.cs b/Content.Shared/Cargo/CargoOrderData.cs index 831010cedd..ce05d92236 100644 --- a/Content.Shared/Cargo/CargoOrderData.cs +++ b/Content.Shared/Cargo/CargoOrderData.cs @@ -1,47 +1,55 @@ using Robust.Shared.Serialization; -using Content.Shared.Access.Components; using System.Text; namespace Content.Shared.Cargo { - [NetSerializable, Serializable] - public sealed class CargoOrderData + [DataDefinition, NetSerializable, Serializable] + public sealed partial class CargoOrderData { /// /// Price when the order was added. /// + [DataField] public int Price; /// /// A unique (arbitrary) ID which identifies this order. /// - public readonly int OrderId; + [DataField] + public int OrderId { get; private set; } /// /// Prototype Id for the item to be created /// - public readonly string ProductId; + [DataField] + public string ProductId { get; private set; } /// /// Prototype Name /// - public readonly string ProductName; + [DataField] + public string ProductName { get; private set; } /// /// The number of items in the order. Not readonly, as it might change /// due to caps on the amount of orders that can be placed. /// + [DataField] public int OrderQuantity; /// /// How many instances of this order that we've already dispatched /// + [DataField] public int NumDispatched = 0; - public readonly string Requester; + [DataField] + public string Requester { get; private set; } // public String RequesterRank; // TODO Figure out how to get Character ID card data // public int RequesterId; - public readonly string Reason; + [DataField] + public string Reason { get; private set; } public bool Approved => Approver is not null; + [DataField] public string? Approver; public CargoOrderData(int orderId, string productId, string productName, int price, int amount, string requester, string reason) diff --git a/Content.Shared/Cargo/Components/SharedCargoTelepadComponent.cs b/Content.Shared/Cargo/Components/SharedCargoTelepadComponent.cs index 911ea41cca..5bc3934768 100644 --- a/Content.Shared/Cargo/Components/SharedCargoTelepadComponent.cs +++ b/Content.Shared/Cargo/Components/SharedCargoTelepadComponent.cs @@ -12,11 +12,14 @@ namespace Content.Shared.Cargo.Components; [RegisterComponent, NetworkedComponent, Access(typeof(SharedCargoSystem))] public sealed partial class CargoTelepadComponent : Component { + [DataField] + public List CurrentOrders = new(); + /// /// The actual amount of time it takes to teleport from the telepad /// [DataField("delay"), ViewVariables(VVAccess.ReadWrite)] - public float Delay = 10f; + public float Delay = 5f; /// /// How much time we've accumulated until next teleport. diff --git a/Resources/Prototypes/Entities/Structures/cargo_telepad.yml b/Resources/Prototypes/Entities/Structures/cargo_telepad.yml index d395235a53..9dc9f77cff 100644 --- a/Resources/Prototypes/Entities/Structures/cargo_telepad.yml +++ b/Resources/Prototypes/Entities/Structures/cargo_telepad.yml @@ -47,3 +47,5 @@ board: CargoTelepadMachineCircuitboard - type: Appearance - type: CollideOnAnchor + - type: NameIdentifier + group: CargoTelepads diff --git a/Resources/Prototypes/name_identifier_groups.yml b/Resources/Prototypes/name_identifier_groups.yml index 82c2f3bce9..4823e31f55 100644 --- a/Resources/Prototypes/name_identifier_groups.yml +++ b/Resources/Prototypes/name_identifier_groups.yml @@ -37,3 +37,9 @@ id: Bounty minValue: 0 maxValue: 999 + +- type: nameIdentifierGroup + id: CargoTelepads + prefix: TELE + minValue: 0 + maxValue: 999