Add climb & slip tests (#15459)
This commit is contained in:
@@ -5,8 +5,6 @@ namespace Content.IntegrationTests.Tests.Interaction;
|
||||
// Should make it easier to mass-change hard coded strings if prototypes get renamed.
|
||||
public abstract partial class InteractionTest
|
||||
{
|
||||
protected const string PlayerEntity = "AdminObserver";
|
||||
|
||||
// Tiles
|
||||
protected const string Floor = "FloorSteel";
|
||||
protected const string FloorItem = "FloorTileItemSteel";
|
||||
|
||||
@@ -8,10 +8,15 @@ using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Client.Chemistry.UI;
|
||||
using Content.Client.Construction;
|
||||
using Content.Server.Atmos;
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Construction.Components;
|
||||
using Content.Server.Gravity;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Server.Tools.Components;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Construction.Prototypes;
|
||||
using Content.Shared.Gravity;
|
||||
using Content.Shared.Item;
|
||||
using NUnit.Framework;
|
||||
using OpenToolkit.GraphicsLibraryFramework;
|
||||
@@ -84,8 +89,10 @@ public abstract partial class InteractionTest
|
||||
/// <summary>
|
||||
/// Spawn an entity entity and set it as the target.
|
||||
/// </summary>
|
||||
[MemberNotNull(nameof(Target))]
|
||||
protected async Task SpawnTarget(string prototype)
|
||||
{
|
||||
Target = EntityUid.Invalid;
|
||||
await Server.WaitPost(() =>
|
||||
{
|
||||
Target = SEntMan.SpawnEntity(prototype, TargetCoords);
|
||||
@@ -493,6 +500,19 @@ public abstract partial class InteractionTest
|
||||
Assert.That(tile.TypeId, Is.EqualTo(targetTile.TypeId));
|
||||
}
|
||||
|
||||
protected void AssertGridCount(int value)
|
||||
{
|
||||
var count = 0;
|
||||
var query = SEntMan.AllEntityQueryEnumerator<MapGridComponent, TransformComponent>();
|
||||
while (query.MoveNext(out _, out var xform))
|
||||
{
|
||||
if (xform.MapUid == MapData.MapUid)
|
||||
count++;
|
||||
}
|
||||
|
||||
Assert.That(count, Is.EqualTo(value));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Entity lookups
|
||||
@@ -669,13 +689,20 @@ public abstract partial class InteractionTest
|
||||
await RunTicks(5);
|
||||
}
|
||||
|
||||
#region Time/Tick managment
|
||||
|
||||
protected async Task RunTicks(int ticks)
|
||||
{
|
||||
await PoolManager.RunTicksSync(PairTracker.Pair, ticks);
|
||||
}
|
||||
|
||||
protected int SecondsToTicks(float seconds)
|
||||
=> (int) Math.Ceiling(seconds / TickPeriod);
|
||||
|
||||
protected async Task RunSeconds(float seconds)
|
||||
=> await RunTicks((int) Math.Ceiling(seconds / TickPeriod));
|
||||
=> await RunTicks(SecondsToTicks(seconds));
|
||||
|
||||
#endregion
|
||||
|
||||
#region BUI
|
||||
/// <summary>
|
||||
@@ -723,9 +750,6 @@ public abstract partial class InteractionTest
|
||||
return false;
|
||||
}
|
||||
|
||||
var first = ui.Interfaces.First();
|
||||
|
||||
|
||||
bui = ui.Interfaces.FirstOrDefault(x => x.UiKey.Equals(key));
|
||||
if (bui == null)
|
||||
{
|
||||
@@ -878,4 +902,110 @@ public abstract partial class InteractionTest
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Map Setup
|
||||
|
||||
/// <summary>
|
||||
/// Adds gravity to a given entity. Defaults to the grid if no entity is specified.
|
||||
/// </summary>
|
||||
protected async Task AddGravity(EntityUid? uid = null)
|
||||
{
|
||||
var target = uid ?? MapData.GridUid;
|
||||
await Server.WaitPost(() =>
|
||||
{
|
||||
var gravity = SEntMan.EnsureComponent<GravityComponent>(target);
|
||||
SEntMan.System<GravitySystem>().EnableGravity(target, gravity);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a default atmosphere to the test map.
|
||||
/// </summary>
|
||||
protected async Task AddAtmosphere(EntityUid? uid = null)
|
||||
{
|
||||
var target = uid ?? MapData.MapUid;
|
||||
await Server.WaitPost(() =>
|
||||
{
|
||||
var atmos = SEntMan.EnsureComponent<MapAtmosphereComponent>(target);
|
||||
atmos.Space = false;
|
||||
var moles = new float[Atmospherics.AdjustedNumberOfGases];
|
||||
moles[(int) Gas.Oxygen] = 21.824779f;
|
||||
moles[(int) Gas.Nitrogen] = 82.10312f;
|
||||
|
||||
atmos.Mixture = new GasMixture(2500)
|
||||
{
|
||||
Temperature = 293.15f,
|
||||
Moles = moles,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Inputs
|
||||
|
||||
/// <summary>
|
||||
/// Make the client press and then release a key. This assumes the key is currently released.
|
||||
/// </summary>
|
||||
protected async Task PressKey(
|
||||
BoundKeyFunction key,
|
||||
int ticks = 1,
|
||||
EntityCoordinates? coordinates = null,
|
||||
EntityUid cursorEntity = default)
|
||||
{
|
||||
await SetKey(key, BoundKeyState.Down, coordinates, cursorEntity);
|
||||
await RunTicks(ticks);
|
||||
await SetKey(key, BoundKeyState.Up, coordinates, cursorEntity);
|
||||
await RunTicks(1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make the client press or release a key
|
||||
/// </summary>
|
||||
protected async Task SetKey(
|
||||
BoundKeyFunction key,
|
||||
BoundKeyState state,
|
||||
EntityCoordinates? coordinates = null,
|
||||
EntityUid cursorEntity = default)
|
||||
{
|
||||
var coords = coordinates ?? TargetCoords;
|
||||
ScreenCoordinates screen = default;
|
||||
|
||||
var funcId = InputManager.NetworkBindMap.KeyFunctionID(key);
|
||||
var message = new FullInputCmdMessage(CTiming.CurTick, CTiming.TickFraction, funcId, state,
|
||||
coords, screen, cursorEntity);
|
||||
|
||||
await Client.WaitPost(() => InputSystem.HandleInputCommand(ClientSession, key, message));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Variant of <see cref="SetKey"/> for setting movement keys.
|
||||
/// </summary>
|
||||
protected async Task SetMovementKey(DirectionFlag dir, BoundKeyState state)
|
||||
{
|
||||
if ((dir & DirectionFlag.South) != 0)
|
||||
await SetKey(EngineKeyFunctions.MoveDown, state);
|
||||
|
||||
if ((dir & DirectionFlag.East) != 0)
|
||||
await SetKey(EngineKeyFunctions.MoveRight, state);
|
||||
|
||||
if ((dir & DirectionFlag.North) != 0)
|
||||
await SetKey(EngineKeyFunctions.MoveUp, state);
|
||||
|
||||
if ((dir & DirectionFlag.West) != 0)
|
||||
await SetKey(EngineKeyFunctions.MoveLeft, state);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make the client hold the move key in some direction for some amount of time.
|
||||
/// </summary>
|
||||
protected async Task Move(DirectionFlag dir, float seconds)
|
||||
{
|
||||
await SetMovementKey(dir, BoundKeyState.Down);
|
||||
await RunSeconds(seconds);
|
||||
await SetMovementKey(dir, BoundKeyState.Up);
|
||||
await RunTicks(1);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -14,9 +14,12 @@ using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Interaction;
|
||||
using NUnit.Framework;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.UnitTesting;
|
||||
@@ -34,6 +37,8 @@ namespace Content.IntegrationTests.Tests.Interaction;
|
||||
[FixtureLifeCycle(LifeCycle.InstancePerTestCase)]
|
||||
public abstract partial class InteractionTest
|
||||
{
|
||||
protected virtual string PlayerPrototype => "AdminObserver";
|
||||
|
||||
protected PairTracker PairTracker = default!;
|
||||
protected TestMapData MapData = default!;
|
||||
|
||||
@@ -59,6 +64,9 @@ public abstract partial class InteractionTest
|
||||
/// </summary>
|
||||
protected EntityUid Player;
|
||||
|
||||
protected ICommonSession ClientSession = default!;
|
||||
protected IPlayerSession ServerSession = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The current target entity. This is the default entity for various helper functions.
|
||||
/// </summary>
|
||||
@@ -79,7 +87,7 @@ public abstract partial class InteractionTest
|
||||
protected ITileDefinitionManager TileMan = default!;
|
||||
protected IMapManager MapMan = default!;
|
||||
protected IPrototypeManager ProtoMan = default!;
|
||||
protected IGameTiming Timing = default!;
|
||||
protected IGameTiming STiming = default!;
|
||||
protected IComponentFactory Factory = default!;
|
||||
protected SharedHandsSystem HandSys = default!;
|
||||
protected StackSystem Stack = default!;
|
||||
@@ -92,16 +100,20 @@ public abstract partial class InteractionTest
|
||||
|
||||
// CLIENT dependencies
|
||||
protected IEntityManager CEntMan = default!;
|
||||
protected IGameTiming CTiming = default!;
|
||||
protected IUserInterfaceManager UiMan = default!;
|
||||
protected IInputManager InputManager = default!;
|
||||
protected InputSystem InputSystem = default!;
|
||||
protected ConstructionSystem CConSys = default!;
|
||||
protected ExamineSystem ExamineSys = default!;
|
||||
protected InteractionTestSystem CTestSystem = default!;
|
||||
protected UserInterfaceSystem CUISystem = default!;
|
||||
|
||||
// player components
|
||||
protected HandsComponent Hands = default!;
|
||||
protected DoAfterComponent DoAfters = default!;
|
||||
|
||||
public float TickPeriod => (float)Timing.TickPeriod.TotalSeconds;
|
||||
public float TickPeriod => (float)STiming.TickPeriod.TotalSeconds;
|
||||
|
||||
[SetUp]
|
||||
public virtual async Task Setup()
|
||||
@@ -114,7 +126,7 @@ public abstract partial class InteractionTest
|
||||
MapMan = Server.ResolveDependency<IMapManager>();
|
||||
ProtoMan = Server.ResolveDependency<IPrototypeManager>();
|
||||
Factory = Server.ResolveDependency<IComponentFactory>();
|
||||
Timing = Server.ResolveDependency<IGameTiming>();
|
||||
STiming = Server.ResolveDependency<IGameTiming>();
|
||||
HandSys = SEntMan.System<SharedHandsSystem>();
|
||||
InteractSys = SEntMan.System<SharedInteractionSystem>();
|
||||
ToolSys = SEntMan.System<ToolSystem>();
|
||||
@@ -127,6 +139,9 @@ public abstract partial class InteractionTest
|
||||
// client dependencies
|
||||
CEntMan = Client.ResolveDependency<IEntityManager>();
|
||||
UiMan = Client.ResolveDependency<IUserInterfaceManager>();
|
||||
CTiming = Client.ResolveDependency<IGameTiming>();
|
||||
InputManager = Client.ResolveDependency<IInputManager>();
|
||||
InputSystem = CEntMan.System<InputSystem>();
|
||||
CTestSystem = CEntMan.System<InteractionTestSystem>();
|
||||
CConSys = CEntMan.System<ConstructionSystem>();
|
||||
ExamineSys = CEntMan.System<ExamineSystem>();
|
||||
@@ -142,16 +157,16 @@ public abstract partial class InteractionTest
|
||||
var cPlayerMan = Client.ResolveDependency<Robust.Client.Player.IPlayerManager>();
|
||||
if (cPlayerMan.LocalPlayer?.Session == null)
|
||||
Assert.Fail("No player");
|
||||
var cSession = cPlayerMan.LocalPlayer!.Session!;
|
||||
var sSession = sPlayerMan.GetSessionByUserId(cSession.UserId);
|
||||
ClientSession = cPlayerMan.LocalPlayer!.Session!;
|
||||
ServerSession = sPlayerMan.GetSessionByUserId(ClientSession.UserId);
|
||||
|
||||
// Spawn player entity & attach
|
||||
EntityUid? old = default;
|
||||
await Server.WaitPost(() =>
|
||||
{
|
||||
old = cPlayerMan.LocalPlayer.ControlledEntity;
|
||||
Player = SEntMan.SpawnEntity(PlayerEntity, PlayerCoords);
|
||||
sSession.AttachToEntity(Player);
|
||||
Player = SEntMan.SpawnEntity(PlayerPrototype, PlayerCoords);
|
||||
ServerSession.AttachToEntity(Player);
|
||||
Hands = SEntMan.GetComponent<HandsComponent>(Player);
|
||||
DoAfters = SEntMan.GetComponent<DoAfterComponent>(Player);
|
||||
});
|
||||
@@ -189,7 +204,7 @@ public abstract partial class InteractionTest
|
||||
// Final player asserts/checks.
|
||||
await PoolManager.ReallyBeIdle(PairTracker.Pair, 5);
|
||||
Assert.That(cPlayerMan.LocalPlayer.ControlledEntity, Is.EqualTo(Player));
|
||||
Assert.That(sPlayerMan.GetSessionByUserId(cSession.UserId).AttachedEntity, Is.EqualTo(Player));
|
||||
Assert.That(sPlayerMan.GetSessionByUserId(ClientSession.UserId).AttachedEntity, Is.EqualTo(Player));
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
|
||||
65
Content.IntegrationTests/Tests/Interaction/MovementTest.cs
Normal file
65
Content.IntegrationTests/Tests/Interaction/MovementTest.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.Interaction;
|
||||
|
||||
/// <summary>
|
||||
/// This is a variation of <see cref="InteractionTest"/> that sets up the player with a normal human entity and a simple
|
||||
/// linear grid with gravity and an atmosphere. It is intended to make it easier to test interactions that involve
|
||||
/// walking (e.g., slipping or climbing tables).
|
||||
/// </summary>
|
||||
public abstract class MovementTest : InteractionTest
|
||||
{
|
||||
protected override string PlayerPrototype => "MobHuman";
|
||||
|
||||
/// <summary>
|
||||
/// Number of tiles to add either side of the player.
|
||||
/// </summary>
|
||||
protected virtual int Tiles => 3;
|
||||
|
||||
/// <summary>
|
||||
/// If true, the tiles at the ends of the grid will have a wall placed on them to avoid players moving off grid.
|
||||
/// </summary>
|
||||
protected virtual bool AddWalls => true;
|
||||
|
||||
[SetUp]
|
||||
public override async Task Setup()
|
||||
{
|
||||
await base.Setup();
|
||||
for (var i = -Tiles; i <= Tiles; i++)
|
||||
{
|
||||
await SetTile(Plating, PlayerCoords.Offset((i,0)), MapData.MapGrid);
|
||||
}
|
||||
AssertGridCount(1);
|
||||
|
||||
if (AddWalls)
|
||||
{
|
||||
await SpawnEntity("WallSolid", PlayerCoords.Offset((-Tiles,0)));
|
||||
await SpawnEntity("WallSolid", PlayerCoords.Offset((Tiles,0)));
|
||||
}
|
||||
|
||||
await AddGravity();
|
||||
await AddAtmosphere();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the relative horizontal between two entities. Defaults to using the target & player entity.
|
||||
/// </summary>
|
||||
protected float Delta(EntityUid? target = null, EntityUid? other = null)
|
||||
{
|
||||
target ??= Target;
|
||||
if (target == null)
|
||||
{
|
||||
Assert.Fail("No target specified");
|
||||
return 0;
|
||||
}
|
||||
|
||||
var delta = Transform.GetWorldPosition(target.Value) - Transform.GetWorldPosition(other ?? Player);
|
||||
return delta.X;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user