Update from Git Manager GUI
This commit is contained in:
@@ -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);
|
||||||
|
|
||||||
|
// ArmorStand für diesen Spieler zwischenspeichern
|
||||||
AST.selectedArmorStand.put(p.getUniqueId(), as);
|
AST.selectedArmorStand.put(p.getUniqueId(), as);
|
||||||
|
|
||||||
|
// Haupt-GUI öffnen
|
||||||
new ArmorStandGUI(as, p);
|
new ArmorStandGUI(as, p);
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Normale Interaktion (Befehl ausführen)
|
|
||||||
Set<String> 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)
|
@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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
ArmorStandTool tool = ArmorStandTool.get(item);
|
||||||
if (tool != null) {
|
if (tool != null) {
|
||||||
tool.execute(as, p);
|
tool.execute(as, p);
|
||||||
// GUI aktualisieren, falls noch offen (SET_NAME schließt es z.B.)
|
// GUI erneuern (außer sie wurde durch execute geschlossen)
|
||||||
if (p.getOpenInventory().getTitle().equals("Nexus ArmorStand Editor")) {
|
if (p.getOpenInventory().getTitle().equals(title)) {
|
||||||
new ArmorStandGUI(as, p);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user