From c78fa8aa45f7da08e24f335f0feab472805566ed Mon Sep 17 00:00:00 2001 From: MaddoScientisto Date: Sun, 8 Feb 2026 16:44:38 +0100 Subject: [PATCH] Enhance weapon creation dialog with 2D/3D mode support and prefill functionality --- addons/weapon_creator/WeaponCreatorDialog.gd | 103 +++++- addons/weapon_creator/WeaponCreatorDock.gd | 40 ++- addons/weapon_creator/WeaponCreatorPlugin.gd | 29 +- addons/weapon_creator/WeaponViewer.gd | 310 +++++++++++++++++-- 4 files changed, 427 insertions(+), 55 deletions(-) diff --git a/addons/weapon_creator/WeaponCreatorDialog.gd b/addons/weapon_creator/WeaponCreatorDialog.gd index 65a4d504..8118a865 100644 --- a/addons/weapon_creator/WeaponCreatorDialog.gd +++ b/addons/weapon_creator/WeaponCreatorDialog.gd @@ -1,4 +1,4 @@ -@tool +@tool extends Window # Popup window for configuring weapon parameters before creation @@ -40,15 +40,37 @@ var random_spread_field: SpinBox var create_button: Button var cancel_button: Button -func setup(editor_iface: EditorInterface) -> void: +# Mode and prefill data +var is_3d_mode: bool = true +var prefill_data: Dictionary = {} +var _ui_built: bool = false + +func setup(editor_iface: EditorInterface, is_3d: bool = true, prefill: Dictionary = {}) -> void: editor_interface = editor_iface + is_3d_mode = is_3d + prefill_data = prefill + + # Update title based on mode and prefill status + 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 + " Weapon (" + mode_text + ")" + # If sprite picker already exists, ensure it has editor context if sprite_picker: _setup_sprite_picker() + + # If UI is already built (setup called after _ready), apply prefill data now + if _ui_built: + if prefill_data.is_empty(): + _set_default_values() + else: + _apply_prefill_data() func _ready() -> void: # Window configuration - title = "Create New Weapon" + 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 + " Weapon (" + mode_text + ")" size = Vector2i(750, 950) transient = true exclusive = true @@ -60,7 +82,14 @@ func _ready() -> void: position = (DisplayServer.screen_get_size() - size) / 2 _build_ui() - _set_default_values() + _ui_built = true + + # Apply prefill data if provided, otherwise use defaults + # Note: this might be overridden by setup() if called via call_deferred + if prefill_data.is_empty(): + _set_default_values() + else: + _apply_prefill_data() func _build_ui() -> void: # Main margin container @@ -326,8 +355,9 @@ func _set_default_values() -> void: short_name_field.text = "NW-1" description_field.text = "A new weapon for testing" - # Select default bullet (simple_ice_bullet) - _select_bullet_by_path("res://Resources/Bullets/simple_ice_bullet.tres") + # Select default bullet based on mode + var default_bullet = "res://Resources/Bullets/3D/icicle_repeater_bullets_3D.tres" if is_3d_mode else "res://Resources/Bullets/simple_ice_bullet.tres" + _select_bullet_by_path(default_bullet) # Weapon stats defaults priority_field.value = 10 @@ -343,8 +373,11 @@ func _set_default_values() -> void: random_spread_field.value = 0.0 func _populate_bullet_dropdown() -> void: - # Get all bullet resources from the Bullets folder + # Get all bullet resources from the appropriate Bullets folder var bullets_dir = "res://Resources/Bullets/" + if is_3d_mode: + bullets_dir += "3D/" + var dir = DirAccess.open(bullets_dir) if dir == null: @@ -410,6 +443,7 @@ func _on_create_pressed() -> void: "weapon_description": description_field.text, "sprite_resource": sprite_resource, "default_bullet_path": bullet_path_field.text, + "is_3d": is_3d_mode, "priority": int(priority_field.value), "ammo_per_shot": int(ammo_per_shot_field.value), "rate_of_fire": rate_of_fire_field.value, @@ -451,6 +485,19 @@ func _validate_inputs() -> bool: if not key.to_upper() == key: _show_warning("Item key should be uppercase") + # Check if resources already exist + var dimension_suffix = "_3D" if is_3d_mode else "_2D" + var weapon_resource_path: String = "res://Resources/Weapons/" + key + dimension_suffix + ".tres" + var item_resource_path: String = "res://Resources/Items/" + key + "_Item" + dimension_suffix + ".tres" + + if ResourceLoader.exists(weapon_resource_path): + _show_error("Weapon resource already exists at:\n" + weapon_resource_path + "\n\nPlease choose a different item key.") + return false + + if ResourceLoader.exists(item_resource_path): + _show_error("Item resource already exists at:\n" + item_resource_path + "\n\nPlease choose a different item key.") + return false + # Check bullet path if not ResourceLoader.exists(bullet_path_field.text): _show_error("Bullet resource does not exist at: " + bullet_path_field.text) @@ -472,3 +519,45 @@ func _show_warning(message: String) -> void: add_child(dialog) dialog.popup_centered() +func _apply_prefill_data() -> void: + # Apply prefilled data from duplicating an existing weapon + if prefill_data.has("weapon_name"): + weapon_name_field.text = prefill_data["weapon_name"] + if prefill_data.has("item_key"): + item_key_field.text = prefill_data["item_key"] + if prefill_data.has("ammo_key"): + ammo_key_field.text = prefill_data["ammo_key"] + if prefill_data.has("short_name"): + short_name_field.text = prefill_data["short_name"] + if prefill_data.has("description"): + description_field.text = prefill_data["description"] + if prefill_data.has("sprite_resource") and prefill_data["sprite_resource"] != null: + sprite_resource = prefill_data["sprite_resource"] + sprite_preview.texture = sprite_resource + sprite_picker.edited_resource = sprite_resource + if prefill_data.has("bullet_path"): + _select_bullet_by_path(prefill_data["bullet_path"]) + + # Apply weapon stats + if prefill_data.has("priority"): + priority_field.value = prefill_data["priority"] + if prefill_data.has("ammo_per_shot"): + ammo_per_shot_field.value = prefill_data["ammo_per_shot"] + if prefill_data.has("rate_of_fire"): + rate_of_fire_field.value = prefill_data["rate_of_fire"] + if prefill_data.has("bullet_capacity"): + bullet_capacity_field.value = prefill_data["bullet_capacity"] + if prefill_data.has("reload_time"): + reload_time_field.value = prefill_data["reload_time"] + if prefill_data.has("infinite_ammo"): + infinite_ammo_check.button_pressed = prefill_data["infinite_ammo"] + if prefill_data.has("recharge_time"): + recharge_time_field.value = prefill_data["recharge_time"] + if prefill_data.has("recharge_amount"): + recharge_amount_field.value = prefill_data["recharge_amount"] + if prefill_data.has("bullets_per_shot"): + bullets_per_shot_field.value = prefill_data["bullets_per_shot"] + if prefill_data.has("spread_angle"): + spread_angle_field.value = prefill_data["spread_angle"] + if prefill_data.has("random_spread"): + random_spread_field.value = prefill_data["random_spread"] diff --git a/addons/weapon_creator/WeaponCreatorDock.gd b/addons/weapon_creator/WeaponCreatorDock.gd index 3b250193..fc5d84c4 100644 --- a/addons/weapon_creator/WeaponCreatorDock.gd +++ b/addons/weapon_creator/WeaponCreatorDock.gd @@ -50,11 +50,17 @@ func _build_ui() -> void: vbox.add_child(button_hbox) open_dialog_button = Button.new() - open_dialog_button.text = "Create New Weapon" + 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) + 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) @@ -79,10 +85,14 @@ func _build_ui() -> void: weapon_viewer.set_script(weapon_viewer_script) weapon_viewer.custom_minimum_size = Vector2(300, 0) weapon_viewer.size_flags_horizontal = Control.SIZE_EXPAND_FILL - if _editor_interface: - weapon_viewer.call("setup", _editor_interface) + # Connect duplicate weapon signal + weapon_viewer.duplicate_weapon_requested.connect(_on_weapon_data_confirmed) hsplit.add_child(weapon_viewer) + # Setup after adding to scene tree + if _editor_interface: + weapon_viewer.setup(_editor_interface) + var log_label = Label.new() log_label.text = "Output Log:" log_vbox.add_child(log_label) @@ -98,22 +108,24 @@ func _build_ui() -> void: log_output.size_flags_vertical = Control.SIZE_EXPAND_FILL log_scroll.add_child(log_output) -func _on_open_dialog_pressed() -> void: +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) - # Setup editor interface - if _editor_interface: - dialog.call("setup", _editor_interface) - - # Connect to the confirmation signal - dialog.weapon_data_confirmed.connect(_on_weapon_data_confirmed) - - # Add to scene tree and show + # Add to scene tree first get_tree().root.add_child(dialog) - dialog.popup_centered() + + # 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 diff --git a/addons/weapon_creator/WeaponCreatorPlugin.gd b/addons/weapon_creator/WeaponCreatorPlugin.gd index 23b81ebc..535c8779 100644 --- a/addons/weapon_creator/WeaponCreatorPlugin.gd +++ b/addons/weapon_creator/WeaponCreatorPlugin.gd @@ -36,7 +36,8 @@ func _on_create_weapon_requested(weapon_data: Dictionary) -> void: var weapon_short_name: String = weapon_data.get("weapon_short_name", "NW-1") var weapon_description: String = weapon_data.get("weapon_description", "A new weapon") var sprite_resource: Texture2D = weapon_data.get("sprite_resource", null) - var default_bullet_path: String = weapon_data.get("default_bullet_path", "res://Resources/Bullets/simple_ice_bullet.tres") + var default_bullet_path: String = weapon_data.get("default_bullet_path", "res://Resources/Bullets/3D/icicle_repeater_bullets_3D.tres") + var is_3d: bool = weapon_data.get("is_3d", true) # Weapon stats var priority: int = weapon_data.get("priority", 10) @@ -51,10 +52,11 @@ func _on_create_weapon_requested(weapon_data: Dictionary) -> void: var spread_angle: float = weapon_data.get("spread_angle", 0.0) var random_spread: float = weapon_data.get("random_spread", 0.0) - # Build resource paths - var weapon_resource_path := "res://Resources/Weapons/" + weapon_item_key + ".tres" - var item_resource_path := "res://Resources/Items/" + weapon_item_key + "_Item.tres" - var items_database_path := "res://Resources/ItemsDatabase.tres" + # Append 2D or 3D to the end of the key + var dimension_suffix = "_3D" if is_3d else "_2D" + var weapon_resource_path: String = "res://Resources/Weapons/" + weapon_item_key + dimension_suffix + ".tres" + var item_resource_path: String = "res://Resources/Items/" + weapon_item_key + "_Item" + dimension_suffix + ".tres" + var items_database_path: String = "res://Resources/ItemsDatabase.tres" # Feedback to the UI dock_instance.call("add_log", "=== Starting Weapon Creation ===") @@ -74,7 +76,7 @@ func _on_create_weapon_requested(weapon_data: Dictionary) -> void: # Step 2: Create LootItem resource if _create_loot_item_resource(item_resource_path, weapon_name, weapon_short_name, - weapon_description, weapon_item_key, weapon_resource_path, sprite_resource): + weapon_description, weapon_item_key, weapon_resource_path, sprite_resource, is_3d): dock_instance.call("add_log", "✓ Created LootItem at: " + item_resource_path, Color.GREEN) else: dock_instance.call("add_log", "✗ Failed to create LootItem", Color.RED) @@ -154,7 +156,7 @@ func _create_weapon_resource(path: String, weapon_name: String, item_key: String func _create_loot_item_resource(path: String, item_name: String, short_name: String, description: String, item_key: String, - weapon_resource_path: String, sprite_resource: Texture2D) -> bool: + weapon_resource_path: String, sprite_resource: Texture2D, is_3d: bool) -> bool: # Check if file already exists if ResourceLoader.exists(path): dock_instance.call("add_log", "Warning: LootItem already exists at " + path, Color.YELLOW) @@ -181,8 +183,17 @@ func _create_loot_item_resource(path: String, item_name: String, short_name: Str loot_item.set("ShortName", short_name) loot_item.set("ItemDescription", description) loot_item.set("ItemKey", item_key) - loot_item.set("Item", 13) # ItemTypes.Weapon = 13 - loot_item.set("WeaponData3D", weapon_resource) + + # ItemTypes.Weapon enum value + var item_types_script = load("res://Scripts/ItemTypes.cs") + var weapon_enum_value = 9 # ItemTypes.Weapon = 9 (from enum definition) + loot_item.set("Item", weapon_enum_value) + + # Set weapon data in appropriate property based on mode + if is_3d: + loot_item.set("WeaponData3D", weapon_resource) + else: + loot_item.set("WeaponData", weapon_resource) loot_item.set("Amount", 1) loot_item.set("Max", 1) loot_item.set("Selectable", true) diff --git a/addons/weapon_creator/WeaponViewer.gd b/addons/weapon_creator/WeaponViewer.gd index f50161a0..4c712f87 100644 --- a/addons/weapon_creator/WeaponViewer.gd +++ b/addons/weapon_creator/WeaponViewer.gd @@ -6,10 +6,13 @@ extends PanelContainer # Clicking opens the weapon resource in the inspector signal weapon_selected(weapon_resource_path: String) +signal duplicate_weapon_requested(weapon_data: Dictionary) 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 func setup(editor_interface: EditorInterface) -> void: _editor_interface = editor_interface @@ -41,6 +44,18 @@ func _build_ui() -> void: title.size_flags_horizontal = Control.SIZE_EXPAND_FILL header_hbox.add_child(title) + _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_weapons) @@ -84,48 +99,86 @@ func refresh_weapons() -> void: _add_error_label("No LootItems found in database") return - # Filter for weapons only (items with WeaponData3D) + # Filter for weapons only (items with WeaponData3D or WeaponData) + 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 weapon_count = 0 for loot_item in loot_items: - var weapon_data = loot_item.get("WeaponData3D") - if weapon_data != null: - _create_weapon_tile(loot_item, weapon_data) + var weapon_data_3d = loot_item.get("WeaponData3D") + var weapon_data_2d = loot_item.get("WeaponData") + + if weapon_data_2d != null and show_2d: + _create_weapon_tile(loot_item, weapon_data_2d, false) + weapon_count += 1 + + if weapon_data_3d != null and show_3d: + _create_weapon_tile(loot_item, weapon_data_3d, true) weapon_count += 1 if weapon_count == 0: _add_error_label("No weapons found in database") -func _create_weapon_tile(loot_item: Resource, weapon_data: Resource) -> void: - var button = Button.new() - button.custom_minimum_size = Vector2(100, 110) - button.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN - button.size_flags_vertical = Control.SIZE_SHRINK_BEGIN +func _on_filter_changed(_toggled: bool) -> void: + refresh_weapons() + +func _create_weapon_tile(loot_item: Resource, weapon_data: 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", 2) + vbox.add_theme_constant_override("separation", 4) vbox.mouse_filter = Control.MOUSE_FILTER_IGNORE var sprite: Texture2D = loot_item.get("InventorySprite") + # 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 - vbox.add_child(texture_rect) 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.custom_minimum_size = Vector2(64, 64) placeholder_label.mouse_filter = Control.MOUSE_FILTER_IGNORE placeholder_label.add_theme_font_size_override("font_size", 10) - vbox.add_child(placeholder_label) + placeholder_label.set_anchors_preset(Control.PRESET_FULL_RECT) + sprite_container.add_child(placeholder_label) + + # Add 2D/3D badge in bottom-right corner with colored text and dark outline + 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)) # Bright red for 3D + else: + badge.add_theme_color_override("font_color", Color(0.3, 0.5, 1.0)) # Bright blue for 2D + badge.add_theme_color_override("font_outline_color", Color(0.15, 0.15, 0.15)) # Dark gray outline + 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 item_name: String = loot_item.get("ItemName") @@ -133,16 +186,15 @@ func _create_weapon_tile(loot_item: Resource, weapon_data: Resource) -> void: 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(90, 0) - name_label.size_flags_vertical = Control.SIZE_EXPAND_FILL - name_label.clip_text = true - name_label.max_lines_visible = 2 + 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", 11) + name_label.add_theme_font_size_override("font_size", 14) name_label.add_theme_constant_override("line_spacing", -2) vbox.add_child(name_label) - button.add_child(vbox) + 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) @@ -151,25 +203,233 @@ func _create_weapon_tile(loot_item: Resource, weapon_data: Resource) -> void: var weapon_path = weapon_data.resource_path if weapon_path: - button.tooltip_text = weapon_path + panel.tooltip_text = weapon_path else: - button.tooltip_text = "Built-in resource (no file path)" + panel.tooltip_text = "Built-in resource (no file path)" - button.pressed.connect(_on_weapon_clicked.bind(weapon_data, weapon_path)) + panel.gui_input.connect(_on_weapon_gui_input.bind(panel, loot_item, weapon_data)) - _grid_container.add_child(button) + _grid_container.add_child(panel) func _on_weapon_clicked(weapon_data: Resource, weapon_path: String) -> void: if not _editor_interface: push_error("Editor interface not available") return - # Select the weapon resource in the inspector if weapon_data: _editor_interface.edit_resource(weapon_data) - _editor_interface.get_inspector().set_current_tab(0) weapon_selected.emit(weapon_path) +func _on_weapon_gui_input(event: InputEvent, panel: PanelContainer, loot_item: Resource, weapon_data: Resource) -> void: + if event is InputEventMouseButton: + var mouse_event = event as InputEventMouseButton + if mouse_event.pressed: + if mouse_event.button_index == MOUSE_BUTTON_LEFT: + var weapon_path = weapon_data.resource_path if weapon_data else "" + _on_weapon_clicked(weapon_data, weapon_path) + elif mouse_event.button_index == MOUSE_BUTTON_RIGHT: + _show_context_menu(panel, loot_item, weapon_data) + +func _show_context_menu(panel: PanelContainer, loot_item: Resource, weapon_data: Resource) -> void: + var popup = PopupMenu.new() + popup.add_item("Open Weapon", 0) + popup.add_item("Open Item", 1) + popup.add_separator() + popup.add_item("Duplicate Weapon (2D)", 2) + popup.add_item("Duplicate Weapon (3D)", 3) + popup.add_separator() + popup.add_item("Copy Weapon Resource Path", 4) + popup.add_item("Copy Item Resource Path", 5) + popup.add_separator() + popup.add_item("Delete", 6) + + popup.id_pressed.connect(_on_context_menu_id_pressed.bind(popup, loot_item, weapon_data)) + + 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, loot_item: Resource, weapon_data: Resource) -> void: + match id: + 0: + _open_weapon(weapon_data) + 1: + _open_item(loot_item) + 2: + _duplicate_weapon(loot_item, weapon_data, false) + 3: + _duplicate_weapon(loot_item, weapon_data, true) + 4: + _copy_weapon_resource_path(weapon_data) + 5: + _copy_item_resource_path(loot_item) + 6: + _confirm_delete(loot_item, weapon_data) + +func _open_weapon(weapon_data: Resource) -> void: + if not _editor_interface: + push_error("Editor interface not available") + return + + if weapon_data: + _editor_interface.edit_resource(weapon_data) + +func _open_item(loot_item: Resource) -> void: + if not _editor_interface: + push_error("Editor interface not available") + return + + if loot_item: + _editor_interface.edit_resource(loot_item) + +func _duplicate_weapon(loot_item: Resource, weapon_data: Resource, is_3d: bool) -> void: + if not _editor_interface: + push_error("Editor interface not available") + return + + # Extract all weapon data into a prefill dictionary + var prefill_data = {} + + # Extract from LootItem + if loot_item: + prefill_data["weapon_name"] = loot_item.get("ItemName") + prefill_data["item_key"] = loot_item.get("ItemKey") + prefill_data["short_name"] = loot_item.get("ShortName") + prefill_data["description"] = loot_item.get("ItemDescription") + prefill_data["sprite_resource"] = loot_item.get("InventorySprite") + + # Extract from WeaponResource + if weapon_data: + var bullet_data = weapon_data.get("BulletData") + if bullet_data and bullet_data.resource_path: + prefill_data["bullet_path"] = bullet_data.resource_path + + prefill_data["ammo_key"] = weapon_data.get("AmmoKey") + prefill_data["priority"] = weapon_data.get("Priority") + prefill_data["ammo_per_shot"] = weapon_data.get("AmmoPerShot") + prefill_data["rate_of_fire"] = weapon_data.get("RateOfFire") + prefill_data["bullet_capacity"] = weapon_data.get("BulletCapacity") + prefill_data["reload_time"] = weapon_data.get("ReloadTime") + prefill_data["infinite_ammo"] = weapon_data.get("InfiniteAmmo") + prefill_data["recharge_time"] = weapon_data.get("RechargeTime") + prefill_data["recharge_amount"] = weapon_data.get("RechargeAmount") + prefill_data["bullets_per_shot"] = weapon_data.get("BulletsPerShot") + prefill_data["spread_angle"] = weapon_data.get("SpreadAngle") + prefill_data["random_spread"] = weapon_data.get("RandomSpread") + + # Open creation dialog with prefilled data + 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 with prefill data using call_deferred to ensure script is ready + dialog.call_deferred("setup", _editor_interface, is_3d, prefill_data) + + # Connect to confirmation signal using call_deferred + dialog.call_deferred("connect", "weapon_data_confirmed", _on_duplicate_weapon_confirmed) + + # Show the dialog + dialog.call_deferred("popup_centered") + +func _on_duplicate_weapon_confirmed(weapon_data: Dictionary) -> void: + # Forward the weapon data to be created + duplicate_weapon_requested.emit(weapon_data) + # Refresh after a short delay to allow file system to update + get_tree().create_timer(0.5).timeout.connect(refresh_weapons) + +func _copy_weapon_resource_path(weapon_data: Resource) -> void: + if weapon_data and weapon_data.resource_path: + DisplayServer.clipboard_set(weapon_data.resource_path) + print("Copied weapon resource path: ", weapon_data.resource_path) + else: + push_warning("No resource path available for weapon (built-in resource)") + +func _copy_item_resource_path(loot_item: Resource) -> void: + if loot_item and loot_item.resource_path: + DisplayServer.clipboard_set(loot_item.resource_path) + print("Copied item resource path: ", loot_item.resource_path) + else: + push_warning("No resource path available for item (built-in resource)") + +func _confirm_delete(loot_item: Resource, weapon_data: Resource) -> void: + var item_name: String = loot_item.get("ItemName") + var weapon_path = weapon_data.resource_path + var loot_item_path = loot_item.resource_path + + var dialog_text = "Are you REALLY sure you want to delete this weapon?\n\n" + dialog_text += "Weapon: " + (item_name if item_name else "Unknown") + "\n\n" + dialog_text += "This will delete:\n" + dialog_text += "- WeaponResource: " + (weapon_path if weapon_path else "Built-in") + "\n" + dialog_text += "- LootItem: " + (loot_item_path if loot_item_path else "Built-in") + "\n" + dialog_text += "- Remove the item from ItemsDatabase\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(loot_item, weapon_data, 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(loot_item: Resource, weapon_data: Resource, dialog: ConfirmationDialog) -> void: + var weapon_path = weapon_data.resource_path + var loot_item_path = loot_item.resource_path + + if not weapon_path or not loot_item_path: + push_error("Cannot delete built-in resources") + dialog.queue_free() + return + + var items_database = load(_items_database_path) + if not items_database: + push_error("Failed to load ItemsDatabase") + dialog.queue_free() + return + + var loot_items = items_database.get("LootItems") + if not loot_items: + push_error("No LootItems found in database") + dialog.queue_free() + return + + var index = loot_items.find(loot_item) + if index >= 0: + loot_items.remove_at(index) + var save_result = ResourceSaver.save(items_database, _items_database_path) + if save_result != OK: + push_error("Failed to save ItemsDatabase after removing item") + + var file_system = _editor_interface.get_resource_filesystem() + + if DirAccess.remove_absolute(weapon_path) == OK: + print("Deleted weapon resource: ", weapon_path) + 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) + else: + push_error("Failed to delete loot item resource: ", loot_item_path) + + if file_system: + file_system.scan() + + dialog.queue_free() + + refresh_weapons() + func _add_error_label(error_message: String) -> void: var label = Label.new() label.text = error_message