diff --git a/src/main/java/net/buildtheearth/buildteamtools/BuildTeamTools.java b/src/main/java/net/buildtheearth/buildteamtools/BuildTeamTools.java index e9314e42..153f7435 100644 --- a/src/main/java/net/buildtheearth/buildteamtools/BuildTeamTools.java +++ b/src/main/java/net/buildtheearth/buildteamtools/BuildTeamTools.java @@ -90,6 +90,10 @@ public MainConfig getMainConfig() { return mainConfigLoader.getConfig(); } + public NavigationConfig getNavigationConfig() { + return navigationConfigLoader.getConfig(); + } + @Override public void saveConfig() { } diff --git a/src/main/java/net/buildtheearth/buildteamtools/modules/common/commands/BttCommandManager.java b/src/main/java/net/buildtheearth/buildteamtools/modules/common/commands/BttCommandManager.java index c44a4f32..ac91d2a3 100644 --- a/src/main/java/net/buildtheearth/buildteamtools/modules/common/commands/BttCommandManager.java +++ b/src/main/java/net/buildtheearth/buildteamtools/modules/common/commands/BttCommandManager.java @@ -38,7 +38,7 @@ public final class BttCommandManager { private static final Set COMMAND_NAMES = Set.of( "btt", "generate", "kml", "geopoints", "geopath", "georing", "geosurface", - "navigator", "buildteam", "btwarps", "explore", "address", "warp", "blockpalette", + "navigator", "buildteam", "btwarps", "address", "warp", "blockpalette", "help", "communicators", "cache", "debug", "checkforupdates", "reload-config", "update" ); private static final ParserDescriptor ARGUMENT_PARSER = @@ -423,7 +423,6 @@ private List configuredAliases(String name) { case "navigator" -> labels.navigator(); case "buildteam" -> labels.buildteam(); case "btwarps" -> labels.btwarps(); - case "explore" -> labels.explore(); case "address" -> labels.address(); case "warp" -> labels.warp(); case "blockpalette" -> labels.blockpalette(); @@ -475,7 +474,6 @@ private static RichDescription descriptionFor(String name) { case "navigator" -> "Open the build team navigation menu."; case "buildteam" -> "Switch to a build team by name or tag."; case "btwarps" -> "Open the warp menu for a build team."; - case "explore" -> "Explore nearby build team regions."; case "address" -> "Look up the address at your current location."; case "warp" -> "Warp to a saved location."; case "blockpalette" -> "Open the block palette menu."; diff --git a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/NavigationModule.java b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/NavigationModule.java index 015c023c..a190f012 100644 --- a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/NavigationModule.java +++ b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/NavigationModule.java @@ -9,7 +9,6 @@ import net.buildtheearth.buildteamtools.modules.navigation.components.bluemap.BluemapComponent; import net.buildtheearth.buildteamtools.modules.navigation.components.navigator.NavigatorComponent; import net.buildtheearth.buildteamtools.modules.navigation.components.navigator.commands.BuildteamCommand; -import net.buildtheearth.buildteamtools.modules.navigation.components.navigator.commands.ExploreCommand; import net.buildtheearth.buildteamtools.modules.navigation.components.navigator.commands.NavigatorCommand; import net.buildtheearth.buildteamtools.modules.navigation.components.navigator.listeners.NavigatorJoinListener; import net.buildtheearth.buildteamtools.modules.navigation.components.navigator.listeners.NavigatorOpenListener; @@ -166,7 +165,6 @@ public void registerCommands() { new NavigatorCommand().register(this); new BuildteamCommand().register(this); new BtWarpsCommand().register(this); - new ExploreCommand().register(this); new AddressCommand().register(this); } diff --git a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/navigator/commands/ExploreCommand.java b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/navigator/commands/ExploreCommand.java deleted file mode 100644 index 26affd1e..00000000 --- a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/navigator/commands/ExploreCommand.java +++ /dev/null @@ -1,36 +0,0 @@ -package net.buildtheearth.buildteamtools.modules.navigation.components.navigator.commands; - -import com.alpsbte.alpslib.utils.ChatHelper; -import net.buildtheearth.buildteamtools.modules.navigation.menu.ExploreMenu; -import net.buildtheearth.buildteamtools.BuildTeamTools; -import net.buildtheearth.buildteamtools.modules.Module; -import net.buildtheearth.buildteamtools.modules.network.model.Permissions; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.jspecify.annotations.NonNull; - -/** - * Command to open the BuildTeams explore menu. - * Usage: /explore - */ -public class ExploreCommand { - public void register(Module owner) { - BuildTeamTools.getInstance().getCommandManager().registerNoArguments( - owner, "explore", Permissions.NAVIGATOR_USE, this::execute); - } - - private void execute(@NonNull CommandSender sender) { - if (!(sender instanceof Player player)) { - sender.sendMessage(ChatHelper.getErrorString("You must be a %s to %s this command!", "player", "execute")); - return; - } - - if (!player.hasPermission(Permissions.NAVIGATOR_USE)) { - player.sendMessage(ChatHelper.getErrorString("You don't have permission to use this command!")); - return; - } - - // Open the buildteams explore menu with a back reference to the main navigator - new ExploreMenu(player, true); - } -} diff --git a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/navigator/commands/NavigatorCommand.java b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/navigator/commands/NavigatorCommand.java index 235623f9..9d9517ad 100644 --- a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/navigator/commands/NavigatorCommand.java +++ b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/navigator/commands/NavigatorCommand.java @@ -1,48 +1,162 @@ package net.buildtheearth.buildteamtools.modules.navigation.components.navigator.commands; import com.alpsbte.alpslib.utils.ChatHelper; -import net.buildtheearth.buildteamtools.modules.navigation.NavigationModule; +import io.papermc.paper.command.brigadier.CommandSourceStack; import net.buildtheearth.buildteamtools.BuildTeamTools; import net.buildtheearth.buildteamtools.modules.Module; +import net.buildtheearth.buildteamtools.modules.common.commands.BttCommandManager; +import net.buildtheearth.buildteamtools.modules.navigation.NavigationModule; +import net.buildtheearth.buildteamtools.modules.navigation.menu.CitySelectorMenu; +import net.buildtheearth.buildteamtools.modules.navigation.menu.CountrySelectorMenu; +import net.buildtheearth.buildteamtools.modules.navigation.menu.ExploreMenu; import net.buildtheearth.buildteamtools.modules.navigation.menu.MainMenu; +import net.buildtheearth.buildteamtools.modules.navigation.menu.StateSelectorMenu; +import net.buildtheearth.buildteamtools.modules.network.model.Continent; import net.buildtheearth.buildteamtools.modules.network.model.Permissions; +import net.buildtheearth.buildteamtools.modules.network.model.Region; +import net.buildtheearth.buildteamtools.utils.io.NavigationConfig; +import net.kyori.adventure.text.Component; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.incendo.cloud.Command; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.minecraft.extras.RichDescription; +import org.incendo.cloud.parser.standard.StringParser; +import org.incendo.cloud.suggestion.SuggestionProvider; import org.jspecify.annotations.NonNull; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.function.Consumer; +import java.util.function.UnaryOperator; + public class NavigatorCommand { + private static final String CONTINENT_ARGUMENT = "continent"; + private static final String STATE_ARGUMENT = "state"; + private static final String PLAYER_ONLY_MESSAGE = "You must be a %s to %s this command!"; + public void register(Module owner) { var commandManager = BuildTeamTools.getInstance().getCommandManager(); - commandManager.registerNoArguments(owner, "navigator", Permissions.NAVIGATOR_USE, this::execute); - commandManager.registerSubcommand(owner, "navigator", "toggle", - "Toggle the navigator item on or off.", - builder -> builder.permission(Permissions.NAVIGATOR_USE), - context -> toggle(context.sender().getSender())); + commandManager.registerNoArguments(owner, "navigator", Permissions.NAVIGATOR_USE, this::openNavigator); + registerSubcommand(owner, "explore", "Open the continent selection menu.", + context -> openPlayer(context.sender().getSender(), player -> new ExploreMenu(player, true))); + registerSubcommand(owner, "countries", "Open the country selection menu for a continent.", + builder -> builder + .permission(Permissions.NAVIGATOR_USE) + .required(CONTINENT_ARGUMENT, StringParser.greedyStringParser(), + RichDescription.of(Component.text("Continent name")), + SuggestionProvider.blockingStrings((context, input) -> + BttCommandManager.matchingSuggestions(continentNames(), input.remainingInput()))), + context -> { + CommandSender sender = context.sender().getSender(); + Continent continent = parseContinent(context.get(CONTINENT_ARGUMENT)); + if (continent == null) { + sender.sendMessage(ChatHelper.getErrorString("Unknown continent. Available continents: %s", + String.join(", ", continentNames()))); + return; + } + openPlayer(sender, player -> new CountrySelectorMenu(player, continent, true)); + }); + registerSubcommand(owner, "states", "Open the USA state selection menu.", + context -> openPlayer(context.sender().getSender(), + player -> new StateSelectorMenu(player, Continent.NORTH_AMERICA, true))); + registerSubcommand(owner, "cities", "Open the city selection menu for a supported state.", + builder -> builder + .permission(Permissions.NAVIGATOR_USE) + .required(STATE_ARGUMENT, StringParser.greedyStringParser(), + RichDescription.of(Component.text("State name")), + SuggestionProvider.blockingStrings((context, input) -> + BttCommandManager.matchingSuggestions(cityStateNames(), input.remainingInput()))), + context -> { + CommandSender sender = context.sender().getSender(); + String stateName = context.get(STATE_ARGUMENT).replace('_', ' '); + Region state = StateSelectorMenu.getCityStates().stream() + .filter(region -> region.getName().equalsIgnoreCase(stateName)) + .findFirst().orElse(null); + if (state == null) { + sender.sendMessage(ChatHelper.getErrorString( + "No city menu is available for that state. Available states: %s", + String.join(", ", cityStateNames()))); + return; + } + openPlayer(sender, player -> new CitySelectorMenu( + state, StateSelectorMenu.getCityTeamsForState(state), player, true)); + }); + // The module constructor registers commands before its own config field is set + NavigationConfig.MainMenuItems items = BuildTeamTools.getInstance().getNavigationConfig().mainMenuItems(); + if (items.buildItem().enabled()) { + registerSubcommand(owner, "build", "Run the configured Build menu action.", + context -> openPlayer(context.sender().getSender(), MainMenu::openBuild)); + } + if (items.plotsystemItem().enabled()) { + registerSubcommand(owner, "plotsystem", "Run the configured Plot System menu action.", + context -> openPlayer(context.sender().getSender(), MainMenu::openPlotSystem)); + } + if (items.tutorialItem().enabled()) { + registerSubcommand(owner, "tutorials", "Open the tutorial menu or run its configured action.", + context -> openPlayer(context.sender().getSender(), MainMenu::openTutorials)); + } + registerSubcommand(owner, "toggle", "Toggle the navigator hotbar item.", + context -> openPlayer(context.sender().getSender(), + NavigationModule.getInstance().getNavigatorComponent()::toggle)); } - private void execute(@NonNull CommandSender sender) { - if (!(sender instanceof Player player)) { - sender.sendMessage(ChatHelper.getErrorString("You must be a %s to %s this command!", "player", "execute")); - return; - } + private void registerSubcommand( + Module owner, + String name, + String description, + Consumer> handler + ) { + registerSubcommand(owner, name, description, + builder -> builder.permission(Permissions.NAVIGATOR_USE), handler); + } - if (!player.hasPermission(Permissions.NAVIGATOR_USE)) { - player.sendMessage(ChatHelper.getErrorString("You don't have permission to use this command!")); - return; - } + private void registerSubcommand( + Module owner, + String name, + String description, + UnaryOperator> argumentBuilder, + Consumer> handler + ) { + BuildTeamTools.getInstance().getCommandManager().registerSubcommand( + owner, "navigator", name, description, + argumentBuilder, + handler); + } - new MainMenu(player); + private void openNavigator(@NonNull CommandSender sender) { + openPlayer(sender, MainMenu::new); } - private void toggle(@NonNull CommandSender sender) { + private void openPlayer(@NonNull CommandSender sender, Consumer action) { if (!(sender instanceof Player player)) { - sender.sendMessage(ChatHelper.getErrorString("You must be a %s to %s this command!", "player", "execute")); - return; - } - if (!player.hasPermission(Permissions.NAVIGATOR_USE)) { - player.sendMessage(ChatHelper.getErrorString("You don't have permission to use this command!")); + sendPlayerOnlyMessage(sender); return; } - NavigationModule.getInstance().getNavigatorComponent().toggle(player); + action.accept(player); + } + + private void sendPlayerOnlyMessage(@NonNull CommandSender sender) { + sender.sendMessage(ChatHelper.getErrorString(PLAYER_ONLY_MESSAGE, "player", "execute")); + } + + private static List continentNames() { + return Arrays.stream(Continent.values()) + .map(continent -> continent.name().toLowerCase(Locale.ROOT)) + .toList(); + } + + private static Continent parseContinent(String name) { + String normalized = name.replace('_', ' ').replace('-', ' '); + return Arrays.stream(Continent.values()) + .filter(continent -> continent.getLabel().equalsIgnoreCase(normalized)) + .findFirst().orElse(null); + } + + private static List cityStateNames() { + return StateSelectorMenu.getCityStates().stream() + .map(region -> region.getName().replace(' ', '_')) + .distinct().toList(); } } diff --git a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/menu/MainMenu.java b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/menu/MainMenu.java index 6ab01f59..87ff99a7 100644 --- a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/menu/MainMenu.java +++ b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/menu/MainMenu.java @@ -36,7 +36,11 @@ public class MainMenu extends AbstractMenu { private static final String INVENTORY_NAME = "BuildTheEarth Navigator"; public MainMenu(Player menuPlayer) { - super(3, INVENTORY_NAME, menuPlayer); + this(menuPlayer, true); + } + + public MainMenu(Player menuPlayer, boolean autoLoad) { + super(3, INVENTORY_NAME, menuPlayer, autoLoad); } @Override @@ -87,7 +91,7 @@ protected void setItemClickEventsAsync() { getMenu().getSlot(Objects.requireNonNull(slots.pollFirst())) .setClickHandler((clickPlayer, clickInformation) -> { clickPlayer.closeInventory(); - performClickAction(clickPlayer, config().mainMenuItems().buildItem().action().replace("&", "§"), "build"); + openBuild(clickPlayer); }); } @@ -96,7 +100,7 @@ protected void setItemClickEventsAsync() { getMenu().getSlot(Objects.requireNonNull(slots.pollFirst())) .setClickHandler((clickPlayer, clickInformation) -> { clickPlayer.closeInventory(); - performClickAction(clickPlayer, config().mainMenuItems().plotsystemItem().action().replace("&", "§"), "plotsystem"); + openPlotSystem(clickPlayer); }); } @@ -118,15 +122,7 @@ protected void setItemClickEventsAsync() { if (config().mainMenuItems().tutorialItem().enabled()) { getMenu().getSlot(Objects.requireNonNull(slots.pollFirst())).setClickHandler((clickPlayer, clickInformation) -> { clickPlayer.closeInventory(); - String action = config().mainMenuItems().tutorialItem().action(); - - // If no command or message is set, open the tutorial menu - if (action == null || action.equals("/command") || action.equals("message")) { - new TutorialsMenu(clickPlayer); - return; - } - - performClickAction(clickPlayer, action.replace("&", "§"), "tutorial"); + openTutorials(clickPlayer); }); } } @@ -189,7 +185,7 @@ protected Mask getMask() { return slots; } - private @NotNull NavigationConfig config() { + private static @NotNull NavigationConfig config() { NavigationModule navigationModule = NavigationModule.getInstance(); if (navigationModule == null) { throw new IllegalStateException("Navigation module is not initialized"); @@ -197,7 +193,34 @@ protected Mask getMask() { return navigationModule.getConfig(); } - private void performClickAction(Player p, String action, String type) { + public static void openBuild(Player player) { + openConfiguredItem(player, config().mainMenuItems().buildItem(), "build"); + } + + public static void openPlotSystem(Player player) { + openConfiguredItem(player, config().mainMenuItems().plotsystemItem(), "plotsystem"); + } + + public static void openTutorials(Player player) { + openConfiguredItem(player, config().mainMenuItems().tutorialItem(), "tutorial"); + } + + static void openConfiguredItem(Player player, NavigationConfig.MenuItem item, String type) { + if (!item.enabled()) { + player.sendMessage(ChatHelper.getErrorString("The %s item is disabled in the navigation config.", type)); + return; + } + String action = item.action(); + if (type.equals("tutorial") && (action == null || action.equals("/command") || action.equals("message"))) { + new TutorialsMenu(player); + } else if (action == null) { + player.sendMessage(ChatHelper.getErrorString("No action is set for the %s item in the config yet! Please contact an %s.", type, "admin")); + } else { + performClickAction(player, action.replace("&", "§"), type); + } + } + + private static void performClickAction(Player p, String action, String type) { // Check if an action is set in the config if (action.startsWith("transfer:")) { NavUtils.transferPlayer(p, action.substring(9)); diff --git a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/menu/StateSelectorMenu.java b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/menu/StateSelectorMenu.java index ca9167d5..666cd4d8 100644 --- a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/menu/StateSelectorMenu.java +++ b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/menu/StateSelectorMenu.java @@ -52,13 +52,15 @@ public StateSelectorMenu(Player menuPlayer, @NonNull Continent continent, boolea this.stateEntries = loadStateEntries(continent); } - private @NonNull List loadStateEntries(@NonNull Continent continent) { - List entries = new ArrayList<>(); - + private static @NonNull List loadStateEntries(@NonNull Continent continent) { NetworkModule networkModule = NetworkModule.getInstance(); - BuildTeam currentTeam = networkModule.getBuildTeam(); + return loadStateEntries(continent, networkModule.getBuildTeams(), networkModule.getBuildTeam()); + } - for (BuildTeam team : networkModule.getBuildTeams()) { + private static @NonNull List loadStateEntries( + @NonNull Continent continent, @NonNull List teams, BuildTeam currentTeam) { + List entries = new ArrayList<>(); + for (BuildTeam team : teams) { if (shouldSkipTeam(team, currentTeam)) { continue; } @@ -70,7 +72,7 @@ public StateSelectorMenu(Player menuPlayer, @NonNull Continent continent, boolea return entries; } - private boolean shouldSkipTeam(BuildTeam team, BuildTeam currentTeam) { + private static boolean shouldSkipTeam(BuildTeam team, BuildTeam currentTeam) { if (team == null || team.getID() == null) { return true; } @@ -82,7 +84,7 @@ private boolean shouldSkipTeam(BuildTeam team, BuildTeam currentTeam) { return team.getID().equals(currentTeam.getID()); } - private void addStateEntriesForTeam( + private static void addStateEntriesForTeam( @NonNull List entries, @NonNull BuildTeam team, @NonNull Continent continent @@ -96,7 +98,7 @@ private void addStateEntriesForTeam( } } - private boolean isStateInContinent(@NonNull Region region, @NonNull Continent continent) { + private static boolean isStateInContinent(@NonNull Region region, @NonNull Continent continent) { return region.getType() == RegionType.STATE && region.getContinent() == continent; } @@ -106,18 +108,35 @@ private boolean isStateInContinent(@NonNull Region region, @NonNull Continent co * Temporary workaround: city-to-state relationships are not available yet, * so New York is treated as the only state with city-level build teams. */ - private boolean hasCitiesForState(@NonNull Region stateRegion) { + private static boolean hasCitiesForState(@NonNull Region stateRegion) { return NEW_YORK_STATE_NAME.equals(stateRegion.getName()); } - private @NonNull List getCityTeamsForState(@NonNull Region stateRegion) { + public static @NonNull List getCityStates() { + NetworkModule networkModule = NetworkModule.getInstance(); + return getCityStates(networkModule.getBuildTeams(), networkModule.getBuildTeam()); + } + + static @NonNull List getCityStates(@NonNull List teams, BuildTeam currentTeam) { + return loadStateEntries(Continent.NORTH_AMERICA, teams, currentTeam).stream() + .filter(StateEntry::hasSubCities) + .map(StateEntry::stateRegion) + .toList(); + } + + public static @NonNull List getCityTeamsForState(@NonNull Region stateRegion) { + return getCityTeamsForState(stateRegion, NetworkModule.getInstance().getBuildTeams()); + } + + static @NonNull List getCityTeamsForState( + @NonNull Region stateRegion, @NonNull List teams) { if (!hasCitiesForState(stateRegion)) { return List.of(); } List cityTeams = new ArrayList<>(); - for (BuildTeam team : NetworkModule.getInstance().getBuildTeams()) { + for (BuildTeam team : teams) { if (team == null || team.getID() == null) { continue; } @@ -130,7 +149,7 @@ private boolean hasCitiesForState(@NonNull Region stateRegion) { return cityTeams; } - private boolean hasNewYorkCityRegion(@NonNull BuildTeam team) { + private static boolean hasNewYorkCityRegion(@NonNull BuildTeam team) { for (Region region : team.getRegions()) { if (region.getType() != RegionType.CITY) { continue; diff --git a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/menu/TutorialsMenu.java b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/menu/TutorialsMenu.java index b19977b9..e985ff66 100644 --- a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/menu/TutorialsMenu.java +++ b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/menu/TutorialsMenu.java @@ -23,7 +23,7 @@ public TutorialsMenu(Player player) { @Override protected void setPreviewItems() { - setBackItem(BACK_BUTTON_SLOT, new MainMenu(getMenuPlayer())); + setBackItem(BACK_BUTTON_SLOT, new MainMenu(getMenuPlayer(), false)); super.setPreviewItems(); } diff --git a/src/main/java/net/buildtheearth/buildteamtools/utils/io/MainConfig.java b/src/main/java/net/buildtheearth/buildteamtools/utils/io/MainConfig.java index 81bdb694..88f798fc 100644 --- a/src/main/java/net/buildtheearth/buildteamtools/utils/io/MainConfig.java +++ b/src/main/java/net/buildtheearth/buildteamtools/utils/io/MainConfig.java @@ -72,7 +72,6 @@ interface Labels { default List navigator() { return List.of("navigator", "nav", "navigate"); } default List buildteam() { return List.of("buildteam", "bt"); } default List btwarps() { return List.of("btwarps", "btwarp", "wbt", "wpt", "buildteamwarps"); } - default List explore() { return List.of("explore"); } default List address() { return List.of("address"); } default List warp() { return List.of("warp", "warps", "wp"); } default List blockpalette() { return List.of("blockpalette", "bp", "blocks"); } diff --git a/src/main/java/net/buildtheearth/buildteamtools/utils/io/NavigationConfig.java b/src/main/java/net/buildtheearth/buildteamtools/utils/io/NavigationConfig.java index bcd70295..513f74d6 100644 --- a/src/main/java/net/buildtheearth/buildteamtools/utils/io/NavigationConfig.java +++ b/src/main/java/net/buildtheearth/buildteamtools/utils/io/NavigationConfig.java @@ -68,10 +68,10 @@ interface MainMenuItems { } interface MenuItem { - @Comments("Enable or disable this menu item.") + @Comments("Enable or disable this menu item and its Build, PlotSystem, or Tutorials navigator shortcut.") default boolean enabled() { return false; } - @Comments("Command or action executed when this menu item is clicked.") + @Comments("Command or action executed when this menu item is clicked. Also used by the Build, PlotSystem, and Tutorials navigator shortcuts.") default String action() { return "/command"; } }