@icon("res://components/Data/book-marked.svg") class_name DataType extends Base ## Data handler to read in data from files ## ## A data handler that will read in data from txt files and other sources for ## use in various parts of the game. Reads both from the parts folder in the ## main game directory and the mods folder in the user folder to allow users to ## easily mod the game. var _FILE_FUNCTIONS = { "*.txt": _read_txt, "*.png": _read_resource, "*.png.import": _read_png_import, "*.ogg": _read_resource, "*.mp3": _read_resource, "*.wav": _read_resource, "*.gd": _read_script_resource, } var _FILE_LOCATION_OVERRIDES = { "*.gd": "scripts", "*.png.import": "images", "*.png": "images", "*.ogg": "audio", "*.wav": "audio" } var data = {} var components = {} var location_overrides = {} func _prespawned(): _information = { "accent": "#a27155", "log_category": "DATA", "icon": "res://components/Data/book-marked.svg" } func _spawned(): reload_data() ## Reload the data object by pulling from available sources func reload_data() -> void: data = {} # Remove all old mod data # Open part folder from project (Ignore if none) and if exists set mod data to it var res_dir = DirAccess.open("res://") if res_dir.dir_exists("parts"): data = _read_directory("res://parts") data = _merge_objects(location_overrides, data) if res_dir.dir_exists("components"): location_overrides = {} components = _read_directory("res://components") components = _merge_objects(location_overrides, components) func _merge_objects(object1, object2): var newObject = {} for key in object1: newObject[key] = object1[key] for key in object2: if(newObject.has(key)): if(typeof(newObject[key]) == TYPE_STRING): newObject[key] = object1[key] elif(typeof(newObject[key]) == TYPE_OBJECT): if newObject[key] is Texture: newObject[key] = object1[key] else: newObject[key] = _merge_objects(object1[key], object2[key]) elif(typeof(newObject[key]) == TYPE_DICTIONARY): newObject[key] = _merge_objects(object1[key], object2[key]) elif(typeof(newObject[key]) == TYPE_ARRAY): newObject[key] = object1[key] + object2[key] else: newObject[key] = object2[key] return newObject func _read_directory(path) -> Dictionary: var dir = DirAccess.open(path) var local_data = {} if not dir: _warn("Could not read directory [%s]" % [path]) return {} dir.list_dir_begin() var file_name = dir.get_next() while file_name != "": if dir.current_is_dir(): var micro_data = _read_directory("%s/%s" % [path, file_name]) if micro_data: local_data[file_name] = micro_data else: var file_handling = _get_file_handling(file_name) if (file_handling): var location_override = _get_location_override(file_name) if location_override: var micro_data = file_handling.call("%s/%s" % [path, file_name]) if not location_overrides.has(location_override): location_overrides[location_override] = {} location_overrides[location_override][file_name.split(".")[0]] = micro_data else: var micro_data = file_handling.call("%s/%s" % [path, file_name]) if micro_data: local_data[file_name.split(".")[0]] = micro_data file_name = dir.get_next() _info("Read directory ♢%s♢" % [path]) return local_data func _get_file_handling(file): var split_file = file.split(".", true, 1) for file_function in _FILE_FUNCTIONS: if(file_function == file): return _FILE_FUNCTIONS[file_function] var split_file_function = file_function.split(".", true, 1) if(split_file_function[0] == "*" and split_file_function[1] == split_file[1]): return _FILE_FUNCTIONS[file_function] return null func _read_script_resource(path): return load(path) func _read_resource(path): var song = load(path) return song func _read_png_import(path): var image = load(path.rsplit(".", true, 1)[0]) return image func _get_location_override(file): var split_file = file.split(".", true, 1) for location_override in _FILE_LOCATION_OVERRIDES: if location_override == file: return _FILE_LOCATION_OVERRIDES[location_override] var split_file_function = location_override.split(".", true, 1) if(split_file_function[0] == "*" and split_file_function[1] == split_file[1]): return _FILE_LOCATION_OVERRIDES[location_override] func _read_txt(path) -> Dictionary: # Open file for reading var file = FileAccess.open(path, FileAccess.READ) var content = file.get_as_text() var local_data = {} # Separate into lines var split_content = content.split("\n", false) # Indentation var indentation = 0 var indentation_levels = [] # Iterate over everything for content_piece in split_content: # Fix Indentation to actual content level var actual_indentation = _count_indentation(content_piece) while(actual_indentation < indentation): indentation -= 1 indentation_levels.pop_back() # Navigate to current indentation data var micro_data = local_data var previous_data = null var i = 0 while i < indentation: previous_data = micro_data micro_data = micro_data[indentation_levels[i]] i += 1 # Reading if(content_piece.ends_with("[]")): # Array handling var trimmed_content = content_piece.strip_edges().trim_suffix("[]") indentation += 1 indentation_levels.append(trimmed_content) micro_data[trimmed_content] = {} else: var split_args = content_piece.split(":", true, 1) if split_args.size() == 2: # Dict Handling var key = split_args[0].strip_edges() var value = split_args[1].strip_edges() if value.is_valid_int(): value = value.to_int() micro_data[key] = value elif split_args.size() == 1: # Value Handling var value = split_args[0].strip_edges() if typeof(micro_data) == TYPE_DICTIONARY: # TODO: FIX BELOW if indentation_levels.size() == 0: continue if previous_data[indentation_levels[indentation_levels.size()-1]].keys().size() == 0: previous_data[indentation_levels[indentation_levels.size()-1]] = [split_args[0].strip_edges()] else: pass else: previous_data[indentation_levels[indentation_levels.size()-1]] += [split_args[0].strip_edges()] return local_data func _count_indentation(string: String): var tabs = 0 for character in string: if(character == "\t"): tabs += 1 else: return tabs return tabs