// /////////////////////////////////////////////////////////////////////////////////////
  // 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();
  }
 /**
  * Returned the entity with given name.
  *
  * @param p_name
  * @return element
  */
 public SpriteEntity getNamedEntity(String p_name) {
   if (p_name != null && !"".equals(p_name)) {
     for (SpriteEntity p : mergedEntities) {
       if (p_name.equalsIgnoreCase(p.getName())) {
         return p;
       }
     }
   }
   return null;
 }
 /**
  * Returned the element with given name.
  *
  * @param p_name
  * @return element
  */
 public Element getNamedElement(String p_name) {
   if (p_name != null && !"".equals(p_name)) {
     for (SpriteEntity p : mergedEntities) {
       if (p.getEntityType() == EntityType.ELEMENT && p_name.equalsIgnoreCase(p.getName())) {
         return (Element) p;
       }
     }
   }
   return null;
 }
 /**
  * Serialize given entities into one unique ByteBuffer.
  *
  * @param p_listEntities entities to serialize
  * @param p_clientSpecific FALSE=serialize only the common entities
  * @return buffer
  */
 public EasyBuffering serializeEntities(
     List<SpriteEntity> p_listEntities, boolean p_clientSpecific) {
   EasyBuffering b = new EasyBuffering();
   for (SpriteEntity entity : p_listEntities) {
     if (!entity.clientSpecific || p_clientSpecific) {
       entity.serialize(b);
     }
   }
   return b;
 }
 /**
  * Get a SpriteEntity list from a ByteBuffer.
  *
  * @param p_buffer
  * @return List<SpriteEntity>
  */
 public static List<SpriteEntity> deserializeEntities(EasyBuffering p_buffer) {
   List<SpriteEntity> entities = new ArrayList<SpriteEntity>();
   while (!p_buffer.eof()) {
     entities.add(SpriteEntity.deserialize(p_buffer));
   }
   return entities;
 }
 public void notifyLoadingMap(boolean p_loading) {
   spriteUpdating = p_loading;
   if (!p_loading) {
     // Loading is over. We have to keep all current entities in order to delete
     // them at the end of the scroll.
     // Except those are currently manipulated by script (ghost is TRUE)
     for (SpriteEntity entity : spriteEntities) {
       if (entity.isGhost()) {
         continue;
       } else {
         suspendedEntities.add(entity);
       }
     }
     spriteEntities.addAll(spriteEntitiesToAdd);
     spriteEntitiesToAdd.clear();
   }
 }
 /**
  * Prepare a list of modified entities on this frame.
  *
  * @return List<SpriteEntity>
  */
 private List<SpriteEntity> getModifiedEntities() {
   List<SpriteEntity> returned = new ArrayList<SpriteEntity>();
   for (SpriteEntity entity : spriteEntities) {
     int id = entity.getId();
     SpriteEntity backedUp = backupEntities.get(id);
     if (backedUp == null) {
       // If this entity hasn't been backed up, it's a new one ==> we
       // send it
       returned.add(entity);
     } else {
       // If this entity has been backed up, we must check whether it
       // has changed
       if (!backedUp.isSame(entity)) {
         returned.add(entity);
       }
       backupEntities.remove(id);
     }
   }
   // Send the entity which has been removed this frame
   for (SpriteEntity entity : backupEntities.values()) {
     entity.dying = true;
     entity.clientSpecific = false; // Send to all clients
     returned.add(entity);
   }
   backupEntities.clear();
   return returned;
 }
 public void clearSuspendedEntities() {
   for (SpriteEntity entity : suspendedEntities) {
     if (entity != null && !entity.isZildo()) {
       // Check if this entity is an element linked to Zildo
       if (entity.getEntityType().isElement() && ((Element) entity).isLinkedToZildo()) {
         continue;
       }
       if (entity.getEntityType().isPerso()) {
         EngineZildo.scriptManagement.stopPersoAction((Perso) entity);
       }
       deleteSprite(entity);
       if (walkableEntities.contains(entity)) {
         walkableEntities.remove(entity);
       }
     }
   }
   suspendedEntities.clear();
 }
 /**
  * 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);
     }
   }
 }
  /**
   * Spawn a character, and all his linked "persoSprites".
   *
   * @param perso
   */
  public void spawnPerso(Perso perso) {
    SpriteEntity entity = perso;
    entity.setScrX((int) perso.x);
    entity.setScrY((int) perso.y);

    spawnSprite(entity);

    // Spawn connected sprites
    if (perso.getPersoSprites().size() != 0) {
      for (Element element : perso.getPersoSprites()) {
        spawnSprite(element);
      }
    }

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

    EngineZildo.persoManagement.addPerso(perso);
  }
  /**
   * 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;
  }
 /**
  * Returns TRUE if the given entity is already spawned.
  *
  * @param p_entity
  * @return boolean
  */
 public boolean isSpawned(SpriteEntity p_entity) {
   return p_entity.getLinkVertices() != 0 || p_entity.getScrX() + p_entity.getScrY() > 0;
 }
 public void updateSprModel(SpriteEntity element) {
   SpriteModel spr = getSpriteBank(element.getNBank()).get_sprite(element.getNSpr());
   element.setSprModel(spr);
 }
  /**
   * 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;
  }