// "Dungeon Dregs" Java applet game engine // By Karl Hornell, July 1997 import java.awt.*; import java.awt.image.*; import java.net.*; public final class dungeon extends java.applet.Applet implements Runnable { // Constants int numBlocks=21,numMobile=55; int blockSize=32,screenX=14,screenY=10,topMargin=15; int totalXScreens=2,totalYScreens=1; int exitX=2,exitY=0,playerStartX=1,playerStartY=8,playerStartLook=3; int timeLimit=5000,grabDistance=25,killDistance=15; long loopInterval=85; int quality[]={0,3,3,4,4,4,5,0, 0,0,1,0,0,0,0,0, 0,0,0,0,2}; int playerAnim[]={0,1,2,1, 3,4,5,4, 6,7,8,7, 9,10,11,10}; int prisAnim[]={12,13,14,13, 15,16,17,16, 18,19,20,19, 21,22,23,22}; int guardAnim[]={24,25,26,25, 27,28,29,28, 30,31,32,31, 33,34,35,34}; int ball[]={36,37,39,38}; int playerHitting[]={40,41,42,43}; int guardEmerging[]={44,45,46}; int prisCheer[]={47,19},cheerTime=7; int prisStunned=48,recoverTime=25; int removeChained=0,removeUnchained=0; int breakable[]={3,4,5},breakAnimate[]={11,12,13, 14,15,16, 17,18,19}; int grapple[]={49,50},grappleTime=50; int guardDeath[]={51,51,51}; int playerDeath[]={52,53,54}; int controlKeys[]={Event.LEFT,Event.RIGHT,Event.DOWN,Event.UP,' '}; // Left, right, down, up, smash int playerSpeed=6,guardSpeed=6,ballSpeed=6,prisFast=5,prisSlow=3; // Normal variables int totalPris,prisDead,prisSaved,staircases[][]; int blockXPos[],blockYPos[]; int di,dj,dk,dl,aj,i,j,k,l,m,n; int map[][],nav[][]; int navData[]={500,500,500,-1,-1,-1}; int navBuf[][]=new int[128][2],navBufStart,navBufEnd,ni,nj,nk; int Otype[]=new int[100],Oxpos[]=new int[100],Oypos[]=new int[100]; int Ostate[]=new int[100],Ocounter[]=new int[100],Olook[]=new int[100]; int Oextra[]=new int[100],OblockX[]=new int[100],OblockY[]=new int[100]; int lastObj,offsetXPix,offsetYPix,limitXPix,limitYPix; int currRoomX,currRoomY,guardCounter[][],guardLoop,ballNumber; boolean brokenFree; int cycloX[]={-1,1,0,0},cycloY[]={0,0,1,-1},cycloDir[]={1,2,3,4}; int swing[]={20,0,20,20, 0,20,20,20, 20,20,20,0, 20,20,0,20}; int guardList[]=new int[8],guards,peopleCounter,roomLoop; int surpX[]={0,-1,1,0,0},surpY[]={0,0,0,1,-1},surpX1[]={0,1,0,0,0},surpY1[]={0,0,0,0,1}; int smashX,smashY,smashCount,smashEnd; int timer,lastTimer,prisonersSaved,lastSaved,seed=1; long nextTime; Thread game; Image collection,extra1,extra2; MediaTracker mt; // Important variables and objects int gameState,pressedKey; boolean refreshScreen; Image farBuffer,nearBuffer,mobiles[]; Graphics farBG,nearBG; public void init() { loadGraphics(); System.gc(); loadSounds(); loadMap(); refreshScreen=true; gameState=9; // Draw intro screen as soon as the main thread starts } public void resetGame() { resetMap(); processMap(); lastObj=1; for (i=1;i<100;i++) Otype[i]=0; Otype[0]=1; // Set player data Oxpos[0]=playerStartX*blockSize; Oypos[0]=playerStartY*blockSize; Ostate[0]=0; Olook[0]=playerStartLook; smashEnd=-1; timer=timeLimit; lastTimer=0; currRoomX=playerStartX/(screenX-1); currRoomY=playerStartY/(screenY-1); prisonersSaved=0; lastSaved=0; ballNumber=-1; brokenFree=false; guardCounter=new int[totalYScreens][totalXScreens]; guards=0; guardLoop=0; for (i=0;i<8;i++) guardList[i]=0; // Timer and saved prisoner counter nearBG.setColor(Color.white); nearBG.drawString("Time:",2,12); nearBG.drawString("Prisoners saved:",310,12); nearBG.drawRect(45,3,250,9); nearBG.setColor(Color.yellow); nearBG.fillRect(47,5,247,6); nearBG.setColor(Color.red); nearBG.fillRect(47,5,50,6); } public void loadMap() // Obtain a map of the dungeon, presumably from an external source { } public void resetMap() // Fill up the array "map" with data { int i,j; int room1[]={1,2,2,1,2,2,2,2,1,2,2,2,2,1, 1,9,20,1,20,20,0,0,3,0,0,8,0,1, 1,20,0,1,10,0,0,9,1,0,0,0,6,1, 1,0,20,1,2,2,2,2,2,0,5,0,0,1, 1,4,2,2,8,0,0,0,0,0,5,0,0,1, 1,0,0,0,0,0,1,2,4,1,5,0,0,1, 1,2,2,2,1,0,1,10,0,1,5,5,0,2, 1,0,9,0,1,0,2,2,2,2,0,0,0,0, 1,0,0,0,3,0,0,8,0,0,0,6,9,1, 2,2,2,2,2,2,2,2,2,2,2,2,2,2}; int room2[]={1,2,2,2,2,2,2,1,2,1,2,2,2,1, 1,9,9,20,20,0,20,1,0,3,0,20,20,1, 1,0,10,0,0,0,0,1,0,2,2,2,2,2, 1,2,2,2,4,2,2,2,0,0,8,0,7,0, 1,8,0,0,0,0,5,5,0,0,0,0,0,1, 1,0,5,5,0,0,5,0,0,0,0,0,6,1, 1,0,0,0,0,5,5,0,1,2,1,0,0,1, 0,0,0,0,0,9,5,0,1,10,1,0,0,1, 1,9,0,6,0,0,0,0,1,8,3,0,0,1, 2,2,2,2,2,2,2,2,2,2,2,2,2,2}; map=new int[(screenX-1)*totalXScreens+1][(screenX-1)*totalXScreens+1]; for (i=0;ik) { nav[baseY+screenY-2][baseX+j]=1; push(baseY+screenY-2,baseX+j); } } for (i=0;il) { nav[baseY+i][baseX+screenX-2]=1; push(baseY+i,baseX+screenX-2); } } staircases[k*totalXScreens+l]=new int[stairCount]; for (i=0;inav[ni][nj]+1) { nav[ni-1][nj]=nav[ni][nj]+1; push(ni-1,nj); } if (nav[ni+1][nj]>nav[ni][nj]+1) { nav[ni+1][nj]=nav[ni][nj]+1; push(ni+1,nj); } if (nav[ni][nj-1]>nav[ni][nj]+1) { nav[ni][nj-1]=nav[ni][nj]+1; push(ni,nj-1); } if (nav[ni][nj+1]>nav[ni][nj]+1) { nav[ni][nj+1]=nav[ni][nj]+1; push(ni,nj+1); } } } public void drawGameScreen(int roomY, int roomX) { dk=(screenY-1)*roomY; dl=(screenX-1)*roomX; for (di=0;di=lastObj) lastObj=aj+1; Otype[aj]=t; Olook[aj]=l; Oxpos[aj]=x; Oypos[aj]=y; Ostate[aj]=s; Ocounter[aj]=c; Oextra[aj]=e; } public void removeObject(int num) { Otype[num]=0; while (Otype[lastObj-1]==0) lastObj--; } public void run() { int i,j,k,l,m,n,rX,rY,counter=0; while (game!=null) { try { game.sleep(Math.max(25,nextTime-System.currentTimeMillis())); } catch (InterruptedException e) {} nextTime=System.currentTimeMillis()+loopInterval; switch(gameState) { case 0: // Prepare for new game nearBG.setColor(Color.black); nearBG.fillRect(0,0,blockSize*screenX,topMargin+blockSize*screenY); resetGame(); drawGameScreen(currRoomY,currRoomX); gameState=1; refreshScreen=true; break; case 1: // Main game loop roomLoop=(roomLoop+1)%(totalXScreens*totalYScreens); rX=roomLoop%totalXScreens; rY=roomLoop/totalXScreens; peopleCounter=0; i=cycloX[0];j=cycloY[0];k=cycloDir[0]; for (l=0;l<3;l++) { cycloX[l]=cycloX[l+1]; cycloY[l]=cycloY[l+1]; cycloDir[l]=cycloDir[l+1]; } cycloX[3]=i;cycloY[3]=j;cycloDir[3]=k; for (i=0;i1) Ostate[i]=0; Oxpos[i]+=surpX[Ostate[i]]*k; Oypos[i]+=surpY[Ostate[i]]*k; } else // Change direction 180 degrees? { if (pressedKey==controlKeys[ ((Ostate[i]-1)&3)+(Ostate[i]&1)]) Ostate[i]=1+((Ostate[i]-1)&3)+(Ostate[i]&1); } } if ((pressedKey==controlKeys[4])&&(Ostate[i]<5)) { j=Ostate[i]; if (j==0) swingBall(); else { k=(blockSize-surpX[j]*(Oxpos[i]%blockSize)- surpY[j]*(Oypos[i]%blockSize))%blockSize; m=(Oypos[i]+surpY[j]*k)/blockSize+surpY[j]; n=(Oxpos[i]+surpX[j]*k)/blockSize+surpX[j]; if ((m>=0)&&(m=0)&&(n0)) { Olook[i]=playerAnim[((Ostate[i]-1)<<2)+(counter&3)]; } if ((Oxpos[i]/blockSize/(screenX-1)==rX)&& (Oypos[i]/blockSize/(screenY-1)==rY)) peopleCounter++; if (Otype[guardList[guardLoop]]==5) { j=guardList[guardLoop]; if ((Oxpos[j]-Oxpos[i]<100)&& (Oxpos[i]-Oxpos[j]<100)&& (Oypos[i]-Oypos[j]<100)&& (Oypos[j]-Oypos[i]<100)) if (nav[Oypos[i]/blockSize][Oxpos[i]/blockSize]< nav[Oypos[j]/blockSize][Oxpos[j]/blockSize]) Oextra[j]=0; else Oextra[j]=1; } for (k=0;k<8;k++) // Check if caught by guard if (Otype[guardList[k]]==5) { j=guardList[k]; if ((Oxpos[j]-Oxpos[i]0) { removeObject(ballNumber); ballNumber=-1; } gameState=6; counter=-1; } } break; case 2: // Ball & chain if (Ostate[i]<5) // Going forward { ballNumber=i; Oxpos[i]+=ballSpeed*surpX[Ostate[i]]; Oypos[i]+=ballSpeed*surpY[Ostate[i]]; Ocounter[i]++; if ((((Oxpos[i]-Oxpos[0])%blockSize)==0)&& (((Oypos[i]-Oypos[0])%blockSize)==0)) Ostate[i]+=4; } else // Retracting { ballNumber=-1; Oxpos[i]-=ballSpeed*surpX[Ostate[i]-4]; Oypos[i]-=ballSpeed*surpY[Ostate[i]-4]; Ocounter[i]--; if (Ocounter[i]==0) // All done { Ostate[0]=Oextra[0]; Olook[0]=playerAnim[(Ostate[i]-5)<<2]; removeObject(i); } } break; case 3: // Prisoner case 4: // Slow prisoner if (Ostate[i]<5) { j=Ostate[i]; if (Otype[i]==3) l=prisFast; else l=prisSlow; Oxpos[i]+=l*surpX[j]; Oypos[i]+=l*surpY[j]; k=(surpX[j]*(Oxpos[i]%blockSize)+ surpY[j]*(Oypos[i]%blockSize)+blockSize)%blockSize; if (k0)) Olook[i]=prisAnim[((Ostate[i]-1)<<2)+(counter&3)]; } else if (Ostate[i]==5) // Cheering { Olook[i]=prisCheer[Ocounter[i]%prisCheer.length]; Ocounter[i]--; if (Ocounter[i]<0) Ostate[i]=0; } else if (Ostate[i]<10) // Exiting dungeon { j=Ostate[i]-5; Oxpos[i]+=prisFast*surpX[j]; Oypos[i]+=prisFast*surpY[j]; Olook[i]=prisAnim[((j-1)<<2)+(counter&3)]; Ocounter[i]--; if (Ocounter[i]==0) { removeObject(i); prisonersSaved++; } } if ((OblockX[i]/(screenX-1)==rX)&& (OblockY[i]/(screenY-1)==rY)) peopleCounter++; for (k=0;k<8;k++) // Check if caught if (Otype[guardList[k]]==5) { j=guardList[k]; if ((Oxpos[j]-Oxpos[i]>1; Oypos[j]=(Oypos[i]+Oypos[j])>>1; if (Ostate[i]<5) Ostate[j]=Ostate[i]; Otype[j]=8; Ocounter[i]=0; Ocounter[j]=0; Oextra[j]=i; k=0; while (guardList[k]!=j) k++; guardList[k]=0; k=8; } else { Otype[i]=9; Olook[i]=prisStunned; Ocounter[i]=0; } } } break; case 5: // Guard; if (Ostate[i]<5) { j=Ostate[i]; Oxpos[i]+=guardSpeed*surpX[j]; Oypos[i]+=guardSpeed*surpY[j]; k=(surpX[j]*(Oxpos[i]%blockSize)+ surpY[j]*(Oypos[i]%blockSize)+blockSize)%blockSize; if (k0)) Olook[i]=guardAnim[((Ostate[i]-1)<<2)+(counter&3)]; } else if (Ostate[i]==5) // Getting out of basement { Olook[i]=guardEmerging[Ocounter[i]]; Ocounter[i]++; if (Ocounter[i]>=guardEmerging.length) Ostate[i]=0; } if (ballNumber>=0) // Check if hit by ball { j=((Ostate[ballNumber]-1)&3)<<2; if ((Oxpos[i]-Oxpos[ballNumber]=guardDeath.length) removeObject(i); else Olook[i]=guardDeath[Ocounter[i]]; break; case 7: // Prisoner getting beaten up Ocounter[i]++; if (Ocounter[i]=0) // Check if hit by ball { j=((Ostate[ballNumber]-1)&3)<<2; if ((Oxpos[i]-Oxpos[ballNumber]30) Otype[Oextra[i]]=Oextra[Oextra[i]]; else Otype[Oextra[i]]=4; } } } else { j=0; while (guardList[j]!=0) j++; guardList[j]=i; Otype[i]=5; } break; case 9: // Prisoner lying down Ocounter[i]++; if (Ocounter[i]>=recoverTime) { Otype[i]=4; } break; default: break; } offsetXPix=(screenX-1)*blockSize*currRoomX; offsetYPix=(screenY-1)*blockSize*currRoomY- topMargin; limitXPix=offsetXPix+screenX*blockSize; limitYPix=offsetYPix+screenY*blockSize; if (smashEnd>0) { drawBlock(breakAnimate[smashCount++],smashX-offsetXPix, smashY-offsetYPix-topMargin); if (smashCount==smashEnd) smashEnd=-1; } nearBG.drawImage(farBuffer,0,topMargin,this); for (i=lastObj-1;i>=0;i--) if ((Otype[i]>0)&& (Oxpos[i]>offsetXPix-blockSize)&& (Oypos[i]>offsetYPix-blockSize)&& (Oxpos[i]0)&& (guards<8)&&((rand()&62)==0)) { guardCounter[rY][rX]++; guards++; i=staircases[rY*totalXScreens+rX].length; j=(rand()%i)&254; addObject(5,0,staircases[rY*totalXScreens+rX][j+1]*blockSize, staircases[rY*totalXScreens+rX][j]*blockSize,5,0,1); i=0; while (guardList[i]!=0) i++; guardList[i]=aj; } else if ((guardCounter[rY][rX]>0)&&((peopleCounter&127)==0)) { for (i=0;i<8;i++) { j=guardList[i]; if ((Otype[j]==5)&&(Oxpos[j]/blockSize/(screenX-1)==rX)&& (Oxpos[j]/blockSize/(screenY-1)==rY)) { removeObject(j); guards--; guardList[j]=0; guardCounter[rY][rX]--; } } } } if (prisonersSaved!=lastSaved) // Another prisoner saved { nearBG.setColor(Color.black); nearBG.fillRect(415,1,30,13); nearBG.setColor(Color.white); nearBG.drawString(""+prisonersSaved,415,12); lastSaved=prisonersSaved; } guardLoop=(guardLoop+1)&7; if (timer==0) // Have we run out of time? gameState=2; break; case 2: // Run out of time nearBG.setColor(Color.black); nearBG.fillRect(0,topMargin,screenX*blockSize,screenY*blockSize); nearBG.setColor(Color.white); nearBG.drawString("Time is up",180,100); gameState=3; break; case 3: // Screen after running out of time break; case 4: // Escaped from prison nearBG.setColor(Color.black); nearBG.fillRect(0,topMargin,screenX*blockSize,screenY*blockSize); nearBG.setColor(Color.white); nearBG.drawString("You have escaped",175,100); nearBG.drawString("Press SPACE to start over",160,150); gameState=5; break; case 5: // Screen after escaping if (pressedKey==32) gameState=0; break; case 6: // Player death scene nearBG.drawImage(farBuffer,0,topMargin,this); if (counter25) gameState=7; break; case 7: // After death, clear screen nearBG.setColor(Color.black); nearBG.fillRect(0,topMargin,screenX*blockSize,screenY*blockSize); nearBG.setColor(Color.white); nearBG.drawString("You have perished",175,100); nearBG.drawString("Press SPACE to start over",160,150); gameState=8; break; case 8: // Screen after getting killed if (pressedKey==' ') gameState=0; break; case 9: // Initialize presentation nearBG.setColor(Color.black); nearBG.fillRect(0,0,screenX*blockSize,screenY*blockSize+topMargin); nearBG.setColor(Color.white); nearBG.drawString("Last modified July 18, 00:55 CET",140,75); nearBG.drawString("Press SPACE to start",160,100); gameState=10; break; case 10: // Presentation screen -- wait for SPACE if (pressedKey==32) gameState=0; break; default: break; } counter=(counter+1)&255; if (refreshScreen) repaint(); } } public void swingBall() // Activate ball and chain { k=-1; for (l=0;l<16;l++) if (Olook[0]==playerAnim[l]) k=l; if (k>=0) { k=(k+4)>>2; m=Oypos[0]/blockSize+surpY[k]; n=Oxpos[0]/blockSize+surpX[k]; l=quality[map[m][n]]; if ((l==0)||(l==4)) { Oextra[0]=0; Ostate[0]=6; Olook[0]=playerHitting[k-1]; addObject(2,ball[k-1],Oxpos[0]+surpX[k]*(blockSize%ballSpeed), Oypos[0]+surpY[k]*(blockSize%ballSpeed),k,0,0); if (l==4) // Break something { brokenFree=true; j=map[m][n]; l=0; while (j!=breakable[l]) l++; smashCount=3*l; smashEnd=smashCount+3; map[m][n]=breakAnimate[smashCount+2]; smashX=Oxpos[0]+blockSize*surpX[k]; smashY=Oypos[0]+blockSize*surpY[k]; l=500; if ((nav[m-1][n]=0)) l=nav[m-1][n]; if ((nav[m+1][n]=0)) l=nav[m+1][n]; if ((nav[m][n-1]=0)) l=nav[m][n-1]; if ((nav[m][n+1]=0)) l=nav[m][n+1]; nav[m][n]=l+1; push(m,n); } } } } public void start() { if (game==null) { game=new Thread(this); game.start(); nextTime=System.currentTimeMillis()+loopInterval; } } public void stop() { if ((game!=null)&&(game.isAlive())) { game.stop(); } game=null; } public int rand() { seed=(seed*171)%30269; return seed; } public boolean keyDown(java.awt.Event evt, int key) { pressedKey=key; return true; } public boolean keyUp(java.awt.Event evt, int key) { pressedKey=0; return true; } public void paint(Graphics g) { g.drawImage(nearBuffer,0,0,this); } public void update(Graphics g) { paint(g); } }