Update from Git Manager GUI
This commit is contained in:
326
src/main/java/me/viper/teamplugin/gui/ApplicationGUI.java
Normal file
326
src/main/java/me/viper/teamplugin/gui/ApplicationGUI.java
Normal file
@@ -0,0 +1,326 @@
|
||||
package me.viper.teamplugin.gui;
|
||||
|
||||
import me.viper.teamplugin.Main;
|
||||
import me.viper.teamplugin.manager.ApplicationManager;
|
||||
import me.viper.teamplugin.manager.AuditLog;
|
||||
import me.viper.teamplugin.manager.DataManager;
|
||||
import me.viper.teamplugin.manager.LangManager;
|
||||
import me.viper.teamplugin.util.Utils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.bukkit.inventory.meta.SkullMeta;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* ApplicationGUI – Admin-only GUI for reviewing and managing pending applications.
|
||||
*
|
||||
* LIST VIEW (54 slots) /team apply [admin only]
|
||||
* ┌───────────────────────────────────┐
|
||||
* │ Header (cyan glass) │
|
||||
* │ Applicant heads slots 9-44 │ one PLAYER_HEAD per pending application
|
||||
* │ Footer: ←45 | close 49 | →53 │
|
||||
* └───────────────────────────────────┘
|
||||
*
|
||||
* DETAIL VIEW (54 slots)
|
||||
* ┌───────────────────────────────────┐
|
||||
* │ Background filler │
|
||||
* │ slot 13 Applicant head + info │
|
||||
* │ slot 20 Accept (GREEN) │
|
||||
* │ slot 24 Deny (RED) │
|
||||
* │ slot 49 Back │
|
||||
* └───────────────────────────────────┘
|
||||
*
|
||||
* Players apply purely via command: /team bewerben <rank> [reason]
|
||||
* No GUI is shown to the applicant.
|
||||
*/
|
||||
public class ApplicationGUI {
|
||||
|
||||
private static final int ITEMS_PER_PAGE = 36;
|
||||
|
||||
/** UUID → current list-view page */
|
||||
private static final Map<UUID, Integer> CURRENT_PAGE = new HashMap<>();
|
||||
/** UUID → applicant name currently shown in the detail view */
|
||||
private static final Map<UUID, String> VIEWING_PLAYER = new HashMap<>();
|
||||
|
||||
// ── Open: list view ───────────────────────────────────────────────
|
||||
|
||||
public static void openApplicationList(Player admin, int page) {
|
||||
List<String[]> all = ApplicationManager.getAllApplications(); // [rank, name, iso, reason]
|
||||
int totalPages = Math.max(1, (int) Math.ceil((double) all.size() / ITEMS_PER_PAGE));
|
||||
page = Math.max(0, Math.min(page, totalPages - 1));
|
||||
CURRENT_PAGE.put(admin.getUniqueId(), page);
|
||||
|
||||
Inventory inv = Bukkit.createInventory(null, 54, Utils.color(getSelectionTitle()));
|
||||
|
||||
// Header row
|
||||
ItemStack hGlass = filler(Material.CYAN_STAINED_GLASS_PANE);
|
||||
for (int i = 0; i < 9; i++) inv.setItem(i, hGlass);
|
||||
|
||||
// Footer row
|
||||
ItemStack fGlass = filler(Material.BLACK_STAINED_GLASS_PANE);
|
||||
for (int i = 45; i < 54; i++) inv.setItem(i, fGlass);
|
||||
|
||||
// Info item (slot 4)
|
||||
inv.setItem(4, buildInfoItem(all.size(), page + 1, totalPages));
|
||||
|
||||
// Applicant items (slots 9-44)
|
||||
int start = page * ITEMS_PER_PAGE;
|
||||
for (int i = 0; i < ITEMS_PER_PAGE; i++) {
|
||||
int idx = start + i;
|
||||
if (idx >= all.size()) break;
|
||||
inv.setItem(9 + i, buildApplicantItem(all.get(idx)));
|
||||
}
|
||||
|
||||
// Navigation
|
||||
if (page > 0) inv.setItem(45, navItem(Material.ARROW,
|
||||
Utils.color(LangManager.get("mailbox_prev_page")), List.of()));
|
||||
inv.setItem(49, navItem(Material.BARRIER,
|
||||
Utils.color(LangManager.get("apply_gui_close")), List.of()));
|
||||
if (page + 1 < totalPages) inv.setItem(53, navItem(Material.ARROW,
|
||||
Utils.color(LangManager.get("mailbox_next_page")), List.of()));
|
||||
|
||||
admin.openInventory(inv);
|
||||
}
|
||||
|
||||
// ── Open: detail view ─────────────────────────────────────────────
|
||||
|
||||
private static void openDetail(Player admin, String[] app) {
|
||||
// app: [rank, name, iso, reason]
|
||||
VIEWING_PLAYER.put(admin.getUniqueId(), app[1]);
|
||||
|
||||
Inventory inv = Bukkit.createInventory(null, 54, Utils.color(getDetailTitle()));
|
||||
|
||||
ItemStack bg = filler(Material.GRAY_STAINED_GLASS_PANE);
|
||||
for (int i = 0; i < 54; i++) inv.setItem(i, bg);
|
||||
|
||||
// Applicant head + full info (slot 13)
|
||||
inv.setItem(13, buildDetailItem(app));
|
||||
|
||||
// Accept (slot 20)
|
||||
String rankDisplay = Main.getInstance().getConfig()
|
||||
.getString("rank-settings." + app[0] + ".display", app[0]);
|
||||
inv.setItem(20, navItem(Material.GREEN_CONCRETE,
|
||||
Utils.color("&a&l" + LangManager.get("apply_accepted_btn")),
|
||||
List.of(Utils.color("&7" + app[1] + " &8→ " + rankDisplay))));
|
||||
|
||||
// Deny (slot 24)
|
||||
inv.setItem(24, navItem(Material.RED_CONCRETE,
|
||||
Utils.color("&c&l" + LangManager.get("apply_denied_btn")),
|
||||
List.of(Utils.color("&7" + LangManager.get("apply_deny_lore")))));
|
||||
|
||||
// Back (slot 49)
|
||||
inv.setItem(49, navItem(Material.ARROW,
|
||||
Utils.color(LangManager.get("mailbox_back_btn")), List.of()));
|
||||
|
||||
admin.openInventory(inv);
|
||||
}
|
||||
|
||||
// ── Click handlers ────────────────────────────────────────────────
|
||||
|
||||
public static void handleSelectionClick(Player admin, InventoryClickEvent e) {
|
||||
e.setCancelled(true);
|
||||
int slot = e.getRawSlot();
|
||||
ItemStack clicked = e.getCurrentItem();
|
||||
if (clicked == null || clicked.getType().isAir()) return;
|
||||
|
||||
int page = CURRENT_PAGE.getOrDefault(admin.getUniqueId(), 0);
|
||||
|
||||
switch (slot) {
|
||||
case 45 -> openApplicationList(admin, page - 1);
|
||||
case 49 -> { cleanup(admin.getUniqueId()); admin.closeInventory(); }
|
||||
case 53 -> openApplicationList(admin, page + 1);
|
||||
default -> {
|
||||
if (slot >= 9 && slot <= 44) {
|
||||
List<String[]> all = ApplicationManager.getAllApplications();
|
||||
int idx = page * ITEMS_PER_PAGE + (slot - 9);
|
||||
if (idx < all.size()) openDetail(admin, all.get(idx));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void handleDetailClick(Player admin, InventoryClickEvent e) {
|
||||
e.setCancelled(true);
|
||||
int slot = e.getRawSlot();
|
||||
ItemStack clicked = e.getCurrentItem();
|
||||
if (clicked == null || clicked.getType().isAir()) return;
|
||||
|
||||
String applicantName = VIEWING_PLAYER.get(admin.getUniqueId());
|
||||
if (applicantName == null) { admin.closeInventory(); return; }
|
||||
|
||||
int page = CURRENT_PAGE.getOrDefault(admin.getUniqueId(), 0);
|
||||
String appliedRank = ApplicationManager.findApplication(applicantName);
|
||||
|
||||
switch (slot) {
|
||||
case 20 -> { // Accept
|
||||
if (appliedRank != null) {
|
||||
ApplicationManager.removeApplication(applicantName);
|
||||
DataManager.addMember(appliedRank, applicantName);
|
||||
AuditLog.log(AuditLog.APPLY_ACCEPT, admin.getName(),
|
||||
applicantName + " \u2192 " + appliedRank);
|
||||
admin.sendMessage(Utils.color(Utils.replace(
|
||||
LangManager.get("apply_accepted"),
|
||||
"%player%", applicantName, "%rank%", appliedRank)));
|
||||
Player applicant = Bukkit.getPlayerExact(applicantName);
|
||||
if (applicant != null) applicant.sendMessage(Utils.color(
|
||||
Utils.replace(LangManager.get("apply_you_accepted"),
|
||||
"%rank%", appliedRank)));
|
||||
}
|
||||
VIEWING_PLAYER.remove(admin.getUniqueId());
|
||||
openApplicationList(admin, page);
|
||||
}
|
||||
case 24 -> { // Deny
|
||||
if (appliedRank != null) {
|
||||
ApplicationManager.removeApplication(applicantName);
|
||||
AuditLog.log(AuditLog.APPLY_DENY, admin.getName(),
|
||||
applicantName + " for " + appliedRank);
|
||||
admin.sendMessage(Utils.color(Utils.replace(
|
||||
LangManager.get("apply_denied"),
|
||||
"%player%", applicantName)));
|
||||
Player applicant = Bukkit.getPlayerExact(applicantName);
|
||||
if (applicant != null) applicant.sendMessage(Utils.color(
|
||||
Utils.replace(LangManager.get("apply_you_denied"),
|
||||
"%rank%", appliedRank)));
|
||||
}
|
||||
VIEWING_PLAYER.remove(admin.getUniqueId());
|
||||
openApplicationList(admin, page);
|
||||
}
|
||||
case 49 -> { // Back
|
||||
VIEWING_PLAYER.remove(admin.getUniqueId());
|
||||
openApplicationList(admin, page);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Title helpers ─────────────────────────────────────────────────
|
||||
|
||||
public static String getSelectionTitle() {
|
||||
return LangManager.get("apply_gui_selection_title");
|
||||
}
|
||||
|
||||
public static String getDetailTitle() {
|
||||
return LangManager.get("apply_gui_detail_title");
|
||||
}
|
||||
|
||||
public static boolean isSelectionTitle(String colored) {
|
||||
return colored.equals(Utils.color(getSelectionTitle()));
|
||||
}
|
||||
|
||||
public static boolean isDetailTitle(String colored) {
|
||||
return colored.equals(Utils.color(getDetailTitle()));
|
||||
}
|
||||
|
||||
// ── Cleanup ────────────────────────────────────────────────────────
|
||||
|
||||
public static void cleanup(UUID uuid) {
|
||||
CURRENT_PAGE.remove(uuid);
|
||||
VIEWING_PLAYER.remove(uuid);
|
||||
}
|
||||
|
||||
// ── Stubs kept so ChatListener compiles without changes ────────────
|
||||
// (The old player-text-input flow has been removed.)
|
||||
public static boolean isAwaitingText(UUID uuid) { return false; }
|
||||
public static boolean handleTextInput(Player player, String text) { return false; }
|
||||
|
||||
// ── Item builders ─────────────────────────────────────────────────
|
||||
|
||||
private static ItemStack buildInfoItem(int total, int page, int totalPages) {
|
||||
ItemStack item = new ItemStack(Material.ENCHANTED_BOOK);
|
||||
ItemMeta m = item.getItemMeta();
|
||||
if (m != null) {
|
||||
m.setDisplayName(Utils.color("&b&lBewerbungen"));
|
||||
m.setLore(List.of(
|
||||
Utils.color("&7Ausstehend&8: &e" + total),
|
||||
Utils.color("&7Seite &e" + page + " &7/ &e" + totalPages)
|
||||
));
|
||||
item.setItemMeta(m);
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
private static ItemStack buildApplicantItem(String[] app) {
|
||||
// app: [rank, name, iso, reason]
|
||||
ItemStack item = new ItemStack(Material.PLAYER_HEAD);
|
||||
SkullMeta meta = (SkullMeta) item.getItemMeta();
|
||||
if (meta != null) {
|
||||
meta.setOwningPlayer(Bukkit.getOfflinePlayer(app[1]));
|
||||
meta.setDisplayName(Utils.color("&e&l" + app[1]));
|
||||
String rankDisplay = Main.getInstance().getConfig()
|
||||
.getString("rank-settings." + app[0] + ".display", app[0]);
|
||||
List<String> lore = new ArrayList<>();
|
||||
lore.add(Utils.color("&7Rang&8: " + rankDisplay));
|
||||
lore.add(Utils.color("&7Datum&8: &f" + Utils.prettifyIso(app[2])));
|
||||
if (!app[3].equals("-")) lore.add(Utils.color("&7Grund&8: &f" + app[3]));
|
||||
lore.add("");
|
||||
lore.add(Utils.color("&aKlicken zum Bearbeiten"));
|
||||
meta.setLore(lore);
|
||||
item.setItemMeta(meta);
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
private static ItemStack buildDetailItem(String[] app) {
|
||||
// app: [rank, name, iso, reason]
|
||||
ItemStack item = new ItemStack(Material.PLAYER_HEAD);
|
||||
SkullMeta meta = (SkullMeta) item.getItemMeta();
|
||||
if (meta != null) {
|
||||
meta.setOwningPlayer(Bukkit.getOfflinePlayer(app[1]));
|
||||
meta.setDisplayName(Utils.color("&e&l" + app[1]));
|
||||
String rankDisplay = Main.getInstance().getConfig()
|
||||
.getString("rank-settings." + app[0] + ".display", app[0]);
|
||||
List<String> lore = new ArrayList<>();
|
||||
lore.add(Utils.color("&7Rang&8: " + rankDisplay));
|
||||
lore.add(Utils.color("&7Datum&8: &f" + Utils.prettifyIso(app[2])));
|
||||
lore.add("");
|
||||
if (app[3].equals("-")) {
|
||||
lore.add(Utils.color("&8Kein Bewerbungstext angegeben"));
|
||||
} else {
|
||||
lore.add(Utils.color("&7Bewerbungstext&8:"));
|
||||
for (String line : wrapText(app[3], 35))
|
||||
lore.add(Utils.color("&f " + line));
|
||||
}
|
||||
meta.setLore(lore);
|
||||
item.setItemMeta(meta);
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
// ── Generic helpers ───────────────────────────────────────────────
|
||||
|
||||
private static ItemStack filler(Material mat) {
|
||||
ItemStack item = new ItemStack(mat);
|
||||
ItemMeta m = item.getItemMeta();
|
||||
if (m != null) { m.setDisplayName(" "); item.setItemMeta(m); }
|
||||
return item;
|
||||
}
|
||||
|
||||
private static ItemStack navItem(Material mat, String name, List<String> lore) {
|
||||
ItemStack item = new ItemStack(mat);
|
||||
ItemMeta m = item.getItemMeta();
|
||||
if (m != null) { m.setDisplayName(name); m.setLore(lore); item.setItemMeta(m); }
|
||||
return item;
|
||||
}
|
||||
|
||||
private static List<String> wrapText(String text, int maxChars) {
|
||||
List<String> lines = new ArrayList<>();
|
||||
String[] words = text.split(" ");
|
||||
StringBuilder cur = new StringBuilder();
|
||||
for (String w : words) {
|
||||
if (cur.length() > 0 && cur.length() + 1 + w.length() > maxChars) {
|
||||
lines.add(cur.toString());
|
||||
cur = new StringBuilder(w);
|
||||
} else {
|
||||
if (cur.length() > 0) cur.append(' ');
|
||||
cur.append(w);
|
||||
}
|
||||
}
|
||||
if (cur.length() > 0) lines.add(cur.toString());
|
||||
return lines.isEmpty() ? List.of(text) : lines;
|
||||
}
|
||||
}
|
||||
313
src/main/java/me/viper/teamplugin/gui/MailboxGUI.java
Normal file
313
src/main/java/me/viper/teamplugin/gui/MailboxGUI.java
Normal file
@@ -0,0 +1,313 @@
|
||||
package me.viper.teamplugin.gui;
|
||||
|
||||
import me.viper.teamplugin.manager.LangManager;
|
||||
import me.viper.teamplugin.manager.MailboxManager;
|
||||
import me.viper.teamplugin.manager.MessageManager;
|
||||
import me.viper.teamplugin.util.Utils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* MailboxGUI – two-view postfach for TeamPlugin.
|
||||
*
|
||||
* LIST VIEW (54 slots)
|
||||
* ┌─────────────────────────┐
|
||||
* │ Header row (glass) │ slot 4 = mailbox info item
|
||||
* │ Message items 9-44 │ WRITTEN_BOOK=unread, BOOK=read
|
||||
* │ Footer nav 45/49/53 │ ← page | close | page →
|
||||
* └─────────────────────────┘
|
||||
*
|
||||
* READ VIEW (54 slots)
|
||||
* ┌─────────────────────────┐
|
||||
* │ Background filler │
|
||||
* │ Full message at 22 │
|
||||
* │ Delete:20 Reply:24 │
|
||||
* │ Back:49 │
|
||||
* └─────────────────────────┘
|
||||
*/
|
||||
public class MailboxGUI {
|
||||
|
||||
private static final int ITEMS_PER_PAGE = 36; // rows 1-4, slots 9-44
|
||||
|
||||
/** UUID → current list-view page number */
|
||||
private static final Map<UUID, Integer> CURRENT_PAGE = new HashMap<>();
|
||||
/** UUID → whose mailbox is open (player name) */
|
||||
private static final Map<UUID, String> OPEN_FOR_PLAYER = new HashMap<>();
|
||||
/** UUID → msgId of message currently being read */
|
||||
private static final Map<UUID, String> READING_ID = new HashMap<>();
|
||||
|
||||
// ── Open: list view ───────────────────────────────────────────────
|
||||
|
||||
/** Opens the caller's own mailbox at page 0. */
|
||||
public static void openMailbox(Player viewer) {
|
||||
openMailbox(viewer, viewer.getName(), 0);
|
||||
}
|
||||
|
||||
/** Opens a specific player's mailbox at a given page (admins can inspect others). */
|
||||
public static void openMailbox(Player viewer, String targetPlayer, int page) {
|
||||
List<String[]> messages = MailboxManager.getParsedMessages(targetPlayer);
|
||||
int totalPages = Math.max(1, (int) Math.ceil((double) messages.size() / ITEMS_PER_PAGE));
|
||||
page = Math.max(0, Math.min(page, totalPages - 1));
|
||||
|
||||
OPEN_FOR_PLAYER.put(viewer.getUniqueId(), targetPlayer);
|
||||
CURRENT_PAGE.put(viewer.getUniqueId(), page);
|
||||
|
||||
Inventory inv = Bukkit.createInventory(null, 54, Utils.color(getListTitle()));
|
||||
|
||||
// Header row
|
||||
ItemStack headerGlass = filler(Material.CYAN_STAINED_GLASS_PANE);
|
||||
for (int i = 0; i < 9; i++) inv.setItem(i, headerGlass);
|
||||
|
||||
// Footer row
|
||||
ItemStack footerGlass = filler(Material.BLACK_STAINED_GLASS_PANE);
|
||||
for (int i = 45; i < 54; i++) inv.setItem(i, footerGlass);
|
||||
|
||||
// Info item (slot 4)
|
||||
int unread = MailboxManager.getUnreadCount(targetPlayer);
|
||||
inv.setItem(4, buildInfoItem(targetPlayer, unread, page + 1, totalPages));
|
||||
|
||||
// Message items (slots 9-44)
|
||||
int start = page * ITEMS_PER_PAGE;
|
||||
for (int i = 0; i < ITEMS_PER_PAGE; i++) {
|
||||
int idx = start + i;
|
||||
if (idx >= messages.size()) break;
|
||||
inv.setItem(9 + i, buildMessageListItem(messages.get(idx)));
|
||||
}
|
||||
|
||||
// Navigation
|
||||
if (page > 0) inv.setItem(45, navItem(Material.ARROW,
|
||||
Utils.color(LangManager.get("mailbox_prev_page")),
|
||||
List.of(Utils.color("&7Vorherige Seite"))));
|
||||
|
||||
inv.setItem(49, navItem(Material.BARRIER,
|
||||
Utils.color(LangManager.get("mailbox_close")),
|
||||
List.of(Utils.color("&7Postfach schließen"))));
|
||||
|
||||
if (page + 1 < totalPages) inv.setItem(53, navItem(Material.ARROW,
|
||||
Utils.color(LangManager.get("mailbox_next_page")),
|
||||
List.of(Utils.color("&7Nächste Seite"))));
|
||||
|
||||
viewer.openInventory(inv);
|
||||
}
|
||||
|
||||
// ── Open: read view ───────────────────────────────────────────────
|
||||
|
||||
private static void openMessage(Player viewer, String[] parts) {
|
||||
// parts: [id, from, isoTimestamp, read, text]
|
||||
String msgId = parts[0];
|
||||
String from = parts[1];
|
||||
String date = Utils.prettifyIso(parts[2]);
|
||||
String text = parts[4];
|
||||
|
||||
READING_ID.put(viewer.getUniqueId(), msgId);
|
||||
|
||||
String targetPlayer = OPEN_FOR_PLAYER.getOrDefault(viewer.getUniqueId(), viewer.getName());
|
||||
MailboxManager.markRead(targetPlayer, msgId);
|
||||
|
||||
Inventory inv = Bukkit.createInventory(null, 54, Utils.color(getReadTitle()));
|
||||
|
||||
// Background
|
||||
ItemStack bg = filler(Material.GRAY_STAINED_GLASS_PANE);
|
||||
for (int i = 0; i < 54; i++) inv.setItem(i, bg);
|
||||
|
||||
// Message paper at slot 22
|
||||
inv.setItem(22, buildFullMessageItem(from, date, text));
|
||||
|
||||
// Delete (slot 20)
|
||||
inv.setItem(20, navItem(Material.RED_CONCRETE,
|
||||
Utils.color(LangManager.get("mailbox_delete_btn")),
|
||||
List.of(Utils.color("&7Nachricht endgültig löschen"))));
|
||||
|
||||
// Reply (slot 24)
|
||||
inv.setItem(24, navItem(Material.WRITABLE_BOOK,
|
||||
Utils.color(Utils.replace(LangManager.get("mailbox_reply_btn"), "%player%", from)),
|
||||
List.of(Utils.color("&7An &b" + from + " &7antworten"))));
|
||||
|
||||
// Back (slot 49)
|
||||
inv.setItem(49, navItem(Material.ARROW,
|
||||
Utils.color(LangManager.get("mailbox_back_btn")),
|
||||
List.of(Utils.color("&7Zurück zur Liste"))));
|
||||
|
||||
viewer.openInventory(inv);
|
||||
}
|
||||
|
||||
// ── Click handlers ────────────────────────────────────────────────
|
||||
|
||||
public static void handleListClick(Player player, InventoryClickEvent e) {
|
||||
e.setCancelled(true);
|
||||
int slot = e.getRawSlot();
|
||||
ItemStack clicked = e.getCurrentItem();
|
||||
if (clicked == null || clicked.getType().isAir()) return;
|
||||
|
||||
String target = OPEN_FOR_PLAYER.getOrDefault(player.getUniqueId(), player.getName());
|
||||
int page = CURRENT_PAGE.getOrDefault(player.getUniqueId(), 0);
|
||||
|
||||
switch (slot) {
|
||||
case 45 -> openMailbox(player, target, page - 1);
|
||||
case 49 -> {
|
||||
cleanup(player.getUniqueId());
|
||||
player.closeInventory();
|
||||
}
|
||||
case 53 -> openMailbox(player, target, page + 1);
|
||||
default -> {
|
||||
if (slot >= 9 && slot <= 44) {
|
||||
List<String[]> messages = MailboxManager.getParsedMessages(target);
|
||||
int idx = page * ITEMS_PER_PAGE + (slot - 9);
|
||||
if (idx < messages.size()) openMessage(player, messages.get(idx));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void handleReadClick(Player player, InventoryClickEvent e) {
|
||||
e.setCancelled(true);
|
||||
int slot = e.getRawSlot();
|
||||
ItemStack clicked = e.getCurrentItem();
|
||||
if (clicked == null || clicked.getType().isAir()) return;
|
||||
|
||||
String target = OPEN_FOR_PLAYER.getOrDefault(player.getUniqueId(), player.getName());
|
||||
String msgId = READING_ID.get(player.getUniqueId());
|
||||
int page = CURRENT_PAGE.getOrDefault(player.getUniqueId(), 0);
|
||||
|
||||
switch (slot) {
|
||||
case 20 -> { // delete
|
||||
if (msgId != null) MailboxManager.delete(target, msgId);
|
||||
READING_ID.remove(player.getUniqueId());
|
||||
player.sendMessage(Utils.color(LangManager.get("mail_deleted")));
|
||||
openMailbox(player, target, page);
|
||||
}
|
||||
case 24 -> { // reply
|
||||
if (msgId != null) {
|
||||
MailboxManager.getParsedMessages(target).stream()
|
||||
.filter(a -> a[0].equals(msgId))
|
||||
.findFirst()
|
||||
.ifPresent(parts -> {
|
||||
cleanup(player.getUniqueId());
|
||||
player.closeInventory();
|
||||
MessageManager.startReply(player, parts[1]);
|
||||
});
|
||||
}
|
||||
}
|
||||
case 49 -> { // back
|
||||
READING_ID.remove(player.getUniqueId());
|
||||
openMailbox(player, target, page);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Title helpers ─────────────────────────────────────────────────
|
||||
|
||||
public static String getListTitle() {
|
||||
return LangManager.get("mailbox_title");
|
||||
}
|
||||
|
||||
public static String getReadTitle() {
|
||||
return LangManager.get("mailbox_read_title");
|
||||
}
|
||||
|
||||
public static boolean isListTitle(String colored) {
|
||||
return colored.equals(Utils.color(getListTitle()));
|
||||
}
|
||||
|
||||
public static boolean isReadTitle(String colored) {
|
||||
return colored.equals(Utils.color(getReadTitle()));
|
||||
}
|
||||
|
||||
// ── State cleanup ─────────────────────────────────────────────────
|
||||
|
||||
public static void cleanup(UUID uuid) {
|
||||
CURRENT_PAGE.remove(uuid);
|
||||
OPEN_FOR_PLAYER.remove(uuid);
|
||||
READING_ID.remove(uuid);
|
||||
}
|
||||
|
||||
// ── Item builders ─────────────────────────────────────────────────
|
||||
|
||||
private static ItemStack buildInfoItem(String owner, int unread, int page, int total) {
|
||||
ItemStack item = new ItemStack(Material.ENCHANTED_BOOK);
|
||||
ItemMeta m = item.getItemMeta();
|
||||
if (m != null) {
|
||||
m.setDisplayName(Utils.color("&b&lPostfach &8– &7" + owner));
|
||||
m.setLore(List.of(
|
||||
Utils.color("&7Ungelesen&8: &e" + unread),
|
||||
Utils.color("&7Seite &e" + page + " &7/ &e" + total)
|
||||
));
|
||||
item.setItemMeta(m);
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
private static ItemStack buildMessageListItem(String[] parts) {
|
||||
// parts: [id, from, iso, read, text]
|
||||
boolean read = "true".equals(parts[3]);
|
||||
ItemStack item = new ItemStack(read ? Material.BOOK : Material.WRITTEN_BOOK);
|
||||
ItemMeta m = item.getItemMeta();
|
||||
if (m != null) {
|
||||
m.setDisplayName(Utils.color((read ? "&7" : "&e&l") + "Von: " + parts[1]));
|
||||
String preview = parts[4].length() > 38 ? parts[4].substring(0, 38) + "…" : parts[4];
|
||||
m.setLore(List.of(
|
||||
Utils.color("&7" + Utils.prettifyIso(parts[2])),
|
||||
Utils.color("&f" + preview),
|
||||
"",
|
||||
Utils.color(read ? "&8Gelesen" : "&eUngelesen – Klicken zum Lesen")
|
||||
));
|
||||
item.setItemMeta(m);
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
private static ItemStack buildFullMessageItem(String from, String date, String text) {
|
||||
ItemStack item = new ItemStack(Material.PAPER);
|
||||
ItemMeta m = item.getItemMeta();
|
||||
if (m != null) {
|
||||
m.setDisplayName(Utils.color("&bVon: &e" + from + " &8| &7" + date));
|
||||
List<String> lore = new ArrayList<>();
|
||||
lore.add("");
|
||||
for (String line : wrapText(text, 40)) lore.add(Utils.color("&f" + line));
|
||||
m.setLore(lore);
|
||||
item.setItemMeta(m);
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
// ── Generic helpers ───────────────────────────────────────────────
|
||||
|
||||
private static ItemStack filler(Material mat) {
|
||||
ItemStack item = new ItemStack(mat);
|
||||
ItemMeta m = item.getItemMeta();
|
||||
if (m != null) { m.setDisplayName(" "); item.setItemMeta(m); }
|
||||
return item;
|
||||
}
|
||||
|
||||
private static ItemStack navItem(Material mat, String name, List<String> lore) {
|
||||
ItemStack item = new ItemStack(mat);
|
||||
ItemMeta m = item.getItemMeta();
|
||||
if (m != null) { m.setDisplayName(name); m.setLore(lore); item.setItemMeta(m); }
|
||||
return item;
|
||||
}
|
||||
|
||||
/** Word-wraps {@code text} at {@code maxChars} characters per line. */
|
||||
private static List<String> wrapText(String text, int maxChars) {
|
||||
List<String> lines = new ArrayList<>();
|
||||
String[] words = text.split(" ");
|
||||
StringBuilder cur = new StringBuilder();
|
||||
for (String w : words) {
|
||||
if (cur.length() > 0 && cur.length() + 1 + w.length() > maxChars) {
|
||||
lines.add(cur.toString());
|
||||
cur = new StringBuilder(w);
|
||||
} else {
|
||||
if (cur.length() > 0) cur.append(' ');
|
||||
cur.append(w);
|
||||
}
|
||||
}
|
||||
if (cur.length() > 0) lines.add(cur.toString());
|
||||
return lines.isEmpty() ? List.of(text) : lines;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,8 @@ package me.viper.teamplugin.gui;
|
||||
import me.viper.teamplugin.Main;
|
||||
import me.viper.teamplugin.manager.DataManager;
|
||||
import me.viper.teamplugin.manager.LangManager;
|
||||
import me.viper.teamplugin.manager.MessageManager;
|
||||
import me.viper.teamplugin.util.SkinResolver;
|
||||
import me.viper.teamplugin.util.Utils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
@@ -13,106 +15,462 @@ import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.bukkit.inventory.meta.SkullMeta;
|
||||
import org.bukkit.profile.PlayerProfile;
|
||||
import org.bukkit.profile.PlayerTextures;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* TeamGUI – two-layer navigation with optional pagination on rank pages.
|
||||
*
|
||||
* Layer 1 Overview – one configurable icon per rank.
|
||||
* Slot read from rank-settings.<rank>.slot (config.yml).
|
||||
* Clicking opens the rank's member page.
|
||||
*
|
||||
* Layer 2 Rank Page – shows player heads.
|
||||
* If members exceed capacity → pagination.
|
||||
* Footer layout (slots 45-53):
|
||||
* 45 ← prev rank (wraps)
|
||||
* 46 ← prev page (hidden if page 0)
|
||||
* 49 home
|
||||
* 52 → next page (hidden if last page)
|
||||
* 53 → next rank (wraps)
|
||||
*
|
||||
* Page capacity:
|
||||
* rank-settings.<rank>.member_slots defined → capacity = list size
|
||||
* otherwise → gui.members_per_page (default 40)
|
||||
*
|
||||
* GUI size is always 54 and NOT configurable.
|
||||
*/
|
||||
public class TeamGUI {
|
||||
|
||||
// Zeilenpositionen für Ränge
|
||||
private static final int[] rows = {1, 2, 3, 4};
|
||||
private static final int GUI_SIZE = 54;
|
||||
|
||||
public static void openTeamGUI(Player player) {
|
||||
FileConfiguration cfg = Main.getInstance().getConfig();
|
||||
FileConfiguration data = DataManager.getData();
|
||||
private static final int[] OVERVIEW_SLOTS_FALLBACK = {13, 22, 31, 40, 11, 15, 29, 33};
|
||||
|
||||
int size = cfg.getInt("gui.size", 54);
|
||||
String title = Utils.color(cfg.getString("gui.title", "&8» &bTeam Übersicht"));
|
||||
Inventory inv = Bukkit.createInventory(null, size, title);
|
||||
public static final int NAV_PREV = 45;
|
||||
public static final int NAV_PREV_PAGE = 46;
|
||||
public static final int NAV_HOME = 49;
|
||||
public static final int NAV_NEXT_PAGE = 52;
|
||||
public static final int NAV_NEXT = 53;
|
||||
|
||||
// Hintergrund-Glas setzen
|
||||
Material bgMat = Material.valueOf(cfg.getString("gui.background", "GRAY_STAINED_GLASS_PANE"));
|
||||
ItemStack filler = new ItemStack(bgMat);
|
||||
ItemMeta fm = filler.getItemMeta();
|
||||
if (fm != null) {
|
||||
fm.setDisplayName(" ");
|
||||
filler.setItemMeta(fm);
|
||||
}
|
||||
for (int i = 0; i < inv.getSize(); i++) inv.setItem(i, filler);
|
||||
/** UUID → current page on the rank page they have open */
|
||||
private static final Map<UUID, Integer> RANK_PAGE = new HashMap<>();
|
||||
|
||||
List<String> ranks = cfg.getStringList("ranks");
|
||||
for (int i = 0; i < ranks.size() && i < rows.length; i++) {
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
// PUBLIC ENTRY POINTS
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
|
||||
public static void openTeamGUI(Player player) { openOverview(player); }
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
// LAYER 1 – OVERVIEW
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
|
||||
public static void openOverview(Player player) {
|
||||
FileConfiguration cfg = Main.getInstance().getConfig();
|
||||
List<String> ranks = cfg.getStringList("ranks");
|
||||
|
||||
Inventory inv = Bukkit.createInventory(null, GUI_SIZE, Utils.color(getGuiTitle()));
|
||||
fillBackground(inv, cfg.getString("gui.background", "GRAY_STAINED_GLASS_PANE"));
|
||||
|
||||
for (int i = 0; i < ranks.size(); i++) {
|
||||
String rank = ranks.get(i);
|
||||
List<String> members = data.getStringList("Team." + rank);
|
||||
if (members == null) members = new ArrayList<>();
|
||||
int slot = resolveOverviewSlot(cfg, rank, i);
|
||||
if (slot < 0 || slot >= GUI_SIZE) continue;
|
||||
inv.setItem(slot, createRankOverviewBlock(rank));
|
||||
}
|
||||
player.openInventory(inv);
|
||||
}
|
||||
|
||||
int rowStart = rows[i] * 9;
|
||||
int count = Math.min(members.size(), 9);
|
||||
if (count == 0) {
|
||||
ItemStack empty = createInfoItem("§7Kein/e " + rank, List.of("§7Keine Mitglieder"));
|
||||
inv.setItem(rowStart + 4, empty);
|
||||
continue;
|
||||
private static int resolveOverviewSlot(FileConfiguration cfg, String rank, int index) {
|
||||
if (cfg.isInt("rank-settings." + rank + ".slot"))
|
||||
return cfg.getInt("rank-settings." + rank + ".slot");
|
||||
if (index < OVERVIEW_SLOTS_FALLBACK.length)
|
||||
return OVERVIEW_SLOTS_FALLBACK[index];
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static ItemStack createRankOverviewBlock(String rank) {
|
||||
FileConfiguration cfg = Main.getInstance().getConfig();
|
||||
String displayRaw = cfg.getString("rank-settings." + rank + ".display", rank);
|
||||
String skullTexture = cfg.getString("rank-settings." + rank + ".skull_texture", "");
|
||||
int memberCount = DataManager.getData().getStringList("Team." + rank).size();
|
||||
|
||||
ItemStack item = skullTexture.isEmpty()
|
||||
? new ItemStack(parseMaterial(cfg.getString("rank-settings." + rank + ".material", "STONE"), Material.STONE))
|
||||
: buildCustomSkull(skullTexture);
|
||||
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
if (meta != null) {
|
||||
meta.setDisplayName(Utils.color(displayRaw));
|
||||
List<String> lore = new ArrayList<>();
|
||||
lore.add(Utils.color("&7Mitglieder&8: &e" + memberCount));
|
||||
lore.add("");
|
||||
lore.add(Utils.color("&7\u25ba &fKlicke zum \u00d6ffnen"));
|
||||
meta.setLore(lore);
|
||||
item.setItemMeta(meta);
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
// LAYER 2 – RANK PAGE (with pagination)
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
|
||||
public static void openRankPage(Player player, String rank) {
|
||||
openRankPage(player, rank, 0);
|
||||
}
|
||||
|
||||
public static void openRankPage(Player player, String rank, int page) {
|
||||
FileConfiguration cfg = Main.getInstance().getConfig();
|
||||
List<String> ranks = cfg.getStringList("ranks");
|
||||
int rankIdx = ranks.indexOf(rank);
|
||||
if (rankIdx < 0) return;
|
||||
|
||||
int rankCount = ranks.size();
|
||||
|
||||
// All members for this rank
|
||||
List<String> allMembers = DataManager.getData().getStringList("Team." + rank);
|
||||
|
||||
// Determine page capacity
|
||||
List<Integer> configuredSlots = cfg.getIntegerList("rank-settings." + rank + ".member_slots")
|
||||
.stream().filter(s -> s >= 0 && s < 45).collect(Collectors.toList());
|
||||
int capacity = configuredSlots.isEmpty()
|
||||
? Math.min(cfg.getInt("gui.members_per_page", 40), 45)
|
||||
: configuredSlots.size();
|
||||
|
||||
int totalPages = Math.max(1, (int) Math.ceil((double) allMembers.size() / capacity));
|
||||
page = Math.max(0, Math.min(page, totalPages - 1));
|
||||
RANK_PAGE.put(player.getUniqueId(), page);
|
||||
|
||||
// Slice for this page
|
||||
int start = page * capacity;
|
||||
int end = Math.min(start + capacity, allMembers.size());
|
||||
List<String> members = allMembers.subList(start, end);
|
||||
|
||||
String glassStr = cfg.getString("rank-settings." + rank + ".glass",
|
||||
cfg.getString("gui.background", "GRAY_STAINED_GLASS_PANE"));
|
||||
|
||||
Inventory inv = Bukkit.createInventory(null, GUI_SIZE, Utils.color(getRankPageTitle(rank)));
|
||||
fillBackground(inv, glassStr);
|
||||
|
||||
// Dark footer row
|
||||
ItemStack navFiller = createFiller(Material.BLACK_STAINED_GLASS_PANE);
|
||||
for (int i = 45; i < 54; i++) inv.setItem(i, navFiller);
|
||||
|
||||
// ── Rank navigation (always visible) ────────────────────────
|
||||
String prevRank = ranks.get((rankIdx - 1 + rankCount) % rankCount);
|
||||
String prevDisplay = cfg.getString("rank-settings." + prevRank + ".display", prevRank);
|
||||
inv.setItem(NAV_PREV, createNavItem(Material.ARROW,
|
||||
Utils.color(Utils.replace(LangManager.get("nav_prev_label"), "%rank%", prevDisplay)),
|
||||
List.of(Utils.color(LangManager.get("nav_prev_lore")))));
|
||||
|
||||
inv.setItem(NAV_HOME, createNavItem(Material.NETHER_STAR,
|
||||
Utils.color(LangManager.get("nav_home_label")),
|
||||
List.of(Utils.color(LangManager.get("nav_home_lore")))));
|
||||
|
||||
String nextRank = ranks.get((rankIdx + 1) % rankCount);
|
||||
String nextDisplay = cfg.getString("rank-settings." + nextRank + ".display", nextRank);
|
||||
inv.setItem(NAV_NEXT, createNavItem(Material.ARROW,
|
||||
Utils.color(Utils.replace(LangManager.get("nav_next_label"), "%rank%", nextDisplay)),
|
||||
List.of(Utils.color(LangManager.get("nav_next_lore")))));
|
||||
|
||||
// ── Page navigation (only if multiple pages) ─────────────────
|
||||
if (totalPages > 1) {
|
||||
if (page > 0) inv.setItem(NAV_PREV_PAGE, createNavItem(Material.SPECTRAL_ARROW,
|
||||
Utils.color(LangManager.get("page_prev_label")),
|
||||
List.of(Utils.color("&7Seite " + page + " / " + totalPages))));
|
||||
|
||||
// Page indicator in home slot lore (update lore only)
|
||||
ItemStack homeItem = inv.getItem(NAV_HOME);
|
||||
if (homeItem != null) {
|
||||
ItemMeta hm = homeItem.getItemMeta();
|
||||
if (hm != null) {
|
||||
hm.setLore(List.of(
|
||||
Utils.color(LangManager.get("nav_home_lore")),
|
||||
Utils.color("&8Seite &7" + (page + 1) + " &8/ &7" + totalPages)
|
||||
));
|
||||
homeItem.setItemMeta(hm);
|
||||
}
|
||||
}
|
||||
|
||||
int startOffset = (9 - count) / 2;
|
||||
for (int j = 0; j < count; j++) {
|
||||
String name = members.get(j);
|
||||
int slot = rowStart + startOffset + j;
|
||||
if (slot >= 0 && slot < inv.getSize()) {
|
||||
inv.setItem(slot, createPlayerHead(name, rank));
|
||||
}
|
||||
if (page + 1 < totalPages) inv.setItem(NAV_NEXT_PAGE, createNavItem(Material.SPECTRAL_ARROW,
|
||||
Utils.color(LangManager.get("page_next_label")),
|
||||
List.of(Utils.color("&7Seite " + (page + 2) + " / " + totalPages))));
|
||||
}
|
||||
|
||||
// ── Member heads ──────────────────────────────────────────────
|
||||
List<Integer> memberSlots = resolveMemberSlots(cfg, rank, members.size());
|
||||
|
||||
if (allMembers.isEmpty()) {
|
||||
inv.setItem(22, createInfoItem("\u00a77Keine Mitglieder",
|
||||
List.of("\u00a77Dieser Rang hat keine Mitglieder.")));
|
||||
} else {
|
||||
for (int i = 0; i < members.size() && i < memberSlots.size(); i++) {
|
||||
int slot = memberSlots.get(i);
|
||||
String memberName = members.get(i);
|
||||
|
||||
inv.setItem(slot, createPlayerHeadSync(memberName, rank));
|
||||
|
||||
final int finalSlot = slot;
|
||||
SkinResolver.resolveAndUpdate(memberName, inv, finalSlot,
|
||||
(uuid, updatedInv) -> {
|
||||
if (!player.isOnline()) return;
|
||||
if (!player.getOpenInventory().getTitle()
|
||||
.equals(Utils.color(getRankPageTitle(rank)))) return;
|
||||
updatedInv.setItem(finalSlot,
|
||||
createPlayerHeadWithUUID(memberName, rank, uuid));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
player.openInventory(inv);
|
||||
}
|
||||
|
||||
private static ItemStack createPlayerHead(String name, String rank) {
|
||||
FileConfiguration cfg = Main.getInstance().getConfig();
|
||||
private static List<Integer> resolveMemberSlots(FileConfiguration cfg, String rank, int count) {
|
||||
List<Integer> configured = cfg.getIntegerList("rank-settings." + rank + ".member_slots")
|
||||
.stream().filter(s -> s >= 0 && s < 45).collect(Collectors.toList());
|
||||
return (!configured.isEmpty()) ? configured : computeSlots(count);
|
||||
}
|
||||
|
||||
ItemStack skull = new ItemStack(Material.PLAYER_HEAD);
|
||||
SkullMeta meta = (SkullMeta) skull.getItemMeta();
|
||||
if (meta != null) {
|
||||
OfflinePlayer off = Bukkit.getOfflinePlayer(name);
|
||||
meta.setOwningPlayer(off);
|
||||
|
||||
// Rank aus config
|
||||
String rankDisplay = cfg.getString("rank-settings." + rank + ".display", rank);
|
||||
String rankPrefix = cfg.getString("rank-settings." + rank + ".prefix", "");
|
||||
|
||||
// Name + Prefix
|
||||
String displayName = (rankPrefix == null ? "" : rankPrefix + " ") + "&b" + name;
|
||||
meta.setDisplayName(Utils.color(displayName.trim()));
|
||||
|
||||
List<String> lore = new ArrayList<>();
|
||||
|
||||
// Rang
|
||||
String rankLine = Utils.replace(LangManager.get("tooltip_rank"), "%rank%", rankDisplay);
|
||||
lore.add(Utils.color(rankLine));
|
||||
|
||||
// Online-/Offline-Status aus config
|
||||
String statusOnline = cfg.getString("status.online", "&a🟢 Online");
|
||||
String statusOffline = cfg.getString("status.offline", "&c🔴 Offline");
|
||||
boolean isOnline = off.isOnline();
|
||||
String statusLine = isOnline ? statusOnline : statusOffline;
|
||||
lore.add(Utils.color("&7Status: " + statusLine));
|
||||
|
||||
// Join-Datum, falls vorhanden
|
||||
String iso = DataManager.getData().getString("JoinDates." + name, "");
|
||||
if (iso != null && !iso.isEmpty()) {
|
||||
String joinLine = Utils.replace(LangManager.get("tooltip_joined"), "%joindate%", Utils.prettifyIso(iso));
|
||||
lore.add(Utils.color(joinLine));
|
||||
private static List<Integer> computeSlots(int count) {
|
||||
List<Integer> result = new ArrayList<>();
|
||||
if (count == 0) return result;
|
||||
final int MAX = 45, COLS = 9, ROWS = 5;
|
||||
int total = Math.min(count, MAX);
|
||||
int perRow = Math.min((int) Math.ceil((double) total / ROWS), COLS);
|
||||
int placed = 0;
|
||||
for (int row = 0; row < ROWS && placed < total; row++) {
|
||||
int inRow = Math.min(perRow, total - placed);
|
||||
int offset = (COLS - inRow) / 2;
|
||||
for (int col = 0; col < inRow && placed < total; col++) {
|
||||
result.add(row * COLS + offset + col);
|
||||
placed++;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
meta.setLore(lore);
|
||||
skull.setItemMeta(meta);
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
// CLICK HANDLERS
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
|
||||
public static void handleOverviewClick(Player player, int slot) {
|
||||
FileConfiguration cfg = Main.getInstance().getConfig();
|
||||
List<String> ranks = cfg.getStringList("ranks");
|
||||
for (int i = 0; i < ranks.size(); i++) {
|
||||
if (slot == resolveOverviewSlot(cfg, ranks.get(i), i)) {
|
||||
openRankPage(player, ranks.get(i));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void handleRankPageClick(Player player, int slot, String currentRank) {
|
||||
List<String> ranks = Main.getInstance().getConfig().getStringList("ranks");
|
||||
int idx = ranks.indexOf(currentRank);
|
||||
int size = ranks.size();
|
||||
if (idx < 0 || size == 0) return;
|
||||
|
||||
int page = RANK_PAGE.getOrDefault(player.getUniqueId(), 0);
|
||||
|
||||
switch (slot) {
|
||||
case NAV_HOME -> openOverview(player);
|
||||
case NAV_PREV -> openRankPage(player, ranks.get((idx - 1 + size) % size));
|
||||
case NAV_NEXT -> openRankPage(player, ranks.get((idx + 1) % size));
|
||||
case NAV_PREV_PAGE -> openRankPage(player, currentRank, page - 1);
|
||||
case NAV_NEXT_PAGE -> openRankPage(player, currentRank, page + 1);
|
||||
default -> {
|
||||
// Member head click → message flow
|
||||
String targetName = getMemberAtSlot(currentRank, slot, page);
|
||||
if (targetName != null && !targetName.equalsIgnoreCase(player.getName())) {
|
||||
player.closeInventory();
|
||||
MessageManager.startInput(player, targetName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
// HEAD → MEMBER LOOKUP
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Returns the member name at the given slot on a rank page (accounting for page offset),
|
||||
* or null if the slot holds no member head.
|
||||
*/
|
||||
public static String getMemberAtSlot(String rank, int slot, int page) {
|
||||
FileConfiguration cfg = Main.getInstance().getConfig();
|
||||
List<String> allMembers = DataManager.getData().getStringList("Team." + rank);
|
||||
|
||||
List<Integer> configuredSlots = cfg.getIntegerList("rank-settings." + rank + ".member_slots")
|
||||
.stream().filter(s -> s >= 0 && s < 45).collect(Collectors.toList());
|
||||
int capacity = configuredSlots.isEmpty()
|
||||
? Math.min(cfg.getInt("gui.members_per_page", 40), 45)
|
||||
: configuredSlots.size();
|
||||
|
||||
int start = page * capacity;
|
||||
int end = Math.min(start + capacity, allMembers.size());
|
||||
List<String> pageMembers = allMembers.subList(start, end);
|
||||
List<Integer> memberSlots = resolveMemberSlots(cfg, rank, pageMembers.size());
|
||||
|
||||
for (int i = 0; i < pageMembers.size() && i < memberSlots.size(); i++) {
|
||||
if (memberSlots.get(i) == slot) return pageMembers.get(i);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
// TITLE HELPERS
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
|
||||
public static String getGuiTitle() {
|
||||
return Main.getInstance().getConfig().getString("gui.title", "&8\u00bb &bTeam \u00dcbersicht");
|
||||
}
|
||||
|
||||
public static String getRankPageTitle(String rank) {
|
||||
FileConfiguration cfg = Main.getInstance().getConfig();
|
||||
String display = cfg.getString("rank-settings." + rank + ".display", rank);
|
||||
String prefix = cfg.getString("gui.rank_page_title", "&8\u00bb &bTeam &7| ");
|
||||
return prefix + display;
|
||||
}
|
||||
|
||||
public static boolean isOverviewTitle(String coloredTitle) {
|
||||
return coloredTitle.equals(Utils.color(getGuiTitle()));
|
||||
}
|
||||
|
||||
public static String extractRankFromTitle(String coloredTitle) {
|
||||
for (String rank : Main.getInstance().getConfig().getStringList("ranks")) {
|
||||
if (coloredTitle.equals(Utils.color(getRankPageTitle(rank)))) return rank;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
// HEAD FACTORIES
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
|
||||
private static ItemStack createPlayerHeadSync(String name, String rank) {
|
||||
ItemStack skull = new ItemStack(Material.PLAYER_HEAD);
|
||||
SkullMeta meta = (SkullMeta) skull.getItemMeta();
|
||||
if (meta == null) return skull;
|
||||
@SuppressWarnings("deprecation")
|
||||
OfflinePlayer off = Bukkit.getOfflinePlayer(name);
|
||||
meta.setOwningPlayer(off);
|
||||
applyHeadMeta(meta, name, rank);
|
||||
skull.setItemMeta(meta);
|
||||
return skull;
|
||||
}
|
||||
|
||||
private static ItemStack createPlayerHeadWithUUID(String name, String rank, UUID uuid) {
|
||||
ItemStack skull = new ItemStack(Material.PLAYER_HEAD);
|
||||
SkullMeta meta = (SkullMeta) skull.getItemMeta();
|
||||
if (meta == null) return skull;
|
||||
OfflinePlayer off = (uuid != null) ? Bukkit.getOfflinePlayer(uuid)
|
||||
: Bukkit.getOfflinePlayer(name); //noinspection deprecation
|
||||
meta.setOwningPlayer(off);
|
||||
applyHeadMeta(meta, name, rank);
|
||||
skull.setItemMeta(meta);
|
||||
return skull;
|
||||
}
|
||||
|
||||
private static void applyHeadMeta(SkullMeta meta, String name, String rank) {
|
||||
FileConfiguration cfg = Main.getInstance().getConfig();
|
||||
String rankPrefix = cfg.getString("rank-settings." + rank + ".prefix", "");
|
||||
String displayName = rankPrefix.isEmpty() ? "&b" + name : rankPrefix + " &b" + name;
|
||||
meta.setDisplayName(Utils.color(displayName.trim()));
|
||||
|
||||
List<String> lore = new ArrayList<>();
|
||||
String rankDisplay = cfg.getString("rank-settings." + rank + ".display", rank);
|
||||
lore.add(Utils.color(Utils.replace(LangManager.get("tooltip_rank"), "%rank%", rankDisplay)));
|
||||
|
||||
String statusOnline = cfg.getString("status.online", "&a\uD83D\uDFE2 Online");
|
||||
String statusOffline = cfg.getString("status.offline", "&c\uD83D\uDD34 Offline");
|
||||
@SuppressWarnings("deprecation")
|
||||
boolean online = Bukkit.getOfflinePlayer(name).isOnline();
|
||||
lore.add(Utils.color("&7Status&8: " + (online ? statusOnline : statusOffline)));
|
||||
|
||||
String iso = DataManager.getData().getString("JoinDates." + name, "");
|
||||
if (iso != null && !iso.isEmpty())
|
||||
lore.add(Utils.color(Utils.replace(LangManager.get("tooltip_joined"),
|
||||
"%joindate%", Utils.prettifyIso(iso))));
|
||||
|
||||
lore.add("");
|
||||
lore.add(Utils.color(LangManager.get("msg_head_hint")));
|
||||
meta.setLore(lore);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
// CUSTOM SKULL
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
|
||||
private static ItemStack buildCustomSkull(String texture) {
|
||||
ItemStack skull = new ItemStack(Material.PLAYER_HEAD);
|
||||
if (texture == null || texture.isEmpty()) return skull;
|
||||
try {
|
||||
String url = resolveTextureUrl(texture);
|
||||
if (url == null) {
|
||||
Main.getInstance().getLogger().warning("[TeamGUI] Cannot resolve texture: " + texture);
|
||||
return skull;
|
||||
}
|
||||
PlayerProfile profile = Bukkit.createPlayerProfile(UUID.randomUUID(), "CustomSkull");
|
||||
PlayerTextures textures = profile.getTextures();
|
||||
textures.setSkin(new java.net.URL(url));
|
||||
profile.setTextures(textures);
|
||||
SkullMeta meta = (SkullMeta) skull.getItemMeta();
|
||||
if (meta != null) { meta.setOwnerProfile(profile); skull.setItemMeta(meta); }
|
||||
} catch (Exception e) {
|
||||
Main.getInstance().getLogger().warning("[TeamGUI] Skull error: " + e.getMessage());
|
||||
}
|
||||
return skull;
|
||||
}
|
||||
|
||||
private static String resolveTextureUrl(String texture) {
|
||||
if (texture == null || texture.isEmpty()) return null;
|
||||
if (texture.startsWith("http://") || texture.startsWith("https://")) return texture;
|
||||
if (texture.matches("[0-9a-fA-F]{64}")) return "http://textures.minecraft.net/texture/" + texture;
|
||||
if (texture.startsWith("eyJ")) {
|
||||
try {
|
||||
String json = new String(Base64.getDecoder().decode(texture), StandardCharsets.UTF_8);
|
||||
int s = json.indexOf("\"url\":\"");
|
||||
if (s < 0) return null;
|
||||
s += 7;
|
||||
int e = json.indexOf('"', s);
|
||||
return (e > s) ? json.substring(s, e) : null;
|
||||
} catch (IllegalArgumentException ignored) {}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
// BACKGROUND / GENERIC HELPERS
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
|
||||
private static void fillBackground(Inventory inv, String matStr) {
|
||||
ItemStack filler = createFiller(parseMaterial(matStr, Material.GRAY_STAINED_GLASS_PANE));
|
||||
for (int i = 0; i < inv.getSize(); i++) inv.setItem(i, filler);
|
||||
}
|
||||
|
||||
private static ItemStack createFiller(Material mat) {
|
||||
ItemStack item = new ItemStack(mat);
|
||||
ItemMeta m = item.getItemMeta();
|
||||
if (m != null) { m.setDisplayName(" "); item.setItemMeta(m); }
|
||||
return item;
|
||||
}
|
||||
|
||||
private static ItemStack createNavItem(Material mat, String name, List<String> lore) {
|
||||
ItemStack item = new ItemStack(mat);
|
||||
ItemMeta m = item.getItemMeta();
|
||||
if (m != null) { m.setDisplayName(name); m.setLore(lore); item.setItemMeta(m); }
|
||||
return item;
|
||||
}
|
||||
|
||||
private static ItemStack createInfoItem(String name, List<String> lore) {
|
||||
ItemStack item = new ItemStack(Material.PAPER);
|
||||
ItemMeta m = item.getItemMeta();
|
||||
ItemMeta m = item.getItemMeta();
|
||||
if (m != null) {
|
||||
m.setDisplayName(Utils.color(name));
|
||||
m.setLore(lore.stream().map(Utils::color).toList());
|
||||
@@ -121,7 +479,7 @@ public class TeamGUI {
|
||||
return item;
|
||||
}
|
||||
|
||||
public static String getGuiTitle() {
|
||||
return Main.getInstance().getConfig().getString("gui.title", "&8» &bTeam Übersicht");
|
||||
private static Material parseMaterial(String s, Material fallback) {
|
||||
try { return Material.valueOf(s); } catch (Exception e) { return fallback; }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user