public static Optional<Tuple<DamageModifier, Function<? super Double, Double>>>
     createHardHatModifier(EntityLivingBase entityLivingBase, DamageSource damageSource) {
   if ((damageSource instanceof FallingBlockDamageSource)
       && entityLivingBase.getEquipmentInSlot(4) != null) {
     DamageModifier modifier =
         DamageModifier.builder()
             .cause(
                 Cause.of(
                     NamedCause.of(
                         DamageEntityEvent.HARD_HAT_ARMOR,
                         ((ItemStack) entityLivingBase.getEquipmentInSlot(4)).createSnapshot())))
             .type(DamageModifierTypes.HARD_HAT)
             .build();
     return Optional.of(new Tuple<>(modifier, HARD_HAT_FUNCTION));
   }
   return Optional.empty();
 }
  @Override
  @SideOnly(Side.CLIENT)
  public ModelBiped getArmorModel(
      EntityLivingBase entityLiving, ItemStack itemStack, int armorSlot) {

    ModelBackBokken armorModel = null;
    if (itemStack != null) {
      if (itemStack.getItem() instanceof ItemArmourKatana) {
        int type = ((ItemArmor) itemStack.getItem()).armorType;

        if (type == 1) {
          armorModel = new ModelBackBokken();
          DataWatcher dw = entityLiving.getDataWatcher();
          armorModel.animationID = dw.getWatchableObjectString(20);
          armorModel.animationlastID = dw.getWatchableObjectString(26);
          armorModel.animationTick = dw.getWatchableObjectFloat(25);
        }
      }
    }
    if (entityLiving.getHeldItem() != null) {
      // armorModel.blade.isHidden = true;
      // armorModel.blade.isHidden = false;
      armorModel.handle.isHidden = entityLiving.getHeldItem().getItem() == NarutoItems.Katana;
    } else {
      // armorModel.blade.isHidden = false;
      armorModel.handle.isHidden = false;
    }

    if (armorModel != null) {
      armorModel.bipedBody.showModel = armorSlot == 1;

      armorModel.isSneak = entityLiving.isSneaking();
      armorModel.isRiding = entityLiving.isRiding();
      armorModel.isChild = entityLiving.isChild();
      armorModel.heldItemRight = entityLiving.getEquipmentInSlot(0) != null ? 1 : 0;
      armorModel.isSprinting = entityLiving.isSprinting();
      if (entityLiving instanceof EntityPlayer) {
        EntityPlayer entityPlayer = (EntityPlayer) entityLiving;
        if (itemStack != null && entityPlayer.getItemInUseCount() > 0) {
          EnumAction enumaction = itemStack.getItemUseAction();

          if (enumaction == EnumAction.block) {
            armorModel.heldItemRight = 3;
          } else if (enumaction == EnumAction.bow) {
            armorModel.aimedBow = true;
          } else if (enumaction == NarutoItems.Throw) {
            if (FMLClientHandler.instance().getClient().thePlayer == entityPlayer) {
              armorModel.isClientThrowing = true;
            } else {
              armorModel.isThrowing = true;
            }
          }
        }
      }
    }

    return armorModel;
  }
  @SubscribeEvent
  public void fallEvent(LivingFallEvent event) {
    EntityLivingBase e = event.entityLiving;
    ItemStack is = e.getEquipmentInSlot(1);

    if (is != null) {
      if (is.getItem() instanceof ItemSpringBoots) {
        if (is.getItem() == ItemRegistry.BEDJUMP.getItemInstance() || is.getItemDamage() > 0) {
          // ReikaJavaLibrary.pConsole(event.distance);
          event.distance *= 0.6F;
          // ReikaJavaLibrary.pConsole(event.distance);
          if (is.getItem() == ItemRegistry.BEDJUMP.getItemInstance())
            event.distance = Math.min(event.distance, 25);
        }
      }
    }
  }
 @Override
 protected void onImpact(MovingObjectPosition mop) {
   if (mop.typeOfHit == MovingObjectType.BLOCK) {
     Block block = worldObj.getBlock(mop.blockX, mop.blockY, mop.blockZ);
     if (!isInGround() && ticksExisted < getMaxDistance()) {
       WorldUtils.playSoundAtEntity(this, Sounds.WHIP_CRACK, 1.0F, 0.2F);
       motionX = motionY = motionZ = 0.0D;
       if (canGrabBlock(block, mop.blockX, mop.blockY, mop.blockZ, mop.sideHit)) {
         setInGround(true);
         AxisAlignedBB box =
             block.getCollisionBoundingBoxFromPool(worldObj, mop.blockX, mop.blockY, mop.blockZ);
         // bounding box may be null, depending on the block
         if (box != null) {
           posX = box.minX + ((box.maxX - box.minX) / 2.0D);
           posY = box.minY + ((box.maxY - box.minY) / 2.0D);
           posZ = box.minZ + ((box.maxZ - box.minZ) / 2.0D);
           switch (mop.sideHit) {
             case 5:
               posX = box.maxX;
               break; // EAST
             case 4:
               posX = box.minX - 0.015D;
               break; // WEST (a little extra to compensate for block border, otherwise renders
                      // black)
             case 3:
               posZ = box.maxZ;
               break; // SOUTH
             case 2:
               posZ = box.minZ - 0.015D;
               break; // NORTH (a little extra to compensate for block border, otherwise renders
                      // black)
             case SideHit.TOP:
               posY = box.maxY;
               break;
             case SideHit.BOTTOM:
               posY = box.minY - 0.015D;
               break;
           }
         } else {
           // adjusting posX/Y/Z here seems to make no difference to the rendering, even when
           // client side makes same changes
           posX = (double) mop.blockX + 0.5D;
           posY = (double) mop.blockY + 0.5D;
           posZ = (double) mop.blockZ + 0.5D;
           // side hit documentation is wrong!!! based on printing out mop.sideHit:
           // 2 = NORTH (face of block), 3 = SOUTH, 4 = WEST, 5 = EAST, 0 = BOTTOM, 1 = TOP
           switch (mop.sideHit) {
               // case 5: posX += 0.5D; break; // EAST
               // case 4: posX -= 0.515D; break; // WEST (a little extra to compensate for block
               // border, otherwise renders black)
               // case 3: posZ += 0.5D; break; // SOUTH
               // case 2: posZ -= 0.515D; break; // NORTH (a little extra to compensate for block
               // border, otherwise renders black)
             case SideHit.TOP:
               posY = mop.blockY + 1.0D;
               break;
             case SideHit.BOTTOM:
               posY = mop.blockY - 0.015D;
               break;
           }
         }
         // however, setting position as watched values and using these on the client works...
         // weird
         dataWatcher.updateObject(HIT_POS_X, (float) posX);
         dataWatcher.updateObject(HIT_POS_Y, (float) posY);
         dataWatcher.updateObject(HIT_POS_Z, (float) posZ);
         // unfortunately, this means the datawatcher values are no longer usable for getting the
         // block,
         // so need to store hitX/Y/Z separately for updating
         hitX = mop.blockX;
         hitY = mop.blockY;
         hitZ = mop.blockZ;
       } else if (canBreakBlock(
           block, block.getMaterial(), mop.blockX, mop.blockY, mop.blockZ, mop.sideHit)) {
         if (!worldObj.isRemote) {
           // don't drop items for players in creative mode
           boolean drop =
               (getThrower() instanceof EntityPlayer
                   ? !(((EntityPlayer) getThrower()).capabilities.isCreativeMode)
                   : true);
           worldObj.func_147480_a(mop.blockX, mop.blockY, mop.blockZ, drop);
           setDead();
         }
       } else if (block.getMaterial().blocksMovement()) {
         // Only call onEntityCollidedWithBlock if the whip didn't already grab or break the block
         block.onEntityCollidedWithBlock(worldObj, mop.blockX, mop.blockY, mop.blockZ, this);
       } else {
         block.onEntityCollidedWithBlock(worldObj, mop.blockX, mop.blockY, mop.blockZ, this);
         return;
       }
     }
   } else if (mop.entityHit != null) {
     worldObj.playSoundAtEntity(mop.entityHit, Sounds.WHIP_CRACK, 1.0F, 1.0F);
     boolean inflictDamage = true; // set to false if held item disarmed
     if (mop.entityHit instanceof EntityLivingBase) {
       EntityLivingBase target = (EntityLivingBase) mop.entityHit;
       if (getThrower() instanceof EntityPlayer) {
         EntityPlayer player = (EntityPlayer) getThrower();
         if (lootTarget(player, target)) {
           inflictDamage =
               (target instanceof IEntityLootable
                   ? ((IEntityLootable) target).isHurtOnTheft(player, getType())
                   : Config.getHurtOnSteal());
         } else if (target.getHeldItem() != null
             && ZSSPlayerSkills.get(player).hasSkill(SkillBase.parry)) {
           float chance = Parry.getDisarmModifier(player, target);
           float yaw = (target.rotationYaw - player.rotationYaw);
           while (yaw >= 360.0F) {
             yaw -= 360.0F;
           }
           while (yaw < 0.0F) {
             yaw += 360.0F;
           }
           yaw = Math.abs(Math.abs(yaw) - 180.0F);
           // should be impossible to disarm from more than 90 degrees to either side
           // however, rotationYaw does not seem to be the most reliable, but it's close enough
           float mod = 0.5F - (0.25F * (yaw / 45.0F));
           chance = 0.05F + (chance * mod); // max chance is 0.3F, min is 0.05F
           if (worldObj.rand.nextFloat() < chance) {
             WorldUtils.dropHeldItem(target);
             inflictDamage = false;
           }
         }
       }
       if (inflictDamage && target.getEquipmentInSlot(ArmorIndex.EQUIPPED_CHEST) != null) {
         inflictDamage = false; // cannot damage armor-wearing opponents
       }
     }
     if (inflictDamage) {
       mop.entityHit.attackEntityFrom(getDamageSource(), getDamage());
     }
     setDead();
   }
 }
  @SubscribeEvent
  public void onAttackEntity(AttackEntityEvent event) {
    if (event.entityLiving.worldObj.isRemote) return;

    EntityLivingBase attacker = event.entityLiving;
    EntityPlayer player = event.entityPlayer;
    Entity target = event.target;
    ItemStack stack = attacker.getEquipmentInSlot(0);
    if (stack != null && stack.getItem().onLeftClickEntity(stack, player, target)) return;

    if (target.canAttackWithItem()) {
      if (!target.hitByEntity(target)) {
        float damageAmount = TFC_MobData.STEVE_DAMAGE;
        if (stack != null) {
          damageAmount =
              (float)
                  player
                      .getEntityAttribute(SharedMonsterAttributes.attackDamage)
                      .getAttributeValue();
          // player.addChatMessage("Damage: " + i);
          if (damageAmount == 1.0f) {
            damageAmount = TFC_MobData.STEVE_DAMAGE;
            // i = player.inventory.getCurrentItem().getItem().getDamageVsEntity(target,
            // player.inventory.getCurrentItem());
          }
        }

        if (player.isPotionActive(Potion.damageBoost))
          damageAmount += 3 << player.getActivePotionEffect(Potion.damageBoost).getAmplifier();

        if (player.isPotionActive(Potion.weakness))
          damageAmount -= 2 << player.getActivePotionEffect(Potion.weakness).getAmplifier();

        int knockback = 0;
        float enchantmentDamage = 0;

        if (target instanceof EntityLiving) {
          enchantmentDamage =
              EnchantmentHelper.getEnchantmentModifierLiving(player, (EntityLiving) target);
          knockback += EnchantmentHelper.getKnockbackModifier(player, (EntityLiving) target);
        }

        if (player.isSprinting()) ++knockback;

        if (damageAmount > 0 || enchantmentDamage > 0) {
          boolean criticalHit =
              player.fallDistance > 0.0F
                  && !player.onGround
                  && !player.isOnLadder()
                  && !player.isInWater()
                  && !player.isPotionActive(Potion.blindness)
                  && player.ridingEntity == null
                  && target instanceof EntityLiving;

          if (criticalHit && damageAmount > 0)
            damageAmount += event.entity.worldObj.rand.nextInt((int) (damageAmount / 2 + 2));

          damageAmount += enchantmentDamage;
          boolean onFire = false;
          int fireAspect = EnchantmentHelper.getFireAspectModifier(player);

          if (target instanceof EntityLiving && fireAspect > 0 && !target.isBurning()) {
            onFire = true;
            target.setFire(1);
          }

          boolean entityAttacked =
              target.attackEntityFrom(DamageSource.causePlayerDamage(player), damageAmount);

          if (entityAttacked) {
            if (knockback > 0) {
              target.addVelocity(
                  -MathHelper.sin(player.rotationYaw * (float) Math.PI / 180.0F) * knockback * 0.5F,
                  0.1D,
                  MathHelper.cos(player.rotationYaw * (float) Math.PI / 180.0F) * knockback * 0.5F);
              player.motionX *= 0.6D;
              player.motionZ *= 0.6D;
              player.setSprinting(false);
            }

            if (criticalHit) player.onCriticalHit(target);

            if (enchantmentDamage > 0) player.onEnchantmentCritical(target);

            if (damageAmount >= 18) player.triggerAchievement(AchievementList.overkill);

            player.setLastAttacker(target);

            if (target instanceof EntityLiving)
              target.attackEntityFrom(DamageSource.causeThornsDamage(attacker), damageAmount);
          }

          ItemStack itemstack = player.getCurrentEquippedItem();
          Object object = target;

          if (target instanceof EntityDragonPart) {
            IEntityMultiPart ientitymultipart = ((EntityDragonPart) target).entityDragonObj;
            if (ientitymultipart instanceof EntityLiving) object = ientitymultipart;
          }

          if (itemstack != null && object instanceof EntityLiving) {
            itemstack.hitEntity((EntityLiving) object, player);
            if (itemstack.stackSize <= 0) player.destroyCurrentEquippedItem();
          }

          if (target instanceof EntityLivingBase) {
            player.addStat(StatList.damageDealtStat, Math.round(damageAmount * 10.0f));
            if (fireAspect > 0 && entityAttacked) target.setFire(fireAspect * 4);
            else if (onFire) target.extinguish();
          }

          player.addExhaustion(0.3F);
        }
      }
    }
    event.setCanceled(true);
  }