diff --git a/addons/weapon_creator/BulletCreatorDialog.gd b/addons/weapon_creator/BulletCreatorDialog.gd new file mode 100644 index 00000000..bf36f6c9 --- /dev/null +++ b/addons/weapon_creator/BulletCreatorDialog.gd @@ -0,0 +1,394 @@ +@tool +extends Window + +# Popup window for configuring bullet parameters before creation + +signal bullet_data_confirmed(bullet_data: Dictionary) + +# UI fields +var bullet_name_field: LineEdit +var bullet_sprite_picker: EditorResourcePicker +var bullet_sprite_preview: TextureRect +var bullet_scene_picker: EditorResourcePicker +var bullet_size_field: SpinBox +var bullet_speed_field: SpinBox +var bullet_damage_field: SpinBox +var max_damage_field: SpinBox +var knockback_field: SpinBox +var life_time_field: SpinBox +var graze_value_field: SpinBox +var destruction_particles_picker: EditorResourcePicker + +# Buttons +var create_button: Button +var cancel_button: Button + +# Mode and prefill data +var is_3d_mode: bool = true +var prefill_data: Dictionary = {} +var _ui_built: bool = false +var editor_interface: EditorInterface + +func setup(editor_iface: EditorInterface, is_3d: bool = true, prefill: Dictionary = {}) -> void: + editor_interface = editor_iface + is_3d_mode = is_3d + prefill_data = prefill + + var mode_text = "3D" if is_3d_mode else "2D" + var action_text = "Duplicate" if not prefill_data.is_empty() else "Create New" + title = action_text + " Bullet (" + mode_text + ")" + + if _ui_built: + if prefill_data.is_empty(): + _set_default_values() + else: + _apply_prefill_data() + +func _ready() -> void: + var mode_text = "3D" if is_3d_mode else "2D" + var action_text = "Duplicate" if not prefill_data.is_empty() else "Create New" + title = action_text + " Bullet (" + mode_text + ")" + size = Vector2i(750, 750) + transient = false + exclusive = false + unresizable = false + + close_requested.connect(_on_cancel_pressed) + position = (DisplayServer.screen_get_size() - size) / 2 + + _build_ui() + _ui_built = true + + if prefill_data.is_empty(): + _set_default_values() + else: + _apply_prefill_data() + +func _build_ui() -> void: + var margin = MarginContainer.new() + margin.set_anchors_preset(Control.PRESET_FULL_RECT) + margin.add_theme_constant_override("margin_left", 12) + margin.add_theme_constant_override("margin_top", 12) + margin.add_theme_constant_override("margin_right", 12) + margin.add_theme_constant_override("margin_bottom", 12) + add_child(margin) + + var vbox = VBoxContainer.new() + vbox.add_theme_constant_override("separation", 8) + margin.add_child(vbox) + + var scroll = ScrollContainer.new() + scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL + scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED + vbox.add_child(scroll) + + var main_vbox = VBoxContainer.new() + main_vbox.size_flags_horizontal = Control.SIZE_EXPAND_FILL + main_vbox.add_theme_constant_override("separation", 12) + scroll.add_child(main_vbox) + + _build_basic_section(main_vbox) + _build_stats_section(main_vbox) + + vbox.add_child(HSeparator.new()) + + var button_hbox = HBoxContainer.new() + button_hbox.alignment = BoxContainer.ALIGNMENT_CENTER + button_hbox.add_theme_constant_override("separation", 8) + vbox.add_child(button_hbox) + + cancel_button = Button.new() + cancel_button.text = "Cancel" + cancel_button.custom_minimum_size = Vector2(100, 0) + cancel_button.pressed.connect(_on_cancel_pressed) + button_hbox.add_child(cancel_button) + + create_button = Button.new() + create_button.text = "Create Bullet" + create_button.custom_minimum_size = Vector2(150, 0) + create_button.pressed.connect(_on_create_pressed) + button_hbox.add_child(create_button) + +func _build_basic_section(parent: Control) -> void: + var section = VBoxContainer.new() + section.add_theme_constant_override("separation", 4) + parent.add_child(section) + + var header = Label.new() + header.text = "Basic Information" + header.add_theme_font_size_override("font_size", 16) + section.add_child(header) + + bullet_name_field = _add_line_edit_field(section, "Bullet Name:", "e.g., ice_bullet") + + section.add_child(HSeparator.new()) + + # Sprite Selection + var sprite_header = Label.new() + sprite_header.text = "Bullet Sprite" + sprite_header.add_theme_font_size_override("font_size", 14) + section.add_child(sprite_header) + + var sprite_container = HBoxContainer.new() + sprite_container.add_theme_constant_override("separation", 8) + section.add_child(sprite_container) + + var sprite_vbox = VBoxContainer.new() + sprite_vbox.size_flags_horizontal = Control.SIZE_EXPAND_FILL + sprite_vbox.add_theme_constant_override("separation", 4) + sprite_container.add_child(sprite_vbox) + + var sprite_picker_hbox = HBoxContainer.new() + sprite_picker_hbox.add_theme_constant_override("separation", 4) + sprite_vbox.add_child(sprite_picker_hbox) + + var sprite_label = Label.new() + sprite_label.text = "Sprite:" + sprite_label.custom_minimum_size = Vector2(120, 0) + sprite_picker_hbox.add_child(sprite_label) + + bullet_sprite_picker = EditorResourcePicker.new() + bullet_sprite_picker.base_type = "Texture2D" + bullet_sprite_picker.editable = true + bullet_sprite_picker.size_flags_horizontal = Control.SIZE_EXPAND_FILL + bullet_sprite_picker.resource_changed.connect(_on_sprite_changed) + bullet_sprite_picker.resource_selected.connect(_on_resource_picker_opening) + sprite_picker_hbox.add_child(bullet_sprite_picker) + + var preview_container = PanelContainer.new() + preview_container.custom_minimum_size = Vector2(64, 64) + sprite_container.add_child(preview_container) + + bullet_sprite_preview = TextureRect.new() + bullet_sprite_preview.custom_minimum_size = Vector2(64, 64) + bullet_sprite_preview.expand_mode = TextureRect.EXPAND_FIT_WIDTH_PROPORTIONAL + bullet_sprite_preview.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED + preview_container.add_child(bullet_sprite_preview) + + section.add_child(HSeparator.new()) + + # Bullet Scene + var scene_header = Label.new() + scene_header.text = "Bullet Scene" + scene_header.add_theme_font_size_override("font_size", 14) + section.add_child(scene_header) + + var scene_hbox = HBoxContainer.new() + scene_hbox.add_theme_constant_override("separation", 4) + section.add_child(scene_hbox) + + var scene_label = Label.new() + scene_label.text = "Scene:" + scene_label.custom_minimum_size = Vector2(120, 0) + scene_hbox.add_child(scene_label) + + bullet_scene_picker = EditorResourcePicker.new() + bullet_scene_picker.base_type = "PackedScene" + bullet_scene_picker.editable = true + bullet_scene_picker.size_flags_horizontal = Control.SIZE_EXPAND_FILL + bullet_scene_picker.resource_selected.connect(_on_resource_picker_opening) + scene_hbox.add_child(bullet_scene_picker) + + section.add_child(HSeparator.new()) + + # Destruction Particles + var particles_header = Label.new() + particles_header.text = "Destruction Particles (Optional)" + particles_header.add_theme_font_size_override("font_size", 14) + section.add_child(particles_header) + + var particles_hbox = HBoxContainer.new() + particles_hbox.add_theme_constant_override("separation", 4) + section.add_child(particles_hbox) + + var particles_label = Label.new() + particles_label.text = "Particles Scene:" + particles_label.custom_minimum_size = Vector2(120, 0) + particles_hbox.add_child(particles_label) + + destruction_particles_picker = EditorResourcePicker.new() + destruction_particles_picker.base_type = "PackedScene" + destruction_particles_picker.editable = true + destruction_particles_picker.size_flags_horizontal = Control.SIZE_EXPAND_FILL + destruction_particles_picker.resource_selected.connect(_on_resource_picker_opening) + particles_hbox.add_child(destruction_particles_picker) + +func _build_stats_section(parent: Control) -> void: + var section = VBoxContainer.new() + section.add_theme_constant_override("separation", 4) + parent.add_child(section) + + var header = Label.new() + header.text = "Bullet Statistics" + header.add_theme_font_size_override("font_size", 16) + section.add_child(header) + + var grid = GridContainer.new() + grid.columns = 2 + grid.add_theme_constant_override("h_separation", 8) + grid.add_theme_constant_override("v_separation", 4) + section.add_child(grid) + + bullet_size_field = _add_spinbox_field(grid, "Bullet Size:", 0, 100, 0.1, 1.0) + bullet_speed_field = _add_spinbox_field(grid, "Bullet Speed:", 0, 10000, 1, 100) + bullet_damage_field = _add_spinbox_field(grid, "Bullet Damage:", 0, 10000, 0.1, 1.0) + max_damage_field = _add_spinbox_field(grid, "Max Damage:", 0, 10000, 0.1, 1.0) + knockback_field = _add_spinbox_field(grid, "Knockback:", 0, 1000, 0.1, 1.0) + life_time_field = _add_spinbox_field(grid, "Life Time (s):", 0, 100, 0.1, 10.0) + graze_value_field = _add_spinbox_field(grid, "Graze Value:", 0, 100, 0.1, 0.2) + +func _add_line_edit_field(parent: Control, label_text: String, placeholder: String) -> LineEdit: + var hbox = HBoxContainer.new() + parent.add_child(hbox) + + var label = Label.new() + label.text = label_text + label.custom_minimum_size = Vector2(120, 0) + hbox.add_child(label) + + var line_edit = LineEdit.new() + line_edit.placeholder_text = placeholder + line_edit.size_flags_horizontal = Control.SIZE_EXPAND_FILL + hbox.add_child(line_edit) + + return line_edit + +func _add_spinbox_field(parent: Control, label_text: String, min_val: float, max_val: float, step_val: float, default_val: float) -> SpinBox: + var label = Label.new() + label.text = label_text + parent.add_child(label) + + var spinbox = SpinBox.new() + spinbox.min_value = min_val + spinbox.max_value = max_val + spinbox.step = step_val + spinbox.value = default_val + spinbox.size_flags_horizontal = Control.SIZE_EXPAND_FILL + parent.add_child(spinbox) + + return spinbox + +func _set_default_values() -> void: + bullet_name_field.text = "new_bullet" + + # Set default bullet scene based on mode + if is_3d_mode: + var default_scene = load("res://Scenes/Weapons/base_generic_bullet_3D.tscn") + bullet_scene_picker.edited_resource = default_scene + + bullet_size_field.value = 1.0 + bullet_speed_field.value = 100.0 + bullet_damage_field.value = 1.0 + max_damage_field.value = 1.0 + knockback_field.value = 1.0 + life_time_field.value = 10.0 + graze_value_field.value = 0.2 + +func _apply_prefill_data() -> void: + if prefill_data.has("bullet_sprite") and prefill_data["bullet_sprite"] != null: + bullet_sprite_picker.edited_resource = prefill_data["bullet_sprite"] + bullet_sprite_preview.texture = prefill_data["bullet_sprite"] + + if prefill_data.has("bullet_scene") and prefill_data["bullet_scene"] != null: + bullet_scene_picker.edited_resource = prefill_data["bullet_scene"] + + if prefill_data.has("bullet_size"): + bullet_size_field.value = prefill_data["bullet_size"] + if prefill_data.has("bullet_speed"): + bullet_speed_field.value = prefill_data["bullet_speed"] + if prefill_data.has("bullet_damage"): + bullet_damage_field.value = prefill_data["bullet_damage"] + if prefill_data.has("max_damage"): + max_damage_field.value = prefill_data["max_damage"] + if prefill_data.has("knockback"): + knockback_field.value = prefill_data["knockback"] + if prefill_data.has("life_time"): + life_time_field.value = prefill_data["life_time"] + if prefill_data.has("graze_value"): + graze_value_field.value = prefill_data["graze_value"] + if prefill_data.has("destruction_particles_scene") and prefill_data["destruction_particles_scene"] != null: + destruction_particles_picker.edited_resource = prefill_data["destruction_particles_scene"] + +func _on_sprite_changed(resource: Resource) -> void: + if resource is Texture2D: + bullet_sprite_preview.texture = resource + + # Restore window focus after resource picker closes + call_deferred("_restore_window_focus") + +func _on_resource_picker_opening(_resource: Resource, _inspect: bool) -> void: + # Called when resource picker button is clicked - store current state + pass + +func _restore_window_focus() -> void: + # Bring window back to front after file picker closes + if visible: + move_to_foreground() + +func _on_create_pressed() -> void: + if not _validate_inputs(): + return + + var dimension_suffix = "_3D" if is_3d_mode else "" + var bullet_name = bullet_name_field.text.strip_edges() + + # Ensure name ends with dimension suffix if 3D + if is_3d_mode and not bullet_name.ends_with("_3D"): + bullet_name += "_3D" + + var bullet_data = { + "bullet_name": bullet_name, + "is_3d": is_3d_mode, + "bullet_sprite": bullet_sprite_picker.edited_resource, + "bullet_scene": bullet_scene_picker.edited_resource, + "bullet_size": bullet_size_field.value, + "bullet_speed": bullet_speed_field.value, + "bullet_damage": bullet_damage_field.value, + "max_damage": max_damage_field.value, + "knockback": knockback_field.value, + "life_time": life_time_field.value, + "graze_value": graze_value_field.value, + "destruction_particles_scene": destruction_particles_picker.edited_resource + } + + bullet_data_confirmed.emit(bullet_data) + + hide() + queue_free() + +func _on_cancel_pressed() -> void: + hide() + queue_free() + +func _validate_inputs() -> bool: + if bullet_name_field.text.strip_edges().is_empty(): + _show_error("Bullet name cannot be empty") + return false + + var bullet_name = bullet_name_field.text.strip_edges() + if is_3d_mode and not bullet_name.ends_with("_3D"): + bullet_name += "_3D" + + var bullets_dir = "res://Resources/Bullets/" + if is_3d_mode: + bullets_dir += "3D/" + + var bullet_path = bullets_dir + bullet_name + ".tres" + + if ResourceLoader.exists(bullet_path): + _show_error("Bullet resource already exists at:\n" + bullet_path + "\n\nPlease choose a different name.") + return false + + if not bullet_scene_picker.edited_resource: + _show_error("Bullet scene is required") + return false + + return true + +func _show_error(message: String) -> void: + var dialog = AcceptDialog.new() + dialog.dialog_text = message + dialog.title = "Error" + add_child(dialog) + dialog.popup_centered() + diff --git a/addons/weapon_creator/BulletCreatorDialog.gd.uid b/addons/weapon_creator/BulletCreatorDialog.gd.uid new file mode 100644 index 00000000..829afa67 --- /dev/null +++ b/addons/weapon_creator/BulletCreatorDialog.gd.uid @@ -0,0 +1 @@ +uid://dx6paglgfjh1c diff --git a/addons/weapon_creator/BulletViewer.gd b/addons/weapon_creator/BulletViewer.gd new file mode 100644 index 00000000..3e96d7a3 --- /dev/null +++ b/addons/weapon_creator/BulletViewer.gd @@ -0,0 +1,428 @@ +@tool +extends PanelContainer + +# Displays all bullets from the Resources/Bullets folders in a grid format +# Shows sprite and name, with tooltip showing resource path + +signal bullet_selected(bullet_resource_path: String) +signal duplicate_bullet_requested(bullet_data: Dictionary) +signal bullet_deleted(bullet_name: String, bullet_path: String) +signal bullet_duplication_started(bullet_name: String, is_3d: bool) + +var _editor_interface: EditorInterface +var _grid_container: HFlowContainer +var _show_2d_checkbox: CheckBox +var _show_3d_checkbox: CheckBox +var _dock: PanelContainer + +const SETTING_SHOW_2D = "weapon_creator/bullet_filter_show_2d" +const SETTING_SHOW_3D = "weapon_creator/bullet_filter_show_3d" + +func setup(editor_interface: EditorInterface, dock: PanelContainer = null) -> void: + _editor_interface = editor_interface + _dock = dock + if _show_2d_checkbox: + _show_2d_checkbox.button_pressed = _load_filter_setting(SETTING_SHOW_2D, true) + if _show_3d_checkbox: + _show_3d_checkbox.button_pressed = _load_filter_setting(SETTING_SHOW_3D, true) + refresh_bullets() + +func _ready() -> void: + _build_ui() + refresh_bullets() + +func _build_ui() -> void: + var margin = MarginContainer.new() + margin.add_theme_constant_override("margin_left", 8) + margin.add_theme_constant_override("margin_top", 8) + margin.add_theme_constant_override("margin_right", 8) + margin.add_theme_constant_override("margin_bottom", 8) + add_child(margin) + + var vbox = VBoxContainer.new() + vbox.add_theme_constant_override("separation", 8) + margin.add_child(vbox) + + # Header with title, filters, and buttons + var header_hbox = HBoxContainer.new() + vbox.add_child(header_hbox) + + var create_2d_button = Button.new() + create_2d_button.text = "Create (2D)" + create_2d_button.pressed.connect(_on_create_bullet_pressed.bind(false)) + header_hbox.add_child(create_2d_button) + + var create_3d_button = Button.new() + create_3d_button.text = "Create (3D)" + create_3d_button.pressed.connect(_on_create_bullet_pressed.bind(true)) + header_hbox.add_child(create_3d_button) + + # Spacer + var spacer = Control.new() + spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL + header_hbox.add_child(spacer) + + _show_2d_checkbox = CheckBox.new() + _show_2d_checkbox.text = "2D" + _show_2d_checkbox.button_pressed = true + _show_2d_checkbox.toggled.connect(_on_filter_changed) + header_hbox.add_child(_show_2d_checkbox) + + _show_3d_checkbox = CheckBox.new() + _show_3d_checkbox.text = "3D" + _show_3d_checkbox.button_pressed = true + _show_3d_checkbox.toggled.connect(_on_filter_changed) + header_hbox.add_child(_show_3d_checkbox) + + var refresh_button = Button.new() + refresh_button.text = "Refresh" + refresh_button.pressed.connect(refresh_bullets) + header_hbox.add_child(refresh_button) + + vbox.add_child(HSeparator.new()) + + # Scroll container for grid + var scroll = ScrollContainer.new() + scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL + scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED + vbox.add_child(scroll) + + _grid_container = HFlowContainer.new() + _grid_container.size_flags_horizontal = Control.SIZE_EXPAND_FILL + _grid_container.add_theme_constant_override("h_separation", 8) + _grid_container.add_theme_constant_override("v_separation", 8) + scroll.add_child(_grid_container) + +func refresh_bullets() -> void: + if not _grid_container: + return + + # Clear existing items + for child in _grid_container.get_children(): + child.queue_free() + + var show_2d = _show_2d_checkbox == null or _show_2d_checkbox.button_pressed + var show_3d = _show_3d_checkbox == null or _show_3d_checkbox.button_pressed + + var bullet_count = 0 + + # Load 2D bullets + if show_2d: + bullet_count += _load_bullets_from_directory("res://Resources/Bullets/", false) + + # Load 3D bullets + if show_3d: + bullet_count += _load_bullets_from_directory("res://Resources/Bullets/3D/", true) + + if bullet_count == 0: + _add_error_label("No bullets found") + +func _load_bullets_from_directory(dir_path: String, is_3d: bool) -> int: + var dir = DirAccess.open(dir_path) + if dir == null: + return 0 + + var count = 0 + dir.list_dir_begin() + var file_name = dir.get_next() + + while file_name != "": + if not dir.current_is_dir() and file_name.ends_with(".tres"): + var bullet_path = dir_path + file_name + var bullet_resource = load(bullet_path) + + if bullet_resource and bullet_resource.get_script(): + var script_path = bullet_resource.get_script().resource_path + if script_path and "BulletResource.cs" in script_path: + _create_bullet_tile(bullet_resource, is_3d) + count += 1 + + file_name = dir.get_next() + + dir.list_dir_end() + return count + +func _create_bullet_tile(bullet_resource: Resource, is_3d: bool) -> void: + var panel = PanelContainer.new() + panel.custom_minimum_size = Vector2(100, 145) + panel.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN + panel.size_flags_vertical = Control.SIZE_SHRINK_BEGIN + panel.mouse_filter = Control.MOUSE_FILTER_STOP + panel.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND + + var vbox = VBoxContainer.new() + vbox.add_theme_constant_override("separation", 4) + vbox.mouse_filter = Control.MOUSE_FILTER_IGNORE + + var sprite: Texture2D = bullet_resource.get("BulletSprite") + + # Container for sprite with badge overlay + var sprite_container = Control.new() + sprite_container.custom_minimum_size = Vector2(64, 64) + sprite_container.mouse_filter = Control.MOUSE_FILTER_IGNORE + vbox.add_child(sprite_container) + + var texture_rect = TextureRect.new() + texture_rect.custom_minimum_size = Vector2(64, 64) + texture_rect.expand_mode = TextureRect.EXPAND_FIT_WIDTH_PROPORTIONAL + texture_rect.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED + texture_rect.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST + texture_rect.mouse_filter = Control.MOUSE_FILTER_IGNORE + texture_rect.set_anchors_preset(Control.PRESET_FULL_RECT) + sprite_container.add_child(texture_rect) + + if sprite: + texture_rect.texture = sprite + else: + var placeholder_label = Label.new() + placeholder_label.text = "No Icon" + placeholder_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + placeholder_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + placeholder_label.mouse_filter = Control.MOUSE_FILTER_IGNORE + placeholder_label.add_theme_font_size_override("font_size", 10) + placeholder_label.set_anchors_preset(Control.PRESET_FULL_RECT) + sprite_container.add_child(placeholder_label) + + # Add 2D/3D badge + var badge = Label.new() + badge.text = "3D" if is_3d else "2D" + badge.add_theme_font_size_override("font_size", 14) + if is_3d: + badge.add_theme_color_override("font_color", Color(1.0, 0.2, 0.2)) + else: + badge.add_theme_color_override("font_color", Color(0.3, 0.5, 1.0)) + badge.add_theme_color_override("font_outline_color", Color(0.15, 0.15, 0.15)) + badge.add_theme_constant_override("outline_size", 3) + badge.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT + badge.vertical_alignment = VERTICAL_ALIGNMENT_BOTTOM + badge.mouse_filter = Control.MOUSE_FILTER_IGNORE + badge.position = Vector2(38, 44) + badge.size = Vector2(24, 20) + sprite_container.add_child(badge) + + var name_label = Label.new() + var bullet_name: String = bullet_resource.resource_path.get_file().get_basename() + name_label.text = bullet_name if bullet_name else "Unknown" + name_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + name_label.vertical_alignment = VERTICAL_ALIGNMENT_TOP + name_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART + name_label.custom_minimum_size = Vector2(92, 48) + name_label.clip_text = false + name_label.max_lines_visible = 3 + name_label.mouse_filter = Control.MOUSE_FILTER_IGNORE + name_label.add_theme_font_size_override("font_size", 14) + name_label.add_theme_constant_override("line_spacing", -2) + vbox.add_child(name_label) + + panel.add_child(vbox) + vbox.set_anchors_preset(Control.PRESET_FULL_RECT) + vbox.add_theme_constant_override("margin_left", 4) + vbox.add_theme_constant_override("margin_top", 4) + vbox.add_theme_constant_override("margin_right", 4) + vbox.add_theme_constant_override("margin_bottom", 4) + + var bullet_path = bullet_resource.resource_path + if bullet_path: + panel.tooltip_text = bullet_path + else: + panel.tooltip_text = "Built-in resource (no file path)" + + panel.gui_input.connect(_on_bullet_gui_input.bind(panel, bullet_resource)) + + _grid_container.add_child(panel) + +func _on_bullet_gui_input(event: InputEvent, panel: PanelContainer, bullet_resource: Resource) -> void: + if event is InputEventMouseButton: + var mouse_event = event as InputEventMouseButton + if mouse_event.pressed: + if mouse_event.button_index == MOUSE_BUTTON_LEFT: + _on_bullet_clicked(bullet_resource) + elif mouse_event.button_index == MOUSE_BUTTON_RIGHT: + _show_context_menu(panel, bullet_resource) + +func _on_bullet_clicked(bullet_resource: Resource) -> void: + if not _editor_interface: + push_error("Editor interface not available") + return + + if bullet_resource: + _editor_interface.edit_resource(bullet_resource) + bullet_selected.emit(bullet_resource.resource_path) + +func _show_context_menu(panel: PanelContainer, bullet_resource: Resource) -> void: + var popup = PopupMenu.new() + popup.add_item("Open Bullet", 0) + popup.add_separator() + + # Determine if this is a 2D or 3D bullet + var is_3d = "3D" in bullet_resource.resource_path + popup.add_item("Duplicate Bullet (2D)", 1) + popup.add_item("Duplicate Bullet (3D)", 2) + popup.add_separator() + popup.add_item("Copy Resource Path", 3) + popup.add_separator() + popup.add_item("Delete", 4) + + popup.id_pressed.connect(_on_context_menu_id_pressed.bind(popup, bullet_resource)) + + get_tree().root.add_child(popup) + + var mouse_pos = DisplayServer.mouse_get_position() + popup.position = mouse_pos + popup.popup() + + popup.popup_hide.connect(func(): popup.queue_free()) + +func _on_context_menu_id_pressed(id: int, popup: PopupMenu, bullet_resource: Resource) -> void: + match id: + 0: + _open_bullet(bullet_resource) + 1: + _duplicate_bullet(bullet_resource, false) + 2: + _duplicate_bullet(bullet_resource, true) + 3: + _copy_bullet_resource_path(bullet_resource) + 4: + _confirm_delete(bullet_resource) + +func _open_bullet(bullet_resource: Resource) -> void: + if not _editor_interface: + push_error("Editor interface not available") + return + + if bullet_resource: + _editor_interface.edit_resource(bullet_resource) + +func _duplicate_bullet(bullet_resource: Resource, is_3d: bool) -> void: + if not _editor_interface: + push_error("Editor interface not available") + return + + var bullet_name = bullet_resource.resource_path.get_file().get_basename() + var dimension = "3D" if is_3d else "2D" + + if _dock: + _dock.call("add_log", "=== Duplicating Bullet (" + dimension + ") ===", Color.CYAN) + _dock.call("add_log", "Source: " + bullet_name, Color.CYAN) + + bullet_duplication_started.emit(bullet_name, is_3d) + + # Extract bullet data + var prefill_data = {} + prefill_data["bullet_sprite"] = bullet_resource.get("BulletSprite") + prefill_data["bullet_size"] = bullet_resource.get("BulletSize") + prefill_data["bullet_speed"] = bullet_resource.get("BulletSpeed") + prefill_data["bullet_damage"] = bullet_resource.get("BulletDamage") + prefill_data["max_damage"] = bullet_resource.get("MaxDamage") + prefill_data["knockback"] = bullet_resource.get("Knockback") + prefill_data["life_time"] = bullet_resource.get("LifeTime") + prefill_data["graze_value"] = bullet_resource.get("GrazeValue") + prefill_data["bullet_scene"] = bullet_resource.get("BulletScene") + prefill_data["destruction_particles_scene"] = bullet_resource.get("DestructionParticlesScene") + + _open_bullet_dialog(is_3d, prefill_data) + +func _on_duplicate_bullet_confirmed(bullet_data: Dictionary) -> void: + duplicate_bullet_requested.emit(bullet_data) + get_tree().create_timer(0.5).timeout.connect(refresh_bullets) + +func _copy_bullet_resource_path(bullet_resource: Resource) -> void: + if bullet_resource and bullet_resource.resource_path: + DisplayServer.clipboard_set(bullet_resource.resource_path) + print("Copied bullet resource path: ", bullet_resource.resource_path) + +func _confirm_delete(bullet_resource: Resource) -> void: + var bullet_name: String = bullet_resource.resource_path.get_file().get_basename() + var bullet_path = bullet_resource.resource_path + + var dialog_text = "Are you REALLY sure you want to delete this bullet?\n\n" + dialog_text += "Bullet: " + bullet_name + "\n\n" + dialog_text += "Path: " + bullet_path + "\n\n" + dialog_text += "This action cannot be undone!" + + var confirm_dialog = ConfirmationDialog.new() + confirm_dialog.dialog_text = dialog_text + confirm_dialog.title = "Confirm Deletion" + confirm_dialog.ok_button_text = "Delete" + + confirm_dialog.confirmed.connect(_perform_delete.bind(bullet_resource, confirm_dialog)) + confirm_dialog.close_requested.connect(func(): confirm_dialog.queue_free()) + confirm_dialog.canceled.connect(func(): confirm_dialog.queue_free()) + + get_tree().root.add_child(confirm_dialog) + confirm_dialog.popup_centered() + +func _perform_delete(bullet_resource: Resource, dialog: ConfirmationDialog) -> void: + var bullet_path = bullet_resource.resource_path + var bullet_name = bullet_resource.resource_path.get_file().get_basename() + + if not bullet_path: + push_error("Cannot delete built-in resources") + dialog.queue_free() + return + + var deleted_success = false + + if DirAccess.remove_absolute(bullet_path) == OK: + print("Deleted bullet resource: ", bullet_path) + deleted_success = true + else: + push_error("Failed to delete bullet resource: ", bullet_path) + + if _editor_interface: + _editor_interface.get_resource_filesystem().scan() + + dialog.queue_free() + + if deleted_success: + bullet_deleted.emit(bullet_name, bullet_path) + + if _dock: + _dock.call("add_log", "=== Bullet Deleted ===", Color.ORANGE) + _dock.call("add_log", "Bullet: " + bullet_name, Color.ORANGE) + _dock.call("add_log", "✓ Deleted BulletResource: " + bullet_path, Color.GREEN) + + refresh_bullets() + +func _on_create_bullet_pressed(is_3d: bool) -> void: + _open_bullet_dialog(is_3d, {}) + +func _open_bullet_dialog(is_3d: bool, prefill_data: Dictionary = {}) -> void: + var dialog_script = load("res://addons/weapon_creator/BulletCreatorDialog.gd") + var dialog = Window.new() + dialog.set_script(dialog_script) + + get_tree().root.add_child(dialog) + + dialog.call_deferred("setup", _editor_interface, is_3d, prefill_data) + dialog.call_deferred("connect", "bullet_data_confirmed", _on_duplicate_bullet_confirmed) + dialog.call_deferred("popup_centered") + +func _add_error_label(error_message: String) -> void: + var label = Label.new() + label.text = error_message + label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + label.modulate = Color.ORANGE_RED + _grid_container.add_child(label) + +func _on_filter_changed(_toggled: bool) -> void: + _save_filter_settings() + refresh_bullets() + +func _load_filter_setting(key: String, default_value: bool) -> bool: + if not _editor_interface: + return default_value + var editor_settings = _editor_interface.get_editor_settings() + if editor_settings and editor_settings.has_setting(key): + return editor_settings.get_setting(key) + return default_value + +func _save_filter_settings() -> void: + if not _editor_interface: + return + var editor_settings = _editor_interface.get_editor_settings() + if editor_settings: + editor_settings.set_setting(SETTING_SHOW_2D, _show_2d_checkbox.button_pressed) + editor_settings.set_setting(SETTING_SHOW_3D, _show_3d_checkbox.button_pressed) + diff --git a/addons/weapon_creator/BulletViewer.gd.uid b/addons/weapon_creator/BulletViewer.gd.uid new file mode 100644 index 00000000..8c3217aa --- /dev/null +++ b/addons/weapon_creator/BulletViewer.gd.uid @@ -0,0 +1 @@ +uid://b56a4ljlod8rm diff --git a/addons/weapon_creator/WeaponCreatorDialog.gd b/addons/weapon_creator/WeaponCreatorDialog.gd index 8118a865..7064cfd9 100644 --- a/addons/weapon_creator/WeaponCreatorDialog.gd +++ b/addons/weapon_creator/WeaponCreatorDialog.gd @@ -72,8 +72,9 @@ func _ready() -> void: var action_text = "Duplicate" if not prefill_data.is_empty() else "Create New" title = action_text + " Weapon (" + mode_text + ")" size = Vector2i(750, 950) - transient = true - exclusive = true + transient = false + exclusive = false + unresizable = false # Connect close request (X button) to cancel action close_requested.connect(_on_cancel_pressed) @@ -243,6 +244,7 @@ func _build_sprite_selector(parent: Control) -> void: sprite_picker.editable = true sprite_picker.size_flags_horizontal = Control.SIZE_EXPAND_FILL sprite_picker.resource_changed.connect(_on_sprite_resource_changed) + sprite_picker.resource_selected.connect(_on_resource_picker_opening) picker_hbox.add_child(sprite_picker) # Setup editor integration if interface is available @@ -261,7 +263,6 @@ func _build_sprite_selector(parent: Control) -> void: preview_container.add_child(sprite_preview) func _on_sprite_resource_changed(resource: Resource) -> void: - # Update sprite resource and preview when changed via EditorResourcePicker if resource is Texture2D: sprite_resource = resource sprite_preview.texture = resource @@ -272,6 +273,18 @@ func _on_sprite_resource_changed(resource: Resource) -> void: print("Sprite resource cleared") else: push_warning("Selected resource is not a Texture2D") + + # Restore window focus after resource picker closes + call_deferred("_restore_window_focus") + +func _on_resource_picker_opening(_resource: Resource, _inspect: bool) -> void: + # Called when resource picker button is clicked + pass + +func _restore_window_focus() -> void: + # Bring window back to front after file picker closes + if visible: + move_to_foreground() func _setup_sprite_picker() -> void: # Ensure sprite picker is properly integrated with editor diff --git a/addons/weapon_creator/WeaponCreatorDock.gd b/addons/weapon_creator/WeaponCreatorDock.gd index b2110500..3b1cc340 100644 --- a/addons/weapon_creator/WeaponCreatorDock.gd +++ b/addons/weapon_creator/WeaponCreatorDock.gd @@ -2,15 +2,17 @@ extends PanelContainer # UI dock for the Weapon Creator plugin -# Provides a button to open the creation dialog and displays creation logs +# Provides tabs for weapons and bullets, with creation dialogs and logs signal create_weapon_requested(weapon_data: Dictionary) +signal create_bullet_requested(bullet_data: Dictionary) # UI elements -var open_dialog_button: Button var clear_button: Button var log_output: RichTextLabel var weapon_viewer: PanelContainer +var bullet_viewer: PanelContainer +var tab_container: TabContainer var _is_creating: bool = false var _editor_interface: EditorInterface @@ -35,40 +37,7 @@ func _build_ui() -> void: vbox.add_theme_constant_override("separation", 8) margin.add_child(vbox) - # Title - var title = Label.new() - title.text = "3D Weapon Creator" - title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER - vbox.add_child(title) - - vbox.add_child(HSeparator.new()) - - # Buttons - var button_hbox = HBoxContainer.new() - button_hbox.alignment = BoxContainer.ALIGNMENT_CENTER - button_hbox.add_theme_constant_override("separation", 8) - vbox.add_child(button_hbox) - - open_dialog_button = Button.new() - open_dialog_button.text = "Create Weapon (2D)" - open_dialog_button.custom_minimum_size = Vector2(150, 0) - open_dialog_button.pressed.connect(_on_open_dialog_pressed.bind(false)) - button_hbox.add_child(open_dialog_button) - - var open_dialog_button_3d = Button.new() - open_dialog_button_3d.text = "Create Weapon (3D)" - open_dialog_button_3d.custom_minimum_size = Vector2(150, 0) - open_dialog_button_3d.pressed.connect(_on_open_dialog_pressed.bind(true)) - button_hbox.add_child(open_dialog_button_3d) - - clear_button = Button.new() - clear_button.text = "Clear Log" - clear_button.pressed.connect(_on_clear_button_pressed) - button_hbox.add_child(clear_button) - - vbox.add_child(HSeparator.new()) - - # Horizontal split container for weapon viewer and log + # Horizontal split container for tabs and log var hsplit = HSplitContainer.new() hsplit.size_flags_vertical = Control.SIZE_EXPAND_FILL vbox.add_child(hsplit) @@ -79,23 +48,50 @@ func _build_ui() -> void: log_vbox.add_theme_constant_override("separation", 4) hsplit.add_child(log_vbox) - # Weapon Viewer (right side) + # Tab Container for Weapons and Bullets (right side) + tab_container = TabContainer.new() + tab_container.custom_minimum_size = Vector2(300, 0) + tab_container.size_flags_horizontal = Control.SIZE_EXPAND_FILL + hsplit.add_child(tab_container) + + # Weapon Viewer Tab var weapon_viewer_script = load("res://addons/weapon_creator/WeaponViewer.gd") weapon_viewer = PanelContainer.new() weapon_viewer.set_script(weapon_viewer_script) - weapon_viewer.custom_minimum_size = Vector2(300, 0) - weapon_viewer.size_flags_horizontal = Control.SIZE_EXPAND_FILL - # Connect duplicate weapon signal + weapon_viewer.name = "Weapons" weapon_viewer.duplicate_weapon_requested.connect(_on_weapon_data_confirmed) - hsplit.add_child(weapon_viewer) + weapon_viewer.weapon_deleted.connect(_on_weapon_deleted) + weapon_viewer.weapon_duplication_started.connect(_on_weapon_duplication_started) + tab_container.add_child(weapon_viewer) + + # Bullet Viewer Tab + var bullet_viewer_script = load("res://addons/weapon_creator/BulletViewer.gd") + bullet_viewer = PanelContainer.new() + bullet_viewer.set_script(bullet_viewer_script) + bullet_viewer.name = "Bullets" + bullet_viewer.duplicate_bullet_requested.connect(_on_bullet_data_confirmed) + bullet_viewer.bullet_deleted.connect(_on_bullet_deleted) + bullet_viewer.bullet_duplication_started.connect(_on_bullet_duplication_started) + tab_container.add_child(bullet_viewer) # Setup after adding to scene tree if _editor_interface: - weapon_viewer.setup(_editor_interface) + weapon_viewer.setup(_editor_interface, self) + bullet_viewer.setup(_editor_interface, self) + + # Log header with label and clear button + var log_header_hbox = HBoxContainer.new() + log_vbox.add_child(log_header_hbox) var log_label = Label.new() log_label.text = "Output Log:" - log_vbox.add_child(log_label) + log_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL + log_header_hbox.add_child(log_label) + + clear_button = Button.new() + clear_button.text = "Clear Log" + clear_button.pressed.connect(_on_clear_button_pressed) + log_header_hbox.add_child(clear_button) var log_scroll = ScrollContainer.new() log_scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL @@ -110,49 +106,48 @@ func _build_ui() -> void: log_output.size_flags_vertical = Control.SIZE_EXPAND_FILL log_scroll.add_child(log_output) -func _on_open_dialog_pressed(is_3d: bool) -> void: - # Open the weapon creation dialog window - var dialog_script = load("res://addons/weapon_creator/WeaponCreatorDialog.gd") - var dialog = Window.new() - dialog.set_script(dialog_script) - - # Add to scene tree first - get_tree().root.add_child(dialog) - - # Setup editor interface and mode using call_deferred to ensure script is ready - if _editor_interface: - dialog.call_deferred("setup", _editor_interface, is_3d) - - # Connect to the confirmation signal using call_deferred - dialog.call_deferred("connect", "weapon_data_confirmed", _on_weapon_data_confirmed) - - # Show the dialog - dialog.call_deferred("popup_centered") - func _on_weapon_data_confirmed(weapon_data: Dictionary) -> void: - # Receive weapon data from dialog and forward to plugin if _is_creating: return _is_creating = true - open_dialog_button.disabled = true log_output.clear() - # Emit signal to plugin create_weapon_requested.emit(weapon_data) func _on_clear_button_pressed() -> void: log_output.clear() +func _on_weapon_deleted(weapon_name: String, weapon_path: String, item_path: String) -> void: + pass + +func _on_weapon_duplication_started(weapon_name: String, is_3d: bool) -> void: + pass + +func _on_bullet_data_confirmed(bullet_data: Dictionary) -> void: + if _is_creating: + return + + _is_creating = true + + create_bullet_requested.emit(bullet_data) + +func _on_bullet_deleted(bullet_name: String, bullet_path: String) -> void: + # Logging is handled directly in BulletViewer + pass + +func _on_bullet_duplication_started(bullet_name: String, is_3d: bool) -> void: + # Logging is handled directly in BulletViewer + pass + func add_log(message: String, color: Color = Color.WHITE) -> void: # Add colored message to log output log_output.append_text("[color=#" + color.to_html(false) + "]" + message + "[/color]\n") func set_creation_complete() -> void: - # Re-enable the create button after creation is complete _is_creating = false - open_dialog_button.disabled = false - # Refresh weapon viewer to show newly created weapon if weapon_viewer: weapon_viewer.call("refresh_weapons") + if bullet_viewer: + bullet_viewer.call("refresh_bullets") diff --git a/addons/weapon_creator/WeaponCreatorPlugin.gd b/addons/weapon_creator/WeaponCreatorPlugin.gd index 535c8779..625c39cb 100644 --- a/addons/weapon_creator/WeaponCreatorPlugin.gd +++ b/addons/weapon_creator/WeaponCreatorPlugin.gd @@ -18,6 +18,8 @@ func _enter_tree() -> void: # Connect the create button signal from the dock if dock_instance.has_signal("create_weapon_requested"): dock_instance.create_weapon_requested.connect(_on_create_weapon_requested) + if dock_instance.has_signal("create_bullet_requested"): + dock_instance.create_bullet_requested.connect(_on_create_bullet_requested) # Add the dock to the editor (bottom dock area) add_control_to_bottom_panel(dock_instance, "Weapon Creator") @@ -249,3 +251,77 @@ func _add_to_items_database(database_path: String, item_resource_path: String) - return false return true + +func _on_create_bullet_requested(bullet_data: Dictionary) -> void: + var bullet_name: String = bullet_data.get("bullet_name", "new_bullet") + var is_3d: bool = bullet_data.get("is_3d", true) + + var bullets_dir = "res://Resources/Bullets/" + if is_3d: + bullets_dir += "3D/" + + var bullet_path = bullets_dir + bullet_name + ".tres" + + dock_instance.call("add_log", "=== Starting Bullet Creation ===") + dock_instance.call("add_log", "Bullet Name: " + bullet_name) + dock_instance.call("add_log", "Mode: " + ("3D" if is_3d else "2D")) + + if _create_bullet_resource(bullet_path, bullet_data): + dock_instance.call("add_log", "✓ Created BulletResource at: " + bullet_path, Color.GREEN) + else: + dock_instance.call("add_log", "✗ Failed to create BulletResource", Color.RED) + dock_instance.call("set_creation_complete") + return + + dock_instance.call("add_log", "=== Bullet Creation Complete ===", Color.CYAN) + dock_instance.call("add_log", "Next steps:") + dock_instance.call("add_log", "1. Test the bullet in-game") + dock_instance.call("add_log", "2. Adjust properties as needed") + dock_instance.call("set_creation_complete") + + get_editor_interface().get_resource_filesystem().scan() + +func _create_bullet_resource(path: String, bullet_data: Dictionary) -> bool: + if ResourceLoader.exists(path): + dock_instance.call("add_log", "Warning: BulletResource already exists at " + path, Color.YELLOW) + return false + + var bullet_script = load("res://Scripts/Resources/BulletResource.cs") + if bullet_script == null: + dock_instance.call("add_log", "Error: Could not load BulletResource.cs script", Color.RED) + return false + + var bullet_resource = Resource.new() + bullet_resource.set_script(bullet_script) + + # Set properties from bullet_data + var bullet_sprite = bullet_data.get("bullet_sprite", null) + if bullet_sprite: + bullet_resource.set("BulletSprite", bullet_sprite) + + var bullet_scene = bullet_data.get("bullet_scene", null) + if bullet_scene: + bullet_resource.set("BulletScene", bullet_scene) + + bullet_resource.set("BulletSize", bullet_data.get("bullet_size", 1.0)) + bullet_resource.set("BulletSpeed", bullet_data.get("bullet_speed", 100.0)) + bullet_resource.set("BulletDamage", bullet_data.get("bullet_damage", 1.0)) + bullet_resource.set("MaxDamage", bullet_data.get("max_damage", 1.0)) + bullet_resource.set("Knockback", bullet_data.get("knockback", 1.0)) + bullet_resource.set("LifeTime", bullet_data.get("life_time", 10.0)) + bullet_resource.set("GrazeValue", bullet_data.get("graze_value", 0.2)) + + var destruction_particles = bullet_data.get("destruction_particles_scene", null) + if destruction_particles: + bullet_resource.set("DestructionParticlesScene", destruction_particles) + + # Set default Direction + bullet_resource.set("Direction", Vector2.RIGHT) + + var err = ResourceSaver.save(bullet_resource, path) + if err != OK: + dock_instance.call("add_log", "Error saving BulletResource: " + str(err), Color.RED) + return false + + return true + diff --git a/addons/weapon_creator/WeaponViewer.gd b/addons/weapon_creator/WeaponViewer.gd index 0402860e..ee7d7ac9 100644 --- a/addons/weapon_creator/WeaponViewer.gd +++ b/addons/weapon_creator/WeaponViewer.gd @@ -7,18 +7,22 @@ extends PanelContainer signal weapon_selected(weapon_resource_path: String) signal duplicate_weapon_requested(weapon_data: Dictionary) +signal weapon_deleted(weapon_name: String, weapon_path: String, item_path: String) +signal weapon_duplication_started(weapon_name: String, is_3d: bool) var _editor_interface: EditorInterface var _grid_container: HFlowContainer var _items_database_path := "res://Resources/ItemsDatabase.tres" var _show_2d_checkbox: CheckBox var _show_3d_checkbox: CheckBox +var _dock: PanelContainer # Reference to the dock for logging const SETTING_SHOW_2D = "weapon_creator/filter_show_2d" const SETTING_SHOW_3D = "weapon_creator/filter_show_3d" -func setup(editor_interface: EditorInterface) -> void: +func setup(editor_interface: EditorInterface, dock: PanelContainer = null) -> void: _editor_interface = editor_interface + _dock = dock # Load saved filter settings after editor interface is available if _show_2d_checkbox: _show_2d_checkbox.button_pressed = _load_filter_setting(SETTING_SHOW_2D, true) @@ -49,10 +53,20 @@ func _build_ui() -> void: var header_hbox = HBoxContainer.new() vbox.add_child(header_hbox) - var title = Label.new() - title.text = "Weapons" - title.size_flags_horizontal = Control.SIZE_EXPAND_FILL - header_hbox.add_child(title) + var create_2d_button = Button.new() + create_2d_button.text = "Create (2D)" + create_2d_button.pressed.connect(_on_create_weapon_pressed.bind(false)) + header_hbox.add_child(create_2d_button) + + var create_3d_button = Button.new() + create_3d_button.text = "Create (3D)" + create_3d_button.pressed.connect(_on_create_weapon_pressed.bind(true)) + header_hbox.add_child(create_3d_button) + + # Spacer + var spacer = Control.new() + spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL + header_hbox.add_child(spacer) _show_2d_checkbox = CheckBox.new() _show_2d_checkbox.text = "2D" @@ -318,6 +332,20 @@ func _duplicate_weapon(loot_item: Resource, weapon_data: Resource, is_3d: bool) push_error("Editor interface not available") return + var weapon_name = loot_item.get("ItemName") if loot_item else "Unknown" + var dimension = "3D" if is_3d else "2D" + + # Log to dock + if _dock: + _dock.call("add_log", "=== Duplicating Weapon (" + dimension + ") ===", Color.CYAN) + _dock.call("add_log", "Source: " + weapon_name, Color.CYAN) + _dock.call("add_log", "Opening creation dialog with prefilled data...", Color.CYAN) + + # Also log to Godot console + print("Duplicating weapon (", dimension, "): ", weapon_name) + + weapon_duplication_started.emit(weapon_name, is_3d) + # Extract all weapon data into a prefill dictionary var prefill_data = {} @@ -413,6 +441,7 @@ func _confirm_delete(loot_item: Resource, weapon_data: Resource) -> void: func _perform_delete(loot_item: Resource, weapon_data: Resource, dialog: ConfirmationDialog) -> void: var weapon_path = weapon_data.resource_path var loot_item_path = loot_item.resource_path + var weapon_name = loot_item.get("ItemName") if loot_item else "Unknown" if not weapon_path or not loot_item_path: push_error("Cannot delete built-in resources") @@ -440,13 +469,18 @@ func _perform_delete(loot_item: Resource, weapon_data: Resource, dialog: Confirm var file_system = _editor_interface.get_resource_filesystem() + var weapon_deleted_success = false + var item_deleted_success = false + if DirAccess.remove_absolute(weapon_path) == OK: print("Deleted weapon resource: ", weapon_path) + weapon_deleted_success = true else: push_error("Failed to delete weapon resource: ", weapon_path) if DirAccess.remove_absolute(loot_item_path) == OK: print("Deleted loot item resource: ", loot_item_path) + item_deleted_success = true else: push_error("Failed to delete loot item resource: ", loot_item_path) @@ -455,6 +489,18 @@ func _perform_delete(loot_item: Resource, weapon_data: Resource, dialog: Confirm dialog.queue_free() + # Emit deletion signal and log if both files were deleted successfully + if weapon_deleted_success and item_deleted_success: + weapon_deleted.emit(weapon_name, weapon_path, loot_item_path) + + # Log to dock + if _dock: + _dock.call("add_log", "=== Weapon Deleted ===", Color.ORANGE) + _dock.call("add_log", "Weapon: " + weapon_name, Color.ORANGE) + _dock.call("add_log", "✓ Deleted WeaponResource: " + weapon_path, Color.GREEN) + _dock.call("add_log", "✓ Deleted LootItem: " + loot_item_path, Color.GREEN) + _dock.call("add_log", "✓ Removed from ItemsDatabase", Color.GREEN) + refresh_weapons() func _add_error_label(error_message: String) -> void: @@ -464,3 +510,18 @@ func _add_error_label(error_message: String) -> void: label.modulate = Color.ORANGE_RED _grid_container.add_child(label) +func _on_create_weapon_pressed(is_3d: bool) -> void: + if not _editor_interface: + push_error("Editor interface not available") + return + + var dialog_script = load("res://addons/weapon_creator/WeaponCreatorDialog.gd") + var dialog = Window.new() + dialog.set_script(dialog_script) + + get_tree().root.add_child(dialog) + + dialog.call_deferred("setup", _editor_interface, is_3d) + dialog.call_deferred("connect", "weapon_data_confirmed", _on_duplicate_weapon_confirmed) + dialog.call_deferred("popup_centered") +