I wanted to create a loot table mechanic for any future game projects, and came up with what I am about to show you guys. I was wondering if someone could give it a glance and see if there could be any errors, or if it could be improved in any way.
using System;
using System.Collections.Generic;
namespace NovaEngineFramework.Framework.Probability
{
/// <summary>
/// A String-based loot table.
/// The table is based upon a pie-chart design. In other words,
/// each item is added with a desired rarity; that rarity counts
/// towards a total maximum of 100. Example:
/// Item1 = 10
/// Item2 = 40
/// Item3 = 50
/// Total = 100.
/// Item1 is the rarest item in the table, while Item3
/// is the most common.
/// </summary>
public class StringBasedLootTable
{
private readonly List<string> _loot;
private readonly Dictionary<string , int> _cachedItems;
private int _remainingStorage = 100;
private readonly Random _rngInstance;
public StringBasedLootTable()
{
_cachedItems = new Dictionary<string , int>();
_loot = new List<string>();
_rngInstance = new Random();
}
/// <summary>
/// Adds an item to the Loot Table with it's own specified rarity.
/// An error may be thrown if the storage capacity(maximum 100) is
/// exceeded by a previously added item.
/// </summary>
/// <param name="rarity">How rare the item will be.</param>
/// <param name="itemName">The string name representation of the item to be added.</param>
/// <exception cref="InvalidOperationException">Thrown if the capacity has already been exceeded, or is about to be exceeded.</exception>
public void Store( int rarity , string itemName )
{
int test = ( _remainingStorage - rarity );
if( test < 0 )
{
throw new Exception( "Storage Capacity Was full" );
}
_remainingStorage = _remainingStorage - rarity;
_cachedItems.Add( itemName , rarity );
}
/// <summary>
/// Shows the remaining storage of this loot table.
/// </summary>
/// <returns></returns>
public int CheckRemainingStorage()
{
return _remainingStorage;
}
/// <summary>
/// Rebuilds the loot table. An error will be thrown if
/// all of the available space has not been properly utilized.
/// This function should only be called once all the items have
/// been added to the table.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown if the capacity is not full.</exception>
public void Rebuild()
{
if( _remainingStorage > 0 )
{
throw new Exception( "The Capacity was not completely full." );
}
_loot.Clear();
foreach( KeyValuePair<string , int> pair in _cachedItems )
{
for( int i = 0; i < pair.Value; i++ )
{
_loot.Add( pair.Key );
}
}
}
/// <summary>
/// Finds a random item(string) within the loot table.
/// </summary>
/// <returns></returns>
public string GetRandomItem()
{
return _loot[ _rngInstance.Next( _loot.Count ) ];
}
}
}
And here is a brief implementation:
StringBasedLootTable swordTable = new StringBasedLootTable();
swordTable.Store( 40 , "Rusty Sword" );
swordTable.Store( 20 , "Steel Sword" );
swordTable.Store( 10 , "Titanium Sword" );
swordTable.Store( 30 , "Iron Sword" );
swordTable.Rebuild();
Console.WriteLine( swordTable.GetRandomItem() );
And the general concept of how it works:
It is based upon a pie-graph, in a way. Each item you add takes up a slice of the pie, and all the item probabilities must add up to exactly 100. A visual rendition would likely facillitate better understanding, so I went ahead and made this:

As you can see, the most probable item for one to recieve from the above implementation is a "rusty sword", being that it has the largest section of the pie graph. The least common item would be a Titanium sword, etc.
This post is not meant to fuel an argument about the use of var. Please do not offer it as an example of something that could be used towards fixing the code. The use of var is based upon purely personal, arbitrary taste; I have no intentions of using it.
