All parallel agent tasks finished: - Stage 2-3: Player states, Killer system - Stage 4-5: Survivor, Generators, SkillCheck - Stage 6-7: Hooks, UI (ActionBar, Sidebar, TitleUI) - Stage 8-9: Pallets, Windows, Endgame, Gates, Hatch - Stage 10-11: Custom Items, Chests, Sounds, Terror Radius - Stage 12: Data storage (YAML + SQLite stub) - Stage 13: Perks (Survivor + Killer), Totems - Stage 14: Listeners, Utils, Polish
17 KiB
17 KiB
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 21, ProtocolLib) plugin.ymlс пермишнамиDBDPlugin.java— onEnable/onDisableConfigManager— загрузка yaml без перезаписиLocationsConfig— структура для всех координатGameConfig— балансные цифры
Этап 1: Core Game Loop
GameStateenumGameSession— хранит игроков, фазу, таймерPhaseinterfaceLobbyPhase— ожидание, минимум 5 игроков, таймер 60секRoleAssigner+RoleQueue— кто киллерRoleAssignPhase— телепорты, задержка 5секGameplayPhase— заглушка/dbd join,/dbd leave,/dbd forcestart
Этап 2: Игроки и состояния
BasePlayer,Survivor,KillerPlayerStateenum: HEALTHY, INJURED, DOWNED, HOOKED, DEAD, ESCAPEDStateMachine— переходы с валидациейPlayerManager— map UUID -> BasePlayerSpawnPosition— телепорт из конфига
Этап 3: Киллер (базовый)
KillerController— основной классKillerAttack— ЛКМ, кулдаун 2.5с, hitboxHitValidator— line of sight, расстояниеDamageHandler— Healthy->Injured, Injured->DownedKillerCarry— подъём, перенос, wiggle progressKillerMovement— скорость 115%, замедление после удара
Этап 4: Выживший (базовый)
SurvivorControllerSurvivorMovement— sprint, exhaustion 3сек/5сек кулдаунSurvivorHealing— аптечка 10сек, другие 16сек, прогресс-бар- Downed movement — crawl 30% скорости
Этап 5: Генераторы
Generatorкласс — прогресс, статусGeneratorManager— 7 штук, 5 нужноGeneratorRepair— зажать ПКМ, прогресс в ActionBarSkillCheck— логика 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/brokenPalletDrop— ПКМ выжившимPalletStun— 2.5сек стан киллеруPalletBreak— зажать ПКМ 3секWindow— классWindowVault— 1сек выживший, 1.5сек киллер
Этап 9: Эндгейм
ExitGate— открытие 20секGateManager— 2 воротаHatch— условие появленияHatchCondition— формула: живые < починенные геныHatchEscape— прыжок = побегEndgamePhase— коллапс 120сек
Этап 10: Предметы
ItemTypeenumBaseIteminterfaceItemMedkit,ItemFlashlight,ItemToolbox,ItemKey,ItemMapChest— 3сек открытиеChestLoot— весовая таблица
Этап 11: Звуки и Terror Radius
SoundManagerTerrorRadiusSound— 3 уровня по расстояниюKillerVision— red stain частицы- Звуки генераторов, взрывы
Этап 12: Data (YAML -> SQLite)
DataStorageinterfaceYamlStorage— реализацияSQLiteStorage— заглушкаPlayerStats— сохранение BP, матчейMatchResult— результат матча
Этап 13: Перки (отложено до стабильности)
IPerkinterface: onEquip, onTrigger, onRemovePerkRegistry— фабрика перков- 4 перка выжившего (Self-Care, Dead Hard, Sprint Burst, Borrowed Time)
- 3 перка киллера (Hex: Ruin, Corrupt Intervention, BBQ)
Totem+TotemManager— только с Hex
Этап 14: Полировка
- Обработка дисконнекта
- Баланс скоростей
- Утилиты (LocationUtil, PlayerUtil, TimeUtil, MathUtil, ParticleUtil)
- Оптимизация (проверка не каждый тик)
Правила <150 строк на файл
// ✅ Правильно: 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
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
public interface Phase {
void enter(GameSession session);
void tick(GameSession session); // каждую секунду
void exit(GameSession session);
boolean isComplete();
}
DataStorage
public interface DataStorage {
void saveStats(UUID player, PlayerStats stats);
PlayerStats loadStats(UUID player);
void saveMatch(MatchResult result);
}