diff --git a/config.yml b/config.yml index ac928e8..d573702 100644 --- a/config.yml +++ b/config.yml @@ -1,6 +1,14 @@ # Only drop heads when killed directly by a player player-kills-only: true +# Whether to drop heads if killed by projectile weapon from a player +# Only checked if player-kills-only is true +drop-for-ranged-kills: false + +# Whether to drop heads for any death cause if last-damager is a player +# Only checked if player-kills-only is true +drop-for-indirect-kills: false + # Only drop player heads (you can also do this in "default chances" file # by making all mobs except players have a 0% chance of dropping a head) player-heads-only: false @@ -8,30 +16,35 @@ player-heads-only: false # Whether to keep entity heads synced with the latest vanilla textures refresh-textures: true +# Whether to reset the item name for a head when refreshing its texture +refresh-item-names: false + # When a player changes their skin, should old heads be updated to the # new texture? Note: to refresh, heads must be mined or dropped update-on-skin-change: false # Enable the mechanic where any mob killed by a charged creeper explosion # will drop its head with 100% probability * the spawn-cause modifier -# This setting will override "player-kills-only" (but not "player-heads-only") +# This setting overrides "player-kills-only" (but not "player-heads-only") charged-creeper-drops: true # A Minecraft easter egg is that mobs named 'Dinnerbone' or 'Grumm' are # upside down.. If you want their heads to share this trait, set to true grumm-heads: true -# Percent increase in drop chance per looting level -looting-bonus: 0.4 - -# Uses non-linear (taylor series modifiers) when calculating drop chance -use-taylor-modifiers: true +# Uses non-linear function when calculating drop chance looting modifier. +# If set to 'false', each looting level will add 1% to the drop chance. +use-taylor-modifiers: false # Only drop heads if the killer used an axe must-use-axe: false # Enable plugin Updater (checks for new versions with new head textures) -update-plugin: true +# Note: Disabled by default because some servers have reported errors +update-plugin: false + +# Run in debug mode (prints extra information to console) +debug-messages: true # (Optional) Whitelist of tools a player must be using for heads to drop # If "must-use-axe" is enabled, this list will be ignored @@ -43,6 +56,7 @@ must-use: # (Optional) Let certain tools have a higher chance of beheading # The number is the percent increase in drop chance when using that tool +# Eg: 3 = 3x chance, 0.8 = 80% chance increase, etc. specific-tool-modifiers: GOLD_AXE: 3 DIAMOND_AXE: 1 diff --git a/head-drop-rates.txt b/head-drop-rates.txt index 013fa6b..f051b85 100644 --- a/head-drop-rates.txt +++ b/head-drop-rates.txt @@ -1,21 +1,21 @@ PLAYER: 0.15 UNKNOWN: 0.0001 BAT: 0.01 -BLAZE: 0.02 +BLAZE: 0.009 CAT: 0.008 CAVE_SPIDER: 0.001 CHICKEN: 0.001 COD: 0.009 -COW: 0.004 +COW: 0.003 CREEPER: 0.04 : NoLooting DOLPHIN: 0.01 DONKEY: 0.007 -DROWNED: 0.008 +DROWNED: 0.005 ELDER_GUARDIAN: 0.05 ENDER_DRAGON: 0.5 ENDERMAN: 0.005 ENDERMITE: 0.01 -EVOKER: 0.15 +EVOKER: 0.008 GHAST: 0.006 GIANT: 0.05 GUARDIAN: 0.004 @@ -34,7 +34,7 @@ PARROT: 0.008 PHANTOM: 0.009 PIG: 0.004 PIG_ZOMBIE: 0.002 -PILLAGER: 0.01 +PILLAGER: 0.008 POLAR_BEAR: 0.008 PUFFERFISH: 0.012 RABBIT: 0.003 @@ -47,7 +47,7 @@ SKELETON: 0.007 SKELETON_HORSE: 0.01 SLIME: 0.006 SNOWMAN: 0.008 -SPIDER: 0.007 +SPIDER: 0.006 SQUID: 0.005 STRAY: 0.009 TROPICAL_FISH: 0.02 @@ -55,13 +55,13 @@ TURTLE: 0.007 TRADER_LLAMA: 0.012 VEX: 0.007 VILLAGER: 0.007 -VINDICATOR: 0.01 +VINDICATOR: 0.008 WANDERING_TRADER: 0.017 WITCH: 0.004 WITHER: 0.05 WITHER_SKELETON: 0.025 # Vanilla default WOLF: 0.007 -ZOMBIE: 0.007 +ZOMBIE: 0.006 ZOMBIE_HORSE: 0.007 ZOMBIE_VILLAGER: 0.008 diff --git a/head-textures.txt b/head-textures.txt index b8934f8..95549fb 100644 --- a/head-textures.txt +++ b/head-textures.txt @@ -157,7 +157,7 @@ TROPICAL_FISH|CLOWNFISH: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJ TROPICAL_FISH|COTTON_CANDY_BETTA: xxx TROPICAL_FISH|DOTTYBACK: xxx TROPICAL_FISH|EMPEROR_RED_SNAPPER: xxx -TROPICAL_FISH|RED_EMPEROR: EMPEROR_RED_SNAPPER +TROPICAL_FISH|RED_EMPEROR: xxx TROPICAL_FISH|GOATFISH: xxx TROPICAL_FISH|MOORISH_IDOL: xxx TROPICAL_FISH|ORNATE_BUTTERFLYFISH: xxx diff --git a/plugin.yml b/plugin.yml index 22980c6..c7d2f06 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,7 +1,7 @@ name: DropHeads main: net.evmodder.DropHeads.DropHeads author: EvModder/EvilWitchdoctor -version: 3.2.8 #8=stable 1.14 release +version: 3.2.10 #10=item renaming,wither drop rate fix api-version: 1.13 commands: @@ -9,28 +9,28 @@ commands: description: Get a player or mob head usage: /gethead [name] aliases: [gethead,dhspawn,spawn-head,head,phspawn,skull] - permission: evp.dropheads.spawn + permission: dropheads.spawn permission-message: You do not have permission to do this. debug_all_heads: description: Place all heads into the world around the player usage: /debug_all_heads - permission: evp.dropheads.debug + permission: dropheads.debug permission-message: You do not have permission to do this. permissions: - evp.dropheads.alwaysbehead: + dropheads.alwaysbehead: description: This player will get heads 100% of the time default: false - evp.dropheads.canbehead: + dropheads.canbehead: description: This player will be able to get heads default: true - evp.dropheads.spawn: + dropheads.spawn: description: Permission to spawn heads with a command default: op - evp.dropheads.debug: + dropheads.debug: description: Permission to spawn all the heads in a big cube default: false \ No newline at end of file diff --git a/src/net/evmodder/DropHeads/DropHeads.java b/src/net/evmodder/DropHeads/DropHeads.java index b18807d..72784bd 100644 --- a/src/net/evmodder/DropHeads/DropHeads.java +++ b/src/net/evmodder/DropHeads/DropHeads.java @@ -1,3 +1,21 @@ +/* + * DropHeds - a Bukkit plugin for naturally dropping mob heads + * + * Copyright (C) 2017 - 2019 Nathan / EvModder + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package net.evmodder.DropHeads; import net.evmodder.DropHeads.commands.*; @@ -18,7 +36,10 @@ public final class DropHeads extends EvPlugin{ api = new HeadAPI(); getServer().getPluginManager().registerEvents(new EntitySpawnListener(), this); getServer().getPluginManager().registerEvents(new EntityDeathListener(), this); - if(config.getBoolean("refresh-textures", true)){ + if(config.getBoolean("drop-for-indirect-kills", false)){ + getServer().getPluginManager().registerEvents(new EntityDamageListener(), this); + } + if(config.getBoolean("refresh-textures", false)){ getServer().getPluginManager().registerEvents(new ItemDropListener(), this); getServer().getPluginManager().registerEvents(new BlockBreakListener(), this); } diff --git a/src/net/evmodder/DropHeads/TextureKeyLookup.java b/src/net/evmodder/DropHeads/TextureKeyLookup.java index 37b012b..3e70b91 100644 --- a/src/net/evmodder/DropHeads/TextureKeyLookup.java +++ b/src/net/evmodder/DropHeads/TextureKeyLookup.java @@ -122,7 +122,8 @@ static String getTextureKey(LivingEntity entity){ if(entity.getCustomName() != null && entity.getCustomName().equals("jeb_")) return "SHEEP|JEB"; else return "SHEEP|"+((Sheep)entity).getColor().name(); case SHULKER: - return "SHULKER|"+((Shulker)entity).getColor().name(); + DyeColor color = ((Shulker)entity).getColor(); + return color == null ? "SHULKER" : "SHULKER|"+color.name(); case TROPICAL_FISH: TropicalFish f = (TropicalFish)entity; return "TROPICAL_FISH|"+f.getBodyColor()+"|"+f.getPatternColor()+"|"+f.getPattern(); diff --git a/src/net/evmodder/DropHeads/listeners/EntityDamageListener.java b/src/net/evmodder/DropHeads/listeners/EntityDamageListener.java new file mode 100644 index 0000000..f1bb889 --- /dev/null +++ b/src/net/evmodder/DropHeads/listeners/EntityDamageListener.java @@ -0,0 +1,29 @@ +package net.evmodder.DropHeads.listeners; + +import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.metadata.FixedMetadataValue; +import net.evmodder.DropHeads.DropHeads; + +public class EntityDamageListener implements Listener{ + final DropHeads pl; + final boolean allowProjectileKills; + + // Only enabled if drop-for-indirect-kills is TRUE + public EntityDamageListener(){ + pl = DropHeads.getPlugin(); + allowProjectileKills = pl.getConfig().getBoolean("drop-for-ranged-kills", false); + } + @EventHandler(priority = EventPriority.HIGH) + public void entityDamageEvent(EntityDamageByEntityEvent evt){ + if(evt.isCancelled()) return; + if(evt.getDamager() instanceof Player || (allowProjectileKills && evt.getDamager() instanceof Projectile + && ((Projectile)evt.getDamager()).getShooter() instanceof Player)){ + evt.getEntity().setMetadata("PlayerDamage", new FixedMetadataValue(pl, System.currentTimeMillis())); + } + } +} \ No newline at end of file diff --git a/src/net/evmodder/DropHeads/listeners/EntityDeathListener.java b/src/net/evmodder/DropHeads/listeners/EntityDeathListener.java index c219083..a6a06d6 100644 --- a/src/net/evmodder/DropHeads/listeners/EntityDeathListener.java +++ b/src/net/evmodder/DropHeads/listeners/EntityDeathListener.java @@ -3,9 +3,8 @@ import java.io.InputStream; import java.util.HashMap; import java.util.HashSet; -import java.util.Map; +import java.util.Iterator; import java.util.Random; -import java.util.Set; import org.bukkit.Material; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.enchantments.Enchantment; @@ -14,6 +13,7 @@ import org.bukkit.entity.EntityType; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityDamageByEntityEvent; @@ -21,27 +21,33 @@ import org.bukkit.event.entity.EntityDeathEvent; import org.bukkit.inventory.ItemStack; import net.evmodder.DropHeads.DropHeads; +import net.evmodder.EvLib.EvUtils; import net.evmodder.EvLib.FileIO; public class EntityDeathListener implements Listener{ final DropHeads pl; - final boolean playerKillsOnly, playerHeadsOnly, useTaylorModifiers, chargedCreepers; - final Set mustUseTools = new HashSet(); - final Set noLootingEffectMobs = new HashSet(); - final Map mobChances = new HashMap(); - final Map toolBonuses = new HashMap(); - final double lootingBonus; double DEFAULT_CHANCE; + final boolean playerKillsOnly, allowIndirectKills, allowProjectileKills; + final boolean playerHeadsOnly, useTaylorModifiers, CHARGED_CREEPER_DROPS; + final HashSet mustUseTools; + final HashSet noLootingEffectMobs; + final HashMap mobChances; + final HashMap toolBonuses; + double DEFAULT_CHANCE; final Random rand; + final boolean DEBUG_MODE; public EntityDeathListener(){ pl = DropHeads.getPlugin(); playerKillsOnly = pl.getConfig().getBoolean("player-kills-only", true); + allowIndirectKills = pl.getConfig().getBoolean("drop-for-indirect-kills", false); + allowProjectileKills = pl.getConfig().getBoolean("drop-for-ranged-kills", false); playerHeadsOnly = pl.getConfig().getBoolean("player-heads-only", false); useTaylorModifiers = pl.getConfig().getBoolean("use-taylor-modifiers", true); - chargedCreepers = pl.getConfig().getBoolean("charged-creeper-drops", true); - lootingBonus = pl.getConfig().getDouble("looting-bonus", 0.4); + CHARGED_CREEPER_DROPS = pl.getConfig().getBoolean("charged-creeper-drops", true); + DEBUG_MODE = pl.getConfig().getBoolean("debug-messages", true); rand = new Random(); + mustUseTools = new HashSet(); if(pl.getConfig().getBoolean("must-use-axe")){ mustUseTools.add(Material.DIAMOND_AXE); mustUseTools.add(Material.IRON_AXE); @@ -56,95 +62,114 @@ else for(String toolName : pl.getConfig().getStringList("must-use")){ else pl.getLogger().warning("Unknown Tool \""+toolName+"\"!"); } + toolBonuses = new HashMap(); ConfigurationSection specificModifiers = pl.getConfig().getConfigurationSection("specific-tool-modifiers"); if(specificModifiers != null) for(String toolName : specificModifiers.getKeys(false)){ Material mat = Material.getMaterial(toolName.toUpperCase()); - if(mat != null) toolBonuses.put(mat, (float) specificModifiers.getDouble(toolName)); + if(mat != null) toolBonuses.put(mat, specificModifiers.getDouble(toolName)); } //Load individual mobs' drop chances InputStream defaultChances = pl.getClass().getResourceAsStream("/head-drop-rates.txt"); String chances = FileIO.loadFile("head-drop-rates.txt", defaultChances); + mobChances = new HashMap(); + noLootingEffectMobs = new HashSet(); for(String line : chances.split("\n")){ String[] parts = line.replace(" ", "").replace("\t", "").toUpperCase().split(":"); - if(parts.length > 1){ - try{ - if(parts[0].equals("UNKNOWN")){ - DEFAULT_CHANCE = Float.parseFloat(parts[1]); - continue; - } - EntityType eType = EntityType.valueOf(parts[0]); - float dropChance = Float.parseFloat(parts[1]); - mobChances.put(eType, dropChance); - if(parts.length > 2 && parts[2].equals("NOLOOTING")) noLootingEffectMobs.add(eType); - // Commented out to allow >1 because Spawn-Reason modifiers are added after this - /*if(dropChance > 1F){ - pl.getLogger().severe("Invalid value: "+parts[1]); - pl.getLogger().severe("Drop chance must be between 0 and 1"); - mobChances.put(eType, Math.min(dropChance/10F, 1F)); - }*/ - } - catch(NumberFormatException ex){pl.getLogger().severe("Invalid value: "+parts[1]);} - catch(IllegalArgumentException ex){ - pl.getLogger().severe("Unknown entity type: "+parts[0]); + if(parts.length < 2) continue; + try{ + if(parts[0].equals("UNKNOWN")){ + DEFAULT_CHANCE = Float.parseFloat(parts[1]); + continue; } + EntityType eType = EntityType.valueOf(parts[0]); + double dropChance = Double.parseDouble(parts[1]); + mobChances.put(eType, dropChance); + if(parts.length > 2 && parts[2].equals("NOLOOTING")) noLootingEffectMobs.add(eType); + // Commented out to allow >1 because Spawn-Reason modifiers are added after this + /*if(dropChance > 1F){ + pl.getLogger().severe("Invalid value: "+parts[1]); + pl.getLogger().severe("Drop chance must be between 0 and 1"); + mobChances.put(eType, Math.min(dropChance/10F, 1F)); + }*/ + } + catch(NumberFormatException ex){pl.getLogger().severe("Invalid value: "+parts[1]);} + catch(IllegalArgumentException ex){ + pl.getLogger().severe("Unknown entity type: "+parts[0]); } } } + static long timeSinceLastPlayerDamage(Entity entity){ + long lastDamage = entity.hasMetadata("PlayerDamage") ? entity.getMetadata("PlayerDamage").get(0).asLong() : 0; + return System.currentTimeMillis() - lastDamage; + } + static double getSpawnCauseModifier(Entity e){ + return e.hasMetadata("SpawnReason") ? e.getMetadata("SpawnReason").get(0).asDouble() : 1D; + } + @EventHandler public void entityDeathEvent(EntityDeathEvent evt){ - double rawDropChance, dropChance; - double lootBonus = 0D, toolBonus = 0D; - double spawnCauseModifier = evt.getEntity().hasMetadata("SpawnReason") ? - evt.getEntity().getMetadata("SpawnReason").get(0).asDouble() : 1D; - - if(playerHeadsOnly && evt.getEntity() instanceof Player == false) return; - + LivingEntity victim = evt.getEntity(); + if(playerHeadsOnly && victim instanceof Player == false) return; Entity killer = null; - EntityDamageEvent lastDamage = evt.getEntity().getLastDamageCause(); + boolean killedByChargedCreeper = false, alwaysBeheadPerm = false; + double lootingBonus = 0D, weaponBonus = 0D; + EntityDamageEvent lastDamage = victim.getLastDamageCause(); if(lastDamage != null && lastDamage instanceof EntityDamageByEntityEvent){ killer = ((EntityDamageByEntityEvent)lastDamage).getDamager(); - - if(chargedCreepers && killer instanceof Creeper && ((Creeper)killer).isPowered()) dropChance = 1D; - else if(playerKillsOnly && killer instanceof Player == false) return; - else if(!killer.hasPermission("evp.dropheads.canbehead")) return; - else{ - ItemStack heldItem = null; - if(killer instanceof LivingEntity){ - heldItem = ((LivingEntity)killer).getEquipment().getItemInMainHand(); - if(heldItem != null){ - lootBonus = heldItem.getEnchantmentLevel(Enchantment.LOOT_BONUS_MOBS)*lootingBonus; - if(toolBonuses.containsKey(heldItem.getType())) toolBonus = toolBonuses.get(heldItem.getType()); - } + if(killer instanceof Creeper && ((Creeper)killer).isPowered()) killedByChargedCreeper = true; + //"else if" - Overrides "player-kills-only" - intentional design + else if(playerKillsOnly && killer instanceof Player == false){ + if(allowProjectileKills && killer instanceof Projectile + && ((Projectile)killer).getShooter() instanceof Player); + else if(allowIndirectKills && timeSinceLastPlayerDamage(victim) < 60*1000); + else return; + } + if(!killer.hasPermission("dropheads.canbehead")) return; + alwaysBeheadPerm = killer.hasPermission("dropheads.alwaysbehead"); + + ItemStack itemInHand = null; + if(killer instanceof LivingEntity){ + itemInHand = ((LivingEntity)killer).getEquipment().getItemInMainHand(); + if(itemInHand != null && !noLootingEffectMobs.contains(victim.getType())){ + lootingBonus = itemInHand.getEnchantmentLevel(Enchantment.LOOT_BONUS_MOBS)*0.01D; + weaponBonus = toolBonuses.getOrDefault(itemInHand.getType(), 0D); } - if(!mustUseTools.isEmpty() && (heldItem == null || !mustUseTools.contains(heldItem.getType()))) return; - - if(killer.hasPermission("evp.dropheads.alwaysbehead")) dropChance = spawnCauseModifier = 1D; - else dropChance = mobChances.containsKey(evt.getEntityType()) ? mobChances.get(evt.getEntityType()) : 0D; } + if(!mustUseTools.isEmpty() && (itemInHand == null || !mustUseTools.contains(itemInHand.getType()))) return; } - else if(playerKillsOnly) return; - else dropChance = mobChances.containsKey(evt.getEntityType()) - ? mobChances.get(evt.getEntityType()) : DEFAULT_CHANCE; - rawDropChance = (dropChance *= spawnCauseModifier); + double rawDropChance = mobChances.getOrDefault(victim.getType(), DEFAULT_CHANCE); + double spawnCauseMod = getSpawnCauseModifier(victim); + double dropChanceWithSpawnMod = + ((CHARGED_CREEPER_DROPS && killedByChargedCreeper) || alwaysBeheadPerm) ? 1D : + Math.min(rawDropChance*spawnCauseMod, 1D); + double dropChance = dropChanceWithSpawnMod; if(useTaylorModifiers){ - toolBonus = Math.pow(2D, toolBonus*dropChance)-1D; - lootBonus = Math.pow(2D, lootBonus*dropChance)-1D; - dropChance += (lootBonus == 0D ? 0D : (lootBonus*(1D-dropChance))/(lootBonus+1D));//apply modifiers - dropChance += (toolBonus == 0D ? 0D : (toolBonus*(1D-dropChance))/(toolBonus+1D)); + lootingBonus = Math.pow(2D, 40*lootingBonus*dropChance)-1D; + if(lootingBonus > 0D) dropChance += (lootingBonus*(1D-dropChance))/(lootingBonus+1D);//apply modifiers } - else dropChance += lootBonus*dropChance + toolBonus*dropChance; + else dropChance += lootingBonus*dropChance; + dropChance += weaponBonus*dropChance; + dropChance = Math.min(dropChance, 1D); - if(rand.nextDouble() < dropChance){ - evt.getEntity().getWorld().dropItem(evt.getEntity().getLocation(), pl.getAPI().getHead(evt.getEntity())); + //Remove vanilla head drops (TODO: test this) + Iterator it = evt.getDrops().iterator(); + while(it.hasNext()) if(EvUtils.isHead(it.next().getType())) it.remove(); - pl.getLogger().info("Head dropped!\nDrop chance before tool modifiers: "+rawDropChance+ - "\nDrop chance after tool modifiers: "+dropChance+ - "\nMob killed: "+evt.getEntityType().name()); + if(rand.nextDouble() < dropChance){ + victim.getWorld().dropItem(victim.getLocation(), pl.getAPI().getHead(victim)); + if(DEBUG_MODE){ + pl.getLogger().info("Dropped Head: "+victim.getType().name() + +"\nRaw chance: "+rawDropChance + +", SpawnReason Modifier: "+spawnCauseMod + +", Looting Bonus: "+lootingBonus+", Weapon Bonus: "+weaponBonus + +", Charged Creeper: "+killedByChargedCreeper+", Always-Behead Perm: "+alwaysBeheadPerm + +", Final drop chance: "+dropChance); + } } } } \ No newline at end of file diff --git a/src/net/evmodder/DropHeads/listeners/EntitySpawnListener.java b/src/net/evmodder/DropHeads/listeners/EntitySpawnListener.java index e766927..06e4a2c 100644 --- a/src/net/evmodder/DropHeads/listeners/EntitySpawnListener.java +++ b/src/net/evmodder/DropHeads/listeners/EntitySpawnListener.java @@ -46,4 +46,4 @@ public void entitySpawnEvent(CreatureSpawnEvent evt){ } } } -} +} \ No newline at end of file diff --git a/src/net/evmodder/DropHeads/listeners/ItemDropListener.java b/src/net/evmodder/DropHeads/listeners/ItemDropListener.java index e894aaa..294d5c7 100644 --- a/src/net/evmodder/DropHeads/listeners/ItemDropListener.java +++ b/src/net/evmodder/DropHeads/listeners/ItemDropListener.java @@ -3,6 +3,8 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.entity.ItemSpawnEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.SkullMeta; import com.mojang.authlib.GameProfile; import net.evmodder.DropHeads.DropHeads; @@ -10,15 +12,27 @@ import net.evmodder.EvLib.EvUtils; public class ItemDropListener implements Listener{ + final boolean FORCE_RENAME; + public ItemDropListener(){ + FORCE_RENAME = DropHeads.getPlugin().getConfig().getBoolean("refresh-item-names", false); + } + @EventHandler public void onBarf(ItemSpawnEvent evt){ if(evt.isCancelled() || !EvUtils.isPlayerHead(evt.getEntity().getItemStack().getType()) || !evt.getEntity().getItemStack().hasItemMeta()) return; SkullMeta meta = (SkullMeta) evt.getEntity().getItemStack().getItemMeta(); + String name = !FORCE_RENAME && meta.hasDisplayName() ? meta.getDisplayName() : null; GameProfile profile = HeadUtils.getGameProfile(meta); if(profile != null){ - evt.getEntity().setItemStack(DropHeads.getPlugin().getAPI().getHead(profile)); + ItemStack refreshedItem = DropHeads.getPlugin().getAPI().getHead(profile); + if(name != null){ + ItemMeta newMeta = refreshedItem.getItemMeta(); + newMeta.setDisplayName(name); + refreshedItem.setItemMeta(newMeta); + } + evt.getEntity().setItemStack(refreshedItem); } } } \ No newline at end of file