using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Shared.ActionBlocker;
-using Content.Shared.Administration;
using Content.Shared.Administration.Logs;
-using Content.Shared.Administration.Managers;
using Content.Shared.CombatMode;
using Content.Shared.Database;
+using Content.Shared.Ghost;
using Content.Shared.Hands;
using Content.Shared.Hands.Components;
using Content.Shared.Input;
using Content.Shared.Inventory.Events;
using Content.Shared.Item;
using Content.Shared.Movement.Components;
-using Content.Shared.Movement.Pulling.Components;
using Content.Shared.Movement.Pulling.Systems;
using Content.Shared.Physics;
using Content.Shared.Popups;
+using Content.Shared.Storage;
using Content.Shared.Tag;
using Content.Shared.Timing;
+using Content.Shared.UserInterface;
using Content.Shared.Verbs;
using Content.Shared.Wall;
using JetBrains.Annotations;
using Robust.Shared.Random;
using Robust.Shared.Serialization;
using Robust.Shared.Timing;
+using Robust.Shared.Utility;
#pragma warning disable 618
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly INetManager _net = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
- [Dependency] private readonly ISharedAdminManager _adminManager = default!;
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
[Dependency] private readonly RotateToFaceSystem _rotateToFaceSystem = default!;
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
- [Dependency] private readonly SharedPhysicsSystem _sharedBroadphaseSystem = default!;
+ [Dependency] private readonly SharedPhysicsSystem _broadphase = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly SharedVerbSystem _verbSystem = default!;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
[Dependency] private readonly InventorySystem _inventory = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly TagSystem _tagSystem = default!;
+ [Dependency] private readonly SharedUserInterfaceSystem _ui = default!;
+
+ private EntityQuery<IgnoreUIRangeComponent> _ignoreUiRangeQuery;
+ private EntityQuery<FixturesComponent> _fixtureQuery;
+ private EntityQuery<ItemComponent> _itemQuery;
+ private EntityQuery<PhysicsComponent> _physicsQuery;
+ private EntityQuery<HandsComponent> _handsQuery;
+ private EntityQuery<InteractionRelayComponent> _relayQuery;
+ private EntityQuery<CombatModeComponent> _combatQuery;
+ private EntityQuery<WallMountComponent> _wallMountQuery;
+ private EntityQuery<UseDelayComponent> _delayQuery;
+ private EntityQuery<ActivatableUIComponent> _uiQuery;
private const CollisionGroup InRangeUnobstructedMask = CollisionGroup.Impassable | CollisionGroup.InteractImpassable;
public override void Initialize()
{
+ _ignoreUiRangeQuery = GetEntityQuery<IgnoreUIRangeComponent>();
+ _fixtureQuery = GetEntityQuery<FixturesComponent>();
+ _itemQuery = GetEntityQuery<ItemComponent>();
+ _physicsQuery = GetEntityQuery<PhysicsComponent>();
+ _handsQuery = GetEntityQuery<HandsComponent>();
+ _relayQuery = GetEntityQuery<InteractionRelayComponent>();
+ _combatQuery = GetEntityQuery<CombatModeComponent>();
+ _wallMountQuery = GetEntityQuery<WallMountComponent>();
+ _delayQuery = GetEntityQuery<UseDelayComponent>();
+ _uiQuery = GetEntityQuery<ActivatableUIComponent>();
+
SubscribeLocalEvent<BoundUserInterfaceCheckRangeEvent>(HandleUserInterfaceRangeCheck);
SubscribeLocalEvent<BoundUserInterfaceMessageAttempt>(OnBoundInterfaceInteractAttempt);
/// </summary>
private void OnBoundInterfaceInteractAttempt(BoundUserInterfaceMessageAttempt ev)
{
- var user = ev.Actor;
+ _uiQuery.TryComp(ev.Target, out var uiComp);
+ if (!_actionBlockerSystem.CanInteract(ev.Actor, ev.Target))
+ {
+ // We permit ghosts to open uis unless explicitly blocked
+ if (ev.Message is not OpenBoundInterfaceMessage || !HasComp<GhostComponent>(ev.Actor) || uiComp?.BlockSpectators == true)
+ {
+ ev.Cancel();
+ return;
+ }
+ }
+
+ var range = _ui.GetUiRange(ev.Target, ev.UiKey);
- if (!_actionBlockerSystem.CanInteract(user, ev.Target))
+ // As long as range>0, the UI frame updates should have auto-closed the UI if it is out of range.
+ DebugTools.Assert(range <= 0 || UiRangeCheck(ev.Actor, ev.Target, range));
+
+ if (range <= 0 && !IsAccessible(ev.Actor, ev.Target))
{
ev.Cancel();
return;
}
- // Check if the bound entity is accessible. Note that we allow admins to ignore this restriction, so that
- // they can fiddle with UI's that people can't normally interact with (e.g., placing things directly into
- // other people's backpacks).
- if (!_containerSystem.IsInSameOrParentContainer(user, ev.Target)
- && !CanAccessViaStorage(user, ev.Target)
- && !_adminManager.HasAdminFlag(user, AdminFlags.Admin))
+ if (uiComp == null)
+ return;
+
+ if (uiComp.SingleUser && uiComp.CurrentSingleUser != ev.Actor)
{
ev.Cancel();
return;
}
- if (!InRangeUnobstructed(user, ev.Target))
- {
+ if (!uiComp.RequireHands)
+ return;
+
+ if (!_handsQuery.TryComp(ev.Actor, out var hands) || hands.Hands.Count == 0)
ev.Cancel();
- }
+ }
+
+ private bool UiRangeCheck(Entity<TransformComponent?> user, Entity<TransformComponent?> target, float range)
+ {
+ if (!Resolve(target, ref target.Comp))
+ return false;
+
+ if (user.Owner == target.Owner)
+ return true;
+
+ // Fast check: if the user is the parent of the entity (e.g., holding it), we always assume that it is in range
+ if (target.Comp.ParentUid == user.Owner)
+ return true;
+
+ return InRangeAndAccessible(user, target, range) || _ignoreUiRangeQuery.HasComp(user);
}
/// <summary>
if (!InRangeUnobstructed(userEntity.Value, uid, popup: true))
return false;
- if (!TryComp(uid, out PullableComponent? pull))
- return false;
-
- _pullSystem.TogglePull(uid, userEntity.Value, pull);
+ _pullSystem.TogglePull(uid, userEntity.Value);
return false;
}
public bool CombatModeCanHandInteract(EntityUid user, EntityUid? target)
{
// Always allow attack in these cases
- if (target == null || !TryComp<HandsComponent>(user, out var hands) || hands.ActiveHand?.HeldEntity is not null)
+ if (target == null || !_handsQuery.TryComp(user, out var hands) || hands.ActiveHand?.HeldEntity is not null)
return false;
// Only eat input if:
// - Target doesn't cancel should-interact event
// This is intended to allow items to be picked up in combat mode,
// but to also allow items to force attacks anyway (like mobs which are items, e.g. mice)
- if (!HasComp<ItemComponent>(target))
+ if (!_itemQuery.HasComp(target))
return false;
var combatEv = new CombatModeShouldHandInteractEvent();
bool checkAccess = true,
bool checkCanUse = true)
{
- if (TryComp<InteractionRelayComponent>(user, out var relay) && relay.RelayEntity is not null)
+ if (_relayQuery.TryComp(user, out var relay) && relay.RelayEntity is not null)
{
// TODO this needs to be handled better. This probably bypasses many complex can-interact checks in weird roundabout ways.
if (_actionBlockerSystem.CanInteract(user, target))
if (target != null && Deleted(target.Value))
return;
- if (!altInteract && TryComp<CombatModeComponent>(user, out var combatMode) && combatMode.IsInCombatMode)
+ if (!altInteract && _combatQuery.TryComp(user, out var combatMode) && combatMode.IsInCombatMode)
{
if (!CombatModeCanHandInteract(user, target))
return;
// Check if interacted entity is in the same container, the direct child, or direct parent of the user.
// Also checks if the item is accessible via some storage UI (e.g., open backpack)
- if (checkAccess
- && target != null
- && !_containerSystem.IsInSameOrParentContainer(user, target.Value)
- && !CanAccessViaStorage(user, target.Value))
+ if (checkAccess && target != null && !IsAccessible(user, target.Value))
return;
var inRangeUnobstructed = target == null
: !checkAccess || InRangeUnobstructed(user, target.Value); // permits interactions with wall mounted entities
// Does the user have hands?
- if (!TryComp<HandsComponent>(user, out var hands) || hands.ActiveHand == null)
+ if (!_handsQuery.TryComp(user, out var hands) || hands.ActiveHand == null)
{
var ev = new InteractNoHandEvent(user, target, coordinates);
RaiseLocalEvent(user, ev);
predicate ??= _ => false;
var ray = new CollisionRay(origin.Position, dir.Normalized(), collisionMask);
- var rayResults = _sharedBroadphaseSystem.IntersectRayWithPredicate(origin.MapId, ray, dir.Length(), predicate.Invoke, false).ToList();
+ var rayResults = _broadphase.IntersectRayWithPredicate(origin.MapId, ray, dir.Length(), predicate.Invoke, false).ToList();
if (rayResults.Count == 0)
return dir.Length();
}
var ray = new CollisionRay(origin.Position, dir.Normalized(), (int) collisionMask);
- var rayResults = _sharedBroadphaseSystem.IntersectRayWithPredicate(origin.MapId, ray, length, predicate.Invoke, false).ToList();
+ var rayResults = _broadphase.IntersectRayWithPredicate(origin.MapId, ray, length, predicate.Invoke, false).ToList();
return rayResults.Count == 0;
}
public bool InRangeUnobstructed(
- EntityUid origin,
- EntityUid other,
+ Entity<TransformComponent?> origin,
+ Entity<TransformComponent?> other,
float range = InteractionRange,
CollisionGroup collisionMask = InRangeUnobstructedMask,
Ignored? predicate = null,
bool popup = false)
{
- if (!TryComp(other, out TransformComponent? otherXform))
+ if (!Resolve(other, ref other.Comp))
return false;
- return InRangeUnobstructed(origin, other, otherXform.Coordinates, otherXform.LocalRotation, range, collisionMask, predicate,
+ return InRangeUnobstructed(origin,
+ other,
+ other.Comp.Coordinates,
+ other.Comp.LocalRotation,
+ range,
+ collisionMask,
+ predicate,
popup);
}
/// True if the two points are within a given range without being obstructed.
/// </returns>
public bool InRangeUnobstructed(
- EntityUid origin,
- EntityUid other,
+ Entity<TransformComponent?> origin,
+ Entity<TransformComponent?> other,
EntityCoordinates otherCoordinates,
Angle otherAngle,
float range = InteractionRange,
Ignored? predicate = null,
bool popup = false)
{
- Ignored combinedPredicate = e => e == origin || (predicate?.Invoke(e) ?? false);
+ Ignored combinedPredicate = e => e == origin.Owner || (predicate?.Invoke(e) ?? false);
var inRange = true;
MapCoordinates originPos = default;
- var targetPos = otherCoordinates.ToMap(EntityManager, _transform);
+ var targetPos = _transform.ToMapCoordinates(otherCoordinates);
Angle targetRot = default;
// So essentially:
// Alternatively we could check centre distances first though
// that means we wouldn't be able to easily check overlap interactions.
if (range > 0f &&
- TryComp<FixturesComponent>(origin, out var fixtureA) &&
+ _fixtureQuery.TryComp(origin, out var fixtureA) &&
// These fixture counts are stuff that has the component but no fixtures for <reasons> (e.g. buttons).
// At least until they get removed.
fixtureA.FixtureCount > 0 &&
- TryComp<FixturesComponent>(other, out var fixtureB) &&
+ _fixtureQuery.TryComp(other, out var fixtureB) &&
fixtureB.FixtureCount > 0 &&
- TryComp(origin, out TransformComponent? xformA))
+ Resolve(origin, ref origin.Comp))
{
- var (worldPosA, worldRotA) = xformA.GetWorldPositionRotation();
+ var (worldPosA, worldRotA) = origin.Comp.GetWorldPositionRotation();
var xfA = new Transform(worldPosA, worldRotA);
var parentRotB = _transform.GetWorldRotation(otherCoordinates.EntityId);
var xfB = new Transform(targetPos.Position, parentRotB + otherAngle);
// Different map or the likes.
- if (!_sharedBroadphaseSystem.TryGetNearest(origin, other,
- out _, out _, out var distance,
- xfA, xfB, fixtureA, fixtureB))
+ if (!_broadphase.TryGetNearest(
+ origin,
+ other,
+ out _,
+ out _,
+ out var distance,
+ xfA,
+ xfB,
+ fixtureA,
+ fixtureB))
{
inRange = false;
}
else
{
// We'll still do the raycast from the centres but we'll bump the range as we know they're in range.
- originPos = _transform.GetMapCoordinates(origin, xform: xformA);
+ originPos = _transform.GetMapCoordinates(origin, xform: origin.Comp);
range = (originPos.Position - targetPos.Position).Length();
}
}
// No fixtures, e.g. wallmounts.
else
{
- originPos = _transform.GetMapCoordinates(origin);
- var otherParent = Transform(other).ParentUid;
+ originPos = _transform.GetMapCoordinates(origin, origin);
+ var otherParent = (other.Comp ?? Transform(other)).ParentUid;
targetRot = otherParent.IsValid() ? Transform(otherParent).LocalRotation + otherAngle : otherAngle;
}
{
HashSet<EntityUid> ignored = new();
- if (HasComp<ItemComponent>(target) && TryComp(target, out PhysicsComponent? physics) && physics.CanCollide)
+ if (_itemQuery.HasComp(target) && _physicsQuery.TryComp(target, out var physics) && physics.CanCollide)
{
// If the target is an item, we ignore any colliding entities. Currently done so that if items get stuck
// inside of walls, users can still pick them up.
- ignored.UnionWith(_sharedBroadphaseSystem.GetEntitiesIntersectingBody(target, (int) collisionMask, false, physics));
+ ignored.UnionWith(_broadphase.GetEntitiesIntersectingBody(target, (int) collisionMask, false, physics));
}
- else if (TryComp(target, out WallMountComponent? wallMount))
+ else if (_wallMountQuery.TryComp(target, out var wallMount))
{
// wall-mount exemptions may be restricted to a specific angle range.da
ignored.UnionWith(grid.GetAnchoredEntities(targetCoords));
}
- Ignored combinedPredicate = e =>
- {
- return e == target
- || (predicate?.Invoke(e) ?? false)
- || ignored.Contains(e);
- };
-
+ Ignored combinedPredicate = e => e == target || (predicate?.Invoke(e) ?? false) || ignored.Contains(e);
return combinedPredicate;
}
bool checkUseDelay = true,
bool checkAccess = true)
{
- UseDelayComponent? delayComponent = null;
- if (checkUseDelay
- && TryComp(used, out delayComponent)
- && _useDelay.IsDelayed((used, delayComponent)))
+ _delayQuery.TryComp(used, out var delayComponent);
+ if (checkUseDelay && delayComponent != null && _useDelay.IsDelayed((used, delayComponent)))
return false;
if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, used))
// Check if interacted entity is in the same container, the direct child, or direct parent of the user.
// This is bypassed IF the interaction happened through an item slot (e.g., backpack UI)
- if (checkAccess && !_containerSystem.IsInSameOrParentContainer(user, used) && !CanAccessViaStorage(user, used))
+ if (checkAccess && !IsAccessible(user, used))
return false;
// Does the user have hands?
- if (!HasComp<HandsComponent>(user))
+ if (!_handsQuery.HasComp(user))
return false;
var activateMsg = new ActivateInWorldEvent(user, used);
DoContactInteraction(user, used, activateMsg);
// Still need to call this even without checkUseDelay in case this gets relayed from Activate.
- _useDelay.TryResetDelay(used, component: delayComponent);
+ if (delayComponent != null)
+ _useDelay.TryResetDelay(used, component: delayComponent);
+
if (!activateMsg.WasLogged)
_adminLogger.Add(LogType.InteractActivate, LogImpact.Low, $"{ToPrettyString(user):user} activated {ToPrettyString(used):used}");
return true;
bool checkCanInteract = true,
bool checkUseDelay = true)
{
- UseDelayComponent? delayComponent = null;
-
- if (checkUseDelay
- && TryComp(used, out delayComponent)
- && _useDelay.IsDelayed((used, delayComponent)))
+ _delayQuery.TryComp(used, out var delayComponent);
+ if (checkUseDelay && delayComponent != null && _useDelay.IsDelayed((used, delayComponent)))
return true; // if the item is on cooldown, we consider this handled.
if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, used))
}
#endregion
+ /// <summary>
+ /// Check if a user can access a target (stored in the same containers) and is in range without obstructions.
+ /// </summary>
+ public bool InRangeAndAccessible(
+ Entity<TransformComponent?> user,
+ Entity<TransformComponent?> target,
+ float range = InteractionRange,
+ CollisionGroup collisionMask = InRangeUnobstructedMask,
+ Ignored? predicate = null)
+ {
+ if (user == target)
+ return true;
+
+ if (!Resolve(user, ref user.Comp))
+ return false;
+
+ if (!Resolve(target, ref target.Comp))
+ return false;
+
+ return IsAccessible(user, target) && InRangeUnobstructed(user, target, range, collisionMask, predicate);
+ }
+
+ /// <summary>
+ /// Check if a user can access a target or if they are stored in different containers.
+ /// </summary>
+ public bool IsAccessible(Entity<TransformComponent?> user, Entity<TransformComponent?> target)
+ {
+ if (_containerSystem.IsInSameOrParentContainer(user, target, out _, out var container))
+ return true;
+
+ return container != null && CanAccessViaStorage(user, target, container);
+ }
+
/// <summary>
/// If a target is in range, but not in the same container as the user, it may be inside of a backpack. This
/// checks if the user can access the item in these situations.
/// </summary>
- public abstract bool CanAccessViaStorage(EntityUid user, EntityUid target);
+ public bool CanAccessViaStorage(EntityUid user, EntityUid target)
+ {
+ if (!_containerSystem.TryGetContainingContainer(target, out var container))
+ return false;
+
+ return CanAccessViaStorage(user, target, container);
+ }
+
+ /// <inheritdoc cref="CanAccessViaStorage(Robust.Shared.GameObjects.EntityUid,Robust.Shared.GameObjects.EntityUid)"/>
+ public bool CanAccessViaStorage(EntityUid user, EntityUid target, BaseContainer container)
+ {
+ if (StorageComponent.ContainerId != container.ID)
+ return false;
+
+ // we don't check if the user can access the storage entity itself. This should be handed by the UI system.
+ return _ui.IsUiOpen(target, StorageComponent.StorageUiKey.Key, user);
+ }
/// <summary>
/// Checks whether an entity currently equipped by another player is accessible to some user. This shouldn't
RaiseLocalEvent(uidB.Value, new ContactInteractionEvent(uidA));
}
+
private void HandleUserInterfaceRangeCheck(ref BoundUserInterfaceCheckRangeEvent ev)
{
if (ev.Result == BoundUserInterfaceRangeResult.Fail)
return;
- if (InRangeUnobstructed(ev.Actor, ev.Target, ev.Data.InteractionRange))
- {
- ev.Result = BoundUserInterfaceRangeResult.Pass;
- }
- else
- {
- ev.Result = BoundUserInterfaceRangeResult.Fail;
- }
+ ev.Result = UiRangeCheck(ev.Actor!, ev.Target, ev.Data.InteractionRange)
+ ? BoundUserInterfaceRangeResult.Pass
+ : BoundUserInterfaceRangeResult.Fail;
}
}
using Content.Shared.Interaction.Events;
using Content.Shared.Popups;
using Content.Shared.Verbs;
-using Robust.Shared.Containers;
+using Robust.Shared.Utility;
namespace Content.Shared.UserInterface;
[Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
[Dependency] private readonly SharedHandsSystem _hands = default!;
- [Dependency] private readonly SharedContainerSystem _container = default!;
- [Dependency] private readonly SharedInteractionSystem _interaction = default!;
-
- private readonly List<EntityUid> _toClose = new();
public override void Initialize()
{
base.Initialize();
+ SubscribeLocalEvent<ActivatableUIComponent, ComponentStartup>(OnStartup);
SubscribeLocalEvent<ActivatableUIComponent, UseInHandEvent>(OnUseInHand);
SubscribeLocalEvent<ActivatableUIComponent, ActivateInWorldEvent>(OnActivate);
SubscribeLocalEvent<ActivatableUIComponent, InteractUsingEvent>(OnInteractUsing);
SubscribeLocalEvent<ActivatableUIComponent, GetVerbsEvent<ActivationVerb>>(GetActivationVerb);
SubscribeLocalEvent<ActivatableUIComponent, GetVerbsEvent<Verb>>(GetVerb);
- // TODO ActivatableUI
- // Add UI-user component, and listen for user container changes.
- // I.e., should lose a computer UI if a player gets shut into a locker.
- SubscribeLocalEvent<ActivatableUIComponent, EntGotInsertedIntoContainerMessage>(OnGotInserted);
- SubscribeLocalEvent<ActivatableUIComponent, EntGotRemovedFromContainerMessage>(OnGotRemoved);
-
- SubscribeLocalEvent<BoundUserInterfaceMessageAttempt>(OnBoundInterfaceInteractAttempt);
SubscribeLocalEvent<UserInterfaceComponent, OpenUiActionEvent>(OnActionPerform);
InitializePower();
}
- private void OnBoundInterfaceInteractAttempt(BoundUserInterfaceMessageAttempt ev)
+ private void OnStartup(Entity<ActivatableUIComponent> ent, ref ComponentStartup args)
{
- if (!TryComp(ev.Target, out ActivatableUIComponent? comp))
- return;
-
- if (!comp.RequireHands)
+ if (ent.Comp.Key == null)
+ {
+ Log.Error($"Missing UI Key for entity: {ToPrettyString(ent)}");
return;
+ }
- if (!TryComp(ev.Actor, out HandsComponent? hands) || hands.Hands.Count == 0)
- ev.Cancel();
+ // TODO BUI
+ // set interaction range to zero to avoid constant range checks.
+ //
+ // if (ent.Comp.InHandsOnly && _uiSystem.TryGetInterfaceData(ent.Owner, ent.Comp.Key, out var data))
+ // data.InteractionRange = 0;
}
private void OnActionPerform(EntityUid uid, UserInterfaceComponent component, OpenUiActionEvent args)
args.Verbs.Add(new ActivationVerb
{
- // TODO VERBS add "open UI" icon
Act = () => InteractUI(args.User, uid, component),
- Text = Loc.GetString(component.VerbText)
+ Text = Loc.GetString(component.VerbText),
+ // TODO VERB ICON find a better icon
+ Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/settings.svg.192dpi.png")),
});
}
args.Verbs.Add(new Verb
{
- // TODO VERBS add "open UI" icon
Act = () => InteractUI(args.User, uid, component),
- Text = Loc.GetString(component.VerbText)
+ Text = Loc.GetString(component.VerbText),
+ // TODO VERB ICON find a better icon
+ Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/settings.svg.192dpi.png")),
});
}
}
}
- return args.CanInteract || component.AllowSpectator && HasComp<GhostComponent>(args.User);
+ return args.CanInteract || HasComp<GhostComponent>(args.User) && !component.BlockSpectators;
}
private void OnUseInHand(EntityUid uid, ActivatableUIComponent component, UseInHandEvent args)
return true;
}
- if (!_blockerSystem.CanInteract(user, uiEntity) && (!aui.AllowSpectator || !HasComp<GhostComponent>(user)))
+ if (!_blockerSystem.CanInteract(user, uiEntity) && (!HasComp<GhostComponent>(user) || aui.BlockSpectators))
return false;
if (aui.RequireHands)
if (ent.Comp.RequireHands && ent.Comp.InHandsOnly)
CloseAll(ent, ent);
}
-
- private void OnGotInserted(Entity<ActivatableUIComponent> ent, ref EntGotInsertedIntoContainerMessage args)
- {
- CheckAccess((ent, ent));
- }
-
- private void OnGotRemoved(Entity<ActivatableUIComponent> ent, ref EntGotRemovedFromContainerMessage args)
- {
- CheckAccess((ent, ent));
- }
-
- public void CheckAccess(Entity<ActivatableUIComponent?> ent)
- {
- if (!Resolve(ent, ref ent.Comp))
- return;
-
- if (ent.Comp.Key == null)
- {
- Log.Error($"Encountered null key in activatable ui on entity {ToPrettyString(ent)}");
- return;
- }
-
- foreach (var user in _uiSystem.GetActors(ent.Owner, ent.Comp.Key))
- {
- if (!_container.IsInSameOrParentContainer(user, ent)
- && !_interaction.CanAccessViaStorage(user, ent))
- {
- _toClose.Add(user);
- continue;
-
- }
-
- if (!_interaction.InRangeUnobstructed(user, ent))
- _toClose.Add(user);
- }
-
- foreach (var user in _toClose)
- {
- _uiSystem.CloseUi(ent.Owner, ent.Comp.Key, user);
- }
-
- _toClose.Clear();
- }
}