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);
            });
Пример #2
0
        private void OnItemButtonUp(BaseButton.ButtonEventArgs args)
        {
            // note the buttonup only fires on the control that was originally
            // pressed to initiate the drag, NOT the one we are currently hovering
            if (args.Event.Function != EngineKeyFunctions.UIClick)
            {
                return;
            }

            if (UserInterfaceManager.CurrentlyHovered is ActionSlot targetSlot)
            {
                if (!_dragDropHelper.IsDragging || _dragDropHelper.Dragged?.Action == null)
                {
                    _dragDropHelper.EndDrag();
                    return;
                }

                // drag and drop
                switch (_dragDropHelper.Dragged.Action)
                {
                // assign the dragged action to the target slot
                case ActionPrototype actionPrototype:
                    _actionsComponent.Assignments.AssignSlot(_actionsUI.SelectedHotbar, targetSlot.SlotIndex, ActionAssignment.For(actionPrototype.ActionType));
                    break;

                case ItemActionPrototype itemActionPrototype:
                    // the action menu doesn't show us if the action has an associated item,
                    // so when we perform the assignment, we should check if we currently have an unassigned state
                    // for this item and assign it tied to that item if so, otherwise assign it "itemless"

                    // this is not particularly efficient but we don't maintain an index from
                    // item action type to its action states, and this method should be pretty infrequent so it's probably fine
                    var assigned = false;
                    foreach (var(item, itemStates) in _actionsComponent.ItemActionStates())
                    {
                        foreach (var(actionType, _) in itemStates)
                        {
                            if (actionType != itemActionPrototype.ActionType)
                            {
                                continue;
                            }
                            var assignment = ActionAssignment.For(actionType, item);
                            if (_actionsComponent.Assignments.HasAssignment(assignment))
                            {
                                continue;
                            }
                            // no assignment for this state, assign tied to the item
                            assigned = true;
                            _actionsComponent.Assignments.AssignSlot(_actionsUI.SelectedHotbar, targetSlot.SlotIndex, assignment);
                            break;
                        }

                        if (assigned)
                        {
                            break;
                        }
                    }

                    if (!assigned)
                    {
                        _actionsComponent.Assignments.AssignSlot(_actionsUI.SelectedHotbar, targetSlot.SlotIndex, ActionAssignment.For(itemActionPrototype.ActionType));
                    }
                    break;
                }

                _actionsUI.UpdateUI();
            }

            _dragDropHelper.EndDrag();
        }