281 lines
11 KiB
Java
281 lines
11 KiB
Java
package net.minecraft.world.entity.npc;
|
|
|
|
import net.minecraft.core.Position;
|
|
import net.minecraft.world.phys.Vec3;
|
|
import java.util.EnumSet;
|
|
import net.minecraft.world.entity.ai.navigation.PathNavigation;
|
|
import net.minecraft.world.item.Item;
|
|
import net.minecraft.world.damagesource.DamageSource;
|
|
import net.minecraft.sounds.SoundEvent;
|
|
import net.minecraft.world.entity.ExperienceOrb;
|
|
import net.minecraft.nbt.Tag;
|
|
import net.minecraft.nbt.NbtUtils;
|
|
import net.minecraft.nbt.CompoundTag;
|
|
import net.minecraft.world.item.trading.MerchantOffer;
|
|
import net.minecraft.world.item.trading.MerchantOffers;
|
|
import net.minecraft.world.entity.Entity;
|
|
import net.minecraft.stats.Stats;
|
|
import net.minecraft.world.InteractionHand;
|
|
import net.minecraft.world.entity.AgableMob;
|
|
import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal;
|
|
import net.minecraft.world.entity.LivingEntity;
|
|
import net.minecraft.world.entity.ai.goal.InteractGoal;
|
|
import net.minecraft.world.entity.player.Player;
|
|
import net.minecraft.world.entity.ai.goal.WaterAvoidingRandomStrollGoal;
|
|
import net.minecraft.world.entity.ai.goal.MoveTowardsRestrictionGoal;
|
|
import net.minecraft.world.entity.ai.goal.LookAtTradingPlayerGoal;
|
|
import net.minecraft.world.entity.ai.goal.PanicGoal;
|
|
import net.minecraft.world.entity.monster.Illusioner;
|
|
import net.minecraft.world.entity.monster.Pillager;
|
|
import net.minecraft.world.entity.monster.Vex;
|
|
import net.minecraft.world.entity.monster.Vindicator;
|
|
import net.minecraft.world.entity.monster.Evoker;
|
|
import net.minecraft.world.entity.PathfinderMob;
|
|
import net.minecraft.world.entity.ai.goal.AvoidEntityGoal;
|
|
import net.minecraft.world.entity.monster.Zombie;
|
|
import net.minecraft.world.entity.ai.goal.TradeWithPlayerGoal;
|
|
import net.minecraft.world.entity.ai.goal.UseItemGoal;
|
|
import net.minecraft.sounds.SoundEvents;
|
|
import net.minecraft.world.item.alchemy.PotionUtils;
|
|
import net.minecraft.world.item.alchemy.Potions;
|
|
import net.minecraft.world.level.ItemLike;
|
|
import net.minecraft.world.item.ItemStack;
|
|
import net.minecraft.world.item.Items;
|
|
import net.minecraft.world.entity.ai.goal.Goal;
|
|
import net.minecraft.world.entity.Mob;
|
|
import net.minecraft.world.entity.ai.goal.FloatGoal;
|
|
import net.minecraft.world.level.Level;
|
|
import net.minecraft.world.entity.EntityType;
|
|
import javax.annotation.Nullable;
|
|
import net.minecraft.core.BlockPos;
|
|
|
|
public class WanderingTrader extends AbstractVillager {
|
|
@Nullable
|
|
private BlockPos wanderTarget;
|
|
private int despawnDelay;
|
|
|
|
public WanderingTrader(final EntityType<? extends WanderingTrader> akr, final Level bjt) {
|
|
super(akr, bjt);
|
|
this.forcedLoading = true;
|
|
}
|
|
|
|
@Override
|
|
protected void registerGoals() {
|
|
this.goalSelector.addGoal(0, new FloatGoal(this));
|
|
this.goalSelector.addGoal(0, new UseItemGoal<>(this, PotionUtils.setPotion(new ItemStack(Items.POTION), Potions.INVISIBILITY), SoundEvents.WANDERING_TRADER_DISAPPEARED, axz -> !this.level.isDay() && !axz.isInvisible()));
|
|
this.goalSelector.addGoal(0, new UseItemGoal<>(this, new ItemStack(Items.MILK_BUCKET), SoundEvents.WANDERING_TRADER_REAPPEARED, axz -> this.level.isDay() && axz.isInvisible()));
|
|
this.goalSelector.addGoal(1, new TradeWithPlayerGoal(this));
|
|
this.goalSelector.addGoal(1, new AvoidEntityGoal<>(this, Zombie.class, 8.0f, 0.5, 0.5));
|
|
this.goalSelector.addGoal(1, new AvoidEntityGoal<>(this, Evoker.class, 12.0f, 0.5, 0.5));
|
|
this.goalSelector.addGoal(1, new AvoidEntityGoal<>(this, Vindicator.class, 8.0f, 0.5, 0.5));
|
|
this.goalSelector.addGoal(1, new AvoidEntityGoal<>(this, Vex.class, 8.0f, 0.5, 0.5));
|
|
this.goalSelector.addGoal(1, new AvoidEntityGoal<>(this, Pillager.class, 15.0f, 0.5, 0.5));
|
|
this.goalSelector.addGoal(1, new AvoidEntityGoal<>(this, Illusioner.class, 12.0f, 0.5, 0.5));
|
|
this.goalSelector.addGoal(1, new PanicGoal(this, 0.5));
|
|
this.goalSelector.addGoal(1, new LookAtTradingPlayerGoal(this));
|
|
this.goalSelector.addGoal(2, new WanderToPositionGoal(this, 2.0, 0.35));
|
|
this.goalSelector.addGoal(4, new MoveTowardsRestrictionGoal(this, 0.35));
|
|
this.goalSelector.addGoal(8, new WaterAvoidingRandomStrollGoal(this, 0.35));
|
|
this.goalSelector.addGoal(9, new InteractGoal(this, Player.class, 3.0f, 1.0f));
|
|
this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Mob.class, 8.0f));
|
|
}
|
|
|
|
@Nullable
|
|
@Override
|
|
public AgableMob getBreedOffspring(final AgableMob akl) {
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public boolean showProgressBar() {
|
|
return false;
|
|
}
|
|
|
|
@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.isBaby()) {
|
|
return super.mobInteract(ayg, ajh);
|
|
}
|
|
if (ajh == InteractionHand.MAIN_HAND) {
|
|
ayg.awardStat(Stats.TALKED_TO_VILLAGER);
|
|
}
|
|
if (this.getOffers().isEmpty()) {
|
|
return super.mobInteract(ayg, ajh);
|
|
}
|
|
if (!this.level.isClientSide) {
|
|
this.setTradingPlayer(ayg);
|
|
this.openTradingScreen(ayg, this.getDisplayName(), 1);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
protected void updateTrades() {
|
|
final VillagerTrades.ItemListing[] arr2 = (VillagerTrades.ItemListing[])VillagerTrades.WANDERING_TRADER_TRADES.get(1);
|
|
final VillagerTrades.ItemListing[] arr3 = (VillagerTrades.ItemListing[])VillagerTrades.WANDERING_TRADER_TRADES.get(2);
|
|
if (arr2 == null || arr3 == null) {
|
|
return;
|
|
}
|
|
final MerchantOffers bix4 = this.getOffers();
|
|
this.addOffersFromItemListings(bix4, arr2, 5);
|
|
final int integer5 = this.random.nextInt(arr3.length);
|
|
final VillagerTrades.ItemListing f6 = arr3[integer5];
|
|
final MerchantOffer biw7 = f6.getOffer(this, this.random);
|
|
if (biw7 != null) {
|
|
bix4.add(biw7);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void addAdditionalSaveData(final CompoundTag jt) {
|
|
super.addAdditionalSaveData(jt);
|
|
jt.putInt("DespawnDelay", this.despawnDelay);
|
|
if (this.wanderTarget != null) {
|
|
jt.put("WanderTarget", NbtUtils.writeBlockPos(this.wanderTarget));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void readAdditionalSaveData(final CompoundTag jt) {
|
|
super.readAdditionalSaveData(jt);
|
|
if (jt.contains("DespawnDelay", 99)) {
|
|
this.despawnDelay = jt.getInt("DespawnDelay");
|
|
}
|
|
if (jt.contains("WanderTarget")) {
|
|
this.wanderTarget = NbtUtils.readBlockPos(jt.getCompound("WanderTarget"));
|
|
}
|
|
this.setAge(Math.max(0, this.getAge()));
|
|
}
|
|
|
|
@Override
|
|
public boolean removeWhenFarAway(final double double1) {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
protected void rewardTradeXp(final MerchantOffer biw) {
|
|
if (biw.shouldRewardExp()) {
|
|
final int integer3 = 3 + this.random.nextInt(4);
|
|
this.level.addFreshEntity(new ExperienceOrb(this.level, this.getX(), this.getY() + 0.5, this.getZ(), integer3));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected SoundEvent getAmbientSound() {
|
|
if (this.isTrading()) {
|
|
return SoundEvents.WANDERING_TRADER_TRADE;
|
|
}
|
|
return SoundEvents.WANDERING_TRADER_AMBIENT;
|
|
}
|
|
|
|
@Override
|
|
protected SoundEvent getHurtSound(final DamageSource ajw) {
|
|
return SoundEvents.WANDERING_TRADER_HURT;
|
|
}
|
|
|
|
@Override
|
|
protected SoundEvent getDeathSound() {
|
|
return SoundEvents.WANDERING_TRADER_DEATH;
|
|
}
|
|
|
|
@Override
|
|
protected SoundEvent getDrinkingSound(final ItemStack bek) {
|
|
final Item bef3 = bek.getItem();
|
|
if (bef3 == Items.MILK_BUCKET) {
|
|
return SoundEvents.WANDERING_TRADER_DRINK_MILK;
|
|
}
|
|
return SoundEvents.WANDERING_TRADER_DRINK_POTION;
|
|
}
|
|
|
|
@Override
|
|
protected SoundEvent getTradeUpdatedSound(final boolean boolean1) {
|
|
return boolean1 ? SoundEvents.WANDERING_TRADER_YES : SoundEvents.WANDERING_TRADER_NO;
|
|
}
|
|
|
|
@Override
|
|
public SoundEvent getNotifyTradeSound() {
|
|
return SoundEvents.WANDERING_TRADER_YES;
|
|
}
|
|
|
|
public void setDespawnDelay(final int integer) {
|
|
this.despawnDelay = integer;
|
|
}
|
|
|
|
public int getDespawnDelay() {
|
|
return this.despawnDelay;
|
|
}
|
|
|
|
@Override
|
|
public void aiStep() {
|
|
super.aiStep();
|
|
if (!this.level.isClientSide) {
|
|
this.maybeDespawn();
|
|
}
|
|
}
|
|
|
|
private void maybeDespawn() {
|
|
if (this.despawnDelay > 0 && !this.isTrading() && --this.despawnDelay == 0) {
|
|
this.remove();
|
|
}
|
|
}
|
|
|
|
public void setWanderTarget(@Nullable final BlockPos fk) {
|
|
this.wanderTarget = fk;
|
|
}
|
|
|
|
@Nullable
|
|
private BlockPos getWanderTarget() {
|
|
return this.wanderTarget;
|
|
}
|
|
|
|
class WanderToPositionGoal extends Goal {
|
|
final WanderingTrader trader;
|
|
final double stopDistance;
|
|
final double speedModifier;
|
|
|
|
WanderToPositionGoal(final WanderingTrader axz2, final double double3, final double double4) {
|
|
this.trader = axz2;
|
|
this.stopDistance = double3;
|
|
this.speedModifier = double4;
|
|
this.setFlags(EnumSet.<Flag>of(Flag.MOVE));
|
|
}
|
|
|
|
@Override
|
|
public void stop() {
|
|
this.trader.setWanderTarget(null);
|
|
WanderingTrader.this.navigation.stop();
|
|
}
|
|
|
|
@Override
|
|
public boolean canUse() {
|
|
final BlockPos fk2 = this.trader.getWanderTarget();
|
|
return fk2 != null && this.isTooFarAway(fk2, this.stopDistance);
|
|
}
|
|
|
|
@Override
|
|
public void tick() {
|
|
final BlockPos fk2 = this.trader.getWanderTarget();
|
|
if (fk2 != null && WanderingTrader.this.navigation.isDone()) {
|
|
if (this.isTooFarAway(fk2, 10.0)) {
|
|
final Vec3 cvi3 = new Vec3(fk2.getX() - this.trader.getX(), fk2.getY() - this.trader.getY(), fk2.getZ() - this.trader.getZ()).normalize();
|
|
final Vec3 cvi4 = cvi3.scale(10.0).add(this.trader.getX(), this.trader.getY(), this.trader.getZ());
|
|
WanderingTrader.this.navigation.moveTo(cvi4.x, cvi4.y, cvi4.z, this.speedModifier);
|
|
}
|
|
else {
|
|
WanderingTrader.this.navigation.moveTo(fk2.getX(), fk2.getY(), fk2.getZ(), this.speedModifier);
|
|
}
|
|
}
|
|
}
|
|
|
|
private boolean isTooFarAway(final BlockPos fk, final double double2) {
|
|
return !fk.closerThan(this.trader.position(), double2);
|
|
}
|
|
}
|
|
}
|