aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md81
-rw-r--r--build.gradle2
-rw-r--r--og-guide.md33
-rw-r--r--src/main/java/com/MylesAndMore/tumble/EventListener.java277
-rw-r--r--src/main/java/com/MylesAndMore/tumble/Game.java593
-rw-r--r--src/main/java/com/MylesAndMore/tumble/GameManager.java60
-rw-r--r--src/main/java/com/MylesAndMore/tumble/Main.java17
-rw-r--r--src/main/java/com/MylesAndMore/tumble/TumbleManager.java4
-rw-r--r--src/main/java/com/MylesAndMore/tumble/api/Generator.java97
-rw-r--r--src/main/java/com/MylesAndMore/tumble/api/Layers.java300
-rw-r--r--src/main/java/com/MylesAndMore/tumble/commands/SetAutoStart.java97
-rw-r--r--src/main/java/com/MylesAndMore/tumble/commands/SetWinnerLoc.java115
-rw-r--r--src/main/java/com/MylesAndMore/tumble/commands/SetWorldConfig.java4
-rw-r--r--src/main/java/com/MylesAndMore/tumble/commands/StartGame.java93
-rw-r--r--src/main/resources/config.yml32
-rw-r--r--src/main/resources/plugin.yml26
16 files changed, 1682 insertions, 149 deletions
diff --git a/README.md b/README.md
index 54b93ba..37150ef 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,79 @@
-# tumble
-A Minecraft: Java Edition plugin recreating the Tumble minigame from Minecraft Legacy Console Edition.
+# tumble
-*This plugin is currently in **alpha**! Most changes are done on branches other than this one.*
+## Overview
-This readme will be updated on our first release. Thank you!
+Tumble is a Spigot/Paper plugin that aims to recreate the Tumble minigame from the bygone era of the Minecraft Legacy Console Editions.
+
+## What *is* Tumble?
+
+If you've never heard of it, [Tumble](https://minecraft-archive.fandom.com/wiki/Tumble_Mode) is a twist on the classic Minecraft minigame of spleef, where the objective is to break the blocks under your opponents. But in Tumble, you play on randomly generated layers of blocks, using shovels, snowballs, or both to try and eliminate your opponents.
+
+## Features
+
+- Choose from three different game modes present in the original game--shovels, snowballs, and mixed
+- Four types of random layer generation
+- 15 unique, themed layer varieties
+- Quick and easy setup and use
+- Support for 2-8 players
+- Highly customizable
+- Open-source codebase
+
+## Setup
+
+1. Simply [download](https://github.com/MylesAndMore/tumble/releases/tag/stable) the plugin's JAR file and place it in your server's plugins directory.
+
+ - *Note: Multiverse is also required for the plugin to run, you may download it [here](https://www.spigotmc.org/resources/multiverse-core.390/).*
+
+2. Make sure that you have at least two worlds in your world directory! One is for your lobby world, and the other is for your game arena.
+
+ - If you would like an experience similar to the original game, see [my guide](https://github.com/MylesAndMore/tumble/blob/main/og-guide.md) for using the original worlds.
+
+3. Start your server. The plugin will generate a couple of warnings, these are normal.
+4. Ensure that you have imported your worlds into Multiverse. This can be done with the command ```/mv import <your-world-name> normal```.
+5. Now you need to tell Tumble which world is your lobby and which world is your game arena. You can do this with ```/tumble:link <your-lobby-world> lobby``` and ```/tumble:link <your-game-world> game``` respectively.
+6. **VERY IMPORTANT:** The plugin will teleport players to the world spawn point of each world, and generate the game's blocks around the spawn point of the game world. Ensure that your spawn points are clear of any obstructions, and that a 20x20x20 cube is cleared out from the spawn of whatever game world you are using. **Any blocks in this area will be destroyed when the game begins.**
+7. You're done! You can now start games with the command ```/tumble:start```.
+
+Scroll down for more options to configure your game.
+
+## Commands
+
+- ```/tumble:reload```
+
+ - *Description:* Reloads the plugin's configuration.
+ - *Usage:* ```/tumble:reload```
+ - *Permission:* ```tumble.reload```
+- ```/tumble:link```
+ - *Description:* Links a world on the server as a lobby or game world.
+ - *Usage:* ```/tumble:link <world> (lobby|game)```
+ - *Permission:* ```tumble.link```
+- ```/tumble:start```
+ - *Description:* Force starts a Tumble match (with an optional game type).
+ - *Usage:* ```/tumble:start [game-type]```
+ - *Permission:* ```tumble.start```
+- ```/tumble:winlocation```
+ - *Description:* Sets the location to teleport the winning player of a game. Uses the player's location if no arguments are specified.
+ - *Usage:* ```/tumble:winlocation [x] [y] [z]```
+ - *Permission:* ```tumble.winlocation```
+- ```/tumble:autostart```
+ - *Description:* Configures the auto start functions of Tumble.
+ - *Usage:* ```/tumble:autostart <playerAmount> [enable|disable]```
+ - *Permission:* ```tumble.autostart```
+
+## Configuration
+
+- ```gameMode```
+ - Customize the default game mode of Tumble.
+ - Acceptable options include: shovels, snowballs, mixed
+ - *Default:* ```mixed```
+
+- ```hideJoinLeaveMessages```
+ - Hides join/leave messages in public chat.
+ - *Default:* ```false```
+
+- ```permissionMessage```
+ - Customize the message that displays when the player does not have permission to execute a command from this plugin.
+
+## Issues & Feedback
+
+Feel free to report any bugs, leave feedback, ask questions, or submit ideas for new features on our [GitHub issues page](https://github.com/MylesAndMore/tumble/issues/new)!
diff --git a/build.gradle b/build.gradle
index 019bafd..a7f815e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -9,7 +9,7 @@ java {
}
group 'tumble'
-version '0.0.1-SNAPSHOT'
+version '1.0.0'
repositories {
// Use Maven Central for resolving dependencies.
diff --git a/og-guide.md b/og-guide.md
new file mode 100644
index 0000000..314ddf8
--- /dev/null
+++ b/og-guide.md
@@ -0,0 +1,33 @@
+# tumble
+
+## Guide for original worlds
+
+In this guide, I'll go over how to set up the Tumble plugin with the original game worlds from the Legacy Console Editions.
+
+## Steps
+
+1. Download the worlds and unzip them into your server's main/root directory. **Ensure you download the Java and not the Bedrock versions**!
+A huge thanks to *Catmanjoe* for porting these worlds! This game would not be the same without you!
+
+ - [Lobby (new edition)](https://mcpedl.com/mc-2017-new-mini-games-lobby-download-map/)
+ - [Lobby (old edition)](https://mcpedl.com/minecraft-2016-classic-mini-games-lobby-map/)
+ - [Arena](https://www.planetminecraft.com/project/minecraft-2016-classic-mini-games-lobby-download-bedrock-edition/)
+2. Take note of the names of the world folders (you may rename them), we will need this in a moment.
+3. Start and join your server.
+4. Import both worlds into Multiverse. You can do this by running the command ```/mv import <your-world-name> normal``` for both worlds.
+5. Now you can link each world! Do this with ```/tumble:link <your-lobby-world> lobby``` and ```/tumble:link <your-game-world> game``` respectively.
+6. Teleport to your new lobby world by using ```/mvtp <your-lobby-world>```.
+7. Set the correct spawn location in this world using ```/setworlspawn```. For me, the correct coordinates were ```/setworldspawn place holder L```, but your results may vary.
+8. Set the location that the winner will be teleported using ```tumble:winloc```. Again, the correct coordinates were ```tumble:winloc wait no u``` in my case.
+9. Now, teleport to the game world. Use ```/mvtp <your-game world>```.
+10. Set the correct spawn point of this world. This is also where the game will generate its blocks. My preferred position is ```/setworldspawn 0 60 0```, but you may place the spawn whereever you like.
+
+You're done!
+
+## Continuation
+
+With this, the setup for this plugin is complete, but there still may be more for you to do. There are other plugins out there to fine-tune your experience even more. Plugins like [WorldGuard](https://dev.bukkit.org/projects/worldguard) and [CyberWorldReset](https://www.spigotmc.org/resources/cyberworldreset-standard-%E2%9C%A8-regenerate-worlds-scheduled-resets-lag-optimized%E3%80%8C1-8-1-19%E3%80%8D.96834/) can protect players from breaking blocks in the lobby and reset any redstone they activated, while others like [ViaVersion](https://www.spigotmc.org/resources/viaversion.19254/) can allow you to play Tumble from your favorite Minecraft version (yes, you, 1.8.9 players).
+
+Whatever you choose, the experience is up to you.
+
+Happy playing!
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