diff options
author | sowgro <tpoke.ferrari@gmail.com> | 2024-06-09 14:29:05 -0400 |
---|---|---|
committer | sowgro <tpoke.ferrari@gmail.com> | 2024-06-09 14:29:05 -0400 |
commit | baee001a9eceeae9f12a5f701c138123ff21b177 (patch) | |
tree | 5402de4bc363eaefdda031a408fe2b98a70ac066 /src/main/java | |
parent | 2664109ebcc022e0f9e02c2bd173b70608a68a72 (diff) | |
download | Tumble-baee001a9eceeae9f12a5f701c138123ff21b177.tar.gz Tumble-baee001a9eceeae9f12a5f701c138123ff21b177.tar.bz2 Tumble-baee001a9eceeae9f12a5f701c138123ff21b177.zip |
Implement multi arena support, and various other improvements (pretty much a rewrite)
Diffstat (limited to 'src/main/java')
21 files changed, 1113 insertions, 1043 deletions
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; +} |