628 lines
18 KiB
GDScript
628 lines
18 KiB
GDScript
tool
|
|
class_name DialogicUtil
|
|
|
|
|
|
|
|
|
|
static func list_to_dict(list):
|
|
var dict: = {}
|
|
for val in list:
|
|
dict[val["file"]] = val
|
|
return dict
|
|
|
|
|
|
|
|
|
|
|
|
static func get_character_list()->Array:
|
|
var characters:Array = []
|
|
for file in DialogicResources.listdir(DialogicResources.get_path("CHAR_DIR")):
|
|
if ".json" in file:
|
|
var data:Dictionary = DialogicResources.get_character_json(file)
|
|
|
|
characters.append({
|
|
"name":data.get("name", data["id"]),
|
|
"color":Color(data.get("color", "#ffffff")),
|
|
"file":file,
|
|
"portraits":data.get("portraits", []),
|
|
"display_name":data.get("display_name", ""),
|
|
"nickname":data.get("nickname", ""),
|
|
"data":data
|
|
})
|
|
return characters
|
|
|
|
|
|
|
|
static func get_characters_dict():
|
|
return list_to_dict(get_character_list())
|
|
|
|
|
|
static func get_sorted_character_list():
|
|
var array = get_character_list()
|
|
array.sort_custom(DialgicSorter, "sort_resources")
|
|
return array
|
|
|
|
|
|
|
|
static func get_character(character_id):
|
|
var characters = get_character_list()
|
|
for c in characters:
|
|
if c["file"] == character_id:
|
|
return c
|
|
return {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static func get_timeline_list()->Array:
|
|
var timelines:Array = []
|
|
for file in DialogicResources.listdir(DialogicResources.get_path("TIMELINE_DIR")):
|
|
if ".json" in file:
|
|
var data = DialogicResources.get_timeline_json(file)
|
|
if data.has("error") == false:
|
|
if data.has("metadata"):
|
|
var metadata = data["metadata"]
|
|
var color = Color("#ffffff")
|
|
if metadata.has("name"):
|
|
timelines.append({"name":metadata["name"], "color":color, "file":file})
|
|
else :
|
|
timelines.append({"name":file.split(".")[0], "color":color, "file":file})
|
|
return timelines
|
|
|
|
|
|
static func get_timeline_dict()->Dictionary:
|
|
return list_to_dict(get_timeline_list())
|
|
|
|
|
|
static func get_sorted_timeline_list():
|
|
var array = get_timeline_list()
|
|
array.sort_custom(DialgicSorter, "sort_resources")
|
|
return array
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static func get_theme_list()->Array:
|
|
var themes:Array = []
|
|
for file in DialogicResources.listdir(DialogicResources.get_path("THEME_DIR")):
|
|
if ".cfg" in file:
|
|
var config = DialogicResources.get_theme_config(file)
|
|
themes.append({
|
|
"file":file,
|
|
"name":config.get_value("settings", "name", file),
|
|
"config":config
|
|
})
|
|
return themes
|
|
|
|
|
|
static func get_theme_dict()->Dictionary:
|
|
return list_to_dict(get_theme_list())
|
|
|
|
|
|
static func get_sorted_theme_list():
|
|
var array = get_theme_list()
|
|
array.sort_custom(DialgicSorter, "sort_resources")
|
|
return array
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static func get_default_definitions_list()->Array:
|
|
return DialogicDefinitionsUtil.definitions_json_to_array(DialogicResources.get_default_definitions())
|
|
|
|
|
|
static func get_default_definitions_dict():
|
|
var dict = {}
|
|
for val in get_default_definitions_list():
|
|
dict[val["id"]] = val
|
|
return dict
|
|
|
|
|
|
static func get_sorted_default_definitions_list():
|
|
var array = get_default_definitions_list()
|
|
array.sort_custom(DialgicSorter, "sort_resources")
|
|
return array
|
|
|
|
|
|
static func compare_definitions(def_value:String, event_value:String, condition:String):
|
|
var definitions
|
|
if not Engine.is_editor_hint():
|
|
if Engine.get_main_loop().has_meta("definitions"):
|
|
definitions = Engine.get_main_loop().get_meta("definitions")
|
|
else :
|
|
definitions = DialogicResources.get_default_definitions()
|
|
Engine.get_main_loop().set_meta("definitions", definitions)
|
|
else :
|
|
definitions = DialogicResources.get_default_definitions()
|
|
var condition_met = false
|
|
if def_value != null and event_value != null:
|
|
|
|
|
|
|
|
|
|
|
|
var converted_def_value = def_value
|
|
var converted_event_value = event_value
|
|
if def_value.is_valid_float() and event_value.is_valid_float():
|
|
converted_def_value = float(def_value)
|
|
converted_event_value = float(event_value)
|
|
if condition == "":
|
|
condition = "=="
|
|
match condition:
|
|
"==":
|
|
condition_met = converted_def_value == converted_event_value
|
|
"!=":
|
|
condition_met = converted_def_value != converted_event_value
|
|
">":
|
|
condition_met = converted_def_value > converted_event_value
|
|
">=":
|
|
condition_met = converted_def_value >= converted_event_value
|
|
"<":
|
|
condition_met = converted_def_value < converted_event_value
|
|
"<=":
|
|
condition_met = converted_def_value <= converted_event_value
|
|
return condition_met
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static func get_parent_path(path:String):
|
|
return path.replace("/" + path.split("/")[ - 1], "")
|
|
|
|
|
|
|
|
|
|
static func get_full_resource_folder_structure():
|
|
return DialogicResources.get_resource_folder_structure()
|
|
|
|
static func get_timelines_folder_structure():
|
|
return get_folder_at_path("Timelines")
|
|
|
|
static func get_characters_folder_structure():
|
|
return get_folder_at_path("Characters")
|
|
|
|
static func get_definitions_folder_structure():
|
|
return get_folder_at_path("Definitions")
|
|
|
|
static func get_theme_folder_structure():
|
|
return get_folder_at_path("Themes")
|
|
|
|
|
|
|
|
static func get_folder_at_path(path):
|
|
var folder_data = get_full_resource_folder_structure()
|
|
|
|
for folder in path.split("/"):
|
|
if folder:
|
|
folder_data = folder_data["folders"][folder]
|
|
|
|
if folder_data == null:
|
|
folder_data = {"folders":{}, "files":[]}
|
|
return folder_data
|
|
|
|
|
|
|
|
static func set_folder_content_recursive(path_array:Array, orig_data:Dictionary, new_data:Dictionary)->Dictionary:
|
|
if len(path_array) == 1:
|
|
if path_array[0] in orig_data["folders"].keys():
|
|
if new_data.empty():
|
|
orig_data["folders"].erase(path_array[0])
|
|
else :
|
|
orig_data["folders"][path_array[0]] = new_data
|
|
else :
|
|
var current_folder = path_array.pop_front()
|
|
orig_data["folders"][current_folder] = set_folder_content_recursive(path_array, orig_data["folders"][current_folder], new_data)
|
|
return orig_data
|
|
|
|
static func set_folder_at_path(path:String, data:Dictionary):
|
|
var orig_structure = get_full_resource_folder_structure()
|
|
var new_data = set_folder_content_recursive(path.split("/"), orig_structure, data)
|
|
DialogicResources.save_resource_folder_structure(new_data)
|
|
return OK
|
|
|
|
|
|
static func set_folder_meta(folder_path:String, key:String, value):
|
|
var data = get_folder_at_path(folder_path)
|
|
data["metadata"][key] = value
|
|
set_folder_at_path(folder_path, data)
|
|
|
|
static func get_folder_meta(folder_path:String, key:String):
|
|
return get_folder_at_path(folder_path)["metadata"][key]
|
|
|
|
|
|
|
|
static func add_folder(path:String, folder_name:String):
|
|
|
|
if folder_name in get_folder_at_path(path)["folders"].keys():
|
|
print("[D] A folder with the name '" + folder_name + "' already exists in the target folder '" + path + "'.")
|
|
return ERR_ALREADY_EXISTS
|
|
|
|
var folder_data = get_folder_at_path(path)
|
|
folder_data["folders"][folder_name] = {"folders":{}, "files":[], "metadata":{"color":null, "folded":false}}
|
|
set_folder_at_path(path, folder_data)
|
|
|
|
return OK
|
|
|
|
static func remove_folder(folder_path:String, delete_files:bool = true):
|
|
|
|
for folder in get_folder_at_path(folder_path)["folders"]:
|
|
remove_folder(folder_path + "/" + folder, delete_files)
|
|
|
|
if delete_files:
|
|
for file in get_folder_at_path(folder_path)["files"]:
|
|
|
|
match folder_path.split("/")[0]:
|
|
"Timelines":
|
|
DialogicResources.delete_timeline(file)
|
|
"Characters":
|
|
DialogicResources.delete_character(file)
|
|
"Definitions":
|
|
DialogicResources.delete_default_definition(file)
|
|
"Themes":
|
|
DialogicResources.delete_theme(file)
|
|
set_folder_at_path(folder_path, {})
|
|
|
|
static func rename_folder(path:String, new_folder_name:String):
|
|
|
|
if new_folder_name in get_folder_at_path(get_parent_path(path))["folders"].keys():
|
|
print("[D] A folder with the name '" + new_folder_name + "' already exists in the target folder '" + get_parent_path(path) + "'.")
|
|
return ERR_ALREADY_EXISTS
|
|
elif new_folder_name.empty():
|
|
return ERR_PRINTER_ON_FIRE
|
|
|
|
|
|
|
|
var folder_content = get_folder_at_path(path)
|
|
|
|
|
|
remove_folder(path, false)
|
|
|
|
|
|
add_folder(get_parent_path(path), new_folder_name)
|
|
var new_path = get_parent_path(path) + "/" + new_folder_name
|
|
set_folder_at_path(new_path, folder_content)
|
|
|
|
return OK
|
|
|
|
static func move_folder_to_folder(orig_path, target_folder):
|
|
|
|
if orig_path.split("/")[ - 1] in get_folder_at_path(target_folder)["folders"].keys():
|
|
print("[D] A folder with the name '" + orig_path.split("/")[ - 1] + "' already exists in the target folder '" + target_folder + "'.")
|
|
return ERR_ALREADY_EXISTS
|
|
|
|
|
|
var folder_content = get_folder_at_path(orig_path)
|
|
|
|
|
|
|
|
remove_folder(orig_path, false)
|
|
|
|
|
|
var folder_name = orig_path.split("/")[ - 1]
|
|
add_folder(target_folder, folder_name)
|
|
var new_path = target_folder + "/" + folder_name
|
|
set_folder_at_path(new_path, folder_content)
|
|
|
|
return OK
|
|
|
|
|
|
static func move_file_to_folder(file_name, orig_folder, target_folder):
|
|
remove_file_from_folder(orig_folder, file_name)
|
|
add_file_to_folder(target_folder, file_name)
|
|
|
|
static func add_file_to_folder(folder_path, file_name):
|
|
var folder_data = get_folder_at_path(folder_path)
|
|
folder_data["files"].append(file_name)
|
|
set_folder_at_path(folder_path, folder_data)
|
|
|
|
static func remove_file_from_folder(folder_path, file_name):
|
|
var folder_data = get_folder_at_path(folder_path)
|
|
folder_data["files"].erase(file_name)
|
|
set_folder_at_path(folder_path, folder_data)
|
|
|
|
|
|
|
|
|
|
static func update_resource_folder_structure():
|
|
var character_files = DialogicResources.listdir(DialogicResources.get_path("CHAR_DIR"))
|
|
var timeline_files = DialogicResources.listdir(DialogicResources.get_path("TIMELINE_DIR"))
|
|
var theme_files = DialogicResources.listdir(DialogicResources.get_path("THEME_DIR"))
|
|
var definition_files = get_default_definitions_dict().keys()
|
|
|
|
var folder_structure = DialogicResources.get_resource_folder_structure()
|
|
|
|
folder_structure["folders"]["Timelines"] = check_folders_section(folder_structure["folders"]["Timelines"], timeline_files)
|
|
folder_structure["folders"]["Characters"] = check_folders_section(folder_structure["folders"]["Characters"], character_files)
|
|
folder_structure["folders"]["Themes"] = check_folders_section(folder_structure["folders"]["Themes"], theme_files)
|
|
folder_structure["folders"]["Definitions"] = check_folders_section(folder_structure["folders"]["Definitions"], definition_files)
|
|
|
|
DialogicResources.save_resource_folder_structure(folder_structure)
|
|
|
|
|
|
static func check_folders_section(section_structure:Dictionary, section_files:Array):
|
|
var result = check_folders_recursive(section_structure, section_files)
|
|
section_structure = result[0]
|
|
section_structure["files"] += result[1]
|
|
return section_structure
|
|
|
|
static func check_folders_recursive(folder_data:Dictionary, file_names:Array):
|
|
if not folder_data.has("metadata"):
|
|
folder_data["metadata"] = {"color":null, "folded":false}
|
|
for folder in folder_data["folders"].keys():
|
|
var result = check_folders_recursive(folder_data["folders"][folder], file_names)
|
|
folder_data["folders"][folder] = result[0]
|
|
file_names = result[1]
|
|
for file in folder_data["files"]:
|
|
if not file in file_names:
|
|
folder_data["files"].erase(file)
|
|
|
|
else :
|
|
file_names.erase(file)
|
|
return [folder_data, file_names]
|
|
|
|
|
|
static func beautify_filename(animation_name:String)->String:
|
|
if animation_name == "[Default]" or animation_name == "[No Animation]":
|
|
return animation_name
|
|
var a_string = animation_name.get_file().trim_suffix(".gd")
|
|
if "-" in a_string:
|
|
a_string = a_string.split("-")[1].capitalize()
|
|
else :
|
|
a_string = a_string.capitalize()
|
|
return a_string
|
|
|
|
|
|
|
|
|
|
|
|
static func generate_random_id()->String:
|
|
return str(OS.get_unix_time()) + "-" + str(100 + randi() % 899 + 1)
|
|
|
|
|
|
static func compare_dicts(dict_1:Dictionary, dict_2:Dictionary)->bool:
|
|
|
|
|
|
if str(dict_1) != "Null" and str(dict_2) != "Null":
|
|
if str(dict_1) == str(dict_2):
|
|
return true
|
|
return false
|
|
|
|
|
|
static func path_fixer_load(path):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
match path:
|
|
"res://addons/dialogic/Fonts/DefaultFont.tres":
|
|
return load("res://addons/dialogic/Example Assets/Fonts/DefaultFont.tres")
|
|
"res://addons/dialogic/Fonts/GlossaryFont.tres":
|
|
return load("res://addons/dialogic/Example Assets/Fonts/GlossaryFont.tres")
|
|
"res://addons/dialogic/Images/background/background-1.png":
|
|
return load("res://addons/dialogic/Example Assets/backgrounds/background-1.png")
|
|
"res://addons/dialogic/Images/background/background-2.png":
|
|
return load("res://addons/dialogic/Example Assets/backgrounds/background-2.png")
|
|
"res://addons/dialogic/Images/next-indicator.png":
|
|
return load("res://addons/dialogic/Example Assets/next-indicator/next-indicator.png")
|
|
|
|
return load(path)
|
|
|
|
|
|
|
|
static func resource_fixer():
|
|
var update_index = DialogicResources.get_settings_config().get_value("updates", "updatenumber", 0)
|
|
|
|
if update_index < 1:
|
|
print("[D] Update NR. " + str(update_index) + " | Adds event ids. Don't worry about this.")
|
|
for timeline_info in get_timeline_list():
|
|
var timeline = DialogicResources.get_timeline_json(timeline_info["file"])
|
|
|
|
var events = timeline["events"]
|
|
for i in events:
|
|
if not i.has("event_id"):
|
|
match i:
|
|
|
|
|
|
{"text", "character", "portrait"}:
|
|
i["event_id"] = "dialogic_001"
|
|
|
|
{"character", "action", "position", "portrait", ..}:
|
|
i["event_id"] = "dialogic_002"
|
|
|
|
{"character", "action"}:
|
|
i["event_id"] = "dialogic_003"
|
|
|
|
|
|
|
|
{"question", "options", ..}:
|
|
i["event_id"] = "dialogic_010"
|
|
|
|
{"choice", ..}:
|
|
i["event_id"] = "dialogic_011"
|
|
|
|
{"condition", "definition", "value"}:
|
|
i["event_id"] = "dialogic_012"
|
|
|
|
{"endbranch"}:
|
|
i["event_id"] = "dialogic_013"
|
|
|
|
{"set_value", "definition", ..}:
|
|
i["event_id"] = "dialogic_014"
|
|
|
|
|
|
|
|
{"change_timeline"}:
|
|
i["event_id"] = "dialogic_020"
|
|
|
|
{"background"}:
|
|
i["event_id"] = "dialogic_021"
|
|
|
|
{"close_dialog", ..}:
|
|
i["event_id"] = "dialogic_022"
|
|
|
|
{"wait_seconds"}:
|
|
i["event_id"] = "dialogic_023"
|
|
|
|
{"set_theme"}:
|
|
i["event_id"] = "dialogic_024"
|
|
|
|
|
|
|
|
{"audio", "file", ..}:
|
|
i["event_id"] = "dialogic_030"
|
|
|
|
{"background-music", "file", ..}:
|
|
i["event_id"] = "dialogic_031"
|
|
|
|
|
|
|
|
{"emit_signal"}:
|
|
i["event_id"] = "dialogic_040"
|
|
|
|
{"change_scene"}:
|
|
i["event_id"] = "dialogic_041"
|
|
|
|
{"call_node"}:
|
|
i["event_id"] = "dialogic_042"
|
|
timeline["events"] = events
|
|
DialogicResources.set_timeline(timeline)
|
|
if update_index < 2:
|
|
|
|
print("[D] Update NR. " + str(update_index) + " | Changes how some theme values are saved. No need to worry about this.")
|
|
for theme_info in get_theme_list():
|
|
var theme = DialogicResources.get_theme_config(theme_info["file"])
|
|
|
|
match theme.get_value("text", "alignment", "Left"):
|
|
"Left":
|
|
DialogicResources.set_theme_value(theme_info["file"], "text", "alignment", 0)
|
|
"Center":
|
|
DialogicResources.set_theme_value(theme_info["file"], "text", "alignment", 1)
|
|
"Right":
|
|
DialogicResources.set_theme_value(theme_info["file"], "text", "alignment", 2)
|
|
|
|
if update_index < 3:
|
|
|
|
print("[D] Update NR. " + str(update_index) + " | Removes Character Join and Character Leave events in favor of the new 'Character' event. No need to worry about this.")
|
|
for timeline_info in get_timeline_list():
|
|
var timeline = DialogicResources.get_timeline_json(timeline_info["file"])
|
|
var events = timeline["events"]
|
|
for i in range(len(events)):
|
|
if events[i]["event_id"] == "dialogic_002":
|
|
var new_event = {
|
|
"event_id":"dialogic_002",
|
|
"type":0,
|
|
"character":events[i].get("character", ""),
|
|
"portrait":events[i].get("portrait", "Default"),
|
|
"position":events[i].get("position"),
|
|
"animation":"[Default]",
|
|
"animation_length":0.5,
|
|
"mirror_portrait":events[i].get("mirror", false),
|
|
"z_index":events[i].get("z_index", 0),
|
|
}
|
|
if new_event["portrait"].empty():new_event["portrait"] = "Default"
|
|
events[i] = new_event
|
|
elif events[i]["event_id"] == "dialogic_003":
|
|
var new_event = {
|
|
"event_id":"dialogic_002",
|
|
"type":1,
|
|
"character":events[i].get("character", ""),
|
|
"animation":"[Default]",
|
|
"animation_length":0.5,
|
|
"mirror_portrait":events[i].get("mirror", false),
|
|
"z_index":events[i].get("z_index", 0),
|
|
}
|
|
events[i] = new_event
|
|
timeline["events"] = events
|
|
DialogicResources.set_timeline(timeline)
|
|
|
|
DialogicResources.set_settings_value("updates", "updatenumber", 3)
|
|
|
|
if not ProjectSettings.has_setting("input/dialogic_default_action"):
|
|
print("[D] Added the 'dialogic_default_action' to the InputMap. This is the default if you didn't select a different one in the dialogic settings. You will have to force the InputMap editor to update before you can see the action (reload project or add a new input action).")
|
|
var input_enter = InputEventKey.new()
|
|
input_enter.scancode = KEY_ENTER
|
|
var input_left_click = InputEventMouseButton.new()
|
|
input_left_click.button_index = BUTTON_LEFT
|
|
input_left_click.pressed = true
|
|
var input_space = InputEventKey.new()
|
|
input_space.scancode = KEY_SPACE
|
|
var input_x = InputEventKey.new()
|
|
input_x.scancode = KEY_X
|
|
var input_controller = InputEventJoypadButton.new()
|
|
input_controller.button_index = JOY_BUTTON_0
|
|
|
|
ProjectSettings.set_setting("input/dialogic_default_action", {"deadzone":0.5, "events":[input_enter, input_left_click, input_space, input_x, input_controller]})
|
|
ProjectSettings.save()
|
|
if DialogicResources.get_settings_value("input", "default_action_key", "[Default]") == "[Default]":
|
|
DialogicResources.set_settings_value("input", "default_action_key", "dialogic_default_action")
|
|
|
|
static func get_editor_scale(ref)->float:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var _scale = ref.get_constant("inspector_margin", "Editor")
|
|
_scale = _scale * 0.125
|
|
|
|
return _scale
|
|
|
|
|
|
static func list_dir(path:String)->Array:
|
|
var files = []
|
|
var dir = Directory.new()
|
|
dir.open(path)
|
|
dir.list_dir_begin(true)
|
|
|
|
var file = dir.get_next()
|
|
while file != "":
|
|
files += [file]
|
|
file = dir.get_next()
|
|
return files
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class DialgicSorter:
|
|
|
|
static func key_available(key, a:Dictionary)->bool:
|
|
return key in a.keys() and not a[key].empty()
|
|
|
|
static func get_compare_value(a:Dictionary)->String:
|
|
if key_available("display_name", a):
|
|
return a["display_name"]
|
|
|
|
if key_available("name", a):
|
|
return a["name"]
|
|
|
|
if key_available("id", a):
|
|
return a["id"]
|
|
|
|
if "metadata" in a.keys():
|
|
var a_metadata = a["metadata"]
|
|
if key_available("name", a_metadata):
|
|
return a_metadata["name"]
|
|
if key_available("file", a_metadata):
|
|
return a_metadata["file"]
|
|
return ""
|
|
|
|
static func sort_resources(a:Dictionary, b:Dictionary):
|
|
return get_compare_value(a).to_lower() < get_compare_value(b).to_lower()
|