diff --git a/src/main/java/de/nexuslobby/modules/armorstandtools/ASTListener.java b/src/main/java/de/nexuslobby/modules/armorstandtools/ASTListener.java index fc4a11a..e41f3de 100644 --- a/src/main/java/de/nexuslobby/modules/armorstandtools/ASTListener.java +++ b/src/main/java/de/nexuslobby/modules/armorstandtools/ASTListener.java @@ -1,9 +1,5 @@ package de.nexuslobby.modules.armorstandtools; -import de.nexuslobby.NexusLobby; -import com.google.common.io.ByteArrayDataOutput; -import com.google.common.io.ByteStreams; -import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.entity.ArmorStand; import org.bukkit.entity.Player; @@ -14,84 +10,120 @@ import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.player.PlayerInteractAtEntityEvent; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; - -import java.util.Set; +import org.bukkit.util.EulerAngle; public class ASTListener implements Listener { + /** + * Erkennt den Rechtsklick auf den ArmorStand. + * Öffnet bei Sneak + Rechtsklick die Haupt-GUI. + */ @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onInteract(PlayerInteractAtEntityEvent event) { + // Sicherstellen, dass nur die Haupthand zählt (verhindert Doppel-Trigger) if (event.getHand() != EquipmentSlot.HAND) return; if (!(event.getRightClicked() instanceof ArmorStand as)) return; Player p = event.getPlayer(); - // Sneak-Rechtsklick zum Bearbeiten + // Prüfen ob Spieler schleicht (Shift) if (p.isSneaking()) { if (p.hasPermission("nexuslobby.armorstand.use")) { event.setCancelled(true); + + // ArmorStand für diesen Spieler zwischenspeichern AST.selectedArmorStand.put(p.getUniqueId(), as); + + // Haupt-GUI öffnen new ArmorStandGUI(as, p); } - return; - } - - // Normale Interaktion (Befehl ausführen) - Set tags = as.getScoreboardTags(); - for (String tag : tags) { - if (tag.startsWith("ascmd:")) { - event.setCancelled(true); - executeNexusCommand(p, tag); - break; - } } } + /** + * Verwaltet alle Klicks innerhalb der verschiedenen GUIs. + */ @EventHandler(priority = EventPriority.LOW) public void onInventoryClick(InventoryClickEvent event) { - if (!event.getView().getTitle().equals("Nexus ArmorStand Editor")) return; + String title = event.getView().getTitle(); + + // Prüfen, ob es eine unserer GUIs ist + if (!title.equals("Nexus ArmorStand Editor") && + !title.equals("Pose: Körperteil wählen") && + !title.startsWith("Achsen:")) return; event.setCancelled(true); if (!(event.getWhoClicked() instanceof Player p)) return; - ItemStack item = event.getCurrentItem(); - if (item == null || item.getType() == Material.AIR) return; - ArmorStand as = AST.selectedArmorStand.get(p.getUniqueId()); if (as == null || !as.isValid()) { p.closeInventory(); return; } - ArmorStandTool tool = ArmorStandTool.get(item); - if (tool != null) { - tool.execute(as, p); - // GUI aktualisieren, falls noch offen (SET_NAME schließt es z.B.) - if (p.getOpenInventory().getTitle().equals("Nexus ArmorStand Editor")) { + ItemStack item = event.getCurrentItem(); + if (item == null || item.getType() == Material.AIR) return; + + // --- 1. LOGIK: HAUPTMENÜ --- + if (title.equals("Nexus ArmorStand Editor")) { + ArmorStandTool tool = ArmorStandTool.get(item); + if (tool != null) { + tool.execute(as, p); + // GUI erneuern (außer sie wurde durch execute geschlossen) + if (p.getOpenInventory().getTitle().equals(title)) { + new ArmorStandGUI(as, p); + } + } + } + + // --- 2. LOGIK: KÖRPERTEIL-AUSWAHL --- + else if (title.equals("Pose: Körperteil wählen")) { + if (item.getType() == Material.BARRIER) { new ArmorStandGUI(as, p); + return; + } + + String targetPart = null; + switch (item.getType()) { + case PLAYER_HEAD -> targetPart = "HEAD_ROT"; + case IRON_CHESTPLATE -> targetPart = "BODY_ROT"; + case STICK -> targetPart = (event.getSlot() == 14) ? "L_ARM_ROT" : "R_ARM_ROT"; + case LEATHER_BOOTS -> targetPart = (event.getSlot() == 28) ? "L_LEG_ROT" : "R_LEG_ROT"; + } + + if (targetPart != null) { + ArmorStandPoseGUI.openAxisDetailMenu(p, as, targetPart); } } - } - - private void executeNexusCommand(Player p, String tag) { - try { - String[] parts = tag.split(":", 3); - if (parts.length < 3) return; - String type = parts[1].toLowerCase(); - String finalizedCommand = parts[2].replace("%player%", p.getName()); - - switch (type) { - case "player" -> p.performCommand(finalizedCommand); - case "console" -> Bukkit.dispatchCommand(Bukkit.getConsoleSender(), finalizedCommand); - case "bungee" -> sendToBungee(p, finalizedCommand); + + // --- 3. LOGIK: ACHSEN-FEINJUSTIERUNG --- + else if (title.startsWith("Achsen:")) { + if (item.getType() == Material.ARROW) { + ArmorStandPoseGUI.openPartSelectionMenu(p, as); + return; } - } catch (Exception e) { e.printStackTrace(); } - } + + String partName = title.split(": ")[1]; + ArmorStandTool tool = ArmorStandTool.valueOf(partName); + + // Berechnung des Winkels (Links/Rechts & Shift) + double change = event.isShiftClick() ? Math.toRadians(1) : Math.toRadians(15); + if (event.isRightClick()) change *= -1; - private void sendToBungee(Player p, String server) { - ByteArrayDataOutput out = ByteStreams.newDataOutput(); - out.writeUTF("Connect"); - out.writeUTF(server); - p.sendPluginMessage(NexusLobby.getInstance(), "BungeeCord", out.toByteArray()); + EulerAngle oldPose = ArmorStandPoseGUI.getAngleForPart(as, tool); + EulerAngle newPose = oldPose; + + if (item.getType() == Material.RED_DYE) { + newPose = oldPose.setX(oldPose.getX() + change); + } else if (item.getType() == Material.GREEN_DYE) { + newPose = oldPose.setY(oldPose.getY() + change); + } else if (item.getType() == Material.BLUE_DYE) { + newPose = oldPose.setZ(oldPose.getZ() + change); + } + + ArmorStandPoseGUI.setAngleForPart(as, tool, newPose); + // GUI aktualisieren um Werte in der Lore anzuzeigen + ArmorStandPoseGUI.openAxisDetailMenu(p, as, partName); + } } } \ No newline at end of file diff --git a/src/main/java/de/nexuslobby/modules/armorstandtools/ArmorStandPoseGUI.java b/src/main/java/de/nexuslobby/modules/armorstandtools/ArmorStandPoseGUI.java new file mode 100644 index 0000000..7f37c1d --- /dev/null +++ b/src/main/java/de/nexuslobby/modules/armorstandtools/ArmorStandPoseGUI.java @@ -0,0 +1,108 @@ +package de.nexuslobby.modules.armorstandtools; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.util.EulerAngle; +import java.util.Arrays; + +public class ArmorStandPoseGUI { + + // Schritt 1: Auswahl welches Körperteil (Die "letzte Reihe" ist jetzt hier) + public static void openPartSelectionMenu(Player p, ArmorStand as) { + Inventory inv = Bukkit.createInventory(null, 45, "Pose: Körperteil wählen"); + ItemStack filler = new ItemStack(Material.BLACK_STAINED_GLASS_PANE); + for (int i = 0; i < 45; i++) inv.setItem(i, filler); + + inv.setItem(10, createPartIcon(Material.PLAYER_HEAD, "§eKopf", as.getHeadPose())); + inv.setItem(12, createPartIcon(Material.IRON_CHESTPLATE, "§eTorso", as.getBodyPose())); + inv.setItem(14, createPartIcon(Material.STICK, "§eLinker Arm", as.getLeftArmPose())); + inv.setItem(16, createPartIcon(Material.STICK, "§eRechter Arm", as.getRightArmPose())); + inv.setItem(28, createPartIcon(Material.LEATHER_BOOTS, "§eLinkes Bein", as.getLeftLegPose())); + inv.setItem(30, createPartIcon(Material.LEATHER_BOOTS, "§eRechtes Bein", as.getRightLegPose())); + + inv.setItem(40, createNamedItem(Material.BARRIER, "§cZurück zum Hauptmenü")); + p.openInventory(inv); + } + + // Schritt 2: Detail-Einstellung der Achsen (X, Y, Z) + public static void openAxisDetailMenu(Player p, ArmorStand as, String partName) { + Inventory inv = Bukkit.createInventory(null, 27, "Achsen: " + partName); + ItemStack filler = new ItemStack(Material.GRAY_STAINED_GLASS_PANE); + for (int i = 0; i < 27; i++) inv.setItem(i, filler); + + ArmorStandTool tool = ArmorStandTool.valueOf(partName); + EulerAngle angle = getAngleForPart(as, tool); + + inv.setItem(11, createAxisItem(Material.RED_DYE, "§c§lX-Achse (Pitch)", angle.getX())); + inv.setItem(13, createAxisItem(Material.GREEN_DYE, "§a§lY-Achse (Yaw)", angle.getY())); + inv.setItem(15, createAxisItem(Material.BLUE_DYE, "§b§lZ-Achse (Roll)", angle.getZ())); + + inv.setItem(22, createNamedItem(Material.ARROW, "§7Zurück zur Auswahl")); + p.openInventory(inv); + } + + private static ItemStack createPartIcon(Material mat, String name, EulerAngle angle) { + ItemStack item = new ItemStack(mat); + ItemMeta meta = item.getItemMeta(); + meta.setDisplayName(name); + meta.setLore(Arrays.asList( + "§8Rotation:", + "§7X: §f" + Math.round(Math.toDegrees(angle.getX())) + "°", + "§7Y: §f" + Math.round(Math.toDegrees(angle.getY())) + "°", + "§7Z: §f" + Math.round(Math.toDegrees(angle.getZ())) + "°", + "", "§6Klicken zum Justieren" + )); + item.setItemMeta(meta); + return item; + } + + private static ItemStack createAxisItem(Material mat, String name, double angleRad) { + ItemStack item = new ItemStack(mat); + ItemMeta meta = item.getItemMeta(); + meta.setDisplayName(name); + meta.setLore(Arrays.asList( + "§7Aktueller Winkel: §f" + Math.round(Math.toDegrees(angleRad)) + "°", + "", + "§eLinksklick: §f+15° §8| §eRechtsklick: §f-15°", + "§6Shift-Klick: §f+1° §8| §6Shift-Rechts: §f-1°" + )); + item.setItemMeta(meta); + return item; + } + + private static ItemStack createNamedItem(Material mat, String name) { + ItemStack item = new ItemStack(mat); + ItemMeta meta = item.getItemMeta(); + meta.setDisplayName(name); + item.setItemMeta(meta); + return item; + } + + public static EulerAngle getAngleForPart(ArmorStand as, ArmorStandTool part) { + return switch (part) { + case HEAD_ROT -> as.getHeadPose(); + case BODY_ROT -> as.getBodyPose(); + case L_ARM_ROT -> as.getLeftArmPose(); + case R_ARM_ROT -> as.getRightArmPose(); + case L_LEG_ROT -> as.getLeftLegPose(); + case R_LEG_ROT -> as.getRightLegPose(); + default -> EulerAngle.ZERO; + }; + } + + public static void setAngleForPart(ArmorStand as, ArmorStandTool part, EulerAngle angle) { + switch (part) { + case HEAD_ROT -> as.setHeadPose(angle); + case BODY_ROT -> as.setBodyPose(angle); + case L_ARM_ROT -> as.setLeftArmPose(angle); + case R_ARM_ROT -> as.setRightArmPose(angle); + case L_LEG_ROT -> as.setLeftLegPose(angle); + case R_LEG_ROT -> as.setRightLegPose(angle); + } + } +} \ No newline at end of file diff --git a/src/main/java/de/nexuslobby/modules/armorstandtools/ArmorStandTool.java b/src/main/java/de/nexuslobby/modules/armorstandtools/ArmorStandTool.java index d8fae30..678342d 100644 --- a/src/main/java/de/nexuslobby/modules/armorstandtools/ArmorStandTool.java +++ b/src/main/java/de/nexuslobby/modules/armorstandtools/ArmorStandTool.java @@ -10,80 +10,27 @@ import java.util.ArrayList; import java.util.List; public enum ArmorStandTool { - // Sichtbarkeit & Basis-Attribute - INVIS(Material.GLASS_PANE, 10) { - @Override public void execute(ArmorStand as, Player p) { as.setVisible(!as.isVisible()); } - }, - ARMS(Material.STICK, 11) { - @Override public void execute(ArmorStand as, Player p) { as.setArms(!as.hasArms()); } - }, - BASE(Material.STONE_SLAB, 12) { - @Override public void execute(ArmorStand as, Player p) { as.setBasePlate(!as.hasBasePlate()); } - }, - SIZE(Material.PUMPKIN_SEEDS, 13) { - @Override public void execute(ArmorStand as, Player p) { as.setSmall(!as.isSmall()); } - }, - GRAV(Material.ANVIL, 14) { - @Override public void execute(ArmorStand as, Player p) { as.setGravity(!as.hasGravity()); } - }, - INVUL(Material.BEDROCK, 15) { - @Override public void execute(ArmorStand as, Player p) { as.setInvulnerable(!as.isInvulnerable()); } - }, + // --- HAUPTMENÜ ATTRIBUTE --- + INVIS(Material.GLASS_PANE, 10), + ARMS(Material.STICK, 11), + BASE(Material.STONE_SLAB, 12), + SIZE(Material.PUMPKIN_SEEDS, 13), + GRAV(Material.ANVIL, 14), + INVUL(Material.BEDROCK, 15), + SET_NAME(Material.NAME_TAG, 16), - // Hologramm / Name setzen - SET_NAME(Material.NAME_TAG, 16) { - @Override public void execute(ArmorStand as, Player p) { - p.closeInventory(); - p.sendMessage(" "); - p.sendMessage("§8§m--------------------------------------"); - p.sendMessage("§6§lHologramm-Editor"); - p.sendMessage("§7Nutze: §e/nexuscmd name §7um den Namen zu setzen."); - p.sendMessage("§7Nutze: §e/nexuscmd name none §7um den Namen zu entfernen."); - p.sendMessage("§7Farbcodes mit §6& §7werden unterstützt."); - p.sendMessage("§8§m--------------------------------------"); - AST.selectedArmorStand.put(p.getUniqueId(), as); - } - }, + // Zentrales Item zum Öffnen des Pose-Editors + OPEN_POSE_EDITOR(Material.ARMOR_STAND, 31), - // KÖRPERGESTALTUNG (Rotationen) - HEAD_ROT(Material.PLAYER_HEAD, 28) { - @Override public void execute(ArmorStand as, Player p) { - as.setHeadPose(as.getHeadPose().add(Math.toRadians(15), 0, 0)); - } - }, - BODY_ROT(Material.IRON_CHESTPLATE, 29) { - @Override public void execute(ArmorStand as, Player p) { - as.setBodyPose(as.getBodyPose().add(0, Math.toRadians(15), 0)); - } - }, - L_ARM_ROT(Material.STICK, 30) { - @Override public void execute(ArmorStand as, Player p) { - as.setLeftArmPose(as.getLeftArmPose().add(Math.toRadians(15), 0, 0)); - } - }, - R_ARM_ROT(Material.STICK, 31) { - @Override public void execute(ArmorStand as, Player p) { - as.setRightArmPose(as.getRightArmPose().add(Math.toRadians(15), 0, 0)); - } - }, - L_LEG_ROT(Material.LEATHER_BOOTS, 32) { - @Override public void execute(ArmorStand as, Player p) { - as.setLeftLegPose(as.getLeftLegPose().add(Math.toRadians(15), 0, 0)); - } - }, - R_LEG_ROT(Material.LEATHER_BOOTS, 33) { - @Override public void execute(ArmorStand as, Player p) { - as.setRightLegPose(as.getRightLegPose().add(Math.toRadians(15), 0, 0)); - } - }, + // --- DIESE TEILE WANDERN IN DAS UNTERMENÜ (isForGui = false) --- + HEAD_ROT(Material.PLAYER_HEAD, -1), + BODY_ROT(Material.IRON_CHESTPLATE, -1), + L_ARM_ROT(Material.STICK, -1), + R_ARM_ROT(Material.STICK, -1), + L_LEG_ROT(Material.LEATHER_BOOTS, -1), + R_LEG_ROT(Material.LEATHER_BOOTS, -1), - REMOVE(Material.BARRIER, 40) { - @Override public void execute(ArmorStand as, Player p) { - as.remove(); - p.closeInventory(); - p.sendMessage("§cNexus ArmorStand entfernt."); - } - }; + REMOVE(Material.BARRIER, 40); private final Material material; private final int slot; @@ -93,20 +40,40 @@ public enum ArmorStandTool { this.slot = slot; } - public abstract void execute(ArmorStand as, Player p); - + public void execute(ArmorStand as, Player p) { + switch (this) { + case INVIS -> as.setVisible(!as.isVisible()); + case ARMS -> as.setArms(!as.hasArms()); + case BASE -> as.setBasePlate(!as.hasBasePlate()); + case SIZE -> as.setSmall(!as.isSmall()); + case GRAV -> as.setGravity(!as.hasGravity()); + case INVUL -> as.setInvulnerable(!as.isInvulnerable()); + case SET_NAME -> { + p.closeInventory(); + p.sendMessage("§6§lHologramm-Editor: §7Nutze §e/nexuscmd name "); + AST.selectedArmorStand.put(p.getUniqueId(), as); + } + case REMOVE -> { + as.remove(); + p.closeInventory(); + p.sendMessage("§cNexus ArmorStand entfernt."); + } + case OPEN_POSE_EDITOR -> ArmorStandPoseGUI.openPartSelectionMenu(p, as); + default -> {} // Pose-Detailklicks werden direkt im PoseGUI/Listener behandelt + } + } + public int getSlot() { return slot; } - - // Diese Methode hat im letzten Code gefehlt: - public boolean isForGui() { return true; } + public boolean isForGui() { return slot != -1; } public ItemStack updateLore(ArmorStand as) { ItemStack item = new ItemStack(material); ItemMeta meta = item.getItemMeta(); if (meta != null) { - meta.setDisplayName("§6" + this.name().replace("_", " ")); + String name = (this == OPEN_POSE_EDITOR) ? "§a§lPose Editor öffnen" : "§6" + this.name().replace("_", " "); + meta.setDisplayName(name); List lore = new ArrayList<>(); - lore.add("§7Klicken zum Bearbeiten"); + lore.add("§7Klicken zum Verwalten"); meta.setLore(lore); item.setItemMeta(meta); } @@ -116,6 +83,8 @@ public enum ArmorStandTool { public static ArmorStandTool get(ItemStack item) { if (item == null || !item.hasItemMeta() || !item.getItemMeta().hasDisplayName()) return null; String name = ChatColor.stripColor(item.getItemMeta().getDisplayName()).replace(" ", "_"); + // Spezielles Mapping für den Pose-Editor-Button im Listener + if (name.equals("Pose_Editor_öffnen")) return OPEN_POSE_EDITOR; try { return valueOf(name); } catch (Exception e) { return null; } } } \ No newline at end of file diff --git a/src/main/java/de/nexuslobby/modules/armorstandtools/DynamicArmorStandModule.java b/src/main/java/de/nexuslobby/modules/armorstandtools/DynamicArmorStandModule.java index 78b9ef2..3428ea9 100644 --- a/src/main/java/de/nexuslobby/modules/armorstandtools/DynamicArmorStandModule.java +++ b/src/main/java/de/nexuslobby/modules/armorstandtools/DynamicArmorStandModule.java @@ -3,25 +3,20 @@ package de.nexuslobby.modules.armorstandtools; import de.nexuslobby.NexusLobby; import de.nexuslobby.api.Module; import org.bukkit.Bukkit; +import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.NamespacedKey; +import org.bukkit.Particle; import org.bukkit.World; import org.bukkit.entity.ArmorStand; import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.persistence.PersistentDataType; +import org.bukkit.util.EulerAngle; -import java.time.LocalTime; - -/** - * DynamicArmorStandModule - * Reagiert auf Uhrzeit und Wetter. - * Nutzt PersistentData, damit die Einstellung auch nach einem Server-Neustart bleibt. - */ public class DynamicArmorStandModule implements Module { - // Der Key muss mit dem Command übereinstimmen oder das Modul nutzt Tags. - // Wir nutzen hier PersistentData, da es sicherer ist als Tags. private final NamespacedKey npcKey = new NamespacedKey(NexusLobby.getInstance(), "dynamic_npc"); @Override @@ -32,88 +27,106 @@ public class DynamicArmorStandModule implements Module { @Override public void onEnable() { startUpdateTask(); - Bukkit.getLogger().info("[NexusLobby] DynamicArmorStandModule wurde aktiviert."); + Bukkit.getLogger().info("§a[NexusLobby] DynamicArmorStandModule aktiv: Ingame-Zeit & Sternen-Effekt geladen."); } - /** - * Startet einen Timer, der alle 5 Sekunden (100 Ticks) prüft. - */ private void startUpdateTask() { - Bukkit.getScheduler().runTaskTimer(NexusLobby.getInstance(), () -> { - updateAllArmorStands(); - }, 100L, 100L); // Alle 5 Sekunden statt 2 Minuten + // Alle 5 Ticks (0.25s) für flüssige Partikel + Bukkit.getScheduler().runTaskTimer(NexusLobby.getInstance(), this::updateAllArmorStands, 20L, 5L); } public void updateAllArmorStands() { - int hour = LocalTime.now().getHour(); - for (World world : Bukkit.getWorlds()) { - boolean isRaining = world.hasStorm(); + long time = world.getTime(); + boolean isIngameNight = (time >= 13000 && time <= 23000); for (Entity entity : world.getEntities()) { - if (entity instanceof ArmorStand armorStand) { - // PRÜFUNG: Entweder PersistentData ODER Scoreboard-Tag "as_dynamic" - if (armorStand.getPersistentDataContainer().has(npcKey, PersistentDataType.BYTE) || - armorStand.getScoreboardTags().contains("as_dynamic")) { + if (entity instanceof ArmorStand as) { + if (as.getPersistentDataContainer().has(npcKey, PersistentDataType.BYTE) || + as.getScoreboardTags().contains("as_dynamic")) { - applyDynamicChanges(armorStand, hour, isRaining); + applyDynamicChanges(as, isIngameNight); + + if (isIngameNight && as.getEquipment().getItemInOffHand().getType() == Material.TORCH) { + spawnStarParticles(as); + } } } } } } - private void applyDynamicChanges(ArmorStand as, int hour, boolean isRaining) { - // --- ZEIT-LOGIK (Nacht: 20:00 - 06:00) --- - if (hour >= 20 || hour <= 6) { + private void applyDynamicChanges(ArmorStand as, boolean isNight) { + if (!as.hasArms()) as.setArms(true); + + // --- SPIELER-NÄHE --- + boolean playerNearby = false; + for (Entity nearby : as.getNearbyEntities(2.0, 2.0, 2.0)) { + if (nearby instanceof Player) { playerNearby = true; break; } + } + + if (playerNearby) { + if (as.getEquipment().getItemInMainHand().getType() != Material.STONE_SWORD) { + as.getEquipment().setItemInMainHand(new ItemStack(Material.STONE_SWORD)); + as.setRightArmPose(new EulerAngle(Math.toRadians(-80), Math.toRadians(-10), 0)); + } + } else { + if (as.getEquipment().getItemInMainHand().getType() == Material.STONE_SWORD) { + as.getEquipment().setItemInMainHand(null); + as.setRightArmPose(EulerAngle.ZERO); + } + } + + // --- INGAME-ZEIT --- + if (isNight) { if (as.getEquipment().getItemInOffHand().getType() != Material.TORCH) { as.getEquipment().setItemInOffHand(new ItemStack(Material.TORCH)); + as.setLeftArmPose(new EulerAngle(Math.toRadians(-50), Math.toRadians(15), 0)); } } else { if (as.getEquipment().getItemInOffHand().getType() == Material.TORCH) { as.getEquipment().setItemInOffHand(null); - } - } - - // --- WETTER-LOGIK (Regen) --- - if (isRaining) { - if (as.getEquipment().getHelmet() == null || as.getEquipment().getHelmet().getType() != Material.LEATHER_HELMET) { - as.getEquipment().setHelmet(new ItemStack(Material.LEATHER_HELMET)); - } - } else { - // Nur ausziehen, wenn es hellwach ist und nicht regnet - if (hour < 20 && hour > 6) { - if (as.getEquipment().getHelmet() != null && as.getEquipment().getHelmet().getType() == Material.LEATHER_HELMET) { - as.getEquipment().setHelmet(null); - } + as.setLeftArmPose(EulerAngle.ZERO); } } } - /** - * Schaltet den Status um. Wird vom ArmorStandCommand aufgerufen. - */ + private void spawnStarParticles(ArmorStand as) { + Location loc = as.getLocation(); + double yawRad = Math.toRadians(loc.getYaw()); + + // Vektoren für Blickrichtung und Seite + double dirX = -Math.sin(yawRad); + double dirZ = Math.cos(yawRad); + double sideX = Math.cos(yawRad); + double sideZ = Math.sin(yawRad); + + // Position: Vorne (0.4), Links (0.5), Oben (1.45) + double finalX = loc.getX() + (dirX * 0.8) + (sideX * 0.2); + double finalY = loc.getY() + 1.45; + double finalZ = loc.getZ() + (dirZ * 0.8) + (sideZ * 0.2); + + Location particleLoc = new Location(as.getWorld(), finalX, finalY, finalZ); + as.getWorld().spawnParticle(Particle.WAX_OFF, particleLoc, 2, 0.05, 0.05, 0.05, 0.01); + } + public void toggleDynamicStatus(ArmorStand as) { if (as.getPersistentDataContainer().has(npcKey, PersistentDataType.BYTE)) { - // Deaktivieren as.getPersistentDataContainer().remove(npcKey); - as.removeScoreboardTag("as_dynamic"); // Zur Sicherheit beides entfernen + as.removeScoreboardTag("as_dynamic"); + as.getEquipment().setItemInMainHand(null); as.getEquipment().setItemInOffHand(null); - as.getEquipment().setHelmet(null); + as.setRightArmPose(EulerAngle.ZERO); + as.setLeftArmPose(EulerAngle.ZERO); } else { - // Aktivieren as.getPersistentDataContainer().set(npcKey, PersistentDataType.BYTE, (byte) 1); as.addScoreboardTag("as_dynamic"); - - // Sofortige visuelle Rückmeldung - int hour = LocalTime.now().getHour(); - boolean isRaining = as.getWorld().hasStorm(); - applyDynamicChanges(as, hour, isRaining); + applyDynamicChanges(as, (as.getWorld().getTime() >= 13000 && as.getWorld().getTime() <= 23000)); } } @Override public void onDisable() { - // Nichts zu tun + // Bereinigung falls nötig } } \ No newline at end of file