using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace DunGen
{
/**
* Lots of code rewriting since Unity doesn't support serializing generics
*/
#region Helper Class
[Serializable]
public sealed class GameObjectChance
{
public GameObject Value = null;
public float MainPathWeight = 1f;
public float BranchPathWeight = 1f;
public AnimationCurve DepthWeightScale = AnimationCurve.Linear(0, 1, 1, 1);
public TileSet TileSet; // Only used at runtime - should probably move this elsewhere
public GameObjectChance()
: this(null, 1, 1, null)
{
}
public GameObjectChance(GameObject value)
: this(value, 1, 1, null)
{
}
public GameObjectChance(GameObject value, float mainPathWeight, float branchPathWeight, TileSet tileSet)
{
Value = value;
MainPathWeight = mainPathWeight;
BranchPathWeight = branchPathWeight;
TileSet = tileSet;
}
public float GetWeight(bool isOnMainPath, float normalizedDepth)
{
float weight = (isOnMainPath) ? MainPathWeight : BranchPathWeight;
weight *= DepthWeightScale.Evaluate(normalizedDepth);
return weight;
}
}
#endregion
///
/// A table containing weighted values to be picked at random
///
[Serializable]
public class GameObjectChanceTable
{
public List Weights = new List();
public GameObjectChanceTable Clone()
{
GameObjectChanceTable newTable = new GameObjectChanceTable();
foreach (var w in Weights)
newTable.Weights.Add(new GameObjectChance(w.Value, w.MainPathWeight, w.BranchPathWeight, w.TileSet) { DepthWeightScale = w.DepthWeightScale });
return newTable;
}
///
/// Is there at least one non-null value in the table?
///
public bool HasAnyValidEntries()
{
bool hasValidEntries = false;
foreach (var entry in Weights)
{
if (entry.Value != null)
{
hasValidEntries = true;
break;
}
}
return hasValidEntries;
}
///
/// Is there at least one non-null value in the table, given the appropriate input settings?
///
///
///
///
///
///
///
public bool HasAnyValidEntries(bool isOnMainPath, float normalizedDepth, GameObject previouslyChosen, bool allowImmediateRepeats, bool allowNullSelection = false)
{
if (allowNullSelection)
return true;
bool hasValidEntries = false;
foreach (var entry in Weights)
{
if (entry.Value != null)
{
float weight = entry.GetWeight(isOnMainPath, normalizedDepth);
if (weight > 0f && (allowImmediateRepeats || previouslyChosen != entry.Value))
{
hasValidEntries = true;
break;
}
}
}
return hasValidEntries;
}
///
/// Does this chance table contain the specified GameObject?
///
/// The object to check
/// True if the GameObject is included in the chance table
public bool ContainsGameObject(GameObject obj)
{
foreach (var weight in Weights)
if (weight.Value == obj)
return true;
return false;
}
///
/// Picks an object from the table at random, taking weights into account
///
/// The random number generator to use
/// Is this object to be spawn on the main path
/// The normalized depth (0-1) that this object is to be spawned at in the dungeon
/// A random value
public GameObjectChance GetRandom(RandomStream random, bool isOnMainPath, float normalizedDepth, GameObject previouslyChosen, bool allowImmediateRepeats, bool removeFromTable = false, bool allowNullSelection = false)
{
float totalWeight = 0;
foreach (var w in Weights)
{
if (w == null)
continue;
if (!allowNullSelection && w.Value == null)
continue;
if (!(allowImmediateRepeats || previouslyChosen == null || w.Value != previouslyChosen))
continue;
totalWeight += w.GetWeight(isOnMainPath, normalizedDepth);
}
float randomNumber = (float)(random.NextDouble() * totalWeight);
foreach (var w in Weights)
{
if (w == null)
continue;
if (!allowNullSelection && w.Value == null)
continue;
if (w.Value == previouslyChosen && Weights.Count > 1 && !allowImmediateRepeats)
continue;
float weight = w.GetWeight(isOnMainPath, normalizedDepth);
if (randomNumber < weight)
{
if(removeFromTable)
Weights.Remove(w);
return w;
}
randomNumber -= weight;
}
return null;
}
///
/// Picks an object at random from a collection of tables, taking weights into account
///
/// The random number generator to use
/// Is this object to be spawn on the main path
/// The normalized depth (0-1) that this object is to be spawned at in the dungeon
/// A list of chance tables to pick from
/// A random value
public static GameObject GetCombinedRandom(RandomStream random, bool isOnMainPath, float normalizedDepth, params GameObjectChanceTable[] tables)
{
float totalWeight = tables.SelectMany(x => x.Weights.Select(y => y.GetWeight(isOnMainPath, normalizedDepth))).Sum();
float randomNumber = (float)(random.NextDouble() * totalWeight);
foreach(var w in tables.SelectMany(x => x.Weights))
{
float weight = w.GetWeight(isOnMainPath, normalizedDepth);
if (randomNumber < weight)
return w.Value;
randomNumber -= weight;
}
return null;
}
public static GameObjectChanceTable Combine(params GameObjectChanceTable[] tables)
{
GameObjectChanceTable combined = new GameObjectChanceTable();
foreach(var t in tables)
foreach(var w in t.Weights)
combined.Weights.Add(new GameObjectChance(w.Value, w.MainPathWeight, w.BranchPathWeight, w.TileSet) { DepthWeightScale = w.DepthWeightScale });
return combined;
}
}
}