/* VERSION INFO: 000 first public release Jan 12 2004 001 fix: drops only counted as +1 level, regardless of the number of lines killed */ /** mac compile: gcc -framework OpenGL -framework GLUT -framework Foundation -o FallingUp fallingup.c */ /* OS X */ #include #include #include /**/ /* WINDOWS #include #include #include #include */ /** drawBlock "borrowed" from "gluttris" by manny najera [ mnajera@banarnar.com ] http://www.banarnar.com/gluttris */ /** customizable 'dedication' or 'about' screen? customizable colors customizable shapes and number of shapes? splash screen? top 10? (resettable, save where/how?) tetris sounds: block tick, block drop, line clear (2,3,4?), board "lock" music??? two modes of play? how to switch/choose? menu option, changes at 'new game' status bar? standard glut thing, or not? maybe I do need to go SDL? stages (levels?): 1 (0 lines) -- normal normal 2 (1 line) -- swings upside-down 3 (8 lines) -- swings left-right 4 (15 lines)-- slow spin left-right 5 (23 lines)-- slow spin top-bottom 6 (31 lines)-- slow random spin [[ how to fade?? ]] 7 (39 lines)-- slow random spin based on key presses [[ how to fade?? ]] 8 (47 lines)-- medium * */ #include #include #include #define true 1 #define false 0 #define INFO 0 #define RUNNING 1 #define PAUSED 2 #define OVER 3 #define HIGHSCORE 4 #define PIECES 7 #define TOPN 10 #define XOFF -6.0 #define YOFF -9.0 #define ZOFF -16.0 #define MAXFLAME 120.0f #define ROT_NONE 0 #define ROT_BOUNCE 1 #define ROT_CONTINUE 2 #define ROT_STOP 3 #define ROT_ONCE 4 /* GL/GLUT stuff... */ int mainWindowId; int font = (int)GLUT_BITMAP_9_BY_15; GLuint texture; int replacescore=-1, replacechar=0; const short CHANGEY=0, CHANGEX=1, CHANGEROT=2; const int BASE_SPEED=1200; const short PC_EL=0, PC_ELB=1, PC_S=2, PC_Z=3, PC_BLOCK=4, PC_BAR=5, PC_TEE=6; //PC:Piece# const short data[][4][16]= { { { 0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1 } ,{ 0,0,0,0,0,0,1,0,0,0,1,0,0,1,1,0 } ,{ 0,0,0,0,0,1,0,0,0,1,1,1,0,0,0,0 } ,{ 0,0,0,0,0,0,1,1,0,0,1,0,0,0,1,0 } } , //PC_EL { { 0,0,0,0,0,0,0,0,0,1,1,1,0,1,0,0 } ,{ 0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,1 } ,{ 0,0,0,0,0,0,0,1,0,1,1,1,0,0,0,0 } ,{ 0,0,0,0,0,1,1,0,0,0,1,0,0,0,1,0 } } , //PC_ELB { { 0,0,0,0,0,0,0,0,0,0,1,1,0,1,1,0 } ,{ 0,0,0,0,0,1,0,0,0,1,1,0,0,0,1,0 } ,{ 0,0,0,0,0,0,0,0,0,0,1,1,0,1,1,0 } ,{ 0,0,0,0,0,1,0,0,0,1,1,0,0,0,1,0 } } , //PC_S { { 0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,1 } ,{ 0,0,0,0,0,0,1,0,0,1,1,0,0,1,0,0 } ,{ 0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,1 } ,{ 0,0,0,0,0,0,1,0,0,1,1,0,0,1,0,0 } } , //PC_Z { { 0,0,0,0,0,0,1,1,0,0,1,1,0,0,0,0 } ,{ 0,0,0,0,0,0,1,1,0,0,1,1,0,0,0,0 } ,{ 0,0,0,0,0,0,1,1,0,0,1,1,0,0,0,0 } ,{ 0,0,0,0,0,0,1,1,0,0,1,1,0,0,0,0 } } , //PC_BLOCK { { 0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0 } ,{ 0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0 } ,{ 0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0 } ,{ 0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0 } } , //PC_BAR { { 0,0,0,0,0,0,0,0,0,1,1,1,0,0,1,0 } ,{ 0,0,0,0,0,0,1,0,0,0,1,1,0,0,1,0 } ,{ 0,0,0,0,0,0,1,0,0,1,1,1,0,0,0,0 } ,{ 0,0,0,0,0,0,1,0,0,1,1,0,0,0,1,0 } } //PC_TEE }; float colors[][3] = { { 0.2,0.2,0.6 } , { 0.6,0.2,0.2 } , { 0.4,0.2,0.4 } , { 0.2,0.6,0.2 } , { 0.8,0.2,0.1 } , { 0.8,0.8,0.8 } , { 0.2,0.2,0.2 } }; const int BLOCKTIMER=0; const int SCALE=128,SCALEWIDTH=256,SCALEHEIGHT=256; int HEIGHT=600, WIDTH=425, BLOCKW=200, BLOCKH=400; char PROGRAM_NAME[80] = "Falling Up"; char title[80], scoreString[80], lineString[80], levelString[80]; int MAXLEVEL_NAMES=8; char *levels[8] = { "Deceptive beginnings...", //0 "Flipped!", //1 "Flip^2", //2 "A hint of things to come", //3 "Double gasp", //4 "You think you know terror?", //5 "Lightly whirled peas", //6 "eight" //7 }; char *topnames[TOPN]; int topscores[TOPN]; int toplevels[TOPN]; int linelevels[8] = { 1, 2, 3, 4, 5, 6, 7, 10000000 }; char *levelstring; //info for keeping track of the time and frames per second int timeOfRender=0; int timeAtLastFrame=0; int timeAtLastSecond=0; int timeAtLastRender=0; int timeOfBlockDrop=0; int timeAtLastTexture=0; int timeAtLastFlameCount= 0; int timeAtLastRotCount = 0; int frame=0; int fps=0; int displayFPS = false, displayNext = false; int paused, gameOn; int flameCount=1, flameDirection=1, rotCount=0, rotDirection=1, rotted=0; int mode; int lines, score, speed, level, nextlevel; int rotMsec, rotMax, rotSpeed, rotStyle, rotDegrees; short current, nextpiece; short orientation; short posx, posy; //position based on *center* of 3x3 grid. bar goes outside to the left/top short grid[10][24]; //4 added to the top in the case of the bar being flipped up char* makeString(int i) { char* mystring = (char*)malloc(sizeof(char)*(i+1)); for (;i>=0;i--) mystring[i]=0; return mystring; } void writeScores(void) { FILE *out; int i; if ((out = fopen("fallingup.sco","w")) == NULL) { puts("Unable to open high score file for writing :(\n"); exit(0); } else { for (i=0;i topscores[i]) { replacescore = i; replacechar=0; mode = HIGHSCORE; free (topnames[TOPN-1]); for (j=TOPN-1;j>=i;j--) { topscores[j+1]=topscores[j]; topnames[j+1]=topnames[j]; toplevels[j+1]=toplevels[j]; } topnames[replacescore]=makeString(20); topnames[replacescore][replacechar]='_'; topscores[replacescore]=score; toplevels[replacescore]=level; } } if (mode != HIGHSCORE) mode=OVER; paused=false; //EnableMenuItem(menu,IDM_START,MF_ENABLED); //EnableMenuItem(menu,IDM_STOP,MF_GRAYED); //EnableMenuItem(menu,IDM_PAUSE,MF_GRAYED); gameOn=false; } int isHit(int myposx, int myposy, const short* tempblock) { int x,y; for (x=0;x<4;x++) for (y=0;y<4;y++) { if (tempblock[x+y*4] != 0) { //if this space exists in the grid, or is outside the left/right/bottom bounds... bip if (myposx+x < 0 || myposx+x > 9 || myposy-y < 0) return 1; if (grid[myposx+x][myposy-y] != -1) return 1; } } return 0; } void setBlock(int myposx, int myposy, int myorientation,int mycurrent) { int x,y,clear,numclear,suby; //TODO: reset timer? for (x=0;x<4;x++) for (y=0;y<4;y++) { if(data[mycurrent][myorientation][x+y*4] != 0) { grid[myposx+x][myposy-y] = current; } } current=nextpiece; nextpiece=rand()%PIECES; posx=3; posy=22; orientation=0; if (isHit(posx,posy,data[current][orientation])) { endGame(); return; } /*test to see if we can clear some lines :)*/ numclear=0; for (y=0;y<21;y++) { clear=1; for (x=0;x<10;x++) if (grid[x][y] == -1) clear=0; if (clear) { numclear++; lines+=1; //go up a level, depending //TODO: make this a formula? while (lines >= linelevels[nextlevel]) nextlevel++; for (suby=y+1;suby<21;suby++) for (x=0;x<10;x++) grid[x][suby-1]=grid[x][suby]; y--; } //TODO: better speed alg? //speed is msecs to wait for a block drop speed=BASE_SPEED - lines * 35; if (speed < 125) speed=125; //TODO: better scoring system? score+=100*(numclear*numclear); } clear=1; for (x=0;x<10;x++) if (grid[x][21] != -1) clear=0; if (clear!=1) endGame(); /*test to see if the game is over*/ } int testBounds(short changeType, int amount) { //if we would hit something, and it's due to ydif, stick us there. //hitting something INCLUDES the bottom of the screen, BTW, just in case you //were wondering, this is where that check happens. Neener neener. :) //if we're rotating or xdiffing or ydiffing and not hitting anything, make it so. int i,mycurrent,myposx,myposy,myorientation, stuck; short tempblock[16]; mycurrent=current; myposx=posx; myposy=posy; myorientation=orientation; stuck = false; if (changeType == CHANGEY) { for (i=0;i<16;i++) tempblock[i]=data[mycurrent][myorientation][i]; if (isHit(myposx,myposy-amount,tempblock) == 1) { //stick us there setBlock(myposx,myposy,myorientation,mycurrent); stuck = true; } else { posy-=amount; } } else if (changeType == CHANGEX) { for (i=0;i<16;i++) tempblock[i]=data[current][orientation][i]; if (isHit(myposx-amount,myposy,tempblock) != 1) posx-=amount; } else { //changeType == CHANGEROT for (i=0;i<16;i++) tempblock[i]=data[current][(orientation+1)%4][i]; if (isHit(myposx,myposy,tempblock) != 1) orientation=(orientation+1)%4; } return stuck; } void drawBlock(float x, float y, float z, float red, float green, float blue) { // save the current position and move to the new one glPushMatrix(); x-=5; y-=7; glTranslatef(x, y, z); // begin to draw a block in this position glBegin(GL_QUADS); // we set the color to what was passed, but we also make our own shades glColor4f(red, green, blue,1.0); glVertex3f(-0.5, 0.5, 0.0); glVertex3f(0.5, 0.5, 0.0); glVertex3f(0.5, -0.5, 0.0); glColor4f((1.0-red)/2.0, (1.0-green)/2.0, (1.0-blue)/2.0,1.0); glVertex3f(-0.5, -0.5, 0.0); glEnd(); // now, surround it with a pretty line border glBegin(GL_LINES); glColor4f(0.0, 0.0, 0.0, 1.0); glVertex3f(-0.5, 0.5, 0.0); glVertex3f(0.5, 0.5, 0.0); glVertex3f(-0.5, 0.5, 0.0); glVertex3f(-0.5, -0.5, 0.0); glVertex3f(0.5, 0.5, 0.0); glVertex3f(0.5, -0.5, 0.0); glVertex3f(-0.5, -0.5, 0.0); glVertex3f(0.5, -0.5, 0.0); glEnd(); // before we finish, restore the matrix glPopMatrix(); } void renderBitmapString(float x, float y, void *font,char *string) { char *c; // set position to start drawing fonts glRasterPos2f(x, y); // loop all the characters in the string for (c=string; *c != '\0'; c++) { glutBitmapCharacter(font, *c); } } long getAbsoluteMillis(void) { //return GetTickCount(); //TODO: not partable enough??? // needed for windows? return glutGet(GLUT_ELAPSED_TIME); //fine on OS X... seemed bad on windows... } void setOrthographicProjection(void) { // switch to projection mode glMatrixMode(GL_PROJECTION); // save previous matrix which contains the //settings for the perspective projection glPushMatrix(); // reset matrix glLoadIdentity(); // set a 2D orthographic projection gluOrtho2D(0, WIDTH, 0, HEIGHT/2); glScalef(1, -1, 1); // mover the origin from the bottom left corner // to the upper left corner glTranslatef(0, -HEIGHT/2, 0); glMatrixMode(GL_MODELVIEW); } void resetPerspectiveProjection(void) { // set the current matrix to GL_PROJECTION glMatrixMode(GL_PROJECTION); // restore previous settings glPopMatrix(); // get back to GL_MODELVIEW matrix glMatrixMode(GL_MODELVIEW); } void texturize(void) { glPushMatrix(); glDisable(GL_DEPTH_TEST); glEnable(GL_BLEND); //glColor4f (1.0f, 1.0f, 1.0f, 1.0f - (1.0f/6.0f)); //TODO: (frames) pick blur glColor4f (1.0f, 1.0f, 1.0f, 1.0f - (1.0f/6.0f)); //TODO: (frames) pick blur glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0, WIDTH, HEIGHT, 0, 0, 1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glBindTexture(GL_TEXTURE_2D, texture); glBegin(GL_QUADS); glTexCoord2f(0.0, 1.0); glVertex2f(0, 0); glTexCoord2f(0.0, 0.0); glVertex2f(0, HEIGHT); glTexCoord2f(1.0, 0.0); glVertex2f(WIDTH, HEIGHT); glTexCoord2f(1.0, 1.0); glVertex2f(WIDTH, 0); glEnd(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glEnable(GL_DEPTH_TEST); glDisable(GL_BLEND); glPopMatrix(); glPushMatrix(); glTranslatef (0.0f, 0.0f, -200.0f); //TODO: DO THE ACTUAL MAGIC OF ADDING SOMETHING TO THE TEXTURE! //glClearColor(16.0/(double)flameCount,16.0/(double)flameCount,16.0/(double)flameCount,1); //glClearColor(MAXFLAME/(double)flameCount,MAXFLAME/(double)flameCount,MAXFLAME/(double)flameCount,1); glClearColor((double)flameCount/MAXFLAME,(double)flameCount/MAXFLAME,(double)flameCount/MAXFLAME,1); //glRotatef(360.0f*(flameCount/MAXFLAME),0,0,1); // 010 glBegin(GL_QUADS); // we set the color to what was passed, but we also make our own shades glColor4f(.2, .2, .2,1.0); glVertex3f(-220, 220, 0.0); glColor4f(.2, .9, .2,1.0); glVertex3f(220, 220, 0.0); glColor4f(.8, .8, .8,1.0); glVertex3f(220, -220, 0.0); glColor4f(.3,.3, 1.0,1.0); glVertex3f(-220, -220, 0.0); glEnd(); //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //TODO: needed? glPopMatrix(); } void renderBlocks(void) { int x, y; float XLEFT, XRIGHT, YTOP, YBOTTOM; XLEFT = -5.5; XRIGHT= XLEFT+10.0; YTOP = -7.5; YBOTTOM = YTOP-0.5; glTranslatef(XOFF/2, YOFF/2, ZOFF); //increment 'display level' if (level != nextlevel) { if (rotStyle != ROT_STOP && rotCount > 0) rotStyle = ROT_STOP; if (rotCount == 0 || rotted) { rotted=false; rotCount = 0; //level = nextlevel; level++; levelstring=levels[level]; if (level == 0) { //already set up? } else if (level == 1) { rotStyle=ROT_ONCE; rotMax=230; rotMsec=15; rotDirection = 1; } else if (level == 2) { rotStyle=ROT_ONCE; rotMax=230; rotMsec=15; rotDirection = 1; } else if (level == 3) { rotStyle=ROT_ONCE; rotMax=230; rotMsec=15; rotDirection = 1; } else if (level == 4) { rotStyle=ROT_ONCE; rotMax=230; rotMsec=15; rotDirection = 1; } else if (level == 5) { rotStyle=ROT_CONTINUE; rotMax=230; rotMsec=15; rotDirection = 1; } else if (level == 6) { rotStyle=ROT_ONCE; rotMax=230; rotMsec=15; rotDirection = 1; } else if (level == 7) { rotStyle=ROT_ONCE; rotMax=230; rotMsec=15; rotDirection = 1; } else if (level == 8) { rotStyle=ROT_ONCE; rotMax=230; rotMsec=15; rotDirection = 1; } else if (level == 9) { rotStyle=ROT_ONCE; rotMax=230; rotMsec=15; rotDirection = 1; } else { rotStyle=ROT_ONCE; rotMax=230; rotMsec=15; rotDirection = 1; } } } if (level == 0) { rotCount = 0; } else if (level == 1) { glRotatef(180.0f*((float)rotCount/(float)rotMax),5,0,0); glTranslatef(0.0f, -5.0*((float)rotCount/(float)rotMax), 0.0f); } else if (level == 2) { glRotatef(180.0f,5,0,0); glTranslatef(0.0f, -5.0, 0.0f); glRotatef(180.0f*((float)rotCount/(float)rotMax),0,5,0); //glRotatef(360.0f*((float)rotCount/(float)rotMax),1,1,1); } else if (level == 3) { glRotatef(180.0f*((float)(rotMax-rotCount)/(float)rotMax),5,0,0); glRotatef(180.0f*((float)(rotMax-rotCount)/(float)rotMax),0,5,0); glTranslatef(0.0f, -5.0*((float)(rotMax-rotCount)/(float)rotMax), 0.0f); glRotatef(360.0f*((float)rotCount/(float)rotMax),-10,0,0); } else if (level == 4) { glRotatef(360.0f*((float)rotCount/(float)rotMax),0,0,0); glRotatef(360.0f*((float)rotCount/(float)rotMax),5,0,0); } else if (level == 5) { glRotatef(360.0f*((float)rotCount/(float)rotMax),0,5,0); /* glRotatef(360.0f*((float)rotCount/(float)rotMax),0,1,0); glRotatef(360.0f*((float)rotCount/(float)rotMax),1,0,0); glRotatef(360.0f*((float)rotCount/(float)rotMax),0,0,1); */ } else if (level > 5) { glRotatef(180.0f*((float)rotCount/(float)rotMax),0,5,0); glRotatef(360.0f*((float)rotCount/(float)rotMax),0,1,0); glRotatef(360.0f*((float)rotCount/(float)rotMax),1,0,0); glRotatef(360.0f*((float)rotCount/(float)rotMax),0,0,1); } switch(mode) { case HIGHSCORE: case OVER: case RUNNING: //for each x/y of the grid, paint what's there... then paint our block in its position? for (x=0;x<10;x++) for (y=0;y<20;y++) { if (grid[x][y] > -1) { drawBlock(x,y,0,colors[grid[x][y]][0],colors[grid[x][y]][1],colors[grid[x][y]][2]); } } if (current != -1) { for (x=0;x<4;x++) for (y=0;y<4;y++) { if (data[current][orientation][y*4+x] != 0) drawBlock(posx+x,posy-y,0,colors[current][0],colors[current][1],colors[current][2]); } } // begin to draw the UNDERLINE glBegin(GL_QUADS); glColor3f(1.0, 1.0, 0.0); glVertex3f(XLEFT, YTOP, 0); glVertex3f(XRIGHT, YTOP, 0); glVertex3f(XRIGHT, YBOTTOM, 0); glVertex3f(XLEFT, YBOTTOM, 0); glEnd(); // now, surround it with a pretty line border glBegin(GL_LINES); glColor3f(0.0, 0.0, 0.0); glVertex3f(XLEFT, YTOP, 0); glVertex3f(XRIGHT, YTOP, 0); glVertex3f(XLEFT, YTOP, 0); glVertex3f(XLEFT, YBOTTOM, 0); glVertex3f(XRIGHT, YTOP, 0); glVertex3f(XRIGHT, YBOTTOM, 0); glVertex3f(XLEFT, YBOTTOM, 0); glVertex3f(XRIGHT, YBOTTOM, 0); glEnd(); glTranslatef(-XOFF/2, -YOFF/2, ZOFF); setOrthographicProjection(); glPushMatrix(); glLoadIdentity(); sprintf(scoreString,"%d",score); sprintf(lineString,"%d",lines); glColor3f(0.0,0.0,0.0); renderBitmapString(14,20,(void *)font,levelstring); if (displayNext) { renderBitmapString(288,165,(void *)font,"Next:"); } renderBitmapString(288,226,(void *)font,"Score:"); renderBitmapString(298,241,(void *)font,scoreString); renderBitmapString(288,256,(void *)font,"Lines:"); renderBitmapString(298,271,(void *)font,lineString); glColor3f(0.0,1.0,0.6); renderBitmapString(15,19,(void *)font,levelstring); if (displayNext) { renderBitmapString(289,166,(void *)font,"Next: (F5)"); } renderBitmapString(289,227,(void *)font,"Score:"); renderBitmapString(299,242,(void *)font,scoreString); renderBitmapString(289,257,(void *)font,"Lines:"); renderBitmapString(299,272,(void *)font,lineString); glPopMatrix(); resetPerspectiveProjection(); break; case PAUSED: break; default: //TODO: error? probably not, really. break; } } void renderScores(void) { static char lineitem[80]; int i; for (i=0;i 20) { flameCount+=flameDirection; //if (flameCount > 15 || flameCount < 1) flameDirection *= -1; //if (flameCount >= MAXFLAME) flameCount = 0; if (flameCount > MAXFLAME || flameCount < 1) flameDirection *= -1; timeAtLastFlameCount = timeOfRender; } if (timeOfRender - timeAtLastRotCount > rotMsec) { if (!rotted && rotStyle != ROT_NONE) { rotCount +=rotDirection; if (rotCount < 0) { if (rotStyle == ROT_ONCE || rotStyle == ROT_STOP) { rotted = true; rotCount = 0;} else if (rotStyle == ROT_BOUNCE) { rotDirection *=-1; rotCount = rotDirection; } else if (rotStyle == ROT_CONTINUE) { rotCount += rotMax; } } else { if (rotCount > rotMax) { if (rotStyle == ROT_ONCE || rotStyle == ROT_STOP) { rotted = true; rotCount = rotMax; } else if (rotStyle == ROT_BOUNCE) { rotDirection *=-1; rotCount = rotMax+rotDirection; } else if (rotStyle == ROT_CONTINUE) { rotCount -= rotMax; } } } timeAtLastRotCount = timeOfRender; } } if (timeOfRender - timeAtLastSecond > 1000) { fps = frame*1000.0/(timeOfRender - timeAtLastSecond); timeAtLastSecond = timeOfRender; frame = 0; sprintf(title,"%s [%3d FPS], %d, %d, %d, %d",PROGRAM_NAME,fps,flameCount,rotCount, level, nextlevel); if (displayFPS) glutSetWindowTitle(title); } //TODO: swap renderScenes out depending on mode!! seems more OO. switch (mode) { case INFO: setOrthographicProjection(); glPushMatrix(); glLoadIdentity(); glColor3f(0.0,0.0,0.0); renderBitmapString(20,15,(void *)font,"Falling Up"); renderBitmapString(20,30,(void *)font,"(c) 2004 Kaolin Fire [http://erif.org]"); renderBitmapString(20,160,(void *)font,"Top scores"); glColor3f(0.0,1.0,0.6); renderBitmapString(21,16,(void *)font,"Falling Up"); renderBitmapString(21,31,(void *)font,"(c) 2004 Kaolin Fire [http://erif.org]"); renderBitmapString(21,161,(void *)font,"Top scores"); glColor3f(1.0,1.0,0.0); renderBitmapString(40,60,(void *)font,"F1 -- start"); renderBitmapString(40,70,(void *)font,"F2 -- pause"); renderBitmapString(40,80,(void *)font,"F3 -- toggle framerate display"); renderBitmapString(40,90,(void *)font,"F5 -- toggle 'next piece'"); renderBitmapString(40,100,(void *)font,"F12/ESC -- bosskey (pause/minimize)"); renderBitmapString(40,110,(void *)font,"ALT-F4 -- quit :)"); renderScores(); glPopMatrix(); resetPerspectiveProjection(); break; case RUNNING: if (timeOfRender > timeOfBlockDrop + speed) { testBounds(CHANGEY,1); timeOfBlockDrop = timeOfRender; } renderBlocks(); if (displayNext) { if (nextpiece != -1) { glPushMatrix(); glLoadIdentity(); glTranslatef(9.5, 1.0, ZOFF); for (x=0;x<4;x++) for (y=0;y<4;y++) { if (data[nextpiece][0][y*4+x] != 0) drawBlock(x,4-y,0,colors[nextpiece][0],colors[nextpiece][1],colors[nextpiece][2]); } glPopMatrix(); } } break; case OVER: renderBlocks(); setOrthographicProjection(); glPushMatrix(); glLoadIdentity(); glColor3f(0.0,0.0,0.0); renderBitmapString(63,135,(void *)font," -- GAME OVER: F1 to play again --"); glColor3f(0.5,0.5,0.9); renderBitmapString(64,136,(void *)font," -- GAME OVER: F1 to play again --"); glPopMatrix(); resetPerspectiveProjection(); break; case PAUSED: timeOfBlockDrop += (timeOfRender - timeAtLastRender); // extend block drop setOrthographicProjection(); glPushMatrix(); glLoadIdentity(); glColor3f(0.0,0.0,0.0); renderBitmapString(83,135,(void *)font," -- PAUSED: F2 to continue --"); glColor3f(0.5,0.5,0.9); renderBitmapString(84,136,(void *)font," -- PAUSED: F2 to continue --"); glPopMatrix(); resetPerspectiveProjection(); break; case HIGHSCORE: setOrthographicProjection(); glPushMatrix(); glLoadIdentity(); renderBitmapString(94,136,(void *)font,"!!! HIGH SCORE !!!"); renderScores(); glPopMatrix(); resetPerspectiveProjection(); break; default: break; //TODO: error? } glutSwapBuffers(); // TODO: almost certainly needed... somewhere :) here? //glutPostRedisplay(); // TODO: necessary? only for really cool stuff? } void startGame(void) { int x, y; mode=RUNNING; //TODO: start in INFO mode gameOn=true; srand(time(NULL)); lines=0; level=0; nextlevel=0; score=0; levelstring=levels[0]; speed=BASE_SPEED; for (x=0;x<10;x++) for (y=0;y<22;y++) grid[x][y]=-1; current=(short)(rand()%PIECES); nextpiece=(short)(rand()%PIECES); posx=(short)3; posy=(short)22; orientation=(short)0; timeOfBlockDrop=0; rotCount=0; rotStyle=ROT_NONE; rotMax=180; } void bosskey(void) { if (mode == RUNNING) { mode=PAUSED; } glutIconifyWindow(); } void processNormalKeys(unsigned char key, int x, int y) { if (key == 27) { bosskey(); } else if (mode == RUNNING) { if (key == ' ') { while (!testBounds(CHANGEY,1)); } } else if (mode == OVER) { if (key == '\n' || key == ' ') mode = INFO; } else if (mode == HIGHSCORE) { if (key == 8 || key == 14 || key == 127 || key == '_') { // TODO: what other ascii characters (backspace vs delete?) if (replacechar > 0) { topnames[replacescore][replacechar]=0; replacechar--; topnames[replacescore][replacechar]='_'; } } else if ((key >= 'a' && key <= 'z') || (key >= 'A' && key <= 'Z')) { if (replacechar < 20 - 1) { topnames[replacescore][replacechar]=key; replacechar++; topnames[replacescore][replacechar]='_'; } } else if (key == '\n' || key == 13 || key == 10) { topnames[replacescore][replacechar]=0; writeScores(); mode = INFO; } } } void processSpecialKeys(int key, int x, int y) { switch (key) { case GLUT_KEY_DOWN: if (mode == RUNNING) testBounds(CHANGEY,1); break; case GLUT_KEY_LEFT: if (mode == RUNNING) testBounds(CHANGEX,1); break; case GLUT_KEY_RIGHT: if (mode == RUNNING) testBounds(CHANGEX,-1); break; case GLUT_KEY_UP: if (mode == RUNNING) testBounds(CHANGEROT,1); break; case GLUT_KEY_F3: displayFPS = !displayFPS; glutSetWindowTitle(PROGRAM_NAME); break; case GLUT_KEY_F5: displayNext = !displayNext; break; case GLUT_KEY_F1: if (mode != RUNNING) startGame(); break; case GLUT_KEY_F4: if (glutGetModifiers() & GLUT_ACTIVE_ALT) exit(0); break; case GLUT_KEY_F2: if (mode == RUNNING) { mode=PAUSED; } else if (mode == PAUSED) { mode=RUNNING; } break; case GLUT_KEY_F12: bosskey(); break; default: break; } } void fallingupInit(void) { unsigned int *pData, *pDataTemp; int i, memory=128*128*4*sizeof(unsigned int); mode=INFO; pDataTemp = pData = (unsigned int*) malloc((512*512) * 4 * sizeof(unsigned int)); for (i=0;i