diff --git a/Common/ac/game_version.h b/Common/ac/game_version.h
index 72553b5e04a..4b8d3f55ee5 100644
--- a/Common/ac/game_version.h
+++ b/Common/ac/game_version.h
@@ -114,6 +114,8 @@ Idle animation speed, modifiable hotspot names, fixed video frame
Some adjustments to gui text alignment.
3.6.1:
In RTL mode all text is reversed, not only wrappable (labels etc).
+3.6.1.10:
+Disabled automatic SetRestartPoint.
*/
@@ -153,7 +155,8 @@ enum GameDataVersion
kGameVersion_360_16 = 3060016,
kGameVersion_360_21 = 3060021,
kGameVersion_361 = 3060100,
- kGameVersion_Current = kGameVersion_361
+ kGameVersion_361_10 = 3060110,
+ kGameVersion_Current = kGameVersion_361_10
};
// Data format version of the loaded game
diff --git a/Editor/AGS.Editor/AGSEditor.cs b/Editor/AGS.Editor/AGSEditor.cs
index c2cec7413db..af7f80fea43 100644
--- a/Editor/AGS.Editor/AGSEditor.cs
+++ b/Editor/AGS.Editor/AGSEditor.cs
@@ -102,8 +102,10 @@ public class AGSEditor
* 3.6.1.2 - GUIListBox.Translated property moved to GUIControl parent
* 3.6.1.3 - RuntimeSetup.TextureCache, SoundCache
* 3.6.1.9 - Settings.ScaleCharacterSpriteOffsets
+ * 3.6.1.10 - SetRestartPoint() is no longer auto called in the engine,
+ * add one into the global script when importing older games.
*/
- public const int LATEST_XML_VERSION_INDEX = 3060109;
+ public const int LATEST_XML_VERSION_INDEX = 3060110;
/*
* LATEST_USER_DATA_VERSION is the last version of the user data file that used a
* 4-point-4-number string to identify the version of AGS that saved the file.
@@ -873,13 +875,16 @@ private Script CompileDialogs(CompileMessages errors, bool rebuildAll)
DialogScriptConverter dialogConverter = new DialogScriptConverter();
string dialogScriptsText = dialogConverter.ConvertGameDialogScripts(_game, errors, rebuildAll);
Script dialogScripts = new Script(Script.DIALOG_SCRIPTS_FILE_NAME, dialogScriptsText, false);
- Script globalScript = _game.RootScriptFolder.GetScriptByFileName(Script.GLOBAL_SCRIPT_FILE_NAME, true);
- if (!System.Text.RegularExpressions.Regex.IsMatch(globalScript.Text, @"function\s+dialog_request\s*\("))
+
+ // A dialog_request must exist in the global script, otherwise
+ // the dialogs script fails to load at run-time
+ // TODO: check if it's still true, and is necessary!
+ Script script = CurrentGame.RootScriptFolder.GetScriptByFileName(Script.GLOBAL_SCRIPT_FILE_NAME, true);
+ if (script != null)
{
- // A dialog_request must exist in the global script, otherwise
- // the dialogs script fails to load at run-time
- globalScript.Text += Environment.NewLine + "function dialog_request(int param) {" + Environment.NewLine + "}";
+ script.Text = ScriptGeneration.InsertFunction(script.Text, "dialog_request", "int param");
}
+
return dialogScripts;
}
diff --git a/Editor/AGS.Editor/AGSEditor.csproj b/Editor/AGS.Editor/AGSEditor.csproj
index 09f0155c6da..eca03473469 100644
--- a/Editor/AGS.Editor/AGSEditor.csproj
+++ b/Editor/AGS.Editor/AGSEditor.csproj
@@ -375,6 +375,7 @@
+
Designer
diff --git a/Editor/AGS.Editor/Components/RoomsComponent.cs b/Editor/AGS.Editor/Components/RoomsComponent.cs
index 50d878396c0..21148a7d520 100644
--- a/Editor/AGS.Editor/Components/RoomsComponent.cs
+++ b/Editor/AGS.Editor/Components/RoomsComponent.cs
@@ -859,32 +859,17 @@ private bool AddPlayMusicCommandToPlayerEntersRoomScript(Room room, CompileMessa
return scriptModified;
}
- string scriptName = room.Interactions.GetScriptFunctionNameForInteractionSuffix(Room.EVENT_SUFFIX_ROOM_LOAD);
- if (string.IsNullOrEmpty(scriptName))
+ string functionName = room.Interactions.GetScriptFunctionNameForInteractionSuffix(Room.EVENT_SUFFIX_ROOM_LOAD);
+ if (string.IsNullOrEmpty(functionName))
{
- scriptName = "Room_" + Room.EVENT_SUFFIX_ROOM_LOAD;
- room.Interactions.SetScriptFunctionNameForInteractionSuffix(Room.EVENT_SUFFIX_ROOM_LOAD, scriptName);
- room.Script.Text += Environment.NewLine + "function " + scriptName + "()" +
- Environment.NewLine + "{" + Environment.NewLine +
- "}" + Environment.NewLine;
- scriptModified = true;
- }
- int functionOffset = room.Script.Text.IndexOf(scriptName);
- if (functionOffset < 0)
- {
- errors.Add(new CompileWarning("Room " + room.Number + ": Unable to find definition for " + scriptName + " to add Room Load music " + room.PlayMusicOnRoomLoad));
- }
- else
- {
- functionOffset = room.Script.Text.IndexOf('{', functionOffset);
- functionOffset = room.Script.Text.IndexOf('\n', functionOffset) + 1;
- string newScript = room.Script.Text.Substring(0, functionOffset);
- newScript += " " + clip.ScriptName + ".Play();" + Environment.NewLine;
- newScript += room.Script.Text.Substring(functionOffset);
- room.Script.Text = newScript;
- room.PlayMusicOnRoomLoad = 0;
- scriptModified = true;
+ functionName = "Room_" + Room.EVENT_SUFFIX_ROOM_LOAD;
+ room.Interactions.SetScriptFunctionNameForInteractionSuffix(Room.EVENT_SUFFIX_ROOM_LOAD, functionName);
}
+
+ room.Script.Text = ScriptGeneration.InsertFunction(room.Script.Text, functionName, "",
+ " " + clip.ScriptName + ".Play();", amendExisting: true, insertBeforeExistingCode: true);
+ room.PlayMusicOnRoomLoad = 0;
+ scriptModified = true;
}
return scriptModified;
diff --git a/Editor/AGS.Editor/Components/ScriptsComponent.cs b/Editor/AGS.Editor/Components/ScriptsComponent.cs
index 5d3ecfea5a1..a4132991b22 100644
--- a/Editor/AGS.Editor/Components/ScriptsComponent.cs
+++ b/Editor/AGS.Editor/Components/ScriptsComponent.cs
@@ -76,6 +76,8 @@ public ScriptsComponent(GUIController guiController, AGSEditor agsEditor)
_guiController.OnScriptChanged += new GUIController.ScriptChangedHandler(GUIController_OnScriptChanged);
_guiController.OnGetScriptEditorControl += new GUIController.GetScriptEditorControlHandler(_guiController_OnGetScriptEditorControl);
_guiController.ProjectTree.OnAfterLabelEdit += new ProjectTree.AfterLabelEditHandler(ProjectTree_OnAfterLabelEdit);
+
+ Factory.Events.GamePostLoad += Events_GamePostLoad;
}
private void _guiController_OnGetScriptEditorControl(GetScriptEditorControlEventArgs evArgs)
@@ -630,5 +632,26 @@ protected override IList GetFlatList()
{
return null;
}
+
+ private void Events_GamePostLoad()
+ {
+ var game = _agsEditor.CurrentGame;
+ if (game.SavedXmlVersionIndex >= 3060110)
+ return; // no upgrade necessary
+
+ // < 3060110 - SetRestartPoint() has to be added to Global Script's game_start,
+ // emulate legacy behavior where its call was hardcoded in the engine.
+ if (game.SavedXmlVersionIndex < 3060110)
+ {
+ Script script = AGSEditor.Instance.CurrentGame.RootScriptFolder.GetScriptByFileName(Script.GLOBAL_SCRIPT_FILE_NAME, true);
+ if (script != null)
+ {
+ script.Text =
+ ScriptGeneration.InsertFunction(script.Text, "game_start", "", " SetRestartPoint();", amendExisting: true);
+ // CHECKME: do not save the script here, in case user made a mistake opening this in a newer editor
+ // and closes project without saving after upgrade? Upgrade process is not well defined...
+ }
+ }
+ }
}
}
diff --git a/Editor/AGS.Editor/GUI/GUIController.cs b/Editor/AGS.Editor/GUI/GUIController.cs
index 47c55291c83..c21063215f3 100644
--- a/Editor/AGS.Editor/GUI/GUIController.cs
+++ b/Editor/AGS.Editor/GUI/GUIController.cs
@@ -1296,18 +1296,11 @@ private void ScriptFunctionUIEditor_CreateScriptFunction(bool isGlobalScript, st
OnGetScript(scriptToRetrieve, ref script);
if (script != null)
{
- string functionStart = "function " + functionName + "(";
- if (script.Text.IndexOf(functionStart) < 0)
+ if (_agsEditor.AttemptToGetWriteAccess(script.FileName))
{
- if (_agsEditor.AttemptToGetWriteAccess(script.FileName))
- {
- script.Text += Environment.NewLine + functionStart + parameters + ")" + Environment.NewLine;
- script.Text += "{" + Environment.NewLine + Environment.NewLine + "}" + Environment.NewLine;
- if (OnScriptChanged != null)
- {
- OnScriptChanged(script);
- }
- }
+ script.Text = ScriptGeneration.InsertFunction(script.Text, functionName, parameters);
+ if (script.Modified)
+ OnScriptChanged?.Invoke(script);
}
}
}
diff --git a/Editor/AGS.Editor/Utils/ScriptGeneration.cs b/Editor/AGS.Editor/Utils/ScriptGeneration.cs
new file mode 100644
index 00000000000..3e357a21cd1
--- /dev/null
+++ b/Editor/AGS.Editor/Utils/ScriptGeneration.cs
@@ -0,0 +1,98 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace AGS.Editor
+{
+ ///
+ /// Helper class ScriptGeneration provides methods for automatic script editing.
+ ///
+ public class ScriptGeneration
+ {
+ ///
+ /// Counts braces starting with the startIndex, and finds the matching closing one.
+ /// Returns an index of a closing brace.
+ ///
+ public static int FindClosingBrace(string text, int startIndex, char braceOpenChar = '{', char braceCloseChar = '}')
+ {
+ // TODO: is it possible to do this using regex?
+ int count = 0;
+ int closeBraceAt = -1;
+ for (int i = startIndex; i < text.Length; ++i)
+ {
+ if (text[i] == braceOpenChar)
+ {
+ count++;
+ }
+ else if (text[i] == braceCloseChar)
+ {
+ count--;
+ if (count == 0)
+ {
+ closeBraceAt = i;
+ break;
+ }
+ }
+ }
+ return closeBraceAt;
+ }
+
+ ///
+ /// Appends a function with optional parameter list and optional contents to a
+ /// script's text.
+ ///
+ /// Tells whether to amend the function code in existing
+ /// function, or only set it if the new function is created.
+ /// Resulting script's text.
+ public static string InsertFunction(string text, string functionName, string paramList = "", string functionCode = "",
+ bool amendExisting = false, bool insertBeforeExistingCode = false)
+ {
+ // TODO: support matching indentation for the new code?
+
+ // NOTE: we must find a function with opening brace, because there may also
+ // be a function prototype somewhere.
+ var match = Regex.Match(text, string.Format(@"function\s+{0}\s*\(.*\)\s*{{", functionName));
+ if (match.Success && (!amendExisting || string.IsNullOrWhiteSpace(functionCode)))
+ return text; // function already exists and don't have to amend
+ if (match.Success)
+ {
+ // Find the required position in the existing function, and insert required code
+ int functionStart = match.Index + match.Length;
+ if (insertBeforeExistingCode)
+ {
+ text = string.Format("{0}{1}{2}{3}{4}",
+ text.Substring(0, functionStart),
+ Environment.NewLine, functionCode, Environment.NewLine,
+ text.Substring(functionStart));
+ }
+ else
+ {
+ int closeBraceAt = FindClosingBrace(text, match.Index);
+ if (closeBraceAt >= 0)
+ {
+ text = string.Format("{0}{1}{2}{3}{4}",
+ text.Substring(0, closeBraceAt),
+ Environment.NewLine, functionCode, Environment.NewLine,
+ text.Substring(closeBraceAt));
+ }
+ else
+ {
+ // Script missing closing brace?
+ text = string.Format("{0}{1}{2}{3}}}",
+ text, Environment.NewLine, functionCode, Environment.NewLine);
+ }
+ }
+ }
+ else
+ {
+ // Add a new function to the end of the script
+ text += string.Format("{0}function {1}({2}){3}{{{4}{5}{6}}}{7}",
+ Environment.NewLine, functionName, paramList, Environment.NewLine, Environment.NewLine,
+ functionCode, Environment.NewLine, Environment.NewLine);
+ }
+ return text;
+ }
+ }
+}
diff --git a/Editor/AGS.Types/Script.cs b/Editor/AGS.Types/Script.cs
index b6076013aaf..798f3a211c3 100644
--- a/Editor/AGS.Types/Script.cs
+++ b/Editor/AGS.Types/Script.cs
@@ -67,7 +67,14 @@ public Script(string fileName, string text, string name, string description, str
public string Text
{
get { return _text; }
- set { _text = value ?? string.Empty; _modified = true; }
+ set
+ {
+ if (_text != value)
+ {
+ _text = value ?? string.Empty;
+ _modified = true;
+ }
+ }
}
[ReadOnly(true)]
diff --git a/Engine/main/game_start.cpp b/Engine/main/game_start.cpp
index 9462ce64cee..28287ee253b 100644
--- a/Engine/main/game_start.cpp
+++ b/Engine/main/game_start.cpp
@@ -72,7 +72,10 @@ void start_game() {
our_eip = -43;
- SetRestartPoint();
+ // Only auto-set first restart point in < 3.6.1 games,
+ // since 3.6.1+ users are suggested to set one manually in script.
+ if (loaded_game_file_version < kGameVersion_361_10)
+ SetRestartPoint();
our_eip=-3;