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); });
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(); }