mirror of
https://github.com/DeltaV-Station/Delta-v.git
synced 2026-09-30 16:17:27 +00:00
* Fix the sensor monitoring console forcing a GC every 3 seconds (#38146) * Optimize sensor monitoring window graph drawing * Add shared static Vector2 pool for all GraphView instances * Address requested changes * remove lock * Update submodule to 264.0.0 (#38629) * Toy/Plushie Inhands and Wearables (#38514) * Fixed dependency injection and some other issues in a few places. * More issue fixes * patchfor broken NetworkConfiguratorLinkMenu (#38632) fix * Fix solutions flickering when transferring contents (#34838) * Use Solution clones when applying SolutionComponent states * Revert "Use Solution clones when applying SolutionComponent states" This reverts commit 013fd111cf92b22562e00f98a7aaa49bc4b4ed62. * Make Solution implement ICloneable and rename Clone method. * Copy CanReact value when cloning a Solution * Convert to IRobustCloneable * Fix typos in guidebook: Buisness → Business (#38636) * Fix typo in `MinorAntagonists.xml` Buisness → Business * Fix typo in `YourFirstCharacter.xml` Buisness → Business * Various Headphones Fixes and Tweaks (#38479) * sprites, wearables * neck * icon-on sprite * Cleanup prototype instantiation in `DamageTest` (#38639) Cleanup prototype instantiation in DamageTest * Cleanup prototype instantiation in `ExplosionSystem` (#38642) Cleanup prototype instantiation in ExplosionSystem * AddBodyPartCommand localization. (#38612) commit * fix ItemSlotsSystem debug assert (#38655) * Allow the Command & Super door remotes to use the access of their user. (Re-creation of PR due to changes to game balance) (#35536) * Added directional beacons (#38284) * Added directional beacons Signed-off-by: Nox38 <nebulousnox38@gmail.com> * Fixed names Signed-off-by: Nox38 <nebulousnox38@gmail.com> --------- Signed-off-by: Nox38 <nebulousnox38@gmail.com> * Power stat and nuke codes commands get some LEC love. (#38585) * commit * requested changes. * Dsay Dirty and Follow commands converted to LEC and localized. (#38666) * commit * whoopwhoopwhoop * Retro laser sprite fix (#38676) * Fixed everything except the icon Signed-off-by: Nox38 <nebulousnox38@gmail.com> * fixed icon Signed-off-by: Nox38 <nebulousnox38@gmail.com> --------- Signed-off-by: Nox38 <nebulousnox38@gmail.com> * fix water coolers (#38681) * Monochromacy typo fix (#38686) * fixes the typo * Fixed cloning looking for the trait, not the component, RE https://github.com/space-wizards/space-station-14/pull/38686#issuecomment-3025093504 * Apply suggestions from code review --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Validate `CloningSettingsPrototype`s (#38688) * Validate CloningSettingsPrototypes * Update Content.IntegrationTests/Tests/Cloning/CloningSettingsPrototypeTest.cs Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Check EventComponents too --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Add test of objective-related console commands (#36400) * Add test of objective add/list/remove commands * Not sure why we're validating test prototypes, but sure * We don't need a map * Improvements and fixups for New Status Effect API (#38660) * Predict healing and bloodstream (#38690) * initial commit * reapply 38126 * fix rootable * someone missed an important minus sign here * try this * fix * fix * reenable crit hits * cleanup * fix status time dirtying * fix * camelCase * Healing and bloodstream prediction fixes. * Resolving Wizard casting recall on nuke disk making it impossible to disarm (#38661) * Resolving Wizard Recall on Nuke disk making it impossible to disarm - Adding a DisarmBomb case to nuke status update loop - Changing a few methods and parameters to properly follow formatting standards - Updating some names to follow camelCase * Updating missed tag * Reverting DataField change Should prevent this preventative bugfix being a breaking change. * Switch HSV to the default colorspace for character customization (#38434) * Made HSV default for character editor * Adds/fixes comments to HSV defaulting * Added dropbox fix, potentially cursed * Revert "Added dropbox fix, potentially cursed" This reverts commit a709883366fbee813e839742125e70844672af29. --------- Co-authored-by: TrixxedHeart <46364955+TrixxedBit@users.noreply.github.com> * Fix: Don't deploy foldables when clicking on items inside containers (#38709) * Fix * Apply suggestions from code review --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Pressure Relief Valve (#36708) * initial system (this math is probably WRONG) * General code cleanup and OnExamined support (holy moly this code sucks) * UICode and related events foundation TODO: - Actually write the XAML UI and the underlying system - Un-shitcode the entire thing - Actually test everything... * Working UI code TODO: Make predicted, as this certainly isn't predicted. Even though I said it was. It isn't. * Remove one TODO for unshitcoding the examine code * Add reminder yea * Make predicted (defenitely isn't) (also defenitely isn't a copypaste from pressure pump code) * It's predicted! TODO: - Give it snazzy predicted visuals! - Have a different field for pressure entry, lest it gets bulldozed every UI update. * Improve gas pressure relief valve UI TODO: Reminder to reduce amount of dirties using deltafields * Implement DirtyField prediction * Entity<T> cleanup A lot of Entity<T> conversions and lukewarm cleanup. Also got caught copy pasting code in 4K UHD but it's not like you couldn't tell. * More cleanup and comments * Remove TODO comment on bulldozing window title * """refactoring""" - Move appearance out of shared and finally fix it. Pointless to predict appearance in this instance. - More Entity<T> conversions because I like them. - Move UI creation handling over entirely to the ActivatableUI system. - Fix a hardcoded locale string (why????). * Add visuals * Revert debugging variable replacememt yea * Revert skissue * Remove unused using directives and remove TODO * Localize, cleanup, document * Fix adminlogging discrepancy * Add ability to construct, add guidebook entry * Clear up comment * Add guidebook tooltip to valve * Convert GasPressureReliefValveBoundUserInterface declaration into primary constructor * Adds more input handling and adds autofill on open * Un-deepfry input validator shitcode Genuinely what was I smoking * improve visuals logic * Refactor again - Update math to the correct implementation - Moved code that could be re-used in the future into a helper method under AtmosphereSystem.Gases.cs * I'm sorry but I hate warnings * Remove unused using directive in AtmosphereSystem.Gases.cs * Review and cleanup * Lukewarm UI glossup * Maintainer for the upstream project btw * Remove redundant state sets and messy logic * Unduplicate valve updater code * Redo UI (im sorry Slarti) * run tests * Test refactored UI messaging * Second round of UI improvements - God please find a way to improve this system. Feels bad. * Update loop implementation * Further predict UI * Clear up SetToCurrentThreshold * cleanup * Update to master + pipe layers and bug fixes want to run tests * fixes * Deploy rename pipebomb * Documentation and requested changes * Rename the method that wiggled away * Undo rounding changes * Fix comment * Rename and cleanup * Apply suggestions from code review --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Fix TextLinkTag (#32203) * Adjust uplink buy button to be under item icon (#38596) * Adjusted uplink buy button to be under item icon * Put the discount subtext under the icon * Indent fixes, added margin --------- Co-authored-by: TrixxedHeart <46364955+TrixxedBit@users.noreply.github.com> * Scurrets - Audio Improvements (#38482) * Scurret audio tuning * Add new sfx * Update sneezing sfx * YAML support * Rename a folder * make telesci wreck easier (#37569) rel * Validate `ShaderPrototype` IDs (#38728) * Convert all shader prototype string literals to protoids in overlays * Convert more shader prototype literal strings to protoids * Convert ValidatePrototypeId to ProtoId * Later * [BUGFIX] Fixed revenant malfunction ability not working properly only MediBots and Stasis bed (#38664) * fixed * clean up * orks fix smart * review fix 1 * more requested changes * less cursed * more descriptive description * better wording * Tiny Tiny Cleanup of the EyeClosingSystem. (#38734) Update EyeClosingSystem.cs * Fix human skin tone distribution (#38701) * fix: Use PredictedQueueDel for gib spell (#38729) * fix: don't default to uncharged sprite state for cells (#38730) * Fix Hristov description - remove inaccurate technical specs (#38746) - Removes inaccurate 'armor piercing 14.5mm shells' reference - Replaces it with a more funny description, matching the style of the other snipers and guns - Keeps ammunition type '.60 anti-materiel ammo' specification - Fixes issue #38590 Co-authored-by: Arthur Fiorese de Andrade <aandrade@cmcxs.gov.br> * feat: allow mopping evaporating puddles (#38743) * Validate remaining `ProtoId` strings (#38747) Validate remaining ProtoId strings * Validate `ProtoId`s in tests (#38745) * Convert string literals to protoids in Content.Tests * Convert string literals to protoids or consts in Content.IntegrationTests * Fix linter failures Tricksy static using misled me * Cleanup warning in StomachSystem (#38748) you did not see this * Vox scars (#38592) * Added vox scars n'stuff, renamed vox_tattoos.ftl to just vox.ftl * Revert "Added vox scars n'stuff, renamed vox_tattoos.ftl to just vox.ftl" This reverts commit c73da55ba3b39ddf93b493aecd85604c54dd8a15. * locale key fix * Changed top surgery scar names to be more generalized * Adjusted face scars * Formatting fixes --------- Co-authored-by: TrixxedHeart <46364955+TrixxedBit@users.noreply.github.com> * UnlockNode command to LEC. (#38751) * commit * Update UnlockNodeCommand.cs * commit * move command locale to its own file. * Update Content.Server/Xenoarchaeology/Artifact/UnlockNodeCommand.cs --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Typofixes for figurine dialogue (#38737) * Typofixes for figurine dialogue * Forgot two * Janitor Tool: Wire Brush (#38667) * Wow! It's -brush- * spacing. * Update Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Update Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * fixed changes --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Minor escape menu UX improvements (#38650) * fix: spellbooks can have infinite charges (#38376) * fix: spellbooks can have infinite charges * refactor: indicate infinite spellbook charges with null Not sure if I like this much better...
732 lines
24 KiB
C#
732 lines
24 KiB
C#
using System.Diagnostics.CodeAnalysis;
|
|
using System.Linq;
|
|
using System.Numerics;
|
|
using Content.Client.Hands.Systems;
|
|
using Content.Client.Items.Systems;
|
|
using Content.Client.Storage;
|
|
using Content.Client.Storage.Systems;
|
|
using Content.Shared.IdentityManagement;
|
|
using Content.Shared.Input;
|
|
using Content.Shared.Item;
|
|
using Content.Shared.Storage;
|
|
using Robust.Client.GameObjects;
|
|
using Robust.Client.Graphics;
|
|
using Robust.Client.UserInterface;
|
|
using Robust.Client.UserInterface.Controls;
|
|
using Robust.Client.UserInterface.CustomControls;
|
|
using Robust.Shared.Collections;
|
|
using Robust.Shared.Containers;
|
|
using Robust.Shared.Timing;
|
|
using Robust.Shared.Utility;
|
|
|
|
namespace Content.Client.UserInterface.Systems.Storage.Controls;
|
|
|
|
public sealed class StorageWindow : 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;
|
|
|
|
private Control _titleContainer;
|
|
private Label _titleLabel;
|
|
|
|
// Needs to be nullable in case a piece is in default spot.
|
|
private readonly Dictionary<EntityUid, (ItemStorageLocation? Loc, ItemGridPiece Control)> _pieces = new();
|
|
private readonly List<Control> _controlGrid = new();
|
|
|
|
private ValueList<EntityUid> _contained = new();
|
|
private ValueList<EntityUid> _toRemove = new();
|
|
|
|
// Manually store this because you can't have a 0x0 GridContainer but we still need to add child controls for 1x1 containers.
|
|
private Vector2i _pieceGridSize;
|
|
|
|
private TextureButton? _backButton;
|
|
|
|
private bool _isDirty;
|
|
|
|
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 _emptyOpaqueTexturePath = "Storage/tile_empty_opaque";
|
|
private Texture? _emptyOpaqueTexture;
|
|
private readonly string _blockedOpaqueTexturePath = "Storage/tile_blocked_opaque";
|
|
private Texture? _blockedOpaqueTexture;
|
|
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 StorageWindow()
|
|
{
|
|
IoCManager.InjectDependencies(this);
|
|
Resizable = false;
|
|
|
|
_storageController = UserInterfaceManager.GetUIController<StorageUIController>();
|
|
|
|
OnThemeUpdated();
|
|
|
|
MouseFilter = MouseFilterMode.Stop;
|
|
|
|
_sidebar = new GridContainer
|
|
{
|
|
Name = "SideBar",
|
|
HSeparationOverride = 0,
|
|
VSeparationOverride = 0,
|
|
Columns = 1
|
|
};
|
|
|
|
_pieceGrid = new GridContainer
|
|
{
|
|
Name = "PieceGrid",
|
|
HSeparationOverride = 0,
|
|
VSeparationOverride = 0
|
|
};
|
|
|
|
_backgroundGrid = new GridContainer
|
|
{
|
|
Name = "BackgroundGrid",
|
|
HSeparationOverride = 0,
|
|
VSeparationOverride = 0
|
|
};
|
|
|
|
_titleLabel = new Label()
|
|
{
|
|
HorizontalExpand = true,
|
|
Name = "StorageLabel",
|
|
ClipText = true,
|
|
Text = "Dummy",
|
|
StyleClasses =
|
|
{
|
|
"FancyWindowTitle",
|
|
}
|
|
};
|
|
|
|
_titleContainer = new PanelContainer()
|
|
{
|
|
StyleClasses =
|
|
{
|
|
"WindowHeadingBackground"
|
|
},
|
|
Children =
|
|
{
|
|
_titleLabel
|
|
}
|
|
};
|
|
|
|
var container = new BoxContainer
|
|
{
|
|
Orientation = BoxContainer.LayoutOrientation.Vertical,
|
|
Children =
|
|
{
|
|
_titleContainer,
|
|
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;
|
|
_emptyOpaqueTexture = Theme.ResolveTextureOrNull(_emptyOpaqueTexturePath)?.Texture;
|
|
_blockedOpaqueTexture = Theme.ResolveTextureOrNull(_blockedOpaqueTexturePath)?.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;
|
|
|
|
if (UserInterfaceManager.GetUIController<StorageUIController>().WindowTitle)
|
|
{
|
|
_titleLabel.Text = Identity.Name(entity.Value, _entity);
|
|
_titleContainer.Visible = true;
|
|
}
|
|
else
|
|
{
|
|
_titleContainer.Visible = false;
|
|
}
|
|
|
|
BuildGridRepresentation();
|
|
}
|
|
|
|
private void CloseParent()
|
|
{
|
|
if (StorageEntity == null)
|
|
return;
|
|
|
|
var containerSystem = _entity.System<SharedContainerSystem>();
|
|
var uiSystem = _entity.System<UserInterfaceSystem>();
|
|
|
|
if (containerSystem.TryGetContainingContainer(StorageEntity.Value, out var container) &&
|
|
_entity.TryGetComponent(container.Owner, out StorageComponent? storage) &&
|
|
storage.Container.Contains(StorageEntity.Value) &&
|
|
uiSystem
|
|
.TryGetOpenUi<StorageBoundUserInterface>(container.Owner,
|
|
StorageComponent.StorageUiKey.Key,
|
|
out var parentBui))
|
|
{
|
|
parentBui.CloseWindow(Position);
|
|
}
|
|
}
|
|
|
|
private void BuildGridRepresentation()
|
|
{
|
|
if (!_entity.TryGetComponent<StorageComponent>(StorageEntity, out var comp) || comp.Grid.Count == 0)
|
|
return;
|
|
|
|
var boundingGrid = comp.Grid.GetBoundingBox();
|
|
|
|
BuildBackground();
|
|
|
|
#region Sidebar
|
|
_sidebar.Children.Clear();
|
|
var rows = boundingGrid.Height + 1;
|
|
_sidebar.Rows = rows;
|
|
|
|
var exitButton = new TextureButton
|
|
{
|
|
Name = "ExitButton",
|
|
TextureNormal = _exitTexture,
|
|
Scale = new Vector2(2, 2),
|
|
};
|
|
exitButton.OnPressed += _ =>
|
|
{
|
|
// Close ourselves and all parent BUIs.
|
|
Close();
|
|
CloseParent();
|
|
};
|
|
exitButton.OnKeyBindDown += args =>
|
|
{
|
|
// it just makes sense...
|
|
if (!args.Handled && args.Function == ContentKeyFunctions.ActivateItemInWorld)
|
|
{
|
|
Close();
|
|
CloseParent();
|
|
args.Handle();
|
|
}
|
|
};
|
|
|
|
var exitContainer = new BoxContainer
|
|
{
|
|
Name = "ExitContainer",
|
|
Children =
|
|
{
|
|
new TextureRect
|
|
{
|
|
Texture = boundingGrid.Height != 0
|
|
? _sidebarTopTexture
|
|
: _sidebarFatTexture,
|
|
TextureScale = new Vector2(2, 2),
|
|
Children =
|
|
{
|
|
exitButton
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
_sidebar.AddChild(exitContainer);
|
|
var offset = 2;
|
|
|
|
if (_entity.System<StorageSystem>().NestedStorage && rows > 0)
|
|
{
|
|
_backButton = new TextureButton
|
|
{
|
|
TextureNormal = _backTexture,
|
|
Scale = new Vector2(2, 2),
|
|
};
|
|
_backButton.OnPressed += _ =>
|
|
{
|
|
var containerSystem = _entity.System<SharedContainerSystem>();
|
|
|
|
if (containerSystem.TryGetContainingContainer(StorageEntity.Value, out var container) &&
|
|
_entity.TryGetComponent(container.Owner, out StorageComponent? storage) &&
|
|
storage.Container.Contains(StorageEntity.Value))
|
|
{
|
|
Close();
|
|
|
|
if (_entity.System<SharedUserInterfaceSystem>()
|
|
.TryGetOpenUi<StorageBoundUserInterface>(container.Owner,
|
|
StorageComponent.StorageUiKey.Key,
|
|
out var parentBui))
|
|
{
|
|
parentBui.Show(Position);
|
|
}
|
|
}
|
|
};
|
|
|
|
var backContainer = new BoxContainer
|
|
{
|
|
Name = "ExitContainer",
|
|
Children =
|
|
{
|
|
new TextureRect
|
|
{
|
|
Texture = rows > 2 ? _sidebarMiddleTexture : _sidebarBottomTexture,
|
|
TextureScale = new Vector2(2, 2),
|
|
Children =
|
|
{
|
|
_backButton,
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
_sidebar.AddChild(backContainer);
|
|
}
|
|
|
|
var fillerRows = rows - offset;
|
|
|
|
for (var i = 0; i < fillerRows; i++)
|
|
{
|
|
_sidebar.AddChild(new TextureRect
|
|
{
|
|
Texture = i != (fillerRows - 1) ? _sidebarMiddleTexture : _sidebarBottomTexture,
|
|
TextureScale = new Vector2(2, 2),
|
|
});
|
|
}
|
|
|
|
#endregion
|
|
|
|
FlagDirty();
|
|
}
|
|
|
|
public void BuildBackground()
|
|
{
|
|
if (!_entity.TryGetComponent<StorageComponent>(StorageEntity, out var comp) || !comp.Grid.Any())
|
|
return;
|
|
|
|
var boundingGrid = comp.Grid.GetBoundingBox();
|
|
|
|
var emptyTexture = _storageController.OpaqueStorageWindow
|
|
? _emptyOpaqueTexture
|
|
: _emptyTexture;
|
|
var blockedTexture = _storageController.OpaqueStorageWindow
|
|
? _blockedOpaqueTexture
|
|
: _blockedTexture;
|
|
|
|
_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)
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Reclaim(ItemStorageLocation location, ItemGridPiece draggingGhost)
|
|
{
|
|
draggingGhost.OnPiecePressed += OnPiecePressed;
|
|
draggingGhost.OnPieceUnpressed += OnPieceUnpressed;
|
|
_pieces[draggingGhost.Entity] = (location, draggingGhost);
|
|
draggingGhost.Location = location;
|
|
var controlIndex = GetGridIndex(draggingGhost);
|
|
_controlGrid[controlIndex].AddChild(draggingGhost);
|
|
}
|
|
|
|
private int GetGridIndex(ItemGridPiece piece)
|
|
{
|
|
return piece.Location.Position.X + piece.Location.Position.Y * _pieceGrid.Columns;
|
|
}
|
|
|
|
public void FlagDirty()
|
|
{
|
|
_isDirty = true;
|
|
}
|
|
|
|
public void RemoveGrid(ItemGridPiece control)
|
|
{
|
|
control.Orphan();
|
|
_pieces.Remove(control.Entity);
|
|
control.OnPiecePressed -= OnPiecePressed;
|
|
control.OnPieceUnpressed -= OnPieceUnpressed;
|
|
}
|
|
|
|
public void BuildItemPieces()
|
|
{
|
|
if (!_entity.TryGetComponent<StorageComponent>(StorageEntity, out var storageComp))
|
|
return;
|
|
|
|
if (storageComp.Grid.Count == 0)
|
|
return;
|
|
|
|
var boundingGrid = storageComp.Grid.GetBoundingBox();
|
|
var size = _emptyTexture!.Size * 2;
|
|
_contained.Clear();
|
|
_contained.AddRange(storageComp.Container.ContainedEntities.Reverse());
|
|
|
|
var width = boundingGrid.Width + 1;
|
|
var height = boundingGrid.Height + 1;
|
|
|
|
// Build the grid representation
|
|
if (_pieceGrid.Rows != _pieceGridSize.Y || _pieceGrid.Columns != _pieceGridSize.X)
|
|
{
|
|
_pieceGrid.Rows = height;
|
|
_pieceGrid.Columns = width;
|
|
_controlGrid.Clear();
|
|
|
|
for (var y = boundingGrid.Bottom; y <= boundingGrid.Top; y++)
|
|
{
|
|
for (var x = boundingGrid.Left; x <= boundingGrid.Right; x++)
|
|
{
|
|
var control = new Control
|
|
{
|
|
MinSize = size
|
|
};
|
|
|
|
_controlGrid.Add(control);
|
|
_pieceGrid.AddChild(control);
|
|
}
|
|
}
|
|
}
|
|
|
|
_pieceGridSize = new(width, height);
|
|
_toRemove.Clear();
|
|
|
|
// Remove entities no longer relevant / Update existing ones
|
|
foreach (var (ent, data) in _pieces)
|
|
{
|
|
if (storageComp.StoredItems.TryGetValue(ent, out var updated))
|
|
{
|
|
data.Control.Marked = IsMarked(ent);
|
|
|
|
if (data.Loc.Equals(updated))
|
|
{
|
|
DebugTools.Assert(data.Control.Location == updated);
|
|
continue;
|
|
}
|
|
|
|
// Update
|
|
data.Control.Location = updated;
|
|
var index = GetGridIndex(data.Control);
|
|
data.Control.Orphan();
|
|
_controlGrid[index].AddChild(data.Control);
|
|
_pieces[ent] = (updated, data.Control);
|
|
continue;
|
|
}
|
|
|
|
_toRemove.Add(ent);
|
|
}
|
|
|
|
foreach (var ent in _toRemove)
|
|
{
|
|
_pieces.Remove(ent, out var data);
|
|
data.Control.Orphan();
|
|
}
|
|
|
|
// Add new ones
|
|
foreach (var (ent, loc) in storageComp.StoredItems)
|
|
{
|
|
if (_pieces.TryGetValue(ent, out var existing))
|
|
{
|
|
DebugTools.Assert(existing.Loc == loc);
|
|
continue;
|
|
}
|
|
|
|
if (_entity.TryGetComponent<ItemComponent>(ent, out var itemEntComponent))
|
|
{
|
|
var gridPiece = new ItemGridPiece((ent, itemEntComponent), loc, _entity)
|
|
{
|
|
MinSize = size,
|
|
Marked = IsMarked(ent),
|
|
};
|
|
gridPiece.OnPiecePressed += OnPiecePressed;
|
|
gridPiece.OnPieceUnpressed += OnPieceUnpressed;
|
|
var controlIndex = loc.Position.X + loc.Position.Y * (boundingGrid.Width + 1);
|
|
|
|
_controlGrid[controlIndex].AddChild(gridPiece);
|
|
_pieces[ent] = (loc, gridPiece);
|
|
}
|
|
}
|
|
}
|
|
|
|
private ItemGridPieceMarks? IsMarked(EntityUid uid)
|
|
{
|
|
return _contained.IndexOf(uid) switch
|
|
{
|
|
0 => ItemGridPieceMarks.First,
|
|
1 => ItemGridPieceMarks.Second,
|
|
_ => null,
|
|
};
|
|
}
|
|
|
|
protected override void FrameUpdate(FrameEventArgs args)
|
|
{
|
|
base.FrameUpdate(args);
|
|
|
|
if (!IsOpen)
|
|
return;
|
|
|
|
if (_isDirty)
|
|
{
|
|
_isDirty = false;
|
|
BuildItemPieces();
|
|
}
|
|
|
|
var containerSystem = _entity.System<SharedContainerSystem>();
|
|
|
|
if (_backButton != null)
|
|
{
|
|
if (StorageEntity != null && _entity.System<StorageSystem>().NestedStorage)
|
|
{
|
|
// If parent container nests us then show back button
|
|
if (containerSystem.TryGetContainingContainer(StorageEntity.Value, out var container) &&
|
|
_entity.TryGetComponent(container.Owner, out StorageComponent? storageComp) && storageComp.Container.Contains(StorageEntity.Value))
|
|
{
|
|
_backButton.Visible = true;
|
|
}
|
|
else
|
|
{
|
|
_backButton.Visible = false;
|
|
}
|
|
}
|
|
// Hide the button.
|
|
else
|
|
{
|
|
_backButton.Visible = false;
|
|
}
|
|
}
|
|
|
|
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 StorageWindow 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);
|
|
|
|
foreach (var locations in storageComponent.SavedLocations)
|
|
{
|
|
if (!_entity.TryGetComponent<MetaDataComponent>(currentEnt, out var meta) || meta.EntityName != locations.Key)
|
|
continue;
|
|
|
|
float spot = 0;
|
|
var marked = new ValueList<Control>();
|
|
|
|
foreach (var location in locations.Value)
|
|
{
|
|
var shape = itemSystem.GetAdjustedItemShape(currentEnt, location);
|
|
var bound = shape.GetBoundingBox();
|
|
|
|
var spotFree = storageSystem.ItemFitsInGridLocation(currentEnt, StorageEntity.Value, location);
|
|
|
|
if (spotFree)
|
|
spot++;
|
|
|
|
for (var y = bound.Bottom; y <= bound.Top; y++)
|
|
{
|
|
for (var x = bound.Left; x <= bound.Right; x++)
|
|
{
|
|
if (TryGetBackgroundCell(x, y, out var cell) && shape.Contains(x, y) && !marked.Contains(cell))
|
|
{
|
|
marked.Add(cell);
|
|
cell.ModulateSelfOverride = spotFree
|
|
? Color.FromHsv(new Vector4(0.18f, 1 / spot, 0.5f / spot + 0.5f, 1f))
|
|
: Color.FromHex("#2222CC");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var validColor = usingInHand ? Color.Goldenrod : Color.FromHex("#1E8000");
|
|
|
|
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.FromHex("#B40046");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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));
|
|
_storageController.DraggingRotation = Angle.Zero;
|
|
args.Handle();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|