/**
* 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
* 
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();
}