diff --git a/Content.Client/Delivery/DeliveryVisualizerSystem.cs b/Content.Client/Delivery/DeliveryVisualizerSystem.cs index 8ed15344a4..bf4a8f066b 100644 --- a/Content.Client/Delivery/DeliveryVisualizerSystem.cs +++ b/Content.Client/Delivery/DeliveryVisualizerSystem.cs @@ -43,3 +43,9 @@ public enum DeliveryVisualLayers : byte Breakage, Trash, } + +public enum DeliverySpawnerVisualLayers : byte +{ + Contents, +} + diff --git a/Content.Server/Delivery/DeliverySystem.Spawning.cs b/Content.Server/Delivery/DeliverySystem.Spawning.cs index 19087e7448..a7496a343b 100644 --- a/Content.Server/Delivery/DeliverySystem.Spawning.cs +++ b/Content.Server/Delivery/DeliverySystem.Spawning.cs @@ -1,6 +1,6 @@ -using Content.Server.Power.EntitySystems; -using Content.Server.StationRecords; using Content.Shared.Delivery; +using Content.Shared.Power.EntitySystems; +using Content.Server.StationRecords; using Content.Shared.EntityTable; using Robust.Shared.Random; using Robust.Shared.Timing; @@ -16,7 +16,7 @@ public sealed partial class DeliverySystem [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly EntityTableSystem _entityTable = default!; - [Dependency] private readonly PowerReceiverSystem _power = default!; + [Dependency] private readonly SharedPowerReceiverSystem _power = default!; private void InitializeSpawning() { @@ -28,16 +28,14 @@ public sealed partial class DeliverySystem ent.Comp.NextDelivery = _timing.CurTime + ent.Comp.MinDeliveryCooldown; // We want an early wave of mail so cargo doesn't have to wait } - private void SpawnDelivery(Entity ent, int amount) + protected override void SpawnDeliveries(Entity ent) { if (!Resolve(ent.Owner, ref ent.Comp)) return; var coords = Transform(ent).Coordinates; - _audio.PlayPvs(ent.Comp.SpawnSound, ent.Owner); - - for (int i = 0; i < amount; i++) + for (int i = 0; i < ent.Comp.ContainedDeliveryAmount; i++) { var spawns = _entityTable.GetSpawns(ent.Comp.Table); @@ -46,9 +44,12 @@ public sealed partial class DeliverySystem Spawn(id, coords); } } + + ent.Comp.ContainedDeliveryAmount = 0; + Dirty(ent); } - private void SpawnStationDeliveries(Entity ent) + private void AdjustStationDeliveries(Entity ent) { if (!TryComp(ent, out var records)) return; @@ -72,7 +73,7 @@ public sealed partial class DeliverySystem { foreach (var spawner in spawners) { - SpawnDelivery(spawner, deliveryCount); + AddDeliveriesToSpawner(spawner, deliveryCount); } } else @@ -87,18 +88,18 @@ public sealed partial class DeliverySystem } for (int j = 0; j < spawners.Count; j++) { - SpawnDelivery(spawners[j], amounts[j]); + AddDeliveriesToSpawner(spawners[j], amounts[j]); } } } - private List GetValidSpawners(Entity ent) + private List> GetValidSpawners(Entity ent) { - var validSpawners = new List(); + var validSpawners = new List>(); var spawners = EntityQueryEnumerator(); - while (spawners.MoveNext(out var spawnerUid, out _)) + while (spawners.MoveNext(out var spawnerUid, out var spawnerComp)) { var spawnerStation = _station.GetOwningStation(spawnerUid); @@ -108,12 +109,23 @@ public sealed partial class DeliverySystem if (!_power.IsPowered(spawnerUid)) continue; - validSpawners.Add(spawnerUid); + if (spawnerComp.ContainedDeliveryAmount >= spawnerComp.MaxContainedDeliveryAmount) + continue; + + validSpawners.Add((spawnerUid, spawnerComp)); } return validSpawners; } + private void AddDeliveriesToSpawner(Entity ent, int amount) + { + ent.Comp.ContainedDeliveryAmount += Math.Clamp(amount, 0, ent.Comp.MaxContainedDeliveryAmount - ent.Comp.ContainedDeliveryAmount); + _audio.PlayPvs(ent.Comp.SpawnSound, ent.Owner); + UpdateDeliverySpawnerVisuals(ent, ent.Comp.ContainedDeliveryAmount); + Dirty(ent); + } + private void UpdateSpawner(float frameTime) { var dataQuery = EntityQueryEnumerator(); @@ -125,7 +137,7 @@ public sealed partial class DeliverySystem continue; deliveryData.NextDelivery += _random.Next(deliveryData.MinDeliveryCooldown, deliveryData.MaxDeliveryCooldown); // Random cooldown between min and max - SpawnStationDeliveries((uid, deliveryData)); + AdjustStationDeliveries((uid, deliveryData)); } } } diff --git a/Content.Shared/Delivery/DeliverySpawnerComponent.cs b/Content.Shared/Delivery/DeliverySpawnerComponent.cs index 4491509cb3..9ed3ff1b91 100644 --- a/Content.Shared/Delivery/DeliverySpawnerComponent.cs +++ b/Content.Shared/Delivery/DeliverySpawnerComponent.cs @@ -9,7 +9,7 @@ namespace Content.Shared.Delivery; /// Used to mark entities that are valid for spawning deliveries on. /// If this requires power, it needs to be powered to count as a valid spawner. /// -[RegisterComponent, NetworkedComponent] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] public sealed partial class DeliverySpawnerComponent : Component { /// @@ -18,9 +18,28 @@ public sealed partial class DeliverySpawnerComponent : Component [DataField(required: true)] public EntityTableSelector Table = default!; + /// + /// The max amount of deliveries this spawner can hold at a time. + /// + [DataField] + public int MaxContainedDeliveryAmount = 20; + + /// + /// The currently held amount of deliveries. + /// They are stored as an int and only spawned on use, as to not create additional entities without the need to. + /// + [DataField, AutoNetworkedField] + public int ContainedDeliveryAmount; + /// /// The sound to play when the spawner spawns a delivery. /// [DataField] public SoundSpecifier? SpawnSound = new SoundCollectionSpecifier("DeliverySpawnSounds", AudioParams.Default.WithVolume(-7)); + + /// + /// The sound to play when a spawner is opened, and spills all the deliveries out. + /// + [DataField] + public SoundSpecifier? OpenSound = new SoundCollectionSpecifier("storageRustle"); } diff --git a/Content.Shared/Delivery/DeliveryVisuals.cs b/Content.Shared/Delivery/DeliveryVisuals.cs index 6da45b6277..69eb5fda9e 100644 --- a/Content.Shared/Delivery/DeliveryVisuals.cs +++ b/Content.Shared/Delivery/DeliveryVisuals.cs @@ -13,3 +13,9 @@ public enum DeliveryVisuals : byte IsPriorityInactive, JobIcon, } + +[Serializable, NetSerializable] +public enum DeliverySpawnerVisuals : byte +{ + Contents, +} diff --git a/Content.Shared/Delivery/SharedDeliverySystem.cs b/Content.Shared/Delivery/SharedDeliverySystem.cs index 319bf41fce..8d343b4acb 100644 --- a/Content.Shared/Delivery/SharedDeliverySystem.cs +++ b/Content.Shared/Delivery/SharedDeliverySystem.cs @@ -38,12 +38,15 @@ public abstract class SharedDeliverySystem : EntitySystem { base.Initialize(); - SubscribeLocalEvent(OnExamine); + SubscribeLocalEvent(OnDeliveryExamine); SubscribeLocalEvent(OnUseInHand); - SubscribeLocalEvent>(OnGetVerbs); + SubscribeLocalEvent>(OnGetDeliveryVerbs); + + SubscribeLocalEvent(OnSpawnerExamine); + SubscribeLocalEvent>(OnGetSpawnerVerbs); } - private void OnExamine(Entity ent, ref ExaminedEvent args) + private void OnDeliveryExamine(Entity ent, ref ExaminedEvent args) { var jobTitle = ent.Comp.RecipientJobTitle ?? Loc.GetString("delivery-recipient-no-job"); var recipientName = ent.Comp.RecipientName ?? Loc.GetString("delivery-recipient-no-name"); @@ -56,6 +59,11 @@ public abstract class SharedDeliverySystem : EntitySystem args.PushText(Loc.GetString("delivery-recipient-examine", ("recipient", recipientName), ("job", jobTitle))); } + private void OnSpawnerExamine(Entity ent, ref ExaminedEvent args) + { + args.PushMarkup(Loc.GetString("delivery-teleporter-amount-examine", ("amount", ent.Comp.ContainedDeliveryAmount)), 50); + } + private void OnUseInHand(Entity ent, ref UseInHandEvent args) { args.Handled = true; @@ -69,7 +77,7 @@ public abstract class SharedDeliverySystem : EntitySystem OpenDelivery(ent, args.User); } - private void OnGetVerbs(Entity ent, ref GetVerbsEvent args) + private void OnGetDeliveryVerbs(Entity ent, ref GetVerbsEvent args) { if (!args.CanAccess || !args.CanInteract || args.Hands == null || ent.Comp.IsOpened) return; @@ -92,6 +100,33 @@ public abstract class SharedDeliverySystem : EntitySystem }); } + private void OnGetSpawnerVerbs(Entity ent, ref GetVerbsEvent args) + { + if (!args.CanAccess || !args.CanInteract || args.Hands == null) + return; + + var user = args.User; + + args.Verbs.Add(new AlternativeVerb() + { + Act = () => + { + _audio.PlayPredicted(ent.Comp.OpenSound, ent.Owner, user); + + if(ent.Comp.ContainedDeliveryAmount == 0) + { + _popup.PopupPredicted(Loc.GetString("delivery-teleporter-empty", ("entity", ent)), null, ent, user); + return; + } + + SpawnDeliveries(ent.Owner); + + UpdateDeliverySpawnerVisuals(ent, ent.Comp.ContainedDeliveryAmount); + }, + Text = Loc.GetString("delivery-teleporter-empty-verb"), + }); + } + private bool TryUnlockDelivery(Entity ent, EntityUid user, bool rewardMoney = true) { // Check fingerprint access if there is a reader on the mail @@ -168,7 +203,14 @@ public abstract class SharedDeliverySystem : EntitySystem _appearance.SetData(uid, DeliveryVisuals.IsPriority, false); } + protected void UpdateDeliverySpawnerVisuals(EntityUid uid, int contents) + { + _appearance.SetData(uid, DeliverySpawnerVisuals.Contents, contents > 0); + } + protected virtual void GrantSpesoReward(Entity ent) { } + + protected virtual void SpawnDeliveries(Entity ent) { } } /// diff --git a/Resources/Locale/en-US/delivery/delivery-component.ftl b/Resources/Locale/en-US/delivery/delivery-component.ftl index 499b708284..fbe4e74937 100644 --- a/Resources/Locale/en-US/delivery/delivery-component.ftl +++ b/Resources/Locale/en-US/delivery/delivery-component.ftl @@ -10,3 +10,11 @@ delivery-opened-others = {CAPITALIZE($recipient)} opened the {$delivery}. delivery-unlock-verb = Unlock delivery-open-verb = Open + +delivery-teleporter-amount-examine = + { $amount -> + [one] It contains [color=yellow]{$amount}[/color] delivery. + *[other] It contains [color=yellow]{$amount}[/color] deliveries. + } +delivery-teleporter-empty = The {$entity} is empty. +delivery-teleporter-empty-verb = Take mail diff --git a/Resources/Prototypes/Entities/Structures/Machines/mail_teleporter.yml b/Resources/Prototypes/Entities/Structures/Machines/mail_teleporter.yml index a03f166bbe..6842e3c958 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/mail_teleporter.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/mail_teleporter.yml @@ -20,6 +20,9 @@ snapCardinals: true layers: - state: icon + - state: contents + map: [ "enum.DeliverySpawnerVisualLayers.Contents" ] + visible: false - state: unlit shader: unshaded visible: false @@ -32,6 +35,10 @@ - type: Appearance - type: GenericVisualizer visuals: + enum.DeliverySpawnerVisuals.Contents: + enum.DeliverySpawnerVisualLayers.Contents: + True: { visible: true } + False: { visible: false } enum.PowerDeviceVisuals.Powered: enum.PowerDeviceVisualLayers.Powered: True: { visible: true } diff --git a/Resources/Textures/Structures/mailbox.rsi/contents.png b/Resources/Textures/Structures/mailbox.rsi/contents.png new file mode 100644 index 0000000000..45fab01257 Binary files /dev/null and b/Resources/Textures/Structures/mailbox.rsi/contents.png differ diff --git a/Resources/Textures/Structures/mailbox.rsi/meta.json b/Resources/Textures/Structures/mailbox.rsi/meta.json index ce3494c3d4..711646c2b3 100644 --- a/Resources/Textures/Structures/mailbox.rsi/meta.json +++ b/Resources/Textures/Structures/mailbox.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Created by meijunkie on Discord, funkystation.org at commit https://github.com/funky-station/funky-station/commit/430be9247ac220367f338c2c1a716a7503b1446d", + "copyright": "Created by meijunkie on Discord, funkystation.org at commit https://github.com/funky-station/funky-station/commit/430be9247ac220367f338c2c1a716a7503b1446d, contents by ScarKy0 (Github)", "size": { "x": 32, "y": 32 @@ -12,6 +12,9 @@ }, { "name": "unlit" + }, + { + "name": "contents" } ] }