Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Editor/AGS.Editor/AGSEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ public class AGSEditor : IAGSEditorDirectories
public const int AGS_4_0_0_XML_VERSION_INDEX_PO_TRANSLATIONS = 3999907;
public const int AGS_4_0_0_XML_VERSION_INDEX_COLORS_32BIT = 4000009;
public const int AGS_4_0_0_XML_VERSION_INDEX_FONT_SOURCES = 4000010;
public const int AGS_4_0_0_XML_VERSION_INDEX_DIALOG_FILES = 4000027;

/*
* LATEST_XML_VERSION is the X.Y.Z.W string which defines project's user data format.
Expand Down
1 change: 1 addition & 0 deletions Editor/AGS.Editor/AGSEditor.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@
<Compile Include="Entities\GenericMessagesArgs.cs" />
<Compile Include="Entities\TreeItemTryDragEventArgs.cs" />
<Compile Include="Entities\UpgradeGame\UpgradeGameCommonTask.cs" />
<Compile Include="Entities\UpgradeGame\UpgradeGameDialogsToIndividualFiles.cs" />
<Compile Include="Entities\UpgradeGame\UpgradeGameEventArgs.cs" />
<Compile Include="Entities\UpgradeGame\UpgradeGameFontsToFontFilesTask.cs" />
<Compile Include="Entities\UpgradeGame\UpgradeGameIntroAndBackupTask.cs" />
Expand Down
81 changes: 76 additions & 5 deletions Editor/AGS.Editor/Components/DialogsComponent.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
using AGS.Editor.TextProcessing;
using AGS.Types;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using AGS.Types;
using AGS.Editor.TextProcessing;

namespace AGS.Editor.Components
{
Expand All @@ -32,7 +33,8 @@ public DialogsComponent(GUIController guiController, AGSEditor agsEditor)
_guiController.ProjectTree.AddTreeRoot(this, TOP_LEVEL_COMMAND_ID, "Dialogs", ICON_KEY);
_guiController.OnZoomToFile += GUIController_OnZoomToFile;
_guiController.OnGetScriptEditorControl += _guiController_OnGetScriptEditorControl;
RePopulateTreeView();
Factory.Events.GamePrepareUpgrade += Events_GamePrepareUpgrade;
RePopulateTreeView();
}

private void _guiController_OnGetScriptEditorControl(GetScriptEditorControlEventArgs evArgs)
Expand All @@ -55,9 +57,9 @@ protected override void ItemCommandClick(string controlID)
{
if (controlID == COMMAND_NEW_ITEM)
{
Dialog newItem = new Dialog();
string name = _agsEditor.GetFirstAvailableScriptName("dDialog");
Dialog newItem = new Dialog(name);
newItem.ID = _agsEditor.CurrentGame.RootDialogFolder.GetAllItemsCount();
newItem.Name = _agsEditor.GetFirstAvailableScriptName("dDialog");
string newNodeID = AddSingleItem(newItem);
_guiController.ProjectTree.SelectNode(this, newNodeID);
ShowPaneForDialog(newItem);
Expand Down Expand Up @@ -413,5 +415,74 @@ protected override IList<Dialog> GetFlatList()
{
return _agsEditor.CurrentGame.DialogFlatList;
}


private void Events_GamePrepareUpgrade(UpgradeGameEventArgs args)
{
args.Tasks.Add(new UpgradeGameDialogsToIndividualFiles(ConvertAllDialogsToIndividualFiles));
}

private void ConvertAllDialogsToIndividualFiles(Game game, IWorkProgress progress, CompileMessages errors)
{
if (_agsEditor.CurrentGame.SavedXmlVersionIndex >= AGSEditor.AGS_4_0_0_XML_VERSION_INDEX_DIALOG_FILES)
return; // already converted

// this logic is convoluted
// a person may have a Dialogs dir with notes or source files
// perhaps only move already existing .asd files?
// need to look carefully into this, to move only the contents that could conflict
if (Directory.Exists(DialogScript.DIALOGUES_DIR))
{
string backupRootDir = Utilities.MakeUniqueDirectory(_agsEditor.CurrentGame.DirectoryPath, DialogScript.DIALOGUES_DIR, "Backup-");

Utilities.SafeMoveDirectoryFiles(DialogScript.DIALOGUES_DIR, backupRootDir);
}

// I think I can just check here if the directory already exists
// then the only way this can fail is some weird permission setting from file system
try
{
if (!Directory.Exists(DialogScript.DIALOGUES_DIR))
{
Directory.CreateDirectory(DialogScript.DIALOGUES_DIR);
}
}
catch (Exception e)
{
errors.Add(new CompileError($"Failed to create directory '{DialogScript.DIALOGUES_DIR}'.", e));
// this probably needs a return but I don't know what a failure here means, is the project left in an intermediary state?
}

// now we need to do the actual conversion, but how do I handle partial conversion?
// I guess this could only happen through power failure? perhaps cycle through all files again and do the save to disk later?
// I also think this issue means I still want a way to load the old stuff in Dialog in ags types...

int totalCount = game.Dialogs.Count;
int convertedCount = 0;

// ACTUALLY FOR THIS TO WORK I GUESS I NEED TO MOVE STUFF OUT OF THE CURRENT DIALOG LOADING?
// I HAVE NO IDEA HOW TO DO THIS
foreach (Dialog dialog in game.Dialogs)
{
try
{
string fileName = DialogScript.GetFileName(dialog.Name);
DialogScript script = new DialogScript(fileName, dialog.Script);
script.SaveToDisk(true);
convertedCount++;
}
catch (Exception e)
{
errors.Add(new CompileError($"Failed to convert dialog '{dialog.Name}'", e));
}
}

// I dont know if I should report these things at all or just a "Done!" message or nothing.
if (convertedCount != totalCount)
{
errors.Add(new CompileWarning($"Converted {convertedCount} of {totalCount} dialogs. Failed to convert {totalCount - convertedCount}."));
}

}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using AGS.Types;

namespace AGS.Editor
{
public class UpgradeGameDialogsToIndividualFiles : IUpgradeGameTask
{
// TODO: revise this later.
// The conversion process is run via a delegate here, because at the time
// I was not certain if it should remain in DialogComponent or not.
// It sort of makes sense, conceptually.
// Also, the conversion calls a number of private methods from DialogComponent,
// therefore this seemed to be the most trivial way to proceed.
public delegate void ConvertDialogs(Game game, IWorkProgress progress, CompileMessages errors);
private ConvertDialogs _convertDialogs;

public UpgradeGameDialogsToIndividualFiles(ConvertDialogs convertDialogs)
{
_convertDialogs = convertDialogs;
Enabled = true;
}

/// <summary>
/// A unique string identifier of this upgrade task.
/// </summary>
public string ID { get { return "UpgradeGameDialogsToIndividualFiles"; } }
/// <summary>
/// An arbitrary title, used to identify this task when
/// presenting to a user.
/// </summary>
public string Title { get { return "Dialogs as Individual Files Outside Project"; } }
/// <summary>
/// An arbitrary description, may contain any amount of text.
/// </summary>
public string Description
{
get
{
return
"Dialog scripts are now stored as separate .asd files in the Dialogs directory. " +
"Previously these dialog scripts were part of the game project file instead." +
Environment.NewLine + Environment.NewLine +
"During this upgrade step AGS your dialogues will be extracted to a new " + DialogScript.DIALOGUES_DIR + " directory in your project.";
}
}
/// <summary>
/// A game project version that introduced this upgrade task.
/// If a loaded game has a less project version, then this task
/// must be applied, otherwise it should not.
/// Returns null if should be applied regardless of the game version
/// (but the execution process may still have version checks inside).
/// </summary>
public System.Version GameVersion { get { return new System.Version(AGSEditor.FIRST_XML_VERSION_USING_INDEX); } }
/// <summary>
/// A game project version in form of a numeric index, for the projects
/// which used these.
/// </summary>
public int? GameVersionIndex { get { return AGSEditor.AGS_4_0_0_XML_VERSION_INDEX_DIALOG_FILES; } }
/// <summary>
/// Tells whether this upgrade task is to be executed unconditionally,
/// without warning user about it.
/// </summary>
public bool Implicit { get { return false; } }
/// <summary>
/// Tells whether this upgrade task may be disabled by user's choice.
/// </summary>
public bool Optional { get { return false; } }
/// <summary>
/// Tells whether the upgrade process is allowed to continue if this
/// task had errors.
/// </summary>
public bool AllowToSkipIfHadErrors { get { return false; } }
/// <summary>
/// Tells whether user should be asked for a confirmation in order to
/// continue the upgrade process in case this task had errors.
/// </summary>
public bool RequestConfirmationOnErrors { get { return false; } }

/// <summary>
/// Whether this task is enabled, otherwise should be skipped.
/// </summary>
public bool Enabled { get; set; }

/// <summary>
/// Provides WizardPage controls used to represent this upgrade task.
/// The page implementation may have this IUpgradeGameTask passed into
/// constructor in order to assign settings right into it.
/// </summary>
public UpgradeGameWizardPage[] CreateWizardPages(Game game)
{
return new UpgradeGameWizardPage[] { new UpdateGameGenericInfoPage(game, this) };
}
/// <summary>
/// Apply task options reading them from the dictionary of key-values.
/// </summary>
public void ApplyOptions(Dictionary<string, string> options)
{
// does not have any options
}
/// <summary>
/// Execute the upgrade task over the given Game project.
/// Fills any errors or warnings into the provided "errors" collection.
/// </summary>
public void Execute(Game game, IWorkProgress progress, CompileMessages errors)
{
_convertDialogs(game, progress, errors);
}
}
}
2 changes: 1 addition & 1 deletion Editor/AGS.Native/agsnative.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2832,7 +2832,7 @@ Game^ import_compiled_game_dta(const AGSString &filename)

for (int i = 0; i < thisgame.numdialog; i++)
{
AGS::Types::Dialog ^newDialog = gcnew AGS::Types::Dialog();
AGS::Types::Dialog ^newDialog = gcnew AGS::Types::Dialog(AGS::Types::DialogScript::GetFileName(TextHelper::ConvertASCII(thisgame.dialogScriptNames[i])));
newDialog->ID = i;
for (int j = 0; j < dialog[i].GetOptionCount(); j++)
{
Expand Down
1 change: 1 addition & 0 deletions Editor/AGS.Types/AGS.Types.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@
<Compile Include="AudioClipFolder.cs" />
<Compile Include="Constants.cs" />
<Compile Include="DebugLog.cs" />
<Compile Include="DialogScript.cs" />
<Compile Include="EditorFeatures\AutoComplete\ScriptDefine.cs" />
<Compile Include="EditorFeatures\AutoComplete\ScriptEnum.cs" />
<Compile Include="EditorFeatures\AutoComplete\ScriptEnumValue.cs" />
Expand Down
61 changes: 47 additions & 14 deletions Editor/AGS.Types/Dialog.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Text;
using System.Xml;
using AGS.Types.Interfaces;
Expand All @@ -13,17 +14,16 @@ public class Dialog : IScript, IToXml, IComparable<Dialog>
private int _id;
private string _name;
private bool _showTextParser;
private string _script;
private DialogScript _script;
private bool _scriptChangedSinceLastCompile;
private string _cachedConvertedScript;
private List<DialogOption> _options = new List<DialogOption>();
private CustomProperties _properties = new CustomProperties(CustomPropertyAppliesTo.Dialogs);

public Dialog()
public Dialog(string name)
{
_script = "// Dialog script file" + Environment.NewLine +
"@S // Dialog startup entry point" + Environment.NewLine +
"return" + Environment.NewLine;
_name = Utilities.ValidateScriptName(name);
_script = DialogScript.CreateDefault(_name);
_cachedConvertedScript = null;
_scriptChangedSinceLastCompile = true;
}
Expand All @@ -35,7 +35,10 @@ public Dialog()
public int ID
{
get { return _id; }
set { _id = value; }
set
{
_id = value;
}
}

[Description("The script name of the dialog")]
Expand All @@ -44,14 +47,17 @@ public int ID
public string Name
{
get { return _name; }
set { _name = Utilities.ValidateScriptName(value); }
set {
_name = Utilities.ValidateScriptName(value);
_script.FileName = DialogScript.GetFileName(_name);
}
}

[Browsable(false)]
public string FileName { get { return "Dialog " + ID; } }
public string FileName { get { return _script.FileName; } }

[Browsable(false)]
public string Text { get { return _script; } }
public string Text { get { return _script.Text; } }

[Browsable(false)]
public ScriptAutoCompleteData AutoCompleteData { get { return null; } }
Expand All @@ -67,14 +73,14 @@ public bool ShowTextParser
[Browsable(false)]
public string Script
{
get { return _script; }
get { return _script.Text; }
set
{
if (_script != value)
if (_script.Text != value)
{
_scriptChangedSinceLastCompile = true;
}
_script = value;
_script.Text = value;
}
}

Expand Down Expand Up @@ -133,7 +139,27 @@ public Dialog(XmlNode node)
_showTextParser = Boolean.Parse(SerializeUtils.GetElementString(node, "ShowTextParser"));
XmlNode scriptNode = node.SelectSingleNode("Script");
// Luckily the CDATA section is easy to read back
_script = scriptNode.InnerText;
// FIX-ME: we will need to figure how to look the .asd file and then if it fails look into the inner text?
// or the reverse? Need to think on this
String fileName = DialogScript.GetFileName(_name);

if (File.Exists(fileName))
{
// read from .asd file
_script = new DialogScript(fileName, "");
_script.LoadFromDisk();
}
else if (!string.IsNullOrEmpty(scriptNode.InnerText))
{
// try the CData?
_script = new DialogScript(fileName, scriptNode.InnerText);
}
else
{
// I don't think we should be here???
_script = DialogScript.CreateDefault(fileName);
}
_script.FileName = fileName;

foreach (XmlNode child in SerializeUtils.GetChildNodes(node, "DialogOptions"))
{
Expand All @@ -143,12 +169,19 @@ public Dialog(XmlNode node)

public void ToXml(XmlTextWriter writer)
{
// lets save the .asd file
_script.SaveToDisk(true);

writer.WriteStartElement("Dialog");
writer.WriteElementString("ID", ID.ToString());
writer.WriteElementString("Name", _name);
writer.WriteElementString("ShowTextParser", _showTextParser.ToString());
writer.WriteStartElement("Script");
writer.WriteCData(_script);
// Actually I am commenting this
// FIX-ME: move this out because the writing will be in a file by DialogScript.
// For now we keep this so things still work.
// writer.WriteCData(_script.Text);
writer.WriteCData(string.Empty);
writer.WriteEndElement();

writer.WriteStartElement("DialogOptions");
Expand Down
Loading
Loading