package net.minecraft.world.entity.monster.breeze; import com.mojang.serialization.Dynamic; import java.util.Optional; import net.minecraft.core.particles.BlockParticleOption; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.network.protocol.game.DebugPackets; import net.minecraft.network.syncher.EntityDataAccessor; 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.EntityTypeTags; import net.minecraft.util.profiling.Profiler; import net.minecraft.util.profiling.ProfilerFiller; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.AnimationState; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.Pose; import net.minecraft.world.entity.ai.Brain; import net.minecraft.world.entity.ai.Brain.Provider; import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.ai.attributes.AttributeSupplier.Builder; import net.minecraft.world.entity.ai.memory.MemoryModuleType; import net.minecraft.world.entity.monster.Monster; import net.minecraft.world.entity.projectile.Projectile; import net.minecraft.world.entity.projectile.ProjectileDeflection; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.RenderShape; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.pathfinder.PathType; import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.Nullable; public class Breeze extends Monster { private static final int SLIDE_PARTICLES_AMOUNT = 20; private static final int IDLE_PARTICLES_AMOUNT = 1; private static final int JUMP_DUST_PARTICLES_AMOUNT = 20; private static final int JUMP_TRAIL_PARTICLES_AMOUNT = 3; private static final int JUMP_TRAIL_DURATION_TICKS = 5; private static final int JUMP_CIRCLE_DISTANCE_Y = 10; private static final float FALL_DISTANCE_SOUND_TRIGGER_THRESHOLD = 3.0F; private static final int WHIRL_SOUND_FREQUENCY_MIN = 1; private static final int WHIRL_SOUND_FREQUENCY_MAX = 80; public AnimationState idle = new AnimationState(); public AnimationState slide = new AnimationState(); public AnimationState slideBack = new AnimationState(); public AnimationState longJump = new AnimationState(); public AnimationState shoot = new AnimationState(); public AnimationState inhale = new AnimationState(); private int jumpTrailStartedTick = 0; private int soundTick = 0; private static final ProjectileDeflection PROJECTILE_DEFLECTION = (projectile, entity, randomSource) -> { entity.level().playSound(null, entity, SoundEvents.BREEZE_DEFLECT, entity.getSoundSource(), 1.0F, 1.0F); ProjectileDeflection.REVERSE.deflect(projectile, entity, randomSource); }; public static Builder createAttributes() { return Mob.createMobAttributes() .add(Attributes.MOVEMENT_SPEED, 0.63F) .add(Attributes.MAX_HEALTH, 30.0) .add(Attributes.FOLLOW_RANGE, 24.0) .add(Attributes.ATTACK_DAMAGE, 3.0); } public Breeze(EntityType entityType, Level level) { super(entityType, level); this.setPathfindingMalus(PathType.DANGER_TRAPDOOR, -1.0F); this.setPathfindingMalus(PathType.DAMAGE_FIRE, -1.0F); this.xpReward = 10; } @Override protected Brain makeBrain(Dynamic dynamic) { return BreezeAi.makeBrain(this, this.brainProvider().makeBrain(dynamic)); } @Override public Brain getBrain() { return (Brain)super.getBrain(); } @Override protected Provider brainProvider() { return Brain.provider(BreezeAi.MEMORY_TYPES, BreezeAi.SENSOR_TYPES); } @Override public void onSyncedDataUpdated(EntityDataAccessor dataAccessor) { if (this.level().isClientSide() && DATA_POSE.equals(dataAccessor)) { this.resetAnimations(); Pose pose = this.getPose(); switch (pose) { case SHOOTING: this.shoot.startIfStopped(this.tickCount); break; case INHALING: this.inhale.startIfStopped(this.tickCount); break; case SLIDING: this.slide.startIfStopped(this.tickCount); } } super.onSyncedDataUpdated(dataAccessor); } private void resetAnimations() { this.shoot.stop(); this.idle.stop(); this.inhale.stop(); this.longJump.stop(); } @Override public void tick() { Pose pose = this.getPose(); switch (pose) { case SHOOTING: case INHALING: case STANDING: this.resetJumpTrail().emitGroundParticles(1 + this.getRandom().nextInt(1)); break; case SLIDING: this.emitGroundParticles(20); break; case LONG_JUMPING: this.longJump.startIfStopped(this.tickCount); this.emitJumpTrailParticles(); } this.idle.startIfStopped(this.tickCount); if (pose != Pose.SLIDING && this.slide.isStarted()) { this.slideBack.start(this.tickCount); this.slide.stop(); } this.soundTick = this.soundTick == 0 ? this.random.nextIntBetweenInclusive(1, 80) : this.soundTick - 1; if (this.soundTick == 0) { this.playWhirlSound(); } super.tick(); } public Breeze resetJumpTrail() { this.jumpTrailStartedTick = 0; return this; } public void emitJumpTrailParticles() { if (++this.jumpTrailStartedTick <= 5) { BlockState blockState = !this.getInBlockState().isAir() ? this.getInBlockState() : this.getBlockStateOn(); Vec3 vec3 = this.getDeltaMovement(); Vec3 vec32 = this.position().add(vec3).add(0.0, 0.1F, 0.0); for (int i = 0; i < 3; i++) { this.level().addParticle(new BlockParticleOption(ParticleTypes.BLOCK, blockState), vec32.x, vec32.y, vec32.z, 0.0, 0.0, 0.0); } } } public void emitGroundParticles(int count) { if (!this.isPassenger()) { Vec3 vec3 = this.getBoundingBox().getCenter(); Vec3 vec32 = new Vec3(vec3.x, this.position().y, vec3.z); BlockState blockState = !this.getInBlockState().isAir() ? this.getInBlockState() : this.getBlockStateOn(); if (blockState.getRenderShape() != RenderShape.INVISIBLE) { for (int i = 0; i < count; i++) { this.level().addParticle(new BlockParticleOption(ParticleTypes.BLOCK, blockState), vec32.x, vec32.y, vec32.z, 0.0, 0.0, 0.0); } } } } @Override public void playAmbientSound() { if (this.getTarget() == null || !this.onGround()) { this.level().playLocalSound(this, this.getAmbientSound(), this.getSoundSource(), 1.0F, 1.0F); } } public void playWhirlSound() { float f = 0.7F + 0.4F * this.random.nextFloat(); float g = 0.8F + 0.2F * this.random.nextFloat(); this.level().playLocalSound(this, SoundEvents.BREEZE_WHIRL, this.getSoundSource(), g, f); } @Override public ProjectileDeflection deflection(Projectile projectile) { if (projectile.getType() != EntityType.BREEZE_WIND_CHARGE && projectile.getType() != EntityType.WIND_CHARGE) { return this.getType().is(EntityTypeTags.DEFLECTS_PROJECTILES) ? PROJECTILE_DEFLECTION : ProjectileDeflection.NONE; } else { return ProjectileDeflection.NONE; } } @Override public SoundSource getSoundSource() { return SoundSource.HOSTILE; } @Override protected SoundEvent getDeathSound() { return SoundEvents.BREEZE_DEATH; } @Override protected SoundEvent getHurtSound(DamageSource damageSource) { return SoundEvents.BREEZE_HURT; } @Override protected SoundEvent getAmbientSound() { return this.onGround() ? SoundEvents.BREEZE_IDLE_GROUND : SoundEvents.BREEZE_IDLE_AIR; } public Optional getHurtBy() { return this.getBrain() .getMemory(MemoryModuleType.HURT_BY) .map(DamageSource::getEntity) .filter(entity -> entity instanceof LivingEntity) .map(entity -> (LivingEntity)entity); } public boolean withinInnerCircleRange(Vec3 pos) { Vec3 vec3 = this.blockPosition().getCenter(); return pos.closerThan(vec3, 4.0, 10.0); } @Override protected void customServerAiStep(ServerLevel level) { ProfilerFiller profilerFiller = Profiler.get(); profilerFiller.push("breezeBrain"); this.getBrain().tick(level, this); profilerFiller.popPush("breezeActivityUpdate"); BreezeAi.updateActivity(this); profilerFiller.pop(); super.customServerAiStep(level); } @Override protected void sendDebugPackets() { super.sendDebugPackets(); DebugPackets.sendEntityBrain(this); DebugPackets.sendBreezeInfo(this); } @Override public boolean canAttackType(EntityType entityType) { return entityType == EntityType.PLAYER || entityType == EntityType.IRON_GOLEM; } @Override public int getMaxHeadYRot() { return 30; } @Override public int getHeadRotSpeed() { return 25; } public double getFiringYPosition() { return this.getY() + this.getBbHeight() / 2.0F + 0.3F; } @Override public boolean isInvulnerableTo(ServerLevel level, DamageSource damageSource) { return damageSource.getEntity() instanceof Breeze || super.isInvulnerableTo(level, damageSource); } @Override public double getFluidJumpThreshold() { return this.getEyeHeight(); } @Override public boolean causeFallDamage(float fallDistance, float multiplier, DamageSource source) { if (fallDistance > 3.0F) { this.playSound(SoundEvents.BREEZE_LAND, 1.0F, 1.0F); } return super.causeFallDamage(fallDistance, multiplier, source); } @Override protected Entity.MovementEmission getMovementEmission() { return Entity.MovementEmission.EVENTS; } @Nullable @Override public LivingEntity getTarget() { return this.getTargetFromBrain(); } }