From 87f80d30c5c298d236093f2dc18881b653694de2 Mon Sep 17 00:00:00 2001 From: Git Manager GUI Date: Wed, 8 Apr 2026 02:42:04 +0200 Subject: [PATCH] Upload folder via GUI - src --- .../dev/viper/eventengine/EventEngine.java | 14 + .../eventengine/command/EventCommand.java | 61 +++- .../command/RegionSetupManager.java | 123 ++++++++ .../eventengine/config/ConfigManager.java | 37 +++ .../config/EventOverrideLoader.java | 25 ++ .../events/builtin/FunHandlers.java | 88 +++++- .../events/builtin/MiscHandlers.java | 213 +++++++++++++- .../viper/eventengine/gui/ItemRainGUI.java | 277 ++++++++++++++++++ .../listener/RaceGoalListener.java | 55 ++++ .../eventengine/manager/EventManager.java | 19 ++ .../manager/ScoreboardManager.java | 174 +++++++++++ .../viper/eventengine/model/ActiveEvent.java | 5 + .../eventengine/model/EventDefinition.java | 65 ++++ .../viper/eventengine/model/EventRegion.java | 10 + .../viper/eventengine/model/EventType.java | 2 +- src/main/resources/config.yml | 14 + 16 files changed, 1160 insertions(+), 22 deletions(-) create mode 100644 src/main/java/dev/viper/eventengine/gui/ItemRainGUI.java create mode 100644 src/main/java/dev/viper/eventengine/listener/RaceGoalListener.java create mode 100644 src/main/java/dev/viper/eventengine/manager/ScoreboardManager.java diff --git a/src/main/java/dev/viper/eventengine/EventEngine.java b/src/main/java/dev/viper/eventengine/EventEngine.java index b51f7b7..9fbd925 100644 --- a/src/main/java/dev/viper/eventengine/EventEngine.java +++ b/src/main/java/dev/viper/eventengine/EventEngine.java @@ -18,8 +18,10 @@ public class EventEngine extends JavaPlugin { private ConfigManager configManager; private EventRegistry eventRegistry; private EventManager eventManager; + private dev.viper.eventengine.manager.ScoreboardManager scoreboardManager; private EventScheduler eventScheduler; private EventGUI eventGUI; + private dev.viper.eventengine.gui.ItemRainGUI itemRainGUI; private EventCreateListener eventCreateListener; private RegionSetupManager regionSetupManager; @@ -35,6 +37,9 @@ public class EventEngine extends JavaPlugin { eventRegistry = new EventRegistry(this); eventRegistry.init(); + // Scoreboard-Manager + scoreboardManager = new dev.viper.eventengine.manager.ScoreboardManager(this); + // Event-Manager (Lifecycle) eventManager = new EventManager(this); @@ -46,6 +51,10 @@ public class EventEngine extends JavaPlugin { eventGUI = new EventGUI(this); getServer().getPluginManager().registerEvents(eventGUI, this); + // Item-Regen GUI + itemRainGUI = new dev.viper.eventengine.gui.ItemRainGUI(this); + getServer().getPluginManager().registerEvents(itemRainGUI, this); + // Create-Listener (Custom Event Builder) eventCreateListener = new EventCreateListener(this); getServer().getPluginManager().registerEvents(eventCreateListener, this); @@ -53,6 +62,9 @@ public class EventEngine extends JavaPlugin { // Region-Schutz Listener getServer().getPluginManager().registerEvents(new EventProtectionListener(this), this); + // Race-Goal Listener + getServer().getPluginManager().registerEvents(new dev.viper.eventengine.listener.RaceGoalListener(this), this); + // Region-Setup Manager regionSetupManager = new RegionSetupManager(this); @@ -79,8 +91,10 @@ public class EventEngine extends JavaPlugin { public ConfigManager getConfigManager() { return configManager; } public EventRegistry getEventRegistry() { return eventRegistry; } public EventManager getEventManager() { return eventManager; } + public dev.viper.eventengine.manager.ScoreboardManager getScoreboardManager() { return scoreboardManager; } public EventScheduler getEventScheduler() { return eventScheduler; } public EventGUI getEventGUI() { return eventGUI; } + public dev.viper.eventengine.gui.ItemRainGUI getItemRainGUI() { return itemRainGUI; } public EventCreateListener getEventCreateListener() { return eventCreateListener; } public RegionSetupManager getRegionSetupManager() { return regionSetupManager; } diff --git a/src/main/java/dev/viper/eventengine/command/EventCommand.java b/src/main/java/dev/viper/eventengine/command/EventCommand.java index 5c5664f..ff3b7d6 100644 --- a/src/main/java/dev/viper/eventengine/command/EventCommand.java +++ b/src/main/java/dev/viper/eventengine/command/EventCommand.java @@ -44,6 +44,13 @@ public class EventCommand implements CommandExecutor, TabCompleter { requirePlayer(sender, () -> plugin.getEventGUI().openMain((Player) sender)); } + // ─── ITEM-REGEN GUI ────────────────────────────────────────── + case "itemrain" -> { + requirePerm(sender, () -> { + requirePlayer(sender, () -> plugin.getItemRainGUI().open((Player) sender)); + }); + } + // ─── START ───────────────────────────────────────────────────── case "start" -> { requirePerm(sender, () -> { @@ -89,6 +96,9 @@ public class EventCommand implements CommandExecutor, TabCompleter { 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("§7 Winner-Belohnungen: §f" + def.getWinnerRewards().size()); + sender.sendMessage("§7 Startbereich: §f" + (def.hasStartRegion() ? "gesetzt" : "-")); + sender.sendMessage("§7 Zielbereich: §f" + (def.hasGoalRegion() ? "gesetzt" : "-")); sender.sendMessage("§8§m══════════════════════════════"); }, () -> sender.sendMessage(p + "§cEvent nicht gefunden.")); } else { @@ -231,6 +241,43 @@ public class EventCommand implements CommandExecutor, TabCompleter { }); } + // ─── RACE START/GOAL ─────────────────────────────────────── + case "race" -> { + requirePerm(sender, () -> { + requirePlayer(sender, () -> { + if (args.length < 4) { + sender.sendMessage(p + "§cUsage: /event race "); + return; + } + String target = args[1].toLowerCase(); + String sub = args[2].toLowerCase(); + String id = args[3].toLowerCase(); + RegionSetupManager rsm = plugin.getRegionSetupManager(); + if (target.equals("start")) { + switch (sub) { + case "pos1" -> rsm.handleStartPos1((Player) sender, id); + case "pos2" -> rsm.handleStartPos2((Player) sender, id); + case "clear" -> rsm.handleStartClear((Player) sender, id); + case "info" -> rsm.handleStartInfo((Player) sender, id); + default -> sender.sendMessage(p + "§cUnbekannte Sub-Option. pos1 | pos2 | clear | info"); + } + return; + } + if (target.equals("goal")) { + switch (sub) { + case "pos1" -> rsm.handleGoalPos1((Player) sender, id); + case "pos2" -> rsm.handleGoalPos2((Player) sender, id); + case "clear" -> rsm.handleGoalClear((Player) sender, id); + case "info" -> rsm.handleGoalInfo((Player) sender, id); + default -> sender.sendMessage(p + "§cUnbekannte Sub-Option. pos1 | pos2 | clear | info"); + } + return; + } + sender.sendMessage(p + "§cUnbekannte Option. start | goal"); + }); + }); + } + default -> sendHelp(sender); } @@ -243,7 +290,7 @@ public class EventCommand implements CommandExecutor, TabCompleter { 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"); + "schedule", "create", "delete", "score", "rotation", "region", "race", "itemrain", "reload"); } if (args.length == 2) { return switch (args[0].toLowerCase()) { @@ -257,15 +304,25 @@ public class EventCommand implements CommandExecutor, TabCompleter { case "rotation" -> filter(args[1], "add", "remove"); case "score" -> null; // Spielernamen case "region" -> filter(args[1], "pos1", "pos2", "clear", "info"); + case "race" -> filter(args[1], "start", "goal"); default -> null; }; } + if (args.length == 3 && args[0].equalsIgnoreCase("race")) { + return filter(args[2], "pos1", "pos2", "clear", "info"); + } 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()); } + if (args.length == 4 && args[0].equalsIgnoreCase("race")) { + return plugin.getEventRegistry().getAll().stream() + .map(EventDefinition::getId) + .filter(id -> id.startsWith(args[3].toLowerCase())) + .collect(Collectors.toList()); + } return null; } @@ -287,6 +344,8 @@ public class EventCommand implements CommandExecutor, TabCompleter { 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("§e /event race §7- Start/Ziel setzen"); + sender.sendMessage("§e /event itemrain §7- Item-Regen GUI"); sender.sendMessage("§8§m══════════════════════════════"); } diff --git a/src/main/java/dev/viper/eventengine/command/RegionSetupManager.java b/src/main/java/dev/viper/eventengine/command/RegionSetupManager.java index 240b51d..628d061 100644 --- a/src/main/java/dev/viper/eventengine/command/RegionSetupManager.java +++ b/src/main/java/dev/viper/eventengine/command/RegionSetupManager.java @@ -23,6 +23,8 @@ public class RegionSetupManager { private final EventEngine plugin; // Temporäre Pos1-Speicher pro Spieler: UUID → [eventId, Location] private final Map pos1Selection = new HashMap<>(); + private final Map startPos1Selection = new HashMap<>(); + private final Map goalPos1Selection = new HashMap<>(); public RegionSetupManager(EventEngine plugin) { this.plugin = plugin; @@ -78,6 +80,120 @@ public class RegionSetupManager { player.sendMessage(plugin.prefix() + "§7Region für §e" + def.getDisplayName() + " §7entfernt (gesamte Welt)."); } + public void handleStartPos1(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; } + + startPos1Selection.put(player.getUniqueId(), new Object[]{eventId, player.getLocation().clone()}); + player.sendMessage(plugin.prefix() + "§aStart-Pos1 gesetzt: §f" + formatLoc(player)); + player.sendMessage(plugin.prefix() + "§7Jetzt: §f/event race start pos2 " + eventId); + } + + public void handleStartPos2(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 = startPos1Selection.get(player.getUniqueId()); + if (sel == null || !sel[0].equals(eventId)) { + player.sendMessage(plugin.prefix() + "§cZuerst Start-Pos1 setzen: §f/event race start 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.setStartRegion(region); + if (def.isCustom()) plugin.getConfigManager().saveCustomEvent(def); + startPos1Selection.remove(player.getUniqueId()); + + player.sendMessage(plugin.prefix() + "§a✔ Startbereich gesetzt für §e" + def.getDisplayName() + "§a:"); + sendRegionInfo(player, region); + } + + public void handleStartClear(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.setStartRegion(null); + if (def.isCustom()) plugin.getConfigManager().saveCustomEvent(def); + player.sendMessage(plugin.prefix() + "§7Startbereich für §e" + def.getDisplayName() + " §7entfernt."); + } + + public void handleStartInfo(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.hasStartRegion()) { + player.sendMessage(plugin.prefix() + "§e" + def.getDisplayName() + " §7hat keinen Startbereich."); + return; + } + player.sendMessage("§8§m══════════════════════════════"); + player.sendMessage("§6 Startbereich: §e" + def.getDisplayName()); + sendRegionInfo(player, def.getStartRegion()); + player.sendMessage("§8§m══════════════════════════════"); + } + + public void handleGoalPos1(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; } + + goalPos1Selection.put(player.getUniqueId(), new Object[]{eventId, player.getLocation().clone()}); + player.sendMessage(plugin.prefix() + "§aZiel-Pos1 gesetzt: §f" + formatLoc(player)); + player.sendMessage(plugin.prefix() + "§7Jetzt: §f/event race goal pos2 " + eventId); + } + + public void handleGoalPos2(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 = goalPos1Selection.get(player.getUniqueId()); + if (sel == null || !sel[0].equals(eventId)) { + player.sendMessage(plugin.prefix() + "§cZuerst Ziel-Pos1 setzen: §f/event race goal 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.setGoalRegion(region); + if (def.isCustom()) plugin.getConfigManager().saveCustomEvent(def); + goalPos1Selection.remove(player.getUniqueId()); + + player.sendMessage(plugin.prefix() + "§a✔ Zielbereich gesetzt für §e" + def.getDisplayName() + "§a:"); + sendRegionInfo(player, region); + } + + public void handleGoalClear(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.setGoalRegion(null); + if (def.isCustom()) plugin.getConfigManager().saveCustomEvent(def); + player.sendMessage(plugin.prefix() + "§7Zielbereich für §e" + def.getDisplayName() + " §7entfernt."); + } + + public void handleGoalInfo(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.hasGoalRegion()) { + player.sendMessage(plugin.prefix() + "§e" + def.getDisplayName() + " §7hat keinen Zielbereich."); + return; + } + player.sendMessage("§8§m══════════════════════════════"); + player.sendMessage("§6 Zielbereich: §e" + def.getDisplayName()); + sendRegionInfo(player, def.getGoalRegion()); + player.sendMessage("§8§m══════════════════════════════"); + } + 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; } @@ -101,4 +217,11 @@ public class RegionSetupManager { + p.getLocation().getBlockY() + ", " + p.getLocation().getBlockZ(); } + + private void sendRegionInfo(Player player, EventRegion region) { + 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()); + } } diff --git a/src/main/java/dev/viper/eventengine/config/ConfigManager.java b/src/main/java/dev/viper/eventengine/config/ConfigManager.java index 07e1529..1c3a469 100644 --- a/src/main/java/dev/viper/eventengine/config/ConfigManager.java +++ b/src/main/java/dev/viper/eventengine/config/ConfigManager.java @@ -4,6 +4,7 @@ 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.Material; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; @@ -18,6 +19,18 @@ import java.util.logging.Logger; public class ConfigManager { + private static final List DEFAULT_ITEM_RAIN_ITEMS = List.of( + 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 final Logger log; @@ -44,6 +57,7 @@ public class ConfigManager { private boolean enforceRegionBoundary; private boolean noExplosionBlockDamage; private boolean restrictBlockInteraction; + private List itemRainItems; public ConfigManager(EventEngine plugin) { this.plugin = plugin; @@ -73,6 +87,16 @@ public class ConfigManager { 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 ")); + + List rawItems = mainConfig.getStringList("item-rain.items"); + itemRainItems = new ArrayList<>(); + for (String name : rawItems) { + try { + Material mat = Material.valueOf(name.toUpperCase()); + if (mat.isItem()) itemRainItems.add(mat); + } catch (Exception ignored) {} + } + if (itemRainItems.isEmpty()) itemRainItems.addAll(DEFAULT_ITEM_RAIN_ITEMS); } private void loadSchedule() { @@ -166,4 +190,17 @@ public class ConfigManager { public boolean isNoExplosionBlockDamage() { return noExplosionBlockDamage; } public boolean isRestrictBlockInteraction() { return restrictBlockInteraction; } public FileConfiguration getMainConfig() { return mainConfig; } + public List getItemRainItems() { return Collections.unmodifiableList(itemRainItems); } + + public void setItemRainItems(List items) { + itemRainItems = new ArrayList<>(items); + if (itemRainItems.isEmpty()) itemRainItems.addAll(DEFAULT_ITEM_RAIN_ITEMS); + List raw = itemRainItems.stream().map(Material::name).toList(); + mainConfig.set("item-rain.items", raw); + plugin.saveConfig(); + } + + public void resetItemRainItems() { + setItemRainItems(DEFAULT_ITEM_RAIN_ITEMS); + } } diff --git a/src/main/java/dev/viper/eventengine/config/EventOverrideLoader.java b/src/main/java/dev/viper/eventengine/config/EventOverrideLoader.java index b17b8ba..6b72fca 100644 --- a/src/main/java/dev/viper/eventengine/config/EventOverrideLoader.java +++ b/src/main/java/dev/viper/eventengine/config/EventOverrideLoader.java @@ -39,10 +39,35 @@ public class EventOverrideLoader { 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("winner-rewards")) def.setWinnerRewards(sec.getStringList("winner-rewards")); if (sec.contains("category")) { try { def.setCategory(EventCategory.valueOf(sec.getString("category").toUpperCase())); } catch (Exception ignored) {} } + if (sec.contains("start-region.world")) { + try { + String w = sec.getString("start-region.world"); + int minX = sec.getInt("start-region.min-x"); + int minY = sec.getInt("start-region.min-y"); + int minZ = sec.getInt("start-region.min-z"); + int maxX = sec.getInt("start-region.max-x"); + int maxY = sec.getInt("start-region.max-y"); + int maxZ = sec.getInt("start-region.max-z"); + def.setStartRegion(new dev.viper.eventengine.model.EventRegion(w, minX, minY, minZ, maxX, maxY, maxZ)); + } catch (Exception ignored) {} + } + if (sec.contains("goal-region.world")) { + try { + String w = sec.getString("goal-region.world"); + int minX = sec.getInt("goal-region.min-x"); + int minY = sec.getInt("goal-region.min-y"); + int minZ = sec.getInt("goal-region.min-z"); + int maxX = sec.getInt("goal-region.max-x"); + int maxY = sec.getInt("goal-region.max-y"); + int maxZ = sec.getInt("goal-region.max-z"); + def.setGoalRegion(new dev.viper.eventengine.model.EventRegion(w, minX, minY, minZ, maxX, maxY, maxZ)); + } catch (Exception ignored) {} + } ConfigurationSection settings = sec.getConfigurationSection("settings"); if (settings != null) { for (String key : settings.getKeys(false)) { diff --git a/src/main/java/dev/viper/eventengine/events/builtin/FunHandlers.java b/src/main/java/dev/viper/eventengine/events/builtin/FunHandlers.java index 10329b9..2494938 100644 --- a/src/main/java/dev/viper/eventengine/events/builtin/FunHandlers.java +++ b/src/main/java/dev/viper/eventengine/events/builtin/FunHandlers.java @@ -12,6 +12,7 @@ import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; import org.bukkit.scheduler.BukkitRunnable; +import java.util.List; import java.util.Random; import java.util.UUID; @@ -24,6 +25,7 @@ class TntRainHandler implements IEventHandler, Listener { private ActiveEvent currentEvent; private BukkitRunnable rain; private final Random rng = new Random(); + private final java.util.Set spawnedTnt = new java.util.HashSet<>(); TntRainHandler(EventEngine plugin) { this.plugin = plugin; } @@ -47,6 +49,7 @@ class TntRainHandler implements IEventHandler, Listener { (rng.nextDouble() - 0.5) * 20, 15, (rng.nextDouble() - 0.5) * 20); TNTPrimed tnt = p.getWorld().spawn(loc, TNTPrimed.class); tnt.setFuseTicks(40); + spawnedTnt.add(tnt.getUniqueId()); } } }; @@ -57,12 +60,25 @@ class TntRainHandler implements IEventHandler, Listener { public void onEnd(ActiveEvent event) { HandlerList.unregisterAll(this); if (rain != null) rain.cancel(); + clearSpawnedTnt(); for (UUID uuid : event.getParticipants()) { Player p = Bukkit.getPlayer(uuid); if (p != null) p.removePotionEffect(PotionEffectType.RESISTANCE); } this.currentEvent = null; } + + private void clearSpawnedTnt() { + if (spawnedTnt.isEmpty()) return; + for (World world : Bukkit.getWorlds()) { + for (TNTPrimed tnt : world.getEntitiesByClass(TNTPrimed.class)) { + if (spawnedTnt.remove(tnt.getUniqueId())) { + tnt.remove(); + } + } + } + spawnedTnt.clear(); + } } // ═══════════════════════════════════════════════════════════════════ @@ -70,16 +86,13 @@ class TntRainHandler implements IEventHandler, Listener { // ═══════════════════════════════════════════════════════════════════ 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(); + private BukkitRunnable afkCheck; + private final java.util.Map lastMove = new java.util.HashMap<>(); + private final java.util.Map warnedAt = new java.util.HashMap<>(); ItemRainHandler(EventEngine plugin) { this.plugin = plugin; } @@ -87,13 +100,21 @@ class ItemRainHandler implements IEventHandler, Listener { public void onStart(ActiveEvent event) { this.currentEvent = event; Bukkit.broadcastMessage(plugin.prefix() + "§d🌧 §eItem-Regen! §7Items fallen vom Himmel!"); + Bukkit.getPluginManager().registerEvents(this, plugin); + + long now = System.currentTimeMillis(); + for (UUID uuid : event.getParticipants()) { + lastMove.put(uuid, now); + warnedAt.remove(uuid); + } rain = new BukkitRunnable() { @Override public void run() { if (currentEvent == null) { cancel(); return; } for (Player p : Bukkit.getOnlinePlayers()) { - Material mat = ITEMS[rng.nextInt(ITEMS.length)]; + List items = plugin.getConfigManager().getItemRainItems(); + Material mat = items.get(rng.nextInt(items.size())); 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))); @@ -101,14 +122,67 @@ class ItemRainHandler implements IEventHandler, Listener { } }; rain.runTaskTimer(plugin, 10L, 15L); + + afkCheck = new BukkitRunnable() { + @Override + public void run() { + if (currentEvent == null) { cancel(); return; } + long now = System.currentTimeMillis(); + for (UUID uuid : new java.util.ArrayList<>(currentEvent.getParticipants())) { + Player p = Bukkit.getPlayer(uuid); + if (p == null) continue; + long last = lastMove.getOrDefault(uuid, now); + if (now - last < 60_000L) continue; + + Long warned = warnedAt.get(uuid); + if (warned == null) { + warnedAt.put(uuid, now); + p.sendMessage(plugin.prefix() + "§eDu bist seit 1 Minute AFK. Bewege dich, sonst wirst du entfernt!"); + continue; + } + if (now - warned >= 10_000L) { + warnedAt.remove(uuid); + plugin.getEventManager().leave(p); + p.sendMessage(plugin.prefix() + "§cAFK: Du wurdest aus dem Event entfernt."); + } + } + } + }; + afkCheck.runTaskTimer(plugin, 20L, 20L); } @Override public void onEnd(ActiveEvent event) { HandlerList.unregisterAll(this); if (rain != null) rain.cancel(); + if (afkCheck != null) afkCheck.cancel(); + lastMove.clear(); + warnedAt.clear(); this.currentEvent = null; } + + @Override + public void onPlayerJoin(ActiveEvent event, Player player) { + lastMove.put(player.getUniqueId(), System.currentTimeMillis()); + warnedAt.remove(player.getUniqueId()); + } + + @Override + public void onPlayerLeave(ActiveEvent event, Player player) { + lastMove.remove(player.getUniqueId()); + warnedAt.remove(player.getUniqueId()); + } + + @org.bukkit.event.EventHandler(ignoreCancelled = true) + public void onMove(org.bukkit.event.player.PlayerMoveEvent e) { + if (currentEvent == null || !currentEvent.isParticipant(e.getPlayer())) return; + if (e.getFrom().getBlockX() == e.getTo().getBlockX() + && e.getFrom().getBlockY() == e.getTo().getBlockY() + && e.getFrom().getBlockZ() == e.getTo().getBlockZ()) return; + + lastMove.put(e.getPlayer().getUniqueId(), System.currentTimeMillis()); + warnedAt.remove(e.getPlayer().getUniqueId()); + } } // ═══════════════════════════════════════════════════════════════════ diff --git a/src/main/java/dev/viper/eventengine/events/builtin/MiscHandlers.java b/src/main/java/dev/viper/eventengine/events/builtin/MiscHandlers.java index dd407ea..0244414 100644 --- a/src/main/java/dev/viper/eventengine/events/builtin/MiscHandlers.java +++ b/src/main/java/dev/viper/eventengine/events/builtin/MiscHandlers.java @@ -12,6 +12,7 @@ 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.util.Vector; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; import org.bukkit.scheduler.BukkitRunnable; @@ -28,15 +29,38 @@ class BaseRaceHandler implements IEventHandler { protected final String icon; protected final String name; protected BukkitRunnable ticker; + private final java.util.Random rng = new java.util.Random(); 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!"); + teleportToStart(e); + warnIfMissingGoal(e); 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(); } + + protected void teleportToStart(ActiveEvent e) { + var start = e.getDefinition().getStartRegion(); + if (start == null) { + Bukkit.broadcastMessage(plugin.prefix() + "§e⚠ Kein Startbereich gesetzt. /event race start pos1/pos2 " + e.getDefinition().getId()); + return; + } + for (UUID u : e.getParticipants()) { + Player p = Bukkit.getPlayer(u); + if (p == null) continue; + org.bukkit.Location loc = start.randomPoint(rng); + if (loc != null) p.teleport(loc); + } + } + + protected void warnIfMissingGoal(ActiveEvent e) { + if (!e.getDefinition().hasGoalRegion()) { + Bukkit.broadcastMessage(plugin.prefix() + "§e⚠ Kein Zielbereich gesetzt. /event race goal pos1/pos2 " + e.getDefinition().getId()); + } + } } class ParkourRaceHandler extends BaseRaceHandler { @@ -367,30 +391,154 @@ class ColorWarHandler implements IEventHandler, Listener { class TinyPlayersHandler implements IEventHandler { private final EventEngine p; + private final Map prevScale = new HashMap<>(); 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));} + List players = new ArrayList<>(); + if (e.getParticipants().isEmpty()) { + players.addAll(Bukkit.getOnlinePlayers()); + } else { + for(UUID u:e.getParticipants()){Player pl=Bukkit.getPlayer(u);if(pl!=null)players.add(pl);} + } + for (Player pl : players) { + applyScale(pl, 0.6); + 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);}}} + @Override public void onEnd(ActiveEvent e){ + if (e.getParticipants().isEmpty()) { + for (Player pl : Bukkit.getOnlinePlayers()) { + restoreScale(pl); + pl.removePotionEffect(PotionEffectType.SLOWNESS); + pl.removePotionEffect(PotionEffectType.JUMP_BOOST); + } + } else { + for(UUID u:e.getParticipants()){ + Player pl=Bukkit.getPlayer(u); + if(pl!=null){ + restoreScale(pl); + pl.removePotionEffect(PotionEffectType.SLOWNESS); + pl.removePotionEffect(PotionEffectType.JUMP_BOOST); + } + } + } + prevScale.clear(); + } + + private void applyScale(Player pl, double scale) { + org.bukkit.attribute.Attribute attrType = getScaleAttribute(); + if (attrType == null) return; + org.bukkit.attribute.AttributeInstance attr = pl.getAttribute(attrType); + if (attr == null) return; + prevScale.putIfAbsent(pl.getUniqueId(), attr.getBaseValue()); + attr.setBaseValue(scale); + } + + private void restoreScale(Player pl) { + org.bukkit.attribute.Attribute attrType = getScaleAttribute(); + if (attrType == null) return; + org.bukkit.attribute.AttributeInstance attr = pl.getAttribute(attrType); + if (attr == null) return; + Double prev = prevScale.remove(pl.getUniqueId()); + if (prev != null) attr.setBaseValue(prev); + } + + private org.bukkit.attribute.Attribute getScaleAttribute() { + try { + return org.bukkit.attribute.Attribute.valueOf("GENERIC_SCALE"); + } catch (IllegalArgumentException ex) { + return null; + } + } } class GiantPlayersHandler implements IEventHandler { private final EventEngine p; + private final Map prevScale = new HashMap<>(); 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));} + List players = new ArrayList<>(); + if (e.getParticipants().isEmpty()) { + players.addAll(Bukkit.getOnlinePlayers()); + } else { + for(UUID u:e.getParticipants()){Player pl=Bukkit.getPlayer(u);if(pl!=null)players.add(pl);} + } + for(Player pl:players){ + applyScale(pl, 1.6); + 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);}}} + @Override public void onEnd(ActiveEvent e){ + if (e.getParticipants().isEmpty()) { + for (Player pl : Bukkit.getOnlinePlayers()) { + restoreScale(pl); + pl.removePotionEffect(PotionEffectType.SPEED); + pl.removePotionEffect(PotionEffectType.STRENGTH); + } + } else { + for(UUID u:e.getParticipants()){ + Player pl=Bukkit.getPlayer(u); + if(pl!=null){ + restoreScale(pl); + pl.removePotionEffect(PotionEffectType.SPEED); + pl.removePotionEffect(PotionEffectType.STRENGTH); + } + } + } + prevScale.clear(); + } + + private void applyScale(Player pl, double scale) { + org.bukkit.attribute.Attribute attrType = getScaleAttribute(); + if (attrType == null) return; + org.bukkit.attribute.AttributeInstance attr = pl.getAttribute(attrType); + if (attr == null) return; + prevScale.putIfAbsent(pl.getUniqueId(), attr.getBaseValue()); + attr.setBaseValue(scale); + } + + private void restoreScale(Player pl) { + org.bukkit.attribute.Attribute attrType = getScaleAttribute(); + if (attrType == null) return; + org.bukkit.attribute.AttributeInstance attr = pl.getAttribute(attrType); + if (attr == null) return; + Double prev = prevScale.remove(pl.getUniqueId()); + if (prev != null) attr.setBaseValue(prev); + } + + private org.bukkit.attribute.Attribute getScaleAttribute() { + try { + return org.bukkit.attribute.Attribute.valueOf("GENERIC_SCALE"); + } catch (IllegalArgumentException ex) { + return null; + } + } } 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));} + if (e.getParticipants().isEmpty()) { + for (Player pl : Bukkit.getOnlinePlayers()) { + pl.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY,e.getDefinition().getDurationSeconds()*20,0,false,false)); + } + } else { + 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);}} + @Override public void onEnd(ActiveEvent e){ + if (e.getParticipants().isEmpty()) { + for (Player pl : Bukkit.getOnlinePlayers()) { + pl.removePotionEffect(PotionEffectType.INVISIBILITY); + } + } else { + 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; @@ -398,7 +546,25 @@ class RandomTeleportHandler implements IEventHandler { @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=new BukkitRunnable(){@Override public void run(){ + if(e.getState()==ActiveEvent.State.ENDED){cancel();return;} + if (e.getParticipants().isEmpty()) { + for (Player p : Bukkit.getOnlinePlayers()) { + if (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!"); + } + 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(); } @@ -407,12 +573,20 @@ 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); + List players = new ArrayList<>(); + if (e.getParticipants().isEmpty()) { + players.addAll(Bukkit.getOnlinePlayers()); + } else { + for (UUID u : e.getParticipants()) { + Player pl = Bukkit.getPlayer(u); + if (pl != null) players.add(pl); + } + } + if(players.size()<2){Bukkit.broadcastMessage(p.prefix()+"§cMindestens 2 Spieler benötigt!");return;} + Collections.shuffle(players); 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 pageMap = new HashMap<>(); + private final Map filterMap = new HashMap<>(); + private final Map sortMap = new HashMap<>(); + private final Set awaitingSearch = new HashSet<>(); + + public ItemRainGUI(EventEngine plugin) { + this.plugin = plugin; + } + + public void open(Player player) { + open(player, 0); + } + + public void open(Player player, int page) { + Inventory inv = Bukkit.createInventory(null, 54, TITLE); + fillBorder(inv, Material.GRAY_STAINED_GLASS_PANE); + + List items = applyFilterAndSort(player, plugin.getConfigManager().getItemRainItems()); + int[] slots = buildContentSlots(); + int perPage = slots.length; + int totalPages = Math.max(1, (int) Math.ceil(items.size() / (double) perPage)); + page = Math.max(0, Math.min(page, totalPages - 1)); + pageMap.put(player.getUniqueId(), page); + + int start = page * perPage; + int end = Math.min(items.size(), start + perPage); + for (int i = start; i < end; i++) { + Material mat = items.get(i); + List lore = List.of("§7Klicken zum Entfernen"); + inv.setItem(slots[i - start], makeItem(mat, "§e" + mat.name(), lore)); + } + + if (page > 0) inv.setItem(45, makeItem(Material.ARROW, "§7◀ Zurueck", List.of("§7Seite " + page))); + if (page < totalPages - 1) inv.setItem(53, makeItem(Material.ARROW, "§7Weiter ▶", List.of("§7Seite " + (page + 2)))); + + inv.setItem(46, makeItem(Material.NAME_TAG, "§bSuche", List.of( + "§7Filter: §f" + currentFilter(player), + "§7Klicken um im Chat zu suchen" + ))); + inv.setItem(47, makeItem(Material.HOPPER, "§eSortierung", List.of( + "§7" + sortLabel(player), + "§7Klicken um zu wechseln" + ))); + inv.setItem(48, makeItem(Material.REDSTONE_BLOCK, "§cReset", List.of("§7Standard-Items wiederherstellen"))); + inv.setItem(49, makeItem(Material.BARRIER, "§cSchliessen", List.of("§7GUI schliessen"))); + inv.setItem(50, makeItem(Material.CHEST, "§aAus Inventar", List.of( + "§7Fuegt alle Items aus", + "§7deinem Inventar hinzu" + ))); + inv.setItem(52, makeItem(Material.BOOK, "§aHinzufuegen", List.of( + "§7Lege ein Item auf den Cursor", + "§7und klicke auf einen freien Slot." + ))); + + player.openInventory(inv); + } + + @EventHandler + public void onClick(InventoryClickEvent event) { + if (!(event.getWhoClicked() instanceof Player player)) return; + if (!event.getView().getTitle().equals(TITLE)) return; + + event.setCancelled(true); + + ItemStack clicked = event.getCurrentItem(); + ItemStack cursor = event.getCursor(); + + if (clicked != null && clicked.getType() == Material.BARRIER + && clicked.getItemMeta() != null + && clicked.getItemMeta().getDisplayName().contains("Schliessen")) { + player.closeInventory(); + return; + } + + List items = new ArrayList<>(plugin.getConfigManager().getItemRainItems()); + Set itemSet = new HashSet<>(items); + + if (clicked != null && clicked.getType() == Material.ARROW) { + int page = pageMap.getOrDefault(player.getUniqueId(), 0); + if (clicked.getItemMeta() != null && clicked.getItemMeta().getDisplayName().contains("Zurueck")) { + open(player, page - 1); + } else { + open(player, page + 1); + } + return; + } + + if (clicked != null && clicked.getType() == Material.NAME_TAG) { + awaitingSearch.add(player.getUniqueId()); + player.closeInventory(); + player.sendMessage(plugin.prefix() + "§7Suche: Tippe einen Begriff im Chat (leer = Filter loeschen)." ); + return; + } + + if (clicked != null && clicked.getType() == Material.HOPPER) { + cycleSort(player); + open(player, 0); + return; + } + + if (clicked != null && clicked.getType() == Material.CHEST) { + addAllFromInventory(player, items, itemSet); + plugin.getConfigManager().setItemRainItems(items); + open(player, 0); + return; + } + + if (clicked != null && clicked.getType() == Material.REDSTONE_BLOCK + && clicked.getItemMeta() != null + && clicked.getItemMeta().getDisplayName().contains("Reset")) { + plugin.getConfigManager().resetItemRainItems(); + open(player, 0); + return; + } + + if (clicked != null && clicked.getType() != Material.AIR + && clicked.getType() != Material.GRAY_STAINED_GLASS_PANE + && clicked.getType() != Material.BOOK + && clicked.getType() != Material.NAME_TAG + && clicked.getType() != Material.HOPPER + && clicked.getType() != Material.CHEST + && clicked.getType() != Material.BARRIER + && clicked.getType() != Material.REDSTONE_BLOCK) { + Material mat = clicked.getType(); + items.remove(mat); + plugin.getConfigManager().setItemRainItems(items); + open(player, 0); + return; + } + + if (cursor != null && cursor.getType() != Material.AIR) { + Material mat = cursor.getType(); + if (!mat.isItem()) return; + if (!itemSet.contains(mat)) { + items.add(mat); + plugin.getConfigManager().setItemRainItems(items); + open(player, 0); + } + } + } + + @EventHandler + public void onChat(AsyncPlayerChatEvent event) { + Player player = event.getPlayer(); + if (!awaitingSearch.remove(player.getUniqueId())) return; + event.setCancelled(true); + + String msg = event.getMessage().trim(); + if (msg.isEmpty()) filterMap.remove(player.getUniqueId()); + else filterMap.put(player.getUniqueId(), msg.toLowerCase()); + + Bukkit.getScheduler().runTask(plugin, () -> open(player, 0)); + } + + 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() { + 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(); + } + + private List applyFilterAndSort(Player player, List items) { + String filter = currentFilter(player); + List filtered = new ArrayList<>(); + for (Material mat : items) { + if (filter.isEmpty() || mat.name().toLowerCase().contains(filter)) { + filtered.add(mat); + } + } + + SortMode mode = sortMap.getOrDefault(player.getUniqueId(), SortMode.DEFAULT); + if (mode == SortMode.AZ) { + filtered.sort(Comparator.comparing(Material::name)); + } else if (mode == SortMode.ZA) { + filtered.sort(Comparator.comparing(Material::name).reversed()); + } else if (mode == SortMode.RARE) { + filtered.sort(Comparator.comparingInt(Material::getMaxStackSize)); + } else if (mode == SortMode.COMMON) { + filtered.sort(Comparator.comparingInt(Material::getMaxStackSize).reversed()); + } + return filtered; + } + + private String currentFilter(Player player) { + return filterMap.getOrDefault(player.getUniqueId(), ""); + } + + private String sortLabel(Player player) { + SortMode mode = sortMap.getOrDefault(player.getUniqueId(), SortMode.DEFAULT); + return switch (mode) { + case DEFAULT -> "Sortierung: Standard"; + case AZ -> "Sortierung: A-Z"; + case ZA -> "Sortierung: Z-A"; + case RARE -> "Sortierung: Selten (Stack)"; + case COMMON -> "Sortierung: Haeufig (Stack)"; + }; + } + + private void cycleSort(Player player) { + SortMode mode = sortMap.getOrDefault(player.getUniqueId(), SortMode.DEFAULT); + SortMode next = switch (mode) { + case DEFAULT -> SortMode.AZ; + case AZ -> SortMode.ZA; + case ZA -> SortMode.RARE; + case RARE -> SortMode.COMMON; + case COMMON -> SortMode.DEFAULT; + }; + sortMap.put(player.getUniqueId(), next); + } + + private void addAllFromInventory(Player player, List items, Set itemSet) { + for (ItemStack stack : player.getInventory().getContents()) { + if (stack == null || stack.getType() == Material.AIR) continue; + Material mat = stack.getType(); + if (!mat.isItem()) continue; + if (itemSet.add(mat)) items.add(mat); + } + } + + private enum SortMode { + DEFAULT, + AZ, + ZA, + RARE, + COMMON + } +} diff --git a/src/main/java/dev/viper/eventengine/listener/RaceGoalListener.java b/src/main/java/dev/viper/eventengine/listener/RaceGoalListener.java new file mode 100644 index 0000000..420f01a --- /dev/null +++ b/src/main/java/dev/viper/eventengine/listener/RaceGoalListener.java @@ -0,0 +1,55 @@ +package dev.viper.eventengine.listener; + +import dev.viper.eventengine.EventEngine; +import dev.viper.eventengine.model.ActiveEvent; +import dev.viper.eventengine.model.EventCategory; +import dev.viper.eventengine.model.EventDefinition; +import dev.viper.eventengine.model.EventRegion; +import dev.viper.eventengine.model.EventType; +import org.bukkit.Location; +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.PlayerMoveEvent; + +public class RaceGoalListener implements Listener { + + private final EventEngine plugin; + + public RaceGoalListener(EventEngine plugin) { + this.plugin = plugin; + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onMove(PlayerMoveEvent e) { + ActiveEvent active = plugin.getEventManager().getCurrentEvent(); + if (active == null || active.getState() != ActiveEvent.State.RUNNING) return; + + Player player = e.getPlayer(); + if (!active.isParticipant(player)) return; + + EventDefinition def = active.getDefinition(); + if (!isRaceEvent(def)) return; + + EventRegion goal = def.getGoalRegion(); + if (goal == null) return; + + Location to = e.getTo(); + if (to == null) return; + + if (e.getFrom().getBlockX() == to.getBlockX() + && e.getFrom().getBlockY() == to.getBlockY() + && e.getFrom().getBlockZ() == to.getBlockZ()) return; + + if (!goal.contains(to)) return; + if (active.hasWinner()) return; + + plugin.getEventManager().endEventWithWinner(player, "Ziel erreicht"); + } + + private boolean isRaceEvent(EventDefinition def) { + if (def.getCategory() == EventCategory.RACING) return true; + return def.getType() == EventType.TEAM_RELAY_RACE; + } +} diff --git a/src/main/java/dev/viper/eventengine/manager/EventManager.java b/src/main/java/dev/viper/eventengine/manager/EventManager.java index e8ad6f2..9e2e451 100644 --- a/src/main/java/dev/viper/eventengine/manager/EventManager.java +++ b/src/main/java/dev/viper/eventengine/manager/EventManager.java @@ -53,6 +53,7 @@ public class EventManager { runCommands(def.getStartCommands(), null); handlerRegistry.get(def.getType()).ifPresent(h -> h.onStart(currentEvent)); + plugin.getScoreboardManager().start(currentEvent); if (def.getDurationSeconds() > 0) { final ActiveEvent snap = currentEvent; @@ -87,6 +88,7 @@ public class EventManager { Bukkit.getScheduler().cancelTask(currentEvent.getTaskId()); handlerRegistry.get(def.getType()).ifPresent(h -> h.onEnd(currentEvent)); + plugin.getScoreboardManager().stop(); broadcast(plugin.prefix() + "§6✦ §e" + def.getDisplayName() + " §7ist beendet! §7(" + reason + ")"); @@ -106,6 +108,16 @@ public class EventManager { return true; } + public boolean endEventWithWinner(Player winner, String reason) { + if (currentEvent == null || currentEvent.getState() == ActiveEvent.State.ENDED) return false; + if (winner != null) { + currentEvent.setWinner(winner.getUniqueId()); + broadcast(plugin.prefix() + "§6🏁 " + winner.getName() + " §ehat das Ziel erreicht und gewinnt!"); + distributeWinnerRewards(currentEvent.getDefinition(), winner); + } + return endEvent(reason); + } + public boolean join(Player player) { if (currentEvent == null || currentEvent.getState() == ActiveEvent.State.ENDED) { player.sendMessage(plugin.prefix() + "§cKein Event aktiv."); return false; @@ -121,6 +133,7 @@ public class EventManager { broadcast(plugin.prefix() + "§a" + player.getName() + " §7nimmt teil! §8(" + currentEvent.getParticipantCount() + "/" + def.getMaxPlayers() + ")"); handlerRegistry.get(def.getType()).ifPresent(h -> h.onPlayerJoin(currentEvent, player)); + plugin.getScoreboardManager().onJoin(player, currentEvent); return true; } @@ -130,6 +143,7 @@ public class EventManager { currentEvent.removeParticipant(player); handlerRegistry.get(currentEvent.getDefinition().getType()) .ifPresent(h -> h.onPlayerLeave(currentEvent, player)); + plugin.getScoreboardManager().onLeave(player); player.sendMessage(plugin.prefix() + "§7Du hast das Event verlassen."); } @@ -161,6 +175,11 @@ public class EventManager { } } + private void distributeWinnerRewards(EventDefinition def, Player winner) { + if (def.getWinnerRewards().isEmpty() || winner == null) return; + runCommands(def.getWinnerRewards(), winner); + } + private void runCommands(List cmds, Player player) { for (String cmd : cmds) { String filled = player != null ? cmd.replace("%player%", player.getName()) : cmd; diff --git a/src/main/java/dev/viper/eventengine/manager/ScoreboardManager.java b/src/main/java/dev/viper/eventengine/manager/ScoreboardManager.java new file mode 100644 index 0000000..3f41baf --- /dev/null +++ b/src/main/java/dev/viper/eventengine/manager/ScoreboardManager.java @@ -0,0 +1,174 @@ +package dev.viper.eventengine.manager; + +import dev.viper.eventengine.EventEngine; +import dev.viper.eventengine.model.ActiveEvent; +import dev.viper.eventengine.model.EventDefinition; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.scoreboard.DisplaySlot; +import org.bukkit.scoreboard.Objective; +import org.bukkit.scoreboard.Scoreboard; +import org.bukkit.scoreboard.Criteria; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +public class ScoreboardManager { + + private static final String OBJECTIVE_NAME = "eventengine"; + + private final EventEngine plugin; + private BukkitRunnable ticker; + private ActiveEvent current; + + private final Map boards = new HashMap<>(); + private final Map previous = new HashMap<>(); + + public ScoreboardManager(EventEngine plugin) { + this.plugin = plugin; + } + + public void start(ActiveEvent event) { + stop(); + this.current = event; + ticker = new BukkitRunnable() { + @Override public void run() { + if (current == null || current.getState() != ActiveEvent.State.RUNNING) return; + for (UUID uuid : current.getParticipants()) { + Player p = Bukkit.getPlayer(uuid); + if (p == null) continue; + updateFor(p, current); + } + } + }; + ticker.runTaskTimer(plugin, 0L, 20L); + } + + public void stop() { + if (ticker != null) { + ticker.cancel(); + ticker = null; + } + if (current != null) { + for (UUID uuid : current.getParticipants()) { + Player p = Bukkit.getPlayer(uuid); + if (p != null) clearFor(p); + } + } + current = null; + } + + public void onJoin(Player player, ActiveEvent event) { + updateFor(player, event); + } + + public void onLeave(Player player) { + clearFor(player); + } + + private void updateFor(Player player, ActiveEvent event) { + org.bukkit.scoreboard.ScoreboardManager sm = Bukkit.getScoreboardManager(); + if (sm == null) return; + + Scoreboard sb = boards.get(player.getUniqueId()); + if (sb == null) { + if (!previous.containsKey(player.getUniqueId())) { + previous.put(player.getUniqueId(), player.getScoreboard()); + } + sb = sm.getNewScoreboard(); + boards.put(player.getUniqueId(), sb); + player.setScoreboard(sb); + } + + Objective obj = sb.getObjective(OBJECTIVE_NAME); + if (obj == null) { + obj = sb.registerNewObjective(OBJECTIVE_NAME, Criteria.DUMMY, titleFor(event.getDefinition())); + obj.setDisplaySlot(DisplaySlot.SIDEBAR); + } else { + obj.setDisplayName(titleFor(event.getDefinition())); + } + + for (String entry : new HashSet<>(sb.getEntries())) { + sb.resetScores(entry); + } + + List lines = buildLines(player, event); + int score = lines.size(); + for (String line : lines) { + obj.getScore(line).setScore(score--); + } + } + + private void clearFor(Player player) { + Scoreboard prev = previous.remove(player.getUniqueId()); + if (prev != null) { + player.setScoreboard(prev); + } else { + org.bukkit.scoreboard.ScoreboardManager sm = Bukkit.getScoreboardManager(); + if (sm != null) player.setScoreboard(sm.getMainScoreboard()); + } + boards.remove(player.getUniqueId()); + } + + private List buildLines(Player player, ActiveEvent event) { + EventDefinition def = event.getDefinition(); + List lines = new ArrayList<>(); + + lines.add("§7Event:"); + lines.add("§f" + trim(def.getDisplayName(), 28)); + lines.add("§7Kategorie: §f" + trim(def.getCategory().getDisplayName(), 16)); + lines.add("§7Zeit: §f" + EventManager.formatTime((int) event.getRemainingSeconds())); + lines.add("§7Teilnehmer: §f" + event.getParticipantCount() + "/" + def.getMaxPlayers()); + lines.add("§7Deine Punkte: §f" + event.getScore(player.getUniqueId())); + lines.add("§8----------------"); + lines.add("§7Top 3:"); + + List> lb = event.getLeaderboard(); + for (int i = 0; i < 3; i++) { + if (i >= lb.size()) { + lines.add("§8-" + (i + 1) + ". §f---"); + continue; + } + UUID uuid = lb.get(i).getKey(); + int pts = lb.get(i).getValue(); + Player p = Bukkit.getPlayer(uuid); + String name = p != null ? p.getName() : "Unbekannt"; + lines.add("§e" + (i + 1) + ". §f" + trim(name, 12) + " §7(" + pts + ")"); + } + + lines.add("§8"); + return ensureUnique(lines); + } + + private String titleFor(EventDefinition def) { + return trim("§6✦ §e" + def.getDisplayName(), 32); + } + + private List ensureUnique(List lines) { + List result = new ArrayList<>(lines.size()); + Set used = new HashSet<>(); + int colorIndex = 0; + for (String line : lines) { + String unique = line; + while (used.contains(unique)) { + unique = line + "§" + Integer.toHexString(colorIndex % 16); + colorIndex++; + } + used.add(unique); + result.add(unique); + } + return result; + } + + private String trim(String text, int maxLen) { + if (text == null) return ""; + if (text.length() <= maxLen) return text; + return text.substring(0, Math.max(0, maxLen - 3)) + "..."; + } +} diff --git a/src/main/java/dev/viper/eventengine/model/ActiveEvent.java b/src/main/java/dev/viper/eventengine/model/ActiveEvent.java index b9cacba..c121d87 100644 --- a/src/main/java/dev/viper/eventengine/model/ActiveEvent.java +++ b/src/main/java/dev/viper/eventengine/model/ActiveEvent.java @@ -17,6 +17,7 @@ public class ActiveEvent { private final Set participants; private final Map scores; private int taskId = -1; + private UUID winner; public ActiveEvent(EventDefinition definition) { this.definition = definition; @@ -24,6 +25,7 @@ public class ActiveEvent { this.startTime = System.currentTimeMillis(); this.participants = new LinkedHashSet<>(); this.scores = new HashMap<>(); + this.winner = null; } public void addParticipant(Player player) { @@ -72,4 +74,7 @@ public class ActiveEvent { public int getTaskId() { return taskId; } public void setTaskId(int taskId) { this.taskId = taskId; } public int getParticipantCount() { return participants.size(); } + public UUID getWinner() { return winner; } + public void setWinner(UUID winner) { this.winner = winner; } + public boolean hasWinner() { return winner != null; } } diff --git a/src/main/java/dev/viper/eventengine/model/EventDefinition.java b/src/main/java/dev/viper/eventengine/model/EventDefinition.java index 36264da..b0e44c4 100644 --- a/src/main/java/dev/viper/eventengine/model/EventDefinition.java +++ b/src/main/java/dev/viper/eventengine/model/EventDefinition.java @@ -34,12 +34,17 @@ public class EventDefinition { private boolean inRotation; // Gewichtung in der Rotation private int weight; + // Optionaler Start-/Zielbereich (Rennen) + private EventRegion startRegion; + private EventRegion goalRegion; // Custom-Einstellungen (Key-Value) private Map customSettings; // Ist es ein benutzerdefiniertes Event? private boolean isCustom; // Optionale Event-Region (null = gesamte Welt) private EventRegion region; + // Winner-Belohnungen (nur Sieger) + private List winnerRewards; // ─── Konstruktor für builtin Events ──────────────────────────────────── public EventDefinition(EventType type) { @@ -57,8 +62,11 @@ public class EventDefinition { this.announcement = ColorUtil.color("&6✦ &e" + displayName + " &6startet jetzt!"); this.inRotation = true; this.weight = 1; + this.startRegion = null; + this.goalRegion = null; this.customSettings = new HashMap<>(); this.isCustom = false; + this.winnerRewards = new ArrayList<>(); } // ─── Konstruktor für Custom Events ───────────────────────────────────── @@ -77,8 +85,11 @@ public class EventDefinition { this.announcement = ColorUtil.color("&6✦ &e" + id + " &6startet jetzt!"); this.inRotation = true; this.weight = 1; + this.startRegion = null; + this.goalRegion = null; this.customSettings = new HashMap<>(); this.isCustom = true; + this.winnerRewards = new ArrayList<>(); } // ─── Laden aus ConfigurationSection ──────────────────────────────────── @@ -96,6 +107,7 @@ public class EventDefinition { "&6✦ &e" + def.displayName + " &6startet jetzt!")); def.inRotation = sec.getBoolean("in-rotation", true); def.weight = sec.getInt("weight", 1); + def.winnerRewards = sec.getStringList("winner-rewards"); String catStr = sec.getString("category", "CUSTOM"); try { def.category = EventCategory.valueOf(catStr); } catch (Exception e) { def.category = EventCategory.CUSTOM; } @@ -120,6 +132,32 @@ public class EventDefinition { def.region = new EventRegion(w, minX, minY, minZ, maxX, maxY, maxZ); } catch (Exception ignored) {} } + + // Start-/Zielregion laden + if (sec.contains("start-region.world")) { + try { + String w = sec.getString("start-region.world"); + int minX = sec.getInt("start-region.min-x"); + int minY = sec.getInt("start-region.min-y"); + int minZ = sec.getInt("start-region.min-z"); + int maxX = sec.getInt("start-region.max-x"); + int maxY = sec.getInt("start-region.max-y"); + int maxZ = sec.getInt("start-region.max-z"); + def.startRegion = new EventRegion(w, minX, minY, minZ, maxX, maxY, maxZ); + } catch (Exception ignored) {} + } + if (sec.contains("goal-region.world")) { + try { + String w = sec.getString("goal-region.world"); + int minX = sec.getInt("goal-region.min-x"); + int minY = sec.getInt("goal-region.min-y"); + int minZ = sec.getInt("goal-region.min-z"); + int maxX = sec.getInt("goal-region.max-x"); + int maxY = sec.getInt("goal-region.max-y"); + int maxZ = sec.getInt("goal-region.max-z"); + def.goalRegion = new EventRegion(w, minX, minY, minZ, maxX, maxY, maxZ); + } catch (Exception ignored) {} + } return def; } @@ -136,6 +174,7 @@ public class EventDefinition { sec.set("announcement", announcement); sec.set("in-rotation", inRotation); sec.set("weight", weight); + sec.set("winner-rewards", winnerRewards); sec.set("category", category.name()); if (region != null) { sec.set("region.world", region.getWorldName()); @@ -146,6 +185,24 @@ public class EventDefinition { sec.set("region.max-y", region.getMaxY()); sec.set("region.max-z", region.getMaxZ()); } + if (startRegion != null) { + sec.set("start-region.world", startRegion.getWorldName()); + sec.set("start-region.min-x", startRegion.getMinX()); + sec.set("start-region.min-y", startRegion.getMinY()); + sec.set("start-region.min-z", startRegion.getMinZ()); + sec.set("start-region.max-x", startRegion.getMaxX()); + sec.set("start-region.max-y", startRegion.getMaxY()); + sec.set("start-region.max-z", startRegion.getMaxZ()); + } + if (goalRegion != null) { + sec.set("goal-region.world", goalRegion.getWorldName()); + sec.set("goal-region.min-x", goalRegion.getMinX()); + sec.set("goal-region.min-y", goalRegion.getMinY()); + sec.set("goal-region.min-z", goalRegion.getMinZ()); + sec.set("goal-region.max-x", goalRegion.getMaxX()); + sec.set("goal-region.max-y", goalRegion.getMaxY()); + sec.set("goal-region.max-z", goalRegion.getMaxZ()); + } if (!customSettings.isEmpty()) { for (Map.Entry entry : customSettings.entrySet()) { sec.set("settings." + entry.getKey(), entry.getValue()); @@ -174,6 +231,8 @@ public class EventDefinition { public void setEndCommands(List l) { this.endCommands = l; } public List getRewards() { return rewards; } public void setRewards(List l) { this.rewards = l; } + public List getWinnerRewards() { return winnerRewards; } + public void setWinnerRewards(List l) { this.winnerRewards = l; } public String getAnnouncement() { return announcement; } public void setAnnouncement(String a) { this.announcement = ColorUtil.color(a); } public boolean isInRotation() { return inRotation; } @@ -185,6 +244,12 @@ public class EventDefinition { public EventRegion getRegion() { return region; } public void setRegion(EventRegion r) { this.region = r; } public boolean hasRegion() { return region != null; } + public EventRegion getStartRegion() { return startRegion; } + public void setStartRegion(EventRegion r) { this.startRegion = r; } + public boolean hasStartRegion() { return startRegion != null; } + public EventRegion getGoalRegion() { return goalRegion; } + public void setGoalRegion(EventRegion r) { this.goalRegion = r; } + public boolean hasGoalRegion() { return goalRegion != null; } public Object getSetting(String key) { return customSettings.get(key); } public void setSetting(String key, Object val) { customSettings.put(key, val); } } diff --git a/src/main/java/dev/viper/eventengine/model/EventRegion.java b/src/main/java/dev/viper/eventengine/model/EventRegion.java index 37b9092..bfee62f 100644 --- a/src/main/java/dev/viper/eventengine/model/EventRegion.java +++ b/src/main/java/dev/viper/eventengine/model/EventRegion.java @@ -79,6 +79,16 @@ public class EventRegion { return new Location(world, x + 0.5, y, z + 0.5); } + /** Zufälliger Punkt direkt innerhalb der Region (ohne Geländeprüfung) */ + public Location randomPoint(java.util.Random rng) { + World world = Bukkit.getWorld(worldName); + if (world == null) return null; + int x = minX + rng.nextInt(Math.max(1, maxX - minX + 1)); + int y = minY + rng.nextInt(Math.max(1, maxY - minY + 1)); + int z = minZ + rng.nextInt(Math.max(1, maxZ - minZ + 1)); + return new Location(world, x + 0.5, y + 0.1, z + 0.5); + } + public World getWorld() { return Bukkit.getWorld(worldName); } public String getWorldName() { return worldName; } public int getMinX() { return minX; } diff --git a/src/main/java/dev/viper/eventengine/model/EventType.java b/src/main/java/dev/viper/eventengine/model/EventType.java index 94c80ed..1e7e86f 100644 --- a/src/main/java/dev/viper/eventengine/model/EventType.java +++ b/src/main/java/dev/viper/eventengine/model/EventType.java @@ -104,7 +104,7 @@ public enum EventType { 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), + FUN_BOUNCY_BLOCKS("Jump-Funn", EventCategory.FUN), // ═══════════════════════════════════════════════════════════ // QUIZ & KNOWLEDGE EVENTS (5) diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 0eb5ac9..c553773 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -119,3 +119,17 @@ protection: no-explosion-block-damage: true # Teilnehmer können nur innerhalb der Region Blöcke abbauen/setzen restrict-block-interaction: true + +# ── Item-Regen ────────────────────────────────────────────────── +item-rain: + # Items, die vom Himmel fallen (Material-Namen) + items: + - DIAMOND + - GOLD_INGOT + - IRON_INGOT + - EMERALD + - COAL + - BREAD + - ARROW + - GOLDEN_APPLE + - EXPERIENCE_BOTTLE