Content update for UI prediction (#27214)
* Content update for UI refactor * Big update * Sharing * Remaining content updates * First big update * Prototype updates * AUGH * Fix UI comp ref * Cleanup - Fix predicted message, fix item slots, fix interaction range check. * Fix regressions * Make this predictive idk why it wasn't. * Fix slime merge * Merge conflict * Fix merge
This commit is contained in:
@@ -2,9 +2,12 @@ using System.Collections.Frozen;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Administration.Managers;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Content.Shared.Destructible;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Ghost;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Implants.Components;
|
||||
@@ -29,6 +32,7 @@ using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared.Storage.EntitySystems;
|
||||
|
||||
@@ -36,6 +40,7 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
[Dependency] protected readonly IRobustRandom Random = default!;
|
||||
[Dependency] private readonly ISharedAdminManager _admin = default!;
|
||||
[Dependency] protected readonly ActionBlockerSystem ActionBlocker = default!;
|
||||
[Dependency] private readonly EntityLookupSystem _entityLookupSystem = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
@@ -73,6 +78,7 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
private FrozenDictionary<string, ItemSizePrototype> _nextSmallest = FrozenDictionary<string, ItemSizePrototype>.Empty;
|
||||
|
||||
private const string QuickInsertUseDelayID = "quickInsert";
|
||||
private const string OpenUiUseDelayID = "storage";
|
||||
|
||||
protected readonly List<string> CantFillReasons = [];
|
||||
|
||||
@@ -86,7 +92,13 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
_xformQuery = GetEntityQuery<TransformComponent>();
|
||||
_prototype.PrototypesReloaded += OnPrototypesReloaded;
|
||||
|
||||
Subs.BuiEvents<StorageComponent>(StorageComponent.StorageUiKey.Key, subs =>
|
||||
{
|
||||
subs.Event<BoundUIClosedEvent>(OnBoundUIClosed);
|
||||
});
|
||||
|
||||
SubscribeLocalEvent<StorageComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<StorageComponent, GetVerbsEvent<ActivationVerb>>(AddUiVerb);
|
||||
SubscribeLocalEvent<StorageComponent, ComponentGetState>(OnStorageGetState);
|
||||
SubscribeLocalEvent<StorageComponent, ComponentHandleState>(OnStorageHandleState);
|
||||
SubscribeLocalEvent<StorageComponent, ComponentInit>(OnComponentInit, before: new[] { typeof(SharedContainerSystem) });
|
||||
@@ -121,10 +133,13 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
UpdatePrototypeCache();
|
||||
}
|
||||
|
||||
protected virtual void OnMapInit(Entity<StorageComponent> entity, ref MapInitEvent args)
|
||||
private void OnMapInit(Entity<StorageComponent> entity, ref MapInitEvent args)
|
||||
{
|
||||
if (TryComp<UseDelayComponent>(entity, out var useDelayComp))
|
||||
{
|
||||
UseDelay.SetLength((entity, useDelayComp), entity.Comp.QuickInsertCooldown, QuickInsertUseDelayID);
|
||||
UseDelay.SetLength((entity, useDelayComp), entity.Comp.OpenUiCooldown, OpenUiUseDelayID);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnStorageGetState(EntityUid uid, StorageComponent component, ref ComponentGetState args)
|
||||
@@ -139,7 +154,6 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
args.State = new StorageComponentState()
|
||||
{
|
||||
Grid = new List<Box2i>(component.Grid),
|
||||
IsUiOpen = component.IsUiOpen,
|
||||
MaxItemSize = component.MaxItemSize,
|
||||
StoredItems = storedItems,
|
||||
SavedLocations = component.SavedLocations
|
||||
@@ -153,7 +167,6 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
|
||||
component.Grid.Clear();
|
||||
component.Grid.AddRange(state.Grid);
|
||||
component.IsUiOpen = state.IsUiOpen;
|
||||
component.MaxItemSize = state.MaxItemSize;
|
||||
|
||||
component.StoredItems.Clear();
|
||||
@@ -205,9 +218,108 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
UpdateAppearance((uid, storageComp, null));
|
||||
}
|
||||
|
||||
public virtual void UpdateUI(Entity<StorageComponent?> entity) {}
|
||||
/// <summary>
|
||||
/// If the user has nested-UIs open (e.g., PDA UI open when pda is in a backpack), close them.
|
||||
/// </summary>
|
||||
private void CloseNestedInterfaces(EntityUid uid, EntityUid actor, StorageComponent? storageComp = null)
|
||||
{
|
||||
if (!Resolve(uid, ref storageComp))
|
||||
return;
|
||||
|
||||
public virtual void OpenStorageUI(EntityUid uid, EntityUid entity, StorageComponent? storageComp = null, bool silent = false) { }
|
||||
// for each containing thing
|
||||
// if it has a storage comp
|
||||
// ensure unsubscribe from session
|
||||
// if it has a ui component
|
||||
// close ui
|
||||
foreach (var entity in storageComp.Container.ContainedEntities)
|
||||
{
|
||||
_ui.CloseUis(entity, actor);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnBoundUIClosed(EntityUid uid, StorageComponent storageComp, BoundUIClosedEvent args)
|
||||
{
|
||||
CloseNestedInterfaces(uid, args.Actor, storageComp);
|
||||
|
||||
// If UI is closed for everyone
|
||||
if (!_ui.IsUiOpen(uid, args.UiKey))
|
||||
{
|
||||
UpdateAppearance((uid, storageComp, null));
|
||||
Audio.PlayPredicted(storageComp.StorageCloseSound, uid, args.Actor);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddUiVerb(EntityUid uid, StorageComponent component, GetVerbsEvent<ActivationVerb> args)
|
||||
{
|
||||
var silent = false;
|
||||
if (!args.CanAccess || !args.CanInteract || TryComp<LockComponent>(uid, out var lockComponent) && lockComponent.Locked)
|
||||
{
|
||||
// we allow admins to open the storage anyways
|
||||
if (!_admin.HasAdminFlag(args.User, AdminFlags.Admin))
|
||||
return;
|
||||
|
||||
silent = true;
|
||||
}
|
||||
|
||||
silent |= HasComp<GhostComponent>(args.User);
|
||||
|
||||
// Does this player currently have the storage UI open?
|
||||
var uiOpen = _ui.IsUiOpen(uid, StorageComponent.StorageUiKey.Key, args.User);
|
||||
|
||||
ActivationVerb verb = new()
|
||||
{
|
||||
Act = () =>
|
||||
{
|
||||
if (uiOpen)
|
||||
{
|
||||
_ui.CloseUi(uid, StorageComponent.StorageUiKey.Key, args.User);
|
||||
}
|
||||
else
|
||||
{
|
||||
OpenStorageUI(uid, args.User, component, silent);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (uiOpen)
|
||||
{
|
||||
verb.Text = Loc.GetString("comp-storage-verb-close-storage");
|
||||
verb.Icon = new SpriteSpecifier.Texture(
|
||||
new("/Textures/Interface/VerbIcons/close.svg.192dpi.png"));
|
||||
}
|
||||
else
|
||||
{
|
||||
verb.Text = Loc.GetString("comp-storage-verb-open-storage");
|
||||
verb.Icon = new SpriteSpecifier.Texture(
|
||||
new("/Textures/Interface/VerbIcons/open.svg.192dpi.png"));
|
||||
}
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the storage UI for an entity
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity to open the UI for</param>
|
||||
public void OpenStorageUI(EntityUid uid, EntityUid entity, StorageComponent? storageComp = null, bool silent = false)
|
||||
{
|
||||
if (!Resolve(uid, ref storageComp, false))
|
||||
return;
|
||||
|
||||
// prevent spamming bag open / honkerton honk sound
|
||||
silent |= TryComp<UseDelayComponent>(uid, out var useDelay) && UseDelay.IsDelayed((uid, useDelay));
|
||||
if (!silent)
|
||||
{
|
||||
if (!_ui.IsUiOpen(uid, StorageComponent.StorageUiKey.Key))
|
||||
Audio.PlayPredicted(storageComp.StorageOpenSound, uid, entity);
|
||||
|
||||
if (useDelay != null)
|
||||
UseDelay.TryResetDelay((uid, useDelay));
|
||||
}
|
||||
|
||||
_ui.OpenUi(uid, StorageComponent.StorageUiKey.Key, entity);
|
||||
}
|
||||
|
||||
public virtual void UpdateUI(Entity<StorageComponent?> entity) {}
|
||||
|
||||
private void AddTransferVerbs(EntityUid uid, StorageComponent component, GetVerbsEvent<UtilityVerb> args)
|
||||
{
|
||||
@@ -261,7 +373,16 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
if (args.Handled || TryComp<LockComponent>(uid, out var lockComponent) && lockComponent.Locked)
|
||||
return;
|
||||
|
||||
OpenStorageUI(uid, args.User, storageComp);
|
||||
// Toggle
|
||||
if (_ui.IsUiOpen(uid, StorageComponent.StorageUiKey.Key, args.User))
|
||||
{
|
||||
_ui.CloseUi(uid, StorageComponent.StorageUiKey.Key, args.User);
|
||||
}
|
||||
else
|
||||
{
|
||||
OpenStorageUI(uid, args.User, storageComp);
|
||||
}
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
@@ -460,8 +581,7 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
if (!TryComp<StorageComponent>(uid, out var storageComp))
|
||||
return;
|
||||
|
||||
if (!_ui.TryGetUi(uid, StorageComponent.StorageUiKey.Key, out var bui) ||
|
||||
!bui.SubscribedSessions.Contains(args.SenderSession))
|
||||
if (!_ui.IsUiOpen(uid, StorageComponent.StorageUiKey.Key, player))
|
||||
return;
|
||||
|
||||
if (!Exists(entity))
|
||||
@@ -503,8 +623,7 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
if (!TryComp<StorageComponent>(storageEnt, out var storageComp))
|
||||
return;
|
||||
|
||||
if (!_ui.TryGetUi(storageEnt, StorageComponent.StorageUiKey.Key, out var bui) ||
|
||||
!bui.SubscribedSessions.Contains(args.SenderSession))
|
||||
if (!_ui.IsUiOpen(storageEnt, StorageComponent.StorageUiKey.Key, player))
|
||||
return;
|
||||
|
||||
if (!Exists(itemEnt))
|
||||
@@ -530,8 +649,7 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
if (!TryComp<StorageComponent>(storageEnt, out var storageComp))
|
||||
return;
|
||||
|
||||
if (!_ui.TryGetUi(storageEnt, StorageComponent.StorageUiKey.Key, out var bui) ||
|
||||
!bui.SubscribedSessions.Contains(args.SenderSession))
|
||||
if (!_ui.IsUiOpen(storageEnt, StorageComponent.StorageUiKey.Key, player))
|
||||
return;
|
||||
|
||||
if (!Exists(itemEnt))
|
||||
@@ -558,8 +676,7 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
if (!TryComp<StorageComponent>(storageEnt, out var storageComp))
|
||||
return;
|
||||
|
||||
if (!_ui.TryGetUi(storageEnt, StorageComponent.StorageUiKey.Key, out var bui) ||
|
||||
!bui.SubscribedSessions.Contains(args.SenderSession))
|
||||
if (!_ui.IsUiOpen(storageEnt, StorageComponent.StorageUiKey.Key, player))
|
||||
return;
|
||||
|
||||
if (!Exists(itemEnt))
|
||||
@@ -584,11 +701,10 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
var storage = GetEntity(msg.Storage);
|
||||
var item = GetEntity(msg.Item);
|
||||
|
||||
if (!TryComp<StorageComponent>(storage, out var storageComp))
|
||||
if (!HasComp<StorageComponent>(storage))
|
||||
return;
|
||||
|
||||
if (!_ui.TryGetUi(storage, StorageComponent.StorageUiKey.Key, out var bui) ||
|
||||
!bui.SubscribedSessions.Contains(args.SenderSession))
|
||||
if (!_ui.IsUiOpen(storage, StorageComponent.StorageUiKey.Key, player))
|
||||
return;
|
||||
|
||||
if (!Exists(item))
|
||||
@@ -605,11 +721,7 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
|
||||
private void OnBoundUIOpen(EntityUid uid, StorageComponent storageComp, BoundUIOpenedEvent args)
|
||||
{
|
||||
if (!storageComp.IsUiOpen)
|
||||
{
|
||||
storageComp.IsUiOpen = true;
|
||||
UpdateAppearance((uid, storageComp, null));
|
||||
}
|
||||
UpdateAppearance((uid, storageComp, null));
|
||||
}
|
||||
|
||||
private void OnEntInserted(Entity<StorageComponent> entity, ref EntInsertedIntoContainerMessage args)
|
||||
@@ -687,11 +799,13 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
var capacity = storage.Grid.GetArea();
|
||||
var used = GetCumulativeItemAreas((uid, storage));
|
||||
|
||||
var isOpen = _ui.IsUiOpen(entity.Owner, StorageComponent.StorageUiKey.Key);
|
||||
|
||||
_appearance.SetData(uid, StorageVisuals.StorageUsed, used, appearance);
|
||||
_appearance.SetData(uid, StorageVisuals.Capacity, capacity, appearance);
|
||||
_appearance.SetData(uid, StorageVisuals.Open, storage.IsUiOpen, appearance);
|
||||
_appearance.SetData(uid, SharedBagOpenVisuals.BagState, storage.IsUiOpen ? SharedBagState.Open : SharedBagState.Closed, appearance);
|
||||
_appearance.SetData(uid, StackVisuals.Hide, !storage.IsUiOpen, appearance);
|
||||
_appearance.SetData(uid, StorageVisuals.Open, isOpen, appearance);
|
||||
_appearance.SetData(uid, SharedBagOpenVisuals.BagState, isOpen ? SharedBagState.Open : SharedBagState.Closed, appearance);
|
||||
_appearance.SetData(uid, StackVisuals.Hide, !isOpen, appearance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1343,8 +1457,6 @@ public abstract class SharedStorageSystem : EntitySystem
|
||||
[Serializable, NetSerializable]
|
||||
protected sealed class StorageComponentState : ComponentState
|
||||
{
|
||||
public bool IsUiOpen;
|
||||
|
||||
public Dictionary<NetEntity, ItemStorageLocation> StoredItems = new();
|
||||
|
||||
public Dictionary<string, List<ItemStorageLocation>> SavedLocations = new();
|
||||
|
||||
Reference in New Issue
Block a user