aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md12
-rw-r--r--src/main/java/com/MylesAndMore/Tumble/Main.java38
-rw-r--r--src/main/java/com/MylesAndMore/Tumble/commands/Config.java35
-rw-r--r--src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java48
-rw-r--r--src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java48
-rw-r--r--src/main/java/com/MylesAndMore/Tumble/commands/Join.java103
-rw-r--r--src/main/java/com/MylesAndMore/Tumble/commands/Leave.java53
-rw-r--r--src/main/java/com/MylesAndMore/Tumble/commands/Reload.java26
-rw-r--r--src/main/java/com/MylesAndMore/Tumble/commands/SetAutoStart.java94
-rw-r--r--src/main/java/com/MylesAndMore/Tumble/commands/SetWinnerLoc.java110
-rw-r--r--src/main/java/com/MylesAndMore/Tumble/commands/SetWorldConfig.java74
-rw-r--r--src/main/java/com/MylesAndMore/Tumble/commands/StartGame.java85
-rw-r--r--src/main/java/com/MylesAndMore/Tumble/game/Arena.java26
-rw-r--r--src/main/java/com/MylesAndMore/Tumble/game/EventListener.java204
-rw-r--r--src/main/java/com/MylesAndMore/Tumble/game/Game.java652
-rw-r--r--src/main/java/com/MylesAndMore/Tumble/game/Generator.java135
-rw-r--r--src/main/java/com/MylesAndMore/Tumble/plugin/ConfigManager.java152
-rw-r--r--src/main/java/com/MylesAndMore/Tumble/plugin/Constants.java25
-rw-r--r--src/main/java/com/MylesAndMore/Tumble/plugin/EventListener.java222
-rw-r--r--src/main/java/com/MylesAndMore/Tumble/plugin/GameState.java7
-rw-r--r--src/main/java/com/MylesAndMore/Tumble/plugin/GameType.java11
-rw-r--r--src/main/java/com/MylesAndMore/Tumble/plugin/Result.java8
-rw-r--r--src/main/resources/config.yml49
-rw-r--r--src/main/resources/plugin.yml72
24 files changed, 1187 insertions, 1102 deletions
diff --git a/README.md b/README.md
index a2b349b..d06fefa 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@ Tumble is a Spigot/Paper plugin that aims to recreate the Tumble minigame from t
## 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.
+If you've never heard of it, [Tumble](https://minecraft.wiki/w/Tumble) 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
@@ -17,19 +17,19 @@ If you've never heard of it, [Tumble](https://minecraft-archive.fandom.com/wiki/
- Support for 2-8 players
- Highly customizable
- Open-source codebase
+- Multiple arenas and concurrent games
## Setup
-1. Simply [download](https://github.com/MylesAndMore/Tumble/releases) 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/).*
-
+1. Simply [download](https://github.com/MylesAndMore/Tumble/releases) the plugin's JAR file and place it in your server's plugins directory.
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```.
+4. Ensure that you have imported your worlds using a plugin like Multiverse. This can be done with the command ```/mv import <your-world-name> normal```.
+
+[//]: # (TODO: finish this once config system is done)
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```.
diff --git a/src/main/java/com/MylesAndMore/Tumble/Main.java b/src/main/java/com/MylesAndMore/Tumble/Main.java
index a9a07ee..f3ea52e 100644
--- a/src/main/java/com/MylesAndMore/Tumble/Main.java
+++ b/src/main/java/com/MylesAndMore/Tumble/Main.java
@@ -1,39 +1,33 @@
package com.MylesAndMore.Tumble;
import com.MylesAndMore.Tumble.commands.*;
-import com.MylesAndMore.Tumble.plugin.Constants;
-import com.MylesAndMore.Tumble.plugin.EventListener;
-
-import com.jeff_media.updatechecker.UpdateCheckSource;
-import com.jeff_media.updatechecker.UpdateChecker;
+import com.MylesAndMore.Tumble.plugin.ConfigManager;
import org.bstats.bukkit.Metrics;
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
+import java.util.Objects;
+
public class Main extends JavaPlugin{
+ public static Main plugin;
+
@Override
public void onEnable() {
- // Register setup items
- getServer().getPluginManager().registerEvents(new EventListener(), this);
- this.getCommand("reload").setExecutor(new Reload());
- this.getCommand("link").setExecutor(new SetWorldConfig());
- this.getCommand("start").setExecutor(new StartGame());
- this.getCommand("winlocation").setExecutor(new SetWinnerLoc());
- this.getCommand("autostart").setExecutor(new SetAutoStart());
+ plugin = this;
+
+ Objects.requireNonNull(this.getCommand("reload")).setExecutor(new Reload());
+ Objects.requireNonNull(this.getCommand("config")).setExecutor(new Config());
+ Objects.requireNonNull(this.getCommand("forcestart")).setExecutor(new ForceStart());
+ Objects.requireNonNull(this.getCommand("join")).setExecutor(new Join());
+ Objects.requireNonNull(this.getCommand("leave")).setExecutor(new Leave());
+ Objects.requireNonNull(this.getCommand("forcestop")).setExecutor(new ForceStop());
new Metrics(this, 16940);
- this.saveDefaultConfig(); // Saves the default config file (packaged in the JAR) if we haven't already
+ // TODO: change command format
- // Check if worlds are null in config and throw warnings if so
- if (Constants.getGameWorld() == null) {
- 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 (Constants.getLobbyWorld() == null) {
- 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.");
- }
+ this.saveDefaultConfig(); // Saves the default config file (packaged in the JAR) if we haven't already
+ ConfigManager.readConfig();
Bukkit.getServer().getLogger().info("[Tumble] Tumble successfully enabled!");
}
diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Config.java b/src/main/java/com/MylesAndMore/Tumble/commands/Config.java
new file mode 100644
index 0000000..d91a5b5
--- /dev/null
+++ b/src/main/java/com/MylesAndMore/Tumble/commands/Config.java
@@ -0,0 +1,35 @@
+package com.MylesAndMore.Tumble.commands;
+
+import org.bukkit.ChatColor;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.command.TabCompleter;
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Config implements CommandExecutor, TabCompleter {
+ @Override
+ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
+ if (!(sender instanceof Player)) {
+ sender.sendMessage(ChatColor.RED + "This cannot be run by the console");
+ return false;
+ }
+
+ if (!sender.hasPermission("tumble.config")) {
+ sender.sendMessage(ChatColor.RED + "You do not have permission to perform this command!");
+ return false;
+ }
+
+ sender.sendMessage(ChatColor.RED + "Not implemented yet"); // TODO
+ return true;
+ }
+
+ @Override
+ public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
+ return new ArrayList<>();
+ }
+}
diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java
new file mode 100644
index 0000000..78ff183
--- /dev/null
+++ b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStart.java
@@ -0,0 +1,48 @@
+package com.MylesAndMore.Tumble.commands;
+
+import com.MylesAndMore.Tumble.game.Game;
+import com.MylesAndMore.Tumble.plugin.ConfigManager;
+import org.bukkit.ChatColor;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.command.TabCompleter;
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ForceStart implements CommandExecutor, TabCompleter {
+ @Override
+ public boolean onCommand(CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
+
+ if (!sender.hasPermission("tumble.forcestart")) {
+ sender.sendMessage(ChatColor.RED + "You do not have permission to perform this command!");
+ return false;
+ }
+
+ Game game;
+ if (args.length < 1 || args[0] == null) {
+ game = ConfigManager.findGamePlayerIsIn((Player)sender);
+ if (game == null) {
+ sender.sendMessage(ChatColor.RED + "Missing arena name");
+ return false;
+ }
+ }
+ else {
+ game = ConfigManager.arenas.get(args[0]).game;
+ }
+
+ game.startGame();
+ return true;
+ }
+
+ @Override
+ public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
+ if (args.length == 1) {
+ return ConfigManager.arenas.keySet().stream().toList();
+ }
+ return new ArrayList<>();
+ }
+}
diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java
new file mode 100644
index 0000000..7f266d7
--- /dev/null
+++ b/src/main/java/com/MylesAndMore/Tumble/commands/ForceStop.java
@@ -0,0 +1,48 @@
+package com.MylesAndMore.Tumble.commands;
+
+import com.MylesAndMore.Tumble.game.Game;
+import com.MylesAndMore.Tumble.plugin.ConfigManager;
+import org.bukkit.ChatColor;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.command.TabCompleter;
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ForceStop implements CommandExecutor, TabCompleter {
+ @Override
+ public boolean onCommand(CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
+
+ if (!sender.hasPermission("tumble.forcestop")) {
+ sender.sendMessage(ChatColor.RED + "You do not have permission to perform this command!");
+ return false;
+ }
+
+ Game game;
+ if (args.length < 1 || args[0] == null) {
+ game = ConfigManager.findGamePlayerIsIn((Player)sender);
+ if (game == null) {
+ sender.sendMessage(ChatColor.RED + "Missing arena name");
+ return false;
+ }
+ }
+ else {
+ game = ConfigManager.arenas.get(args[0]).game;
+ }
+
+ game.killGame();
+ return true;
+ }
+
+ @Override
+ public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
+ if (args.length == 1) {
+ return ConfigManager.arenas.keySet().stream().toList();
+ }
+ return new ArrayList<>();
+ }
+}
diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Join.java b/src/main/java/com/MylesAndMore/Tumble/commands/Join.java
new file mode 100644
index 0000000..4dd4ef8
--- /dev/null
+++ b/src/main/java/com/MylesAndMore/Tumble/commands/Join.java
@@ -0,0 +1,103 @@
+package com.MylesAndMore.Tumble.commands;
+
+import com.MylesAndMore.Tumble.game.Arena;
+import com.MylesAndMore.Tumble.plugin.ConfigManager;
+import com.MylesAndMore.Tumble.game.Game;
+import com.MylesAndMore.Tumble.plugin.GameState;
+import com.MylesAndMore.Tumble.plugin.GameType;
+import org.bukkit.ChatColor;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.command.TabCompleter;
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+public class Join implements CommandExecutor, TabCompleter {
+ @Override
+ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
+
+ if (!(sender instanceof Player)) {
+ sender.sendMessage(ChatColor.RED + "This cannot be run by the console");
+ return false;
+ }
+
+ if (!sender.hasPermission("tumble.join")) {
+ sender.sendMessage(ChatColor.RED + "You do not have permission to perform this command!");
+ return false;
+ }
+
+ if (ConfigManager.findGamePlayerIsIn((Player)sender) != null) {
+ sender.sendMessage(ChatColor.RED + "You are already in a game! Leave it to join another one");
+ }
+
+ if (args.length < 1 || args[0] == null) {
+ sender.sendMessage(ChatColor.RED + "Missing arena name");
+ return false;
+ }
+ String arenaName = args[0];
+ if (!ConfigManager.arenas.containsKey(arenaName))
+ {
+ sender.sendMessage(ChatColor.RED + "This arena does not exist");
+ return false;
+ }
+ Arena arena = ConfigManager.arenas.get(arenaName);
+
+ Game game;
+ if (args.length < 2 || args[1] == null) {
+ if (arena.game == null) {
+ sender.sendMessage(ChatColor.RED + "no game is currently taking place in this arena, specify the game type to start one");
+ return false;
+ }
+ else {
+ game = arena.game;
+ }
+ }
+ else {
+ GameType type;
+ switch (args[1]) {
+ case "shovels", "shovel" -> type = GameType.SHOVELS;
+ case "snowballs", "snowball" -> type = GameType.SNOWBALLS;
+ case "mix", "mixed" -> type = GameType.MIXED;
+ default -> {
+ sender.sendMessage(ChatColor.RED + "Invalid game type");
+ return false;
+ }
+ }
+
+ if (arena.game == null) {
+ game = arena.game = new Game(arena, type);
+ }
+ else {
+ sender.sendMessage(ChatColor.RED + "A game of "+type+" is currently taking place in this arena, choose another arena or join it with /tumble:join "+arena.name+" "+type);
+ return false;
+ }
+ }
+
+ if (game.gameState != GameState.WAITING) {
+ sender.sendMessage(ChatColor.RED + "This game is still in progress, wait until it finishes or join another game");
+ return false;
+ }
+
+ game.addPlayer((Player)sender);
+ sender.sendMessage(ChatColor.GREEN + "Joined game " + arena.name + " - " + game.type);
+ return true;
+ }
+
+ @Override
+ public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
+ if (args.length == 1) {
+ return ConfigManager.arenas.keySet().stream().toList();
+ }
+ if (args.length == 2) {
+ return Arrays.stream(GameType.values()).map(Objects::toString).collect(Collectors.toList());
+ }
+ return new ArrayList<>();
+ }
+} \ No newline at end of file
diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Leave.java b/src/main/java/com/MylesAndMore/Tumble/commands/Leave.java
new file mode 100644
index 0000000..94255a4
--- /dev/null
+++ b/src/main/java/com/MylesAndMore/Tumble/commands/Leave.java
@@ -0,0 +1,53 @@
+package com.MylesAndMore.Tumble.commands;
+
+import com.MylesAndMore.Tumble.game.Game;
+import com.MylesAndMore.Tumble.plugin.ConfigManager;
+import org.bukkit.ChatColor;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.command.TabCompleter;
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Leave implements CommandExecutor, TabCompleter {
+ @Override
+ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
+
+ if (!(sender instanceof Player)) {
+ sender.sendMessage(ChatColor.RED + "This cannot be run by the console");
+ return false;
+ }
+
+ if (!sender.hasPermission("tumble.leave")) {
+ sender.sendMessage(ChatColor.RED + "You do not have permission to perform this command!");
+ return false;
+ }
+
+ Game game;
+ if (args.length < 1 || args[0] == null) {
+ game = ConfigManager.findGamePlayerIsIn((Player)sender);
+ if (game == null) {
+ sender.sendMessage(ChatColor.RED + "Missing arena name");
+ return false;
+ }
+ }
+ else {
+ game = ConfigManager.arenas.get(args[0]).game;
+ }
+
+ game.removePlayer((Player) sender);
+ return true;
+ }
+
+ @Override
+ public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
+ if (args.length == 1) {
+ return ConfigManager.arenas.keySet().stream().toList();
+ }
+ return new ArrayList<>();
+ }
+} \ No newline at end of file
diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/Reload.java b/src/main/java/com/MylesAndMore/Tumble/commands/Reload.java
index ffc6dd8..5d35a03 100644
--- a/src/main/java/com/MylesAndMore/Tumble/commands/Reload.java
+++ b/src/main/java/com/MylesAndMore/Tumble/commands/Reload.java
@@ -1,22 +1,32 @@
package com.MylesAndMore.Tumble.commands;
-import com.MylesAndMore.Tumble.plugin.Constants;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
+import org.bukkit.command.TabCompleter;
import org.jetbrains.annotations.NotNull;
-public class Reload implements CommandExecutor {
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.MylesAndMore.Tumble.Main.plugin;
+
+public class Reload implements CommandExecutor, TabCompleter {
+
@Override
public boolean onCommand(CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
- if (sender.hasPermission("tumble.reload")) {
- Constants.getPlugin().reloadConfig();
- sender.sendMessage(ChatColor.GREEN + "Tumble configuration reloaded successfully.");
- }
- else {
- sender.sendMessage(ChatColor.RED + Constants.getPermissionMessage());
+ if (!sender.hasPermission("tumble.reload")) {
+ sender.sendMessage(ChatColor.RED + "You do not have permission to perform this command!");
+ return false;
}
+ plugin.onEnable();
+ sender.sendMessage(ChatColor.GREEN + "Tumble configuration reloaded successfully.");
return true;
}
+
+ @Override
+ public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
+ return new ArrayList<>();
+ }
}
diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/SetAutoStart.java b/src/main/java/com/MylesAndMore/Tumble/commands/SetAutoStart.java
deleted file mode 100644
index b3da74e..0000000
--- a/src/main/java/com/MylesAndMore/Tumble/commands/SetAutoStart.java
+++ /dev/null
@@ -1,94 +0,0 @@
-package com.MylesAndMore.Tumble.commands;
-
-import com.MylesAndMore.Tumble.plugin.Constants;
-import org.bukkit.ChatColor;
-import org.bukkit.command.Command;
-import org.bukkit.command.CommandExecutor;
-import org.bukkit.command.CommandSender;
-import org.jetbrains.annotations.NotNull;
-
-import java.util.Objects;
-
-public class SetAutoStart implements CommandExecutor{
- @Override
- public boolean onCommand(CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
- if (sender.hasPermission("tumble.autostart")) {
- if (Constants.getGameWorld() != null) {
- if (Constants.getLobbyWorld() != null) {
- 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
- if ((args0 >= 2) && (args0 <= 8)) {
- if (Objects.equals(args[1], "enable")) {
- // Write values to the config
- Constants.getPlugin().getConfig().set("autoStart.players", args0);
- Constants.getPlugin().getConfig().set("autoStart.enabled", true);
- Constants.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")) {
- Constants.getPlugin().getConfig().set("autoStart.players", args0);
- Constants.getPlugin().getConfig().set("autoStart.enabled", false);
- Constants.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)) {
- Constants.getPlugin().getConfig().set("autoStart.players", args0);
- Constants.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 + Constants.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
deleted file mode 100644
index 38e6444..0000000
--- a/src/main/java/com/MylesAndMore/Tumble/commands/SetWinnerLoc.java
+++ /dev/null
@@ -1,110 +0,0 @@
-package com.MylesAndMore.Tumble.commands;
-
-import com.MylesAndMore.Tumble.plugin.Constants;
-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;
-import org.jetbrains.annotations.NotNull;
-
-public class SetWinnerLoc implements CommandExecutor {
- @Override
- public boolean onCommand(CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
- if (sender.hasPermission("tumble.winlocation")) {
- if (Constants.getLobbyWorld() != null) {
- 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))) {
- Constants.getPlugin().getConfig().set("winnerTeleport.x", args0);
- Constants.getPlugin().getConfig().set("winnerTeleport.y", args1);
- Constants.getPlugin().getConfig().set("winnerTeleport.z", args2);
- Constants.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
- Constants.getPlugin().getConfig().set("winnerTeleport.x", senderPos.getX());
- Constants.getPlugin().getConfig().set("winnerTeleport.y", senderPos.getY());
- Constants.getPlugin().getConfig().set("winnerTeleport.z", senderPos.getZ());
- Constants.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 if (sender instanceof ConsoleCommandSender) {
- 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.");
- }
- if (!((args0 == 0) || (args1 == 0) || (args2 == 0))) {
- Constants.getPlugin().getConfig().set("winnerTeleport.x", args0);
- Constants.getPlugin().getConfig().set("winnerTeleport.y", args1);
- Constants.getPlugin().getConfig().set("winnerTeleport.z", args2);
- Constants.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 + Constants.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
deleted file mode 100644
index 90e0a96..0000000
--- a/src/main/java/com/MylesAndMore/Tumble/commands/SetWorldConfig.java
+++ /dev/null
@@ -1,74 +0,0 @@
-package com.MylesAndMore.Tumble.commands;
-
-import com.MylesAndMore.Tumble.plugin.Constants;
-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;
-import org.jetbrains.annotations.NotNull;
-
-import java.util.Objects;
-
-public class SetWorldConfig implements CommandExecutor {
- @Override
- public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
- // Catch for null arguments
- if (args.length == 2) {
- if (sender.hasPermission("tumble.link")){
- // Initialize vars for their respective command arguments
- String world = args[0];
- String worldType = args[1];
- if (Objects.equals(worldType, "lobby")) {
- // Check if the world is actually a world on the server
- if (Bukkit.getWorld(world) != null) {
- // Check if the world has already been configured
- if (!Objects.equals(Constants.getGameWorld(), world)) {
- // Set the specified value of the world in the config under lobbyWorld
- Constants.getPlugin().getConfig().set("lobbyWorld", world);
- Constants.getPlugin().saveConfig();
- sender.sendMessage(ChatColor.GREEN + "Lobby 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!");
- }
- else {
- sender.sendMessage(ChatColor.RED + "That world has already been linked, please choose/create another world!");
- }
- }
- else {
- sender.sendMessage(ChatColor.RED + "Failed to find a world named " + ChatColor.GRAY + world);
- }
- }
- else if (Objects.equals(args[1], "game")) {
- if (Bukkit.getWorld(world) != null) {
- if (!Objects.equals(Constants.getLobbyWorld(), world)) {
- Constants.getPlugin().getConfig().set("gameWorld", world);
- Constants.getPlugin().saveConfig();
- // Set the gamerule of doImmediateRespawn in the gameWorld for later
- Objects.requireNonNull(Bukkit.getWorld(world)).setGameRule(GameRule.DO_IMMEDIATE_RESPAWN, true);
- Objects.requireNonNull(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!");
- }
- else {
- sender.sendMessage(ChatColor.RED + "That world has already been linked, please choose/create another world!");
- }
- }
- else {
- sender.sendMessage(ChatColor.RED + "Failed to find a world named " + ChatColor.GRAY + world);
- }
- }
- else {
- sender.sendMessage(ChatColor.RED + "Allowed world types are " + ChatColor.GRAY + "lobby " + ChatColor.RED + "and " + ChatColor.GRAY + "game" + ChatColor.RED + ".");
- }
- }
- else {
- sender.sendMessage(ChatColor.RED + Constants.getPermissionMessage());
- }
- }
- else {
- return false;
- }
- return true;
- }
-}
diff --git a/src/main/java/com/MylesAndMore/Tumble/commands/StartGame.java b/src/main/java/com/MylesAndMore/Tumble/commands/StartGame.java
deleted file mode 100644
index 706b33a..0000000
--- a/src/main/java/com/MylesAndMore/Tumble/commands/StartGame.java
+++ /dev/null
@@ -1,85 +0,0 @@
-package com.MylesAndMore.Tumble.commands;
-
-import com.MylesAndMore.Tumble.game.Game;
-import com.MylesAndMore.Tumble.plugin.Constants;
-import org.bukkit.ChatColor;
-import org.bukkit.command.Command;
-import org.bukkit.command.CommandExecutor;
-import org.bukkit.command.CommandSender;
-import org.jetbrains.annotations.NotNull;
-
-import java.util.Objects;
-
-public class StartGame implements CommandExecutor {
- @Override
- public boolean onCommand(CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
- if (sender.hasPermission("tumble.start")) {
- if (Constants.getLobbyWorld() != null) {
- if (Constants.getPlayersInLobby().size() > 1) {
- if (Constants.getGameWorld() != null) {
- 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 (Constants.getMVWorldManager().loadWorld(Constants.getGameWorld())) {
- // If there is no starting argument,
- if (args.length == 0) {
- // pull which gamemode to initiate from the config file
- if (!Game.getGame().startGame(Constants.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 + Constants.getPlugin().getConfig().getString("gameMode"));
- }
- }
- }
- // If there was an argument for gameType, pass that instead
- 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 {
- sender.sendMessage(ChatColor.RED + "Failed to find a world named " + ChatColor.GRAY + Constants.getGameWorld());
- sender.sendMessage(ChatColor.RED + "Is the configuration file correct?");
- }
- }
- else {
- sender.sendMessage(ChatColor.RED + "A game is already queued to begin!");
- }
- }
- else {
- sender.sendMessage(ChatColor.RED + "Please link a game world first!");
- }
- }
- else {
- sender.sendMessage(ChatColor.RED + "You can't start a game with yourself!");
- }
- }
- else {
- sender.sendMessage(ChatColor.RED + "Please link a lobby world first!");
- }
- }
- else {
- sender.sendMessage(ChatColor.RED + Constants.getPermissionMessage());
- }
- return true;
- }
-}
diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Arena.java b/src/main/java/com/MylesAndMore/Tumble/game/Arena.java
new file mode 100644
index 0000000..ef477d2
--- /dev/null
+++ b/src/main/java/com/MylesAndMore/Tumble/game/Arena.java
@@ -0,0 +1,26 @@
+package com.MylesAndMore.Tumble.game;
+
+import org.bukkit.Location;
+import org.bukkit.World;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * An arena is the world and spawn location where a game can take place. An arena can only host one game at a time.
+ */
+public class Arena {
+ public Game game = null;
+ public final World world;
+ public final Location location;
+ public final String name;
+
+ /**
+ * Creates a new Arena
+ * @param name Name of the arena
+ * @param location Center point / spawn point.
+ */
+ public Arena(@NotNull String name, @NotNull Location location) {
+ this.location = location;
+ this.world = location.getWorld();
+ this.name = name;
+ }
+}
diff --git a/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java b/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java
new file mode 100644
index 0000000..16698f4
--- /dev/null
+++ b/src/main/java/com/MylesAndMore/Tumble/game/EventListener.java
@@ -0,0 +1,204 @@
+package com.MylesAndMore.Tumble.game;
+
+import java.util.Objects;
+
+import com.MylesAndMore.Tumble.plugin.ConfigManager;
+import com.MylesAndMore.Tumble.plugin.GameState;
+import org.bukkit.*;
+import org.bukkit.entity.Player;
+import org.bukkit.entity.Snowball;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+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.inventory.InventoryDragEvent;
+import org.bukkit.event.player.*;
+import org.bukkit.util.Vector;
+
+import org.bukkit.inventory.ItemStack;
+
+import static com.MylesAndMore.Tumble.Main.plugin;
+
+/**
+ * Tumble event listener for all plugin and game-related events.
+ */
+public class EventListener implements Listener {
+
+ World gameWorld;
+ Game game;
+ public EventListener(Game game) {
+ this.game = game;
+ this.gameWorld = game.gameWorld;
+ }
+
+ @EventHandler
+ public void PlayerJoinEvent(PlayerJoinEvent event) {
+ // Hide/show join message accordingly
+ if (ConfigManager.HideLeaveJoin) {
+ event.setJoinMessage(null);
+ }
+ if (event.getPlayer().getWorld() == gameWorld) {
+ // Send the player back to the lobby if they try to join in the middle of a game
+ event.getPlayer().teleport(Objects.requireNonNull(ConfigManager.lobby));
+ }
+ }
+
+ @EventHandler
+ public void PlayerQuitEvent(PlayerQuitEvent event) {
+ // Hide/show leave message accordingly
+ if (ConfigManager.HideLeaveJoin) {
+ event.setQuitMessage(null);
+ }
+ if (event.getPlayer().getWorld() == gameWorld) {
+ game.removePlayer(event.getPlayer());
+ }
+ }
+
+ @EventHandler
+ public void PlayerDeathEvent(PlayerDeathEvent event) {
+ if (event.getEntity().getWorld() == gameWorld) {
+ game.playerDeath(event.getEntity());
+ }
+ }
+
+ @EventHandler
+ public void PlayerItemDamageEvent(PlayerItemDamageEvent event) {
+ // Remove item damage within games
+ if (event.getPlayer().getWorld() == gameWorld) {
+ event.setCancelled(true);
+ }
+ }
+
+ @EventHandler
+ public void ProjectileLaunchEvent(ProjectileLaunchEvent event) {
+ if (event.getEntity().getWorld() == gameWorld
+ && event.getEntity() instanceof Snowball
+ && event.getEntity().getShooter() instanceof Player player) {
+
+ // Prevent projectiles (snowballs) from being thrown before the game starts
+ if (Objects.equals(game.gameState, GameState.STARTING)) {
+ event.setCancelled(true);
+ }
+ else {
+ // Give players a snowball when they've used one (infinite snowballs)
+ Bukkit.getServer().getScheduler().runTask(plugin, () -> player.getInventory().addItem(new ItemStack(Material.SNOWBALL, 1)));
+ }
+ }
+ }
+
+ @EventHandler
+ public void ProjectileHitEvent(ProjectileHitEvent event) {
+ if (event.getHitBlock() == null) { return; }
+ // Removes blocks that snowballs thrown by players have hit in the game world
+ if (event.getHitBlock().getWorld() == gameWorld) {
+ if (event.getEntity() instanceof Snowball) {
+ if (event.getEntity().getShooter() instanceof Player p) {
+ if (event.getHitBlock() != null) {
+ if (event.getHitBlock().getLocation().distanceSquared(Objects.requireNonNull(game.arena.location)) < 579) {
+ p.playEffect(
+ event.getHitBlock().getLocation(),
+ Effect.STEP_SOUND,
+ event.getHitBlock().getType());
+ event.getHitBlock().setType(Material.AIR);
+ }
+ }
+ else if (event.getHitEntity() != null) {
+ if (event.getHitEntity() instanceof Player hitPlayer) {
+ // Also cancel any knockback
+ Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> hitPlayer.setVelocity(new Vector()));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @EventHandler
+ public void PlayerDropItemEvent(PlayerDropItemEvent event) {
+ // Don't allow items to drop in the game world
+ if (event.getPlayer().getWorld() == gameWorld) {
+ event.setCancelled(true);
+ }
+ }
+
+ @EventHandler
+ public void PlayerMoveEvent(PlayerMoveEvent event) {
+ // Cancel movement if the game is starting (so players can't move before the game starts)
+ if (Objects.equals(game.gameState, GameState.STARTING)
+ && event.getPlayer().getWorld().equals(gameWorld)) {
+ event.setCancelled(true);
+ }
+ }
+
+ @EventHandler
+ public void BlockDropItemEvent(BlockDropItemEvent event) {
+ // If a block was going to drop an item (ex. snow dropping snowballs) in the game world, cancel it
+ if (event.getBlock().getWorld() == gameWorld) {
+ event.setCancelled(true);
+ }
+ }
+
+ @EventHandler
+ public void PlayerInteractEvent(PlayerInteractEvent event) {
+ // Remove blocks when clicked in the game world (all gamemodes require this functionality)
+ if (event.getAction() == Action.LEFT_CLICK_BLOCK
+ && Objects.requireNonNull(event.getClickedBlock()).getWorld() == gameWorld) {
+ event.getPlayer().playEffect(
+ event.getClickedBlock().getLocation(),
+ Effect.STEP_SOUND,
+ event.getClickedBlock().getType()
+ );
+ event.getClickedBlock().setType(Material.AIR);
+ }
+ }
+
+ @EventHandler
+ public void BlockBreakEvent(BlockBreakEvent event) {
+ // This just doesn't allow blocks to break in the gameWorld; the PlayerInteractEvent will take care of everything
+ // This prevents any weird client-server desync
+ if (event.getBlock().getWorld() == gameWorld) {
+ event.setCancelled(true);
+ }
+ }
+
+ @EventHandler
+ public void FoodLevelChangeEvent(FoodLevelChangeEvent event) {
+ // INFINITE FOOD (YAY!!!!)
+ if (event.getEntity().getWorld() == gameWorld) {
+ event.setCancelled(true);
+ }
+ }
+
+ @EventHandler
+ public void EntityDamageEvent(EntityDamageEvent event) {
+ // 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() == gameWorld) {
+ 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);
+ }
+ }
+ }
+ }
+
+ @EventHandler
+ public void InventoryDragEvent(InventoryDragEvent event) {
+ if (event.getWhoClicked().getWorld() == gameWorld) {
+ event.setCancelled(true);
+ }
+ }
+
+ @EventHandler
+ public void PlayerRespwanEvent(PlayerRespawnEvent event) {
+ // Make sure players respawn in the correct location
+ if (game.gamePlayers.contains(event.getPlayer())) {
+ event.setRespawnLocation(game.arena.location);
+ }
+ }
+
+ // TODO: stop tile drops for pistons
+}
diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Game.java b/src/main/java/com/MylesAndMore/Tumble/game/Game.java
index cdaede9..8fa9289 100644
--- a/src/main/java/com/MylesAndMore/Tumble/game/Game.java
+++ b/src/main/java/com/MylesAndMore/Tumble/game/Game.java
@@ -1,113 +1,222 @@
package com.MylesAndMore.Tumble.game;
-import com.MylesAndMore.Tumble.plugin.Constants;
-
+import com.MylesAndMore.Tumble.plugin.ConfigManager;
+import com.MylesAndMore.Tumble.plugin.GameState;
+import com.MylesAndMore.Tumble.plugin.GameType;
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.event.HandlerList;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
+import static com.MylesAndMore.Tumble.Main.plugin;
+
/**
* Everything relating to the Tumble game
*/
public class Game {
- // Singleton class logic
- private static Game gameInstance;
- private Game() {
- gameWorld = Bukkit.getWorld(Constants.getGameWorld());
- gameSpawn = Objects.requireNonNull(gameWorld).getSpawnLocation();
- }
- public static Game getGame() {
- if (gameInstance == null) {
- gameInstance = new Game();
- }
- return gameInstance;
- }
- // Define local game vars
- private String gameState;
- private String gameType;
+ public final GameType type;
+ public final Arena arena;
+ public final World gameWorld;
+ public final List<Player> gamePlayers = new ArrayList<>();
+ private final Location gameSpawn;
+ private final HashMap<Player, Integer> gameWins = new HashMap<>();
+ public GameState gameState = GameState.WAITING;
+ public GameType roundType;
private int gameID = -1;
private int autoStartID = -1;
- private final World gameWorld;
- private final Location gameSpawn;
- private List<Player> gamePlayers;
- private List<Player> roundPlayers;
- private List<Integer> gameWins;
+ private List<Player> playersAlive;
+ private EventListener eventListener;
+
+ public Game(@NotNull Arena arena, @NotNull GameType type) {
+ this.arena = arena;
+ this.type = type;
+ this.gameWorld = arena.world;
+ this.gameSpawn = arena.location;
- private final Random Random = new Random();
+ }
/**
* 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) {
+ public void startGame() {
+
// Check if the game is starting or running
- if (Objects.equals(gameState, "starting")) { return false; }
- else if (Objects.equals(gameState, "running")) { return false; }
+ if (gameState != GameState.WAITING) {
+ return;
+ }
+
+ Bukkit.getServer().getScheduler().cancelTask(autoStartID);
+ autoStartID = -1;
+// if (waitingPlayers.size() < 2) {
+// return false;
+// }
+
+ eventListener = new EventListener(this);
+ Bukkit.getServer().getPluginManager().registerEvents(eventListener, plugin);
+
+ // clear area in case it did not get properly cleared
+ roundStart();
+ }
+
+ /**
+ * Starts a new round
+ */
+ private void roundStart() {
+ gameState = GameState.STARTING;
+ playersAlive = new ArrayList<>(gamePlayers);
+ // Put all players in spectator to prevent them from getting kicked for flying
+ setGamemode(gamePlayers, GameMode.SPECTATOR);
+ scatterPlayers(gamePlayers);
+ clearInventories(gamePlayers);
+ clearArena();
+ prepareGameType(type);
+ Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> {
+ // Begin the countdown sequence
+ countdown(() -> {
+ setGamemode(gamePlayers, GameMode.SURVIVAL);
+ gameState = GameState.RUNNING;
+ });
+ }, 100);
+ }
+
+ /**
+ * Type specific setup: Generating layers and giving items
+ * @param type can be either "shovels", "snowballs", or "mixed"
+ */
+ private void prepareGameType(GameType type) {
+ roundType = type; // note: may need deepcopy this for it to work properly
+ if (roundType.equals(GameType.MIXED)) {
+ // Randomly select either shovels or snowballs and re-run the method
+ Random random = new Random();
+ switch (random.nextInt(2)) {
+ case 0 -> roundType = GameType.SHOVELS;
+ case 1 -> roundType = GameType.SNOWBALLS;
+ }
+ }
+
+ switch (roundType) {
+ case SHOVELS -> {
+ Generator.generateLayersShovels(gameSpawn.clone());
+ ItemStack shovel = new ItemStack(Material.IRON_SHOVEL);
+ shovel.addEnchantment(Enchantment.SILK_TOUCH, 1);
+ giveItems(gamePlayers, 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(plugin, () -> {
+ 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(plugin, this::roundEnd, 3000);
+ }, 3160);
+ }
+ case SNOWBALLS -> {
+ Generator.generateLayersSnowballs(gameSpawn.clone());
+ giveItems(gamePlayers, new ItemStack(Material.SNOWBALL));
+
+ // End the round in 5m
+ gameID = Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, this::roundEnd, 6160);
+ }
+ }
+ }
+
+ /**
+ * Round end stuff: Finds and displays winner, starts next round if necessary
+ */
+ private void roundEnd() {
+ // 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)
+ playSound(gamePlayers, Sound.BLOCK_NOTE_BLOCK_PLING, SoundCategory.BLOCKS, 5, 0);
+ // Check if there was a definite winner or not
+ if (!playersAlive.isEmpty()) {
+ Player winner = playersAlive.get(0);
+ // Set the wins of the player to their current # of wins + 1
+ if (!gameWins.containsKey(winner)) {
+ gameWins.put(winner, 0);
+ }
+ gameWins.put(winner, gameWins.get(winner)+1);
+ if (gameWins.get(winner) == 3) {
+ gameEnd();
+ }
+ // If that player doesn't have three wins, nobody else does, so we need another round
+ else {
+ displayTitles(gamePlayers, ChatColor.RED + "Round over!", ChatColor.GOLD + winner.getName() + " has won the round!", 5, 60, 5);
+ Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, this::roundStart, 100);
+ }
+ }
else {
- // Define the gameType
- switch (type) {
- case "shovels", "snowballs", "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(Constants.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(Constants.getPlayersInLobby());
- } else {
- return false;
- }
+ displayTitles(gamePlayers, ChatColor.RED + "Round over!", ChatColor.GOLD + "Draw!", 5, 60, 5);
+ Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, this::roundStart, 100);
+ }
+ }
+
+ /**
+ * Game end stuff: Displays overall winner and teleports players to lobby
+ */
+ private void gameEnd() {
+ if (!gamePlayers.isEmpty()) {
+ Player winner = getPlayerWithMostWins(gameWins);
+ setGamemode(gamePlayers, GameMode.SPECTATOR);
+ clearInventories(gamePlayers);
+ if (winner != null) {
+ 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(plugin, () -> {
+ // First, check to see if there is a separate location to tp the winner to
+ if (ConfigManager.winnerLobby != null && winner != null) {
+ winner.teleport(ConfigManager.winnerLobby);
+ // Remove the winner from the game so they don't get double-tp'd
+ gamePlayers.remove(winner);
}
- default -> {
- // The game type in the config did not match a specified game type
- return false;
+ // Send all players back to lobby (spawn)
+ for (Player aPlayer : gamePlayers) {
+ aPlayer.teleport(Objects.requireNonNull(ConfigManager.lobby));
}
- }
- // Update the game/round players for later
- gamePlayers = new ArrayList<>(Constants.getPlayersInGame());
- roundPlayers = new ArrayList<>(Constants.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));
- // Put all players in spectator to prevent them from getting kicked for flying (this needs a delay bc servers are slow)
- Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Constants.getPlugin(), () -> setGamemode(gamePlayers, GameMode.SPECTATOR), 25);
- // Wait 5s (100t) for the clients to load in
- Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Constants.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(Constants.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(Constants.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(Constants.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);
+ }, 200);
+ }
+ HandlerList.unregisterAll(eventListener);
+ arena.game = null;
+ }
+
+ /**
+ * Force stops a game
+ */
+ public void killGame() {
+ Bukkit.getServer().getScheduler().cancelTask(gameID);
+ HandlerList.unregisterAll(eventListener);
+ arena.game = null;
+ }
+
+ /**
+ * Removes a player from the game
+ * Called when a player leaves the server, or if they issue the leave command
+ * @param p Player to remove
+ */
+ public void removePlayer(Player p) {
+ gamePlayers.remove(p);
+ if (gamePlayers.size() < 2) {
+ gameEnd();
+ }
+ p.teleport(ConfigManager.lobby);
+ }
+
+ public void addPlayer(Player p) {
+ gamePlayers.add(p);
+ if (gamePlayers.size() >= 2 && gameState == GameState.WAITING) {
+ autoStart();
}
- return true;
}
/**
@@ -115,217 +224,92 @@ public class Game {
*/
public void autoStart() {
// Wait for the player to load in
- Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Constants.getPlugin(), () -> {
- gameState = "waiting";
- displayActionbar(Constants.getPlayersInLobby(), ChatColor.GREEN + "Game will begin in 15 seconds!");
- playSound(Constants.getPlayersInLobby(), Sound.BLOCK_NOTE_BLOCK_CHIME, SoundCategory.BLOCKS, 1, 1);
- Constants.getMVWorldManager().loadWorld(Constants.getGameWorld());
+ Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> {
+ displayActionbar(gamePlayers, ChatColor.GREEN + "Game will begin in 15 seconds!");
+ playSound(gamePlayers, Sound.BLOCK_NOTE_BLOCK_CHIME, SoundCategory.BLOCKS, 1, 1);
// 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(Constants.getPlugin(), () -> startGame(Constants.getGameType()), 300);
+ autoStartID = Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, this::startGame, 300);
}, 50);
}
/**
- * Cancels a "waiting" automatic start
- */
- public void cancelStart() {
- Bukkit.getServer().getScheduler().cancelTask(Game.getGame().getAutoStartID());
- displayActionbar(Constants.getPlayersInLobby(), ChatColor.RED + "Game start cancelled!");
- playSound(Constants.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 (yes you Jacob)
- // Delay is needed because instant respawn is a lie (it's not actually instant)
- Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Constants.getPlugin(), () -> {
- player.teleport(gameSpawn);
- Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Constants.getPlugin(), () -> player.setGameMode(GameMode.SPECTATOR), 5);
- }, 5);
// remove that player (who just died) from the roundPlayersArray, effectively eliminating them,
- roundPlayers.remove(player);
+ playersAlive.remove(player);
// If there are less than 2 players in the game (1 just died),
- if (roundPlayers.size() < 2) {
- roundEnd(roundPlayers.get(0));
+ if (playersAlive.size() < 2) {
+ roundEnd();
}
}
- // Methods to get the game type and game state for other classes outside the Game
+ // utility functions
/**
- * @return The game's current state as a String ("waiting", "starting", "running", "complete")
- * Can also be null if not initialized.
+ * Teleports a list of players to the specified scatter locations in the gameWorld
+ * @param players a List of Players to teleport
*/
- public String getGameState() { return gameState; }
+ private void scatterPlayers(List<Player> players) {
+ 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)));
+ Collections.shuffle(scatterLocations);
+ for (Player aPlayer : players) {
+ if (!aPlayer.teleport(scatterLocations.get(0))) {
+ plugin.getLogger().info("dbg: FAILED TELEPORT");
+ }
+ scatterLocations.remove(0); // Remove that location so multiple players won't get the same one
+ }
+ }
/**
- * @return The Bukkit process ID of the autostart process, if applicable
- * Can also be null if not initialized, or -1 if the process failed to schedule.
+ * Displays the 3, 2, 1 countdown
+ * @param doAfter Will be executed after the countdown
*/
- public int getAutoStartID() { return autoStartID; }
-
+ private void countdown(Runnable doAfter) {
+ 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(plugin, () -> {
+ playSound(gamePlayers, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, SoundCategory.NEUTRAL, 5, 1);
+ displayTitles(gamePlayers, ChatColor.YELLOW + "2", null, 3, 10, 7);
+ Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> {
+ 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(plugin, () -> {
+ playSound(gamePlayers, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, SoundCategory.NEUTRAL, 5, 2);
+ displayTitles(gamePlayers, ChatColor.GREEN + "Go!", null, 1, 5, 1);
+ doAfter.run();
+ }, 20);
+ }, 20);
+ }, 20);
+ }
- 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
+ * Finds the player with the most wins
+ * @param list List of players and their number of wins
+ * @return Player with the most wins
*/
- 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.getSafeMaterialList());
- }
- 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.getSafeMaterialList());
+ private Player getPlayerWithMostWins(HashMap<Player, Integer> list) {
+ Player largest = null;
+ for (Player p: list.keySet()) {
+ if (largest == null || list.get(p) > list.get(largest)) {
+ largest = p;
}
- else if (Random.nextInt(4) == 2) {
- // Multi-tiered circle
- Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.SNOW_BLOCK), layers.getSafeMaterialList());
- 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.getSafeMaterialList());
- 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(Constants.getPlayersInGame(), shovel);
- }
- else if (Objects.equals(gameState, "starting")) {
- giveItems(Constants.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(Constants.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(Constants.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.getSafeMaterialList());
- 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.getSafeMaterialList());
- 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.getSafeMaterialList());
- 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.getSafeMaterialList());
- 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.getSafeMaterialList());
- 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.getSafeMaterialList());
- 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.getSafeMaterialList());
- 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.getSafeMaterialList());
- 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(Constants.getPlayersInGame(), new ItemStack(Material.SNOWBALL));
- }
- else if (Objects.equals(gameState, "starting")) {
- giveItems(Constants.getPlayersInLobby(), new ItemStack(Material.SNOWBALL));
- }
- // End the round in 5m
- gameID = Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Constants.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;
+ return largest;
}
/**
@@ -401,139 +385,13 @@ public class Game {
}
/**
- * Teleports a list of players to the specified scatter locations in the gameWorld
- * @param players a List of Players to teleport
+ * Clears old layers
+ * (as a fill command, this would be /fill ~-20 ~-20 ~-20 ~20 ~ ~20 relative to spawn)
*/
- private void scatterPlayers(List<Player> players) {
- 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)));
- Collections.shuffle(scatterLocations);
- for (Player aPlayer : players) {
- aPlayer.teleport(scatterLocations.get(0));
- scatterLocations.remove(0); // Remove that location so multiple players won't get the same one
- }
- }
-
- 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 the player to respawn before completely lagging the server ._.
- Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Constants.getPlugin(), () -> {
- generateLayers(gameType);
- // Wait 5s (100t) for tp method
- Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Constants.getPlugin(), () -> {
- // Kill all items (pistons are weird)
- for (Entity entity : gameWorld.getEntities()) {
- if (entity instanceof Item) {
- entity.remove();
- }
- }
- 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(Constants.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(Constants.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(Constants.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.clear();
- roundPlayers.addAll(gamePlayers);
- clearInventories(gamePlayers);
- displayTitles(gamePlayers, ChatColor.RED + "Round over!", ChatColor.GOLD + "Draw!", 5, 60, 5);
- Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Constants.getPlugin(), () -> {
- generateLayers(gameType);
- Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Constants.getPlugin(), () -> {
- for (Entity entity : gameWorld.getEntities()) {
- if (entity instanceof Item) {
- entity.remove();
- }
- }
- 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(Constants.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(Constants.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(Constants.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);
- 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(Constants.getPlugin(), () -> {
- // First, check to see if there is a separate location to tp the winner to
- if ((Constants.getPlugin().getConfig().getDouble("winnerTeleport.x") != 0) && (Constants.getPlugin().getConfig().getDouble("winnerTeleport.y") != 0) && (Constants.getPlugin().getConfig().getDouble("winnerTeleport.z") != 0)) {
- winner.teleport(new Location(Bukkit.getWorld(Constants.getLobbyWorld()), Constants.getPlugin().getConfig().getDouble("winnerTeleport.x"), Constants.getPlugin().getConfig().getDouble("winnerTeleport.y"), Constants.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(Objects.requireNonNull(Bukkit.getWorld(Constants.getLobbyWorld())).getSpawnLocation());
- }
- }, 200);
- gameState = "complete";
+ private void clearArena() {
+ 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);
}
}
diff --git a/src/main/java/com/MylesAndMore/Tumble/game/Generator.java b/src/main/java/com/MylesAndMore/Tumble/game/Generator.java
index ecaa1b7..c8ecb06 100644
--- a/src/main/java/com/MylesAndMore/Tumble/game/Generator.java
+++ b/src/main/java/com/MylesAndMore/Tumble/game/Generator.java
@@ -1,8 +1,6 @@
package com.MylesAndMore.Tumble.game;
-import org.bukkit.Location;
-import org.bukkit.Material;
-import org.bukkit.World;
+import org.bukkit.*;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
@@ -12,6 +10,131 @@ import java.util.*;
* Holds the methods that generate blocks in-game such as cylinders, cuboids, and block clumps.
*/
public class Generator {
+
+ /**
+ * Generates layers for a round of type shovels
+ * @param layer Location where the layers should start
+ */
+ public static void generateLayersShovels(Location layer) {
+ Random random = new Random();
+ Layers layers = new Layers();
+
+ 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.getSafeMaterialList());
+ }
+ 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.getSafeMaterialList());
+ }
+ else if (random.nextInt(4) == 2) {
+ // Multi-tiered circle
+ Generator.generateClumps(Generator.generateLayer(layer, 17, 1, Material.SNOW_BLOCK), layers.getSafeMaterialList());
+ 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.getSafeMaterialList());
+ 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());
+ }
+ }
+
+ /**
+ * Generates layers for round of type snowballs
+ * @param layer Location where the layers should start
+ */
+ public static void generateLayersSnowballs(Location layer) {
+ Random random = new Random();
+ Layers layers = new Layers();
+
+ 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.getSafeMaterialList());
+ 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.getSafeMaterialList());
+ 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.getSafeMaterialList());
+ 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.getSafeMaterialList());
+ 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.getSafeMaterialList());
+ 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.getSafeMaterialList());
+ 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.getSafeMaterialList());
+ 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.getSafeMaterialList());
+ 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());
+ }
+ }
+
/**
* Generates a layer (basically just a cylinder) as good as possible with blocks
* @param center The center of the layer (Location)
@@ -21,7 +144,7 @@ public class Generator {
*
* @return A list of Blocks containing all the blocks it just changed
*/
- public static List<Block> generateLayer(Location center, int radius, int height, Material material) {
+ private static List<Block> generateLayer(Location center, int radius, int height, Material material) {
int Cx = center.getBlockX();
int Cy = center.getBlockY();
int Cz = center.getBlockZ();
@@ -77,13 +200,13 @@ public class Generator {
* 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) {
+ private static void generateClumps(List<Block> blockList, List<Material> materialList) {
Random random = new Random();
// Make new lists so we can manipulate them
List<Block> blocks = new ArrayList<>(blockList);
List<Material> materials = new ArrayList<>(materialList);
Collections.shuffle(materials);
- while (blocks.size() > 0) {
+ while (!blocks.isEmpty()) {
Material randomMaterial = materials.get(random.nextInt(materials.size()));
Block aBlock = blocks.get(0);
aBlock.setType(randomMaterial);
diff --git a/src/main/java/com/MylesAndMore/Tumble/plugin/ConfigManager.java b/src/main/java/com/MylesAndMore/Tumble/plugin/ConfigManager.java
new file mode 100644
index 0000000..dae6dd5
--- /dev/null
+++ b/src/main/java/com/MylesAndMore/Tumble/plugin/ConfigManager.java
@@ -0,0 +1,152 @@
+package com.MylesAndMore.Tumble.plugin;
+
+import com.MylesAndMore.Tumble.game.Arena;
+import com.MylesAndMore.Tumble.game.Game;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.World;
+import org.bukkit.configuration.ConfigurationSection;
+import org.bukkit.configuration.file.FileConfiguration;
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.HashMap;
+import java.util.Objects;
+
+import static com.MylesAndMore.Tumble.Main.plugin;
+
+public class ConfigManager {
+ public static HashMap<String, Arena> arenas;
+ public static Location lobby;
+ public static Location winnerLobby;
+ public static Location waitArea;
+ public static boolean HideLeaveJoin;
+ public static int waitDuration;
+
+ /**
+ * Reads config file and populates values above
+ */
+ public static void readConfig() {
+ FileConfiguration config = plugin.getConfig();
+
+ // arenas
+ if (config.getConfigurationSection("arenas") == null) {
+ plugin.getLogger().warning("Section arenas is missing from config");
+ return;
+ }
+ arenas = new HashMap<>();
+ for (String arenaName: Objects.requireNonNull(config.getConfigurationSection("arenas")).getKeys(false)) {
+ ConfigurationSection section = Objects.requireNonNull(config.getConfigurationSection("arenas")).getConfigurationSection(arenaName);
+
+ Result<Location> res = readWorld(section);
+ if (!res.success) {
+ plugin.getLogger().warning("Failed to load arena "+arenaName+": "+res.error);
+ continue;
+ }
+
+ arenas.put(arenaName, new Arena(arenaName, res.value));
+ }
+
+ // lobby
+ {
+ Result<Location>res = readWorld(config.getConfigurationSection("lobby-spawn"));
+ if (!res.success) {
+ plugin.getLogger().warning("Failed to load lobby: "+res.error);
+ plugin.getLogger().severe("^ THIS IS REQUIRED, PLEASE FIX TO AVOID UNDEFINED BEHAVIOR");
+ }
+
+ lobby = res.value;
+ }
+
+ // winner lobby
+ if (config.getBoolean("enable-winner-lobby-spawn")) {
+ Result<Location>res = readWorld(config.getConfigurationSection("winner-lobby-spawn"));
+ if (!res.success) {
+ plugin.getLogger().warning("Failed to load winner lobby: "+res.error);
+ }
+
+ winnerLobby = res.value;
+ }
+
+ // wait area
+ if (config.getBoolean("enable-wait-area")) {
+ Result<Location>res = readWorld(config.getConfigurationSection("wait-area"));
+ if (!res.success) {
+ plugin.getLogger().warning("Failed to load winner lobby: "+res.error);
+ }
+
+ waitArea = res.value;
+ }
+
+ // other
+ HideLeaveJoin = config.getBoolean("hideJoinLeaveMessages");
+ waitDuration = config.getInt("wait-duration", 15);
+ }
+
+ /**
+ * tries to convert a config section in the following format to a world
+ * section:
+ * x:
+ * y:
+ * z:
+ * world:
+ * @param section the section in the yaml with x, y, z, and world as its children
+ * @return result of either:
+ * success = true and a world
+ * success = false and an error string
+ */
+ private static Result<Location> readWorld(@Nullable ConfigurationSection section) {
+
+ if (section == null) {
+ Result<Location> res = new Result<>();
+ res.success = false;
+ res.error = "Section missing from config";
+ return res;
+ }
+
+ double x = section.getDouble("x");
+ double y = section.getDouble("y");
+ double z = section.getDouble("x");
+ if (x==0 || y == 0 || z == 0) {
+ Result<Location> res = new Result<>();
+ res.success = false;
+ res.error = "Arena coordinates are missing or are zero. Coordinates cannot be zero.";
+ return res;
+ }
+
+ String worldName = section.getString("world");
+ if (worldName == null) {
+ Result<Location> res = new Result<>();
+ res.success = false;
+ res.error = "World name is missing";
+ return res;
+ }
+
+ World world = Bukkit.getWorld(worldName);
+ if (world == null) {
+ Result<Location> res = new Result<>();
+ res.success = false;
+ res.error = "Failed to load world " + worldName;
+ return res;
+ }
+
+ Result<Location> res = new Result<>();
+ res.success = true;
+ res.value = new Location(world,x,y,z);
+ return res;
+ }
+
+ /**
+ * Searches all arenas for a game that player p is in
+ * @param p Player to search for
+ * @return the game the player is in, or null if not found
+ */
+ public static Game findGamePlayerIsIn(Player p) {
+ for (Arena a : arenas.values()) {
+ if (a.game != null && a.game.gamePlayers.contains(p)) {
+ return a.game;
+ }
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/com/MylesAndMore/Tumble/plugin/Constants.java b/src/main/java/com/MylesAndMore/Tumble/plugin/Constants.java
deleted file mode 100644
index 118af23..0000000
--- a/src/main/java/com/MylesAndMore/Tumble/plugin/Constants.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.MylesAndMore.Tumble.plugin;
-
-import com.onarandombox.MultiverseCore.MultiverseCore;
-import com.onarandombox.MultiverseCore.api.MVWorldManager;
-import org.bukkit.Bukkit;
-import org.bukkit.entity.Player;
-import org.bukkit.plugin.Plugin;
-
-import java.util.List;
-import java.util.Objects;
-
-public class Constants {
- public static Plugin getPlugin() {
- return Bukkit.getServer().getPluginManager().getPlugin("tumble");
- }
- public static String getPermissionMessage() { return Constants.getPlugin().getConfig().getString("permissionMessage"); }
- public static String getGameWorld() { return Constants.getPlugin().getConfig().getString("gameWorld"); }
- public static String getLobbyWorld() { return Constants.getPlugin().getConfig().getString("lobbyWorld"); }
- public static String getGameType() { return Constants.getPlugin().getConfig().getString("gameMode"); }
- public static List<Player> getPlayersInGame() { return Objects.requireNonNull(Bukkit.getServer().getWorld(Constants.getGameWorld())).getPlayers(); }
- public static List<Player> getPlayersInLobby() { return Objects.requireNonNull(Bukkit.getServer().getWorld(Constants.getLobbyWorld())).getPlayers(); }
-
- public static MultiverseCore getMV() { return (MultiverseCore) Bukkit.getServer().getPluginManager().getPlugin("Multiverse-Core"); }
- public static MVWorldManager getMVWorldManager() { return getMV().getMVWorldManager(); }
-}
diff --git a/src/main/java/com/MylesAndMore/Tumble/plugin/EventListener.java b/src/main/java/com/MylesAndMore/Tumble/plugin/EventListener.java
deleted file mode 100644
index 9a4dd62..0000000
--- a/src/main/java/com/MylesAndMore/Tumble/plugin/EventListener.java
+++ /dev/null
@@ -1,222 +0,0 @@
-package com.MylesAndMore.Tumble.plugin;
-
-import java.util.Objects;
-
-import com.MylesAndMore.Tumble.game.Game;
-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.block.Action;
-import org.bukkit.event.block.BlockBreakEvent;
-import org.bukkit.event.block.BlockDropItemEvent;
-import org.bukkit.event.entity.*;
-import org.bukkit.event.inventory.InventoryDragEvent;
-import org.bukkit.event.player.*;
-import org.bukkit.inventory.ItemStack;
-import org.bukkit.util.Vector;
-
-/**
- * Tumble event listener for all plugin and game-related events.
- */
-public class EventListener implements Listener {
- @EventHandler
- public void PlayerJoinEvent(PlayerJoinEvent event) {
- // Hide/show join message accordingly
- if (Constants.getPlugin().getConfig().getBoolean("hideJoinLeaveMessages")) {
- event.setJoinMessage(null);
- }
- // Check if either of the worlds are not defined in config, if so, end to avoid any NPEs later on
- if (Constants.getGameWorld() == null || Constants.getLobbyWorld() == null) { return; }
- if (event.getPlayer().getWorld() == Bukkit.getWorld(Constants.getGameWorld())) {
- // Send the player back to the lobby if they try to join in the middle of a game
- event.getPlayer().teleport(Objects.requireNonNull(Bukkit.getWorld(Constants.getLobbyWorld())).getSpawnLocation());
- }
- if (Constants.getPlugin().getConfig().getBoolean("autoStart.enabled")) {
- if (Constants.getPlayersInLobby().size() == Constants.getPlugin().getConfig().getInt("autoStart.players")) {
- // The autoStart should begin if it is already enabled and the amount of players is correct; pass this to the Game
- Game.getGame().autoStart();
- }
- }
- }
-
- @EventHandler
- public void PlayerChangedWorldEvent(PlayerChangedWorldEvent event) {
- if (Constants.getGameWorld() == null || Constants.getLobbyWorld() == null) {
- return;
- }
- if (event.getPlayer().getWorld() == Bukkit.getWorld(Constants.getLobbyWorld())) {
- // Another event on which autostart could be triggered
- if (Constants.getPlugin().getConfig().getBoolean("autoStart.enabled")) {
- if (Constants.getPlayersInLobby().size() == Constants.getPlugin().getConfig().getInt("autoStart.players")) {
- Game.getGame().autoStart();
- }
- }
- }
- // Also check if the player left to another world and cancel autostart
- else if (event.getFrom() == Bukkit.getWorld(Constants.getLobbyWorld())) {
- if (Objects.equals(Game.getGame().getGameState(), "waiting")) {
- Game.getGame().cancelStart();
- }
- }
- }
-
- @EventHandler
- public void PlayerQuitEvent(PlayerQuitEvent event) {
- // Hide/show leave message accordingly
- if (Constants.getPlugin().getConfig().getBoolean("hideJoinLeaveMessages")) {
- event.setQuitMessage(null);
- }
- if (Constants.getLobbyWorld() == null) { return; }
- if (event.getPlayer().getWorld() == Bukkit.getWorld(Constants.getLobbyWorld())) {
- // Check if the game is in the process of autostarting, if so cancel
- if (Objects.equals(Game.getGame().getGameState(), "waiting")) {
- Game.getGame().cancelStart();
- }
- }
- }
-
- @EventHandler
- public void PlayerDeathEvent(PlayerDeathEvent event) {
- if (Constants.getGameWorld() == null) { return; }
- // Pass game deaths to the Game
- if (event.getEntity().getWorld() == Bukkit.getWorld(Constants.getGameWorld())) {
- Game.getGame().playerDeath(event.getEntity());
- }
- }
-
- @EventHandler
- public void PlayerItemDamageEvent(PlayerItemDamageEvent event) {
- if (Constants.getGameWorld() == null) { return; }
- // Remove item damage within games
- if (event.getPlayer().getWorld() == Bukkit.getWorld(Constants.getGameWorld())) {
- event.setCancelled(true);
- }
- }
-
- @EventHandler
- public void ProjectileLaunchEvent(ProjectileLaunchEvent event) {
- if (Constants.getGameWorld() == null) {
- return;
- }
- if (event.getEntity().getWorld() == Bukkit.getWorld(Constants.getGameWorld())) {
- if (event.getEntity() instanceof Snowball) {
- if (event.getEntity().getShooter() instanceof Player player) {
- // Prevent projectiles (snowballs) from being thrown before the game starts
- if (Objects.equals(Game.getGame().getGameState(), "starting")) {
- event.setCancelled(true);
- }
- else {
- // Give players a snowball when they've used one (infinite snowballs)
- Bukkit.getServer().getScheduler().runTask(Constants.getPlugin(), () -> player.getInventory().addItem(new ItemStack(Material.SNOWBALL, 1)));
- }
- }
- }
- }
- }
-
- @EventHandler
- public void ProjectileHitEvent(ProjectileHitEvent event) {
- if (Constants.getGameWorld() == null) { return; }
- else if (event.getHitBlock() == null) { return; }
- // Removes blocks that snowballs thrown by players have hit in the game world
- if (event.getHitBlock().getWorld() == Bukkit.getWorld(Constants.getGameWorld())) {
- if (event.getEntity() instanceof Snowball) {
- if (event.getEntity().getShooter() instanceof Player) {
- if (event.getHitBlock() != null) {
- if (event.getHitBlock().getLocation().distanceSquared(Objects.requireNonNull(Bukkit.getWorld(Constants.getGameWorld())).getSpawnLocation()) < 579) {
- event.getHitBlock().setType(Material.AIR);
- }
- }
- else if (event.getHitEntity() != null) {
- if (event.getHitEntity() instanceof Player hitPlayer) {
- // Also cancel any knockback
- Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Constants.getPlugin(), () -> hitPlayer.setVelocity(new Vector()));
- }
- }
- }
- }
- }
- }
-
- @EventHandler
- public void PlayerDropItemEvent(PlayerDropItemEvent event) {
- if (Constants.getGameWorld() == null) { return; }
- // Don't allow items to drop in the game world
- if (event.getPlayer().getWorld() == Bukkit.getWorld((Constants.getGameWorld()))) {
- event.setCancelled(true);
- }
- }
-
- @EventHandler
- public void PlayerMoveEvent(PlayerMoveEvent event) {
- if (Constants.getGameWorld() == null) { return; }
- // Cancel movement if the game is starting (so players can't move before the game starts)
- if (Objects.equals(Game.getGame().getGameState(), "starting")) {
- event.setCancelled(true);
- }
- }
-
- @EventHandler
- public void BlockDropItemEvent(BlockDropItemEvent event) {
- if (Constants.getGameWorld() == null) { return; }
- // If a block was going to drop an item (ex. snow dropping snowballs) in the game world, cancel it
- if (event.getBlock().getWorld() == Bukkit.getWorld(Constants.getGameWorld())) {
- event.setCancelled(true);
- }
- }
-
- @EventHandler
- public void PlayerInteractEvent(PlayerInteractEvent event) {
- if (Constants.getGameWorld() == null) { return; }
- // Remove blocks when clicked in the game world (all gamemodes require this functionality)
- if (event.getAction() == Action.LEFT_CLICK_BLOCK) {
- if (Objects.requireNonNull(event.getClickedBlock()).getWorld() == Bukkit.getWorld(Constants.getGameWorld())) {
- event.getClickedBlock().setType(Material.AIR);
- }
- }
- }
-
- @EventHandler
- public void BlockBreakEvent(BlockBreakEvent event) {
- if (Constants.getGameWorld() == null) { return; }
- // This just doesn't allow blocks to break in the gameWorld; the PlayerInteractEvent will take care of everything
- // This prevents any weird client-server desync
- if (event.getBlock().getWorld() == Bukkit.getWorld(Constants.getGameWorld())) {
- event.setCancelled(true);
- }
- }
-
- @EventHandler
- public void FoodLevelChangeEvent(FoodLevelChangeEvent event) {
- if (Constants.getGameWorld() == null) { return; }
- // INFINITE FOOD (YAY!!!!)
- if (event.getEntity().getWorld() == Bukkit.getWorld(Constants.getGameWorld())) {
- event.setCancelled(true);
- }
- }
-
- @EventHandler
- public void EntityDamageEvent(EntityDamageEvent event) {
- if (Constants.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(Constants.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);
- }
- }
- }
- }
-
- @EventHandler
- public void InventoryDragEvent(InventoryDragEvent event) {
- if (Constants.getGameWorld() == null) { return; }
- if (event.getWhoClicked().getWorld() == Bukkit.getWorld((Constants.getGameWorld()))) {
- event.setCancelled(true);
- }
- }
-
-}
diff --git a/src/main/java/com/MylesAndMore/Tumble/plugin/GameState.java b/src/main/java/com/MylesAndMore/Tumble/plugin/GameState.java
new file mode 100644
index 0000000..879a1f5
--- /dev/null
+++ b/src/main/java/com/MylesAndMore/Tumble/plugin/GameState.java
@@ -0,0 +1,7 @@
+package com.MylesAndMore.Tumble.plugin;
+
+public enum GameState {
+ WAITING,
+ STARTING,
+ RUNNING,
+}
diff --git a/src/main/java/com/MylesAndMore/Tumble/plugin/GameType.java b/src/main/java/com/MylesAndMore/Tumble/plugin/GameType.java
new file mode 100644
index 0000000..cf01c88
--- /dev/null
+++ b/src/main/java/com/MylesAndMore/Tumble/plugin/GameType.java
@@ -0,0 +1,11 @@
+package com.MylesAndMore.Tumble.plugin;
+
+public enum GameType {
+ SHOVELS,
+ SNOWBALLS,
+ MIXED;
+
+ public String toString() {
+ return this.name().toLowerCase();
+ }
+}
diff --git a/src/main/java/com/MylesAndMore/Tumble/plugin/Result.java b/src/main/java/com/MylesAndMore/Tumble/plugin/Result.java
new file mode 100644
index 0000000..68e1133
--- /dev/null
+++ b/src/main/java/com/MylesAndMore/Tumble/plugin/Result.java
@@ -0,0 +1,8 @@
+package com.MylesAndMore.Tumble.plugin;
+
+// java does not have result types (i miss rust </3) so i did this
+public class Result<T> {
+ public boolean success;
+ public T value;
+ public String error;
+}
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
index 68b4e3d..8413843 100644
--- a/src/main/resources/config.yml
+++ b/src/main/resources/config.yml
@@ -1,24 +1,39 @@
-# Customize the default game mode; options include: shovels, snowballs, mixed
-gameMode: mixed
-
-# Customize the auto start feature of Tumble; players can be up to 8
-autoStart:
- enabled: false
- players: 2
-
# Hides player join/leave messages in public chat
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!
+# Duration in seconds to wait for more players to join
+wait-duration: 15
+# Teleport players somewhere while waiting for the game to start
+# Keep in mind that these coordinates cannot be zero! Use something like 0.5 instead
+enable-wait-area: false
+wait-area:
+ x:
+ y:
+ z:
+ world:
+
+# Place where everyone is teleported to after a game ends REQUIRED
+# Keep in mind that these coordinates cannot be zero! Use something like 0.5 instead
+lobby-spawn:
+ x: 0.5
+ y: 100
+ z: 0.5
+ world: world
-# 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
-winnerTeleport:
+# Place that the winner is teleported after a game ends
+# Keep in mind that these coordinates cannot be zero! Use something like 0.5 instead
+enable-winner-lobby-spawn: false
+winner-lobby-spawn:
x:
y:
- z:
+ z:
+ world:
-# The plugin will populate these fields automatically
-lobbyWorld:
-gameWorld: \ No newline at end of file
+# Add/remove as you wish
+# Keep in mind that these coordinates cannot be zero! Use something like 0.5 instead
+arenas:
+ 'test':
+ x: 0.5
+ y: 60
+ z: 0.5
+ world: world \ No newline at end of file
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
index d070c3e..a890bdd 100644
--- a/src/main/resources/plugin.yml
+++ b/src/main/resources/plugin.yml
@@ -3,51 +3,51 @@ name: Tumble
version: 1.0.4
description: 'A Minecraft: Java Edition plugin recreating the Tumble minigame from Minecraft Legacy Console Edition.'
api-version: 1.16
-load: STARTUP
+load: POSTWORLD
author: MylesAndMore
website: https://github.com/MylesAndMore/Tumble
-depend:
- - Multiverse-Core
commands:
+ join:
+ description: Joins a Tumble match.
+ usage: '§cUsage: /tumble:join <arenaName> [gameType]'
+ permission: tumble.join
+ leave:
+ description: Quits a Tumble match.
+ usage: '§cUsage: /tumble:leave [arenaName]'
+ permission: tumble.leave
+ forcestart:
+ description: Force starts a Tumble match.
+ usage: '§cUsage: /tumble:forcestart [arenaName]'
+ permission: tumble.forcestart
+ forcestop:
+ description: Force stops a Tumble match.
+ usage: '§cUsage: /tumble:forcestop [arenaName]'
+ permission: tumble.forcestop
+ config:
+ description: Modify arenas and settings
+ usage: '§cUsage: /tumble:config *not implemented yet*'
+ permission: tumble.config
reload:
description: Reloads the plugin's config.
usage: '§cUsage: /tumble:reload'
permission: tumble.reload
- link:
- description: Links a world on the server as a lobby/game world.
- usage: '§cUsage: /tumble:link <world> (lobby|game)'
- permission: tumble.link
- aliases: [linkworld, link-world]
- start:
- description: Force starts a Tumble match with an optional game type.
- usage: '§cUsage: /tumble:start [gameType]'
- permission: tumble.start
- winlocation:
- description: Links the location to teleport the winning player of a game.
- usage: '§cUsage: /tumble:winlocation [x] [y] [z]'
- permission: tumble.winlocation
- aliases: [win-location, winloc, win-loc]
- autostart:
- description: Configures the auto start functions of Tumble.
- usage: '§cUsage: /tumble:autostart <playerAmount> [enable|disable]'
- permission: tumble.autostart
- aliases: [auto-start]
permissions:
- tumble.reload:
- description: Allows you to reload the plugin's config.
- default: op
- tumble.link:
- description: Allows you to link a world on the server as a lobby/game world.
- default: op
- tumble.start:
- description: Allows you to start a Tumble match.
+ tumble.join:
+ description: Allows you to join a Tumble match.
+ default: true
+ tumble.leave:
+ description: Allows you to leave a Tumble match.
+ default: true
+ tumble.forcestart:
+ description: Allows you to force start a Tumble match.
default: op
- tumble.winlocation:
- description: Allows you to link a win location.
+ tumble.forcestop:
+ description: Allows you to force stop a Tumble match.
default: op
- tumble.autostart:
- description: Allows you to set the autostart details of Tumble.
- default: op
- tumble.update:
+ tumble.config:
description: Allows you to get a notification if Tumble is out of date.
default: op
+ tumble.reload:
+ description: Allows you to reload the plugin's config.
+ default: op
+