You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
503 lines
12 KiB
503 lines
12 KiB
14 years ago
|
/****************************************************************************
|
||
|
**
|
||
|
** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
|
||
|
**
|
||
|
** This file is part of an example program for Qt. This example
|
||
|
** program may be used, distributed and modified without limitation.
|
||
|
**
|
||
|
*****************************************************************************/
|
||
|
|
||
|
#include "gtetrix.h"
|
||
|
|
||
|
#include <string.h>
|
||
|
|
||
|
GenericTetrix::GenericTetrix(int boardWidth,int boardHeight)
|
||
|
{
|
||
|
int i,j;
|
||
|
|
||
|
width = boardWidth;
|
||
|
height = boardHeight;
|
||
|
boardPtr = new int[height*width]; // Note the order, this makes it easier
|
||
|
// to remove full lines.
|
||
|
for(i = 0 ; i < height ; i++)
|
||
|
for(j = 0 ; j < width ; j++)
|
||
|
board(j,i) = 0;
|
||
|
currentLine = -1; // -1 if no falling piece.
|
||
|
currentPos = 0;
|
||
|
showNext = 0; // FALSE
|
||
|
nLinesRemoved = 0;
|
||
|
nPiecesDropped = 0;
|
||
|
score = 0;
|
||
|
level = 1;
|
||
|
gameID = 0;
|
||
|
nClearLines = height;
|
||
|
}
|
||
|
|
||
|
GenericTetrix::~GenericTetrix()
|
||
|
{
|
||
|
delete[] boardPtr;
|
||
|
}
|
||
|
|
||
|
|
||
|
void GenericTetrix::clearBoard(int fillRandomLines)
|
||
|
{
|
||
|
int i,j;
|
||
|
|
||
|
if (fillRandomLines >= height)
|
||
|
fillRandomLines = height - 1;
|
||
|
|
||
|
erasePiece();
|
||
|
for(i = height - nClearLines - 1 ; i >= fillRandomLines ; i--)
|
||
|
for(j = 0 ; j < width ; j++)
|
||
|
if (board(j,i) != 0) {
|
||
|
draw(j,i,0);
|
||
|
board(j,i) = 0;
|
||
|
}
|
||
|
if (fillRandomLines != 0)
|
||
|
for (i = 0 ; i < fillRandomLines ; i++) {
|
||
|
fillRandom(i);
|
||
|
}
|
||
|
nClearLines = height - fillRandomLines;
|
||
|
}
|
||
|
|
||
|
void GenericTetrix::showBoard()
|
||
|
{
|
||
|
int i,j;
|
||
|
|
||
|
showPiece();
|
||
|
for(i = height - nClearLines - 1 ; i >= 0 ; i--)
|
||
|
for(j = 0 ; j < width ; j++)
|
||
|
if (board(j,i) != 0)
|
||
|
draw(j,i,board(j,i));
|
||
|
}
|
||
|
|
||
|
void GenericTetrix::hideBoard()
|
||
|
{
|
||
|
int i,j;
|
||
|
|
||
|
erasePiece();
|
||
|
for(i = height - nClearLines - 1 ; i >= 0 ; i--)
|
||
|
for(j = 0 ; j < width ; j++)
|
||
|
if (board(j,i) != 0)
|
||
|
draw(j,i,0);
|
||
|
}
|
||
|
|
||
|
void GenericTetrix::startGame(int gameType,int fillRandomLines)
|
||
|
{
|
||
|
gameID = gameType;
|
||
|
clearBoard(fillRandomLines);
|
||
|
nLinesRemoved = 0;
|
||
|
updateRemoved(nLinesRemoved);
|
||
|
nClearLines = height;
|
||
|
nPiecesDropped = 0;
|
||
|
score = 0;
|
||
|
updateScore(score);
|
||
|
level = 1;
|
||
|
updateLevel(level);
|
||
|
newPiece();
|
||
|
}
|
||
|
|
||
|
void GenericTetrix::revealNextPiece(int revealIt)
|
||
|
{
|
||
|
if (showNext == revealIt)
|
||
|
return;
|
||
|
showNext = revealIt;
|
||
|
if (!showNext)
|
||
|
eraseNextPiece();
|
||
|
else
|
||
|
showNextPiece();
|
||
|
}
|
||
|
|
||
|
void GenericTetrix::updateBoard(int x1,int y1,int x2, int y2,
|
||
|
int dontUpdateBlanks)
|
||
|
{
|
||
|
int i,j;
|
||
|
int tmp;
|
||
|
|
||
|
if (x1 > x2) {
|
||
|
tmp = x2;
|
||
|
x2 = x1;
|
||
|
x1 = tmp;
|
||
|
}
|
||
|
if (y1 > y2) {
|
||
|
tmp = y2;
|
||
|
y2 = y1;
|
||
|
y1 = tmp;
|
||
|
}
|
||
|
if (x1 < 0)
|
||
|
x1 = 0;
|
||
|
if (x2 >= width)
|
||
|
x2 = width - 1;
|
||
|
if (y1 < 0)
|
||
|
y1 = 0;
|
||
|
if (y2 >= height)
|
||
|
y2 = height - 1;
|
||
|
for(i = y1 ; i <= y2 ; i++)
|
||
|
for(j = x1 ; j <= x2 ; j++)
|
||
|
if (!dontUpdateBlanks || board(j,height - i - 1) != 0)
|
||
|
draw(j,height - i - 1,board(j,height - i - 1));
|
||
|
showPiece(); // Remember to update piece correctly!!!!
|
||
|
}
|
||
|
|
||
|
|
||
|
void GenericTetrix::fillRandom(int line)
|
||
|
{
|
||
|
int i,j;
|
||
|
int holes;
|
||
|
|
||
|
for(i = 0 ; i < width ; i++)
|
||
|
board(i,line) = TetrixPiece::randomValue(7);
|
||
|
holes = 0;
|
||
|
for(i = 0 ; i < width ; i++)
|
||
|
if (board(i,line) == 0) // Count holes in the line.
|
||
|
holes++;
|
||
|
if (holes == 0) // Full line, make a random hole:
|
||
|
board(TetrixPiece::randomValue(width),line) = 0;
|
||
|
if (holes == width) // Empty line, make a random square:
|
||
|
board(TetrixPiece::randomValue(width),line) =
|
||
|
TetrixPiece::randomValue(6) + 1;
|
||
|
for(j = 0 ; j < width ; j++)
|
||
|
draw(j,i,board(j,i));
|
||
|
}
|
||
|
|
||
|
void GenericTetrix::moveLeft(int steps)
|
||
|
{
|
||
|
while(steps) {
|
||
|
if (!canMoveTo(currentPos - 1,currentLine))
|
||
|
return;
|
||
|
moveTo(currentPos - 1,currentLine);
|
||
|
steps--;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void GenericTetrix::moveRight(int steps)
|
||
|
{
|
||
|
while(steps) {
|
||
|
if (!canMoveTo(currentPos + 1,currentLine))
|
||
|
return;
|
||
|
moveTo(currentPos + 1,currentLine);
|
||
|
steps--;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void GenericTetrix::rotateLeft()
|
||
|
{
|
||
|
TetrixPiece tmp(currentPiece);
|
||
|
|
||
|
tmp.rotateLeft();
|
||
|
if (!canPosition(tmp))
|
||
|
return;
|
||
|
position(tmp);
|
||
|
currentPiece = tmp;
|
||
|
}
|
||
|
|
||
|
void GenericTetrix::rotateRight()
|
||
|
{
|
||
|
TetrixPiece tmp(currentPiece);
|
||
|
|
||
|
tmp.rotateRight();
|
||
|
if (!canPosition(tmp))
|
||
|
return;
|
||
|
position(tmp);
|
||
|
currentPiece = tmp;
|
||
|
}
|
||
|
|
||
|
void GenericTetrix::dropDown()
|
||
|
{
|
||
|
if (currentLine == -1)
|
||
|
return;
|
||
|
|
||
|
int dropHeight = 0;
|
||
|
int newLine = currentLine;
|
||
|
while(newLine) {
|
||
|
if (!canMoveTo(currentPos,newLine - 1))
|
||
|
break;
|
||
|
newLine--;
|
||
|
dropHeight++;
|
||
|
}
|
||
|
if (dropHeight != 0)
|
||
|
moveTo(currentPos,newLine);
|
||
|
internalPieceDropped(dropHeight);
|
||
|
}
|
||
|
|
||
|
void GenericTetrix::oneLineDown()
|
||
|
{
|
||
|
if (currentLine == -1)
|
||
|
return;
|
||
|
if (canMoveTo(currentPos,currentLine - 1)) {
|
||
|
moveTo(currentPos,currentLine - 1);
|
||
|
} else {
|
||
|
internalPieceDropped(0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void GenericTetrix::newPiece()
|
||
|
{
|
||
|
currentPiece = nextPiece;
|
||
|
if (showNext)
|
||
|
eraseNextPiece();
|
||
|
nextPiece.setRandomType();
|
||
|
if (showNext)
|
||
|
showNextPiece();
|
||
|
currentLine = height - 1 + currentPiece.getMinY();
|
||
|
currentPos = width/2 + 1;
|
||
|
if (!canMoveTo(currentPos,currentLine)) {
|
||
|
currentLine = -1;
|
||
|
gameOver();
|
||
|
} else {
|
||
|
showPiece();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void GenericTetrix::removePiece()
|
||
|
{
|
||
|
erasePiece();
|
||
|
currentLine = -1;
|
||
|
}
|
||
|
|
||
|
void GenericTetrix::drawNextSquare(int,int,int)
|
||
|
{
|
||
|
|
||
|
}
|
||
|
|
||
|
void GenericTetrix::pieceDropped(int)
|
||
|
{
|
||
|
newPiece();
|
||
|
}
|
||
|
|
||
|
void GenericTetrix::updateRemoved(int)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void GenericTetrix::updateScore(int)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void GenericTetrix::updateLevel(int)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void GenericTetrix::removeFullLines()
|
||
|
{
|
||
|
int i,j,k;
|
||
|
int nFullLines;
|
||
|
|
||
|
for(i = 0 ; i < height - nClearLines ; i++) {
|
||
|
for(j = 0 ; j < width ; j++)
|
||
|
if (board(j,i) == 0)
|
||
|
break;
|
||
|
if (j == width) {
|
||
|
nFullLines = 1;
|
||
|
for(k = i + 1 ; k < height - nClearLines ; k++) {
|
||
|
for(j = 0 ; j < width ; j++)
|
||
|
if (board(j,k) == 0)
|
||
|
break;
|
||
|
if (j == width) {
|
||
|
nFullLines++;
|
||
|
} else {
|
||
|
for(j = 0 ; j < width ; j++) {
|
||
|
if (board(j,k - nFullLines) != board(j,k)) {
|
||
|
board(j,k - nFullLines) = board(j,k);
|
||
|
draw( j,k - nFullLines,
|
||
|
board(j,k - nFullLines));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
nClearLines = nClearLines + nFullLines;
|
||
|
nLinesRemoved = nLinesRemoved + nFullLines;
|
||
|
updateRemoved(nLinesRemoved);
|
||
|
score = score + 10*nFullLines; // updateScore must be
|
||
|
// called by caller!
|
||
|
for (i = height - nClearLines ;
|
||
|
i < height - nClearLines + nFullLines ;
|
||
|
i++)
|
||
|
for(j = 0 ; j < width ; j++)
|
||
|
if (board(j,i) != 0) {
|
||
|
draw(j,i,0);
|
||
|
board(j,i) = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void GenericTetrix::showPiece()
|
||
|
{
|
||
|
int x,y;
|
||
|
|
||
|
if (currentLine == -1)
|
||
|
return;
|
||
|
|
||
|
for(int i = 0 ; i < 4 ; i++) {
|
||
|
currentPiece.getCoord(i,x,y);
|
||
|
draw(currentPos + x,currentLine - y,currentPiece.getType());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void GenericTetrix::erasePiece()
|
||
|
{
|
||
|
int x,y;
|
||
|
|
||
|
if (currentLine == -1)
|
||
|
return;
|
||
|
|
||
|
for(int i = 0 ; i < 4 ; i++) {
|
||
|
currentPiece.getCoord(i,x,y);
|
||
|
draw(currentPos + x,currentLine - y,0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void GenericTetrix::internalPieceDropped(int dropHeight)
|
||
|
{
|
||
|
gluePiece();
|
||
|
nPiecesDropped++;
|
||
|
if (nPiecesDropped % 25 == 0) {
|
||
|
level++;
|
||
|
updateLevel(level);
|
||
|
}
|
||
|
score = score + 7 + dropHeight;
|
||
|
removeFullLines();
|
||
|
updateScore(score);
|
||
|
pieceDropped(dropHeight);
|
||
|
}
|
||
|
|
||
|
void GenericTetrix::gluePiece()
|
||
|
{
|
||
|
int x,y;
|
||
|
int min;
|
||
|
|
||
|
if (currentLine == -1)
|
||
|
return;
|
||
|
|
||
|
for(int i = 0 ; i < 4 ; i++) {
|
||
|
currentPiece.getCoord(i,x,y);
|
||
|
board(currentPos + x,currentLine - y) = currentPiece.getType();
|
||
|
}
|
||
|
min = currentPiece.getMinY();
|
||
|
if (currentLine - min >= height - nClearLines)
|
||
|
nClearLines = height - currentLine + min - 1;
|
||
|
}
|
||
|
|
||
|
void GenericTetrix::showNextPiece(int erase)
|
||
|
{
|
||
|
int x,y;
|
||
|
int minX = nextPiece.getMinX();
|
||
|
int minY = nextPiece.getMinY();
|
||
|
int maxX = nextPiece.getMaxX();
|
||
|
int maxY = nextPiece.getMaxY();
|
||
|
|
||
|
int xOffset = (3 - (maxX - minX))/2;
|
||
|
int yOffset = (3 - (maxY - minY))/2;
|
||
|
|
||
|
for(int i = 0 ; i < 4 ; i++) {
|
||
|
nextPiece.getCoord(i,x,y);
|
||
|
if (erase)
|
||
|
drawNextSquare(x + xOffset - minX,
|
||
|
y + yOffset - minY,0);
|
||
|
else
|
||
|
drawNextSquare(x + xOffset - minX,
|
||
|
y + yOffset - minY,nextPiece.getType());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int GenericTetrix::canPosition(TetrixPiece &piece)
|
||
|
{
|
||
|
if (currentLine == -1)
|
||
|
return 0;
|
||
|
|
||
|
int x,y;
|
||
|
|
||
|
for(int i = 0 ; i < 4 ; i++) {
|
||
|
piece.getCoord(i,x,y);
|
||
|
x = currentPos + x;
|
||
|
y = currentLine - y; // Board and pieces have inverted y-coord. systems.
|
||
|
if (x < 0 || x >= width || y < 0 || y >= height)
|
||
|
return 0; // Outside board, cannot put piece here.
|
||
|
if (board(x,y) != 0)
|
||
|
return 0; // Over a non-zero square, cannot put piece here.
|
||
|
}
|
||
|
return 1; // Inside board and no non-zero squares underneath.
|
||
|
|
||
|
}
|
||
|
|
||
|
int GenericTetrix::canMoveTo(int xPosition,int line)
|
||
|
{
|
||
|
if (currentLine == -1)
|
||
|
return 0;
|
||
|
|
||
|
int x,y;
|
||
|
|
||
|
for(int i = 0 ; i < 4 ; i++) {
|
||
|
currentPiece.getCoord(i,x,y);
|
||
|
x = xPosition + x;
|
||
|
y = line - y; // Board and pieces have inverted y-coord. systems.
|
||
|
if (x < 0 || x >= width || y < 0 || y >= height)
|
||
|
return 0; // Outside board, cannot put piece here.
|
||
|
if (board(x,y) != 0)
|
||
|
return 0; // Over a non-zero square, cannot put piece here.
|
||
|
}
|
||
|
return 1; // Inside board and no non-zero squares underneath.
|
||
|
}
|
||
|
|
||
|
void GenericTetrix::moveTo(int xPosition,int line)
|
||
|
{
|
||
|
if (currentLine == -1)
|
||
|
return;
|
||
|
optimizedMove(xPosition,line,currentPiece);
|
||
|
currentPos = xPosition;
|
||
|
currentLine = line;
|
||
|
}
|
||
|
|
||
|
void GenericTetrix::position(TetrixPiece &piece)
|
||
|
{
|
||
|
if (currentLine == -1)
|
||
|
return;
|
||
|
|
||
|
optimizedMove(currentPos,currentLine,piece);
|
||
|
}
|
||
|
|
||
|
void GenericTetrix::optimizedMove(int newPos, int newLine,
|
||
|
TetrixPiece &newPiece)
|
||
|
{
|
||
|
int updates [8][3];
|
||
|
int nUpdates;
|
||
|
int value;
|
||
|
int x,y;
|
||
|
int i,j;
|
||
|
|
||
|
for(i = 0 ; i < 4 ; i++) { // Put the erasing coords into updates
|
||
|
currentPiece.getCoord(i,x,y);
|
||
|
updates[i][0] = currentPos + x;
|
||
|
updates[i][1] = currentLine - y;
|
||
|
updates[i][2] = 0;
|
||
|
}
|
||
|
nUpdates = 4;
|
||
|
for(i = 0 ; i < 4 ; i++) { // Any drawing coord same as an erasing one?
|
||
|
newPiece.getCoord(i,x,y);
|
||
|
x = newPos + x;
|
||
|
y = newLine - y;
|
||
|
for (j = 0 ; j < 4 ; j++)
|
||
|
if (updates[j][0] == x && updates[j][1] == y) { // Same coord,
|
||
|
// don't have to erase
|
||
|
if (currentPiece.getType() == newPiece.getType())
|
||
|
updates[j][2] = -1; // Correct on screen, no update!
|
||
|
else
|
||
|
updates[j][2] = newPiece.getType();
|
||
|
break;
|
||
|
}
|
||
|
if (j == 4) { // This coord does not overlap an erasing one
|
||
|
updates[nUpdates][0] = x;
|
||
|
updates[nUpdates][1] = y;
|
||
|
updates[nUpdates][2] = newPiece.getType();
|
||
|
nUpdates++;
|
||
|
}
|
||
|
}
|
||
|
for (i = 0 ; i < nUpdates ; i++) { // Do the updating
|
||
|
x = updates[i][0];
|
||
|
y = updates[i][1];
|
||
|
value = updates[i][2];
|
||
|
if (value != -1) // Only update if new value != current
|
||
|
draw(x,y,value);
|
||
|
}
|
||
|
}
|