// ###################################################################### // # # // # ## #### ## ## #### ## ## #### ## ##### # // # ## ## ## ## ## ## ## ### ## ## ## ## ## ## # // # ## ## ## ## ## ## ## ###### ## ## ## ## ## # // # ## ## ###### ## ## ###### ## ### ## ## ## ## ## # // # #### ## ## ### ## ## ## ## #### ## ##### # // # # // # [][][][] - A BLOCK BREAKER APPLET BY REMI FAITOUT - [][][][] # // # [][] [] [][] [] # // # o VERSION 1.53 15/03/1998 o # // # o==o o==o # // # # // ###################################################################### // ## IMPORTS ########################################################### import java.lang.*; import java.applet.*; import java.io.*; import java.awt.*; import java.awt.image.*; import java.awt.event.*; import java.net.*; // ## JAVANOID : THE MAIN CLASS ######################################### // The user interface that contains the game itself... public class javanoid extends Applet implements ActionListener, ItemListener { // References jngame game; // User interface Panel control; Checkbox soundCheck; Choice speedChoice; Button startButton, demoButton; Button pauseButton, resumeButton; Button unlockButton, nextButton; // javanoid initialization public void init() { // Game init showStatus("Creating game objects..."); game = new jngame(this); // User interface construction showStatus("Building user interface..."); setLayout(new FlowLayout()); setBackground(Color.lightGray); control = new Panel(); control.setLayout(new GridLayout(0,1)); control.add(startButton = new Button("START")); startButton.addActionListener(this); control.add(demoButton = new Button("DEMO")); demoButton.addActionListener(this); control.add(pauseButton = new Button("PAUSE")); pauseButton.addActionListener(this); control.add(resumeButton = new Button("RESUME")); resumeButton.addActionListener(this); control.add(unlockButton = new Button("UNLOCK")); unlockButton.addActionListener(this); control.add(new Label("SPEED", Label.CENTER)); control.add(speedChoice = new Choice()); speedChoice.addItemListener(this); speedChoice.addItem("MAXIMUM"); speedChoice.addItem("FAST"); speedChoice.addItem("AVERAGE"); speedChoice.addItem("SLOW"); speedChoice.addItem("MINIMUM"); speedChoice.select(2); control.add(soundCheck = new Checkbox("SOUND")); soundCheck.addItemListener(this); control.doLayout(); add(game); add(control); doLayout(); // Start in demo mode showStatus(""); game.startGame(true); } // Event handler (panel) public void actionPerformed(ActionEvent evt) { Object src = evt.getSource(); if (game!=null) { if (src==startButton) { game.stopGame(); game.startGame(false); } else if (src==demoButton) { game.stopGame(); game.startGame(true); } else if (src==pauseButton) game.stop(); else if (src==resumeButton) game.start(); else if (src==unlockButton) game.unlockBalls(); } } public void itemStateChanged(ItemEvent evt) { Object src = evt.getSource(); if (game!=null) { if (src==speedChoice) game.setAnimationDelay (2 + 5 * speedChoice.getSelectedIndex()); else if (src==soundCheck) game.toggleSound(); } } // Applet information methods public String getAppletInfo() { return "Applet Javanoid v1.53 by Rémi FAITOUT"; } } // ## JAVANOID GAME CLASS ############################################### // Main class for javanoid : It manages all the game objects, their lives, // collisions, both the user and game events (moves, bullets, pills and // balls throws, next level, ...). final class jngame extends jnbuffer implements Runnable, MouseListener, MouseMotionListener { // Game default size final static int defaultWidth = 312; final static int defaultHeight = 300; // File names final static String imageFile = "javanoid.gif"; final static String soundFiles[] = {"start.au", "wall.au", "shoot.au"}; final static String levelFile = "javanoid.dat"; // Number of images per object final static int nbBallImg = 2; final static int nbPaddleImg = 4; final static int nbBlockImg = 16; final static int nbPillImg = 6; final static int nbPillImgAnim = 4; final static int nbBulletImg = 1; // Number of sound files final static int nbSounds = 3; // Size & coordinates of the images final static int blockCoords[][] = { {0 , 0, 24, 16}, {0 , 16, 24, 16}, {0 , 32, 24, 16}, {0 , 48, 24, 16}, {24, 0, 24, 16}, {24, 16, 24, 16}, {24, 32, 24, 16}, {24, 48, 24, 16}, {48, 0, 24, 16}, {48, 16, 24, 16}, {48, 32, 24, 16}, {48, 48, 24, 16}, {72, 0, 24, 16}, {72, 16, 24, 16}, {72, 32, 24, 16}, {72, 48, 24, 16}}; final static int ballCoords[][] = { {0, 64, 12, 12}, {12, 64, 12, 12}}; final static int paddleCoords[][] = { {24, 64, 40, 12}, {64, 64, 32, 12}, {0, 76, 48, 12}, {48, 76, 40, 12} }; final static int bulletCoords[][] = { {88, 82, 4, 6}}; final static int pillCoords[][][] = { {{0, 88, 16, 8}, {0, 96, 16, 8}, {0, 104, 16, 8}, {0, 112, 16, 8}}, {{16, 88, 16, 8}, {16, 96, 16, 8}, {16, 104, 16, 8}, {16, 112, 16, 8}}, {{32, 88, 16, 8}, {32, 96, 16, 8}, {32, 104, 16, 8}, {32, 112, 16, 8}}, {{48, 88, 16, 8}, {48, 96, 16, 8}, {48, 104, 16, 8}, {48, 112, 16, 8}}, {{64, 88, 16, 8}, {64, 96, 16, 8}, {64, 104, 16, 8}, {64, 112, 16, 8}}, {{80, 88, 16, 8}, {80, 96, 16, 8}, {80, 104, 16, 8}, {80, 112, 16, 8}}}; // Arrays for images protected Image ballImg[] = new Image[nbBallImg]; protected Image paddleImg[] = new Image[nbPaddleImg]; protected Image blockImg[] = new Image[nbBlockImg]; protected Image pillImg[][] = new Image[nbPillImg][nbPillImgAnim]; protected Image bulletImg[] = new Image[nbBulletImg]; // Array for sounds protected AudioClip sounds[] = new AudioClip[nbSounds]; // Level definitions final static int nbLevels = 12; final static int nbLevelRows = 12; final static int nbLevelCols = 13; final static int colWidth = 24; final static int rowHeight = 16; protected String levelNames[] = new String[nbLevels]; protected String levelDefinitions[][] = new String[nbLevels][nbLevelRows]; // Current level definition protected int currentLevel = 0; protected int nbBlocksLeft = 0; protected jnblock levelMap[][] = new jnblock[nbLevelCols][nbLevelRows]; // Game default attributes final static int nbPills = 4; final static int nbBullets = 8; final static int nbBalls = 3; final static int displayAlt = 90; final static int paddleAlt = 40; final static int scoreAlt = 20; final static int nbPillValues = 6; // Idle time during the run loop protected int animationDelay = 12; // Current sound protected int currentSound = 0; // Flags for game state protected boolean goToNext = false; protected boolean demoMode = false; protected boolean fireMode = false; protected boolean soundOn = false; protected boolean gameover = false; // Javanoid objects protected jnstatus status; protected jnpill pills[] = new jnpill[nbPills]; protected jnbullet bullets[] = new jnbullet[nbBullets]; protected jnball balls[] = new jnball[nbBalls]; protected jnpaddle paddle; // References Thread myThread; Applet applet; // Constructor public jngame (Applet a) { // Canvas init super(a, defaultWidth, defaultHeight); applet = a; // Loading images applet.showStatus("Loading images..."); MediaTracker tracker = new MediaTracker(applet); URL url = applet.getCodeBase(); Image imageTmp = applet.getImage(url, imageFile); tracker.addImage(imageTmp, 0); ImageFilter filter; // Extracting images for (int i=0; i=nbLevels) currentLevel = 0; clearBackground(); computeLevel(); status.addLevel(); } // New ball & paddle reset balls[0] = new jnball(this, paddle, ballImg); paddle.setNormal(); fireMode = false; // Automatic start or level name display whether demo or not if (demoMode) balls[0].launch(); else status.showName(levelNames[currentLevel]); } void computeLevel() { char blockRow[]; nbBlocksLeft = 0; for (int y=0;y hiScore); } // reset & set (partial or not) : Draw the status on the buffer public void reset() { if (score > hiScore) hiScore = score; lives = nbLives; level = 1; score = 0; set(); } void set() { buffer.clearPermanentImage(0, statusY, width, height); buffer.drawPermanentString(livesString, livesX, statusY + height); for (int i=0;i 0) { // Paddle hit if (paddle.isHit(X + width / 2, Y + height)) { if (glueMode) { launched = false; posX = X - paddle.getX(); posY = Y - paddle.getY(); } setBallDX(paddle.hit(X + width / 2, dX, speed), true); } else if (game.hitBlock(X + width / 2, Y + height, killMode)) invertDY(); } else if (game.hitBlock(X + width / 2, Y, killMode)) invertDY(); if (dX < 0) { if (game.hitBlock(X, Y + height / 2, killMode)) invertDX(); } else if (game.hitBlock(X + width, Y + height / 2, killMode)) invertDX(); } } // Ball settings public void setBallDX (int d, boolean b) { dX = d; dY = (int)(Math.max(Math.sqrt((double)(speed * speed - dX * dX)), 1)); if (b) dY = - dY; } public void setSpeed (int s) { speed = s; } public void setGlue (boolean g) { glueMode = g; } public void setKiller (boolean g) { killMode = g; if (killMode) change(images[1], defaultWidth, defaultHeight); else change(images[0], defaultWidth, defaultHeight); } public void setRandomDX() { setBallDX((int)((speed - 1) * (2 * Math.random() - 1)), false); } // Ball requests public int getSpeed() { return speed; } public boolean isLaunched() { return launched; } } // ## JAVANOID PILL CLASS ############################################### // This class represents the pills in the game : They are moving objects // with a value that gives their effects on the game when they're catched // by the paddle final class jnpill extends jnmovingobject { // Default attributes final static int defaultSpeed = 3; final static int defaultWidth = 16; final static int defaultHeight = 8; // Animation frame protected int pillNbFrames; protected double pillStep = 0.25; protected double pillFrame; // Values gives the effect protected int pillValue; Image images[]; jngame game; jnpaddle paddle; // Constructor public jnpill(jngame g, jnpaddle p, Image i[], int v, int bx, int by) { super(g, i[0], defaultWidth, defaultHeight, bx, by, 0, defaultSpeed); game = g; paddle = p; images = i; pillNbFrames = images.length; pillValue = v; pillFrame = 0; } // Pill life (paddle detection + dies when at the bottom of the screen) public void live() { // Pill animation pillFrame+=pillStep; if (pillFrame>=pillNbFrames) pillFrame = 0; change(images[(int)pillFrame], defaultWidth, defaultHeight); // Pill move super.live(); // Pill death if (paddle.isHit(X + width / 2, Y + height)) { game.playHit(); game.pillEvent(pillValue); die(); } } } // ## JAVANOID BULLET CLASS ############################################# // This class represents the paddle bullets : They are very similar to // pills except they move from bottom to top and hit the blocks. final class jnbullet extends jnmovingobject { final static int defaultSpeed = 6; final static int defaultWidth = 4; final static int defaultHeight = 6; jngame game; // Constructor public jnbullet(jngame g, Image i, int px, int py) { super(g, i, defaultWidth, defaultHeight, px, py, 0, - defaultSpeed); game = g; } public void live() { super.live(); if ((dY > 0) || (game.hitBlock(X + width / 2, Y, false))) { die(); } } } // ## JAVANOID PADDLE CLASS ############################################# // This class represents the paddle in the game : It a 'static' object // that can be moved to a position (ex : mouse position). It also gives // the direction of the ball. final class jnpaddle extends jnobject { // Default attributes final static int defaultWidth = 40; final static int defaultWidthBig = 48; final static int defaultWidthSmall = 32; final static int defaultHeight = 12; final static int defaultImage = 0; // Mouse position protected int minX, maxX; protected int newX; protected boolean demo; jngame game; Image images[]; // Constructor public jnpaddle (jngame g, Image[] i, int alt, boolean d) { super(g, i[0], defaultWidth, defaultHeight, (g.getWidth() - defaultWidth) / 2, g.getHeight() - alt); game = g; images = i; demo = d; minX = 0; maxX = game.getWidth() - defaultWidth; newX = X; } // Paddle life (move to the saved position) public void live() { setX(newX); super.live(); } // Paddle settings public int hit(int bx, int bdx, int s) { int ndx = 2 * s * (bx - X - width / 2) / width; game.playTouch(); return (ndx==0) ? bdx : ndx; } // Methods of controls public void followBall (int bx) { newX = bx - width / 2; } public void followMouse (int mx) { newX = mx - width / 2; } public void followArrows (int dx) { newX = newX + dx; } // Paddle settings public void setNormal() { change(images[0], defaultWidth, defaultHeight); } public void setSmall () { change(images[1], defaultWidthSmall, defaultHeight); } public void setBig () { change(images[2], defaultWidthBig, defaultHeight); } public void setFire () { change(images[3], defaultWidth, defaultHeight); } } // ## JAVANOID BLOCK CLASS ############################################## // Blocks are basic 'static object', except they die after a certain // number of hits. As they don't move at all, they are drawn or erased on // the permanent buffer. final class jnblock extends jnobject { final static int defaultWidth = 24; final static int defaultHeight = 16; final static int defaultImage = 0; // i.e. nb hits before it dies protected int resistance; // position in the level protected int column, row; jngame game; // Constructor public jnblock (jngame g, Image i, int col, int row, int r) { super(g, i, defaultWidth, defaultHeight, col * defaultWidth, row * defaultHeight); game = g; resistance = r; } // Life : no drawing there !!! public void live() {} // Blocks are "permanent" images : Draw on the background & erase public void draw() { buffer.drawPermanentImage(image, X, Y); } public void clear() { buffer.clearPermanentImage(X, Y, width, height); } // Death depends on resistance public void die() { if (resistance==0) { clear(); super.die(); } else resistance--; } // Hit method (tells if it dies or not) public boolean hit() { die(); if (alive) game.playTouch(); else game.playHit(); return alive ? false : true; } // Kill method (dies & tells if it's a gold one) public boolean kill() { clear(); super.die(); return (resistance >= 0); } } // ## JAVANOID BUFFER CLASS ############################################# // This class implements a sort of triple buffer : one for the background // image & the objects that you draw for a long time, one where the moving // objects are drawn, and one for display (see update method). abstract class jnbuffer extends Panel { // Width & height of the game canvas protected int width; protected int height; protected int numBackground = 0; protected int nbBackgrounds = 6; protected int backgroundColors[][] = { {255, 191, 0 , 0 , 191, 0 , 0 , 0 , 255, 0 , 191, 255}, // yellow, green, blue & cyan {0 , 191, 255, 0 , 0 , 255, 255, 0 , 0 , 255, 0 , 255}, // cyan, blue, red & pink {255, 0 , 255, 255, 0 , 0 , 0 , 191, 0 , 255, 191, 0 }, // pink, red, green & yellow {255, 191, 0 , 255, 0 , 0 , 0 , 0 , 255, 255, 0 , 255}, // yellow, red, blue & pink {255, 0 , 255, 0 , 0 , 255, 0 , 191, 0 , 0 , 191, 255}, // pink, blue, green & cyan {0 , 191, 255, 0 , 191, 0 , 255, 0 , 0 , 255, 191, 0 }};// cyan, green, red & yellow Applet applet; // On & off graphics for triple (!) buffering Graphics offGraphics, offBackGraphics; // Fontmetrics object FontMetrics fontMetrics; // Buffer & background images Image offImage, offBackImage, backImage; Dimension minSize; // Font style for text Font textFont = new Font("SansSerif", Font.BOLD, 12); // Color for texts & background Color textColor = new Color (255, 223, 0); Color backColor = new Color (0, 0, 0); // Constructor public jnbuffer (Applet a, int w, int h) { applet = a; width = w; height = h; // Off backround graphics : "Permanent" background graphics offBackImage = applet.createImage(width, height); offBackGraphics = offBackImage.getGraphics(); offBackGraphics.setColor(textColor); offBackGraphics.setFont(textFont); // Off graphics : Double buffer graphics offImage = applet.createImage(width, height); offGraphics = offImage.getGraphics(); offGraphics.setColor(textColor); offGraphics.setFont(textFont); // Dimension fontMetrics = offGraphics.getFontMetrics(); minSize = new Dimension(width, height); setSize(width, height); clearBackground(); } // Compute background image void computeBackground() { int[] pix = new int[width * height]; int index = 0; double alphai, alphaj, betai, betaj; int r1, r2, r, g1, g2, g, b1, b2, b; if (numBackground >= nbBackgrounds) numBackground = 0; // Make a fade between the four corner colors for (int j=0;j maxX) { X = maxX; invertDX(); } else if (x < minX) { X = minX; invertDX(); } else X = x; } public void setY(int y) { if (y > maxY) die(); else if (y < minY) { Y = minY; invertDY(); } else Y = y; } public void setDXDY(int dx, int dy) { dX = dx; dY = dy; } public void setDX(int dx) { dX = dx; } public void setDY(int dy) { dY = dy; } public void invertDXDY() { dX = - dX; dY = - dY; } public void invertDX() { dX = - dX; } public void invertDY() { dY = - dY; } public void reset() { dX = dX0; dY = dY0; super.reset(); } // Object life : move + drawing (dies when at the bottom of the screen) public void live() { if (alive) { setX(X + dX); setY(Y + dY); draw(); } } // Object requests public int getDX() { return dX; } public int getDY() { return dY; } } // ## JAVANOID OBJECT ################################################### // It's the basic object of javanoid. Though it doesn't move by itself, // it can be moved to absolute coordinates (ex: the paddle, which moves // to the position of the mouse pointer). A javanoid object lives (i.e. // is on the screen) & dies when it touches the bottom abstract class jnobject extends Object { // Width & height of the object protected int width, height; // Initial & current position of the object protected int X0, Y0; protected int X, Y; // X & Y limits for the canvas protected int minX, maxX; protected int minY, maxY; // Tells if the object is still alive protected boolean alive; // The buffer where the object is drawn jnbuffer buffer; // The bitmap image of the object Image image; // Constructor public jnobject(jnbuffer c, Image i, int w, int h, int x, int y) { buffer = c; image = i; width = w; height = h; X0 = x; Y0 = y; X = x; Y = y; minX = 0; maxX = buffer.getWidth() - width; minY = 0; maxY = buffer.getHeight() - height; alive = true; draw(); } // Object position settings public void setXY(int x, int y) { setX(x); setY(y); } public void setX(int x) { if (x > maxX) X = maxX; else if (x < minX) X = minX; else X = x; } public void setY(int y) { if (y > maxY) Y = maxY; else if (y < minY) Y = minY; else Y = y; } public void reset() { X = X0; Y = Y0; } // Object drawing & erasing public void draw() { buffer.drawImage(image, X, Y); } // Objet life (what he does until he's dead) public void live() { if (alive) draw(); } // Object death public void die() { alive = false; } // Collision detection public boolean isHit(int x, int y) { return (alive) && (x > X) && (x < X + width) && (y > Y) && (y < Y + height); } // Object graphic changes public void change(Image i, int w, int h) { image = i; width = w; height = h; minX = 0; maxX = buffer.getWidth() - width; minY = 0; maxY = buffer.getHeight() - height; } // Object requests public boolean isAlive() { return alive; } public int getWidth() { return width; } public int getHeight() { return height; } public int getX() { return X; } public int getY() { return Y; } }