Browse Source

Moving TextVariationHandler to a tokeniser rather than regex so we can manage recursive variation sections.

master
desktop-maesty/steve 7 years ago
parent
commit
f676198032
  1. 1
      Assets/Fungus/Scripts/Commands/Menu.cs
  2. 187
      Assets/Fungus/Scripts/Utils/TextVariationHandler.cs
  3. 10
      Assets/FungusExamples/VariationText/TextVariation.unity

1
Assets/Fungus/Scripts/Commands/Menu.cs

@ -17,6 +17,7 @@ namespace Fungus
public class Menu : Command, ILocalizable public class Menu : Command, ILocalizable
{ {
[Tooltip("Text to display on the menu button")] [Tooltip("Text to display on the menu button")]
[TextArea()]
[SerializeField] protected string text = "Option Text"; [SerializeField] protected string text = "Option Text";
[Tooltip("Notes about the option text for other authors, localization, etc.")] [Tooltip("Notes about the option text for other authors, localization, etc.")]

187
Assets/Fungus/Scripts/Utils/TextVariationHandler.cs

@ -14,6 +14,8 @@ namespace Fungus
/// Default behaviour is to show one element after another and hold the final element. Such that [a|b|c] will show /// Default behaviour is to show one element after another and hold the final element. Such that [a|b|c] will show
/// a the first time it is parsed, b the second and every subsequent time it will show c. /// a the first time it is parsed, b the second and every subsequent time it will show c.
/// ///
/// Empty sections are allowed, such that [a||c], on second showing it will have 0 characters.
///
/// This behaviour can be modified with certain characters at the start of the [], eg. [&a|b|c]; /// This behaviour can be modified with certain characters at the start of the [], eg. [&a|b|c];
/// - & does not hold the final element it wraps back around to the begining in a looping fashion /// - & does not hold the final element it wraps back around to the begining in a looping fashion
/// - ! does not hold the final element, it instead returns empty for the varying section /// - ! does not hold the final element, it instead returns empty for the varying section
@ -21,12 +23,11 @@ namespace Fungus
/// </summary> /// </summary>
public static class TextVariationHandler public static class TextVariationHandler
{ {
static Dictionary<int, int> hashedSections = new Dictionary<int, int>(); public class Section
static StringBuilder sb = new StringBuilder(); {
const string pattern = @"\[([^]]+?)\]"; public VaryType type = VaryType.Sequence;
static Regex r = new Regex(pattern);
private enum VaryType public enum VaryType
{ {
Sequence, Sequence,
Cycle, Cycle,
@ -34,45 +35,157 @@ namespace Fungus
Random Random
} }
static public string SelectVariations(string input) public string entire = string.Empty;
public List<string> elements = new List<string>();
public string Select(ref int index)
{ {
sb.Length = 0; switch (type)
sb.Append(input); {
case VaryType.Sequence:
index = UnityEngine.Mathf.Min(index, elements.Count - 1);
break;
case VaryType.Cycle:
index = index % elements.Count;
break;
case VaryType.Once:
//clamp to 1 more than options
index = UnityEngine.Mathf.Min(index, elements.Count);
break;
case VaryType.Random:
index = UnityEngine.Random.Range(0, elements.Count);
break;
default:
break;
}
// Match the regular expression pattern against a text string. if (index >= 0 && index < elements.Count)
var results = r.Matches(input); return elements[index];
for (int i = 0; i < results.Count; i++)
return string.Empty;
}
}
static Dictionary<int, int> hashedSections = new Dictionary<int, int>();
//static StringBuilder sb = new StringBuilder();
//const string pattern = @"\[([^]]+?)\]";
//static Regex r = new Regex(pattern);
/// <summary>
/// Simple parser to extract depth matched [].
///
/// Such that a string of "[Hail and well met|Hello|[Good |]Morning] Traveler" will return
/// "[Hail and well met|Hello|[Good |]Morning]"
/// and string of "Hail and well met|Hello|[Good |]Morning"
/// will return [Good |]
/// </summary>
/// <param name="input"></param>
/// <param name="varyingSections"></param>
/// <returns></returns>
static public bool TokenizeVarySections(string input, List<Section> varyingSections)
{ {
Match match = results[i]; varyingSections.Clear();
int currentDepth = 0;
int curStartIndex = 0;
int curPipeIndex = 0;
Section curSection = null;
//determine type for (int i = 0; i < input.Length; i++)
VaryType t = VaryType.Sequence; {
switch (input[i])
{
case '[':
if (currentDepth == 0)
{
curSection = new Section();
varyingSections.Add(curSection);
var typedIndicatingChar = match.Value[1]; //determine type and skip control char
var typedIndicatingChar = input[i + 1];
switch (typedIndicatingChar) switch (typedIndicatingChar)
{ {
case '~': case '~':
t = VaryType.Random; curSection.type = Section.VaryType.Random;
break; break;
case '&': case '&':
t = VaryType.Cycle; curSection.type = Section.VaryType.Cycle;
break; break;
case '!': case '!':
t = VaryType.Once; curSection.type = Section.VaryType.Once;
break;
default:
break;
}
//mark start
curStartIndex = i;
curPipeIndex = i + 1;
}
currentDepth++;
break;
case ']':
if (currentDepth == 1)
{
//extract, including the ]
curSection.entire = input.Substring(curStartIndex, i - curStartIndex + 1);
//close an element if we started one
if (curStartIndex != curPipeIndex - 1)
{
curSection.elements.Add(input.Substring(curPipeIndex, i - curPipeIndex));
}
//if has control var, clean first element
if(curSection.type != Section.VaryType.Sequence)
{
curSection.elements[0] = curSection.elements[0].Substring(1);
}
}
currentDepth--;
break;
case '|':
if (currentDepth == 1)
{
//split
curSection.elements.Add(input.Substring(curPipeIndex, i - curPipeIndex));
//over the | on the next one
curPipeIndex = i + 1;
}
break; break;
default: default:
break; break;
} }
}
return varyingSections.Count > 0;
}
//explode and remove the control char static public string SelectVariations(string input)
int startSubStrIndex = t != VaryType.Sequence ? 2 : 1; {
int subStrLen = match.Value.Length - 1 - startSubStrIndex; // Match the regular expression pattern against a text string.
var exploded = match.Value.Substring(startSubStrIndex, subStrLen).Split('|'); List<Section> sections = new List<Section>();
bool foundSections = TokenizeVarySections(input, sections);
if (!foundSections)
return input;
StringBuilder sb = new StringBuilder();
sb.Length = 0;
sb.Append(input);
for (int i = 0; i < sections.Count; i++)
{
var curSection = sections[i];
string selected = string.Empty; string selected = string.Empty;
//fetched hashed value //fetched hashed value
int index = -1; int index = -1;
int key = input.GetHashCode() ^ match.Value.GetHashCode(); int key = input.GetHashCode() ^ curSection.entire.GetHashCode();
int foundVal = 0; int foundVal = 0;
if (hashedSections.TryGetValue(key, out foundVal)) if (hashedSections.TryGetValue(key, out foundVal))
@ -82,34 +195,16 @@ namespace Fungus
index++; index++;
switch (t) selected = curSection.Select(ref index);
{
case VaryType.Sequence:
index = UnityEngine.Mathf.Min(index, exploded.Length - 1);
break;
case VaryType.Cycle:
index = index % exploded.Length;
break;
case VaryType.Once:
//clamp to 1 more than options
index = UnityEngine.Mathf.Min(index, exploded.Length);
break;
case VaryType.Random:
index = UnityEngine.Random.Range(0, exploded.Length - 1);
break;
default:
break;
}
//update hashed value //update hashed value
hashedSections[key] = index; hashedSections[key] = index;
//selected updated if valid //handle sub vary within selected section
if(index >=0 && index < exploded.Length) selected = SelectVariations(selected);
selected = exploded[index];
sb.Replace(match.Value, selected); //update with selecton
sb.Replace(curSection.entire, selected);
} }
return sb.ToString(); return sb.ToString();

10
Assets/FungusExamples/VariationText/TextVariation.unity

@ -942,8 +942,10 @@ MonoBehaviour:
y: -340 y: -340
width: 1114 width: 1114
height: 859 height: 859
selectedBlocks: [] selectedBlocks:
selectedCommands: [] - {fileID: 1755499608}
selectedCommands:
- {fileID: 1755499607}
variables: [] variables: []
description: description:
stepPause: 0 stepPause: 0
@ -973,9 +975,9 @@ MonoBehaviour:
stringVal: 'john calling-neutral left: [Good morning, it''s John|Hmm|Yes, still stringVal: 'john calling-neutral left: [Good morning, it''s John|Hmm|Yes, still
here]. here].
sherlock explaining right: [Who are you talking to?|Still at it, I see.] We sherlock explaining right: [!Who are you talking to?||Still at it, I see.] We
have a case to [~deal with|work|tend to|solve]. [!I''m right as rain.|Stop fussing have a case to [~deal with|work|tend to|solve]. [!I''m right as rain.|Stop fussing
will you.]' will you.|||Rude.] '
clearPrevious: clearPrevious:
booleanRef: {fileID: 0} booleanRef: {fileID: 0}
booleanVal: 1 booleanVal: 1

Loading…
Cancel
Save