/// <summary> /// Ensure item slots have containers. /// </summary> private void Oninitialize(EntityUid uid, ItemSlotsComponent itemSlots, ComponentInit args) { foreach (var(id, slot) in itemSlots.Slots) { slot.ContainerSlot = ContainerHelpers.EnsureContainer <ContainerSlot>(itemSlots.Owner, id); } }
/// <summary> /// Tries to insert a held item in any fitting item slot. If a valid slot already contains an item, it will /// swap it out and place the old one in the user's hand. /// </summary> /// <remarks> /// This only handles the event if the user has an applicable entity that can be inserted. This allows for /// other interactions to still happen (e.g., open UI, or toggle-open), despite the user holding an item. /// Maybe this is undesirable. /// </remarks> private void OnInteractUsing(EntityUid uid, ItemSlotsComponent itemSlots, InteractUsingEvent args) { if (args.Handled) { return; } if (!EntityManager.TryGetComponent(args.User, out SharedHandsComponent hands)) { return; } foreach (var slot in itemSlots.Slots.Values) { if (!CanInsert(uid, args.Used, slot, swap: slot.Swap, popup: args.User)) { continue; } // Drop the held item onto the floor. Return if the user cannot drop. if (!_handsSystem.TryDrop(args.User, args.Used, handsComp: hands)) { return; } if (slot.Item != null) { _handsSystem.TryPickupAnyHand(args.User, slot.Item.Value, handsComp: hands); } Insert(uid, slot, args.Used, args.User, excludeUserAudio: args.Predicted); args.Handled = true; return; } }
/// <summary> /// Tries to insert a held item in any fitting item slot. If a valid slot already contains an item, it will /// swap it out and place the old one in the user's hand. /// </summary> /// <remarks> /// This only handles the event if the user has an applicable entity that can be inserted. This allows for /// other interactions to still happen (e.g., open UI, or toggle-open), despite the user holding an item. /// Maybe this is undesirable. /// </remarks> private void OnInteractUsing(EntityUid uid, ItemSlotsComponent itemSlots, InteractUsingEvent args) { if (args.Handled) { return; } if (!EntityManager.TryGetComponent(args.User, out SharedHandsComponent hands)) { return; } foreach (var slot in itemSlots.Slots.Values) { if (!CanInsert(uid, args.Used, slot, swap: slot.Swap, popup: args.User)) { continue; } // Drop the held item onto the floor. Return if the user cannot drop. if (!hands.Drop(args.Used)) { return; } if (slot.Item != null) { hands.TryPutInAnyHand(slot.Item.Value); } Insert(uid, slot, args.Used, args.User); args.Handled = true; return; } }
/// <summary> /// Update the locked state of the managed item slots. /// </summary> /// <remarks> /// Note that the slot's ContainerSlot performs its own networking, so we don't need to send information /// about the contained entity. /// </remarks> private void HandleItemSlotsState(EntityUid uid, ItemSlotsComponent component, ref ComponentHandleState args) { if (args.Current is not ItemSlotsComponentState state) { return; } foreach (var(key, slot) in component.Slots) { if (!state.Slots.ContainsKey(key)) { RemoveItemSlot(uid, slot, component); } } foreach (var(serverKey, serverSlot) in state.Slots) { if (component.Slots.TryGetValue(serverKey, out var itemSlot)) { itemSlot.CopyFrom(serverSlot); itemSlot.ContainerSlot = _containers.EnsureContainer <ContainerSlot>(uid, serverKey); } else { var slot = new ItemSlot(serverSlot); slot.Local = false; AddItemSlot(uid, serverKey, slot); } } }
/// <summary> /// Eject items from (some) slots when the entity is destroyed. /// </summary> private void OnBreak(EntityUid uid, ItemSlotsComponent component, EntityEventArgs args) { foreach (var slot in component.Slots.Values) { if (slot.EjectOnBreak && slot.HasItem) { TryEject(uid, slot, null, out var _); } } }
/// <summary> /// Update the locked state of the managed item slots. /// </summary> /// <remarks> /// Note that the slot's ContainerSlot performs its own networking, so we don't need to send information /// about the contained entity. /// </remarks> private void HandleItemSlotsState(EntityUid uid, ItemSlotsComponent component, ref ComponentHandleState args) { if (args.Current is not ItemSlotsComponentState state) { return; } foreach (var(id, locked) in state.SlotLocked) { component.Slots[id].Locked = locked; } }
/// <summary> /// Spawn in starting items for any item slots that should have one. /// </summary> private void OnMapInit(EntityUid uid, ItemSlotsComponent itemSlots, MapInitEvent args) { foreach (var slot in itemSlots.Slots.Values) { if (slot.HasItem || string.IsNullOrEmpty(slot.StartingItem)) { continue; } var item = EntityManager.SpawnEntity(slot.StartingItem, EntityManager.GetComponent <TransformComponent>(itemSlots.Owner).Coordinates); slot.ContainerSlot?.Insert(item); } }
private void AddEjectVerbs(EntityUid uid, ItemSlotsComponent itemSlots, GetVerbsEvent <AlternativeVerb> args) { if (args.Hands == null || !args.CanAccess || !args.CanInteract) { return; } foreach (var slot in itemSlots.Slots.Values) { if (slot.EjectOnInteract) { // For this item slot, ejecting/inserting is a primary interaction. Instead of an eject category // alt-click verb, there will be a "Take item" primary interaction verb. continue; } if (!CanEject(slot)) { continue; } if (!_actionBlockerSystem.CanPickup(args.User, slot.Item !.Value)) { continue; } var verbSubject = slot.Name != string.Empty ? Loc.GetString(slot.Name) : EntityManager.GetComponent <MetaDataComponent>(slot.Item.Value).EntityName ?? string.Empty; AlternativeVerb verb = new(); verb.IconEntity = slot.Item; verb.Act = () => TryEjectToHands(uid, slot, args.User, excludeUserAudio: true); if (slot.EjectVerbText == null) { verb.Text = verbSubject; verb.Category = VerbCategory.Eject; } else { verb.Text = Loc.GetString(slot.EjectVerbText); } verb.Priority = slot.Priority; args.Verbs.Add(verb); } }
private void HandleButtonPressed(EntityUid uid, ItemSlotsComponent component, ItemSlotButtonPressedEvent args) { if (!component.Slots.TryGetValue(args.SlotId, out var slot)) { return; } if (args.TryEject && slot.HasItem) { TryEjectToHands(uid, slot, args.Session.AttachedEntity); } else if (args.TryInsert && !slot.HasItem && args.Session.AttachedEntity is EntityUid user) { TryInsertFromHand(uid, slot, user); } }
/// <summary> /// Attempt to eject an item from the first valid item slot. /// </summary> private void OnUseInHand(EntityUid uid, ItemSlotsComponent itemSlots, UseInHandEvent args) { if (args.Handled) { return; } foreach (var slot in itemSlots.Slots.Values) { if (slot.Locked || !slot.EjectOnUse || slot.Item == null) { continue; } args.Handled = true; TryEjectToHands(uid, slot, args.User); break; } }
private void AddEjectVerbs(EntityUid uid, ItemSlotsComponent itemSlots, GetAlternativeVerbsEvent args) { if (args.Hands == null || !args.CanAccess || !args.CanInteract || !_actionBlockerSystem.CanPickup(args.User)) { return; } foreach (var slot in itemSlots.Slots.Values) { if (slot.Locked || !slot.HasItem) { continue; } if (slot.EjectOnInteract) { // For this item slot, ejecting/inserting is a primary interaction. Instead of an eject category // alt-click verb, there will be a "Take item" primary interaction verb. continue; } var verbSubject = slot.Name != string.Empty ? Loc.GetString(slot.Name) : EntityManager.GetComponent <MetaDataComponent>(slot.Item !.Value).EntityName ?? string.Empty; Verb verb = new(); verb.Act = () => TryEjectToHands(uid, slot, args.User); if (slot.EjectVerbText == null) { verb.Text = verbSubject; verb.Category = VerbCategory.Eject; } else { verb.Text = Loc.GetString(slot.EjectVerbText); verb.IconTexture = "/Textures/Interface/VerbIcons/eject.svg.192dpi.png"; } args.Verbs.Add(verb); } }
private void GetItemSlotsState(EntityUid uid, ItemSlotsComponent component, ref ComponentGetState args) { args.State = new ItemSlotsComponentState(component.Slots); }
private void AddInteractionVerbsVerbs(EntityUid uid, ItemSlotsComponent itemSlots, GetVerbsEvent <InteractionVerb> args) { if (args.Hands == null || !args.CanAccess || !args.CanInteract) { return; } // If there are any slots that eject on left-click, add a "Take <item>" verb. foreach (var slot in itemSlots.Slots.Values) { if (!slot.EjectOnInteract || !CanEject(slot)) { continue; } if (!_actionBlockerSystem.CanPickup(args.User, slot.Item !.Value)) { continue; } var verbSubject = slot.Name != string.Empty ? Loc.GetString(slot.Name) : EntityManager.GetComponent <MetaDataComponent>(slot.Item !.Value).EntityName ?? string.Empty; InteractionVerb takeVerb = new(); takeVerb.IconEntity = slot.Item; takeVerb.Act = () => TryEjectToHands(uid, slot, args.User, excludeUserAudio: true); if (slot.EjectVerbText == null) { takeVerb.Text = Loc.GetString("take-item-verb-text", ("subject", verbSubject)); } else { takeVerb.Text = Loc.GetString(slot.EjectVerbText); } args.Verbs.Add(takeVerb); } // Next, add the insert-item verbs if (args.Using == null || !_actionBlockerSystem.CanDrop(args.User)) { return; } foreach (var slot in itemSlots.Slots.Values) { if (!CanInsert(uid, args.Using.Value, slot)) { continue; } var verbSubject = slot.Name != string.Empty ? Loc.GetString(slot.Name) : Name(args.Using.Value) ?? string.Empty; InteractionVerb insertVerb = new(); insertVerb.IconEntity = args.Using; insertVerb.Act = () => Insert(uid, slot, args.Using.Value, args.User, excludeUserAudio: true); if (slot.InsertVerbText != null) { insertVerb.Text = Loc.GetString(slot.InsertVerbText); insertVerb.IconTexture = "/Textures/Interface/VerbIcons/insert.svg.192dpi.png"; } else if (slot.EjectOnInteract) { // Inserting/ejecting is a primary interaction for this entity. Instead of using the insert // category, we will use a single "Place <item>" verb. insertVerb.Text = Loc.GetString("place-item-verb-text", ("subject", verbSubject)); insertVerb.IconTexture = "/Textures/Interface/VerbIcons/drop.svg.192dpi.png"; } else { insertVerb.Category = VerbCategory.Insert; insertVerb.Text = verbSubject; } args.Verbs.Add(insertVerb); } }
/// <summary> /// Lock an item slot. This stops items from being inserted into or ejected from this slot. /// </summary> public void SetLock(ItemSlotsComponent itemSlots, ItemSlot slot, bool locked) { slot.Locked = locked; itemSlots.Dirty(); }