import java.awt.*; import java.awt.event.KeyEvent; import java.net.URL; import java.io.*; /** CatBurglar. A simple retro game. **/ public class CatBurglar extends RetroApplet { private int state = INTRO; private final static int INTRO = 0; private final static int GAME = 1; private final static int OUTRO = 2; private final int WIDTH = 256; private final int HEIGHT = 256; private int score = 0; private int lives = 9; private int level = 1; private int counter = 0; private int invincible = 0; private SpriteImages images = null; private Level theLevel = new Level(); private Sprite catSprite = null; private Image background = null; private String[] levelFiles = { "test.lev" }; /** Do some initialisation. **/ public void init() { super.init(); images = new SpriteImages( this ); catSprite = new Sprite( images.getCatImage() ); // create another back buffer that the static (unchanging) // images will be drawn onto, so that drawing them is only // one operation instead of several background = createImage( Level.GRID_WIDTH*Level.ELEMENT_SIZE, Level.GRID_HEIGHT*Level.ELEMENT_SIZE ); // load the list of levels try { URL url = new URL( getCodeBase(), "levels.txt" ); InputStream in = url.openStream(); BufferedReader r = new BufferedReader( new InputStreamReader( in ) ); java.util.Vector v = new java.util.Vector(); String str = null; while( (str = r.readLine()) != null ) { v.addElement( str ); } levelFiles = new String[ v.size() ]; v.copyInto( levelFiles ); } catch( Exception e ) { System.err.println( e ); } } /** Do a timestep. **/ public void tick() { counter++; switch( state ) { case INTRO: intro(); break; case GAME: game(); break; case OUTRO: outro(); break; default: System.err.println( "unknown state" ); } } private Font bigFont = new Font( "SansSerif", Font.BOLD, 20 ); private Font smallFont = new Font( "SansSerif", Font.PLAIN, 12 ); /** Draw an intro screen, and wait for user input before changing * to the game screen. **/ public void intro() { Graphics g = getBackBuffer(); g.setColor( Color.black ); g.fillRect( 0, 0, WIDTH, HEIGHT ); g.setColor( Color.white ); g.setFont( bigFont ); FontMetrics metrics = g.getFontMetrics(); String str = "Catburglar!"; int w = metrics.stringWidth( str ); g.drawString( str, (WIDTH-w)>>1, HEIGHT>>1 ); g.setFont( smallFont ); g.drawString( "click on applet, then press space", 5, HEIGHT-4 ); if ( counter > 25 ) { // wait for 25 frames before drawing g.setColor( Color.green ); g.fillOval( (WIDTH>>1)-40, (HEIGHT>>1)+14, 30, 22 ); g.fillOval( (WIDTH>>1)+10, (HEIGHT>>1)+14, 30, 22 ); g.setColor( Color.black ); g.fillOval( (WIDTH>>1)-27, (HEIGHT>>1)+14, 6, 22 ); g.fillOval( (WIDTH>>1)+22, (HEIGHT>>1)+14, 6, 22 ); int c = counter - 25; c = c > 90 ? 90 : c; double d = (1.0 + Math.cos( c*0.1f )); int h = (int)(10*d); g.fillRect( 0, (HEIGHT>>1)+10, WIDTH, h ); h = (int)(7*d); g.fillRect( 0, (HEIGHT>>1)+40-h, WIDTH, h ); if ( counter > 150 ) counter = 55; } // copy to screen flipBuffer(); if ( counter >= 25 && fire() ) { // wait 1 second before allowing lives = 9; // user to go into game score = 0; level = 0; state = GAME; counter = 0; invincible = 0; nextLevel(); } } /** Run the game for one timestep and draw it. **/ public void game() { drawGame(); if ( lives < 0 ) { // if all lives gone then go to outro state = OUTRO; counter = 0; } else if ( counter < 50 ) { // wait a brief while to show an if ( counter > 25 ) // intro screen if ( fire() ) counter = 50; // fire pressed so go into main game else counter = 26; } else { theLevel.tick(); // update the level catSprite.tick( theLevel ); // update the main sprite if ( theLevel.levelDone( catSprite ) ) { // check if finished nextLevel(); counter = 0; invincible = 0; return; } // see if cat is on a jewel int score = theLevel.checkJewelCollisions( catSprite ); this.score += score; // if the cat hits a dog make it invincible for a while (after // losing a life) so it can get away invincible--; if ( invincible < 0 && theLevel.checkDogCollisions( catSprite ) ) { lives--; invincible = 50; } // work out how to move the cat next // all the logic is setup to deal // with what happens when several keys are pressed boolean u = (up() && !down()); boolean d = (!up() && down()); boolean v = u || d; boolean l = (left() && !right()); boolean r = (!left() && right()); boolean h = l || r ; int k = lastKeyPressed(); if ( !v || (k != UP_KEY && k != DOWN_KEY) ) { if ( l ) catSprite.goLeft(); else if ( r ) catSprite.goRight(); } if ( !h || (k != LEFT_KEY && k != RIGHT_KEY) ) { if ( u ) catSprite.goUp(); else if ( d ) catSprite.goDown(); } } } /** Draw the game. **/ private void drawGame() { Graphics g = getBackBuffer(); g.setColor( Color.black ); g.fillRect( 0, 0, WIDTH, HEIGHT ); g.setColor( Color.white ); g.setFont( smallFont ); g.drawString( "lives: " + lives, 5, HEIGHT-4 ); g.drawString( "level: " + level, (WIDTH/3) + 5, HEIGHT-4 ); g.drawString( "score: " + score, ((WIDTH<<1)/3) + 5, HEIGHT-4 ); if ( counter < 50 ) { // if we're still on the level intro screen g.setFont( bigFont ); FontMetrics metrics = g.getFontMetrics(); String str = "Next Level"; int w = metrics.stringWidth( str ); g.drawString( str, (WIDTH-w)>>1, HEIGHT>>1 ); g.setFont( smallFont ); g.drawString( "press space", (WIDTH-w)>>1, (HEIGHT>>1) + metrics.getHeight() ); } else { // draw the static images (walls, floor etc) g.drawImage( background, 0, 0, this ); // paint the rest of the level (dogs, jewels etc) theLevel.paint( g, this ); // if the cat is invincible only draw it once in // a while, so that it flashes if ( invincible < 0 || invincible % 4 == 0 ) catSprite.paint( g, this ); //g.drawImage( images.getDogImage(), 50, 50, this ); } // copy to screen flipBuffer(); } private void loadLevel( String level ) { try { // load the level from the file specified URL url = new URL( getCodeBase(), level ); InputStream in = url.openStream(); Reader r = new BufferedReader( new InputStreamReader( in ) ); theLevel.load( r, catSprite ); } catch( Exception e ) { System.err.println( e ); } // paint the static (unmoving) images to an image // so they can be drawn in one operation theLevel.paintStatic( background.getGraphics(), this ); } private void nextLevel() { // load the next level from the list of the levels catSprite.reset(); loadLevel( levelFiles[ level % levelFiles.length ] ); level++; } /** Show the outro (or gameover) screen. **/ public void outro() { Graphics g = getBackBuffer(); g.setColor( Color.black ); g.fillRect( 0, 0, WIDTH, HEIGHT ); g.setColor( Color.white ); g.setFont( bigFont ); FontMetrics metrics = g.getFontMetrics(); String str = "Game Over!"; int w = metrics.stringWidth( str ); g.drawString( str, (WIDTH-w)>>1, HEIGHT>>1 ); g.setFont( smallFont ); g.drawString( "level: " + level, (WIDTH/3) + 5, HEIGHT-4 ); g.drawString( "score: " + score, ((WIDTH<<1)/3) + 5, HEIGHT-4 ); flipBuffer(); if ( (counter > 12 && fire()) ) { // wait for a bit before moving state = INTRO; // back to intro counter = 0; } } }