diff options
author | Myles <43725835+MylesAndMore@users.noreply.github.com> | 2022-12-13 14:16:59 -0600 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-12-13 14:16:59 -0600 |
commit | 6739bbb14d03bd215f8c7d72dc14d961b6bc175e (patch) | |
tree | e2c680914445931fdfad35169eb17dd0404301f5 /src | |
parent | cea002dc786f7826a1a3faef26fb659e3d8e908e (diff) | |
parent | cbafd10bc90273a263d019faeccb356ead442eb1 (diff) | |
download | Tumble-6739bbb14d03bd215f8c7d72dc14d961b6bc175e.tar.gz Tumble-6739bbb14d03bd215f8c7d72dc14d961b6bc175e.tar.bz2 Tumble-6739bbb14d03bd215f8c7d72dc14d961b6bc175e.zip |
Merge pull request #1 from MylesAndMore/beta
merge beta to main for release
Diffstat (limited to '')
13 files changed, 1571 insertions, 144 deletions
diff --git a/src/main/java/com/MylesAndMore/tumble/EventListener.java b/src/main/java/com/MylesAndMore/tumble/EventListener.java index 6c20a80..5e46e87 100644 --- a/src/main/java/com/MylesAndMore/tumble/EventListener.java +++ b/src/main/java/com/MylesAndMore/tumble/EventListener.java @@ -1,37 +1,284 @@ package com.MylesAndMore.tumble; +import java.util.Objects; + import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.entity.Snowball; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerJoinEvent; -import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockDropItemEvent; +import org.bukkit.event.entity.*; +import org.bukkit.event.player.*; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.Vector; -public class EventListener implements Listener{ +/** + * Tumble event listener for all plugin and game-related events. + */ +public class EventListener implements Listener { @EventHandler - public void PlayerJoinEvent(PlayerJoinEvent event){ - // On a PlayerJoinEvent, check if the config is set to hide the join/leave messages - // If true, null out the join message (which just makes it so that there is no message) + public void PlayerJoinEvent(PlayerJoinEvent event) { + // On a PlayerJoinEvent, check if the config is set to hide the join/leave + // messages + // If true, null out the join message (which just makes it so that there is no + // message) // If false, nothing will happen, and the default message will display if (TumbleManager.getPlugin().getConfig().getBoolean("hideJoinLeaveMessages")) { event.setJoinMessage(null); } - // If the gameWorld and lobbyWorld is not null, then check - if (TumbleManager.getGameWorld() != null && TumbleManager.getLobbyWorld() != null) { - // if the player joining is in the game world, then - if (event.getPlayer().getWorld() == Bukkit.getWorld(TumbleManager.getGameWorld())) { - // send them back to the lobby. - event.getPlayer().teleport(Bukkit.getWorld(TumbleManager.getLobbyWorld()).getSpawnLocation()); + // Check if either of the worlds are not defined in config, if so, end + // This is to avoid NPEs and such + if (TumbleManager.getGameWorld() == null || TumbleManager.getLobbyWorld() == null) { + return; + } + // Check if the player joining is in the game world, if true then + if (event.getPlayer().getWorld() == Bukkit.getWorld(TumbleManager.getGameWorld())) { + // send them back to the lobby. + event.getPlayer().teleport(Bukkit.getWorld(TumbleManager.getLobbyWorld()).getSpawnLocation()); + } + // For auto-start function: check if the autoStart is enabled + if (TumbleManager.getPlugin().getConfig().getBoolean("autoStart.enabled")) { + // If so, check if the amount of players has been reached + if (TumbleManager.getPlayersInLobby().size() == TumbleManager.getPlugin().getConfig().getInt("autoStart.players")) { + // The autoStart should begin; pass this to the Game + Game.getGame().autoStart(); } } } @EventHandler - public void PlayerQuitEvent(PlayerQuitEvent event){ - // On a PlayerQuitEvent, check if the config is set to hide the join/leave messages - // If true, null out the quit message (which just makes it so that there is no message) + public void PlayerChangedWorldEvent(PlayerChangedWorldEvent event) { + if (TumbleManager.getGameWorld() == null || TumbleManager.getLobbyWorld() == null) { + return; + } + // Check if the player changed to the lobbyWorld, then + if (event.getPlayer().getWorld() == Bukkit.getWorld(TumbleManager.getLobbyWorld())) { + // run the autostart checks (commented above) + if (TumbleManager.getPlugin().getConfig().getBoolean("autoStart.enabled")) { + if (TumbleManager.getPlayersInLobby().size() == TumbleManager.getPlugin().getConfig().getInt("autoStart.players")) { + Game.getGame().autoStart(); + } + } + } + // also check if the player left to another world + else if (event.getFrom() == Bukkit.getWorld(TumbleManager.getLobbyWorld())) { + if (Objects.equals(Game.getGame().getGameState(), "waiting")) { + Game.getGame().cancelStart(); + } + } + } + + @EventHandler + public void PlayerQuitEvent(PlayerQuitEvent event) { + // On a PlayerQuitEvent, check if the config is set to hide the join/leave + // messages + // If true, null out the quit message (which just makes it so that there is no + // message) // If false, nothing will happen, and the default message will display if (TumbleManager.getPlugin().getConfig().getBoolean("hideJoinLeaveMessages")) { event.setQuitMessage(null); } + if (TumbleManager.getLobbyWorld() == null) { + return; + } + if (event.getPlayer().getWorld() == Bukkit.getWorld(TumbleManager.getLobbyWorld())) { + // Check if the game is in the process of autostarting + if (Objects.equals(Game.getGame().getGameState(), "waiting")) { + // Cancel the autostart + Game.getGame().cancelStart(); + } + } + } + + @EventHandler + public void PlayerDeathEvent(PlayerDeathEvent event) { + if (TumbleManager.getGameWorld() == null) { + return; + } + // On a PlayerDeathEvent, + // check to see if the player died in the gameWorld, + if (event.getEntity().getWorld() == Bukkit.getWorld(TumbleManager.getGameWorld())) { + // then pass this off to the Game + Game.getGame().playerDeath(event.getEntity()); + } + } + + @EventHandler + public void PlayerItemDamageEvent(PlayerItemDamageEvent event) { + if (TumbleManager.getGameWorld() == null) { + return; + } + // On an ItemDamageEvent + // check to see if the item was damaged in the gameWorld, + if (event.getPlayer().getWorld() == Bukkit.getWorld(TumbleManager.getGameWorld())) { + event.setCancelled(true); + } + } + + // private long lastTimeP; + @EventHandler + public void ProjectileLaunchEvent(ProjectileLaunchEvent event) { + if (TumbleManager.getGameWorld() == null) { + return; + } + // When a projectile is launched, + // check to see if projectile was thrown in the gameWorld. + if (event.getEntity().getWorld() == Bukkit.getWorld(TumbleManager.getGameWorld())) { + if (event.getEntity() instanceof Snowball) { + if (event.getEntity().getShooter() instanceof Player player) { + // Check to see if the last snowball was thrown less than 200ms ago, if so, don't allow another + // if ((System.currentTimeMillis() - lastTimeP) < 200) { event.setCancelled(true); } + // else { + // // Otherwise, continue with logic + // lastTimeP = System.currentTimeMillis(); + // // This prevents players from shooting snowballs before the game actually begins + if (Objects.equals(Game.getGame().getGameState(), "starting")) { + event.setCancelled(true); + } + else { + // This gives players a snowball when they've used one + Bukkit.getServer().getScheduler().runTask(TumbleManager.getPlugin(), () -> { + player.getInventory().addItem(new ItemStack(Material.SNOWBALL, 1)); + }); + } + // } + } + } + } + } + + @EventHandler + public void ProjectileHitEvent(ProjectileHitEvent event) { + if (TumbleManager.getGameWorld() == null) { + return; + } + // Weird stacktrace thing + else if (event.getHitBlock() == null) { + return; + } + // When a projectile hits + // check to see if the projectile hit in the gameWorld, + if (event.getHitBlock().getWorld() == Bukkit.getWorld(TumbleManager.getGameWorld())) { + // then check if the projectile was a snowball, + if (event.getEntity() instanceof Snowball) { + // then check if a player threw it, + if (event.getEntity().getShooter() instanceof Player shooterPlayer) { + // then check to see if it hit a player or a block + if (event.getHitBlock() != null) { + // if it was a block, check if that block is within the game area, + if (event.getHitBlock().getLocation().distanceSquared(Bukkit.getWorld(TumbleManager.getGameWorld()).getSpawnLocation()) < 579) { + // then remove that block. + event.getHitBlock().setType(Material.AIR); + } + } + else if (event.getHitEntity() != null) { + // if it was an entity, check if it hit a player, + if (event.getHitEntity() instanceof Player hitPlayer) { + // then cancel the knockback (has to be delayed by a tick for some reason) + Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(TumbleManager.getPlugin(), () -> { + hitPlayer.setVelocity(new Vector()); + }); + } + } + } + } + } + } + + @EventHandler + public void PlayerDropItemEvent(PlayerDropItemEvent event) { + if (TumbleManager.getGameWorld() == null) { + return; + } + // When an item is dropped, + // check if the item was dropped in the game world + if (event.getPlayer().getWorld() == Bukkit.getWorld((TumbleManager.getGameWorld()))) { + event.setCancelled(true); + } + } + + @EventHandler + public void PlayerMoveEvent(PlayerMoveEvent event) { + if (TumbleManager.getGameWorld() == null) { + return; + } + // On a PlayerMoveEvent, check if the game is starting + if (Objects.equals(Game.getGame().getGameState(), "starting")) { + // Cancel the event if the game is starting (so players can't move before the game starts) + event.setCancelled(true); + } + } + + @EventHandler + public void BlockDropItemEvent(BlockDropItemEvent event) { + if (TumbleManager.getGameWorld() == null) { + return; + } + // If a block was going to drop an item (ex. snow dropping snowballs) in the GameWorld, cancel it + if (event.getBlock().getWorld() == Bukkit.getWorld(TumbleManager.getGameWorld())) { + event.setCancelled(true); + } + } + + // private long lastTimeI; + @EventHandler + public void PlayerInteractEvent(PlayerInteractEvent event) { + if (TumbleManager.getGameWorld() == null) { + return; + } + // Check if a player was left clicking a block in the gameWorld + if (event.getAction() == Action.LEFT_CLICK_BLOCK) { + if (event.getClickedBlock().getWorld() == Bukkit.getWorld(TumbleManager.getGameWorld())) { + // Then check to see if the player interacted less than 150ms ago + // if ((System.currentTimeMillis() - lastTimeI) < 150) return; + // If not, set that block to air (break it) + // else { + // lastTimeI = System.currentTimeMillis(); + event.getClickedBlock().setType(Material.AIR); + // } + } + } + } + + @EventHandler + public void BlockBreakEvent(BlockBreakEvent event) { + if (TumbleManager.getGameWorld() == null) { + return; + } + // This just doesn't allow blocks to break in the gameWorld; the PlayerInteractEvent will take care of everything + // It just keeps client commonality w/ animations and stuff + if (event.getBlock().getWorld() == Bukkit.getWorld(TumbleManager.getGameWorld())) { + event.setCancelled(true); + } + } + + @EventHandler + public void FoodLevelChangeEvent(FoodLevelChangeEvent event) { + if (TumbleManager.getGameWorld() == null) { + return; + } + // When someone's food level changes, check if that happened in the gameWorld, then cancel it + if (event.getEntity().getWorld() == Bukkit.getWorld(TumbleManager.getGameWorld())) { + event.setCancelled(true); + } + } + + @EventHandler + public void EntityDamageEvent(EntityDamageEvent event) { + if (TumbleManager.getGameWorld() == null) { + return; + } + // Check to see if a player got damaged by another entity (player, snowball, etc) in the gameWorld, if so, cancel it + if (event.getEntity().getWorld() == Bukkit.getWorld(TumbleManager.getGameWorld())) { + if (event.getEntity() instanceof Player) { + if (event.getCause() == EntityDamageEvent.DamageCause.ENTITY_ATTACK || event.getCause() == EntityDamageEvent.DamageCause.ENTITY_SWEEP_ATTACK || event.getCause() == EntityDamageEvent.DamageCause.FALL) { + event.setCancelled(true); + } + } + } } } 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"; + } +} diff --git a/src/main/java/com/MylesAndMore/tumble/GameManager.java b/src/main/java/com/MylesAndMore/tumble/GameManager.java deleted file mode 100644 index a778c7d..0000000 --- a/src/main/java/com/MylesAndMore/tumble/GameManager.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.MylesAndMore.tumble; - -import com.MylesAndMore.tumble.api.Generator; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; - -import java.util.List; -import java.util.Objects; - -public class GameManager { - public static boolean createGame(String gameType) { - if (Objects.equals(gameType, "shovels")) { - // Generate layers - Location layer = Bukkit.getWorld(TumbleManager.getGameWorld()).getSpawnLocation(); - layer.setY(layer.getY() - 1); - Generator.generateLayer(layer, 17, 1, Material.SNOW_BLOCK); - Generator.generateLayer(layer, 13, 1, Material.AIR); - layer.setY(layer.getY() - 1); - Generator.generateLayer(layer, 13, 1, Material.GRASS_BLOCK); - layer.setY(layer.getY() - 1); - Generator.generateLayer(layer, 4, 1, Material.PODZOL); - layer.setY(layer.getY() + 1); - Generator.generateLayer(layer, 4, 2, Material.GRASS); - // Give players diamond shovels - giveItems(new ItemStack(Material.DIAMOND_SHOVEL)); - // Pass on the game type - - } - else if (Objects.equals(gameType, "snowballs")) { - // Generate three layers - // (Will make this customizable in later versions) - // Remember, the snowballs don't interact with players! - - // Give players infinite snowballs - - // Pass on the game type - } - else if (Objects.equals(gameType, "mixed")) { - // Randomly select rounds from above - - // Pass on the game type - } - else { - return false; - } - return true; - } - - public static void giveItems(ItemStack itemStack) { - for (List<Player> playersWithoutItem = TumbleManager.getPlayersInLobby(); playersWithoutItem.size() > 0; playersWithoutItem.remove(0)) { - // Get a singular player from the player list - Player playerWithoutItem = playersWithoutItem.get(0); - // Give that player the specified item - playerWithoutItem.getInventory().addItem(itemStack); - } - } -} diff --git a/src/main/java/com/MylesAndMore/tumble/Main.java b/src/main/java/com/MylesAndMore/tumble/Main.java index 513b7d1..73dedf5 100644 --- a/src/main/java/com/MylesAndMore/tumble/Main.java +++ b/src/main/java/com/MylesAndMore/tumble/Main.java @@ -1,9 +1,7 @@ package com.MylesAndMore.tumble; -import com.MylesAndMore.tumble.commands.ReloadCommand; +import com.MylesAndMore.tumble.commands.*; import com.MylesAndMore.tumble.api.Metrics; -import com.MylesAndMore.tumble.commands.SetWorldConfig; -import com.MylesAndMore.tumble.commands.StartGame; import org.bukkit.Bukkit; import org.bukkit.plugin.java.JavaPlugin; @@ -16,6 +14,8 @@ public class Main extends JavaPlugin{ this.getCommand("reload").setExecutor(new ReloadCommand()); this.getCommand("link").setExecutor(new SetWorldConfig()); this.getCommand("start").setExecutor(new StartGame()); + this.getCommand("winlocation").setExecutor(new SetWinnerLoc()); + this.getCommand("autostart").setExecutor(new SetAutoStart()); // Save the default config file (packaged in the JAR) this.saveDefaultConfig(); @@ -25,12 +25,15 @@ public class Main extends JavaPlugin{ // Check if worlds are null in config if (TumbleManager.getGameWorld() == null) { - Bukkit.getServer().getLogger().warning("It appears you have not configured a game world for Tumble."); - Bukkit.getServer().getLogger().info("If this is your first time running the plugin, you may disregard this message."); + Bukkit.getServer().getLogger().warning("[tumble] It appears you have not configured a game world for Tumble."); + Bukkit.getServer().getLogger().info("[tumble] If this is your first time running the plugin, you may disregard this message."); } if (TumbleManager.getLobbyWorld() == null) { - Bukkit.getServer().getLogger().warning("It appears you have not configured a lobby world for Tumble."); - Bukkit.getServer().getLogger().info("If this is your first time running the plugin, you may disregard this message."); + Bukkit.getServer().getLogger().warning("[tumble] It appears you have not configured a lobby world for Tumble."); + Bukkit.getServer().getLogger().info("[tumble] If this is your first time running the plugin, you may disregard this message."); } + + // Init message + Bukkit.getServer().getLogger().info("[tumble] Tumble successfully enabled!"); } }
\ No newline at end of file diff --git a/src/main/java/com/MylesAndMore/tumble/TumbleManager.java b/src/main/java/com/MylesAndMore/tumble/TumbleManager.java index 0b62494..43cc241 100644 --- a/src/main/java/com/MylesAndMore/tumble/TumbleManager.java +++ b/src/main/java/com/MylesAndMore/tumble/TumbleManager.java @@ -8,6 +8,9 @@ import org.bukkit.plugin.Plugin; import java.util.List; +/** + * Class to store long return methods to make writing this plugin slightly less painful. + */ public class TumbleManager { // Tumble plugin public static Plugin getPlugin() { @@ -18,6 +21,7 @@ public class TumbleManager { public static String getPermissionMessage() { return TumbleManager.getPlugin().getConfig().getString("permissionMessage"); } public static String getGameWorld() { return TumbleManager.getPlugin().getConfig().getString("gameWorld"); } public static String getLobbyWorld() { return TumbleManager.getPlugin().getConfig().getString("lobbyWorld"); } + public static String getGameType() { return TumbleManager.getPlugin().getConfig().getString("gameMode"); } public static List<Player> getPlayersInGame() { return Bukkit.getServer().getWorld(TumbleManager.getGameWorld()).getPlayers(); } public static List<Player> getPlayersInLobby() { return Bukkit.getServer().getWorld(TumbleManager.getLobbyWorld()).getPlayers(); } diff --git a/src/main/java/com/MylesAndMore/tumble/api/Generator.java b/src/main/java/com/MylesAndMore/tumble/api/Generator.java index 632627d..db8bacc 100644 --- a/src/main/java/com/MylesAndMore/tumble/api/Generator.java +++ b/src/main/java/com/MylesAndMore/tumble/api/Generator.java @@ -3,13 +3,34 @@ package com.MylesAndMore.tumble.api; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Random; + +/** + * This class holds the methods that generate blocks in-game such as cylinders, cubiods, and clump logic. + */ public class Generator { - public static void generateLayer(Location center, int radius, int height, Material material) { + /** + * Generates a layer (bascally just a cylinder) as best as it can w/ blocks + * + * @return A list of Blocks containing all the blocks it just changed + * + * @param center The center of the layer (Location) + * @param radius The whole number radius of the circle + * @param height The whole number height of the circle (1 for a flat layer) + * @param material The Material to use for generation + */ + public static List<Block> generateLayer(Location center, int radius, int height, Material material) { int Cx = center.getBlockX(); int Cy = center.getBlockY(); int Cz = center.getBlockZ(); World world = center.getWorld(); + List<Block> blocks = new ArrayList<>(); int rSq = radius * radius; @@ -17,11 +38,83 @@ public class Generator { for (int x = Cx - radius; x <= Cx + radius; x++) { for (int z = Cz - radius; z <= Cz + radius; z++) { if ((Cx - x) * (Cx - x) + (Cz - z) * (Cz - z) <= rSq) { - Location block = new Location(world, x, y, z); world.getBlockAt(x, y, z).setType(material); + blocks.add(world.getBlockAt(x, y, z)); } } } } + return blocks; + } + + /** + * Generates a cubiod (literally just a ripoff fill command) + * @param firstPos The first Location to fill (first three coords in a fill command) + * @param secondPos The second Location to fill to (second three coords) + * @param material The Material to fill + */ + public static List<Block> generateCuboid(Location firstPos, Location secondPos, Material material) { + World world = firstPos.getWorld(); + List<Block> blocks = new ArrayList<>(); + int fX = firstPos.getBlockX(); + int fY = firstPos.getBlockY(); + int fZ = firstPos.getBlockZ(); + int sX = secondPos.getBlockX(); + int sY = secondPos.getBlockY(); + int sZ = secondPos.getBlockZ(); + + for (int x = fX; x <= sX; x++) { + for (int y = fY; y <= sY; y++) { + for (int z = fZ; z <= sZ; z++) { + world.getBlockAt(x, y, z).setType(material); + blocks.add(world.getBlockAt(x, y, z)); + } + } + } + return blocks; + } + + /** + * Generates clumps in a pre-generated layer. + * @param blockList A list of block Locations that this method is allowed to edit + * @param materialList A list of Materials for the generator to randomly choose from. + * Keep in mind that not all Materials may be used, the amount used depends on the size of the layer. + * More Materials = more randomization + */ + public static void generateClumps(List<Block> blockList, List<Material> materialList) { + // Define random class + Random random = new Random(); + // Define new blocks list so we can manipulate it + List<Block> blocks = new ArrayList<>(blockList); + // Define new shuffled Materials list + List<Material> materials = new ArrayList<>(materialList); + Collections.shuffle(materials); + // This loop will run until there are no blocks left to change + while (blocks.size() > 0) { + // Get a random Material from the provided materials list + Material randomMaterial = materials.get(random.nextInt(materials.size())); + // Gets the first Block from the list, to modify + Block aBlock = blocks.get(0); + // Modifies the block + aBlock.setType(randomMaterial); + // Get the blocks around that and change it to that same material + if (blocks.contains(aBlock.getRelative(BlockFace.NORTH))) { + aBlock.getRelative(BlockFace.NORTH).setType(randomMaterial); + blocks.remove(aBlock.getRelative(BlockFace.NORTH)); + } + if (blocks.contains(aBlock.getRelative(BlockFace.SOUTH))) { + aBlock.getRelative(BlockFace.SOUTH).setType(randomMaterial); + blocks.remove(aBlock.getRelative(BlockFace.SOUTH)); + } + if (blocks.contains(aBlock.getRelative(BlockFace.EAST))) { + aBlock.getRelative(BlockFace.EAST).setType(randomMaterial); + blocks.remove(aBlock.getRelative(BlockFace.EAST)); + } + if (blocks.contains(aBlock.getRelative(BlockFace.WEST))) { + aBlock.getRelative(BlockFace.WEST).setType(randomMaterial); + blocks.remove(aBlock.getRelative(BlockFace.WEST)); + } + blocks.remove(aBlock); + } } } diff --git a/src/main/java/com/MylesAndMore/tumble/api/Layers.java b/src/main/java/com/MylesAndMore/tumble/api/Layers.java new file mode 100644 index 0000000..8818631 --- /dev/null +++ b/src/main/java/com/MylesAndMore/tumble/api/Layers.java @@ -0,0 +1,300 @@ +package com.MylesAndMore.tumble.api; + +import org.bukkit.Material; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +/** + * This class is dedicated to storing the different types of layers that can be generated. + */ +public class Layers { + + public Layers(){ + // Make the other layers more common than the glass layer + for (int i = 0; i < 10; i++) { + matList.add(gen0); + matList.add(gen1); + matList.add(gen2); + matList.add(gen3); + matList.add(gen4); + matList.add(gen5); + matList.add(gen6); + matList.add(gen7); + matList.add(gen8); + matList.add(gen9); + matList.add(gen10); + matList.add(gen12); + matList.add(gen15); + matList.add(gen16); + } + // Glass layer + matList.add(gen11); + } + + // Define Random class + Random random = new Random(); + /** + * @return A random predefined List of Materials that are okay to use in the clump generator + */ + public List<Material> getMaterialList() { + return matList.get(random.nextInt(matList.size())); + } + + // Define the list that will store the material lists + private final List<List<Material>> matList = new ArrayList<>(); + + + // Begin lists + + // private final List<Material> gen = new ArrayList<>() {{ + // add(Material. + // }}; + + private final List<Material> gen0 = new ArrayList<>() {{ + add(Material.COAL_ORE); + add(Material.COAL_ORE); + add(Material.COAL_ORE); + add(Material.COAL_ORE); + add(Material.COAL_ORE); + add(Material.IRON_ORE); + add(Material.REDSTONE_ORE); + add(Material.EMERALD_ORE); + add(Material.GOLD_ORE); + add(Material.LAPIS_ORE); + add(Material.DIAMOND_ORE); + add(Material.GRASS_BLOCK); + add(Material.GRASS_BLOCK); + add(Material.GRASS_BLOCK); + add(Material.GRASS_BLOCK); + // add(Material.COBWEB); + }}; + + private final List<Material> gen1 = new ArrayList<>() {{ + add(Material.YELLOW_GLAZED_TERRACOTTA); + add(Material.LIGHT_BLUE_GLAZED_TERRACOTTA); + add(Material.GRAY_GLAZED_TERRACOTTA); + add(Material.PODZOL); + add(Material.PODZOL); + add(Material.PODZOL); + add(Material.ORANGE_GLAZED_TERRACOTTA); + }}; + + private final List<Material> gen2 = new ArrayList<>() {{ + add(Material.PINK_TERRACOTTA); + add(Material.PURPLE_TERRACOTTA); + add(Material.GRAY_TERRACOTTA); + add(Material.BLUE_TERRACOTTA); + add(Material.LIGHT_BLUE_TERRACOTTA); + add(Material.WHITE_TERRACOTTA); + add(Material.BROWN_TERRACOTTA); + add(Material.GREEN_TERRACOTTA); + add(Material.YELLOW_TERRACOTTA); + add(Material.PINK_TERRACOTTA); + add(Material.PURPLE_TERRACOTTA); + add(Material.GRAY_TERRACOTTA); + add(Material.BLUE_TERRACOTTA); + add(Material.LIGHT_BLUE_TERRACOTTA); + add(Material.WHITE_TERRACOTTA); + add(Material.BROWN_TERRACOTTA); + add(Material.GREEN_TERRACOTTA); + add(Material.YELLOW_TERRACOTTA); + add(Material.WHITE_STAINED_GLASS); + add(Material.HONEYCOMB_BLOCK); + add(Material.HONEYCOMB_BLOCK); + }}; + + private final List<Material> gen3 = new ArrayList<>() {{ + add(Material.PACKED_ICE); + add(Material.PACKED_ICE); + add(Material.NOTE_BLOCK); + add(Material.TNT); + add(Material.LIGHT_BLUE_CONCRETE); + add(Material.GLASS); + add(Material.PACKED_ICE); + add(Material.PACKED_ICE); + add(Material.NOTE_BLOCK); + add(Material.TNT); + add(Material.LIGHT_BLUE_CONCRETE); + add(Material.GLASS); + add(Material.SOUL_SAND); + }}; + + private final List<Material> gen4 = new ArrayList<>() {{ + add(Material.DIAMOND_BLOCK); + add(Material.GOLD_BLOCK); + add(Material.REDSTONE_BLOCK); + add(Material.REDSTONE_BLOCK); + add(Material.LAPIS_BLOCK); + add(Material.LAPIS_BLOCK); + add(Material.IRON_BLOCK); + add(Material.COAL_BLOCK); + add(Material.IRON_BLOCK); + add(Material.COAL_BLOCK); + add(Material.IRON_BLOCK); + add(Material.COAL_BLOCK); + add(Material.COAL_BLOCK); + }}; + + private final List<Material> gen5 = new ArrayList<>() {{ + add(Material.WHITE_TERRACOTTA); + add(Material.BLUE_ICE); + add(Material.SOUL_SAND); + add(Material.STONE_SLAB); + add(Material.WHITE_TERRACOTTA); + add(Material.BLUE_ICE); + add(Material.SOUL_SAND); + add(Material.STONE_SLAB); + add(Material.WHITE_TERRACOTTA); + add(Material.BLUE_ICE); + add(Material.SOUL_SAND); + add(Material.STONE_SLAB); + add(Material.GLOWSTONE); + add(Material.GLOWSTONE); + add(Material.HONEY_BLOCK); + add(Material.SLIME_BLOCK); + }}; + + private final List<Material> gen6 = new ArrayList<>() {{ + add(Material.NETHERRACK); + add(Material.NETHERRACK); + add(Material.NETHERRACK); + add(Material.NETHER_BRICKS); + add(Material.NETHER_BRICKS); + add(Material.NETHERRACK); + add(Material.NETHERRACK); + add(Material.NETHERRACK); + add(Material.NETHER_BRICKS); + add(Material.NETHER_BRICKS); + add(Material.NETHER_GOLD_ORE); + add(Material.NETHER_GOLD_ORE); + add(Material.CRIMSON_NYLIUM); + add(Material.WARPED_NYLIUM); + add(Material.SOUL_SOIL); + add(Material.CRACKED_NETHER_BRICKS); + add(Material.RED_NETHER_BRICKS); + add(Material.NETHER_WART_BLOCK); + add(Material.CRYING_OBSIDIAN); + add(Material.MAGMA_BLOCK); + }}; + + private final List<Material> gen7 = new ArrayList<>() {{ + add(Material.END_STONE); + add(Material.END_STONE_BRICKS); + add(Material.END_STONE); + add(Material.END_STONE_BRICKS); + add(Material.END_STONE); + add(Material.END_STONE_BRICKS); + add(Material.END_STONE); + add(Material.END_STONE_BRICKS); + add(Material.OBSIDIAN); + add(Material.PURPUR_BLOCK); + add(Material.PURPUR_PILLAR); + add(Material.COBBLESTONE); + }}; + + private final List<Material> gen8 = new ArrayList<>() {{ + add(Material.REDSTONE_BLOCK); + add(Material.REDSTONE_BLOCK); + add(Material.REDSTONE_LAMP); + add(Material.TARGET); + add(Material.DAYLIGHT_DETECTOR); + add(Material.PISTON); + add(Material.STICKY_PISTON); + add(Material.SLIME_BLOCK); + add(Material.OBSERVER); + add(Material.HOPPER); + }}; + + private final List<Material> gen9 = new ArrayList<>() {{ + add(Material.PRISMARINE); + add(Material.DARK_PRISMARINE); + add(Material.BLUE_STAINED_GLASS); + add(Material.WET_SPONGE); + add(Material.PRISMARINE_BRICKS); + add(Material.PRISMARINE_BRICK_SLAB); + add(Material.DARK_PRISMARINE); + add(Material.SEA_LANTERN); + add(Material.TUBE_CORAL_BLOCK); + add(Material.BRAIN_CORAL_BLOCK); + add(Material.BUBBLE_CORAL_BLOCK); + }}; + + private final List<Material> gen10 = new ArrayList<>() {{ + add(Material.OAK_LOG); + add(Material.SPRUCE_LOG); + add(Material.ACACIA_LOG); + add(Material.STRIPPED_OAK_LOG); + add(Material.STRIPPED_SPRUCE_LOG); + add(Material.STRIPPED_ACACIA_LOG); + add(Material.OAK_WOOD); + add(Material.SPRUCE_WOOD); + add(Material.ACACIA_WOOD); + add(Material.OAK_LEAVES); + add(Material.SPRUCE_LEAVES); + add(Material.ACACIA_LEAVES); + add(Material.OAK_LEAVES); + add(Material.SPRUCE_LEAVES); + add(Material.ACACIA_LEAVES); + }}; + + private final List<Material> gen11 = new ArrayList<>() {{ + add(Material.GLASS); + add(Material.GLASS); + add(Material.GLASS); + add(Material.GLASS); + add(Material.GLASS); + add(Material.GLASS); + add(Material.GLASS); + add(Material.GLASS); + add(Material.GLASS); + add(Material.GLASS); + add(Material.GLASS); + add(Material.GLASS); + add(Material.GLASS); + add(Material.GLASS); + add(Material.GLASS); + add(Material.GLASS); + add(Material.GLASS); + add(Material.GLASS); + add(Material.GLASS); + add(Material.GLASS); + add(Material.GLASS); + add(Material.GLASS); + add(Material.WHITE_STAINED_GLASS); + }}; + + private final List<Material> gen12 = new ArrayList<>() {{ + add(Material.DIRT); + add(Material.DIRT_PATH); + add(Material.GRASS_BLOCK); + add(Material.OAK_SLAB); + add(Material.BRICK_WALL); + add(Material.BRICK_STAIRS); + }}; + + private final List<Material> gen15 = new ArrayList<>() {{ + add(Material.SANDSTONE); + add(Material.SANDSTONE_SLAB); + add(Material.RED_SANDSTONE); + add(Material.RED_SANDSTONE_SLAB); + add(Material.RED_TERRACOTTA); + add(Material.TERRACOTTA); + add(Material.YELLOW_TERRACOTTA); + }}; + + private final List<Material> gen16 = new ArrayList<>() {{ + add(Material.JUNGLE_LOG); + add(Material.STRIPPED_JUNGLE_LOG); + add(Material.JUNGLE_WOOD); + add(Material.STRIPPED_JUNGLE_WOOD); + add(Material.MOSSY_COBBLESTONE); + add(Material.MOSSY_COBBLESTONE); + add(Material.MOSSY_COBBLESTONE); + add(Material.JUNGLE_LEAVES); + add(Material.JUNGLE_SLAB); + add(Material.JUNGLE_TRAPDOOR); + }}; + +} diff --git a/src/main/java/com/MylesAndMore/tumble/commands/SetAutoStart.java b/src/main/java/com/MylesAndMore/tumble/commands/SetAutoStart.java new file mode 100644 index 0000000..4b97d9a --- /dev/null +++ b/src/main/java/com/MylesAndMore/tumble/commands/SetAutoStart.java @@ -0,0 +1,97 @@ +package com.MylesAndMore.tumble.commands; + +import com.MylesAndMore.tumble.TumbleManager; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; + +import java.util.Objects; + +public class SetAutoStart implements CommandExecutor{ + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + // Check if sender has perms to run command + if (sender.hasPermission("autostart")) { + // Check if game and lobby worlds are null + if (TumbleManager.getGameWorld() != null) { + if (TumbleManager.getLobbyWorld() != null) { + // Check the amount of args entered + if (args.length == 2) { + // Check the player # argument and parse it into an int + int args0; + try { + args0 = Integer.parseInt(args[0]); + } catch (NumberFormatException nfe){ + sender.sendMessage(ChatColor.RED + "Player amount must be a valid number."); + return true; + } catch (Exception e){ + sender.sendMessage(ChatColor.RED + "Invalid player amount."); + return true; + } + // PlayerAmount & enable/disable were entered + // Check if a playerAmount between 2-8 was entered + if ((args0 >= 2) && (args0 <= 8)) { + if (Objects.equals(args[1], "enable")) { + // Write values to the config + TumbleManager.getPlugin().getConfig().set("autoStart.players", args0); + TumbleManager.getPlugin().getConfig().set("autoStart.enabled", true); + TumbleManager.getPlugin().saveConfig(); + sender.sendMessage(ChatColor.GREEN + "Configuration saved!"); + sender.sendMessage(ChatColor.GREEN + "Run " + ChatColor.GRAY + "/tumble:reload " + ChatColor.GREEN + "the changes to take effect."); + } + else if (Objects.equals(args[1], "disable")) { + TumbleManager.getPlugin().getConfig().set("autoStart.players", args0); + TumbleManager.getPlugin().getConfig().set("autoStart.enabled", false); + TumbleManager.getPlugin().saveConfig(); + sender.sendMessage(ChatColor.GREEN + "Configuration saved!"); + sender.sendMessage(ChatColor.GREEN + "Run " + ChatColor.GRAY + "/tumble:reload " + ChatColor.GREEN + "the changes to take effect."); + } + else { + return false; + } + } + else { + sender.sendMessage(ChatColor.RED + "Please enter a player amount between two and eight!"); + } + } + else if (args.length == 1) { + // Only PlayerAmount was entered + int args0; + try { + args0 = Integer.parseInt(args[0]); + } catch (NumberFormatException nfe){ + sender.sendMessage(ChatColor.RED + "Player amount must be a valid number."); + return true; + } catch (Exception e){ + sender.sendMessage(ChatColor.RED + "Invalid player amount."); + return true; + } + if ((args0 >= 2) && (args0 <= 8)) { + TumbleManager.getPlugin().getConfig().set("autoStart.players", args0); + TumbleManager.getPlugin().saveConfig(); + sender.sendMessage(ChatColor.GREEN + "Configuration saved!"); + sender.sendMessage(ChatColor.GREEN + "Run " + ChatColor.GRAY + "/tumble:reload " + ChatColor.GREEN + "the changes to take effect."); + } + else { + sender.sendMessage(ChatColor.RED + "Please enter a player amount between two and eight!"); + } + } + else { + return false; + } + } + else { + sender.sendMessage(ChatColor.RED + "Please link a lobby world first!"); + } + } + else { + sender.sendMessage(ChatColor.RED + "Please link a game world first!"); + } + } + else { + sender.sendMessage(ChatColor.RED + TumbleManager.getPermissionMessage()); + } + return true; + } +} diff --git a/src/main/java/com/MylesAndMore/tumble/commands/SetWinnerLoc.java b/src/main/java/com/MylesAndMore/tumble/commands/SetWinnerLoc.java new file mode 100644 index 0000000..ec145d1 --- /dev/null +++ b/src/main/java/com/MylesAndMore/tumble/commands/SetWinnerLoc.java @@ -0,0 +1,115 @@ +package com.MylesAndMore.tumble.commands; + +import com.MylesAndMore.tumble.TumbleManager; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.entity.Player; + +public class SetWinnerLoc implements CommandExecutor { + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + // Check if sender has perms to run command + if (sender.hasPermission("tumble.winlocation")) { + // Check if the lobby world has been configured + if (TumbleManager.getLobbyWorld() != null) { + // Check if the sender is a player + if (sender instanceof Player) { + // Check the sender entered the correct number of args + if (args.length == 3) { + double args0 = 0; + double args1 = 0; + double args2 = 0; + try { + args0 = Double.parseDouble(args[0]); + args1 = Double.parseDouble(args[1]); + args2 = Double.parseDouble(args[2]); + } catch (NumberFormatException nfe){ + sender.sendMessage(ChatColor.RED + "Input arguments must be valid numbers."); + } catch (Exception e){ + sender.sendMessage(ChatColor.RED + "Invalid input arguments."); + } + // Check if any of the args were 0 (this will cause future problems so we prevent it here) + if (!((args0 == 0) || (args1 == 0) || (args2 == 0))) { + TumbleManager.getPlugin().getConfig().set("winnerTeleport.x", args0); + TumbleManager.getPlugin().getConfig().set("winnerTeleport.y", args1); + TumbleManager.getPlugin().getConfig().set("winnerTeleport.z", args2); + TumbleManager.getPlugin().saveConfig(); + sender.sendMessage(ChatColor.GREEN + "Win location successfully set!"); + sender.sendMessage(ChatColor.GREEN + "Run " + ChatColor.GRAY + "/tumble:reload " + ChatColor.GREEN + "the changes to take effect."); + } + else { + sender.sendMessage(ChatColor.RED + "Your coordinates cannot be zero!"); + sender.sendMessage(ChatColor.RED + "Use something like 0.5 (the middle of the block) instead."); + } + } + // If the sender entered no args, use their current location + else if (args.length == 0) { + Location senderPos = ((Player) sender).getLocation(); + // if so, check if any of their locations are zero + if (!((senderPos.getX() == 0) || (senderPos.getY() == 0) || (senderPos.getZ() == 0))) { + // set the config values to their current pos + TumbleManager.getPlugin().getConfig().set("winnerTeleport.x", senderPos.getX()); + TumbleManager.getPlugin().getConfig().set("winnerTeleport.y", senderPos.getY()); + TumbleManager.getPlugin().getConfig().set("winnerTeleport.z", senderPos.getZ()); + TumbleManager.getPlugin().saveConfig(); + sender.sendMessage(ChatColor.GREEN + "Win location successfully set!"); + sender.sendMessage(ChatColor.GREEN + "Run " + ChatColor.GRAY + "/tumble:reload " + ChatColor.GREEN + "the changes to take effect."); + } + else { + sender.sendMessage(ChatColor.RED + "Your coordinates cannot be zero!"); + sender.sendMessage(ChatColor.RED + "Use something like 0.5 (the middle of the block) instead."); + } + } + else { + return false; + } + } + // Check if the sender is the console + else if (sender instanceof ConsoleCommandSender) { + // Check if the correct # of args were entered + if (args.length == 3) { + double args0 = 0; + double args1 = 0; + double args2 = 0; + try { + args0 = Double.parseDouble(args[0]); + args1 = Double.parseDouble(args[1]); + args2 = Double.parseDouble(args[2]); + } catch (NumberFormatException nfe){ + sender.sendMessage(ChatColor.RED + "Input arguments must be valid numbers."); + } catch (Exception e){ + sender.sendMessage(ChatColor.RED + "Invalid input arguments."); + } + // Check if any of the args were 0 (this will cause future problems so we prevent it here) + if (!((args0 == 0) || (args1 == 0) || (args2 == 0))) { + TumbleManager.getPlugin().getConfig().set("winnerTeleport.x", args0); + TumbleManager.getPlugin().getConfig().set("winnerTeleport.y", args1); + TumbleManager.getPlugin().getConfig().set("winnerTeleport.z", args2); + TumbleManager.getPlugin().saveConfig(); + sender.sendMessage(ChatColor.GREEN + "Win location successfully set!"); + sender.sendMessage(ChatColor.GREEN + "Run " + ChatColor.GRAY + "/tumble:reload " + ChatColor.GREEN + "the changes to take effect."); + } + else { + sender.sendMessage(ChatColor.RED + "Your coordinates cannot be zero!"); + sender.sendMessage(ChatColor.RED + "Use something like 0.5 (the middle of the block) instead."); + } + } + else { + return false; + } + } + } + else { + sender.sendMessage(ChatColor.RED + "Please link a lobby world first!"); + } + } + else { + sender.sendMessage(ChatColor.RED + TumbleManager.getPermissionMessage()); + } + return true; + } +} diff --git a/src/main/java/com/MylesAndMore/tumble/commands/SetWorldConfig.java b/src/main/java/com/MylesAndMore/tumble/commands/SetWorldConfig.java index d27a5a8..695c248 100644 --- a/src/main/java/com/MylesAndMore/tumble/commands/SetWorldConfig.java +++ b/src/main/java/com/MylesAndMore/tumble/commands/SetWorldConfig.java @@ -3,6 +3,7 @@ package com.MylesAndMore.tumble.commands; import com.MylesAndMore.tumble.TumbleManager; import org.bukkit.Bukkit; import org.bukkit.ChatColor; +import org.bukkit.GameRule; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; @@ -49,6 +50,9 @@ public class SetWorldConfig implements CommandExecutor { if (!Objects.equals(TumbleManager.getLobbyWorld(), world)) { TumbleManager.getPlugin().getConfig().set("gameWorld", world); TumbleManager.getPlugin().saveConfig(); + // Set the gamerule of doImmediateRespawn in the gameWorld for later + Bukkit.getWorld(world).setGameRule(GameRule.DO_IMMEDIATE_RESPAWN, true); + Bukkit.getWorld(world).setGameRule(GameRule.KEEP_INVENTORY, true); sender.sendMessage(ChatColor.GREEN + "Game world successfully linked: " + ChatColor.GRAY + world); sender.sendMessage(ChatColor.GREEN + "Please restart your server for the changes to take effect; " + ChatColor.RED + "reloading the plugin is insufficient!"); } diff --git a/src/main/java/com/MylesAndMore/tumble/commands/StartGame.java b/src/main/java/com/MylesAndMore/tumble/commands/StartGame.java index 9daa959..c138cda 100644 --- a/src/main/java/com/MylesAndMore/tumble/commands/StartGame.java +++ b/src/main/java/com/MylesAndMore/tumble/commands/StartGame.java @@ -1,19 +1,17 @@ package com.MylesAndMore.tumble.commands; -import com.MylesAndMore.tumble.GameManager; +import com.MylesAndMore.tumble.Game; import com.MylesAndMore.tumble.TumbleManager; -import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import java.util.List; +import java.util.Objects; public class StartGame implements CommandExecutor { - // Define the startGame method so that other classes can refrence it - public void startGame(CommandSender sender, String[] args) { + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { // Check if sender has perms to run command if (sender.hasPermission("tumble.start")) { // Check if there is a lobbyWorld specified in config @@ -22,28 +20,54 @@ public class StartGame implements CommandExecutor { if (TumbleManager.getPlayersInLobby().size() > 1) { // Check if there is a gameWorld specified in config if (TumbleManager.getGameWorld() != null) { - sender.sendMessage("Checking world, this could take a few moments..."); - // Use multiverse to load game world - // If the load was successful, start game - if (TumbleManager.getMVWorldManager().loadWorld(TumbleManager.getGameWorld())) { - sender.sendMessage("Generating layers..."); - // Check which gamemode to initiate from the config file - if (GameManager.createGame(TumbleManager.getPlugin().getConfig().getString("gameMode"))) { - // If game type exists, send players to the world - // At this point, layers have been generated, and items have been allotted from the createGame method - sendWorld(); + // Check if a game is already pending to start + if (!Objects.equals(Game.getGame().getGameState(), "waiting")) { + sender.sendMessage(ChatColor.BLUE + "Generating layers, please wait."); + // Use multiverse to load game world + // If the load was successful, start game + if (TumbleManager.getMVWorldManager().loadWorld(TumbleManager.getGameWorld())) { + // If there is no starting argument, + if (args.length == 0) { + // pull which gamemode to initiate from the config file + if (!Game.getGame().startGame(TumbleManager.getGameType())) { + // Sender feedback for if the game failed to start + if (Objects.equals(Game.getGame().getGameState(), "starting")) { + sender.sendMessage(ChatColor.RED + "A game is already starting!"); + } + else if (Objects.equals(Game.getGame().getGameState(), "running")) { + sender.sendMessage(ChatColor.RED + "A game is already running!"); + } + else { + sender.sendMessage(ChatColor.RED + "Failed to recognize game of type " + ChatColor.GRAY + TumbleManager.getPlugin().getConfig().getString("gameMode")); + } + } + } + // If there was an argument for gameType, pass that into the startGame method + else { + if (!Game.getGame().startGame(args[0])) { + // Sender feedback for if the game failed to start + if (Objects.equals(Game.getGame().getGameState(), "starting")) { + sender.sendMessage(ChatColor.RED + "A game is already starting!"); + } + else if (Objects.equals(Game.getGame().getGameState(), "running")) { + sender.sendMessage(ChatColor.RED + "A game is already running!"); + } + else { + sender.sendMessage(ChatColor.RED + "Failed to recognize game of type " + ChatColor.GRAY + args[0]); + } + } + } } + // If load was unsuccessful, give feedback + // Note: this should not occur unless the config file was edited externally, + // because the plugin prevents adding "worlds" that are not actually present to the config. else { - // If game type does not exist, give sender feedback - sender.sendMessage(ChatColor.RED + "Failed to recognize game of type " + ChatColor.GRAY + TumbleManager.getPlugin().getConfig().getString("gameMode")); + sender.sendMessage(ChatColor.RED + "Failed to find a world named " + ChatColor.GRAY + TumbleManager.getGameWorld()); + sender.sendMessage(ChatColor.RED + "Is the configuration file correct?"); } } - // If load was unsuccessful, give feedback - // Note: this should not occur unless the config file was edited externally, - // because the plugin prevents adding "worlds" that are not actually present to the config. else { - sender.sendMessage(ChatColor.RED + "Failed to find a world named " + ChatColor.GRAY + TumbleManager.getGameWorld()); - sender.sendMessage(ChatColor.RED + "Is the configuration file correct?"); + sender.sendMessage(ChatColor.RED + "A game is already queued to begin!"); } } // Feedback for if there is no gameWorld in the config @@ -64,29 +88,6 @@ public class StartGame implements CommandExecutor { else { sender.sendMessage(ChatColor.RED + TumbleManager.getPermissionMessage()); } - } - - public void sendWorld() { - // Create Locations to scatter players around the first layer - - // While there are still players in the lobby, send them to the gameWorld - // This is just a way of sending everybody in the lobby to the game - for (List<Player> playersInLobby = TumbleManager.getPlayersInLobby(); playersInLobby.size() > 0; playersInLobby = TumbleManager.getPlayersInLobby()) { - // Get a singular player from the player list - Player aPlayer = playersInLobby.get(0); - // Teleport that player to the spawn of the gameWorld - aPlayer.teleport(Bukkit.getWorld(TumbleManager.getGameWorld()).getSpawnLocation()); - } - - // Add a little break because it can take the clients a bit to load into the new world - // Then, transition to another method because this one is getting really long - // In that method: set a flag to monitor the playerDeathEvent so we know when all the players have died - // Also start music - } - - @Override - public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { - startGame(sender, args); return true; } } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index b2ba268..ee2a2c7 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,18 +1,32 @@ +# Customize the default game mode of Tumble +# Acceptable options include: shovels, snowballs, mixed +# Default is mixed +gameMode: mixed + # Hides join/leave messages in public chat -# Default is true -hideJoinLeaveMessages: true +# Default is false +hideJoinLeaveMessages: false # Customize the message that displays when the player does not have permission to execute a command from this plugin permissionMessage: You do not have permission to perform this command! -# Customize the game mode of Tumble -# Acceptable options include: shovels, snowballs, mixed -# Default is shovels -gameMode: shovels +# Customize the auto start feature of Tumble +# Defaults are false and two players +# Players can be up to 8 +autoStart: + enabled: false + players: 2 + +# Customize the place that the winner is teleported after a game ends +# Keep in mind that these coordinates cannot be zero! The teleport will fail if any of them are; use something like 0.5 instead. +# These are optional values--defaults are nothing +winnerTeleport: + x: + y: + z: # This tells the plugin which worlds it should use as the lobby/game worlds # Do NOT change unless you know what you're doing!! # Will be blank by default -lobbyWorld: -# Game world -gameWorld:
\ No newline at end of file +lobbyWorld: +gameWorld:
\ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index dd24bcc..c78eece 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,7 +1,7 @@ main: com.MylesAndMore.tumble.Main name: tumble -version: 0.0.1-SNAPSHOT -description: 'A Minecraft: Java Edition plugin recreating the Tumble minigame from Minecraft: Xbox 360 Edition.' +version: 1.0.0 +description: 'A Minecraft: Java Edition plugin recreating the Tumble minigame from Minecraft Legacy Console Edition.' api-version: 1.19 load: STARTUP author: MylesAndMore @@ -15,13 +15,23 @@ commands: permission: reload link: description: Links a world on the server as a lobby/game world. - usage: '§cUsage: /tumble:link <world> lobby|game' + usage: '§cUsage: /tumble:link <world> (lobby|game)' permission: link aliases: [linkworld, link-world] start: - description: Force starts a Tumble match. - usage: '§cUsage: /tumble:start' + description: Force starts a Tumble match with an optional game type. + usage: '§cUsage: /tumble:start [gameType]' permission: start + winlocation: + description: Links the location to teleport the winning player of a game. + usage: '§cUsage: /tumble:winlocation [x] [y] [z]' + permission: winlocation + aliases: [win-location, winloc, win-loc] + autostart: + description: Configures the auto start functions of Tumble. + usage: '§cUsage: /tumble:autostart <playerAmount> [enable|disable]' + permission: autostart + aliases: [auto-start] permissions: reload: description: Allows you to reload the plugin's config. @@ -32,3 +42,9 @@ permissions: start: description: Allows you to start a Tumble match. default: op + winlocation: + description: Allows you to link a win location. + default: op + autostart: + description: Allows you to set the autostart details of Tumble. + default: op |