extends PanelContainer @onready var avatar_type_combo: OptionButton = $VBoxContainer/MarginContainer/ToolBar/AvatarTypeCombo @onready var avatar_part_combo: OptionButton = $VBoxContainer/MarginContainer/ToolBar/AvatarPartCombo @onready var palette_combo: OptionButton = $VBoxContainer/MarginContainer/ToolBar/PaletteCombo @onready var state_panel: VBoxContainer = $VBoxContainer/HSplitContainer/StatePanel @onready var canvas_2d: Node2D = $VBoxContainer/HSplitContainer/HSplitContainer/VSplitContainer/PanelContainer/SubViewportContainer/SubViewport/Canvas2d @onready var sub_viewport_container: SubViewportContainer = $VBoxContainer/HSplitContainer/HSplitContainer/VSplitContainer/PanelContainer/SubViewportContainer @onready var asset_panel: PanelContainer = $VBoxContainer/HSplitContainer/HSplitContainer/VSplitContainer/PanelContainer2/AssetPanel @onready var panel_container: PanelContainer = $VBoxContainer/HSplitContainer/HSplitContainer/VSplitContainer/PanelContainer @onready var asset_line_edit: LineEdit = $VBoxContainer/HSplitContainer/HSplitContainer/VBoxContainer2/PanelContainer2/MarginContainer/VBoxContainer/GridContainer/AssetLineEdit @onready var layer_line_edit: LineEdit = $VBoxContainer/HSplitContainer/HSplitContainer/VBoxContainer2/PanelContainer2/MarginContainer/VBoxContainer/GridContainer/HBoxContainer/LayerLineEdit @onready var layer_button: Button = $VBoxContainer/HSplitContainer/HSplitContainer/VBoxContainer2/PanelContainer2/MarginContainer/VBoxContainer/GridContainer/HBoxContainer/LayerButton @onready var layer_offset_spin_box: SpinBox = $VBoxContainer/HSplitContainer/HSplitContainer/VBoxContainer2/PanelContainer2/MarginContainer/VBoxContainer/GridContainer/LayerOffsetSpinBox @onready var position_x_spin_box: SpinBox = $VBoxContainer/HSplitContainer/HSplitContainer/VBoxContainer2/PanelContainer2/MarginContainer/VBoxContainer/GridContainer/HBoxContainer2/PositionXSpinBox @onready var position_y_spin_box: SpinBox = $VBoxContainer/HSplitContainer/HSplitContainer/VBoxContainer2/PanelContainer2/MarginContainer/VBoxContainer/GridContainer/HBoxContainer2/PositionYSpinBox @onready var rotation_spin_box: SpinBox = $VBoxContainer/HSplitContainer/HSplitContainer/VBoxContainer2/PanelContainer2/MarginContainer/VBoxContainer/GridContainer/RotationSpinBox @onready var scale_x_spin_box: SpinBox = $VBoxContainer/HSplitContainer/HSplitContainer/VBoxContainer2/PanelContainer2/MarginContainer/VBoxContainer/GridContainer/HBoxContainer4/ScaleXSpinBox @onready var scale_y_spin_box: SpinBox = $VBoxContainer/HSplitContainer/HSplitContainer/VBoxContainer2/PanelContainer2/MarginContainer/VBoxContainer/GridContainer/HBoxContainer4/ScaleYSpinBox @onready var popup_panel: PopupPanel = $VBoxContainer/HSplitContainer/HSplitContainer/VBoxContainer2/PanelContainer2/MarginContainer/VBoxContainer/GridContainer/HBoxContainer/LayerButton/PopupPanel @onready var layer_selecter: Panel = $VBoxContainer/HSplitContainer/HSplitContainer/VBoxContainer2/PanelContainer2/MarginContainer/VBoxContainer/GridContainer/HBoxContainer/LayerButton/PopupPanel/LayerSelecter @onready var palette_property_rich_text_label: RichTextLabel = $VBoxContainer/HSplitContainer/HSplitContainer/VBoxContainer2/PanelContainer/MarginContainer/VBoxContainer/PalettePropertyRichTextLabel @onready var guid_line_edit: LineEdit = $VBoxContainer/HSplitContainer/HSplitContainer/VBoxContainer2/PanelContainer/MarginContainer/VBoxContainer/GridContainer/GuidLineEdit @onready var name_line_edit: LineEdit = $VBoxContainer/HSplitContainer/HSplitContainer/VBoxContainer2/PanelContainer/MarginContainer/VBoxContainer/GridContainer/NameLineEdit @onready var intro_text_edit: TextEdit = $VBoxContainer/HSplitContainer/HSplitContainer/VBoxContainer2/PanelContainer/MarginContainer/VBoxContainer/GridContainer/IntroTextEdit var avatar_type: String = "Clothes" var _current_part = null var _current_part_index = null var _current_state_code = null var _current_palette_index = -1 var _current_elements = null var _current_element_index = -1 var _current_element = null func _ready() -> void: state_panel.avatar_type = avatar_type _init_gui() func _init_gui(): get_tree().get_root().files_dropped.connect(_on_files_dropped) visibility_changed.connect(_on_visibility_changed) avatar_type_combo.item_selected.connect(_on_avatar_type_combo_item_selected) sub_viewport_container.mouse_entered.connect(_on_canvas_mouse_entered) sub_viewport_container.mouse_exited.connect(_on_canvas_mouse_exited) canvas_2d.element_property_changed.connect(_on_canvas_element_property_changed) canvas_2d.element_selected.connect(_on_canvas_element_selected) canvas_2d.element_action.connect(_on_canvas_element_action, ConnectFlags.CONNECT_DEFERRED) canvas_2d.asset_drop.connect(_on_asset_dropped) asset_panel.asset_updated.connect(_on_asset_updated) avatar_part_combo.item_selected.connect(_on_avatar_part_combo_item_selected) state_panel.on_select_change.connect(_on_state_treeview_select_change) palette_combo.item_selected.connect(_on_palette_combo_item_selected) position_x_spin_box.value_changed.connect(_on_property_changed.bind("PositionX")) position_y_spin_box.value_changed.connect(_on_property_changed.bind("PositionY")) rotation_spin_box.value_changed.connect(_on_property_changed.bind("Rotation")) scale_x_spin_box.value_changed.connect(_on_property_changed.bind("ScaleX")) scale_y_spin_box.value_changed.connect(_on_property_changed.bind("ScaleY")) layer_button.pressed.connect(_on_layer_button_pressed) layer_selecter.pressed.connect(_on_layer_selecter_pressed) name_line_edit.text_changed.connect(_on_name_line_edit_text_submitted) intro_text_edit.text_changed.connect(_on_intro_text_edit_text_submitted) func _on_visibility_changed() -> void: if is_visible_in_tree() == true: set_select(avatar_type, _current_part_index, _current_state_code, _current_element_index) canvas_2d.sync_background() else: canvas_2d.cancle_free_transform() func load_data(): if AvatarDollDataMgr.initialed == false or avatar_type not in AvatarDollDataMgr.avatar_doll_data: avatar_part_combo.clear() return avatar_part_combo.clear() var parts = AvatarDollDataMgr.avatar_doll_data[avatar_type] for part in parts: avatar_part_combo.add_item(part["Name"]) if parts.size() > 0: avatar_part_combo.selected = 0 _current_part = AvatarDollDataMgr.avatar_doll_data[avatar_type][0] palette_combo.clear() palette_combo.add_item("Default") for i in range(_current_part["Palettes"].size()): var palette = _current_part["Palettes"][i] palette_combo.add_item(palette["Name"]) else: _current_part = null state_panel.set_avatar_type(0, true) update_data() pass func update_data(): if _current_part != null: _current_part_index = avatar_part_combo.selected _current_state_code = state_panel.current_state_code _current_part = AvatarDollDataMgr.avatar_doll_data[avatar_type][_current_part_index] _current_element = null _current_element_index = -1 state_panel.set_avatar_type(_current_part_index, false) if _current_state_code in _current_part["Elements"]: _current_elements = _current_part["Elements"][_current_state_code] canvas_2d.set_editable(true) canvas_2d.load_elements(_current_elements) elif _current_state_code != "": _current_elements = [] _current_part["Elements"][_current_state_code] = _current_elements canvas_2d.set_editable(true) canvas_2d.load_elements(_current_elements) else: _current_elements = null canvas_2d.set_editable(false) canvas_2d.load_elements([]) canvas_2d.zoom_fit() # palette palette_combo.clear() palette_combo.add_item("Default") for i in range(_current_part["Palettes"].size()): var palette = _current_part["Palettes"][i] palette_combo.add_item(palette["Name"]) asset_panel.update_data() else: canvas_2d.load_elements([]) canvas_2d.set_editable(false) update_property() update_asset_property() pass func update_property(): if AvatarDollDataMgr.initialed == false or _current_part == null: name_line_edit.editable = false intro_text_edit.editable = false guid_line_edit.text = "" name_line_edit.text = "" intro_text_edit.text = "" palette_property_rich_text_label.text = "[font_size=20][b]Property[/b][/font_size]" return name_line_edit.editable = true intro_text_edit.editable = true if _current_palette_index < 0: palette_property_rich_text_label.text = "[font_size=20][b]Property [Default][/b][/font_size]" guid_line_edit.text = str(_current_palette_index) name_line_edit.text = _current_part["Name"] intro_text_edit.text = _current_part["Introduction"] else: palette_property_rich_text_label.text = "[font_size=20][b]Property [Palette %d][/b][/font_size]" % _current_palette_index guid_line_edit.text = str(_current_palette_index) name_line_edit.text = _current_part["Palettes"][_current_palette_index]["Name"] intro_text_edit.text = _current_part["Palettes"][_current_palette_index]["Introduction"] func update_asset_property(): if _current_element != null: asset_line_edit.text = AvatarDollDataMgr.get_asset_name(_current_element["AssetId"]) layer_line_edit.text = str(_current_element["Layer"]) layer_offset_spin_box.set_value_no_signal(_current_element["LayerOffset"]) position_x_spin_box.set_value_no_signal(_current_element["PositionX"]) position_y_spin_box.set_value_no_signal(_current_element["PositionY"]) rotation_spin_box.set_value_no_signal(_current_element["Rotation"]) scale_x_spin_box.set_value_no_signal(_current_element["ScaleX"]) scale_y_spin_box.set_value_no_signal(_current_element["ScaleY"]) layer_button.disabled = false layer_offset_spin_box.editable = true position_x_spin_box.editable = true position_y_spin_box.editable = true rotation_spin_box.editable = true scale_x_spin_box.editable = true scale_y_spin_box.editable = true else: asset_line_edit.text = "-1" layer_line_edit.text = "0" layer_offset_spin_box.set_value_no_signal(0) position_x_spin_box.set_value_no_signal(0) position_y_spin_box.set_value_no_signal(0) rotation_spin_box.set_value_no_signal(0) scale_x_spin_box.set_value_no_signal(0) scale_y_spin_box.set_value_no_signal(0) layer_button.disabled = true layer_offset_spin_box.editable = false position_x_spin_box.editable = false position_y_spin_box.editable = false rotation_spin_box.editable = false scale_x_spin_box.editable = false scale_y_spin_box.editable = false func set_select(new_type, part_index, state_code, element_index): if AvatarDollDataMgr.initialed == false or new_type not in AvatarDollDataMgr.avatar_doll_data: return avatar_type_combo.set_block_signals(true) avatar_type_combo.selected = AvatarDollDataMgr.avatar_part_scope.keys().find(new_type) avatar_type_combo.set_block_signals(false) state_panel.avatar_type = new_type avatar_type = new_type load_data() if part_index < AvatarDollDataMgr.avatar_doll_data[avatar_type].size(): _current_part_index = part_index avatar_part_combo.selected = part_index _current_part = AvatarDollDataMgr.avatar_doll_data[avatar_type][part_index] if state_code in _current_part["Elements"]: state_panel.set_state_code(state_code) _current_state_code = state_code if element_index >= 0 and element_index < _current_part["Elements"][state_code].size(): _current_elements = _current_part["Elements"][state_code] _current_element_index = element_index _current_element = _current_elements[element_index] canvas_2d.load_elements(_current_elements, element_index) #update_asset_property() func set_palette(index): _current_palette_index = index update_property() var state_code = state_panel.current_state_code if state_code not in _current_part["Elements"]: _current_state_code = null return var palettes = AvatarDollDataMgr.get_render_palettes(avatar_type, avatar_part_combo.selected, state_code, index, false) for asset_name in palettes: canvas_2d.update_palette(asset_name, palettes[asset_name]) func _on_avatar_type_combo_item_selected(index): avatar_type = avatar_type_combo.get_item_text(index) state_panel.avatar_type = avatar_type load_data() func _on_avatar_part_combo_item_selected(index): _current_part = AvatarDollDataMgr.avatar_doll_data[avatar_type][index] _current_palette_index = -1 update_data() canvas_2d.zoom_fit() func _on_palette_combo_item_selected(index): set_palette(index - 1) func _on_state_treeview_select_change(_avatar_type, _index, _state_code): update_data() set_palette(_current_palette_index) canvas_2d.zoom_fit() func _on_canvas_mouse_entered(): canvas_2d.update_mouse_in_canvas(true) func _on_canvas_mouse_exited(): canvas_2d.update_mouse_in_canvas(false) func _on_canvas_element_selected(index: int): _current_element_index = index _current_element = _current_elements[index] if index != -1 else null update_asset_property() func _on_canvas_element_property_changed(_index: int, _position: Vector2, _rotation: float, _scale: Vector2, _skew: float): if _current_part == null or _current_state_code == null: return _current_element["PositionX"] = _position.x _current_element["PositionY"] = _position.y _current_element["Rotation"] = _rotation _current_element["ScaleX"] = _scale.x _current_element["ScaleY"] = _scale.y _current_element["Skew"] = _skew update_asset_property() func _on_canvas_element_action(index: int, action: String, data: Dictionary): if action == "UP" and index > 0: var temp = _current_elements[index - 1] _current_elements[index - 1] = _current_elements[index] _current_elements[index] = temp canvas_2d.load_elements(_current_elements, index - 1) elif action == "DOWN" and index < _current_elements.size() - 1: var temp = _current_elements[index + 1] _current_elements[index + 1] = _current_elements[index] _current_elements[index] = temp canvas_2d.load_elements(_current_elements, index + 1) elif action == "REMOVE": var msg = "确定删除 %s 吗?" % AvatarDollDataMgr.get_asset_name(_current_elements[index]["AssetId"]) var msg_box = MessageBox.warning(self, "警告", msg, MessageBox.StandardButton.YES | MessageBox.StandardButton.NO) msg_box.accepted.connect(func(): _current_elements.remove_at(index) canvas_2d.load_elements(_current_elements) state_panel.update_data() ) pass func _on_property_changed(value, property: String): _current_element[property] = value update_asset_property() canvas_2d.update_element(_current_element_index, _current_element) func _on_name_line_edit_text_submitted(new_text): if not AvatarDollDataMgr.initialed or _current_part == null: return var new_name = name_line_edit.text if _current_palette_index < 0: _current_part["Name"] = new_name avatar_part_combo.set_item_text(_current_part_index, new_name) else: _current_part["Palettes"][_current_palette_index]["Name"] = new_name palette_combo.set_item_text(_current_palette_index, new_name) func _on_intro_text_edit_text_submitted(): if not AvatarDollDataMgr.initialed or _current_part == null: return if _current_palette_index < 0: _current_part["Introduction"] = intro_text_edit.text else: _current_part["Palettes"][_current_palette_index]["Introduction"] = intro_text_edit.text func _on_layer_button_pressed(): var pos = layer_button.global_position + layer_button.get_rect().size pos.x -= popup_panel.size.x popup_panel.position = pos popup_panel.show() func _on_layer_selecter_pressed(index): popup_panel.hide() _current_element["Layer"] = index update_asset_property() func _on_files_dropped(files): if not AvatarDollDataMgr.initialed: return if mouse_over_control(sub_viewport_container): if _current_state_code: for file_path in files: var asset_id = AvatarDollDataMgr.load_asset(file_path) if _current_elements != null: var element = {} element["AssetId"] = asset_id element["LayerOffset"] = 0 element["PositionX"] = 0.0 element["PositionY"] = 0.0 element["Rotation"] = 0.0 element["ScaleX"] = 1.0 element["ScaleY"] = 1.0 element["Skew"] = 0.0 match avatar_type: "Bodies": element["Layer"] = 0 "Heads": element["Layer"] = 9 "Hairs": element["Layer"] = 10 "Faces": element["Layer"] = 14 "Clothes": element["Layer"] = 7 _current_elements.append(element) update_data() canvas_2d.set_selected(_current_elements.size() - 1) else: for file_path in files: var asset_id = AvatarDollDataMgr.load_asset(file_path) update_data() elif mouse_over_control(asset_panel): for file_path in files: var asset_id = AvatarDollDataMgr.load_asset(file_path) update_data() func _on_asset_dropped(asset_name, pos): if _current_elements == null: return var element = {} element["AssetId"] = AvatarDollDataMgr.get_asset_id(asset_name) element["LayerOffset"] = 0 element["PositionX"] = pos.x element["PositionY"] = pos.y element["Rotation"] = 0.0 element["ScaleX"] = 1.0 element["ScaleY"] = 1.0 element["Skew"] = 0.0 match avatar_type: "Bodies": element["Layer"] = 0 "Heads": element["Layer"] = 9 "Hairs": element["Layer"] = 10 "Faces": element["Layer"] = 14 "Clothes": element["Layer"] = 7 _current_elements.append(element) update_data() canvas_2d.set_selected(_current_elements.size() - 1) func _on_asset_updated(): update_data() func mouse_over_control(node: Control): var rect = node.get_global_transform() * node.get_rect() var mouse = get_global_mouse_position() return rect.has_point(mouse)