Entity.java

/*
 * Copyright © 2012 Nokia Corporation. All rights reserved.
 * Nokia and Nokia Connecting People are registered trademarks of Nokia Corporation.
 * Oracle and Java are trademarks or registered trademarks of Oracle and/or its
 * affiliates. Other product and company names mentioned herein may be trademarks
 * or trade names of their respective owners.
 * See LICENSE.TXT for license information.
 */
package com.nokia.example.battletank.game.entities;

import com.nokia.example.battletank.game.Point;
import com.nokia.example.battletank.game.Resources;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import javax.microedition.lcdui.game.Sprite;

public abstract class Entity {

    public static final int DIRECTION_UP = 0;
    public static final int DIRECTION_RIGHT = 1;
    public static final int DIRECTION_DOWN = 2;
    public static final int DIRECTION_LEFT = 3;
    protected Sprite sprite = null;
    protected volatile int direction = DIRECTION_UP;
    private volatile boolean updateSpriteDirection = true;
    protected final Point position = new Point(0, 0);
    private volatile boolean updateSpritePosition = true;
    public final int width;
    public final int height;
    protected final Resources resources;

    /**
     * Creates a game entity.
     *
     * @param width Width of entity
     * @param height Height of entity
     * @param resources Resources object
     */
    protected Entity(int width, int height, Resources resources) {
        this.width = width;
        this.height = height;
        this.resources = resources;
    }

    public int getDirection() {
        return direction;
    }

    public int getX() {
        return position.x;
    }

    public int getY() {
        return position.y;
    }

    public int getCenterX() {
        return position.x + width / 2;
    }

    public int getCenterY() {
        return position.y + height / 2;
    }

    /**
     * Returns true if the entity collides with the given entity
     *
     * @param e Entity that maybe collides
     * @return True if the entities collide, false otherwise
     */
    public boolean collidesWith(Entity e) {
        return collidesWith(e.getX(), e.getY(), e.width, e.height);
    }

    /**
     * Returns true if the entity collides with the given area. Colliding
     * requires the area to be solid.
     *
     * @param x X coordinate of the area
     * @param y Y coordinate of the area
     * @param w Width of the area
     * @param h Height of the area
     * @return True if the entity collides with the area.
     */
    public boolean collidesWith(int x, int y, int w, int h) {
        if (!isSolid()) {
            return false;
        }
        return overlaps(x, x + w, position.x, position.x + width) && overlaps(y,
            y + h, position.y, position.y + height);
    }

    public Sprite getSprite() {
        if (sprite == null) {
            sprite = createSprite();
        }
        return sprite;
    }

    /**
     * Refreshes the direction and position of the entity.
     */
    public void refresh() {
        if (updateSpriteDirection) {
            updateSpriteDirection = false;
            switch (direction) {
                case DIRECTION_UP:
                    sprite.setTransform(Sprite.TRANS_NONE);
                    break;
                case DIRECTION_DOWN:
                    sprite.setTransform(Sprite.TRANS_ROT180);
                    break;
                case DIRECTION_LEFT:
                    sprite.setTransform(Sprite.TRANS_ROT270);
                    break;
                case DIRECTION_RIGHT:
                    sprite.setTransform(Sprite.TRANS_ROT90);
                    break;
            }
        }
        if (updateSpritePosition) {
            updateSpritePosition = false;
            sprite.setPosition(position.x, position.y);
        }
    }

    public void update() {
    }

    /**
     * Writes the entity to a DataOutputStream
     *
     * @param dout DataOutputStream where the entity is written
     * @throws IOException
     */
    public void writeTo(DataOutputStream dout)
        throws IOException {
        dout.writeInt(direction);
        position.writeTo(dout);
    }

    protected void readFrom(DataInputStream din)
        throws IOException {
        direction = din.readInt();
        Point p = Point.readFrom(din);
        position.x = p.x;
        position.y = p.y;
    }

    protected boolean changeDirection(int newDirection) {
        boolean changed = direction != newDirection;
        direction = newDirection;
        if (changed) {
            updateSpriteDirection = true;
            updateSpritePosition = true;
        }
        return changed;
    }

    protected abstract Sprite createSprite();

    protected boolean move(int distance) {
        int x = position.x;
        int y = position.y;

        switch (direction) {
            case DIRECTION_UP:
                y -= distance;
                break;
            case DIRECTION_DOWN:
                y += distance;
                break;
            case DIRECTION_LEFT:
                x -= distance;
                break;
            case DIRECTION_RIGHT:
                x += distance;
                break;
        }
        return setPosition(x, y);
    }

    protected boolean setPosition(int x, int y) {
        position.x = x;
        position.y = y;
        updateSpritePosition = true;
        return true;
    }

    protected boolean isSolid() {
        return true;
    }

    private boolean overlaps(int startA, int endA, int startB, int endB) {
        return startA < startB ? endA > startB : endB > startA;
    }
}