/** * Game 03: "Color Meltdown"
* Put the same color on opposite sides of a white tile
* Matched colors turn white
* Get chain reactions
* P to pause
* Creative Commons License
This work by Nathan McCoy is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License. */ // final int WIDTH = 640; final int HEIGHT = 480; final int BOARD_DIM = 11; //NxN tiles in the board; should be odd final int TILE_SIZE = 36; final int BOARD_SIZE = TILE_SIZE * BOARD_DIM; final int BOARD_LEFT = WIDTH/2 - BOARD_SIZE/2; final int BOARD_TOP = HEIGHT/2 - BOARD_SIZE/2; final int CASCADE_TIME = 15; final int REMOVE_TIME = 30; final int GRAVITY_TIME = 0; final int TIME_MAX = 60 * 60; final int TIME_BONUS = 60; boolean lastmouse; boolean paused; import ddf.minim.*; Minim minim; AudioSample plonkSound; AudioSample cascadeSound1; AudioSample cascadeSound2; AudioSample cascadeSound3; AudioSample cascadeSound4; AudioSample cascadeSound5; AudioSample[] cascade; int cascadeCount; AudioSample clearSound; AudioSample timerSound; AudioSample warningSound; AudioSample gameOverSound; //color consts int NUM_COLORS = 3; //plus white final color[] COLORS = { color(255, 255, 255), color(230, 32, 32), color(32, 192, 32), color(64, 64, 255), color(230, 200, 32), color(230, 64, 230), color(64, 192, 255), color(170, 180, 190)}; int[] board; boolean[] flagged; int score; int gameTimer; int fadeTimer; PFont font; boolean gameover; ArrayList pieceQueue; int QUEUE_SIZE = 7; int phase; final int READY = 0; final int CASCADING = 1; final int REMOVING = 2; final int FALLING = 3; int gameMode; final int DEMOLISH = 0; final int BUILD = 1; //pick a tile based on screen coordinates int screen2board(PVector screencoords) { int x = (int) (screencoords.x - BOARD_LEFT)/TILE_SIZE; int y = (int) (screencoords.y - BOARD_TOP)/TILE_SIZE; x = (int) clamp(x, 0, BOARD_DIM-1); y = (int) clamp(y, 0, BOARD_DIM-1); return x + y * BOARD_DIM; } //get the coordinates of the upper left of the given tile PVector board2screen(int tile) { int x = (int) BOARD_LEFT + (tile % BOARD_DIM) * TILE_SIZE; int y = (int) BOARD_TOP + (tile / BOARD_DIM) * TILE_SIZE; return new PVector(x, y); } void clearTile(int tile) { // board[tile] = -1; board[tile] = randomPiece(); } float clamp(float value, float minimum, float maximum) { if(value < minimum) return minimum; if(value > maximum) return maximum; return value; } final int ROWS = 0; final int COLS = 1; boolean scan(int index, int mode) { boolean found = false; int lastcolor = -1; int lastcolorAt = -1; boolean seenWhite = false; for(int i = 0; i < BOARD_DIM; i++) { int cell; if(mode == ROWS) cell = BOARD_DIM*index + i; else cell = index + BOARD_DIM * i; int cellcolor = board[cell]; if(cellcolor == 0) seenWhite = true; else { if(cellcolor == lastcolor && seenWhite && cellcolor != -1) { flagged[cell] = true; flagged[lastcolorAt] = true; found = true; } lastcolor = board[cell]; lastcolorAt = cell; seenWhite = false; } } return found; } boolean doScan() { boolean found = false; for(int i = 0; i < BOARD_DIM; i++) { found = scan(i, ROWS) || found; found = scan(i, COLS) || found; } return found; } boolean gravity() { boolean somethingFell = false; for(int y = BOARD_DIM -1; y >= 1; y--) for(int x = 0; x < BOARD_DIM; x++) if(board[x+y*BOARD_DIM] == -1) { if(board[x+(y-1)*BOARD_DIM] != -1) somethingFell = true; board[x+y*BOARD_DIM] = board[x+(y-1)*BOARD_DIM]; flagged[x+y*BOARD_DIM] = true; board[x+(y-1)*BOARD_DIM] = -1; } else flagged[x+y*BOARD_DIM] = false; return somethingFell; } void flaggedToWhite() { for(int i=0; i 0) { clearSound.trigger(); fadeTimer = REMOVE_TIME; } else fadeTimer = 0; } int nextPiece() { return ((Integer)pieceQueue.get(0)).intValue(); } void clicky() { if(gameover) { //reset(); } else if(phase == READY) { int mouseTile = screen2board(new PVector(mouseX, mouseY)); board[mouseTile] = nextPiece(); if(nextPiece() == 0) pieceQueue.add(new Integer(0)); else pieceQueue.add(new Integer(randomPiece())); pieceQueue.remove(0); plonkSound.trigger(); if(pieceQueue.get(0).equals(new Integer(0))) warningSound.trigger(); cascadePhase(); } } void draw() { background(0); drawBoard(); drawQueue(); drawTimer(); textAlign(RIGHT, TOP); fill(255); //text("SCORE\n"+score+"\n\nTIME\n"+(gameTimer/60)+"."+(gameTimer%60), width, 0); text("SCORE\n"+score+"\n\nTIME\n"+(gameTimer/60), width, 0); if(!paused) { if(!gameover) { if((gameTimer <= 60* 10) && (gameTimer%60 == 0)) timerSound.trigger(); if(phase == READY) { cascadeCount = 0; } if(phase == CASCADING) { if(fadeTimer == 0) { flaggedToWhite(); boolean continuing = doScan(); if(continuing) { fadeTimer = CASCADE_TIME; cascade[cascadeCount].trigger(); cascadeCount++; if(cascadeCount > 4) cascadeCount = 4; } else { removalPhase(); } } } if(phase == REMOVING) { if(fadeTimer == 0) { if(gameMode == DEMOLISH) phase = FALLING; else phase = READY; } } if(phase == FALLING) { if(fadeTimer == 0) { boolean fall = gravity(); if(fall) fadeTimer = GRAVITY_TIME; else phase = READY; } } if(fadeTimer > 0) fadeTimer--; fill(color(255, 255, 255, 160)); stroke(255); int mouseTile = screen2board(new PVector(mouseX, mouseY)); PVector corner = board2screen(mouseTile); //rect(corner.x, corner.y, TILE_SIZE, TILE_SIZE); fill(COLORS[nextPiece()]); ellipse(corner.x + TILE_SIZE/2, corner.y+TILE_SIZE/2, TILE_SIZE/2, TILE_SIZE/2); gameTimer--; if(gameTimer <= 0) { if(phase == READY) { gameover = true; gameOverSound.trigger(); } else gameTimer = 0; } } else { fill(0, 128); rect(0,0,width, height); textAlign(CENTER, CENTER); fill(255); //text("SCORE\n"+score+"\n\nTIME\n"+(gameTimer/60)+"."+(gameTimer%60), width, 0); text("COLOR MELTDOWN\n\npress space for new game", width/2, height/2); } if(!lastmouse && mousePressed) clicky(); lastmouse = mousePressed; } else { textAlign(RIGHT, TOP); fill(255); //text("SCORE\n"+score+"\n\nTIME\n"+(gameTimer/60)+"."+(gameTimer%60)+"\nPAUSED", width, 0); text("SCORE\n"+score+"\n\nTIME\n"+(gameTimer/60)+"\nPAUSED", width, 0); } } void keyPressed() { if(key == 'p') paused = !paused; if(key == ' ' && gameover) reset(); } void stop() { plonkSound.close(); cascadeSound1.close(); cascadeSound2.close(); cascadeSound3.close(); cascadeSound4.close(); cascadeSound5.close(); clearSound.close(); timerSound.close(); warningSound.close(); gameOverSound.close(); minim.stop(); super.stop(); }