// Warp// By Karl Hornell, June 10, 1996// Last modified June 27import java.awt.*;import java.awt.image.*;import java.applet.AudioClip;import java.net.*;import java.awt.Font;public final class warp extends java.applet.Applet implements Runnable{	int i,j,k,p,q,r,counter=0,mileage,windowHeight,windowStep,loadedLev=-1;	int runMode,objCount,level,lives,currentKey=0,bulletIx,currentSnd=-1;	int objX[],objY[],objType[],objMode[],objLook[],objPar[];	int brickPos[],brickType[],brickArea[];	int brickMap[];	int sortBuf1[],sortBuf2[],touching[]={0,0,0,0};	int fixQueueX[],fixQueueY[],fixQueueBrick[];	Font fo;	boolean objDraw[],bulletOn,showLights,drawScore;	final int maxObj=10,maxLev=9,maxFix=10,objectImages=26,maxOtherBricks=6;	final int cyclic[]={0,1,2,1},shark[]={31,32,33,34,35,35,35,35,35,35,35,34,33,32,31};	final int groundMouth[]={46,47,48,49,50,51,52,52,52,51,50,49,48,47,46};	final int cyclic2[]={0,1,2,3,2,1};	final int maxEnemies=4,levObjStart[]={13,19,29,36,43,53,61,74,82,90};	final int blastable=14,totalBricks=10;	final int objImX[]={240,280,240,280,296,280,96,128,160,192,224,256,288,		248,288,328,368,408,448, 288,328,368,408,448,488,528,568,608,648,		312,352,392,424,456,488,528, 288,328,368,408,448,488,528,		280,320,360,400,432,464,504,544,584,624, 288,328,368,408,448,488,528,568,		328,368,408,448,488,528,568,600,632,664,704,744,784,		320,360,400,440,480,520,560,600, 320,360,400,440,480,520,560,576};	final int objImY[]={128,128,160,160,160,176,96,96,96,96,96,96,96};	final int objImW[]={40,40,40,16,16,16,32,32,32,32,32,32,32,		40,40,40,40,40,40, 40,40,40,40,40,40,40,40,40,40,		40,40,32,32,32,40,40, 40,40,40,40,40,40,40, 40,40,40,32,32,40,40,40,40,40,		40,40,40,40,40,40,40,40, 40,40,40,40,40,40,32,32,32,40,40,40,40,		40,40,40,40,40,40,40,40, 40,40,40,40,40,40,16,16};	final int objImPractW[]={32,32,32,16,16,16,32,32,32,32,32,32,32,		32,32,32,32,32,32, 32,32,32,32,32,32,32,32,32,32,		32,32,32,32,32,32,32, 32,32,32,32,32,32,32, 32,32,32,32,32,32,32,32,32,32,		32,32,32,32,32,32,32,32, 32,32,32,32,32,32,32,32,32,32,32,32,32,		32,32,32,32,32,32,32,32, 32,32,32,32,32,32,16,16};	final int objImH[]={32,32,32,16,16,16,32,32,32,32,32,32,32,		32,32,32,32,32,32, 32,32,32,32,32,32,32,32,32,32,		32,32,32,32,32,32,32, 32,32,32,32,32,32,32, 32,32,32,32,32,32,32,32,32,32,		32,32,32,32,32,32,32,32, 32,32,32,32,32,32,32,32,32,32,32,32,32,		32,32,32,32,32,32,32,32, 32,32,32,32,32,32,16,16};	final int objNum[]={0,1,2,3,4,5,6,7,8,9,10,11,12, 13,14,15,16,17,18,		13,14,15,16,17,18,19,20,21,22, 13,14,15,16,17,18,19, 13,14,15,16,17,18,19,		13,14,15,16,17,18,19,20,21,22, 13,14,15,16,17,18,19,20,		13,14,15,16,17,18,19,20,21,22,23,24,25, 13,14,15,16,17,18,19,20,		13,14,15,16,17,18,19,20};		final int brickX[]={64, 0,32,72,104,144,176,208, 0,32,72,104,144,184,216,256,		0,32,64,96,128,168,200,240,280, 0,32,64,96,136,176,216,248,		0,32,64,104,136,176,208,240, 0,32,64,104,136,176,216,248,		0,32,72,112,152,192,224,264,296, 0,32,72,112,144,184,216,256,288,		0,32,72,104,136,176,216,256,288};	final int brickW[]={32, 32,40,32,40,32,32,40, 32,40,32,40,32,32,40,32,		32,32,32,32,40,32,40,40,32, 32,32,32,40,40,40,32,40, 32,32,40,32,40,32,32,40,		32,32,40,32,40,40,32,40, 32,40,40,40,40,32,40,32,32, 32,40,40,32,40,32,40,32,32,		32,40,32,32,40,40,40,32,32};	final int levBrickStart[]={1,8,16,25,33,41,49,58,67,76};	final int brickQual[]={0, 0,3,0,1,0,0,1, 0,3,0,3,0,0,1,0, 0,4,4,4,3,4,1,3,4,		0,0,0,1,1,3,0,1, 0,4,3,4,1,4,4,1, 0,0,3,0,1,0,0,1, 0,3,0,3,0,4,1,0,0,		0,1,3,0,3,0,1,0,0, 0,3,0,0,1,1,3,4,0};	final int brickIm[]={0, 1,2,3,4,5,6,7, 1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8,9,		1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8,9,		1,2,3,4,5,6,7,8,9, 1,2,3,4,5,6,7,8,9};	final int levBackgr[]={1,8,16,25,33,41,49,58,67};	final int blastBrick[]={2,9,11,20,23,30,35,43,50,52,60,62,68,73};	final int brickPoints[]={15,15,15,20,10,20,25,15,25,20,20,20,25,25};	final double bricksPerLine[]={3,3.2,3,3.1,3,3.2,3.1,3.2,2.9};	final int levBrick[]={2,4,5,6,7,0, 9,11,13,14,15,0, 17,18,19,20,22,23,		26,27,28,29,30,32, 34,35,37,38,39,40, 42,43,45,46,47,48, 50,52,54,55,56,57,		59,60,62,64,65,66, 68,70,71,72,73,75};	final double randBrick[]={0.25,0.5,0.75,0.85,1,1, 0.2,0.4,0.55,0.70,1,1,		0.15,0.30,0.4,0.55,0.80,1, 0.1,0.35,0.5,0.65,0.85,1, 0.15,0.3,0.4,0.55,0.70,1,		0.14,0.46,0.54,0.70,0.85,1, 0.22,0.4,0.5,0.68,0.82,1, 0.18,0.4,0.6,0.75,0.83,1,		0.2,0.34,0.52,0.67,0.83,1};	final int levEnemy[]={7,8,0,0, 8,12,0,0, 13,14,0,0, 7,15,0,0, 8,14,0,0, 15,13,7,0,		15,8,7,0, 7,15,0,0, 8,16,0,0};	final int enemyPoints[]={25,30,0,0, 30,40,0,0, 40,50,0,0, 35,30,0,0, 30,50,0,0,		30,40,25,0, 20,30,25,0, 25,25,0,0, 30,25,0,0};	final double randEnemy[]={0.5,1,1,1, 0.5,1,1,1, 0.5,1,1,1, 0.5,1,1,1, 0.5,1,1,1,		0.4,0.6,1,1, 0.4,0.7,1,1, 0.5,1,1,1, 0.7,1,1,1};	final int levLength[]={700,700,700,700,700,700,700,700,700};	Image offImage,landscape,defaultStrip,modStrip,logo,bricks[];	Image panel,outline[],objects[],scoreDisp,clearDisp,clearScreen;	AudioClip sound[];	Graphics offGraphics,landscapeG,defaultSG,modSG,scoreDG,clearDG,clearSG;	Color bgcolor;	ImageFilter filter;	Thread updateThread;	long startTime,score,newScore,tempScore,highScores[]={0,0,0,0,0};	Math m;	public void init()	{		bgcolor=findBGColor();		setBackground(bgcolor);		fo=new Font("Courier",Font.BOLD,14);		setFont(fo);		sound=new AudioClip[3];		sound[0] = getAudioClip(getCodeBase(),"warpsnd0.au");		sound[1] = getAudioClip(getCodeBase(),"warpsnd1.au");		sound[2] = getAudioClip(getCodeBase(),"warpsnd2.au");				bricks=new Image[totalBricks];		brickMap=new int[22*9];		outline=new Image[5];		objects=new Image[objectImages];		defaultStrip=createImage(288,32);		defaultSG=defaultStrip.getGraphics();		modStrip=createImage(288,32);		modSG=modStrip.getGraphics();		scoreDisp=createImage(74,13);		scoreDG=scoreDisp.getGraphics();		scoreDG.setColor(Color.lightGray);		scoreDG.fillRect(0,0,74,13);		scoreDG.setColor(Color.black);		scoreDG.setFont(fo);		clearDisp=createImage(74,13);		clearDG=clearDisp.getGraphics();		clearDG.setColor(Color.lightGray);		clearDG.fillRect(0,0,74,13);		clearScreen=createImage(288,8);		clearSG=clearScreen.getGraphics();		clearSG.setColor(Color.black);		clearSG.fillRect(0,0,288,8);		getMainGraphics();		System.gc();				objX=new int[maxObj];		objY=new int[maxObj];		objType=new int[maxObj];		objMode=new int[maxObj];		objLook=new int[maxObj];		objPar=new int[maxObj];		objDraw=new boolean[maxObj];		brickPos=new int[5];		brickType=new int[5];		brickArea=new int[9];		fixQueueX=new int[maxFix];		fixQueueY=new int[maxFix];		fixQueueBrick=new int[maxFix];		sortBuf1=new int[maxObj];		sortBuf2=new int[maxObj];							offImage=createImage(288,288);		offGraphics=offImage.getGraphics();		offGraphics.setFont(fo);		landscape=createImage(288,608);		landscapeG=landscape.getGraphics();		landscapeG.setFont(fo);		preparePresentation();		handleSound(2,false);		resize(320,400);	}	public Color findBGColor() // Convert hexadecimal RGB parameter to color	{		int hex[];		String s,h="0123456789abcdef";		Color c;		hex=new int[6];		s=getParameter("bgcolor");		if ((s!=null)&&(s.length()==6))		{			for (i=0;i<6;i++)				for (j=0;j<16;j++)					if (s.charAt(i)==h.charAt(j))						hex[i]=j;			c=new Color(hex[0]*16+hex[1],hex[2]*16+hex[3],hex[4]*16+hex[5]);		}		else			c=Color.lightGray; // Default		return c;	}	public void getMainGraphics() // Load and process the most common graphics	{		Image collection;		MediaTracker tracker;		int i;				tracker=new MediaTracker(this);		collection = getImage(getCodeBase(),"warp0.gif");		tracker.addImage(collection,0);		try		{			tracker.waitForID(0);		}			catch(InterruptedException e) {}					logo=createImage(new FilteredImageSource(				collection.getSource(),new CropImageFilter(0,128,240,70)));		tracker.addImage(logo,1);		bricks[0]=createImage(new FilteredImageSource(				collection.getSource(),new CropImageFilter(brickX[0],96,				32,32)));		tracker.addImage(bricks[0],1);		outline[0]=createImage(new FilteredImageSource(			collection.getSource(),new CropImageFilter(0,96,16,32)));		tracker.addImage(outline[0],1);		outline[1]=createImage(new FilteredImageSource(			collection.getSource(),new CropImageFilter(16,96,16,32)));		tracker.addImage(outline[1],1);		outline[2]=createImage(new FilteredImageSource(			collection.getSource(),new CropImageFilter(32,96,32,16)));		tracker.addImage(outline[2],1);		outline[3]=createImage(new FilteredImageSource(			collection.getSource(),new CropImageFilter(32,112,16,16)));		tracker.addImage(outline[3],1);		outline[4]=createImage(new FilteredImageSource(			collection.getSource(),new CropImageFilter(48,112,16,16)));		tracker.addImage(outline[4],1);		panel=createImage(new FilteredImageSource(				collection.getSource(),new CropImageFilter(0,0,320,96)));		tracker.addImage(panel,1);		for (i=0;i<levObjStart[0];i++)		{			objects[i]=createImage(new FilteredImageSource(				collection.getSource(),new CropImageFilter(objImX[i],objImY[i],				objImW[i],objImH[i])));			tracker.addImage(objects[i],1);		}				try		{			tracker.waitForID(1);		}			catch(InterruptedException e) {}	}	public void getLevelGraphics(int lev) // Fetch new graphics when entering new level	{		Image collection;		MediaTracker tracker;		int i,j=13;		tracker=new MediaTracker(this);		collection = getImage(getCodeBase(),"warp"+(lev+1)+".gif");		tracker.addImage(collection,0);		try		{			tracker.waitForID(0);		}			catch(InterruptedException e) {}		if (lev!=loadedLev)		{			for (i=levObjStart[lev];i<levObjStart[lev+1];i++)			{				objects[objNum[i]]=createImage(new FilteredImageSource(					collection.getSource(),new CropImageFilter(objImX[i],0,					objImW[i],objImH[i])));				tracker.addImage(objects[objNum[i]],1);			}			for (i=levBrickStart[lev];i<levBrickStart[lev+1];i++)			{				bricks[brickIm[i]]=createImage(new FilteredImageSource(					collection.getSource(),new CropImageFilter(brickX[i],0,					brickW[i],32)));				tracker.addImage(bricks[brickIm[i]],1);			}			try			{				tracker.waitForID(1);			}				catch(InterruptedException e) {}			loadedLev=lev;		}	}	public void handleSound(int sndNum, boolean loop)	{		if (currentSnd>=0)			sound[currentSnd].stop();		if (sndNum>=0)			if (loop)				sound[sndNum].loop();			else				sound[sndNum].play();		currentSnd=sndNum;	}	public void preparePresentation()	{		windowHeight=0;		windowStep=2;		paintBackground(0);		runMode=1;		for (i=0;i<maxObj;i++)			objType[i]=0;		objCount=0;		activateObject(128,240,3,0,0,128,true); // Activate dummy spaceship		level=9;		lives=4;		score=0;		newScore=0;		scoreDG.drawImage(clearDisp,0,0,this);		showLights=true;		drawScore=true;	}	public void paintBackground(int brickNum) // Fill the background with given brick	{		for (j=0;j<9;j++)			defaultSG.drawImage(bricks[brickIm[brickNum]],j*32,0,this);		modSG.drawImage(defaultStrip,0,0,this);		for (j=0;j<19;j++)			landscapeG.drawImage(defaultStrip,0,j*32,this);		for (j=0;j<maxFix;j++)			fixQueueBrick[j]=0;	}	public void scrollScreen() // Move screen window and project onto offImage	{		windowHeight-=windowStep;		if (windowHeight<0)			windowHeight+=320;		offGraphics.drawImage(landscape,0,-windowHeight,this);	}	public void drawObjects() // Draw each object onto offImage	{		int i,j,k;		for (i=0;i<objCount;i++)		{			sortBuf1[i]=i;			sortBuf2[i]=objX[i];		}		for (i=0;i<(objCount-1);i++)			for (j=i;j<objCount;j++)				if (sortBuf2[j]>sortBuf2[j+1])				{					k=sortBuf1[j];					sortBuf1[j]=sortBuf1[j+1];					sortBuf1[j]=k;					k=sortBuf2[j];					sortBuf2[j]=sortBuf2[j+1];					sortBuf2[j]=k;				}		for (i=0;i<objCount;i++)			if ((objType[sortBuf1[i]]>0) && objDraw[sortBuf1[i]])				offGraphics.drawImage(objects[objNum[objLook[sortBuf1[i]]]],objX[sortBuf1[i]],					objY[sortBuf1[i]],this);	}	public void activateObject(int x,int y,int type,int mode,int look,		int par, boolean drawIt) // Start up a new live object	{		objX[objCount]=x;		objY[objCount]=y;		objLook[objCount]=look;		objMode[objCount]=mode;		objDraw[objCount]=drawIt;		objType[objCount]=type;		objPar[objCount]=par;		objCount++;	}	public void smallUpdates() // Make requested small modifications of landscape	{		int i;		if (fixQueueBrick[0]>0)		{			landscapeG.drawImage(bricks[brickIm[fixQueueBrick[0]]],fixQueueX[0],				fixQueueY[0],this);			fixQueueBrick[0]=0;			i=1;			while (fixQueueBrick[i]>0)			{				fixQueueX[i-1]=fixQueueX[i];				fixQueueY[i-1]=fixQueueY[i];				fixQueueBrick[i-1]=fixQueueBrick[i];				fixQueueBrick[i]=0;				i++;			}		}		else if (newScore>score)		{			score=newScore;			scoreDG.drawImage(clearDisp,0,0,this);			scoreDG.drawString(String.valueOf(score),2,11);			drawScore=true;		}	}	public void blastBrick(int pos) // Draw destroyed brick (or rather place in queue)	{		int i,j;		for (i=0;i<blastable;i++)			if (blastBrick[i]==brickMap[pos])			{				newScore+=brickPoints[i];				j=0;				while (fixQueueBrick[j]>0)					j++;				fixQueueBrick[j]=blastBrick[i]+1;				brickMap[pos]=blastBrick[i]+1;				fixQueueX[j]=32*(pos % 9);				fixQueueY[j]=32*(pos/9-1);				if (pos<90) // We need one update for bottom half too				{					brickMap[pos+90]=blastBrick[i]+1;					fixQueueBrick[j+1]=blastBrick[i]+1;					fixQueueX[j+1]=fixQueueX[j];					fixQueueY[j+1]=fixQueueY[j]+320;				}				i=blastable;			}	}	public void placeBricks() // Choose bricks to distribute randomly onto modStrip, then enemies	{		int i,j,k,l;		double d;		i=(int)(m.random()*(1+bricksPerLine[level]));		for (j=0;j<9;j++)			brickArea[j]=0;		for (j=0;j<i;j++)		{			k=(int)(m.random()*9);			while (brickArea[k]>0)				k=(int)(m.random()*9);			d=m.random();			l=0;			while (d>randBrick[maxOtherBricks*level+l])				l++;			brickArea[k]=levBrick[level*maxOtherBricks+l];		}		j=0;		for (i=0;i<9;i++)			if (brickArea[i]>0)			{				brickPos[j]=i;				brickType[j]=brickArea[i];				j++;			}		i=(int)(m.random()*9); // And now for the enemies		if ((brickQual[brickMap[9*((32+windowHeight-24)/32)+i]]==0)&&(objCount<5)&&(m.random()<0.3))		{			d=m.random();			l=0;			while (d>randEnemy[maxEnemies*level+l])				l++;			activateObject(i*32,-24,levEnemy[level*maxEnemies+l],0,0,0,true);		}	}	public void initiatePlaying() // Prepares for start of new game or level	{		windowHeight=0;		windowStep=4;		paintBackground(levBackgr[level]);		for (i=0;i<9;i++)			brickArea[i]=0;		for (i=0;i<22*9;i++)			brickMap[i]=0;		for (i=0;i<5;i++)			brickPos[i]=-1;		for (i=0;i<maxObj;i++)			objType[i]=0;		objCount=0;		activateObject(128,240,1,0,0,0,true);		scoreDG.drawImage(clearDisp,0,0,this);		scoreDG.drawString(String.valueOf(score),2,11);		bulletOn=false;	}	public void loadBalance(int cyclic) // Do timed parts of the graphics updating	{		switch (cyclic)		{			case 0:				landscapeG.drawImage(modStrip,0,32*((windowHeight/32)-1),this);				for (i=0;i<9;i++)					brickMap[9*(windowHeight/32)+i]=brickArea[i];				break;			case 1:				landscapeG.drawImage(modStrip,0,32*((windowHeight/32)+9),this);				for (i=0;i<9;i++)					brickMap[90+9*(windowHeight/32)+i]=brickArea[i];				break;			case 6:				for (i=0;i<5;i++)	// Clear places for bricks					brickPos[i]=-1;				if (mileage<levLength[level])					placeBricks();				else					for (i=0;i<9;i++)						brickArea[i]=0;				break;			case 7:				modSG.drawImage(defaultStrip,0,0,this);				break;			default:				break;		}		if ((cyclic>1)&&(cyclic<7))			if (brickPos[6-cyclic]>=0)					modSG.drawImage(bricks[brickIm[brickType[6-cyclic]]],						brickPos[6-cyclic]*32,0,this);				else					smallUpdates();	}	public void sortObjects() // Called after deleting an object	{		int lastEmpty,lastExisting,j;		lastEmpty=-1;		lastExisting=-1;		for (j=0;j<maxObj;j++)		{			if (objType[j]==0)			{				if (lastEmpty<0)					lastEmpty=j;			}			else			{				if (lastEmpty<0)				{					lastExisting=j;				}				else				{					objX[lastEmpty]=objX[j];					objY[lastEmpty]=objY[j];					objType[lastEmpty]=objType[j];					objLook[lastEmpty]=objLook[j];					objMode[lastEmpty]=objMode[j];					objDraw[lastEmpty]=objDraw[j];					objPar[lastEmpty]=objPar[j];					objType[j]=0;					j=lastEmpty;					lastExisting=j;					lastEmpty=-1;				}			}		}		objCount=lastExisting+1;	}	public void run()	{		int i;		while (updateThread !=null)		{			try			{				updateThread.sleep(m.max(startTime-System.currentTimeMillis(),20));			} catch (InterruptedException e) {}			startTime=System.currentTimeMillis()+70;			counter++;			switch (runMode)			{				case 1: // Presentation loop					scrollScreen();					offGraphics.drawImage(logo,24,50,this);					handleObjects();					drawObjects();					if ((currentKey==32)||((currentKey>64)&&(currentKey<70)))					{						if ((currentKey>64)&&(currentKey<70))							level=currentKey-65;						else							level=0;						runMode=3; // Prepare for playing						offGraphics.setColor(Color.black);						counter=-1;					}					break;				case 2: // Main game loop					loadBalance((windowHeight & 28)/4);					scrollScreen();					handleObjects();					drawObjects();					mileage++;					if (mileage>levLength[level]+80) // Prepare to finish and exit level					{						handleSound(2,true);						runMode=4;						tempScore=newScore+500;						objType[0]=6;						windowStep=0;					}					break;				case 3: // Prepare for playing, clear screen slowly					offGraphics.drawImage(clearScreen,0,counter*8,this);					offGraphics.drawImage(clearScreen,0,280-counter*8,this);					if (counter>17)					{						lives=3; // Initial set-up						score=0;						newScore=0;						counter=0;						getLevelGraphics(level);						System.gc();						initiatePlaying();						runMode=2;						showLights=true;						drawScore=true;					}					break;				case 4: // Finish level					scrollScreen();					handleObjects();											if (newScore<tempScore) // Ticking bonus						newScore+=50;					smallUpdates();					drawObjects();					if (objY[0]<=-32)					{						runMode=5;						counter=-1;						level=(level+1)% maxLev;						mileage=0;						handleSound(-1,false);					}					break;				case 5: // Clear up and start again, possibly on new level					offGraphics.drawImage(clearScreen,0,counter*8,this);					offGraphics.drawImage(clearScreen,0,280-counter*8,this);					if (counter>17)					{						if (lives>=0)						{							getLevelGraphics(level);							System.gc();							initiatePlaying();							runMode=2;							showLights=true;							drawScore=true;						}						else // Game over?						{							counter=0;							p=getFontMetrics(fo).stringWidth("GAME OVER");							offGraphics.setColor(Color.white);							offGraphics.drawString("GAME OVER",144-p/2,140);							offGraphics.setColor(Color.black);							runMode=6;						}					}					break;				case 6: // Game over message					if (counter>50)						if (score<=highScores[4])						{							mileage=0;							preparePresentation();						}						else // New highscore						{							landscapeG.setColor(Color.black);							landscapeG.fillRect(0,0,288,510);							landscapeG.setColor(Color.white);							p=getFontMetrics(fo).stringWidth("GAME OVER");							landscapeG.drawString("GAME OVER",144-p/2,140);							highScores[4]=score;							q=4;							for (i=3;i>=0;i--)								if (score>highScores[i])								{									highScores[i+1]=highScores[i];									highScores[i]=score;									q=i;								}							p=getFontMetrics(fo).stringWidth("Current Highscores");							landscapeG.drawString("Current Highscores",144-p/2,308);							for (i=0;i<5;i++)							{								if (i==q)									landscapeG.setColor(Color.yellow);								else									landscapeG.setColor(Color.white);								landscapeG.drawString(""+(i+1)+".",100,340+i*27);								p=getFontMetrics(fo).stringWidth(String.valueOf(									highScores[i]));								landscapeG.drawString(""+highScores[i],188-p,340+i*27);							}							windowHeight=0;							windowStep=-4;							counter=0;							runMode=7;							score=0;						}					break;				case 7: // Scroll in highscore table					scrollScreen();					if (counter>54)					{						counter=0;						runMode=6; // Go back to waiting					}					break;				default:					break;			}			repaint();		}	}	public void start()	{		if (updateThread==null)		{			updateThread=new Thread(this,"Game");			updateThread.start();			startTime=System.currentTimeMillis();		}	}	public void stop()	{		if ((updateThread!=null)&&(updateThread.isAlive()))		{			updateThread.stop();		}		updateThread=null;	}	public boolean keyDown(java.awt.Event e,int key)	{		currentKey=key;		return false;	}		public boolean keyUp(java.awt.Event e,int key)	{		currentKey=0;		return false;	}	public void handleObjects() // Control non-stationary stuff	{		int i,j,k,mapPos;		boolean upFree,downFree,leftFree,rightFree;		for (i=0;i<objCount;i++)		{						switch (objType[i])			{				case 1: // Spaceship					mapPos=checkTouching(i);					objLook[i]=0;					if ((currentKey==106)&&(objX[i]>0)) // Go left					{						objX[i]-=8;						objLook[i]=1;					}					else if ((currentKey==108)&&(objX[i]<256)) // Go right					{						objX[i]+=8;						objLook[i]=2;					}					if ((currentKey==107)&&(!bulletOn)) // Fire					{						activateObject(objX[i]+8,objY[i],2,0,3,0,true);						bulletOn=true;						handleSound(0,false);					}					if (touching[0]+touching[1]+touching[2]+touching[3]>0)						blowUpShip();					break;				case 2: // Friendly bullet					mapPos=checkTouching(i);					bulletIx=i;					if (touching[0]+touching[1]>0) // Smashed into something?					{						if ((touching[0] & 2)>0) // Destructible?						{							objType[i]=5;							objX[i]-=8;							objY[i]-=16;							objPar[i]=8;							objLook[i]=6;							blastBrick(mapPos);							handleSound(1,false);						}						else if ((touching[1] & 2)>0)						{							objType[i]=5;							objX[i]-=8;							objY[i]-=16;							objPar[i]=8;							objLook[i]=6;							blastBrick(mapPos+1);							handleSound(1,false);						}						else // No? Just remove bullet						{							objType[i]=0;							bulletOn=false;						}											}					else // Keep going up					{						objY[i]-=16;						if (objY[i]<0)						{							objType[i]=0;							bulletOn=false;						}					}					break;				case 3: // Dummy ship (used in intro)					if (objX[i]<objPar[i])					{						objX[i]+=8;						objLook[i]=1;					}					else if (objX[i]>objPar[i])					{						objX[i]-=8;						objLook[i]=2;					}					else						objLook[i]=0;					if (m.random()<0.1)						objPar[i]=8*(1+(int)(m.random()*30.49));					if ((objCount<3)&&(m.random()<0.1)) // Dummy fire						activateObject(objX[i]+8,objY[i],4,0,3,0,true);					break;				case 4: // Dummy bullet					objY[i]-=18;					if (objY[i]<0)					{						objType[i]=0;					}					break;				case 5: // Exploding friendly bullet					objLook[i]++;					objY[i]+=windowStep;					if (objLook[i]>objPar[i])					{						objType[i]=0;						bulletOn=false;					}					break;				case 6: // Exiting ship (also rather dummy)					if (objX[i]<128)					{						objX[i]+=8;						objLook[i]=1;					}					else if (objX[i]>128)					{						objX[i]-=8;						objLook[i]=2;					}					else						objLook[i]=0;					objY[i]-=4;					break;				case 7: // Spiked ball type					objY[i]+=windowStep;					mapPos=9*((32+windowHeight+objY[i])/32)+objX[i]/32;					if ((objX[i]&31)+((objY[i]+windowHeight)&31)==0)					{						downFree=(brickQual[brickMap[mapPos+9]]==0);						leftFree=((brickQual[brickMap[mapPos-1]]==0)&&(objX[i]>0));						rightFree=((brickQual[brickMap[mapPos+1]]==0)&&(objX[i]<256));						if (objMode[i]==0)						{							if (downFree)								objMode[i]=2;							else							{								if (leftFree && rightFree)									objMode[i]=(int)(3+2*m.random());								else if (leftFree)									objMode[i]=3;								else if (rightFree)									objMode[i]=4;							}						}						else						{							if (((objMode[i]==2)&&(!downFree))||								((objMode[i]==3)&&(!leftFree))||								((objMode[i]==4)&&(!rightFree)))								objMode[i]=0;						}					}					switch(objMode[i])					{						case 2:							objY[i]+=4;							break;						case 3:							objX[i]-=4;							break;						case 4:							objX[i]+=4;							break;						default:							break;					}					animateObject(i,0);					if (objY[i]>287)	// Vanished off screen					{						objType[i]=0;					}					else						checkForHit(i);					break;				case 8: // Tank type					objY[i]+=windowStep;					mapPos=9*((32+windowHeight+objY[i])/32)+objX[i]/32;					if ((objX[i]&31)==0)					{						leftFree=((brickQual[brickMap[mapPos-1]]==0)&&(objX[i]>0));						rightFree=((brickQual[brickMap[mapPos+1]]==0)&&(objX[i]<256));						if (objMode[i]==0)						{							if (leftFree && rightFree)								objMode[i]=(int)(3+2*m.random());							else if (leftFree)								objMode[i]=3;							else if (rightFree)								objMode[i]=4;						}						else						{							if (((objMode[i]==3)&&(!leftFree))||								((objMode[i]==4)&&(!rightFree)))								objMode[i]=0;						}					}					if ((objCount<5)&&(objX[i]-objX[0]<40)&&(objX[0]-objX[i]<40)&&						(m.random()<0.12)) // Fire!					{						activateObject(objX[i]+8,objY[i]+12,9,0,4,0,true);						animateObject(i,5);						handleSound(0,false);						objPar[i]=3; // Busy shooting for 3 cycles					}					if (objPar[i]==0) // Not busy					{						switch(objMode[i])						{							case 3:								objX[i]-=4;								break;							case 4:								objX[i]+=4;								break;							default:								break;						}						animateObject(i,objMode[i]);					}					else						objPar[i]--;					if (objY[i]>287)	// Vanished off screen					{						objType[i]=0;					}					else						checkForHit(i);					break;				case 9: // Enemy bullet					objY[i]+=20;					if (objPar[i]==0) // Start as nozzle explosion					{						objLook[i]=5;						objPar[i]=1;					}					else						objLook[i]=4;					mapPos=checkTouching(i);					if ((objType[0]==1)&&((objX[i]-objX[0])*(objX[i]-objX[0])+						(objY[i]-objY[0])*(objY[i]-objY[0])<500)) // Hit ship?					{						blowUpShip();						objType[i]=0;					}					if ((touching[0]+touching[1]+touching[2]+touching[3]>0)||						(objY[i]>287)) // Crashed or off screen?					{						objType[i]=0; // Remove bullet							}					break;				case 10: // Exploding enemy					objLook[i]++;					objY[i]+=windowStep; // Done exploding?					if (objLook[i]>objPar[i])						objType[i]=0;					break;				case 11: // Exploding self					objX[i]+=(int)(10*m.cos(objMode[i]));					objY[i]-=(int)(10*m.sin(objMode[i]));					if (objPar[i]<6)						objLook[i]=6+objPar[i]/2;					else						objDraw[i]=false;					objPar[i]++;					if ((objPar[i]>12)&&(runMode==2))					{						objType[i]=0;						counter=-1;						lives--;						mileage=m.max(mileage,0);						runMode=5;						showLights=true;					}					break;				case 12: // Homing					objY[i]+=(windowStep+2);					if ((objX[i]<objX[0])&&(objPar[i]<10))						objPar[i]+=2;					else if ((objX[i]>objX[0])&&(objPar[i]>-10))						objPar[i]-=2;					objX[i]+=objPar[i];					animateObject(i,0);					if (objY[i]>287)	// Vanished off screen						objType[i]=0;					else						checkForHit(i);					break;				case 13: // Dragonfly type					objY[i]+=(windowStep+8);					animateObject(i,0);					if (objY[i]>287)	// Vanished off screen						objType[i]=0;					else						checkForHit(i);					break;				case 14: // Shark type					objY[i]+=windowStep;					mapPos=9*((32+windowHeight+objY[i])/32);					if (objPar[i]==0)					{						objDraw[i]=false;						j=(int)(m.random()*8);						if ((j!=(objX[i]/32))&&(brickQual[brickMap[mapPos+j]]==0))						{							objPar[i]=16; // Emerge from the depth							objX[i]=32*j;							objDraw[i]=true;						}					}					if (objPar[i]>0)					{						objPar[i]--;						if (objPar[i]>0)						{							animateObject(i,objPar[i]-1);							checkForHit(i);						}						else							objDraw[i]=false;					}					if (objY[i]>287)	// Vanished off screen						objType[i]=0;					break;				case 15: // Moving right-left type					objY[i]+=windowStep;					mapPos=9*((32+windowHeight+objY[i])/32)+objX[i]/32;					if ((objX[i]&31)==0)					{						leftFree=((brickQual[brickMap[mapPos-1]]==0)&&(objX[i]>0));						rightFree=((brickQual[brickMap[mapPos+1]]==0)&&(objX[i]<256));						if (objMode[i]==0)						{							if (leftFree && rightFree)								objMode[i]=(int)(3+2*m.random());							else if (leftFree)								objMode[i]=3;							else if (rightFree)								objMode[i]=4;						}						else						{							if (((objMode[i]==3)&&(!leftFree))||								((objMode[i]==4)&&(!rightFree)))								objMode[i]=0;						}					}					switch(objMode[i])					{						case 3:							objX[i]-=4;							break;						case 4:							objX[i]+=4;							break;						default:							break;					}					animateObject(i,objMode[i]);					if (objY[i]>287)	// Vanished off screen					{						objType[i]=0;					}					else						checkForHit(i);					break;				case 16: // Saucer type					objY[i]+=(windowStep+6);					animateObject(i,0);					if ((objCount<5)&&(m.random()<0.08)) // Fire!					{						activateObject(objX[i]+8,objY[i]+8,17,0,88,8*(1-(counter&2)),							true);						handleSound(0,false);					}					if (objY[i]>287)	// Vanished off screen						objType[i]=0;					else						checkForHit(i);					break;				case 17: // Enemy fireball					objY[i]+=(windowStep+5);					animateObject(i,0);					if ((objX[i]<(objX[0]+12))&&(objPar[i]<10))						objPar[i]+=2;					else if ((objX[i]>objX[0])&&(objPar[i]>-10))						objPar[i]-=2;					objX[i]+=objPar[i];					if (objY[i]>287)	// Vanished off screen						objType[i]=0;					else if ((objType[0]==1)&&((objX[i]-objX[0])*(objX[i]-objX[0])+						(objY[i]-objY[0])*(objY[i]-objY[0])<500))					{						blowUpShip(); // Fireball collided with spaceship						objType[i]=0;					}					break;										default:					break;			}		}		sortObjects();	}	public void animateObject(int i, int action) // Give the enemy the right look	{		switch(objType[i])		{			case 7:				switch(level)				{					case 0: // Spiked ball						objLook[i]=13+(counter%3);						break;					case 3: // Spinning cube						objLook[i]=40+(counter%3);						break;					case 5: // Beach ball						objLook[i]=57+(counter&3);						break;					case 6: // Snowman						objLook[i]=70+(counter&3);						break;					case 7: // Killer tomato						objLook[i]=74+cyclic2[counter%6];						break;					default:						break;				}				break;			case 8:				switch(level)				{					case 0: // Tank						objLook[i]=16+((objX[i]/4)%3);						break;					case 1: // Bazooka bird						if (action==3) // Left							objLook[i]=22+cyclic[(objX[i]/4)%3];						else if (action==4) // Right							objLook[i]=25+cyclic[(objX[i]/4)%3];						else // Fire							objLook[i]=28;						break;					case 4: // Eyeball						if (action==3) // Left							objLook[i]=44;						else if (action==4) // Right							objLook[i]=45;						else // Fire							objLook[i]=43;						break;					case 6: // Fire						objLook[i]=67+(counter%3);						break;					case 8: // Rolling cannon						objLook[i]=82+((objX[i]/4)%3);						break;					default:						break;				}				break;			case 12:				switch(level)				{					case 1: // Homing bird						objLook[i]=19+(counter%3);						break;					default:						break;				}				break;			case 13:				switch(level)				{					case 2: // Dragonfly						objLook[i]=29+(counter&1);						break;					case 5: // Seagull						objLook[i]=56;						break;					default:						break;				}				break;			case 14:				switch(level)				{					case 2: // Shark						objLook[i]=shark[action];						break;					case 4: // Groundmouth						objLook[i]=groundMouth[action];						break;					default:						break;				}				break;			case 15:				switch(level)				{					case 3: // Rolling cube						objLook[i]=36+((objX[i]/4)&3);						break;					case 5: // Crab						objLook[i]=53+cyclic[(objX[i]/4)&3];						break;					case 6: // Penguin						if (action==3)							objLook[i]=61+cyclic[counter&3];						else							objLook[i]=64+cyclic[counter&3];						break;					case 7: // Vegetable						objLook[i]=78+((objX[i]/4)&3);						break;					default:						break;				}				break;			case 16: // Saucer				objLook[i]=85+(counter%3);				break;			case 17: // Fireball				objLook[i]=88+(counter&1);				break;			default:				break;		}	}	public int checkTouching(int i) // Used by handleObjects()	{		int mapPos;		touching[1]=0;		touching[2]=0;		touching[3]=0;		mapPos=9*((32+windowHeight+objY[i])/32)+objX[i]/32; // Check what's under it		touching[0]=brickQual[brickMap[mapPos]]&3;		if (((objX[i]&31)+objImPractW[objLook[i]])>32)		{			touching[1]=brickQual[brickMap[mapPos+1]]&3;			if ((((objY[i]+windowHeight)&31)+objImH[objLook[i]])>32)				touching[3]=brickQual[brickMap[mapPos+10]]&3;		}		if ((((objY[i]+windowHeight)&31)+objImH[objLook[i]])>32)			touching[2]=brickQual[brickMap[mapPos+9]]&3;		return mapPos;	}	public void checkForHit(int i) // Used by handleObjects().	{		int j;		if ((objType[bulletIx]==2)&&((objX[i]-objX[bulletIx])*			(objX[i]-objX[bulletIx])+(objY[i]-objY[bulletIx])*			(objY[i]-objY[bulletIx])<570))		{			for (j=0;j<maxEnemies;j++) // Enemy hit by bullet?				if (objType[i]==levEnemy[j+maxEnemies*level])					newScore+=enemyPoints[j+maxEnemies*level];			objType[bulletIx]=0;			bulletOn=false;			objType[i]=10;			objPar[i]=12;			objLook[i]=9;			handleSound(1,false);		}		else if ((objType[0]==1)&&((objX[i]-objX[0])*(objX[i]-objX[0])+			(objY[i]-objY[0])*(objY[i]-objY[0])<1000))		{			blowUpShip(); // Enemy collided with spaceship			objType[i]=10;			objPar[i]=12;			objLook[i]=9;		}	}	public void blowUpShip()	{		objType[0]=0;		if (bulletOn)			objType[bulletIx]=0;		activateObject(objX[0],objY[0],11,(int)(m.random()*180),6,0,true);		activateObject(objX[0],objY[0],11,(int)(m.random()*180),6,0,true);		activateObject(objX[0],objY[0],11,(int)(m.random()*180),6,0,true);		mileage-=96;		handleSound(1,false);	}	public void paint(Graphics g) // Draw the control panel and stuff	{		g.drawImage(panel,0,304,this);		g.drawImage(outline[3],0,0,this);		g.drawImage(outline[4],304,0,this);		for (i=0;i<9;i++)			g.drawImage(outline[2],16+i*32,0,this);		for (i=0;i<9;i++)		{			g.drawImage(outline[0],0,16+i*32,this);			if (i==3)				g.drawImage(outline[1],304,16+i*32,this);			else				g.drawImage(outline[0],304,16+i*32,this);		}		showLights=true;		drawScore=true;		update(g);			}	public void update(Graphics g)	{		g.drawImage(offImage,16,16,this);		if (showLights) // Draw lives & level indicators		{			for (q=0;q<9;q++)			{				if (q<level)					g.setColor(Color.white);				else if (q==level)					g.setColor(Color.yellow);				else					g.setColor(Color.red);				g.fillRect(20+11*q,326,5,10);			}			for (q=0;q<4;q++)			{				if (q<lives)					g.setColor(Color.white);				else if (q==lives)					g.setColor(Color.yellow);				else					g.setColor(Color.red);				g.fillOval(143+15*q,342,8,8);			}			showLights=false;		}		if (drawScore)		{			g.drawImage(scoreDisp,214,321,this);			drawScore=false;		}	}}