Боты и очередь старта

В небе, кроме самого пилота, летают «другие пилоты» — боты. Они не привязаны к маршруту, просто летают: ищут термики, парят у склона, перелетают от одной цели к другой. Половина из них уже в воздухе, когда начинается полёт — небо не пустое с первой секунды. В сетевой игре те же боты видны всем, и на старт выстраивается общая очередь: сначала живые пилоты, потом боты.

Кто такие боты

Число ботов — настройка «Другие пилоты в небе», 0–20 (bots.count, по умолчанию 4, действует со следующего полёта). У каждого бота своё крыло (случайно из списка configs/bots.json → wings, все примерно поровну; советские крылья в небе редкость — только «Апогей»), масса пилота 65–95 кг и расцветка паруса из палитры без брендов (красный, оранжевый, жёлтый, зелёный, бирюзовый, синий, фиолетовый, малиновый, графит). Все параметры — от сида полёта (configs/bots.json → seed, смешивается с местом старта), поэтому один и тот же полёт (то же место, сид) даёт одних и тех же ботов.

Модель полёта у ботов та же, что у игрока — FlightModel и Atmosphere.air_velocity_at, — просто шаг физики реже, если бот далеко: 30 Гц рядом с игроком (< 1500 м) и на земле, 10 Гц дальше, 4 Гц дальше 4000 м.

Как боты летают

«Мозги» бота — BotPilot (scripts/game/bot_pilot.gd), тот же класс, что у бота-маршрутника из исследований проходимости трасс (docs/plan/atmosphere). Бот «чувствует» только то, что доступно живому пилоту: вариометр, высоту, AGL, курс, крен, позицию, путевую скорость — карту термиков атмосферы (thermals_near) не подглядывает. Режимы полёта:

  • CRUISE — переход по расчёту МакКриди;
  • ENTER — усреднённый вариометр выше порога (0,5 м/с сверх собственного снижения; в режиме «спасения» ниже — 0,3 м/с) → вираж 35–40° в сторону, где был подъём;
  • CIRCLE — центровка термика по правилу Райхмана (сильнее подъём — площе вираж, слабее — круче, круг сдвигается к максимуму), крен 8–50° (номинально 38°), карта подъёма помнится 12 с;
  • PROBE — проверка стороны термика после «пощупал и потерял»;
  • RIDGE — «восьмёрка у склона»: два смежных витка над одной рабочей точкой, разворот всегда от склона к долине; включается, если встречный ветер на старте ≥ 4 м/с, парит 60–300 с (случайно) или пока не наберёт 250 м над стартом, затем уходит в перелёты; ниже 25 м AGL уходит от склона в долину;
  • ниже 300 м AGL — режим «спасения»: скорость минимального снижения, берёт любой подъём, порог входа ниже обычного.

Без цели («другие пилоты в небе», класс WanderPilot, scripts/game/wander_pilot.gd) бот перелетает между случайными точками в круге радиусом 3500 м вокруг старта (перелёт 800–2500 м, первый — 1200 м от склона в долину по курсу старта); если в конфиге включено — цель перелёта выбирается под растущим облаком в секторе ±30° от курса, как пилот читает небо. В одном термике кружат в одну сторону — её задаёт первый, кто вошёл (это может быть и игрок): круги ближе 300 м к центру «занятого» термика в последние 60 с считаются тем же термиком.

Боты расходятся друг с другом и с игроком по прогнозу сближения (configs/bots.json → separation, проверка 4 раза в секунду): не ближе 50 м по горизонтали и 15 м по высоте друг с другом (прогноз на 12 с вперёд), не ближе 90 м по горизонтали и 30 м по высоте от игрока (прогноз на 20 с) — столкновений ботов с игроком в игре нет вообще. Ниже 25 м AGL (разбег, посадка) расхождение не считается — там всё равно занята земля.

Имена над ботами

Над каждым ботом — табличка с именем (NameTag, Label3D-билборд): постоянный размер на экране (22 пикс. при высоте окна 1080, дальше — пропорционально FOV), полностью видно до 250 м, гаснет к 900 м (на земле — уже к 120 м, чтобы не рябило на старте), за рельефом не видно (табличка пишет глубину). Имена — из configs/bot_names.json, отдельный пул на каждый язык интерфейса (ru, en), без повторов в одном полёте, порядок — от сида полёта; при смене языка боты сразу берут имена из нового пула в том же порядке. Настройка «Имена пилотов» (bots.names.show, по умолчанию включена) применяется сразу, даже из паузы.

Имена над ботами

Настройка в экране «Настройки»:

Настройка «Имена пилотов»

Половина уже в воздухе

Если сразу поставить всех ботов в очередь на старт позади игрока, первым лететь в пустое небо скучно. Поэтому половина ботов (округление вниз, но не меньше одного — при одном боте в воздухе будет он) к началу полёта уже кружит впереди старта: 0e07d62. Появляются они 250–700 м впереди старта по курсу разбега, в стороны — до 300 м, по высоте — 200–450 м над рельефом (и не ниже старта плюс этот запас), с курсом ±60° от курса старта — случайно, но детерминированно (сид полёта). Остальные боты ждут в очереди на старт за игроком и взлетают после его отрыва.

Очередь на старт

Боты, ожидающие на земле, стоят с крылом на ровных площадках позади старта — не в коридоре разбега игрока, не у камней, кустов и деревьев (scripts/game/bot_pilots.gd → find_spots): перебор сетки с шагом 3 м, площадки не круче 8° (не хватает — 14°, затем любые), не ближе 12 м друг к другу (размах крыла ~10 м) и не ближе 9 м к игроку. Пока игрок на земле — боты ждут; после его отрыва боты выходят на старт по одному раз в 30 с (launch.interval_s) и разбегаются той же механикой, что и игрок (BotAgent). Сел не долетев или разбился — бот стоит минуту (landed_stand_s), затем убирается и снова встаёт в очередь.

В сетевой игре очередь общая для живых пилотов и ботов (scripts/game/net_queue.gd, NET-43): места ожидания те же, что у ботов в одиночной игре (место 0 — сам старт), но правила — как у живой очереди на настоящем старте:

  1. Сначала живые пилоты, потом боты. Живой, вернувшийся в очередь, встаёт перед первым ботом (кроме бота, который уже идёт на старт или бежит первым — его не сдвигают).
  2. Бежать может только первый. У остальных разбег заблокирован, пока они не станут первыми; взлетел — следующий шагает вперёд.
  3. Простоял первым дольше минуты — в конец. Чтобы никто не держал всю очередь, застряв на месте.
  4. После посадки, аварии или сорванного старта — в конец текущей очереди (перед ботами; если очередь пуста — сразу первым). Это осознанно: лёгкое наказание за неудачный старт и живая очередь, как на настоящем старте — не «баг для починки».
  5. Обходов очереди нет: если очередь мешает, решение — создать сетевую зону без ботов (0 в настройках) и взлетать друг за другом.

Очередь ведёт ведущий зоны (не путать со «старшим» в бытовом смысле — это тот, кто технически держит часы и рассылает ZoneState.queue раз в секунду): он не спрашивает остальных клиентов, а сам видит их фазы (стоит/идёт рядом со стартом — в очереди; бежит — в очереди, не трогать; в воздухе, на буксире, сел, разбился или ушёл дальше 250 м от старта — вне очереди). Каждый клиент сам идёт к своему месту по номеру в очереди; при смене ведущего новый продолжает с последней разосланной очереди, а таймер простоя первого начинается заново.

Очередь на старт

Границы модели

  • Столкновений ботов с игроком нет вообще (расхождение только «на бумаге» — по прогнозу сближения); столкновения бот-бот не проверялись отдельно, расхождение то же 50 м/15 м.
  • Посадки у ботов формально нет: касание земли — не авария и не полноценная посадка, а пауза на минуту с последующей постановкой в очередь.
  • Обход рельефа сбоку и настоящее лавирование между несколькими термиками одновременно не моделируются — только вход/центровка/выход по вариометру, как у честного бота-маршрутника (без подглядывания в карту термиков).
  • «Восьмёрка у склона» включается по одному порогу — встречному ветру на старте ≥ 4 м/с; боковой и попутный ветер на старте отдельно не учитываются.
  • Обходных путей в очереди нет намеренно (см. выше) — это игровое решение, а не недосмотр.

Что рассматривали и почему так

  • Все боты в очереди с самого начала — отклонили: первым лететь в пустое небо неинтересно, добавили «уже в воздухе» половину (0e07d62).
  • Приоритетный вход без очереди («догнать» друга без ожидания старта) — реализован отдельно как механика «Догнать», а не как обход очереди на старт: сама очередь на старт правил не имеет исключений.
  • Настоящая посадка у ботов (с ЛП, разбором полёта) — не сделана: минута паузы после касания земли проще и не требует у бота логики захода на посадку.

Подробнее