using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
namespace DunGen.Graph
{
///
/// A helper class for building a dungeon flow with a fluent interface, removing a lot of manual work
///
public sealed class DungeonFlowBuilder
{
///
/// The dungeon flow that this builder will write to
///
public DungeonFlow Flow { get; private set; }
private List lines = new List();
private List nodes = new List();
private float currentPosition;
public DungeonFlowBuilder(DungeonFlow flow)
{
Flow = flow;
}
///
/// Adds a new line segment to the flow graph. A line segment represents one or more rooms in a path between two nodes
///
/// An archetype to pull rooms from
/// The length of this line segment. The entire path length will be automatically normalized. Must be greater than zero
/// An optional list of the locks that can be applied to this segment
/// An optional list of the keys that can be placed in this segment
///
public DungeonFlowBuilder AddLine(DungeonArchetype archetype, float length = 1f, IEnumerable locks = null, IEnumerable keys = null)
{
return AddLine(new DungeonArchetype[] { archetype }, length, locks, keys);
}
///
/// Adds a new line segment to the flow graph. A line segment represents one or more rooms in a path between two nodes
///
/// A list of archetypes to pull rooms from
/// The length of this line segment. The entire path length will be automatically normalized. Must be greater than zero
/// An optional list of the locks that can be applied to this segment
/// An optional list of the keys that can be placed in this segment
///
public DungeonFlowBuilder AddLine(IEnumerable archetypes, float length = 1f, IEnumerable locks = null, IEnumerable keys = null)
{
if (length <= 0f)
throw new ArgumentOutOfRangeException("Length must be grater than zero");
var line = new GraphLine(Flow);
line.Position = currentPosition;
line.Length = length;
if (archetypes != null && archetypes.Any())
line.DungeonArchetypes.AddRange(archetypes);
if (locks != null && locks.Any())
line.Locks.AddRange(locks);
if (keys != null && keys.Any())
line.Keys.AddRange(keys);
lines.Add(line);
currentPosition += length;
return this;
}
///
/// Continue an existing line. This will just increase the length of the preceding line segment (for use when you need to place a node mid-way through a line segment)
///
/// The additional length to add to this line segment. The entire path length will be automatically normalized. Must be greater than zero
///
public DungeonFlowBuilder ContinueLine(float length = 1f)
{
if (lines.Count == 0)
throw new Exception("Cannot call ContinueLine(..) before AddLine(..)");
lines.Last().Length += length;
currentPosition += length;
return this;
}
///
/// Adds a node to the current point in the dungeon path. A node represents a single room in the layout
///
/// A tile set to pull rooms from
/// An optional label for this node (only visible in the dungeon flow editor)
/// Can locks be placed on the entrance doorway to this room?
/// Can locks be placed on the exit doorway from this room?
/// An optional list of the locks that can be applied to this node
/// An optional list of the keys that can be placed in this node
///
public DungeonFlowBuilder AddNode(TileSet tileSet, string label = null, bool allowLocksOnEntrance = false, bool allowLocksOnExit = false, IEnumerable locks = null, IEnumerable keys = null)
{
return AddNode(new TileSet[] { tileSet }, label, allowLocksOnEntrance, allowLocksOnExit, locks, keys);
}
///
/// Adds a node to the current point in the dungeon path. A node represents a single room in the layout
///
/// A list of tile sets to pull rooms from
/// An optional label for this node (only visible in the dungeon flow editor)
/// Can locks be placed on the entrance doorway to this room?
/// Can locks be placed on the exit doorway from this room?
/// An optional list of the locks that can be applied to this node
/// An optional list of the keys that can be placed in this node
///
public DungeonFlowBuilder AddNode(IEnumerable tileSets, string label = null, bool allowLocksOnEntrance = false, bool allowLocksOnExit = false, IEnumerable locks = null, IEnumerable keys = null)
{
var node = new GraphNode(Flow);
node.Label = (label == null) ? "Node" : label;
node.Position = currentPosition;
node.NodeType = NodeType.Normal;
if (allowLocksOnEntrance)
node.LockPlacement |= NodeLockPlacement.Entrance;
if (allowLocksOnExit)
node.LockPlacement |= NodeLockPlacement.Exit;
if(tileSets != null && tileSets.Any())
node.TileSets.AddRange(tileSets);
if (locks != null && locks.Any())
node.Locks.AddRange(locks);
if (keys != null && keys.Any())
node.Keys.AddRange(keys);
nodes.Add(node);
return this;
}
///
/// Finalize the dungeon flow and assign the built nodes and lines to the targetted dungeon flow asset
///
///
public DungeonFlowBuilder Complete()
{
if (lines.Count == 0)
throw new Exception("DungeonFlowBuilder must have at least one line added before finalizing");
if (nodes.Count < 2)
throw new Exception("DungeonFlowBuilder must have at least two nodes added before finalizing");
// Normalize flow length
float length = currentPosition;
currentPosition = 1.0f;
foreach (var line in lines)
{
line.Position /= length;
line.Length /= length;
}
foreach(var node in nodes)
node.Position /= length;
// Set node types
nodes.First().NodeType = NodeType.Start;
nodes.Last().NodeType = NodeType.Goal;
// Assign lines and nodes to the dungeon flow
Flow.Lines.Clear();
Flow.Nodes.Clear();
Flow.Lines.AddRange(lines);
Flow.Nodes.AddRange(nodes);
return this;
}
}
}