/**
   * Handle EntityTarget events.
   *
   * @param event The event to process
   */
  @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
  public void onEntityTarget(EntityTargetEvent event) {
    Entity entity = event.getEntity();
    Entity target = event.getTarget();

    if (!(entity instanceof Tameable) || !(target instanceof Player)) {
      return;
    }

    Player player = (Player) target;
    Tameable tameable = (Tameable) entity;

    if (!CombatUtils.isFriendlyPet(player, tameable)) {
      return;
    }

    // isFriendlyPet ensures that the Tameable is: Tamed, owned by a player, and the owner is in the
    // same party
    // So we can make some assumptions here, about our casting and our check
    if (!(Permissions.friendlyFire(player)
        && Permissions.friendlyFire((Player) tameable.getOwner()))) {
      event.setCancelled(true);
    }
  }
  /**
   * Handle EntityDamage events that involve modifying the event.
   *
   * @param event The event to modify
   */
  @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
  public void onEntityDamage(EntityDamageEvent event) {
    if (event instanceof FakeEntityDamageEvent) {
      return;
    }

    double damage = event.getDamage();

    if (damage <= 0) {
      return;
    }

    Entity entity = event.getEntity();

    if (entity.hasMetadata(mcMMO.customDamageKey)) {
      entity.removeMetadata(mcMMO.customDamageKey, plugin);
      return;
    }

    if (Misc.isNPCEntity(entity) || !entity.isValid() || !(entity instanceof LivingEntity)) {
      return;
    }

    LivingEntity livingEntity = (LivingEntity) entity;

    if (CombatUtils.isInvincible(livingEntity, damage)) {
      return;
    }

    DamageCause cause = event.getCause();

    if (livingEntity instanceof Player) {
      Player player = (Player) entity;
      McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);

      /* Check for invincibility */
      if (mcMMOPlayer.getGodMode()) {
        event.setCancelled(true);
        return;
      }

      switch (cause) {
        case FALL:
          if (Config.getInstance().getPreventXPAfterTeleport()
              && SkillUtils.calculateTimeLeft(
                      (long) mcMMOPlayer.getTeleportATS() * Misc.TIME_CONVERSION_FACTOR, 5, player)
                  > 0) {
            return;
          }

          AcrobaticsManager acrobaticsManager = mcMMOPlayer.getAcrobaticsManager();

          if (acrobaticsManager.canRoll()) {
            event.setDamage(acrobaticsManager.rollCheck(event.getDamage()));

            if (event.getDamage() == 0) {
              event.setCancelled(true);
              return;
            }
          }
          break;

        case BLOCK_EXPLOSION:
          MiningManager miningManager = mcMMOPlayer.getMiningManager();

          if (miningManager.canUseDemolitionsExpertise()) {
            event.setDamage(miningManager.processDemolitionsExpertise(event.getDamage()));

            if (event.getDamage() == 0) {
              event.setCancelled(true);
              return;
            }
          }
          break;

        default:
          break;
      }

      if (event.getDamage() >= 1) {
        mcMMOPlayer.actualizeRecentlyHurt();
      }
    } else if (livingEntity instanceof Tameable) {
      Tameable pet = (Tameable) livingEntity;
      AnimalTamer owner = pet.getOwner();

      if (Taming.canPreventDamage(pet, owner)) {
        Player player = (Player) owner;
        Wolf wolf = (Wolf) pet;

        TamingManager tamingManager = UserManager.getPlayer(player).getTamingManager();

        switch (cause) {
          case CONTACT:
          case FIRE:
          case LAVA:
            if (tamingManager.canUseEnvironmentallyAware()) {
              tamingManager.processEnvironmentallyAware(wolf, event.getDamage());
            }
            return;

          case FALL:
            if (tamingManager.canUseEnvironmentallyAware()) {
              event.setCancelled(true);
            }
            return;

          case ENTITY_ATTACK:
          case PROJECTILE:
            if (tamingManager.canUseThickFur()) {
              event.setDamage(Taming.processThickFur(wolf, event.getDamage()));

              if (event.getDamage() == 0) {
                event.setCancelled(true);
              }
            }
            return;

          case FIRE_TICK:
            if (tamingManager.canUseThickFur()) {
              Taming.processThickFurFire(wolf);
            }
            return;

          case MAGIC:
          case POISON:
          case WITHER:
            if (tamingManager.canUseHolyHound()) {
              Taming.processHolyHound(wolf, event.getDamage());
            }
            return;

          case BLOCK_EXPLOSION:
          case ENTITY_EXPLOSION:
          case LIGHTNING:
            if (tamingManager.canUseShockProof()) {
              event.setDamage(Taming.processShockProof(wolf, event.getDamage()));

              if (event.getDamage() == 0) {
                event.setCancelled(true);
              }
            }
            return;

          default:
            return;
        }
      }
    }
  }
  /**
   * Handle EntityDamageByEntity events that involve modifying the event.
   *
   * @param event The event to watch
   */
  @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
  public void onEntityDamageByEntity(EntityDamageByEntityEvent event) {
    if (event instanceof FakeEntityDamageByEntityEvent) {
      return;
    }

    double damage = event.getDamage();

    if (damage <= 0) {
      return;
    }

    Entity defender = event.getEntity();

    if (defender.hasMetadata(mcMMO.customDamageKey)) {
      defender.removeMetadata(mcMMO.customDamageKey, plugin);
      return;
    }

    if (Misc.isNPCEntity(defender) || !defender.isValid() || !(defender instanceof LivingEntity)) {
      return;
    }

    LivingEntity target = (LivingEntity) defender;

    if (CombatUtils.isInvincible(target, damage)) {
      return;
    }

    Entity attacker = event.getDamager();

    if (Misc.isNPCEntity(attacker)) {
      return;
    }

    if (attacker instanceof Projectile) {
      attacker = ((Projectile) attacker).getShooter();
    } else if (attacker instanceof Tameable) {
      AnimalTamer animalTamer = ((Tameable) attacker).getOwner();

      if (animalTamer != null && ((OfflinePlayer) animalTamer).isOnline()) {
        attacker = (Entity) animalTamer;
      }
    }

    if (defender instanceof Player && attacker instanceof Player) {
      Player defendingPlayer = (Player) defender;
      Player attackingPlayer = (Player) attacker;

      // We want to make sure we're not gaining XP or applying abilities when we hit ourselves
      if (defendingPlayer.equals(attackingPlayer)) {
        return;
      }

      if (PartyManager.inSameParty(defendingPlayer, attackingPlayer)
          && !(Permissions.friendlyFire(attackingPlayer)
              && Permissions.friendlyFire(defendingPlayer))) {
        event.setCancelled(true);
        return;
      }
    }

    CombatUtils.processCombatAttack(event, attacker, target);
  }