//******************************************************************************** // Mashimaroid.java // // "Mashimaroid" // // (c) 2001 Maeda Mameo http://hw001.gate01.com/frog/ //******************************************************************************** public class Mashimaroid extends java.applet.Applet implements java.awt.event.MouseListener, java.awt.event.MouseMotionListener, Runnable{ private java.awt.Image imageOff, imageBackground; private java.awt.Graphics gOff, gBackground; private boolean paintRequest; private java.awt.FontMetrics theFontMetrics; private int theWidth, theHeight; private Thread theThread; static final double PI = Math.PI; static final double FITDISTANCE = 47; static final double MAXSPEED = 14; static final double FLOCKWEIGHT = .06; static final double CENTERWEIGHT = .007; static final double TARGETWEIGHT = .1; static final double MIMICWEIGHT = .1; static final double ACCELERATION = 1.3; static final double VISIBLEANGLE = .9 * PI; static final java.awt.Color colorActive = java.awt.Color.pink; static final java.awt.Color colorBackground = java.awt.Color.white; static final java.awt.Color colorLetter = java.awt.Color.lightGray; static final java.awt.Color colorMark = java.awt.Color.red; static final java.awt.Color colorShadow = java.awt.Color.gray; private java.awt.Image imageMashimaro[] = new java.awt.Image[ 12]; static final int MAXMASHIMARONUM = 30; private double mashimaroX[] = new double[MAXMASHIMARONUM]; private double mashimaroY[] = new double[MAXMASHIMARONUM]; private double mashimaroAngle[] = new double[MAXMASHIMARONUM]; private double mashimaroSpeed[] = new double[MAXMASHIMARONUM]; private int mashimaroTarget[] = new int[MAXMASHIMARONUM]; private int mashimaroEscape[] = new int[MAXMASHIMARONUM]; private double mashimaroEscapeAngle[] = new double[MAXMASHIMARONUM]; private int mashimaroDY[] = new int[ MAXMASHIMARONUM]; private int mashimaroVY[] = new int[ MAXMASHIMARONUM]; private int mashimaroNum; private int markedMashimaro; private int preparingImageNum; private int mouseX, mouseY, clickedX, clickedY; private boolean fpsRequest; private String fpsString; static final long MINIMUMDELAY = 20; static final int MAXFPS = 20; //******************************************************************************** //******************************************************************************** public void init(){ java.awt.Dimension d; fpsRequest = false; fpsString = ""; paintRequest = false; setBackground( colorBackground); d = getSize(); theWidth = d.width; theHeight = d.height; imageOff = createImage( theWidth, theHeight); gOff = imageOff.getGraphics(); imageBackground = createImage( theWidth, theHeight); gBackground = imageBackground.getGraphics(); gBackground.setFont( new java.awt.Font( "TimesRoman", java.awt.Font.BOLD, 60)); theFontMetrics = gBackground.getFontMetrics(); addMouseListener( this); addMouseMotionListener( this); } public void start(){ theThread = new Thread( this); theThread.start();} public void stop(){ theThread = null;} public void update( java.awt.Graphics g){ paint(g);} public void paint( java.awt.Graphics g){ if(paintRequest){ if( fpsRequest) showStatus( fpsString); g.drawImage( imageOff, 0, 0, this); paintRequest = false; } } public void mouseClicked( java.awt.event.MouseEvent e){} public void mouseDragged( java.awt.event.MouseEvent e){} public void mouseEntered( java.awt.event.MouseEvent e){ fpsRequest = true;} public void mouseExited( java.awt.event.MouseEvent e){ fpsRequest = false; showStatus( null);} public void mouseMoved( java.awt.event.MouseEvent e){ mouseX = e.getX(); mouseY = e.getY();} public void mousePressed( java.awt.event.MouseEvent e){ if( clickedY < 0){ clickedX = e.getX(); clickedY = e.getY();}} public void mouseReleased( java.awt.event.MouseEvent e){} public boolean imageUpdate( java.awt.Image img, int infoFlags, int x, int y, int width, int height){ if(( infoFlags & java.awt.image.ImageObserver.ALLBITS) != 0){ preparingImageNum--; return false; } return true; } //******************************************************************************** //******************************************************************************** public void run(){ int i; double m; String s; int fpsRefreshCount; long delay, averageTime, latestTimeMillis, t; loadMashimaroImage(); fpsRefreshCount = 0; delay = MINIMUMDELAY; latestTimeMillis = System.currentTimeMillis(); mashimaroNum = MAXMASHIMARONUM; clickedY = 0; while( theThread != null){ if( clickedY >= 0){ if( mashimaroNum < MAXMASHIMARONUM){ mashimaroX[ mashimaroNum] = clickedX; mashimaroY[ mashimaroNum] = clickedY; mashimaroAngle[ mashimaroNum] = 2 * PI * Math.random(); mashimaroSpeed[ mashimaroNum] = MAXSPEED; mashimaroTarget[ mashimaroNum] = -1; mashimaroEscape[ mashimaroNum] = 0; markedMashimaro = mashimaroNum; mashimaroNum++; } else resetMashimaro(); gBackground.setColor( colorBackground); gBackground.fillRect( 0, 0, theWidth, theHeight); s = mashimaroNum + " MashiMaros"; gBackground.setColor( colorLetter); gBackground.drawString( s, ( theWidth - theFontMetrics.stringWidth( s)) / 2, ( theHeight + theFontMetrics.getAscent()) / 2); for( i = 0; i < theHeight; i += 3) gBackground.drawLine( 0, i, theWidth - 1, i); gBackground.setColor( colorBackground); for( i = 1; i < theHeight; i += 3) gBackground.drawLine( 0, i, theWidth - 1, i); clickedY = -1; } moveMashimaro(); sleeper( delay); while( paintRequest) sleeper( 1); gOff.drawImage( imageBackground, 0, 0, this); if( markedMashimaro >= 0){ m = 1 + (double)mashimaroDY[ markedMashimaro] / 50; gOff.setColor( colorMark); gOff.fillOval( (int)( mashimaroX[ markedMashimaro] - m * 30), (int)( mashimaroY[ markedMashimaro] - m * 19), (int)( m * 60), (int)( m * 38)); } drawConnection(); drawMashimaro(); paintRequest = true; repaint(); if( ++fpsRefreshCount == MAXFPS){ t = System.currentTimeMillis(); averageTime = ( t - latestTimeMillis) / MAXFPS; fpsString = "" + 1000 / averageTime + "." + 10000 / averageTime % 10 + " frames / second"; delay = Math.max( MINIMUMDELAY, 1000 / MAXFPS - averageTime + delay); latestTimeMillis = t; fpsRefreshCount = 0; } } } private void resetMashimaro(){ int i; double a; mashimaroNum = 7; markedMashimaro = -1; for( i = 0; i < mashimaroNum; i++){ a = 2 * PI * i / mashimaroNum; mashimaroX[ i] = ( .5 + .1 * Math.cos( a)) * theWidth; mashimaroY[ i] = ( .5 + .1 * Math.sin( a)) * theHeight; mashimaroAngle[ i] = a + .5 * PI; mashimaroSpeed[ i] = .3 * MAXSPEED; mashimaroTarget[ i] = -1; mashimaroEscape[ i] = 0; mashimaroDY[ i] = 0; mashimaroVY[ i] = 0; } } private void drawConnection(){ int i, j, t; gOff.setColor( colorShadow); for( i = 0; i < mashimaroNum; i++){ t = mashimaroTarget[ i]; if( t >= 0){ for( j = 0; j < 4; j++){ gOff.drawLine( (int)mashimaroX[ i] + j % 2, (int)mashimaroY[ i] + j / 2, (int)mashimaroX[ t] + j % 2, (int)mashimaroY[ t] + j / 2); } } } } private void drawMashimaro(){ int i, j, n, zOrder[] = new int[ mashimaroNum]; double m; for( i = 0; i < mashimaroNum; i++) zOrder[ i] = i; for( i = 0; i < mashimaroNum - 1; i++){ for( j = mashimaroNum - 2; j >= i; j--){ if( mashimaroY[ zOrder[ j]] < mashimaroY[ zOrder[ j + 1]]){ n = zOrder[ j]; zOrder[ j] = zOrder[ j + 1]; zOrder[ j + 1] = n; } } } for( i = mashimaroNum - 1; i >= 0; i--){ n = zOrder[ i]; mashimaroDY[ n] += mashimaroVY[ n]; if( mashimaroDY[ n] != 0) mashimaroVY[ n]++; else if( mashimaroTarget[ n] < 0) mashimaroVY[ n] = -5; else if( mashimaroVY[ n] != 0) mashimaroVY[ n] = 1 - mashimaroVY[ n]; } for( i = mashimaroNum - 1; i >= 0; i--){ n = zOrder[ i]; m = 1 + (double)mashimaroDY[ n] / 50; if( mashimaroTarget[ n] < 0 && n != markedMashimaro){ gOff.setColor( colorActive); gOff.fillOval( (int)( mashimaroX[ n] - m * 30), (int)( mashimaroY[ n] - m * 19), (int)( m * 60), (int)( m * 38)); } gOff.setColor( colorShadow); gOff.fillOval( (int)( mashimaroX[ n] - m * 20), (int)( mashimaroY[ n] - m * 13), (int)( m * 40), (int)( m * 26)); } for( i = mashimaroNum - 1; i >=0; i--){ n = zOrder[ i]; gOff.drawImage( imageMashimaro[ ((int)( 12 * ( mashimaroAngle[ n] + 2 * PI / 24) / 2 / PI) + 9) % 12], (int)( mashimaroX[ n] - 21), (int)( mashimaroY[ n] + 2 * mashimaroDY[ n] - 79 + 10), null); } } private void moveMashimaro(){ int i, t; double a, s, x, y, cx, cy, vx, vy; for( i = 0; i < mashimaroNum; i++){ x = mashimaroX[ i]; y = mashimaroY[ i]; a = mashimaroAngle[ i]; s = mashimaroSpeed[ i]; x += Math.cos( a) * s; y += Math.sin( a) * s; if( x < 0) x += theWidth; else if( x >= theWidth) x -= theWidth; if( y < 0) y += theHeight; else if( y >= theHeight) y -= theHeight; mashimaroX[ i] = x; mashimaroY[ i] = y; mashimaroAngle[ i] = a; if( fpsRequest && Math.pow( x - mouseX, 2) + Math.pow( y - mouseY, 2) < 20 * 20){ markedMashimaro = i; mashimaroDY[ i] = 0; mashimaroVY[ i] = -5; } } cx = cy = 0; for( i = 0; i < mashimaroNum; i++){ cx += mashimaroX[ i]; cy += mashimaroY[ i]; } cx /= mashimaroNum; cy /= mashimaroNum; for( i = 0 ; i < mashimaroNum; i++){ x = mashimaroX[ i]; y = mashimaroY[ i]; a = mashimaroAngle[ i]; s = mashimaroSpeed[ i]; t = mashimaroTarget[ i]; if( mashimaroEscape[ i] == 0){ t = findTarget( i); if( t < 0){ a += FLOCKWEIGHT * angleDifference( getAngle( cx - x, cy - y), a); s = ( 1 - .7 * Math.random()) * MAXSPEED; } else{ vx = mashimaroX[ t] - x; vy = mashimaroY[ t] - y; a += CENTERWEIGHT * angleDifference( getAngle( theWidth / 2 - x, theHeight / 2 - y), a); a += TARGETWEIGHT * angleDifference( getAngle( vx, vy), a); a += MIMICWEIGHT * angleDifference( mashimaroAngle[ t], a); if( vx * vx + vy * vy < FITDISTANCE * FITDISTANCE) s = 3 + 7 * Math.random(); else s *= ACCELERATION; if( s > MAXSPEED) s = MAXSPEED; else if( s < 1) s = 1; if( Math.random() < .002){ //********** Someone occasionally escapes from the queue. s = MAXSPEED; if( t == 0) mashimaroEscapeAngle[ i] = 0; else if( angleDifference( getAngle( vx, vy), a) < 0) mashimaroEscapeAngle[ i] = .1 * PI; else mashimaroEscapeAngle[ i] = -.07 * PI; t = -1; mashimaroEscape[ i] = 9; mashimaroDY[ i] = 0; mashimaroVY[ i] = -5; } } } else{ a += mashimaroEscapeAngle[ i]; mashimaroEscape[ i]--; } if( a < 0) a += 2 * PI; else if( a >= 2 * PI) a -= 2 * PI; mashimaroAngle[ i] = a; mashimaroSpeed[ i] = s; mashimaroTarget[ i] = t; } } private int findTarget( int n){ //********** Find a target to chase. int i, target; double a, b, d, x, y, vx, vy, targetD; targetD = Math.pow( .6 * theHeight, 2); target = -1; x = mashimaroX[ n]; y = mashimaroY[ n]; a = mashimaroAngle[ n]; for( i = 0; i < mashimaroNum; i++) if( i != n){ vx = mashimaroX[ i] - x; vy = mashimaroY[ i] - y; d = vx * vx + vy * vy; if( d < targetD){ //********** The target must be in short distance. b = angleDifference( a, mashimaroAngle[ i]); if( b > -.6 * PI && b < .6 * PI){ //********** The direction of the target must be similar. b = angleDifference( getAngle( vx, vy), a); if( b > -.5 * VISIBLEANGLE && b < .5 * VISIBLEANGLE){ //********** The target must be in sight. targetD = d; target = i; } } } } return target; } private double getAngle( double vx, double vy){ double a; if( vy == 0) if( vx < 0) return PI; else return 0; a = Math.acos( vx / Math.sqrt( vx * vx + vy * vy)); if( vy > 0) return a; else return ( 2 * PI - a); } private double angleDifference( double t, double s){ double a; a = t - s; if( a > PI) return( a - 2 * PI); if( a < -PI) return( a + 2 * PI); return a; } private void sleeper( long t){ try{ theThread.sleep( t);} catch( InterruptedException e){}} private void loadMashimaroImage(){ int a, i, w, h, pix[]; java.awt.Image imageMashimaroSource; preparingImageNum = 1; imageMashimaroSource = getImage( getDocumentBase(), "Mashimaro12.gif"); if( prepareImage( imageMashimaroSource, this)) preparingImageNum--; while( preparingImageNum > 0) sleeper( 10); w = imageMashimaroSource.getWidth( this); h = imageMashimaroSource.getHeight( this); pix = new int[ w * h]; try{ new java.awt.image.PixelGrabber( imageMashimaroSource, 0, 0, w, h, pix, 0, w).grabPixels();} catch( InterruptedException e){} a = pix[ 1]; for( i = 0; i < w * h; i++) if( pix[ i] == a) pix[ i] = 0; for( i = 0; i < 12; i++){ preparingImageNum = 1; imageMashimaro[ i] = createImage( new java.awt.image.MemoryImageSource( 42, 79, pix, 1 + ( i % 6) * 43 + i / 6 * 79 * w, w)); if( prepareImage( imageMashimaro[ i], this)) preparingImageNum--; while( preparingImageNum > 0) sleeper( 10); } } }