content: package com.github.alexthe666.iceandfire.entity;
  
  import com.google.common.collect.Lists;
  import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
  import net.minecraft.core.particles.ParticleTypes;
  import net.minecraft.network.protocol.Packet;
  import net.minecraft.network.protocol.game.ClientboundGameEventPacket;
  import net.minecraft.server.level.ServerPlayer;
  import net.minecraft.sounds.SoundEvent;
  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.LivingEntity;
  import net.minecraft.world.entity.player.Player;
  import net.minecraft.world.entity.projectile.AbstractArrow;
  import net.minecraft.world.item.ItemStack;
  import net.minecraft.world.level.ClipContext;
  import net.minecraft.world.level.Level;
  import net.minecraft.world.phys.EntityHitResult;
  import net.minecraft.world.phys.HitResult;
  import net.minecraft.world.phys.Vec3;
  import net.minecraftforge.network.NetworkHooks;
  import net.minecraftforge.network.PlayMessages;
  import org.jetbrains.annotations.NotNull;
  
  import java.util.List;
  
  public class EntityGhostSword extends AbstractArrow {
  
      public EntityGhostSword(EntityType<? extends AbstractArrow> type, Level worldIn) {
          super(type, worldIn);
          this.setBaseDamage(9F);
      }
  
      public EntityGhostSword(EntityType<? extends AbstractArrow> type, Level worldIn, double x, double y, double z,
                              float r, float g, float b) {
          this(type, worldIn);
          this.setPos(x, y, z);
          this.setBaseDamage(9F);
      }
  
      public EntityGhostSword(EntityType<? extends AbstractArrow> type, Level worldIn, LivingEntity shooter,
                              double dmg) {
          super(type, shooter, worldIn);
          this.setBaseDamage(dmg);
      }
  
      public EntityGhostSword(PlayMessages.SpawnEntity spawnEntity, Level worldIn) {
          this(IafEntityRegistry.GHOST_SWORD.get(), worldIn);
      }
  
      @Override
      public boolean isInWater() {
          return false;
      }
  
      @Override
      protected void defineSynchedData() {
          super.defineSynchedData();
      }
  
      @Override
      public void tick() {
          super.tick();
          noPhysics = true;
          float sqrt = Mth.sqrt((float) (this.getDeltaMovement().x * this.getDeltaMovement().x + this.getDeltaMovement().z * this.getDeltaMovement().z));
          if ((sqrt < 0.1F) && this.tickCount > 200) {
              this.remove(RemovalReason.DISCARDED);
          }
          double d0 = 0;
          double d1 = 0.0D;
          double d2 = 0.01D;
          double x = this.getX() + this.random.nextFloat() * this.getBbWidth() * 2.0F - this.getBbWidth();
          double y = this.getY() + this.random.nextFloat() * this.getBbHeight() - this.getBbHeight();
          double z = this.getZ() + this.random.nextFloat() * this.getBbWidth() * 2.0F - this.getBbWidth();
          float f = (this.getBbWidth() + this.getBbHeight() + this.getBbWidth()) * 0.333F + 0.5F;
          if (particleDistSq(x, y, z) < f * f) {
              this.level.addParticle(ParticleTypes.SNEEZE, x, y + 0.5D, z, d0, d1, d2);
          }
          Vec3 vector3d = this.getDeltaMovement();
          double f3 = vector3d.horizontalDistance();
          this.setYRot((float) (Mth.atan2(vector3d.x, vector3d.z) * (180F / (float) Math.PI)));
          this.setXRot((float) (Mth.atan2(vector3d.y, f3) * (180F / (float) Math.PI)));
          this.yRotO = this.getYRot();
          this.xRotO = this.getXRot();
          Vec3 vector3d2 = this.position();
          Vec3 vector3d3 = vector3d2.add(vector3d);
          HitResult raytraceresult = this.level.clip(new ClipContext(vector3d2, vector3d3, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, this));
          if (raytraceresult.getType() != HitResult.Type.MISS) {
              vector3d3 = raytraceresult.getLocation();
          }
          while (!this.isRemoved()) {
              EntityHitResult entityraytraceresult = this.findHitEntity(vector3d2, vector3d3);
              if (entityraytraceresult != null) {
                  raytraceresult = entityraytraceresult;
              }
  
              if (raytraceresult != null && raytraceresult.getType() == HitResult.Type.ENTITY) {
                  Entity entity = ((EntityHitResult) raytraceresult).getEntity();
                  Entity entity1 = this.getOwner();
                  if (entity instanceof Player && entity1 instanceof Player && !((Player) entity1).canHarmPlayer((Player) entity)) {
                      raytraceresult = null;
                      entityraytraceresult = null;
                  }
              }
  
              if (raytraceresult != null && raytraceresult.getType() != HitResult.Type.MISS && !net.minecraftforge.event.ForgeEventFactory.onProjectileImpact(this, raytraceresult)) {
                  if (raytraceresult.getType() != HitResult.Type.BLOCK) {
                      this.onHit(raytraceresult);
  
                  }
                  this.hasImpulse = true;
              }
  
              if (entityraytraceresult == null || this.getPierceLevel() <= 0) {
                  break;
              }
  
              raytraceresult = null;
          }
      }
  
      public double particleDistSq(double toX, double toY, double toZ) {
          double d0 = getX() - toX;
          double d1 = getY() - toY;
          double d2 = getZ() - toZ;
          return d0 * d0 + d1 * d1 + d2 * d2;
      }
  
  
      @Override
      public void playSound(@NotNull SoundEvent soundIn, float volume, float pitch) {
          if (!this.isSilent() && soundIn != SoundEvents.ARROW_HIT && soundIn != SoundEvents.ARROW_HIT_PLAYER) {
              this.level.playSound(null, this.getX(), this.getY(), this.getZ(), soundIn, this.getSoundSource(), volume, pitch);
          }
      }
  
      public int getBrightnessForRender() {
          return 15728880;
      }
  
      @Override
      public float getBrightness() {
          return 1.0F;
      }
  
      @Override
      public boolean isNoGravity() {
          return true;
      }
  
      @Override
      protected @NotNull ItemStack getPickupItem() {
          return ItemStack.EMPTY;
      }
  
      @Override
      public @NotNull Packet<?> getAddEntityPacket() {
          return NetworkHooks.getEntitySpawningPacket(this);
      }
  
      private IntOpenHashSet piercedEntities;
      private List<Entity> hitEntities;
      private int knockbackStrength;
  
      @Override
      public void setKnockback(int knockbackStrengthIn) {
          this.knockbackStrength = knockbackStrengthIn;
      }
  
      @Override
      protected void onHitEntity(EntityHitResult result) {
          Entity entity = result.getEntity();
          float f = (float) this.getDeltaMovement().length();
          int i = Mth.ceil(Math.max(f * this.getBaseDamage(), 0.0D));
          if (this.getPierceLevel() > 0) {
              if (this.piercedEntities == null) {
                  this.piercedEntities = new IntOpenHashSet(5);
              }
  
              if (this.hitEntities == null) {
                  this.hitEntities = Lists.newArrayListWithCapacity(5);
              }
  
              if (this.piercedEntities.size() >= this.getPierceLevel() + 1) {
                  this.remove(RemovalReason.DISCARDED);
                  return;
              }
  
              this.piercedEntities.add(entity.getId());
          }
  
          if (this.isCritArrow()) {
              i += this.random.nextInt(i / 2 + 2);
          }
  
          Entity entity1 = this.getOwner();
          DamageSource damagesource = DamageSource.MAGIC;
  
          if (entity1 != null) {
              if (entity1 instanceof LivingEntity) {
                  damagesource = DamageSource.arrow(this, entity1);
                  damagesource.setMagic();
                  ((LivingEntity) entity1).setLastHurtMob(entity);
              }
          }
  
          boolean flag = entity.getType() == EntityType.ENDERMAN;
          int j = entity.getRemainingFireTicks();
          if (this.isOnFire() && !flag) {
              entity.setSecondsOnFire(5);
          }
  
          if (entity.hurt(damagesource, i)) {
              if (flag) {
                  return;
              }
  
              if (entity instanceof LivingEntity) {
                  LivingEntity livingentity = (LivingEntity) entity;
  
                  if (this.knockbackStrength > 0) {
                      Vec3 vec3d = this.getDeltaMovement().multiply(1.0D, 0.0D, 1.0D).normalize()
                          .scale(this.knockbackStrength * 0.6D);
                      if (vec3d.lengthSqr() > 0.0D) {
                          livingentity.push(vec3d.x, 0.1D, vec3d.z);
                      }
                  }
  
                  this.doPostHurtEffects(livingentity);
                  if (entity1 != null && livingentity != entity1 && livingentity instanceof Player && entity1 instanceof ServerPlayer) {
                      ((ServerPlayer) entity1).connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.ARROW_HIT_PLAYER, 0.0F));
                  }
  
                  if (!entity.isAlive() && this.hitEntities != null) {
                      this.hitEntities.add(livingentity);
                  }
  
              }
  
              this.playSound(this.getHitGroundSoundEvent(), 1.0F, 1.2F / (this.random.nextFloat() * 0.2F + 0.9F));
              if (this.getPierceLevel() <= 0) {
                  this.remove(RemovalReason.DISCARDED);
              }
          } else {
              this.setDeltaMovement(this.getDeltaMovement().scale(-0.1D));
              //this.ticksInAir = 0;
              if (!this.level.isClientSide && this.getDeltaMovement().lengthSqr() < 1.0E-7D) {
                  this.remove(RemovalReason.DISCARDED);
              }
          }
  
      }
  }
  