/// <summary>
        /// Checks if the user can vault the dragged entity onto the the target
        /// </summary>
        /// <param name="user">The user that wants to vault the entity</param>
        /// <param name="dragged">The entity that is being vaulted</param>
        /// <param name="target">The object that is being vaulted onto</param>
        /// <param name="reason">The reason why it cant be dropped</param>
        /// <returns></returns>
        private bool CanVault(EntityUid user, EntityUid dragged, EntityUid target, out string reason)
        {
            if (!EntitySystem.Get <ActionBlockerSystem>().CanInteract(user))
            {
                reason = Loc.GetString("comp-climbable-cant-interact");
                return(false);
            }

            if (!_entities.HasComponent <ClimbingComponent>(dragged))
            {
                reason = Loc.GetString("comp-climbable-cant-climb");
                return(false);
            }

            bool Ignored(EntityUid entity) => entity == target || entity == user || entity == dragged;

            if (!user.InRangeUnobstructed(target, Range, predicate: Ignored) ||
                !user.InRangeUnobstructed(dragged, Range, predicate: Ignored))
            {
                reason = Loc.GetString("comp-climbable-cant-reach");
                return(false);
            }

            reason = string.Empty;
            return(true);
        }
        /// <summary>
        /// Checks if the user can vault the target
        /// </summary>
        /// <param name="user">The entity that wants to vault</param>
        /// <param name="target">The object that is being vaulted</param>
        /// <param name="reason">The reason why it cant be dropped</param>
        /// <returns></returns>
        private bool CanVault(EntityUid user, EntityUid target, out string reason)
        {
            if (!EntitySystem.Get <ActionBlockerSystem>().CanInteract(user))
            {
                reason = Loc.GetString("comp-climbable-cant-interact");
                return(false);
            }

            if (!_entities.HasComponent <ClimbingComponent>(user) ||
                !_entities.TryGetComponent(user, out SharedBodyComponent? body))
            {
                reason = Loc.GetString("comp-climbable-cant-climb");
                return(false);
            }

            if (!body.HasPartOfType(BodyPartType.Leg) ||
                !body.HasPartOfType(BodyPartType.Foot))
            {
                reason = Loc.GetString("comp-climbable-cant-climb");
                return(false);
            }

            if (!user.InRangeUnobstructed(target, Range))
            {
                reason = Loc.GetString("comp-climbable-cant-reach");
                return(false);
            }

            reason = string.Empty;
            return(true);
        }
        public override Outcome Execute(float frameTime)
        {
            if (!_target.TryGetContainer(out var container))
            {
                return(Outcome.Success);
            }

            if (!_owner.InRangeUnobstructed(container, popup: true))
            {
                return(Outcome.Failed);
            }

            if (!IoCManager.Resolve <IEntityManager>().TryGetComponent(container.Owner, out EntityStorageComponent? storageComponent) ||
                storageComponent.IsWeldedShut)
            {
                return(Outcome.Failed);
            }

            if (!storageComponent.Open)
            {
                var activateArgs = new ActivateEventArgs(_owner, _target);
                storageComponent.Activate(activateArgs);
            }

            var blackboard = UtilityAiHelpers.GetBlackboard(_owner);

            blackboard?.GetState <LastOpenedStorageState>().SetValue(container.Owner);

            return(Outcome.Success);
        }
        public async void TryPryTile(EntityUid user, EntityCoordinates clickLocation)
        {
            if (!_entMan.TryGetComponent <ToolComponent?>(Owner, out var tool) && _toolComponentNeeded)
            {
                return;
            }

            if (!_mapManager.TryGetGrid(clickLocation.GetGridId(_entMan), out var mapGrid))
            {
                return;
            }

            var tile = mapGrid.GetTileRef(clickLocation);

            var coordinates = mapGrid.GridTileToLocal(tile.GridIndices);

            if (!user.InRangeUnobstructed(coordinates, popup: false))
            {
                return;
            }

            var tileDef = (ContentTileDefinition)_tileDefinitionManager[tile.Tile.TypeId];

            if (!tileDef.CanCrowbar)
            {
                return;
            }

            if (_toolComponentNeeded && !await EntitySystem.Get <ToolSystem>().UseTool(Owner, user, null, 0f, 0f, _qualityNeeded, toolComponent: tool))
            {
                return;
            }

            coordinates.PryTile(_entMan, _mapManager);
        }
        public override Outcome Execute(float frameTime)
        {
            var targetTransform = _entMan.GetComponent <TransformComponent>(_useTarget);

            if (targetTransform.GridID != _entMan.GetComponent <TransformComponent>(_owner).GridID)
            {
                return(Outcome.Failed);
            }

            if (!_owner.InRangeUnobstructed(_useTarget, popup: true))
            {
                return(Outcome.Failed);
            }

            if (_entMan.TryGetComponent(_owner, out CombatModeComponent? combatModeComponent))
            {
                combatModeComponent.IsInCombatMode = false;
            }

            // Click on da thing
            var interactionSystem = EntitySystem.Get <InteractionSystem>();

            interactionSystem.AiUseInteraction(_owner, targetTransform.Coordinates, _useTarget);

            return(Outcome.Success);
        }
Beispiel #6
0
        private 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 (entity.TryGetContainerMan(out var man) && man.Owner == examiner)
            {
                return(true);
            }

            return(examiner.InRangeUnobstructed(entity, ExamineDetailsRange, ignoreInsideBlocker: true) &&
                   examiner.IsInSameOrNoContainer(entity));
        }
Beispiel #7
0
        public override Outcome Execute(float frameTime)
        {
            var entMan = IoCManager.Resolve <IEntityManager>();

            if (entMan.Deleted(_target) ||
                !entMan.HasComponent <SharedItemComponent>(_target) ||
                _target.IsInContainer() ||
                !_owner.InRangeUnobstructed(_target, popup: true))
            {
                return(Outcome.Failed);
            }

            if (!entMan.TryGetComponent(_owner, out HandsComponent? handsComponent))
            {
                return(Outcome.Failed);
            }

            var emptyHands = false;

            foreach (var hand in handsComponent.ActivePriorityEnumerable())
            {
                if (handsComponent.GetItem(hand) == null)
                {
                    if (handsComponent.ActiveHand != hand)
                    {
                        handsComponent.ActiveHand = hand;
                    }

                    emptyHands = true;
                    break;
                }
            }

            if (!emptyHands)
            {
                return(Outcome.Failed);
            }

            var interactionSystem = EntitySystem.Get <InteractionSystem>();

            interactionSystem.InteractHand(_owner, _target);
            return(Outcome.Success);
        }
        /// <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(playerEntity.InRangeUnobstructed(table.Value) && _actionBlockerSystem.CanInteract(playerEntity));
        }
Beispiel #9
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 (!user.InRangeUnobstructed(target, popup: true))
            {
                return(false);
            }

            return(_foodSystem.TryUseFood(target, user));
        }
Beispiel #10
0
        public void DoAttack(EntityUid user, EntityCoordinates coordinates, bool wideAttack, EntityUid targetUid = default)
        {
            if (!ValidateInteractAndFace(user, coordinates))
            {
                return;
            }

            if (!_actionBlockerSystem.CanAttack(user))
            {
                return;
            }

            if (!wideAttack)
            {
                // Check if interacted entity is in the same container, the direct child, or direct parent of the user.
                if (targetUid != default && !user.IsInSameOrParentContainer(targetUid) && !CanAccessViaStorage(user, targetUid))
                {
                    Logger.WarningS("system.interaction",
                                    $"User entity named {EntityManager.GetComponent<MetaDataComponent>(user).EntityName} clicked on object {EntityManager.GetComponent<MetaDataComponent>(targetUid).EntityName} that isn't the parent, child, or in the same container");
                    return;
                }

                // TODO: Replace with body attack range when we get something like arm length or telekinesis or something.
                if (!user.InRangeUnobstructed(coordinates, ignoreInsideBlocker: true))
                {
                    return;
                }
            }

            // Verify user has a hand, and find what object they are currently holding in their active hand
            if (EntityManager.TryGetComponent <HandsComponent?>(user, out var hands))
            {
                if (hands.GetActiveHand?.Owner is { Valid : true } item)
                {
                    if (wideAttack)
                    {
                        var ev = new WideAttackEvent(item, user, coordinates);
                        RaiseLocalEvent(item, ev, false);

                        if (ev.Handled)
                        {
                            _adminLogSystem.Add(LogType.AttackArmedWide, LogImpact.Medium, $"{user} wide attacked with {item} at {coordinates}");
                            return;
                        }
                    }
                    else
                    {
                        var ev = new ClickAttackEvent(item, user, coordinates, targetUid);
                        RaiseLocalEvent(item, ev, false);

                        if (ev.Handled)
                        {
                            if (targetUid != default)
                            {
                                _adminLogSystem.Add(LogType.AttackArmedClick, LogImpact.Medium,
                                                    $"{user} attacked {targetUid} with {item} at {coordinates}");
                            }
                            else
                            {
                                _adminLogSystem.Add(LogType.AttackArmedClick, LogImpact.Medium,
                                                    $"{user} attacked with {item} at {coordinates}");
                            }

                            return;
                        }
                    }
                }
                else if (!wideAttack && targetUid != default && _entityManager.HasComponent <ItemComponent>(targetUid))
                {
                    // We pick up items if our hand is empty, even if we're in combat mode.
                    InteractHand(user, targetUid);
                    return;
                }
            }
Beispiel #11
0
        /// <summary>
        ///     Resolves user interactions with objects.
        /// </summary>
        /// <remarks>
        ///     Checks Whether combat mode is enabled and whether the user can actually interact with the given entity.
        /// </remarks>
        /// <param name="altInteract">Whether to use default or alternative interactions (usually as a result of
        /// alt+clicking). If combat mode is enabled, the alternative action is to perform the default non-combat
        /// interaction. Having an item in the active hand also disables alternative interactions.</param>
        public async void UserInteraction(EntityUid user, EntityCoordinates coordinates, EntityUid target, bool altInteract = false)
        {
            // TODO COMBAT Consider using alt-interact for advanced combat? maybe alt-interact disarms?
            if (!altInteract && EntityManager.TryGetComponent(user, out CombatModeComponent? combatMode) && combatMode.IsInCombatMode)
            {
                DoAttack(user, coordinates, false, target);
                return;
            }

            if (!ValidateInteractAndFace(user, coordinates))
            {
                return;
            }

            if (!_actionBlockerSystem.CanInteract(user))
            {
                return;
            }

            // 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 (target != default && !user.IsInSameOrParentContainer(target) && !CanAccessViaStorage(user, target))
            {
                Logger.WarningS("system.interaction",
                                $"User entity named {EntityManager.GetComponent<MetaDataComponent>(user).EntityName} clicked on object {EntityManager.GetComponent<MetaDataComponent>(target).EntityName} that isn't the parent, child, or in the same container");
                return;
            }

            // Verify user has a hand, and find what object they are currently holding in their active hand
            if (!EntityManager.TryGetComponent <HandsComponent?>(user, out var hands))
            {
                return;
            }

            var item = hands.GetActiveHand?.Owner;

            // TODO: Replace with body interaction range when we get something like arm length or telekinesis or something.
            var inRangeUnobstructed = user.InRangeUnobstructed(coordinates, ignoreInsideBlocker: true);

            if (target == default || !inRangeUnobstructed)
            {
                if (item == null)
                {
                    return;
                }

                if (!await InteractUsingRanged(user, item.Value, target, coordinates, inRangeUnobstructed) &&
                    !inRangeUnobstructed)
                {
                    var message = Loc.GetString("interaction-system-user-interaction-cannot-reach");
                    user.PopupMessage(message);
                }

                return;
            }
            else
            {
                // We are close to the nearby object.
                if (altInteract)
                {
                    // Perform alternative interactions, using context menu verbs.
                    AltInteract(user, target);
                }
                else if (item != null && item != target)
                {
                    // We are performing a standard interaction with an item, and the target isn't the same as the item
                    // currently in our hand. We will use the item in our hand on the nearby object via InteractUsing
                    await InteractUsing(user, item.Value, target, coordinates);
                }
                else if (item == null)
                {
                    // Since our hand is empty we will use InteractHand/Activate
                    InteractHand(user, target);
                }
            }
        }
        public override void DoAttack(EntityUid user, EntityCoordinates coordinates, bool wideAttack, EntityUid?target = null)
        {
            // TODO PREDICTION move server-side interaction logic into the shared system for interaction prediction.
            if (!ValidateInteractAndFace(user, coordinates))
            {
                return;
            }

            if (!_actionBlockerSystem.CanAttack(user))
            {
                return;
            }

            if (!wideAttack)
            {
                // Check if interacted entity is in the same container, the direct child, or direct parent of the user.
                if (target != null && !Deleted(target.Value) && !user.IsInSameOrParentContainer(target.Value) && !CanAccessViaStorage(user, target.Value))
                {
                    Logger.WarningS("system.interaction",
                                    $"User entity {ToPrettyString(user):user} clicked on object {ToPrettyString(target.Value):target} that isn't the parent, child, or in the same container");
                    return;
                }

                // TODO: Replace with body attack range when we get something like arm length or telekinesis or something.
                if (!user.InRangeUnobstructed(coordinates, ignoreInsideBlocker: true))
                {
                    return;
                }
            }

            // Verify user has a hand, and find what object they are currently holding in their active hand
            if (TryComp(user, out HandsComponent? hands))
            {
                var item = hands.GetActiveHand?.Owner;

                if (item != null && !Deleted(item.Value))
                {
                    if (wideAttack)
                    {
                        var ev = new WideAttackEvent(item.Value, user, coordinates);
                        RaiseLocalEvent(item.Value, ev, false);

                        if (ev.Handled)
                        {
                            _adminLogSystem.Add(LogType.AttackArmedWide, LogImpact.Medium, $"{ToPrettyString(user):user} wide attacked with {ToPrettyString(item.Value):used} at {coordinates}");
                            return;
                        }
                    }
                    else
                    {
                        var ev = new ClickAttackEvent(item.Value, user, coordinates, target);
                        RaiseLocalEvent(item.Value, ev, false);

                        if (ev.Handled)
                        {
                            if (target != null)
                            {
                                _adminLogSystem.Add(LogType.AttackArmedClick, LogImpact.Medium,
                                                    $"{ToPrettyString(user):user} attacked {ToPrettyString(target.Value):target} with {ToPrettyString(item.Value):used} at {coordinates}");
                            }
                            else
                            {
                                _adminLogSystem.Add(LogType.AttackArmedClick, LogImpact.Medium,
                                                    $"{ToPrettyString(user):user} attacked with {ToPrettyString(item.Value):used} at {coordinates}");
                            }

                            return;
                        }
                    }
                }
                else if (!wideAttack && target != null && HasComp <SharedItemComponent>(target.Value))
                {
                    // We pick up items if our hand is empty, even if we're in combat mode.
                    InteractHand(user, target.Value);
                    return;
                }
            }

            // TODO: Make this saner?
            // Attempt to do unarmed combat. We don't check for handled just because at this point it doesn't matter.
            if (wideAttack)
            {
                var ev = new WideAttackEvent(user, user, coordinates);
                RaiseLocalEvent(user, ev, false);
                if (ev.Handled)
                {
                    _adminLogSystem.Add(LogType.AttackUnarmedWide, $"{ToPrettyString(user):user} wide attacked at {coordinates}");
                }
            }
            else
            {
                var ev = new ClickAttackEvent(user, user, coordinates, target);
                RaiseLocalEvent(user, ev, false);
                if (ev.Handled)
                {
                    if (target != null)
                    {
                        _adminLogSystem.Add(LogType.AttackUnarmedClick, LogImpact.Medium,
                                            $"{ToPrettyString(user):user} attacked {ToPrettyString(target.Value):target} at {coordinates}");
                    }
                    else
                    {
                        _adminLogSystem.Add(LogType.AttackUnarmedClick, LogImpact.Medium,
                                            $"{ToPrettyString(user):user} attacked at {coordinates}");
                    }
                }
            }
        }
Beispiel #13
0
        /// <summary>
        ///     Resolves user interactions with objects.
        /// </summary>
        /// <remarks>
        ///     Checks Whether combat mode is enabled and whether the user can actually interact with the given entity.
        /// </remarks>
        /// <param name="altInteract">Whether to use default or alternative interactions (usually as a result of
        /// alt+clicking). If combat mode is enabled, the alternative action is to perform the default non-combat
        /// interaction. Having an item in the active hand also disables alternative interactions.</param>
        public async void UserInteraction(EntityUid user, EntityCoordinates coordinates, EntityUid?target, bool altInteract = false)
        {
            if (target != null && Deleted(target.Value))
            {
                return;
            }

            // TODO COMBAT Consider using alt-interact for advanced combat? maybe alt-interact disarms?
            if (!altInteract && TryComp(user, out SharedCombatModeComponent? combatMode) && combatMode.IsInCombatMode)
            {
                DoAttack(user, coordinates, false, target);
                return;
            }

            if (!ValidateInteractAndFace(user, coordinates))
            {
                return;
            }

            if (!_actionBlockerSystem.CanInteract(user))
            {
                return;
            }

            // 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 (target != null && !user.IsInSameOrParentContainer(target.Value) && !CanAccessViaStorage(user, target.Value))
            {
                return;
            }

            // Verify user has a hand, and find what object they are currently holding in their active hand
            if (!TryComp(user, out SharedHandsComponent? hands))
            {
                return;
            }

            // TODO: Replace with body interaction range when we get something like arm length or telekinesis or something.
            var inRangeUnobstructed = user.InRangeUnobstructed(coordinates, ignoreInsideBlocker: true);

            if (target == null || !inRangeUnobstructed)
            {
                if (!hands.TryGetActiveHeldEntity(out var heldEntity))
                {
                    return;
                }

                if (!await InteractUsingRanged(user, heldEntity.Value, target, coordinates, inRangeUnobstructed) &&
                    !inRangeUnobstructed)
                {
                    var message = Loc.GetString("interaction-system-user-interaction-cannot-reach");
                    user.PopupMessage(message);
                }

                return;
            }

            // We are close to the nearby object.
            if (altInteract)
            {
                // Perform alternative interactions, using context menu verbs.
                AltInteract(user, target.Value);
            }
            else if (!hands.TryGetActiveHeldEntity(out var heldEntity))
            {
                // Since our hand is empty we will use InteractHand/Activate
                InteractHand(user, target.Value);
            }
            else if (heldEntity != target)
            {
                await InteractUsing(user, heldEntity.Value, target.Value, coordinates);
            }
        }
        /// <summary>
        /// Tries to eat some food
        /// </summary>
        /// <param name="uid">Food entity.</param>
        /// <param name="user">Feeding initiator.</param>
        /// <returns>True if an interaction occurred (i.e., food was consumed, or a pop-up message was created)</returns>
        public bool TryUseFood(EntityUid uid, EntityUid user, FoodComponent?food = null)
        {
            if (!Resolve(uid, ref food))
            {
                return(false);
            }

            // if currently being used to force-feed, cancel that action.
            if (food.CancelToken != null)
            {
                food.CancelToken.Cancel();
                food.CancelToken = null;
                return(true);
            }

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

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

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

            if (!EntityManager.TryGetComponent(user, out SharedBodyComponent ? body) ||
                !_bodySystem.TryGetComponentsOnMechanisms <StomachComponent>(user, out var stomachs, body))
            {
                return(false);
            }

            if (IsMouthBlocked(user, user))
            {
                return(true);
            }

            var usedUtensils = new List <UtensilComponent>();

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

            if (!user.InRangeUnobstructed(uid, popup: true))
            {
                return(true);
            }

            var transferAmount = food.TransferAmount != null?FixedPoint2.Min((FixedPoint2)food.TransferAmount, solution.CurrentVolume) : solution.CurrentVolume;

            var split        = _solutionContainerSystem.SplitSolution(uid, solution, transferAmount);
            var firstStomach = stomachs.FirstOrNull(
                stomach => _stomachSystem.CanTransferSolution((stomach.Comp).Owner, split));

            if (firstStomach == null)
            {
                _solutionContainerSystem.TryAddSolution(uid, solution, split);
                _popupSystem.PopupEntity(Loc.GetString("food-system-you-cannot-eat-any-more"), user, Filter.Entities(user));
                return(true);
            }

            // TODO: Account for partial transfer.
            split.DoEntityReaction(user, ReactionMethod.Ingestion);
            _stomachSystem.TryTransferSolution((firstStomach.Value.Comp).Owner, split, firstStomach.Value.Comp);

            SoundSystem.Play(Filter.Pvs(user), food.UseSound.GetSound(), user, AudioParams.Default.WithVolume(-1f));
            _popupSystem.PopupEntity(Loc.GetString(food.EatMessage, ("food", food.Owner)), user, Filter.Entities(user));

            // Try to break all used utensils
            foreach (var utensil in usedUtensils)
            {
                _utensilSystem.TryBreak((utensil).Owner, user);
            }

            if (food.UsesRemaining > 0)
            {
                return(true);
            }

            if (string.IsNullOrEmpty(food.TrashPrototype))
            {
                EntityManager.QueueDeleteEntity((food).Owner);
            }
            else
            {
                DeleteAndSpawnTrash(food, user);
            }

            return(true);
        }