4 using Microsoft.Xna.Framework;
5 using Microsoft.Xna.Framework.Graphics;
6 using Microsoft.Xna.Framework.Input;
8 using System.Collections.Generic;
10 using System.Xml.Linq;
14 partial class CharacterInventory : Inventory
24 private enum QuickUseAction
38 private static Dictionary<InvSlotType, Sprite> limbSlotIcons;
43 if (limbSlotIcons ==
null)
45 limbSlotIcons =
new Dictionary<InvSlotType, Sprite>();
48 var sprite = GUIStyle.GetComponentStyle($
"InventorySlot.{invSlotType}")?.GetDefaultSprite();
51 limbSlotIcons.Add(invSlotType, sprite);
56 AddIfMissing(
InvSlotType.Headset,
new Sprite(
"Content/UI/MainIconsAtlas.png",
new Rectangle(384 + margin, 128 + margin, 128 - margin * 2, 128 - margin * 2)));
57 AddIfMissing(
InvSlotType.InnerClothes,
new Sprite(
"Content/UI/MainIconsAtlas.png",
new Rectangle(512 + margin, 128 + margin, 128 - margin * 2, 128 - margin * 2)));
58 AddIfMissing(
InvSlotType.Card,
new Sprite(
"Content/UI/MainIconsAtlas.png",
new Rectangle(640 + margin, 128 + margin, 128 - margin * 2, 128 - margin * 2)));
59 AddIfMissing(
InvSlotType.Head,
new Sprite(
"Content/UI/MainIconsAtlas.png",
new Rectangle(896 + margin, 128 + margin, 128 - margin * 2, 128 - margin * 2)));
62 AddIfMissing(
InvSlotType.OuterClothes,
new Sprite(
"Content/UI/MainIconsAtlas.png",
new Rectangle(256 + margin, 128 + margin, 128 - margin * 2, 128 - margin * 2)));
67 limbSlotIcons.TryAdd(slotType, sprite);
76 private Point screenResolution;
84 get {
return layout; }
87 if (layout == value)
return;
89 SetSlotPositions(layout);
95 partial
void InitProjSpecific(XElement element)
99 SetSlotPositions(layout);
105 if (item ==
null) {
return null; }
108 if (container ==
null || !container.KeepOpenWhenEquippedBy(character))
119 float multiplier =
UIScale * GUI.AspectRatioAdjustment;
129 (
int)(slotSprite.
size.X * multiplier), (
int)(slotSprite.
size.Y * multiplier));
138 if (itemContainer !=
null)
148 SlotSprite = slotSprite,
151 if (prevSlot !=
null)
168 if (subSlot.ParentInventory ==
this && subSlot.SlotIndex > 0 && subSlot.SlotIndex <
visualSlots.Length)
191 frame.Inflate(10, 30);
192 frame.Location -=
new Point(0, 25);
210 if (!ownHealthWindowOpen) {
return true; }
215 if (!otherCharacterHealthWindowOpen) {
return true; }
221 if (layout ==
Layout.Default)
257 private void SetSlotPositions(
Layout layout)
259 int spacing = GUI.IntScale(5);
275 int x = GameMain.GraphicsWidth / 2 - normalSlotCount * (
SlotSize.X + spacing) / 2;
276 int upperX = HUDLayoutSettings.BottomRightInfoArea.X -
SlotSize.X - spacing;
279 x -= Math.Max((x + normalSlotCount * (
SlotSize.X + spacing)) - (upperX - personalSlotCount * (
SlotSize.X + spacing)), 0);
281 int hideButtonSlotIndex = -1;
286 SlotPositions[i] =
new Vector2(upperX, GameMain.GraphicsHeight - bottomOffset);
288 personalSlotArea = (hideButtonSlotIndex == -1) ?
291 hideButtonSlotIndex = i;
295 SlotPositions[i] =
new Vector2(x, GameMain.GraphicsHeight - bottomOffset);
303 int x = HUDLayoutSettings.InventoryAreaLower.Right;
304 int personalSlotX = HUDLayoutSettings.InventoryAreaLower.Right -
SlotSize.X - spacing;
333 SlotPositions[i] =
new Vector2(personalSlotX, personalSlotY);
338 SlotPositions[i] =
new Vector2(x, GameMain.GraphicsHeight - bottomOffset);
349 SlotPositions[i] =
new Vector2(x, GameMain.GraphicsHeight - bottomOffset);
355 int x = HUDLayoutSettings.InventoryAreaLower.X;
356 int personalSlotX = x;
364 SlotPositions[i] =
new Vector2(personalSlotX, personalSlotY);
369 SlotPositions[i] =
new Vector2(x, GameMain.GraphicsHeight - bottomOffset);
379 SlotPositions[i] =
new Vector2(rightSlot ? handSlotX : handSlotX -
visualSlots[0].Rect.Width - spacing, personalSlotY);
383 SlotPositions[i] =
new Vector2(x, GameMain.GraphicsHeight - bottomOffset);
391 int startX = (GameMain.GraphicsWidth / 2) - (
SlotSize.X * columns + spacing * (columns - 1)) / 2;
392 int startY = GameMain.GraphicsHeight / 2 - (
SlotSize.Y * 2);
393 int x = startX, y = startY;
438 if (layout ==
Layout.Default)
453 base.ControlInput(cam);
461 private readonly
static List<SlotReference> hideSubInventories =
new List<SlotReference>();
462 private readonly
static List<SlotReference> tempHighlightedSubInventorySlots =
new List<SlotReference>();
464 public override void Update(
float deltaTime,
Camera cam,
bool isSubInventory =
false)
473 base.Update(deltaTime, cam);
475 bool hoverOnInventory = GUI.MouseOn ==
null &&
479 if (hoverOnInventory) {
HideTimer = 0.5f; }
484 hideSubInventories.Clear();
487 s.ParentInventory ==
this &&
494 tempHighlightedSubInventorySlots.Clear();
496 foreach (var highlightedSubInventorySlot
in tempHighlightedSubInventorySlots)
498 if (highlightedSubInventorySlot.ParentInventory ==
this)
503 if (!highlightedSubInventorySlot.Inventory.IsInventoryHoverAvailable(character,
null))
continue;
508 hideSubInventories.Add(highlightedSubInventorySlot);
512 highlightedSubInventorySlot.Inventory.HideTimer = 1.0f;
520 if (subInventory !=
null && subInventory.IsInventoryHoverAvailable(character,
null))
525 ShowSubInventory(
selectedSlot, deltaTime, cam, hideSubInventories,
false);
536 if (subInventory !=
null)
538 ShowSubInventory(
new SlotReference(
this,
visualSlots[i], i,
false, subInventory), deltaTime, cam, hideSubInventories,
true);
543 foreach (var subInventorySlot
in hideSubInventories)
545 if (subInventorySlot.Inventory ==
null) {
continue; }
546 subInventorySlot.Inventory.HideTimer -= deltaTime;
547 if (subInventorySlot.Inventory.HideTimer < 0.25f)
567 if (itemContainer !=
null &&
568 itemContainer.KeepOpenWhenEquippedBy(character) &&
573 ShowSubInventory(
new SlotReference(
this,
visualSlots[i], i,
false, itemContainer.Inventory), deltaTime, cam, hideSubInventories,
true);
586 QuickUseItem(doubleClickedItem,
true,
true,
true, quickUseAction, playSound: doubleClickedItem ==
doubleClickedItems.First());
589 if (quickUseAction == QuickUseAction.Equip || quickUseAction == QuickUseAction.UseTreatment)
603 if ((quickUseAction == QuickUseAction.TakeFromContainer || quickUseAction == QuickUseAction.PutToEquippedItem) &&
620 HandleButtonEquipStates(item, slot, deltaTime);
631 if (rootContainer !=
null)
636 if (rootInventory !=
null &&
642 if (!(rootContainer !=
null &&
643 rootContainer.DisplaySideBySideWhenLinked &&
667 var quickUseAction = GetQuickUseAction(firstItem,
true,
false,
true);
668 foreach (
Item itemToUse
in slots[i].Items.ToList())
670 QuickUseItem(itemToUse,
true,
true,
true, quickUseAction, playSound: itemToUse == firstItem);
671 if (quickUseAction == QuickUseAction.Equip || quickUseAction == QuickUseAction.UseTreatment)
680 private void HandleButtonEquipStates(
Item item,
VisualSlot slot,
float deltaTime)
695 var quickUseAction = GetQuickUseAction(item, allowEquip:
true, allowInventorySwap:
false, allowApplyTreatment:
false);
697 if (quickUseAction != QuickUseAction.Drop)
700 "" : TextManager.GetWithVariable(
"QuickUseAction." + quickUseAction.ToString(),
"[equippeditem]", character.
HeldItems.FirstOrDefault()?.Name ?? item?.Name);
702 if (PlayerInput.PrimaryMouseButtonClicked())
704 QuickUseItem(item, allowEquip:
true, allowInventorySwap:
false, allowApplyTreatment:
false);
709 private void ShowSubInventory(SlotReference slotRef,
float deltaTime, Camera cam, List<SlotReference> hideSubInventories,
bool isEquippedSubInventory)
712 if (isEquippedSubInventory && slotRef.Inventory is not ItemInventory { Container.MovableFrame: true, Container.KeepOpenWhenEquipped: true })
716 if (highlightedSubInventorySlot == slotRef) {
continue; }
724 if (isEquippedSubInventory)
726 slotRef.Inventory.OpenState = 1.0f;
730 slotRef.Inventory.HideTimer = 1f;
736 if (highlightedSubInventorySlot == slotRef)
continue;
739 hideSubInventories.Add(highlightedSubInventorySlot);
740 highlightedSubInventorySlot.Inventory.HideTimer = 0.0f;
744 HintManager.OnShowSubInventory(slotRef?.Item);
749 int keyBindIndex = 0;
761 private QuickUseAction GetQuickUseAction(
Item item,
bool allowEquip,
bool allowInventorySwap,
bool allowApplyTreatment)
767 return QuickUseAction.UseTreatment;
772 if (Screen.Selected == GameMain.GameScreen)
776 return QuickUseAction.None;
781 return QuickUseAction.None;
784 else if (allowInventorySwap)
788 if (character.
HeldItems.Any(i => i.OwnInventory !=
null && i.OwnInventory.CanBePut(item) && character.
CanAccessInventory(i.OwnInventory)))
790 return QuickUseAction.PutToEquippedItem;
800 if (selectedContainer !=
null &&
801 selectedContainer.Inventory !=
null &&
802 !selectedContainer.Inventory.Locked)
805 return QuickUseAction.PutToContainer;
810 return QuickUseAction.TakeFromContainer;
819 if (selectedContainer !=
null &&
820 selectedContainer.Inventory !=
null &&
821 !selectedContainer.Inventory.Locked &&
822 selectedContainer.DrawInventory &&
826 return QuickUseAction.PutToContainer;
833 return QuickUseAction.PutToCharacter;
841 return QuickUseAction.TakeFromCharacter;
843 else if (character.
HeldItems.FirstOrDefault(i =>
844 i.OwnInventory !=
null &&
845 i.OwnInventory.Container.DrawInventory &&
847 (i.OwnInventory.CanBePut(item) || ((i.OwnInventory.Capacity == 1 || i.OwnInventory.Container.HasSubContainers) && i.OwnInventory.AllowSwappingContainedItems && i.OwnInventory.Container.CanBeContained(item)))) is { } equippedContainer)
853 if (equippedContainer.GetComponent<
ItemContainer>() is { QuickUseMovesItemsInside:
false })
859 return QuickUseAction.Equip;
866 return QuickUseAction.Unequip;
869 return QuickUseAction.PutToEquippedItem;
876 return QuickUseAction.Equip;
881 return QuickUseAction.Unequip;
885 return QuickUseAction.Drop;
890 return QuickUseAction.None;
893 private void QuickUseItem(Item item,
bool allowEquip,
bool allowInventorySwap,
bool allowApplyTreatment, QuickUseAction? action =
null,
bool playSound =
true)
895 if (Screen.Selected is SubEditorScreen editor && !editor.WiringMode && !
Submarine.Unloading)
898 if (item.ParentInventory?.visualSlots !=
null)
900 var invSlots = item.ParentInventory.visualSlots;
901 for (
int i = 0; i < invSlots.Length; i++)
903 if (i < 0 || invSlots.Length <= i || i < 0 || item.ParentInventory.Capacity <= i) {
break; }
905 var slot = invSlots[i];
906 if (item.ParentInventory.GetItemAt(i) == item)
915 SubEditorScreen.StoreCommand(
new AddOrDeleteCommand(
new List<MapEntity> { item },
true));
921 QuickUseAction quickUseAction = action ?? GetQuickUseAction(item, allowEquip, allowInventorySwap, allowApplyTreatment);
922 bool success =
false;
923 switch (quickUseAction)
925 case QuickUseAction.Equip:
926 if (
string.IsNullOrEmpty(item.Prefab.EquipConfirmationText) || character !=
Character.Controlled)
932 if (GUIMessageBox.MessageBoxes.Any(mb => mb.UserData as
string ==
"equipconfirmation")) {
return; }
933 var equipConfirmation =
new GUIMessageBox(
string.Empty, TextManager.Get(item.Prefab.EquipConfirmationText),
934 new LocalizedString[] { TextManager.Get(
"yes"), TextManager.Get(
"no") })
936 UserData =
"equipconfirmation"
938 equipConfirmation.Buttons[0].OnClicked = (btn, userdata) =>
941 equipConfirmation.Close();
944 equipConfirmation.Buttons[1].OnClicked = equipConfirmation.Close;
950 for (
int i =
capacity - 1; i >= 0; i--)
955 if (success) {
break; }
960 for (
int i =
capacity - 1; i >= 0; i--)
970 if (success) {
break; }
975 case QuickUseAction.Unequip:
978 success =
TryPutItem(item,
Character.Controlled,
new List<InvSlotType>() { InvSlotType.Any },
true);
981 case QuickUseAction.UseTreatment:
982 CharacterHealth.OpenHealthWindow?.OnItemDropped(item, ignoreMousePos:
true);
984 case QuickUseAction.Drop:
987 case QuickUseAction.PutToCharacter:
994 case QuickUseAction.PutToContainer:
996 if (selectedContainer !=
null && selectedContainer.Inventory !=
null)
1002 case QuickUseAction.TakeFromCharacter:
1010 case QuickUseAction.TakeFromContainer:
1012 ItemInventory activeSubInventory =
null;
1016 if (activeSubInventory !=
null)
1018 success = activeSubInventory.TryPutItem(item,
Character.Controlled, item.AllowedSlots,
true);
1029 case QuickUseAction.PutToEquippedItem:
1031 foreach (Item heldItem
in character.
HeldItems.OrderByDescending(heldItem => GetContainPriority(item, heldItem)))
1033 if (heldItem.OwnInventory ==
null || !heldItem.OwnInventory.Container.DrawInventory) {
continue; }
1036 bool disallowSwapping =
1037 (heldItem.OwnInventory.Capacity == 1 || heldItem.OwnInventory.Container.HasSubContainers) &&
1038 heldItem.OwnInventory.GetItemAt(0)?.Prefab == item.Prefab &&
1039 heldItem.OwnInventory.GetItemsAt(0).Count() > 1;
1040 if (heldItem.OwnInventory.TryPutItem(item,
Character.Controlled) ||
1041 ((heldItem.OwnInventory.Capacity == 1 || heldItem.OwnInventory.Container.HasSubContainers) && heldItem.OwnInventory.TryPutItem(item, 0, allowSwapping: !disallowSwapping, allowCombine:
false, user:
Character.Controlled)))
1053 static float GetContainPriority(Item item, Item containerItem)
1055 var container = containerItem.GetComponent<
ItemContainer>();
1056 if (container ==
null) {
return 0.0f; }
1057 for (
int i = 0; i < container.Inventory.Capacity; i++)
1060 if (containedItems.Any() && container.Inventory.CanBePutInSlot(item, i))
1066 return -container.GetContainedIndicatorState();
1087 if (item ==
null) {
return false; }
1088 foreach (var allowedSlot
in item.AllowedSlots)
1091 for (
int i = 0; i <
slots.Length; i++)
1095 if (allowedSlot == slotsFree) {
return true; }
1105 if (item ==
null ||
visualSlots ==
null) {
return; }
1106 bool flashed =
false;
1107 foreach (var allowedSlot
in item.AllowedSlots)
1109 for (
int i = 0; i <
slots.Length; i++)
1136 SetSlotPositions(layout);
1141 if (layout ==
Layout.Center)
1144 GUI.DrawRectangle(spriteBatch,
BackgroundFrame, Color.Black * 0.8f,
true);
1145 GUI.DrawString(spriteBatch,
1147 character.
Name, Color.White * 0.9f);
1169 if (
slots[i].Empty() ||
1206 Color color = Color.White;
1212 Vector2 indicatorScale =
new Vector2(
1213 visualSlots[i].EquipButtonRect.Size.X / EquippedIndicator.size.X,
1217 var sprite = state
switch
1222 => isEquipped ? EquippedHoverIndicator : UnequippedHoverIndicator,
1226 => isEquipped ? EquippedClickedIndicator : UnequippedClickedIndicator,
1227 _ =>
throw new NotImplementedException()
1229 sprite.Draw(spriteBatch,
visualSlots[i].EquipButtonRect.Center.ToVector2(), color, EquippedIndicator.Origin, 0, indicatorScale);
1234 GUI.DrawRectangle(spriteBatch, inventoryArea,
new Color(30,30,30,100), isFilled:
true);
1235 var lockIcon = GUIStyle.GetComponentStyle(
"LockIcon")?.GetDefaultSprite();
1236 lockIcon?.Draw(spriteBatch, inventoryArea.Center.ToVector2(), scale: Math.Min(inventoryArea.Height / lockIcon.size.Y * 0.7f, 1.0f));
1239 GUIComponent.
DrawToolTip(spriteBatch, TextManager.Get(
"handcuffed"),
new Rectangle(inventoryArea.Center -
new Point(inventoryArea.Height / 2),
new Point(inventoryArea.Height)));
1242 else if (highlightedQuickUseSlot !=
null && !highlightedQuickUseSlot.
QuickUseButtonToolTip.IsNullOrEmpty())
bool Freeze
Resets to false each time the MoveCamera method is called.
static CharacterHealth?? OpenHealthWindow
GUIComponent InventorySlotContainer
CharacterHealth CharacterHealth
bool HasEquippedItem(Item item, InvSlotType? slotType=null, Func< InvSlotType, bool > predicate=null)
Item????????? SelectedItem
The primary selected item. It can be any device that character interacts with. This excludes items li...
Character SelectedCharacter
CharacterInventory Inventory
bool CanAccessInventory(Inventory inventory, CharacterInventory.AccessLevel accessLevel=CharacterInventory.AccessLevel.Limited)
static Character? Controlled
IEnumerable< Item >?? HeldItems
Items the character has in their hand slots. Doesn't return nulls and only returns items held in both...
void FlashAllowedSlots(Item item, Color color)
Flash the slots the item is allowed to go in (not taking into account whether there's already somethi...
static Dictionary< InvSlotType, Sprite >? LimbSlotIcons
override bool HideSlot(int i)
override void CreateSlots()
override bool TryPutItem(Item item, Character user, IEnumerable< InvSlotType > allowedSlots=null, bool createNetworkEvent=true, bool ignoreCondition=false)
If there is room, puts the item in the inventory and returns true, otherwise returns false
override void CalculateBackgroundFrame()
override void ControlInput(Camera cam)
const InvSlotType PersonalSlots
bool AccessibleWhenAlive
Can the inventory be accessed when the character is still alive
Item GetItemInLimbSlot(InvSlotType limbSlot)
bool AccessibleByOwner
Can the inventory be accessed by the character itself when the character is still alive (only has an ...
void DrawOwn(SpriteBatch spriteBatch)
override ItemInventory GetActiveEquippedSubInventory(int slotIndex)
void ClientEventWrite(IWriteMessage msg, Character.InventoryStateEventData extraData)
bool CanBeAutoMovedToCorrectSlots(Item item)
bool TryPutItemWithAutoEquipCheck(Item item, Character user, IEnumerable< InvSlotType > allowedSlots=null, bool createNetworkEvent=true)
If there is no room in the generic inventory (InvSlotType.Any), check if the item can be auto-equippe...
void RefreshSlotPositions()
CharacterInventory(ContentXElement element, Character character, bool spawnInitialItems)
bool IsInLimbSlot(Item item, InvSlotType limbSlot)
override void Update(float deltaTime, Camera cam, bool isSubInventory=false)
void AssignQuickUseNumKeys()
Responsible for keeping track of the characters in the player crew, saving and loading their orders,...
static bool? IsCommandInterfaceOpen
void DrawToolTip(SpriteBatch spriteBatch)
Creates and draws a tooltip.
static SubEditorScreen SubEditorScreen
static int GraphicsHeight
IReadOnlyList< Item > Items
readonly int capacity
Capacity, or the number of slots in the inventory.
Rectangle BackgroundFrame
readonly ItemSlot[] slots
Item FirstOrDefault()
Return the first item in the inventory, or null if the inventory is empty.
static Sprite SlotSpriteSmall
static void DrawSlot(SpriteBatch spriteBatch, Inventory inventory, VisualSlot slot, Item item, int slotIndex, bool drawItem=true, InvSlotType type=InvSlotType.Any)
bool Contains(Item item)
Is the item contained in this inventory. Does not recursively check items inside items.
static SlotReference selectedSlot
void UpdateSubInventory(float deltaTime, int slotIndex, Camera cam)
static VisualSlot DraggingSlot
void SharedWrite(IWriteMessage msg, Range slotRange)
static readonly List< Item > DraggingItems
static int ContainedIndicatorHeight
Inventory GetSubInventory(int slotIndex)
static Sprite UnequippedIndicator
static HashSet< SlotReference > highlightedSubInventorySlots
int FindIndex(Item item)
Find the index of the first slot the item is contained in.
static Rectangle GetSubInventoryHoverArea(SlotReference subSlot)
static readonly List< Item > doubleClickedItems
IEnumerable< Item > GetItemsAt(int index)
Get all the item stored in the specified inventory slot. Can return more than one item if the slot co...
Inventory ParentInventory
IEnumerable< InvSlotType > AllowedSlots
bool IsInteractable(Character character)
Returns interactibility based on whether the character is on a player team
override bool TryPutItem(Item item, Character user, IEnumerable< InvSlotType > allowedSlots=null, bool createNetworkEvent=true, bool ignoreCondition=false)
If there is room, puts the item in the inventory and returns true, otherwise returns false
int GetMaxStackSize(Inventory inventory)
readonly ItemInventory Inventory
Sprite InventoryBottomSprite
Sprite InventoryTopSprite
readonly List< MapEntity > linkedTo
static bool IsSubEditor()
static bool SkipInventorySlotUpdate
void MoveBorderHighlight(VisualSlot newSlot)
Moves the current border highlight animation (if one is running) to the new slot
GUIComponent.ComponentState EquipButtonState
Rectangle EquipButtonRect
void ShowBorderHighlight(Color color, float fadeInDuration, float fadeOutDuration, float scaleUpAmount=0.5f)
LocalizedString QuickUseButtonToolTip