Tank.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 com.nokia.example.battletank.game.TankImage;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import javax.microedition.lcdui.game.Sprite;

public abstract class Tank
    extends Entity {

    private static final int SPAWN_DURATION = 50;
    private static final int PROTECTED_DURATION = 100;
    private static final int DESTOYED_DURATION = 10;
    private static final int BULLET_RANGE = 20;
    private static final int BULLET_SPEED = 1;
    private static final int LOADING_DURATION = 15;
    public static final int SPAWNING = 0;
    public static final int PROTECTED = 1;
    public static final int NORMAL = 2;
    public static final int DESTROYED = 3;
    private volatile int state = DESTROYED;
    private volatile boolean updateSpriteState = true;
    private int stateCounter = 0;
    public volatile boolean moving = false;
    private boolean movedLastRound = false;
    protected Point spawnPoint;
    private Listener listener;
    private int loadingCounter = 0;

    public Tank(Point spawnPoint, Resources r, Listener li) {
        super(r.gridSizeInPixels * 4, r.gridSizeInPixels * 4, r);
        this.spawnPoint = spawnPoint;
        this.listener = li;
    }

    protected void setState(int newState) {
        state = newState;
        stateCounter = getStateDuration(newState);
        updateSpriteState = true;
    }

    protected int getStateDuration(int state) {
        switch (state) {
            case SPAWNING:
                return SPAWN_DURATION;
            case PROTECTED:
                return PROTECTED_DURATION;
            case DESTROYED:
                return DESTOYED_DURATION;
        }
        return 0;
    }

    protected int getState() {
        return state;
    }

    public void update() {
        if (loadingCounter > 0) {
            loadingCounter--;
        }
        updateState();
    }

    private void updateState() {
        if (stateCounter > 0) {
            stateCounter--;
            if (stateCounter == 0) {
                switch (state) {
                    case SPAWNING:
                        setState(PROTECTED);
                        break;
                    case PROTECTED:
                        setState(NORMAL);
                        break;
                    case DESTROYED:
                        listener.destroyed(this);
                }
            }
        }
    }

    protected void fire() {
        if (loadingCounter > 0) {
            return;
        }
        loadingCounter = getLoadingDuration();
        listener.fireBullet(this);
    }

    protected int getLoadingDuration() {
        return LOADING_DURATION;
    }

    public int getBulletDirection() {
        return direction;
    }

    public int getBulletRange() {
        return BULLET_RANGE;
    }

    public int getBulletSpeed() {
        return BULLET_SPEED;
    }

    protected boolean setPosition(int x, int y) {
        if (!(listener.collidesWithAnything(this, x, y, width, height))) {
            return super.setPosition(x, y);
        }
        return false;
    }

    public boolean canMove() {
        return state != SPAWNING && state != DESTROYED;
    }

    protected boolean move() {
        final int speed = getSpeed();
        for (int i = 0; i < speed; i++) {
            if (listener.isInWater(this) && movedLastRound) {
                movedLastRound = false;
            }
            else if (!move(resources.tankMovementInPixels)) {
                return false;
            }
        }
        return true;
    }

    protected abstract int getSpeed();

    protected boolean move(int distance) {
        movedLastRound = true;
        return super.move(distance);
    }

    protected boolean changeDirection(int newDirection) {
        if (direction != newDirection) {
            int x, y;
            if (newDirection == DIRECTION_UP || newDirection == DIRECTION_DOWN) {
                x = resources.roundToGrid(position.x);
                y = position.y;
            }
            else {
                x = position.x;
                y = resources.roundToGrid(position.y);
            }
            setPosition(x, y);
        }
        return super.changeDirection(newDirection);
    }

    public void spawn() {
        setPosition(spawnPoint.x, spawnPoint.y);
        changeDirection(DIRECTION_UP);
        setState(SPAWNING);
    }

    public boolean destroy() {
        if (state == NORMAL) {
            setState(DESTROYED);
            return true;
        }
        return false;
    }

    protected Sprite createSprite() {
        return new Sprite(resources.spawn, width, height);
    }

    public void refresh() {
        super.refresh();
        if (updateSpriteState) {
            updateSpriteState = false;
            switch (state) {
                case SPAWNING:
                    sprite.setImage(resources.spawn, width, height);
                    sprite.setFrameSequence(Resources.SPAWNING_ANIM_SEQ);
                    sprite.setVisible(true);
                    break;
                case PROTECTED:
                    sprite.setImage(getImage().image, width, height);
                    sprite.setFrameSequence(getImage().protectedSeq);
                    sprite.setVisible(true);
                    break;
                case NORMAL:
                    sprite.setImage(getImage().image, width, height);
                    sprite.setFrameSequence(getImage().normalSeq);
                    sprite.setVisible(true);
                    break;
                case DESTROYED:
                    sprite.setVisible(false);
                    break;
            }
        }
        if (moving || state == SPAWNING) {
            sprite.nextFrame();
        }
    }

    protected abstract TankImage getImage();

    public boolean collidesWith(Bullet bullet) {
        if (bullet.firedBy == this) {
            return false;
        }
        return super.collidesWith(bullet);
    }

    protected boolean isSolid() {
        return state != SPAWNING && state != DESTROYED;
    }

    public boolean isAlive() {
        return state != DESTROYED;
    }

    public static interface Listener {

        void destroyed(Tank tank);

        boolean isInWater(Tank tank);

        boolean collidesWithAnything(Tank tank, int x, int y, int w, int h);

        void fireBullet(Tank tank);
    }

    public void writeTo(DataOutputStream dout)
        throws IOException {
        super.writeTo(dout);
        dout.writeInt(state);
        dout.writeInt(stateCounter);
    }

    public void readFrom(DataInputStream din)
        throws IOException {
        super.readFrom(din);
        state = din.readInt();
        stateCounter = din.readInt();
    }
}