minecraft-source/src/net/minecraft/world/entity/npc/Villager.java

904 lines
38 KiB
Java

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<VillagerData> DATA_VILLAGER_DATA;
public static final Map<Item, Integer> FOOD_POINTS;
private static final Set<Item> 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<MemoryModuleType<?>> MEMORY_TYPES;
private static final ImmutableList<SensorType<? extends Sensor<? super Villager>>> SENSOR_TYPES;
public static final Map<MemoryModuleType<GlobalPos>, BiPredicate<Villager, PoiType>> POI_MEMORIES;
public Villager(final EntityType<? extends Villager> akr, final Level bjt) {
this(akr, bjt, VillagerType.PLAINS);
}
public Villager(final EntityType<? extends Villager> 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<Villager> getBrain() {
return (Brain<Villager>)super.getBrain();
}
@Override
protected Brain<?> makeBrain(final Dynamic<?> dynamic) {
final Brain<Villager> alm3 = new Brain<Villager>(Villager.MEMORY_TYPES, Villager.SENSOR_TYPES, dynamic);
this.registerBrainGoals(alm3);
return alm3;
}
public void refreshBrain(final ServerLevel xd) {
final Brain<Villager> alm3 = this.getBrain();
alm3.stopAll(xd, this);
this.brain = alm3.copyWithoutGoals();
this.registerBrainGoals(this.getBrain());
}
private void registerBrainGoals(final Brain<Villager> 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.<Activity>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.<VillagerData>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().<Tag>serialize((com.mojang.datafixers.types.DynamicOps<Tag>)NbtOps.INSTANCE));
jt.putByte("FoodLevel", this.foodLevel);
jt.put("Gossips", (Tag)this.gossips.store((com.mojang.datafixers.types.DynamicOps<Object>)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.<VillagerData>set(Villager.DATA_VILLAGER_DATA, axu);
}
@Override
public VillagerData getVillagerData() {
return this.entityData.<VillagerData>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<List<LivingEntity>> optional3 = this.brain.<List<LivingEntity>>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<GlobalPos> ari) {
if (!(this.level instanceof ServerLevel)) {
return;
}
final MinecraftServer minecraftServer3 = ((ServerLevel)this.level).getServer();
final ServerLevel xd5;
final PoiManager aso6;
final Optional<PoiType> optional7;
final BiPredicate<Villager, PoiType> biPredicate8;
this.brain.<GlobalPos>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.<Item>of(Items.WHEAT_SEEDS, Items.POTATO, Items.CARROT, Items.BEETROOT_SEEDS));
}
@Override
protected void updateTrades() {
final VillagerData axu2 = this.getVillagerData();
final Int2ObjectMap<VillagerTrades.ItemListing[]> 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<Villager> list6 = this.level.<Villager>getEntitiesOfClass(Villager.class, cvc5);
final List<Villager> 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.<Long>setMemory(MemoryModuleType.GOLEM_LAST_SEEN_TIME, long1);
}
private boolean hasSeenGolemRecently(final long long1) {
final Optional<Long> optional4 = this.brain.<Long>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.<SerializableLong>setMemory(MemoryModuleType.LAST_SLEPT, SerializableLong.of(this.level.getGameTime()));
}
@Override
public void stopSleeping() {
super.stopSleeping();
this.brain.<SerializableLong>setMemory(MemoryModuleType.LAST_WOKEN, SerializableLong.of(this.level.getGameTime()));
}
private boolean golemSpawnConditionsMet(final long long1) {
final Optional<SerializableLong> optional4 = this.brain.<SerializableLong>getMemory(MemoryModuleType.LAST_SLEPT);
final Optional<SerializableLong> optional5 = this.brain.<SerializableLong>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.<VillagerData>defineId(Villager.class, EntityDataSerializers.VILLAGER_DATA);
FOOD_POINTS = ImmutableMap.<Item, Integer>of(Items.BREAD, Integer.valueOf(4), Items.POTATO, Integer.valueOf(1), Items.CARROT, Integer.valueOf(1), Items.BEETROOT, Integer.valueOf(1));
WANTED_ITEMS = ImmutableSet.<Item>of(Items.BREAD, Items.POTATO, Items.CARROT, Items.WHEAT, Items.WHEAT_SEEDS, Items.BEETROOT, new Item[] { Items.BEETROOT_SEEDS });
MEMORY_TYPES = ImmutableList.<MemoryModuleType<GlobalPos>>of(MemoryModuleType.HOME, MemoryModuleType.JOB_SITE, MemoryModuleType.MEETING_POINT, (MemoryModuleType<GlobalPos>)MemoryModuleType.LIVING_ENTITIES, (MemoryModuleType<GlobalPos>)MemoryModuleType.VISIBLE_LIVING_ENTITIES, (MemoryModuleType<GlobalPos>)MemoryModuleType.VISIBLE_VILLAGER_BABIES, (MemoryModuleType<GlobalPos>)MemoryModuleType.NEAREST_PLAYERS, (MemoryModuleType<GlobalPos>)MemoryModuleType.NEAREST_VISIBLE_PLAYER, (MemoryModuleType<GlobalPos>)MemoryModuleType.WALK_TARGET, (MemoryModuleType<GlobalPos>)MemoryModuleType.LOOK_TARGET, (MemoryModuleType<GlobalPos>)MemoryModuleType.INTERACTION_TARGET, (MemoryModuleType<GlobalPos>)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.<SensorType<NearestLivingEntitySensor>>of(SensorType.NEAREST_LIVING_ENTITIES, (SensorType<NearestLivingEntitySensor>)SensorType.NEAREST_PLAYERS, (SensorType<NearestLivingEntitySensor>)SensorType.INTERACTABLE_DOORS, (SensorType<NearestLivingEntitySensor>)SensorType.NEAREST_BED, (SensorType<NearestLivingEntitySensor>)SensorType.HURT_BY, (SensorType<NearestLivingEntitySensor>)SensorType.VILLAGER_HOSTILES, (SensorType<NearestLivingEntitySensor>)SensorType.VILLAGER_BABIES, (SensorType<NearestLivingEntitySensor>)SensorType.SECONDARY_POIS, (SensorType<NearestLivingEntitySensor>)SensorType.GOLEM_LAST_SEEN);
POI_MEMORIES = ImmutableMap.<MemoryModuleType<GlobalPos>, 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);
}
}