diff --git a/addons/weapon_creator/BaseCreatorDialog.gd b/addons/weapon_creator/BaseCreatorDialog.gd index 19175e7e..24a42c34 100644 --- a/addons/weapon_creator/BaseCreatorDialog.gd +++ b/addons/weapon_creator/BaseCreatorDialog.gd @@ -5,32 +5,83 @@ extends Window # Base class for all creator dialogs # Ensures consistent initialization and UI building patterns +const WeaponCreatorSettings = preload("res://addons/weapon_creator/WeaponCreatorSettings.gd") +const SETTINGS_PATH = "user://weapon_creator_settings.tres" + signal data_confirmed(data: Dictionary) var editor_interface: EditorInterface var prefill_data: Dictionary = {} var _ui_built: bool = false +var settings: Resource +# Base setup - override _custom_setup() in derived classes for additional initialization func setup(editor_iface: EditorInterface, prefill: Dictionary = {}) -> void: editor_interface = editor_iface prefill_data = prefill + if ResourceLoader.exists(SETTINGS_PATH): + settings = ResourceLoader.load(SETTINGS_PATH) + if not settings: + settings = WeaponCreatorSettings.new() + + _custom_setup() _update_title() if _ui_built: _apply_data() +# Override this in derived classes for custom initialization +func _custom_setup() -> void: + pass + func _ready() -> void: + if not settings: + if ResourceLoader.exists(SETTINGS_PATH): + settings = ResourceLoader.load(SETTINGS_PATH) + if not settings: + settings = WeaponCreatorSettings.new() + _configure_window() _update_title() - close_requested.connect(_on_cancel_pressed) - position = (DisplayServer.screen_get_size() - size) / 2 + close_requested.connect(_on_window_close_requested) _build_ui() _ui_built = true _apply_data() + + # Restore position after UI is built, or center if no saved position + _restore_or_center_position() + +func _restore_or_center_position() -> void: + var saved_pos = _get_saved_position() + if saved_pos != Vector2i.ZERO: + position = saved_pos + else: + # Use popup_centered_clamped to properly center on the current screen + popup_centered_clamped(size, 0.75) + +func _get_saved_position() -> Vector2i: + # Override in derived classes to return their specific saved position + return Vector2i.ZERO + +func _on_window_close_requested() -> void: + _save_dialog_size_and_position() + _on_cancel_pressed() + +func _save_dialog_size_and_position() -> void: + _save_dialog_size() + _save_dialog_position() + +func _save_dialog_size() -> void: + # Override in derived classes to save their specific dialog size + pass + +func _save_dialog_position() -> void: + # Override in derived classes to save their specific dialog position + pass func _configure_window() -> void: size = Vector2i(750, 850) diff --git a/addons/weapon_creator/BulletCreatorDialog.gd b/addons/weapon_creator/BulletCreatorDialog.gd index bf36f6c9..c76cccce 100644 --- a/addons/weapon_creator/BulletCreatorDialog.gd +++ b/addons/weapon_creator/BulletCreatorDialog.gd @@ -1,5 +1,5 @@ @tool -extends Window +extends BaseCreatorDialog # Popup window for configuring bullet parameters before creation @@ -19,91 +19,71 @@ 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 +# Mode var is_3d_mode: bool = true -var prefill_data: Dictionary = {} -var _ui_built: bool = false -var editor_interface: EditorInterface +var _setup_called: bool = false -func setup(editor_iface: EditorInterface, is_3d: bool = true, prefill: Dictionary = {}) -> void: - editor_interface = editor_iface +# Public method with custom signature - calls base setup internally +func setup_bullet(editor_iface: EditorInterface, is_3d: bool = true, prefill: Dictionary = {}) -> void: 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() + _setup_called = true + # Call parent setup with standard signature + setup(editor_iface, prefill) + +func _custom_setup() -> void: + # Called by parent setup() - use for any additional initialization + pass 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) + # Wait for setup to be called if it hasn't been yet + if not _setup_called: + await get_tree().process_frame + super._ready() + +func _configure_window() -> void: + size = settings.get("bullet_dialog_size") if settings else Vector2i(750, 850) 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()) - +func _update_title() -> 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 + ")" + +func _get_saved_position() -> Vector2i: + if settings: + var pos = settings.get("bullet_dialog_position") + return pos if pos else Vector2i.ZERO + return Vector2i.ZERO + +func _save_dialog_size() -> void: + if settings: + settings.set("bullet_dialog_size", size) + settings.call("save_settings") + +func _save_dialog_position() -> void: + if settings: + settings.set("bullet_dialog_position", position) + settings.call("save_settings") + +func _build_content(container: VBoxContainer) -> void: + _build_basic_section(container) + _build_stats_section(container) + +func _build_buttons(vbox: VBoxContainer) -> void: 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() + var 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() + var create_button = Button.new() create_button.text = "Create Bullet" create_button.custom_minimum_size = Vector2(150, 0) create_button.pressed.connect(_on_create_pressed) @@ -351,12 +331,14 @@ func _on_create_pressed() -> void: "destruction_particles_scene": destruction_particles_picker.edited_resource } + _save_dialog_size() bullet_data_confirmed.emit(bullet_data) hide() queue_free() func _on_cancel_pressed() -> void: + _save_dialog_size() hide() queue_free() @@ -369,9 +351,7 @@ func _validate_inputs() -> bool: 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 bullets_dir = settings.get("bullets_3d_dir") if is_3d_mode else settings.get("bullets_dir") var bullet_path = bullets_dir + bullet_name + ".tres" diff --git a/addons/weapon_creator/BulletViewer.gd b/addons/weapon_creator/BulletViewer.gd index 3e96d7a3..5d77e58a 100644 --- a/addons/weapon_creator/BulletViewer.gd +++ b/addons/weapon_creator/BulletViewer.gd @@ -395,7 +395,7 @@ func _open_bullet_dialog(is_3d: bool, prefill_data: Dictionary = {}) -> void: get_tree().root.add_child(dialog) - dialog.call_deferred("setup", _editor_interface, is_3d, prefill_data) + dialog.call_deferred("setup_bullet", _editor_interface, is_3d, prefill_data) dialog.call_deferred("connect", "bullet_data_confirmed", _on_duplicate_bullet_confirmed) dialog.call_deferred("popup_centered") diff --git a/addons/weapon_creator/ItemCreatorDialog.gd b/addons/weapon_creator/ItemCreatorDialog.gd index a0131741..80d22028 100644 --- a/addons/weapon_creator/ItemCreatorDialog.gd +++ b/addons/weapon_creator/ItemCreatorDialog.gd @@ -1,16 +1,11 @@ @tool -extends Window +extends BaseCreatorDialog # Dialog for creating new items # Provides input fields for all item properties signal item_data_confirmed(item_data: Dictionary) -# Editor reference -var editor_interface: EditorInterface -var prefill_data: Dictionary = {} -var _ui_built: bool = false - # UI elements var _item_name_edit: LineEdit var _item_key_edit: LineEdit @@ -46,72 +41,35 @@ const ITEM_TYPE_NAMES = [ "KeyItem" ] -func setup(editor_iface: EditorInterface, prefill: Dictionary = {}) -> void: - editor_interface = editor_iface - prefill_data = prefill - - var action_text = "Duplicate" if not prefill_data.is_empty() else "Create New" - title = action_text + " Item" - - if _ui_built: - if prefill_data.is_empty(): - _set_default_values() - else: - _apply_prefill_data() - -func _ready() -> void: - var action_text = "Duplicate" if not prefill_data.is_empty() else "Create New" - title = action_text + " Item" - size = Vector2i(750, 850) +func _configure_window() -> void: + size = settings.get("item_dialog_size") if settings else Vector2i(750, 850) 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 _set_default_values() -> void: - pass func _update_title() -> void: var action_text = "Duplicate" if not prefill_data.is_empty() else "Create New" title = action_text + " Item" -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 content_vbox = VBoxContainer.new() - content_vbox.size_flags_horizontal = Control.SIZE_EXPAND_FILL - content_vbox.add_theme_constant_override("separation", 12) - scroll.add_child(content_vbox) - - _build_content(content_vbox) - - vbox.add_child(HSeparator.new()) - _build_buttons(vbox) +func _get_saved_position() -> Vector2i: + if settings: + var pos = settings.get("item_dialog_position") + return pos if pos else Vector2i.ZERO + return Vector2i.ZERO + +func _save_dialog_size() -> void: + if settings: + settings.set("item_dialog_size", size) + settings.call("save_settings") + +func _save_dialog_position() -> void: + if settings: + settings.set("item_dialog_position", position) + settings.call("save_settings") + +func _build_content(container: VBoxContainer) -> void: + _build_basic_info_section(container) + _build_properties_section(container) func _build_buttons(parent: VBoxContainer) -> void: var button_hbox = HBoxContainer.new() @@ -131,7 +89,7 @@ func _build_buttons(parent: VBoxContainer) -> void: create_button.pressed.connect(_on_create_pressed) button_hbox.add_child(create_button) -func _build_content(content_vbox: VBoxContainer) -> void: +func _build_basic_info_section(content_vbox: VBoxContainer) -> void: # Basic Info Section content_vbox.add_child(_create_section_label("Basic Information")) @@ -204,7 +162,8 @@ func _build_content(content_vbox: VBoxContainer) -> void: _sprite_preview.expand_mode = TextureRect.EXPAND_FIT_WIDTH_PROPORTIONAL _sprite_preview.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED preview_container.add_child(_sprite_preview) - + +func _build_properties_section(content_vbox: VBoxContainer) -> void: content_vbox.add_child(HSeparator.new()) # Properties Section @@ -342,10 +301,12 @@ func _on_create_pressed() -> void: "selectable": _selectable_check.button_pressed } + _save_dialog_size() item_data_confirmed.emit(item_data) queue_free() func _on_cancel_pressed() -> void: + _save_dialog_size() queue_free() func _show_error(message: String) -> void: diff --git a/addons/weapon_creator/SettingsDialog.gd b/addons/weapon_creator/SettingsDialog.gd new file mode 100644 index 00000000..5d9f6ca0 --- /dev/null +++ b/addons/weapon_creator/SettingsDialog.gd @@ -0,0 +1,152 @@ +@tool +class_name SettingsDialog +extends BaseCreatorDialog + +# Settings dialog for configuring Weapon Creator plugin defaults + +# UI fields +var weapons_dir_field: LineEdit +var items_dir_field: LineEdit +var bullets_dir_field: LineEdit +var bullets_3d_dir_field: LineEdit +var items_database_field: LineEdit +var default_bullet_2d_field: LineEdit +var default_bullet_3d_field: LineEdit +var reset_button: Button + +func _init() -> void: + if ResourceLoader.exists(SETTINGS_PATH): + settings = ResourceLoader.load(SETTINGS_PATH) + if not settings: + settings = WeaponCreatorSettings.new() + +func _configure_window() -> void: + var saved_size = settings.get("settings_dialog_size") if settings else null + size = saved_size if saved_size else Vector2i(600, 600) + transient = false + exclusive = false + unresizable = false + +func _update_title() -> void: + title = "Weapon Creator Settings" + +func _get_saved_position() -> Vector2i: + if settings: + var pos = settings.get("settings_dialog_position") + return pos if pos else Vector2i.ZERO + return Vector2i.ZERO + +func _save_dialog_size() -> void: + settings.set("settings_dialog_size", size) + +func _save_dialog_position() -> void: + settings.set("settings_dialog_position", position) + +func _build_content(container: VBoxContainer) -> void: + var section_label = _create_section_label("Resource Paths") + container.add_child(section_label) + + var weapons_input = _create_input("Weapons Directory:") + weapons_dir_field = weapons_input.edit + container.add_child(weapons_input.container) + + var items_input = _create_input("Items Directory:") + items_dir_field = items_input.edit + container.add_child(items_input.container) + + var bullets_input = _create_input("Bullets Directory:") + bullets_dir_field = bullets_input.edit + container.add_child(bullets_input.container) + + var bullets_3d_input = _create_input("Bullets 3D Directory:") + bullets_3d_dir_field = bullets_3d_input.edit + container.add_child(bullets_3d_input.container) + + var database_input = _create_input("Items Database:") + items_database_field = database_input.edit + container.add_child(database_input.container) + + container.add_child(HSeparator.new()) + + var defaults_label = _create_section_label("Default Bullets") + container.add_child(defaults_label) + + var bullet_2d_input = _create_input("Default Bullet 2D:") + default_bullet_2d_field = bullet_2d_input.edit + container.add_child(bullet_2d_input.container) + + var bullet_3d_input = _create_input("Default Bullet 3D:") + default_bullet_3d_field = bullet_3d_input.edit + container.add_child(bullet_3d_input.container) + + container.add_child(HSeparator.new()) + + var reset_section = _create_section_label("Reset Options") + container.add_child(reset_section) + + reset_button = Button.new() + reset_button.text = "Reset All Settings (Filters, Dialog Sizes, Defaults)" + reset_button.pressed.connect(_on_reset_pressed) + container.add_child(reset_button) + +func _build_buttons(vbox: VBoxContainer) -> void: + var button_hbox = HBoxContainer.new() + button_hbox.alignment = BoxContainer.ALIGNMENT_END + button_hbox.add_theme_constant_override("separation", 8) + vbox.add_child(button_hbox) + + var cancel_button = Button.new() + cancel_button.text = "Cancel" + cancel_button.pressed.connect(_on_cancel_pressed) + button_hbox.add_child(cancel_button) + + var save_button = Button.new() + save_button.text = "Save" + save_button.pressed.connect(_on_create_pressed) + button_hbox.add_child(save_button) + +func _set_default_values() -> void: + weapons_dir_field.text = settings.get("weapons_dir") + items_dir_field.text = settings.get("items_dir") + bullets_dir_field.text = settings.get("bullets_dir") + bullets_3d_dir_field.text = settings.get("bullets_3d_dir") + items_database_field.text = settings.get("items_database_path") + default_bullet_2d_field.text = settings.get("default_bullet_2d") + default_bullet_3d_field.text = settings.get("default_bullet_3d") + +func _apply_prefill_data() -> void: + _set_default_values() + +func _on_create_pressed() -> void: + settings.set("weapons_dir", weapons_dir_field.text) + settings.set("items_dir", items_dir_field.text) + settings.set("bullets_dir", bullets_dir_field.text) + settings.set("bullets_3d_dir", bullets_3d_dir_field.text) + settings.set("items_database_path", items_database_field.text) + settings.set("default_bullet_2d", default_bullet_2d_field.text) + settings.set("default_bullet_3d", default_bullet_3d_field.text) + + _save_dialog_size() + _save_dialog_position() + settings.call("save_settings") + + queue_free() + +func _on_reset_pressed() -> void: + var dialog = ConfirmationDialog.new() + dialog.dialog_text = "This will reset all settings including:\n- Resource paths\n- Default bullets\n- All dialog sizes\n- All dialog positions\n- All viewer filters\n\nAre you sure?" + dialog.title = "Reset All Settings" + add_child(dialog) + + dialog.confirmed.connect(func(): + settings.call("reset_to_defaults") + _set_default_values() + dialog.queue_free() + ) + + dialog.canceled.connect(func(): + dialog.queue_free() + ) + + dialog.popup_centered() + diff --git a/addons/weapon_creator/SettingsDialog.gd.uid b/addons/weapon_creator/SettingsDialog.gd.uid new file mode 100644 index 00000000..ee167da6 --- /dev/null +++ b/addons/weapon_creator/SettingsDialog.gd.uid @@ -0,0 +1 @@ +uid://b8b2oq0r7gois diff --git a/addons/weapon_creator/WeaponCreatorDialog.gd b/addons/weapon_creator/WeaponCreatorDialog.gd index 7064cfd9..dda4b1f0 100644 --- a/addons/weapon_creator/WeaponCreatorDialog.gd +++ b/addons/weapon_creator/WeaponCreatorDialog.gd @@ -1,5 +1,5 @@ @tool -extends Window +extends BaseCreatorDialog # Popup window for configuring weapon parameters before creation # Displays all input fields and creation/cancel buttons @@ -17,7 +17,6 @@ var description_field: TextEdit var sprite_resource: Texture2D var sprite_picker: EditorResourcePicker var sprite_preview: TextureRect -var editor_interface: EditorInterface # Bullet selection fields var bullet_dropdown: OptionButton @@ -36,109 +35,78 @@ var bullets_per_shot_field: SpinBox var spread_angle_field: SpinBox var random_spread_field: SpinBox -# Buttons -var create_button: Button -var cancel_button: Button - -# Mode and prefill data +# Mode var is_3d_mode: bool = true -var prefill_data: Dictionary = {} -var _ui_built: bool = false +var _setup_called: bool = false -func setup(editor_iface: EditorInterface, is_3d: bool = true, prefill: Dictionary = {}) -> void: - editor_interface = editor_iface +# Public method with custom signature - calls base setup internally +func setup_weapon(editor_iface: EditorInterface, is_3d: bool = true, prefill: Dictionary = {}) -> void: 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 + ")" + _setup_called = true + # Call parent setup with standard signature + setup(editor_iface, prefill) # 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 _custom_setup() -> void: + # Called by parent setup() - use for any additional initialization + pass func _ready() -> void: - # Window configuration - 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) + # Wait for setup to be called if it hasn't been yet + if not _setup_called: + await get_tree().process_frame + super._ready() + +func _configure_window() -> void: + size = settings.get("weapon_dialog_size") if settings else Vector2i(750, 950) transient = false exclusive = false unresizable = false - - # Connect close request (X button) to cancel action - close_requested.connect(_on_cancel_pressed) - - # Center on screen - position = (DisplayServer.screen_get_size() - size) / 2 - - _build_ui() - _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 - 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) - - # Main vbox - var vbox = VBoxContainer.new() - vbox.add_theme_constant_override("separation", 8) - margin.add_child(vbox) - - # Scroll container for form - 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) - +func _update_title() -> 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 + " Weapon (" + mode_text + ")" + +func _get_saved_position() -> Vector2i: + if settings: + var pos = settings.get("weapon_dialog_position") + return pos if pos else Vector2i.ZERO + return Vector2i.ZERO + +func _save_dialog_size() -> void: + if settings: + settings.set("weapon_dialog_size", size) + settings.call("save_settings") + +func _save_dialog_position() -> void: + if settings: + settings.set("weapon_dialog_position", position) + settings.call("save_settings") + +func _build_content(container: VBoxContainer) -> void: # Basic Info Section - _build_basic_info_section(main_vbox) + _build_basic_info_section(container) # Stats Section - _build_stats_section(main_vbox) - - vbox.add_child(HSeparator.new()) - - # Buttons at bottom + _build_stats_section(container) + +func _build_buttons(vbox: VBoxContainer) -> void: 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() + var 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() + var create_button = Button.new() create_button.text = "Create Weapon" create_button.custom_minimum_size = Vector2(150, 0) create_button.pressed.connect(_on_create_pressed) @@ -369,7 +337,7 @@ func _set_default_values() -> void: description_field.text = "A new weapon for testing" # 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" + var default_bullet = settings.get("default_bullet_3d") if is_3d_mode else settings.get("default_bullet_2d") _select_bullet_by_path(default_bullet) # Weapon stats defaults @@ -387,9 +355,7 @@ func _set_default_values() -> void: func _populate_bullet_dropdown() -> void: # Get all bullet resources from the appropriate Bullets folder - var bullets_dir = "res://Resources/Bullets/" - if is_3d_mode: - bullets_dir += "3D/" + var bullets_dir = settings.get("bullets_3d_dir") if is_3d_mode else settings.get("bullets_dir") var dir = DirAccess.open(bullets_dir) @@ -470,6 +436,9 @@ func _on_create_pressed() -> void: "random_spread": random_spread_field.value } + # Save dialog size + _save_dialog_size() + # Emit signal with weapon data weapon_data_confirmed.emit(weapon_data) @@ -478,6 +447,8 @@ func _on_create_pressed() -> void: queue_free() func _on_cancel_pressed() -> void: + # Save dialog size before closing + _save_dialog_size() # Close without creating hide() queue_free() @@ -500,8 +471,8 @@ func _validate_inputs() -> bool: # 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" + var weapon_resource_path: String = settings.get("weapons_dir") + key + dimension_suffix + ".tres" + var item_resource_path: String = settings.get("items_dir") + 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.") diff --git a/addons/weapon_creator/WeaponCreatorDock.gd b/addons/weapon_creator/WeaponCreatorDock.gd index b3af2c88..8f707f83 100644 --- a/addons/weapon_creator/WeaponCreatorDock.gd +++ b/addons/weapon_creator/WeaponCreatorDock.gd @@ -4,12 +4,16 @@ extends PanelContainer # UI dock for the Weapon Creator plugin # Provides tabs for weapons and bullets, with creation dialogs and logs +const WeaponCreatorSettings = preload("res://addons/weapon_creator/WeaponCreatorSettings.gd") +const SETTINGS_PATH = "user://weapon_creator_settings.tres" + signal create_weapon_requested(weapon_data: Dictionary) signal create_bullet_requested(bullet_data: Dictionary) signal create_item_requested(item_data: Dictionary) # UI elements var clear_button: Button +var options_button: Button var log_output: RichTextLabel var weapon_viewer: PanelContainer var bullet_viewer: PanelContainer @@ -18,11 +22,22 @@ var tab_container: TabContainer var _is_creating: bool = false var _editor_interface: EditorInterface +var settings: Resource func setup(editor_interface: EditorInterface) -> void: _editor_interface = editor_interface + + if ResourceLoader.exists(SETTINGS_PATH): + settings = ResourceLoader.load(SETTINGS_PATH) + if not settings: + settings = WeaponCreatorSettings.new() func _ready() -> void: + if not settings: + if ResourceLoader.exists(SETTINGS_PATH): + settings = ResourceLoader.load(SETTINGS_PATH) + if not settings: + settings = WeaponCreatorSettings.new() _build_ui() func _build_ui() -> void: @@ -50,11 +65,33 @@ func _build_ui() -> void: log_vbox.add_theme_constant_override("separation", 4) hsplit.add_child(log_vbox) - # Tab Container for Weapons and Bullets (right side) + # Right side container with tabs and options button + var right_vbox = VBoxContainer.new() + right_vbox.custom_minimum_size = Vector2(300, 0) + right_vbox.size_flags_horizontal = Control.SIZE_EXPAND_FILL + right_vbox.add_theme_constant_override("separation", 4) + hsplit.add_child(right_vbox) + + # Header row with options button aligned to right + var header_hbox = HBoxContainer.new() + header_hbox.size_flags_horizontal = Control.SIZE_EXPAND_FILL + right_vbox.add_child(header_hbox) + + # Spacer to push options button to the right + var spacer = Control.new() + spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL + header_hbox.add_child(spacer) + + options_button = Button.new() + options_button.text = "⚙ Settings" + options_button.pressed.connect(_on_options_pressed) + header_hbox.add_child(options_button) + + # Tab Container for Weapons and Bullets 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) + tab_container.size_flags_vertical = Control.SIZE_EXPAND_FILL + right_vbox.add_child(tab_container) # Weapon Viewer Tab var weapon_viewer_script = load("res://addons/weapon_creator/WeaponViewer.gd") @@ -131,6 +168,29 @@ func _on_weapon_data_confirmed(weapon_data: Dictionary) -> void: func _on_clear_button_pressed() -> void: log_output.clear() +func _on_options_pressed() -> void: + var settings_dialog_script = load("res://addons/weapon_creator/SettingsDialog.gd") + var settings_dialog = Window.new() + settings_dialog.set_script(settings_dialog_script) + settings_dialog.setup(_editor_interface) + add_child(settings_dialog) + settings_dialog.popup_centered() + + # Reload settings after dialog closes + settings_dialog.tree_exited.connect(func(): + if ResourceLoader.exists(SETTINGS_PATH): + settings = ResourceLoader.load(SETTINGS_PATH) + if not settings: + settings = WeaponCreatorSettings.new() + # Refresh all viewers to pick up new settings + if weapon_viewer: + weapon_viewer.call("refresh_weapons") + if bullet_viewer: + bullet_viewer.call("refresh_bullets") + if item_viewer: + item_viewer.call("refresh_items") + ) + func _on_weapon_deleted(weapon_name: String, weapon_path: String, item_path: String) -> void: pass diff --git a/addons/weapon_creator/WeaponCreatorPlugin.gd b/addons/weapon_creator/WeaponCreatorPlugin.gd index b4aef068..ebed119c 100644 --- a/addons/weapon_creator/WeaponCreatorPlugin.gd +++ b/addons/weapon_creator/WeaponCreatorPlugin.gd @@ -4,6 +4,9 @@ extends EditorPlugin # Editor plugin that provides a UI for creating 3D weapons with all required resources # Adds a dock panel to the editor with input fields and a create button +const WeaponCreatorSettings = preload("res://addons/weapon_creator/WeaponCreatorSettings.gd") +const SETTINGS_PATH = "user://weapon_creator_settings.tres" + var dock_instance: PanelContainer func _enter_tree() -> void: @@ -33,6 +36,12 @@ func _exit_tree() -> void: dock_instance.queue_free() func _on_create_weapon_requested(weapon_data: Dictionary) -> void: + var settings: Resource = null + if ResourceLoader.exists(SETTINGS_PATH): + settings = ResourceLoader.load(SETTINGS_PATH) + if not settings: + settings = WeaponCreatorSettings.new() + # Extract data from the dictionary var weapon_name: String = weapon_data.get("weapon_name", "New Weapon") var weapon_item_key: String = weapon_data.get("weapon_item_key", "NEW_WEAPON") @@ -40,7 +49,7 @@ 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/3D/icicle_repeater_bullets_3D.tres") + var default_bullet_path: String = weapon_data.get("default_bullet_path", settings.get("default_bullet_3d")) var is_3d: bool = weapon_data.get("is_3d", true) # Weapon stats @@ -58,9 +67,9 @@ func _on_create_weapon_requested(weapon_data: Dictionary) -> void: # 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" + var weapon_resource_path: String = settings.get("weapons_dir") + weapon_item_key + dimension_suffix + ".tres" + var item_resource_path: String = settings.get("items_dir") + weapon_item_key + "_Item" + dimension_suffix + ".tres" + var items_database_path: String = settings.get("items_database_path") # Feedback to the UI dock_instance.call("add_log", "=== Starting Weapon Creation ===") @@ -255,12 +264,16 @@ func _add_to_items_database(database_path: String, item_resource_path: String) - return true func _on_create_bullet_requested(bullet_data: Dictionary) -> void: + var settings: Resource = null + if ResourceLoader.exists(SETTINGS_PATH): + settings = ResourceLoader.load(SETTINGS_PATH) + if not settings: + settings = WeaponCreatorSettings.new() + 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 bullets_dir = settings.get("bullets_3d_dir") if is_3d else settings.get("bullets_dir") var bullet_path = bullets_dir + bullet_name + ".tres" @@ -284,6 +297,12 @@ func _on_create_bullet_requested(bullet_data: Dictionary) -> void: get_editor_interface().get_resource_filesystem().scan() func _on_create_item_requested(item_data: Dictionary) -> void: + var settings: Resource = null + if ResourceLoader.exists(SETTINGS_PATH): + settings = ResourceLoader.load(SETTINGS_PATH) + if not settings: + settings = WeaponCreatorSettings.new() + var item_name: String = item_data.get("item_name", "New Item") var item_key: String = item_data.get("item_key", "NEW_ITEM") var short_name: String = item_data.get("short_name", "NI") @@ -298,8 +317,8 @@ func _on_create_item_requested(item_data: Dictionary) -> void: var consume_on_use: bool = item_data.get("consume_on_use", false) var selectable: bool = item_data.get("selectable", false) - var item_resource_path: String = "res://Resources/Items/" + item_key + "_Item.tres" - var items_database_path: String = "res://Resources/ItemsDatabase.tres" + var item_resource_path: String = settings.get("items_dir") + item_key + "_Item.tres" + var items_database_path: String = settings.get("items_database_path") dock_instance.call("add_log", "=== Starting Item Creation ===") dock_instance.call("add_log", "Item Name: " + item_name) @@ -409,4 +428,3 @@ func _create_bullet_resource(path: String, bullet_data: Dictionary) -> bool: return false return true - diff --git a/addons/weapon_creator/WeaponCreatorSettings.gd b/addons/weapon_creator/WeaponCreatorSettings.gd new file mode 100644 index 00000000..1eb18dd2 --- /dev/null +++ b/addons/weapon_creator/WeaponCreatorSettings.gd @@ -0,0 +1,74 @@ +@tool +class_name WeaponCreatorSettings +extends Resource + +# Settings manager for the Weapon Creator plugin +# Stores configurable defaults and persists user preferences + +const SETTINGS_PATH = "user://weapon_creator_settings.tres" + +# Resource paths +@export var weapons_dir: String = "res://Resources/Weapons/" +@export var items_dir: String = "res://Resources/Items/" +@export var bullets_dir: String = "res://Resources/Bullets/" +@export var bullets_3d_dir: String = "res://Resources/Bullets/3D/" +@export var items_database_path: String = "res://Resources/ItemsDatabase.tres" + +# Default bullet paths +@export var default_bullet_2d: String = "res://Resources/Bullets/simple_ice_bullet.tres" +@export var default_bullet_3d: String = "res://Resources/Bullets/3D/icicle_repeater_bullets_3D.tres" + +# Dialog sizes (saved automatically) +@export var weapon_dialog_size: Vector2i = Vector2i(750, 950) +@export var bullet_dialog_size: Vector2i = Vector2i(750, 850) +@export var item_dialog_size: Vector2i = Vector2i(750, 850) +@export var settings_dialog_size: Vector2i = Vector2i(600, 600) + +# Dialog positions (saved automatically) +@export var weapon_dialog_position: Vector2i = Vector2i.ZERO +@export var bullet_dialog_position: Vector2i = Vector2i.ZERO +@export var item_dialog_position: Vector2i = Vector2i.ZERO +@export var settings_dialog_position: Vector2i = Vector2i.ZERO + +# Viewer filters (saved automatically) +@export var weapon_viewer_filter: String = "" +@export var bullet_viewer_filter: String = "" +@export var item_viewer_filter: String = "" + +static func load_settings(): + if ResourceLoader.exists(SETTINGS_PATH): + var loaded = ResourceLoader.load(SETTINGS_PATH) + if loaded != null: + return loaded + var new_settings = WeaponCreatorSettings.new() + return new_settings + +func save_settings() -> void: + var err = ResourceSaver.save(self, SETTINGS_PATH) + if err != OK: + push_error("Failed to save weapon creator settings: " + str(err)) + +func reset_to_defaults() -> void: + weapons_dir = "res://Resources/Weapons/" + items_dir = "res://Resources/Items/" + bullets_dir = "res://Resources/Bullets/" + bullets_3d_dir = "res://Resources/Bullets/3D/" + items_database_path = "res://Resources/ItemsDatabase.tres" + default_bullet_2d = "res://Resources/Bullets/simple_ice_bullet.tres" + default_bullet_3d = "res://Resources/Bullets/3D/icicle_repeater_bullets_3D.tres" + weapon_dialog_size = Vector2i(750, 950) + bullet_dialog_size = Vector2i(750, 850) + item_dialog_size = Vector2i(750, 850) + settings_dialog_size = Vector2i(600, 600) + weapon_dialog_position = Vector2i.ZERO + bullet_dialog_position = Vector2i.ZERO + item_dialog_position = Vector2i.ZERO + settings_dialog_position = Vector2i.ZERO + weapon_viewer_filter = "" + bullet_viewer_filter = "" + item_viewer_filter = "" + save_settings() + + + + diff --git a/addons/weapon_creator/WeaponCreatorSettings.gd.uid b/addons/weapon_creator/WeaponCreatorSettings.gd.uid new file mode 100644 index 00000000..0c73aad3 --- /dev/null +++ b/addons/weapon_creator/WeaponCreatorSettings.gd.uid @@ -0,0 +1 @@ +uid://c7heaooyuukr8 diff --git a/addons/weapon_creator/WeaponViewer.gd b/addons/weapon_creator/WeaponViewer.gd index ee7d7ac9..bd0e74d2 100644 --- a/addons/weapon_creator/WeaponViewer.gd +++ b/addons/weapon_creator/WeaponViewer.gd @@ -1,4 +1,4 @@ -@tool +@tool extends PanelContainer # Displays all weapons from the ItemsDatabase in a grid format @@ -385,7 +385,7 @@ func _duplicate_weapon(loot_item: Resource, weapon_data: Resource, is_3d: bool) 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) + dialog.call_deferred("setup_weapon", _editor_interface, is_3d, prefill_data) # Connect to confirmation signal using call_deferred dialog.call_deferred("connect", "weapon_data_confirmed", _on_duplicate_weapon_confirmed) @@ -521,7 +521,6 @@ func _on_create_weapon_pressed(is_3d: bool) -> void: get_tree().root.add_child(dialog) - dialog.call_deferred("setup", _editor_interface, is_3d) + dialog.call_deferred("setup_weapon", _editor_interface, is_3d) dialog.call_deferred("connect", "weapon_data_confirmed", _on_duplicate_weapon_confirmed) dialog.call_deferred("popup_centered") -