diff --git a/Assets/Fungus/Flowchart/Scripts/Flowchart.cs b/Assets/Fungus/Flowchart/Scripts/Flowchart.cs
index 2129741a..4597e734 100644
--- a/Assets/Fungus/Flowchart/Scripts/Flowchart.cs
+++ b/Assets/Fungus/Flowchart/Scripts/Flowchart.cs
@@ -2,6 +2,7 @@ using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Serialization;
using System;
+using System.Text;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
@@ -968,15 +969,15 @@ namespace Fungus
* use the SubstituteVariables() method instead.
*/
[MoonSharp.Interpreter.MoonSharpHidden]
- public virtual string SubstituteStrings(string input)
+ public virtual bool SubstituteStrings(StringBuilder input)
{
- string subbedText = input;
-
// Instantiate the regular expression object.
Regex r = new Regex("{\\$.*?}");
+ bool modified = false;
+
// Match the regular expression pattern against a text string.
- var results = r.Matches(input);
+ var results = r.Matches(input.ToString());
foreach (Match match in results)
{
string key = match.Value.Substring(2, match.Value.Length - 3);
@@ -991,12 +992,14 @@ namespace Fungus
variable.key == key)
{
string value = variable.ToString();
- subbedText = subbedText.Replace(match.Value, value);
+ input.Replace(match.Value, value);
+
+ modified = true;
}
}
}
- return subbedText;
+ return modified;
}
/**
@@ -1012,8 +1015,11 @@ namespace Fungus
stringSubstituer = new StringSubstituter();
}
- string subbedText = input;
-
+ // Use the string builder from StringSubstituter for efficiency.
+ StringBuilder sb = stringSubstituer.stringBuilder;
+ sb.Length = 0;
+ sb.Append(input);
+
// Instantiate the regular expression object.
Regex r = new Regex("{\\$.*?}");
@@ -1033,13 +1039,20 @@ namespace Fungus
variable.key == key)
{
string value = variable.ToString();
- subbedText = subbedText.Replace(match.Value, value);
+ sb.Replace(match.Value, value);
}
}
}
// Now do all other substitutions in the scene
- return stringSubstituer.SubstituteStrings(subbedText);
+ if (stringSubstituer.SubstituteStrings(sb))
+ {
+ return sb.ToString();
+ }
+ else
+ {
+ return input;
+ }
}
}
diff --git a/Assets/Fungus/Narrative/Scripts/Localization.cs b/Assets/Fungus/Narrative/Scripts/Localization.cs
index 84e2c494..c37eecf2 100644
--- a/Assets/Fungus/Narrative/Scripts/Localization.cs
+++ b/Assets/Fungus/Narrative/Scripts/Localization.cs
@@ -6,6 +6,7 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
+using System.Text;
using System.IO;
using Ideafixxxer.CsvParser;
@@ -525,19 +526,19 @@ namespace Fungus
* Implementation of StringSubstituter.ISubstitutionHandler.
* Relaces tokens of the form {$KeyName} with the localized value corresponding to that key.
*/
- public virtual string SubstituteStrings(string input)
+ public virtual bool SubstituteStrings(StringBuilder input)
{
// This method could be called from the Start method of another component, so we
// may need to initilize the localization system.
Init();
- string subbedText = input;
-
// Instantiate the regular expression object.
Regex r = new Regex("{\\$.*?}");
+ bool modified = false;
+
// Match the regular expression pattern against a text string.
- var results = r.Matches(input);
+ var results = r.Matches(input.ToString());
foreach (Match match in results)
{
string key = match.Value.Substring(2, match.Value.Length - 3);
@@ -546,11 +547,12 @@ namespace Fungus
string localizedString = Localization.GetLocalizedString(key);
if (localizedString != null)
{
- subbedText = subbedText.Replace(match.Value, localizedString);
+ input.Replace(match.Value, localizedString);
+ modified = true;
}
}
- return subbedText;
+ return modified;
}
}
diff --git a/Assets/Fungus/Thirdparty/FungusLua/Scripts/LuaUtils.cs b/Assets/Fungus/Thirdparty/FungusLua/Scripts/LuaUtils.cs
index dd6bc8ef..a154eda3 100644
--- a/Assets/Fungus/Thirdparty/FungusLua/Scripts/LuaUtils.cs
+++ b/Assets/Fungus/Thirdparty/FungusLua/Scripts/LuaUtils.cs
@@ -3,6 +3,7 @@ using System.Collections;
using System.Collections.Generic;
using System;
using System.Linq;
+using System.Text;
using System.Diagnostics;
using System.Text.RegularExpressions;
using MoonSharp.Interpreter;
@@ -319,7 +320,7 @@ namespace Fungus
/// The string table value used depends on the currently loaded string table and active language.
///
[MoonSharpHidden]
- public virtual string SubstituteStrings(string input)
+ public virtual bool SubstituteStrings(StringBuilder input)
{
// This method could be called from the Start of another component, so
// we need to ensure that the LuaEnvironment has been initialized.
@@ -333,26 +334,26 @@ namespace Fungus
}
if (luaEnvironment == null)
- {
+ {
UnityEngine.Debug.LogError("No Lua Environment found");
- return input;
+ return false;
}
if (luaEnvironment.Interpreter == null)
{
UnityEngine.Debug.LogError("No Lua interpreter found");
- return input;
+ return false;
}
MoonSharp.Interpreter.Script interpreter = luaEnvironment.Interpreter;
- string subbedText = input;
-
// Instantiate the regular expression object.
Regex r = new Regex("\\{\\$.*?\\}");
+ bool modified = false;
+
// Match the regular expression pattern against a text string.
- var results = r.Matches(input);
+ var results = r.Matches(input.ToString());
foreach (Match match in results)
{
string key = match.Value.Substring(2, match.Value.Length - 3);
@@ -366,7 +367,8 @@ namespace Fungus
DynValue languageEntry = stringTableVar.Table.Get(activeLanguage);
if (languageEntry.Type == DataType.String)
{
- subbedText = subbedText.Replace(match.Value, languageEntry.String);
+ input.Replace(match.Value, languageEntry.String);
+ modified = true;
}
continue;
}
@@ -376,12 +378,13 @@ namespace Fungus
DynValue globalVar = interpreter.Globals.Get(key);
if (globalVar.Type != DataType.Nil)
{
- subbedText = subbedText.Replace(match.Value, globalVar.ToPrintString());
+ input.Replace(match.Value, globalVar.ToPrintString());
+ modified = true;
continue;
}
}
- return subbedText;
+ return modified;
}
///
@@ -390,7 +393,7 @@ namespace Fungus
///
public virtual string Substitute(string input)
{
- return stringSubstituter.SubstituteStrings(input);
+ return stringSubstituter.SubstituteStrings(input);
}
///
diff --git a/Assets/Fungus/Thirdparty/FungusLua/Scripts/StringSubstituter.cs b/Assets/Fungus/Thirdparty/FungusLua/Scripts/StringSubstituter.cs
index 47de117d..3ee4a4ba 100644
--- a/Assets/Fungus/Thirdparty/FungusLua/Scripts/StringSubstituter.cs
+++ b/Assets/Fungus/Thirdparty/FungusLua/Scripts/StringSubstituter.cs
@@ -17,21 +17,33 @@ namespace Fungus
public interface ISubstitutionHandler
{
///
- /// Returns a new string with tokens replaced by subsituted values.
+ /// Modifies a StringBuilder so that tokens are replaced by subsituted values.
/// It's up to clients how to implement substitution but the convention looks like:
/// "Hi {$VarName}" => "Hi John" where VarName == "John"
+ /// True if the input was modified
///
- string SubstituteStrings(string input);
+ bool SubstituteStrings(StringBuilder input);
}
protected List substitutionHandlers = new List();
+ /**
+ * The StringBuilder instance used to substitute strings optimally.
+ * This property is public to support client code optimisations.
+ */
+ public StringBuilder stringBuilder;
+
+ private int recursionDepth;
+
///
/// Constructor which caches all components in the scene that implement ISubstitutionHandler.
+ /// Number of levels of recursively embedded keys to resolve.
///
- public StringSubstituter()
+ public StringSubstituter(int recursionDepth = 5)
{
CacheSubstitutionHandlers();
+ stringBuilder = new StringBuilder(1024);
+ this.recursionDepth = recursionDepth;
}
///
@@ -64,28 +76,48 @@ namespace Fungus
///
public virtual string SubstituteStrings(string input)
{
- string newString = input;
+ stringBuilder.Length = 0;
+ stringBuilder.Append(input);
+
+ if (SubstituteStrings(stringBuilder))
+ {
+ return stringBuilder.ToString();
+ }
+ else
+ {
+ return input; // String wasn't modified
+ }
+ }
+
+ public virtual bool SubstituteStrings(StringBuilder input)
+ {
+ bool result = false;
// Perform the substitution multiple times to expand nested keys
- int lasthash = 0;
- int currenthash = -1;
int loopCount = 0; // Avoid infinite recursion loops
-
- while (lasthash != currenthash &&
- loopCount < 5)
+ while (loopCount < recursionDepth)
{
- lasthash = newString.GetHashCode();
- foreach (ISubstitutionHandler handler in substitutionHandlers)
- {
- newString = handler.SubstituteStrings(newString);
- }
- currenthash = newString.GetHashCode();
+ bool modified = false;
+ foreach (ISubstitutionHandler handler in substitutionHandlers)
+ {
+ if (handler.SubstituteStrings(input))
+ {
+ modified = true;
+ result = true;
+ }
+ }
+
+ if (!modified)
+ {
+ break;
+ }
loopCount++;
}
- return newString;
- }
+ return result;
+ }
+
}
}
\ No newline at end of file
diff --git a/Assets/Tests/StringSubstitution/StringSubstitutionTests.unity b/Assets/Tests/StringSubstitution/StringSubstitutionTests.unity
index b0b275a4..edb7a089 100644
--- a/Assets/Tests/StringSubstitution/StringSubstitutionTests.unity
+++ b/Assets/Tests/StringSubstitution/StringSubstitutionTests.unity
@@ -1427,6 +1427,7 @@ MonoBehaviour:
- Fungus.Command, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
- Fungus.CommandInfoAttribute, Assembly-CSharp, Version=0.0.0.0, Culture=neutral,
PublicKeyToken=null
+ - System.Text.StringBuilder, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
- UnityEngine.Rect, UnityEngine, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
boundObjects:
- key: text
@@ -1592,7 +1593,7 @@ GameObject:
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
- m_IsActive: 1
+ m_IsActive: 0
--- !u!114 &1475498171
MonoBehaviour:
m_ObjectHideFlags: 0
diff --git a/ProjectSettings/ProjectSettings.asset b/ProjectSettings/ProjectSettings.asset
index 7f8ad0fe..3e69da49 100644
--- a/ProjectSettings/ProjectSettings.asset
+++ b/ProjectSettings/ProjectSettings.asset
@@ -117,6 +117,8 @@ PlayerSettings:
m_Bits: 238
iPhoneSdkVersion: 988
iPhoneTargetOSVersion: 22
+ tvOSSdkVersion: 0
+ tvOSTargetOSVersion: 900
uIPrerenderedIcon: 0
uIRequiresPersistentWiFi: 0
uIRequiresFullScreen: 1
@@ -199,6 +201,7 @@ PlayerSettings:
wiiUSystemHeapSize: 128
wiiUTVStartupScreen: {fileID: 0}
wiiUGamePadStartupScreen: {fileID: 0}
+ wiiUDrcBufferDisabled: 0
wiiUProfilerLibPath:
actionOnDotNetUnhandledException: 1
enableInternalProfiler: 0
@@ -266,6 +269,7 @@ PlayerSettings:
ps4DownloadDataSize: 0
ps4GarlicHeapSize: 2048
ps4Passcode: 5PN2qmWqBlQ9wQj99nsQzldVI5ZuGXbE
+ ps4UseDebugIl2cppLibs: 0
ps4pnSessions: 1
ps4pnPresence: 1
ps4pnFriends: 1
@@ -331,6 +335,7 @@ PlayerSettings:
psp2UseLibLocation: 0
psp2InfoBarOnStartup: 0
psp2InfoBarColor: 0
+ psp2UseDebugIl2cppLibs: 0
psmSplashimage: {fileID: 0}
spritePackerPolicy:
scriptingDefineSymbols: {}