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

View file

@ -8,19 +8,25 @@ import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
/**
* Лобби-фаза: ждёт минимум 4 игроков.
* При 4 игроках таймер 45 сек, при 5 — сбрасывается до 15 сек.
*/
public class LobbyPhase implements Phase {
private final GameConfig config;
private int countdown;
private int tickSub; // счётчик тиков внутри текущей секунды
private int secondsLeft;
private boolean started;
private boolean forced;
private int lastPlayerCount; // для отслеживания изменения кол-ва игроков
public LobbyPhase(GameConfig config) {
this.config = config;
this.secondsLeft = config.getLobbyCountdown();
this.countdown = 0;
this.secondsLeft = config.getCountdown4Players(); // начальный таймер
this.tickSub = 0;
this.started = false;
this.forced = false;
this.lastPlayerCount = 0;
}
@Override
@ -43,31 +49,62 @@ public class LobbyPhase implements Phase {
}
int playerCount = session.getPlayerCount();
int minPlayers = config.getMinPlayers();
int minPlayers = config.getMinPlayers(); // 4
int maxPlayers = config.getMaxPlayers(); // 5
// Недостаточно игроков — сбрасываем таймер
if (playerCount < minPlayers) {
secondsLeft = config.getLobbyCountdown();
countdown = 0;
secondsLeft = config.getCountdown4Players();
tickSub = 0;
lastPlayerCount = playerCount;
sendActionBarToAll(session,
Component.text("⏳ Ожидание игроков: " + playerCount + "/" + minPlayers, NamedTextColor.YELLOW));
Component.text("⏳ Ожидание игроков: " + playerCount + "/" + minPlayers,
NamedTextColor.YELLOW));
return;
}
countdown++;
if (countdown >= 20) {
countdown = 0;
secondsLeft--;
// Когда количество игроков меняется — обновляем нужный таймер
if (playerCount != lastPlayerCount) {
lastPlayerCount = playerCount;
if (playerCount >= maxPlayers) {
// При максимуме игроков сокращаем таймер до countdown-5-players
int fast = config.getCountdown5Players();
if (secondsLeft > fast) {
secondsLeft = fast;
tickSub = 0;
Bukkit.broadcast(Component.text(
"[DBD] Все игроки вошли! Старт через " + fast + " сек!",
NamedTextColor.GREEN));
}
}
}
String bar = buildCountdownBar(secondsLeft, config.getLobbyCountdown());
// Тикаем таймер
tickSub++;
if (tickSub >= 20) {
tickSub = 0;
secondsLeft--;
// Анонс вслух на ключевых секундах
if (secondsLeft == 30 || secondsLeft == 15 || secondsLeft == 10
|| secondsLeft == 5 || secondsLeft == 3
|| secondsLeft == 2 || secondsLeft == 1) {
Bukkit.broadcast(Component.text(
"[DBD] До начала: " + secondsLeft + " сек.",
NamedTextColor.YELLOW));
}
}
// ActionBar всем
int total = (playerCount >= maxPlayers)
? config.getCountdown5Players()
: config.getCountdown4Players();
String bar = buildCountdownBar(secondsLeft, total);
NamedTextColor barColor = (playerCount >= maxPlayers)
? NamedTextColor.GREEN : NamedTextColor.YELLOW;
sendActionBarToAll(session,
Component.text("▶ Старт через: " + secondsLeft + "с " + bar +
" (" + playerCount + "/" + minPlayers + ")", NamedTextColor.GREEN));
if (countdown == 0 && (secondsLeft <= 5 || secondsLeft == 10 || secondsLeft == 30)) {
Bukkit.broadcast(Component.text(
"[DBD] До начала: " + secondsLeft + " сек.", NamedTextColor.YELLOW));
}
" (" + playerCount + "/" + minPlayers + ")", barColor));
if (secondsLeft <= 0) {
started = true;
@ -85,7 +122,7 @@ public class LobbyPhase implements Phase {
private String buildCountdownBar(int current, int total) {
int filled = total > 0 ? (int) (10.0 * current / total) : 0;
int empty = 10 - filled;
int empty = 10 - filled;
return "§a" + "█".repeat(Math.max(0, filled)) + "§7" + "░".repeat(Math.max(0, empty));
}
@ -102,9 +139,10 @@ public class LobbyPhase implements Phase {
public void onPlayerJoined(GameSession session, Player player) {
session.getPlugin().getLobbyManager().teleportToLobby(player);
int playerCount = session.getPlayerCount();
int minPlayers = config.getMinPlayers();
int minPlayers = config.getMinPlayers();
Bukkit.broadcast(Component.text(
"[DBD] " + player.getName() + " присоединился к очереди! (" + playerCount + "/" + minPlayers + ")",
"[DBD] " + player.getName() + " присоединился к очереди! (" +
playerCount + "/" + minPlayers + ")",
NamedTextColor.YELLOW));
}