From e5bc3d1f1401cf8dbc383ca7696e9e38ca4c42b9 Mon Sep 17 00:00:00 2001 From: cybsec Date: Wed, 4 Feb 2026 20:12:49 -0500 Subject: [PATCH] added comprehensive interactive help book --- .../oyeshops/command/AdminCommands.java | 18 ++- .../party/cybsec/oyeshops/gui/HelpBook.java | 143 ++++++++++++++++++ 2 files changed, 158 insertions(+), 3 deletions(-) create mode 100644 src/main/java/party/cybsec/oyeshops/gui/HelpBook.java diff --git a/src/main/java/party/cybsec/oyeshops/command/AdminCommands.java b/src/main/java/party/cybsec/oyeshops/command/AdminCommands.java index 423050b..eb5b593 100644 --- a/src/main/java/party/cybsec/oyeshops/command/AdminCommands.java +++ b/src/main/java/party/cybsec/oyeshops/command/AdminCommands.java @@ -11,7 +11,7 @@ import party.cybsec.oyeshops.OyeShopsPlugin; import party.cybsec.oyeshops.model.PendingActivation; import party.cybsec.oyeshops.model.Shop; import party.cybsec.oyeshops.permission.PermissionManager; -import party.cybsec.oyeshops.listener.ShopActivationListener; +import party.cybsec.oyeshops.gui.HelpBook; import java.sql.SQLException; import java.util.ArrayList; @@ -42,12 +42,13 @@ public class AdminCommands implements CommandExecutor, TabCompleter { case "toggle" -> handleToggle(sender); case "notify" -> handleNotifyToggle(sender); case "info" -> handleInfo(sender); + case "help" -> handleHelp(sender); case "reload" -> handleReload(sender); case "inspect", "i" -> handleInspect(sender); case "spoof", "s" -> handleSpoof(sender); case "unregister", "delete", "remove" -> handleUnregister(sender, args); case "_activate" -> handleActivate(sender, args); - default -> sendHelp(sender); + default -> handleHelp(sender); } return true; @@ -55,6 +56,8 @@ public class AdminCommands implements CommandExecutor, TabCompleter { private void sendHelp(CommandSender sender) { sender.sendMessage(Component.text("=== oyeshops commands ===", NamedTextColor.GOLD)); + sender.sendMessage(Component.text("/oyeshops help", NamedTextColor.YELLOW) + .append(Component.text(" - open interactive guide", NamedTextColor.GRAY))); sender.sendMessage(Component.text("/oyeshops on", NamedTextColor.YELLOW) .append(Component.text(" - enable shop creation", NamedTextColor.GRAY))); sender.sendMessage(Component.text("/oyeshops off", NamedTextColor.YELLOW) @@ -86,6 +89,14 @@ public class AdminCommands implements CommandExecutor, TabCompleter { sender.sendMessage(Component.text("cybsec made this plugin", NamedTextColor.GRAY)); } + private void handleHelp(CommandSender sender) { + if (sender instanceof Player player) { + HelpBook.open(player); + } else { + sendHelp(sender); + } + } + private void handleReload(CommandSender sender) { if (!PermissionManager.isAdmin(sender)) { sender.sendMessage(Component.text("no permission", NamedTextColor.RED)); @@ -342,7 +353,8 @@ public class AdminCommands implements CommandExecutor, TabCompleter { public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { List completions = new ArrayList<>(); if (args.length == 1) { - List subCommands = new ArrayList<>(List.of("on", "off", "toggle", "notify", "enable", "disable")); + List subCommands = new ArrayList<>( + List.of("help", "on", "off", "toggle", "notify", "info", "enable", "disable")); if (PermissionManager.isAdmin(sender)) { subCommands.addAll(List.of("reload", "inspect", "spoof", "unregister")); } diff --git a/src/main/java/party/cybsec/oyeshops/gui/HelpBook.java b/src/main/java/party/cybsec/oyeshops/gui/HelpBook.java new file mode 100644 index 0000000..c9efb5c --- /dev/null +++ b/src/main/java/party/cybsec/oyeshops/gui/HelpBook.java @@ -0,0 +1,143 @@ +package party.cybsec.oyeshops.gui; + +import net.kyori.adventure.inventory.Book; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; + +/** + * utility to open an interactive help book for players + * all text is lowercase for consistency + */ +public class HelpBook { + + public static void open(Player player) { + List pages = new ArrayList<>(); + + // page 1: introduction + pages.add(Component.text() + .append(Component.text("oyeshops guide", NamedTextColor.GOLD, TextDecoration.BOLD)) + .append(Component.newline()) + .append(Component.newline()) + .append(Component.text("welcome to the simple item barter system.", NamedTextColor.DARK_GRAY)) + .append(Component.newline()) + .append(Component.newline()) + .append(Component.text("steps to start:", NamedTextColor.GRAY)) + .append(Component.newline()) + .append(Component.text("1. type ", NamedTextColor.BLACK)) + .append(Component.text("/oyes on", NamedTextColor.BLUE)) + .append(Component.newline()) + .append(Component.text("2. place a container", NamedTextColor.BLACK)) + .append(Component.newline()) + .append(Component.text("3. place a wall sign", NamedTextColor.BLACK)) + .build()); + + // page 2: the sign format + pages.add(Component.text() + .append(Component.text("creating a shop", NamedTextColor.GOLD, TextDecoration.BOLD)) + .append(Component.newline()) + .append(Component.newline()) + .append(Component.text("write your trade on the sign like this:", NamedTextColor.DARK_GRAY)) + .append(Component.newline()) + .append(Component.newline()) + .append(Component.text("line 1: ", NamedTextColor.GRAY)) + .append(Component.text("1 diamond", NamedTextColor.BLACK)) + .append(Component.newline()) + .append(Component.text("line 2: ", NamedTextColor.GRAY)) + .append(Component.text("for", NamedTextColor.BLACK)) + .append(Component.newline()) + .append(Component.text("line 3: ", NamedTextColor.GRAY)) + .append(Component.text("64 dirt", NamedTextColor.BLACK)) + .append(Component.newline()) + .append(Component.newline()) + .append(Component.text("(order doesn't matter, it shows a confirmation)", NamedTextColor.DARK_GRAY)) + .build()); + + // page 3: auto detection + pages.add(Component.text() + .append(Component.text("auto detection", NamedTextColor.GOLD, TextDecoration.BOLD)) + .append(Component.newline()) + .append(Component.newline()) + .append(Component.text("lazy? just put your items in the chest and write:", NamedTextColor.DARK_GRAY)) + .append(Component.newline()) + .append(Component.newline()) + .append(Component.text("auto", NamedTextColor.BLUE, TextDecoration.BOLD)) + .append(Component.newline()) + .append(Component.text("for 10 gold", NamedTextColor.BLACK)) + .append(Component.newline()) + .append(Component.newline()) + .append(Component.text("the plugin will see what's in the chest and fill it in for you.", + NamedTextColor.DARK_GRAY)) + .build()); + + // page 4: protection and ownership + pages.add(Component.text() + .append(Component.text("ownership rules", NamedTextColor.GOLD, TextDecoration.BOLD)) + .append(Component.newline()) + .append(Component.newline()) + .append(Component.text("to prevent stealing, you can only make shops on containers you placed ", + NamedTextColor.DARK_GRAY)) + .append(Component.text("this session", NamedTextColor.BLACK, TextDecoration.ITALIC)) + .append(Component.text(".", NamedTextColor.DARK_GRAY)) + .append(Component.newline()) + .append(Component.newline()) + .append(Component.text( + "if the server restarts, you can't turn old chests into shops. place a fresh one!", + NamedTextColor.DARK_GRAY)) + .build()); + + // page 5: containers + pages.add(Component.text() + .append(Component.text("supported blocks", NamedTextColor.GOLD, TextDecoration.BOLD)) + .append(Component.newline()) + .append(Component.newline()) + .append(Component.text("- chests", NamedTextColor.BLACK)) + .append(Component.newline()) + .append(Component.text("- barrels", NamedTextColor.BLACK)) + .append(Component.newline()) + .append(Component.text("- trapped chests", NamedTextColor.BLACK)) + .append(Component.newline()) + .append(Component.newline()) + .append(Component.text("double chests are fully supported. both halves are protected.", + NamedTextColor.DARK_GRAY)) + .build()); + + // page 6: utility commands + pages.add(Component.text() + .append(Component.text("handy commands", NamedTextColor.GOLD, TextDecoration.BOLD)) + .append(Component.newline()) + .append(Component.newline()) + .append(Component.text("/oyes notify", NamedTextColor.BLUE)) + .append(Component.newline()) + .append(Component.text("get alerts when your shops run out of items.", NamedTextColor.DARK_GRAY)) + .append(Component.newline()) + .append(Component.newline()) + .append(Component.text("/oyes info", NamedTextColor.BLUE)) + .append(Component.newline()) + .append(Component.text("see who made this plugin.", NamedTextColor.DARK_GRAY)) + .build()); + + // page 7: tips + pages.add(Component.text() + .append(Component.text("pro tips", NamedTextColor.GOLD, TextDecoration.BOLD)) + .append(Component.newline()) + .append(Component.newline()) + .append(Component.text("- use wall signs only.", NamedTextColor.BLACK)) + .append(Component.newline()) + .append(Component.text("- use abbreviations like ", NamedTextColor.BLACK)) + .append(Component.text("dia", NamedTextColor.BLUE)) + .append(Component.text(" for diamond.", NamedTextColor.BLACK)) + .append(Component.newline()) + .append(Component.text("- shops are ", NamedTextColor.BLACK)) + .append(Component.text("off", NamedTextColor.RED)) + .append(Component.text(" by default to prevent accidents.", NamedTextColor.BLACK)) + .build()); + + Book book = Book.book(Component.text("oyeshops manual"), Component.text("oyeshops"), pages); + player.openBook(book); + } +}