Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix PseudoItems #821

Merged
merged 3 commits into from
Feb 12, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
using Content.Shared.Nyanotrasen.Item.PseudoItem;

namespace Content.Client.Nyanotrasen.Item.PseudoItem;

public sealed class PseudoItemSystem : SharedPseudoItemSystem
{
}
21 changes: 17 additions & 4 deletions Content.Server/Nyanotrasen/Item/PseudoItem/PseudoItemSystem.cs
Original file line number Diff line number Diff line change
@@ -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<PseudoItemComponent, GetVerbsEvent<AlternativeVerb>>(AddInsertAltVerb);
}

// For whatever reason, I have to put these in server or the verbs duplicate
private void AddInsertAltVerb(EntityUid uid, PseudoItemComponent component, GetVerbsEvent<AlternativeVerb> args)
{
if (!args.CanInteract || !args.CanAccess)
Expand All @@ -21,10 +33,11 @@ private void AddInsertAltVerb(EntityUid uid, PseudoItemComponent component, GetV
if (component.Active)
return;

if (!TryComp<StorageComponent>(args.Using, out _))
if (!TryComp<StorageComponent>(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;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
using Content.Shared.Item;
using Content.Shared.Storage;

namespace Content.Shared.Nyanotrasen.Item.PseudoItem;

/// <summary>
/// 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

creating a new component to pass into :

  1. would fail resolve since its not actually added to the entity
  2. extremely sus

im guessing this is just to make it so a felenid cant be picked up instantly, would be better to just have that be an event raised for ItemComponent that felenids cancel, so they still reuse all the logic but cant just left click to pick up like a mouse

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pseudoitems lack the itemcomponent when they're not inside a container because it brings a ton of unwanted features along with it. Even just adding it to run the check could result in weird shit happening if someone knew enough. This approach works "fine" for what its supposed to do

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The entire reason I cant use the original functions as is is because they expect the item to have an itemcomponent, and theres no way to tell it to use anything else, even if it doesn't directly do anything with said component

///
/// This is horrible, and I hate it. But such is life
/// </summary>
public partial class SharedPseudoItemSystem
{
protected bool CheckItemFits(Entity<PseudoItemComponent?> itemEnt, Entity<StorageComponent?> 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<PseudoItemComponent?> itemEnt,
Entity<StorageComponent?> 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<Box2i> GetAdjustedItemShape(Entity<PseudoItemComponent?> 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<Box2i>();
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<PseudoItemComponent?> itemEnt, Entity<StorageComponent?> storageEnt,
Vector2i location, IReadOnlyList<Box2i> 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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<TagPrototype>]
private const string PreventTag = "PreventLabel";
DebugOk marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -29,9 +29,9 @@ public override void Initialize()
SubscribeLocalEvent<PseudoItemComponent, EntGotRemovedFromContainerMessage>(OnEntRemoved);
SubscribeLocalEvent<PseudoItemComponent, GettingPickedUpAttemptEvent>(OnGettingPickedUpAttempt);
SubscribeLocalEvent<PseudoItemComponent, DropAttemptEvent>(OnDropAttempt);
SubscribeLocalEvent<PseudoItemComponent, PseudoItemInsertDoAfterEvent>(OnDoAfter);
SubscribeLocalEvent<PseudoItemComponent, ContainerGettingInsertedAttemptEvent>(OnInsertAttempt);
SubscribeLocalEvent<PseudoItemComponent, InteractionAttemptEvent>(OnInteractAttempt);
SubscribeLocalEvent<PseudoItemComponent, PseudoItemInsertDoAfterEvent>(OnDoAfter);
}

private void AddInsertVerb(EntityUid uid, PseudoItemComponent component, GetVerbsEvent<InnateVerb> args)
Expand All @@ -45,7 +45,8 @@ private void AddInsertVerb(EntityUid uid, PseudoItemComponent component, GetVerb
if (!TryComp<StorageComponent>(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;
Expand All @@ -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<ItemComponent>(toInsert);
return false;
}

component.Active = true;
return true;
}

private void OnEntRemoved(EntityUid uid, PseudoItemComponent component, EntGotRemovedFromContainerMessage args)
{
if (!component.Active)
Expand All @@ -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<ItemComponent>(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))
Expand All @@ -136,20 +155,4 @@ protected internal void StartInsertDoAfter(EntityUid inserter, EntityUid toInser

_doAfter.TryStartDoAfter(args);
}
DebugOk marked this conversation as resolved.
Show resolved Hide resolved

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();
}
}
Loading