We have items, we have lootbags, it’s time to put them together to finish off a big mechanic of the game.

What we’re after is a way to tell an enemy to spawn a lootbag with some items when it dies. Each item should have its own probability of being in the lootbag, eg. common items like potions could have a 100% chance to spawn, while rare items would only be in the loot bag 5% of the time.

Let’s couple the Item and it’s chance of being spawned into a struct, which we’ll call a LootTableEntry.

[System.Serializable]
public class LootTableEntry
{
    public float chance = 1f;
    public Item item;
}

While we’re here, let’s also make a custom PropertyDrawer because currently it looks like this:

Old Inspector

It’s fine, but it takes up 2 lines making it a foldout, which is unnecessary.

#if UNITY_EDITOR
    [CustomPropertyDrawer(typeof(LootTableEntry))]
    public class LootTableEntryDrawer : PropertyDrawer
    {
        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
        {
            var chanceProp = property.FindPropertyRelative("chance");

            chanceProp.floatValue = EditorGUI.Slider(new Rect(position.x, position.y, position.width / 3f, position.height),
                "", chanceProp.floatValue, 0f, 1f);

            EditorGUI.ObjectField(new Rect(position.x + position.width / 3f, position.y, position.width - position.width / 3f, position.height),
                property.FindPropertyRelative("item"), typeof(Item), GUIContent.none);
        }
    }
#endif

Here we just put both on one line, and also turned the chance field into a slider. This is what it looks like now:

New Inspector

For this first version, it’s gonna be rather simple. We setup an array and loop over each of the items, get a random value and use it to determine whether to spawn the item or not.

class LootDropOnDeath
{
    [SerializeField] List<LootTableEntry> lootTable = new();

    // ...

    List<Item> GetItems()
    {
        List<Item> items = new();
        foreach (LootTableEntry entry in lootTable)
        {
            if (Random.value < entry.chance)
            {
                items.Add(entry.item);
            }
        }

        return items;
    }
}

We subscribe to the OnDeath event in Awake() and spawn the loot bag with the items when its invoked.

void Awake()
{
    health.OnDeath += (sender, e) =>
    {
        ItemContainer lootbag = Instantiate(lootbagPrefab, transform.position, Quaternion.identity).GetComponent<ItemContainer>();
        lootbag.TryAddItems(GetItems());
    };
}

I added a simple toggle to ItemContainer, deleteWhenEmpty. If this is on, everytime the container is updated it checks whether all itemslots are empty and if so, simply destroys itself.

Here’s how it looks in action:

Ingame

In the future I will probably expand this system with something like groups, essentially excluding the possibility of getting certain items at once.