package net.minecraft.world.entity.monster; import java.util.EnumSet; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.server.level.ServerLevel; import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvents; import net.minecraft.tags.DamageTypeTags; import net.minecraft.util.RandomSource; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntitySpawnReason; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.ai.attributes.AttributeSupplier.Builder; import net.minecraft.world.entity.ai.goal.ClimbOnTopOfPowderSnowGoal; import net.minecraft.world.entity.ai.goal.FloatGoal; import net.minecraft.world.entity.ai.goal.Goal; import net.minecraft.world.entity.ai.goal.MeleeAttackGoal; import net.minecraft.world.entity.ai.goal.RandomStrollGoal; import net.minecraft.world.entity.ai.goal.Goal.Flag; import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal; import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.GameRules; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.InfestedBlock; import net.minecraft.world.level.block.state.BlockState; import org.jetbrains.annotations.Nullable; public class Silverfish extends Monster { @Nullable private Silverfish.SilverfishWakeUpFriendsGoal friendsGoal; public Silverfish(EntityType entityType, Level level) { super(entityType, level); } @Override protected void registerGoals() { this.friendsGoal = new Silverfish.SilverfishWakeUpFriendsGoal(this); this.goalSelector.addGoal(1, new FloatGoal(this)); this.goalSelector.addGoal(1, new ClimbOnTopOfPowderSnowGoal(this, this.level())); this.goalSelector.addGoal(3, this.friendsGoal); this.goalSelector.addGoal(4, new MeleeAttackGoal(this, 1.0, false)); this.goalSelector.addGoal(5, new Silverfish.SilverfishMergeWithStoneGoal(this)); this.targetSelector.addGoal(1, new HurtByTargetGoal(this).setAlertOthers()); this.targetSelector.addGoal(2, new NearestAttackableTargetGoal(this, Player.class, true)); } public static Builder createAttributes() { return Monster.createMonsterAttributes().add(Attributes.MAX_HEALTH, 8.0).add(Attributes.MOVEMENT_SPEED, 0.25).add(Attributes.ATTACK_DAMAGE, 1.0); } @Override protected Entity.MovementEmission getMovementEmission() { return Entity.MovementEmission.EVENTS; } @Override protected SoundEvent getAmbientSound() { return SoundEvents.SILVERFISH_AMBIENT; } @Override protected SoundEvent getHurtSound(DamageSource damageSource) { return SoundEvents.SILVERFISH_HURT; } @Override protected SoundEvent getDeathSound() { return SoundEvents.SILVERFISH_DEATH; } @Override protected void playStepSound(BlockPos pos, BlockState state) { this.playSound(SoundEvents.SILVERFISH_STEP, 0.15F, 1.0F); } @Override public boolean hurtServer(ServerLevel level, DamageSource damageSource, float amount) { if (this.isInvulnerableTo(level, damageSource)) { return false; } else { if ((damageSource.getEntity() != null || damageSource.is(DamageTypeTags.ALWAYS_TRIGGERS_SILVERFISH)) && this.friendsGoal != null) { this.friendsGoal.notifyHurt(); } return super.hurtServer(level, damageSource, amount); } } @Override public void tick() { this.yBodyRot = this.getYRot(); super.tick(); } @Override public void setYBodyRot(float yBodyRot) { this.setYRot(yBodyRot); super.setYBodyRot(yBodyRot); } @Override public float getWalkTargetValue(BlockPos pos, LevelReader level) { return InfestedBlock.isCompatibleHostBlock(level.getBlockState(pos.below())) ? 10.0F : super.getWalkTargetValue(pos, level); } public static boolean checkSilverfishSpawnRules( EntityType entityType, LevelAccessor level, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random ) { if (!checkAnyLightMonsterSpawnRules(entityType, level, spawnReason, pos, random)) { return false; } else if (EntitySpawnReason.isSpawner(spawnReason)) { return true; } else { Player player = level.getNearestPlayer(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, 5.0, true); return player == null; } } static class SilverfishMergeWithStoneGoal extends RandomStrollGoal { @Nullable private Direction selectedDirection; private boolean doMerge; public SilverfishMergeWithStoneGoal(Silverfish silverfish) { super(silverfish, 1.0, 10); this.setFlags(EnumSet.of(Flag.MOVE)); } @Override public boolean canUse() { if (this.mob.getTarget() != null) { return false; } else if (!this.mob.getNavigation().isDone()) { return false; } else { RandomSource randomSource = this.mob.getRandom(); if (getServerLevel(this.mob).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) && randomSource.nextInt(reducedTickDelay(10)) == 0) { this.selectedDirection = Direction.getRandom(randomSource); BlockPos blockPos = BlockPos.containing(this.mob.getX(), this.mob.getY() + 0.5, this.mob.getZ()).relative(this.selectedDirection); BlockState blockState = this.mob.level().getBlockState(blockPos); if (InfestedBlock.isCompatibleHostBlock(blockState)) { this.doMerge = true; return true; } } this.doMerge = false; return super.canUse(); } } @Override public boolean canContinueToUse() { return this.doMerge ? false : super.canContinueToUse(); } @Override public void start() { if (!this.doMerge) { super.start(); } else { LevelAccessor levelAccessor = this.mob.level(); BlockPos blockPos = BlockPos.containing(this.mob.getX(), this.mob.getY() + 0.5, this.mob.getZ()).relative(this.selectedDirection); BlockState blockState = levelAccessor.getBlockState(blockPos); if (InfestedBlock.isCompatibleHostBlock(blockState)) { levelAccessor.setBlock(blockPos, InfestedBlock.infestedStateByHost(blockState), 3); this.mob.spawnAnim(); this.mob.discard(); } } } } static class SilverfishWakeUpFriendsGoal extends Goal { private final Silverfish silverfish; private int lookForFriends; public SilverfishWakeUpFriendsGoal(Silverfish silverfish) { this.silverfish = silverfish; } public void notifyHurt() { if (this.lookForFriends == 0) { this.lookForFriends = this.adjustedTickDelay(20); } } @Override public boolean canUse() { return this.lookForFriends > 0; } @Override public void tick() { this.lookForFriends--; if (this.lookForFriends <= 0) { Level level = this.silverfish.level(); RandomSource randomSource = this.silverfish.getRandom(); BlockPos blockPos = this.silverfish.blockPosition(); for (int i = 0; i <= 5 && i >= -5; i = (i <= 0 ? 1 : 0) - i) { for (int j = 0; j <= 10 && j >= -10; j = (j <= 0 ? 1 : 0) - j) { for (int k = 0; k <= 10 && k >= -10; k = (k <= 0 ? 1 : 0) - k) { BlockPos blockPos2 = blockPos.offset(j, i, k); BlockState blockState = level.getBlockState(blockPos2); Block block = blockState.getBlock(); if (block instanceof InfestedBlock) { if (getServerLevel(level).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { level.destroyBlock(blockPos2, true, this.silverfish); } else { level.setBlock(blockPos2, ((InfestedBlock)block).hostStateByInfested(level.getBlockState(blockPos2)), 3); } if (randomSource.nextBoolean()) { return; } } } } } } } } }