diff --git a/src/main/java/com/iridium/iridiumenchants/IridiumEnchants.java b/src/main/java/com/iridium/iridiumenchants/IridiumEnchants.java index 5ffb330..c7c0e45 100644 --- a/src/main/java/com/iridium/iridiumenchants/IridiumEnchants.java +++ b/src/main/java/com/iridium/iridiumenchants/IridiumEnchants.java @@ -204,6 +204,7 @@ public void registerEffects() { effects.put("TELEPATHY", new Telepathy()); effects.put("ADD_EXTRA_HEALTH", new AddExtraHealth()); effects.put("REMOVE_EXTRA_HEALTH", new RemoveExtraHealth()); + effects.put("VEINMINE", new VeinMine()); } public void registerConditions() { diff --git a/src/main/java/com/iridium/iridiumenchants/configs/Configuration.java b/src/main/java/com/iridium/iridiumenchants/configs/Configuration.java index cd908c0..a5f3254 100644 --- a/src/main/java/com/iridium/iridiumenchants/configs/Configuration.java +++ b/src/main/java/com/iridium/iridiumenchants/configs/Configuration.java @@ -33,6 +33,31 @@ public class Configuration { .put(XMaterial.DEEPSLATE_GOLD_ORE, XMaterial.GOLD_INGOT) .put(XMaterial.SAND, XMaterial.GLASS) .build(); + + public List veinMiner = Arrays.asList( + XMaterial.COAL_ORE, + XMaterial.DEEPSLATE_COAL_ORE, + XMaterial.COPPER_ORE, + XMaterial.DEEPSLATE_COPPER_ORE, + XMaterial.IRON_ORE, + XMaterial.DEEPSLATE_IRON_ORE, + XMaterial.LAPIS_ORE, + XMaterial.DEEPSLATE_LAPIS_ORE, + XMaterial.REDSTONE_ORE, + XMaterial.DEEPSLATE_REDSTONE_ORE, + XMaterial.GOLD_ORE, + XMaterial.DEEPSLATE_GOLD_ORE, + XMaterial.NETHER_GOLD_ORE, + XMaterial.EMERALD_ORE, + XMaterial.DEEPSLATE_EMERALD_ORE, + XMaterial.DIAMOND_ORE, + XMaterial.DEEPSLATE_DIAMOND_ORE, + XMaterial.NETHER_QUARTZ_ORE, + XMaterial.ANCIENT_DEBRIS + ); + + public int veinMineExpPenaltyAmount = 5; + public List infusionBlacklist = Arrays.asList(XMaterial.BEDROCK, XMaterial.SPAWNER, XMaterial.CHEST, XMaterial.TRAPPED_CHEST, XMaterial.WATER, XMaterial.LAVA); public Map tiers = ImmutableMap.builder() .put("Common", new Tier(new Item(XMaterial.ENCHANTED_BOOK, 11, 1, "&b&lCOMMON ENCHANTMENT", Arrays.asList("&e&lCOST: &7%cost% levels", "", "&e&l[!] &7Left Click to purchase a random common enchantment", "&e&l[!] &7Right Click to view all common enchantments")), 20, ExperienceType.LEVEL)) diff --git a/src/main/java/com/iridium/iridiumenchants/configs/CustomEnchants.java b/src/main/java/com/iridium/iridiumenchants/configs/CustomEnchants.java index 9f767c0..8c17b89 100644 --- a/src/main/java/com/iridium/iridiumenchants/configs/CustomEnchants.java +++ b/src/main/java/com/iridium/iridiumenchants/configs/CustomEnchants.java @@ -238,5 +238,13 @@ public class CustomEnchants { .put("Telepathy", new CustomEnchant("&7Telepathy", "Puts all blocks you break into your inventory", "Tool", "BLOCK_BREAK", ImmutableMap.builder() .put(1, new Level(100, Collections.singletonList("Common"), Collections.singletonList("TELEPATHY"), Collections.emptyList())) .build(), true, true)) + .put("OreSiphon", new CustomEnchant("&7Ore Siphon", "Breaks adjacent ores of the same type", "Tool", "BLOCK_BREAK", ImmutableMap.builder() + .put(1, new Level(100, Collections.singletonList("Common"), Collections.singletonList("VEINMINE:0:25:FALSE:FALSE:TRUE:TRUE"), Collections.emptyList())) + .put(2, new Level(100, Collections.singletonList("Common"), Collections.singletonList("VEINMINE:15:30:FALSE:FALSE:TRUE:TRUE"), Collections.emptyList())) + .put(3, new Level(100, Collections.singletonList("Common"), Collections.singletonList("VEINMINE:30:35:FALSE:TRUE:FALSE:TRUE"), Collections.emptyList())) + .put(4, new Level(100, Collections.singletonList("Elite"), Collections.singletonList("VEINMINE:45:40:TRUE:TRUE:FALSE:TRUE"), Collections.emptyList())) + .put(5, new Level(100, Collections.singletonList("Elite"), Collections.singletonList("VEINMINE:60:45:TRUE:TRUE:FALSE:TRUE"), Collections.emptyList())) + .put(6, new Level(100, Collections.singletonList("Legendary"), Collections.singletonList("VEINMINE:75:50:TRUE:TRUE:FALSE:FALSE"), Collections.emptyList())) + .build(), true, true)) .build(); } diff --git a/src/main/java/com/iridium/iridiumenchants/configs/Messages.java b/src/main/java/com/iridium/iridiumenchants/configs/Messages.java index 6086195..bda36fe 100644 --- a/src/main/java/com/iridium/iridiumenchants/configs/Messages.java +++ b/src/main/java/com/iridium/iridiumenchants/configs/Messages.java @@ -25,4 +25,6 @@ public class Messages { public String noGkit = "%prefix% &7No GKit by that name exists."; public String gaveGKit = "%prefix% &7You gave the %name% gkit to %player%."; public String recievedGKit = "%prefix% &7You received a %name% gkit from %player%."; + public String cannotVeinMine = "%prefix% &7Your &e%tool% &7is low on durability, so you cannot break these blocks."; + public String veinMineWrongTool = "%prefix% &7Your &e%tool% is not capable of getting drops from these blocks, so they will not be mined."; } diff --git a/src/main/java/com/iridium/iridiumenchants/effects/DropHead.java b/src/main/java/com/iridium/iridiumenchants/effects/DropHead.java index e91bca1..0a8b749 100644 --- a/src/main/java/com/iridium/iridiumenchants/effects/DropHead.java +++ b/src/main/java/com/iridium/iridiumenchants/effects/DropHead.java @@ -9,19 +9,18 @@ public class DropHead implements Effect { @Override public void apply(LivingEntity player, LivingEntity target, String[] args, ItemStack itemStack, Event event) { + + ItemStack head = XMaterial.PLAYER_HEAD.parseItem(); + if (head == null) return; + SkullMeta meta = (SkullMeta) head.getItemMeta(); + if (args.length == 2 && args[1].equalsIgnoreCase("target")) { if (target == null) return; - ItemStack head = XMaterial.PLAYER_HEAD.parseItem(); - if (head == null) return; - SkullMeta meta = (SkullMeta) head.getItemMeta(); meta.setOwner(target.getName()); head.setItemMeta(meta); target.getLocation().getWorld().dropItem(target.getLocation(), head); } else { if (player == null) return; - ItemStack head = XMaterial.PLAYER_HEAD.parseItem(); - if (head == null) return; - SkullMeta meta = (SkullMeta) head.getItemMeta(); meta.setOwner(player.getName()); head.setItemMeta(meta); player.getLocation().getWorld().dropItem(player.getLocation(), head); diff --git a/src/main/java/com/iridium/iridiumenchants/effects/VeinMine.java b/src/main/java/com/iridium/iridiumenchants/effects/VeinMine.java new file mode 100644 index 0000000..7b2daf6 --- /dev/null +++ b/src/main/java/com/iridium/iridiumenchants/effects/VeinMine.java @@ -0,0 +1,319 @@ +package com.iridium.iridiumenchants.effects; + +import com.cryptomorin.xseries.XMaterial; +import com.iridium.iridiumcore.utils.StringUtils; +import com.iridium.iridiumenchants.IridiumEnchants; + +import com.iridium.iridiumenchants.listeners.BlockBreakListener; +import lombok.Setter; +import org.apache.commons.lang3.text.WordUtils; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.*; +import org.bukkit.event.Event; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.player.PlayerItemBreakEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.Damageable; + +import java.util.*; + +public class VeinMine implements Effect { + + private int chance; + private int veinCap; + @Setter + private boolean placeDropsAtOrigin; + @Setter + private boolean protectTool; + @Setter + private boolean allowWrongTool; + @Setter + private boolean expPenalty; + private int expPenaltyAmount = IridiumEnchants.getInstance().getConfiguration().veinMineExpPenaltyAmount; + + private final LinkedHashSet blocksBuffer = new LinkedHashSet<>(), recent = new LinkedHashSet<>(); + private final List events = new ArrayList<>(); + + @Override + public void apply(LivingEntity player, LivingEntity target, String[] args, ItemStack itemStack, Event event) { + if (!(player instanceof Player) && !(event instanceof BlockBreakEvent)) return; + + // Create blockBreakEvent + Player realPlayer = (Player) player; + BlockBreakEvent blockBreakEvent = (BlockBreakEvent) event; + + if (events.contains(blockBreakEvent)) { + events.remove(blockBreakEvent); + return; + } + + // Check if we should trigger vein mine at all + if (!IridiumEnchants.getInstance().getConfiguration().veinMiner.contains( + XMaterial.matchXMaterial(blockBreakEvent.getBlock().getType()))) return; + + // Create variables for event block + Block eventBlock = blockBreakEvent.getBlock(); + Location eventBlockLocation = eventBlock.getLocation(); + + // Get the current vein mine settings + setConfigOptions(args); + + // Get the blocks to be mined and check if we're vein mining based on how many are returned + LinkedHashSet blocksToMine = allocateBlocks(eventBlock); + if (blocksToMine.size() < 2) return; + + // Check if we should allow vein mining if the tool will not give drops + blockBreakEvent.setCancelled(triggerWrongToolProtection(itemStack, realPlayer, eventBlock)); + if (blockBreakEvent.isCancelled()) return; + + // Vein mining + for (Block block : blocksToMine) { + + // Create new event for each block + BlockBreakEvent breakEvent = new BlockBreakEvent(block, realPlayer); + events.add(breakEvent); + new BlockBreakListener().onBlockBreak(breakEvent); + if (breakEvent.isCancelled()) continue; + + // Check if we should continue to vein mine based on location permission + breakEvent.setCancelled(triggerLocationProtection(realPlayer, block.getLocation())); + if (breakEvent.isCancelled()) return; + + // Check if we should protect the tool from breaking + breakEvent.setCancelled(triggerToolProtection(itemStack, realPlayer)); + if (breakEvent.isCancelled()) return; + + // Roll the chance of damaging the tool + Random random = new Random(); + int rolledChance = random.nextInt(101); + + // Check if we need to change the amount of exp the block will drop + if (expPenalty) breakEvent.setExpToDrop(breakEvent.getExpToDrop() - expPenaltyAmount); + + // Check if we need to relocate the drops + if (placeDropsAtOrigin) { + for (ItemStack drop : block.getDrops(itemStack)) { + eventBlockLocation.getWorld().dropItemNaturally(eventBlockLocation, drop); + } + block.setType(getAir(block)); + } else block.breakNaturally(itemStack); + + // Check if we need to damage the tool + if (chance <= rolledChance) { + // Check if we need to break the tool + if (damageTool(itemStack, realPlayer)) return; + } + } + } + + public Material getAir(Block block) { + for (BlockFace blockFace : BlockFace.values()) { + if (blockFace == BlockFace.SELF) continue; + if (block.getRelative(blockFace).getType() == Material.CAVE_AIR) return Material.CAVE_AIR; + } + return Material.AIR; + } + + public String getToolName(ItemStack itemStack) { + if (itemStack.getItemMeta().hasDisplayName()) return itemStack.getItemMeta().getDisplayName(); + else return itemStack.getType().toString().toLowerCase(); + } + + public boolean triggerWrongToolProtection(ItemStack itemStack, Player player, Block block) { + if (block.getDrops(itemStack).isEmpty() && !allowWrongTool) { + String displayName = getToolName(itemStack); + player.sendMessage(StringUtils.color(IridiumEnchants.getInstance().getMessages().veinMineWrongTool + .replace("%prefix%", IridiumEnchants.getInstance().getConfiguration().prefix) + .replace("%tool%", WordUtils.capitalize(displayName.replace("_", " "))) + )); + return true; + } + return false; + } + + public boolean triggerToolProtection(ItemStack itemStack, Player player) { + if (getDamage(itemStack) + 1 == getMaxDamage(itemStack) && protectTool) { + String displayName = getToolName(itemStack); + player.sendMessage(StringUtils.color(IridiumEnchants.getInstance().getMessages().cannotVeinMine + .replace("%prefix%", IridiumEnchants.getInstance().getConfiguration().prefix) + .replace("%tool%", WordUtils.capitalize(displayName.replace("_", " "))) + )); + return true; + } + return false; + } + + public boolean triggerLocationProtection(Player player, Location location) { + if (!IridiumEnchants.getInstance().canBuild(player, location)) { + player.sendMessage(StringUtils.color(IridiumEnchants.getInstance().getMessages().noPermission + .replace("%prefix%", IridiumEnchants.getInstance().getConfiguration().prefix))); + return true; + } + return false; + } + + private int getDamage(ItemStack itemStack) { + if(itemStack.getItemMeta() instanceof Damageable) { + if(((Damageable) itemStack.getItemMeta()).hasDamage()) return ((Damageable) itemStack.getItemMeta()).getDamage(); + } + return itemStack.getDurability(); + } + + private int getMaxDamage(ItemStack itemStack) { + return itemStack.getType().getMaxDurability(); + } + + private boolean damageTool(ItemStack itemStack, Player player) { + Damageable toolMeta = (Damageable) itemStack.getItemMeta(); + int toolDamage = getDamage(itemStack); + int maxToolDamage = getMaxDamage(itemStack); + + toolDamage++; + if (toolDamage == maxToolDamage) { + PlayerItemBreakEvent itemBreakEvent = new PlayerItemBreakEvent(player, itemStack); + itemBreakEvent.getBrokenItem().setAmount(1); + return true; + } + + toolMeta.setDamage(toolDamage); + itemStack.setItemMeta(toolMeta); + + return false; + } + + public LinkedHashSet allocateBlocks(Block block) { + LinkedHashSet blocks = new LinkedHashSet<>(); + blocks.add(block); + this.recent.add(block); + + while (blocksBuffer.size() < veinCap) { + recentSearch: + for (Block current : recent) { + for (int x = -1; x <= 1; x++) { + for (int y = -1; y <= 1; y++) { + for (int z = -1; z <= 1; z++) { + + if (x == 0 && y == 0 && z == 0) continue; + + Block relative = current.getRelative(x, y, z); + if (blocks.contains(relative) || blocksBuffer.contains(relative)) { + continue; + } + + if (relative.getType() != current.getType()) { + continue; + } + + if (blocks.size() + blocksBuffer.size() >= veinCap) { + break recentSearch; + } + + this.blocksBuffer.add(relative); + } + } + } + } + + if (blocksBuffer.isEmpty()) { + break; + } + + this.recent.clear(); + this.recent.addAll(blocksBuffer); + blocks.addAll(blocksBuffer); + + this.blocksBuffer.clear(); + } + + this.recent.clear(); + return blocks; + } + + private void setChance(String arg) { + try { + chance = Integer.parseInt(arg); + } catch(NumberFormatException e) { + chance = 0; + } + } + + private void setVeinCap(String arg) { + try { + veinCap = Integer.parseInt(arg); + } catch(NumberFormatException e) { + veinCap = 25; + } + } + + public void setConfigOptions(String[] args) { + + switch(args.length) { + case 0: {} + case 1: { + setChance(""); + setVeinCap(""); + setPlaceDropsAtOrigin(false); + setProtectTool(false); + setAllowWrongTool(false); + setExpPenalty(false); + break; + } + case 2: { + setChance(args[1]); + setVeinCap(""); + setPlaceDropsAtOrigin(false); + setProtectTool(false); + setAllowWrongTool(false); + setExpPenalty(false); + break; + } + case 3: { + setChance(args[1]); + setVeinCap(args[2]); + setPlaceDropsAtOrigin(false); + setProtectTool(false); + setAllowWrongTool(false); + setExpPenalty(false); + break; + } + case 4: { + setChance(args[1]); + setVeinCap(args[2]); + setPlaceDropsAtOrigin(Boolean.parseBoolean(args[3])); + setProtectTool(false); + setAllowWrongTool(false); + setExpPenalty(false); + break; + } + case 5: { + setChance(args[1]); + setVeinCap(args[2]); + setPlaceDropsAtOrigin(Boolean.parseBoolean(args[3])); + setProtectTool(Boolean.parseBoolean(args[4])); + setAllowWrongTool(false); + setExpPenalty(false); + break; + } + case 6: { + setChance(args[1]); + setVeinCap(args[2]); + setPlaceDropsAtOrigin(Boolean.parseBoolean(args[3])); + setProtectTool(Boolean.parseBoolean(args[4])); + setAllowWrongTool(Boolean.parseBoolean(args[5])); + setExpPenalty(false); + break; + } + default: { + setChance(args[1]); + setVeinCap(args[2]); + setPlaceDropsAtOrigin(Boolean.parseBoolean(args[3])); + setProtectTool(Boolean.parseBoolean(args[4])); + setAllowWrongTool(Boolean.parseBoolean(args[5])); + setExpPenalty(Boolean.parseBoolean(args[6])); + } + } + } +} \ No newline at end of file