From a56513d85180c699b566783d7e1ce1f12d641aa4 Mon Sep 17 00:00:00 2001
From: Myles <mylesandmore9@gmail.com>
Date: Sat, 10 Dec 2022 01:23:40 -0600
Subject: 1AM COMMIT LETS GOOOO

- fixed about 20 bugs, glitches, exploits, you name it--Jacob can break anything (not even joking, did my first *real* playtest; it was...valuable...we'll leave it at that)
- added some sort of block balancing (want to improve on it later maybe?)
- change the prefix back to lowercase because we're emo or something (COMMONALITY SHUT UP)
- game env is now more controlled (thanks Jacob)
- can I go to bed now
---
 .../com/MylesAndMore/tumble/EventListener.java     | 120 +++++++++++++++++----
 src/main/java/com/MylesAndMore/tumble/Game.java    |  40 ++++++-
 src/main/java/com/MylesAndMore/tumble/Main.java    |  10 +-
 .../tumble/commands/SetWorldConfig.java            |   1 +
 src/main/resources/config.yml                      |   2 +-
 5 files changed, 141 insertions(+), 32 deletions(-)

(limited to 'src')

diff --git a/src/main/java/com/MylesAndMore/tumble/EventListener.java b/src/main/java/com/MylesAndMore/tumble/EventListener.java
index dd42ea2..a18a9d3 100644
--- a/src/main/java/com/MylesAndMore/tumble/EventListener.java
+++ b/src/main/java/com/MylesAndMore/tumble/EventListener.java
@@ -4,14 +4,15 @@ import java.util.Objects;
 
 import org.bukkit.Bukkit;
 import org.bukkit.Material;
+import org.bukkit.enchantments.Enchantment;
 import org.bukkit.entity.Player;
 import org.bukkit.entity.Snowball;
 import org.bukkit.event.EventHandler;
 import org.bukkit.event.Listener;
-import org.bukkit.event.entity.FoodLevelChangeEvent;
-import org.bukkit.event.entity.PlayerDeathEvent;
-import org.bukkit.event.entity.ProjectileHitEvent;
-import org.bukkit.event.entity.ProjectileLaunchEvent;
+import org.bukkit.event.block.Action;
+import org.bukkit.event.block.BlockBreakEvent;
+import org.bukkit.event.block.BlockDropItemEvent;
+import org.bukkit.event.entity.*;
 import org.bukkit.event.player.*;
 import org.bukkit.inventory.ItemStack;
 import org.bukkit.util.Vector;
@@ -105,20 +106,18 @@ public class EventListener implements Listener {
     }
 
     @EventHandler
-    public void ItemDamageEvent(PlayerItemDamageEvent event) {
+    public void PlayerItemDamageEvent(PlayerItemDamageEvent event) {
         if (TumbleManager.getGameWorld() == null) {
             return;
         }
-        // On a BlockBreakEvent,
-        // check to see if the block was broken in the gameWorld,
+        // On an ItemDamageEvent
+        // check to see if the item was damaged in the gameWorld,
         if (event.getPlayer().getWorld() == Bukkit.getWorld(TumbleManager.getGameWorld())) {
-            // If it was in the gameWorld, check if the roundType was shovels
-            if (Objects.equals(Game.getGame().getRoundType(), "shovels")) {
-                event.setCancelled(true);
-            }
+            event.setCancelled(true);
         }
     }
 
+    private long lastTimeP;
     @EventHandler
     public void ProjectileLaunchEvent(ProjectileLaunchEvent event) {
         if (TumbleManager.getGameWorld() == null) {
@@ -129,7 +128,22 @@ public class EventListener implements Listener {
         if (event.getEntity().getWorld() == Bukkit.getWorld(TumbleManager.getGameWorld())) {
             if (event.getEntity() instanceof Snowball) {
                 if (event.getEntity().getShooter() instanceof Player player) {
-                    player.getInventory().addItem(new ItemStack(Material.SNOWBALL, 1));
+                    // Check to see if the last snowball was thrown less than 200ms ago, if so, don't allow another
+                    if ((System.currentTimeMillis() - lastTimeP) < 200) { event.setCancelled(true); }
+                    else {
+                        // Otherwise, continue with logic
+                        lastTimeP = System.currentTimeMillis();
+                        // This prevents players from shooting snowballs before the game actually begins
+                        if (Objects.equals(Game.getGame().getGameState(), "starting")) {
+                            event.setCancelled(true);
+                        }
+                        else {
+                            // This gives players a snowball when they've used one
+                            Bukkit.getServer().getScheduler().runTask(TumbleManager.getPlugin(), () -> {
+                                player.getInventory().addItem(new ItemStack(Material.SNOWBALL, 1));
+                            });
+                        }
+                    }
                 }
             }
         }
@@ -140,6 +154,10 @@ public class EventListener implements Listener {
         if (TumbleManager.getGameWorld() == null) {
             return;
         }
+        // Weird stacktrace thing
+        else if (event.getHitBlock() == null) {
+            return;
+        }
         // When a projectile hits
         // check to see if the projectile hit in the gameWorld,
         if (event.getHitBlock().getWorld() == Bukkit.getWorld(TumbleManager.getGameWorld())) {
@@ -151,8 +169,8 @@ public class EventListener implements Listener {
                     if (event.getHitBlock() != null) {
                         // if it was a block, check if that block is within the game area,
                         if (event.getHitBlock().getLocation().distanceSquared(Bukkit.getWorld(TumbleManager.getGameWorld()).getSpawnLocation()) < 402) {
-                        // then remove that block.
-                        event.getHitBlock().setType(Material.AIR);
+                            // then remove that block.
+                            event.getHitBlock().setType(Material.AIR);
                         }
                     }
                     else if (event.getHitEntity() != null) {
@@ -161,15 +179,11 @@ public class EventListener implements Listener {
                             // then cancel the knockback (has to be delayed by a tick for some reason)
                             Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(TumbleManager.getPlugin(), () -> {
                                 hitPlayer.setVelocity(new Vector());
-                            }, 1);
+                            });
                         }
                     }
                 }
             }
-            // Weird stacktrace thing
-            else if (event.getHitBlock().getWorld() == null) {
-                event.setCancelled(true);
-            }
         }
     }
 
@@ -187,10 +201,58 @@ public class EventListener implements Listener {
 
     @EventHandler
     public void PlayerMoveEvent(PlayerMoveEvent event) {
+        if (TumbleManager.getGameWorld() == null) {
+            return;
+        }
         // On a PlayerMoveEvent, check if the game is starting
         if (Objects.equals(Game.getGame().getGameState(), "starting")) {
-            // Cancel the event if the game is starting (so players can't move before the
-            // game starts)
+            // Cancel the event if the game is starting (so players can't move before the game starts)
+            event.setCancelled(true);
+        }
+    }
+
+    @EventHandler
+    public void BlockDropItemEvent(BlockDropItemEvent event) {
+        if (TumbleManager.getGameWorld() == null) {
+            return;
+        }
+        // If a block was going to drop an item (ex. snow dropping snowballs) in the GameWorld, cancel it
+        if (event.getBlock().getWorld() == Bukkit.getWorld(TumbleManager.getGameWorld())) {
+            event.setCancelled(true);
+        }
+    }
+
+    private long lastTimeI;
+    @EventHandler
+    public void PlayerInteractEvent(PlayerInteractEvent event) {
+        if (TumbleManager.getGameWorld() == null) {
+            return;
+        }
+        // Check if a player was left clicking a block in the gameWorld
+        if (event.getAction() == Action.LEFT_CLICK_BLOCK) {
+            if (event.getClickedBlock().getWorld() == Bukkit.getWorld(TumbleManager.getGameWorld())) {
+                // Then check if it was with an item enchanted w/ silk touch
+                if (event.getPlayer().getInventory().getItemInMainHand().containsEnchantment(Enchantment.SILK_TOUCH)) {
+                    // Then check to see if the player interacted less than 150ms ago
+                    if ((System.currentTimeMillis() - lastTimeI) < 150) return;
+                    // If not, set that block to air (break it)
+                    else {
+                        lastTimeI = System.currentTimeMillis();
+                        event.getClickedBlock().setType(Material.AIR);
+                    }
+                }
+            }
+        }
+    }
+
+    @EventHandler
+    public void BlockBreakEvent(BlockBreakEvent event) {
+        if (TumbleManager.getGameWorld() == null) {
+            return;
+        }
+        // This just doesn't allow blocks to break in the gameWorld; the PlayerInteractEvent will take care of everything
+        // It just keeps client commonality w/ animations and stuff
+        if (event.getBlock().getWorld() == Bukkit.getWorld(TumbleManager.getGameWorld())) {
             event.setCancelled(true);
         }
     }
@@ -200,10 +262,22 @@ public class EventListener implements Listener {
         if (TumbleManager.getGameWorld() == null) {
             return;
         }
-        // When someone's food level changes
-        // check if that happened in the gameWorld
+        // When someone's food level changes, check if that happened in the gameWorld, then cancel it
         if (event.getEntity().getWorld() == Bukkit.getWorld(TumbleManager.getGameWorld())) {
             event.setCancelled(true);
         }
     }
+
+    @EventHandler
+    public void EntityDamageEvent(EntityDamageEvent event) {
+        if (TumbleManager.getGameWorld() == null) {
+            return;
+        }
+        // Check to see if a player got damaged in the gameWorld, if so, cancel it
+        if (event.getEntity().getWorld() == Bukkit.getWorld(TumbleManager.getGameWorld())) {
+            if (event.getEntity() instanceof Player) {
+                event.setCancelled(true);
+            }
+        }
+    }
 }
diff --git a/src/main/java/com/MylesAndMore/tumble/Game.java b/src/main/java/com/MylesAndMore/tumble/Game.java
index 6d085df..7de988a 100644
--- a/src/main/java/com/MylesAndMore/tumble/Game.java
+++ b/src/main/java/com/MylesAndMore/tumble/Game.java
@@ -6,6 +6,7 @@ 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.Player;
 import org.bukkit.inventory.ItemStack;
 import org.jetbrains.annotations.NotNull;
@@ -78,6 +79,8 @@ public class Game {
                 gameState = "starting";
                 // Set the roundType to gameType since it won't change for this mode
                 roundType = type;
+                // Clear the players' inventories so they can't bring any items into the game
+                clearInventories(TumbleManager.getPlayersInLobby());
                 // Generate the correct layers for a Shovels game
                 // The else statement is just in case the generator fails; this command will fail
                 if (generateLayers(type)) {
@@ -92,6 +95,7 @@ public class Game {
             else if (Objects.equals(type, "snowballs")) {
                 gameState = "starting";
                 roundType = type;
+                clearInventories(TumbleManager.getPlayersInLobby());
                 if (generateLayers(type)) {
                     scatterPlayers(TumbleManager.getPlayersInLobby());
                 }
@@ -102,6 +106,7 @@ public class Game {
             else if (Objects.equals(type, "mixed")) {
                 gameState = "starting";
                 roundType = type;
+                clearInventories(TumbleManager.getPlayersInLobby());
                 if (generateLayers(type)) {
                     scatterPlayers(TumbleManager.getPlayersInLobby());
                 }
@@ -187,7 +192,7 @@ public class Game {
         // Otherwise, the game must have two people left (and one just died), meaning it is over
         // This logic is so that it will not remove the last player standing from the list, so we know who the winner is.
         else {
-            // roundPlayers.remove(player);
+            roundPlayers.remove(player);
             // End the game, passing the winner to the gameEnd method
             roundEnd(roundPlayers.get(0));
         }
@@ -229,11 +234,19 @@ public class Game {
             Generator.generateLayer(layer, 13, 1, Material.AIR);
             layer.setY(layer.getY() - 1);
             Generator.generateLayer(layer, 13, 1, Material.GRASS_BLOCK);
+            Generator.generateLayer(layer, 4, 1, Material.AIR);
             layer.setY(layer.getY() - 1);
             Generator.generateLayer(layer, 4, 1, Material.PODZOL);
             layer.setY(layer.getY() + 2);
             Generator.generateLayer(layer, 4, 2, Material.TALL_GRASS);
-            giveItems(TumbleManager.getPlayersInLobby(), new ItemStack(Material.IRON_SHOVEL));
+            ItemStack shovel = new ItemStack(Material.IRON_SHOVEL);
+            shovel.addEnchantment(Enchantment.SILK_TOUCH, 1);
+            if (Objects.equals(gameState, "running")) {
+                giveItems(TumbleManager.getPlayersInGame(), shovel);
+            }
+            else if (Objects.equals(gameState, "starting")) {
+                giveItems(TumbleManager.getPlayersInLobby(), shovel);
+            }
         }
         else if (Objects.equals(type, "snowballs")) {
             layer.setY(layer.getY() - 1);
@@ -244,7 +257,12 @@ public class Game {
             Generator.generateLayer(layer, 4, 1, Material.AIR);
             layer.setY(layer.getY() - 1);
             Generator.generateLayer(layer, 4, 1, Material.LIME_GLAZED_TERRACOTTA);
-            giveItems(TumbleManager.getPlayersInLobby(), new ItemStack(Material.SNOWBALL));
+            if (Objects.equals(gameState, "running")) {
+                giveItems(TumbleManager.getPlayersInGame(), new ItemStack(Material.SNOWBALL));
+            }
+            else if (Objects.equals(gameState, "starting")) {
+                giveItems(TumbleManager.getPlayersInLobby(), new ItemStack(Material.SNOWBALL));
+            }
         }
         else if (Objects.equals(type, "mixed")) {
             // Randomly select either shovels or snowballs and re-run the method
@@ -273,6 +291,16 @@ public class Game {
         }
     }
 
+    /**
+     * Clears the inventories of a provided player list
+     * @param players The player list for which to clear the inventories of
+     */
+    private void clearInventories(List<Player> players) {
+        for (Player aPlayer : players) {
+            aPlayer.getInventory().clear();
+        }
+    }
+
     /**
      * Sets the gamemodes of a provided list of players
      * @param players The player list for which to set the gamemodes of
@@ -370,6 +398,10 @@ public class Game {
         }
         // 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);
             // Re-generate layers
             generateLayers(roundType);
@@ -399,6 +431,8 @@ public class Game {
     }
 
     private void gameEnd(Player winner) {
+        winner.setGameMode(GameMode.SPECTATOR);
+        clearInventories(gamePlayers);
         // Announce win
         displayTitles(gamePlayers, ChatColor.RED + "Game over!", ChatColor.GOLD + winner.getName() + " has won the game!", 5, 60, 5);
         displayActionbar(gamePlayers, ChatColor.BLUE + "Returning to lobby in ten seconds...");
diff --git a/src/main/java/com/MylesAndMore/tumble/Main.java b/src/main/java/com/MylesAndMore/tumble/Main.java
index a35a519..16f32c4 100644
--- a/src/main/java/com/MylesAndMore/tumble/Main.java
+++ b/src/main/java/com/MylesAndMore/tumble/Main.java
@@ -25,15 +25,15 @@ public class Main extends JavaPlugin{
 
         // Check if worlds are null in config
         if (TumbleManager.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.");
+            Bukkit.getServer().getLogger().warning("[tumble] It appears you have not configured a game world for Tumble.");
+            Bukkit.getServer().getLogger().info("[tumble] If this is your first time running the plugin, you may disregard this message.");
         }
         if (TumbleManager.getLobbyWorld() == null) {
-            Bukkit.getServer().getLogger().warning("[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.");
+            Bukkit.getServer().getLogger().warning("[tumble] It appears you have not configured a lobby world for Tumble.");
+            Bukkit.getServer().getLogger().info("[tumble] If this is your first time running the plugin, you may disregard this message.");
         }
 
         // Init message
-        Bukkit.getServer().getLogger().info("[Tumble] Tumble initialization complete!");
+        Bukkit.getServer().getLogger().info("[tumble] Tumble initialization complete!");
     }
 }
\ No newline at end of file
diff --git a/src/main/java/com/MylesAndMore/tumble/commands/SetWorldConfig.java b/src/main/java/com/MylesAndMore/tumble/commands/SetWorldConfig.java
index 74eaf0d..695c248 100644
--- a/src/main/java/com/MylesAndMore/tumble/commands/SetWorldConfig.java
+++ b/src/main/java/com/MylesAndMore/tumble/commands/SetWorldConfig.java
@@ -52,6 +52,7 @@ public class SetWorldConfig implements CommandExecutor {
                             TumbleManager.getPlugin().saveConfig();
                             // Set the gamerule of doImmediateRespawn in the gameWorld for later
                             Bukkit.getWorld(world).setGameRule(GameRule.DO_IMMEDIATE_RESPAWN, true);
+                            Bukkit.getWorld(world).setGameRule(GameRule.KEEP_INVENTORY, true);
                             sender.sendMessage(ChatColor.GREEN + "Game world successfully linked: " + ChatColor.GRAY + world);
                             sender.sendMessage(ChatColor.GREEN + "Please restart your server for the changes to take effect; " + ChatColor.RED + "reloading the plugin is insufficient!");
                         }
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
index 6a9f62d..bc68c35 100644
--- a/src/main/resources/config.yml
+++ b/src/main/resources/config.yml
@@ -1,5 +1,5 @@
 # Hides join/leave messages in public chat
-# Default is true
+# Default is false
 hideJoinLeaveMessages: false
 
 # Customize the message that displays when the player does not have permission to execute a command from this plugin
-- 
cgit v1.2.3