Files
my-world/tools/test/parkour_run_test.gd
aibrashandClaude Sonnet 5.5 666bebfb60 Ники, общий рейтинг (топ-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
2026-10-29 12:09:33 +05:00

106 lines
5.1 KiB
GDScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
extends Node
# Забег Parkour «по существу»: настоящая карта, игрок, сервер (запускает parkour_run_test.py).
# Старт-линия -> таймер идёт, смерть его не останавливает, финиш у вершины, слишком быстрое время сервер не принимает,
# R перезапускает забег, нормальное время (старт сдвинут на 45 с назад — иначе пришлось бы бежать ~минуту) попадает в рейтинг.
var ok := true
var level
var p
func check(cond: bool, text: String) -> void:
print((" ДА " if cond else " НЕТ ") + text)
if not cond:
ok = false
func wait(s: float) -> void:
await get_tree().create_timer(s).timeout
func _ready() -> void:
_run.call_deferred()
func press_r() -> void:
var ev := InputEventKey.new()
ev.physical_keycode = KEY_R
ev.keycode = KEY_R
ev.pressed = true
Input.parse_input_event(ev)
await get_tree().process_frame
ev = ev.duplicate()
ev.pressed = false
Input.parse_input_event(ev)
func _run() -> void:
level = (load("res://maps/parkour/parkour.tscn") as PackedScene).instantiate()
add_child(level)
await wait(0.5)
p = get_tree().get_first_node_in_group("local_player")
check(await _wait_ready(), "подключились к серверу, вошли в комнату parkour")
check(not level.run_active and not level.run_done, "до линии таймер не идёт")
await wait(1.0)
check(not level.run_active, "стоим на старте — таймер не идёт")
p.global_position = Vector3(0, 1.25, -11.0)
p.velocity = Vector3.ZERO
p.rotation.y = 0.0
await wait(0.3)
Input.action_press("move_forward")
Input.action_press("sprint")
var t0 := Time.get_ticks_msec()
while not level.run_active and Time.get_ticks_msec() - t0 < 4000:
await get_tree().physics_frame
Input.action_release("move_forward")
Input.action_release("sprint")
check(level.run_active and p.global_position.z < level.START_LINE_Z + 1.0, "пересекли линию z=-14 -> таймер пошёл (z=%.1f)" % p.global_position.z)
await wait(1.2)
var e1: float = level.run_elapsed()
check(e1 > 1.0 and level._timer_label.text != "00:00.000", "таймер считает: %s" % level._timer_label.text)
p.die()
await wait(3.2)
check(level.run_active and not p.is_dead and level.run_elapsed() > e1 + 2.5, "смерть и возрождение таймер не останавливают (%.1f с)" % level.run_elapsed())
# слишком быстрый финиш: таймер остановится, сервер время не примет
var D = level.Data
p.global_position = D.PORTAL + Vector3(0, 1.4, 3.0)
p.velocity = Vector3.ZERO
await wait(1.0)
check(level.run_done and not level.run_active and level._results_panel.visible, "у вершины финиш: таймер встал на %s, показаны итоги" % level._timer_label.text)
await wait(0.8)
check(not Net.last_record.has("parkour"), "время %.1f с короче допустимого сервер отверг (ответа нет)" % level.run_time)
var n_before: int = Net.boards["parkour"].top.size() if Net.boards.has("parkour") else 0
# R: заново
await press_r()
await wait(0.4)
check(not level.run_active and not level.run_done and level._timer_label.text == "00:00.000" and p.global_position.distance_to(D.START) < 1.0,
"R: телепорт на старт, таймер сброшен")
# второй забег: линия, затем «состарим» старт на 45 с и финишируем
p.global_position = Vector3(0, 1.25, -11.0)
p.velocity = Vector3.ZERO
await wait(0.3)
Input.action_press("move_forward")
Input.action_press("sprint")
t0 = Time.get_ticks_msec()
while not level.run_active and Time.get_ticks_msec() - t0 < 4000:
await get_tree().physics_frame
Input.action_release("move_forward")
Input.action_release("sprint")
check(level.run_active, "после R линия снова запускает таймер")
level.run_start_tick -= 45000
p.global_position = D.PORTAL + Vector3(0, 1.4, 3.0)
p.velocity = Vector3.ZERO
await wait(1.5)
var rec = Net.last_record.get("parkour")
check(rec != null and absf(rec.time - level.run_time) < 0.01 and rec.rank >= 1 and rec.personal, "сервер принял %.2f с: %s" % [level.run_time, str(rec)])
var b = Net.boards.get("parkour")
check(b != null and b.my_rank >= 1 and absf(b.my_time - level.run_time) < 0.01, "запись в рейтинге Parkour: место %s, время %.2f" % [str(b.my_rank) if b else "?", b.my_time if b else 0.0])
check(level._results_panel.visible, "панель итогов с рейтингом показана")
await wait(8.5)
check(not level._results_panel.visible, "итоги скрылись через ~8 с, дальше свободная игра")
print("\nПАРКУР ПРОЙДЕН" if ok else "\nПАРКУР ПРОВАЛЕН")
get_tree().quit(0 if ok else 1)
func _wait_ready() -> bool:
var t := 0.0
while not (Net.connected and Net.map_ready):
await wait(0.1)
t += 0.1
if t > 8.0:
return false
return true