From 3e843a9f7ab1397d6e2d2cbc97fd1f0afad70e5f Mon Sep 17 00:00:00 2001 From: Debug <49997488+DebugOk@users.noreply.github.com> Date: Mon, 12 Feb 2024 03:36:55 +0100 Subject: [PATCH] Add pain --- .../Item/PseudoItem/PseudoItemSystem.cs | 7 + .../Item/PseudoItem/PseudoItemSystem.cs | 21 ++- .../SharedPseudoItemSystem.Checks.cs | 165 ++++++++++++++++++ .../Item/PseudoItem/SharedPseudoItemSystem.cs | 97 +++++----- 4 files changed, 239 insertions(+), 51 deletions(-) create mode 100644 Content.Client/Nyanotrasen/Item/PseudoItem/PseudoItemSystem.cs create mode 100644 Content.Shared/Nyanotrasen/Item/PseudoItem/SharedPseudoItemSystem.Checks.cs diff --git a/Content.Client/Nyanotrasen/Item/PseudoItem/PseudoItemSystem.cs b/Content.Client/Nyanotrasen/Item/PseudoItem/PseudoItemSystem.cs new file mode 100644 index 00000000000..f9427362a66 --- /dev/null +++ b/Content.Client/Nyanotrasen/Item/PseudoItem/PseudoItemSystem.cs @@ -0,0 +1,7 @@ +using Content.Shared.Nyanotrasen.Item.PseudoItem; + +namespace Content.Client.Nyanotrasen.Item.PseudoItem; + +public sealed class PseudoItemSystem : SharedPseudoItemSystem +{ +} diff --git a/Content.Server/Nyanotrasen/Item/PseudoItem/PseudoItemSystem.cs b/Content.Server/Nyanotrasen/Item/PseudoItem/PseudoItemSystem.cs index 1c1ec1482d4..76cfe7d904b 100644 --- a/Content.Server/Nyanotrasen/Item/PseudoItem/PseudoItemSystem.cs +++ b/Content.Server/Nyanotrasen/Item/PseudoItem/PseudoItemSystem.cs @@ -1,18 +1,30 @@ -using Content.Shared.IdentityManagement; +using Content.Server.DoAfter; +using Content.Server.Item; +using Content.Server.Storage.EntitySystems; +using Content.Shared.DoAfter; +using Content.Shared.IdentityManagement; +using Content.Shared.Item; +using Content.Shared.Item.PseudoItem; using Content.Shared.Nyanotrasen.Item.PseudoItem; using Content.Shared.Storage; +using Content.Shared.Tag; using Content.Shared.Verbs; namespace Content.Server.Nyanotrasen.Item.PseudoItem; public sealed class PseudoItemSystem : SharedPseudoItemSystem { + [Dependency] private readonly StorageSystem _storage = default!; + [Dependency] private readonly ItemSystem _item = default!; + [Dependency] private readonly DoAfterSystem _doAfter = default!; + + public override void Initialize() { + base.Initialize(); SubscribeLocalEvent>(AddInsertAltVerb); } - // For whatever reason, I have to put these in server or the verbs duplicate private void AddInsertAltVerb(EntityUid uid, PseudoItemComponent component, GetVerbsEvent args) { if (!args.CanInteract || !args.CanAccess) @@ -21,10 +33,11 @@ private void AddInsertAltVerb(EntityUid uid, PseudoItemComponent component, GetV if (component.Active) return; - if (!TryComp(args.Using, out _)) + if (!TryComp(args.Using, out var targetStorage)) return; - // There *should* be a check here to see if we can fit, but I'm not aware of an easy way to do that, so eh, who cares + if (!CheckItemFits((uid, component), (args.Using.Value, targetStorage))) + return; if (args.Hands?.ActiveHandEntity == null) return; diff --git a/Content.Shared/Nyanotrasen/Item/PseudoItem/SharedPseudoItemSystem.Checks.cs b/Content.Shared/Nyanotrasen/Item/PseudoItem/SharedPseudoItemSystem.Checks.cs new file mode 100644 index 00000000000..7000c654048 --- /dev/null +++ b/Content.Shared/Nyanotrasen/Item/PseudoItem/SharedPseudoItemSystem.Checks.cs @@ -0,0 +1,165 @@ +using Content.Shared.Item; +using Content.Shared.Storage; + +namespace Content.Shared.Nyanotrasen.Item.PseudoItem; + +/// +/// Almost all of this is code taken from other systems, but adapted to use PseudoItem. +/// I couldn't use the original functions because the resolve would fuck shit up, even if I passed a constructed itemcomp +/// +/// This is horrible, and I hate it. But such is life +/// +public partial class SharedPseudoItemSystem +{ + protected bool CheckItemFits(Entity itemEnt, Entity storageEnt) + { + if (!Resolve(itemEnt, ref itemEnt.Comp) || !Resolve(storageEnt, ref storageEnt.Comp)) + return false; + + if (Transform(itemEnt).Anchored) + return false; + + if (storageEnt.Comp.Whitelist?.IsValid(itemEnt, EntityManager) == false) + return false; + + if (storageEnt.Comp.Blacklist?.IsValid(itemEnt, EntityManager) == true) + return false; + + var maxSize = _storage.GetMaxItemSize(storageEnt); + if (_item.GetSizePrototype(itemEnt.Comp.Size) > maxSize) + return false; + + // The following is shitfucked together straight from TryGetAvailableGridSpace, but eh, it works + + var itemComp = new ItemComponent + { Size = itemEnt.Comp.Size, Shape = itemEnt.Comp.Shape, StoredOffset = itemEnt.Comp.StoredOffset }; + + var storageBounding = storageEnt.Comp.Grid.GetBoundingBox(); + + Angle startAngle; + if (storageEnt.Comp.DefaultStorageOrientation == null) + startAngle = Angle.FromDegrees(-itemComp.StoredRotation); // PseudoItem doesn't support this + else + { + if (storageBounding.Width < storageBounding.Height) + { + startAngle = storageEnt.Comp.DefaultStorageOrientation == StorageDefaultOrientation.Horizontal + ? Angle.Zero + : Angle.FromDegrees(90); + } + else + { + startAngle = storageEnt.Comp.DefaultStorageOrientation == StorageDefaultOrientation.Vertical + ? Angle.Zero + : Angle.FromDegrees(90); + } + } + + for (var y = storageBounding.Bottom; y <= storageBounding.Top; y++) + { + for (var x = storageBounding.Left; x <= storageBounding.Right; x++) + { + for (var angle = startAngle; angle <= Angle.FromDegrees(360 - startAngle); angle += Math.PI / 2f) + { + var location = new ItemStorageLocation(angle, (x, y)); + if (ItemFitsInGridLocation(itemEnt, storageEnt, location.Position, location.Rotation)) + return true; + } + } + } + + return false; + } + + private bool ItemFitsInGridLocation( + Entity itemEnt, + Entity storageEnt, + Vector2i position, + Angle rotation) + { + if (!Resolve(itemEnt, ref itemEnt.Comp) || !Resolve(storageEnt, ref storageEnt.Comp)) + return false; + + var gridBounds = storageEnt.Comp.Grid.GetBoundingBox(); + if (!gridBounds.Contains(position)) + return false; + + var itemShape = GetAdjustedItemShape(itemEnt, rotation, position); + + foreach (var box in itemShape) + { + for (var offsetY = box.Bottom; offsetY <= box.Top; offsetY++) + { + for (var offsetX = box.Left; offsetX <= box.Right; offsetX++) + { + var pos = (offsetX, offsetY); + + if (!IsGridSpaceEmpty(itemEnt, storageEnt, pos, itemShape)) + return false; + } + } + } + + return true; + } + + private IReadOnlyList GetAdjustedItemShape(Entity entity, Angle rotation, + Vector2i position) + { + if (!Resolve(entity, ref entity.Comp)) + return new Box2i[] { }; + + var shapes = entity.Comp.Shape ?? _item.GetSizePrototype(entity.Comp.Size).DefaultShape; + var boundingShape = shapes.GetBoundingBox(); + var boundingCenter = ((Box2) boundingShape).Center; + var matty = Matrix3.CreateTransform(boundingCenter, rotation); + var drift = boundingShape.BottomLeft - matty.TransformBox(boundingShape).BottomLeft; + + var adjustedShapes = new List(); + foreach (var shape in shapes) + { + var transformed = matty.TransformBox(shape).Translated(drift); + var floored = new Box2i(transformed.BottomLeft.Floored(), transformed.TopRight.Floored()); + var translated = floored.Translated(position); + + adjustedShapes.Add(translated); + } + + return adjustedShapes; + } + + private bool IsGridSpaceEmpty(Entity itemEnt, Entity storageEnt, + Vector2i location, IReadOnlyList shape) + { + if (!Resolve(storageEnt, ref storageEnt.Comp)) + return false; + + var validGrid = false; + foreach (var grid in storageEnt.Comp.Grid) + { + if (grid.Contains(location)) + { + validGrid = true; + break; + } + } + + if (!validGrid) + return false; + + foreach (var (ent, storedItem) in storageEnt.Comp.StoredItems) + { + if (ent == itemEnt.Owner) + continue; + + var adjustedShape = shape; + foreach (var box in adjustedShape) + { + if (box.Contains(location)) + return false; + } + } + + return true; + } +} diff --git a/Content.Shared/Nyanotrasen/Item/PseudoItem/SharedPseudoItemSystem.cs b/Content.Shared/Nyanotrasen/Item/PseudoItem/SharedPseudoItemSystem.cs index 934a1e45be1..2705cff8dc6 100644 --- a/Content.Shared/Nyanotrasen/Item/PseudoItem/SharedPseudoItemSystem.cs +++ b/Content.Shared/Nyanotrasen/Item/PseudoItem/SharedPseudoItemSystem.cs @@ -12,12 +12,12 @@ namespace Content.Shared.Nyanotrasen.Item.PseudoItem; -public class SharedPseudoItemSystem : EntitySystem +public abstract partial class SharedPseudoItemSystem : EntitySystem { - [Dependency] private readonly SharedStorageSystem _storageSystem = default!; - [Dependency] private readonly SharedItemSystem _itemSystem = default!; + [Dependency] private readonly SharedStorageSystem _storage = default!; + [Dependency] private readonly SharedItemSystem _item = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; - [Dependency] private readonly TagSystem _tagSystem = default!; + [Dependency] private readonly TagSystem _tag = default!; [ValidatePrototypeId] private const string PreventTag = "PreventLabel"; @@ -29,9 +29,9 @@ public override void Initialize() SubscribeLocalEvent(OnEntRemoved); SubscribeLocalEvent(OnGettingPickedUpAttempt); SubscribeLocalEvent(OnDropAttempt); - SubscribeLocalEvent(OnDoAfter); SubscribeLocalEvent(OnInsertAttempt); SubscribeLocalEvent(OnInteractAttempt); + SubscribeLocalEvent(OnDoAfter); } private void AddInsertVerb(EntityUid uid, PseudoItemComponent component, GetVerbsEvent args) @@ -45,7 +45,8 @@ private void AddInsertVerb(EntityUid uid, PseudoItemComponent component, GetVerb if (!TryComp(args.Target, out var targetStorage)) return; - // There *should* be a check here to see if we can fit, but I'm not aware of an easy way to do that, so eh, who cares + if (!CheckItemFits((uid, component), (args.Target, targetStorage))) + return; if (Transform(args.Target).ParentUid == uid) return; @@ -62,6 +63,33 @@ private void AddInsertVerb(EntityUid uid, PseudoItemComponent component, GetVerb args.Verbs.Add(verb); } + private bool TryInsert(EntityUid storageUid, EntityUid toInsert, PseudoItemComponent component, + StorageComponent? storage = null) + { + if (!Resolve(storageUid, ref storage)) + return false; + + if (!CheckItemFits((toInsert, component), (storageUid, storage))) + return false; + + var itemComp = new ItemComponent + { Size = component.Size, Shape = component.Shape, StoredOffset = component.StoredOffset }; + AddComp(toInsert, itemComp); + _item.VisualsChanged(toInsert); + + _tag.TryAddTag(toInsert, PreventTag); + + if (!_storage.Insert(storageUid, toInsert, out _, null, storage)) + { + component.Active = false; + RemComp(toInsert); + return false; + } + + component.Active = true; + return true; + } + private void OnEntRemoved(EntityUid uid, PseudoItemComponent component, EntGotRemovedFromContainerMessage args) { if (!component.Active) @@ -87,40 +115,31 @@ private void OnDropAttempt(EntityUid uid, PseudoItemComponent component, DropAtt args.Cancel(); } - private void OnDoAfter(EntityUid uid, PseudoItemComponent component, DoAfterEvent args) + private void OnInsertAttempt(EntityUid uid, PseudoItemComponent component, + ContainerGettingInsertedAttemptEvent args) { - if (args.Handled || args.Cancelled || args.Args.Used == null) + if (!component.Active) return; - - args.Handled = TryInsert(args.Args.Used.Value, uid, component); + // This hopefully shouldn't trigger, but this is a failsafe just in case so we dont bluespace them cats + args.Cancel(); } - public bool TryInsert(EntityUid storageUid, EntityUid toInsert, PseudoItemComponent component, - StorageComponent? storage = null) + // Prevents moving within the bag :) + private void OnInteractAttempt(EntityUid uid, PseudoItemComponent component, InteractionAttemptEvent args) { - if (!Resolve(storageUid, ref storage)) - return false; - - // Again, here we really should check if the item will fit, but at least insert takes care of it for us by failing if not /shrug - - var itemComp = new ItemComponent { Size = component.Size, Shape = component.Shape, StoredOffset = component.StoredOffset }; - AddComp(toInsert, itemComp); - _itemSystem.VisualsChanged(toInsert); - - _tagSystem.TryAddTag(toInsert, PreventTag); + if (args.Uid == args.Target && component.Active) + args.Cancel(); + } - if (!_storageSystem.Insert(storageUid, toInsert, out _, null, storage)) - { - component.Active = false; - RemComp(toInsert); - return false; - } + private void OnDoAfter(EntityUid uid, PseudoItemComponent component, DoAfterEvent args) + { + if (args.Handled || args.Cancelled || args.Args.Used == null) + return; - component.Active = true; - return true; + args.Handled = TryInsert(args.Args.Used.Value, uid, component); } - protected internal void StartInsertDoAfter(EntityUid inserter, EntityUid toInsert, EntityUid storageEntity, + protected void StartInsertDoAfter(EntityUid inserter, EntityUid toInsert, EntityUid storageEntity, PseudoItemComponent? pseudoItem = null) { if (!Resolve(toInsert, ref pseudoItem)) @@ -136,20 +155,4 @@ protected internal void StartInsertDoAfter(EntityUid inserter, EntityUid toInser _doAfter.TryStartDoAfter(args); } - - private void OnInsertAttempt(EntityUid uid, PseudoItemComponent component, - ContainerGettingInsertedAttemptEvent args) - { - if (!component.Active) - return; - // This hopefully shouldn't trigger, but this is a failsafe just in case so we dont bluespace them cats - args.Cancel(); - } - - // Prevents moving within the bag :) - private void OnInteractAttempt(EntityUid uid, PseudoItemComponent component, InteractionAttemptEvent args) - { - if (args.Uid == args.Target && component.Active) - args.Cancel(); - } }