private void ReconcilePassageReferences(TwisonStory story) { foreach (var passage in story.passages) { foreach (var link in passage.links) { var matchingPassage = story.passages.FirstOrDefault(i => i.pid == link.pid); if (matchingPassage == null) { throw new TwisonValidationError($"No passage to match link PID {link.pid}", link); } link.passage = matchingPassage; } } // Try to resolve start node, if one is set if (!string.IsNullOrWhiteSpace(story.startnode)) { story.startNodePassage = story.passages.FirstOrDefault(i => i.pid == story.startnode); if (story.startNodePassage == null) { throw new TwisonValidationError($"No passage to match startnode PID {story.startnode}", story); } } }
/// <summary> /// Remove newlines around the edges of passages. /// </summary> public void TrimPassages(TwisonStory story) { foreach (var passage in story.passages) { passage.text = passage.text.Trim(); } }
private void Default(TwisonStory story, Func <TwisonStory, bool> condition, Action <TwisonStory> applyDefault) { if (!condition(story)) { applyDefault(story); } }
/// <summary> /// Unity can't really render [[foo->bar]] as clickable links. /// The link data is already saved in the links object. /// This function removes any links in the text for each passage. /// </summary> public void RemoveLinksFromPassages(TwisonStory story) { var regex = new Regex("\\[\\[[^\\]]+\\]\\]"); foreach (var passage in story.passages) { passage.text = regex.Replace(passage.text, ""); } }
private void OnStartRuntime(TwisonStory story, bool enterDefaultStartNode) { _active = null; _activeLinks = ResolveActiveLinks(); if (enterDefaultStartNode) { GoToPassage(story.startNodePassage, false); } }
/// <summary> /// When pruning links in a story, it is common to end up with a fragment like: /// Hello\n[[foo->bar]]\n[[foo2->bar2]]\n... /// /// Which maps as: /// Hello\n\n\n... /// /// This function will trim out duplicate line sequences and replace them. /// Sequences longer than maxNewLineChain are reduced to that length. Sequences /// shorter are ignored. /// </summary> public void CollapseNewLines(TwisonStory story, int maxNewLineChain) { var pattern = ""; for (var i = 0; i < maxNewLineChain; i++) { pattern += "\n"; } var regex = new Regex($"{pattern}\n*"); foreach (var passage in story.passages) { passage.text = regex.Replace(passage.text, pattern); } }
private void ApplyOptions(TwisonStory story, TwisonLoaderOptions options) { var utility = new TwisonUtility(); if (options.removeLinkTextFromPassages) { utility.RemoveLinksFromPassages(story); } if (options.collapseNewLines) { utility.CollapseNewLines(story, options.maxNewLineChain); } if (options.trimPassages) { utility.TrimPassages(story); } }
private void ValidateStory(TwisonStory story) { Default(story, (s) => s.passages != null, (s) => s.passages = new List <TwisonPassage>()); Default(story, (s) => s.name != null, (s) => s.name = Guid.NewGuid().ToString()); Default(story, (s) => s.creator != null, (s) => s.creator = ""); Default(story, (s) => s.ifid != null, (s) => s.ifid = Guid.NewGuid().ToString()); Default(story, (s) => s.creatorVersion != null, (s) => s.creatorVersion = ""); Default(story, (s) => s.startnode != null, (s) => s.startnode = ""); foreach (var passage in story.passages) { Default(passage, (p) => p.name != null, (p) => p.name = Guid.NewGuid().ToString()); Default(passage, (p) => p.text != null, (p) => p.text = ""); Default(passage, (p) => p.tags != null, (p) => p.tags = new List <string>()); Default(passage, (p) => p.links != null, (p) => p.links = new List <TwisonLink>()); Guard(passage, (p) => !string.IsNullOrWhiteSpace(p.pid), (p) => $"Invalid passage, no PID for {p.name}"); foreach (var link in passage.links) { Default(link, (l) => l.name != null, (l) => l.name = ""); Default(link, (l) => l.link != null, (l) => l.link = ""); Guard(link, (l) => !string.IsNullOrWhiteSpace(l.pid), (l) => $"Invalid passage in link, no PID for {l.name}"); } } }
public TwisonRuntime(TwisonStory story, ITwisonRuntimeBehaviour runtimeBehaviour, bool enterDefaultStartNode) { _story = story; _runtimeBehaviour = runtimeBehaviour; OnStartRuntime(story, enterDefaultStartNode); }