diff --git a/src/main/java/me/shansen/EggCatcher/listeners/EggCatcherEntityListener.java b/src/main/java/me/shansen/EggCatcher/listeners/EggCatcherEntityListener.java index 83a3ee7..533c6c7 100644 --- a/src/main/java/me/shansen/EggCatcher/listeners/EggCatcherEntityListener.java +++ b/src/main/java/me/shansen/EggCatcher/listeners/EggCatcherEntityListener.java @@ -27,6 +27,7 @@ import org.bukkit.Effect; import org.bukkit.Material; +import org.bukkit.attribute.Attribute; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.entity.*; import org.bukkit.event.EventHandler; @@ -40,13 +41,15 @@ import org.bukkit.plugin.java.JavaPlugin; import java.io.File; +import java.util.Objects; +import java.util.Optional; public class EggCatcherEntityListener implements Listener { private final boolean usePermissions; private final boolean useCatchChance; private final boolean useHealthPercentage; - private final boolean looseEggOnFail; + private final boolean loseEggOnFail; private final boolean useVaultCost; private final boolean useItemCost; private final boolean explosionEffect; @@ -65,8 +68,8 @@ public class EggCatcherEntityListener implements Listener { private final boolean logCaptures; FileConfiguration config; JavaPlugin plugin; - private final File captureLogFile; - private final EggCatcherLogger captureLogger; + private final File captureLogFile; + private final EggCatcherLogger captureLogger; public EggCatcherEntityListener(JavaPlugin plugin) { @@ -75,7 +78,7 @@ public EggCatcherEntityListener(JavaPlugin plugin) { this.usePermissions = this.config.getBoolean("UsePermissions", true); this.useCatchChance = this.config.getBoolean("UseCatchChance", true); this.useHealthPercentage = this.config.getBoolean("UseHealthPercentage", false); - this.looseEggOnFail = this.config.getBoolean("LooseEggOnFail", true); + this.loseEggOnFail = this.config.getBoolean("LooseEggOnFail", true); this.useVaultCost = this.config.getBoolean("UseVaultCost", false); this.useItemCost = this.config.getBoolean("UseItemCost", false); this.explosionEffect = this.config.getBoolean("ExplosionEffect", true); @@ -92,159 +95,67 @@ public EggCatcherEntityListener(JavaPlugin plugin) { this.vaultTargetBankAccount = this.config.getString("VaultTargetBankAccount", ""); this.deleteVillagerInventoryOnCatch = this.config.getBoolean("DeleteVillagerInventoryOnCatch", false); this.logCaptures = this.config.getBoolean("LogEggCaptures", false); - this.captureLogFile = new File(plugin.getDataFolder(), "captures.txt"); - this.captureLogger = new EggCatcherLogger(captureLogFile); - } + this.captureLogFile = new File(plugin.getDataFolder(), "captures.txt"); + this.captureLogger = new EggCatcherLogger(captureLogFile); + } @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) public void onEntityHitByEgg(EntityDamageEvent event) { - EntityDamageByEntityEvent damageEvent = null; - Egg egg = null; - EggType eggType = null; - double vaultCost = 0.0; - Entity entity = event.getEntity(); - - if (!(event instanceof EntityDamageByEntityEvent)) { - return; - } - - damageEvent = (EntityDamageByEntityEvent) event; - - if (!(damageEvent.getDamager() instanceof Egg)) { - return; - } - - egg = (Egg) damageEvent.getDamager(); - eggType = EggType.getEggType(entity); + final Entity entity = event.getEntity(); + + Optional.of(event) + // only capture entity damage by other entities, don't care about block damage + .filter(EntityDamageByEntityEvent.class::isInstance) + .map(EntityDamageByEntityEvent.class::cast) + + // egg catcher event restrictions + .filter(this::checkIsEggEvent) + .filter(this::checkAnimalRestrictions) + .map(entityEvent -> new EggCaptureEvent(entity, (Egg) entityEvent.getDamager())) + .ifPresent(eggEvent -> this.plugin.getServer().getPluginManager().callEvent(eggEvent)); + } - if (eggType == null) { - return; - } + @EventHandler(ignoreCancelled = true, priority = EventPriority.NORMAL) + public void onEggCaptureEvent(EggCaptureEvent event) { + Egg egg = event.getEgg(); + Entity entity = event.getEntity(); + EggType eggType = EggType.getEggType(entity); if (!this.spawnChickenOnFail) { EggCatcher.eggs.add(egg); } - if (this.preventCatchingBabyAnimals) { - if (entity instanceof Ageable) { - if (!((Ageable) entity).isAdult()) { - return; - } - } - } - - if (this.preventCatchingTamedAnimals) { - if (entity instanceof Tameable) { - if (((Tameable) entity).isTamed()) { - return; - } - } - } - - if (this.preventCatchingShearedSheeps) { - if (entity instanceof Sheep) { - if (((Sheep) entity).isSheared()) { - return; - } - } - } - - - EggCaptureEvent eggCaptureEvent = new EggCaptureEvent(entity, egg); - this.plugin.getServer().getPluginManager().callEvent(eggCaptureEvent); - if (eggCaptureEvent.isCancelled()) { - return; - } - if (egg.getShooter() instanceof Player) { Player player = (Player) egg.getShooter(); + double vaultCost = getVaultCost(player, Objects.requireNonNull(eggType).getFriendlyName()); + ItemStack itemCost = getItemCost(player, eggType.getFriendlyName()); - if (this.usePermissions) { - if (!player.hasPermission("eggcatcher.catch." + eggType.getFriendlyName().toLowerCase())) { - player.sendMessage(config.getString("Messages.PermissionFail")); - if (!this.looseEggOnFail) { - player.getInventory().addItem(new ItemStack(Material.EGG, 1)); - EggCatcher.eggs.add(egg); - } - return; - } - } - - if (this.useHealthPercentage) { - double healthPercentage = config.getDouble("HealthPercentage." + eggType.getFriendlyName()); - double currentHealth = ((LivingEntity) entity).getHealth() * 100.0 / ((LivingEntity) entity) - .getMaxHealth(); - if (healthPercentage < currentHealth) { - if (this.healthPercentageFailMessage.length() > 0) { - player.sendMessage(String.format(this.healthPercentageFailMessage, healthPercentage)); - } - if (!this.looseEggOnFail) { - player.getInventory().addItem(new ItemStack(Material.EGG, 1)); - EggCatcher.eggs.add(egg); - } - return; - } + // check that the player can actually catch this mob + if (!playerHasRequirements(event, player, vaultCost, itemCost)) { + return; } - if (this.useCatchChance) { - double catchChance = config.getDouble("CatchChance." + eggType.getFriendlyName()); - if (Math.random() * 100 <= catchChance) { - if (this.catchChanceSuccessMessage.length() > 0) { - player.sendMessage(catchChanceSuccessMessage); - } - } else { - if (this.catchChanceFailMessage.length() > 0) { - player.sendMessage(this.catchChanceFailMessage); - } - if (!this.looseEggOnFail) { - player.getInventory().addItem(new ItemStack(Material.EGG, 1)); - EggCatcher.eggs.add(egg); - } - return; - } + // send chance success message + if (this.useCatchChance && this.catchChanceSuccessMessage.length() != 0) { + player.sendMessage(this.catchChanceSuccessMessage); } - - boolean freeCatch = player.hasPermission("eggcatcher.free"); - - if (this.useVaultCost && !freeCatch) { - vaultCost = config.getDouble("VaultCost." + eggType.getFriendlyName()); - if (!EggCatcher.economy.has(player.getName(), vaultCost)) { - player.sendMessage(String.format(config.getString("Messages.VaultFail"), vaultCost)); - if (!this.looseEggOnFail) { - player.getInventory().addItem(new ItemStack(Material.EGG, 1)); - EggCatcher.eggs.add(egg); - } - return; - } else { - EggCatcher.economy.withdrawPlayer(player.getName(), vaultCost); - if (!this.vaultTargetBankAccount.isEmpty()) { - EggCatcher.economy.bankDeposit(this.vaultTargetBankAccount, vaultCost); - } + // withdraw money + if (vaultCost > 0) { + EggCatcher.economy.withdrawPlayer(player, vaultCost); - player.sendMessage(String.format(config.getString("Messages.VaultSuccess"), vaultCost)); + if (!this.vaultTargetBankAccount.isEmpty()) { + EggCatcher.economy.bankDeposit(this.vaultTargetBankAccount, vaultCost); } + + player.sendMessage(String.format(config.getString("Messages.VaultSuccess"), vaultCost)); } - if (this.useItemCost && !freeCatch) { - int itemId = config.getInt("ItemCost.ItemId", 266); - int itemData = config.getInt("ItemCost.ItemData", 0); - int itemAmount = config.getInt("ItemCost.Amount." + eggType.getFriendlyName(), 0); - @SuppressWarnings("deprecation") - ItemStack itemStack = new ItemStack(itemId, itemAmount, (short) itemData); - if (player.getInventory().containsAtLeast(itemStack, itemStack.getAmount())) { - player.sendMessage(String.format(config.getString("Messages.ItemCostSuccess"), - String.valueOf(itemAmount))); - player.getInventory().removeItem(itemStack); - } else { - player.sendMessage(String.format(config.getString("Messages.ItemCostFail"), - String.valueOf(itemAmount))); - if (!this.looseEggOnFail) { - player.getInventory().addItem(new ItemStack(Material.EGG, 1)); - EggCatcher.eggs.add(egg); - } - return; - } + // withdraw items + if (itemCost != null) { + player.sendMessage(String.format(config.getString("Messages.ItemCostSuccess"), + String.valueOf(itemCost.getAmount()))); + player.getInventory().removeItem(itemCost); } } else { // Dispenser @@ -268,10 +179,8 @@ public void onEntityHitByEgg(EntityDamageEvent event) { } ItemStack eggStack = new ItemStack(Material.MONSTER_EGG, 1, eggType.getCreatureId()); - eggStack = NbtReflection.setNewEntityTag(eggStack, entity.getType().getName()); - - String customName = ((LivingEntity) entity).getCustomName(); + String customName = entity.getCustomName(); if (customName != null) { // Entity had custom name @@ -280,25 +189,17 @@ public void onEntityHitByEgg(EntityDamageEvent event) { eggStack.setItemMeta(meta); } - if(entity instanceof Pig) { - if(((Pig)entity).hasSaddle()) { - entity.getWorld().dropItem(entity.getLocation(), new ItemStack(Material.SADDLE, 1)); - } - } - - if(entity instanceof Horse) { - if(((Horse) entity).isCarryingChest()){ - entity.getWorld().dropItemNaturally(entity.getLocation(), new ItemStack(Material.CHEST)); - } - } - - if((entity instanceof Villager && !this.deleteVillagerInventoryOnCatch) || + if (entity instanceof Pig && ((Pig) entity).hasSaddle()) { + entity.getWorld().dropItem(entity.getLocation(), new ItemStack(Material.SADDLE, 1)); + } else if (entity instanceof ChestedHorse && ((ChestedHorse) entity).isCarryingChest()) { + entity.getWorld().dropItemNaturally(entity.getLocation(), new ItemStack(Material.CHEST)); + } else if ((entity instanceof Villager && !this.deleteVillagerInventoryOnCatch) || (!(entity instanceof Villager) && entity instanceof InventoryHolder)) { ItemStack[] items = ((InventoryHolder) entity).getInventory().getContents(); - for(ItemStack itemStack : items) { - if(itemStack!=null){ + for (ItemStack itemStack : items) { + if (itemStack != null) { entity.getWorld().dropItemNaturally(entity.getLocation(), itemStack); } } @@ -311,9 +212,139 @@ public void onEntityHitByEgg(EntityDamageEvent event) { EggCatcher.eggs.add(egg); } } - - if (this.logCaptures){ - captureLogger.logToFile("Player " + ((Player) egg.getShooter()).getName() + " caught " + entity.getType() + " at X" + Math.round(entity.getLocation().getX()) + ",Y" + Math.round(entity.getLocation().getY()) + ",Z" + Math.round(entity.getLocation().getZ())); + + if (this.logCaptures) { + captureLogger.logToFile(String.format("Player %s caught %s at X%d,Y%d,Z%d", + ((Player) egg.getShooter()).getName(), + entity.getType().toString(), + Math.round(entity.getLocation().getX()), + Math.round(entity.getLocation().getY()), + Math.round(entity.getLocation().getZ()))); + } + } + + /** + * Check that the player satisfies requirements necessary to capture mob + * + * @param event egg capture event + * @param player player causing event + * @param vaultCost economy cost to capture mob + * @param itemCost item cost to capture mob + * @return if player should catch the mob + */ + private boolean playerHasRequirements(EggCaptureEvent event, Player player, double vaultCost, ItemStack itemCost) { + LivingEntity entity = (LivingEntity) event.getEntity(); + String eggType = Objects.requireNonNull(EggType.getEggType(event.getEntity())).getFriendlyName(); + double entityHealthRequirement = getEntityHealthRequirement(eggType); + + String message = null; + boolean hasRequirements = true; + + // perform player egg capture validations + if (this.usePermissions && !player.hasPermission("eggcatcher.catch." + eggType.toLowerCase())) { + hasRequirements = false; + message = config.getString("Messages.PermissionFail"); + } else if (entityHealthRequirement > 0 && entityHealthRequirement < getEntityHealthPercentage(entity)) { + hasRequirements = false; + if (this.healthPercentageFailMessage.length() > 0) { + message = String.format(this.healthPercentageFailMessage, entityHealthRequirement); + } + } else if (this.useCatchChance && Math.random() * 100 > config.getDouble("CatchChance." + eggType)) { + hasRequirements = false; + message = this.catchChanceFailMessage; + } else if (vaultCost > 0 && !EggCatcher.economy.has(player, vaultCost)) { + hasRequirements = false; + message = String.format(config.getString("Messages.VaultFail"), vaultCost); + } else if (itemCost != null && !player.getInventory().containsAtLeast(itemCost, itemCost.getAmount())) { + hasRequirements = false; + message = String.format(config.getString("Messages.ItemCostFail"), String.valueOf(itemCost.getAmount())); + } + + if (message != null && message.length() != 0) { + player.sendMessage(message); + } + + if (!hasRequirements) { + if (!this.loseEggOnFail) { + player.getInventory().addItem(new ItemStack(Material.EGG, 1)); + EggCatcher.eggs.add(event.getEgg()); + } } + + return hasRequirements; + } + + /** + * Get the vault (economy) cost of capturing the given egg mob + * + * @param p player attempting to capture mob + * @param eggType friendly name of the mob egg being caught + * @return vault cost of capturing the mob egg + */ + private double getVaultCost(Player p, String eggType) { + return this.useVaultCost && !p.hasPermission("eggcatcher.free") ? config.getDouble("VaultCost." + eggType) : 0; + } + + /** + * Get the item cost of catching a mob egg + * + * @param p player attempting to capture mob + * @param eggType friendly name of the mob egg being caught + * @return item cost of capturing the mob egg + */ + private ItemStack getItemCost(Player p, String eggType) { + if (this.useItemCost && !p.hasPermission("eggcatcher.free")) { + int itemId = config.getInt("ItemCost.ItemId", 266); + int itemData = config.getInt("ItemCost.ItemData", 0); + int itemAmount = config.getInt("ItemCost.Amount." + eggType, 0); + return new ItemStack(itemId, itemAmount, (short) itemData); + } + return null; + } + + /** + * Get the required health percentage to catch given mob egg + * + * @param eggType friendly name of mob egg type being caught + * @return required health percentage to catch given mob egg + */ + private double getEntityHealthRequirement(String eggType) { + return this.useHealthPercentage ? config.getDouble("HealthPercentage." + eggType) : 0; + } + + /** + * Get the current percentage of max health an entity currently has + * + * @param entity Entity to get health percentage of + * @return current percentage of max health an entity currently has + */ + private double getEntityHealthPercentage(LivingEntity entity) { + return entity.getHealth() * 100.0 / entity.getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue(); + } + + /** + * Check that the EntityDamageByEntityEvent is damage done by a valid egg to a valid egg-able mob + * + * @param e EntityDamageByEntityEvent + * @return if this event is a valid egg capture + */ + private boolean checkIsEggEvent(EntityDamageByEntityEvent e) { + return e.getDamager() instanceof Egg && EggType.getEggType(e.getEntity()) != null; + } + + /** + * Check that the entity being damaged satisfies the configured baby/tamed/sheared restrictions + * + * @param e entity damage by entity event + * @return if the entity being damated satisfies the restrictions + */ + private boolean checkAnimalRestrictions(EntityDamageByEntityEvent e) { + Entity entity = e.getEntity(); + + return !( + (this.preventCatchingBabyAnimals && entity instanceof Ageable && !((Ageable) entity).isAdult()) || + (this.preventCatchingTamedAnimals && entity instanceof Tameable && ((Tameable) entity).isTamed()) || + (this.preventCatchingShearedSheeps && entity instanceof Sheep && ((Sheep) entity).isSheared()) + ); } }