Exemple #1
0
 private void OnStrapGetState(EntityUid uid, StrapComponent component, ref ComponentGetState args)
 {
     args.State = new StrapComponentState(component.Position, component.BuckleOffset, component.BuckledEntities, component.MaxBuckleDistance);
 }
        public async Task BuckleUnbuckleCooldownRangeTest()
        {
            var cOptions = new ClientIntegrationOptions {
                ExtraPrototypes = Prototypes
            };
            var sOptions = new ServerIntegrationOptions {
                ExtraPrototypes = Prototypes
            };

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

            EntityUid       human  = default;
            EntityUid       chair  = default;
            BuckleComponent buckle = null;
            StrapComponent  strap  = null;

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

                var actionBlocker = EntitySystem.Get <ActionBlockerSystem>();
                var standingState = EntitySystem.Get <StandingStateSystem>();

                var grid        = GetMainGrid(mapManager);
                var coordinates = new EntityCoordinates(grid.GridEntityId, 0, 0);

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

                // Default state, unbuckled
                Assert.True(entityManager.TryGetComponent(human, out buckle));
                Assert.NotNull(buckle);
                Assert.Null(buckle.BuckledTo);
                Assert.False(buckle.Buckled);
                Assert.True(actionBlocker.CanMove(human));
                Assert.True(actionBlocker.CanChangeDirection(human));
                Assert.True(standingState.Down(human));
                Assert.True(standingState.Stand(human));

                // Default state, no buckled entities, strap
                Assert.True(entityManager.TryGetComponent(chair, 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(actionBlocker.CanMove(human));
                Assert.False(actionBlocker.CanChangeDirection(human));
                Assert.False(standingState.Down(human));
                Assert.That((entityManager.GetComponent <TransformComponent>(human).WorldPosition - entityManager.GetComponent <TransformComponent>(chair).WorldPosition).Length, Is.LessThanOrEqualTo(buckle.BuckleOffset.Length));

                // 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(() =>
            {
                var actionBlocker = EntitySystem.Get <ActionBlockerSystem>();
                var standingState = EntitySystem.Get <StandingStateSystem>();

                // Still buckled
                Assert.True(buckle.Buckled);

                // Unbuckle
                Assert.True(buckle.TryUnbuckle(human));
                Assert.Null(buckle.BuckledTo);
                Assert.False(buckle.Buckled);
                Assert.True(actionBlocker.CanMove(human));
                Assert.True(actionBlocker.CanChangeDirection(human));
                Assert.True(standingState.Down(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(() =>
            {
                var entityManager = IoCManager.Resolve <IEntityManager>();
                var actionBlocker = EntitySystem.Get <ActionBlockerSystem>();
                var standingState = EntitySystem.Get <StandingStateSystem>();

                // Still buckled
                Assert.True(buckle.Buckled);

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

                // Move away from the chair
                entityManager.GetComponent <TransformComponent>(human).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
                entityManager.GetComponent <TransformComponent>(human).WorldPosition = entityManager.GetComponent <TransformComponent>(chair).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(actionBlocker.CanMove(human));
                Assert.True(actionBlocker.CanChangeDirection(human));
                Assert.True(standingState.Down(human));

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

                // Move away from the chair
                entityManager.GetComponent <TransformComponent>(human).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 Test()
        {
            var server = StartServerDummyTicker();

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

            server.Assert(() =>
            {
                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
            server.RunTicks(60);

            server.Assert(() =>
            {
                // 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
            server.RunTicks(60);

            server.Assert(() =>
            {
                // 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);
            });

            server.RunTicks(1);

            server.Assert(() =>
            {
                // No longer buckled
                Assert.False(buckle.Buckled);
                Assert.Null(buckle.BuckledTo);
                Assert.IsEmpty(strap.BuckledEntities);
            });

            await server.WaitIdleAsync();
        }
        public async Task BuckledDyingDropItemsTest()
        {
            var server = StartServer();

            IEntity              human           = null;
            IEntity              chair           = null;
            BuckleComponent      buckle          = null;
            StrapComponent       strap           = null;
            HandsComponent       hands           = null;
            IDamageableComponent humanDamageable = null;

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

                var mapId = new MapId(1);
                mapManager.CreateNewMapEntity(mapId);

                var entityManager = IoCManager.Resolve <IEntityManager>();
                var gridId        = new GridId(1);
                var grid          = mapManager.CreateGrid(mapId, gridId);
                var coordinates   = grid.GridEntityId.ToCoordinates();
                var tileManager   = IoCManager.Resolve <ITileDefinitionManager>();
                var tileId        = tileManager["underplating"].TileId;
                var tile          = new Tile(tileId);

                grid.SetTile(coordinates, tile);

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

                // Component sanity check
                Assert.True(human.TryGetComponent(out buckle));
                Assert.True(chair.TryGetComponent(out strap));
                Assert.True(human.TryGetComponent(out hands));
                Assert.True(human.TryGetComponent(out humanDamageable));

                // Buckle
                Assert.True(buckle.TryBuckle(human, chair));
                Assert.NotNull(buckle.BuckledTo);
                Assert.True(buckle.Buckled);

                // Put an item into every hand
                for (var i = 0; i < hands.Count; i++)
                {
                    var akms = entityManager.SpawnEntity("RifleAk", coordinates);

                    // Equip items
                    Assert.True(akms.TryGetComponent(out ItemComponent item));
                    Assert.True(hands.PutInHand(item));
                }
            });

            server.RunTicks(10);

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

                // With items in all hands
                foreach (var slot in hands.Hands)
                {
                    Assert.NotNull(hands.GetItem(slot));
                }

                // Banish our guy into the shadow realm
                humanDamageable.ChangeDamage(DamageClass.Brute, 1000000, true);
            });

            server.RunTicks(10);

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

                // Now with no item in any hand
                foreach (var slot in hands.Hands)
                {
                    Assert.Null(hands.GetItem(slot));
                }
            });

            await server.WaitIdleAsync();
        }
Exemple #5
0
        public async Task BuckleUnbuckleCooldownRangeTest()
        {
            var options = new ServerIntegrationOptions {
                ExtraPrototypes = Prototypes
            };
            var server = StartServer(options);

            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);
                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);
            });
        }
        // TODO ECS BUCKLE/STRAP These 'Strap' verbs are an incestuous mess of buckle component and strap component
        // functions. Whenever these are fully ECSed, maybe do it in a way that allows for these verbs to be handled in
        // a sensible manner in a single system?

        private void AddStrapVerbs(EntityUid uid, StrapComponent component, GetInteractionVerbsEvent args)
        {
            if (args.Hands == null || !args.CanAccess || !args.CanInteract)
            {
                return;
            }

            // Note that for whatever bloody reason, buckle component has its own interaction range. Additionally, this
            // range can be set per-component, so we have to check a modified InRangeUnobstructed for every verb.

            // Add unstrap verbs for every strapped entity.
            foreach (var entity in component.BuckledEntities)
            {
                var buckledComp = entity.GetComponent <BuckleComponent>();

                if (!_interactionSystem.InRangeUnobstructed(args.User, args.Target, range: buckledComp.Range))
                {
                    continue;
                }

                Verb verb = new();
                verb.Act      = () => buckledComp.TryUnbuckle(args.User);
                verb.Category = VerbCategory.Unbuckle;
                if (entity == args.User)
                {
                    verb.Text = Loc.GetString("verb-self-target-pronoun");
                }
                else
                {
                    verb.Text = entity.Name;
                }

                // In the event that you have more than once entity with the same name strapped to the same object,
                // these two verbs will be identical according to Verb.CompareTo, and only one with actually be added to
                // the verb list. However this should rarely ever be a problem. If it ever is, it could be fixed by
                // appending an integer to verb.Text to distinguish the verbs.

                args.Verbs.Add(verb);
            }

            // Add a verb to buckle the user.
            if (args.User.TryGetComponent <BuckleComponent>(out var buckle) &&
                buckle.BuckledTo != component &&
                args.User != component.Owner &&
                component.HasSpace(buckle) &&
                _interactionSystem.InRangeUnobstructed(args.User, args.Target, range: buckle.Range))
            {
                Verb verb = new();
                verb.Act      = () => buckle.TryBuckle(args.User, args.Target);
                verb.Category = VerbCategory.Buckle;
                verb.Text     = Loc.GetString("verb-self-target-pronoun");
                args.Verbs.Add(verb);
            }

            // If the user is currently holding/pulling an entity that can be buckled, add a verb for that.
            if (args.Using != null &&
                args.Using.TryGetComponent <BuckleComponent>(out var usingBuckle) &&
                component.HasSpace(usingBuckle) &&
                _interactionSystem.InRangeUnobstructed(args.Using, args.Target, range: usingBuckle.Range))
            {
                // Check that the entity is unobstructed from the target (ignoring the user).
                bool Ignored(IEntity entity) => entity == args.User || entity == args.Target || entity == args.Using;

                if (!_interactionSystem.InRangeUnobstructed(args.Using, args.Target, usingBuckle.Range, predicate: Ignored))
                {
                    return;
                }

                Verb verb = new();
                verb.Act      = () => usingBuckle.TryBuckle(args.User, args.Target);
                verb.Category = VerbCategory.Buckle;
                verb.Text     = args.Using.Name;

                // If the used entity is a person being pulled, prioritize this verb. Conversely, if it is
                // just a held object, the user is probably just trying to sit down.
                verb.Priority = args.Using.HasComponent <ActorComponent>() ? 1 : -1;

                args.Verbs.Add(verb);
            }
        }
        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);
        }