diff --git a/src/main/java/lar/minecraft/hg/ServerSchedulers.java b/src/main/java/lar/minecraft/hg/ServerSchedulers.java index 9932f50..31f740c 100644 --- a/src/main/java/lar/minecraft/hg/ServerSchedulers.java +++ b/src/main/java/lar/minecraft/hg/ServerSchedulers.java @@ -94,6 +94,7 @@ public static void lobbyPhase() { ServerManager.getLivingPlayers().forEach(p -> { p.setGameMode(GameMode.ADVENTURE); p.getInventory().clear(); + p.getInventory().addItem(ServerManager.getGameInstructionsBook()); }); gameStartTime = 0; lobbyPhaseTaskId = server.getScheduler().scheduleSyncRepeatingTask(plugin, new Runnable() { diff --git a/src/main/java/lar/minecraft/hg/SpigotPlugin.java b/src/main/java/lar/minecraft/hg/SpigotPlugin.java index ec2b48f..1f7e1fc 100644 --- a/src/main/java/lar/minecraft/hg/SpigotPlugin.java +++ b/src/main/java/lar/minecraft/hg/SpigotPlugin.java @@ -4,7 +4,6 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.sql.SQLException; -import java.util.Arrays; import java.util.Properties; import org.bukkit.Difficulty; @@ -20,7 +19,6 @@ import lar.minecraft.hg.enums.Cmd; import lar.minecraft.hg.enums.ConfigProperty; import lar.minecraft.hg.enums.HGPhase; -import lar.minecraft.hg.enums.PlayerClass; import lar.minecraft.hg.managers.DatabaseManager; import lar.minecraft.hg.managers.PlayerManager; import lar.minecraft.hg.managers.ServerManager; @@ -109,11 +107,10 @@ public void onEnable() { // Enable game commands getCommand(Cmd.scoreboard).setExecutor(new ScoreboardCommand()); getCommand(Cmd.scoreboard).setTabCompleter(new ScoreboardCommand()); - + // Enable class selection commands - Arrays.asList(PlayerClass.values()).forEach(c -> { - getCommand(c.name()).setExecutor(new ClassCommand()); - }); + getCommand(Cmd.class_command).setExecutor(new ClassCommand()); + getCommand(Cmd.class_command).setTabCompleter(new ClassCommand()); // Initialize PlayerManager listener getServer().getPluginManager().registerEvents(new PlayerManager(), this); diff --git a/src/main/java/lar/minecraft/hg/commands/ClassCommand.java b/src/main/java/lar/minecraft/hg/commands/ClassCommand.java index f356c75..6f30b59 100644 --- a/src/main/java/lar/minecraft/hg/commands/ClassCommand.java +++ b/src/main/java/lar/minecraft/hg/commands/ClassCommand.java @@ -1,46 +1,76 @@ -package lar.minecraft.hg.commands; - -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; - -import lar.minecraft.hg.SpigotPlugin; -import lar.minecraft.hg.entities.PlayerExtra; -import lar.minecraft.hg.enums.MessageKey; -import lar.minecraft.hg.enums.PlayerClass; -import lar.minecraft.hg.managers.PlayerManager; -import lar.minecraft.hg.utils.MessageUtils; - -public class ClassCommand implements CommandExecutor { - - @Override - public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { - PlayerClass cmdName = PlayerClass.valueOf(command.getName().toLowerCase()); - - if (sender instanceof Player) { - Player player = (Player) sender; - if (cmdName != null && !PlayerManager.playerExtras.isEmpty() && (SpigotPlugin.isWaitingForStart() || SpigotPlugin.isLobby())) { - PlayerExtra playerExtra = PlayerManager.playerExtras.get(player.getUniqueId()); - - if (playerExtra != null) { - // Check command selection: only premium users or who won last match can use premium classes - if (!cmdName.isPremium() || playerExtra.isPremium() || playerExtra.isLastWinner()) { - playerExtra.setPlayerClass(cmdName); - PlayerManager.playerExtras.put(player.getUniqueId(), playerExtra); - player.sendMessage(MessageUtils.getMessage(MessageKey.class_selected, cmdName.name())); - player.playSound(player, cmdName.getSound(), 10.0f, 10.0f); - } else { - player.sendMessage(MessageUtils.getMessage(MessageKey.class_premium)); - } - } else { - return false; - } - } else { - player.sendMessage(MessageUtils.getMessage(MessageKey.class_not_selected)); - } - } - return true; - } - -} +package lar.minecraft.hg.commands; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabExecutor; +import org.bukkit.entity.Player; +import org.bukkit.util.StringUtil; + +import lar.minecraft.hg.SpigotPlugin; +import lar.minecraft.hg.entities.PlayerExtra; +import lar.minecraft.hg.enums.MessageKey; +import lar.minecraft.hg.enums.PlayerClass; +import lar.minecraft.hg.managers.PlayerManager; +import lar.minecraft.hg.utils.MessageUtils; + +public class ClassCommand implements CommandExecutor, TabExecutor { + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (sender instanceof Player) { + Player player = (Player) sender; + + if (args.length == 1) { + PlayerClass className = PlayerClass.valueOf(args[0].toLowerCase()); + + if (className != null && !PlayerManager.playerExtras.isEmpty() && (SpigotPlugin.isWaitingForStart() || SpigotPlugin.isLobby())) { + PlayerExtra playerExtra = PlayerManager.playerExtras.get(player.getUniqueId()); + + if (playerExtra != null) { + // Check command selection: only premium users or who won last match can use premium classes + if (!className.isPremium() || playerExtra.isPremium() || playerExtra.isLastWinner()) { + playerExtra.setPlayerClass(className); + PlayerManager.playerExtras.put(player.getUniqueId(), playerExtra); + player.sendMessage(MessageUtils.getMessage(MessageKey.class_selected, className.name())); + player.playSound(player, className.getSound(), 10.0f, 10.0f); + } else { + player.sendMessage(MessageUtils.getMessage(MessageKey.class_premium)); + } + } else { + player.sendMessage(MessageUtils.getMessage(MessageKey.class_not_selected)); + return true; + } + } else { + player.sendMessage(MessageUtils.getMessage(MessageKey.class_not_selected)); + } + } else { + player.sendMessage(MessageUtils.getMessage(MessageKey.class_not_selected)); + return true; + } + } + + return true; + } + + @Override + public List onTabComplete(CommandSender sender, Command cmd, String label, String[] args) { + List completions = new ArrayList<>(); + + if (args.length == 1) { + List tempList = Arrays.stream(PlayerClass.values()) + .map(Enum::name) + .collect(Collectors.toList()); + StringUtil.copyPartialMatches(args[0], tempList, completions); + return completions; + } + + Collections.sort(completions); + return Collections.emptyList(); + } +} diff --git a/src/main/java/lar/minecraft/hg/enums/Cmd.java b/src/main/java/lar/minecraft/hg/enums/Cmd.java index 3cdb969..2bfea87 100644 --- a/src/main/java/lar/minecraft/hg/enums/Cmd.java +++ b/src/main/java/lar/minecraft/hg/enums/Cmd.java @@ -11,6 +11,7 @@ public final class Cmd { public static final String restart_hg_server = "restart-hg-server"; public static final String start_hg = "start-hg"; public static final String scoreboard = "scoreboard"; + public static final String class_command = "class"; // Private constructor to prevent instantiation private Cmd() {} diff --git a/src/main/java/lar/minecraft/hg/enums/MessageKey.java b/src/main/java/lar/minecraft/hg/enums/MessageKey.java index ac323a0..ad5dffd 100644 --- a/src/main/java/lar/minecraft/hg/enums/MessageKey.java +++ b/src/main/java/lar/minecraft/hg/enums/MessageKey.java @@ -1,12 +1,17 @@ package lar.minecraft.hg.enums; public enum MessageKey { + welcome_message, current_phase, class_selected, class_selection_lobby, class_wrong, class_premium, class_not_selected, + class_instructions, + class_instructions_premium, + class_instructions_materials, + class_instructions_select, supply_drop_alert, supply_drop, last_match_win, diff --git a/src/main/java/lar/minecraft/hg/enums/PlayerClass.java b/src/main/java/lar/minecraft/hg/enums/PlayerClass.java index 5b5d5b1..d907dda 100644 --- a/src/main/java/lar/minecraft/hg/enums/PlayerClass.java +++ b/src/main/java/lar/minecraft/hg/enums/PlayerClass.java @@ -1,5 +1,8 @@ package lar.minecraft.hg.enums; +import java.util.HashMap; +import java.util.Map; + import org.bukkit.Material; import org.bukkit.Sound; import org.bukkit.entity.Player; @@ -11,23 +14,39 @@ import org.bukkit.potion.PotionEffectType; public enum PlayerClass { - miner (Sound.ENTITY_ITEM_BREAK, false) { + miner (Sound.ENTITY_ITEM_BREAK, "Dig in, my friend!", false) { @Override public PlayerAction getAction() { return (player) -> { player.getInventory().addItem(new ItemStack(Material.STONE_PICKAXE), new ItemStack(Material.TORCH, 8)); }; } + + @Override + public Map getMaterials() { + Map result = new HashMap(); + result.put(Material.STONE_PICKAXE.toString(), 1); + result.put(Material.TORCH.toString(), 8); + return result; + } }, - bowman (Sound.ITEM_CROSSBOW_HIT, false) { + bowman (Sound.ITEM_CROSSBOW_HIT, "Shot other players with a bow and some arrows", false) { @Override public PlayerAction getAction() { return (player) -> { player.getInventory().addItem(new ItemStack(Material.BOW), new ItemStack(Material.ARROW, 16)); }; } + + @Override + public Map getMaterials() { + Map result = new HashMap(); + result.put(Material.BOW.toString(), 1); + result.put(Material.ARROW.toString(), 16); + return result; + } }, - armored (Sound.ITEM_ARMOR_EQUIP_LEATHER, true) { + armored (Sound.ITEM_ARMOR_EQUIP_LEATHER, "Cover yourself with a full leather armor set", true) { @Override public PlayerAction getAction() { return (player) -> { @@ -38,8 +57,15 @@ public PlayerAction getAction() { playerInventory.setBoots(new ItemStack(Material.LEATHER_BOOTS)); }; } + + @Override + public Map getMaterials() { + Map result = new HashMap(); + result.put("LEATHER ARMOR SET", 1); + return result; + } }, - doglover (Sound.ENTITY_WOLF_PANT, false) { + doglover (Sound.ENTITY_WOLF_PANT, "Tame the nature and kill other players with your dog", false) { @Override public PlayerAction getAction() { return (player) -> { @@ -51,8 +77,16 @@ public PlayerAction getAction() { dog.setSitting(false); }; } + + @Override + public Map getMaterials() { + Map result = new HashMap(); + result.put("TAMED DOG", 1); + result.put(Material.BONE.toString(), 8); + return result; + } }, - lavaman (Sound.ITEM_BUCKET_FILL_LAVA, true) { + lavaman (Sound.ITEM_BUCKET_FILL_LAVA, "Burn BURN BURN!!!", true) { @Override public PlayerAction getAction() { return (player) -> { @@ -60,45 +94,80 @@ public PlayerAction getAction() { new ItemStack(Material.LAVA_BUCKET), new ItemStack(Material.LAVA_BUCKET)); }; } + + @Override + public Map getMaterials() { + Map result = new HashMap(); + result.put(Material.LAVA_BUCKET.toString(), 2); + return result; + } }, - mole (Sound.BLOCK_ROOTED_DIRT_PLACE, false) { + mole (Sound.BLOCK_ROOTED_DIRT_PLACE, "Dig out, my friend!", false) { @Override public PlayerAction getAction() { return (player) -> { player.getInventory().addItem(new ItemStack(Material.DIRT, 32)); }; } + + @Override + public Map getMaterials() { + Map result = new HashMap(); + result.put(Material.DIRT.toString(), 32); + return result; + } }, - ghost (Sound.ENTITY_WANDERING_TRADER_DRINK_POTION, true) { + ghost (Sound.ENTITY_WANDERING_TRADER_DRINK_POTION, "Let's get invisible", true) { @Override public PlayerAction getAction() { return (player) -> { ItemStack potion = new ItemStack(Material.POTION, 2); PotionMeta meta = (PotionMeta) potion.getItemMeta(); meta.addCustomEffect(new PotionEffect(PotionEffectType.INVISIBILITY, 600, 1), true); // 600 ticks = 30 seconds - meta.setDisplayName("Potion of Invisibility"); + meta.setDisplayName("Invisibility Potion"); potion.setItemMeta(meta); player.getInventory().addItem(potion); }; } + + @Override + public Map getMaterials() { + Map result = new HashMap(); + result.put("INVISIBILITY POTION", 2); + return result; + } }, - teleporter (Sound.ENTITY_ENDERMAN_TELEPORT, false) { + teleporter (Sound.ENTITY_ENDERMAN_TELEPORT, "I'm here... not anymore", false) { @Override public PlayerAction getAction() { return (player) -> { player.getInventory().addItem(new ItemStack(Material.ENDER_PEARL, 3)); }; } + + @Override + public Map getMaterials() { + Map result = new HashMap(); + result.put(Material.ENDER_PEARL.toString(), 3); + return result; + } }, - barbarian (Sound.ENTITY_PLAYER_ATTACK_SWEEP, false) { + barbarian (Sound.ENTITY_PLAYER_ATTACK_SWEEP, "WWOOWOWOW", false) { @Override public PlayerAction getAction() { return (player) -> { player.getInventory().addItem(new ItemStack(Material.STONE_SWORD)); }; } + + @Override + public Map getMaterials() { + Map result = new HashMap(); + result.put(Material.STONE_SWORD.toString(), 1); + return result; + } }, - hardcore (Sound.ENTITY_PLAYER_BURP, false) { + hardcore (Sound.ENTITY_PLAYER_BURP, "Are you crazy!?!?", false) { @Override public PlayerAction getAction() { return (player) -> { @@ -106,13 +175,22 @@ public PlayerAction getAction() { playerInventory.setHelmet(new ItemStack(Material.CARVED_PUMPKIN)); }; } + + @Override + public Map getMaterials() { + Map result = new HashMap(); + result.put(Material.CARVED_PUMPKIN.toString(), 1); + return result; + } }; private Sound sound = Sound.INTENTIONALLY_EMPTY; + private String description = ""; private boolean premium = false; - PlayerClass(Sound sound, boolean premium) { + PlayerClass(Sound sound, String description, boolean premium) { this.sound = sound; + this.description = description; this.premium = premium; } @@ -125,6 +203,14 @@ public Sound getSound() { public void setSound(Sound sound) { this.sound = sound; } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } public boolean isPremium() { return premium; @@ -141,5 +227,6 @@ public interface PlayerAction { // Abstract method to get the action for each class abstract public PlayerAction getAction(); + abstract public Map getMaterials(); } diff --git a/src/main/java/lar/minecraft/hg/managers/PlayerManager.java b/src/main/java/lar/minecraft/hg/managers/PlayerManager.java index d6311c1..fb3715d 100644 --- a/src/main/java/lar/minecraft/hg/managers/PlayerManager.java +++ b/src/main/java/lar/minecraft/hg/managers/PlayerManager.java @@ -19,6 +19,7 @@ import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.PlayerDeathEvent; +import org.bukkit.event.player.PlayerDropItemEvent; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.inventory.ItemStack; @@ -38,13 +39,17 @@ public class PlayerManager implements Listener { @EventHandler public void onPlayerJoin(PlayerJoinEvent event) { Player player = event.getPlayer(); + event.setJoinMessage(null); if (SpigotPlugin.isWaitingForStart() || SpigotPlugin.isLobby()) { // Teleport each player to a random location Location spawnLocation = ServerManager.getSurfaceRandomLocation(30, SpigotPlugin.newSpawnLocation, 0, 2, 0); player.teleport(spawnLocation); + // Set player gamemode, send welcome message and give instructions book player.setGameMode(GameMode.ADVENTURE); player.playSound(player, Sound.BLOCK_END_PORTAL_FRAME_FILL, 10.0f, 1.0f); + player.sendMessage(MessageUtils.getMessage(MessageKey.welcome_message)); + player.getInventory().addItem(ServerManager.getGameInstructionsBook()); // Check if the player is the winner of the last match or he is premium and create PlayerExtra to track it String lastWinner = DatabaseManager.getLastWinner(SpigotPlugin.serverId); @@ -71,10 +76,10 @@ public void run() { PlayerExtra playerExtra = new PlayerExtra(player.getUniqueId(), player.getName(), isLastWinner, isPremium, winCount); PlayerManager.playerExtras.put(player.getUniqueId(), playerExtra); + // Used to track player position witouth pressing F3 createPlayerLocationBossBar(player); } if (SpigotPlugin.isPlaying() || SpigotPlugin.isWinning() || SpigotPlugin.isSafeArea()) { - event.setJoinMessage(null); player.setGameMode(GameMode.SPECTATOR); } } @@ -112,6 +117,7 @@ public void onPlayerDeath(PlayerDeathEvent event){ */ @EventHandler public void onPlayerQuit(PlayerQuitEvent event){ + event.setQuitMessage(null); if (SpigotPlugin.isWinning() || SpigotPlugin.isLobby()) { PlayerManager.playerExtras.remove(event.getPlayer().getUniqueId()); } @@ -127,6 +133,20 @@ public void onPlayerQuit(PlayerQuitEvent event){ } } + /** + * Player drop item event + * @param event + */ + @EventHandler + public void onPlayerDropItemEvent(PlayerDropItemEvent event) { + // Check if player thrown the Instruction Book and block the event + if (SpigotPlugin.isWaitingForStart() || SpigotPlugin.isLobby()) { + if(event.getItemDrop().getItemStack().getType() == Material.WRITTEN_BOOK) { + event.setCancelled(true); + } + } + } + public static Player getNearestPlayer(Player player, double range) { double distance = Double.POSITIVE_INFINITY; Player target = null; diff --git a/src/main/java/lar/minecraft/hg/managers/ServerManager.java b/src/main/java/lar/minecraft/hg/managers/ServerManager.java index 78cef06..c9d982f 100644 --- a/src/main/java/lar/minecraft/hg/managers/ServerManager.java +++ b/src/main/java/lar/minecraft/hg/managers/ServerManager.java @@ -3,7 +3,9 @@ import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; +import java.util.Map; import java.util.Random; import java.util.concurrent.ThreadLocalRandom; @@ -18,12 +20,21 @@ import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.BookMeta; import lar.minecraft.hg.ServerSchedulers; import lar.minecraft.hg.SpigotPlugin; import lar.minecraft.hg.entities.ItemStackProbability; import lar.minecraft.hg.enums.MessageKey; +import lar.minecraft.hg.enums.PlayerClass; import lar.minecraft.hg.utils.MessageUtils; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.ClickEvent; +import net.md_5.bungee.api.chat.ComponentBuilder; +import net.md_5.bungee.api.chat.HoverEvent; +import net.md_5.bungee.api.chat.hover.content.Text; public class ServerManager { @@ -95,6 +106,56 @@ public static Location getSurfaceRandomLocation(int range, Location startingLoca return(resultLocation); } + + /** + * Get a book with the list of possible classes + * Each class is in a single page + * @return An book with the instruction and materials for each class + */ + public static ItemStack getGameInstructionsBook() { + // Prepare the book + ItemStack book = new ItemStack(Material.WRITTEN_BOOK); + + // Get book meta to set attributes + BookMeta meta = (BookMeta) book.getItemMeta(); + meta.setTitle("Instructions"); + meta.setAuthor(SpigotPlugin.class.getName()); + + StringBuilder commandInstructions = new StringBuilder(); + + Arrays.asList(PlayerClass.values()).forEach(c -> { + // Empty the text that will be added to the page for each page in order to have a clear one + // Add the command description, if it is premium and command to select it + commandInstructions.setLength(0); + commandInstructions.append(ChatColor.GRAY + c.getDescription()); + commandInstructions.append("\n\n"); + if (c.isPremium()) { + commandInstructions.append(MessageUtils.getMessage(MessageKey.class_instructions_premium)); + commandInstructions.append("\n\n"); + } + commandInstructions.append(MessageUtils.getMessage(MessageKey.class_instructions, c.name())); + commandInstructions.append("\n"); + + // Get class materials and append to the command instructions + Map classMaterials = c.getMaterials(); + classMaterials.forEach((x, y) -> { + commandInstructions.append(MessageUtils.getMessage(MessageKey.class_instructions_materials, x, y)); + commandInstructions.append("\n"); + }); + + // Make the text clickable -> player can click on the text instead of writing class command + BaseComponent[] page = new ComponentBuilder(commandInstructions.toString()) + .event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, String.format("/class %s", c.name()))) + .event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text(MessageUtils.getMessage(MessageKey.class_instructions_select)))) + .create(); + + meta.spigot().addPage(page); + }); + + book.setItemMeta(meta); + + return book; + } public static void restartServer() { // Save all worlds before restarting diff --git a/src/main/resources/messages.yml b/src/main/resources/messages.yml index 99fe6a9..e151adb 100644 --- a/src/main/resources/messages.yml +++ b/src/main/resources/messages.yml @@ -1,4 +1,5 @@ -#Use this site to color code generator: https://minecraftitemids.com/color-codes#generator +#Use this site to color code generator: https://minecraftitemids.com/color-codes#generator +welcome_message: "Choose a class and prepare to fight!" current_phase: "&7Current phase is &6{0}" class_selection_lobby: "Class selection is available only in lobby" class_selected: @@ -8,6 +9,10 @@ class_selected: class_wrong: "&7There's no &6&m{0}&7 class" class_premium: "&7You must have won last match or be &6premium &7to use this class!" class_not_selected: "&7Class selection is only available before starting the game" +class_instructions: "&0/class &6{0}" +class_instructions_premium: "&c PREMIUM CLASS" +class_instructions_materials: "&7 -{1} x {0}" +class_instructions_select: "Click to select class" supply_drop_alert: "&fA &6supply drop chest &fwill be spawned in &6{0} &fseconds" supply_drop: "&7Supply chest dropped at XYZ:&2&o {0} / {1} / {2}" last_match_win: "&7You have &6won the last Hunger Games&7! In this game you can choose a &6premium class" diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 174abd3..8790bec 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -34,46 +34,20 @@ commands: usage: / Show current game players that won the most match / global Show global players that won the most match - bowman: - description: Shot other players with a bow and some arrows + class: + description: Use the command to select your class permission: perm.general - usage: / - armored: - description: Cover yourself with a full iron armor set - permission: perm.general - usage: / - doglover: - description: Tame the nature and kill other players with your dog - permission: perm.general - usage: / - lavaman: - description: Burn BURN BURN!!! - permission: perm.general - usage: / - miner: - description: Dig in, my friend! - permission: perm.general - usage: / - mole: - description: Dig out, my friend! - permission: perm.general - usage: / - ghost: - description: Let's get invisible - permission: perm.general - usage: / - teleporter: - description: I'm here... not anymore - permission: perm.general - usage: / - barbarian: - description: WWOOWOWOW - permission: perm.general - usage: / - hardcore: - description: Are you crazy!?!? - permission: perm.general - usage: / + usage: + / bowman Shot other players with a bow and some arrows + / armored Cover yourself with a full iron armor set + / doglover Tame the nature and kill other players with your dog + / lavaman Burn BURN BURN!!! + / miner Dig in, my friend! + / mole Dig out, my friend! + / ghost Let's get invisible + / teleporter I'm here... not anymore + / barbarian WWOOWOWOW + / hardcore Are you crazy!?!? permissions: perm.general: default: true