Grid Inventory (#21931)
* Grid Inventory * oh boy we keep cracking on * auto insertion is kinda working? gross, too! * pieces and proper layouts * fix the sprites * mousing over grid pieces... finally * dragging deez nuts all over the screen * eek! * dragging is 90% less horrendous * auto-rotating * flatten * Rotation at last * fix rotation and change keybind for removing items. * rebinding and keybinding * wow! look at that! configurable with a button! cool! * dragging is a bit cooler, eh? * hover insert, my beloved * add some grids for storage, fix 1x1 storages, fix multiple inputs at once * el navigation * oh yeah some stuff i forgor * more fixes and QOL stuff * the griddening * the last of it (yippee) * sloth review :)
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.CombatMode;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Content.Shared.Destructible;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Implants.Components;
|
||||
@@ -17,7 +17,6 @@ using Content.Shared.Stacks;
|
||||
using Content.Shared.Storage.Components;
|
||||
using Content.Shared.Timing;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Map;
|
||||
@@ -35,15 +34,16 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
[Dependency] private readonly EntityLookupSystem _entityLookupSystem = default!;
|
||||
[Dependency] protected readonly SharedEntityStorageSystem EntityStorage = default!;
|
||||
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
|
||||
[Dependency] private readonly SharedItemSystem _item = default!;
|
||||
[Dependency] protected readonly SharedItemSystem ItemSystem = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _sharedHandsSystem = default!;
|
||||
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] protected readonly SharedAudioSystem Audio = default!;
|
||||
[Dependency] private readonly SharedCombatModeSystem _combatMode = default!;
|
||||
[Dependency] protected readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] protected readonly SharedTransformSystem TransformSystem = default!;
|
||||
[Dependency] private readonly SharedStackSystem _stack = default!;
|
||||
[Dependency] private readonly SharedUserInterfaceSystem _ui = default!;
|
||||
[Dependency] protected readonly UseDelaySystem UseDelay = default!;
|
||||
|
||||
private EntityQuery<ItemComponent> _itemQuery;
|
||||
@@ -69,17 +69,18 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
SubscribeLocalEvent<StorageComponent, OpenStorageImplantEvent>(OnImplantActivate);
|
||||
SubscribeLocalEvent<StorageComponent, AfterInteractEvent>(AfterInteract);
|
||||
SubscribeLocalEvent<StorageComponent, DestructionEventArgs>(OnDestroy);
|
||||
SubscribeLocalEvent<StorageComponent, StorageComponent.StorageInsertItemMessage>(OnInsertItemMessage);
|
||||
SubscribeLocalEvent<StorageComponent, BoundUIOpenedEvent>(OnBoundUIOpen);
|
||||
SubscribeLocalEvent<MetaDataComponent, StackCountChangedEvent>(OnStackCountChanged);
|
||||
|
||||
SubscribeLocalEvent<StorageComponent, EntInsertedIntoContainerMessage>(OnContainerModified);
|
||||
SubscribeLocalEvent<StorageComponent, EntRemovedFromContainerMessage>(OnContainerModified);
|
||||
SubscribeLocalEvent<StorageComponent, EntInsertedIntoContainerMessage>(OnEntInserted);
|
||||
SubscribeLocalEvent<StorageComponent, EntRemovedFromContainerMessage>(OnEntRemoved);
|
||||
SubscribeLocalEvent<StorageComponent, ContainerIsInsertingAttemptEvent>(OnInsertAttempt);
|
||||
|
||||
SubscribeLocalEvent<StorageComponent, AreaPickupDoAfterEvent>(OnDoAfter);
|
||||
|
||||
SubscribeLocalEvent<StorageComponent, StorageInteractWithItemEvent>(OnInteractWithItem);
|
||||
SubscribeAllEvent<StorageInteractWithItemEvent>(OnInteractWithItem);
|
||||
SubscribeAllEvent<StorageSetItemLocationEvent>(OnSetItemLocation);
|
||||
SubscribeAllEvent<StorageInsertItemIntoLocationEvent>(OnInsertItemIntoLocation);
|
||||
}
|
||||
|
||||
private void OnComponentInit(EntityUid uid, StorageComponent storageComp, ComponentInit args)
|
||||
@@ -132,7 +133,7 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
PlayerInsertHeldEntity(uid, args.User, storageComp);
|
||||
// Always handle it, even if insertion fails.
|
||||
// We don't want to trigger any AfterInteract logic here.
|
||||
// Example bug: placing wires if item doesn't fit in backpack.
|
||||
// Example issue would be placing wires if item doesn't fit in backpack.
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
@@ -145,6 +146,7 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
return;
|
||||
|
||||
OpenStorageUI(uid, args.User, storageComp);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -152,11 +154,11 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
/// </summary>
|
||||
private void OnImplantActivate(EntityUid uid, StorageComponent storageComp, OpenStorageImplantEvent args)
|
||||
{
|
||||
// TODO: Make this an action or something.
|
||||
if (args.Handled || !_xformQuery.TryGetComponent(uid, out var xform))
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
OpenStorageUI(uid, xform.ParentUid, storageComp);
|
||||
OpenStorageUI(uid, args.Performer, storageComp);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -224,8 +226,8 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
|
||||
var position = EntityCoordinates.FromMap(
|
||||
parent.IsValid() ? parent : uid,
|
||||
transformEnt.MapPosition,
|
||||
_transform
|
||||
TransformSystem.GetMapCoordinates(transformEnt),
|
||||
TransformSystem
|
||||
);
|
||||
|
||||
args.Handled = true;
|
||||
@@ -270,8 +272,8 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
|
||||
var position = EntityCoordinates.FromMap(
|
||||
xform.ParentUid.IsValid() ? xform.ParentUid : uid,
|
||||
new MapCoordinates(_transform.GetWorldPosition(targetXform), targetXform.MapID),
|
||||
_transform
|
||||
new MapCoordinates(TransformSystem.GetWorldPosition(targetXform), targetXform.MapID),
|
||||
TransformSystem
|
||||
);
|
||||
|
||||
var angle = targetXform.LocalRotation;
|
||||
@@ -284,7 +286,7 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
// If we picked up atleast one thing, play a sound and do a cool animation!
|
||||
// If we picked up at least one thing, play a sound and do a cool animation!
|
||||
if (successfullyInserted.Count > 0)
|
||||
{
|
||||
Audio.PlayPvs(component.StorageInsertSound, uid);
|
||||
@@ -300,7 +302,7 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
|
||||
private void OnDestroy(EntityUid uid, StorageComponent storageComp, DestructionEventArgs args)
|
||||
{
|
||||
var coordinates = _transform.GetMoverCoordinates(uid);
|
||||
var coordinates = TransformSystem.GetMoverCoordinates(uid);
|
||||
|
||||
// Being destroyed so need to recalculate.
|
||||
_containerSystem.EmptyContainer(storageComp.Container, destination: coordinates);
|
||||
@@ -311,16 +313,24 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
/// item in the user's hand if it is currently empty, or interact with the item using the user's currently
|
||||
/// held item.
|
||||
/// </summary>
|
||||
private void OnInteractWithItem(EntityUid uid, StorageComponent storageComp, StorageInteractWithItemEvent args)
|
||||
private void OnInteractWithItem(StorageInteractWithItemEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
if (args.Session.AttachedEntity is not { } player)
|
||||
if (args.SenderSession.AttachedEntity is not { } player)
|
||||
return;
|
||||
|
||||
var entity = GetEntity(args.InteractedItemUID);
|
||||
var uid = GetEntity(msg.StorageUid);
|
||||
var entity = GetEntity(msg.InteractedItemUid);
|
||||
|
||||
if (!TryComp<StorageComponent>(uid, out var storageComp))
|
||||
return;
|
||||
|
||||
if (!_ui.TryGetUi(uid, StorageComponent.StorageUiKey.Key, out var bui) ||
|
||||
!bui.SubscribedSessions.Contains(args.SenderSession))
|
||||
return;
|
||||
|
||||
if (!Exists(entity))
|
||||
{
|
||||
Log.Error($"Player {args.Session} interacted with non-existent item {args.InteractedItemUID} stored in {ToPrettyString(uid)}");
|
||||
Log.Error($"Player {args.SenderSession} interacted with non-existent item {msg.InteractedItemUid} stored in {ToPrettyString(uid)}");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -346,12 +356,58 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
_interactionSystem.InteractUsing(player, hands.ActiveHandEntity.Value, entity, Transform(entity).Coordinates, checkCanInteract: false);
|
||||
}
|
||||
|
||||
private void OnInsertItemMessage(EntityUid uid, StorageComponent storageComp, StorageComponent.StorageInsertItemMessage args)
|
||||
private void OnSetItemLocation(StorageSetItemLocationEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
if (args.Session.AttachedEntity == null)
|
||||
if (args.SenderSession.AttachedEntity is not { } player)
|
||||
return;
|
||||
|
||||
PlayerInsertHeldEntity(uid, args.Session.AttachedEntity.Value, storageComp);
|
||||
var storageEnt = GetEntity(msg.StorageEnt);
|
||||
var itemEnt = GetEntity(msg.ItemEnt);
|
||||
|
||||
if (!TryComp<StorageComponent>(storageEnt, out var storageComp))
|
||||
return;
|
||||
|
||||
if (!_ui.TryGetUi(storageEnt, StorageComponent.StorageUiKey.Key, out var bui) ||
|
||||
!bui.SubscribedSessions.Contains(args.SenderSession))
|
||||
return;
|
||||
|
||||
if (!Exists(itemEnt))
|
||||
{
|
||||
Log.Error($"Player {args.SenderSession} set location of non-existent item {msg.ItemEnt} stored in {ToPrettyString(storageEnt)}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_actionBlockerSystem.CanInteract(player, itemEnt))
|
||||
return;
|
||||
|
||||
TrySetItemStorageLocation((itemEnt, null), (storageEnt, storageComp), msg.Location);
|
||||
}
|
||||
|
||||
private void OnInsertItemIntoLocation(StorageInsertItemIntoLocationEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
if (args.SenderSession.AttachedEntity is not { } player)
|
||||
return;
|
||||
|
||||
var storageEnt = GetEntity(msg.StorageEnt);
|
||||
var itemEnt = GetEntity(msg.ItemEnt);
|
||||
|
||||
if (!TryComp<StorageComponent>(storageEnt, out var storageComp))
|
||||
return;
|
||||
|
||||
if (!_ui.TryGetUi(storageEnt, StorageComponent.StorageUiKey.Key, out var bui) ||
|
||||
!bui.SubscribedSessions.Contains(args.SenderSession))
|
||||
return;
|
||||
|
||||
if (!Exists(itemEnt))
|
||||
{
|
||||
Log.Error($"Player {args.SenderSession} set location of non-existent item {msg.ItemEnt} stored in {ToPrettyString(storageEnt)}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_actionBlockerSystem.CanInteract(player, itemEnt) || !_sharedHandsSystem.IsHolding(player, itemEnt, out _))
|
||||
return;
|
||||
|
||||
InsertAt((storageEnt, storageComp), (itemEnt, null), msg.Location, out _, player);
|
||||
}
|
||||
|
||||
private void OnBoundUIOpen(EntityUid uid, StorageComponent storageComp, BoundUIOpenedEvent args)
|
||||
@@ -363,17 +419,45 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
private void OnContainerModified(EntityUid uid, StorageComponent component, ContainerModifiedMessage args)
|
||||
private void OnEntInserted(Entity<StorageComponent> entity, ref EntInsertedIntoContainerMessage args)
|
||||
{
|
||||
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||
if (component.Container == null)
|
||||
if (entity.Comp.Container == null)
|
||||
return;
|
||||
|
||||
if (args.Container.ID != StorageComponent.ContainerId)
|
||||
return;
|
||||
|
||||
UpdateAppearance((uid, component, null));
|
||||
UpdateUI((uid, component));
|
||||
if (!entity.Comp.StoredItems.ContainsKey(GetNetEntity(args.Entity)))
|
||||
{
|
||||
if (!TryGetAvailableGridSpace((entity.Owner, entity.Comp), (args.Entity, null), out var location))
|
||||
{
|
||||
_containerSystem.Remove(args.Entity, args.Container, force: true);
|
||||
return;
|
||||
}
|
||||
|
||||
entity.Comp.StoredItems[GetNetEntity(args.Entity)] = location.Value;
|
||||
Dirty(entity, entity.Comp);
|
||||
}
|
||||
|
||||
UpdateAppearance((entity, entity.Comp, null));
|
||||
UpdateUI((entity, entity.Comp));
|
||||
}
|
||||
|
||||
private void OnEntRemoved(Entity<StorageComponent> entity, ref EntRemovedFromContainerMessage args)
|
||||
{
|
||||
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||
if (entity.Comp.Container == null)
|
||||
return;
|
||||
|
||||
if (args.Container.ID != StorageComponent.ContainerId)
|
||||
return;
|
||||
|
||||
entity.Comp.StoredItems.Remove(GetNetEntity(args.Entity));
|
||||
Dirty(entity, entity.Comp);
|
||||
|
||||
UpdateAppearance((entity, entity.Comp, null));
|
||||
UpdateUI((entity, entity.Comp));
|
||||
}
|
||||
|
||||
private void OnInsertAttempt(EntityUid uid, StorageComponent component, ContainerIsInsertingAttemptEvent args)
|
||||
@@ -396,18 +480,8 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
if (storage.Container == null)
|
||||
return; // component hasn't yet been initialized.
|
||||
|
||||
int capacity;
|
||||
int used;
|
||||
if (storage.MaxSlots == null)
|
||||
{
|
||||
used = GetCumulativeItemSizes(uid, storage);
|
||||
capacity = storage.MaxTotalWeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
capacity = storage.MaxSlots.Value;
|
||||
used = storage.Container.ContainedEntities.Count;
|
||||
}
|
||||
var capacity = storage.Grid.GetArea();
|
||||
var used = GetCumulativeItemAreas((uid, storage));
|
||||
|
||||
_appearance.SetData(uid, StorageVisuals.StorageUsed, used, appearance);
|
||||
_appearance.SetData(uid, StorageVisuals.Capacity, capacity, appearance);
|
||||
@@ -450,8 +524,17 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
/// <param name="reason">If returning false, the reason displayed to the player</param>
|
||||
/// <param name="storageComp"></param>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="ignoreStacks"></param>
|
||||
/// <param name="ignoreLocation"></param>
|
||||
/// <returns>true if it can be inserted, false otherwise</returns>
|
||||
public bool CanInsert(EntityUid uid, EntityUid insertEnt, out string? reason, StorageComponent? storageComp = null, ItemComponent? item = null, bool ignoreStacks = false)
|
||||
public bool CanInsert(
|
||||
EntityUid uid,
|
||||
EntityUid insertEnt,
|
||||
out string? reason,
|
||||
StorageComponent? storageComp = null,
|
||||
ItemComponent? item = null,
|
||||
bool ignoreStacks = false,
|
||||
bool ignoreLocation = false)
|
||||
{
|
||||
if (!Resolve(uid, ref storageComp) || !Resolve(insertEnt, ref item, false))
|
||||
{
|
||||
@@ -485,38 +568,58 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
return true;
|
||||
}
|
||||
|
||||
var maxSize = _item.GetSizePrototype(GetMaxItemSize((uid, storageComp)));
|
||||
if (_item.GetSizePrototype(item.Size) > maxSize)
|
||||
var maxSize = ItemSystem.GetSizePrototype(GetMaxItemSize((uid, storageComp)));
|
||||
if (ItemSystem.GetSizePrototype(item.Size) > maxSize)
|
||||
{
|
||||
reason = "comp-storage-too-big";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TryComp<StorageComponent>(insertEnt, out var insertStorage)
|
||||
&& _item.GetSizePrototype(GetMaxItemSize((insertEnt, insertStorage))) >= maxSize)
|
||||
&& ItemSystem.GetSizePrototype(GetMaxItemSize((insertEnt, insertStorage))) >= maxSize)
|
||||
{
|
||||
reason = "comp-storage-too-big";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (storageComp.MaxSlots != null)
|
||||
if (!ignoreLocation && !storageComp.StoredItems.ContainsKey(GetNetEntity(insertEnt)))
|
||||
{
|
||||
if (storageComp.Container.ContainedEntities.Count >= storageComp.MaxSlots)
|
||||
if (!TryGetAvailableGridSpace((uid, storageComp), (insertEnt, item), out _))
|
||||
{
|
||||
reason = "comp-storage-insufficient-capacity";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (_item.GetItemSizeWeight(item.Size) + GetCumulativeItemSizes(uid, storageComp) > storageComp.MaxTotalWeight)
|
||||
{
|
||||
reason = "comp-storage-insufficient-capacity";
|
||||
return false;
|
||||
}
|
||||
|
||||
reason = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts into the storage container at a given location
|
||||
/// </summary>
|
||||
/// <returns>true if the entity was inserted, false otherwise. This will also return true if a stack was partially
|
||||
/// inserted.</returns>
|
||||
public bool InsertAt(
|
||||
Entity<StorageComponent?> uid,
|
||||
Entity<ItemComponent?> insertEnt,
|
||||
ItemStorageLocation location,
|
||||
out EntityUid? stackedEntity,
|
||||
EntityUid? user = null,
|
||||
bool playSound = true)
|
||||
{
|
||||
stackedEntity = null;
|
||||
if (!Resolve(uid, ref uid.Comp))
|
||||
return false;
|
||||
|
||||
if (!ItemFitsInGridLocation(insertEnt, uid, location))
|
||||
return false;
|
||||
|
||||
uid.Comp.StoredItems[GetNetEntity(insertEnt)] = location;
|
||||
Dirty(uid, uid.Comp);
|
||||
return Insert(uid, insertEnt, out stackedEntity, out _, user: user, storageComp: uid.Comp, playSound: playSound);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts into the storage container
|
||||
/// </summary>
|
||||
@@ -653,6 +756,148 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to set the location of an item already inside of a storage container.
|
||||
/// </summary>
|
||||
public bool TrySetItemStorageLocation(Entity<ItemComponent?> itemEnt, Entity<StorageComponent?> storageEnt, ItemStorageLocation location)
|
||||
{
|
||||
if (!Resolve(itemEnt, ref itemEnt.Comp) || !Resolve(storageEnt, ref storageEnt.Comp))
|
||||
return false;
|
||||
|
||||
if (!storageEnt.Comp.Container.ContainedEntities.Contains(itemEnt))
|
||||
return false;
|
||||
|
||||
if (!ItemFitsInGridLocation(itemEnt, storageEnt, location.Position, location.Rotation))
|
||||
return false;
|
||||
|
||||
storageEnt.Comp.StoredItems[GetNetEntity(itemEnt)] = location;
|
||||
Dirty(storageEnt, storageEnt.Comp);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find the first available spot on a storage grid.
|
||||
/// starts at the top-left and goes right and down.
|
||||
/// </summary>
|
||||
public bool TryGetAvailableGridSpace(
|
||||
Entity<StorageComponent?> storageEnt,
|
||||
Entity<ItemComponent?> itemEnt,
|
||||
[NotNullWhen(true)] out ItemStorageLocation? storageLocation)
|
||||
{
|
||||
storageLocation = null;
|
||||
|
||||
if (!Resolve(storageEnt, ref storageEnt.Comp) || !Resolve(itemEnt, ref itemEnt.Comp))
|
||||
return false;
|
||||
|
||||
var storageBounding = storageEnt.Comp.Grid.GetBoundingBox();
|
||||
|
||||
for (var y = storageBounding.Bottom; y <= storageBounding.Top; y++)
|
||||
{
|
||||
for (var x = storageBounding.Left; x <= storageBounding.Right; x++)
|
||||
{
|
||||
for (var angle = Angle.Zero; angle <= Angle.FromDegrees(360); angle += Math.PI / 2f)
|
||||
{
|
||||
var location = new ItemStorageLocation(angle, (x, y));
|
||||
if (ItemFitsInGridLocation(itemEnt, storageEnt, location))
|
||||
{
|
||||
storageLocation = location;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if an item fits into a specific spot on a storage grid.
|
||||
/// </summary>
|
||||
public bool ItemFitsInGridLocation(
|
||||
Entity<ItemComponent?> itemEnt,
|
||||
Entity<StorageComponent?> storageEnt,
|
||||
ItemStorageLocation location)
|
||||
{
|
||||
return ItemFitsInGridLocation(itemEnt, storageEnt, location.Position, location.Rotation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if an item fits into a specific spot on a storage grid.
|
||||
/// </summary>
|
||||
public bool ItemFitsInGridLocation(
|
||||
Entity<ItemComponent?> 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 = ItemSystem.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))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a space on a grid is valid and not occupied by any other pieces.
|
||||
/// </summary>
|
||||
public bool IsGridSpaceEmpty(Entity<ItemComponent?> itemEnt, Entity<StorageComponent?> storageEnt, Vector2i location)
|
||||
{
|
||||
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 (netEnt, storedItem) in storageEnt.Comp.StoredItems)
|
||||
{
|
||||
var ent = GetEntity(netEnt);
|
||||
|
||||
if (ent == itemEnt.Owner)
|
||||
continue;
|
||||
|
||||
if (!_itemQuery.TryGetComponent(ent, out var itemComp))
|
||||
continue;
|
||||
|
||||
var adjustedShape = ItemSystem.GetAdjustedItemShape((ent, itemComp), storedItem);
|
||||
foreach (var box in adjustedShape)
|
||||
{
|
||||
if (box.Contains(location))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if there is enough space to theoretically fit another item.
|
||||
/// </summary>
|
||||
@@ -661,13 +906,7 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
if (!Resolve(uid, ref uid.Comp))
|
||||
return false;
|
||||
|
||||
//todo maybe this shouldn't be authoritative over weight? idk.
|
||||
if (uid.Comp.MaxSlots != null)
|
||||
{
|
||||
return uid.Comp.Container.ContainedEntities.Count < uid.Comp.MaxSlots || HasSpaceInStacks(uid);
|
||||
}
|
||||
|
||||
return GetCumulativeItemSizes(uid, uid.Comp) < uid.Comp.MaxTotalWeight || HasSpaceInStacks(uid);
|
||||
return GetCumulativeItemAreas(uid) < uid.Comp.Grid.GetArea() || HasSpaceInStacks(uid);
|
||||
}
|
||||
|
||||
private bool HasSpaceInStacks(Entity<StorageComponent?> uid, string? stackType = null)
|
||||
@@ -695,17 +934,17 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
/// <summary>
|
||||
/// Returns the sum of all the ItemSizes of the items inside of a storage.
|
||||
/// </summary>
|
||||
public int GetCumulativeItemSizes(EntityUid uid, StorageComponent? component = null)
|
||||
public int GetCumulativeItemAreas(Entity<StorageComponent?> entity)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
if (!Resolve(entity, ref entity.Comp))
|
||||
return 0;
|
||||
|
||||
var sum = 0;
|
||||
foreach (var item in component.Container.ContainedEntities)
|
||||
foreach (var item in entity.Comp.Container.ContainedEntities)
|
||||
{
|
||||
if (!_itemQuery.TryGetComponent(item, out var itemComp))
|
||||
continue;
|
||||
sum += _item.GetItemSizeWeight(itemComp.Size);
|
||||
sum += ItemSystem.GetItemShape((item, itemComp)).GetArea();
|
||||
}
|
||||
|
||||
return sum;
|
||||
@@ -722,7 +961,7 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
|
||||
if (!_itemQuery.TryGetComponent(uid, out var item))
|
||||
return DefaultStorageMaxItemSize;
|
||||
var size = _item.GetSizePrototype(item.Size);
|
||||
var size = ItemSystem.GetSizePrototype(item.Size);
|
||||
|
||||
// if there is no max item size specified, the value used
|
||||
// is one below the item size of the storage entity, clamped at ItemSize.Tiny
|
||||
@@ -742,17 +981,6 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
public FixedPoint2 GetStorageFillPercentage(Entity<StorageComponent?> uid)
|
||||
{
|
||||
if (!Resolve(uid, ref uid.Comp))
|
||||
return 0;
|
||||
|
||||
var slotPercent = FixedPoint2.New(uid.Comp.Container.ContainedEntities.Count) / uid.Comp.MaxSlots ?? FixedPoint2.Zero;
|
||||
var weightPercent = FixedPoint2.New(GetCumulativeItemSizes(uid)) / uid.Comp.MaxTotalWeight;
|
||||
|
||||
return FixedPoint2.Max(slotPercent, weightPercent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plays a clientside pickup animation for the specified uid.
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user