@ArenaEventHandler(suppressCastWarnings = true, priority = EventPriority.LOW)
  public void onEntityDamageEvent(EntityDamageEvent event) {
    if (!(event.getEntity() instanceof Player)) return;
    final TransitionOptions to = transitionOptions.getOptions(match.getMatchState());
    if (to == null) return;
    final PVPState pvp = to.getPVP();
    if (pvp == null) return;
    final ArenaPlayer target = BattleArena.toArenaPlayer((Player) event.getEntity());
    if (pvp == PVPState.INVINCIBLE) {
      /// all damage is cancelled
      target.setFireTicks(0);
      handler.setDamage(event, 0);
      event.setCancelled(true);
      return;
    }
    if (!(event instanceof EntityDamageByEntityEvent)) {
      return;
    }

    final Entity damagerEntity = ((EntityDamageByEntityEvent) event).getDamager();
    ArenaPlayer damager;
    switch (pvp) {
      case ON:
        ArenaTeam targetTeam = match.getTeam(target);
        if (targetTeam == null
            || !targetTeam.hasAliveMember(target)) // / We dont care about dead players
        return;
        damager = DmgDeathUtil.getPlayerCause(damagerEntity);
        if (damager == null) { // / damage from some source, its not pvp though. so we dont care
          return;
        }
        ArenaTeam t = match.getTeam(damager);
        if (t != null && t.hasMember(target)) { // / attacker is on the same team
          event.setCancelled(true);
        } else { /// different teams... lets make sure they can actually hit
          event.setCancelled(false);
        }
        break;
      case OFF:
        damager = DmgDeathUtil.getPlayerCause(damagerEntity);
        if (damager != null) { // / damage done from a player
          handler.setDamage(event, 0);
          event.setCancelled(true);
        }
        break;
      default:
        break;
    }
  }
  public static void teleportOut(
      PlayerHolder am,
      ArenaTeam team,
      ArenaPlayer player,
      TransitionOptions mo,
      int teamIndex,
      boolean insideArena,
      boolean onlyInMatch,
      boolean wipeInventory) {
    MatchParams mp = am.getParams();
    Location loc = null;
    ArenaLocation src = player.getCurLocation();
    final LocationType type;
    if (mo.hasOption(TransitionOption.TELEPORTTO)) {
      loc = mo.getTeleportToLoc();
      type = LocationType.CUSTOM;
    } else {
      type = LocationType.HOME;
      loc = player.getOldLocation();
      /// TODO
      /// This is a bit of a kludge, sometimes we are "teleporting them out"
      /// when they are already out... so need to rethink how this can happen and should it
      if (loc == null && src.getType() == LocationType.HOME) {
        loc = src.getLocation();
      }
    }
    player.clearOldLocation();
    if (loc == null) {
      Log.err(
          BattleArena.getNameAndVersion()
              + " Teleporting to a null location!  teleportTo="
              + mo.hasOption(TransitionOption.TELEPORTTO));
    }

    ArenaLocation dest = new ArenaLocation(AbstractAreaContainer.HOMECONTAINER, loc, type);
    ArenaPlayerTeleportEvent apte =
        new ArenaPlayerTeleportEvent(
            am.getParams().getType(), player, team, src, dest, TeleportDirection.OUT);
    movePlayer(player, apte, mp);
  }
  private static ArenaLocation getArenaLocation(
      PlayerHolder am, ArenaTeam team, ArenaPlayer player, TransitionOptions mo, int teamIndex) {
    final MatchParams mp = am.getParams();
    final boolean randomRespawn = mo.hasOption(TransitionOption.RANDOMRESPAWN);
    Location l;
    final boolean teleportWaitRoom = mo.shouldTeleportWaitRoom();
    final boolean teleportLobby = mo.shouldTeleportLobby();
    final boolean teleportSpectate = mo.shouldTeleportSpectate();
    final LocationType type;
    final PlayerHolder ph;
    if (Defaults.DEBUG_TRACE)
      Log.info(" teamindex = " + teamIndex + "   " + am.getClass().getSimpleName() + "  " + am);

    if (teleportWaitRoom) {
      if (mo.hasOption(TransitionOption.TELEPORTMAINWAITROOM)) {
        teamIndex = Defaults.MAIN_SPAWN;
      }
      ph = (am instanceof Match) ? ((Match) am).getArena().getWaitroom() : am;
      type = LocationType.WAITROOM;
      l = jitter(ph.getSpawn(teamIndex, randomRespawn), teamIndex);
    } else if (teleportLobby) {
      if (mo.hasOption(TransitionOption.TELEPORTMAINLOBBY)) {
        teamIndex = Defaults.MAIN_SPAWN;
      }
      ph = RoomController.getLobby(mp.getType());
      type = LocationType.LOBBY;
      l = jitter(RoomController.getLobbySpawn(teamIndex, mp.getType(), randomRespawn), 0);
    } else if (teleportSpectate) {
      ph = (am instanceof Match) ? ((Match) am).getArena().getSpectatorRoom() : am;
      type = LocationType.SPECTATE;
      l = jitter(ph.getSpawn(teamIndex, randomRespawn), teamIndex);
    } else { // They should teleportIn, aka to the Arena
      final Arena arena;
      if (am instanceof Arena) {
        arena = (Arena) am;
      } else if (am instanceof Match) {
        Match m = (Match) am;
        arena = m.getArena();
      } else {
        throw new IllegalStateException("[BA Error] Instance is " + am.getClass().getSimpleName());
      }
      ph = am;
      type = LocationType.ARENA;
      l = arena.getSpawn(teamIndex, false);
    }
    return new ArenaLocation(ph, l, type);
  }
  @ArenaEventHandler(priority = EventPriority.HIGH)
  public void onPlayerRespawn(PlayerRespawnEvent event, final ArenaPlayer p) {
    if (Defaults.DEBUG_TRACE) MessageUtil.sendMessage(p, " -onPlayerRespawn  t=" + p.getTeam());

    if (isWon()) {
      return;
    }
    final TransitionOptions mo = tops.getOptions(MatchState.ONDEATH);

    if (mo == null) return;

    if (respawns) {
      final boolean randomRespawn = mo.randomRespawn();
      /// Lets cancel our death respawn timer
      Integer timer = deathTimer.get(p.getName());
      if (timer != null) {
        Bukkit.getScheduler().cancelTask(timer);
      }
      final Location loc;
      final ArenaTeam t = getTeam(p);
      if (mo.hasAnyOption(
          TransitionOption.TELEPORTLOBBY,
          TransitionOption.TELEPORTMAINLOBBY,
          TransitionOption.TELEPORTWAITROOM,
          TransitionOption.TELEPORTMAINWAITROOM)) {
        final int index = t.getIndex();
        if (mo.hasOption(TransitionOption.TELEPORTLOBBY)) {
          loc = RoomController.getLobbySpawn(index, getParams().getType(), randomRespawn);
        } else if (mo.hasOption(TransitionOption.TELEPORTMAINLOBBY)) {
          loc =
              RoomController.getLobbySpawn(
                  Defaults.MAIN_SPAWN, getParams().getType(), randomRespawn);
        } else if (mo.hasOption(TransitionOption.TELEPORTMAINWAITROOM)) {
          loc = this.getWaitRoomSpawn(Defaults.MAIN_SPAWN, randomRespawn);
        } else {
          loc = this.getWaitRoomSpawn(index, randomRespawn);
        }
        /// Should we respawn the player to the team spawn after a certain amount of time
        if (mo.hasOption(TransitionOption.RESPAWNTIME)) {
          int id =
              Bukkit.getScheduler()
                  .scheduleSyncDelayedTask(
                      BattleArena.getSelf(),
                      new Runnable() {
                        @Override
                        public void run() {
                          Integer id = respawnTimer.remove(p.getName());
                          Bukkit.getScheduler().cancelTask(id);
                          Location loc =
                              getTeamSpawn(
                                  index,
                                  tops.hasOptionAt(
                                      MatchState.ONSPAWN, TransitionOption.RANDOMRESPAWN));
                          TeleportController.teleport(p.getPlayer(), loc);
                        }
                      },
                      mo.getRespawnTime() * 20);
          respawnTimer.put(p.getName(), id);
        }
      } else {
        loc = getTeamSpawn(getTeam(p), randomRespawn);
      }

      event.setRespawnLocation(loc);
      /// For some reason, the player from onPlayerRespawn Event isnt the one in the main thread, so
      // we need to
      /// resync before doing any effects
      final Match am = this;
      Bukkit.getScheduler()
          .scheduleSyncDelayedTask(
              BattleArena.getSelf(),
              new Runnable() {
                public void run() {
                  ArenaTeam t = getTeam(p);
                  PerformTransition.transition(am, MatchState.ONDEATH, p, t, false);
                  PerformTransition.transition(am, MatchState.ONSPAWN, p, t, false);
                  if (respawnsWithClass) {
                    ArenaClass ac = null;
                    if (p.getPreferredClass() != null) {
                      ac = p.getPreferredClass();
                    } else if (p.getCurrentClass() != null) {
                      ac = p.getCurrentClass();
                    }
                    if (ac != null) {
                      ArenaClassController.giveClass(p, ac);
                    }
                  }
                  if (keepsInventory) {
                    psc.restoreMatchItems(p);
                  }
                  if (woolTeams) {
                    TeamUtil.setTeamHead(t.getIndex(), p);
                  }
                }
              });
    } else { /// This player is now out of the system now that we have given the ondeath effects
      Location l =
          tops.hasOptionAt(MatchState.ONLEAVE, TransitionOption.TELEPORTTO)
              ? tops.getOptions(MatchState.ONLEAVE).getTeleportToLoc()
              : oldlocs.get(p.getName());
      if (l != null) event.setRespawnLocation(l);
    }
  }