package de.velocityfall.manager; import de.velocityfall.VelocityFall; import de.velocityfall.utils.ScoreboardHelper; import de.velocityfall.utils.StatsHandler; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.Sound; import org.bukkit.entity.Firework; import org.bukkit.entity.Player; import org.bukkit.inventory.meta.FireworkMeta; import org.bukkit.FireworkEffect; import org.bukkit.Color; import org.bukkit.scheduler.BukkitRunnable; import java.util.ArrayList; import java.util.List; import java.util.Map; public class GameManager { /* --------------------------------------------------- * GAME START COUNTDOWN * --------------------------------------------------- */ public static void startArenaCountdown(String arenaName) { final VelocityFall plugin = VelocityFall.getInstance(); if (plugin == null) return; if (plugin.getGameStartedMap().getOrDefault(arenaName, false)) return; new BukkitRunnable() { int timeLeft = plugin.getConfig().getInt("BeforeGameCD", 15); @Override public void run() { int playerCount = (int) plugin.getPlayerArenaMap().values() .stream().filter(arenaName::equals).count(); if (playerCount < 1) { cancel(); return; } if (timeLeft <= 5 && timeLeft > 0) { broadcast(arenaName, "§aSpiel startet in §e" + timeLeft + "..."); } if (timeLeft <= 0) { plugin.getGameStartedMap().put(arenaName, true); plugin.getArenaPlayersCountMap().put(arenaName, playerCount); plugin.getSignManager().updateSign(arenaName); broadcast(arenaName, "§6§lDAS SPIEL BEGINNT!"); Location spawn = plugin.getArenaManager() .getArenaConfig().getLocation("Arenas." + arenaName + ".Spawn"); // PLAYED Stats erhöhen für alle Spieler plugin.getPlayerArenaMap().forEach((p, a) -> { if (arenaName.equals(a)) { // Stats tracken StatsHandler.addPlayed(p.getUniqueId().toString()); // Teleport & Setup if (spawn != null) p.teleport(spawn); p.setGameMode(GameMode.ADVENTURE); p.setAllowFlight(false); p.playSound(p.getLocation(), Sound.ENTITY_GENERIC_EXPLODE, 1f, 1f); // Scoreboard auf InGame umschalten try { ScoreboardHelper.updateBoard(p, arenaName, true); } catch (Exception e) { plugin.getLogger().warning("Scoreboard Update fehlgeschlagen: " + e.getMessage()); } } }); cancel(); } timeLeft--; } }.runTaskTimer(plugin, 0L, 20L); } public static void startCountdown(String arenaName) { startArenaCountdown(arenaName); } /* --------------------------------------------------- * WIN HANDLING - MIT STATS TRACKING * --------------------------------------------------- */ public static void checkForWinner(String arenaName) { final VelocityFall plugin = VelocityFall.getInstance(); if (plugin == null) return; int remaining = plugin.getArenaPlayersCountMap().getOrDefault(arenaName, 0); if (remaining != 1) return; // 1) Gewinner finden (Nicht-Spectator) Player winner = plugin.getPlayerArenaMap().entrySet().stream() .filter(e -> arenaName.equals(e.getValue())) .map(Map.Entry::getKey) .filter(p -> p.getGameMode() != GameMode.SPECTATOR) .findFirst() .orElse(null); // Fallback if (winner == null) { List arenaPlayers = VelocityFall.getArenaPlayers(arenaName); for (Player p : arenaPlayers) { if (p != null && p.getGameMode() != GameMode.SPECTATOR) { winner = p; break; } } } if (winner == null) return; final Player finalWinner = winner; // 2) Lose-Location ermitteln Location loseLocation = findLoseLocation(arenaName); final Location finalLoseLocation = loseLocation; // 3) Verlierer sammeln (alle Spectators) List losers = new ArrayList<>(); plugin.getPlayerArenaMap().forEach((p, a) -> { if (arenaName.equals(a) && !p.equals(finalWinner) && p.getGameMode() == GameMode.SPECTATOR) { losers.add(p); } }); final List finalLosers = new ArrayList<>(losers); // 4) STATS TRACKING - GEWINNER try { StatsHandler.addWin(finalWinner.getUniqueId().toString()); } catch (Exception e) { plugin.getLogger().warning("Win-Stats konnten nicht gespeichert werden: " + e.getMessage()); } // 5) STATS TRACKING - VERLIERER for (Player loser : finalLosers) { try { StatsHandler.addLose(loser.getUniqueId().toString()); } catch (Exception e) { plugin.getLogger().warning("Lose-Stats konnten nicht gespeichert werden: " + e.getMessage()); } } // 6) GEWINNER VORBEREITEN try { finalWinner.setAllowFlight(true); finalWinner.setFlying(true); Location winnerSafeLoc = plugin.getArenaManager() .getArenaConfig().getLocation("Arenas." + arenaName + ".Spawn"); if (winnerSafeLoc != null) { finalWinner.teleport(winnerSafeLoc); } finalWinner.sendMessage(VelocityFall.prefix + ChatColor.translateAlternateColorCodes('&', plugin.getMessages().getMessagesConfig().getString("WinMessage", "&aDu hast gewonnen!"))); finalWinner.sendTitle( ChatColor.translateAlternateColorCodes('&', "&6&lSIEG!"), ChatColor.translateAlternateColorCodes('&', "&eDu hast gewonnen!"), 10, 70, 20 ); finalWinner.playSound(finalWinner.getLocation(), Sound.UI_TOAST_CHALLENGE_COMPLETE, 1f, 1f); ScoreboardHelper.clearBoard(finalWinner); } catch (Exception e) { plugin.getLogger().warning("Gewinner-Setup fehlgeschlagen: " + e.getMessage()); } // 7) VERLIERER BENACHRICHTIGEN for (Player loser : finalLosers) { try { if (finalLoseLocation != null && loser.getGameMode() == GameMode.SPECTATOR) { loser.teleport(finalLoseLocation); } loser.sendTitle( ChatColor.translateAlternateColorCodes('&', "&c&lNIEDERLAGE"), ChatColor.translateAlternateColorCodes('&', "&e" + finalWinner.getName() + " &7hat gewonnen!"), 10, 70, 20 ); loser.playSound(loser.getLocation(), Sound.ENTITY_VILLAGER_NO, 1f, 1f); ScoreboardHelper.clearBoard(loser); } catch (Exception e) { plugin.getLogger().warning("Verlierer-Setup fehlgeschlagen: " + e.getMessage()); } } // 8) FEUERWERK MIT DELAY int delaySeconds = plugin.getConfig().getInt("PostWinDelay", 4); delaySeconds = Math.min(5, Math.max(3, delaySeconds)); final long delayTicks = delaySeconds * 20L; new BukkitRunnable() { int fireworksSpawned = 0; @Override public void run() { if (fireworksSpawned < 5) { try { spawnFireworks(finalWinner.getLocation().clone().add(0, 1, 0), 1); finalWinner.playSound(finalWinner.getLocation(), Sound.ENTITY_FIREWORK_ROCKET_LAUNCH, 1f, 1f); for (Player loser : finalLosers) { if (loser != null && loser.isOnline()) { loser.playSound(loser.getLocation(), Sound.ENTITY_FIREWORK_ROCKET_LAUNCH, 1f, 1f); } } } catch (Exception e) { plugin.getLogger().warning("Feuerwerk-Spawn fehlgeschlagen: " + e.getMessage()); } fireworksSpawned++; } else { cancel(); // Nach Feuerwerk: Arena resetten Bukkit.getScheduler().runTaskLater(plugin, () -> { try { performSafeResetAndLobbyTeleport(arenaName, finalWinner, finalLosers); } catch (Exception e) { plugin.getLogger().severe("Arena-Reset fehlgeschlagen: " + e.getMessage()); } try { plugin.ingameArenas.remove(arenaName); plugin.getSignManager().updateSign(arenaName); } catch (Exception ignored) {} }, 40L); } } }.runTaskTimer(plugin, delayTicks, 10L); } /* --------------------------------------------------- * LOSE LOCATION FINDER - HELPER * --------------------------------------------------- */ private static Location findLoseLocation(String arenaName) { final VelocityFall plugin = VelocityFall.getInstance(); if (plugin == null) return null; var arenaConfig = plugin.getArenaManager().getArenaConfig(); // Verschiedene mögliche Keys probieren Location loseLocation = arenaConfig.getLocation("Arenas." + arenaName + ".Lose"); if (loseLocation == null) loseLocation = arenaConfig.getLocation("Arenas." + arenaName + ".LoseLocation"); if (loseLocation == null) loseLocation = arenaConfig.getLocation("Arenas." + arenaName + ".LosePoint"); if (loseLocation == null) loseLocation = arenaConfig.getLocation("Arenas." + arenaName + ".SetLose"); // YLose Fallback if (loseLocation == null) { Double yLose = arenaConfig.getDouble("Arenas." + arenaName + ".YLose", Double.NaN); if (!yLose.isNaN()) { Location spawn = arenaConfig.getLocation("Arenas." + arenaName + ".Spawn"); Location lobby = arenaConfig.getLocation("Arenas." + arenaName + ".Lobby"); if (spawn != null) { loseLocation = spawn.clone(); loseLocation.setY(yLose); } else if (lobby != null) { loseLocation = lobby.clone(); loseLocation.setY(yLose); } else { Location pos1 = arenaConfig.getLocation("Arenas." + arenaName + ".Pos1"); Location pos2 = arenaConfig.getLocation("Arenas." + arenaName + ".Pos2"); if (pos1 != null && pos2 != null && pos1.getWorld() != null && pos1.getWorld().equals(pos2.getWorld())) { double midX = (pos1.getX() + pos2.getX()) / 2.0; double midZ = (pos1.getZ() + pos2.getZ()) / 2.0; loseLocation = new Location(pos1.getWorld(), midX, yLose, midZ, 0f, 0f); } } } } return loseLocation; } /* --------------------------------------------------- * SAFE RESET & LOBBY TELEPORT * --------------------------------------------------- */ private static void performSafeResetAndLobbyTeleport(String arenaName, Player winner, List losers) { final VelocityFall plugin = VelocityFall.getInstance(); if (plugin == null) return; // Blöcke zurücksetzen plugin.getArenaBlocksMap().forEach((block, matName) -> { try { block.setType(Material.valueOf(matName)); } catch (Exception e) { plugin.getLogger().warning("Block-Reset fehlgeschlagen: " + e.getMessage()); } }); plugin.getArenaBlocksMap().clear(); // Spielzustand zurücksetzen plugin.getGameStartedMap().put(arenaName, false); plugin.getArenaPlayersCountMap().remove(arenaName); // Lobby-Location Location lobby = plugin.getArenaManager().getArenaConfig().getLocation("Arenas." + arenaName + ".Lobby"); // Alle Spieler sammeln List toHandle = new ArrayList<>(); if (winner != null) toHandle.add(winner); for (Player loser : losers) { if (loser != null && !toHandle.contains(loser)) { toHandle.add(loser); } } // Sicherheit: Alle aus playerArenaMap plugin.getPlayerArenaMap().forEach((p, a) -> { if (arenaName.equals(a) && !toHandle.contains(p)) { toHandle.add(p); } }); // Alle Spieler resetten for (Player p : toHandle) { try { p.setGameMode(GameMode.SURVIVAL); p.setAllowFlight(false); p.setFlying(false); p.getInventory().clear(); p.getInventory().setArmorContents(null); p.setHealth(20.0); p.setFoodLevel(20); p.resetTitle(); ScoreboardHelper.clearBoard(p); if (lobby != null) { p.teleport(lobby); } else { Location spawn = plugin.getArenaManager().getArenaConfig().getLocation("Arenas." + arenaName + ".Spawn"); if (spawn != null) { p.teleport(spawn); } else if (p.getWorld() != null) { p.teleport(p.getWorld().getSpawnLocation()); } } plugin.ingamePlayers.remove(p); plugin.getPlayerArenaMap().remove(p); } catch (Exception e) { plugin.getLogger().warning("Spieler-Reset fehlgeschlagen für " + p.getName() + ": " + e.getMessage()); } } // FloorLoseListener cleanup try { if (plugin.getFloorLoseListener() != null) { plugin.getFloorLoseListener().clearLostPlayers(); } } catch (Exception ignored) {} // Signs updaten try { plugin.getSignManager().updateSign(arenaName); } catch (Exception ignored) {} } /* --------------------------------------------------- * FIREWORKS * --------------------------------------------------- */ private static void spawnFireworks(Location loc, int count) { if (loc == null || loc.getWorld() == null) return; for (int i = 0; i < Math.max(1, count); i++) { try { Firework fw = loc.getWorld().spawn(loc.clone().add(0, i * 0.5, 0), Firework.class); FireworkMeta meta = fw.getFireworkMeta(); Color[] colors = {Color.LIME, Color.YELLOW, Color.ORANGE, Color.RED, Color.AQUA, Color.FUCHSIA}; Color randomColor1 = colors[(int)(Math.random() * colors.length)]; Color randomColor2 = colors[(int)(Math.random() * colors.length)]; FireworkEffect effect = FireworkEffect.builder() .with(FireworkEffect.Type.BALL_LARGE) .withColor(randomColor1, randomColor2) .withFade(Color.WHITE) .trail(true) .flicker(true) .build(); meta.addEffect(effect); meta.setPower(1); fw.setFireworkMeta(meta); } catch (Throwable t) { VelocityFall.getInstance().getLogger().warning("Firework-Spawn fehlgeschlagen: " + t.getMessage()); } } } /* --------------------------------------------------- * BROADCAST * --------------------------------------------------- */ private static void broadcast(String arena, String msg) { final VelocityFall plugin = VelocityFall.getInstance(); if (plugin == null) return; plugin.getPlayerArenaMap().forEach((player, a) -> { if (arena.equals(a)) { player.sendMessage(VelocityFall.prefix + msg); } }); } }