Ники, общий рейтинг (топ-10 по картам), таблица Tab, сдача в гонке, таймер Parkour
- Ники: профиль 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
This commit is contained in:
+107
@@ -0,0 +1,107 @@
|
||||
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))
|
||||
Reference in New Issue
Block a user