Race 2 «Верхние ярусы», фаза 1: карта, вентиляторы, ускоряющие полосы, правила общие с Race
- maps/race2/: сцена, race2.gd (ворота со световыми столбами, лазеры, вентиляторы, полосы, срыв, дождь, гул), данные и модель из Blender models/race_02 (раскладка layout.py -> build_race.py -> bake.py; свет пока запечён грубо, небо и музыка — копии Race) - objects/vent.gd, objects/boost_strip.gd + хуки apply_vent()/apply_boost() в player/player.gd (MAX_SPEED и остальная физика не тронуты) - race_rules.gd берёт данные у карты (общий код правил для Race и Race 2) - портал RACE 2 в хабе, пресет экспорта Race2 + export_all.sh, Net.TIMED_MAPS/MAP_TITLES для race2 (RPC не менялись; серверу нужен deploy_server.sh) - тесты: race_game_test.sh с MAP=race2 (бот проводит игроков телепортами), race2_check.txt - README: раздел «Карта Race 2», результаты ботов по веткам Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
+54
-3
@@ -8,9 +8,13 @@ extends Node
|
||||
# --fall-at-gate=<n>: после ворот n упасть в пустоту (проверка срыва ниже ворот)
|
||||
# --gu1=<план>, --gu2=<план>: сдача в круге 1 / 2. План: «cancel@1,giveup@2» — когда пройдено столько ворот,
|
||||
# держать G 1,0 с и отпустить (отмена) / держать до сдачи (проверка race_giveup_test.sh)
|
||||
# --map=race2: карта «Гонка 2». Бежать по её маршруту без физики бегуна нельзя (вентиляторы, лазеры, стены), поэтому бот
|
||||
# «проводит» игрока через ворота телепортами по ~0,35 м за кадр (меньше TELEPORT_JUMP: правила считают это бегом); проверяются правила, а не проходимость
|
||||
# (проходимость карты — боты в Blender models/race_02/tests/).
|
||||
# Факты печатаются строками «[имя] …»; сверяет их tools/test/race_game_test.sh.
|
||||
|
||||
const Data = preload("res://maps/race/race_data.gd")
|
||||
var Data = null # данные карты: race_data.gd или (--map=race2) race2_data.gd
|
||||
var map_id := "race"
|
||||
|
||||
var label := "?"
|
||||
var delay := 0.0
|
||||
@@ -46,6 +50,7 @@ var gu_after_cancel := false
|
||||
|
||||
func _ready() -> void:
|
||||
for a in OS.get_cmdline_user_args():
|
||||
if a.begins_with("--map="): map_id = a.substr(6)
|
||||
if a.begins_with("--name="): label = a.substr(7)
|
||||
if a.begins_with("--delay="): delay = float(a.substr(8))
|
||||
if a.begins_with("--expect="): expect = int(a.substr(9))
|
||||
@@ -57,7 +62,11 @@ func _ready() -> void:
|
||||
for e in a.substr(6).split(","):
|
||||
var q := e.split("@")
|
||||
gu_plan[int(a.substr(4, 1))].append({"mode": q[0], "gate": int(q[1])})
|
||||
route = JSON.parse_string(FileAccess.get_file_as_string("res://tools/test/race_route.json")).safe
|
||||
if map_id == "race2":
|
||||
Data = load("res://maps/race2/race2_data.gd")
|
||||
else:
|
||||
Data = load("res://maps/race/race_data.gd")
|
||||
route = JSON.parse_string(FileAccess.get_file_as_string("res://tools/test/race_route.json")).safe
|
||||
|
||||
func gp(w: Array) -> Vector3:
|
||||
return Vector3(w[0], w[2], -w[1])
|
||||
@@ -76,7 +85,7 @@ func say(text: String) -> void:
|
||||
|
||||
func _physics_process(_delta: float) -> void:
|
||||
var sc := get_tree().current_scene
|
||||
if sc == null or sc.name != "Race":
|
||||
if sc == null or sc.name != ("Race2" if map_id == "race2" else "Race"):
|
||||
return
|
||||
rules = sc.get_node_or_null("Rules")
|
||||
p = get_tree().get_first_node_in_group("local_player")
|
||||
@@ -220,7 +229,49 @@ func _race() -> void:
|
||||
return
|
||||
_drive()
|
||||
|
||||
func _drive_tele() -> void:
|
||||
var g: int = rules.passed.get(rules.my_id, 0)
|
||||
if g >= Data.CHECKPOINTS.size() or p.is_dead:
|
||||
return
|
||||
var gate: Array = Data.CHECKPOINTS[g]
|
||||
var yaw := deg_to_rad(gate[2])
|
||||
var fwd := Vector3(-sin(yaw), 0, -cos(yaw))
|
||||
var tgt: Vector3 = gate[0] + fwd * 1.5
|
||||
var step := 0.35 # м за кадр (~21 м/с): круг ~60 с, как у настоящей гонки — иначе зритель C не успеет зайти в разгар круга
|
||||
# лазерный мост: идём медленно (12 м/с) и на ближних подступах к воротам выбираем полосу, где лазер выключен в момент пролёта
|
||||
var pos: Vector3 = p.global_position
|
||||
var lane := -1.0
|
||||
if pos.x > 105.0 and pos.x < 115.0 and pos.z < -535.0 and pos.z > -615.0:
|
||||
step = 0.2
|
||||
for l in Data.LASERS:
|
||||
if l[0].x < 110.0: # левая половина; правая — в паре
|
||||
var dz: float = pos.z - l[0].z # > 0 — ворота ещё впереди (идём в -Z)
|
||||
if dz > -1.0 and dz < 8.0:
|
||||
var t := Time.get_unix_time_from_system() + maxf(dz, 0.0) / 12.0
|
||||
var left_on: bool = fposmod(t + l[5], l[3]) < l[4] or fposmod(t + 0.4 + l[5], l[3]) < l[4] or fposmod(t - 0.4 + l[5], l[3]) < l[4]
|
||||
var right_on: bool = fposmod(t + 1.5 + l[5], l[3]) < l[4] or fposmod(t + 1.9 + l[5], l[3]) < l[4] or fposmod(t + 1.1 + l[5], l[3]) < l[4]
|
||||
lane = 112.0 if left_on else 108.0
|
||||
if left_on and right_on:
|
||||
lane = -2.0 # обе полосы заняты — стоим
|
||||
if lane == -2.0:
|
||||
return
|
||||
if lane > 0.0:
|
||||
p.global_position.x = lane
|
||||
pos.x = lane
|
||||
tgt.x = lane
|
||||
var d: Vector3 = tgt - pos
|
||||
if tgt.y - pos.y > 2.0: # ворота выше (крыша башни): сначала поднимаемся вертикально — иначе упрёмся в стену
|
||||
d = Vector3(0, minf(step, tgt.y - pos.y + 1.5), 0)
|
||||
elif d.length() > step:
|
||||
d = d.normalized() * step
|
||||
p.global_position = pos + d
|
||||
p.velocity = Vector3.ZERO
|
||||
p.rotation.y = yaw
|
||||
|
||||
func _drive() -> void:
|
||||
if map_id == "race2":
|
||||
_drive_tele()
|
||||
return
|
||||
var pos: Vector3 = p.global_position
|
||||
if fr % 120 == 0 and OS.get_cmdline_user_args().has("--bot-log"):
|
||||
say("бег: wi=%d поз=(%.1f, %.1f, %.1f) v=(%.1f, %.1f) пол=%s" % [wi, pos.x, pos.y, pos.z, p.velocity.x, p.velocity.z, p.is_on_floor()])
|
||||
|
||||
Reference in New Issue
Block a user