GodotProjects/Sprout/addons/dialogic/Events/Converter/settings_converter.gd
2023-01-03 16:54:53 -07:00

1022 lines
40 KiB
GDScript

@tool
extends HSplitContainer
var folderStructure
var timelineFolderBreakdown:Dictionary = {}
var characterFolderBreakdown:Dictionary = {}
var definitionFolderBreakdown:Dictionary = {}
var themeFolderBreakdown:Dictionary = {}
var definitionsFile = {}
var flatDefinitionsFile = {}
var conversionRootFolder = "res://converted-dialogic"
var contents
var conversionReady = false
var varSubsystemInstalled = false
var anchorNames = {}
var prefixCharacters = false
func refresh():
pass
func _on_verify_pressed():
%OutputLog.text = ""
if FileAccess.file_exists("res://dialogic/settings.cfg"):
%OutputLog.text += "[√] Dialogic 1.x data [color=green]found![/color]\r\n"
if FileAccess.file_exists("res://dialogic/definitions.json"):
%OutputLog.text += "[√] Dialogic 1.x definitions [color=green]found![/color]\r\n"
else:
%OutputLog.text += "[X] Dialogic 1.x definitions [color=red]not found![/color]\r\n"
%OutputLog.text += "Please copy the res://dialogic folder from your Dialogic 1.x project into this project and try again.\r\n"
return
if FileAccess.file_exists("res://dialogic/settings.cfg"):
%OutputLog.text += "[√] Dialogic 1.x settings [color=green]found![/color]\r\n"
else:
%OutputLog.text += "[X] Dialogic 1.x settings [color=red]not found![/color]\r\n"
%OutputLog.text += "Please copy the res://dialogic folder from your Dialogic 1.x project into this project and try again.\r\n"
return
%OutputLog.text += "\r\n"
%OutputLog.text += "Verifying data:\r\n"
var file := FileAccess.open("res://dialogic/folder_structure.json", FileAccess.READ)
var fileContent = file.get_as_text()
var json_object = JSON.new()
var error = json_object.parse(fileContent)
if error == OK:
folderStructure = json_object.get_data()
#print(folderStructure)
else:
print("JSON Parse Error: ", json_object.get_error_message(), " in ", error, " at line ", json_object.get_error_line())
%OutputLog.text += "Dialogic 1.x folder structure [color=red]could not[/color] be read!\r\n"
%OutputLog.text += "Please check the output log for the error the JSON parser encountered.\r\n"
return
#folderStructure = json_object.get_data()
%OutputLog.text += "Dialogic 1.x folder structure read successfully!\r\n"
#I'm going to build a new, simpler tree here, as the folder structure is too complicated
recursive_search("Timeline", folderStructure["folders"]["Timelines"], "/")
recursive_search("Character", folderStructure["folders"]["Characters"], "/")
recursive_search("Definition", folderStructure["folders"]["Definitions"], "/")
recursive_search("Theme", folderStructure["folders"]["Themes"], "/")
%OutputLog.text += "Timelines found: " + str(timelineFolderBreakdown.size()) + "\r\n"
%OutputLog.text += "Characters found: " + str(characterFolderBreakdown.size()) + "\r\n"
%OutputLog.text += "Definitions found: " + str(definitionFolderBreakdown.size()) + "\r\n"
%OutputLog.text += "Themes found: " + str(themeFolderBreakdown.size()) + "\r\n"
%OutputLog.text += "\r\n"
%OutputLog.text += "Verifying count of JSON files for match with folder structure:\r\n"
var timelinesDirectory = list_files_in_directory("res://dialogic/timelines")
if timelinesDirectory.size() == timelineFolderBreakdown.size():
%OutputLog.text += "Timeline files found: [color=green]" + str(timelinesDirectory.size()) + "[/color]\r\n"
else:
%OutputLog.text += "Timeline files found: [color=red]" + str(timelinesDirectory.size()) + "[/color]\r\n"
%OutputLog.text += "[color=yellow]There may be an issue, please check in Dialogic 1.x to make sure that is correct![/color]\r\n"
var characterDirectory = list_files_in_directory("res://dialogic/characters")
if characterDirectory.size() == characterFolderBreakdown.size():
%OutputLog.text += "Character files found: [color=green]" + str(characterDirectory.size()) + "[/color]\r\n"
else:
%OutputLog.text += "Character files found: [color=red]" + str(characterDirectory.size()) + "[/color]\r\n"
%OutputLog.text += "[color=yellow]There may be an issue, please check in Dialogic 1.x to make sure that is correct![/color]\r\n"
file = FileAccess.open("res://dialogic/definitions.json",FileAccess.READ)
fileContent = file.get_as_text()
json_object = JSON.new()
error = json_object.parse(fileContent)
if error == OK:
definitionsFile = json_object.get_data()
#print(folderStructure)
else:
print("JSON Parse Error: ", json_object.get_error_message(), " in ", error, " at line ", json_object.get_error_line())
%OutputLog.text += "Dialogic 1.x definitions file [color=red]could not[/color] be read!\r\n"
%OutputLog.text += "Please check the output log for the error the JSON parser encountered.\r\n"
return
for variable in definitionsFile["variables"]:
var varPath = definitionFolderBreakdown[variable["id"]]
var variableInfo = {}
variableInfo["type"] = "variable"
variableInfo["path"] = varPath
variableInfo["name"] = variable["name"]
variableInfo["value"] = variable["value"]
definitionFolderBreakdown[variable["id"]] = variableInfo
for variable in definitionsFile["glossary"]:
var varPath = definitionFolderBreakdown[variable["id"]]
var variableInfo = {}
variableInfo["type"] = "glossary"
variableInfo["path"] = varPath
variableInfo["name"] = variable["name"]
variableInfo["text"] = variable["text"]
variableInfo["title"] = variable["title"]
variableInfo["extra"] = variable["extra"]
variableInfo["glossary_type"] = variable["type"]
definitionFolderBreakdown[variable["id"]] = variableInfo
if (definitionsFile["glossary"].size() + definitionsFile["variables"].size()) == definitionFolderBreakdown.size():
%OutputLog.text += "Definitions found: [color=green]" + str((definitionsFile["glossary"].size() + definitionsFile["variables"].size())) + "[/color]\r\n"
%OutputLog.text += " • Glossaries found: " + str(definitionsFile["glossary"].size()) + "\r\n"
%OutputLog.text += " • Variables found: " + str(definitionsFile["variables"].size()) + "\r\n"
else:
%OutputLog.text += "Definition files found: [color=red]" + str(definitionsFile.size()) + "[/color]\r\n"
%OutputLog.text += "[color=yellow]There may be an issue, please check in Dialogic 1.x to make sure that is correct![/color]\r\n"
var themeDirectory = list_files_in_directory("res://dialogic/themes")
if themeDirectory.size() == themeFolderBreakdown.size():
%OutputLog.text += "Theme files found: [color=green]" + str(themeDirectory.size()) + "[/color]\r\n"
else:
%OutputLog.text += "Theme files found: [color=red]" + str(themeDirectory.size()) + "[/color]\r\n"
%OutputLog.text += "[color=yellow]There may be an issue, please check in Dialogic 1.x to make sure that is correct![/color]\r\n"
# dirty check for the variable subsystem, as properly calling has subsystem is complicated currently
varSubsystemInstalled = file.file_exists("res://addons/dialogic/Events/Variable/event_variable.gd")
if !varSubsystemInstalled:
%OutputLog.text += "[color=yellow]Variable subsystem is not present in this Dialogic! Variables will not be converted![/color]"
%OutputLog.text += "\r\n"
%OutputLog.text += "Initial integrity check completed!\r\n"
if DirAccess.dir_exists_absolute(conversionRootFolder):
%OutputLog.text += "[color=yellow]Conversion folder already exists, coverting will overwrite existing files.[/color]\r\n"
else:
%OutputLog.text += conversionRootFolder
%OutputLog.text += "Folders are being created in " + conversionRootFolder + ". Converted files will be located there.\r\n"
var directory = DirAccess.open("res://")
directory.make_dir(conversionRootFolder)
directory.open(conversionRootFolder)
directory.make_dir("characters")
directory.make_dir("timelines")
directory.make_dir("themes")
conversionReady = true
$RightPanel/Begin.disabled = false
else:
%OutputLog.text += "[X] Dialogic 1.x data [color=red]not found![/color]\r\n"
%OutputLog.text += "Please copy the res://dialogic folder from your Dialogic 1.x project into this project and try again.\r\n"
func list_files_in_directory(path):
var files = []
var dir = DirAccess.open(path)
dir.list_dir_begin()
while true:
var file = dir.get_next()
if file == "":
break
elif not file.begins_with("."):
if file.ends_with(".json") || file.ends_with(".cfg"):
files.append(file)
dir.list_dir_end()
return files
func recursive_search(currentCheck, currentDictionary, currentFolder):
for structureFile in currentDictionary["files"]:
match currentCheck:
"Timeline": timelineFolderBreakdown[structureFile] = characterNameConversion(currentFolder, false)
"Character": characterFolderBreakdown[structureFile] = characterNameConversion(currentFolder,false)
"Definition": definitionFolderBreakdown[structureFile] = characterNameConversion(currentFolder, false)
"Theme": themeFolderBreakdown[structureFile] = characterNameConversion(currentFolder, false)
for structureFolder in currentDictionary["folders"]:
recursive_search(currentCheck, currentDictionary["folders"][structureFolder], currentFolder + structureFolder + "/")
func _on_begin_pressed():
%OutputLog.text += "-----------------------------------------\r\n"
%OutputLog.text += "Beginning file conversion:\r\n"
%OutputLog.text += "\r\n"
#Variable conversion needs to be first, to build the lookup table for new style
#Character conversion needs to be before timelines, so the character names are available
convertVariables()
convertCharacters()
convertTimelines()
convertGlossaries()
convertThemes()
convertSettings()
%OutputLog.text += "All conversions complete!\r\n"
%OutputLog.text += "\r\nPlease check to make sure your timelines all look good. After that, you can remove the /addons/dialogic/Events/Converter folder, as it is no longer needed.\r\n\r\n"
%OutputLog.text += "Please be aware, Godot may take some time on the next project load to reload all of the Characters and Timelines. This is normal, and should only happen the one time."
func convertTimelines():
%OutputLog.text += "Converting timelines: \r\n"
for item in timelineFolderBreakdown:
var folderPath = timelineFolderBreakdown[item]
%OutputLog.text += "Timeline " + folderPath + item +": "
var jsonData = {}
var file := FileAccess.open("res://dialogic/timelines/" + item, FileAccess.READ)
var fileContent = file.get_as_text()
var json_object = JSON.new()
var error = json_object.parse(fileContent)
if error == OK:
contents = json_object.get_data()
var fileName = contents["metadata"]["name"]
%OutputLog.text += "Name: " + fileName + ", " + str(contents["events"].size()) + " timeline events"
if not DirAccess.dir_exists_absolute(conversionRootFolder + "/timelines" + folderPath):
var directory = DirAccess.open(conversionRootFolder + "/timelines")
var progresiveDirectory = ""
for pathItem in folderPath.split('/'):
directory.open(conversionRootFolder + "/timelines" + progresiveDirectory)
if pathItem!= "":
progresiveDirectory += "/" + pathItem
if !directory.dir_exists(conversionRootFolder + "/timelines" + progresiveDirectory):
directory.make_dir(conversionRootFolder + "/timelines" + progresiveDirectory)
#just double check because sometimes its making double slashes at the final filename
if folderPath.right(1) == "/":
folderPath = folderPath.left(-1)
# we will save it as an intermediary file first, then on second pass cleanup make it the .dtl
var newFilePath = conversionRootFolder + "/timelines" + folderPath + "/" + fileName + ".cnv"
file = FileAccess.open(newFilePath, FileAccess.WRITE)
# update the new location so we know where second pass items are
timelineFolderBreakdown[item] = newFilePath
var processedEvents = 0
var depth = []
for event in contents["events"]:
processedEvents += 1
var eventLine = ""
for i in depth:
eventLine += " "
if "dialogic_" in event["event_id"]:
match event["event_id"]:
"dialogic_001":
#Text Event
var has_character:bool = false
if event['character'] != "" && event['character']:
has_character = true
eventLine += characterFolderBreakdown[event['character']]['searchable_name']
if event['portrait'] != "":
eventLine += "(" + event['portrait'] + ")"
eventLine += ": "
if '\n' in event['text']:
var splitCount = 0
var split = event['text'].split('\n')
for splitItem in split:
if has_character == false && splitItem.find(' ') > 0 && splitItem.find(':') > 0 && (splitItem.find(' ') > splitItem.find(':')):
splitItem = splitItem.insert("\\", splitItem.find(':'))
if splitCount == 0:
file.store_line(eventLine + splitItem + "\\")
else:
file.store_line(splitItem + "\\")
splitCount += 1
else:
var text_line = variableNameConversion(event['text'])
if has_character == false && text_line.find(' ') > 0 && text_line.find(':') > 0 && (text_line.find(' ') > text_line.find(':')):
text_line = text_line.insert("\\", text_line.find(':'))
file.store_string(eventLine + text_line)
"dialogic_002":
# Character event
#For some reason this is loading as a float, and the match is failing. so hard casting as string
var eventType:String = str(event['type'])
match eventType:
"0":
if event['character'] != "":
eventLine += "Join "
eventLine += characterFolderBreakdown[event['character']]['searchable_name']
if (event['portrait'] != ""):
eventLine += " (" + event['portrait'] + ") "
for i in event['position']:
if event['position'][i] == true:
#1.x uses positions 0-4, while the default 2.0 scene uses positions 1-5
eventLine += str(i.to_int() + 1)
if (event['animation'] != "[Default]" && event['animation'] != "") || ('z_index' in event) || ('mirror_portrait' in event):
# Note: due to Anima changes, animations will be converted into a default. Times and wait will be perserved
eventLine += " ["
if (event['animation'] != "[Default]" && event['animation'] != ""):
eventLine += " [animation=\"Instant In Or Out\" "
eventLine += "length=\"" + str(event['animation_length']) + "\""
if "animation_wait" in event:
eventLine += " wait=\"true\""
if 'z_index' in event:
if event['z_index'] != 0:
eventLine += ' z-index="' + str(event['z_index']) + '"'
if 'mirror_portrait' in event:
if event['mirror_portrait']:
eventLine += ' mirrored="true"'
eventLine += "]"
file.store_string(eventLine)
else:
eventLine += "# Character join event that did not have a selected character"
file.store_string(eventLine)
"2":
if event['character'] != "":
if event['character'] != "[All]":
eventLine += "Update "
eventLine += characterFolderBreakdown[event['character']]['searchable_name']
if 'portrait' in event:
if (event['portrait'] != "") && (event['portrait'] != "(Don't change)"):
eventLine += " (" + event['portrait'] + ") "
var positionCheck = false
if 'position' in event:
for i in event['position']:
if event['position'][i] == true:
positionCheck = true
eventLine += str(i.to_int() + 1)
if !positionCheck:
eventLine += " 0"
if (event['animation'] != "[Default]" && event['animation'] != "") || ('z_index' in event) || ('mirror_portrait' in event):
# Note: due to Anima changes, animations will be converted into a default. Times and wait will be perserved
eventLine += " ["
if (event['animation'] != "[Default]" && event['animation'] != ""):
eventLine += "animation=\"Heartbeat\" "
eventLine += "length=\"" + str(event['animation_length']) + "\""
if "animation_wait" in event:
eventLine += " wait=\"true\""
if "animation_repeat" in event:
eventLine += " repeat=\"" + str(event['animation_repeat']) + "\""
if 'z_index' in event: if 'z_index' in event:
if event['z_index'] != 0:
eventLine += ' z-index="' + str(event['z_index']) + '"'
if 'mirror_portrait' in event:
if event['mirror_portrait']:
eventLine += ' mirrored="true"'
eventLine += "]"
file.store_string(eventLine)
else:
file.store_string(eventLine + "# Update and Leave All not currently implemented")
else:
eventLine += "# Character Update event that did not have a selected character"
file.store_string(eventLine)
"1":
if event['character'] != "":
eventLine += "Leave "
if event['character'] == "[All]":
eventLine += "--All--"
else:
eventLine += characterFolderBreakdown[event['character']]['searchable_name']
if event['animation'] != "[Default]" && event['animation'] != "":
# Note: due to Anima changes, animations will be converted into a default. Times and wait will be perserved
eventLine += " [animation=\"Instant In Or Out\" "
eventLine += "length=\"" + str(event['animation_length']) + "\""
if "animation_wait" in event:
eventLine += " wait=\"true\""
eventLine += "]"
file.store_string(eventLine)
else:
eventLine += " # Character Update event that did not have a selected character"
file.store_string(eventLine)
_:
file.store_string("# failed" + str(event['type']))
"dialogic_010":
# Question event
# With the change in 2.0, the root of the Question block is simply text event
if event['character'] != "" && event['character']:
eventLine += characterFolderBreakdown[event['character']]['name']
if event['portrait'] != "":
eventLine += "(" + event['portrait'] + ")"
eventLine += ": "
if '\n' in event['question']:
var splitCount = 0
var split = event['text'].split('\n')
for splitItem in split:
if splitCount == 0:
file.store_line(eventLine + splitItem + "\\")
else:
file.store_line(splitItem + "\\")
splitCount += 1
else:
file.store_string(eventLine + event['question'])
#depth.push_front("question")
"dialogic_011":
#Choice event
#Choice's in 1.x have depth, but they do not have matching End Nodes as Questions and Conditionals do
if depth.size() > 0:
if depth[0] == "choice":
#reset the tabs for this choice to be one tree up
if depth.size() == 1:
eventLine = ""
else:
for i in (depth.size() - 1):
eventLine = " "
else:
#for the next line we want to add a depth
depth.push_front("choice")
eventLine += "- "
eventLine += event['choice']
if 'value' in event:
if event['value'] != "":
var valueLookup = variableNameConversion("[" + definitionFolderBreakdown[event['definition']]['path'] + definitionFolderBreakdown[event['definition']]['name'] + "]" )
eventLine += " [if "
eventLine += valueLookup
if event['condition'] != "":
eventLine += " " + event['condition']
else:
#default is true, so it may not store it
eventLine += " =="
# weird line due to missing type casts on String in current Godot 4 alpha
if event['value'] == str(event['value'].to_int()):
eventLine += " " + event['value']
else:
eventLine += " \"" + event['value'] + "\""
eventLine += "]"
file.store_string(eventLine)
#print("choice node")
#print ("bracnh depth now" + str(depth))
"dialogic_012":
#If event
var valueLookup = variableNameConversion("[" + definitionFolderBreakdown[event['definition']]['path'] + definitionFolderBreakdown[event['definition']]['name'] + "]" )
eventLine += "if "
eventLine += valueLookup
if event['condition'] != "":
eventLine += " " + event['condition']
else:
#default is true, so it may not store it
eventLine += " =="
# weird line due to missing type casts on String in current Godot 4 alpha
if event['value'] == str(event['value'].to_int()):
eventLine += " " + event['value']
else:
eventLine += " \"" + event['value'] + "\""
eventLine += ":"
file.store_string(eventLine)
#print("if branch node")
depth.push_front("condition")
#print ("bracnh depth now" + str(depth))
"dialogic_013":
#End Branch event
# doesnt actually make any lines, just adjusts the tab depth
#print("end branch node")
var _popped = depth.pop_front()
#print ("bracnh depth now" + str(depth))
"dialogic_014":
#Set Value event
if varSubsystemInstalled:
eventLine += "VAR "
eventLine += variableNameConversion("[" + definitionFolderBreakdown[event['definition']]['path'] + definitionFolderBreakdown[event['definition']]['name'] + "]" )
eventLine += " = "
if "set_random" in event:
if event['set_random'] == true:
eventLine += "[random=\"True\""
if "random_lower_limit" in event:
eventLine += " min=\"" + str(event['random_lower_limit']) + "\""
if "random_upper_limit" in event:
eventLine += " max=\"" + str(event['random_upper_limit']) + "\""
eventLine += "]"
else:
eventLine += event['set_value']
else:
eventLine += event['set_value']
file.store_string(eventLine)
else:
file.store_string(eventLine + "# Set variable function. Variables subsystem is disabled")
"dialogic_015":
#Label event
file.store_string(eventLine + "[label name=\"" + event['name'] +"\"]")
anchorNames[event['id']] = event['name']
"dialogic_016":
#Goto event
# Dialogic 1.x only allowed jumping to labels in the same timeline
# But since it is stored as a ID reference, we will have to get it on the second pass
file.store_string(eventLine + "[jump label=<" + event['anchor_id'] +">]")
#file.store_string(eventLine + "# jump label, just a comment for testing")
"dialogic_020":
#Change Timeline event
# we will need to come back to this one on second pass, since we may not know the new path yet
file.store_string(eventLine + "[jump timeline=<" + event['change_timeline'] +">]")
#file.store_string(eventLine + "# jump timeline, just a comment for testing")
"dialogic_021":
#Change Background event
#in Dialogic 1.x, fade default value was 1. its now 0, so we need to always write one here
var time: float = 1.0
if "fade_duration" in event:
time = event['fade_duration']
file.store_string(eventLine + "[background arg=\"" + event['background'] +"\" fade=\"" + str(time) + "\"]")
"dialogic_022":
#Close Dialog event
file.store_string(eventLine + "[end_timeline]")
"dialogic_023":
#Wait event
eventLine += "[wait time=\"" + str(event['wait_seconds']) + "\""
if !("hide_dialogbox" in event) || (event['hide_dialogbox'] == true):
eventLine += ' hide_text="true"'
else:
eventLine += ' hide_text="false"'
eventLine += "]"
file.store_string(eventLine)
"dialogic_024":
#Change Theme event
file.store_string(eventLine + '[style name="<' + event['set_theme'] + '>"]')
"dialogic_025":
#Set Glossary event
file.store_string(eventLine + "# Set Glossary event, not currently implemented")
"dialogic_026":
#Save event
if event['use_default_slot']:
file.store_string(eventLine + "[save slot=\"Default\"]")
else:
file.store_string(eventLine + "[save slot=\"" + event['custom_slot'] + "\"]")
"dialogic_030":
#Audio event
eventLine += "[sound"
eventLine += " path=\"" + event['file'] + "\""
eventLine += " volume=\"" + str(event['volume']) + "\""
eventLine += " bus=\"" + event['audio_bus'] + "\"]"
file.store_string(eventLine)
"dialogic_031":
#Background Music event
eventLine += "[music"
eventLine += " path=\"" + event['file'] + "\""
eventLine += " volume=\"" + str(event['volume']) + "\""
eventLine += " fade=\"" + str(event['fade_length']) + "\""
eventLine += " bus=\"" + event['audio_bus'] + "\""
eventLine += " loop=\"true\"]"
file.store_string(eventLine)
"dialogic_040":
#Emit Signal event
file.store_string(eventLine + "[signal arg=\"" + event['emit_signal'] +"\"]")
"dialogic_041":
#Change Scene event
file.store_string(eventLine + "# Change scene event is deprecated. Scene called was: " + event['change_scene'])
"dialogic_042":
#Call Node event
eventLine += "[call_node path=\"" + event['call_node']['target_node_path'] + "\" "
eventLine += "method=\"" + event['call_node']['method_name'] + "\" "
eventLine += "args=\"["
for arg in event['call_node']['arguments']:
eventLine += "\"" + arg + "\", "
#remove the last comma and space
eventLine = eventLine.left(-2)
eventLine += "]\"]"
file.store_string(eventLine)
_:
file.store_string(eventLine + "# unimplemented Dialogic control with unknown number")
else:
var returnString = CustomEventConverter.convertCustomEvent(event)
if returnString != "":
file.store_string(eventLine + returnString)
else:
eventLine += "# Custom event: "
eventLine += str(event)
eventLine = eventLine.replace("{", "*")
eventLine = eventLine.replace("}", "*")
file.store_string(eventLine)
file.store_string("\r\n\r\n")
%OutputLog.text += "Processed events: " + str(processedEvents) + "\r\n"
else:
%OutputLog.text += "[color=red]There was a problem parsing this file![/color]\r\n"
%OutputLog.text += "\r\n"
#second pass
for item in timelineFolderBreakdown:
%OutputLog.text += "Verifying file: " + timelineFolderBreakdown[item] + "\r\n"
var oldFile = FileAccess.open(timelineFolderBreakdown[item], FileAccess.READ)
var newFile = FileAccess.open(timelineFolderBreakdown[item].replace(".cnv", ".dtl"), FileAccess.WRITE)
var regex = RegEx.new()
regex.compile('(<.*?>)')
#var result = regex.search_all(oldText)
var whitespaceCount = 0
while oldFile.get_position() < oldFile.get_length():
var line = oldFile.get_line()
if line.length() == 0:
#clean up any extra whitespace so theres only one line betwen each command
whitespaceCount += 1
if whitespaceCount < 2:
newFile.store_string("\r\n\r\n")
else:
whitespaceCount = 0
var result = regex.search_all(line)
if result:
for res in result:
var r_string = res.get_string()
var newString = r_string.substr(1,r_string.length()-2)
if "timeline" in line:
newString = "\"" + timelineFolderBreakdown[newString].replace(".cnv", ".dtl") + "\""
if "label" in line:
newString = "\"" + anchorNames[newString] + "\""
if "style" in line:
var prev = newString
var config = ConfigFile.new()
var error = config.load("res://dialogic/themes/" + newString)
if error == OK:
if config.has_section_key("settings", "name"):
newString = config.get_value("settings", "name")
if newString == prev:
newString = "DefaultTheme"
line = line.replace(r_string,newString)
newFile.store_string(line)
else:
newFile.store_string(line)
oldFile = null
var fileDirectory = timelineFolderBreakdown[item].replace(timelineFolderBreakdown[item].split("/")[-1], "")
var dir = DirAccess.open(fileDirectory)
dir.remove(timelineFolderBreakdown[item])
%OutputLog.text += "Completed conversion of file: " + timelineFolderBreakdown[item].replace(".cnv", ".dtl") + "\r\n"
#print(item)
func convertCharacters():
%OutputLog.text += "Converting characters: \r\n"
for item in characterFolderBreakdown:
var original_file = item
var folderPath = characterFolderBreakdown[item]
%OutputLog.text += "Character " + folderPath + item +": "
var jsonData = {}
var file := FileAccess.open("res://dialogic/characters/" + item, FileAccess.READ)
var fileContent = file.get_as_text()
var json_object = JSON.new()
var error = json_object.parse(fileContent)
if error == OK:
contents = json_object.get_data()
var fileName = contents["name"]
%OutputLog.text += "Name: " + fileName + "\r\n"
if ("[" in fileName) || ("]" in fileName) || ("?" in fileName):
%OutputLog.text += " [color=yellow]Stripping invalid characters from file name![/color]\r\n"
fileName = characterNameConversion(fileName)
if not DirAccess.dir_exists_absolute(conversionRootFolder + "/characters" + folderPath):
var directory = DirAccess.open(conversionRootFolder + "/characters")
var progresiveDirectory = ""
for pathItem in folderPath.split('/'):
directory.open(conversionRootFolder + "/characters" + progresiveDirectory)
if pathItem!= "":
progresiveDirectory += "/" + pathItem
if !directory.dir_exists(conversionRootFolder + "/characters" + progresiveDirectory):
directory.make_dir(conversionRootFolder + "/characters" + progresiveDirectory)
#add the prefix if the prefix option is enabled
if prefixCharacters:
var prefix = ""
for level in folderPath.split('/'):
if level != "":
prefix += level.left(2) + "-"
fileName = prefix + fileName
# using the resource constructor for this one
var current_character = DialogicCharacter.new()
current_character.resource_path = conversionRootFolder + "/characters" + folderPath + "/" + fileName + ".dch"
# Everything needs to be in exact order
current_character.color = Color(contents["color"].right(6))
var customInfoDict = {}
customInfoDict["sound_moods"] = {}
customInfoDict["theme"] = ""
current_character.custom_info = customInfoDict
current_character.description = varNameStripSpecial(contents["description"])
if contents["display_name"] == "":
current_character.display_name = varNameStripSpecial(contents["name"])
else:
current_character.display_name = varNameStripSpecial(contents["display_name"])
current_character.mirror = contents["mirror_portraits"]
#current_character.name = varNameStripSpecial(contents["name"])
current_character.nicknames = []
current_character.offset = Vector2(0,0)
var portraitsList = {}
var firstPortrait = ""
for portrait in contents['portraits']:
var portraitData = {}
if portrait['path'] != "":
if ".tscn" in portrait['path']:
portraitData['scene'] = portrait['path']
else:
portraitData['image'] = portrait['path']
#use the global offset, scale, and mirror setting from the origianl character file
portraitData['offset'] = Vector2(contents['offset_x'], contents['offset_y'])
portraitData['scale'] = contents['scale'] / 100
portraitData['mirror'] = contents['mirror_portraits']
#discard it if there's an empty Default, so it doesn't throw an error
if !((portrait['name'] == "Default") && (portrait['path'] == "")) && !((portrait['name'] == "") && (portrait['path'] == "")):
portraitsList[portrait['name']] = portraitData
if firstPortrait == "":
firstPortrait = portrait['name']
current_character.portraits = portraitsList
if firstPortrait != "":
current_character.default_portrait = firstPortrait
current_character.scale = 1.0
ResourceSaver.save(current_character, current_character.resource_path)
# Before we're finished here, update the folder breakdown so it has the proper character name
var infoDict = {}
infoDict["original_file"] = original_file
infoDict["path"] = characterFolderBreakdown[item]
infoDict["name"] = fileName
var name_breakdown:Array = ( folderPath + fileName).split("/")
name_breakdown.reverse()
infoDict["name_breakdown"] = name_breakdown
infoDict["searchable_name"] = infoDict["name_breakdown"][0]
characterFolderBreakdown[item] = infoDict
%OutputLog.text += "\r\n"
else:
%OutputLog.text += "[color=red]There was a problem parsing this file![/color]\r\n"
# Second pass, shorten all of the paths, so they match the character dictionary in Dialogic itself
# Temporarily need an array to be able to sort
var sorting_array:Array = []
for item in characterFolderBreakdown:
sorting_array.append(characterFolderBreakdown[item])
sorting_array.sort_custom(func(a, b):return a['name_breakdown'].count("/") < b['name_breakdown'].count("/"))
var clean_search_path:bool = false
var depth = 1
while !clean_search_path:
var interim_array:Array = []
clean_search_path = true
for i in sorting_array.size():
if sorting_array.filter(func(val): return val['searchable_name'] == sorting_array[i]['searchable_name']).size() > 1:
clean_search_path = false
var replace_dict:Dictionary = sorting_array[i]
replace_dict["searchable_name"] = replace_dict["name_breakdown"][depth] + "/" + replace_dict["searchable_name"]
interim_array.append(replace_dict)
else:
interim_array.append(sorting_array[i])
depth += 1
sorting_array = interim_array
characterFolderBreakdown.clear()
for item in sorting_array:
if item["searchable_name"].count(" ") > 0:
item["searchable_name"] = '"' + item["searchable_name"] + '"'
characterFolderBreakdown[item['original_file']] = item
%OutputLog.text += "Final character name for " + item['original_file'] + ": " + item['searchable_name'] + "\r\n"
%OutputLog.text += "\r\n"
func convertVariables():
%OutputLog.text += "Converting variables: \r\n"
var convertedVariables = 0
# Creating a file with a format identical to how the variables are stored in project settings
if varSubsystemInstalled:
var newVariableDictionary = {}
for varItem in definitionFolderBreakdown:
if "type" in definitionFolderBreakdown[varItem]:
if definitionFolderBreakdown[varItem]["type"] == "variable":
if definitionFolderBreakdown[varItem]["path"] == "/":
newVariableDictionary[varNameStripSpecial(definitionFolderBreakdown[varItem]["name"])] = definitionFolderBreakdown[varItem]["value"]
flatDefinitionsFile[varNameStripSpecial(definitionFolderBreakdown[varItem]["name"])] = varItem
convertedVariables += 1
else:
# I will fill this one in later, need to figure out the recursion for it
var dictRef = newVariableDictionary
var flatNameBuilder = ""
for pathItem in varNameStripSpecial(definitionFolderBreakdown[varItem]["path"]).split("/"):
if pathItem != "":
if pathItem in dictRef:
dictRef = dictRef[pathItem]
flatNameBuilder += pathItem + "."
else:
dictRef[pathItem] = {}
dictRef = dictRef[pathItem]
flatNameBuilder += pathItem + "."
dictRef[varNameStripSpecial(definitionFolderBreakdown[varItem]["name"])] = definitionFolderBreakdown[varItem]["value"]
convertedVariables +=1
var flatName = flatNameBuilder + varNameStripSpecial(definitionFolderBreakdown[varItem]["name"])
flatDefinitionsFile[flatName] = varItem
ProjectSettings.set_setting('dialogic/variables', null)
ProjectSettings.set_setting('dialogic/variables', newVariableDictionary)
ProjectSettings.save()
#rebuild the data in the other tabs, so it doesnt override it
find_parent('SettingsEditor').refresh()
%OutputLog.text += str(convertedVariables) + " variables converted, and saved to project!\r\n"
else:
%OutputLog.text += "[color=yellow]Variable subsystem is not present! Variables were not converted![/color]\r\n"
%OutputLog.text += "\r\n"
func convertGlossaries():
%OutputLog.text += "Converting glossaries: [color=red]not currently implemented[/color] \r\n"
%OutputLog.text += "\r\n"
func convertThemes():
%OutputLog.text += "Converting themes: [color=red]not currently implemented[/color] \r\n"
%OutputLog.text += "\r\n"
func varNameStripSpecial(oldVariable):
# This is to remove special characters from variable names
# Since in code variables are accessed by Dialogic.VAR.path.to.variable, characters not usable in Godot paths have to be removed
var newVariable = oldVariable
newVariable = newVariable.replace(" ", "_")
newVariable = newVariable.replace(".", "_")
newVariable = newVariable.replace("-", "_")
return(newVariable)
func variableNameConversion(oldText):
var newText = oldText
var regex = RegEx.new()
regex.compile('(\\[.*?\\])')
var result = regex.search_all(oldText)
if result:
for res in result:
var r_string = res.get_string()
var newString = res.get_string()
newString = newString.replace("[", "")
newString = newString.replace("]", "")
if newString[0] == '/':
newString = newString.right(-1)
newString = varNameStripSpecial(newString)
newString = newString.replace("/", ".")
if newString in flatDefinitionsFile:
newString = "{" + newString + "}"
newText = newText.replace(r_string, newString)
return(newText)
func characterNameConversion(oldText:String, filter_forward_slash:bool = true):
#as some characters aren't supported in filenames, we need to convert both the filenames, and references to them
#Most character name restrictions are only restrictions of Windows, but we are going to enforce it for Dialogic due to node and regex complexities for platform uniformity
#We're also restricting the special characters used for other designations
var newText = oldText
newText = newText.replace("<","[")
newText = newText.replace(">","]")
newText = newText.replace(":","-")
newText = newText.replace("\\","/")
if filter_forward_slash:
newText = newText.replace("/","-")
newText = newText.replace("|","-")
newText = newText.replace("*","@")
newText = newText.replace("?","0")
newText = newText.replace('"',"'")
#These ones as they are the delimiter for portraits
newText = newText.replace("(","[")
newText = newText.replace(")","]")
return newText
func convertSettings():
%OutputLog.text += "Converting other settings: \r\n"
%OutputLog.text += "[color=yellow]Note! Most original settings can't be converted.[/color] \r\n"
var config = ConfigFile.new()
var err = config.load("res://dialogic/settings.cfg")
if err != OK:
%OutputLog.text += "[color=red]Dialogic 1.x Settings file could not be loaded![/color] \r\n"
return
ProjectSettings.set_setting('dialogic/text/autocolor_names', config.get_value("dialog", "auto_color_names", true))
ProjectSettings.set_setting('dialogic/choices/autofocus_first', config.get_value("input", "autofocus_choices", false))
ProjectSettings.set_setting('dialogic/choices/delay', config.get_value("input", "delay_after_options", 0.2))
ProjectSettings.save()
func _on_check_box_toggled(button_pressed):
prefixCharacters = button_pressed
%OutputLog.text += "\r\n\r\nToggling this will add a prefix to all character filenames, which will have letters from each folder depth they are in. Characters in the root folder will have no prefix. \r\n"