aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/com/MylesAndMore/tumble/Game.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/MylesAndMore/tumble/Game.java')
-rw-r--r--src/main/java/com/MylesAndMore/tumble/Game.java593
1 files changed, 593 insertions, 0 deletions
diff --git a/src/main/java/com/MylesAndMore/tumble/Game.java b/src/main/java/com/MylesAndMore/tumble/Game.java
new file mode 100644
index 0000000..91c2770
--- /dev/null
+++ b/src/main/java/com/MylesAndMore/tumble/Game.java
@@ -0,0 +1,593 @@
+package com.MylesAndMore.tumble;
+
+import com.MylesAndMore.tumble.api.Generator;
+
+import com.MylesAndMore.tumble.api.Layers;
+import net.md_5.bungee.api.ChatMessageType;
+import net.md_5.bungee.api.chat.TextComponent;
+
+import org.bukkit.*;
+import org.bukkit.enchantments.Enchantment;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.Item;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.jetbrains.annotations.NotNull;
+
+import javax.annotation.Nullable;
+import java.util.*;
+
+/**
+ * This class holds all methods relating to the tumble Game in any way!
+ */
+public class Game {
+ // Singleton class logic
+ // Define the gameInstance
+ private static Game gameInstance;
+
+ // Private Game() constructor for singleton instance
+ private Game() {
+ gameWorld = Bukkit.getWorld(TumbleManager.getGameWorld());
+ gameSpawn = gameWorld.getSpawnLocation();
+ }
+
+ // ONLY Public method to get the game instance
+ public static Game getGame() {
+ if (gameInstance == null) {
+ gameInstance = new Game();
+ }
+ return gameInstance;
+ }
+
+
+ // Define local game vars
+ // The gameState keeps the current state of the game (I'm so creative, I know)
+ private String gameState;
+ // Define a variable for the gameType
+ private String gameType;
+ // Define a variable for the game ID
+ private int gameID = -1;
+ // Define a variable for the autostart PID
+ private int autoStartID = -1;
+ // Define a variable to keep the list of tracks that have already played in the game
+ List<String> sounds = new ArrayList<>();
+
+ // Initialize a new instance of the Random class for use later
+ private final Random Random = new Random();
+ // Define the game world and its spawnpoint as a new Location for use later
+ private final World gameWorld;
+ private final Location gameSpawn;
+ // Make a list of the game's players for later
+ private List<Player> gamePlayers;
+ // Make a list of the round's players
+ private List<Player> roundPlayers;
+ // Initialize a list to keep track of wins between rounds
+ private List<Integer> gameWins;
+
+
+ // BEGIN PUBLIC METHODS
+
+ /**
+ * Creates a new Game
+ * @param type The type of game
+ * @return true if the game succeeds creation, and false if not
+ */
+ public boolean startGame(@NotNull String type) {
+ // Check if the game is starting or running, if so, do not start
+ if (Objects.equals(gameState, "starting")) {
+ return false;
+ }
+ else if (Objects.equals(gameState, "running")) {
+ return false;
+ }
+ else {
+ // Define the gameType
+ switch (type) {
+ case "shovels":
+ case "snowballs":
+ case "mixed":
+ gameState = "starting";
+ // Set the type to gameType since it won't change for this mode
+ gameType = type;
+ // Clear the players' inventories so they can't bring any items into the game
+ clearInventories(TumbleManager.getPlayersInLobby());
+ // Generate the correct layers for a Shovels game
+ // The else statement is just in case the generator fails; this command will fail
+ if (generateLayers(type)) {
+ // Send all players from lobby to the game
+ scatterPlayers(TumbleManager.getPlayersInLobby());
+ } else {
+ return false;
+ }
+ break;
+ default:
+ // The game type in the config did not match a specified game type; return false to signify that
+ return false;
+ }
+ // If a game creation succeeded, then,
+ // Update the game's players for later
+ gamePlayers = new ArrayList<>(TumbleManager.getPlayersInGame());
+ // Update the round's players for later
+ roundPlayers = new ArrayList<>(TumbleManager.getPlayersInGame());
+ // Create a list that will later keep track of each player's wins
+ gameWins = new ArrayList<>();
+ gameWins.addAll(List.of(0,0,0,0,0,0,0,0));
+ // Wait 5s (100t) for the clients to load in
+ Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(TumbleManager.getPlugin(), () -> {
+ // Begin the countdown sequence
+ playSound(gamePlayers, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, SoundCategory.NEUTRAL, 5, 1);
+ displayTitles(gamePlayers, ChatColor.DARK_GREEN + "3", null, 3, 10, 7);
+ Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(TumbleManager.getPlugin(), () -> {
+ playSound(gamePlayers, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, SoundCategory.NEUTRAL, 5, 1);
+ displayTitles(gamePlayers, ChatColor.YELLOW + "2", null, 3, 10, 7);
+ Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(TumbleManager.getPlugin(), () -> {
+ playSound(gamePlayers, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, SoundCategory.NEUTRAL, 5, 1);
+ displayTitles(gamePlayers, ChatColor.DARK_RED + "1", null, 3, 10, 7);
+ Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(TumbleManager.getPlugin(), () -> {
+ playSound(gamePlayers, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, SoundCategory.NEUTRAL, 5, 2);
+ displayTitles(gamePlayers, ChatColor.GREEN + "Go!", null, 1, 5, 1);
+ setGamemode(gamePlayers, GameMode.SURVIVAL);
+ gameState = "running";
+ }, 20);
+ }, 20);
+ }, 20);
+ }, 100);
+ }
+ return true;
+ }
+
+ /**
+ * Initiates an automatic start of a Tumble game
+ */
+ public void autoStart() {
+ // Wait for the player to load in
+ Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(TumbleManager.getPlugin(), () -> {
+ gameState = "waiting";
+ displayActionbar(TumbleManager.getPlayersInLobby(), ChatColor.GREEN + "Game will begin in 15 seconds!");
+ playSound(TumbleManager.getPlayersInLobby(), Sound.BLOCK_NOTE_BLOCK_CHIME, SoundCategory.BLOCKS, 1, 1);
+ TumbleManager.getMVWorldManager().loadWorld(TumbleManager.getGameWorld());
+ // Schedule a process to start the game in 300t (15s) and save the PID so we can cancel it later if needed
+ autoStartID = Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(TumbleManager.getPlugin(), () -> {
+ startGame(TumbleManager.getGameType());
+ }, 300);
+ }, 50);
+ }
+
+ /**
+ * Cancels a "waiting" automatic start
+ */
+ public void cancelStart() {
+ Bukkit.getServer().getScheduler().cancelTask(Game.getGame().getAutoStartID());
+ displayActionbar(TumbleManager.getPlayersInLobby(), ChatColor.RED + "Game start cancelled!");
+ playSound(TumbleManager.getPlayersInLobby(), Sound.BLOCK_NOTE_BLOCK_BASS, SoundCategory.BLOCKS, 1, 1);
+ gameState = null;
+ autoStartID = -1;
+ }
+
+ /**
+ * This method should be called on the death of one of the Game's players
+ * @param player The player who died
+ */
+ public void playerDeath(Player player) {
+ player.setGameMode(GameMode.SPECTATOR);
+ // Add a delay to tp them to the gameWorld just in case they have a bed in another world
+ // Delay is needed because instant respawn takes 1t
+ Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(TumbleManager.getPlugin(), () -> {
+ player.teleport(gameSpawn);
+ Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(TumbleManager.getPlugin(), () -> {
+ player.setGameMode(GameMode.SPECTATOR);
+ }, 5);
+ }, 5);
+ // remove that player (who just died) from the roundPlayersArray, effectively eliminating them,
+ roundPlayers.remove(player);
+ // If there are less than 2 players in the game (1 just died),
+ if (roundPlayers.size() < 2) {
+ // End the game, passing the winner to the gameEnd method
+ roundEnd(roundPlayers.get(0));
+ }
+ }
+
+ // Methods to get the game type and game state for other classes outside the Game
+
+ /**
+ * @return The game's current state as a String ("waiting", "starting", "running", "complete")
+ * Can also be null if not initialized.
+ */
+ public String getGameState() { return gameState; }
+
+ /**
+ * @return The Bukkit process ID of the autostart process, if applicable
+ * Can also be null if not initialized, and -1 if the process failed to schedule.
+ */
+ public int getAutoStartID() { return autoStartID; }
+
+
+ // BEGIN PRIVATE METHODS
+
+ // Initialize Layers class
+ private final Layers layers = new Layers();
+ /**
+ * Generates the layers in the gameWorld for a certain gameType
+ * @param type can be either "shovels", "snowballs", or "mixed", anything else will fail generation
+ * @return true if gameType was recognized and layers were (hopefully) generated, false if unrecognized
+ */
+ private boolean generateLayers(String type) {
+ // Create a new Location for the layers to work with--this is so that we don't modify the actual gameSpawn var
+ Location layer = new Location(gameSpawn.getWorld(), gameSpawn.getX(), gameSpawn.getY(), gameSpawn.getZ(), gameSpawn.getYaw(), gameSpawn.getPitch());
+ if (Objects.equals(type, "shovels")) {
+ layer.setY(layer.getY() - 1);
+ // Choose a random type of generation; a circular layer, a square layer, or a multi-tiered layer of either variety
+ if (Random.nextInt(4) == 0) {
+ // Circular layer
+ Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.SNOW_BLOCK), layers.getMaterialList());
+ }
+ else if (Random.nextInt(4) == 1) {
+ // Square layer
+ Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.SNOW_BLOCK), layers.getMaterialList());
+ }
+ else if (Random.nextInt(4) == 2) {
+ // Multi-tiered circle
+ Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.SNOW_BLOCK), layers.getMaterialList());
+ Generator.generateLayer(layer, 13, 1, Material.AIR);
+ layer.setY(layer.getY() - 1);
+ Generator.generateClumps(Generator.generateLayer(layer, 13, 1, Material.GRASS_BLOCK), layers.getMaterialList());
+ Generator.generateLayer(layer, 4, 1, Material.AIR);
+ layer.setY(layer.getY() - 1);
+ Generator.generateClumps(Generator.generateLayer(layer, 4, 1, Material.PODZOL), layers.getMaterialList());
+ }
+ else {
+ // Multi-tiered square
+ Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.SNOW_BLOCK), layers.getMaterialList());
+ Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.AIR);
+ layer.setY(layer.getY() - 1);
+ Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.GRASS_BLOCK), layers.getMaterialList());
+ Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.AIR);
+ layer.setY(layer.getY() - 1);
+ Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.PODZOL), layers.getMaterialList());
+ }
+ ItemStack shovel = new ItemStack(Material.IRON_SHOVEL);
+ shovel.addEnchantment(Enchantment.SILK_TOUCH, 1);
+ if (Objects.equals(gameState, "running")) {
+ giveItems(TumbleManager.getPlayersInGame(), shovel);
+ }
+ else if (Objects.equals(gameState, "starting")) {
+ giveItems(TumbleManager.getPlayersInLobby(), shovel);
+ }
+ // Schedule a process to give snowballs after 2m30s (so people can't island, the OG game had this)
+ // Add 160t because of the countdown
+ gameID = Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(TumbleManager.getPlugin(), () -> {
+ clearInventories(gamePlayers);
+ giveItems(gamePlayers, new ItemStack(Material.SNOWBALL));
+ displayActionbar(gamePlayers, ChatColor.DARK_RED + "Showdown!");
+ playSound(gamePlayers, Sound.ENTITY_ELDER_GUARDIAN_CURSE, SoundCategory.HOSTILE, 1, 1);
+ // End the round in another 2m30s
+ gameID = Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(TumbleManager.getPlugin(), () -> {
+ roundEnd(null);
+ }, 3000);
+ }, 3160);
+ }
+ else if (Objects.equals(type, "snowballs")) {
+ layer.setY(layer.getY() - 1);
+ // Similar generation to shovels, except there are three layers
+ if (Random.nextInt(4) == 0) {
+ // Circular layer
+ Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.STONE), layers.getMaterialList());
+ layer.setY(layer.getY() - 6);
+ Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.STONE), layers.getMaterialList());
+ layer.setY(layer.getY() - 6);
+ Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.STONE), layers.getMaterialList());
+ }
+ else if (Random.nextInt(4) == 1) {
+ // Square layer
+ Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), layers.getMaterialList());
+ layer.setY(layer.getY() - 6);
+ Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), layers.getMaterialList());
+ layer.setY(layer.getY() - 6);
+ Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), layers.getMaterialList());
+ }
+ else if (Random.nextInt(4) == 2) {
+ // Multi-tiered circle
+ Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.STONE), layers.getMaterialList());
+ Generator.generateLayer(layer, 13, 1, Material.AIR);
+ layer.setY(layer.getY() - 1);
+ Generator.generateClumps(Generator.generateLayer(layer, 13, 1, Material.GRANITE), layers.getMaterialList());
+ Generator.generateLayer(layer, 4, 1, Material.AIR);
+ layer.setY(layer.getY() - 1);
+ Generator.generateClumps(Generator.generateLayer(layer, 4, 1, Material.LIME_GLAZED_TERRACOTTA), layers.getMaterialList());
+ layer.setY(layer.getY() - 6);
+
+ Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.STONE), layers.getMaterialList());
+ Generator.generateLayer(layer, 13, 1, Material.AIR);
+ layer.setY(layer.getY() - 1);
+ Generator.generateClumps(Generator.generateLayer(layer, 13, 1, Material.GRANITE), layers.getMaterialList());
+ Generator.generateLayer(layer, 4, 1, Material.AIR);
+ layer.setY(layer.getY() - 1);
+ Generator.generateClumps(Generator.generateLayer(layer, 4, 1, Material.LIME_GLAZED_TERRACOTTA), layers.getMaterialList());
+ layer.setY(layer.getY() - 6);
+
+ Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.STONE), layers.getMaterialList());
+ Generator.generateLayer(layer, 13, 1, Material.AIR);
+ layer.setY(layer.getY() - 1);
+ Generator.generateClumps(Generator.generateLayer(layer, 13, 1, Material.GRANITE), layers.getMaterialList());
+ Generator.generateLayer(layer, 4, 1, Material.AIR);
+ layer.setY(layer.getY() - 1);
+ Generator.generateClumps(Generator.generateLayer(layer, 4, 1, Material.LIME_GLAZED_TERRACOTTA), layers.getMaterialList());
+ }
+ else {
+ // Multi-tiered square
+ Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), layers.getMaterialList());
+ Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.AIR);
+ layer.setY(layer.getY() - 1);
+ Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.GRANITE), layers.getMaterialList());
+ Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.AIR);
+ layer.setY(layer.getY() - 1);
+ Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.LIME_GLAZED_TERRACOTTA), layers.getMaterialList());
+ layer.setY(layer.getY() - 6);
+
+ Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), layers.getMaterialList());
+ Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.AIR);
+ layer.setY(layer.getY() - 1);
+ Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.GRANITE), layers.getMaterialList());
+ Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.AIR);
+ layer.setY(layer.getY() - 1);
+ Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.LIME_GLAZED_TERRACOTTA), layers.getMaterialList());
+ layer.setY(layer.getY() - 6);
+
+ Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 17, layer.getY(), layer.getZ() - 17), new Location(layer.getWorld(), layer.getX() + 17, layer.getY(), layer.getZ() + 17), Material.STONE), layers.getMaterialList());
+ Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.AIR);
+ layer.setY(layer.getY() - 1);
+ Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 13, layer.getY(), layer.getZ() - 13), new Location(layer.getWorld(), layer.getX() + 13, layer.getY(), layer.getZ() + 13), Material.GRANITE), layers.getMaterialList());
+ Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.AIR);
+ layer.setY(layer.getY() - 1);
+ Generator.generateClumps(Generator.generateCuboid(new Location(layer.getWorld(), layer.getX() - 7, layer.getY(), layer.getZ() - 7), new Location(layer.getWorld(), layer.getX() + 7, layer.getY(), layer.getZ() + 7), Material.LIME_GLAZED_TERRACOTTA), layers.getMaterialList());
+ }
+ if (Objects.equals(gameState, "running")) {
+ giveItems(TumbleManager.getPlayersInGame(), new ItemStack(Material.SNOWBALL));
+ }
+ else if (Objects.equals(gameState, "starting")) {
+ giveItems(TumbleManager.getPlayersInLobby(), new ItemStack(Material.SNOWBALL));
+ }
+ // End the round in 5m
+ gameID = Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(TumbleManager.getPlugin(), () -> roundEnd(null), 6160);
+ }
+ else if (Objects.equals(type, "mixed")) {
+ // Randomly select either shovels or snowballs and re-run the method
+ if (Random.nextInt(2) == 0) {
+ generateLayers("shovels");
+ } else {
+ generateLayers("snowballs");
+ }
+ }
+ // Game type was invalid
+ else {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Distributes items to a provided list of players
+ * @param players The player list for which to distribute the items to
+ * @param itemStack The ItemStack to be distributed
+ */
+ private void giveItems(List<Player> players, ItemStack itemStack) {
+ for (Player aPlayer : players) {
+ // Get a singular player from the player list and give that player the specified item
+ aPlayer.getInventory().addItem(itemStack);
+ }
+ }
+
+ /**
+ * Clears the inventories of a provided player list
+ * @param players The player list for which to clear the inventories of
+ */
+ private void clearInventories(List<Player> players) {
+ for (Player aPlayer : players) {
+ aPlayer.getInventory().clear();
+ }
+ }
+
+ /**
+ * Sets the gamemodes of a provided list of players
+ * @param players The player list for which to set the gamemodes of
+ * @param gameMode The GameMode to set
+ */
+ private void setGamemode(List<Player> players, GameMode gameMode) {
+ for (Player aPlayer : players) {
+ // Get a singular player from the player list and set their gamemode to the specified gamemode
+ aPlayer.setGameMode(gameMode);
+ }
+ }
+
+ /**
+ * Displays a customized title to a provided list of players
+ * @param players The player list for which to show the titles to
+ * @param title The top title text
+ * @param subtitle The bottom title subtext (nullable)
+ * @param fadeIn The fadeIn duration (in ticks)
+ * @param stay The stay duration (in ticks)
+ * @param fadeOut The fadeOut duration (in ticks)
+ */
+ private void displayTitles(List<Player> players, String title, @Nullable String subtitle, int fadeIn, int stay, int fadeOut) {
+ for (Player aPlayer : players) {
+ // Get a singular player from the player list and display them the specified title
+ aPlayer.sendTitle(title, subtitle, fadeIn, stay, fadeOut);
+ }
+ }
+
+ /**
+ * Displays an actionbar message to a provided list of players
+ * @param players The player list for which to display the actionbar to
+ * @param message The provided message (String format)
+ */
+ private void displayActionbar(List<Player> players, String message) {
+ for (Player aPlayer : players) {
+ aPlayer.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(message));
+ }
+ }
+
+ /**
+ * Plays a sound to a provided list of players
+ * @param players The player list for which to play the sound to
+ * @param sound The sound to play
+ * @param category The category of the sound
+ * @param volume The volume of the sound
+ * @param pitch The pitch of the sound
+ */
+ private void playSound(@NotNull List<Player> players, @NotNull Sound sound, @NotNull SoundCategory category, float volume, float pitch) {
+ for (Player aPlayer : players) {
+ aPlayer.playSound(aPlayer, sound, category, volume, pitch);
+ }
+ }
+
+ /**
+ * Teleports a list of players to the specified scatter locations in the gameWorld
+ * @param players a List of Players to teleport
+ */
+ private void scatterPlayers(List<Player> players) {
+ // Get the coords of the game's spawn location
+ double x = gameSpawn.getX();
+ double y = gameSpawn.getY();
+ double z = gameSpawn.getZ();
+ // Create the scatter locations based off the game's spawn
+ List<Location> scatterLocations = new ArrayList<>(List.of(
+ new Location(gameWorld, (x - 14.5), y, (z + 0.5), -90, 0),
+ new Location(gameWorld, (x + 0.5), y, (z - 14.5), 0, 0),
+ new Location(gameWorld, (x + 15.5), y, (z + 0.5), 90, 0),
+ new Location(gameWorld, (x + 0.5), y, (z + 15.5), 180, 0),
+ new Location(gameWorld, (x - 10.5), y, (z - 10.5), -45, 0),
+ new Location(gameWorld, (x - 10.5), y, (z + 11.5), -135, 0),
+ new Location(gameWorld, (x + 11.5), y, (z - 10.5), 45, 0),
+ new Location(gameWorld, (x + 11.5), y, (z + 11.5), 135, 0)));
+ // Shuffle the list (randomize)
+ Collections.shuffle(scatterLocations);
+ // While there are still unteleported players from the list, teleport them
+ for (Player aPlayer : players) {
+ // Select a singular player and singular location from the lists and teleport that player
+ aPlayer.teleport(scatterLocations.get(0));
+ // Remove that location so multiple players won't get the same one
+ scatterLocations.remove(0);
+ }
+ }
+
+ private void roundEnd(@Nullable Player winner) {
+ // Cancel the tasks that auto-end the round
+ Bukkit.getServer().getScheduler().cancelTask(gameID);
+ // Clear old layers (as a fill command, this would be /fill ~-20 ~-20 ~-20 ~20 ~ ~20 relative to spawn)
+ Generator.generateCuboid(new Location(gameSpawn.getWorld(), gameSpawn.getX() - 20, gameSpawn.getY() - 20, gameSpawn.getZ() - 20), new Location(gameSpawn.getWorld(), gameSpawn.getX() + 20, gameSpawn.getY(), gameSpawn.getZ() + 20), Material.AIR);
+ playSound(gamePlayers, Sound.BLOCK_NOTE_BLOCK_PLING, SoundCategory.BLOCKS, 5, 0);
+ // Check if there was a definite winner or not
+ if (winner != null) {
+ // Set the wins of the player to their current # of wins + 1
+ gameWins.set(gamePlayers.indexOf(winner), (gameWins.get(gamePlayers.indexOf(winner)) + 1));
+ // If the player has three wins, they won the game, so initiate the gameEnd
+ if (gameWins.get(gamePlayers.indexOf(winner)) == 3) {
+ gameEnd(winner);
+ }
+ // If that player doesn't have three wins, nobody else does, so we need another round
+ else {
+ roundPlayers.get(0).setGameMode(GameMode.SPECTATOR);
+ roundPlayers.remove(0);
+ roundPlayers.addAll(gamePlayers);
+ clearInventories(gamePlayers);
+ displayTitles(gamePlayers, ChatColor.RED + "Round over!", ChatColor.GOLD + winner.getName() + " has won the round!", 5, 60, 5);
+ // Wait for player to respawn before completely l a g g i n g the server ._.
+ Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(TumbleManager.getPlugin(), () -> {
+ // Re-generate layers
+ generateLayers(gameType);
+ // Wait 5s (100t) for tp method
+ Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(TumbleManager.getPlugin(), () -> {
+ // Kill all items (pistons are weird)
+ for (Entity entity : gameWorld.getEntities()) {
+ if (entity instanceof Item) {
+ entity.remove();
+ }
+ }
+ // Re-scatter players
+ gameState = "starting";
+ scatterPlayers(gamePlayers);
+ playSound(gamePlayers, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, SoundCategory.NEUTRAL, 5, 1);
+ displayTitles(gamePlayers, ChatColor.DARK_GREEN + "3", null, 3, 10, 7);
+ Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(TumbleManager.getPlugin(), () -> {
+ playSound(gamePlayers, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, SoundCategory.NEUTRAL, 5, 1);
+ displayTitles(gamePlayers, ChatColor.YELLOW + "2", null, 3, 10, 7);
+ Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(TumbleManager.getPlugin(), () -> {
+ playSound(gamePlayers, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, SoundCategory.NEUTRAL, 5, 1);
+ displayTitles(gamePlayers, ChatColor.DARK_RED + "1", null, 3, 10, 7);
+ Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(TumbleManager.getPlugin(), () -> {
+ playSound(gamePlayers, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, SoundCategory.NEUTRAL, 5, 2);
+ displayTitles(gamePlayers, ChatColor.GREEN + "Go!", null, 1, 5, 1);
+ setGamemode(gamePlayers, GameMode.SURVIVAL);
+ gameState = "running";
+ }, 20);
+ }, 20);
+ }, 20);
+ }, 100);
+ }, 1);
+ }
+ }
+ else {
+ setGamemode(gamePlayers, GameMode.SPECTATOR);
+ roundPlayers.removeAll(roundPlayers);
+ roundPlayers.addAll(gamePlayers);
+ clearInventories(gamePlayers);
+ displayTitles(gamePlayers, ChatColor.RED + "Round over!", ChatColor.GOLD + "Draw!", 5, 60, 5);
+ // Wait for player to respawn before completely l a g g i n g the server ._.
+ Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(TumbleManager.getPlugin(), () -> {
+ // Re-generate layers
+ generateLayers(gameType);
+ // Wait 5s (100t) for tp method
+ Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(TumbleManager.getPlugin(), () -> {
+ // Kill all items (pistons are weird)
+ for (Entity entity : gameWorld.getEntities()) {
+ if (entity instanceof Item) {
+ entity.remove();
+ }
+ }
+ // Re-scatter players
+ gameState = "starting";
+ scatterPlayers(gamePlayers);
+ playSound(gamePlayers, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, SoundCategory.NEUTRAL, 5, 1);
+ displayTitles(gamePlayers, ChatColor.DARK_GREEN + "3", null, 3, 10, 7);
+ Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(TumbleManager.getPlugin(), () -> {
+ playSound(gamePlayers, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, SoundCategory.NEUTRAL, 5, 1);
+ displayTitles(gamePlayers, ChatColor.YELLOW + "2", null, 3, 10, 7);
+ Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(TumbleManager.getPlugin(), () -> {
+ playSound(gamePlayers, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, SoundCategory.NEUTRAL, 5, 1);
+ displayTitles(gamePlayers, ChatColor.DARK_RED + "1", null, 3, 10, 7);
+ Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(TumbleManager.getPlugin(), () -> {
+ playSound(gamePlayers, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, SoundCategory.NEUTRAL, 5, 2);
+ displayTitles(gamePlayers, ChatColor.GREEN + "Go!", null, 1, 5, 1);
+ setGamemode(gamePlayers, GameMode.SURVIVAL);
+ gameState = "running";
+ }, 20);
+ }, 20);
+ }, 20);
+ }, 100);
+ }, 1);
+ }
+ }
+
+ private void gameEnd(Player winner) {
+ winner.setGameMode(GameMode.SPECTATOR);
+ clearInventories(gamePlayers);
+ // Announce win
+ displayTitles(gamePlayers, ChatColor.RED + "Game over!", ChatColor.GOLD + winner.getName() + " has won the game!", 5, 60, 5);
+ displayActionbar(gamePlayers, ChatColor.BLUE + "Returning to lobby in ten seconds...");
+ // Wait 10s (200t), then
+ Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(TumbleManager.getPlugin(), () -> {
+ // First, check to see if there is a separate location to tp the winner to
+ if ((TumbleManager.getPlugin().getConfig().getDouble("winnerTeleport.x") != 0) && (TumbleManager.getPlugin().getConfig().getDouble("winnerTeleport.y") != 0) && (TumbleManager.getPlugin().getConfig().getDouble("winnerTeleport.z") != 0)) {
+ // Tp the winner to that location
+ winner.teleport(new Location(Bukkit.getWorld(TumbleManager.getLobbyWorld()), TumbleManager.getPlugin().getConfig().getDouble("winnerTeleport.x"), TumbleManager.getPlugin().getConfig().getDouble("winnerTeleport.y"), TumbleManager.getPlugin().getConfig().getDouble("winnerTeleport.z")));
+ // Remove the winner from the gamePlayers so they don't get double-tp'd
+ gamePlayers.remove(winner);
+ }
+ // Send all players back to lobby (spawn)
+ for (Player aPlayer : gamePlayers) {
+ aPlayer.teleport(Bukkit.getWorld(TumbleManager.getLobbyWorld()).getSpawnLocation());
+ }
+ }, 200);
+ gameState = "complete";
+ }
+}