package de.nexuslobby.modules.intro; import de.nexuslobby.NexusLobby; import de.nexuslobby.api.Module; import org.bukkit.Bukkit; import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; 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.player.PlayerJoinEvent; import org.bukkit.scheduler.BukkitRunnable; import org.jetbrains.annotations.NotNull; import java.io.File; import java.io.IOException; import java.util.*; public class IntroModule implements Module, Listener, CommandExecutor { private final Set activeIntro = new HashSet<>(); private final List points = new ArrayList<>(); private final List labels = new ArrayList<>(); // Abschluss-Titel (konfigurierbar) private String endTitle = "§a§lWillkommen!"; private String endSubtitle = "§7Viel Spaß auf NexusLobby"; private File configFile; private FileConfiguration config; // --- Einstellungen --- private final int TICKS_FLUG = 70; private final int TICKS_PAUSE = 30; private final int TITLE_FADE_IN = 10; private final int TITLE_STAY = 50; private final int TITLE_FADE_OUT = 10; @Override public String getName() { return "Intro"; } @Override public void onEnable() { Bukkit.getPluginManager().registerEvents(this, NexusLobby.getInstance()); if (NexusLobby.getInstance().getCommand("intro") != null) { NexusLobby.getInstance().getCommand("intro").setExecutor(this); } loadConfig(); } @Override public void onDisable() { org.bukkit.event.HandlerList.unregisterAll(this); activeIntro.clear(); } // ───────────────────────────────────────────────────────────────────────── // Persistenz // ───────────────────────────────────────────────────────────────────────── private void loadConfig() { points.clear(); labels.clear(); configFile = new File(NexusLobby.getInstance().getDataFolder(), "intro.yml"); if (!configFile.exists()) { try { configFile.createNewFile(); } catch (IOException ignored) {} } config = YamlConfiguration.loadConfiguration(configFile); // Abschluss-Titel laden endTitle = config.getString("end-title", "§a§lWillkommen!"); endSubtitle = config.getString("end-subtitle", "§7Viel Spaß auf NexusLobby"); // Punkte laden List list = config.getList("intro-points"); if (list != null) { for (Object obj : list) { if (obj instanceof Map map) { Object locObj = map.get("location"); Object labelObj = map.get("label"); if (locObj instanceof Location loc) { points.add(loc); labels.add(labelObj != null ? labelObj.toString() : ""); } } } } // Rückwärts-Kompatibilität if (points.isEmpty()) { List oldList = config.getList("points"); if (oldList != null) { for (Object obj : oldList) { if (obj instanceof Location loc) { points.add(loc); labels.add(""); } } } } } private void saveConfig() { // Abschluss-Titel speichern config.set("end-title", endTitle); config.set("end-subtitle", endSubtitle); // Punkte speichern List> data = new ArrayList<>(); for (int i = 0; i < points.size(); i++) { Map entry = new LinkedHashMap<>(); entry.put("location", points.get(i)); entry.put("label", labels.get(i)); data.add(entry); } config.set("intro-points", data); config.set("points", null); try { config.save(configFile); } catch (IOException e) { NexusLobby.getInstance().getLogger().severe("Fehler beim Speichern der Intro-Config: " + e.getMessage()); } } // ───────────────────────────────────────────────────────────────────────── // Events // ───────────────────────────────────────────────────────────────────────── @EventHandler public void onJoin(PlayerJoinEvent event) { if (!event.getPlayer().hasPlayedBefore() && points.size() >= 2) { startIntro(event.getPlayer()); } } // ───────────────────────────────────────────────────────────────────────── // Befehle // ───────────────────────────────────────────────────────────────────────── @Override public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { if (!(sender instanceof Player p)) return true; if (!p.hasPermission("nexuslobby.admin")) return true; if (args.length == 0) { sendHelp(p); return true; } switch (args[0].toLowerCase()) { case "add" -> { String pointLabel = args.length > 1 ? String.join(" ", Arrays.copyOfRange(args, 1, args.length)) : ""; points.add(p.getLocation()); labels.add(pointLabel); saveConfig(); if (pointLabel.isEmpty()) { p.sendMessage(de.nexuslobby.utils.LangManager.getPrefix() + "§aPunkt §e#" + points.size() + " §awurde gesetzt."); } else { p.sendMessage(de.nexuslobby.utils.LangManager.getPrefix() + "§aPunkt §e#" + points.size() + " §amit Text §e\"" + pointLabel + "\" §agesetzt."); } } case "clear" -> { points.clear(); labels.clear(); saveConfig(); p.sendMessage(de.nexuslobby.utils.LangManager.getPrefix() + "§cAlle Intro-Punkte wurden gelöscht."); } case "list" -> { if (points.isEmpty()) { p.sendMessage(de.nexuslobby.utils.LangManager.getPrefix() + "§cKeine Punkte vorhanden."); return true; } p.sendMessage("§8§m------------------------------------"); p.sendMessage("§6§lIntro-Punkte (" + points.size() + ")"); for (int i = 0; i < points.size(); i++) { Location loc = points.get(i); String lbl = labels.get(i).isEmpty() ? "§7(kein Text)" : "§e" + labels.get(i); p.sendMessage("§8#" + (i + 1) + " §7" + String.format("%.1f", loc.getX()) + ", " + String.format("%.1f", loc.getY()) + ", " + String.format("%.1f", loc.getZ()) + " " + lbl); } p.sendMessage("§8§m------------------------------------"); } case "start" -> { if (points.size() < 2) { p.sendMessage(de.nexuslobby.utils.LangManager.getPrefix() + "§cDu brauchst mindestens 2 Punkte für eine Fahrt."); } else { startIntro(p); } } // /intro setendtitle — setzt den großen Abschluss-Titel case "setendtitle" -> { if (args.length < 2) { p.sendMessage(de.nexuslobby.utils.LangManager.getPrefix() + "§cBenutzung: §e/intro setendtitle "); p.sendMessage("§7Farbcodes mit §e& §7möglich. Beispiel: §e/intro setendtitle &a&lWillkommen!"); return true; } endTitle = colorize(String.join(" ", Arrays.copyOfRange(args, 1, args.length))); saveConfig(); p.sendMessage(de.nexuslobby.utils.LangManager.getPrefix() + "§aAbschluss-Titel gesetzt: " + endTitle); // Vorschau p.sendTitle(endTitle, endSubtitle, TITLE_FADE_IN, TITLE_STAY, TITLE_FADE_OUT); } // /intro setendsubtitle — setzt den kleinen Abschluss-Untertitel case "setendsubtitle" -> { if (args.length < 2) { p.sendMessage(de.nexuslobby.utils.LangManager.getPrefix() + "§cBenutzung: §e/intro setendsubtitle "); p.sendMessage("§7Farbcodes mit §e& §7möglich. Beispiel: §e/intro setendsubtitle &7Viel Spaß!"); return true; } endSubtitle = colorize(String.join(" ", Arrays.copyOfRange(args, 1, args.length))); saveConfig(); p.sendMessage(de.nexuslobby.utils.LangManager.getPrefix() + "§aAbschluss-Untertitel gesetzt: " + endSubtitle); // Vorschau p.sendTitle(endTitle, endSubtitle, TITLE_FADE_IN, TITLE_STAY, TITLE_FADE_OUT); } default -> sendHelp(p); } return true; } private void sendHelp(Player p) { p.sendMessage("§8§m------------------------------------"); p.sendMessage("§6§lNexus Intro System (Cinematic)"); p.sendMessage("§e/intro add [Text] §7- Punkt + optionaler Titel"); p.sendMessage("§e/intro clear §7- Alle Punkte löschen"); p.sendMessage("§e/intro list §7- Alle Punkte anzeigen"); p.sendMessage("§e/intro start §7- Fahrt testen"); p.sendMessage("§e/intro setendtitle §7- Abschluss-Titel setzen"); p.sendMessage("§e/intro setendsubtitle §7- Abschluss-Untertitel setzen"); p.sendMessage("§7Aktuell: §r" + endTitle + " §8/ §r" + endSubtitle); p.sendMessage("§8§m------------------------------------"); } // ───────────────────────────────────────────────────────────────────────── // Intro-Logik // ───────────────────────────────────────────────────────────────────────── public void startIntro(Player player) { activeIntro.add(player.getUniqueId()); player.setGameMode(GameMode.SPECTATOR); // Titel für den ersten Punkt sofort anzeigen showTitleForSegment(player, 0); new BukkitRunnable() { int currentSegment = 0; int tickInSegment = 0; boolean isPausing = true; int lastTitleSegment = 0; @Override public void run() { try { if (!player.isOnline() || !activeIntro.contains(player.getUniqueId())) { this.cancel(); return; } if (currentSegment >= points.size() - 1) { stopIntro(player); this.cancel(); return; } Location start = points.get(currentSegment); Location end = points.get(currentSegment + 1); if (isPausing) { player.teleport(start); tickInSegment++; if (tickInSegment >= TICKS_PAUSE) { isPausing = false; tickInSegment = 0; } } else { double progress = (double) tickInSegment / (double) TICKS_FLUG; double smoothT = progress * progress * (3 - 2 * progress); player.teleport(interpolate(start, end, smoothT)); tickInSegment++; if (tickInSegment >= TICKS_FLUG) { isPausing = true; tickInSegment = 0; currentSegment++; if (currentSegment < points.size() && currentSegment != lastTitleSegment) { showTitleForSegment(player, currentSegment); lastTitleSegment = currentSegment; } } } } catch (Exception e) { this.cancel(); stopIntro(player); } } }.runTaskTimer(NexusLobby.getInstance(), 0L, 1L); } private void showTitleForSegment(Player player, int segmentIndex) { if (segmentIndex < 0 || segmentIndex >= labels.size()) return; String lbl = labels.get(segmentIndex); if (lbl == null || lbl.isEmpty()) return; player.sendTitle( "§6§l" + lbl, "§7✦ NexusLobby ✦", TITLE_FADE_IN, TITLE_STAY, TITLE_FADE_OUT ); } private Location interpolate(Location start, Location end, double t) { double x = start.getX() + (end.getX() - start.getX()) * t; double y = start.getY() + (end.getY() - start.getY()) * t; double z = start.getZ() + (end.getZ() - start.getZ()) * t; float startYaw = start.getYaw(); float endYaw = end.getYaw(); float diff = (endYaw - startYaw) % 360; if (diff > 180) diff -= 360; if (diff < -180) diff += 360; float yaw = startYaw + diff * (float) t; float pitch = (float) (start.getPitch() + (end.getPitch() - start.getPitch()) * t); return new Location(start.getWorld(), x, y, z, yaw, pitch); } private void stopIntro(Player player) { activeIntro.remove(player.getUniqueId()); player.setGameMode(GameMode.ADVENTURE); player.teleport(player.getWorld().getSpawnLocation()); // Abschluss-Titel — komplett konfigurierbar player.sendTitle(endTitle, endSubtitle, 10, 60, 20); } /** Ersetzt &-Farbcodes durch §-Codes */ private String colorize(String input) { return input.replace("&", "§"); } }