From 6a892c45db9a3903d9d3f4b3c10dbcf5a55f4a34 Mon Sep 17 00:00:00 2001 From: M_Viper Date: Sun, 1 Feb 2026 22:19:40 +0100 Subject: [PATCH] Update from Git Manager GUI --- src/main/java/de/nexuslobby/NexusLobby.java | 14 +- .../commands/LobbyTabCompleter.java | 7 +- .../commands/NexusLobbyCommand.java | 8 + .../nexuslobby/modules/ball/SoccerModule.java | 312 ++++++++++++++++++ .../modules/gadgets/GadgetModule.java | 56 ++-- .../modules/settings/LobbySettingsModule.java | 33 +- src/main/resources/config.yml | 16 + src/main/resources/conversations.yml | 164 +++++++++ src/main/resources/plugin.yml | 2 +- 9 files changed, 570 insertions(+), 42 deletions(-) create mode 100644 src/main/java/de/nexuslobby/modules/ball/SoccerModule.java create mode 100644 src/main/resources/conversations.yml diff --git a/src/main/java/de/nexuslobby/NexusLobby.java b/src/main/java/de/nexuslobby/NexusLobby.java index 1e2615b..2bab6b0 100644 --- a/src/main/java/de/nexuslobby/NexusLobby.java +++ b/src/main/java/de/nexuslobby/NexusLobby.java @@ -22,7 +22,8 @@ import de.nexuslobby.modules.intro.IntroModule; import de.nexuslobby.modules.border.BorderModule; import de.nexuslobby.modules.parkour.ParkourManager; import de.nexuslobby.modules.parkour.ParkourListener; -import de.nexuslobby.modules.player.PlayerInspectModule; // NEU +import de.nexuslobby.modules.player.PlayerInspectModule; +import de.nexuslobby.modules.ball.SoccerModule; // NEU import de.nexuslobby.utils.*; import me.clip.placeholderapi.expansion.PlaceholderExpansion; import net.md_5.bungee.api.chat.ClickEvent; @@ -67,6 +68,7 @@ public class NexusLobby extends JavaPlugin implements Listener { private IntroModule introModule; private BorderModule borderModule; private ParkourManager parkourManager; + private SoccerModule soccerModule; // NEU private ConversationManager conversationManager; @@ -94,6 +96,10 @@ public class NexusLobby extends JavaPlugin implements Listener { return parkourManager; } + public SoccerModule getSoccerModule() { // NEU + return soccerModule; + } + @Override public void onEnable() { instance = this; @@ -233,7 +239,11 @@ public class NexusLobby extends JavaPlugin implements Listener { moduleManager.registerModule(tablistModule); // Player Inspect Modul registrieren - moduleManager.registerModule(new PlayerInspectModule()); // NEU + moduleManager.registerModule(new PlayerInspectModule()); + + // Soccer Modul registrieren + this.soccerModule = new SoccerModule(); // NEU + moduleManager.registerModule(this.soccerModule); // NEU this.portalManager = new PortalManager(this); moduleManager.registerModule(portalManager); diff --git a/src/main/java/de/nexuslobby/commands/LobbyTabCompleter.java b/src/main/java/de/nexuslobby/commands/LobbyTabCompleter.java index 81143ac..bb274ee 100644 --- a/src/main/java/de/nexuslobby/commands/LobbyTabCompleter.java +++ b/src/main/java/de/nexuslobby/commands/LobbyTabCompleter.java @@ -32,7 +32,7 @@ public class LobbyTabCompleter implements TabCompleter { if (cmdName.equals("nexuslobby") || cmdName.equals("nexus")) { if (args.length == 1) { if (sender.hasPermission("nexuslobby.admin")) { - suggestions.addAll(Arrays.asList("reload", "setspawn", "silentjoin", "parkour")); + suggestions.addAll(Arrays.asList("reload", "setspawn", "silentjoin", "parkour", "ball")); // NEU: ball } suggestions.add("sb"); } else if (args.length == 2) { @@ -45,6 +45,10 @@ public class LobbyTabCompleter implements TabCompleter { suggestions.addAll(Arrays.asList("on", "off")); } else if (args[0].equalsIgnoreCase("parkour")) { suggestions.addAll(Arrays.asList("setstart", "setfinish", "setcheckpoint", "reset", "clear", "removeall")); + } else if (args[0].equalsIgnoreCase("ball")) { // NEU: Ball Subcommands + if (sender.hasPermission("nexuslobby.admin")) { + suggestions.addAll(Arrays.asList("setspawn", "reload")); + } } } else if (args.length == 3) { if (args[0].equalsIgnoreCase("parkour") && args[1].equalsIgnoreCase("setcheckpoint")) { @@ -118,7 +122,6 @@ public class LobbyTabCompleter implements TabCompleter { } else if (args[0].equalsIgnoreCase("name")) { suggestions.addAll(Arrays.asList("", "none")); } else if (args[0].equalsIgnoreCase("conv")) { - // ERWEITERT: select3 und select4 hinzugefügt suggestions.addAll(Arrays.asList("select1", "select2", "select3", "select4", "link", "unlink", "start")); } else if (args[0].equalsIgnoreCase("remove")) { suggestions.addAll(Arrays.asList("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "all")); diff --git a/src/main/java/de/nexuslobby/commands/NexusLobbyCommand.java b/src/main/java/de/nexuslobby/commands/NexusLobbyCommand.java index 370a72c..91e9c5e 100644 --- a/src/main/java/de/nexuslobby/commands/NexusLobbyCommand.java +++ b/src/main/java/de/nexuslobby/commands/NexusLobbyCommand.java @@ -110,6 +110,13 @@ public class NexusLobbyCommand implements CommandExecutor { handleScoreboard(player, args); break; + case "ball": // NEU: Weiterleitung an das SoccerModule + if (NexusLobby.getInstance().getSoccerModule() != null) { + return NexusLobby.getInstance().getSoccerModule().onCommand(sender, command, label, args); + } + player.sendMessage("§cDas Fußball-Modul ist nicht geladen."); + break; + case "parkour": if (args.length < 2) { player.sendMessage("§8[§6Nexus§8] §7Nutze: §e/nexus parkour "); @@ -225,6 +232,7 @@ public class NexusLobbyCommand implements CommandExecutor { player.sendMessage("§f/spawn §7- Zum Spawn"); player.sendMessage("§f/setstart §8| §f/setcheckpoint §8| §f/setfinish"); player.sendMessage("§f/nexus parkour removeall §7- Strecke löschen"); + player.sendMessage("§f/nexus ball setspawn §7- Fußball Spawn setzen"); // NEU player.sendMessage("§f/nexus setspawn §7- Spawn setzen"); player.sendMessage("§f/nexus sb §7- Scoreboard"); player.sendMessage("§f/nexus reload §7- Config laden"); diff --git a/src/main/java/de/nexuslobby/modules/ball/SoccerModule.java b/src/main/java/de/nexuslobby/modules/ball/SoccerModule.java new file mode 100644 index 0000000..80db00e --- /dev/null +++ b/src/main/java/de/nexuslobby/modules/ball/SoccerModule.java @@ -0,0 +1,312 @@ +package de.nexuslobby.modules.ball; + +import de.nexuslobby.NexusLobby; +import de.nexuslobby.api.Module; +import org.bukkit.*; +import org.bukkit.block.Block; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.player.PlayerInteractAtEntityEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.SkullMeta; +import org.bukkit.profile.PlayerProfile; +import org.bukkit.profile.PlayerTextures; +import org.bukkit.util.Vector; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.UUID; +import java.util.Objects; + +public class SoccerModule implements Module, Listener, CommandExecutor { + + private ArmorStand ball; + private Location spawnLocation; + private long lastMoveTime; + private final String TEXTURE_URL = "http://textures.minecraft.net/texture/451f8cfcfb85d77945dc6a3618414093e70436b46d2577b28c727f1329b7265e"; + private final String BALL_TAG = "nexusball_entity"; // Eindeutiges Tag zur Identifizierung + private final String BALL_NAME = "§x§N§e§x§u§s§B§a§l§l"; // Zusätzliche Erkennung + + @Override + public String getName() { return "Soccer"; } + + @Override + public void onEnable() { + Bukkit.getPluginManager().registerEvents(this, NexusLobby.getInstance()); + if (NexusLobby.getInstance().getCommand("nexuslobby") != null) { + Objects.requireNonNull(NexusLobby.getInstance().getCommand("nexuslobby")).setExecutor(this); + } + + loadConfigLocation(); + + // AGGRESSIVES MEHRFACHES CLEANUP-SYSTEM + // 1. Sofort beim Enable + removeAllOldBallsGlobal(); + + // 2. Nach 0.5 Sekunden + Bukkit.getScheduler().runTaskLater(NexusLobby.getInstance(), this::removeAllOldBallsGlobal, 10L); + + // 3. Nach 1 Sekunde + Bukkit.getScheduler().runTaskLater(NexusLobby.getInstance(), this::removeAllOldBallsGlobal, 20L); + + // 4. Nach 2 Sekunden - cleanup und dann spawnen + Bukkit.getScheduler().runTaskLater(NexusLobby.getInstance(), () -> { + removeAllOldBallsGlobal(); + spawnBall(); + }, 40L); + + // 5. Nach 3 Sekunden - finales Cleanup für hartnäckige Duplikate + Bukkit.getScheduler().runTaskLater(NexusLobby.getInstance(), this::removeAllOldBallsGlobal, 60L); + + // 6. Nach 5 Sekunden - letzter Check + Bukkit.getScheduler().runTaskLater(NexusLobby.getInstance(), this::removeAllOldBallsGlobal, 100L); + + // Haupt-Physik & Anti-Klon Tick + Bukkit.getScheduler().runTaskTimer(NexusLobby.getInstance(), () -> { + + // ANTI-KLON-SYSTEM: Prüfe die Umgebung des Spawns auf illegale Kopien + if (spawnLocation != null && spawnLocation.getWorld() != null) { + for (Entity entity : spawnLocation.getWorld().getNearbyEntities(spawnLocation, 50, 50, 50)) { + if (entity instanceof ArmorStand stand) { + // Wenn der ArmorStand unser Tag hat, aber nicht unsere aktive Instanz ist -> Löschen + if (stand.getScoreboardTags().contains(BALL_TAG)) { + if (ball == null || !stand.getUniqueId().equals(ball.getUniqueId())) { + stand.remove(); + } + } + } + } + } + + if (ball == null || !ball.isValid()) return; + + Vector vel = ball.getVelocity(); + double speed = vel.length(); + + handleWallBounce(vel); + handleParticles(speed); + + // Dribbel-Logik + for (Entity nearby : ball.getNearbyEntities(0.7, 0.5, 0.7)) { + if (nearby instanceof Player p) { + Vector direction = ball.getLocation().toVector().subtract(p.getLocation().toVector()); + if (direction.lengthSquared() > 0) { + direction.normalize(); + direction.setY(0.12); + ball.setVelocity(direction.multiply(0.35)); + } + lastMoveTime = System.currentTimeMillis(); + } + } + + // Automatischer Respawn bei Inaktivität oder Void + long delay = NexusLobby.getInstance().getConfig().getLong("ball.respawn_delay", 60) * 1000; + if (System.currentTimeMillis() - lastMoveTime > delay || ball.getLocation().getY() < -5) { + respawnBall(); + } + }, 1L, 1L); + } + + /** + * Scannt ALLE Welten nach Entities mit dem BALL_TAG und entfernt sie. + * Nutzt mehrere Erkennungsmethoden für maximale Sicherheit. + */ + private void removeAllOldBallsGlobal() { + int removed = 0; + // Alle Welten durchsuchen + for (World world : Bukkit.getWorlds()) { + for (Entity entity : world.getEntities()) { + if (entity instanceof ArmorStand stand) { + boolean shouldRemove = false; + + // Methode 1: Tag-basiert + if (stand.getScoreboardTags().contains(BALL_TAG)) { + shouldRemove = true; + } + + // Methode 2: Name-basiert (falls Tags verloren gehen) + if (stand.getCustomName() != null && stand.getCustomName().equals(BALL_NAME)) { + shouldRemove = true; + } + + // Methode 3: Kopf-Textur-basiert (prüfe ob es ein Soccer-Ball Kopf ist) + if (stand.getEquipment() != null && stand.getEquipment().getHelmet() != null) { + ItemStack helmet = stand.getEquipment().getHelmet(); + if (helmet.getType() == Material.PLAYER_HEAD && helmet.hasItemMeta()) { + SkullMeta meta = (SkullMeta) helmet.getItemMeta(); + if (meta.hasOwner() && meta.getOwnerProfile() != null) { + PlayerProfile profile = meta.getOwnerProfile(); + if (profile.getName() != null && profile.getName().equals("SoccerBall")) { + shouldRemove = true; + } + } + } + } + + // Methode 4: Position-basiert - Entferne alle unsichtbaren, kleinen ArmorStands in der Nähe des Spawns + if (spawnLocation != null && spawnLocation.getWorld() != null && + stand.getWorld().equals(spawnLocation.getWorld()) && + stand.isSmall() && stand.isInvisible() && !stand.hasBasePlate()) { + + double distance = stand.getLocation().distance(spawnLocation); + // Wenn innerhalb von 5 Blöcken vom Spawn und hat einen Kopf + if (distance < 5.0 && stand.getEquipment() != null && + stand.getEquipment().getHelmet() != null && + stand.getEquipment().getHelmet().getType() == Material.PLAYER_HEAD) { + shouldRemove = true; + } + } + + // Nur entfernen wenn es NICHT unsere aktuelle Ball-Instanz ist + if (shouldRemove && (ball == null || !stand.getUniqueId().equals(ball.getUniqueId()))) { + stand.remove(); + removed++; + } + } + } + } + if (removed > 0) { + Bukkit.getLogger().info("[NexusLobby] " + removed + " alte Ball-Entities entfernt."); + } + } + + private void handleWallBounce(Vector vel) { + if (vel.lengthSquared() < 0.001) return; + Location loc = ball.getLocation(); + Block nextX = loc.clone().add(vel.getX() * 1.3, 0.5, 0).getBlock(); + Block nextZ = loc.clone().add(0, 0.5, vel.getZ() * 1.3).getBlock(); + + boolean bounced = false; + if (nextX.getType().isSolid()) { vel.setX(-vel.getX() * 0.75); bounced = true; } + if (nextZ.getType().isSolid()) { vel.setZ(-vel.getZ() * 0.75); bounced = true; } + + if (bounced) { + ball.setVelocity(vel); + ball.getWorld().playSound(ball.getLocation(), Sound.BLOCK_WOOD_BREAK, 0.6f, 1.3f); + } + } + + private void handleParticles(double speed) { + if (speed < 0.05) return; + Location loc = ball.getLocation().add(0, 0.2, 0); + World world = loc.getWorld(); + if (world == null) return; + + if (speed > 0.85) world.spawnParticle(Particle.SONIC_BOOM, loc, 1, 0, 0, 0, 0); + else if (speed > 0.45) world.spawnParticle(Particle.CRIT, loc, 3, 0.1, 0.1, 0.1, 0.08); + else world.spawnParticle(Particle.SMOKE, loc, 1, 0.05, 0, 0.05, 0.02); + } + + private void spawnBall() { + if (spawnLocation == null || (ball != null && ball.isValid())) return; + + // Ball direkt auf dem Boden spawnen (keine Y-Offset Erhöhung) + Location spawnLoc = spawnLocation.clone(); + ball = (ArmorStand) spawnLoc.getWorld().spawnEntity(spawnLoc, EntityType.ARMOR_STAND); + + ball.setInvisible(true); + ball.setGravity(true); + ball.setBasePlate(false); + ball.setSmall(true); + ball.setInvulnerable(false); + ball.setArms(false); + ball.setCustomNameVisible(false); + + // Setze Custom Name für zusätzliche Erkennung (unsichtbar für Spieler) + ball.setCustomName(BALL_NAME); + + // Deaktiviert das Speichern in der Welt-Datei + ball.setPersistent(false); + // Markiert den Ball für das Cleanup-System + ball.addScoreboardTag(BALL_TAG); + + ItemStack ballHead = getSoccerHead(); + if (ball.getEquipment() != null) ball.getEquipment().setHelmet(ballHead); + lastMoveTime = System.currentTimeMillis(); + } + + private ItemStack getSoccerHead() { + ItemStack head = new ItemStack(Material.PLAYER_HEAD); + SkullMeta meta = (SkullMeta) head.getItemMeta(); + if (meta == null) return head; + + PlayerProfile profile = Bukkit.createPlayerProfile(UUID.randomUUID(), "SoccerBall"); + try { + profile.getTextures().setSkin(new URL(TEXTURE_URL)); + } catch (MalformedURLException ignored) {} + meta.setOwnerProfile(profile); + + head.setItemMeta(meta); + return head; + } + + public void respawnBall() { + if (ball != null) { + ball.remove(); + ball = null; + } + removeAllOldBallsGlobal(); + spawnBall(); + } + + @EventHandler + public void onBallPunch(EntityDamageByEntityEvent event) { + if (event.getEntity().equals(ball)) { + event.setCancelled(true); + if (event.getDamager() instanceof Player p) { + Vector shootDir = p.getLocation().getDirection(); + if (shootDir.lengthSquared() > 0) { + shootDir.normalize().multiply(1.35).setY(0.38); + ball.setVelocity(shootDir); + } + ball.getWorld().playSound(ball.getLocation(), Sound.ENTITY_ZOMBIE_ATTACK_IRON_DOOR, 0.6f, 1.5f); + lastMoveTime = System.currentTimeMillis(); + } + } + } + + @EventHandler + public void onBallInteract(PlayerInteractAtEntityEvent event) { + if (event.getRightClicked().equals(ball)) event.setCancelled(true); + } + + private void loadConfigLocation() { + FileConfiguration config = NexusLobby.getInstance().getConfig(); + if (config.contains("ball.spawn")) spawnLocation = config.getLocation("ball.spawn"); + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player p) || !p.hasPermission("nexuslobby.admin")) return true; + + if (args.length >= 2 && args[0].equalsIgnoreCase("ball")) { + if (args[1].equalsIgnoreCase("setspawn")) { + spawnLocation = p.getLocation(); + NexusLobby.getInstance().getConfig().set("ball.spawn", spawnLocation); + NexusLobby.getInstance().saveConfig(); + respawnBall(); + p.sendMessage("§8[§6Nexus§8] §aBall-Spawn gesetzt. Cleanup ist aktiv!"); + return true; + } else if (args[1].equalsIgnoreCase("respawn")) { + respawnBall(); + p.sendMessage("§8[§6Nexus§8] §eBall manuell respawnt."); + return true; + } + } + return false; + } + + @Override + public void onDisable() { + if (ball != null) ball.remove(); + } +} \ No newline at end of file diff --git a/src/main/java/de/nexuslobby/modules/gadgets/GadgetModule.java b/src/main/java/de/nexuslobby/modules/gadgets/GadgetModule.java index f213fa1..6783883 100644 --- a/src/main/java/de/nexuslobby/modules/gadgets/GadgetModule.java +++ b/src/main/java/de/nexuslobby/modules/gadgets/GadgetModule.java @@ -3,6 +3,7 @@ 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; @@ -15,6 +16,7 @@ import org.bukkit.event.player.PlayerFishEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemFlag; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; @@ -59,7 +61,6 @@ public class GadgetModule implements Module, Listener { }, 1L, 1L); } - // Event für die Benutzung der aktiven Gadgets @EventHandler public void onInteract(PlayerInteractEvent event) { ItemStack item = event.getItem(); @@ -80,7 +81,6 @@ public class GadgetModule implements Module, Listener { } } - // Verhindert Bewegung für eingefrorene Spieler (Stasis) @EventHandler public void onPlayerMove(PlayerMoveEvent event) { if (FreezeRay.frozenPlayers.contains(event.getPlayer().getUniqueId())) { @@ -95,19 +95,11 @@ public class GadgetModule implements Module, Listener { 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); - break; - case SPAWNER: - p.getWorld().spawnParticle(Particle.FLAME, p.getLocation().add(0, 2.1, 0), 1, 0.12, 0.12, 0.12, 0.02); - break; - case SEA_LANTERN: - case BEACON: - p.getWorld().spawnParticle(Particle.END_ROD, p.getLocation().add(0, 2.1, 0), 1, 0.1, 0.1, 0.1, 0.03); - break; - case ENCHANTING_TABLE: - p.getWorld().spawnParticle(Particle.ENCHANT, p.getLocation().add(0, 2.3, 0), 1, 0.2, 0.2, 0.2, 0.5); - break; + 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 -> {} } } @@ -136,7 +128,6 @@ public class GadgetModule implements Module, Listener { 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")); @@ -144,7 +135,6 @@ public class GadgetModule implements Module, Listener { 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")); @@ -224,37 +214,32 @@ public class GadgetModule implements Module, Listener { 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)) { + } else if (title.equals(HAT_TITLE)) { if (item.getType() != Material.GRAY_STAINED_GLASS_PANE) { HatManager.setHat(player, item.getType(), item.getItemMeta().getDisplayName()); player.playSound(player.getLocation(), Sound.ITEM_ARMOR_EQUIP_GENERIC, 1, 1); player.closeInventory(); } - } - else if (title.equals(PET_TITLE)) { + } 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)) { + } 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)) { + } 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)) { + } else if (title.equals(FUN_TITLE)) { if (item.getType() == Material.EGG) { ChickenRain.start(player); player.sendMessage("§8[§6Nexus§8] §fHühnerregen gestartet!"); @@ -288,7 +273,7 @@ public class GadgetModule implements Module, Listener { public void onFish(PlayerFishEvent event) { Player player = event.getPlayer(); ItemStack item = player.getInventory().getItemInMainHand(); - if (item != null && item.getType() == Material.FISHING_ROD && item.hasItemMeta() && item.getItemMeta().getDisplayName().equals("§b§lEnterhaken")) { + if (item.getType() == Material.FISHING_ROD && item.hasItemMeta() && item.getItemMeta().getDisplayName().equals("§b§lEnterhaken")) { if (event.getState() == PlayerFishEvent.State.IN_GROUND || event.getState() == PlayerFishEvent.State.REEL_IN || event.getState() == PlayerFishEvent.State.CAUGHT_ENTITY) { if (event.getHook() != null) { GrapplingHook.pullPlayer(player, event.getHook().getLocation()); @@ -314,6 +299,7 @@ public class GadgetModule implements Module, Listener { } private void fillEdges(Inventory inv) { + // Bedrock braucht einen Space, um den Namen korrekt anzuzeigen 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); @@ -325,11 +311,19 @@ public class GadgetModule implements Module, Listener { ItemMeta meta = item.getItemMeta(); if (meta != null) { meta.setDisplayName(name); - if (lore != null) { - List l = new ArrayList<>(); - l.add(lore); + + // WICHTIG FÜR BEDROCK: Saubere ArrayList für Lore + List l = new ArrayList<>(); + if (lore != null && !lore.isEmpty()) { + l.add(ChatColor.translateAlternateColorCodes('&', lore)); meta.setLore(l); } + + // VERSTECKT ATTRIBUTE: Verhindert "+Armor" etc., was bei Bedrock die Lore überdeckt + meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES); + meta.addItemFlags(ItemFlag.HIDE_UNBREAKABLE); + meta.addItemFlags(ItemFlag.HIDE_ENCHANTS); + item.setItemMeta(meta); } return item; diff --git a/src/main/java/de/nexuslobby/modules/settings/LobbySettingsModule.java b/src/main/java/de/nexuslobby/modules/settings/LobbySettingsModule.java index d8f805d..0ce25ba 100644 --- a/src/main/java/de/nexuslobby/modules/settings/LobbySettingsModule.java +++ b/src/main/java/de/nexuslobby/modules/settings/LobbySettingsModule.java @@ -6,12 +6,12 @@ import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.Sound; import org.bukkit.World; +import org.bukkit.GameRule; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; -import org.bukkit.event.inventory.ClickType; // DIESER IMPORT HAT GEFEHLT import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; @@ -54,8 +54,30 @@ public class LobbySettingsModule implements Module, Listener { Set keys = settingsConfig.getConfigurationSection("gamerules").getKeys(false); for (World world : Bukkit.getWorlds()) { for (String key : keys) { - String value = String.valueOf(settingsConfig.get("gamerules." + key)); - world.setGameRuleValue(key, value); + Object value = settingsConfig.get("gamerules." + key); + updateGameRule(world, key, value); + } + } + } + + // Hilfsmethode zur sicheren Anwendung von GameRules in 1.21 + private void updateGameRule(World world, String ruleName, Object value) { + GameRule rule = GameRule.getByName(ruleName); + if (rule == null) return; + + if (value instanceof Boolean && rule.getType() == Boolean.class) { + world.setGameRule((GameRule) rule, (Boolean) value); + } else if (value instanceof Integer && rule.getType() == Integer.class) { + world.setGameRule((GameRule) rule, (Integer) value); + } else { + // Falls der Wert aus der Config als String kommt, versuchen wir ihn zu parsen + String strValue = String.valueOf(value); + if (rule.getType() == Boolean.class) { + world.setGameRule((GameRule) rule, Boolean.parseBoolean(strValue)); + } else if (rule.getType() == Integer.class) { + try { + world.setGameRule((GameRule) rule, Integer.parseInt(strValue)); + } catch (NumberFormatException ignored) {} } } } @@ -154,7 +176,7 @@ public class LobbySettingsModule implements Module, Listener { settingsConfig.set(path, newValue); saveSettings(); if (path.startsWith("gamerules.")) { - for (World w : Bukkit.getWorlds()) w.setGameRuleValue(key, String.valueOf(newValue)); + for (World w : Bukkit.getWorlds()) updateGameRule(w, key, newValue); } } else if (value instanceof Integer) { int newVal = (Integer) value + (event.getClick().isLeftClick() ? 1 : -1); @@ -162,7 +184,7 @@ public class LobbySettingsModule implements Module, Listener { settingsConfig.set(path, newVal); saveSettings(); if (path.startsWith("gamerules.")) { - for (World w : Bukkit.getWorlds()) w.setGameRuleValue(key, String.valueOf(newVal)); + for (World w : Bukkit.getWorlds()) updateGameRule(w, key, newVal); } } @@ -171,7 +193,6 @@ public class LobbySettingsModule implements Module, Listener { } private void refreshCategory(Player p, String category) { - // Die refresh-Logik wurde vereinfacht, um Fehler zu vermeiden if (category.equals("Lobby-Schutz")) openCategory(p, "Lobby-Schutz", "allowPvp", "allowBlockBreaking", "allowBlockPlacing", "allowBlockInteracting", "allowItemDropping", "allowItemPickup", "allowExplosions"); else if (category.equals("Chat")) openCategory(p, "Chat", "announceAdvancements", "commandBlockOutput", "logAdminCommands", "sendCommandFeedback", "showDeathMessages", "reducedDebugInfo"); else if (category.equals("Drops")) openCategory(p, "Drops", "keepInventory", "doEntityDrops", "doMobLoot", "doTileDrops", "mobExplosionDropDecay", "blockExplosionDropDecay", "tntExplosionDropDecay"); diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 0ce575c..1a0d409 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -160,3 +160,19 @@ hider: all: "&aAlle Spieler: &7Sichtbar" # Anzeigename des Items und Nachricht, wenn alle versteckt sind none: "&cKeine Spieler: &7Versteckt" + +# ----------------------------------------------------- +# BALL / SOCCER EINSTELLUNGEN +# ----------------------------------------------------- +ball: + enabled: true + # Der Spawnpunkt wird automatisch über /nexus ball setspawn hier gespeichert + spawn: + world: "world" + x: 10.5 + y: 65.0 + z: 10.5 + yaw: 0.0 + pitch: 0.0 + # Zeit in Sekunden, bis der Ball bei Inaktivität respawnt + respawn_delay: 60 \ No newline at end of file diff --git a/src/main/resources/conversations.yml b/src/main/resources/conversations.yml new file mode 100644 index 0000000..f001515 --- /dev/null +++ b/src/main/resources/conversations.yml @@ -0,0 +1,164 @@ +# ============================================================= +# NexusLobby - Lebendige Dialoge v2 +# ============================================================= + +conversations: + parkour_begruessung: + morgens: + dialogue: + - '&6&lTrainer &8» &fGuten Morgen! Zeit für Frühsport! *streckt sich*' + - '&6&lTrainer &8» &fWillkommen bei &e&lNexusLobby&f!' + - '&6&lTrainer &8» &eKlick mich an&f, um den Parkour zu starten!' + mittags: + dialogue: + - '&6&lTrainer &8» &fDie Sonne brennt, der Parkour wartet!' + - '&6&lTrainer &8» &fHast du das Zeug zum &aParkour-Meister&f?' + - '&6&lTrainer &8» &eKlick mich an&f, wenn du dich traust!' + abends: + dialogue: + - '&6&lTrainer &8» &fNoch eine Runde vor dem Schlafengehen?' + - '&6&lTrainer &8» &fZeig bei &e&lNexusLobby&f, was du noch drauf hast!' + - '&6&lTrainer &8» &eKlick mich an&f, für deinen neuen Rekord!' + + owner_besuch: + morgens: + dialogue: + - '&dTimmy: &fMami, ist der Owner schon wach? &e*hüpft*' + - '&5Sarah: &7Bestimmt! Er schließt den Server auf.' + - '&dTimmy: &fIch zeig ihm meinen Teddy mit Krone! &a*stolz*' + - '&5Sarah: &7Leise sein, falls er noch arbeitet. *psst*' + mittags: + dialogue: + - '&dTimmy: &fDa ist das Büro! Ich seh die Fenster! &e*rennt*' + - '&5Sarah: &cStopp! &7Nicht so stürmisch, Timmy.' + - '&dTimmy: &fAber Teddy will den Schreibtisch sehen!' + - '&5Sarah: &7Komm an meine Hand. Wir klopfen ganz brav.' + abends: + dialogue: + - '&dTimmy: &fGuck! Im Büro brennt noch Licht! &b*zeigt*' + - '&5Sarah: &7Er arbeitet sicher noch an Updates.' + - '&dTimmy: &fIst er ein Roboter? Er schläft nie! &7*staunt*' + - '&5Sarah: &7Nein, er braucht nur viel Zaubertrank. *lacht*' + + fussball_match: + morgens: + dialogue: + - '&eLeon: &fFinn! Aufwachen! Kick den Ball! &e*kickt*' + - '&6Finn: &8*gähnt* &7Mein Fuß schläft noch, Leon...' + - '&eLeon: &fKeine Ausreden! Pass an! &6*kickt hart*' + - '&6Finn: &fHuch! &7*stoppt unsicher* &fBin ja schon wach!' + mittags: + dialogue: + - '&eLeon: &fPass auf! Jetzt kommt mein Spezialschuss!' + - '&6Finn: &fDen hab ich! &e*macht Hechtsprung* &fJaaaa!' + - '&eLeon: &7Wie hast du den denn erwischt? &c*schmollt*' + - '&6Finn: &fIch kenne deine Tricks, Leon! *grinst*' + abends: + dialogue: + - '&eLeon: &7Ich seh den Ball kaum noch. &8*kneift Augen*' + - '&6Finn: &fEgal! Nächstes Tor gewinnt die Krone!' + - '&eLeon: &fHe! Das war Foul! Du hast geschubst! &c*meckert*' + - '&6Finn: &7Körpereinsatz! Fang mich doch! &a*rennt weg*' + + baum_drama: + morgens: + dialogue: + - '&aLotte: &fDie Aussicht ist göttlich! &d*atmet tief*' + - '&bSchmidt: &7Lotte, komm runter! Es ist 7 Uhr morgens!' + - '&aLotte: &fIch beobachte Vögel. Ich bin jetzt einer!' + - '&bSchmidt: &7Du brauchst Kaffee, keinen Baum. &8*seufzt*' + mittags: + dialogue: + - '&aLotte: &fIch kann fliegen! Seht mich an! &7*balanciert*' + - '&bSchmidt: &cLotte, komm sofort vom Baum runter!' + - '&aLotte: &fIch bin ein stolzer Adler! &e*breitet Arme aus*' + - '&bSchmidt: &7Du bist eine Frau auf einer Birke! Abstieg!' + abends: + dialogue: + - '&aLotte: &fVon hier sieht man das Feuerwerk besser!' + - '&bSchmidt: &7Es wird kalt. Und da oben sind Spinnen!' + - '&aLotte: &fSpinnen? &7*schaut hektisch* &fWo?!' + - '&bSchmidt: &7Komm zur Leiter. Ich rette dich. &e*grinst*' + + familien_spaziergang: + morgens: + dialogue: + - '&9Tom: &7Schaut mal diesen Sonnenaufgang an. &6*genießt*' + - '&5Eva: &7Lass dir Zeit. Die Welt wacht gerade erst auf.' + - '&dJasmin: &fPapa, die Blumen gehen auf! &e*bleibt stehen*' + - '&9Tom: &7Wollen wir zum Bäcker? Frische Brötchen!' + mittags: + dialogue: + - '&9Tom: &7Klar, Jasmin. Wünsch dir was ganz Besonderes!' + - '&5Eva: &7Nicht reinfallen! Das Wasser ist tief. &6*lacht*' + - '&dJasmin: &fDarf ich eine Münze werfen? Bitte! &d*hüpft*' + - '&9Tom: &7Ich wünsche mir... &7*kneift Augen zu* &f...Eis!' + abends: + dialogue: + - '&9Tom: &7Der ganze Spawn leuchtet jetzt schön. &b*guckt*' + - '&5Eva: &7Es wird so herrlich ruhig hier, oder? &d*lächelt*' + - '&dJasmin: &fPapa, darf ich auf deine Schultern? &7*gähnt*' + - '&9Tom: &7Hopp! &e*hebt sie hoch* &7Na, wie ist die Sicht?' + + frauen_plausch: + morgens: + dialogue: + - '&9Marie: &7Guten Morgen! Du siehst verschlafen aus.' + - '&fPia: &7Die Party gestern war zu lang. Kaffee... &8*gähnt*' + - '&9Marie: &7Der Bäcker hat frische Zimtschnecken! Komm!' + - '&fPia: &fSorg dafür, dass ich nicht umkippe. &7*lacht*' + mittags: + dialogue: + - '&9Marie: &7Hast du den neuen Shop gesehen? Tolle Erze!' + - '&fPia: &fEcht jetzt? &7Ich dachte, die Mine ist zu.' + - '&9Marie: &7Die haben einen neuen Tunnel! Riesige Smaragde!' + - '&fPia: &fWahnsinn. Ich muss mein Inventar leeren!' + abends: + dialogue: + - '&9Marie: &7Hast du die Lichter am Hafen gesehen? &b*staunt*' + - '&fPia: &7Leider verpasst... ich musste Truhen sortieren.' + - '&9Marie: &7Du arbeitest zu viel, Pia. Genieß den Abend!' + - '&fPia: &7Stimmt. Morgen machen wir blau, okay?' + + wettrennen_jungs: + morgens: + dialogue: + - '&bNico: &fErster am Portal! Lauf schneller, Lukas!' + - '&fLukas: &c*keucht* &fWarte! Meine Schuhe sind offen!' + - '&bNico: &fKeine Ausreden! Die Sonne lacht! &a*rennt*' + - '&fLukas: &fNa warte, ich krieg dich noch! &7*sprintet*' + mittags: + dialogue: + - '&bNico: &fKomm schon, du lahme Ente! &a*rast davon*' + - '&fLukas: &c*außer Atem* &7Ich... hab heute schon gefarmt!' + - '&bNico: &fErster! Gewonnen! &7*tanzt* &fHer mit dem Apfel!' + - '&fLukas: &fMorgen gewinnen meine Speed-Stiefel! &c*schwitzt*' + abends: + dialogue: + - '&bNico: &fLetztes Rennen! Verlierer poliert Rüstungen!' + - '&fLukas: &fDiesmal nicht, Nico! &e*nimmt Anlauf*' + - '&bNico: &fOh, jetzt wird es ernst? &7Fertig... LOS!' + - '&fLukas: &fJaaa! Ich bin vorne! Wer ist jetzt lahm?! &a*lacht*' + + mutter_kind_spiel: + morgens: + dialogue: + - '&dMia: &fMami, schau! Meine Puppen warten schon. &d*zeigt*' + - '&5Sandra: &7Warten sie auf den Empfang, Mia?' + - '&dMia: &fJa, sie wollen zum Owner! &e*rückt Puppe recht*' + - '&5Sandra: &7Dann müssen sie brav Schlange stehen. &7*lächelt*' + mittags: + dialogue: + - '&dMia: &fGuck! Ein riesiges Hotel aus Klötzen! &e*stapelt*' + - '&5Sandra: &7Oha, wird das ein Dino-Hotel?' + - '&dMia: &fJa! Der Dino frisst nur Kekse! &e*füttert ihn*' + - '&5Sandra: &7Bau fleißig weiter, kleine Architektin.' + abends: + dialogue: + - '&dMia: &fMami, Dino ist müde. &7*gähnt laut*' + - '&5Sandra: &7Pack ihn gut in seine Decke ein, Mia.' + - '&dMia: &fMuss ich meine Klötze einpacken? &c*traurig*' + - '&5Sandra: &7Ja, aber morgen bauen wir ein Schloss!' + +links: + diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index fb15ad1..7ec7d1e 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,6 +1,6 @@ name: NexusLobby main: de.nexuslobby.NexusLobby -version: "1.0.8" +version: "1.1.0" api-version: "1.21" author: M_Viper description: Modular Lobby Plugin with an invisible Particle-Parkour system.