package de.nexuslobby.modules.intro; import de.nexuslobby.NexusLobby; import de.nexuslobby.api.Module; import net.md_5.bungee.api.ChatMessageType; import net.md_5.bungee.api.chat.TextComponent; 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.event.player.PlayerToggleSneakEvent; 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 File configFile; private FileConfiguration config; // --- Einstellungen --- private final int TICKS_FLUG = 70; // Dauer der Fahrt zwischen zwei Punkten (3.5 Sek) private final int TICKS_PAUSE = 30; // Standzeit an jedem Punkt (1.5 Sek) @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); } loadPoints(); } @Override public void onDisable() { activeIntro.clear(); } private void loadPoints() { points.clear(); configFile = new File(NexusLobby.getInstance().getDataFolder(), "intro.yml"); if (!configFile.exists()) { try { configFile.createNewFile(); } catch (IOException ignored) {} } config = YamlConfiguration.loadConfiguration(configFile); List list = config.getList("points"); if (list != null) { for (Object obj : list) { if (obj instanceof Location loc) points.add(loc); } } } private void savePoints() { config.set("points", points); try { config.save(configFile); } catch (IOException e) { e.printStackTrace(); } } @EventHandler public void onJoin(PlayerJoinEvent event) { if (!event.getPlayer().hasPlayedBefore() && points.size() >= 2) { startIntro(event.getPlayer()); } } @EventHandler public void onSneak(PlayerToggleSneakEvent event) { if (activeIntro.contains(event.getPlayer().getUniqueId()) && event.isSneaking()) { stopIntro(event.getPlayer(), true); } } @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) { p.sendMessage("§8§m------------------------------------"); p.sendMessage("§6§lNexus Intro System (Cinematic)"); p.sendMessage("§e/intro add §7- Punkt hinzufügen"); p.sendMessage("§e/intro clear §7- Alle Punkte löschen"); p.sendMessage("§e/intro start §7- Teste die Fahrt"); p.sendMessage("§8§m------------------------------------"); return true; } switch (args[0].toLowerCase()) { case "add" -> { points.add(p.getLocation()); savePoints(); p.sendMessage("§8[§6Nexus§8] §aPunkt #" + points.size() + " wurde gesetzt!"); } case "clear" -> { points.clear(); savePoints(); p.sendMessage("§8[§6Nexus§8] §cAlle Intro-Punkte wurden gelöscht."); } case "start" -> { if (points.size() < 2) { p.sendMessage("§8[§6Nexus§8] §cDu brauchst mindestens 2 Punkte für eine Fahrt."); } else { startIntro(p); } } } return true; } public void startIntro(Player player) { activeIntro.add(player.getUniqueId()); player.setGameMode(GameMode.SPECTATOR); new BukkitRunnable() { int currentSegment = 0; int tickInSegment = 0; boolean isPausing = true; @Override public void run() { try { if (!player.isOnline() || !activeIntro.contains(player.getUniqueId())) { this.cancel(); return; } if (currentSegment >= points.size() - 1) { stopIntro(player, false); this.cancel(); return; } Location start = points.get(currentSegment); Location end = points.get(currentSegment + 1); if (isPausing) { // Kamera steht am aktuellen Punkt player.teleport(start); tickInSegment++; if (tickInSegment >= TICKS_PAUSE) { isPausing = false; tickInSegment = 0; } } else { // Kamera fliegt zum nächsten Punkt double progress = (double) tickInSegment / (double) TICKS_FLUG; // "Smooth Step" für flüssigeres Beschleunigen/Bremsen double smoothT = progress * progress * (3 - 2 * progress); Location nextLoc = interpolate(start, end, smoothT); player.teleport(nextLoc); tickInSegment++; if (tickInSegment >= TICKS_FLUG) { isPausing = true; tickInSegment = 0; currentSegment++; } } player.spigot().sendMessage(ChatMessageType.ACTION_BAR, new TextComponent("§6§lINTRO-TOUR §8| §ePunkt " + (currentSegment + 1) + " §8| §7Sneak zum Abbrechen")); } catch (Exception e) { this.cancel(); stopIntro(player, true); } } }.runTaskTimer(NexusLobby.getInstance(), 0L, 1L); } 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; // Sanfte Drehung float startYaw = start.getYaw(); float endYaw = end.getYaw(); // Verhindert ruckartige 360-Grad Dreher 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, boolean canceled) { activeIntro.remove(player.getUniqueId()); player.setGameMode(GameMode.ADVENTURE); player.teleport(player.getWorld().getSpawnLocation()); if (canceled) { player.sendMessage("§8[§6Nexus§8] §cIntro abgebrochen."); } else { player.sendMessage("§8[§6Nexus§8] §aWillkommen auf dem Netzwerk!"); } } }