using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using TMPro; using UnityEngine.EventSystems; // TODO: Expand for other measurement systems later namespace SimpleInventorySystem { public enum Orientation { Portrait, Landscape }; [Serializable] public struct Size { public int Width; public int Height; public Size(int width, int height) { this.Width = width; this.Height = height; } public Size(float width, float height) { this.Width = (int)width; this.Height = (int)height; } public int span { get { return Width * Height; } } } public struct KeyInput { public bool pressedKey, holdingKey; public KeyCode keyCode; public KeyInput(KeyCode keyCode) { this.keyCode = keyCode; pressedKey = false; holdingKey = false; } public void Update() { if (Input.GetKeyDown(keyCode) && !pressedKey && !holdingKey) { //Debug.Log("R"); pressedKey = true; } else if (pressedKey && !holdingKey) { holdingKey = true; } else if (Input.GetKeyUp(keyCode)) { //Debug.Log("R - up"); holdingKey = false; pressedKey = false; } } public bool PressedKey(bool singlePress) { return pressedKey && (!singlePress || !holdingKey); } } [Serializable] public class TagSlotPairing { public ItemTags itemTag; public SlotUI slot; public bool prioritizeItemStoreFirst; public bool HasSlotOccupied() { return slot.GetItemUI() != null; } public bool DropOntoSlot(ItemUI item) { return CanEquip(item) && slot.DropOntoSlot(item.gameObject); } public bool CanEquip(ItemUI item) { return item.ItemTag == itemTag && slot != null && (!HasSlotOccupied() || slot.IsStacking()) && slot.CanDropOntoSlot(); } } [Serializable] public class TagSlotGridPairing { public ItemTags itemTag; public enum ReferenceTypes { SlotUI, GridUI, GridUIFromSlot, TagSlotGridPairUI } public ReferenceTypes referenceType = ReferenceTypes.SlotUI; public GameObject ReferencedGrid; [HideInInspector] public SlotUI slot; [HideInInspector] public GridUI grid; public TagSlotGridPairing() { Debug.Log("this should be called"); SetSlotsOrGrid(); /*if (tagSlotGridPairUI == null && slot == null && tagSlotGridPairUI == null) { Debug.LogWarning($"TagSlotGridPairing: This TagSlotGridPairing will not work for {itemTag} on type {referenceType}."); }*/ } public void SetSlotsOrGrid() { switch (referenceType) { case ReferenceTypes.SlotUI: { if (ReferencedGrid != null) slot = ReferencedGrid.GetComponent(); else { Debug.LogWarning($"TagSlotGridPairing: ReferencedGrid missing."); } break; } case ReferenceTypes.GridUI: { if (ReferencedGrid != null) grid = ReferencedGrid.GetComponent(); else { Debug.LogWarning($"TagSlotGridPairing: ReferencedGrid missing."); } break; } case ReferenceTypes.GridUIFromSlot: { if (ReferencedGrid != null) { slot = ReferencedGrid.GetComponent(); } else { Debug.LogWarning($"TagSlotGridPairing: ReferencedGrid missing."); } break; } case ReferenceTypes.TagSlotGridPairUI: { if (ReferencedGrid != null) { Debug.LogWarning($"TagSlotGridPairing: This TagSlotGridPairing may not use {ReferencedGrid.name} as a Referenced Grid because reference type {referenceType} will search for most available slot based on Heiarchy order."); } break; } } } public bool IsSlotOccupied() { return slot.GetItemUI() != null; } protected bool DropOntoGrid(ItemUI item) { //List cells = new List(); //bool canMoveInCells = grid.CanMoveInCells(item, slot, cells); bool dropped = CanStore(item) && grid.DropItemOnGrid(item.gameObject); Debug.Log("IS:Dropped:" + dropped); return dropped; } protected bool DropOntoSlot(ItemUI item) { bool dropped = CanStore(item) && slot.DropOntoSlot(item.gameObject); Debug.Log("IS:Dropped:" + dropped); return dropped; } public bool Drop(ItemUI item) { bool dropped = (referenceType == ReferenceTypes.SlotUI) ? DropOntoSlot(item) : DropOntoGrid(item); return dropped; } public bool CanStore(ItemUI item) { SetSlotsOrGrid(); bool preliminaryCheck = item != null && (item.ItemTag == itemTag || itemTag == ItemTags.Any) && slot != null; //Debug.Log("IS:CanStore" + (preliminaryCheck)); //Debug.Log("IS:CanStore" + (slot != null)); bool slotCheck = false; bool gridCheck = false; if (slot != null && (referenceType == ReferenceTypes.GridUIFromSlot || referenceType == ReferenceTypes.SlotUI)) { if(referenceType == ReferenceTypes.GridUIFromSlot) { slotCheck = preliminaryCheck && IsSlotOccupied() && slot.GetItemUI().container != null && slot != null && slot.GetItemUI() != null && slot.GetItemUI().container != null; if (slotCheck) { grid = slot.GetItemUI().container.GetComponent(); } gridCheck = grid != null && !grid.IsAssociatedItem(item) && !grid.ContainsItemFromGrid(item); // TODO: improve on this for stacking //Debug.Log($"Contains Item From Grid: {gridCheck}"); //TODO: create function to check if item is inside grid. } else { slotCheck = preliminaryCheck && (!IsSlotOccupied() || slot.GetItemUI().Stackable) && slot != item.GetSlotUI(); //Debug.Log(slotCheck); gridCheck = true; } } else if (referenceType == ReferenceTypes.GridUI) { slotCheck = true; gridCheck = grid != null && !grid.ContainsItemFromGrid(item); } else if (referenceType == ReferenceTypes.TagSlotGridPairUI) { slotCheck = true; TagSlotGridPairUI[] gridPairs = InventorySystem.instance.inventoryUI.GetComponentsInChildren(); foreach (TagSlotGridPairUI gp in gridPairs) { if (gp.IsAllowed(item.ItemTag)) { grid = gp.GetComponent(); if (grid != null) { //Debug.Log($"TagSlotGridPairing: {grid.gameObject.name}"); if (grid.slots != null && grid.slots.Length > 0 && grid.slots[0] != null) gridCheck = grid.CanMoveInCells(item, grid.slots[0]) && !grid.ContainsItemFromGrid(item);// && grid.associatedItemUI.ContainedItems != null && !grid.associatedItemUI.ContainedItems.Contains(item); // TODO: improve on this for stacking if (gridCheck) { break; } } } } } //Debug.Log("IS:CanStore" + (slotCheck && gridCheck)); return slotCheck && gridCheck; } } public class InventorySystem : MonoBehaviour, IPointerClickHandler { public static InventorySystem instance; public Canvas inventoryUI; public Canvas playerUI; ///private TextMeshProUGUI lootMessage; public GameObject InventoryMenuUI; public GameObject LootMenuUI; public GameObject StatsMenuUI; public KeyCode rotateKey; public KeyCode menuKey; public KeyCode interactKey; public KeyCode dropItemKey; public KeyCode equipItemKey; protected KeyInput rotationKeyInput; protected KeyInput menuKeyInput; protected KeyInput interactKeyInput; protected KeyInput dropItemKeyInput; protected KeyInput equipItemKeyInput; [SerializeField] private int DefaultWidthOnGrid = 100; [SerializeField] private int DefaultHeightOnGrid = 100; public Size DefaultSizeOnGrid; public Camera playerCamera; public GameObject EventsObject; private GameObject toolbarEvents; public StandaloneInputModule StandaloneInputModule; public Vector2 MouseInCanvasPosition; private ItemUI draggedItem; public ItemUI DraggedItem { set { draggedItem = value; IsDraggingItem = true; } get { return draggedItem; } } public ContextMenuManagerUI ContextMenuUI; public static List hoverResults; public Player_IS player; public List TagSlotPairings; public List TagSlotGridPairings; public List raritiesList; [Range(0, 1)] public float ItemBackdropTransparency = .5f; public GridUI LootGrid; private Inventory LastLootedInventory; public GameObject StorageWindowPrefab; public Transform DefaultItemSpawn; [HideInInspector] public bool IsDraggingItem; public enum States { Undefined, Loot, PickUp }; public States CurrentState = States.Undefined; private void Awake() { if (instance == null) { instance = this; } else { // TODO: Test this, make sure only one inventory system is active throughout the game Destroy(gameObject); } DefaultSizeOnGrid = new Size(DefaultWidthOnGrid, DefaultHeightOnGrid); rotationKeyInput = new KeyInput(rotateKey); menuKeyInput = new KeyInput(menuKey); interactKeyInput = new KeyInput(interactKey); dropItemKeyInput = new KeyInput(dropItemKey); equipItemKeyInput = new KeyInput(equipItemKey); toolbarEvents = EventsObject.transform.Find("Toolbar").gameObject; } public Rarity GetRarityFromList(RarityType rarityType) { foreach(Rarity rarity in raritiesList) { if(rarity.rarityType == rarityType) { return rarity; } } return null; } public void SetPlayerMessageToLoot() { } // Start is called before the first frame update void Start() { InventoryMenuUI.SetActive(false); inventoryUI.overrideSorting = true; CheckForRequiredComponents(); } public bool CheckForRequiredComponents() { if (playerCamera == null || !playerCamera.gameObject.activeSelf) { Camera[] cameras = FindObjectsOfType(); foreach(Camera cam in cameras) { if (cam.gameObject.activeSelf) { playerCamera = cam; break; } } if(playerCamera == null) { return false; } } if (player == null) { player = FindObjectOfType(); return false; // TODO: Temporary solution. We will need to find the exact player for the client player. } if(LootGrid == null) { LootGrid = GetComponentInChildren(); return false; } return true; } public GameObject OpenWindow() { if(InventorySystem.instance.StorageWindowPrefab.GetComponent() != null) return Instantiate(InventorySystem.instance.StorageWindowPrefab, inventoryUI.transform); else return null; } // Update is called once per frame void Update() { if(DraggedItem == null) { IsDraggingItem = false; } CheckForRequiredComponents(); rotationKeyInput.Update(); menuKeyInput.Update(); interactKeyInput.Update(); dropItemKeyInput.Update(); equipItemKeyInput.Update(); //else { DetectObject(); } if (menuKeyInput.PressedKey(true)) { if(InventoryMenuUI != null) { ToggleInventory(); } } hoverResults = GetPointerOverUIObject(); } private List GetPointerOverUIObject() { PointerEventData eventDataCurrentPosition = new PointerEventData(EventSystem.current); eventDataCurrentPosition.position = new Vector2(Input.mousePosition.x, Input.mousePosition.y); List results = new List(); EventSystem.current.RaycastAll(eventDataCurrentPosition, results); List rGOs = new List(); for (int i = 0; i < results.Count; i++) { if (results[i].gameObject.layer == LayerMask.GetMask("Ignore Raycast")) { results.RemoveAt(i); i--; } else// if (results[i].gameObject.layer == LayerMask.GetMask("UI")) { rGOs.Add(results[i].gameObject); } } return rGOs; } public static bool IsMouseOverUI(GameObject go) { if(hoverResults == null || hoverResults.Count == 0) { return false; } return hoverResults.Contains(go); } public static bool IsMouseOverItemUITop(ItemUI item) { if (hoverResults == null || hoverResults.Count == 0) { return false; } foreach(GameObject go in hoverResults) { if(go.GetComponent() != null) { if (go.GetComponent() == item) return true; else return false; } } return false; } private void FixedUpdate() { Vector2 movePos; RectTransformUtility.ScreenPointToLocalPointInRectangle(inventoryUI.transform as RectTransform, Input.mousePosition, inventoryUI.worldCamera, out movePos); MouseInCanvasPosition = InventorySystem.instance.inventoryUI.transform.TransformPoint(movePos); } public void ToggleInventory() { if (InventoryMenuUI.activeInHierarchy) { if(DraggedItem != null) { Debug.Log("Return to Slot"); DraggedItem.ReturnToSlot(true); } CloseInventory(); } else { OpenInventory(); } } public void OpenInventory() { InventoryMenuUI.SetActive(true); Cursor.visible = true; Cursor.lockState = CursorLockMode.Confined; toolbarEvents.GetComponent().MoveToTarget(false, true); if(GetComponent() != null) { GetComponent().pauseMenu.PopEscapeHandlerShow(); } } public void CloseInventory() { toolbarEvents.GetComponent().MoveToSource(true, false); Cursor.lockState = CursorLockMode.Locked; Cursor.visible = false; InventoryMenuUI.SetActive(false); if (GetComponent() != null) { GetComponent().pauseMenu.PushEscapeHandlerShow(); } } public void OpenContextMenu(MonoBehaviour uiContext) { if (uiContext.GetType() == typeof(ItemUI)) { ContextMenuUI.OpenContextMenu((ItemUI)uiContext); } } private void DetectObject() { CurrentState = States.Undefined; if (playerCamera == null) { //Debug.Log("Uh OHHH. No camera detected"); } else { //Debug.Log("Detecting..."); } //Ray ray = playerCamera.ScreenPointToRay(new Vector2(Screen.width / 2, Screen.height / 2));//controls.Mouse.Position.ReadValue()); Vector3 direction = Vector3.forward; Ray ray = new Ray(playerCamera.transform.position, playerCamera.transform.TransformDirection(direction * 200)); Debug.DrawRay(playerCamera.transform.position, playerCamera.transform.TransformDirection(direction * 200), Color.red); //RaycastHit hit; /*if (Physics.Raycast(ray, out hit)) { // for single hits, i guess... }*/ RaycastHit[] hits = Physics.RaycastAll(ray, 200); for(int i = 0; i < hits.Length; i++) { if (hits[i].collider != null) { //Debug.Log("3D Hit All: " + hits[i].collider.gameObject.name); } if (hits[i].collider != null)// && hit.transform.parent.parent.parent != null) { PickUp response_pickUp = hits[i].collider.transform.GetComponent(); if (response_pickUp && !InventorySystem.instance.IsUsingInventoryMenuUI()) { //Debug.Log("Detecting"); CurrentState = States.PickUp; DisplayMessageSystem.instance.NameOfItemInWorldWithFocus = response_pickUp.itemUI.itemName; //Debug.Log("IS: Detecting PickUp"); if (interactKeyInput.PressedKey(true)) { //Debug.Log("IS: Pickup handle"); player.HandlePickup(response_pickUp); } return; } else { CurrentState = States.Undefined; } //Debug.Log("3D Hit: " + hit.collider.transform.name); // TODO: functionality here Inventory response_inventory = hits[i].collider.transform.GetComponent(); if(response_inventory != null && response_inventory == player.inventory) { continue; // ignore self } if (response_inventory != null && !InventorySystem.instance.IsUsingInventoryMenuUI()) { //Debug.Log("Opening Inventory"); if (interactKeyInput.PressedKey(true)) { OpenInventory(); LootMenuUI.SetActive(true); StatsMenuUI.SetActive(false); Loot(response_inventory); CurrentState = States.Undefined; } else if (!InventorySystem.instance.IsUsingInventoryMenuUI()) { CurrentState = States.Loot; } } else if(!InventorySystem.instance.IsUsingInventoryMenuUI()) { CurrentState = States.Undefined; LootMenuUI.SetActive(false); StatsMenuUI.SetActive(true); } } } /*RaycastHit[] hitsNonAlloc = new RaycastHit[1]; int numberOfHits = Physics.RaycastNonAlloc(ray, hitsNonAlloc); for (int i = 0; i < numberOfHits; i++) { if (hitsNonAlloc[i].collider != null) { Debug.Log("3D Hit Non Alloc All: " + hitsNonAlloc[i].collider.tag); } }*/ } public void Loot(Inventory inventory) { if (inventory != null && inventory != player.inventory && !inventory.hasBeenLooted) { inventory.Loot(); //Debug.Log($"Looting: {inventory.itemUIs.Count}"); StartCoroutine(LootGrid.PopulateItemsWithNewInventory(inventory.itemUIs)); } } /// /// This can be called to check if player is interacting with the Inventory System's UI /// /// public bool IsUsingInventoryMenuUI() { return InventoryMenuUI.activeInHierarchy; } public bool PressedKeyRotation(bool singlePress) { return rotationKeyInput.PressedKey(singlePress); } public bool PressedDropItemKey(bool singlePress) { return dropItemKeyInput.PressedKey(singlePress) && Input.GetMouseButton(0); } public bool PressedEquipItemKey(bool singlePress) { return equipItemKeyInput.PressedKey(singlePress) && Input.GetMouseButton(0); } public void OnPointerClick(PointerEventData eventData) { if(eventData.button == PointerEventData.InputButton.Left && !ContextMenuManagerUI.instance.ItemInspectMenuHasFocus() && ContextMenuManagerUI.instance.IsInspectingItem()) { ContextMenuManagerUI.instance.CloseContextMenu(); } } } }