@tool extends Window # Popup window for configuring weapon parameters before creation # Displays all input fields and creation/cancel buttons signal weapon_data_confirmed(weapon_data: Dictionary) # Basic info fields var weapon_name_field: LineEdit var item_key_field: LineEdit var ammo_key_field: LineEdit var short_name_field: LineEdit var description_field: TextEdit var bullet_dropdown: OptionButton var bullet_path_field: LineEdit # Weapon stats fields var priority_field: SpinBox var ammo_per_shot_field: SpinBox var rate_of_fire_field: SpinBox var bullet_capacity_field: SpinBox var reload_time_field: SpinBox var infinite_ammo_check: CheckBox var recharge_time_field: SpinBox var recharge_amount_field: SpinBox 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 func _ready() -> void: # Window configuration title = "Create New Weapon" size = Vector2i(600, 700) transient = true exclusive = true popup_window = true # Center on screen position = (DisplayServer.screen_get_size() - size) / 2 _build_ui() _set_default_values() 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) # Basic Info Section _build_basic_info_section(main_vbox) # Stats Section _build_stats_section(main_vbox) vbox.add_child(HSeparator.new()) # Buttons at bottom 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 Weapon" create_button.custom_minimum_size = Vector2(150, 0) create_button.pressed.connect(_on_create_pressed) button_hbox.add_child(create_button) func _build_basic_info_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) # Weapon Name weapon_name_field = _add_line_edit_field(section, "Weapon Name:", "e.g., Ice Rifle") weapon_name_field.text_changed.connect(_on_weapon_name_changed) # Item Key item_key_field = _add_line_edit_field(section, "Item Key:", "AUTO_GENERATED") # Ammo Key ammo_key_field = _add_line_edit_field(section, "Ammo Key:", "(optional, leave empty for none)") # Short Name short_name_field = _add_line_edit_field(section, "Short Name:", "e.g., IR-7") # Description var desc_label = Label.new() desc_label.text = "Description:" section.add_child(desc_label) description_field = TextEdit.new() description_field.custom_minimum_size = Vector2(0, 60) description_field.placeholder_text = "Enter weapon description..." section.add_child(description_field) section.add_child(HSeparator.new()) # Bullet Selection var bullet_header = Label.new() bullet_header.text = "Bullet Configuration" bullet_header.add_theme_font_size_override("font_size", 14) section.add_child(bullet_header) # Bullet dropdown var bullet_hbox = HBoxContainer.new() section.add_child(bullet_hbox) var bullet_label = Label.new() bullet_label.text = "Bullet Type:" bullet_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL bullet_hbox.add_child(bullet_label) bullet_dropdown = OptionButton.new() bullet_dropdown.size_flags_horizontal = Control.SIZE_EXPAND_FILL bullet_dropdown.item_selected.connect(_on_bullet_selected) bullet_hbox.add_child(bullet_dropdown) # Populate bullet dropdown _populate_bullet_dropdown() # Bullet Path (read-only display) bullet_path_field = _add_line_edit_field(section, "Bullet Path:", "") bullet_path_field.editable = false bullet_path_field.text = "res://Resources/Bullets/simple_ice_bullet.tres" 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 = "Weapon 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) # Create all stat fields priority_field = _add_spinbox_field(grid, "Priority:", 0, 100, 1, 1) ammo_per_shot_field = _add_spinbox_field(grid, "Ammo Per Shot:", 0, 1000, 1, 1) rate_of_fire_field = _add_spinbox_field(grid, "Rate of Fire (s):", 0, 100, 0.01, 0.2) bullet_capacity_field = _add_spinbox_field(grid, "Bullet Capacity:", 0, 10000, 1, 100) reload_time_field = _add_spinbox_field(grid, "Reload Time (s):", 0, 100, 0.1, 2.0) # Infinite ammo checkbox var inf_label = Label.new() inf_label.text = "Infinite Ammo:" grid.add_child(inf_label) infinite_ammo_check = CheckBox.new() grid.add_child(infinite_ammo_check) recharge_time_field = _add_spinbox_field(grid, "Recharge Time (s):", 0, 100, 0.1, 0) recharge_amount_field = _add_spinbox_field(grid, "Recharge Amount:", 0, 1000, 1, 0) bullets_per_shot_field = _add_spinbox_field(grid, "Bullets Per Shot:", 1, 100, 1, 1) spread_angle_field = _add_spinbox_field(grid, "Spread Angle (°):", 0, 360, 0.1, 0) random_spread_field = _add_spinbox_field(grid, "Random Spread (°):", 0, 360, 0.1, 0) 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: # Basic info defaults weapon_name_field.text = "New Weapon" item_key_field.text = "NEW_WEAPON" ammo_key_field.text = "" 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") # Weapon stats defaults priority_field.value = 10 ammo_per_shot_field.value = 1 rate_of_fire_field.value = 0.2 bullet_capacity_field.value = 50 reload_time_field.value = 1.0 infinite_ammo_check.button_pressed = false recharge_time_field.value = 0.5 recharge_amount_field.value = 5 bullets_per_shot_field.value = 1 spread_angle_field.value = 0.0 random_spread_field.value = 0.0 func _populate_bullet_dropdown() -> void: # Get all bullet resources from the Bullets folder var bullets_dir = "res://Resources/Bullets/" var dir = DirAccess.open(bullets_dir) if dir == null: push_error("Could not open bullets directory: " + bullets_dir) return dir.list_dir_begin() var file_name = dir.get_next() var bullet_files: Array[String] = [] while file_name != "": if not dir.current_is_dir() and file_name.ends_with(".tres"): bullet_files.append(bullets_dir + file_name) file_name = dir.get_next() dir.list_dir_end() # Sort alphabetically bullet_files.sort() # Add to dropdown for bullet_path in bullet_files: var bullet_name = bullet_path.get_file().get_basename() bullet_dropdown.add_item(bullet_name) bullet_dropdown.set_item_metadata(bullet_dropdown.item_count - 1, bullet_path) func _select_bullet_by_path(path: String) -> void: # Find and select the bullet in the dropdown for i in range(bullet_dropdown.item_count): if bullet_dropdown.get_item_metadata(i) == path: bullet_dropdown.select(i) bullet_path_field.text = path return # If not found, select first item if bullet_dropdown.item_count > 0: bullet_dropdown.select(0) bullet_path_field.text = bullet_dropdown.get_item_metadata(0) func _on_bullet_selected(index: int) -> void: # Update the bullet path field when dropdown selection changes var selected_path = bullet_dropdown.get_item_metadata(index) bullet_path_field.text = selected_path func _on_weapon_name_changed(new_name: String) -> void: # Auto-generate item key from weapon name var generated_key = new_name.to_upper().replace(" ", "_") generated_key = generated_key.replace("-", "_").replace("'", "").replace("\"", "") item_key_field.text = generated_key func _on_create_pressed() -> void: # Validate inputs if not _validate_inputs(): return # Build weapon data dictionary var weapon_data = { "weapon_name": weapon_name_field.text, "weapon_item_key": item_key_field.text, "ammo_key": ammo_key_field.text, "weapon_short_name": short_name_field.text, "weapon_description": description_field.text, "default_bullet_path": bullet_path_field.text, "priority": int(priority_field.value), "ammo_per_shot": int(ammo_per_shot_field.value), "rate_of_fire": rate_of_fire_field.value, "bullet_capacity": int(bullet_capacity_field.value), "reload_time": reload_time_field.value, "infinite_ammo": infinite_ammo_check.button_pressed, "recharge_time": recharge_time_field.value, "recharge_amount": int(recharge_amount_field.value), "bullets_per_shot": int(bullets_per_shot_field.value), "spread_angle": spread_angle_field.value, "random_spread": random_spread_field.value } # Emit signal with weapon data weapon_data_confirmed.emit(weapon_data) # Close the window hide() queue_free() func _on_cancel_pressed() -> void: # Close without creating hide() queue_free() func _validate_inputs() -> bool: # Check weapon name if weapon_name_field.text.strip_edges().is_empty(): _show_error("Weapon name cannot be empty") return false # Check item key if item_key_field.text.strip_edges().is_empty(): _show_error("Item key cannot be empty") return false # Check item key format var key = item_key_field.text if not key.to_upper() == key: _show_warning("Item key should be uppercase") # Check bullet path if not ResourceLoader.exists(bullet_path_field.text): _show_error("Bullet resource does not exist at: " + bullet_path_field.text) 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() func _show_warning(message: String) -> void: var dialog = AcceptDialog.new() dialog.dialog_text = message dialog.title = "Warning" add_child(dialog) dialog.popup_centered()