diff --git a/src/main/java/dev/viper/eventengine/EventEngine.java b/src/main/java/dev/viper/eventengine/EventEngine.java new file mode 100644 index 0000000..b51f7b7 --- /dev/null +++ b/src/main/java/dev/viper/eventengine/EventEngine.java @@ -0,0 +1,88 @@ +package dev.viper.eventengine; + +import dev.viper.eventengine.command.EventCommand; +import dev.viper.eventengine.command.RegionSetupManager; +import dev.viper.eventengine.config.ConfigManager; +import dev.viper.eventengine.gui.EventGUI; +import dev.viper.eventengine.listener.EventCreateListener; +import dev.viper.eventengine.listener.EventProtectionListener; +import dev.viper.eventengine.manager.EventManager; +import dev.viper.eventengine.manager.EventRegistry; +import dev.viper.eventengine.scheduler.EventScheduler; +import org.bukkit.plugin.java.JavaPlugin; + +public class EventEngine extends JavaPlugin { + + private static EventEngine instance; + + private ConfigManager configManager; + private EventRegistry eventRegistry; + private EventManager eventManager; + private EventScheduler eventScheduler; + private EventGUI eventGUI; + private EventCreateListener eventCreateListener; + private RegionSetupManager regionSetupManager; + + @Override + public void onEnable() { + instance = this; + + // Config + configManager = new ConfigManager(this); + configManager.load(); + + // Registry (builtin + custom) + eventRegistry = new EventRegistry(this); + eventRegistry.init(); + + // Event-Manager (Lifecycle) + eventManager = new EventManager(this); + + // Scheduler + eventScheduler = new EventScheduler(this); + eventScheduler.start(); + + // GUI + eventGUI = new EventGUI(this); + getServer().getPluginManager().registerEvents(eventGUI, this); + + // Create-Listener (Custom Event Builder) + eventCreateListener = new EventCreateListener(this); + getServer().getPluginManager().registerEvents(eventCreateListener, this); + + // Region-Schutz Listener + getServer().getPluginManager().registerEvents(new EventProtectionListener(this), this); + + // Region-Setup Manager + regionSetupManager = new RegionSetupManager(this); + + // Command + EventCommand cmd = new EventCommand(this); + getCommand("event").setExecutor(cmd); + getCommand("event").setTabCompleter(cmd); + + getLogger().info("EventEngine gestartet — " + eventRegistry.count() + " Events geladen."); + } + + @Override + public void onDisable() { + if (eventScheduler != null) eventScheduler.stop(); + if (eventManager != null && eventManager.isRunning()) { + eventManager.endEvent("Server-Shutdown"); + } + getLogger().info("EventEngine deaktiviert."); + } + + // ─── Getter ──────────────────────────────────────────────────────────── + + public static EventEngine getInstance() { return instance; } + public ConfigManager getConfigManager() { return configManager; } + public EventRegistry getEventRegistry() { return eventRegistry; } + public EventManager getEventManager() { return eventManager; } + public EventScheduler getEventScheduler() { return eventScheduler; } + public EventGUI getEventGUI() { return eventGUI; } + public EventCreateListener getEventCreateListener() { return eventCreateListener; } + public RegionSetupManager getRegionSetupManager() { return regionSetupManager; } + + public String prefix() { return configManager.getPrefix(); } +} diff --git a/src/main/java/dev/viper/eventengine/command/EventCommand.java b/src/main/java/dev/viper/eventengine/command/EventCommand.java new file mode 100644 index 0000000..5c5664f --- /dev/null +++ b/src/main/java/dev/viper/eventengine/command/EventCommand.java @@ -0,0 +1,318 @@ +package dev.viper.eventengine.command; + +import dev.viper.eventengine.EventEngine; +import dev.viper.eventengine.command.RegionSetupManager; +import dev.viper.eventengine.model.ActiveEvent; +import dev.viper.eventengine.model.EventDefinition; +import dev.viper.eventengine.model.ScheduleEntry; +import dev.viper.eventengine.manager.EventManager; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.bukkit.entity.Player; + +import java.util.*; +import java.util.stream.Collectors; + +public class EventCommand implements CommandExecutor, TabCompleter { + + private final EventEngine plugin; + + public EventCommand(EventEngine plugin) { + this.plugin = plugin; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + String p = plugin.prefix(); + + if (args.length == 0) { + if (sender instanceof Player player) { + plugin.getEventGUI().openMain(player); + } else { + sendHelp(sender); + } + return true; + } + + switch (args[0].toLowerCase()) { + + // ─── GUI ─────────────────────────────────────────────────────── + case "gui" -> { + requirePlayer(sender, () -> plugin.getEventGUI().openMain((Player) sender)); + } + + // ─── START ───────────────────────────────────────────────────── + case "start" -> { + requirePerm(sender, () -> { + if (args.length < 2) { sender.sendMessage(p + "§cUsage: /event start "); return; } + String id = args[1].toLowerCase(); + EventDefinition def; + if (id.equals("random")) { + def = plugin.getEventRegistry().getWeightedRandom().orElse(null); + } else { + def = plugin.getEventRegistry().get(id).orElse(null); + } + if (def == null) { sender.sendMessage(p + "§cEvent nicht gefunden: §f" + id); return; } + Player initiator = sender instanceof Player pl ? pl : null; + plugin.getEventManager().startEvent(def, initiator); + }); + } + + // ─── STOP ────────────────────────────────────────────────────── + case "stop" -> { + requirePerm(sender, () -> { + String reason = args.length > 1 ? String.join(" ", Arrays.copyOfRange(args, 1, args.length)) : "Admin"; + if (!plugin.getEventManager().endEvent(reason)) { + sender.sendMessage(p + "§cKein Event läuft."); + } + }); + } + + // ─── JOIN/LEAVE ──────────────────────────────────────────────── + case "join" -> requirePlayer(sender, () -> plugin.getEventManager().join((Player) sender)); + case "leave" -> requirePlayer(sender, () -> plugin.getEventManager().leave((Player) sender)); + + // ─── INFO ────────────────────────────────────────────────────── + case "info" -> { + if (args.length >= 2) { + // Event-Info + plugin.getEventRegistry().get(args[1]).ifPresentOrElse(def -> { + sender.sendMessage("§8§m══════════════════════════════"); + sender.sendMessage("§6 " + def.getDisplayName() + " §8[" + def.getId() + "]"); + sender.sendMessage("§7 Kategorie: §f" + def.getCategory().getDisplayName()); + sender.sendMessage("§7 Beschreibung: §f" + def.getDescription()); + sender.sendMessage("§7 Dauer: §f" + EventManager.formatTime(def.getDurationSeconds())); + sender.sendMessage("§7 Spieler: §f" + def.getMinPlayers() + "–" + def.getMaxPlayers()); + sender.sendMessage("§7 Custom: §f" + def.isCustom()); + sender.sendMessage("§7 Start-Befehle: §f" + def.getStartCommands().size()); + sender.sendMessage("§7 Belohnungen: §f" + def.getRewards().size()); + sender.sendMessage("§8§m══════════════════════════════"); + }, () -> sender.sendMessage(p + "§cEvent nicht gefunden.")); + } else { + // Aktuelles Event + ActiveEvent current = plugin.getEventManager().getCurrentEvent(); + if (current == null) { + sender.sendMessage(p + "§cKein Event aktiv."); + } else { + sender.sendMessage("§8§m══════════════════════════════"); + sender.sendMessage("§6 Aktuelles Event: §e" + current.getDefinition().getDisplayName()); + sender.sendMessage("§7 Teilnehmer: §f" + current.getParticipantCount()); + sender.sendMessage("§7 Verbleibend: §f" + EventManager.formatTime((int) current.getRemainingSeconds())); + sender.sendMessage("§7 Status: §f" + current.getState()); + sender.sendMessage("§8§m══════════════════════════════"); + } + } + } + + // ─── LIST ────────────────────────────────────────────────────── + case "list" -> { + int page = args.length >= 2 ? parseIntSafe(args[1], 1) - 1 : 0; + List all = new ArrayList<>(plugin.getEventRegistry().getAll()); + int perPage = 12; + int totalPages = Math.max(1, (int) Math.ceil(all.size() / (double) perPage)); + page = Math.max(0, Math.min(page, totalPages - 1)); + + sender.sendMessage("§8§m══════════════════════════════"); + sender.sendMessage("§6 EventEngine – Events §8(" + (page + 1) + "/" + totalPages + ") §7[" + all.size() + " total]"); + all.stream().skip((long) page * perPage).limit(perPage).forEach(def -> + sender.sendMessage(" §7- §e" + def.getId() + " §8│ §f" + def.getDisplayName() + " §8│ §7" + def.getCategory().getDisplayName()) + ); + sender.sendMessage("§8§m══════════════════════════════"); + if (totalPages > 1) sender.sendMessage("§7Nächste Seite: §f/event list " + (page + 2)); + } + + // ─── SCHEDULE ────────────────────────────────────────────────── + case "schedule" -> { + List schedule = plugin.getConfigManager().getSchedule(); + sender.sendMessage("§8§m══════════════════════════════"); + sender.sendMessage("§6 Geplante Events §8(" + schedule.size() + ")"); + if (schedule.isEmpty()) { + sender.sendMessage("§7 Kein Zeitplan konfiguriert."); + sender.sendMessage("§7 Füge Einträge in der config.yml hinzu."); + } else { + schedule.forEach(e -> sender.sendMessage(" §7• §f" + e)); + } + sender.sendMessage("§8§m══════════════════════════════"); + } + + // ─── CREATE ──────────────────────────────────────────────────── + case "create" -> { + requirePerm(sender, () -> { + requirePlayer(sender, () -> { + if (args.length < 2) { sender.sendMessage(p + "§cUsage: /event create "); return; } + String id = args[1].toLowerCase().replaceAll("[^a-z0-9_]", "_"); + if (plugin.getEventRegistry().exists(id)) { + sender.sendMessage(p + "§cID bereits vorhanden: §f" + id); + return; + } + if (plugin.getEventCreateListener().hasSession((Player) sender)) { + sender.sendMessage(p + "§cDu hast bereits einen laufenden Builder."); + return; + } + plugin.getEventCreateListener().startSession((Player) sender, id); + }); + }); + } + + // ─── DELETE ──────────────────────────────────────────────────── + case "delete" -> { + requirePerm(sender, () -> { + if (args.length < 2) { sender.sendMessage(p + "§cUsage: /event delete "); return; } + String id = args[1].toLowerCase(); + EventDefinition def = plugin.getEventRegistry().get(id).orElse(null); + if (def == null) { sender.sendMessage(p + "§cEvent nicht gefunden."); return; } + if (!def.isCustom()) { sender.sendMessage(p + "§cEingebaute Events können nicht gelöscht werden."); return; } + plugin.getEventRegistry().remove(id); + sender.sendMessage(p + "§aCustom Event §e'" + id + "' §agelöscht."); + }); + } + + // ─── SCORE ───────────────────────────────────────────────────── + case "score" -> { + requirePerm(sender, () -> { + if (args.length < 3) { sender.sendMessage(p + "§cUsage: /event score "); return; } + Player target = Bukkit.getPlayer(args[1]); + if (target == null) { sender.sendMessage(p + "§cSpieler nicht gefunden."); return; } + int points = parseIntSafe(args[2], 0); + ActiveEvent ev = plugin.getEventManager().getCurrentEvent(); + if (ev == null) { sender.sendMessage(p + "§cKein Event aktiv."); return; } + ev.addScore(target.getUniqueId(), points); + sender.sendMessage(p + "§a+" + points + " Punkte für §e" + target.getName()); + }); + } + + // ─── ROTATION ────────────────────────────────────────────────── + case "rotation" -> { + requirePerm(sender, () -> { + if (args.length < 3) { sender.sendMessage(p + "§cUsage: /event rotation "); return; } + String action = args[1].toLowerCase(); + String id = args[2].toLowerCase(); + plugin.getEventRegistry().get(id).ifPresentOrElse(def -> { + boolean rotState = action.equals("add"); + def.setInRotation(rotState); + if (def.isCustom()) plugin.getConfigManager().saveCustomEvent(def); + sender.sendMessage(p + "§e" + def.getDisplayName() + " §7→ Rotation: " + (rotState ? "§aAktiv" : "§cInaktiv")); + }, () -> sender.sendMessage(p + "§cEvent nicht gefunden.")); + }); + } + + // ─── RELOAD ──────────────────────────────────────────────────── + case "reload" -> { + requirePerm(sender, () -> { + plugin.getConfigManager().load(); + plugin.getEventRegistry().init(); + plugin.getEventScheduler().restart(); + sender.sendMessage(p + "§aKonfiguration neu geladen. §7(" + plugin.getEventRegistry().count() + " Events)"); + }); + } + + // ─── REGION ──────────────────────────────────────────────── + case "region" -> { + requirePerm(sender, () -> { + requirePlayer(sender, () -> { + if (args.length < 3) { + sender.sendMessage(p + "§cUsage: /event region "); + return; + } + String sub = args[1].toLowerCase(); + String id = args[2].toLowerCase(); + RegionSetupManager rsm = plugin.getRegionSetupManager(); + switch (sub) { + case "pos1" -> rsm.handlePos1((Player) sender, id); + case "pos2" -> rsm.handlePos2((Player) sender, id); + case "clear" -> rsm.handleClear((Player) sender, id); + case "info" -> rsm.handleInfo((Player) sender, id); + default -> sender.sendMessage(p + "§cUnbekannte Sub-Option. pos1 | pos2 | clear | info"); + } + }); + }); + } + + default -> sendHelp(sender); + } + + return true; + } + + // ─── Tab-Completer ───────────────────────────────────────────────────── + + @Override + public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { + if (args.length == 1) { + return filter(args[0], "gui", "start", "stop", "join", "leave", "info", "list", + "schedule", "create", "delete", "score", "rotation", "region", "reload"); + } + if (args.length == 2) { + return switch (args[0].toLowerCase()) { + case "start", "info", "delete" -> { + List ids = plugin.getEventRegistry().getAll().stream() + .map(EventDefinition::getId) + .collect(Collectors.toList()); + if (args[0].equalsIgnoreCase("start")) ids.add(0, "random"); + yield filter(args[1], ids.toArray(new String[0])); + } + case "rotation" -> filter(args[1], "add", "remove"); + case "score" -> null; // Spielernamen + case "region" -> filter(args[1], "pos1", "pos2", "clear", "info"); + default -> null; + }; + } + if (args.length == 3 && args[0].equalsIgnoreCase("rotation")) { + return plugin.getEventRegistry().getAll().stream() + .map(EventDefinition::getId) + .filter(id -> id.startsWith(args[2].toLowerCase())) + .collect(Collectors.toList()); + } + return null; + } + + // ─── Hilfsmethoden ───────────────────────────────────────────────────── + + private void sendHelp(CommandSender sender) { + sender.sendMessage("§8§m══════════════════════════════"); + sender.sendMessage("§6 EventEngine Befehle"); + sender.sendMessage("§e /event gui §7- GUI öffnen"); + sender.sendMessage("§e /event start §7- Event starten"); + sender.sendMessage("§e /event stop [grund] §7- Event beenden"); + sender.sendMessage("§e /event join/leave §7- Am Event teilnehmen"); + sender.sendMessage("§e /event info [id] §7- Event-Info anzeigen"); + sender.sendMessage("§e /event list [seite] §7- Alle Events auflisten"); + sender.sendMessage("§e /event schedule §7- Zeitplan anzeigen"); + sender.sendMessage("§e /event create §7- Custom Event erstellen"); + sender.sendMessage("§e /event delete §7- Custom Event löschen"); + sender.sendMessage("§e /event score §7- Punkte vergeben"); + sender.sendMessage("§e /event rotation §7- Rotation steuern"); + sender.sendMessage("§e /event reload §7- Config neu laden"); + sender.sendMessage("§e /event region §7- Event-Arena setzen"); + sender.sendMessage("§8§m══════════════════════════════"); + } + + private void requirePerm(CommandSender sender, Runnable r) { + if (!sender.hasPermission("eventengine.admin")) { + sender.sendMessage(plugin.prefix() + "§cKeine Berechtigung."); + return; + } + r.run(); + } + + private void requirePlayer(CommandSender sender, Runnable r) { + if (!(sender instanceof Player)) { + sender.sendMessage(plugin.prefix() + "§cNur für Spieler."); + return; + } + r.run(); + } + + private List filter(String prefix, String... options) { + return Arrays.stream(options) + .filter(s -> s.toLowerCase().startsWith(prefix.toLowerCase())) + .collect(Collectors.toList()); + } + + private int parseIntSafe(String s, int def) { + try { return Integer.parseInt(s); } catch (Exception e) { return def; } + } +} diff --git a/src/main/java/dev/viper/eventengine/command/RegionSetupManager.java b/src/main/java/dev/viper/eventengine/command/RegionSetupManager.java new file mode 100644 index 0000000..240b51d --- /dev/null +++ b/src/main/java/dev/viper/eventengine/command/RegionSetupManager.java @@ -0,0 +1,104 @@ +package dev.viper.eventengine.command; + +import dev.viper.eventengine.EventEngine; +import dev.viper.eventengine.model.EventDefinition; +import dev.viper.eventengine.model.EventRegion; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * Stellt Pos1/Pos2-Auswahl für Event-Regionen bereit. + * Eingebaut in EventCommand via sub-commands: + * + * /event region pos1 → Pos1 auf Spieler-Position setzen + * /event region pos2 → Pos2 setzen + Region speichern + * /event region clear → Region entfernen + * /event region info → Region anzeigen + */ +public class RegionSetupManager { + + private final EventEngine plugin; + // Temporäre Pos1-Speicher pro Spieler: UUID → [eventId, Location] + private final Map pos1Selection = new HashMap<>(); + + public RegionSetupManager(EventEngine plugin) { + this.plugin = plugin; + } + + public void handlePos1(Player player, String eventId) { + EventDefinition def = plugin.getEventRegistry().get(eventId).orElse(null); + if (def == null) { player.sendMessage(plugin.prefix() + "§cEvent nicht gefunden: §f" + eventId); return; } + + pos1Selection.put(player.getUniqueId(), new Object[]{eventId, player.getLocation().clone()}); + player.sendMessage(plugin.prefix() + "§aPos1 gesetzt: §f" + formatLoc(player)); + player.sendMessage(plugin.prefix() + "§7Jetzt: §f/event region pos2 " + eventId); + } + + public void handlePos2(Player player, String eventId) { + EventDefinition def = plugin.getEventRegistry().get(eventId).orElse(null); + if (def == null) { player.sendMessage(plugin.prefix() + "§cEvent nicht gefunden: §f" + eventId); return; } + + Object[] sel = pos1Selection.get(player.getUniqueId()); + if (sel == null || !sel[0].equals(eventId)) { + player.sendMessage(plugin.prefix() + "§cZuerst Pos1 setzen: §f/event region pos1 " + eventId); + return; + } + + org.bukkit.Location pos1 = (org.bukkit.Location) sel[1]; + org.bukkit.Location pos2 = player.getLocation().clone(); + + if (!pos1.getWorld().equals(pos2.getWorld())) { + player.sendMessage(plugin.prefix() + "§cBeide Positionen müssen in der gleichen Welt sein!"); + return; + } + + EventRegion region = new EventRegion(pos1, pos2); + def.setRegion(region); + + // Custom Events direkt speichern; builtin: in memory (gilt bis Reload) + if (def.isCustom()) plugin.getConfigManager().saveCustomEvent(def); + + pos1Selection.remove(player.getUniqueId()); + + player.sendMessage(plugin.prefix() + "§a✔ Region gesetzt für §e" + def.getDisplayName() + "§a:"); + player.sendMessage("§7 Welt: §f" + region.getWorldName()); + player.sendMessage("§7 Von: §f" + region.getMinX() + ", " + region.getMinY() + ", " + region.getMinZ()); + player.sendMessage("§7 Bis: §f" + region.getMaxX() + ", " + region.getMaxY() + ", " + region.getMaxZ()); + player.sendMessage("§7 Größe: §f" + region.getSizeX() + "x" + region.getSizeY() + "x" + region.getSizeZ()); + } + + public void handleClear(Player player, String eventId) { + EventDefinition def = plugin.getEventRegistry().get(eventId).orElse(null); + if (def == null) { player.sendMessage(plugin.prefix() + "§cEvent nicht gefunden: §f" + eventId); return; } + def.setRegion(null); + if (def.isCustom()) plugin.getConfigManager().saveCustomEvent(def); + player.sendMessage(plugin.prefix() + "§7Region für §e" + def.getDisplayName() + " §7entfernt (gesamte Welt)."); + } + + public void handleInfo(Player player, String eventId) { + EventDefinition def = plugin.getEventRegistry().get(eventId).orElse(null); + if (def == null) { player.sendMessage(plugin.prefix() + "§cEvent nicht gefunden: §f" + eventId); return; } + if (!def.hasRegion()) { + player.sendMessage(plugin.prefix() + "§e" + def.getDisplayName() + " §7hat keine Region (gesamte Welt)."); + return; + } + EventRegion r = def.getRegion(); + player.sendMessage("§8§m══════════════════════════════"); + player.sendMessage("§6 Region: §e" + def.getDisplayName()); + player.sendMessage("§7 Welt: §f" + r.getWorldName()); + player.sendMessage("§7 Von: §f" + r.getMinX() + ", " + r.getMinY() + ", " + r.getMinZ()); + player.sendMessage("§7 Bis: §f" + r.getMaxX() + ", " + r.getMaxY() + ", " + r.getMaxZ()); + player.sendMessage("§7 Größe: §f" + r.getSizeX() + "×" + r.getSizeY() + "×" + r.getSizeZ() + " Blöcke"); + player.sendMessage("§8§m══════════════════════════════"); + } + + private String formatLoc(Player p) { + return p.getWorld().getName() + " " + + p.getLocation().getBlockX() + ", " + + p.getLocation().getBlockY() + ", " + + p.getLocation().getBlockZ(); + } +} diff --git a/src/main/java/dev/viper/eventengine/config/ConfigManager.java b/src/main/java/dev/viper/eventengine/config/ConfigManager.java new file mode 100644 index 0000000..07e1529 --- /dev/null +++ b/src/main/java/dev/viper/eventengine/config/ConfigManager.java @@ -0,0 +1,169 @@ +package dev.viper.eventengine.config; + +import dev.viper.eventengine.EventEngine; +import dev.viper.eventengine.model.EventDefinition; +import dev.viper.eventengine.model.ScheduleEntry; +import dev.viper.eventengine.util.ColorUtil; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.io.IOException; +import java.time.DayOfWeek; +import java.time.LocalTime; +import java.time.format.DateTimeParseException; +import java.util.*; +import java.util.logging.Logger; + +public class ConfigManager { + + private final EventEngine plugin; + private final Logger log; + + private FileConfiguration mainConfig; + private File customEventsFile; + private FileConfiguration customEventsConfig; + + // Geladene Zeitplan-Einträge + private final List schedule = new ArrayList<>(); + + // Intervall-Fallback (Minuten), wenn kein Zeitplan aktiv + private int intervalMinutes; + private boolean useInterval; + private boolean randomOnInterval; + private String defaultEventId; + + // Ankündigungs-Vorlauf in Sekunden + private int announceBefore; + // Ob Events im Chat-Log erscheinen + private boolean logEvents; + // Prefix für alle Nachrichten + private String prefix; + // Schutz-Einstellungen + private boolean enforceRegionBoundary; + private boolean noExplosionBlockDamage; + private boolean restrictBlockInteraction; + + public ConfigManager(EventEngine plugin) { + this.plugin = plugin; + this.log = plugin.getLogger(); + } + + // ─── Laden ───────────────────────────────────────────────────────────── + + public void load() { + plugin.saveDefaultConfig(); + plugin.reloadConfig(); + mainConfig = plugin.getConfig(); + + loadSchedule(); + loadGeneral(); + loadCustomEventsFile(); + } + + private void loadGeneral() { + intervalMinutes = mainConfig.getInt("settings.interval-minutes", 60); + useInterval = mainConfig.getBoolean("settings.use-interval", false); + randomOnInterval = mainConfig.getBoolean("settings.random-on-interval", true); + defaultEventId = mainConfig.getString("settings.default-event", "RANDOM"); + announceBefore = mainConfig.getInt("settings.announce-before-seconds", 30); + logEvents = mainConfig.getBoolean("settings.log-events", true); + enforceRegionBoundary = mainConfig.getBoolean("protection.enforce-region-boundary", true); + noExplosionBlockDamage = mainConfig.getBoolean("protection.no-explosion-block-damage", true); + restrictBlockInteraction = mainConfig.getBoolean("protection.restrict-block-interaction", true); + prefix = ColorUtil.color(mainConfig.getString("settings.prefix", "&8[&6EventEngine&8]&r ")); + } + + private void loadSchedule() { + schedule.clear(); + List> entries = mainConfig.getMapList("schedule"); + for (Map entry : entries) { + try { + String timeStr = (String) entry.get("time"); + String eventId = (String) entry.get("event"); + if (timeStr == null || eventId == null) continue; + + LocalTime time = LocalTime.parse(timeStr); + List days = new ArrayList<>(); + + Object daysRaw = entry.get("days"); + if (daysRaw instanceof List dayList) { + for (Object d : dayList) { + String ds = d.toString().toUpperCase(); + if (ds.equals("DAILY")) { days.clear(); break; } + try { days.add(DayOfWeek.valueOf(ds)); } + catch (Exception ignored) {} + } + } + + schedule.add(new ScheduleEntry(days, time, eventId)); + } catch (DateTimeParseException e) { + log.warning("Ungültiger Zeitplan-Eintrag: " + entry); + } + } + log.info("Zeitplan geladen: " + schedule.size() + " Einträge."); + } + + private void loadCustomEventsFile() { + customEventsFile = new File(plugin.getDataFolder(), "custom_events.yml"); + if (!customEventsFile.exists()) { + try { customEventsFile.createNewFile(); } + catch (IOException e) { log.severe("Konnte custom_events.yml nicht erstellen!"); } + } + customEventsConfig = YamlConfiguration.loadConfiguration(customEventsFile); + } + + // ─── Custom Events lesen/schreiben ───────────────────────────────────── + + public Map loadCustomEvents() { + Map result = new LinkedHashMap<>(); + ConfigurationSection root = customEventsConfig.getConfigurationSection("events"); + if (root == null) return result; + + for (String id : root.getKeys(false)) { + ConfigurationSection sec = root.getConfigurationSection(id); + if (sec != null) { + EventDefinition def = EventDefinition.fromConfig(id, sec); + result.put(id, def); + } + } + log.info("Custom Events geladen: " + result.size()); + return result; + } + + public void saveCustomEvent(EventDefinition def) { + ConfigurationSection root = customEventsConfig.getConfigurationSection("events"); + if (root == null) root = customEventsConfig.createSection("events"); + ConfigurationSection sec = root.createSection(def.getId()); + def.saveToConfig(sec); + try { + customEventsConfig.save(customEventsFile); + } catch (IOException e) { + log.severe("Konnte custom_events.yml nicht speichern: " + e.getMessage()); + } + } + + public void deleteCustomEvent(String id) { + if (customEventsConfig.contains("events." + id)) { + customEventsConfig.set("events." + id, null); + try { customEventsConfig.save(customEventsFile); } + catch (IOException e) { log.severe("Speicherfehler: " + e.getMessage()); } + } + } + + // ─── Getter ──────────────────────────────────────────────────────────── + + public List getSchedule() { return Collections.unmodifiableList(schedule); } + public int getIntervalMinutes() { return intervalMinutes; } + public boolean isUseInterval() { return useInterval; } + public boolean isRandomOnInterval() { return randomOnInterval; } + public String getDefaultEventId() { return defaultEventId; } + public int getAnnounceBefore() { return announceBefore; } + public boolean isLogEvents() { return logEvents; } + public String getPrefix() { return prefix; } + public boolean isEnforceRegionBoundary() { return enforceRegionBoundary; } + public boolean isNoExplosionBlockDamage() { return noExplosionBlockDamage; } + public boolean isRestrictBlockInteraction() { return restrictBlockInteraction; } + public FileConfiguration getMainConfig() { return mainConfig; } +} diff --git a/src/main/java/dev/viper/eventengine/config/EventOverrideLoader.java b/src/main/java/dev/viper/eventengine/config/EventOverrideLoader.java new file mode 100644 index 0000000..b17b8ba --- /dev/null +++ b/src/main/java/dev/viper/eventengine/config/EventOverrideLoader.java @@ -0,0 +1,53 @@ +package dev.viper.eventengine.config; + +import dev.viper.eventengine.EventEngine; +import dev.viper.eventengine.util.ColorUtil; +import dev.viper.eventengine.model.EventCategory; +import dev.viper.eventengine.model.EventDefinition; +import org.bukkit.configuration.ConfigurationSection; + +import java.util.List; + +/** + * Liest den "event-overrides" Block aus config.yml und + * überschreibt die Defaults eingebauter EventDefinitions. + */ +public class EventOverrideLoader { + + private final EventEngine plugin; + + public EventOverrideLoader(EventEngine plugin) { + this.plugin = plugin; + } + + public void apply(EventDefinition def) { + ConfigurationSection overrides = plugin.getConfigManager() + .getMainConfig().getConfigurationSection("event-overrides"); + if (overrides == null) return; + + ConfigurationSection sec = overrides.getConfigurationSection(def.getId()); + if (sec == null) return; + + if (sec.contains("display-name")) def.setDisplayName(sec.getString("display-name")); + if (sec.contains("description")) def.setDescription(sec.getString("description")); + if (sec.contains("duration-seconds")) def.setDurationSeconds(sec.getInt("duration-seconds")); + if (sec.contains("min-players")) def.setMinPlayers(sec.getInt("min-players")); + if (sec.contains("max-players")) def.setMaxPlayers(sec.getInt("max-players")); + if (sec.contains("announcement")) def.setAnnouncement(ColorUtil.color(sec.getString("announcement"))); + if (sec.contains("in-rotation")) def.setInRotation(sec.getBoolean("in-rotation")); + if (sec.contains("weight")) def.setWeight(sec.getInt("weight")); + if (sec.contains("start-commands")) def.setStartCommands(sec.getStringList("start-commands")); + if (sec.contains("end-commands")) def.setEndCommands(sec.getStringList("end-commands")); + if (sec.contains("rewards")) def.setRewards(sec.getStringList("rewards")); + if (sec.contains("category")) { + try { def.setCategory(EventCategory.valueOf(sec.getString("category").toUpperCase())); } + catch (Exception ignored) {} + } + ConfigurationSection settings = sec.getConfigurationSection("settings"); + if (settings != null) { + for (String key : settings.getKeys(false)) { + def.setSetting(key, settings.get(key)); + } + } + } +} diff --git a/src/main/java/dev/viper/eventengine/events/builtin/BossRushHandler.java b/src/main/java/dev/viper/eventengine/events/builtin/BossRushHandler.java new file mode 100644 index 0000000..a93a18d --- /dev/null +++ b/src/main/java/dev/viper/eventengine/events/builtin/BossRushHandler.java @@ -0,0 +1,123 @@ +package dev.viper.eventengine.events.builtin; + +import dev.viper.eventengine.EventEngine; +import dev.viper.eventengine.model.ActiveEvent; +import org.bukkit.*; +import org.bukkit.attribute.Attribute; +import org.bukkit.boss.BarColor; +import org.bukkit.boss.BarStyle; +import org.bukkit.boss.BossBar; +import org.bukkit.entity.*; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDeathEvent; + +import java.util.UUID; + +/** + * Boss Rush: + * - 5 Bosse nacheinander, jeder stärker als der vorherige + * - BossBar zeigt HP + * - Wer den letzten Schlag landet: Bonus-Punkte + */ +public class BossRushHandler implements IEventHandler, Listener { + + private static final String[][] BOSSES = { + {"§2Mini-Zomboss", "ZOMBIE"}, + {"§6Skelett-König", "SKELETON"}, + {"§dHexen-Mutter", "WITCH"}, + {"§cRiesen-Creeper", "CREEPER"}, + {"§4§lDER ENDGEGNER", "WITHER_SKELETON"} + }; + + private final EventEngine plugin; + private ActiveEvent currentEvent; + private int bossIndex = 0; + private LivingEntity currentBoss; + private BossBar bossBar; + + public BossRushHandler(EventEngine plugin) { + this.plugin = plugin; + } + + @Override + public void onStart(ActiveEvent event) { + this.currentEvent = event; + this.bossIndex = 0; + Bukkit.getPluginManager().registerEvents(this, plugin); + broadcast("§4⚔ §cBoss Rush! §75 Bosse nacheinander. Wer überlebt?"); + spawnNextBoss(); + } + + @Override + public void onEnd(ActiveEvent event) { + HandlerList.unregisterAll(this); + if (bossBar != null) { bossBar.removeAll(); bossBar = null; } + if (currentBoss != null && !currentBoss.isDead()) currentBoss.remove(); + this.currentEvent = null; + } + + private void spawnNextBoss() { + if (bossIndex >= BOSSES.length) { + broadcast("§a✔ §eAlle Bosse besiegt! Boss Rush abgeschlossen!"); + plugin.getEventManager().endEvent("Alle Bosse besiegt"); + return; + } + + String[] bossData = BOSSES[bossIndex]; + String bossName = bossData[0]; + EntityType type; + try { type = EntityType.valueOf(bossData[1]); } + catch (Exception e) { type = EntityType.ZOMBIE; } + + // Spawn bei erstem Online-Teilnehmer + Location loc = null; + for (UUID uuid : currentEvent.getParticipants()) { + Player p = Bukkit.getPlayer(uuid); + if (p != null) { loc = p.getLocation().add(5, 0, 0); break; } + } + if (loc == null) return; + + double hp = 40 + (bossIndex * 40); + currentBoss = (LivingEntity) loc.getWorld().spawnEntity(loc, type); + currentBoss.setCustomName(bossName); + currentBoss.setCustomNameVisible(true); + if (currentBoss.getAttribute(Attribute.MAX_HEALTH) != null) { + currentBoss.getAttribute(Attribute.MAX_HEALTH).setBaseValue(hp); + currentBoss.setHealth(hp); + } + + // BossBar + if (bossBar != null) bossBar.removeAll(); + bossBar = Bukkit.createBossBar(bossName, BarColor.RED, BarStyle.SEGMENTED_10); + for (UUID uuid : currentEvent.getParticipants()) { + Player p = Bukkit.getPlayer(uuid); + if (p != null) bossBar.addPlayer(p); + } + bossBar.setVisible(true); + + broadcast("§c⚠ §lBoss " + (bossIndex + 1) + "/" + BOSSES.length + ": " + bossName + " §c§l erscheint!"); + bossIndex++; + } + + @EventHandler + public void onBossDeath(EntityDeathEvent e) { + if (currentEvent == null || currentBoss == null) return; + if (!e.getEntity().getUniqueId().equals(currentBoss.getUniqueId())) return; + + Player killer = e.getEntity().getKiller(); + if (killer != null && currentEvent.isParticipant(killer)) { + int bonus = bossIndex * 10; + currentEvent.addScore(killer.getUniqueId(), bonus); + broadcast("§a✔ §f" + killer.getName() + " §7hat den Boss bezwungen! §a+" + bonus + " §7Punkte"); + } + + if (bossBar != null) bossBar.setVisible(false); + broadcast("§c§l☠ Boss besiegt! §r§7Nächster Boss in 5 Sekunden..."); + + Bukkit.getScheduler().runTaskLater(plugin, this::spawnNextBoss, 100L); + } + + private void broadcast(String msg) { Bukkit.broadcastMessage(msg); } +} diff --git a/src/main/java/dev/viper/eventengine/events/builtin/DeathMatchHandler.java b/src/main/java/dev/viper/eventengine/events/builtin/DeathMatchHandler.java new file mode 100644 index 0000000..8e85d82 --- /dev/null +++ b/src/main/java/dev/viper/eventengine/events/builtin/DeathMatchHandler.java @@ -0,0 +1,99 @@ +package dev.viper.eventengine.events.builtin; + +import dev.viper.eventengine.EventEngine; +import dev.viper.eventengine.model.ActiveEvent; +import org.bukkit.Bukkit; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.PlayerDeathEvent; +import org.bukkit.potion.PotionEffect; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * PvP Deathmatch: + * - Kills = Punkte + * - Tod → Respawn nach 3s mit vollen HP + * - Am Ende: Spieler mit meisten Kills gewinnt + */ +public class DeathMatchHandler implements IEventHandler, Listener { + + private final EventEngine plugin; + private ActiveEvent currentEvent; + private final Map killCount = new HashMap<>(); + + public DeathMatchHandler(EventEngine plugin) { + this.plugin = plugin; + } + + @Override + public void onStart(ActiveEvent event) { + this.currentEvent = event; + killCount.clear(); + Bukkit.getPluginManager().registerEvents(this, plugin); + broadcast("§c⚔ §ePvP ist aktiv! Kills = Punkte. Viel Erfolg!"); + } + + @Override + public void onEnd(ActiveEvent event) { + HandlerList.unregisterAll(this); + this.currentEvent = null; + killCount.clear(); + + // Alle Teilnehmer: PvP-Effekte entfernen + for (UUID uuid : event.getParticipants()) { + Player p = Bukkit.getPlayer(uuid); + if (p != null) { + for (PotionEffect eff : p.getActivePotionEffects()) + p.removePotionEffect(eff.getType()); + p.setHealth(Math.min(p.getMaxHealth(), 20)); + } + } + } + + @EventHandler + public void onDeath(PlayerDeathEvent e) { + Player victim = e.getEntity(); + if (currentEvent == null || !currentEvent.isParticipant(victim)) return; + + Player killer = victim.getKiller(); + if (killer != null && currentEvent.isParticipant(killer)) { + // Kill → Punkt + currentEvent.addScore(killer.getUniqueId(), 1); + killCount.merge(killer.getUniqueId(), 1, Integer::sum); + int kills = killCount.getOrDefault(killer.getUniqueId(), 0); + killer.sendTitle("§c⚔ Kill!", "§7Kills: §e" + kills, 5, 30, 10); + killer.playSound(killer.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 1f, 1.5f); + } + + // Respawn nach 3 Sekunden + Location respawn = victim.getWorld().getSpawnLocation(); + e.setDeathMessage(null); + Bukkit.getScheduler().runTaskLater(plugin, () -> { + if (victim.isOnline()) { + victim.spigot().respawn(); + victim.teleport(respawn); + victim.setHealth(Math.min(victim.getMaxHealth(), 20)); + victim.sendMessage(plugin.prefix() + "§7Du bist respawnt — kämpfe weiter!"); + } + }, 60L); // 3s + } + + @Override + public void onPlayerJoin(ActiveEvent event, Player player) { + player.setHealth(Math.min(player.getMaxHealth(), 20)); + player.setFoodLevel(20); + broadcast("§a+ §f" + player.getName() + " §7nimmt am Deathmatch teil!"); + } + + private void broadcast(String msg) { + Bukkit.broadcastMessage(plugin.prefix() + msg); + } +} diff --git a/src/main/java/dev/viper/eventengine/events/builtin/DropPartyHandler.java b/src/main/java/dev/viper/eventengine/events/builtin/DropPartyHandler.java new file mode 100644 index 0000000..bba9dcb --- /dev/null +++ b/src/main/java/dev/viper/eventengine/events/builtin/DropPartyHandler.java @@ -0,0 +1,112 @@ +package dev.viper.eventengine.events.builtin; + +import dev.viper.eventengine.EventEngine; +import dev.viper.eventengine.model.ActiveEvent; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.inventory.ItemStack; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.Arrays; +import java.util.List; +import java.util.Random; +import java.util.UUID; + +/** + * Drop Party: + * - Items regnen alle 2s über allen Online-Spielern + * - Item-Pool: Common/Rare/Epic-Loot + * - Loot landet als Entity am Boden + */ +public class DropPartyHandler implements IEventHandler, Listener { + + // Loot-Tabelle: Material, Menge, Gewichtung + private static final Object[][] LOOT = { + // Common (viel) + {Material.BREAD, 8, 30}, + {Material.IRON_INGOT, 5, 25}, + {Material.COAL, 16, 20}, + {Material.ARROW, 32, 20}, + {Material.TORCH, 16, 20}, + // Uncommon + {Material.GOLD_INGOT, 5, 10}, + {Material.EXPERIENCE_BOTTLE, 3, 10}, + {Material.GOLDEN_APPLE, 2, 8}, + // Rare + {Material.DIAMOND, 3, 4}, + {Material.EMERALD, 2, 3}, + {Material.ENCHANTED_GOLDEN_APPLE, 1, 1}, + {Material.DIAMOND_SWORD, 1, 1}, + {Material.ELYTRA, 1, 1}, + }; + + private final EventEngine plugin; + private ActiveEvent currentEvent; + private BukkitRunnable dropper; + private final Random rng = new Random(); + + public DropPartyHandler(EventEngine plugin) { + this.plugin = plugin; + } + + @Override + public void onStart(ActiveEvent event) { + this.currentEvent = event; + Bukkit.getPluginManager().registerEvents(this, plugin); + broadcast("§6🎉 §eDropParty! §7Items regnen vom Himmel! Schnapp dir so viel du kannst!"); + + for (Player p : Bukkit.getOnlinePlayers()) + p.playSound(p.getLocation(), Sound.ENTITY_FIREWORK_ROCKET_LARGE_BLAST, 1f, 1f); + + dropper = new BukkitRunnable() { + @Override + public void run() { + if (currentEvent == null) { cancel(); return; } + dropItems(); + } + }; + dropper.runTaskTimer(plugin, 20L, 40L); // alle 2s + } + + @Override + public void onEnd(ActiveEvent event) { + HandlerList.unregisterAll(this); + if (dropper != null) dropper.cancel(); + this.currentEvent = null; + broadcast("§6🎉 §7Drop Party vorbei! Viel Spaß mit dem Loot!"); + } + + private void dropItems() { + for (Player p : Bukkit.getOnlinePlayers()) { + if (!p.isOnline()) continue; + // 1-3 Items pro Spieler pro Drop + int count = 1 + rng.nextInt(3); + for (int i = 0; i < count; i++) { + ItemStack item = rollLoot(); + Location dropLoc = p.getLocation().clone() + .add((rng.nextDouble() - 0.5) * 8, 8, (rng.nextDouble() - 0.5) * 8); + p.getWorld().dropItemNaturally(dropLoc, item); + } + } + } + + private ItemStack rollLoot() { + int totalWeight = Arrays.stream(LOOT).mapToInt(e -> (int) e[2]).sum(); + int roll = rng.nextInt(totalWeight); + int cumulative = 0; + for (Object[] entry : LOOT) { + cumulative += (int) entry[2]; + if (roll < cumulative) { + return new ItemStack((Material) entry[0], (int) entry[1]); + } + } + return new ItemStack(Material.BREAD); + } + + private void broadcast(String msg) { Bukkit.broadcastMessage(msg); } +} diff --git a/src/main/java/dev/viper/eventengine/events/builtin/EventHandlerRegistry.java b/src/main/java/dev/viper/eventengine/events/builtin/EventHandlerRegistry.java new file mode 100644 index 0000000..6a520d3 --- /dev/null +++ b/src/main/java/dev/viper/eventengine/events/builtin/EventHandlerRegistry.java @@ -0,0 +1,146 @@ +package dev.viper.eventengine.events.builtin; + +import dev.viper.eventengine.EventEngine; +import dev.viper.eventengine.model.EventType; + +import java.util.EnumMap; +import java.util.Map; +import java.util.Optional; + +public class EventHandlerRegistry { + + private final Map handlers = new EnumMap<>(EventType.class); + + public EventHandlerRegistry(EventEngine plugin) { + registerAll(plugin); + } + + private void registerAll(EventEngine plugin) { + + // ── KAMPF ────────────────────────────────────────────────────────── + register(EventType.PVP_DEATHMATCH, new DeathMatchHandler(plugin)); + register(EventType.PVP_FREE_FOR_ALL, new DeathMatchHandler(plugin)); + register(EventType.PVP_LAST_MAN_STANDING, new LastManStandingHandler(plugin)); + register(EventType.PVP_SUMO, new SumoHandler(plugin)); + register(EventType.PVP_SPLEEF, new SpleefHandler(plugin)); + register(EventType.PVP_SNOWBALL_FIGHT, new SnowballFightHandler(plugin)); + register(EventType.PVP_ONE_VS_ONE, new OneVsOneHandler(plugin)); + register(EventType.PVP_TEAM_BATTLE, new TeamBattleHandler(plugin)); + register(EventType.PVP_BOW_ONLY, new BowOnlyHandler(plugin)); + register(EventType.PVP_SWORD_ONLY, new SwordOnlyHandler(plugin)); + register(EventType.PVP_FIST_FIGHT, new FistFightHandler(plugin)); + register(EventType.PVP_KIT_PVP, new KitPvPHandler(plugin)); + register(EventType.PVP_KING_OF_THE_HILL, new KingOfTheHillHandler(plugin)); + register(EventType.PVP_HUNGER_GAMES, new HungerGamesHandler(plugin)); + register(EventType.PVP_CAPTURE_THE_FLAG, new CaptureTheFlagHandler(plugin)); + register(EventType.PVP_BEDWARS_LITE, new BedWarsLiteHandler(plugin)); + register(EventType.PVP_SKYWARS_LITE, new SkyWarsLiteHandler(plugin)); + register(EventType.PVP_UHC, new UHCHandler(plugin)); + register(EventType.PVP_ELYTRA_PVP, new ElytraPvPHandler(plugin)); + register(EventType.PVP_ARENA_SURVIVAL, new ArenaSurvivalHandler(plugin)); + + // ── ÜBERLEBEN ────────────────────────────────────────────────────── + register(EventType.SURVIVAL_MOB_WAVE, new MobWaveHandler(plugin)); + register(EventType.SURVIVAL_BOSS_RUSH, new BossRushHandler(plugin)); + register(EventType.SURVIVAL_LAVA_RISING, new LavaRisingHandler(plugin)); + register(EventType.SURVIVAL_ZOMBIE_SIEGE, new ZombieSiegeHandler(plugin)); + register(EventType.SURVIVAL_WITHER_STORM, new WitherStormHandler(plugin)); + register(EventType.SURVIVAL_DRAGON_FIGHT, new DragonFightHandler(plugin)); + register(EventType.SURVIVAL_HARDCORE_ROUND,new HardcoreRoundHandler(plugin)); + register(EventType.SURVIVAL_ISLAND, new IslandSurvivalHandler(plugin)); + register(EventType.SURVIVAL_NETHER_RUN, new NetherRunHandler(plugin)); + register(EventType.SURVIVAL_ENDURANCE, new EnduranceHandler(plugin)); + register(EventType.SURVIVAL_RAIDS_PLUS, new RaidsPlusHandler(plugin)); + register(EventType.SURVIVAL_MONSTER_HUNT, new MonsterHuntHandler(plugin)); + register(EventType.SURVIVAL_GRAVITY_SHIFT,new GravityShiftHandler(plugin)); + register(EventType.SURVIVAL_RANDOM_SPAWN, new RandomSpawnHandler(plugin)); + register(EventType.SURVIVAL_MANHUNT, new ManhuntHandler(plugin)); + + // ── BAUEN ────────────────────────────────────────────────────────── + register(EventType.BUILD_BATTLE, new BuildBattleHandler(plugin)); + register(EventType.BUILD_SPEED_BUILD, new SpeedBuildHandler(plugin)); + register(EventType.BUILD_THEME_CHALLENGE, new ThemeChallengeHandler(plugin)); + register(EventType.BUILD_PIXEL_ART, new PixelArtHandler(plugin)); + register(EventType.BUILD_TALLEST_TOWER, new TallestTowerHandler(plugin)); + register(EventType.BUILD_BRIDGE, new BridgeBuildHandler(plugin)); + register(EventType.BUILD_REDSTONE, new RedstoneChallengeHandler(plugin)); + register(EventType.BUILD_UNDERGROUND, new UndergroundBuildHandler(plugin)); + register(EventType.BUILD_SKYBLOCK_STYLE, new SkyblockStyleHandler(plugin)); + register(EventType.BUILD_BLINDBUILD, new BlindBuildHandler(plugin)); + + // ── RENNEN ───────────────────────────────────────────────────────── + register(EventType.RACE_PARKOUR, new ParkourRaceHandler(plugin)); + register(EventType.RACE_ELYTRA, new ElytraRaceHandler(plugin)); + register(EventType.RACE_BOAT, new BoatRaceHandler(plugin)); + register(EventType.RACE_HORSE, new HorseRaceHandler(plugin)); + register(EventType.RACE_PIG, new PigRaceHandler(plugin)); + register(EventType.RACE_MINECART, new MinecartRaceHandler(plugin)); + register(EventType.RACE_SWIMMING, new SwimmingRaceHandler(plugin)); + register(EventType.RACE_OBSTACLE, new ObstacleRaceHandler(plugin)); + register(EventType.RACE_MAZE, new MazeHandler(plugin)); + register(EventType.RACE_ICEBOAT, new IceBoatRaceHandler(plugin)); + + // ── SAMMELN ──────────────────────────────────────────────────────── + register(EventType.COLLECT_SCAVENGER_HUNT,new ScavengerHuntHandler(plugin)); + register(EventType.COLLECT_MINING_COMP, new MiningCompHandler(plugin)); + register(EventType.COLLECT_FISHING_COMP, new FishingCompHandler(plugin)); + register(EventType.COLLECT_FARMING_COMP, new FarmingCompHandler(plugin)); + register(EventType.COLLECT_MOB_DROPS, new MobDropsHandler(plugin)); + register(EventType.COLLECT_TREASURE_HUNT, new TreasureHuntHandler(plugin)); + register(EventType.COLLECT_EASTER_EGG, new EasterEggHandler(plugin)); + register(EventType.COLLECT_SPEED_MINE, new SpeedMineHandler(plugin)); + register(EventType.COLLECT_WOODCUTTING, new WoodcuttingHandler(plugin)); + register(EventType.COLLECT_ENCHANT_RACE, new EnchantRaceHandler(plugin)); + + // ── SPASS ────────────────────────────────────────────────────────── + register(EventType.FUN_DROP_PARTY, new DropPartyHandler(plugin)); + register(EventType.FUN_TNT_RAIN, new TntRainHandler(plugin)); + register(EventType.FUN_ITEM_RAIN, new ItemRainHandler(plugin)); + register(EventType.FUN_RANDOM_EFFECTS, new RandomEffectsHandler(plugin)); + register(EventType.FUN_FIREWORK_SHOW, new FireworkShowHandler(plugin)); + register(EventType.FUN_LIGHTNING_STORM, new LightningStormHandler(plugin)); + register(EventType.FUN_SPEED_BOOST, new SpeedBoostHandler(plugin)); + register(EventType.FUN_CHAOS_MODE, new ChaosModeHandler(plugin)); + register(EventType.FUN_TINY_PLAYERS, new TinyPlayersHandler(plugin)); + register(EventType.FUN_GIANT_PLAYERS, new GiantPlayersHandler(plugin)); + register(EventType.FUN_INVISIBLE_PLAYERS, new InvisiblePlayersHandler(plugin)); + register(EventType.FUN_RANDOM_TELEPORT, new RandomTeleportHandler(plugin)); + register(EventType.FUN_SWAP_INVENTORIES, new SwapInventoriesHandler(plugin)); + register(EventType.FUN_REVERSE_GRAVITY, new ReverseGravityHandler(plugin)); + register(EventType.FUN_BOUNCY_BLOCKS, new BouncyBlocksHandler(plugin)); + + // ── QUIZ ─────────────────────────────────────────────────────────── + register(EventType.QUIZ_MINECRAFT_TRIVIA, new TriviaHandler(plugin)); + register(EventType.QUIZ_SPEED_QUIZ, new TriviaHandler(plugin)); + register(EventType.QUIZ_GENERAL_KNOWLEDGE,new GeneralKnowledgeQuizHandler(plugin)); + register(EventType.QUIZ_CRAFTING_CHALLENGE,new CraftingChallengeHandler(plugin)); + register(EventType.QUIZ_MOB_QUIZ, new MobQuizHandler(plugin)); + + // ── WIRTSCHAFT ───────────────────────────────────────────────────── + register(EventType.ECONOMY_LOTTERY, new LotteryHandler(plugin)); + register(EventType.ECONOMY_AUCTION, new AuctionHandler(plugin)); + register(EventType.ECONOMY_MARKET_RUSH, new MarketRushHandler(plugin)); + register(EventType.ECONOMY_TRADE_FRENZY, new TradeFrenzyHandler(plugin)); + register(EventType.ECONOMY_BETTING, new BettingHandler(plugin)); + + // ── TEAM ─────────────────────────────────────────────────────────── + register(EventType.TEAM_RELAY_RACE, new TeamRelayRaceHandler(plugin)); + register(EventType.TEAM_BUILD_BATTLE, new TeamBuildBattleHandler(plugin)); + register(EventType.TEAM_SURVIVAL, new TeamSurvivalHandler(plugin)); + register(EventType.TEAM_TREASURE_HUNT, new TeamTreasureHuntHandler(plugin)); + register(EventType.TEAM_QUIZ, new TeamQuizHandler(plugin)); + register(EventType.TEAM_CAPTURE_POINTS, new CapturePointsHandler(plugin)); + register(EventType.TEAM_RESOURCE_RACE, new ResourceRaceHandler(plugin)); + register(EventType.TEAM_HIDE_AND_SEEK, new HideAndSeekHandler(plugin)); + register(EventType.TEAM_MURDER_MYSTERY, new MurderMysteryHandler(plugin)); + register(EventType.TEAM_COLOR_WAR, new ColorWarHandler(plugin)); + } + + public void register(EventType type, IEventHandler handler) { + handlers.put(type, handler); + } + + public Optional get(EventType type) { + return Optional.ofNullable(handlers.get(type)); + } +} diff --git a/src/main/java/dev/viper/eventengine/events/builtin/FireworkShowHandler.java b/src/main/java/dev/viper/eventengine/events/builtin/FireworkShowHandler.java new file mode 100644 index 0000000..e48d9c9 --- /dev/null +++ b/src/main/java/dev/viper/eventengine/events/builtin/FireworkShowHandler.java @@ -0,0 +1,75 @@ +package dev.viper.eventengine.events.builtin; + +import dev.viper.eventengine.EventEngine; +import dev.viper.eventengine.model.ActiveEvent; +import org.bukkit.*; +import org.bukkit.entity.Firework; +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.inventory.meta.FireworkMeta; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.Random; + +/** + * Feuerwerk-Show: Feuerwerke steigen über allen Spielern auf + */ +public class FireworkShowHandler implements IEventHandler, Listener { + + private final EventEngine plugin; + private ActiveEvent currentEvent; + private BukkitRunnable show; + private final Random rng = new Random(); + + private static final Color[] COLORS = { + Color.RED, Color.BLUE, Color.GREEN, Color.YELLOW, + Color.PURPLE, Color.AQUA, Color.ORANGE, Color.WHITE, + Color.FUCHSIA, Color.LIME + }; + + public FireworkShowHandler(EventEngine plugin) { this.plugin = plugin; } + + @Override + public void onStart(ActiveEvent event) { + this.currentEvent = event; + Bukkit.broadcastMessage(plugin.prefix() + "§e✨ §eFeuerwerk-Show! §7Lehnt euch zurück und genießt!"); + + show = new BukkitRunnable() { + @Override + public void run() { + if (currentEvent == null) { cancel(); return; } + for (Player p : Bukkit.getOnlinePlayers()) { + spawnFirework(p.getLocation().add( + (rng.nextDouble()-0.5)*10, 5, (rng.nextDouble()-0.5)*10)); + } + } + }; + show.runTaskTimer(plugin, 5L, 15L); + } + + @Override + public void onEnd(ActiveEvent event) { + HandlerList.unregisterAll(this); + if (show != null) show.cancel(); + this.currentEvent = null; + } + + private void spawnFirework(Location loc) { + Firework fw = loc.getWorld().spawn(loc, Firework.class); + FireworkMeta meta = fw.getFireworkMeta(); + meta.setPower(1 + rng.nextInt(2)); + + FireworkEffect.Type[] types = FireworkEffect.Type.values(); + FireworkEffect effect = FireworkEffect.builder() + .withColor(COLORS[rng.nextInt(COLORS.length)]) + .withFade(COLORS[rng.nextInt(COLORS.length)]) + .with(types[rng.nextInt(types.length)]) + .trail(rng.nextBoolean()) + .flicker(rng.nextBoolean()) + .build(); + + meta.addEffect(effect); + fw.setFireworkMeta(meta); + } +} diff --git a/src/main/java/dev/viper/eventengine/events/builtin/FunHandlers.java b/src/main/java/dev/viper/eventengine/events/builtin/FunHandlers.java new file mode 100644 index 0000000..10329b9 --- /dev/null +++ b/src/main/java/dev/viper/eventengine/events/builtin/FunHandlers.java @@ -0,0 +1,181 @@ +package dev.viper.eventengine.events.builtin; + +import dev.viper.eventengine.EventEngine; +import dev.viper.eventengine.model.ActiveEvent; +import org.bukkit.*; +import org.bukkit.entity.Player; +import org.bukkit.entity.TNTPrimed; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.Random; +import java.util.UUID; + +// ═══════════════════════════════════════════════════════════════════ +// TNT-Regen Handler +// ═══════════════════════════════════════════════════════════════════ +class TntRainHandler implements IEventHandler, Listener { + + private final EventEngine plugin; + private ActiveEvent currentEvent; + private BukkitRunnable rain; + private final Random rng = new Random(); + + TntRainHandler(EventEngine plugin) { this.plugin = plugin; } + + @Override + public void onStart(ActiveEvent event) { + this.currentEvent = event; + Bukkit.broadcastMessage(plugin.prefix() + "§c💣 §eTNT-Regen! §7Duck dich! (Spieler haben Blast Protection...)"); + + // Spielern Blast Protection geben (Rüstung) + for (UUID uuid : event.getParticipants()) { + Player p = Bukkit.getPlayer(uuid); + if (p != null) p.addPotionEffect(new PotionEffect(PotionEffectType.RESISTANCE, 99999, 1, false, false)); + } + + rain = new BukkitRunnable() { + @Override + public void run() { + if (currentEvent == null) { cancel(); return; } + for (Player p : Bukkit.getOnlinePlayers()) { + Location loc = p.getLocation().add( + (rng.nextDouble() - 0.5) * 20, 15, (rng.nextDouble() - 0.5) * 20); + TNTPrimed tnt = p.getWorld().spawn(loc, TNTPrimed.class); + tnt.setFuseTicks(40); + } + } + }; + rain.runTaskTimer(plugin, 20L, 30L); + } + + @Override + public void onEnd(ActiveEvent event) { + HandlerList.unregisterAll(this); + if (rain != null) rain.cancel(); + for (UUID uuid : event.getParticipants()) { + Player p = Bukkit.getPlayer(uuid); + if (p != null) p.removePotionEffect(PotionEffectType.RESISTANCE); + } + this.currentEvent = null; + } +} + +// ═══════════════════════════════════════════════════════════════════ +// Item-Regen Handler +// ═══════════════════════════════════════════════════════════════════ +class ItemRainHandler implements IEventHandler, Listener { + + private static final Material[] ITEMS = { + Material.DIAMOND, Material.GOLD_INGOT, Material.IRON_INGOT, + Material.EMERALD, Material.COAL, Material.BREAD, Material.ARROW, + Material.GOLDEN_APPLE, Material.EXPERIENCE_BOTTLE + }; + + private final EventEngine plugin; + private ActiveEvent currentEvent; + private BukkitRunnable rain; + private final Random rng = new Random(); + + ItemRainHandler(EventEngine plugin) { this.plugin = plugin; } + + @Override + public void onStart(ActiveEvent event) { + this.currentEvent = event; + Bukkit.broadcastMessage(plugin.prefix() + "§d🌧 §eItem-Regen! §7Items fallen vom Himmel!"); + + rain = new BukkitRunnable() { + @Override + public void run() { + if (currentEvent == null) { cancel(); return; } + for (Player p : Bukkit.getOnlinePlayers()) { + Material mat = ITEMS[rng.nextInt(ITEMS.length)]; + Location loc = p.getLocation().add( + (rng.nextDouble() - 0.5) * 12, 12, (rng.nextDouble() - 0.5) * 12); + p.getWorld().dropItemNaturally(loc, new ItemStack(mat, 1 + rng.nextInt(5))); + } + } + }; + rain.runTaskTimer(plugin, 10L, 15L); + } + + @Override + public void onEnd(ActiveEvent event) { + HandlerList.unregisterAll(this); + if (rain != null) rain.cancel(); + this.currentEvent = null; + } +} + +// ═══════════════════════════════════════════════════════════════════ +// Blitz-Sturm Handler +// ═══════════════════════════════════════════════════════════════════ +class LightningStormHandler implements IEventHandler, Listener { + + private final EventEngine plugin; + private ActiveEvent currentEvent; + private BukkitRunnable storm; + private final Random rng = new Random(); + + LightningStormHandler(EventEngine plugin) { this.plugin = plugin; } + + @Override + public void onStart(ActiveEvent event) { + this.currentEvent = event; + Bukkit.broadcastMessage(plugin.prefix() + "§e⚡ §eBlitz-Sturm! §7Es blitzt überall!"); + storm = new BukkitRunnable() { + @Override + public void run() { + if (currentEvent == null) { cancel(); return; } + for (Player p : Bukkit.getOnlinePlayers()) { + Location loc = p.getLocation().add( + (rng.nextDouble() - 0.5) * 16, 0, (rng.nextDouble() - 0.5) * 16); + p.getWorld().strikeLightningEffect(loc); + } + } + }; + storm.runTaskTimer(plugin, 10L, 20L); + } + + @Override + public void onEnd(ActiveEvent event) { + HandlerList.unregisterAll(this); + if (storm != null) storm.cancel(); + this.currentEvent = null; + } +} + +// ═══════════════════════════════════════════════════════════════════ +// Geschwindigkeits-Boost Handler +// ═══════════════════════════════════════════════════════════════════ +class SpeedBoostHandler implements IEventHandler, Listener { + + private final EventEngine plugin; + private ActiveEvent currentEvent; + + SpeedBoostHandler(EventEngine plugin) { this.plugin = plugin; } + + @Override + public void onStart(ActiveEvent event) { + this.currentEvent = event; + Bukkit.broadcastMessage(plugin.prefix() + "§a💨 §eSpeed Boost! §7Alle Spieler sind für kurze Zeit superschnell!"); + for (Player p : Bukkit.getOnlinePlayers()) { + p.addPotionEffect(new PotionEffect(PotionEffectType.SPEED, event.getDefinition().getDurationSeconds() * 20, 4, false, true)); + p.addPotionEffect(new PotionEffect(PotionEffectType.JUMP_BOOST, event.getDefinition().getDurationSeconds() * 20, 3, false, true)); + } + } + + @Override + public void onEnd(ActiveEvent event) { + HandlerList.unregisterAll(this); + for (Player p : Bukkit.getOnlinePlayers()) { + p.removePotionEffect(PotionEffectType.SPEED); + p.removePotionEffect(PotionEffectType.JUMP_BOOST); + } + this.currentEvent = null; + } +} diff --git a/src/main/java/dev/viper/eventengine/events/builtin/IEventHandler.java b/src/main/java/dev/viper/eventengine/events/builtin/IEventHandler.java new file mode 100644 index 0000000..0086c75 --- /dev/null +++ b/src/main/java/dev/viper/eventengine/events/builtin/IEventHandler.java @@ -0,0 +1,26 @@ +package dev.viper.eventengine.events.builtin; + +import dev.viper.eventengine.model.ActiveEvent; +import org.bukkit.entity.Player; + +/** + * Optionaler Handler für eingebaute Events mit echter Spiellogik. + * Plugins können über EventHandlerRegistry eigene Handler registrieren. + */ +public interface IEventHandler { + + /** Wird beim Start aufgerufen (nach Ankündigung, Sync Main-Thread) */ + void onStart(ActiveEvent event); + + /** Wird beim Ende aufgerufen */ + void onEnd(ActiveEvent event); + + /** Ein Spieler tritt bei */ + default void onPlayerJoin(ActiveEvent event, Player player) {} + + /** Ein Spieler verlässt das Event */ + default void onPlayerLeave(ActiveEvent event, Player player) {} + + /** Wird jede Sekunde aufgerufen (Tick) */ + default void onTick(ActiveEvent event) {} +} diff --git a/src/main/java/dev/viper/eventengine/events/builtin/LastManStandingHandler.java b/src/main/java/dev/viper/eventengine/events/builtin/LastManStandingHandler.java new file mode 100644 index 0000000..fa51221 --- /dev/null +++ b/src/main/java/dev/viper/eventengine/events/builtin/LastManStandingHandler.java @@ -0,0 +1,87 @@ +package dev.viper.eventengine.events.builtin; + +import dev.viper.eventengine.EventEngine; +import dev.viper.eventengine.model.ActiveEvent; +import org.bukkit.Bukkit; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.PlayerDeathEvent; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * Last Man Standing: + * - Kein Respawn — einmal tot = draußen + * - Letzter Überlebender gewinnt + */ +public class LastManStandingHandler implements IEventHandler, Listener { + + private final EventEngine plugin; + private ActiveEvent currentEvent; + private final Set alive = new HashSet<>(); + + public LastManStandingHandler(EventEngine plugin) { + this.plugin = plugin; + } + + @Override + public void onStart(ActiveEvent event) { + this.currentEvent = event; + alive.clear(); + alive.addAll(event.getParticipants()); + Bukkit.getPluginManager().registerEvents(this, plugin); + broadcast("§c☠ §eLast Man Standing! §7Einmal tot = §craus§7."); + } + + @Override + public void onEnd(ActiveEvent event) { + HandlerList.unregisterAll(this); + this.currentEvent = null; + alive.clear(); + } + + @EventHandler + public void onDeath(PlayerDeathEvent e) { + Player victim = e.getEntity(); + if (currentEvent == null || !currentEvent.isParticipant(victim)) return; + + alive.remove(victim.getUniqueId()); + currentEvent.removeParticipant(victim); + e.setDeathMessage(null); + + int remaining = alive.size(); + broadcast("§c☠ §f" + victim.getName() + " §7ist ausgeschieden! §8[§e" + remaining + " §7übrig§8]"); + + victim.sendMessage(plugin.prefix() + "§cDu bist ausgeschieden! Schau dir das Finale an."); + victim.setGameMode(org.bukkit.GameMode.SPECTATOR); + + // Noch 1 übrig → Gewinner + if (remaining == 1) { + UUID winnerId = alive.iterator().next(); + Player winner = Bukkit.getPlayer(winnerId); + String winName = winner != null ? winner.getName() : "Unbekannt"; + + currentEvent.addScore(winnerId, 100); + broadcast("§6✦ §e" + winName + " §6ist der letzte Überlebende! §6GEWONNEN!"); + if (winner != null) { + winner.playSound(winner.getLocation(), Sound.UI_TOAST_CHALLENGE_COMPLETE, 1f, 1f); + winner.sendTitle("§6GEWONNEN!", "§7Last Man Standing!", 10, 60, 20); + } + plugin.getEventManager().endEvent(winName + " hat gewonnen"); + } else if (remaining == 0) { + plugin.getEventManager().endEvent("Unentschieden"); + } + } + + @Override + public void onPlayerJoin(ActiveEvent event, Player player) { + alive.add(player.getUniqueId()); + } + + private void broadcast(String msg) { Bukkit.broadcastMessage(msg); } +} diff --git a/src/main/java/dev/viper/eventengine/events/builtin/LavaRisingHandler.java b/src/main/java/dev/viper/eventengine/events/builtin/LavaRisingHandler.java new file mode 100644 index 0000000..28b65e0 --- /dev/null +++ b/src/main/java/dev/viper/eventengine/events/builtin/LavaRisingHandler.java @@ -0,0 +1,105 @@ +package dev.viper.eventengine.events.builtin; + +import dev.viper.eventengine.EventEngine; +import dev.viper.eventengine.model.ActiveEvent; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.UUID; + +/** + * Steigendes Lava: + * - Lava-Level steigt alle 10s um 1 Block + * - Spieler müssen nach oben klettern + * - Wer in der Lava landet = ausgeschieden (Tod) + * - Höchster Überlebender gewinnt (Zeit überleben = Punkte) + */ +public class LavaRisingHandler implements IEventHandler, Listener { + + private final EventEngine plugin; + private ActiveEvent currentEvent; + private int lavaY = 30; + private BukkitRunnable ticker; + + public LavaRisingHandler(EventEngine plugin) { + this.plugin = plugin; + } + + @Override + public void onStart(ActiveEvent event) { + this.currentEvent = event; + + // Startlevel aus Spieler-Y + lavaY = getLowestPlayerY() - 5; + + Bukkit.getPluginManager().registerEvents(this, plugin); + broadcast("§c🔥 §eDie Lava steigt! §7Klettere nach oben und überlebe so lange wie möglich!"); + + ticker = new BukkitRunnable() { + int tick = 0; + @Override + public void run() { + if (currentEvent == null) { cancel(); return; } + tick++; + + // Punkte pro Sekunde für Überlebende + for (UUID uuid : currentEvent.getParticipants()) { + Player p = Bukkit.getPlayer(uuid); + if (p != null) currentEvent.addScore(uuid, 1); + } + + // Alle 10s: Lava steigt + if (tick % 10 == 0) { + lavaY++; + // Warnung wenn Lava nah + for (UUID uuid : currentEvent.getParticipants()) { + Player p = Bukkit.getPlayer(uuid); + if (p == null) continue; + double dist = p.getLocation().getY() - lavaY; + if (dist < 5) { + p.sendActionBar("§c🔥 LAVA §f" + (int)dist + " §cBlöcke unter dir!"); + p.playSound(p.getLocation(), Sound.BLOCK_LAVA_AMBIENT, 1f, 1.5f); + } + } + if (tick % 30 == 0) broadcast("§c🔥 §7Lava-Level: §cY=" + lavaY); + } + + // Check: Spieler in/unter Lava + for (UUID uuid : currentEvent.getParticipants()) { + Player p = Bukkit.getPlayer(uuid); + if (p != null && p.getLocation().getY() <= lavaY) { + p.setFireTicks(100); + p.damage(4.0); + p.sendActionBar("§c§l🔥 LAVA! Raus da!"); + } + } + } + }; + ticker.runTaskTimer(plugin, 20L, 20L); + } + + @Override + public void onEnd(ActiveEvent event) { + HandlerList.unregisterAll(this); + if (ticker != null) ticker.cancel(); + this.currentEvent = null; + } + + private int getLowestPlayerY() { + int lowest = 64; + if (currentEvent == null) return lowest; + for (UUID uuid : currentEvent.getParticipants()) { + Player p = Bukkit.getPlayer(uuid); + if (p != null) lowest = Math.min(lowest, p.getLocation().getBlockY()); + } + return lowest; + } + + private void broadcast(String msg) { Bukkit.broadcastMessage(msg); } +} diff --git a/src/main/java/dev/viper/eventengine/events/builtin/LotteryHandler.java b/src/main/java/dev/viper/eventengine/events/builtin/LotteryHandler.java new file mode 100644 index 0000000..a55731a --- /dev/null +++ b/src/main/java/dev/viper/eventengine/events/builtin/LotteryHandler.java @@ -0,0 +1,65 @@ +package dev.viper.eventengine.events.builtin; + +import dev.viper.eventengine.EventEngine; +import dev.viper.eventengine.model.ActiveEvent; +import org.bukkit.Bukkit; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; + +import java.util.*; + +public class LotteryHandler implements IEventHandler, Listener { + + private final EventEngine plugin; + private ActiveEvent currentEvent; + private final List pool = new ArrayList<>(); + + public LotteryHandler(EventEngine plugin) { this.plugin = plugin; } + + @Override + public void onStart(ActiveEvent event) { + this.currentEvent = event; + pool.clear(); + Bukkit.getPluginManager().registerEvents(this, plugin); + Bukkit.broadcastMessage(plugin.prefix() + "§6🎟 §eLotterie! §7Nimm teil mit §f/event join §7um ein Los zu erhalten!"); + Bukkit.broadcastMessage(plugin.prefix() + "§7Am Ende wird ein §6Gewinner §7gezogen. Viel Glück!"); + } + + @Override + public void onEnd(ActiveEvent event) { + HandlerList.unregisterAll(this); + pool.addAll(event.getParticipants()); + + if (pool.isEmpty()) { + Bukkit.broadcastMessage(plugin.prefix() + "§7Keine Teilnehmer — keine Lotterie."); + this.currentEvent = null; + return; + } + + UUID winnerId = pool.get(new Random().nextInt(pool.size())); + Player winner = Bukkit.getPlayer(winnerId); + String name = winner != null ? winner.getName() : "Offline-Spieler"; + + Bukkit.broadcastMessage("§8§m══════════════════════════════"); + Bukkit.broadcastMessage("§6 🎟 Lotterie-Ergebnis!"); + Bukkit.broadcastMessage("§7 Teilnehmer: §f" + new HashSet<>(pool).size()); + Bukkit.broadcastMessage("§6 🏆 GEWINNER: §e§l" + name + "§r §6🏆"); + Bukkit.broadcastMessage("§8§m══════════════════════════════"); + + if (winner != null) { + winner.playSound(winner.getLocation(), Sound.UI_TOAST_CHALLENGE_COMPLETE, 1f, 1f); + winner.sendTitle("§6GEWONNEN!", "§7Lotterie!", 10, 60, 20); + for (String cmd : event.getDefinition().getRewards()) + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), cmd.replace("%player%", winner.getName())); + } + this.currentEvent = null; + } + + @Override + public void onPlayerJoin(ActiveEvent event, Player player) { + pool.add(player.getUniqueId()); + player.sendMessage(plugin.prefix() + "§a🎟 Los erhalten! §7Du bist im Pool. Viel Glück!"); + } +} diff --git a/src/main/java/dev/viper/eventengine/events/builtin/MiscHandlers.java b/src/main/java/dev/viper/eventengine/events/builtin/MiscHandlers.java new file mode 100644 index 0000000..dd407ea --- /dev/null +++ b/src/main/java/dev/viper/eventengine/events/builtin/MiscHandlers.java @@ -0,0 +1,441 @@ +package dev.viper.eventengine.events.builtin; + +import dev.viper.eventengine.EventEngine; +import dev.viper.eventengine.model.ActiveEvent; +import org.bukkit.*; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.player.PlayerFishEvent; +import org.bukkit.event.player.PlayerHarvestBlockEvent; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.*; + +// ══════════════════════════════════════════════════════════ +// RENNEN — alle 10 +// ══════════════════════════════════════════════════════════ + +/** Basis für alle Renn-Events: Punkte pro Sekunde, wer am Ende mehr hat gewinnt */ +class BaseRaceHandler implements IEventHandler { + protected final EventEngine plugin; + protected final String icon; + protected final String name; + protected BukkitRunnable ticker; + + BaseRaceHandler(EventEngine p, String icon, String name) { this.plugin=p; this.icon=icon; this.name=name; } + + @Override public void onStart(ActiveEvent e) { + Bukkit.broadcastMessage(plugin.prefix()+icon+" §e"+name+"! §7Bereitet euch vor — die Strecke ist bereit!"); + ticker=new BukkitRunnable(){ @Override public void run(){ if(e.getState()==ActiveEvent.State.ENDED){cancel();return;} }}; + ticker.runTaskTimer(plugin,20L,20L); + } + @Override public void onEnd(ActiveEvent e){ if(ticker!=null)ticker.cancel(); } +} + +class ParkourRaceHandler extends BaseRaceHandler { + ParkourRaceHandler(EventEngine p){super(p,"🏃","Parkour Race");} + @Override public void onStart(ActiveEvent e){ super.onStart(e); Bukkit.broadcastMessage(plugin.prefix()+"§a🏃 §eParkour Race! §7Lauft durch den Kurs — wer zuerst am Ziel ist, gewinnt!"); } +} +class ElytraRaceHandler extends BaseRaceHandler { + ElytraRaceHandler(EventEngine p){super(p,"🪂","Elytra-Rennen");} + @Override public void onStart(ActiveEvent e){ + super.onStart(e); + for(UUID u:e.getParticipants()){Player p=Bukkit.getPlayer(u);if(p==null)continue;p.getEquipment().setChestplate(new ItemStack(Material.ELYTRA));p.getInventory().addItem(new ItemStack(Material.FIREWORK_ROCKET,64));} + Bukkit.broadcastMessage(plugin.prefix()+"§b🪂 §eElytra-Rennen! §7Fliegt mit Elytren zum Ziel!"); + } +} +class BoatRaceHandler extends BaseRaceHandler { + BoatRaceHandler(EventEngine p){super(p,"⛵","Boot-Rennen");} + @Override public void onStart(ActiveEvent e){ super.onStart(e); for(UUID u:e.getParticipants()){Player p=Bukkit.getPlayer(u);if(p==null)continue;p.getInventory().addItem(new ItemStack(Material.OAK_BOAT));} Bukkit.broadcastMessage(plugin.prefix()+"§b⛵ §eBoot-Rennen! §7Klettert in euer Boot und rast zum Ziel!"); } +} +class HorseRaceHandler extends BaseRaceHandler { + HorseRaceHandler(EventEngine p){super(p,"🐎","Pferde-Rennen");} + @Override public void onStart(ActiveEvent e){ super.onStart(e); Bukkit.broadcastMessage(plugin.prefix()+"§6🐎 §ePferde-Rennen! §7Besteigt eure Pferde — Zügel in die Hand und auf zum Start!"); } +} +class PigRaceHandler extends BaseRaceHandler { + PigRaceHandler(EventEngine p){super(p,"🐷","Schweine-Rennen");} + @Override public void onStart(ActiveEvent e){ super.onStart(e); for(UUID u:e.getParticipants()){Player p=Bukkit.getPlayer(u);if(p==null)continue;p.getInventory().addItem(new ItemStack(Material.CARROT_ON_A_STICK));} Bukkit.broadcastMessage(plugin.prefix()+"§e🐷 §eSchweine-Rennen! §7Schnappt euch eure Karotten-Angel und reitet eure Schweine!"); } +} +class MinecartRaceHandler extends BaseRaceHandler { + MinecartRaceHandler(EventEngine p){super(p,"🚃","Lore-Rennen");} + @Override public void onStart(ActiveEvent e){ super.onStart(e); Bukkit.broadcastMessage(plugin.prefix()+"§7🚃 §eLore-Rennen! §7Setzt euch in eure Lore und rast die Schienen entlang!"); } +} +class SwimmingRaceHandler extends BaseRaceHandler { + SwimmingRaceHandler(EventEngine p){super(p,"🏊","Schwimm-Rennen");} + @Override public void onStart(ActiveEvent e){ super.onStart(e); for(UUID u:e.getParticipants()){Player p=Bukkit.getPlayer(u);if(p==null)continue;p.addPotionEffect(new PotionEffect(PotionEffectType.DOLPHINS_GRACE,e.getDefinition().getDurationSeconds()*20,0));} Bukkit.broadcastMessage(plugin.prefix()+"§9🏊 §eSchwimm-Rennen! §7Los — durch den Kanal zum Ziel!"); } +} +class ObstacleRaceHandler extends BaseRaceHandler { + ObstacleRaceHandler(EventEngine p){super(p,"🏅","Hindernislauf");} + @Override public void onStart(ActiveEvent e){ super.onStart(e); Bukkit.broadcastMessage(plugin.prefix()+"§e🏅 §eHindernislauf! §7Überwindet alle Hindernisse und erreicht das Ziel!"); } +} +class MazeHandler extends BaseRaceHandler { + MazeHandler(EventEngine p){super(p,"🌀","Labyrinth");} + @Override public void onStart(ActiveEvent e){ super.onStart(e); Bukkit.broadcastMessage(plugin.prefix()+"§d🌀 §eLabyrinth! §7Findet den Weg durch das Labyrinth — wer zuerst herauskommt gewinnt!"); } +} +class IceBoatRaceHandler extends BaseRaceHandler { + IceBoatRaceHandler(EventEngine p){super(p,"❄","Eisboot-Rennen");} + @Override public void onStart(ActiveEvent e){ super.onStart(e); for(UUID u:e.getParticipants()){Player p=Bukkit.getPlayer(u);if(p==null)continue;p.getInventory().addItem(new ItemStack(Material.OAK_BOAT));} Bukkit.broadcastMessage(plugin.prefix()+"§b❄ §eEisboot-Rennen! §7Rast auf dem Eis — Boote auf Blaueis sind blitzschnell!"); } +} + +// ══════════════════════════════════════════════════════════ +// SAMMEL EVENTS — alle 10 +// ══════════════════════════════════════════════════════════ + +class ScavengerHuntHandler implements IEventHandler { + private final EventEngine plugin; + ScavengerHuntHandler(EventEngine p){this.plugin=p;} + @Override public void onStart(ActiveEvent e){ Bukkit.broadcastMessage(plugin.prefix()+"§6🔍 §eSchatzsuche! §7Findet alle versteckten Items in der Arena. Hinweise folgen im Chat!"); scheduleHints(e); } + @Override public void onEnd(ActiveEvent e){} + private void scheduleHints(ActiveEvent e){ + String[] hints={"§7Tipp 1: §fSchau in der Nähe von Bäumen!","§7Tipp 2: §fUnter der Erde könnte etwas versteckt sein...","§7Tipp 3: §fDas Wasser verbirgt Geheimnisse!"}; + for(int i=0;i ORE_SCORES=new EnumMap<>(Material.class); + static{ORE_SCORES.put(Material.COAL_ORE,1);ORE_SCORES.put(Material.DEEPSLATE_COAL_ORE,1);ORE_SCORES.put(Material.IRON_ORE,2);ORE_SCORES.put(Material.DEEPSLATE_IRON_ORE,2);ORE_SCORES.put(Material.GOLD_ORE,4);ORE_SCORES.put(Material.DEEPSLATE_GOLD_ORE,4);ORE_SCORES.put(Material.DIAMOND_ORE,10);ORE_SCORES.put(Material.DEEPSLATE_DIAMOND_ORE,10);ORE_SCORES.put(Material.EMERALD_ORE,8);ORE_SCORES.put(Material.ANCIENT_DEBRIS,20);} + MiningCompHandler(EventEngine p){this.plugin=p;} + @Override public void onStart(ActiveEvent e){current=e;Bukkit.getPluginManager().registerEvents(this,plugin);Bukkit.broadcastMessage(plugin.prefix()+"§7⛏ §eMining-Wettbewerb! §7Baut so viele Erze wie möglich ab. Diamanten = 10 Punkte!");} + @Override public void onEnd(ActiveEvent e){HandlerList.unregisterAll(this);current=null;} + @EventHandler public void onBreak(BlockBreakEvent e){if(current==null||!current.isParticipant(e.getPlayer()))return;Integer pts=ORE_SCORES.get(e.getBlock().getType());if(pts!=null){current.addScore(e.getPlayer().getUniqueId(),pts);e.getPlayer().sendActionBar("§7+"+pts+" §7["+e.getBlock().getType().name()+"]");}} +} + +class FishingCompHandler implements IEventHandler, Listener { + private final EventEngine plugin; private ActiveEvent current; + FishingCompHandler(EventEngine p){this.plugin=p;} + @Override public void onStart(ActiveEvent e){current=e;Bukkit.getPluginManager().registerEvents(this,plugin);for(UUID u:e.getParticipants()){Player p=Bukkit.getPlayer(u);if(p==null)continue;p.getInventory().addItem(new ItemStack(Material.FISHING_ROD));}Bukkit.broadcastMessage(plugin.prefix()+"§9🎣 §eAngel-Wettbewerb! §7Angelt so viele Fische wie möglich. Schatz-Funde geben Bonuspunkte!");} + @Override public void onEnd(ActiveEvent e){HandlerList.unregisterAll(this);current=null;} + @EventHandler public void onFish(PlayerFishEvent e){ + if(current==null||!current.isParticipant(e.getPlayer()))return; + if(e.getState()!=PlayerFishEvent.State.CAUGHT_FISH&&e.getState()!=PlayerFishEvent.State.CAUGHT_ENTITY)return; + int pts=e.getState()==PlayerFishEvent.State.CAUGHT_ENTITY?5:2; + current.addScore(e.getPlayer().getUniqueId(),pts); + e.getPlayer().sendActionBar("§9+"+pts+" §7Angelpunkte!"); + } +} + +class FarmingCompHandler implements IEventHandler, Listener { + private final EventEngine plugin; private ActiveEvent current; + private static final Map CROP_SCORES=new EnumMap<>(Material.class); + static{CROP_SCORES.put(Material.WHEAT,1);CROP_SCORES.put(Material.CARROT,1);CROP_SCORES.put(Material.POTATO,1);CROP_SCORES.put(Material.BEETROOT,1);CROP_SCORES.put(Material.MELON,2);CROP_SCORES.put(Material.PUMPKIN,2);CROP_SCORES.put(Material.SWEET_BERRIES,1);CROP_SCORES.put(Material.COCOA_BEANS,1);} + FarmingCompHandler(EventEngine p){this.plugin=p;} + @Override public void onStart(ActiveEvent e){current=e;Bukkit.getPluginManager().registerEvents(this,plugin);Bukkit.broadcastMessage(plugin.prefix()+"§a🌾 §eFarm-Wettbewerb! §7Erntet so viele Crops wie möglich!");} + @Override public void onEnd(ActiveEvent e){HandlerList.unregisterAll(this);current=null;} + @EventHandler public void onHarvest(PlayerHarvestBlockEvent e){if(current==null||!current.isParticipant(e.getPlayer()))return;for(ItemStack item:e.getItemsHarvested()){Integer pts=CROP_SCORES.get(item.getType());if(pts!=null){current.addScore(e.getPlayer().getUniqueId(),pts*item.getAmount());e.getPlayer().sendActionBar("§a+"+pts+" §7pro Ernte");break;}}} +} + +class MobDropsHandler implements IEventHandler, Listener { + private final EventEngine plugin; private ActiveEvent current; + private static final Map MOB_SCORES=new EnumMap<>(org.bukkit.entity.EntityType.class); + static{MOB_SCORES.put(org.bukkit.entity.EntityType.ZOMBIE,1);MOB_SCORES.put(org.bukkit.entity.EntityType.SKELETON,1);MOB_SCORES.put(org.bukkit.entity.EntityType.SPIDER,1);MOB_SCORES.put(org.bukkit.entity.EntityType.CREEPER,2);MOB_SCORES.put(org.bukkit.entity.EntityType.ENDERMAN,5);MOB_SCORES.put(org.bukkit.entity.EntityType.BLAZE,4);MOB_SCORES.put(org.bukkit.entity.EntityType.WITCH,3);MOB_SCORES.put(org.bukkit.entity.EntityType.WITHER_SKELETON,8);} + MobDropsHandler(EventEngine p){this.plugin=p;} + @Override public void onStart(ActiveEvent e){current=e;Bukkit.getPluginManager().registerEvents(this,plugin);Bukkit.broadcastMessage(plugin.prefix()+"§c🗡 §eMob-Drops! §7Töte Mobs und sammle ihre Drops. Seltene Mobs = mehr Punkte!");} + @Override public void onEnd(ActiveEvent e){HandlerList.unregisterAll(this);current=null;} + @EventHandler public void onKill(EntityDeathEvent e){if(current==null)return;Player k=e.getEntity().getKiller();if(k==null||!current.isParticipant(k))return;Integer pts=MOB_SCORES.get(e.getEntityType());if(pts!=null){current.addScore(k.getUniqueId(),pts);k.sendActionBar("§c+"+pts+" §7Punkte");}} +} + +class TreasureHuntHandler implements IEventHandler { + private final EventEngine plugin; + TreasureHuntHandler(EventEngine p){this.plugin=p;} + @Override public void onStart(ActiveEvent e){Bukkit.broadcastMessage(plugin.prefix()+"§6💎 §eSchatz finden! §7Ein versteckter Schatz liegt irgendwo in der Arena. Findet ihn zuerst und gewinnt alles darin!");scheduleHints(e);} + @Override public void onEnd(ActiveEvent e){} + private void scheduleHints(ActiveEvent e){new BukkitRunnable(){@Override public void run(){if(e.getState()!=ActiveEvent.State.ENDED)Bukkit.broadcastMessage(plugin.prefix()+"§7💡 §eTipp: §fDer Schatz liegt in der Nähe von Wasser!");}}.runTaskLater(plugin,60*20L);} +} + +class EasterEggHandler implements IEventHandler, Listener { + private final EventEngine plugin; private ActiveEvent current; + EasterEggHandler(EventEngine p){this.plugin=p;} + @Override public void onStart(ActiveEvent e){current=e;Bukkit.getPluginManager().registerEvents(this,plugin);Bukkit.broadcastMessage(plugin.prefix()+"§d🥚 §eEier suchen! §7Findet alle versteckten Eier in der Arena. Wer die meisten hat, gewinnt!");} + @Override public void onEnd(ActiveEvent e){HandlerList.unregisterAll(this);current=null;} +} + +class SpeedMineHandler implements IEventHandler, Listener { + private final EventEngine plugin; private ActiveEvent current; + SpeedMineHandler(EventEngine p){this.plugin=p;} + @Override public void onStart(ActiveEvent e){ + current=e;Bukkit.getPluginManager().registerEvents(this,plugin); + for(UUID u:e.getParticipants()){Player p=Bukkit.getPlayer(u);if(p==null)continue;p.addPotionEffect(new PotionEffect(PotionEffectType.HASTE,e.getDefinition().getDurationSeconds()*20,1));} + Bukkit.broadcastMessage(plugin.prefix()+"§7⛏ §eSpeed Mining! §7Baut so schnell wie möglich! Alle erhalten Eile II!"); + } + @Override public void onEnd(ActiveEvent e){HandlerList.unregisterAll(this);for(UUID u:e.getParticipants()){Player p=Bukkit.getPlayer(u);if(p!=null)p.removePotionEffect(PotionEffectType.HASTE);}current=null;} + @EventHandler public void onBreak(BlockBreakEvent e){if(current==null||!current.isParticipant(e.getPlayer()))return;current.addScore(e.getPlayer().getUniqueId(),1);} +} + +class WoodcuttingHandler implements IEventHandler, Listener { + private final EventEngine plugin; private ActiveEvent current; + private static final Map WOOD_SCORES=new EnumMap<>(Material.class); + static{WOOD_SCORES.put(Material.OAK_LOG,1);WOOD_SCORES.put(Material.BIRCH_LOG,1);WOOD_SCORES.put(Material.SPRUCE_LOG,1);WOOD_SCORES.put(Material.JUNGLE_LOG,2);WOOD_SCORES.put(Material.ACACIA_LOG,2);WOOD_SCORES.put(Material.DARK_OAK_LOG,3);WOOD_SCORES.put(Material.MANGROVE_LOG,2);WOOD_SCORES.put(Material.CHERRY_LOG,3);} + WoodcuttingHandler(EventEngine p){this.plugin=p;} + @Override public void onStart(ActiveEvent e){current=e;Bukkit.getPluginManager().registerEvents(this,plugin);Bukkit.broadcastMessage(plugin.prefix()+"§2🪓 §eHolzfäll-Comp! §7Fällt so viele Bäume wie möglich. Dunkles Holz = mehr Punkte!");} + @Override public void onEnd(ActiveEvent e){HandlerList.unregisterAll(this);current=null;} + @EventHandler public void onBreak(BlockBreakEvent e){if(current==null||!current.isParticipant(e.getPlayer()))return;Integer pts=WOOD_SCORES.get(e.getBlock().getType());if(pts!=null){current.addScore(e.getPlayer().getUniqueId(),pts);}} +} + +class EnchantRaceHandler implements IEventHandler { + private final EventEngine plugin; + EnchantRaceHandler(EventEngine p){this.plugin=p;} + @Override public void onStart(ActiveEvent e){Bukkit.broadcastMessage(plugin.prefix()+"§5✨ §eVerzauberungsrennen! §7Sammelt Erfahrung und verzaubert euer Item zuerst auf Stufe 30! Wer zuerst fertig ist, gewinnt!");} + @Override public void onEnd(ActiveEvent e){} +} + +// ══════════════════════════════════════════════════════════ +// BUILD EVENTS — alle 10 +// ══════════════════════════════════════════════════════════ + +class BuildBattleHandler implements IEventHandler { + protected final EventEngine p; private final String theme; + BuildBattleHandler(EventEngine p){this.p=p;this.theme=null;} + BuildBattleHandler(EventEngine p,String theme){this.p=p;this.theme=theme;} + @Override public void onStart(ActiveEvent e){ + String t=theme!=null?theme:pickTheme(); + Bukkit.broadcastMessage(p.prefix()+"§a🏗 §eBuild Battle! §7Thema: §6§l"+t); + Bukkit.broadcastMessage(p.prefix()+"§7Ihr habt §e"+e.getDefinition().getDurationSeconds()/60+" Minuten §7- Los geht's!"); + } + @Override public void onEnd(ActiveEvent e){Bukkit.broadcastMessage(p.prefix()+"§7✦ §eBauzeit vorbei! §7Alle Bauten werden jetzt bewertet.");} + private String pickTheme(){String[]themes={"Burg","Unterwasserwelt","Vulkan","Raumstation","Verzauberte Waldlichtung","Drachen-Nest","Piratenschiff","Zukunftsstadt","Dschungeltempel","Eispalast"};return themes[new Random().nextInt(themes.length)];} +} +class SpeedBuildHandler extends BuildBattleHandler { SpeedBuildHandler(EventEngine p){super(p);} @Override public void onStart(ActiveEvent e){Bukkit.broadcastMessage(p.prefix()+"§e⚡ §eSpeed Build! §7Nur 2 Minuten! Thema kommt gleich..."); super.onStart(e);} } +class ThemeChallengeHandler extends BuildBattleHandler { ThemeChallengeHandler(EventEngine p){super(p);} } +class PixelArtHandler implements IEventHandler { private final EventEngine p; PixelArtHandler(EventEngine pp){this.p=pp;} @Override public void onStart(ActiveEvent e){Bukkit.broadcastMessage(p.prefix()+"§d🎨 §ePixel Art Contest! §7Malt ein Pixel-Art-Bild auf dem flachen Boden. Motiv: §6Creeper-Gesicht");} @Override public void onEnd(ActiveEvent e){Bukkit.broadcastMessage(p.prefix()+"§7Pinsel runter! Alle Bilder werden bewertet.");} } +class TallestTowerHandler implements IEventHandler, Listener { private final EventEngine plugin; private ActiveEvent current; TallestTowerHandler(EventEngine p){this.plugin=p;} @Override public void onStart(ActiveEvent e){current=e;Bukkit.getPluginManager().registerEvents(this,plugin);Bukkit.broadcastMessage(plugin.prefix()+"§e🗼 §eHöchster Turm! §7Baut so hoch wie möglich! Am Ende wird die Höhe gemessen.");} @Override public void onEnd(ActiveEvent e){HandlerList.unregisterAll(this);if(current==null)return;int best=0;UUID winner=null;for(UUID u:e.getParticipants()){Player p=Bukkit.getPlayer(u);if(p==null)continue;int y=p.getLocation().getBlockY();if(y>best){best=y;winner=u;}}if(winner!=null){e.addScore(winner,100);Player wp=Bukkit.getPlayer(winner);Bukkit.broadcastMessage(plugin.prefix()+"§6✦ §7Höchster Punkt: §e"+(wp!=null?wp.getName():"?")+" §7bei Y=§e"+best);}current=null;} } +class BridgeBuildHandler implements IEventHandler { private final EventEngine p; BridgeBuildHandler(EventEngine pp){this.p=pp;} @Override public void onStart(ActiveEvent e){Bukkit.broadcastMessage(p.prefix()+"§7🌉 §eBrücke bauen! §7Baut eine Brücke von Punkt A zu Punkt B. Schnellste und schönste Brücke gewinnt!");} @Override public void onEnd(ActiveEvent e){Bukkit.broadcastMessage(p.prefix()+"§7Bauzeit vorbei! §eDie Brücken werden jetzt bewertet.");} } +class RedstoneChallengeHandler implements IEventHandler { private final EventEngine p; RedstoneChallengeHandler(EventEngine pp){this.p=pp;} @Override public void onStart(ActiveEvent e){Bukkit.broadcastMessage(p.prefix()+"§c⚡ §eRedstone Challenge! §7Aufgabe: §6Baut eine automatisch öffnende Tür mit Druckplatten!");} @Override public void onEnd(ActiveEvent e){Bukkit.broadcastMessage(p.prefix()+"§7Zeit abgelaufen! §eSchaltkreise werden geprüft.");} } +class UndergroundBuildHandler implements IEventHandler { private final EventEngine p; UndergroundBuildHandler(EventEngine pp){this.p=pp;} @Override public void onStart(ActiveEvent e){Bukkit.broadcastMessage(p.prefix()+"§8⛏ §eUnterirdisch bauen! §7Grabt euch ein und baut eure Anlage NUR unterhalb der Erde!");} @Override public void onEnd(ActiveEvent e){Bukkit.broadcastMessage(p.prefix()+"§7Bauten werden begutachtet — die kreativste unterirdische Anlage gewinnt!");} } +class SkyblockStyleHandler implements IEventHandler { private final EventEngine p; SkyblockStyleHandler(EventEngine pp){this.p=pp;} @Override public void onStart(ActiveEvent e){Bukkit.broadcastMessage(p.prefix()+"§e☁ §eSkyblock Style! §7Ihr startet auf eurer Insel — baut sie so weit wie möglich aus! Kreativste Insel am Ende gewinnt.");} @Override public void onEnd(ActiveEvent e){Bukkit.broadcastMessage(p.prefix()+"§7Inselentwicklung vorbei — Bewertung beginnt!");} } +class BlindBuildHandler implements IEventHandler, Listener { private final EventEngine plugin; private ActiveEvent current; BlindBuildHandler(EventEngine p){this.plugin=p;} @Override public void onStart(ActiveEvent e){current=e;Bukkit.getPluginManager().registerEvents(this,plugin);for(UUID u:e.getParticipants()){Player p=Bukkit.getPlayer(u);if(p==null)continue;p.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS,e.getDefinition().getDurationSeconds()*20,0));}Bukkit.broadcastMessage(plugin.prefix()+"§8👁 §eBlind bauen! §7Ihr könnt kaum etwas sehen — baut das Thema: §6Haus");} @Override public void onEnd(ActiveEvent e){HandlerList.unregisterAll(this);for(UUID u:e.getParticipants()){Player p=Bukkit.getPlayer(u);if(p!=null)p.removePotionEffect(PotionEffectType.BLINDNESS);}Bukkit.broadcastMessage(plugin.prefix()+"§7Blindheit weg! §eSchaut euch gegenseitig eure Werke an 😂");current=null;} } + +// ══════════════════════════════════════════════════════════ +// QUIZ — 3 fehlende +// ══════════════════════════════════════════════════════════ + +class GeneralKnowledgeQuizHandler extends TriviaHandler { + private static final List GENERAL_QUESTIONS=List.of( + new dev.viper.eventengine.events.builtin.TriviaHandler.QuestionEntry("Was ist die Hauptstadt von Deutschland?","Berlin",10), + new dev.viper.eventengine.events.builtin.TriviaHandler.QuestionEntry("Wie viele Kontinente gibt es?","7",10), + new dev.viper.eventengine.events.builtin.TriviaHandler.QuestionEntry("Welches ist das größte Land der Welt?","Russland",10), + new dev.viper.eventengine.events.builtin.TriviaHandler.QuestionEntry("Wie viele Planeten hat unser Sonnensystem?","8",15), + new dev.viper.eventengine.events.builtin.TriviaHandler.QuestionEntry("Was ist H2O?","Wasser",5) + ); + GeneralKnowledgeQuizHandler(EventEngine p){super(p,GENERAL_QUESTIONS);} +} + +class CraftingChallengeHandler implements IEventHandler, Listener { + private final EventEngine plugin; private ActiveEvent current; + private static final String[][] CHALLENGES={ + {"Goldener Apfel","Crafting: 8 Goldbarren + 1 Apfel in der Mitte"}, + {"Verzauberungstisch","Crafting: Buch, Diamant x2, Obsidian x4"}, + {"Enderauge","Crafting: Enderpearl + Blaze Powder"}, + {"TNT","Crafting: 5x Sand + 4x Schwarzpulver (Schachbrett)"} + }; + CraftingChallengeHandler(EventEngine p){this.plugin=p;} + @Override public void onStart(ActiveEvent e){ + current=e;Bukkit.getPluginManager().registerEvents(this,plugin); + String[]challenge=CHALLENGES[new Random().nextInt(CHALLENGES.length)]; + Bukkit.broadcastMessage(plugin.prefix()+"§6🔨 §eCrafting-Challenge! §7Craftet als Erstes: §e"+challenge[0]); + Bukkit.broadcastMessage(plugin.prefix()+"§7Tipp: §f"+challenge[1]); + } + @Override public void onEnd(ActiveEvent e){HandlerList.unregisterAll(this);current=null;} +} + +class MobQuizHandler implements IEventHandler, Listener { + private final EventEngine plugin; private ActiveEvent current; + private final List questions=Arrays.asList( + new String[]{"Ich lebe im Nether, fliege und schieße Feuerbälle. Wer bin ich?","Ghast"}, + new String[]{"Ich bin grün, habe keine Arme und explodiere. Wer bin ich?","Creeper"}, + new String[]{"Ich bin sehr groß, lila und droppe Enderpearls. Wer bin ich?","Enderman"}, + new String[]{"Ich schwimme, lebe im Meer und bin der seltenste Boss. Wer bin ich?","Elder Guardian"}, + new String[]{"Ich werfe Tränke und trage immer einen Hut. Wer bin ich?","Witch"} + ); + private int idx=0; private boolean answered=false; + MobQuizHandler(EventEngine p){this.plugin=p;} + @Override public void onStart(ActiveEvent e){ + current=e;idx=0;answered=false;Bukkit.getPluginManager().registerEvents(this,plugin); + Bukkit.broadcastMessage(plugin.prefix()+"§2🐾 §eMob-Quiz! §7Erkenne den Mob anhand der Beschreibung!"); + nextQuestion(e); + } + @Override public void onEnd(ActiveEvent e){HandlerList.unregisterAll(this);current=null;} + private void nextQuestion(ActiveEvent e){if(idx>=questions.size()){plugin.getEventManager().endEvent("Alle Fragen beantwortet");return;}answered=false;String[]q=questions.get(idx);Bukkit.broadcastMessage("§2❓ §f"+q[0]);} + @EventHandler public void onChat(org.bukkit.event.player.AsyncPlayerChatEvent e){ + if(current==null||answered) return; + Player p=e.getPlayer();if(!current.isParticipant(p))return; + String[]q=questions.get(idx); + if(e.getMessage().trim().equalsIgnoreCase(q[1])){answered=true;current.addScore(p.getUniqueId(),10);Bukkit.broadcastMessage("§a✔ §f"+p.getName()+" §7hat es gewusst! §a+10 Punkte");idx++;Bukkit.getScheduler().runTaskLater(plugin,()->nextQuestion(current),40L);} + } +} + +// ══════════════════════════════════════════════════════════ +// ECONOMY — 4 fehlende +// ══════════════════════════════════════════════════════════ + +class AuctionHandler implements IEventHandler { private final EventEngine p; AuctionHandler(EventEngine pp){this.p=pp;} @Override public void onStart(ActiveEvent e){Bukkit.broadcastMessage(p.prefix()+"§6🔨 §eAuktion! §7Ein Item wird gleich versteigert. Biete im Chat mit einem Betrag! Höchstes Gebot gewinnt!");} @Override public void onEnd(ActiveEvent e){Bukkit.broadcastMessage(p.prefix()+"§7Auktion beendet! §eDer Gewinner erhält sein Item.");} } +class MarketRushHandler implements IEventHandler { private final EventEngine p; MarketRushHandler(EventEngine pp){this.p=pp;} @Override public void onStart(ActiveEvent e){Bukkit.broadcastMessage(p.prefix()+"§e💹 §eMarkt-Rush! §7Für die nächsten §c"+e.getDefinition().getDurationSeconds()/60+" Minuten §7werden §6Eisenbarren §7zum 3-fachen Preis eingekauft! Sammelt so viel wie möglich!");} @Override public void onEnd(ActiveEvent e){Bukkit.broadcastMessage(p.prefix()+"§7Markt-Rush vorbei! §7Der Sonderpreis ist nicht mehr aktiv.");} } +class TradeFrenzyHandler implements IEventHandler { private final EventEngine p; TradeFrenzyHandler(EventEngine pp){this.p=pp;} @Override public void onStart(ActiveEvent e){Bukkit.broadcastMessage(p.prefix()+"§a🤝 §eHandels-Frenzy! §7Handelt so viel wie möglich mit anderen Spielern! Jeder abgeschlossene Handel gibt Bonus-Punkte.");} @Override public void onEnd(ActiveEvent e){Bukkit.broadcastMessage(p.prefix()+"§7Handels-Zeit vorbei!");} } +class BettingHandler implements IEventHandler { private final EventEngine p; BettingHandler(EventEngine pp){this.p=pp;} @Override public void onStart(ActiveEvent e){Bukkit.broadcastMessage(p.prefix()+"§6🎲 §eWetten-Event! §7Setzt auf den Gewinner des nächsten Events! Nutzt die Wetten-Befehle um mitzumachen.");} @Override public void onEnd(ActiveEvent e){Bukkit.broadcastMessage(p.prefix()+"§7Wetten sind geschlossen — Gewinner wird ausgezahlt!");} } + +// ══════════════════════════════════════════════════════════ +// TEAM EVENTS — alle 10 +// ══════════════════════════════════════════════════════════ + +class TeamRelayRaceHandler implements IEventHandler { private final EventEngine p; TeamRelayRaceHandler(EventEngine pp){this.p=pp;} @Override public void onStart(ActiveEvent e){Bukkit.broadcastMessage(p.prefix()+"§e🏅 §eStaffellauf! §7Teams treten im Staffelrennen an. Erst wenn Spieler A das Ziel erreicht, startet Spieler B!");} @Override public void onEnd(ActiveEvent e){Bukkit.broadcastMessage(p.prefix()+"§7Staffellauf beendet — das schnellste Team gewinnt!");} } +class TeamBuildBattleHandler implements IEventHandler { private final EventEngine p; TeamBuildBattleHandler(EventEngine pp){this.p=pp;} @Override public void onStart(ActiveEvent e){Bukkit.broadcastMessage(p.prefix()+"§a👥 §eTeam Build Battle! §7Baut gemeinsam im Team zum Thema: §6Verlassene Burg!");} @Override public void onEnd(ActiveEvent e){Bukkit.broadcastMessage(p.prefix()+"§7Bauzeit vorbei — Teams werden bewertet!");} } +class TeamSurvivalHandler implements IEventHandler, Listener { + private final EventEngine plugin; private ActiveEvent current; + TeamSurvivalHandler(EventEngine p){this.plugin=p;} + @Override public void onStart(ActiveEvent e){current=e;Bukkit.getPluginManager().registerEvents(this,plugin);Bukkit.broadcastMessage(plugin.prefix()+"§2👥 §eTeam Survival! §7Überlebt gemeinsam so lange wie möglich gegen Mobs und die Umwelt!");} + @Override public void onEnd(ActiveEvent e){HandlerList.unregisterAll(this);current=null;} +} +class TeamTreasureHuntHandler implements IEventHandler { private final EventEngine p; TeamTreasureHuntHandler(EventEngine pp){this.p=pp;} @Override public void onStart(ActiveEvent e){Bukkit.broadcastMessage(p.prefix()+"§6👥 §eTeam Schatzsuche! §7Findet gemeinsam alle versteckten Truhen in der Arena. Das erste Team das alle findet, gewinnt!");} @Override public void onEnd(ActiveEvent e){Bukkit.broadcastMessage(p.prefix()+"§7Schatzsuche beendet!");} } +class TeamQuizHandler implements IEventHandler, Listener { + private final EventEngine plugin; private ActiveEvent current; + TeamQuizHandler(EventEngine p){this.plugin=p;} + @Override public void onStart(ActiveEvent e){current=e;Bukkit.getPluginManager().registerEvents(this,plugin);Bukkit.broadcastMessage(plugin.prefix()+"§e👥❓ §eTeam-Quiz! §7Teams beantworten Fragen — schnellste richtige Antwort gewinnt die Runde!");nextQuestion();} + @Override public void onEnd(ActiveEvent e){HandlerList.unregisterAll(this);current=null;} + private final String[][]questions={{"Was ist die härteste Materie?","Diamant"},{"Wie heißt der End-Boss?","Ender Dragon"},{"Was dropped ein Creeper?","Gunpowder"}}; + private int qi=0; + private void nextQuestion(){if(qi>=questions.length){plugin.getEventManager().endEvent("Quiz beendet");return;}Bukkit.broadcastMessage("§e❓ §f"+questions[qi][0]);} + @EventHandler public void onChat(org.bukkit.event.player.AsyncPlayerChatEvent e){if(current==null||qi>=questions.length)return;if(!current.isParticipant(e.getPlayer()))return;if(e.getMessage().trim().equalsIgnoreCase(questions[qi][1])){current.addScore(e.getPlayer().getUniqueId(),10);Bukkit.broadcastMessage("§a✔ §f"+e.getPlayer().getName()+"! §a+10");qi++;Bukkit.getScheduler().runTaskLater(plugin,this::nextQuestion,40L);}} +} +class CapturePointsHandler implements IEventHandler { + private final EventEngine plugin; private BukkitRunnable ticker; + CapturePointsHandler(EventEngine p){this.plugin=p;} + @Override public void onStart(ActiveEvent e){ + Bukkit.broadcastMessage(plugin.prefix()+"§e🚩 §ePunkte-Capture! §7Steht auf den markierten Punkten um Punkte zu sammeln. Mehr Spieler auf einem Punkt = schnellere Einnahme!"); + ticker=new BukkitRunnable(){ @Override public void run(){ if(e.getState()==ActiveEvent.State.ENDED){cancel();return;} /* Punkt-Check hier — benötigt vorbereitete Punkte-Positionen in den Settings */ for(UUID u:e.getParticipants()){Player p=Bukkit.getPlayer(u);if(p==null)continue;/* Placeholder: jeder Spieler auf einem Kontrollpunkt bekommt Punkte */} }}; + ticker.runTaskTimer(plugin,20L,20L); + } + @Override public void onEnd(ActiveEvent e){ if(ticker!=null)ticker.cancel(); } +} +class ResourceRaceHandler implements IEventHandler { private final EventEngine p; ResourceRaceHandler(EventEngine pp){this.p=pp;} @Override public void onStart(ActiveEvent e){Bukkit.broadcastMessage(p.prefix()+"§7👥 §eRessourcen-Rennen! §7Sammelt gemeinsam: §e64x Eisen, 32x Gold, 16x Diamant §7— gebt alles in der Truhe ab!");} @Override public void onEnd(ActiveEvent e){Bukkit.broadcastMessage(p.prefix()+"§7Ressourcen-Zeit vorbei — wer hat die Liste zuerst erfüllt?");} } +class HideAndSeekHandler implements IEventHandler { private final EventEngine p; HideAndSeekHandler(EventEngine pp){this.p=pp;} @Override public void onStart(ActiveEvent e){Bukkit.broadcastMessage(p.prefix()+"§8👁 §eVerstecken! §7Ein Team versteckt sich — das andere sucht. §c30 Sekunden §7zum Verstecken — lauft!");} @Override public void onEnd(ActiveEvent e){Bukkit.broadcastMessage(p.prefix()+"§7Verstecken beendet! §eDie Sucher haben alle gefunden (oder nicht 😄)");} } +class MurderMysteryHandler implements IEventHandler, Listener { + private final EventEngine plugin; private ActiveEvent current; private UUID murderer; private UUID detective; + MurderMysteryHandler(EventEngine p){this.plugin=p;} + @Override public void onStart(ActiveEvent e){ + current=e;Bukkit.getPluginManager().registerEvents(this,plugin); + Listparts=new ArrayList<>(e.getParticipants());Collections.shuffle(parts); + if(parts.size()<3){Bukkit.broadcastMessage(plugin.prefix()+"§cMindestens 3 Spieler benötigt!");return;} + murderer=parts.get(0);detective=parts.get(1); + Player mp=Bukkit.getPlayer(murderer);Player dp=Bukkit.getPlayer(detective); + if(mp!=null){mp.sendTitle("§c§lDU BIST DER MÖRDER!","§7Töte alle ohne entdeckt zu werden!",10,60,20);mp.getInventory().setItem(0,new ItemStack(Material.IRON_SWORD));} + if(dp!=null){dp.sendTitle("§9§lDU BIST DER DETEKTIV!","§7Finde den Mörder und bringe ihn zur Strecke!",10,60,20);dp.getInventory().setItem(0,new ItemStack(Material.BOW));dp.getInventory().addItem(new ItemStack(Material.ARROW,1));} + Bukkit.broadcastMessage(plugin.prefix()+"§8🔍 §eMurder Mystery! §7Der Mörder ist unter euch. Detektiv: finde ihn!"); + } + @Override public void onEnd(ActiveEvent e){HandlerList.unregisterAll(this);current=null;murderer=null;detective=null;} + @EventHandler public void onDeath(org.bukkit.event.entity.PlayerDeathEvent e){ + if(current==null||!current.isParticipant(e.getEntity()))return; + e.setDeathMessage(null); + if(e.getEntity().getUniqueId().equals(murderer)){Bukkit.broadcastMessage(plugin.prefix()+"§a✔ §7Der Mörder wurde gefangen! §9Detektiv und Unschuldige §7gewinnen!");if(detective!=null){current.addScore(detective,100);}plugin.getEventManager().endEvent("Mörder gefangen");} + else if(e.getEntity().getUniqueId().equals(detective)){Bukkit.broadcastMessage(plugin.prefix()+"§c§lDer Detektiv ist tot! §r§7Der Mörder macht weiter...");e.getEntity().setGameMode(GameMode.SPECTATOR);} + else{e.getEntity().setGameMode(GameMode.SPECTATOR);Bukkit.broadcastMessage("§7"+e.getEntity().getName()+" §7wurde ermordet...");} + } +} +class ColorWarHandler implements IEventHandler, Listener { + private final EventEngine plugin; private ActiveEvent current; private final Map teamColors=new HashMap<>(); + ColorWarHandler(EventEngine p){this.plugin=p;} + private static final Material[]COLORS={Material.RED_WOOL,Material.BLUE_WOOL,Material.GREEN_WOOL,Material.YELLOW_WOOL}; + @Override public void onStart(ActiveEvent e){ + current=e;Bukkit.getPluginManager().registerEvents(this,plugin); + Listparts=new ArrayList<>(e.getParticipants()); + for(int i=0;icounts=new EnumMap<>(Material.class);dev.viper.eventengine.model.EventRegion r=e.getDefinition().getRegion();for(int x=r.getMinX();x<=r.getMaxX();x++)for(int z=r.getMinZ();z<=r.getMaxZ();z++){Material t=w.getBlockAt(x,r.getMinY(),z).getType();if(Arrays.asList(COLORS).contains(t))counts.merge(t,1,Integer::sum);}Bukkit.broadcastMessage(plugin.prefix()+"§e🎨 §7Farben-Ergebnis:");counts.forEach((m,cnt)->Bukkit.broadcastMessage(" §7"+m.name()+": §e"+cnt+" §7Blöcke"));} + } + current=null;teamColors.clear(); + } + // Damit ist der Import nötig + private static final Material[] COLORS_REF = COLORS; +} + +// ══════════════════════════════════════════════════════════ +// FUN EVENTS — 7 fehlende +// ══════════════════════════════════════════════════════════ + +class TinyPlayersHandler implements IEventHandler { + private final EventEngine p; + TinyPlayersHandler(EventEngine pp){this.p=pp;} + @Override public void onStart(ActiveEvent e){ + for(UUID u:e.getParticipants()){Player pl=Bukkit.getPlayer(u);if(pl==null)continue;pl.addPotionEffect(new PotionEffect(PotionEffectType.SLOWNESS,e.getDefinition().getDurationSeconds()*20,0,false,false));pl.addPotionEffect(new PotionEffect(PotionEffectType.JUMP_BOOST,e.getDefinition().getDurationSeconds()*20,2,false,false));} + Bukkit.broadcastMessage(p.prefix()+"§e🐭 §eMini-Spieler! §7Alle sind jetzt winzig klein!"); + } + @Override public void onEnd(ActiveEvent e){for(UUID u:e.getParticipants()){Player pl=Bukkit.getPlayer(u);if(pl!=null){pl.removePotionEffect(PotionEffectType.SLOWNESS);pl.removePotionEffect(PotionEffectType.JUMP_BOOST);}}} +} +class GiantPlayersHandler implements IEventHandler { + private final EventEngine p; + GiantPlayersHandler(EventEngine pp){this.p=pp;} + @Override public void onStart(ActiveEvent e){ + for(UUID u:e.getParticipants()){Player pl=Bukkit.getPlayer(u);if(pl==null)continue;pl.addPotionEffect(new PotionEffect(PotionEffectType.SPEED,e.getDefinition().getDurationSeconds()*20,1,false,false));pl.addPotionEffect(new PotionEffect(PotionEffectType.STRENGTH,e.getDefinition().getDurationSeconds()*20,1,false,false));} + Bukkit.broadcastMessage(p.prefix()+"§6🦣 §eRiesen-Spieler! §7Alle sind jetzt riesig! Stärke und Geschwindigkeit erhöht!"); + } + @Override public void onEnd(ActiveEvent e){for(UUID u:e.getParticipants()){Player pl=Bukkit.getPlayer(u);if(pl!=null){pl.removePotionEffect(PotionEffectType.SPEED);pl.removePotionEffect(PotionEffectType.STRENGTH);}}} +} +class InvisiblePlayersHandler implements IEventHandler { + private final EventEngine p; + InvisiblePlayersHandler(EventEngine pp){this.p=pp;} + @Override public void onStart(ActiveEvent e){ + for(UUID u:e.getParticipants()){Player pl=Bukkit.getPlayer(u);if(pl==null)continue;pl.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY,e.getDefinition().getDurationSeconds()*20,0,false,false));} + Bukkit.broadcastMessage(p.prefix()+"§7👻 §eUnsichtbarkeit! §7Alle Spieler sind unsichtbar!"); + } + @Override public void onEnd(ActiveEvent e){for(UUID u:e.getParticipants()){Player pl=Bukkit.getPlayer(u);if(pl!=null)pl.removePotionEffect(PotionEffectType.INVISIBILITY);}} +} +class RandomTeleportHandler implements IEventHandler { + private final EventEngine plugin; private BukkitRunnable tpRunner; + RandomTeleportHandler(EventEngine p){this.plugin=p;} + @Override public void onStart(ActiveEvent e){ + Bukkit.broadcastMessage(plugin.prefix()+"§d🌀 §eZufalls-TP! §7Alle 5 Sekunden wird jemand teleportiert!"); + final Random rng=new Random(); + tpRunner=new BukkitRunnable(){@Override public void run(){if(e.getState()==ActiveEvent.State.ENDED){cancel();return;} for(UUID u:e.getParticipants()){Player p=Bukkit.getPlayer(u);if(p==null||rng.nextInt(3)!=0)continue;Location loc=e.getDefinition().hasRegion()?e.getDefinition().getRegion().randomLocation(rng):p.getLocation().add((rng.nextDouble()-0.5)*30,0,(rng.nextDouble()-0.5)*30);if(loc!=null)p.teleport(loc);p.sendActionBar("§d⚡ ZUFALLS-TP!");}}}; + tpRunner.runTaskTimer(plugin,20L,100L); + } + @Override public void onEnd(ActiveEvent e){ if(tpRunner!=null)tpRunner.cancel(); } +} +class SwapInventoriesHandler implements IEventHandler { + private final EventEngine p; + SwapInventoriesHandler(EventEngine pp){this.p=pp;} + @Override public void onStart(ActiveEvent e){ + Listparts=new ArrayList<>(e.getParticipants()); + if(parts.size()<2){Bukkit.broadcastMessage(p.prefix()+"§cMindestens 2 Spieler benötigt!");return;} + Collections.shuffle(parts); + Listinventories=new ArrayList<>(); + for(UUID u:parts){Player pl=Bukkit.getPlayer(u);inventories.add(pl!=null?pl.getInventory().getContents().clone():new ItemStack[41]);} + for(int i=0;i MAX_WAVES) { + cancel(); + plugin.getEventManager().endEvent("Alle Wellen überstanden!"); + return; + } + spawnWave(currentWave); + } + }; + waveRunner.runTaskTimer(plugin, 60L, WAVE_INTERVAL_SEC * 20L); + } + + @Override + public void onEnd(ActiveEvent event) { + HandlerList.unregisterAll(this); + if (waveRunner != null) waveRunner.cancel(); + this.currentEvent = null; + } + + private void spawnWave(int wave) { + broadcast("§c🌊 §eWelle §c" + wave + "§e/" + MAX_WAVES + " §7startet!"); + + // Spawn-Punkt = erster Teilnehmer oder Welt-Spawn + Location spawnLoc = null; + for (UUID uuid : currentEvent.getParticipants()) { + Player p = Bukkit.getPlayer(uuid); + if (p != null) { spawnLoc = p.getLocation(); break; } + } + if (spawnLoc == null) return; + + World world = spawnLoc.getWorld(); + int mobCount = 3 + (wave * 2); + + for (int i = 0; i < mobCount; i++) { + // Zufälliger Offset + double offsetX = (Math.random() - 0.5) * 20; + double offsetZ = (Math.random() - 0.5) * 20; + Location loc = spawnLoc.clone().add(offsetX, 0, offsetZ); + + EntityType type = getMobForWave(wave); + try { + Entity mob = world.spawnEntity(loc, type); + // HP skalieren + if (mob instanceof LivingEntity le) { + double baseHp = 20 + (wave * 4); + if (le.getAttribute(Attribute.MAX_HEALTH) != null) { + le.getAttribute(Attribute.MAX_HEALTH).setBaseValue(baseHp); + le.setHealth(baseHp); + } + } + } catch (Exception ignored) {} + } + + // Boss ab Welle 5 + if (wave == 5) spawnBoss(spawnLoc, "Mini-Boss der Welle 5", EntityType.ELDER_GUARDIAN); + if (wave == 10) spawnBoss(spawnLoc, "§c⚠ ENDGEGNER", EntityType.WITHER_SKELETON); + } + + private EntityType getMobForWave(int wave) { + EntityType[] early = {EntityType.ZOMBIE, EntityType.SKELETON, EntityType.SPIDER}; + EntityType[] mid = {EntityType.CREEPER, EntityType.WITCH, EntityType.HUSK}; + EntityType[] late = {EntityType.VINDICATOR, EntityType.RAVAGER, EntityType.PIGLIN_BRUTE}; + + if (wave <= 3) return early[(int)(Math.random() * early.length)]; + if (wave <= 6) return mid[(int)(Math.random() * mid.length)]; + return late[(int)(Math.random() * late.length)]; + } + + private void spawnBoss(Location loc, String name, EntityType type) { + try { + Entity boss = loc.getWorld().spawnEntity(loc, type); + if (boss instanceof LivingEntity le) { + le.setCustomName("§c§l" + name); + le.setCustomNameVisible(true); + if (le.getAttribute(Attribute.MAX_HEALTH) != null) { + le.getAttribute(Attribute.MAX_HEALTH).setBaseValue(200); + le.setHealth(200); + } + } + broadcast("§c⚠ §l" + name + " §c§ltaucht auf!"); + } catch (Exception ignored) {} + } + + @EventHandler + public void onMobDeath(EntityDeathEvent e) { + if (currentEvent == null) return; + Player killer = e.getEntity().getKiller(); + if (killer == null || !currentEvent.isParticipant(killer)) return; + + int points = getMobPoints(e.getEntityType()); + currentEvent.addScore(killer.getUniqueId(), points); + killer.sendActionBar("§c+" + points + " §7Punkte (Welle §e" + currentWave + "§7)"); + } + + private int getMobPoints(EntityType type) { + return switch (type) { + case ZOMBIE, SKELETON, SPIDER -> 1; + case CREEPER, WITCH, HUSK -> 2; + case VINDICATOR, RAVAGER -> 5; + case ELDER_GUARDIAN, WITHER_SKELETON, PIGLIN_BRUTE -> 10; + default -> 1; + }; + } + + private void broadcast(String msg) { Bukkit.broadcastMessage(msg); } +} diff --git a/src/main/java/dev/viper/eventengine/events/builtin/MoreFunHandlers.java b/src/main/java/dev/viper/eventengine/events/builtin/MoreFunHandlers.java new file mode 100644 index 0000000..eb1846a --- /dev/null +++ b/src/main/java/dev/viper/eventengine/events/builtin/MoreFunHandlers.java @@ -0,0 +1,136 @@ +package dev.viper.eventengine.events.builtin; + +import dev.viper.eventengine.EventEngine; +import dev.viper.eventengine.model.ActiveEvent; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.List; +import java.util.Random; + +/** + * Zufalls-Effekte: Alle 15s bekommt jeder Spieler einen zufälligen Potion-Effekt + */ +class RandomEffectsHandler implements IEventHandler, Listener { + + private static final List EFFECTS = List.of( + PotionEffectType.SPEED, PotionEffectType.SLOWNESS, PotionEffectType.JUMP_BOOST, + PotionEffectType.STRENGTH, PotionEffectType.WEAKNESS, PotionEffectType.BLINDNESS, + PotionEffectType.NIGHT_VISION, PotionEffectType.INVISIBILITY, PotionEffectType.NAUSEA, + PotionEffectType.REGENERATION, PotionEffectType.FIRE_RESISTANCE, PotionEffectType.POISON + ); + + private final EventEngine plugin; + private ActiveEvent currentEvent; + private BukkitRunnable roller; + private final Random rng = new Random(); + + RandomEffectsHandler(EventEngine plugin) { this.plugin = plugin; } + + @Override + public void onStart(ActiveEvent event) { + this.currentEvent = event; + Bukkit.broadcastMessage(plugin.prefix() + "§5🎲 §eZufalls-Effekte! §7Alle 15s ein neuer zufälliger Trank-Effekt!"); + + roller = new BukkitRunnable() { + @Override + public void run() { + if (currentEvent == null) { cancel(); return; } + for (Player p : Bukkit.getOnlinePlayers()) { + PotionEffectType type = EFFECTS.get(rng.nextInt(EFFECTS.size())); + int amp = rng.nextInt(3); // 0-2 + p.addPotionEffect(new PotionEffect(type, 300, amp, false, true)); + p.sendActionBar("§5🎲 Effekt: §f" + formatEffect(type) + " §7(Stufe " + (amp + 1) + ")"); + } + } + }; + roller.runTaskTimer(plugin, 20L, 300L); // 15s + } + + @Override + public void onEnd(ActiveEvent event) { + HandlerList.unregisterAll(this); + if (roller != null) roller.cancel(); + for (Player p : Bukkit.getOnlinePlayers()) + EFFECTS.forEach(p::removePotionEffect); + this.currentEvent = null; + } + + private String formatEffect(PotionEffectType type) { + return type.getName().replace("_", " ").toLowerCase() + .substring(0, 1).toUpperCase() + type.getName().replace("_", " ").toLowerCase().substring(1); + } +} + +/** + * Chaos-Modus: Verschiedene verrückte Effekte gleichzeitig + */ +class ChaosModeHandler implements IEventHandler, Listener { + + private final EventEngine plugin; + private ActiveEvent currentEvent; + private BukkitRunnable chaos; + private final Random rng = new Random(); + + ChaosModeHandler(EventEngine plugin) { this.plugin = plugin; } + + @Override + public void onStart(ActiveEvent event) { + this.currentEvent = event; + Bukkit.broadcastMessage(plugin.prefix() + "§c§l⚡ CHAOS MODUS! §r§7Alles ist möglich!"); + + chaos = new BukkitRunnable() { + int tick = 0; + @Override + public void run() { + if (currentEvent == null) { cancel(); return; } + tick++; + for (Player p : Bukkit.getOnlinePlayers()) { + int action = rng.nextInt(6); + switch (action) { + case 0 -> { // Random Teleport in der Nähe + var loc = p.getLocation().add( + (rng.nextDouble()-0.5)*30, 0, (rng.nextDouble()-0.5)*30); + p.teleport(loc); + p.sendActionBar("§c⚡ TELEPORT!"); + } + case 1 -> { // Zufällige Geschwindigkeit + p.addPotionEffect(new PotionEffect(PotionEffectType.SPEED, + 100, rng.nextInt(5), false, false)); + } + case 2 -> { // Feuern + if (rng.nextBoolean()) p.setFireTicks(40); + } + case 3 -> { // In die Luft schleudern + p.setVelocity(p.getVelocity().setY(1.5 + rng.nextDouble())); + p.sendActionBar("§6⬆ YEET!"); + } + case 4 -> { // Unsichtbar machen + p.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, + 100, 0, false, false)); + } + case 5 -> { /* Nichts */ } + } + } + } + }; + chaos.runTaskTimer(plugin, 20L, 40L); // alle 2s + } + + @Override + public void onEnd(ActiveEvent event) { + HandlerList.unregisterAll(this); + if (chaos != null) chaos.cancel(); + for (Player p : Bukkit.getOnlinePlayers()) { + p.setFireTicks(0); + p.removePotionEffect(PotionEffectType.SPEED); + p.removePotionEffect(PotionEffectType.INVISIBILITY); + } + this.currentEvent = null; + } +} diff --git a/src/main/java/dev/viper/eventengine/events/builtin/PvPHandlers.java b/src/main/java/dev/viper/eventengine/events/builtin/PvPHandlers.java new file mode 100644 index 0000000..790c17c --- /dev/null +++ b/src/main/java/dev/viper/eventengine/events/builtin/PvPHandlers.java @@ -0,0 +1,471 @@ +package dev.viper.eventengine.events.builtin; + +import dev.viper.eventengine.EventEngine; +import dev.viper.eventengine.model.ActiveEvent; +import org.bukkit.*; +import org.bukkit.attribute.Attribute; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.PlayerDeathEvent; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.*; + +// ═══════════════════════════════════════════════════════════════════ +// 1v1 Turnier — genau 2 Spieler, wer zuerst stirbt verliert +// ═══════════════════════════════════════════════════════════════════ +class OneVsOneHandler implements IEventHandler, Listener { + private final EventEngine plugin; + private ActiveEvent current; + OneVsOneHandler(EventEngine p) { this.plugin = p; } + + @Override public void onStart(ActiveEvent e) { + current = e; + Bukkit.getPluginManager().registerEvents(this, plugin); + broadcast("§61v1 §e– zwei Spieler, ein Gewinner. Kein Respawn!"); + } + @Override public void onEnd(ActiveEvent e) { HandlerList.unregisterAll(this); current = null; } + private void broadcast(String m) { Bukkit.broadcastMessage(m); } + + @EventHandler public void onDeath(PlayerDeathEvent e) { + if (current == null || !current.isParticipant(e.getEntity())) return; + e.setDeathMessage(null); + Player loser = e.getEntity(); + current.removeParticipant(loser); + loser.setGameMode(GameMode.SPECTATOR); + // Übriggebliebener = Gewinner + current.getParticipants().stream().findFirst().ifPresent(uuid -> { + current.addScore(uuid, 100); + Player w = Bukkit.getPlayer(uuid); + if (w != null) { w.sendTitle("§6GEWONNEN!", "§71v1 Champion!", 10,60,20); } + }); + plugin.getEventManager().endEvent(loser.getName() + " wurde besiegt"); + } +} + +// ═══════════════════════════════════════════════════════════════════ +// Nur Bogen — Kills mit Bogen = Punkte, Nahkampfschaden = 0 +// ═══════════════════════════════════════════════════════════════════ +class BowOnlyHandler implements IEventHandler, Listener { + private final EventEngine plugin; + private ActiveEvent current; + BowOnlyHandler(EventEngine p) { this.plugin = p; } + + @Override public void onStart(ActiveEvent e) { + current = e; + Bukkit.getPluginManager().registerEvents(this, plugin); + for (UUID uuid : e.getParticipants()) { + Player p = Bukkit.getPlayer(uuid); if (p == null) continue; + p.getInventory().clear(); + p.getInventory().setItem(0, enchant(new ItemStack(Material.BOW), Enchantment.INFINITY, 1)); + p.getInventory().setItem(9, new ItemStack(Material.ARROW, 1)); + p.setHealth(Math.min(p.getMaxHealth(),20)); + } + broadcast("§9🏹 §eNur Bogen! §7Nahkampf macht keinen Schaden."); + } + @Override public void onEnd(ActiveEvent e) { HandlerList.unregisterAll(this); current = null; } + private void broadcast(String m) { Bukkit.broadcastMessage(m); } + + @EventHandler public void onDamage(EntityDamageByEntityEvent e) { + if (current == null || !(e.getEntity() instanceof Player victim)) return; + if (!current.isParticipant(victim)) return; + if (e.getCause() != org.bukkit.event.entity.EntityDamageEvent.DamageCause.PROJECTILE) + e.setDamage(0); + } + @EventHandler public void onDeath(PlayerDeathEvent e) { + if (current == null || !current.isParticipant(e.getEntity())) return; + e.setDeathMessage(null); + Player killer = e.getEntity().getKiller(); + if (killer != null && current.isParticipant(killer)) current.addScore(killer.getUniqueId(),1); + Bukkit.getScheduler().runTaskLater(plugin, () -> { if(e.getEntity().isOnline()){ e.getEntity().spigot().respawn(); resetKit(e.getEntity()); }}, 60L); + } + private void resetKit(Player p) { + p.getInventory().clear(); + p.getInventory().setItem(0, enchant(new ItemStack(Material.BOW), Enchantment.INFINITY, 1)); + p.getInventory().setItem(9, new ItemStack(Material.ARROW, 1)); + p.setHealth(Math.min(p.getMaxHealth(),20)); + } + private ItemStack enchant(ItemStack i, Enchantment e, int lvl) { + ItemMeta m = i.getItemMeta(); m.addEnchant(e, lvl, true); i.setItemMeta(m); return i; + } +} + +// ═══════════════════════════════════════════════════════════════════ +// Nur Schwert — nur Schwertschaden zählt +// ═══════════════════════════════════════════════════════════════════ +class SwordOnlyHandler implements IEventHandler, Listener { + private final EventEngine plugin; + private ActiveEvent current; + SwordOnlyHandler(EventEngine p) { this.plugin = p; } + + @Override public void onStart(ActiveEvent e) { + current = e; + Bukkit.getPluginManager().registerEvents(this, plugin); + for (UUID uuid : e.getParticipants()) { + Player p = Bukkit.getPlayer(uuid); if (p == null) continue; + p.getInventory().clear(); + p.getInventory().setItem(0, new ItemStack(Material.IRON_SWORD)); + p.setHealth(Math.min(p.getMaxHealth(),20)); + } + broadcast("§c⚔ §eNur Schwert! §7Bogen-Schaden wird blockiert."); + } + @Override public void onEnd(ActiveEvent e) { HandlerList.unregisterAll(this); current = null; } + private void broadcast(String m) { Bukkit.broadcastMessage(m); } + + @EventHandler public void onDamage(EntityDamageByEntityEvent e) { + if (current == null || !(e.getEntity() instanceof Player victim)) return; + if (!current.isParticipant(victim)) return; + if (e.getCause() == org.bukkit.event.entity.EntityDamageEvent.DamageCause.PROJECTILE) + e.setDamage(0); + } + @EventHandler public void onDeath(PlayerDeathEvent e) { + if (current == null || !current.isParticipant(e.getEntity())) return; + e.setDeathMessage(null); + Player killer = e.getEntity().getKiller(); + if (killer != null && current.isParticipant(killer)) current.addScore(killer.getUniqueId(),1); + Bukkit.getScheduler().runTaskLater(plugin, () -> { if(e.getEntity().isOnline()){ e.getEntity().spigot().respawn(); e.getEntity().getInventory().setItem(0, new ItemStack(Material.IRON_SWORD)); e.getEntity().setHealth(Math.min(e.getEntity().getMaxHealth(),20)); }}, 60L); + } +} + +// ═══════════════════════════════════════════════════════════════════ +// Faustkampf — kein Item-Schaden +// ═══════════════════════════════════════════════════════════════════ +class FistFightHandler implements IEventHandler, Listener { + private final EventEngine plugin; + private ActiveEvent current; + FistFightHandler(EventEngine p) { this.plugin = p; } + + @Override public void onStart(ActiveEvent e) { + current = e; + Bukkit.getPluginManager().registerEvents(this, plugin); + for (UUID uuid : e.getParticipants()) { + Player p = Bukkit.getPlayer(uuid); if (p == null) continue; + p.getInventory().clear(); + p.setHealth(Math.min(p.getMaxHealth(),20)); + } + broadcast("§e✊ §eFaustkampf! §7Kein Item erlaubt — nur Fäuste!"); + } + @Override public void onEnd(ActiveEvent e) { HandlerList.unregisterAll(this); current = null; } + private void broadcast(String m) { Bukkit.broadcastMessage(m); } + + @EventHandler public void onDamage(EntityDamageByEntityEvent e) { + if (current == null || !(e.getEntity() instanceof Player victim)) return; + if (!current.isParticipant(victim)) return; + // Nur Hand-Schaden (ENTITY_ATTACK mit leerem Item) + if (e.getDamager() instanceof Player attacker) { + if (attacker.getInventory().getItemInMainHand().getType() != Material.AIR + && attacker.getInventory().getItemInMainHand().getType() != Material.AIR) + e.setDamage(1.5); // normaler Faust-Schaden + } + } + @EventHandler public void onDeath(PlayerDeathEvent e) { + if (current == null || !current.isParticipant(e.getEntity())) return; + e.setDeathMessage(null); + Player killer = e.getEntity().getKiller(); + if (killer != null && current.isParticipant(killer)) current.addScore(killer.getUniqueId(),1); + Bukkit.getScheduler().runTaskLater(plugin, () -> { if(e.getEntity().isOnline()){ e.getEntity().spigot().respawn(); e.getEntity().getInventory().clear(); e.getEntity().setHealth(Math.min(e.getEntity().getMaxHealth(),20)); }}, 60L); + } +} + +// ═══════════════════════════════════════════════════════════════════ +// Kit PvP — alle Spieler kriegen gleiches Kit +// ═══════════════════════════════════════════════════════════════════ +class KitPvPHandler implements IEventHandler, Listener { + private final EventEngine plugin; + private ActiveEvent current; + KitPvPHandler(EventEngine p) { this.plugin = p; } + + @Override public void onStart(ActiveEvent e) { + current = e; + Bukkit.getPluginManager().registerEvents(this, plugin); + for (UUID uuid : e.getParticipants()) giveKit(Bukkit.getPlayer(uuid)); + broadcast("§6🗡 §eKit PvP! §7Alle erhalten gleiches Kit — nur Skill entscheidet!"); + } + @Override public void onEnd(ActiveEvent e) { HandlerList.unregisterAll(this); current = null; } + @Override public void onPlayerJoin(ActiveEvent e, Player p) { giveKit(p); } + private void broadcast(String m) { Bukkit.broadcastMessage(m); } + + private void giveKit(Player p) { + if (p == null) return; + p.getInventory().clear(); + p.getInventory().setItem(0, new ItemStack(Material.IRON_SWORD)); + p.getInventory().setItem(1, enchant(new ItemStack(Material.BOW), Enchantment.INFINITY,1)); + p.getInventory().setItem(2, new ItemStack(Material.GOLDEN_APPLE, 3)); + p.getInventory().setItem(9, new ItemStack(Material.ARROW,1)); + p.getEquipment().setHelmet(new ItemStack(Material.IRON_HELMET)); + p.getEquipment().setChestplate(new ItemStack(Material.IRON_CHESTPLATE)); + p.getEquipment().setLeggings(new ItemStack(Material.IRON_LEGGINGS)); + p.getEquipment().setBoots(new ItemStack(Material.IRON_BOOTS)); + p.setHealth(Math.min(p.getMaxHealth(),20)); p.setFoodLevel(20); + } + @EventHandler public void onDeath(PlayerDeathEvent e) { + if (current == null || !current.isParticipant(e.getEntity())) return; + e.setDeathMessage(null); + Player killer = e.getEntity().getKiller(); + if (killer != null && current.isParticipant(killer)) current.addScore(killer.getUniqueId(),1); + Bukkit.getScheduler().runTaskLater(plugin, () -> { if(e.getEntity().isOnline()){ e.getEntity().spigot().respawn(); giveKit(e.getEntity()); }},60L); + } + private ItemStack enchant(ItemStack i, Enchantment e, int lvl) { + ItemMeta m = i.getItemMeta(); m.addEnchant(e,lvl,true); i.setItemMeta(m); return i; + } +} + +// ═══════════════════════════════════════════════════════════════════ +// King of the Hill — Zentrum halten = Punkte pro Sekunde +// ═══════════════════════════════════════════════════════════════════ +class KingOfTheHillHandler implements IEventHandler, Listener { + private final EventEngine plugin; + private ActiveEvent current; + private BukkitRunnable ticker; + KingOfTheHillHandler(EventEngine p) { this.plugin = p; } + + @Override public void onStart(ActiveEvent e) { + current = e; + Bukkit.getPluginManager().registerEvents(this, plugin); + broadcast("§6👑 §eKing of the Hill! §7Halte die Mitte und sammle Punkte!"); + + ticker = new BukkitRunnable() { + @Override public void run() { + if (current == null) { cancel(); return; } + org.bukkit.Location center = current.getDefinition().hasRegion() + ? current.getDefinition().getRegion().getCenter() : null; + if (center == null) return; + for (UUID uuid : current.getParticipants()) { + Player p = Bukkit.getPlayer(uuid); if (p == null) continue; + if (p.getLocation().distance(center) <= 5) { + current.addScore(uuid, 1); + p.sendActionBar("§6👑 §eAuf dem Hügel! §7+" + 1 + " Punkt"); + } + } + } + }; + ticker.runTaskTimer(plugin, 20L, 20L); + } + @Override public void onEnd(ActiveEvent e) { HandlerList.unregisterAll(this); if(ticker!=null)ticker.cancel(); current=null; } + private void broadcast(String m) { Bukkit.broadcastMessage(m); } + @EventHandler public void onDeath(PlayerDeathEvent e) { + if (current == null || !current.isParticipant(e.getEntity())) return; + e.setDeathMessage(null); + Bukkit.getScheduler().runTaskLater(plugin, () -> { if(e.getEntity().isOnline()) e.getEntity().spigot().respawn(); }, 60L); + } +} + +// ═══════════════════════════════════════════════════════════════════ +// Team Battle — 2 Teams, Kills = Teampunkte +// ═══════════════════════════════════════════════════════════════════ +class TeamBattleHandler implements IEventHandler, Listener { + private final EventEngine plugin; + private ActiveEvent current; + private final Map teamMap = new HashMap<>(); // 0 = rot, 1 = blau + TeamBattleHandler(EventEngine p) { this.plugin = p; } + + @Override public void onStart(ActiveEvent e) { + current = e; teamMap.clear(); + Bukkit.getPluginManager().registerEvents(this, plugin); + List participants = new ArrayList<>(e.getParticipants()); + for (int i = 0; i < participants.size(); i++) teamMap.put(participants.get(i), i % 2); + broadcast("§cTeam Rot §7vs §9Team Blau §7– Kills gewinnen!"); + for (UUID uuid : teamMap.keySet()) { + Player p = Bukkit.getPlayer(uuid); if (p == null) continue; + p.sendMessage(plugin.prefix() + "Dein Team: " + (teamMap.get(uuid)==0 ? "§cRot":"§9Blau")); + } + } + @Override public void onEnd(ActiveEvent e) { HandlerList.unregisterAll(this); current=null; teamMap.clear(); } + private void broadcast(String m) { Bukkit.broadcastMessage(m); } + @EventHandler public void onDeath(PlayerDeathEvent e) { + if (current == null || !current.isParticipant(e.getEntity())) return; + e.setDeathMessage(null); + Player killer = e.getEntity().getKiller(); + if (killer != null && current.isParticipant(killer)) { + // Team des Killers bekommt Punkt + Integer killerTeam = teamMap.get(killer.getUniqueId()); + if (killerTeam != null) { + teamMap.forEach((uuid,team) -> { if(team.equals(killerTeam)) current.addScore(uuid,1); }); + } + } + Bukkit.getScheduler().runTaskLater(plugin, () -> { if(e.getEntity().isOnline()){ e.getEntity().spigot().respawn(); e.getEntity().setHealth(Math.min(e.getEntity().getMaxHealth(),20)); }},60L); + } +} + +// ═══════════════════════════════════════════════════════════════════ +// Elytra PvP — Spieler mit Elytra + Bogen, keine Gravity +// ═══════════════════════════════════════════════════════════════════ +class ElytraPvPHandler implements IEventHandler, Listener { + private final EventEngine plugin; + private ActiveEvent current; + ElytraPvPHandler(EventEngine p) { this.plugin = p; } + + @Override public void onStart(ActiveEvent e) { + current = e; + Bukkit.getPluginManager().registerEvents(this, plugin); + for (UUID uuid : e.getParticipants()) { + Player p = Bukkit.getPlayer(uuid); if (p == null) continue; + p.getInventory().clear(); + p.getEquipment().setChestplate(new ItemStack(Material.ELYTRA)); + ItemStack bow = new ItemStack(Material.BOW); + ItemMeta bm = bow.getItemMeta(); bm.addEnchant(Enchantment.INFINITY,1,true); bow.setItemMeta(bm); + p.getInventory().setItem(0, bow); + p.getInventory().setItem(9, new ItemStack(Material.ARROW,1)); + p.getInventory().setItem(1, new ItemStack(Material.FIREWORK_ROCKET, 64)); + p.setHealth(Math.min(p.getMaxHealth(),20)); + } + broadcast("§b🪂 §eElytra PvP! §7Fliege und schieße deine Gegner ab!"); + } + @Override public void onEnd(ActiveEvent e) { HandlerList.unregisterAll(this); current=null; } + private void broadcast(String m) { Bukkit.broadcastMessage(m); } + @EventHandler public void onDeath(PlayerDeathEvent e) { + if (current==null||!current.isParticipant(e.getEntity())) return; + e.setDeathMessage(null); + Player killer = e.getEntity().getKiller(); + if (killer!=null&¤t.isParticipant(killer)) current.addScore(killer.getUniqueId(),1); + Bukkit.getScheduler().runTaskLater(plugin,()->{ if(e.getEntity().isOnline()) e.getEntity().spigot().respawn(); },60L); + } +} + +// ═══════════════════════════════════════════════════════════════════ +// Arena Survival — PvP + Mobs gleichzeitig, beide geben Punkte +// ═══════════════════════════════════════════════════════════════════ +class ArenaSurvivalHandler implements IEventHandler, Listener { + private final EventEngine plugin; + private ActiveEvent current; + private BukkitRunnable spawner; + ArenaSurvivalHandler(EventEngine p) { this.plugin = p; } + + @Override public void onStart(ActiveEvent e) { + current = e; + Bukkit.getPluginManager().registerEvents(this, plugin); + broadcast("§c⚔🧟 §eArena Survival! §7Besiege Spieler UND Mobs für Punkte!"); + spawner = new BukkitRunnable() { + final org.bukkit.entity.EntityType[] mobs = { + org.bukkit.entity.EntityType.ZOMBIE,org.bukkit.entity.EntityType.SKELETON,org.bukkit.entity.EntityType.SPIDER + }; + final Random rng = new Random(); + @Override public void run() { + if (current==null){cancel();return;} + for (UUID uuid : current.getParticipants()) { + Player p = Bukkit.getPlayer(uuid); if(p==null) continue; + org.bukkit.Location loc = p.getLocation().add((rng.nextDouble()-0.5)*10,0,(rng.nextDouble()-0.5)*10); + p.getWorld().spawnEntity(loc, mobs[rng.nextInt(mobs.length)]); + } + } + }; + spawner.runTaskTimer(plugin, 100L, 100L); + } + @Override public void onEnd(ActiveEvent e) { HandlerList.unregisterAll(this); if(spawner!=null)spawner.cancel(); current=null; } + private void broadcast(String m) { Bukkit.broadcastMessage(m); } + @EventHandler public void onPlayerDeath(PlayerDeathEvent e) { + if (current==null||!current.isParticipant(e.getEntity())) return; + e.setDeathMessage(null); + Player killer=e.getEntity().getKiller(); + if (killer!=null&¤t.isParticipant(killer)) current.addScore(killer.getUniqueId(),3); + Bukkit.getScheduler().runTaskLater(plugin,()->{ if(e.getEntity().isOnline()){ e.getEntity().spigot().respawn(); e.getEntity().setHealth(Math.min(e.getEntity().getMaxHealth(),20)); }},60L); + } + @EventHandler public void onMobDeath(org.bukkit.event.entity.EntityDeathEvent e) { + if (current==null) return; + Player killer=e.getEntity().getKiller(); + if (killer!=null&¤t.isParticipant(killer)) current.addScore(killer.getUniqueId(),1); + } +} + +// ═══════════════════════════════════════════════════════════════════ +// UHC — kein natürliches Heilen, Goldenäpfel heilen +// ═══════════════════════════════════════════════════════════════════ +class UHCHandler implements IEventHandler, Listener { + private final EventEngine plugin; + private ActiveEvent current; + private final Set alive = new HashSet<>(); + UHCHandler(EventEngine p) { this.plugin = p; } + + @Override public void onStart(ActiveEvent e) { + current = e; alive.clear(); alive.addAll(e.getParticipants()); + Bukkit.getPluginManager().registerEvents(this, plugin); + for (UUID uuid : e.getParticipants()) { + Player p = Bukkit.getPlayer(uuid); if(p==null) continue; + p.addPotionEffect(new PotionEffect(PotionEffectType.REGENERATION, 0, 0)); // sicherstellen keine regen + } + broadcast("§c☠ §eUltra Hardcore! §7Keine natürliche Regeneration. Nur Goldäpfel heilen!"); + } + @Override public void onEnd(ActiveEvent e) { HandlerList.unregisterAll(this); current=null; alive.clear(); } + private void broadcast(String m) { Bukkit.broadcastMessage(m); } + @EventHandler public void onDeath(PlayerDeathEvent e) { + if (current==null||!current.isParticipant(e.getEntity())) return; + e.setDeathMessage(null); alive.remove(e.getEntity().getUniqueId()); + if (alive.size()==1) { + UUID w=alive.iterator().next(); current.addScore(w,100); + Player wp=Bukkit.getPlayer(w); String wn=wp!=null?wp.getName():"?"; + broadcast("§6✦ §e"+wn+" §6gewinnt Ultra Hardcore!"); + plugin.getEventManager().endEvent(wn+" hat gewonnen"); + } + } + // Natürliche Regeneration durch hunger blockieren + @EventHandler public void onRegen(org.bukkit.event.entity.EntityRegainHealthEvent e) { + if (current==null||!(e.getEntity() instanceof Player p)) return; + if (!current.isParticipant(p)) return; + if (e.getRegainReason()==org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.SATIATED|| + e.getRegainReason()==org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.REGEN) + e.setCancelled(true); + } +} + +// ═══════════════════════════════════════════════════════════════════ +// Hunger Games — starte ohne Items, sammle Loot, letzter gewinnt +// ═══════════════════════════════════════════════════════════════════ +class HungerGamesHandler implements IEventHandler, Listener { + private final EventEngine plugin; + private ActiveEvent current; + private final Set alive = new HashSet<>(); + HungerGamesHandler(EventEngine p) { this.plugin = p; } + + @Override public void onStart(ActiveEvent e) { + current = e; alive.clear(); alive.addAll(e.getParticipants()); + Bukkit.getPluginManager().registerEvents(this, plugin); + for (UUID uuid : e.getParticipants()) { + Player p = Bukkit.getPlayer(uuid); if(p==null) continue; + p.getInventory().clear(); p.setHealth(Math.min(p.getMaxHealth(),20)); + } + broadcast("§2🌲 §eHunger Games! §7Keine Ausrüstung — sammle Loot und überlebe!"); + } + @Override public void onEnd(ActiveEvent e) { HandlerList.unregisterAll(this); current=null; alive.clear(); } + private void broadcast(String m) { Bukkit.broadcastMessage(m); } + @EventHandler public void onDeath(PlayerDeathEvent e) { + if (current==null||!current.isParticipant(e.getEntity())) return; + e.setDeathMessage(null); alive.remove(e.getEntity().getUniqueId()); + e.getEntity().setGameMode(GameMode.SPECTATOR); + Player killer=e.getEntity().getKiller(); + if (killer!=null&¤t.isParticipant(killer)) current.addScore(killer.getUniqueId(),5); + broadcast("§c☠ §f"+e.getEntity().getName()+" §7scheidet aus! §8[§e"+alive.size()+" §7übrig§8]"); + if (alive.size()==1) { + UUID w=alive.iterator().next(); current.addScore(w,100); + Player wp=Bukkit.getPlayer(w); String wn=wp!=null?wp.getName():"?"; + broadcast("§6✦ §e"+wn+" §6gewinnt die Hunger Games!"); plugin.getEventManager().endEvent(wn+" hat gewonnen"); + } else if (alive.isEmpty()) plugin.getEventManager().endEvent("Unentschieden"); + } +} + +// Capture the Flag, BedWars Lite, SkyWars Lite: Arena-abhängig, Basislogik +class CaptureTheFlagHandler implements IEventHandler { + private final EventEngine plugin; + CaptureTheFlagHandler(EventEngine p) { this.plugin = p; } + @Override public void onStart(ActiveEvent e) { Bukkit.broadcastMessage(plugin.prefix()+"§e🚩 Capture the Flag! §7Stehle die Flagge des Gegners und bringe sie zu deiner Basis!"); } + @Override public void onEnd(ActiveEvent e) {} +} +class BedWarsLiteHandler implements IEventHandler { + private final EventEngine plugin; + BedWarsLiteHandler(EventEngine p) { this.plugin = p; } + @Override public void onStart(ActiveEvent e) { Bukkit.broadcastMessage(plugin.prefix()+"§e🛏 BedWars Lite! §7Schütze dein Bett und zerstöre das der Gegner!"); } + @Override public void onEnd(ActiveEvent e) {} +} +class SkyWarsLiteHandler implements IEventHandler { + private final EventEngine plugin; + SkyWarsLiteHandler(EventEngine p) { this.plugin = p; } + @Override public void onStart(ActiveEvent e) { Bukkit.broadcastMessage(plugin.prefix()+"§e☁ SkyWars Lite! §7Starte auf deiner Insel — sammle Loot und besiege alle anderen!"); } + @Override public void onEnd(ActiveEvent e) {} +} \ No newline at end of file diff --git a/src/main/java/dev/viper/eventengine/events/builtin/SnowballFightHandler.java b/src/main/java/dev/viper/eventengine/events/builtin/SnowballFightHandler.java new file mode 100644 index 0000000..ee719ef --- /dev/null +++ b/src/main/java/dev/viper/eventengine/events/builtin/SnowballFightHandler.java @@ -0,0 +1,75 @@ +package dev.viper.eventengine.events.builtin; + +import dev.viper.eventengine.EventEngine; +import dev.viper.eventengine.model.ActiveEvent; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.entity.Snowball; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.inventory.ItemStack; + +/** + * Schneeballschlacht: + * - Jeder Treffer = 1 Punkt + * - Spieler bekommen unendlich Schneebälle + * - Kein normaler Schaden + */ +public class SnowballFightHandler implements IEventHandler, Listener { + + private final EventEngine plugin; + private ActiveEvent currentEvent; + + public SnowballFightHandler(EventEngine plugin) { + this.plugin = plugin; + } + + @Override + public void onStart(ActiveEvent event) { + this.currentEvent = event; + Bukkit.getPluginManager().registerEvents(this, plugin); + // Schneebälle verteilen + for (var uuid : event.getParticipants()) { + Player p = Bukkit.getPlayer(uuid); + if (p != null) { + p.getInventory().clear(); + p.getInventory().setItem(0, new ItemStack(Material.SNOWBALL, 64)); + p.setHealth(Math.min(p.getMaxHealth(), 20)); + } + } + broadcast("§b❄ §eSchneeballschlacht! §7Treffe Gegner für Punkte!"); + } + + @Override + public void onEnd(ActiveEvent event) { + HandlerList.unregisterAll(this); + this.currentEvent = null; + } + + @EventHandler + public void onHit(EntityDamageByEntityEvent e) { + if (!(e.getEntity() instanceof Player victim)) return; + if (!(e.getDamager() instanceof Snowball snowball)) return; + if (!(snowball.getShooter() instanceof Player shooter)) return; + if (currentEvent == null) return; + if (!currentEvent.isParticipant(shooter) || !currentEvent.isParticipant(victim)) return; + if (shooter.equals(victim)) return; + + e.setDamage(0); // Kein Schaden + currentEvent.addScore(shooter.getUniqueId(), 1); + int score = currentEvent.getScore(shooter.getUniqueId()); + shooter.sendActionBar("§b❄ Treffer! §7Punkte: §e" + score); + shooter.playSound(shooter.getLocation(), Sound.BLOCK_GLASS_BREAK, 0.5f, 1.5f); + + // Nachladen + if (!shooter.getInventory().contains(Material.SNOWBALL)) { + shooter.getInventory().addItem(new ItemStack(Material.SNOWBALL, 64)); + } + } + + private void broadcast(String msg) { Bukkit.broadcastMessage(msg); } +} diff --git a/src/main/java/dev/viper/eventengine/events/builtin/SpleefHandler.java b/src/main/java/dev/viper/eventengine/events/builtin/SpleefHandler.java new file mode 100644 index 0000000..02fe487 --- /dev/null +++ b/src/main/java/dev/viper/eventengine/events/builtin/SpleefHandler.java @@ -0,0 +1,122 @@ +package dev.viper.eventengine.events.builtin; + +import dev.viper.eventengine.EventEngine; +import dev.viper.eventengine.model.ActiveEvent; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.entity.PlayerDeathEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * Spleef: + * - Spieler graben Schnee/Eis unter Gegnern weg + * - Fallen = ausgeschieden + * - Kein normaler Block-Break außerhalb der Spleef-Arena-Materialien + */ +public class SpleefHandler implements IEventHandler, Listener { + + private static final Set SPLEEF_MATERIALS = Set.of( + Material.SNOW_BLOCK, Material.ICE, Material.PACKED_ICE, + Material.BLUE_ICE, Material.WHITE_CONCRETE + ); + + private final EventEngine plugin; + private ActiveEvent currentEvent; + private final Set alive = new HashSet<>(); + + public SpleefHandler(EventEngine plugin) { + this.plugin = plugin; + } + + @Override + public void onStart(ActiveEvent event) { + this.currentEvent = event; + alive.clear(); + alive.addAll(event.getParticipants()); + Bukkit.getPluginManager().registerEvents(this, plugin); + + // Jedem Spieler eine Schneeschaufel geben + for (UUID uuid : event.getParticipants()) { + Player p = Bukkit.getPlayer(uuid); + if (p != null) { + p.getInventory().addItem(new ItemStack(Material.DIAMOND_SHOVEL)); + } + } + broadcast("§a⛏ §eSpleef! §7Grabe Schnee unter deinen Gegnern weg. §7Wer fällt, verliert!"); + } + + @Override + public void onEnd(ActiveEvent event) { + HandlerList.unregisterAll(this); + this.currentEvent = null; + alive.clear(); + } + + @EventHandler + public void onBlockBreak(BlockBreakEvent e) { + Player p = e.getPlayer(); + if (currentEvent == null || !currentEvent.isParticipant(p)) return; + // Nur Spleef-Materialien erlaubt + if (!SPLEEF_MATERIALS.contains(e.getBlock().getType())) { + e.setCancelled(true); + return; + } + e.setDropItems(false); // Kein Item-Drop + p.playSound(p.getLocation(), Sound.BLOCK_SNOW_BREAK, 1f, 1f); + } + + @EventHandler + public void onDamage(EntityDamageEvent e) { + if (!(e.getEntity() instanceof Player p)) return; + if (currentEvent == null || !currentEvent.isParticipant(p)) return; + if (e.getCause() == EntityDamageEvent.DamageCause.FALL || + e.getCause() == EntityDamageEvent.DamageCause.VOID) { + e.setCancelled(true); + eliminate(p); + } else { + e.setDamage(0); // Kein PvP-Schaden + } + } + + @EventHandler + public void onDeath(PlayerDeathEvent e) { + if (currentEvent == null || !currentEvent.isParticipant(e.getEntity())) return; + e.setDeathMessage(null); + } + + private void eliminate(Player p) { + if (!alive.contains(p.getUniqueId())) return; + alive.remove(p.getUniqueId()); + currentEvent.removeParticipant(p); + p.setGameMode(org.bukkit.GameMode.SPECTATOR); + broadcast("§c⬇ §f" + p.getName() + " §7ist gefallen! §8[§e" + alive.size() + " §7übrig§8]"); + checkWinner(); + } + + private void checkWinner() { + if (alive.size() == 1) { + UUID winnerId = alive.iterator().next(); + Player winner = Bukkit.getPlayer(winnerId); + String name = winner != null ? winner.getName() : "?"; + currentEvent.addScore(winnerId, 100); + broadcast("§6✦ §e" + name + " §6gewinnt Spleef!"); + if (winner != null) winner.playSound(winner.getLocation(), Sound.UI_TOAST_CHALLENGE_COMPLETE, 1f, 1f); + plugin.getEventManager().endEvent(name + " hat gewonnen"); + } else if (alive.isEmpty()) { + plugin.getEventManager().endEvent("Unentschieden"); + } + } + + private void broadcast(String msg) { Bukkit.broadcastMessage(msg); } +} diff --git a/src/main/java/dev/viper/eventengine/events/builtin/SumoHandler.java b/src/main/java/dev/viper/eventengine/events/builtin/SumoHandler.java new file mode 100644 index 0000000..5729876 --- /dev/null +++ b/src/main/java/dev/viper/eventengine/events/builtin/SumoHandler.java @@ -0,0 +1,123 @@ +package dev.viper.eventengine.events.builtin; + +import dev.viper.eventengine.EventEngine; +import dev.viper.eventengine.model.ActiveEvent; +import org.bukkit.Bukkit; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.entity.PlayerDeathEvent; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * Sumo: + * - Kein normaler Schaden (nur Knockback) + * - Fall in Void/Wasser = ausgeschieden + * - Spieler erhalten Speed I + kein Schaden-Handling außer Knockback + */ +public class SumoHandler implements IEventHandler, Listener { + + private final EventEngine plugin; + private ActiveEvent currentEvent; + private final Set alive = new HashSet<>(); + private BukkitRunnable voidChecker; + + public SumoHandler(EventEngine plugin) { + this.plugin = plugin; + } + + @Override + public void onStart(ActiveEvent event) { + this.currentEvent = event; + alive.clear(); + alive.addAll(event.getParticipants()); + Bukkit.getPluginManager().registerEvents(this, plugin); + + // Teilnehmer vorbereiten + for (UUID uuid : event.getParticipants()) { + Player p = Bukkit.getPlayer(uuid); + if (p == null) continue; + p.addPotionEffect(new PotionEffect(PotionEffectType.SPEED, Integer.MAX_VALUE, 0, false, false)); + p.setHealth(Math.min(p.getMaxHealth(), 20)); + } + + // Void-Check alle 5 Ticks + voidChecker = new BukkitRunnable() { + @Override + public void run() { + if (currentEvent == null) { cancel(); return; } + for (UUID uuid : new HashSet<>(alive)) { + Player p = Bukkit.getPlayer(uuid); + if (p != null && p.getLocation().getY() < 0) { + eliminate(p); + } + } + } + }; + voidChecker.runTaskTimer(plugin, 5L, 5L); + + broadcast("§b🌊 §eSumo! §7Stoße deine Gegner in den Void! §7Kein Schaden — nur Knockback."); + } + + @Override + public void onEnd(ActiveEvent event) { + HandlerList.unregisterAll(this); + if (voidChecker != null) voidChecker.cancel(); + for (UUID uuid : event.getParticipants()) { + Player p = Bukkit.getPlayer(uuid); + if (p != null) p.removePotionEffect(PotionEffectType.SPEED); + } + this.currentEvent = null; + alive.clear(); + } + + // Schaden blockieren (außer Knockback bleibt) + @EventHandler + public void onDamage(EntityDamageEvent e) { + if (!(e.getEntity() instanceof Player p)) return; + if (currentEvent == null || !currentEvent.isParticipant(p)) return; + if (e.getCause() == EntityDamageEvent.DamageCause.VOID) { + e.setCancelled(true); + eliminate(p); + return; + } + // Kein HP-Verlust, aber Knockback bleibt + e.setDamage(0); + } + + @EventHandler + public void onDeath(PlayerDeathEvent e) { + if (currentEvent == null || !currentEvent.isParticipant(e.getEntity())) return; + e.setDeathMessage(null); + e.getEntity().spigot().respawn(); + } + + private void eliminate(Player p) { + if (!alive.contains(p.getUniqueId())) return; + alive.remove(p.getUniqueId()); + currentEvent.removeParticipant(p); + p.setGameMode(org.bukkit.GameMode.SPECTATOR); + broadcast("§c💧 §f" + p.getName() + " §7ist ins Wasser gefallen! §8[§e" + alive.size() + " §7übrig§8]"); + + if (alive.size() == 1) { + UUID winnerId = alive.iterator().next(); + Player winner = Bukkit.getPlayer(winnerId); + String name = winner != null ? winner.getName() : "?"; + currentEvent.addScore(winnerId, 100); + broadcast("§6✦ §e" + name + " §6gewinnt das Sumo-Event!"); + if (winner != null) winner.playSound(winner.getLocation(), Sound.UI_TOAST_CHALLENGE_COMPLETE, 1f, 1f); + plugin.getEventManager().endEvent(name + " hat gewonnen"); + } + } + + private void broadcast(String msg) { Bukkit.broadcastMessage(msg); } +} diff --git a/src/main/java/dev/viper/eventengine/events/builtin/SurvivalHandlers.java b/src/main/java/dev/viper/eventengine/events/builtin/SurvivalHandlers.java new file mode 100644 index 0000000..4e3ea79 --- /dev/null +++ b/src/main/java/dev/viper/eventengine/events/builtin/SurvivalHandlers.java @@ -0,0 +1,308 @@ +package dev.viper.eventengine.events.builtin; + +import dev.viper.eventengine.EventEngine; +import dev.viper.eventengine.model.ActiveEvent; +import org.bukkit.*; +import org.bukkit.attribute.Attribute; +import org.bukkit.entity.*; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.event.entity.PlayerDeathEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.*; + +// ═══════════════════════════════════════════════════════════════════ +// Zombie Belagerung +// ═══════════════════════════════════════════════════════════════════ +class ZombieSiegeHandler implements IEventHandler, Listener { + private final EventEngine plugin; private ActiveEvent current; + private BukkitRunnable spawner; private int wave = 0; + ZombieSiegeHandler(EventEngine p) { this.plugin = p; } + + @Override public void onStart(ActiveEvent e) { + current = e; + Bukkit.getPluginManager().registerEvents(this, plugin); + broadcast("§2🧟 §eZombie-Belagerung! §7Halte die Stellung — Zombies kommen in Wellen!"); + spawner = new BukkitRunnable() { + @Override public void run() { + if (current==null){cancel();return;} + wave++; + broadcast("§2🧟 §eZombie-Welle §c"+wave+"§e kommt!"); + Location loc = getSpawnLoc(); + if (loc==null) return; + int count = 5 + wave * 3; + Random rng = new Random(); + for (int i=0;i 3) z.addPotionEffect(new PotionEffect(PotionEffectType.SPEED,99999,1)); + } + } + }; + spawner.runTaskTimer(plugin, 20L, 30*20L); + } + @Override public void onEnd(ActiveEvent e) { HandlerList.unregisterAll(this); if(spawner!=null)spawner.cancel(); current=null; } + @EventHandler public void onKill(EntityDeathEvent e) { + if (current==null||!(e.getEntity() instanceof Zombie)) return; + Player killer=e.getEntity().getKiller(); + if (killer!=null&¤t.isParticipant(killer)) { current.addScore(killer.getUniqueId(),1); killer.sendActionBar("§2+1 §7Zombie Kill"); } + } + private Location getSpawnLoc() { for (UUID u:current.getParticipants()){Player p=Bukkit.getPlayer(u);if(p!=null)return p.getLocation();} return null; } + private void broadcast(String m){Bukkit.broadcastMessage(m);} +} + +// ═══════════════════════════════════════════════════════════════════ +// Wither Sturm +// ═══════════════════════════════════════════════════════════════════ +class WitherStormHandler implements IEventHandler, Listener { + private final EventEngine plugin; private ActiveEvent current; private Wither wither; + WitherStormHandler(EventEngine p){this.plugin=p;} + + @Override public void onStart(ActiveEvent e) { + current=e; Bukkit.getPluginManager().registerEvents(this, plugin); + broadcast("§8☠ §lWITHER STURM! §r§7Besiegt den Wither gemeinsam! Wer den letzten Schlag landet gewinnt den Jackpot!"); + Location loc=getCenter(); if(loc==null)return; + wither=(Wither)loc.getWorld().spawnEntity(loc.add(0,5,0), EntityType.WITHER); + wither.setCustomName("§8§l☠ DER WITHER ☠"); + wither.setCustomNameVisible(true); + } + @Override public void onEnd(ActiveEvent e){ HandlerList.unregisterAll(this); if(wither!=null&&!wither.isDead())wither.remove(); current=null; } + @EventHandler public void onWitherDeath(EntityDeathEvent e){ + if(current==null||!(e.getEntity() instanceof Wither)) return; + Player killer=e.getEntity().getKiller(); + if(killer!=null&¤t.isParticipant(killer)){ + current.addScore(killer.getUniqueId(),100); + broadcast("§6✦ §f"+killer.getName()+" §6hat den Wither besiegt und bekommt 100 Punkte!"); + } + plugin.getEventManager().endEvent("Wither besiegt"); + } + private Location getCenter(){if(current.getDefinition().hasRegion())return current.getDefinition().getRegion().getCenter(); for(UUID u:current.getParticipants()){Player p=Bukkit.getPlayer(u);if(p!=null)return p.getLocation();}return null;} + private void broadcast(String m){Bukkit.broadcastMessage(m);} +} + +// ═══════════════════════════════════════════════════════════════════ +// Drachen Event +// ═══════════════════════════════════════════════════════════════════ +class DragonFightHandler implements IEventHandler, Listener { + private final EventEngine plugin; private ActiveEvent current; + DragonFightHandler(EventEngine p){this.plugin=p;} + @Override public void onStart(ActiveEvent e){ + current=e; Bukkit.getPluginManager().registerEvents(this, plugin); + broadcast("§5🐉 §eDrachen-Event! §7Besiegt den Ender-Dragon! Wer den Killing Blow landet gewinnt den Jackpot!"); + } + @Override public void onEnd(ActiveEvent e){ HandlerList.unregisterAll(this); current=null; } + @EventHandler public void onDragonDeath(EntityDeathEvent e){ + if(current==null||!(e.getEntity() instanceof EnderDragon)) return; + Player killer=e.getEntity().getKiller(); + if(killer!=null&¤t.isParticipant(killer)){ + current.addScore(killer.getUniqueId(),200); + broadcast("§6✦ §f"+killer.getName()+" §6hat den Drachen getötet! §e+200 Punkte!"); + } + broadcast("§5🐉 §eDer Drache ist gefallen! §7Alle Teilnehmer erhalten eine Belohnung!"); + plugin.getEventManager().endEvent("Drache besiegt"); + } + private void broadcast(String m){Bukkit.broadcastMessage(m);} +} + +// ═══════════════════════════════════════════════════════════════════ +// Hardcore Runde — kein Respawn, wer lebt gewinnt +// ═══════════════════════════════════════════════════════════════════ +class HardcoreRoundHandler implements IEventHandler, Listener { + private final EventEngine plugin; private ActiveEvent current; private final Set alive=new HashSet<>(); + HardcoreRoundHandler(EventEngine p){this.plugin=p;} + @Override public void onStart(ActiveEvent e){ + current=e; alive.clear(); alive.addAll(e.getParticipants()); + Bukkit.getPluginManager().registerEvents(this, plugin); + broadcast("§c💀 §eHardcore-Runde! §7Kein Respawn — stirb einmal und du bist raus!"); + } + @Override public void onEnd(ActiveEvent e){ HandlerList.unregisterAll(this); current=null; alive.clear(); } + private void broadcast(String m) { Bukkit.broadcastMessage(m); } + @EventHandler public void onDeath(PlayerDeathEvent e){ + if(current==null||!current.isParticipant(e.getEntity())) return; + e.setDeathMessage(null); alive.remove(e.getEntity().getUniqueId()); + e.getEntity().setGameMode(GameMode.SPECTATOR); + broadcast("§c💀 §f"+e.getEntity().getName()+" §7ist ausgeschieden! §8[§e"+alive.size()+" §7übrig§8]"); + if(alive.size()==1){ UUID w=alive.iterator().next(); current.addScore(w,100); Player wp=Bukkit.getPlayer(w); broadcast("§6✦ "+(wp!=null?wp.getName():"?")+" §6gewinnt die Hardcore-Runde!"); plugin.getEventManager().endEvent("Gewinner ermittelt"); } + else if(alive.isEmpty()) plugin.getEventManager().endEvent("Unentschieden"); + } +} + +// ═══════════════════════════════════════════════════════════════════ +// Insel Überleben — punkte pro Sekunde +// ═══════════════════════════════════════════════════════════════════ +class IslandSurvivalHandler implements IEventHandler { + private final EventEngine plugin; private BukkitRunnable ticker; + IslandSurvivalHandler(EventEngine p){this.plugin=p;} + @Override public void onStart(ActiveEvent e){ + Bukkit.broadcastMessage(plugin.prefix()+"§2🏝 §eInsel-Überleben! §7Überlebt und sammelt Punkte pro Sekunde!"); + ticker=new BukkitRunnable(){ @Override public void run(){ if(e.getState()==dev.viper.eventengine.model.ActiveEvent.State.ENDED){cancel();return;} for(UUID u:e.getParticipants()){Player p=Bukkit.getPlayer(u);if(p!=null)e.addScore(u,1);} }}; + ticker.runTaskTimer(plugin,20L,20L); + } + @Override public void onEnd(ActiveEvent e){ if(ticker!=null)ticker.cancel(); } +} + +// ═══════════════════════════════════════════════════════════════════ +// Nether Sprint — Punkte für Nether-Items +// ═══════════════════════════════════════════════════════════════════ +class NetherRunHandler implements IEventHandler, Listener { + private final EventEngine plugin; private ActiveEvent current; + private static final Map NETHER_SCORES=new EnumMap<>(Material.class); + static { NETHER_SCORES.put(Material.BLAZE_ROD,5); NETHER_SCORES.put(Material.NETHER_QUARTZ_ORE,2); NETHER_SCORES.put(Material.MAGMA_CREAM,3); NETHER_SCORES.put(Material.GHAST_TEAR,8); NETHER_SCORES.put(Material.WITHER_SKELETON_SKULL,20); NETHER_SCORES.put(Material.ANCIENT_DEBRIS,15); } + NetherRunHandler(EventEngine p){this.plugin=p;} + @Override public void onStart(ActiveEvent e){ current=e; Bukkit.getPluginManager().registerEvents(this,plugin); broadcast("§c🔥 §eNether-Sprint! §7Sammle Nether-Items für Punkte! Wer hat am Ende das meiste?"); } + @Override public void onEnd(ActiveEvent e){ + HandlerList.unregisterAll(this); + // Inventar-Scan beim Ende + for(UUID uuid:e.getParticipants()){ Player p=Bukkit.getPlayer(uuid); if(p==null)continue; for(ItemStack item:p.getInventory().getContents()){ if(item==null)continue; Integer pts=NETHER_SCORES.get(item.getType()); if(pts!=null) e.addScore(uuid,pts*item.getAmount()); } } + current=null; + } + private void broadcast(String m){Bukkit.broadcastMessage(m);} +} + +// ═══════════════════════════════════════════════════════════════════ +// Ausdauer Test — Schaden über Zeit, wer überlebt länger = mehr Punkte +// ═══════════════════════════════════════════════════════════════════ +class EnduranceHandler implements IEventHandler, Listener { + private final EventEngine plugin; private ActiveEvent current; private BukkitRunnable ticker; + EnduranceHandler(EventEngine p){this.plugin=p;} + @Override public void onStart(ActiveEvent e){ + current=e; Bukkit.getPluginManager().registerEvents(this,plugin); + broadcast("§9⏱ §eAusdauer-Test! §7Jede Sekunde wachsen die Gefahren — wer hält am längsten durch?"); + ticker=new BukkitRunnable(){ + int tick=0; + @Override public void run(){ + if(current==null){cancel();return;} tick++; + for(UUID uuid:current.getParticipants()){ + Player p=Bukkit.getPlayer(uuid); if(p==null) continue; + current.addScore(uuid,1); + // Nach 30s: Gift; nach 60s: Hunger; nach 90s: Langsamkeit + if(tick>=30) p.addPotionEffect(new PotionEffect(PotionEffectType.POISON,40,0,false,false)); + if(tick>=60) p.setFoodLevel(Math.max(0,p.getFoodLevel()-1)); + if(tick>=90) p.addPotionEffect(new PotionEffect(PotionEffectType.SLOWNESS,40,1,false,false)); + } + } + }; + ticker.runTaskTimer(plugin,20L,20L); + } + @Override public void onEnd(ActiveEvent e){ HandlerList.unregisterAll(this); if(ticker!=null)ticker.cancel(); current=null; } + private void broadcast(String m) { Bukkit.broadcastMessage(m); } + @EventHandler public void onDeath(PlayerDeathEvent e){ if(current!=null&¤t.isParticipant(e.getEntity())){ e.setDeathMessage(null); current.removeParticipant(e.getEntity()); e.getEntity().setGameMode(GameMode.SPECTATOR); } } +} + +// ═══════════════════════════════════════════════════════════════════ +// Raid Plus — verstärkte Raid-Wellen +// ═══════════════════════════════════════════════════════════════════ +class RaidsPlusHandler implements IEventHandler, Listener { + private final EventEngine plugin; private ActiveEvent current; private BukkitRunnable spawner; + private static final EntityType[] RAID_MOBS={EntityType.PILLAGER,EntityType.VINDICATOR,EntityType.WITCH,EntityType.EVOKER,EntityType.RAVAGER}; + RaidsPlusHandler(EventEngine p){this.plugin=p;} + @Override public void onStart(ActiveEvent e){ + current=e; Bukkit.getPluginManager().registerEvents(this,plugin); + broadcast("§4🏹 §eRaid+! §7Verstärkte Illager-Angriffe — besiegt alle Wellen!"); + spawner=new BukkitRunnable(){ + int wave=0; final Random rng=new Random(); + @Override public void run(){ + if(current==null){cancel();return;} wave++; + broadcast("§4🏹 §eRaid-Welle §c"+wave+"§e startet!"); + Location loc=getCenter(); if(loc==null)return; + int count=4+wave*2; + for(int i=0;i BOUNTY=new EnumMap<>(EntityType.class); + static{ BOUNTY.put(EntityType.ZOMBIE,1);BOUNTY.put(EntityType.SKELETON,1);BOUNTY.put(EntityType.CREEPER,2);BOUNTY.put(EntityType.ENDERMAN,3);BOUNTY.put(EntityType.WITCH,4);BOUNTY.put(EntityType.ELDER_GUARDIAN,10);BOUNTY.put(EntityType.WITHER_SKELETON,6);BOUNTY.put(EntityType.BLAZE,5); } + MonsterHuntHandler(EventEngine p){this.plugin=p;} + @Override public void onStart(ActiveEvent e){ current=e; Bukkit.getPluginManager().registerEvents(this,plugin); broadcast("§c🎯 §eMonster-Jagd! §7Töte Mobs für Punkte. Rare Mobs = mehr Punkte!"); } + @Override public void onEnd(ActiveEvent e){ HandlerList.unregisterAll(this); current=null; } + @EventHandler public void onKill(EntityDeathEvent e){ if(current==null) return; Player k=e.getEntity().getKiller(); if(k==null||!current.isParticipant(k))return; Integer pts=BOUNTY.get(e.getEntityType()); if(pts!=null){ current.addScore(k.getUniqueId(),pts); k.sendActionBar("§c+"+pts+" §7["+e.getEntityType().name()+"]"); } } + private void broadcast(String m){Bukkit.broadcastMessage(m);} +} + +// ═══════════════════════════════════════════════════════════════════ +// Schwerkraft Chaos — Spieler werden hochgeworfen +// ═══════════════════════════════════════════════════════════════════ +class GravityShiftHandler implements IEventHandler { + private final EventEngine plugin; private BukkitRunnable ticker; + GravityShiftHandler(EventEngine p){this.plugin=p;} + @Override public void onStart(ActiveEvent e){ + Bukkit.broadcastMessage(plugin.prefix()+"§b🌀 §eSchwerkraft-Chaos! §7Die Schwerkraft verhält sich zufällig!"); + final Random rng=new Random(); + ticker=new BukkitRunnable(){ @Override public void run(){ if(e.getState()==dev.viper.eventengine.model.ActiveEvent.State.ENDED){cancel();return;} for(UUID u:e.getParticipants()){Player p=Bukkit.getPlayer(u);if(p==null)continue; if(rng.nextInt(5)==0) p.setVelocity(p.getVelocity().setY(1.5+rng.nextDouble()*2)); e.addScore(u,1); } }}; + ticker.runTaskTimer(plugin,20L,20L); + } + @Override public void onEnd(ActiveEvent e){ if(ticker!=null)ticker.cancel(); } +} + +// ═══════════════════════════════════════════════════════════════════ +// Random Spawn — Spieler werden zufällig in Arena gespawnt, kein Respawn +// ═══════════════════════════════════════════════════════════════════ +class RandomSpawnHandler implements IEventHandler, Listener { + private final EventEngine plugin; private ActiveEvent current; private final Set alive=new HashSet<>(); + RandomSpawnHandler(EventEngine p){this.plugin=p;} + @Override public void onStart(ActiveEvent e){ + current=e; alive.clear(); alive.addAll(e.getParticipants()); + Bukkit.getPluginManager().registerEvents(this,plugin); + final Random rng=new Random(); + for(UUID uuid:e.getParticipants()){ Player p=Bukkit.getPlayer(uuid); if(p==null)continue; Location loc=e.getDefinition().hasRegion()?e.getDefinition().getRegion().randomLocation(rng):p.getWorld().getSpawnLocation(); p.teleport(loc); } + broadcast("§d🎲 §eRandom Spawn! §7Alle wurden zufällig verteilt — kein Respawn. Letzter gewinnt!"); + } + @Override public void onEnd(ActiveEvent e){ HandlerList.unregisterAll(this); current=null; alive.clear(); } + @EventHandler public void onDeath(PlayerDeathEvent e){ if(current==null||!current.isParticipant(e.getEntity()))return; e.setDeathMessage(null); alive.remove(e.getEntity().getUniqueId()); e.getEntity().setGameMode(GameMode.SPECTATOR); broadcast("§c☠ §f"+e.getEntity().getName()+" §7scheidet aus! §8[§e"+alive.size()+" §7übrig§8]"); if(alive.size()==1){UUID w=alive.iterator().next();current.addScore(w,100);Player wp=Bukkit.getPlayer(w);broadcast("§6✦ "+(wp!=null?wp.getName():"?")+" §6gewinnt!");plugin.getEventManager().endEvent("Gewinner ermittelt");}else if(alive.isEmpty())plugin.getEventManager().endEvent("Unentschieden"); } + private void broadcast(String m){Bukkit.broadcastMessage(m);} +} + +// ═══════════════════════════════════════════════════════════════════ +// Manhunt — 1 Gejagter, alle anderen Jäger +// ═══════════════════════════════════════════════════════════════════ +class ManhuntHandler implements IEventHandler, Listener { + private final EventEngine plugin; private ActiveEvent current; private UUID target; + ManhuntHandler(EventEngine p){this.plugin=p;} + @Override public void onStart(ActiveEvent e){ + current=e; Bukkit.getPluginManager().registerEvents(this,plugin); + List parts=new ArrayList<>(e.getParticipants()); + if(parts.isEmpty())return; + target=parts.get(new Random().nextInt(parts.size())); + Player tp=Bukkit.getPlayer(target); + broadcast("§c🎯 §eManhunt! §fGejagter: §c"+(tp!=null?tp.getName():"?")+" §7— alle anderen sind Jäger!"); + if(tp!=null){ tp.sendTitle("§cDU BIST GEJAGT!","§7Überlebe so lange wie möglich!",10,60,20); tp.addPotionEffect(new PotionEffect(PotionEffectType.SPEED,99999,0,false,false)); } + } + @Override public void onEnd(ActiveEvent e){ HandlerList.unregisterAll(this); current=null; target=null; } + @EventHandler public void onDeath(PlayerDeathEvent e){ + if(current==null||!current.isParticipant(e.getEntity()))return; + e.setDeathMessage(null); + if(e.getEntity().getUniqueId().equals(target)){ + broadcast("§6✦ §7Die Jäger haben gewonnen! §c"+(e.getEntity().getName())+" §7wurde gefangen!"); + for(UUID u:current.getParticipants()) if(!u.equals(target)) current.addScore(u,50); + plugin.getEventManager().endEvent("Gejagter gefangen"); + } else { + broadcast("§7Ein Jäger ist gestorben. §c"+e.getEntity().getName()+" §7scheidet aus."); + e.getEntity().setGameMode(GameMode.SPECTATOR); + } + } + // Kompatibilitätsmethode + private void addPotion(Player p, PotionEffect effect){ p.addPotionEffect(effect); } + private void broadcast(String m){Bukkit.broadcastMessage(m);} +} \ No newline at end of file diff --git a/src/main/java/dev/viper/eventengine/events/builtin/TriviaHandler.java b/src/main/java/dev/viper/eventengine/events/builtin/TriviaHandler.java new file mode 100644 index 0000000..434bc74 --- /dev/null +++ b/src/main/java/dev/viper/eventengine/events/builtin/TriviaHandler.java @@ -0,0 +1,124 @@ +package dev.viper.eventengine.events.builtin; + +import dev.viper.eventengine.EventEngine; +import dev.viper.eventengine.model.ActiveEvent; +import org.bukkit.Bukkit; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.player.AsyncPlayerChatEvent; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.*; + +/** + * Minecraft-Trivia: + * - Frage wird im Chat gestellt + * - Erster der richtig antwortet bekommt Punkte + * - 10 Fragen, danach endet das Event + */ +public class TriviaHandler implements IEventHandler, Listener { + + /** Öffentlich für Subklassen (GeneralKnowledgeQuizHandler etc.) */ + public static final class QuestionEntry { + public final String question, answer; public final int points; + public QuestionEntry(String q, String a, int p){this.question=q;this.answer=a;this.points=p;} + } + + private static final List QUESTIONS = List.of( + new QuestionEntry("Wie viele Blöcke hoch ist eine Karotte wenn sie voll gewachsen ist?", "1", 10), + new QuestionEntry("Was braucht man um ein Bett zu craften? (Material)", "Wolle", 10), + new QuestionEntry("Wie heißt der End-Boss?", "Ender Dragon", 15), + new QuestionEntry("Was dropped ein Blaze?", "Blaze Rod", 10), + new QuestionEntry("Aus wie vielen Blöcken besteht ein Chunk?", "16", 15), + new QuestionEntry("Welches Erz ist das seltenste in Minecraft?", "Netherit", 20), + new QuestionEntry("Was ist die maximale Verzauberungsstufe in Vanilla?", "30", 10), + new QuestionEntry("Wie nennt man den grünen feindlichen Mob ohne Arme?", "Creeper", 5), + new QuestionEntry("Welches Tier liefert Wolle?", "Schaf", 5), + new QuestionEntry("Was dropped ein Ender Dragon?", "Dragon Egg", 20), + new QuestionEntry("Wie heißt der Minecraft-Entwickler?", "Notch", 10), + new QuestionEntry("Aus welchem Material werden Schienen gecraftet?", "Eisen", 10), + new QuestionEntry("Was ist die tiefste Y-Koordinate in 1.18+?", "-64", 15), + new QuestionEntry("Wie viele Lebensherzen hat ein Spieler maximal (vanilla)?", "10", 10), + new QuestionEntry("Was braucht man um einen Kompass zu craften?", "Redstone", 15) + ); + + private final EventEngine plugin; + private ActiveEvent currentEvent; + private QuestionEntry currentQuestion; + private boolean answered; + private int questionIndex = 0; + private final List shuffled = new ArrayList<>(); + private BukkitRunnable questionRunner; + + public TriviaHandler(EventEngine plugin) { this.plugin = plugin; this.externalQuestions = null; } + + private final List externalQuestions; + public TriviaHandler(EventEngine plugin, List questions) { this.plugin = plugin; this.externalQuestions = questions; } + + @Override + public void onStart(ActiveEvent event) { + this.currentEvent = event; + this.questionIndex = 0; + this.answered = false; + shuffled.clear(); + shuffled.addAll(externalQuestions != null ? externalQuestions : QUESTIONS); + Collections.shuffle(shuffled); + + Bukkit.getPluginManager().registerEvents(this, plugin); + broadcast("§e❓ §eMinecraft-Quiz! §7Beantworte Fragen im Chat!"); + + questionRunner = new BukkitRunnable() { + @Override + public void run() { + if (currentEvent == null) { cancel(); return; } + if (questionIndex >= Math.min(10, shuffled.size())) { + cancel(); + plugin.getEventManager().endEvent("Alle Fragen beantwortet"); + return; + } + if (!answered && currentQuestion != null) { + broadcast("§c⏰ §7Zu langsam! Die Antwort war: §e" + currentQuestion.answer); + } + nextQuestion(); + } + }; + questionRunner.runTaskTimer(plugin, 60L, 200L); // 10s pro Frage + } + + @Override + public void onEnd(ActiveEvent event) { + HandlerList.unregisterAll(this); + if (questionRunner != null) questionRunner.cancel(); + this.currentEvent = null; + } + + private void nextQuestion() { + if (questionIndex >= shuffled.size()) return; + currentQuestion = shuffled.get(questionIndex++); + answered = false; + broadcast("§8§m══════════════════════════════"); + broadcast("§e❓ Frage " + questionIndex + ": §f" + currentQuestion.question); + broadcast("§7Punkte: §e" + currentQuestion.points + " §8| §7Tippe die Antwort im Chat!"); + } + + @EventHandler(priority = EventPriority.LOWEST) + public void onChat(AsyncPlayerChatEvent e) { + if (currentEvent == null || currentQuestion == null || answered) return; + Player p = e.getPlayer(); + String msg = e.getMessage().trim(); + + if (msg.equalsIgnoreCase(currentQuestion.answer)) { + answered = true; + currentEvent.addScore(p.getUniqueId(), currentQuestion.points); + broadcast("§a✔ §f" + p.getName() + " §7hat die Antwort gewusst! §a+" + currentQuestion.points + " §7Punkte"); + Bukkit.getScheduler().runTask(plugin, () -> + p.playSound(p.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 1f, 1.5f)); + } + } + + private void broadcast(String msg) { Bukkit.broadcastMessage(msg); } +} diff --git a/src/main/java/dev/viper/eventengine/events/custom/CustomEventBuilder.java b/src/main/java/dev/viper/eventengine/events/custom/CustomEventBuilder.java new file mode 100644 index 0000000..2a15f23 --- /dev/null +++ b/src/main/java/dev/viper/eventengine/events/custom/CustomEventBuilder.java @@ -0,0 +1,219 @@ +package dev.viper.eventengine.events.custom; + +import dev.viper.eventengine.EventEngine; +import dev.viper.eventengine.util.ColorUtil; +import dev.viper.eventengine.model.EventCategory; +import dev.viper.eventengine.model.EventDefinition; +import org.bukkit.entity.Player; + +import java.util.*; + +/** + * Geführter CLI-Wizard für Admins zum Erstellen von Custom Events. + * + * Ablauf: + * /event create → Startet den Wizard + * → Spieler antwortet auf Fragen über Chat + * + * Wird über EventCreateListener empfangen. + */ +public class CustomEventBuilder { + + public enum Step { + DISPLAY_NAME, + DESCRIPTION, + CATEGORY, + DURATION, + MIN_PLAYERS, + MAX_PLAYERS, + ANNOUNCEMENT, + START_COMMANDS, + END_COMMANDS, + REWARDS, + CONFIRM + } + + private final EventEngine plugin; + private final Player player; + private final EventDefinition def; + private Step currentStep; + + public CustomEventBuilder(EventEngine plugin, Player player, String id) { + this.plugin = plugin; + this.player = player; + this.def = new EventDefinition(id.toLowerCase()); + this.currentStep = Step.DISPLAY_NAME; + promptCurrentStep(); + } + + // ─── Eingabe verarbeiten ─────────────────────────────────────────────── + + public boolean handleInput(String input) { + switch (currentStep) { + case DISPLAY_NAME -> { + def.setDisplayName(input); + advance(); + } + case DESCRIPTION -> { + def.setDescription(input); + advance(); + } + case CATEGORY -> { + try { + EventCategory cat = EventCategory.valueOf(input.toUpperCase()); + def.setCategory(cat); + advance(); + } catch (Exception e) { + sendError("Ungültige Kategorie. Wähle: " + getCategoryList()); + } + } + case DURATION -> { + if (input.equalsIgnoreCase("skip") || input.equals("0")) { + def.setDurationSeconds(0); + advance(); + } else { + try { + int sec = Integer.parseInt(input); + if (sec < 0) throw new NumberFormatException(); + def.setDurationSeconds(sec); + advance(); + } catch (NumberFormatException e) { + sendError("Bitte eine Zahl in Sekunden eingeben (0 = unbegrenzt)."); + } + } + } + case MIN_PLAYERS -> { + try { + int n = Integer.parseInt(input); + if (n < 1) throw new NumberFormatException(); + def.setMinPlayers(n); + advance(); + } catch (NumberFormatException e) { + sendError("Bitte eine Zahl ≥ 1 eingeben."); + } + } + case MAX_PLAYERS -> { + try { + int n = Integer.parseInt(input); + if (n < def.getMinPlayers()) throw new NumberFormatException(); + def.setMaxPlayers(n); + advance(); + } catch (NumberFormatException e) { + sendError("Bitte eine Zahl ≥ " + def.getMinPlayers() + " eingeben."); + } + } + case ANNOUNCEMENT -> { + if (input.equalsIgnoreCase("skip")) { + // Default beibehalten + } else { + def.setAnnouncement(ColorUtil.color(input)); + } + advance(); + } + case START_COMMANDS -> { + if (!input.equalsIgnoreCase("skip")) { + List cmds = parseCommandList(input); + def.setStartCommands(cmds); + } + advance(); + } + case END_COMMANDS -> { + if (!input.equalsIgnoreCase("skip")) { + List cmds = parseCommandList(input); + def.setEndCommands(cmds); + } + advance(); + } + case REWARDS -> { + if (!input.equalsIgnoreCase("skip")) { + List rewards = parseCommandList(input); + def.setRewards(rewards); + } + advance(); + } + case CONFIRM -> { + if (input.equalsIgnoreCase("ja") || input.equalsIgnoreCase("yes")) { + plugin.getEventRegistry().register(def); + send("§a✔ Custom Event §e'" + def.getDisplayName() + "' §awurde erstellt und gespeichert!"); + send("§7ID: §f" + def.getId() + " §8| §7Start: §f/event start " + def.getId()); + return true; // fertig + } else { + send("§cAbgebrochen. Das Event wurde nicht gespeichert."); + return true; + } + } + } + return false; // noch nicht fertig + } + + // ─── Navigation ──────────────────────────────────────────────────────── + + private void advance() { + Step[] steps = Step.values(); + int idx = currentStep.ordinal() + 1; + if (idx < steps.length) { + currentStep = steps[idx]; + promptCurrentStep(); + } + } + + private void promptCurrentStep() { + String p = plugin.prefix(); + switch (currentStep) { + case DISPLAY_NAME -> send(p + "§e[1/10] §7Anzeigename des Events: §f(z.B. 'Mein Spaß-Event')"); + case DESCRIPTION -> send(p + "§e[2/10] §7Beschreibung: §f(kurze Info was passiert)"); + case CATEGORY -> send(p + "§e[3/10] §7Kategorie: §f" + getCategoryList()); + case DURATION -> send(p + "§e[4/10] §7Dauer in Sekunden: §f(0 oder 'skip' = unbegrenzt)"); + case MIN_PLAYERS -> send(p + "§e[5/10] §7Mindestspieler: §f(Standard: 1)"); + case MAX_PLAYERS -> send(p + "§e[6/10] §7Maximale Spieler: §f(Standard: 50)"); + case ANNOUNCEMENT -> send(p + "§e[7/10] §7Ankündigungstext: §f(oder 'skip' für Standard, &c = Farbe)"); + case START_COMMANDS -> send(p + "§e[8/10] §7Start-Befehle: §f(kommagetrennt, %player%=Spieler, oder 'skip')"); + case END_COMMANDS -> send(p + "§e[9/10] §7End-Befehle: §f(kommagetrennt, oder 'skip')"); + case REWARDS -> send(p + "§e[10/10] §7Belohnungen: §f(Befehle pro Teilnehmer, %player%, oder 'skip')"); + case CONFIRM -> { + sendSummary(); + send(p + "§eSpeichern? §a[ja] §c[nein]"); + } + } + } + + private void sendSummary() { + send("§8§m══════════════════════════════"); + send("§6 Event Zusammenfassung"); + send("§7 ID: §f" + def.getId()); + send("§7 Name: §f" + def.getDisplayName()); + send("§7 Kategorie: §f" + def.getCategory().getDisplayName()); + send("§7 Dauer: §f" + (def.getDurationSeconds() == 0 ? "∞" : def.getDurationSeconds() + "s")); + send("§7 Spieler: §f" + def.getMinPlayers() + "–" + def.getMaxPlayers()); + send("§7 Start-Befehle: §f" + def.getStartCommands().size()); + send("§7 Belohnungen: §f" + def.getRewards().size()); + send("§8§m══════════════════════════════"); + } + + // ─── Hilfsmethoden ───────────────────────────────────────────────────── + + private List parseCommandList(String input) { + List result = new ArrayList<>(); + for (String part : input.split(",")) { + String trimmed = part.trim(); + if (!trimmed.isEmpty()) result.add(trimmed); + } + return result; + } + + private String getCategoryList() { + StringBuilder sb = new StringBuilder(); + for (EventCategory cat : EventCategory.values()) { + if (sb.length() > 0) sb.append("§7, "); + sb.append("§e").append(cat.name()); + } + return sb.toString(); + } + + private void send(String msg) { player.sendMessage(msg); } + private void sendError(String msg) { player.sendMessage("§c✗ " + msg); } + + public Step getCurrentStep() { return currentStep; } + public EventDefinition getDef() { return def; } + public Player getPlayer() { return player; } +} diff --git a/src/main/java/dev/viper/eventengine/gui/EventGUI.java b/src/main/java/dev/viper/eventengine/gui/EventGUI.java new file mode 100644 index 0000000..234ca59 --- /dev/null +++ b/src/main/java/dev/viper/eventengine/gui/EventGUI.java @@ -0,0 +1,225 @@ +package dev.viper.eventengine.gui; + +import dev.viper.eventengine.EventEngine; +import dev.viper.eventengine.model.EventCategory; +import dev.viper.eventengine.model.EventDefinition; +import dev.viper.eventengine.manager.EventManager; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * Haupt-GUI für EventEngine: + * Seite 1 → Kategorien-Übersicht + * Seite 2 → Events der gewählten Kategorie + * Klick auf Event → Startet das Event (Admin) + */ +public class EventGUI implements Listener { + + private static final String MAIN_TITLE = "§8✦ §6EventEngine §8✦ Kategorien"; + private static final String EVENT_TITLE_PREFIX = "§8✦ §6Events: "; + + private final EventEngine plugin; + // Welche Kategorie der Spieler gerade betrachtet + private final Map categoryView = new HashMap<>(); + // Welche Seite der Spieler bei Events ist + private final Map pagemap = new HashMap<>(); + + public EventGUI(EventEngine plugin) { + this.plugin = plugin; + } + + // ─── Öffnen ──────────────────────────────────────────────────────────── + + public void openMain(Player player) { + Inventory inv = Bukkit.createInventory(null, 54, MAIN_TITLE); + fillBorder(inv, Material.BLACK_STAINED_GLASS_PANE); + + // Kategorien anzeigen + EventCategory[] cats = EventCategory.values(); + int[] slots = {10, 11, 12, 13, 14, 15, 16, 28, 29, 30}; + for (int i = 0; i < cats.length && i < slots.length; i++) { + EventCategory cat = cats[i]; + long count = plugin.getEventRegistry().getAll().stream() + .filter(d -> d.getCategory() == cat).count(); + ItemStack item = makeItem(cat.getIcon(), + "§6" + cat.getDisplayName(), + Arrays.asList("§7" + count + " Events", "", "§eKlicken zum Anzeigen")); + inv.setItem(slots[i], item); + } + + // Status-Info + String statusText; + if (plugin.getEventManager().isRunning()) { + statusText = "§a▶ Läuft: §f" + plugin.getEventManager().getCurrentEvent().getDefinition().getDisplayName(); + } else { + statusText = "§7Kein Event aktiv"; + } + ItemStack status = makeItem(Material.CLOCK, "§eStatus", Arrays.asList(statusText, + "§7Gesamt Events: §f" + plugin.getEventRegistry().count())); + inv.setItem(4, status); + + // Zufälliges Event starten + ItemStack random = makeItem(Material.ENDER_PEARL, "§d✦ Zufälliges Event", + Arrays.asList("§7Startet ein zufälliges Event", "§7aus der Rotation.", "", "§eKlicken")); + inv.setItem(40, random); + + player.openInventory(inv); + } + + public void openCategory(Player player, EventCategory category, int page) { + List events = plugin.getEventRegistry().getByCategory(category); + int perPage = 28; + int totalPages = Math.max(1, (int) Math.ceil(events.size() / (double) perPage)); + page = Math.max(0, Math.min(page, totalPages - 1)); + + categoryView.put(player.getUniqueId(), category); + pagemap.put(player.getUniqueId(), page); + + String title = EVENT_TITLE_PREFIX + category.getDisplayName() + " §8(" + (page + 1) + "/" + totalPages + ")"; + Inventory inv = Bukkit.createInventory(null, 54, title); + fillBorder(inv, Material.GRAY_STAINED_GLASS_PANE); + + List pageEvents = events.stream() + .skip((long) page * perPage) + .limit(perPage) + .collect(Collectors.toList()); + + int[] contentSlots = buildContentSlots(); + for (int i = 0; i < pageEvents.size() && i < contentSlots.length; i++) { + EventDefinition def = pageEvents.get(i); + List lore = new ArrayList<>(); + lore.add("§7" + def.getDescription()); + lore.add(""); + lore.add("§7Dauer: §f" + EventManager.formatTime(def.getDurationSeconds())); + lore.add("§7Spieler: §f" + def.getMinPlayers() + "–" + def.getMaxPlayers()); + if (def.isCustom()) lore.add("§d✦ Custom Event"); + lore.add(""); + lore.add("§aKlicken zum Starten"); + + Material icon = def.getCategory().getIcon(); + if (plugin.getEventManager().isRunning() && + plugin.getEventManager().getCurrentEvent().getDefinition().getId().equals(def.getId())) { + icon = Material.LIME_CONCRETE; + } + inv.setItem(contentSlots[i], makeItem(icon, "§e" + def.getDisplayName(), lore)); + } + + // Navigation + if (page > 0) { + inv.setItem(45, makeItem(Material.ARROW, "§7◀ Zurück", List.of("§7Seite " + page))); + } + inv.setItem(49, makeItem(Material.BARRIER, "§c← Hauptmenü", List.of("§7Zurück zur Kategorien-Übersicht"))); + if (page < totalPages - 1) { + inv.setItem(53, makeItem(Material.ARROW, "§7Weiter ▶", List.of("§7Seite " + (page + 2)))); + } + + player.openInventory(inv); + } + + // ─── Click-Handler ───────────────────────────────────────────────────── + + @EventHandler + public void onClick(InventoryClickEvent event) { + if (!(event.getWhoClicked() instanceof Player player)) return; + String title = event.getView().getTitle(); + if (!title.startsWith("§8✦ §6EventEngine") && !title.startsWith(EVENT_TITLE_PREFIX)) return; + + event.setCancelled(true); + ItemStack clicked = event.getCurrentItem(); + if (clicked == null || clicked.getType() == Material.AIR) return; + if (clicked.getType() == Material.BLACK_STAINED_GLASS_PANE || + clicked.getType() == Material.GRAY_STAINED_GLASS_PANE) return; + + String itemName = ChatColor.stripColor(clicked.getItemMeta() != null ? + clicked.getItemMeta().getDisplayName() : ""); + + if (title.equals(MAIN_TITLE)) { + handleMainClick(player, itemName, clicked); + } else { + handleCategoryClick(player, title, itemName, clicked, event.getSlot()); + } + } + + private void handleMainClick(Player player, String itemName, ItemStack item) { + if (itemName.equals("✦ Zufälliges Event")) { + plugin.getEventRegistry().getWeightedRandom() + .ifPresent(def -> plugin.getEventManager().startEvent(def, player)); + player.closeInventory(); + return; + } + // Kategorien-Match + for (EventCategory cat : EventCategory.values()) { + if (item.getType() == cat.getIcon()) { + openCategory(player, cat, 0); + return; + } + } + } + + private void handleCategoryClick(Player player, String title, String itemName, ItemStack item, int slot) { + if (item.getType() == Material.BARRIER) { + openMain(player); + return; + } + if (item.getType() == Material.ARROW) { + EventCategory cat = categoryView.get(player.getUniqueId()); + int page = pagemap.getOrDefault(player.getUniqueId(), 0); + if (slot == 45) openCategory(player, cat, page - 1); + else openCategory(player, cat, page + 1); + return; + } + + // Event-Klick: Name raussuchen + String stripped = itemName.trim(); + plugin.getEventRegistry().getAll().stream() + .filter(d -> d.getDisplayName().equals(stripped)) + .findFirst() + .ifPresent(def -> { + player.closeInventory(); + plugin.getEventManager().startEvent(def, player); + }); + } + + // ─── Utility ─────────────────────────────────────────────────────────── + + private ItemStack makeItem(Material mat, String name, List lore) { + ItemStack item = new ItemStack(mat); + ItemMeta meta = item.getItemMeta(); + if (meta == null) return item; + meta.setDisplayName(name); + meta.setLore(lore); + item.setItemMeta(meta); + return item; + } + + private void fillBorder(Inventory inv, Material mat) { + ItemStack glass = makeItem(mat, " ", List.of()); + int size = inv.getSize(); + for (int i = 0; i < 9; i++) inv.setItem(i, glass); + for (int i = size - 9; i < size; i++) inv.setItem(i, glass); + for (int i = 9; i < size - 9; i += 9) inv.setItem(i, glass); + for (int i = 17; i < size - 9; i += 9) inv.setItem(i, glass); + } + + private int[] buildContentSlots() { + // Slots 10-16, 19-25, 28-34, 37-43 (ohne Rand) + List slots = new ArrayList<>(); + for (int row = 1; row <= 4; row++) { + for (int col = 1; col <= 7; col++) { + slots.add(row * 9 + col); + } + } + return slots.stream().mapToInt(Integer::intValue).toArray(); + } +} diff --git a/src/main/java/dev/viper/eventengine/listener/EventCreateListener.java b/src/main/java/dev/viper/eventengine/listener/EventCreateListener.java new file mode 100644 index 0000000..ba5ec09 --- /dev/null +++ b/src/main/java/dev/viper/eventengine/listener/EventCreateListener.java @@ -0,0 +1,67 @@ +package dev.viper.eventengine.listener; + +import dev.viper.eventengine.EventEngine; +import dev.viper.eventengine.events.custom.CustomEventBuilder; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.AsyncPlayerChatEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * Leitet Chat-Nachrichten von Admins im Builder-Modus an CustomEventBuilder weiter. + */ +public class EventCreateListener implements Listener { + + private final EventEngine plugin; + // Laufende Builder-Sessions + private final Map sessions = new HashMap<>(); + + public EventCreateListener(EventEngine plugin) { + this.plugin = plugin; + } + + public void startSession(Player player, String id) { + if (sessions.containsKey(player.getUniqueId())) { + player.sendMessage(plugin.prefix() + "§cDu hast bereits einen laufenden Builder. Schreibe 'abbrechen' um ihn zu stoppen."); + return; + } + sessions.put(player.getUniqueId(), new CustomEventBuilder(plugin, player, id)); + } + + public boolean hasSession(Player player) { + return sessions.containsKey(player.getUniqueId()); + } + + @EventHandler(priority = EventPriority.LOWEST) + public void onChat(AsyncPlayerChatEvent event) { + Player player = event.getPlayer(); + CustomEventBuilder builder = sessions.get(player.getUniqueId()); + if (builder == null) return; + + event.setCancelled(true); // Eingabe nicht im globalen Chat zeigen + String input = event.getMessage().trim(); + + if (input.equalsIgnoreCase("abbrechen") || input.equalsIgnoreCase("cancel")) { + sessions.remove(player.getUniqueId()); + player.sendMessage(plugin.prefix() + "§cEvent-Builder abgebrochen."); + return; + } + + // An Builder weitergeben — synchron auf Main-Thread + plugin.getServer().getScheduler().runTask(plugin, () -> { + boolean done = builder.handleInput(input); + if (done) sessions.remove(player.getUniqueId()); + }); + } + + @EventHandler + public void onQuit(PlayerQuitEvent event) { + sessions.remove(event.getPlayer().getUniqueId()); + } +} diff --git a/src/main/java/dev/viper/eventengine/listener/EventProtectionListener.java b/src/main/java/dev/viper/eventengine/listener/EventProtectionListener.java new file mode 100644 index 0000000..9fc8d87 --- /dev/null +++ b/src/main/java/dev/viper/eventengine/listener/EventProtectionListener.java @@ -0,0 +1,127 @@ +package dev.viper.eventengine.listener; + +import dev.viper.eventengine.EventEngine; +import dev.viper.eventengine.model.ActiveEvent; +import dev.viper.eventengine.model.EventRegion; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.entity.TNTPrimed; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.entity.EntityExplodeEvent; +import org.bukkit.event.player.PlayerMoveEvent; + +/** + * Schützt Event-Regionen während laufender Events: + * + * 1. TNT / Explosionen → kein Block-Schaden innerhalb der Region + * 2. Spieler die die Region verlassen → werden zurückteleportiert + * 3. Block-Interaktionen außerhalb der Region durch Teilnehmer → geblockt + */ +public class EventProtectionListener implements Listener { + + private final EventEngine plugin; + + public EventProtectionListener(EventEngine plugin) { + this.plugin = plugin; + } + + // ─── 1. Explosionen (TNT, Creeper, Ghast, ...) ──────────────────────── + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onExplode(EntityExplodeEvent e) { + ActiveEvent active = plugin.getEventManager().getCurrentEvent(); + + // Immer wenn ein Event läuft: TNT-Blockschaden deaktivieren + if (active == null) return; + if (!plugin.getConfigManager().isNoExplosionBlockDamage()) return; + + EventRegion region = active.getDefinition().getRegion(); + + if (region != null) { + // Nur Blöcke INNERHALB der Region schützen — Explosion außerhalb normal + e.blockList().removeIf(block -> region.contains(block.getLocation())); + } else { + // Kein Region definiert → global während Event kein Block-Schaden durch Explosionen + e.blockList().clear(); + } + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onEntityDamageByExplosion(EntityDamageEvent e) { + // Spieler-Schaden durch Explosionen während Events: behalten (nur Block-Schaden deaktiviert) + // Nichts tun — Spieler sollen Schaden nehmen können + } + + // ─── 2. Spieler-Bewegung: Region-Grenze prüfen ──────────────────────── + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onMove(PlayerMoveEvent e) { + Player player = e.getPlayer(); + ActiveEvent active = plugin.getEventManager().getCurrentEvent(); + if (active == null) return; + + // Nur für Event-Teilnehmer + if (!active.isParticipant(player)) return; + + EventRegion region = active.getDefinition().getRegion(); + if (region == null) return; // keine Region → frei bewegen + if (!plugin.getConfigManager().isEnforceRegionBoundary()) return; + + Location to = e.getTo(); + if (to == null) return; + + // Nur prüfen wenn Spieler Block-Grenze überschreitet (Performance) + if (e.getFrom().getBlockX() == to.getBlockX() + && e.getFrom().getBlockY() == to.getBlockY() + && e.getFrom().getBlockZ() == to.getBlockZ()) return; + + if (!region.contains(to)) { + // Zurückteleportieren zur letzten gültigen Position + e.setCancelled(true); + player.sendActionBar("§c⚠ Du kannst die Event-Arena nicht verlassen!"); + } + } + + // ─── 3. Block-Break/Place außerhalb der Region ──────────────────────── + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onBlockBreak(BlockBreakEvent e) { + Player player = e.getPlayer(); + ActiveEvent active = plugin.getEventManager().getCurrentEvent(); + if (active == null) return; + if (!active.isParticipant(player)) return; + + EventRegion region = active.getDefinition().getRegion(); + if (region == null) return; + if (!plugin.getConfigManager().isRestrictBlockInteraction()) return; + + if (!region.contains(e.getBlock().getLocation())) { + e.setCancelled(true); + player.sendActionBar("§c⚠ Du kannst außerhalb der Event-Arena keine Blöcke abbauen!"); + } + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onBlockPlace(BlockPlaceEvent e) { + Player player = e.getPlayer(); + ActiveEvent active = plugin.getEventManager().getCurrentEvent(); + if (active == null) return; + if (!active.isParticipant(player)) return; + + EventRegion region = active.getDefinition().getRegion(); + if (region == null) return; + if (!plugin.getConfigManager().isRestrictBlockInteraction()) return; + + if (!region.contains(e.getBlock().getLocation())) { + e.setCancelled(true); + player.sendActionBar("§c⚠ Du kannst außerhalb der Event-Arena keine Blöcke setzen!"); + } + } +} diff --git a/src/main/java/dev/viper/eventengine/manager/EventManager.java b/src/main/java/dev/viper/eventengine/manager/EventManager.java new file mode 100644 index 0000000..e8ad6f2 --- /dev/null +++ b/src/main/java/dev/viper/eventengine/manager/EventManager.java @@ -0,0 +1,188 @@ +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.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.*; +import java.util.logging.Logger; + +/** + * Steuert den Lebenszyklus von Events: + * start → announce → 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; + } + + currentEvent = new ActiveEvent(def); + currentEvent.setState(ActiveEvent.State.RUNNING); + + 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()); + } + + runCommands(def.getStartCommands(), null); + handlerRegistry.get(def.getType()).ifPresent(h -> h.onStart(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); + } + return true; + } + + 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)); + + 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 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)); + 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)); + 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())); + } + } + + 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; } +} diff --git a/src/main/java/dev/viper/eventengine/manager/EventRegistry.java b/src/main/java/dev/viper/eventengine/manager/EventRegistry.java new file mode 100644 index 0000000..4494d04 --- /dev/null +++ b/src/main/java/dev/viper/eventengine/manager/EventRegistry.java @@ -0,0 +1,142 @@ +package dev.viper.eventengine.manager; + +import dev.viper.eventengine.EventEngine; +import dev.viper.eventengine.util.ColorUtil; +import dev.viper.eventengine.config.EventOverrideLoader; +import dev.viper.eventengine.model.EventCategory; +import dev.viper.eventengine.model.EventDefinition; +import dev.viper.eventengine.model.EventType; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * Zentrales Registry für alle EventDefinitions. + * Enthält alle 100+ eingebauten Events und beliebig viele Custom-Events. + */ +public class EventRegistry { + + private final EventEngine plugin; + // Geordnete Map: id → Definition + private final Map events = new LinkedHashMap<>(); + + public EventRegistry(EventEngine plugin) { + this.plugin = plugin; + } + + // ─── Initialisierung ─────────────────────────────────────────────────── + + public void init() { + events.clear(); + registerBuiltins(); + loadCustom(); + plugin.getLogger().info("EventRegistry: " + events.size() + " Events registriert."); + } + + private void registerBuiltins() { + EventOverrideLoader overrideLoader = new EventOverrideLoader(plugin); + for (EventType type : EventType.values()) { + if (type == EventType.CUSTOM) continue; + EventDefinition def = createBuiltin(type); + overrideLoader.apply(def); // config.yml overrides anwenden + events.put(def.getId(), def); + } + } + + /** + * Erstellt eine EventDefinition mit sinnvollen Defaults pro Typ. + * Hier können Standardwerte (Dauer, Spielerzahl) pro Event gesetzt werden. + */ + private EventDefinition createBuiltin(EventType type) { + EventDefinition def = new EventDefinition(type); + + // Spezifische Overrides pro Typ + switch (type) { + case PVP_ONE_VS_ONE -> { def.setMinPlayers(2); def.setMaxPlayers(2); } + case PVP_TEAM_BATTLE -> { def.setMinPlayers(4); def.setMaxPlayers(40); } + case PVP_UHC -> { def.setDurationSeconds(3600); def.setMinPlayers(4); } + case PVP_HUNGER_GAMES -> { def.setDurationSeconds(1800); def.setMinPlayers(8); } + case SURVIVAL_BOSS_RUSH -> { def.setDurationSeconds(600); def.setMinPlayers(1); } + case SURVIVAL_WITHER_STORM -> { def.setDurationSeconds(900); def.setMinPlayers(2); } + case BUILD_BATTLE -> { def.setDurationSeconds(300); def.setMinPlayers(2); } + case BUILD_SPEED_BUILD -> { def.setDurationSeconds(120); def.setMinPlayers(1); } + case RACE_PARKOUR -> { def.setDurationSeconds(180); def.setMinPlayers(1); } + case FUN_DROP_PARTY -> { def.setDurationSeconds(60); def.setMinPlayers(1); def.setMaxPlayers(200); } + case FUN_TNT_RAIN -> { def.setDurationSeconds(30); def.setMinPlayers(1); def.setMaxPlayers(200); } + case QUIZ_SPEED_QUIZ -> { def.setDurationSeconds(120); def.setMinPlayers(1); } + case ECONOMY_LOTTERY -> { def.setDurationSeconds(120); def.setMinPlayers(1); def.setMaxPlayers(200); } + default -> { /* Defaults aus Konstruktor */ } + } + + // Ankündigungs-Texte + def.setAnnouncement("&6✦ &e" + type.getDisplayName() + " &7[" + type.getCategory().getDisplayName() + "&7] &6startet jetzt! &7Viel Erfolg!"); + + return def; + } + + private void loadCustom() { + Map custom = plugin.getConfigManager().loadCustomEvents(); + events.putAll(custom); + } + + // ─── API ─────────────────────────────────────────────────────────────── + + public Optional get(String id) { + return Optional.ofNullable(events.get(id.toLowerCase())); + } + + public Collection getAll() { + return Collections.unmodifiableCollection(events.values()); + } + + public List getByCategory(EventCategory category) { + return events.values().stream() + .filter(d -> d.getCategory() == category) + .collect(Collectors.toList()); + } + + public List getRotation() { + return events.values().stream() + .filter(EventDefinition::isInRotation) + .collect(Collectors.toList()); + } + + /** + * Wählt ein zufälliges Event aus der Rotation, gewichtet. + */ + public Optional getWeightedRandom() { + List rotation = getRotation(); + if (rotation.isEmpty()) return Optional.empty(); + + int totalWeight = rotation.stream().mapToInt(EventDefinition::getWeight).sum(); + int roll = new Random().nextInt(Math.max(totalWeight, 1)); + int cumulative = 0; + for (EventDefinition def : rotation) { + cumulative += def.getWeight(); + if (roll < cumulative) return Optional.of(def); + } + return Optional.of(rotation.get(0)); + } + + public void register(EventDefinition def) { + events.put(def.getId().toLowerCase(), def); + if (def.isCustom()) { + plugin.getConfigManager().saveCustomEvent(def); + } + } + + public boolean remove(String id) { + EventDefinition def = events.remove(id.toLowerCase()); + if (def != null && def.isCustom()) { + plugin.getConfigManager().deleteCustomEvent(id); + return true; + } + return def != null; + } + + public boolean exists(String id) { + return events.containsKey(id.toLowerCase()); + } + + public int count() { return events.size(); } +} diff --git a/src/main/java/dev/viper/eventengine/model/ActiveEvent.java b/src/main/java/dev/viper/eventengine/model/ActiveEvent.java new file mode 100644 index 0000000..b9cacba --- /dev/null +++ b/src/main/java/dev/viper/eventengine/model/ActiveEvent.java @@ -0,0 +1,75 @@ +package dev.viper.eventengine.model; + +import org.bukkit.entity.Player; + +import java.util.*; + +/** + * Repräsentiert eine aktuell laufende Event-Instanz. + */ +public class ActiveEvent { + + public enum State { WAITING, RUNNING, ENDED } + + private final EventDefinition definition; + private State state; + private final long startTime; + private final Set participants; + private final Map scores; + private int taskId = -1; + + public ActiveEvent(EventDefinition definition) { + this.definition = definition; + this.state = State.WAITING; + this.startTime = System.currentTimeMillis(); + this.participants = new LinkedHashSet<>(); + this.scores = new HashMap<>(); + } + + public void addParticipant(Player player) { + participants.add(player.getUniqueId()); + scores.putIfAbsent(player.getUniqueId(), 0); + } + + public void removeParticipant(Player player) { + participants.remove(player.getUniqueId()); + } + + public boolean isParticipant(Player player) { + return participants.contains(player.getUniqueId()); + } + + public void addScore(UUID uuid, int points) { + scores.merge(uuid, points, Integer::sum); + } + + public int getScore(UUID uuid) { + return scores.getOrDefault(uuid, 0); + } + + /** Gibt Spieler sortiert nach Score zurück */ + public List> getLeaderboard() { + List> entries = new ArrayList<>(scores.entrySet()); + entries.sort((a, b) -> b.getValue() - a.getValue()); + return entries; + } + + public long getElapsedSeconds() { + return (System.currentTimeMillis() - startTime) / 1000; + } + + public long getRemainingSeconds() { + if (definition.getDurationSeconds() == 0) return -1; + long remaining = definition.getDurationSeconds() - getElapsedSeconds(); + return Math.max(0, remaining); + } + + public EventDefinition getDefinition() { return definition; } + public State getState() { return state; } + public void setState(State state) { this.state = state; } + public Set getParticipants() { return Collections.unmodifiableSet(participants); } + public Map getScores() { return scores; } + public int getTaskId() { return taskId; } + public void setTaskId(int taskId) { this.taskId = taskId; } + public int getParticipantCount() { return participants.size(); } +} diff --git a/src/main/java/dev/viper/eventengine/model/EventCategory.java b/src/main/java/dev/viper/eventengine/model/EventCategory.java new file mode 100644 index 0000000..bfd3487 --- /dev/null +++ b/src/main/java/dev/viper/eventengine/model/EventCategory.java @@ -0,0 +1,27 @@ +package dev.viper.eventengine.model; + +import org.bukkit.Material; + +public enum EventCategory { + COMBAT("⚔ Kampf", Material.DIAMOND_SWORD), + SURVIVAL("🌲 Überleben", Material.COOKED_BEEF), + BUILDING("🏗 Bauen", Material.BRICKS), + RACING("🏁 Rennen", Material.GOLDEN_BOOTS), + COLLECTION("📦 Sammeln", Material.CHEST), + FUN("🎉 Spaß", Material.FIREWORK_ROCKET), + QUIZ("❓ Quiz", Material.BOOK), + ECONOMY("💰 Wirtschaft", Material.GOLD_INGOT), + TEAM("👥 Team", Material.YELLOW_BANNER), + CUSTOM("⭐ Custom", Material.COMMAND_BLOCK); + + private final String displayName; + private final Material icon; + + EventCategory(String displayName, Material icon) { + this.displayName = displayName; + this.icon = icon; + } + + public String getDisplayName() { return displayName; } + public Material getIcon() { return icon; } +} diff --git a/src/main/java/dev/viper/eventengine/model/EventDefinition.java b/src/main/java/dev/viper/eventengine/model/EventDefinition.java new file mode 100644 index 0000000..36264da --- /dev/null +++ b/src/main/java/dev/viper/eventengine/model/EventDefinition.java @@ -0,0 +1,191 @@ +package dev.viper.eventengine.model; + +import dev.viper.eventengine.util.ColorUtil; +import dev.viper.eventengine.model.EventRegion; +import org.bukkit.configuration.ConfigurationSection; + +import java.util.*; + +/** + * Repräsentiert ein einzelnes Event — sowohl eingebaute als auch Custom-Events. + * Custom-Events werden in custom_events.yml gespeichert. + */ +public class EventDefinition { + + private final String id; + private String displayName; + private String description; + private EventType type; + private EventCategory category; + + // Zeitlimit in Sekunden (0 = unbegrenzt) + private int durationSeconds; + // Mindest-/Höchst-Spielerzahl + private int minPlayers; + private int maxPlayers; + // Befehle die beim Start/Ende ausgeführt werden + private List startCommands; + private List endCommands; + // Belohnungen + private List rewards; + // Ankündigungs-Text + private String announcement; + // Ob das Event in der Rotation ist + private boolean inRotation; + // Gewichtung in der Rotation + private int weight; + // Custom-Einstellungen (Key-Value) + private Map customSettings; + // Ist es ein benutzerdefiniertes Event? + private boolean isCustom; + // Optionale Event-Region (null = gesamte Welt) + private EventRegion region; + + // ─── Konstruktor für builtin Events ──────────────────────────────────── + public EventDefinition(EventType type) { + this.id = type.name().toLowerCase(); + this.type = type; + this.category = type.getCategory(); + this.displayName = type.getDisplayName(); + this.description = "Ein " + type.getDisplayName() + " Event."; + this.durationSeconds = 300; + this.minPlayers = 2; + this.maxPlayers = 50; + this.startCommands = new ArrayList<>(); + this.endCommands = new ArrayList<>(); + this.rewards = new ArrayList<>(); + this.announcement = ColorUtil.color("&6✦ &e" + displayName + " &6startet jetzt!"); + this.inRotation = true; + this.weight = 1; + this.customSettings = new HashMap<>(); + this.isCustom = false; + } + + // ─── Konstruktor für Custom Events ───────────────────────────────────── + public EventDefinition(String id) { + this.id = id; + this.type = EventType.CUSTOM; + this.category = EventCategory.CUSTOM; + this.displayName = id; + this.description = "Benutzerdefiniertes Event."; + this.durationSeconds = 300; + this.minPlayers = 1; + this.maxPlayers = 50; + this.startCommands = new ArrayList<>(); + this.endCommands = new ArrayList<>(); + this.rewards = new ArrayList<>(); + this.announcement = ColorUtil.color("&6✦ &e" + id + " &6startet jetzt!"); + this.inRotation = true; + this.weight = 1; + this.customSettings = new HashMap<>(); + this.isCustom = true; + } + + // ─── Laden aus ConfigurationSection ──────────────────────────────────── + public static EventDefinition fromConfig(String id, ConfigurationSection sec) { + EventDefinition def = new EventDefinition(id); + def.displayName = sec.getString("display-name", id); + def.description = sec.getString("description", "Kein Beschreibungstext."); + def.durationSeconds = sec.getInt("duration-seconds", 300); + def.minPlayers = sec.getInt("min-players", 1); + def.maxPlayers = sec.getInt("max-players", 50); + def.startCommands = sec.getStringList("start-commands"); + def.endCommands = sec.getStringList("end-commands"); + def.rewards = sec.getStringList("rewards"); + def.announcement = ColorUtil.color(sec.getString("announcement", + "&6✦ &e" + def.displayName + " &6startet jetzt!")); + def.inRotation = sec.getBoolean("in-rotation", true); + def.weight = sec.getInt("weight", 1); + String catStr = sec.getString("category", "CUSTOM"); + try { def.category = EventCategory.valueOf(catStr); } + catch (Exception e) { def.category = EventCategory.CUSTOM; } + + ConfigurationSection cs = sec.getConfigurationSection("settings"); + if (cs != null) { + for (String key : cs.getKeys(false)) { + def.customSettings.put(key, cs.get(key)); + } + } + + // Region laden + if (sec.contains("region.world")) { + try { + String w = sec.getString("region.world"); + int minX = sec.getInt("region.min-x"); + int minY = sec.getInt("region.min-y"); + int minZ = sec.getInt("region.min-z"); + int maxX = sec.getInt("region.max-x"); + int maxY = sec.getInt("region.max-y"); + int maxZ = sec.getInt("region.max-z"); + def.region = new EventRegion(w, minX, minY, minZ, maxX, maxY, maxZ); + } catch (Exception ignored) {} + } + return def; + } + + // ─── Speichern in ConfigurationSection ───────────────────────────────── + public void saveToConfig(ConfigurationSection sec) { + sec.set("display-name", displayName); + sec.set("description", description); + sec.set("duration-seconds", durationSeconds); + sec.set("min-players", minPlayers); + sec.set("max-players", maxPlayers); + sec.set("start-commands", startCommands); + sec.set("end-commands", endCommands); + sec.set("rewards", rewards); + sec.set("announcement", announcement); + sec.set("in-rotation", inRotation); + sec.set("weight", weight); + sec.set("category", category.name()); + if (region != null) { + sec.set("region.world", region.getWorldName()); + sec.set("region.min-x", region.getMinX()); + sec.set("region.min-y", region.getMinY()); + sec.set("region.min-z", region.getMinZ()); + sec.set("region.max-x", region.getMaxX()); + sec.set("region.max-y", region.getMaxY()); + sec.set("region.max-z", region.getMaxZ()); + } + if (!customSettings.isEmpty()) { + for (Map.Entry entry : customSettings.entrySet()) { + sec.set("settings." + entry.getKey(), entry.getValue()); + } + } + } + + // ─── Getter/Setter ────────────────────────────────────────────────────── + public String getId() { return id; } + public String getDisplayName() { return displayName; } + public void setDisplayName(String n) { this.displayName = n; } + public String getDescription() { return description; } + public void setDescription(String d) { this.description = d; } + public EventType getType() { return type; } + public EventCategory getCategory() { return category; } + public void setCategory(EventCategory c) { this.category = c; } + public int getDurationSeconds() { return durationSeconds; } + public void setDurationSeconds(int s) { this.durationSeconds = s; } + public int getMinPlayers() { return minPlayers; } + public void setMinPlayers(int n) { this.minPlayers = n; } + public int getMaxPlayers() { return maxPlayers; } + public void setMaxPlayers(int n) { this.maxPlayers = n; } + public List getStartCommands() { return startCommands; } + public void setStartCommands(List l) { this.startCommands = l; } + public List getEndCommands() { return endCommands; } + public void setEndCommands(List l) { this.endCommands = l; } + public List getRewards() { return rewards; } + public void setRewards(List l) { this.rewards = l; } + public String getAnnouncement() { return announcement; } + public void setAnnouncement(String a) { this.announcement = ColorUtil.color(a); } + public boolean isInRotation() { return inRotation; } + public void setInRotation(boolean b) { this.inRotation = b; } + public int getWeight() { return weight; } + public void setWeight(int w) { this.weight = w; } + public Map getCustomSettings() { return customSettings; } + public boolean isCustom() { return isCustom; } + public EventRegion getRegion() { return region; } + public void setRegion(EventRegion r) { this.region = r; } + public boolean hasRegion() { return region != null; } + public Object getSetting(String key) { return customSettings.get(key); } + public void setSetting(String key, Object val) { customSettings.put(key, val); } +} +// Wird unten in der Klasse ergänzt — separate Patch-Datei diff --git a/src/main/java/dev/viper/eventengine/model/EventRegion.java b/src/main/java/dev/viper/eventengine/model/EventRegion.java new file mode 100644 index 0000000..37b9092 --- /dev/null +++ b/src/main/java/dev/viper/eventengine/model/EventRegion.java @@ -0,0 +1,101 @@ +package dev.viper.eventengine.model; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; + +/** + * Achsenparalleler Quader in einer Welt. + * Wird pro EventDefinition gespeichert. + */ +public class EventRegion { + + private final String worldName; + private final int minX, minY, minZ; + private final int maxX, maxY, maxZ; + + public EventRegion(Location pos1, Location pos2) { + if (!pos1.getWorld().equals(pos2.getWorld())) + throw new IllegalArgumentException("Beide Positionen müssen in der gleichen Welt sein!"); + this.worldName = pos1.getWorld().getName(); + this.minX = Math.min(pos1.getBlockX(), pos2.getBlockX()); + this.minY = Math.min(pos1.getBlockY(), pos2.getBlockY()); + this.minZ = Math.min(pos1.getBlockZ(), pos2.getBlockZ()); + this.maxX = Math.max(pos1.getBlockX(), pos2.getBlockX()); + this.maxY = Math.max(pos1.getBlockY(), pos2.getBlockY()); + this.maxZ = Math.max(pos1.getBlockZ(), pos2.getBlockZ()); + } + + /** Konstruktor aus gespeicherten Werten (Config-Load) */ + public EventRegion(String worldName, int minX, int minY, int minZ, + int maxX, int maxY, int maxZ) { + this.worldName = worldName; + this.minX = minX; this.minY = minY; this.minZ = minZ; + this.maxX = maxX; this.maxY = maxY; this.maxZ = maxZ; + } + + // ─── Prüfungen ───────────────────────────────────────────────────────── + + public boolean contains(Location loc) { + if (loc == null || loc.getWorld() == null) return false; + if (!loc.getWorld().getName().equals(worldName)) return false; + int x = loc.getBlockX(), y = loc.getBlockY(), z = loc.getBlockZ(); + return x >= minX && x <= maxX + && y >= minY && y <= maxY + && z >= minZ && z <= maxZ; + } + + /** Erweiterte Prüfung mit horizontalem Puffer (für Boundary-Events) */ + public boolean containsWithBuffer(Location loc, int buffer) { + if (loc == null || loc.getWorld() == null) return false; + if (!loc.getWorld().getName().equals(worldName)) return false; + int x = loc.getBlockX(), y = loc.getBlockY(), z = loc.getBlockZ(); + return x >= minX - buffer && x <= maxX + buffer + && y >= minY - buffer && y <= maxY + buffer + && z >= minZ - buffer && z <= maxZ + buffer; + } + + // ─── Hilfsmethoden ───────────────────────────────────────────────────── + + /** Mittelpunkt der Region auf Bodenhöhe (minY) */ + public Location getCenter() { + World world = Bukkit.getWorld(worldName); + if (world == null) return null; + return new Location(world, + (minX + maxX) / 2.0 + 0.5, + minY, + (minZ + maxZ) / 2.0 + 0.5); + } + + /** Zufällige sichere Position innerhalb der Region */ + public Location randomLocation(java.util.Random rng) { + World world = Bukkit.getWorld(worldName); + if (world == null) return null; + int x = minX + rng.nextInt(Math.max(1, maxX - minX)); + int z = minZ + rng.nextInt(Math.max(1, maxZ - minZ)); + // Y auf Oberfläche oder minY+1 + int y = world.getHighestBlockYAt(x, z); + y = Math.max(y, minY + 1); + return new Location(world, x + 0.5, y, z + 0.5); + } + + public World getWorld() { return Bukkit.getWorld(worldName); } + public String getWorldName() { return worldName; } + public int getMinX() { return minX; } + public int getMinY() { return minY; } + public int getMinZ() { return minZ; } + public int getMaxX() { return maxX; } + public int getMaxY() { return maxY; } + public int getMaxZ() { return maxZ; } + + /** Größe in Blöcken */ + public int getSizeX() { return maxX - minX + 1; } + public int getSizeY() { return maxY - minY + 1; } + public int getSizeZ() { return maxZ - minZ + 1; } + + @Override + public String toString() { + return worldName + " [" + minX + "," + minY + "," + minZ + + "] → [" + maxX + "," + maxY + "," + maxZ + "]"; + } +} diff --git a/src/main/java/dev/viper/eventengine/model/EventType.java b/src/main/java/dev/viper/eventengine/model/EventType.java new file mode 100644 index 0000000..94c80ed --- /dev/null +++ b/src/main/java/dev/viper/eventengine/model/EventType.java @@ -0,0 +1,156 @@ +package dev.viper.eventengine.model; + +public enum EventType { + + // ═══════════════════════════════════════════════════════════ + // COMBAT EVENTS (20) + // ═══════════════════════════════════════════════════════════ + PVP_DEATHMATCH("PvP Deathmatch", EventCategory.COMBAT), + PVP_TEAM_BATTLE("Team Battle", EventCategory.COMBAT), + PVP_FREE_FOR_ALL("Free for All", EventCategory.COMBAT), + PVP_LAST_MAN_STANDING("Last Man Standing", EventCategory.COMBAT), + PVP_ONE_VS_ONE("1v1 Turnier", EventCategory.COMBAT), + PVP_BOW_ONLY("Nur Bogen", EventCategory.COMBAT), + PVP_SWORD_ONLY("Nur Schwert", EventCategory.COMBAT), + PVP_FIST_FIGHT("Faustkampf", EventCategory.COMBAT), + PVP_KIT_PVP("Kit PvP", EventCategory.COMBAT), + PVP_CAPTURE_THE_FLAG("Capture the Flag", EventCategory.COMBAT), + PVP_KING_OF_THE_HILL("King of the Hill", EventCategory.COMBAT), + PVP_HUNGER_GAMES("Hunger Games", EventCategory.COMBAT), + PVP_BEDWARS_LITE("BedWars Lite", EventCategory.COMBAT), + PVP_SKYWARS_LITE("SkyWars Lite", EventCategory.COMBAT), + PVP_UHC("Ultra Hardcore", EventCategory.COMBAT), + PVP_SPLEEF("Spleef", EventCategory.COMBAT), + PVP_SUMO("Sumo", EventCategory.COMBAT), + PVP_SNOWBALL_FIGHT("Schneeballschlacht", EventCategory.COMBAT), + PVP_ELYTRA_PVP("Elytra PvP", EventCategory.COMBAT), + PVP_ARENA_SURVIVAL("Arena Survival", EventCategory.COMBAT), + + // ═══════════════════════════════════════════════════════════ + // SURVIVAL EVENTS (15) + // ═══════════════════════════════════════════════════════════ + SURVIVAL_MOB_WAVE("Mob-Wellen", EventCategory.SURVIVAL), + SURVIVAL_ZOMBIE_SIEGE("Zombie-Belagerung", EventCategory.SURVIVAL), + SURVIVAL_BOSS_RUSH("Boss Rush", EventCategory.SURVIVAL), + SURVIVAL_WITHER_STORM("Wither Sturm", EventCategory.SURVIVAL), + SURVIVAL_DRAGON_FIGHT("Drachen-Event", EventCategory.SURVIVAL), + SURVIVAL_HARDCORE_ROUND("Hardcore-Runde", EventCategory.SURVIVAL), + SURVIVAL_ISLAND("Insel-Überleben", EventCategory.SURVIVAL), + SURVIVAL_NETHER_RUN("Nether-Sprint", EventCategory.SURVIVAL), + SURVIVAL_ENDURANCE("Ausdauer-Test", EventCategory.SURVIVAL), + SURVIVAL_RAIDS_PLUS("Raid+", EventCategory.SURVIVAL), + SURVIVAL_MONSTER_HUNT("Monster-Jagd", EventCategory.SURVIVAL), + SURVIVAL_LAVA_RISING("Steigendes Lava", EventCategory.SURVIVAL), + SURVIVAL_GRAVITY_SHIFT("Schwerkraft-Chaos", EventCategory.SURVIVAL), + SURVIVAL_RANDOM_SPAWN("Random Spawn", EventCategory.SURVIVAL), + SURVIVAL_MANHUNT("Manhunt", EventCategory.SURVIVAL), + + // ═══════════════════════════════════════════════════════════ + // BUILDING EVENTS (10) + // ═══════════════════════════════════════════════════════════ + BUILD_BATTLE("Build Battle", EventCategory.BUILDING), + BUILD_SPEED_BUILD("Speed Build", EventCategory.BUILDING), + BUILD_THEME_CHALLENGE("Themen-Challenge", EventCategory.BUILDING), + BUILD_PIXEL_ART("Pixel Art Contest", EventCategory.BUILDING), + BUILD_TALLEST_TOWER("Höchster Turm", EventCategory.BUILDING), + BUILD_BRIDGE("Brücke bauen", EventCategory.BUILDING), + BUILD_REDSTONE("Redstone Challenge", EventCategory.BUILDING), + BUILD_UNDERGROUND("Unterirdisch bauen", EventCategory.BUILDING), + BUILD_SKYBLOCK_STYLE("Skyblock Style", EventCategory.BUILDING), + BUILD_BLINDBUILD("Blind bauen", EventCategory.BUILDING), + + // ═══════════════════════════════════════════════════════════ + // RACING EVENTS (10) + // ═══════════════════════════════════════════════════════════ + RACE_PARKOUR("Parkour Race", EventCategory.RACING), + RACE_ELYTRA("Elytra-Rennen", EventCategory.RACING), + RACE_BOAT("Boot-Rennen", EventCategory.RACING), + RACE_HORSE("Pferde-Rennen", EventCategory.RACING), + RACE_PIG("Schweine-Rennen", EventCategory.RACING), + RACE_MINECART("Lore-Rennen", EventCategory.RACING), + RACE_SWIMMING("Schwimm-Rennen", EventCategory.RACING), + RACE_OBSTACLE("Hindernislauf", EventCategory.RACING), + RACE_MAZE("Labyrinth", EventCategory.RACING), + RACE_ICEBOAT("Eisboot-Rennen", EventCategory.RACING), + + // ═══════════════════════════════════════════════════════════ + // COLLECTION EVENTS (10) + // ═══════════════════════════════════════════════════════════ + COLLECT_SCAVENGER_HUNT("Schatzsuche", EventCategory.COLLECTION), + COLLECT_MINING_COMP("Mining-Wettbewerb", EventCategory.COLLECTION), + COLLECT_FISHING_COMP("Angel-Wettbewerb", EventCategory.COLLECTION), + COLLECT_FARMING_COMP("Farm-Wettbewerb", EventCategory.COLLECTION), + COLLECT_MOB_DROPS("Mob-Drops sammeln", EventCategory.COLLECTION), + COLLECT_TREASURE_HUNT("Schatz finden", EventCategory.COLLECTION), + COLLECT_EASTER_EGG("Eier suchen", EventCategory.COLLECTION), + COLLECT_SPEED_MINE("Speed Mining", EventCategory.COLLECTION), + COLLECT_WOODCUTTING("Holzfäll-Comp", EventCategory.COLLECTION), + COLLECT_ENCHANT_RACE("Verzauberungsrennen", EventCategory.COLLECTION), + + // ═══════════════════════════════════════════════════════════ + // FUN EVENTS (15) + // ═══════════════════════════════════════════════════════════ + FUN_RANDOM_EFFECTS("Zufalls-Effekte", EventCategory.FUN), + FUN_TINY_PLAYERS("Mini-Spieler", EventCategory.FUN), + FUN_GIANT_PLAYERS("Riesen-Spieler", EventCategory.FUN), + FUN_INVISIBLE_PLAYERS("Unsichtbarkeit", EventCategory.FUN), + FUN_CHAOS_MODE("Chaos-Modus", EventCategory.FUN), + FUN_LIGHTNING_STORM("Blitz-Sturm", EventCategory.FUN), + FUN_TNT_RAIN("TNT-Regen", EventCategory.FUN), + FUN_ITEM_RAIN("Item-Regen", EventCategory.FUN), + FUN_DROP_PARTY("Drop-Party", EventCategory.FUN), + FUN_FIREWORK_SHOW("Feuerwerk-Show", EventCategory.FUN), + FUN_RANDOM_TELEPORT("Zufalls-TP", EventCategory.FUN), + FUN_SWAP_INVENTORIES("Inventar-Tausch", EventCategory.FUN), + FUN_REVERSE_GRAVITY("Umgekehrte Schwerkraft", EventCategory.FUN), + FUN_SPEED_BOOST("Geschwindigkeits-Boost", EventCategory.FUN), + FUN_BOUNCY_BLOCKS("Hüpfende Blöcke", EventCategory.FUN), + + // ═══════════════════════════════════════════════════════════ + // QUIZ & KNOWLEDGE EVENTS (5) + // ═══════════════════════════════════════════════════════════ + QUIZ_MINECRAFT_TRIVIA("Minecraft-Quiz", EventCategory.QUIZ), + QUIZ_GENERAL_KNOWLEDGE("Allgemeinwissen-Quiz", EventCategory.QUIZ), + QUIZ_SPEED_QUIZ("Speed-Quiz", EventCategory.QUIZ), + QUIZ_CRAFTING_CHALLENGE("Crafting-Challenge", EventCategory.QUIZ), + QUIZ_MOB_QUIZ("Mob-Quiz", EventCategory.QUIZ), + + // ═══════════════════════════════════════════════════════════ + // ECONOMY EVENTS (5) + // ═══════════════════════════════════════════════════════════ + ECONOMY_AUCTION("Auktion", EventCategory.ECONOMY), + ECONOMY_MARKET_RUSH("Markt-Rush", EventCategory.ECONOMY), + ECONOMY_LOTTERY("Lotterie", EventCategory.ECONOMY), + ECONOMY_TRADE_FRENZY("Handels-Frenzy", EventCategory.ECONOMY), + ECONOMY_BETTING("Wetten-Event", EventCategory.ECONOMY), + + // ═══════════════════════════════════════════════════════════ + // TEAM EVENTS (10) + // ═══════════════════════════════════════════════════════════ + TEAM_RELAY_RACE("Staffellauf", EventCategory.TEAM), + TEAM_BUILD_BATTLE("Team Build Battle", EventCategory.TEAM), + TEAM_SURVIVAL("Team Survival", EventCategory.TEAM), + TEAM_TREASURE_HUNT("Team Schatzsuche", EventCategory.TEAM), + TEAM_QUIZ("Team-Quiz", EventCategory.TEAM), + TEAM_CAPTURE_POINTS("Punkte-Capture", EventCategory.TEAM), + TEAM_RESOURCE_RACE("Ressourcen-Rennen", EventCategory.TEAM), + TEAM_HIDE_AND_SEEK("Verstecken", EventCategory.TEAM), + TEAM_MURDER_MYSTERY("Murder Mystery", EventCategory.TEAM), + TEAM_COLOR_WAR("Farben-Krieg", EventCategory.TEAM), + + // ═══════════════════════════════════════════════════════════ + // CUSTOM (dynamisch erstellt) + // ═══════════════════════════════════════════════════════════ + CUSTOM("Eigenes Event", EventCategory.CUSTOM); + + private final String displayName; + private final EventCategory category; + + EventType(String displayName, EventCategory category) { + this.displayName = displayName; + this.category = category; + } + + public String getDisplayName() { return displayName; } + public EventCategory getCategory() { return category; } +} diff --git a/src/main/java/dev/viper/eventengine/model/ScheduleEntry.java b/src/main/java/dev/viper/eventengine/model/ScheduleEntry.java new file mode 100644 index 0000000..d506095 --- /dev/null +++ b/src/main/java/dev/viper/eventengine/model/ScheduleEntry.java @@ -0,0 +1,56 @@ +package dev.viper.eventengine.model; + +import java.time.DayOfWeek; +import java.time.LocalTime; +import java.util.List; + +/** + * Repräsentiert einen Zeitplan-Eintrag aus der config.yml. + * + * Beispiel-Config: + * schedule: + * - days: [MONDAY, FRIDAY] + * time: "18:00" + * event: "pvp_deathmatch" + * - days: [DAILY] + * time: "12:00" + * event: "RANDOM" + */ +public class ScheduleEntry { + + public static final String RANDOM = "RANDOM"; + + private final List days; // leer = täglich + private final LocalTime time; + private final String eventId; // EventDefinition-ID oder "RANDOM" + private final boolean everyDay; + + public ScheduleEntry(List days, LocalTime time, String eventId) { + this.days = days; + this.time = time; + this.eventId = eventId; + this.everyDay = days == null || days.isEmpty(); + } + + public boolean matchesNow(DayOfWeek day, LocalTime now) { + boolean dayMatch = everyDay || days.contains(day); + boolean timeMatch = now.getHour() == time.getHour() + && now.getMinute() == time.getMinute(); + return dayMatch && timeMatch; + } + + public boolean isRandom() { + return RANDOM.equalsIgnoreCase(eventId); + } + + public List getDays() { return days; } + public LocalTime getTime() { return time; } + public String getEventId() { return eventId; } + public boolean isEveryDay() { return everyDay; } + + @Override + public String toString() { + String dayStr = everyDay ? "Täglich" : days.toString(); + return dayStr + " um " + time + " → " + eventId; + } +} diff --git a/src/main/java/dev/viper/eventengine/scheduler/EventScheduler.java b/src/main/java/dev/viper/eventengine/scheduler/EventScheduler.java new file mode 100644 index 0000000..9fce6a0 --- /dev/null +++ b/src/main/java/dev/viper/eventengine/scheduler/EventScheduler.java @@ -0,0 +1,140 @@ +package dev.viper.eventengine.scheduler; + +import dev.viper.eventengine.EventEngine; +import dev.viper.eventengine.model.EventDefinition; +import dev.viper.eventengine.model.ScheduleEntry; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.scheduler.BukkitTask; + +import java.time.DayOfWeek; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.List; +import java.util.Optional; +import java.util.logging.Logger; + +/** + * Läuft jede Minute und prüft, ob ein geplantes Event feuern soll. + * Unterstützt: + * - Expliziten Zeitplan aus config.yml (schedule: []) + * - Intervall-Modus (use-interval: true, interval-minutes: 60) + */ +public class EventScheduler { + + private final EventEngine plugin; + private final Logger log; + private BukkitTask minuteTask; + private BukkitTask intervalTask; + + // Für Intervall-Modus: Zähler + private int minuteCounter = 0; + + public EventScheduler(EventEngine plugin) { + this.plugin = plugin; + this.log = plugin.getLogger(); + } + + public void start() { + stop(); + + // Jede Minute: Zeitplan prüfen + minuteTask = new BukkitRunnable() { + @Override + public void run() { + tickSchedule(); + } + }.runTaskTimer(plugin, 1200L, 1200L); // 1200 Ticks = 60 Sekunden + + log.info("EventScheduler gestartet."); + + // Intervall-Modus als Zusatz + if (plugin.getConfigManager().isUseInterval()) { + startIntervalMode(); + } + } + + private void startIntervalMode() { + int intervalMin = plugin.getConfigManager().getIntervalMinutes(); + if (intervalMin <= 0) return; + + long ticks = intervalMin * 60L * 20L; + intervalTask = new BukkitRunnable() { + @Override + public void run() { + if (plugin.getEventManager().isRunning()) return; + fireRandomOrDefault(); + } + }.runTaskTimer(plugin, ticks, ticks); + + log.info("Intervall-Modus: alle " + intervalMin + " Minuten."); + } + + private void tickSchedule() { + LocalDateTime now = LocalDateTime.now(); + DayOfWeek day = now.getDayOfWeek(); + LocalTime time = now.toLocalTime(); + + List schedule = plugin.getConfigManager().getSchedule(); + for (ScheduleEntry entry : schedule) { + if (!entry.matchesNow(day, time)) continue; + + if (plugin.getEventManager().isRunning()) { + log.info("Zeitplan-Event übersprungen (läuft bereits): " + entry.getEventId()); + continue; + } + + // Ankündigung? + int announceBefore = plugin.getConfigManager().getAnnounceBefore(); + if (announceBefore > 0) { + scheduleAnnouncement(entry, announceBefore); + } + + fireScheduleEntry(entry); + } + } + + private void scheduleAnnouncement(ScheduleEntry entry, int secondsBefore) { + String name = entry.isRandom() ? "ein zufälliges Event" : entry.getEventId(); + long ticks = secondsBefore * 20L; + // Ankündigung JETZT senden (Event startet in X Sekunden) + // (Zeitplan feuert genau zur geplanten Minute) + plugin.getServer().broadcastMessage( + plugin.prefix() + "§e⏰ In §c" + secondsBefore + " Sekunden §estartet: §6" + name + "§e!"); + } + + private void fireScheduleEntry(ScheduleEntry entry) { + if (entry.isRandom()) { + fireRandomOrDefault(); + return; + } + + Optional defOpt = plugin.getEventRegistry().get(entry.getEventId()); + if (defOpt.isPresent()) { + plugin.getEventManager().startEvent(defOpt.get(), null); + } else { + log.warning("Zeitplan-Event nicht gefunden: " + entry.getEventId() + " — feuere zufälliges."); + fireRandomOrDefault(); + } + } + + private void fireRandomOrDefault() { + if (plugin.getConfigManager().isRandomOnInterval()) { + Optional random = plugin.getEventRegistry().getWeightedRandom(); + random.ifPresent(def -> plugin.getEventManager().startEvent(def, null)); + } else { + String defaultId = plugin.getConfigManager().getDefaultEventId(); + Optional def = plugin.getEventRegistry().get(defaultId); + def.ifPresent(d -> plugin.getEventManager().startEvent(d, null)); + } + } + + public void stop() { + if (minuteTask != null) { minuteTask.cancel(); minuteTask = null; } + if (intervalTask != null) { intervalTask.cancel(); intervalTask = null; } + } + + public void restart() { + stop(); + start(); + } +} diff --git a/src/main/java/dev/viper/eventengine/util/ColorUtil.java b/src/main/java/dev/viper/eventengine/util/ColorUtil.java new file mode 100644 index 0000000..63dcbce --- /dev/null +++ b/src/main/java/dev/viper/eventengine/util/ColorUtil.java @@ -0,0 +1,75 @@ +package dev.viper.eventengine.util; + +import net.md_5.bungee.api.ChatColor; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Wandelt alle Farbformate um: + * &c → §c (Standard-Codes) + * &#FF5733 → Hex-Farbe (Paper / Spigot 1.16+) + * &x&F&F&5&7&3&3 → Bukkit-Hex-Format + * §c → bleibt §c (bereits konvertiert) + */ +public final class ColorUtil { + + // &#RRGGBB oder #RRGGBB + private static final Pattern HEX_HASH = Pattern.compile("&#([0-9A-Fa-f]{6})|#([0-9A-Fa-f]{6})"); + // &x&R&R&G&G&B&B (Bukkit-Hex-Format) + private static final Pattern HEX_BUKKIT = Pattern.compile("&x(&[0-9A-Fa-f]){6}"); + + private ColorUtil() {} + + /** + * Konvertiert einen String mit beliebigen Farbcodes zu einem Minecraft-farbigen String. + */ + public static String color(String input) { + if (input == null) return ""; + String s = input; + + // 1. &#RRGGBB und #RRGGBB → §x§R§R§G§G§B§B + Matcher hexHash = HEX_HASH.matcher(s); + StringBuffer sb = new StringBuffer(); + while (hexHash.find()) { + String hex = hexHash.group(1) != null ? hexHash.group(1) : hexHash.group(2); + hexHash.appendReplacement(sb, hexToBukkit(hex)); + } + hexHash.appendTail(sb); + s = sb.toString(); + + // 2. &x&R&R&G&G&B&B → §x§R§R§G§G§B§B (falls jemand es schon im Bukkit-Format angibt) + Matcher hexBukkit = HEX_BUKKIT.matcher(s); + sb = new StringBuffer(); + while (hexBukkit.find()) { + hexBukkit.appendReplacement(sb, hexBukkit.group().replace('&', '§')); + } + hexBukkit.appendTail(sb); + s = sb.toString(); + + // 3. &a–&f, &0–&9, &k–&r → §-Codes + s = ChatColor.translateAlternateColorCodes('&', s); + + return s; + } + + /** + * Entfernt alle Farbcodes aus einem String (für Vergleiche, Logs etc.) + */ + public static String strip(String input) { + if (input == null) return ""; + return ChatColor.stripColor(color(input)); + } + + /** + * Konvertiert einen 6-stelligen Hex-String (#RRGGBB ohne #) in das + * Minecraft §x§R§R§G§G§B§B Format. + */ + private static String hexToBukkit(String hex) { + StringBuilder result = new StringBuilder("§x"); + for (char c : hex.toCharArray()) { + result.append('§').append(c); + } + return result.toString(); + } +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..0eb5ac9 --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,121 @@ +# ═══════════════════════════════════════════════════════════════════ +# EventEngine — Konfiguration +# Plugin von Viper | dev.viper.eventengine +# ═══════════════════════════════════════════════════════════════════ + +# ── Allgemeine Einstellungen ───────────────────────────────────── +settings: + # Prefix für alle Plugin-Nachrichten (& = Farb-Code) + prefix: "&8[&6EventEngine&8]&r " + + # Logs Events in der Server-Konsole? + log-events: true + + # Sekunden VOR dem Event-Start, wann die Ankündigung erfolgt + announce-before-seconds: 30 + + # Intervall-Modus: Startet automatisch alle X Minuten ein Event + # Nützlich wenn kein fester Zeitplan gewünscht ist + use-interval: false + interval-minutes: 60 + + # Welches Event beim Intervall? "RANDOM" = zufällig aus Rotation + default-event: "RANDOM" + + # Zufälliges Event im Intervall-Modus? + random-on-interval: true + +# ── Zeitplan ───────────────────────────────────────────────────── +# Jeder Eintrag hat: +# days: Wochentage (MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY) +# Leer lassen oder "DAILY" für jeden Tag +# time: Uhrzeit im Format HH:MM (24h) +# event: Event-ID (aus /event list) oder "RANDOM" +# +# ─── Beispiel-Zeitplan ─────────────────────────────────────────── +schedule: + # Täglich 12 Uhr: Zufälliges Event + - days: [] + time: "12:00" + event: "RANDOM" + + # Täglich 18 Uhr: PvP Deathmatch + - days: [] + time: "18:00" + event: "pvp_deathmatch" + + # Freitag & Samstag Abend: Hunger Games + - days: [FRIDAY, SATURDAY] + time: "20:00" + event: "pvp_hunger_games" + + # Samstag Nachmittag: Build Battle + - days: [SATURDAY] + time: "15:00" + event: "build_battle" + + # Sonntag Mittag: Drop Party + - days: [SUNDAY] + time: "14:00" + event: "fun_drop_party" + + # Montag: Lotterie + - days: [MONDAY] + time: "19:00" + event: "economy_lottery" + + # Mittwoch: Team Survival + - days: [WEDNESDAY] + time: "18:00" + event: "team_survival" + +# ── Event-Overrides ─────────────────────────────────────────────── +# Hier können einzelne eingebaute Events angepasst werden. +# OHNE diese Sektion gelten die Plugin-Defaults. +# Custom Events werden in custom_events.yml gespeichert. +# +# Beispiel: +# event-overrides: +# pvp_deathmatch: +# duration-seconds: 600 +# min-players: 4 +# rewards: +# - "eco give %player% 500" +# - "give %player% diamond 3" +# fun_drop_party: +# duration-seconds: 120 +# announcement: "&6✦ &eDROP PARTY &7startet! Kommt alle auf den Spawn!" +event-overrides: {} + +# ── Event-Regionen ──────────────────────────────────────────────── +# Regionen können direkt hier oder ingame per Command gesetzt werden: +# /event region pos1 → Erste Ecke (stehe an der Position) +# /event region pos2 → Zweite Ecke → Region wird gespeichert +# /event region clear → Region entfernen (gesamte Welt) +# /event region info → Region anzeigen +# +# Für Custom Events wird die Region in custom_events.yml gespeichert. +# Für builtin Events hier in event-overrides eintragen: +# +# event-overrides: +# pvp_deathmatch: +# region: +# world: world +# min-x: -50 +# min-y: 60 +# min-z: -50 +# max-x: 50 +# max-y: 120 +# max-z: 50 +# +# ── Block-Schutz während Events ────────────────────────────────── +# TNT und Explosionen zerstören KEINE Blöcke solange ein Event läuft. +# Das gilt immer — unabhängig ob eine Region definiert ist oder nicht. +# Spieler (PvP-Schaden durch Explosionen) bleibt aktiv. +protection: + # Spieler können die Region während eines Events nicht verlassen + enforce-region-boundary: true + # TNT/Explosionen zerstören keine Blöcke während Events + no-explosion-block-damage: true + # Teilnehmer können nur innerhalb der Region Blöcke abbauen/setzen + restrict-block-interaction: true diff --git a/src/main/resources/custom_events_example.yml b/src/main/resources/custom_events_example.yml new file mode 100644 index 0000000..61cc5f1 --- /dev/null +++ b/src/main/resources/custom_events_example.yml @@ -0,0 +1,33 @@ +# ═══════════════════════════════════════════════════════════════════ +# EventEngine — Custom Events +# Diese Datei wird automatisch verwaltet. +# Zum Erstellen: /event create +# Zum Löschen: /event delete +# ═══════════════════════════════════════════════════════════════════ +# +# Du kannst Events auch manuell hier eintragen: +# +# events: +# mein_event: +# display-name: "Mein tolles Event" +# description: "Ein cooles Custom Event für unseren Server!" +# category: FUN # COMBAT, SURVIVAL, BUILDING, RACING, COLLECTION, FUN, QUIZ, ECONOMY, TEAM, CUSTOM +# duration-seconds: 300 # 0 = unbegrenzt +# min-players: 2 +# max-players: 30 +# announcement: "&6✦ &eMein Event &7startet! Seid dabei!" +# in-rotation: true # In der Zufalls-Rotation? +# weight: 2 # Gewichtung (höher = häufiger bei RANDOM) +# start-commands: +# - "broadcast Das Event startet!" +# - "title @a title {\"text\":\"EVENT START\",\"color\":\"gold\"}" +# end-commands: +# - "broadcast Das Event ist vorbei!" +# rewards: # Wird an JEDEN Teilnehmer ausgeführt (%player% = Spieler) +# - "eco give %player% 1000" +# - "give %player% golden_apple 5" +# settings: # Beliebige Custom-Einstellungen (für eigene Erweiterungen) +# pvp-enabled: false +# teleport-spawn: true + +events: {} diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..7c40262 --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,24 @@ +name: EventEngine +version: '${project.version}' +main: dev.viper.eventengine.EventEngine +api-version: '1.21' +description: 100+ Server Events mit Custom-Builder und Zeitplan +author: Viper + +commands: + event: + description: Haupt-Befehl für EventEngine + usage: /event + aliases: [ee, events] + permission: eventengine.admin + +permissions: + eventengine.admin: + description: Zugriff auf alle EventEngine-Befehle + default: op + eventengine.participate: + description: Darf an Events teilnehmen + default: true + eventengine.notify: + description: Erhält Event-Benachrichtigungen + default: true