diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8f6fa32..fd742ed 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -4,7 +4,7 @@ stages: variables: name: "UltimateStacker" path: "/builds/$CI_PROJECT_PATH" - version: "1.9.6" + version: "1.9.9" build: stage: build diff --git a/src/main/java/com/songoda/ultimatestacker/listeners/DeathListeners.java b/src/main/java/com/songoda/ultimatestacker/listeners/DeathListeners.java index fd95e42..b31fc4e 100644 --- a/src/main/java/com/songoda/ultimatestacker/listeners/DeathListeners.java +++ b/src/main/java/com/songoda/ultimatestacker/listeners/DeathListeners.java @@ -2,24 +2,37 @@ package com.songoda.ultimatestacker.listeners; import com.songoda.lootables.loot.Drop; import com.songoda.ultimatestacker.UltimateStacker; +import com.songoda.ultimatestacker.entity.EntityStack; import com.songoda.ultimatestacker.utils.DropUtils; +import com.songoda.ultimatestacker.utils.ServerVersion; import com.songoda.ultimatestacker.utils.settings.Setting; +import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.ChestedHorse; +import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityDeathEvent; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.Damageable; +import org.bukkit.inventory.meta.ItemMeta; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.Random; public class DeathListeners implements Listener { private final UltimateStacker instance; + private Random random; public DeathListeners(UltimateStacker instance) { this.instance = instance; + random = new Random(); } @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) @@ -33,7 +46,10 @@ public class DeathListeners implements Listener { for (ItemStack item : event.getDrops()) drops.add(new Drop(item)); } - event.getDrops().clear(); + for (ItemStack item : new ArrayList<>(event.getDrops())) { + if (!shouldDrop(event.getEntity(), item.getType())) + event.getDrops().remove(item); + } if (instance.getEntityStackManager().isStacked(event.getEntity())) instance.getEntityStackManager().getStack(event.getEntity()) @@ -41,4 +57,95 @@ public class DeathListeners implements Listener { else DropUtils.processStackedDrop(event.getEntity(), drops); } + + private boolean shouldDrop(LivingEntity entity, Material material) { + if (entity.getEquipment() != null && entity.getEquipment().getArmorContents().length != 0) { + List items = new ArrayList<>(Arrays.asList(entity.getEquipment().getArmorContents())); + items.add(entity.getEquipment().getItemInHand()); + if (instance.isServerVersionAtLeast(ServerVersion.V1_9)) + items.add(entity.getEquipment().getItemInOffHand()); + for (ItemStack item : items) { + if (item.getType() == material) + return true; + } + } + if (instance.isServerVersionAtLeast(ServerVersion.V1_11) && entity instanceof ChestedHorse) { + if (((ChestedHorse) entity).getInventory().contains(material)) + return true; + } + + switch (material.name()) { + case "SADDLE": + return !entity.getType().name().equals("RAVAGER"); + case "DIAMOND_HORSE_ARMOR": + case "GOLDEN_HORSE_ARMOR": + case "IRON_HORSE_ARMOR": + case "LEATHER_HORSE_ARMOR": + case "CYAN_CARPET": + case "BLACK_CARPET": + case "BLUE_CARPET": + case "BROWN_CARPET": + case "GRAY_CARPET": + case "GREEN_CARPET": + case "LIGHT_BLUE_CARPET": + case "LIGHT_GRAY_CARPET": + case "LIME_CARPET": + case "MAGENTA_CARPET": + case "ORANGE_CARPET": + case "PINK_CARPET": + case "PURPLE_CARPET": + case "RED_CARPET": + case "WHITE_CARPET": + case "YELLOW_CARPET": + case "CARPET": + case "CHEST": + return true; + default: + return false; + } + } + + @EventHandler + public void onEntityHit(EntityDamageByEntityEvent event) { + if (!(event.getDamager() instanceof Player)) return; + if (!instance.getEntityStackManager().isStacked(event.getEntity())) return; + EntityStack stack = instance.getEntityStackManager().getStack(event.getEntity()); + + if (Setting.KILL_WHOLE_STACK_ON_DEATH.getBoolean() && Setting.REALISTIC_DAMAGE.getBoolean()) { + Player player = (Player) event.getDamager(); + ItemStack tool = player.getInventory().getItemInMainHand(); + if (tool.getType().getMaxDurability() < 1 || (tool.getItemMeta() != null && (tool.getItemMeta().isUnbreakable() + || tool.getItemMeta().spigot().isUnbreakable()))) + return; + + int unbreakingLevel = tool.getEnchantmentLevel(Enchantment.DURABILITY); + Damageable damageable = (Damageable) tool.getItemMeta(); + + int actualDamage = 0; + for (int i = 0; i < stack.getAmount(); i++) + if (checkUnbreakingChance(unbreakingLevel)) + actualDamage++; + + damageable.setDamage(damageable.getDamage() + actualDamage-1); + tool.setItemMeta((ItemMeta) damageable); + + if (!this.hasEnoughDurability(tool, 1)) + player.getInventory().setItemInMainHand(null); + + } + } + + public boolean hasEnoughDurability(ItemStack tool, int requiredAmount) { + if (!tool.hasItemMeta() || !(tool.getItemMeta() instanceof Damageable) || tool.getType().getMaxDurability() < 1) + return true; + + Damageable damageable = (Damageable) tool.getItemMeta(); + int durabilityRemaining = tool.getType().getMaxDurability() - damageable.getDamage(); + return durabilityRemaining > requiredAmount; + } + + public boolean checkUnbreakingChance(int level) { + return (1.0 / (level + 1)) > random.nextDouble(); + } + } diff --git a/src/main/java/com/songoda/ultimatestacker/lootables/LootablesManager.java b/src/main/java/com/songoda/ultimatestacker/lootables/LootablesManager.java index b41deef..8e3c6eb 100644 --- a/src/main/java/com/songoda/ultimatestacker/lootables/LootablesManager.java +++ b/src/main/java/com/songoda/ultimatestacker/lootables/LootablesManager.java @@ -40,7 +40,7 @@ public class LootablesManager { ? entity.getKiller().getItemInHand().getEnchantmentLevel(Enchantment.LOOT_BONUS_MOBS) : 0; - int rerollChance = looting / (looting + 1); + int rerollChance = Setting.REROLL.getBoolean() ? looting / (looting + 1) : 0; for (Loot loot : lootable.getRegisteredLoot()) toDrop.addAll(runLoot(entity, loot, rerollChance, looting)); @@ -53,9 +53,7 @@ public class LootablesManager { if (entity.getType() == EntityType.SHEEP) { modify = (Loot loot2) -> { Material material = loot2.getMaterial(); - if (material == (instance.isServerVersionAtLeast(com.songoda.lootables.utils.ServerVersion.V1_13) - ? Material.WHITE_WOOL : Material.valueOf("WOOL")) - && ((Sheep) entity).getColor() != null) { + if (material.name().contains("WOOL") && ((Sheep) entity).getColor() != null) { if (((Sheep) entity).isSheared()) return null; if (instance.isServerVersionAtLeast(com.songoda.lootables.utils.ServerVersion.V1_13)) loot2.setMaterial(Material.valueOf(((Sheep) entity).getColor() + "_WOOL")); @@ -72,10 +70,10 @@ public class LootablesManager { Entity killerEntity = ((EntityDamageByEntityEvent) entity.getLastDamageCause()).getDamager(); killer = killerEntity.getType(); if (killerEntity instanceof Projectile) { - Projectile projectile = (Projectile) killerEntity; - if (projectile.getShooter() instanceof Entity) { - killer = ((Entity) projectile.getShooter()).getType(); - } + Projectile projectile = (Projectile) killerEntity; + if (projectile.getShooter() instanceof Entity) { + killer = ((Entity) projectile.getShooter()).getType(); + } } } diff --git a/src/main/java/com/songoda/ultimatestacker/utils/settings/Setting.java b/src/main/java/com/songoda/ultimatestacker/utils/settings/Setting.java new file mode 100644 index 0000000..9ec7c2f --- /dev/null +++ b/src/main/java/com/songoda/ultimatestacker/utils/settings/Setting.java @@ -0,0 +1,303 @@ +package com.songoda.ultimatestacker.utils.settings; + +import com.songoda.ultimatestacker.UltimateStacker; +import com.songoda.ultimatestacker.entity.Check; +import com.songoda.ultimatestacker.entity.Split; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +public enum Setting { + + STACK_SEARCH_TICK_SPEED("Main.Stack Search Tick Speed", 5, + "The speed in which a new stacks will be created.", + "It is advised to keep this number low."), + + DISABLED_WORLDS("Main.Disabled Worlds", Arrays.asList("World1", "World2", "World3"), + "Worlds that stacking doesn't happen in."), + + STACK_ENTITIES("Entities.Enabled", true, + "Should entities be stacked?"), + + NAME_FORMAT_ENTITY("Entities.Name Format", "&f{TYPE} &6{AMT}x", + "The text displayed above an entities head where {TYPE} refers to", + "The entities type and {AMT} is the amount currently stacked."), + + SEARCH_RADIUS("Entities.Search Radius", 5, + "The distance entities must be to each other in order to stack."), + + MAX_STACK_ENTITIES("Entities.Max Stack Size", 15, + "The max amount of entities in a single stack."), + + MIN_STACK_ENTITIES("Entities.Min Stack Amount", 5, + "The minimum amount required before a stack can be formed.", + "Do not set this to lower than 2."), + + MAX_PER_TYPE_STACKS_PER_CHUNK("Entities.Max Per Type Stacks Per Chunk", -1, + "The maximum amount of each entity type stack allowed in a chunk."), + + STACK_WHOLE_CHUNK("Entities.Stack Whole Chunk", false, + "Should all qualifying entities in each chunk be stacked?", + "This will override the stacking radius."), + + ENTITY_HOLOGRAMS("Entities.Holograms Enabled", true, + "Should holograms be displayed above stacked entities?"), + + HOLOGRAMS_ON_LOOK_ENTITY("Entities.Only Show Holograms On Look", false, + "Only show nametags above an entities head when looking directly at them."), + + CUSTOM_DROPS("Entities.Custom Drops.Enabled", true, + "Should custom drops be enabled?"), + + REROLL("Entities.Custom Drops.Reroll", true, + "Increases chance of uncommon drops by making a second attempt to", + "drop if the original attempt failed (Requires the looting enchantment).", + "This is a default Minecraft mechanic."), + + KILL_WHOLE_STACK_ON_DEATH("Entities.Kill Whole Stack On Death", false, + "Should killing a stack of entities kill the whole stack or", + "just one out of the stack? If you want only certain entities to be", + "effected by this you can configure it in the entities.yml"), + + CLEAR_LAG("Entities.Clear Lag", false, + "When enabled, the plugin will hook into ClearLag and extend the", + "clear task to include stacked entities from this plugin. If this is enabled", + "the built in task will not run."), + + INSTANT_KILL("Entities.Instant Kill", Arrays.asList("FALL", "DROWNING", "LAVA", "VOID"), + "Events that will trigger an entire stack to be killed.", + "It should be noted that this is useless if the above setting is true.", + "Any of the following can be added to the list:", + "CONTACT, ENTITY_ATTACK, ENTITY_SWEEP_ATTACK, PROJECTILE", + "SUFFOCATION, FALL, FIRE, FIRE_TICK", + "MELTING, LAVA, DROWNING, BLOCK_EXPLOSION", + "ENTITY_EXPLOSION, VOID, LIGHTNING, SUICIDE", + "STARVATION, POISON, MAGIC, WITHER", + "FALLING_BLOCK, THORNS, DRAGON_BREATH, CUSTOM", + "FLY_INTO_WALL, HOT_FLOOR, CRAMMING, DRYOUT"), + + NO_EXP_INSTANT_KILL("Entities.No Exp For Instant Kills", false, + "Should no experience be dropped when an instant kill is performed?"), + + STACK_CHECKS("Entities.Stack Checks", Arrays.asList(Check.values()).stream() + .filter(Check::isEnabledByDefault).map(Check::name).collect(Collectors.toList()), + "These are checks that are processed before an entity is stacked.", + "You can add and remove from the list at will.", + "The acceptable check options are:", + "SPAWN_REASON, NERFED, AGE, TICK_AGE, PHANTOM_SIZE", + "IS_TAMED, ANIMAL_OWNER, SKELETON_TYPE", + "ZOMBIE_BABY, SLIME_SIZE, PIG_SADDLE, SHEEP_SHEERED", + "SHEEP_COLOR, WOLF_COLLAR_COLOR, OCELOT_TYPE, HORSE_COLOR", + "HORSE_STYLE, HORSE_CARRYING_CHEST, HORSE_HAS_ARMOR", + "HORSE_HAS_SADDLE, HORSE_JUMP, RABBIT_TYPE, VILLAGER_PROFESSION", + "LLAMA_COLOR, LLAMA_STRENGTH, PARROT_TYPE, PUFFERFISH_STATE", + "TROPICALFISH_PATTERN, TROPICALFISH_BODY_COLOR, TROPICALFISH_PATTERN_COLOR"), + + SPLIT_CHECKS("Entities.Split Checks", Arrays.asList(Split.values()).stream() + .map(Split::name).collect(Collectors.toList()), + "These are checks that when achieved will break separate a single entity", + "from a stack."), + + KEEP_FIRE("Entities.Keep Fire", true, + "Should fire ticks persist to the next entity when an entity dies?"), + + KEEP_POTION("Entities.Keep Potion Effects", true, + "Should potion effects persist to the next entity when an entity dies?"), + + CARRY_OVER_LOWEST_HEALTH("Entities.Carry Over Lowest Health", false, + "Should the lowest health be carried over when stacked?", + "This should not be used in collaboration with 'Stack Entity Health'.", + "If it is used this setting will be overrode."), + + ONLY_STACK_FROM_SPAWNERS("Entities.Only Stack From Spawners", false, + "Should entities only be stacked if they originate from a spawner?", + "It should be noted that the identifier that tells the plugin", + "if the entity originated from a spawner or not is wiped on", + "server restart."), + + STACK_REASONS("Entities.Stack Reasons", Arrays.asList(), + "This will limit mob stacking to mobs who spawned via the listed reasons.", + "This list is ignored if Only Stack From Spawners = true.", + "The following reasons can be added to the list:", + "NATURAL, JOCKEY, CHUNK_GEN, SPAWNER, EGG, SPAWNER_EGG, LIGHTNING, BUILD_SNOWMAN, ", + "BUILD_IRONGOLEM, BUILD_WITHER, VILLAGE_DEFENSE, VILLAGE_INVASION, BREEDING,", + "SLIME_SPLIT, REINFORCEMENTS, NETHER_PORTAL, DISPENSE_EGG, INFECTION,", + "CURED, OCELOT_BABY, SILVERFISH_BLOCK, MOUNT, TRAP, ENDER_PEARL, ", + "SHOULDER_ENTITY, DROWNED, SHEARED, EXPLOSION" + ), + + CARRY_OVER_METADATA_ON_DEATH("Entities.Carry Over Metadata On Death", true, + "With this enabled any metadata assigned from supported plugins such", + "as EpicSpawners and mcMMO will be preserved when the entity is killed."), + + ONLY_STACK_ON_SURFACE("Entities.Only Stack On Surface", true, + "Should entities only be stacked if they are touching the ground", + "or swimming? This does not effect flying entities."), + + STACK_ENTITY_HEALTH("Entities.Stack Entity Health", true, + "Should entity health be stacked? When enabled Entity stacks will", + "remember the health of all entities inside of the stack. This", + "works the best with 'Only Stack On Surface enabled' as entities", + "falling out of grinders may stack before hitting the ground."), + + ONLY_STACK_FLYING_DOWN("Entities.Only Stack Flying Down", true, + "Should entities that fly only stack with entities that are lower on the", + "Y axis. This is important for grinders so that flying entities don't continuously", + "stack upwards to a higher up entity."), + + REALISTIC_DAMAGE("Entities.Use Realistic Weapon Damage", true, + "Should weapons take damage based on the amount of entites in the stack?"), + + STACK_ITEMS("Items.Enabled", true, + "Should items be stacked?"), + + ITEM_HOLOGRAMS("Items.Holograms Enabled", true, + "Should holograms be displayed above stacked items?"), + + ITEM_HOLOGRAM_SINGLE("Items.Show Hologram For Single", true, + "Should holograms be displayed above items when there is only a single", + "item in the stack?"), + + ITEM_HOLOGRAM_BLACKLIST("Items.Show Holograms For Blacklisted Items", true, + "Should items that are blacklisted display holograms?"), + + MAX_STACK_ITEMS("Items.Max Stack Size", 512, + "The max stack size for items.", + "Currently this can only be set to a max of 120."), + + NAME_FORMAT_ITEM("Items.Name Format", "&f{TYPE} &r[&6{AMT}x]", + "The text displayed above a dropped item."), + + NAME_FORMAT_RESET("Items.Name Format Reset", true, + "Should color codes in dropped item names be removed?", + "This is added only because it looks smoother in game. This is only visual and", + "doesn't actually effect the item."), + + ITEM_BLACKLIST("Items.Blacklist", Collections.singletonList("EGG"), + "Items included in this list will stack to default Minecraft amounts.", + "Material list: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html", + "Leave this empty by using \"blacklist: []\" if you do not wish to disable", + "stacking for any items."), + + ITEM_WHITELIST("Items.Whitelist", new ArrayList(), + "Items included in this whitelist will be stacked.", + "Material list: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html", + "Leave this empty by using \"whitelist: []\" if you want everything to be stacked.", + "Items not in this list will act as if they are blacklisted."), + + SHOW_STACK_SIZE_SINGLE("Items.Show Stack Size For Single", false, + "When enabled stack sizes for a stack with a single item will", + "not display the stack size. The stack size will be added", + "for stacks containing two or more items."), + + SPAWNERS_ENABLED("Spawners.Enabled", true, + "Should spawners be stacked?"), + + SPAWNER_HOLOGRAMS("Spawners.Holograms Enabled", true, + "Should holograms be displayed above stacked spawners?"), + + EGGS_CONVERT_SPAWNERS("Spawners.Eggs Convert Spawners", true, + "Should eggs convert spawners? If enabled you will", + "still need to give perms for it to work."), + + MAX_STACK_SPAWNERS("Spawners.Max Stack Size", 5, + "What should the max a spawner can stack to be?"), + + SNEAK_FOR_STACK("Spawners.Sneak To Receive A Stacked Spawner", true, + "Toggle ability to receive a stacked spawner when breaking a spawner while sneaking."), + + SPAWNERS_DONT_EXPLODE("Spawners.Prevent Spawners From Exploding", false, + "Should spawners not break when blown up?"), + + EXPLOSION_DROP_CHANCE_TNT("Spawners.Chance On TNT Explosion", "100%", + "Chance of a TNT explosion dropping a spawner."), + + EXPLOSION_DROP_CHANCE_CREEPER("Spawners.Chance On Creeper Explosion", "100%", + "Chance of a creeper explosion dropping a spawner."), + + NAME_FORMAT_SPAWNER("Spawners.Name Format", "&f{TYPE} Spawner &6{AMT}x", + "The text displayed above a stacked spawner where {TYPE} refers to", + "The entities type and {AMT} is the amount currently stacked."), + + LANGUGE_MODE("System.Language Mode", "en_US", + "The enabled language file.", + "More language files (if available) can be found in the plugins data folder."), + + MYSQL_ENABLED("MySQL.Enabled", false, "Set to 'true' to use MySQL instead of SQLite for data storage."), + MYSQL_HOSTNAME("MySQL.Hostname", "localhost"), + MYSQL_PORT("MySQL.Port", 3306), + MYSQL_DATABASE("MySQL.Database", "your-database"), + MYSQL_USERNAME("MySQL.Username", "user"), + MYSQL_PASSWORD("MySQL.Password", "pass"), + MYSQL_USE_SSL("MySQL.Use SSL", false); + + private String setting; + private Object option; + private String[] comments; + + Setting(String setting, Object option, String... comments) { + this.setting = setting; + this.option = option; + this.comments = comments; + } + + Setting(String setting, Object option) { + this.setting = setting; + this.option = option; + this.comments = null; + } + + public static Setting getSetting(String setting) { + List settings = Arrays.stream(values()).filter(setting1 -> setting1.setting.equals(setting)).collect(Collectors.toList()); + if (settings.isEmpty()) return null; + return settings.get(0); + } + + public String getSetting() { + return setting; + } + + public Object getOption() { + return option; + } + + public String[] getComments() { + return comments; + } + + public List getIntegerList() { + return UltimateStacker.getInstance().getConfig().getIntegerList(setting); + } + + public List getStringList() { + return UltimateStacker.getInstance().getConfig().getStringList(setting); + } + + public boolean getBoolean() { + return UltimateStacker.getInstance().getConfig().getBoolean(setting); + } + + public int getInt() { + return UltimateStacker.getInstance().getConfig().getInt(setting); + } + + public long getLong() { + return UltimateStacker.getInstance().getConfig().getLong(setting); + } + + public String getString() { + return UltimateStacker.getInstance().getConfig().getString(setting); + } + + public char getChar() { + return UltimateStacker.getInstance().getConfig().getString(setting).charAt(0); + } + + public double getDouble() { + return UltimateStacker.getInstance().getConfig().getDouble(setting); + } + +} \ No newline at end of file diff --git a/target/maven-archiver/pom.properties b/target/maven-archiver/pom.properties deleted file mode 100644 index 9ffa5cb..0000000 --- a/target/maven-archiver/pom.properties +++ /dev/null @@ -1,5 +0,0 @@ -#Generated by Maven -#Sat Dec 08 20:46:16 CET 2018 -version=maven-version-number -groupId=com.songoda -artifactId=UltimateStacker