Mega Code Archive

 
Categories / Java / J2ME
 

Tumbleweed game

/*  Title:  J2ME Games With MIDP2  Authors:  Carol Hamer  Publisher:  Apress  ISBN:   1590593820  */ import javax.microedition.media.*; import javax.microedition.media.control.*; import java.util.Random; import javax.microedition.lcdui.game.*; import javax.microedition.midlet.*; import javax.microedition.lcdui.*; /**  * This is the main class of the tumbleweed game.  *   * @author Carol Hamer  */ public class Jump extends MIDlet implements CommandListener {   //---------------------------------------------------------   //   commands   /**    * the command to end the game.    */   private Command myExitCommand = new Command("Exit", Command.EXIT, 99);   /**    * the command to start moving when the game is paused.    */   private Command myGoCommand = new Command("Go", Command.SCREEN, 1);   /**    * the command to pause the game.    */   private Command myPauseCommand = new Command("Pause", Command.SCREEN, 1);   /**    * the command to start a new game.    */   private Command myNewCommand = new Command("Play Again", Command.SCREEN, 1);   /**    * The command to start/pause the music. (This command may appear in a menu)    */   private Command myMusicCommand = new Command("Music", Command.SCREEN, 2);   //---------------------------------------------------------   //   game object fields   /**    * the the canvas that all of the game will be drawn on.    */   private JumpCanvas myCanvas;   //---------------------------------------------------------   //   thread fields   /**    * the thread that advances the cowboy.    */   private GameThread myGameThread;   /**    * The class that plays music if the user wants.    */   //private MusicMaker myMusicMaker;   private ToneControlMusicMaker myMusicMaker;   /**    * The thread tha sets tumbleweeds in motion at random intervals.    */   private TumbleweedThread myTumbleweedThread;   /**    * if the user has paused the game.    */   private boolean myGamePause;   /**    * if the game is paused because it is hidden.    */   private boolean myHiddenPause;   //-----------------------------------------------------   //    initialization and game state changes   /**    * Initialize the canvas and the commands.    */   public Jump() {     try {       myCanvas = new JumpCanvas(this);       myCanvas.addCommand(myExitCommand);       myCanvas.addCommand(myMusicCommand);       myCanvas.addCommand(myPauseCommand);       myCanvas.setCommandListener(this);     } catch (Exception e) {       errorMsg(e);     }   }   /**    * Switch the command to the play again command.    */   void setNewCommand() {     myCanvas.removeCommand(myPauseCommand);     myCanvas.removeCommand(myGoCommand);     myCanvas.addCommand(myNewCommand);   }   /**    * Switch the command to the go command.    */   private void setGoCommand() {     myCanvas.removeCommand(myPauseCommand);     myCanvas.removeCommand(myNewCommand);     myCanvas.addCommand(myGoCommand);   }   /**    * Switch the command to the pause command.    */   private void setPauseCommand() {     myCanvas.removeCommand(myNewCommand);     myCanvas.removeCommand(myGoCommand);     myCanvas.addCommand(myPauseCommand);   }   //----------------------------------------------------------------   //  implementation of MIDlet   // these methods may be called by the application management   // software at any time, so we always check fields for null   // before calling methods on them.   /**    * Start the application.    */   public void startApp() throws MIDletStateChangeException {     try {       if (myCanvas != null) {         myCanvas.start();         myCanvas.flushKeys();         systemStartThreads();       }     } catch (Exception e) {       errorMsg(e);     }   }   /**    * stop and throw out the garbage.    */   public void destroyApp(boolean unconditional)       throws MIDletStateChangeException {     try {       stopThreads();       myCanvas = null;       System.gc();     } catch (Exception e) {       errorMsg(e);     }   }   /**    * request the game to pause. This method is called by the application    * management software, not in response to a user pausing the game.    */   public void pauseApp() {     try {       if (myCanvas != null) {         setGoCommand();         systemPauseThreads();       }     } catch (Exception e) {       errorMsg(e);     }   }   //----------------------------------------------------------------   //  implementation of CommandListener   /*    * Respond to a command issued on the Canvas. (either reset or exit).    */   public void commandAction(Command c, Displayable s) {     try {       if (c == myGoCommand) {         myCanvas.removeCommand(myGoCommand);         myCanvas.addCommand(myPauseCommand);         myCanvas.flushKeys();         userStartThreads();       } else if (c == myPauseCommand) {         myCanvas.removeCommand(myPauseCommand);         myCanvas.addCommand(myGoCommand);         userPauseThreads();       } else if (c == myNewCommand) {         myCanvas.removeCommand(myNewCommand);         myCanvas.addCommand(myPauseCommand);         System.gc();         myCanvas.reset();         myCanvas.flushKeys();         myHiddenPause = false;         myGamePause = false;         startThreads();       } else if (c == myMusicCommand) {         if (myMusicMaker != null) {           myMusicMaker.toggle();           myCanvas.repaint();           myCanvas.serviceRepaints();         }       } else if ((c == myExitCommand)/* || (c == Alert.DISMISS_COMMAND)*/) {         try {           destroyApp(false);           notifyDestroyed();         } catch (MIDletStateChangeException ex) {         }       }     } catch (Exception e) {       errorMsg(e);     }   }   //-------------------------------------------------------   //  thread methods   /**    * start up all of the game's threads. Creates them if necessary. to be    * called when the user hits the go command.    */   private synchronized void userStartThreads() throws Exception {     myGamePause = false;     if (!myHiddenPause) {       startThreads();     }   }   /**    * start up all of the game's threads. Creates them if necessary. used by    * showNotify    */   synchronized void systemStartThreads() throws Exception {     myHiddenPause = false;     if (!myGamePause) {       startThreads();     }   }   /**    * start up all of the game's threads. Creates them if necessary. internal    * version. note: if this were synchronized, whould it cause deadlock?    */   private void startThreads() throws Exception {     if (myGameThread == null) {       myGameThread = new GameThread(myCanvas);       myGameThread.start();     } else {       myGameThread.resumeGame();     }     if (myTumbleweedThread == null) {       myTumbleweedThread = new TumbleweedThread(myCanvas);       myTumbleweedThread.start();     } else {       myTumbleweedThread.resumeGame();     }     if (myMusicMaker == null) {       myMusicMaker = new ToneControlMusicMaker();       //myMusicMaker = new MusicMaker();       myMusicMaker.start();     } else {       myMusicMaker.resumeGame();     }   }   /**    * Pause all of the threads started by this game. to be called when the user    * hits the pause command.    */   synchronized void userPauseThreads() {     myGamePause = true;     pauseThreads();   }   /**    * Pause all of the threads started by this game. used by hideNotify    */   void systemPauseThreads() {     myHiddenPause = true;     pauseThreads();   }   /**    * start up all of the game's threads. Creates them if necessary. internal    * version. note: if this were synchronized, whould it cause deadlock?    */   private void pauseThreads() {     if (myGameThread != null) {       myGameThread.pauseGame();     }     if (myTumbleweedThread != null) {       myTumbleweedThread.pauseGame();     }     if (myMusicMaker != null) {       myMusicMaker.pauseGame();     }   }   /**    * Stop all of the threads started by this game and delete them as they are    * no longer usable.    */   private synchronized void stopThreads() {     if (myGameThread != null) {       myGameThread.requestStop();     }     if (myTumbleweedThread != null) {       myTumbleweedThread.requestStop();     }     if (myMusicMaker != null) {       myMusicMaker.requestStop();     }     myGameThread = null;     myTumbleweedThread = null;     myMusicMaker = null;   }   //-------------------------------------------------------   //  error methods   /**    * Converts an exception to a message and displays the message..    */   void errorMsg(Exception e) {     if (e.getMessage() == null) {       errorMsg(e.getClass().getName());     } else {       errorMsg(e.getClass().getName() + ":" + e.getMessage());     }   }   /**    * Displays an error message alert if something goes wrong.    */   void errorMsg(String msg) {     Alert errorAlert = new Alert("error", msg, null, AlertType.ERROR);     errorAlert.setCommandListener(this);     errorAlert.setTimeout(Alert.FOREVER);     Display.getDisplay(this).setCurrent(errorAlert);   } } /**  * This class is the display of the game.  *   * @author Carol Hamer  */ class JumpCanvas extends javax.microedition.lcdui.game.GameCanvas {   //---------------------------------------------------------   //   dimension fields   //  (constant after initialization)   /**    * the height of the green region below the ground.    */   static final int GROUND_HEIGHT = 32;   /**    * a screen dimension.    */   static final int CORNER_X = 0;   /**    * a screen dimension.    */   static final int CORNER_Y = 0;   /**    * a screen dimension.    */   static int DISP_WIDTH;   /**    * a screen dimension.    */   static int DISP_HEIGHT;   /**    * a font dimension.    */   static int FONT_HEIGHT;   /**    * the default font.    */   static Font FONT;   /**    * a font dimension.    */   static int SCORE_WIDTH;   /**    * The width of the string that displays the time, saved for placement of    * time display.    */   static int TIME_WIDTH;   /**    * color constant    */   public static final int BLACK = 0;   /**    * color constant    */   public static final int WHITE = 0xffffff;   //---------------------------------------------------------   //   game object fields   /**    * a handle to the display.    */   private Display myDisplay;   /**    * a handle to the MIDlet object (to keep track of buttons).    */   private Jump myJump;   /**    * the LayerManager that handles the game graphics.    */   private JumpManager myManager;   /**    * whether or not the game has ended.    */   private boolean myGameOver;   /**    * the player's score.    */   private int myScore = 0;   /**    * How many ticks we start with.    */   private int myInitialGameTicks = 950;   /**    * this is saved to determine if the time string needs to be recomputed.    */   private int myOldGameTicks = myInitialGameTicks;   /**    * the number of game ticks that have passed.    */   private int myGameTicks = myOldGameTicks;   /**    * we save the time string to avoid recreating it unnecessarily.    */   private static String myInitialString = "1:00";   /**    * we save the time string to avoid recreating it unnecessarily.    */   private String myTimeString = myInitialString;   //-----------------------------------------------------   //    gets/sets   /**    * This is called when the game ends.    */   void setGameOver() {     myGameOver = true;     myJump.userPauseThreads();   }   /**    * @return a handle to the tumbleweed objects.    */   Tumbleweed[] getTumbleweeds() {     return (myManager.getTumbleweeds());   }   //-----------------------------------------------------   //    initialization and game state changes   /**    * Constructor sets the data, performs dimension calculations, and creates    * the graphical objects.    */   public JumpCanvas(Jump midlet) throws Exception {     super(false);     myDisplay = Display.getDisplay(midlet);     myJump = midlet;     // calculate the dimensions     DISP_WIDTH = getWidth();     DISP_HEIGHT = getHeight();     Display disp = Display.getDisplay(myJump);     if (disp.numColors() < 256) {       throw (new Exception("game requires 256 shades"));     }     if ((DISP_WIDTH < 150) || (DISP_HEIGHT < 170)) {       throw (new Exception("Screen too small"));     }     if ((DISP_WIDTH > 250) || (DISP_HEIGHT > 250)) {       throw (new Exception("Screen too large"));     }     FONT = getGraphics().getFont();     FONT_HEIGHT = FONT.getHeight();     SCORE_WIDTH = FONT.stringWidth("Score: 000");     TIME_WIDTH = FONT.stringWidth("Time: " + myInitialString);     if (myManager == null) {       myManager = new JumpManager(CORNER_X, CORNER_Y + FONT_HEIGHT * 2,           DISP_WIDTH, DISP_HEIGHT - FONT_HEIGHT * 2 - GROUND_HEIGHT);     }   }   /**    * This is called as soon as the application begins.    */   void start() {     myGameOver = false;     myDisplay.setCurrent(this);     repaint();   }   /**    * sets all variables back to their initial positions.    */   void reset() {     myManager.reset();     myScore = 0;     myGameOver = false;     myGameTicks = myInitialGameTicks;     myOldGameTicks = myInitialGameTicks;     repaint();   }   /**    * clears the key states.    */   void flushKeys() {     getKeyStates();   }   /**    * pause the game when it's hidden.    */   protected void hideNotify() {     try {       myJump.systemPauseThreads();     } catch (Exception oe) {       myJump.errorMsg(oe);     }   }   /**    * When it comes back into view, unpause it.    */   protected void showNotify() {     try {       myJump.systemStartThreads();     } catch (Exception oe) {       myJump.errorMsg(oe);     }   }   //-------------------------------------------------------   //  graphics methods   /**    * paint the game graphic on the screen.    */   public void paint(Graphics g) {     // clear the screen:     g.setColor(WHITE);     g.fillRect(CORNER_X, CORNER_Y, DISP_WIDTH, DISP_HEIGHT);     // color the grass green     g.setColor(0, 255, 0);     g.fillRect(CORNER_X, CORNER_Y + DISP_HEIGHT - GROUND_HEIGHT,         DISP_WIDTH, DISP_HEIGHT);     // paint the layer manager:     try {       myManager.paint(g);     } catch (Exception e) {       myJump.errorMsg(e);     }     // draw the time and score     g.setColor(BLACK);     g.setFont(FONT);     g.drawString("Score: " + myScore, (DISP_WIDTH - SCORE_WIDTH) / 2,         DISP_HEIGHT + 5 - GROUND_HEIGHT, g.TOP | g.LEFT);     g.drawString("Time: " + formatTime(), (DISP_WIDTH - TIME_WIDTH) / 2,         CORNER_Y + FONT_HEIGHT, g.TOP | g.LEFT);     // write game over if the game is over     if (myGameOver) {       myJump.setNewCommand();       // clear the top region:       g.setColor(WHITE);       g.fillRect(CORNER_X, CORNER_Y, DISP_WIDTH, FONT_HEIGHT * 2 + 1);       int goWidth = FONT.stringWidth("Game Over");       g.setColor(BLACK);       g.setFont(FONT);       g.drawString("Game Over", (DISP_WIDTH - goWidth) / 2, CORNER_Y           + FONT_HEIGHT, g.TOP | g.LEFT);     }   }   /**    * a simple utility to make the number of ticks look like a time...    */   public String formatTime() {     if ((myGameTicks / 16) + 1 != myOldGameTicks) {       myTimeString = "";       myOldGameTicks = (myGameTicks / 16) + 1;       int smallPart = myOldGameTicks % 60;       int bigPart = myOldGameTicks / 60;       myTimeString += bigPart + ":";       if (smallPart / 10 < 1) {         myTimeString += "0";       }       myTimeString += smallPart;     }     return (myTimeString);   }   //-------------------------------------------------------   //  game movements   /**    * Tell the layer manager to advance the layers and then update the display.    */   void advance() {     myGameTicks--;     myScore += myManager.advance(myGameTicks);     if (myGameTicks == 0) {       setGameOver();     }     // paint the display     try {       paint(getGraphics());       flushGraphics();     } catch (Exception e) {       myJump.errorMsg(e);     }   }   /**    * Respond to keystrokes.    */   public void checkKeys() {     if (!myGameOver) {       int keyState = getKeyStates();       if ((keyState & LEFT_PRESSED) != 0) {         myManager.setLeft(true);       }       if ((keyState & RIGHT_PRESSED) != 0) {         myManager.setLeft(false);       }       if ((keyState & UP_PRESSED) != 0) {         myManager.jump();       }     }   } } /**  * This class draws the background grass.  *   * @author Carol Hamer  */ class Grass extends TiledLayer {   //---------------------------------------------------------   //    dimension fields   //  (constant after initialization)   /**    * The width of the square tiles that make up this layer..    */   static final int TILE_WIDTH = 20;   /**    * This is the order that the frames should be displayed for the animation.    */   static final int[] FRAME_SEQUENCE = { 2, 3, 2, 4 };   /**    * This gives the number of squares of grass to put along the bottom of the    * screen.    */   static int COLUMNS;   /**    * After how many tiles does the background repeat.    */   static final int CYCLE = 5;   /**    * the fixed Y coordinate of the strip of grass.    */   static int TOP_Y;   //---------------------------------------------------------   //    instance fields   /**    * Which tile we are currently on in the frame sequence.    */   private int mySequenceIndex = 0;   /**    * The index to use in the static tiles array to get the animated tile..    */   private int myAnimatedTileIndex;   //---------------------------------------------------------   //   gets / sets   /**    * Takes the width of the screen and sets my columns to the correct    * corresponding number    */   static int setColumns(int screenWidth) {     COLUMNS = ((screenWidth / 20) + 1) * 3;     return (COLUMNS);   }   //---------------------------------------------------------   //   initialization   /**    * constructor initializes the image and animation.    */   public Grass() throws Exception {     super(setColumns(JumpCanvas.DISP_WIDTH), 1, Image         .createImage("/images/grass.png"), TILE_WIDTH, TILE_WIDTH);     TOP_Y = JumpManager.DISP_HEIGHT - TILE_WIDTH;     setPosition(0, TOP_Y);     myAnimatedTileIndex = createAnimatedTile(2);     for (int i = 0; i < COLUMNS; i++) {       if ((i % CYCLE == 0) || (i % CYCLE == 2)) {         setCell(i, 0, myAnimatedTileIndex);       } else {         setCell(i, 0, 1);       }     }   }   //---------------------------------------------------------   //   graphics   /**    * sets the grass back to its initial position..    */   void reset() {     setPosition(-(TILE_WIDTH * CYCLE), TOP_Y);     mySequenceIndex = 0;     setAnimatedTile(myAnimatedTileIndex, FRAME_SEQUENCE[mySequenceIndex]);   }   /**    * alter the background image appropriately for this frame..    *     * @param left    *            whether or not the player is moving left    */   void advance(int tickCount) {     if (tickCount % 2 == 0) { // slow the animation down a little       mySequenceIndex++;       mySequenceIndex %= 4;       setAnimatedTile(myAnimatedTileIndex,           FRAME_SEQUENCE[mySequenceIndex]);     }   } } /**  * This class contains the loop that keeps the game running.  *   * @author Carol Hamer  */ class GameThread extends Thread {   //---------------------------------------------------------   //   fields   /**    * Whether or not the main thread would like this thread to pause.    */   private boolean myShouldPause;   /**    * Whether or not the main thread would like this thread to stop.    */   private boolean myShouldStop;   /**    * A handle back to the graphical components.    */   private JumpCanvas myJumpCanvas;   /**    * The System.time of the last screen refresh, used to regulate refresh    * speed.    */   private long myLastRefreshTime;   //----------------------------------------------------------   //   initialization   /**    * standard constructor.    */   GameThread(JumpCanvas canvas) {     myJumpCanvas = canvas;   }   //----------------------------------------------------------   //   utilities   /**    * Get the amount of time to wait between screen refreshes. Normally we wait    * only a single millisecond just to give the main thread a chance to update    * the keystroke info, but this method ensures that the game will not    * attempt to show too many frames per second.    */   private long getWaitTime() {     long retVal = 1;     long difference = System.currentTimeMillis() - myLastRefreshTime;     if (difference < 75) {       retVal = 75 - difference;     }     return (retVal);   }   //----------------------------------------------------------   //   actions   /**    * pause the game.    */   void pauseGame() {     myShouldPause = true;   }   /**    * restart the game after a pause.    */   synchronized void resumeGame() {     myShouldPause = false;     notify();   }   /**    * stops the game.    */   synchronized void requestStop() {     myShouldStop = true;     notify();   }   /**    * start the game..    */   public void run() {     // flush any keystrokes that occurred before the     // game started:     myJumpCanvas.flushKeys();     myShouldStop = false;     myShouldPause = false;     while (true) {       myLastRefreshTime = System.currentTimeMillis();       if (myShouldStop) {         break;       }       synchronized (this) {         while (myShouldPause) {           try {             wait();           } catch (Exception e) {           }         }       }       myJumpCanvas.checkKeys();       myJumpCanvas.advance();       // we do a very short pause to allow the other thread       // to update the information about which keys are pressed:       synchronized (this) {         try {           wait(getWaitTime());         } catch (Exception e) {         }       }     }   } } /**  * This class represents the player.  *   * @author Carol Hamer  */ class Cowboy extends Sprite {   //---------------------------------------------------------   //    dimension fields   /**    * The width of the cowboy's bounding rectangle.    */   static final int WIDTH = 32;   /**    * The height of the cowboy's bounding rectangle.    */   static final int HEIGHT = 48;   /**    * This is the order that the frames should be displayed for the animation.    */   static final int[] FRAME_SEQUENCE = { 3, 2, 1, 2 };   //---------------------------------------------------------   //    instance fields   /**    * the X coordinate of the cowboy where the cowboy starts the game.    */   private int myInitialX;   /**    * the Y coordinate of the cowboy when not jumping.    */   private int myInitialY;   /**    * The jump index that indicates that no jump is currently in progress..    */   private int myNoJumpInt = -6;   /**    * Where the cowboy is in the jump sequence.    */   private int myIsJumping = myNoJumpInt;   /**    * If the cowboy is currently jumping, this keeps track of how many points    * have been scored so far during the jump. This helps the calculation of    * bonus points since the points being scored depend on how many tumbleweeds    * are jumped in a single jump.    */   private int myScoreThisJump = 0;   //---------------------------------------------------------   //   initialization   /**    * constructor initializes the image and animation.    */   public Cowboy(int initialX, int initialY) throws Exception {     super(Image.createImage("/images/cowboy.png"), WIDTH, HEIGHT);     myInitialX = initialX;     myInitialY = initialY;     // we define the reference pixel to be in the middle     // of the cowboy image so that when the cowboy turns     // from right to left (and vice versa) he does not     // appear to move to a different location.     defineReferencePixel(WIDTH / 2, 0);     setRefPixelPosition(myInitialX, myInitialY);     setFrameSequence(FRAME_SEQUENCE);   }   //---------------------------------------------------------   //   game methods   /**    * If the cowboy has landed on a tumbleweed, we decrease the score.    */   int checkCollision(Tumbleweed tumbleweed) {     int retVal = 0;     if (collidesWith(tumbleweed, true)) {       retVal = 1;       // once the cowboy has collided with the tumbleweed,       // that tumbleweed is done for now, so we call reset       // which makes it invisible and ready to be reused.       tumbleweed.reset();     }     return (retVal);   }   /**    * set the cowboy back to its initial position.    */   void reset() {     myIsJumping = myNoJumpInt;     setRefPixelPosition(myInitialX, myInitialY);     setFrameSequence(FRAME_SEQUENCE);     myScoreThisJump = 0;     // at first the cowboy faces right:     setTransform(TRANS_NONE);   }   //---------------------------------------------------------   //   graphics   /**    * alter the cowboy image appropriately for this frame..    */   void advance(int tickCount, boolean left) {     if (left) {       // use the mirror image of the cowboy graphic when       // the cowboy is going towards the left.       setTransform(TRANS_MIRROR);       move(-1, 0);     } else {       // use the (normal, untransformed) image of the cowboy       // graphic when the cowboy is going towards the right.       setTransform(TRANS_NONE);       move(1, 0);     }     // this section advances the animation:     // every third time through the loop, the cowboy     // image is changed to the next image in the walking     // animation sequence:     if (tickCount % 3 == 0) { // slow the animation down a little       if (myIsJumping == myNoJumpInt) {         // if he's not jumping, set the image to the next         // frame in the walking animation:         nextFrame();       } else {         // if he's jumping, advance the jump:         // the jump continues for several passes through         // the main game loop, and myIsJumping keeps track         // of where we are in the jump:         myIsJumping++;         if (myIsJumping < 0) {           // myIsJumping starts negative, and while it's           // still negative, the cowboy is going up.           // here we use a shift to make the cowboy go up a           // lot in the beginning of the jump, and ascend           // more and more slowly as he reaches his highest           // position:           setRefPixelPosition(getRefPixelX(), getRefPixelY()               - (2 << (-myIsJumping)));         } else {           // once myIsJumping is negative, the cowboy starts           // going back down until he reaches the end of the           // jump sequence:           if (myIsJumping != -myNoJumpInt - 1) {             setRefPixelPosition(getRefPixelX(), getRefPixelY()                 + (2 << myIsJumping));           } else {             // once the jump is done, we reset the cowboy to             // his non-jumping position:             myIsJumping = myNoJumpInt;             setRefPixelPosition(getRefPixelX(), myInitialY);             // we set the image back to being the walking             // animation sequence rather than the jumping image:             setFrameSequence(FRAME_SEQUENCE);             // myScoreThisJump keeps track of how many points             // were scored during the current jump (to keep             // track of the bonus points earned for jumping             // multiple tumbleweeds). Once the current jump is done,             // we set it back to zero.             myScoreThisJump = 0;           }         }       }     }   }   /**    * makes the cowboy jump.    */   void jump() {     if (myIsJumping == myNoJumpInt) {       myIsJumping++;       // switch the cowboy to use the jumping image       // rather than the walking animation images:       setFrameSequence(null);       setFrame(0);     }   }   /**    * This is called whenever the cowboy clears a tumbleweed so that more    * points are scored when more tumbleweeds are cleared in a single jump.    */   int increaseScoreThisJump() {     if (myScoreThisJump == 0) {       myScoreThisJump++;     } else {       myScoreThisJump *= 2;     }     return (myScoreThisJump);   } } /**  * This class contains the loop that keeps the game running.  *   * @author Carol Hamer  */ class TumbleweedThread extends Thread {   //---------------------------------------------------------   //   fields   /**    * Whether or not the main thread would like this thread to pause.    */   private boolean myShouldPause;   /**    * Whether or not the main thread would like this thread to stop.    */   private boolean myShouldStop;   /**    * A handle back to the graphical components.    */   private Tumbleweed[] myTumbleweeds;   /**    * Random number generator to randomly decide when to appear.    */   private Random myRandom = new Random();   //----------------------------------------------------------   //   initialization   /**    * standard constructor, sets data.    */   TumbleweedThread(JumpCanvas canvas) throws Exception {     myTumbleweeds = canvas.getTumbleweeds();   }   //----------------------------------------------------------   //   actions   /**    * pause the thread.    */   void pauseGame() {     myShouldPause = true;   }   /**    * restart the thread after a pause.    */   synchronized void resumeGame() {     myShouldPause = false;     notify();   }   /**    * stops the thread.    */   synchronized void requestStop() {     myShouldStop = true;     notify();   }   /**    * start the thread..    */   public void run() {     myShouldStop = false;     myShouldPause = false;     while (true) {       if (myShouldStop) {         break;       }       synchronized (this) {         while (myShouldPause) {           try {             wait();           } catch (Exception e) {           }         }       }       // wait a random length of time:       int waitTime = (1 + getRandomInt(10)) * 100;       synchronized (this) {         try {           wait(waitTime);         } catch (Exception e) {         }       }       if (!myShouldPause) {         // randomly select which one to set in motion and         // tell it to go. If the chosen tumbleweed is         // currently visible, it will not be affected         int whichWeed = getRandomInt(myTumbleweeds.length);         myTumbleweeds[whichWeed].go();       }     }   }   //----------------------------------------------------------   //   randomization utilities   /**    * Gets a random int between zero and the param upper (exclusive).    */   public int getRandomInt(int upper) {     int retVal = myRandom.nextInt() % upper;     if (retVal < 0) {       retVal += upper;     }     return (retVal);   } } /**  * This class represents the tumbleweeds that the player must jump over.  *   * @author Carol Hamer  */ class Tumbleweed extends Sprite {   //---------------------------------------------------------   //   dimension fields   /**    * The width of the tumbleweed's bounding square.    */   static final int WIDTH = 16;   //---------------------------------------------------------   //    instance fields   /**    * whether or not this tumbleweed has been jumped over. This is used to    * calculate the score.    */   private boolean myJumpedOver;   /**    * whether or not this tumbleweed enters from the left.    */   private boolean myLeft;   /**    * the Y coordinate of the tumbleweed.    */   private int myY;   /**    * the leftmost visible pixel.    */   private int myCurrentLeftBound;   /**    * the rightmost visible pixel.    */   private int myCurrentRightBound;   //---------------------------------------------------------   //   initialization   /**    * constructor initializes the image and animation.    *     * @param left    *            whether or not this tumbleweed enters from the left.    */   public Tumbleweed(boolean left) throws Exception {     super(Image.createImage("/images/tumbleweed.png"), WIDTH, WIDTH);     myY = JumpManager.DISP_HEIGHT - WIDTH - 2;     myLeft = left;     if (!myLeft) {       setTransform(TRANS_MIRROR);     }     myJumpedOver = false;     setVisible(false);   }   //---------------------------------------------------------   //   game actions   /**    * Set the tumbleweed in motion if it is not currently visible.    */   synchronized boolean go() {     boolean retVal = false;     if (!isVisible()) {       retVal = true;       //System.out.println("Tumbleweed.go-->not visible");       myJumpedOver = false;       setVisible(true);       // set the tumbleweed's position to the point       // where it just barely appears on the screen       // to that it can start approaching the cowboy:       if (myLeft) {         setRefPixelPosition(myCurrentRightBound, myY);         move(-1, 0);       } else {         setRefPixelPosition(myCurrentLeftBound, myY);         move(1, 0);       }     } else {       //System.out.println("Tumbleweed.go-->visible");     }     return (retVal);   }   //---------------------------------------------------------   //   graphics   /**    * move the tumbleweed back to its initial (inactive) state.    */   void reset() {     setVisible(false);     myJumpedOver = false;   }   /**    * alter the tumbleweed image appropriately for this frame..    *     * @param left    *            whether or not the player is moving left    * @return how much the score should change by after this advance.    */   synchronized int advance(Cowboy cowboy, int tickCount, boolean left,       int currentLeftBound, int currentRightBound) {     int retVal = 0;     myCurrentLeftBound = currentLeftBound;     myCurrentRightBound = currentRightBound;     // if the tumbleweed goes outside of the display     // region, set it to invisible since it is     // no longer in use.     if ((getRefPixelX() - WIDTH >= currentRightBound) && (!myLeft)) {       setVisible(false);     }     if ((getRefPixelX() + WIDTH <= currentLeftBound) && myLeft) {       setVisible(false);     }     if (isVisible()) {       // when the tumbleweed is active, we advance the       // rolling animation to the next frame and then       // move the tumbleweed in the right direction across       // the screen.       if (tickCount % 2 == 0) { // slow the animation down a little         nextFrame();       }       if (myLeft) {         move(-3, 0);         // if the cowboy just passed the tumbleweed         // (without colliding with it) we increase the         // cowboy's score and set myJumpedOver to true         // so that no further points will be awarded         // for this tumbleweed until it goes offscreen         // and then is later reactivated:         if ((!myJumpedOver) && (getRefPixelX() < cowboy.getRefPixelX())) {           myJumpedOver = true;           retVal = cowboy.increaseScoreThisJump();         }       } else {         move(3, 0);         if ((!myJumpedOver)             && (getRefPixelX() > cowboy.getRefPixelX()                 + Cowboy.WIDTH)) {           myJumpedOver = true;           retVal = cowboy.increaseScoreThisJump();         }       }     }     return (retVal);   } } /**  * This is the class that plays a little tune while you play the game. This  * version uses the Player and Control interfaces.  *   * @author Carol Hamer  */ class ToneControlMusicMaker implements PlayerListener {   //---------------------------------------------------------   //   fields   /**    * The player object that plays the tune.    */   private Player myPlayer;   /**    * Whether or not the player wants to pause the music.    */   private boolean myShouldPause;   /**    * Whether or not the system wants to pause the music.    */   private boolean myGamePause;   /**    * The tune played by the game, stored as an array of bytes in BNF notation.    */   private byte[] myTune = {   // first set the version       ToneControl.VERSION, 1,       // set the tempo       ToneControl.TEMPO, 30,       // define the first line of the song       ToneControl.BLOCK_START, 0, 69, 8, 69, 8, 69, 8, 71, 8, 73, 16, 71,       16, 69, 8, 73, 8, 71, 8, 71, 8, 69, 32, ToneControl.BLOCK_END, 0,       // define the other line of the song       ToneControl.BLOCK_START, 1, 71, 8, 71, 8, 71, 8, 71, 8, 66, 16, 66,       16, 71, 8, 69, 8, 68, 8, 66, 8, 64, 32, ToneControl.BLOCK_END, 1,       // play the song       ToneControl.PLAY_BLOCK, 0, ToneControl.PLAY_BLOCK, 0,       ToneControl.PLAY_BLOCK, 1, ToneControl.PLAY_BLOCK, 0, };   //----------------------------------------------------------   //   actions   /**    * call this when the game pauses. This method does not affect the field    * myShouldPause because this method is called only when the system pauses    * the music, not when the player pauses the music.    */   void pauseGame() {     try {       myGamePause = true;       myPlayer.stop();       // when the application pauses the game, resources       // are supposed to be released, so we close the       // player and throw it away.       myPlayer.close();       myPlayer = null;     } catch (Exception e) {       // the music isn't necessary, so we ignore exceptions.     }   }   /**    * call this when the game resumes. This method does not affect the field    * myShouldPause because this method is called only when the system reusmes    * the music, not when the player pauses the music.    */   synchronized void resumeGame() {     try {       myGamePause = false;       if (!myShouldPause) {         // if the player is null, we create a new one.         if (myPlayer == null) {           start();         }         // start the music.         myPlayer.start();       }     } catch (Exception e) {       // the music isn't necessary, so we ignore exceptions.     }   }   /**    * toggle the music. (pause it if it's going, start it again if it's    * paused).    */   synchronized void toggle() {     try {       myShouldPause = !myShouldPause;       if (myShouldPause) {         if (myPlayer != null) {           myPlayer.stop();         }       } else if (!myGamePause) {         // if the player is null, we create a new one.         if (myPlayer == null) {           start();         }         // start the music.         myPlayer.start();       }     } catch (Exception e) {       // the music isn't necessary, so we ignore exceptions.     }   }   /**    * stops the music.    */   synchronized void requestStop() {     try {       myPlayer.stop();       // this is called when the game is over, to we close       // up the player to release the resources.       myPlayer.close();     } catch (Exception e) {       // the music isn't necessary, so we ignore exceptions.     }   }   //----------------------------------------------------------   //   initialization   /**    * start the music.. Here the method is "start" instead of "run" because it    * is not necessary to create a thread for the Player. the Player runs on    * its own thread.    */   public void start() {     ToneControl control = null;     try {       myPlayer = Manager.createPlayer(Manager.TONE_DEVICE_LOCATOR);       // do the preliminary set-up:       myPlayer.realize();       // set a listener to listen for the end of the tune:       myPlayer.addPlayerListener(this);       // get the ToneControl object in order to set the tune data:       control = (ToneControl) myPlayer.getControl("ToneControl");       control.setSequence(myTune);       // set the volume to the highest possible volume:       VolumeControl vc = (VolumeControl) myPlayer           .getControl("VolumeControl");       vc.setLevel(100);     } catch (Exception e) {       // the music isn't necessary, so we ignore exceptions.     }   }   //----------------------------------------------------------   //   implementation of PlayerListener   /**    * If we reach the end of the song, play it again...    */   public void playerUpdate(Player player, String event, Object eventData) {     if (event.equals(PlayerListener.END_OF_MEDIA)) {       if ((!myShouldPause) && (!myGamePause)) {         try {           myPlayer.start();         } catch (Exception e) {           // the music isn't necessary, so we ignore exceptions.         }       }     }   } } /**  * This is the class that plays a little tune while you play the game.  *   * @author Carol Hamer  */ class MusicMaker extends Thread {   //---------------------------------------------------------   //   fields   /**    * Whether or not the main thread would like this thread to stop.    */   public static final int NOTE_LENGTH = 250;   /**    * Whether or not the main thread would like this thread to pause.    */   private boolean myShouldPause;   /**    * If the whole game is paused, we pause the music too..    */   private boolean myGamePause;   /**    * Whether or not the main thread would like this thread to stop.    */   private static boolean myShouldStop;   /**    * The tune played by the game, stored as an array of notes and durations.    *     * NOTE: 69 is A. To get other notes, just add or subtract their difference    * from A on the keyboard including the black keys in the calculation. See    * the scales below for an idea.    *      */   private byte[][] myTune = { { 69, 1 }, { 69, 1 }, { 69, 1 }, { 71, 1 },       { 73, 2 }, { 71, 2 }, { 69, 1 }, { 73, 1 }, { 71, 1 }, { 71, 1 },       { 69, 4 }, { 69, 1 }, { 69, 1 }, { 69, 1 }, { 71, 1 }, { 73, 2 },       { 71, 2 }, { 69, 1 }, { 73, 1 }, { 71, 1 }, { 71, 1 }, { 69, 4 },       { 71, 1 }, { 71, 1 }, { 71, 1 }, { 71, 1 }, { 66, 2 }, { 66, 2 },       { 71, 1 }, { 69, 1 }, { 68, 1 }, { 66, 1 }, { 64, 4 }, { 69, 1 },       { 69, 1 }, { 69, 1 }, { 71, 1 }, { 73, 2 }, { 71, 2 }, { 69, 1 },       { 73, 1 }, { 71, 1 }, { 71, 1 }, { 69, 4 } };   /**    * An example "tune" that is just a scale.. not used.    */   private byte[][] myScale = { { 69, 1 }, { 71, 1 }, { 73, 1 }, { 74, 1 },       { 76, 1 }, { 78, 1 }, { 80, 1 }, { 81, 1 } };   /**    * An example "tune" that is just a scale.. not used.    */   private byte[][] myScale2 = { { 57, 1 }, { 59, 1 }, { 61, 1 }, { 62, 1 },       { 64, 1 }, { 66, 1 }, { 68, 1 }, { 69, 1 } };   //----------------------------------------------------------   //   actions   /**    * call this when the game pauses.    */   void pauseGame() {     myGamePause = true;   }   /**    * call this when the game resumes.    */   synchronized void resumeGame() {     myGamePause = false;     this.notify();   }   /**    * toggle the music. (pause it if it's going, start it again if it's    * paused).    */   synchronized void toggle() {     myShouldPause = !myShouldPause;     this.notify();   }   /**    * stops the music.    */   synchronized void requestStop() {     myShouldStop = true;     this.notify();   }   /**    * start the music..    */   public void run() {     myShouldStop = false;     myShouldPause = true;     myGamePause = false;     int counter = 0;     while (true) {       if (myShouldStop) {         break;       }       synchronized (this) {         while ((myShouldPause) || (myGamePause)) {           try {             wait();           } catch (Exception e) {           }         }       }       try {         Manager.playTone(myTune[counter][0], myTune[counter][1]             * NOTE_LENGTH, 50);       } catch (Exception e) {         // the music isn't necessary, so we ignore exceptions.       }       synchronized (this) {         try {           wait(myTune[counter][1] * NOTE_LENGTH);         } catch (Exception e) {         }       }       counter++;       if (counter >= myTune.length) {         counter = 0;       }     }   } } /**  * This handles the graphics objects.  *   * @author Carol Hamer  */ class JumpManager extends javax.microedition.lcdui.game.LayerManager {   //---------------------------------------------------------   //   dimension fields   //  (constant after initialization)   /**    * The x-coordinate of the place on the game canvas where the LayerManager    * window should appear, in terms of the coordiantes of the game canvas.    */   static int CANVAS_X;   /**    * The y-coordinate of the place on the game canvas where the LayerManager    * window should appear, in terms of the coordiantes of the game canvas.    */   static int CANVAS_Y;   /**    * The width of the display window.    */   static int DISP_WIDTH;   /**    * The height of this object's graphical region. This is the same as the    * height of the visible part because in this game the layer manager's    * visible part scrolls only left and right but not up and down.    */   static int DISP_HEIGHT;   //   game object fields   // the player's object.   private Cowboy myCowboy;   /**    * the tumbleweeds that enter from the left.    */   private Tumbleweed[] myLeftTumbleweeds;   /**    * the tumbleweeds that enter from the right.    */   private Tumbleweed[] myRightTumbleweeds;   /**    * the object representing the grass in the background..    */   private Grass myGrass;   /**    * Whether or not the player is currently going left.    */   private boolean myLeft;   /**    * The leftmost x-coordinate that should be visible on the screen in terms    * of this objects internal coordinates.    */   private int myCurrentLeftX;   //-----------------------------------------------------   //    gets/sets   /**    * This tells the player to turn left or right.    *     * @param left    *            whether or not the turn is towards the left..    */   void setLeft(boolean left) {     myLeft = left;   }   /**    * @return a handle to the tumbleweed objects.    */   Tumbleweed[] getTumbleweeds() {     Tumbleweed[] retArray = new Tumbleweed[myLeftTumbleweeds.length         + myRightTumbleweeds.length];     for (int i = 0; i < myLeftTumbleweeds.length; i++) {       retArray[i] = myLeftTumbleweeds[i];     }     for (int i = 0; i < myRightTumbleweeds.length; i++) {       retArray[i + myLeftTumbleweeds.length] = myRightTumbleweeds[i];     }     return (retArray);   }   //-----------------------------------------------------   //    initialization and game state changes   /**    * Constructor sets the data and constructs the graphical objects..    *     * @param x    *            The x-coordinate of the place on the game canvas where the    *            LayerManager window should appear, in terms of the coordiantes    *            of the game canvas.    * @param y    *            The y-coordinate of the place on the game canvas where the    *            LayerManager window should appear, in terms of the coordiantes    *            of the game canvas.    * @param width    *            the width of the region that is to be occupied by the    *            LayoutManager.    * @param height    *            the height of the region that is to be occupied by the    *            LayoutManager.    */   public JumpManager(int x, int y, int width, int height) throws Exception {     CANVAS_X = x;     CANVAS_Y = y;     DISP_WIDTH = width;     DISP_HEIGHT = height;     myCurrentLeftX = Grass.CYCLE * Grass.TILE_WIDTH;     setViewWindow(0, 0, DISP_WIDTH, DISP_HEIGHT);     // create the player:     if (myCowboy == null) {       myCowboy = new Cowboy(myCurrentLeftX + DISP_WIDTH / 2, DISP_HEIGHT           - Cowboy.HEIGHT - 2);       append(myCowboy);     }     // create the tumbleweeds to jump over:     if (myLeftTumbleweeds == null) {       myLeftTumbleweeds = new Tumbleweed[2];       for (int i = 0; i < myLeftTumbleweeds.length; i++) {         myLeftTumbleweeds[i] = new Tumbleweed(true);         append(myLeftTumbleweeds[i]);       }     }     if (myRightTumbleweeds == null) {       myRightTumbleweeds = new Tumbleweed[2];       for (int i = 0; i < myRightTumbleweeds.length; i++) {         myRightTumbleweeds[i] = new Tumbleweed(false);         append(myRightTumbleweeds[i]);       }     }     // create the background object:     if (myGrass == null) {       myGrass = new Grass();       append(myGrass);     }   }   /**    * sets all variables back to their initial positions.    */   void reset() {     if (myGrass != null) {       myGrass.reset();     }     if (myCowboy != null) {       myCowboy.reset();     }     if (myLeftTumbleweeds != null) {       for (int i = 0; i < myLeftTumbleweeds.length; i++) {         myLeftTumbleweeds[i].reset();       }     }     if (myRightTumbleweeds != null) {       for (int i = 0; i < myRightTumbleweeds.length; i++) {         myRightTumbleweeds[i].reset();       }     }     myLeft = false;     myCurrentLeftX = Grass.CYCLE * Grass.TILE_WIDTH;   }   //-------------------------------------------------------   //  graphics methods   /**    * paint the game graphic on the screen.    */   public void paint(Graphics g) {     setViewWindow(myCurrentLeftX, 0, DISP_WIDTH, DISP_HEIGHT);     paint(g, CANVAS_X, CANVAS_Y);   }   /**    * If the cowboy gets to the end of the graphical region, move all of the    * pieces so that the screen appears to wrap.    */   private void wrap() {     if (myCurrentLeftX % (Grass.TILE_WIDTH * Grass.CYCLE) == 0) {       if (myLeft) {         myCowboy.move(Grass.TILE_WIDTH * Grass.CYCLE, 0);         myCurrentLeftX += (Grass.TILE_WIDTH * Grass.CYCLE);         for (int i = 0; i < myLeftTumbleweeds.length; i++) {           myLeftTumbleweeds[i]               .move(Grass.TILE_WIDTH * Grass.CYCLE, 0);         }         for (int i = 0; i < myRightTumbleweeds.length; i++) {           myRightTumbleweeds[i].move(Grass.TILE_WIDTH * Grass.CYCLE,               0);         }       } else {         myCowboy.move(-(Grass.TILE_WIDTH * Grass.CYCLE), 0);         myCurrentLeftX -= (Grass.TILE_WIDTH * Grass.CYCLE);         for (int i = 0; i < myLeftTumbleweeds.length; i++) {           myLeftTumbleweeds[i].move(-Grass.TILE_WIDTH * Grass.CYCLE,               0);         }         for (int i = 0; i < myRightTumbleweeds.length; i++) {           myRightTumbleweeds[i].move(-Grass.TILE_WIDTH * Grass.CYCLE,               0);         }       }     }   }   //-------------------------------------------------------   //  game movements   /**    * Tell all of the moving components to advance.    *     * @param gameTicks    *            the remainaing number of times that the main loop of the game    *            will be executed before the game ends.    * @return the change in the score after the pieces have advanced.    */   int advance(int gameTicks) {     int retVal = 0;     // first we move the view window     // (so we are showing a slightly different view of     // the manager's graphical area.)     if (myLeft) {       myCurrentLeftX--;     } else {       myCurrentLeftX++;     }     // now we tell the game objects to move accordingly.     myGrass.advance(gameTicks);     myCowboy.advance(gameTicks, myLeft);     for (int i = 0; i < myLeftTumbleweeds.length; i++) {       retVal += myLeftTumbleweeds[i].advance(myCowboy, gameTicks, myLeft,           myCurrentLeftX, myCurrentLeftX + DISP_WIDTH);       retVal -= myCowboy.checkCollision(myLeftTumbleweeds[i]);     }     for (int i = 0; i < myLeftTumbleweeds.length; i++) {       retVal += myRightTumbleweeds[i].advance(myCowboy, gameTicks,           myLeft, myCurrentLeftX, myCurrentLeftX + DISP_WIDTH);       retVal -= myCowboy.checkCollision(myRightTumbleweeds[i]);     }     // now we check if we have reached an edge of the viewable     // area, and if so we move the view area and all of the     // game objects so that the game appears to wrap.     wrap();     return (retVal);   }   /**    * Tell the cowboy to jump..    */   void jump() {     myCowboy.jump();   } }