Starter-Kit-City-Builder/scripts/structure_menu.gd

315 lines
11 KiB
GDScript

extends Control
@export var builder: Node3D
@export var base_menu_width: float = 300.0 # Base width for the menu
@export var item_width: float = 280.0 # Width of each item
@export var item_spacing: float = 20.0 # Spacing between items
@export var menu_speed: float = 0.3
@export var visible_menu_width: float = 600.0 # The visible width of the menu panel
@export var item_height: float = 180.0
@export var menu_vertical_offset: float = 40.0
var is_open: bool = false
var selected_index: int = -1
var menu_width: float = base_menu_width # Will be updated based on items
@onready var toggle_button = $ToggleButton
@onready var menu_panel = $MenuPanel
@onready var items_container = $MenuPanel/ScrollContainer/ItemsContainer
func _ready():
# Ensure we have a valid builder reference
if not builder:
builder = get_node_or_null("/root/Main/Builder")
if not builder:
push_error("StructureMenu: Builder node not found!")
return
# Initialize menu panel position and size
menu_panel.size.x = base_menu_width
menu_panel.position.x = -menu_panel.size.x # Start closed
# Vertically center the toggle button on the menu panel
toggle_button.anchor_top = 0.5
toggle_button.anchor_bottom = 0.5
toggle_button.offset_top = -toggle_button.size.y / 2
toggle_button.offset_bottom = toggle_button.size.y / 2
# Place toggle button at the left edge of the menu
toggle_button.position.x = 0
toggle_button.text = ""
# Connect signals
toggle_button.pressed.connect(_on_toggle_button_pressed)
# Connect to builder's structure update signal if it exists
if "structure_updated" in builder:
print("Connecting to builder's structure_updated signal") # Debug print
builder.structure_updated.connect(_on_builder_structure_updated)
# Wait a frame to ensure all nodes are ready
await get_tree().process_frame
_center_toggle_button()
# Populate the menu
populate_menu()
# Print debug info
print("StructureMenu: Builder found: ", builder != null)
print("StructureMenu: Structures array size: ", builder.structures.size() if builder and "structures" in builder else 0)
# Initialize selection if builder has a current index
if builder and "index" in builder:
selected_index = builder.index
update_selection_highlight()
func _center_toggle_button():
# Vertically center the toggle button on the menu panel
var menu_height = menu_panel.size.y
var button_height = toggle_button.size.y
toggle_button.position.y = (menu_height - button_height) / 2
# Horizontally: always at 0 when closed, at menu_panel.size.x when open
toggle_button.position.x = menu_panel.size.x if is_open else 0
func _on_toggle_button_pressed():
is_open = !is_open
var tween = create_tween()
tween.tween_property(menu_panel, "position:x", 0.0 if is_open else -menu_panel.size.x, menu_speed)
tween.parallel().tween_property(toggle_button, "position:x", menu_panel.size.x if is_open else 0.0, menu_speed)
toggle_button.text = "" if is_open else ""
_center_toggle_button()
func _on_builder_structure_updated(index):
print("Builder structure updated signal received. Index: ", index) # Debug print
# Update the selected index and highlight
selected_index = index
update_selection_highlight()
func populate_menu():
if not builder or not "structures" in builder:
push_error("StructureMenu: Builder or structures array not found!")
return
# Clear existing items
for child in items_container.get_children():
child.queue_free()
# Count unlocked structures
var unlocked_count = 0
for structure in builder.structures:
if "unlocked" in structure and structure.unlocked:
unlocked_count += 1
# Calculate menu width and height using the user's formula
var padding = item_spacing
menu_width = (unlocked_count * item_width) + ((unlocked_count + 1) * padding)
var menu_height = item_height + 2 * padding
# Center the menu panel horizontally and set its size using anchors and offsets
menu_panel.anchor_left = 0.5
menu_panel.anchor_right = 0.5
menu_panel.offset_left = -menu_width / 2
menu_panel.offset_right = menu_width / 2
menu_panel.anchor_top = 0.0
menu_panel.anchor_bottom = 0.0
menu_panel.offset_top = 0
menu_panel.offset_bottom = menu_height
$MenuPanel/ScrollContainer.size.x = menu_width
$MenuPanel/ScrollContainer.size.y = menu_height
$MenuPanel/ScrollContainer.position.y = 0
$MenuPanel/ScrollContainer.clip_contents = true
# If menu is closed, keep it offscreen
if not is_open:
menu_panel.position.x = -menu_width
_center_toggle_button()
# Create a MarginContainer for all-side padding
var margin = MarginContainer.new()
margin.add_theme_constant_override("margin_left", padding)
margin.add_theme_constant_override("margin_right", padding)
margin.add_theme_constant_override("margin_top", padding)
margin.add_theme_constant_override("margin_bottom", padding)
margin.size_flags_horizontal = Control.SIZE_EXPAND_FILL
margin.size_flags_vertical = Control.SIZE_EXPAND_FILL
items_container.add_child(margin)
margin.position.y = menu_vertical_offset
# Create a CenterContainer to center the row both horizontally and vertically
var center = CenterContainer.new()
center.size_flags_horizontal = Control.SIZE_EXPAND_FILL
center.size_flags_vertical = Control.SIZE_EXPAND_FILL
margin.add_child(center)
# Create a horizontal container for all items
var row = HBoxContainer.new()
row.add_theme_constant_override("separation", padding)
# DO NOT set row.size_flags_horizontal so it doesn't expand
center.add_child(row)
# Add all unlocked structures to the row
for i in range(builder.structures.size()):
var structure = builder.structures[i]
if "unlocked" in structure and structure.unlocked:
var item = create_structure_item(structure, i)
row.add_child(item)
# Update selection highlight after populating
update_selection_highlight()
func create_structure_item(structure, index):
var item = PanelContainer.new()
item.custom_minimum_size = Vector2(item_width, item_height)
item.size_flags_horizontal = 0 # Prevent horizontal expansion
item.add_theme_stylebox_override("panel", StyleBoxFlat.new())
var style = item.get_theme_stylebox("panel") as StyleBoxFlat
style.bg_color = Color(0.2, 0.2, 0.2, 0.8)
style.border_width_left = 2
style.border_width_top = 2
style.border_width_right = 2
style.border_width_bottom = 2
style.border_color = Color(0.3, 0.3, 0.3)
style.corner_radius_top_left = 5
style.corner_radius_top_right = 5
style.corner_radius_bottom_left = 5
style.corner_radius_bottom_right = 5
# Store the structure index in the item for reference
item.set_meta("structure_index", index)
# Main container for vertical layout
var vbox = VBoxContainer.new()
vbox.custom_minimum_size = Vector2(item_width, item_height)
vbox.size_flags_horizontal = 0 # Prevent horizontal expansion
vbox.add_theme_constant_override("separation", item_spacing)
item.add_child(vbox)
# Thumbnail container
var thumbnail_container = CenterContainer.new()
thumbnail_container.custom_minimum_size = Vector2(128, 128)
thumbnail_container.size_flags_horizontal = Control.SIZE_SHRINK_CENTER
vbox.add_child(thumbnail_container)
# Thumbnail
var thumbnail = TextureRect.new()
thumbnail.custom_minimum_size = Vector2(128, 128)
thumbnail.size_flags_horizontal = Control.SIZE_SHRINK_CENTER
thumbnail.size_flags_vertical = Control.SIZE_SHRINK_CENTER
thumbnail.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
# Try to load thumbnail
if "thumbnail" in structure and structure.thumbnail != null:
var texture = load(structure.thumbnail)
if texture != null:
# Scale the texture to 128x128
var image = texture.get_image()
image.resize(128, 128)
var scaled_texture = ImageTexture.create_from_image(image)
thumbnail.texture = scaled_texture
print("Structure ", index, " thumbnail loaded: ", structure.thumbnail)
print("Texture size: ", scaled_texture.get_size())
print("Thumbnail size: ", thumbnail.size)
print("Thumbnail container size: ", thumbnail_container.size)
thumbnail_container.add_child(thumbnail)
# Debug print after adding to container
print("After adding to container:")
print("Thumbnail size: ", thumbnail.size)
print("Thumbnail container size: ", thumbnail_container.size)
print("Thumbnail custom_minimum_size: ", thumbnail.custom_minimum_size)
print("Container custom_minimum_size: ", thumbnail_container.custom_minimum_size)
# Info container
var info = VBoxContainer.new()
info.size_flags_horizontal = Control.SIZE_EXPAND_FILL
info.add_theme_constant_override("separation", 5)
vbox.add_child(info)
# Structure name
var name_label = Label.new()
if "title" in structure and structure.title != null:
name_label.text = structure.title
else:
name_label.text = "Structure " + str(index)
name_label.add_theme_font_size_override("font_size", 18)
name_label.add_theme_color_override("font_color", Color(0.9, 0.9, 0.2))
name_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
info.add_child(name_label)
# Structure cost
var cost_label = Label.new()
if "price" in structure:
cost_label.text = "Cost: $" + str(structure.price)
else:
cost_label.text = "Cost: $0"
cost_label.add_theme_color_override("font_color", Color(0.2, 0.8, 0.2))
cost_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
info.add_child(cost_label)
# Connect click handler
item.gui_input.connect(func(event): _on_item_gui_input(event, index))
# Add to container
items_container.add_child(item)
# Add separator if not last item
if index < builder.structures.size() - 1:
var sep = HSeparator.new()
items_container.add_child(sep)
func _on_item_gui_input(event, index):
if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
select_structure(index)
func select_structure(index):
if not builder or not "structures" in builder:
return
if index >= 0 and index < builder.structures.size():
print("Selecting structure index: ", index) # Debug print
# Update builder's selected structure
builder.index = index
if "update_structure" in builder:
builder.update_structure()
# Update our selected index
selected_index = index
# Update visual feedback
update_selection_highlight()
func update_selection_highlight():
print("Updating selection highlight. Selected index: ", selected_index) # Debug print
# Update selection highlight for all items
for i in range(items_container.get_child_count()):
var child = items_container.get_child(i)
if child is PanelContainer:
var style = child.get_theme_stylebox("panel") as StyleBoxFlat
var item_index = child.get_meta("structure_index")
if item_index == selected_index:
print("Highlighting item: ", item_index) # Debug print
# Selected item
style.border_color = Color(0.9, 0.9, 0.2) # Yellow border
style.bg_color = Color(0.3, 0.3, 0.3, 0.8) # Slightly lighter background
else:
# Unselected item
style.border_color = Color(0.3, 0.3, 0.3)
style.bg_color = Color(0.2, 0.2, 0.2, 0.8)
func get_structure_name(structure):
var file_name = structure.model.resource_path.get_file().get_basename()
var names = file_name.split("-")
var title = ""
for part in names:
if part.length() > 0:
title += part[0].to_upper() + part.substring(1) + " "
return title.strip_edges()
func is_mouse_over_menu() -> bool:
var mouse_pos = get_viewport().get_mouse_position()
return menu_panel.get_global_rect().has_point(mouse_pos) or toggle_button.get_global_rect().has_point(mouse_pos)