/**
  * Check if 'quelPerso' is colliding with the given element.
  *
  * @param x
  * @param y
  * @param quelElement
  * @param quelPerso
  * @param rayon
  * @return Perso
  */
 private boolean checkCollisionOnPerso(
     int x, int y, Element quelElement, Perso quelPerso, int rayon) {
   if (quelPerso.isZildo()
       && quelElement != null
       && quelElement.getDesc() instanceof ElementDescription) {
     ElementDescription d = (ElementDescription) quelElement.getDesc();
     if (d.isPushable() && quelElement.vx + quelElement.vy != 0f) {
       return false;
     }
   }
   int tx = (int) quelPerso.getX();
   int ty = (int) quelPerso.getY();
   PersoDescription descToCompare = quelPerso.getDesc();
   int rayonPersoToCompare = rayon;
   if (descToCompare != null) {
     rayonPersoToCompare = descToCompare.getRadius();
   }
   // Do we have a Perso in parameters ?
   Perso perso = null;
   if (quelElement != null && quelElement.getEntityType().isPerso()) {
     perso = (Perso) quelElement;
   }
   if (EngineZildo.collideManagement.checkCollisionCircles(
       x, y, tx, ty, rayon, rayonPersoToCompare)) {
     if (perso != null && perso.isZildo() && perso.linkedSpritesContains(quelPerso)) {
       // Collision entre Zildo et l'objet qu'il porte dans les mains => on laisse
     } else if (quelElement == null || quelElement.getLinkedPerso() != quelPerso) {
       return true;
     }
   }
   // No collision
   return false;
 }
 /**
  * Translate every entities with the given offset.
  *
  * @param p_offset
  * @param p_translateZildo TRUE=Translate Zildo too / FALSE=Don't touch him
  */
 public void translateEntities(Point p_offset, boolean p_translateZildo) {
   for (SpriteEntity entity : spriteEntities) {
     if (!entity.clientSpecific && (!entity.isZildo() || p_translateZildo)) {
       if (entity.getEntityType().isElement()) {
         Element e = (Element) entity;
         if (e.getLinkedPerso() != null && e.getLinkedPerso().isZildo()) {
           continue;
         }
       }
       entity.x += p_offset.x;
       entity.y += p_offset.y;
       entity.setAjustedX(entity.getAjustedX() + p_offset.x);
       entity.setAjustedY(entity.getAjustedY() + p_offset.y);
     }
   }
 }
  // /////////////////////////////////////////////////////////////////////////////////////
  // clearSpritesWithoutZildo
  // /////////////////////////////////////////////////////////////////////////////////////
  // -Delete every sprites in the entities list
  // -Clean the sort array
  // -Reinitializes local camera
  // /////////////////////////////////////////////////////////////////////////////////////
  public void clearSprites(boolean includingZildo) {
    // Get Zildo to avoid to remove it
    Perso zildo = EngineZildo.persoManagement.getZildo();

    // Destroy entities
    List<SpriteEntity> listToRemove = new ArrayList<SpriteEntity>();

    for (SpriteEntity entity : spriteEntities) {
      if (entity != null) {
        boolean canDelete = true;
        if (entity == zildo) {
          // This IS Zildo ! So we keep him
          canDelete = includingZildo;
        } else if (entity.getEntityType().isElement()) {
          Element element = (Element) entity;
          if (zildo != null && element.getLinkedPerso() == zildo) {
            // This is an element related to zildo, so we can't
            // remove it now
            canDelete = includingZildo;
          }
        } else if (entity.getEntityType().isPerso()) {
          canDelete = false;
        }
        if (canDelete) {
          listToRemove.add(entity);
        }
      }
    }

    for (SpriteEntity entity : listToRemove) {
      deleteSprite(entity);
    }

    walkableEntities.clear();
  }
  /**
   * Find an element near a given one.<br>
   * WARNING: it's an unoptimized method, contrary to {@link #collideSprite(int, int, Element)}, but
   * it's used only once when player press action key. So it's acceptable now.
   *
   * @param x
   * @param y
   * @param quelElement
   * @param radius
   * @return Element
   */
  public Element collideElement(
      int x, int y, Element quelElement, int radius, SpriteDescription... expectedDesc) {
    Perso perso = null;
    if (quelElement != null && quelElement.getEntityType().isPerso()) {
      perso = (Perso) quelElement;
    }

    for (SpriteEntity entity : spriteEntities) {
      if (entity.getEntityType().isElement()) {
        if (entity != quelElement) {
          int tx = (int) entity.x;
          int ty = (int) entity.y;
          if (EngineZildo.collideManagement.checkCollisionCircles(x, y, tx, ty, radius, radius)) {
            if (perso != null && perso.isZildo() && perso.linkedSpritesContains(entity)) {
              // Collision between hero and object he's carrying => let it go
            } else if (quelElement == null || quelElement.getLinkedPerso() != entity) {
              // Check that found element is one of expected ones
              if (expectedDesc != null && expectedDesc.length > 0) {
                for (SpriteDescription sDesc : expectedDesc) {
                  if (sDesc == entity.getDesc()) {
                    return (Element) entity;
                  }
                }
                continue; // Check next one
              }
              Element elem = (Element) entity;
              // Found an element, but is it really the most pertinent ? (ex:shadow)
              if (elem.getLinkedPerso() != null) {
                return elem.getLinkedPerso();
              }
              return elem;
            }
          }
        }
      }
    }
    return null;
  }
  /**
   * Do sprite's stuff
   *
   * <ul>
   *   <li>animate sprites & persos
   *   <li>
   *   <li>delete if need (the only place to do this)
   * </ul>
   *
   * @param p_blockMoves TRUE=don't animate perso except Zildo
   */
  public void updateSprites(boolean p_blockMoves) {
    spriteUpdating = true;
    spriteEntitiesToAdd.clear();

    // Backup current entities, if backup buffer is empty
    if (EngineZildo.game.multiPlayer && backupEntities.size() == 0) {
      for (SpriteEntity entity : spriteEntities) {
        SpriteEntity cloned = entity.clone();
        backupEntities.put(cloned.getId(), cloned);
      }
    }

    sprColli.initFrame(spriteEntities);
    persoColli.initFrame(EngineZildo.persoManagement.tab_perso);

    boolean blockNPC = p_blockMoves || temporaryBlocked;

    // Do perso animations
    // Mandatory to do that first, because one perso can be connected to
    // other sprites
    int compt = EngineZildo.compteur_animation; // % (3 * 20);
    for (SpriteEntity entity : spriteEntities) {
      if (entity.getEntityType().isPerso()) {
        Perso perso = (Perso) entity;
        boolean allowedToMoveAndCollide =
            !blockNPC || /*perso.getInfo() == PersoInfo.ZILDO ||*/ perso.getFollowing() != null;
        if (allowedToMoveAndCollide) {
          // Animate persos
          perso.animate(compt);
        }
        perso.finaliseComportement(compt);
        updateSprModel(perso);
        SpriteModel spr = perso.getSprModel();
        if (allowedToMoveAndCollide) {
          perso.manageCollision();
        }

        if (!perso.isZildo()) {
          // Non-zildo sprite haven't same way to display correctly (bad...)
          perso.setAjustedX(perso.getAjustedX() - (spr.getTaille_x() / 2));
          perso.setAjustedY(perso.getAjustedY() - (spr.getTaille_y() - 3));
        }
      }
    }

    List<SpriteEntity> toDelete = new ArrayList<SpriteEntity>();
    for (Iterator<SpriteEntity> it = spriteEntities.iterator(); it.hasNext(); ) {
      SpriteEntity entity = it.next();
      if (toDelete.contains(entity)) {
        continue; // It's a dead one
      }
      Element element = null;
      // Calcul physique du sprite
      if (entity.dying) {
        toDelete.add(entity);
      } else if (entity.getEntityType().isEntity()) {
        entity.animate();
      } else if (entity.getEntityType().isElement()) {
        // X, vX, aX, ...
        element = (Element) entity;
        if (!blockNPC || element.isLinkedToZildo()) {
          element.animate();
          if (element.dying) {
            SpriteEntity linkedOne = element.getLinkedPerso();
            // L'élément est arrivé au terme de son existence : on le supprime de la liste
            if (linkedOne != null && EntityType.ELEMENT == linkedOne.getEntityType()) {
              toDelete.add(linkedOne);
            }
            toDelete.add(element);
          } else {
            if (element.isVisible()) {
              updateSprModel(element);
            }
          }
        }
      }
    }

    // Remove what need to
    for (SpriteEntity entity : toDelete) {
      deleteSprite(entity);
      if (entity.getEntityType().isPerso()) {
        persoColli.notifyDeletion((Perso) entity);
      } else {
        sprColli.notifyDeletion(entity);
      }
    }

    spriteUpdating = false;
    spriteEntities.addAll(spriteEntitiesToAdd);
  }