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 :)
@@ -30,6 +30,8 @@ namespace Content.Client.Input
|
|||||||
common.AddFunction(ContentKeyFunctions.TakeScreenshot);
|
common.AddFunction(ContentKeyFunctions.TakeScreenshot);
|
||||||
common.AddFunction(ContentKeyFunctions.TakeScreenshotNoUI);
|
common.AddFunction(ContentKeyFunctions.TakeScreenshotNoUI);
|
||||||
common.AddFunction(ContentKeyFunctions.ToggleFullscreen);
|
common.AddFunction(ContentKeyFunctions.ToggleFullscreen);
|
||||||
|
common.AddFunction(ContentKeyFunctions.MoveStoredItem);
|
||||||
|
common.AddFunction(ContentKeyFunctions.RotateStoredItem);
|
||||||
common.AddFunction(ContentKeyFunctions.Point);
|
common.AddFunction(ContentKeyFunctions.Point);
|
||||||
common.AddFunction(ContentKeyFunctions.ZoomOut);
|
common.AddFunction(ContentKeyFunctions.ZoomOut);
|
||||||
common.AddFunction(ContentKeyFunctions.ZoomIn);
|
common.AddFunction(ContentKeyFunctions.ZoomIn);
|
||||||
|
|||||||
@@ -97,6 +97,12 @@ namespace Content.Client.Options.UI.Tabs
|
|||||||
_deferCommands.Add(_inputManager.SaveToUserData);
|
_deferCommands.Add(_inputManager.SaveToUserData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void HandleStaticStorageUI(BaseButton.ButtonToggledEventArgs args)
|
||||||
|
{
|
||||||
|
_cfg.SetCVar(CCVars.StaticStorageUI, args.Pressed);
|
||||||
|
_cfg.SaveToFile();
|
||||||
|
}
|
||||||
|
|
||||||
public KeyRebindTab()
|
public KeyRebindTab()
|
||||||
{
|
{
|
||||||
IoCManager.InjectDependencies(this);
|
IoCManager.InjectDependencies(this);
|
||||||
@@ -175,6 +181,9 @@ namespace Content.Client.Options.UI.Tabs
|
|||||||
AddButton(ContentKeyFunctions.Drop);
|
AddButton(ContentKeyFunctions.Drop);
|
||||||
AddButton(ContentKeyFunctions.ExamineEntity);
|
AddButton(ContentKeyFunctions.ExamineEntity);
|
||||||
AddButton(ContentKeyFunctions.SwapHands);
|
AddButton(ContentKeyFunctions.SwapHands);
|
||||||
|
AddButton(ContentKeyFunctions.MoveStoredItem);
|
||||||
|
AddButton(ContentKeyFunctions.RotateStoredItem);
|
||||||
|
AddCheckBox("ui-options-static-storage-ui", _cfg.GetCVar(CCVars.StaticStorageUI), HandleStaticStorageUI);
|
||||||
|
|
||||||
AddHeader("ui-options-header-interaction-adv");
|
AddHeader("ui-options-header-interaction-adv");
|
||||||
AddButton(ContentKeyFunctions.SmartEquipBackpack);
|
AddButton(ContentKeyFunctions.SmartEquipBackpack);
|
||||||
|
|||||||
@@ -1,129 +1,37 @@
|
|||||||
using Content.Client.Examine;
|
using Content.Client.Storage.Systems;
|
||||||
using Content.Client.Storage.UI;
|
|
||||||
using Content.Client.UserInterface.Controls;
|
|
||||||
using Content.Client.Verbs.UI;
|
|
||||||
using Content.Shared.Input;
|
|
||||||
using Content.Shared.Interaction;
|
|
||||||
using Content.Shared.Storage;
|
using Content.Shared.Storage;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Client.GameObjects;
|
|
||||||
using Robust.Client.UserInterface;
|
|
||||||
using Robust.Client.UserInterface.Controls;
|
|
||||||
using Robust.Shared.Input;
|
|
||||||
using static Content.Shared.Storage.StorageComponent;
|
|
||||||
|
|
||||||
namespace Content.Client.Storage
|
namespace Content.Client.Storage;
|
||||||
|
|
||||||
|
[UsedImplicitly]
|
||||||
|
public sealed class StorageBoundUserInterface : BoundUserInterface
|
||||||
{
|
{
|
||||||
[UsedImplicitly]
|
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||||
public sealed class StorageBoundUserInterface : BoundUserInterface
|
|
||||||
|
private readonly StorageSystem _storage;
|
||||||
|
|
||||||
|
public StorageBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||||
{
|
{
|
||||||
[ViewVariables]
|
IoCManager.InjectDependencies(this);
|
||||||
private StorageWindow? _window;
|
_storage = _entManager.System<StorageSystem>();
|
||||||
|
}
|
||||||
|
|
||||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
protected override void Open()
|
||||||
|
{
|
||||||
|
base.Open();
|
||||||
|
|
||||||
public StorageBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
if (_entManager.TryGetComponent<StorageComponent>(Owner, out var comp))
|
||||||
{
|
_storage.OpenStorageUI(Owner, comp);
|
||||||
IoCManager.InjectDependencies(this);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Open()
|
protected override void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
base.Open();
|
base.Dispose(disposing);
|
||||||
|
if (!disposing)
|
||||||
|
return;
|
||||||
|
|
||||||
if (_window == null)
|
_storage.CloseStorageUI(Owner);
|
||||||
{
|
|
||||||
// TODO: This is a bit of a mess but storagecomponent got moved to shared and cleaned up a bit.
|
|
||||||
var controller = IoCManager.Resolve<IUserInterfaceManager>().GetUIController<StorageUIController>();
|
|
||||||
_window = controller.EnsureStorageWindow(Owner);
|
|
||||||
_window.Title = EntMan.GetComponent<MetaDataComponent>(Owner).EntityName;
|
|
||||||
|
|
||||||
_window.EntityList.GenerateItem += _window.GenerateButton;
|
|
||||||
_window.EntityList.ItemPressed += InteractWithItem;
|
|
||||||
_window.StorageContainerButton.OnPressed += TouchedContainerButton;
|
|
||||||
|
|
||||||
_window.OnClose += Close;
|
|
||||||
|
|
||||||
if (EntMan.TryGetComponent<StorageComponent>(Owner, out var storageComp))
|
|
||||||
{
|
|
||||||
BuildEntityList(Owner, storageComp);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_window.Open();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void BuildEntityList(EntityUid uid, StorageComponent component)
|
|
||||||
{
|
|
||||||
_window?.BuildEntityList(uid, component);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void InteractWithItem(BaseButton.ButtonEventArgs? args, ListData? cData)
|
|
||||||
{
|
|
||||||
if (args == null || cData is not EntityListData { Uid: var entity })
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (args.Event.Function == EngineKeyFunctions.UIClick)
|
|
||||||
{
|
|
||||||
SendPredictedMessage(new StorageInteractWithItemEvent(_entManager.GetNetEntity(entity)));
|
|
||||||
}
|
|
||||||
else if (EntMan.EntityExists(entity))
|
|
||||||
{
|
|
||||||
OnButtonPressed(args.Event, entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnButtonPressed(GUIBoundKeyEventArgs args, EntityUid entity)
|
|
||||||
{
|
|
||||||
if (args.Function == ContentKeyFunctions.ExamineEntity)
|
|
||||||
{
|
|
||||||
EntMan.System<ExamineSystem>()
|
|
||||||
.DoExamine(entity);
|
|
||||||
}
|
|
||||||
else if (args.Function == EngineKeyFunctions.UseSecondary)
|
|
||||||
{
|
|
||||||
IoCManager.Resolve<IUserInterfaceManager>().GetUIController<VerbMenuUIController>().OpenVerbMenu(entity);
|
|
||||||
}
|
|
||||||
else if (args.Function == ContentKeyFunctions.ActivateItemInWorld)
|
|
||||||
{
|
|
||||||
EntMan.EntityNetManager?.SendSystemNetworkMessage(
|
|
||||||
new InteractInventorySlotEvent(EntMan.GetNetEntity(entity), altInteract: false));
|
|
||||||
}
|
|
||||||
else if (args.Function == ContentKeyFunctions.AltActivateItemInWorld)
|
|
||||||
{
|
|
||||||
EntMan.RaisePredictiveEvent(new InteractInventorySlotEvent(EntMan.GetNetEntity(entity), altInteract: true));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
args.Handle();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void TouchedContainerButton(BaseButton.ButtonEventArgs args)
|
|
||||||
{
|
|
||||||
SendPredictedMessage(new StorageInsertItemMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
base.Dispose(disposing);
|
|
||||||
if (!disposing)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (_window != null)
|
|
||||||
{
|
|
||||||
_window.Orphan();
|
|
||||||
_window.EntityList.GenerateItem -= _window.GenerateButton;
|
|
||||||
_window.EntityList.ItemPressed -= InteractWithItem;
|
|
||||||
_window.StorageContainerButton.OnPressed -= TouchedContainerButton;
|
|
||||||
_window.OnClose -= Close;
|
|
||||||
_window = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,30 @@
|
|||||||
using Content.Client.Animations;
|
using System.Linq;
|
||||||
|
using Content.Client.Animations;
|
||||||
using Content.Shared.Hands;
|
using Content.Shared.Hands;
|
||||||
using Content.Shared.Storage;
|
using Content.Shared.Storage;
|
||||||
using Content.Shared.Storage.EntitySystems;
|
using Content.Shared.Storage.EntitySystems;
|
||||||
|
using Robust.Shared.Collections;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
namespace Content.Client.Storage.Systems;
|
namespace Content.Client.Storage.Systems;
|
||||||
|
|
||||||
// TODO kill this is all horrid.
|
|
||||||
public sealed class StorageSystem : SharedStorageSystem
|
public sealed class StorageSystem : SharedStorageSystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IGameTiming _timing = default!;
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
[Dependency] private readonly EntityPickupAnimationSystem _entityPickupAnimation = default!;
|
[Dependency] private readonly EntityPickupAnimationSystem _entityPickupAnimation = default!;
|
||||||
|
|
||||||
public event Action<EntityUid, StorageComponent>? StorageUpdated;
|
private readonly List<Entity<StorageComponent>> _openStorages = new();
|
||||||
|
public int OpenStorageAmount => _openStorages.Count;
|
||||||
|
|
||||||
|
public event Action<Entity<StorageComponent>>? StorageUpdated;
|
||||||
|
public event Action<Entity<StorageComponent>?>? StorageOrderChanged;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<StorageComponent, ComponentShutdown>(OnShutdown);
|
||||||
SubscribeNetworkEvent<PickupAnimationEvent>(HandlePickupAnimation);
|
SubscribeNetworkEvent<PickupAnimationEvent>(HandlePickupAnimation);
|
||||||
SubscribeNetworkEvent<AnimateInsertingEntitiesEvent>(HandleAnimatingInsertingEntities);
|
SubscribeNetworkEvent<AnimateInsertingEntitiesEvent>(HandleAnimatingInsertingEntities);
|
||||||
}
|
}
|
||||||
@@ -26,7 +32,74 @@ public sealed class StorageSystem : SharedStorageSystem
|
|||||||
public override void UpdateUI(Entity<StorageComponent?> entity)
|
public override void UpdateUI(Entity<StorageComponent?> entity)
|
||||||
{
|
{
|
||||||
if (Resolve(entity.Owner, ref entity.Comp))
|
if (Resolve(entity.Owner, ref entity.Comp))
|
||||||
StorageUpdated?.Invoke(entity.Owner, entity.Comp);
|
StorageUpdated?.Invoke((entity, entity.Comp));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OpenStorageUI(EntityUid uid, StorageComponent component)
|
||||||
|
{
|
||||||
|
if (_openStorages.Contains((uid, component)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ClearNonParentStorages(uid);
|
||||||
|
_openStorages.Add((uid, component));
|
||||||
|
Entity<StorageComponent>? last = _openStorages.LastOrDefault();
|
||||||
|
StorageOrderChanged?.Invoke(last);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CloseStorageUI(Entity<StorageComponent?> entity)
|
||||||
|
{
|
||||||
|
if (!Resolve(entity, ref entity.Comp))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!_openStorages.Contains((entity, entity.Comp)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var storages = new ValueList<Entity<StorageComponent>>(_openStorages);
|
||||||
|
var reverseStorages = storages.Reverse();
|
||||||
|
|
||||||
|
foreach (var storage in reverseStorages)
|
||||||
|
{
|
||||||
|
CloseStorageBoundUserInterface(storage.Owner);
|
||||||
|
_openStorages.Remove(storage);
|
||||||
|
if (storage.Owner == entity.Owner)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity<StorageComponent>? last = null;
|
||||||
|
if (_openStorages.Any())
|
||||||
|
last = _openStorages.LastOrDefault();
|
||||||
|
StorageOrderChanged?.Invoke(last);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClearNonParentStorages(EntityUid uid)
|
||||||
|
{
|
||||||
|
var storages = new ValueList<Entity<StorageComponent>>(_openStorages);
|
||||||
|
var reverseStorages = storages.Reverse();
|
||||||
|
|
||||||
|
foreach (var storage in reverseStorages)
|
||||||
|
{
|
||||||
|
if (storage.Comp.Container.Contains(uid))
|
||||||
|
break;
|
||||||
|
|
||||||
|
CloseStorageBoundUserInterface(storage.Owner);
|
||||||
|
_openStorages.Remove(storage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CloseStorageBoundUserInterface(Entity<UserInterfaceComponent?> entity)
|
||||||
|
{
|
||||||
|
if (!Resolve(entity, ref entity.Comp, false))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (entity.Comp.OpenInterfaces.GetValueOrDefault(StorageComponent.StorageUiKey.Key) is not { } bui)
|
||||||
|
return;
|
||||||
|
|
||||||
|
bui.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnShutdown(Entity<StorageComponent> ent, ref ComponentShutdown args)
|
||||||
|
{
|
||||||
|
CloseStorageUI((ent, ent.Comp));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -49,14 +122,14 @@ public sealed class StorageSystem : SharedStorageSystem
|
|||||||
if (!_timing.IsFirstTimePredicted)
|
if (!_timing.IsFirstTimePredicted)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (finalCoords.InRange(EntityManager, _transform, initialCoords, 0.1f) ||
|
if (finalCoords.InRange(EntityManager, TransformSystem, initialCoords, 0.1f) ||
|
||||||
!Exists(initialCoords.EntityId) || !Exists(finalCoords.EntityId))
|
!Exists(initialCoords.EntityId) || !Exists(finalCoords.EntityId))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var finalMapPos = finalCoords.ToMapPos(EntityManager, _transform);
|
var finalMapPos = finalCoords.ToMapPos(EntityManager, TransformSystem);
|
||||||
var finalPos = _transform.GetInvWorldMatrix(initialCoords.EntityId).Transform(finalMapPos);
|
var finalPos = TransformSystem.GetInvWorldMatrix(initialCoords.EntityId).Transform(finalMapPos);
|
||||||
|
|
||||||
_entityPickupAnimation.AnimateEntityPickup(item, initialCoords, finalPos, initialAngle);
|
_entityPickupAnimation.AnimateEntityPickup(item, initialCoords, finalPos, initialAngle);
|
||||||
}
|
}
|
||||||
@@ -74,7 +147,7 @@ public sealed class StorageSystem : SharedStorageSystem
|
|||||||
var entity = GetEntity(msg.StoredEntities[i]);
|
var entity = GetEntity(msg.StoredEntities[i]);
|
||||||
|
|
||||||
var initialPosition = msg.EntityPositions[i];
|
var initialPosition = msg.EntityPositions[i];
|
||||||
if (EntityManager.EntityExists(entity) && transformComp != null)
|
if (Exists(entity) && transformComp != null)
|
||||||
{
|
{
|
||||||
_entityPickupAnimation.AnimateEntityPickup(entity, GetCoordinates(initialPosition), transformComp.LocalPosition, msg.EntityAngles[i]);
|
_entityPickupAnimation.AnimateEntityPickup(entity, GetCoordinates(initialPosition), transformComp.LocalPosition, msg.EntityAngles[i]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,71 +0,0 @@
|
|||||||
using Content.Client.Gameplay;
|
|
||||||
using Content.Client.Storage.Systems;
|
|
||||||
using Content.Shared.Storage;
|
|
||||||
using Robust.Client.UserInterface.Controllers;
|
|
||||||
|
|
||||||
namespace Content.Client.Storage.UI;
|
|
||||||
|
|
||||||
public sealed class StorageUIController : UIController, IOnSystemChanged<StorageSystem>, IOnStateExited<GameplayState>
|
|
||||||
{
|
|
||||||
// This is mainly to keep legacy functionality for now.
|
|
||||||
private readonly Dictionary<EntityUid, StorageWindow> _storageWindows = new();
|
|
||||||
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
EntityManager.EventBus.SubscribeLocalEvent<StorageComponent, ComponentShutdown>(OnStorageShutdown);
|
|
||||||
}
|
|
||||||
public StorageWindow EnsureStorageWindow(EntityUid uid)
|
|
||||||
{
|
|
||||||
if (_storageWindows.TryGetValue(uid, out var window))
|
|
||||||
{
|
|
||||||
UIManager.WindowRoot.AddChild(window);
|
|
||||||
return window;
|
|
||||||
}
|
|
||||||
|
|
||||||
window = new StorageWindow(EntityManager);
|
|
||||||
_storageWindows[uid] = window;
|
|
||||||
window.OpenCenteredLeft();
|
|
||||||
return window;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnStorageShutdown(EntityUid uid, StorageComponent component, ComponentShutdown args)
|
|
||||||
{
|
|
||||||
if (!_storageWindows.TryGetValue(uid, out var window))
|
|
||||||
return;
|
|
||||||
|
|
||||||
_storageWindows.Remove(uid);
|
|
||||||
window.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnStorageUpdate(EntityUid uid, StorageComponent component)
|
|
||||||
{
|
|
||||||
if (EntityManager.TryGetComponent<UserInterfaceComponent>(uid, out var uiComp) &&
|
|
||||||
uiComp.OpenInterfaces.TryGetValue(StorageComponent.StorageUiKey.Key, out var bui))
|
|
||||||
{
|
|
||||||
var storageBui = (StorageBoundUserInterface) bui;
|
|
||||||
|
|
||||||
storageBui.BuildEntityList(uid, component);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnSystemLoaded(StorageSystem system)
|
|
||||||
{
|
|
||||||
system.StorageUpdated += OnStorageUpdate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnSystemUnloaded(StorageSystem system)
|
|
||||||
{
|
|
||||||
system.StorageUpdated -= OnStorageUpdate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnStateExited(GameplayState state)
|
|
||||||
{
|
|
||||||
foreach (var window in _storageWindows.Values)
|
|
||||||
{
|
|
||||||
window.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
_storageWindows.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,182 +0,0 @@
|
|||||||
using System.Numerics;
|
|
||||||
using Content.Client.Items.Systems;
|
|
||||||
using Content.Client.Message;
|
|
||||||
using Robust.Client.Graphics;
|
|
||||||
using Robust.Client.UserInterface.Controls;
|
|
||||||
using Content.Client.Stylesheets;
|
|
||||||
using Content.Client.UserInterface.Controls;
|
|
||||||
using Content.Shared.IdentityManagement;
|
|
||||||
using Content.Shared.Item;
|
|
||||||
using Content.Shared.Stacks;
|
|
||||||
using Content.Shared.Storage;
|
|
||||||
using Content.Shared.Storage.EntitySystems;
|
|
||||||
using Robust.Client.UserInterface;
|
|
||||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
|
||||||
using Direction = Robust.Shared.Maths.Direction;
|
|
||||||
|
|
||||||
namespace Content.Client.Storage.UI
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// GUI class for client storage component
|
|
||||||
/// </summary>
|
|
||||||
public sealed class StorageWindow : FancyWindow
|
|
||||||
{
|
|
||||||
private readonly IEntityManager _entityManager;
|
|
||||||
|
|
||||||
private readonly SharedStorageSystem _storage;
|
|
||||||
private readonly ItemSystem _item;
|
|
||||||
|
|
||||||
private readonly RichTextLabel _information;
|
|
||||||
public readonly ContainerButton StorageContainerButton;
|
|
||||||
public readonly ListContainer EntityList;
|
|
||||||
private readonly StyleBoxFlat _hoveredBox = new() { BackgroundColor = Color.Black.WithAlpha(0.35f) };
|
|
||||||
private readonly StyleBoxFlat _unHoveredBox = new() { BackgroundColor = Color.Black.WithAlpha(0.0f) };
|
|
||||||
|
|
||||||
public StorageWindow(IEntityManager entityManager)
|
|
||||||
{
|
|
||||||
_entityManager = entityManager;
|
|
||||||
_storage = _entityManager.System<SharedStorageSystem>();
|
|
||||||
_item = _entityManager.System<ItemSystem>();
|
|
||||||
SetSize = new Vector2(240, 320);
|
|
||||||
Title = Loc.GetString("comp-storage-window-title");
|
|
||||||
RectClipContent = true;
|
|
||||||
|
|
||||||
StorageContainerButton = new ContainerButton
|
|
||||||
{
|
|
||||||
Name = "StorageContainerButton",
|
|
||||||
MouseFilter = MouseFilterMode.Pass,
|
|
||||||
};
|
|
||||||
|
|
||||||
ContentsContainer.AddChild(StorageContainerButton);
|
|
||||||
|
|
||||||
var innerContainerButton = new PanelContainer
|
|
||||||
{
|
|
||||||
PanelOverride = _unHoveredBox,
|
|
||||||
};
|
|
||||||
|
|
||||||
StorageContainerButton.AddChild(innerContainerButton);
|
|
||||||
|
|
||||||
Control vBox = new BoxContainer()
|
|
||||||
{
|
|
||||||
Orientation = LayoutOrientation.Vertical,
|
|
||||||
MouseFilter = MouseFilterMode.Ignore,
|
|
||||||
Margin = new Thickness(5),
|
|
||||||
};
|
|
||||||
|
|
||||||
StorageContainerButton.AddChild(vBox);
|
|
||||||
|
|
||||||
_information = new RichTextLabel
|
|
||||||
{
|
|
||||||
VerticalAlignment = VAlignment.Center
|
|
||||||
};
|
|
||||||
_information.SetMessage(Loc.GetString("comp-storage-window-weight",
|
|
||||||
("weight", 0),
|
|
||||||
("maxWeight", 0),
|
|
||||||
("size", _item.GetItemSizeLocale(SharedStorageSystem.DefaultStorageMaxItemSize))));
|
|
||||||
|
|
||||||
vBox.AddChild(_information);
|
|
||||||
|
|
||||||
EntityList = new ListContainer
|
|
||||||
{
|
|
||||||
Name = "EntityListContainer",
|
|
||||||
};
|
|
||||||
|
|
||||||
vBox.AddChild(EntityList);
|
|
||||||
|
|
||||||
EntityList.OnMouseEntered += _ =>
|
|
||||||
{
|
|
||||||
innerContainerButton.PanelOverride = _hoveredBox;
|
|
||||||
};
|
|
||||||
|
|
||||||
EntityList.OnMouseExited += _ =>
|
|
||||||
{
|
|
||||||
innerContainerButton.PanelOverride = _unHoveredBox;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Loops through stored entities creating buttons for each, updates information labels
|
|
||||||
/// </summary>
|
|
||||||
public void BuildEntityList(EntityUid entity, StorageComponent component)
|
|
||||||
{
|
|
||||||
var storedCount = component.Container.ContainedEntities.Count;
|
|
||||||
var list = new List<EntityListData>(storedCount);
|
|
||||||
|
|
||||||
foreach (var uid in component.Container.ContainedEntities)
|
|
||||||
{
|
|
||||||
list.Add(new EntityListData(uid));
|
|
||||||
}
|
|
||||||
|
|
||||||
EntityList.PopulateList(list);
|
|
||||||
|
|
||||||
SetStorageInformation((entity, component));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetStorageInformation(Entity<StorageComponent> uid)
|
|
||||||
{
|
|
||||||
//todo: text is the straight agenda. What about anything else?
|
|
||||||
if (uid.Comp.MaxSlots == null)
|
|
||||||
{
|
|
||||||
_information.SetMarkup(Loc.GetString("comp-storage-window-weight",
|
|
||||||
("weight", _storage.GetCumulativeItemSizes(uid, uid.Comp)),
|
|
||||||
("maxWeight", uid.Comp.MaxTotalWeight),
|
|
||||||
("size", _item.GetItemSizeLocale(_storage.GetMaxItemSize((uid, uid.Comp))))));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_information.SetMarkup(Loc.GetString("comp-storage-window-slots",
|
|
||||||
("itemCount", uid.Comp.Container.ContainedEntities.Count),
|
|
||||||
("maxCount", uid.Comp.MaxSlots),
|
|
||||||
("size", _item.GetItemSizeLocale(_storage.GetMaxItemSize((uid, uid.Comp))))));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Button created for each entity that represents that item in the storage UI, with a texture, and name and size label
|
|
||||||
/// </summary>
|
|
||||||
public void GenerateButton(ListData data, ListContainerButton button)
|
|
||||||
{
|
|
||||||
if (data is not EntityListData {Uid: var entity}
|
|
||||||
|| !_entityManager.EntityExists(entity))
|
|
||||||
return;
|
|
||||||
|
|
||||||
_entityManager.TryGetComponent(entity, out StackComponent? stack);
|
|
||||||
_entityManager.TryGetComponent(entity, out ItemComponent? item);
|
|
||||||
var count = stack?.Count ?? 1;
|
|
||||||
|
|
||||||
var spriteView = new SpriteView
|
|
||||||
{
|
|
||||||
HorizontalAlignment = HAlignment.Left,
|
|
||||||
VerticalAlignment = VAlignment.Center,
|
|
||||||
SetSize = new Vector2(32.0f, 32.0f),
|
|
||||||
OverrideDirection = Direction.South,
|
|
||||||
};
|
|
||||||
spriteView.SetEntity(entity);
|
|
||||||
button.AddChild(new BoxContainer
|
|
||||||
{
|
|
||||||
Orientation = LayoutOrientation.Horizontal,
|
|
||||||
SeparationOverride = 2,
|
|
||||||
Children =
|
|
||||||
{
|
|
||||||
spriteView,
|
|
||||||
new Label
|
|
||||||
{
|
|
||||||
HorizontalExpand = true,
|
|
||||||
ClipText = true,
|
|
||||||
Text = _entityManager.GetComponent<MetaDataComponent>(Identity.Entity(entity, _entityManager)).EntityName +
|
|
||||||
(count > 1 ? $" x {count}" : string.Empty)
|
|
||||||
},
|
|
||||||
new Label
|
|
||||||
{
|
|
||||||
Align = Label.AlignMode.Right,
|
|
||||||
Text = item?.Size != null
|
|
||||||
? $"{_item.GetItemSizeWeight(item.Size)}"
|
|
||||||
: Loc.GetString("comp-storage-no-item-size")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
button.StyleClasses.Add(StyleNano.StyleClassStorageButton);
|
|
||||||
button.EnableAllKeybinds = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,6 +4,8 @@ using Content.Client.UserInterface.Systems.Hands.Controls;
|
|||||||
using Content.Client.UserInterface.Systems.Hotbar.Widgets;
|
using Content.Client.UserInterface.Systems.Hotbar.Widgets;
|
||||||
using Content.Client.UserInterface.Systems.Inventory;
|
using Content.Client.UserInterface.Systems.Inventory;
|
||||||
using Content.Client.UserInterface.Systems.Inventory.Controls;
|
using Content.Client.UserInterface.Systems.Inventory.Controls;
|
||||||
|
using Content.Client.UserInterface.Systems.Storage;
|
||||||
|
using Content.Client.UserInterface.Systems.Storage.Controls;
|
||||||
using Robust.Client.UserInterface;
|
using Robust.Client.UserInterface;
|
||||||
using Robust.Client.UserInterface.Controllers;
|
using Robust.Client.UserInterface.Controllers;
|
||||||
|
|
||||||
@@ -13,6 +15,7 @@ public sealed class HotbarUIController : UIController
|
|||||||
{
|
{
|
||||||
private InventoryUIController? _inventory;
|
private InventoryUIController? _inventory;
|
||||||
private HandsUIController? _hands;
|
private HandsUIController? _hands;
|
||||||
|
private StorageUIController? _storage;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
@@ -27,12 +30,14 @@ public sealed class HotbarUIController : UIController
|
|||||||
ReloadHotbar();
|
ReloadHotbar();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Setup(HandsContainer handsContainer, ItemSlotButtonContainer inventoryBar, ItemStatusPanel handStatus)
|
public void Setup(HandsContainer handsContainer, ItemSlotButtonContainer inventoryBar, ItemStatusPanel handStatus, StorageContainer storageContainer)
|
||||||
{
|
{
|
||||||
_inventory = UIManager.GetUIController<InventoryUIController>();
|
_inventory = UIManager.GetUIController<InventoryUIController>();
|
||||||
_hands = UIManager.GetUIController<HandsUIController>();
|
_hands = UIManager.GetUIController<HandsUIController>();
|
||||||
|
_storage = UIManager.GetUIController<StorageUIController>();
|
||||||
_hands.RegisterHandContainer(handsContainer);
|
_hands.RegisterHandContainer(handsContainer);
|
||||||
_inventory.RegisterInventoryBarContainer(inventoryBar);
|
_inventory.RegisterInventoryBarContainer(inventoryBar);
|
||||||
|
_storage.RegisterStorageContainer(storageContainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ReloadHotbar()
|
public void ReloadHotbar()
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<widgets:HotbarGui
|
<widgets:HotbarGui
|
||||||
xmlns="https://spacestation14.io"
|
xmlns="https://spacestation14.io"
|
||||||
xmlns:inventory="clr-namespace:Content.Client.UserInterface.Systems.Inventory.Controls"
|
xmlns:inventory="clr-namespace:Content.Client.UserInterface.Systems.Inventory.Controls"
|
||||||
|
xmlns:storage="clr-namespace:Content.Client.UserInterface.Systems.Storage.Controls"
|
||||||
xmlns:hands="clr-namespace:Content.Client.UserInterface.Systems.Hands.Controls"
|
xmlns:hands="clr-namespace:Content.Client.UserInterface.Systems.Hands.Controls"
|
||||||
xmlns:widgets="clr-namespace:Content.Client.UserInterface.Systems.Hotbar.Widgets"
|
xmlns:widgets="clr-namespace:Content.Client.UserInterface.Systems.Hotbar.Widgets"
|
||||||
Name="HotbarInterface"
|
Name="HotbarInterface"
|
||||||
@@ -8,11 +9,21 @@
|
|||||||
VerticalAlignment="Bottom"
|
VerticalAlignment="Bottom"
|
||||||
Orientation="Vertical"
|
Orientation="Vertical"
|
||||||
HorizontalAlignment="Center">
|
HorizontalAlignment="Center">
|
||||||
<inventory:ItemStatusPanel
|
<Control HorizontalAlignment="Center">
|
||||||
Name="StatusPanel"
|
<inventory:ItemStatusPanel
|
||||||
Visible="False"
|
Name="StatusPanel"
|
||||||
HorizontalAlignment="Center"
|
Visible="False"
|
||||||
/>
|
HorizontalAlignment="Center"
|
||||||
|
/>
|
||||||
|
<BoxContainer Name="StorageContainer"
|
||||||
|
Access="Public"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Margin="10">
|
||||||
|
<storage:StorageContainer
|
||||||
|
Name="StoragePanel"
|
||||||
|
Visible="False"/>
|
||||||
|
</BoxContainer>
|
||||||
|
</Control>
|
||||||
<inventory:ItemSlotButtonContainer
|
<inventory:ItemSlotButtonContainer
|
||||||
Name="InventoryHotbar"
|
Name="InventoryHotbar"
|
||||||
Access="Public"
|
Access="Public"
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ public sealed partial class HotbarGui : UIWidget
|
|||||||
StatusPanel.Update(null);
|
StatusPanel.Update(null);
|
||||||
var hotbarController = UserInterfaceManager.GetUIController<HotbarUIController>();
|
var hotbarController = UserInterfaceManager.GetUIController<HotbarUIController>();
|
||||||
|
|
||||||
hotbarController.Setup(HandContainer, InventoryHotbar, StatusPanel);
|
hotbarController.Setup(HandContainer, InventoryHotbar, StatusPanel, StoragePanel);
|
||||||
LayoutContainer.SetGrowVertical(this, LayoutContainer.GrowDirection.Begin);
|
LayoutContainer.SetGrowVertical(this, LayoutContainer.GrowDirection.Begin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,220 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
|
using Content.Client.Items.Systems;
|
||||||
|
using Content.Shared.Item;
|
||||||
|
using Content.Shared.Storage;
|
||||||
|
using Content.Shared.Storage.EntitySystems;
|
||||||
|
using Robust.Client.GameObjects;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Client.UserInterface;
|
||||||
|
|
||||||
|
namespace Content.Client.UserInterface.Systems.Storage.Controls;
|
||||||
|
|
||||||
|
public sealed class ItemGridPiece : Control
|
||||||
|
{
|
||||||
|
private readonly ItemSystem _itemSystem;
|
||||||
|
private readonly SpriteSystem _spriteSystem;
|
||||||
|
private readonly StorageUIController _storageController;
|
||||||
|
|
||||||
|
private readonly List<(Texture, Vector2)> _texturesPositions = new();
|
||||||
|
|
||||||
|
public readonly EntityUid Entity;
|
||||||
|
public ItemStorageLocation Location;
|
||||||
|
|
||||||
|
public event Action<GUIBoundKeyEventArgs, ItemGridPiece>? OnPiecePressed;
|
||||||
|
public event Action<GUIBoundKeyEventArgs, ItemGridPiece>? OnPieceUnpressed;
|
||||||
|
|
||||||
|
#region Textures
|
||||||
|
private readonly string _centerTexturePath = "Storage/piece_center";
|
||||||
|
private Texture? _centerTexture;
|
||||||
|
private readonly string _topTexturePath = "Storage/piece_top";
|
||||||
|
private Texture? _topTexture;
|
||||||
|
private readonly string _bottomTexturePath = "Storage/piece_bottom";
|
||||||
|
private Texture? _bottomTexture;
|
||||||
|
private readonly string _leftTexturePath = "Storage/piece_left";
|
||||||
|
private Texture? _leftTexture;
|
||||||
|
private readonly string _rightTexturePath = "Storage/piece_right";
|
||||||
|
private Texture? _rightTexture;
|
||||||
|
private readonly string _topLeftTexturePath = "Storage/piece_topLeft";
|
||||||
|
private Texture? _topLeftTexture;
|
||||||
|
private readonly string _topRightTexturePath = "Storage/piece_topRight";
|
||||||
|
private Texture? _topRightTexture;
|
||||||
|
private readonly string _bottomLeftTexturePath = "Storage/piece_bottomLeft";
|
||||||
|
private Texture? _bottomLeftTexture;
|
||||||
|
private readonly string _bottomRightTexturePath = "Storage/piece_bottomRight";
|
||||||
|
private Texture? _bottomRightTexture;
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public ItemGridPiece(Entity<ItemComponent> entity, ItemStorageLocation location, IEntityManager entityManager)
|
||||||
|
{
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
|
||||||
|
_itemSystem = entityManager.System<ItemSystem>();
|
||||||
|
_spriteSystem = entityManager.System<SpriteSystem>();
|
||||||
|
_storageController = UserInterfaceManager.GetUIController<StorageUIController>();
|
||||||
|
|
||||||
|
Entity = entity.Owner;
|
||||||
|
Location = location;
|
||||||
|
|
||||||
|
Visible = true;
|
||||||
|
MouseFilter = MouseFilterMode.Pass;
|
||||||
|
|
||||||
|
OnThemeUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnThemeUpdated()
|
||||||
|
{
|
||||||
|
base.OnThemeUpdated();
|
||||||
|
|
||||||
|
_centerTexture = Theme.ResolveTextureOrNull(_centerTexturePath)?.Texture;
|
||||||
|
_topTexture = Theme.ResolveTextureOrNull(_topTexturePath)?.Texture;
|
||||||
|
_bottomTexture = Theme.ResolveTextureOrNull(_bottomTexturePath)?.Texture;
|
||||||
|
_leftTexture = Theme.ResolveTextureOrNull(_leftTexturePath)?.Texture;
|
||||||
|
_rightTexture = Theme.ResolveTextureOrNull(_rightTexturePath)?.Texture;
|
||||||
|
_topLeftTexture = Theme.ResolveTextureOrNull(_topLeftTexturePath)?.Texture;
|
||||||
|
_topRightTexture = Theme.ResolveTextureOrNull(_topRightTexturePath)?.Texture;
|
||||||
|
_bottomLeftTexture = Theme.ResolveTextureOrNull(_bottomLeftTexturePath)?.Texture;
|
||||||
|
_bottomRightTexture = Theme.ResolveTextureOrNull(_bottomRightTexturePath)?.Texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Draw(DrawingHandleScreen handle)
|
||||||
|
{
|
||||||
|
base.Draw(handle);
|
||||||
|
|
||||||
|
if (_storageController.IsDragging && _storageController.CurrentlyDragging == this)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var adjustedShape = _itemSystem.GetAdjustedItemShape((Entity, null), Location.Rotation, Vector2i.Zero);
|
||||||
|
var boundingGrid = adjustedShape.GetBoundingBox();
|
||||||
|
var size = _centerTexture!.Size * 2 * UIScale;
|
||||||
|
|
||||||
|
var hovering = !_storageController.IsDragging && UserInterfaceManager.CurrentlyHovered == this;
|
||||||
|
//yeah, this coloring is kinda hardcoded. deal with it. B)
|
||||||
|
Color? colorModulate = hovering ? null : Color.FromHex("#a8a8a8");
|
||||||
|
|
||||||
|
_texturesPositions.Clear();
|
||||||
|
for (var y = boundingGrid.Bottom; y <= boundingGrid.Top; y++)
|
||||||
|
{
|
||||||
|
for (var x = boundingGrid.Left; x <= boundingGrid.Right; x++)
|
||||||
|
{
|
||||||
|
if (!adjustedShape.Contains(x, y))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var offset = size * 2 * new Vector2(x - boundingGrid.Left, y - boundingGrid.Bottom);
|
||||||
|
var topLeft = PixelPosition + offset.Floored();
|
||||||
|
|
||||||
|
if (GetTexture(adjustedShape, new Vector2i(x, y), Direction.NorthEast) is {} neTexture)
|
||||||
|
{
|
||||||
|
var neOffset = new Vector2(size.X, 0);
|
||||||
|
handle.DrawTextureRect(neTexture, new UIBox2(topLeft + neOffset, topLeft + neOffset + size), colorModulate);
|
||||||
|
}
|
||||||
|
if (GetTexture(adjustedShape, new Vector2i(x, y), Direction.NorthWest) is {} nwTexture)
|
||||||
|
{
|
||||||
|
_texturesPositions.Add((nwTexture, Position + offset / UIScale));
|
||||||
|
handle.DrawTextureRect(nwTexture, new UIBox2(topLeft, topLeft + size), colorModulate);
|
||||||
|
}
|
||||||
|
if (GetTexture(adjustedShape, new Vector2i(x, y), Direction.SouthEast) is {} seTexture)
|
||||||
|
{
|
||||||
|
var seOffset = size;
|
||||||
|
handle.DrawTextureRect(seTexture, new UIBox2(topLeft + seOffset, topLeft + seOffset + size), colorModulate);
|
||||||
|
}
|
||||||
|
if (GetTexture(adjustedShape, new Vector2i(x, y), Direction.SouthWest) is {} swTexture)
|
||||||
|
{
|
||||||
|
var swOffset = new Vector2(0, size.Y);
|
||||||
|
handle.DrawTextureRect(swTexture, new UIBox2(topLeft + swOffset, topLeft + swOffset + size), colorModulate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// typically you'd divide by two, but since the textures are half a tile, this is done implicitly
|
||||||
|
var iconOffset = new Vector2((boundingGrid.Width + 1) * size.X ,
|
||||||
|
(boundingGrid.Height + 1) * size.Y);
|
||||||
|
|
||||||
|
_spriteSystem.ForceUpdate(Entity);
|
||||||
|
handle.DrawEntity(Entity,
|
||||||
|
PixelPosition + iconOffset,
|
||||||
|
Vector2.One * 2 * UIScale,
|
||||||
|
Angle.Zero,
|
||||||
|
overrideDirection: Direction.South);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool HasPoint(Vector2 point)
|
||||||
|
{
|
||||||
|
foreach (var (texture, position) in _texturesPositions)
|
||||||
|
{
|
||||||
|
if (!new Box2(position, position + texture.Size * 4).Contains(point))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void KeyBindDown(GUIBoundKeyEventArgs args)
|
||||||
|
{
|
||||||
|
base.KeyBindDown(args);
|
||||||
|
|
||||||
|
OnPiecePressed?.Invoke(args, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void KeyBindUp(GUIBoundKeyEventArgs args)
|
||||||
|
{
|
||||||
|
base.KeyBindUp(args);
|
||||||
|
|
||||||
|
OnPieceUnpressed?.Invoke(args, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Texture? GetTexture(IReadOnlyList<Box2i> boxes, Vector2i position, Direction corner)
|
||||||
|
{
|
||||||
|
var top = !boxes.Contains(position - Vector2i.Up);
|
||||||
|
var bottom = !boxes.Contains(position - Vector2i.Down);
|
||||||
|
var left = !boxes.Contains(position + Vector2i.Left);
|
||||||
|
var right = !boxes.Contains(position + Vector2i.Right);
|
||||||
|
|
||||||
|
switch (corner)
|
||||||
|
{
|
||||||
|
case Direction.NorthEast:
|
||||||
|
if (top && right)
|
||||||
|
return _topRightTexture;
|
||||||
|
if (top)
|
||||||
|
return _topTexture;
|
||||||
|
if (right)
|
||||||
|
return _rightTexture;
|
||||||
|
return _centerTexture;
|
||||||
|
case Direction.NorthWest:
|
||||||
|
if (top && left)
|
||||||
|
return _topLeftTexture;
|
||||||
|
if (top)
|
||||||
|
return _topTexture;
|
||||||
|
if (left)
|
||||||
|
return _leftTexture;
|
||||||
|
return _centerTexture;
|
||||||
|
case Direction.SouthEast:
|
||||||
|
if (bottom && right)
|
||||||
|
return _bottomRightTexture;
|
||||||
|
if (bottom)
|
||||||
|
return _bottomTexture;
|
||||||
|
if (right)
|
||||||
|
return _rightTexture;
|
||||||
|
return _centerTexture;
|
||||||
|
case Direction.SouthWest:
|
||||||
|
if (bottom && left)
|
||||||
|
return _bottomLeftTexture;
|
||||||
|
if (bottom)
|
||||||
|
return _bottomTexture;
|
||||||
|
if (left)
|
||||||
|
return _leftTexture;
|
||||||
|
return _centerTexture;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Vector2 GetCenterOffset(Entity<ItemComponent?> entity, ItemStorageLocation location, IEntityManager entMan)
|
||||||
|
{
|
||||||
|
var boxSize = entMan.System<ItemSystem>().GetAdjustedItemShape(entity, location).GetBoundingBox().Size;
|
||||||
|
var actualSize = new Vector2(boxSize.X + 1, boxSize.Y + 1);
|
||||||
|
return actualSize * new Vector2i(8, 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,431 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
|
using Content.Client.Hands.Systems;
|
||||||
|
using Content.Client.Items.Systems;
|
||||||
|
using Content.Client.Storage.Systems;
|
||||||
|
using Content.Shared.Input;
|
||||||
|
using Content.Shared.Item;
|
||||||
|
using Content.Shared.Storage;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Client.UserInterface;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Client.UserInterface.Systems.Storage.Controls;
|
||||||
|
|
||||||
|
public sealed class StorageContainer : BaseWindow
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IEntityManager _entity = default!;
|
||||||
|
private readonly StorageUIController _storageController;
|
||||||
|
|
||||||
|
public EntityUid? StorageEntity;
|
||||||
|
|
||||||
|
private readonly GridContainer _pieceGrid;
|
||||||
|
private readonly GridContainer _backgroundGrid;
|
||||||
|
private readonly GridContainer _sidebar;
|
||||||
|
|
||||||
|
public event Action<GUIBoundKeyEventArgs, ItemGridPiece>? OnPiecePressed;
|
||||||
|
public event Action<GUIBoundKeyEventArgs, ItemGridPiece>? OnPieceUnpressed;
|
||||||
|
|
||||||
|
private readonly string _emptyTexturePath = "Storage/tile_empty";
|
||||||
|
private Texture? _emptyTexture;
|
||||||
|
private readonly string _blockedTexturePath = "Storage/tile_blocked";
|
||||||
|
private Texture? _blockedTexture;
|
||||||
|
private readonly string _exitTexturePath = "Storage/exit";
|
||||||
|
private Texture? _exitTexture;
|
||||||
|
private readonly string _backTexturePath = "Storage/back";
|
||||||
|
private Texture? _backTexture;
|
||||||
|
private readonly string _sidebarTopTexturePath = "Storage/sidebar_top";
|
||||||
|
private Texture? _sidebarTopTexture;
|
||||||
|
private readonly string _sidebarMiddleTexturePath = "Storage/sidebar_mid";
|
||||||
|
private Texture? _sidebarMiddleTexture;
|
||||||
|
private readonly string _sidebarBottomTexturePath = "Storage/sidebar_bottom";
|
||||||
|
private Texture? _sidebarBottomTexture;
|
||||||
|
private readonly string _sidebarFatTexturePath = "Storage/sidebar_fat";
|
||||||
|
private Texture? _sidebarFatTexture;
|
||||||
|
|
||||||
|
public StorageContainer()
|
||||||
|
{
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
|
||||||
|
_storageController = UserInterfaceManager.GetUIController<StorageUIController>();
|
||||||
|
|
||||||
|
OnThemeUpdated();
|
||||||
|
|
||||||
|
MouseFilter = MouseFilterMode.Stop;
|
||||||
|
|
||||||
|
_sidebar = new GridContainer
|
||||||
|
{
|
||||||
|
HSeparationOverride = 0,
|
||||||
|
VSeparationOverride = 0,
|
||||||
|
Columns = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
_pieceGrid = new GridContainer
|
||||||
|
{
|
||||||
|
HSeparationOverride = 0,
|
||||||
|
VSeparationOverride = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
_backgroundGrid = new GridContainer
|
||||||
|
{
|
||||||
|
HSeparationOverride = 0,
|
||||||
|
VSeparationOverride = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
var container = new BoxContainer
|
||||||
|
{
|
||||||
|
Orientation = BoxContainer.LayoutOrientation.Vertical,
|
||||||
|
Children =
|
||||||
|
{
|
||||||
|
new BoxContainer
|
||||||
|
{
|
||||||
|
Orientation = BoxContainer.LayoutOrientation.Horizontal,
|
||||||
|
Children =
|
||||||
|
{
|
||||||
|
_sidebar,
|
||||||
|
new Control
|
||||||
|
{
|
||||||
|
Children =
|
||||||
|
{
|
||||||
|
_backgroundGrid,
|
||||||
|
_pieceGrid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
AddChild(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnThemeUpdated()
|
||||||
|
{
|
||||||
|
base.OnThemeUpdated();
|
||||||
|
|
||||||
|
_emptyTexture = Theme.ResolveTextureOrNull(_emptyTexturePath)?.Texture;
|
||||||
|
_blockedTexture = Theme.ResolveTextureOrNull(_blockedTexturePath)?.Texture;
|
||||||
|
_exitTexture = Theme.ResolveTextureOrNull(_exitTexturePath)?.Texture;
|
||||||
|
_backTexture = Theme.ResolveTextureOrNull(_backTexturePath)?.Texture;
|
||||||
|
_sidebarTopTexture = Theme.ResolveTextureOrNull(_sidebarTopTexturePath)?.Texture;
|
||||||
|
_sidebarMiddleTexture = Theme.ResolveTextureOrNull(_sidebarMiddleTexturePath)?.Texture;
|
||||||
|
_sidebarBottomTexture = Theme.ResolveTextureOrNull(_sidebarBottomTexturePath)?.Texture;
|
||||||
|
_sidebarFatTexture = Theme.ResolveTextureOrNull(_sidebarFatTexturePath)?.Texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateContainer(Entity<StorageComponent>? entity)
|
||||||
|
{
|
||||||
|
Visible = entity != null;
|
||||||
|
StorageEntity = entity;
|
||||||
|
if (entity == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
BuildGridRepresentation(entity.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BuildGridRepresentation(Entity<StorageComponent> entity)
|
||||||
|
{
|
||||||
|
var comp = entity.Comp;
|
||||||
|
if (!comp.Grid.Any())
|
||||||
|
return;
|
||||||
|
|
||||||
|
var boundingGrid = comp.Grid.GetBoundingBox();
|
||||||
|
|
||||||
|
_backgroundGrid.Children.Clear();
|
||||||
|
_backgroundGrid.Rows = boundingGrid.Height + 1;
|
||||||
|
_backgroundGrid.Columns = boundingGrid.Width + 1;
|
||||||
|
for (var y = boundingGrid.Bottom; y <= boundingGrid.Top; y++)
|
||||||
|
{
|
||||||
|
for (var x = boundingGrid.Left; x <= boundingGrid.Right; x++)
|
||||||
|
{
|
||||||
|
var texture = comp.Grid.Contains(x, y)
|
||||||
|
? _emptyTexture
|
||||||
|
: _blockedTexture;
|
||||||
|
|
||||||
|
_backgroundGrid.AddChild(new TextureRect
|
||||||
|
{
|
||||||
|
Texture = texture,
|
||||||
|
TextureScale = new Vector2(2, 2)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Sidebar
|
||||||
|
_sidebar.Children.Clear();
|
||||||
|
_sidebar.Rows = boundingGrid.Height + 1;
|
||||||
|
var exitButton = new TextureButton
|
||||||
|
{
|
||||||
|
TextureNormal = _entity.System<StorageSystem>().OpenStorageAmount == 1
|
||||||
|
?_exitTexture
|
||||||
|
: _backTexture,
|
||||||
|
Scale = new Vector2(2, 2),
|
||||||
|
};
|
||||||
|
exitButton.OnPressed += _ =>
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
};
|
||||||
|
var exitContainer = new BoxContainer
|
||||||
|
{
|
||||||
|
Children =
|
||||||
|
{
|
||||||
|
new TextureRect
|
||||||
|
{
|
||||||
|
Texture = boundingGrid.Height != 0
|
||||||
|
? _sidebarTopTexture
|
||||||
|
: _sidebarFatTexture,
|
||||||
|
TextureScale = new Vector2(2, 2),
|
||||||
|
Children =
|
||||||
|
{
|
||||||
|
exitButton
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
_sidebar.AddChild(exitContainer);
|
||||||
|
for (var i = 0; i < boundingGrid.Height - 1; i++)
|
||||||
|
{
|
||||||
|
_sidebar.AddChild(new TextureRect
|
||||||
|
{
|
||||||
|
Texture = _sidebarMiddleTexture,
|
||||||
|
TextureScale = new Vector2(2, 2),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (boundingGrid.Height > 0)
|
||||||
|
{
|
||||||
|
_sidebar.AddChild(new TextureRect
|
||||||
|
{
|
||||||
|
Texture = _sidebarBottomTexture,
|
||||||
|
TextureScale = new Vector2(2, 2),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
BuildItemPieces();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BuildItemPieces()
|
||||||
|
{
|
||||||
|
if (!_entity.TryGetComponent<StorageComponent>(StorageEntity, out var storageComp))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!storageComp.Grid.Any())
|
||||||
|
return;
|
||||||
|
|
||||||
|
var boundingGrid = storageComp.Grid.GetBoundingBox();
|
||||||
|
var size = _emptyTexture!.Size * 2;
|
||||||
|
|
||||||
|
//todo. at some point, we may want to only rebuild the pieces that have actually received new data.
|
||||||
|
|
||||||
|
_pieceGrid.Children.Clear();
|
||||||
|
_pieceGrid.Rows = boundingGrid.Height + 1;
|
||||||
|
_pieceGrid.Columns = boundingGrid.Width + 1;
|
||||||
|
for (var y = boundingGrid.Bottom; y <= boundingGrid.Top; y++)
|
||||||
|
{
|
||||||
|
for (var x = boundingGrid.Left; x <= boundingGrid.Right; x++)
|
||||||
|
{
|
||||||
|
var currentPosition = new Vector2i(x, y);
|
||||||
|
var item = storageComp.StoredItems
|
||||||
|
.Where(pair => pair.Value.Position == currentPosition)
|
||||||
|
.FirstOrNull();
|
||||||
|
|
||||||
|
var control = new Control
|
||||||
|
{
|
||||||
|
MinSize = size
|
||||||
|
};
|
||||||
|
|
||||||
|
if (item != null)
|
||||||
|
{
|
||||||
|
var itemEnt = _entity.GetEntity(item.Value.Key);
|
||||||
|
|
||||||
|
if (_entity.TryGetComponent<ItemComponent>(itemEnt, out var itemEntComponent))
|
||||||
|
{
|
||||||
|
var gridPiece = new ItemGridPiece((itemEnt, itemEntComponent), item.Value.Value, _entity)
|
||||||
|
{
|
||||||
|
MinSize = size,
|
||||||
|
};
|
||||||
|
gridPiece.OnPiecePressed += OnPiecePressed;
|
||||||
|
gridPiece.OnPieceUnpressed += OnPieceUnpressed;
|
||||||
|
|
||||||
|
control.AddChild(gridPiece);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_pieceGrid.AddChild(control);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void FrameUpdate(FrameEventArgs args)
|
||||||
|
{
|
||||||
|
base.FrameUpdate(args);
|
||||||
|
|
||||||
|
if (!IsOpen)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var itemSystem = _entity.System<ItemSystem>();
|
||||||
|
var storageSystem = _entity.System<StorageSystem>();
|
||||||
|
var handsSystem = _entity.System<HandsSystem>();
|
||||||
|
|
||||||
|
foreach (var child in _backgroundGrid.Children)
|
||||||
|
{
|
||||||
|
child.ModulateSelfOverride = Color.FromHex("#222222");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UserInterfaceManager.CurrentlyHovered is StorageContainer con && con != this)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!_entity.TryGetComponent<StorageComponent>(StorageEntity, out var storageComponent))
|
||||||
|
return;
|
||||||
|
|
||||||
|
EntityUid currentEnt;
|
||||||
|
ItemStorageLocation currentLocation;
|
||||||
|
var usingInHand = false;
|
||||||
|
if (_storageController.IsDragging && _storageController.DraggingGhost is { } dragging)
|
||||||
|
{
|
||||||
|
currentEnt = dragging.Entity;
|
||||||
|
currentLocation = dragging.Location;
|
||||||
|
}
|
||||||
|
else if (handsSystem.GetActiveHandEntity() is { } handEntity &&
|
||||||
|
storageSystem.CanInsert(StorageEntity.Value, handEntity, out _, storageComp: storageComponent, ignoreLocation: true))
|
||||||
|
{
|
||||||
|
currentEnt = handEntity;
|
||||||
|
currentLocation = new ItemStorageLocation(_storageController.DraggingRotation, Vector2i.Zero);
|
||||||
|
usingInHand = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_entity.TryGetComponent<ItemComponent>(currentEnt, out var itemComp))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var origin = GetMouseGridPieceLocation((currentEnt, itemComp), currentLocation);
|
||||||
|
|
||||||
|
var itemShape = itemSystem.GetAdjustedItemShape(
|
||||||
|
(currentEnt, itemComp),
|
||||||
|
currentLocation.Rotation,
|
||||||
|
origin);
|
||||||
|
var itemBounding = itemShape.GetBoundingBox();
|
||||||
|
|
||||||
|
var validLocation = storageSystem.ItemFitsInGridLocation(
|
||||||
|
(currentEnt, itemComp),
|
||||||
|
(StorageEntity.Value, storageComponent),
|
||||||
|
origin,
|
||||||
|
currentLocation.Rotation);
|
||||||
|
|
||||||
|
var validColor = usingInHand ? Color.Goldenrod : Color.Green;
|
||||||
|
|
||||||
|
for (var y = itemBounding.Bottom; y <= itemBounding.Top; y++)
|
||||||
|
{
|
||||||
|
for (var x = itemBounding.Left; x <= itemBounding.Right; x++)
|
||||||
|
{
|
||||||
|
if (TryGetBackgroundCell(x, y, out var cell) && itemShape.Contains(x, y))
|
||||||
|
{
|
||||||
|
cell.ModulateSelfOverride = validLocation ? validColor : Color.Red;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override DragMode GetDragModeFor(Vector2 relativeMousePos)
|
||||||
|
{
|
||||||
|
if (_storageController.StaticStorageUIEnabled)
|
||||||
|
return DragMode.None;
|
||||||
|
|
||||||
|
if (_sidebar.SizeBox.Contains(relativeMousePos - _sidebar.Position))
|
||||||
|
{
|
||||||
|
return DragMode.Move;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DragMode.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector2i GetMouseGridPieceLocation(Entity<ItemComponent?> entity, ItemStorageLocation location)
|
||||||
|
{
|
||||||
|
var origin = Vector2i.Zero;
|
||||||
|
|
||||||
|
if (StorageEntity != null)
|
||||||
|
origin = _entity.GetComponent<StorageComponent>(StorageEntity.Value).Grid.GetBoundingBox().BottomLeft;
|
||||||
|
|
||||||
|
var textureSize = (Vector2) _emptyTexture!.Size * 2;
|
||||||
|
var position = ((UserInterfaceManager.MousePositionScaled.Position
|
||||||
|
- _backgroundGrid.GlobalPosition
|
||||||
|
- ItemGridPiece.GetCenterOffset(entity, location, _entity) * 2
|
||||||
|
+ textureSize / 2f)
|
||||||
|
/ textureSize).Floored() + origin;
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetBackgroundCell(int x, int y, [NotNullWhen(true)] out Control? cell)
|
||||||
|
{
|
||||||
|
cell = null;
|
||||||
|
|
||||||
|
if (!_entity.TryGetComponent<StorageComponent>(StorageEntity, out var storageComponent))
|
||||||
|
return false;
|
||||||
|
var boundingBox = storageComponent.Grid.GetBoundingBox();
|
||||||
|
x -= boundingBox.Left;
|
||||||
|
y -= boundingBox.Bottom;
|
||||||
|
|
||||||
|
if (x < 0 ||
|
||||||
|
x >= _backgroundGrid.Columns ||
|
||||||
|
y < 0 ||
|
||||||
|
y >= _backgroundGrid.Rows)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cell = _backgroundGrid.GetChild(y * _backgroundGrid.Columns + x);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void KeyBindDown(GUIBoundKeyEventArgs args)
|
||||||
|
{
|
||||||
|
base.KeyBindDown(args);
|
||||||
|
|
||||||
|
if (!IsOpen)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var storageSystem = _entity.System<StorageSystem>();
|
||||||
|
var handsSystem = _entity.System<HandsSystem>();
|
||||||
|
|
||||||
|
if (args.Function == ContentKeyFunctions.MoveStoredItem && StorageEntity != null)
|
||||||
|
{
|
||||||
|
if (handsSystem.GetActiveHandEntity() is { } handEntity &&
|
||||||
|
storageSystem.CanInsert(StorageEntity.Value, handEntity, out _))
|
||||||
|
{
|
||||||
|
var pos = GetMouseGridPieceLocation((handEntity, null),
|
||||||
|
new ItemStorageLocation(_storageController.DraggingRotation, Vector2i.Zero));
|
||||||
|
|
||||||
|
var insertLocation = new ItemStorageLocation(_storageController.DraggingRotation, pos);
|
||||||
|
if (storageSystem.ItemFitsInGridLocation(
|
||||||
|
(handEntity, null),
|
||||||
|
(StorageEntity.Value, null),
|
||||||
|
insertLocation))
|
||||||
|
{
|
||||||
|
_entity.RaisePredictiveEvent(new StorageInsertItemIntoLocationEvent(
|
||||||
|
_entity.GetNetEntity(handEntity),
|
||||||
|
_entity.GetNetEntity(StorageEntity.Value),
|
||||||
|
insertLocation));
|
||||||
|
args.Handle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Close()
|
||||||
|
{
|
||||||
|
base.Close();
|
||||||
|
|
||||||
|
if (StorageEntity == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_entity.System<StorageSystem>().CloseStorageUI(StorageEntity.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,327 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using Content.Client.Examine;
|
||||||
|
using Content.Client.Hands.Systems;
|
||||||
|
using Content.Client.Interaction;
|
||||||
|
using Content.Client.Storage.Systems;
|
||||||
|
using Content.Client.UserInterface.Systems.Hotbar.Widgets;
|
||||||
|
using Content.Client.UserInterface.Systems.Storage.Controls;
|
||||||
|
using Content.Client.Verbs.UI;
|
||||||
|
using Content.Shared.CCVar;
|
||||||
|
using Content.Shared.Input;
|
||||||
|
using Content.Shared.Interaction;
|
||||||
|
using Content.Shared.Item;
|
||||||
|
using Content.Shared.Storage;
|
||||||
|
using Robust.Client.Input;
|
||||||
|
using Robust.Client.UserInterface;
|
||||||
|
using Robust.Client.UserInterface.Controllers;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Shared.Configuration;
|
||||||
|
using Robust.Shared.Input;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
|
namespace Content.Client.UserInterface.Systems.Storage;
|
||||||
|
|
||||||
|
public sealed class StorageUIController : UIController, IOnSystemChanged<StorageSystem>
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IConfigurationManager _configuration = default!;
|
||||||
|
[Dependency] private readonly IEntityManager _entity = default!;
|
||||||
|
[Dependency] private readonly IInputManager _input = default!;
|
||||||
|
[Dependency] private readonly IUserInterfaceManager _ui = default!;
|
||||||
|
|
||||||
|
private readonly DragDropHelper<ItemGridPiece> _menuDragHelper;
|
||||||
|
private StorageContainer? _container;
|
||||||
|
|
||||||
|
private Vector2? _lastContainerPosition;
|
||||||
|
|
||||||
|
private HotbarGui? Hotbar => UIManager.GetActiveUIWidgetOrNull<HotbarGui>();
|
||||||
|
|
||||||
|
public ItemGridPiece? DraggingGhost;
|
||||||
|
public Angle DraggingRotation = Angle.Zero;
|
||||||
|
public bool StaticStorageUIEnabled;
|
||||||
|
|
||||||
|
public bool IsDragging => _menuDragHelper.IsDragging;
|
||||||
|
public ItemGridPiece? CurrentlyDragging => _menuDragHelper.Dragged;
|
||||||
|
|
||||||
|
public StorageUIController()
|
||||||
|
{
|
||||||
|
_menuDragHelper = new DragDropHelper<ItemGridPiece>(OnMenuBeginDrag, OnMenuContinueDrag, OnMenuEndDrag);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
_configuration.OnValueChanged(CCVars.StaticStorageUI, OnStaticStorageChanged, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnSystemLoaded(StorageSystem system)
|
||||||
|
{
|
||||||
|
_input.FirstChanceOnKeyEvent += OnMiddleMouse;
|
||||||
|
system.StorageUpdated += OnStorageUpdated;
|
||||||
|
system.StorageOrderChanged += OnStorageOrderChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnSystemUnloaded(StorageSystem system)
|
||||||
|
{
|
||||||
|
_input.FirstChanceOnKeyEvent -= OnMiddleMouse;
|
||||||
|
system.StorageUpdated -= OnStorageUpdated;
|
||||||
|
system.StorageOrderChanged -= OnStorageOrderChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStorageOrderChanged(Entity<StorageComponent>? nullEnt)
|
||||||
|
{
|
||||||
|
if (_container == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_container.UpdateContainer(nullEnt);
|
||||||
|
|
||||||
|
if (IsDragging)
|
||||||
|
_menuDragHelper.EndDrag();
|
||||||
|
|
||||||
|
if (nullEnt is not null)
|
||||||
|
{
|
||||||
|
if (_lastContainerPosition == null)
|
||||||
|
{
|
||||||
|
_container.OpenCenteredAt(new Vector2(0.5f, 0.75f));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_container.Open();
|
||||||
|
|
||||||
|
if (!StaticStorageUIEnabled)
|
||||||
|
LayoutContainer.SetPosition(_container, _lastContainerPosition.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StaticStorageUIEnabled)
|
||||||
|
{
|
||||||
|
// we have to orphan it here because Open() sets the parent.
|
||||||
|
_container.Orphan();
|
||||||
|
Hotbar?.StorageContainer.AddChild(_container);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_lastContainerPosition = _container.GlobalPosition;
|
||||||
|
_container.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStaticStorageChanged(bool obj)
|
||||||
|
{
|
||||||
|
if (StaticStorageUIEnabled == obj)
|
||||||
|
return;
|
||||||
|
|
||||||
|
StaticStorageUIEnabled = obj;
|
||||||
|
|
||||||
|
if (_container == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!_container.IsOpen)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_container.Orphan();
|
||||||
|
if (StaticStorageUIEnabled)
|
||||||
|
{
|
||||||
|
Hotbar?.StorageContainer.AddChild(_container);
|
||||||
|
_lastContainerPosition = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_ui.WindowRoot.AddChild(_container);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// One might ask, Hey Emo, why are you parsing raw keyboard input just to rotate a rectangle?
|
||||||
|
/// The answer is, that input bindings regarding mouse inputs are always intercepted by the UI,
|
||||||
|
/// thus, if i want to be able to rotate my damn piece anywhere on the screen,
|
||||||
|
/// I have to side-step all of the input handling. Cheers.
|
||||||
|
private void OnMiddleMouse(KeyEventArgs keyEvent, KeyEventType type)
|
||||||
|
{
|
||||||
|
if (keyEvent.Handled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (type != KeyEventType.Down)
|
||||||
|
return;
|
||||||
|
|
||||||
|
//todo there's gotta be a method for this in InputManager just expose it to content I BEG.
|
||||||
|
if (!_input.TryGetKeyBinding(ContentKeyFunctions.RotateStoredItem, out var binding))
|
||||||
|
return;
|
||||||
|
if (binding.BaseKey != keyEvent.Key)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (keyEvent.Shift &&
|
||||||
|
!(binding.Mod1 == Keyboard.Key.Shift ||
|
||||||
|
binding.Mod2 == Keyboard.Key.Shift ||
|
||||||
|
binding.Mod3 == Keyboard.Key.Shift))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (keyEvent.Alt &&
|
||||||
|
!(binding.Mod1 == Keyboard.Key.Alt ||
|
||||||
|
binding.Mod2 == Keyboard.Key.Alt ||
|
||||||
|
binding.Mod3 == Keyboard.Key.Alt))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (keyEvent.Control &&
|
||||||
|
!(binding.Mod1 == Keyboard.Key.Control ||
|
||||||
|
binding.Mod2 == Keyboard.Key.Control ||
|
||||||
|
binding.Mod3 == Keyboard.Key.Control))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!IsDragging && _entity.System<HandsSystem>().GetActiveHandEntity() == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
//clamp it to a cardinal.
|
||||||
|
DraggingRotation = (DraggingRotation + Math.PI / 2f).GetCardinalDir().ToAngle();
|
||||||
|
if (DraggingGhost != null)
|
||||||
|
DraggingGhost.Location.Rotation = DraggingRotation;
|
||||||
|
|
||||||
|
if (IsDragging || (_container != null && UIManager.CurrentlyHovered == _container))
|
||||||
|
keyEvent.Handle();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStorageUpdated(Entity<StorageComponent> uid)
|
||||||
|
{
|
||||||
|
if (_container?.StorageEntity != uid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_container.BuildItemPieces();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RegisterStorageContainer(StorageContainer container)
|
||||||
|
{
|
||||||
|
if (_container != null)
|
||||||
|
{
|
||||||
|
container.OnPiecePressed -= OnPiecePressed;
|
||||||
|
container.OnPieceUnpressed -= OnPieceUnpressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
_container = container;
|
||||||
|
container.OnPiecePressed += OnPiecePressed;
|
||||||
|
container.OnPieceUnpressed += OnPieceUnpressed;
|
||||||
|
|
||||||
|
if (!StaticStorageUIEnabled)
|
||||||
|
_container.Orphan();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPiecePressed(GUIBoundKeyEventArgs args, ItemGridPiece control)
|
||||||
|
{
|
||||||
|
if (IsDragging || !_container?.IsOpen == true)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (args.Function == ContentKeyFunctions.MoveStoredItem)
|
||||||
|
{
|
||||||
|
_menuDragHelper.MouseDown(control);
|
||||||
|
_menuDragHelper.Update(0f);
|
||||||
|
|
||||||
|
args.Handle();
|
||||||
|
}
|
||||||
|
else if (args.Function == ContentKeyFunctions.ExamineEntity)
|
||||||
|
{
|
||||||
|
_entity.System<ExamineSystem>().DoExamine(control.Entity);
|
||||||
|
args.Handle();
|
||||||
|
}
|
||||||
|
else if (args.Function == EngineKeyFunctions.UseSecondary)
|
||||||
|
{
|
||||||
|
UIManager.GetUIController<VerbMenuUIController>().OpenVerbMenu(control.Entity);
|
||||||
|
args.Handle();
|
||||||
|
}
|
||||||
|
else if (args.Function == ContentKeyFunctions.ActivateItemInWorld)
|
||||||
|
{
|
||||||
|
_entity.EntityNetManager?.SendSystemNetworkMessage(
|
||||||
|
new InteractInventorySlotEvent(_entity.GetNetEntity(control.Entity), altInteract: false));
|
||||||
|
args.Handle();
|
||||||
|
}
|
||||||
|
else if (args.Function == ContentKeyFunctions.AltActivateItemInWorld)
|
||||||
|
{
|
||||||
|
_entity.RaisePredictiveEvent(new InteractInventorySlotEvent(_entity.GetNetEntity(control.Entity), altInteract: true));
|
||||||
|
args.Handle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPieceUnpressed(GUIBoundKeyEventArgs args, ItemGridPiece control)
|
||||||
|
{
|
||||||
|
if (_container?.StorageEntity is not { } storageEnt)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (args.Function == ContentKeyFunctions.MoveStoredItem)
|
||||||
|
{
|
||||||
|
if (DraggingGhost is { } draggingGhost)
|
||||||
|
{
|
||||||
|
var position = _container.GetMouseGridPieceLocation(draggingGhost.Entity, draggingGhost.Location);
|
||||||
|
_entity.RaisePredictiveEvent(new StorageSetItemLocationEvent(
|
||||||
|
_entity.GetNetEntity(draggingGhost.Entity),
|
||||||
|
_entity.GetNetEntity(storageEnt),
|
||||||
|
new ItemStorageLocation(DraggingRotation, position)));
|
||||||
|
_container?.BuildItemPieces();
|
||||||
|
}
|
||||||
|
else //if we just clicked, then take it out of the bag.
|
||||||
|
{
|
||||||
|
_entity.RaisePredictiveEvent(new StorageInteractWithItemEvent(
|
||||||
|
_entity.GetNetEntity(control.Entity),
|
||||||
|
_entity.GetNetEntity(storageEnt)));
|
||||||
|
}
|
||||||
|
_menuDragHelper.EndDrag();
|
||||||
|
args.Handle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool OnMenuBeginDrag()
|
||||||
|
{
|
||||||
|
if (_menuDragHelper.Dragged is not { } dragged)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
DraggingRotation = dragged.Location.Rotation;
|
||||||
|
DraggingGhost = new ItemGridPiece(
|
||||||
|
(dragged.Entity, _entity.GetComponent<ItemComponent>(dragged.Entity)),
|
||||||
|
dragged.Location,
|
||||||
|
_entity);
|
||||||
|
DraggingGhost.MouseFilter = Control.MouseFilterMode.Ignore;
|
||||||
|
DraggingGhost.Visible = true;
|
||||||
|
DraggingGhost.Orphan();
|
||||||
|
|
||||||
|
UIManager.PopupRoot.AddChild(DraggingGhost);
|
||||||
|
SetDraggingRotation();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool OnMenuContinueDrag(float frameTime)
|
||||||
|
{
|
||||||
|
if (DraggingGhost == null)
|
||||||
|
return false;
|
||||||
|
SetDraggingRotation();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetDraggingRotation()
|
||||||
|
{
|
||||||
|
if (DraggingGhost == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var offset = ItemGridPiece.GetCenterOffset(
|
||||||
|
(DraggingGhost.Entity, null),
|
||||||
|
new ItemStorageLocation(DraggingRotation, Vector2i.Zero),
|
||||||
|
_entity);
|
||||||
|
|
||||||
|
// I don't know why it divides the position by 2. Hope this helps! -emo
|
||||||
|
LayoutContainer.SetPosition(DraggingGhost, UIManager.MousePositionScaled.Position / 2 - offset );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnMenuEndDrag()
|
||||||
|
{
|
||||||
|
if (DraggingGhost == null)
|
||||||
|
return;
|
||||||
|
DraggingGhost.Visible = false;
|
||||||
|
DraggingGhost = null;
|
||||||
|
DraggingRotation = Angle.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void FrameUpdate(FrameEventArgs args)
|
||||||
|
{
|
||||||
|
base.FrameUpdate(args);
|
||||||
|
|
||||||
|
_menuDragHelper.Update(args.DeltaSeconds);
|
||||||
|
|
||||||
|
if (!StaticStorageUIEnabled && _container?.Parent != null)
|
||||||
|
_lastContainerPosition = _container.GlobalPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -128,15 +128,7 @@ namespace Content.IntegrationTests.Tests
|
|||||||
if (maxSize == null)
|
if (maxSize == null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (storage.MaxSlots != null)
|
Assert.That(size, Is.LessThanOrEqualTo(storage.Grid.GetArea()), $"{proto.ID} storage fill is too large.");
|
||||||
{
|
|
||||||
Assert.That(GetFillSize(fill, true, protoMan, itemSys), Is.LessThanOrEqualTo(storage.MaxSlots),
|
|
||||||
$"{proto.ID} storage fill has too many items.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Assert.That(size, Is.LessThanOrEqualTo(storage.MaxTotalWeight), $"{proto.ID} storage fill is too large.");
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var entry in fill.Contents)
|
foreach (var entry in fill.Contents)
|
||||||
{
|
{
|
||||||
@@ -210,7 +202,7 @@ namespace Content.IntegrationTests.Tests
|
|||||||
|
|
||||||
|
|
||||||
if (proto.TryGetComponent<ItemComponent>("Item", out var item))
|
if (proto.TryGetComponent<ItemComponent>("Item", out var item))
|
||||||
return itemSystem.GetItemSizeWeight(item.Size) * entry.Amount;
|
return itemSystem.GetItemShape(item).GetArea() * entry.Amount;
|
||||||
|
|
||||||
Assert.Fail($"Prototype is missing item comp: {entry.PrototypeId}");
|
Assert.Fail($"Prototype is missing item comp: {entry.PrototypeId}");
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -355,7 +355,7 @@ namespace Content.Server.Chemistry.EntitySystems
|
|||||||
return (Name(pill), quantity);
|
return (Name(pill), quantity);
|
||||||
})).ToList();
|
})).ToList();
|
||||||
|
|
||||||
return new ContainerInfo(name, _storageSystem.GetCumulativeItemSizes(container.Value, storage), storage.MaxTotalWeight)
|
return new ContainerInfo(name, _storageSystem.GetCumulativeItemAreas((container.Value, storage)), storage.Grid.GetArea())
|
||||||
{
|
{
|
||||||
Entities = pills
|
Entities = pills
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
|
using System.Linq;
|
||||||
using Content.Server.Spawners.Components;
|
using Content.Server.Spawners.Components;
|
||||||
using Content.Server.Storage.Components;
|
using Content.Server.Storage.Components;
|
||||||
|
using Content.Shared.Item;
|
||||||
using Content.Shared.Prototypes;
|
using Content.Shared.Prototypes;
|
||||||
using Content.Shared.Storage;
|
using Content.Shared.Storage;
|
||||||
using Content.Shared.Storage.Components;
|
using Content.Shared.Storage.Components;
|
||||||
@@ -15,14 +17,70 @@ public sealed partial class StorageSystem
|
|||||||
if (component.Contents.Count == 0)
|
if (component.Contents.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
TryComp<StorageComponent>(uid, out var storageComp);
|
if (TryComp<StorageComponent>(uid, out var storageComp))
|
||||||
TryComp<EntityStorageComponent>(uid, out var entityStorageComp);
|
{
|
||||||
|
FillStorage((uid, component, storageComp));
|
||||||
if (entityStorageComp == null && storageComp == null)
|
}
|
||||||
|
else if (TryComp<EntityStorageComponent>(uid, out var entityStorageComp))
|
||||||
|
{
|
||||||
|
FillEntityStorage((uid, component, entityStorageComp));
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
Log.Error($"StorageFillComponent couldn't find any StorageComponent ({uid})");
|
Log.Error($"StorageFillComponent couldn't find any StorageComponent ({uid})");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FillStorage(Entity<StorageFillComponent?, StorageComponent?> entity)
|
||||||
|
{
|
||||||
|
var (uid, component, storage) = entity;
|
||||||
|
|
||||||
|
if (!Resolve(uid, ref component, ref storage))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var coordinates = Transform(uid).Coordinates;
|
||||||
|
|
||||||
|
var spawnItems = EntitySpawnCollection.GetSpawns(component.Contents, Random);
|
||||||
|
|
||||||
|
var items = new List<Entity<ItemComponent>>();
|
||||||
|
foreach (var spawnPrototype in spawnItems)
|
||||||
|
{
|
||||||
|
var ent = Spawn(spawnPrototype, coordinates);
|
||||||
|
|
||||||
|
// No, you are not allowed to fill a container with entity spawners.
|
||||||
|
DebugTools.Assert(!_prototype.Index<EntityPrototype>(spawnPrototype)
|
||||||
|
.HasComponent(typeof(RandomSpawnerComponent)));
|
||||||
|
|
||||||
|
if (!TryComp<ItemComponent>(ent, out var itemComp))
|
||||||
|
{
|
||||||
|
Log.Error($"Tried to fill {ToPrettyString(entity)} with non-item {spawnPrototype}.");
|
||||||
|
Del(ent);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
items.Add((ent, itemComp));
|
||||||
|
}
|
||||||
|
|
||||||
|
// we order the items from biggest to smallest to try and reduce poor placement in the grid.
|
||||||
|
var sortedItems = items
|
||||||
|
.OrderByDescending(x => ItemSystem.GetItemShape(x.Comp).GetArea());
|
||||||
|
|
||||||
|
foreach (var ent in sortedItems)
|
||||||
|
{
|
||||||
|
if (Insert(uid, ent, out _, out var reason, storageComp: storage, playSound: false))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Log.Error($"Tried to StorageFill {ToPrettyString(ent)} inside {ToPrettyString(uid)} but can't. reason: {reason}");
|
||||||
|
Del(ent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FillEntityStorage(Entity<StorageFillComponent?, EntityStorageComponent?> entity)
|
||||||
|
{
|
||||||
|
var (uid, component, entityStorageComp) = entity;
|
||||||
|
|
||||||
|
if (!Resolve(uid, ref component, ref entityStorageComp))
|
||||||
|
return;
|
||||||
|
|
||||||
var coordinates = Transform(uid).Coordinates;
|
var coordinates = Transform(uid).Coordinates;
|
||||||
|
|
||||||
@@ -32,18 +90,14 @@ public sealed partial class StorageSystem
|
|||||||
// No, you are not allowed to fill a container with entity spawners.
|
// No, you are not allowed to fill a container with entity spawners.
|
||||||
DebugTools.Assert(!_prototype.Index<EntityPrototype>(item)
|
DebugTools.Assert(!_prototype.Index<EntityPrototype>(item)
|
||||||
.HasComponent(typeof(RandomSpawnerComponent)));
|
.HasComponent(typeof(RandomSpawnerComponent)));
|
||||||
var ent = EntityManager.SpawnEntity(item, coordinates);
|
var ent = Spawn(item, coordinates);
|
||||||
|
|
||||||
// handle depending on storage component, again this should be unified after ECS
|
// handle depending on storage component, again this should be unified after ECS
|
||||||
if (entityStorageComp != null && EntityStorage.Insert(ent, uid, entityStorageComp))
|
if (entityStorageComp != null && EntityStorage.Insert(ent, uid, entityStorageComp))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var reason = string.Empty;
|
Log.Error($"Tried to StorageFill {item} inside {ToPrettyString(uid)} but can't.");
|
||||||
if (storageComp != null && Insert(uid, ent, out _, out reason, storageComp: storageComp, playSound: false))
|
Del(ent);
|
||||||
continue;
|
|
||||||
|
|
||||||
Log.Error($"Tried to StorageFill {item} inside {ToPrettyString(uid)} but can't. Reason: {Loc.GetString(reason ?? "no reason.")}");
|
|
||||||
EntityManager.DeleteEntity(ent);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1655,6 +1655,12 @@ namespace Content.Shared.CCVar
|
|||||||
public static readonly CVarDef<bool> ToggleWalk =
|
public static readonly CVarDef<bool> ToggleWalk =
|
||||||
CVarDef.Create("control.toggle_walk", false, CVar.CLIENTONLY | CVar.ARCHIVE);
|
CVarDef.Create("control.toggle_walk", false, CVar.CLIENTONLY | CVar.ARCHIVE);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether or not the storage UI is static and bound to the hotbar, or unbound and allowed to be dragged anywhere.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly CVarDef<bool> StaticStorageUI =
|
||||||
|
CVarDef.Create("control.static_storage_ui", true, CVar.CLIENTONLY | CVar.ARCHIVE);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* UPDATE
|
* UPDATE
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ public sealed class ContainerHeldSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (_storage.GetCumulativeItemSizes(uid, storage) >= comp.Threshold)
|
if (_storage.GetCumulativeItemAreas(uid) >= comp.Threshold)
|
||||||
{
|
{
|
||||||
_item.SetHeldPrefix(uid, "full", item);
|
_item.SetHeldPrefix(uid, "full", item);
|
||||||
_appearance.SetData(uid, ToggleVisuals.Toggled, true, appearance);
|
_appearance.SetData(uid, ToggleVisuals.Toggled, true, appearance);
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ namespace Content.Shared.Input
|
|||||||
public static readonly BoundKeyFunction SmartEquipBelt = "SmartEquipBelt";
|
public static readonly BoundKeyFunction SmartEquipBelt = "SmartEquipBelt";
|
||||||
public static readonly BoundKeyFunction OpenAHelp = "OpenAHelp";
|
public static readonly BoundKeyFunction OpenAHelp = "OpenAHelp";
|
||||||
public static readonly BoundKeyFunction SwapHands = "SwapHands";
|
public static readonly BoundKeyFunction SwapHands = "SwapHands";
|
||||||
|
public static readonly BoundKeyFunction MoveStoredItem = "MoveStoredItem";
|
||||||
|
public static readonly BoundKeyFunction RotateStoredItem = "RotateStoredItem";
|
||||||
public static readonly BoundKeyFunction ThrowItemInHand = "ThrowItemInHand";
|
public static readonly BoundKeyFunction ThrowItemInHand = "ThrowItemInHand";
|
||||||
public static readonly BoundKeyFunction TryPullObject = "TryPullObject";
|
public static readonly BoundKeyFunction TryPullObject = "TryPullObject";
|
||||||
public static readonly BoundKeyFunction MovePulledObject = "MovePulledObject";
|
public static readonly BoundKeyFunction MovePulledObject = "MovePulledObject";
|
||||||
|
|||||||
@@ -34,6 +34,13 @@ public sealed partial class ItemComponent : Component
|
|||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
[DataField("sprite")]
|
[DataField("sprite")]
|
||||||
public string? RsiPath;
|
public string? RsiPath;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An optional override for the shape of the item within the grid storage.
|
||||||
|
/// If null, a default shape will be used based on <see cref="Size"/>.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public List<Box2i>? Shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
|
|||||||
@@ -24,6 +24,12 @@ public sealed partial class ItemSizePrototype : IPrototype, IComparable<ItemSize
|
|||||||
[DataField]
|
[DataField]
|
||||||
public readonly LocId Name;
|
public readonly LocId Name;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The default inventory shape associated with this item size.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(required: true)]
|
||||||
|
public IReadOnlyList<Box2i> DefaultShape = new List<Box2i>();
|
||||||
|
|
||||||
public int CompareTo(ItemSizePrototype? other)
|
public int CompareTo(ItemSizePrototype? other)
|
||||||
{
|
{
|
||||||
if (other is not { } otherItemSize)
|
if (other is not { } otherItemSize)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using Content.Shared.Hands.EntitySystems;
|
|||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Verbs;
|
using Content.Shared.Verbs;
|
||||||
using Content.Shared.Examine;
|
using Content.Shared.Examine;
|
||||||
|
using Content.Shared.Storage;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
@@ -150,4 +151,58 @@ public abstract class SharedItemSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
return GetSizePrototype(size).Weight;
|
return GetSizePrototype(size).Weight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the default shape of an item.
|
||||||
|
/// </summary>
|
||||||
|
public IReadOnlyList<Box2i> GetItemShape(Entity<ItemComponent?> uid)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref uid.Comp))
|
||||||
|
return new Box2i[] { };
|
||||||
|
|
||||||
|
return uid.Comp.Shape ?? GetSizePrototype(uid.Comp.Size).DefaultShape;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the default shape of an item.
|
||||||
|
/// </summary>
|
||||||
|
public IReadOnlyList<Box2i> GetItemShape(ItemComponent component)
|
||||||
|
{
|
||||||
|
return component.Shape ?? GetSizePrototype(component.Size).DefaultShape;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the shape of an item, adjusting for rotation and offset.
|
||||||
|
/// </summary>
|
||||||
|
public IReadOnlyList<Box2i> GetAdjustedItemShape(Entity<ItemComponent?> entity, ItemStorageLocation location)
|
||||||
|
{
|
||||||
|
return GetAdjustedItemShape(entity, location.Rotation, location.Position);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the shape of an item, adjusting for rotation and offset.
|
||||||
|
/// </summary>
|
||||||
|
public IReadOnlyList<Box2i> GetAdjustedItemShape(Entity<ItemComponent?> entity, Angle rotation, Vector2i position)
|
||||||
|
{
|
||||||
|
if (!Resolve(entity, ref entity.Comp))
|
||||||
|
return new Box2i[] { };
|
||||||
|
|
||||||
|
var shapes = GetItemShape(entity);
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Content.Shared.ActionBlocker;
|
using Content.Shared.ActionBlocker;
|
||||||
using Content.Shared.CombatMode;
|
using Content.Shared.CombatMode;
|
||||||
using Content.Shared.Containers.ItemSlots;
|
using Content.Shared.Containers.ItemSlots;
|
||||||
using Content.Shared.Destructible;
|
using Content.Shared.Destructible;
|
||||||
using Content.Shared.DoAfter;
|
using Content.Shared.DoAfter;
|
||||||
using Content.Shared.FixedPoint;
|
|
||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Hands.Components;
|
||||||
using Content.Shared.Hands.EntitySystems;
|
using Content.Shared.Hands.EntitySystems;
|
||||||
using Content.Shared.Implants.Components;
|
using Content.Shared.Implants.Components;
|
||||||
@@ -17,7 +17,6 @@ using Content.Shared.Stacks;
|
|||||||
using Content.Shared.Storage.Components;
|
using Content.Shared.Storage.Components;
|
||||||
using Content.Shared.Timing;
|
using Content.Shared.Timing;
|
||||||
using Content.Shared.Verbs;
|
using Content.Shared.Verbs;
|
||||||
using Robust.Shared.Audio;
|
|
||||||
using Robust.Shared.Audio.Systems;
|
using Robust.Shared.Audio.Systems;
|
||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
@@ -35,15 +34,16 @@ public abstract class SharedStorageSystem : EntitySystem
|
|||||||
[Dependency] private readonly EntityLookupSystem _entityLookupSystem = default!;
|
[Dependency] private readonly EntityLookupSystem _entityLookupSystem = default!;
|
||||||
[Dependency] protected readonly SharedEntityStorageSystem EntityStorage = default!;
|
[Dependency] protected readonly SharedEntityStorageSystem EntityStorage = default!;
|
||||||
[Dependency] private readonly SharedInteractionSystem _interactionSystem = 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 SharedPopupSystem _popupSystem = default!;
|
||||||
[Dependency] private readonly SharedHandsSystem _sharedHandsSystem = default!;
|
[Dependency] private readonly SharedHandsSystem _sharedHandsSystem = default!;
|
||||||
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
|
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
|
||||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||||
[Dependency] protected readonly SharedAudioSystem Audio = default!;
|
[Dependency] protected readonly SharedAudioSystem Audio = default!;
|
||||||
[Dependency] private readonly SharedCombatModeSystem _combatMode = 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 SharedStackSystem _stack = default!;
|
||||||
|
[Dependency] private readonly SharedUserInterfaceSystem _ui = default!;
|
||||||
[Dependency] protected readonly UseDelaySystem UseDelay = default!;
|
[Dependency] protected readonly UseDelaySystem UseDelay = default!;
|
||||||
|
|
||||||
private EntityQuery<ItemComponent> _itemQuery;
|
private EntityQuery<ItemComponent> _itemQuery;
|
||||||
@@ -69,17 +69,18 @@ public abstract class SharedStorageSystem : EntitySystem
|
|||||||
SubscribeLocalEvent<StorageComponent, OpenStorageImplantEvent>(OnImplantActivate);
|
SubscribeLocalEvent<StorageComponent, OpenStorageImplantEvent>(OnImplantActivate);
|
||||||
SubscribeLocalEvent<StorageComponent, AfterInteractEvent>(AfterInteract);
|
SubscribeLocalEvent<StorageComponent, AfterInteractEvent>(AfterInteract);
|
||||||
SubscribeLocalEvent<StorageComponent, DestructionEventArgs>(OnDestroy);
|
SubscribeLocalEvent<StorageComponent, DestructionEventArgs>(OnDestroy);
|
||||||
SubscribeLocalEvent<StorageComponent, StorageComponent.StorageInsertItemMessage>(OnInsertItemMessage);
|
|
||||||
SubscribeLocalEvent<StorageComponent, BoundUIOpenedEvent>(OnBoundUIOpen);
|
SubscribeLocalEvent<StorageComponent, BoundUIOpenedEvent>(OnBoundUIOpen);
|
||||||
SubscribeLocalEvent<MetaDataComponent, StackCountChangedEvent>(OnStackCountChanged);
|
SubscribeLocalEvent<MetaDataComponent, StackCountChangedEvent>(OnStackCountChanged);
|
||||||
|
|
||||||
SubscribeLocalEvent<StorageComponent, EntInsertedIntoContainerMessage>(OnContainerModified);
|
SubscribeLocalEvent<StorageComponent, EntInsertedIntoContainerMessage>(OnEntInserted);
|
||||||
SubscribeLocalEvent<StorageComponent, EntRemovedFromContainerMessage>(OnContainerModified);
|
SubscribeLocalEvent<StorageComponent, EntRemovedFromContainerMessage>(OnEntRemoved);
|
||||||
SubscribeLocalEvent<StorageComponent, ContainerIsInsertingAttemptEvent>(OnInsertAttempt);
|
SubscribeLocalEvent<StorageComponent, ContainerIsInsertingAttemptEvent>(OnInsertAttempt);
|
||||||
|
|
||||||
SubscribeLocalEvent<StorageComponent, AreaPickupDoAfterEvent>(OnDoAfter);
|
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)
|
private void OnComponentInit(EntityUid uid, StorageComponent storageComp, ComponentInit args)
|
||||||
@@ -132,7 +133,7 @@ public abstract class SharedStorageSystem : EntitySystem
|
|||||||
PlayerInsertHeldEntity(uid, args.User, storageComp);
|
PlayerInsertHeldEntity(uid, args.User, storageComp);
|
||||||
// Always handle it, even if insertion fails.
|
// Always handle it, even if insertion fails.
|
||||||
// We don't want to trigger any AfterInteract logic here.
|
// 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;
|
args.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,6 +146,7 @@ public abstract class SharedStorageSystem : EntitySystem
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
OpenStorageUI(uid, args.User, storageComp);
|
OpenStorageUI(uid, args.User, storageComp);
|
||||||
|
args.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -152,11 +154,11 @@ public abstract class SharedStorageSystem : EntitySystem
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnImplantActivate(EntityUid uid, StorageComponent storageComp, OpenStorageImplantEvent args)
|
private void OnImplantActivate(EntityUid uid, StorageComponent storageComp, OpenStorageImplantEvent args)
|
||||||
{
|
{
|
||||||
// TODO: Make this an action or something.
|
if (args.Handled)
|
||||||
if (args.Handled || !_xformQuery.TryGetComponent(uid, out var xform))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
OpenStorageUI(uid, xform.ParentUid, storageComp);
|
OpenStorageUI(uid, args.Performer, storageComp);
|
||||||
|
args.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -224,8 +226,8 @@ public abstract class SharedStorageSystem : EntitySystem
|
|||||||
|
|
||||||
var position = EntityCoordinates.FromMap(
|
var position = EntityCoordinates.FromMap(
|
||||||
parent.IsValid() ? parent : uid,
|
parent.IsValid() ? parent : uid,
|
||||||
transformEnt.MapPosition,
|
TransformSystem.GetMapCoordinates(transformEnt),
|
||||||
_transform
|
TransformSystem
|
||||||
);
|
);
|
||||||
|
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
@@ -270,8 +272,8 @@ public abstract class SharedStorageSystem : EntitySystem
|
|||||||
|
|
||||||
var position = EntityCoordinates.FromMap(
|
var position = EntityCoordinates.FromMap(
|
||||||
xform.ParentUid.IsValid() ? xform.ParentUid : uid,
|
xform.ParentUid.IsValid() ? xform.ParentUid : uid,
|
||||||
new MapCoordinates(_transform.GetWorldPosition(targetXform), targetXform.MapID),
|
new MapCoordinates(TransformSystem.GetWorldPosition(targetXform), targetXform.MapID),
|
||||||
_transform
|
TransformSystem
|
||||||
);
|
);
|
||||||
|
|
||||||
var angle = targetXform.LocalRotation;
|
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)
|
if (successfullyInserted.Count > 0)
|
||||||
{
|
{
|
||||||
Audio.PlayPvs(component.StorageInsertSound, uid);
|
Audio.PlayPvs(component.StorageInsertSound, uid);
|
||||||
@@ -300,7 +302,7 @@ public abstract class SharedStorageSystem : EntitySystem
|
|||||||
|
|
||||||
private void OnDestroy(EntityUid uid, StorageComponent storageComp, DestructionEventArgs args)
|
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.
|
// Being destroyed so need to recalculate.
|
||||||
_containerSystem.EmptyContainer(storageComp.Container, destination: coordinates);
|
_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
|
/// item in the user's hand if it is currently empty, or interact with the item using the user's currently
|
||||||
/// held item.
|
/// held item.
|
||||||
/// </summary>
|
/// </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;
|
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))
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -346,12 +356,58 @@ public abstract class SharedStorageSystem : EntitySystem
|
|||||||
_interactionSystem.InteractUsing(player, hands.ActiveHandEntity.Value, entity, Transform(entity).Coordinates, checkCanInteract: false);
|
_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;
|
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)
|
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
|
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||||
if (component.Container == null)
|
if (entity.Comp.Container == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (args.Container.ID != StorageComponent.ContainerId)
|
if (args.Container.ID != StorageComponent.ContainerId)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
UpdateAppearance((uid, component, null));
|
if (!entity.Comp.StoredItems.ContainsKey(GetNetEntity(args.Entity)))
|
||||||
UpdateUI((uid, component));
|
{
|
||||||
|
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)
|
private void OnInsertAttempt(EntityUid uid, StorageComponent component, ContainerIsInsertingAttemptEvent args)
|
||||||
@@ -396,18 +480,8 @@ public abstract class SharedStorageSystem : EntitySystem
|
|||||||
if (storage.Container == null)
|
if (storage.Container == null)
|
||||||
return; // component hasn't yet been initialized.
|
return; // component hasn't yet been initialized.
|
||||||
|
|
||||||
int capacity;
|
var capacity = storage.Grid.GetArea();
|
||||||
int used;
|
var used = GetCumulativeItemAreas((uid, storage));
|
||||||
if (storage.MaxSlots == null)
|
|
||||||
{
|
|
||||||
used = GetCumulativeItemSizes(uid, storage);
|
|
||||||
capacity = storage.MaxTotalWeight;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
capacity = storage.MaxSlots.Value;
|
|
||||||
used = storage.Container.ContainedEntities.Count;
|
|
||||||
}
|
|
||||||
|
|
||||||
_appearance.SetData(uid, StorageVisuals.StorageUsed, used, appearance);
|
_appearance.SetData(uid, StorageVisuals.StorageUsed, used, appearance);
|
||||||
_appearance.SetData(uid, StorageVisuals.Capacity, capacity, 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="reason">If returning false, the reason displayed to the player</param>
|
||||||
/// <param name="storageComp"></param>
|
/// <param name="storageComp"></param>
|
||||||
/// <param name="item"></param>
|
/// <param name="item"></param>
|
||||||
|
/// <param name="ignoreStacks"></param>
|
||||||
|
/// <param name="ignoreLocation"></param>
|
||||||
/// <returns>true if it can be inserted, false otherwise</returns>
|
/// <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))
|
if (!Resolve(uid, ref storageComp) || !Resolve(insertEnt, ref item, false))
|
||||||
{
|
{
|
||||||
@@ -485,38 +568,58 @@ public abstract class SharedStorageSystem : EntitySystem
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var maxSize = _item.GetSizePrototype(GetMaxItemSize((uid, storageComp)));
|
var maxSize = ItemSystem.GetSizePrototype(GetMaxItemSize((uid, storageComp)));
|
||||||
if (_item.GetSizePrototype(item.Size) > maxSize)
|
if (ItemSystem.GetSizePrototype(item.Size) > maxSize)
|
||||||
{
|
{
|
||||||
reason = "comp-storage-too-big";
|
reason = "comp-storage-too-big";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TryComp<StorageComponent>(insertEnt, out var insertStorage)
|
if (TryComp<StorageComponent>(insertEnt, out var insertStorage)
|
||||||
&& _item.GetSizePrototype(GetMaxItemSize((insertEnt, insertStorage))) >= maxSize)
|
&& ItemSystem.GetSizePrototype(GetMaxItemSize((insertEnt, insertStorage))) >= maxSize)
|
||||||
{
|
{
|
||||||
reason = "comp-storage-too-big";
|
reason = "comp-storage-too-big";
|
||||||
return false;
|
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";
|
reason = "comp-storage-insufficient-capacity";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (_item.GetItemSizeWeight(item.Size) + GetCumulativeItemSizes(uid, storageComp) > storageComp.MaxTotalWeight)
|
|
||||||
{
|
|
||||||
reason = "comp-storage-insufficient-capacity";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
reason = null;
|
reason = null;
|
||||||
return true;
|
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>
|
/// <summary>
|
||||||
/// Inserts into the storage container
|
/// Inserts into the storage container
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -653,6 +756,148 @@ public abstract class SharedStorageSystem : EntitySystem
|
|||||||
return true;
|
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>
|
/// <summary>
|
||||||
/// Returns true if there is enough space to theoretically fit another item.
|
/// Returns true if there is enough space to theoretically fit another item.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -661,13 +906,7 @@ public abstract class SharedStorageSystem : EntitySystem
|
|||||||
if (!Resolve(uid, ref uid.Comp))
|
if (!Resolve(uid, ref uid.Comp))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
//todo maybe this shouldn't be authoritative over weight? idk.
|
return GetCumulativeItemAreas(uid) < uid.Comp.Grid.GetArea() || HasSpaceInStacks(uid);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool HasSpaceInStacks(Entity<StorageComponent?> uid, string? stackType = null)
|
private bool HasSpaceInStacks(Entity<StorageComponent?> uid, string? stackType = null)
|
||||||
@@ -695,17 +934,17 @@ public abstract class SharedStorageSystem : EntitySystem
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the sum of all the ItemSizes of the items inside of a storage.
|
/// Returns the sum of all the ItemSizes of the items inside of a storage.
|
||||||
/// </summary>
|
/// </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;
|
return 0;
|
||||||
|
|
||||||
var sum = 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))
|
if (!_itemQuery.TryGetComponent(item, out var itemComp))
|
||||||
continue;
|
continue;
|
||||||
sum += _item.GetItemSizeWeight(itemComp.Size);
|
sum += ItemSystem.GetItemShape((item, itemComp)).GetArea();
|
||||||
}
|
}
|
||||||
|
|
||||||
return sum;
|
return sum;
|
||||||
@@ -722,7 +961,7 @@ public abstract class SharedStorageSystem : EntitySystem
|
|||||||
|
|
||||||
if (!_itemQuery.TryGetComponent(uid, out var item))
|
if (!_itemQuery.TryGetComponent(uid, out var item))
|
||||||
return DefaultStorageMaxItemSize;
|
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
|
// 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
|
// 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>
|
/// <summary>
|
||||||
/// Plays a clientside pickup animation for the specified uid.
|
/// Plays a clientside pickup animation for the specified uid.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
40
Content.Shared/Storage/ItemStorageLocation.cs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Storage;
|
||||||
|
|
||||||
|
[DataDefinition, Serializable, NetSerializable]
|
||||||
|
public partial record struct ItemStorageLocation
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The rotation, stored a cardinal direction in order to reduce rounding errors.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
private Direction _rotation;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The rotation of the piece in storage.
|
||||||
|
/// </summary>
|
||||||
|
public Angle Rotation
|
||||||
|
{
|
||||||
|
get => _rotation.ToAngle();
|
||||||
|
set => _rotation = value.GetCardinalDir();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Where the item is located in storage.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public Vector2i Position;
|
||||||
|
|
||||||
|
public ItemStorageLocation(Angle rotation, Vector2i position)
|
||||||
|
{
|
||||||
|
Rotation = rotation;
|
||||||
|
Position = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(ItemStorageLocation? other)
|
||||||
|
{
|
||||||
|
return Rotation == other?.Rotation &&
|
||||||
|
Position == other.Value.Position;
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -5,6 +5,7 @@ using Robust.Shared.Audio;
|
|||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Player;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
@@ -26,11 +27,16 @@ namespace Content.Shared.Storage
|
|||||||
public Container Container = default!;
|
public Container Container = default!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A limit for the cumulative ItemSize weights that can be inserted in this storage.
|
/// A dictionary storing each entity to its position within the storage grid.
|
||||||
/// If MaxSlots is not null, then this is ignored.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
|
[DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
|
||||||
public int MaxTotalWeight;
|
public Dictionary<NetEntity, ItemStorageLocation> StoredItems = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A list of boxes that comprise a combined grid that determines the location that items can be stored.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
|
||||||
|
public List<Box2i> Grid = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The maximum size item that can be inserted into this storage,
|
/// The maximum size item that can be inserted into this storage,
|
||||||
@@ -39,12 +45,6 @@ namespace Content.Shared.Storage
|
|||||||
[Access(typeof(SharedStorageSystem))]
|
[Access(typeof(SharedStorageSystem))]
|
||||||
public ProtoId<ItemSizePrototype>? MaxItemSize;
|
public ProtoId<ItemSizePrototype>? MaxItemSize;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The max number of entities that can be inserted into this storage.
|
|
||||||
/// </summary>
|
|
||||||
[DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
|
|
||||||
public int? MaxSlots;
|
|
||||||
|
|
||||||
// TODO: Make area insert its own component.
|
// TODO: Make area insert its own component.
|
||||||
[DataField("quickInsert")]
|
[DataField("quickInsert")]
|
||||||
public bool QuickInsert; // Can insert storables by "attacking" them with the storage entity
|
public bool QuickInsert; // Can insert storables by "attacking" them with the storage entity
|
||||||
@@ -94,11 +94,6 @@ namespace Content.Shared.Storage
|
|||||||
[DataField("storageCloseSound")]
|
[DataField("storageCloseSound")]
|
||||||
public SoundSpecifier? StorageCloseSound;
|
public SoundSpecifier? StorageCloseSound;
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public sealed class StorageInsertItemMessage : BoundUserInterfaceMessage
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public enum StorageUiKey
|
public enum StorageUiKey
|
||||||
{
|
{
|
||||||
@@ -107,15 +102,54 @@ namespace Content.Shared.Storage
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public sealed class StorageInteractWithItemEvent : BoundUserInterfaceMessage
|
public sealed class StorageInteractWithItemEvent : EntityEventArgs
|
||||||
{
|
{
|
||||||
public readonly NetEntity InteractedItemUID;
|
public readonly NetEntity InteractedItemUid;
|
||||||
public StorageInteractWithItemEvent(NetEntity interactedItemUID)
|
|
||||||
|
public readonly NetEntity StorageUid;
|
||||||
|
|
||||||
|
public StorageInteractWithItemEvent(NetEntity interactedItemUid, NetEntity storageUid)
|
||||||
{
|
{
|
||||||
InteractedItemUID = interactedItemUID;
|
InteractedItemUid = interactedItemUid;
|
||||||
|
StorageUid = storageUid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class StorageSetItemLocationEvent : EntityEventArgs
|
||||||
|
{
|
||||||
|
public readonly NetEntity ItemEnt;
|
||||||
|
|
||||||
|
public readonly NetEntity StorageEnt;
|
||||||
|
|
||||||
|
public readonly ItemStorageLocation Location;
|
||||||
|
|
||||||
|
public StorageSetItemLocationEvent(NetEntity itemEnt, NetEntity storageEnt, ItemStorageLocation location)
|
||||||
|
{
|
||||||
|
ItemEnt = itemEnt;
|
||||||
|
StorageEnt = storageEnt;
|
||||||
|
Location = location;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class StorageInsertItemIntoLocationEvent : EntityEventArgs
|
||||||
|
{
|
||||||
|
public readonly NetEntity ItemEnt;
|
||||||
|
|
||||||
|
public readonly NetEntity StorageEnt;
|
||||||
|
|
||||||
|
public readonly ItemStorageLocation Location;
|
||||||
|
|
||||||
|
public StorageInsertItemIntoLocationEvent(NetEntity itemEnt, NetEntity storageEnt, ItemStorageLocation location)
|
||||||
|
{
|
||||||
|
ItemEnt = itemEnt;
|
||||||
|
StorageEnt = storageEnt;
|
||||||
|
Location = location;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Network event for displaying an animation of entities flying into a storage entity
|
/// Network event for displaying an animation of entities flying into a storage entity
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
76
Content.Shared/Storage/StorageHelpers.cs
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
namespace Content.Shared.Storage;
|
||||||
|
|
||||||
|
public static class StorageHelper
|
||||||
|
{
|
||||||
|
public static Box2i GetBoundingBox(this IReadOnlyList<Box2i> boxes)
|
||||||
|
{
|
||||||
|
if (boxes.Count == 0)
|
||||||
|
return new Box2i();
|
||||||
|
|
||||||
|
var firstBox = boxes[0];
|
||||||
|
|
||||||
|
if (boxes.Count == 1)
|
||||||
|
return firstBox;
|
||||||
|
|
||||||
|
var bottom = firstBox.Bottom;
|
||||||
|
var left = firstBox.Left;
|
||||||
|
var top = firstBox.Top;
|
||||||
|
var right = firstBox.Right;
|
||||||
|
|
||||||
|
for (var i = 1; i < boxes.Count; i++)
|
||||||
|
{
|
||||||
|
var box = boxes[i];
|
||||||
|
|
||||||
|
if (bottom > box.Bottom)
|
||||||
|
bottom = box.Bottom;
|
||||||
|
|
||||||
|
if (left > box.Left)
|
||||||
|
left = box.Left;
|
||||||
|
|
||||||
|
if (top < box.Top)
|
||||||
|
top = box.Top;
|
||||||
|
|
||||||
|
if (right < box.Right)
|
||||||
|
right = box.Right;
|
||||||
|
}
|
||||||
|
return new Box2i(left, bottom, right, top);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetArea(this IReadOnlyList<Box2i> boxes)
|
||||||
|
{
|
||||||
|
var area = 0;
|
||||||
|
var bounding = boxes.GetBoundingBox();
|
||||||
|
for (var y = bounding.Bottom; y <= bounding.Top; y++)
|
||||||
|
{
|
||||||
|
for (var x = bounding.Left; x <= bounding.Right; x++)
|
||||||
|
{
|
||||||
|
if (boxes.Contains(x, y))
|
||||||
|
area++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return area;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool Contains(this IReadOnlyList<Box2i> boxes, int x, int y)
|
||||||
|
{
|
||||||
|
foreach (var box in boxes)
|
||||||
|
{
|
||||||
|
if (box.Contains(x, y))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool Contains(this IReadOnlyList<Box2i> boxes, Vector2i point)
|
||||||
|
{
|
||||||
|
foreach (var box in boxes)
|
||||||
|
{
|
||||||
|
if (box.Contains(point))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -109,6 +109,9 @@ ui-options-function-alt-activate-item-in-world = Alternative activate item in wo
|
|||||||
ui-options-function-drop = Drop item
|
ui-options-function-drop = Drop item
|
||||||
ui-options-function-examine-entity = Examine
|
ui-options-function-examine-entity = Examine
|
||||||
ui-options-function-swap-hands = Swap hands
|
ui-options-function-swap-hands = Swap hands
|
||||||
|
ui-options-function-move-stored-item = Move stored item
|
||||||
|
ui-options-function-rotate-stored-item = Rotate stored item
|
||||||
|
ui-options-static-storage-ui = Static storage UI
|
||||||
|
|
||||||
ui-options-function-smart-equip-backpack = Smart-equip to backpack
|
ui-options-function-smart-equip-backpack = Smart-equip to backpack
|
||||||
ui-options-function-smart-equip-belt = Smart-equip to belt
|
ui-options-function-smart-equip-belt = Smart-equip to belt
|
||||||
|
|||||||
@@ -318,7 +318,8 @@
|
|||||||
description: Holds the kit of CentComm's most feared agents.
|
description: Holds the kit of CentComm's most feared agents.
|
||||||
components:
|
components:
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxTotalWeight: 56
|
grid:
|
||||||
|
- 0,0,7,6
|
||||||
- type: StorageFill
|
- type: StorageFill
|
||||||
contents:
|
contents:
|
||||||
- id: BoxSurvivalEngineering
|
- id: BoxSurvivalEngineering
|
||||||
|
|||||||
@@ -234,9 +234,9 @@
|
|||||||
- type: StorageFill
|
- type: StorageFill
|
||||||
contents:
|
contents:
|
||||||
- id: BoxSurvival
|
- id: BoxSurvival
|
||||||
|
- id: BoxForensicPad
|
||||||
- id: Lighter
|
- id: Lighter
|
||||||
- id: CigPackBlack
|
- id: CigPackBlack
|
||||||
- id: BoxForensicPad
|
|
||||||
- id: HandLabeler
|
- id: HandLabeler
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
|
|||||||
@@ -153,13 +153,13 @@
|
|||||||
tags: [] # ignore "WhitelistChameleon" tag
|
tags: [] # ignore "WhitelistChameleon" tag
|
||||||
- type: StorageFill
|
- type: StorageFill
|
||||||
contents:
|
contents:
|
||||||
|
- id: ClothingOuterArmorBasic
|
||||||
- id: ClothingHeadHatCentcom
|
- id: ClothingHeadHatCentcom
|
||||||
- id: ClothingEyesGlassesSunglasses
|
- id: ClothingEyesGlassesSunglasses
|
||||||
- id: ClothingUniformJumpsuitCentcomOfficial
|
- id: ClothingUniformJumpsuitCentcomOfficial
|
||||||
- id: ClothingShoesBootsJack
|
- id: ClothingShoesBootsJack
|
||||||
- id: ClothingHandsGlovesColorBlack
|
- id: ClothingHandsGlovesColorBlack
|
||||||
- id: ClothingHeadsetAltCentComFake
|
- id: ClothingHeadsetAltCentComFake
|
||||||
- id: ClothingOuterArmorBasic
|
|
||||||
- id: Paper
|
- id: Paper
|
||||||
- id: Pen
|
- id: Pen
|
||||||
- id: CentcomPDAFake
|
- id: CentcomPDAFake
|
||||||
@@ -198,8 +198,6 @@
|
|||||||
name: syndicate pyjama duffel bag
|
name: syndicate pyjama duffel bag
|
||||||
description: Contains 3 pairs of syndicate pyjamas and 3 plushies for the ultimate sleepover.
|
description: Contains 3 pairs of syndicate pyjamas and 3 plushies for the ultimate sleepover.
|
||||||
components:
|
components:
|
||||||
- type: Storage
|
|
||||||
maxTotalWeight: 44
|
|
||||||
- type: StorageFill
|
- type: StorageFill
|
||||||
contents:
|
contents:
|
||||||
- id: ClothingUniformJumpsuitPyjamaSyndicateRed
|
- id: ClothingUniformJumpsuitPyjamaSyndicateRed
|
||||||
|
|||||||
@@ -37,7 +37,8 @@
|
|||||||
- state: box
|
- state: box
|
||||||
- state: light
|
- state: light
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxTotalWeight: 24
|
grid:
|
||||||
|
- 0,0,5,3
|
||||||
whitelist:
|
whitelist:
|
||||||
components:
|
components:
|
||||||
- LightBulb
|
- LightBulb
|
||||||
@@ -60,7 +61,8 @@
|
|||||||
- state: box
|
- state: box
|
||||||
- state: lighttube
|
- state: lighttube
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxTotalWeight: 24
|
grid:
|
||||||
|
- 0,0,5,3
|
||||||
whitelist:
|
whitelist:
|
||||||
components:
|
components:
|
||||||
- LightBulb
|
- LightBulb
|
||||||
@@ -85,7 +87,8 @@
|
|||||||
- state: box
|
- state: box
|
||||||
- state: lightmixed
|
- state: lightmixed
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxTotalWeight: 24
|
grid:
|
||||||
|
- 0,0,5,3
|
||||||
whitelist:
|
whitelist:
|
||||||
components:
|
components:
|
||||||
- LightBulb
|
- LightBulb
|
||||||
@@ -250,7 +253,8 @@
|
|||||||
- id: TrashBag
|
- id: TrashBag
|
||||||
amount: 6
|
amount: 6
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxTotalWeight: 24
|
grid:
|
||||||
|
- 0,0,5,3
|
||||||
maxItemSize: Normal
|
maxItemSize: Normal
|
||||||
whitelist:
|
whitelist:
|
||||||
tags:
|
tags:
|
||||||
@@ -391,7 +395,8 @@
|
|||||||
- id: HandheldGPSBasic
|
- id: HandheldGPSBasic
|
||||||
amount: 6
|
amount: 6
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxTotalWeight: 24
|
grid:
|
||||||
|
- 0,0,5,3
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
layers:
|
layers:
|
||||||
- state: box
|
- state: box
|
||||||
@@ -419,7 +424,8 @@
|
|||||||
- state: box
|
- state: box
|
||||||
- state: candle
|
- state: candle
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxTotalWeight: 30
|
grid:
|
||||||
|
- 0,0,9,2
|
||||||
- type: StorageFill
|
- type: StorageFill
|
||||||
contents:
|
contents:
|
||||||
- id: Candle
|
- id: Candle
|
||||||
@@ -443,7 +449,8 @@
|
|||||||
- state: box
|
- state: box
|
||||||
- state: candle
|
- state: candle
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxTotalWeight: 30
|
grid:
|
||||||
|
- 0,0,9,2
|
||||||
- type: StorageFill
|
- type: StorageFill
|
||||||
contents:
|
contents:
|
||||||
- id: CandleSmall
|
- id: CandleSmall
|
||||||
@@ -476,7 +483,8 @@
|
|||||||
- id: DartYellow
|
- id: DartYellow
|
||||||
amount: 3
|
amount: 3
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxTotalWeight: 24
|
grid:
|
||||||
|
- 0,0,5,3
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
layers:
|
layers:
|
||||||
- state: box
|
- state: box
|
||||||
|
|||||||
@@ -94,7 +94,8 @@
|
|||||||
id: BoxMouthSwab
|
id: BoxMouthSwab
|
||||||
components:
|
components:
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxTotalWeight: 20
|
grid:
|
||||||
|
- 0,0,4,3
|
||||||
- type: StorageFill
|
- type: StorageFill
|
||||||
contents:
|
contents:
|
||||||
- id: DiseaseSwab
|
- id: DiseaseSwab
|
||||||
|
|||||||
@@ -5,14 +5,15 @@
|
|||||||
description: A box full of beakers.
|
description: A box full of beakers.
|
||||||
components:
|
components:
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxTotalWeight: 12
|
grid:
|
||||||
|
- 0,0,3,2
|
||||||
maxItemSize: Normal
|
maxItemSize: Normal
|
||||||
- type: StorageFill
|
- type: StorageFill
|
||||||
contents:
|
contents:
|
||||||
- id: Beaker
|
- id: LargeBeaker
|
||||||
amount: 2
|
amount: 2
|
||||||
- id: LargeBeaker
|
- id: Beaker
|
||||||
amount: 2
|
amount: 2
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
layers:
|
layers:
|
||||||
- state: box
|
- state: box
|
||||||
|
|||||||
@@ -50,7 +50,8 @@
|
|||||||
description: A box full of zipties.
|
description: A box full of zipties.
|
||||||
components:
|
components:
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxTotalWeight: 20
|
grid:
|
||||||
|
- 0,0,4,3
|
||||||
whitelist:
|
whitelist:
|
||||||
components:
|
components:
|
||||||
- Handcuff
|
- Handcuff
|
||||||
@@ -70,7 +71,8 @@
|
|||||||
description: A box of forensic pads.
|
description: A box of forensic pads.
|
||||||
components:
|
components:
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxTotalWeight: 20
|
grid:
|
||||||
|
- 0,0,4,3
|
||||||
- type: StorageFill
|
- type: StorageFill
|
||||||
contents:
|
contents:
|
||||||
- id: ForensicPad
|
- id: ForensicPad
|
||||||
|
|||||||
@@ -126,8 +126,9 @@
|
|||||||
- type: Item
|
- type: Item
|
||||||
size: Ginormous
|
size: Ginormous
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 8
|
|
||||||
maxItemSize: Normal
|
maxItemSize: Normal
|
||||||
|
grid:
|
||||||
|
- 0,0,7,1
|
||||||
- type: StorageFill
|
- type: StorageFill
|
||||||
contents:
|
contents:
|
||||||
- id: ExGrenade
|
- id: ExGrenade
|
||||||
|
|||||||
@@ -19,6 +19,8 @@
|
|||||||
size: Ginormous
|
size: Ginormous
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxItemSize: Huge
|
maxItemSize: Huge
|
||||||
|
grid:
|
||||||
|
- 0,0,6,3
|
||||||
- type: StorageFill
|
- type: StorageFill
|
||||||
contents:
|
contents:
|
||||||
- id: WeaponSniperHristov
|
- id: WeaponSniperHristov
|
||||||
|
|||||||
@@ -14,8 +14,9 @@
|
|||||||
slots:
|
slots:
|
||||||
- back
|
- back
|
||||||
- type: Storage
|
- type: Storage
|
||||||
|
grid:
|
||||||
|
- 0,0,6,3
|
||||||
maxItemSize: Huge
|
maxItemSize: Huge
|
||||||
maxTotalWeight: 28
|
|
||||||
- type: ContainerContainer
|
- type: ContainerContainer
|
||||||
containers:
|
containers:
|
||||||
storagebase: !type:Container
|
storagebase: !type:Container
|
||||||
@@ -197,7 +198,8 @@
|
|||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Clothing/Back/Backpacks/ertleader.rsi
|
sprite: Clothing/Back/Backpacks/ertleader.rsi
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxTotalWeight: 44
|
grid:
|
||||||
|
- 0,0,10,3
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: ClothingBackpackERTLeader
|
parent: ClothingBackpackERTLeader
|
||||||
@@ -264,7 +266,8 @@
|
|||||||
size: Ginormous
|
size: Ginormous
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxItemSize: Huge
|
maxItemSize: Huge
|
||||||
maxTotalWeight: 200 #5 duffels worth of gear
|
grid:
|
||||||
|
- 0,0,19,9
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: ClothingBackpackClown
|
parent: ClothingBackpackClown
|
||||||
@@ -278,3 +281,62 @@
|
|||||||
- type: Unremoveable
|
- type: Unremoveable
|
||||||
deleteOnDrop: false
|
deleteOnDrop: false
|
||||||
|
|
||||||
|
# Debug
|
||||||
|
- type: entity
|
||||||
|
parent: ClothingBackpack
|
||||||
|
id: ClothingBackpackDebug
|
||||||
|
name: wackpack
|
||||||
|
description: What the fuck is this?
|
||||||
|
suffix: Debug
|
||||||
|
components:
|
||||||
|
- type: Storage
|
||||||
|
grid:
|
||||||
|
- 0,0,3,3
|
||||||
|
- 5,0,7,2
|
||||||
|
- 0,5,7,5
|
||||||
|
- 6,4,7,5
|
||||||
|
- 9,2,10,3
|
||||||
|
- 9,5,9,5
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
parent: ClothingBackpack
|
||||||
|
id: ClothingBackpackDebug2
|
||||||
|
name: big wackpack
|
||||||
|
description: What the fuck is this?
|
||||||
|
suffix: Debug
|
||||||
|
components:
|
||||||
|
- type: Storage
|
||||||
|
grid:
|
||||||
|
- 0,0,39,24
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
parent: ClothingBackpack
|
||||||
|
id: ClothingBackpackDebug3
|
||||||
|
name: gay wackpack
|
||||||
|
description: What the fuck is this?
|
||||||
|
suffix: Debug
|
||||||
|
components:
|
||||||
|
- type: Storage
|
||||||
|
grid:
|
||||||
|
- 0,0,0,3
|
||||||
|
- 0,0,2,0
|
||||||
|
- 0,3,2,3
|
||||||
|
- 2,1,2,1
|
||||||
|
- 4,0,4,2
|
||||||
|
- 6,0,6,2
|
||||||
|
- 5,1,5,1
|
||||||
|
- 5,3,5,3
|
||||||
|
- 9,0,9,1
|
||||||
|
- 8,2,8,3
|
||||||
|
- 10,2,10,3
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
parent: ClothingBackpack
|
||||||
|
id: ClothingBackpackDebug4
|
||||||
|
name: offset wackpack
|
||||||
|
description: What the fuck is this?
|
||||||
|
suffix: Debug
|
||||||
|
components:
|
||||||
|
- type: Storage
|
||||||
|
grid:
|
||||||
|
- 5,5,11,8
|
||||||
|
|||||||
@@ -8,7 +8,8 @@
|
|||||||
sprite: Clothing/Back/Duffels/duffel.rsi
|
sprite: Clothing/Back/Duffels/duffel.rsi
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxItemSize: Huge
|
maxItemSize: Huge
|
||||||
maxTotalWeight: 40
|
grid:
|
||||||
|
- 0,0,9,4
|
||||||
- type: ClothingSpeedModifier
|
- type: ClothingSpeedModifier
|
||||||
walkModifier: 1
|
walkModifier: 1
|
||||||
sprintModifier: 0.9
|
sprintModifier: 0.9
|
||||||
@@ -231,7 +232,8 @@
|
|||||||
size: Ginormous
|
size: Ginormous
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxItemSize: Huge
|
maxItemSize: Huge
|
||||||
maxTotalWeight: 200 #5 duffels worth of gear
|
grid:
|
||||||
|
- 0,0,19,9
|
||||||
- type: ClothingSpeedModifier
|
- type: ClothingSpeedModifier
|
||||||
sprintModifier: 1 # makes its stats identical to other variants of bag of holding
|
sprintModifier: 1 # makes its stats identical to other variants of bag of holding
|
||||||
|
|
||||||
@@ -243,7 +245,6 @@
|
|||||||
components:
|
components:
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxItemSize: Huge
|
maxItemSize: Huge
|
||||||
maxTotalWeight: 40
|
|
||||||
- type: ClothingSpeedModifier
|
- type: ClothingSpeedModifier
|
||||||
walkModifier: 1
|
walkModifier: 1
|
||||||
sprintModifier: 1
|
sprintModifier: 1
|
||||||
|
|||||||
@@ -171,4 +171,5 @@
|
|||||||
size: Ginormous
|
size: Ginormous
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxItemSize: Huge
|
maxItemSize: Huge
|
||||||
maxTotalWeight: 200 #5 duffels worth of gear
|
grid:
|
||||||
|
- 0,0,19,9
|
||||||
|
|||||||
@@ -22,8 +22,9 @@
|
|||||||
id: ClothingBeltStorageBase
|
id: ClothingBeltStorageBase
|
||||||
components:
|
components:
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 7
|
|
||||||
maxItemSize: Normal
|
maxItemSize: Normal
|
||||||
|
grid:
|
||||||
|
- 0,0,7,1
|
||||||
- type: Item
|
- type: Item
|
||||||
size: Ginormous
|
size: Ginormous
|
||||||
- type: ContainerContainer
|
- type: ContainerContainer
|
||||||
|
|||||||
@@ -391,7 +391,8 @@
|
|||||||
- type: Clothing
|
- type: Clothing
|
||||||
sprite: Clothing/Belt/sheath.rsi
|
sprite: Clothing/Belt/sheath.rsi
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 1
|
grid:
|
||||||
|
- 0,0,1,1
|
||||||
whitelist:
|
whitelist:
|
||||||
tags:
|
tags:
|
||||||
- CaptainSabre
|
- CaptainSabre
|
||||||
@@ -449,7 +450,8 @@
|
|||||||
- type: Clothing
|
- type: Clothing
|
||||||
sprite: Clothing/Belt/holster.rsi
|
sprite: Clothing/Belt/holster.rsi
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 3
|
grid:
|
||||||
|
- 0,0,3,1
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: ClothingBeltStorageBase
|
parent: ClothingBeltStorageBase
|
||||||
@@ -554,7 +556,8 @@
|
|||||||
- type: Clothing
|
- type: Clothing
|
||||||
sprite: Clothing/Belt/wand.rsi
|
sprite: Clothing/Belt/wand.rsi
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 8
|
grid:
|
||||||
|
- 0,0,15,1
|
||||||
whitelist:
|
whitelist:
|
||||||
tags:
|
tags:
|
||||||
- WizardWand
|
- WizardWand
|
||||||
|
|||||||
@@ -12,7 +12,8 @@
|
|||||||
visible: false
|
visible: false
|
||||||
- type: Clothing
|
- type: Clothing
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 15
|
grid:
|
||||||
|
- 0,0,7,3
|
||||||
maxItemSize: Small
|
maxItemSize: Small
|
||||||
whitelist:
|
whitelist:
|
||||||
tags:
|
tags:
|
||||||
|
|||||||
@@ -9,8 +9,8 @@
|
|||||||
- type: Clothing
|
- type: Clothing
|
||||||
sprite: Clothing/Belt/waistbag_leather.rsi
|
sprite: Clothing/Belt/waistbag_leather.rsi
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 4
|
grid:
|
||||||
maxItemSize: Small
|
- 0,0,3,1
|
||||||
|
|
||||||
#Colorization on worn items doesn't work. If this ever gets fixed, you can duplicate this entry and change the color on the sprite to add color variants.
|
#Colorization on worn items doesn't work. If this ever gets fixed, you can duplicate this entry and change the color on the sprite to add color variants.
|
||||||
#- type: entity
|
#- type: entity
|
||||||
|
|||||||
@@ -208,7 +208,8 @@
|
|||||||
- type: Clothing
|
- type: Clothing
|
||||||
sprite: Clothing/Head/Hats/chefhat.rsi
|
sprite: Clothing/Head/Hats/chefhat.rsi
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 1
|
grid:
|
||||||
|
- 0,0,0,0
|
||||||
- type: UserInterface
|
- type: UserInterface
|
||||||
interfaces:
|
interfaces:
|
||||||
- key: enum.StorageUiKey.Key
|
- key: enum.StorageUiKey.Key
|
||||||
@@ -765,7 +766,8 @@
|
|||||||
size: Small
|
size: Small
|
||||||
sprite: Clothing/Head/Hats/magician.rsi
|
sprite: Clothing/Head/Hats/magician.rsi
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 1
|
grid:
|
||||||
|
- 0,0,0,0
|
||||||
- type: UserInterface
|
- type: UserInterface
|
||||||
interfaces:
|
interfaces:
|
||||||
- key: enum.StorageUiKey.Key
|
- key: enum.StorageUiKey.Key
|
||||||
|
|||||||
@@ -29,7 +29,8 @@
|
|||||||
id: ClothingOuterStorageBase
|
id: ClothingOuterStorageBase
|
||||||
components:
|
components:
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxTotalWeight: 6
|
grid:
|
||||||
|
- 0,0,2,1
|
||||||
- type: ContainerContainer
|
- type: ContainerContainer
|
||||||
containers:
|
containers:
|
||||||
storagebase: !type:Container
|
storagebase: !type:Container
|
||||||
|
|||||||
@@ -41,7 +41,8 @@
|
|||||||
id: ClothingShoesStorageBase
|
id: ClothingShoesStorageBase
|
||||||
components:
|
components:
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 1
|
grid:
|
||||||
|
- 0,0,1,1
|
||||||
maxItemSize: Normal
|
maxItemSize: Normal
|
||||||
- type: ContainerContainer
|
- type: ContainerContainer
|
||||||
containers:
|
containers:
|
||||||
|
|||||||
21
Resources/Prototypes/Entities/Debugging/item.yml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
- type: entity
|
||||||
|
parent: BaseItem
|
||||||
|
id: DebugItemShapeWeird
|
||||||
|
name: weirdly shaped item
|
||||||
|
description: What is it...?
|
||||||
|
suffix: DEBUG
|
||||||
|
components:
|
||||||
|
- type: Tag
|
||||||
|
tags:
|
||||||
|
- Debug
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Objects/Misc/skub.rsi
|
||||||
|
state: icon
|
||||||
|
- type: Item
|
||||||
|
size: Tiny
|
||||||
|
shape:
|
||||||
|
- 0, 0, 2, 2
|
||||||
|
- 1, 1, 1, 4
|
||||||
|
- 1, 4, 6, 4
|
||||||
|
- 6, 2, 6, 2
|
||||||
|
- 5, 3, 5, 5
|
||||||
@@ -165,7 +165,8 @@
|
|||||||
node: bot
|
node: bot
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxItemSize: Huge
|
maxItemSize: Huge
|
||||||
maxTotalWeight: 40
|
grid:
|
||||||
|
- 0,0,9,3
|
||||||
- type: Access
|
- type: Access
|
||||||
groups:
|
groups:
|
||||||
- Cargo
|
- Cargo
|
||||||
|
|||||||
@@ -403,7 +403,8 @@
|
|||||||
- type: Item
|
- type: Item
|
||||||
size: Normal
|
size: Normal
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 6
|
grid:
|
||||||
|
- 0,0,5,1
|
||||||
whitelist:
|
whitelist:
|
||||||
tags:
|
tags:
|
||||||
- Cola
|
- Cola
|
||||||
|
|||||||
@@ -38,7 +38,8 @@
|
|||||||
map: ["pink-box6"]
|
map: ["pink-box6"]
|
||||||
visible: false
|
visible: false
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 6
|
grid:
|
||||||
|
- 0,0,2,1
|
||||||
whitelist:
|
whitelist:
|
||||||
tags:
|
tags:
|
||||||
- Donut
|
- Donut
|
||||||
@@ -118,7 +119,8 @@
|
|||||||
map: ["box12"]
|
map: ["box12"]
|
||||||
visible: false
|
visible: false
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 12
|
grid:
|
||||||
|
- 0,0,5,1
|
||||||
whitelist:
|
whitelist:
|
||||||
tags:
|
tags:
|
||||||
- Egg
|
- Egg
|
||||||
@@ -206,11 +208,9 @@
|
|||||||
map: ["enum.StorageVisualLayers.Door"]
|
map: ["enum.StorageVisualLayers.Door"]
|
||||||
# TODO make these entitystorage again + placeablesurface after entity storage ECS gets merged.
|
# TODO make these entitystorage again + placeablesurface after entity storage ECS gets merged.
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 1
|
grid:
|
||||||
|
- 0,0,1,1
|
||||||
maxItemSize: Normal
|
maxItemSize: Normal
|
||||||
whitelist:
|
|
||||||
tags:
|
|
||||||
- Pizza
|
|
||||||
- type: Item
|
- type: Item
|
||||||
sprite: Objects/Consumable/Food/Baked/pizza.rsi
|
sprite: Objects/Consumable/Food/Baked/pizza.rsi
|
||||||
heldPrefix: box
|
heldPrefix: box
|
||||||
@@ -297,7 +297,8 @@
|
|||||||
map: ["box6"]
|
map: ["box6"]
|
||||||
visible: false
|
visible: false
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 6
|
grid:
|
||||||
|
- 0,0,2,1
|
||||||
- type: Item
|
- type: Item
|
||||||
sprite: Objects/Consumable/Food/Baked/nuggets.rsi
|
sprite: Objects/Consumable/Food/Baked/nuggets.rsi
|
||||||
size: Small
|
size: Small
|
||||||
@@ -331,7 +332,8 @@
|
|||||||
sprite: Objects/Consumable/Food/Baked/donkpocket.rsi
|
sprite: Objects/Consumable/Food/Baked/donkpocket.rsi
|
||||||
state: box
|
state: box
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxTotalWeight: 12
|
grid:
|
||||||
|
- 0,0,3,2
|
||||||
whitelist:
|
whitelist:
|
||||||
tags:
|
tags:
|
||||||
- DonkPocket
|
- DonkPocket
|
||||||
@@ -459,6 +461,9 @@
|
|||||||
- type: Item
|
- type: Item
|
||||||
sprite: Objects/Storage/Happyhonk/clown.rsi
|
sprite: Objects/Storage/Happyhonk/clown.rsi
|
||||||
heldPrefix: box
|
heldPrefix: box
|
||||||
|
- type: Storage
|
||||||
|
grid:
|
||||||
|
- 0,0,3,3
|
||||||
- type: Tag
|
- type: Tag
|
||||||
tags:
|
tags:
|
||||||
- Trash
|
- Trash
|
||||||
@@ -583,7 +588,8 @@
|
|||||||
name: syndicate snack box
|
name: syndicate snack box
|
||||||
components:
|
components:
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 9
|
grid:
|
||||||
|
- 0,0,5,2
|
||||||
- type: StorageFill
|
- type: StorageFill
|
||||||
contents:
|
contents:
|
||||||
# toy
|
# toy
|
||||||
|
|||||||
@@ -14,7 +14,8 @@
|
|||||||
sprite: Objects/Consumable/Smokeables/Cigarettes/Cartons/green.rsi
|
sprite: Objects/Consumable/Smokeables/Cigarettes/Cartons/green.rsi
|
||||||
size: Normal
|
size: Normal
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 6
|
grid:
|
||||||
|
- 0,0,5,1
|
||||||
- type: StorageFill
|
- type: StorageFill
|
||||||
contents:
|
contents:
|
||||||
- id: CigPackGreen
|
- id: CigPackGreen
|
||||||
|
|||||||
@@ -43,7 +43,8 @@
|
|||||||
Steel: 50
|
Steel: 50
|
||||||
- type: SpaceGarbage
|
- type: SpaceGarbage
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 5
|
grid:
|
||||||
|
- 0,0,4,1
|
||||||
- type: Item
|
- type: Item
|
||||||
size: Small
|
size: Small
|
||||||
- type: StorageFill
|
- type: StorageFill
|
||||||
@@ -108,8 +109,8 @@
|
|||||||
Steel: 50
|
Steel: 50
|
||||||
- type: SpaceGarbage
|
- type: SpaceGarbage
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 10
|
grid:
|
||||||
maxTotalWeight: 20
|
- 0,0,4,1
|
||||||
- type: Item
|
- type: Item
|
||||||
size: Small
|
size: Small
|
||||||
- type: StorageFill
|
- type: StorageFill
|
||||||
|
|||||||
@@ -5,11 +5,12 @@
|
|||||||
description: A pack of thin pieces of paper used to make fine smokeables.
|
description: A pack of thin pieces of paper used to make fine smokeables.
|
||||||
components:
|
components:
|
||||||
- type: Storage
|
- type: Storage
|
||||||
|
grid:
|
||||||
|
- 0,0,3,1
|
||||||
whitelist:
|
whitelist:
|
||||||
tags:
|
tags:
|
||||||
- RollingPaper
|
- RollingPaper
|
||||||
- CigFilter
|
- CigFilter
|
||||||
maxSlots: 20
|
|
||||||
- type: StorageFill
|
- type: StorageFill
|
||||||
contents:
|
contents:
|
||||||
- id: PaperRolling
|
- id: PaperRolling
|
||||||
|
|||||||
@@ -35,7 +35,8 @@
|
|||||||
map: ["cigar8"]
|
map: ["cigar8"]
|
||||||
visible: false
|
visible: false
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 8
|
grid:
|
||||||
|
- 0,0,3,1
|
||||||
- type: Item
|
- type: Item
|
||||||
sprite: Objects/Consumable/Smokeables/Cigars/case.rsi
|
sprite: Objects/Consumable/Smokeables/Cigars/case.rsi
|
||||||
size: Small
|
size: Small
|
||||||
|
|||||||
@@ -23,7 +23,8 @@
|
|||||||
False: {state: empty_icon}
|
False: {state: empty_icon}
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxItemSize: Small
|
maxItemSize: Small
|
||||||
maxTotalWeight: 10
|
grid:
|
||||||
|
- 0,0,3,2
|
||||||
whitelist:
|
whitelist:
|
||||||
components:
|
components:
|
||||||
- Pill
|
- Pill
|
||||||
|
|||||||
@@ -235,7 +235,8 @@
|
|||||||
sprite: Objects/Fun/crayons.rsi
|
sprite: Objects/Fun/crayons.rsi
|
||||||
state: box
|
state: box
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 7
|
grid:
|
||||||
|
- 0,0,6,0
|
||||||
maxItemSize: Tiny
|
maxItemSize: Tiny
|
||||||
- type: Item
|
- type: Item
|
||||||
sprite: Objects/Fun/crayons.rsi
|
sprite: Objects/Fun/crayons.rsi
|
||||||
|
|||||||
@@ -19,7 +19,8 @@
|
|||||||
- type: Item
|
- type: Item
|
||||||
size: Small
|
size: Small
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxTotalWeight: 8
|
grid:
|
||||||
|
- 0,0,3,1
|
||||||
whitelist:
|
whitelist:
|
||||||
tags:
|
tags:
|
||||||
- Dice
|
- Dice
|
||||||
@@ -33,4 +34,5 @@
|
|||||||
sprite: Objects/Fun/dice.rsi
|
sprite: Objects/Fun/dice.rsi
|
||||||
state: magicdicebag
|
state: magicdicebag
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxTotalWeight: 20
|
grid:
|
||||||
|
- 0,0,4,3
|
||||||
|
|||||||
@@ -8,9 +8,12 @@
|
|||||||
- type: Item
|
- type: Item
|
||||||
sprite: Objects/Storage/boxes.rsi
|
sprite: Objects/Storage/boxes.rsi
|
||||||
size: Large
|
size: Large
|
||||||
|
shape:
|
||||||
|
- 0,0,2,2
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxTotalWeight: 8
|
|
||||||
maxItemSize: Small
|
maxItemSize: Small
|
||||||
|
grid:
|
||||||
|
- 0,0,2,2
|
||||||
- type: ContainerContainer
|
- type: ContainerContainer
|
||||||
containers:
|
containers:
|
||||||
storagebase: !type:Container
|
storagebase: !type:Container
|
||||||
|
|||||||
@@ -7,7 +7,8 @@
|
|||||||
- type: Item
|
- type: Item
|
||||||
size: Ginormous
|
size: Ginormous
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxTotalWeight: 24
|
grid:
|
||||||
|
- 0,0,5,3
|
||||||
- type: Tag
|
- type: Tag
|
||||||
tags:
|
tags:
|
||||||
- Briefcase
|
- Briefcase
|
||||||
@@ -33,12 +34,6 @@
|
|||||||
components:
|
components:
|
||||||
- type: Item
|
- type: Item
|
||||||
size: Huge
|
size: Huge
|
||||||
- type: Storage
|
|
||||||
maxSlots: 6
|
|
||||||
maxTotalWeight: 24
|
|
||||||
- type: Tag
|
|
||||||
tags:
|
|
||||||
- Briefcase
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
name: brown briefcase
|
name: brown briefcase
|
||||||
|
|||||||
@@ -11,8 +11,8 @@
|
|||||||
sprite: Objects/Storage/medalcase.rsi
|
sprite: Objects/Storage/medalcase.rsi
|
||||||
size: Normal
|
size: Normal
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 8
|
grid:
|
||||||
maxTotalWeight: 16
|
- 0,0,7,1
|
||||||
- type: StorageFill
|
- type: StorageFill
|
||||||
contents:
|
contents:
|
||||||
- id: ClothingNeckGoldmedal
|
- id: ClothingNeckGoldmedal
|
||||||
|
|||||||
@@ -338,9 +338,9 @@
|
|||||||
sprite: Objects/Misc/bureaucracy.rsi
|
sprite: Objects/Misc/bureaucracy.rsi
|
||||||
size: Small
|
size: Small
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 10
|
|
||||||
maxItemSize: Small
|
maxItemSize: Small
|
||||||
maxTotalWeight: 20
|
grid:
|
||||||
|
- 0,0,4,3
|
||||||
whitelist:
|
whitelist:
|
||||||
tags:
|
tags:
|
||||||
- Document
|
- Document
|
||||||
@@ -476,8 +476,8 @@
|
|||||||
quickEquip: false
|
quickEquip: false
|
||||||
sprite: Objects/Misc/clipboard.rsi
|
sprite: Objects/Misc/clipboard.rsi
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 15
|
grid:
|
||||||
maxTotalWeight: 30
|
- 0,0,5,3
|
||||||
whitelist:
|
whitelist:
|
||||||
tags:
|
tags:
|
||||||
- Document
|
- Document
|
||||||
@@ -535,7 +535,8 @@
|
|||||||
quickEquip: false
|
quickEquip: false
|
||||||
sprite: Objects/Misc/qm_clipboard.rsi
|
sprite: Objects/Misc/qm_clipboard.rsi
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 20
|
grid:
|
||||||
|
- 0,0,4,3
|
||||||
quickInsert: true
|
quickInsert: true
|
||||||
whitelist:
|
whitelist:
|
||||||
tags:
|
tags:
|
||||||
|
|||||||
@@ -118,7 +118,8 @@
|
|||||||
components:
|
components:
|
||||||
- Hands # no use giving a mouse a storage implant, but a monkey is another story...
|
- Hands # no use giving a mouse a storage implant, but a monkey is another story...
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 4
|
grid:
|
||||||
|
- 0,0,2,2
|
||||||
maxItemSize: Small
|
maxItemSize: Small
|
||||||
- type: ContainerContainer
|
- type: ContainerContainer
|
||||||
containers:
|
containers:
|
||||||
|
|||||||
@@ -37,7 +37,8 @@
|
|||||||
slots:
|
slots:
|
||||||
- Belt
|
- Belt
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 1
|
grid:
|
||||||
|
- 0,0,0,1
|
||||||
- type: UserInterface
|
- type: UserInterface
|
||||||
interfaces:
|
interfaces:
|
||||||
- key: enum.StorageUiKey.Key
|
- key: enum.StorageUiKey.Key
|
||||||
|
|||||||
@@ -16,7 +16,8 @@
|
|||||||
size: Ginormous
|
size: Ginormous
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxItemSize: Normal # allow up to 5 large beakers / 10 beakers / 10 pill canisters
|
maxItemSize: Normal # allow up to 5 large beakers / 10 beakers / 10 pill canisters
|
||||||
maxTotalWeight: 20
|
grid:
|
||||||
|
- 0,0,4,3
|
||||||
quickInsert: true
|
quickInsert: true
|
||||||
areaInsert: true
|
areaInsert: true
|
||||||
whitelist:
|
whitelist:
|
||||||
|
|||||||
@@ -128,7 +128,8 @@
|
|||||||
slots:
|
slots:
|
||||||
- belt
|
- belt
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 40
|
grid:
|
||||||
|
- 0,0,7,4
|
||||||
maxItemSize: Small
|
maxItemSize: Small
|
||||||
quickInsert: true
|
quickInsert: true
|
||||||
areaInsert: true
|
areaInsert: true
|
||||||
|
|||||||
@@ -9,8 +9,9 @@
|
|||||||
- state: icon-0
|
- state: icon-0
|
||||||
map: ["enum.StorageFillLayers.Fill"]
|
map: ["enum.StorageFillLayers.Fill"]
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxTotalWeight: 48
|
|
||||||
maxItemSize: Small
|
maxItemSize: Small
|
||||||
|
grid:
|
||||||
|
- 0,0,7,5
|
||||||
quickInsert: true
|
quickInsert: true
|
||||||
areaInsert: true
|
areaInsert: true
|
||||||
storageOpenSound:
|
storageOpenSound:
|
||||||
@@ -56,9 +57,9 @@
|
|||||||
parent: TrashBagBlue
|
parent: TrashBagBlue
|
||||||
components:
|
components:
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 100
|
|
||||||
maxItemSize: Huge
|
maxItemSize: Huge
|
||||||
maxTotalWeight: 200
|
grid:
|
||||||
|
- 0,0,19,9
|
||||||
quickInsert: true
|
quickInsert: true
|
||||||
areaInsert: true
|
areaInsert: true
|
||||||
areaInsertRadius: 1000
|
areaInsertRadius: 1000
|
||||||
|
|||||||
@@ -48,7 +48,8 @@
|
|||||||
type: StorageBoundUserInterface
|
type: StorageBoundUserInterface
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxItemSize: Normal
|
maxItemSize: Normal
|
||||||
maxTotalWeight: 40
|
grid:
|
||||||
|
- 0,0,7,4
|
||||||
- type: TileFrictionModifier
|
- type: TileFrictionModifier
|
||||||
modifier: 0.4 # makes it slide
|
modifier: 0.4 # makes it slide
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,8 @@
|
|||||||
- type: Item
|
- type: Item
|
||||||
size: Ginormous
|
size: Ginormous
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 15
|
grid:
|
||||||
|
- 0,0,7,3
|
||||||
quickInsert: true
|
quickInsert: true
|
||||||
areaInsert: true
|
areaInsert: true
|
||||||
whitelist:
|
whitelist:
|
||||||
|
|||||||
@@ -8,8 +8,9 @@
|
|||||||
sprite: Objects/Specific/Medical/firstaidkits.rsi
|
sprite: Objects/Specific/Medical/firstaidkits.rsi
|
||||||
state: firstaid
|
state: firstaid
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxTotalWeight: 8
|
|
||||||
maxItemSize: Small
|
maxItemSize: Small
|
||||||
|
grid:
|
||||||
|
- 0,0,3,1
|
||||||
- type: Item
|
- type: Item
|
||||||
size: Large
|
size: Large
|
||||||
sprite: Objects/Specific/Medical/firstaidkits.rsi
|
sprite: Objects/Specific/Medical/firstaidkits.rsi
|
||||||
|
|||||||
@@ -15,7 +15,8 @@
|
|||||||
- MachineUpgrading
|
- MachineUpgrading
|
||||||
- type: PartExchanger
|
- type: PartExchanger
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 30
|
grid:
|
||||||
|
- 0,0,9,2
|
||||||
quickInsert: true
|
quickInsert: true
|
||||||
areaInsert: true
|
areaInsert: true
|
||||||
whitelist:
|
whitelist:
|
||||||
|
|||||||
@@ -16,8 +16,9 @@
|
|||||||
- type: Item
|
- type: Item
|
||||||
size: Ginormous
|
size: Ginormous
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 10
|
|
||||||
maxItemSize: Normal
|
maxItemSize: Normal
|
||||||
|
grid:
|
||||||
|
- 0,0,9,3
|
||||||
quickInsert: true
|
quickInsert: true
|
||||||
areaInsert: true
|
areaInsert: true
|
||||||
whitelist:
|
whitelist:
|
||||||
|
|||||||
@@ -480,7 +480,8 @@
|
|||||||
tags:
|
tags:
|
||||||
- PillCanister
|
- PillCanister
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxTotalWeight: 10
|
grid:
|
||||||
|
- 0,0,4,1
|
||||||
quickInsert: true
|
quickInsert: true
|
||||||
areaInsert: true
|
areaInsert: true
|
||||||
areaInsertRadius: 1
|
areaInsertRadius: 1
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
abstract: true
|
abstract: true
|
||||||
components:
|
components:
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 7
|
grid:
|
||||||
maxTotalWeight: 14
|
- 0,0,6,1
|
||||||
- type: Item
|
- type: Item
|
||||||
size: Small
|
size: Small
|
||||||
|
|
||||||
@@ -83,12 +83,12 @@
|
|||||||
heldPrefix: matchbox
|
heldPrefix: matchbox
|
||||||
size: Small
|
size: Small
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 5
|
grid:
|
||||||
maxTotalWeight: 5
|
- 0,0,2,1
|
||||||
- type: StorageFill
|
- type: StorageFill
|
||||||
contents:
|
contents:
|
||||||
- id: Matchstick
|
- id: Matchstick
|
||||||
amount: 5
|
amount: 6
|
||||||
- type: ItemCounter
|
- type: ItemCounter
|
||||||
count:
|
count:
|
||||||
tags: [Matchstick]
|
tags: [Matchstick]
|
||||||
|
|||||||
@@ -8,9 +8,9 @@
|
|||||||
sound:
|
sound:
|
||||||
path: /Audio/Items/toolbox_drop.ogg
|
path: /Audio/Items/toolbox_drop.ogg
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 7
|
|
||||||
maxItemSize: Normal
|
maxItemSize: Normal
|
||||||
maxTotalWeight: 14
|
grid:
|
||||||
|
- 0,0,6,3
|
||||||
- type: Item
|
- type: Item
|
||||||
size: Ginormous
|
size: Ginormous
|
||||||
- type: ItemCooldown
|
- type: ItemCooldown
|
||||||
@@ -117,8 +117,8 @@
|
|||||||
sprite: Objects/Tools/Toolboxes/toolbox_syn.rsi
|
sprite: Objects/Tools/Toolboxes/toolbox_syn.rsi
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxItemSize: Huge
|
maxItemSize: Huge
|
||||||
maxSlots: 8
|
grid:
|
||||||
maxTotalWeight: 32
|
- 0,0,7,3
|
||||||
- type: MeleeWeapon
|
- type: MeleeWeapon
|
||||||
damage:
|
damage:
|
||||||
types:
|
types:
|
||||||
|
|||||||
@@ -32,9 +32,9 @@
|
|||||||
container: storagebase
|
container: storagebase
|
||||||
- type: PneumaticCannon
|
- type: PneumaticCannon
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 8
|
|
||||||
maxItemSize: Normal
|
maxItemSize: Normal
|
||||||
maxTotalWeight: 32
|
grid:
|
||||||
|
- 0,0,7,3
|
||||||
blacklist:
|
blacklist:
|
||||||
tags:
|
tags:
|
||||||
- CannonRestrict
|
- CannonRestrict
|
||||||
@@ -77,9 +77,9 @@
|
|||||||
layers:
|
layers:
|
||||||
- state: piecannon
|
- state: piecannon
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 8
|
|
||||||
maxItemSize: Normal
|
maxItemSize: Normal
|
||||||
maxTotalWeight: 32
|
grid:
|
||||||
|
- 0,0,7,3
|
||||||
whitelist:
|
whitelist:
|
||||||
components:
|
components:
|
||||||
- CreamPie
|
- CreamPie
|
||||||
@@ -126,9 +126,9 @@
|
|||||||
- type: Item
|
- type: Item
|
||||||
size: Ginormous
|
size: Ginormous
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 100
|
|
||||||
maxItemSize: Ginormous
|
maxItemSize: Ginormous
|
||||||
maxTotalWeight: 1600
|
grid:
|
||||||
|
- 0,0,19,10
|
||||||
whitelist:
|
whitelist:
|
||||||
tags: [] #dodging a test fail like the IRS
|
tags: [] #dodging a test fail like the IRS
|
||||||
- type: PneumaticCannon
|
- type: PneumaticCannon
|
||||||
|
|||||||
@@ -47,7 +47,8 @@
|
|||||||
- type: Pullable
|
- type: Pullable
|
||||||
- type: Occluder
|
- type: Occluder
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 32
|
grid:
|
||||||
|
- 0,0,15,3
|
||||||
maxItemSize: Normal
|
maxItemSize: Normal
|
||||||
whitelist:
|
whitelist:
|
||||||
tags:
|
tags:
|
||||||
|
|||||||
@@ -25,7 +25,8 @@
|
|||||||
- !type:DoActsBehavior
|
- !type:DoActsBehavior
|
||||||
acts: [ "Destruction" ]
|
acts: [ "Destruction" ]
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 6
|
grid:
|
||||||
|
- 0,0,5,3
|
||||||
maxItemSize: Normal
|
maxItemSize: Normal
|
||||||
- type: ContainerContainer
|
- type: ContainerContainer
|
||||||
containers:
|
containers:
|
||||||
|
|||||||
@@ -6,7 +6,8 @@
|
|||||||
description: A cabinet for all your filing needs.
|
description: A cabinet for all your filing needs.
|
||||||
components:
|
components:
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 12
|
grid:
|
||||||
|
- 0,0,9,3
|
||||||
maxItemSize: Normal
|
maxItemSize: Normal
|
||||||
whitelist:
|
whitelist:
|
||||||
tags:
|
tags:
|
||||||
@@ -66,7 +67,8 @@
|
|||||||
description: A small drawer for all your filing needs, Now with wheels!
|
description: A small drawer for all your filing needs, Now with wheels!
|
||||||
components:
|
components:
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 8
|
grid:
|
||||||
|
- 0,0,7,2
|
||||||
maxItemSize: Normal
|
maxItemSize: Normal
|
||||||
whitelist:
|
whitelist:
|
||||||
tags:
|
tags:
|
||||||
|
|||||||
@@ -50,7 +50,8 @@
|
|||||||
True: { visible: false }
|
True: { visible: false }
|
||||||
False: { visible: true }
|
False: { visible: true }
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 60
|
grid:
|
||||||
|
- 0,0,9,5
|
||||||
maxItemSize: Normal
|
maxItemSize: Normal
|
||||||
storageOpenSound: /Audio/Effects/closetopen.ogg
|
storageOpenSound: /Audio/Effects/closetopen.ogg
|
||||||
storageCloseSound: /Audio/Effects/closetclose.ogg
|
storageCloseSound: /Audio/Effects/closetclose.ogg
|
||||||
|
|||||||
@@ -38,7 +38,8 @@
|
|||||||
- !type:DoActsBehavior
|
- !type:DoActsBehavior
|
||||||
acts: ["Destruction"]
|
acts: ["Destruction"]
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 10
|
grid:
|
||||||
|
- 0,0,4,3
|
||||||
maxItemSize: Small
|
maxItemSize: Small
|
||||||
whitelist:
|
whitelist:
|
||||||
tags:
|
tags:
|
||||||
|
|||||||
@@ -52,9 +52,9 @@
|
|||||||
storagebase: !type:Container
|
storagebase: !type:Container
|
||||||
ents: [ ]
|
ents: [ ]
|
||||||
- type: Storage
|
- type: Storage
|
||||||
maxSlots: 5
|
|
||||||
maxItemSize: Huge
|
maxItemSize: Huge
|
||||||
maxTotalWeight: 40
|
grid:
|
||||||
|
- 0,0,10,5
|
||||||
|
|
||||||
- type: artifactEffect
|
- type: artifactEffect
|
||||||
id: EffectPhasing
|
id: EffectPhasing
|
||||||
|
|||||||
@@ -3,33 +3,45 @@
|
|||||||
id: Tiny
|
id: Tiny
|
||||||
weight: 1
|
weight: 1
|
||||||
name: item-component-size-Tiny
|
name: item-component-size-Tiny
|
||||||
|
defaultShape:
|
||||||
|
- 0,0,0,0
|
||||||
|
|
||||||
# Items that can fit inside of a standard pocket.
|
# Items that can fit inside of a standard pocket.
|
||||||
- type: itemSize
|
- type: itemSize
|
||||||
id: Small
|
id: Small
|
||||||
weight: 2
|
weight: 2
|
||||||
name: item-component-size-Small
|
name: item-component-size-Small
|
||||||
|
defaultShape:
|
||||||
|
- 0,0,0,1
|
||||||
|
|
||||||
# Items that can fit inside of a standard bag.
|
# Items that can fit inside of a standard bag.
|
||||||
- type: itemSize
|
- type: itemSize
|
||||||
id: Normal
|
id: Normal
|
||||||
weight: 4
|
weight: 4
|
||||||
name: item-component-size-Normal
|
name: item-component-size-Normal
|
||||||
|
defaultShape:
|
||||||
|
- 0,0,1,1
|
||||||
|
|
||||||
# Items that are too large to fit inside of standard bags, but can worn in exterior slots or placed in custom containers.
|
# Items that are too large to fit inside of standard bags, but can worn in exterior slots or placed in custom containers.
|
||||||
- type: itemSize
|
- type: itemSize
|
||||||
id: Large
|
id: Large
|
||||||
weight: 8
|
weight: 8
|
||||||
name: item-component-size-Large
|
name: item-component-size-Large
|
||||||
|
defaultShape:
|
||||||
|
- 0,0,3,1
|
||||||
|
|
||||||
# Items that are too large to place inside of any kind of container.
|
# Items that are too large to place inside of any kind of container.
|
||||||
- type: itemSize
|
- type: itemSize
|
||||||
id: Huge
|
id: Huge
|
||||||
weight: 16
|
weight: 16
|
||||||
name: item-component-size-Huge
|
name: item-component-size-Huge
|
||||||
|
defaultShape:
|
||||||
|
- 0,0,3,3
|
||||||
|
|
||||||
# Picture furry gf
|
# Picture furry gf
|
||||||
- type: itemSize
|
- type: itemSize
|
||||||
id: Ginormous
|
id: Ginormous
|
||||||
weight: 32
|
weight: 32
|
||||||
name: item-component-size-Ginormous
|
name: item-component-size-Ginormous
|
||||||
|
defaultShape:
|
||||||
|
- 0,0,5,5
|
||||||
|
|||||||
BIN
Resources/Textures/Interface/Default/Storage/back.png
Normal file
|
After Width: | Height: | Size: 204 B |
BIN
Resources/Textures/Interface/Default/Storage/exit.png
Normal file
|
After Width: | Height: | Size: 208 B |
BIN
Resources/Textures/Interface/Default/Storage/piece_bottom.png
Normal file
|
After Width: | Height: | Size: 114 B |
|
After Width: | Height: | Size: 146 B |
|
After Width: | Height: | Size: 147 B |
BIN
Resources/Textures/Interface/Default/Storage/piece_center.png
Normal file
|
After Width: | Height: | Size: 100 B |
BIN
Resources/Textures/Interface/Default/Storage/piece_left.png
Normal file
|
After Width: | Height: | Size: 109 B |
BIN
Resources/Textures/Interface/Default/Storage/piece_right.png
Normal file
|
After Width: | Height: | Size: 112 B |
BIN
Resources/Textures/Interface/Default/Storage/piece_top.png
Normal file
|
After Width: | Height: | Size: 112 B |
BIN
Resources/Textures/Interface/Default/Storage/piece_topLeft.png
Normal file
|
After Width: | Height: | Size: 132 B |
BIN
Resources/Textures/Interface/Default/Storage/piece_topRight.png
Normal file
|
After Width: | Height: | Size: 147 B |
BIN
Resources/Textures/Interface/Default/Storage/sidebar_bottom.png
Normal file
|
After Width: | Height: | Size: 136 B |
BIN
Resources/Textures/Interface/Default/Storage/sidebar_fat.png
Normal file
|
After Width: | Height: | Size: 149 B |
BIN
Resources/Textures/Interface/Default/Storage/sidebar_mid.png
Normal file
|
After Width: | Height: | Size: 112 B |
BIN
Resources/Textures/Interface/Default/Storage/sidebar_top.png
Normal file
|
After Width: | Height: | Size: 130 B |
BIN
Resources/Textures/Interface/Default/Storage/tile_blocked.png
Normal file
|
After Width: | Height: | Size: 124 B |
BIN
Resources/Textures/Interface/Default/Storage/tile_empty.png
Normal file
|
After Width: | Height: | Size: 132 B |
@@ -163,6 +163,13 @@ binds:
|
|||||||
- function: SwapHands
|
- function: SwapHands
|
||||||
type: State
|
type: State
|
||||||
key: X
|
key: X
|
||||||
|
- function: MoveStoredItem
|
||||||
|
type: State
|
||||||
|
key: MouseLeft
|
||||||
|
canFocus: true
|
||||||
|
- function: RotateStoredItem
|
||||||
|
type: State
|
||||||
|
key: MouseRight
|
||||||
- function: Drop
|
- function: Drop
|
||||||
type: State
|
type: State
|
||||||
key: Q
|
key: Q
|
||||||
|
|||||||