package net.minecraft.world.entity.projectile; import net.minecraft.core.BlockPos; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtOps; import net.minecraft.nbt.Tag; 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.resources.RegistryOps; import net.minecraft.server.level.ServerLevel; import net.minecraft.sounds.SoundEvents; import net.minecraft.util.Mth; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec3; public class EyeOfEnder extends Entity implements ItemSupplier { private static final float MIN_CAMERA_DISTANCE_SQUARED = 12.25F; private static final EntityDataAccessor DATA_ITEM_STACK = SynchedEntityData.defineId(EyeOfEnder.class, EntityDataSerializers.ITEM_STACK); private double tx; private double ty; private double tz; private int life; private boolean surviveAfterDeath; public EyeOfEnder(EntityType entityType, Level level) { super(entityType, level); } public EyeOfEnder(Level level, double x, double y, double z) { this(EntityType.EYE_OF_ENDER, level); this.setPos(x, y, z); } public void setItem(ItemStack stack) { if (stack.isEmpty()) { this.getEntityData().set(DATA_ITEM_STACK, this.getDefaultItem()); } else { this.getEntityData().set(DATA_ITEM_STACK, stack.copyWithCount(1)); } } @Override public ItemStack getItem() { return this.getEntityData().get(DATA_ITEM_STACK); } @Override protected void defineSynchedData(Builder builder) { builder.define(DATA_ITEM_STACK, this.getDefaultItem()); } @Override public boolean shouldRenderAtSqrDistance(double distance) { if (this.tickCount < 2 && distance < 12.25) { return false; } else { double d = this.getBoundingBox().getSize() * 4.0; if (Double.isNaN(d)) { d = 4.0; } d *= 64.0; return distance < d * d; } } public void signalTo(BlockPos pos) { double d = pos.getX(); int i = pos.getY(); double e = pos.getZ(); double f = d - this.getX(); double g = e - this.getZ(); double h = Math.sqrt(f * f + g * g); if (h > 12.0) { this.tx = this.getX() + f / h * 12.0; this.tz = this.getZ() + g / h * 12.0; this.ty = this.getY() + 8.0; } else { this.tx = d; this.ty = i; this.tz = e; } this.life = 0; this.surviveAfterDeath = this.random.nextInt(5) > 0; } @Override public void lerpMotion(double x, double y, double z) { this.setDeltaMovement(x, y, z); if (this.xRotO == 0.0F && this.yRotO == 0.0F) { double d = Math.sqrt(x * x + z * z); this.setYRot((float)(Mth.atan2(x, z) * 180.0F / (float)Math.PI)); this.setXRot((float)(Mth.atan2(y, d) * 180.0F / (float)Math.PI)); this.yRotO = this.getYRot(); this.xRotO = this.getXRot(); } } @Override public void tick() { super.tick(); Vec3 vec3 = this.getDeltaMovement(); double d = this.getX() + vec3.x; double e = this.getY() + vec3.y; double f = this.getZ() + vec3.z; double g = vec3.horizontalDistance(); this.setXRot(Projectile.lerpRotation(this.xRotO, (float)(Mth.atan2(vec3.y, g) * 180.0F / (float)Math.PI))); this.setYRot(Projectile.lerpRotation(this.yRotO, (float)(Mth.atan2(vec3.x, vec3.z) * 180.0F / (float)Math.PI))); if (!this.level().isClientSide) { double h = this.tx - d; double i = this.tz - f; float j = (float)Math.sqrt(h * h + i * i); float k = (float)Mth.atan2(i, h); double l = Mth.lerp(0.0025, g, (double)j); double m = vec3.y; if (j < 1.0F) { l *= 0.8; m *= 0.8; } int n = this.getY() < this.ty ? 1 : -1; vec3 = new Vec3(Math.cos(k) * l, m + (n - m) * 0.015F, Math.sin(k) * l); this.setDeltaMovement(vec3); } float o = 0.25F; if (this.isInWater()) { for (int p = 0; p < 4; p++) { this.level().addParticle(ParticleTypes.BUBBLE, d - vec3.x * 0.25, e - vec3.y * 0.25, f - vec3.z * 0.25, vec3.x, vec3.y, vec3.z); } } else { this.level() .addParticle( ParticleTypes.PORTAL, d - vec3.x * 0.25 + this.random.nextDouble() * 0.6 - 0.3, e - vec3.y * 0.25 - 0.5, f - vec3.z * 0.25 + this.random.nextDouble() * 0.6 - 0.3, vec3.x, vec3.y, vec3.z ); } if (!this.level().isClientSide) { this.setPos(d, e, f); this.life++; if (this.life > 80 && !this.level().isClientSide) { this.playSound(SoundEvents.ENDER_EYE_DEATH, 1.0F, 1.0F); this.discard(); if (this.surviveAfterDeath) { this.level().addFreshEntity(new ItemEntity(this.level(), this.getX(), this.getY(), this.getZ(), this.getItem())); } else { this.level().levelEvent(2003, this.blockPosition(), 0); } } } else { this.setPos(d, e, f); } } @Override public void addAdditionalSaveData(CompoundTag tag) { RegistryOps registryOps = this.registryAccess().createSerializationContext(NbtOps.INSTANCE); tag.store("Item", ItemStack.CODEC, registryOps, this.getItem()); } @Override public void readAdditionalSaveData(CompoundTag tag) { RegistryOps registryOps = this.registryAccess().createSerializationContext(NbtOps.INSTANCE); this.setItem((ItemStack)tag.read("Item", ItemStack.CODEC, registryOps).orElse(this.getDefaultItem())); } private ItemStack getDefaultItem() { return new ItemStack(Items.ENDER_EYE); } @Override public float getLightLevelDependentMagicValue() { return 1.0F; } @Override public boolean isAttackable() { return false; } @Override public boolean hurtServer(ServerLevel level, DamageSource damageSource, float amount) { return false; } }