Loot/Item system

I made a Loot and Item system earlier and thought it was brilliant so I decided to share it. Lets get into it.

We need a ItemDatabase class that holds data of all items. We have a basic Item class that contains data of a single item. Then we have a constructor that deep copies our item, so holding multiple same items would work and different same items would act as individual instead of being a shallow copies of the main one.

ItemDatabase has two static methods that returns you item based on name or id.

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class ItemDatabase : MonoBehaviour
{
	public List<Item> allItems;
	public static List<Item> items;

	[System.Serializable]
	public class Item
	{
		public string itemName = "";
		public int itemID = 0;
		public int dmg = 0;
		public int armor = 0;
		public Sprite icon;

		public enum Type
		{
			equipment,
			other
		}

		public enum Rarity
		{
			common,
			uncommon,
			rare,
			epic
		}

		public Type type;
		public Rarity rarity;

		public Item()
		{

		}

		public Item(Item itemCopy)
		{
			itemID = itemCopy.itemID;
			icon = itemCopy.icon;
			itemName = itemCopy.itemName;
			dmg = itemCopy.dmg;
			armor = itemCopy.armor;
		}
	}

	private void Awake()
	{
		InitItems ();
	}

	private void InitItems()
	{
		items = allItems;
	}

	public static Item GetItemByName(string itemName)
	{
		for( int i = 0;i<ItemDatabase.items.Count;i++ )
		{
			if(ItemDatabase.items[i].itemName == itemName)
			{
				return ItemDatabase.items[i];
			}
		 }
		 return null;
	}

	public static Item GetItemById(int id)
	{
		for( int i = 0;i<ItemDatabase.items.Count;i++ )
		{
			if(ItemDatabase.items[i].itemID == id)
			{
				return ItemDatabase.items[i];
			}
		}
		return null;
	}
}

Then we have a Loot class. This is attached to anything that drops loot. Loot class spawns a loot prefab which holds the dropped item, so player can pick it up. In Loot class you can specify which drop % the items have and what items the enemy drops. When enemy dies we call DropItems method. In that method we basically roll a dice for every item drop possibility that the enemy has. Then we check on which gap the roll landed into.

For example we have a loot table like this:

Sword                   25%
Staff                      15%
Potion                   45%
Nothing                 55%

Total                     140%

We first add all those drop percentages together and get a total value. The value of 140. Now we need to take all the % values from 140 and add them together to get the maximum value. So 25% from 140 is 35 then we add 15% to it which is 21 and so on, until we get the maximum value of 196.

25%            35
15%            21
45%            63
55%            77

140%          196

Now when we roll between 0 and maximum value, we can check if roll land between 0 and 35 if it does we give player a sword. Next item to check is a staff, if player rolled between 35 and 56 we give player a staff. Now if player rolls 145 for example we don’t give him anything as the roll landed between nothing gap etc.

using UnityEngine;
using System.Collections;

public class Loot : MonoBehaviour
{
	private Transform m_transform;
	private int m_amountToDrop;

	public NewPickUp pickUpPrefab;
	public int minAmountOfDrops, maxAmountOfDrops;

	[System.Serializable]
	public class DropItem
	{
		public bool empty;
		public string itemName;
		public int amount;
		public bool randomizeAmount;
		public int min,max;
		public float dropChance;
	}

	public DropItem[] itemsToDrop;

	private float maximumPropability;

	private void Start()
	{
		m_transform = transform;
		m_amountToDrop = Random.Range( minAmountOfDrops,maxAmountOfDrops );
		InitItemChances ();
	}

	/// <summary>
	/// Initializes drop chances
	/// </summary>
	private void InitItemChances()
	{
		for( int i = 0;i<itemsToDrop.Length;i++ )
		{
			if(i != 0)
			{
				itemsToDrop[i].dropChance += itemsToDrop[i - 1].dropChance;
			}
		}

		maximumPropability = itemsToDrop[itemsToDrop.Length - 1].dropChance;

		for( int i = 0;i<itemsToDrop.Length;i++ )
		{
			int temp = System.Convert.ToInt32(((maximumPropability / 100) * itemsToDrop[i].dropChance));
			itemsToDrop[i].dropChance = (float)temp;
		}

		maximumPropability = itemsToDrop[itemsToDrop.Length - 1].dropChance;
	}

	/// <summary>
	/// Drops the items.
	/// </summary>
	public void DropItems()
	{
		for( int i = 0;i<m_amountToDrop;i++ )
		{
			int rnd = Random.Range(0,(int)maximumPropability + 1);
			DropItem droppedItem = ChooseItem(rnd);

			if( !droppedItem.empty )
			{
				SpawnItem(droppedItem);
			}
		}
	}

	/// <summary>
	/// Spawns item.
	/// </summary>
	/// <param name="droppedItem">Dropped item.</param>
	public void SpawnItem(DropItem droppedItem)
	{
		NewPickUp temp = GameObject.Instantiate( pickUpPrefab,m_transform.position,Quaternion.identity ) as NewPickUp;

		temp.itemName = droppedItem.itemName;

		if(droppedItem.randomizeAmount)
			temp.amount = Random.Range(droppedItem.min,droppedItem.max);
		else
			temp.amount = 1;

		temp.SetUp();
	}

	/// <summary>
	/// Choose item to drop by its drop chance
	/// </summary>
	/// <returns>The item.</returns>
	/// <param name="rnd">Random.</param>
	public DropItem ChooseItem(int rnd)
	{
		int maxVal = (int)itemsToDrop [itemsToDrop.Length - 1].dropChance;

		if(rnd == maxVal)
			rnd -= 1;

		int index = 0;
		while (itemsToDrop[index].dropChance <= rnd)
		{
			index++;
		}
		return itemsToDrop [index];
	}
}

Here is the pickup class. This class is spawned into the world as a prefab. If player steps on it we take the item to our inventory if we have space.

using UnityEngine;
using System.Collections;

public class NewPickUp : MonoBehaviour
{
	public string itemName;
	public int amount;

	private void Start()
	{
		SetUp();
	}

	public void SetUp()
	{
		Item item = ItemDatabase.GetItemByName( itemName );

		if(item != null)
		{
			GetComponent<SpriteRenderer>().sprite = item.icon;
		}
	}

	private void OnTriggerEnter2D(Collider2D col)
	{
		if (col.GetComponent<Inventory> ())
		{
			Inventory inv = col.GetComponent<Inventory> ();

			//We need to create deep copy from the item, so we can have multiple same items.
			ItemDatabase.Item newItem = new ItemDatabase.Item(item);

			if(newItem != null)
			{
				for(int i = 0;i<amount;i++)
				{
					if(!inv.IsFull(newItem))
					{
						inv.AddItem(newItem);
						amount--;
					}
				}
			}

			if(amount == 0)
			{
				Destroy(gameObject);
			}
		}
	}
}

I didn’t include my Inventory class, because it’s mostly about how the Inventory UI works. It contains 2 lists of itemslots. One for equipped items and one for inventory itself. Then you just do some kind of equipping system and handle all the checks such as; if your high enough level for the item, add and remove stats etc etc.

Thanks for reading and hope this helps someone.