using System; using System.Collections.Generic; using System.Linq; using UnityEngine; namespace DunGen { #region Helper Class /// /// A value with a weight /// /// The type of value to use [Serializable] public class Chance { public T Value; public float Weight; public Chance() :this(default(T), 1) { } public Chance(T value) :this(value, 1) { } public Chance(T value, float weight) { Value = value; Weight = weight; } } #endregion /// /// A table containing weighted values to be picked at random /// /// The type of object to be picked public class ChanceTable { /// /// Values and their corresponding weights, which determine how likely a value is to be picked relative to others in the table /// [SerializeField] public List> Weights = new List>(); /// /// Adds a value-weight pair to the table /// /// The value to add /// Its weight, representing the chance this value has of being picked, relative to the others in the table public void Add(T value, float weight) { Weights.Add(new Chance(value, weight)); } /// /// Removes a value-weight pair from the table /// /// The value to remove public void Remove(T value) { for (int i = 0; i < Weights.Count; i++) { if (Weights[i].Value.Equals(value)) Weights.RemoveAt(i); } } /// /// Picks an object from the table at random, taking weights into account /// /// The random number generator to use /// A random value public T GetRandom(RandomStream random) { float totalWeight = Weights.Select(x => x.Weight).Sum(); float randomNumber = (float)(random.NextDouble() * totalWeight); foreach(var w in Weights) { if (randomNumber < w.Weight) return w.Value; randomNumber -= w.Weight; } return default(T); } /// /// Picks an object at random from a collection of tables, taking weights into account /// /// The random number generator to use /// A list of chance tables to pick from /// A random value public static TVal GetCombinedRandom(RandomStream random, params ChanceTable[] tables) { float totalWeight = tables.SelectMany(x => x.Weights.Select(y => y.Weight)).Sum(); float randomNumber = (float)(random.NextDouble() * totalWeight); foreach (var w in tables.SelectMany(x => x.Weights)) { if (randomNumber < w.Weight) return w.Value; randomNumber -= w.Weight; } return default(TVal); } } }