package net.minecraft.server; import java.util.Iterator; import java.util.Locale; import java.util.Random; import javax.annotation.Nullable; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; // CraftBukkit start import org.bukkit.Bukkit; import org.bukkit.craftbukkit.entity.CraftVillager; import org.bukkit.craftbukkit.event.CraftEventFactory; import org.bukkit.craftbukkit.inventory.CraftMerchantRecipe; import org.bukkit.entity.Villager; import org.bukkit.event.entity.EntityTransformEvent; import org.bukkit.event.entity.VillagerAcquireTradeEvent; import org.bukkit.event.entity.VillagerReplenishTradeEvent; // CraftBukkit end public class EntityVillager extends EntityAgeable implements NPC, IMerchant { private static final Logger bC = LogManager.getLogger(); private static final DataWatcherObject bD = DataWatcher.a(EntityVillager.class, DataWatcherRegistry.b); private int profession; private boolean bF; private boolean bG; private Village village; @Nullable private EntityHuman tradingPlayer; @Nullable public MerchantRecipeList trades; private int bK; private boolean bL; private boolean bM; public int riches; private String bO; public int careerId; public int careerLevel; private boolean bR; private boolean bS; public final InventorySubcontainer inventory; private static final EntityVillager.IMerchantRecipeOption[][][][] bU = new EntityVillager.IMerchantRecipeOption[][][][] { { { { new EntityVillager.MerchantRecipeOptionBuy(Items.WHEAT, new EntityVillager.MerchantOptionRandomRange(18, 22)), new EntityVillager.MerchantRecipeOptionBuy(Items.POTATO, new EntityVillager.MerchantOptionRandomRange(15, 19)), new EntityVillager.MerchantRecipeOptionBuy(Items.CARROT, new EntityVillager.MerchantOptionRandomRange(15, 19)), new EntityVillager.MerchantRecipeOptionSell(Items.BREAD, new EntityVillager.MerchantOptionRandomRange(-4, -2))}, { new EntityVillager.MerchantRecipeOptionBuy(Blocks.PUMPKIN, new EntityVillager.MerchantOptionRandomRange(8, 13)), new EntityVillager.MerchantRecipeOptionSell(Items.PUMPKIN_PIE, new EntityVillager.MerchantOptionRandomRange(-3, -2))}, { new EntityVillager.MerchantRecipeOptionBuy(Blocks.MELON, new EntityVillager.MerchantOptionRandomRange(7, 12)), new EntityVillager.MerchantRecipeOptionSell(Items.APPLE, new EntityVillager.MerchantOptionRandomRange(-7, -5))}, { new EntityVillager.MerchantRecipeOptionSell(Items.COOKIE, new EntityVillager.MerchantOptionRandomRange(-10, -6)), new EntityVillager.MerchantRecipeOptionSell(Blocks.CAKE, new EntityVillager.MerchantOptionRandomRange(1, 1))}}, { { new EntityVillager.MerchantRecipeOptionBuy(Items.STRING, new EntityVillager.MerchantOptionRandomRange(15, 20)), new EntityVillager.MerchantRecipeOptionBuy(Items.COAL, new EntityVillager.MerchantOptionRandomRange(16, 24)), new EntityVillager.MerchantRecipeOptionProcess(Items.COD, new EntityVillager.MerchantOptionRandomRange(6, 6), Items.COOKED_COD, new EntityVillager.MerchantOptionRandomRange(6, 6)), new EntityVillager.MerchantRecipeOptionProcess(Items.SALMON, new EntityVillager.MerchantOptionRandomRange(6, 6), Items.COOKED_SALMON, new EntityVillager.MerchantOptionRandomRange(6, 6))}, { new EntityVillager.MerchantRecipeOptionEnchant(Items.FISHING_ROD, new EntityVillager.MerchantOptionRandomRange(7, 8))}}, { { new EntityVillager.MerchantRecipeOptionBuy(Blocks.WHITE_WOOL, new EntityVillager.MerchantOptionRandomRange(16, 22)), new EntityVillager.MerchantRecipeOptionSell(Items.SHEARS, new EntityVillager.MerchantOptionRandomRange(3, 4))}, { new EntityVillager.MerchantRecipeOptionSell(new ItemStack(Blocks.WHITE_WOOL), new EntityVillager.MerchantOptionRandomRange(1, 2)), new EntityVillager.MerchantRecipeOptionSell(new ItemStack(Blocks.ORANGE_WOOL), new EntityVillager.MerchantOptionRandomRange(1, 2)), new EntityVillager.MerchantRecipeOptionSell(new ItemStack(Blocks.MAGENTA_WOOL), new EntityVillager.MerchantOptionRandomRange(1, 2)), new EntityVillager.MerchantRecipeOptionSell(new ItemStack(Blocks.LIGHT_BLUE_WOOL), new EntityVillager.MerchantOptionRandomRange(1, 2)), new EntityVillager.MerchantRecipeOptionSell(new ItemStack(Blocks.YELLOW_WOOL), new EntityVillager.MerchantOptionRandomRange(1, 2)), new EntityVillager.MerchantRecipeOptionSell(new ItemStack(Blocks.LIME_WOOL), new EntityVillager.MerchantOptionRandomRange(1, 2)), new EntityVillager.MerchantRecipeOptionSell(new ItemStack(Blocks.PINK_WOOL), new EntityVillager.MerchantOptionRandomRange(1, 2)), new EntityVillager.MerchantRecipeOptionSell(new ItemStack(Blocks.GRAY_WOOL), new EntityVillager.MerchantOptionRandomRange(1, 2)), new EntityVillager.MerchantRecipeOptionSell(new ItemStack(Blocks.LIGHT_GRAY_WOOL), new EntityVillager.MerchantOptionRandomRange(1, 2)), new EntityVillager.MerchantRecipeOptionSell(new ItemStack(Blocks.CYAN_WOOL), new EntityVillager.MerchantOptionRandomRange(1, 2)), new EntityVillager.MerchantRecipeOptionSell(new ItemStack(Blocks.PURPLE_WOOL), new EntityVillager.MerchantOptionRandomRange(1, 2)), new EntityVillager.MerchantRecipeOptionSell(new ItemStack(Blocks.BLUE_WOOL), new EntityVillager.MerchantOptionRandomRange(1, 2)), new EntityVillager.MerchantRecipeOptionSell(new ItemStack(Blocks.BROWN_WOOL), new EntityVillager.MerchantOptionRandomRange(1, 2)), new EntityVillager.MerchantRecipeOptionSell(new ItemStack(Blocks.GREEN_WOOL), new EntityVillager.MerchantOptionRandomRange(1, 2)), new EntityVillager.MerchantRecipeOptionSell(new ItemStack(Blocks.RED_WOOL), new EntityVillager.MerchantOptionRandomRange(1, 2)), new EntityVillager.MerchantRecipeOptionSell(new ItemStack(Blocks.BLACK_WOOL), new EntityVillager.MerchantOptionRandomRange(1, 2))}}, { { new EntityVillager.MerchantRecipeOptionBuy(Items.STRING, new EntityVillager.MerchantOptionRandomRange(15, 20)), new EntityVillager.MerchantRecipeOptionSell(Items.ARROW, new EntityVillager.MerchantOptionRandomRange(-12, -8))}, { new EntityVillager.MerchantRecipeOptionSell(Items.BOW, new EntityVillager.MerchantOptionRandomRange(2, 3)), new EntityVillager.MerchantRecipeOptionProcess(Blocks.GRAVEL, new EntityVillager.MerchantOptionRandomRange(10, 10), Items.FLINT, new EntityVillager.MerchantOptionRandomRange(6, 10))}}}, { { { new EntityVillager.MerchantRecipeOptionBuy(Items.PAPER, new EntityVillager.MerchantOptionRandomRange(24, 36)), new EntityVillager.MerchantRecipeOptionBook()}, { new EntityVillager.MerchantRecipeOptionBuy(Items.BOOK, new EntityVillager.MerchantOptionRandomRange(8, 10)), new EntityVillager.MerchantRecipeOptionSell(Items.COMPASS, new EntityVillager.MerchantOptionRandomRange(10, 12)), new EntityVillager.MerchantRecipeOptionSell(Blocks.BOOKSHELF, new EntityVillager.MerchantOptionRandomRange(3, 4))}, { new EntityVillager.MerchantRecipeOptionBuy(Items.WRITTEN_BOOK, new EntityVillager.MerchantOptionRandomRange(2, 2)), new EntityVillager.MerchantRecipeOptionSell(Items.CLOCK, new EntityVillager.MerchantOptionRandomRange(10, 12)), new EntityVillager.MerchantRecipeOptionSell(Blocks.GLASS, new EntityVillager.MerchantOptionRandomRange(-5, -3))}, { new EntityVillager.MerchantRecipeOptionBook()}, { new EntityVillager.MerchantRecipeOptionBook()}, { new EntityVillager.MerchantRecipeOptionSell(Items.NAME_TAG, new EntityVillager.MerchantOptionRandomRange(20, 22))}}, { { new EntityVillager.MerchantRecipeOptionBuy(Items.PAPER, new EntityVillager.MerchantOptionRandomRange(24, 36))}, { new EntityVillager.MerchantRecipeOptionBuy(Items.COMPASS, new EntityVillager.MerchantOptionRandomRange(1, 1))}, { new EntityVillager.MerchantRecipeOptionSell(Items.MAP, new EntityVillager.MerchantOptionRandomRange(7, 11))}, { new EntityVillager.h(new EntityVillager.MerchantOptionRandomRange(12, 20), "Monument", MapIcon.Type.MONUMENT), new EntityVillager.h(new EntityVillager.MerchantOptionRandomRange(16, 28), "Mansion", MapIcon.Type.MANSION)}}}, { { { new EntityVillager.MerchantRecipeOptionBuy(Items.ROTTEN_FLESH, new EntityVillager.MerchantOptionRandomRange(36, 40)), new EntityVillager.MerchantRecipeOptionBuy(Items.GOLD_INGOT, new EntityVillager.MerchantOptionRandomRange(8, 10))}, { new EntityVillager.MerchantRecipeOptionSell(Items.REDSTONE, new EntityVillager.MerchantOptionRandomRange(-4, -1)), new EntityVillager.MerchantRecipeOptionSell(new ItemStack(Items.LAPIS_LAZULI), new EntityVillager.MerchantOptionRandomRange(-2, -1))}, { new EntityVillager.MerchantRecipeOptionSell(Items.ENDER_PEARL, new EntityVillager.MerchantOptionRandomRange(4, 7)), new EntityVillager.MerchantRecipeOptionSell(Blocks.GLOWSTONE, new EntityVillager.MerchantOptionRandomRange(-3, -1))}, { new EntityVillager.MerchantRecipeOptionSell(Items.EXPERIENCE_BOTTLE, new EntityVillager.MerchantOptionRandomRange(3, 11))}}}, { { { new EntityVillager.MerchantRecipeOptionBuy(Items.COAL, new EntityVillager.MerchantOptionRandomRange(16, 24)), new EntityVillager.MerchantRecipeOptionSell(Items.IRON_HELMET, new EntityVillager.MerchantOptionRandomRange(4, 6))}, { new EntityVillager.MerchantRecipeOptionBuy(Items.IRON_INGOT, new EntityVillager.MerchantOptionRandomRange(7, 9)), new EntityVillager.MerchantRecipeOptionSell(Items.IRON_CHESTPLATE, new EntityVillager.MerchantOptionRandomRange(10, 14))}, { new EntityVillager.MerchantRecipeOptionBuy(Items.DIAMOND, new EntityVillager.MerchantOptionRandomRange(3, 4)), new EntityVillager.MerchantRecipeOptionEnchant(Items.DIAMOND_CHESTPLATE, new EntityVillager.MerchantOptionRandomRange(16, 19))}, { new EntityVillager.MerchantRecipeOptionSell(Items.CHAINMAIL_BOOTS, new EntityVillager.MerchantOptionRandomRange(5, 7)), new EntityVillager.MerchantRecipeOptionSell(Items.CHAINMAIL_LEGGINGS, new EntityVillager.MerchantOptionRandomRange(9, 11)), new EntityVillager.MerchantRecipeOptionSell(Items.CHAINMAIL_HELMET, new EntityVillager.MerchantOptionRandomRange(5, 7)), new EntityVillager.MerchantRecipeOptionSell(Items.CHAINMAIL_CHESTPLATE, new EntityVillager.MerchantOptionRandomRange(11, 15))}}, { { new EntityVillager.MerchantRecipeOptionBuy(Items.COAL, new EntityVillager.MerchantOptionRandomRange(16, 24)), new EntityVillager.MerchantRecipeOptionSell(Items.IRON_AXE, new EntityVillager.MerchantOptionRandomRange(6, 8))}, { new EntityVillager.MerchantRecipeOptionBuy(Items.IRON_INGOT, new EntityVillager.MerchantOptionRandomRange(7, 9)), new EntityVillager.MerchantRecipeOptionEnchant(Items.IRON_SWORD, new EntityVillager.MerchantOptionRandomRange(9, 10))}, { new EntityVillager.MerchantRecipeOptionBuy(Items.DIAMOND, new EntityVillager.MerchantOptionRandomRange(3, 4)), new EntityVillager.MerchantRecipeOptionEnchant(Items.DIAMOND_SWORD, new EntityVillager.MerchantOptionRandomRange(12, 15)), new EntityVillager.MerchantRecipeOptionEnchant(Items.DIAMOND_AXE, new EntityVillager.MerchantOptionRandomRange(9, 12))}}, { { new EntityVillager.MerchantRecipeOptionBuy(Items.COAL, new EntityVillager.MerchantOptionRandomRange(16, 24)), new EntityVillager.MerchantRecipeOptionEnchant(Items.IRON_SHOVEL, new EntityVillager.MerchantOptionRandomRange(5, 7))}, { new EntityVillager.MerchantRecipeOptionBuy(Items.IRON_INGOT, new EntityVillager.MerchantOptionRandomRange(7, 9)), new EntityVillager.MerchantRecipeOptionEnchant(Items.IRON_PICKAXE, new EntityVillager.MerchantOptionRandomRange(9, 11))}, { new EntityVillager.MerchantRecipeOptionBuy(Items.DIAMOND, new EntityVillager.MerchantOptionRandomRange(3, 4)), new EntityVillager.MerchantRecipeOptionEnchant(Items.DIAMOND_PICKAXE, new EntityVillager.MerchantOptionRandomRange(12, 15))}}}, { { { new EntityVillager.MerchantRecipeOptionBuy(Items.PORKCHOP, new EntityVillager.MerchantOptionRandomRange(14, 18)), new EntityVillager.MerchantRecipeOptionBuy(Items.CHICKEN, new EntityVillager.MerchantOptionRandomRange(14, 18))}, { new EntityVillager.MerchantRecipeOptionBuy(Items.COAL, new EntityVillager.MerchantOptionRandomRange(16, 24)), new EntityVillager.MerchantRecipeOptionSell(Items.COOKED_PORKCHOP, new EntityVillager.MerchantOptionRandomRange(-7, -5)), new EntityVillager.MerchantRecipeOptionSell(Items.COOKED_CHICKEN, new EntityVillager.MerchantOptionRandomRange(-8, -6))}}, { { new EntityVillager.MerchantRecipeOptionBuy(Items.LEATHER, new EntityVillager.MerchantOptionRandomRange(9, 12)), new EntityVillager.MerchantRecipeOptionSell(Items.LEATHER_LEGGINGS, new EntityVillager.MerchantOptionRandomRange(2, 4))}, { new EntityVillager.MerchantRecipeOptionEnchant(Items.LEATHER_CHESTPLATE, new EntityVillager.MerchantOptionRandomRange(7, 12))}, { new EntityVillager.MerchantRecipeOptionSell(Items.SADDLE, new EntityVillager.MerchantOptionRandomRange(8, 10))}}}, { new EntityVillager.IMerchantRecipeOption[0][]}}; public EntityVillager(World world) { this(world, 0); } public EntityVillager(World world, int i) { super(EntityTypes.VILLAGER, world); this.inventory = new InventorySubcontainer(new ChatComponentText("Items"), 8, (CraftVillager) this.getBukkitEntity()); // CraftBukkit add argument this.setProfession(i); this.setSize(0.6F, 1.95F); ((Navigation) this.getNavigation()).a(true); this.p(true); } protected void n() { this.goalSelector.a(0, new PathfinderGoalFloat(this)); this.goalSelector.a(1, new PathfinderGoalAvoidTarget<>(this, EntityZombie.class, 8.0F, 0.6D, 0.6D)); this.goalSelector.a(1, new PathfinderGoalAvoidTarget<>(this, EntityEvoker.class, 12.0F, 0.8D, 0.8D)); this.goalSelector.a(1, new PathfinderGoalAvoidTarget<>(this, EntityVindicator.class, 8.0F, 0.8D, 0.8D)); this.goalSelector.a(1, new PathfinderGoalAvoidTarget<>(this, EntityVex.class, 8.0F, 0.6D, 0.6D)); this.goalSelector.a(1, new PathfinderGoalTradeWithPlayer(this)); this.goalSelector.a(1, new PathfinderGoalLookAtTradingPlayer(this)); this.goalSelector.a(2, new PathfinderGoalMoveIndoors(this)); this.goalSelector.a(3, new PathfinderGoalRestrictOpenDoor(this)); this.goalSelector.a(4, new PathfinderGoalOpenDoor(this, true)); this.goalSelector.a(5, new PathfinderGoalMoveTowardsRestriction(this, 0.6D)); this.goalSelector.a(6, new PathfinderGoalMakeLove(this)); this.goalSelector.a(7, new PathfinderGoalTakeFlower(this)); this.goalSelector.a(9, new PathfinderGoalInteract(this, EntityHuman.class, 3.0F, 1.0F)); this.goalSelector.a(9, new PathfinderGoalInteractVillagers(this)); this.goalSelector.a(9, new PathfinderGoalRandomStrollLand(this, 0.6D)); this.goalSelector.a(10, new PathfinderGoalLookAtPlayer(this, EntityInsentient.class, 8.0F)); } private void dJ() { if (!this.bS) { this.bS = true; if (this.isBaby()) { this.goalSelector.a(8, new PathfinderGoalPlay(this, 0.32D)); } else if (this.getProfession() == 0) { this.goalSelector.a(6, new PathfinderGoalVillagerFarm(this, 0.6D)); } } } protected void l() { if (this.getProfession() == 0) { this.goalSelector.a(8, new PathfinderGoalVillagerFarm(this, 0.6D)); } super.l(); } protected void initAttributes() { super.initAttributes(); this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(0.5D); } // Spigot Start @Override public void inactiveTick() { // SPIGOT-3874 if (world.spigotConfig.tickInactiveVillagers) { // SPIGOT-3894 Chunk startingChunk = this.world.getChunkIfLoaded(MathHelper.floor(this.locX) >> 4, MathHelper.floor(this.locZ) >> 4); if (!(startingChunk != null && startingChunk.areNeighborsLoaded(1))) { return; } this.mobTick(); // SPIGOT-3846 } super.inactiveTick(); } // Spigot End protected void mobTick() { if (--this.profession <= 0) { BlockPosition blockposition = new BlockPosition(this); this.world.af().a(blockposition); this.profession = 70 + this.random.nextInt(50); this.village = this.world.af().getClosestVillage(blockposition, 32); if (this.village == null) { this.dv(); } else { BlockPosition blockposition1 = this.village.a(); this.a(blockposition1, this.village.b()); if (this.bR) { this.bR = false; this.village.b(5); } } } if (!this.dB() && this.bK > 0) { --this.bK; if (this.bK <= 0) { if (this.bL) { Iterator iterator = this.trades.iterator(); while (iterator.hasNext()) { MerchantRecipe merchantrecipe = (MerchantRecipe) iterator.next(); if (merchantrecipe.h()) { // CraftBukkit start int bonus = this.random.nextInt(6) + this.random.nextInt(6) + 2; VillagerReplenishTradeEvent event = new VillagerReplenishTradeEvent((Villager) this.getBukkitEntity(), merchantrecipe.asBukkit(), bonus); Bukkit.getPluginManager().callEvent(event); if (!event.isCancelled()) { merchantrecipe.a(event.getBonus()); } // CraftBukkit end } } this.populateTrades(); this.bL = false; if (this.village != null && this.bO != null) { this.world.broadcastEntityEffect(this, (byte) 14); this.village.a(this.bO, 1); } } this.addEffect(new MobEffect(MobEffects.REGENERATION, 200, 0), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.VILLAGER_TRADE); // CraftBukkit } } super.mobTick(); } public boolean a(EntityHuman entityhuman, EnumHand enumhand) { ItemStack itemstack = entityhuman.b(enumhand); boolean flag = itemstack.getItem() == Items.NAME_TAG; if (flag) { itemstack.a(entityhuman, (EntityLiving) this, enumhand); return true; } else if (itemstack.getItem() != Items.VILLAGER_SPAWN_EGG && this.isAlive() && !this.dB() && !this.isBaby()) { if (this.trades == null) { this.populateTrades(); } if (enumhand == EnumHand.MAIN_HAND) { entityhuman.a(StatisticList.TALKED_TO_VILLAGER); } if (!this.world.isClientSide && !this.trades.isEmpty()) { this.setTradingPlayer(entityhuman); entityhuman.openTrade(this); } else if (this.trades.isEmpty()) { return super.a(entityhuman, enumhand); } return true; } else { return super.a(entityhuman, enumhand); } } protected void x_() { super.x_(); this.datawatcher.register(EntityVillager.bD, 0); } public void b(NBTTagCompound nbttagcompound) { super.b(nbttagcompound); nbttagcompound.setInt("Profession", this.getProfession()); nbttagcompound.setInt("Riches", this.riches); nbttagcompound.setInt("Career", this.careerId); nbttagcompound.setInt("CareerLevel", this.careerLevel); nbttagcompound.setBoolean("Willing", this.bM); if (this.trades != null) { nbttagcompound.set("Offers", this.trades.a()); } NBTTagList nbttaglist = new NBTTagList(); for (int i = 0; i < this.inventory.getSize(); ++i) { ItemStack itemstack = this.inventory.getItem(i); if (!itemstack.isEmpty()) { nbttaglist.add((NBTBase) itemstack.save(new NBTTagCompound())); } } nbttagcompound.set("Inventory", nbttaglist); } public void a(NBTTagCompound nbttagcompound) { super.a(nbttagcompound); this.setProfession(nbttagcompound.getInt("Profession")); this.riches = nbttagcompound.getInt("Riches"); this.careerId = nbttagcompound.getInt("Career"); this.careerLevel = nbttagcompound.getInt("CareerLevel"); this.bM = nbttagcompound.getBoolean("Willing"); if (nbttagcompound.hasKeyOfType("Offers", 10)) { NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("Offers"); this.trades = new MerchantRecipeList(nbttagcompound1); } NBTTagList nbttaglist = nbttagcompound.getList("Inventory", 10); for (int i = 0; i < nbttaglist.size(); ++i) { ItemStack itemstack = ItemStack.a(nbttaglist.getCompound(i)); if (!itemstack.isEmpty()) { this.inventory.a(itemstack); } } this.p(true); this.dJ(); } public boolean isTypeNotPersistent() { return false; } protected SoundEffect D() { return this.dB() ? SoundEffects.ENTITY_VILLAGER_TRADE : SoundEffects.ENTITY_VILLAGER_AMBIENT; } protected SoundEffect d(DamageSource damagesource) { return SoundEffects.ENTITY_VILLAGER_HURT; } protected SoundEffect cs() { return SoundEffects.ENTITY_VILLAGER_DEATH; } @Nullable protected MinecraftKey getDefaultLootTable() { return LootTables.aA; } public void setProfession(int i) { this.datawatcher.set(EntityVillager.bD, i); } public int getProfession() { return Math.max((Integer) this.datawatcher.get(EntityVillager.bD) % 6, 0); } public boolean isInLove() { return this.bF; } public void s(boolean flag) { this.bF = flag; } public void t(boolean flag) { this.bG = flag; } public boolean dA() { return this.bG; } public void setLastDamager(@Nullable EntityLiving entityliving) { super.setLastDamager(entityliving); if (this.village != null && entityliving != null) { this.village.a(entityliving); if (entityliving instanceof EntityHuman) { byte b0 = -1; if (this.isBaby()) { b0 = -3; } this.village.a(((EntityHuman) entityliving).getProfile().getName(), b0); if (this.isAlive()) { this.world.broadcastEntityEffect(this, (byte) 13); } } } } public void die(DamageSource damagesource) { if (this.village != null) { Entity entity = damagesource.getEntity(); if (entity != null) { if (entity instanceof EntityHuman) { this.village.a(((EntityHuman) entity).getProfile().getName(), -2); } else if (entity instanceof IMonster) { this.village.h(); } } else { EntityHuman entityhuman = this.world.findNearbyPlayer(this, 16.0D); if (entityhuman != null) { this.village.h(); } } } super.die(damagesource); } public void setTradingPlayer(@Nullable EntityHuman entityhuman) { this.tradingPlayer = entityhuman; } @Nullable public EntityHuman getTrader() { return this.tradingPlayer; } public boolean dB() { return this.tradingPlayer != null; } public boolean u(boolean flag) { if (!this.bM && flag && this.dE()) { boolean flag1 = false; for (int i = 0; i < this.inventory.getSize(); ++i) { ItemStack itemstack = this.inventory.getItem(i); if (!itemstack.isEmpty()) { if (itemstack.getItem() == Items.BREAD && itemstack.getCount() >= 3) { flag1 = true; this.inventory.splitStack(i, 3); } else if ((itemstack.getItem() == Items.POTATO || itemstack.getItem() == Items.CARROT) && itemstack.getCount() >= 12) { flag1 = true; this.inventory.splitStack(i, 12); } } if (flag1) { this.world.broadcastEntityEffect(this, (byte) 18); this.bM = true; break; } } } return this.bM; } public void v(boolean flag) { this.bM = flag; } public void a(MerchantRecipe merchantrecipe) { merchantrecipe.increaseUses(); this.a_ = -this.z(); this.a(SoundEffects.ENTITY_VILLAGER_YES, this.cD(), this.cE()); int i = 3 + this.random.nextInt(4); if (merchantrecipe.e() == 1 || this.random.nextInt(5) == 0) { this.bK = 40; this.bL = true; this.bM = true; if (this.tradingPlayer != null) { this.bO = this.tradingPlayer.getProfile().getName(); } else { this.bO = null; } i += 5; } if (merchantrecipe.getBuyItem1().getItem() == Items.EMERALD) { this.riches += merchantrecipe.getBuyItem1().getCount(); } if (merchantrecipe.j()) { this.world.addEntity(new EntityExperienceOrb(this.world, this.locX, this.locY + 0.5D, this.locZ, i, org.bukkit.entity.ExperienceOrb.SpawnReason.VILLAGER_TRADE, tradingPlayer, this)); // Paper } if (this.tradingPlayer instanceof EntityPlayer) { CriterionTriggers.s.a((EntityPlayer) this.tradingPlayer, this, merchantrecipe.getBuyItem3()); } } public void a(ItemStack itemstack) { if (!this.world.isClientSide && this.a_ > -this.z() + 20) { this.a_ = -this.z(); this.a(itemstack.isEmpty() ? SoundEffects.ENTITY_VILLAGER_NO : SoundEffects.ENTITY_VILLAGER_YES, this.cD(), this.cE()); } } @Nullable public MerchantRecipeList getOffers(EntityHuman entityhuman) { if (this.trades == null) { this.populateTrades(); } return this.trades; } public void populateTrades() { EntityVillager.IMerchantRecipeOption[][][] aentityvillager_imerchantrecipeoption = EntityVillager.bU[this.getProfession()]; if (this.careerId != 0 && this.careerLevel != 0) { ++this.careerLevel; } else { this.careerId = this.random.nextInt(aentityvillager_imerchantrecipeoption.length) + 1; this.careerLevel = 1; } if (this.trades == null) { this.trades = new MerchantRecipeList(); } int i = this.careerId - 1; int j = this.careerLevel - 1; if (i >= 0 && i < aentityvillager_imerchantrecipeoption.length) { EntityVillager.IMerchantRecipeOption[][] aentityvillager_imerchantrecipeoption1 = aentityvillager_imerchantrecipeoption[i]; if (j >= 0 && j < aentityvillager_imerchantrecipeoption1.length) { EntityVillager.IMerchantRecipeOption[] aentityvillager_imerchantrecipeoption2 = aentityvillager_imerchantrecipeoption1[j]; EntityVillager.IMerchantRecipeOption[] aentityvillager_imerchantrecipeoption3 = aentityvillager_imerchantrecipeoption2; int k = aentityvillager_imerchantrecipeoption2.length; for (int l = 0; l < k; ++l) { EntityVillager.IMerchantRecipeOption entityvillager_imerchantrecipeoption = aentityvillager_imerchantrecipeoption3[l]; // CraftBukkit start // this is a hack. this must be done because otherwise, if // mojang adds a new type of villager merchant option, it will need to // have event handling added manually. this is better than having to do that. MerchantRecipeList list = new MerchantRecipeList(); entityvillager_imerchantrecipeoption.a(this, list, this.random); for (MerchantRecipe recipe : list) { VillagerAcquireTradeEvent event = new VillagerAcquireTradeEvent((Villager) getBukkitEntity(), recipe.asBukkit()); Bukkit.getPluginManager().callEvent(event); if (!event.isCancelled()) { this.trades.add(CraftMerchantRecipe.fromBukkit(event.getRecipe()).toMinecraft()); } } // CraftBukkit end } } } } public World getWorld() { return this.world; } public BlockPosition getPosition() { return new BlockPosition(this); } public IChatBaseComponent getScoreboardDisplayName() { ScoreboardTeamBase scoreboardteambase = this.getScoreboardTeam(); IChatBaseComponent ichatbasecomponent = this.getCustomName(); if (ichatbasecomponent != null) { return ScoreboardTeam.a(scoreboardteambase, ichatbasecomponent).a((chatmodifier) -> { chatmodifier.setChatHoverable(this.bC()).setInsertion(this.bu()); }); } else { if (this.trades == null) { this.populateTrades(); } String s = null; switch (this.getProfession()) { case 0: if (this.careerId == 1) { s = "farmer"; } else if (this.careerId == 2) { s = "fisherman"; } else if (this.careerId == 3) { s = "shepherd"; } else if (this.careerId == 4) { s = "fletcher"; } break; case 1: if (this.careerId == 1) { s = "librarian"; } else if (this.careerId == 2) { s = "cartographer"; } break; case 2: s = "cleric"; break; case 3: if (this.careerId == 1) { s = "armorer"; } else if (this.careerId == 2) { s = "weapon_smith"; } else if (this.careerId == 3) { s = "tool_smith"; } break; case 4: if (this.careerId == 1) { s = "butcher"; } else if (this.careerId == 2) { s = "leatherworker"; } break; case 5: s = "nitwit"; } if (s != null) { IChatBaseComponent ichatbasecomponent1 = (new ChatMessage(this.P().d() + '.' + s, new Object[0])).a((chatmodifier) -> { chatmodifier.setChatHoverable(this.bC()).setInsertion(this.bu()); }); if (scoreboardteambase != null) { ichatbasecomponent1.a(scoreboardteambase.getColor()); } return ichatbasecomponent1; } else { return super.getScoreboardDisplayName(); } } } public float getHeadHeight() { return this.isBaby() ? 0.81F : 1.62F; } @Nullable public GroupDataEntity prepare(DifficultyDamageScaler difficultydamagescaler, @Nullable GroupDataEntity groupdataentity, @Nullable NBTTagCompound nbttagcompound) { return this.a(difficultydamagescaler, groupdataentity, nbttagcompound, true); } public GroupDataEntity a(DifficultyDamageScaler difficultydamagescaler, @Nullable GroupDataEntity groupdataentity, @Nullable NBTTagCompound nbttagcompound, boolean flag) { groupdataentity = super.prepare(difficultydamagescaler, groupdataentity, nbttagcompound); if (flag) { this.setProfession(this.world.random.nextInt(6)); } this.dJ(); this.populateTrades(); return groupdataentity; } public void dC() { this.bR = true; } public EntityVillager createChild(EntityAgeable entityageable) { EntityVillager entityvillager = EntityTypes.VILLAGER.create(world); // Paper entityvillager.prepare(this.world.getDamageScaler(new BlockPosition(entityvillager)), (GroupDataEntity) null, (NBTTagCompound) null); return entityvillager; } public boolean a(EntityHuman entityhuman) { return false; } public void onLightningStrike(EntityLightning entitylightning) { if (!this.world.isClientSide && !this.dead) { EntityWitch entitywitch = EntityTypes.WITCH.create(world); // Paper // Paper start if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityZapEvent(this, entitylightning, entitywitch).isCancelled()) { return; } // Paper end entitywitch.setPositionRotation(this.locX, this.locY, this.locZ, this.yaw, this.pitch); entitywitch.prepare(this.world.getDamageScaler(new BlockPosition(entitywitch)), (GroupDataEntity) null, (NBTTagCompound) null); entitywitch.setNoAI(this.isNoAI()); if (this.hasCustomName()) { entitywitch.setCustomName(this.getCustomName()); entitywitch.setCustomNameVisible(this.getCustomNameVisible()); } // CraftBukkit start if (CraftEventFactory.callEntityTransformEvent(this, entitywitch, EntityTransformEvent.TransformReason.LIGHTNING).isCancelled()) { return; } if (!new com.destroystokyo.paper.event.entity.EntityTransformedEvent(this.getBukkitEntity(), entitywitch.getBukkitEntity(), com.destroystokyo.paper.event.entity.EntityTransformedEvent.TransformedReason.LIGHTNING).callEvent()) return; // Paper this.world.addEntity(entitywitch, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.LIGHTNING); // CraftBukkit end this.die(); } } public InventorySubcontainer dD() { return this.inventory; } protected void a(EntityItem entityitem) { ItemStack itemstack = entityitem.getItemStack(); Item item = itemstack.getItem(); if (this.a(item)) { ItemStack itemstack1 = this.inventory.a(itemstack); if (itemstack1.isEmpty()) { entityitem.die(); } else { itemstack.setCount(itemstack1.getCount()); } } } private boolean a(Item item) { return item == Items.BREAD || item == Items.POTATO || item == Items.CARROT || item == Items.WHEAT || item == Items.WHEAT_SEEDS || item == Items.BEETROOT || item == Items.BEETROOT_SEEDS; } public boolean dE() { return this.p(1); } public boolean dF() { return this.p(2); } public boolean dG() { boolean flag = this.getProfession() == 0; return flag ? !this.p(5) : !this.p(1); } private boolean p(int i) { boolean flag = this.getProfession() == 0; for (int j = 0; j < this.inventory.getSize(); ++j) { ItemStack itemstack = this.inventory.getItem(j); Item item = itemstack.getItem(); int k = itemstack.getCount(); if (item == Items.BREAD && k >= 3 * i || item == Items.POTATO && k >= 12 * i || item == Items.CARROT && k >= 12 * i || item == Items.BEETROOT && k >= 12 * i) { return true; } if (flag && item == Items.WHEAT && k >= 9 * i) { return true; } } return false; } public boolean dH() { for (int i = 0; i < this.inventory.getSize(); ++i) { Item item = this.inventory.getItem(i).getItem(); if (item == Items.WHEAT_SEEDS || item == Items.POTATO || item == Items.CARROT || item == Items.BEETROOT_SEEDS) { return true; } } return false; } public boolean c(int i, ItemStack itemstack) { if (super.c(i, itemstack)) { return true; } else { int j = i - 300; if (j >= 0 && j < this.inventory.getSize()) { this.inventory.setItem(j, itemstack); return true; } else { return false; } } } static class MerchantRecipeOptionProcess implements EntityVillager.IMerchantRecipeOption { public ItemStack a; public EntityVillager.MerchantOptionRandomRange b; public ItemStack c; public EntityVillager.MerchantOptionRandomRange d; public MerchantRecipeOptionProcess(IMaterial imaterial, EntityVillager.MerchantOptionRandomRange entityvillager_merchantoptionrandomrange, Item item, EntityVillager.MerchantOptionRandomRange entityvillager_merchantoptionrandomrange1) { this.a = new ItemStack(imaterial); this.b = entityvillager_merchantoptionrandomrange; this.c = new ItemStack(item); this.d = entityvillager_merchantoptionrandomrange1; } public void a(IMerchant imerchant, MerchantRecipeList merchantrecipelist, Random random) { int i = this.b.a(random); int j = this.d.a(random); merchantrecipelist.add(new MerchantRecipe(new ItemStack(this.a.getItem(), i), new ItemStack(Items.EMERALD), new ItemStack(this.c.getItem(), j))); } } static class h implements EntityVillager.IMerchantRecipeOption { public EntityVillager.MerchantOptionRandomRange a; public String b; public MapIcon.Type c; public h(EntityVillager.MerchantOptionRandomRange entityvillager_merchantoptionrandomrange, String s, MapIcon.Type mapicon_type) { this.a = entityvillager_merchantoptionrandomrange; this.b = s; this.c = mapicon_type; } public void a(IMerchant imerchant, MerchantRecipeList merchantrecipelist, Random random) { int i = this.a.a(random); World world = imerchant.getWorld(); if (!world.paperConfig.enableTreasureMaps) return; //Paper BlockPosition blockposition = world.a(this.b, imerchant.getPosition(), 100, true); if (blockposition != null) { ItemStack itemstack = ItemWorldMap.createFilledMapView(world, blockposition.getX(), blockposition.getZ(), (byte) 2, true, true); ItemWorldMap.applySepiaFilter(world, itemstack); WorldMap.decorateMap(itemstack, blockposition, "+", this.c); itemstack.a((IChatBaseComponent) (new ChatMessage("filled_map." + this.b.toLowerCase(Locale.ROOT), new Object[0]))); merchantrecipelist.add(new MerchantRecipe(new ItemStack(Items.EMERALD, i), new ItemStack(Items.COMPASS), itemstack)); } } } static class MerchantRecipeOptionBook implements EntityVillager.IMerchantRecipeOption { public MerchantRecipeOptionBook() {} public void a(IMerchant imerchant, MerchantRecipeList merchantrecipelist, Random random) { Enchantment enchantment = (Enchantment) IRegistry.ENCHANTMENT.a(random); int i = MathHelper.nextInt(random, enchantment.getStartLevel(), enchantment.getMaxLevel()); ItemStack itemstack = ItemEnchantedBook.a(new WeightedRandomEnchant(enchantment, i)); int j = 2 + random.nextInt(5 + i * 10) + 3 * i; if (enchantment.isTreasure()) { j *= 2; } if (j > 64) { j = 64; } merchantrecipelist.add(new MerchantRecipe(new ItemStack(Items.BOOK), new ItemStack(Items.EMERALD, j), itemstack)); } } static class MerchantRecipeOptionEnchant implements EntityVillager.IMerchantRecipeOption { public ItemStack a; public EntityVillager.MerchantOptionRandomRange b; public MerchantRecipeOptionEnchant(Item item, EntityVillager.MerchantOptionRandomRange entityvillager_merchantoptionrandomrange) { this.a = new ItemStack(item); this.b = entityvillager_merchantoptionrandomrange; } public void a(IMerchant imerchant, MerchantRecipeList merchantrecipelist, Random random) { int i = 1; if (this.b != null) { i = this.b.a(random); } ItemStack itemstack = new ItemStack(Items.EMERALD, i); ItemStack itemstack1 = EnchantmentManager.a(random, new ItemStack(this.a.getItem()), 5 + random.nextInt(15), false); merchantrecipelist.add(new MerchantRecipe(itemstack, itemstack1)); } } static class MerchantRecipeOptionSell implements EntityVillager.IMerchantRecipeOption { public ItemStack a; public EntityVillager.MerchantOptionRandomRange b; public MerchantRecipeOptionSell(Block block, EntityVillager.MerchantOptionRandomRange entityvillager_merchantoptionrandomrange) { this(new ItemStack(block), entityvillager_merchantoptionrandomrange); } public MerchantRecipeOptionSell(Item item, EntityVillager.MerchantOptionRandomRange entityvillager_merchantoptionrandomrange) { this(new ItemStack(item), entityvillager_merchantoptionrandomrange); } public MerchantRecipeOptionSell(ItemStack itemstack, EntityVillager.MerchantOptionRandomRange entityvillager_merchantoptionrandomrange) { this.a = itemstack; this.b = entityvillager_merchantoptionrandomrange; } public void a(IMerchant imerchant, MerchantRecipeList merchantrecipelist, Random random) { int i = 1; if (this.b != null) { i = this.b.a(random); } ItemStack itemstack; ItemStack itemstack1; if (i < 0) { itemstack = new ItemStack(Items.EMERALD); itemstack1 = new ItemStack(this.a.getItem(), -i); } else { itemstack = new ItemStack(Items.EMERALD, i); itemstack1 = new ItemStack(this.a.getItem()); } merchantrecipelist.add(new MerchantRecipe(itemstack, itemstack1)); } } static class MerchantRecipeOptionBuy implements EntityVillager.IMerchantRecipeOption { public Item a; public EntityVillager.MerchantOptionRandomRange b; public MerchantRecipeOptionBuy(IMaterial imaterial, EntityVillager.MerchantOptionRandomRange entityvillager_merchantoptionrandomrange) { this.a = imaterial.getItem(); this.b = entityvillager_merchantoptionrandomrange; } public void a(IMerchant imerchant, MerchantRecipeList merchantrecipelist, Random random) { ItemStack itemstack = new ItemStack(this.a, this.b == null ? 1 : this.b.a(random)); merchantrecipelist.add(new MerchantRecipe(itemstack, Items.EMERALD)); } } interface IMerchantRecipeOption { void a(IMerchant imerchant, MerchantRecipeList merchantrecipelist, Random random); } static class MerchantOptionRandomRange extends Tuple { public MerchantOptionRandomRange(int i, int j) { super(i, j); if (j < i) { EntityVillager.bC.warn("PriceRange({}, {}) invalid, {} smaller than {}", i, j, j, i); } } public int a(Random random) { return (Integer) this.a() >= (Integer) this.b() ? (Integer) this.a() : (Integer) this.a() + random.nextInt((Integer) this.b() - (Integer) this.a() + 1); } } }