feat(gameplay): implement hook sabotage, generator kick, jump listener, and boss bar UI

- Add HookSabotage system for survivors to disable hooks temporarily
- Add GeneratorKick killer ability to regress generator progress
- Add PlayerJumpListener to track survivor jump actions
- Add BossBarManager for displaying game state and objectives
- Update generator regression logic to support killer kick mechanic
- Update KillerController to integrate generator kick ability
- Update PlayerInteractListener to handle new interaction types
- Refactor game phases to use BossBarManager for UI updates
- Update .gitignore to exclude compiled bin/ directory
- Simplify PLAN.md documentation for easier maintenance
This commit is contained in:
loki5512344 2026-06-04 12:11:04 +02:00
parent 3d6204cc83
commit 89dc9eb00d
78 changed files with 2293 additions and 1210 deletions

463
PLAN.md
View file

@ -4,330 +4,203 @@
- **KISS**: Нет абстракций ради абстракций. Простые классы с одной ответственностью.
- **DRY**: Общая логика в утилитах, не копипаста.
- **SOLID**:
- SRP: Один класс = одна задача (StateManager только состояния, DamageHandler только урон)
- OCP: Перки через интерфейс, не if-else везде
- LSP: KillerController и SurvivorController наследуются от PlayerController корректно
- ISP: Маленькие интерфейсы (IRepairable, IHealable, IHookable)
- DIP: Высокоуровневые модули зависят от абстракций, не от имплементаций
- max 150 строк/файл
## Структура папок (max 150 строк/файл)
- **SOLID**: SRP, OCP, LSP, ISP, DIP. Max 150 строк/файл.
```
src/main/java/com/dbd/plugin/
├── DBDPlugin.java # Точка входа (max 50 строк)
├── config/
│ ├── ConfigManager.java # Загрузка всех yaml
│ ├── LocationsConfig.java # Координаты объектов
│ └── GameConfig.java # Балансные цифры
├── core/
│ ├── GameManager.java # Главный контроллер игр
│ ├── GameSession.java # Одна игра (lobby -> end)
│ ├── GameState.java # Enum: LOBBY, IN_GAME, ENDING
│ └── GameEventBus.java # Внутренние события игры
├── phase/
│ ├── Phase.java # Interface: enter(), tick(), exit()
│ ├── LobbyPhase.java # Ожидание игроков
│ ├── RoleAssignPhase.java # Назначение ролей, таймер 5сек
│ ├── GameplayPhase.java # Основная игра
│ └── EndgamePhase.java # Эндгейм коллапс + результаты
├── player/
│ ├── BasePlayer.java # UUID, Player объект
│ ├── Survivor.java # Данные выжившего
│ ├── Killer.java # Данные киллера
│ ├── PlayerState.java # Enum: HEALTHY, INJURED, DOWNED...
│ └── PlayerManager.java # Map<UUID, BasePlayer>
├── state/
│ ├── StateMachine.java # Машина состояний игрока
│ ├── StateTransition.java # Проверка можно ли перейти
│ └── StateHandler.java # Обработчик изменений
├── role/
│ ├── RoleAssigner.java # Логика выбора кто киллер
│ ├── RoleQueue.java # Очередь на киллера
│ └── SpawnPosition.java # Спавн-точки из конфига
├── survivor/
│ ├── SurvivorController.java # Действия выжившего
│ ├── SurvivorMovement.java # Sprint, Exhaustion
│ ├── SurvivorHealing.java # Лечение себя/других
│ └── SurvivorInventory.java # Предметы в руках
├── killer/
│ ├── KillerController.java # Действия киллера
│ ├── KillerAttack.java # ЛКМ, кулдаун, hit detection
│ ├── KillerCarry.java # Подъём, перенос, wiggle
│ └── KillerVision.java # Terror radius, red stain
├── damage/
│ ├── DamageHandler.java # Нанесение урона (Healthy->Injured...)
│ ├── AttackListener.java # Слушатель ЛКМ киллера
│ └── HitValidator.java # Line of sight, range check
├── objective/
│ ├── Generator.java # Один генератор
│ ├── GeneratorManager.java # Все 7 генераторов
│ ├── GeneratorRepair.java # Логика починки
│ ├── GeneratorRegression.java # Откат прогресса
│ ├── SkillCheck.java # QTE система
│ └── SkillCheckUI.java # Title-анимация
├── hook/
│ ├── Hook.java # Один крюк
│ ├── HookManager.java # Все крюки
│ ├── HookStage.java # Stage 1, Stage 2
│ ├── HookStruggle.java # Skill checks на Stage 2
│ ├── HookRescue.java # Спасание с крюка
│ └── HookSacrifice.java # Смерть после Stage 2
├── pallet/
│ ├── Pallet.java # Один паллет
│ ├── PalletManager.java # Все паллеты
│ ├── PalletDrop.java # Выживший бросает
│ ├── PalletStun.java # Стан киллера
│ └── PalletBreak.java # Киллер ломает
├── window/
│ ├── Window.java # Одно окно
│ ├── WindowManager.java # Все окна
│ ├── WindowVault.java # Прыжок через окно
│ └── WindowFastVault.java # Быстрый/медленный волт
├── gate/
│ ├── ExitGate.java # Одни ворота
│ ├── GateManager.java # 2 ворота
│ └── GateOpening.java # 20 сек открытие
├── hatch/
│ ├── Hatch.java # Люк
│ ├── HatchCondition.java # Условие появления
│ └── HatchEscape.java # Прыжок в люк
├── item/
│ ├── ItemType.java # Enum: MEDKIT, FLASHLIGHT...
│ ├── BaseItem.java # Interface предмета
│ ├── ItemMedkit.java # Аптечка
│ ├── ItemFlashlight.java # Фонарик
│ ├── ItemToolbox.java # Ящик инструментов
│ ├── ItemKey.java # Ключ от люка
│ ├── ItemMap.java # Карта
│ └── ChestLoot.java # Лут из сундуков
├── chest/
│ ├── Chest.java # Сундук
│ ├── ChestManager.java # Все сундуки
│ └── ChestOpening.java # 3 сек открытие
├── totem/
│ ├── Totem.java # Тотем
│ ├── TotemManager.java # 5 тотемов
│ ├── DullTotem.java # Обычный тотем
│ └── HexTotem.java # Проклятый (с перком)
├── perk/
│ ├── IPerk.java # Интерфейс перка
│ ├── PerkType.java # Enum: SURVIVOR, KILLER
│ ├── PerkRegistry.java # Map всех перков
│ ├── survivor/
│ │ ├── SelfCarePerk.java
│ │ ├── DeadHardPerk.java
│ │ ├── SprintBurstPerk.java
│ │ └── BorrowedTimePerk.java
│ └── killer/
│ ├── HexRuinPerk.java
│ ├── CorruptInterventionPerk.java
│ └── BBQChiliPerk.java
├── ui/
│ ├── ActionBar.java # Нижняя полоска
│ ├── Sidebar.java # Scoreboard
│ ├── TitleUI.java # Title/Subtitle
│ ├── ProgressBar.java # [████░░░░░░] 40%
│ └── StatusIcons.java # ❤ 🩸 💀
├── sound/
│ ├── SoundManager.java # Воспроизведение звуков
│ ├── TerrorRadiusSound.java # Сердцебиение
│ ├── GeneratorSound.java # Звуки генератора
│ └── SoundLevels.java # 3 уровня громкости
├── util/
│ ├── LocationUtil.java # Dist, line of sight
│ ├── PlayerUtil.java # Is sneaking, sprinting
│ ├── TimeUtil.java # Форматирование секунд
│ ├── MathUtil.java # Clamp, lerp
│ └── ParticleUtil.java # Спавн частиц
├── command/
│ ├── DBDCommand.java # /dbd главная
│ ├── JoinSubcommand.java # /dbd join
│ ├── LeaveSubcommand.java # /dbd leave
│ ├── SetupSubcommand.java # /dbd setgen, sethook...
│ ├── AdminSubcommand.java # /dbd forcestart, stop
│ └── StatsSubcommand.java # /dbd stats
├── listener/
│ ├── PlayerQuitListener.java # Дисконнект во время игры
│ ├── PlayerInteractListener.java # ПКМ, ЛКМ
│ ├── PlayerMoveListener.java # Движение, ползание
│ └── DamageListener.java # Отмена ванильного урона
├── data/
│ ├── DataStorage.java # Interface: save(), load()
│ ├── YamlStorage.java # YAML реализация
│ ├── SQLiteStorage.java # SQLite реализация (позже)
│ ├── PlayerStats.java # Статистика игрока
│ └── MatchResult.java # Результат матча
└── exception/
├── DBDException.java # Базовое исключение
├── InvalidLocationException.java
└── InvalidStateException.java
```
---
## TODO (по порядку)
## Статус реализации
### Этап 0: Скелет проекта
- [x] Создать `build.gradle` (Paper 1.20.6, Java 21, ProtocolLib)
### ✅ Этап 0: Скелет проекта
- [x] `build.gradle` (Paper 1.21.1, Java 21)
- [x] `plugin.yml` с пермишнами
- [x] `DBDPlugin.java` — onEnable/onDisable
- [x] `ConfigManager` — загрузка yaml без перезаписи
- [x] `LocationsConfig` — структура для всех координат
- [x] `GameConfig` — балансные цифры
- [x] `LocationsConfig` — координаты объектов
- [x] `GameConfig` — все балансные цифры (BossBar, carry, heal limits, lobby timers, sizes, kick cooldown, sabotage)
### Этап 1: Core Game Loop
### ✅ Этап 1: Core Game Loop
- [x] `GameState` enum
- [x] `GameSession` — хранит игроков, фазу, таймер
- [x] `GameSession` — хранит игроков, фазу, таймер; передаёт `GateManager` и `collapseSeconds` в `EndgamePhase`
- [x] `Phase` interface
- [x] `LobbyPhase` — ожидание, минимум 5 игроков, таймер 60сек
- [x] `RoleAssigner` + `RoleQueue` — кто киллер
- [x] `RoleAssignPhase` — телепорты, задержка 5сек
- [x] `GameplayPhase` — заглушка
- [x] `/dbd join`, `/dbd leave`, `/dbd forcestart`
- [x] `LobbyPhase` — таймер **45 сек при 4 игроках**, **15 сек при 5**, min-players=4, анонс на ключевых секундах
- [x] `RoleAssigner` + `RoleQueue` — выбор киллера; применяет `GENERIC_SCALE` через Attribute API
- [x] `RoleAssignPhase` — телепорты, задержка 5 сек
- [x] `GameplayPhase` — полная логика (см. ниже)
- [x] `EndgamePhase` — `collapseSeconds` из конфига, таймер в `Sidebar`, `GateManager` из сессии, Adventure API title
- [x] `/dbd join`, `/dbd leave`, `/dbd forcestart`, `/dbd stop`, `/dbd reload`, `/dbd lobby`, `/dbd setlobby`
### Этап 2: Игроки и состояния
### ✅ Этап 2: Игроки и состояния
- [x] `BasePlayer`, `Survivor`, `Killer`
- [x] `PlayerState` enum: HEALTHY, INJURED, DOWNED, HOOKED, DEAD, ESCAPED
- [x] `PlayerState`: HEALTHY, INJURED, DOWNED, HOOKED, CARRIED, DEAD, ESCAPED
- [x] `StateMachine` — переходы с валидацией
- [x] `PlayerManager` — map UUID -> BasePlayer
- [x] `PlayerManager` — map UUID → BasePlayer
- [x] `SpawnPosition` — телепорт из конфига
- [x] Перевод в `GameMode.SPECTATOR` при `PlayerState.DEAD` (в `GameplayPhase.checkWinConditions` и `HookSacrifice`)
- [x] Размеры через `Attribute.GENERIC_SCALE`: Survivor=1.0, Killer=1.5 (в `RoleAssigner` и `GameplayPhase`)
### Этап 3: Киллер (базовый)
- [x] `KillerController` — основной класс
- [x] `KillerAttack` — ЛКМ, кулдаун 2.5с, hitbox
### ✅ Этап 3: Киллер
- [x] `KillerController` — без дублирующей регистрации `AttackListener`
- [x] `KillerAttack` — ЛКМ, кулдаун 2.5 с, hitbox
- [x] `HitValidator` — line of sight, расстояние
- [x] `DamageHandler` — Healthy->Injured, Injured->Downed
- [x] `KillerCarry` — подъём, перенос, wiggle progress
- [x] `KillerMovement` — скорость 115%, замедление после удара
- [x] `DamageHandler` — Healthy→Injured, Injured→Downed
- [x] `KillerCarry` — побег через **спам пробела** (PlayerJumpListener), шкала в ActionBar, Speed I при побеге
- [x] `KillerMovement` — скорость из конфига, замедление после удара
- [x] `GeneratorKick` — Shift 3 сек → −20% прогресса, кулдаун 30 сек, progress bar
- [x] `BossBarManager` — BossBar с градиентом по дистанции до маньяка, читает все параметры из конфига
### Этап 4: Выживший (базовый)
- [x] `SurvivorController`
- [x] `SurvivorMovement` — sprint, exhaustion 3сек/5сек кулдаун
- [x] `SurvivorHealing` — аптечка 10сек, другие 16сек, прогресс-бар
### ✅ Этап 4: Выживший
- [x] `SurvivorController` — передаёт `GameConfig` в `SurvivorHealing`
- [x] `SurvivorMovement` — sprint, exhaustion
- [x] `SurvivorHealing` — таймеры из конфига (other=15 сек, self=30 сек), лимит **4 лечений** из конфига, sneak-check, range-check
- [x] Downed movement — crawl 30% скорости
### Этап 5: Генераторы
- [x] `Generator` класс — прогресс, статус
- [x] `GeneratorManager` — 7 штук, 5 нужно
- [x] `GeneratorRepair` — зажать ПКМ, прогресс в ActionBar
- [x] `SkillCheck` — логика QTE, успех/провал
- [x] `SkillCheckUI` — Title-анимация, круг (текстом или картинкой)
- [x] `GeneratorRegression` — откат 0.25%/сек, Kick 2.5%/сек 30сек
### ✅ Этап 5: Генераторы
- [x] `Generator` — `setUnderRegression(bool, ticks)`, `wasKicked()`
- [x] `GeneratorManager` — `getCompletedCount()` считает живое состояние
- [x] `GeneratorRepair` — ПКМ, прогресс в ActionBar; при фейле skillcheck: `setUnderRegression` + **компас маньяку** на `compass-duration-seconds`
- [x] `GeneratorRegression` — скорость полностью из конфига (`regression-rate`, `kick-regression-rate`), без хардкодированных констант
- [x] `SkillCheck` + `SkillCheckUI` — QTE система
### Этап 6: Крюки
- [x] `Hook` класс
- [x] `HookManager` — все крюки, подвал
- [x] `HookStage` — Stage 1 (60с), Stage 2 (60с)
- [x] `HookStruggle` — skill check каждые 2с
- [x] `HookRescue` — снятие 2сек
- [x] `HookSacrifice` — смерть
- [x] Счётчик крюков — 3 раза макс
### ✅ Этап 6: Крюки
- [x] `Hook` — поле `disabled` + `isDisabled()`/`setDisabled()`
- [x] `HookManager` — `maxHooks` из конфига, `tryHookSurvivor` проверяет лимит → при превышении сразу `DEAD`; `findNearestUnoccupiedHook` и `findAvailableHook` учитывают `isDisabled()`
- [x] `HookStage` — Stage 1 (60 с), Stage 2 (60 с)
- [x] `HookStruggle` — `struggleIntervalTicks` из конфига (`hooks.struggle-interval`)
- [x] `HookRescue` — 60 тиков (3 сек из конфига), sneak-check, range-check, Speed I после спасения
- [x] `HookSacrifice` — смерть + **SPECTATOR mode**
- [x] `HookSabotage` — выживший держит Shift **10 сек** → крюк отключён, восстановление **60 сек** (всё из конфига), progress bar, broadcast
### Этап 7: UI
### ✅ Этап 7: UI
- [x] `ActionBar` — статус, прогресс-бары
- [x] `ProgressBar` — [████░░░░░░] формат
- [x] `Sidebar` — генераторы, живые
- [x] `TitleUI` — начало игры, skill check, победа/поражение
- [x] `StatusIcons` — ❤ 🩸 💀
- [x] `Sidebar` — сердца ♥ (по `hooksTaken`), цвета ников по состоянию, `GeneratorManager` (живой счёт), таймер коллапса
- [x] `TitleUI`, `ProgressBar`, `StatusIcons`
- [x] `BossBarManager` — BossBar для каждого выжившего, градиент по дистанции
- [x] Шкала wiggle в `KillerCarry.showWiggleProgress()` (ActionBar)
### Этап 8: Паллеты и окна
- [x] `Pallet` — standing/dropped/broken
- [x] `PalletDrop` — ПКМ выжившим
- [x] `PalletStun` — 2.5сек стан киллеру
- [x] `PalletBreak` — зажать ПКМ 3сек
- [x] `Window` — класс
- [x] `WindowVault` — 1сек выживший, 1.5сек киллер
### ✅ Этап 8: Паллеты и окна
- [x] `Pallet`, `PalletManager`, `PalletDrop`, `PalletBreak`, `PalletStun`
- [x] `Window`, `WindowManager`, `WindowVault`, `WindowFastVault`
### Этап 9: Эндгейм
- [x] `ExitGate` — открытие 20сек
- [x] `GateManager` — 2 ворота
- [x] `Hatch` — условие появления
- [x] `HatchCondition` — формула: живые < починенные гены
- [x] `HatchEscape` — прыжок = побег
- [x] `EndgamePhase` — коллапс 120сек
### ✅ Этап 9: Ворота и люк
- [x] `ExitGate` — `savedProgress` + `getSavedProgress()`/`saveProgress()`/`restoreProgress()`/`setProgressDirect()`; `reset()` чистит `savedProgress`
- [x] `GateManager` — `activateEndgame()` только включает питание, **без авто-открытия**
- [x] `GateOpening` — **линейный прогресс** (`setProgressDirect`), сохраняет/восстанавливает прогресс при уходе/возврате игрока
- [x] `Hatch`, `HatchCondition`, `HatchEscape`
### Этап 10: Предметы
- [x] `ItemType` enum
- [x] `BaseItem` interface
- [x] `ItemMedkit`, `ItemFlashlight`, `ItemToolbox`, `ItemKey`, `ItemMap`
- [x] `Chest` — 3сек открытие
- [x] `ChestLoot` — весовая таблица
### ✅ Этап 10: Звук
- [x] `TerrorRadiusSound` — `terrorRadius` параметр конструктора (из конфига), дефолт 32
- [x] `SoundManager`, `GeneratorSound`, `SoundLevels`
### Этап 11: Звуки и Terror Radius
- [x] `SoundManager`
- [x] `TerrorRadiusSound` — 3 уровня по расстоянию
- [x] `KillerVision` — red stain частицы
- [x] Звуки генераторов, взрывы
### ✅ Этап 11: Слушатели
- [x] `PlayerJumpListener` — прыжки для wiggle при переноске
- [x] `PlayerInteractListener` — `setOnKillerSneak` + `PlayerToggleSneakEvent` (маньяк → GeneratorKick, выживший → HookSabotage)
- [x] `PlayerMoveListener`, `PlayerQuitListener`, `DamageListener`
### Этап 12: Data (YAML -> SQLite)
- [x] `DataStorage` interface
- [x] `YamlStorage` — реализация
- [x] `SQLiteStorage` — заглушка
- [x] `PlayerStats` — сохранение BP, матчей
- [x] `MatchResult` — результат матча
### ✅ Этап 12: Данные и хранение
- [x] `DataStorage`, `YamlStorage`, `SQLiteStorage`, `PlayerStats`, `MatchResult`
### Этап 13: Перки (отложено до стабильности)
- [x] `IPerk` interface: onEquip, onTrigger, onRemove
- [x] `PerkRegistry` — фабрика перков
- [x] 4 перка выжившего (Self-Care, Dead Hard, Sprint Burst, Borrowed Time)
- [x] 3 перка киллера (Hex: Ruin, Corrupt Intervention, BBQ)
- [x] `Totem` + `TotemManager` — только с Hex
---
### Этап 14: Полировка
- [x] Обработка дисконнекта
- [x] Баланс скоростей
- [x] Утилиты (LocationUtil, PlayerUtil, TimeUtil, MathUtil, ParticleUtil)
- [x] Оптимизация (проверка не каждый тик)
## ❌ Не реализовано (следующие этапы)
## Правила <150 строк на файл
### Пользовательские лобби
- [ ] `LobbySession` — пользовательское лобби (пароль, владелец, приватность)
- [ ] `LobbyRegistry` — реестр серверных и пользовательских лобби, YAML persistence
- [ ] `/dbd create <name> [password] [private]` — для обычных игроков (сейчас требует `mdbd.admin`)
- [ ] `/dbd join <name>` — вход в конкретное лобби
```java
// ✅ Правильно: Controller делегирует
public class KillerController {
private final KillerAttack attack;
private final KillerCarry carry;
private final KillerMovement movement;
public void onLeftClick(Player target) {
attack.tryAttack(target);
}
}
### Механики крюков
- [ ] `HookRecovery` — самостоятельное слезание с крюка на Stage 2
// ❌ Неправильно: всё в одном классе
public class KillerController {
// 500 строк логики атаки, переноса, движения...
}
```
## Ключевые интерфейсы
### IPerk
```java
public interface IPerk {
String getId();
String getName();
PerkType getType(); // SURVIVOR or KILLER
void onEquip(BasePlayer player);
void onTrigger(BasePlayer player, PerkTrigger trigger);
void onRemove(BasePlayer player);
}
```
### Phase
```java
public interface Phase {
void enter(GameSession session);
void tick(GameSession session); // каждую секунду
void exit(GameSession session);
boolean isComplete();
}
```
### DataStorage
```java
public interface DataStorage {
void saveStats(UUID player, PlayerStats stats);
PlayerStats loadStats(UUID player);
void saveMatch(MatchResult result);
}
### Перки
- [ ] `IPerk` интерфейс, `PerkRegistry`
- [ ] Survivor: `SelfCarePerk`, `DeadHardPerk`, `SprintBurstPerk`, `BorrowedTimePerk`
- [ ] Killer: `HexRuinPerk`, `CorruptInterventionPerk`, `BBQChiliPerk`
### Предметы из конфига
- [ ] `ChestLoot` — читать веса и типы предметов из `game.yml` (сейчас захардкожено)
---
## Конфиги (game.yml) — актуальные ключи
```yaml
lobby:
min-players: 4
max-players: 5
countdown-4-players: 45
countdown-5-players: 15
role-assign-delay: 5
players:
survivor-scale: 1.0
killer-scale: 1.5
generators:
total-count: 7
required-count: 5
repair-seconds: 90
regression-rate: 0.25
kick-regression-rate: 2.5
kick-regression-percent: 20.0
kick-duration-seconds: 30
kick-cooldown-seconds: 30
kick-charge-seconds: 3
interaction-range: 3.0
hooks:
stage1-seconds: 60
stage2-seconds: 60
struggle-interval: 2
max-hooks: 3
interaction-range: 3.0
rescue-seconds: 3
sabotage-seconds: 10
sabotage-recovery-seconds: 60
healing:
other-heal-seconds: 15
self-heal-seconds: 30
medkit-bonus: 0.5
interrupt-on-move: true
max-heals-per-match: 4
heal-range: 3.0
carry:
escape-jumps: 15
escape-speed-seconds: 3
jump-fill-percent: 6.67
killer:
attack-cooldown: 2.5
attack-range: 3.0
pallet-stun-seconds: 2.0
pallet-break-seconds: 3.0
terror-radius: 32
pallet-interaction-range: 3.0
bossbar:
enabled: true
title: "<red>☠ МАНЬЯК РЯДОМ ☠</red>"
color: RED
style: SOLID
appear-range: 32
full-range: 8
endgame:
gate-open-seconds: 20
collapse-seconds: 120
chest-open-seconds: 3
gate-interaction-range: 3.0
killer-notification:
compass-duration-seconds: 5
scoreboard:
show-hearts: true
heart-symbol: "♥"
color-healthy: "WHITE"
color-injured: "RED"
color-healing: "GREEN"
color-hooked: "DARK_RED"
```