Files
modbison/scripts/world_list.gd
T

137 lines
4.3 KiB
GDScript

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.
const WORLD_CARD_SCENE := preload("res://scenes/world_card.tscn")
# 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/RefreshButton
@onready var back_button: Button = $VBoxContainer/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:
var card := VBoxContainer.new()
card.size_flags_horizontal = Control.SIZE_FILL
card.custom_minimum_size = Vector2(0, 80)
card.theme_override_constants/separation = 4
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 := 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 := world_dict.get("visibility", "public")
var created := 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)
# Subtle background for the card
var bg := ColorRect.new()
bg.color = Color(0.15, 0.15, 0.15, 1.0)
bg.size_flags_horizontal = Control.SIZE_FILL
bg.size_flags_vertical = Control.SIZE_FILL
bg.custom_minimum_size = Vector2(0, 80)
card.add_child(bg)
card.move_child(bg, 0)
return card