Browse Source

Initial commit

jamesperet 3 years ago
commit
7779a29d05
77 changed files with 2636 additions and 0 deletions
  1. 8 0
      Editor.meta
  2. 69 0
      Editor/ItemBaseEditor.cs
  3. 11 0
      Editor/ItemBaseEditor.cs.meta
  4. 74 0
      ItemLibrary.prefab
  5. 9 0
      ItemLibrary.prefab.meta
  6. 8 0
      Runtime.meta
  7. 8 0
      Runtime/Commands.meta
  8. 24 0
      Runtime/Commands/CloseItemContainerCommand.cs
  9. 11 0
      Runtime/Commands/CloseItemContainerCommand.cs.meta
  10. 27 0
      Runtime/Commands/OpenItemContainerCommand.cs
  11. 11 0
      Runtime/Commands/OpenItemContainerCommand.cs.meta
  12. 8 0
      Runtime/Events.meta
  13. 46 0
      Runtime/Events/GenericEventsInventoryExtensions.cs
  14. 11 0
      Runtime/Events/GenericEventsInventoryExtensions.cs.meta
  15. 91 0
      Runtime/Events/ItemContainerEvents.cs
  16. 11 0
      Runtime/Events/ItemContainerEvents.cs.meta
  17. 51 0
      Runtime/Events/RequestEventsInventoryExtensions.cs
  18. 11 0
      Runtime/Events/RequestEventsInventoryExtensions.cs.meta
  19. 34 0
      Runtime/InventoryModule.cs
  20. 11 0
      Runtime/InventoryModule.cs.meta
  21. 91 0
      Runtime/ItemConsoleCommands.cs
  22. 11 0
      Runtime/ItemConsoleCommands.cs.meta
  23. 147 0
      Runtime/ItemContainer.cs
  24. 11 0
      Runtime/ItemContainer.cs.meta
  25. 163 0
      Runtime/ItemContainerUi.cs
  26. 11 0
      Runtime/ItemContainerUi.cs.meta
  27. 51 0
      Runtime/ItemLibrary.cs
  28. 11 0
      Runtime/ItemLibrary.cs.meta
  29. 22 0
      Runtime/KairoEngine.Inventory.asmdef
  30. 7 0
      Runtime/KairoEngine.Inventory.asmdef.meta
  31. 68 0
      Runtime/RandomItems.cs
  32. 11 0
      Runtime/RandomItems.cs.meta
  33. 8 0
      Runtime/Templates.meta
  34. 52 0
      Runtime/Templates/ItemBase.cs
  35. 11 0
      Runtime/Templates/ItemBase.cs.meta
  36. 32 0
      Runtime/Templates/ItemBaseAmmo.cs
  37. 11 0
      Runtime/Templates/ItemBaseAmmo.cs.meta
  38. 79 0
      Runtime/Templates/ItemBaseFirearm.cs
  39. 11 0
      Runtime/Templates/ItemBaseFirearm.cs.meta
  40. 32 0
      Runtime/Templates/ItemBaseHandWeapon.cs
  41. 11 0
      Runtime/Templates/ItemBaseHandWeapon.cs.meta
  42. 40 0
      Runtime/Templates/ItemBaseHoldable.cs
  43. 11 0
      Runtime/Templates/ItemBaseHoldable.cs.meta
  44. 22 0
      Runtime/Templates/ItemBaseInstantEffect.cs
  45. 11 0
      Runtime/Templates/ItemBaseInstantEffect.cs.meta
  46. 39 0
      Runtime/Templates/ItemRef.cs
  47. 11 0
      Runtime/Templates/ItemRef.cs.meta
  48. 65 0
      Runtime/TransferItems.cs
  49. 11 0
      Runtime/TransferItems.cs.meta
  50. 8 0
      Runtime/UI.meta
  51. 202 0
      Runtime/UI/BarterControllerUi.cs
  52. 11 0
      Runtime/UI/BarterControllerUi.cs.meta
  53. 94 0
      Runtime/UI/CurrentAmmoUi.cs
  54. 11 0
      Runtime/UI/CurrentAmmoUi.cs.meta
  55. 129 0
      Runtime/UI/DragAndDropItem.cs
  56. 11 0
      Runtime/UI/DragAndDropItem.cs.meta
  57. 35 0
      Runtime/UI/FollowWorldPosition.cs
  58. 11 0
      Runtime/UI/FollowWorldPosition.cs.meta
  59. 230 0
      Runtime/UI/ItemSlotUi.cs
  60. 11 0
      Runtime/UI/ItemSlotUi.cs.meta
  61. 20 0
      Runtime/WorldItem.cs
  62. 11 0
      Runtime/WorldItem.cs.meta
  63. 18 0
      Runtime/WorldItemContainer.cs
  64. 11 0
      Runtime/WorldItemContainer.cs.meta
  65. 8 0
      Tests.meta
  66. 8 0
      Tests/Editor.meta
  67. 57 0
      Tests/Editor/GenericEventsInventoryExtensionTests.cs
  68. 11 0
      Tests/Editor/GenericEventsInventoryExtensionTests.cs.meta
  69. 22 0
      Tests/Editor/KairoEngine.Inventory.EditorTests.asmdef
  70. 7 0
      Tests/Editor/KairoEngine.Inventory.EditorTests.asmdef.meta
  71. 61 0
      Tests/Editor/RequestEventsInventoryExtensionTests.cs
  72. 11 0
      Tests/Editor/RequestEventsInventoryExtensionTests.cs.meta
  73. 8 0
      Tests/Runtime.meta
  74. 20 0
      package.json
  75. 7 0
      package.json.meta
  76. 1 0
      readme.md
  77. 7 0
      readme.md.meta

+ 8 - 0
Editor.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 3d95a7dc85fe07446a67de6aa427025b
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 69 - 0
Editor/ItemBaseEditor.cs

@@ -0,0 +1,69 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEditor;
+using Sirenix.OdinInspector.Editor;
+using KairoEngine.Inventory;
+
+namespace KairoEngine.Inventory.Editor
+{
+    [CustomEditor(typeof(ItemBase))]
+    public class ItemBaseEditor : OdinEditor
+    {
+        public override Texture2D RenderStaticPreview(string assetPath, Object[] subAssets, int width, int height)
+        {
+            ItemBase item = (ItemBase)target;
+            Texture2D texture = new Texture2D(width, height);
+            EditorUtility.CopySerialized(item.icon.texture, texture);
+            return texture;
+        }
+    }
+
+    [CustomEditor(typeof(ItemBaseFirearm))]
+    public class ItemBaseFirearmEditor : OdinEditor
+    {
+        public override Texture2D RenderStaticPreview(string assetPath, Object[] subAssets, int width, int height)
+        {
+            ItemBase item = (ItemBase)target;
+            Texture2D texture = new Texture2D(width, height);
+            EditorUtility.CopySerialized(item.icon.texture, texture);
+            return texture;
+        }
+    }
+
+    [CustomEditor(typeof(ItemBaseAmmo))]
+    public class ItemBaseAmmoEditor : OdinEditor
+    {
+        public override Texture2D RenderStaticPreview(string assetPath, Object[] subAssets, int width, int height)
+        {
+            ItemBase item = (ItemBase)target;
+            Texture2D texture = new Texture2D(width, height);
+            EditorUtility.CopySerialized(item.icon.texture, texture);
+            return texture;
+        }
+    }
+
+    [CustomEditor(typeof(ItemBaseInstantEffect))]
+    public class ItemBaseInstantEffectEditor : OdinEditor
+    {
+        public override Texture2D RenderStaticPreview(string assetPath, Object[] subAssets, int width, int height)
+        {
+            ItemBase item = (ItemBase)target;
+            Texture2D texture = new Texture2D(width, height);
+            EditorUtility.CopySerialized(item.icon.texture, texture);
+            return texture;
+        }
+    }
+
+    [CustomEditor(typeof(ItemBaseHandWeapon))]
+    public class ItemBaseHandWeaponEditor : OdinEditor
+    {
+        public override Texture2D RenderStaticPreview(string assetPath, Object[] subAssets, int width, int height)
+        {
+            ItemBase item = (ItemBase)target;
+            Texture2D texture = new Texture2D(width, height);
+            EditorUtility.CopySerialized(item.icon.texture, texture);
+            return texture;
+        }
+    }
+}

+ 11 - 0
Editor/ItemBaseEditor.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 57508a73590b07f47a4474a96a629f86
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 74 - 0
ItemLibrary.prefab

@@ -0,0 +1,74 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &1723067626578023085
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1723067626578023086}
+  - component: {fileID: 1723067626578023087}
+  m_Layer: 0
+  m_Name: ItemLibrary
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1723067626578023086
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1723067626578023085}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 0}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &1723067626578023087
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1723067626578023085}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: a401e475bc9dbdf4ebfce375cc9e2784, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  itemsDataPath: Assets/Data/MaxRaider/Items/
+  availableItems:
+  - {fileID: 11400000, guid: 84be1e333f1ae3a48849f2665f4c5faa, type: 2}
+  - {fileID: 11400000, guid: 9d88ef351d906cf4a8890acf6bd3fe67, type: 2}
+  - {fileID: 11400000, guid: 3c196a446eb7a094eab48e5a75bddafa, type: 2}
+  - {fileID: 11400000, guid: f18f3044b4d5cb943b18832e95ed4c67, type: 2}
+  - {fileID: 11400000, guid: 217a31c8f286bcc4e8a0c9cadc0326df, type: 2}
+  - {fileID: 11400000, guid: d0c6a57451f95d0458de5cbd9bf2c5b8, type: 2}
+  - {fileID: 11400000, guid: d2f3dd764985dfe4abf4369f890cceb6, type: 2}
+  - {fileID: 11400000, guid: a693eb9089efa2544b109f653cfd7ed2, type: 2}
+  - {fileID: 11400000, guid: e2fbfa006f1e8a940bb86407ad0451c9, type: 2}
+  - {fileID: 11400000, guid: bf916d93e18673c4fbf9d18c7f67f44a, type: 2}
+  - {fileID: 11400000, guid: 05da7dc91548ad8468ea16bc9cdba164, type: 2}
+  - {fileID: 11400000, guid: cf5ecbce4cc9cef479f8a5643783f622, type: 2}
+  - {fileID: 11400000, guid: f36ef635d1921764580ee0aebb22fbef, type: 2}
+  - {fileID: 11400000, guid: 03281fbb8ad15644eadd2fae76150df4, type: 2}
+  - {fileID: 11400000, guid: fafaa0e41f89a1142acbcaf829b70e0f, type: 2}
+  - {fileID: 11400000, guid: 0ce8c4352c64b404fa453b5691545ad7, type: 2}
+  - {fileID: 11400000, guid: 1ce6e6fd509f3f741bf8eb35f12aa8f1, type: 2}
+  - {fileID: 11400000, guid: f498b55af5267164fabbe3d979833f4b, type: 2}
+  - {fileID: 11400000, guid: 00f01e14d3977d547a863bb9ba87168a, type: 2}
+  - {fileID: 11400000, guid: acc36c9187d46a248894040538fe1b14, type: 2}
+  - {fileID: 11400000, guid: c6fdced921b4e18498159e9dea599918, type: 2}
+  - {fileID: 11400000, guid: dd030f04beb2d6643a6913ff701537a2, type: 2}
+  - {fileID: 11400000, guid: a0e9b5bb9a51b6f418e9c3c98ea099a8, type: 2}
+  - {fileID: 11400000, guid: bcf83a9dd325ce046af88994677081b4, type: 2}
+  - {fileID: 11400000, guid: 7e722ec17764bac49a184ee518f914a6, type: 2}
+  - {fileID: 11400000, guid: 79f4c7b41df25424296089850dcf594f, type: 2}
+  - {fileID: 11400000, guid: 9e2a9a345583a204195cc28b607671f2, type: 2}

+ 9 - 0
ItemLibrary.prefab.meta

@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 5dbe8ba08af485c47aa3efeb880c6b9a
+labels:
+- Manager
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 8 - 0
Runtime.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: e5a08ec0b140d054189b80dcc2ffb64c
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 8 - 0
Runtime/Commands.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: d821d239a1d2fe94395a5e612acec4b6
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 24 - 0
Runtime/Commands/CloseItemContainerCommand.cs

@@ -0,0 +1,24 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using KairoEngine.Core;
+
+namespace KairoEngine.Inventory
+{
+    public class CloseItemContainerCommand : ICommand
+    {
+        string title;
+
+        float speed;
+
+        public CloseItemContainerCommand(string title)
+        {
+            this.title = title;
+        }
+
+        public void Execute()
+        {
+            ItemContainerEvents.Close(title);
+        }
+    }
+}

+ 11 - 0
Runtime/Commands/CloseItemContainerCommand.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 3b786ad52a52b61488c574642331ad75
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 27 - 0
Runtime/Commands/OpenItemContainerCommand.cs

@@ -0,0 +1,27 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using KairoEngine.Core;
+
+namespace KairoEngine.Inventory
+{
+    public class OpenItemContainerCommand : ICommand
+    {
+        string title;
+        ItemContainer itemContainer;
+
+        float speed;
+
+        public OpenItemContainerCommand(string title, ItemContainer itemContainer)
+        {
+            this.title = title;
+            this.itemContainer = itemContainer;
+        }
+
+        public void Execute()
+        {
+            //Debug.Log("Opening " + title);
+            ItemContainerEvents.Open(title, itemContainer);
+        }
+    }
+}

+ 11 - 0
Runtime/Commands/OpenItemContainerCommand.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 76440fe767dd45d4182c40855fbd1b09
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 8 - 0
Runtime/Events.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 2e8efde203c007449a93806c100ed5e3
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 46 - 0
Runtime/Events/GenericEventsInventoryExtensions.cs

@@ -0,0 +1,46 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using KairoEngine.Core;
+
+namespace KairoEngine.Inventory
+{
+    public static class GenericEventsInventoryExtensions
+    {
+        public static Dictionary<string, System.Action<ItemContainer, ItemRef>> list = new Dictionary<string, System.Action<ItemContainer, ItemRef>>();
+        
+        public static void StartListening(this GenericEvents e, string title, System.Action<ItemContainer, ItemRef> listener)
+        {
+            System.Action<ItemContainer, ItemRef> action = null;
+            if (list.TryGetValue(title, out action))
+            {
+                action += listener;
+                list[title] = action;
+            }
+            else
+            {
+                action += listener;
+                list.Add(title, action);
+            }
+        }
+
+        public static void StopListening(this GenericEvents e,string title, System.Action<ItemContainer, ItemRef> listener)
+        {
+            System.Action<ItemContainer, ItemRef> action = null;
+            if (list.TryGetValue(title, out action))
+            {
+                action -= listener;
+                list[title] = action;
+            }
+        }
+
+        public static void Trigger(this GenericEvents e, string title, ItemContainer container, ItemRef item)
+        {
+            System.Action<ItemContainer, ItemRef> action = null;
+            if (list.TryGetValue(title, out action))
+            {
+                if(action != null) action(container, item);
+            }
+        }
+    }
+}

+ 11 - 0
Runtime/Events/GenericEventsInventoryExtensions.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 0042bd301148c1a47ab04b654e1cc536
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 91 - 0
Runtime/Events/ItemContainerEvents.cs

@@ -0,0 +1,91 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace KairoEngine.Inventory
+{
+    public class ItemContainerEvents
+    {
+        public static Dictionary<string, System.Action<ItemContainer>> OnOpen = new Dictionary<string, System.Action<ItemContainer>>();
+        public static Dictionary<string, System.Action> OnClose = new Dictionary<string, System.Action>();
+        public static event System.Action<ItemContainer> OnUpdate;
+        
+        public static void Open(string title, ItemContainer itemContainer)
+        {
+            if(OnOpen[title] != null)
+            {
+                //Debug.Log("ItemContainerEvents.OnOpen has been triggered");
+                OnOpen[title](itemContainer);
+            }
+        }
+
+        public static void Close(string title)
+        {
+            if(OnClose[title] != null)
+            {
+                //Debug.Log("ItemContainerEvents.OnOpen has been triggered");
+                OnClose[title]();
+            }
+        }
+
+        public static void Update(ItemContainer itemContainer)
+        {
+            if(OnUpdate != null)
+            {
+                //Debug.Log("ItemContainerEvents.OnOpen has been triggered");
+                OnUpdate(itemContainer);
+            }
+        }
+
+        public static void StartListeningOnOpen(string title, System.Action<ItemContainer> listener)
+        {
+            System.Action<ItemContainer> action = null;
+            if (OnOpen.TryGetValue(title, out action))
+            {
+                action += listener;
+                OnOpen[title] = action;
+            }
+            else
+            {
+                action += listener;
+                OnOpen.Add(title, action);
+            }
+        }
+
+        public static void StartListeningOnClose(string title, System.Action listener)
+        {
+            System.Action action = null;
+            if (OnClose.TryGetValue(title, out action))
+            {
+                action += listener;
+                OnClose[title] = action;
+            }
+            else
+            {
+                action += listener;
+                OnClose.Add(title, action);
+            }
+        }
+
+        public static void StopListeningOnOpen(string title, System.Action<ItemContainer> listener)
+        {
+            System.Action<ItemContainer> action = null;
+            if (OnOpen.TryGetValue(title, out action))
+            {
+                action -= listener;
+                OnOpen[title] = action;
+            }
+        }
+
+        public static void StopListeningOnClose(string title, System.Action listener)
+        {
+            System.Action action = null;
+            if (OnClose.TryGetValue(title, out action))
+            {
+                action -= listener;
+                OnClose[title] = action;
+            }
+        }
+
+    }
+}

+ 11 - 0
Runtime/Events/ItemContainerEvents.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 0c116ba2024012641908d47d753617b6
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 51 - 0
Runtime/Events/RequestEventsInventoryExtensions.cs

@@ -0,0 +1,51 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using KairoEngine.Core;
+
+namespace KairoEngine.Inventory
+{
+    public static class RequestEventsInventoryExtensions
+    {
+        public static Dictionary<string, System.Func<ItemContainer>> list = new Dictionary<string, System.Func<ItemContainer>>();
+
+        public static void Bind(this RequestEvents e, string title, System.Func<ItemContainer> listener)
+        {
+            System.Func<ItemContainer> action = null;
+            if (list.TryGetValue(title, out action) == false)
+            {
+                action += listener;
+                list.Add(title, action);
+            }
+        }
+
+        public static void Unbind(this RequestEvents e, string title, System.Func<ItemContainer> listener)
+        {
+            System.Func<ItemContainer> action = null;
+            if (list.TryGetValue(title, out action))
+            {
+                action -= listener;
+                list.Remove(title);
+            }
+        }
+
+        public static EventResponse<ItemContainer> GetItemContainer(this RequestEvents e, string title)
+        {
+            System.Func<ItemContainer> action = null;
+            EventResponse<ItemContainer> response = new EventResponse<ItemContainer>();
+            if (list.TryGetValue(title, out action))
+            {
+                if(action != null) 
+                {
+                    ItemContainer result = action();
+                    response.value = result;
+                    if(response.value == null) response.status = EventResponseStatus.EmptyResponse;
+                    else response.status = EventResponseStatus.OK;
+                }
+            }
+            else response.status = EventResponseStatus.NotFound;
+            return response;
+        }
+    }
+}
+

+ 11 - 0
Runtime/Events/RequestEventsInventoryExtensions.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 86e4525f2ef79e74c85fdddf09800072
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 34 - 0
Runtime/InventoryModule.cs

@@ -0,0 +1,34 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using KairoEngine.Core;
+using KairoEngine.Core.ModuleSystem;
+
+namespace KairoEngine.Inventory
+{
+    public class InventoryModule : IGameModule
+    {
+        private string _name = "Inventory Module";
+
+        public string name { get =>  _name; set => _name = value; }
+
+        public GameObject itemLibraryPrefab;
+
+
+        public void Load(Transform parent)
+        {
+            GameObject obj = GameObject.Instantiate(itemLibraryPrefab, parent);
+            obj.name = obj.name.Replace("(Clone)", "");
+        }
+
+        public void Reset()
+        {
+            
+        }
+
+        public void Destroy()
+        {
+            
+        }
+    }
+}

+ 11 - 0
Runtime/InventoryModule.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: f807c8c826be5df4b9070fc3e59d4c4e
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 91 - 0
Runtime/ItemConsoleCommands.cs

@@ -0,0 +1,91 @@
+using System.Collections;
+using System.Text;
+using System.Collections.Generic;
+using UnityEngine;
+using Sirenix.OdinInspector;
+using QFSW.QC;
+using QFSW.QC.Utilities;
+using KairoEngine.Core;
+
+namespace KairoEngine.Inventory
+{
+    public class ItemConsoleCommands : MonoBehaviour
+    {
+        private static readonly Pool<StringBuilder> _builderPool = new Pool<StringBuilder>();
+
+        [Command("get-item", "Adds target item to player's inventory")]
+        private static string GetItem(string itemName, int quantity = 1)
+        {
+            QuantumConsole console = QuantumConsole.Instance;
+            Color successColor = Color.green;
+            Color errorColor = Color.red;
+            Color blueColor = Color.blue;
+            if(console != null)
+            {
+                QuantumTheme theme = console.GetTheme();
+                successColor = theme ? theme.SuccessColor : Color.white;
+                errorColor = theme ? theme.ErrorColor : Color.white;
+                blueColor = theme ? theme.CommandLogColor : Color.white;
+            }
+            ItemContainer playerInventory = EventManager.request.GetItemContainer("PlayeritemContainer").value;
+            if(playerInventory == null)
+            {
+                return ColorExtensions.ColorText("Could not find player's inventory", errorColor);
+            }
+            ItemBase item = ItemLibrary.GetItemByName(itemName);
+            if(item == null)
+            {
+                return ColorExtensions.ColorText("Item \"" + itemName + "\" could not be found!", errorColor);
+            }
+            if(item.category == ItemType.firearm)
+            {
+                ItemBaseFirearm firearmItem = (ItemBaseFirearm)item;
+                ItemFirearmRef firearmRef = new ItemFirearmRef(item, 1, firearmItem.ammoType, quantity);
+                playerInventory.AddItem(firearmRef);
+                string msg = "Added ";
+                msg += ColorExtensions.ColorText(itemName, successColor); 
+                msg += " and ";
+                msg += ColorExtensions.ColorText(quantity.ToString() + "x ", blueColor); 
+                msg += ColorExtensions.ColorText(firearmItem.ammoType.title, successColor);
+                msg += " to player inventory.";
+                return msg;
+            }
+            else
+            {
+                ItemRef itemRef = new ItemRef(item, quantity, 1000);
+                playerInventory.AddItem(itemRef);
+                string msg = "Added ";
+                msg += ColorExtensions.ColorText(quantity.ToString() + "x", blueColor);
+                msg += " "; 
+                msg += ColorExtensions.ColorText(itemName, successColor);
+                msg += " to player inventory.";
+                return msg;
+            }
+        }
+
+        [Command("list-items", "List all items available in the ItemLibrary")]
+        private static string ListItems()
+        {
+            QuantumConsole console = QuantumConsole.Instance;
+            Color successColor = Color.green;
+            StringBuilder buffer = _builderPool.GetObject();
+            string result = "";
+            if(console != null)
+            {
+                QuantumTheme theme = console.GetTheme();
+                successColor = theme ? theme.SuccessColor : Color.white;
+            }
+            for (int i = 0; i < ItemLibrary.itemList.Count; i++)
+            {
+                string t = ColorExtensions.ColorText(ItemLibrary.itemList[i].title, successColor);
+                buffer.Append(t);
+                if(i < ItemLibrary.itemList.Count -1) buffer.Append(", ");
+            }
+             result = buffer.ToString();
+            _builderPool.Release(buffer);
+            return result;
+        }
+    }
+}
+
+

+ 11 - 0
Runtime/ItemConsoleCommands.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 6924fa6452dd68e4e8aa92f27ce55d4b
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 147 - 0
Runtime/ItemContainer.cs

@@ -0,0 +1,147 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using Sirenix.OdinInspector;
+
+namespace KairoEngine.Inventory
+{
+    public class ItemContainer : MonoBehaviour
+    {
+        public string title = "";
+
+        [ShowInInspector]
+        public List<ItemRef> inventory = new List<ItemRef>();
+
+        public void Start()
+        {
+            if(title == "") title = gameObject.name + "\'s Items";
+        }
+
+        public List<ItemRef> ItemList 
+        {
+            get
+            {
+                return inventory;
+            }
+        }
+
+        public bool HasItem(string title)
+        {
+            for (int i = 0; i < inventory.Count; i++)
+            {
+                if (inventory[i].item.title == title)
+                {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        public bool HasItem(string title, int quantity)
+        {
+            for (int i = 0; i < inventory.Count; i++)
+            {
+                if (inventory[i].item.title == title && inventory[i].quantity >= quantity)
+                {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        public void AddItem(string title, int quantity)
+        {
+            for (int i = 0; i < inventory.Count; i++)
+            {
+                if(title == inventory[i].item.title)
+                {
+                    if(inventory[i].item.stackble)
+                    {
+                        inventory[i].quantity += quantity;
+                        ItemContainerEvents.Update(this);
+                        return;
+                    }
+                }
+            }
+            ItemBase item = ItemLibrary.GetItemByName(title);
+            ItemRef itemRef = new ItemRef(item, quantity);
+            inventory.Add(itemRef);
+            ItemContainerEvents.Update(this);
+        }
+
+        public void AddItem(ItemRef ItemRef)
+        {
+            for (int i = 0; i < inventory.Count; i++)
+            {
+                if(ItemRef.item.title == inventory[i].item.title)
+                {
+                    if(inventory[i].item.stackble)
+                    {
+                        inventory[i].quantity += ItemRef.quantity;
+                        ItemContainerEvents.Update(this);
+                        return;
+                    }
+                }
+            }
+            inventory.Add(ItemRef);
+            ItemContainerEvents.Update(this);
+        }
+
+        public void RemoveItem(ItemRef itemRef)
+        {
+            if (itemRef == null) return;
+            for (int i = 0; i < inventory.Count; i++)
+            {
+                if(inventory[i].item.title == itemRef.item.title)
+                {
+                    if(inventory[i].quantity <= itemRef.quantity)
+                    {
+                        inventory.RemoveAt(i);
+                    }
+                    else
+                    {
+                        inventory[i].quantity -= itemRef.quantity;
+                    }
+                    ItemContainerEvents.Update(this);
+                    return;
+                }
+            }
+        }
+
+        public ItemRef GetItem(ItemBase item)
+        {
+            ItemRef itemRef = null;
+            for (int i = 0; i < inventory.Count; i++)
+            {
+                if(inventory[i].item.title == item.title)
+                {
+                    itemRef = inventory[i];
+                    return itemRef;
+                }
+            }
+            return itemRef;
+        }
+
+        public void UpdateItem(ItemRef itemRef)
+        {
+            for (int i = 0; i < inventory.Count; i++)
+            {
+                if (inventory[i].item.title == itemRef.item.title)
+                {
+                    if(itemRef.quantity <= 0) inventory.Remove(inventory[i]);
+                    else inventory[i] = itemRef;
+                    ItemContainerEvents.Update(this);
+                    return;
+                }
+            }
+        }
+    
+        void Update()
+        {
+            for (int i = 0; i < inventory.Count; i++)
+            {
+                inventory[i].lastUsed += Time.deltaTime;
+            }
+        }
+    }
+}

+ 11 - 0
Runtime/ItemContainer.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 13b13727fe5f9f742b14ffa4f3e771e3
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 163 - 0
Runtime/ItemContainerUi.cs

@@ -0,0 +1,163 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.UI;
+using UnityEngine.Events;
+using TMPro;
+using Sirenix.OdinInspector;
+using KairoEngine.Core;
+using KairoEngine.UI;
+
+namespace KairoEngine.Inventory
+{
+
+    public class ItemContainerUi : MonoBehaviour
+    {
+        public string containerTitle;
+        public GameObject itemContainerPanel;
+        public GameObject itemSlotPrefab;
+        public bool generateSlots = true;
+        public bool showPrice = false;
+        public bool showValueText = false;
+        public int itemSlotCount = 16;
+        public Transform slotsContainer;
+        public TextMeshProUGUI windowTitleText;
+        public List<ItemSlotUi> itemSlots = new List<ItemSlotUi>();
+        public ItemContainer targetItemContainer;
+
+        void OnEnable()
+        {
+            ItemContainerEvents.StartListeningOnOpen(containerTitle, this.OnOpenWindow);
+            ItemContainerEvents.StartListeningOnClose(containerTitle, this.OnCloseWindow);
+            ItemContainerEvents.OnUpdate += this.OnUpdateItemContainer;
+        }
+
+        void OnDisable()
+        {
+            ItemContainerEvents.StopListeningOnOpen(containerTitle, this.OnOpenWindow);
+            ItemContainerEvents.StopListeningOnClose(containerTitle, this.OnCloseWindow);
+            ItemContainerEvents.OnUpdate -= this.OnUpdateItemContainer;
+        }
+
+        void Start()
+        {
+            InstantiateItemSlots();
+        }
+
+        private void InstantiateItemSlots()
+        {
+            if(generateSlots == false) return;
+            for (int i = 0; i < itemSlotCount; i++)
+            {
+                GameObject itemSlotObj = GameObject.Instantiate(itemSlotPrefab, slotsContainer);
+                ItemSlotUi itemSlotUi = itemSlotObj.GetComponentInChildren<ItemSlotUi>();
+                if(itemSlotUi == null) Debug.LogError("ItemSlotPrefab GameObject is missing a ItemSlotUi component");
+                else 
+                {
+                    itemSlots.Add(itemSlotUi);
+                }
+            }
+        }
+
+        private void ResetItemSlots()
+        {
+            for (int i = 0; i < itemSlots.Count; i++)
+            {
+                string value = "";
+                if(showValueText) value = (i + 1).ToString();
+                itemSlots[i].Setup(null, targetItemContainer, this, true, showPrice, value);
+            }
+        }
+
+        public void SetWindowTitle(string title)
+        {
+            if(windowTitleText == null) 
+            {
+                return;
+            }
+            windowTitleText.text = title;
+
+        }
+
+        private void UpdateItemSlots()
+        {
+            List<ItemRef> itemList = targetItemContainer.ItemList;
+            if(itemList == null)
+            {
+                Debug.LogError("Missing itemList in ItemContainerUI");
+            }
+            for (int i = 0; i < itemList.Count; i++)
+            {
+                ItemSlotUi slot = null;
+                for (int a = 0; a < itemSlots.Count; a++)
+                {
+                    if (itemSlots[a].itemRef == null) 
+                    {
+                        slot =  itemSlots[a];
+                        break;
+                    }
+                }
+                string value = "";
+                if(showValueText) value = (i + 1).ToString();
+                if (slot != null) slot.Setup(itemList[i], targetItemContainer, this, true, showPrice, value);
+                //else Debug.LogError("No available ItemSlotUi was found for item " + itemList[i].item.title);
+            }
+        }
+
+        private void OnOpenWindow(ItemContainer itemContainer)
+        {
+            if(itemContainer == null)
+            {
+                Debug.LogError("EventOpenItemContainer contains no reference to target itemContainer");
+                return;
+            }
+            targetItemContainer = itemContainer;
+            if(!itemContainerPanel.activeSelf)
+            {
+                ResetItemSlots();
+                UpdateItemSlots();
+                SetWindowTitle(itemContainer.title);
+                itemContainerPanel.SetActive(true); 
+                UiAnimator[] animators = itemContainerPanel.GetComponentsInChildren<UiAnimator>(true);
+                for (int i = 0; i < animators.Length; i++)
+                {
+                    animators[i].gameObject.SetActive(true);
+                }
+            }
+        }
+
+        private void OnCloseWindow()
+        {
+            UiAnimator panelAnimator = itemContainerPanel.GetComponent<UiAnimator>();
+            UiAnimator[] animators = itemContainerPanel.GetComponentsInChildren<UiAnimator>();
+            if(animators.Length > 0 || panelAnimator != null)
+            {
+                panelAnimator.Disable();
+                foreach (var animator in animators)
+                {
+                    animator.Disable();
+                }
+            }  
+            else
+            {
+                itemContainerPanel.SetActive(false);
+            }
+        }
+
+        private void OnUpdateItemContainer(ItemContainer itemContainer)
+        {
+            if(itemContainer != targetItemContainer) return;
+            if(itemContainer == targetItemContainer)
+            {
+                ResetItemSlots();
+                UpdateItemSlots();
+            }
+        }
+
+        public void CloseWindow()
+        {
+            ICommand closeInventoryCommand = new CloseItemContainerCommand(containerTitle);
+            CommandInvoker.AddCommand(closeInventoryCommand);
+        }
+    }
+}

+ 11 - 0
Runtime/ItemContainerUi.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: fe1a21212e722b54ca21043e9c603e07
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 51 - 0
Runtime/ItemLibrary.cs

@@ -0,0 +1,51 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using Sirenix.OdinInspector;
+using UnityEditor;
+
+namespace KairoEngine.Inventory
+{
+    public class ItemLibrary : MonoBehaviour
+    {
+        
+        public string itemsDataPath = "/Assets/Data/MaxRaider/Items";
+
+        
+        public static List<ItemBase> itemList;
+        public List<ItemBase> availableItems;
+
+        void Awake()
+        {
+            ItemLibrary.itemList = availableItems;
+        }
+
+        public static ItemBase GetItemByName(string title)
+        {
+            for (int i = 0; i < itemList.Count; i++)
+            {
+                if(itemList[i].title == title)
+                {
+                    return itemList[i];
+                }
+            }
+            return null;
+        }
+
+        // [HorizontalGroup("Import Items from Path")]
+        // [Button]
+        // private void ImportLibrary()
+        // {
+        //     Debug.Log("Importing items from " + itemsDataPath);
+        //     itemList = new List<ItemBase>();
+        //     Object[] objectArray = AssetDatabase.LoadAllAssetsAtPath(itemsDataPath);
+        //     Debug.Log("Found " + objectArray.Length + " files.");
+        //     foreach (var obj in objectArray)
+        //     {
+        //         Debug.Log(obj);
+        //         itemList.Add((ItemBase)obj);
+        //     }
+        //     Debug.Log("Imported " + itemList.Count + " items to the library.");
+        // }
+    }
+}

+ 11 - 0
Runtime/ItemLibrary.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a401e475bc9dbdf4ebfce375cc9e2784
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 22 - 0
Runtime/KairoEngine.Inventory.asmdef

@@ -0,0 +1,22 @@
+{
+    "name": "KairoEngine.Inventory",
+    "references": [
+        "GUID:7e5ae6a38d1532248b4c890eca668b06",
+        "GUID:bc1ac81aedfa56c4083dea5332ed3810",
+        "GUID:d5cdde771ecbd5e47bdbe207903a3b3c",
+        "GUID:fb24642277b1db2449da7ac148ce939d",
+        "GUID:adb0f400804b2c0448682f502e5eb39b",
+        "GUID:6055be8ebefd69e48b49212b09b47b2f",
+        "GUID:142285d3db5e7e849b02ea3a75bc2de7",
+        "GUID:5f03fc37b95cb644599751ca563336b2"
+    ],
+    "includePlatforms": [],
+    "excludePlatforms": [],
+    "allowUnsafeCode": false,
+    "overrideReferences": false,
+    "precompiledReferences": [],
+    "autoReferenced": true,
+    "defineConstraints": [],
+    "versionDefines": [],
+    "noEngineReferences": false
+}

+ 7 - 0
Runtime/KairoEngine.Inventory.asmdef.meta

@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 9fde6e799ff66bc4fadf2ce1ed9e1bb2
+AssemblyDefinitionImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 68 - 0
Runtime/RandomItems.cs

@@ -0,0 +1,68 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using Sirenix.OdinInspector;
+
+namespace KairoEngine.Inventory
+{
+    [System.Serializable]
+    public class RandomItem
+    {
+        public ItemBase item;
+        [Range(0, 100f)] public float probability = 5f;
+
+        [MinMaxSlider(0, 1000, true)] public Vector2 quantity = new Vector2(1, 1);
+    }
+
+    public class RandomItems : MonoBehaviour
+    {
+        public bool addItems = true;
+        [ShowInInspector] public ItemContainer itemContainer;
+        public List<RandomItem> items = new List<RandomItem>();
+
+        void Start()
+        {
+            if(itemContainer == null)
+            {
+                Debug.LogError("Missing item container in RandomItems for " + gameObject.name);
+                return;
+            }
+            if(addItems) 
+            {
+                addItems = false;
+                AddRandomItems();
+            }
+        }
+
+        private void AddRandomItems()
+        {
+            foreach (var item in items)
+            {
+                float dice = Random.Range(0, 101);
+                if(dice <= item.probability)
+                {
+                    AddItem(item);
+                }
+            }
+        }
+
+        private void AddItem(RandomItem item)
+        {
+            int quantity = Random.Range((int)item.quantity.x, ((int)item.quantity.y) + 1);
+            if(item.item.category == ItemType.firearm)
+            {
+                ItemBaseFirearm itemBaseFirearm = (ItemBaseFirearm)item.item;
+                ItemBaseAmmo itemBaseAmmo = (ItemBaseAmmo)itemBaseFirearm.ammoType;
+                int ammo = itemBaseFirearm.ammoCapacity;
+                ItemFirearmRef firearmRef = new ItemFirearmRef(itemBaseFirearm, quantity, itemBaseAmmo, ammo);
+                itemContainer.AddItem(firearmRef);
+            }
+            else
+            {
+                ItemRef itemRef = new ItemRef(item.item, quantity, 10000);
+                itemContainer.AddItem(itemRef);
+            }
+        }
+    }
+
+}

+ 11 - 0
Runtime/RandomItems.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 4e511c98fcf82224e80e988b2f793229
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 8 - 0
Runtime/Templates.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 5b3a3018620a42a4b9cc418e7d09506d
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 52 - 0
Runtime/Templates/ItemBase.cs

@@ -0,0 +1,52 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using Sirenix.OdinInspector;
+
+namespace KairoEngine.Inventory
+{
+    public enum ItemType
+    {
+        simple,
+        firearm,
+        ammo,
+        instantEffect,
+        handWeapon
+    }
+
+    [CreateAssetMenu(fileName = "Item", menuName = "KairoEngine/Items/Item", order = 1)]
+    public class ItemBase : ScriptableObject
+    {
+        [Space]
+        [VerticalGroup("row1/left")]
+        public ItemType category;
+        
+        [VerticalGroup("row1/left")]
+        public string title;
+        
+        [VerticalGroup("row1/left")]
+        public int value = 0;
+
+        [VerticalGroup("row1/left")]
+        public bool stackble = false;
+        
+        [VerticalGroup("row1/left")]
+        public GameObject prefab;
+
+        [VerticalGroup("row1/left")]
+        public Sprite icon;
+
+        [Space]
+        [HideLabel]
+        [PreviewField(100, ObjectFieldAlignment.Right)]
+        [HorizontalGroup("row1", 110), VerticalGroup("row1/right")]
+        public Sprite image;
+
+        [TextArea(4, 5)]
+        public string description;
+        
+        
+
+        
+    }
+}

+ 11 - 0
Runtime/Templates/ItemBase.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: fb55ac8301a5c6449b8bf1498d816d8c
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 32 - 0
Runtime/Templates/ItemBaseAmmo.cs

@@ -0,0 +1,32 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using Sirenix.OdinInspector;
+
+namespace KairoEngine.Inventory
+{
+    [CreateAssetMenu(fileName = "Item", menuName = "KairoEngine/Items/Ammo Item", order = 4)]
+    public class ItemBaseAmmo : ItemBase
+    {
+        [TitleGroup("Ammo Properties")]
+        public GameObject bulletPrefab;
+
+        [TitleGroup("Ammo Properties")]
+        public string bulletPrefabPool = "";
+
+        [TitleGroup("Ammo Properties")]
+        public float bulletSpeed = 10f;
+
+        [TitleGroup("Ammo Properties")]
+        public float accuracyModifier = 0;
+
+        [TitleGroup("Ammo Properties")]
+        public int damageModifier = 0;
+
+        [TitleGroup("Ammo Properties")]
+        public int armorPiercing = 0;
+
+        [TitleGroup("Ammo Properties")]
+        public float lifetime = 3f;
+    }
+}

+ 11 - 0
Runtime/Templates/ItemBaseAmmo.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 97fa33f3bbba95747a3c3822b864d3c3
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 79 - 0
Runtime/Templates/ItemBaseFirearm.cs

@@ -0,0 +1,79 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using Sirenix.OdinInspector;
+using KairoEngine.SFX;
+
+namespace KairoEngine.Inventory
+{
+    public enum FirearmType
+    {
+        pistol,
+        rifle
+    }
+
+    [CreateAssetMenu(fileName = "Item", menuName = "KairoEngine/Items/Weapon Item", order = 3)]
+    public class ItemBaseFirearm : ItemBaseHoldable
+    {
+        [TitleGroup("Weapon Properties")]
+        public FirearmType firearmType;
+
+        [TitleGroup("Weapon Properties")]
+        public int damage = 1;
+        
+        [TitleGroup("Weapon Properties")]
+        public bool isAutomatic = false;
+
+        [TitleGroup("Weapon Properties"), SuffixLabel("shoots", true)]
+        public int burstFire = 1;
+        
+        [TitleGroup("Weapon Properties"), SuffixLabel("seconds", true)]
+        public float firingSpeed = 0.2f;
+        
+        [TitleGroup("Weapon Properties"), SuffixLabel("seconds", true)]
+        public float reloadSpeed = 1.5f;
+        
+        [TitleGroup("Weapon Properties")]
+        public ItemBaseAmmo ammoType;
+        
+        [TitleGroup("Weapon Properties")]
+        public int ammoCapacity;
+
+        [TitleGroup("Weapon Properties")]
+        public int bulletsPerShot = 1;
+
+        [Tooltip("Weapon acuracy ranges perfect accuracy at 100% to total random directions at 0%")]
+        [TitleGroup("Weapon Properties"), Range(0, 100), SuffixLabel("%", true)]
+        public float accuracy;
+
+        [Tooltip("Adds this percentage to the critial hit chance at each bullet hit")]
+        [TitleGroup("Weapon Properties"), Range(0, 100), SuffixLabel("%", true)]
+        public float criticalChanceModifier = 3f;
+
+        [TitleGroup("Weapon Properties"), LabelText("Point Blank distance"), SuffixLabel("min", true)] 
+        public float minDistance = 1.5f;
+        [TitleGroup("Weapon Properties"), LabelText("Half distance"), SuffixLabel("half", true)] 
+        public float halfDistance = 12f;
+        [TitleGroup("Weapon Properties"), LabelText("Max travel distance"), SuffixLabel("max", true)] 
+        public float maxDistance = 20f;
+
+        [TitleGroup("Weapon Properties")]
+        public SFXClip shootSound;
+
+        [TitleGroup("Weapon Properties")]
+        public SFXClip emptySound;
+        
+        [TitleGroup("Weapon Properties")]
+        public SFXClip reloadSound;
+
+        public int CalculateDamage(ItemFirearmRef firearmRef, Vector3 firearmPos, Vector3 impactPos)
+        {
+            return damage + ammoType.damageModifier;
+        }
+
+        public float Accuracy(ItemFirearmRef firearmRef)
+        {
+            return accuracy + ammoType.accuracyModifier;
+        }
+    }
+}

+ 11 - 0
Runtime/Templates/ItemBaseFirearm.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 577ad9650a5b20a4b835d81688834f2a
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 32 - 0
Runtime/Templates/ItemBaseHandWeapon.cs

@@ -0,0 +1,32 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using Sirenix.OdinInspector;
+using KairoEngine.SFX;
+
+namespace KairoEngine.Inventory
+{
+    public enum HandWeaponType
+    {
+        Club,
+        Sword,
+        Knife,
+        Spear
+    }
+
+    [CreateAssetMenu(fileName = "Item", menuName = "KairoEngine/Items/Hand Weapon Item", order = 4)]
+    public class ItemBaseHandWeapon : ItemBaseHoldable
+    {
+        [TitleGroup("Hand Weapon Properties")]
+        public HandWeaponType weaponType;
+
+        [TitleGroup("Hand Weapon Properties")]
+        public int damage = 1;
+
+        [TitleGroup("Hand Weapon Properties")]
+        public float attackDistance = 1.3f;
+
+        [TitleGroup("Weapon Properties")]
+        public SFXClip attackSound;
+    }
+}

+ 11 - 0
Runtime/Templates/ItemBaseHandWeapon.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 1c4dd9c1ee8bb98409895445694ca8d3
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 40 - 0
Runtime/Templates/ItemBaseHoldable.cs

@@ -0,0 +1,40 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using Sirenix.OdinInspector;
+
+namespace KairoEngine.Inventory
+{
+    [CreateAssetMenu(fileName = "Holdable Item", menuName = "KairoEngine/Items/Holdable Item", order = 2)]
+    public class ItemBaseHoldable : ItemBase
+    {
+        [TitleGroup("Holdable Item Properties")]
+        public GameObject holdablePrefab;
+
+        [TitleGroup("Holdable Item Properties")]
+        public int animationMode = 1;
+
+        [TitleGroup("Holdable Item Properties")]
+        public Vector3 handPosition;
+
+        [TitleGroup("Holdable Item Properties")]
+        public Vector3 handRotation;
+
+        [TitleGroup("Holdable Item Properties")]
+        public Vector3 handScale;
+
+        public void Setup(GameObject obj, float scaleMultiplier)
+        {
+            obj.transform.localPosition = handPosition * scaleMultiplier;
+            obj.transform.localRotation = Quaternion.Euler(handRotation.x, handRotation.y, handRotation.z);
+            obj.transform.localScale = handScale * scaleMultiplier;
+            Rigidbody rigidBody = obj.GetComponent<Rigidbody>();
+            if (rigidBody != null)
+                GameObject.Destroy(rigidBody);
+            BoxCollider boxCollider = obj.GetComponent<BoxCollider>();
+            if (boxCollider != null)
+                GameObject.Destroy(boxCollider);
+        }
+    }
+
+}

+ 11 - 0
Runtime/Templates/ItemBaseHoldable.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 04064e7d5665cab4c908f026e400c4f6
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 22 - 0
Runtime/Templates/ItemBaseInstantEffect.cs

@@ -0,0 +1,22 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using Sirenix.OdinInspector;
+using KairoEngine.Stats;
+
+namespace KairoEngine.Inventory
+{
+    [CreateAssetMenu(fileName = "Item", menuName = "KairoEngine/Items/Intant Effect Item", order = 5)]
+    public class ItemBaseInstantEffect : ItemBase
+    {
+        //[TitleGroup("Effect Properties"), ShowInInspector, SerializeReference]
+        //public IStatusEffect statusEffect;
+
+        [TitleGroup("Effect Properties")]
+        public bool hasCooldown = true;
+
+        [TitleGroup("Effect Properties"), ShowIf("@hasCooldown")]
+        public float cooldownTime = 5f;
+
+    }
+}

+ 11 - 0
Runtime/Templates/ItemBaseInstantEffect.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 87f3fff3a675c554a875430a7e4a1cbf
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 39 - 0
Runtime/Templates/ItemRef.cs

@@ -0,0 +1,39 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using KairoEngine.Core;
+using Sirenix.OdinInspector;
+
+namespace KairoEngine.Inventory
+{
+    [System.Serializable]
+    public class ItemRef 
+    {
+        [HorizontalGroup("itemRef", 0.85f), HideLabel] public ItemBase item;
+        [HorizontalGroup("itemRef", 0.15f), HideLabel] public int quantity = 1;
+        [HideInInspector] public float lastUsed = 10000f;
+
+        public ItemRef(ItemBase item, int quantity, float lastUsed = 0f)
+        {
+            this.item = item;
+            this.quantity = quantity;
+            this.lastUsed = lastUsed;
+        }
+    }
+
+    [System.Serializable]
+    public class ItemFirearmRef : ItemRef
+    {
+        public ItemBaseAmmo ammoType;
+        public int ammo;
+
+        public float accuracyModifier = 0f;
+
+        public ItemFirearmRef(ItemBase item, int quantity, ItemBaseAmmo ammoType, int ammo, float accuracyModifier = 0f, float lastUsed = 0f) : base(item, quantity, lastUsed)
+        {
+            this.ammoType = ammoType;
+            this.ammo = ammo;
+        }
+    }
+
+}

+ 11 - 0
Runtime/Templates/ItemRef.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 1509d13c5b143de4cb96c2f8d5de3333
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 65 - 0
Runtime/TransferItems.cs

@@ -0,0 +1,65 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using Sirenix.OdinInspector;
+
+namespace KairoEngine.Inventory
+{
+    public class TransferItems : MonoBehaviour
+    {
+        public bool onStart = true;
+        public ItemContainer sourceItemContainer;
+        public ItemContainer targetItemContainer;
+
+        public bool transferFirearms = true;
+        public bool transferHandWeapons = true;
+        public bool transferInstantEffect = true;
+        public bool transferAmmo = false;
+        public bool transferMoney = false;
+        
+        void Start()
+        {
+            if(onStart) Transfer();
+        }
+
+        void Update()
+        {
+            if(sourceItemContainer.inventory.Count > 0) Transfer();
+        }
+
+        public void Transfer()
+        {
+            for (int i = 0; i < sourceItemContainer.inventory.Count; i++)
+            {
+                ItemRef itemRef = sourceItemContainer.inventory[i];
+                if(itemRef == null) continue;
+                switch (itemRef.item.category)
+                {
+                    case ItemType.firearm:
+                        if(transferFirearms) TransferItem(itemRef);
+                        break;
+                    case ItemType.handWeapon:
+                        if(transferHandWeapons) TransferItem(itemRef);
+                        break;
+                    case ItemType.instantEffect:
+                        if(transferInstantEffect) TransferItem(itemRef);
+                        break;
+                    case ItemType.ammo:
+                        if(transferAmmo) TransferItem(itemRef);
+                        break;
+                    default:
+                        if(transferMoney && itemRef.item.title == "Old Dollars") TransferItem(itemRef);
+                        break;
+                }
+            }
+            
+        }
+
+        void TransferItem(ItemRef itemRef)
+        {
+            targetItemContainer.AddItem(itemRef);
+            sourceItemContainer.RemoveItem(itemRef);
+        }
+    }
+}
+

+ 11 - 0
Runtime/TransferItems.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: cf02664f6fb35f54da77cf02e3883dab
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 8 - 0
Runtime/UI.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 9d50be7c698f02a4e978ee6e98ebee55
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 202 - 0
Runtime/UI/BarterControllerUi.cs

@@ -0,0 +1,202 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using Sirenix.OdinInspector;
+using KairoEngine.Core;
+using KairoEngine.SFX;
+
+namespace KairoEngine.Inventory
+{
+    public class BarterControllerUi : MonoBehaviour
+    {
+        public static BarterControllerUi instance;
+        public GameObject buyItemSlotPrefab;
+
+        public ItemContainerUi buyUi;
+        public ItemContainerUi sellUi;
+        public float panelDistance = 4.5f;
+
+        public SFXClip sfxAddItem;
+        public SFXClip sfxRemoveItem;
+        public SFXClip sfxConfirm;
+        public SFXClip sfxCancel;
+        public SFXClip sfxNegative;
+        public SFXClip sfxOpenWindow;
+
+        private bool initialized = false;
+        
+        private void Awake()
+        {
+            if(instance == null) instance = this;
+            else Destroy(this);
+        }
+        private void Update ()
+        {
+            if(buyUi.itemContainerPanel.activeSelf && buyUi.targetItemContainer != null)
+            {
+                if(!initialized) Initialize();
+                UpdatePanelPosition(buyUi.targetItemContainer.transform, buyUi.itemContainerPanel.transform);
+                UpdatePurchaseValue();
+                
+            }
+            if(sellUi.itemContainerPanel.activeSelf && sellUi.targetItemContainer != null)
+            {
+                if(!initialized) Initialize();
+                UpdatePanelPosition(sellUi.targetItemContainer.transform, sellUi.itemContainerPanel.transform);
+                UpdateSaleValue();
+            }
+        }
+
+        private void Initialize()
+        {
+            initialized = true;
+            SoundController.EmmitSound(sfxOpenWindow, this.transform.position);
+        }
+
+        private void UpdatePanelPosition(Transform target, Transform targetPanel)
+        {
+            RectTransform panelTransform = targetPanel.GetComponent<RectTransform>();
+            Vector3 pos = new Vector3(target.position.x, target.position.y + panelDistance, target.position.z);
+            Vector3 screenPos = Camera.main.WorldToScreenPoint(pos);
+            if(Vector3.Distance(panelTransform.position, screenPos) > 0.1f)
+            {
+                screenPos.z = 100;
+                panelTransform.position = screenPos;
+            }
+            
+        }
+
+        private void UpdateSaleValue()
+        {
+            int total = 0;
+            foreach (var item in sellUi.targetItemContainer.inventory)
+            {
+                total += item.item.value * item.quantity;
+            }
+            string title = "Sell Items ($" + total + ")";
+            sellUi.SetWindowTitle(title);
+        }
+
+        private void UpdatePurchaseValue()
+        {
+            int total = 0;
+            foreach (var itemSlot in buyUi.itemSlots)
+            {
+                if(itemSlot.itemRef != null)
+                {
+                    total += itemSlot.itemRef.item.value * itemSlot.quantityToBuy;
+                }
+                
+            }
+            string title = "Buy Items ($" + total + ")";
+            buyUi.SetWindowTitle(title);
+        }
+
+        public void ConcludeSale()
+        {
+            int total = 0;
+            foreach (var item in sellUi.targetItemContainer.inventory)
+            {
+                total += item.item.value * item.quantity;
+            }
+            sellUi.targetItemContainer.inventory.Clear();
+            ItemBase itemBase = ItemLibrary.GetItemByName("Old Dollars");
+            ItemRef itemRef = new ItemRef(itemBase, total, 1000);
+            ItemContainer playerInventory = null; //GameController.instance.heroCharacter.GetComponent<ItemContainer>();
+            if(playerInventory == null) 
+            {
+                Debug.LogError("playerInventory is missing!");
+                return;
+            }
+            playerInventory.AddItem(itemRef);
+            ICommand closeItemContainerCommand = new CloseItemContainerCommand(sellUi.targetItemContainer.title);
+            CommandInvoker.AddCommand(closeItemContainerCommand);
+            EmmitEvent(sellUi.targetItemContainer, total);
+            initialized = false;
+            SoundController.EmmitSound(sfxConfirm, this.transform.position);
+        }
+
+        public void ConcludePurchase()
+        {
+            ItemContainer playerInventory = EventManager.request.GetItemContainer("PlayerItemContainer").value;
+            if(playerInventory == null) 
+            {
+                Debug.LogError("playerInventory is missing!");
+                return;
+            }
+            List<ItemRef> tradedItems = new List<ItemRef>();
+            int total = 0;
+            for (int i = 0; i < buyUi.itemSlots.Count; i++)
+            {
+                ItemSlotUi itemSlot = buyUi.itemSlots[i];
+                if(itemSlot.itemRef != null)
+                {
+                    if(itemSlot.quantityToBuy > 0)
+                    {
+                        total += itemSlot.itemRef.item.value * itemSlot.quantityToBuy;
+                        ItemRef playerItemRef = new ItemRef(itemSlot.itemRef.item, itemSlot.quantityToBuy, 1000);
+                        ItemRef currentItemRef = new ItemRef(itemSlot.itemRef.item, itemSlot.quantityToBuy, 1000);
+                        if(currentItemRef.item.category == ItemType.firearm)
+                        {
+                            ItemBaseFirearm firearmItem = (ItemBaseFirearm)playerItemRef.item;
+                            ItemFirearmRef firearmRef = new ItemFirearmRef(firearmItem, playerItemRef.quantity, firearmItem.ammoType, firearmItem.ammoCapacity);
+                            playerItemRef = firearmRef;
+                        }
+                        tradedItems.Add(currentItemRef);
+                        playerInventory.AddItem(playerItemRef);
+                        GenericEvents.Trigger("Player buy item", $"{playerItemRef.item.title}_{playerItemRef.item.value}", playerItemRef.quantity);
+                    }
+                }
+            }
+            ItemBase itemBase = ItemLibrary.GetItemByName("Old Dollars");
+            ItemRef itemRef = new ItemRef(itemBase, total, 1000);
+            playerInventory.RemoveItem(itemRef);
+            foreach (var tradedItem in tradedItems)
+            {
+                buyUi.targetItemContainer.RemoveItem(tradedItem);
+            }
+            ICommand closeItemContainerCommand = new CloseItemContainerCommand(buyUi.containerTitle);
+            CommandInvoker.AddCommand(closeItemContainerCommand);
+            EmmitEvent(buyUi.targetItemContainer, total);
+            initialized = false;
+            SoundController.EmmitSound(sfxConfirm, this.transform.position);
+        }
+
+        public void CancelSale()
+        {
+            ItemContainer playerInventory = null; //GameController.instance.heroCharacter.GetComponent<ItemContainer>();
+            if(playerInventory == null) 
+            {
+                Debug.LogError("playerInventory is missing!");
+                return;
+            }
+            foreach (var item in sellUi.targetItemContainer.inventory)
+            {
+                playerInventory.AddItem(item);
+            }
+            sellUi.targetItemContainer.inventory.Clear();
+            ICommand closeItemContainerCommand = new CloseItemContainerCommand(sellUi.targetItemContainer.title);
+            CommandInvoker.AddCommand(closeItemContainerCommand);
+            EmmitEvent(sellUi.targetItemContainer, 0);
+            initialized = false;
+            SoundController.EmmitSound(sfxCancel, this.transform.position);
+        }
+
+        public void CancelPurchase()
+        {
+            ICommand closeItemContainerCommand = new CloseItemContainerCommand(buyUi.containerTitle);
+            CommandInvoker.AddCommand(closeItemContainerCommand);
+            EmmitEvent(buyUi.targetItemContainer, 0);
+            initialized = false;
+            SoundController.EmmitSound(sfxCancel, this.transform.position);
+        }
+
+        private void EmmitEvent(ItemContainer itemContainer, int value)
+        {
+            CharacterController character = itemContainer.GetComponent<CharacterController>();
+            if(character == null) return;
+            GenericEvents.Trigger("TradeFinished");
+        }
+    }
+}
+

+ 11 - 0
Runtime/UI/BarterControllerUi.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 7571dd55c94ab2c458239ba4fe9bc0d1
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 94 - 0
Runtime/UI/CurrentAmmoUi.cs

@@ -0,0 +1,94 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.SceneManagement;
+using Sirenix.OdinInspector;
+using TMPro;
+using KairoEngine.Core;
+using KairoEngine.UI;
+
+namespace KairoEngine.Inventory
+{
+    // Todo: Refactor Update method to remove CharacterSystem references using the EventSystem for changing values
+    public class CurrentAmmoUi : MonoBehaviour
+    {
+        private ItemContainer playerInventory;
+        private ItemRef currentItem;
+
+        public GameObject canvasParent;
+
+        public ItemSlotUi itemSlotUi;
+
+        public TextMeshProUGUI textAmmo;
+
+        public TextMeshProUGUI textAmmoType;
+
+        private string currentScene;
+
+        void OnEnable()
+        {
+            EventManager.broadcast.StartListening("PlayerShowAmmo", ShowAmmoUI);
+            GenericEvents.StartListening("PlayerHideAmmo", HideAmmoUI);
+            UnityEngine.SceneManagement.SceneManager.sceneLoaded += OnSceneLoaded;
+        }
+
+        void OnDisable()
+        {
+            EventManager.broadcast.StopListening("PlayerShowAmmo", ShowAmmoUI);
+            GenericEvents.StopListening("PlayerHideAmmo", HideAmmoUI);
+            UnityEngine.SceneManagement.SceneManager.sceneLoaded -= OnSceneLoaded;
+        }
+
+        public void Start()
+        {
+            canvasParent.SetActive(false);
+        }
+
+        private void ShowAmmoUI(ItemContainer itemContainer, ItemRef itemRef)
+        {
+            this.playerInventory = itemContainer;
+            this.currentItem = itemRef;
+            if(itemRef.item.category != ItemType.firearm)
+            {
+                canvasParent.SetActive(false);
+                return;
+            }
+            this.canvasParent.SetActive(true);
+            canvasParent.SetActive(true);
+        }
+
+        private void HideAmmoUI()
+        {
+            canvasParent.SetActive(false);
+        }
+
+        private void UpdateAmmo()
+        {
+            int totalAmmo;
+            ItemFirearmRef firearmRef = currentItem as ItemFirearmRef;
+            ItemRef ammoRef = playerInventory.GetItem(firearmRef.ammoType);
+            if(ammoRef == null) 
+            {
+                totalAmmo = 0;
+                ammoRef = new ItemRef(firearmRef.ammoType, 0);
+            }
+            else 
+            {
+                totalAmmo = ammoRef.quantity;
+            }
+            itemSlotUi.Setup(ammoRef, null, null, false);
+            textAmmoType.text = firearmRef.ammoType.title;
+            textAmmo.text = firearmRef.ammo + "/" + totalAmmo;
+        }
+
+        public void Update()
+        {
+            if(canvasParent.activeSelf) UpdateAmmo();
+        }
+
+        private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
+        {
+            currentScene = scene.name;
+        }
+    }
+}

+ 11 - 0
Runtime/UI/CurrentAmmoUi.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: b2472bfb54b45504a9a8445a6c49bd98
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 129 - 0
Runtime/UI/DragAndDropItem.cs

@@ -0,0 +1,129 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.UI;
+using UnityEngine.EventSystems;
+using Sirenix.OdinInspector;
+using KairoEngine.UI;
+
+namespace KairoEngine.Inventory
+{
+    // Todo: Fix droping items on the ground
+
+    public class DragAndDropItem : MonoBehaviour
+    {
+
+        public GraphicRaycaster m_Raycaster;
+        public EventSystem m_EventSystem;
+        
+        public Image itemCursorImage;
+
+        private PointerEventData m_PointerEventData;
+
+        private ItemSlotUi previousItemSlot;
+        private bool isDragging = false;
+        private ItemRef itemRef;
+
+        private List<RaycastResult> results;
+
+        void Update()
+        { 
+            if (MouseInputUIBlocker.BlockedByUI == true)
+            {
+                ItemSlotUi targetItemSlot = RaycastSlot();
+                if(targetItemSlot != null)
+                {
+                    // Start Draging item from itemslot
+                    if(!isDragging && Input.GetButtonDown("Fire1") && targetItemSlot.draggable)
+                    {
+                        isDragging = true;
+                        previousItemSlot = targetItemSlot;
+                        itemRef = previousItemSlot.itemRef;
+                        previousItemSlot.parentItemContainer.RemoveItem(itemRef);
+                        itemCursorImage.sprite = itemRef.item.icon;
+                        itemCursorImage.gameObject.SetActive(true);
+                        itemCursorImage.transform.position = new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0);
+                    }
+                    // Release item on itemslot
+                    if(isDragging && Input.GetButtonUp("Fire1"))
+                    {
+                        isDragging = false;
+                        itemCursorImage.gameObject.SetActive(false);
+                        if(targetItemSlot.itemRef == null && targetItemSlot.dropable)
+                        {
+                            targetItemSlot.parentItemContainer.AddItem(itemRef);
+                        }
+                        else
+                        {
+                            previousItemSlot.parentItemContainer.AddItem(itemRef);
+                        }
+                    }
+                }
+                else
+                {
+                    // Check ui raycast results
+                    if(results.Count > 0)
+                    {
+                        // Item droped somewhere else in the ui
+                        if(isDragging && Input.GetButtonUp("Fire1"))
+                        {
+                            isDragging = false;
+                            previousItemSlot.parentItemContainer.AddItem(itemRef);
+                            itemCursorImage.gameObject.SetActive(false);
+                        }
+                    }
+                    else
+                    {
+                        if(isDragging && Input.GetButtonUp("Fire1"))
+                        {
+                            isDragging = false;
+                            itemCursorImage.gameObject.SetActive(false);
+                            // ! Fix when dependencies are solved
+                            //ICommand dropItemCommand = new DropItemCommand(previousItemSlot.parentItemContainer, itemRef);
+                            //CommandInvoker.AddCommand(dropItemCommand);
+                        }
+                    }
+                    
+                }
+            }
+            // Release item on ground
+            
+            // Update Dragged item's icon image position
+            if (isDragging && Input.GetButton("Fire1"))
+            {
+                itemCursorImage.transform.position = new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0);
+            }
+        }
+
+        private ItemSlotUi RaycastSlot()
+        {
+            //Set up the new Pointer Event
+            m_PointerEventData = new PointerEventData(m_EventSystem);
+            //Set the Pointer Event Position to that of the mouse position
+            m_PointerEventData.position = Input.mousePosition;
+            //Create a list of Raycast Results
+            results = new List<RaycastResult>();
+            //Raycast using the Graphics Raycaster and mouse click position
+            m_Raycaster.Raycast(m_PointerEventData, results);
+            //For every result returned, output the name of the GameObject on the Canvas hit by the Ray
+            foreach (RaycastResult result in results)
+            {
+                ItemSlotUi slot = result.gameObject.GetComponent<ItemSlotUi>();
+                if (slot != null)
+                {
+                    // Hovering a item slot
+                    if(slot.itemRef == null)
+                    {
+                        //Debug.Log("Hovering empty item slot " + slot.name);
+                    }
+                    else
+                    {
+                        //Debug.Log("Hovering item " + slot.itemRef.item.name);
+                    }
+                    return slot;
+                }
+            }
+            return null;
+        }
+    }
+}

+ 11 - 0
Runtime/UI/DragAndDropItem.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 7464cc860a6f42c4681329fa16dba497
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 35 - 0
Runtime/UI/FollowWorldPosition.cs

@@ -0,0 +1,35 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using Sirenix.OdinInspector;
+
+namespace KairoEngine.Inventory
+{
+    public class FollowWorldPosition : MonoBehaviour
+    {
+        public ItemContainerUi ItemContainerUi;
+        public GameObject panel;
+        public float yDistance = 2;
+        private Transform target;
+
+        void OnEnable()
+        {
+            
+        }
+
+        void Update()
+        {
+            if(panel.activeSelf)
+            {
+                target = ItemContainerUi.targetItemContainer.transform;
+                if(target != null)
+                {
+                    Vector3 pos = new Vector3(target.position.x, target.position.y + yDistance, target.position.z);
+                    Vector3 screenPos = Camera.main.WorldToScreenPoint(pos);
+                    screenPos.z = 100;
+                    panel.GetComponent<RectTransform>().position = screenPos;
+                }
+            } 
+        }
+    }
+}

+ 11 - 0
Runtime/UI/FollowWorldPosition.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 059d5be62304ee64190397c2b104cb93
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 230 - 0
Runtime/UI/ItemSlotUi.cs

@@ -0,0 +1,230 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.UI;
+using TMPro;
+using Sirenix.OdinInspector;
+using KairoEngine.SFX;
+using KairoEngine.Core;
+
+namespace KairoEngine.Inventory
+{
+    public class ItemSlotUi : MonoBehaviour
+    {
+        public ItemRef itemRef;
+        public Image iconUi;
+        public TextMeshProUGUI quantityUi;
+        public TextMeshProUGUI priceUi;
+        public TextMeshProUGUI valueUI;
+        public RectTransform curtainTransform;
+        public bool draggable = true;
+        public bool dropable = true;
+        public bool forSaleItem = false;
+
+        [ShowIf("@forSaleItem")] public Sprite buttonNormal;
+        [ShowIf("@forSaleItem")] public Sprite buttonInteraction;
+        [ShowIf("@forSaleItem")] public Sprite buttonNavigation;
+        [ShowIf("@forSaleItem")] public Sprite buttonSelected;
+        [ShowIf("@forSaleItem")] public Sprite buttonDisabled;
+
+        [ShowIf("@forSaleItem")] public Sprite buttonSuccess;
+        [ShowIf("@forSaleItem")] public Sprite buttonSuccessInteraction;
+        [ShowIf("@forSaleItem")] public Sprite buttonSuccessNavigation;
+        [ShowIf("@forSaleItem")] public Sprite buttonSuccessSelected;
+
+        [ReadOnly] public ItemContainer parentItemContainer;
+        [ReadOnly] public ItemContainerUi parentItemContainerUi;
+
+        [ReadOnly] public int quantityToBuy = 0;
+
+        private BarterControllerUi baterCtrl;
+
+        
+
+        public void Setup(ItemRef newItemRef, ItemContainer parentItemContainer, ItemContainerUi itemContainerUi, bool showQuantity = true, bool showPrice = false, string value = "")
+        {
+            this.parentItemContainerUi = itemContainerUi;
+            this.parentItemContainer = parentItemContainer;
+            itemRef = newItemRef;
+            if(forSaleItem) ResetButtonUi();
+            if (itemRef != null)
+            {
+                iconUi.sprite = itemRef.item.icon;
+                iconUi.enabled = true;
+                CooldownCurtain();
+                if (itemRef.item.stackble && showQuantity)
+                {
+                    quantityUi.gameObject.SetActive(true);
+                    quantityUi.enabled = true;
+                    quantityUi.text = itemRef.quantity.ToString();
+                }
+                if(showPrice)
+                {
+                    priceUi.gameObject.SetActive(true);
+                    priceUi.enabled = true;
+                    priceUi.text = "$" + itemRef.item.value.ToString();
+                }
+                if(forSaleItem) 
+                {
+                    quantityToBuy = 0;
+                    SetupBuy();
+                }
+                else gameObject.GetComponent<Button>().interactable = true;
+            } else
+            {
+                iconUi.sprite = null;
+                iconUi.enabled = false;
+                quantityUi.gameObject.SetActive(false);
+                quantityUi.enabled = false;
+                priceUi.gameObject.SetActive(false);
+                priceUi.enabled = false;
+                curtainTransform.sizeDelta = new Vector2 (75, 0);
+            }
+            if(value != "")
+            {
+                valueUI.gameObject.SetActive(true);
+                valueUI.enabled = true;
+                valueUI.text = value;
+            }
+            else
+            {
+                valueUI.gameObject.SetActive(false);
+                valueUI.enabled = false;
+            }
+            baterCtrl = BarterControllerUi.instance;
+        }
+
+        void Update()
+        {
+            CooldownCurtain();
+        }
+
+        private void CooldownCurtain()
+        {
+            if(itemRef != null)
+            {
+                if(itemRef.item == null) return;
+                if(itemRef.item.category == ItemType.instantEffect)
+                {
+                    ItemBaseInstantEffect item = (ItemBaseInstantEffect)itemRef.item;
+                    if(item.hasCooldown)
+                    {
+                        if(itemRef.lastUsed < item.cooldownTime)
+                        {
+                            float height = (75/item.cooldownTime) * (item.cooldownTime - itemRef.lastUsed);
+                            curtainTransform.sizeDelta = new Vector2 (75, height);
+                            return;
+                        }
+                    }
+                }
+            }
+            curtainTransform.sizeDelta = new Vector2 (75, 0);
+        }
+
+        private void SetupBuy()
+        {
+            ItemContainer playerInventory = EventManager.request.GetItemContainer("PlayerItemContainer").value;
+            if(playerInventory == null) Debug.LogError("Missing playerInventory");
+            bool hasMoney = playerInventory.HasItem("Old Dollars", itemRef.item.value);
+            Button button = gameObject.GetComponent<Button>();
+            if(hasMoney == false)
+            {
+                if(button != null) button.interactable = false;
+            }
+            else
+            {
+                if(button != null) button.interactable = true;
+            }
+        }
+
+        public void AddToCart()
+        {
+            if(itemRef == null) return;
+            bool x10 = false;
+            if(Input.GetKey(KeyCode.LeftControl)) x10 = true;
+            ItemContainer playerInventory = EventManager.request.GetItemContainer("PlayerItemContainer").value;
+            if(playerInventory == null) Debug.LogError("Missing playerInventory");
+            int total = 0;
+            foreach (var itemSlot in parentItemContainerUi.itemSlots)
+            {
+                if(itemSlot.itemRef != null)
+                {
+                    total += itemSlot.itemRef.item.value * itemSlot.quantityToBuy;
+                }
+            }
+            var x = x10 && itemRef.quantity >= quantityToBuy + 10 ? 10 : itemRef.quantity - quantityToBuy;
+            total += x10 ? itemRef.item.value * x : itemRef.item.value;
+            bool hasMoney = playerInventory.HasItem("Old Dollars", total);
+            if(hasMoney == false) 
+            {
+                SoundController.EmmitSound(baterCtrl.sfxNegative, this.transform.position);
+                return;
+            }
+            if(itemRef.quantity > quantityToBuy) 
+            {
+                SoundController.EmmitSound(baterCtrl.sfxAddItem, this.transform.position);
+                quantityToBuy += x10 ? x : 1;
+                
+            }
+            if(quantityToBuy > 1)
+            {
+                valueUI.gameObject.SetActive(true);
+                valueUI.enabled = true;
+                valueUI.text = quantityToBuy.ToString() + "x";
+            }
+            quantityUi.text = (itemRef.quantity - quantityToBuy).ToString();
+            Image image = gameObject.GetComponent<Image>();
+            if(image != null)
+            {
+                image.sprite = buttonSuccess;
+            }
+            Button button = gameObject.GetComponent<Button>();
+            if(button != null)
+            {
+                SpriteState state = new SpriteState();
+                state.highlightedSprite = buttonSuccessSelected;
+                state.pressedSprite = buttonSuccessInteraction;
+                state.selectedSprite = buttonSuccessNavigation;
+                button.spriteState = state;
+            }
+        }
+
+        public void RemoveFromCart()
+        {
+            if(itemRef == null) return;
+            bool x10 = false;
+            if(Input.GetKey(KeyCode.LeftControl)) x10 = true;
+            SoundController.EmmitSound(baterCtrl.sfxRemoveItem, this.transform.position);
+            if(x10 && quantityToBuy > 10) quantityToBuy -= 10;
+            else if(x10 && quantityToBuy <= 10) quantityToBuy = 0;
+            else if(!x10 && quantityToBuy > 0) quantityToBuy -= 1;
+            if(quantityToBuy < 2)
+            {
+                valueUI.gameObject.SetActive(false);
+                valueUI.enabled = false;
+            }
+            valueUI.text = quantityToBuy.ToString() + "x";
+            quantityUi.text = (itemRef.quantity - quantityToBuy).ToString();
+            if(quantityToBuy == 0) ResetButtonUi();
+        }
+
+        private void ResetButtonUi()
+        {
+            Image image = gameObject.GetComponent<Image>();
+            if(image != null)
+            {
+                image.sprite = buttonNormal;
+            }
+            Button button = gameObject.GetComponent<Button>();
+            if(button != null)
+            {
+                SpriteState state = new SpriteState();
+                state.highlightedSprite = buttonSelected;
+                state.pressedSprite = buttonInteraction;
+                state.selectedSprite = buttonNavigation;
+                state.disabledSprite = buttonDisabled;
+                button.spriteState = state;
+            }
+        }
+    }
+}

+ 11 - 0
Runtime/UI/ItemSlotUi.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d59697381ff3da649b0bf3b9c3596c67
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 20 - 0
Runtime/WorldItem.cs

@@ -0,0 +1,20 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace KairoEngine.Inventory
+{
+    public class WorldItem : MonoBehaviour
+    {
+        public int id = 0;
+        public string itemName;
+        public int quantity = 1;
+
+        public string itemAmmoName;
+        public int ammoQuantity;
+
+        public float lastUsed = 1000f;
+
+        public MeshRenderer mesh;
+    }
+}

+ 11 - 0
Runtime/WorldItem.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: b1122e79b6dce49429bdbd9b361a0b4e
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 18 - 0
Runtime/WorldItemContainer.cs

@@ -0,0 +1,18 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using Sirenix.OdinInspector;
+
+namespace KairoEngine.Inventory
+{
+    public class WorldItemContainer : MonoBehaviour
+    {
+        public ItemContainer itemContainer;
+        public MeshRenderer meshRenderer;
+
+        void Start()
+        {
+            transform.parent = null;
+        }
+    }
+}

+ 11 - 0
Runtime/WorldItemContainer.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e8fc08eca0a0055458b48c40a84d1bb4
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 8 - 0
Tests.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: df101903063da7d4fbc5b361c4f53baa
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 8 - 0
Tests/Editor.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 1ccb168197823ad4380428dbe4343ef3
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 57 - 0
Tests/Editor/GenericEventsInventoryExtensionTests.cs

@@ -0,0 +1,57 @@
+using System.Collections;
+using System.Collections.Generic;
+using NUnit.Framework;
+using UnityEngine;
+using UnityEngine.TestTools;
+using KairoEngine.Core;
+
+namespace KairoEngine.Inventory.EditorTests
+{
+    public class GenericEventsInventoryExtensionsTests
+    {
+        private void TestEvent(ItemContainer itemContainer, ItemRef itemRef)
+        {
+            Assert.NotNull(itemContainer);
+            Assert.NotNull(itemRef);
+        }
+        
+        [Test]
+        public void GenericEvents_StartListening_ItemContainer_ItemRef()
+        {
+            EventManager.broadcast.StartListening("TestEvent", TestEvent);
+            System.Action<ItemContainer, ItemRef> action = null;
+            bool value = GenericEventsInventoryExtensions.list.TryGetValue("TestEvent", out action);
+            Assert.AreEqual(true, value);
+        }
+
+        [Test]
+        public void GenericEvents_StopListening_CharacterController()
+        {
+            EventManager.broadcast.StartListening("TestEvent", TestEvent);
+            System.Action<ItemContainer, ItemRef> action1 = null;
+            GenericEventsInventoryExtensions.list.TryGetValue("TestEvent", out action1);
+            Assert.IsNotNull(action1);
+            EventManager.broadcast.StopListening("TestEvent", TestEvent);
+            System.Action<ItemContainer, ItemRef> action2 = null;
+            GenericEventsInventoryExtensions.list.TryGetValue("TestEvent", out action2);
+            Assert.IsNull(action2);
+        }
+
+        [Test]
+        public void GenericEvents_Trigger_CharacterController()
+        {
+            EventManager.broadcast.StartListening("TestEvent", TestEvent);
+            GameObject obj = new GameObject();
+            obj.AddComponent<ItemContainer>();
+            ItemContainer itemContainer = obj.GetComponent<ItemContainer>();
+            ItemRef itemRef = new ItemRef(null, 1, 1000);
+            EventManager.broadcast.Trigger("TestEvent", itemContainer, itemRef);
+        }
+
+        [TearDown]
+        public void TearDown()
+        {
+            EventManager.broadcast.StopListening("TestEvent", TestEvent);
+        }
+    }
+}

+ 11 - 0
Tests/Editor/GenericEventsInventoryExtensionTests.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a9c4948d66a86a647871ef2c37396a39
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 22 - 0
Tests/Editor/KairoEngine.Inventory.EditorTests.asmdef

@@ -0,0 +1,22 @@
+{
+    "name": "KairoEngine.Inventory.EditorTests",
+    "references": [
+        "GUID:7e5ae6a38d1532248b4c890eca668b06",
+        "GUID:9fde6e799ff66bc4fadf2ce1ed9e1bb2",
+        "GUID:0acc523941302664db1f4e527237feb3",
+        "GUID:27619889b8ba8c24980f49ee34dbb44a"
+    ],
+    "includePlatforms": [
+        "Editor"
+    ],
+    "excludePlatforms": [],
+    "allowUnsafeCode": false,
+    "overrideReferences": true,
+    "precompiledReferences": [
+        "nunit.framework.dll"
+    ],
+    "autoReferenced": true,
+    "defineConstraints": [],
+    "versionDefines": [],
+    "noEngineReferences": false
+}

+ 7 - 0
Tests/Editor/KairoEngine.Inventory.EditorTests.asmdef.meta

@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: f5d4c158a30fec841aeecea5936c8b08
+AssemblyDefinitionImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 61 - 0
Tests/Editor/RequestEventsInventoryExtensionTests.cs

@@ -0,0 +1,61 @@
+using System.Collections;
+using System.Collections.Generic;
+using NUnit.Framework;
+using UnityEngine;
+using UnityEngine.TestTools;
+using KairoEngine.Core;
+using KairoEngine.Inventory;
+
+namespace KairoEngine.Inventory.EditorTests
+{
+    public class RequestEventsInventoryExtensionTests
+    {
+        private ItemContainer TestItemContainer() 
+        {
+            GameObject obj = new GameObject();
+            obj.AddComponent<ItemContainer>();
+            ItemContainer itemContainer = obj.GetComponent<ItemContainer>();
+            return itemContainer;
+        }
+
+        private ItemContainer EmptyTestItemContainer() => null;
+
+        [Test]
+        public void RequestEvents_Get_ItemContainer()
+        {
+            EventManager.request.Bind("TestItemContainer", TestItemContainer);
+            EventResponse<ItemContainer> response = EventManager.request.GetItemContainer("TestItemContainer");
+            Assert.AreEqual(typeof(ItemContainer), response.value.GetType());
+            Assert.AreEqual(EventResponseStatus.OK, response.status);
+        }
+
+        [Test]
+        public void RequestEvents_Get_ItemContainer_EmptyResponse()
+        {
+            EventManager.request.Bind("EmptyTestItemContainer", EmptyTestItemContainer);
+            EventResponse<ItemContainer> response = EventManager.request.GetItemContainer("EmptyTestItemContainer");
+            Assert.IsNull(response.value);
+            Assert.AreEqual(EventResponseStatus.EmptyResponse, response.status);
+        }
+
+        [Test]
+        public void RequestEvents_Get_ItemContainer_NotFound()
+        {
+            EventResponse<ItemContainer> response = EventManager.request.GetItemContainer("WrongTestItemContainer");
+            Assert.IsNull(response.value);
+            Assert.AreEqual(EventResponseStatus.NotFound, response.status);
+        }
+
+        [Test]
+        public void RequestEvents_Unbind_ItemContainer()
+        {
+            EventManager.request.Bind("UnbindTestItemContainer", TestItemContainer);
+            EventResponse<ItemContainer> response = EventManager.request.GetItemContainer("UnbindTestItemContainer");
+            Assert.AreEqual(true, response.value.GetType() == typeof(ItemContainer));
+            EventManager.request.Unbind("UnbindTestItemContainer", TestItemContainer);
+            response = EventManager.request.GetItemContainer("UnbindTestItemContainer");
+            Assert.IsNull(response.value);
+            Assert.AreEqual(EventResponseStatus.NotFound, response.status);
+        }
+    }
+}

+ 11 - 0
Tests/Editor/RequestEventsInventoryExtensionTests.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 5f2205cc3af2fe044bf6844a9caf5601
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 8 - 0
Tests/Runtime.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 21cb89def029df641b844a368f24b0e9
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 20 - 0
package.json

@@ -0,0 +1,20 @@
+{
+    "name": "at.kairoscope.kairoengine.inventory",
+    "displayName" : "KairoEngine Inventory",
+    "version": "0.1.0",
+    "unity": "2020.3",
+    "description": "Items and inventory library",
+    "repository": {
+      "type": "git",
+      "url": "https://git.kairoscope.net/kairoengine/inventory.git"
+    },
+    "author": "Kairoscope",
+    "dependencies": {
+      "at.kairoscope.kairoengine.core":"0.1.9",
+      "at.kairoscope.kairoengine.sfx":"0.1.3",
+      "at.kairoscope.kairoengine.stats":"0.1.2",
+      "at.kairoscope.kairoengine.ui":"0.1.7",
+      "at.kairoscope.thirdparty.tmpro":"1.0.0"
+    }
+  }
+  

+ 7 - 0
package.json.meta

@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 28f2da688ce0f5e4f9f4d4dc27cb0cac
+TextScriptImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 1 - 0
readme.md

@@ -0,0 +1 @@
+# KairoEngine Inventory v0.1.0

+ 7 - 0
readme.md.meta

@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 07777f60144223146b818e670c384147
+TextScriptImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: