332 lines
16 KiB
Markdown
332 lines
16 KiB
Markdown
# DBD Plugin — План разработки
|
||
|
||
## Архитектурные принципы
|
||
|
||
- **KISS**: Нет абстракций ради абстракций. Простые классы с одной ответственностью.
|
||
- **DRY**: Общая логика в утилитах, не копипаста.
|
||
- **SOLID**:
|
||
- SRP: Один класс = одна задача (StateManager только состояния, DamageHandler только урон)
|
||
- OCP: Перки через интерфейс, не if-else везде
|
||
- LSP: KillerController и SurvivorController наследуются от PlayerController корректно
|
||
- ISP: Маленькие интерфейсы (IRepairable, IHealable, IHookable)
|
||
- DIP: Высокоуровневые модули зависят от абстракций, не от имплементаций
|
||
- max 150 строк/файл
|
||
## Структура папок (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: Скелет проекта
|
||
- [ ] Создать `build.gradle` (Paper 1.20.6, Java 17, ProtocolLib)
|
||
- [ ] `plugin.yml` с пермишнами
|
||
- [ ] `DBDPlugin.java` — onEnable/onDisable
|
||
- [ ] `ConfigManager` — загрузка yaml без перезаписи
|
||
- [ ] `LocationsConfig` — структура для всех координат
|
||
- [ ] `GameConfig` — балансные цифры
|
||
|
||
### Этап 1: Core Game Loop
|
||
- [ ] `GameState` enum
|
||
- [ ] `GameSession` — хранит игроков, фазу, таймер
|
||
- [ ] `Phase` interface
|
||
- [ ] `LobbyPhase` — ожидание, минимум 5 игроков, таймер 60сек
|
||
- [ ] `RoleAssigner` + `RoleQueue` — кто киллер
|
||
- [ ] `RoleAssignPhase` — телепорты, задержка 5сек
|
||
- [ ] `GameplayPhase` — заглушка
|
||
- [ ] `/dbd join`, `/dbd leave`, `/dbd forcestart`
|
||
|
||
### Этап 2: Игроки и состояния
|
||
- [ ] `BasePlayer`, `Survivor`, `Killer`
|
||
- [ ] `PlayerState` enum: HEALTHY, INJURED, DOWNED, HOOKED, DEAD, ESCAPED
|
||
- [ ] `StateMachine` — переходы с валидацией
|
||
- [ ] `PlayerManager` — map UUID -> BasePlayer
|
||
- [ ] `SpawnPosition` — телепорт из конфига
|
||
|
||
### Этап 3: Киллер (базовый)
|
||
- [ ] `KillerController` — основной класс
|
||
- [ ] `KillerAttack` — ЛКМ, кулдаун 2.5с, hitbox
|
||
- [ ] `HitValidator` — line of sight, расстояние
|
||
- [ ] `DamageHandler` — Healthy->Injured, Injured->Downed
|
||
- [ ] `KillerCarry` — подъём, перенос, wiggle progress
|
||
- [ ] `KillerMovement` — скорость 115%, замедление после удара
|
||
|
||
### Этап 4: Выживший (базовый)
|
||
- [ ] `SurvivorController`
|
||
- [ ] `SurvivorMovement` — sprint, exhaustion 3сек/5сек кулдаун
|
||
- [ ] `SurvivorHealing` — аптечка 10сек, другие 16сек, прогресс-бар
|
||
- [ ] Downed movement — crawl 30% скорости
|
||
|
||
### Этап 5: Генераторы
|
||
- [ ] `Generator` класс — прогресс, статус
|
||
- [ ] `GeneratorManager` — 7 штук, 5 нужно
|
||
- [ ] `GeneratorRepair` — зажать ПКМ, прогресс в ActionBar
|
||
- [ ] `SkillCheck` — логика QTE, успех/провал
|
||
- [ ] `SkillCheckUI` — Title-анимация, круг (текстом или картинкой)
|
||
- [ ] `GeneratorRegression` — откат 0.25%/сек, Kick 2.5%/сек 30сек
|
||
|
||
### Этап 6: Крюки
|
||
- [ ] `Hook` класс
|
||
- [ ] `HookManager` — все крюки, подвал
|
||
- [ ] `HookStage` — Stage 1 (60с), Stage 2 (60с)
|
||
- [ ] `HookStruggle` — skill check каждые 2с
|
||
- [ ] `HookRescue` — снятие 2сек
|
||
- [ ] `HookSacrifice` — смерть
|
||
- [ ] Счётчик крюков — 3 раза макс
|
||
|
||
### Этап 7: UI
|
||
- [ ] `ActionBar` — статус, прогресс-бары
|
||
- [ ] `ProgressBar` — [████░░░░░░] формат
|
||
- [ ] `Sidebar` — генераторы, живые
|
||
- [ ] `TitleUI` — начало игры, skill check, победа/поражение
|
||
- [ ] `StatusIcons` — ❤ 🩸 💀
|
||
|
||
### Этап 8: Паллеты и окна
|
||
- [ ] `Pallet` — standing/dropped/broken
|
||
- [ ] `PalletDrop` — ПКМ выжившим
|
||
- [ ] `PalletStun` — 2.5сек стан киллеру
|
||
- [ ] `PalletBreak` — зажать ПКМ 3сек
|
||
- [ ] `Window` — класс
|
||
- [ ] `WindowVault` — 1сек выживший, 1.5сек киллер
|
||
|
||
### Этап 9: Эндгейм
|
||
- [ ] `ExitGate` — открытие 20сек
|
||
- [ ] `GateManager` — 2 ворота
|
||
- [ ] `Hatch` — условие появления
|
||
- [ ] `HatchCondition` — формула: живые < починенные гены
|
||
- [ ] `HatchEscape` — прыжок = побег
|
||
- [ ] `EndgamePhase` — коллапс 120сек
|
||
|
||
### Этап 10: Предметы
|
||
- [ ] `ItemType` enum
|
||
- [ ] `BaseItem` interface
|
||
- [ ] `ItemMedkit`, `ItemFlashlight`, `ItemToolbox`, `ItemKey`, `ItemMap`
|
||
- [ ] `Chest` — 3сек открытие
|
||
- [ ] `ChestLoot` — весовая таблица
|
||
|
||
### Этап 11: Звуки и Terror Radius
|
||
- [ ] `SoundManager`
|
||
- [ ] `TerrorRadiusSound` — 3 уровня по расстоянию
|
||
- [ ] `KillerVision` — red stain частицы
|
||
- [ ] Звуки генераторов, взрывы
|
||
|
||
### Этап 12: Data (YAML -> SQLite)
|
||
- [ ] `DataStorage` interface
|
||
- [ ] `YamlStorage` — реализация
|
||
- [ ] `PlayerStats` — сохранение BP, матчей
|
||
- [ ] Статистика после матча
|
||
|
||
### Этап 13: Перки (отложено до стабильности)
|
||
- [ ] `IPerk` interface: onEquip, onTrigger, onRemove
|
||
- [ ] `PerkRegistry` — фабрика перков
|
||
- [ ] 4 перка выжившего (Self-Care, Dead Hard, Sprint Burst, Borrowed Time)
|
||
- [ ] 3 перка киллера (Hex: Ruin, Corrupt Intervention, BBQ)
|
||
- [ ] `Totem` + `TotemManager` — только с Hex
|
||
|
||
### Этап 14: Полировка
|
||
- [ ] Обработка дисконнекта
|
||
- [ ] Баланс скоростей
|
||
- [ ] Тест-раунды
|
||
- [ ] Оптимизация (проверка не каждый тик)
|
||
|
||
## Правила <150 строк на файл
|
||
|
||
```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);
|
||
}
|
||
}
|
||
|
||
// ❌ Неправильно: всё в одном классе
|
||
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);
|
||
}
|
||
```
|