#if USE_CELTX
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
namespace PixelCrushers.DialogueSystem.Celtx
{
///
/// This class does the actual work of converting Celtx Data (Raw) into a dialogue database.
///
public class CeltxToDialogueDatabase
{
Template template = Template.FromDefault();
DialogueDatabase database;
CeltxData celtxData;
public DialogueDatabase ProcessRawCeltxData(CeltxDataRaw celtxDataRaw, DialogueDatabase database, bool importGameplayScriptText, bool importBreakdownCatalogContent, bool checkSequenceSyntax)
{
try
{
this.database = database;
celtxData = new CeltxData();
List docContentList = celtxDataRaw.doc.content;
Debug.Log("Converting Celtx Data to Dialogue System database");
CxContent cxMetadata = docContentList.Find(c => c.type.Equals("cxmetadata"));
ProcessCxCatalog(cxMetadata);
ProcessCxVariables(cxMetadata);
ProcessCxConditions(cxMetadata);
List sequenceList = docContentList.Where(c => c.type.Equals("cxsequence")).ToList();
ProcessSequenceList(sequenceList);
if (checkSequenceSyntax) CheckSequenceSyntax();
ConvertBlankNodesToGroupNodes();
}
catch (System.Exception e)
{
LogError(MethodBase.GetCurrentMethod(), e);
}
return database;
}
private void ProcessCxCatalog(CxContent cxMetadata)
{
List cxCatalogContent;
try
{
cxCatalogContent = cxMetadata.content.Find(c => c.type.Equals("cxcatalog")).content;
}
catch (System.Exception)
{
Debug.Log("Skipping catalog because it is null or empty");
return;
}
foreach (CxContent contentObject in cxCatalogContent)
{
switch (contentObject.attrs.type)
{
case "character":
ConvertCharacterCatalogEntryToDSActor(contentObject.attrs);
break;
case "location":
ConvertLocationCatalogEntryToDSLocation(contentObject.attrs);
break;
case "item":
ConvertItemCatalogEntryToDSItem(contentObject.attrs);
break;
case "branch":
case "jump":
case "sequence":
ExtractCustomVarsList(contentObject.attrs);
break;
}
}
}
private void ProcessCxVariables(CxContent cxMetadata)
{
List cxVariablesList;
try
{
cxVariablesList = cxMetadata.content.Find(c => c.type.Equals("cxconditions")).attrs.variables;
}
catch (System.Exception)
{
Debug.Log("Skipping variables because there are none");
return;
}
foreach (CxVariable variableObject in cxVariablesList)
{
ConvertCeltxVariableToDSVariable(variableObject);
}
}
private void ProcessCxConditions(CxContent cxMetadata)
{
List cxConditionsContent;
try
{
cxConditionsContent = cxMetadata.content.Find(c => c.type.Equals("cxconditions")).content;
}
catch (System.Exception)
{
Debug.Log("Skipping variables because there are none");
return;
}
foreach (CxContent contentObject in cxConditionsContent)
{
ExtractCeltxCondition(contentObject.attrs);
}
foreach (CeltxCondition condition in celtxData.conditions)
{
GenerateLuaCondition(condition);
}
}
private void AppendToField(List fields, string title, string value, FieldType fieldType)
{
string currentFieldValue = Field.LookupValue(fields, title);
if (value == null || value.Equals("") || currentFieldValue != null && currentFieldValue.Equals(value)) { return; }
string updatedString;
if (currentFieldValue == null || currentFieldValue.Equals("") || currentFieldValue.Equals("[]")) { updatedString = value; }
else { updatedString = currentFieldValue + " " + value; }
Field.SetValue(fields, title, updatedString, fieldType);
}
#region CxSequence Processing
private void ProcessSequenceList(List sequenceList)
{
sequenceList.ForEach(c => ExtractSequenceLinkingData(c));
celtxData.sequenceLinkingDataList.ForEach(i => CheckIfRoot(i));
List rootItems = celtxData.sequenceLinkingDataList.Where(i => i.isRoot).ToList();
rootItems.ForEach(i => ProcessConversation(i, sequenceList));
}
private void ExtractSequenceLinkingData(CxContent cxSequenceContentObject)
{
try
{
SequenceLinkingData sequenceLinkingData = new SequenceLinkingData();
sequenceLinkingData.id = cxSequenceContentObject.attrs.id;
sequenceLinkingData.name = cxSequenceContentObject.attrs.name;
if (cxSequenceContentObject.attrs.transitions != null && cxSequenceContentObject.attrs.transitions.Count == 1 && cxSequenceContentObject.attrs.transitions[0].id != null)
{
sequenceLinkingData.linkedIds.Add(cxSequenceContentObject.attrs.transitions[0].id);
}
List cxBranches = cxSequenceContentObject.content[0].content.Where(c => c.type.Equals("cxbranch")).ToList();
if (cxBranches != null && cxBranches.Count > 0)
{
foreach (CxContent branch in cxBranches)
{
foreach (CxLinked linkedItem in branch.attrs.linked)
{
sequenceLinkingData.linkedIds.Add(linkedItem.id);
}
}
}
List cxJumps = cxSequenceContentObject.content[0].content.Where(c => c.type.Equals("cxjump")).ToList();
if (cxJumps != null && cxJumps.Count > 0)
{
foreach (CxContent jump in cxJumps)
{
sequenceLinkingData.linkedIds.Add(jump.attrs.linked_jump.id);
}
}
celtxData.idsWithIncomingLinks.AddRange(sequenceLinkingData.linkedIds);
celtxData.sequenceLinkingDataList.Add(sequenceLinkingData);
}
catch (System.Exception e)
{
LogError(MethodBase.GetCurrentMethod(), e, cxSequenceContentObject.attrs.id);
}
}
public void CheckIfRoot(SequenceLinkingData sequenceLinkingData)
{
try
{
if (!celtxData.idsWithIncomingLinks.Contains(sequenceLinkingData.id))
{
sequenceLinkingData.isRoot = true;
}
}
catch (System.Exception e)
{
LogError(MethodBase.GetCurrentMethod(), e, sequenceLinkingData.id);
}
}
public void ProcessConversation(SequenceLinkingData sequenceLinkingData, List sequenceList)
{
try
{
string sequenceId = sequenceLinkingData.id;
CxContent cxSequenceContentObject = sequenceList.Find(c => c.attrs.id.Equals(sequenceLinkingData.id));
DialogueEntry conversationStartNode = ProcessConversationRootSequence(cxSequenceContentObject, sequenceId);
if (conversationStartNode == null) return;
ProcessConversationChildSequences(sequenceId, conversationStartNode, sequenceLinkingData, sequenceList);
}
catch (System.Exception e)
{
LogError(MethodBase.GetCurrentMethod(), e, sequenceLinkingData.id);
}
}
private DialogueEntry ProcessConversationRootSequence(CxContent cxSequenceContentObject, string sequenceId)
{
try
{
string sequenceName = cxSequenceContentObject.attrs.name;
string sequenceCatalogId = cxSequenceContentObject.attrs.catalog_id;
List pageContents = GetSequencePageContents(cxSequenceContentObject);
if (pageContents == null)
{
LogWarning("Skipping this sequence map because the Conversation Root node's page is null or empty. Please ensure the node has 2 characters",
cxSequenceContentObject.attrs.id, cxSequenceContentObject.attrs.name);
return null;
}
List characters = pageContents.FindAll(c => c.type.Equals("cxcharacter"));
if (characters == null || characters.Count < 2)
{
LogWarning("Skipping this sequence map because the Conversation Root node has less than 2 characters. Please ensure the node has 2 characters",
cxSequenceContentObject.attrs.id, cxSequenceContentObject.attrs.name);
return null;
}
Conversation conversation = template.CreateConversation(template.GetNextConversationID(database), sequenceName);
database.conversations.Add(conversation);
conversation.ActorID = GetActorIdForCharacter(characters[0]);
conversation.ConversantID = GetActorIdForCharacter(characters[1]);
DialogueEntry startNode = CreateNextDialogueEntryForConversation(conversation, "START", sequenceId);
startNode.Sequence = "None()";
AddSetVariableScript(startNode, sequenceCatalogId);
return startNode;
}
catch (System.Exception e)
{
LogError(MethodBase.GetCurrentMethod(), e, cxSequenceContentObject.attrs.id);
return null;
}
}
public void ProcessConversationChildSequences(string sourceCxId, DialogueEntry lastCreatedDialogueEntry, SequenceLinkingData sequenceLinkingData, List sequenceList)
{
try
{
foreach (string linkedSequenceId in sequenceLinkingData.linkedIds)
{
if (sequenceLinkingData.linksProcessed.Contains(linkedSequenceId))
{
continue;
}
SequenceLinkingData targetSequenceLinkingData = celtxData.sequenceLinkingDataList.Find(o => o.id.Equals(linkedSequenceId));
if (!targetSequenceLinkingData.isRoot)
{
CxContent targetCxSequenceContentObject = sequenceList.Find(c => c.attrs.id.Equals(targetSequenceLinkingData.id));
DialogueEntry targetSequenceLastCreatedDialogueEntry = ProcessTargetConversationSequence(targetCxSequenceContentObject, targetSequenceLinkingData, lastCreatedDialogueEntry, sourceCxId);
if (targetSequenceLastCreatedDialogueEntry != null)
{
ProcessConversationChildSequences(targetCxSequenceContentObject.attrs.id, targetSequenceLastCreatedDialogueEntry, targetSequenceLinkingData, sequenceList);
}
}
sequenceLinkingData.linksProcessed.Add(linkedSequenceId);
}
sequenceLinkingData.sequenceProcessingComplete = true;
}
catch (System.Exception e)
{
LogError(MethodBase.GetCurrentMethod(), e, sequenceLinkingData.id);
}
}
private DialogueEntry ProcessTargetConversationSequence(CxContent targetSequenceContentObject, SequenceLinkingData sequenceLinkingData, DialogueEntry parentDialogueEntry, string sourceCxId)
{
try
{
string sequenceId = sequenceLinkingData.id;
string sequenceName = targetSequenceContentObject.attrs.name;
string sequenceCatalogId = targetSequenceContentObject.attrs.catalog_id;
int conversationId = -1;
conversationId = parentDialogueEntry.conversationID;
Conversation conversation = database.GetConversation(conversationId);
DialogueEntry targetEntry = null;
foreach (DialogueEntry entry in conversation.dialogueEntries)
{
if (Field.LookupValue(entry.fields, CeltxFields.CeltxId).Equals(sequenceId))
{
targetEntry = entry;
break;
}
}
if (targetEntry == null)
{
targetEntry = CreateNextDialogueEntryForConversation(conversation, sequenceName, sequenceId);
AddSetVariableScript(targetEntry, sequenceCatalogId);
}
else
{
string linkConditionId = GetLinkConditionId(sourceCxId, sequenceId);
if (parentDialogueEntry.outgoingLinks.Count > 0)
{
foreach (Link link in parentDialogueEntry.outgoingLinks)
{
string linkCxId = Field.LookupValue(conversation.GetDialogueEntry(link.destinationDialogueID).fields, CeltxFields.CeltxId);
string targetLinkId = Field.LookupValue(parentDialogueEntry.fields, CeltxFields.CeltxId) + "-" + Field.LookupValue(targetEntry.fields, CeltxFields.CeltxId);
if (linkCxId.Contains(targetLinkId))
{
return targetEntry;
}
}
}
}
LinkDialogueEntries(parentDialogueEntry, targetEntry, GetLinkConditionId(sourceCxId, sequenceId));
// Process actual sequence node contents
if (!sequenceLinkingData.sequenceProcessingComplete)
{
return ProcessSequenceNodeContents(targetEntry, targetSequenceContentObject);
}
else
{
return null;
}
}
catch (System.Exception e)
{
LogError(MethodBase.GetCurrentMethod(), e, targetSequenceContentObject.attrs.id);
return null;
}
}
private List GetSequencePageContents(CxContent cxSequenceContentObject)
{
if (cxSequenceContentObject.content != null && cxSequenceContentObject.content.Count > 0)
{
CxContent page = cxSequenceContentObject.content[0];
if (page.type.Equals("cxpage"))
{
return page.content;
}
}
return null;
}
private DialogueEntry ProcessSequenceNodeContents(DialogueEntry initialEntry, CxContent cxSequenceContentObject)
{
DialogueEntry currentEntry = initialEntry;
Conversation conversation = database.GetConversation(currentEntry.conversationID);
try
{
List sequencePageContentList = cxSequenceContentObject.content[0].content;
int entryCount = 1;
bool createNewEntry = false;
foreach (CxContent pageContentItem in sequencePageContentList)
{
if (!pageContentItem.type.Equals("cxgameplay") &&
!pageContentItem.type.Equals("cxcharacter") &&
!pageContentItem.type.Equals("cxparenthetical") &&
!pageContentItem.type.Equals("cxdialog"))
{
continue;
}
string combinedText = GetCombinedText(pageContentItem.content);
if (createNewEntry)
{
entryCount += 1;
currentEntry = CreateAdditionalEntryForSequence(conversation, currentEntry, initialEntry, entryCount);
createNewEntry = false;
}
switch (pageContentItem.type)
{
case "cxgameplay":
if (StringStartsWithTag(combinedText, "[SEQ]"))
{
currentEntry.Sequence = GetTextWithoutTag(combinedText);
}
else if (StringStartsWithTag(combinedText, "[COND]"))
{
if (!string.IsNullOrEmpty(currentEntry.conditionsString)) currentEntry.conditionsString += ";\n";
currentEntry.conditionsString += GetTextWithoutTag(combinedText);
}
else if (StringStartsWithTag(combinedText, "[SCRIPT]"))
{
if (!string.IsNullOrEmpty(currentEntry.userScript)) currentEntry.userScript += ";\n";
currentEntry.userScript += GetTextWithoutTag(combinedText);
}
break;
case "cxcharacter":
currentEntry.ActorID = GetActorIdForCharacter(pageContentItem);
if (currentEntry.ActorID == conversation.ActorID)
{
currentEntry.ConversantID = conversation.ConversantID;
}
else
{
currentEntry.ConversantID = conversation.ActorID;
}
break;
case "cxparenthetical":
if (StringStartsWithTag(combinedText, "[C]"))
{
currentEntry.ConversantID = database.GetActor(GetTextWithoutTag(combinedText).ToUpper()).id;
}
else if (StringStartsWithTag(combinedText, "[VO]"))
{
AppendToField(currentEntry.fields, CeltxFields.VoiceOverFile, GetTextWithoutTag(combinedText), FieldType.Files);
}
else
{
currentEntry.MenuText = combinedText;
}
break;
case "cxdialog":
currentEntry.DialogueText = GetCombinedText(pageContentItem.content);
createNewEntry = true;
break;
}
}
}
catch (System.Exception e)
{
LogError(MethodBase.GetCurrentMethod(), e, cxSequenceContentObject.attrs.id);
}
return currentEntry;
}
private DialogueEntry CreateAdditionalEntryForSequence(Conversation conversation, DialogueEntry currentEntry, DialogueEntry initialEntry, int entryCount)
{
DialogueEntry newEntry = CreateNextDialogueEntryForConversation(conversation, initialEntry.Title + "-" + entryCount,
Field.LookupValue(initialEntry.fields, CeltxFields.CeltxId) + "-" + entryCount);
newEntry.ActorID = currentEntry.ActorID;
newEntry.ConversantID = currentEntry.ConversantID;
LinkDialogueEntries(currentEntry, newEntry, null);
return newEntry;
}
private void AddSetVariableScript(DialogueEntry entry, string sequenceCatalogId)
{
try
{
if (celtxData.customVarListLookupByCxSequenceId.ContainsKey(sequenceCatalogId))
{
if (!celtxData.customVarListLookupByCxSequenceId.ContainsKey(sequenceCatalogId)) Debug.LogError("Celtx Import: Can't find custom variable list for sequence ID " + sequenceCatalogId);
List cxCustomVars = celtxData.customVarListLookupByCxSequenceId[sequenceCatalogId];
StringBuilder luaScript = new StringBuilder();
foreach (CxCustomVar cxCustomVar in cxCustomVars)
{
if (luaScript.Length != 0) { luaScript.Append("; "); }
if (!celtxData.variableLookupByCeltxId.ContainsKey(cxCustomVar.id)) Debug.LogError("Celtx Import: Can't find variable with ID " + cxCustomVar.id);
Variable var = database.GetVariable(celtxData.variableLookupByCeltxId[cxCustomVar.id]);
if (var == null) Debug.LogError("Celtx Import: Dialogue database doesn't contain variable named " + celtxData.variableLookupByCeltxId[cxCustomVar.id]);
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append("Variable");
stringBuilder.Append("[\"" + var.Name + "\"]");
stringBuilder.Append(" = ");
if (var.Type == FieldType.Boolean)
{
if (!cxCustomVar.boolVal.ToString().Equals(var.InitialValue)) { stringBuilder.Append(cxCustomVar.boolVal.ToString().ToLower()); }
else { stringBuilder.Append(var.InitialValue); }
}
else if (var.Type == FieldType.Number)
{
if (!cxCustomVar.longVal.ToString().Equals(var.InitialValue)) { stringBuilder.Append(cxCustomVar.longVal.ToString()); }
else { stringBuilder.Append(var.InitialValue); }
}
else if (var.Type == FieldType.Text)
{
if (cxCustomVar.type != null && cxCustomVar.type.Equals("radio"))
{
stringBuilder.Append(cxCustomVar.config.options[(int)cxCustomVar.longVal].strVal);
}
else
{
if (cxCustomVar.strVal != null && !cxCustomVar.strVal.Equals(var.InitialValue)) { stringBuilder.Append("\"" + cxCustomVar.strVal + "\""); }
else { stringBuilder.Append("\"" + var.InitialValue + "\""); }
}
}
luaScript.Append(stringBuilder.ToString());
}
entry.userScript = luaScript.ToString();
}
}
catch (System.Exception e)
{
LogError(MethodBase.GetCurrentMethod(), e, "SetVar[" + sequenceCatalogId + "]");
}
}
private int GetActorIdForCharacter(CxContent characterContentItem)
{
if (!celtxData.actorIdLookupByCxCharacterCatalogId.ContainsKey(characterContentItem.attrs.catalog_id)) Debug.LogError("Celtx Import: Lookup failed for actor with ID " + characterContentItem.attrs.catalog_id);
return celtxData.actorIdLookupByCxCharacterCatalogId[characterContentItem.attrs.catalog_id];
}
private DialogueEntry CreateNextDialogueEntryForConversation(Conversation conversation, string title, string celtxId, bool isGroup = false)
{
try
{
DialogueEntry dialogueEntry = template.CreateDialogueEntry(template.GetNextDialogueEntryID(conversation), conversation.id, title);
conversation.dialogueEntries.Add(dialogueEntry);
dialogueEntry.isGroup = isGroup;
dialogueEntry.ActorID = conversation.ActorID;
dialogueEntry.ConversantID = conversation.ConversantID;
if (celtxId != null)
{
Field.SetValue(dialogueEntry.fields, CeltxFields.CeltxId, celtxId);
celtxData.dialogueEntryLookupByCeltxId.Add(celtxId, dialogueEntry);
}
return dialogueEntry;
}
catch (System.Exception e)
{
LogError(MethodBase.GetCurrentMethod(), e, celtxId, title);
}
return null;
}
private void ConvertBlankNodesToGroupNodes()
{
foreach (Conversation conversation in database.conversations)
{
foreach (DialogueEntry entry in conversation.dialogueEntries)
{
var isBlankNode = string.IsNullOrEmpty(entry.DialogueText) &&
string.IsNullOrEmpty(entry.MenuText) &&
string.IsNullOrEmpty(entry.Sequence);
if (isBlankNode)
{
entry.isGroup = true;
}
}
}
}
#endregion
#region Linking
private string GetLinkConditionId(string sourceCeltxId, string destinationCeltxId)
{
try
{
foreach (CeltxCondition condition in celtxData.conditions)
{
foreach (CxOnObj onObj in condition.links)
{
if (onObj.from.Equals(sourceCeltxId) && onObj.to.Equals(destinationCeltxId))
{
return condition.id;
}
}
}
}
catch (System.Exception e)
{
LogError(MethodBase.GetCurrentMethod(), e, sourceCeltxId + "-" + destinationCeltxId);
}
return null;
}
private void LinkDialogueEntries(DialogueEntry source, DialogueEntry destination, string conditionId)
{
try
{
if (conditionId == null)
{
source.outgoingLinks.Add(new Link(source.conversationID, source.id, destination.conversationID, destination.id));
}
else
{
CeltxCondition condition = celtxData.conditions.Find(c => c.id.Equals(conditionId));
DialogueEntry entryToLinkToCondition = source;
if (condition.delay)
{
DialogueEntry delayEntry = CreateNextDialogueEntryForConversation(database.GetConversation(source.conversationID),
"[D]-" + condition.name,
"D-" + Field.LookupValue(source.fields, CeltxFields.CeltxId) + "-" + Field.LookupValue(destination.fields, CeltxFields.CeltxId), true);
source.outgoingLinks.Add(new Link(source.conversationID, source.id, delayEntry.conversationID, delayEntry.id));
entryToLinkToCondition = delayEntry;
}
DialogueEntry conditionEntry = CreateNextDialogueEntryForConversation(database.GetConversation(source.conversationID),
"[COND]" + condition.name,
"C-" + Field.LookupValue(source.fields, CeltxFields.CeltxId) + "-" + Field.LookupValue(destination.fields, CeltxFields.CeltxId), true);
conditionEntry.conditionsString = condition.luaConditionString;
entryToLinkToCondition.outgoingLinks.Add(new Link(entryToLinkToCondition.conversationID, entryToLinkToCondition.id, conditionEntry.conversationID, conditionEntry.id));
conditionEntry.outgoingLinks.Add(new Link(conditionEntry.conversationID, conditionEntry.id, destination.conversationID, destination.id));
}
}
catch (System.Exception e)
{
LogError(MethodBase.GetCurrentMethod(), e, source.Title + "-" + destination.Title + "-" + conditionId);
}
}
#endregion
#region Text Processing
private bool StringStartsWithTag(string stringToCheck, string targetTag)
{
if (stringToCheck == null || targetTag == null) { return false; }
return stringToCheck.StartsWith(targetTag, System.StringComparison.OrdinalIgnoreCase);
}
private string GetTextWithoutTag(string text)
{
if (string.IsNullOrEmpty(text)) { return ""; }
//return System.Text.RegularExpressions.Regex.Split(text, "(\\[.*\\])")[2].Trim();
var endTagPos = text.IndexOf(']');
return (endTagPos == -1) ? "" : text.Substring(endTagPos + 1);
}
private string GetCombinedText(List contentList)
{
if (contentList == null) { return ""; }
StringBuilder stringBuilder = new StringBuilder();
foreach (CxContent content in contentList)
{
if (content.type.Equals("text"))
{
stringBuilder.Append(GetTextWithMarks(content));
}
}
return stringBuilder.ToString();
}
private string GetTextWithMarks(CxContent textContent)
{
try
{
string text = textContent.text;
if (textContent.marks != null && textContent.marks.Count > 0)
{
string prependString = "";
string appendString = "";
foreach (CxContent mark in textContent.marks)
{
switch (mark.type)
{
case "strong":
prependString = prependString + "";
appendString = "" + appendString;
break;
case "em":
prependString = prependString + "";
appendString = "" + appendString;
break;
case "underline":
prependString = prependString + "";
appendString = "" + appendString;
break;
}
}
return prependString + text + appendString;
}
else
{
return text;
}
}
catch (System.Exception e)
{
LogError(MethodBase.GetCurrentMethod(), e, textContent.attrs.id);
return "";
}
}
#endregion
#region Catalog Data Processing
private void ConvertCharacterCatalogEntryToDSActor(CxAttrs catalogItemAttrs)
{
try
{
Actor actor = database.GetActor(catalogItemAttrs.title);
if (actor == null)
{
int actorID = template.GetNextActorID(database);
actor = template.CreateActor(actorID, catalogItemAttrs.title, IsPlayerCharacter(catalogItemAttrs));
database.actors.Add(actor);
}
else
{
var originalActor = (database.syncInfo.syncActors && database.syncInfo.syncActorsDatabase != null)
? database.syncInfo.syncActorsDatabase.GetActor(actor.Name) : null;
if (originalActor != null)
{
AppendToField(originalActor.fields, CeltxFields.CatalogId, catalogItemAttrs.id, FieldType.Text);
AppendToField(originalActor.fields, CeltxFields.Description, catalogItemAttrs.item_data.description, FieldType.Text);
AppendToField(originalActor.fields, CeltxFields.Pictures, getPictureString(catalogItemAttrs), FieldType.Files);
#if UNITY_EDITOR
UnityEditor.EditorUtility.SetDirty(database.syncInfo.syncActorsDatabase);
#endif
}
else
{
Debug.LogWarning("Celtx Import: Actor " + catalogItemAttrs.title + " was already added with Celtx ID " + actor.LookupValue(CeltxFields.CatalogId) + " but another actor with same name has Celtx ID " + catalogItemAttrs.id);
}
}
AppendToField(actor.fields, CeltxFields.CatalogId, catalogItemAttrs.id, FieldType.Text);
AppendToField(actor.fields, CeltxFields.Description, catalogItemAttrs.item_data.description, FieldType.Text);
AppendToField(actor.fields, CeltxFields.Pictures, getPictureString(catalogItemAttrs), FieldType.Files);
celtxData.actorIdLookupByCxCharacterCatalogId[catalogItemAttrs.id] = actor.id;
}
catch (System.Exception e)
{
LogError(MethodBase.GetCurrentMethod(), e, catalogItemAttrs.id, catalogItemAttrs.title);
}
}
private bool IsPlayerCharacter(CxAttrs characterAttrs)
{
try
{
if (characterAttrs.item_data == null)
{
LogWarning(GetNullDataString("attrs.item_data") +
"Cannot determine if character is a player. Please ensure Character Type is set in the catalog", characterAttrs.id, characterAttrs.title);
return false;
}
if (characterAttrs.item_data.character_type == null)
{
LogWarning(GetNullDataString("attrs.item_data.character_type") +
"Defaulting to non-player Actor. Please ensure Character Type is set in the catalog", characterAttrs.id, characterAttrs.title);
return false;
}
return characterAttrs.item_data.character_type.Equals("pc");
}
catch (System.Exception e)
{
LogError(MethodBase.GetCurrentMethod(), e, characterAttrs.id, characterAttrs.title);
}
return false;
}
private string getPictureString(CxAttrs catalogItemAttrs)
{
try
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append("[");
if (catalogItemAttrs.item_data.media != null && catalogItemAttrs.item_data.media.Count > 0)
{
foreach (CxMedia media in catalogItemAttrs.item_data.media)
{
stringBuilder.Append(media.name);
if (media.Equals(catalogItemAttrs.item_data.media.Last()))
{
stringBuilder.Append("]");
}
else
{
stringBuilder.Append(";");
}
}
return stringBuilder.ToString();
}
else
{
return "[]";
}
}
catch (System.Exception e)
{
LogError(MethodBase.GetCurrentMethod(), e, catalogItemAttrs.id, catalogItemAttrs.title);
}
return "[]";
}
private void ConvertLocationCatalogEntryToDSLocation(CxAttrs catalogItemAttrs)
{
try
{
Location location = database.GetLocation(catalogItemAttrs.title);
if (location == null)
{
int locationId = template.GetNextLocationID(database);
location = template.CreateLocation(locationId, catalogItemAttrs.title);
database.locations.Add(location);
}
else
{
var originalLocation = (database.syncInfo.syncLocations && database.syncInfo.syncLocationsDatabase != null)
? database.syncInfo.syncLocationsDatabase.GetLocation(location.Name) : null;
if (originalLocation != null)
{
AppendToField(originalLocation.fields, CeltxFields.CatalogId, catalogItemAttrs.id, FieldType.Text);
AppendToField(originalLocation.fields, CeltxFields.Description, catalogItemAttrs.item_data.description, FieldType.Text);
#if UNITY_EDITOR
UnityEditor.EditorUtility.SetDirty(database.syncInfo.syncLocationsDatabase);
#endif
}
}
AppendToField(location.fields, CeltxFields.CatalogId, catalogItemAttrs.id, FieldType.Text);
AppendToField(location.fields, CeltxFields.Description, catalogItemAttrs.item_data.description, FieldType.Text);
}
catch (System.Exception e)
{
LogError(MethodBase.GetCurrentMethod(), e, catalogItemAttrs.id, catalogItemAttrs.title);
}
}
private void ConvertItemCatalogEntryToDSItem(CxAttrs catalogItemAttrs)
{
try
{
Item item = database.GetItem(catalogItemAttrs.title);
if (item == null)
{
int itemId = template.GetNextItemID(database);
item = template.CreateItem(itemId, catalogItemAttrs.title);
database.items.Add(item);
}
else
{
var originalItem = (database.syncInfo.syncItems && database.syncInfo.syncItemsDatabase != null)
? database.syncInfo.syncItemsDatabase.GetItem(item.Name) : null;
if (originalItem != null)
{
AppendToField(originalItem.fields, CeltxFields.CatalogId, catalogItemAttrs.id, FieldType.Text);
AppendToField(originalItem.fields, CeltxFields.Description, catalogItemAttrs.item_data.description, FieldType.Text);
AppendToField(originalItem.fields, CeltxFields.ItemType, catalogItemAttrs.item_data.item_type, FieldType.Text);
AppendToField(originalItem.fields, CeltxFields.ItemProperties, catalogItemAttrs.item_data.item_properties, FieldType.Text);
AppendToField(originalItem.fields, CeltxFields.ItemAvailability, catalogItemAttrs.item_data.item_availability, FieldType.Text);
#if UNITY_EDITOR
UnityEditor.EditorUtility.SetDirty(database.syncInfo.syncItemsDatabase);
#endif
}
}
AppendToField(item.fields, CeltxFields.CatalogId, catalogItemAttrs.id, FieldType.Text);
AppendToField(item.fields, CeltxFields.Description, catalogItemAttrs.item_data.description, FieldType.Text);
AppendToField(item.fields, CeltxFields.ItemType, catalogItemAttrs.item_data.item_type, FieldType.Text);
AppendToField(item.fields, CeltxFields.ItemProperties, catalogItemAttrs.item_data.item_properties, FieldType.Text);
AppendToField(item.fields, CeltxFields.ItemAvailability, catalogItemAttrs.item_data.item_availability, FieldType.Text);
}
catch (System.Exception e)
{
LogError(MethodBase.GetCurrentMethod(), e, catalogItemAttrs.id, catalogItemAttrs.title);
}
}
private void ExtractCustomVarsList(CxAttrs catalogItemAttrs)
{
try
{
string catalogId = catalogItemAttrs.id;
List customVars = catalogItemAttrs.custom_vars;
if (customVars != null && customVars.Count > 0)
{
celtxData.customVarListLookupByCxSequenceId.Add(catalogId, customVars);
}
}
catch (System.Exception e)
{
LogError(MethodBase.GetCurrentMethod(), e, catalogItemAttrs.id, catalogItemAttrs.title);
}
}
#endregion
#region Variables and Conditions
private void ConvertCeltxVariableToDSVariable(CxVariable variableObject)
{
try
{
FieldType fieldType = FieldType.Text;
string variableDefaultValue = "";
List extraFields = new List();
switch (variableObject.type)
{
case "boolean":
variableDefaultValue = variableObject.config.defaultBool.ToString();
fieldType = FieldType.Boolean;
break;
case "number":
case "range":
variableDefaultValue = variableObject.config.defaultNum.ToString();
fieldType = FieldType.Number;
break;
case "date":
case "text":
case "textarea":
case "time":
variableDefaultValue = variableObject.config.defaultString;
fieldType = FieldType.Text;
break;
case "radio":
variableDefaultValue = variableObject.config.options[(int)variableObject.config.defaultNum].strVal;
fieldType = FieldType.Text;
break;
}
var variable = database.GetVariable(variableObject.name);
if (variable == null)
{
int variableId = template.GetNextVariableID(database);
variable = template.CreateVariable(variableId, variableObject.name, variableDefaultValue, fieldType);
database.variables.Add(variable);
}
else
{
var originalVariable = (database.syncInfo.syncVariables && database.syncInfo.syncVariablesDatabase != null)
? database.syncInfo.syncVariablesDatabase.GetVariable(variableObject.name) : null;
if (originalVariable != null)
{
AppendToField(originalVariable.fields, CeltxFields.Description, variableObject.desc, FieldType.Text);
#if UNITY_EDITOR
UnityEditor.EditorUtility.SetDirty(database.syncInfo.syncVariablesDatabase);
#endif
}
}
AppendToField(variable.fields, CeltxFields.Description, variableObject.desc, FieldType.Text);
celtxData.variableLookupByCeltxId[variableObject.id] = variableObject.name;
}
catch (System.Exception e)
{
LogError(MethodBase.GetCurrentMethod(), e, variableObject.id, variableObject.name);
}
}
private void ExtractCeltxCondition(CxAttrs conditionItemAttrs)
{
try
{
CeltxCondition condition = new CeltxCondition();
condition.id = conditionItemAttrs.id;
condition.catalogId = conditionItemAttrs.catalog_id;
condition.name = conditionItemAttrs.name;
if (conditionItemAttrs.desc != null)
{
condition.delay = conditionItemAttrs.desc.Contains("[D]");
}
if (condition.delay) condition.description = conditionItemAttrs.desc.Substring(0, "[D]".Length);
condition.links = conditionItemAttrs.on;
condition.literals = conditionItemAttrs.literals;
SetFollowingOperators(condition, conditionItemAttrs.clause);
celtxData.conditions.Add(condition);
}
catch (System.Exception e)
{
LogError(MethodBase.GetCurrentMethod(), e, conditionItemAttrs.id, conditionItemAttrs.title);
}
}
private void SetFollowingOperators(CeltxCondition condition, string clause)
{
try
{
List subSections = new List();
List orSplit = new List();
subSections.AddRange(System.Text.RegularExpressions.Regex.Split(clause, "(\\+|\\.)"));
if (subSections.ElementAt(subSections.Count - 1).Equals("")) { subSections.RemoveAt(subSections.Count - 1); }
for (int i = 0; i < subSections.Count - 1; i += 2)
{
CxLiteral literal = condition.literals.Find(l => l.lit_name.Equals(subSections.ElementAt(i)));
literal.followingOp = subSections.ElementAt(i + 1);
}
}
catch (System.Exception e)
{
LogError(MethodBase.GetCurrentMethod(), e, condition.id, condition.name);
}
}
private void GenerateLuaCondition(CeltxCondition condition)
{
try
{
if (condition.literals == null)
{
LogMessage(LogLevel.W,
"Condition has no literals(literals list is null). Lua script generation will be skipped for this condition. Please add literals to the condition in Celtx", condition.id);
return;
}
StringBuilder conditionString = new StringBuilder();
string followingOp = null;
foreach (CxLiteral literal in condition.literals)
{
if (followingOp != null) { conditionString.Append(" " + followingOp + " "); }
if (literal.type.Equals("variable"))
{
conditionString.Append("Variable");
conditionString.Append("[\"" + literal.name + "\"]");
conditionString.Append(" " + GetLuaComparisonOperator(literal.comparisonOperator) + " ");
string comparisonVal;
if (literal.varType != null && literal.varType.Equals("radio"))
{
comparisonVal = literal.config.options[(int)literal.comparisonValue.longVal].strVal;
}
else
{
comparisonVal = literal.comparisonValue.strVal;
}
if (SurroundComparisonValueInQuotes(literal))
{
conditionString.Append("\"" + comparisonVal + "\"");
}
else
{
conditionString.Append(comparisonVal);
}
}
else if (literal.type.Equals("condition"))
{
conditionString.Append("(");
conditionString.Append(GetNestedConditionString(literal.conditionId));
conditionString.Append(") == ");
conditionString.Append(literal.comparisonValue.strVal);
}
else
{
LogMessage(LogLevel.W,
"Unsupported literal type(" + literal.type + ") in condition. Only variable and condition literal types are supported", literal.id);
continue;
}
if (literal.followingOp.Equals(".")) { followingOp = "and"; }
else if (literal.followingOp.Equals("+")) { followingOp = "or"; }
else
{
LogMessage(LogLevel.W,
"Unsupported following operator(" + literal.followingOp + ") in condition. Only \".\"(AND) and \"+\"(OR) are supported", literal.id);
continue;
}
}
condition.luaConditionString = conditionString.ToString();
}
catch (System.Exception e)
{
LogError(MethodBase.GetCurrentMethod(), e, condition.id, condition.name);
}
}
private bool SurroundComparisonValueInQuotes(CxLiteral literal)
{
try
{
string variableId = literal.id;
if (!celtxData.variableLookupByCeltxId.ContainsKey(variableId)) Debug.LogError("Celtx Import: Can't find variable with ID " + variableId);
Variable var = database.GetVariable(celtxData.variableLookupByCeltxId[variableId]);
if (var == null) Debug.LogError("Celtx Import: Dialogue database doesn't contain variable named " + celtxData.variableLookupByCeltxId[variableId]);
FieldType varType = var.Type;
if (varType == FieldType.Text)
{
return true;
}
else
{
return false;
}
}
catch (System.Exception e)
{
LogError(MethodBase.GetCurrentMethod(), e, literal.id);
}
return false;
}
private string GetNestedConditionString(string conditionId)
{
try
{
CeltxCondition condition = celtxData.conditions.Find(c => c.id.Equals(conditionId));
if (condition.luaConditionString == null)
{
GenerateLuaCondition(condition);
}
return condition.luaConditionString;
}
catch (System.Exception e)
{
LogError(MethodBase.GetCurrentMethod(), e, conditionId);
}
return null;
}
private string GetLuaComparisonOperator(string literalOp)
{
switch (literalOp)
{
case "<":
case "<=":
case ">":
case ">=":
return literalOp;
case "=":
return "==";
case "!=":
return "~=";
case "exists":
LogMessage(LogLevel.W,
"Literal operator(" + literalOp + ") not supported. Defaulting to \"==\"");
return "==";
case "does not exist":
LogMessage(LogLevel.W,
"Literal operator(" + literalOp + ") not supported. Defaulting to \"~=\"");
return "~=";
default:
LogMessage(LogLevel.W,
"Unknown comparison operator(" + literalOp + ") in literal. Defaulting to \"==\"");
return "==";
}
}
#endregion
#region Check Sequence Syntax
///
/// Check syntax of all sequences in all dialogue entries in database.
/// Log warning for any entries with syntax errors.
///
private void CheckSequenceSyntax()
{
if (database == null) return;
var parser = new SequenceParser();
foreach (Conversation conversation in database.conversations)
{
foreach (DialogueEntry entry in conversation.dialogueEntries)
{
var sequence = entry.Sequence;
if (string.IsNullOrEmpty(sequence)) continue;
var result = parser.Parse(sequence);
if (result == null || result.Count == 0)
{
var text = entry.Title;
if (string.IsNullOrEmpty(text)) text = entry.subtitleText;
LogWarning("Dialogue entry " + conversation.id + ":" + entry.id +
" in conversation '" + conversation.Title + "' has syntax error in [SEQ] Sequence: " + sequence,
Field.LookupValue(entry.fields, "Celtx ID"), entry.Title);
}
}
}
}
#endregion
#region Logging
private string GetNullDataString(string nullFieldName)
{
return nullFieldName + " is null - ";
}
private string GetErrorString(MethodBase methodName, System.Exception ex)
{
return "Error in " + methodName + " : " + ex.ToString();
}
private void LogError(MethodBase methodName, System.Exception ex, string celtxId = null, string name = null)
{
string messageCore = GetErrorString(methodName, ex);
LogMessage(LogLevel.E, messageCore, celtxId, name);
}
private void LogWarning(string messageCore, string celtxId, string name)
{
LogMessage(LogLevel.W, messageCore, celtxId, name);
}
private void LogMessage(LogLevel logLevel, string messageCore, string celtxId = null, string name = null)
{
StringBuilder logMessage = new StringBuilder();
if (celtxId != null) { logMessage.Append("Celtx Object : " + celtxId + "(" + name + ")"); }
logMessage.Append(" | " + messageCore);
if (logLevel == LogLevel.W)
{
Debug.LogWarning(logMessage.ToString());
}
else if (logLevel == LogLevel.E)
{
Debug.LogError(logMessage.ToString());
}
else
{
Debug.Log(logMessage.ToString());
}
}
enum LogLevel
{
W, E, I
}
#endregion
}
}
#endif