469 lines
17 KiB
C#
469 lines
17 KiB
C#
using Barotrauma.Items.Components;
|
|
using Microsoft.Xna.Framework;
|
|
using Microsoft.Xna.Framework.Graphics;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
|
|
namespace Barotrauma
|
|
{
|
|
class InventorySlot
|
|
{
|
|
public Rectangle Rect;
|
|
|
|
public Rectangle InteractRect;
|
|
|
|
public bool Disabled;
|
|
|
|
public GUIComponent.ComponentState State;
|
|
|
|
public Color Color;
|
|
|
|
public Color BorderHighlightColor;
|
|
private CoroutineHandle BorderHighlightCoroutine;
|
|
|
|
public bool IsHighlighted
|
|
{
|
|
get
|
|
{
|
|
return State == GUIComponent.ComponentState.Hover;
|
|
}
|
|
}
|
|
|
|
public InventorySlot(Rectangle rect)
|
|
{
|
|
Rect = rect;
|
|
InteractRect = rect;
|
|
InteractRect.Inflate(5, 5);
|
|
State = GUIComponent.ComponentState.None;
|
|
Color = Color.White * 0.4f;
|
|
}
|
|
|
|
public void ShowBorderHighlight(Color color, float fadeInDuration, float fadeOutDuration)
|
|
{
|
|
if (BorderHighlightCoroutine != null)
|
|
{
|
|
BorderHighlightCoroutine = null;
|
|
}
|
|
|
|
BorderHighlightCoroutine = CoroutineManager.StartCoroutine(UpdateBorderHighlight(color, fadeInDuration, fadeOutDuration));
|
|
}
|
|
|
|
private IEnumerable<object> UpdateBorderHighlight(Color color, float fadeInDuration, float fadeOutDuration)
|
|
{
|
|
float t = 0.0f;
|
|
while (t < fadeInDuration + fadeOutDuration)
|
|
{
|
|
BorderHighlightColor = (t < fadeInDuration) ?
|
|
Color.Lerp(Color.Transparent, color, t / fadeInDuration) :
|
|
Color.Lerp(color, Color.Transparent, (t - fadeInDuration) / fadeOutDuration);
|
|
|
|
t += CoroutineManager.DeltaTime;
|
|
|
|
yield return CoroutineStatus.Running;
|
|
}
|
|
|
|
yield return CoroutineStatus.Success;
|
|
}
|
|
}
|
|
|
|
partial class Inventory
|
|
{
|
|
public class SlotReference
|
|
{
|
|
public readonly Inventory Inventory;
|
|
public readonly InventorySlot Slot;
|
|
public readonly int SlotIndex;
|
|
|
|
public bool IsSubSlot;
|
|
|
|
public SlotReference(Inventory inventory, InventorySlot slot, int slotIndex, bool isSubSlot)
|
|
{
|
|
Inventory = inventory;
|
|
Slot = slot;
|
|
SlotIndex = slotIndex;
|
|
IsSubSlot = isSubSlot;
|
|
}
|
|
}
|
|
|
|
public static InventorySlot draggingSlot;
|
|
public static Item draggingItem;
|
|
|
|
public static Item doubleClickedItem;
|
|
|
|
private int slotsPerRow;
|
|
public int SlotsPerRow
|
|
{
|
|
set { slotsPerRow = Math.Max(1, value); }
|
|
}
|
|
|
|
protected static SlotReference highlightedSubInventorySlot;
|
|
protected static Inventory highlightedSubInventory;
|
|
|
|
protected static SlotReference selectedSlot;
|
|
|
|
public InventorySlot[] slots;
|
|
|
|
private Vector2 centerPos;
|
|
|
|
public Vector2 CenterPos
|
|
{
|
|
get { return centerPos; }
|
|
set
|
|
{
|
|
centerPos = value;
|
|
centerPos.X *= GameMain.GraphicsWidth;
|
|
centerPos.Y *= GameMain.GraphicsHeight;
|
|
}
|
|
}
|
|
|
|
private Vector2 drawOffset;
|
|
public Vector2 DrawOffset
|
|
{
|
|
get
|
|
{
|
|
return drawOffset;
|
|
}
|
|
|
|
set
|
|
{
|
|
if (value == drawOffset) return;
|
|
|
|
drawOffset = value;
|
|
CreateSlots();
|
|
}
|
|
}
|
|
|
|
public static SlotReference SelectedSlot
|
|
{
|
|
get { return selectedSlot; }
|
|
}
|
|
|
|
protected virtual void CreateSlots()
|
|
{
|
|
slots = new InventorySlot[capacity];
|
|
|
|
int rectWidth = 40, rectHeight = 40;
|
|
int spacing = 10;
|
|
|
|
int rows = (int)Math.Ceiling((double)capacity / slotsPerRow);
|
|
|
|
int startX = (int)centerPos.X - (rectWidth * slotsPerRow + spacing * (slotsPerRow - 1)) / 2;
|
|
int startY = (int)centerPos.Y - rows * (spacing + rectHeight);
|
|
|
|
Rectangle slotRect = new Rectangle(startX, startY, rectWidth, rectHeight);
|
|
for (int i = 0; i < capacity; i++)
|
|
{
|
|
slotRect.X = startX + (rectWidth + spacing) * (i % slotsPerRow) + (int)DrawOffset.X;
|
|
slotRect.Y = startY + (rectHeight + spacing) * ((int)Math.Floor((double)i / slotsPerRow)) + (int)DrawOffset.Y;
|
|
|
|
slots[i] = new InventorySlot(slotRect);
|
|
}
|
|
|
|
if (selectedSlot != null && selectedSlot.Inventory == this)
|
|
{
|
|
selectedSlot = new SlotReference(this, slots[selectedSlot.SlotIndex], selectedSlot.SlotIndex, selectedSlot.IsSubSlot);
|
|
}
|
|
}
|
|
|
|
public virtual void Update(float deltaTime, bool subInventory = false)
|
|
{
|
|
syncItemsDelay = Math.Max(syncItemsDelay - deltaTime, 0.0f);
|
|
|
|
if (slots == null || isSubInventory != subInventory)
|
|
{
|
|
CreateSlots();
|
|
isSubInventory = subInventory;
|
|
}
|
|
|
|
for (int i = 0; i < capacity; i++)
|
|
{
|
|
if (slots[i].Disabled) continue;
|
|
UpdateSlot(slots[i], i, Items[i], subInventory);
|
|
}
|
|
}
|
|
|
|
protected void UpdateSlot(InventorySlot slot, int slotIndex, Item item, bool isSubSlot)
|
|
{
|
|
bool mouseOn = slot.InteractRect.Contains(PlayerInput.MousePosition) && !Locked;
|
|
|
|
slot.State = GUIComponent.ComponentState.None;
|
|
|
|
if (mouseOn &&
|
|
(draggingItem != null || selectedSlot == null || selectedSlot.Slot == slot) &&
|
|
(highlightedSubInventory == null || highlightedSubInventory == this || highlightedSubInventorySlot?.Slot == slot || highlightedSubInventory.Owner == item))
|
|
{
|
|
slot.State = GUIComponent.ComponentState.Hover;
|
|
|
|
if (selectedSlot == null || (!selectedSlot.IsSubSlot && isSubSlot))
|
|
{
|
|
selectedSlot = new SlotReference(this, slot, slotIndex, isSubSlot);
|
|
}
|
|
|
|
if (draggingItem == null)
|
|
{
|
|
if (PlayerInput.LeftButtonHeld())
|
|
{
|
|
draggingItem = Items[slotIndex];
|
|
draggingSlot = slot;
|
|
}
|
|
}
|
|
else if (PlayerInput.LeftButtonReleased())
|
|
{
|
|
if (PlayerInput.DoubleClicked())
|
|
{
|
|
doubleClickedItem = item;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
protected Inventory GetSubInventory(int slotIndex)
|
|
{
|
|
var item = Items[slotIndex];
|
|
if (item == null) return null;
|
|
|
|
var container = item.GetComponent<ItemContainer>();
|
|
if (container == null) return null;
|
|
|
|
return container.Inventory;
|
|
}
|
|
|
|
public void UpdateSubInventory(float deltaTime, int slotIndex)
|
|
{
|
|
Inventory subInventory = GetSubInventory(slotIndex);
|
|
if (subInventory == null) return;
|
|
|
|
if (subInventory.slots == null) subInventory.CreateSlots();
|
|
|
|
int itemCapacity = subInventory.Items.Length;
|
|
|
|
var slot = slots[slotIndex];
|
|
new Rectangle(slot.Rect.X - 5, slot.Rect.Y - (40 + 10) * itemCapacity - 5,
|
|
slot.Rect.Width + 10, slot.Rect.Height + (40 + 10) * itemCapacity + 10);
|
|
|
|
Rectangle subRect = slot.Rect;
|
|
subRect.Height = 40;
|
|
|
|
for (int i = 0; i < itemCapacity; i++)
|
|
{
|
|
subRect.Y = subRect.Y - subRect.Height - 10;
|
|
subInventory.slots[i].Rect = subRect;
|
|
subInventory.slots[i].InteractRect = subRect;
|
|
subInventory.slots[i].InteractRect.Inflate(5, 5);
|
|
|
|
if (subRect.Y < GameMain.GraphicsHeight * 0.4f)
|
|
{
|
|
subRect = slot.Rect;
|
|
subRect.X = subInventory.slots[i].Rect.Right + 10;
|
|
}
|
|
}
|
|
|
|
subInventory.isSubInventory = true;
|
|
|
|
slots[slotIndex].State = GUIComponent.ComponentState.Hover;
|
|
|
|
subInventory.Update(deltaTime, true);
|
|
}
|
|
|
|
|
|
public virtual void Draw(SpriteBatch spriteBatch, bool subInventory = false)
|
|
{
|
|
if (slots == null || isSubInventory != subInventory) return;
|
|
|
|
for (int i = 0; i < capacity; i++)
|
|
{
|
|
if (slots[i].Disabled) continue;
|
|
|
|
//don't draw the item if it's being dragged out of the slot
|
|
bool drawItem = draggingItem == null || draggingItem != Items[i] || slots[i].IsHighlighted;
|
|
|
|
DrawSlot(spriteBatch, slots[i], Items[i], drawItem);
|
|
}
|
|
|
|
for (int i = 0; i < capacity; i++)
|
|
{
|
|
if (slots[i].InteractRect.Contains(PlayerInput.MousePosition) && !slots[i].Disabled && Items[i] != null)
|
|
{
|
|
string toolTip = "";
|
|
if (GameMain.DebugDraw)
|
|
{
|
|
toolTip = Items[i].ToString();
|
|
}
|
|
else
|
|
{
|
|
string description = Items[i].Description;
|
|
if (Items[i].Prefab.NameMatches("ID Card"))
|
|
{
|
|
string[] readTags = Items[i].Tags.Split(',');
|
|
string idName = null;
|
|
string idJob = null;
|
|
foreach (string tag in readTags)
|
|
{
|
|
string[] s = tag.Split(':');
|
|
if (s[0] == "name")
|
|
idName = s[1];
|
|
if (s[0] == "job")
|
|
idJob = s[1];
|
|
}
|
|
if (idName != null)
|
|
description = "This belongs to " + idName + (idJob != null ? ", the " + idJob + "." : ".") + description;
|
|
}
|
|
toolTip = string.IsNullOrEmpty(description) ?
|
|
Items[i].Name :
|
|
Items[i].Name + '\n' + description;
|
|
}
|
|
|
|
DrawToolTip(spriteBatch, toolTip, slots[i].Rect);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected void DrawToolTip(SpriteBatch spriteBatch, string toolTip, Rectangle highlightedSlot)
|
|
{
|
|
int maxWidth = 300;
|
|
|
|
toolTip = ToolBox.WrapText(toolTip, maxWidth, GUI.Font);
|
|
|
|
Vector2 textSize = GUI.Font.MeasureString(toolTip);
|
|
Vector2 rectSize = textSize * 1.2f;
|
|
|
|
Vector2 pos = new Vector2(highlightedSlot.Right, highlightedSlot.Y);
|
|
pos.X = (int)(pos.X + 3);
|
|
pos.Y = (int)pos.Y - Math.Max((pos.Y + rectSize.Y) - GameMain.GraphicsHeight, 0);
|
|
|
|
GUI.DrawRectangle(spriteBatch, pos, rectSize, Color.Black * 0.8f, true);
|
|
GUI.Font.DrawString(spriteBatch, toolTip,
|
|
new Vector2((int)(pos.X + rectSize.X * 0.5f), (int)(pos.Y + rectSize.Y * 0.5f)),
|
|
Color.White, 0.0f,
|
|
new Vector2((int)(textSize.X * 0.5f), (int)(textSize.Y * 0.5f)),
|
|
1.0f, SpriteEffects.None, 0.0f);
|
|
}
|
|
|
|
|
|
public void DrawSubInventory(SpriteBatch spriteBatch, int slotIndex)
|
|
{
|
|
var item = Items[slotIndex];
|
|
if (item == null) return;
|
|
|
|
var container = item.GetComponent<ItemContainer>();
|
|
if (container == null) return;
|
|
|
|
if (container.Inventory.slots == null || !container.Inventory.isSubInventory) return;
|
|
|
|
int itemCapacity = container.Capacity;
|
|
|
|
#if DEBUG
|
|
System.Diagnostics.Debug.Assert(slotIndex >= 0 && slotIndex < Items.Length);
|
|
#else
|
|
if (slotIndex < 0 || slotIndex >= Items.Length) return;
|
|
#endif
|
|
|
|
var slot = slots[slotIndex];
|
|
Rectangle containerRect = container.Inventory.slots[0].InteractRect;
|
|
for (int i = 1; i< container.Inventory.slots.Length; i++)
|
|
{
|
|
containerRect = Rectangle.Union(containerRect, container.Inventory.slots[i].InteractRect);
|
|
}
|
|
|
|
GUI.DrawRectangle(spriteBatch, new Rectangle(containerRect.X, containerRect.Y, containerRect.Width, containerRect.Height - slot.Rect.Height - 5), Color.Black * 0.8f, true);
|
|
GUI.DrawRectangle(spriteBatch, containerRect, Color.White);
|
|
|
|
container.Inventory.Draw(spriteBatch, true);
|
|
}
|
|
|
|
public static void UpdateDragging()
|
|
{
|
|
if (draggingItem != null && PlayerInput.LeftButtonReleased())
|
|
{
|
|
if (selectedSlot == null)
|
|
{
|
|
draggingItem.ParentInventory?.CreateNetworkEvent();
|
|
draggingItem.Drop();
|
|
GUI.PlayUISound(GUISoundType.DropItem);
|
|
}
|
|
else if (selectedSlot.Inventory.Items[selectedSlot.SlotIndex] != draggingItem)
|
|
{
|
|
Inventory selectedInventory = selectedSlot.Inventory;
|
|
int slotIndex = selectedSlot.SlotIndex;
|
|
if (selectedInventory.TryPutItem(draggingItem, slotIndex, true, true, Character.Controlled))
|
|
{
|
|
if (selectedInventory.slots != null) selectedInventory.slots[slotIndex].ShowBorderHighlight(Color.White, 0.1f, 0.4f);
|
|
GUI.PlayUISound(GUISoundType.PickItem);
|
|
}
|
|
else
|
|
{
|
|
if (selectedInventory.slots != null) selectedInventory.slots[slotIndex].ShowBorderHighlight(Color.Red, 0.1f, 0.9f);
|
|
GUI.PlayUISound(GUISoundType.PickItemFail);
|
|
}
|
|
draggingItem = null;
|
|
draggingSlot = null;
|
|
}
|
|
|
|
draggingItem = null;
|
|
}
|
|
|
|
if (selectedSlot != null && !selectedSlot.Slot.InteractRect.Contains(PlayerInput.MousePosition))
|
|
{
|
|
selectedSlot = null;
|
|
}
|
|
}
|
|
|
|
public static void DrawDragging(SpriteBatch spriteBatch)
|
|
{
|
|
if (draggingItem == null) return;
|
|
|
|
if (draggingSlot == null || (!draggingSlot.InteractRect.Contains(PlayerInput.MousePosition)))
|
|
{
|
|
Rectangle dragRect = new Rectangle(
|
|
(int)PlayerInput.MousePosition.X - 10,
|
|
(int)PlayerInput.MousePosition.Y - 10,
|
|
40, 40);
|
|
|
|
DrawSlot(spriteBatch, new InventorySlot(dragRect), draggingItem);
|
|
}
|
|
}
|
|
|
|
public static void DrawSlot(SpriteBatch spriteBatch, InventorySlot slot, Item item, bool drawItem = true)
|
|
{
|
|
Rectangle rect = slot.Rect;
|
|
|
|
GUI.DrawRectangle(spriteBatch, rect, (slot.IsHighlighted ? Color.Red * 0.4f : slot.Color), true);
|
|
|
|
if (item != null && drawItem)
|
|
{
|
|
if (item.Condition < item.Prefab.Health)
|
|
{
|
|
GUI.DrawRectangle(spriteBatch, new Rectangle(rect.X, rect.Bottom - 8, rect.Width, 8), Color.Black * 0.8f, true);
|
|
GUI.DrawRectangle(spriteBatch,
|
|
new Rectangle(rect.X, rect.Bottom - 8, (int)(rect.Width * item.Condition / item.Prefab.Health), 8),
|
|
Color.Lerp(Color.Red, Color.Green, item.Condition / 100.0f) * 0.8f, true);
|
|
}
|
|
|
|
var containedItems = item.ContainedItems;
|
|
if (containedItems != null && containedItems.Length == 1 && containedItems[0].Condition < item.Prefab.Health)
|
|
{
|
|
GUI.DrawRectangle(spriteBatch, new Rectangle(rect.X, rect.Y, rect.Width, 8), Color.Black * 0.8f, true);
|
|
GUI.DrawRectangle(spriteBatch,
|
|
new Rectangle(rect.X, rect.Y, (int)(rect.Width * containedItems[0].Condition / 100.0f), 8),
|
|
Color.Lerp(Color.Red, Color.Green, containedItems[0].Condition / item.Prefab.Health) * 0.8f, true);
|
|
}
|
|
}
|
|
|
|
GUI.DrawRectangle(spriteBatch, rect, (slot.IsHighlighted ? Color.Red * 0.4f : slot.Color), false);
|
|
|
|
if (slot.BorderHighlightColor != Color.Transparent)
|
|
{
|
|
Rectangle highlightRect = slot.Rect;
|
|
highlightRect.Inflate(3, 3);
|
|
|
|
GUI.DrawRectangle(spriteBatch, highlightRect, slot.BorderHighlightColor, false, 0, 5);
|
|
}
|
|
|
|
if (item == null || !drawItem) return;
|
|
|
|
item.Sprite.Draw(spriteBatch, new Vector2(rect.X + rect.Width / 2, rect.Y + rect.Height / 2), item.GetSpriteColor());
|
|
}
|
|
}
|
|
}
|