/**
   * 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);
  }
  /**
   * Spawn a sprite with minimal requirements:
   *
   * <ul>
   *   <li>build an entity with given parameters
   *   <li>add it to the sprite engine
   *
   * @param x
   * @param y
   * @param p_foreground TRUE=above the other sprites (GUI) / FALSE=in-game sprite
   * @param p_reverse reverse flag
   * @param p_adjustPos TRUE=center the element / FALSE=no location adjustment
   */
  public SpriteEntity spawnSprite(
      SpriteDescription desc,
      int x,
      int y,
      boolean p_foreground,
      Reverse p_reverse,
      boolean p_adjustPos) {

    int nBank = desc.getBank();
    int nSpr = desc.getNSpr();

    if (desc.isPushable()) { // || nSpr == 179) {
      // Particular sprite (Block that Zildo can move, chest...)
      return spawnElement(
          nBank, nSpr, x, y, 0, Reverse.NOTHING, Rotation.NOTHING); // + spr.getTaille_y() / 2 - 3,
      // 0);
    }

    // SpriteEntity informations
    SpriteEntity entity;
    ElementDescription elemDesc = null;
    if (desc instanceof ElementDescription) {
      elemDesc = (ElementDescription) desc;
    }
    boolean weapon = false;
    if (elemDesc != null) {
      ItemKind kind = ItemKind.fromDesc(elemDesc);
      weapon = kind != null && kind.isWeapon();
    }
    if (!p_foreground && weapon) {
      entity = new ElementWeapon(x, y);
    } else if (desc == ElementDescription.LAUNCHER1) {
      entity = new ElementLauncher(x, y);
      entity.setAjustedX(x);
      entity.setAjustedY(y);
    } else if (desc.getBank() == SpriteBank.BANK_GEAR) {
      entity = new ElementGear(x, y);
      entity.setAjustedX(x);
      entity.setAjustedY(y);
    } else if (desc == ElementDescription.QUAD1) {
      EngineZildo.multiplayerManagement.spawnQuad(x, y);
      return null;
    } else {
      entity = new SpriteEntity(x, y, true);
      int adjustX = 0;
      int adjustY = 0;
      SpriteModel spr = getSpriteBank(desc.getBank()).get_sprite(nSpr);
      adjustX = -(spr.getTaille_x() >> 1);
      if (p_adjustPos) {
        adjustY = -(spr.getTaille_y() >> 1);
      } else {
        adjustY = -spr.getTaille_y();
      }
      entity.setAjustedX(x + adjustX);
      entity.setAjustedY(y + adjustY);
    }

    entity.setNSpr(nSpr);
    entity.setNBank(nBank);
    entity.setForeground(p_foreground);

    entity.reverse = p_reverse;

    spawnSprite(entity);

    // Store walkable entities
    if (ElementDescription.isPlatform(desc)) {
      entity.initMover();
      walkableEntities.add(entity);
    }

    return entity;
  }