/*************************** Cytris by andre-michelle.com ****************************/ int mode; static final int STARTSCREEN = 0; static final int PLAYING = 1; static final int CLEARLINES = 2; static final int GAMEOVER = 3; boolean Key_UP = false; boolean Key_DN = false; boolean Key_LT = false; boolean Key_RT = false; boolean Key_CT = false; boolean Key_SH = false; CytrisDisplay display; CytrisLogic logic; CytrisPlayField playfield; CytrisAudio audio; BImage[] tiles; BImage bg; BImage elayer; int ms; int defMsPerFrame = 27; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void setup() { beginSound(); size( 400, 500 ); bg = loadImage( "bg.gif" ); elayer = new BImage( width , height ); tiles = new BImage[8]; tiles[0] = loadImage( "texture.gif" ); tiles[1] = loadImage( "green.gif" ); tiles[2] = loadImage( "yellow.gif" ); tiles[3] = loadImage( "blue.gif" ); tiles[4] = loadImage( "turkey.gif" ); tiles[5] = loadImage( "red.gif" ); tiles[6] = loadImage( "pink.gif" ); tiles[7] = loadImage( "orange.gif" ); mode = STARTSCREEN; display = new CytrisDisplay(); logic = new CytrisLogic( 12 , 20 ); playfield = new CytrisPlayField( logic ); audio = new CytrisAudio(); logic.prepare(); } void onStart() { audio.playSound( "start" ); mode = PLAYING; } void onRestart() { logic.prepare(); mode = STARTSCREEN; } void onClearLines() { display.setScoreLines( logic.fullLines.size() ); playfield.initClearLines(); audio.playSound( "unBuildRows" ); mode = CLEARLINES; } void onClearedLines() { logic.removeFullLines(); audio.playSound( "nextBlock" ); mode = PLAYING; } void onGameOver() { display.setTimeOutGameOver(); mode = GAMEOVER; } void loop() { ms = millis(); //-------------------------------------------// background( bg ); switch ( mode ) { case STARTSCREEN: playfield.randomBlocks(); display.showStartText(); display.showStartButton(); break; case PLAYING: logic.nextFrame(); playfield.showLogicResult(); playfield.lookAtBlock(); playfield.showNextBlock(); display.updateScore(); break; case CLEARLINES: playfield.showLogicResult(); playfield.lookAtBlock(); playfield.clearingLines(); playfield.showNextBlock(); display.updateScore(); break; case GAMEOVER: playfield.randomBlocks(); display.showGameOverScreen(); break; default: println( "NO MODE SELECTED" ); } //-------------------------------------------// int frameTime = ( millis() - ms ); int rest = defMsPerFrame - frameTime; if ( rest > 0 ) { //-- DELAY UNTILL ELAPSE delay( rest ); } else { //println( frameTime ); } } public void keyPressed( KeyEvent e ) { int key = e.getKeyCode(); if ( key == 37 ) Key_LT = true; if ( key == 38 ) Key_UP = true; if ( key == 39 ) Key_RT = true; if ( key == 40 ) Key_DN = true; if ( key == 16 ) Key_SH = true; if ( key == 17 ) Key_CT = true; } public void keyReleased( KeyEvent e ) { int key = e.getKeyCode(); if ( key == 37 ) Key_LT = false; if ( key == 38 ) Key_UP = false; if ( key == 39 ) Key_RT = false; if ( key == 40 ) Key_DN = false; if ( key == 16 ) Key_SH = false; if ( key == 17 ) Key_CT = false; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// class CytrisDisplay { BImage startButton; BImage startButton_over; int startButtonX; int startButtonY; BitmapFont hud; BitmapFont dscore; BitmapFont dlines; BitmapFont startText; BitmapFont dgameover; int score; int lines; int timeOutGameOver; CytrisDisplay() { init(); score = 0; lines = 0; } void init() { startButton = loadImage( "startButton.gif" ); startButton_over = loadImage( "startButton_over.gif" ); startButtonX = width / 2 - startButton.width / 2; startButtonY = 250; startText = new BitmapFont( "letters.gif" , "0123456789ABCDEFGHIJKLMNOPQRSTUVWZYX" , 16 , 22 ); hud = new BitmapFont( "letters.gif" , "0123456789ABCDEFGHIJKLMNOPQRSTUVWZYX" , 16 , 22 ); dscore = new BitmapFont( "letters.gif" , "0123456789ABCDEFGHIJKLMNOPQRSTUVWZYX" , 16 , 22 ); dlines = new BitmapFont( "letters.gif" , "0123456789ABCDEFGHIJKLMNOPQRSTUVWZYX" , 16 , 22 ); dgameover = new BitmapFont( "letters.gif" , "0123456789ABCDEFGHIJKLMNOPQRSTUVWZYX" , 16 , 22 ); startText.setText( "***WELCOME TO CYTRIS* PRESS START***********BY ANDRE MICHELLE" ); hud.setText( "SCORE***LINES" ); } void setScoreLines( int lines ) { this.lines += lines; score += lines * lines * 50; } void showStartText() { startText.draw(); } void showStartButton() { int bit = startButton.get( mouseX - startButtonX , mouseY - startButtonY ); if ( bit != 0 ) { if ( mousePressed ) { onStart(); } else { image( startButton_over , startButtonX , startButtonY ); } } else { image( startButton , startButtonX , startButtonY ); } } void updateScore() { hud.draw( 300 , 25 ); //-- DRAW SCORE --// dscore.setText( "*" + nf( score , 5 ) ); dscore.draw( 300 , 25 ); //-- DRAW LINES dscore.setText( "****" + nf( lines , 5 ) ); dscore.draw( 300 , 25 ); } void setTimeOutGameOver() { timeOutGameOver = 0; } void showGameOverScreen() { if ( ++ timeOutGameOver >= 210 ) { onRestart(); return; } dgameover.setText( " GAME OVER*RESTART IN " + ( 8 - int(timeOutGameOver / 30) ) + " SECONDS*** YOUR SCORE* " + nf( score , 5 ) ); dgameover.draw(); } } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// class CytrisPlayField { CytrisLogic logic; BGraphics preview; BImage preview_bg; int pdim = 94; //-- CAMERA SETTINGS --// float fovy, aspect, Znear, Zfar; float distance; //-- CYLINDER BLOCKS --// float radius0; float radius1; //-- VIEW ANGLE, TRANSLATE --// float ay = 0; float path; float previewRot; float grayCycle; float spinAngle; //-- UNBUILDLINES --// BImage[] vanishTiles; int[][] vanishBlocks; int timer; CytrisPlayField( CytrisLogic logic ) { this.logic = logic; init(); } void init() { preview = new BGraphics( pdim , pdim ); preview_bg = loadImage( "preview.gif" ); distance = 1100.0; fovy = 68.0; aspect = ( float )width / ( float )height; Znear = 1.0; // ? Zfar = 1000.0; // ? path = -1.0; grayCycle = 0; previewRot = 0; spinAngle = 0; //-- TEXTURES HARDLY SET ON 64PX --// radius0 = 64 / ( 2 * sin( TWO_PI / ( logic.cols - 1 ) / 2 ) ); radius1 = radius0 + 4; } void randomBlocks() { prepareMainCamera(); //-- locals --// BImage block; int cols = logic.cols; grayCycle -= .05; //-- SHOW RANDOM TILES --// int bx, by; for ( by = 0 ; by < logic.rows ; by++ ) { float tintValue = 220 + sin( grayCycle + (float)by / HALF_PI ) * 35; tint( tintValue ); for ( bx = 0 ; bx < cols ; bx++ ) { push(); float angle = TWO_PI / cols * bx; translate( sin( angle ) * radius0 , by * 64 -9.5 * 64 , cos( angle ) * radius0 ); rotateY( angle ); int index = int( random( 0 , 50 ) ); if ( index >= tiles.length ) index = 0; block = tiles[ index ]; image( block , -30 , -30 , 60 , 60 ); pop(); } noTint(); path += ( sin( spinAngle += .001 ) * 1.5 - path ) / 16; } } void initClearLines() { timer = 0; int tcount = tiles.length; int i,x,y; vanishTiles = new BImage[ tcount ]; for ( i = 0 ; i < tcount ; i++ ) { vanishTiles[i] = tiles[i].copy(); } int cols = logic.cols; int rows = logic.rows; vanishBlocks = new int[rows][cols]; for( y = 0 ; y < rows ; y++ ) { for ( x = 0 ; x < cols ; x++ ) { vanishBlocks[y][x] = logic.released[y][x]; } } } void clearingLines() { timer++; //-- BUILD NOISE ALPHA FIELD --// BImage noiseField = new BImage( 64 , 64 ); float noiseScale = .1; noiseDetail( 8 , (float)timer / 50 ); for(int y=0; y<64; y++) { for(int x=0; x<64; x++) { int grey = 255 - (int)( noise( x * noiseScale , y * noiseScale ) * 255 ); noiseField.pixels[ y * 64 + x ] = color( grey , grey , grey ); } } //-- locals --// BImage block; int cols = logic.cols; grayCycle -= .1; //-- SHOW RELEASED TILES --// int bx, by, ty; float radius2 = radius1 + timer * 10; Stack fullLines = logic.fullLines; for ( ty = 0 ; ty < fullLines.size() ; ty++ ) { by = ((Integer)fullLines.elementAt( ty )).intValue(); for ( bx = 0 ; bx < cols ; bx++ ) { push(); float angle = TWO_PI / cols * bx -ay * 2; translate( sin( angle ) * radius2 , by * 64 -9.5 * 64 + (float)by / 2 * (float)timer * 4 , cos( angle ) * radius2 ); rotateY( angle ); block = vanishTiles[ vanishBlocks[ by ][ bx ] ]; block.alpha( noiseField.pixels ); image( block , -32 , -32 , 64 , 64 ); pop(); } } ay += .05; if ( timer > 50 ) { onClearedLines(); } } void showLogicResult() { prepareMainCamera(); //-- locals --// BImage block; int cols = logic.cols; grayCycle -= .05; //-- SHOW RELEASED TILES --// int bx, by; for ( by = 0 ; by < logic.rows ; by++ ) { float tintValue = 220 + sin( grayCycle + (float)by / HALF_PI ) * 35; tint( tintValue ); for ( bx = 0 ; bx < cols ; bx++ ) { push(); float angle = TWO_PI / cols * bx; translate( sin( angle ) * radius0 , by * 64 -9.5 * 64 , cos( angle ) * radius0 ); rotateY( angle ); block = tiles[ logic.released[ by ][ bx ] ]; image( block , -30 , -30 , 60 , 60 ); pop(); } noTint(); } //-- DRAW CURRENT BLOCK --// int [][] blockdata = logic.getCurrentBlock(); int blockIndex = logic.getCurrentBlockIndex(); int dim = blockdata.length; int sx = logic.getBlockX(); int sy = logic.getBlockY(); for( by = sy ; by < sy + dim ; by++ ) { for ( bx = sx ; bx < sx + dim ; bx++ ) { push(); float angle = TWO_PI / cols * bx; translate( sin( angle ) * radius1 , by * 64 -9.5 * 64 , cos( angle ) * radius1 ); rotateY( angle ); if ( blockdata[ by - sy ][ bx - sx ] == 1 ) { block = tiles[ blockIndex + 1 ]; image( block , -32 , -32 ); } pop(); } } } void showNextBlock() { previewRot += .15; preview.background( preview_bg ); preview.beginCamera(); preview.push(); preview.perspective( fovy , 1 , Znear , Zfar ); preview.translate( 0 , 0 , -300 ); preview.rotateY( previewRot ); preview.rotateX( previewRot / 2 ); preview.endCamera(); preview.pop(); int x, y; int[][] blockdata = logic.getNextBlock(); int dim = blockdata.length; int blockIndex = logic.getNextBlockIndex(); preview.tint( 255 , 196 ); for( y = 0 ; y < dim ; y++ ) { for( x = 0 ; x < dim ; x++ ) { if( blockdata[y][x] == 1 ) { preview.push(); preview.translate( x * 64 - (float)dim * 32 , y * 64 - (float)dim * 32 , 0 ); preview.image( tiles[ blockIndex + 1 ] , 0 , 0 , 64 , 64 ); preview.pop(); } } } image( preview , 16 , 25 , pdim , pdim ); } void prepareMainCamera() { beginCamera(); push(); perspective( fovy , aspect , Znear , Zfar ); translate( 0 , path * -150 , -distance ); rotateX( path * PI / 5 ); rotateY( ay ); endCamera(); pop(); } void lookAtBlock() { int sx = logic.getBlockX(); int sy = logic.getBlockY(); int dim = logic.getCurrentBlock().length; float b_ay = -( (float)sx + (float)dim / 2 - .5 ) / (float)logic.cols * TWO_PI - ay; while ( b_ay < -PI ) b_ay += TWO_PI; while ( b_ay > PI ) b_ay -= TWO_PI; ay += b_ay / 8; //-- MOVE CAMERA ALONG PATH --// float b_path = (float)( sy + dim / 2 ) / (float)logic.rows * 2 - 1; path += ( b_path - path ) / 8; } } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// class CytrisLogic { //-- BLOCKS --// int [][][][] blocks = { {{{0,1,0},{1,1,1},{0,0,0}},{{0,1,0},{0,1,1},{0,1,0}},{{0,0,0},{1,1,1},{0,1,0}},{{0,1,0},{1,1,0},{0,1,0}}}, {{{0,1,0},{0,1,0},{1,1,0}},{{1,0,0},{1,1,1},{0,0,0}},{{0,1,1},{0,1,0},{0,1,0}},{{0,0,0},{1,1,1},{0,0,1}}}, {{{0,1,0},{0,1,0},{0,1,1}},{{0,0,0},{1,1,1},{1,0,0}},{{1,1,0},{0,1,0},{0,1,0}},{{0,0,1},{1,1,1},{0,0,0}}}, {{{0,0,0},{0,1,1},{1,1,0}},{{1,0,0},{1,1,0},{0,1,0}},{{0,1,1},{1,1,0},{0,0,0}},{{0,1,0},{0,1,1},{0,0,1}}}, {{{0,0,0},{1,1,0},{0,1,1}},{{0,1,0},{1,1,0},{1,0,0}},{{1,1,0},{0,1,1},{0,0,0}},{{0,0,1},{0,1,1},{0,1,0}}}, {{{1,1},{1,1}},{{1,1},{1,1}},{{1,1},{1,1}},{{1,1},{1,1}}}, {{{0,1,0,0},{0,1,0,0},{0,1,0,0},{0,1,0,0}},{{0,0,0,0},{1,1,1,1},{0,0,0,0},{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}}} }; int rows; int cols; int [][]released; int[] nextBlock; Stack fullLines; int currentblockIndex; int currentrotation; int blockX; int blockY; int lastblockX; int lastblockY; int lastcurrentrotation; int timer; int droptime; int keyTime; int keydelay; boolean keyAvail; CytrisLogic( int cols, int rows ) { this.cols = cols; this.rows = rows; keydelay = 1; droptime = 16; } void prepare() { released = new int[ rows ][ cols ]; fullLines = new Stack(); timer = 0; keyAvail = true; nextBlock = getNewBlock(); attachNextBlock(); } void nextFrame() { timer++; saveState(); if ( timer % droptime == 0 ) { blockY++; if( checkCollide() ) { restoreState(); releaseBlock(); attachNextBlock(); if ( checkCollide() ) { onGameOver(); return; } checkFullLines(); } else { audio.playSound( "freemove" ); } } else { int userinput = checkUserInput(); if ( userinput > 0 ) { if( checkCollide() ) { restoreState(); if ( userinput == 3 ) { releaseBlock(); attachNextBlock(); if ( checkCollide() ) { onGameOver(); return; } checkFullLines(); } } else { audio.playSound( userinput == 2 ? "rotate" : "freemove" ); } } } } void checkFullLines() { int x, y; boolean full; int count = 0; for( y = 0 ; y < rows ; y++ ) { full = true; for( x = 0 ; x < cols ; x++ ) { if ( released[ y ][ x ] == 0 ) { full = false; } } if( full ) { fullLines.push( new Integer( y ) ); } } if ( fullLines.empty() ) { audio.playSound( "released" ); } else { onClearLines(); for( int i = 0 ; i < fullLines.size() ; i++ ) { y =(( Integer)fullLines.elementAt( i ) ).intValue(); for( x = 0 ; x < cols ; x++ ) { released[ y ][ x ] = 0; } } } } void removeFullLines() { for( int i = 0 ; i < fullLines.size() ; i++ ) { removeRow( ( (Integer)fullLines.elementAt( i ) ).intValue() ); } fullLines = new Stack(); } void removeRow( int row ) { int x, y; for ( y = row - 1 ; y > -1 ; y-- ) { for( x = 0 ; x < cols ; x++ ) { released[ y + 1 ][ x ] = released[ y ][ x ]; } } //-- fill first row --// for( x = 0 ; x < cols ; x++ ) { released[ 0 ][ x ] = 0; } } void releaseBlock() { int x, y; int[][] block = getCurrentBlock(); int dim = block.length; for( y = 0 ; y < dim ; y++ ) { for( x = 0 ; x < dim ; x++ ) { if( block[ y ][ x ] == 1 ) { int bx = x + blockX; if ( bx >= cols ) { bx -= cols; } released[ y + blockY ][ bx ] = currentblockIndex + 1; } } } } void attachNextBlock() { currentblockIndex = nextBlock[0]; currentrotation = nextBlock[1]; blockX = nextBlock[2]; blockY = nextBlock[3]; nextBlock = getNewBlock(); } int[] getNewBlock() { int[] block = new int[ 4 ]; block[ 0 ] = getRandomBlockIndex(); block[ 1 ] = int( random( 0 , 4 ) ); block[ 2 ] = int( random( 0 , cols ) ); block[ 3 ] = 0; return block; } void saveState() { lastblockX = blockX; lastblockY = blockY; lastcurrentrotation = currentrotation; } void restoreState() { blockX = lastblockX; blockY = lastblockY; currentrotation = lastcurrentrotation; } int checkUserInput() { if ( keyAvail ) { if ( Key_RT ) { if ( ++blockX == cols ) blockX = 0; keyTime = timer; keyAvail = false; return 1; } if ( Key_LT ) { if ( --blockX == -1 ) blockX = cols - 1; keyTime = timer; keyAvail = false; return 1; } if ( Key_UP ) { if ( ++currentrotation == 4 ) currentrotation = 0; keyTime = timer; keyAvail = false; return 2; } if ( Key_DN ) { blockY++; keyTime = timer; keyAvail = false; return 3; } } else { if ( ( timer - keyTime ) > keydelay ) { keyAvail = true; } } return 0; } boolean checkCollide() { int x, y; int[][] block = getCurrentBlock(); int dim = block.length; for( y = 0 ; y < dim ; y++ ) { for( x = 0 ; x < dim ; x++ ) { if( block[ y ][ x ] == 1 ) { if( y + blockY >= rows ) { return true; } int bx = x + blockX; if ( bx >= cols ) { bx -= cols; } if( released[ y + blockY ][ bx ] > 0 ) { return true; } } } } return false; } int getCurrentBlockIndex() { return currentblockIndex; } int getNextBlockIndex() { return nextBlock[0]; } int getRandomBlockIndex() { return int( random( 0 , blocks.length ) ); } int[][] getCurrentBlock() { return blocks[ currentblockIndex ][ currentrotation ]; } int[][] getNextBlock() { return blocks[ nextBlock[0] ][ nextBlock[1] ]; } int getBlockX() { return blockX; } int getBlockY() { return blockY; } } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// class CytrisAudio implements EventListener { BSound[] tone; BSound released; CytrisAudio() { tone = new BSound[5]; } public void playSound( String sound ) { if( sound == "freemove" ) { tone[0] = loadSound( "freemove.wav" ); play( tone[0] ); } else if ( sound == "released" ) { tone[1] = loadSound( "released.wav" ); play( tone[1] ); } else if ( sound == "unBuildRows" ) { tone[2] = loadSound( "unBuidRows.wav" ); play( tone[2] ); } else if ( sound == "rotate" ) { tone[3] = loadSound( "rotate.wav" ); play( tone[3] ); } else if ( sound == "start" ) { tone[3] = loadSound( "start.wav" ); play( tone[3] ); } else if ( sound == "nextBlock" ) { tone[4] = loadSound( "nextBlock.wav" ); play( tone[4] ); } } } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// class BitmapFont { BImage fontmap; BImage resultImage; HashMap letters; int letterW; int letterH; int offset = 4; char newline; BitmapFont( String fontName , String content, int letterW, int letterH ) { fontmap = loadImage( fontName ); this.letterW = letterW; this.letterH = letterH; letters = new HashMap(); for( int i = 0 ; i < content.length() ; i++ ) { char key = content.charAt( i ); letters.put( key + "" , new Integer( i ) ); } newline = '*'; } void setText( String txt ) { int tw = txt.length(); int i; int linebreaks = 0; int maxWidth = 0; int lastBreak = 0; for ( i = 0 ; i < tw ; i++ ) { if( txt.charAt( i ) == newline ) { linebreaks++; maxWidth = max( maxWidth, i - lastBreak ); lastBreak = i; } } maxWidth = max( maxWidth, i - lastBreak ); //-- 1 row --// if( maxWidth == 0 ) maxWidth = tw; resultImage = new BImage( --maxWidth * letterW , letterH * ++linebreaks + offset * --linebreaks ); int col = 0; int row = 0; for( i = 0 ; i < tw ; i++ ) { char letter = txt.charAt( i ); if ( letter == newline ) { row++; col = 0; } else { Integer hashobj = (Integer)letters.get( letter + "" ); if ( hashobj != null ) { int position = hashobj.intValue(); int sx = position * letterW; int dx = col * letterW; int dy = row * letterH + row * offset; resultImage.replicate( fontmap, sx, 0 , sx + letterW , letterH , dx , dy , dx + letterW , dy + letterH ); } col++; } } } void draw( int x, int y ) { if ( resultImage != null ) { image( resultImage , x , y ); } }; void draw() { if ( resultImage != null ) { image( resultImage , width / 2 - resultImage.width / 2 , height / 2 - resultImage.height / 2 ); } } } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////