Hotbar Improvements + Item Action Integration Test (#2749)
* my IDE keeps wanting to change this so.... * Add item actions integration test, fix bug where empty item action dict was left in SharedActionsComponent state * bigger hotbar arrows * nice wide hotbar pagination hitboxes * add ability to switch hotbar loadout via keybinds * always highlight on drag over of actions hotbar * dont rely on content entity for integration test
This commit is contained in:
@@ -1,19 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Client.GameObjects.Components.Mobs;
|
||||
using Content.Client.UserInterface;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Server.GameObjects.Components.GUI;
|
||||
using Content.Server.GameObjects.Components.Items.Storage;
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.GameObjects.Components.Mobs;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using NUnit.Framework;
|
||||
using Robust.Client.Interfaces.UserInterface;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Content.Shared.Utility;
|
||||
using Robust.Shared.Interfaces.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.GameObjects.Components.Mobs
|
||||
{
|
||||
@@ -21,8 +24,39 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.Mobs
|
||||
[TestOf(typeof(SharedActionsComponent))]
|
||||
[TestOf(typeof(ClientActionsComponent))]
|
||||
[TestOf(typeof(ServerActionsComponent))]
|
||||
[TestOf(typeof(ItemActionsComponent))]
|
||||
public class ActionsComponentTests : ContentIntegrationTest
|
||||
{
|
||||
const string PROTOTYPES = @"
|
||||
- type: entity
|
||||
name: flashlight
|
||||
parent: BaseItem
|
||||
id: TestFlashlight
|
||||
components:
|
||||
- type: HandheldLight
|
||||
- type: ItemActions
|
||||
actions:
|
||||
- actionType: ToggleLight
|
||||
- type: PowerCellSlot
|
||||
- type: Sprite
|
||||
sprite: Objects/Tools/flashlight.rsi
|
||||
layers:
|
||||
- state: lantern_off
|
||||
- state: HandheldLightOnOverlay
|
||||
shader: unshaded
|
||||
visible: false
|
||||
- type: Item
|
||||
sprite: Objects/Tools/flashlight.rsi
|
||||
HeldPrefix: off
|
||||
- type: PointLight
|
||||
enabled: false
|
||||
radius: 3
|
||||
- type: LoopingSound
|
||||
- type: Appearance
|
||||
visuals:
|
||||
- type: FlashLightVisualizer
|
||||
";
|
||||
|
||||
[Test]
|
||||
public async Task GrantsAndRevokesActionsTest()
|
||||
{
|
||||
@@ -47,6 +81,7 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.Mobs
|
||||
Assert.That(actionsComponent.TryGetActionState(innateAction, out var innateState));
|
||||
Assert.That(innateState.Enabled);
|
||||
}
|
||||
Assert.That(innateActions.Count, Is.GreaterThan(0));
|
||||
|
||||
actionsComponent.Grant(ActionType.DebugInstant);
|
||||
Assert.That(actionsComponent.TryGetActionState(ActionType.HumanScream, out var state) && state.Enabled);
|
||||
@@ -182,5 +217,161 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.Mobs
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GrantsAndRevokesItemActions()
|
||||
{
|
||||
var serverOptions = new ServerIntegrationOptions { ExtraPrototypes = PROTOTYPES };
|
||||
var clientOptions = new ClientIntegrationOptions { ExtraPrototypes = PROTOTYPES };
|
||||
var (client, server) = await StartConnectedServerClientPair(serverOptions: serverOptions, clientOptions: clientOptions);
|
||||
|
||||
await server.WaitIdleAsync();
|
||||
await client.WaitIdleAsync();
|
||||
|
||||
var serverPlayerManager = server.ResolveDependency<Robust.Server.Interfaces.Player.IPlayerManager>();
|
||||
var serverEntManager = server.ResolveDependency<IEntityManager>();
|
||||
var serverGameTiming = server.ResolveDependency<IGameTiming>();
|
||||
|
||||
var cooldown = Cooldowns.SecondsFromNow(30, serverGameTiming);
|
||||
|
||||
ServerActionsComponent serverActionsComponent = null;
|
||||
ClientActionsComponent clientActionsComponent = null;
|
||||
IEntity serverPlayerEnt = null;
|
||||
IEntity serverFlashlight = null;
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
serverPlayerEnt = serverPlayerManager.GetAllPlayers().Single().AttachedEntity;
|
||||
serverActionsComponent = serverPlayerEnt.GetComponent<ServerActionsComponent>();
|
||||
|
||||
// spawn and give them an item that has actions
|
||||
serverFlashlight = serverEntManager.SpawnEntity("TestFlashlight",
|
||||
new EntityCoordinates(new EntityUid(1), (0, 0)));
|
||||
Assert.That(serverFlashlight.TryGetComponent<ItemActionsComponent>(out var itemActions));
|
||||
// we expect this only to have a toggle light action initially
|
||||
var actionConfigs = itemActions.ActionConfigs.ToList();
|
||||
Assert.That(actionConfigs.Count == 1);
|
||||
Assert.That(actionConfigs[0].ActionType == ItemActionType.ToggleLight);
|
||||
Assert.That(actionConfigs[0].Enabled);
|
||||
|
||||
// grant an extra item action, before pickup, initially disabled
|
||||
itemActions.GrantOrUpdate(ItemActionType.DebugToggle, false);
|
||||
serverPlayerEnt.GetComponent<HandsComponent>().PutInHand(serverFlashlight.GetComponent<ItemComponent>(), false);
|
||||
// grant an extra item action, after pickup, with a cooldown
|
||||
itemActions.GrantOrUpdate(ItemActionType.DebugInstant, cooldown: cooldown);
|
||||
|
||||
Assert.That(serverActionsComponent.TryGetItemActionStates(serverFlashlight.Uid, out var state));
|
||||
// they should have been granted all 3 actions
|
||||
Assert.That(state.Count == 3);
|
||||
Assert.That(state.TryGetValue(ItemActionType.ToggleLight, out var toggleLightState));
|
||||
Assert.That(toggleLightState.Equals(new ActionState(true)));
|
||||
Assert.That(state.TryGetValue(ItemActionType.DebugInstant, out var debugInstantState));
|
||||
Assert.That(debugInstantState.Equals(new ActionState(true, cooldown: cooldown)));
|
||||
Assert.That(state.TryGetValue(ItemActionType.DebugToggle, out var debugToggleState));
|
||||
Assert.That(debugToggleState.Equals(new ActionState(false)));
|
||||
});
|
||||
|
||||
await server.WaitRunTicks(5);
|
||||
await client.WaitRunTicks(5);
|
||||
|
||||
// check that client has the actions, and toggle the light on via the action slot it was auto-assigned to
|
||||
var clientPlayerMgr = client.ResolveDependency<IPlayerManager>();
|
||||
var clientUIMgr = client.ResolveDependency<IUserInterfaceManager>();
|
||||
var clientEntMgr = client.ResolveDependency<IEntityManager>();
|
||||
EntityUid clientFlashlight = default;
|
||||
await client.WaitAssertion(() =>
|
||||
{
|
||||
var local = clientPlayerMgr.LocalPlayer;
|
||||
var controlled = local.ControlledEntity;
|
||||
clientActionsComponent = controlled.GetComponent<ClientActionsComponent>();
|
||||
|
||||
var lightEntry = clientActionsComponent.ItemActionStates()
|
||||
.Where(entry => entry.Value.ContainsKey(ItemActionType.ToggleLight))
|
||||
.FirstOrNull();
|
||||
clientFlashlight = lightEntry.Value.Key;
|
||||
Assert.That(lightEntry, Is.Not.Null);
|
||||
Assert.That(lightEntry.Value.Value.TryGetValue(ItemActionType.ToggleLight, out var lightState));
|
||||
Assert.That(lightState.Equals(new ActionState(true)));
|
||||
Assert.That(lightEntry.Value.Value.TryGetValue(ItemActionType.DebugInstant, out var debugInstantState));
|
||||
Assert.That(debugInstantState.Equals(new ActionState(true, cooldown: cooldown)));
|
||||
Assert.That(lightEntry.Value.Value.TryGetValue(ItemActionType.DebugToggle, out var debugToggleState));
|
||||
Assert.That(debugToggleState.Equals(new ActionState(false)));
|
||||
|
||||
var actionsUI = clientUIMgr.StateRoot.Children.FirstOrDefault(c => c is ActionsUI) as ActionsUI;
|
||||
Assert.That(actionsUI, Is.Not.Null);
|
||||
|
||||
var toggleLightSlot = actionsUI.Slots.FirstOrDefault(slot => slot.Action is ItemActionPrototype
|
||||
{
|
||||
ActionType: ItemActionType.ToggleLight
|
||||
});
|
||||
Assert.That(toggleLightSlot, Is.Not.Null);
|
||||
|
||||
clientActionsComponent.AttemptAction(toggleLightSlot);
|
||||
});
|
||||
|
||||
await server.WaitRunTicks(5);
|
||||
await client.WaitRunTicks(5);
|
||||
|
||||
// server should see the action toggled on
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
Assert.That(serverActionsComponent.ItemActionStates().TryGetValue(serverFlashlight.Uid, out var lightStates));
|
||||
Assert.That(lightStates.TryGetValue(ItemActionType.ToggleLight, out var lightState));
|
||||
Assert.That(lightState, Is.EqualTo(new ActionState(true, toggledOn: true)));
|
||||
});
|
||||
|
||||
// client should see it toggled on.
|
||||
await client.WaitAssertion(() =>
|
||||
{
|
||||
Assert.That(clientActionsComponent.ItemActionStates().TryGetValue(clientFlashlight, out var lightStates));
|
||||
Assert.That(lightStates.TryGetValue(ItemActionType.ToggleLight, out var lightState));
|
||||
Assert.That(lightState, Is.EqualTo(new ActionState(true, toggledOn: true)));
|
||||
});
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
// drop the item, and the item actions should go away
|
||||
serverPlayerEnt.GetComponent<HandsComponent>()
|
||||
.Drop(serverFlashlight, serverPlayerEnt.Transform.Coordinates, false);
|
||||
Assert.That(serverActionsComponent.ItemActionStates().ContainsKey(serverFlashlight.Uid), Is.False);
|
||||
});
|
||||
|
||||
await server.WaitRunTicks(5);
|
||||
await client.WaitRunTicks(5);
|
||||
|
||||
// client should see they have no item actions for that item either.
|
||||
await client.WaitAssertion(() =>
|
||||
{
|
||||
Assert.That(clientActionsComponent.ItemActionStates().ContainsKey(clientFlashlight), Is.False);
|
||||
});
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
// pick the item up again, the states should be back to what they were when dropped,
|
||||
// as the states "stick" with the item
|
||||
serverPlayerEnt.GetComponent<HandsComponent>().PutInHand(serverFlashlight.GetComponent<ItemComponent>(), false);
|
||||
Assert.That(serverActionsComponent.ItemActionStates().TryGetValue(serverFlashlight.Uid, out var lightStates));
|
||||
Assert.That(lightStates.TryGetValue(ItemActionType.ToggleLight, out var lightState));
|
||||
Assert.That(lightState.Equals(new ActionState(true, toggledOn: true)));
|
||||
Assert.That(lightStates.TryGetValue(ItemActionType.DebugInstant, out var debugInstantState));
|
||||
Assert.That(debugInstantState.Equals(new ActionState(true, cooldown: cooldown)));
|
||||
Assert.That(lightStates.TryGetValue(ItemActionType.DebugToggle, out var debugToggleState));
|
||||
Assert.That(debugToggleState.Equals(new ActionState(false)));
|
||||
});
|
||||
|
||||
await server.WaitRunTicks(5);
|
||||
await client.WaitRunTicks(5);
|
||||
|
||||
// client should see the actions again, with their states back to what they were
|
||||
await client.WaitAssertion(() =>
|
||||
{
|
||||
Assert.That(clientActionsComponent.ItemActionStates().TryGetValue(clientFlashlight, out var lightStates));
|
||||
Assert.That(lightStates.TryGetValue(ItemActionType.ToggleLight, out var lightState));
|
||||
Assert.That(lightState.Equals(new ActionState(true, toggledOn: true)));
|
||||
Assert.That(lightStates.TryGetValue(ItemActionType.DebugInstant, out var debugInstantState));
|
||||
Assert.That(debugInstantState.Equals(new ActionState(true, cooldown: cooldown)));
|
||||
Assert.That(lightStates.TryGetValue(ItemActionType.DebugToggle, out var debugToggleState));
|
||||
Assert.That(debugToggleState.Equals(new ActionState(false)));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user