752 lines
32 KiB
Java
752 lines
32 KiB
Java
package games.dmg.spigotbase;
|
|
|
|
import io.papermc.paper.dialog.Dialog;
|
|
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(
|
|
"ui", "status", "upgrade", "pocket", "visitors", "navigation", "flight", "border",
|
|
"spawnable", "bossbar"
|
|
);
|
|
private static final List<String> VISITOR_MODES = List.of("allowed", "blocked");
|
|
private static final List<String> ENABLE_MODES = List.of("enable", "disable");
|
|
private static final List<String> POCKET_WORLD_TYPES = List.of(
|
|
"void", "nether", "overworld"
|
|
);
|
|
private final BaseStateManager stateManager;
|
|
private final PluginSettingsProvider settings;
|
|
private final TeleportPolicy teleportPolicy;
|
|
private final BaseFlightController flightController;
|
|
private final PocketBaseManager pocketBases;
|
|
private final DialogProvider dialogProvider;
|
|
|
|
BaseSettingsCommand(
|
|
BaseStateManager stateManager,
|
|
PluginSettingsProvider settings,
|
|
BaseFlightController flightController
|
|
) {
|
|
this(stateManager, settings, flightController, null);
|
|
}
|
|
|
|
BaseSettingsCommand(
|
|
BaseStateManager stateManager,
|
|
PluginSettingsProvider settings,
|
|
BaseFlightController flightController,
|
|
PocketBaseManager pocketBases
|
|
) {
|
|
this(
|
|
stateManager,
|
|
settings,
|
|
flightController,
|
|
pocketBases,
|
|
(owner, pocket) -> new BaseSettingsDialogFactory(settings.current())
|
|
.create(owner, pocket)
|
|
);
|
|
}
|
|
|
|
BaseSettingsCommand(
|
|
BaseStateManager stateManager,
|
|
PluginSettingsProvider settings,
|
|
BaseFlightController flightController,
|
|
PocketBaseManager pocketBases,
|
|
DialogProvider dialogProvider
|
|
) {
|
|
this.stateManager = stateManager;
|
|
this.settings = settings;
|
|
this.teleportPolicy = new TeleportPolicy(settings);
|
|
this.flightController = flightController;
|
|
this.pocketBases = pocketBases;
|
|
this.dialogProvider = dialogProvider;
|
|
}
|
|
|
|
@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("ui")) {
|
|
showDialog(player, state);
|
|
return true;
|
|
}
|
|
if (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("pocket")
|
|
&& arguments[1].equalsIgnoreCase("upgrade")) {
|
|
return purchasePocketUpgrade(player, state);
|
|
}
|
|
if (arguments.length == 3 && arguments[0].equalsIgnoreCase("pocket")
|
|
&& arguments[1].equalsIgnoreCase("flight")) {
|
|
return updatePocketFlight(player, arguments[2]);
|
|
}
|
|
if (arguments.length == 4 && arguments[0].equalsIgnoreCase("pocket")
|
|
&& arguments[1].equalsIgnoreCase("mobs")) {
|
|
return updatePocketMobSpawning(player, arguments[2], arguments[3]);
|
|
}
|
|
if (arguments.length == 4 && arguments[0].equalsIgnoreCase("pocket")
|
|
&& arguments[1].equalsIgnoreCase("type")) {
|
|
return purchasePocketBiome(player, arguments[2], arguments[3]);
|
|
}
|
|
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("border")) {
|
|
return updateBorder(player, state, arguments[1]);
|
|
}
|
|
if (arguments.length == 2 && arguments[0].equalsIgnoreCase("spawnable")) {
|
|
return updateSpawnableOverlay(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 purchasePocketUpgrade(Player player, PlayerState owner) {
|
|
if (pocketBases == null) {
|
|
player.sendMessage(ChatColor.RED + "Pocket Bases are currently unavailable.");
|
|
return true;
|
|
}
|
|
if (owner.baseLevel() < 4 || owner.base().isEmpty()) {
|
|
player.sendMessage(ChatColor.RED
|
|
+ "Establish Base IV before purchasing a Pocket Base.");
|
|
return true;
|
|
}
|
|
PocketBaseState current = pocketBases.state(owner.playerId());
|
|
int price = current.level() == 0
|
|
? settings.current().pocketBaseUnlockCost()
|
|
: settings.current().pocketBaseUpgradeCost();
|
|
Material currency = Material.valueOf(settings.current().pocketBaseCurrencyMaterial());
|
|
PlayerInventory inventory = player.getInventory();
|
|
if (countCurrency(inventory, currency) < price) {
|
|
player.sendMessage(ChatColor.RED + "Pocket Base "
|
|
+ (current.level() == 0 ? "I" : "level " + (current.level() + 1))
|
|
+ " costs " + price + " "
|
|
+ currency.name().toLowerCase(java.util.Locale.ROOT) + ".");
|
|
return true;
|
|
}
|
|
ItemStack[] snapshot = cloneContents(inventory.getStorageContents());
|
|
removeCurrency(inventory, currency, price);
|
|
final PocketBaseState upgraded;
|
|
try {
|
|
upgraded = pocketBases.upgrade(owner.playerId());
|
|
} catch (IOException | RuntimeException exception) {
|
|
inventory.setStorageContents(snapshot);
|
|
player.sendMessage(ChatColor.RED
|
|
+ "The Pocket Base upgrade failed; your payment was restored.");
|
|
return true;
|
|
}
|
|
player.sendTitle(
|
|
ChatColor.GOLD + "Pocket Base " + upgraded.level() + " Unlocked",
|
|
ChatColor.YELLOW + "Build a portal inside your base",
|
|
settings.current().titleFadeInTicks(),
|
|
settings.current().titleStayTicks(),
|
|
settings.current().titleFadeOutTicks()
|
|
);
|
|
player.sendMessage(ChatColor.GREEN + "Pocket Base level " + upgraded.level()
|
|
+ " unlocked for " + price + " "
|
|
+ currency.name().toLowerCase(java.util.Locale.ROOT) + ".");
|
|
return true;
|
|
}
|
|
|
|
private boolean purchasePocketBiome(
|
|
Player player,
|
|
String worldTypeName,
|
|
String biomeName
|
|
) {
|
|
if (pocketBases == null) {
|
|
player.sendMessage(ChatColor.RED + "Pocket Bases are currently unavailable.");
|
|
return true;
|
|
}
|
|
PocketBaseState current = pocketBases.state(player.getUniqueId());
|
|
if (current.level() < 1) {
|
|
player.sendMessage(ChatColor.RED + "Pocket Base I is still locked.");
|
|
return true;
|
|
}
|
|
PocketBaseBiome biome = PocketBaseWorldType.fromCommand(worldTypeName)
|
|
.flatMap(worldType -> PocketBaseBiome.fromCommand(worldType, biomeName))
|
|
.orElse(null);
|
|
if (biome == null) {
|
|
player.sendMessage(ChatColor.RED + "That Pocket Base type and subtype are invalid.");
|
|
return true;
|
|
}
|
|
if (biome == current.biome()) {
|
|
player.sendMessage(ChatColor.RED + "Your Pocket Base already uses "
|
|
+ biome.commandName() + ".");
|
|
return true;
|
|
}
|
|
int price = settings.current().pocketBaseBiomeChangeCost();
|
|
Material currency = Material.valueOf(
|
|
settings.current().pocketBaseBiomeCurrencyMaterial()
|
|
);
|
|
PlayerInventory inventory = player.getInventory();
|
|
if (countCurrency(inventory, currency) < price) {
|
|
player.sendMessage(ChatColor.RED + "Changing Pocket Base biome costs "
|
|
+ price + " " + currency.name().toLowerCase(Locale.ROOT) + ".");
|
|
return true;
|
|
}
|
|
ItemStack[] snapshot = cloneContents(inventory.getStorageContents());
|
|
removeCurrency(inventory, currency, price);
|
|
final PocketBaseState updated;
|
|
try {
|
|
updated = pocketBases.setBiome(player.getUniqueId(), biome);
|
|
} catch (IOException | RuntimeException exception) {
|
|
inventory.setStorageContents(snapshot);
|
|
player.sendMessage(ChatColor.RED
|
|
+ "The Pocket Base biome change failed; your payment was restored.");
|
|
return true;
|
|
}
|
|
player.sendMessage(ChatColor.GREEN + "Pocket Base biome changed to "
|
|
+ updated.biome().commandName() + " for " + price + " "
|
|
+ currency.name().toLowerCase(Locale.ROOT) + ".");
|
|
return true;
|
|
}
|
|
|
|
private boolean updatePocketFlight(Player player, String mode) {
|
|
if (pocketBases == null) {
|
|
player.sendMessage(ChatColor.RED + "Pocket Bases are currently unavailable.");
|
|
return true;
|
|
}
|
|
PocketBaseState current = pocketBases.state(player.getUniqueId());
|
|
if (!current.flightUnlocked()) {
|
|
player.sendMessage(ChatColor.RED
|
|
+ "Win a raid in your Pocket Base before enabling automatic flight.");
|
|
return true;
|
|
}
|
|
Boolean enabled = enabledMode(mode);
|
|
if (enabled == null) {
|
|
sendUsage(player);
|
|
return true;
|
|
}
|
|
try {
|
|
current = pocketBases.setFlightEnabled(player.getUniqueId(), enabled);
|
|
} catch (IOException | RuntimeException exception) {
|
|
player.sendMessage(ChatColor.RED
|
|
+ "The Pocket Base flight setting could not be changed.");
|
|
return true;
|
|
}
|
|
player.sendMessage(ChatColor.YELLOW + "Pocket Base automatic flight is now "
|
|
+ (current.flightEnabled() ? ChatColor.GREEN + "enabled" : ChatColor.RED + "disabled")
|
|
+ ChatColor.YELLOW + ".");
|
|
return true;
|
|
}
|
|
|
|
private boolean updatePocketMobSpawning(
|
|
Player player,
|
|
String category,
|
|
String mode
|
|
) {
|
|
if (pocketBases == null) {
|
|
player.sendMessage(ChatColor.RED + "Pocket Bases are currently unavailable.");
|
|
return true;
|
|
}
|
|
PocketBaseState current = pocketBases.state(player.getUniqueId());
|
|
if (current.level() < 1) {
|
|
player.sendMessage(ChatColor.RED + "Pocket Base I is still locked.");
|
|
return true;
|
|
}
|
|
Boolean enabled = enabledMode(mode);
|
|
if (enabled == null) {
|
|
sendUsage(player);
|
|
return true;
|
|
}
|
|
final PocketBaseState updated;
|
|
final boolean settingEnabled;
|
|
try {
|
|
if (category.equalsIgnoreCase("hostile")) {
|
|
updated = pocketBases.setHostileMobSpawning(
|
|
player.getUniqueId(), enabled
|
|
);
|
|
settingEnabled = updated.hostileMobSpawningEnabled();
|
|
} else if (category.equalsIgnoreCase("passive")) {
|
|
updated = pocketBases.setPassiveMobSpawning(
|
|
player.getUniqueId(), enabled
|
|
);
|
|
settingEnabled = updated.passiveMobSpawningEnabled();
|
|
} else {
|
|
sendUsage(player);
|
|
return true;
|
|
}
|
|
} catch (IOException | RuntimeException exception) {
|
|
player.sendMessage(ChatColor.RED
|
|
+ "The Pocket Base mob-spawning setting could not be changed.");
|
|
return true;
|
|
}
|
|
player.sendMessage(ChatColor.YELLOW + "Pocket Base "
|
|
+ category.toLowerCase(Locale.ROOT) + " mob spawning is now "
|
|
+ (settingEnabled ? ChatColor.GREEN + "enabled" : ChatColor.RED + "disabled")
|
|
+ ChatColor.YELLOW + ".");
|
|
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 updateBorder(Player player, PlayerState state, String mode) {
|
|
if (state.baseLevel() < 1 || state.base().isEmpty()) {
|
|
player.sendMessage(ChatColor.RED
|
|
+ "Establish Base I before enabling border visualization.");
|
|
return true;
|
|
}
|
|
Boolean enabled = enabledMode(mode);
|
|
if (enabled == null) {
|
|
sendUsage(player);
|
|
return true;
|
|
}
|
|
state = stateManager.update(
|
|
player.getUniqueId(),
|
|
player.getName(),
|
|
current -> current.withBorderEnabled(enabled)
|
|
);
|
|
stateManager.saveIfDirty();
|
|
player.sendMessage(ChatColor.YELLOW + "Base border visualization is now "
|
|
+ (state.borderEnabled() ? ChatColor.GREEN + "enabled" : ChatColor.RED + "disabled")
|
|
+ ChatColor.YELLOW + ".");
|
|
return true;
|
|
}
|
|
|
|
private boolean updateSpawnableOverlay(Player player, PlayerState state, String mode) {
|
|
if (state.totalBlocksPlaced() < settings.current().spawnableOverlayUnlockPlacements()) {
|
|
player.sendMessage(ChatColor.RED + "Spawnable highlighting unlocks after "
|
|
+ settings.current().spawnableOverlayUnlockPlacements() + " total placements.");
|
|
return true;
|
|
}
|
|
if (state.baseLevel() < 1 || state.base().isEmpty()) {
|
|
player.sendMessage(ChatColor.RED
|
|
+ "Establish Base I before enabling spawnable highlighting.");
|
|
return true;
|
|
}
|
|
Boolean enabled = enabledMode(mode);
|
|
if (enabled == null) {
|
|
sendUsage(player);
|
|
return true;
|
|
}
|
|
state = stateManager.update(
|
|
player.getUniqueId(),
|
|
player.getName(),
|
|
current -> current.withSpawnableOverlayEnabled(enabled)
|
|
);
|
|
stateManager.saveIfDirty();
|
|
player.sendMessage(ChatColor.YELLOW + "Spawnable block highlighting is now "
|
|
+ (state.spawnableOverlayEnabled()
|
|
? 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 showDialog(Player player, PlayerState state) {
|
|
PocketBaseState pocket = pocketBases == null
|
|
? PocketBaseState.locked(state.playerId())
|
|
: pocketBases.state(state.playerId());
|
|
player.showDialog(dialogProvider.create(state, pocket));
|
|
}
|
|
|
|
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);
|
|
showPocketPath(player, state);
|
|
int spawnableThreshold = settings.current().spawnableOverlayUnlockPlacements();
|
|
player.sendMessage(ChatColor.YELLOW + "Spawnable Overlay: " + ChatColor.GRAY
|
|
+ state.totalBlocksPlaced() + "/" + spawnableThreshold + " placements; "
|
|
+ (state.totalBlocksPlaced() >= spawnableThreshold ? "unlocked" : "locked")
|
|
+ "; setting=" + (state.spawnableOverlayEnabled() ? "enabled" : "disabled"));
|
|
player.sendMessage(ChatColor.GRAY + "Settings: visitors="
|
|
+ (state.visitorsEnabled() ? "allowed" : "blocked")
|
|
+ " navigation=" + (state.navigationEnabled() ? "enabled" : "disabled")
|
|
+ " flight=" + (state.flightEnabled() ? "enabled" : "disabled")
|
|
+ " border=" + (state.borderEnabled() ? "enabled" : "disabled")
|
|
+ " spawnable=" + (state.spawnableOverlayEnabled() ? "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 showPocketPath(Player player, PlayerState owner) {
|
|
PocketBaseState pocket = pocketBases == null
|
|
? PocketBaseState.locked(owner.playerId())
|
|
: pocketBases.state(owner.playerId());
|
|
int price = pocket.level() == 0
|
|
? settings.current().pocketBaseUnlockCost()
|
|
: settings.current().pocketBaseUpgradeCost();
|
|
player.sendMessage(colorForPrerequisite(owner.baseLevel() >= 4 && owner.base().isPresent())
|
|
+ "Pocket Base " + pocket.level() + ": " + ChatColor.GRAY + price + " "
|
|
+ settings.current().pocketBaseCurrencyMaterial().toLowerCase(Locale.ROOT)
|
|
+ " → /basesettings pocket upgrade; type="
|
|
+ pocket.biome().worldType().commandName() + "/" + pocket.biome().commandName()
|
|
+ "; hostile spawning="
|
|
+ (pocket.hostileMobSpawningEnabled() ? "enabled" : "disabled")
|
|
+ "; passive spawning="
|
|
+ (pocket.passiveMobSpawningEnabled() ? "enabled" : "disabled")
|
|
+ "; flight="
|
|
+ (pocket.flightUnlocked()
|
|
? "automatic " + (pocket.flightEnabled() ? "enabled" : "disabled")
|
|
: "complete a raid to unlock"));
|
|
}
|
|
|
|
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 "pocket" -> List.of("upgrade", "flight", "mobs", "type");
|
|
case "navigation", "flight", "border", "spawnable", "bossbar" -> ENABLE_MODES;
|
|
default -> List.of();
|
|
};
|
|
String prefix = arguments[1].toLowerCase(Locale.ROOT);
|
|
return modes.stream().filter(mode -> mode.startsWith(prefix)).toList();
|
|
}
|
|
if (arguments.length == 3 && arguments[0].equalsIgnoreCase("pocket")) {
|
|
String prefix = arguments[2].toLowerCase(Locale.ROOT);
|
|
if (arguments[1].equalsIgnoreCase("flight")) {
|
|
return ENABLE_MODES.stream()
|
|
.filter(mode -> mode.startsWith(prefix))
|
|
.toList();
|
|
}
|
|
if (arguments[1].equalsIgnoreCase("mobs")) {
|
|
return List.of("hostile", "passive").stream()
|
|
.filter(category -> category.startsWith(prefix))
|
|
.toList();
|
|
}
|
|
if (arguments[1].equalsIgnoreCase("type")) {
|
|
return POCKET_WORLD_TYPES.stream()
|
|
.filter(type -> type.startsWith(prefix))
|
|
.toList();
|
|
}
|
|
}
|
|
if (arguments.length == 4 && arguments[0].equalsIgnoreCase("pocket")) {
|
|
String prefix = arguments[3].toLowerCase(Locale.ROOT);
|
|
if (arguments[1].equalsIgnoreCase("mobs")
|
|
&& (arguments[2].equalsIgnoreCase("hostile")
|
|
|| arguments[2].equalsIgnoreCase("passive"))) {
|
|
return ENABLE_MODES.stream()
|
|
.filter(mode -> mode.startsWith(prefix))
|
|
.toList();
|
|
}
|
|
if (arguments[1].equalsIgnoreCase("type")) {
|
|
return PocketBaseWorldType.fromCommand(arguments[2])
|
|
.map(PocketBaseBiome::commandNames)
|
|
.orElse(List.of())
|
|
.stream()
|
|
.filter(biome -> biome.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;
|
|
}
|
|
|
|
@FunctionalInterface
|
|
interface DialogProvider {
|
|
Dialog create(PlayerState owner, PocketBaseState pocket);
|
|
}
|
|
|
|
private static void sendUsage(Player player) {
|
|
player.sendMessage(ChatColor.RED + "Usage: /basesettings "
|
|
+ "[ui|status|upgrade|pocket upgrade|pocket flight <enable|disable>|pocket mobs "
|
|
+ "<hostile|passive> <enable|disable>"
|
|
+ "|pocket type <void|nether|overworld> <subtype>"
|
|
+ "|visitors <allowed|blocked>|navigation <enable|disable>"
|
|
+ "|flight <enable|disable>|border <enable|disable>"
|
|
+ "|spawnable <enable|disable>|bossbar <enable|disable>]");
|
|
}
|
|
}
|