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: {}