me.deecaad.core.commands.HelpCommandBuilder Maven / Gradle / Ivy
package me.deecaad.core.commands;
import me.deecaad.core.MechanicsCore;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextDecoration;
import org.bukkit.ChatColor;
import java.util.List;
import java.util.Locale;
import static net.kyori.adventure.text.Component.newline;
import static net.kyori.adventure.text.Component.text;
/**
* Consider the command: /wm test explosion sphere 5.0 3 DEFAULT #logs
*
*
* It is relatively intuitive, but what is 'test'? 'explosion'? What other options do we have? What
* do they all do? We want to be able to use /wm help, /wm help test, /wm help test explosion, and
* /wm help test explosion sphere.
*
*
* For any command with sub-commands, 'help' should list the sub-commands with their descriptions.
* Therefore, /wm help test explosion produces:
*
* {@code
* /wm test explosion: Spawns in an explosion that regenerates
*
* : Causes a spherical explosion
* : Causes a cubical explosion
* : Causes a parabolic explosion
* : Causes a vanilla MC explosion
* }
*
*
*
*
*
* For any command without sub-commands, we should give the user information on the arguments of the
* command. Therefore, /wm help give produces:
*
* {@code
* /wm give: Gives the target(s) with requested weapon(s)
* Usage: /wm give
* Permission: weaponmechanics.commands.give
* Aliases: [] #
*
* : Who is given the weapon(s).
* : Which weapon to choose, use "*" to give all.
* : How many weapons should be given. Default: 1
* : Should any extra data be added to the weapon? Default: {}
* }
*
*
*/
public class HelpCommandBuilder {
/**
* Don't let anyone instantiate this class
*/
private HelpCommandBuilder() {
}
public static void register(CommandBuilder command, HelpColor color) {
CommandBuilder help = new CommandBuilder("help")
.withAliases("?")
.withDescription(command.label);
buildHelp(help, command, color);
help.withDescription("Displays useful information about how to use the commands");
command.withSubcommand(help);
}
private static void buildHelp(CommandBuilder help, CommandBuilder parent, HelpColor color) {
for (CommandBuilder subcommand : parent.subcommands) {
CommandBuilder subHelp = new CommandBuilder(subcommand.label)
.withPermission(subcommand.permission)
.withRequirements(subcommand.requirements)
.withDescription(help.description + " " + subcommand.label);
help.withSubcommand(subHelp);
buildHelp(subHelp, subcommand, color);
}
// When a command has no sub-commands, we need to build an argument-based
// help command (Which will show the player how to use command arguments)
if (help.subcommands.isEmpty()) {
help.cache = buildCommandWithoutSubcommands(help, parent, color).build();
help.executes(CommandExecutor.any((sender, args) -> {
MechanicsCore.getPlugin().adventure.sender(sender).sendMessage(help.cache);
}));
// When a sub-command has at least 1 required argument, then we
// should show the help information by default. Consider the
// command '/wm give'. It fails since it is missing the required
// arguments, so we should instead execute as if the user had
// run the command '/wm help give'
if (!parent.args.isEmpty() && parent.args.get(0).isRequired()) {
CommandBuilder friend = new CommandBuilder(parent.label)
.withDescription(parent.description)
.withPermission(parent.permission)
.withRequirements(parent.requirements);
friend.executes(CommandExecutor.any((sender, args) -> {
MechanicsCore.getPlugin().adventure.sender(sender).sendMessage(help.cache);
}));
parent.friend = friend;
}
}
// When a command has sub-commands, we need to build a list-based
// help command (Which will show the player which commands they can use)
else {
help.cache = buildCommandWithSubcommands(help, parent, color).build();
help.executes(CommandExecutor.any((sender, args) -> {
MechanicsCore.getPlugin().adventure.sender(sender).sendMessage(help.cache);
}));
// Consider the command '/wm'. It doesn't have an executor, and it
// should show useful information.
if (parent.executor == null) {
parent.executes(CommandExecutor.any((sender, args) -> {
MechanicsCore.getPlugin().adventure.sender(sender).sendMessage(help.cache);
}));
}
}
}
private static TextComponent.Builder universal(CommandBuilder help, CommandBuilder parent, HelpColor color) {
TextComponent.Builder builder = text();
builder.append(text().content("/" + help.description + ": ").style(color.a));
builder.append(text().content(String.valueOf(parent.description)).style(color.b));
builder.append(newline());
// If a permission is present, lets add "click to copy" support.
if (parent.permission != null) {
builder.append(text().content("Permission: ").style(color.a).clickEvent(ClickEvent.copyToClipboard(parent.permission.getName())).hoverEvent(text("Click to copy")));
builder.append(text().content(parent.permission.getName()).style(color.b).clickEvent(ClickEvent.copyToClipboard(parent.permission.getName())).hoverEvent(text("Click to copy")));
builder.append(newline());
}
if (!parent.aliases.isEmpty()) {
builder.append(text().content("Aliases: ").style(color.a));
builder.append(text().content(String.join(", ", parent.aliases)).style(color.b));
builder.append(newline());
}
return builder;
}
private static TextComponent.Builder buildCommandWithSubcommands(CommandBuilder help, CommandBuilder parent, HelpColor color) {
TextComponent.Builder builder = universal(help, parent, color);
builder.append(newline());
List subcommands = parent.subcommands;
for (int i = 0; i < subcommands.size(); i++) {
CommandBuilder subcommand = subcommands.get(i);
builder.append(text().content("<" + subcommand.label + ">: ").style(color.a).clickEvent(ClickEvent.suggestCommand("/" + help.description + " " + subcommand.label)).hoverEvent(
subcommand.cache));
builder.append(text().content(String.valueOf(subcommand.description)).style(color.b).clickEvent(ClickEvent.suggestCommand("/" + help.description + " " + subcommand.label)).hoverEvent(
subcommand.cache));
if (i != subcommands.size() - 1)
builder.append(newline());
}
return builder;
}
private static TextComponent.Builder buildCommandWithoutSubcommands(CommandBuilder help, CommandBuilder parent, HelpColor color) {
TextComponent.Builder builder = universal(help, parent, color);
// Show how to use the command + allow them to click to auto-fill
builder.append(text().content("Usage: ").style(color.a));
builder.append(text().content("/" + help.description).style(color.b).clickEvent(ClickEvent.suggestCommand("/" + help.description)).hoverEvent(text("Click to run")));
// Shows <...>
for (Argument> arg : parent.args)
arg.append(builder, color.b);
// When a command has arguments, we should show the descriptions of
// each argument
if (!parent.args.isEmpty()) {
builder.append(newline());
builder.append(newline());
// Shows : \n : \n...
List> args = parent.args;
for (int i = 0; i < args.size(); i++) {
Argument> arg = args.get(i);
arg.append(builder, color.a);
builder.append(text().content(": ").style(color.a));
builder.append(text().content(String.valueOf(arg.description)).style(color.b));
if (i != args.size() - 1)
builder.append(newline());
}
}
return builder;
}
public static final class HelpColor {
public Style a;
public Style b;
public String symbol;
public HelpColor(Style a, Style b, String symbol) {
this.a = a;
this.b = b;
this.symbol = symbol;
}
public static HelpColor from(ChatColor a, ChatColor b, char c) {
return new HelpColor(
Style.style(NamedTextColor.NAMES.value(a.name().toLowerCase(Locale.ROOT)), TextDecoration.BOLD),
Style.style(NamedTextColor.NAMES.value(b.name().toLowerCase(Locale.ROOT))),
String.valueOf(c));
}
}
}