/**************************************************************************** ** ** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. ** ** This file is part of an example program for TQt. This example ** program may be used, distributed and modified without limitation. ** *****************************************************************************/ #include "gtetrix.h" #include 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); } }