diff --git a/Content.Server/Storage/Components/PickRandomComponent.cs b/Content.Server/Storage/Components/PickRandomComponent.cs new file mode 100644 index 0000000000..1046c55911 --- /dev/null +++ b/Content.Server/Storage/Components/PickRandomComponent.cs @@ -0,0 +1,31 @@ +using Content.Server.Storage.EntitySystems; +using Content.Shared.Whitelist; + +namespace Content.Server.Storage.Components; + +/// +/// Adds a verb to pick a random item from a container. +/// Only picks items that match the whitelist. +/// +[RegisterComponent] +[Access(typeof(PickRandomSystem))] +public sealed class PickRandomComponent : Component +{ + /// + /// Whitelist for potential picked items. + /// + [DataField("whitelist"), ViewVariables(VVAccess.ReadWrite)] + public EntityWhitelist? Whitelist; + + /// + /// Locale id for the pick verb text. + /// + [DataField("verbText"), ViewVariables(VVAccess.ReadWrite)] + public string VerbText = "comp-pick-random-verb-text"; + + /// + /// Locale id for the empty storage message. + /// + [DataField("emptyText"), ViewVariables(VVAccess.ReadWrite)] + public string EmptyText = "comp-pick-random-empty"; +} diff --git a/Content.Server/Storage/EntitySystems/PickRandomSystem.cs b/Content.Server/Storage/EntitySystems/PickRandomSystem.cs new file mode 100644 index 0000000000..eb48829b26 --- /dev/null +++ b/Content.Server/Storage/EntitySystems/PickRandomSystem.cs @@ -0,0 +1,61 @@ +using Content.Server.Storage.Components; +using Content.Shared.Database; +using Content.Shared.Hands.EntitySystems; +using Content.Shared.Verbs; +using Robust.Shared.Containers; +using Robust.Shared.Random; +using System.Linq; + +namespace Content.Server.Storage.EntitySystems; + +// TODO: move this to shared for verb prediction if/when storage is in shared +public sealed class PickRandomSystem : EntitySystem +{ + [Dependency] private readonly SharedContainerSystem _container = default!; + [Dependency] private readonly SharedHandsSystem _hands = default!; + [Dependency] private readonly IRobustRandom _random = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent>(OnGetAlternativeVerbs); + } + + private void OnGetAlternativeVerbs(EntityUid uid, PickRandomComponent comp, GetVerbsEvent args) + { + if (!args.CanAccess || !args.CanInteract || !TryComp(uid, out var storage)) + return; + + var user = args.User; + + // alt-click / alt-z to pick an item + args.Verbs.Add(new AlternativeVerb + { + Act = (() => { + TryPick(uid, comp, storage, user); + }), + Impact = LogImpact.Low, + Text = Loc.GetString(comp.VerbText), + Disabled = !(storage.StoredEntities?.Any(item => comp.Whitelist?.IsValid(item, EntityManager) ?? true) ?? false), + Message = Loc.GetString(comp.EmptyText, ("storage", uid)) + }); + } + + private void TryPick(EntityUid uid, PickRandomComponent comp, ServerStorageComponent storage, EntityUid user) + { + if (storage.StoredEntities == null) + return; + + var entities = storage.StoredEntities.Where(item => comp.Whitelist?.IsValid(item, EntityManager) ?? true); + if (!entities.Any()) + return; + + var picked = _random.Pick(entities.ToList()); + // if it fails to go into a hand of the user, will be on the storage + _container.AttachParentToContainerOrGrid(Transform(picked)); + + // TODO: try to put in hands, failing that put it on the storage + _hands.TryPickupAnyHand(user, picked); + } +} diff --git a/Resources/Locale/en-US/storage/components/pick-random-component.ftl b/Resources/Locale/en-US/storage/components/pick-random-component.ftl new file mode 100644 index 0000000000..a3e72a0e6a --- /dev/null +++ b/Resources/Locale/en-US/storage/components/pick-random-component.ftl @@ -0,0 +1,2 @@ +comp-pick-random-empty = {CAPITALIZE(THE($storage))} is empty! +comp-pick-random-verb-text = Pick item