mirror of
https://github.com/Ratstail91/Mementos.git
synced 2025-11-29 02:24:28 +11:00
193 lines
6.7 KiB
C#
193 lines
6.7 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
namespace Ink.Parsed
|
|
{
|
|
public class Path
|
|
{
|
|
public FlowLevel baseTargetLevel {
|
|
get {
|
|
if (baseLevelIsAmbiguous)
|
|
return FlowLevel.Story;
|
|
else
|
|
return (FlowLevel) _baseTargetLevel;
|
|
}
|
|
}
|
|
|
|
public bool baseLevelIsAmbiguous {
|
|
get {
|
|
return _baseTargetLevel == null;
|
|
}
|
|
}
|
|
|
|
public string firstComponent {
|
|
get {
|
|
if (components == null || components.Count == 0)
|
|
return null;
|
|
|
|
return components [0].name;
|
|
}
|
|
}
|
|
|
|
public int numberOfComponents {
|
|
get {
|
|
return components.Count;
|
|
}
|
|
}
|
|
|
|
public string dotSeparatedComponents {
|
|
get {
|
|
if( _dotSeparatedComponents == null ) {
|
|
_dotSeparatedComponents = string.Join(".", components.Select(c => c?.name));
|
|
}
|
|
|
|
return _dotSeparatedComponents;
|
|
}
|
|
}
|
|
string _dotSeparatedComponents;
|
|
|
|
public List<Identifier> components { get; }
|
|
|
|
public Path(FlowLevel baseFlowLevel, List<Identifier> components)
|
|
{
|
|
_baseTargetLevel = baseFlowLevel;
|
|
this.components = components;
|
|
}
|
|
|
|
public Path(List<Identifier> components)
|
|
{
|
|
_baseTargetLevel = null;
|
|
this.components = components;
|
|
}
|
|
|
|
public Path(Identifier ambiguousName)
|
|
{
|
|
_baseTargetLevel = null;
|
|
components = new List<Identifier> ();
|
|
components.Add (ambiguousName);
|
|
}
|
|
|
|
public override string ToString ()
|
|
{
|
|
if (components == null || components.Count == 0) {
|
|
if (baseTargetLevel == FlowLevel.WeavePoint)
|
|
return "-> <next gather point>";
|
|
else
|
|
return "<invalid Path>";
|
|
}
|
|
|
|
return "-> " + dotSeparatedComponents;
|
|
}
|
|
|
|
public Parsed.Object ResolveFromContext(Parsed.Object context)
|
|
{
|
|
if (components == null || components.Count == 0) {
|
|
return null;
|
|
}
|
|
|
|
// Find base target of path from current context. e.g.
|
|
// ==> BASE.sub.sub
|
|
var baseTargetObject = ResolveBaseTarget (context);
|
|
if (baseTargetObject == null) {
|
|
return null;
|
|
|
|
}
|
|
|
|
// Given base of path, resolve final target by working deeper into hierarchy
|
|
// e.g. ==> base.mid.FINAL
|
|
if (components.Count > 1) {
|
|
return ResolveTailComponents (baseTargetObject);
|
|
}
|
|
|
|
return baseTargetObject;
|
|
}
|
|
|
|
// Find the root object from the base, i.e. root from:
|
|
// root.sub1.sub2
|
|
Parsed.Object ResolveBaseTarget(Parsed.Object originalContext)
|
|
{
|
|
var firstComp = firstComponent;
|
|
|
|
// Work up the ancestry to find the node that has the named object
|
|
Parsed.Object ancestorContext = originalContext;
|
|
while (ancestorContext != null) {
|
|
|
|
// Only allow deep search when searching deeper from original context.
|
|
// Don't allow search upward *then* downward, since that's searching *everywhere*!
|
|
// Allowed examples:
|
|
// - From an inner gather of a stitch, you should search up to find a knot called 'x'
|
|
// at the root of a story, but not a stitch called 'x' in that knot.
|
|
// - However, from within a knot, you should be able to find a gather/choice
|
|
// anywhere called 'x'
|
|
// (that latter example is quite loose, but we allow it)
|
|
bool deepSearch = ancestorContext == originalContext;
|
|
|
|
var foundBase = TryGetChildFromContext (ancestorContext, firstComp, null, deepSearch);
|
|
if (foundBase != null)
|
|
return foundBase;
|
|
|
|
ancestorContext = ancestorContext.parent;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
// Find the final child from path given root, i.e.:
|
|
// root.sub.finalChild
|
|
Parsed.Object ResolveTailComponents(Parsed.Object rootTarget)
|
|
{
|
|
Parsed.Object foundComponent = rootTarget;
|
|
for (int i = 1; i < components.Count; ++i) {
|
|
var compName = components [i].name;
|
|
|
|
FlowLevel minimumExpectedLevel;
|
|
var foundFlow = foundComponent as FlowBase;
|
|
if (foundFlow != null)
|
|
minimumExpectedLevel = (FlowLevel)(foundFlow.flowLevel + 1);
|
|
else
|
|
minimumExpectedLevel = FlowLevel.WeavePoint;
|
|
|
|
|
|
foundComponent = TryGetChildFromContext (foundComponent, compName, minimumExpectedLevel);
|
|
if (foundComponent == null)
|
|
break;
|
|
}
|
|
|
|
return foundComponent;
|
|
}
|
|
|
|
// See whether "context" contains a child with a given name at a given flow level
|
|
// Can either be a named knot/stitch (a FlowBase) or a weave point within a Weave (Choice or Gather)
|
|
// This function also ignores any other object types that are neither FlowBase nor Weave.
|
|
// Called from both ResolveBase (force deep) and ResolveTail for the individual components.
|
|
Parsed.Object TryGetChildFromContext(Parsed.Object context, string childName, FlowLevel? minimumLevel, bool forceDeepSearch = false)
|
|
{
|
|
// null childLevel means that we don't know where to find it
|
|
bool ambiguousChildLevel = minimumLevel == null;
|
|
|
|
// Search for WeavePoint within Weave
|
|
var weaveContext = context as Weave;
|
|
if ( weaveContext != null && (ambiguousChildLevel || minimumLevel == FlowLevel.WeavePoint)) {
|
|
return (Parsed.Object) weaveContext.WeavePointNamed (childName);
|
|
}
|
|
|
|
// Search for content within Flow (either a sub-Flow or a WeavePoint)
|
|
var flowContext = context as FlowBase;
|
|
if (flowContext != null) {
|
|
|
|
// When searching within a Knot, allow a deep searches so that
|
|
// named weave points (choices and gathers) can be found within any stitch
|
|
// Otherwise, we just search within the immediate object.
|
|
var shouldDeepSearch = forceDeepSearch || flowContext.flowLevel == FlowLevel.Knot;
|
|
return flowContext.ContentWithNameAtLevel (childName, minimumLevel, shouldDeepSearch);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
FlowLevel? _baseTargetLevel;
|
|
}
|
|
}
|
|
|