Sunday, April 19, 2015

Loot Tables Code - Heroes' Journey

Hello all!

This is a continuation of my previous post, here, where I went over the design side of my Loot Tables. This post will go over the actual code I am using to implement the logic from the last post.

Code                                                                                   

The first thing we need are our variables: our List of entries, an int to store the total of all the weights, a List to hold the weight converted to percentages, and a List to hold the cumulative percentages that we check against.

private List<LootTableEntry> lootEntries = new List<LootTableEntry>(); //The sum of all of the entries' weights private int totalWeights = 0; //Item weights as overall percentages private List<int> weightPercentages = new List<int>(); //The cumulative percentage table to check against private List<int> cumulativePercentages = new List<int>();
I also made a method that makes it easier to add an entry,  I use this in child classes' constructors to build the Entry list.

protected void AddNewEntry(int probability, params BaseItem[] items) { lootEntries.Add(new LootTableEntry(probability, new List<BaseItem>(items))); }
Here is my LootTableEntry class for reference.

using UnityEngine; using System.Collections; using System.Collections.Generic; public class LootTableEntry { public List<BaseItem> Items = new List<BaseItem>(); public int Weight = 0; public LootTableEntry(int weight, List<BaseItem> items) { Weight = weight; Items = items; } }
After I add all of the entries I call a method that initializes the Loot Table (which fills out the other variables we declared earlier)

protected void InitTable() { GetTotalWeights(); GetWeightPercentages(); GetCumulativePercentages(); }
The first method called by InitTable is GetTotalWeights. This just iterates through all of the entries add gets a sum of all their weights.

private void GetTotalWeights() { totalWeights = 0; //Add all of the items' Weights together foreach (LootTableEntry entry in lootEntries) { totalWeights += entry.Weight; } }
The second method is GetWeightPercentages. This iterates through all of the entries again and divides their weights by the total to get their actual percentages.

private void GetWeightPercentages() { weightPercentages.Clear(); for (int i = 0; i < lootEntries.Count; i++ ) { //Get the probability of the item based upon the total probability int percent = Mathf.RoundToInt(((float)lootEntries[i].Weight / (float)totalWeights) * 100); weightPercentages.Add(percent); } }
The last method InitTable calls is the GetCumulativePercentages. This is where I build the list to check against. The first thing I do is add a 0 to the list as the first element, this will give me the lower bounds for checking the first range. Next, I go through each element in the weightPercentages List. On each element add it to the running total and add the new total to the cumulatvePercentages List. The final element should bring the total up to 100.

private void GetCumulativePercentages() { cumulativePercentages.Clear(); int cumulativePercent = 0; cumulativePercentages.Add(cumulativePercent); for (int i = 0; i < weightPercentages.Count; i++) { //Add the new item's probability cumulativePercent += weightPercentages[i]; //add the new percent as the key, and the index of the associated entry as the value cumulativePercentages.Add(cumulativePercent); } }
Here is an example LootTable that I based the logic examples off of.

using UnityEngine; using System.Collections; public class LT_Test : BaseLootTable { public LT_Test() { AddNewEntry(5, new AT_SimpleClothJerkin(), new AHD_SimpleLeatherCap()); AddNewEntry(15, new WM_SimpleLongSword()); AddNewEntry(60, null); InitTable(); } }
After a battle I need to get which Loot Entry the player will receive. I have a method called GetLoot that picks a random number and checks the ranges in the cumulativePercentages list. Then it returns the Entry in the lootEntries List that has the 'found' index.

public List<BaseItem> GetLoot() { int check = Random.Range(1, 101); for (int i = 0; i < cumulativePercentages.Count - 1; i++) { //Check if the random number we generated is between this set of values if (check > cumulativePercentages[i] && check <= cumulativePercentages[i + 1]) { return lootEntries[i].Items; } } return new List<BaseItem>(); }
And here is the full BaseLootTable class

using UnityEngine; using System.Collections; using System.Collections.Generic; public class BaseLootTable { private List<LootTableEntry> lootEntries = new List<LootTableEntry>(); //The sum of all of the entries' weights private int totalWeights = 0; //Item weights as overall percentages private List<int> weightPercentages = new List<int>(); //The cumulative percentage table to check against private List<int> cumulativePercentages = new List<int>(); protected void InitTable() { GetTotalWeights(); GetWeightPercentages(); GetCumulativePercentages(); } public List<BaseItem> GetLoot() { int check = Random.Range(1, 101); for (int i = 0; i < cumulativePercentages.Count - 1; i++) { //Check if the random number we generated is between this set of values if (check > cumulativePercentages[i] && check <= cumulativePercentages[i + 1]) { return lootEntries[i].Items; } } return new List<BaseItem>(); } private void GetTotalWeights() { totalWeights = 0; //Add all of the items' Weights together foreach (LootTableEntry entry in lootEntries) { totalWeights += entry.Weight; } } private void GetWeightPercentages() { weightPercentages.Clear(); for (int i = 0; i < lootEntries.Count; i++ ) { //Get the probability of the item based upon the total probability int percent = Mathf.RoundToInt(((float)lootEntries[i].Weight / (float)totalWeights) * 100); weightPercentages.Add(percent); } } private void GetCumulativePercentages() { cumulativePercentages.Clear(); int cumulativePercent = 0; cumulativePercentages.Add(cumulativePercent); for (int i = 0; i < weightPercentages.Count; i++) { //Add the new item's probability cumulativePercent += weightPercentages[i]; //add the new percent as the key, and the index of the associated entry as the value cumulativePercentages.Add(cumulativePercent); } } protected void AddNewEntry(int probability, params BaseItem[] items) { lootEntries.Add(new LootTableEntry(probability, new List<BaseItem>(items))); } }
That is how I implemented my Loot Tables, I'm sure there are better ways of doing it, but this works fine for this game. Anyway, I hope this helps someone develop a loot system for their game.

No comments:

Post a Comment