extends Control ## World list screen — browses public worlds via GET /api/worlds. ## ## Displays a scrollable list of world cards (name + description). ## Handles loading, empty list, errors, and refresh. # UI references @onready var scroll_container: ScrollContainer = $VBoxContainer/ScrollContainer @onready var world_list: VBoxContainer = $VBoxContainer/ScrollContainer/WorldList @onready var loading_label: Label = $VBoxContainer/LoadingLabel @onready var error_label: Label = $VBoxContainer/ErrorLabel @onready var refresh_button: Button = $VBoxContainer/HeaderHBox/RefreshButton @onready var back_button: Button = $VBoxContainer/HeaderHBox/BackButton @onready var empty_label: Label = $VBoxContainer/EmptyLabel func _ready() -> void: refresh_button.disabled = false back_button.disabled = false loading_label.visible = false error_label.visible = false empty_label.visible = false world_list.visible = false _refresh_worlds() func _on_refresh_button_pressed() -> void: _refresh_worlds() func _on_back_button_pressed() -> void: get_tree().change_scene_to_file("res://scenes/main_menu.tscn") ## Fetch the world list from the backend and update the UI. func _refresh_worlds() -> void: # Reset UI state loading_label.visible = true error_label.visible = false empty_label.visible = false world_list.visible = false refresh_button.disabled = true # Clear previous world cards for child in world_list.get_children(): child.queue_free() var result := await ApiClient.get_worlds() loading_label.visible = false refresh_button.disabled = false if not result.ok: # Show error message (401, network failure, etc.) error_label.text = "Error: %s" % result.detail error_label.visible = true empty_label.visible = false world_list.visible = false return var data = result.data if not data is Array: error_label.text = "Unexpected response format." error_label.visible = true empty_label.visible = false world_list.visible = false return if data.is_empty(): empty_label.text = "No worlds yet. Check back later!" empty_label.visible = true error_label.visible = false world_list.visible = false return # Render world cards world_list.visible = true empty_label.visible = false error_label.visible = false for world_dict in data: if not world_dict is Dictionary: continue var card := _create_world_card(world_dict) world_list.add_child(card) ## Create a visual card for a single world entry. func _create_world_card(world_dict: Dictionary) -> Control: # PanelContainer for the background, not a ColorRect moved to child index # 0 of a VBoxContainer -- a VBoxContainer lays out ALL its children in a # vertical sequence, it has no concept of "float this one child behind # the others as an overlay". That version rendered as an empty dark bar # sitting above the text, not a background behind it. var panel := PanelContainer.new() panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL panel.custom_minimum_size = Vector2(0, 80) var style := StyleBoxFlat.new() style.bg_color = Color(0.15, 0.15, 0.15, 1.0) style.content_margin_left = 12 style.content_margin_right = 12 style.content_margin_top = 8 style.content_margin_bottom = 8 panel.add_theme_stylebox_override("panel", style) var card := VBoxContainer.new() card.size_flags_horizontal = Control.SIZE_EXPAND_FILL card.add_theme_constant_override("separation", 4) panel.add_child(card) var name_label := Label.new() name_label.text = world_dict.get("name", "Unnamed World") name_label.autowrap_mode = TextServer.AUTOWRAP_WORD name_label.size_flags_horizontal = Control.SIZE_FILL name_label.size_flags_vertical = Control.SIZE_SHRINK_BEGIN name_label.add_theme_font_size_override("font_size", 18) var desc_label := Label.new() var description: String = str(world_dict.get("description", "")) desc_label.text = description if description != "" else "No description." desc_label.autowrap_mode = TextServer.AUTOWRAP_WORD desc_label.size_flags_horizontal = Control.SIZE_FILL desc_label.size_flags_vertical = Control.SIZE_EXPAND_FILL desc_label.add_theme_font_size_override("font_size", 14) desc_label.add_theme_color_override("font_color", Color(0.7, 0.7, 0.7, 1.0)) var meta_label := Label.new() var visibility: String = str(world_dict.get("visibility", "public")) var created: String = str(world_dict.get("created_at", "")) meta_label.text = "Visibility: %s%s" % [visibility, " | Created: " + created if created != "" else ""] meta_label.autowrap_mode = TextServer.AUTOWRAP_WORD meta_label.size_flags_horizontal = Control.SIZE_FILL meta_label.size_flags_vertical = Control.SIZE_SHRINK_BEGIN meta_label.add_theme_font_size_override("font_size", 12) meta_label.add_theme_color_override("font_color", Color(0.5, 0.5, 0.5, 1.0)) card.add_child(name_label) card.add_child(desc_label) card.add_child(meta_label) return panel