+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;
[DataField]
public EntProtoId PrinterOutput = "PaperCargoInvoice";
}
+
+/// <summary>
+/// Event broadcast before a cargo order is fulfilled, allowing alternate systems to fulfill the order.
+/// </summary>
+[ByRefEvent]
+public record struct FulfillCargoOrderEvent(Entity<StationDataComponent> Station, CargoOrderData Order, Entity<CargoOrderConsoleComponent> OrderConsole)
+{
+ public Entity<CargoOrderConsoleComponent> OrderConsole = OrderConsole;
+ public Entity<StationDataComponent> Station = Station;
+ public CargoOrderData Order = Order;
+
+ public EntityUid? FulfillmentEntity;
+ public bool Handled = false;
+}
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);
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,
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<StationDataComponent> stationData, CargoOrderData order, StationCargoOrderDatabaseComponent orderDatabase)
{
// No slots at the trade station
_listEnts.Clear();
/// Updates all of the cargo-related consoles for a particular station.
/// This should be called whenever orders change.
/// </summary>
- private void UpdateOrders(EntityUid dbUid, StationCargoOrderDatabaseComponent _)
+ private void UpdateOrders(EntityUid dbUid)
{
// Order added so all consoles need updating.
var orderQuery = AllEntityQuery<CargoOrderConsoleComponent>();
string description,
string dest,
StationCargoOrderDatabaseComponent component,
- StationDataComponent stationData
+ Entity<StationDataComponent> stationData
)
{
DebugTools.Assert(_protoMan.HasIndex<EntityPrototype>(spawnId));
private bool TryAddOrder(EntityUid dbUid, CargoOrderData data, StationCargoOrderDatabaseComponent component)
{
component.Orders.Add(data);
- UpdateOrders(dbUid, component);
+ UpdateOrders(dbUid);
return true;
}
{
orderDB.Orders.RemoveAt(sequenceIdx);
}
- UpdateOrders(dbUid, orderDB);
+ UpdateOrders(dbUid);
}
public void ClearOrders(StationCargoOrderDatabaseComponent component)
+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;
private void InitializeTelepad()
{
SubscribeLocalEvent<CargoTelepadComponent, ComponentInit>(OnInit);
+ SubscribeLocalEvent<CargoTelepadComponent, ComponentShutdown>(OnShutdown);
SubscribeLocalEvent<CargoTelepadComponent, PowerChangedEvent>(OnTelepadPowerChange);
// Shouldn't need re-anchored event
SubscribeLocalEvent<CargoTelepadComponent, AnchorStateChangedEvent>(OnTelepadAnchorChange);
+ SubscribeLocalEvent<FulfillCargoOrderEvent>(OnTelepadFulfillCargoOrder);
}
+
+ private void OnTelepadFulfillCargoOrder(ref FulfillCargoOrderEvent args)
+ {
+ var query = EntityQueryEnumerator<CargoTelepadComponent, TransformComponent>();
+ 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<DeviceLinkSinkComponent>(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<CargoTelepadComponent>();
continue;
}
- if (!TryComp<DeviceLinkSinkComponent>(uid, out var sinkComponent) ||
- sinkComponent.LinkedSources.FirstOrNull() is not { } console ||
- !HasComp<CargoOrderConsoleComponent>(console))
- {
- comp.Accumulator = comp.Delay;
- continue;
- }
-
comp.Accumulator -= frameTime;
// Uhh listen teleporting takes time and I just want the 1 float.
continue;
}
- var station = _station.GetOwningStation(console);
-
- if (!TryComp<StationCargoOrderDatabaseComponent>(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);
}
_linker.EnsureSinkPorts(uid, telepad.ReceiverPort);
}
+ private void OnShutdown(Entity<CargoTelepadComponent> 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<StationCargoOrderDatabaseComponent>).ToList());
+ }
+
+ if (!TryComp<StationCargoOrderDatabaseComponent>(station, out var db) ||
+ !TryComp<StationDataComponent>(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)
{
Loc.GetString(component.Description),
Loc.GetString(component.Dest),
cargoDb,
- stationData!
+ (station.Value, stationData)
))
{
break;
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
{
/// <summary>
/// Price when the order was added.
/// </summary>
+ [DataField]
public int Price;
/// <summary>
/// A unique (arbitrary) ID which identifies this order.
/// </summary>
- public readonly int OrderId;
+ [DataField]
+ public int OrderId { get; private set; }
/// <summary>
/// Prototype Id for the item to be created
/// </summary>
- public readonly string ProductId;
+ [DataField]
+ public string ProductId { get; private set; }
/// <summary>
/// Prototype Name
/// </summary>
- public readonly string ProductName;
+ [DataField]
+ public string ProductName { get; private set; }
/// <summary>
/// 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.
/// </summary>
+ [DataField]
public int OrderQuantity;
/// <summary>
/// How many instances of this order that we've already dispatched
/// </summary>
+ [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)
[RegisterComponent, NetworkedComponent, Access(typeof(SharedCargoSystem))]
public sealed partial class CargoTelepadComponent : Component
{
+ [DataField]
+ public List<CargoOrderData> CurrentOrders = new();
+
/// <summary>
/// The actual amount of time it takes to teleport from the telepad
/// </summary>
[DataField("delay"), ViewVariables(VVAccess.ReadWrite)]
- public float Delay = 10f;
+ public float Delay = 5f;
/// <summary>
/// How much time we've accumulated until next teleport.
board: CargoTelepadMachineCircuitboard
- type: Appearance
- type: CollideOnAnchor
+ - type: NameIdentifier
+ group: CargoTelepads
id: Bounty
minValue: 0
maxValue: 999
+
+- type: nameIdentifierGroup
+ id: CargoTelepads
+ prefix: TELE
+ minValue: 0
+ maxValue: 999