Update from Git Manager GUI

This commit is contained in:
2026-01-31 21:24:57 +01:00
parent 85aaadbf99
commit ce43cac14f
16 changed files with 1381 additions and 185 deletions

View File

@@ -13,13 +13,16 @@ import de.nexuslobby.modules.settings.LobbySettingsModule;
import de.nexuslobby.modules.portal.PortalManager; import de.nexuslobby.modules.portal.PortalManager;
import de.nexuslobby.modules.portal.PortalCommand; import de.nexuslobby.modules.portal.PortalCommand;
import de.nexuslobby.modules.servers.ServerSwitcherListener; import de.nexuslobby.modules.servers.ServerSwitcherListener;
import de.nexuslobby.modules.servers.ServerChecker; // Hinzugefügt import de.nexuslobby.modules.servers.ServerChecker;
import de.nexuslobby.modules.armorstandtools.*; import de.nexuslobby.modules.armorstandtools.*;
import de.nexuslobby.modules.gadgets.GadgetModule; import de.nexuslobby.modules.gadgets.GadgetModule;
import de.nexuslobby.modules.hologram.HologramModule; import de.nexuslobby.modules.hologram.HologramModule;
import de.nexuslobby.modules.mapart.MapArtModule; import de.nexuslobby.modules.mapart.MapArtModule;
import de.nexuslobby.modules.intro.IntroModule; import de.nexuslobby.modules.intro.IntroModule;
import de.nexuslobby.modules.border.BorderModule; 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.utils.*; import de.nexuslobby.utils.*;
import me.clip.placeholderapi.expansion.PlaceholderExpansion; import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import net.md_5.bungee.api.chat.ClickEvent; import net.md_5.bungee.api.chat.ClickEvent;
@@ -38,6 +41,7 @@ import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerInteractAtEntityEvent;
import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scheduler.BukkitRunnable;
@@ -62,6 +66,7 @@ public class NexusLobby extends JavaPlugin implements Listener {
private MapArtModule mapArtModule; private MapArtModule mapArtModule;
private IntroModule introModule; private IntroModule introModule;
private BorderModule borderModule; private BorderModule borderModule;
private ParkourManager parkourManager;
private ConversationManager conversationManager; private ConversationManager conversationManager;
@@ -85,6 +90,10 @@ public class NexusLobby extends JavaPlugin implements Listener {
return conversationManager; return conversationManager;
} }
public ParkourManager getParkourManager() {
return parkourManager;
}
@Override @Override
public void onEnable() { public void onEnable() {
instance = this; instance = this;
@@ -94,15 +103,17 @@ public class NexusLobby extends JavaPlugin implements Listener {
getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord"); getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord");
moduleManager = new ModuleManager(this); moduleManager = new ModuleManager(this);
// --- Parkour & Conversation Initialisierung ---
this.parkourManager = new ParkourManager(this);
this.conversationManager = new ConversationManager(this); this.conversationManager = new ConversationManager(this);
ArmorStandGUI.init(); ArmorStandGUI.init();
registerModules(); registerModules();
moduleManager.enableAll(); moduleManager.enableAll();
registerListeners(); registerListeners();
// --- STATUS CHECKER START ---
ServerChecker.startGlobalChecker(); ServerChecker.startGlobalChecker();
new ArmorStandLookAtModule(); new ArmorStandLookAtModule();
@@ -124,40 +135,11 @@ public class NexusLobby extends JavaPlugin implements Listener {
new BukkitRunnable() { new BukkitRunnable() {
@Override @Override
public void run() { public void run() {
if (conversationManager == null) return; if (conversationManager != null) {
conversationManager.startAllAutomatedConversations();
for (World world : Bukkit.getWorlds()) {
for (ArmorStand as : world.getEntitiesByClass(ArmorStand.class)) {
if (as.getScoreboardTags().stream().anyMatch(tag -> tag.startsWith("conv_id:"))) {
boolean playerNearby = false;
for (Entity nearby : as.getNearbyEntities(30, 30, 30)) {
if (nearby instanceof Player) {
playerNearby = true;
break;
} }
} }
}.runTaskTimer(this, 100L, 300L);
if (playerNearby) {
String dialogId = null;
String partnerUUID = null;
for (String tag : as.getScoreboardTags()) {
if (tag.startsWith("conv_id:")) dialogId = tag.split(":")[1];
if (tag.startsWith("conv_partner:")) partnerUUID = tag.split(":")[1];
}
if (dialogId != null && partnerUUID != null) {
try {
UUID partnerId = UUID.fromString(partnerUUID);
conversationManager.playConversation(as.getUniqueId(), partnerId, dialogId);
} catch (Exception ignored) {}
}
}
}
}
}
}
}.runTaskTimer(this, 20L * 30, 20L * 90);
} }
public void reloadPlugin() { public void reloadPlugin() {
@@ -250,6 +232,9 @@ public class NexusLobby extends JavaPlugin implements Listener {
this.tablistModule = new TablistModule(); this.tablistModule = new TablistModule();
moduleManager.registerModule(tablistModule); moduleManager.registerModule(tablistModule);
// Player Inspect Modul registrieren
moduleManager.registerModule(new PlayerInspectModule()); // NEU
this.portalManager = new PortalManager(this); this.portalManager = new PortalManager(this);
moduleManager.registerModule(portalManager); moduleManager.registerModule(portalManager);
} }
@@ -262,6 +247,24 @@ public class NexusLobby extends JavaPlugin implements Listener {
getServer().getPluginManager().registerEvents(new PlayerHider(), this); getServer().getPluginManager().registerEvents(new PlayerHider(), this);
getServer().getPluginManager().registerEvents(new MaintenanceListener(), this); getServer().getPluginManager().registerEvents(new MaintenanceListener(), this);
getServer().getPluginManager().registerEvents(new ASTListener(), this); getServer().getPluginManager().registerEvents(new ASTListener(), this);
getServer().getPluginManager().registerEvents(new ParkourListener(this.parkourManager), this);
getServer().getPluginManager().registerEvents(new NPCClickListener(), this);
}
public class NPCClickListener implements Listener {
@EventHandler
public void onNPCClick(PlayerInteractAtEntityEvent event) {
Entity entity = event.getRightClicked();
Player player = event.getPlayer();
if (entity instanceof ArmorStand as) {
if (as.getScoreboardTags().contains("parkour_trainer")) {
player.performCommand("warp parkour");
player.sendMessage("§6§lTrainer §8» §aViel Erfolg beim Parkour! Gib dein Bestes!");
}
}
}
} }
@EventHandler(priority = EventPriority.LOWEST) @EventHandler(priority = EventPriority.LOWEST)
@@ -348,7 +351,9 @@ public class NexusLobby extends JavaPlugin implements Listener {
@Override @Override
public void onDisable() { public void onDisable() {
getServer().getMessenger().unregisterOutgoingPluginChannel(this, "BungeeCord"); getServer().getMessenger().unregisterOutgoingPluginChannel(this, "BungeeCord");
if (moduleManager != null) moduleManager.disableAll(); if (moduleManager != null) {
moduleManager.disableAll();
}
getLogger().info("NexusLobby deaktiviert."); getLogger().info("NexusLobby deaktiviert.");
} }
@@ -417,17 +422,22 @@ public class NexusLobby extends JavaPlugin implements Listener {
if (params.equalsIgnoreCase("version")) return NexusLobby.this.getDescription().getVersion(); if (params.equalsIgnoreCase("version")) return NexusLobby.this.getDescription().getVersion();
if (params.equalsIgnoreCase("build_mode")) return BuildCommand.isInBuildMode(player) ? "§aAktiv" : "§cInaktiv"; if (params.equalsIgnoreCase("build_mode")) return BuildCommand.isInBuildMode(player) ? "§aAktiv" : "§cInaktiv";
if (params.equalsIgnoreCase("silent_join")) return silentPlayers.contains(player.getUniqueId()) ? "§aEin" : "§cAus"; if (params.equalsIgnoreCase("silent_join")) return silentPlayers.contains(player.getUniqueId()) ? "§aEin" : "§cAus";
if (params.equalsIgnoreCase("parkour_top")) {
return parkourManager != null ? parkourManager.getTopTen() : "N/A";
}
return null; return null;
} }
} }
public ModuleManager getModuleManager() { return moduleManager; } public ModuleManager getModuleManager() { return moduleManager; }
public PortalManager getPortalManager() { return portalManager; } // Hinzugefügt/Wiederhergestellt public PortalManager getPortalManager() { return portalManager; }
public TablistModule getTablistModule() { return tablistModule; } public TablistModule getTablistModule() { return tablistModule; }
public LobbySettingsModule getLobbySettingsModule() { return lobbySettingsModule; } public LobbySettingsModule getLobbySettingsModule() { return lobbySettingsModule; }
public ItemsModule getItemsModule() { return itemsModule; } public ItemsModule getItemsModule() { return itemsModule; }
public GadgetModule getGadgetModule() { return gadgetModule; } public GadgetModule getGadgetModule() { return gadgetModule; }
public HologramModule getHologramModule() { return hologramModule; } public HologramModule getHologramModule() { return hologramModule; }
public DynamicArmorStandModule getDynamicArmorStandModule() { return dynamicArmorStandModule; } public DynamicArmorStandModule getDynamicArmorStandModule() { return dynamicArmorStandModule; }
public MapArtModule getMapArtModule() { return mapArtModule; } // Wiederhergestellt public MapArtModule getMapArtModule() { return mapArtModule; }
public IntroModule getIntroModule() { return introModule; }
public BorderModule getBorderModule() { return borderModule; }
} }

View File

@@ -32,7 +32,7 @@ public class LobbyTabCompleter implements TabCompleter {
if (cmdName.equals("nexuslobby") || cmdName.equals("nexus")) { if (cmdName.equals("nexuslobby") || cmdName.equals("nexus")) {
if (args.length == 1) { if (args.length == 1) {
if (sender.hasPermission("nexuslobby.admin")) { if (sender.hasPermission("nexuslobby.admin")) {
suggestions.addAll(Arrays.asList("reload", "setspawn", "silentjoin")); suggestions.addAll(Arrays.asList("reload", "setspawn", "silentjoin", "parkour"));
} }
suggestions.add("sb"); suggestions.add("sb");
} else if (args.length == 2) { } else if (args.length == 2) {
@@ -43,6 +43,12 @@ public class LobbyTabCompleter implements TabCompleter {
} }
} else if (args[0].equalsIgnoreCase("silentjoin")) { } else if (args[0].equalsIgnoreCase("silentjoin")) {
suggestions.addAll(Arrays.asList("on", "off")); 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.length == 3) {
if (args[0].equalsIgnoreCase("parkour") && args[1].equalsIgnoreCase("setcheckpoint")) {
suggestions.addAll(Arrays.asList("1", "2", "3", "4", "5", "6", "7", "8", "9"));
} }
} }
} }
@@ -101,10 +107,10 @@ public class LobbyTabCompleter implements TabCompleter {
} }
} }
// --- NexusCmd (ArmorStand Commands & Dialoge) --- // --- NexusCmd / ArmorStandTools ---
else if (cmdName.equals("nexuscmd") || cmdName.equals("ncmd") || cmdName.equals("ascmd")) { else if (cmdName.equals("nexuscmd") || cmdName.equals("ncmd") || cmdName.equals("ascmd") || cmdName.equals("conv")) {
if (args.length == 1) { if (args.length == 1) {
suggestions.addAll(Arrays.asList("add", "remove", "list", "name", "lookat", "conv")); suggestions.addAll(Arrays.asList("add", "remove", "list", "name", "lookat", "conv", "say"));
} }
else if (args.length == 2) { else if (args.length == 2) {
if (args[0].equalsIgnoreCase("add")) { if (args[0].equalsIgnoreCase("add")) {
@@ -112,10 +118,12 @@ public class LobbyTabCompleter implements TabCompleter {
} else if (args[0].equalsIgnoreCase("name")) { } else if (args[0].equalsIgnoreCase("name")) {
suggestions.addAll(Arrays.asList("<Anzeigename>", "none")); suggestions.addAll(Arrays.asList("<Anzeigename>", "none"));
} else if (args[0].equalsIgnoreCase("conv")) { } else if (args[0].equalsIgnoreCase("conv")) {
// NEU: unlink hinzugefügt // ERWEITERT: select3 und select4 hinzugefügt
suggestions.addAll(Arrays.asList("select1", "select2", "link", "unlink", "start")); suggestions.addAll(Arrays.asList("select1", "select2", "select3", "select4", "link", "unlink", "start"));
} else if (args[0].equalsIgnoreCase("remove")) { } else if (args[0].equalsIgnoreCase("remove")) {
suggestions.add("all"); suggestions.addAll(Arrays.asList("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "all"));
} else if (args[0].equalsIgnoreCase("say")) {
suggestions.add("<Nachricht>");
} }
} }
else if (args.length == 3) { else if (args.length == 3) {
@@ -139,10 +147,13 @@ public class LobbyTabCompleter implements TabCompleter {
} }
} }
// --- ArmorStandTools (/astools) --- // --- ArmorStandTools Alternate ---
else if (cmdName.equals("astools") || cmdName.equals("nt") || cmdName.equals("ntools")) { else if (cmdName.equals("astools") || cmdName.equals("nt") || cmdName.equals("ntools")) {
if (args.length == 1) { if (args.length == 1) {
suggestions.addAll(Arrays.asList("dynamic", "lookat", "addplayer", "addconsole", "remove", "reload")); suggestions.addAll(Arrays.asList("dynamic", "lookat", "addplayer", "addconsole", "remove", "reload", "say"));
}
else if (args.length == 2 && args[0].equalsIgnoreCase("say")) {
suggestions.add("<Nachricht>");
} }
else if (args.length == 2 && (args[0].equalsIgnoreCase("addplayer") || args[0].equalsIgnoreCase("addconsole"))) { else if (args.length == 2 && (args[0].equalsIgnoreCase("addplayer") || args[0].equalsIgnoreCase("addconsole"))) {
suggestions.addAll(Arrays.asList("0", "1", "2", "3", "4", "5", "6", "7", "8", "9")); suggestions.addAll(Arrays.asList("0", "1", "2", "3", "4", "5", "6", "7", "8", "9"));
@@ -152,7 +163,7 @@ public class LobbyTabCompleter implements TabCompleter {
} }
} }
// Filtert die Liste basierend auf der bisherigen Eingabe (für Case-Insensitivity und Teilübereinstimmung) // Filtert die Liste basierend auf der bisherigen Eingabe
return suggestions.stream() return suggestions.stream()
.filter(s -> s.toLowerCase().startsWith(args[args.length - 1].toLowerCase())) .filter(s -> s.toLowerCase().startsWith(args[args.length - 1].toLowerCase()))
.collect(Collectors.toList()); .collect(Collectors.toList());

View File

@@ -2,6 +2,7 @@ package de.nexuslobby.commands;
import de.nexuslobby.NexusLobby; import de.nexuslobby.NexusLobby;
import de.nexuslobby.modules.ScoreboardModule; import de.nexuslobby.modules.ScoreboardModule;
import de.nexuslobby.modules.parkour.ParkourManager;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Sound; import org.bukkit.Sound;
@@ -10,9 +11,13 @@ import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.List;
public class NexusLobbyCommand implements CommandExecutor { public class NexusLobbyCommand implements CommandExecutor {
@Override @Override
@@ -23,8 +28,29 @@ public class NexusLobbyCommand implements CommandExecutor {
return true; return true;
} }
String cmdName = command.getName().toLowerCase();
ParkourManager pm = NexusLobby.getInstance().getParkourManager();
// --- DIREKTE KURZ-BEFEHLE ---
if (cmdName.equalsIgnoreCase("setstart")) {
if (!player.hasPermission("nexuslobby.admin")) return noPerm(player);
handleSetStart(player, pm);
return true;
}
if (cmdName.equalsIgnoreCase("setcheckpoint")) {
if (!player.hasPermission("nexuslobby.admin")) return noPerm(player);
pm.setCheckpoint(player, player.getLocation());
return true;
}
if (cmdName.equalsIgnoreCase("setfinish")) {
if (!player.hasPermission("nexuslobby.admin")) return noPerm(player);
pm.setFinishLocation(player.getLocation());
player.sendMessage("§8[§6Nexus§8] §aParkour-Zielpunkt gesetzt!");
return true;
}
// --- SPAWN BEFEHL --- // --- SPAWN BEFEHL ---
if (command.getName().equalsIgnoreCase("spawn")) { if (cmdName.equalsIgnoreCase("spawn")) {
FileConfiguration config = NexusLobby.getInstance().getConfig(); FileConfiguration config = NexusLobby.getInstance().getConfig();
if (config.contains("spawn.world")) { if (config.contains("spawn.world")) {
Location loc = getSpawnFromConfig(config); Location loc = getSpawnFromConfig(config);
@@ -41,7 +67,7 @@ public class NexusLobbyCommand implements CommandExecutor {
return true; return true;
} }
// --- HAUPTBEFEHL ARGUMENTE --- // --- HAUPTBEFEHL /NEXUSLOBBY oder /NEXUS ---
if (args.length == 0) { if (args.length == 0) {
sendInfo(player); sendInfo(player);
return true; return true;
@@ -49,23 +75,14 @@ public class NexusLobbyCommand implements CommandExecutor {
switch (args[0].toLowerCase()) { switch (args[0].toLowerCase()) {
case "reload": case "reload":
if (!player.hasPermission("nexuslobby.admin")) { if (!player.hasPermission("nexuslobby.admin")) return noPerm(player);
player.sendMessage("§cKeine Berechtigung.");
return true;
}
// Aufruf der Reload-Methode in der Hauptklasse
NexusLobby.getInstance().reloadPlugin(); NexusLobby.getInstance().reloadPlugin();
player.sendMessage("§8[§6Nexus§8] §aPlugin erfolgreich neu geladen!"); player.sendMessage("§8[§6Nexus§8] §aPlugin erfolgreich neu geladen!");
player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 1f, 1.5f); player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 1f, 1.5f);
break; break;
case "setspawn": case "setspawn":
if (!player.hasPermission("nexuslobby.admin")) { if (!player.hasPermission("nexuslobby.admin")) return noPerm(player);
player.sendMessage("§cKeine Berechtigung.");
return true;
}
Location loc = player.getLocation(); Location loc = player.getLocation();
FileConfiguration config = NexusLobby.getInstance().getConfig(); FileConfiguration config = NexusLobby.getInstance().getConfig();
config.set("spawn.world", loc.getWorld().getName()); config.set("spawn.world", loc.getWorld().getName());
@@ -79,10 +96,7 @@ public class NexusLobbyCommand implements CommandExecutor {
break; break;
case "silentjoin": case "silentjoin":
if (!player.hasPermission("nexuslobby.silentjoin")) { if (!player.hasPermission("nexuslobby.silentjoin")) return noPerm(player);
player.sendMessage("§cKeine Berechtigung.");
return true;
}
if (NexusLobby.getInstance().getSilentPlayers().contains(player.getUniqueId())) { if (NexusLobby.getInstance().getSilentPlayers().contains(player.getUniqueId())) {
NexusLobby.getInstance().getSilentPlayers().remove(player.getUniqueId()); NexusLobby.getInstance().getSilentPlayers().remove(player.getUniqueId());
player.sendMessage("§8[§6Nexus§8] §7Silent Join: §cDeaktiviert"); player.sendMessage("§8[§6Nexus§8] §7Silent Join: §cDeaktiviert");
@@ -96,6 +110,45 @@ public class NexusLobbyCommand implements CommandExecutor {
handleScoreboard(player, args); handleScoreboard(player, args);
break; break;
case "parkour":
if (args.length < 2) {
player.sendMessage("§8[§6Nexus§8] §7Nutze: §e/nexus parkour <setstart|setfinish|setcheckpoint|reset|clear|removeall>");
return true;
}
String sub = args[1].toLowerCase();
if (!player.hasPermission("nexuslobby.admin") && !sub.equals("reset")) return noPerm(player);
switch (sub) {
case "setstart":
handleSetStart(player, pm);
break;
case "setfinish":
pm.setFinishLocation(player.getLocation());
player.sendMessage("§8[§6Nexus§8] §aParkour-Zielpunkt gesetzt!");
break;
case "setcheckpoint":
pm.setCheckpoint(player, player.getLocation());
break;
case "reset":
pm.stopParkour(player);
player.sendMessage("§8[§6Nexus§8] §7Dein aktueller Lauf wurde abgebrochen.");
break;
case "clear":
pm.clearStats();
player.sendMessage("§8[§6Nexus§8] §aAlle Parkour-Bestzeiten wurden gelöscht!");
break;
case "removeall":
pm.removeAllPoints();
player.sendMessage("§8[§6Nexus§8] §cDie gesamte Strecke (Checkpoints & Ziel) wurde gelöscht!");
player.playSound(player.getLocation(), Sound.ENTITY_ITEM_BREAK, 1f, 1f);
break;
default:
player.sendMessage("§cUnbekannter Unterbefehl.");
break;
}
break;
default: default:
sendInfo(player); sendInfo(player);
break; break;
@@ -104,6 +157,34 @@ public class NexusLobbyCommand implements CommandExecutor {
return true; return true;
} }
private void handleSetStart(Player player, ParkourManager pm) {
// NPC Erkennung (Blickrichtung auf ArmorStand)
ArmorStand targetAs = null;
List<Entity> nearby = player.getNearbyEntities(4, 4, 4);
for (Entity e : nearby) {
if (e instanceof ArmorStand as) {
double dot = player.getLocation().getDirection().dot(as.getLocation().toVector().subtract(player.getLocation().toVector()).normalize());
if (dot > 0.9) {
targetAs = as;
break;
}
}
}
if (targetAs != null) {
targetAs.addScoreboardTag("parkour_npc");
player.sendMessage("§8[§6Nexus§8] §aArmorStand als Parkour-NPC markiert!");
}
pm.setStartLocation(player.getLocation());
player.sendMessage("§8[§6Nexus§8] §aParkour-Startpunkt an deiner Position gesetzt!");
}
private boolean noPerm(Player player) {
player.sendMessage("§cKeine Berechtigung.");
return true;
}
private void handleScoreboard(Player player, String[] args) { private void handleScoreboard(Player player, String[] args) {
if (args.length < 2) { if (args.length < 2) {
player.sendMessage("§cBenutzung: /nexus sb <on|off|admin|spieler>"); player.sendMessage("§cBenutzung: /nexus sb <on|off|admin|spieler>");
@@ -141,11 +222,12 @@ public class NexusLobbyCommand implements CommandExecutor {
player.sendMessage("§8§m--------------------------------------"); player.sendMessage("§8§m--------------------------------------");
player.sendMessage("§6§lNexusLobby §7- Informationen"); player.sendMessage("§6§lNexusLobby §7- Informationen");
player.sendMessage(""); player.sendMessage("");
player.sendMessage("§f/spawn §7- Zum Spawn teleportieren"); 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 setspawn §7- Spawn setzen"); player.sendMessage("§f/nexus setspawn §7- Spawn setzen");
player.sendMessage("§f/nexus silentjoin §7- Join-Nachricht umschalten");
player.sendMessage("§f/nexus sb <on|off> §7- Scoreboard"); player.sendMessage("§f/nexus sb <on|off> §7- Scoreboard");
player.sendMessage("§f/nexus reload §7- Konfiguration laden"); player.sendMessage("§f/nexus reload §7- Config laden");
player.sendMessage("§8§m--------------------------------------"); player.sendMessage("§8§m--------------------------------------");
} }
} }

View File

@@ -16,6 +16,8 @@ import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.util.EulerAngle; import org.bukkit.util.EulerAngle;
import java.util.UUID;
public class ASTListener implements Listener { public class ASTListener implements Listener {
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
@@ -35,7 +37,11 @@ public class ASTListener implements Listener {
return; return;
} }
// --- 2. FALL: NORMALER KLICK -> Befehle ausführen (Bungee, etc.) --- // --- 2. FALL: NORMALER KLICK -> Dialog manuell triggern ---
// Dies triggert das Gruppensystem im ConversationManager
checkAndTriggerDialog(as, p);
// --- 3. FALL: Befehle ausführen (Bungee, etc.) ---
for (String tag : as.getScoreboardTags()) { for (String tag : as.getScoreboardTags()) {
if (tag.startsWith("ascmd:")) { if (tag.startsWith("ascmd:")) {
String[] parts = tag.split(":"); String[] parts = tag.split(":");
@@ -44,7 +50,7 @@ public class ASTListener implements Listener {
String type = parts[3].toLowerCase(); String type = parts[3].toLowerCase();
String command = parts[4]; String command = parts[4];
event.setCancelled(true); // Verhindert z.B. dass man Items klaut event.setCancelled(true);
switch (type) { switch (type) {
case "bungee": case "bungee":
@@ -57,7 +63,38 @@ public class ASTListener implements Listener {
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), command.replace("%player%", p.getName())); Bukkit.dispatchCommand(Bukkit.getConsoleSender(), command.replace("%player%", p.getName()));
break; break;
} }
break; // Nur den ersten gefundenen Befehl ausführen break;
}
}
}
/**
* Prüft, ob der ArmorStand Dialog-Tags hat und startet das Gespräch über den Manager.
* Nutzt nun die Gruppen-Logik (z.B. owner_suche).
*/
private void checkAndTriggerDialog(ArmorStand as, Player p) {
String groupName = null;
String partnerUUIDString = null;
for (String tag : as.getScoreboardTags()) {
// conv_id enthält jetzt den Gruppennamen aus der conversations.yml
if (tag.startsWith("conv_id:")) groupName = tag.split(":")[1];
if (tag.startsWith("conv_partner:")) partnerUUIDString = tag.split(":")[1];
}
if (groupName != null && partnerUUIDString != null) {
try {
UUID partnerUUID = UUID.fromString(partnerUUIDString);
// Wir rufen playConversation auf. Der Manager entscheidet selbst
// anhand der Uhrzeit, ob er morgens, mittags oder nachts abspielt.
NexusLobby.getInstance().getConversationManager().playConversation(
as.getUniqueId(),
partnerUUID,
groupName
);
} catch (Exception ignored) {
// Falls die UUID oder Gruppe ungültig ist
} }
} }
} }
@@ -97,6 +134,7 @@ public class ASTListener implements Listener {
ArmorStandTool tool = ArmorStandTool.get(item); ArmorStandTool tool = ArmorStandTool.get(item);
if (tool != null) { if (tool != null) {
tool.execute(as, p); tool.execute(as, p);
// Menü aktualisieren, falls wir noch im selben Editor sind
if (p.getOpenInventory().getTitle().equals(title)) { if (p.getOpenInventory().getTitle().equals(title)) {
new ArmorStandGUI(as, p); new ArmorStandGUI(as, p);
} }

View File

@@ -15,11 +15,15 @@ import org.bukkit.util.EulerAngle;
import org.bukkit.util.RayTraceResult; import org.bukkit.util.RayTraceResult;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID; import java.util.UUID;
/** /**
* ArmorStandCmdExecutor - Erweiterte Steuerung für ArmorStand-Interaktionen. * ArmorStandCmdExecutor - Erweiterte Steuerung für ArmorStand-Interaktionen.
* Nutzt Raytracing für präzise Auswahl und permanentes Dialog-Linking sowie Status-Backup. * Nutzt Raytracing für präzise Auswahl und permanentes Dialog-Linking sowie Status-Backup.
* Erweitert um Unterstützung für Gruppen-Dialoge (bis zu 4 NPCs).
* * Update: Bedrock-Kompatibilität für Namen korrigiert.
*/ */
public class ArmorStandCmdExecutor implements CommandExecutor { public class ArmorStandCmdExecutor implements CommandExecutor {
@@ -45,18 +49,21 @@ public class ArmorStandCmdExecutor implements CommandExecutor {
switch (args[1].toLowerCase()) { switch (args[1].toLowerCase()) {
case "select1": case "select1":
case "select2": case "select2":
case "select3":
case "select4":
ArmorStand target = getTargetArmorStand(p); ArmorStand target = getTargetArmorStand(p);
if (target == null) { if (target == null) {
p.sendMessage(prefix + "§cDu musst einen ArmorStand direkt anschauen (Fadenkreuz)!"); p.sendMessage(prefix + "§cDu musst einen ArmorStand direkt anschauen (Fadenkreuz)!");
return true; return true;
} }
boolean isFirst = args[1].equalsIgnoreCase("select1"); // Dynamische Ermittlung des Slots (1, 2, 3 oder 4)
String metaKey = isFirst ? "conv_npc1" : "conv_npc2"; String slotStr = args[1].toLowerCase().replace("select", "");
String metaKey = "conv_npc" + slotStr;
UUID targetUUID = target.getUniqueId(); UUID targetUUID = target.getUniqueId();
p.setMetadata(metaKey, new FixedMetadataValue(NexusLobby.getInstance(), targetUUID.toString())); p.setMetadata(metaKey, new FixedMetadataValue(NexusLobby.getInstance(), targetUUID.toString()));
p.sendMessage(prefix + "§aNPC §e" + (isFirst ? "1" : "2") + " §amarkiert (§7" + targetUUID.toString().substring(0, 8) + "...§a)"); p.sendMessage(prefix + "§aNPC §e" + slotStr + " §amarkiert (§7" + targetUUID.toString().substring(0, 8) + "...§a)");
p.spawnParticle(Particle.WITCH, target.getLocation().add(0, 1.0, 0), 15, 0.2, 0.2, 0.2, 0.05); p.spawnParticle(Particle.WITCH, target.getLocation().add(0, 1.0, 0), 15, 0.2, 0.2, 0.2, 0.05);
break; break;
@@ -66,24 +73,43 @@ public class ArmorStandCmdExecutor implements CommandExecutor {
return true; return true;
} }
if (!p.hasMetadata("conv_npc1") || !p.hasMetadata("conv_npc2")) { if (!p.hasMetadata("conv_npc1") || !p.hasMetadata("conv_npc2")) {
p.sendMessage(prefix + "§cBitte markiere erst beide NPCs (select1 & select2)!"); p.sendMessage(prefix + "§cBitte markiere mindestens die ersten beiden NPCs (select1 & select2)!");
return true; return true;
} }
UUID id1 = UUID.fromString(p.getMetadata("conv_npc1").get(0).asString()); UUID id1 = UUID.fromString(p.getMetadata("conv_npc1").get(0).asString());
UUID id2 = UUID.fromString(p.getMetadata("conv_npc2").get(0).asString()); UUID id2 = UUID.fromString(p.getMetadata("conv_npc2").get(0).asString());
// Optionale Partner 3 und 4 abrufen falls vorhanden
UUID id3 = p.hasMetadata("conv_npc3") ? UUID.fromString(p.getMetadata("conv_npc3").get(0).asString()) : null;
UUID id4 = p.hasMetadata("conv_npc4") ? UUID.fromString(p.getMetadata("conv_npc4").get(0).asString()) : null;
String dialogId = args[2]; String dialogId = args[2];
Entity entity1 = Bukkit.getEntity(id1); Entity entity1 = Bukkit.getEntity(id1);
if (entity1 instanceof ArmorStand as1) { if (entity1 instanceof ArmorStand as1) {
as1.getScoreboardTags().removeIf(tag -> tag.startsWith("conv_partner:") || tag.startsWith("conv_id:")); // Vorhandene Tags säubern
as1.getScoreboardTags().removeIf(tag -> tag.startsWith("conv_partner") || tag.startsWith("conv_id:"));
// Tags für Partner setzen
as1.addScoreboardTag("conv_partner:" + id2.toString()); as1.addScoreboardTag("conv_partner:" + id2.toString());
if (id3 != null) as1.addScoreboardTag("conv_partner2:" + id3.toString());
if (id4 != null) as1.addScoreboardTag("conv_partner3:" + id4.toString());
as1.addScoreboardTag("conv_id:" + dialogId); as1.addScoreboardTag("conv_id:" + dialogId);
NexusLobby.getInstance().getConversationManager().saveLink(id1, id2, dialogId); // Im Manager speichern (Nutzt die erweiterte Methode für Gruppen)
NexusLobby.getInstance().getConversationManager().saveLinkExtended(id1, id2, id3, id4, dialogId);
p.sendMessage(prefix + "§a§lDauerhafte Verknüpfung erstellt!"); p.sendMessage(prefix + "§a§lDauerhafte Verknüpfung erstellt!");
p.sendMessage(prefix + "§7Beteiligte NPCs: §e" + (id3 == null ? "2" : (id4 == null ? "3" : "4")));
p.spawnParticle(Particle.HAPPY_VILLAGER, as1.getLocation().add(0, 1.5, 0), 20, 0.4, 0.4, 0.4, 0.1); p.spawnParticle(Particle.HAPPY_VILLAGER, as1.getLocation().add(0, 1.5, 0), 20, 0.4, 0.4, 0.4, 0.1);
// Metadaten nach dem Linken aufräumen
p.removeMetadata("conv_npc1", NexusLobby.getInstance());
p.removeMetadata("conv_npc2", NexusLobby.getInstance());
p.removeMetadata("conv_npc3", NexusLobby.getInstance());
p.removeMetadata("conv_npc4", NexusLobby.getInstance());
} else { } else {
p.sendMessage(prefix + "§cFehler: Sprecher 1 nicht gefunden."); p.sendMessage(prefix + "§cFehler: Sprecher 1 nicht gefunden.");
} }
@@ -96,8 +122,8 @@ public class ArmorStandCmdExecutor implements CommandExecutor {
return true; return true;
} }
// Ingame Tags entfernen // Ingame Tags entfernen (alle conv_ Tags)
targetUnlink.getScoreboardTags().removeIf(tag -> tag.startsWith("conv_partner:") || tag.startsWith("conv_id:")); targetUnlink.getScoreboardTags().removeIf(tag -> tag.startsWith("conv_"));
// Aus Konfiguration löschen // Aus Konfiguration löschen
NexusLobby.getInstance().getConversationManager().removeLink(targetUnlink.getUniqueId()); NexusLobby.getInstance().getConversationManager().removeLink(targetUnlink.getUniqueId());
@@ -112,12 +138,19 @@ public class ArmorStandCmdExecutor implements CommandExecutor {
return true; return true;
} }
if (!p.hasMetadata("conv_npc1") || !p.hasMetadata("conv_npc2")) { if (!p.hasMetadata("conv_npc1") || !p.hasMetadata("conv_npc2")) {
p.sendMessage(prefix + "§cBitte markiere erst beide NPCs!"); p.sendMessage(prefix + "§cBitte markiere mindestens zwei NPCs!");
return true; return true;
} }
UUID s1 = UUID.fromString(p.getMetadata("conv_npc1").get(0).asString());
UUID s2 = UUID.fromString(p.getMetadata("conv_npc2").get(0).asString()); // Liste der Teilnehmer für den Startbefehl erstellen
NexusLobby.getInstance().getConversationManager().playConversation(s1, s2, args[2]); List<UUID> participants = new ArrayList<>();
participants.add(UUID.fromString(p.getMetadata("conv_npc1").get(0).asString()));
participants.add(UUID.fromString(p.getMetadata("conv_npc2").get(0).asString()));
if (p.hasMetadata("conv_npc3")) participants.add(UUID.fromString(p.getMetadata("conv_npc3").get(0).asString()));
if (p.hasMetadata("conv_npc4")) participants.add(UUID.fromString(p.getMetadata("conv_npc4").get(0).asString()));
// Gespräch über die Gruppen-Methode starten
NexusLobby.getInstance().getConversationManager().playConversationGroup(participants, args[2]);
break; break;
default: default:
@@ -126,9 +159,22 @@ public class ArmorStandCmdExecutor implements CommandExecutor {
return true; return true;
} }
// --- STANDARD TOOLS (LOOKAT / NAME / ADD) --- // --- STANDARD TOOLS (LOOKAT / NAME / ADD / SAY) ---
ArmorStand target = getTargetArmorStand(p); ArmorStand target = getTargetArmorStand(p);
if (args[0].equalsIgnoreCase("say") && args.length >= 2) {
if (target == null) { p.sendMessage(prefix + "§cSchau einen ArmorStand an!"); return true; }
String text = buildString(args, 1);
String colored = ChatColor.translateAlternateColorCodes('&', text);
// Nutzt die showBubble-Logik aus dem ConversationManager (Ohne Partner-Zwang)
NexusLobby.getInstance().getConversationManager().showBubble(target, colored);
p.sendMessage(prefix + "§aNPC-Sprechblase gesendet.");
return true;
}
if (args[0].equalsIgnoreCase("lookat")) { if (args[0].equalsIgnoreCase("lookat")) {
if (target == null) { p.sendMessage(prefix + "§cSchau einen ArmorStand an!"); return true; } if (target == null) { p.sendMessage(prefix + "§cSchau einen ArmorStand an!"); return true; }
if (target.getScoreboardTags().contains("as_lookat")) { if (target.getScoreboardTags().contains("as_lookat")) {
@@ -146,18 +192,21 @@ public class ArmorStandCmdExecutor implements CommandExecutor {
if (target == null) { p.sendMessage(prefix + "§cSchau einen ArmorStand an!"); return true; } if (target == null) { p.sendMessage(prefix + "§cSchau einen ArmorStand an!"); return true; }
String nameInput = buildString(args, 1); String nameInput = buildString(args, 1);
// Wichtig: Alle alten Namens-Tags entfernen für Konsistenz
target.getScoreboardTags().removeIf(tag -> tag.startsWith("asname:") || tag.startsWith("as_displayname:"));
if (nameInput.equalsIgnoreCase("none")) { if (nameInput.equalsIgnoreCase("none")) {
target.setCustomName(""); target.setCustomName("");
target.setCustomNameVisible(false); target.setCustomNameVisible(false);
target.getScoreboardTags().removeIf(tag -> tag.startsWith("asname:"));
p.sendMessage(prefix + "§eName entfernt."); p.sendMessage(prefix + "§eName entfernt.");
} else { } else {
String colored = ChatColor.translateAlternateColorCodes('&', nameInput); String colored = ChatColor.translateAlternateColorCodes('&', nameInput);
target.setCustomName(colored); target.setCustomName(colored);
target.setCustomNameVisible(true); target.setCustomNameVisible(true);
target.getScoreboardTags().removeIf(tag -> tag.startsWith("asname:")); // Wir speichern es unter as_displayname, damit das StatusModule es findet
target.addScoreboardTag("asname:" + nameInput); // ":" wird durch "§§" ersetzt, um Probleme in Scoreboard-Tags zu vermeiden
target.addScoreboardTag("as_displayname:" + nameInput.replace(":", "§§"));
p.sendMessage(prefix + "§7Name gesetzt: " + colored); p.sendMessage(prefix + "§7Name gesetzt: " + colored);
p.sendMessage(prefix + "§8(Status-Backup wurde gespeichert)"); p.sendMessage(prefix + "§8(Status-Backup wurde gespeichert)");
@@ -218,8 +267,7 @@ public class ArmorStandCmdExecutor implements CommandExecutor {
private boolean sendConvHelp(Player p) { private boolean sendConvHelp(Player p) {
p.sendMessage(" "); p.sendMessage(" ");
p.sendMessage("§6§lConversation Setup:"); p.sendMessage("§6§lConversation Setup:");
p.sendMessage("§e/nexuscmd conv select1 §7- Sprecher 1"); p.sendMessage("§e/nexuscmd conv select1-4 §7- NPCs markieren");
p.sendMessage("§e/nexuscmd conv select2 §7- Sprecher 2");
p.sendMessage("§e/nexuscmd conv link <ID> §7- Speichern"); p.sendMessage("§e/nexuscmd conv link <ID> §7- Speichern");
p.sendMessage("§e/nexuscmd conv unlink §7- Verknüpfung lösen"); p.sendMessage("§e/nexuscmd conv unlink §7- Verknüpfung lösen");
p.sendMessage("§e/nexuscmd conv start <ID> §7- Testen"); p.sendMessage("§e/nexuscmd conv start <ID> §7- Testen");
@@ -229,7 +277,8 @@ public class ArmorStandCmdExecutor implements CommandExecutor {
private boolean sendHelp(Player p) { private boolean sendHelp(Player p) {
p.sendMessage("§6§lNexus Tools Hilfe:"); p.sendMessage("§6§lNexus Tools Hilfe:");
p.sendMessage("§e/nexuscmd name <Text> §7- Setzt Namen & Status-Backup"); p.sendMessage("§e/nexuscmd name <Text> §7- Setzt Namen (Bedrock-Safe)");
p.sendMessage("§e/nexuscmd say <Text> §7- Erstellt eine Sprechblase");
p.sendMessage("§e/nexuscmd lookat §7- Blickkontakt umschalten"); p.sendMessage("§e/nexuscmd lookat §7- Blickkontakt umschalten");
p.sendMessage("§e/nexuscmd add <s1> <s2> bungee <Server> §7- Bungee-Bindung"); p.sendMessage("§e/nexuscmd add <s1> <s2> bungee <Server> §7- Bungee-Bindung");
p.sendMessage("§e/nexuscmd conv §7- Gesprächs-Menü"); p.sendMessage("§e/nexuscmd conv §7- Gesprächs-Menü");

View File

@@ -9,8 +9,24 @@ import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.World; import org.bukkit.World;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* ArmorStandStatusModule - Optimierte Statusanzeige für Java & Bedrock.
* - Schaltet zwischen Servername und "Offline" um.
* - Anti-Blink-Logik für Java-Spieler.
* - Force-Refresh-Logik für Bedrock-Spieler (Geyser).
*/
public class ArmorStandStatusModule implements Module { public class ArmorStandStatusModule implements Module {
private final Map<UUID, Boolean> lastStatus = new HashMap<>();
private final Map<String, Integer> failCount = new HashMap<>();
// Zähler für den Force-Refresh (Bedrock-Sicherheit)
private int refreshTicks = 0;
@Override @Override
public String getName() { public String getName() {
return "ArmorStandStatus"; return "ArmorStandStatus";
@@ -18,53 +34,92 @@ public class ArmorStandStatusModule implements Module {
@Override @Override
public void onEnable() { public void onEnable() {
// Alle 10 Sekunden prüfen // Alle 10 Sekunden (200 Ticks)
Bukkit.getScheduler().runTaskTimer(NexusLobby.getInstance(), this::updateAllServerArmorStands, 100L, 200L); Bukkit.getScheduler().runTaskTimer(NexusLobby.getInstance(), () -> {
refreshTicks++;
updateAllServerArmorStands();
}, 100L, 200L);
} }
@Override @Override
public void onDisable() {} public void onDisable() {
lastStatus.clear();
failCount.clear();
}
private void updateAllServerArmorStands() { private void updateAllServerArmorStands() {
// Alle 6 Durchgänge (ca. jede Minute) erzwingen wir ein Update für Bedrock
boolean forceRefresh = (refreshTicks >= 6);
if (forceRefresh) refreshTicks = 0;
for (World world : Bukkit.getWorlds()) { for (World world : Bukkit.getWorlds()) {
for (Entity entity : world.getEntitiesByClass(ArmorStand.class)) { for (Entity entity : world.getEntitiesByClass(ArmorStand.class)) {
if (entity instanceof ArmorStand as) { if (entity instanceof ArmorStand as) {
checkAndRefreshStatus(as); checkAndRefreshStatus(as, forceRefresh);
} }
} }
} }
} }
private void checkAndRefreshStatus(ArmorStand as) { private void checkAndRefreshStatus(ArmorStand as, boolean forceRefresh) {
String bungeeTag = null; String serverName = null;
for (String tag : as.getScoreboardTags()) { for (String tag : as.getScoreboardTags()) {
if (tag.startsWith("ascmd:bungee:")) { if (tag.startsWith("ascmd:")) {
bungeeTag = tag.replace("ascmd:bungee:", ""); if (tag.contains(":bungee:")) {
break; String[] parts = tag.split(":");
if (parts.length >= 5) {
serverName = parts[4].toLowerCase();
} else if (tag.startsWith("ascmd:bungee:")) {
serverName = tag.replace("ascmd:bungee:", "").toLowerCase();
}
}
if (serverName != null) break;
} }
} }
if (bungeeTag == null) return; if (serverName == null) return;
String serverName = bungeeTag.toLowerCase(); final String finalServerName = serverName;
String ip = NexusLobby.getInstance().getConfig().getString("servers." + serverName + ".ip", "127.0.0.1"); String ip = NexusLobby.getInstance().getConfig().getString("servers." + serverName + ".ip", "127.0.0.1");
int port = NexusLobby.getInstance().getConfig().getInt("servers." + serverName + ".port", 25565); int port = NexusLobby.getInstance().getConfig().getInt("servers." + serverName + ".port", 25565);
ServerChecker.isOnline(ip, port).thenAccept(isOnline -> { ServerChecker.isOnline(ip, port).thenAccept(isOnline -> {
Bukkit.getScheduler().runTask(NexusLobby.getInstance(), () -> { Bukkit.getScheduler().runTask(NexusLobby.getInstance(), () -> {
// Toleranz-Logik gegen kurzes Flackern
if (isOnline) {
failCount.put(finalServerName, 0);
} else {
int fails = failCount.getOrDefault(finalServerName, 0) + 1;
failCount.put(finalServerName, fails);
if (fails < 2) return;
}
String originalDisplayName = getOriginalName(as); String originalDisplayName = getOriginalName(as);
if (originalDisplayName == null) return; if (originalDisplayName == null) return;
String translatedName = ChatColor.translateAlternateColorCodes('&', originalDisplayName); // Status-Check: Hat sich etwas geändert?
Boolean lastKnown = lastStatus.get(as.getUniqueId());
// Nur wenn Status neu ODER Force-Refresh (für Bedrock) aktiv ist
if (!forceRefresh && lastKnown != null && lastKnown == isOnline) {
return;
}
// Namen setzen
if (isOnline) { if (isOnline) {
// Zeigt nur den normalen Namen an, wenn online String translatedName = ChatColor.translateAlternateColorCodes('&', originalDisplayName);
as.setCustomName(translatedName); as.setCustomName(translatedName);
} else { } else {
// Zeigt den Namen an und darunter (getrennt durch Leerzeichen/Format) den Status as.setCustomName("§cOffline");
// Da Minecraft Namen meist einzeilig sind, nutzen wir eine klare farbliche Trennung
as.setCustomName(translatedName + " §7- §cOffline");
} }
// Bedrock-Fix: Sichtbarkeit explizit triggern
as.setCustomNameVisible(true);
// Status speichern
lastStatus.put(as.getUniqueId(), isOnline);
}); });
}); });
} }

View File

@@ -1,9 +1,8 @@
package de.nexuslobby.modules.armorstandtools; package de.nexuslobby.modules.armorstandtools;
import de.nexuslobby.NexusLobby; import de.nexuslobby.NexusLobby;
import org.bukkit.Bukkit; import org.bukkit.*;
import org.bukkit.Location; import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.Sound;
import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.ArmorStand; import org.bukkit.entity.ArmorStand;
@@ -13,10 +12,8 @@ import org.bukkit.scheduler.BukkitRunnable;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.*;
import java.util.Arrays; import java.util.stream.Collectors;
import java.util.List;
import java.util.UUID;
public class ConversationManager { public class ConversationManager {
@@ -24,88 +21,145 @@ public class ConversationManager {
private File file; private File file;
private FileConfiguration config; private FileConfiguration config;
// Verhindert Mehrfach-Gespräche und regelt die 60s Pause
private final Set<UUID> activeSpeakers = new HashSet<>();
public ConversationManager(NexusLobby plugin) { public ConversationManager(NexusLobby plugin) {
this.plugin = plugin; this.plugin = plugin;
setupFile(); setupFile();
clearHangingBubbles();
}
/**
* Gibt alle verfügbaren IDs zurück.
*/
public List<String> getConversationIds() {
if (config == null || !config.contains("conversations")) {
return new ArrayList<>();
}
ConfigurationSection section = config.getConfigurationSection("conversations");
if (section == null) return new ArrayList<>();
return new ArrayList<>(section.getKeys(false));
}
/**
* Entfernt alle verwaisten Sprechblasen in allen Welten.
*/
public void clearHangingBubbles() {
int count = 0;
for (World world : Bukkit.getWorlds()) {
for (ArmorStand as : world.getEntitiesByClass(ArmorStand.class)) {
if (as.getScoreboardTags().contains("nexus_bubble") ||
(!as.isVisible() && as.isMarker() && as.getCustomName() != null)) {
as.remove();
count++;
}
}
}
if (count > 0) {
Bukkit.getLogger().info("[NexusLobby] Cleanup: " + count + " verwaiste Sprechblasen entfernt.");
}
} }
public void setupFile() { public void setupFile() {
this.file = new File(plugin.getDataFolder(), "conversations.yml"); this.file = new File(plugin.getDataFolder(), "conversations.yml");
if (!file.exists()) { if (!file.exists()) {
try {
if (!plugin.getDataFolder().exists()) {
plugin.getDataFolder().mkdirs();
}
plugin.saveResource("conversations.yml", false); plugin.saveResource("conversations.yml", false);
} catch (Exception e) {
try {
file.createNewFile();
YamlConfiguration defaultConfig = YamlConfiguration.loadConfiguration(file);
defaultConfig.set("conversations.test.dialogue", Arrays.asList(
"&eNPC 1: &7Hallo!",
"&aNPC 2: &7Hi, wie geht es dir?",
"&eNPC 1: &7Bestens, danke!"
));
defaultConfig.save(file);
} catch (IOException ex) {
ex.printStackTrace();
}
}
} }
this.config = YamlConfiguration.loadConfiguration(file); this.config = YamlConfiguration.loadConfiguration(file);
} }
public void saveLink(UUID id1, UUID id2, String dialogId) { /**
config.set("links." + id1.toString() + ".partner", id2.toString()); * Ermittelt die Tageszeit für die YAML-Pfade.
config.set("links." + id1.toString() + ".dialog", dialogId); // WICHTIG: ".dialog" */
saveConfig(); private String getDaytimeSuffix() {
if (Bukkit.getWorlds().isEmpty()) return "mittags";
World world = Bukkit.getWorlds().get(0);
long time = world.getTime();
if (time >= 0 && time < 12000) return "morgens";
if (time >= 12000 && time < 13000) return "abends";
if (time >= 13000 && time < 23000) return "nacht";
return "mittags";
} }
public void removeLink(UUID id) { /**
if (config.contains("links." + id.toString())) { * Pool-Logik für zufällige Dialog-Varianten oder zeitbasierte IDs.
config.set("links." + id.toString(), null); */
saveConfig(); private String resolveDialogKey(String key) {
} ConfigurationSection section = config.getConfigurationSection("conversations");
if (section == null) return key;
String daytimeKey = key + "_" + getDaytimeSuffix();
if (section.contains(daytimeKey)) return daytimeKey;
List<String> pool = section.getKeys(false).stream()
.filter(s -> s.startsWith(key))
.collect(Collectors.toList());
if (pool.isEmpty()) return key;
return pool.get(new Random().nextInt(pool.size()));
} }
/**
* Automatische Prüfung für NPCs in Spieler-Nähe (Unterstützt bis zu 4 Partner).
*/
public void startAllAutomatedConversations() { public void startAllAutomatedConversations() {
if (config.getConfigurationSection("links") == null) return; ConfigurationSection links = config.getConfigurationSection("links");
if (links == null) return;
for (String npc1String : config.getConfigurationSection("links").getKeys(false)) { for (String npc1String : links.getKeys(false)) {
try { try {
UUID id1 = UUID.fromString(npc1String); UUID id1 = UUID.fromString(npc1String);
UUID id2 = UUID.fromString(config.getString("links." + npc1String + ".partner")); if (activeSpeakers.contains(id1)) continue;
String dialogId = config.getString("links." + npc1String + ".dialog"); // WICHTIG: ".dialog"
Entity e1 = Bukkit.getEntity(id1); ConfigurationSection npcLink = links.getConfigurationSection(npc1String);
Entity e2 = Bukkit.getEntity(id2); if (npcLink == null) continue;
if (e1 instanceof ArmorStand as1 && e2 instanceof ArmorStand as2) { String dialogId = npcLink.getString("dialog");
List<UUID> partners = new ArrayList<>();
partners.add(id1); // Der Starter ist immer Teilnehmer 0
// Partner 1 bis 3 einsammeln (Max 4 Teilnehmer insgesamt)
if (npcLink.contains("partner")) partners.add(UUID.fromString(npcLink.getString("partner")));
if (npcLink.contains("partner2")) partners.add(UUID.fromString(npcLink.getString("partner2")));
if (npcLink.contains("partner3")) partners.add(UUID.fromString(npcLink.getString("partner3")));
Entity starter = Bukkit.getEntity(id1);
if (starter instanceof ArmorStand as1) {
if (as1.getNearbyEntities(15, 15, 15).stream().anyMatch(e -> e instanceof Player)) { if (as1.getNearbyEntities(15, 15, 15).stream().anyMatch(e -> e instanceof Player)) {
playConversation(id1, id2, dialogId); String finalDialogId = resolveDialogKey(dialogId);
playConversationGroup(partners, finalDialogId);
} }
} }
} catch (Exception ignored) {} } catch (Exception ignored) {}
} }
} }
public List<String> getConversationIds() { /**
if (config == null || !config.contains("conversations")) { * Neue Gruppen-Logik: Spielt Dialoge für 2, 3 oder 4 Teilnehmer ab.
return new ArrayList<>(); */
public void playConversationGroup(List<UUID> participants, String key) {
String daytime = getDaytimeSuffix();
// Pfad-Ermittlung
String path = "conversations." + key + "." + daytime;
if (!config.contains(path + ".dialogue")) {
path = "conversations." + key + ".mittags";
} }
return new ArrayList<>(config.getConfigurationSection("conversations").getKeys(false)); if (!config.contains(path + ".dialogue")) {
path = "conversations." + key;
} }
public void playConversation(UUID id1, UUID id2, String key) { if (!config.contains(path + ".dialogue")) return;
// Sicherstellen, dass wir auf "conversations.KEY.dialogue" prüfen
if (config == null || !config.contains("conversations." + key)) {
Bukkit.getLogger().warning("[NexusLobby] Dialog-ID '" + key + "' nicht in conversations.yml gefunden!");
return;
}
List<String> lines = config.getStringList("conversations." + key + ".dialogue"); List<String> lines = config.getStringList(path + ".dialogue");
if (lines == null || lines.isEmpty()) return; if (lines.isEmpty()) return;
// Alle Teilnehmer blockieren
for (UUID id : participants) activeSpeakers.add(id);
new BukkitRunnable() { new BukkitRunnable() {
int step = 0; int step = 0;
@@ -113,28 +167,57 @@ public class ConversationManager {
@Override @Override
public void run() { public void run() {
if (step >= lines.size()) { if (step >= lines.size()) {
// 60 SEKUNDEN COOLDOWN
Bukkit.getScheduler().runTaskLater(plugin, () -> {
for (UUID id : participants) activeSpeakers.remove(id);
}, 1200L);
this.cancel(); this.cancel();
return; return;
} }
UUID speakerUUID = (step % 2 == 0) ? id1 : id2; // Rotations-Logik: Teilnehmer 0 -> 1 -> 2 -> 3 -> 0 ...
UUID speakerUUID = participants.get(step % participants.size());
Entity entity = Bukkit.getEntity(speakerUUID); Entity entity = Bukkit.getEntity(speakerUUID);
if (entity instanceof ArmorStand as) { if (entity instanceof ArmorStand as) {
as.getWorld().playSound(as.getLocation(), Sound.ENTITY_CHICKEN_EGG, 0.5f, 1.5f); playDynamicSound(as);
showBubble(as, lines.get(step)); showBubble(as, lines.get(step));
} else { } else {
for (UUID id : participants) activeSpeakers.remove(id);
this.cancel(); this.cancel();
return; return;
} }
step++; step++;
} }
}.runTaskTimer(plugin, 0L, 70L); }.runTaskTimer(plugin, 0L, 90L); // 4.5s Intervall
} }
private void showBubble(ArmorStand as, String text) { /**
Location loc = as.getEyeLocation().add(0, 0.6, 0); * Spielt einen Sound ab, dessen Pitch (Tonhöhe) zum Namen des ArmorStands passt.
*/
private void playDynamicSound(ArmorStand as) {
float pitch = 1.0f;
String name = as.getCustomName() != null ? as.getCustomName() : "";
if (name.contains("Timmy") || name.contains("Mia") || name.contains("Lotte")) {
pitch = 1.5f + (new Random().nextFloat() * 0.3f); // Hoch (Kind/Frau)
} else if (name.contains("Schmidt") || name.contains("Vater") || name.contains("Trainer")) {
pitch = 0.6f + (new Random().nextFloat() * 0.2f); // Tief (Mann)
} else {
pitch = 0.9f + (new Random().nextFloat() * 0.4f); // Neutral
}
as.getWorld().playSound(as.getLocation(), Sound.ENTITY_VILLAGER_AMBIENT, 0.5f, pitch);
}
/**
* Erzeugt die Sprechblase und einen visuellen Effekt (Partikel).
*/
public void showBubble(ArmorStand as, String text) {
Location loc = as.getEyeLocation().add(0, 0.8, 0);
// Kleiner Partikel-Effekt ("Sprechwolke")
as.getWorld().spawnParticle(Particle.CLOUD, loc, 3, 0.1, 0.1, 0.1, 0.02);
ArmorStand bubble = as.getWorld().spawn(loc, ArmorStand.class, s -> { ArmorStand bubble = as.getWorld().spawn(loc, ArmorStand.class, s -> {
s.setMarker(true); s.setMarker(true);
@@ -143,24 +226,51 @@ public class ConversationManager {
s.setCustomName(ChatColorTranslate(text)); s.setCustomName(ChatColorTranslate(text));
s.setCustomNameVisible(true); s.setCustomNameVisible(true);
s.setInvulnerable(true); s.setInvulnerable(true);
s.addScoreboardTag("nexus_bubble");
}); });
Bukkit.getScheduler().runTaskLater(plugin, bubble::remove, 60L); Bukkit.getScheduler().runTaskLater(plugin, bubble::remove, 80L); // 4s Sichtbarkeit
}
// --- Legacy Support & Config Methoden ---
public void playConversation(UUID id1, UUID id2, String key) {
playConversationGroup(Arrays.asList(id1, id2), key);
}
/**
* Neue erweiterte Speicher-Methode für bis zu 4 Partner.
*/
public void saveLinkExtended(UUID id1, UUID id2, UUID id3, UUID id4, String dialogId) {
String path = "links." + id1.toString();
config.set(path + ".dialog", dialogId);
config.set(path + ".partner", id2.toString());
if (id3 != null) config.set(path + ".partner2", id3.toString());
if (id4 != null) config.set(path + ".partner3", id4.toString());
saveConfig();
}
public void saveLink(UUID id1, UUID id2, String dialogId) {
saveLinkExtended(id1, id2, null, null, dialogId);
}
public void removeLink(UUID id) {
config.set("links." + id.toString(), null);
saveConfig();
} }
private String ChatColorTranslate(String text) { private String ChatColorTranslate(String text) {
if (text == null) return "";
return text.replace("&", "§"); return text.replace("&", "§");
} }
private void saveConfig() { private void saveConfig() {
try { try { config.save(file); } catch (IOException e) { e.printStackTrace(); }
config.save(file);
} catch (IOException e) {
e.printStackTrace();
}
} }
public void reload() { public void reload() {
this.config = YamlConfiguration.loadConfiguration(file); setupFile();
activeSpeakers.clear();
clearHangingBubbles();
} }
} }

View File

@@ -0,0 +1,97 @@
package de.nexuslobby.modules.gadgets;
import de.nexuslobby.NexusLobby;
import org.bukkit.Location;
import org.bukkit.Particle;
import org.bukkit.Sound;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
public class FreezeRay {
// Auf public gesetzt, damit das GadgetModule im PlayerMoveEvent darauf prüfen kann
public static final Set<UUID> frozenPlayers = new HashSet<>();
public static void shoot(Player shooter) {
Location start = shooter.getEyeLocation();
Vector direction = start.getDirection();
// Sound beim Schießen
shooter.getWorld().playSound(start, Sound.ENTITY_SNOW_GOLEM_SHOOT, 1.0f, 1.5f);
// Wir prüfen in 0.3er Schritten bis zu 15 Blöcke weit
for (double d = 0; d < 15; d += 0.3) {
Location point = start.clone().add(direction.clone().multiply(d));
// Partikel-Strahl sichtbar machen
point.getWorld().spawnParticle(Particle.SNOWFLAKE, point, 1, 0, 0, 0, 0);
// Prüfung auf Spieler im Umkreis von 0.8 Blöcken (etwas großzügiger)
for (Entity entity : point.getWorld().getNearbyEntities(point, 0.8, 0.8, 0.8)) {
if (entity instanceof Player target && target != shooter) {
applyFreeze(target);
return; // Stoppt den Strahl beim ersten Treffer
}
}
// Stoppe den Strahl, falls er eine Wand trifft
if (point.getBlock().getType().isSolid()) {
break;
}
}
}
private static void applyFreeze(Player target) {
if (frozenPlayers.contains(target.getUniqueId())) return;
frozenPlayers.add(target.getUniqueId());
// Fixiere die Position für den Stasis-Effekt
final Location freezeLocation = target.getLocation();
// Feedback für getroffenen Spieler
target.sendMessage("§8[§6Nexus§8] §bDu wurdest eingefroren!");
target.getWorld().playSound(target.getLocation(), Sound.BLOCK_GLASS_BREAK, 1.0f, 0.5f);
new org.bukkit.scheduler.BukkitRunnable() {
int ticks = 0;
@Override
public void run() {
// Sicherheitscheck: Ist der Spieler noch online?
if (!target.isOnline() || ticks >= 60) {
frozenPlayers.remove(target.getUniqueId());
this.cancel();
return;
}
// Stasis-Effekt: Bewegung auf 0 setzen und Position fixieren
target.setVelocity(new Vector(0, 0, 0));
// Alle 2 Ticks zurückteleportieren, falls er versucht zu laufen
// (Behält die Blickrichtung des Spielers bei)
Location current = target.getLocation();
if (current.getX() != freezeLocation.getX() || current.getZ() != freezeLocation.getZ()) {
freezeLocation.setYaw(current.getYaw());
freezeLocation.setPitch(current.getPitch());
target.teleport(freezeLocation);
}
// Optischer Käfig (Ring-Effekt)
Location loc = target.getLocation();
for (int i = 0; i < 8; i++) {
double angle = i * Math.PI / 4;
double x = Math.cos(angle) * 0.7;
double z = Math.sin(angle) * 0.7;
loc.getWorld().spawnParticle(Particle.SNOWFLAKE, loc.clone().add(x, 1, z), 1, 0, 0, 0, 0);
loc.getWorld().spawnParticle(Particle.SNOWFLAKE, loc.clone().add(x, 0.2, z), 1, 0, 0, 0, 0);
}
ticks += 2;
}
}.runTaskTimer(NexusLobby.getInstance(), 0L, 2L);
}
}

View File

@@ -9,8 +9,11 @@ import org.bukkit.Sound;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.player.PlayerFishEvent; 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.Inventory;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.ItemMeta;
@@ -56,6 +59,37 @@ public class GadgetModule implements Module, Listener {
}, 1L, 1L); }, 1L, 1L);
} }
// Event für die Benutzung der aktiven Gadgets
@EventHandler
public void onInteract(PlayerInteractEvent event) {
ItemStack item = event.getItem();
if (item == null || !item.hasItemMeta()) return;
String name = item.getItemMeta().getDisplayName();
if (event.getAction() == Action.RIGHT_CLICK_AIR || event.getAction() == Action.RIGHT_CLICK_BLOCK) {
if (name.equals("§b§lFreeze-Ray")) {
FreezeRay.shoot(event.getPlayer());
event.setCancelled(true);
} else if (name.equals("§6§lPaintball-Gun")) {
PaintballGun.shoot(event.getPlayer());
event.setCancelled(true);
} else if (name.equals("§c§lMeteorit")) {
MeteorStrike.launch(event.getPlayer());
event.setCancelled(true);
}
}
}
// Verhindert Bewegung für eingefrorene Spieler (Stasis)
@EventHandler
public void onPlayerMove(PlayerMoveEvent event) {
if (FreezeRay.frozenPlayers.contains(event.getPlayer().getUniqueId())) {
if (event.getFrom().getX() != event.getTo().getX() || event.getFrom().getZ() != event.getTo().getZ()) {
event.setTo(event.getFrom().setDirection(event.getTo().getDirection()));
}
}
}
private void handleSpecialHatEffects(Player p) { private void handleSpecialHatEffects(Player p) {
ItemStack hat = p.getInventory().getHelmet(); ItemStack hat = p.getInventory().getHelmet();
if (hat == null || hat.getType() == Material.AIR) return; if (hat == null || hat.getType() == Material.AIR) return;
@@ -162,9 +196,12 @@ public class GadgetModule implements Module, Listener {
private void openFunGUI(Player player) { private void openFunGUI(Player player) {
Inventory gui = Bukkit.createInventory(null, 27, FUN_TITLE); Inventory gui = Bukkit.createInventory(null, 27, FUN_TITLE);
fillEdges(gui); fillEdges(gui);
gui.setItem(11, createItem(Material.FISHING_ROD, "§b§lEnterhaken", "§7Zieh dich durch die Luft!")); gui.setItem(10, createItem(Material.FISHING_ROD, "§b§lEnterhaken", "§7Zieh dich durch die Luft!"));
gui.setItem(13, createItem(Material.SHIELD, "§5§lSchutzzone", "§7Halte andere auf Distanz")); gui.setItem(11, createItem(Material.PACKED_ICE, "§b§lFreeze-Ray", "§7Friere andere Spieler ein!"));
gui.setItem(15, createItem(Material.EGG, "§f§lChicken-Rain", "§7Gack-Gack! Hühner überall!")); 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!"));
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ü")); gui.setItem(22, createItem(Material.ARROW, "§7Zurück", "§8Zum Hauptmenü"));
player.openInventory(gui); player.openInventory(gui);
} }
@@ -225,6 +262,15 @@ public class GadgetModule implements Module, Listener {
} else if (item.getType() == Material.FISHING_ROD) { } else if (item.getType() == Material.FISHING_ROD) {
player.getInventory().addItem(createItem(Material.FISHING_ROD, "§b§lEnterhaken", "§7Rechtsklick zum Katapultieren")); player.getInventory().addItem(createItem(Material.FISHING_ROD, "§b§lEnterhaken", "§7Rechtsklick zum Katapultieren"));
player.closeInventory(); player.closeInventory();
} else if (item.getType() == Material.PACKED_ICE) {
player.getInventory().addItem(createItem(Material.PACKED_ICE, "§b§lFreeze-Ray", "§7Rechtsklick zum Einfrieren"));
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"));
player.closeInventory();
} else if (item.getType() == Material.SHIELD) { } else if (item.getType() == Material.SHIELD) {
if (activeShields.contains(player.getUniqueId())) { if (activeShields.contains(player.getUniqueId())) {
activeShields.remove(player.getUniqueId()); activeShields.remove(player.getUniqueId());
@@ -261,6 +307,9 @@ public class GadgetModule implements Module, Listener {
PetManager.removePet(player); PetManager.removePet(player);
HatManager.removeHat(player); HatManager.removeHat(player);
player.getInventory().remove(Material.FISHING_ROD); 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."); player.sendMessage("§8[§6Nexus§8] §cAlle Gadgets abgelegt.");
} }

View File

@@ -0,0 +1,47 @@
package de.nexuslobby.modules.gadgets;
import de.nexuslobby.NexusLobby;
import org.bukkit.Location;
import org.bukkit.Particle;
import org.bukkit.Sound;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
public class MeteorStrike {
public static void launch(Player shooter) {
Location target = null;
Location start = shooter.getEyeLocation();
Vector direction = start.getDirection();
for (double d = 0; d < 30; d += 0.5) {
Location point = start.clone().add(direction.clone().multiply(d));
if (point.getBlock().getType().isSolid()) {
target = point;
break;
}
}
if (target == null) return;
final Location finalTarget = target.clone().add(0, 0.5, 0);
finalTarget.getWorld().spawnParticle(Particle.FLAME, finalTarget, 20, 0.5, 0.1, 0.5, 0.05);
shooter.sendMessage("§8[§6Nexus§8] §cMeteorit im Anflug...");
org.bukkit.Bukkit.getScheduler().runTaskLater(NexusLobby.getInstance(), () -> {
// EXPLOSION_EMITTER ist der moderne Name für HUGE_EXPLOSION
finalTarget.getWorld().spawnParticle(Particle.EXPLOSION_EMITTER, finalTarget, 1);
finalTarget.getWorld().spawnParticle(Particle.LAVA, finalTarget, 30, 0.5, 0.5, 0.5, 0.1);
finalTarget.getWorld().playSound(finalTarget, Sound.ENTITY_GENERIC_EXPLODE, 1.0f, 0.8f);
for (Entity entity : finalTarget.getWorld().getNearbyEntities(finalTarget, 4, 4, 4)) {
if (entity instanceof Player p) {
Vector v = p.getLocation().toVector().subtract(finalTarget.toVector()).normalize().multiply(1.5).setY(0.5);
p.setVelocity(v);
p.sendMessage("§cBUMM!");
}
}
}, 30L);
}
}

View File

@@ -0,0 +1,89 @@
package de.nexuslobby.modules.gadgets;
import de.nexuslobby.NexusLobby;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.Sound;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import java.util.Random;
public class PaintballGun {
private static final Random random = new Random();
// Wir nutzen jetzt Wolle für kräftigere Farben
private static final Material[] COLORS = {
Material.RED_WOOL, Material.BLUE_WOOL, Material.LIME_WOOL,
Material.ORANGE_WOOL, Material.MAGENTA_WOOL, Material.LIGHT_BLUE_WOOL,
Material.YELLOW_WOOL, Material.PURPLE_WOOL, Material.PINK_WOOL
};
public static void shoot(Player shooter) {
Location start = shooter.getEyeLocation();
Vector direction = start.getDirection();
Material randomColor = COLORS[random.nextInt(COLORS.length)];
shooter.getWorld().playSound(start, Sound.ENTITY_CHICKEN_EGG, 1.0f, 2.0f);
for (double d = 0; d < 25; d += 0.5) {
Location point = start.clone().add(direction.clone().multiply(d));
// Flug-Partikel (kleiner Rauch oder Schneeball)
point.getWorld().spawnParticle(Particle.ITEM_SNOWBALL, point, 1, 0, 0, 0, 0);
Block block = point.getBlock();
if (block.getType().isSolid()) {
impact(block, randomColor);
break;
}
}
}
private static void impact(Block centerBlock, Material color) {
Location centerLoc = centerBlock.getLocation();
centerLoc.getWorld().playSound(centerLoc, Sound.ENTITY_SLIME_SQUISH, 1.0f, 1.2f);
int radius = 2; // Radius der Farbkugel
// Wir gehen alle Blöcke im Würfel um den Einschlag durch
for (int x = -radius; x <= radius; x++) {
for (int y = -radius; y <= radius; y++) {
for (int z = -radius; z <= radius; z++) {
// Berechne Distanz für eine Kugelform (statt Würfel)
if (x * x + y * y + z * z <= radius * radius + 0.5) {
Block target = centerLoc.clone().add(x, y, z).getBlock();
// Nur solide Blöcke färben (keine Luft/Gras)
if (target.getType().isSolid()) {
applyColor(target, color);
}
}
}
}
}
}
private static void applyColor(Block block, Material color) {
Location loc = block.getLocation();
// Effekt-Partikel am Block
loc.getWorld().spawnParticle(Particle.BLOCK, loc.clone().add(0.5, 0.5, 0.5), 3, 0.1, 0.1, 0.1, color.createBlockData());
// Block-Änderung an alle senden
for (Player online : Bukkit.getOnlinePlayers()) {
online.sendBlockChange(loc, color.createBlockData());
}
// Nach 10 Sekunden zurücksetzen
Bukkit.getScheduler().runTaskLater(NexusLobby.getInstance(), () -> {
for (Player online : Bukkit.getOnlinePlayers()) {
online.sendBlockChange(loc, block.getBlockData());
}
}, 400L);
}
}

View File

@@ -0,0 +1,113 @@
package de.nexuslobby.modules.parkour;
import de.nexuslobby.NexusLobby;
import org.bukkit.Location;
import org.bukkit.Sound;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerInteractAtEntityEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
public class ParkourListener implements Listener {
private final ParkourManager manager;
private final String NPC_TAG = "parkour_npc";
private final HashMap<UUID, Long> startCooldown = new HashMap<>();
public ParkourListener(ParkourManager manager) {
this.manager = manager;
}
/**
* Startet den Parkour per Rechtsklick auf den ArmorStand
*/
@EventHandler
public void onInteract(PlayerInteractAtEntityEvent event) {
if (!(event.getRightClicked() instanceof ArmorStand as)) return;
// Prüfen, ob der ArmorStand den richtigen Tag hat
if (as.getScoreboardTags().contains(NPC_TAG)) {
Player player = event.getPlayer();
// Abbrechen, wenn der Spieler schon im Parkour ist
if (manager.isIngame(player)) return;
// Cooldown-Check (3 Sekunden)
if (startCooldown.containsKey(player.getUniqueId())) {
if (System.currentTimeMillis() - startCooldown.get(player.getUniqueId()) < 3000) {
player.sendMessage("§cBitte warte einen Moment, bevor du erneut startest.");
return;
}
}
// Parkour starten
manager.startParkour(player, as.getLocation());
player.sendMessage("§8[§6Parkour§8] §eViel Erfolg! Erreiche das Ziel so schnell wie möglich.");
player.playSound(player.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1f, 1f);
// Event abbrechen, damit man keine Ausrüstung vom ArmorStand klaut
event.setCancelled(true);
}
}
@EventHandler
public void onMove(PlayerMoveEvent event) {
// Performance: Nur berechnen, wenn ein voller Block gewechselt wurde
if (event.getFrom().getBlockX() == event.getTo().getBlockX() &&
event.getFrom().getBlockY() == event.getTo().getBlockY() &&
event.getFrom().getBlockZ() == event.getTo().getBlockZ()) return;
Player player = event.getPlayer();
if (!manager.isIngame(player)) return;
Location loc = player.getLocation();
// --- 1. ABSTURZ-CHECK ---
// Passe die Höhe '50' an deine Map an!
if (loc.getY() < 50) {
Location lastCp = manager.getCheckpoint(player);
if (lastCp != null) {
player.teleport(lastCp);
player.sendMessage("§8[§6Parkour§8] §cAbgestürzt! Zurück zum Checkpoint.");
player.playSound(player.getLocation(), Sound.ENTITY_ENDERMAN_TELEPORT, 0.5f, 1.0f);
}
return;
}
// --- 2. CHECKPOINT-CHECK ---
List<Location> cps = manager.getOrderedCheckpoints();
for (int i = 0; i < cps.size(); i++) {
if (isNearby(loc, cps.get(i))) {
manager.reachCheckpoint(player, i);
}
}
// --- 3. ZIEL-CHECK ---
Location finish = manager.getFinishLocation();
if (finish != null && isNearby(loc, finish)) {
manager.finishParkour(player);
// Nach dem Ziel setzen wir einen Cooldown
startCooldown.put(player.getUniqueId(), System.currentTimeMillis());
}
}
private boolean isNearby(Location playerLoc, Location targetLoc) {
if (playerLoc.getWorld() == null || !playerLoc.getWorld().equals(targetLoc.getWorld())) return false;
// 2.25 entspricht einem Radius von ca. 1.5 Blöcken
return playerLoc.distanceSquared(targetLoc) <= 2.25;
}
@EventHandler
public void onQuit(PlayerQuitEvent event) {
UUID uuid = event.getPlayer().getUniqueId();
manager.stopParkour(event.getPlayer());
startCooldown.remove(uuid);
}
}

View File

@@ -0,0 +1,231 @@
package de.nexuslobby.modules.parkour;
import de.nexuslobby.NexusLobby;
import net.md_5.bungee.api.ChatMessageType;
import net.md_5.bungee.api.chat.TextComponent;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Particle;
import org.bukkit.Sound;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
public class ParkourManager {
private final NexusLobby plugin;
private final File file;
private FileConfiguration config;
private final Map<UUID, Long> startTime = new HashMap<>();
private final Map<UUID, Location> lastCheckpointLoc = new HashMap<>();
private final Map<UUID, Integer> nextCheckpointIndex = new HashMap<>();
public ParkourManager(NexusLobby plugin) {
this.plugin = plugin;
this.file = new File(plugin.getDataFolder(), "parkour.yml");
loadConfig();
startParticleTask();
}
private void loadConfig() {
if (!file.exists()) {
file.getParentFile().mkdirs();
try { file.createNewFile(); } catch (IOException e) { e.printStackTrace(); }
}
config = YamlConfiguration.loadConfiguration(file);
}
public void setCheckpoint(Player player, Location loc) {
int nextId = 1;
if (config.contains("locations.checkpoints") && config.getConfigurationSection("locations.checkpoints") != null) {
nextId = config.getConfigurationSection("locations.checkpoints").getKeys(false).size() + 1;
}
addCheckpointLocation(String.valueOf(nextId), loc);
player.sendMessage("§8[§6Parkour§8] §7Checkpoint §e#" + nextId + " §7an deiner Position gesetzt.");
player.playSound(player.getLocation(), Sound.BLOCK_AMETHYST_BLOCK_CHIME, 1.0f, 1.5f);
}
/**
* Löscht die gesamte Strecke (Checkpoints und Ziel) und bricht aktuelle Läufe ab.
*/
public void removeAllPoints() {
config.set("locations.checkpoints", null);
config.set("locations.finish", null);
save();
// Alle Spieler aus dem Parkour werfen, damit keine Partikel zu gelöschten Zielen führen
for (UUID uuid : new HashSet<>(startTime.keySet())) {
Player p = Bukkit.getPlayer(uuid);
if (p != null) {
stopParkour(p);
p.sendMessage("§8[§6Parkour§8] §cDie aktuelle Strecke wurde soeben gelöscht.");
}
}
}
public void startParkour(Player player, Location startLoc) {
if (startTime.containsKey(player.getUniqueId())) return;
startTime.put(player.getUniqueId(), System.currentTimeMillis());
lastCheckpointLoc.put(player.getUniqueId(), startLoc);
nextCheckpointIndex.put(player.getUniqueId(), 0);
player.playSound(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_CHIME, 1.0f, 1.2f);
new BukkitRunnable() {
@Override
public void run() {
if (!isIngame(player) || !player.isOnline()) {
this.cancel();
return;
}
long diff = System.currentTimeMillis() - startTime.get(player.getUniqueId());
double sec = diff / 1000.0;
player.spigot().sendMessage(ChatMessageType.ACTION_BAR,
new TextComponent("§6⏱ Zeit: §e" + String.format("%.2f", sec) + "s §8| §bNächster Punkt: §7Partikel folgen"));
}
}.runTaskTimer(plugin, 0L, 1L);
}
private void startParticleTask() {
new BukkitRunnable() {
@Override
public void run() {
for (UUID uuid : startTime.keySet()) {
Player p = Bukkit.getPlayer(uuid);
if (p == null) continue;
int nextIdx = nextCheckpointIndex.getOrDefault(uuid, 0);
List<Location> cps = getOrderedCheckpoints();
if (nextIdx < cps.size()) {
Location nextCp = cps.get(nextIdx);
if (nextCp != null && p.getWorld().equals(nextCp.getWorld())) {
p.spawnParticle(Particle.SOUL_FIRE_FLAME, nextCp.clone().add(0, 0.5, 0), 5, 0.1, 0.3, 0.1, 0.02);
}
} else {
Location finish = getFinishLocation();
if (finish != null && p.getWorld().equals(finish.getWorld())) {
p.spawnParticle(Particle.HAPPY_VILLAGER, finish.clone().add(0, 0.5, 0), 8, 0.2, 0.5, 0.2, 0.02);
}
}
}
}
}.runTaskTimer(plugin, 0L, 6L);
}
public void reachCheckpoint(Player player, int reachedIndex) {
int currentNext = nextCheckpointIndex.getOrDefault(player.getUniqueId(), 0);
if (reachedIndex == currentNext) {
List<Location> cps = getOrderedCheckpoints();
if (reachedIndex < cps.size()) {
lastCheckpointLoc.put(player.getUniqueId(), cps.get(reachedIndex));
nextCheckpointIndex.put(player.getUniqueId(), reachedIndex + 1);
player.sendMessage("§8[§6Parkour§8] §bCheckpoint #" + (reachedIndex + 1) + " erreicht!");
player.playSound(player.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 0.8f, 1.5f);
}
}
}
public void finishParkour(Player player) {
if (!startTime.containsKey(player.getUniqueId())) return;
if (nextCheckpointIndex.getOrDefault(player.getUniqueId(), 0) < getOrderedCheckpoints().size()) {
player.sendMessage("§cDu hast Checkpoints übersprungen! Folge den Partikeln.");
return;
}
long duration = System.currentTimeMillis() - startTime.get(player.getUniqueId());
double seconds = duration / 1000.0;
player.sendMessage("§8§m--------------------------------------");
player.sendMessage("§8[§6Parkour§8] §a§lZiel erreicht!");
player.sendMessage("§7Deine Zeit: §e" + String.format("%.2f", seconds) + "s");
player.sendMessage("§8§m--------------------------------------");
player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 1.0f, 1.0f);
saveBestTime(player, seconds);
stopParkour(player);
}
public void stopParkour(Player player) {
startTime.remove(player.getUniqueId());
lastCheckpointLoc.remove(player.getUniqueId());
nextCheckpointIndex.remove(player.getUniqueId());
}
public void setStartLocation(Location loc) { config.set("locations.start", loc); save(); }
public void setFinishLocation(Location loc) { config.set("locations.finish", loc); save(); }
public void addCheckpointLocation(String id, Location loc) { config.set("locations.checkpoints." + id, loc); save(); }
public Location getStartLocation() { return config.getLocation("locations.start"); }
public Location getFinishLocation() { return config.getLocation("locations.finish"); }
public List<Location> getOrderedCheckpoints() {
if (!config.contains("locations.checkpoints") || config.getConfigurationSection("locations.checkpoints") == null)
return new ArrayList<>();
return config.getConfigurationSection("locations.checkpoints").getKeys(false).stream()
.sorted(Comparator.comparingInt(Integer::parseInt))
.map(key -> config.getLocation("locations.checkpoints." + key))
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
private void save() { try { config.save(file); } catch (IOException e) { e.printStackTrace(); } }
public void clearStats() {
config.set("besttimes", null);
config.set("names", null);
save();
}
public boolean isIngame(Player player) { return startTime.containsKey(player.getUniqueId()); }
public Location getCheckpoint(Player player) { return lastCheckpointLoc.get(player.getUniqueId()); }
private void saveBestTime(Player player, double time) {
String path = "besttimes." + player.getUniqueId();
double currentTime = config.getDouble(path, 99999.9);
if (time < currentTime) {
config.set(path, time);
config.set("names." + player.getUniqueId(), player.getName());
save();
player.sendMessage("§8[§6Parkour§8] §6§lNeuer Rekord! §7Du hast dich verbessert.");
}
}
public String getTopTen() {
if (!config.contains("besttimes") || config.getConfigurationSection("besttimes") == null)
return "§6§l🏆 TOP 10 PARKOUR 🏆\n§7Noch keine Rekorde.";
Map<String, Double> allTimes = new HashMap<>();
for (String uuidStr : config.getConfigurationSection("besttimes").getKeys(false)) {
allTimes.put(uuidStr, config.getDouble("besttimes." + uuidStr));
}
List<Map.Entry<String, Double>> sortedList = allTimes.entrySet().stream()
.sorted(Map.Entry.comparingByValue())
.limit(10)
.toList();
StringBuilder builder = new StringBuilder("§6§l🏆 TOP 10 PARKOUR 🏆");
int rank = 1;
for (Map.Entry<String, Double> entry : sortedList) {
String name = config.getString("names." + entry.getKey(), "Unbekannt");
builder.append("\n§e#").append(rank).append(" §f").append(name).append(" §8» §a").append(String.format("%.2f", entry.getValue())).append("s");
rank++;
}
return builder.toString();
}
}

View File

@@ -0,0 +1,188 @@
package de.nexuslobby.modules.player;
import de.nexuslobby.NexusLobby;
import de.nexuslobby.api.Module;
import me.clip.placeholderapi.PlaceholderAPI;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.Statistic;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.SkullMeta;
import java.util.ArrayList;
import java.util.List;
/**
* Modul zur Inspektion von Spielern in der Lobby.
* Zeigt detaillierte Statistiken in einer interaktiven GUI an.
*/
public class PlayerInspectModule implements Module, Listener {
@Override
public String getName() {
return "PlayerInspect";
}
@Override
public void onEnable() {
Bukkit.getPluginManager().registerEvents(this, NexusLobby.getInstance());
}
@Override
public void onDisable() {}
@EventHandler
public void onPlayerInteract(PlayerInteractEntityEvent event) {
// Prüfen, ob ein Spieler rechtsgeklickt wurde
if (event.getRightClicked() instanceof Player target) {
Player viewer = event.getPlayer();
// GUI nur öffnen, wenn die Hand leer ist (verhindert Konflikte mit Items)
if (viewer.getInventory().getItemInMainHand().getType() == Material.AIR) {
openDetailedInspectGUI(viewer, target);
}
}
}
@EventHandler
public void onInventoryClick(InventoryClickEvent event) {
// Sicherstellen, dass es unser Statistik-Inventar ist anhand des Titels
if (event.getView().getTitle().contains("Statistiken")) {
event.setCancelled(true); // Verhindert, dass Items herausgenommen werden
ItemStack clickedItem = event.getCurrentItem();
if (clickedItem == null || clickedItem.getType() == Material.AIR) return;
// Logik für den Schließen-Button (Barriere)
if (clickedItem.getType() == Material.BARRIER) {
event.getWhoClicked().closeInventory();
}
}
}
public void openDetailedInspectGUI(Player viewer, Player target) {
// Titel mit Farbcodes
String title = "§8» §3Statistiken: §b" + target.getName();
Inventory gui = Bukkit.createInventory(null, 45, title);
// --- Design: Hintergrund mit Glasscheiben füllen ---
ItemStack separator = createSimpleItem(Material.GRAY_STAINED_GLASS_PANE, " ");
int[] borderSlots = {
0,1,2,3,4,5,6,7,8,
9,17,
18,26,
27,35,
36,37,38,39,41,42,43,44
};
for (int slot : borderSlots) gui.setItem(slot, separator);
// --- Kopf des Spielers mit LuckPerms Prefix via PAPI ---
ItemStack head = new ItemStack(Material.PLAYER_HEAD);
SkullMeta headMeta = (SkullMeta) head.getItemMeta();
if (headMeta != null) {
headMeta.setOwningPlayer(target);
headMeta.setDisplayName("§e§l" + target.getName());
List<String> lore = new ArrayList<>();
lore.add("§8§m-----------------------");
// LuckPerms Prefix über PlaceholderAPI auslesen
String prefix = "%luckperms_prefix%";
prefix = PlaceholderAPI.setPlaceholders(target, prefix);
// KORREKTUR: Farbcodes (&) in echte Minecraft-Farben (§) umwandeln
prefix = ChatColor.translateAlternateColorCodes('&', prefix);
lore.add("§7Rang: " + (prefix.isEmpty() ? "§fSpieler" : prefix));
lore.add("§7Level: §a" + target.getLevel());
lore.add("§7Status: §aOnline");
lore.add("§8§m-----------------------");
headMeta.setLore(lore);
head.setItemMeta(headMeta);
}
gui.setItem(4, head);
// --- Statistik Kategorie: KAMPF (Slot 19) ---
gui.setItem(19, createStatsItem(Material.DIAMOND_SWORD, "§c§lKampf-Statistiken",
"§8» §7Spieler-Kills: §f" + target.getStatistic(Statistic.PLAYER_KILLS),
"§8» §7Mob-Kills: §f" + target.getStatistic(Statistic.MOB_KILLS),
"§8» §7Tode gesamt: §f" + target.getStatistic(Statistic.DEATHS),
"§8» §7Schaden verursacht: §f" + (target.getStatistic(Statistic.DAMAGE_DEALT) / 10) + ""));
// --- Statistik Kategorie: ARBEIT (Slot 21) ---
// Optimierte Abfrage für wichtige Blöcke
int totalBlocks = target.getStatistic(Statistic.MINE_BLOCK, Material.STONE) +
target.getStatistic(Statistic.MINE_BLOCK, Material.DIRT) +
target.getStatistic(Statistic.MINE_BLOCK, Material.COBBLESTONE);
gui.setItem(21, createStatsItem(Material.IRON_PICKAXE, "§e§lHandwerk & Fleiß",
"§8» §7Blöcke (S/D/C): §f" + totalBlocks,
"§8» §7Items gedroppt: §f" + target.getStatistic(Statistic.DROP_COUNT),
"§8» §7Fische gefangen: §f" + target.getStatistic(Statistic.FISH_CAUGHT),
"§8» §7Glocken geläutet: §f" + target.getStatistic(Statistic.BELL_RING)));
// --- Statistik Kategorie: BEWEGUNG (Slot 23) ---
double walkKm = target.getStatistic(Statistic.WALK_ONE_CM) / 100000.0;
double flyKm = target.getStatistic(Statistic.FLY_ONE_CM) / 100000.0;
gui.setItem(23, createStatsItem(Material.GOLDEN_BOOTS, "§b§lReise-Statistiken",
"§8» §7Gelaufen: §f" + String.format("%.2f", walkKm) + " km",
"§8» §7Geflogen: §f" + String.format("%.2f", flyKm) + " km",
"§8» §7Sprünge: §f" + target.getStatistic(Statistic.JUMP)));
// --- Statistik Kategorie: ZEIT (Slot 25) ---
long ticks = target.getStatistic(Statistic.PLAY_ONE_MINUTE);
long hours = ticks / 72000;
long mins = (ticks % 72000) / 1200;
gui.setItem(25, createStatsItem(Material.CLOCK, "§a§lZeit-Statistiken",
"§8» §7Spielzeit: §f" + hours + " Std. " + mins + " Min.",
"§8» §7Letzter Tod vor: §f" + (target.getStatistic(Statistic.TIME_SINCE_DEATH) / 1200) + " Min.",
"§8» §7Tage auf Server: §f" + (target.getStatistic(Statistic.PLAY_ONE_MINUTE) / 1728000)));
// --- Schließen Button (Slot 40) ---
gui.setItem(40, createSimpleItem(Material.BARRIER, "§c§lMenü schließen"));
// Inventar für den Zuschauer öffnen
viewer.openInventory(gui);
}
/**
* Erstellt ein ItemStack mit Name und Lore-Zeilen inklusive einer Leerzeile am Anfang.
*/
private ItemStack createStatsItem(Material material, String displayName, String... loreLines) {
ItemStack item = new ItemStack(material);
ItemMeta meta = item.getItemMeta();
if (meta != null) {
meta.setDisplayName(displayName);
List<String> lore = new ArrayList<>();
lore.add(" "); // Leerzeile für sauberes Design
for (String line : loreLines) {
lore.add(line);
}
meta.setLore(lore);
item.setItemMeta(meta);
}
return item;
}
/**
* Erstellt ein einfaches ItemStack ohne Lore für Designzwecke.
*/
private ItemStack createSimpleItem(Material material, String displayName) {
ItemStack item = new ItemStack(material);
ItemMeta meta = item.getItemMeta();
if (meta != null) {
meta.setDisplayName(displayName);
item.setItemMeta(meta);
}
return item;
}
}

View File

@@ -97,6 +97,13 @@ compass:
lore: lore:
- "&7Zeige was du kannst!" - "&7Zeige was du kannst!"
# -----------------------------------------------------
# PLAYER INSPECT (Statistiken per Rechtsklick)
# -----------------------------------------------------
player_inspect:
enabled: true
gui_title: "&8Statistiken von &6{PLAYER}"
# --- Suppressor / Global Chat Einstellungen --- # --- Suppressor / Global Chat Einstellungen ---
suppressor: suppressor:
enabled: true enabled: true

View File

@@ -1,9 +1,9 @@
name: NexusLobby name: NexusLobby
main: de.nexuslobby.NexusLobby main: de.nexuslobby.NexusLobby
version: "1.0.5" version: "1.0.8"
api-version: "1.21" api-version: "1.21"
author: M_Viper author: M_Viper
description: Modular Lobby Plugin description: Modular Lobby Plugin with an invisible Particle-Parkour system.
softdepend: [LuckPerms, PlaceholderAPI, Vault, WorldGuard] softdepend: [LuckPerms, PlaceholderAPI, Vault, WorldGuard]
commands: commands:
@@ -38,8 +38,8 @@ commands:
permission: nexuslobby.build permission: nexuslobby.build
permission-message: "§cDu hast keine Rechte!" permission-message: "§cDu hast keine Rechte!"
nexuslobby: nexuslobby:
description: Zeigt Informationen über das Plugin an oder lädt es neu description: Hauptbefehl für Plugin-Verwaltung, Spawn-Setup und Parkour-Konfiguration
usage: /nexuslobby [reload|setspawn] usage: /nexuslobby [reload|setspawn|silentjoin|sb|parkour]
aliases: [nexus, lobby] aliases: [nexus, lobby]
nexustools: nexustools:
description: Nexus ArmorStand Editor (LookAt, Dynamic, etc.) description: Nexus ArmorStand Editor (LookAt, Dynamic, etc.)
@@ -71,6 +71,20 @@ commands:
usage: /spawn usage: /spawn
aliases: [l, hub] aliases: [l, hub]
# --- NEUE PARKOUR DIREKT-BEFEHLE ---
setstart:
description: Markiert einen ArmorStand als Parkour-NPC oder setzt die Start-Location
usage: /setstart
permission: nexuslobby.admin
setcheckpoint:
description: Setzt einen neuen Checkpoint an deiner aktuellen Position
usage: /setcheckpoint
permission: nexuslobby.admin
setfinish:
description: Setzt den Zielpunkt für den Parkour
usage: /setfinish
permission: nexuslobby.admin
permissions: permissions:
nexuslobby.portal: nexuslobby.portal:
description: Zugriff auf Portalbefehle description: Zugriff auf Portalbefehle
@@ -85,7 +99,7 @@ permissions:
description: Zugriff auf den Server Switcher description: Zugriff auf den Server Switcher
default: true default: true
nexuslobby.admin: nexuslobby.admin:
description: Voller Zugriff auf Lobby-Gamerules, Einstellungen, Intro, Border und Reload description: Voller Zugriff auf Lobby-Gamerules, Einstellungen, Intro, Border, Parkour-Setup und Reload
default: op default: op
nexuslobby.build: nexuslobby.build:
description: Erlaubt das Umgehen des Lobby-Schutzes zum Bauen description: Erlaubt das Umgehen des Lobby-Schutzes zum Bauen
@@ -108,3 +122,9 @@ permissions:
nexuslobby.mapart: nexuslobby.mapart:
description: Erlaubt das Erstellen von Map-Art Bildern description: Erlaubt das Erstellen von Map-Art Bildern
default: op default: op
nexuslobby.silentjoin:
description: Versteckt die Join-Nachricht für den Spieler (Silent Join)
default: op
nexuslobby.parkour.admin:
description: Erlaubt das Setzen der unsichtbaren Parkour-Punkte (Start, Ziel, Checkpoints)
default: op