package dev.viper.eventengine.manager; import dev.viper.eventengine.EventEngine; import dev.viper.eventengine.events.builtin.EventHandlerRegistry; import dev.viper.eventengine.events.builtin.IEventHandler; import dev.viper.eventengine.model.ActiveEvent; import dev.viper.eventengine.model.EventDefinition; import org.bukkit.Bukkit; import org.bukkit.Sound; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitRunnable; import java.util.*; import java.util.logging.Logger; /** * Steuert den Lebenszyklus von Events: * start → announce → countdown → run → end → reward */ public class EventManager { private final EventEngine plugin; private final Logger log; private final EventHandlerRegistry handlerRegistry; private ActiveEvent currentEvent = null; private final Deque queue = new ArrayDeque<>(); public EventManager(EventEngine plugin) { this.plugin = plugin; this.log = plugin.getLogger(); this.handlerRegistry = new EventHandlerRegistry(plugin); } public boolean startEvent(EventDefinition def, Player initiator) { if (currentEvent != null && currentEvent.getState() != ActiveEvent.State.ENDED) { if (initiator != null) initiator.sendMessage(plugin.prefix() + "§cEin Event läuft bereits: §e" + currentEvent.getDefinition().getDisplayName()); return false; } // Ankündigung broadcast(def.getAnnouncement()); if (def.getDurationSeconds() > 0) broadcast(plugin.prefix() + "§7Dauer: §e" + formatTime(def.getDurationSeconds())); if (plugin.getConfigManager().isLogEvents()) { String by = initiator != null ? initiator.getName() : "Zeitplan"; log.info("Event gestartet von " + by + ": " + def.getDisplayName()); } // 3..2..1..Go Countdown, danach eigentlicher Start int countdown = plugin.getConfigManager().getCountdownSeconds(); if (countdown > 0) { currentEvent = new ActiveEvent(def); currentEvent.setState(ActiveEvent.State.WAITING); runCountdown(def, initiator, countdown); } else { launchEvent(def, initiator); } return true; } /** Countdown-Loop: 3..2..1..Go */ private void runCountdown(EventDefinition def, Player initiator, int seconds) { final ActiveEvent snap = currentEvent; new BukkitRunnable() { int remaining = seconds; @Override public void run() { if (snap != currentEvent) { cancel(); return; } // abgebrochen if (remaining > 0) { // Titel + Sound für alle Online-Spieler String color = remaining <= 3 ? "§c" : "§e"; for (Player p : Bukkit.getOnlinePlayers()) { p.sendTitle(color + remaining, "§7" + def.getDisplayName() + " §7startet gleich!", 0, 25, 5); p.playSound(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_HAT, 1f, 1f); } broadcast(plugin.prefix() + "§e⏳ " + def.getDisplayName() + " §7startet in §c" + remaining + " §7Sekunde" + (remaining == 1 ? "" : "n") + "..."); remaining--; } else { cancel(); for (Player p : Bukkit.getOnlinePlayers()) { p.sendTitle("§a§lGO!", "§7" + def.getDisplayName(), 0, 30, 10); p.playSound(p.getLocation(), Sound.ENTITY_FIREWORK_ROCKET_LARGE_BLAST, 1f, 1.2f); } broadcast(plugin.prefix() + "§a§l🚀 GO! §r§e" + def.getDisplayName() + " §7hat begonnen!"); launchEvent(def, initiator); } } }.runTaskTimer(plugin, 0L, 20L); } /** Eigentlicher Event-Start nach dem Countdown */ private void launchEvent(EventDefinition def, Player initiator) { if (currentEvent == null || currentEvent.getState() == ActiveEvent.State.ENDED) { currentEvent = new ActiveEvent(def); } currentEvent.setState(ActiveEvent.State.RUNNING); runCommands(def.getStartCommands(), null); handlerRegistry.get(def.getType()).ifPresent(h -> h.onStart(currentEvent)); plugin.getScoreboardManager().start(currentEvent); if (def.getDurationSeconds() > 0) { final ActiveEvent snap = currentEvent; if (def.getDurationSeconds() > 70) new BukkitRunnable() { @Override public void run() { if (currentEvent == snap && currentEvent.getState() == ActiveEvent.State.RUNNING) broadcast(plugin.prefix() + "§e⏰ Noch §c60 Sekunden §ebis das Event endet!"); }}.runTaskLater(plugin, (def.getDurationSeconds() - 60) * 20L); if (def.getDurationSeconds() > 15) new BukkitRunnable() { @Override public void run() { if (currentEvent == snap && currentEvent.getState() == ActiveEvent.State.RUNNING) broadcast(plugin.prefix() + "§c⏰ Noch 10 Sekunden!"); }}.runTaskLater(plugin, (def.getDurationSeconds() - 10) * 20L); int taskId = (int) new BukkitRunnable() { @Override public void run() { if (currentEvent == snap && currentEvent.getState() == ActiveEvent.State.RUNNING) endEvent("Zeit abgelaufen"); }}.runTaskLater(plugin, def.getDurationSeconds() * 20L).getTaskId(); currentEvent.setTaskId(taskId); } } public boolean endEvent(String reason) { if (currentEvent == null || currentEvent.getState() == ActiveEvent.State.ENDED) return false; EventDefinition def = currentEvent.getDefinition(); currentEvent.setState(ActiveEvent.State.ENDED); if (currentEvent.getTaskId() != -1) Bukkit.getScheduler().cancelTask(currentEvent.getTaskId()); handlerRegistry.get(def.getType()).ifPresent(h -> h.onEnd(currentEvent)); plugin.getScoreboardManager().stop(); broadcast(plugin.prefix() + "§6✦ §e" + def.getDisplayName() + " §7ist beendet! §7(" + reason + ")"); showLeaderboard(); runCommands(def.getEndCommands(), null); distributeRewards(def); if (plugin.getConfigManager().isLogEvents()) log.info("Event beendet: " + def.getDisplayName() + " [" + reason + "]"); currentEvent = null; if (!queue.isEmpty()) { EventDefinition next = queue.poll(); Bukkit.getScheduler().runTaskLater(plugin, () -> startEvent(next, null), 100L); } return true; } public boolean endEventWithWinner(Player winner, String reason) { if (currentEvent == null || currentEvent.getState() == ActiveEvent.State.ENDED) return false; if (winner != null) { currentEvent.setWinner(winner.getUniqueId()); // Ziel-Nachricht mit Zeit long elapsed = currentEvent.getElapsedSeconds(); broadcast(plugin.prefix() + "§6🏁 §f" + winner.getName() + " §ehat das Ziel erreicht und gewinnt! §8(Zeit: §f" + formatTime((int) elapsed) + "§8)"); // Title für Gewinner winner.sendTitle("§6§l🏁 ZIEL!", "§e" + currentEvent.getDefinition().getDisplayName(), 0, 60, 20); winner.playSound(winner.getLocation(), Sound.UI_TOAST_CHALLENGE_COMPLETE, 1f, 1f); // Title für alle anderen for (UUID uuid : currentEvent.getParticipants()) { Player p = Bukkit.getPlayer(uuid); if (p != null && !p.equals(winner)) { p.sendTitle("§c§lZU SPÄT!", "§7" + winner.getName() + " §7war schneller.", 0, 40, 15); } } distributeWinnerRewards(currentEvent.getDefinition(), winner); } return endEvent(reason); } public boolean join(Player player) { if (currentEvent == null || currentEvent.getState() == ActiveEvent.State.ENDED) { player.sendMessage(plugin.prefix() + "§cKein Event aktiv."); return false; } if (currentEvent.isParticipant(player)) { player.sendMessage(plugin.prefix() + "§cDu nimmst bereits teil."); return false; } EventDefinition def = currentEvent.getDefinition(); if (currentEvent.getParticipantCount() >= def.getMaxPlayers()) { player.sendMessage(plugin.prefix() + "§cDas Event ist voll!"); return false; } currentEvent.addParticipant(player); broadcast(plugin.prefix() + "§a" + player.getName() + " §7nimmt teil! §8(" + currentEvent.getParticipantCount() + "/" + def.getMaxPlayers() + ")"); handlerRegistry.get(def.getType()).ifPresent(h -> h.onPlayerJoin(currentEvent, player)); plugin.getScoreboardManager().onJoin(player, currentEvent); return true; } public void leave(Player player) { if (currentEvent == null) { player.sendMessage(plugin.prefix() + "§cKein Event aktiv."); return; } if (!currentEvent.isParticipant(player)) { player.sendMessage(plugin.prefix() + "§cDu nimmst nicht teil."); return; } currentEvent.removeParticipant(player); handlerRegistry.get(currentEvent.getDefinition().getType()) .ifPresent(h -> h.onPlayerLeave(currentEvent, player)); plugin.getScoreboardManager().onLeave(player); player.sendMessage(plugin.prefix() + "§7Du hast das Event verlassen."); } private void showLeaderboard() { if (currentEvent == null) return; List> lb = currentEvent.getLeaderboard(); if (lb.isEmpty()) return; broadcast("§8§m══════════════════════════════"); broadcast("§6 🏆 Event Leaderboard §8– §e" + currentEvent.getDefinition().getDisplayName()); int rank = 1; for (Map.Entry e : lb) { if (rank > 5) break; Player p = Bukkit.getPlayer(e.getKey()); String name = p != null ? p.getName() : "Unbekannt"; String medal = switch (rank) { case 1 -> "§6🥇"; case 2 -> "§7🥈"; case 3 -> "§c🥉"; default -> "§7 " + rank + "."; }; broadcast(" " + medal + " §f" + name + " §8– §e" + e.getValue() + " Punkte"); rank++; } broadcast("§8§m══════════════════════════════"); } private void distributeRewards(EventDefinition def) { if (def.getRewards().isEmpty() || currentEvent == null) return; for (UUID uuid : currentEvent.getParticipants()) { Player p = Bukkit.getPlayer(uuid); if (p == null) continue; for (String cmd : def.getRewards()) Bukkit.dispatchCommand(Bukkit.getConsoleSender(), cmd.replace("%player%", p.getName())); } } public void distributeWinnerRewards(EventDefinition def, Player winner) { if (def.getWinnerRewards().isEmpty() || winner == null) return; runCommands(def.getWinnerRewards(), winner); } private void runCommands(List cmds, Player player) { for (String cmd : cmds) { String filled = player != null ? cmd.replace("%player%", player.getName()) : cmd; try { Bukkit.dispatchCommand(Bukkit.getConsoleSender(), filled); } catch (Exception ex) { log.warning("Befehl fehlgeschlagen: " + filled); } } } private void broadcast(String msg) { Bukkit.broadcastMessage(msg); } public static String formatTime(int seconds) { if (seconds <= 0) return "∞"; int m = seconds / 60, s = seconds % 60; if (m == 0) return s + "s"; if (s == 0) return m + "m"; return m + "m " + s + "s"; } public void enqueue(EventDefinition def) { queue.add(def); } public void clearQueue() { queue.clear(); } public int queueSize() { return queue.size(); } public ActiveEvent getCurrentEvent() { return currentEvent; } public boolean isRunning() { return currentEvent != null && currentEvent.getState() == ActiveEvent.State.RUNNING; } public EventHandlerRegistry getHandlerRegistry() { return handlerRegistry; } }