예제 #1
0
 protected override void Initialize()
 {
     base.Initialize();
     _powerSolarSystem = _entitySystemManager.GetEntitySystem <PowerSolarSystem>();
 }
예제 #2
0
        public virtual bool TryUseFood(IEntity?user, IEntity?target, UtensilComponent?utensilUsed = null)
        {
            if (!Owner.TryGetComponent(out SolutionContainerComponent? solution))
            {
                return(false);
            }

            if (user == null)
            {
                return(false);
            }

            if (UsesRemaining <= 0)
            {
                user.PopupMessage(Loc.GetString("{0:TheName} is empty!", Owner));
                return(false);
            }

            var trueTarget = target ?? user;

            if (!trueTarget.TryGetComponent(out IBody? body) ||
                !body.TryGetMechanismBehaviors <StomachBehavior>(out var stomachs))
            {
                return(false);
            }

            var utensils = utensilUsed != null
                ? new List <UtensilComponent> {
                utensilUsed
            }
                : null;

            if (_utensilsNeeded != UtensilType.None)
            {
                utensils = new List <UtensilComponent>();
                var types = UtensilType.None;

                if (user.TryGetComponent(out HandsComponent? hands))
                {
                    foreach (var item in hands.GetAllHeldItems())
                    {
                        if (!item.Owner.TryGetComponent(out UtensilComponent? utensil))
                        {
                            continue;
                        }

                        utensils.Add(utensil);
                        types |= utensil.Types;
                    }
                }

                if (!types.HasFlag(_utensilsNeeded))
                {
                    trueTarget.PopupMessage(user, Loc.GetString("food-you-need-to-hold-utensil", ("utensil", _utensilsNeeded)));
                    return(false);
                }
            }

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

            var transferAmount = ReagentUnit.Min(_transferAmount, solution.CurrentVolume);
            var split          = solution.SplitSolution(transferAmount);
            var firstStomach   = stomachs.FirstOrDefault(stomach => stomach.CanTransferSolution(split));

            if (firstStomach == null)
            {
                trueTarget.PopupMessage(user, Loc.GetString("You can't eat any more!"));
                return(false);
            }

            // TODO: Account for partial transfer.

            split.DoEntityReaction(trueTarget, ReactionMethod.Ingestion);

            firstStomach.TryTransferSolution(split);

            _entitySystem.GetEntitySystem <AudioSystem>()
            .PlayFromEntity(_useSound, trueTarget, AudioParams.Default.WithVolume(-1f));
            trueTarget.PopupMessage(user, Loc.GetString("Nom"));

            // If utensils were used
            if (utensils != null)
            {
                foreach (var utensil in utensils)
                {
                    utensil.TryBreak(user);
                }
            }

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

            if (string.IsNullOrEmpty(_trashPrototype))
            {
                Owner.Delete();
                return(true);
            }

            //We're empty. Become trash.
            var position = Owner.Transform.Coordinates;
            var finisher = Owner.EntityManager.SpawnEntity(_trashPrototype, position);

            // If the user is holding the item
            if (user.TryGetComponent(out HandsComponent? handsComponent) &&
                handsComponent.IsHolding(Owner))
            {
                Owner.Delete();

                // Put the trash in the user's hand
                if (finisher.TryGetComponent(out ItemComponent? item) &&
                    handsComponent.CanPutInHand(item))
                {
                    handsComponent.PutInHand(item);
                }
            }
예제 #3
0
        public virtual bool TryUseFood(IEntity user, IEntity target, UtensilComponent utensilUsed = null)
        {
            if (user == null)
            {
                return(false);
            }

            if (UsesRemaining <= 0)
            {
                user.PopupMessage(user, Loc.GetString("{0:TheName} is empty!", Owner));
                return(false);
            }

            var trueTarget = target ?? user;

            if (!trueTarget.TryGetComponent(out StomachComponent stomach))
            {
                return(false);
            }

            var utensils = utensilUsed != null
                ? new List <UtensilComponent> {
                utensilUsed
            }
                : null;

            if (_utensilsNeeded != UtensilType.None)
            {
                utensils = new List <UtensilComponent>();
                var types = UtensilType.None;

                if (user.TryGetComponent(out HandsComponent hands))
                {
                    foreach (var item in hands.GetAllHeldItems())
                    {
                        if (!item.Owner.TryGetComponent(out UtensilComponent utensil))
                        {
                            continue;
                        }

                        utensils.Add(utensil);
                        types |= utensil.Types;
                    }
                }

                if (!types.HasFlag(_utensilsNeeded))
                {
                    trueTarget.PopupMessage(user, Loc.GetString("You need to be holding a {0} to eat that!", _utensilsNeeded));
                    return(false);
                }
            }

            if (!InteractionChecks.InRangeUnobstructed(user, trueTarget.Transform.MapPosition))
            {
                return(false);
            }

            var transferAmount = ReagentUnit.Min(_transferAmount, _contents.CurrentVolume);
            var split          = _contents.SplitSolution(transferAmount);

            if (!stomach.TryTransferSolution(split))
            {
                _contents.TryAddSolution(split);
                trueTarget.PopupMessage(user, Loc.GetString("You can't eat any more!"));
                return(false);
            }

            _entitySystem.GetEntitySystem <AudioSystem>()
            .PlayFromEntity(_useSound, trueTarget, AudioParams.Default.WithVolume(-1f));
            trueTarget.PopupMessage(user, Loc.GetString("Nom"));

            // If utensils were used
            if (utensils != null)
            {
                foreach (var utensil in utensils)
                {
                    utensil.TryBreak(user);
                }
            }

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

            if (string.IsNullOrEmpty(_trashPrototype))
            {
                Owner.Delete();
                return(true);
            }

            //We're empty. Become trash.
            var position = Owner.Transform.GridPosition;
            var finisher = Owner.EntityManager.SpawnEntity(_trashPrototype, position);

            // If the user is holding the item
            if (user.TryGetComponent(out HandsComponent handsComponent) &&
                handsComponent.IsHolding(Owner))
            {
                Owner.Delete();

                // Put the trash in the user's hand
                if (finisher.TryGetComponent(out ItemComponent item) &&
                    handsComponent.CanPutInHand(item))
                {
                    handsComponent.PutInHand(item);
                }
            }
 public override void Initialize()
 {
     base.Initialize();
     _actSystem = _entitySystemManager.GetEntitySystem <ActSystem>();
 }
 public void Init()
 {
     _reactions   = _prototypeManager.EnumeratePrototypes <ReactionPrototype>();
     _audioSystem = _entitySystemManager.GetEntitySystem <AudioSystem>();
 }
        /// <summary>
        /// Gets called by an AreaEffectInceptionComponent. "Clones" Owner into the four directions and copies the
        /// solution into each of them.
        /// </summary>
        public void Spread()
        {
            var meta = _entities.GetComponent <MetaDataComponent>(Owner);

            if (meta.EntityPrototype == null)
            {
                Logger.Error("AreaEffectComponent needs its owner to be spawned by a prototype.");
                return;
            }

            var xform  = _entities.GetComponent <TransformComponent>(Owner);
            var solSys = _systems.GetEntitySystem <SolutionContainerSystem>();

            if (!_entities.TryGetComponent(xform.GridUid, out IMapGridComponent? gridComp))
            {
                return;
            }

            var grid   = gridComp.Grid;
            var origin = grid.TileIndicesFor(xform.Coordinates);

            DebugTools.Assert(xform.Anchored, "Area effect entity prototypes must be anchored.");

            void SpreadToDir(Direction dir)
            {
                // Currently no support for spreading off or across grids.
                var index = origin + dir.ToIntVec();

                if (!grid.TryGetTileRef(index, out var tile) || tile.Tile.IsEmpty)
                {
                    return;
                }

                foreach (var neighbor in grid.GetAnchoredEntities(index))
                {
                    if (_entities.TryGetComponent(neighbor,
                                                  out SolutionAreaEffectComponent? comp) && comp.Inception == Inception)
                    {
                        return;
                    }

                    // TODO for thindows and the like, need to check the directions that are being blocked.
                    // --> would then also mean you need to check for blockers on the origin tile.
                    if (_entities.TryGetComponent(neighbor,
                                                  out AirtightComponent? airtight) && airtight.AirBlocked)
                    {
                        return;
                    }
                }

                var newEffect = _entities.SpawnEntity(
                    meta.EntityPrototype.ID,
                    grid.GridTileToLocal(index));

                if (!_entities.TryGetComponent(newEffect, out SolutionAreaEffectComponent? effectComponent))
                {
                    _entities.DeleteEntity(newEffect);
                    return;
                }

                if (solSys.TryGetSolution(Owner, SolutionName, out var solution))
                {
                    effectComponent.TryAddSolution(solution.Clone());
                }

                effectComponent.Amount = Amount - 1;
                Inception?.Add(effectComponent);
            }

            SpreadToDir(Direction.North);
            SpreadToDir(Direction.East);
            SpreadToDir(Direction.South);
            SpreadToDir(Direction.West);
        }
        private bool Explosion()
        {
            //Prevent adjacent explosives from infinitely blowing each other up.
            if (_beingExploded)
            {
                return(true);
            }
            _beingExploded = true;

            var maxRange = MathHelper.Max(DevastationRange, HeavyImpactRange, LightImpactRange, 0f);
            //Entity damage calculation
            var entitiesAll = _serverEntityManager.GetEntitiesInRange(Owner.Transform.GridPosition, maxRange).ToList();

            foreach (var entity in entitiesAll)
            {
                if (entity == Owner)
                {
                    continue;
                }
                if (!entity.Transform.IsMapTransform)
                {
                    continue;
                }
                var distanceFromEntity = (int)entity.Transform.GridPosition.Distance(_mapManager, Owner.Transform.GridPosition);
                var exAct    = _entitySystemManager.GetEntitySystem <ActSystem>();
                var severity = ExplosionSeverity.Destruction;
                if (distanceFromEntity < DevastationRange)
                {
                    severity = ExplosionSeverity.Destruction;
                }
                else if (distanceFromEntity < HeavyImpactRange)
                {
                    severity = ExplosionSeverity.Heavy;
                }
                else if (distanceFromEntity < LightImpactRange)
                {
                    severity = ExplosionSeverity.Light;
                }
                else
                {
                    continue;
                }
                exAct.HandleExplosion(Owner, entity, severity);
            }

            //Tile damage calculation mockup
            //TODO: make it into some sort of actual damage component or whatever the boys think is appropriate
            var mapGrid = _mapManager.GetGrid(Owner.Transform.GridPosition.GridID);
            var circle  = new Circle(Owner.Transform.GridPosition.Position, maxRange);
            var tiles   = mapGrid.GetTilesIntersecting(circle);

            foreach (var tile in tiles)
            {
                var tileLoc          = mapGrid.GridTileToLocal(tile.GridIndices);
                var tileDef          = (ContentTileDefinition)_tileDefinitionManager[tile.Tile.TypeId];
                var distanceFromTile = (int)tileLoc.Distance(_mapManager, Owner.Transform.GridPosition);
                if (!string.IsNullOrWhiteSpace(tileDef.SubFloor))
                {
                    if (distanceFromTile < DevastationRange)
                    {
                        mapGrid.SetTile(tileLoc, new Tile(_tileDefinitionManager["space"].TileId));
                    }
                    if (distanceFromTile < HeavyImpactRange)
                    {
                        if (_robustRandom.Prob(80))
                        {
                            mapGrid.SetTile(tileLoc, new Tile(_tileDefinitionManager[tileDef.SubFloor].TileId));
                        }
                        else
                        {
                            mapGrid.SetTile(tileLoc, new Tile(_tileDefinitionManager["space"].TileId));
                        }
                    }
                    if (distanceFromTile < LightImpactRange)
                    {
                        if (_robustRandom.Prob(50))
                        {
                            mapGrid.SetTile(tileLoc, new Tile(_tileDefinitionManager[tileDef.SubFloor].TileId));
                        }
                    }
                }
            }

            //Effects and sounds
            var time    = IoCManager.Resolve <IGameTiming>().CurTime;
            var message = new EffectSystemMessage
            {
                EffectSprite = "Effects/explosion.rsi",
                RsiState     = "explosionfast",
                Born         = time,
                DeathTime    = time + TimeSpan.FromSeconds(5),
                Size         = new Vector2(FlashRange / 2, FlashRange / 2),
                Coordinates  = Owner.Transform.GridPosition,
                //Rotated from east facing
                Rotation   = 0f,
                ColorDelta = new Vector4(0, 0, 0, -1500f),
                Color      = Vector4.Multiply(new Vector4(255, 255, 255, 750), 0.5f),
                Shaded     = false
            };

            _entitySystemManager.GetEntitySystem <EffectSystem>().CreateParticle(message);
            _entitySystemManager.GetEntitySystem <AudioSystem>().Play("/Audio/effects/explosion.ogg", Owner);

            // Knock back cameras of all players in the area.

            var playerManager = IoCManager.Resolve <IPlayerManager>();
            var selfPos       = Owner.Transform.WorldPosition;

            foreach (var player in playerManager.GetAllPlayers())
            {
                if (player.AttachedEntity == null ||
                    player.AttachedEntity.Transform.MapID != mapGrid.ParentMapId ||
                    !player.AttachedEntity.TryGetComponent(out CameraRecoilComponent recoil))
                {
                    continue;
                }

                var playerPos = player.AttachedEntity.Transform.WorldPosition;
                var delta     = selfPos - playerPos;
                var distance  = delta.LengthSquared;

                var effect = 1 / (1 + 0.2f * distance);
                if (effect > 0.01f)
                {
                    var kick = -delta.Normalized * effect;
                    recoil.Kick(kick);
                }
            }

            Owner.Delete();
            return(true);
        }
        private void TryCreatePackage(EntityUid user, UiAction action, string label, int pillAmount, int bottleAmount)
        {
            if (BufferSolution.TotalVolume == 0)
            {
                user.PopupMessageCursor(Loc.GetString("chem-master-window-buffer-empty-text"));
                return;
            }

            var handSys = _sysMan.GetEntitySystem <SharedHandsSystem>();
            var solSys  = _sysMan.GetEntitySystem <SolutionContainerSystem>();

            if (action == UiAction.CreateBottles)
            {
                var individualVolume = BufferSolution.TotalVolume / FixedPoint2.New(bottleAmount);
                if (individualVolume < FixedPoint2.New(1))
                {
                    user.PopupMessageCursor(Loc.GetString("chem-master-window-buffer-low-text"));
                    return;
                }

                var actualVolume = FixedPoint2.Min(individualVolume, FixedPoint2.New(30));
                for (int i = 0; i < bottleAmount; i++)
                {
                    var bottle = _entities.SpawnEntity("ChemistryEmptyBottle01", _entities.GetComponent <TransformComponent>(Owner).Coordinates);

                    //Adding label
                    LabelComponent labelComponent = bottle.EnsureComponent <LabelComponent>();
                    labelComponent.OriginalName = _entities.GetComponent <MetaDataComponent>(bottle).EntityName;
                    string val = _entities.GetComponent <MetaDataComponent>(bottle).EntityName + $" ({label})";
                    _entities.GetComponent <MetaDataComponent>(bottle).EntityName = val;
                    labelComponent.CurrentLabel = label;

                    var bufferSolution = BufferSolution.SplitSolution(actualVolume);
                    var bottleSolution = solSys.EnsureSolution(bottle, "drink");

                    solSys.TryAddSolution(bottle, bottleSolution, bufferSolution);

                    //Try to give them the bottle
                    handSys.PickupOrDrop(user, bottle);
                }
            }
            else //Pills
            {
                var individualVolume = BufferSolution.TotalVolume / FixedPoint2.New(pillAmount);
                if (individualVolume < FixedPoint2.New(1))
                {
                    user.PopupMessageCursor(Loc.GetString("chem-master-window-buffer-low-text"));
                    return;
                }

                var actualVolume = FixedPoint2.Min(individualVolume, FixedPoint2.New(50));
                for (int i = 0; i < pillAmount; i++)
                {
                    var pill = _entities.SpawnEntity("Pill", _entities.GetComponent <TransformComponent>(Owner).Coordinates);

                    //Adding label
                    LabelComponent labelComponent = pill.EnsureComponent <LabelComponent>();
                    labelComponent.OriginalName = _entities.GetComponent <MetaDataComponent>(pill).EntityName;
                    string val = _entities.GetComponent <MetaDataComponent>(pill).EntityName + $" ({label})";
                    _entities.GetComponent <MetaDataComponent>(pill).EntityName = val;
                    labelComponent.CurrentLabel = label;

                    var bufferSolution = BufferSolution.SplitSolution(actualVolume);
                    var pillSolution   = EntitySystem.Get <SolutionContainerSystem>().EnsureSolution(pill, "food");
                    solSys.TryAddSolution(pill, pillSolution, bufferSolution);

                    //Change pill Sprite component state
                    if (!_entities.TryGetComponent(pill, out SpriteComponent? sprite))
                    {
                        return;
                    }
                    sprite?.LayerSetState(0, "pill" + _pillType);

                    //Try to give them the bottle
                    handSys.PickupOrDrop(user, pill);
                }
            }

            if (_bufferSolution?.Contents.Count == 0)
            {
                _label = "";
            }

            UpdateUserInterface();
        }
        public override void FrameUpdate(FrameEventArgs e)
        {
            base.FrameUpdate(e);

            // If there is no local player, there is no session, and therefore nothing to do here.
            var localPlayer = PlayerManager.LocalPlayer;

            if (localPlayer == null)
            {
                return;
            }

            var mousePosWorld = EyeManager.ScreenToMap(InputManager.MouseScreenPosition);
            var entityToClick = UserInterfaceManager.CurrentlyHovered != null
                ? null
                : GetEntityUnderPosition(mousePosWorld);

            var inRange = false;

            if (localPlayer.ControlledEntity != null && entityToClick != null)
            {
                var playerPos = localPlayer.ControlledEntity.Transform.MapPosition;
                var entityPos = entityToClick.Transform.MapPosition;
                inRange = EntitySystemManager.GetEntitySystem <SharedInteractionSystem>()
                          .InRangeUnobstructed(playerPos, entityPos,
                                               predicate: entity =>
                                               entity == localPlayer.ControlledEntity || entity == entityToClick,
                                               ignoreInsideBlocker: true);
            }

            InteractionOutlineComponent outline;

            if (!ConfigurationManager.GetCVar <bool>("outline.enabled"))
            {
                if (entityToClick != null && entityToClick.TryGetComponent(out outline))
                {
                    outline.OnMouseLeave(); //Prevent outline remains from persisting post command.
                }
                return;
            }

            if (entityToClick == _lastHoveredEntity)
            {
                if (entityToClick != null && entityToClick.TryGetComponent(out outline))
                {
                    outline.UpdateInRange(inRange);
                }

                return;
            }

            if (_lastHoveredEntity != null && !_lastHoveredEntity.Deleted &&
                _lastHoveredEntity.TryGetComponent(out outline))
            {
                outline.OnMouseLeave();
            }

            _lastHoveredEntity = entityToClick;

            if (_lastHoveredEntity != null && _lastHoveredEntity.TryGetComponent(out outline))
            {
                outline.OnMouseEnter(inRange);
            }
        }
        void TryStartStructureConstruction(GridCoordinates loc, string prototypeName, Angle angle, int ack)
        {
            var prototype = _prototypeManager.Index <ConstructionPrototype>(prototypeName);

            var transform = Owner.Transform;

            if (!loc.InRange(_mapManager, transform.GridPosition, InteractionSystem.InteractionRange))
            {
                return;
            }

            if (prototype.Stages.Count < 2)
            {
                throw new InvalidOperationException($"Prototype '{prototypeName}' does not have enough stages.");
            }

            var stage0 = prototype.Stages[0];

            if (!(stage0.Forward is ConstructionStepMaterial matStep))
            {
                throw new NotImplementedException();
            }

            // Try to find the stack with the material in the user's hand.
            var hands      = Owner.GetComponent <HandsComponent>();
            var activeHand = hands.GetActiveHand?.Owner;

            if (activeHand == null)
            {
                return;
            }

            if (!activeHand.TryGetComponent(out StackComponent stack) || !ConstructionComponent.MaterialStackValidFor(matStep, stack))
            {
                return;
            }

            if (!stack.Use(matStep.Amount))
            {
                return;
            }

            // OK WE'RE GOOD CONSTRUCTION STARTED.
            _entitySystemManager.GetEntitySystem <AudioSystem>().Play("/Audio/items/deconstruct.ogg", loc);
            if (prototype.Stages.Count == 2)
            {
                // Exactly 2 stages, so don't make an intermediate frame.
                var ent = _serverEntityManager.SpawnEntity(prototype.Result, loc);
                ent.Transform.LocalRotation = angle;
            }
            else
            {
                var frame        = _serverEntityManager.SpawnEntity("structureconstructionframe", loc);
                var construction = frame.GetComponent <ConstructionComponent>();
                construction.Init(prototype);
                frame.Transform.LocalRotation = angle;
            }

            var msg = new AckStructureConstructionMessage(ack);

            SendNetworkMessage(msg);
        }
예제 #11
0
        /// <inheritdoc />
        public void SendGameStateUpdate()
        {
            DebugTools.Assert(_networkManager.IsServer);

            _entityManager.Update();

            if (!_networkManager.IsConnected)
            {
                // Prevent deletions piling up if we have no clients.
                _entityManager.CullDeletionHistory(GameTick.MaxValue);
                _mapManager.CullDeletionHistory(GameTick.MaxValue);
                return;
            }

            var inputSystem = _systemManager.GetEntitySystem <InputSystem>();

            var oldestAck = GameTick.MaxValue;


            foreach (var channel in _networkManager.Channels)
            {
                var session = _playerManager.GetSessionByChannel(channel);
                if (session == null || session.Status != SessionStatus.InGame)
                {
                    // client still joining, maybe iterate over sessions instead?
                    continue;
                }

                if (!_ackedStates.TryGetValue(channel.ConnectionId, out var lastAck))
                {
                    DebugTools.Assert("Why does this channel not have an entry?");
                }

                var entStates = lastAck == GameTick.Zero || !PvsEnabled
                    ? _entityManager.GetEntityStates(lastAck)
                    : _entityManager.UpdatePlayerSeenEntityStates(lastAck, session, _entityManager.MaxUpdateRange);

                var playerStates = _playerManager.GetPlayerStates(lastAck);
                var deletions    = _entityManager.GetDeletedEntities(lastAck);
                var mapData      = _mapManager.GetStateData(lastAck);


                // lastAck varies with each client based on lag and such, we can't just make 1 global state and send it to everyone
                var lastInputCommand  = inputSystem.GetLastInputCommand(session);
                var lastSystemMessage = _entityNetworkManager.GetLastMessageSequence(session);
                var state             = new GameState(lastAck, _gameTiming.CurTick,
                                                      Math.Max(lastInputCommand, lastSystemMessage), entStates, playerStates, deletions, mapData);
                if (lastAck < oldestAck)
                {
                    oldestAck = lastAck;
                }

                // actually send the state
                var stateUpdateMessage = _networkManager.CreateNetMessage <MsgState>();
                stateUpdateMessage.State = state;
                _networkManager.ServerSendMessage(stateUpdateMessage, channel);

                // If the state is too big we let Lidgren send it reliably.
                // This is to avoid a situation where a state is so large that it consistently gets dropped
                // (or, well, part of it).
                // When we send them reliably, we immediately update the ack so that the next state will not be huge.
                if (stateUpdateMessage.ShouldSendReliably())
                {
                    _ackedStates[channel.ConnectionId] = _gameTiming.CurTick;
                }
            }

            // keep the deletion history buffers clean
            if (oldestAck > _lastOldestAck)
            {
                _lastOldestAck = oldestAck;
                _entityManager.CullDeletionHistory(oldestAck);
                _mapManager.CullDeletionHistory(oldestAck);
            }
        }
예제 #12
0
        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);
        }
        /// <summary>
        ///     Places item in user's active hand to an inventory slot.
        /// </summary>
        private async void PlaceActiveHandItemInInventory(EntityUid user, string slot)
        {
            var userHands = _entities.GetComponent <HandsComponent>(user);
            var invSystem = _sysMan.GetEntitySystem <InventorySystem>();
            var handSys   = _sysMan.GetEntitySystem <SharedHandsSystem>();

            bool Check()
            {
                if (userHands.ActiveHand?.HeldEntity is not EntityUid held)
                {
                    user.PopupMessageCursor(Loc.GetString("strippable-component-not-holding-anything"));
                    return(false);
                }

                if (!handSys.CanDropHeld(user, userHands.ActiveHand))
                {
                    user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-drop"));
                    return(false);
                }

                if (!invSystem.HasSlot(Owner, slot))
                {
                    return(false);
                }

                if (invSystem.TryGetSlotEntity(Owner, slot, out _))
                {
                    user.PopupMessageCursor(Loc.GetString("strippable-component-item-slot-occupied", ("owner", Owner)));
                    return(false);
                }

                if (!invSystem.CanEquip(user, Owner, held, slot, out _))
                {
                    user.PopupMessageCursor(Loc.GetString("strippable-component-cannot-equip-message", ("owner", 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.WaitDoAfter(doAfterArgs);

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

            if (userHands.ActiveHand?.HeldEntity is EntityUid held &&
                handSys.TryDrop(user, userHands.ActiveHand, handsComp: userHands))
            {
                invSystem.TryEquip(user, Owner, held, slot);
            }

            UpdateState();
        }