feat: killer/survivor win conditions with title announcements, fix forcestart reset bug

This commit is contained in:
loki5512344 2026-06-03 21:15:31 +02:00
parent 78fc8bfb55
commit d59b965407
4 changed files with 95 additions and 24 deletions

View file

@ -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);
}
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}