mirror of
https://gitlab.com/MaddoScientisto/cirnogodot.git
synced 2026-06-01 10:35:34 +00:00
382 lines
12 KiB
GDScript3
382 lines
12 KiB
GDScript3
|
|
@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()
|
|||
|
|
|