Exemple #1
0
        public void Activate(ActivateEventArgs eventArgs)
        {
            if (!eventArgs.User.TryGetComponent(out IActorComponent? actor))
            {
                return;
            }
            if (!Powered)
            {
                return;
            }
            if (!ActionBlockerSystem.CanInteract(actor.playerSession.AttachedEntity))
            {
                return;
            }

            if (_wiresComponent?.IsPanelOpen == true)
            {
                _wiresComponent.OpenInterface(actor.playerSession);
            }
            else
            {
                UserInterface?.Toggle(actor.playerSession);
            }
        }
        private void OnHandleState(EntityUid uid, PilotComponent component, ref ComponentHandleState args)
        {
            if (args.Current is not PilotComponentState state)
            {
                return;
            }

            var console = state.Console.GetValueOrDefault();

            if (!console.IsValid())
            {
                component.Console = null;
                return;
            }

            if (!TryComp <ShuttleConsoleComponent>(console, out var shuttleConsoleComponent))
            {
                Logger.Warning($"Unable to set Helmsman console to {console}");
                return;
            }

            component.Console = shuttleConsoleComponent;
            ActionBlockerSystem.UpdateCanMove(uid);
        }
Exemple #3
0
            protected override void GetData(IEntity user, BaseCharger component, VerbData data)
            {
                if (!ActionBlockerSystem.CanInteract(user))
                {
                    data.Visibility = VerbVisibility.Invisible;
                    return;
                }
                if (!user.TryGetComponent(out HandsComponent? handsComponent))
                {
                    data.Visibility = VerbVisibility.Invisible;
                    return;
                }

                if (component._container.ContainedEntity != null || handsComponent.GetActiveHand == null)
                {
                    data.Visibility = VerbVisibility.Invisible;
                    return;
                }

                var heldItemName = Loc.GetString(handsComponent.GetActiveHand.Owner.Name);

                data.Text        = Loc.GetString("Insert {0}", heldItemName);
                data.IconTexture = "/Textures/Interface/VerbIcons/insert.svg.192dpi.png";
            }
            protected override void GetData(IEntity user, PowerCellSlotComponent component, VerbData data)
            {
                if (!component.ShowVerb || !ActionBlockerSystem.CanInteract(user))
                {
                    data.Visibility = VerbVisibility.Invisible;
                    return;
                }

                if (component.Cell == null)
                {
                    data.Text       = Loc.GetString("No cell");
                    data.Visibility = VerbVisibility.Disabled;
                }
                else
                {
                    data.Text        = Loc.GetString("Eject cell");
                    data.IconTexture = "/Textures/Interface/VerbIcons/eject.svg.192dpi.png";
                }

                if (component.Cell == null || !component.CanRemoveCell)
                {
                    data.Visibility = VerbVisibility.Disabled;
                }
            }
        /// <summary>
        ///     Checks whether an item can be put in the specified slot.
        /// </summary>
        /// <param name="slot">The slot to check for.</param>
        /// <param name="item">The item to check for.</param>
        /// <param name="reason">The translated reason why the item cannot be equiped, if this function returns false. Can be null.</param>
        /// <returns>True if the item can be inserted into the specified slot.</returns>
        public bool CanEquip(Slots slot, ItemComponent item, out string reason)
        {
            var pass = false;

            reason = null;

            if (!ActionBlockerSystem.CanEquip(Owner))
            {
                return(false);
            }

            if (item is ClothingComponent clothing)
            {
                if (clothing.SlotFlags != SlotFlags.PREVENTEQUIP && (clothing.SlotFlags & SlotMasks[slot]) != 0)
                {
                    pass = true;
                }
                else
                {
                    reason = Loc.GetString("This doesn't fit.");
                }
            }

            if (Owner.TryGetComponent(out IInventoryController controller))
            {
                pass   = controller.CanEquip(slot, item.Owner, pass, out var controllerReason);
                reason = controllerReason ?? reason;
            }

            if (!pass && reason == null)
            {
                reason = Loc.GetString("You can't equip this!");
            }

            return(pass && SlotContainers[slot].CanInsert(item.Owner));
        }
Exemple #6
0
        private void UserInterfaceOnOnReceiveMessage(ServerBoundUserInterfaceMessage obj)
        {
            switch (obj.Message)
            {
            case BlockGameMessages.BlockGameUserUnregisterMessage unregisterMessage:
                UnRegisterPlayerSession(obj.Session);
                break;

            case BlockGameMessages.BlockGamePlayerActionMessage playerActionMessage:
                if (obj.Session != _player)
                {
                    break;
                }

                if (!ActionBlockerSystem.CanInteract(Owner))
                {
                    DeactivePlayer(obj.Session);
                    break;
                }

                if (playerActionMessage.PlayerAction == BlockGamePlayerAction.NewGame)
                {
                    if (_game?.Started == true)
                    {
                        _game = new BlockGame(this);
                    }
                    _game?.StartGame();
                }
                else
                {
                    _game?.ProcessInput(playerActionMessage.PlayerAction);
                }

                break;
            }
        }
Exemple #7
0
            protected override void GetData(IEntity user, SolutionContainerComponent component, VerbData data)
            {
                if (!ActionBlockerSystem.CanInteract(user) ||
                    !user.TryGetComponent <HandsComponent>(out var hands) ||
                    hands.GetActiveHand == null ||
                    hands.GetActiveHand.Owner == component.Owner ||
                    !hands.GetActiveHand.Owner.TryGetComponent <SolutionContainerComponent>(out var solution) ||
                    !solution.CanAddSolutions ||
                    !component.CanRemoveSolutions)
                {
                    data.Visibility = VerbVisibility.Invisible;
                    return;
                }

                var heldEntityName = hands.GetActiveHand.Owner?.Prototype?.Name ?? "<Item>";
                var myName         = component.Owner.Prototype?.Name ?? "<Item>";

                var locHeldEntityName = Loc.GetString(heldEntityName);
                var locMyName         = Loc.GetString(myName);

                data.Visibility = VerbVisibility.Visible;
                data.Text       = Loc.GetString("Transfer liquid from [{0}] to [{1}].", locMyName, locHeldEntityName);
                return;
            }
Exemple #8
0
        async Task <bool> IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
        {
            if (eventArgs.Target == null)
            {
                return(false);
            }

            if (!eventArgs.Target.TryGetComponent(out IDamageableComponent damageable))
            {
                return(true);
            }

            if (!ActionBlockerSystem.CanInteract(eventArgs.User))
            {
                return(true);
            }

            if (eventArgs.User != eventArgs.Target &&
                !eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true))
            {
                return(true);
            }

            if (Owner.TryGetComponent(out StackComponent stack) &&
                !stack.Use(1))
            {
                return(true);
            }

            foreach (var(type, amount) in Heal)
            {
                damageable.ChangeDamage(type, -amount, true);
            }

            return(true);
        }
        private bool TryBuckle(IEntity user, IEntity to)
        {
            if (user == null || user == to)
            {
                return(false);
            }

            if (!ActionBlockerSystem.CanInteract(user))
            {
                _notifyManager.PopupMessage(user, user,
                                            Loc.GetString("You can't do that!"));
                return(false);
            }

            var strapPosition = Owner.Transform.MapPosition;
            var range         = SharedInteractionSystem.InteractionRange / 2;

            if (!InteractionChecks.InRangeUnobstructed(user, strapPosition, range))
            {
                _notifyManager.PopupMessage(user, user,
                                            Loc.GetString("You can't reach there!"));
                return(false);
            }

            if (!user.TryGetComponent(out HandsComponent hands))
            {
                _notifyManager.PopupMessage(user, user,
                                            Loc.GetString("You don't have hands!"));
                return(false);
            }

            if (hands.GetActiveHand != null)
            {
                _notifyManager.PopupMessage(user, user,
                                            Loc.GetString("Your hand isn't free!"));
                return(false);
            }

            if (BuckledTo != null)
            {
                _notifyManager.PopupMessage(Owner, user,
                                            Loc.GetString(Owner == user
                        ? "You are already buckled in!"
                        : "{0:They} are already buckled in!", Owner));
                return(false);
            }

            if (!to.TryGetComponent(out StrapComponent strap))
            {
                _notifyManager.PopupMessage(Owner, user,
                                            Loc.GetString(Owner == user
                        ? "You can't buckle yourself there!"
                        : "You can't buckle {0:them} there!", Owner));
                return(false);
            }

            var parent = to.Transform.Parent;

            while (parent != null)
            {
                if (parent == user.Transform)
                {
                    _notifyManager.PopupMessage(Owner, user,
                                                Loc.GetString(Owner == user
                            ? "You can't buckle yourself there!"
                            : "You can't buckle {0:them} there!", Owner));
                    return(false);
                }

                parent = parent.Parent;
            }

            if (!strap.HasSpace(this))
            {
                _notifyManager.PopupMessage(Owner, user,
                                            Loc.GetString(Owner == user
                        ? "You can't fit there!"
                        : "{0:They} can't fit there!", Owner));
                return(false);
            }

            _entitySystem.GetEntitySystem <AudioSystem>()
            .PlayFromEntity(strap.BuckleSound, Owner);

            if (!strap.TryAdd(this))
            {
                _notifyManager.PopupMessage(Owner, user,
                                            Loc.GetString(Owner == user
                        ? "You can't buckle yourself there!"
                        : "You can't buckle {0:them} there!", Owner));
                return(false);
            }

            BuckledTo = strap;

            if (Owner.TryGetComponent(out AppearanceComponent appearance))
            {
                appearance.SetData(BuckleVisuals.Buckled, true);
            }

            var ownTransform   = Owner.Transform;
            var strapTransform = strap.Owner.Transform;

            ownTransform.GridPosition = strapTransform.GridPosition;
            ownTransform.AttachParent(strapTransform);

            switch (strap.Position)
            {
            case StrapPosition.None:
                ownTransform.WorldRotation = strapTransform.WorldRotation;
                break;

            case StrapPosition.Stand:
                StandingStateHelper.Standing(Owner);
                ownTransform.WorldRotation = strapTransform.WorldRotation;
                break;

            case StrapPosition.Down:
                StandingStateHelper.Down(Owner);
                ownTransform.WorldRotation = Angle.South;
                break;
            }

            BuckleStatus();

            return(true);
        }
        bool IDragDropOn.CanDragDropOn(DragDropEventArgs eventArgs)
        {
            if (!ActionBlockerSystem.CanInteract(eventArgs.User))
            {
                _notifyManager.PopupMessage(eventArgs.User, eventArgs.User, Loc.GetString("You can't do that!"));

                return(false);
            }

            if (eventArgs.User == eventArgs.Dropped) // user is dragging themselves onto a climbable
            {
                if (!eventArgs.User.HasComponent <ClimbingComponent>())
                {
                    _notifyManager.PopupMessage(eventArgs.User, eventArgs.User, Loc.GetString("You are incapable of climbing!"));

                    return(false);
                }

                var bodyManager = eventArgs.User.GetComponent <BodyManagerComponent>();

                if (bodyManager.GetBodyPartsOfType(Shared.GameObjects.Components.Body.BodyPartType.Leg).Count == 0 ||
                    bodyManager.GetBodyPartsOfType(Shared.GameObjects.Components.Body.BodyPartType.Foot).Count == 0)
                {
                    _notifyManager.PopupMessage(eventArgs.User, eventArgs.User, Loc.GetString("You are unable to climb!"));

                    return(false);
                }

                var userPosition      = eventArgs.User.Transform.MapPosition;
                var climbablePosition = eventArgs.Target.Transform.MapPosition;
                var interaction       = EntitySystem.Get <SharedInteractionSystem>();
                bool Ignored(IEntity entity) => (entity == eventArgs.Target || entity == eventArgs.User);

                if (!interaction.InRangeUnobstructed(userPosition, climbablePosition, _range, predicate: Ignored))
                {
                    _notifyManager.PopupMessage(eventArgs.User, eventArgs.User, Loc.GetString("You can't reach there!"));

                    return(false);
                }
            }
            else // user is dragging some other entity onto a climbable
            {
                if (eventArgs.Target == null || !eventArgs.Dropped.HasComponent <ClimbingComponent>())
                {
                    _notifyManager.PopupMessage(eventArgs.User, eventArgs.User, Loc.GetString("You can't do that!"));

                    return(false);
                }

                var userPosition      = eventArgs.User.Transform.MapPosition;
                var otherUserPosition = eventArgs.Dropped.Transform.MapPosition;
                var climbablePosition = eventArgs.Target.Transform.MapPosition;
                var interaction       = EntitySystem.Get <SharedInteractionSystem>();
                bool Ignored(IEntity entity) => (entity == eventArgs.Target || entity == eventArgs.User || entity == eventArgs.Dropped);

                if (!interaction.InRangeUnobstructed(userPosition, climbablePosition, _range, predicate: Ignored) ||
                    !interaction.InRangeUnobstructed(userPosition, otherUserPosition, _range, predicate: Ignored))
                {
                    _notifyManager.PopupMessage(eventArgs.User, eventArgs.User, Loc.GetString("You can't reach there!"));

                    return(false);
                }
            }

            return(true);
        }
Exemple #11
0
        /// <summary>
        /// Attempt to uncuff a cuffed entity. Can be called by the cuffed entity, or another entity trying to help uncuff them.
        /// If the uncuffing succeeds, the cuffs will drop on the floor.
        /// </summary>
        /// <param name="user">The cuffed entity</param>
        /// <param name="cuffsToRemove">Optional param for the handcuff entity to remove from the cuffed entity. If null, uses the most recently added handcuff entity.</param>
        public async void TryUncuff(IEntity user, IEntity cuffsToRemove = null)
        {
            var isOwner = user == Owner;

            if (cuffsToRemove == null)
            {
                cuffsToRemove = LastAddedCuffs;
            }
            else
            {
                if (!_container.ContainedEntities.Contains(cuffsToRemove))
                {
                    Logger.Warning("A user is trying to remove handcuffs that aren't in the owner's container. This should never happen!");
                }
            }

            if (!cuffsToRemove.TryGetComponent <HandcuffComponent>(out var cuff))
            {
                Logger.Warning($"A user is trying to remove handcuffs without a {nameof(HandcuffComponent)}. This should never happen!");
                return;
            }

            if (!isOwner && !ActionBlockerSystem.CanInteract(user))
            {
                user.PopupMessage(user, Loc.GetString("You can't do that!"));
                return;
            }

            if (!isOwner && user.InRangeUnobstructed(Owner, _interactRange))
            {
                user.PopupMessage(user, Loc.GetString("You are too far away to remove the cuffs."));
                return;
            }

            if (!cuffsToRemove.InRangeUnobstructed(Owner, _interactRange))
            {
                Logger.Warning("Handcuffs being removed from player are obstructed or too far away! This should not happen!");
                return;
            }

            user.PopupMessage(user, Loc.GetString("You start removing the cuffs."));

            var audio = EntitySystem.Get <AudioSystem>();

            audio.PlayFromEntity(isOwner ? cuff.StartBreakoutSound : cuff.StartUncuffSound, Owner);

            var uncuffTime       = isOwner ? cuff.BreakoutTime : cuff.UncuffTime;
            var doAfterEventArgs = new DoAfterEventArgs(user, uncuffTime)
            {
                BreakOnUserMove = true,
                BreakOnDamage   = true,
                BreakOnStun     = true,
                NeedHand        = true
            };

            var doAfterSystem = EntitySystem.Get <DoAfterSystem>();
            var result        = await doAfterSystem.DoAfter(doAfterEventArgs);

            if (result != DoAfterStatus.Cancelled)
            {
                audio.PlayFromEntity(cuff.EndUncuffSound, Owner);

                _container.ForceRemove(cuffsToRemove);
                cuffsToRemove.Transform.AttachToGridOrMap();
                cuffsToRemove.Transform.WorldPosition = Owner.Transform.WorldPosition;

                if (cuff.BreakOnRemove)
                {
                    cuff.Broken = true;

                    cuffsToRemove.Name        = cuff.BrokenName;
                    cuffsToRemove.Description = cuff.BrokenDesc;

                    if (cuffsToRemove.TryGetComponent <SpriteComponent>(out var sprite))
                    {
                        sprite.LayerSetState(0, cuff.BrokenState); // TODO: safety check to see if RSI contains the state?
                    }
                }

                CanStillInteract = _hands.Hands.Count() > CuffedHandCount;
                OnCuffedStateChanged.Invoke();
                UpdateStatusEffect();
                Dirty();

                if (CuffedHandCount == 0)
                {
                    _notifyManager.PopupMessage(user, user, Loc.GetString("You successfully remove the cuffs."));

                    if (!isOwner)
                    {
                        _notifyManager.PopupMessage(user, Owner, Loc.GetString("{0:theName} uncuffs your hands.", user));
                    }
                }
                else
                {
                    if (!isOwner)
                    {
                        _notifyManager.PopupMessage(user, user, Loc.GetString("You successfully remove the cuffs. {0} of {1:theName}'s hands remain cuffed.", CuffedHandCount, user));
                        _notifyManager.PopupMessage(user, Owner, Loc.GetString("{0:theName} removes your cuffs. {1} of your hands remain cuffed.", user, CuffedHandCount));
                    }
                    else
                    {
                        _notifyManager.PopupMessage(user, user, Loc.GetString("You successfully remove the cuffs. {0} of your hands remain cuffed.", CuffedHandCount));
                    }
                }
            }
            else
            {
                _notifyManager.PopupMessage(user, user, Loc.GetString("You fail to remove the cuffs."));
            }

            return;
        }
        private async void PlaceActiveHandItemInInventory(IEntity user, Slots slot)
        {
            var inventory = Owner.GetComponent <InventoryComponent>();
            var userHands = user.GetComponent <HandsComponent>();
            var item      = userHands.GetActiveHand;

            bool Check()
            {
                if (!ActionBlockerSystem.CanInteract(user))
                {
                    return(false);
                }

                if (item == null)
                {
                    _notifyManager.PopupMessageCursor(user, Loc.GetString("You aren't holding anything!"));
                    return(false);
                }

                if (!userHands.CanDrop(userHands.ActiveHand !))
                {
                    _notifyManager.PopupMessageCursor(user, Loc.GetString("You can't drop that!"));
                    return(false);
                }

                if (!inventory.HasSlot(slot))
                {
                    return(false);
                }

                if (inventory.TryGetSlotItem(slot, out ItemComponent _))
                {
                    _notifyManager.PopupMessageCursor(user, Loc.GetString("{0:They} already {0:have} something there!", Owner));
                    return(false);
                }

                if (!inventory.CanEquip(slot, item, false))
                {
                    _notifyManager.PopupMessageCursor(user, Loc.GetString("{0:They} cannot equip that there!", Owner));
                    return(false);
                }

                return(true);
            }

            var doAfterSystem = EntitySystem.Get <DoAfterSystem>();

            var doAfterArgs = new DoAfterEventArgs(user, StripDelay, CancellationToken.None, Owner)
            {
                ExtraCheck        = Check,
                BreakOnStun       = true,
                BreakOnDamage     = true,
                BreakOnTargetMove = true,
                BreakOnUserMove   = true,
                NeedHand          = true,
            };

            var result = await doAfterSystem.DoAfter(doAfterArgs);

            if (result != DoAfterStatus.Finished)
            {
                return;
            }

            userHands.Drop(item !.Owner, false);
            inventory.Equip(slot, item !.Owner, false);

            UpdateSubscribed();
        }
Exemple #13
0
        /// <summary>
        /// Process thermal regulation
        /// </summary>
        /// <param name="frameTime"></param>
        private void ProcessThermalRegulation(float frameTime)
        {
            if (!Owner.TryGetComponent(out TemperatureComponent temperatureComponent))
            {
                return;
            }
            temperatureComponent.ReceiveHeat(MetabolismHeat);
            temperatureComponent.RemoveHeat(RadiatedHeat);

            // implicit heat regulation
            var tempDiff   = Math.Abs(temperatureComponent.CurrentTemperature - NormalBodyTemperature);
            var targetHeat = tempDiff * temperatureComponent.HeatCapacity;

            if (temperatureComponent.CurrentTemperature > NormalBodyTemperature)
            {
                temperatureComponent.RemoveHeat(Math.Min(targetHeat, ImplicitHeatRegulation));
            }
            else
            {
                temperatureComponent.ReceiveHeat(Math.Min(targetHeat, ImplicitHeatRegulation));
            }

            // recalc difference and target heat
            tempDiff   = Math.Abs(temperatureComponent.CurrentTemperature - NormalBodyTemperature);
            targetHeat = tempDiff * temperatureComponent.HeatCapacity;

            // if body temperature is not within comfortable, thermal regulation
            // processes starts
            if (tempDiff < ThermalRegulationTemperatureThreshold)
            {
                if (_isShivering || _isSweating)
                {
                    Owner.PopupMessage(Loc.GetString("You feel comfortable"));
                }

                _isShivering = false;
                _isSweating  = false;
                return;
            }


            if (temperatureComponent.CurrentTemperature > NormalBodyTemperature)
            {
                if (!ActionBlockerSystem.CanSweat(Owner))
                {
                    return;
                }
                if (!_isSweating)
                {
                    Owner.PopupMessage(Loc.GetString("You are sweating"));
                    _isSweating = true;
                }

                // creadth: sweating does not help in airless environment
                if (Owner.Transform.Coordinates.TryGetTileAir(out _, _entityManager))
                {
                    temperatureComponent.RemoveHeat(Math.Min(targetHeat, SweatHeatRegulation));
                }
            }
            else
            {
                if (!ActionBlockerSystem.CanShiver(Owner))
                {
                    return;
                }
                if (!_isShivering)
                {
                    Owner.PopupMessage(Loc.GetString("You are shivering"));
                    _isShivering = true;
                }

                temperatureComponent.ReceiveHeat(Math.Min(targetHeat, ShiveringHeatRegulation));
            }
        }
        private async void TakeItemFromInventory(IEntity user, Slots slot)
        {
            var inventory = Owner.GetComponent <InventoryComponent>();
            var userHands = user.GetComponent <HandsComponent>();

            bool Check()
            {
                if (!ActionBlockerSystem.CanInteract(user))
                {
                    return(false);
                }

                if (!inventory.HasSlot(slot))
                {
                    return(false);
                }

                if (!inventory.TryGetSlotItem(slot, out ItemComponent? itemToTake))
                {
                    user.PopupMessageCursor(Loc.GetString("{0:They} {0:have} nothing there!", Owner));
                    return(false);
                }

                if (!inventory.CanUnequip(slot, false))
                {
                    user.PopupMessageCursor(Loc.GetString("{0:They} cannot unequip that!", Owner));
                    return(false);
                }

                return(true);
            }

            var doAfterSystem = EntitySystem.Get <DoAfterSystem>();

            var doAfterArgs = new DoAfterEventArgs(user, StripDelay, CancellationToken.None, Owner)
            {
                ExtraCheck        = Check,
                BreakOnStun       = true,
                BreakOnDamage     = true,
                BreakOnTargetMove = true,
                BreakOnUserMove   = true,
            };

            var result = await doAfterSystem.DoAfter(doAfterArgs);

            if (result != DoAfterStatus.Finished)
            {
                return;
            }

            var item = inventory.GetSlotItem(slot);

            inventory.Unequip(slot, false);

            if (item != null)
            {
                userHands.PutInHandOrDrop(item);
            }

            UpdateSubscribed();
        }
        public async Task BuckleUnbuckleCooldownRangeTest()
        {
            var cOptions = new ClientIntegrationOptions {
                ExtraPrototypes = Prototypes
            };
            var sOptions = new ServerIntegrationOptions {
                ExtraPrototypes = Prototypes
            };

            var(client, server) = await StartConnectedServerClientPair(cOptions, sOptions);

            IEntity         human  = null;
            IEntity         chair  = null;
            BuckleComponent buckle = null;
            StrapComponent  strap  = null;

            await server.WaitAssertion(() =>
            {
                var mapManager    = IoCManager.Resolve <IMapManager>();
                var entityManager = IoCManager.Resolve <IEntityManager>();

                var gridId      = new GridId(1);
                var grid        = mapManager.GetGrid(gridId);
                var coordinates = grid.GridEntityId.ToCoordinates();

                human = entityManager.SpawnEntity(BuckleDummyId, coordinates);
                chair = entityManager.SpawnEntity(StrapDummyId, coordinates);

                // Default state, unbuckled
                Assert.True(human.TryGetComponent(out buckle));
                Assert.NotNull(buckle);
                Assert.Null(buckle.BuckledTo);
                Assert.False(buckle.Buckled);
                Assert.True(ActionBlockerSystem.CanMove(human));
                Assert.True(ActionBlockerSystem.CanChangeDirection(human));
                Assert.True(EffectBlockerSystem.CanFall(human));

                // Default state, no buckled entities, strap
                Assert.True(chair.TryGetComponent(out strap));
                Assert.NotNull(strap);
                Assert.IsEmpty(strap.BuckledEntities);
                Assert.Zero(strap.OccupiedSize);

                // Side effects of buckling
                Assert.True(buckle.TryBuckle(human, chair));
                Assert.NotNull(buckle.BuckledTo);
                Assert.True(buckle.Buckled);

                var player = IoCManager.Resolve <IPlayerManager>().GetAllPlayers().Single();
                Assert.True(((BuckleComponentState)buckle.GetComponentState(player)).Buckled);
                Assert.False(ActionBlockerSystem.CanMove(human));
                Assert.False(ActionBlockerSystem.CanChangeDirection(human));
                Assert.False(EffectBlockerSystem.CanFall(human));
                Assert.That(human.Transform.WorldPosition, Is.EqualTo(chair.Transform.WorldPosition));

                // Side effects of buckling for the strap
                Assert.That(strap.BuckledEntities, Does.Contain(human));
                Assert.That(strap.OccupiedSize, Is.EqualTo(buckle.Size));
                Assert.Positive(strap.OccupiedSize);

                // Trying to buckle while already buckled fails
                Assert.False(buckle.TryBuckle(human, chair));

                // Trying to unbuckle too quickly fails
                Assert.False(buckle.TryUnbuckle(human));
                Assert.False(buckle.ToggleBuckle(human, chair));
                Assert.True(buckle.Buckled);
            });

            // Wait enough ticks for the unbuckling cooldown to run out
            await server.WaitRunTicks(60);

            await server.WaitAssertion(() =>
            {
                // Still buckled
                Assert.True(buckle.Buckled);

                // Unbuckle
                Assert.True(buckle.TryUnbuckle(human));
                Assert.Null(buckle.BuckledTo);
                Assert.False(buckle.Buckled);
                Assert.True(ActionBlockerSystem.CanMove(human));
                Assert.True(ActionBlockerSystem.CanChangeDirection(human));
                Assert.True(EffectBlockerSystem.CanFall(human));

                // Unbuckle, strap
                Assert.IsEmpty(strap.BuckledEntities);
                Assert.Zero(strap.OccupiedSize);

                // Re-buckling has no cooldown
                Assert.True(buckle.TryBuckle(human, chair));
                Assert.True(buckle.Buckled);

                // On cooldown
                Assert.False(buckle.TryUnbuckle(human));
                Assert.True(buckle.Buckled);
                Assert.False(buckle.ToggleBuckle(human, chair));
                Assert.True(buckle.Buckled);
                Assert.False(buckle.ToggleBuckle(human, chair));
                Assert.True(buckle.Buckled);
            });

            // Wait enough ticks for the unbuckling cooldown to run out
            await server.WaitRunTicks(60);

            await server.WaitAssertion(() =>
            {
                // Still buckled
                Assert.True(buckle.Buckled);

                // Unbuckle
                Assert.True(buckle.TryUnbuckle(human));
                Assert.False(buckle.Buckled);

                // Move away from the chair
                human.Transform.WorldPosition += (1000, 1000);

                // Out of range
                Assert.False(buckle.TryBuckle(human, chair));
                Assert.False(buckle.TryUnbuckle(human));
                Assert.False(buckle.ToggleBuckle(human, chair));

                // Move near the chair
                human.Transform.WorldPosition = chair.Transform.WorldPosition + (0.5f, 0);

                // In range
                Assert.True(buckle.TryBuckle(human, chair));
                Assert.True(buckle.Buckled);
                Assert.False(buckle.TryUnbuckle(human));
                Assert.True(buckle.Buckled);
                Assert.False(buckle.ToggleBuckle(human, chair));
                Assert.True(buckle.Buckled);

                // Force unbuckle
                Assert.True(buckle.TryUnbuckle(human, true));
                Assert.False(buckle.Buckled);
                Assert.True(ActionBlockerSystem.CanMove(human));
                Assert.True(ActionBlockerSystem.CanChangeDirection(human));
                Assert.True(EffectBlockerSystem.CanFall(human));

                // Re-buckle
                Assert.True(buckle.TryBuckle(human, chair));

                // Move away from the chair
                human.Transform.WorldPosition += (1, 0);
            });

            await server.WaitRunTicks(1);

            await server.WaitAssertion(() =>
            {
                // No longer buckled
                Assert.False(buckle.Buckled);
                Assert.Null(buckle.BuckledTo);
                Assert.IsEmpty(strap.BuckledEntities);
            });
        }
        public async Task <bool> InteractUsing(InteractUsingEventArgs eventArgs)
        {
            var user      = eventArgs.User;
            var usingItem = eventArgs.Using;

            if (usingItem == null || usingItem.Deleted || !ActionBlockerSystem.CanInteract(user))
            {
                return(false);
            }

            if (usingItem.TryGetComponent(out SeedComponent? seeds))
            {
                if (Seed == null)
                {
                    if (seeds.Seed == null)
                    {
                        user.PopupMessageCursor(Loc.GetString("The packet seems to be empty. You throw it away."));
                        usingItem.Delete();
                        return(false);
                    }

                    user.PopupMessageCursor(Loc.GetString("You plant the {0} {1}.", seeds.Seed.SeedName, seeds.Seed.SeedNoun));

                    Seed       = seeds.Seed;
                    Dead       = false;
                    Age        = 1;
                    Health     = Seed.Endurance;
                    _lastCycle = _gameTiming.CurTime;

                    usingItem.Delete();

                    CheckLevelSanity();
                    UpdateSprite();

                    return(true);
                }

                user.PopupMessageCursor(Loc.GetString("The {0} already has seeds in it!", Owner.Name));
                return(false);
            }

            if (usingItem.HasComponent <HoeComponent>())
            {
                if (WeedLevel > 0)
                {
                    user.PopupMessageCursor(Loc.GetString("You remove the weeds from the {0}.", Owner.Name));
                    user.PopupMessageOtherClients(Loc.GetString("{0} starts uprooting the weeds.", user.Name));
                    WeedLevel = 0;
                    UpdateSprite();
                }
                else
                {
                    user.PopupMessageCursor(Loc.GetString("This plot is devoid of weeds! It doesn't need uprooting."));
                }

                return(true);
            }

            if (usingItem.TryGetComponent(out SolutionContainerComponent? solution) && solution.CanRemoveSolutions)
            {
                var amount  = 5f;
                var sprayed = false;

                if (usingItem.TryGetComponent(out SprayComponent? spray))
                {
                    sprayed = true;
                    amount  = 1f;
                    EntitySystem.Get <AudioSystem>().PlayFromEntity(spray.SpraySound, usingItem, AudioHelpers.WithVariation(0.125f));
                }

                var chemAmount = ReagentUnit.New(amount);

                var split = solution.Solution.SplitSolution(chemAmount <= solution.Solution.TotalVolume ? chemAmount : solution.Solution.TotalVolume);

                user.PopupMessageCursor(Loc.GetString(sprayed ? $"You spray {Owner.Name} with {usingItem.Name}." : $"You transfer {split.TotalVolume.ToString()}u to {Owner.Name}"));

                _solutionContainer?.TryAddSolution(split);

                SkipAging++; // We're forcing an update cycle, so one age hasn't passed.
                ForceUpdate = true;
                Update();

                return(true);
            }

            if (usingItem.HasComponent <PlantSampleTakerComponent>())
            {
                if (Seed == null)
                {
                    user.PopupMessageCursor(Loc.GetString("There is nothing to take a sample of!"));
                    return(false);
                }

                if (Sampled)
                {
                    user.PopupMessageCursor(Loc.GetString("This plant has already been sampled."));
                    return(false);
                }

                if (Dead)
                {
                    user.PopupMessageCursor(Loc.GetString("This plant is dead."));
                    return(false);
                }

                var seed = Seed.SpawnSeedPacket(user.Transform.Coordinates);
                seed.RandomOffset(0.25f);
                user.PopupMessageCursor(Loc.GetString($"You take a sample from the {Seed.DisplayName}."));
                Health -= (_random.Next(3, 5) * 10);

                if (_random.Prob(0.3f))
                {
                    Sampled = true;
                }

                // Just in case.
                CheckLevelSanity();
                SkipAging++; // We're forcing an update cycle, so one age hasn't passed.
                ForceUpdate = true;
                Update();

                return(true);
            }

            if (usingItem.HasComponent <BotanySharpComponent>())
            {
                return(DoHarvest(user));
            }

            return(false);
        }
 private bool UserCanFire(IEntity user)
 {
     return((UserCanFireHandler == null || UserCanFireHandler(user)) && ActionBlockerSystem.CanAttack(user));
 }
        public async Task BuckleUnbuckleCooldownRangeTest()
        {
            var server = StartServerDummyTicker();

            IEntity         human  = null;
            IEntity         chair  = null;
            BuckleComponent buckle = null;
            StrapComponent  strap  = null;

            await server.WaitAssertion(() =>
            {
                var mapManager = IoCManager.Resolve <IMapManager>();

                mapManager.CreateNewMapEntity(MapId.Nullspace);

                var entityManager = IoCManager.Resolve <IEntityManager>();

                human = entityManager.SpawnEntity("HumanMob_Content", MapCoordinates.Nullspace);
                chair = entityManager.SpawnEntity("ChairWood", MapCoordinates.Nullspace);

                // Default state, unbuckled
                Assert.True(human.TryGetComponent(out buckle));
                Assert.NotNull(buckle);
                Assert.Null(buckle.BuckledTo);
                Assert.False(buckle.Buckled);
                Assert.True(ActionBlockerSystem.CanMove(human));
                Assert.True(ActionBlockerSystem.CanChangeDirection(human));
                Assert.True(EffectBlockerSystem.CanFall(human));

                // Default state, no buckled entities, strap
                Assert.True(chair.TryGetComponent(out strap));
                Assert.NotNull(strap);
                Assert.IsEmpty(strap.BuckledEntities);
                Assert.Zero(strap.OccupiedSize);

                // Side effects of buckling
                Assert.True(buckle.TryBuckle(human, chair));
                Assert.NotNull(buckle.BuckledTo);
                Assert.True(buckle.Buckled);
                Assert.True(((BuckleComponentState)buckle.GetComponentState()).Buckled);
                Assert.False(ActionBlockerSystem.CanMove(human));
                Assert.False(ActionBlockerSystem.CanChangeDirection(human));
                Assert.False(EffectBlockerSystem.CanFall(human));
                Assert.That(human.Transform.WorldPosition, Is.EqualTo(chair.Transform.WorldPosition));

                // Side effects of buckling for the strap
                Assert.That(strap.BuckledEntities, Does.Contain(human));
                Assert.That(strap.OccupiedSize, Is.EqualTo(buckle.Size));
                Assert.Positive(strap.OccupiedSize);

                // Trying to buckle while already buckled fails
                Assert.False(buckle.TryBuckle(human, chair));

                // Trying to unbuckle too quickly fails
                Assert.False(buckle.TryUnbuckle(human));
                Assert.False(buckle.ToggleBuckle(human, chair));
                Assert.True(buckle.Buckled);
            });

            // Wait enough ticks for the unbuckling cooldown to run out
            await server.WaitRunTicks(60);

            await server.WaitAssertion(() =>
            {
                // Still buckled
                Assert.True(buckle.Buckled);

                // Unbuckle
                Assert.True(buckle.TryUnbuckle(human));
                Assert.Null(buckle.BuckledTo);
                Assert.False(buckle.Buckled);
                Assert.True(ActionBlockerSystem.CanMove(human));
                Assert.True(ActionBlockerSystem.CanChangeDirection(human));
                Assert.True(EffectBlockerSystem.CanFall(human));

                // Unbuckle, strap
                Assert.IsEmpty(strap.BuckledEntities);
                Assert.Zero(strap.OccupiedSize);

                // Re-buckling has no cooldown
                Assert.True(buckle.TryBuckle(human, chair));
                Assert.True(buckle.Buckled);

                // On cooldown
                Assert.False(buckle.TryUnbuckle(human));
                Assert.True(buckle.Buckled);
                Assert.False(buckle.ToggleBuckle(human, chair));
                Assert.True(buckle.Buckled);
                Assert.False(buckle.ToggleBuckle(human, chair));
                Assert.True(buckle.Buckled);
            });

            // Wait enough ticks for the unbuckling cooldown to run out
            await server.WaitRunTicks(60);

            await server.WaitAssertion(() =>
            {
                // Still buckled
                Assert.True(buckle.Buckled);

                // Unbuckle
                Assert.True(buckle.TryUnbuckle(human));
                Assert.False(buckle.Buckled);

                // Move away from the chair
                human.Transform.WorldPosition += (1000, 1000);

                // Out of range
                Assert.False(buckle.TryBuckle(human, chair));
                Assert.False(buckle.TryUnbuckle(human));
                Assert.False(buckle.ToggleBuckle(human, chair));

                // Move near the chair
                human.Transform.WorldPosition = chair.Transform.WorldPosition + (0.5f, 0);

                // In range
                Assert.True(buckle.TryBuckle(human, chair));
                Assert.True(buckle.Buckled);
                Assert.False(buckle.TryUnbuckle(human));
                Assert.True(buckle.Buckled);
                Assert.False(buckle.ToggleBuckle(human, chair));
                Assert.True(buckle.Buckled);

                // Force unbuckle
                Assert.True(buckle.TryUnbuckle(human, true));
                Assert.False(buckle.Buckled);
                Assert.True(ActionBlockerSystem.CanMove(human));
                Assert.True(ActionBlockerSystem.CanChangeDirection(human));
                Assert.True(EffectBlockerSystem.CanFall(human));

                // Re-buckle
                Assert.True(buckle.TryBuckle(human, chair));

                // Move away from the chair
                human.Transform.WorldPosition += (1, 0);
            });

            await server.WaitRunTicks(1);

            await server.WaitAssertion(() =>
            {
                // No longer buckled
                Assert.False(buckle.Buckled);
                Assert.Null(buckle.BuckledTo);
                Assert.IsEmpty(strap.BuckledEntities);
            });
        }
 public bool CanBeStripped(IEntity by)
 {
     return(by != Owner &&
            by.HasComponent <ISharedHandsComponent>() &&
            ActionBlockerSystem.CanInteract(by));
 }
Exemple #20
0
        public async void UserInteraction(IEntity user, EntityCoordinates coordinates, EntityUid clickedUid)
        {
            if (user.TryGetComponent(out CombatModeComponent? combatMode) && combatMode.IsInCombatMode)
            {
                DoAttack(user, coordinates, false, clickedUid);
                return;
            }

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

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

            // Get entity clicked upon from UID if valid UID, if not assume no entity clicked upon and null
            EntityManager.TryGetEntity(clickedUid, out var target);

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

            // Verify user has a hand, and find what object he is currently holding in his active hand
            if (!user.TryGetComponent <IHandsComponent>(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 == null || !inRangeUnobstructed)
            {
                if (item == null)
                {
                    return;
                }

                if (!await InteractUsingRanged(user, item, target, coordinates, inRangeUnobstructed) &&
                    !inRangeUnobstructed)
                {
                    var message = Loc.GetString("You can't reach there!");
                    user.PopupMessage(message);
                }

                return;
            }
            else
            {
                // We are close to the nearby object and the object isn't contained in our active hand
                // InteractUsing/AfterInteract: We will either use the item on the nearby object
                if (item != null)
                {
                    await InteractUsing(user, item, target, coordinates);
                }
                // InteractHand/Activate: Since our hand is empty we will use InteractHand/Activate
                else
                {
                    InteractHand(user, target);
                }
            }
        }
        private async void PlaceActiveHandItemInHands(IEntity user, string hand)
        {
            var hands     = Owner.GetComponent <HandsComponent>();
            var userHands = user.GetComponent <HandsComponent>();
            var item      = userHands.GetActiveHand;

            bool Check()
            {
                if (!ActionBlockerSystem.CanInteract(user))
                {
                    return(false);
                }

                if (item == null)
                {
                    user.PopupMessageCursor(Loc.GetString("You aren't holding anything!"));
                    return(false);
                }

                if (!userHands.CanDrop(userHands.ActiveHand !))
                {
                    user.PopupMessageCursor(Loc.GetString("You can't drop that!"));
                    return(false);
                }

                if (!hands.HasHand(hand))
                {
                    return(false);
                }

                if (hands.TryGetItem(hand, out var _))
                {
                    user.PopupMessageCursor(Loc.GetString("{0:They} already {0:have} something there!", Owner));
                    return(false);
                }

                if (!hands.CanPutInHand(item, hand, false))
                {
                    user.PopupMessageCursor(Loc.GetString("{0:They} cannot put that there!", Owner));
                    return(false);
                }

                return(true);
            }

            var doAfterSystem = EntitySystem.Get <DoAfterSystem>();

            var doAfterArgs = new DoAfterEventArgs(user, StripDelay, CancellationToken.None, Owner)
            {
                ExtraCheck        = Check,
                BreakOnStun       = true,
                BreakOnDamage     = true,
                BreakOnTargetMove = true,
                BreakOnUserMove   = true,
                NeedHand          = true,
            };

            var result = await doAfterSystem.DoAfter(doAfterArgs);

            if (result != DoAfterStatus.Finished)
            {
                return;
            }

            userHands.Drop(hand, false);
            hands.PutInHand(item !, hand, false, false);
            UpdateSubscribed();
        }
Exemple #22
0
        async Task <bool> IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
        {
            var user      = eventArgs.User;
            var usingItem = eventArgs.Using;

            if (usingItem == null || usingItem.Deleted || !ActionBlockerSystem.CanInteract(user))
            {
                return(false);
            }

            if (usingItem.TryGetComponent(out SeedComponent? seeds))
            {
                if (Seed == null)
                {
                    if (seeds.Seed == null)
                    {
                        user.PopupMessageCursor(Loc.GetString("The packet seems to be empty. You throw it away."));
                        usingItem.Delete();
                        return(false);
                    }

                    user.PopupMessageCursor(Loc.GetString("You plant the {0} {1}.", seeds.Seed.SeedName, seeds.Seed.SeedNoun));

                    Seed       = seeds.Seed;
                    Dead       = false;
                    Age        = 1;
                    Health     = Seed.Endurance;
                    _lastCycle = _gameTiming.CurTime;

                    usingItem.Delete();

                    CheckLevelSanity();
                    UpdateSprite();

                    return(true);
                }

                user.PopupMessageCursor(Loc.GetString("The {0} already has seeds in it!", Owner.Name));
                return(false);
            }

            if (usingItem.HasTag("Hoe"))
            {
                if (WeedLevel > 0)
                {
                    user.PopupMessageCursor(Loc.GetString("You remove the weeds from the {0}.", Owner.Name));
                    user.PopupMessageOtherClients(Loc.GetString("{0} starts uprooting the weeds.", user.Name));
                    WeedLevel = 0;
                    UpdateSprite();
                }
                else
                {
                    user.PopupMessageCursor(Loc.GetString("This plot is devoid of weeds! It doesn't need uprooting."));
                }

                return(true);
            }

            if (usingItem.HasTag("Shovel"))
            {
                if (Seed != null)
                {
                    user.PopupMessageCursor(Loc.GetString("You remove the plant from the {0}.", Owner.Name));
                    user.PopupMessageOtherClients(Loc.GetString("{0} removes the plant.", user.Name));
                    RemovePlant();
                }
                else
                {
                    user.PopupMessageCursor(Loc.GetString("There is no plant to remove."));
                }

                return(true);
            }

            if (usingItem.TryGetComponent(out ISolutionInteractionsComponent? solution) && solution.CanDrain)
            {
                var amount  = ReagentUnit.New(5);
                var sprayed = false;

                if (usingItem.TryGetComponent(out SprayComponent? spray))
                {
                    sprayed = true;
                    amount  = ReagentUnit.New(1);

                    if (!string.IsNullOrEmpty(spray.SpraySound))
                    {
                        SoundSystem.Play(Filter.Pvs(usingItem), spray.SpraySound, usingItem, AudioHelpers.WithVariation(0.125f));
                    }
                }

                var split = solution.Drain(amount);
                if (split.TotalVolume == 0)
                {
                    user.PopupMessageCursor(Loc.GetString("{0:TheName} is empty!", usingItem));
                    return(true);
                }

                user.PopupMessageCursor(Loc.GetString(
                                            sprayed ? "You spray {0:TheName}" : "You transfer {1}u to {0:TheName}",
                                            Owner, split.TotalVolume));

                _solutionContainer?.TryAddSolution(split);

                ForceUpdateByExternalCause();

                return(true);
            }

            if (usingItem.HasTag("PlantSampleTaker"))
            {
                if (Seed == null)
                {
                    user.PopupMessageCursor(Loc.GetString("There is nothing to take a sample of!"));
                    return(false);
                }

                if (Sampled)
                {
                    user.PopupMessageCursor(Loc.GetString("This plant has already been sampled."));
                    return(false);
                }

                if (Dead)
                {
                    user.PopupMessageCursor(Loc.GetString("This plant is dead."));
                    return(false);
                }

                var seed = Seed.SpawnSeedPacket(user.Transform.Coordinates);
                seed.RandomOffset(0.25f);
                user.PopupMessageCursor(Loc.GetString($"You take a sample from the {Seed.DisplayName}."));
                Health -= (_random.Next(3, 5) * 10);

                if (_random.Prob(0.3f))
                {
                    Sampled = true;
                }

                // Just in case.
                CheckLevelSanity();
                ForceUpdateByExternalCause();

                return(true);
            }

            if (usingItem.HasTag("BotanySharp"))
            {
                return(DoHarvest(user));
            }

            if (usingItem.HasComponent <ProduceComponent>())
            {
                user.PopupMessageCursor(Loc.GetString("You compost {1:theName} into {0:theName}.", Owner, usingItem));
                user.PopupMessageOtherClients(Loc.GetString("{0:TheName} composts {1:theName} into {2:theName}.", user, usingItem, Owner));

                if (usingItem.TryGetComponent(out SolutionContainerComponent? solution2))
                {
                    // This deliberately discards overfill.
                    _solutionContainer?.TryAddSolution(solution2.SplitSolution(solution2.Solution.TotalVolume));

                    ForceUpdateByExternalCause();
                }

                usingItem.Delete();

                return(true);
            }

            return(false);
        }
Exemple #23
0
        /// <summary>
        ///     Throw an entity in the direction of <paramref name="targetLoc"/> from <paramref name="sourceLoc"/>.
        /// </summary>
        /// <param name="thrownEnt">The entity to throw.</param>
        /// <param name="throwForce">
        /// The force to throw the entity with.
        /// Total impulse applied is equal to this force applied for one second.
        /// </param>
        /// <param name="targetLoc">
        /// The target location to throw at.
        /// This is only used to calculate a direction,
        /// actual distance is purely determined by <paramref name="throwForce"/>.
        /// </param>
        /// <param name="sourceLoc">
        /// The position to start the throw from.
        /// </param>
        /// <param name="spread">
        /// If true, slightly spread the actual throw angle.
        /// </param>
        /// <param name="throwSourceEnt">
        /// The entity that did the throwing. An opposite impulse will be applied to this entity if passed in.
        /// </param>
        public static void Throw(this IEntity thrownEnt, float throwForce, EntityCoordinates targetLoc, EntityCoordinates sourceLoc, bool spread = false, IEntity throwSourceEnt = null)
        {
            if (!thrownEnt.TryGetComponent(out IPhysicsComponent colComp))
            {
                return;
            }

            var entityManager = IoCManager.Resolve <IEntityManager>();

            colComp.CanCollide = true;
            // I can now collide with player, so that i can do damage.

            if (!thrownEnt.TryGetComponent(out ThrownItemComponent projComp))
            {
                projComp = thrownEnt.AddComponent <ThrownItemComponent>();

                if (colComp.PhysicsShapes.Count == 0)
                {
                    colComp.PhysicsShapes.Add(new PhysShapeAabb());
                }

                colComp.PhysicsShapes[0].CollisionMask |= (int)CollisionGroup.ThrownItem;
                colComp.Status = BodyStatus.InAir;
            }
            var angle = new Angle(targetLoc.ToMapPos(entityManager) - sourceLoc.ToMapPos(entityManager));

            if (spread)
            {
                var spreadRandom = IoCManager.Resolve <IRobustRandom>();
                angle += Angle.FromDegrees(spreadRandom.NextGaussian(0, 3));
            }

            if (throwSourceEnt != null)
            {
                projComp.User = throwSourceEnt;
                projComp.IgnoreEntity(throwSourceEnt);

                if (ActionBlockerSystem.CanChangeDirection(throwSourceEnt))
                {
                    throwSourceEnt.Transform.LocalRotation = angle.GetCardinalDir().ToAngle();
                }
            }

            // scaling is handled elsewhere, this is just multiplying by 60 independent of timing as a fix until elsewhere values are updated
            var spd = throwForce * 60;

            projComp.StartThrow(angle.ToVec(), spd);

            if (throwSourceEnt != null &&
                throwSourceEnt.TryGetComponent <IPhysicsComponent>(out var physics))
            {
                if (throwSourceEnt.IsWeightless())
                {
                    // We don't check for surrounding entities,
                    // so you'll still get knocked around if you're hugging the station wall in zero g.
                    // I got kinda lazy is the reason why. Also it makes a bit of sense.
                    // If somebody wants they can come along and make it so magboots completely hold you still.
                    // Would be a cool incentive to use them.
                    const float throwFactor = 0.2f; // Break Newton's Third Law for better gameplay
                    var         mover       = physics.EnsureController <ThrowKnockbackController>();
                    mover.Push(-angle.ToVec(), spd * throwFactor);
                }
            }
        }
Exemple #24
0
        public void DoAttack(IEntity user, EntityCoordinates coordinates, bool wideAttack, EntityUid targetUid = default)
        {
            if (!ValidateInteractAndFace(user, coordinates))
            {
                return;
            }

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

            IEntity?targetEnt = null;

            if (!wideAttack)
            {
                // Get entity clicked upon from UID if valid UID, if not assume no entity clicked upon and null
                EntityManager.TryGetEntity(targetUid, out targetEnt);

                // Check if interacted entity is in the same container, the direct child, or direct parent of the user.
                if (targetEnt != null && !user.IsInSameOrParentContainer(targetEnt))
                {
                    Logger.WarningS("system.interaction",
                                    $"User entity named {user.Name} clicked on object {targetEnt.Name} 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 he is currently holding in his active hand
            if (user.TryGetComponent <IHandsComponent>(out var hands))
            {
                var item = hands.GetActiveHand?.Owner;

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

                        if (ev.Handled)
                        {
                            return;
                        }
                    }
                    else
                    {
                        var ev = new ClickAttackEvent(item, user, coordinates, targetUid);
                        RaiseLocalEvent(item.Uid, ev, false);

                        if (ev.Handled)
                        {
                            return;
                        }
                    }
                }
                else if (!wideAttack &&
                         (targetEnt != null || EntityManager.TryGetEntity(targetUid, out targetEnt)) &&
                         targetEnt.HasComponent <ItemComponent>())
                {
                    // We pick up items if our hand is empty, even if we're in combat mode.
                    InteractHand(user, targetEnt);
                    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)
            {
                RaiseLocalEvent(user.Uid, new WideAttackEvent(user, user, coordinates), false);
            }
            else
            {
                RaiseLocalEvent(user.Uid, new ClickAttackEvent(user, user, coordinates, targetUid), false);
            }
        }