WIP checkpoint: Address review feedback: Implement Godot dedicated server scene + player spawning
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
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
|
||||
Reference in New Issue
Block a user