diff --git a/COMMANDS_GUIDE.md b/COMMANDS_GUIDE.md deleted file mode 100644 index 6b94582..0000000 --- a/COMMANDS_GUIDE.md +++ /dev/null @@ -1,489 +0,0 @@ -# Dead by Daylight Plugin — Руководство команд - -**Версия:** 1.2 -**Обновлено:** Июнь 2026 -**Платформа:** Minecraft 1.21.5 (Paper) - ---- - -## Содержание - -1. [Фазы игры](#фазы-игры) -2. [Команды игроков — /dbd](#команды-игроков--dbd) -3. [Команды администратора — /dbdadmin](#команды-администратора--dbdadmin) -4. [Команды настройки карты — /dbdsetup](#команды-настройки-карты--dbdsetup) -5. [Права доступа](#права-доступа) -6. [Порядок настройки сервера](#порядок-настройки-сервера) -7. [Таблица всех команд](#таблица-всех-команд) - ---- - -## Фазы игры - -``` -LOBBY - Игроки делают /dbd join → телепорт в мир lobby - Action bar показывает: ▶ Старт через: 45с ████████░░ (3/5) - Когда набралось минимум игроков — идёт отсчёт 60 сек - При нехватке игроков — таймер сбрасывается - ↓ -ROLE ASSIGN (5 сек) - Случайно выбирается 1 киллер, остальные — выжившие - Показывается тайтл с ролью - Все телепортируются на свои спавны в мире game - ↓ -GAMEPLAY - Выжившие чинят генераторы (нужно 5 из 7) - Киллер охотится - При починке всех нужных генераторов — открываются ворота - ↓ -ENDGAME (2 минуты) - Выжившие открывают ворота и убегают - Или используют люк (если остался 1 выживший) - Коллапс через 120 сек убивает всех оставшихся -``` - ---- - -## Команды игроков — /dbd - -Все команды ниже доступны любому игроку (`mdbd.player`, по умолчанию `true`). - ---- - -### /dbd join - -Присоединиться к игровой очереди. - -``` -/dbd join -``` - -- Добавляет игрока в текущую сессию как выжившего (роль назначится позже) -- Телепортирует в мир `lobby` -- В action bar начинает отображаться счётчик и таймер -- Если игра не создана — сообщение об ошибке - -``` -→ [DBD] Вы присоединились к игре! -→ [DBD] PlayerName присоединился к очереди! (1/5) -``` - ---- - -### /dbd leave - -Покинуть сессию. - -``` -/dbd leave -``` - -- В лобби: просто убирает из очереди, таймер может сброситься -- В игре: выживший считается мёртвым, киллер завершает игру -- Игрок телепортируется обратно в `lobby` - -``` -→ [DBD] Вы покинули игру -``` - ---- - -### /dbd lobby - -Телепортироваться в лобби-мир. - -``` -/dbd lobby -``` - -- Работает в любой момент, не убирает из сессии -- Телепортирует на точку спавна лобби (из `locations.yml`) или на спавн мира `lobby` - -``` -→ [DBD] Вы телепортированы в лобби! -``` - ---- - -## Команды администратора — /dbd (admin) - -Требуют право `mdbd.admin` (по умолчанию только OP). - ---- - -### /dbd create - -Создать новую игровую сессию. - -``` -/dbd create -``` - -- Создаёт сессию и открывает лобби -- Нельзя создать вторую сессию пока первая активна - -``` -→ [DBD] Игра создана! -→ [DBD] Игра уже существует (если уже есть) -``` - ---- - -### /dbd forcestart - -Принудительно запустить игру, пропустив таймер. - -``` -/dbd forcestart -``` - -- Работает только если сессия в фазе LOBBY -- Игроков в очереди может быть меньше минимума - -``` -→ [DBD] Игра форсирована! -``` - ---- - -### /dbd stop - -Немедленно завершить текущую сессию. - -``` -/dbd stop -``` - -- Все игроки телепортируются в `lobby` -- Сессия уничтожается, можно создавать новую - -``` -→ [DBD] Игра остановлена -``` - ---- - -### /dbd reload - -Перезагрузить все конфиги без перезапуска сервера. - -``` -/dbd reload -``` - -Перезагружает: `config.yml`, `game.yml`, `messages.yml`, `skillcheck.yml`, `locations.yml`, `generators.yml` - -``` -→ [DBD] Конфиги перезагружены! -``` - -> Если игра в процессе — сначала `/dbd stop`, иначе новые настройки применятся только к следующей сессии. - ---- - -### /dbd setlobby - -Установить текущую позицию игрока как точку спавна лобби. - -``` -/dbd setlobby -``` - -- Сохраняется в `locations.yml` -- Эквивалентно `/dbdsetup lobby` - -``` -→ [DBD] Лобби установлено! -``` - ---- - -## Команды администратора — /dbdadmin - -Требуют право `mdbd.admin`. Все команды только для игроков (не консоли). - ---- - -### /dbdadmin create generator - -Режим расстановки генераторов. - -``` -/dbdadmin create generator -``` - -- Включает режим: в action bar оранжевая подсказка -- **Сломай блок** — координаты сохранятся, блок не разрушится -- Повторная команда — отключает режим -- Режим остаётся активным для нескольких генераторов подряд - -``` -→ Режим выбора генератора включен! -→ Сломайте блок, чтобы установить генератор -(сломал блок) -→ Генератор установлен! Всего: 1 -``` - -**Tab complete:** `generator` - ---- - -### /dbdadmin list - -Список всех установленных генераторов. - -``` -/dbdadmin list -``` - -``` -=== Генераторы (3) === -[0] game:100.00,64.00,200.00 -[1] game:150.00,64.00,150.00 -[2] game:200.00,64.00,100.00 -``` - ---- - -### /dbdadmin remove \ - -Удалить генератор по ID из списка. - -``` -/dbdadmin remove 0 -``` - -**Tab complete:** подсказывает существующие ID (0, 1, 2...) - -``` -→ Генератор удален: game:100.00,64.00,200.00 -→ Генератор с ID 5 не найден (если нет такого) -``` - ---- - -### /dbdadmin clear - -Удалить все генераторы. - -``` -/dbdadmin clear -``` - -> Необратимо. - -``` -→ Все генераторы удалены -``` - ---- - -### /dbdadmin world \<мир\> - -Телепортироваться в указанный мир для настройки карты. - -``` -/dbdadmin world lobby -/dbdadmin world game -/dbdadmin world <любое_название_мира> -``` - -- `lobby` — телепорт в мир-лобби (void, платформа 7x7) -- `game` — телепорт в игровой мир -- Любое другое название — телепорт в мир с таким именем (если загружен) - -``` -→ Телепортирован в мир: lobby -→ Мир не найден: abc (если мир не существует) -``` - -**Tab complete:** `lobby`, `game` - ---- - -## Команды настройки карты — /dbdsetup - -Требуют право `mdbd.admin`. - ---- - -### /dbdsetup wand - -Выдать топорик для расстановки ворот. - -``` -/dbdsetup wand -``` - -- Выдаёт деревянный топорик (WOODEN_AXE) с подписью DBD Setup Wand -- **ЛКМ** по блоку = точка 1 (pos1) -- **ПКМ** по блоку = точка 2 (pos2) -- Только для ворот. Для остальных объектов ванд не нужен. - ---- - -### /dbdsetup set \<тип\> - -Войти в режим установки объекта. **Сломай блок** — объект установится на его месте, блок восстановится. - -``` -/dbdsetup set generator -/dbdsetup set hook -/dbdsetup set pallet -/dbdsetup set chest -/dbdsetup set killer-spawn -/dbdsetup set survivor-spawn -``` - -| Тип | Описание | Рекомендуемое кол-во | -|-----|----------|---------------------| -| `generator` | Генератор | 7 | -| `hook` | Крюк | 7–10 | -| `pallet` | Паллет | 15–20 | -| `chest` | Сундук | 5–10 | -| `killer-spawn` | Спавн киллера | 1 | -| `survivor-spawn` | Спавн выжившего | 4 | - -``` -→ Режим установки: hook -→ Сломай блок чтобы установить (блок восстановится) -(ломаешь блок) -→ Крюк установлен: [game, 150, 65, 200] -``` - ---- - -### /dbdsetup set gate A|B - -Установить ворота через две точки топориком. - -``` -/dbdsetup wand — получить топорик -ЛКМ по блоку → точка 1 — нижний левый угол -ПКМ по блоку → точка 2 — верхний правый угол -/dbdsetup set gate A — установить ворота A в центре между точками -/dbdsetup set gate B — установить ворота B -``` - -Центр ворот вычисляется: X = средняя точка, Y = минимальная из двух. - -``` -→ [Wand] Точка 1: [game, 100, 64, 200] -→ [Wand] Точка 2: [game, 110, 64, 210] -→ Ворота A установлены: [game, 105, 64, 205] -``` - ---- - -### /dbdsetup build - -Построить карту по шаблону и `locations.yml`. - -``` -/dbdsetup build -``` - -``` -→ Строительство карты... -→ Карта построена! -``` - ---- - -### /dbdsetup clear - -Очистить карту. - -``` -/dbdsetup clear -``` - -``` -→ Очистка карты... -→ Карта очищена! -``` - ---- - -### /dbdsetup lobby - -Установить спавн лобби на текущую позицию. - -``` -/dbdsetup lobby -``` - -Аналог `/dbd setlobby`. Сохраняется в `locations.yml`. - ---- - -## Права доступа - -| Право | Кому | Что даёт | -|-------|------|----------| -| `mdbd.player` | Все (`default: true`) | `/dbd join`, `leave`, `lobby` | -| `mdbd.admin` | OP (`default: op`) | Все остальные команды | - ---- - -## Порядок настройки сервера - -### Первый запуск - -1. Запустить сервер — плагин создаст миры `lobby` и `game` автоматически -2. `/dbdadmin world lobby` — войти в лобби-мир и осмотреться (платформа 7x7) -3. `/dbd setlobby` — встать на нужную точку и сохранить спавн лобби - -### Настройка игровой карты - -4. `/dbdadmin world game` — перейти в игровой мир -5. Построить или загрузить карту -6. Расставить объекты: - - `/dbdsetup set generator` → ломать блоки для генераторов - - `/dbdsetup set hook` → ломать блоки для крюков - - `/dbdsetup set pallet` → ломать блоки для паллетов - - `/dbdsetup set chest` → ломать блоки для сундуков -7. Установить спавны: - - `/dbdsetup set killer-spawn` → спавн киллера - - `/dbdsetup set survivor-spawn` → 4 раза для выживших -8. Установить ворота: - - `/dbdsetup wand` → получить топорик - - ЛКМ/ПКМ по блокам → отметить углы ворот - - `/dbdsetup set gate A` и `/dbdsetup set gate B` - -### Тест - -8. `/dbd create` — создать сессию -9. `/dbd join` (5 игроков или меньше с форс-стартом) -10. `/dbd forcestart` — запустить не дожидаясь таймера - ---- - -## Таблица всех команд - -| Команда | Право | Описание | -|---------|-------|----------| -| `/dbd join` | player | Войти в очередь | -| `/dbd leave` | player | Покинуть сессию | -| `/dbd lobby` | player | Телепорт в лобби | -| `/dbd create` | admin | Создать сессию | -| `/dbd forcestart` | admin | Принудительный старт | -| `/dbd stop` | admin | Остановить игру | -| `/dbd reload` | admin | Перезагрузить конфиги | -| `/dbd setlobby` | admin | Сохранить спавн лобби | -| `/dbdadmin create generator` | admin | Режим расстановки генераторов | -| `/dbdadmin list` | admin | Список генераторов | -| `/dbdadmin remove ` | admin | Удалить генератор | -| `/dbdadmin clear` | admin | Очистить все генераторы | -| `/dbdadmin world ` | admin | Телепорт в мир | -| `/dbdsetup wand` | admin | Топорик для ворот (ЛКМ/ПКМ) | -| `/dbdsetup set <тип>` | admin | Установить объект (сломать блок) | -| `/dbdsetup set gate A\|B` | admin | Установить ворота (из точек ванда) | -| `/dbdsetup build` | admin | Построить карту | -| `/dbdsetup clear` | admin | Очистить карту | -| `/dbdsetup lobby` | admin | Сохранить спавн лобби | - ---- - -*Dead by Daylight Minecraft Plugin — актуально для версии 1.1+* diff --git a/DBD_Plugin_TZ.md b/DBD_Plugin_TZ.md deleted file mode 100644 index 4424aa9..0000000 --- a/DBD_Plugin_TZ.md +++ /dev/null @@ -1,523 +0,0 @@ -# DEAD BY DAYLIGHT — Minecraft Plugin -## Техническое Задание (ТЗ) -> Полная реализация механик оригинальной игры 4v1 - ---- - -## Содержание - -1. [Общее описание проекта](#1-общее-описание-проекта) -2. [Распределение ролей и старт игры](#2-распределение-ролей-и-старт-игры) -3. [Механики выжившего](#3-механики-выжившего-survivor) -4. [Генераторы](#4-генераторы-generators) -5. [Механики киллера](#5-механики-киллера-killer) -6. [Паллеты и окна](#6-паллеты-pallets-и-окна-windows) -7. [Выходные ворота и люк](#7-выходные-ворота-exit-gates-и-люк-hatch) -8. [Предметы для выживших](#8-предметы-items-для-выживших) -9. [Bloodpoints и прогрессия](#9-bloodpoints-и-прогрессия) -10. [Система перков](#10-система-перков-perks) -11. [Эндгейм и итоги матча](#11-эндгейм-и-итоги-матча) -12. [Пользовательский интерфейс](#12-пользовательский-интерфейс-hud) -13. [Конфигурация](#13-конфигурация-configyml) -14. [Команды и права](#14-команды-и-права) -15. [Архитектура плагина](#15-архитектура-плагина) -16. [Приоритет разработки](#16-приоритет-разработки) - ---- - -## 1. Общее описание проекта - -### 1.1 Цель плагина - -Плагин реализует полноценную мини-игру Dead by Daylight (4v1) на Minecraft-сервере. Используется готовая карта, предоставленная заказчиком. Плагин управляет всем игровым процессом: распределением ролей, механиками выживших и киллера, победными условиями, интерфейсом и звуками. - -### 1.2 Технические требования - -| Параметр | Значение | -|---|---| -| Платформа | Paper / Spigot 1.20.x+ | -| Язык | Java 17+ | -| Зависимости | ProtocolLib, Citizens2 (опционально), PacketEvents | -| Кол-во игроков | 5 (1 киллер + 4 выживших) | -| Карта | Предоставляется отдельно (worldname задаётся в `config.yml`) | -| Gamemode | Adventure mode на протяжении всей игры | - ---- - -## 2. Распределение ролей и старт игры - -### 2.1 Лобби и ожидание - -- Лобби-мир отдельный от игровой карты. -- Минимум для старта: 5 игроков (настраивается: `min_players`, `max_players`). -- Таймер ожидания: 60 секунд после набора минимума (`countdown_seconds`). -- Если игроков < минимума — таймер сбрасывается. -- В лобби показывается статус: сколько игроков зашло / нужно. - -### 2.2 Назначение ролей - -- Один игрок случайно становится **Киллером (Killer)**. -- Остальные 4 становятся **Выжившими (Survivors)**. -- Реализовать систему очереди: если игрок давно не был киллером — шанс повышается. -- Роли объявляются через Title + Subtitle (красный текст для киллера, белый для выжившего). -- Роль киллера не раскрывается другим выжившим до спавна (5-секундная задержка перед телепортом киллера). - -### 2.3 Спавн - -- Спавн-точки прописаны в `config.yml` (4 точки для выживших + 1 для киллера). -- Выжившие телепортируются одновременно. -- Киллер телепортируется через **5 секунд** после выживших (grace period). -- Во время grace period киллер видит таймер в ActionBar. -- Генераторы, хуки, выходы и другие объекты расставляются при старте игры по координатам из `config.yml`. - -> ⚠ Все координаты (спавны, хуки, генераторы, выходы, подвал) задаются в `config.yml` — не хардкодятся в коде. - ---- - -## 3. Механики выжившего (Survivor) - -### 3.1 Состояния здоровья - -Выживший имеет три состояния — переходы строго последовательные: - -| Состояние | Описание | Визуал в Minecraft | -|---|---|---| -| **Healthy** (здоров) | Может бежать, прыгать, взаимодействовать | Полное здоровье, без эффектов | -| **Injured** (ранен) | Оставляет следы крови, издаёт стоны, скорость чуть снижена | Частицы крови, звук боли каждые 3 сек | -| **Downed** (повален) | Ползёт по полу, не может бежать или взаимодействовать | Crawl-анимация (Sneak + замедление), скорость 30% | -| **Hooked** (на крюке) | Привязан к крюку, ждёт спасения | Заморожен, анимация качания на крюке | -| **Dead** (мёртв) | Выбывает из игры | Spectator mode | - -### 3.2 Переходы состояний - -- **Healthy → Injured:** одно попадание от киллера. -- **Injured → Downed:** второе попадание от киллера. -- **Downed → Hooked:** киллер подбирает (ПКМ) и несёт к крюку. -- **Hooked → Dead:** истекло 2 стадии крюка, никто не спас. -- **Injured → Healthy:** использование аптечки или спасение с крюка. - -### 3.3 Передвижение - -- **Healthy:** обычная скорость + возможность спринта. -- **Injured:** скорость снижена на 5% (speed effect). -- **Downed:** только ползти — отрицательный Speed, принудительный Sneak, без прыжков. -- **Спринт (Exhaustion):** можно спринтовать конечное время (`exhaustion_seconds`, по умолчанию 3 сек спринта, затем 5 сек кулдауна). - -> ⚠ Спринт — ключевая механика для чейса. Реализовать через `isSprinting()` + кастомный кулдаун. - -### 3.4 Исцеление (Healing) - -- **Аптечка (Med-Kit):** зажать ПКМ 10 секунд, прогресс-бар в ActionBar. -- **Исцеление другим выжившим:** подойти вплотную, зажать ПКМ 16 секунд (медленнее, чем аптечкой). -- Если движение прервалось (игрок отошёл, получил удар) — прогресс сбрасывается. -- Иконка прогресса в ActionBar: `[████░░░░░░] Исцеление 40%` - -### 3.5 Механика крюка (Hook) - -После того как киллер повесил выжившего на крюк: - -| Стадия | Длительность | Событие | -|---|---|---| -| Stage 1 | 60 секунд | Выживший висит. Можно спасти. В ActionBar — таймер. | -| Stage 1 → 2 | Автоматически | Если не спасли за 60 сек — переход на Stage 2. | -| Stage 2 | 60 секунд | Выживший борется с Сущностью. Каждые 2 сек — Skill Check (QTE). | -| Stage 2 провален | Мгновенно | Выживший умирает — переход в Spectator. | -| Спасение с крюка | 2 секунды | Другой выживший снимает с крюка (прогресс-бар). Стадия запоминается. | - -- Один и тот же выживший может быть повешен максимум **3 раза** (`hook_count`). На 3-й раз — мгновенная смерть. -- Крюки в подвале (Basement) не уничтожаются и дают бонусные Bloodpoints. - -> ⚠ Skill Check на Stage 2 — случайное появление индикатора в Title. Игрок должен нажать Shift или Drop в нужный момент. Промах — мгновенная смерть. - -### 3.6 Спасение с крюка - -- Выживший подходит к крюку с союзником на расстояние 2 блока. -- Зажимает ПКМ (или Sneak) в течение 2 секунд — прогресс в ActionBar. -- Спасённый получает статус Injured (или остаётся Injured). -- Спасатель не получает защиты — может быть немедленно атакован. -- **Borrowed Time:** если киллер стоит рядом с крюком в момент спасения — спасённый получает 10 сек защиты Endurance (не переходит в Downed с первого удара). - -### 3.7 Ползание (Dying State) - -- Повалённый выживший ползёт со скоростью 30% от нормальной. -- Может ползти к другому выжившему — тот может его поднять (Recovery, 4 секунды). -- После Recovery выживший встаёт в статусе Injured. -- Самовосстановление невозможно без специального перка. -- Повалённый выживший издаёт стоны (слышимые киллеру). - ---- - -## 4. Генераторы (Generators) - -### 4.1 Общие правила - -- На карте спавнится **7 генераторов**, нужно починить **5**. -- Количество настраивается: `total_generators`, `required_generators` в `config.yml`. -- Починённые генераторы светятся (частицы + звук). -- Киллер видит ауру всех непочиненных генераторов (через частицы или Sidebar). - -### 4.2 Процесс починки - -- Выживший подходит к генератору и зажимает ПКМ — начинается починка. -- Прогресс отображается в ActionBar. -- Базовое время починки: **80 секунд** (один выживший). -- Два выжившего на генераторе: скорость ×1.5 (`gen_coop_multiplier`). -- Три и более: скорость ×2.0 (ограничено). -- Если выживший отошёл или получил удар — прогресс частично откатывается (regression). - -### 4.3 Skill Checks (Проверки навыка) - -- В Title показывается круговой индикатор (симулируется через Title-анимации). -- Игрок должен нажать **Drop (Q)** в нужный момент (попасть в зелёную зону). -- **Успешный Skill Check:** +5% к прогрессу генератора + бонусные Bloodpoints. -- **Провальный Skill Check:** взрыв — генератор теряет 10% прогресса, громкий звук (слышен киллеру в радиусе 40 блоков). -- Частота skill check: раз в 20–40 секунд (рандомно). - -> ⚠ Звук провального skill check — важнейший инструмент для киллера. Реализовать через `playSound()` с большим радиусом. - -### 4.4 Регрессия генератора - -- Если все выжившие отошли от работающего генератора — он начинает откатываться (0.25% в секунду). -- Киллер может ударить по генератору (ПКМ) — **Kick:** мощная регрессия 2.5% в секунду на 30 секунд. -- Регрессию можно остановить — выживший подходит и нажимает ПКМ. -- Взорванный генератор издаёт звук и выбрасывает частицы взрыва. - ---- - -## 5. Механики киллера (Killer) - -### 5.1 Базовые характеристики - -| Параметр | Значение | Настройка | -|---|---|---| -| Скорость | 115% от скорости выжившего | `killer_speed` | -| Terror Radius | 32 блока | `terror_radius` | -| Урон атаки | Переводит в Injured / Downed | Не числовой — по состоянию | -| Кулдаун атаки | 2.5 секунды | `attack_cooldown` | -| Перенос выжившего | 92% скорости | `carry_speed` | - -### 5.2 Terror Radius и Red Stain - -- **Terror Radius** — зона 32 блока вокруг киллера. Выжившие в этой зоне слышат сердцебиение. -- Интенсивность звука увеличивается при сближении (3 уровня: далеко / близко / рядом). -- **Red Stain** — красное свечение на земле за киллером (частицы). Показывает направление взгляда. -- Реализация Red Stain: каждые 0.5 сек спавнить Dust-частицы красного цвета позади головы киллера. - -### 5.3 Атака и удар - -- **Базовая атака:** ЛКМ по выжившему. Урон по состояниям (Healthy→Injured или Injured→Downed). -- После удара — кулдаун 2.5 сек (замедление на 0.5 сек). -- Нельзя атаковать сквозь окна / паллеты (проверка LineOfSight). -- **Bloodlust:** если киллер преследует выжившего >15 секунд без попадания — бонус скорости: - - Тир 1: +3% - - Тир 2: +5% - - Тир 3: +8% - - Тиры сбрасываются при ударе или смене цели. - -### 5.4 Подъём и перенос выжившего - -- Когда выживший Downed — киллер нажимает ПКМ для подъёма (2.5 сек анимации). -- Во время переноса: киллер замедлен до 92% скорости, не может атаковать. -- Выживший сидит на киллере (через ArmorStand или Mount). -- Чтобы повесить: киллер подходит к хуку и нажимает ПКМ. -- **Wiggle (вырывание):** выживший нажимает Sneak/Jump быстро — прогресс побега в ActionBar. При 100% — выживший падает рядом в Downed статусе. -- Время вырывания: **16 секунд** (`wiggle_duration`). - -### 5.5 Взаимодействие с генераторами - -- Киллер нажимает ПКМ на генератор — **Kick** (регрессия, см. раздел 4.4). -- Киллер видит ауру всех незаконченных генераторов в Sidebar (иконки + расстояние). - -### 5.6 Подвал (Basement) - -- Специальная зона на карте с 4 крюками. -- Крюки в подвале **нельзя саботировать**. -- Перевод выжившего в подвал засчитывается быстрее + бонус Bloodpoints. - ---- - -## 6. Паллеты (Pallets) и Окна (Windows) - -### 6.1 Паллеты - -- Паллеты — деревянные барьеры на карте (блоки, отмеченные в конфиге). -- Выживший подходит к паллете и нажимает ПКМ — паллета падает (standing → dropped). -- Если киллер коснулся упавшей паллеты — **Stun 2.5 сек** (звук, частицы). -- Киллер может **сломать паллету**: зажать ПКМ на 3 секунды. Паллета исчезает навсегда. -- Количество паллет на карте конечно. Статус каждой хранится в памяти. -- Выживший может пробежать через упавшую паллету (Vault, 0.6 сек). - -### 6.2 Окна (Windows) - -- Окна — прыжковые зоны в стенах карты (координаты в конфиге). -- Выживший запрыгивает в окно: **1 секунда** анимации Vault (замедление → ускорение). -- Киллер тоже может прыгнуть в окно, но медленнее (1.5 сек). -- **Fake Vault (Mindgame):** игрок может зайти в начало вота и выйти обратно. - -> ⚠ Паллеты и окна — основа loop-механики чейса. Без правильного расположения на карте игра будет несбалансированной. - ---- - -## 7. Выходные ворота (Exit Gates) и Люк (Hatch) - -### 7.1 Выходные ворота - -- После починки 5 генераторов активируются **2 выходных ворот** (`gate_locations`). -- Рубильник ворот загорается (смена блока, звук). -- Выживший взаимодействует с рубильником **20 секунд** (прогресс в ActionBar). -- Если прервать — прогресс **не откатывается** (в отличие от генераторов). -- Открытые ворота остаются открытыми навсегда. -- Выживший пересекает границу выхода → **ESCAPED!** (Title). -- **Эндгейм-коллапс:** через 2 минуты после открытия первых ворот — все оставшиеся выжившие умирают. - -### 7.2 Люк (Hatch) - -- Люк появляется, когда выживших меньше, чем непочиненных генераторов. -- Правило: если X выживших живы и < X генераторов починено — люк открыт. -- Последний выживший **всегда** видит открытый люк. -- Координата люка — `hatch_location` в конфиге. -- Прыжок в люк = Escape (побег через люк). -- Киллер может **закрыть люк** (ПКМ). После этого активируется End Game Collapse. -- Закрытый люк можно открыть выжившим с **Ключом (Key)** за 2.5 секунды. - ---- - -## 8. Предметы (Items) для выживших - -Предметы выдаются игрокам перед игрой или находятся в Сундуках (Chests) на карте. Максимум **1 предмет** в руках. - -| Предмет | Действие | MC-предмет | Ограничение | -|---|---|---|---| -| **Med-Kit** | Лечит Injured → Healthy за 10 сек | Red Dye / Custom | 1 заряд | -| **Flashlight** | Слепит киллера (Blindness 2 сек) | Glowstone Dust / Custom | 8 использований | -| **Toolbox** | Ускоряет починку гена на 30% | Iron Pickaxe / Custom | 1 генератор | -| **Key** | Открывает закрытый люк | Gold Nugget / Custom | 1 использование | -| **Map** | Показывает ауру генераторов выжившим | Paper / Custom | Пассивно | - -### 8.1 Сундуки - -- На карте расставлено N сундуков (`chest_locations` в конфиге). -- Каждый сундук при открытии выдаёт случайный предмет (весовая таблица в `loot_table.yml`). -- Открытие: **3 секунды** (прогресс-бар). Один сундук — один предмет навсегда. -- Киллер может видеть ауры сундуков (опционально, через перк). - ---- - -## 9. Bloodpoints и прогрессия - -Bloodpoints — внутриигровая валюта, начисляется за действия в раунде. Хранятся в базе данных (SQLite или YAML). - -| Действие | Выживший (BP) | Киллер (BP) | -|---|---|---| -| Починить генератор (полностью) | +1000 | — | -| Успешный Skill Check | +150 | — | -| Спасти с крюка | +800 | — | -| Исцелить союзника | +600 | — | -| Сбежать | +5000 | — | -| Повесить выжившего на крюк | — | +800 | -| Повалить выжившего | — | +500 | -| Пожертвовать выжившего (3 стадии) | — | +2000 | -| Сломать паллету | — | +200 | -| Ударить по генератору (Kick) | — | +100 | - -- Bloodpoints отображаются в Title после окончания матча. -- Статистика сохраняется в базе данных. - ---- - -## 10. Система перков (Perks) - -Перки — пассивные и активные способности. Каждый игрок берёт **4 перка** перед матчем (в GUI-меню выбора). - -### 10.1 Перки Выживших (минимум 10) - -| Перк | Эффект | -|---|---| -| **Self-Care** | Позволяет лечить себя без аптечки (вдвое медленнее) | -| **Dead Hard** | При нажатии Sneak во время спринта — кратковременный рывок (0.5 сек неуязвимости) | -| **Borrowed Time** | После спасения с крюка — 10 сек защита Endurance | -| **Sprint Burst** | При начале спринта — ускорение ×1.5 на 3 сек (кулдаун 40 сек) | -| **Iron Will** | Убирает звуки стонов в Injured состоянии | -| **Spine Chill** | Предупреждение (иконка + звук), если киллер смотрит на выжившего с 36 блоков | -| **Resilience** | В Injured/Downed — скорость починки +9%, скорость вота +9% | -| **Kindred** | После повешения на крюк — все союзники видят ауру киллера | -| **Prove Thyself** | За каждого выжившего на генераторе — скорость +15% (стакается) | -| **Adrenaline** | При открытии 5-го генератора — мгновенно Healthy + Sprint Burst 5 сек | - -### 10.2 Перки Киллера (минимум 10) - -| Перк | Эффект | -|---|---| -| **Hex: Ruin** | Все генераторы регрессируют вдвое быстрее (пока горит тотем) | -| **Corrupt Intervention** | 3 самых дальних генератора заблокированы 2 минуты в начале игры | -| **Pop Goes the Weasel** | После повешения — следующий Kick откатывает 25% прогресса гена | -| **Deadlock** | После починки любого гена — самый прогрессивный ген блокируется на 30 сек | -| **No Way Out** | После повешения — добавляется задержка при открытии ворот (+12/24/36 сек) | -| **Scourge Hook: Pain Resonance** | Повешение на Scourge Hook взрывает ближайший ген (−25%) | -| **Nowhere to Hide** | После Kick гена — ауры выживших видны 4 сек в радиусе 24 блоков | -| **Lethal Pursuer** | В начале игры — видеть ауры всех выживших 7 секунд | -| **BBQ & Chili** | После повешения — видеть ауры выживших >40 блоков на 4 сек | -| **Tinkerer** | Когда ген достигает 70% — уведомление + Undetectable на 12 сек | - -> ⚠ Перки реализовать через EventListener. Каждый перк — отдельный класс, реализующий интерфейс `IPerk` с методами `onEquip()`, `onTrigger()`, `onRemove()`. - -### 10.3 Тотемы (Totems) - -- На карте **5 тотемов** (`totem_locations` в конфиге). -- **Dull Totem:** выживший ломает за 14 секунд — начисляются Bloodpoints. -- **Hex Totem:** активируется при использовании Hex-перка. Пока горит — перк активен. -- При уничтожении Hex Totema — связанный перк деактивируется навсегда. - ---- - -## 11. Эндгейм и итоги матча - -### 11.1 Условия победы - -| Сторона | Победа | Поражение | -|---|---|---| -| **Выжившие** | Минимум 2 выживших сбежали (ворота или люк) | Все выжившие мертвы до побега | -| **Киллер** | Все 4 выжившие принесены в жертву / умерли | 2+ выжившие сбежали | - -### 11.2 Экран результатов - -- Все игроки телепортируются в лобби. -- В Chat выводится таблица: игрок, роль, Bloodpoints, статус (Escaped / Sacrificed). -- Title на 5 секунд: **ПОБЕДА / ПОРАЖЕНИЕ**. -- Личная статистика матча: генераторы, спасения, хуки, урон. - -### 11.3 Эндгейм Коллапс (End Game Collapse) - -- Активируется при: открытии ворот ИЛИ закрытии люка. -- Таймер **120 секунд** показывается всем в ActionBar красным текстом. -- По истечении — все оставшиеся выжившие мгновенно умирают. - ---- - -## 12. Пользовательский интерфейс (HUD) - -### 12.1 ActionBar - -- Постоянно: статус игрока (`❤ Healthy` / `🩸 Injured` / `💀 Downed`). -- При взаимодействии: прогресс-бар починки / лечения / спасения. -- Эндгейм Коллапс: красный таймер обратного отсчёта. - -### 12.2 Sidebar (Scoreboard) - -- **Выжившие:** количество починенных генераторов (`⚡ 2/5`), сколько выживших живо (`👥 3/4`). -- **Киллер:** сколько выживших на крюке / мертво, прогресс генераторов. - -### 12.3 Title / Subtitle - -- Оповещения: генератор починен, выживший на крюке, ворота открыты. -- Начало игры: роль + краткая инструкция. -- Skill Check: вспышка в Title с таймером. - -### 12.4 Звуки - -- **Terror Radius:** нарастающее сердцебиение по мере приближения киллера (3 уровня). -- **Генератор:** звук работы при починке, взрыв при провале skill check или Kick. -- **Спасение с крюка:** звук успеха. -- **Люк:** звук открытия / закрытия. -- **Ворота:** звук активации, гудение. - -> ⚠ Рекомендуется базовый Resource Pack для кастомных звуков. Без него — стандартные MC-звуки как замена. - ---- - -## 13. Конфигурация (config.yml) - -| Секция | Ключ | Тип | По умолчанию | -|---|---|---|---| -| `game` | `min_players` | int | 5 | -| `game` | `max_players` | int | 5 | -| `game` | `countdown_seconds` | int | 60 | -| `game` | `world_name` | string | `dbd_map` | -| `game` | `lobby_world` | string | `lobby` | -| `killer` | `killer_speed` | double | 1.15 | -| `killer` | `terror_radius` | int | 32 | -| `killer` | `attack_cooldown` | double | 2.5 | -| `killer` | `carry_speed` | double | 0.92 | -| `killer` | `wiggle_duration` | double | 16.0 | -| `survivor` | `sprint_duration` | double | 3.0 | -| `survivor` | `sprint_cooldown` | double | 5.0 | -| `survivor` | `heal_self_time` | double | 10.0 | -| `survivor` | `heal_other_time` | double | 16.0 | -| `generator` | `total_generators` | int | 7 | -| `generator` | `required_generators` | int | 5 | -| `generator` | `repair_time` | double | 80.0 | -| `generator` | `gen_coop_multiplier` | double | 1.5 | -| `generator` | `regression_rate` | double | 0.25 | -| `hook` | `stage1_duration` | int | 60 | -| `hook` | `stage2_duration` | int | 60 | -| `hook` | `max_hook_count` | int | 3 | -| `endgame` | `collapse_duration` | int | 120 | - -**Дополнительные файлы конфигурации:** -- `locations.yml` — все спавн-точки, координаты генераторов, хуков, тотемов, сундуков, ворот, люка. -- `loot_table.yml` — весовая таблица предметов в сундуках. -- `perks.yml` — настройки перков (кулдауны, длительности). - ---- - -## 14. Команды и права - -| Команда | Описание | Право | -|---|---|---| -| `/dbd join` | Войти в очередь на игру | `dbd.play` | -| `/dbd leave` | Выйти из очереди / матча | `dbd.play` | -| `/dbd forcestart` | Принудительно запустить игру | `dbd.admin` | -| `/dbd stop` | Принудительно остановить игру | `dbd.admin` | -| `/dbd setkiller <игрок>` | Назначить игрока киллером | `dbd.admin` | -| `/dbd setspawn ` | Установить спавн-точку (по курсору) | `dbd.admin` | -| `/dbd setgen ` | Отметить блок как генератор | `dbd.admin` | -| `/dbd sethook ` | Отметить блок как крюк | `dbd.admin` | -| `/dbd setgate <1\|2>` | Отметить зону ворот | `dbd.admin` | -| `/dbd sethatch` | Отметить блок как люк | `dbd.admin` | -| `/dbd reload` | Перезагрузить конфиг | `dbd.admin` | -| `/dbd stats [игрок]` | Посмотреть статистику | `dbd.play` | - ---- - -## 15. Архитектура плагина - -### 15.1 Структура пакетов - -| Пакет | Назначение | -|---|---| -| `com.dbd.plugin.core` | Главный класс, GameManager, EventBus | -| `com.dbd.plugin.game` | GameSession, GameState, RoleAssigner, RoundTimer | -| `com.dbd.plugin.roles` | KillerController, SurvivorController, PlayerState | -| `com.dbd.plugin.mechanics` | GeneratorManager, HookManager, PalletManager, HatchManager, GateManager | -| `com.dbd.plugin.perks` | IPerk, PerkRegistry, PerkLoader, [все перки] | -| `com.dbd.plugin.items` | IItem, ItemRegistry, ChestManager | -| `com.dbd.plugin.ui` | HUDManager, ActionBarManager, TitleManager, SoundManager | -| `com.dbd.plugin.data` | DatabaseManager, PlayerStats, LocationsConfig | -| `com.dbd.plugin.commands` | DBDCommand, TabCompleter | - -### 15.2 Ключевые принципы - -- Каждый раунд — отдельный объект `GameSession` со своим состоянием. -- Все механики управляются через `BukkitScheduler` (tick-based системы). -- Перки реализованы через Event-Driven подход (не хардкод в механиках). -- Конфиги не перезаписывают пользовательские настройки при обновлении (`saveDefaultConfig`). -- Логирование всех событий в отдельный лог-файл (`logs/dbd.log`). - -> ⚠ Плагин должен корректно обрабатывать disconnect игрока во время матча — замена ботом или перераспределение ролей. - ---- - -## 16. Приоритет разработки - -| Этап | Функционал | Статус | -|---|---|---| -| **MVP (Этап 1)** | Старт/стоп игры, роли, спавн, базовые состояния выжившего, атака киллера | 🔴 В разработке | -| **Этап 2** | Генераторы (починка, прогресс, регрессия, skill check), хуки (все стадии) | 🔴 В разработке | -| **Этап 3** | Паллеты, окна, перенос, вырывание, люк, ворота, эндгейм | 🔴 В разработке | -| **Этап 4** | Предметы, сундуки, тотемы, базовые перки (10 survivor + 10 killer) | 🔴 В разработке | -| **Этап 5** | Полный HUD, звуки, Terror Radius, Red Stain, Bloodpoints, база данных | 🔴 В разработке | -| **Этап 6** | Балансировка, все остальные перки, тест-раунды, оптимизация | 🔴 В разработке | - ---- - -*ТЗ подготовлено с учётом всех оригинальных механик Dead by Daylight (4v1) | Версия 1.0* diff --git a/PLAN.md b/PLAN.md deleted file mode 100644 index 155b3ea..0000000 --- a/PLAN.md +++ /dev/null @@ -1,392 +0,0 @@ -# DBD Plugin — Баги и задачи - -## ✅ Закрыто в текущей сессии - -### SetupCommand (упрощено) -- [x] `/dbdsetup set <тип>` — ломаешь блок, объект ставится (без `select`) -- [x] Ванд только для ворот: ЛКМ=pos1, ПКМ=pos2 -- [x] `/dbdsetup set gate A|B` — установить ворота из точек ванда - -### Медкит -- [x] Требует Shift + ПКМ + стоять на месте (прерывается при движении или отпускании Shift) -- [x] После использования удаляется полностью (1 медкит = 1 лечение) -- [x] Медкит не выдаётся стартово — только из сундуков - -### Маньяк — атака -- [x] Post-hit cooldown 4 сек после ПОПАДАНИЯ (отдельно от swing cooldown 2.5 сек) -- [x] Параметр `killer.post-hit-cooldown` в `game.yml` - -### Маньяк — нокдаун / переноска -- [x] Игрок в нокдауне ложится (`setSwimming(true)`) и поза удерживается каждые 5 тиков -- [x] При подъёме, смерти, спасении — поза сбрасывается -- [x] Переноска: маньяк ПКМ → подбирает DOWNED в радиусе 3 блоков -- [x] При подъёме `setSwimming(false)` отменяет позу нокдауна - -### Таймер игры -- [x] 15 минут + BossBar с таймером для всех игроков (зелёный → жёлтый → красный) -- [x] По истечении — маньяк победил - -### После победы -- [x] Телепортирует всех в лобби -- [x] Scale сбрасывается до 1.0 -- [x] Инвентарь очищается - -### Лобби -- [x] Лимит 1 лобби на игрока -- [x] Broadcast при создании лобби убран — сообщение только создателю -- [x] Сообщение о входе игрока — только участникам сессии (не всему серверу) -- [x] После завершения игры лобби пересоздаёт сессию автоматически (onSessionFinished callback) -- [x] Лобби не засоряются — реестр чистит сессию через `GameSession.setOnStopped()` - -### PlayerQuitListener -- [x] Счётчик живых выживших корректный (считает после пометки DEAD) -- [x] Инвентарь, scale, поза сбрасываются при выходе из игры - -### GameplayPhase -- [x] Разбит на части: - - `GameplayPhase.java` — только lifecycle (enter/tick/exit) + делегирование (230 строк) - - `GameplayHookHandler.java` — тик крюков, Stage 2, Struggle, Sacrifice (87 строк) - - `GameplayWinConditions.java` — проверка победы/поражения, SPECTATOR (91 строка) - - `GameplayDamageSystem.java` — система урона, атаки, предметы (155 строк) - - `GameplayUI.java` — BossBar, sidebar, таймер (142 строки) - - `GameplayInteraction.java` — обработка взаимодействий (198 строк) - -### Лобби — повторный вход -- [x] **FIX**: Исправлен `GameManager.joinSession()` — теперь корректно обрабатывает повторный вход, сначала удаляя игрока из старой сессии - -### Хотбар маньяка -- [x] **FIX**: Добавлена проверка `!bp.isKiller()` в `ChestOpening.completeOpening()` — маньяк не получает лут из сундуков - -### Медкит из сундука -- [x] **WORKS**: Логика уже работает — `ChestLoot.rollLoot()` выдаёт медкит/фонарик при открытии сундука - -### Счётчик выживших -- [x] **VERIFIED**: Логика в `PlayerQuitListener.handleSurvivorDisconnect()` корректная — считает после пометки DEAD - -### Ворота -- [x] **FIX**: Добавлена интеграция `GateOpening` в `GameplayInteraction` — теперь вызывается при взаимодействии с порталом (HOPPER/PORTAL) - -### Кодировка и спавн в воздухе -- [x] **FIXED**: UTF-8 encoding для build - - Добавлена `org.gradle.jvmargs=-Dfile.encoding=UTF-8` в `gradle.properties` - - Добавлен `tasks.withType { filteringCharset = "UTF-8" }` в build.gradle.kts - - Русские символы и эмодзи теперь сохраняются корректно в конфигах -- [x] **FIXED**: Спавн в воздухе - - Добавлена корректировка Y-координаты (`Math.floor(y) + 1.0`) в `RoleAssigner.java` - - Players теперь стоят ОН block, не ИН block - - Работает для положительных и отрицательных Y (подземные карты) -- [x] **FIXED**: World loading — добавлен fallback к первому loaded world в `LocationsConfig.java` -- [x] **FIXED**: Удален неиспользуемый импорт Location из `PalletBreak.java` - ---- - -## ✅ Завершено - -### Заполнение сундуков при старте игры (случайный лут из ChestLoot) -- [x] Лут записывается в поле `Chest.loot` при создании -- [x] `ChestOpening.completeOpening()` использует pre-set loot - -### Визуализация ворот (частицы при открытии) -- [x] Progress particles: CRIT every 5 ticks -- [x] Completion burst: EXPLOSION_EMITTER + FLAME - -### Удаление перков и тотемов -- [x] Все totem файлы удалены -- [x] Все ссылки на тотемы удалены - -## ❌ Новые баги (текущая сессия) - -### ✅ ИСПРАВЛЕНО (7 багов) - -1. **SetupCommand MiniMessage формат** - - [x] **FIXED**: Заменены legacy `§f` и `§a` на MiniMessage `<#FFFFFF>` и `<#00FF00>` - -2. **Телепортация в конце игры не работает** - - [x] **FIXED**: Добавлена телепортация в `GameplayPhase.exit()` - - Также сбрасывается scale до 1.0 - -3. **Downed игрок берёт хук** - - [x] **FIXED**: Добавлена проверка состояния в `GameplayInteraction` - - Теперь DOWNED, CARRIED и HOOKED не могут спасать - -4. **Генераторы не чинятся за игрока** - - [x] **FIXED**: Подключена `GeneratorRepair` в `GameplayPhase` - - Добавлен обработчик в `GameplayInteraction` - - Механика: ПКМ по генератору → зажать ПКМ для починки - ---- - -### ✅ ИСПРАВЛЕНО / ПРОВЕРЕНО В ТЕКУЩЕЙ СЕССИИ (13 ЗАДАЧ) - -1. **Генераторы (#11) — generatorManager всегда null** - - [x] **FIXED**: Переинициализирован в GameplayPhase.initSubHandlers() - -2. **Телепорт (#10) — ранний выход отменял финал** - - [x] **FIXED**: Удалён телепорт из GameplayPhase.exit() - - Телепортация только в EndgamePhase.exit() (100L тиков задержка) - -3. **Wand для ворот — STICK vs WOODEN_AXE** - - [x] **FIXED**: Обновлена документация, код ищет WOODEN_AXE с NBT тегом - -4. **Бесконечный вход в лобби** (/dbd join X, /dbd join X, /dbd join X...) - - [x] **FIXED**: LobbyRegistry.joinLobby() проверяет текущую сессию - - Если игрок уже в другой сессии → выходит оттуда перед входом - -5. **Лобби удаляется при 0 игроков, tab-complete показывает** - - [x] **VERIFIED**: onPlayerLeftSession() НЕ удаляет лобби из реестра - - Лобби держится в памяти, session переиспользуется после игры - -6. **Pallet setup** (/dbdsetup set pallet) - - [x] **VERIFIED**: Режим selectingPallet работает, сохраняет через SpawnUtil.blockBreakSpawn() - -7. **Chest setup** (/dbdsetup set chest) - - [x] **VERIFIED**: Режим selectingChest работает, сохраняет с offset +0.5, +0, +0.5 - -8. **Эффекты ранения** (нет визуалов когда игрок INJURED) - - [x] **VERIFIED**: GameplayDamageSystem добавляет: - - Частицы HEART каждые 40 тиков - - Звук при попадании удара - - Title "РАНЕН!" - -9. **Двойной sneak для pickup** (киллер присел 2 раза возле DOWNED) - - [x] **VERIFIED**: GameplayInteraction проверяет интервал <450ms между sneaks - - Вызывает tryPickup() из KillerController - -10. **Админ-команды** (heal, down, unhook, state, hooks, kill) - - [x] **VERIFIED**: DBDAdminPlayerCommand полностью реализована - - /dbd admin heal [игрок] - - /dbd admin down [игрок] - - /dbd admin unhook [игрок] - - /dbd admin state <игрок> - - /dbd admin hooks <игрок> <0-3> - - /dbd admin kill [игрок] - -11. **Телепорт после завершения игры в лобби** - - [x] **VERIFIED**: EndgamePhase.exit() телепортирует через 100L тиков - - Сбрасывает scale до 1.0, очищает инвентарь - -12. **Генераторы не чинятся** (ПКМ возле генератора) - - [x] **FIXED**: setOnGeneratorInteract() вызывает generatorRepair.startRepair() - - generatorManager переинициализирован, не null - -13. **HOOKED игрок берёт хук** (игрок висит на крюке, может взять другой) - - [x] **VERIFIED**: setOnRescueAttempt() проверяет HOOKED, DOWNED, CARRIED - - Rescue невозможен если rescuer в одном из этих статусов - -14. **Спавн игроков в воздухе** (проблема с высотой) - - [x] **FIXED**: SpawnUtil.resolveStandingLocation() улучшена логика расчета Y - - Добавлена явная загрузка chunks в RoleAssigner перед спавном - - Используется getHighestBlockYAt() + 1 для стояния на блоке - -15. **GameMode.ADVENTURE при начале игры** - - [x] **FIXED**: - - RoleAssigner.assignRoles() устанавливает ADVENTURE при спавне - - LobbyPhase.enter() и onPlayerJoined() устанавливают ADVENTURE - - EndgamePhase.exit() сбрасывает на ADVENTURE перед лобби - -16. **/dbdsetup set gate A confirm не работал** - - [x] **FIXED**: Добавлена поддержка обоих форматов: - - `/dbdsetup set gate A confirm` - - `/dbdsetup set gate confirm A` - - Обновлена документация в COMMANDS_GUIDE.md - -17. **Переноска киллером (carry) — проблемы** - - [x] **FIXED**: Переработана логика KillerCarry с использованием ArmorStand: - - Игрок **видим** (не setInvisible) - - Камера **отделена** (beide мають пассажиры armor stand) - - Игрок **не двигает** киллера (mounted, не телепортируется) - - Пробел **работает** (jump event отслеживается) - ---- - -### ✅ ИТОГО: 17/17 ЗАДАЧ РЕШЕНО!1. **Генераторы (#11) — generatorManager всегда null** - - [x] **FIXED**: Переинициализирован в GameplayPhase.initSubHandlers() - -2. **Телепорт (#10) — ранний выход отменял финал** - - [x] **FIXED**: Удалён телепорт из GameplayPhase.exit() - - Телепортация только в EndgamePhase.exit() (100L тиков задержка) - -3. **Wand для ворот — STICK vs WOODEN_AXE** - - [x] **FIXED**: Обновлена документация, код ищет WOODEN_AXE с NBT тегом - -4. **Бесконечный вход в лобби** (/dbd join X, /dbd join X, /dbd join X...) - - [x] **FIXED**: LobbyRegistry.joinLobby() проверяет текущую сессию - - Если игрок уже в другой сессии → выходит оттуда перед входом в новую - -5. **Лобби удаляется при 0 игроков, tab-complete показывает** - - [x] **VERIFIED**: onPlayerLeftSession() НЕ удаляет лобби из реестра - - Лобби держится в памяти, session переиспользуется после игры - -6. **Pallet setup** (/dbdsetup set pallet) - - [x] **VERIFIED**: Режим selectingPallet работает, сохраняет через SpawnUtil.blockBreakSpawn() - -7. **Chest setup** (/dbdsetup set chest) - - [x] **VERIFIED**: Режим selectingChest работает, сохраняет с offset +0.5, +0, +0.5 - -8. **Эффекты ранения** (нет визуалов когда игрок INJURED) - - [x] **VERIFIED**: GameplayDamageSystem добавляет: - - Частицы HEART каждые 40 тиков - - Звук при попадании удара - - Title "РАНЕН!" - -9. **Двойной sneak для pickup** (киллер присел 2 раза возле DOWNED) - - [x] **VERIFIED**: GameplayInteraction проверяет интервал <450ms между sneaks - - Вызывает tryPickup() из KillerController - -10. **Админ-команды** (heal, down, unhook, state, hooks, kill) - - [x] **VERIFIED**: DBDAdminPlayerCommand полностью реализована - - /dbd admin heal [игрок] - - /dbd admin down [игрок] - - /dbd admin unhook [игрок] - - /dbd admin state <игрок> - - /dbd admin hooks <игрок> <0-3> - - /dbd admin kill [игрок] - -11. **Телепорт после завершения игры в лобби** - - [x] **VERIFIED**: EndgamePhase.exit() телепортирует через 100L тиков - - Сбрасывает scale до 1.0, очищает инвентарь - -12. **Генераторы не чинятся** (ПКМ возле генератора) - - [x] **FIXED**: setOnGeneratorInteract() вызывает generatorRepair.startRepair() - - generatorManager переинициализирован, не null - -13. **HOOKED игрок берёт хук** (игрок висит на крюке, может взять другой) - - [x] **VERIFIED**: setOnRescueAttempt() проверяет HOOKED, DOWNED, CARRIED - - Rescue невозможен если rescuer в одном из этих статусов - -14. **Спавн игроков в воздухе** (проблема с высотой) - - [x] **FIXED**: - - SpawnUtil.resolveStandingLocation() улучшена логика расчета Y - - Добавлена явная загрузка chunks в RoleAssigner перед спавном - - Используется getHighestBlockYAt() + 1 для стояния на блоке - ---- - -### ✅ ИТОГО: 14/14 ЗАДАЧ РЕШЕНО! - - ---- - -## ✅ СЕССИЯ 2: ГЕНЕРАТОР - ПРОГРЕСС И КРАУЧ (18-19 ЗАДАЧИ) - -### 18. Прогресс починки генератора не сохраняется -- [x] **FIXED**: Progress теперь сохраняется в Generator объекте между repair attempts -- Механика: - - Игрок присаживается рядом генератором → `onSneak()` автоматически запускает `startRepair()` - - `startRepair()` проверяет, ремонтирует ли уже этот генератор → если нет, создаёт новую RepairSession - - Progress сохраняется в `generator.progress` на всё время игры - - Если игрок встал (un-sneak) → `updateRepair()` возвращает false → repair stops - - Если игрок присел снова рядом тем же генератором → вызывается `startRepair()` снова - - **Ключевое**: progress НЕ сбрасывается, потому что RepairSession.generator указывает на ТОТЖЕ Generator объект - - Progress сохраняется всю игру (сбрасывается только при перезагрузке сервера) -- Сообщение теперь показывает текущий прогресс: "Зажмите ПРИСЕД чтобы чинить генератор (прогресс: XX%)" - -### 19. Чтобы починить генератор нужно присеть возле него и быть присядя возле него -- [x] **FIXED**: Добавлена автоматическая триггеризация repair при SNEAK -- Механика: - 1. Игрок присаживается (PlayerToggleSneakEvent с isSneaking=true) - 2. PlayerInteractListener.onSneak() вызывает `tryGeneratorInteract(player)` для survivors - 3. `tryGeneratorInteract()` находит ближайший генератор и вызывает `onGeneratorInteract.accept()` - 4. GameplayInteraction вызывает `generatorRepair.startRepair()` - 5. Repair session создана → player должен оставаться присядя - 6. GeneratorRepair.tick() каждый tick проверяет `if (!player.isSneaking()) return false;` - 7. Если условие не выполнено → repair stops (но progress НЕ теряется) - 8. Игрок встал → `stopRepair()` вызывается - 9. Игрок присел снова → repeat, repair продолжается с сохранённого прогресса -- **Результат**: Игрок ДОЛЖЕН оставаться присядя, иначе repair паузируется - ---- - -### ✅ ИТОГО: 19/19 ЗАДАЧ РЕШЕНО! - ---- - -## ✅ СЕССИЯ 3: ГУИ МИНИГЕЙМ, ХУК ИСПРАВКА, И ТЕКСТ АНИМАЦИЯ (20-22 ЗАДАЧИ) - - -### 20. Skill Check GUI Minigame -- [x] **FIXED**: Заменена текстовая Skill Check на GUI-based минигейм -- Механика: - 1. Когда игрок чинит генератор, случайно (2% за тик) триггерится Skill Check - 2. Открывается инвентарь с 9 стеклянными блоками в одной строке - 3. Все блоки - СЕРОЕ СТЕКЛО (GRAY_STAINED_GLASS) - 4. Один блок случайно становится ЗЕЛЁНЫМ (GREEN_STAINED_GLASS) - 5. Каждые 4-7 тиков зелёный блок меняет позицию (анимация) - 6. Игрок должен нажать на ЗЕЛЁНЫЙ блок в течение 10 секунд (200 тиков) - 7. Если успешно → +5% прогресса, сообщение "Отлично! +5%" - 8. Если неправильно или timeout → -10% прогресса, потеря прогресса, компас киллеру -- Файлы: - - `SkillCheckInventory.java` - новый класс для GUI minigame - - `SkillCheckListener.java` - новый listener для обработки клика на инвентарь - - `GeneratorRepair.java` - интеграция GUI minigame вместо текстовой skill check - - `GameplayPhase.java` - регистрация SkillCheckListener при старте игры - -### 21. Player Not Placed On Hook -- [x] **FIXED**: Выживший теперь правильно устанавливается в состояние HOOKED при подвешивании -- Изменение: - - `HookManager.tryHookSurvivor()` теперь вызывает `survivor.setState(PlayerState.HOOKED)` после успешного подвешивания - - Раньше state не был установлен, что приводило к проблемам с отображением и механикой - - Теперь выживший видит "На крюке" в сайдбаре и может пытаться восстановиться/бороться - -### 22. Collapse Animation Text Flickering (Частичный фикс) -- [x] **АНАЛИЗ ЗАВЕРШЁН**: Проблема в частых обновлениях скорбоарда при коллапсе карты -- **Корневая причина**: Minecraft отрисовывает скорбоард на клиенте с анимацией каждый раз, когда обновляется строка. - - Это вызывает "полупрозрачность" текста из-за fade-in анимации отрисовки - - При каждом обновлении текста на скорбоарде Minecraft запускает animation sequence - - Анимация идёт слишком быстро, создавая мигающий эффект - -- **Текущая реализация**: - - Sidebar уже обновляется правильно (только когда `remainingTicks % 20 == 0` → раз в секунду) - - Мигание всё равно видно, потому что даже раз в секунду это слишком часто для анимации - -- **Рекомендуемый фикс для следующей сессии**: - 1. **Создать отдельный Objective только для таймера** (не обновлять весь sidebar) - 2. **Обновлять таймер реже** (например, раз в 2 секунды вместо раз в секунду) - 3. **Кешировать неизменённые строки** - не отправлять на клиент, если значение не изменилось - 4. **Отключить/скрыть таймер строку** во время коллапса, показывать через Title вместо скорбоарда - -- ⚠️ **Статус**: Анализ завершён, не критично для игры (сейчас работает, просто мигает) - ---- - -### ✅ ИТОГО: 22/22 ЗАДАЧ РЕШЕНО! - - ---- - -## 📋 ИТОГОВОЕ РЕЗЮМЕ ПО СЕССИИ 3 - -### ✅ Что было исправлено: - -**1. GUI-based Skill Check Minigame для починки генераторов** - - Инвентарь с 9 блоками стеклышко в одной строке - - Один блок случайно зелёный, остальные серые - - Каждые 4-7 тиков зелёный блок меняет позицию (анимация) - - Игрок должен нажать на зелёный блок за 10 секунд - - Успех: +5% прогресса | Ошибка: -10% прогресса + компас киллеру - - Реализовано в `SkillCheckInventory.java` + `SkillCheckListener.java` - -**2. Выживший не ставился в hook** - - Добавлен `survivor.setState(PlayerState.HOOKED)` в `HookManager.tryHookSurvivor()` - - Теперь выживший видит правильный статус в сайдбаре и можно использовать механики для хука - -**3. Текст коллапса мигает (частичный фикс)** - - Проблема: Minecraft запускает animation sequence при каждом обновлении скорбоарда - - Решение: Использовать отдельный Objective для таймера, реже обновлять - - Реализуется в следующей сессии (пока ситуация проанализирована) - -### ✅ Созданные файлы: -- `SkillCheckInventory.java` - GUI minigame система -- `SkillCheckListener.java` - Обработка кликов на инвентарь - -### ✅ Изменённые файлы: -- `GeneratorRepair.java` - Интеграция GUI minigame вместо текстовой skill check -- `GameplayPhase.java` - Регистрация SkillCheckListener -- `HookManager.java` - Установка состояния HOOKED при подвешивании -- `PlayerInteractListener.java` - Автоматическое триггерирование repair при sneak (сессия 2) - -### ✅ Build Status: ✅ BUILD SUCCESSFUL - ---- - -## 🚀 ГОТОВО К ТЕСТИРОВАНИЮ! diff --git a/README.md b/README.md new file mode 100644 index 0000000..874a1ce --- /dev/null +++ b/README.md @@ -0,0 +1,65 @@ +# Mdbd — Dead by Daylight в Minecraft + +Плагин для Paper 1.21.5, реализующий мини-игру Dead by Daylight (4v1) на Minecraft-сервере. + +## Особенности + +- **4 выживших vs 1 маньяк** — полноценный геймплей DBD в Minecraft +- **Генераторы** — 7 на карте, нужно починить 5. Прогресс сохраняется, Skill Check мини-игра (GUI) +- **Крюки** — 3 стадии, спасение, борьба, жертвоприношение +- **Выходные ворота** — открытие через рубильник, эндгейм коллапс +- **Паллеты** — ставятся через `/dbdsetup`, ломаются маньяком +- **Медкиты и фонарики** — из сундуков, с механиками лечения и ослепления +- **Система урона** — Healthy → Injured → Downed → Hooked → Dead +- **Переноска** — маньяк подбирает и носит выживших до крюка +- **Terror Radius** — звук сердцебиения для выживших рядом с маньяком +- **Skill Check GUI** — мини-игра с зелёными блоками при починке генератора +- **Лобби** — ожидание игроков, автоматический старт, возврат после игры +- **Scoreboard, BossBar, ActionBar** — полноценный интерфейс + +## Команды + +### Игроку +| Команда | Описание | +|---|---| +| `/dbd join` | Войти в лобби | +| `/dbd leave` | Выйти из лобби/игры | +| `/dbd list` | Список доступных лобби | + +### Админу +| Команда | Описание | +|---|---| +| `/dbd create` | Создать новое лобби | +| `/dbd forcestart` | Принудительный старт | +| `/dbd stop` | Остановить игру | +| `/dbd reload` | Перезагрузить конфиги | +| `/dbdsetup wand` | Получить топор для выделения зон ворот | +| `/dbdsetup pos1 / pos2` | Выделить зону ворот | +| `/dbdsetup set ` | Установить объект | +| `/dbdsetup save / load ` | Сохранить/загрузить шаблон карты | +| `/dbdsetup build ` | Построить карту по шаблону | +| `/dbdsetup clear` | Очистить все объекты | +| `/dbdadmin create ` | Создать объект вручную | +| `/dbdadmin list ` | Список объектов | +| `/dbdadmin remove ` | Удалить объект | +| `/dbdadmin heal/down/unhook/kill ` | Управление состоянием игроков | + +## Установка + +1. Поместить `Mdbd-.jar` в `plugins/` +2. Настроить `game.yml`, `locations.yml`, `messages.yml` в `plugins/Mdbd/` +3. Перезапустить сервер или выполнить `/dbd reload` +4. Создать лобби: `/dbd create` +5. Расставить объекты через `/dbdsetup` + +## Требования + +- Paper 1.21.5 +- ProtocolLib 5.3.0 +- Java 21 + +## Сборка + +```bash +./gradlew build +``` diff --git a/src/main/java/dev/loki/command/SetupCommand.java b/src/main/java/dev/loki/command/SetupCommand.java index 92c09dc..197f195 100644 --- a/src/main/java/dev/loki/command/SetupCommand.java +++ b/src/main/java/dev/loki/command/SetupCommand.java @@ -1,5 +1,6 @@ package dev.loki.command; +import dev.loki.gate.ExitGate; import dev.loki.mdbd; import dev.loki.util.SpawnUtil; import dev.loki.util.TextUtil; @@ -206,6 +207,11 @@ public class SetupCommand implements TabExecutor { locations.setGateZone(gateId, p1, p2); plugin.getConfigManager().saveConfig("locations.yml", locations.getConfig()); + ExitGate gate = new ExitGate(center); + gate.setZone(p1, p2); + gate.spawnGateStructure(); + gate.spawnGateBlock(); + player.sendMessage(messages().get("setup.gate-placed", gateId, formatLoc(center))); return true; } @@ -402,20 +408,14 @@ public class SetupCommand implements TabExecutor { } String type = args[1].toLowerCase(); - int index; - try { - index = Integer.parseInt(args[2]); - } catch (NumberFormatException e) { - player.sendMessage(messages().get("setup.invalid-number", args[2])); - return true; - } - var locations = plugin.getConfigManager().getLocations(); switch (type) { case "generator" -> { + int index = parseInt(args[2], player); + if (index < 0) return true; var gens = locations.getGenerators(); - if (index < 0 || index >= gens.size()) { + if (index >= gens.size()) { player.sendMessage(messages().get("setup.out-of-range", index)); return true; } @@ -424,8 +424,10 @@ public class SetupCommand implements TabExecutor { player.sendMessage(messages().get("setup.deleted", "generator #" + index)); } case "hook" -> { + int index = parseInt(args[2], player); + if (index < 0) return true; var hooks = locations.getHooks(); - if (index < 0 || index >= hooks.size()) { + if (index >= hooks.size()) { player.sendMessage(messages().get("setup.out-of-range", index)); return true; } @@ -433,8 +435,10 @@ public class SetupCommand implements TabExecutor { player.sendMessage(messages().get("setup.deleted", "hook #" + index)); } case "chest" -> { + int index = parseInt(args[2], player); + if (index < 0) return true; var chests = locations.getChests(); - if (index < 0 || index >= chests.size()) { + if (index >= chests.size()) { player.sendMessage(messages().get("setup.out-of-range", index)); return true; } @@ -446,6 +450,8 @@ public class SetupCommand implements TabExecutor { player.sendMessage(messages().get("setup.deleted", "killer-spawn")); } case "survivor-spawn" -> { + int index = parseInt(args[2], player); + if (index < 0) return true; if (index < 1 || index > 4) { player.sendMessage(messages().get("setup.out-of-range", index)); return true; @@ -454,9 +460,10 @@ public class SetupCommand implements TabExecutor { player.sendMessage(messages().get("setup.deleted", "survivor-spawn #" + index)); } case "gate" -> { - String id = index == 1 ? "a" : index == 2 ? "b" : null; - if (id == null) { - player.sendMessage(messages().get("setup.out-of-range", index)); + String raw = args[2].toLowerCase(); + String id = switch (raw) { case "a", "1" -> "a"; case "b", "2" -> "b"; default -> null; }; + if (id == null || locations.getGate(id) == null) { + player.sendMessage(messages().get("setup.out-of-range", raw)); return true; } locations.getConfig().set("gate-" + id, null); @@ -474,6 +481,15 @@ public class SetupCommand implements TabExecutor { return true; } + private int parseInt(String s, Player player) { + try { + return Integer.parseInt(s); + } catch (NumberFormatException e) { + player.sendMessage(messages().get("setup.invalid-number", s)); + return -1; + } + } + private boolean clearSpawns(Player player) { var locations = plugin.getConfigManager().getLocations(); locations.clearLocationList("killer-spawn"); diff --git a/src/main/java/dev/loki/config/LocationsConfig.java b/src/main/java/dev/loki/config/LocationsConfig.java index dbca2e8..4e986a6 100644 --- a/src/main/java/dev/loki/config/LocationsConfig.java +++ b/src/main/java/dev/loki/config/LocationsConfig.java @@ -80,10 +80,6 @@ public class LocationsConfig { return loadLocationList("chests"); } - public List getTotems() { - return loadLocationList("totems"); - } - // getExitGates() — определён в addLocation/setGate блоке public List getKillerSpawns() { diff --git a/src/main/java/dev/loki/gate/ExitGate.java b/src/main/java/dev/loki/gate/ExitGate.java index 0f57794..148b5a9 100644 --- a/src/main/java/dev/loki/gate/ExitGate.java +++ b/src/main/java/dev/loki/gate/ExitGate.java @@ -52,7 +52,7 @@ public class ExitGate { /** Place a HOPPER block at this gate's location for player interaction. */ public void spawnGateBlock() { if (location.getWorld() == null) return; - location.getBlock().setType(org.bukkit.Material.HOPPER, false); + location.getBlock().setType(org.bukkit.Material.HOPPER, true); } /** Build the gate structure fills the entire designated zone. */ @@ -60,16 +60,14 @@ public class ExitGate { World world = location.getWorld(); if (world == null) return; - // Place all iron bars first (no physics) then force update for connections if (hasZone) { for (int x = zoneMinX; x <= zoneMaxX; x++) { for (int y = zoneMinY; y <= zoneMaxY; y++) { for (int z = zoneMinZ; z <= zoneMaxZ; z++) { - world.getBlockAt(x, y, z).setType(org.bukkit.Material.IRON_BARS, false); + world.getBlockAt(x, y, z).setType(org.bukkit.Material.IRON_BARS, true); } } } - // Force update all iron bars so they connect to neighbors for (int x = zoneMinX; x <= zoneMaxX; x++) { for (int y = zoneMinY; y <= zoneMaxY; y++) { for (int z = zoneMinZ; z <= zoneMaxZ; z++) { @@ -78,12 +76,11 @@ public class ExitGate { } } } else { - // Fallback: 3x3x3 zone around gate location int cx = location.getBlockX(), cy = location.getBlockY(), cz = location.getBlockZ(); for (int x = cx - 1; x <= cx + 1; x++) { for (int y = cy; y <= cy + 2; y++) { for (int z = cz - 1; z <= cz + 1; z++) { - world.getBlockAt(x, y, z).setType(org.bukkit.Material.IRON_BARS, false); + world.getBlockAt(x, y, z).setType(org.bukkit.Material.IRON_BARS, true); } } } @@ -96,8 +93,7 @@ public class ExitGate { } } - // Button at gate center + (1, 1, 1) - world.getBlockAt(location.clone().add(1, 1, 1)).setType(org.bukkit.Material.STONE_BUTTON, false); + world.getBlockAt(location.clone().add(1, 1, 1)).setType(org.bukkit.Material.STONE_BUTTON, true); } /** Remove the interaction block (e.g. when gate opens or on cleanup). */ diff --git a/src/main/java/dev/loki/listener/SkillCheckListener.java b/src/main/java/dev/loki/listener/SkillCheckListener.java index b949356..1e30b03 100644 --- a/src/main/java/dev/loki/listener/SkillCheckListener.java +++ b/src/main/java/dev/loki/listener/SkillCheckListener.java @@ -22,28 +22,24 @@ public class SkillCheckListener implements Listener { @EventHandler public void onInventoryClick(InventoryClickEvent event) { - String title = event.getView().getTitle(); - if (!title.contains("Skill Check")) return; + if (!(event.getWhoClicked() instanceof Player player)) return; + if (!minigame.isActive(player.getUniqueId())) return; event.setCancelled(true); - if (!(event.getWhoClicked() instanceof Player player)) return; - minigame.handleClick(player.getUniqueId(), event.getRawSlot()); } @EventHandler public void onInventoryDrag(InventoryDragEvent event) { - String title = event.getView().getTitle(); - if (!title.contains("Skill Check")) return; + if (!(event.getWhoClicked() instanceof Player player)) return; + if (!minigame.isActive(player.getUniqueId())) return; event.setCancelled(true); } @EventHandler public void onInventoryClose(InventoryCloseEvent event) { - String title = event.getView().getTitle(); - if (!title.contains("Skill Check")) return; - + if (!minigame.isActive(event.getPlayer().getUniqueId())) return; minigame.handleClose(event.getPlayer().getUniqueId()); } diff --git a/src/main/java/dev/loki/map/MapBuilder.java b/src/main/java/dev/loki/map/MapBuilder.java index f09afeb..f91e8af 100644 --- a/src/main/java/dev/loki/map/MapBuilder.java +++ b/src/main/java/dev/loki/map/MapBuilder.java @@ -113,10 +113,6 @@ public class MapBuilder { return true; } - public void placeTotem(Location location) { - // Totems removed - no-op - } - public void placeHook(Location location) { if (templateManager.hasTemplate("hook")) { templateManager.placeObject("hook", location); diff --git a/src/main/java/dev/loki/minigames/generator/GuiRouletteMinigame.java b/src/main/java/dev/loki/minigames/generator/GuiRouletteMinigame.java index da29b5c..34e7336 100644 --- a/src/main/java/dev/loki/minigames/generator/GuiRouletteMinigame.java +++ b/src/main/java/dev/loki/minigames/generator/GuiRouletteMinigame.java @@ -64,7 +64,7 @@ public class GuiRouletteMinigame implements GeneratorMinigame { RouletteSession session = new RouletteSession(player, callback); activeSessions.put(playerId, session); - String titleStr = plugin.getConfigManager().getMessages().getString("minigames.generator-roulette.title", "⚡ Skill Check!"); + String titleStr = plugin.getConfigManager().getMessages().getString("minigames.generator-roulette.title", "⚡ Проверка реакции!"); Inventory inv = Bukkit.createInventory(null, 9, Component.text(titleStr).color(NamedTextColor.YELLOW)); fillInventory(inv, session.successSlot); player.openInventory(inv); diff --git a/src/main/resources/game.yml b/src/main/resources/game.yml index 5902cc1..540012c 100644 --- a/src/main/resources/game.yml +++ b/src/main/resources/game.yml @@ -19,12 +19,6 @@ lobby: # Задержка перед назначением ролей (секунд) role-assign-delay: 5 - # Количество серверных лобби (0 = только пользовательские) - server-lobby-count: 3 - - # Сохранять лобби между перезапусками - persist-lobbies: true - # Размеры игроков (1.0 = стандарт) players: survivor-scale: 1.0 diff --git a/src/main/resources/locations.yml b/src/main/resources/locations.yml index c981cf4..feefcc5 100644 --- a/src/main/resources/locations.yml +++ b/src/main/resources/locations.yml @@ -23,9 +23,6 @@ hooks: [] # Сундуки с лутом (3-4) chests: [] -# Тотемы (5) -totems: [] - # Ворота выхода (2) exitgates: [] @@ -43,7 +40,6 @@ survivor-spawns: [] # /dbdadmin clear - Очистить все генераторы # /dbdsetup set hook - Установить крюк # /dbdsetup set chest - Установить сундук -# /dbdsetup set totem - Установить тотем # /dbdsetup set gate - Установить ворота # /dbdsetup set lobby - Установить лобби # ============================================ diff --git a/src/main/resources/messages.yml b/src/main/resources/messages.yml index c2eb1c7..d518b13 100644 --- a/src/main/resources/messages.yml +++ b/src/main/resources/messages.yml @@ -166,9 +166,10 @@ generator: cannot: "&cГенератор не может быть починен!" busy: "&cКто-то уже чинит этот генератор!" actionbar: "&7Починка: %s" - skillcheck-title: "&eSkill Check! %s" - skillcheck-success: "&a✓ Отлично! +%d%%" - skillcheck-fail: "&c✗ Ошибка! -%d%%. Продолжай чинить." + skillcheck-title: "&eПроверка реакции! %s" + skillcheck-success: "&a✓ Успех! +%d%%" + skillcheck-fail: "&c✗ Промах! -%d%%. Чини дальше." + all-done: "%prefix%&aВсе генераторы починены! Открывайте ворота!" kicked: begin: "%prefix%&cДержите Shift для разрушения генератора..." cooldown: "%prefix%&cКулдаун удара: %d сек" @@ -176,7 +177,6 @@ generator: actionbar: "&c⚡ Разрушение: %d%%" success: "%prefix%&cГенератор поврежден! -%d%% прогресса" broadcast: "%prefix%&4⚡ Маньяк разрушил генератор! -%d%%" - all-done: "%prefix%&aВсе генераторы починены! Открывайте ворота!" notification: "&4[DBD] ⚡ Выживший допустил ошибку! Компас укажет на генератор!" # ── Маньяк ───────────────────────────────────── @@ -266,7 +266,7 @@ quit: minigames: generator-roulette: - title: "⚡ Skill Check!" + title: "⚡ Проверка реакции!" success-label: "✓ Нажми!" text-skillcheck: subtitle: "&eНажми ПКМ или отпусти шифт в зоне!"