package net.minecraft.world.entity.npc; import net.minecraft.world.entity.ai.sensing.NearestLivingEntitySensor; import com.google.common.collect.ImmutableMap; import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.network.syncher.EntityDataSerializers; import net.minecraft.core.SerializableLong; import net.minecraft.world.entity.ai.gossip.GossipType; import net.minecraft.world.level.LevelReader; import net.minecraft.world.entity.animal.IronGolem; import net.minecraft.world.phys.AABB; import java.util.stream.Collector; import java.util.stream.Collectors; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import net.minecraft.world.level.ItemLike; import net.minecraft.world.SimpleContainer; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.monster.Witch; import net.minecraft.world.entity.global.LightningBolt; import net.minecraft.world.entity.AgableMob; import net.minecraft.world.entity.SpawnGroupData; import net.minecraft.world.entity.MobSpawnType; import net.minecraft.world.DifficultyInstance; import net.minecraft.world.level.LevelAccessor; import net.minecraft.core.particles.ParticleOptions; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.network.chat.TranslatableComponent; import net.minecraft.core.Registry; import net.minecraft.network.chat.Component; import net.minecraft.world.entity.ai.village.poi.PoiManager; import net.minecraft.server.MinecraftServer; import net.minecraft.network.protocol.game.DebugPackets; import java.util.Optional; import java.util.List; import net.minecraft.world.entity.ExperienceOrb; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.sounds.SoundEvent; import net.minecraft.nbt.ListTag; import net.minecraft.world.item.trading.MerchantOffers; import net.minecraft.nbt.Tag; import net.minecraft.util.Mth; import java.util.Iterator; import net.minecraft.world.item.trading.MerchantOffer; import net.minecraft.sounds.SoundEvents; import net.minecraft.world.item.ItemStack; import net.minecraft.stats.Stats; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.item.Items; import net.minecraft.world.InteractionHand; import net.minecraft.world.entity.raid.Raid; import net.minecraft.core.BlockPos; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.ai.village.ReputationEventType; import net.minecraft.world.effect.MobEffectInstance; import net.minecraft.world.effect.MobEffects; import com.google.common.collect.ImmutableSet; import com.mojang.datafixers.util.Pair; import net.minecraft.world.entity.ai.memory.MemoryStatus; import net.minecraft.world.entity.ai.behavior.VillagerGoalPackages; import net.minecraft.world.entity.schedule.Activity; import net.minecraft.world.entity.schedule.Schedule; import net.minecraft.world.entity.monster.SharedMonsterAttributes; import net.minecraft.server.level.ServerLevel; import java.util.Collection; import net.minecraft.world.entity.ai.Brain; import com.mojang.datafixers.types.DynamicOps; import com.mojang.datafixers.Dynamic; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtOps; import net.minecraft.world.entity.ai.navigation.GroundPathNavigation; import net.minecraft.world.level.Level; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.ai.village.poi.PoiType; import java.util.function.BiPredicate; import net.minecraft.core.GlobalPos; import net.minecraft.world.entity.ai.sensing.Sensor; import net.minecraft.world.entity.ai.sensing.SensorType; import net.minecraft.world.entity.ai.memory.MemoryModuleType; import com.google.common.collect.ImmutableList; import net.minecraft.world.entity.ai.gossip.GossipContainer; import javax.annotation.Nullable; import net.minecraft.world.entity.player.Player; import java.util.Set; import net.minecraft.world.item.Item; import java.util.Map; import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.world.entity.ReputationEventHandler; public class Villager extends AbstractVillager implements ReputationEventHandler, VillagerDataHolder { private static final EntityDataAccessor DATA_VILLAGER_DATA; public static final Map FOOD_POINTS; private static final Set WANTED_ITEMS; private int updateMerchantTimer; private boolean increaseProfessionLevelOnUpdate; @Nullable private Player lastTradedPlayer; private byte foodLevel; private final GossipContainer gossips; private long lastGossipTime; private long lastGossipDecayTime; private int villagerXp; private long lastRestockGameTime; private int numberOfRestocksToday; private long lastRestockCheckDayTime; private static final ImmutableList> MEMORY_TYPES; private static final ImmutableList>> SENSOR_TYPES; public static final Map, BiPredicate> POI_MEMORIES; public Villager(final EntityType akr, final Level bjt) { this(akr, bjt, VillagerType.PLAINS); } public Villager(final EntityType akr, final Level bjt, final VillagerType axy) { super(akr, bjt); this.gossips = new GossipContainer(); ((GroundPathNavigation)this.getNavigation()).setCanOpenDoors(true); this.getNavigation().setCanFloat(true); this.setCanPickUpLoot(true); this.setVillagerData(this.getVillagerData().setType(axy).setProfession(VillagerProfession.NONE)); this.brain = this.makeBrain(new Dynamic((DynamicOps)NbtOps.INSTANCE, new CompoundTag())); } @Override public Brain getBrain() { return (Brain)super.getBrain(); } @Override protected Brain makeBrain(final Dynamic dynamic) { final Brain alm3 = new Brain(Villager.MEMORY_TYPES, Villager.SENSOR_TYPES, dynamic); this.registerBrainGoals(alm3); return alm3; } public void refreshBrain(final ServerLevel xd) { final Brain alm3 = this.getBrain(); alm3.stopAll(xd, this); this.brain = alm3.copyWithoutGoals(); this.registerBrainGoals(this.getBrain()); } private void registerBrainGoals(final Brain alm) { final VillagerProfession axw3 = this.getVillagerData().getProfession(); final float float4 = (float)this.getAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).getValue(); if (this.isBaby()) { alm.setSchedule(Schedule.VILLAGER_BABY); alm.addActivity(Activity.PLAY, VillagerGoalPackages.getPlayPackage(float4)); } else { alm.setSchedule(Schedule.VILLAGER_DEFAULT); alm.addActivity(Activity.WORK, VillagerGoalPackages.getWorkPackage(axw3, float4), ImmutableSet.of(Pair.of(MemoryModuleType.JOB_SITE, MemoryStatus.VALUE_PRESENT))); } alm.addActivity(Activity.CORE, VillagerGoalPackages.getCorePackage(axw3, float4)); alm.addActivity(Activity.MEET, VillagerGoalPackages.getMeetPackage(axw3, float4), ImmutableSet.of(Pair.of(MemoryModuleType.MEETING_POINT, MemoryStatus.VALUE_PRESENT))); alm.addActivity(Activity.REST, VillagerGoalPackages.getRestPackage(axw3, float4)); alm.addActivity(Activity.IDLE, VillagerGoalPackages.getIdlePackage(axw3, float4)); alm.addActivity(Activity.PANIC, VillagerGoalPackages.getPanicPackage(axw3, float4)); alm.addActivity(Activity.PRE_RAID, VillagerGoalPackages.getPreRaidPackage(axw3, float4)); alm.addActivity(Activity.RAID, VillagerGoalPackages.getRaidPackage(axw3, float4)); alm.addActivity(Activity.HIDE, VillagerGoalPackages.getHidePackage(axw3, float4)); alm.setCoreActivities(ImmutableSet.of(Activity.CORE)); alm.setDefaultActivity(Activity.IDLE); alm.setActivity(Activity.IDLE); alm.updateActivity(this.level.getDayTime(), this.level.getGameTime()); } @Override protected void ageBoundaryReached() { super.ageBoundaryReached(); if (this.level instanceof ServerLevel) { this.refreshBrain((ServerLevel)this.level); } } @Override protected void registerAttributes() { super.registerAttributes(); this.getAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.5); this.getAttribute(SharedMonsterAttributes.FOLLOW_RANGE).setBaseValue(48.0); } @Override protected void customServerAiStep() { this.level.getProfiler().push("brain"); this.getBrain().tick((ServerLevel)this.level, this); this.level.getProfiler().pop(); if (!this.isTrading() && this.updateMerchantTimer > 0) { --this.updateMerchantTimer; if (this.updateMerchantTimer <= 0) { if (this.increaseProfessionLevelOnUpdate) { this.increaseMerchantCareer(); this.increaseProfessionLevelOnUpdate = false; } this.addEffect(new MobEffectInstance(MobEffects.REGENERATION, 200, 0)); } } if (this.lastTradedPlayer != null && this.level instanceof ServerLevel) { ((ServerLevel)this.level).onReputationEvent(ReputationEventType.TRADE, this.lastTradedPlayer, this); this.level.broadcastEntityEvent(this, (byte)14); this.lastTradedPlayer = null; } if (!this.isNoAi() && this.random.nextInt(100) == 0) { final Raid azk2 = ((ServerLevel)this.level).getRaidAt(new BlockPos(this)); if (azk2 != null && azk2.isActive() && !azk2.isOver()) { this.level.broadcastEntityEvent(this, (byte)42); } } if (this.getVillagerData().getProfession() == VillagerProfession.NONE && this.isTrading()) { this.stopTrading(); } super.customServerAiStep(); } @Override public void tick() { super.tick(); if (this.getUnhappyCounter() > 0) { this.setUnhappyCounter(this.getUnhappyCounter() - 1); } this.maybeDecayGossip(); } @Override public boolean mobInteract(final Player ayg, final InteractionHand ajh) { final ItemStack bek4 = ayg.getItemInHand(ajh); final boolean boolean5 = bek4.getItem() == Items.NAME_TAG; if (boolean5) { bek4.interactEnemy(ayg, this, ajh); return true; } if (bek4.getItem() == Items.VILLAGER_SPAWN_EGG || !this.isAlive() || this.isTrading() || this.isSleeping()) { return super.mobInteract(ayg, ajh); } if (this.isBaby()) { this.setUnhappy(); return super.mobInteract(ayg, ajh); } final boolean boolean6 = this.getOffers().isEmpty(); if (ajh == InteractionHand.MAIN_HAND) { if (boolean6 && !this.level.isClientSide) { this.setUnhappy(); } ayg.awardStat(Stats.TALKED_TO_VILLAGER); } if (boolean6) { return super.mobInteract(ayg, ajh); } if (!this.level.isClientSide && !this.offers.isEmpty()) { this.startTrading(ayg); } return true; } private void setUnhappy() { this.setUnhappyCounter(40); if (!this.level.isClientSide()) { this.playSound(SoundEvents.VILLAGER_NO, this.getSoundVolume(), this.getVoicePitch()); } } private void startTrading(final Player ayg) { this.updateSpecialPrices(ayg); this.setTradingPlayer(ayg); this.openTradingScreen(ayg, this.getDisplayName(), this.getVillagerData().getLevel()); } @Override public void setTradingPlayer(@Nullable final Player ayg) { final boolean boolean3 = this.getTradingPlayer() != null && ayg == null; super.setTradingPlayer(ayg); if (boolean3) { this.stopTrading(); } } @Override protected void stopTrading() { super.stopTrading(); this.resetSpecialPrices(); } private void resetSpecialPrices() { for (final MerchantOffer biw3 : this.getOffers()) { biw3.resetSpecialPriceDiff(); } } @Override public boolean canRestock() { return true; } public void restock() { this.updateDemand(); for (final MerchantOffer biw3 : this.getOffers()) { biw3.resetUses(); } if (this.getVillagerData().getProfession() == VillagerProfession.FARMER) { this.makeBread(); } this.lastRestockGameTime = this.level.getGameTime(); ++this.numberOfRestocksToday; } private boolean needsToRestock() { for (final MerchantOffer biw3 : this.getOffers()) { if (biw3.needsRestock()) { return true; } } return false; } private boolean allowedToRestock() { return this.numberOfRestocksToday == 0 || (this.numberOfRestocksToday < 2 && this.level.getGameTime() > this.lastRestockGameTime + 2400L); } public boolean shouldRestock() { final long long2 = this.lastRestockGameTime + 12000L; final long long3 = this.level.getGameTime(); boolean boolean6 = long3 > long2; final long long4 = this.level.getDayTime(); if (this.lastRestockCheckDayTime > 0L) { final long long5 = this.lastRestockCheckDayTime / 24000L; final long long6 = long4 / 24000L; boolean6 |= (long6 > long5); } this.lastRestockCheckDayTime = long4; if (boolean6) { this.lastRestockGameTime = long3; this.resetNumberOfRestocks(); } return this.allowedToRestock() && this.needsToRestock(); } private void catchUpDemand() { final int integer2 = 2 - this.numberOfRestocksToday; if (integer2 > 0) { for (final MerchantOffer biw4 : this.getOffers()) { biw4.resetUses(); } } for (int integer3 = 0; integer3 < integer2; ++integer3) { this.updateDemand(); } } private void updateDemand() { for (final MerchantOffer biw3 : this.getOffers()) { biw3.updateDemand(); } } private void updateSpecialPrices(final Player ayg) { final int integer3 = this.getPlayerReputation(ayg); if (integer3 != 0) { for (final MerchantOffer biw5 : this.getOffers()) { biw5.addToSpecialPriceDiff(-Mth.floor(integer3 * biw5.getPriceMultiplier())); } } if (ayg.hasEffect(MobEffects.HERO_OF_THE_VILLAGE)) { final MobEffectInstance akh4 = ayg.getEffect(MobEffects.HERO_OF_THE_VILLAGE); final int integer4 = akh4.getAmplifier(); for (final MerchantOffer biw6 : this.getOffers()) { final double double8 = 0.3 + 0.0625 * integer4; final int integer5 = (int)Math.floor(double8 * biw6.getBaseCostA().getCount()); biw6.addToSpecialPriceDiff(-Math.max(integer5, 1)); } } } @Override protected void defineSynchedData() { super.defineSynchedData(); this.entityData.define(Villager.DATA_VILLAGER_DATA, new VillagerData(VillagerType.PLAINS, VillagerProfession.NONE, 1)); } @Override public void addAdditionalSaveData(final CompoundTag jt) { super.addAdditionalSaveData(jt); jt.put("VillagerData", this.getVillagerData().serialize((com.mojang.datafixers.types.DynamicOps)NbtOps.INSTANCE)); jt.putByte("FoodLevel", this.foodLevel); jt.put("Gossips", (Tag)this.gossips.store((com.mojang.datafixers.types.DynamicOps)NbtOps.INSTANCE).getValue()); jt.putInt("Xp", this.villagerXp); jt.putLong("LastRestock", this.lastRestockGameTime); jt.putLong("LastGossipDecay", this.lastGossipDecayTime); jt.putInt("RestocksToday", this.numberOfRestocksToday); } @Override public void readAdditionalSaveData(final CompoundTag jt) { super.readAdditionalSaveData(jt); if (jt.contains("VillagerData", 10)) { this.setVillagerData(new VillagerData(new Dynamic((DynamicOps)NbtOps.INSTANCE, jt.get("VillagerData")))); } if (jt.contains("Offers", 10)) { this.offers = new MerchantOffers(jt.getCompound("Offers")); } if (jt.contains("FoodLevel", 1)) { this.foodLevel = jt.getByte("FoodLevel"); } final ListTag jz3 = jt.getList("Gossips", 10); this.gossips.update(new Dynamic((DynamicOps)NbtOps.INSTANCE, jz3)); if (jt.contains("Xp", 3)) { this.villagerXp = jt.getInt("Xp"); } this.lastRestockGameTime = jt.getLong("LastRestock"); this.lastGossipDecayTime = jt.getLong("LastGossipDecay"); this.setCanPickUpLoot(true); if (this.level instanceof ServerLevel) { this.refreshBrain((ServerLevel)this.level); } this.numberOfRestocksToday = jt.getInt("RestocksToday"); } @Override public boolean removeWhenFarAway(final double double1) { return false; } @Nullable @Override protected SoundEvent getAmbientSound() { if (this.isSleeping()) { return null; } if (this.isTrading()) { return SoundEvents.VILLAGER_TRADE; } return SoundEvents.VILLAGER_AMBIENT; } @Override protected SoundEvent getHurtSound(final DamageSource ajw) { return SoundEvents.VILLAGER_HURT; } @Override protected SoundEvent getDeathSound() { return SoundEvents.VILLAGER_DEATH; } public void playWorkSound() { final SoundEvent aah2 = this.getVillagerData().getProfession().getWorkSound(); if (aah2 != null) { this.playSound(aah2, this.getSoundVolume(), this.getVoicePitch()); } } public void setVillagerData(final VillagerData axu) { final VillagerData axu2 = this.getVillagerData(); if (axu2.getProfession() != axu.getProfession()) { this.offers = null; } this.entityData.set(Villager.DATA_VILLAGER_DATA, axu); } @Override public VillagerData getVillagerData() { return this.entityData.get(Villager.DATA_VILLAGER_DATA); } @Override protected void rewardTradeXp(final MerchantOffer biw) { int integer3 = 3 + this.random.nextInt(4); this.villagerXp += biw.getXp(); this.lastTradedPlayer = this.getTradingPlayer(); if (this.shouldIncreaseLevel()) { this.updateMerchantTimer = 40; this.increaseProfessionLevelOnUpdate = true; integer3 += 5; } if (biw.shouldRewardExp()) { this.level.addFreshEntity(new ExperienceOrb(this.level, this.getX(), this.getY() + 0.5, this.getZ(), integer3)); } } @Override public void setLastHurtByMob(@Nullable final LivingEntity akw) { if (akw != null && this.level instanceof ServerLevel) { ((ServerLevel)this.level).onReputationEvent(ReputationEventType.VILLAGER_HURT, akw, this); if (this.isAlive() && akw instanceof Player) { this.level.broadcastEntityEvent(this, (byte)13); } } super.setLastHurtByMob(akw); } @Override public void die(final DamageSource ajw) { Villager.LOGGER.info("Villager {} died, message: '{}'", this, ajw.getLocalizedDeathMessage(this).getString()); final Entity akn3 = ajw.getEntity(); if (akn3 != null) { this.tellWitnessesThatIWasMurdered(akn3); } this.releasePoi(MemoryModuleType.HOME); this.releasePoi(MemoryModuleType.JOB_SITE); this.releasePoi(MemoryModuleType.MEETING_POINT); super.die(ajw); } private void tellWitnessesThatIWasMurdered(final Entity akn) { if (!(this.level instanceof ServerLevel)) { return; } final Optional> optional3 = this.brain.>getMemory(MemoryModuleType.VISIBLE_LIVING_ENTITIES); if (!optional3.isPresent()) { return; } final ServerLevel xd4 = (ServerLevel)this.level; optional3.get().stream().filter(akw -> akw instanceof ReputationEventHandler).forEach(akw -> xd4.onReputationEvent(ReputationEventType.VILLAGER_KILLED, akn, akw)); } public void releasePoi(final MemoryModuleType ari) { if (!(this.level instanceof ServerLevel)) { return; } final MinecraftServer minecraftServer3 = ((ServerLevel)this.level).getServer(); final ServerLevel xd5; final PoiManager aso6; final Optional optional7; final BiPredicate biPredicate8; this.brain.getMemory(ari).ifPresent(fr -> { xd5 = minecraftServer3.getLevel(fr.dimension()); aso6 = xd5.getPoiManager(); optional7 = aso6.getType(fr.pos()); biPredicate8 = Villager.POI_MEMORIES.get(ari); if (optional7.isPresent() && biPredicate8.test(this, optional7.get())) { aso6.release(fr.pos()); DebugPackets.sendPoiTicketCountPacket(xd5, fr.pos()); } }); } public boolean canBreed() { return this.foodLevel + this.countFoodPointsInInventory() >= 12 && this.getAge() == 0; } private boolean hungry() { return this.foodLevel < 12; } private void eatUntilFull() { if (!this.hungry() || this.countFoodPointsInInventory() == 0) { return; } for (int integer2 = 0; integer2 < this.getInventory().getContainerSize(); ++integer2) { final ItemStack bek3 = this.getInventory().getItem(integer2); if (!bek3.isEmpty()) { final Integer integer3 = Villager.FOOD_POINTS.get(bek3.getItem()); if (integer3 != null) { int integer5; for (int integer4 = integer5 = bek3.getCount(); integer5 > 0; --integer5) { this.foodLevel += (byte)integer3; this.getInventory().removeItem(integer2, 1); if (!this.hungry()) { return; } } } } } } public int getPlayerReputation(final Player ayg) { return this.gossips.getReputation(ayg.getUUID(), arg -> true); } private void digestFood(final int integer) { this.foodLevel -= (byte)integer; } public void eatAndDigestFood() { this.eatUntilFull(); this.digestFood(12); } public void setOffers(final MerchantOffers bix) { this.offers = bix; } private boolean shouldIncreaseLevel() { final int integer2 = this.getVillagerData().getLevel(); return VillagerData.canLevelUp(integer2) && this.villagerXp >= VillagerData.getMaxXpPerLevel(integer2); } private void increaseMerchantCareer() { this.setVillagerData(this.getVillagerData().setLevel(this.getVillagerData().getLevel() + 1)); this.updateTrades(); } @Override protected Component getTypeName() { return new TranslatableComponent(this.getType().getDescriptionId() + '.' + Registry.VILLAGER_PROFESSION.getKey(this.getVillagerData().getProfession()).getPath(), new Object[0]); } @Override public void handleEntityEvent(final byte byte1) { if (byte1 == 12) { this.addParticlesAroundSelf(ParticleTypes.HEART); } else if (byte1 == 13) { this.addParticlesAroundSelf(ParticleTypes.ANGRY_VILLAGER); } else if (byte1 == 14) { this.addParticlesAroundSelf(ParticleTypes.HAPPY_VILLAGER); } else if (byte1 == 42) { this.addParticlesAroundSelf(ParticleTypes.SPLASH); } else { super.handleEntityEvent(byte1); } } @Nullable @Override public SpawnGroupData finalizeSpawn(final LevelAccessor bju, final DifficultyInstance ajg, final MobSpawnType akz, @Nullable final SpawnGroupData alj, @Nullable final CompoundTag jt) { if (akz == MobSpawnType.BREEDING) { this.setVillagerData(this.getVillagerData().setProfession(VillagerProfession.NONE)); } if (akz == MobSpawnType.COMMAND || akz == MobSpawnType.SPAWN_EGG || akz == MobSpawnType.SPAWNER || akz == MobSpawnType.DISPENSER) { this.setVillagerData(this.getVillagerData().setType(VillagerType.byBiome(bju.getBiome(new BlockPos(this))))); } return super.finalizeSpawn(bju, ajg, akz, alj, jt); } @Override public Villager getBreedOffspring(final AgableMob akl) { final double double4 = this.random.nextDouble(); VillagerType axy3; if (double4 < 0.5) { axy3 = VillagerType.byBiome(this.level.getBiome(new BlockPos(this))); } else if (double4 < 0.75) { axy3 = this.getVillagerData().getType(); } else { axy3 = ((Villager)akl).getVillagerData().getType(); } final Villager axt6 = new Villager(EntityType.VILLAGER, this.level, axy3); axt6.finalizeSpawn(this.level, this.level.getCurrentDifficultyAt(new BlockPos(axt6)), MobSpawnType.BREEDING, null, null); return axt6; } @Override public void thunderHit(final LightningBolt avv) { final Witch axk3 = EntityType.WITCH.create(this.level); axk3.moveTo(this.getX(), this.getY(), this.getZ(), this.yRot, this.xRot); axk3.finalizeSpawn(this.level, this.level.getCurrentDifficultyAt(new BlockPos(axk3)), MobSpawnType.CONVERSION, null, null); axk3.setNoAi(this.isNoAi()); if (this.hasCustomName()) { axk3.setCustomName(this.getCustomName()); axk3.setCustomNameVisible(this.isCustomNameVisible()); } this.level.addFreshEntity(axk3); this.remove(); } @Override protected void pickUpItem(final ItemEntity avy) { final ItemStack bek3 = avy.getItem(); final Item bef4 = bek3.getItem(); if (this.wantToPickUp(bef4)) { final SimpleContainer ajn5 = this.getInventory(); boolean boolean6 = false; for (int integer7 = 0; integer7 < ajn5.getContainerSize(); ++integer7) { final ItemStack bek4 = ajn5.getItem(integer7); if (bek4.isEmpty() || (bek4.getItem() == bef4 && bek4.getCount() < bek4.getMaxStackSize())) { boolean6 = true; break; } } if (!boolean6) { return; } int integer7 = ajn5.countItem(bef4); if (integer7 == 256) { return; } if (integer7 > 256) { ajn5.removeItemType(bef4, integer7 - 256); return; } this.take(avy, bek3.getCount()); final ItemStack bek4 = ajn5.addItem(bek3); if (bek4.isEmpty()) { avy.remove(); } else { bek3.setCount(bek4.getCount()); } } } public boolean wantToPickUp(final Item bef) { return Villager.WANTED_ITEMS.contains(bef) || this.getVillagerData().getProfession().getRequestedItems().contains(bef); } public boolean hasExcessFood() { return this.countFoodPointsInInventory() >= 24; } public boolean wantsMoreFood() { return this.countFoodPointsInInventory() < 12; } private int countFoodPointsInInventory() { final SimpleContainer ajn2 = this.getInventory(); return Villager.FOOD_POINTS.entrySet().stream().mapToInt(entry -> ajn2.countItem(entry.getKey()) * (int)entry.getValue()).sum(); } private void makeBread() { final SimpleContainer ajn2 = this.getInventory(); final int integer3 = ajn2.countItem(Items.WHEAT); final int integer4 = integer3 / 3; if (integer4 == 0) { return; } final int integer5 = integer4 * 3; ajn2.removeItemType(Items.WHEAT, integer5); final ItemStack bek6 = ajn2.addItem(new ItemStack(Items.BREAD, integer4)); if (!bek6.isEmpty()) { this.spawnAtLocation(bek6, 0.5f); } } public boolean hasFarmSeeds() { final SimpleContainer ajn2 = this.getInventory(); return ajn2.hasAnyOf(ImmutableSet.of(Items.WHEAT_SEEDS, Items.POTATO, Items.CARROT, Items.BEETROOT_SEEDS)); } @Override protected void updateTrades() { final VillagerData axu2 = this.getVillagerData(); final Int2ObjectMap int2ObjectMap3 = VillagerTrades.TRADES.get(axu2.getProfession()); if (int2ObjectMap3 == null || int2ObjectMap3.isEmpty()) { return; } final VillagerTrades.ItemListing[] arr4 = (VillagerTrades.ItemListing[])int2ObjectMap3.get(axu2.getLevel()); if (arr4 == null) { return; } final MerchantOffers bix5 = this.getOffers(); this.addOffersFromItemListings(bix5, arr4, 2); } public void gossip(final Villager axt, final long long2) { if ((long2 >= this.lastGossipTime && long2 < this.lastGossipTime + 1200L) || (long2 >= axt.lastGossipTime && long2 < axt.lastGossipTime + 1200L)) { return; } this.gossips.transferFrom(axt.gossips, this.random, 10); this.lastGossipTime = long2; this.spawnGolemIfNeeded(axt.lastGossipTime = long2, 5); } private void maybeDecayGossip() { final long long2 = this.level.getGameTime(); if (this.lastGossipDecayTime == 0L) { this.lastGossipDecayTime = long2; return; } if (long2 < this.lastGossipDecayTime + 24000L) { return; } this.gossips.decay(); this.lastGossipDecayTime = long2; } public void spawnGolemIfNeeded(final long long1, final int integer) { if (!this.wantsToSpawnGolem(long1)) { return; } final AABB cvc5 = this.getBoundingBox().inflate(10.0, 10.0, 10.0); final List list6 = this.level.getEntitiesOfClass(Villager.class, cvc5); final List list7 = list6.stream().filter(axt -> axt.wantsToSpawnGolem(long1)).limit(5L).collect(Collectors.toList()); if (list7.size() < integer) { return; } final IronGolem ati8 = this.trySpawnGolem(); if (ati8 == null) { return; } list6.forEach(axt -> axt.sawGolem(long1)); } private void sawGolem(final long long1) { this.brain.setMemory(MemoryModuleType.GOLEM_LAST_SEEN_TIME, long1); } private boolean hasSeenGolemRecently(final long long1) { final Optional optional4 = this.brain.getMemory(MemoryModuleType.GOLEM_LAST_SEEN_TIME); if (!optional4.isPresent()) { return false; } final Long long2 = optional4.get(); return long1 - long2 <= 600L; } public boolean wantsToSpawnGolem(final long long1) { final VillagerData axu4 = this.getVillagerData(); return axu4.getProfession() != VillagerProfession.NONE && axu4.getProfession() != VillagerProfession.NITWIT && this.golemSpawnConditionsMet(this.level.getGameTime()) && !this.hasSeenGolemRecently(long1); } @Nullable private IronGolem trySpawnGolem() { final BlockPos fk2 = new BlockPos(this); for (int integer3 = 0; integer3 < 10; ++integer3) { final double double4 = this.level.random.nextInt(16) - 8; final double double5 = this.level.random.nextInt(16) - 8; double double6 = 6.0; for (int integer4 = 0; integer4 >= -12; --integer4) { final BlockPos fk3 = fk2.offset(double4, double6 + integer4, double5); if ((this.level.getBlockState(fk3).isAir() || this.level.getBlockState(fk3).getMaterial().isLiquid()) && this.level.getBlockState(fk3.below()).getMaterial().isSolidBlocking()) { double6 += integer4; break; } } final BlockPos fk4 = fk2.offset(double4, double6, double5); final IronGolem ati11 = EntityType.IRON_GOLEM.create(this.level, null, null, null, fk4, MobSpawnType.MOB_SUMMONED, false, false); if (ati11 != null) { if (ati11.checkSpawnRules(this.level, MobSpawnType.MOB_SUMMONED) && ati11.checkSpawnObstruction(this.level)) { this.level.addFreshEntity(ati11); return ati11; } ati11.remove(); } } return null; } @Override public void onReputationEventFrom(final ReputationEventType asl, final Entity akn) { if (asl == ReputationEventType.ZOMBIE_VILLAGER_CURED) { this.gossips.add(akn.getUUID(), GossipType.MAJOR_POSITIVE, 20); this.gossips.add(akn.getUUID(), GossipType.MINOR_POSITIVE, 25); } else if (asl == ReputationEventType.TRADE) { this.gossips.add(akn.getUUID(), GossipType.TRADING, 2); } else if (asl == ReputationEventType.VILLAGER_HURT) { this.gossips.add(akn.getUUID(), GossipType.MINOR_NEGATIVE, 25); } else if (asl == ReputationEventType.VILLAGER_KILLED) { this.gossips.add(akn.getUUID(), GossipType.MAJOR_NEGATIVE, 25); } } @Override public int getVillagerXp() { return this.villagerXp; } public void setVillagerXp(final int integer) { this.villagerXp = integer; } private void resetNumberOfRestocks() { this.catchUpDemand(); this.numberOfRestocksToday = 0; } public GossipContainer getGossips() { return this.gossips; } public void setGossips(final Tag kj) { this.gossips.update(new Dynamic((DynamicOps)NbtOps.INSTANCE, kj)); } @Override protected void sendDebugPackets() { super.sendDebugPackets(); DebugPackets.sendEntityBrain(this); } @Override public void startSleeping(final BlockPos fk) { super.startSleeping(fk); this.brain.setMemory(MemoryModuleType.LAST_SLEPT, SerializableLong.of(this.level.getGameTime())); } @Override public void stopSleeping() { super.stopSleeping(); this.brain.setMemory(MemoryModuleType.LAST_WOKEN, SerializableLong.of(this.level.getGameTime())); } private boolean golemSpawnConditionsMet(final long long1) { final Optional optional4 = this.brain.getMemory(MemoryModuleType.LAST_SLEPT); final Optional optional5 = this.brain.getMemory(MemoryModuleType.LAST_WORKED_AT_POI); return optional4.isPresent() && optional5.isPresent() && long1 - optional4.get().value() < 24000L && long1 - optional5.get().value() < 36000L; } static { DATA_VILLAGER_DATA = SynchedEntityData.defineId(Villager.class, EntityDataSerializers.VILLAGER_DATA); FOOD_POINTS = ImmutableMap.of(Items.BREAD, Integer.valueOf(4), Items.POTATO, Integer.valueOf(1), Items.CARROT, Integer.valueOf(1), Items.BEETROOT, Integer.valueOf(1)); WANTED_ITEMS = ImmutableSet.of(Items.BREAD, Items.POTATO, Items.CARROT, Items.WHEAT, Items.WHEAT_SEEDS, Items.BEETROOT, new Item[] { Items.BEETROOT_SEEDS }); MEMORY_TYPES = ImmutableList.>of(MemoryModuleType.HOME, MemoryModuleType.JOB_SITE, MemoryModuleType.MEETING_POINT, (MemoryModuleType)MemoryModuleType.LIVING_ENTITIES, (MemoryModuleType)MemoryModuleType.VISIBLE_LIVING_ENTITIES, (MemoryModuleType)MemoryModuleType.VISIBLE_VILLAGER_BABIES, (MemoryModuleType)MemoryModuleType.NEAREST_PLAYERS, (MemoryModuleType)MemoryModuleType.NEAREST_VISIBLE_PLAYER, (MemoryModuleType)MemoryModuleType.WALK_TARGET, (MemoryModuleType)MemoryModuleType.LOOK_TARGET, (MemoryModuleType)MemoryModuleType.INTERACTION_TARGET, (MemoryModuleType)MemoryModuleType.BREED_TARGET, MemoryModuleType.PATH, MemoryModuleType.INTERACTABLE_DOORS, MemoryModuleType.OPENED_DOORS, MemoryModuleType.NEAREST_BED, MemoryModuleType.HURT_BY, MemoryModuleType.HURT_BY_ENTITY, MemoryModuleType.NEAREST_HOSTILE, MemoryModuleType.SECONDARY_JOB_SITE, MemoryModuleType.HIDING_PLACE, MemoryModuleType.HEARD_BELL_TIME, MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE, MemoryModuleType.LAST_SLEPT, MemoryModuleType.LAST_WOKEN, MemoryModuleType.LAST_WORKED_AT_POI, MemoryModuleType.GOLEM_LAST_SEEN_TIME); SENSOR_TYPES = ImmutableList.>of(SensorType.NEAREST_LIVING_ENTITIES, (SensorType)SensorType.NEAREST_PLAYERS, (SensorType)SensorType.INTERACTABLE_DOORS, (SensorType)SensorType.NEAREST_BED, (SensorType)SensorType.HURT_BY, (SensorType)SensorType.VILLAGER_HOSTILES, (SensorType)SensorType.VILLAGER_BABIES, (SensorType)SensorType.SECONDARY_POIS, (SensorType)SensorType.GOLEM_LAST_SEEN); POI_MEMORIES = ImmutableMap., Object>of(MemoryModuleType.HOME, (axt, asr) -> asr == PoiType.HOME, MemoryModuleType.JOB_SITE, (axt, asr) -> axt.getVillagerData().getProfession().getJobPoiType() == asr, MemoryModuleType.MEETING_POINT, (axt, asr) -> asr == PoiType.MEETING); } }