// quadblok // - a familiar game // // Scott "Jerry" Lawrence // sdlpci@cis.rit.edu // // $Id: quadblok.pde,v 1.1 2003/09/04 03:43:02 jerry Exp $ // Known Bugs: // - do doubles/triples/quads score correctly? // - game does not end - error when filled! // Things to add: // - play modes // - clear N lines // - start with N garbage lines // - get down to 0 rows // - increasing speed with levels cleared // app-level stuff int hs = 10; // horiz size per square on the screen int vs = 10; // vert size per square on the screen BFont fff; // the font we're using for the text // game level stuff boolean paused = false; // are we paused? int score; // the current score int scDrop = 5; // when a piece is dropped int scAffix = 1; // when a piece is affixed int scOne = 10; // one row, doubled each line cleared int rowsCleared; // total number of rows cleared Piece nextPiece; // the next piece we're gonna use Playfield field; // the playfield we're playing on int gameState = 0; // currrent game state // 0: attract // 1: play // 2: game over void setup() { size( 200, 200 ); background( 0, 0, 50 ); fff = loadFont( "OCR-A.vlw.gz" ); hint( SMOOTH_IMAGES ); resetGame(); } int val = 0; int last = 0; void loop() { if( gameState == 1 ) { int s = second(); if( last != s ) { field.timeStep(); last = s; } nextPiece.nextDraw( 130, 50 ); } field.draw(); drawText(); if( paused ) uberText( "paused", 65, 100 ); } // keyPressed - keyboard handler - gameplay void keyPressed() { if( key == UP ) field.moveCCW(); // rotate the piece if( key == LEFT ) field.moveLeft(); // move piece left if( key == RIGHT ) field.moveRight(); // move piece right if( ( key == ' ' ) || ( key == DOWN ) ) { field.moveDown(); // drop the piece last = -1; // force an update } } // keyReleased - keyboard handler - overall game control void keyReleased() { if( key == 'q' ) // quit game! resetGame(); if( gameState == 1 ) { if( key == 'p' ) // pause! { if( paused ) paused = false; else paused = true; } } if( gameState == 2) { if( key == 'n' ) { resetGame(); gameState = 1; } } if( gameState == 0 ) // space to continue... { if( key == 's' ) gameState = 1; field = new Playfield( 10, 20 ); } } void resetGame() { gameState = 0; score = 0; rowsCleared = 0; paused = false; nextPiece = new Piece(); field = new Playfield( 10, 20 ); } void drawText() { setFont( fff, 30 ); fill( 0 ); text( "QUAD", 110, 21 ); text( "BLOK", 131, 31 ); if( gameState == 0 || gameState == 2 ) fill( random( 255 )); else fill( 255 ); text( "QUAD", 109, 20 ); if( gameState == 0 || gameState == 2 ) fill( random( 255 )); else fill( 255 ); text( "BLOK", 130, 30 ); if( gameState == 0 ) uberText( "'s' to start", 40, 150 ); if( gameState == 2 ) { uberText( "G A M E O V E R", 20, 80 ); uberText( "'n' for new game", 20, 110 ); } if( gameState >= 1 ) { setFont( fff, 25 ); /* not used yet fill( 0, 0, 255 ); text( nf(0, 3), 108, 141 ); */ fill( 255, 0, 0 ); text( nf(rowsCleared, 3), 108, 163 ); fill( 255, 255, 0 ); text( nf(score, 6), 108, 185 ); } } void uberText(String text, int x, int y) { setFont( fff, 18 ); fill( 40, 0, 0 ); rect( 0, y-15, width, 20 ); fill( 0 ); text( text, x-1, y ); text( text, x+1, y ); text( text, x, y-1 ); text( text, x, y+1 ); fill( 255, 255, 0 ); text( text, x, y ); } // Playfield class - stores all of the data for the playfield class Playfield { color[][] data; // the pixels in the data int w; int h; Piece activePiece; // the active piece in play int apx; // x location of the piece int apy; // y location of the piece Playfield(int ww, int hh) { w = ww; h = hh; data = new color[w][h]; zeroFill(); diffFill( 5 ); newPiece(); } // zeroFill - clear the playfield void zeroFill() { for( int y=0 ; y= apx && x < apx+activePiece.getSize() ) && ( y >= apy && y < apy+activePiece.getSize() )) { if( activePiece.active( x-apx, y-apy )) { fill( activePiece.c() ); } } } rect( x*hs, y*vs, hs-1, vs-1 ); } } } // moveCCW -- attempt to rotate the piece void moveCCW() { if( !rotateCollide() ) activePiece.spin(); } // moveLeft -- attempt to move piece to the left void moveLeft() { if( !collide( apx-1, apy )) apx--; } // moveRight -- attempt to move piece to the right void moveRight() { if( !collide( apx+1, apy )) apx++; } // moveDown -- attemp to drop piece down void moveDown() { if( !collide( apx, apy+1 )) { apy++; moveDown(); } score += scDrop; // score adjust } // timeStep - every N ticks of the clock, we do these things. (auto drop, affix) void timeStep() { gameOverCheck(); if( !collide( apx, apy+1 )) { apy++; } else { // glue down the piece, make a new one affixPiece(); newPiece(); score += scAffix; // score adjust } // check for line clearing lineClear(); } // gameOverCheck -- check for game over void gameOverCheck() { if( collide( apx, apy ) ) { gameState = 2; } } // lineClear -- check for rows to be cleared and clear them void lineClear() { int scoreAdjust = 0; int remove = checkClear(); while( remove > 0 ) { // shift them all up for( int j=remove ; j>0 ; j-- ) { for( int i=0 ; i=0 ; row-- ) { int set = 0; for( int col=0 ; col= w || x+i < 0 || y+j >=h ) collision = true; else if( data[ x+i ][ y+j ] != color( 0, 0, 0 )) collision = true; } } } return( collision ); } // rotateCollide - check for current piece collisions with the new rotation boolean rotateCollide() { Piece rotatedPiece = new Piece( activePiece.getWhich(), activePiece.getFrame()+1 ); boolean collision = false; for( int i=0 ; i= w || apx+i < 0 || apy+j >=h ) collision = true; else if( data[ apx+i ][ apy+j ] != color( 0, 0, 0 )) collision = true; } } } return( collision ); } // affixPiece void affixPiece() { for( int i=0 ; i 1 ) data[i][j] = true; // pixel is occupied else data[i][j] = false; // pixel is empty } } } // active - returns true if the pixel in the data array is occupied boolean active( int x, int y ) { return( data[x][y] ); // just a simple accessor of the data array } // top -- returns the first row number with data in it int top() { for( int y=0 ; y