Package games.stendhal.client.gui.j2d.entity

Source Code of games.stendhal.client.gui.j2d.entity.Entity2DView

/* $Id: Entity2DView.java,v 1.55 2011/01/05 18:17:27 kiheru Exp $ */
/***************************************************************************
*                   (C) Copyright 2003-2010 - Stendhal                    *
***************************************************************************
***************************************************************************
*                                                                         *
*   This program is free software; you can redistribute it and/or modify  *
*   it under the terms of the GNU General Public License as published by  *
*   the Free Software Foundation; either version 2 of the License, or     *
*   (at your option) any later version.                                   *
*                                                                         *
***************************************************************************/
package games.stendhal.client.gui.j2d.entity;

//
//

import games.stendhal.client.IGameScreen;
import games.stendhal.client.stendhal;
import games.stendhal.client.entity.ActionType;
import games.stendhal.client.entity.EntityChangeListener;
import games.stendhal.client.entity.IEntity;
import games.stendhal.client.entity.Inspector;
import games.stendhal.client.entity.User;
import games.stendhal.client.gui.j2DClient;
import games.stendhal.client.gui.styled.cursor.StendhalCursor;
import games.stendhal.client.sprite.AnimatedSprite;
import games.stendhal.client.sprite.Sprite;
import games.stendhal.client.sprite.SpriteStore;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

/**
* The 2D view of an entity.
*/
public abstract class Entity2DView implements EntityView, EntityChangeListener {
  /**
   * The entity this view is for.
   */
  protected IEntity entity;
 
  /**
   * The entity drawing composite.
   */
  protected Composite entityComposite;
 
  /**
   * Model values affecting animation.
   */
  protected boolean animatedChanged;

  /**
   * The position value changed.
   */
  protected boolean positionChanged;

  /**
   * Model values affecting visual representation changed.
   */
  protected boolean representationChanged;

  /**
   * The visibility value changed.
   */
  protected boolean visibilityChanged;
 
  /**
   * The screen X coordinate.
   */
  protected int x;

  /**
   * The X alignment offset.
   */
  protected int xoffset;

  /**
   * The screen Y coordinate.
   */
  protected int y;

  /**
   * The Y alignment offset.
   */
  protected int yoffset;

  /**
   * The entity image (or current one at least).
   */
  private Sprite sprite;

  /**
   * Whether this view is contained.
   */
  private boolean contained;
 
  /**
   * Some model value changed.
   */
  private boolean changed;
 
  /**
   * Flag for detecting that the view has been released <code>true</code> if
   * the view has been released, <code>false</code> otherwise.
   */
  private volatile boolean released = false;

  public void initialize(final IEntity entity) {
    if (entity == null) {
      throw new IllegalArgumentException("entity must not be null");
    }
    this.entity = entity;

    x = 0;
    y = 0;
    xoffset = 0;
    yoffset = 0;

    entityComposite = AlphaComposite.SrcOver;
    contained = false;
    animatedChanged = false;
    changed = true;
    positionChanged = true;
    visibilityChanged = true;
    representationChanged = true;

    entity.addChangeListener(this);
  }

  //
  // Entity2DView
  //

  /**
   * Handle entity changes
   */
  protected void applyChanges() {
    if (changed) {
      update();
      changed = false;
    }
  }

  /**
   * Build a list of entity specific actions. <strong>NOTE: The first entry
   * should be the default.</strong>
   *
   * @param list
   *            The list to populate.
   */
  protected void buildActions(final List<String> list) {
    list.add(ActionType.LOOK.getRepresentation());
  }

  /**
   * Rebuild the representation using the base entity.
   *
   * @param entity the eEntity to build the representation for
   */
  protected void buildRepresentation(IEntity entity) {
    setSprite(SpriteStore.get().getSprite(translate(entity.getType())));
  }

  /**
   * Calculate sprite image offset for the entity.
   *
   * @param entity entity
   * @param swidth
   *            The sprite width (in pixels).
   * @param sheight
   *            The sprite height (in pixels).
   */
  protected void calculateOffset(IEntity entity, final int swidth, final int sheight) {
    final Rectangle2D area = entity.getArea();

    calculateOffset(swidth, sheight, (int) (IGameScreen.SIZE_UNIT_PIXELS * area.getWidth()),
        (int) (IGameScreen.SIZE_UNIT_PIXELS * area.getHeight()));
  }

  /**
   * Calculate sprite image offset (default centered). Sub-classes may
   * override this to change alignment.
   *
   * @param swidth
   *            The sprite width (in pixels).
   * @param sheight
   *            The sprite height (in pixels).
   * @param ewidth
   *            The entity width (in pixels).
   * @param eheight
   *            The entity height (in pixels).
   */
  protected void calculateOffset(final int swidth, final int sheight,
      final int ewidth, final int eheight) {
    /*
     * X alignment centered, Y alignment centered
     */
    xoffset = (ewidth - swidth) / 2;
    yoffset = (eheight - sheight) / 2;
  }

  /**
   * Mark this as changed. This will force the <code>update()</code> method to
   * be called.
   */
  protected void markChanged() {
    changed = true;
  }

  /**
   * Draw the entity.
   *
   * @param g2d
   *            The graphics to drawn on.
   */ 
  public void draw(final Graphics2D g2d) {
    applyChanges();

    final Rectangle r = getDrawingArea();

    if (isContained()) {
      r.setLocation(0, 0);
    } else {
      if (!isOnScreen(g2d, r)) {
        return;
      }
    }

    final Composite oldComposite = g2d.getComposite();

    try {
      g2d.setComposite(entityComposite);
      draw(g2d, r.x, r.y, r.width, r.height);
    } finally {
      g2d.setComposite(oldComposite);
    }

    drawEffect(g2d, r.x, r.y, r.width, r.height);

  }
 
  private boolean isOnScreen(Graphics2D g2d, Rectangle r) {
    Rectangle clip = g2d.getClipBounds();
    return ((clip == null) || r.intersects(g2d.getClipBounds()));
  }

  /**
   * Draw the entity.
   *
   * @param g2d
   *            The graphics context.
   * @param x
   *            The drawn X coordinate.
   * @param y
   *            The drawn Y coordinate.
   * @param width
   *            The drawn entity width.
   * @param height
   *            The drawn entity height.
   */
  protected void draw(final Graphics2D g2d, final int x, final int y,
      final int width, final int height) {
    drawEntity(g2d, x, y, width, height);

    if (stendhal.SHOW_COLLISION_DETECTION) {
      g2d.setColor(Color.blue);
      g2d.drawRect(x, y, width, height);

      g2d.setColor(Color.green);
      g2d.draw(entity.getArea());
    }
  }

  /**
   * Draw the effect part. This is drawn independent of the visibility
   * setting.
   *
   * @param g2d
   *            The graphics context.
   * @param x
   *            The drawn X coordinate.
   * @param y
   *            The drawn Y coordinate.
   * @param width
   *            The drawn entity width.
   * @param height
   *            The drawn entity height.
   */
  protected void drawEffect(final Graphics2D g2d, final int x, final int y,
      final int width, final int height) {
  }

  /**
   * Draw the base entity part.
   *
   * @param g2d
   *            The graphics context.
   * @param x
   *            The drawn X coordinate.
   * @param y
   *            The drawn Y coordinate.
   * @param width
   *            The drawn entity width.
   * @param height
   *            The drawn entity height.
   */
  protected void drawEntity(final Graphics2D g2d, final int x, final int y,
      final int width, final int height) {
    getSprite().draw(g2d, x, y);
  }

  /**
   * Draw the top layer parts of an entity. This will be on down after all
   * other game layers are rendered.
   *
   * @param g2d
   *            The graphics to drawn on.
   */
  public void drawTop(final Graphics2D g2d) {
    final Rectangle r = getArea();

    if (isContained()) {
      r.setLocation(0, 0);
    } else {
      if (!isOnScreen(g2d, r)) {
        return;
      }
    }

    final Composite oldComposite = g2d.getComposite();

    try {
      g2d.setComposite(entityComposite);
      drawTop(g2d, r.x, r.y, r.width, r.height);
    } finally {
      g2d.setComposite(oldComposite);
    }
  }

  /**
   * Draw the entity.
   *
   * @param g2d
   *            The graphics context.
   * @param x
   *            The drawn X coordinate.
   * @param y
   *            The drawn Y coordinate.
   * @param width
   *            The drawn entity width.
   * @param height
   *            The drawn entity height.
   */
  protected void drawTop(final Graphics2D g2d, final int x, final int y,
      final int width, final int height) {
  }

  /**
   * Get the screen area this is drawn in. NOTE: This only covers the area for
   * the main sprite.
   *
   * @return The area this draws in.
   */
  public Rectangle getArea() {
    return new Rectangle(getX() + getXOffset(), getY() + getYOffset(),
        getWidth(), getHeight());
  }
 
  /**
   * Get the drawn area used by the entity. Used for checking if the entity
   * should be drawn. By default the same as getArea(), but extending classes
   * can override it to return a different area if they need it.
   * 
   * @return The area this draws in.
   */
  protected Rectangle getDrawingArea() {
    return getArea();
  }

  /**
   * Get the class resource sub-path. The is the base sprite image name,
   * relative to <code>translate()</code>.
   *
   * @return The resource path.
   */
  protected String getClassResourcePath() {
    String rpath = entity.getEntityClass();

    if (rpath != null) {
      final String subclass = entity.getEntitySubclass();

      if (subclass != null) {
        rpath += "/" + subclass;
      }
    }

    return rpath;
  }

  /**
   * Get the drawing composite.
   *
   * @return The drawing composite.
   */
  protected AlphaComposite getComposite() {
    final int visibility = getVisibility();
    if (visibility >= 100) {
      return AlphaComposite.SrcOver;
    } else if (visibility <= 0) {
      return AlphaComposite.Dst;
    } else {
      return AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
          visibility / 100.0f);
    }
  }

  /**
   * Get the height.
   *
   * @return The height (in pixels).
   */
  public int getHeight() {
    return IGameScreen.SIZE_UNIT_PIXELS;
  }

  /**
   * Get the sprite image for this entity.
   *
   * @return The image representation.
   */
  public Sprite getSprite() {
    return sprite;
  }

  /**
   * Get the entity's visibility.
   *
   * @return The visibility value (0-100).
   */
  protected int getVisibility() {
    return entity.getVisibility();
  }

  /**
   * Get the width.
   *
   * @return The width (in pixels).
   */
  public int getWidth() {
    return IGameScreen.SIZE_UNIT_PIXELS;
  }

  /**
   * Get the entity's X coordinate.
   *
   * @return The X coordinate (in pixels).
   */
  protected int getX() {
    return x;
  }

  /**
   * Get the X offset alignment adjustment.
   *
   * @return The X offset (in pixels).
   */
  protected int getXOffset() {
    return xoffset;
  }

  /**
   * Get the entity's Y coordinate.
   *
   * @return The Y coordinate (in pixels).
   */
  protected int getY() {
    return y;
  }

  /**
   * Get the Y offset alignment adjustment.
   *
   * @return The Y offset (in pixels).
   */
  protected int getYOffset() {
    return yoffset;
  }

  /**
   * Determines on top of which other entities this entity should be drawn.
   * Entities with a high Z index will be drawn on top of ones with a lower Z
   * index.
   *
   * Also, players can only interact with the topmost entity.
   *
   * @return The drawing index.
   */
  public int getZIndex() {
    return 10000;
  }

  /**
   * Determine if this view is currently animatable.
   *
   * @return <code>true</code> if animating enabled.
   */
  protected boolean isAnimating() {
    // Allow sprites to animate by default
    return true;
  }

  /**
   * Determine if this view is contained, and should render in a compressed
   * (it's defined) area without clipping anything important.
   *
   * @return <code>true</code> if contained.
   */
  public boolean isContained() {
    return contained;
  }

  /**
   * Reorder the actions list (if needed). Please use as last resort.
   *
   * @param list
   *            The list to reorder.
   */
  protected void reorderActions(final List<String> list) {
  }

  /**
   * Set the sprite's animation state (if applicable).
   *
   * @param sprite
   *            The sprite.
   */
  protected void setAnimation(final Sprite sprite) {
    if (sprite instanceof AnimatedSprite) {
      final AnimatedSprite asprite = (AnimatedSprite) sprite;

      if (isAnimating()) {
        asprite.start();
      } else {
        asprite.stop();
        asprite.reset();
      }
    }
  }

  /**
   * Set whether this view is contained, and should render in a compressed
   * (it's defined) area without clipping anything important.
   *
   * @param contained
   *            <code>true</code> if contained.
   */
  public void setContained(final boolean contained) {
    this.contained = contained;
  }

  /**
   * Set the content inspector for this entity (if needed).
   *
   * @param inspector
   *            The inspector.
   */
  public void setInspector(final Inspector inspector) {
  }

  /**
   * Set the sprite.
   *
   * @param sprite
   *            The sprite.
   */
  protected void setSprite(final Sprite sprite) {
    setAnimation(sprite);
    animatedChanged = false;

    this.sprite = sprite;
  }

  /**
   * Translate a resource name into it's sprite image path.
   *
   * @param name
   *            The resource name.
   *
   * @return The full resource name.
   */
  protected String translate(final String name) {
    return "data/sprites/" + name + ".png";
  }

  /**
   * Handle updates.
   */
  protected void update() {
    IEntity entity = this.entity;
    if (entity == null) {
      return;
    }
    if (representationChanged) {
      buildRepresentation(entity);
      representationChanged = false;
    }

    if (positionChanged) {
      x = (int) (IGameScreen.SIZE_UNIT_PIXELS * entity.getX());
      y = (int) (IGameScreen.SIZE_UNIT_PIXELS * entity.getY());
      positionChanged = false;
    }

    if (visibilityChanged) {
      entityComposite = getComposite();
      visibilityChanged = false;
    }

    if (animatedChanged) {
      setAnimation(getSprite());
      animatedChanged = false;
    }
  }


  //
  // EntityChangeListener
  //

  /**
   * An entity was changed.
   *
   * @param entity
   *            The entity that was changed.
   * @param property
   *            The property identifier.
   */
  public void entityChanged(final IEntity entity, final Object property) {
    changed = true;

    if (property == IEntity.PROP_ANIMATED) {
      animatedChanged = true;
    } else if (property == IEntity.PROP_POSITION) {
      positionChanged = true;
    } else if (property == IEntity.PROP_VISIBILITY) {
      visibilityChanged = true;
    }
  }

  //
  // EntityView
  //

  /**
   * Get the list of actions.
   *
   * @return The list of actions.
   */
  public final String[] getActions() {
    final List<String> list = new ArrayList<String>();

    buildActions(list);

    /*
     * Special admin options
     */
    if (User.isAdmin()) {
      list.add(ActionType.ADMIN_INSPECT.getRepresentation());
      list.add(ActionType.ADMIN_DESTROY.getRepresentation());
      if (!this.isContained()) {
        list.add(ActionType.ADMIN_ALTER.getRepresentation());
      }
    }

    reorderActions(list);

    return list.toArray(new String[list.size()]);
  }

  /**
   * Get the view's entity.
   *
   * @return The view's entity.
   */
  public IEntity getEntity() {
    return entity;
  }

  /**
   * Determine if this entity can be moved (e.g. via dragging).
   *
   * @return <code>true</code> if the entity is movable.
   */
  public boolean isMovable() {
    return false;
  }

  /**
   * Perform the default action.
   */
  public void onAction() {
    onAction(ActionType.LOOK);
  }


  /**
   * Perform the default action unless it is not safe.
   *
   * @return <code>true</code> if the action was performed, <code>false</code> if nothing was done
   */
  public boolean onHarmlessAction() {
    onAction();
    return true;
  }

  /**
   * Perform an action.
   *
   * @param at
   *            The action.
   */
  public void onAction(final ActionType at) {
    IEntity entity = this.entity;
    // return prematurely if view has already been released
    if (isReleased()) {
      Logger.getLogger(Entity2DView.class).debug(
          "View " + this + " already released - action not processed: " + at);
      return;
    }

    final int id = entity.getID().getObjectID();

    switch (at) {
    case LOOK:
    case ADMIN_INSPECT:
    case ADMIN_DESTROY:
      at.send(at.fillTargetInfo(entity.getRPObject()));
      break;

    case ADMIN_ALTER:
      j2DClient.get().setChatLine("/alter #" + id + " ");
      break;

    default:
      Logger.getLogger(Entity2DView.class).error(
          "Unknown action not processed: " + at);
      break;
    }
  }

  /**
   * is this entity interactive so that the player can click or move it?
   *
   * @return true if the player can interact with it, false otherwise.
   */
  public boolean isInteractive() {
    return true;
  }

  /**
   * Release any view resources. This view should not be used after this is
   * called.
   */
  public void release() {
    entity.removeChangeListener(this);
    released = true;
  }
 
  /**
   * Check if the view has been released. Usually a released view should not
   * be used anymore, but in certain situations it may be preferable to send
   * an action for a deleted entity to the server anyway. That can happen for
   * example with items in bag, where a new view gets created after an item
   * has changed, but the new view represents the same item stack as the old
   * one.
   *
   * @return <code>true<code> if the view has been released,
   *   <code>false</code> otherwise
   */
  protected boolean isReleased() {
    return released;
  }

  /**
   * gets the mouse cursor image to use for this entity
   *
   * @return StendhalCursor
   */
  public StendhalCursor getCursor() {
    String cursorName = entity.getCursor();
    return StendhalCursor.valueOf(cursorName, StendhalCursor.UNKNOWN);
  }
}
TOP

Related Classes of games.stendhal.client.gui.j2d.entity.Entity2DView

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.