Update from Git Manager GUI

This commit is contained in:
2026-01-23 22:08:30 +01:00
parent aac2482511
commit 4c847401a0
4 changed files with 301 additions and 179 deletions

View File

@@ -1,9 +1,5 @@
package de.nexuslobby.modules.armorstandtools; 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.Material;
import org.bukkit.entity.ArmorStand; import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@@ -14,84 +10,120 @@ import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.player.PlayerInteractAtEntityEvent; import org.bukkit.event.player.PlayerInteractAtEntityEvent;
import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.util.EulerAngle;
import java.util.Set;
public class ASTListener implements Listener { public class ASTListener implements Listener {
/**
* Erkennt den Rechtsklick auf den ArmorStand.
* Öffnet bei Sneak + Rechtsklick die Haupt-GUI.
*/
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onInteract(PlayerInteractAtEntityEvent event) { public void onInteract(PlayerInteractAtEntityEvent event) {
// Sicherstellen, dass nur die Haupthand zählt (verhindert Doppel-Trigger)
if (event.getHand() != EquipmentSlot.HAND) return; if (event.getHand() != EquipmentSlot.HAND) return;
if (!(event.getRightClicked() instanceof ArmorStand as)) return; if (!(event.getRightClicked() instanceof ArmorStand as)) return;
Player p = event.getPlayer(); Player p = event.getPlayer();
// Sneak-Rechtsklick zum Bearbeiten // Prüfen ob Spieler schleicht (Shift)
if (p.isSneaking()) { if (p.isSneaking()) {
if (p.hasPermission("nexuslobby.armorstand.use")) { if (p.hasPermission("nexuslobby.armorstand.use")) {
event.setCancelled(true); event.setCancelled(true);
AST.selectedArmorStand.put(p.getUniqueId(), as);
new ArmorStandGUI(as, p);
}
return;
}
// Normale Interaktion (Befehl ausführen) // ArmorStand für diesen Spieler zwischenspeichern
Set<String> tags = as.getScoreboardTags(); AST.selectedArmorStand.put(p.getUniqueId(), as);
for (String tag : tags) {
if (tag.startsWith("ascmd:")) { // Haupt-GUI öffnen
event.setCancelled(true); new ArmorStandGUI(as, p);
executeNexusCommand(p, tag);
break;
} }
} }
} }
/**
* Verwaltet alle Klicks innerhalb der verschiedenen GUIs.
*/
@EventHandler(priority = EventPriority.LOW) @EventHandler(priority = EventPriority.LOW)
public void onInventoryClick(InventoryClickEvent event) { 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); event.setCancelled(true);
if (!(event.getWhoClicked() instanceof Player p)) return; 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()); ArmorStand as = AST.selectedArmorStand.get(p.getUniqueId());
if (as == null || !as.isValid()) { if (as == null || !as.isValid()) {
p.closeInventory(); p.closeInventory();
return; return;
} }
ArmorStandTool tool = ArmorStandTool.get(item); ItemStack item = event.getCurrentItem();
if (tool != null) { if (item == null || item.getType() == Material.AIR) return;
tool.execute(as, p);
// GUI aktualisieren, falls noch offen (SET_NAME schließt es z.B.) // --- 1. LOGIK: HAUPTMENÜ ---
if (p.getOpenInventory().getTitle().equals("Nexus ArmorStand Editor")) { if (title.equals("Nexus ArmorStand Editor")) {
new ArmorStandGUI(as, p); 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);
}
} }
} }
}
private void executeNexusCommand(Player p, String tag) { // --- 2. LOGIK: KÖRPERTEIL-AUSWAHL ---
try { else if (title.equals("Pose: Körperteil wählen")) {
String[] parts = tag.split(":", 3); if (item.getType() == Material.BARRIER) {
if (parts.length < 3) return; new ArmorStandGUI(as, p);
String type = parts[1].toLowerCase(); return;
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);
} }
} catch (Exception e) { e.printStackTrace(); }
}
private void sendToBungee(Player p, String server) { String targetPart = null;
ByteArrayDataOutput out = ByteStreams.newDataOutput(); switch (item.getType()) {
out.writeUTF("Connect"); case PLAYER_HEAD -> targetPart = "HEAD_ROT";
out.writeUTF(server); case IRON_CHESTPLATE -> targetPart = "BODY_ROT";
p.sendPluginMessage(NexusLobby.getInstance(), "BungeeCord", out.toByteArray()); 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);
}
}
// --- 3. LOGIK: ACHSEN-FEINJUSTIERUNG ---
else if (title.startsWith("Achsen:")) {
if (item.getType() == Material.ARROW) {
ArmorStandPoseGUI.openPartSelectionMenu(p, as);
return;
}
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;
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);
}
} }
} }

View File

@@ -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);
}
}
}

View File

@@ -10,80 +10,27 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
public enum ArmorStandTool { public enum ArmorStandTool {
// Sichtbarkeit & Basis-Attribute // --- HAUPTMENÜ ATTRIBUTE ---
INVIS(Material.GLASS_PANE, 10) { INVIS(Material.GLASS_PANE, 10),
@Override public void execute(ArmorStand as, Player p) { as.setVisible(!as.isVisible()); } ARMS(Material.STICK, 11),
}, BASE(Material.STONE_SLAB, 12),
ARMS(Material.STICK, 11) { SIZE(Material.PUMPKIN_SEEDS, 13),
@Override public void execute(ArmorStand as, Player p) { as.setArms(!as.hasArms()); } GRAV(Material.ANVIL, 14),
}, INVUL(Material.BEDROCK, 15),
BASE(Material.STONE_SLAB, 12) { SET_NAME(Material.NAME_TAG, 16),
@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()); }
},
// Hologramm / Name setzen // Zentrales Item zum Öffnen des Pose-Editors
SET_NAME(Material.NAME_TAG, 16) { OPEN_POSE_EDITOR(Material.ARMOR_STAND, 31),
@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 <Text> §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);
}
},
// KÖRPERGESTALTUNG (Rotationen) // --- DIESE TEILE WANDERN IN DAS UNTERMENÜ (isForGui = false) ---
HEAD_ROT(Material.PLAYER_HEAD, 28) { HEAD_ROT(Material.PLAYER_HEAD, -1),
@Override public void execute(ArmorStand as, Player p) { BODY_ROT(Material.IRON_CHESTPLATE, -1),
as.setHeadPose(as.getHeadPose().add(Math.toRadians(15), 0, 0)); L_ARM_ROT(Material.STICK, -1),
} R_ARM_ROT(Material.STICK, -1),
}, L_LEG_ROT(Material.LEATHER_BOOTS, -1),
BODY_ROT(Material.IRON_CHESTPLATE, 29) { R_LEG_ROT(Material.LEATHER_BOOTS, -1),
@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));
}
},
REMOVE(Material.BARRIER, 40) { REMOVE(Material.BARRIER, 40);
@Override public void execute(ArmorStand as, Player p) {
as.remove();
p.closeInventory();
p.sendMessage("§cNexus ArmorStand entfernt.");
}
};
private final Material material; private final Material material;
private final int slot; private final int slot;
@@ -93,20 +40,40 @@ public enum ArmorStandTool {
this.slot = slot; 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 <Text>");
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; } public int getSlot() { return slot; }
public boolean isForGui() { return slot != -1; }
// Diese Methode hat im letzten Code gefehlt:
public boolean isForGui() { return true; }
public ItemStack updateLore(ArmorStand as) { public ItemStack updateLore(ArmorStand as) {
ItemStack item = new ItemStack(material); ItemStack item = new ItemStack(material);
ItemMeta meta = item.getItemMeta(); ItemMeta meta = item.getItemMeta();
if (meta != null) { 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<String> lore = new ArrayList<>(); List<String> lore = new ArrayList<>();
lore.add("§7Klicken zum Bearbeiten"); lore.add("§7Klicken zum Verwalten");
meta.setLore(lore); meta.setLore(lore);
item.setItemMeta(meta); item.setItemMeta(meta);
} }
@@ -116,6 +83,8 @@ public enum ArmorStandTool {
public static ArmorStandTool get(ItemStack item) { public static ArmorStandTool get(ItemStack item) {
if (item == null || !item.hasItemMeta() || !item.getItemMeta().hasDisplayName()) return null; if (item == null || !item.hasItemMeta() || !item.getItemMeta().hasDisplayName()) return null;
String name = ChatColor.stripColor(item.getItemMeta().getDisplayName()).replace(" ", "_"); 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; } try { return valueOf(name); } catch (Exception e) { return null; }
} }
} }

View File

@@ -3,25 +3,20 @@ package de.nexuslobby.modules.armorstandtools;
import de.nexuslobby.NexusLobby; import de.nexuslobby.NexusLobby;
import de.nexuslobby.api.Module; import de.nexuslobby.api.Module;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.NamespacedKey; import org.bukkit.NamespacedKey;
import org.bukkit.Particle;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.entity.ArmorStand; import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.persistence.PersistentDataType; 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 { 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"); private final NamespacedKey npcKey = new NamespacedKey(NexusLobby.getInstance(), "dynamic_npc");
@Override @Override
@@ -32,88 +27,106 @@ public class DynamicArmorStandModule implements Module {
@Override @Override
public void onEnable() { public void onEnable() {
startUpdateTask(); 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() { private void startUpdateTask() {
Bukkit.getScheduler().runTaskTimer(NexusLobby.getInstance(), () -> { // Alle 5 Ticks (0.25s) für flüssige Partikel
updateAllArmorStands(); Bukkit.getScheduler().runTaskTimer(NexusLobby.getInstance(), this::updateAllArmorStands, 20L, 5L);
}, 100L, 100L); // Alle 5 Sekunden statt 2 Minuten
} }
public void updateAllArmorStands() { public void updateAllArmorStands() {
int hour = LocalTime.now().getHour();
for (World world : Bukkit.getWorlds()) { for (World world : Bukkit.getWorlds()) {
boolean isRaining = world.hasStorm(); long time = world.getTime();
boolean isIngameNight = (time >= 13000 && time <= 23000);
for (Entity entity : world.getEntities()) { for (Entity entity : world.getEntities()) {
if (entity instanceof ArmorStand armorStand) { if (entity instanceof ArmorStand as) {
// PRÜFUNG: Entweder PersistentData ODER Scoreboard-Tag "as_dynamic" if (as.getPersistentDataContainer().has(npcKey, PersistentDataType.BYTE) ||
if (armorStand.getPersistentDataContainer().has(npcKey, PersistentDataType.BYTE) || as.getScoreboardTags().contains("as_dynamic")) {
armorStand.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) { private void applyDynamicChanges(ArmorStand as, boolean isNight) {
// --- ZEIT-LOGIK (Nacht: 20:00 - 06:00) --- if (!as.hasArms()) as.setArms(true);
if (hour >= 20 || hour <= 6) {
// --- 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) { if (as.getEquipment().getItemInOffHand().getType() != Material.TORCH) {
as.getEquipment().setItemInOffHand(new ItemStack(Material.TORCH)); as.getEquipment().setItemInOffHand(new ItemStack(Material.TORCH));
as.setLeftArmPose(new EulerAngle(Math.toRadians(-50), Math.toRadians(15), 0));
} }
} else { } else {
if (as.getEquipment().getItemInOffHand().getType() == Material.TORCH) { if (as.getEquipment().getItemInOffHand().getType() == Material.TORCH) {
as.getEquipment().setItemInOffHand(null); as.getEquipment().setItemInOffHand(null);
} as.setLeftArmPose(EulerAngle.ZERO);
}
// --- 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);
}
} }
} }
} }
/** private void spawnStarParticles(ArmorStand as) {
* Schaltet den Status um. Wird vom ArmorStandCommand aufgerufen. 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) { public void toggleDynamicStatus(ArmorStand as) {
if (as.getPersistentDataContainer().has(npcKey, PersistentDataType.BYTE)) { if (as.getPersistentDataContainer().has(npcKey, PersistentDataType.BYTE)) {
// Deaktivieren
as.getPersistentDataContainer().remove(npcKey); 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().setItemInOffHand(null);
as.getEquipment().setHelmet(null); as.setRightArmPose(EulerAngle.ZERO);
as.setLeftArmPose(EulerAngle.ZERO);
} else { } else {
// Aktivieren
as.getPersistentDataContainer().set(npcKey, PersistentDataType.BYTE, (byte) 1); as.getPersistentDataContainer().set(npcKey, PersistentDataType.BYTE, (byte) 1);
as.addScoreboardTag("as_dynamic"); as.addScoreboardTag("as_dynamic");
applyDynamicChanges(as, (as.getWorld().getTime() >= 13000 && as.getWorld().getTime() <= 23000));
// Sofortige visuelle Rückmeldung
int hour = LocalTime.now().getHour();
boolean isRaining = as.getWorld().hasStorm();
applyDynamicChanges(as, hour, isRaining);
} }
} }
@Override @Override
public void onDisable() { public void onDisable() {
// Nichts zu tun // Bereinigung falls nötig
} }
} }