Skip to content

Волны спавна 2.3

Волны - это последовательность волн врагов с паузами и условиями завершения: зашли в зону → пауза → волна 1 → убили всех → пауза → волна 2 → ... → победа (штатная WIN-цепочка точки). Каждая волна - именованная переиспользуемая настройка в папке Waves/, на которую могут ссылаться разные триггеры.

Что дают волны

  • Сценарные зоны: арена из 3 волн с усилением, финальный босс, зачистка по таймеру.
  • Переиспользование: волна boss подключается в нескольких точках; правится в одном месте.
  • Гибкий состав: внутри волны - обычный spawnList (формат точки) и/или @group-ссылки со всеми условиями групп (время, погода, шанс).
  • Условия завершения: allDead (всех убили), timer (N секунд), remainingAlive (осталось ≤ N).

Как это работает

Волновая точка не использует регулярный цикл спавна (cooldown, spawnLoopInside, spawnMin/spawnMax напрямую). Последовательностью владеет автомат на триггере:

IDLE (ожидание входа)
→ пауза waveFirstDelay
→ волна 1 → ... → волна N
→ WIN
→ DONE
  ├─ "none"    → DONE до рестарта/reload
  └─ "restart" → следующий вход начинает заново
  • Старт - первый вход игрока в зону (штатные пред-проверки точки действуют: isDisabled, triggerWorkingTime, triggerDependencies, triggerRequireItem).
  • Завершение волны проверяется каждую секунду: по счётчику живых сущностей точки (allDead, remainingAlive) или одноразовым таймером (timer). Лут в подсчёте не участвует.
  • Уведомления получают игроки внутри зоны (заголовок/время/иконка - точечные, из конфига точки). Дефолтные тексты: "Волна {current} из {total}" и "Волна {current} из {total} через {seconds} сек.".
  • После последней волны - штатная WIN-цепочка: нотификация notificationTextWin, маппинг addOnWin, triggersToEnableOnWin, triggerDisableOnWin, событие OnTriggerWin.

Отмена при выходе игроков

По умолчанию (waveContinueOnLeave = false) выход всех игроков из зоны отменяет волны: следующий вход начнёт с волны 1. Это согласуется с triggerCleanupOnLeave - чистка живности не запускает каскад волн на пустой зоне. При waveContinueOnLeave: true волны продолжаются до завершения даже в пустой зоне (семантика BossHorde).

Файлы волн

Волны хранятся в папке $profile:MPG_Spawner/Waves/. Каждый файл - массив волн:

$profile:MPG_Spawner/
├── Config.json
├── Points/
│   └── ...точки...
├── Groups/
│   └── ...группы...
└── Waves/                  ← папка волн
    ├── Waves_Example.json
    └── ...

Автоскан папки

Папка Waves/ сканируется целиком при запуске сервера - все *.json грузятся автоматически. Список файлов в Config.json (как pointsConfigs для точек) не нужен. При первом старте мод сам создаёт папку и пример Waves_Example.json.

Волна с дублирующимся waveName (в том же или другом файле) - ошибка: в лог пишется ERROR, загружается первая встретившаяся волна с этим именем, дубликат пропускается.

Поля волны

json
[
  {
    "waveName": "scouts",
    "isDisabled": false,
    "spawnList": [
      "@dayZombies",
      "ZmbM_JournalistNormal_Blue|0.6"
    ],
    "groupSelectMode": "",
    "spawnMin": 3,
    "spawnMax": 5,
    "completion": {
      "type": "allDead",
      "value": 0
    },
    "delayAfter": "10-15",
    "cleanBefore": false,
    "notificationText": ""
  }
]

waveName

waveName - уникальное имя волны, обязательное поле. По нему на волну ссылаются из waveList точки.

Дубликат waveName (в том же или другом файле) - ошибка: в лог пишется ERROR, загружается первая волна, дубликат пропускается.

Пример: "waveName": "scouts",

isDisabled

isDisabled - отключение волны. Отключённая волна пропускается (в лог пишется INFO), но остаётся в файле - удобно временно выключить волну, не удаляя конфиг. Если отключены или не найдены все волны из waveList, последовательность не запускается.

Значение по умолчанию: false (включена).

Пример: "isDisabled": true,

spawnList

spawnList - состав волны, полностью идентичен spawnList точки: тот же формат "Class|chance|lifeTime|qty|health|foodStage".

Поддержаны @group-ссылки со всеми условиями групп (время, погода, шанс, spawnOnce) - см. Группы спавна. Вложенные группы наследуют счёт от top-level ссылки: счёт определяет та группа, на которую ссылается волна.

Лут в волнах

Лут из spawnList волны заспавнится, но в условии завершения (completion) не участвует. Волна, в которой только лут, завершится сразу (в лог пишется WARN).

groupSelectMode

groupSelectMode - режим выбора для @group-ссылок волны. Семантика та же, что у точки - см. Режим выбора группы:

  • "" или "combined" - все прошедшие условия группы разворачиваются в общий пул;
  • "exclusive" - одна группа за волну.

Значение по умолчанию: "" (combined).

Ограничение exclusive для ботов

isAiBandits-группы в exclusive-волнах не поддерживаются: такой режим молча пропускается, при загрузке волн пишется WARN.

Пример: "groupSelectMode": "exclusive",

spawnMin

spawnMin - нижняя граница количества сущностей волны. Работает в паре со spawnMax.

Если оба равны 0, количество берётся из spawnMin/spawnMax точки. Но раньше фолбэка работает override группы: если пул волны однороден (одна группа со своими spawnMin/spawnMax), счёт берётся из группы. Приоритет: группа > волна > точка.

У isAiBandits-ботов пара spawnMin/spawnMax не действует - количество задаёт состав группы в DynamicAIB.json.

Значение по умолчанию: 0.

Пример: "spawnMin": 3,

spawnMax

spawnMax - верхняя граница количества сущностей волны. Семантика и условия срабатывания - как у spawnMin.

Значение по умолчанию: 0.

Пример: "spawnMax": 5,

spawnPositions

spawnPositions - свои позиции спавна волны. Формат точечный, поворот через | поддержан - см. spawnPositions точки. Пусто/отсутствует - используются точечные spawnPositions.

Переопределение позиций и радиуса независимы: волна может задать только позиции, только радиус или и то, и другое.

Визуализация

Дебаг-мод рисует только точечные spawnPositions; переопределение позиций волной не визуализируется.

Боты isAiBandits спавнятся у точечных spawnPositions/spawnRadius - свои позиции волны на них не действуют.

spawnRadius

spawnRadius - свой радиус разброса волны в метрах. 0 - используется точечный spawnRadius.

Значение по умолчанию: 0.

Пример: "spawnRadius": 15.0,

completion

completion - условие завершения волны. Объект из двух полей:

json
{
  "type": "allDead",
  "value": 0
}
  • allDead - волна завершена, когда убиты все сущности волны;
  • timer - волна завершается через value секунд после старта, независимо от живых (значение должно быть больше 0);
  • remainingAlive - волна завершена, когда живых осталось value или меньше.

Завершение проверяется каждую секунду по счётчику живых сущностей точки; лут в подсчёте не участвует.

Значение по умолчанию: allDead. Пустой объект {} тихо трактуется как allDead; неизвестный type - WARN в лог, сброс на allDead.

delayAfter

delayAfter - пауза после этой волны, формат диапазона "10-15" (секунды). Пусто - берётся точечный waveDelayBetween. Так каждой волной можно задать собственную паузу: длинную перед боссом, короткую между зачистками.

Значение по умолчанию: "" (точечный waveDelayBetween).

Пример: "delayAfter": "30-45",

cleanBefore

cleanBefore - перед стартом волны иммерсивно удалить живых, оставшихся от прошлых волн: живность "умирает", труп исчезает через две секунды - так же, как при triggerCleanupImmersive.

Значение по умолчанию: false.

Пример: "cleanBefore": true,

notificationText

notificationText - текст нотификации при старте волны. Плейсхолдеры: {current} - номер текущей волны, {total} - всего волн. Пусто - дефолтный текст из кода: "Волна {current} из {total}".

Нотификации получают игроки внутри зоны. Заголовок, иконка и время берутся из настроек точки (notificationTitle, notificationIcon, notificationTime) - у волны своих нет.

Пример: "notificationText": "Волна {current} из {total}. Держитесь!",

playerCountMultipliers

playerCountMultipliers - масштабирование количества сущностей волны по числу игроков в зоне. Массив объектов:

json
[
  { "players": 2, "coef": 1.5 },
  { "players": 4, "coef": 2.0 }
]

Выбирается коэффициент с наибольшим порогом players, не превышающим фактическое число игроков в зоне; количество сущностей волны умножается на него, итог ограничивается spawnCountLimit точки.

Пусто/отсутствует - коэффициент 1.0.

Поля точки для волнового режима

Волновой режим включается на точке одним полем - waveList. Пока список пуст или отсутствует, точка работает по-старому, а все остальные wave*-поля игнорируются.

waveList

waveList - упорядоченный список имён волн (waveName). Именно наличие этого поля переводит точку в волновой режим.

Неизвестное имя в списке (опечатка, волна удалена) - ERROR в лог, имя пропускается, точка грузится с оставшимися волнами.

Пример:

json
"waveList": [
  "scouts",
  "boss"
]

waveFirstDelay

waveFirstDelay - пауза после входа игрока до старта волны 1, формат "10-15". Пусто - первая волна стартует сразу.

Значение по умолчанию: "" (старт сразу).

Пример: "waveFirstDelay": "5-10",

waveDelayBetween

waveDelayBetween - пауза между волнами по умолчанию, формат "10-15". Пусто - используется "10-15". Перекрывается полем delayAfter конкретной волны.

Значение по умолчанию: """10-15".

Пример: "waveDelayBetween": "15-20",

waveAfterAllWaves

waveAfterAllWaves - поведение точки после победы (все волны завершены):

  • "" или "none" - точка завершает сценарий и ждёт рестарта сервера или перезагрузки конфига;
  • "restart" - следующий вход игрока запускает волны заново, с волны 1.

Значение по умолчанию: "" (none).

Пример: "waveAfterAllWaves": "restart",

waveContinueOnLeave

waveContinueOnLeave - продолжать ли волны, когда зону покинули все игроки:

  • false - выход всех игроков отменяет волны; следующий вход начнёт с волны 1. Подробнее - Отмена при выходе игроков.
  • true - волны продолжаются до завершения даже в пустой зоне (семантика BossHorde).

Значение по умолчанию: false.

Пример: "waveContinueOnLeave": true,

waveCountdownInterval

waveCountdownInterval - повтор отсчёта в паузе между волнами: уведомление "Волна {current} из {total} через {seconds} сек." приходит каждые N секунд. 0 - выключено (одно уведомление при старте паузы).

Значение по умолчанию: 0.

Пример: "waveCountdownInterval": 10,

waveCountdownFinalSeconds

waveCountdownFinalSeconds - отсчёт на финальных секундах паузы: когда до старта следующей волны осталось N секунд или меньше, уведомление приходит каждую секунду. 0 - выключено.

Значение по умолчанию: 0.

Пример: "waveCountdownFinalSeconds": 5,

Взаимодействие с настройками точки

НастройкаВ волновом режиме
spawnMin/spawnMaxИспользуются только как fallback, если у волны оба 0. Но раньше фолбэка работает override группы: если пул волны однороден (одна группа с её spawnMin/spawnMax), счёт берётся из группы (группа > волна > точка)
spawnPositions/spawnRadiusКак обычно - позиции и разброс спавна всех волн; волна может переопределить своими spawnPositions/spawnRadius. Визуализация дебаг-мода показывает точечные spawnPositions, override волн не рисуется
spawnCountLimitДействует (клэмп одновременного количества). Для волн рекомендуется 0
spawnLoopInside, triggerCooldownИгнорируются (WARN при валидации)
notificationTextSpawnРаботает как обычно - на каждый цикл спавна (в т.ч. волновой)
triggerCleanupOnLeave/ImmersiveРаботают как обычно; при дефолтной отмене волн конфликта нет
triggerInactiveResetDelayРаботает: сброс триггера возвращает волны к волне 1
enableEquipCE, clearDeath*Работают как обычно

Группы isAiBandits в волнах

@group с isAiBandits в волне легален: боты группы (состав задаёт DynamicAIB.json) попадают в cleanup-список точки, поэтому работают штатные механизмы волн:

  • completion (allDead/remainingAlive) считает живых ботов; spawnMin/spawnMax на ботов не влияют (бот-онли волна: количество задаёт состав группы, WARN "spawn count is 0" не пишется). Бот-онли волна не запускает FIRST_SPAWN-эффекты точки (addOnFirstSpawn, triggersToEnableOnFirstSpawn, событие OnTriggerFirstSpawn) - их дают только волны с обычным пулом. Боты спавнятся у точечныхspawnPositions/spawnRadius - свои позиции волны на ботов не действуют.
  • cleanBefore удаляет ботов иммерсивно (NotifyDespawn → bridge чистит группу); то же при DeleteTrigger.
  • Exclusive-режим волн ботов не поддерживает (молча пропускает; WARN при загрузке волн).

Edge-кейсы: отмена волн (все игроки ушли) ботов не удаляет - остаются до cleanBefore/следующего цикла. При config reload ботов удаляют и bridge, и DeleteTrigger(hard) через cleanup-список - двойное удаление идемпотентно.

Подробности о группах ботов - в AI_Bandits интеграция.

Валидация и логи

СитуацияУровеньПоведение
waveName пустой / дубликатERRORВолна пропускается
Неизвестное имя в waveListERRORИмя пропускается, точка грузится
@group → isAiBandits внутри волны-Поддержан: боты в cleanup-списке (completion/cleanBefore); в exclusive - WARN и пропуск
completion.type неизвестныйWARNСброс на allDead
completion пустой объект {}-Тихо allDead
spawnList волны пустойWARNВолна завершится сразу
spawnPositions волны кривая / вне картыWARNНе блокирует загрузку
waveList + spawnLoopInsideWARNИнформирование об игнорировании
Волны отключены/не найдены всеINFO/WARNПоследовательность не запускается

Полный пример

Арена из двух волн: зачистка зомби, затем иммерсивная чистка сцены и выход босса. При 2+ игроках в зоне первая волна масштабируется.

Waves/Arena.json

json
[
  {
    "waveName": "scouts",
    "isDisabled": false,
    "spawnList": [
      "@dayZombies",
      "ZmbM_JournalistNormal_Blue|0.6"
    ],
    "spawnMin": 3,
    "spawnMax": 5,
    "playerCountMultipliers": [
      { "players": 2, "coef": 1.5 },
      { "players": 4, "coef": 2.0 }
    ],
    "completion": {
      "type": "allDead",
      "value": 0
    },
    "delayAfter": "10-15",
    "cleanBefore": false,
    "notificationText": ""
  },
  {
    "waveName": "boss",
    "spawnList": [
      "ZmbM_SoldierNormal_Heavy|1|-3|-3|80-100"
    ],
    "spawnMin": 1,
    "spawnMax": 1,
    "completion": {
      "type": "remainingAlive",
      "value": 0
    },
    "cleanBefore": true,
    "notificationText": "Босс! Волна {current} из {total}"
  }
]

Использование в точке (Points/Arena.json)

json
{
  "pointId": 10,
  "notificationTitle": "Арена",
  "notificationTextEnter": "Вы вошли на арену. Началось!",
  "notificationTextWin": "Волны отбиты!",
  "triggerPosition": "4246.0 0.0 5586.7",
  "triggerRadius": "50.0",
  "spawnPositions": [
    "4246.0 0.0 5586.7"
  ],
  "spawnRadius": 30.0,
  "waveList": [
    "scouts",
    "boss"
  ],
  "waveFirstDelay": "5-10",
  "waveDelayBetween": "15-20",
  "waveAfterAllWaves": "restart"
}

Что делает эта конфигурация:

  • Точка работает только в волновом режиме (waveList задан): регулярный цикл спавна отключён, spawnLoopInside и triggerCooldown игнорируются.
  • Через 5-10 секунд после первого входа стартует волна scouts: 3-5 зомби из группы @dayZombies (по её условиям) и прямого класснейма с шансом 60%.
  • При 2+ игроках в зоне количество зомби умножается на коэффициент (playerCountMultipliers).
  • Волна завершится, когда всех убьют (allDead); после паузы 10-15 сек (delayAfter волны перекрывает waveDelayBetween) стартует boss.
  • Перед боссом выжившие зомби прошлых волн иммерсивно удаляются (cleanBefore), затем спавнится 1 солдат с здоровьем 80-100%.
  • Когда босс умрёт (remainingAlive: 0), сработает штатная WIN-цепочка: нотификация "Волны отбиты!".
  • Следующий вход игроков запустит сценарий заново, с волны 1 (waveAfterAllWaves: "restart").

Крайние случаи

  • spawnList волны пустойWARN в лог, волна завершится сразу.
  • Волна только с лутом → лут заспавнится, но в завершении не считается → волна завершится сразу (WARN).
  • Все волны отключены или не найдены → последовательность не запускается (INFO/WARN).
  • Кривые spawnPositions волны / вне картыWARN, загрузку не блокирует.
  • waveList вместе со spawnLoopInside → цикл игнорируется, WARN при валидации.
  • spawnCountLimit клэмпит одновременное количество - для волн рекомендуется 0.
  • Отмена волн (все игроки ушли) не удаляет ботов AI Bandits - остаются до cleanBefore/следующего цикла.
  • Бот-онли волна не запускает FIRST_SPAWN-эффекты точки - их дают только волны с обычным пулом.
  • Config reload - ботов удаляют и bridge, и DeleteTrigger(hard) через cleanup-список; двойное удаление идемпотентно.
  • triggerInactiveResetDelay - сброс триггера возвращает волны к волне 1.

Тесты

TestSuite: точки 128 (полный сценарий 3 волн + restart), 129 (exclusive + waveContinueOnLeave + triggerDisableOnWin), 130 (disabled/badgroup/bandits - ERROR от ts_w7 ожидаем, ts_w8 спавнит ботов), 131 (fallback количества к точке), 132 (масштабирование по игрокам), 133 (свои позиции волны), 134 (повтор отсчёта в паузе), 135 (боты AI Bandits в волне, требуется мод AI Bandits). Волны - 100-Waves.json из Help/test-suite/ (скопировать в $profile:MPG_Spawner/Waves/).

См. также