Browse Source
We’ve decided against using the Fountain format for text import / export as it’s too easy for users to incorrectly format the text.master
chrisgregan
10 years ago
4 changed files with 0 additions and 299 deletions
@ -1,187 +0,0 @@ |
|||||||
using UnityEditor; |
|
||||||
using UnityEngine; |
|
||||||
using System.Collections; |
|
||||||
using System.Collections.Generic; |
|
||||||
using System.Linq; |
|
||||||
using System.Reflection; |
|
||||||
using System.IO; |
|
||||||
|
|
||||||
namespace Fungus |
|
||||||
{ |
|
||||||
|
|
||||||
/** |
|
||||||
* Import and export a Fungus story in the .fountain screenplay format. |
|
||||||
* The exported file contains special tags in note blocks which map the |
|
||||||
* story text to the corresponding commands. |
|
||||||
*/ |
|
||||||
public class FountainExporter |
|
||||||
{ |
|
||||||
|
|
||||||
public static void ExportStrings(Flowchart flowchart) |
|
||||||
{ |
|
||||||
if (flowchart == null) |
|
||||||
{ |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
string path = EditorUtility.SaveFilePanel("Export strings", "", |
|
||||||
flowchart.name + ".txt", ""); |
|
||||||
|
|
||||||
if(path.Length == 0) |
|
||||||
{ |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
// Write out character names |
|
||||||
|
|
||||||
string exportText = "Title: " + flowchart.name + "\n"; |
|
||||||
exportText += "Draft date: " + System.DateTime.Today.ToString("d") + "\n"; |
|
||||||
exportText += "\n"; |
|
||||||
|
|
||||||
// In every block, write out Say & Menu text in order |
|
||||||
Block[] blocks = flowchart.GetComponentsInChildren<Block>(); |
|
||||||
foreach (Block block in blocks) |
|
||||||
{ |
|
||||||
// Check for any Say, Menu or Comment commands |
|
||||||
bool hasText = false; |
|
||||||
foreach (Command c in block.commandList) |
|
||||||
{ |
|
||||||
System.Type t = c.GetType(); |
|
||||||
if (t == typeof(Say) || |
|
||||||
t == typeof(Menu) || |
|
||||||
t == typeof(Comment)) |
|
||||||
{ |
|
||||||
hasText = true; |
|
||||||
} |
|
||||||
} |
|
||||||
if (!hasText) |
|
||||||
{ |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
exportText += "." + block.blockName.ToUpper() + "\n\n"; |
|
||||||
|
|
||||||
foreach (Command c in block.commandList) |
|
||||||
{ |
|
||||||
if (c.GetType() == typeof(Say)) |
|
||||||
{ |
|
||||||
string idText = ""; |
|
||||||
Say say = c as Say; |
|
||||||
|
|
||||||
if (say.character == null) |
|
||||||
{ |
|
||||||
exportText += "NO CHARACTER\n"; |
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
exportText += say.character.nameText.ToUpper() + "\n"; |
|
||||||
} |
|
||||||
|
|
||||||
idText += "[[Say," + c.itemId + "]]\n"; |
|
||||||
|
|
||||||
exportText += idText; |
|
||||||
|
|
||||||
// Fountain requires blank dialogue lines to contain 2 spaces or else |
|
||||||
// they will be interpreted as ACTION text. |
|
||||||
string trimmedText = say.storyText.Trim(); |
|
||||||
string[] lines = trimmedText.Split(new [] { '\r', '\n' }); |
|
||||||
foreach (string line in lines) |
|
||||||
{ |
|
||||||
string trimmed = line.Trim(); |
|
||||||
if (line.Length == 0) |
|
||||||
{ |
|
||||||
exportText += " \n"; |
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
exportText += trimmed + "\n"; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
exportText += "\n"; |
|
||||||
} |
|
||||||
else if (c.GetType() == typeof(Menu)) |
|
||||||
{ |
|
||||||
exportText += "MENU\n"; |
|
||||||
|
|
||||||
string idText = ""; |
|
||||||
Menu menu = c as Menu; |
|
||||||
idText += "[[Menu," + c.itemId + "]]\n"; |
|
||||||
|
|
||||||
exportText += idText + menu.text.Trim() + "\n\n"; |
|
||||||
} |
|
||||||
else if (c.GetType() == typeof(Comment)) |
|
||||||
{ |
|
||||||
string idText = ""; |
|
||||||
Comment comment = c as Comment; |
|
||||||
idText += "[[Comment," + c.itemId + "]]\n"; |
|
||||||
|
|
||||||
exportText += idText + comment.commentText.Trim() + "\n\n"; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
File.WriteAllText(path, exportText); |
|
||||||
} |
|
||||||
|
|
||||||
public static void ImportStrings(Flowchart flowchart) |
|
||||||
{ |
|
||||||
string path = EditorUtility.OpenFilePanel("Import strings", "", ""); |
|
||||||
|
|
||||||
if(path.Length == 0) |
|
||||||
{ |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
string stringsFile = File.ReadAllText(path); |
|
||||||
|
|
||||||
StringsParser parser = new StringsParser(); |
|
||||||
List<StringsParser.StringItem> items = parser.ProcessText(stringsFile); |
|
||||||
|
|
||||||
// Build dict of commands |
|
||||||
Dictionary<int, Command> commandDict = new Dictionary<int, Command>(); |
|
||||||
foreach (Command c in flowchart.gameObject.GetComponentsInChildren<Command>()) |
|
||||||
{ |
|
||||||
commandDict.Add (c.itemId, c); |
|
||||||
} |
|
||||||
|
|
||||||
foreach (StringsParser.StringItem item in items) |
|
||||||
{ |
|
||||||
if (item.parameters.Length != 2) |
|
||||||
{ |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
string stringType = item.parameters[0]; |
|
||||||
if (stringType == "Say") |
|
||||||
{ |
|
||||||
int itemId = int.Parse(item.parameters[1]); |
|
||||||
Say sayCommand = commandDict[itemId] as Say; |
|
||||||
if (sayCommand != null) |
|
||||||
{ |
|
||||||
sayCommand.storyText = item.bodyText; |
|
||||||
} |
|
||||||
} |
|
||||||
else if (stringType == "Menu") |
|
||||||
{ |
|
||||||
int itemId = int.Parse(item.parameters[1]); |
|
||||||
Menu menuCommand = commandDict[itemId] as Menu; |
|
||||||
if (menuCommand != null) |
|
||||||
{ |
|
||||||
menuCommand.text = item.bodyText; |
|
||||||
} |
|
||||||
} |
|
||||||
else if (stringType == "Comment") |
|
||||||
{ |
|
||||||
int itemId = int.Parse(item.parameters[1]); |
|
||||||
Comment commentCommand = commandDict[itemId] as Comment; |
|
||||||
if (commentCommand != null) |
|
||||||
{ |
|
||||||
commentCommand.commentText = item.bodyText; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,96 +0,0 @@ |
|||||||
using UnityEngine; |
|
||||||
using System.Collections; |
|
||||||
using System.Collections.Generic; |
|
||||||
using System.Text.RegularExpressions; |
|
||||||
using Fungus; |
|
||||||
|
|
||||||
namespace Fungus |
|
||||||
{ |
|
||||||
/** |
|
||||||
* Parses an exported strings file using the Fountain file format for screenplays |
|
||||||
* See http://fountain.io for details. |
|
||||||
* We only support a small subset of Fountain markup, and use note tags to embed meta data to |
|
||||||
* bind dialogue text to the corresponding Say / Menu commands. |
|
||||||
*/ |
|
||||||
public class StringsParser |
|
||||||
{ |
|
||||||
public class StringItem |
|
||||||
{ |
|
||||||
public string[] parameters; |
|
||||||
public string bodyText; |
|
||||||
} |
|
||||||
|
|
||||||
public virtual List<StringItem> ProcessText(string text) |
|
||||||
{ |
|
||||||
List<StringItem> items = new List<StringItem>(); |
|
||||||
|
|
||||||
// Split text into lines. Add a newline at end to ensure last command is always parsed |
|
||||||
string[] lines = Regex.Split(text + "\n", "(?<=\n)"); |
|
||||||
|
|
||||||
int i = 0; |
|
||||||
while (i < lines.Length) |
|
||||||
{ |
|
||||||
string line = lines[i].Trim(); |
|
||||||
|
|
||||||
if (!(line.StartsWith("[[") && line.EndsWith("]]"))) |
|
||||||
{ |
|
||||||
i++; |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
string blockTag = line.Substring(2, line.Length - 4); |
|
||||||
|
|
||||||
// Find next empty line, #, [[ or eof |
|
||||||
int start = i + 1; |
|
||||||
int end = lines.Length - 1; |
|
||||||
for (int j = start; j <= end; ++j) |
|
||||||
{ |
|
||||||
string line2 = lines[j].Trim(); |
|
||||||
|
|
||||||
if (line2.Length == 0 || |
|
||||||
line2.StartsWith("#") || |
|
||||||
line2.StartsWith("[[")) |
|
||||||
{ |
|
||||||
end = j; |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if (end > start) |
|
||||||
{ |
|
||||||
string blockBuffer = ""; |
|
||||||
for (int j = start; j <= end; ++j) |
|
||||||
{ |
|
||||||
blockBuffer += lines[j].Trim() + "\n"; |
|
||||||
} |
|
||||||
|
|
||||||
blockBuffer = blockBuffer.Trim(); |
|
||||||
|
|
||||||
StringItem item = CreateItem(blockTag, blockBuffer); |
|
||||||
if (item != null) |
|
||||||
{ |
|
||||||
items.Add(item); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
i = end + 1; |
|
||||||
} |
|
||||||
|
|
||||||
return items; |
|
||||||
} |
|
||||||
|
|
||||||
protected StringItem CreateItem(string commandInfo, string bodyText) |
|
||||||
{ |
|
||||||
string[] parameters = commandInfo.Split(new char[] { ',' }); |
|
||||||
if (parameters.Length > 0) |
|
||||||
{ |
|
||||||
StringItem item = new StringItem(); |
|
||||||
item.parameters = parameters; |
|
||||||
item.bodyText = bodyText; |
|
||||||
return item; |
|
||||||
} |
|
||||||
|
|
||||||
return null; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
Loading…
Reference in new issue