package de.nexuslobby.modules.gadgets; import de.nexuslobby.NexusLobby; import de.nexuslobby.api.Module; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Material; import org.bukkit.Particle; import org.bukkit.Sound; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.block.Action; import org.bukkit.event.entity.PlayerLeashEntityEvent; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.player.PlayerFishEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.player.PlayerUnleashEntityEvent; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemFlag; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; 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 GadgetModule implements Module, Listener { private final Map activeBalloons = new HashMap<>(); private final Map activeEffects = new HashMap<>(); private final Set activeShields = new HashSet<>(); // ── Cooldowns ───────────────────────────────────────────────────────────── /** Letzter Einsatz-Zeitstempel pro Spieler */ private final Map meteorCooldowns = new HashMap<>(); private final Map freezeCooldowns = new HashMap<>(); private final Map grapplingCooldowns = new HashMap<>(); /** Cooldown-Dauer in Millisekunden */ private static final long METEOR_CD_MS = 15_000L; // 15 Sekunden private static final long FREEZE_CD_MS = 10_000L; // 10 Sekunden private static final long GRAPPLING_CD_MS = 3_000L; // 3 Sekunden // ────────────────────────────────────────────────────────────────────────── private final String MAIN_TITLE = "§b§lGadgets §8- §7Menü"; private final String BALLOON_TITLE = "§b§lGadgets §8- §eBallons"; private final String PARTICLE_TITLE = "§b§lGadgets §8- §dPartikel"; private final String FUN_TITLE = "§b§lGadgets §8- §6Lustiges"; private final String HAT_TITLE = "§b§lGadgets §8- §aHüte & Köpfe"; private final String PET_TITLE = "§b§lGadgets §8- §dBegleiter"; @Override public String getName() { return "Gadgets"; } @Override public void onEnable() { Bukkit.getPluginManager().registerEvents(this, NexusLobby.getInstance()); PetManager.register(); Bukkit.getScheduler().runTaskTimer(NexusLobby.getInstance(), () -> { PetManager.updatePets(); activeBalloons.values().forEach(Balloon::update); for (Player p : Bukkit.getOnlinePlayers()) { UUID uuid = p.getUniqueId(); handleSpecialHatEffects(p); if (activeEffects.containsKey(uuid)) activeEffects.get(uuid).update(p); if (activeShields.contains(uuid)) ShieldTask.handleShield(p); } }, 1L, 1L); } // ───────────────────────────────────────────────────────────────────────── // Cooldown-Hilfsmethoden // ───────────────────────────────────────────────────────────────────────── /** * Prüft ob der Spieler noch im Cooldown ist. * @return true → Gadget darf benutzt werden (Cooldown abgelaufen / nicht gesetzt) * false → Cooldown noch aktiv, Nachricht wird gesendet */ private boolean checkAndSetCooldown(Player player, Map cdMap, long durationMs, String gadgetName) { long now = System.currentTimeMillis(); long last = cdMap.getOrDefault(player.getUniqueId(), 0L); long remaining = durationMs - (now - last); if (remaining > 0) { long secLeft = (long) Math.ceil(remaining / 1000.0); player.sendMessage("§8[§6Nexus§8] §c" + gadgetName + " §7hat noch §e" + secLeft + "s §7Cooldown."); player.playSound(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 0.5f, 0.8f); return false; } cdMap.put(player.getUniqueId(), now); return true; } // ───────────────────────────────────────────────────────────────────────── // Event Handler // ───────────────────────────────────────────────────────────────────────── /** * Verhindert Rechtsklick auf den unsichtbaren Ballon-Pig * (verhindert Leine lösen, Inventar-Öffnen, etc.). */ @EventHandler public void onInteractAtEntity(org.bukkit.event.player.PlayerInteractAtEntityEvent event) { if (event.getRightClicked().getScoreboardTags().contains("nexus_balloon")) { event.setCancelled(true); } } /** * Verhindert das Ablösen der Leine vom Ballon-Pig durch Rechtsklick. * Ohne diesen Handler wird die Leine gedroppt und der Ballon verschwindet. */ @EventHandler public void onUnleash(PlayerUnleashEntityEvent event) { if (event.getEntity().getScoreboardTags().contains("nexus_balloon")) { event.setCancelled(true); } } @EventHandler public void onInteract(PlayerInteractEvent event) { ItemStack item = event.getItem(); if (item == null || !item.hasItemMeta() || !item.getItemMeta().hasDisplayName()) return; String name = item.getItemMeta().getDisplayName(); if (event.getAction() != Action.RIGHT_CLICK_AIR && event.getAction() != Action.RIGHT_CLICK_BLOCK) return; Player player = event.getPlayer(); // Wenn der Schütze selbst geschützt ist (Parkour oder Admin-Schutz) → kampfbezogene Gadgets sperren if (GadgetShield.isProtected(player)) { if (name.equals("§b§lFreeze-Ray") || name.equals("§c§lMeteorit")) { player.sendMessage("§8[§6Parkour§8] §cGadgets können während des Parkours nicht benutzt werden."); event.setCancelled(true); return; } } if (name.equals("§b§lFreeze-Ray")) { event.setCancelled(true); if (checkAndSetCooldown(player, freezeCooldowns, FREEZE_CD_MS, "§b§lFreeze-Ray")) { FreezeRay.shoot(player); } } else if (name.equals("§6§lPaintball-Gun")) { PaintballGun.shoot(player); event.setCancelled(true); } else if (name.equals("§c§lMeteorit")) { event.setCancelled(true); if (checkAndSetCooldown(player, meteorCooldowns, METEOR_CD_MS, "§c§lMeteorit")) { MeteorStrike.launch(player); } } } @EventHandler public void onFish(PlayerFishEvent event) { Player player = event.getPlayer(); ItemStack item = player.getInventory().getItemInMainHand(); if (item.getType() != Material.FISHING_ROD || !item.hasItemMeta() || !item.getItemMeta().hasDisplayName()) return; if (!item.getItemMeta().getDisplayName().equals("§b§lEnterhaken")) return; if (event.getState() == PlayerFishEvent.State.IN_GROUND || event.getState() == PlayerFishEvent.State.REEL_IN || event.getState() == PlayerFishEvent.State.CAUGHT_ENTITY) { if (event.getHook() == null) return; // Parkour-Check: Schütze im Parkour oder Admin-Schutz → nicht benutzen if (GadgetShield.isProtected(player)) { player.sendMessage("§8[§6Nexus§8] §cDer Enterhaken ist gerade nicht verfügbar."); return; } if (!checkAndSetCooldown(player, grapplingCooldowns, GRAPPLING_CD_MS, "§b§lEnterhaken")) return; GrapplingHook.pullPlayer(player, event.getHook().getLocation()); } } @EventHandler public void onPlayerMove(PlayerMoveEvent event) { if (FreezeRay.isFrozen(event.getPlayer().getUniqueId())) { if (event.getFrom().getX() != event.getTo().getX() || event.getFrom().getZ() != event.getTo().getZ()) { event.setTo(event.getFrom().setDirection(event.getTo().getDirection())); } } } // ───────────────────────────────────────────────────────────────────────── // GUI // ───────────────────────────────────────────────────────────────────────── private void handleSpecialHatEffects(Player p) { ItemStack hat = p.getInventory().getHelmet(); if (hat == null || hat.getType() == Material.AIR) return; switch (hat.getType()) { case CAMPFIRE -> p.getWorld().spawnParticle(Particle.CAMPFIRE_COSY_SMOKE, p.getLocation().add(0, 2.2, 0), 1, 0.05, 0.05, 0.05, 0.02); case SPAWNER -> p.getWorld().spawnParticle(Particle.FLAME, p.getLocation().add(0, 2.1, 0), 1, 0.12, 0.12, 0.12, 0.02); case SEA_LANTERN, BEACON -> p.getWorld().spawnParticle(Particle.END_ROD, p.getLocation().add(0, 2.1, 0), 1, 0.1, 0.1, 0.1, 0.03); case ENCHANTING_TABLE -> p.getWorld().spawnParticle(Particle.ENCHANT, p.getLocation().add(0, 2.3, 0), 1, 0.2, 0.2, 0.2, 0.5); default -> {} } } public void openGUI(Player player) { Inventory gui = Bukkit.createInventory(null, 27, MAIN_TITLE); fillEdges(gui); gui.setItem(10, createItem(Material.LEAD, "§e§lBallons", "§7Wähle einen fliegenden Begleiter")); gui.setItem(11, createItem(Material.GOLDEN_HELMET, "§a§lHüte", "§7Setze dir etwas auf den Kopf")); gui.setItem(13, createItem(Material.BONE, "§d§lBegleiter", "§7Echte Tiere, die dir folgen")); gui.setItem(15, createItem(Material.FIREWORK_ROCKET,"§6§lLustiges", "§7Witzige Effekte")); gui.setItem(16, createItem(Material.NETHER_STAR, "§d§lPartikel", "§7Magische Auren & Effekte")); gui.setItem(22, createItem(Material.BARRIER, "§c§lStopp", "§7Alle Gadgets entfernen")); player.openInventory(gui); } private void openHatGUI(Player player) { Inventory gui = Bukkit.createInventory(null, 45, HAT_TITLE); fillEdges(gui); gui.setItem(10, createItem(Material.JACK_O_LANTERN, "§6Kürbis-Hut", "§7Es ist immer Halloween!")); gui.setItem(11, createItem(Material.SEA_LANTERN, "§bMeeres-Leuchten", "§7§oEffekt: Glitzern")); gui.setItem(12, createItem(Material.GLOWSTONE, "§eGlowstone-Kopf", "§7Werde zur Lampe")); gui.setItem(13, createItem(Material.TNT, "§cExplosiv-Hut", "§7Vorsicht, heiß!")); gui.setItem(14, createItem(Material.GLASS, "§fAstronaut", "§7Bereit für den Mond?")); gui.setItem(15, createItem(Material.DRAGON_HEAD, "§5Enderdrache", "§7Der König der Lüfte")); gui.setItem(16, createItem(Material.CAKE, "§dKuchen-Kopf", "§7Jeder mag Kuchen!")); gui.setItem(19, createItem(Material.SLIME_BLOCK, "§aGlibber-Block", "§7Ziemlich klebrig...")); gui.setItem(20, createItem(Material.MELON, "§aMelonen-Helm", "§7Frisch und saftig")); gui.setItem(21, createItem(Material.HAY_BLOCK, "§eStrohhut", "§7Sommer auf dem Land")); gui.setItem(22, createItem(Material.SPAWNER, "§8Monster-Käfig", "§7§oEffekt: Flammen")); gui.setItem(23, createItem(Material.CRAFTING_TABLE, "§6Werkbank", "§7Immer am Basteln")); gui.setItem(24, createItem(Material.BOOKSHELF, "§fBücherregal", "§7Ein wahrer Schlaukopf")); gui.setItem(25, createItem(Material.HONEY_BLOCK, "§6Honig-Hut", "§7Süß und klebrig")); gui.setItem(28, createItem(Material.GOLD_BLOCK, "§6Gold-Bonze", "§7Zeig was du hast")); gui.setItem(29, createItem(Material.DIAMOND_ORE, "§bDiamant-Erz", "§7Bau mich bloß nicht ab!")); gui.setItem(30, createItem(Material.BEACON, "§fLeuchtfeuer", "§7§oEffekt: Glitzern")); gui.setItem(31, createItem(Material.CONDUIT, "§3Auge des Meeres", "§7Die Macht von Atlantis")); gui.setItem(32, createItem(Material.ENCHANTING_TABLE,"§dMagier", "§7§oEffekt: Runen")); gui.setItem(33, createItem(Material.CAMPFIRE, "§cHeißer Kopf", "§7§oEffekt: Rauch")); gui.setItem(34, createItem(Material.SKELETON_SKULL, "§7Skelett", "§7Ein wenig gruselig")); gui.setItem(40, createItem(Material.ARROW, "§7Zurück", "§8Zum Hauptmenü")); player.openInventory(gui); } private void openPetGUI(Player player) { Inventory gui = Bukkit.createInventory(null, 27, PET_TITLE); fillEdges(gui); gui.setItem(11, createItem(Material.BONE, "§fWolf", "§7Ein treuer Begleiter")); gui.setItem(13, createItem(Material.CAT_SPAWN_EGG, "§6Katze", "§7Ein verschmuster Freund")); gui.setItem(15, createItem(Material.PANDA_SPAWN_EGG,"§aPanda", "§7Ein gemütlicher Zeitgenosse")); gui.setItem(22, createItem(Material.ARROW, "§7Zurück", "§8Zum Hauptmenü")); player.openInventory(gui); } private void openBalloonGUI(Player player) { Inventory gui = Bukkit.createInventory(null, 36, BALLOON_TITLE); fillEdges(gui); Material[] wools = {Material.WHITE_WOOL, Material.ORANGE_WOOL, Material.MAGENTA_WOOL, Material.LIGHT_BLUE_WOOL, Material.YELLOW_WOOL, Material.LIME_WOOL, Material.PINK_WOOL, Material.GRAY_WOOL, Material.CYAN_WOOL, Material.PURPLE_WOOL, Material.BLUE_WOOL, Material.BROWN_WOOL, Material.GREEN_WOOL, Material.RED_WOOL}; int slot = 10; for (Material m : wools) { if (slot == 17) slot = 19; gui.setItem(slot++, createItem(m, "§fBallon: " + m.name().replace("_WOOL",""), "§7Klicke zum Ausrüsten")); } gui.setItem(31, createItem(Material.ARROW, "§7Zurück", "§8Zum Hauptmenü")); player.openInventory(gui); } private void openParticleGUI(Player player) { Inventory gui = Bukkit.createInventory(null, 27, PARTICLE_TITLE); fillEdges(gui); gui.setItem(11, createItem(Material.POPPY, "§cHerzchen-Aura", "§7Verbreite Liebe in der Lobby")); gui.setItem(13, createItem(Material.BLAZE_POWDER, "§6Flammen-Ring", "§7Lass es brennen!")); gui.setItem(15, createItem(Material.WATER_BUCKET, "§bRegenwolke", "§7Deine persönliche Abkühlung")); gui.setItem(22, createItem(Material.ARROW, "§7Zurück", "§8Zum Hauptmenü")); player.openInventory(gui); } private void openFunGUI(Player player) { Inventory gui = Bukkit.createInventory(null, 27, FUN_TITLE); fillEdges(gui); gui.setItem(10, createItem(Material.FISHING_ROD, "§b§lEnterhaken", "§7Zieh dich durch die Luft! §8(3s CD)")); gui.setItem(11, createItem(Material.PACKED_ICE, "§b§lFreeze-Ray", "§7Friere andere ein! §8(10s CD)")); gui.setItem(12, createItem(Material.GOLDEN_HOE, "§6§lPaintball-Gun","§7Male die Lobby bunt aus!")); gui.setItem(14, createItem(Material.FIRE_CHARGE, "§c§lMeteorit", "§7Lass es krachen! §8(15s CD)")); gui.setItem(15, createItem(Material.SHIELD, "§5§lSchutzzone", "§7Halte andere auf Distanz")); gui.setItem(16, createItem(Material.EGG, "§f§lChicken-Rain","§7Gack-Gack! Hühner überall!")); gui.setItem(22, createItem(Material.ARROW, "§7Zurück", "§8Zum Hauptmenü")); player.openInventory(gui); } @EventHandler public void onInventoryClick(InventoryClickEvent event) { String title = event.getView().getTitle(); if (!title.startsWith("§b§lGadgets")) return; event.setCancelled(true); Player player = (Player) event.getWhoClicked(); ItemStack item = event.getCurrentItem(); if (item == null || item.getType() == Material.AIR) return; if (item.getType() == Material.ARROW) { openGUI(player); return; } if (title.equals(MAIN_TITLE)) { if (item.getType() == Material.LEAD) openBalloonGUI(player); else if (item.getType() == Material.GOLDEN_HELMET) openHatGUI(player); else if (item.getType() == Material.BONE) openPetGUI(player); else if (item.getType() == Material.NETHER_STAR) openParticleGUI(player); else if (item.getType() == Material.FIREWORK_ROCKET) openFunGUI(player); else if (item.getType() == Material.BARRIER) { removeGadgets(player); player.closeInventory(); } } else if (title.equals(HAT_TITLE)) { if (item.getType() != Material.GRAY_STAINED_GLASS_PANE) { String hatName = item.hasItemMeta() && item.getItemMeta().hasDisplayName() ? item.getItemMeta().getDisplayName() : item.getType().name(); HatManager.setHat(player, item.getType(), hatName); player.playSound(player.getLocation(), Sound.ITEM_ARMOR_EQUIP_GENERIC, 1, 1); player.closeInventory(); } } else if (title.equals(PET_TITLE)) { if (item.getType() == Material.BONE) PetManager.spawnEntityPet(player, "WOLF"); else if (item.getType() == Material.CAT_SPAWN_EGG) PetManager.spawnEntityPet(player, "CAT"); else if (item.getType() == Material.PANDA_SPAWN_EGG) PetManager.spawnEntityPet(player, "PANDA"); player.sendMessage("§8[§6Nexus§8] §dDein Pet wurde gerufen!"); player.closeInventory(); } else if (title.equals(BALLOON_TITLE)) { if (item.getType().toString().endsWith("_WOOL")) { if (activeBalloons.containsKey(player.getUniqueId())) activeBalloons.get(player.getUniqueId()).remove(); activeBalloons.put(player.getUniqueId(), new Balloon(player, item.getType())); player.sendMessage("§8[§6Nexus§8] §aBallon aktiviert!"); player.closeInventory(); } } else if (title.equals(PARTICLE_TITLE)) { if (item.getType() == Material.POPPY) activeEffects.put(player.getUniqueId(), new ParticleEffect("hearts")); else if (item.getType() == Material.BLAZE_POWDER) activeEffects.put(player.getUniqueId(), new ParticleEffect("flames")); else if (item.getType() == Material.WATER_BUCKET) activeEffects.put(player.getUniqueId(), new ParticleEffect("cloud")); player.sendMessage("§8[§6Nexus§8] §aPartikel aktiviert!"); player.closeInventory(); } else if (title.equals(FUN_TITLE)) { if (item.getType() == Material.EGG) { ChickenRain.start(player); player.sendMessage("§8[§6Nexus§8] §fHühnerregen gestartet!"); player.closeInventory(); } else if (item.getType() == Material.FISHING_ROD) { player.getInventory().addItem(createItem(Material.FISHING_ROD, "§b§lEnterhaken", "§7Rechtsklick zum Katapultieren §8(3s CD)")); player.closeInventory(); } else if (item.getType() == Material.PACKED_ICE) { player.getInventory().addItem(createItem(Material.PACKED_ICE, "§b§lFreeze-Ray", "§7Rechtsklick zum Einfrieren §8(10s CD)")); player.closeInventory(); } else if (item.getType() == Material.GOLDEN_HOE) { player.getInventory().addItem(createItem(Material.GOLDEN_HOE, "§6§lPaintball-Gun", "§7Rechtsklick zum Schießen")); player.closeInventory(); } else if (item.getType() == Material.FIRE_CHARGE) { player.getInventory().addItem(createItem(Material.FIRE_CHARGE, "§c§lMeteorit", "§7Rechtsklick zum Markieren §8(15s CD)")); player.closeInventory(); } else if (item.getType() == Material.SHIELD) { if (activeShields.contains(player.getUniqueId())) { activeShields.remove(player.getUniqueId()); player.sendMessage("§8[§6Nexus§8] §cSchutzzone deaktiviert."); } else { activeShields.add(player.getUniqueId()); player.sendMessage("§8[§6Nexus§8] §5Schutzzone aktiviert!"); } player.closeInventory(); } } } private void removeGadgets(Player player) { if (activeBalloons.containsKey(player.getUniqueId())) { activeBalloons.get(player.getUniqueId()).remove(); activeBalloons.remove(player.getUniqueId()); } activeEffects.remove(player.getUniqueId()); activeShields.remove(player.getUniqueId()); PetManager.removePet(player); FreezeRay.unfreeze(player.getUniqueId()); HatManager.removeHat(player); player.getInventory().remove(Material.FISHING_ROD); player.getInventory().remove(Material.PACKED_ICE); player.getInventory().remove(Material.GOLDEN_HOE); player.getInventory().remove(Material.FIRE_CHARGE); player.sendMessage("§8[§6Nexus§8] §cAlle Gadgets abgelegt."); } private void fillEdges(Inventory inv) { ItemStack glass = createItem(Material.GRAY_STAINED_GLASS_PANE, " ", null); for (int i = 0; i < inv.getSize(); i++) { if (i < 9 || i >= inv.getSize() - 9 || i % 9 == 0 || (i + 1) % 9 == 0) inv.setItem(i, glass); } } private ItemStack createItem(Material mat, String name, String lore) { ItemStack item = new ItemStack(mat); ItemMeta meta = item.getItemMeta(); if (meta != null) { meta.setDisplayName(name); List l = new ArrayList<>(); if (lore != null && !lore.isEmpty()) { l.add(ChatColor.translateAlternateColorCodes('&', lore)); meta.setLore(l); } meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES); meta.addItemFlags(ItemFlag.HIDE_UNBREAKABLE); meta.addItemFlags(ItemFlag.HIDE_ENCHANTS); item.setItemMeta(meta); } return item; } @Override public void onDisable() { org.bukkit.event.HandlerList.unregisterAll(this); PetManager.unregister(); PetManager.clearAll(); activeBalloons.values().forEach(Balloon::remove); activeBalloons.clear(); activeEffects.clear(); activeShields.clear(); GadgetShield.clear(); } }