Перейти к основному содержимому

Волны и враги EternalTD

Файл волны в plugins/EternalTD/waves/ определяет весь состав врагов для уровня, включая обычные волны, волны внезапной смерти и опциональные сообщения перед волной или во время волны. Каждая конфигурация уровня ссылается на один файл волны через wavesConfigFile:.

Поля верхнего уровня​

ПолеТипПо умолчаниюПримечания
isEnabledbooltrueОтключённые файлы волн не загружаются; зависимые уровни не могут запуститься без этой конфигурации
baseMobAmountint20Количество по умолчанию, используемое, когда запись не указывает amount=
waveEntitiesсписок строкобязательноЗаписи врагов по волнам
suddenDeathWaveEntitiesсписок строкопциональноОпциональный циклический пул волн, запускаемый после исчерпания настроенных волн
prewaveMessagesсписок картопциональноСообщения по текущему счётчику: 0 при входе, N после окончания волны N
waveMessagesсписок картопциональноСообщения, показываемые при начале волны

Без записей внезапной смерти сессия заканчивается, когда переход к следующей волне не находит её в конфигурации. Обычно это происходит после последнего окна строительства, если игрок не перешёл дальше раньше.

Формат записи сущности волны​

Каждая строка в waveEntities (или suddenDeathWaveEntities) — это запись, разделённая двоеточиями:

- wave=1:entityType=ZOMBIE:healthMultiplier=1.0:amount=10
- wave=2:entityType=SKELETON:fast:treasure
- wave=5:entityType=WITHER_SKELETON:boss
- wave=8:entityType=PHANTOM:air:horde
КлючТипПримечания
waveintОбязательно. Номер волны, к которой относится эта запись
entityTypeenumОбязательно. Точное значение перечисления Bukkit EntityType; выбирайте живую сущность, которую можно создать
healthMultiplierdoubleОпционально, по умолчанию 1.0. Умножает максимальное здоровье сущности
amountintОпционально. Переопределяет baseMobAmount для этой записи
namestringОпционально. Строка имени. Разбирается и сохраняется, но сейчас не применяется к появившемуся мобу; вместо имени отображаются полосы здоровья и брони
resistфлагПомечает сущность как невосприимчивую к замедлению
fastфлагУвеличивает скорость передвижения
treasureфлагУдваивает золотую награду за убийство
bossфлагИспользует правило количества боссов ниже, масштабирование здоровья и отдельную полосу для каждого босса
hordeфлагУдваивает amount и уменьшает здоровье вдвое
airфлагСущность появляется в 4 блоках над путём и игнорирует наземные стены

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

Внутренняя математика количества​

  • К amount прибавляется wave / 10, затем результат усекается до целого. Поэтому amount=20 даёт 21 моба на волне 15 и 22 на волне 20.
  • Записи boss переопределяют amount в max(1, floor(wave / 10)).
  • Записи horde удваивают итоговое amount.
  • Помимо количества в записях, при создании волны добавляются копии. Индексы относятся к полностью развёрнутому списку мобов: парсер уже создал объект для каждого моба, поэтому строка amount=20 занимает 20 последовательных индексов. Для каждого чётного индекса i (0, 2, 4, ...), который существует в списке и меньше номера волны, добавляется одна копия моба с этим индексом. Боссы так не копируются. Дополнительное количество приблизительно равно ceil(min(waveNumber, listSize) / 2): 1 на волне 1, 2 на волнах 3–4, 5 на волнах 9–10, 8 на волнах 15–16, пока номер волны не превысит размер списка.

Интервал появления равен min(floor(200 / totalEntities), 20) тиков. Для обычного появления счётчик должен превысить удвоенный интервал; для ритма орды — удвоенный интервал после целочисленного деления пополам. Строгое сравнение добавляет ещё один тик счётчика: это не ровно двойной интервал. Ритм орды выбирается по первому оставшемуся мобу в списке, поэтому смесь обычных записей и орды может менять темп по мере изменения списка. Каждый моб при появлении случайно выбирает одну из стартовых клеток уровня.

Формат блока сообщений​

И prewaveMessages, и waveMessages принимают список карт:

prewaveMessages:
- wave: 0
message:
- "&aWelcome! Build a couple of basic towers along the path."
- wave: 4
message:
- "&6Heads up: a boss is coming next wave."

prewaveMessages[0] запускается при входе. После волны N, во время строительства для следующей, запускается prewaveMessages[N]: для предупреждения о волне 5 используйте ключ 4. waveMessages[N] запускается в начале волны N. Ключи используют отображаемый счётчик сессии, в том числе во внезапной смерти. Повторные записи сообщений с одним ключом заменяют предыдущие.

Жизненный цикл волны​

Сессия проходит через эти состояния:

  • STARTING — до того, как игрок впервые выполнит /etd start.
  • CONSTRUCTION_PHASE — 10-секундное окно строительства между оборонительными фазами.
  • DEFENSIVE_PHASE — враги появляются и идут по пути.
  • ENDING — игрок вышел, проиграл или у уровня закончились волны.

Обычный таймер строительства длится 10 секунд. /etd start или инструмент Next Wave позволяют перейти раньше, но требования обучения остаются обязательными (см. Особый случай уровня обучения). Начальная фаза ждёт действия запуска; обучение может продвинуться после принятия последнего требования.

Поведение окончания волны:

  • Завершение волны даёт waveNumber * 10 золота.
  • Если вы не потеряли жизней во время волны, вы получаете «Идеальный» бонус, равный половине бонуса волны (waveNumber * 10 * 0.5). Объявляемое число и фактически начисляемое золото теперь совпадают.
  • Каждая волна может выплатить награду только один раз — волна, которая почему-либо сообщит о завершении дважды, во второй раз игнорируется, и выплата не происходит вообще, как только сессия завершается.
  • После окончания волны объявляется непустая строка модификаторов следующей. Она берётся у первого настроенного врага, а не у всех записей смешанной волны.

Когда уровень заканчивается​

Сессия продвигается дальше, только если номер следующей волны существует как ключ в waveEntities, поэтому номера волн должны идти подряд начиная с 1. Когда следующего номера нет:

  • если файл определяет suddenDeathWaveEntities, забег переходит во внезапную смерть;
  • иначе уровень заканчивается немедленно, не засчитывая волну, которая так и не началась. Сохраняемый рекорд и заголовок о завершении используют последнюю волну, которую игрок действительно играл.

Внезапная смерть​

Если определён suddenDeathWaveEntities, уровень продолжает работать после списка настроенных волн.

Пул внезапной смерти перебирает настроенные номера волн. При необходимости EternalTD выбирает наименьший неотрицательный ключ, больший текущего ключа пула, или возвращается к наименьшему, если большего нет. Начальный счётчик пула равен 0: настроенный ключ 0 пропускается при первом проходе, если есть положительные ключи. Используйте положительные ключи; пропуски допустимы, а ключ 0 также не подходит для формулы здоровья ниже.

Отображаемый счётчик волн продолжает расти. Здоровье, броня, скорость, базовая награда и пороги splitterlings используют настроенное wave= выбранной записи, а не растут автоматически вместе с отображаемым счётчиком. Дополнительные копии во время выполнения и стихийные броски используют отображаемый счётчик. Например, записи пула 10 и 20 сохраняют эти значения масштабирования даже на отображаемой волне 100. Поиск следующей волны по-прежнему сначала проверяет обычную запись для каждого отображаемого номера, поэтому после раннего пропуска может быть использован более поздний обычный ключ.

Стихийная привязка сущностей волны​

Каждая волна имеет свою стихийную привязку, которая чередуется по волнам:

  • Волна 1 → Огонь
  • Волна 2 → Воздух
  • Волна 3 → Вода
  • Волна 4 → Земля
  • (затем повторяется)

Когда появляется обычная сущность волны, вероятность наследования её стихии равна отображаемому waveNumber / 100; начиная с отображаемой волны 100 бросок всегда успешен. Цветной тег показывает стихию. Splitterlings создаются отдельно и не получают этот бросок.

Правила стихийного урона обрабатываются классом DamageTower башни:

  • Атакующий с той же привязкой против цели с той же привязкой — урон 0.5× (сопротивление).
  • Противоположные одиночные стихии (Огонь против Воды, Воздух против Земли) — урон 2× (крит).
  • Слитые привязки следуют таблице ниже. Другие несовпадающие сочетания наносят урон 1×.
  • Башня без привязки наносит урон 1× всем.
Привязка башниУрон 2× противУрон 0.5× против
МагмаВоздух, ВодаЗемля, Огонь, Магма
ВзрывЗемля, ВодаВоздух, Огонь, Взрыв
ШтормОгонь, ЗемляВоздух, Вода, Шторм
СлизьОгонь, ВоздухЗемля, Вода, Слизь

Слитые привязки образуются, когда Basic Tower касаются вспомогательные башни с комбинирующимися стихийными привязками:

КомбинацияРезультирующее слияние
Огонь + ЗемляМагма
Огонь + ВоздухВзрыв
Вода + ВоздухШторм
Вода + ЗемляСлизь

Правила конфликтов действуют с такими ограничениями:

  • Земля + Воздух всегда возвращает башню к состоянию «Без привязки», независимо от остальных соседних башен.
  • Огонь + Вода возвращает к этому состоянию только при наличии трёх или более различных привязок. При ровно одной огненной и одной водной вспомогательной башне произвольно сохраняется одна из них: код выбирает первую привязку из неупорядоченного множества.
  • Вспомогательные башни без привязки (Spyglass, Gold Mine) не влияют на результат: они удаляются из множества при наличии любой стихийной башни.

Каждый пересчёт начинается с собственной стихии Basic Tower — «Без привязки», поэтому удаление последней стихийной вспомогательной башни возвращает башню к состоянию «Без привязки».

Масштабирование здоровья, брони и награды​

WaveEntity при появлении использует свой настроенный номер волны (или половину родительской волны для splitterling) и выполняет расчёт в следующем порядке:

  1. Базовое здоровье начинается с 0.5 и растёт с волной: health = 0.5 + ((wave - 1)^1.2) / 2.
  2. У боссов оно умножается на baseMobAmount × 0.5.
  3. У орды оно делится пополам.
  4. Здесь рассчитывается награда: ceil(health), до healthMultiplier и штрафа воздушных врагов. Для treasure она удваивается.
  5. maxHealth = health × healthMultiplier.
  6. У воздушных врагов maxHealth дополнительно делится пополам.
  7. С волны 10 броня равна maxHealth / 80.

Порядок шага 4 важен: healthMultiplier со значением 3.0 утраивает здоровье без изменения базовой награды. Воздушные враги дают такую же базовую награду, как наземные аналоги, несмотря на половинное здоровье. Gold Mine добавляет бонус при смертельном ударе, затем результат усекается до целого; маленькая награда может не получить дополнительного золота.

Скорость движения:

speed = 0.3 × (1 - slow)
× 1.5 × (1 - slow) if fast
× 0.5 if air
+ wave / 1000

Добавка wave / 1000 использует настроенную волну врага и прибавляется после множителей воздуха и замедления. Это значение управляет атрибутом скорости движения мобов. Для сущностей, не являющихся Mob, запасной путь вместо этого телепортирует на 0.1 блока к следующей точке каждый тик движения. Формула не задаёт универсальную скорость в блоках за тик.

Splitterlings (мини-споуны)​

С волны 20 и далее неполётные враги при смерти спавнят дополнительных малых мобов («splitterlings»).

  • Количество: floor(wave / 20) за убийство.
  • Если родитель был CAVE_SPIDER, splitterlings будут SILVERFISH. В противном случае это CAVE_SPIDER.
  • Splitterlings используют floor(parentWave / 2) и наследуют текущую позицию на маршруте. Они используют множитель здоровья 0.5 из своего конструктора и не наследуют родительские флаги или стихийную привязку. На достаточно высоких настроенных волнах они могут породить новых splitterlings при смерти.
  • Splitterlings не дают золота при смерти, а воздушные враги никогда не спавнят splitterlings.

Особый случай уровня обучения​

Уровень tutorial.yml использует жёстко заданный скрипт, привязанный к координатам его карты:

  • Требуемые типы башен нужно поставить на выделенных клетках или улучшить там, если соответствующая башня уже стоит. Разрешена продажа неправильной башни на требуемой клетке; посторонние установки, продажи и улучшения отклоняются, пока действует эта стадия скрипта. Последнее принятое требование немедленно запрашивает следующую волну.
  • Цветной бетон обозначает нужные клетки. Если доступны FMM и модель reddownarrow, красная стрелка над каждой клеткой указывает требуемую башню или улучшение.
  • При переходе к строительству после волн 1, 2 и 3 обучение добавляет +100 золота, после волны 4 — +200. Это дополнительный доход, а не гарантия возможности оплатить башни. Другие номера волн такой добавки не получают.
  • Цвет клетки соответствует требуемой башне: зелёный бетон для Basic Tower, коричневый для Barricade, белый для Sugar, бирюзовый для Spyglass, жёлтый для Beam Generator, красный для Explosion Factory, синий для Ice Box, серый для Armor Crusher, оранжевый для Gold Mine, фиолетовый для Obsidian Quarry и чёрный для остальных.
  • На каждой стадии скрипта зелёная полоса босса считает установленные требуемые башни в виде Build towers! done/total!. Каждые две минуты обучение напоминает, что выйти можно через /etd quit, и, пока требования не выполнены, повторяет текущее сообщение перед волной.

Жизни и золото игрока​

  • Стартовые жизни: 20.
  • Стартовое золото: 150.
  • Мобы-боссы стоят 5 жизней, если они достигают конца. Все остальные мобы стоят 1 жизнь.
  • Каждое переключение /etd cheat, включая отключение, сбрасывает золото до 10,000,000, а жизни до 1,000,000. Сессия навсегда теряет право на рекорд.
  • /etd setwave <n> изменяет счётчик сессии и защиту наград и помечает забег как читерский. Команда не заменяет активных врагов и не запускает указанную волну немедленно. Следующий переход использует n + 1.

Отслеживание рекордов​

Каждая конфигурация уровня сохраняет highscoreWave и highscorePlayerName. В конце нечитерской сессии плагин сохраняет новый номер волны в файл уровня и транслирует сообщение по всему серверу, если он побил предыдущий рекорд.