- Ники: профиль user://profile.cfg (client_id + ник), панель при первом запуске и по N; Net.nick_of(id); ники над призраками, в HUD/итогах гонки и таблицах - Рейтинг на сервере: лучшее время каждого игрока по картам race/parkour/metro, топ-10, user://leaderboard.json (атомарная запись, лимиты, проверка карты/комнаты/времени) - Таблица лидеров по Tab, доски топ-3 над порталами в хабе, итоги гонки с общим рейтингом - Гонка: сдача (G, удержание 1,5 с), RACE_GIVEUP; Parkour: таймер, финиш на вершине, R — перезапуск - Новые RPC (submit_profile, recv_nick, submit_record, recv_record_result, request_leaderboard, recv_leaderboard): клиент и сервер выкладываются вместе - Тесты: nicks_lb_test, race_giveup_test, parkour_run_test, nick_ui_test, ui_tour; README обновлён Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WxZo3ekR5m1af437Qzqwc1
108 lines
4.0 KiB
GDScript
108 lines
4.0 KiB
GDScript
class_name LbView
|
|
extends VBoxContainer
|
|
# Таблица рейтинга карты (топ-10 + «Вы: N-й»), данные берутся из Net.boards и обновляются сами по сигналу.
|
|
# Одна и та же таблица в панели Tab, в итогах гонки, итогах Parkour и будущей карты Metro: LbView.create("metro").
|
|
# Ники выводятся обычными Label (не RichText), поэтому «[b]» в чужом нике оформление не ломает.
|
|
|
|
const TEXT := Color(0.92, 0.88, 0.8)
|
|
const DIM := Color(0.62, 0.6, 0.56)
|
|
const MINE := Color(0.96, 0.76, 0.44)
|
|
const GOOD := Color(0.5, 0.85, 0.4)
|
|
|
|
var map_id := ""
|
|
var font_size := 18
|
|
var banner := false # сверху пометка «НОВЫЙ РЕКОРД КАРТЫ / ЛИЧНЫЙ РЕКОРД» (после submit_record)
|
|
var name_width := 190.0
|
|
|
|
static func create(id: String, size := 18, with_banner := false) -> LbView:
|
|
var v := LbView.new()
|
|
v.map_id = id
|
|
v.font_size = size
|
|
v.banner = with_banner
|
|
v.add_theme_constant_override("separation", 6)
|
|
return v
|
|
|
|
static func make_label(text: String, size: int, color: Color) -> Label:
|
|
var l := Label.new()
|
|
l.text = text
|
|
l.add_theme_font_size_override("font_size", size)
|
|
l.add_theme_color_override("font_color", color)
|
|
l.add_theme_constant_override("outline_size", 3)
|
|
l.add_theme_color_override("font_outline_color", Color(0, 0, 0, 0.85))
|
|
l.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
return l
|
|
|
|
# Тёмная полупрозрачная подложка в мрачном стиле
|
|
static func panel_style(margin := 20) -> StyleBoxFlat:
|
|
var sb := StyleBoxFlat.new()
|
|
sb.bg_color = Color(0.035, 0.04, 0.045, 0.86)
|
|
sb.border_color = Color(0.45, 0.30, 0.16, 0.9)
|
|
sb.set_border_width_all(2)
|
|
sb.set_content_margin_all(margin)
|
|
return sb
|
|
|
|
func _ready() -> void:
|
|
Net.leaderboard_updated.connect(_on_updated)
|
|
Net.record_result.connect(_on_record)
|
|
render()
|
|
|
|
func set_map(id: String) -> void:
|
|
map_id = id
|
|
render()
|
|
|
|
func _on_updated(id: String) -> void:
|
|
if id == map_id:
|
|
render()
|
|
|
|
func _on_record(id: String, _t: float, _rank: int, _pb: bool, _mr: bool) -> void:
|
|
if id == map_id:
|
|
render()
|
|
|
|
func render() -> void:
|
|
for c in get_children():
|
|
remove_child(c)
|
|
c.queue_free()
|
|
if banner:
|
|
var rec = Net.last_record.get(map_id)
|
|
if rec != null:
|
|
var txt := ""
|
|
if rec.map_record:
|
|
txt = "НОВЫЙ РЕКОРД КАРТЫ"
|
|
elif rec.personal:
|
|
txt = "НОВЫЙ ЛИЧНЫЙ РЕКОРД"
|
|
if txt != "":
|
|
var b := make_label(txt, font_size + 4, GOOD)
|
|
b.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
|
add_child(b)
|
|
var board = Net.boards.get(map_id)
|
|
if board == null:
|
|
add_child(make_label("—" if Net.connected else "Нет связи с сервером", font_size, DIM))
|
|
return
|
|
var top: Array = board.top
|
|
if top.is_empty():
|
|
add_child(make_label("Пока нет результатов", font_size, DIM))
|
|
var grid := GridContainer.new()
|
|
grid.columns = 3
|
|
grid.add_theme_constant_override("h_separation", 16)
|
|
grid.add_theme_constant_override("v_separation", 2)
|
|
add_child(grid)
|
|
for i in top.size():
|
|
var mine: bool = board.my_rank == i + 1
|
|
var col := MINE if mine else TEXT
|
|
var place := make_label("%d" % (i + 1), font_size, col if mine else DIM)
|
|
place.custom_minimum_size.x = font_size * 1.4
|
|
place.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
|
|
grid.add_child(place)
|
|
var nm := make_label(str(top[i][0]), font_size, col)
|
|
nm.custom_minimum_size.x = name_width
|
|
nm.clip_text = true
|
|
nm.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
|
grid.add_child(nm)
|
|
var tm := make_label(Net.fmt_time(float(top[i][1])), font_size, col)
|
|
tm.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
|
|
grid.add_child(tm)
|
|
if board.my_rank > top.size():
|
|
add_child(make_label("Вы: %d-й, %s" % [board.my_rank, Net.fmt_time(board.my_time)], font_size, MINE))
|
|
elif board.my_rank == 0 and not top.is_empty():
|
|
add_child(make_label("Вы: нет результата", font_size, DIM))
|