DungeonFlowBuilder.cs 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using UnityEngine;
  6. namespace DunGen.Graph
  7. {
  8. /// <summary>
  9. /// A helper class for building a dungeon flow with a fluent interface, removing a lot of manual work
  10. /// </summary>
  11. public sealed class DungeonFlowBuilder
  12. {
  13. /// <summary>
  14. /// The dungeon flow that this builder will write to
  15. /// </summary>
  16. public DungeonFlow Flow { get; private set; }
  17. private List<GraphLine> lines = new List<GraphLine>();
  18. private List<GraphNode> nodes = new List<GraphNode>();
  19. private float currentPosition;
  20. public DungeonFlowBuilder(DungeonFlow flow)
  21. {
  22. Flow = flow;
  23. }
  24. /// <summary>
  25. /// Adds a new line segment to the flow graph. A line segment represents one or more rooms in a path between two nodes
  26. /// </summary>
  27. /// <param name="archetype">An archetype to pull rooms from</param>
  28. /// <param name="length">The length of this line segment. The entire path length will be automatically normalized. Must be greater than zero</param>
  29. /// <param name="locks">An optional list of the locks that can be applied to this segment</param>
  30. /// <param name="keys">An optional list of the keys that can be placed in this segment</param>
  31. /// <returns></returns>
  32. public DungeonFlowBuilder AddLine(DungeonArchetype archetype, float length = 1f, IEnumerable<KeyLockPlacement> locks = null, IEnumerable<KeyLockPlacement> keys = null)
  33. {
  34. return AddLine(new DungeonArchetype[] { archetype }, length, locks, keys);
  35. }
  36. /// <summary>
  37. /// Adds a new line segment to the flow graph. A line segment represents one or more rooms in a path between two nodes
  38. /// </summary>
  39. /// <param name="archetypes">A list of archetypes to pull rooms from</param>
  40. /// <param name="length">The length of this line segment. The entire path length will be automatically normalized. Must be greater than zero</param>
  41. /// <param name="locks">An optional list of the locks that can be applied to this segment</param>
  42. /// <param name="keys">An optional list of the keys that can be placed in this segment</param>
  43. /// <returns></returns>
  44. public DungeonFlowBuilder AddLine(IEnumerable<DungeonArchetype> archetypes, float length = 1f, IEnumerable<KeyLockPlacement> locks = null, IEnumerable<KeyLockPlacement> keys = null)
  45. {
  46. if (length <= 0f)
  47. throw new ArgumentOutOfRangeException("Length must be grater than zero");
  48. var line = new GraphLine(Flow);
  49. line.Position = currentPosition;
  50. line.Length = length;
  51. if (archetypes != null && archetypes.Any())
  52. line.DungeonArchetypes.AddRange(archetypes);
  53. if (locks != null && locks.Any())
  54. line.Locks.AddRange(locks);
  55. if (keys != null && keys.Any())
  56. line.Keys.AddRange(keys);
  57. lines.Add(line);
  58. currentPosition += length;
  59. return this;
  60. }
  61. /// <summary>
  62. /// 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)
  63. /// </summary>
  64. /// <param name="length">The additional length to add to this line segment. The entire path length will be automatically normalized. Must be greater than zero</param>
  65. /// <returns></returns>
  66. public DungeonFlowBuilder ContinueLine(float length = 1f)
  67. {
  68. if (lines.Count == 0)
  69. throw new Exception("Cannot call ContinueLine(..) before AddLine(..)");
  70. lines.Last().Length += length;
  71. currentPosition += length;
  72. return this;
  73. }
  74. /// <summary>
  75. /// Adds a node to the current point in the dungeon path. A node represents a single room in the layout
  76. /// </summary>
  77. /// <param name="tileSet">A tile set to pull rooms from</param>
  78. /// <param name="label">An optional label for this node (only visible in the dungeon flow editor)</param>
  79. /// <param name="allowLocksOnEntrance">Can locks be placed on the entrance doorway to this room?</param>
  80. /// <param name="allowLocksOnExit">Can locks be placed on the exit doorway from this room?</param>
  81. /// <param name="locks">An optional list of the locks that can be applied to this node</param>
  82. /// <param name="keys">An optional list of the keys that can be placed in this node</param>
  83. /// <returns></returns>
  84. public DungeonFlowBuilder AddNode(TileSet tileSet, string label = null, bool allowLocksOnEntrance = false, bool allowLocksOnExit = false, IEnumerable<KeyLockPlacement> locks = null, IEnumerable<KeyLockPlacement> keys = null)
  85. {
  86. return AddNode(new TileSet[] { tileSet }, label, allowLocksOnEntrance, allowLocksOnExit, locks, keys);
  87. }
  88. /// <summary>
  89. /// Adds a node to the current point in the dungeon path. A node represents a single room in the layout
  90. /// </summary>
  91. /// <param name="tileSets">A list of tile sets to pull rooms from</param>
  92. /// <param name="label">An optional label for this node (only visible in the dungeon flow editor)</param>
  93. /// <param name="allowLocksOnEntrance">Can locks be placed on the entrance doorway to this room?</param>
  94. /// <param name="allowLocksOnExit">Can locks be placed on the exit doorway from this room?</param>
  95. /// <param name="locks">An optional list of the locks that can be applied to this node</param>
  96. /// <param name="keys">An optional list of the keys that can be placed in this node</param>
  97. /// <returns></returns>
  98. public DungeonFlowBuilder AddNode(IEnumerable<TileSet> tileSets, string label = null, bool allowLocksOnEntrance = false, bool allowLocksOnExit = false, IEnumerable<KeyLockPlacement> locks = null, IEnumerable<KeyLockPlacement> keys = null)
  99. {
  100. var node = new GraphNode(Flow);
  101. node.Label = (label == null) ? "Node" : label;
  102. node.Position = currentPosition;
  103. node.NodeType = NodeType.Normal;
  104. if (allowLocksOnEntrance)
  105. node.LockPlacement |= NodeLockPlacement.Entrance;
  106. if (allowLocksOnExit)
  107. node.LockPlacement |= NodeLockPlacement.Exit;
  108. if(tileSets != null && tileSets.Any())
  109. node.TileSets.AddRange(tileSets);
  110. if (locks != null && locks.Any())
  111. node.Locks.AddRange(locks);
  112. if (keys != null && keys.Any())
  113. node.Keys.AddRange(keys);
  114. nodes.Add(node);
  115. return this;
  116. }
  117. /// <summary>
  118. /// Finalize the dungeon flow and assign the built nodes and lines to the targetted dungeon flow asset
  119. /// </summary>
  120. /// <returns></returns>
  121. public DungeonFlowBuilder Complete()
  122. {
  123. if (lines.Count == 0)
  124. throw new Exception("DungeonFlowBuilder must have at least one line added before finalizing");
  125. if (nodes.Count < 2)
  126. throw new Exception("DungeonFlowBuilder must have at least two nodes added before finalizing");
  127. // Normalize flow length
  128. float length = currentPosition;
  129. currentPosition = 1.0f;
  130. foreach (var line in lines)
  131. {
  132. line.Position /= length;
  133. line.Length /= length;
  134. }
  135. foreach(var node in nodes)
  136. node.Position /= length;
  137. // Set node types
  138. nodes.First().NodeType = NodeType.Start;
  139. nodes.Last().NodeType = NodeType.Goal;
  140. // Assign lines and nodes to the dungeon flow
  141. Flow.Lines.Clear();
  142. Flow.Nodes.Clear();
  143. Flow.Lines.AddRange(lines);
  144. Flow.Nodes.AddRange(nodes);
  145. return this;
  146. }
  147. }
  148. }