diff --git a/src/main/java/dev/loki/core/GameSession.java b/src/main/java/dev/loki/core/GameSession.java index 85ed62e..53ce2e4 100644 --- a/src/main/java/dev/loki/core/GameSession.java +++ b/src/main/java/dev/loki/core/GameSession.java @@ -30,7 +30,8 @@ public class GameSession { private RoleAssigner roleAssigner; private GeneratorManager generatorManager; - // Optional managers set by GameplayPhase + private boolean forceStarted = false; + private PalletManager palletManager; private HookManager hookManager; private GateManager gateManager; @@ -85,7 +86,9 @@ public class GameSession { private Phase determineNextPhase() { if (currentPhase instanceof LobbyPhase) { - return new RoleAssignPhase(roleAssigner); + boolean skip = forceStarted; + forceStarted = false; + return new RoleAssignPhase(roleAssigner, skip); } if (currentPhase instanceof RoleAssignPhase) { return new GameplayPhase(generatorManager); @@ -109,7 +112,6 @@ public class GameSession { eventBus.emit(new PlayerJoinEvent(player)); } - /** Replaces an existing player entry with a new role-assigned instance (Killer/Survivor). */ public void replacePlayer(UUID uuid, BasePlayer newPlayer) { players.put(uuid, newPlayer); } @@ -154,10 +156,6 @@ public class GameSession { return generatorManager; } - // ------------------------------------------------------------------------- - // Gameplay-phase managers (set by GameplayPhase.enter()) - // ------------------------------------------------------------------------- - public PalletManager getPalletManager() { return palletManager; } public void setPalletManager(PalletManager pm) { this.palletManager = pm; } @@ -169,6 +167,7 @@ public class GameSession { public void forceStart() { if (currentPhase instanceof LobbyPhase lobby) { + forceStarted = true; lobby.forceStart(); } } @@ -182,7 +181,6 @@ public class GameSession { } public void notifyPlayerJoined(org.bukkit.entity.Player player) { - // Called when player joins from command, updates UI if (currentPhase != null) { currentPhase.onPlayerJoined(this, player); } @@ -190,4 +188,4 @@ public class GameSession { public record PlayerJoinEvent(BasePlayer player) {} public record PlayerLeaveEvent(BasePlayer player) {} -} +} \ No newline at end of file diff --git a/src/main/java/dev/loki/phase/GameplayPhase.java b/src/main/java/dev/loki/phase/GameplayPhase.java index 1c3f5be..6bee55f 100644 --- a/src/main/java/dev/loki/phase/GameplayPhase.java +++ b/src/main/java/dev/loki/phase/GameplayPhase.java @@ -209,16 +209,75 @@ public class GameplayPhase implements Phase { PlayerState st = bp.getState(); if (st == PlayerState.ESCAPED) { escapedSurvivors++; - } else if (st != PlayerState.DEAD && st != PlayerState.HOOKED) { + } else if (st == PlayerState.DEAD) { + // dead, counted below + } else { + // HEALTHY, INJURED, DOWNED, CARRIED, HOOKED — still in game aliveSurvivors++; } } - boolean allDead = (aliveSurvivors == 0 && escapedSurvivors == 0 && totalSurvivors > 0); - boolean allEscaped = (escapedSurvivors == totalSurvivors && totalSurvivors > 0); + if (totalSurvivors == 0) return; // no survivors registered yet - if (allDead || allEscaped) { + boolean killerWin = (aliveSurvivors == 0 && escapedSurvivors == 0); + boolean survivorsWin = (escapedSurvivors == totalSurvivors); + + if (killerWin) { + announceKillerWin(session); shouldEnd = true; + } else if (survivorsWin) { + announceSurvivorWin(session); + shouldEnd = true; + } + } + + private void announceKillerWin(GameSession session) { + Bukkit.broadcast(Component.text("", NamedTextColor.RED)); + Bukkit.broadcast(Component.text("☠ КИЛЛЕР ПОБЕДИЛ! ☠", NamedTextColor.DARK_RED)); + Bukkit.broadcast(Component.text("Все выжившие уничтожены.", NamedTextColor.RED)); + Bukkit.broadcast(Component.text("", NamedTextColor.RED)); + + for (BasePlayer bp : session.getPlayers().values()) { + Player p = bp.getPlayer(); + if (p == null || !p.isOnline()) continue; + if (bp.isKiller()) { + p.showTitle(net.kyori.adventure.title.Title.title( + Component.text("ПОБЕДА!", net.kyori.adventure.text.format.TextColor.color(0xFF2222)), + Component.text("Ты уничтожил всех выживших", NamedTextColor.RED) + )); + p.playSound(p.getLocation(), Sound.UI_TOAST_CHALLENGE_COMPLETE, 1.0f, 1.0f); + } else { + p.showTitle(net.kyori.adventure.title.Title.title( + Component.text("ПОРАЖЕНИЕ", net.kyori.adventure.text.format.TextColor.color(0x888888)), + Component.text("Киллер победил", NamedTextColor.GRAY) + )); + p.playSound(p.getLocation(), Sound.ENTITY_WITHER_DEATH, 0.5f, 0.8f); + } + } + } + + private void announceSurvivorWin(GameSession session) { + Bukkit.broadcast(Component.text("", NamedTextColor.GREEN)); + Bukkit.broadcast(Component.text("✔ ВЫЖИВШИЕ ПОБЕДИЛИ! ✔", NamedTextColor.GREEN)); + Bukkit.broadcast(Component.text("Все выжившие сбежали!", NamedTextColor.YELLOW)); + Bukkit.broadcast(Component.text("", NamedTextColor.GREEN)); + + for (BasePlayer bp : session.getPlayers().values()) { + Player p = bp.getPlayer(); + if (p == null || !p.isOnline()) continue; + if (!bp.isKiller()) { + p.showTitle(net.kyori.adventure.title.Title.title( + Component.text("ПОБЕГ!", net.kyori.adventure.text.format.TextColor.color(0x00FF00)), + Component.text("Вы выбрались живыми!", NamedTextColor.YELLOW) + )); + p.playSound(p.getLocation(), Sound.UI_TOAST_CHALLENGE_COMPLETE, 1.0f, 1.2f); + } else { + p.showTitle(net.kyori.adventure.title.Title.title( + Component.text("ПОРАЖЕНИЕ", net.kyori.adventure.text.format.TextColor.color(0x888888)), + Component.text("Все выжившие сбежали", NamedTextColor.GRAY) + )); + p.playSound(p.getLocation(), Sound.ENTITY_WITHER_DEATH, 0.5f, 0.8f); + } } } diff --git a/src/main/java/dev/loki/phase/LobbyPhase.java b/src/main/java/dev/loki/phase/LobbyPhase.java index 76a56e4..e3cfe68 100644 --- a/src/main/java/dev/loki/phase/LobbyPhase.java +++ b/src/main/java/dev/loki/phase/LobbyPhase.java @@ -13,18 +13,19 @@ public class LobbyPhase implements Phase { private int countdown; private int secondsLeft; private boolean started; + private boolean forced; public LobbyPhase(GameConfig config) { this.config = config; this.secondsLeft = config.getLobbyCountdown(); this.countdown = 0; this.started = false; + this.forced = false; } @Override public void enter(GameSession session) { Bukkit.broadcast(Component.text("[DBD] Лобби открыто! Используйте /dbd join", NamedTextColor.GREEN)); - // Teleport already-in-session players to lobby (e.g. on restart) var lobbyManager = session.getPlugin().getLobbyManager(); for (var player : session.getPlayers().values()) { var bukkitPlayer = player.getPlayer(); @@ -36,14 +37,17 @@ public class LobbyPhase implements Phase { @Override public void tick(GameSession session) { + if (forced) { + started = true; + return; + } + int playerCount = session.getPlayerCount(); int minPlayers = config.getMinPlayers(); if (playerCount < minPlayers) { secondsLeft = config.getLobbyCountdown(); countdown = 0; - started = false; - // Show waiting message in action bar to all players in lobby sendActionBarToAll(session, Component.text("⏳ Ожидание игроков: " + playerCount + "/" + minPlayers, NamedTextColor.YELLOW)); return; @@ -55,13 +59,11 @@ public class LobbyPhase implements Phase { secondsLeft--; } - // Action bar: показываем каждый тик (обновляется плавно) String bar = buildCountdownBar(secondsLeft, config.getLobbyCountdown()); sendActionBarToAll(session, Component.text("▶ Старт через: " + secondsLeft + "с " + bar + " (" + playerCount + "/" + minPlayers + ")", NamedTextColor.GREEN)); - // Chat-announce на ключевых отметках if (countdown == 0 && (secondsLeft <= 5 || secondsLeft == 10 || secondsLeft == 30)) { Bukkit.broadcast(Component.text( "[DBD] До начала: " + secondsLeft + " сек.", NamedTextColor.YELLOW)); @@ -89,7 +91,6 @@ public class LobbyPhase implements Phase { @Override public void exit(GameSession session) { - // Clear action bar for all for (BasePlayer bp : session.getPlayers().values()) { Player p = bp.getPlayer(); if (p != null && p.isOnline()) p.sendActionBar(Component.empty()); @@ -113,11 +114,10 @@ public class LobbyPhase implements Phase { } public void forceStart() { - secondsLeft = 0; - started = true; + this.forced = true; } public int getSecondsLeft() { return secondsLeft; } -} +} \ No newline at end of file diff --git a/src/main/java/dev/loki/phase/RoleAssignPhase.java b/src/main/java/dev/loki/phase/RoleAssignPhase.java index 101ca2d..1f3249a 100644 --- a/src/main/java/dev/loki/phase/RoleAssignPhase.java +++ b/src/main/java/dev/loki/phase/RoleAssignPhase.java @@ -10,20 +10,34 @@ public class RoleAssignPhase implements Phase { private final RoleAssigner assigner; private int ticksLeft; private boolean complete; + private final boolean skipDelay; public RoleAssignPhase(RoleAssigner assigner) { + this(assigner, false); + } + + public RoleAssignPhase(RoleAssigner assigner, boolean skipDelay) { this.assigner = assigner; this.complete = false; + this.skipDelay = skipDelay; } @Override public void enter(GameSession session) { - ticksLeft = session.getConfig().getRoleAssignDelay() * 20; + if (skipDelay) { + ticksLeft = 0; + } else { + ticksLeft = session.getConfig().getRoleAssignDelay() * 20; + } assigner.assignRoles(session); } @Override public void tick(GameSession session) { + if (skipDelay) { + complete = true; + return; + } ticksLeft--; if (ticksLeft <= 0) { complete = true; @@ -39,4 +53,4 @@ public class RoleAssignPhase implements Phase { public boolean isComplete() { return complete; } -} +} \ No newline at end of file