Newer
Older
TheVengeance-Project-IADE-Unity2D / Assets / Ink / InkLibs / InkCompiler / ParsedHierarchy / Path.cs
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. namespace Ink.Parsed
  5. {
  6. public class Path
  7. {
  8. public FlowLevel baseTargetLevel {
  9. get {
  10. if (baseLevelIsAmbiguous)
  11. return FlowLevel.Story;
  12. else
  13. return (FlowLevel) _baseTargetLevel;
  14. }
  15. }
  16. public bool baseLevelIsAmbiguous {
  17. get {
  18. return _baseTargetLevel == null;
  19. }
  20. }
  21. public string firstComponent {
  22. get {
  23. if (components == null || components.Count == 0)
  24. return null;
  25. return components [0].name;
  26. }
  27. }
  28. public int numberOfComponents {
  29. get {
  30. return components.Count;
  31. }
  32. }
  33. public string dotSeparatedComponents {
  34. get {
  35. if( _dotSeparatedComponents == null ) {
  36. _dotSeparatedComponents = string.Join(".", components.Select(c => c?.name));
  37. }
  38. return _dotSeparatedComponents;
  39. }
  40. }
  41. string _dotSeparatedComponents;
  42. public List<Identifier> components { get; }
  43. public Path(FlowLevel baseFlowLevel, List<Identifier> components)
  44. {
  45. _baseTargetLevel = baseFlowLevel;
  46. this.components = components;
  47. }
  48. public Path(List<Identifier> components)
  49. {
  50. _baseTargetLevel = null;
  51. this.components = components;
  52. }
  53. public Path(Identifier ambiguousName)
  54. {
  55. _baseTargetLevel = null;
  56. components = new List<Identifier> ();
  57. components.Add (ambiguousName);
  58. }
  59. public override string ToString ()
  60. {
  61. if (components == null || components.Count == 0) {
  62. if (baseTargetLevel == FlowLevel.WeavePoint)
  63. return "-> <next gather point>";
  64. else
  65. return "<invalid Path>";
  66. }
  67. return "-> " + dotSeparatedComponents;
  68. }
  69. public Parsed.Object ResolveFromContext(Parsed.Object context)
  70. {
  71. if (components == null || components.Count == 0) {
  72. return null;
  73. }
  74. // Find base target of path from current context. e.g.
  75. // ==> BASE.sub.sub
  76. var baseTargetObject = ResolveBaseTarget (context);
  77. if (baseTargetObject == null) {
  78. return null;
  79. }
  80. // Given base of path, resolve final target by working deeper into hierarchy
  81. // e.g. ==> base.mid.FINAL
  82. if (components.Count > 1) {
  83. return ResolveTailComponents (baseTargetObject);
  84. }
  85. return baseTargetObject;
  86. }
  87. // Find the root object from the base, i.e. root from:
  88. // root.sub1.sub2
  89. Parsed.Object ResolveBaseTarget(Parsed.Object originalContext)
  90. {
  91. var firstComp = firstComponent;
  92. // Work up the ancestry to find the node that has the named object
  93. Parsed.Object ancestorContext = originalContext;
  94. while (ancestorContext != null) {
  95. // Only allow deep search when searching deeper from original context.
  96. // Don't allow search upward *then* downward, since that's searching *everywhere*!
  97. // Allowed examples:
  98. // - From an inner gather of a stitch, you should search up to find a knot called 'x'
  99. // at the root of a story, but not a stitch called 'x' in that knot.
  100. // - However, from within a knot, you should be able to find a gather/choice
  101. // anywhere called 'x'
  102. // (that latter example is quite loose, but we allow it)
  103. bool deepSearch = ancestorContext == originalContext;
  104. var foundBase = TryGetChildFromContext (ancestorContext, firstComp, null, deepSearch);
  105. if (foundBase != null)
  106. return foundBase;
  107. ancestorContext = ancestorContext.parent;
  108. }
  109. return null;
  110. }
  111. // Find the final child from path given root, i.e.:
  112. // root.sub.finalChild
  113. Parsed.Object ResolveTailComponents(Parsed.Object rootTarget)
  114. {
  115. Parsed.Object foundComponent = rootTarget;
  116. for (int i = 1; i < components.Count; ++i) {
  117. var compName = components [i].name;
  118. FlowLevel minimumExpectedLevel;
  119. var foundFlow = foundComponent as FlowBase;
  120. if (foundFlow != null)
  121. minimumExpectedLevel = (FlowLevel)(foundFlow.flowLevel + 1);
  122. else
  123. minimumExpectedLevel = FlowLevel.WeavePoint;
  124. foundComponent = TryGetChildFromContext (foundComponent, compName, minimumExpectedLevel);
  125. if (foundComponent == null)
  126. break;
  127. }
  128. return foundComponent;
  129. }
  130. // See whether "context" contains a child with a given name at a given flow level
  131. // Can either be a named knot/stitch (a FlowBase) or a weave point within a Weave (Choice or Gather)
  132. // This function also ignores any other object types that are neither FlowBase nor Weave.
  133. // Called from both ResolveBase (force deep) and ResolveTail for the individual components.
  134. Parsed.Object TryGetChildFromContext(Parsed.Object context, string childName, FlowLevel? minimumLevel, bool forceDeepSearch = false)
  135. {
  136. // null childLevel means that we don't know where to find it
  137. bool ambiguousChildLevel = minimumLevel == null;
  138. // Search for WeavePoint within Weave
  139. var weaveContext = context as Weave;
  140. if ( weaveContext != null && (ambiguousChildLevel || minimumLevel == FlowLevel.WeavePoint)) {
  141. return (Parsed.Object) weaveContext.WeavePointNamed (childName);
  142. }
  143. // Search for content within Flow (either a sub-Flow or a WeavePoint)
  144. var flowContext = context as FlowBase;
  145. if (flowContext != null) {
  146. // When searching within a Knot, allow a deep searches so that
  147. // named weave points (choices and gathers) can be found within any stitch
  148. // Otherwise, we just search within the immediate object.
  149. var shouldDeepSearch = forceDeepSearch || flowContext.flowLevel == FlowLevel.Knot;
  150. return flowContext.ContentWithNameAtLevel (childName, minimumLevel, shouldDeepSearch);
  151. }
  152. return null;
  153. }
  154. FlowLevel? _baseTargetLevel;
  155. }
  156. }