mirror of
https://gitlab.com/MaddoScientisto/cirnogodot.git
synced 2026-06-01 10:25:35 +00:00
Add item creation and viewer dialogs with filtering options for 2D/3D items
This commit is contained in:
parent
670a37140f
commit
1b572e82bf
8 changed files with 1236 additions and 0 deletions
156
addons/weapon_creator/BaseCreatorDialog.gd
Normal file
156
addons/weapon_creator/BaseCreatorDialog.gd
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
@tool
|
||||
class_name BaseCreatorDialog
|
||||
extends Window
|
||||
|
||||
# Base class for all creator dialogs
|
||||
# Ensures consistent initialization and UI building patterns
|
||||
|
||||
signal data_confirmed(data: Dictionary)
|
||||
|
||||
var editor_interface: EditorInterface
|
||||
var prefill_data: Dictionary = {}
|
||||
var _ui_built: bool = false
|
||||
|
||||
func setup(editor_iface: EditorInterface, prefill: Dictionary = {}) -> void:
|
||||
editor_interface = editor_iface
|
||||
prefill_data = prefill
|
||||
|
||||
_update_title()
|
||||
|
||||
if _ui_built:
|
||||
_apply_data()
|
||||
|
||||
func _ready() -> void:
|
||||
_configure_window()
|
||||
_update_title()
|
||||
|
||||
close_requested.connect(_on_cancel_pressed)
|
||||
position = (DisplayServer.screen_get_size() - size) / 2
|
||||
|
||||
_build_ui()
|
||||
_ui_built = true
|
||||
|
||||
_apply_data()
|
||||
|
||||
func _configure_window() -> void:
|
||||
size = Vector2i(750, 850)
|
||||
transient = false
|
||||
exclusive = false
|
||||
unresizable = false
|
||||
|
||||
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 _build_content(container: VBoxContainer) -> void:
|
||||
push_warning("_build_content should be overridden in derived class")
|
||||
|
||||
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 create_button = Button.new()
|
||||
create_button.text = "Create"
|
||||
create_button.pressed.connect(_on_create_pressed)
|
||||
button_hbox.add_child(create_button)
|
||||
|
||||
func _apply_data() -> void:
|
||||
if prefill_data.is_empty():
|
||||
_set_default_values()
|
||||
else:
|
||||
_apply_prefill_data()
|
||||
|
||||
func _set_default_values() -> void:
|
||||
pass
|
||||
|
||||
func _apply_prefill_data() -> void:
|
||||
pass
|
||||
|
||||
func _on_cancel_pressed() -> void:
|
||||
queue_free()
|
||||
|
||||
func _on_create_pressed() -> void:
|
||||
push_warning("_on_create_pressed should be overridden in derived class")
|
||||
|
||||
func _show_error(message: String) -> void:
|
||||
var dialog = AcceptDialog.new()
|
||||
dialog.dialog_text = message
|
||||
dialog.title = "Error"
|
||||
add_child(dialog)
|
||||
dialog.popup_centered()
|
||||
dialog.confirmed.connect(func(): dialog.queue_free())
|
||||
|
||||
# Helper methods for creating common UI elements
|
||||
func _create_input(label_text: String) -> Dictionary:
|
||||
var hbox = HBoxContainer.new()
|
||||
|
||||
var label = Label.new()
|
||||
label.text = label_text
|
||||
label.custom_minimum_size = Vector2(120, 0)
|
||||
hbox.add_child(label)
|
||||
|
||||
var edit = LineEdit.new()
|
||||
edit.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
hbox.add_child(edit)
|
||||
|
||||
return {"container": hbox, "edit": edit}
|
||||
|
||||
func _create_spinbox(label_text: String, default_value: float, min_value: float, max_value: float, step: float) -> Dictionary:
|
||||
var hbox = HBoxContainer.new()
|
||||
|
||||
var label = Label.new()
|
||||
label.text = label_text
|
||||
label.custom_minimum_size = Vector2(120, 0)
|
||||
hbox.add_child(label)
|
||||
|
||||
var spinbox = SpinBox.new()
|
||||
spinbox.min_value = min_value
|
||||
spinbox.max_value = max_value
|
||||
spinbox.step = step
|
||||
spinbox.value = default_value
|
||||
spinbox.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
hbox.add_child(spinbox)
|
||||
|
||||
return {"container": hbox, "spinbox": spinbox}
|
||||
|
||||
func _create_section_label(text: String) -> Label:
|
||||
var label = Label.new()
|
||||
label.text = text
|
||||
label.add_theme_font_size_override("font_size", 16)
|
||||
return label
|
||||
1
addons/weapon_creator/BaseCreatorDialog.gd.uid
Normal file
1
addons/weapon_creator/BaseCreatorDialog.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://vom73sy0ykrr
|
||||
396
addons/weapon_creator/ItemCreatorDialog.gd
Normal file
396
addons/weapon_creator/ItemCreatorDialog.gd
Normal file
|
|
@ -0,0 +1,396 @@
|
|||
@tool
|
||||
extends Window
|
||||
|
||||
# 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
|
||||
var _short_name_edit: LineEdit
|
||||
var _description_edit: TextEdit
|
||||
var _sprite_picker: EditorResourcePicker
|
||||
var _sprite_preview: TextureRect
|
||||
var _selected_sprite: Texture2D = null
|
||||
|
||||
var _item_type_option: OptionButton
|
||||
var _tier_spin: SpinBox
|
||||
var _price_spin: SpinBox
|
||||
var _amount_spin: SpinBox
|
||||
var _max_spin: SpinBox
|
||||
var _pickup_if_maxed_check: CheckBox
|
||||
var _consume_on_use_check: CheckBox
|
||||
var _selectable_check: CheckBox
|
||||
|
||||
const ITEM_TYPE_NAMES = [
|
||||
"KeycardRed",
|
||||
"KeycardBlue",
|
||||
"KeycardGreen",
|
||||
"Ammo",
|
||||
"Medkit",
|
||||
"FrogBomb",
|
||||
"Bomb",
|
||||
"Mine",
|
||||
"Battery",
|
||||
"Weapon",
|
||||
"Power",
|
||||
"Points",
|
||||
"Credits",
|
||||
"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)
|
||||
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 _build_buttons(parent: VBoxContainer) -> void:
|
||||
var button_hbox = HBoxContainer.new()
|
||||
button_hbox.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||
button_hbox.add_theme_constant_override("separation", 8)
|
||||
parent.add_child(button_hbox)
|
||||
|
||||
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)
|
||||
|
||||
var create_button = Button.new()
|
||||
create_button.text = "Create"
|
||||
create_button.custom_minimum_size = Vector2(100, 0)
|
||||
create_button.pressed.connect(_on_create_pressed)
|
||||
button_hbox.add_child(create_button)
|
||||
|
||||
func _build_content(content_vbox: VBoxContainer) -> void:
|
||||
# Basic Info Section
|
||||
content_vbox.add_child(_create_section_label("Basic Information"))
|
||||
|
||||
var name_input = _create_input("Item Name:")
|
||||
content_vbox.add_child(name_input["container"])
|
||||
_item_name_edit = name_input["edit"]
|
||||
|
||||
var key_input = _create_input("Item Key:")
|
||||
content_vbox.add_child(key_input["container"])
|
||||
_item_key_edit = key_input["edit"]
|
||||
|
||||
var short_name_input = _create_input("Short Name:")
|
||||
content_vbox.add_child(short_name_input["container"])
|
||||
_short_name_edit = short_name_input["edit"]
|
||||
|
||||
var desc_label = Label.new()
|
||||
desc_label.text = "Description:"
|
||||
content_vbox.add_child(desc_label)
|
||||
|
||||
_description_edit = TextEdit.new()
|
||||
_description_edit.custom_minimum_size = Vector2(0, 60)
|
||||
_description_edit.wrap_mode = TextEdit.LINE_WRAPPING_BOUNDARY
|
||||
content_vbox.add_child(_description_edit)
|
||||
|
||||
content_vbox.add_child(HSeparator.new())
|
||||
|
||||
# Sprite Section
|
||||
var sprite_label = Label.new()
|
||||
sprite_label.text = "Inventory Sprite"
|
||||
sprite_label.add_theme_font_size_override("font_size", 14)
|
||||
content_vbox.add_child(sprite_label)
|
||||
|
||||
var sprite_container = HBoxContainer.new()
|
||||
sprite_container.add_theme_constant_override("separation", 8)
|
||||
content_vbox.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)
|
||||
|
||||
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)
|
||||
_sprite_picker.resource_selected.connect(_on_resource_picker_opening)
|
||||
picker_hbox.add_child(_sprite_picker)
|
||||
|
||||
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)
|
||||
|
||||
content_vbox.add_child(HSeparator.new())
|
||||
|
||||
# Properties Section
|
||||
var properties_label = Label.new()
|
||||
properties_label.text = "Properties"
|
||||
properties_label.add_theme_font_size_override("font_size", 16)
|
||||
content_vbox.add_child(properties_label)
|
||||
|
||||
var type_hbox = HBoxContainer.new()
|
||||
content_vbox.add_child(type_hbox)
|
||||
|
||||
var type_label = Label.new()
|
||||
type_label.text = "Item Type:"
|
||||
type_label.custom_minimum_size = Vector2(120, 0)
|
||||
type_hbox.add_child(type_label)
|
||||
|
||||
_item_type_option = OptionButton.new()
|
||||
_item_type_option.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
for item_type in ITEM_TYPE_NAMES:
|
||||
_item_type_option.add_item(item_type)
|
||||
type_hbox.add_child(_item_type_option)
|
||||
|
||||
var tier_spin = _create_spinbox("Tier:", 0, 0, 100, 1)
|
||||
content_vbox.add_child(tier_spin["container"])
|
||||
_tier_spin = tier_spin["spinbox"]
|
||||
|
||||
var price_spin = _create_spinbox("Price:", 0, 0, 999999, 1)
|
||||
content_vbox.add_child(price_spin["container"])
|
||||
_price_spin = price_spin["spinbox"]
|
||||
|
||||
var amount_spin = _create_spinbox("Amount:", 1, 0, 999999, 1)
|
||||
content_vbox.add_child(amount_spin["container"])
|
||||
_amount_spin = amount_spin["spinbox"]
|
||||
|
||||
var max_spin = _create_spinbox("Max:", 1, 0, 999999, 1)
|
||||
content_vbox.add_child(max_spin["container"])
|
||||
_max_spin = max_spin["spinbox"]
|
||||
|
||||
_pickup_if_maxed_check = CheckBox.new()
|
||||
_pickup_if_maxed_check.text = "Pickup If Maxed"
|
||||
content_vbox.add_child(_pickup_if_maxed_check)
|
||||
|
||||
_consume_on_use_check = CheckBox.new()
|
||||
_consume_on_use_check.text = "Consume On Use"
|
||||
content_vbox.add_child(_consume_on_use_check)
|
||||
|
||||
_selectable_check = CheckBox.new()
|
||||
_selectable_check.text = "Selectable"
|
||||
content_vbox.add_child(_selectable_check)
|
||||
|
||||
func _apply_prefill_data() -> void:
|
||||
if prefill_data.is_empty():
|
||||
return
|
||||
|
||||
if prefill_data.has("item_name"):
|
||||
_item_name_edit.text = str(prefill_data["item_name"])
|
||||
if prefill_data.has("item_key"):
|
||||
_item_key_edit.text = str(prefill_data["item_key"])
|
||||
if prefill_data.has("short_name"):
|
||||
_short_name_edit.text = str(prefill_data["short_name"])
|
||||
if prefill_data.has("description"):
|
||||
_description_edit.text = str(prefill_data["description"])
|
||||
if prefill_data.has("sprite_resource") and prefill_data["sprite_resource"] != null:
|
||||
_selected_sprite = prefill_data["sprite_resource"]
|
||||
_sprite_preview.texture = _selected_sprite
|
||||
_sprite_picker.edited_resource = _selected_sprite
|
||||
if prefill_data.has("item_type"):
|
||||
_item_type_option.selected = int(prefill_data["item_type"])
|
||||
if prefill_data.has("tier"):
|
||||
_tier_spin.value = int(prefill_data["tier"])
|
||||
if prefill_data.has("price"):
|
||||
_price_spin.value = int(prefill_data["price"])
|
||||
if prefill_data.has("amount"):
|
||||
_amount_spin.value = int(prefill_data["amount"])
|
||||
if prefill_data.has("max"):
|
||||
_max_spin.value = int(prefill_data["max"])
|
||||
if prefill_data.has("pickup_if_maxed"):
|
||||
_pickup_if_maxed_check.button_pressed = bool(prefill_data["pickup_if_maxed"])
|
||||
if prefill_data.has("consume_on_use"):
|
||||
_consume_on_use_check.button_pressed = bool(prefill_data["consume_on_use"])
|
||||
if prefill_data.has("selectable"):
|
||||
_selectable_check.button_pressed = bool(prefill_data["selectable"])
|
||||
|
||||
func _on_sprite_resource_changed(resource: Resource) -> void:
|
||||
if resource is Texture2D:
|
||||
_selected_sprite = resource
|
||||
_sprite_preview.texture = resource
|
||||
print("Sprite resource changed: ", resource.resource_path if resource.resource_path else "[Unsaved Resource]")
|
||||
elif resource == null:
|
||||
_selected_sprite = null
|
||||
_sprite_preview.texture = null
|
||||
print("Sprite resource cleared")
|
||||
else:
|
||||
push_warning("Selected resource is not a Texture2D")
|
||||
|
||||
call_deferred("_restore_window_focus")
|
||||
|
||||
func _on_resource_picker_opening(_resource: Resource, _inspect: bool) -> void:
|
||||
pass
|
||||
|
||||
func _restore_window_focus() -> void:
|
||||
if visible:
|
||||
move_to_foreground()
|
||||
|
||||
func _setup_sprite_picker() -> void:
|
||||
if _sprite_picker and editor_interface:
|
||||
_sprite_picker.toggle_mode = false
|
||||
|
||||
|
||||
func _on_create_pressed() -> void:
|
||||
var item_name = _item_name_edit.text.strip_edges()
|
||||
var item_key = _item_key_edit.text.strip_edges()
|
||||
|
||||
if item_name.is_empty():
|
||||
_show_error("Item Name cannot be empty")
|
||||
return
|
||||
|
||||
if item_key.is_empty():
|
||||
_show_error("Item Key cannot be empty")
|
||||
return
|
||||
|
||||
var item_data = {
|
||||
"item_name": item_name,
|
||||
"item_key": item_key,
|
||||
"short_name": _short_name_edit.text.strip_edges(),
|
||||
"description": _description_edit.text,
|
||||
"sprite_resource": _selected_sprite,
|
||||
"item_type": _item_type_option.selected,
|
||||
"tier": int(_tier_spin.value),
|
||||
"price": int(_price_spin.value),
|
||||
"amount": int(_amount_spin.value),
|
||||
"max": int(_max_spin.value),
|
||||
"pickup_if_maxed": _pickup_if_maxed_check.button_pressed,
|
||||
"consume_on_use": _consume_on_use_check.button_pressed,
|
||||
"selectable": _selectable_check.button_pressed
|
||||
}
|
||||
|
||||
item_data_confirmed.emit(item_data)
|
||||
queue_free()
|
||||
|
||||
func _on_cancel_pressed() -> void:
|
||||
queue_free()
|
||||
|
||||
func _show_error(message: String) -> void:
|
||||
var dialog = AcceptDialog.new()
|
||||
dialog.dialog_text = message
|
||||
dialog.title = "Error"
|
||||
add_child(dialog)
|
||||
dialog.popup_centered()
|
||||
dialog.confirmed.connect(func(): dialog.queue_free())
|
||||
|
||||
# Helper methods
|
||||
func _create_input(label_text: String) -> Dictionary:
|
||||
var hbox = HBoxContainer.new()
|
||||
|
||||
var label = Label.new()
|
||||
label.text = label_text
|
||||
label.custom_minimum_size = Vector2(120, 0)
|
||||
hbox.add_child(label)
|
||||
|
||||
var edit = LineEdit.new()
|
||||
edit.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
hbox.add_child(edit)
|
||||
|
||||
return {"container": hbox, "edit": edit}
|
||||
|
||||
func _create_spinbox(label_text: String, default_value: float, min_value: float, max_value: float, step: float) -> Dictionary:
|
||||
var hbox = HBoxContainer.new()
|
||||
|
||||
var label = Label.new()
|
||||
label.text = label_text
|
||||
label.custom_minimum_size = Vector2(120, 0)
|
||||
hbox.add_child(label)
|
||||
|
||||
var spinbox = SpinBox.new()
|
||||
spinbox.min_value = min_value
|
||||
spinbox.max_value = max_value
|
||||
spinbox.step = step
|
||||
spinbox.value = default_value
|
||||
spinbox.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
hbox.add_child(spinbox)
|
||||
|
||||
return {"container": hbox, "spinbox": spinbox}
|
||||
|
||||
func _create_section_label(text: String) -> Label:
|
||||
var label = Label.new()
|
||||
label.text = text
|
||||
label.add_theme_font_size_override("font_size", 16)
|
||||
return label
|
||||
1
addons/weapon_creator/ItemCreatorDialog.gd.uid
Normal file
1
addons/weapon_creator/ItemCreatorDialog.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://cwy7bmjb6bsab
|
||||
566
addons/weapon_creator/ItemViewer.gd
Normal file
566
addons/weapon_creator/ItemViewer.gd
Normal file
|
|
@ -0,0 +1,566 @@
|
|||
@tool
|
||||
extends PanelContainer
|
||||
|
||||
# Displays all items from the ItemsDatabase in a grid format
|
||||
# Shows sprite, name, and item type badge
|
||||
# Clicking opens the item resource in the inspector
|
||||
|
||||
signal item_selected(item_resource_path: String)
|
||||
signal duplicate_item_requested(item_data: Dictionary)
|
||||
signal item_deleted(item_name: String, item_path: String)
|
||||
signal item_duplication_started(item_name: String)
|
||||
|
||||
var _editor_interface: EditorInterface
|
||||
var _grid_container: HFlowContainer
|
||||
var _items_database_path := "res://Resources/ItemsDatabase.tres"
|
||||
var _show_2d_checkbox: CheckBox
|
||||
var _show_3d_checkbox: CheckBox
|
||||
var _show_neither_checkbox: CheckBox
|
||||
var _dock: PanelContainer
|
||||
|
||||
const SETTING_SHOW_2D = "weapon_creator/item_filter_show_2d"
|
||||
const SETTING_SHOW_3D = "weapon_creator/item_filter_show_3d"
|
||||
const SETTING_SHOW_NEITHER = "weapon_creator/item_filter_show_neither"
|
||||
|
||||
# ItemTypes enum values mapped to colors
|
||||
const ITEM_TYPE_COLORS = {
|
||||
0: Color(1.0, 0.2, 0.2), # KeycardRed - Red
|
||||
1: Color(0.3, 0.5, 1.0), # KeycardBlue - Blue
|
||||
2: Color(0.3, 1.0, 0.3), # KeycardGreen - Green
|
||||
3: Color(1.0, 0.8, 0.2), # Ammo - Yellow
|
||||
4: Color(1.0, 0.3, 0.3), # Medkit - Light Red
|
||||
5: Color(0.5, 1.0, 0.3), # FrogBomb - Lime
|
||||
6: Color(1.0, 0.5, 0.0), # Bomb - Orange
|
||||
7: Color(0.8, 0.3, 0.0), # Mine - Brown/Orange
|
||||
8: Color(1.0, 1.0, 0.2), # Battery - Bright Yellow
|
||||
9: Color(0.7, 0.7, 0.7), # Weapon - Gray
|
||||
10: Color(1.0, 0.2, 1.0), # Power - Magenta
|
||||
11: Color(1.0, 1.0, 1.0), # Points - White
|
||||
12: Color(0.8, 0.8, 0.0), # Credits - Gold
|
||||
13: Color(0.5, 0.3, 1.0) # KeyItem - Purple
|
||||
}
|
||||
|
||||
const ITEM_TYPE_NAMES = [
|
||||
"KeycardRed",
|
||||
"KeycardBlue",
|
||||
"KeycardGreen",
|
||||
"Ammo",
|
||||
"Medkit",
|
||||
"FrogBomb",
|
||||
"Bomb",
|
||||
"Mine",
|
||||
"Battery",
|
||||
"Weapon",
|
||||
"Power",
|
||||
"Points",
|
||||
"Credits",
|
||||
"KeyItem"
|
||||
]
|
||||
|
||||
func setup(editor_interface: EditorInterface, dock: PanelContainer = null) -> void:
|
||||
_editor_interface = editor_interface
|
||||
_dock = dock
|
||||
|
||||
if _show_2d_checkbox:
|
||||
_show_2d_checkbox.button_pressed = _load_filter_setting(SETTING_SHOW_2D, true)
|
||||
if _show_3d_checkbox:
|
||||
_show_3d_checkbox.button_pressed = _load_filter_setting(SETTING_SHOW_3D, true)
|
||||
if _show_neither_checkbox:
|
||||
_show_neither_checkbox.button_pressed = _load_filter_setting(SETTING_SHOW_NEITHER, true)
|
||||
|
||||
refresh_items()
|
||||
|
||||
func _ready() -> void:
|
||||
_build_ui()
|
||||
refresh_items()
|
||||
|
||||
func _build_ui() -> void:
|
||||
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)
|
||||
|
||||
var vbox = VBoxContainer.new()
|
||||
vbox.add_theme_constant_override("separation", 8)
|
||||
margin.add_child(vbox)
|
||||
|
||||
var header_hbox = HBoxContainer.new()
|
||||
vbox.add_child(header_hbox)
|
||||
|
||||
var create_button = Button.new()
|
||||
create_button.text = "Create Item"
|
||||
create_button.pressed.connect(_on_create_item_pressed)
|
||||
header_hbox.add_child(create_button)
|
||||
|
||||
var spacer = Control.new()
|
||||
spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
header_hbox.add_child(spacer)
|
||||
|
||||
_show_2d_checkbox = CheckBox.new()
|
||||
_show_2d_checkbox.text = "2D"
|
||||
_show_2d_checkbox.button_pressed = true
|
||||
_show_2d_checkbox.toggled.connect(_on_filter_changed)
|
||||
header_hbox.add_child(_show_2d_checkbox)
|
||||
|
||||
_show_3d_checkbox = CheckBox.new()
|
||||
_show_3d_checkbox.text = "3D"
|
||||
_show_3d_checkbox.button_pressed = true
|
||||
_show_3d_checkbox.toggled.connect(_on_filter_changed)
|
||||
header_hbox.add_child(_show_3d_checkbox)
|
||||
|
||||
_show_neither_checkbox = CheckBox.new()
|
||||
_show_neither_checkbox.text = "Neither"
|
||||
_show_neither_checkbox.button_pressed = true
|
||||
_show_neither_checkbox.toggled.connect(_on_filter_changed)
|
||||
_show_neither_checkbox.tooltip_text = "Show items that don't have 2D or 3D properties set"
|
||||
header_hbox.add_child(_show_neither_checkbox)
|
||||
|
||||
var refresh_button = Button.new()
|
||||
refresh_button.text = "Refresh"
|
||||
refresh_button.pressed.connect(refresh_items)
|
||||
header_hbox.add_child(refresh_button)
|
||||
|
||||
vbox.add_child(HSeparator.new())
|
||||
|
||||
var scroll = ScrollContainer.new()
|
||||
scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
|
||||
vbox.add_child(scroll)
|
||||
|
||||
_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_items() -> void:
|
||||
if not _grid_container:
|
||||
return
|
||||
|
||||
for child in _grid_container.get_children():
|
||||
child.queue_free()
|
||||
|
||||
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
|
||||
|
||||
var show_2d = _show_2d_checkbox == null or _show_2d_checkbox.button_pressed
|
||||
var show_3d = _show_3d_checkbox == null or _show_3d_checkbox.button_pressed
|
||||
var show_neither = _show_neither_checkbox == null or _show_neither_checkbox.button_pressed
|
||||
|
||||
var item_count = 0
|
||||
for loot_item in loot_items:
|
||||
var has_2d = _item_has_2d(loot_item)
|
||||
var has_3d = _item_has_3d(loot_item)
|
||||
|
||||
var should_show = false
|
||||
if has_2d and has_3d:
|
||||
should_show = show_2d or show_3d
|
||||
elif has_2d:
|
||||
should_show = show_2d
|
||||
elif has_3d:
|
||||
should_show = show_3d
|
||||
else:
|
||||
should_show = show_neither
|
||||
|
||||
if should_show:
|
||||
_create_item_tile(loot_item, has_2d, has_3d)
|
||||
item_count += 1
|
||||
|
||||
if item_count == 0:
|
||||
_add_error_label("No items match the current filter")
|
||||
|
||||
func _item_has_2d(loot_item: Resource) -> bool:
|
||||
var weapon_data = loot_item.get("WeaponData")
|
||||
var drop_scene = loot_item.get("DropScenePath")
|
||||
return weapon_data != null or (drop_scene != null and drop_scene != "")
|
||||
|
||||
func _item_has_3d(loot_item: Resource) -> bool:
|
||||
var weapon_data_3d = loot_item.get("WeaponData3D")
|
||||
var drop_scene_3d = loot_item.get("DropScenePath3D")
|
||||
return weapon_data_3d != null or (drop_scene_3d != null and drop_scene_3d != "")
|
||||
|
||||
func _on_filter_changed(_toggled: bool) -> void:
|
||||
_save_filter_settings()
|
||||
refresh_items()
|
||||
|
||||
func _load_filter_setting(key: String, default_value: bool) -> bool:
|
||||
if not _editor_interface:
|
||||
return default_value
|
||||
var editor_settings = _editor_interface.get_editor_settings()
|
||||
if editor_settings and editor_settings.has_setting(key):
|
||||
return editor_settings.get_setting(key)
|
||||
return default_value
|
||||
|
||||
func _save_filter_settings() -> void:
|
||||
if not _editor_interface:
|
||||
return
|
||||
var editor_settings = _editor_interface.get_editor_settings()
|
||||
if editor_settings:
|
||||
editor_settings.set_setting(SETTING_SHOW_2D, _show_2d_checkbox.button_pressed)
|
||||
editor_settings.set_setting(SETTING_SHOW_3D, _show_3d_checkbox.button_pressed)
|
||||
editor_settings.set_setting(SETTING_SHOW_NEITHER, _show_neither_checkbox.button_pressed)
|
||||
|
||||
func _create_item_tile(loot_item: Resource, has_2d: bool, has_3d: bool) -> void:
|
||||
var panel = PanelContainer.new()
|
||||
panel.custom_minimum_size = Vector2(100, 145)
|
||||
panel.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
|
||||
panel.size_flags_vertical = Control.SIZE_SHRINK_BEGIN
|
||||
panel.mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
panel.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND
|
||||
|
||||
var vbox = VBoxContainer.new()
|
||||
vbox.add_theme_constant_override("separation", 4)
|
||||
vbox.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
|
||||
var sprite: Texture2D = loot_item.get("InventorySprite")
|
||||
|
||||
var sprite_container = Control.new()
|
||||
sprite_container.custom_minimum_size = Vector2(64, 64)
|
||||
sprite_container.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
vbox.add_child(sprite_container)
|
||||
|
||||
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
|
||||
texture_rect.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
sprite_container.add_child(texture_rect)
|
||||
|
||||
if sprite:
|
||||
texture_rect.texture = sprite
|
||||
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.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
placeholder_label.add_theme_font_size_override("font_size", 10)
|
||||
placeholder_label.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
sprite_container.add_child(placeholder_label)
|
||||
|
||||
# Add item type badge in top-left corner with color coding
|
||||
var item_type: int = loot_item.get("Item")
|
||||
var item_type_color = ITEM_TYPE_COLORS.get(item_type, Color.WHITE)
|
||||
var item_type_name = ITEM_TYPE_NAMES[item_type] if item_type < ITEM_TYPE_NAMES.size() else str(item_type)
|
||||
|
||||
var type_badge = Label.new()
|
||||
type_badge.text = item_type_name
|
||||
type_badge.add_theme_font_size_override("font_size", 10)
|
||||
type_badge.add_theme_color_override("font_color", item_type_color)
|
||||
type_badge.add_theme_color_override("font_outline_color", Color(0.15, 0.15, 0.15))
|
||||
type_badge.add_theme_constant_override("outline_size", 3)
|
||||
type_badge.horizontal_alignment = HORIZONTAL_ALIGNMENT_LEFT
|
||||
type_badge.vertical_alignment = VERTICAL_ALIGNMENT_TOP
|
||||
type_badge.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
type_badge.position = Vector2(2, 2)
|
||||
type_badge.custom_minimum_size = Vector2(60, 12)
|
||||
type_badge.clip_text = true
|
||||
sprite_container.add_child(type_badge)
|
||||
|
||||
# Add 2D/3D/Neither badge in bottom-right corner
|
||||
var dimension_badge = Label.new()
|
||||
if has_2d and has_3d:
|
||||
dimension_badge.text = "2D+3D"
|
||||
dimension_badge.add_theme_color_override("font_color", Color(0.8, 0.2, 0.8)) # Purple
|
||||
elif has_2d:
|
||||
dimension_badge.text = "2D"
|
||||
dimension_badge.add_theme_color_override("font_color", Color(0.3, 0.5, 1.0)) # Blue
|
||||
elif has_3d:
|
||||
dimension_badge.text = "3D"
|
||||
dimension_badge.add_theme_color_override("font_color", Color(1.0, 0.2, 0.2)) # Red
|
||||
else:
|
||||
dimension_badge.text = "??"
|
||||
dimension_badge.add_theme_color_override("font_color", Color(1.0, 0.5, 0.0)) # Orange (warning)
|
||||
|
||||
dimension_badge.add_theme_font_size_override("font_size", 12)
|
||||
dimension_badge.add_theme_color_override("font_outline_color", Color(0.15, 0.15, 0.15))
|
||||
dimension_badge.add_theme_constant_override("outline_size", 3)
|
||||
dimension_badge.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
|
||||
dimension_badge.vertical_alignment = VERTICAL_ALIGNMENT_BOTTOM
|
||||
dimension_badge.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
dimension_badge.position = Vector2(24, 44)
|
||||
dimension_badge.size = Vector2(38, 20)
|
||||
sprite_container.add_child(dimension_badge)
|
||||
|
||||
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(92, 48)
|
||||
name_label.clip_text = false
|
||||
name_label.max_lines_visible = 3
|
||||
name_label.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
name_label.add_theme_font_size_override("font_size", 14)
|
||||
name_label.add_theme_constant_override("line_spacing", -2)
|
||||
vbox.add_child(name_label)
|
||||
|
||||
panel.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 item_path = loot_item.resource_path
|
||||
if item_path:
|
||||
panel.tooltip_text = item_path
|
||||
else:
|
||||
panel.tooltip_text = "Built-in resource (no file path)"
|
||||
|
||||
panel.gui_input.connect(_on_item_gui_input.bind(panel, loot_item, has_2d, has_3d))
|
||||
|
||||
_grid_container.add_child(panel)
|
||||
|
||||
func _on_item_clicked(loot_item: Resource, item_path: String) -> void:
|
||||
if not _editor_interface:
|
||||
push_error("Editor interface not available")
|
||||
return
|
||||
|
||||
if loot_item:
|
||||
_editor_interface.edit_resource(loot_item)
|
||||
item_selected.emit(item_path)
|
||||
|
||||
func _on_item_gui_input(event: InputEvent, panel: PanelContainer, loot_item: Resource, has_2d: bool, has_3d: bool) -> void:
|
||||
if event is InputEventMouseButton:
|
||||
var mouse_event = event as InputEventMouseButton
|
||||
if mouse_event.pressed:
|
||||
if mouse_event.button_index == MOUSE_BUTTON_LEFT:
|
||||
var item_path = loot_item.resource_path if loot_item else ""
|
||||
_on_item_clicked(loot_item, item_path)
|
||||
elif mouse_event.button_index == MOUSE_BUTTON_RIGHT:
|
||||
_show_context_menu(panel, loot_item, has_2d, has_3d)
|
||||
|
||||
func _show_context_menu(panel: PanelContainer, loot_item: Resource, has_2d: bool, has_3d: bool) -> void:
|
||||
var popup = PopupMenu.new()
|
||||
popup.add_item("Open Item", 0)
|
||||
|
||||
var weapon_data_2d = loot_item.get("WeaponData")
|
||||
var weapon_data_3d = loot_item.get("WeaponData3D")
|
||||
|
||||
if weapon_data_2d or weapon_data_3d:
|
||||
popup.add_separator()
|
||||
if weapon_data_2d:
|
||||
popup.add_item("Open Weapon (2D)", 1)
|
||||
if weapon_data_3d:
|
||||
popup.add_item("Open Weapon (3D)", 2)
|
||||
|
||||
popup.add_separator()
|
||||
popup.add_item("Duplicate Item", 3)
|
||||
popup.add_separator()
|
||||
popup.add_item("Copy Item Resource Path", 4)
|
||||
popup.add_separator()
|
||||
popup.add_item("Delete", 5)
|
||||
|
||||
popup.id_pressed.connect(_on_context_menu_id_pressed.bind(popup, loot_item))
|
||||
|
||||
get_tree().root.add_child(popup)
|
||||
|
||||
var mouse_pos = DisplayServer.mouse_get_position()
|
||||
popup.position = mouse_pos
|
||||
popup.popup()
|
||||
|
||||
popup.popup_hide.connect(func(): popup.queue_free())
|
||||
|
||||
func _on_context_menu_id_pressed(id: int, popup: PopupMenu, loot_item: Resource) -> void:
|
||||
match id:
|
||||
0:
|
||||
_open_item(loot_item)
|
||||
1:
|
||||
_open_weapon(loot_item.get("WeaponData"))
|
||||
2:
|
||||
_open_weapon(loot_item.get("WeaponData3D"))
|
||||
3:
|
||||
_duplicate_item(loot_item)
|
||||
4:
|
||||
_copy_item_resource_path(loot_item)
|
||||
5:
|
||||
_confirm_delete(loot_item)
|
||||
|
||||
func _open_item(loot_item: Resource) -> void:
|
||||
if not _editor_interface:
|
||||
push_error("Editor interface not available")
|
||||
return
|
||||
|
||||
if loot_item:
|
||||
_editor_interface.edit_resource(loot_item)
|
||||
|
||||
func _open_weapon(weapon_data: Resource) -> void:
|
||||
if not _editor_interface:
|
||||
push_error("Editor interface not available")
|
||||
return
|
||||
|
||||
if weapon_data:
|
||||
_editor_interface.edit_resource(weapon_data)
|
||||
|
||||
func _duplicate_item(loot_item: Resource) -> void:
|
||||
if not _editor_interface:
|
||||
push_error("Editor interface not available")
|
||||
return
|
||||
|
||||
var item_name = loot_item.get("ItemName") if loot_item else "Unknown"
|
||||
|
||||
if _dock:
|
||||
_dock.call("add_log", "=== Duplicating Item ===", Color.CYAN)
|
||||
_dock.call("add_log", "Source: " + item_name, Color.CYAN)
|
||||
_dock.call("add_log", "Opening creation dialog with prefilled data...", Color.CYAN)
|
||||
|
||||
print("Duplicating item: ", item_name)
|
||||
|
||||
item_duplication_started.emit(item_name)
|
||||
|
||||
var prefill_data = {}
|
||||
|
||||
if loot_item:
|
||||
prefill_data["item_name"] = loot_item.get("ItemName")
|
||||
prefill_data["item_key"] = loot_item.get("ItemKey")
|
||||
prefill_data["short_name"] = loot_item.get("ShortName")
|
||||
prefill_data["description"] = loot_item.get("ItemDescription")
|
||||
prefill_data["sprite_resource"] = loot_item.get("InventorySprite")
|
||||
prefill_data["item_type"] = loot_item.get("Item")
|
||||
prefill_data["tier"] = loot_item.get("Tier")
|
||||
prefill_data["price"] = loot_item.get("Price")
|
||||
prefill_data["amount"] = loot_item.get("Amount")
|
||||
prefill_data["max"] = loot_item.get("Max")
|
||||
prefill_data["pickup_if_maxed"] = loot_item.get("PickupIfMaxed")
|
||||
prefill_data["consume_on_use"] = loot_item.get("ConsumeOnUse")
|
||||
prefill_data["selectable"] = loot_item.get("Selectable")
|
||||
|
||||
var dialog_script = load("res://addons/weapon_creator/ItemCreatorDialog.gd")
|
||||
var dialog = Window.new()
|
||||
dialog.set_script(dialog_script)
|
||||
|
||||
get_tree().root.add_child(dialog)
|
||||
|
||||
dialog.call_deferred("setup", _editor_interface, prefill_data)
|
||||
dialog.call_deferred("connect", "item_data_confirmed", _on_duplicate_item_confirmed)
|
||||
dialog.call_deferred("popup_centered")
|
||||
|
||||
func _on_duplicate_item_confirmed(item_data: Dictionary) -> void:
|
||||
duplicate_item_requested.emit(item_data)
|
||||
get_tree().create_timer(0.5).timeout.connect(refresh_items)
|
||||
|
||||
func _copy_item_resource_path(loot_item: Resource) -> void:
|
||||
if loot_item and loot_item.resource_path:
|
||||
DisplayServer.clipboard_set(loot_item.resource_path)
|
||||
print("Copied item resource path: ", loot_item.resource_path)
|
||||
else:
|
||||
push_warning("No resource path available for item (built-in resource)")
|
||||
|
||||
func _confirm_delete(loot_item: Resource) -> void:
|
||||
var item_name: String = loot_item.get("ItemName")
|
||||
var loot_item_path = loot_item.resource_path
|
||||
|
||||
var dialog_text = "Are you sure you want to delete this item?\n\n"
|
||||
dialog_text += "Item: " + (item_name if item_name else "Unknown") + "\n\n"
|
||||
dialog_text += "This will delete:\n"
|
||||
dialog_text += "- LootItem: " + (loot_item_path if loot_item_path else "Built-in") + "\n"
|
||||
dialog_text += "- Remove the item from ItemsDatabase\n\n"
|
||||
dialog_text += "Note: This will NOT delete any linked weapon resources or drop scenes.\n"
|
||||
dialog_text += "This action cannot be undone!"
|
||||
|
||||
var confirm_dialog = ConfirmationDialog.new()
|
||||
confirm_dialog.dialog_text = dialog_text
|
||||
confirm_dialog.title = "Confirm Deletion"
|
||||
confirm_dialog.ok_button_text = "Delete"
|
||||
|
||||
confirm_dialog.confirmed.connect(_perform_delete.bind(loot_item, confirm_dialog))
|
||||
confirm_dialog.close_requested.connect(func(): confirm_dialog.queue_free())
|
||||
confirm_dialog.canceled.connect(func(): confirm_dialog.queue_free())
|
||||
|
||||
get_tree().root.add_child(confirm_dialog)
|
||||
confirm_dialog.popup_centered()
|
||||
|
||||
func _perform_delete(loot_item: Resource, dialog: ConfirmationDialog) -> void:
|
||||
var loot_item_path = loot_item.resource_path
|
||||
var item_name = loot_item.get("ItemName") if loot_item else "Unknown"
|
||||
|
||||
if not loot_item_path:
|
||||
push_error("Cannot delete built-in resources")
|
||||
dialog.queue_free()
|
||||
return
|
||||
|
||||
var items_database = load(_items_database_path)
|
||||
if not items_database:
|
||||
push_error("Failed to load ItemsDatabase")
|
||||
dialog.queue_free()
|
||||
return
|
||||
|
||||
var loot_items = items_database.get("LootItems")
|
||||
if not loot_items:
|
||||
push_error("No LootItems found in database")
|
||||
dialog.queue_free()
|
||||
return
|
||||
|
||||
var index = loot_items.find(loot_item)
|
||||
if index >= 0:
|
||||
loot_items.remove_at(index)
|
||||
var save_result = ResourceSaver.save(items_database, _items_database_path)
|
||||
if save_result != OK:
|
||||
push_error("Failed to save ItemsDatabase after removing item")
|
||||
|
||||
var file_system = _editor_interface.get_resource_filesystem()
|
||||
|
||||
var item_deleted_success = false
|
||||
|
||||
if DirAccess.remove_absolute(loot_item_path) == OK:
|
||||
print("Deleted loot item resource: ", loot_item_path)
|
||||
item_deleted_success = true
|
||||
else:
|
||||
push_error("Failed to delete loot item resource: ", loot_item_path)
|
||||
|
||||
if file_system:
|
||||
file_system.scan()
|
||||
|
||||
dialog.queue_free()
|
||||
|
||||
if item_deleted_success:
|
||||
item_deleted.emit(item_name, loot_item_path)
|
||||
|
||||
if _dock:
|
||||
_dock.call("add_log", "=== Item Deleted ===", Color.ORANGE)
|
||||
_dock.call("add_log", "Item: " + item_name, Color.ORANGE)
|
||||
_dock.call("add_log", "✓ Deleted LootItem: " + loot_item_path, Color.GREEN)
|
||||
_dock.call("add_log", "✓ Removed from ItemsDatabase", Color.GREEN)
|
||||
_dock.call("add_log", "Note: Linked assets were NOT deleted", Color.YELLOW)
|
||||
|
||||
refresh_items()
|
||||
|
||||
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)
|
||||
|
||||
func _on_create_item_pressed() -> void:
|
||||
if not _editor_interface:
|
||||
push_error("Editor interface not available")
|
||||
return
|
||||
|
||||
var dialog_script = load("res://addons/weapon_creator/ItemCreatorDialog.gd")
|
||||
var dialog = Window.new()
|
||||
dialog.set_script(dialog_script)
|
||||
|
||||
get_tree().root.add_child(dialog)
|
||||
|
||||
dialog.call_deferred("setup", _editor_interface)
|
||||
dialog.call_deferred("connect", "item_data_confirmed", _on_duplicate_item_confirmed)
|
||||
dialog.call_deferred("popup_centered")
|
||||
|
||||
1
addons/weapon_creator/ItemViewer.gd.uid
Normal file
1
addons/weapon_creator/ItemViewer.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://ss8kgnlsw60s
|
||||
|
|
@ -6,12 +6,14 @@ extends PanelContainer
|
|||
|
||||
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 log_output: RichTextLabel
|
||||
var weapon_viewer: PanelContainer
|
||||
var bullet_viewer: PanelContainer
|
||||
var item_viewer: PanelContainer
|
||||
var tab_container: TabContainer
|
||||
|
||||
var _is_creating: bool = false
|
||||
|
|
@ -74,10 +76,21 @@ func _build_ui() -> void:
|
|||
bullet_viewer.bullet_duplication_started.connect(_on_bullet_duplication_started)
|
||||
tab_container.add_child(bullet_viewer)
|
||||
|
||||
# Item Viewer Tab
|
||||
var item_viewer_script = load("res://addons/weapon_creator/ItemViewer.gd")
|
||||
item_viewer = PanelContainer.new()
|
||||
item_viewer.set_script(item_viewer_script)
|
||||
item_viewer.name = "Items"
|
||||
item_viewer.duplicate_item_requested.connect(_on_item_data_confirmed)
|
||||
item_viewer.item_deleted.connect(_on_item_deleted)
|
||||
item_viewer.item_duplication_started.connect(_on_item_duplication_started)
|
||||
tab_container.add_child(item_viewer)
|
||||
|
||||
# Setup after adding to scene tree
|
||||
if _editor_interface:
|
||||
weapon_viewer.setup(_editor_interface, self)
|
||||
bullet_viewer.setup(_editor_interface, self)
|
||||
item_viewer.setup(_editor_interface, self)
|
||||
|
||||
# Log header with label and clear button
|
||||
var log_header_hbox = HBoxContainer.new()
|
||||
|
|
@ -140,6 +153,21 @@ func _on_bullet_duplication_started(bullet_name: String, is_3d: bool) -> void:
|
|||
# Logging is handled directly in BulletViewer
|
||||
pass
|
||||
|
||||
func _on_item_data_confirmed(item_data: Dictionary) -> void:
|
||||
if _is_creating:
|
||||
return
|
||||
|
||||
_is_creating = true
|
||||
log_output.clear()
|
||||
|
||||
create_item_requested.emit(item_data)
|
||||
|
||||
func _on_item_deleted(item_name: String, item_path: String) -> void:
|
||||
pass
|
||||
|
||||
func _on_item_duplication_started(item_name: String) -> void:
|
||||
pass
|
||||
|
||||
func add_log(message: String, color: Color = Color.WHITE) -> void:
|
||||
# Add colored message to log output
|
||||
log_output.append_text("[color=#" + color.to_html(false) + "]" + message + "[/color]\n")
|
||||
|
|
@ -151,3 +179,5 @@ func set_creation_complete() -> void:
|
|||
weapon_viewer.call("refresh_weapons")
|
||||
if bullet_viewer:
|
||||
bullet_viewer.call("refresh_bullets")
|
||||
if item_viewer:
|
||||
item_viewer.call("refresh_items")
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ func _enter_tree() -> void:
|
|||
dock_instance.create_weapon_requested.connect(_on_create_weapon_requested)
|
||||
if dock_instance.has_signal("create_bullet_requested"):
|
||||
dock_instance.create_bullet_requested.connect(_on_create_bullet_requested)
|
||||
if dock_instance.has_signal("create_item_requested"):
|
||||
dock_instance.create_item_requested.connect(_on_create_item_requested)
|
||||
|
||||
# Add the dock to the editor (bottom dock area)
|
||||
add_control_to_bottom_panel(dock_instance, "Weapon Creator")
|
||||
|
|
@ -281,6 +283,89 @@ 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 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")
|
||||
var description: String = item_data.get("description", "A new item")
|
||||
var sprite_resource: Texture2D = item_data.get("sprite_resource", null)
|
||||
var item_type: int = item_data.get("item_type", 0)
|
||||
var tier: int = item_data.get("tier", 0)
|
||||
var price: int = item_data.get("price", 0)
|
||||
var amount: int = item_data.get("amount", 1)
|
||||
var max: int = item_data.get("max", 1)
|
||||
var pickup_if_maxed: bool = item_data.get("pickup_if_maxed", false)
|
||||
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"
|
||||
|
||||
dock_instance.call("add_log", "=== Starting Item Creation ===")
|
||||
dock_instance.call("add_log", "Item Name: " + item_name)
|
||||
dock_instance.call("add_log", "Item Key: " + item_key)
|
||||
|
||||
if _create_loot_item_only(item_resource_path, item_name, short_name, description,
|
||||
item_key, sprite_resource, item_type, tier, price,
|
||||
amount, max, pickup_if_maxed, consume_on_use, selectable):
|
||||
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)
|
||||
dock_instance.call("set_creation_complete")
|
||||
return
|
||||
|
||||
if _add_to_items_database(items_database_path, item_resource_path):
|
||||
dock_instance.call("add_log", "✓ Added to ItemsDatabase", Color.GREEN)
|
||||
else:
|
||||
dock_instance.call("add_log", "✗ Failed to add to ItemsDatabase", Color.RED)
|
||||
dock_instance.call("set_creation_complete")
|
||||
return
|
||||
|
||||
dock_instance.call("add_log", "=== Item Creation Complete ===", Color.CYAN)
|
||||
dock_instance.call("set_creation_complete")
|
||||
|
||||
get_editor_interface().get_resource_filesystem().scan()
|
||||
|
||||
func _create_loot_item_only(path: String, item_name: String, short_name: String,
|
||||
description: String, item_key: String, sprite_resource: Texture2D,
|
||||
item_type: int, tier: int, price: int, amount: int, max: int,
|
||||
pickup_if_maxed: bool, consume_on_use: bool, selectable: bool) -> bool:
|
||||
if ResourceLoader.exists(path):
|
||||
dock_instance.call("add_log", "Warning: LootItem already exists at " + path, Color.YELLOW)
|
||||
return false
|
||||
|
||||
var loot_item_script = load("res://Scripts/Resources/LootItem.cs")
|
||||
if loot_item_script == null:
|
||||
dock_instance.call("add_log", "Error: Could not load LootItem.cs script", Color.RED)
|
||||
return false
|
||||
|
||||
var loot_item = Resource.new()
|
||||
loot_item.set_script(loot_item_script)
|
||||
|
||||
loot_item.set("ItemName", item_name)
|
||||
loot_item.set("ShortName", short_name)
|
||||
loot_item.set("ItemDescription", description)
|
||||
loot_item.set("ItemKey", item_key)
|
||||
loot_item.set("Item", item_type)
|
||||
loot_item.set("Tier", tier)
|
||||
loot_item.set("Price", price)
|
||||
loot_item.set("Amount", amount)
|
||||
loot_item.set("Max", max)
|
||||
loot_item.set("PickupIfMaxed", pickup_if_maxed)
|
||||
loot_item.set("ConsumeOnUse", consume_on_use)
|
||||
loot_item.set("Selectable", selectable)
|
||||
|
||||
if sprite_resource != null:
|
||||
loot_item.set("InventorySprite", sprite_resource)
|
||||
|
||||
var err = ResourceSaver.save(loot_item, path)
|
||||
if err != OK:
|
||||
dock_instance.call("add_log", "Error saving LootItem: " + str(err), Color.RED)
|
||||
return false
|
||||
|
||||
return true
|
||||
|
||||
|
||||
func _create_bullet_resource(path: String, bullet_data: Dictionary) -> bool:
|
||||
if ResourceLoader.exists(path):
|
||||
dock_instance.call("add_log", "Warning: BulletResource already exists at " + path, Color.YELLOW)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue