mirror of
https://gitlab.com/MaddoScientisto/cirnogodot.git
synced 2026-06-01 08:45:33 +00:00
Add sprite selection and weapon viewer to weapon creation plugin
This commit is contained in:
parent
b4c38b159e
commit
04cb3da0cd
8 changed files with 327 additions and 10 deletions
|
|
@ -12,6 +12,14 @@ var item_key_field: LineEdit
|
|||
var ammo_key_field: LineEdit
|
||||
var short_name_field: LineEdit
|
||||
var description_field: TextEdit
|
||||
|
||||
# Sprite selection fields
|
||||
var sprite_resource: Texture2D
|
||||
var sprite_picker: EditorResourcePicker
|
||||
var sprite_preview: TextureRect
|
||||
var editor_interface: EditorInterface
|
||||
|
||||
# Bullet selection fields
|
||||
var bullet_dropdown: OptionButton
|
||||
var bullet_path_field: LineEdit
|
||||
|
||||
|
|
@ -32,13 +40,21 @@ var random_spread_field: SpinBox
|
|||
var create_button: Button
|
||||
var cancel_button: Button
|
||||
|
||||
func setup(editor_iface: EditorInterface) -> void:
|
||||
editor_interface = editor_iface
|
||||
# If sprite picker already exists, ensure it has editor context
|
||||
if sprite_picker:
|
||||
_setup_sprite_picker()
|
||||
|
||||
func _ready() -> void:
|
||||
# Window configuration
|
||||
title = "Create New Weapon"
|
||||
size = Vector2i(600, 700)
|
||||
size = Vector2i(750, 950)
|
||||
transient = true
|
||||
exclusive = true
|
||||
popup_window = true
|
||||
|
||||
# Connect close request (X button) to cancel action
|
||||
close_requested.connect(_on_cancel_pressed)
|
||||
|
||||
# Center on screen
|
||||
position = (DisplayServer.screen_get_size() - size) / 2
|
||||
|
|
@ -133,6 +149,16 @@ func _build_basic_info_section(parent: Control) -> void:
|
|||
|
||||
section.add_child(HSeparator.new())
|
||||
|
||||
# Sprite Selection
|
||||
var sprite_header = Label.new()
|
||||
sprite_header.text = "Item Sprite"
|
||||
sprite_header.add_theme_font_size_override("font_size", 14)
|
||||
section.add_child(sprite_header)
|
||||
|
||||
_build_sprite_selector(section)
|
||||
|
||||
section.add_child(HSeparator.new())
|
||||
|
||||
# Bullet Selection
|
||||
var bullet_header = Label.new()
|
||||
bullet_header.text = "Bullet Configuration"
|
||||
|
|
@ -161,6 +187,70 @@ func _build_basic_info_section(parent: Control) -> void:
|
|||
bullet_path_field.editable = false
|
||||
bullet_path_field.text = "res://Resources/Bullets/simple_ice_bullet.tres"
|
||||
|
||||
func _build_sprite_selector(parent: Control) -> void:
|
||||
# Container for sprite selector with preview
|
||||
var sprite_container = HBoxContainer.new()
|
||||
sprite_container.add_theme_constant_override("separation", 8)
|
||||
parent.add_child(sprite_container)
|
||||
|
||||
# Left side: Label and resource picker
|
||||
var left_vbox = VBoxContainer.new()
|
||||
left_vbox.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
left_vbox.add_theme_constant_override("separation", 4)
|
||||
sprite_container.add_child(left_vbox)
|
||||
|
||||
# Resource picker
|
||||
var picker_hbox = HBoxContainer.new()
|
||||
picker_hbox.add_theme_constant_override("separation", 4)
|
||||
left_vbox.add_child(picker_hbox)
|
||||
|
||||
var picker_label = Label.new()
|
||||
picker_label.text = "Sprite:"
|
||||
picker_label.custom_minimum_size = Vector2(120, 0)
|
||||
picker_hbox.add_child(picker_label)
|
||||
|
||||
sprite_picker = EditorResourcePicker.new()
|
||||
sprite_picker.base_type = "Texture2D"
|
||||
sprite_picker.editable = true
|
||||
sprite_picker.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
sprite_picker.resource_changed.connect(_on_sprite_resource_changed)
|
||||
picker_hbox.add_child(sprite_picker)
|
||||
|
||||
# Setup editor integration if interface is available
|
||||
if editor_interface:
|
||||
_setup_sprite_picker()
|
||||
|
||||
# Right side: Preview
|
||||
var preview_container = PanelContainer.new()
|
||||
preview_container.custom_minimum_size = Vector2(64, 64)
|
||||
sprite_container.add_child(preview_container)
|
||||
|
||||
sprite_preview = TextureRect.new()
|
||||
sprite_preview.custom_minimum_size = Vector2(64, 64)
|
||||
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 _on_sprite_resource_changed(resource: Resource) -> void:
|
||||
# Update sprite resource and preview when changed via EditorResourcePicker
|
||||
if resource is Texture2D:
|
||||
sprite_resource = resource
|
||||
sprite_preview.texture = resource
|
||||
print("Sprite resource changed: ", resource.resource_path if resource.resource_path else "[Unsaved Resource]")
|
||||
elif resource == null:
|
||||
sprite_resource = null
|
||||
sprite_preview.texture = null
|
||||
print("Sprite resource cleared")
|
||||
else:
|
||||
push_warning("Selected resource is not a Texture2D")
|
||||
|
||||
func _setup_sprite_picker() -> void:
|
||||
# Ensure sprite picker is properly integrated with editor
|
||||
if sprite_picker and editor_interface:
|
||||
# EditorResourcePicker automatically integrates with editor when in editor context
|
||||
# Just ensure toggle_mode is set correctly
|
||||
sprite_picker.toggle_mode = false
|
||||
|
||||
func _build_stats_section(parent: Control) -> void:
|
||||
var section = VBoxContainer.new()
|
||||
section.add_theme_constant_override("separation", 4)
|
||||
|
|
@ -299,6 +389,7 @@ func _on_bullet_selected(index: int) -> void:
|
|||
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(" ", "_")
|
||||
|
|
@ -317,6 +408,7 @@ func _on_create_pressed() -> void:
|
|||
"ammo_key": ammo_key_field.text,
|
||||
"weapon_short_name": short_name_field.text,
|
||||
"weapon_description": description_field.text,
|
||||
"sprite_resource": sprite_resource,
|
||||
"default_bullet_path": bullet_path_field.text,
|
||||
"priority": int(priority_field.value),
|
||||
"ammo_per_shot": int(ammo_per_shot_field.value),
|
||||
|
|
|
|||
1
addons/weapon_creator/WeaponCreatorDialog.gd.uid
Normal file
1
addons/weapon_creator/WeaponCreatorDialog.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://byyekqx5glmun
|
||||
|
|
@ -10,8 +10,13 @@ signal create_weapon_requested(weapon_data: Dictionary)
|
|||
var open_dialog_button: Button
|
||||
var clear_button: Button
|
||||
var log_output: RichTextLabel
|
||||
var weapon_viewer: PanelContainer
|
||||
|
||||
var _is_creating: bool = false
|
||||
var _editor_interface: EditorInterface
|
||||
|
||||
func setup(editor_interface: EditorInterface) -> void:
|
||||
_editor_interface = editor_interface
|
||||
|
||||
func _ready() -> void:
|
||||
_build_ui()
|
||||
|
|
@ -57,14 +62,34 @@ func _build_ui() -> void:
|
|||
|
||||
vbox.add_child(HSeparator.new())
|
||||
|
||||
# Log section
|
||||
# Horizontal split container for weapon viewer and log
|
||||
var hsplit = HSplitContainer.new()
|
||||
hsplit.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
vbox.add_child(hsplit)
|
||||
|
||||
# Log section (left side)
|
||||
var log_vbox = VBoxContainer.new()
|
||||
log_vbox.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
log_vbox.add_theme_constant_override("separation", 4)
|
||||
hsplit.add_child(log_vbox)
|
||||
|
||||
# Weapon Viewer (right side)
|
||||
var weapon_viewer_script = load("res://addons/weapon_creator/WeaponViewer.gd")
|
||||
weapon_viewer = PanelContainer.new()
|
||||
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)
|
||||
hsplit.add_child(weapon_viewer)
|
||||
|
||||
var log_label = Label.new()
|
||||
log_label.text = "Output Log:"
|
||||
vbox.add_child(log_label)
|
||||
log_vbox.add_child(log_label)
|
||||
|
||||
var log_scroll = ScrollContainer.new()
|
||||
log_scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
vbox.add_child(log_scroll)
|
||||
log_vbox.add_child(log_scroll)
|
||||
|
||||
log_output = RichTextLabel.new()
|
||||
log_output.bbcode_enabled = true
|
||||
|
|
@ -79,6 +104,10 @@ func _on_open_dialog_pressed() -> void:
|
|||
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)
|
||||
|
||||
|
|
@ -109,3 +138,7 @@ func set_creation_complete() -> void:
|
|||
# Re-enable the create button after creation is complete
|
||||
_is_creating = false
|
||||
open_dialog_button.disabled = false
|
||||
|
||||
# Refresh weapon viewer to show newly created weapon
|
||||
if weapon_viewer:
|
||||
weapon_viewer.call("refresh_weapons")
|
||||
|
|
|
|||
1
addons/weapon_creator/WeaponCreatorDock.gd.uid
Normal file
1
addons/weapon_creator/WeaponCreatorDock.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://dpt41usbrbnul
|
||||
|
|
@ -12,6 +12,9 @@ func _enter_tree() -> void:
|
|||
dock_instance = PanelContainer.new()
|
||||
dock_instance.set_script(dock_script)
|
||||
|
||||
# Pass editor interface to dock
|
||||
dock_instance.call("setup", get_editor_interface())
|
||||
|
||||
# Connect the create button signal from the dock
|
||||
if dock_instance.has_signal("create_weapon_requested"):
|
||||
dock_instance.create_weapon_requested.connect(_on_create_weapon_requested)
|
||||
|
|
@ -32,6 +35,7 @@ func _on_create_weapon_requested(weapon_data: Dictionary) -> void:
|
|||
var ammo_key: String = weapon_data.get("ammo_key", "")
|
||||
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")
|
||||
|
||||
# Weapon stats
|
||||
|
|
@ -70,7 +74,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):
|
||||
weapon_description, weapon_item_key, weapon_resource_path, sprite_resource):
|
||||
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)
|
||||
|
|
@ -87,9 +91,8 @@ func _on_create_weapon_requested(weapon_data: Dictionary) -> void:
|
|||
|
||||
dock_instance.call("add_log", "=== Weapon Creation Complete ===", Color.CYAN)
|
||||
dock_instance.call("add_log", "Next steps:")
|
||||
dock_instance.call("add_log", "1. Add sounds to the weapon resource")
|
||||
dock_instance.call("add_log", "2. Add a sprite texture to the LootItem")
|
||||
dock_instance.call("add_log", "3. Test the weapon in-game")
|
||||
dock_instance.call("add_log", "1. Add sounds to the weapon resource if needed")
|
||||
dock_instance.call("add_log", "2. Test the weapon in-game")
|
||||
dock_instance.call("set_creation_complete")
|
||||
|
||||
# Refresh the filesystem
|
||||
|
|
@ -151,7 +154,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) -> bool:
|
||||
weapon_resource_path: String, sprite_resource: Texture2D) -> bool:
|
||||
# Check if file already exists
|
||||
if ResourceLoader.exists(path):
|
||||
dock_instance.call("add_log", "Warning: LootItem already exists at " + path, Color.YELLOW)
|
||||
|
|
@ -185,6 +188,12 @@ func _create_loot_item_resource(path: String, item_name: String, short_name: Str
|
|||
loot_item.set("Selectable", true)
|
||||
loot_item.set("DropScenePath3D", "res://Scenes/Items/GenericItem3D.tscn")
|
||||
|
||||
# Set sprite if provided (supports all Texture2D types including AtlasTexture)
|
||||
if sprite_resource != null:
|
||||
loot_item.set("InventorySprite", sprite_resource)
|
||||
var sprite_type = sprite_resource.get_class()
|
||||
dock_instance.call("add_log", "✓ Set inventory sprite (" + sprite_type + ")", Color.GREEN)
|
||||
|
||||
# Save the resource
|
||||
var err = ResourceSaver.save(loot_item, path)
|
||||
if err != OK:
|
||||
|
|
|
|||
1
addons/weapon_creator/WeaponCreatorPlugin.gd.uid
Normal file
1
addons/weapon_creator/WeaponCreatorPlugin.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://dtk437yfdttf
|
||||
179
addons/weapon_creator/WeaponViewer.gd
Normal file
179
addons/weapon_creator/WeaponViewer.gd
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
@tool
|
||||
extends PanelContainer
|
||||
|
||||
# Displays all weapons from the ItemsDatabase in a grid format
|
||||
# Shows sprite and name, with tooltip showing resource path
|
||||
# Clicking opens the weapon resource in the inspector
|
||||
|
||||
signal weapon_selected(weapon_resource_path: String)
|
||||
|
||||
var _editor_interface: EditorInterface
|
||||
var _grid_container: HFlowContainer
|
||||
var _items_database_path := "res://Resources/ItemsDatabase.tres"
|
||||
|
||||
func setup(editor_interface: EditorInterface) -> void:
|
||||
_editor_interface = editor_interface
|
||||
|
||||
func _ready() -> void:
|
||||
_build_ui()
|
||||
refresh_weapons()
|
||||
|
||||
func _build_ui() -> void:
|
||||
# Main margin container
|
||||
var margin = MarginContainer.new()
|
||||
margin.add_theme_constant_override("margin_left", 8)
|
||||
margin.add_theme_constant_override("margin_top", 8)
|
||||
margin.add_theme_constant_override("margin_right", 8)
|
||||
margin.add_theme_constant_override("margin_bottom", 8)
|
||||
add_child(margin)
|
||||
|
||||
# Main vbox
|
||||
var vbox = VBoxContainer.new()
|
||||
vbox.add_theme_constant_override("separation", 8)
|
||||
margin.add_child(vbox)
|
||||
|
||||
# Header with title and refresh button
|
||||
var header_hbox = HBoxContainer.new()
|
||||
vbox.add_child(header_hbox)
|
||||
|
||||
var title = Label.new()
|
||||
title.text = "Weapons"
|
||||
title.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
header_hbox.add_child(title)
|
||||
|
||||
var refresh_button = Button.new()
|
||||
refresh_button.text = "Refresh"
|
||||
refresh_button.pressed.connect(refresh_weapons)
|
||||
header_hbox.add_child(refresh_button)
|
||||
|
||||
vbox.add_child(HSeparator.new())
|
||||
|
||||
# Scroll container for grid
|
||||
var scroll = ScrollContainer.new()
|
||||
scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
|
||||
vbox.add_child(scroll)
|
||||
|
||||
# Flow container for responsive wrapping
|
||||
_grid_container = HFlowContainer.new()
|
||||
_grid_container.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
_grid_container.add_theme_constant_override("h_separation", 8)
|
||||
_grid_container.add_theme_constant_override("v_separation", 8)
|
||||
scroll.add_child(_grid_container)
|
||||
|
||||
func refresh_weapons() -> void:
|
||||
if not _grid_container:
|
||||
return
|
||||
|
||||
# Clear existing items
|
||||
for child in _grid_container.get_children():
|
||||
child.queue_free()
|
||||
|
||||
# Load ItemsDatabase
|
||||
if not ResourceLoader.exists(_items_database_path):
|
||||
_add_error_label("ItemsDatabase not found at: " + _items_database_path)
|
||||
return
|
||||
|
||||
var items_database = load(_items_database_path)
|
||||
if items_database == null:
|
||||
_add_error_label("Failed to load ItemsDatabase")
|
||||
return
|
||||
|
||||
var loot_items = items_database.get("LootItems")
|
||||
if loot_items == null:
|
||||
_add_error_label("No LootItems found in database")
|
||||
return
|
||||
|
||||
# Filter for weapons only (items with WeaponData3D)
|
||||
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)
|
||||
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
|
||||
|
||||
var vbox = VBoxContainer.new()
|
||||
vbox.add_theme_constant_override("separation", 2)
|
||||
vbox.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
|
||||
var sprite: Texture2D = loot_item.get("InventorySprite")
|
||||
|
||||
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
|
||||
|
||||
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)
|
||||
|
||||
var name_label = Label.new()
|
||||
var item_name: String = loot_item.get("ItemName")
|
||||
name_label.text = item_name if item_name else "Unknown"
|
||||
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.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
name_label.add_theme_font_size_override("font_size", 11)
|
||||
name_label.add_theme_constant_override("line_spacing", -2)
|
||||
vbox.add_child(name_label)
|
||||
|
||||
button.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)
|
||||
vbox.add_theme_constant_override("margin_right", 4)
|
||||
vbox.add_theme_constant_override("margin_bottom", 4)
|
||||
|
||||
var weapon_path = weapon_data.resource_path
|
||||
if weapon_path:
|
||||
button.tooltip_text = weapon_path
|
||||
else:
|
||||
button.tooltip_text = "Built-in resource (no file path)"
|
||||
|
||||
button.pressed.connect(_on_weapon_clicked.bind(weapon_data, weapon_path))
|
||||
|
||||
_grid_container.add_child(button)
|
||||
|
||||
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 _add_error_label(error_message: String) -> void:
|
||||
var label = Label.new()
|
||||
label.text = error_message
|
||||
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
label.modulate = Color.ORANGE_RED
|
||||
_grid_container.add_child(label)
|
||||
|
||||
1
addons/weapon_creator/WeaponViewer.gd.uid
Normal file
1
addons/weapon_creator/WeaponViewer.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://b5oxs5f5sfuk2
|
||||
Loading…
Add table
Add a link
Reference in a new issue