package net.minecraft.world.entity.animal.frog; import com.google.common.collect.ImmutableList; import com.mojang.serialization.Dynamic; import java.util.Objects; import java.util.Optional; import java.util.OptionalInt; import javax.annotation.Nullable; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; import net.minecraft.core.Registry; import net.minecraft.core.component.DataComponentGetter; import net.minecraft.core.component.DataComponentType; import net.minecraft.core.component.DataComponents; import net.minecraft.core.registries.Registries; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.protocol.game.DebugPackets; import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.EntityDataSerializers; import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.resources.ResourceKey; 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.EntityTypeTags; import net.minecraft.tags.ItemTags; import net.minecraft.util.Mth; import net.minecraft.util.RandomSource; import net.minecraft.util.Unit; import net.minecraft.util.profiling.Profiler; import net.minecraft.util.profiling.ProfilerFiller; import net.minecraft.world.DifficultyInstance; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.AgeableMob; import net.minecraft.world.entity.AnimationState; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntitySpawnReason; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.MoverType; 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.attributes.AttributeSupplier; import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.ai.control.LookControl; import net.minecraft.world.entity.ai.control.SmoothSwimmingMoveControl; import net.minecraft.world.entity.ai.memory.MemoryModuleType; import net.minecraft.world.entity.ai.navigation.AmphibiousPathNavigation; import net.minecraft.world.entity.ai.navigation.PathNavigation; import net.minecraft.world.entity.ai.sensing.Sensor; import net.minecraft.world.entity.ai.sensing.SensorType; import net.minecraft.world.entity.animal.Animal; import net.minecraft.world.entity.monster.Slime; import net.minecraft.world.entity.variant.SpawnContext; import net.minecraft.world.entity.variant.VariantUtils; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.ServerLevelAccessor; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.pathfinder.AmphibiousNodeEvaluator; import net.minecraft.world.level.pathfinder.Node; import net.minecraft.world.level.pathfinder.PathFinder; import net.minecraft.world.level.pathfinder.PathType; import net.minecraft.world.level.pathfinder.PathfindingContext; import net.minecraft.world.phys.Vec3; public class Frog extends Animal { protected static final ImmutableList>> SENSOR_TYPES = ImmutableList.of( SensorType.NEAREST_LIVING_ENTITIES, SensorType.HURT_BY, SensorType.FROG_ATTACKABLES, SensorType.FROG_TEMPTATIONS, SensorType.IS_IN_WATER ); protected static final ImmutableList> MEMORY_TYPES = ImmutableList.of( MemoryModuleType.LOOK_TARGET, MemoryModuleType.NEAREST_LIVING_ENTITIES, MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES, MemoryModuleType.WALK_TARGET, MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE, MemoryModuleType.PATH, MemoryModuleType.BREED_TARGET, MemoryModuleType.LONG_JUMP_COOLDOWN_TICKS, MemoryModuleType.LONG_JUMP_MID_JUMP, MemoryModuleType.ATTACK_TARGET, MemoryModuleType.TEMPTING_PLAYER, MemoryModuleType.TEMPTATION_COOLDOWN_TICKS, MemoryModuleType.IS_TEMPTED, MemoryModuleType.HURT_BY, MemoryModuleType.HURT_BY_ENTITY, MemoryModuleType.NEAREST_ATTACKABLE, MemoryModuleType.IS_IN_WATER, MemoryModuleType.IS_PREGNANT, MemoryModuleType.IS_PANICKING, MemoryModuleType.UNREACHABLE_TONGUE_TARGETS ); private static final EntityDataAccessor> DATA_VARIANT_ID = SynchedEntityData.defineId(Frog.class, EntityDataSerializers.FROG_VARIANT); private static final EntityDataAccessor DATA_TONGUE_TARGET_ID = SynchedEntityData.defineId(Frog.class, EntityDataSerializers.OPTIONAL_UNSIGNED_INT); private static final int FROG_FALL_DAMAGE_REDUCTION = 5; private static final ResourceKey DEFAULT_VARIANT = FrogVariants.TEMPERATE; public final AnimationState jumpAnimationState = new AnimationState(); public final AnimationState croakAnimationState = new AnimationState(); public final AnimationState tongueAnimationState = new AnimationState(); public final AnimationState swimIdleAnimationState = new AnimationState(); public Frog(EntityType p_218470_, Level p_218471_) { super(p_218470_, p_218471_); this.lookControl = new Frog.FrogLookControl(this); this.setPathfindingMalus(PathType.WATER, 4.0F); this.setPathfindingMalus(PathType.TRAPDOOR, -1.0F); this.moveControl = new SmoothSwimmingMoveControl(this, 85, 10, 0.02F, 0.1F, true); } @Override protected Brain.Provider brainProvider() { return Brain.provider(MEMORY_TYPES, SENSOR_TYPES); } @Override protected Brain makeBrain(Dynamic p_218494_) { return FrogAi.makeBrain(this.brainProvider().makeBrain(p_218494_)); } @Override public Brain getBrain() { return (Brain)super.getBrain(); } @Override protected void defineSynchedData(SynchedEntityData.Builder p_332901_) { super.defineSynchedData(p_332901_); Registry registry = this.registryAccess().lookupOrThrow(Registries.FROG_VARIANT); p_332901_.define(DATA_VARIANT_ID, VariantUtils.getDefaultOrAny(this.registryAccess(), DEFAULT_VARIANT)); p_332901_.define(DATA_TONGUE_TARGET_ID, OptionalInt.empty()); } public void eraseTongueTarget() { this.entityData.set(DATA_TONGUE_TARGET_ID, OptionalInt.empty()); } public Optional getTongueTarget() { return this.entityData.get(DATA_TONGUE_TARGET_ID).stream().mapToObj(this.level()::getEntity).filter(Objects::nonNull).findFirst(); } public void setTongueTarget(Entity p_218482_) { this.entityData.set(DATA_TONGUE_TARGET_ID, OptionalInt.of(p_218482_.getId())); } @Override public int getHeadRotSpeed() { return 35; } @Override public int getMaxHeadYRot() { return 5; } public Holder getVariant() { return this.entityData.get(DATA_VARIANT_ID); } private void setVariant(Holder p_329156_) { this.entityData.set(DATA_VARIANT_ID, p_329156_); } @Nullable @Override public T get(DataComponentType p_395428_) { return p_395428_ == DataComponents.FROG_VARIANT ? castComponentValue((DataComponentType)p_395428_, this.getVariant()) : super.get(p_395428_); } @Override protected void applyImplicitComponents(DataComponentGetter p_396714_) { this.applyImplicitComponentIfPresent(p_396714_, DataComponents.FROG_VARIANT); super.applyImplicitComponents(p_396714_); } @Override protected boolean applyImplicitComponent(DataComponentType p_395194_, T p_391742_) { if (p_395194_ == DataComponents.FROG_VARIANT) { this.setVariant(castComponentValue(DataComponents.FROG_VARIANT, p_391742_)); return true; } else { return super.applyImplicitComponent(p_395194_, p_391742_); } } @Override public void addAdditionalSaveData(CompoundTag p_218508_) { super.addAdditionalSaveData(p_218508_); VariantUtils.writeVariant(p_218508_, this.getVariant()); } @Override public void readAdditionalSaveData(CompoundTag p_218496_) { super.readAdditionalSaveData(p_218496_); VariantUtils.readVariant(p_218496_, this.registryAccess(), Registries.FROG_VARIANT).ifPresent(this::setVariant); } @Override protected void customServerAiStep(ServerLevel p_364897_) { ProfilerFiller profilerfiller = Profiler.get(); profilerfiller.push("frogBrain"); this.getBrain().tick(p_364897_, this); profilerfiller.pop(); profilerfiller.push("frogActivityUpdate"); FrogAi.updateActivity(this); profilerfiller.pop(); super.customServerAiStep(p_364897_); } @Override public void tick() { if (this.level().isClientSide()) { this.swimIdleAnimationState.animateWhen(this.isInWater() && !this.walkAnimation.isMoving(), this.tickCount); } super.tick(); } @Override public void onSyncedDataUpdated(EntityDataAccessor p_218498_) { if (DATA_POSE.equals(p_218498_)) { Pose pose = this.getPose(); if (pose == Pose.LONG_JUMPING) { this.jumpAnimationState.start(this.tickCount); } else { this.jumpAnimationState.stop(); } if (pose == Pose.CROAKING) { this.croakAnimationState.start(this.tickCount); } else { this.croakAnimationState.stop(); } if (pose == Pose.USING_TONGUE) { this.tongueAnimationState.start(this.tickCount); } else { this.tongueAnimationState.stop(); } } super.onSyncedDataUpdated(p_218498_); } @Override protected void updateWalkAnimation(float p_268239_) { float f; if (this.jumpAnimationState.isStarted()) { f = 0.0F; } else { f = Math.min(p_268239_ * 25.0F, 1.0F); } this.walkAnimation.update(f, 0.4F, this.isBaby() ? 3.0F : 1.0F); } @Override public void playEatingSound() { this.level().playSound(null, this, SoundEvents.FROG_EAT, SoundSource.NEUTRAL, 2.0F, 1.0F); } @Nullable @Override public AgeableMob getBreedOffspring(ServerLevel p_218476_, AgeableMob p_218477_) { Frog frog = EntityType.FROG.create(p_218476_, EntitySpawnReason.BREEDING); if (frog != null) { FrogAi.initMemories(frog, p_218476_.getRandom()); } return frog; } @Override public boolean isBaby() { return false; } @Override public void setBaby(boolean p_218500_) { } @Override public void spawnChildFromBreeding(ServerLevel p_218479_, Animal p_218480_) { this.finalizeSpawnChildFromBreeding(p_218479_, p_218480_, null); this.getBrain().setMemory(MemoryModuleType.IS_PREGNANT, Unit.INSTANCE); } @Override public SpawnGroupData finalizeSpawn(ServerLevelAccessor p_218488_, DifficultyInstance p_218489_, EntitySpawnReason p_366918_, @Nullable SpawnGroupData p_218491_) { FrogVariants.selectVariantToSpawn(this.random, this.registryAccess(), SpawnContext.create(p_218488_, this.blockPosition())).ifPresent(this::setVariant); FrogAi.initMemories(this, p_218488_.getRandom()); return super.finalizeSpawn(p_218488_, p_218489_, p_366918_, p_218491_); } public static AttributeSupplier.Builder createAttributes() { return Animal.createAnimalAttributes() .add(Attributes.MOVEMENT_SPEED, 1.0) .add(Attributes.MAX_HEALTH, 10.0) .add(Attributes.ATTACK_DAMAGE, 10.0) .add(Attributes.STEP_HEIGHT, 1.0); } @Nullable @Override protected SoundEvent getAmbientSound() { return SoundEvents.FROG_AMBIENT; } @Nullable @Override protected SoundEvent getHurtSound(DamageSource p_218510_) { return SoundEvents.FROG_HURT; } @Nullable @Override protected SoundEvent getDeathSound() { return SoundEvents.FROG_DEATH; } @Override protected void playStepSound(BlockPos p_218505_, BlockState p_218506_) { this.playSound(SoundEvents.FROG_STEP, 0.15F, 1.0F); } @Override public boolean isPushedByFluid() { return false; } @Override protected void sendDebugPackets() { super.sendDebugPackets(); DebugPackets.sendEntityBrain(this); } @Override protected int calculateFallDamage(double p_393819_, float p_218519_) { return super.calculateFallDamage(p_393819_, p_218519_) - 5; } @Override public void travel(Vec3 p_218530_) { if (this.isInWater()) { this.moveRelative(this.getSpeed(), p_218530_); this.move(MoverType.SELF, this.getDeltaMovement()); this.setDeltaMovement(this.getDeltaMovement().scale(0.9)); } else { super.travel(p_218530_); } } public static boolean canEat(LivingEntity p_218533_) { return p_218533_ instanceof Slime slime && slime.getSize() != 1 ? false : p_218533_.getType().is(EntityTypeTags.FROG_FOOD); } @Override protected PathNavigation createNavigation(Level p_218486_) { return new Frog.FrogPathNavigation(this, p_218486_); } @Nullable @Override public LivingEntity getTarget() { return this.getTargetFromBrain(); } @Override public boolean isFood(ItemStack p_218535_) { return p_218535_.is(ItemTags.FROG_FOOD); } public static boolean checkFrogSpawnRules( EntityType p_218512_, LevelAccessor p_218513_, EntitySpawnReason p_369828_, BlockPos p_218515_, RandomSource p_218516_ ) { return p_218513_.getBlockState(p_218515_.below()).is(BlockTags.FROGS_SPAWNABLE_ON) && isBrightEnoughToSpawn(p_218513_, p_218515_); } class FrogLookControl extends LookControl { FrogLookControl(final Mob p_218544_) { super(p_218544_); } @Override protected boolean resetXRotOnTick() { return Frog.this.getTongueTarget().isEmpty(); } } static class FrogNodeEvaluator extends AmphibiousNodeEvaluator { private final BlockPos.MutableBlockPos belowPos = new BlockPos.MutableBlockPos(); public FrogNodeEvaluator(boolean p_218548_) { super(p_218548_); } @Override public Node getStart() { return !this.mob.isInWater() ? super.getStart() : this.getStartNode( new BlockPos( Mth.floor(this.mob.getBoundingBox().minX), Mth.floor(this.mob.getBoundingBox().minY), Mth.floor(this.mob.getBoundingBox().minZ) ) ); } @Override public PathType getPathType(PathfindingContext p_329957_, int p_331836_, int p_331056_, int p_330649_) { this.belowPos.set(p_331836_, p_331056_ - 1, p_330649_); BlockState blockstate = p_329957_.getBlockState(this.belowPos); return blockstate.is(BlockTags.FROG_PREFER_JUMP_TO) ? PathType.OPEN : super.getPathType(p_329957_, p_331836_, p_331056_, p_330649_); } } static class FrogPathNavigation extends AmphibiousPathNavigation { FrogPathNavigation(Frog p_218556_, Level p_218557_) { super(p_218556_, p_218557_); } @Override public boolean canCutCorner(PathType p_330883_) { return p_330883_ != PathType.WATER_BORDER && super.canCutCorner(p_330883_); } @Override protected PathFinder createPathFinder(int p_218559_) { this.nodeEvaluator = new Frog.FrogNodeEvaluator(true); return new PathFinder(this.nodeEvaluator, p_218559_); } } }