public bool CanAccess(EntityUid actor, EntityUid target, EntityUid itemUid)
    {
        // Can the actor reach the target?
        if (actor != target && !(_interactionSystem.InRangeUnobstructed(actor, target) && _containerSystem.IsInSameOrParentContainer(actor, target)))
        {
            return(false);
        }

        // Can the actor reach the item?
        if (_interactionSystem.InRangeUnobstructed(actor, itemUid) && _containerSystem.IsInSameOrParentContainer(actor, itemUid))
        {
            return(true);
        }

        // Is the item in an open storage UI, i.e., is the user quick-equipping from an open backpack?
        if (_interactionSystem.CanAccessViaStorage(actor, itemUid))
        {
            return(true);
        }

        // Is the actor currently stripping the target? Here we could check if the actor has the stripping UI open, but
        // that requires server/client specific code. so lets just check if they **could** open the stripping UI.
        // Note that this doesn't check that the item is equipped by the target, as this is done elsewhere.
        return(actor != target &&
               TryComp(target, out SharedStrippableComponent? strip) &&
               strip.CanBeStripped(actor));
    }
    public bool ValidateEntityTarget(EntityUid user, EntityUid target, EntityTargetAction action)
    {
        if (!target.IsValid() || Deleted(target))
        {
            return(false);
        }

        if (action.Whitelist != null && !action.Whitelist.IsValid(target, EntityManager))
        {
            return(false);
        }

        if (action.CheckCanInteract && !_actionBlockerSystem.CanInteract(user, target))
        {
            return(false);
        }

        if (user == target)
        {
            return(action.CanTargetSelf);
        }

        if (!action.CheckCanAccess)
        {
            // even if we don't check for obstructions, we may still need to check the range.
            var xform       = Transform(user);
            var targetXform = Transform(target);

            if (xform.MapID != targetXform.MapID)
            {
                return(false);
            }

            if (action.Range <= 0)
            {
                return(true);
            }

            return((xform.WorldPosition - targetXform.WorldPosition).Length <= action.Range);
        }

        if (_interactionSystem.InRangeUnobstructed(user, target, range: action.Range) &&
            _containerSystem.IsInSameOrParentContainer(user, target))
        {
            return(true);
        }

        return(_interactionSystem.CanAccessViaStorage(user, target));
    }
        public void FlashArea(EntityUid source, EntityUid?user, float range, float duration, float slowTo = 0.8f, bool displayPopup = false, SoundSpecifier?sound = null)
        {
            var transform         = EntityManager.GetComponent <TransformComponent>(source);
            var mapPosition       = transform.MapPosition;
            var flashableEntities = new List <EntityUid>();
            var flashableQuery    = GetEntityQuery <FlashableComponent>();

            foreach (var entity in _entityLookup.GetEntitiesInRange(transform.Coordinates, range))
            {
                if (!flashableQuery.HasComponent(entity))
                {
                    continue;
                }

                flashableEntities.Add(entity);
            }

            foreach (var entity in flashableEntities)
            {
                // Check for unobstructed entities while ignoring the mobs with flashable components.
                if (!_interactionSystem.InRangeUnobstructed(entity, mapPosition, range, CollisionGroup.Opaque, (e) => flashableEntities.Contains(e)))
                {
                    continue;
                }

                // They shouldn't have flash removed in between right?
                Flash(entity, user, source, duration, slowTo, displayPopup, flashableQuery.GetComponent(entity));
            }
            if (sound != null)
            {
                SoundSystem.Play(sound.GetSound(), Filter.Pvs(transform), source);
            }
        }
        /// <summary>
        /// When activated, blasts everyone in LOS within n tiles
        /// with a high-probability disease infection attempt
        /// </summary>
        private void OnActivate(EntityUid uid, DiseaseArtifactComponent component, ArtifactActivatedEvent args)
        {
            if (component.SpawnDisease == null)
            {
                return;
            }

            var xform        = Transform(uid);
            var carrierQuery = GetEntityQuery <DiseaseCarrierComponent>();

            foreach (var entity in _lookup.GetEntitiesInRange(xform.Coordinates, component.Range))
            {
                if (!carrierQuery.TryGetComponent(entity, out var carrier))
                {
                    continue;
                }

                if (!_interactionSystem.InRangeUnobstructed(uid, entity, component.Range))
                {
                    continue;
                }

                _disease.TryInfect(carrier, component.SpawnDisease, forced: true);
            }
        }
        private void OnInteract(EntityUid uid, AirAlarmComponent component, InteractHandEvent args)
        {
            if (!_interactionSystem.InRangeUnobstructed(args.User, args.Target))
            {
                return;
            }

            if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor))
            {
                return;
            }

            if (EntityManager.TryGetComponent(uid, out WiresComponent wire) && wire.IsPanelOpen)
            {
                args.Handled = false;
                return;
            }

            if (EntityManager.TryGetComponent(uid, out ApcPowerReceiverComponent recv) && !recv.Powered)
            {
                return;
            }

            _uiSystem.GetUiOrNull(component.Owner, SharedAirAlarmInterfaceKey.Key)?.Open(actor.PlayerSession);
            component.ActivePlayers.Add(actor.PlayerSession.UserId);
            AddActiveInterface(uid);
            SendAddress(uid);
            SendAlarmMode(uid);
            SendThresholds(uid);
            SyncAllDevices(uid);
            SendAirData(uid);
        }
        /// <summary>
        /// Whether the table exists, and the player can interact with it.
        /// </summary>
        /// <param name="playerEntity">The player entity to check.</param>
        /// <param name="table">The table entity to check.</param>
        protected bool CanSeeTable(EntityUid playerEntity, EntityUid?table)
        {
            // Table may have been deleted, hence TryComp
            if (!TryComp(table, out MetaDataComponent? meta) ||
                meta.EntityLifeStage >= EntityLifeStage.Terminating ||
                (meta.Flags & MetaDataFlags.InContainer) == MetaDataFlags.InContainer)
            {
                return(false);
            }

            return(_interactionSystem.InRangeUnobstructed(playerEntity, table.Value) && _actionBlockerSystem.CanInteract(playerEntity, table));
        }
Exemple #7
0
        private void OnBeforeInteract(EntityUid uid, DoorRemoteComponent component, BeforeRangedInteractEvent args)
        {
            if (args.Handled ||
                args.Target == null ||
                !TryComp <DoorComponent>(args.Target, out var doorComp) || // If it isn't a door we don't use it
                !TryComp <AirlockComponent>(args.Target, out var airlockComp)   // Remotes only work on airlocks
                // The remote can be used anywhere the user can see the door.
                // This doesn't work that well, but I don't know of an alternative
                || !_interactionSystem.InRangeUnobstructed(args.User, args.Target.Value,
                                                           SharedInteractionSystem.MaxRaycastRange, CollisionGroup.Opaque))
            {
                return;
            }

            args.Handled = true;

            if (!this.IsPowered(args.Target.Value, EntityManager))
            {
                ShowPopupToUser("door-remote-no-power", args.User);
                return;
            }

            if (TryComp <AccessReaderComponent>(args.Target, out var accessComponent) &&
                !_doorSystem.HasAccess(doorComp.Owner, args.Used, accessComponent))
            {
                _doorSystem.Deny(airlockComp.Owner, doorComp, args.User);
                ShowPopupToUser("door-remote-denied", args.User);
                return;
            }

            switch (component.Mode)
            {
            case OperatingMode.OpenClose:
                _doorSystem.TryToggleDoor(doorComp.Owner, doorComp, args.Used);
                break;

            case OperatingMode.ToggleBolts:
                //TODO: What about cut wires...?
                airlockComp.SetBoltsWithAudio(!airlockComp.IsBolted());
                break;

            case OperatingMode.ToggleEmergencyAccess:
                _sharedAirlockSystem.ToggleEmergencyAccess(airlockComp);
                break;

            default:
                throw new InvalidOperationException(
                          $"{nameof(DoorRemoteComponent)} had invalid mode {component.Mode}");
            }
        }
        private void OnAfterInteract(EntityUid uid, DoorRemoteComponent component, AfterInteractEvent args)
        {
            if (args.Handled ||
                args.Target == null ||
                !TryComp <DoorComponent>(args.Target, out var doorComponent) || // If it isn't a door we don't use it
                !HasComp <AccessReaderComponent>(args.Target) || // Remotes do not work on doors without access requirements
                !TryComp <AirlockComponent>(args.Target, out var airlockComponent) || // Remotes only work on airlocks
                !_interactionSystem.InRangeUnobstructed(args.User, doorComponent.Owner, -1f, CollisionGroup.Opaque))
            {
                return;
            }

            args.Handled = true;

            if (component.Mode == DoorRemoteComponent.OperatingMode.OpenClose)
            {
                _sharedDoorSystem.TryToggleDoor(doorComponent.Owner, user: args.Used);
            }

            if (component.Mode == DoorRemoteComponent.OperatingMode.ToggleBolts &&
                airlockComponent.IsPowered())
            {
                if (_doorSystem.HasAccess(doorComponent.Owner, args.Used))
                {
                    airlockComponent.SetBoltsWithAudio(!airlockComponent.IsBolted());
                }
                else
                {
                    if (doorComponent.State != DoorState.Open)
                    {
                        _sharedDoorSystem.Deny(airlockComponent.Owner, user: args.User);
                    }
                    else if (doorComponent.DenySound != null)
                    {
                        SoundSystem.Play(Filter.Pvs(args.Target.Value), doorComponent.DenySound.GetSound(), args.Target.Value);
                    }
                }
            }

            if (component.Mode == DoorRemoteComponent.OperatingMode.ToggleEmergencyAccess &&
                airlockComponent.IsPowered())
            {
                if (_doorSystem.HasAccess(doorComponent.Owner, args.Used))
                {
                    _sharedAirlockSystem.ToggleEmergencyAccess(airlockComponent);
                }
            }
        }
    public override void Update(float frameTime)
    {
        base.Update(frameTime);

        foreach (var component in EntityManager.EntityQuery <NetworkConfiguratorComponent>())
        {
            if (component.ActiveDeviceList != null && EntityManager.EntityExists(component.ActiveDeviceList.Value) &&
                _interactionSystem.InRangeUnobstructed(component.Owner, component.ActiveDeviceList.Value))
            {
                return;
            }

            //The network configurator is a handheld device. There can only ever be an ui session open for the player holding the device.
            _uiSystem.GetUiOrNull(component.Owner, NetworkConfiguratorUiKey.Configure)?.CloseAll();
        }
    }
        /// <summary>
        /// When activated, blasts everyone in LOS within 3 tiles
        /// with a high-probability disease infection attempt
        /// </summary>
        private void OnActivate(EntityUid uid, DiseaseArtifactComponent component, ArtifactActivatedEvent args)
        {
            var xform = Transform(uid);

            foreach (var entity in _lookup.GetEntitiesInRange(xform.MapID, xform.WorldPosition, 3f))
            {
                if (!_interactionSystem.InRangeUnobstructed(uid, entity, 3f))
                {
                    continue;
                }

                if (TryComp <DiseaseCarrierComponent>(entity, out var carrier))
                {
                    EntitySystem.Get <DiseaseSystem>().TryInfect(carrier, component.ResolveDisease);
                }
            }
        }
    public bool CanOpen(EntityUid user, EntityUid target, bool silent = false, EntityStorageComponent?component = null)
    {
        if (!Resolve(target, ref component))
        {
            return(false);
        }

        if (!HasComp <SharedHandsComponent>(user))
        {
            return(false);
        }

        if (component.IsWeldedShut)
        {
            if (!silent && !component.Contents.Contains(user))
            {
                _popupSystem.PopupEntity(Loc.GetString("entity-storage-component-welded-shut-message"), target, Filter.Pvs(target));
            }

            return(false);
        }

        //Checks to see if the opening position, if offset, is inside of a wall.
        if (component.EnteringOffset != (0, 0)) //if the entering position is offset
        {
            var targetXform = Transform(target);
            var newCoords   = new EntityCoordinates(target, component.EnteringOffset);
            if (!_interactionSystem.InRangeUnobstructed(target, newCoords, collisionMask: component.EnteringOffsetCollisionFlags))
            {
                if (!silent)
                {
                    _popupSystem.PopupEntity(Loc.GetString("entity-storage-component-cannot-open-no-space"), target, Filter.Pvs(target));
                }

                return(false);
            }
        }

        var ev = new StorageOpenAttemptEvent(silent);

        RaiseLocalEvent(target, ev, true);

        return(!ev.Cancelled);
    }
Exemple #12
0
        /// <summary>
        ///     Inserts an Entity (<paramref name="toInsert"/>) in the world into storage, informing <paramref name="player"/> if it fails.
        ///     <paramref name="toInsert"/> is *NOT* held, see <see cref="PlayerInsertHeldEntity(Robust.Shared.GameObjects.EntityUid)"/>.
        /// </summary>
        /// <param name="player">The player to insert an entity with</param>
        /// <returns>true if inserted, false otherwise</returns>
        public bool PlayerInsertEntityInWorld(EntityUid uid, EntityUid player, EntityUid toInsert, ServerStorageComponent?storageComp = null)
        {
            if (!Resolve(uid, ref storageComp))
            {
                return(false);
            }

            if (!_sharedInteractionSystem.InRangeUnobstructed(player, uid, popup: storageComp.ShowPopup))
            {
                return(false);
            }

            if (!Insert(uid, toInsert, storageComp))
            {
                Popup(uid, player, "comp-storage-cant-insert", storageComp);
                return(false);
            }
            return(true);
        }
        /// <summary>
        /// Whether the table exists, and the player can interact with it.
        /// </summary>
        /// <param name="playerEntity">The player entity to check.</param>
        /// <param name="table">The table entity to check.</param>
        protected bool CanSeeTable(EntityUid playerEntity, EntityUid?table)
        {
            if (table == null)
            {
                return(false);
            }

            if (EntityManager.GetComponent <TransformComponent>(table.Value).Parent?.Owner is not {
            } parent)
            {
                return(false);
            }

            if (!EntityManager.HasComponent <MapComponent>(parent) && !EntityManager.HasComponent <IMapGridComponent>(parent))
            {
                return(false);
            }

            return(_interactionSystem.InRangeUnobstructed(playerEntity, table.Value) && _actionBlockerSystem.CanInteract(playerEntity, table));
        }
Exemple #14
0
        private void OnInteractHand(EntityUid uid, FireAlarmComponent component, InteractHandEvent args)
        {
            if (!_interactionSystem.InRangeUnobstructed(args.User, args.Target))
            {
                return;
            }

            if (EntityManager.TryGetComponent(args.User, out ActorComponent? actor) &&
                EntityManager.TryGetComponent(uid, out AtmosMonitorComponent? monitor) &&
                this.IsPowered(uid, EntityManager))
            {
                if (monitor.HighestAlarmInNetwork == AtmosMonitorAlarmType.Normal)
                {
                    _monitorSystem.Alert(uid, AtmosMonitorAlarmType.Danger);
                }
                else
                {
                    _monitorSystem.ResetAll(uid);
                }
            }
        }
Exemple #15
0
        private bool TryUseUtensil(EntityUid user, EntityUid target, UtensilComponent component)
        {
            if (!EntityManager.TryGetComponent(target, out FoodComponent food))
            {
                return(false);
            }

            //Prevents food usage with a wrong utensil
            if ((food.Utensil & component.Types) == 0)
            {
                _popupSystem.PopupEntity(Loc.GetString("food-system-wrong-utensil", ("food", food.Owner), ("utensil", component.Owner)), user, Filter.Entities(user));
                return(false);
            }

            if (!_interactionSystem.InRangeUnobstructed(user, target, popup: true))
            {
                return(false);
            }

            return(_foodSystem.TryFeed(user, target, food));
        }
Exemple #16
0
        public bool IsInDetailsRange(EntityUid examiner, EntityUid entity)
        {
            // check if the mob is in ciritcal or dead
            if (EntityManager.TryGetComponent(examiner, out MobStateComponent mobState) && mobState.IsIncapacitated())
            {
                return(false);
            }

            if (!_interactionSystem.InRangeUnobstructed(examiner, entity, ExamineDetailsRange))
            {
                return(false);
            }

            // Is the target hidden in a opaque locker or something? Currently this check allows players to examine
            // their organs, if they can somehow target them. Really this should be with userSeeInsideSelf: false, and a
            // separate check for if the item is in their inventory or hands.
            if (_containerSystem.IsInSameOrTransparentContainer(examiner, entity, userSeeInsideSelf: true))
            {
                return(true);
            }

            // is it inside of an open storage (e.g., an open backpack)?
            return(_interactionSystem.CanAccessViaStorage(examiner, entity));
        }
        /// <summary>
        ///     Raises a number of events in order to get all verbs of the given type(s) defined in local systems. This
        ///     does not request verbs from the server.
        /// </summary>
        public virtual Dictionary <VerbType, SortedSet <Verb> > GetLocalVerbs(EntityUid target, EntityUid user, VerbType verbTypes, bool force = false)
        {
            Dictionary <VerbType, SortedSet <Verb> > verbs = new();

            // accessibility checks
            bool canAccess = false;

            if (force || target == user)
            {
                canAccess = true;
            }
            else if (_interactionSystem.InRangeUnobstructed(user, target, ignoreInsideBlocker: true))
            {
                if (user.IsInSameOrParentContainer(target))
                {
                    canAccess = true;
                }
                else
                {
                    // the item might be in a backpack that the user has open
                    canAccess = _interactionSystem.CanAccessViaStorage(user, target);
                }
            }

            // A large number of verbs need to check action blockers. Instead of repeatedly having each system individually
            // call ActionBlocker checks, just cache it for the verb request.
            var canInteract = force || _actionBlockerSystem.CanInteract(user);

            EntityUid @using = default;

            if (EntityManager.TryGetComponent(user, out SharedHandsComponent? hands) && (force || _actionBlockerSystem.CanUse(user)))
            {
                hands.TryGetActiveHeldEntity(out @using);

                // Check whether the "Held" entity is a virtual pull entity. If yes, set that as the entity being "Used".
                // This allows you to do things like buckle a dragged person onto a surgery table, without click-dragging
                // their sprite.
                if (@using != default && EntityManager.TryGetComponent <HandVirtualItemComponent?>(@using, out var pull))
                {
                    @using = pull.BlockingEntity;
                }
            }

            if ((verbTypes & VerbType.Interaction) == VerbType.Interaction)
            {
                GetInteractionVerbsEvent getVerbEvent = new(user, target, @using, hands, canInteract, canAccess);
                RaiseLocalEvent(target, getVerbEvent);
                verbs.Add(VerbType.Interaction, getVerbEvent.Verbs);
            }

            if ((verbTypes & VerbType.Activation) == VerbType.Activation)
            {
                GetActivationVerbsEvent getVerbEvent = new(user, target, @using, hands, canInteract, canAccess);
                RaiseLocalEvent(target, getVerbEvent);
                verbs.Add(VerbType.Activation, getVerbEvent.Verbs);
            }

            if ((verbTypes & VerbType.Alternative) == VerbType.Alternative)
            {
                GetAlternativeVerbsEvent getVerbEvent = new(user, target, @using, hands, canInteract, canAccess);
                RaiseLocalEvent(target, getVerbEvent);
                verbs.Add(VerbType.Alternative, getVerbEvent.Verbs);
            }

            if ((verbTypes & VerbType.Other) == VerbType.Other)
            {
                GetOtherVerbsEvent getVerbEvent = new(user, target, @using, hands, canInteract, canAccess);
                RaiseLocalEvent(target, getVerbEvent);
                verbs.Add(VerbType.Other, getVerbEvent.Verbs);
            }

            return(verbs);
        }
        /// <summary>
        ///     Raises a number of events in order to get all verbs of the given type(s) defined in local systems. This
        ///     does not request verbs from the server.
        /// </summary>
        public SortedSet <Verb> GetLocalVerbs(EntityUid target, EntityUid user, List <Type> types, bool force = false)
        {
            SortedSet <Verb> verbs = new();

            // accessibility checks
            bool canAccess = false;

            if (force || target == user)
            {
                canAccess = true;
            }
            else if (EntityManager.EntityExists(target) && _interactionSystem.InRangeUnobstructed(user, target))
            {
                if (ContainerSystem.IsInSameOrParentContainer(user, target))
                {
                    canAccess = true;
                }
                else
                {
                    // the item might be in a backpack that the user has open
                    canAccess = _interactionSystem.CanAccessViaStorage(user, target);
                }
            }

            // A large number of verbs need to check action blockers. Instead of repeatedly having each system individually
            // call ActionBlocker checks, just cache it for the verb request.
            var canInteract = force || _actionBlockerSystem.CanInteract(user, target);

            EntityUid? @using = null;

            if (TryComp(user, out SharedHandsComponent? hands) && (force || _actionBlockerSystem.CanUseHeldEntity(user)))
            {
                @using = hands.ActiveHandEntity;

                // Check whether the "Held" entity is a virtual pull entity. If yes, set that as the entity being "Used".
                // This allows you to do things like buckle a dragged person onto a surgery table, without click-dragging
                // their sprite.

                if (TryComp(@using, out HandVirtualItemComponent? pull))
                {
                    @using = pull.BlockingEntity;
                }
            }

            if (types.Contains(typeof(InteractionVerb)))
            {
                var verbEvent = new GetVerbsEvent <InteractionVerb>(user, target, @using, hands, canInteract, canAccess);
                RaiseLocalEvent(target, verbEvent, true);
                verbs.UnionWith(verbEvent.Verbs);
            }

            if (types.Contains(typeof(UtilityVerb)) &&
                @using != null &&
                @using != target)
            {
                var verbEvent = new GetVerbsEvent <UtilityVerb>(user, target, @using, hands, canInteract, canAccess);
                RaiseLocalEvent(@using.Value, verbEvent, true); // directed at used, not at target
                verbs.UnionWith(verbEvent.Verbs);
            }

            if (types.Contains(typeof(InnateVerb)))
            {
                var verbEvent = new GetVerbsEvent <InnateVerb>(user, target, @using, hands, canInteract, canAccess);
                RaiseLocalEvent(user, verbEvent, true);
                verbs.UnionWith(verbEvent.Verbs);
            }

            if (types.Contains(typeof(AlternativeVerb)))
            {
                var verbEvent = new GetVerbsEvent <AlternativeVerb>(user, target, @using, hands, canInteract, canAccess);
                RaiseLocalEvent(target, verbEvent, true);
                verbs.UnionWith(verbEvent.Verbs);
            }

            if (types.Contains(typeof(ActivationVerb)))
            {
                var verbEvent = new GetVerbsEvent <ActivationVerb>(user, target, @using, hands, canInteract, canAccess);
                RaiseLocalEvent(target, verbEvent, true);
                verbs.UnionWith(verbEvent.Verbs);
            }

            if (types.Contains(typeof(ExamineVerb)))
            {
                var verbEvent = new GetVerbsEvent <ExamineVerb>(user, target, @using, hands, canInteract, canAccess);
                RaiseLocalEvent(target, verbEvent, true);
                verbs.UnionWith(verbEvent.Verbs);
            }

            // generic verbs
            if (types.Contains(typeof(Verb)))
            {
                var verbEvent = new GetVerbsEvent <Verb>(user, target, @using, hands, canInteract, canAccess);
                RaiseLocalEvent(target, verbEvent, true);
                verbs.UnionWith(verbEvent.Verbs);
            }

            return(verbs);
        }
        private bool IsRCDStillValid(RCDComponent rcd, AfterInteractEvent eventArgs, IMapGrid mapGrid, TileRef tile, RcdMode startingMode)
        {
            //Less expensive checks first. Failing those ones, we need to check that the tile isn't obstructed.
            if (rcd.CurrentAmmo <= 0)
            {
                _popup.PopupEntity(Loc.GetString("rcd-component-no-ammo-message"), rcd.Owner, Filter.Entities(eventArgs.User));
                return(false);
            }

            if (rcd.Mode != startingMode)
            {
                return(false);
            }

            var unobstructed = eventArgs.Target == null
                ? _interactionSystem.InRangeUnobstructed(eventArgs.User, mapGrid.GridTileToWorld(tile.GridIndices), popup : true)
                : _interactionSystem.InRangeUnobstructed(eventArgs.User, eventArgs.Target.Value, popup: true);

            if (!unobstructed)
            {
                return(false);
            }

            switch (rcd.Mode)
            {
            //Floor mode just needs the tile to be a space tile (subFloor)
            case RcdMode.Floors:
                if (!tile.Tile.IsEmpty)
                {
                    _popup.PopupEntity(Loc.GetString("rcd-component-cannot-build-floor-tile-not-empty-message"), rcd.Owner, Filter.Entities(eventArgs.User));
                    return(false);
                }

                return(true);

            //We don't want to place a space tile on something that's already a space tile. Let's do the inverse of the last check.
            case RcdMode.Deconstruct:
                if (tile.Tile.IsEmpty)
                {
                    return(false);
                }

                //They tried to decon a turf but the turf is blocked
                if (eventArgs.Target == null && tile.IsBlockedTurf(true))
                {
                    _popup.PopupEntity(Loc.GetString("rcd-component-tile-obstructed-message"), rcd.Owner, Filter.Entities(eventArgs.User));
                    return(false);
                }
                //They tried to decon a non-turf but it's not in the whitelist
                if (eventArgs.Target != null && !_tagSystem.HasTag(eventArgs.Target.Value, "RCDDeconstructWhitelist"))
                {
                    _popup.PopupEntity(Loc.GetString("rcd-component-deconstruct-target-not-on-whitelist-message"), rcd.Owner, Filter.Entities(eventArgs.User));
                    return(false);
                }

                return(true);

            //Walls are a special behaviour, and require us to build a new object with a transform rather than setting a grid tile, thus we early return to avoid the tile set code.
            case RcdMode.Walls:
                if (tile.Tile.IsEmpty)
                {
                    _popup.PopupEntity(Loc.GetString("rcd-component-cannot-build-wall-tile-not-empty-message"), rcd.Owner, Filter.Entities(eventArgs.User));
                    return(false);
                }

                if (tile.IsBlockedTurf(true))
                {
                    _popup.PopupEntity(Loc.GetString("rcd-component-tile-obstructed-message"), rcd.Owner, Filter.Entities(eventArgs.User));
                    return(false);
                }
                return(true);

            case RcdMode.Airlock:
                if (tile.Tile.IsEmpty)
                {
                    _popup.PopupEntity(Loc.GetString("rcd-component-cannot-build-airlock-tile-not-empty-message"), rcd.Owner, Filter.Entities(eventArgs.User));
                    return(false);
                }
                if (tile.IsBlockedTurf(true))
                {
                    _popup.PopupEntity(Loc.GetString("rcd-component-tile-obstructed-message"), rcd.Owner, Filter.Entities(eventArgs.User));
                    return(false);
                }
                return(true);

            default:
                return(false);    //I don't know why this would happen, but sure I guess. Get out of here invalid state!
            }
        }
        private bool TryDrink(EntityUid user, EntityUid target, DrinkComponent drink)
        {
            // cannot stack do-afters
            if (drink.CancelToken != null)
            {
                drink.CancelToken.Cancel();
                drink.CancelToken = null;
                return(true);
            }

            if (!EntityManager.HasComponent <SharedBodyComponent>(target))
            {
                return(false);
            }

            if (!drink.Opened)
            {
                _popupSystem.PopupEntity(Loc.GetString("drink-component-try-use-drink-not-open",
                                                       ("owner", EntityManager.GetComponent <MetaDataComponent>(drink.Owner).EntityName)), drink.Owner, Filter.Entities(user));
                return(true);
            }

            if (!_solutionContainerSystem.TryGetDrainableSolution(drink.Owner, out var drinkSolution) ||
                drinkSolution.DrainAvailable <= 0)
            {
                _popupSystem.PopupEntity(Loc.GetString("drink-component-try-use-drink-is-empty",
                                                       ("entity", EntityManager.GetComponent <MetaDataComponent>(drink.Owner).EntityName)), drink.Owner, Filter.Entities(user));
                return(true);
            }

            if (_foodSystem.IsMouthBlocked(target, user))
            {
                return(true);
            }

            if (!_interactionSystem.InRangeUnobstructed(user, drink.Owner, popup: true))
            {
                return(true);
            }

            var forceDrink = user != target;

            if (forceDrink)
            {
                EntityManager.TryGetComponent(user, out MetaDataComponent? meta);
                var userName = meta?.EntityName ?? string.Empty;

                _popupSystem.PopupEntity(Loc.GetString("drink-component-force-feed", ("user", userName)),
                                         user, Filter.Entities(target));

                // logging
                _logSystem.Add(LogType.ForceFeed, LogImpact.Medium, $"{ToPrettyString(user):user} is forcing {ToPrettyString(target):target} to drink {ToPrettyString(drink.Owner):drink} {SolutionContainerSystem.ToPrettyString(drinkSolution)}");
            }

            drink.CancelToken = new CancellationTokenSource();
            _doAfterSystem.DoAfter(new DoAfterEventArgs(user, forceDrink ? drink.ForceFeedDelay : drink.Delay, drink.CancelToken.Token, target)
            {
                BreakOnUserMove         = true,
                BreakOnDamage           = true,
                BreakOnStun             = true,
                BreakOnTargetMove       = true,
                MovementThreshold       = 0.01f,
                TargetFinishedEvent     = new DrinkEvent(user, drink, drinkSolution),
                BroadcastCancelledEvent = new DrinkCancelledEvent(drink),
                NeedHand = true,
            });

            return(true);
        }
        public bool TryFeed(EntityUid user, EntityUid target, FoodComponent food)
        {
            // if currently being used to feed, cancel that action.
            if (food.CancelToken != null)
            {
                food.CancelToken.Cancel();
                food.CancelToken = null;
                return(true);
            }

            if (food.Owner == user ||                                                                                  //Suppresses self-eating
                EntityManager.TryGetComponent <MobStateComponent>(food.Owner, out var mobState) && mobState.IsAlive()) // Suppresses eating alive mobs
            {
                return(false);
            }

            // Target can't be fed
            if (!EntityManager.HasComponent <SharedBodyComponent>(target))
            {
                return(false);
            }

            if (!_solutionContainerSystem.TryGetSolution(food.Owner, food.SolutionName, out var foodSolution))
            {
                return(false);
            }

            if (food.UsesRemaining <= 0)
            {
                _popupSystem.PopupEntity(Loc.GetString("food-system-try-use-food-is-empty",
                                                       ("entity", food.Owner)), user, Filter.Entities(user));
                DeleteAndSpawnTrash(food, user);
                return(false);
            }

            if (IsMouthBlocked(target, user))
            {
                return(false);
            }

            if (!TryGetRequiredUtensils(user, food, out var utensils))
            {
                return(false);
            }

            if (!_interactionSystem.InRangeUnobstructed(user, food.Owner, popup: true))
            {
                return(true);
            }

            var forceFeed = user != target;

            food.CancelToken = new CancellationTokenSource();

            if (forceFeed)
            {
                EntityManager.TryGetComponent(user, out MetaDataComponent? meta);
                var userName = meta?.EntityName ?? string.Empty;

                _popupSystem.PopupEntity(Loc.GetString("food-system-force-feed", ("user", userName)),
                                         user, Filter.Entities(target));

                // logging
                _logSystem.Add(LogType.ForceFeed, LogImpact.Medium, $"{ToPrettyString(user):user} is forcing {ToPrettyString(target):target} to eat {ToPrettyString(food.Owner):food} {SolutionContainerSystem.ToPrettyString(foodSolution)}");
            }

            var moveBreak = user != target;

            _doAfterSystem.DoAfter(new DoAfterEventArgs(user, forceFeed ? food.ForceFeedDelay : food.Delay, food.CancelToken.Token, target)
            {
                BreakOnUserMove         = moveBreak,
                BreakOnDamage           = true,
                BreakOnStun             = true,
                BreakOnTargetMove       = moveBreak,
                MovementThreshold       = 0.01f,
                TargetFinishedEvent     = new FeedEvent(user, food, foodSolution, utensils),
                BroadcastCancelledEvent = new ForceFeedCancelledEvent(food),
                NeedHand = true,
            });

            return(true);
        }