package net.minecraft.world.entity.animal; import java.util.Optional; import java.util.UUID; import net.minecraft.core.BlockPos; import net.minecraft.core.component.DataComponents; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtOps; 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.server.level.ServerLevel; import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.tags.BlockTags; import net.minecraft.tags.ItemTags; import net.minecraft.util.RandomSource; import net.minecraft.util.StringRepresentable; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.AgeableMob; import net.minecraft.world.entity.ConversionParams; import net.minecraft.world.entity.EntitySpawnReason; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.LightningBolt; import net.minecraft.world.entity.Shearable; import net.minecraft.world.entity.VariantHolder; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemUtils; import net.minecraft.world.item.Items; import net.minecraft.world.item.component.SuspiciousStewEffects; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.SuspiciousEffectHolder; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.gameevent.GameEvent; import net.minecraft.world.level.storage.loot.BuiltInLootTables; import org.jetbrains.annotations.Nullable; public class MushroomCow extends Cow implements Shearable, VariantHolder { private static final EntityDataAccessor DATA_TYPE = SynchedEntityData.defineId(MushroomCow.class, EntityDataSerializers.STRING); private static final int MUTATE_CHANCE = 1024; private static final String TAG_STEW_EFFECTS = "stew_effects"; @Nullable private SuspiciousStewEffects stewEffects; /** * Stores the UUID of the most recent lightning bolt to strike */ @Nullable private UUID lastLightningBoltUUID; public MushroomCow(EntityType entityType, Level level) { super(entityType, level); } @Override public float getWalkTargetValue(BlockPos pos, LevelReader level) { return level.getBlockState(pos.below()).is(Blocks.MYCELIUM) ? 10.0F : level.getPathfindingCostFromLightLevels(pos); } public static boolean checkMushroomSpawnRules( EntityType entityType, LevelAccessor levelAccessor, EntitySpawnReason entitySpawnReason, BlockPos blockPos, RandomSource randomSource ) { return levelAccessor.getBlockState(blockPos.below()).is(BlockTags.MOOSHROOMS_SPAWNABLE_ON) && isBrightEnoughToSpawn(levelAccessor, blockPos); } @Override public void thunderHit(ServerLevel level, LightningBolt lightning) { UUID uUID = lightning.getUUID(); if (!uUID.equals(this.lastLightningBoltUUID)) { this.setVariant(this.getVariant() == MushroomCow.Variant.RED ? MushroomCow.Variant.BROWN : MushroomCow.Variant.RED); this.lastLightningBoltUUID = uUID; this.playSound(SoundEvents.MOOSHROOM_CONVERT, 2.0F, 1.0F); } } @Override protected void defineSynchedData(Builder builder) { super.defineSynchedData(builder); builder.define(DATA_TYPE, MushroomCow.Variant.RED.type); } @Override public InteractionResult mobInteract(Player player, InteractionHand hand) { ItemStack itemStack = player.getItemInHand(hand); if (itemStack.is(Items.BOWL) && !this.isBaby()) { boolean bl = false; ItemStack itemStack2; if (this.stewEffects != null) { bl = true; itemStack2 = new ItemStack(Items.SUSPICIOUS_STEW); itemStack2.set(DataComponents.SUSPICIOUS_STEW_EFFECTS, this.stewEffects); this.stewEffects = null; } else { itemStack2 = new ItemStack(Items.MUSHROOM_STEW); } ItemStack itemStack3 = ItemUtils.createFilledResult(itemStack, player, itemStack2, false); player.setItemInHand(hand, itemStack3); SoundEvent soundEvent; if (bl) { soundEvent = SoundEvents.MOOSHROOM_MILK_SUSPICIOUSLY; } else { soundEvent = SoundEvents.MOOSHROOM_MILK; } this.playSound(soundEvent, 1.0F, 1.0F); return InteractionResult.SUCCESS; } else if (itemStack.is(Items.SHEARS) && this.readyForShearing()) { if (this.level() instanceof ServerLevel serverLevel) { this.shear(serverLevel, SoundSource.PLAYERS, itemStack); this.gameEvent(GameEvent.SHEAR, player); itemStack.hurtAndBreak(1, player, getSlotForHand(hand)); } return InteractionResult.SUCCESS; } else if (this.getVariant() == MushroomCow.Variant.BROWN && itemStack.is(ItemTags.SMALL_FLOWERS)) { if (this.stewEffects != null) { for (int i = 0; i < 2; i++) { this.level() .addParticle( ParticleTypes.SMOKE, this.getX() + this.random.nextDouble() / 2.0, this.getY(0.5), this.getZ() + this.random.nextDouble() / 2.0, 0.0, this.random.nextDouble() / 5.0, 0.0 ); } } else { Optional optional = this.getEffectsFromItemStack(itemStack); if (optional.isEmpty()) { return InteractionResult.PASS; } itemStack.consume(1, player); for (int j = 0; j < 4; j++) { this.level() .addParticle( ParticleTypes.EFFECT, this.getX() + this.random.nextDouble() / 2.0, this.getY(0.5), this.getZ() + this.random.nextDouble() / 2.0, 0.0, this.random.nextDouble() / 5.0, 0.0 ); } this.stewEffects = (SuspiciousStewEffects)optional.get(); this.playSound(SoundEvents.MOOSHROOM_EAT, 2.0F, 1.0F); } return InteractionResult.SUCCESS; } else { return super.mobInteract(player, hand); } } @Override public void shear(ServerLevel serverLevel, SoundSource soundSource, ItemStack itemStack) { serverLevel.playSound(null, this, SoundEvents.MOOSHROOM_SHEAR, soundSource, 1.0F, 1.0F); this.convertTo(EntityType.COW, ConversionParams.single(this, false, false), cow -> { serverLevel.sendParticles(ParticleTypes.EXPLOSION, this.getX(), this.getY(0.5), this.getZ(), 1, 0.0, 0.0, 0.0, 0.0); this.dropFromShearingLootTable(serverLevel, BuiltInLootTables.SHEAR_MOOSHROOM, itemStack, (serverLevelxx, itemStackxx) -> { for (int i = 0; i < itemStackxx.getCount(); i++) { serverLevelxx.addFreshEntity(new ItemEntity(this.level(), this.getX(), this.getY(1.0), this.getZ(), itemStackxx.copyWithCount(1))); } }); }); } @Override public boolean readyForShearing() { return this.isAlive() && !this.isBaby(); } @Override public void addAdditionalSaveData(CompoundTag compound) { super.addAdditionalSaveData(compound); compound.putString("Type", this.getVariant().getSerializedName()); if (this.stewEffects != null) { SuspiciousStewEffects.CODEC.encodeStart(NbtOps.INSTANCE, this.stewEffects).ifSuccess(tag -> compound.put("stew_effects", tag)); } } @Override public void readAdditionalSaveData(CompoundTag compound) { super.readAdditionalSaveData(compound); this.setVariant(MushroomCow.Variant.byName(compound.getString("Type"))); if (compound.contains("stew_effects", 9)) { SuspiciousStewEffects.CODEC .parse(NbtOps.INSTANCE, compound.get("stew_effects")) .ifSuccess(suspiciousStewEffects -> this.stewEffects = suspiciousStewEffects); } } private Optional getEffectsFromItemStack(ItemStack stack) { SuspiciousEffectHolder suspiciousEffectHolder = SuspiciousEffectHolder.tryGet(stack.getItem()); return suspiciousEffectHolder != null ? Optional.of(suspiciousEffectHolder.getSuspiciousEffects()) : Optional.empty(); } public void setVariant(MushroomCow.Variant variant) { this.entityData.set(DATA_TYPE, variant.type); } public MushroomCow.Variant getVariant() { return MushroomCow.Variant.byName(this.entityData.get(DATA_TYPE)); } @Nullable public MushroomCow getBreedOffspring(ServerLevel level, AgeableMob otherParent) { MushroomCow mushroomCow = EntityType.MOOSHROOM.create(level, EntitySpawnReason.BREEDING); if (mushroomCow != null) { mushroomCow.setVariant(this.getOffspringVariant((MushroomCow)otherParent)); } return mushroomCow; } private MushroomCow.Variant getOffspringVariant(MushroomCow mushroomCow) { MushroomCow.Variant variant = this.getVariant(); MushroomCow.Variant variant2 = mushroomCow.getVariant(); MushroomCow.Variant variant3; if (variant == variant2 && this.random.nextInt(1024) == 0) { variant3 = variant == MushroomCow.Variant.BROWN ? MushroomCow.Variant.RED : MushroomCow.Variant.BROWN; } else { variant3 = this.random.nextBoolean() ? variant : variant2; } return variant3; } public static enum Variant implements StringRepresentable { RED("red", Blocks.RED_MUSHROOM.defaultBlockState()), BROWN("brown", Blocks.BROWN_MUSHROOM.defaultBlockState()); public static final StringRepresentable.EnumCodec CODEC = StringRepresentable.fromEnum(MushroomCow.Variant::values); final String type; private final BlockState blockState; private Variant(final String string2, final BlockState blockState) { this.type = string2; this.blockState = blockState; } public BlockState getBlockState() { return this.blockState; } @Override public String getSerializedName() { return this.type; } static MushroomCow.Variant byName(String string) { return (MushroomCow.Variant)CODEC.byName(string, RED); } } }