├── .gitignore ├── README.md ├── src ├── me │ └── petersoj │ │ ├── nms │ │ ├── ActionBarSender.java │ │ ├── actionbars │ │ │ ├── ActionBarv1_8_8.java │ │ │ ├── ActionBarv1_11_2.java │ │ │ └── ActionBarv1_12.java │ │ └── NMSUtils.java │ │ ├── FlameThrowerPlugin.java │ │ ├── listener │ │ ├── Listeners.java │ │ └── Commands.java │ │ └── controller │ │ └── FlameController.java ├── config.yml └── plugin.yml └── FlameThrower.iml /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea/ 3 | out/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flame Thrower 2 | Flame Thrower 3 | -------------------------------------------------------------------------------- /src/me/petersoj/nms/ActionBarSender.java: -------------------------------------------------------------------------------- 1 | package me.petersoj.nms; 2 | 3 | import org.bukkit.entity.Player; 4 | 5 | public interface ActionBarSender { 6 | 7 | void sendActionBar(Player player, String message); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/config.yml: -------------------------------------------------------------------------------- 1 | # This sets the item ID of the flame thrower. 2 | item: 377 3 | 4 | # This sets the percentage (%) chance of a flame thrower setting a block on fire. 5 | chance-of-fire: 30 6 | 7 | # This sets whether players can craft flame throwers or not. 8 | # use true or false 9 | craft-flame-thrower: true -------------------------------------------------------------------------------- /src/plugin.yml: -------------------------------------------------------------------------------- 1 | name: FlameThrower 2 | main: me.petersoj.FlameThrowerPlugin 3 | version: 1.0 4 | description: Adds flame throwing capabilities to Minecraft! 5 | commands: 6 | flamethrower: 7 | description: Gives a player a flamethrower with the specified time the fuel tank lasts and amount of refuels (-1 for inifinite) 8 | usage: /flamethrower 9 | permission: flamethrower.give 10 | permissions: 11 | flamethrower.give: 12 | default: op -------------------------------------------------------------------------------- /src/me/petersoj/nms/actionbars/ActionBarv1_8_8.java: -------------------------------------------------------------------------------- 1 | package me.petersoj.nms.actionbars; 2 | 3 | import me.petersoj.nms.ActionBarSender; 4 | import net.minecraft.server.v1_8_R3.IChatBaseComponent; 5 | import net.minecraft.server.v1_8_R3.PacketPlayOutChat; 6 | import org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer; 7 | import org.bukkit.entity.Player; 8 | 9 | public class ActionBarv1_8_8 implements ActionBarSender { 10 | 11 | public void sendActionBar(Player player, String message) { 12 | IChatBaseComponent chatBaseComponent = IChatBaseComponent.ChatSerializer.a("{\"text\": \"" + message + "\"}"); 13 | 14 | PacketPlayOutChat packetPlayOutChat = new PacketPlayOutChat(chatBaseComponent, (byte) 2); 15 | 16 | ((CraftPlayer) player).getHandle().playerConnection.sendPacket(packetPlayOutChat); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/me/petersoj/nms/actionbars/ActionBarv1_11_2.java: -------------------------------------------------------------------------------- 1 | package me.petersoj.nms.actionbars; 2 | 3 | import me.petersoj.nms.ActionBarSender; 4 | import net.minecraft.server.v1_11_R1.IChatBaseComponent; 5 | import net.minecraft.server.v1_11_R1.PacketPlayOutChat; 6 | import org.bukkit.craftbukkit.v1_11_R1.entity.CraftPlayer; 7 | import org.bukkit.entity.Player; 8 | 9 | public class ActionBarv1_11_2 implements ActionBarSender { 10 | 11 | public void sendActionBar(Player player, String message) { 12 | IChatBaseComponent chatBaseComponent = IChatBaseComponent.ChatSerializer.a("{\"text\": \"" + message + "\"}"); 13 | 14 | PacketPlayOutChat packetPlayOutChat = new PacketPlayOutChat(chatBaseComponent, (byte) 2); 15 | 16 | ((CraftPlayer) player).getHandle().playerConnection.sendPacket(packetPlayOutChat); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /FlameThrower.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/me/petersoj/nms/actionbars/ActionBarv1_12.java: -------------------------------------------------------------------------------- 1 | package me.petersoj.nms.actionbars; 2 | 3 | import me.petersoj.nms.ActionBarSender; 4 | import net.minecraft.server.v1_12_R1.ChatMessageType; 5 | import net.minecraft.server.v1_12_R1.IChatBaseComponent; 6 | import net.minecraft.server.v1_12_R1.PacketPlayOutChat; 7 | import org.bukkit.craftbukkit.v1_12_R1.entity.CraftPlayer; 8 | import org.bukkit.entity.Player; 9 | 10 | public class ActionBarv1_12 implements ActionBarSender { 11 | 12 | public void sendActionBar(Player player, String message) { 13 | IChatBaseComponent chatBaseComponent = IChatBaseComponent.ChatSerializer.a("{\"text\": \"" + message + "\"}"); 14 | 15 | PacketPlayOutChat packetPlayOutChat = new PacketPlayOutChat(chatBaseComponent, ChatMessageType.GAME_INFO); 16 | 17 | ((CraftPlayer) player).getHandle().playerConnection.sendPacket(packetPlayOutChat); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/me/petersoj/nms/NMSUtils.java: -------------------------------------------------------------------------------- 1 | package me.petersoj.nms; 2 | 3 | import me.petersoj.nms.actionbars.ActionBarv1_11_2; 4 | import me.petersoj.nms.actionbars.ActionBarv1_12; 5 | import me.petersoj.nms.actionbars.ActionBarv1_8_8; 6 | import org.bukkit.Bukkit; 7 | import org.bukkit.entity.Player; 8 | 9 | public class NMSUtils { 10 | // I know that you can simply call player.spigot().sendMessage(), but I don't think that method exists for 1.8.8 11 | 12 | private static ActionBarSender actionBarSender; 13 | 14 | public static void setupNMS() { 15 | String version = Bukkit.getVersion(); 16 | 17 | if (version.contains("1.8.8")) { 18 | actionBarSender = new ActionBarv1_8_8(); 19 | } else if (version.contains("1.11.2")) { 20 | actionBarSender = new ActionBarv1_11_2(); 21 | } else if (version.contains("1.12")) { 22 | actionBarSender = new ActionBarv1_12(); 23 | } 24 | } 25 | 26 | public static void sendActionBar(Player player, String message) { 27 | actionBarSender.sendActionBar(player, message); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/me/petersoj/FlameThrowerPlugin.java: -------------------------------------------------------------------------------- 1 | package me.petersoj; 2 | 3 | import me.petersoj.controller.FlameController; 4 | import me.petersoj.listener.Commands; 5 | import me.petersoj.listener.Listeners; 6 | import me.petersoj.nms.NMSUtils; 7 | import org.bukkit.command.Command; 8 | import org.bukkit.command.CommandSender; 9 | import org.bukkit.plugin.java.JavaPlugin; 10 | 11 | public class FlameThrowerPlugin extends JavaPlugin { 12 | 13 | private FlameController flameController; 14 | private Commands commands; 15 | private Listeners listeners; 16 | 17 | @Override 18 | public void onEnable() { 19 | this.flameController = new FlameController(this); 20 | this.commands = new Commands(this); 21 | this.listeners = new Listeners(this); 22 | 23 | NMSUtils.setupNMS(); 24 | 25 | this.listeners.listen(); 26 | } 27 | 28 | public FlameController getFlameController() { 29 | return flameController; 30 | } 31 | 32 | @Override 33 | public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { 34 | return commands.onCommand(sender, command, label, args); 35 | } 36 | } -------------------------------------------------------------------------------- /src/me/petersoj/listener/Listeners.java: -------------------------------------------------------------------------------- 1 | package me.petersoj.listener; 2 | 3 | import me.petersoj.FlameThrowerPlugin; 4 | import org.bukkit.Bukkit; 5 | import org.bukkit.ChatColor; 6 | import org.bukkit.event.EventHandler; 7 | import org.bukkit.event.EventPriority; 8 | import org.bukkit.event.Listener; 9 | import org.bukkit.event.block.Action; 10 | import org.bukkit.event.player.PlayerInteractEvent; 11 | import org.bukkit.inventory.meta.ItemMeta; 12 | 13 | public class Listeners implements Listener { 14 | 15 | private FlameThrowerPlugin plugin; 16 | 17 | public Listeners(FlameThrowerPlugin flameThrower) { 18 | this.plugin = flameThrower; 19 | } 20 | 21 | public void listen() { 22 | Bukkit.getPluginManager().registerEvents(this, plugin); 23 | } 24 | 25 | @EventHandler(priority = EventPriority.HIGHEST) // Happens last 26 | public void onPlayerInteract(PlayerInteractEvent e) { 27 | if (e.getAction() == Action.RIGHT_CLICK_AIR || e.getAction() == Action.RIGHT_CLICK_BLOCK && e.hasItem()) { 28 | ItemMeta itemMeta = e.getItem().getItemMeta(); 29 | 30 | if (itemMeta.hasLore() && itemMeta.hasDisplayName()) { 31 | if (ChatColor.stripColor(itemMeta.getDisplayName()).equals("Flame Thrower")) { 32 | 33 | plugin.getFlameController().useFlameThrower(e.getPlayer(), e.getItem()); 34 | e.setCancelled(true); 35 | } 36 | } 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/me/petersoj/listener/Commands.java: -------------------------------------------------------------------------------- 1 | package me.petersoj.listener; 2 | 3 | import me.petersoj.FlameThrowerPlugin; 4 | import org.bukkit.Bukkit; 5 | import org.bukkit.ChatColor; 6 | import org.bukkit.command.Command; 7 | import org.bukkit.command.CommandSender; 8 | import org.bukkit.entity.Player; 9 | import org.bukkit.inventory.ItemStack; 10 | 11 | public class Commands { 12 | 13 | private FlameThrowerPlugin plugin; 14 | 15 | public Commands(FlameThrowerPlugin plugin) { 16 | this.plugin = plugin; 17 | } 18 | 19 | public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { 20 | if (command.getLabel().equals("flamethrower")) { 21 | 22 | if (args.length == 3) { 23 | Player giveTo = Bukkit.getPlayer(args[0]); 24 | 25 | int fullFuelSeconds = parseArgumentNumber(args[1]); 26 | int refuels = parseArgumentNumber(args[2]); 27 | 28 | 29 | ItemStack flameThrower = plugin.getFlameController().getNewFlameThrower(fullFuelSeconds, refuels); 30 | 31 | if (giveTo.getInventory().addItem(flameThrower).size() > 0) { // If greater than 0, inventory was full. 32 | giveTo.getWorld().dropItem(giveTo.getLocation(), flameThrower); 33 | } 34 | 35 | if (sender instanceof Player) { 36 | sender.sendMessage(ChatColor.GREEN + "Successfully gave a flamethrower to " + giveTo.getName()); 37 | } 38 | } else { 39 | sender.sendMessage(ChatColor.RED + "To few arguments!"); 40 | sender.sendMessage(ChatColor.RED + "Usage: " + ChatColor.WHITE + command.getUsage()); 41 | } 42 | } 43 | return true; 44 | } 45 | 46 | private int parseArgumentNumber(String arg) { 47 | int number; 48 | try { 49 | number = Integer.parseInt(arg); 50 | } catch (Exception e) { // Couldn't parse a number 51 | number = -1; // Infinite 52 | } 53 | return number; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/me/petersoj/controller/FlameController.java: -------------------------------------------------------------------------------- 1 | package me.petersoj.controller; 2 | 3 | import me.petersoj.FlameThrowerPlugin; 4 | import me.petersoj.nms.NMSUtils; 5 | import org.bukkit.*; 6 | import org.bukkit.block.Block; 7 | import org.bukkit.block.BlockFace; 8 | import org.bukkit.configuration.file.FileConfiguration; 9 | import org.bukkit.entity.Player; 10 | import org.bukkit.inventory.ItemStack; 11 | import org.bukkit.inventory.ShapedRecipe; 12 | import org.bukkit.inventory.meta.ItemMeta; 13 | import org.bukkit.scheduler.BukkitRunnable; 14 | import org.bukkit.util.Vector; 15 | 16 | import java.math.RoundingMode; 17 | import java.text.DecimalFormat; 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | import java.util.Set; 21 | 22 | public class FlameController { 23 | 24 | private FlameThrowerPlugin plugin; 25 | 26 | private final int flameThrowerID; 27 | private final double fireChance; 28 | 29 | public FlameController(FlameThrowerPlugin plugin) { 30 | this.plugin = plugin; 31 | 32 | plugin.saveDefaultConfig(); // Copies the default config if it doesn't exist. 33 | 34 | FileConfiguration config = plugin.getConfig(); 35 | this.flameThrowerID = config.getInt("item"); 36 | this.fireChance = ((double) config.getInt("chance-of-fire")) / 100; 37 | 38 | if (config.getBoolean("craft-flame-thrower")) { 39 | this.addRecipe(); 40 | } 41 | } 42 | 43 | private void addRecipe() { 44 | ShapedRecipe recipe = new ShapedRecipe(getNewFlameThrower(30, 20)); 45 | 46 | recipe.shape("B ", "DT ", "C "); 47 | 48 | recipe.setIngredient('B', Material.BLAZE_POWDER); 49 | recipe.setIngredient('D', Material.DISPENSER); 50 | recipe.setIngredient('T', Material.TORCH); 51 | recipe.setIngredient('C', Material.COAL); 52 | 53 | Bukkit.addRecipe(recipe); 54 | } 55 | 56 | public ItemStack getNewFlameThrower(int fullFuelSeconds, int refuels) { 57 | ItemStack flameThrower = new ItemStack(flameThrowerID); 58 | this.setFlameThrowerLore(flameThrower, fullFuelSeconds, fullFuelSeconds, refuels); 59 | return flameThrower; 60 | } 61 | 62 | private void setFlameThrowerLore(ItemStack flameThrower, double fuelTime, int fullFuelSeconds, int refuels) { 63 | ItemMeta itemMeta = flameThrower.getItemMeta(); 64 | itemMeta.setDisplayName(ChatColor.RED + "Flame " + ChatColor.GOLD + "Thrower"); 65 | 66 | ArrayList lore = new ArrayList<>(); 67 | 68 | DecimalFormat df = new DecimalFormat("###.##"); 69 | df.setRoundingMode(RoundingMode.DOWN); 70 | 71 | lore.add(ChatColor.DARK_RED + "Fuel: " + ChatColor.WHITE + (fuelTime > -1 ? df.format(fuelTime) + "/" + fullFuelSeconds : "Infinite")); 72 | lore.add(ChatColor.GRAY + "Re-fuels: " + ChatColor.WHITE + (refuels > -1 ? refuels : "Infinite")); 73 | 74 | itemMeta.setLore(lore); 75 | flameThrower.setItemMeta(itemMeta); 76 | } 77 | 78 | // This method is VERY messy, because I was trying to make it as optimized as possible :D 79 | // I could have done better still... :P 80 | public void useFlameThrower(Player player, ItemStack flameThrower) { 81 | new BukkitRunnable() { 82 | 83 | List lore = flameThrower.getItemMeta().getLore(); // Gosh darn getItemMeta gets cloned 84 | 85 | double fuelTime = getFuel(lore); 86 | int fullFuelSeconds = getFullFuelTime(lore); 87 | int refuels = getReFuels(lore); 88 | 89 | int index = 0; 90 | 91 | @Override 92 | public void run() { 93 | 94 | fuelTime = getFuel(flameThrower.getItemMeta().getLore()); // Needed to prevent overlapping bukkit tasks 95 | 96 | if (fuelTime > 0 || fuelTime == -1) { // -1 for Infinite 97 | 98 | this.shootFlames(); 99 | 100 | if (fuelTime != -1) { 101 | this.fuelTime -= 0.05; // Each tick decrement is equal to 1/20 of a second 102 | 103 | this.sendActionBar(); 104 | } 105 | 106 | } else if (index == 0) { // Make sure this is the first time through 107 | if (refuels > 0 || refuels == -1) { // -1 for Infinite 108 | int itemIndex = player.getInventory().first(Material.COAL); 109 | if (itemIndex >= 0) { 110 | ItemStack coal = player.getInventory().getItem(itemIndex); 111 | if (coal.getAmount() <= 1) { 112 | player.getInventory().clear(itemIndex); 113 | } else { 114 | coal.setAmount(coal.getAmount() - 1); 115 | } 116 | 117 | this.fuelTime = fullFuelSeconds; 118 | 119 | if (refuels != -1) { 120 | refuels--; 121 | } 122 | 123 | NMSUtils.sendActionBar(player, ChatColor.GREEN + "Flame Thrower re-fueled!"); 124 | player.playSound(player.getLocation(), Sound.ITEM_FLINTANDSTEEL_USE, 1f, 1f); 125 | } else { 126 | NMSUtils.sendActionBar(player, ChatColor.RED + "You need coal in order to refuel this Flame Thrower!"); 127 | player.playSound(player.getLocation(), Sound.ITEM_FLINTANDSTEEL_USE, 1f, 0f); 128 | 129 | this.fuelTime = 0; 130 | } 131 | 132 | this.index = 3; // To cancel the task 133 | } else { 134 | NMSUtils.sendActionBar(player, ChatColor.RED + "You've run out of uses for this Flame Thrower!"); 135 | 136 | this.index = 3; // To cancel the task 137 | 138 | this.fuelTime = 0; 139 | } 140 | } 141 | 142 | setFlameThrowerLore(flameThrower, fuelTime, fullFuelSeconds, refuels); 143 | 144 | if (index >= 3) { 145 | this.cancel(); 146 | } else { 147 | index++; 148 | } 149 | } 150 | 151 | private void shootFlames() { 152 | 153 | Vector playerDirection = player.getLocation().getDirection(); 154 | Vector particleVector = playerDirection.clone(); 155 | 156 | playerDirection.multiply(8); // Set length to 8 blocks out 157 | 158 | // rotate particle location 90 degrees 159 | double x = particleVector.getX(); 160 | particleVector.setX(-particleVector.getZ()); 161 | particleVector.setZ(x); 162 | particleVector.divide(new Vector(3, 3, 3)); // Divide it by 2 to shorten length 163 | 164 | Location particleLocation = particleVector.toLocation(player.getWorld()).add(player.getLocation()).add(0, 1.05, 0); 165 | 166 | for (int i = 0; i < 4; i++) { // Shoot 4 times for more flames! Change here to shoot more flames! 167 | shootSingleFlame(playerDirection, particleLocation); 168 | } 169 | 170 | if (Math.random() < fireChance) { // Light fire to block one fifth of the time 171 | Block lookingBlock = player.getTargetBlock((Set) null, 15); // Get target block in 15 block range 172 | if (lookingBlock != null && lookingBlock.getType().isBlock()) { 173 | Block upBlock = lookingBlock.getRelative(BlockFace.UP); 174 | if (upBlock != null && upBlock.getType() == Material.AIR) { 175 | new BukkitRunnable() { 176 | @Override 177 | public void run() { 178 | upBlock.setType(Material.FIRE); 179 | } 180 | }.runTaskLater(plugin, 10); // run half a second later for a more realistic effect. 181 | } 182 | } 183 | } 184 | } 185 | 186 | // Separate method for efficiency 187 | private void shootSingleFlame(Vector playerDirection, Location particleLocation) { 188 | Vector particlePath = playerDirection.clone(); // clone to prevent extra math calculations 189 | 190 | particlePath.add(new Vector(Math.random() - Math.random(), Math.random() - Math.random(), Math.random() - Math.random())); // Add some randomness 191 | 192 | Location offsetLocation = particlePath.toLocation(player.getWorld()); 193 | 194 | player.getWorld().spawnParticle(Particle.FLAME, particleLocation, 0, offsetLocation.getX(), offsetLocation.getY(), offsetLocation.getZ(), 0.1); // Count of zero, to respect 'speed' 195 | } 196 | 197 | private void sendActionBar() { 198 | String block = "\u2588"; // Block character unicode 199 | double fuelRatio = (fuelTime / (double) fullFuelSeconds) * 10; 200 | 201 | System.out.println(fuelRatio); 202 | 203 | StringBuilder builder = new StringBuilder(ChatColor.RED + "Fuel: "); 204 | for (int i = 0; i < 10; i++) { // For ten block characters 205 | if (i < fuelRatio) { 206 | builder.append(ChatColor.GREEN + block); 207 | } else { 208 | builder.append(ChatColor.GRAY + block); 209 | } 210 | } 211 | 212 | NMSUtils.sendActionBar(player, builder.toString()); 213 | } 214 | }.runTaskTimer(plugin, 0, 0); 215 | } 216 | 217 | private double getFuel(List lore) { 218 | String line = ChatColor.stripColor(lore.get(0)); 219 | try { 220 | return Double.parseDouble(line.substring(6, line.indexOf("/"))); // get the first line and substring at 6th position to the "/" 221 | } catch (Exception e) { 222 | return -1; 223 | } 224 | } 225 | 226 | private int getFullFuelTime(List lore) { 227 | String line = ChatColor.stripColor(lore.get(0)); 228 | try { 229 | return Integer.parseInt(line.substring(line.indexOf("/") + 1)); // get past the "/" to the end. 230 | } catch (Exception e) { // Catch for infinite 231 | return -1; 232 | } 233 | } 234 | 235 | private int getReFuels(List lore) { // Return -1 for infinite 236 | String number = ChatColor.stripColor(lore.get(1)).substring(10); // get the second line and substring at 10th position 237 | 238 | if (number.startsWith("Inf")) { // Will be infinite 239 | return -1; 240 | } else { 241 | return Integer.parseInt(number); 242 | } 243 | } 244 | } 245 | --------------------------------------------------------------------------------