259 lines
8.8 KiB
Java
259 lines
8.8 KiB
Java
/*
|
|
* Decompiled with CFR 0.152.
|
|
*
|
|
* Could not load the following classes:
|
|
* com.google.common.collect.Lists
|
|
* org.jspecify.annotations.Nullable
|
|
*/
|
|
package net.minecraft.world.entity.npc;
|
|
|
|
import com.google.common.collect.Lists;
|
|
import java.util.ArrayList;
|
|
import net.minecraft.advancements.CriteriaTriggers;
|
|
import net.minecraft.core.particles.ParticleOptions;
|
|
import net.minecraft.network.syncher.EntityDataAccessor;
|
|
import net.minecraft.network.syncher.EntityDataSerializers;
|
|
import net.minecraft.network.syncher.SynchedEntityData;
|
|
import net.minecraft.server.level.ServerLevel;
|
|
import net.minecraft.server.level.ServerPlayer;
|
|
import net.minecraft.sounds.SoundEvent;
|
|
import net.minecraft.sounds.SoundEvents;
|
|
import net.minecraft.util.Mth;
|
|
import net.minecraft.world.DifficultyInstance;
|
|
import net.minecraft.world.SimpleContainer;
|
|
import net.minecraft.world.damagesource.DamageSource;
|
|
import net.minecraft.world.entity.AgeableMob;
|
|
import net.minecraft.world.entity.Entity;
|
|
import net.minecraft.world.entity.EntitySpawnReason;
|
|
import net.minecraft.world.entity.EntityType;
|
|
import net.minecraft.world.entity.SlotAccess;
|
|
import net.minecraft.world.entity.SpawnGroupData;
|
|
import net.minecraft.world.entity.npc.InventoryCarrier;
|
|
import net.minecraft.world.entity.npc.Npc;
|
|
import net.minecraft.world.entity.npc.VillagerTrades;
|
|
import net.minecraft.world.entity.player.Player;
|
|
import net.minecraft.world.item.ItemStack;
|
|
import net.minecraft.world.item.trading.Merchant;
|
|
import net.minecraft.world.item.trading.MerchantOffer;
|
|
import net.minecraft.world.item.trading.MerchantOffers;
|
|
import net.minecraft.world.level.Level;
|
|
import net.minecraft.world.level.ServerLevelAccessor;
|
|
import net.minecraft.world.level.pathfinder.PathType;
|
|
import net.minecraft.world.level.portal.TeleportTransition;
|
|
import net.minecraft.world.level.storage.ValueInput;
|
|
import net.minecraft.world.level.storage.ValueOutput;
|
|
import net.minecraft.world.phys.Vec3;
|
|
import org.jspecify.annotations.Nullable;
|
|
|
|
public abstract class AbstractVillager
|
|
extends AgeableMob
|
|
implements Npc,
|
|
Merchant,
|
|
InventoryCarrier {
|
|
private static final EntityDataAccessor<Integer> DATA_UNHAPPY_COUNTER = SynchedEntityData.defineId(AbstractVillager.class, EntityDataSerializers.INT);
|
|
public static final int VILLAGER_SLOT_OFFSET = 300;
|
|
private static final int VILLAGER_INVENTORY_SIZE = 8;
|
|
private @Nullable Player tradingPlayer;
|
|
protected @Nullable MerchantOffers offers;
|
|
private final SimpleContainer inventory = new SimpleContainer(8);
|
|
|
|
public AbstractVillager(EntityType<? extends AbstractVillager> type, Level level) {
|
|
super((EntityType<? extends AgeableMob>)type, level);
|
|
this.setPathfindingMalus(PathType.DANGER_FIRE, 16.0f);
|
|
this.setPathfindingMalus(PathType.DAMAGE_FIRE, -1.0f);
|
|
}
|
|
|
|
@Override
|
|
public @Nullable SpawnGroupData finalizeSpawn(ServerLevelAccessor level, DifficultyInstance difficulty, EntitySpawnReason spawnReason, @Nullable SpawnGroupData groupData) {
|
|
if (groupData == null) {
|
|
groupData = new AgeableMob.AgeableMobGroupData(false);
|
|
}
|
|
return super.finalizeSpawn(level, difficulty, spawnReason, groupData);
|
|
}
|
|
|
|
public int getUnhappyCounter() {
|
|
return this.entityData.get(DATA_UNHAPPY_COUNTER);
|
|
}
|
|
|
|
public void setUnhappyCounter(int value) {
|
|
this.entityData.set(DATA_UNHAPPY_COUNTER, value);
|
|
}
|
|
|
|
@Override
|
|
public int getVillagerXp() {
|
|
return 0;
|
|
}
|
|
|
|
@Override
|
|
protected void defineSynchedData(SynchedEntityData.Builder entityData) {
|
|
super.defineSynchedData(entityData);
|
|
entityData.define(DATA_UNHAPPY_COUNTER, 0);
|
|
}
|
|
|
|
@Override
|
|
public void setTradingPlayer(@Nullable Player player) {
|
|
this.tradingPlayer = player;
|
|
}
|
|
|
|
@Override
|
|
public @Nullable Player getTradingPlayer() {
|
|
return this.tradingPlayer;
|
|
}
|
|
|
|
public boolean isTrading() {
|
|
return this.tradingPlayer != null;
|
|
}
|
|
|
|
@Override
|
|
public MerchantOffers getOffers() {
|
|
Level level = this.level();
|
|
if (!(level instanceof ServerLevel)) {
|
|
throw new IllegalStateException("Cannot load Villager offers on the client");
|
|
}
|
|
ServerLevel serverLevel = (ServerLevel)level;
|
|
if (this.offers == null) {
|
|
this.offers = new MerchantOffers();
|
|
this.updateTrades(serverLevel);
|
|
}
|
|
return this.offers;
|
|
}
|
|
|
|
@Override
|
|
public void overrideOffers(@Nullable MerchantOffers offers) {
|
|
}
|
|
|
|
@Override
|
|
public void overrideXp(int xp) {
|
|
}
|
|
|
|
@Override
|
|
public void notifyTrade(MerchantOffer offer) {
|
|
offer.increaseUses();
|
|
this.ambientSoundTime = -this.getAmbientSoundInterval();
|
|
this.rewardTradeXp(offer);
|
|
if (this.tradingPlayer instanceof ServerPlayer) {
|
|
CriteriaTriggers.TRADE.trigger((ServerPlayer)this.tradingPlayer, this, offer.getResult());
|
|
}
|
|
}
|
|
|
|
protected abstract void rewardTradeXp(MerchantOffer var1);
|
|
|
|
@Override
|
|
public boolean showProgressBar() {
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public void notifyTradeUpdated(ItemStack itemStack) {
|
|
if (!this.level().isClientSide() && this.ambientSoundTime > -this.getAmbientSoundInterval() + 20) {
|
|
this.ambientSoundTime = -this.getAmbientSoundInterval();
|
|
this.makeSound(this.getTradeUpdatedSound(!itemStack.isEmpty()));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public SoundEvent getNotifyTradeSound() {
|
|
return SoundEvents.VILLAGER_YES;
|
|
}
|
|
|
|
protected SoundEvent getTradeUpdatedSound(boolean validTrade) {
|
|
return validTrade ? SoundEvents.VILLAGER_YES : SoundEvents.VILLAGER_NO;
|
|
}
|
|
|
|
public void playCelebrateSound() {
|
|
this.makeSound(SoundEvents.VILLAGER_CELEBRATE);
|
|
}
|
|
|
|
@Override
|
|
protected void addAdditionalSaveData(ValueOutput output) {
|
|
MerchantOffers offers;
|
|
super.addAdditionalSaveData(output);
|
|
if (!this.level().isClientSide() && !(offers = this.getOffers()).isEmpty()) {
|
|
output.store("Offers", MerchantOffers.CODEC, offers);
|
|
}
|
|
this.writeInventoryToTag(output);
|
|
}
|
|
|
|
@Override
|
|
protected void readAdditionalSaveData(ValueInput input) {
|
|
super.readAdditionalSaveData(input);
|
|
this.offers = input.read("Offers", MerchantOffers.CODEC).orElse(null);
|
|
this.readInventoryFromTag(input);
|
|
}
|
|
|
|
@Override
|
|
public @Nullable Entity teleport(TeleportTransition transition) {
|
|
this.stopTrading();
|
|
return super.teleport(transition);
|
|
}
|
|
|
|
protected void stopTrading() {
|
|
this.setTradingPlayer(null);
|
|
}
|
|
|
|
@Override
|
|
public void die(DamageSource source) {
|
|
super.die(source);
|
|
this.stopTrading();
|
|
}
|
|
|
|
protected void addParticlesAroundSelf(ParticleOptions particle) {
|
|
for (int i = 0; i < 5; ++i) {
|
|
double xa = this.random.nextGaussian() * 0.02;
|
|
double ya = this.random.nextGaussian() * 0.02;
|
|
double za = this.random.nextGaussian() * 0.02;
|
|
this.level().addParticle(particle, this.getRandomX(1.0), this.getRandomY() + 1.0, this.getRandomZ(1.0), xa, ya, za);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean canBeLeashed() {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public SimpleContainer getInventory() {
|
|
return this.inventory;
|
|
}
|
|
|
|
@Override
|
|
public @Nullable SlotAccess getSlot(int slot) {
|
|
int inventorySlot = slot - 300;
|
|
if (inventorySlot >= 0 && inventorySlot < this.inventory.getContainerSize()) {
|
|
return this.inventory.getSlot(inventorySlot);
|
|
}
|
|
return super.getSlot(slot);
|
|
}
|
|
|
|
protected abstract void updateTrades(ServerLevel var1);
|
|
|
|
protected void addOffersFromItemListings(ServerLevel level, MerchantOffers merchantOffers, VillagerTrades.ItemListing[] itemListings, int numberOfOffers) {
|
|
ArrayList potentialOffers = Lists.newArrayList((Object[])itemListings);
|
|
int offersFound = 0;
|
|
while (offersFound < numberOfOffers && !potentialOffers.isEmpty()) {
|
|
MerchantOffer offer = ((VillagerTrades.ItemListing)potentialOffers.remove(this.random.nextInt(potentialOffers.size()))).getOffer(level, this, this.random);
|
|
if (offer == null) continue;
|
|
merchantOffers.add(offer);
|
|
++offersFound;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Vec3 getRopeHoldPosition(float partialTickTime) {
|
|
float yRot = Mth.lerp(partialTickTime, this.yBodyRotO, this.yBodyRot) * ((float)Math.PI / 180);
|
|
Vec3 offset = new Vec3(0.0, this.getBoundingBox().getYsize() - 1.0, 0.2);
|
|
return this.getPosition(partialTickTime).add(offset.yRot(-yRot));
|
|
}
|
|
|
|
@Override
|
|
public boolean isClientSide() {
|
|
return this.level().isClientSide();
|
|
}
|
|
|
|
@Override
|
|
public boolean stillValid(Player player) {
|
|
return this.getTradingPlayer() == player && this.isAlive() && player.canInteractWithEntity(this, 4.0);
|
|
}
|
|
}
|
|
|