/* A basic extension of the java.applet.Applet class */ import java.awt.*; import java.applet.*; import UserPanel; import GlobsPanel; import netscape.javascript.*; import java.awt.image.*; public class Globs extends Applet implements ImageObserver { public void init() { super.init(); setLayout(new BorderLayout(0,0)); resize(615,410); // This is the Graphing Panel for this program. globsPanel = new GlobsPanel(this); add( "Center", globsPanel ); // This holds controls and place for minimal written feedback to user. userPanel = new UserPanel(this); add("East", userPanel); // These are the images in the exploding teacher animation. deadMather = getImage(getDocumentBase(), "deadMather.gif"); liveMather = getImage(getDocumentBase(), "liveMather.gif"); // This begin loading of primary teacher image. prepareImage( liveMather, this ); explosion = getImage(getDocumentBase(), "Explosion.gif"); mushroom = getImage(getDocumentBase(), "Mushroom.gif"); // This begin loading of remaining images. globsPanel.setImages(); // This object allows applet to communicate with pluggin via JavaScript. mainWindow = JSObject.getWindow(this); } /** * This callback routine observes the loading of the primary teacher image * and waits until it is loaded to trigger the laffThread. The laffThread plays * at the start of each game and here begins the program the same way. **/ public synchronized boolean imageUpdate(Image liveMather, int flags, int x, int y, int w, int h) { if ((flags & ImageObserver.ALLBITS) != 0) { Laff laff = new Laff( globsPanel, this ); Thread laffThread = new Thread( laff, "laffThread" ); laffThread.setPriority(Thread.NORM_PRIORITY); laffThread.start(); } return (flags & (ALLBITS|ABORT)) == 0; } /** * This plays the audio clips, (Laff and explosion) indicated by the parameter. **/ public void playClip( int clipChoice ) { mainWindow.eval("document.embeds[" + clipChoice + "].play()"); } UserPanel userPanel; GlobsPanel globsPanel; Image deadMather; Image liveMather; Image explosion; Image mushroom; JSObject mainWindow; }// End Class Globs /* A basic extension of the java.awt.Panel class */ import java.awt.*; import java.applet.*; public class GlobsPanel extends Panel { /** * The initialization sets up a reference to applet. **/ public GlobsPanel( Globs globs ) { this.globs = globs; setLayout(null); resize(410,410); // This creates a new array for holding the users lines drawn. clearLines(); } /** * This begins the loading of images other than primary teacher. No observation is used. **/ public void setImages() { this.deadMather = globs.deadMather; this.liveMather = globs.liveMather; this.explosion = globs.explosion; this.mushroom = globs.mushroom; prepareImage( deadMather, this ); prepareImage( explosion, this ); prepareImage( mushroom, this ); blowMather[0] = explosion; blowMather[1] = mushroom; blowMather[2] = deadMather; } /** * This converts Java Graph Panel x coordinate to coordinate grid x value. **/ public double java2gridX( int x ) { double myx; myx = (double)(x - 200)/((double)20); return myx; } /** * This converts Java Graph Panel y coordinate to coordinate grid y value. **/ public double java2gridY( int y ) { double myy; myy = (double)( 200 - y)/(20F); return myy; } /** * This converts coordinate grid x value to Java Graph Panel x coordinate. **/ public int grid2javaX( double myx ) { int x; x = (int)Math.round( (20F)*myx + 200 ); return x; } /** * This converts coordinate grid y value to Java Graph Panel y coordinate. **/ public int grid2javaY( double myy ) { int y; y = (int)Math.round( 200 - (20F)*myy ); return y; } /** * This returns y value of the line determined by the x coordinate the integer line number. * * @param myx is the x coordinate grid value * @param i is the number of the line to be drawn. * @return myy is the y coordinate grid value * **/ public double evalFunctionLo( double myx, int i ) { double myy = 0; myy = lines.getSlope(i)*myx + lines.getIntercept(i); return myy; } /** * This returns x value of the line determined by the y coordinate the integer line number. * * @param myy is the y coordinate grid value * @param i is the number of the line to be drawn. * @return myx is the x coordinate grid value * **/ public double evalFunctionHi( double myy, int i ) { double myx = 0; myx = ( myy - lines.getIntercept(i) )/lines.getSlope(i); return myx; } /** * This quick line drawer is no longer in use in favor of threaded version. **/ public int[][] currentPlot() { Graphics g = this.getGraphics(); int[][] linePoints = new int[160][2]; int numLines; double myx, myy; numLines = lines.getNumLines(); for ( int i = 0 ; i < 160 ; i++ ) { myx = ((double)i)/8 - 10; linePoints[i][0] = grid2javaX( myx ); linePoints[i][1] = grid2javaY( evalFunctionLo( myx , numLines -1 ) ); } return linePoints; } /** * This draws the old lines, in red, that the user input prior to current. **/ public void oldPlot() { Graphics g = this.getGraphics(); int oldx, oldy, x, y; double myx, myy; for ( int j = 0; j < lines.getNumLines() - 1; j++ ) { oldx = grid2javaX( -20F ); oldy = grid2javaY( evalFunctionLo( -20F, j )); for ( int i = 1 ; i <= 320 ; i++ ) { myx = ((double)i)/8 - 20; x = grid2javaX( myx ); y = grid2javaY( evalFunctionLo( myx, j ) ); g.setColor( Color.red ); g.drawLine( oldx, oldy, x, y ); g.setColor(Color.black); oldx = x; oldy = y; } } } /** * This draws the coordinate grid and the images of the currently living math teachers. **/ public void paint(Graphics g) { g.drawLine( 0, 200, 400, 200 ); g.drawLine( 200, 0, 200, 400 ); for ( int i = 1; i <= 10; i++ ) { g.drawLine( 200 + 20*i, 195, 200 + 20*i, 205 ); g.drawLine( 200 - 20*i, 195, 200 - 20*i, 205 ); } for ( int i = 1; i <= 10; i++ ) { g.drawLine( 195, 200 + 20*i, 205, 200 + 20*i ); g.drawLine( 195, 200 - 20*i, 205, 200 - 20*i ); } for ( int i = 1; i <= 10; i++ ) { g.drawString( i + "", 196 + 20*i, 217 ); g.drawString( "-" + i, 190 - 20*i, 217 ); } for ( int i = 1; i <= 10; i++ ) { g.drawString( "-" + i, 208, 205 + 20*i ); g.drawString( i + "", 208, 206 - 20*i ); } oldPlot(); for ( int i = 0; i < 10; i++ ) { if ( showPoint[i] ) { g.drawImage( liveMather, points[i][0] - 13, points[i][1] - 13 , this ); } } super.paint(g); } /** * This simply draws the image referred to. These are the images in the BLowUp sequence. * * @param i is array value of the teacher to be exploded. * @param j points to one of the images in the BlowUp sequence, that reside in killMather * array. **/ public void killMather( int i, int j ) { Graphics g = getGraphics(); if ( j != 1 ) { g.drawImage( blowMather[j], points[i][0] - 13, points[i][1] - 13 , this ); } else { g.drawImage( blowMather[j], points[i][0] - 13, points[i][1] - 26 , this ); } } /** * This uses the local random random function to create new coordinates for the locations * of teachers. Called to do all ten at start of the game. **/ public void newPoints() { for ( int i = 0; i < 10; i++ ) { points[i] = this.random(); showPoint[i] = true; } repaint(); } /** * This erases all math teachers drawn. **/ public void clearPoints() { for ( int i = 0; i < 10; i++ ) { showPoint[i] = false; } repaint(); } /** * This scales the java random number to create a random integer point in the games * coordinates. The coordinates are converted to Java Graphing Panel coordinates before * return. * @return a pair of Java Graphing Panel coordinates in an array. **/ public int[] random() { int[] point = new int[2]; point[0] = grid2javaX( (double)Math.round( Math.random()*20 - 10 )); point[1] = grid2javaY( (double)Math.round( Math.random()*20 - 10 )); return point; } /** * This erases all lines by simply creating a new array to hold the next set of lines. **/ public void clearLines() { Lines tempLines = new Lines(); lines = tempLines; } public void setSlope( double slope ) { slope = slope; } public void setIntercept( double x ) { intercept = x; } public void incrementEx() { if ( currentEx < exCount - 1 ) { currentEx++; } else { currentEx = 0; } } public void setShowPoint( int i, boolean show ) { showPoint[i] = show; } /** * @return This gets the array that keeps track of which teacher had been blown up * and which is still showing. **/ public boolean[] getShowPoint() { return showPoint; } /** * @return This gets the array that contains all points holding locations of * math teachers. **/ public int[][] getPoints() { return points; } boolean[] showPoint = new boolean[10]; int[][] points = new int[10][2]; Lines lines = new Lines(); private int exCount; private int currentEx = 0; private double coefficient[]; private double xTrans[]; private double yTrans[]; private double slope = 1F; private double intercept = 0F; Image deadMather; Image liveMather; Image explosion; Image mushroom; Image[] blowMather = new Image[3]; Globs globs; private double numerator[] = new double[4]; private double denominator[] = new double[4]; private int x,y; private double myx, myy; private int vertTrans, horizTrans; private int horizScale = 20; private int vertScale = 20; private int horizIncrement = 1; private int vertIncrement = 1; }// End Class GlobsPanel import java.awt.*; import Globs; /** * This class provides the user interface Control Panel. There is a * button that restarts the game and one that draws a new line. The * latter is redundent as pressing enter while a text box has focus * does the same. There are also the two text boxes for inputting * slope and intercept of the line, and the text symbols of the * line's equation. There are also routines in the paint program for * drawing the errant teacher picture and Oops! message. **/ public class UserPanel extends Panel { public UserPanel( Globs globs ) { this.globs = globs; setLayout(null); this.reshape(413,0,200,400); // This button restarts the game when clicked. newButton = new java.awt.Button("New"); newButton.reshape(24,30,60,23); add(newButton); // This button draws a new line. drawButton = new java.awt.Button("Draw Line"); drawButton.reshape(107,30,60,23); add(drawButton); // This textbox is for the user to enter the slope of a new line slopeField = new java.awt.TextField(); slopeField.setText("1"); slopeField.reshape(5,150,40,30); add(slopeField); // This textbox is for the user to input the y-intercept of a // new line. interceptField = new java.awt.TextField(); interceptField.setText("0"); interceptField.reshape(145,150,40,30); add(interceptField); } /** * This event handler does one of two things. * * 1. The first part initiates the drawing of a new line. It reads * the text in the textboxes, converting the text into doubles. * It catches the Number Format Exception by simply changing the * text and double to the default value. * * 2. The second part starts a new game creating a new Laff Thread * Look there for the details of startup. **/ public boolean action( Event evt , Object arg ) { // This gets the slope and y-intercept, draws a new line. Most of // work takes place in the appendThreads routine. There is a call // there to new DrawLine that starts the drawing process. if (( evt.target instanceof TextField )||(( evt.target instanceof Button )&& ( arg.equals("Draw Line")))) { String text = slopeField.getText(); try { slope = (Double.valueOf(text)).doubleValue(); globs.globsPanel.setSlope( slope ); } // This catches the Number Format Exception by simply changing the // text and double to the default value. catch ( NumberFormatException e ) { slopeField.setText( "1.0" ); slope = (double)1; repaint(); } text = interceptField.getText(); try { intercept = (Double.valueOf(text)).doubleValue(); globs.globsPanel.setIntercept( intercept ); } // This catches the Number Format Exception by simply changing the // text and double to the default value. catch ( NumberFormatException e ) { interceptField.setText( "0.0" ); intercept = (double)0; repaint(); } globs.globsPanel.lines.appendLines( slope, intercept ); threads.appendThreads( this ); return true; } // This starts the game anew mostly in the call to new Laff. else if (( evt.target instanceof Button )&&( arg.equals("New"))) { Laff laff = new Laff( globs.globsPanel, globs ); Thread laffThread = new Thread( laff, "laffThread" ); laffThread.setPriority(Thread.NORM_PRIORITY); laffThread.start(); return true; } else return false; } /** * This implements a null layout manager by reshaping. it then draws the * text of an equation of a line. Finally it contains the routines that * are called in the Laff Thread. They are turned on and off by control * booleans. The functionality of these routines are described below. **/ public void paint(Graphics g) { this.reshape(413,0,200,400); newButton.reshape(24,30,60,23); drawButton.reshape(107,30,60,23); slopeField.reshape(5,150,50,30); interceptField.reshape(142,150,50,30); int size = getFont().getSize(); setFontSize( 20, g ); g.drawString( "y =", 40, 110 ); g.drawString( "*", 60, 175 ); g.drawString( "x", 77, 169 ); g.drawString( " +", 99, 171 ); setFontSize( 14, g ); setFontSize( size, g ); // This routine paints the "errant" teacher in this panel as an // excuse to play the Laugh Track. if ( doLaff ) { point[0] = (int)(Math.floor(Math.random()*200)); point[1] = (int)(Math.floor(Math.random()*180) + 220); g.drawImage( globs.liveMather, point[0] - 13, point[1] - 13 , this ); doLaff = false; } // This shows the "score" or number of lines drawn when the last // teacher is hit. It stays until the next paint. if ( finished ) { size = getFont().getSize(); setFontSize( 20, g ); g.drawString( "Your score was " + numLines, 10, 240 ); g.drawString( " lines." , 10, 280 ); setFontSize( size, g ); finished = false; } // When the "errant" teacher dissappears, this replaces it with, // "Oops! Missed again!". if ( oops ) { size = getFont().getSize(); setFontSize( 25, g ); g.drawString( "Oops! Missed", 20, 240 ); g.drawString( "again!", 20, 280 ); setFontSize( size, g ); oops = false; } } /** * This method is no longer used. It painted a star instead of the * math teacher picture now used. **/ public void drawPoint( Graphics g, int x, int y ) { g.drawLine( x, y + 2, x, y + 13 ); g.drawLine( x + 1, y + 1, x + 10, y + 10 ); g.drawLine( x + 2, y, x + 13, y ); g.drawLine( x + 1, y - 1, x + 10, y - 10 ); g.drawLine( x, y - 2, x, y - 13 ); g.drawLine( x - 1, y - 1, x - 10, y - 10 ); g.drawLine( x - 2, y, x - 13, y ); g.drawLine( x -1, y + 1, x - 10, y + 10 ); } /** * This method sets the variable that records the number of lines * that have been drawn, sets the indicator that the game is * finished, and calls repaint. **/ public void showScore( int numLines ) { this.numLines = numLines; finished = true; repaint(); } // This method records the existing font and changes the font size of that // font. If there is no font, it creates a "serif". void setFontSize( int size, Graphics g ) { Font f = g.getFont(); if ( f == null ) { f = new Font("serif", Font.PLAIN, 14 ); } else { f = new Font( g.getFont().getName(), g.getFont().getStyle(), size ); } g.setFont(f); } public double getSlope() { return slope; } public double getIntercept() { return intercept; } public void setDoLaff( boolean doLaff ) { this.doLaff = doLaff; } public void setOops( boolean oops ) { this.oops = oops; } int numLines; int[] point = new int[2]; int[][] points = new int[10][2]; boolean oops = false; boolean finished = false; boolean doLaff = false; Threads threads = new Threads(); private double slope; private double intercept; Globs globs; java.awt.Button newButton; java.awt.Button drawButton; java.awt.TextField slopeField; java.awt.TextField interceptField; }// End Class UserPanel import java.lang.Runnable; import java.awt.*; /** * This thread handles the drawing of the line when the user clicks * the Draw Line button or hits enter when a textbox has focus. It * gets an array of 160 points and connects them to draw the linw. * In each loop, it draws a segment and checks to see if it is within * 13 pixels of the center of a teacher. If so, it "Blows Up" that * teacher, backs up six segments, and resumes drawing. At each loop * it pauses 1.5 seconds. When the line is drawn, it checks to see * if the game is over. If so, it shows the score. **/ public class DrawLine implements java.lang.Runnable { public DrawLine( UserPanel userPanel ) { this.userPanel = userPanel; this.globs = userPanel.globs; this.globsPanel = userPanel.globs.globsPanel; gp = globsPanel.getGraphics(); points = globsPanel.getPoints(); showPoint = globsPanel.getShowPoint(); } public void run() { // This gets an array of 160 points that comprise a plot of the // current line. linePoints = globsPanel.currentPlot(); for ( int i = 1; i < 160; i++ ) { gp.setColor( Color.blue ); gp.drawLine( linePoints[i - 1][0], linePoints[i - 1][1], linePoints[i][0], linePoints[i][1] ); gp.setColor(Color.black); // This checks to see if it is within 13 pixels of the center of // a teacher, blows him up, and set the appropriate showPoint. for ( int j = 0; j < 10; j++ ) { if ( ((( linePoints[i][0] - points[j][0])* ( linePoints[i][0] - points[j][0]) + ( linePoints[i][1] - points[j][1])* ( linePoints[i][1] - points[j][1])) < 169 )&& ( showPoint[j] == true )) { globsPanel.setShowPoint( j, false ); showPoint[j] = false; globs.playClip(1); // This runs the animation of the exploding teacher. For purposes // of speed, the repaint is clipped to the size of the picture. for ( int k = 0; k < 2; k++ ) { globsPanel.killMather( j, k ); try { java.lang.Thread.sleep(500); } catch( InterruptedException e ) {} globsPanel.repaint(points[j][0] - 13, points[j][1] - 26, 26, 39); Thread.yield(); } globsPanel.killMather( j, 2 ); try { java.lang.Thread.sleep(1500); } catch( InterruptedException e ) {} // This erases the last picture in the animation. globsPanel.repaint(points[j][0] - 13, points[j][1] - 13, 26, 26); i = java.lang.Math.max( 0, i - 6 ); } } try { java.lang.Thread.sleep(20); } catch( InterruptedException e ) {} } // This checks to see if the game is over and if so, shows the score. if ( !( showPoint[0]||showPoint[1]||showPoint[2]|| showPoint[3]||showPoint[4]||showPoint[5]|| showPoint[6]||showPoint[7]||showPoint[8]||showPoint[9] ) ) { try { java.lang.Thread.sleep(500); } catch( InterruptedException e ) {} userPanel.showScore(globsPanel.lines.getNumLines()); } } boolean[] showPoint = new boolean[10]; int[][] points = new int[10][2]; int[][] linePoints = new int[160][2]; Graphics gp; UserPanel userPanel; GlobsPanel globsPanel; Globs globs; }//End Class DrawLine import java.awt.*; import java.lang.Thread; import Globs; /** * This class opens a new play of the game. It runs on a separate thread because * it takes some time, so the player can begin play. See Run method for details. **/ public class Laff extends Thread { public Laff( GlobsPanel globsPanel, Globs globs ) { this.globs = globs; this.userPanel = globs.userPanel; this.globsPanel = globsPanel; } /** * This thread starts the game by creating a new set of teacher locations, * ( and showing all of them ) clearing all previously drawn lines, and then * starting the Laff Routine. * The Laff Routine consists of drawing a teacher in the control panel, (ostensibly * by mistake) starting the Chipmunk Laff Track, erasing the teacher, writing the * maessage, "Oops! Missed Again!", and finally erasing that and exiting with * laughter still playing. **/ public void run() { // resets lines and teachers. globsPanel.newPoints(); globsPanel.clearLines(); globsPanel.repaint(); // Causes teacher to be drawn in the UserPanel Control Panel. userPanel.setDoLaff( true ); userPanel.repaint(); // Causes browser to start playing the Laugh Track. globs.playClip(0); try { java.lang.Thread.sleep(3500); } catch( InterruptedException e ) {} // After delay, erases the teacher and paints the "Oops! Missed Again!" // message. userPanel.setOops( true ); userPanel.repaint(); try { java.lang.Thread.sleep(3500); } catch( InterruptedException e ) {} // Ater delay, erases the Oops! mesaage userPanel.repaint(); } Graphics ug, gp; UserPanel userPanel; GlobsPanel globsPanel; Globs globs; }// End Class Laff import java.lang.Double; /** * This data structure is an array of slopes and y-intercepts that comprise * the set of already drawn lines. appendLines changes the size of the array * and adds the newest line. **/ public class Lines { public Lines() { lines = new double[2][1]; } /** * AppendLines changes the size of the array and adds the newest line. * * @param double m is the slope of the new line. * @param double b is the y-intercept of the new line. **/ public void appendLines( double m, double b) { this.m = m; this.b = b; numLines++; double[][] tempLines = new double[2][numLines]; java.lang.System.arraycopy( lines[0], 0,tempLines[0], 0, numLines - 1 ); java.lang.System.arraycopy( lines[1], 0,tempLines[1], 0, numLines - 1 ); tempLines[0][numLines - 1] = m; tempLines[1][numLines - 1] = b; lines = tempLines; } public double getSlope( int i ) { return lines[0][i]; } public double getIntercept( int i ) { return lines[1][i]; } public int getNumLines() { return numLines; } int numLines = 0; double m; double b; double[][] lines; }// End Class Lines import java.lang.Thread; /** * This class maintains an array of threads so that reference to them could * made in a loop. It dynamically increases the size of the array as needed * but does not decrease the size. The aformentioned references were never * needed so this class is unnecessary now. However, the call to new drawline * here begins the line drawing process. **/ public class Threads { public Threads() { threads = new Thread[0]; } public void appendThreads( UserPanel userPanel ) { for ( int i = 0; i < numThreads; i++ ) { if ( threads[i] == null ) { DrawLine drawLine = new DrawLine( userPanel ); threads[i] = new Thread(drawLine); threads[i].start(); notfull = true; } } if ( !notfull ) { DrawLine drawLine = new DrawLine( userPanel ); numThreads++; Thread[] tempThreads = new Thread[numThreads]; java.lang.System.arraycopy( threads, 0,tempThreads, 0, numThreads - 1 ); tempThreads[numThreads - 1] = new Thread(drawLine); threads = tempThreads; threads[numThreads - 1].start(); notfull = false; } notfull = false; } public Thread getthread( int i ) { return threads[i]; } public int getNumThreads() { return numThreads; } boolean notfull = false; int numThreads = 0; Thread[] threads; }// End Class Threads