private bool CheckRangeAndSetFacing(EntityCoordinates target, IEntity player) { // ensure it's within their clickable range var targetWorldPos = target.ToMapPos(EntityManager); var rangeBox = new Box2(player.Transform.WorldPosition, player.Transform.WorldPosition) .Enlarged(_entityManager.MaxUpdateRange); if (!rangeBox.Contains(targetWorldPos)) { Logger.DebugS("action", "user {0} attempted to" + " perform target action further than allowed range", player.Name); return(false); } if (!ActionBlockerSystem.CanChangeDirection(player)) { return(true); } // don't set facing unless they clicked far enough away var diff = targetWorldPos - player.Transform.WorldPosition; if (diff.LengthSquared > 0.01f) { player.Transform.LocalRotation = new Angle(diff); } return(true); }
private void FaceClickCoordinates(IEntity user, EntityCoordinates coordinates) { var diff = coordinates.ToMapPos(EntityManager) - user.Transform.MapPosition.Position; if (diff.LengthSquared <= 0.01f) { return; } var diffAngle = Angle.FromWorldVec(diff); if (_actionBlockerSystem.CanChangeDirection(user)) { user.Transform.WorldRotation = diffAngle; } else { if (user.TryGetComponent(out BuckleComponent? buckle) && (buckle.BuckledTo != null)) { // We're buckled to another object. Is that object rotatable? if (buckle.BuckledTo !.Owner.TryGetComponent(out RotatableComponent? rotatable) && rotatable.RotateWhileAnchored) { // Note the assumption that even if unanchored, user can only do spinnychair with an "independent wheel". // (Since the user being buckled to it holds it down with their weight.) // This is logically equivalent to RotateWhileAnchored. // Barstools and office chairs have independent wheels, while regular chairs don't. rotatable.Owner.Transform.LocalRotation = diffAngle; } } } }
public bool TryFaceAngle(EntityUid user, Angle diffAngle) { if (_actionBlockerSystem.CanChangeDirection(user)) { EntityManager.GetComponent <TransformComponent>(user).WorldRotation = diffAngle; return(true); } else { if (EntityManager.TryGetComponent(user, out SharedBuckleComponent? buckle) && buckle.Buckled) { var suid = buckle.LastEntityBuckledTo; if (suid != null) { // We're buckled to another object. Is that object rotatable? if (EntityManager.TryGetComponent <RotatableComponent>(suid.Value !, out var rotatable) && rotatable.RotateWhileAnchored) { // Note the assumption that even if unanchored, user can only do spinnychair with an "independent wheel". // (Since the user being buckled to it holds it down with their weight.) // This is logically equivalent to RotateWhileAnchored. // Barstools and office chairs have independent wheels, while regular chairs don't. EntityManager.GetComponent <TransformComponent>(rotatable.Owner).WorldRotation = diffAngle; return(true); } } } } return(false); }
/// <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); } } }
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 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); }); }