package net.minecraft.world.entity.animal; import java.util.Objects; import net.minecraft.core.BlockPos; import net.minecraft.core.particles.ParticleOptions; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.server.level.ServerLevel; import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvents; import net.minecraft.tags.FluidTags; import net.minecraft.util.Mth; import net.minecraft.world.DifficultyInstance; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.effect.MobEffects; import net.minecraft.world.entity.AgeableMob; 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.SpawnGroupData; import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.ai.attributes.AttributeSupplier.Builder; import net.minecraft.world.entity.ai.goal.Goal; import net.minecraft.world.level.Level; import net.minecraft.world.level.ServerLevelAccessor; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.FluidState; import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.Nullable; public class Squid extends AgeableWaterCreature { public float xBodyRot; public float xBodyRotO; public float zBodyRot; public float zBodyRotO; public float tentacleMovement; public float oldTentacleMovement; public float tentacleAngle; public float oldTentacleAngle; private float speed; private float tentacleSpeed; private float rotateSpeed; Vec3 movementVector = Vec3.ZERO; public Squid(EntityType entityType, Level level) { super(entityType, level); this.random.setSeed(this.getId()); this.tentacleSpeed = 1.0F / (this.random.nextFloat() + 1.0F) * 0.2F; } @Override protected void registerGoals() { this.goalSelector.addGoal(0, new Squid.SquidRandomMovementGoal(this)); this.goalSelector.addGoal(1, new Squid.SquidFleeGoal()); } public static Builder createAttributes() { return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 10.0); } @Override protected SoundEvent getAmbientSound() { return SoundEvents.SQUID_AMBIENT; } @Override protected SoundEvent getHurtSound(DamageSource damageSource) { return SoundEvents.SQUID_HURT; } @Override protected SoundEvent getDeathSound() { return SoundEvents.SQUID_DEATH; } protected SoundEvent getSquirtSound() { return SoundEvents.SQUID_SQUIRT; } @Override public boolean canBeLeashed() { return true; } @Override protected float getSoundVolume() { return 0.4F; } @Override protected Entity.MovementEmission getMovementEmission() { return Entity.MovementEmission.EVENTS; } @Nullable @Override public AgeableMob getBreedOffspring(ServerLevel level, AgeableMob otherParent) { return EntityType.SQUID.create(level, EntitySpawnReason.BREEDING); } @Override protected double getDefaultGravity() { return 0.08; } @Override public void aiStep() { super.aiStep(); this.xBodyRotO = this.xBodyRot; this.zBodyRotO = this.zBodyRot; this.oldTentacleMovement = this.tentacleMovement; this.oldTentacleAngle = this.tentacleAngle; this.tentacleMovement = this.tentacleMovement + this.tentacleSpeed; if (this.tentacleMovement > Math.PI * 2) { if (this.level().isClientSide) { this.tentacleMovement = (float) (Math.PI * 2); } else { this.tentacleMovement -= (float) (Math.PI * 2); if (this.random.nextInt(10) == 0) { this.tentacleSpeed = 1.0F / (this.random.nextFloat() + 1.0F) * 0.2F; } this.level().broadcastEntityEvent(this, (byte)19); } } if (this.isInWater()) { if (this.tentacleMovement < (float) Math.PI) { float f = this.tentacleMovement / (float) Math.PI; this.tentacleAngle = Mth.sin(f * f * (float) Math.PI) * (float) Math.PI * 0.25F; if (f > 0.75) { if (this.isLocalInstanceAuthoritative()) { this.setDeltaMovement(this.movementVector); } this.rotateSpeed = 1.0F; } else { this.rotateSpeed *= 0.8F; } } else { this.tentacleAngle = 0.0F; if (this.isLocalInstanceAuthoritative()) { this.setDeltaMovement(this.getDeltaMovement().scale(0.9)); } this.rotateSpeed *= 0.99F; } Vec3 vec3 = this.getDeltaMovement(); double d = vec3.horizontalDistance(); this.yBodyRot = this.yBodyRot + (-((float)Mth.atan2(vec3.x, vec3.z)) * (180.0F / (float)Math.PI) - this.yBodyRot) * 0.1F; this.setYRot(this.yBodyRot); this.zBodyRot = this.zBodyRot + (float) Math.PI * this.rotateSpeed * 1.5F; this.xBodyRot = this.xBodyRot + (-((float)Mth.atan2(d, vec3.y)) * (180.0F / (float)Math.PI) - this.xBodyRot) * 0.1F; } else { this.tentacleAngle = Mth.abs(Mth.sin(this.tentacleMovement)) * (float) Math.PI * 0.25F; if (!this.level().isClientSide) { double e = this.getDeltaMovement().y; if (this.hasEffect(MobEffects.LEVITATION)) { e = 0.05 * (this.getEffect(MobEffects.LEVITATION).getAmplifier() + 1); } else { e -= this.getGravity(); } this.setDeltaMovement(0.0, e * 0.98F, 0.0); } this.xBodyRot = this.xBodyRot + (-90.0F - this.xBodyRot) * 0.02F; } } @Override public boolean hurtServer(ServerLevel level, DamageSource damageSource, float amount) { if (super.hurtServer(level, damageSource, amount) && this.getLastHurtByMob() != null) { this.spawnInk(); return true; } else { return false; } } private Vec3 rotateVector(Vec3 vector) { Vec3 vec3 = vector.xRot(this.xBodyRotO * (float) (Math.PI / 180.0)); return vec3.yRot(-this.yBodyRotO * (float) (Math.PI / 180.0)); } private void spawnInk() { this.makeSound(this.getSquirtSound()); Vec3 vec3 = this.rotateVector(new Vec3(0.0, -1.0, 0.0)).add(this.getX(), this.getY(), this.getZ()); for (int i = 0; i < 30; i++) { Vec3 vec32 = this.rotateVector(new Vec3(this.random.nextFloat() * 0.6 - 0.3, -1.0, this.random.nextFloat() * 0.6 - 0.3)); float f = this.isBaby() ? 0.1F : 0.3F; Vec3 vec33 = vec32.scale(f + this.random.nextFloat() * 2.0F); ((ServerLevel)this.level()).sendParticles(this.getInkParticle(), vec3.x, vec3.y + 0.5, vec3.z, 0, vec33.x, vec33.y, vec33.z, 0.1F); } } protected ParticleOptions getInkParticle() { return ParticleTypes.SQUID_INK; } @Override public void travel(Vec3 travelVector) { this.move(MoverType.SELF, this.getDeltaMovement()); } @Override public void handleEntityEvent(byte id) { if (id == 19) { this.tentacleMovement = 0.0F; } else { super.handleEntityEvent(id); } } public boolean hasMovementVector() { return this.movementVector.lengthSqr() > 1.0E-5F; } @Nullable @Override public SpawnGroupData finalizeSpawn( ServerLevelAccessor level, DifficultyInstance difficulty, EntitySpawnReason spawnReason, @Nullable SpawnGroupData spawnGroupData ) { SpawnGroupData spawnGroupData2 = (SpawnGroupData)Objects.requireNonNullElseGet(spawnGroupData, () -> new AgeableMob.AgeableMobGroupData(0.05F)); return super.finalizeSpawn(level, difficulty, spawnReason, spawnGroupData2); } class SquidFleeGoal extends Goal { private static final float SQUID_FLEE_SPEED = 3.0F; private static final float SQUID_FLEE_MIN_DISTANCE = 5.0F; private static final float SQUID_FLEE_MAX_DISTANCE = 10.0F; private int fleeTicks; @Override public boolean canUse() { LivingEntity livingEntity = Squid.this.getLastHurtByMob(); return Squid.this.isInWater() && livingEntity != null ? Squid.this.distanceToSqr(livingEntity) < 100.0 : false; } @Override public void start() { this.fleeTicks = 0; } @Override public boolean requiresUpdateEveryTick() { return true; } @Override public void tick() { this.fleeTicks++; LivingEntity livingEntity = Squid.this.getLastHurtByMob(); if (livingEntity != null) { Vec3 vec3 = new Vec3(Squid.this.getX() - livingEntity.getX(), Squid.this.getY() - livingEntity.getY(), Squid.this.getZ() - livingEntity.getZ()); BlockState blockState = Squid.this.level() .getBlockState(BlockPos.containing(Squid.this.getX() + vec3.x, Squid.this.getY() + vec3.y, Squid.this.getZ() + vec3.z)); FluidState fluidState = Squid.this.level() .getFluidState(BlockPos.containing(Squid.this.getX() + vec3.x, Squid.this.getY() + vec3.y, Squid.this.getZ() + vec3.z)); if (fluidState.is(FluidTags.WATER) || blockState.isAir()) { double d = vec3.length(); if (d > 0.0) { vec3.normalize(); double e = 3.0; if (d > 5.0) { e -= (d - 5.0) / 5.0; } if (e > 0.0) { vec3 = vec3.scale(e); } } if (blockState.isAir()) { vec3 = vec3.subtract(0.0, vec3.y, 0.0); } Squid.this.movementVector = new Vec3(vec3.x / 20.0, vec3.y / 20.0, vec3.z / 20.0); } if (this.fleeTicks % 10 == 5) { Squid.this.level().addParticle(ParticleTypes.BUBBLE, Squid.this.getX(), Squid.this.getY(), Squid.this.getZ(), 0.0, 0.0, 0.0); } } } } static class SquidRandomMovementGoal extends Goal { private final Squid squid; public SquidRandomMovementGoal(Squid squid) { this.squid = squid; } @Override public boolean canUse() { return true; } @Override public void tick() { int i = this.squid.getNoActionTime(); if (i > 100) { this.squid.movementVector = Vec3.ZERO; } else if (this.squid.getRandom().nextInt(reducedTickDelay(50)) == 0 || !this.squid.wasTouchingWater || !this.squid.hasMovementVector()) { float f = this.squid.getRandom().nextFloat() * (float) (Math.PI * 2); this.squid.movementVector = new Vec3(Mth.cos(f) * 0.2F, -0.1F + this.squid.getRandom().nextFloat() * 0.2F, Mth.sin(f) * 0.2F); } } } }