package net.minecraft.world.entity.animal; import com.mojang.serialization.Codec; import io.netty.buffer.ByteBuf; import java.util.Optional; import java.util.UUID; import java.util.function.IntFunction; import net.minecraft.core.BlockPos; import net.minecraft.core.component.DataComponentGetter; import net.minecraft.core.component.DataComponentType; import net.minecraft.core.component.DataComponents; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; 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.util.ByIdMap; import net.minecraft.util.RandomSource; import net.minecraft.util.StringRepresentable; import net.minecraft.util.ByIdMap.OutOfBoundsStrategy; 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.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 AbstractCow implements Shearable { private static final EntityDataAccessor DATA_TYPE = SynchedEntityData.defineId(MushroomCow.class, EntityDataSerializers.INT); 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 level, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random ) { return level.getBlockState(pos.below()).is(BlockTags.MOOSHROOMS_SPAWNABLE_ON) && isBrightEnoughToSpawn(level, pos); } @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.DEFAULT.id); } @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) { Optional optional = this.getEffectsFromItemStack(itemStack); if (optional.isEmpty()) { return super.mobInteract(player, hand); } else { 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 { itemStack.consume(1, player); for (int i = 0; i < 4; i++) { 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 level, SoundSource soundSource, ItemStack shears) { level.playSound(null, this, SoundEvents.MOOSHROOM_SHEAR, soundSource, 1.0F, 1.0F); this.convertTo(EntityType.COW, ConversionParams.single(this, false, false), cow -> { level.sendParticles(ParticleTypes.EXPLOSION, this.getX(), this.getY(0.5), this.getZ(), 1, 0.0, 0.0, 0.0, 0.0); this.dropFromShearingLootTable(level, BuiltInLootTables.SHEAR_MOOSHROOM, shears, (serverLevelx, itemStackx) -> { for (int i = 0; i < itemStackx.getCount(); i++) { serverLevelx.addFreshEntity(new ItemEntity(this.level(), this.getX(), this.getY(1.0), this.getZ(), itemStackx.copyWithCount(1))); } }); }); } @Override public boolean readyForShearing() { return this.isAlive() && !this.isBaby(); } @Override public void addAdditionalSaveData(CompoundTag tag) { super.addAdditionalSaveData(tag); tag.store("Type", MushroomCow.Variant.CODEC, this.getVariant()); tag.storeNullable("stew_effects", SuspiciousStewEffects.CODEC, this.stewEffects); } @Override public void readAdditionalSaveData(CompoundTag tag) { super.readAdditionalSaveData(tag); this.setVariant((MushroomCow.Variant)tag.read("Type", MushroomCow.Variant.CODEC).orElse(MushroomCow.Variant.DEFAULT)); this.stewEffects = (SuspiciousStewEffects)tag.read("stew_effects", SuspiciousStewEffects.CODEC).orElse(null); } private Optional getEffectsFromItemStack(ItemStack stack) { SuspiciousEffectHolder suspiciousEffectHolder = SuspiciousEffectHolder.tryGet(stack.getItem()); return suspiciousEffectHolder != null ? Optional.of(suspiciousEffectHolder.getSuspiciousEffects()) : Optional.empty(); } private void setVariant(MushroomCow.Variant variant) { this.entityData.set(DATA_TYPE, variant.id); } public MushroomCow.Variant getVariant() { return MushroomCow.Variant.byId(this.entityData.get(DATA_TYPE)); } @Nullable @Override public T get(DataComponentType component) { return component == DataComponents.MOOSHROOM_VARIANT ? castComponentValue((DataComponentType)component, this.getVariant()) : super.get(component); } @Override protected void applyImplicitComponents(DataComponentGetter componentGetter) { this.applyImplicitComponentIfPresent(componentGetter, DataComponents.MOOSHROOM_VARIANT); super.applyImplicitComponents(componentGetter); } @Override protected boolean applyImplicitComponent(DataComponentType component, T value) { if (component == DataComponents.MOOSHROOM_VARIANT) { this.setVariant(castComponentValue(DataComponents.MOOSHROOM_VARIANT, value)); return true; } else { return super.applyImplicitComponent(component, value); } } @Nullable public MushroomCow getBreedOffspring(ServerLevel serverLevel, AgeableMob ageableMob) { MushroomCow mushroomCow = EntityType.MOOSHROOM.create(serverLevel, EntitySpawnReason.BREEDING); if (mushroomCow != null) { mushroomCow.setVariant(this.getOffspringVariant((MushroomCow)ageableMob)); } return mushroomCow; } private MushroomCow.Variant getOffspringVariant(MushroomCow partner) { MushroomCow.Variant variant = this.getVariant(); MushroomCow.Variant variant2 = partner.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", 0, Blocks.RED_MUSHROOM.defaultBlockState()), BROWN("brown", 1, Blocks.BROWN_MUSHROOM.defaultBlockState()); public static final MushroomCow.Variant DEFAULT = RED; public static final Codec CODEC = StringRepresentable.fromEnum(MushroomCow.Variant::values); private static final IntFunction BY_ID = ByIdMap.continuous(MushroomCow.Variant::id, values(), OutOfBoundsStrategy.CLAMP); public static final StreamCodec STREAM_CODEC = ByteBufCodecs.idMapper(BY_ID, MushroomCow.Variant::id); private final String type; final int id; private final BlockState blockState; private Variant(final String type, final int id, final BlockState blockState) { this.type = type; this.id = id; this.blockState = blockState; } public BlockState getBlockState() { return this.blockState; } @Override public String getSerializedName() { return this.type; } private int id() { return this.id; } static MushroomCow.Variant byId(int id) { return (MushroomCow.Variant)BY_ID.apply(id); } } }