Карта Race: гонка на ногах по киберпанк-антиутопии, 8 игроков

- maps/race: трасса 633 м из Blender (три развилки: срез по контейнерам, каньон со стенами
  против нижней дороги, труба с лазерами против коридора), запечённый свет, небо, дождь, гул;
  фоновая песня (Ogg Vorbis, ~0,6 МБ) включается на «СТАРТ!»
- Правила (race_rules.gd): R — готов; когда готовы все — отсчёт 6 с, заморозка в слотах,
  ворота строго по порядку, сплиты, место, итоги, личный рекорд (user://race_best.cfg)
- Сеть: Net.submit_race / recv_race / race_event (RACE_READY, START, CP, FINISH, STATE).
  ВНИМАНИЕ: набор RPC изменён — клиент и сервер выкладываются вместе, старый клиент
  с новым сервером не работает (rpc node checksum failed)
- player.gd: input_frozen и place() для режимов карт; project.godot: действие ready (R)
- Портал RACE в хабе, пресет экспорта Race (include_filter для ресурсов, грузимых строкой)
- tools/test: двухклиентный тест гонки на локальном сервере, бот, облёт, проверка пакета
- README: правила гонки, события, проверки, заметки

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WxZo3ekR5m1af437Qzqwc1
This commit is contained in:
2026-10-28 17:28:05 +05:00
co-authored by Claude Sonnet 5.5
parent 2a6397da79
commit 9130202a51
31 changed files with 1775 additions and 4 deletions
+27
View File
@@ -18,7 +18,15 @@ const FLAG_MANTLE = 8
const FLAG_WALL_RIGHT = 16 # Бег по стене: стена справа (иначе слева)
const FLAG_AIR = 32 # В воздухе (не на полу, не на стене, не на уступе)
# Виды событий гонки (см. maps/race/race_rules.gd): сервер их не разбирает, только пересылает
const RACE_READY = 0 # a: 1 — готов, 0 — нет
const RACE_START = 1 # b: unix-время старта (секунды, часы отправителя)
const RACE_CP = 2 # a: индекс пройденных ворот, b: время гонки, с
const RACE_FINISH = 3 # b: время финиша, с
const RACE_STATE = 4 # «маяк» раз в секунду: a — биты состояния (см. race_rules.gd), b — время финиша или -1
signal peer_respawned(pos: Vector3) # Чужой игрок возродился в точке pos
signal race_event(id: int, kind: int, a: int, b: float) # Событие гонки от игрока id (кроме нас самих)
var is_server := false
var ghosts := {} # peer_id -> Ghost
@@ -165,6 +173,15 @@ func submit_life(dead: bool, pos: Vector3) -> void:
for other in _room(peer_maps.get(sender, ""), sender):
recv_life.rpc_id(other, sender, dead, pos)
# Клиент -> сервер: событие гонки (RACE_*). Сервер пересылает остальным на той же карте.
@rpc("any_peer", "reliable")
func submit_race(kind: int, a: int, b: float) -> void:
if not is_server:
return
var sender := multiplayer.get_remote_sender_id()
for other in _room(peer_maps.get(sender, ""), sender):
recv_race.rpc_id(other, sender, kind, a, b)
# ---------- Клиент ----------
func _server_url() -> String:
@@ -274,6 +291,16 @@ func recv_state(id: int, pos: Vector3, yaw: float, pitch: float, flags: int) ->
get_tree().current_scene.add_child(ghost)
ghost.set_target(pos, yaw, pitch, flags)
@rpc("authority", "reliable")
func recv_race(id: int, kind: int, a: int, b: float) -> void:
if map_ready:
race_event.emit(id, kind, a, b)
# Отправить своё событие гонки остальным в комнате (без сети — молча ничего)
func send_race(kind: int, a: int, b: float) -> void:
if connected and map_ready:
submit_race.rpc_id(1, kind, a, b)
@rpc("authority", "reliable")
func recv_left(id: int) -> void:
var ghost = ghosts.get(id)