feat(commands): unify settings and admin commands
This commit is contained in:
@@ -1,17 +1,35 @@
|
||||
package games.dmg.spigotbase;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.TreeSet;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabCompleter;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
final class BaseAdminCommand implements CommandExecutor {
|
||||
final class BaseAdminCommand implements CommandExecutor, TabCompleter {
|
||||
private static final List<String> SUBCOMMANDS = List.of(
|
||||
"status", "setlevel", "setprogress", "clearcooldown", "reset", "config"
|
||||
);
|
||||
private static final List<String> PATHS = List.of(
|
||||
"base", "size", "flight", "warmup", "cooldown"
|
||||
);
|
||||
private static final List<String> COUNTERS = List.of(
|
||||
"grass_dirt", "stone", "deepslate", "obsidian", "placements", "base_breaks"
|
||||
);
|
||||
private static final List<String> COOLDOWN_TYPES = List.of("personal", "visitor", "all");
|
||||
private static final List<String> RESET_PATHS = List.of(
|
||||
"size", "flight", "warmup", "cooldown", "all"
|
||||
);
|
||||
private static final List<String> LEVELS_THREE = List.of("0", "1", "2", "3");
|
||||
private static final List<String> LEVELS_FOUR = List.of("0", "1", "2", "3", "4");
|
||||
private final JavaPlugin plugin;
|
||||
private final BaseStateManager stateManager;
|
||||
private final AdminProgressionService progressionService;
|
||||
@@ -48,7 +66,7 @@ final class BaseAdminCommand implements CommandExecutor {
|
||||
return true;
|
||||
}
|
||||
return switch (arguments[0].toLowerCase(Locale.ROOT)) {
|
||||
case "progress" -> showProgress(sender, target.orElseThrow());
|
||||
case "status" -> showProgress(sender, target.orElseThrow(), arguments);
|
||||
case "setlevel" -> setLevel(sender, target.orElseThrow(), arguments);
|
||||
case "setprogress" -> setProgress(sender, target.orElseThrow(), arguments);
|
||||
case "clearcooldown" -> clearCooldown(sender, target.orElseThrow(), arguments);
|
||||
@@ -60,7 +78,15 @@ final class BaseAdminCommand implements CommandExecutor {
|
||||
};
|
||||
}
|
||||
|
||||
private boolean showProgress(CommandSender sender, PlayerState player) {
|
||||
private boolean showProgress(
|
||||
CommandSender sender,
|
||||
PlayerState player,
|
||||
String[] arguments
|
||||
) {
|
||||
if (arguments.length != 2) {
|
||||
sender.sendMessage(ChatColor.RED + "Usage: /baseadmin status <player>");
|
||||
return true;
|
||||
}
|
||||
sender.sendMessage(ChatColor.GOLD + "=== " + player.latestName() + " Base Progress ===");
|
||||
sender.sendMessage(ChatColor.YELLOW + "Levels: base=" + player.baseLevel()
|
||||
+ " size=" + player.sizeLevel() + " flight=" + player.flightLevel()
|
||||
@@ -89,8 +115,16 @@ final class BaseAdminCommand implements CommandExecutor {
|
||||
sender.sendMessage(ChatColor.RED + "Usage: /baseadmin setlevel <player> <path> <level>");
|
||||
return true;
|
||||
}
|
||||
final ProgressionPath path;
|
||||
try {
|
||||
path = ProgressionPath.valueOf(arguments[2].toUpperCase(Locale.ROOT));
|
||||
} catch (IllegalArgumentException exception) {
|
||||
sender.sendMessage(ChatColor.RED
|
||||
+ "Usage: /baseadmin setlevel <player> "
|
||||
+ "<base|size|flight|warmup|cooldown> <level>");
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
ProgressionPath path = ProgressionPath.valueOf(arguments[2].toUpperCase(Locale.ROOT));
|
||||
int level = Integer.parseInt(arguments[3]);
|
||||
PlayerState updated = stateManager.update(
|
||||
target.playerId(),
|
||||
@@ -100,6 +134,8 @@ final class BaseAdminCommand implements CommandExecutor {
|
||||
stateManager.saveIfDirty();
|
||||
sender.sendMessage(ChatColor.GREEN + "Set " + updated.latestName() + "'s "
|
||||
+ path.name().toLowerCase(Locale.ROOT) + " level to " + level + ".");
|
||||
} catch (NumberFormatException exception) {
|
||||
sender.sendMessage(ChatColor.RED + "The level must be an integer.");
|
||||
} catch (IllegalArgumentException exception) {
|
||||
sender.sendMessage(ChatColor.RED + exception.getMessage());
|
||||
}
|
||||
@@ -116,10 +152,18 @@ final class BaseAdminCommand implements CommandExecutor {
|
||||
+ "Usage: /baseadmin setprogress <player> <counter> <amount>");
|
||||
return true;
|
||||
}
|
||||
final ProgressCounter counter;
|
||||
try {
|
||||
ProgressCounter counter = ProgressCounter.valueOf(
|
||||
counter = ProgressCounter.valueOf(
|
||||
arguments[2].toUpperCase(Locale.ROOT).replace('-', '_')
|
||||
);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
sender.sendMessage(ChatColor.RED
|
||||
+ "Usage: /baseadmin setprogress <player> "
|
||||
+ "<grass_dirt|stone|deepslate|obsidian|placements|base_breaks> <amount>");
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
long amount = Long.parseLong(arguments[3]);
|
||||
PlayerState updated = stateManager.update(
|
||||
target.playerId(),
|
||||
@@ -142,7 +186,12 @@ final class BaseAdminCommand implements CommandExecutor {
|
||||
PlayerState target,
|
||||
String[] arguments
|
||||
) {
|
||||
String selection = arguments.length >= 3
|
||||
if (arguments.length < 2 || arguments.length > 3) {
|
||||
sender.sendMessage(ChatColor.RED
|
||||
+ "Usage: /baseadmin clearcooldown <player> [personal|visitor|all]");
|
||||
return true;
|
||||
}
|
||||
String selection = arguments.length == 3
|
||||
? arguments[2].toLowerCase(Locale.ROOT)
|
||||
: "all";
|
||||
if (!Arrays.asList("personal", "visitor", "all").contains(selection)) {
|
||||
@@ -186,8 +235,26 @@ final class BaseAdminCommand implements CommandExecutor {
|
||||
sender.sendMessage(ChatColor.GREEN + "Reset all progression for " + target.latestName() + ".");
|
||||
return true;
|
||||
}
|
||||
if (arguments.length != 3) {
|
||||
sender.sendMessage(ChatColor.RED
|
||||
+ "Usage: /baseadmin reset <player> <size|flight|warmup|cooldown|all> [confirm]");
|
||||
return true;
|
||||
}
|
||||
if (arguments[2].equalsIgnoreCase("base")) {
|
||||
sender.sendMessage(ChatColor.RED
|
||||
+ "Resetting base removes all progression. Use: /baseadmin reset "
|
||||
+ target.latestName() + " all confirm");
|
||||
return true;
|
||||
}
|
||||
final ProgressionPath path;
|
||||
try {
|
||||
path = ProgressionPath.valueOf(arguments[2].toUpperCase(Locale.ROOT));
|
||||
} catch (IllegalArgumentException exception) {
|
||||
sender.sendMessage(ChatColor.RED
|
||||
+ "Usage: /baseadmin reset <player> <size|flight|warmup|cooldown|all> [confirm]");
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
ProgressionPath path = ProgressionPath.valueOf(arguments[2].toUpperCase(Locale.ROOT));
|
||||
stateManager.update(
|
||||
target.playerId(),
|
||||
target.latestName(),
|
||||
@@ -239,6 +306,76 @@ final class BaseAdminCommand implements CommandExecutor {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(
|
||||
CommandSender sender,
|
||||
Command command,
|
||||
String alias,
|
||||
String[] arguments
|
||||
) {
|
||||
if (!sender.hasPermission("spigotbase.admin")) {
|
||||
return List.of();
|
||||
}
|
||||
if (arguments.length == 1) {
|
||||
String prefix = arguments[0].toLowerCase(Locale.ROOT);
|
||||
return SUBCOMMANDS.stream()
|
||||
.filter(subcommand -> subcommand.startsWith(prefix))
|
||||
.toList();
|
||||
}
|
||||
if (arguments.length == 2 && arguments[0].equalsIgnoreCase("config")) {
|
||||
String prefix = arguments[1].toLowerCase(Locale.ROOT);
|
||||
return plugin.getConfig().getValues(false).entrySet().stream()
|
||||
.filter(entry -> entry.getValue() instanceof Number)
|
||||
.map(java.util.Map.Entry::getKey)
|
||||
.filter(key -> key.toLowerCase(Locale.ROOT).startsWith(prefix))
|
||||
.sorted(String.CASE_INSENSITIVE_ORDER)
|
||||
.toList();
|
||||
}
|
||||
if (arguments.length == 2) {
|
||||
String prefix = arguments[1].toLowerCase(Locale.ROOT);
|
||||
TreeSet<String> names = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
|
||||
stateManager.knownPlayers().values().stream()
|
||||
.map(PlayerState::latestName)
|
||||
.forEach(names::add);
|
||||
if (plugin.getServer() != null) {
|
||||
plugin.getServer().getOnlinePlayers().stream()
|
||||
.map(Player::getName)
|
||||
.forEach(names::add);
|
||||
}
|
||||
return names.stream()
|
||||
.filter(name -> name.toLowerCase(Locale.ROOT).startsWith(prefix))
|
||||
.toList();
|
||||
}
|
||||
if (arguments.length == 3) {
|
||||
List<String> options = switch (arguments[0].toLowerCase(Locale.ROOT)) {
|
||||
case "setlevel" -> PATHS;
|
||||
case "setprogress" -> COUNTERS;
|
||||
case "clearcooldown" -> COOLDOWN_TYPES;
|
||||
case "reset" -> RESET_PATHS;
|
||||
default -> List.of();
|
||||
};
|
||||
return complete(options, arguments[2]);
|
||||
}
|
||||
if (arguments.length == 4 && arguments[0].equalsIgnoreCase("setlevel")) {
|
||||
List<String> levels = switch (arguments[2].toLowerCase(Locale.ROOT)) {
|
||||
case "base", "cooldown" -> LEVELS_FOUR;
|
||||
case "size", "flight", "warmup" -> LEVELS_THREE;
|
||||
default -> List.of();
|
||||
};
|
||||
return complete(levels, arguments[3]);
|
||||
}
|
||||
if (arguments.length == 4 && arguments[0].equalsIgnoreCase("reset")
|
||||
&& arguments[2].equalsIgnoreCase("all")) {
|
||||
return complete(List.of("confirm"), arguments[3]);
|
||||
}
|
||||
return List.of();
|
||||
}
|
||||
|
||||
private static List<String> complete(List<String> options, String input) {
|
||||
String prefix = input.toLowerCase(Locale.ROOT);
|
||||
return options.stream().filter(option -> option.startsWith(prefix)).toList();
|
||||
}
|
||||
|
||||
private Optional<PlayerState> resolve(String name) {
|
||||
Player online = Bukkit.getPlayerExact(name);
|
||||
if (online != null) {
|
||||
@@ -249,7 +386,7 @@ final class BaseAdminCommand implements CommandExecutor {
|
||||
|
||||
private static void sendUsage(CommandSender sender) {
|
||||
sender.sendMessage(ChatColor.YELLOW + "Usage: /baseadmin "
|
||||
+ "<progress|setlevel|setprogress|clearcooldown|reset> <player> ...");
|
||||
+ "<status|setlevel|setprogress|clearcooldown|reset> <player> ...");
|
||||
sender.sendMessage(ChatColor.YELLOW + " /baseadmin config <key> <integer>");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +1,16 @@
|
||||
package games.dmg.spigotbase;
|
||||
|
||||
import java.io.IOException;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.PlayerInventory;
|
||||
|
||||
final class BaseCommand implements CommandExecutor {
|
||||
private final BaseTeleportManager teleportManager;
|
||||
private final BaseStateManager stateManager;
|
||||
private final VisitorPolicy visitorPolicy;
|
||||
private final PluginSettingsProvider settings;
|
||||
|
||||
BaseCommand(
|
||||
BaseTeleportManager teleportManager,
|
||||
BaseStateManager stateManager,
|
||||
VisitorPolicy visitorPolicy,
|
||||
PluginSettingsProvider settings
|
||||
) {
|
||||
BaseCommand(BaseTeleportManager teleportManager) {
|
||||
this.teleportManager = teleportManager;
|
||||
this.stateManager = stateManager;
|
||||
this.visitorPolicy = visitorPolicy;
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -38,95 +23,7 @@ final class BaseCommand implements CommandExecutor {
|
||||
teleportManager.start(player);
|
||||
return true;
|
||||
}
|
||||
if (arguments.length == 1 && arguments[0].equalsIgnoreCase("upgrade")) {
|
||||
purchaseVisitorAccess(player);
|
||||
return true;
|
||||
}
|
||||
player.sendMessage(ChatColor.RED + "Usage: /base [upgrade]");
|
||||
player.sendMessage(ChatColor.RED + "Usage: /base");
|
||||
return true;
|
||||
}
|
||||
|
||||
private void purchaseVisitorAccess(Player player) {
|
||||
PlayerState state = stateManager.player(player.getUniqueId(), player.getName());
|
||||
if (!visitorPolicy.canPurchase(state)) {
|
||||
player.sendMessage(ChatColor.RED + (state.baseLevel() >= 4
|
||||
? "Base IV is already unlocked."
|
||||
: "You must unlock Base III before purchasing Base IV."));
|
||||
return;
|
||||
}
|
||||
int price = settings.current().visitorUnlockDiamondCost();
|
||||
Material currency = Material.valueOf(settings.current().visitorCurrencyMaterial());
|
||||
PlayerInventory inventory = player.getInventory();
|
||||
if (countCurrency(inventory, currency) < price) {
|
||||
player.sendMessage(ChatColor.RED + "Base IV costs " + price + " "
|
||||
+ currency.name().toLowerCase(java.util.Locale.ROOT) + ".");
|
||||
return;
|
||||
}
|
||||
ItemStack[] snapshot = cloneContents(inventory.getStorageContents());
|
||||
removeCurrency(inventory, currency, price);
|
||||
try {
|
||||
stateManager.updateAndSave(
|
||||
player.getUniqueId(),
|
||||
player.getName(),
|
||||
current -> current.withBaseLevel(4).withVisitorsEnabled(true)
|
||||
);
|
||||
} catch (IOException | RuntimeException exception) {
|
||||
inventory.setStorageContents(snapshot);
|
||||
player.sendMessage(ChatColor.RED + "The upgrade could not be saved; your diamonds were restored.");
|
||||
return;
|
||||
}
|
||||
player.sendTitle(
|
||||
ChatColor.GOLD + "Base IV Unlocked",
|
||||
ChatColor.YELLOW + "Visitors may now teleport to your base",
|
||||
settings.current().titleFadeInTicks(),
|
||||
settings.current().titleStayTicks(),
|
||||
settings.current().titleFadeOutTicks()
|
||||
);
|
||||
player.sendMessage(ChatColor.GREEN + "Base IV unlocked for " + price + " "
|
||||
+ currency.name().toLowerCase(java.util.Locale.ROOT) + ".");
|
||||
}
|
||||
|
||||
private static int countCurrency(PlayerInventory inventory, Material currency) {
|
||||
int count = 0;
|
||||
for (ItemStack item : inventory.getStorageContents()) {
|
||||
if (item != null && item.getType() == currency) {
|
||||
count += item.getAmount();
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private static void removeCurrency(
|
||||
PlayerInventory inventory,
|
||||
Material currency,
|
||||
int amount
|
||||
) {
|
||||
ItemStack[] contents = inventory.getStorageContents();
|
||||
int remaining = amount;
|
||||
for (int index = 0; index < contents.length && remaining > 0; index++) {
|
||||
ItemStack item = contents[index];
|
||||
if (item == null || item.getType() != currency) {
|
||||
continue;
|
||||
}
|
||||
int removed = Math.min(remaining, item.getAmount());
|
||||
remaining -= removed;
|
||||
int newAmount = item.getAmount() - removed;
|
||||
if (newAmount == 0) {
|
||||
contents[index] = null;
|
||||
} else {
|
||||
ItemStack reduced = item.clone();
|
||||
reduced.setAmount(newAmount);
|
||||
contents[index] = reduced;
|
||||
}
|
||||
}
|
||||
inventory.setStorageContents(contents);
|
||||
}
|
||||
|
||||
private static ItemStack[] cloneContents(ItemStack[] contents) {
|
||||
ItemStack[] copy = new ItemStack[contents.length];
|
||||
for (int index = 0; index < contents.length; index++) {
|
||||
copy[index] = contents[index] == null ? null : contents[index].clone();
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
package games.dmg.spigotbase;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabCompleter;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
final class BaseFlightCommand implements CommandExecutor, TabCompleter {
|
||||
private static final List<String> MODES = List.of("on", "off");
|
||||
private final BaseStateManager stateManager;
|
||||
private final BaseFlightController controller;
|
||||
|
||||
BaseFlightCommand(BaseStateManager stateManager, BaseFlightController controller) {
|
||||
this.stateManager = stateManager;
|
||||
this.controller = controller;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] arguments) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage("Only players can use base flight.");
|
||||
return true;
|
||||
}
|
||||
PlayerState state = stateManager.player(player.getUniqueId(), player.getName());
|
||||
if (state.flightLevel() < 1) {
|
||||
player.sendMessage(ChatColor.RED + "Base flight is still locked.");
|
||||
return true;
|
||||
}
|
||||
final boolean enabled;
|
||||
try {
|
||||
enabled = TogglePreference.resolve(state.flightEnabled(), arguments);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
player.sendMessage(ChatColor.RED + "Usage: /baseflight [on|off]");
|
||||
return true;
|
||||
}
|
||||
state = stateManager.update(
|
||||
player.getUniqueId(),
|
||||
player.getName(),
|
||||
current -> current.withFlightEnabled(enabled)
|
||||
);
|
||||
if (!state.flightEnabled()) {
|
||||
controller.removeGrantedFlight(player);
|
||||
}
|
||||
stateManager.saveIfDirty();
|
||||
player.sendMessage(ChatColor.YELLOW + "Base flight is now "
|
||||
+ (state.flightEnabled() ? ChatColor.GREEN + "on" : ChatColor.RED + "off")
|
||||
+ ChatColor.YELLOW + ".");
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(
|
||||
CommandSender sender,
|
||||
Command command,
|
||||
String alias,
|
||||
String[] arguments
|
||||
) {
|
||||
if (arguments.length != 1) {
|
||||
return List.of();
|
||||
}
|
||||
String prefix = arguments[0].toLowerCase(Locale.ROOT);
|
||||
return MODES.stream().filter(mode -> mode.startsWith(prefix)).toList();
|
||||
}
|
||||
}
|
||||
@@ -80,7 +80,7 @@ final class BaseFlightController implements Runnable {
|
||||
stateManager.saveIfDirty();
|
||||
player.sendTitle(
|
||||
ChatColor.GOLD + "Base Flight " + roman(updated.flightLevel()) + " Unlocked",
|
||||
ChatColor.YELLOW + "Use /baseflight to toggle flight",
|
||||
ChatColor.YELLOW + "Use /basesettings flight enable to enable flight",
|
||||
settings.current().titleFadeInTicks(),
|
||||
settings.current().titleStayTicks(),
|
||||
settings.current().titleFadeOutTicks()
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
package games.dmg.spigotbase;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabCompleter;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
final class BaseNavigationCommand implements CommandExecutor, TabCompleter {
|
||||
private static final List<String> MODES = List.of("on", "off");
|
||||
|
||||
private final BaseStateManager stateManager;
|
||||
|
||||
BaseNavigationCommand(BaseStateManager stateManager) {
|
||||
this.stateManager = stateManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] arguments) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage("Only players can use base navigation.");
|
||||
return true;
|
||||
}
|
||||
PlayerState state = stateManager.player(player.getUniqueId(), player.getName());
|
||||
if (state.baseLevel() < 2) {
|
||||
player.sendMessage(ChatColor.RED + "Base II navigation is still locked.");
|
||||
return true;
|
||||
}
|
||||
if (state.base().isEmpty()) {
|
||||
player.sendMessage(ChatColor.RED + "Set your base before enabling navigation.");
|
||||
return true;
|
||||
}
|
||||
final boolean enabled;
|
||||
try {
|
||||
enabled = TogglePreference.resolve(state.navigationEnabled(), arguments);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
player.sendMessage(ChatColor.RED + "Usage: /basenavigation [on|off]");
|
||||
return true;
|
||||
}
|
||||
state = stateManager.update(
|
||||
player.getUniqueId(),
|
||||
player.getName(),
|
||||
current -> current.withNavigationEnabled(enabled)
|
||||
);
|
||||
stateManager.saveIfDirty();
|
||||
player.sendMessage(ChatColor.YELLOW + "Base navigation is now "
|
||||
+ (state.navigationEnabled() ? ChatColor.GREEN + "on" : ChatColor.RED + "off")
|
||||
+ ChatColor.YELLOW + ".");
|
||||
if (state.navigationEnabled()
|
||||
&& !player.getWorld().getUID().equals(state.base().orElseThrow().worldId())) {
|
||||
player.sendMessage(ChatColor.RED + "Your base is in another world: "
|
||||
+ state.base().orElseThrow().worldName() + ".");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(
|
||||
CommandSender sender,
|
||||
Command command,
|
||||
String alias,
|
||||
String[] arguments
|
||||
) {
|
||||
if (arguments.length != 1) {
|
||||
return List.of();
|
||||
}
|
||||
String prefix = arguments[0].toLowerCase(Locale.ROOT);
|
||||
return MODES.stream().filter(mode -> mode.startsWith(prefix)).toList();
|
||||
}
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
package games.dmg.spigotbase;
|
||||
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
final class BaseProgressCommand implements CommandExecutor {
|
||||
private final BaseStateManager stateManager;
|
||||
private final PluginSettingsProvider settings;
|
||||
private final TeleportPolicy teleportPolicy;
|
||||
|
||||
BaseProgressCommand(BaseStateManager stateManager, PluginSettingsProvider settings) {
|
||||
this.stateManager = stateManager;
|
||||
this.settings = settings;
|
||||
this.teleportPolicy = new TeleportPolicy(settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] arguments) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage("Only players have base progression.");
|
||||
return true;
|
||||
}
|
||||
PlayerState state = stateManager.player(player.getUniqueId(), player.getName());
|
||||
if (arguments.length == 1 && arguments[0].equalsIgnoreCase("bossbar")) {
|
||||
state = stateManager.update(
|
||||
player.getUniqueId(),
|
||||
player.getName(),
|
||||
current -> current.withBossBarEnabled(!current.bossBarEnabled())
|
||||
);
|
||||
stateManager.saveIfDirty();
|
||||
player.sendMessage(ChatColor.YELLOW + "Automatic progress boss bars are now "
|
||||
+ (state.bossBarEnabled() ? ChatColor.GREEN + "on" : ChatColor.RED + "off")
|
||||
+ ChatColor.YELLOW + ".");
|
||||
return true;
|
||||
}
|
||||
player.sendMessage(ChatColor.GOLD + "=== Base Progress ===");
|
||||
showBasePath(player, state);
|
||||
showSizePath(player, state);
|
||||
showFlightPath(player, state);
|
||||
showWarmupPath(player, state);
|
||||
showCooldownPath(player, state);
|
||||
player.sendMessage(ChatColor.GRAY + "Boss bars: " + (state.bossBarEnabled() ? "on" : "off"));
|
||||
state.base().ifPresentOrElse(
|
||||
base -> player.sendMessage(ChatColor.GRAY + "Base: " + base.worldName() + " "
|
||||
+ base.x() + ", " + base.y() + ", " + base.z()),
|
||||
() -> player.sendMessage(ChatColor.GRAY + "Base: not set")
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void showBasePath(Player player, PlayerState state) {
|
||||
String detail = switch (state.baseLevel()) {
|
||||
case 0 -> state.grassAndDirtBroken() + "/" + settings.current().baseUnlockBlocks()
|
||||
+ " grass or dirt → /setbase";
|
||||
case 1 -> state.grassAndDirtBroken() + "/" + settings.current().navigationUnlockBlocks()
|
||||
+ " grass or dirt → navigation";
|
||||
case 2 -> state.blocksPlacedInBase() + "/" + settings.current().teleportUnlockPlacements()
|
||||
+ " placements → /base";
|
||||
case 3 -> settings.current().visitorUnlockDiamondCost() + " diamonds → visitor access";
|
||||
case 4 -> "complete; visitor access unlocked";
|
||||
default -> "invalid";
|
||||
};
|
||||
player.sendMessage(ChatColor.YELLOW + "Base " + state.baseLevel() + "/4: "
|
||||
+ ChatColor.GRAY + detail);
|
||||
}
|
||||
|
||||
private void showSizePath(Player player, PlayerState state) {
|
||||
String detail = switch (state.sizeLevel()) {
|
||||
case 0 -> state.stoneBroken() + "/" + settings.current().stoneExpansionBlocks() + " stone";
|
||||
case 1 -> state.deepslateBroken() + "/" + settings.current().deepslateExpansionBlocks() + " deepslate";
|
||||
case 2 -> state.obsidianBroken() + "/" + settings.current().obsidianExpansionBlocks() + " obsidian";
|
||||
case 3 -> "complete; 150-block radius by default";
|
||||
default -> "invalid";
|
||||
};
|
||||
player.sendMessage(colorForPrerequisite(state.baseLevel() >= 1) + "Base Size "
|
||||
+ state.sizeLevel() + "/3: " + ChatColor.GRAY + detail);
|
||||
}
|
||||
|
||||
private void showFlightPath(Player player, PlayerState state) {
|
||||
String detail = state.flightLevel() >= 3
|
||||
? "complete; world build height"
|
||||
: (state.flightLevel() + 1) + " simultaneous elytra required";
|
||||
player.sendMessage(colorForPrerequisite(state.baseLevel() >= 1) + "Base Flight "
|
||||
+ state.flightLevel() + "/3: " + ChatColor.GRAY + detail
|
||||
+ "; toggle=" + (state.flightEnabled() ? "on" : "off"));
|
||||
}
|
||||
|
||||
private void showWarmupPath(Player player, PlayerState state) {
|
||||
String detail = switch (state.warmupLevel()) {
|
||||
case 0 -> state.blocksPlacedInBase() + "/" + settings.current().secondWarmupPlacements();
|
||||
case 1 -> state.blocksPlacedInBase() + "/" + settings.current().thirdWarmupPlacements();
|
||||
case 2 -> state.blocksPlacedInBase() + "/" + settings.current().instantWarmupPlacements();
|
||||
case 3 -> "complete";
|
||||
default -> "invalid";
|
||||
};
|
||||
player.sendMessage(colorForPrerequisite(state.baseLevel() >= 3) + "Teleport Warm-up "
|
||||
+ state.warmupLevel() + "/3: " + ChatColor.GRAY
|
||||
+ DurationFormatter.friendly(teleportPolicy.warmup(state)) + "; " + detail);
|
||||
}
|
||||
|
||||
private void showCooldownPath(Player player, PlayerState state) {
|
||||
String detail = switch (state.cooldownLevel()) {
|
||||
case 0 -> state.blocksBrokenInBase() + "/" + settings.current().firstCooldownBreaks();
|
||||
case 1 -> state.blocksBrokenInBase() + "/" + settings.current().secondCooldownBreaks();
|
||||
case 2 -> state.blocksBrokenInBase() + "/" + settings.current().thirdCooldownBreaks();
|
||||
case 3 -> state.blocksBrokenInBase() + "/" + settings.current().instantCooldownBreaks();
|
||||
case 4 -> "complete";
|
||||
default -> "invalid";
|
||||
};
|
||||
player.sendMessage(colorForPrerequisite(state.baseLevel() >= 3) + "Teleport Cooldown "
|
||||
+ state.cooldownLevel() + "/4: " + ChatColor.GRAY
|
||||
+ DurationFormatter.friendly(teleportPolicy.cooldown(state)) + "; " + detail);
|
||||
}
|
||||
|
||||
private static ChatColor colorForPrerequisite(boolean met) {
|
||||
return met ? ChatColor.YELLOW : ChatColor.RED;
|
||||
}
|
||||
}
|
||||
@@ -149,7 +149,7 @@ final class BaseProgressListener implements Listener {
|
||||
int level = update.player().baseLevel();
|
||||
String subtitle = switch (level) {
|
||||
case 1 -> "/setbase is now available";
|
||||
case 2 -> "/basenavigation is now available";
|
||||
case 2 -> "/basesettings navigation enable is now available";
|
||||
case 3 -> "/base is now available";
|
||||
case 4 -> "Visitors can now travel to your base";
|
||||
default -> "A new base benefit is available";
|
||||
|
||||
@@ -0,0 +1,377 @@
|
||||
package games.dmg.spigotbase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabCompleter;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.PlayerInventory;
|
||||
|
||||
final class BaseSettingsCommand implements CommandExecutor, TabCompleter {
|
||||
private static final List<String> SETTINGS = List.of(
|
||||
"status", "upgrade", "visitors", "navigation", "flight", "bossbar"
|
||||
);
|
||||
private static final List<String> VISITOR_MODES = List.of("allowed", "blocked");
|
||||
private static final List<String> ENABLE_MODES = List.of("enable", "disable");
|
||||
private final BaseStateManager stateManager;
|
||||
private final PluginSettingsProvider settings;
|
||||
private final TeleportPolicy teleportPolicy;
|
||||
private final BaseFlightController flightController;
|
||||
|
||||
BaseSettingsCommand(
|
||||
BaseStateManager stateManager,
|
||||
PluginSettingsProvider settings,
|
||||
BaseFlightController flightController
|
||||
) {
|
||||
this.stateManager = stateManager;
|
||||
this.settings = settings;
|
||||
this.teleportPolicy = new TeleportPolicy(settings);
|
||||
this.flightController = flightController;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] arguments) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage("Only players have base progression.");
|
||||
return true;
|
||||
}
|
||||
PlayerState state = stateManager.player(player.getUniqueId(), player.getName());
|
||||
if (arguments.length == 0
|
||||
|| arguments.length == 1 && arguments[0].equalsIgnoreCase("status")) {
|
||||
showStatus(player, state);
|
||||
return true;
|
||||
}
|
||||
if (arguments.length == 1 && arguments[0].equalsIgnoreCase("upgrade")) {
|
||||
return purchaseVisitorAccess(player, state);
|
||||
}
|
||||
if (arguments.length == 2 && arguments[0].equalsIgnoreCase("visitors")) {
|
||||
return updateVisitors(player, state, arguments[1]);
|
||||
}
|
||||
if (arguments.length == 2 && arguments[0].equalsIgnoreCase("navigation")) {
|
||||
return updateNavigation(player, state, arguments[1]);
|
||||
}
|
||||
if (arguments.length == 2 && arguments[0].equalsIgnoreCase("flight")) {
|
||||
return updateFlight(player, state, arguments[1]);
|
||||
}
|
||||
if (arguments.length == 2 && arguments[0].equalsIgnoreCase("bossbar")) {
|
||||
return updateBossBar(player, arguments[1]);
|
||||
}
|
||||
sendUsage(player);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean purchaseVisitorAccess(Player player, PlayerState state) {
|
||||
if (!new VisitorPolicy().canPurchase(state)) {
|
||||
player.sendMessage(ChatColor.RED + (state.baseLevel() >= 4
|
||||
? "Base IV is already unlocked."
|
||||
: "You must unlock Base III before purchasing Base IV."));
|
||||
return true;
|
||||
}
|
||||
int price = settings.current().visitorUnlockDiamondCost();
|
||||
Material currency = Material.valueOf(settings.current().visitorCurrencyMaterial());
|
||||
PlayerInventory inventory = player.getInventory();
|
||||
if (countCurrency(inventory, currency) < price) {
|
||||
player.sendMessage(ChatColor.RED + "Base IV costs " + price + " "
|
||||
+ currency.name().toLowerCase(java.util.Locale.ROOT) + ".");
|
||||
return true;
|
||||
}
|
||||
ItemStack[] snapshot = cloneContents(inventory.getStorageContents());
|
||||
removeCurrency(inventory, currency, price);
|
||||
try {
|
||||
stateManager.updateAndSave(
|
||||
player.getUniqueId(),
|
||||
player.getName(),
|
||||
current -> current.withBaseLevel(4).withVisitorsEnabled(true)
|
||||
);
|
||||
} catch (IOException | RuntimeException exception) {
|
||||
inventory.setStorageContents(snapshot);
|
||||
player.sendMessage(ChatColor.RED
|
||||
+ "The upgrade could not be saved; your diamonds were restored.");
|
||||
return true;
|
||||
}
|
||||
player.sendTitle(
|
||||
ChatColor.GOLD + "Base IV Unlocked",
|
||||
ChatColor.YELLOW + "Visitors may now teleport to your base",
|
||||
settings.current().titleFadeInTicks(),
|
||||
settings.current().titleStayTicks(),
|
||||
settings.current().titleFadeOutTicks()
|
||||
);
|
||||
player.sendMessage(ChatColor.GREEN + "Base IV unlocked for " + price + " "
|
||||
+ currency.name().toLowerCase(java.util.Locale.ROOT) + ".");
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean updateVisitors(Player player, PlayerState state, String mode) {
|
||||
if (state.baseLevel() < 4) {
|
||||
player.sendMessage(ChatColor.RED + "Base IV visitor access is still locked.");
|
||||
return true;
|
||||
}
|
||||
final boolean enabled;
|
||||
if (mode.equalsIgnoreCase("allowed")) {
|
||||
enabled = true;
|
||||
} else if (mode.equalsIgnoreCase("blocked")) {
|
||||
enabled = false;
|
||||
} else {
|
||||
sendUsage(player);
|
||||
return true;
|
||||
}
|
||||
state = stateManager.update(
|
||||
player.getUniqueId(),
|
||||
player.getName(),
|
||||
current -> current.withVisitorsEnabled(enabled)
|
||||
);
|
||||
stateManager.saveIfDirty();
|
||||
player.sendMessage(ChatColor.YELLOW + "Visitor teleports are now "
|
||||
+ (state.visitorsEnabled() ? ChatColor.GREEN + "allowed" : ChatColor.RED + "blocked")
|
||||
+ ChatColor.YELLOW + ".");
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean updateNavigation(Player player, PlayerState state, String mode) {
|
||||
if (state.baseLevel() < 2) {
|
||||
player.sendMessage(ChatColor.RED + "Base II navigation is still locked.");
|
||||
return true;
|
||||
}
|
||||
if (state.base().isEmpty()) {
|
||||
player.sendMessage(ChatColor.RED + "Set your base before enabling navigation.");
|
||||
return true;
|
||||
}
|
||||
Boolean enabled = enabledMode(mode);
|
||||
if (enabled == null) {
|
||||
sendUsage(player);
|
||||
return true;
|
||||
}
|
||||
state = stateManager.update(
|
||||
player.getUniqueId(),
|
||||
player.getName(),
|
||||
current -> current.withNavigationEnabled(enabled)
|
||||
);
|
||||
stateManager.saveIfDirty();
|
||||
player.sendMessage(ChatColor.YELLOW + "Base navigation is now "
|
||||
+ (state.navigationEnabled() ? ChatColor.GREEN + "enabled" : ChatColor.RED + "disabled")
|
||||
+ ChatColor.YELLOW + ".");
|
||||
if (state.navigationEnabled()
|
||||
&& !player.getWorld().getUID().equals(state.base().orElseThrow().worldId())) {
|
||||
player.sendMessage(ChatColor.RED + "Your base is in another world: "
|
||||
+ state.base().orElseThrow().worldName() + ".");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean updateFlight(Player player, PlayerState state, String mode) {
|
||||
if (state.flightLevel() < 1) {
|
||||
player.sendMessage(ChatColor.RED + "Base flight is still locked.");
|
||||
return true;
|
||||
}
|
||||
Boolean enabled = enabledMode(mode);
|
||||
if (enabled == null) {
|
||||
sendUsage(player);
|
||||
return true;
|
||||
}
|
||||
state = stateManager.update(
|
||||
player.getUniqueId(),
|
||||
player.getName(),
|
||||
current -> current.withFlightEnabled(enabled)
|
||||
);
|
||||
if (!state.flightEnabled()) {
|
||||
flightController.removeGrantedFlight(player);
|
||||
}
|
||||
stateManager.saveIfDirty();
|
||||
player.sendMessage(ChatColor.YELLOW + "Base flight is now "
|
||||
+ (state.flightEnabled() ? ChatColor.GREEN + "enabled" : ChatColor.RED + "disabled")
|
||||
+ ChatColor.YELLOW + ".");
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean updateBossBar(Player player, String mode) {
|
||||
Boolean enabled = enabledMode(mode);
|
||||
if (enabled == null) {
|
||||
sendUsage(player);
|
||||
return true;
|
||||
}
|
||||
PlayerState state = stateManager.update(
|
||||
player.getUniqueId(),
|
||||
player.getName(),
|
||||
current -> current.withBossBarEnabled(enabled)
|
||||
);
|
||||
stateManager.saveIfDirty();
|
||||
player.sendMessage(ChatColor.YELLOW + "Automatic progress boss bars are now "
|
||||
+ (state.bossBarEnabled() ? ChatColor.GREEN + "enabled" : ChatColor.RED + "disabled")
|
||||
+ ChatColor.YELLOW + ".");
|
||||
return true;
|
||||
}
|
||||
|
||||
private void showStatus(Player player, PlayerState state) {
|
||||
player.sendMessage(ChatColor.GOLD + "=== Base Progress ===");
|
||||
showBasePath(player, state);
|
||||
showSizePath(player, state);
|
||||
showFlightPath(player, state);
|
||||
showWarmupPath(player, state);
|
||||
showCooldownPath(player, state);
|
||||
player.sendMessage(ChatColor.GRAY + "Settings: visitors="
|
||||
+ (state.visitorsEnabled() ? "allowed" : "blocked")
|
||||
+ " navigation=" + (state.navigationEnabled() ? "enabled" : "disabled")
|
||||
+ " flight=" + (state.flightEnabled() ? "enabled" : "disabled")
|
||||
+ " bossbar=" + (state.bossBarEnabled() ? "enabled" : "disabled"));
|
||||
state.base().ifPresentOrElse(
|
||||
base -> player.sendMessage(ChatColor.GRAY + "Base: " + base.worldName() + " "
|
||||
+ base.x() + ", " + base.y() + ", " + base.z()),
|
||||
() -> player.sendMessage(ChatColor.GRAY + "Base: not set")
|
||||
);
|
||||
}
|
||||
|
||||
private void showBasePath(Player player, PlayerState state) {
|
||||
String detail = switch (state.baseLevel()) {
|
||||
case 0 -> state.grassAndDirtBroken() + "/" + settings.current().baseUnlockBlocks()
|
||||
+ " grass or dirt → /setbase";
|
||||
case 1 -> state.grassAndDirtBroken() + "/" + settings.current().navigationUnlockBlocks()
|
||||
+ " grass or dirt → navigation";
|
||||
case 2 -> state.blocksPlacedInBase() + "/" + settings.current().teleportUnlockPlacements()
|
||||
+ " placements → /base";
|
||||
case 3 -> settings.current().visitorUnlockDiamondCost()
|
||||
+ " diamonds → /basesettings upgrade";
|
||||
case 4 -> "complete; visitor access unlocked";
|
||||
default -> "invalid";
|
||||
};
|
||||
player.sendMessage(ChatColor.YELLOW + "Base " + state.baseLevel() + "/4: "
|
||||
+ ChatColor.GRAY + detail);
|
||||
}
|
||||
|
||||
private void showSizePath(Player player, PlayerState state) {
|
||||
String detail = switch (state.sizeLevel()) {
|
||||
case 0 -> state.stoneBroken() + "/" + settings.current().stoneExpansionBlocks() + " stone";
|
||||
case 1 -> state.deepslateBroken() + "/" + settings.current().deepslateExpansionBlocks() + " deepslate";
|
||||
case 2 -> state.obsidianBroken() + "/" + settings.current().obsidianExpansionBlocks() + " obsidian";
|
||||
case 3 -> "complete; 150-block radius by default";
|
||||
default -> "invalid";
|
||||
};
|
||||
player.sendMessage(colorForPrerequisite(state.baseLevel() >= 1) + "Base Size "
|
||||
+ state.sizeLevel() + "/3: " + ChatColor.GRAY + detail);
|
||||
}
|
||||
|
||||
private void showFlightPath(Player player, PlayerState state) {
|
||||
String detail = state.flightLevel() >= 3
|
||||
? "complete; world build height"
|
||||
: (state.flightLevel() + 1) + " simultaneous elytra required";
|
||||
player.sendMessage(colorForPrerequisite(state.baseLevel() >= 1) + "Base Flight "
|
||||
+ state.flightLevel() + "/3: " + ChatColor.GRAY + detail
|
||||
+ "; setting=" + (state.flightEnabled() ? "enabled" : "disabled"));
|
||||
}
|
||||
|
||||
private void showWarmupPath(Player player, PlayerState state) {
|
||||
String detail = switch (state.warmupLevel()) {
|
||||
case 0 -> state.blocksPlacedInBase() + "/" + settings.current().secondWarmupPlacements();
|
||||
case 1 -> state.blocksPlacedInBase() + "/" + settings.current().thirdWarmupPlacements();
|
||||
case 2 -> state.blocksPlacedInBase() + "/" + settings.current().instantWarmupPlacements();
|
||||
case 3 -> "complete";
|
||||
default -> "invalid";
|
||||
};
|
||||
player.sendMessage(colorForPrerequisite(state.baseLevel() >= 3) + "Teleport Warm-up "
|
||||
+ state.warmupLevel() + "/3: " + ChatColor.GRAY
|
||||
+ DurationFormatter.friendly(teleportPolicy.warmup(state)) + "; " + detail);
|
||||
}
|
||||
|
||||
private void showCooldownPath(Player player, PlayerState state) {
|
||||
String detail = switch (state.cooldownLevel()) {
|
||||
case 0 -> state.blocksBrokenInBase() + "/" + settings.current().firstCooldownBreaks();
|
||||
case 1 -> state.blocksBrokenInBase() + "/" + settings.current().secondCooldownBreaks();
|
||||
case 2 -> state.blocksBrokenInBase() + "/" + settings.current().thirdCooldownBreaks();
|
||||
case 3 -> state.blocksBrokenInBase() + "/" + settings.current().instantCooldownBreaks();
|
||||
case 4 -> "complete";
|
||||
default -> "invalid";
|
||||
};
|
||||
player.sendMessage(colorForPrerequisite(state.baseLevel() >= 3) + "Teleport Cooldown "
|
||||
+ state.cooldownLevel() + "/4: " + ChatColor.GRAY
|
||||
+ DurationFormatter.friendly(teleportPolicy.cooldown(state)) + "; " + detail);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(
|
||||
CommandSender sender,
|
||||
Command command,
|
||||
String alias,
|
||||
String[] arguments
|
||||
) {
|
||||
if (arguments.length == 1) {
|
||||
String prefix = arguments[0].toLowerCase(Locale.ROOT);
|
||||
return SETTINGS.stream().filter(setting -> setting.startsWith(prefix)).toList();
|
||||
}
|
||||
if (arguments.length == 2) {
|
||||
List<String> modes = arguments[0].equalsIgnoreCase("visitors")
|
||||
? VISITOR_MODES
|
||||
: switch (arguments[0].toLowerCase(Locale.ROOT)) {
|
||||
case "navigation", "flight", "bossbar" -> ENABLE_MODES;
|
||||
default -> List.of();
|
||||
};
|
||||
String prefix = arguments[1].toLowerCase(Locale.ROOT);
|
||||
return modes.stream().filter(mode -> mode.startsWith(prefix)).toList();
|
||||
}
|
||||
return List.of();
|
||||
}
|
||||
|
||||
private static int countCurrency(PlayerInventory inventory, Material currency) {
|
||||
int count = 0;
|
||||
for (ItemStack item : inventory.getStorageContents()) {
|
||||
if (item != null && item.getType() == currency) {
|
||||
count += item.getAmount();
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private static void removeCurrency(PlayerInventory inventory, Material currency, int amount) {
|
||||
ItemStack[] contents = inventory.getStorageContents();
|
||||
int remaining = amount;
|
||||
for (int index = 0; index < contents.length && remaining > 0; index++) {
|
||||
ItemStack item = contents[index];
|
||||
if (item == null || item.getType() != currency) {
|
||||
continue;
|
||||
}
|
||||
int removed = Math.min(remaining, item.getAmount());
|
||||
remaining -= removed;
|
||||
int newAmount = item.getAmount() - removed;
|
||||
if (newAmount == 0) {
|
||||
contents[index] = null;
|
||||
} else {
|
||||
ItemStack reduced = item.clone();
|
||||
reduced.setAmount(newAmount);
|
||||
contents[index] = reduced;
|
||||
}
|
||||
}
|
||||
inventory.setStorageContents(contents);
|
||||
}
|
||||
|
||||
private static ItemStack[] cloneContents(ItemStack[] contents) {
|
||||
ItemStack[] copy = new ItemStack[contents.length];
|
||||
for (int index = 0; index < contents.length; index++) {
|
||||
copy[index] = contents[index] == null ? null : contents[index].clone();
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
private static Boolean enabledMode(String mode) {
|
||||
if (mode.equalsIgnoreCase("enable")) {
|
||||
return true;
|
||||
}
|
||||
if (mode.equalsIgnoreCase("disable")) {
|
||||
return false;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static ChatColor colorForPrerequisite(boolean met) {
|
||||
return met ? ChatColor.YELLOW : ChatColor.RED;
|
||||
}
|
||||
|
||||
private static void sendUsage(Player player) {
|
||||
player.sendMessage(ChatColor.RED + "Usage: /basesettings "
|
||||
+ "[status|upgrade|visitors <allowed|blocked>|navigation <enable|disable>"
|
||||
+ "|flight <enable|disable>|bossbar <enable|disable>]");
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
package games.dmg.spigotbase;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabCompleter;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
final class BaseVisitorsCommand implements CommandExecutor, TabCompleter {
|
||||
private static final List<String> MODES = List.of("on", "off");
|
||||
private final BaseStateManager stateManager;
|
||||
|
||||
BaseVisitorsCommand(BaseStateManager stateManager) {
|
||||
this.stateManager = stateManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] arguments) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage("Only players can manage base visitors.");
|
||||
return true;
|
||||
}
|
||||
PlayerState state = stateManager.player(player.getUniqueId(), player.getName());
|
||||
if (state.baseLevel() < 4) {
|
||||
player.sendMessage(ChatColor.RED + "Base IV visitor access is still locked.");
|
||||
return true;
|
||||
}
|
||||
final boolean enabled;
|
||||
try {
|
||||
enabled = TogglePreference.resolve(state.visitorsEnabled(), arguments);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
player.sendMessage(ChatColor.RED + "Usage: /basevisitors [on|off]");
|
||||
return true;
|
||||
}
|
||||
state = stateManager.update(
|
||||
player.getUniqueId(),
|
||||
player.getName(),
|
||||
current -> current.withVisitorsEnabled(enabled)
|
||||
);
|
||||
stateManager.saveIfDirty();
|
||||
player.sendMessage(ChatColor.YELLOW + "Visitor teleports are now "
|
||||
+ (state.visitorsEnabled() ? ChatColor.GREEN + "on" : ChatColor.RED + "off")
|
||||
+ ChatColor.YELLOW + ".");
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(
|
||||
CommandSender sender,
|
||||
Command command,
|
||||
String alias,
|
||||
String[] arguments
|
||||
) {
|
||||
if (arguments.length != 1) {
|
||||
return List.of();
|
||||
}
|
||||
String prefix = arguments[0].toLowerCase(Locale.ROOT);
|
||||
return MODES.stream().filter(mode -> mode.startsWith(prefix)).toList();
|
||||
}
|
||||
}
|
||||
@@ -65,32 +65,25 @@ public final class SpigotBasePlugin extends JavaPlugin {
|
||||
getServer().getPluginManager().registerEvents(teleportManager, this);
|
||||
|
||||
command("setbase").setExecutor(new SetBaseCommand(stateManager, baseService, Clock.systemUTC()));
|
||||
command("base").setExecutor(
|
||||
new BaseCommand(teleportManager, stateManager, visitorPolicy, settingsProvider)
|
||||
command("base").setExecutor(new BaseCommand(teleportManager));
|
||||
BaseSettingsCommand settingsCommand = new BaseSettingsCommand(
|
||||
stateManager,
|
||||
settingsProvider,
|
||||
flightController
|
||||
);
|
||||
command("baseprogress").setExecutor(
|
||||
new BaseProgressCommand(stateManager, settingsProvider)
|
||||
);
|
||||
BaseNavigationCommand navigationCommand = new BaseNavigationCommand(stateManager);
|
||||
command("basenavigation").setExecutor(navigationCommand);
|
||||
command("basenavigation").setTabCompleter(navigationCommand);
|
||||
BaseFlightCommand flightCommand = new BaseFlightCommand(stateManager, flightController);
|
||||
command("baseflight").setExecutor(flightCommand);
|
||||
command("baseflight").setTabCompleter(flightCommand);
|
||||
BaseVisitorsCommand visitorsCommand = new BaseVisitorsCommand(stateManager);
|
||||
command("basevisitors").setExecutor(visitorsCommand);
|
||||
command("basevisitors").setTabCompleter(visitorsCommand);
|
||||
command("basesettings").setExecutor(settingsCommand);
|
||||
command("basesettings").setTabCompleter(settingsCommand);
|
||||
GoToBaseCommand goToBaseCommand = new GoToBaseCommand(stateManager, teleportManager);
|
||||
command("gotobase").setExecutor(goToBaseCommand);
|
||||
command("gotobase").setTabCompleter(goToBaseCommand);
|
||||
command("baseadmin").setExecutor(
|
||||
new BaseAdminCommand(
|
||||
this,
|
||||
stateManager,
|
||||
new AdminProgressionService(settingsProvider),
|
||||
settingsProvider
|
||||
)
|
||||
BaseAdminCommand adminCommand = new BaseAdminCommand(
|
||||
this,
|
||||
stateManager,
|
||||
new AdminProgressionService(settingsProvider),
|
||||
settingsProvider
|
||||
);
|
||||
command("baseadmin").setExecutor(adminCommand);
|
||||
command("baseadmin").setTabCompleter(adminCommand);
|
||||
|
||||
getServer().getScheduler().runTaskTimer(
|
||||
this,
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
package games.dmg.spigotbase;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
final class TogglePreference {
|
||||
private TogglePreference() {
|
||||
}
|
||||
|
||||
static boolean resolve(boolean current, String[] arguments) {
|
||||
if (arguments.length == 0) {
|
||||
return !current;
|
||||
}
|
||||
if (arguments.length != 1) {
|
||||
throw new IllegalArgumentException("expected zero or one argument");
|
||||
}
|
||||
return switch (arguments[0].toLowerCase(Locale.ROOT)) {
|
||||
case "on" -> true;
|
||||
case "off" -> false;
|
||||
default -> throw new IllegalArgumentException("expected on or off");
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -10,31 +10,21 @@ commands:
|
||||
usage: /setbase
|
||||
aliases: [sethome]
|
||||
base:
|
||||
description: Teleport to or upgrade your base.
|
||||
usage: /base [upgrade]
|
||||
description: Teleport to your base.
|
||||
usage: /base
|
||||
aliases: [home]
|
||||
basenavigation:
|
||||
description: Toggle particle navigation toward your base.
|
||||
usage: /basenavigation [on|off]
|
||||
aliases: [homenavigation]
|
||||
baseflight:
|
||||
description: Toggle flight within your base.
|
||||
usage: /baseflight [on|off]
|
||||
aliases: [homeflight]
|
||||
basevisitors:
|
||||
description: Toggle visitor access to your base.
|
||||
usage: /basevisitors [on|off]
|
||||
aliases: [homevisitors]
|
||||
gotobase:
|
||||
description: Visit an available player base.
|
||||
usage: /gotobase <player>
|
||||
aliases: [visit]
|
||||
baseprogress:
|
||||
description: View progression or toggle progress boss bars.
|
||||
usage: /baseprogress [bossbar]
|
||||
basesettings:
|
||||
description: View progression, upgrade, and manage base settings.
|
||||
usage: /basesettings [status|upgrade|visitors|navigation|flight|bossbar]
|
||||
aliases: [homesettings]
|
||||
baseadmin:
|
||||
description: Administer Spigot Base.
|
||||
usage: /baseadmin <subcommand>
|
||||
aliases: [homeadmin]
|
||||
permission: spigotbase.admin
|
||||
permissions:
|
||||
spigotbase.admin:
|
||||
|
||||
Reference in New Issue
Block a user