Hub / src / main / java / com / lifeknight / relaymchub / miscellaneous / SmartGUIUtils.java
SmartGUIUtils.java
Raw
package com.lifeknight.relaymchub.miscellaneous;

import com.lifeknight.relaymchub.ExtraListeners;
import com.lifeknight.relaymchub.Main;
import com.lifeknight.relaymchub.player.HubPlayer;
import com.lifeknight.relaymcutils.player.*;
import com.lifeknight.relaymcutils.player.settings.GameSettings;
import com.lifeknight.relayutils.RelayUtils;
import com.lifeknight.relayutils.basic.Miscellaneous;
import com.lifeknight.relayutils.basic.Text;
import com.lifeknight.relayutils.data.QueueData;
import com.lifeknight.relayutils.game.GameType;
import com.lifeknight.relayutils.game.MainGame;
import com.lifeknight.relayutils.player.Group;
import com.lifeknight.relayutils.utilities.ItemUtilities;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern;

import static org.bukkit.ChatColor.*;

public class SmartGUIUtils {
    public static List<QueueData> currentGames;
    public static List<QueueData> queuedGames;
    private static Map<GameType, Integer> counts = new HashMap<>();

    public static void initialize() {

    }

    public static final SmartGUI MAIN_GUI = new SmartGUI("Relay", 3, false);

    static {
        MAIN_GUI.add(
                0,
                Material.COMPASS,
                (player, rightClick, shift) -> HubPlayer.getHubPlayer(player).sendToMainHub(),
                YELLOW + "Main Lobby",
                AQUA + "Go to the Main lobby.");
        MAIN_GUI.add(
                18,
                Material.COMPARATOR,
                (player, rightClick, shift) -> HubPlayer.getHubPlayer(player).showSettingsGui(),
                YELLOW + "Settings",
                AQUA + "Edit your server settings.");
        fillMainGui();
        MAIN_GUI.add(26,
                Material.PINK_DYE,
                (player, b, b1) -> {
                    HubPlayer.getHubPlayer(player).togglePlayers();

                },
                YELLOW + "Toggle Players",
                AQUA + "Show or hide players in the lobby.");
        MAIN_GUI.fillEmptySpots();

        for (GameType value : GameType.getGameTypes()) {
            setGuiForGameType(new SmartGUI(value.getFullPrettyName(), 1, false), value);
        }

        for (MainGame mainGame : MainGame.values()) {
            List<GameType> types = Miscellaneous.filter(mainGame.getGameTypes(), gameType -> gameType.isEnabled() && gameType.isMainType());

            SmartGUI gameModes = new SmartGUI(mainGame.getName() + " Modes", 1, false);
            setForGameModes(gameModes, types);
        }
    }

    public static final SmartItem NAVIGATOR = new SmartItem(Material.COMPASS, YELLOW + "Navigator");
    public static final SmartItem PLAY = new SmartItem(Material.DIAMOND_SWORD, BLUE + "Play");
    public static final SmartItem REQUEUE = new SmartItem(Material.REDSTONE, LIGHT_PURPLE + "Requeue");
    public static final SmartItem COSMETICS = new SmartItem(Material.EMERALD, GREEN + "Cosmetics");
    public static final SmartItem MENU_OPEN = new SmartItem(Material.EMERALD, GREEN + "Menu");
    public static final SmartItem HIDE_PLAYERS = new SmartItem(Material.LIME_DYE, GREEN + "Hide Players");
    public static final SmartItem SHOW_PLAYERS = new SmartItem(Material.GRAY_DYE, GRAY + "Show Players");
    public static final SmartItem LOBBY_SELECTOR = new SmartItem(Material.NETHER_STAR, GOLD + "Lobby Selector");
    public static final SmartItem STATISTICS = new SmartItem(Material.PAPER, AQUA + "Statistics");
    public static final SmartItem KIT_EDITOR = new SmartItem(Material.BOOK, LIGHT_PURPLE + "Kits");
    public static final SmartItem KIT_SELECTOR = new SmartItem(Material.FIREWORK_STAR, GOLD + "Kit Selector");
    public static final SmartItem CUSTOM_MAP_MENU = new SmartItem(Material.NETHER_STAR, AQUA + "Menu");

    public static final SmartItem SPAWN = new SmartItem(Material.FEATHER, RED + "Back to Spawn");

    public static final SmartGUI LOBBY_SELECTOR_GUI = new SmartGUI("Lobby Selector", 1);
    public static final SmartGUI PARTY_MODES = new SmartGUI("Party Modes", 5);

    public static final SmartGUI MENU = new SmartGUI("Menu", 1, false);

    public static final SmartGUI CUSTOM_KITS_QUEUE = new SmartGUI("Custom Kits", 6, false);

    public static final HashMap<GameType, SmartGUI> GAME_QUEUES = new HashMap<>();

    static {
        for (GameType gameType : Miscellaneous.getList(GameType.DUELS_AXE, GameType.DUELS_CRYSTAL, GameType.MANHUNT_1V1, GameType.DUELS_SWORD, GameType.DUELS_NETHERITE_POT, GameType.DUELS_UHC, GameType.DUELS_DIAMOND_POT)) {
            GAME_QUEUES.put(gameType, new SmartGUI(gameType.getFullPrettyName(), 4, false));
        }
        if (Main.isDuelsLobby()) {
            GAME_QUEUES.put(GameType.DUELS_CUSTOM, CUSTOM_KITS_QUEUE);
        }
    }

    public static final SmartItem PREVIEW_KIT = new SmartItem(Material.NETHER_STAR, GOLD + "Preview Kit");
    public static final SmartItem ACCEPT_KIT = new SmartItem(Material.GREEN_TERRACOTTA, GOLD + "Accept Kit");
    public static final SmartItem CANCEL_KIT = new SmartItem(Material.RED_TERRACOTTA, GOLD + "Return to Browsing");
    public static final SmartItem LEAVE_QUEUE = new SmartItem(Material.FEATHER, RED + "Leave Queue");

    public static final SmartItem CANCEL_MAP = new SmartItem(Material.RED_TERRACOTTA, RED + "Cancel", DARK_GRAY + "Cancel selecting this map.");
    public static final SmartItem ACCEPT_MAP = new SmartItem(Material.GREEN_TERRACOTTA, GREEN + "Accept", DARK_GRAY + "Select this map for your kit.");
    public static final SmartItem ADD_TO_MAP_LIBRARY = new SmartItem(Material.WRITTEN_BOOK, AQUA + "Add to Your Library", DARK_GRAY + "Add a copy of this map to your library.");
    public static final SmartItem MAP_INFO = new SmartItem(Material.PAPER, AQUA + "Map Info", DARK_GRAY + "View information for this custom map.");

    static {
        MENU.add(2, Material.EMERALD, (player, b, b2) -> HubPlayer.getHubPlayer(player).openCosmeticsGui(), GREEN + "Cosmetics");
        MENU.add(4, Material.BOOK, (player, b, b2) -> HubPlayer.getHubPlayer(player).openDefaultKitListGui(), LIGHT_PURPLE + "Kits");
        MENU.add(6, Material.PAPER, (player, b, b2) -> HubPlayer.getHubPlayer(player).showStatistics(), AQUA + "Statistics");

        KIT_SELECTOR.onPlayerClick((player, b, b1) -> HubPlayer.getHubPlayer(player).showLobbyKitSelect());
    }

    static {
        NAVIGATOR.onPlayerClick((player, rightClick, shift) -> MAIN_GUI.open(player));
        PLAY.onPlayerClick((player, rightClick, shift) -> getGameModeGUI().open(player));
        REQUEUE.onPlayerClick((player, b, b1) -> HubPlayer.getHubPlayer(player).reQueue());
        MENU_OPEN.onPlayerClick((player, b, b2) -> HubPlayer.getHubPlayer(player).openGui(MENU));
        STATISTICS.onPlayerClick((player, b, b2) -> HubPlayer.getHubPlayer(player).showStatistics());
        HIDE_PLAYERS.onPlayerClick((player, b, b2) -> HubPlayer.getHubPlayer(player).hidePlayers());
        COSMETICS.onPlayerClick((player, b, b2) -> HubPlayer.getHubPlayer(player).openCosmeticsGui());
        SHOW_PLAYERS.onPlayerClick((player, b, b2) -> HubPlayer.getHubPlayer(player).showPlayers());
        KIT_EDITOR.onPlayerClick((player, b, b2) -> HubPlayer.getHubPlayer(player).openDefaultKitListGui());
        LOBBY_SELECTOR.onPlayerClick((player, b, b2) -> LOBBY_SELECTOR_GUI.open(player));
        CUSTOM_MAP_MENU.onPlayerClick((player, b, b1) -> HubPlayer.getHubPlayer(player).showCustomMapMenu());
        SPAWN.onPlayerClick((player, b, b1) -> {
            HubPlayer hubPlayer = HubPlayer.getHubPlayer(player);
            if (hubPlayer.isCollaboratingCustomMap()) {
                hubPlayer.leaveCustomMap();
            } else {
                player.chat("/spawn");
            }
        });

        PREVIEW_KIT.onPlayerClick((player, b, b1) -> HubPlayer.getHubPlayer(player).previewKit());
        ACCEPT_KIT.onPlayerClick((player, b, b1) -> HubPlayer.getHubPlayer(player).acceptKit());
        CANCEL_KIT.onPlayerClick((player, b, b1) -> HubPlayer.getHubPlayer(player).cancelKit(true));

        ACCEPT_MAP.onPlayerClick((player, b, b1) -> HubPlayer.getHubPlayer(player).acceptMap());
        CANCEL_MAP.onPlayerClick(((player, b, b1) -> HubPlayer.getHubPlayer(player).cancelMap()));
        ADD_TO_MAP_LIBRARY.onPlayerClick(((player, b, b1) -> HubPlayer.getHubPlayer(player).addToMapLibrary()));
        MAP_INFO.onPlayerClick((p, b, b1) -> HubPlayer.getHubPlayer(p).showMapInfo());

        LEAVE_QUEUE.onPlayerClick(((player, b, b1) -> HubPlayer.getHubPlayer(player).cancelQueue()));
    }

    public static final SmartItem CHECKPOINT = new SmartItem(Material.HEAVY_WEIGHTED_PRESSURE_PLATE, GREEN + "Teleport to Last Checkpoint");
    public static final SmartItem RESET = new SmartItem(Material.RED_BED, YELLOW + "Reset");
    public static final SmartItem EXIT = new SmartItem(Material.FEATHER, RED + "Exit");

    static {
        CHECKPOINT.onPlayerClick((player, b, b1) -> HubPlayer.getHubPlayer(player).teleportToParkourCheckpoint());
        RESET.onPlayerClick((player, b, b1) -> HubPlayer.getHubPlayer(player).resetParkour());
        EXIT.onPlayerClick((player, b, b1) -> HubPlayer.getHubPlayer(player).exitParkour());
    }

    public static void setGuiForGameType(SmartGUI smartGUI, GameType gameType) {
        if (gameType.getMainGame().getPartyGameType() != null) {
            if (gameType == GameType.DUELS_CUSTOM) {
                smartGUI.add(
                        2,
                        Material.DIAMOND_AXE,
                        (player, rightClick, shift) -> HubPlayer.getHubPlayer(player).queue(gameType, new QueueData().attachData("other", false)),
                        GOLD + BOLD.toString() + "YOUR KIT",
                        GRAY + "Play duels with your custom kit.");
                smartGUI.add(
                        4,
                        Material.FEATHER,
                        (player, rightClick, shift) -> HubPlayer.getHubPlayer(player).cancel(gameType),
                        RED + "Leave Queue",
                        GRAY + RelayUtils.format("Leave the %s queue.", gameType.getFullPrettyName()));
                smartGUI.add(
                        6,
                        Material.NETHERITE_AXE,
                        (player, rightClick, shift) -> HubPlayer.getHubPlayer(player).queue(gameType, new QueueData().attachData("other", true)),
                        GOLD + BOLD.toString() + "ANOTHER'S KIT",
                        GRAY + "Play duels with someone else's custom kit.");
            } else {
                smartGUI.add(
                        3,
                        Material.COMPASS,
                        (player, rightClick, shift) -> HubPlayer.getHubPlayer(player).queue(gameType),
                        YELLOW + "Play " + GREEN + gameType.getPrefix(),
                        GRAY + RelayUtils.format("Click to queue for %s.", gameType.getFullPrettyName()));
                smartGUI.add(
                        5,
                        Material.FEATHER,
                        (player, rightClick, shift) -> HubPlayer.getHubPlayer(player).cancel(gameType),
                        RED + "Leave Queue",
                        GRAY + RelayUtils.format("Leave the %s queue.", gameType.getFullPrettyName()));
            }
        } else {
            smartGUI.add(
                    3,
                    Material.COMPASS,
                    (player, rightClick, shift) -> HubPlayer.getHubPlayer(player).queue(gameType),
                    YELLOW + "Play " + GREEN + gameType.getPrefix().toUpperCase(),
                    GRAY + RelayUtils.format("Click to queue for public %s.", gameType.getFullPrettyName()));
            smartGUI.add(
                    5,
                    gameType.getRepresentative(),
                    (player, rightClick, shift) -> HubPlayer.getHubPlayer(player).partyQueue(gameType),
                    DARK_RED + "PRIVATE " + GREEN + gameType.getPrefix().toUpperCase(),
                    GRAY + RelayUtils.format("Click to play %sprivate %s with custom game settings!.", RED, gameType.getPrefix()));
        }
        smartGUI.fillEmptySpots();
    }

    public static void setForGameModes(SmartGUI smartGUI, List<GameType> types) {
        types = Miscellaneous.firstFew(5, types);
        int index = 4 - types.size() + 1;
        int iteration = 0;
        for (GameType type : types) {
            if (counts != null) {
                Integer count = counts.getOrDefault(type, -1);
                smartGUI.add(
                        index,
                        getMaterialForIteration(iteration),
                        (player, rightClick, shift) -> queueGui(SmartPlayer.getSmartPlayer(player), type),
                        GOLD + type.getSuffix(),
                        YELLOW.toString() + count + " playing");
            } else {
                smartGUI.add(
                        index,
                        getMaterialForIteration(iteration),
                        (player, rightClick, shift) -> selectGameMode(player, type),
                        GOLD + type.getSuffix());

            }

            index += 2;
            iteration++;
        }

        smartGUI.fillEmptySpots();
    }

    public static void onPlayerCount(Map<GameType, Integer> counts) {
        SmartGUIUtils.counts = counts;
        for (MainGame mainGame : MainGame.values()) {
            List<GameType> types = Miscellaneous.filter(mainGame.getGameTypes(), gameType -> gameType.isEnabled() && gameType.isMainType());

            SmartGUI gameModes = SmartGUI.getByName(mainGame.getName() + " Modes");
            if (gameModes != null) {
                setForGameModes(gameModes, types);
            }
        }

        fillMainGui();
    }

    public static void selectGameMode(Player player, GameType gameType) {
        HubPlayer hubPlayer = HubPlayer.getHubPlayer(player);
        SmartGUI smartGUI = new SmartGUI(gameType.getFullPrettyName(), 1, true);
        if (gameType.isExplicitParty()) {
            ItemStack privateQueue = new ItemStack(Material.COMPASS);
            privateQueue.setAmount(gameType.getTotalPlayerCount());
            ItemUtilities.enchanted(privateQueue);
            smartGUI.add(
                    4,
                    privateQueue,
                    (player1, b, b1) -> hubPlayer.partyQueue(gameType),
                    RelayUtils.format("%s%sPRIVATE %s%s%s", DARK_RED, BOLD, GREEN, BOLD, gameType.getPrefix().toUpperCase()),
                    GRAY + RelayUtils.format("Click to play %sprivate%s %s with custom game settings!", RED, GRAY, gameType.getFullPrettyName()),
                    " ",
                    RelayUtils.format("%sPREMIUM %srequired to play.", GOLD, DARK_GRAY),
                    RelayUtils.format("%s%s", DARK_GREEN, getPlayerCountLine(gameType)));
        } else {
            ItemStack publicQueue = gameType.getRepresentative().clone();
            publicQueue.setAmount(Math.min(64, hubPlayer.getPartySize()));
            smartGUI.add(
                    3,
                    publicQueue,
                    (player1, b, b1) -> hubPlayer.queue(gameType),
                    RelayUtils.format("%sPlay %s%s%s", YELLOW, GREEN, BOLD, gameType.getPrefix().toUpperCase()),
                    GRAY + RelayUtils.format("Click to queue for public %s.", gameType.getFullPrettyName()),
                    " ",
                    RelayUtils.format("%sTwo players required to start.", DARK_GRAY),
                    RelayUtils.format("%s%s", DARK_GREEN, getPlayerCountLine(gameType)));
            boolean shouldShowFeather = gameType.getMainGame().getPartyGameType() != null;
            ItemStack privateQueue = new ItemStack(Material.COMPASS);
            privateQueue.setAmount(gameType.getTotalPlayerCount());
            ItemUtilities.enchanted(privateQueue);
            if (shouldShowFeather) {
                smartGUI.add(
                        5,
                        Material.FEATHER,
                        (p, rightClick, shift) -> hubPlayer.cancel(gameType),
                        RED + "Leave Queue",
                        GRAY + RelayUtils.format("Leave the %s queue.", gameType.getFullPrettyName()));
            } else {
                smartGUI.add(
                        5,
                        privateQueue,
                        (player1, b, b1) -> hubPlayer.partyQueue(gameType),
                        RelayUtils.format("%s%sPRIVATE %s%s%s", DARK_RED, BOLD, GREEN, BOLD, gameType.getPrefix().toUpperCase()),
                        GRAY + RelayUtils.format("Click to play %sprivate%s %s with custom game settings!", RED, GRAY, gameType.getFullPrettyName()),
                        " ",
                        RelayUtils.format("%sPREMIUM %srequired to play.", GOLD, DARK_GRAY),
                        RelayUtils.format("%s%s", DARK_GREEN, getPlayerCountLine(gameType)));
            }
        }
        hubPlayer.openGui(smartGUI);
    }

    private static String getPlayerCountLine(GameType gameType) {
        return counts.getOrDefault(gameType, 0) + " playing";
    }

    public static void fillMainGui() {
        List<MainGame> elements = Miscellaneous.getList(MainGame.MANHUNT, MainGame.DUELS, MainGame.PARTY);
        int row = 1;
        final int maxRowSize = 5;
        int index = 4 - Math.min(elements.size(), maxRowSize) / 2;
        int rowIndex = 1;
        for (MainGame mainGame : elements) {
            MAIN_GUI.add(
                    row * 9 + index,
                    mainGame.getRepresentative(),
                    (player, b, b1) -> {
                        if (mainGame == MainGame.PARTY) {
                            getPartyModesGui().open(player);
                        } else {
                            if (Main.mainGame == mainGame) {
                                ExtraListeners.spawnPlayer(player);
                            } else {
                                SmartPlayer.getSmartPlayer(player).sendToServer(mainGame.getComparableName());
                            }
                        }
                    },
                    GREEN + (mainGame == MainGame.PARTY ? "Other Modes" : mainGame.getName()),
                    Miscellaneous.with(Miscellaneous.processList(Miscellaneous.getList(mainGame.getDescription().split("\n")), s -> WHITE + s), YELLOW + getTotalPlayerCount(mainGame)));
            index++;
            rowIndex++;
            if (rowIndex > maxRowSize) {
                row++;
                rowIndex = 1;
                index = 4 - Math.min(elements.size() - (row - 1) * maxRowSize, maxRowSize) / 2;
            }
        }
    }

    public static List<String> getDescription(GameType gameType, int playingCount) {
        List<String> description = new ArrayList<>();
        String descriptionString;
        if (gameType == GameType.BLOCK_SHUFFLE) {
            descriptionString = "Players must stand on a given block before\n" +
                    "time runs out. The player who lasts the most\n" +
                    "rounds wins!";
        } else if (gameType == GameType.RANDOM_ITEMS) {
            descriptionString = "Battle royale where each player gets random items\n" +
                    "every round. Blocks do not drop items.";
        } else if (gameType == GameType.DEATH_SWAP) {
            descriptionString = "Every 5 minutes players swap places, kill\n" +
                    "the other player before they kill you!";
        } else if (gameType == GameType.DEATH_SHUFFLE) {
            descriptionString = "Players must die a certain way before the\n" +
                    "time runs out. The player who lasts the most\n" +
                    "rounds wins!";
        } else if (gameType == GameType.MANSAVE) {
            descriptionString = "The runner must die before the time runs out\n" +
                    "while the saver is trying to stop them.";
        } else if (gameType == GameType.BINGO) {
            descriptionString = "Complete the challenges on a bingo board \n" +
                    "horizontally, vertically, or diagonally to win!";
        } else {
            descriptionString = "The hunter must punch the runner before the \n" +
                    "time runs out, while the runner must survive.";
        }

        description.addAll(Miscellaneous.processList(o -> GRAY + o, descriptionString.split(Pattern.quote("\n"))));
        description.add(YELLOW.toString() + playingCount + " playing");

        return description;
    }

    public static SmartGUI getPartyModesGui() {
        SmartGUI smartGUI = new SmartGUI("Other Modes", 3, false);
        List<GameType> elements = Miscellaneous.getList(GameType.BLOCK_SHUFFLE, GameType.RANDOM_ITEMS,
                GameType.DEATH_SWAP, GameType.DEATH_SHUFFLE, GameType.MANSAVE, GameType.ULTIMATE_TAG, GameType.BINGO);
        int index = 10;

        for (GameType gameType : elements) {
            smartGUI.add(index++,
                    gameType.getRepresentative(),
                    (player, b, b1) -> selectGameMode(player, gameType),
                    YELLOW + gameType.getPrefix(),
                    getDescription(gameType, counts.getOrDefault(gameType, 0)));
        }
/*
        int row = 1;
        final int maxRowSize = 8;
        int index = 4 - Math.min(elements.size(), maxRowSize) / 2;
        int rowIndex = 0;
        for (GameType gameType : elements) {
            smartGUI.add(
                    row * 9 + index,
                    gameType.getRepresentative(),
                    (player, b, b1) -> selectGameMode(player, gameType),
                    YELLOW + gameType.getPrefix(),
                    GRAY + "Play " + gameType.getPrefix() + ".",
                    YELLOW.toString() + counts.getOrDefault(gameType, 0) + " playing");
            index++;
            rowIndex++;
            if (rowIndex > maxRowSize) {
                row++;
                rowIndex = 1;
                index = 4 - Math.min(elements.size() - (row - 1) * maxRowSize, maxRowSize) / 2;
            }
        }
*/

        smartGUI.fillEmptySpots();

        return smartGUI;
    }

    public static String getTotalPlayerCount(MainGame mainGame) {
        AtomicInteger count = new AtomicInteger();
        counts.forEach((gameType, integer) -> {
            if (gameType.getMainGame() == mainGame) {
                count.addAndGet(integer);
            }
        });

        return count.get() + " playing";
    }

    private static Material getMaterialForIteration(int iteration) {
        switch (iteration) {
            case 0:
                return Material.IRON_INGOT;
            case 1:
                return Material.GOLD_INGOT;
            case 2:
                return Material.DIAMOND;
            case 3:
                return Material.EMERALD;
        }

        return Material.NETHERITE_INGOT;
    }

    public static ItemStack getItemStackForSettingName(String name) {
        name = Text.toComparable(name);

        switch (name) {
            case "anybody":
            case "low":
                return new ItemStack(Material.GREEN_TERRACOTTA);
            case "friendsoffriends":
            case "medium":
                return new ItemStack(Material.YELLOW_TERRACOTTA);
            case "friends":
            case "high":
                return new ItemStack(Material.RED_TERRACOTTA);
        }

        return new ItemStack(Material.BLACK_TERRACOTTA);
    }


    public static SmartGUI getGameModeGUI() {
        if (Main.isGameLobby()) {
            return SmartGUI.getByName(Main.getMainGame().getName() + " Modes");
        }

        return null;
    }


    public static void onHubs(Map<String, Integer> hubs) {
        int index = 0;
        LOBBY_SELECTOR_GUI.clearAll();
        for (String hubName : hubs.keySet()) {
            int count = hubs.get(hubName);
            String countLine = (count >= RelayUtils.HUB_DEFAULT_MAX_SIZE ? RED.toString() + count : YELLOW.toString() + count) + AQUA + "/" + RelayUtils.HUB_DEFAULT_MAX_SIZE;
            Material representative;
            if (count >= RelayUtils.HUB_DEFAULT_MAX_SIZE) {
                representative = count >= RelayUtils.HUB_EXTENDED_MAX_SIZE ? Material.REDSTONE_BLOCK : Material.IRON_BLOCK;
            } else {
                representative = Material.IRON_BLOCK;
            }
            LOBBY_SELECTOR_GUI.add(index,
                    hubName.equalsIgnoreCase(RelayUtils.getName()) ? ItemUtilities.enchanted(representative) : new ItemStack(representative),
                    (player, b, b2) -> {
                        HubPlayer hubPlayer = HubPlayer.getHubPlayer(player);
                        if (count >= RelayUtils.HUB_DEFAULT_MAX_SIZE) {
                            if (hubPlayer.isInGroupOrHigher(Group.TIERIII)) {
                                if (count >= RelayUtils.HUB_EXTENDED_MAX_SIZE) {
                                    if (hubPlayer.isStaff()) {
                                        hubPlayer.sendToServer(hubName);
                                    } else {
                                        hubPlayer.sendErrorMessage("That lobby has reached maximum capacity!");
                                        hubPlayer.playErrorSound();
                                    }
                                } else {
                                    hubPlayer.sendToServer(hubName);
                                }
                            } else {
                                hubPlayer.sendErrorMessage("That lobby has reached maximum capacity! Upgrade to Tier III to join lobbies with more than %d players!", RelayUtils.HUB_EXTENDED_MAX_SIZE);
                                hubPlayer.playErrorSound();
                            }
                        } else {
                            hubPlayer.sendToServer(hubName);
                        }
                    },
                    YELLOW + getPrettyName(Main.hubType) + " Lobby " + (index + 1),
                    getHubInfoLines(countLine, hubName));
            index++;
        }
    }

    public static String[] getHubInfoLines(String countLine, String hubName) {
        if (hubName.equalsIgnoreCase(RelayUtils.getName())) {
            return new String[]{countLine, GREEN + "Connected"};
        }

        return new String[]{countLine};
    }

    public static String getPrettyName(String codeName) {
        if ("main".equals(Text.toComparable(codeName))) {
            return "Main";
        }

        return MainGame.getMainGame(Main.hubType).getName();
    }

    public static void queueGui(SmartPlayer smartPlayer, GameType gameType) {
        HubPlayer hubPlayer = HubPlayer.getHubPlayer(smartPlayer);

        if (hubPlayer.isInParty() && !GAME_QUEUES.containsKey(gameType)) {
            hubPlayer.partyQueue(gameType);
            return;
        } else {
            if (GAME_QUEUES.containsKey(gameType)) {
                hubPlayer.openGui(GAME_QUEUES.get(gameType));
                return;
            }
        }

        SmartGUI smartGUI = new SmartGUI(gameType.getFullPrettyName(), 1, true);
        if (gameType.getMainGame().getPartyGameType() != null) {
            if (gameType == GameType.DUELS_CUSTOM) {
                smartGUI.add(
                        2,
                        Material.DIAMOND_AXE,
                        (player, rightClick, shift) -> HubPlayer.getHubPlayer(player).queue(gameType, new QueueData().attachData("other", false)),
                        GOLD + BOLD.toString() + "YOUR KIT",
                        GRAY + "Play duels with your custom kit.");
                smartGUI.add(
                        4,
                        Material.FEATHER,
                        (player, rightClick, shift) -> HubPlayer.getHubPlayer(player).cancel(gameType),
                        RED + "Leave Queue",
                        GRAY + RelayUtils.format("Leave the %s queue.", gameType.getFullPrettyName()));
                smartGUI.add(
                        6,
                        Material.NETHERITE_AXE,
                        (player, rightClick, shift) -> HubPlayer.getHubPlayer(player).queue(gameType, new QueueData().attachData("other", true)),
                        GOLD + BOLD.toString() + "ANOTHER'S KIT",
                        GRAY + "Play duels with someone else's custom kit.");
            } else {
                SmartGUI smartGUI1 = GAME_QUEUES.get(gameType);
                if (smartGUI1 != null) {
                    smartPlayer.openGui(smartGUI1);
                    return;
                }
                smartGUI.add(
                        3,
                        Material.COMPASS,
                        (player, rightClick, shift) -> HubPlayer.getHubPlayer(player).queue(gameType),
                        YELLOW + "Play " + GREEN + gameType.getPrefix(),
                        GRAY + RelayUtils.format("Click to queue for %s.", gameType.getFullPrettyName()));
                smartGUI.add(
                        5,
                        Material.FEATHER,
                        (player, rightClick, shift) -> HubPlayer.getHubPlayer(player).cancel(gameType),
                        RED + "Leave Queue",
                        GRAY + RelayUtils.format("Leave the %s queue.", gameType.getFullPrettyName()));
            }
        } else {
            smartGUI.add(
                    3,
                    Material.COMPASS,
                    (player, rightClick, shift) -> HubPlayer.getHubPlayer(player).queue(gameType),
                    YELLOW + "Play " + GREEN + gameType.getPrefix().toUpperCase(),
                    GRAY + RelayUtils.format("Click to queue for public %s.", gameType.getFullPrettyName()));
            smartGUI.add(
                    5,
                    gameType.getRepresentative(),
                    (player, rightClick, shift) -> HubPlayer.getHubPlayer(player).partyQueue(gameType),
                    DARK_RED + "PRIVATE " + GREEN + gameType.getPrefix().toUpperCase(),
                    GRAY + RelayUtils.format("Click to play %sprivate %s with custom game settings!.", RED, gameType.getPrefix()));
        }
        smartGUI.fillEmptySpots();

        smartPlayer.openGui(smartGUI);
    }

    public static void updateQueueGUIs(List<QueueData> currentGames, List<QueueData> queuedGames) {
        for (Game game : GAMES) {
            if (Miscellaneous.match(queuedGames, queueData -> queueData.hasData("owner") && queueData.getData("owner").equals(game.ownerUUID)) == null) {
                HubPlayer.onCustomQueueRemoved(game.ownerUUID);
            }
        }

        GAMES.removeIf(game -> {
            QueueData current = Miscellaneous.match(currentGames, queueData -> queueData.hasData("owner") && queueData.getData("owner").equals(game.ownerUUID));
            QueueData queued = Miscellaneous.match(queuedGames, queueData -> queueData.hasData("owner") && queueData.getData("owner").equals(game.ownerUUID));

            if (current == null && queued == null) {
                return true;
            }

            if (queued != null && Text.comparablyEquals(queued.getData("gameType"), game.gameType)) {
                return true;
            }

            return false;
        });

        GAME_QUEUES.forEach((gameType, smartGUI) -> updateQueuesGUI(gameType, smartGUI, currentGames, queuedGames));

        for (QueueData liveGame : currentGames) {
            if (liveGame.hasData("index")) {
                Game game = getGame(liveGame);
                try {
                    updateCurrentGame(game, liveGame);
                } catch (Exception e) {
                    RelayUtils.printError("couldn't do the live one man", e, 5);
                }
            }
        }
    }

    private static void updateQueuesGUI(GameType gameType, SmartGUI smartGUI, List<QueueData> currentGames, List<QueueData> queuedGames) {
        smartGUI.clearAll();
        smartGUI.fillEmptySpots();

        for (int i = 9; i < (gameType == GameType.DUELS_CUSTOM ? 54 : 36); i++) {
            int finalI = i;
            smartGUI.add(i, Material.POLISHED_BLACKSTONE_BUTTON, (player, b, b1) -> HubPlayer.getHubPlayer(player).createQueue(finalI, gameType), GREEN + BOLD.toString() + "QUEUE " + gameType.getDisplaySuffix().toUpperCase());
        }

        if (gameType.getMainGame() != MainGame.MANHUNT) {


            smartGUI.add(3, Material.OAK_SIGN, (player, b, b1) -> HubPlayer.getHubPlayer(player).changePreferredSize(smartGUI, gameType), LIGHT_PURPLE + (gameType == GameType.DUELS_CUSTOM ? "Size" : "Public Settings"), GRAY + (gameType == GameType.DUELS_CUSTOM ? "Change the size of the game you want to play in." : "Change the settings of your public game."));

            smartGUI.add(
                    5,
                    Material.COMPASS,
                    (player, rightClick, shift) -> HubPlayer.getHubPlayer(player).partyQueue(gameType),
                    DARK_RED + "Private " + AQUA + (gameType.getMainGame() == MainGame.MANHUNT ? "Manhunt" : gameType.getDisplaySuffix()),
                    DARK_GRAY + RelayUtils.format("Click to play %s with your party.", gameType.getMainGame() == MainGame.MANHUNT ? "Private Manhunt" : gameType.getDisplaySuffix() + " Duels"));
        } else {
            smartGUI.add(4, Material.OAK_SIGN, (player, b, b1) -> HubPlayer.getHubPlayer(player).changePreferredSize(smartGUI, gameType), LIGHT_PURPLE + (gameType == GameType.DUELS_CUSTOM ? "Size" : "Public Settings"), GRAY + (gameType == GameType.DUELS_CUSTOM ? "Change the size of the game you want to play in." : "Change the settings of your public game."));

        }

        for (QueueData queuedGame : queuedGames) {
            if (queuedGame.hasData("owner") && GameType.getGameType(queuedGame.getData("gameType")) == gameType) {
                List<UUID> ready = Miscellaneous.processList(Text.separateCSV(queuedGame.getData("queued")), UUID::fromString);

                for (QueueData game : queuedGames) {
                    if (game.hasData("with") && game.getData("with").equals(queuedGame.getData("owner"))) {
                        ready.addAll(Miscellaneous.processList(Text.separateCSV(game.getData("queued")), UUID::fromString));
                    }
                }
                Game game = getGame(queuedGame);
                if (gameType == GameType.DUELS_CUSTOM) {
                    updateQueuedKit(game, queuedGame, ready);
                } else {
                    updateQueuedGame(game, queuedGame, gameType, smartGUI, ready);
                }
            }
        }
    }

    private static void updateQueuedGame(Game game, QueueData queuedGame, GameType gameType, SmartGUI smartGUI, List<UUID> ready) {
        SmartPlayer owner = SmartPlayer.getOrCreate(UUID.fromString(queuedGame.getData("owner")));
        if (owner == null) {
            Main.error("owner null for %s", queuedGame.getData("owner"));
            return;
        }
        ItemStack representative = gameType.getRepresentative();

        int playersNeeded = queuedGame.getInt("size");

        ItemStack toShow = new ItemStack(representative);

        List<String> playersDescription = new ArrayList<>();
        playersDescription.add("");
        playersDescription.add(AQUA.toString() + ready.size() + "/" + playersNeeded + DARK_AQUA + " Players");
        for (UUID uuid : ready) {
            playersDescription.add(DARK_GRAY + SmartPlayer.getOrCreate(uuid).getName());
        }
        if (gameType.getMainGame() == MainGame.DUELS) {
            int teamSize = playersNeeded / 2;
            List<String> description = new ArrayList<>();
            if (queuedGame.hasData("map")) {
                description.add(YELLOW + "Map: " + GRAY + Main.getMaps(gameType).get(queuedGame.getInt("map")).getName());
            }
            if (queuedGame.hasData("rounds")) {
                description.add(YELLOW + "Rounds: " + GRAY + queuedGame.getData("rounds"));
            }
            toShow = ItemUtilities.edit(toShow, DARK_GREEN + "Play " + gameType.getDisplaySuffix() + GREEN + " " + teamSize + "v" + teamSize + ".", Miscellaneous.with(description, playersDescription));
            smartGUI.add(game.id, toShow, (player, b, b1) -> HubPlayer.getHubPlayer(player).onClickQueuedGame(queuedGame, gameType, playersNeeded, ready.size()));
        } else {
            int runners = queuedGame.getInt("runners");
            int hunters = queuedGame.getInt("hunters");
            List<String> description = new ArrayList<>();
            description.add(GRAY + "Runners: " + YELLOW + runners);
            description.add(GRAY + "Hunters: " + YELLOW + hunters);
            description.add("");

            if (queuedGame.hasData("release")) {
                description.add(GRAY + "Hunter Release: " + YELLOW + queuedGame.getInt("release") + " seconds");
            }

            if (queuedGame.hasData("dropRate")) {
                int rate = queuedGame.getInt("dropRate");
                description.add(GRAY + "Drops: " + YELLOW + (rate == 1 ? "Boosted" : "Normal"));
            }

            if (queuedGame.hasData("tradeRate")) {
                int rate = queuedGame.getInt("tradeRate");
                description.add(GRAY + "Trading: " + YELLOW + (rate == 0 ? "1.16" : (rate == 1 ? "1.19" : "Boosted")));
            }

            description.addAll(playersDescription);
            toShow = ItemUtilities.edit(toShow, DARK_GREEN + "Play Manhunt " + GREEN + runners + "v" + hunters, description);
            smartGUI.add(game.id, toShow, (player, b, b1) -> HubPlayer.getHubPlayer(player).onClickQueuedGame(queuedGame, gameType, playersNeeded, ready.size()));
        }
    }

    private static void updateCurrentGame(Game game, QueueData currentGame) {
        try {

            List<String> description = new ArrayList<>();
            GameType gameType = GameType.getGameType(currentGame.getData("gameType"));
            if (currentGame.hasData("players")) {
                if (currentGame.hasData("map")) {
                    description.add(DARK_GRAY + "Map: " + YELLOW + currentGame.getData("map"));
                }
                if (currentGame.hasData("rounds")) {
                    description.add(DARK_GRAY + "Rounds Remaining: " + YELLOW + currentGame.getData("rounds"));
                }

                description.add(DARK_GRAY + "Players:");
                List<String> players = Text.separateCSV(currentGame.getData("players"));
                for (String player : players) {
                    description.add(YELLOW + "- " + player);
                }
            } else {
                if (gameType == GameType.MANHUNT_1V1) {
                    if (currentGame.hasData("livingRunners")) {
                        description.add(GREEN + "Runners:");
                        for (String livingRunner : Text.separateCSV(currentGame.getData("livingRunners"))) {
                            description.add(DARK_GRAY + " - " + livingRunner);
                        }

                        if (currentGame.hasData("hunters")) {
                            description.add(RED + "Hunters:");
                            for (String hunter : Text.separateCSV(currentGame.getData("hunters"))) {
                                description.add(DARK_GRAY + " - " + hunter);
                            }
                        }
                    } else {
                        description.add(DARK_GRAY + "Pre-game");
                        description.add(DARK_GRAY + "Wait to spectate...");
                    }
                } else {
                    description.add(DARK_GRAY + "Pre-game");
                    description.add(DARK_GRAY + "Wait to spectate...");
                }
            }
            ItemStack representative = ItemUtilities.getItemStack(Material.GRAY_DYE, AQUA + BOLD.toString() + "Spectate " + (gameType.getMainGame() == MainGame.DUELS ? "Duel" : "Manhunt"), description);
            SmartGUI smartGUI =
                    GAME_QUEUES.get(GameType.getGameType(currentGame.getData("gameType")));
            if (smartGUI != null) {
                smartGUI.add(game.id, representative, (player, b, b1) -> HubPlayer.getHubPlayer(player).spectate(currentGame.getData("owner")));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void updateQueuedKit(Game game, QueueData queuedGame, List<UUID> ready) {
        SmartPlayer owner = SmartPlayer.getOrCreate(UUID.fromString(queuedGame.getData("owner")));
        if (owner == null) {
            Main.error("owner null for %s", queuedGame.getData("owner"));
            return;
        }
        if (!queuedGame.hasData("size")) {
            return;
        }

        CustomDuelsKit customDuelsKit = owner.getSelectedCustomDuelsKit();

        List<String> description = new ArrayList<>();
        description.add(DARK_GRAY + "Right-Click to preview");
        description.add(GRAY + "Kit: " + YELLOW + (customDuelsKit == null ? "Loading..." : customDuelsKit.getKit().name));
        int playersNeeded = queuedGame.getInt("size");
        if (customDuelsKit != null) {

            String map = customDuelsKit.getMaps().get(0);

            if (DuelMap.getDuelMap(map) == null) {
                CustomMap customMap = CustomMap.CODE_TO_MAP.get(map);
                String val;
                if (customMap != null) {
                    val = customMap.getName();
                } else {
                    val = map;
                }
                description.add(GRAY + "Map: " + YELLOW + val + DARK_AQUA + " (Custom)");
            } else {
                description.add(GRAY + "Map: " + YELLOW + DuelMap.getDuelMap(map).getName());
            }

            GameSettings gameSettings = HubPlayer.getDefaultCustomDuelsGameSettings();
            gameSettings.applyFromJson(customDuelsKit.getGameSettings());

            description.add(GRAY + "Max Rounds: " + YELLOW + gameSettings.getInteger("rounds_to_win"));
            description.add(GRAY + "Items:");

            int itemCount = customDuelsKit.getKit().items.size();

            int j = 0;
            for (KitItem item : customDuelsKit.getKit().items) {
                ItemStack itemStack = item.itemStack;
                int count = itemStack.getAmount();
                Material material = itemStack.getType();
                description.add(DARK_GRAY + "- " + Text.prettify(material) + (count > 1 ? (" x" + count) : ""));
                j++;
                if (j >= 5) break;
            }
            if (itemCount > 5) {
                int remaining = itemCount - 5;
                description.add(DARK_GRAY + "..." + remaining + " More Item" + (remaining > 1 ? "s" : "") + "...");
            }

            description.add("");

            description.add(AQUA.toString() + ready.size() + "/" + playersNeeded + DARK_AQUA + " Players");
            for (UUID uuid : ready) {
                description.add(DARK_GRAY + SmartPlayer.getOrCreate(uuid).getName());
            }

        }
        ItemStack representative = ItemUtilities.getItemStack(customDuelsKit == null ? Material.RED_BANNER : customDuelsKit.getKit().representative.itemStack.getType(), AQUA + "Play " + Text.getPossessiveName(owner.getFormattedName()) + AQUA + " Kit", description);

        CUSTOM_KITS_QUEUE.add(game.id, representative, (player, b, b1) -> HubPlayer.getHubPlayer(player).onClickQueuedGame(queuedGame, b, customDuelsKit, playersNeeded, ready.size()));
    }

    private static Game getGame(QueueData queueData) {
        GameType gameType = GameType.getGameType(queueData.getData("gameType"));
        for (Game game : GAMES) {
            if (game.ownerUUID.equals(queueData.getData("owner")) && game.gameType == gameType) {
                return game;
            }
        }
        Game game = new Game(queueData.hasData("index") ? queueData.getInt("index") : getAvailableIndex(gameType), queueData.getData("owner"), GameType.getGameType(queueData.getData("gameType")));
        GAMES.add(game);

        return game;
    }

    public static int getAvailableIndex(GameType gameType) {
        for (int i = 9; i < 36; i++) {
            boolean canUse = true;
            for (Game game : GAMES) {
                if (game.gameType == gameType && game.id == i) {
                    canUse = false;
                    break;
                }
            }
            if (canUse) {
                return i;
            }
        }

        return 0;
    }

    private static final List<Game> GAMES = new ArrayList<>();

    private record Game(int id, String ownerUUID, GameType gameType) {

    }
}