Ghost.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.ghosts;

import java.io.IOException;
import java.util.Random;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;

/**
 * This class implements ghost behavior.
 *
 * The ghost sets its position on the screen and waits for some time
 * to allow the player to aim and shoot. During the waiting period the ghost moves a little.
 * When the ghost is shot and hit, it changes its appearance for a little while before
 * it is repositioned. After a successful hit the waiting period is reduced to
 * increase difficulty.
 */
public class Ghost
{
    private Size parentSize;
    private Image ghostImage;
    private Image ghostShotImage;
    private Point ghostPosition = new Point();
    private Size  ghostSize = new Size();
    private final long initialGhostRelocationPeriod = 10000; //ms
    private long ghostRelocationPeriod = 0; //ms
    private long lastGhostRelocationTime = 0;
    private final int ghostJitterLimit = 10;
    private int ghostJitterPosition = 0;
    private int ghostJitterDirection = 1;
    private boolean shot = false;
    private Random rand;

    public Ghost(Size parentSize) throws IOException
    {
        this.parentSize = parentSize;
        this.ghostImage = Image.createImage("/images/Ghost.png");
        this.ghostShotImage = Image.createImage("/images/GhostShot.png");

        this.rand = new Random(System.currentTimeMillis());
        this.ghostRelocationPeriod = this.initialGhostRelocationPeriod;
        this.ghostSize.width = this.ghostImage.getWidth();
        this.ghostSize.height = this.ghostImage.getHeight();
    }

    public void paint(Graphics graphics)
    {
        // Draw the ghost
        if (!this.shot)
        {
            graphics.drawImage(
                this.ghostImage,
                this.ghostPosition.x + ghostJitterPosition,
                this.ghostPosition.y,
                Graphics.TOP | Graphics.LEFT
            );
        }
        else
        {
            graphics.drawImage(
                this.ghostShotImage,
                this.ghostPosition.x,
                this.ghostPosition.y,
                Graphics.TOP | Graphics.LEFT
            );
        }
    }

    /**
     * Sets ghost's shot status.
     * When a ghost is shot, its appearance changes. The image of the shot ghost stays
     * on the screen for a short time before it is repositioned.
     */
    public void setShot(boolean shot)
    {
        this.shot = shot;
        // Plan ghost relocation to one second from now
        this.lastGhostRelocationTime = (System.currentTimeMillis() - this.ghostRelocationPeriod) + 1000;
        // Reduce relocation period to increase difficulty
        if (this.ghostRelocationPeriod > 1500)
        {
            this.ghostRelocationPeriod -= 500;
        }
    }


    /**
     * Returns the percentage of the waiting period used already.
     */
    public int getPercentOfTimeUsed()
    {
        return (int)(100 *
                     (double)(System.currentTimeMillis() - this.lastGhostRelocationTime) / (double)this.ghostRelocationPeriod
                    );
    }

    /**
     * This method should be called periodically from the game's world-clock handler.
     */
    public boolean handleClockTick()
    {
        boolean relocated = false;
        if ((System.currentTimeMillis() - this.lastGhostRelocationTime) > this.ghostRelocationPeriod)
        {
            // Waiting time has elapsed. Relocate the ghost.
            if (!this.shot)
            {
                // Ghost was not hit - reset waiting time to initial value.
                this.ghostRelocationPeriod = this.initialGhostRelocationPeriod;
            }
            this.shot = false;
            this.relocateGhost();
            this.lastGhostRelocationTime = System.currentTimeMillis();
            relocated = true;
        }

        // Update the ghost's jittering position to let it move in
        // small range from left to right.
        if (this.ghostJitterPosition > this.ghostJitterLimit)
        {
            this.ghostJitterDirection = -1;
        }
        else if (this.ghostJitterPosition < -1 * this.ghostJitterLimit)
        {
            this.ghostJitterDirection = 1;
        }
        this.ghostJitterPosition += this.ghostJitterDirection;
        return relocated;
    }

    /**
     * Checks whether the given point is inside of the ghost's rectangle.
     */
    public boolean hitTest(int x, int y)
    {
        return (x > this.ghostPosition.x)
               && (x < this.ghostPosition.x + this.ghostSize.width)
               && (y > this.ghostPosition.y)
               && (y < this.ghostPosition.y + this.ghostSize.height);
    }

    private void relocateGhost()
    {
        int newPosX = this.ghostSize.width
                      + this.rand.nextInt(this.parentSize.width - 3 * this.ghostSize.width);
        int newPosY = this.ghostSize.height / 5
                      + this.rand.nextInt(this.parentSize.height - 6 * this.ghostSize.height / 5);
        this.ghostPosition.x = newPosX;
        this.ghostPosition.y = newPosY;
    }
}