481 lines
		
	
	
	
		
			17 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			481 lines
		
	
	
	
		
			17 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| package net.minecraft.world.entity.monster.piglin;
 | |
| 
 | |
| import com.google.common.collect.ImmutableList;
 | |
| import com.mojang.serialization.Dynamic;
 | |
| import java.util.List;
 | |
| import net.minecraft.core.BlockPos;
 | |
| import net.minecraft.network.syncher.EntityDataAccessor;
 | |
| import net.minecraft.network.syncher.EntityDataSerializers;
 | |
| import net.minecraft.network.syncher.SynchedEntityData;
 | |
| import net.minecraft.network.syncher.SynchedEntityData.Builder;
 | |
| import net.minecraft.resources.ResourceLocation;
 | |
| import net.minecraft.server.level.ServerLevel;
 | |
| import net.minecraft.sounds.SoundEvent;
 | |
| import net.minecraft.sounds.SoundEvents;
 | |
| import net.minecraft.tags.ItemTags;
 | |
| import net.minecraft.tags.TagKey;
 | |
| import net.minecraft.util.RandomSource;
 | |
| import net.minecraft.util.VisibleForDebug;
 | |
| import net.minecraft.util.profiling.Profiler;
 | |
| import net.minecraft.util.profiling.ProfilerFiller;
 | |
| import net.minecraft.world.DifficultyInstance;
 | |
| import net.minecraft.world.InteractionHand;
 | |
| import net.minecraft.world.InteractionResult;
 | |
| import net.minecraft.world.SimpleContainer;
 | |
| import net.minecraft.world.damagesource.DamageSource;
 | |
| import net.minecraft.world.entity.Entity;
 | |
| import net.minecraft.world.entity.EntityDimensions;
 | |
| import net.minecraft.world.entity.EntitySpawnReason;
 | |
| import net.minecraft.world.entity.EntityType;
 | |
| import net.minecraft.world.entity.EquipmentSlot;
 | |
| import net.minecraft.world.entity.LivingEntity;
 | |
| import net.minecraft.world.entity.Pose;
 | |
| import net.minecraft.world.entity.SpawnGroupData;
 | |
| import net.minecraft.world.entity.ai.Brain;
 | |
| import net.minecraft.world.entity.ai.Brain.Provider;
 | |
| import net.minecraft.world.entity.ai.attributes.AttributeInstance;
 | |
| import net.minecraft.world.entity.ai.attributes.AttributeModifier;
 | |
| import net.minecraft.world.entity.ai.attributes.Attributes;
 | |
| import net.minecraft.world.entity.ai.attributes.AttributeModifier.Operation;
 | |
| import net.minecraft.world.entity.ai.memory.MemoryModuleType;
 | |
| import net.minecraft.world.entity.ai.sensing.Sensor;
 | |
| import net.minecraft.world.entity.ai.sensing.SensorType;
 | |
| import net.minecraft.world.entity.item.ItemEntity;
 | |
| import net.minecraft.world.entity.monster.Creeper;
 | |
| import net.minecraft.world.entity.monster.CrossbowAttackMob;
 | |
| import net.minecraft.world.entity.monster.Monster;
 | |
| import net.minecraft.world.entity.npc.InventoryCarrier;
 | |
| import net.minecraft.world.entity.player.Player;
 | |
| import net.minecraft.world.item.CrossbowItem;
 | |
| import net.minecraft.world.item.Item;
 | |
| import net.minecraft.world.item.ItemStack;
 | |
| import net.minecraft.world.item.Items;
 | |
| import net.minecraft.world.item.ProjectileWeaponItem;
 | |
| import net.minecraft.world.item.enchantment.EnchantmentEffectComponents;
 | |
| import net.minecraft.world.item.enchantment.EnchantmentHelper;
 | |
| import net.minecraft.world.level.GameRules;
 | |
| import net.minecraft.world.level.Level;
 | |
| import net.minecraft.world.level.LevelAccessor;
 | |
| import net.minecraft.world.level.ServerLevelAccessor;
 | |
| import net.minecraft.world.level.block.Blocks;
 | |
| import net.minecraft.world.level.block.state.BlockState;
 | |
| import net.minecraft.world.level.storage.ValueInput;
 | |
| import net.minecraft.world.level.storage.ValueOutput;
 | |
| import org.jetbrains.annotations.Nullable;
 | |
| 
 | |
| public class Piglin extends AbstractPiglin implements CrossbowAttackMob, InventoryCarrier {
 | |
| 	private static final EntityDataAccessor<Boolean> DATA_BABY_ID = SynchedEntityData.defineId(Piglin.class, EntityDataSerializers.BOOLEAN);
 | |
| 	private static final EntityDataAccessor<Boolean> DATA_IS_CHARGING_CROSSBOW = SynchedEntityData.defineId(Piglin.class, EntityDataSerializers.BOOLEAN);
 | |
| 	private static final EntityDataAccessor<Boolean> DATA_IS_DANCING = SynchedEntityData.defineId(Piglin.class, EntityDataSerializers.BOOLEAN);
 | |
| 	private static final ResourceLocation SPEED_MODIFIER_BABY_ID = ResourceLocation.withDefaultNamespace("baby");
 | |
| 	private static final AttributeModifier SPEED_MODIFIER_BABY = new AttributeModifier(SPEED_MODIFIER_BABY_ID, 0.2F, Operation.ADD_MULTIPLIED_BASE);
 | |
| 	private static final int MAX_HEALTH = 16;
 | |
| 	private static final float MOVEMENT_SPEED_WHEN_FIGHTING = 0.35F;
 | |
| 	private static final int ATTACK_DAMAGE = 5;
 | |
| 	private static final float CHANCE_OF_WEARING_EACH_ARMOUR_ITEM = 0.1F;
 | |
| 	private static final int MAX_PASSENGERS_ON_ONE_HOGLIN = 3;
 | |
| 	private static final float PROBABILITY_OF_SPAWNING_AS_BABY = 0.2F;
 | |
| 	private static final EntityDimensions BABY_DIMENSIONS = EntityType.PIGLIN.getDimensions().scale(0.5F).withEyeHeight(0.97F);
 | |
| 	private static final double PROBABILITY_OF_SPAWNING_WITH_CROSSBOW_INSTEAD_OF_SWORD = 0.5;
 | |
| 	private static final boolean DEFAULT_IS_BABY = false;
 | |
| 	private static final boolean DEFAULT_CANNOT_HUNT = false;
 | |
| 	private final SimpleContainer inventory = new SimpleContainer(8);
 | |
| 	private boolean cannotHunt = false;
 | |
| 	protected static final ImmutableList<SensorType<? extends Sensor<? super Piglin>>> SENSOR_TYPES = ImmutableList.of(
 | |
| 		SensorType.NEAREST_LIVING_ENTITIES, SensorType.NEAREST_PLAYERS, SensorType.NEAREST_ITEMS, SensorType.HURT_BY, SensorType.PIGLIN_SPECIFIC_SENSOR
 | |
| 	);
 | |
| 	protected static final ImmutableList<MemoryModuleType<?>> MEMORY_TYPES = ImmutableList.of(
 | |
| 		MemoryModuleType.LOOK_TARGET,
 | |
| 		MemoryModuleType.DOORS_TO_CLOSE,
 | |
| 		MemoryModuleType.NEAREST_LIVING_ENTITIES,
 | |
| 		MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES,
 | |
| 		MemoryModuleType.NEAREST_VISIBLE_PLAYER,
 | |
| 		MemoryModuleType.NEAREST_VISIBLE_ATTACKABLE_PLAYER,
 | |
| 		MemoryModuleType.NEAREST_VISIBLE_ADULT_PIGLINS,
 | |
| 		MemoryModuleType.NEARBY_ADULT_PIGLINS,
 | |
| 		MemoryModuleType.NEAREST_VISIBLE_WANTED_ITEM,
 | |
| 		MemoryModuleType.ITEM_PICKUP_COOLDOWN_TICKS,
 | |
| 		MemoryModuleType.HURT_BY,
 | |
| 		MemoryModuleType.HURT_BY_ENTITY,
 | |
| 		MemoryModuleType.WALK_TARGET,
 | |
| 		MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE,
 | |
| 		MemoryModuleType.ATTACK_TARGET,
 | |
| 		MemoryModuleType.ATTACK_COOLING_DOWN,
 | |
| 		MemoryModuleType.INTERACTION_TARGET,
 | |
| 		MemoryModuleType.PATH,
 | |
| 		MemoryModuleType.ANGRY_AT,
 | |
| 		MemoryModuleType.UNIVERSAL_ANGER,
 | |
| 		MemoryModuleType.AVOID_TARGET,
 | |
| 		MemoryModuleType.ADMIRING_ITEM,
 | |
| 		MemoryModuleType.TIME_TRYING_TO_REACH_ADMIRE_ITEM,
 | |
| 		MemoryModuleType.ADMIRING_DISABLED,
 | |
| 		MemoryModuleType.DISABLE_WALK_TO_ADMIRE_ITEM,
 | |
| 		MemoryModuleType.CELEBRATE_LOCATION,
 | |
| 		MemoryModuleType.DANCING,
 | |
| 		MemoryModuleType.HUNTED_RECENTLY,
 | |
| 		MemoryModuleType.NEAREST_VISIBLE_BABY_HOGLIN,
 | |
| 		MemoryModuleType.NEAREST_VISIBLE_NEMESIS,
 | |
| 		MemoryModuleType.NEAREST_VISIBLE_ZOMBIFIED,
 | |
| 		MemoryModuleType.RIDE_TARGET,
 | |
| 		MemoryModuleType.VISIBLE_ADULT_PIGLIN_COUNT,
 | |
| 		MemoryModuleType.VISIBLE_ADULT_HOGLIN_COUNT,
 | |
| 		MemoryModuleType.NEAREST_VISIBLE_HUNTABLE_HOGLIN,
 | |
| 		MemoryModuleType.NEAREST_TARGETABLE_PLAYER_NOT_WEARING_GOLD,
 | |
| 		MemoryModuleType.NEAREST_PLAYER_HOLDING_WANTED_ITEM,
 | |
| 		MemoryModuleType.ATE_RECENTLY,
 | |
| 		MemoryModuleType.NEAREST_REPELLENT
 | |
| 	);
 | |
| 
 | |
| 	public Piglin(EntityType<? extends AbstractPiglin> entityType, Level level) {
 | |
| 		super(entityType, level);
 | |
| 		this.xpReward = 5;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected void addAdditionalSaveData(ValueOutput output) {
 | |
| 		super.addAdditionalSaveData(output);
 | |
| 		output.putBoolean("IsBaby", this.isBaby());
 | |
| 		output.putBoolean("CannotHunt", this.cannotHunt);
 | |
| 		this.writeInventoryToTag(output);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected void readAdditionalSaveData(ValueInput input) {
 | |
| 		super.readAdditionalSaveData(input);
 | |
| 		this.setBaby(input.getBooleanOr("IsBaby", false));
 | |
| 		this.setCannotHunt(input.getBooleanOr("CannotHunt", false));
 | |
| 		this.readInventoryFromTag(input);
 | |
| 	}
 | |
| 
 | |
| 	@VisibleForDebug
 | |
| 	@Override
 | |
| 	public SimpleContainer getInventory() {
 | |
| 		return this.inventory;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected void dropCustomDeathLoot(ServerLevel level, DamageSource damageSource, boolean recentlyHit) {
 | |
| 		super.dropCustomDeathLoot(level, damageSource, recentlyHit);
 | |
| 		if (damageSource.getEntity() instanceof Creeper creeper && creeper.canDropMobsSkull()) {
 | |
| 			ItemStack itemStack = new ItemStack(Items.PIGLIN_HEAD);
 | |
| 			creeper.increaseDroppedSkulls();
 | |
| 			this.spawnAtLocation(level, itemStack);
 | |
| 		}
 | |
| 
 | |
| 		this.inventory.removeAllItems().forEach(itemStackx -> this.spawnAtLocation(level, itemStackx));
 | |
| 	}
 | |
| 
 | |
| 	protected ItemStack addToInventory(ItemStack stack) {
 | |
| 		return this.inventory.addItem(stack);
 | |
| 	}
 | |
| 
 | |
| 	protected boolean canAddToInventory(ItemStack stack) {
 | |
| 		return this.inventory.canAddItem(stack);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected void defineSynchedData(Builder builder) {
 | |
| 		super.defineSynchedData(builder);
 | |
| 		builder.define(DATA_BABY_ID, false);
 | |
| 		builder.define(DATA_IS_CHARGING_CROSSBOW, false);
 | |
| 		builder.define(DATA_IS_DANCING, false);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void onSyncedDataUpdated(EntityDataAccessor<?> dataAccessor) {
 | |
| 		super.onSyncedDataUpdated(dataAccessor);
 | |
| 		if (DATA_BABY_ID.equals(dataAccessor)) {
 | |
| 			this.refreshDimensions();
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public static net.minecraft.world.entity.ai.attributes.AttributeSupplier.Builder createAttributes() {
 | |
| 		return Monster.createMonsterAttributes().add(Attributes.MAX_HEALTH, 16.0).add(Attributes.MOVEMENT_SPEED, 0.35F).add(Attributes.ATTACK_DAMAGE, 5.0);
 | |
| 	}
 | |
| 
 | |
| 	public static boolean checkPiglinSpawnRules(
 | |
| 		EntityType<Piglin> entityType, LevelAccessor level, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random
 | |
| 	) {
 | |
| 		return !level.getBlockState(pos.below()).is(Blocks.NETHER_WART_BLOCK);
 | |
| 	}
 | |
| 
 | |
| 	@Nullable
 | |
| 	@Override
 | |
| 	public SpawnGroupData finalizeSpawn(
 | |
| 		ServerLevelAccessor level, DifficultyInstance difficulty, EntitySpawnReason spawnReason, @Nullable SpawnGroupData spawnGroupData
 | |
| 	) {
 | |
| 		RandomSource randomSource = level.getRandom();
 | |
| 		if (spawnReason != EntitySpawnReason.STRUCTURE) {
 | |
| 			if (randomSource.nextFloat() < 0.2F) {
 | |
| 				this.setBaby(true);
 | |
| 			} else if (this.isAdult()) {
 | |
| 				this.setItemSlot(EquipmentSlot.MAINHAND, this.createSpawnWeapon());
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		PiglinAi.initMemories(this, level.getRandom());
 | |
| 		this.populateDefaultEquipmentSlots(randomSource, difficulty);
 | |
| 		this.populateDefaultEquipmentEnchantments(level, randomSource, difficulty);
 | |
| 		return super.finalizeSpawn(level, difficulty, spawnReason, spawnGroupData);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected boolean shouldDespawnInPeaceful() {
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public boolean removeWhenFarAway(double distanceToClosestPlayer) {
 | |
| 		return !this.isPersistenceRequired();
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected void populateDefaultEquipmentSlots(RandomSource random, DifficultyInstance difficulty) {
 | |
| 		if (this.isAdult()) {
 | |
| 			this.maybeWearArmor(EquipmentSlot.HEAD, new ItemStack(Items.GOLDEN_HELMET), random);
 | |
| 			this.maybeWearArmor(EquipmentSlot.CHEST, new ItemStack(Items.GOLDEN_CHESTPLATE), random);
 | |
| 			this.maybeWearArmor(EquipmentSlot.LEGS, new ItemStack(Items.GOLDEN_LEGGINGS), random);
 | |
| 			this.maybeWearArmor(EquipmentSlot.FEET, new ItemStack(Items.GOLDEN_BOOTS), random);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	private void maybeWearArmor(EquipmentSlot slot, ItemStack stack, RandomSource random) {
 | |
| 		if (random.nextFloat() < 0.1F) {
 | |
| 			this.setItemSlot(slot, stack);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected Provider<Piglin> brainProvider() {
 | |
| 		return Brain.provider(MEMORY_TYPES, SENSOR_TYPES);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected Brain<?> makeBrain(Dynamic<?> dynamic) {
 | |
| 		return PiglinAi.makeBrain(this, this.brainProvider().makeBrain(dynamic));
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public Brain<Piglin> getBrain() {
 | |
| 		return (Brain<Piglin>)super.getBrain();
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public InteractionResult mobInteract(Player player, InteractionHand hand) {
 | |
| 		InteractionResult interactionResult = super.mobInteract(player, hand);
 | |
| 		if (interactionResult.consumesAction()) {
 | |
| 			return interactionResult;
 | |
| 		} else if (this.level() instanceof ServerLevel serverLevel) {
 | |
| 			return PiglinAi.mobInteract(serverLevel, this, player, hand);
 | |
| 		} else {
 | |
| 			boolean bl = PiglinAi.canAdmire(this, player.getItemInHand(hand)) && this.getArmPose() != PiglinArmPose.ADMIRING_ITEM;
 | |
| 			return (InteractionResult)(bl ? InteractionResult.SUCCESS : InteractionResult.PASS);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public EntityDimensions getDefaultDimensions(Pose pose) {
 | |
| 		return this.isBaby() ? BABY_DIMENSIONS : super.getDefaultDimensions(pose);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void setBaby(boolean baby) {
 | |
| 		this.getEntityData().set(DATA_BABY_ID, baby);
 | |
| 		if (!this.level().isClientSide) {
 | |
| 			AttributeInstance attributeInstance = this.getAttribute(Attributes.MOVEMENT_SPEED);
 | |
| 			attributeInstance.removeModifier(SPEED_MODIFIER_BABY.id());
 | |
| 			if (baby) {
 | |
| 				attributeInstance.addTransientModifier(SPEED_MODIFIER_BABY);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public boolean isBaby() {
 | |
| 		return this.getEntityData().get(DATA_BABY_ID);
 | |
| 	}
 | |
| 
 | |
| 	private void setCannotHunt(boolean cannotHunt) {
 | |
| 		this.cannotHunt = cannotHunt;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected boolean canHunt() {
 | |
| 		return !this.cannotHunt;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected void customServerAiStep(ServerLevel level) {
 | |
| 		ProfilerFiller profilerFiller = Profiler.get();
 | |
| 		profilerFiller.push("piglinBrain");
 | |
| 		this.getBrain().tick(level, this);
 | |
| 		profilerFiller.pop();
 | |
| 		PiglinAi.updateActivity(this);
 | |
| 		super.customServerAiStep(level);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected int getBaseExperienceReward(ServerLevel level) {
 | |
| 		return this.xpReward;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected void finishConversion(ServerLevel serverLevel) {
 | |
| 		PiglinAi.cancelAdmiring(serverLevel, this);
 | |
| 		this.inventory.removeAllItems().forEach(itemStack -> this.spawnAtLocation(serverLevel, itemStack));
 | |
| 		super.finishConversion(serverLevel);
 | |
| 	}
 | |
| 
 | |
| 	private ItemStack createSpawnWeapon() {
 | |
| 		return this.random.nextFloat() < 0.5 ? new ItemStack(Items.CROSSBOW) : new ItemStack(Items.GOLDEN_SWORD);
 | |
| 	}
 | |
| 
 | |
| 	@Nullable
 | |
| 	@Override
 | |
| 	public TagKey<Item> getPreferredWeaponType() {
 | |
| 		return this.isBaby() ? null : ItemTags.PIGLIN_PREFERRED_WEAPONS;
 | |
| 	}
 | |
| 
 | |
| 	private boolean isChargingCrossbow() {
 | |
| 		return this.entityData.get(DATA_IS_CHARGING_CROSSBOW);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void setChargingCrossbow(boolean chargingCrossbow) {
 | |
| 		this.entityData.set(DATA_IS_CHARGING_CROSSBOW, chargingCrossbow);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void onCrossbowAttackPerformed() {
 | |
| 		this.noActionTime = 0;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public PiglinArmPose getArmPose() {
 | |
| 		if (this.isDancing()) {
 | |
| 			return PiglinArmPose.DANCING;
 | |
| 		} else if (PiglinAi.isLovedItem(this.getOffhandItem())) {
 | |
| 			return PiglinArmPose.ADMIRING_ITEM;
 | |
| 		} else if (this.isAggressive() && this.isHoldingMeleeWeapon()) {
 | |
| 			return PiglinArmPose.ATTACKING_WITH_MELEE_WEAPON;
 | |
| 		} else if (this.isChargingCrossbow()) {
 | |
| 			return PiglinArmPose.CROSSBOW_CHARGE;
 | |
| 		} else {
 | |
| 			return this.isHolding(Items.CROSSBOW) && CrossbowItem.isCharged(this.getWeaponItem()) ? PiglinArmPose.CROSSBOW_HOLD : PiglinArmPose.DEFAULT;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public boolean isDancing() {
 | |
| 		return this.entityData.get(DATA_IS_DANCING);
 | |
| 	}
 | |
| 
 | |
| 	public void setDancing(boolean dancing) {
 | |
| 		this.entityData.set(DATA_IS_DANCING, dancing);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public boolean hurtServer(ServerLevel level, DamageSource damageSource, float amount) {
 | |
| 		boolean bl = super.hurtServer(level, damageSource, amount);
 | |
| 		if (bl && damageSource.getEntity() instanceof LivingEntity livingEntity) {
 | |
| 			PiglinAi.wasHurtBy(level, this, livingEntity);
 | |
| 		}
 | |
| 
 | |
| 		return bl;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void performRangedAttack(LivingEntity target, float velocity) {
 | |
| 		this.performCrossbowAttack(this, 1.6F);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public boolean canFireProjectileWeapon(ProjectileWeaponItem projectileWeapon) {
 | |
| 		return projectileWeapon == Items.CROSSBOW;
 | |
| 	}
 | |
| 
 | |
| 	protected void holdInMainHand(ItemStack stack) {
 | |
| 		this.setItemSlotAndDropWhenKilled(EquipmentSlot.MAINHAND, stack);
 | |
| 	}
 | |
| 
 | |
| 	protected void holdInOffHand(ItemStack stack) {
 | |
| 		if (stack.is(PiglinAi.BARTERING_ITEM)) {
 | |
| 			this.setItemSlot(EquipmentSlot.OFFHAND, stack);
 | |
| 			this.setGuaranteedDrop(EquipmentSlot.OFFHAND);
 | |
| 		} else {
 | |
| 			this.setItemSlotAndDropWhenKilled(EquipmentSlot.OFFHAND, stack);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public boolean wantsToPickUp(ServerLevel level, ItemStack stack) {
 | |
| 		return level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) && this.canPickUpLoot() && PiglinAi.wantsToPickup(this, stack);
 | |
| 	}
 | |
| 
 | |
| 	protected boolean canReplaceCurrentItem(ItemStack candidate) {
 | |
| 		EquipmentSlot equipmentSlot = this.getEquipmentSlotForItem(candidate);
 | |
| 		ItemStack itemStack = this.getItemBySlot(equipmentSlot);
 | |
| 		return this.canReplaceCurrentItem(candidate, itemStack, equipmentSlot);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected boolean canReplaceCurrentItem(ItemStack newItem, ItemStack currentItem, EquipmentSlot slot) {
 | |
| 		if (EnchantmentHelper.has(currentItem, EnchantmentEffectComponents.PREVENT_ARMOR_CHANGE)) {
 | |
| 			return false;
 | |
| 		} else {
 | |
| 			TagKey<Item> tagKey = this.getPreferredWeaponType();
 | |
| 			boolean bl = PiglinAi.isLovedItem(newItem) || tagKey != null && newItem.is(tagKey);
 | |
| 			boolean bl2 = PiglinAi.isLovedItem(currentItem) || tagKey != null && currentItem.is(tagKey);
 | |
| 			if (bl && !bl2) {
 | |
| 				return true;
 | |
| 			} else {
 | |
| 				return !bl && bl2 ? false : super.canReplaceCurrentItem(newItem, currentItem, slot);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected void pickUpItem(ServerLevel level, ItemEntity entity) {
 | |
| 		this.onItemPickup(entity);
 | |
| 		PiglinAi.pickUpItem(level, this, entity);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public boolean startRiding(Entity vehicle, boolean force) {
 | |
| 		if (this.isBaby() && vehicle.getType() == EntityType.HOGLIN) {
 | |
| 			vehicle = this.getTopPassenger(vehicle, 3);
 | |
| 		}
 | |
| 
 | |
| 		return super.startRiding(vehicle, force);
 | |
| 	}
 | |
| 
 | |
| 	private Entity getTopPassenger(Entity vehicle, int maxPosition) {
 | |
| 		List<Entity> list = vehicle.getPassengers();
 | |
| 		return maxPosition != 1 && !list.isEmpty() ? this.getTopPassenger((Entity)list.getFirst(), maxPosition - 1) : vehicle;
 | |
| 	}
 | |
| 
 | |
| 	@Nullable
 | |
| 	@Override
 | |
| 	protected SoundEvent getAmbientSound() {
 | |
| 		return this.level().isClientSide ? null : (SoundEvent)PiglinAi.getSoundForCurrentActivity(this).orElse(null);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected SoundEvent getHurtSound(DamageSource damageSource) {
 | |
| 		return SoundEvents.PIGLIN_HURT;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected SoundEvent getDeathSound() {
 | |
| 		return SoundEvents.PIGLIN_DEATH;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected void playStepSound(BlockPos pos, BlockState state) {
 | |
| 		this.playSound(SoundEvents.PIGLIN_STEP, 0.15F, 1.0F);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected void playConvertedSound() {
 | |
| 		this.makeSound(SoundEvents.PIGLIN_CONVERTED_TO_ZOMBIFIED);
 | |
| 	}
 | |
| }
 |