extends Node class_name AvatarDollDataManager static var instance: AvatarDollDataManager var avatar_part_scope = {"Clothes": 0, "Bodies": 1, "Heads": 2, "Hairs": 3, "Faces": 4} var default_texture: Texture2D = preload("res://Resources/UI/missing_image.png") var blank_texture: Texture2D = preload("res://Resources/__blank__.png") var initialed = false var work_json_file = "" var avatar_part_state = {} var avatar_part_state_name_to_index = {} var avatar_part_state_index_to_name = {} var avatar_doll_data = null var avatar_asset_id_max = -1 var avatar_asset = {} var avatar_asset_name = {} var char_to_index: Dictionary = {} var index_to_char: Dictionary = {} func _init(): instance = self var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" for i in range(chars.length()): var c = chars[i] char_to_index[c] = i index_to_char[i] = str(c) func load_json(json_path) -> bool: var file = FileAccess.open(json_path, FileAccess.READ) var json_str = file.get_as_text() var json = JSON.new() var error = json.parse(json_str) if error != OK: push_error("load_avatar_doll_json ERROR!") initialed = false return false avatar_doll_data = _fix_avatar_dool_data(json.get_data()) work_json_file = json_path # _load_setting reload_setting() # AssetMap reload_all_asset() initialed = true return true func save_json(json_path=""): if json_path == "": json_path = work_json_file if json_path == "" or initialed == false: return var data = _fix_avatar_dool_data(avatar_doll_data) var json_str = JSON.stringify(data) var file = FileAccess.open(json_path, FileAccess.WRITE) file.store_string(json_str) file.close() func reload_setting(): avatar_part_state.clear() avatar_part_state_name_to_index.clear() avatar_part_state_index_to_name.clear() for part in avatar_part_scope: avatar_part_state[part] = [] var scope_flag = avatar_part_scope[part] for state in avatar_doll_data["Settings"]["States"]: if 1 == 1 & (int(state["Scope"]) >> scope_flag): avatar_part_state[part].append(state) var result = {} _full_permutation_state(result, avatar_part_state[part]) avatar_part_state_name_to_index[part] = result var swapped_dict = {} for key in result: var value = result[key] swapped_dict[value] = key avatar_part_state_index_to_name[part] = swapped_dict func _fix_avatar_dool_data(data): # fix data data["FormatVersion"] = int(data["FormatVersion"]) data["Settings"]["MaxBodySlotCost"] = int(data["Settings"]["MaxBodySlotCost"]) for i in range(data["Settings"]["States"].size()): data["Settings"]["States"][i]["Id"] = int(data["Settings"]["States"][i]["Id"]) data["Settings"]["States"][i]["Scope"] = int(data["Settings"]["States"][i]["Scope"]) var new_map = {} for key in data["AssetMap"]: if key is String and not key.is_valid_int(): continue elif key is int and key <= 0: continue var asset_id = int(key) if asset_id > 0: new_map[asset_id] = [ data["AssetMap"][key][0].simplify_path(), int(data["AssetMap"][key][1]), int(data["AssetMap"][key][2]) ] data["AssetMap"] = new_map for p in avatar_part_scope.keys(): if p not in data: continue for index in range(data[p].size()): #data[p][index]["Guid"] = data[p][index]["Guid"] data[p][index]["IconAssetId"] = int(data[p][index]["IconAssetId"]) for i in range(data[p][index]["Palettes"].size()): #data[p][index]["Palettes"][i]["Guid"] = data[p][index]["Palettes"][i]["Guid"] for j in range(data[p][index]["Palettes"][i]["IconPaletteParam"].size()): data[p][index]["Palettes"][i]["IconPaletteParam"][j]["ElementIndex"] = int(data[p][index]["Palettes"][i]["IconPaletteParam"][j]["ElementIndex"]) data[p][index]["Palettes"][i]["IconPaletteParam"][j]["BlendMode"] = int(data[p][index]["Palettes"][i]["IconPaletteParam"][j]["BlendMode"]) for j in range(data[p][index]["Palettes"][i]["ElementPaletteParam"].size()): data[p][index]["Palettes"][i]["ElementPaletteParam"][j]["ElementIndex"] = int(data[p][index]["Palettes"][i]["ElementPaletteParam"][j]["ElementIndex"]) data[p][index]["Palettes"][i]["ElementPaletteParam"][j]["BlendMode"] = int(data[p][index]["Palettes"][i]["ElementPaletteParam"][j]["BlendMode"]) var elements = {} for key in data[p][index]["Elements"]: if data[p][index]["Elements"][key].size() > 0: elements[key] = data[p][index]["Elements"][key] for i in range(data[p][index]["Elements"][key].size()): data[p][index]["Elements"][key][i]["AssetId"] = int(data[p][index]["Elements"][key][i]["AssetId"]) data[p][index]["Elements"][key][i]["Layer"] = int(data[p][index]["Elements"][key][i]["Layer"]) data[p][index]["Elements"][key][i]["LayerOffset"] = int(data[p][index]["Elements"][key][i]["LayerOffset"]) data[p][index]["Elements"] = elements if p == "Clothes": var ClothClasses = int(data[p][index]["ClothClasses"]["Hash"]) data[p][index]["ClothClasses"] = { "Hash": ClothClasses } var BodySlotCost = {} for slot in data[p][index]["BodySlotCost"]: BodySlotCost[int(slot)] = int(data[p][index]["BodySlotCost"][slot]) data[p][index]["BodySlotCost"] = BodySlotCost return data func _full_permutation_state(result: Dictionary, states: Array, depth = 0, current_list = ["R"], current_code=""): if states.size() == depth: result["/".join(current_list)] = current_code else: var state = states[depth] for i in range(state["Values"].size()): var new_list = current_list + [state["Values"][i]] var new_code = current_code + str(index_to_char.get(i, "#")) if state["Required"][i] == false: new_list[0] = "N" _full_permutation_state(result, states, depth + 1, new_list, new_code) func load_texture(texture_path: String) -> ImageTexture: var image = Image.new() var err = image.load(texture_path) if err != OK: #push_error("load image fail: %s" % texture_path) return null var image_texture = ImageTexture.new() image_texture.set_image(image) return image_texture func load_asset(texture_path: String): texture_path = texture_path.simplify_path() for asset_id in avatar_doll_data["AssetMap"]: if avatar_doll_data["AssetMap"][asset_id][0] == texture_path: return asset_id var texture = load_texture(texture_path) if texture != null: var asset_id = avatar_asset_id_max + 1 avatar_asset[asset_id] = texture avatar_asset_name[asset_id] = texture_path.get_file() var asset = [texture_path, int(texture.get_size().x), int(texture.get_size().y)] avatar_doll_data["AssetMap"][asset_id] = asset avatar_asset_id_max += 1 return asset_id return null func replace_asset(asset_id, texture_path): if asset_id not in avatar_doll_data["AssetMap"]: return false texture_path = texture_path.simplify_path() var texture = load_texture(texture_path) if texture == null: return false avatar_asset[asset_id] = texture avatar_asset_name[asset_id] = texture_path.get_file() var asset = [texture_path, int(texture.get_size().x), int(texture.get_size().y)] avatar_doll_data["AssetMap"][asset_id] = asset avatar_asset_id_max += 1 return true func remove_asset(asset_id): if asset_id not in avatar_doll_data["AssetMap"]: return for scope in avatar_part_scope: if scope not in avatar_doll_data: continue for part_index in range(avatar_doll_data[scope].size()): var part = avatar_doll_data[scope][part_index] # icon if part["IconAssetId"] == asset_id: part["IconAssetId"] = 0 # element for state_code in part["Elements"]: var new_elements = [] for element_index in range(part["Elements"][state_code].size()): if part["Elements"][state_code][element_index]["AssetId"] != asset_id: new_elements.append(part["Elements"][state_code][element_index]) part["Elements"][state_code] = new_elements avatar_doll_data["AssetMap"].erase(asset_id) avatar_asset.erase(asset_id) avatar_asset_name.erase(asset_id) func reload_all_asset(): avatar_asset_id_max = 0 avatar_asset.clear() for asset_id in avatar_doll_data["AssetMap"]: avatar_asset_id_max = max(asset_id, avatar_asset_id_max) var asset = avatar_doll_data["AssetMap"][asset_id] var asset_path = asset[0] var asset_name = asset_path.get_file().get_basename() var texture = load_texture(asset_path) if texture != null: avatar_doll_data["AssetMap"][asset_id] = [asset_path, int(texture.get_size().x), int(texture.get_size().y)] avatar_asset[asset_id] = texture avatar_asset_name[asset_id] = asset_name else: var image: Image = default_texture.get_image() image.resize(asset[1], asset[2], Image.INTERPOLATE_LANCZOS) avatar_asset[asset_id] = ImageTexture.create_from_image(image) avatar_asset_name[asset_id] = asset_name func get_asset(asset_id): if asset_id == 0: return blank_texture return avatar_asset[asset_id] func get_asset_name(asset_id): asset_id = int(asset_id) if asset_id == 0: return "__blank__" elif asset_id in avatar_asset_name: return avatar_asset_name[asset_id] else: return "" func get_asset_id(asset_name): if asset_name == "__blank__": return 0 for asset_id in avatar_asset_name: if asset_name == avatar_asset_name[asset_id]: return asset_id return -1 func get_asset_using(asset_id): if not initialed: return [] var result = [] for scope in avatar_part_scope: if scope not in avatar_doll_data: continue for part_index in range(avatar_doll_data[scope].size()): var part = avatar_doll_data[scope][part_index] # icon if part["IconAssetId"] == asset_id: result.append(["icon", scope, part_index]) # element for state_code in part["Elements"]: for element_index in range(part["Elements"][state_code].size()): if part["Elements"][state_code][element_index]["AssetId"] == asset_id: result.append(["elem", scope, part_index, state_code, element_index]) return result func get_avatar_part_scope_flag(avatar_part): return avatar_part_scope.get(avatar_part, -1) func get_states_hash_code(setting) -> int: var final_hash := 0 for state in setting["States"]: var list_hash := 0 for value in state["Required"]: var value_hash := 1 if value else 0 list_hash = _combine_hashes(list_hash, value_hash) final_hash = _combine_hashes(final_hash, list_hash) return final_hash func _combine_hashes(h1: int, h2: int) -> int: var _hash: int = 2166136261 _hash = (_hash ^ (h1 & 0xFF)) * 16777619 _hash = (_hash ^ ((h1 >> 8) & 0xFF)) * 16777619 _hash = (_hash ^ ((h1 >> 16) & 0xFF)) * 16777619 _hash = (_hash ^ ((h1 >> 24) & 0xFF)) * 16777619 _hash = (_hash ^ (h2 & 0xFF)) * 16777619 _hash = (_hash ^ ((h2 >> 8) & 0xFF)) * 16777619 _hash = (_hash ^ ((h2 >> 16) & 0xFF)) * 16777619 _hash = (_hash ^ ((h2 >> 24) & 0xFF)) * 16777619 return _hash & 0x7FFFFFFF func get_state_index(c: String) -> int: return char_to_index.get(c, -1) func get_state_char(index: int) -> String: return index_to_char.get(index, "#") func get_state_names(avatar_type, state_code): var state_names = [] var state = avatar_part_state[avatar_type] for i in range(state_code.length()): var index = get_state_index(state_code[i]) state_names.append(state[i]["Values"][index]) return state_names func get_render_palettes(part: String, avatar_index: int, state_code: String, palette_id: int, is_icon: bool = false): var palettes = {} var target = avatar_doll_data[part][avatar_index] var elements = target["Elements"][state_code] var palette_setting if palette_id < 0: palette_setting = [] elif is_icon: palette_setting = target["Palettes"][palette_id]["IconPaletteParam"] else: palette_setting = target["Palettes"][palette_id]["ElementPaletteParam"] for i in range(elements.size()): palettes[i] = [] for setting in palette_setting: var element_index = setting["ElementIndex"] if element_index == -1: for i in palettes: palettes[i].append(setting) else: palettes[element_index].append(setting) return palettes