cirnogodot/addons/weapon_creator/WeaponCreatorDialog.gd

382 lines
12 KiB
GDScript3
Raw Normal View History

2026-02-08 13:11:14 +01:00
@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()