Qt 5.11 | Qt Widgets | Tetrix Example | tetrixboard.cpp Example File

Qt 5.11.3 Reference Documentation

# tetrixboard.cpp Example File

widgets/tetrix/tetrixboard.cpp

      /****************************************************************************
      **
      ** Copyright (C) 2016 The Qt Company Ltd.
      ** Contact: https://www.qt.io/licensing/
      **
      ** This file is part of the examples of the Qt Toolkit.
      **
      ** $QT_BEGIN_LICENSE:BSD$
      ** Commercial License Usage
      ** Licensees holding valid commercial Qt licenses may use this file in
      ** accordance with the commercial license agreement provided with the
      ** Software or, alternatively, in accordance with the terms contained in
      ** a written agreement between you and The Qt Company. For licensing terms
      ** and conditions see https://www.qt.io/terms-conditions. For further
      ** information use the contact form at https://www.qt.io/contact-us.
      **
      ** BSD License Usage
      ** Alternatively, you may use this file under the terms of the BSD license
      ** as follows:
      **
      ** "Redistribution and use in source and binary forms, with or without
      ** modification, are permitted provided that the following conditions are
      ** met:
      **   * Redistributions of source code must retain the above copyright
      **     notice, this list of conditions and the following disclaimer.
      **   * Redistributions in binary form must reproduce the above copyright
      **     notice, this list of conditions and the following disclaimer in
      **     the documentation and/or other materials provided with the
      **     distribution.
      **   * Neither the name of The Qt Company Ltd nor the names of its
      **     contributors may be used to endorse or promote products derived
      **     from this software without specific prior written permission.
      **
      **
      ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
      ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
      ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
      ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
      ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
      ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
      ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
      ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
      **
      ** $QT_END_LICENSE$
      **
      ****************************************************************************/

      #include <QtWidgets>

      #include "tetrixboard.h"

      TetrixBoard::TetrixBoard(QWidget *parent)
          : QFrame(parent)
      {
          setFrameStyle(QFrame::Panel | QFrame::Sunken);
          setFocusPolicy(Qt::StrongFocus);
          isStarted = false;
          isPaused = false;
          clearBoard();

          nextPiece.setRandomShape();
      }

      void TetrixBoard::setNextPieceLabel(QLabel *label)
      {
          nextPieceLabel = label;
      }

      QSize TetrixBoard::sizeHint() const
      {
          return QSize(BoardWidth * 15 + frameWidth() * 2,
                       BoardHeight * 15 + frameWidth() * 2);
      }

      QSize TetrixBoard::minimumSizeHint() const
      {
          return QSize(BoardWidth * 5 + frameWidth() * 2,
                       BoardHeight * 5 + frameWidth() * 2);
      }

      void TetrixBoard::start()
      {
          if (isPaused)
              return;

          isStarted = true;
          isWaitingAfterLine = false;
          numLinesRemoved = 0;
          numPiecesDropped = 0;
          score = 0;
          level = 1;
          clearBoard();

          emit linesRemovedChanged(numLinesRemoved);
          emit scoreChanged(score);
          emit levelChanged(level);

          newPiece();
          timer.start(timeoutTime(), this);
      }

      void TetrixBoard::pause()
      {
          if (!isStarted)
              return;

          isPaused = !isPaused;
          if (isPaused) {
              timer.stop();
          } else {
              timer.start(timeoutTime(), this);
          }
          update();
      }

      void TetrixBoard::paintEvent(QPaintEvent *event)
      {
          QFrame::paintEvent(event);

          QPainter painter(this);
          QRect rect = contentsRect();

          if (isPaused) {
              painter.drawText(rect, Qt::AlignCenter, tr("Pause"));
              return;
          }

          int boardTop = rect.bottom() - BoardHeight*squareHeight();

          for (int i = 0; i < BoardHeight; ++i) {
              for (int j = 0; j < BoardWidth; ++j) {
                  TetrixShape shape = shapeAt(j, BoardHeight - i - 1);
                  if (shape != NoShape)
                      drawSquare(painter, rect.left() + j * squareWidth(),
                                 boardTop + i * squareHeight(), shape);
              }
          }

          if (curPiece.shape() != NoShape) {
              for (int i = 0; i < 4; ++i) {
                  int x = curX + curPiece.x(i);
                  int y = curY - curPiece.y(i);
                  drawSquare(painter, rect.left() + x * squareWidth(),
                             boardTop + (BoardHeight - y - 1) * squareHeight(),
                             curPiece.shape());
              }
          }
      }

      void TetrixBoard::keyPressEvent(QKeyEvent *event)
      {
          if (!isStarted || isPaused || curPiece.shape() == NoShape) {
              QFrame::keyPressEvent(event);
              return;
          }

          switch (event->key()) {
          case Qt::Key_Left:
              tryMove(curPiece, curX - 1, curY);
              break;
          case Qt::Key_Right:
              tryMove(curPiece, curX + 1, curY);
              break;
          case Qt::Key_Down:
              tryMove(curPiece.rotatedRight(), curX, curY);
              break;
          case Qt::Key_Up:
              tryMove(curPiece.rotatedLeft(), curX, curY);
              break;
          case Qt::Key_Space:
              dropDown();
              break;
          case Qt::Key_D:
              oneLineDown();
              break;
          default:
              QFrame::keyPressEvent(event);
          }
      }

      void TetrixBoard::timerEvent(QTimerEvent *event)
      {
          if (event->timerId() == timer.timerId()) {
              if (isWaitingAfterLine) {
                  isWaitingAfterLine = false;
                  newPiece();
                  timer.start(timeoutTime(), this);
              } else {
                  oneLineDown();
              }
          } else {
              QFrame::timerEvent(event);
          }
      }

      void TetrixBoard::clearBoard()
      {
          for (int i = 0; i < BoardHeight * BoardWidth; ++i)
              board[i] = NoShape;
      }

      void TetrixBoard::dropDown()
      {
          int dropHeight = 0;
          int newY = curY;
          while (newY > 0) {
              if (!tryMove(curPiece, curX, newY - 1))
                  break;
              --newY;
              ++dropHeight;
          }
          pieceDropped(dropHeight);
      }

      void TetrixBoard::oneLineDown()
      {
          if (!tryMove(curPiece, curX, curY - 1))
              pieceDropped(0);
      }

      void TetrixBoard::pieceDropped(int dropHeight)
      {
          for (int i = 0; i < 4; ++i) {
              int x = curX + curPiece.x(i);
              int y = curY - curPiece.y(i);
              shapeAt(x, y) = curPiece.shape();
          }

          ++numPiecesDropped;
          if (numPiecesDropped % 25 == 0) {
              ++level;
              timer.start(timeoutTime(), this);
              emit levelChanged(level);
          }

          score += dropHeight + 7;
          emit scoreChanged(score);
          removeFullLines();

          if (!isWaitingAfterLine)
              newPiece();
      }

      void TetrixBoard::removeFullLines()
      {
          int numFullLines = 0;

          for (int i = BoardHeight - 1; i >= 0; --i) {
              bool lineIsFull = true;

              for (int j = 0; j < BoardWidth; ++j) {
                  if (shapeAt(j, i) == NoShape) {
                      lineIsFull = false;
                      break;
                  }
              }

              if (lineIsFull) {
                  ++numFullLines;
                  for (int k = i; k < BoardHeight - 1; ++k) {
                      for (int j = 0; j < BoardWidth; ++j)
                          shapeAt(j, k) = shapeAt(j, k + 1);
                  }
                  for (int j = 0; j < BoardWidth; ++j)
                      shapeAt(j, BoardHeight - 1) = NoShape;
              }
          }

          if (numFullLines > 0) {
              numLinesRemoved += numFullLines;
              score += 10 * numFullLines;
              emit linesRemovedChanged(numLinesRemoved);
              emit scoreChanged(score);

              timer.start(500, this);
              isWaitingAfterLine = true;
              curPiece.setShape(NoShape);
              update();
          }
      }

      void TetrixBoard::newPiece()
      {
          curPiece = nextPiece;
          nextPiece.setRandomShape();
          showNextPiece();
          curX = BoardWidth / 2 + 1;
          curY = BoardHeight - 1 + curPiece.minY();

          if (!tryMove(curPiece, curX, curY)) {
              curPiece.setShape(NoShape);
              timer.stop();
              isStarted = false;
          }
      }

      void TetrixBoard::showNextPiece()
      {
          if (!nextPieceLabel)
              return;

          int dx = nextPiece.maxX() - nextPiece.minX() + 1;
          int dy = nextPiece.maxY() - nextPiece.minY() + 1;

          QPixmap pixmap(dx * squareWidth(), dy * squareHeight());
          QPainter painter(&pixmap);
          painter.fillRect(pixmap.rect(), nextPieceLabel->palette().background());

          for (int i = 0; i < 4; ++i) {
              int x = nextPiece.x(i) - nextPiece.minX();
              int y = nextPiece.y(i) - nextPiece.minY();
              drawSquare(painter, x * squareWidth(), y * squareHeight(),
                         nextPiece.shape());
          }
          nextPieceLabel->setPixmap(pixmap);
      }

      bool TetrixBoard::tryMove(const TetrixPiece &newPiece, int newX, int newY)
      {
          for (int i = 0; i < 4; ++i) {
              int x = newX + newPiece.x(i);
              int y = newY - newPiece.y(i);
              if (x < 0 || x >= BoardWidth || y < 0 || y >= BoardHeight)
                  return false;
              if (shapeAt(x, y) != NoShape)
                  return false;
          }

          curPiece = newPiece;
          curX = newX;
          curY = newY;
          update();
          return true;
      }

      void TetrixBoard::drawSquare(QPainter &painter, int x, int y, TetrixShape shape)
      {
          static const QRgb colorTable[8] = {
              0x000000, 0xCC6666, 0x66CC66, 0x6666CC,
              0xCCCC66, 0xCC66CC, 0x66CCCC, 0xDAAA00
          };

          QColor color = colorTable[int(shape)];
          painter.fillRect(x + 1, y + 1, squareWidth() - 2, squareHeight() - 2,
                           color);

          painter.setPen(color.light());
          painter.drawLine(x, y + squareHeight() - 1, x, y);
          painter.drawLine(x, y, x + squareWidth() - 1, y);

          painter.setPen(color.dark());
          painter.drawLine(x + 1, y + squareHeight() - 1,
                           x + squareWidth() - 1, y + squareHeight() - 1);
          painter.drawLine(x + squareWidth() - 1, y + squareHeight() - 1,
                           x + squareWidth() - 1, y + 1);
      }