Files
tbd-station-14/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs
collinlunn f2816e8081 Moves Hands to shared, some prediction (#3829)
* HandsGuiState

* Gui state setting methods

* code cleanup

* Removes TryGetHands

* ClientHand

* Gui Hands

* Refactor WIP 1

* Hand index

* refactors 2

* wip 3

* wip 4

* wiip 4

* wip 5

* wip 6

* wip 7

* wip 8

* wip 9

* wip 11

* Hand ui mostly looks fine

* hands gui cleanup 1

* cleanup 2

* wip 13

* hand enabled

* stuff

* Hands gui gap fix

* onpressed test

* hand gui buttons events work

* bag activation works

* fix item use

* todo comment

* hands activate fix

* Moves Client Hands back to using strings to identify active hand

* fixes action hand highlighting

* diff fix

* serverhand

* SharedHand

* SharedHand, IReadOnlyHand

* Client Hands only stores SharedHand

* cleanup server hands

* server hand container shutdown

* misc renames, refactors of serverhand

* stuff 1

* stuff 3

* server hand refactor 1

* Undo API changes to remove massive diff

* More API name fixes

* server hands cleanup 2

* cleanup 3

* dropping cleanup

* Cleanup 4

* MoveItemFromHand

* Stuff

* region sorting

* Hand Putter methods cleanup

* stuff 2

* Merges all of serverhand and clienthand into sharedhand

* Other hands systems, hack to make inhands update (gui state set every frame, visualzier updated every frame)

* GetFinalDropCoordinates cleanup

* SwapHands cleanup

* Moves server hands code to shared hands

* Fixed hand selected and deselected

* Naming fixes

* Server hands system cleanup

* Hands privacy fixes

* Client hand updates when containers are modified

* HeldItemVisualizer

* Fixes hand gui item status panel

* method name fix

* Swap hands prediction

* Dropping prediction

* Fixes pickup entity animation

* Fixes HeldItemsVisualizer

* moves item pickup to shared

* PR cleanup

* fixes hand enabling/disabling

* build fix

* Conflict fixes

* Fixes pickup animation

* Uses component directed message subscriptions

* event unsubscriptions in hand system

* unsubscribe fix

* CanInsertEntityIntoHand checks if hand is enabled

* Moving items from one hand to another checks if the hands can pick up and drop

* Fixes stop pulling not re-enabling hand

* Fixes pickup animation for entities containers on the floor

* Fixes using held items

* Fixes multiple hands guis appearing

* test fix

* removes obsolete system sunsubscribes

* Checks IsFirstTimePredicted before playing drop animation

* fixes hand item deleted crash

* Uses Get to get other system

* Replaces AppearanceComponent with SharedAppearanceComponent

* Replaces EnsureComponent with TryGetComponent

* Improves event class names

* Moves property up to top of class

* Moves code for determining the hand visualizer rsi state into the visualizer instead of being determined on hand component

* Eventbus todo comment

* Yaml fix for changed visualizer name

* Makes HandsVisuals a byte

* Removes state from HandsVisualizer

* Fixes hand using interaction method name

* Namespace changes fixes

* Fix for changed hand interaction method

* missing }

* conflict build fix

* Moves cleint HandsSystem to correct folder

* Moved namespace fix for interaction test

* Moves Handsvisualizer ot correct folder

* Moves SharedHandsSystem to correct folder

* Fixes errors from moving namespace of hand systems

* Fixes PDA component changes

* Fixes ActionsComponent diff

* Fixes inventory component diff

* fixes null ref

* Replaces obsolete Loc.GetString usage with fluent translations

* Fluent for hands disarming

* SwapHands and Drop user input specify to the server which hand

* Pickup animation WorldPosiiton todo

* Cleans up hands gui subscription handling

* Fixes change in ActionBlockerSystem access

* Namespace references fixes

* HandsComponent PlayerAttached/Detached messages are handled through eventbus

* Fixes GasCanisterSystem drop method usage

* Fix gameticker equipping method at new location

Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
2021-06-21 19:21:20 +10:00

364 lines
13 KiB
C#

using System.Linq;
using System.Threading.Tasks;
using Content.Server.Buckle.Components;
using Content.Server.Hands.Components;
using Content.Server.Items;
using Content.Shared.ActionBlocker;
using Content.Shared.Body.Components;
using Content.Shared.Body.Part;
using Content.Shared.Buckle.Components;
using Content.Shared.Coordinates;
using Content.Shared.EffectBlocker;
using Content.Shared.Interaction.Events;
using Content.Shared.Movement;
using NUnit.Framework;
using Robust.Server.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
namespace Content.IntegrationTests.Tests.Buckle
{
[TestFixture]
[TestOf(typeof(BuckleComponent))]
[TestOf(typeof(StrapComponent))]
public class BuckleTest : ContentIntegrationTest
{
private const string BuckleDummyId = "BuckleDummy";
private const string StrapDummyId = "StrapDummy";
private const string ItemDummyId = "ItemDummy";
private static readonly string Prototypes = $@"
- type: entity
name: {BuckleDummyId}
id: {BuckleDummyId}
components:
- type: Buckle
- type: Hands
- type: Body
template: HumanoidTemplate
preset: HumanPreset
centerSlot: torso
- type: entity
name: {StrapDummyId}
id: {StrapDummyId}
components:
- type: Strap
- type: entity
name: {ItemDummyId}
id: {ItemDummyId}
components:
- type: Item
";
[Test]
public async Task BuckleUnbuckleCooldownRangeTest()
{
var cOptions = new ClientIntegrationOptions {ExtraPrototypes = Prototypes};
var sOptions = new ServerIntegrationOptions {ExtraPrototypes = Prototypes};
var (_, 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 actionBlocker = EntitySystem.Get<ActionBlockerSystem>();
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(actionBlocker.CanMove(human));
Assert.True(actionBlocker.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(actionBlocker.CanMove(human));
Assert.False(actionBlocker.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(() =>
{
var actionBlocker = EntitySystem.Get<ActionBlockerSystem>();
// 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(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(() =>
{
var actionBlocker = EntitySystem.Get<ActionBlockerSystem>();
// 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(actionBlocker.CanMove(human));
Assert.True(actionBlocker.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);
});
}
[Test]
public async Task BuckledDyingDropItemsTest()
{
var options = new ServerContentIntegrationOption {ExtraPrototypes = Prototypes};
var server = StartServer(options);
IEntity human = null;
BuckleComponent buckle = null;
HandsComponent hands = null;
SharedBodyComponent body = null;
await server.WaitIdleAsync();
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);
IEntity chair = entityManager.SpawnEntity(StrapDummyId, coordinates);
// Component sanity check
Assert.True(human.TryGetComponent(out buckle));
Assert.True(chair.HasComponent<StrapComponent>());
Assert.True(human.TryGetComponent(out hands));
Assert.True(human.TryGetComponent(out body));
// 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(ItemDummyId, coordinates);
// Equip items
Assert.True(akms.TryGetComponent(out ItemComponent item));
Assert.True(hands.PutInHand(item));
}
});
await server.WaitRunTicks(10);
await server.WaitAssertion(() =>
{
// Still buckled
Assert.True(buckle.Buckled);
// With items in all hands
foreach (var slot in hands.HandNames)
{
Assert.NotNull(hands.GetItem(slot));
}
var legs = body.GetPartsOfType(BodyPartType.Leg);
// Break our guy's kneecaps
foreach (var leg in legs)
{
body.RemovePart(leg);
}
});
await server.WaitRunTicks(10);
await server.WaitAssertion(() =>
{
// Still buckled
Assert.True(buckle.Buckled);
// Now with no item in any hand
foreach (var slot in hands.HandNames)
{
Assert.Null(hands.GetItem(slot));
}
buckle.TryUnbuckle(human, true);
});
}
[Test]
public async Task ForceUnbuckleBuckleTest()
{
var options = new ServerContentIntegrationOption
{
ExtraPrototypes = Prototypes
};
var server = StartServer(options);
IEntity human = null;
IEntity chair = null;
BuckleComponent buckle = 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);
// Component sanity check
Assert.True(human.TryGetComponent(out buckle));
Assert.True(chair.HasComponent<StrapComponent>());
// Buckle
Assert.True(buckle.TryBuckle(human, chair));
Assert.NotNull(buckle.BuckledTo);
Assert.True(buckle.Buckled);
// Move the buckled entity away
human.Transform.LocalPosition += (100, 0);
});
await WaitUntil(server, () => !buckle.Buckled, 10);
Assert.False(buckle.Buckled);
await server.WaitAssertion(() =>
{
// Move the now unbuckled entity back onto the chair
human.Transform.LocalPosition -= (100, 0);
// Buckle
Assert.True(buckle.TryBuckle(human, chair));
Assert.NotNull(buckle.BuckledTo);
Assert.True(buckle.Buckled);
});
await server.WaitRunTicks(60);
await server.WaitAssertion(() =>
{
// Still buckled
Assert.NotNull(buckle.BuckledTo);
Assert.True(buckle.Buckled);
});
}
}
}