extends Control @onready var net: Node = get_node_or_null("/root/NetAPI") var status_label: Label var list_box: VBoxContainer func _ready() -> void: _build_ui() _load_scores() func _build_ui() -> void: # background anchor_left = 0 anchor_top = 0 anchor_right = 1 anchor_bottom = 1 offset_left = 0 offset_top = 0 offset_right = 0 offset_bottom = 0 # Margin var margin := MarginContainer.new() margin.anchor_right = 1 margin.anchor_bottom = 1 margin.offset_left = 32 margin.offset_top = 32 margin.offset_right = -32 margin.offset_bottom = -32 add_child(margin) # Panel var panel := PanelContainer.new() panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL panel.size_flags_vertical = Control.SIZE_EXPAND_FILL margin.add_child(panel) # Root VBox var root := VBoxContainer.new() root.size_flags_horizontal = Control.SIZE_EXPAND_FILL root.size_flags_vertical = Control.SIZE_EXPAND_FILL root.add_theme_constant_override("separation", 14) panel.add_child(root) # Title var title := Label.new() title.text = "Leaderboard" title.add_theme_font_size_override("font_size", 44) title.size_flags_horizontal = Control.SIZE_EXPAND_FILL root.add_child(title) # Status status_label = Label.new() status_label.text = "Loading..." status_label.add_theme_font_size_override("font_size", 18) status_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL root.add_child(status_label) # Scroll var scroll := ScrollContainer.new() scroll.size_flags_horizontal = Control.SIZE_EXPAND_FILL scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL root.add_child(scroll) # List VBox inside scroll list_box = VBoxContainer.new() list_box.size_flags_horizontal = Control.SIZE_EXPAND_FILL list_box.size_flags_vertical = Control.SIZE_EXPAND_FILL list_box.add_theme_constant_override("separation", 6) scroll.add_child(list_box) # Footer with back button var footer := HBoxContainer.new() footer.size_flags_horizontal = Control.SIZE_EXPAND_FILL root.add_child(footer) footer.add_spacer(true) var back := Button.new() back.text = "Back" back.pressed.connect(func(): get_tree().change_scene_to_file("res://scenes/MainMenu.tscn")) footer.add_child(back) footer.add_spacer(true) func _clear_list() -> void: for c in list_box.get_children(): c.queue_free() func _load_scores() -> void: if net == null: status_label.text = "NetAPI nav atrasts (/root/NetAPI)." return # get_scores ir async (atgriež GDScriptFunctionState), tāpēc obligāti await _fetch_scores() func _fetch_scores() -> void: # palaid asynchroni bez bloķēšanas call_deferred("_fetch_scores_deferred") func _fetch_scores_deferred() -> void: var resp = await net.call("get_scores", 50) if resp == null: status_label.text = "No response." return if resp is Dictionary and resp.get("ok", false) == false: status_label.text = str(resp.get("error", "Failed")) return var arr: Array = [] if resp is Dictionary: arr = resp.get("scores", []) if arr.is_empty(): status_label.text = "No scores yet." _clear_list() return status_label.text = "" _render_table(arr) func _render_table(arr: Array) -> void: _clear_list() # Header row list_box.add_child(_make_row("#", "USERNAME", "LVL", "SCORE", "TIME", "RESULT", true)) var idx := 1 for r in arr: if typeof(r) != TYPE_DICTIONARY: continue var username := str(r.get("username", "guest")) var level := str(int(r.get("level", 1))) var score := str(int(r.get("score", 0))) var time_sec := str(int(r.get("match_time_sec", r.get("match_time", 0)))) var result := str(r.get("result", "")) list_box.add_child(_make_row(str(idx), username, level, score, time_sec + "s", result, false)) idx += 1 func _make_row(a: String, b: String, c: String, d: String, e: String, f: String, header: bool) -> Control: var row := HBoxContainer.new() row.size_flags_horizontal = Control.SIZE_EXPAND_FILL row.add_theme_constant_override("separation", 10) # "table" columns with fixed-ish widths row.add_child(_cell(a, 40, header)) row.add_child(_cell(b, 260, header)) row.add_child(_cell(c, 60, header)) row.add_child(_cell(d, 90, header)) row.add_child(_cell(e, 70, header)) row.add_child(_cell(f, 90, header)) return row func _cell(text: String, width: int, header: bool) -> Control: var l := Label.new() l.text = text l.clip_text = true l.custom_minimum_size = Vector2(width, 0) l.size_flags_horizontal = Control.SIZE_FILL l.size_flags_vertical = Control.SIZE_FILL l.vertical_alignment = VERTICAL_ALIGNMENT_CENTER if header: l.add_theme_font_size_override("font_size", 18) else: l.add_theme_font_size_override("font_size", 16) return l