Волны спавна 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, загружается первая встретившаяся волна с этим именем, дубликат пропускается.
Поля волны
[
{
"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 - условие завершения волны. Объект из двух полей:
{
"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 - масштабирование количества сущностей волны по числу игроков в зоне. Массив объектов:
[
{ "players": 2, "coef": 1.5 },
{ "players": 4, "coef": 2.0 }
]Выбирается коэффициент с наибольшим порогом players, не превышающим фактическое число игроков в зоне; количество сущностей волны умножается на него, итог ограничивается spawnCountLimit точки.
Пусто/отсутствует - коэффициент 1.0.
Поля точки для волнового режима
Волновой режим включается на точке одним полем - waveList. Пока список пуст или отсутствует, точка работает по-старому, а все остальные wave*-поля игнорируются.
waveList
waveList - упорядоченный список имён волн (waveName). Именно наличие этого поля переводит точку в волновой режим.
Неизвестное имя в списке (опечатка, волна удалена) - ERROR в лог, имя пропускается, точка грузится с оставшимися волнами.
Пример:
"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 | Волна пропускается |
Неизвестное имя в waveList | ERROR | Имя пропускается, точка грузится |
@group → isAiBandits внутри волны | - | Поддержан: боты в cleanup-списке (completion/cleanBefore); в exclusive - WARN и пропуск |
completion.type неизвестный | WARN | Сброс на allDead |
completion пустой объект {} | - | Тихо allDead |
spawnList волны пустой | WARN | Волна завершится сразу |
spawnPositions волны кривая / вне карты | WARN | Не блокирует загрузку |
waveList + spawnLoopInside | WARN | Информирование об игнорировании |
| Волны отключены/не найдены все | INFO/WARN | Последовательность не запускается |
Полный пример
Арена из двух волн: зачистка зомби, затем иммерсивная чистка сцены и выход босса. При 2+ игроках в зоне первая волна масштабируется.
Waves/Arena.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)
{
"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/).
См. также
- Группы спавна - состав волны через
@group - Конфигурация точки - базовые настройки триггера
