From 9c0a36f61cebd4b6587c155d9c46761c5c877da7 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Fri, 9 Jun 2023 17:03:44 +0200 Subject: Improve login screen --- src/mainwindow.cpp | 2 + src/snake.cpp | 245 +++++++++++++++++++++++++++++++++++++++++------------ src/snake.h | 13 ++- 3 files changed, 203 insertions(+), 57 deletions(-) diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 27d99d1..0ed85b8 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -213,6 +213,7 @@ void MainWindow::mouseDoubleClickEvent(QMouseEvent *) if (m_Snake == nullptr) { if (clicks++ > 0) { m_Snake = new GameCore(this); + m_Clock->parentWidget()->hide(); } } else { m_Snake->addSnake(); @@ -224,6 +225,7 @@ void MainWindow::keyPressEvent(QKeyEvent *event) switch (event->key()) { case Qt::Key_Escape: if (m_Snake != nullptr) { + m_Clock->parentWidget()->show(); delete m_Snake; m_Snake = nullptr; } diff --git a/src/snake.cpp b/src/snake.cpp index 2ce1a0d..e855250 100644 --- a/src/snake.cpp +++ b/src/snake.cpp @@ -1,4 +1,4 @@ -#include "snake.h" +#include "snake.h" #include #include @@ -16,38 +16,57 @@ #define DIR_RIGHT 3 #define CELL_FREE 0 -#define CELL_WALL 1 +#define CELL_PADDLE_BACKING 1 #define CELL_SNAKE 2 #define CELL_FOOD 3 -#define CELL_BRICK 4 +#define CELL_BREAKOUT_BRICK 4 #define CELL_SNAKEBRICK 5 +#define CELL_PADDLE 6 #define AXIS_X 0 #define AXIS_Y 1 #define FIELD(x,y) _field[(x) + ((y) * _width)] +struct Paddle; + struct Cell { int type; QBrush color; - Cell(int t, QColor c) : type(t), color(QBrush(c)) {} + Paddle *player; + Cell(int t, QColor c) : type(t), color(QBrush(c)), player(nullptr) {} bool isFood() const { return type == CELL_FOOD; } - bool willKill() const { return type == CELL_WALL || type == CELL_SNAKE || type == CELL_SNAKEBRICK; } + bool willKill() const { return type == CELL_PADDLE_BACKING | type == CELL_PADDLE || type == CELL_SNAKE || type == CELL_SNAKEBRICK; } bool isFree() const { return type == CELL_FREE; } - bool isBrick() const { return type == CELL_BRICK || type == CELL_SNAKEBRICK; } - bool canSpawnFood() const { return type == CELL_SNAKE || type == CELL_FREE || type == CELL_BRICK; } + bool isPaddleFree() const { return type == CELL_FREE || type == CELL_SNAKEBRICK || type == CELL_BREAKOUT_BRICK || type == CELL_FOOD; } + bool isBrick() const { return type == CELL_BREAKOUT_BRICK || type == CELL_SNAKEBRICK; } + bool canSpawnFood() const { return type == CELL_SNAKE || type == CELL_FREE || type == CELL_BREAKOUT_BRICK; } bool ballWillDestroy() const { return isFood() || isBrick(); } }; +static const Cell empty(CELL_FREE, QColor()); +static const Cell snakebrick(CELL_SNAKEBRICK, QColor::fromRgb(200, 200, 200)); +static Cell breakoutCenterBrick(CELL_BREAKOUT_BRICK, QColor::fromRgb(255, 255, 255)); +static const Cell food[] = { + Cell(CELL_FOOD, QColor::fromRgb(0, 255, 0)), + Cell(CELL_FOOD, QColor::fromRgb(0, 200, 0)), + Cell(CELL_FOOD, QColor::fromRgb(0, 160, 0)), + Cell(CELL_FOOD, QColor::fromRgb(0, 120, 0)), +}; + +struct Paddle; + struct Ball { int x, y; int dx, dy; const Cell *cell; QPoint gridPos; + Paddle *lastPlayer; Ball(int startX, int startY) : x(startX), y(startY), dx(qrand() % 4 + 1), dy(qrand() % 4 + 1), - cell(new Cell(CELL_WALL, QColor::fromHsv(qrand() % 360, 32 + qrand() % 64, 32 + qrand() % 64))) {} + cell(new Cell(CELL_PADDLE_BACKING, QColor::fromHsv(qrand() % 360, 32 + qrand() % 64, 32 + qrand() % 64))), + lastPlayer(nullptr) {} virtual ~Ball() { delete cell; } }; @@ -56,9 +75,11 @@ struct Paddle int x, y; int axis; int size; - const Cell *cell; + Cell *cell; + Cell *dell; Paddle(int startX, int startY, GameCore *field, int ax) : x(startX), y(startY), axis(ax), size(5), - cell(new Cell(CELL_WALL, QColor::fromHsv(qrand() % 360, 192 + qrand() % 64, 192 + qrand() % 64))) { + cell(new Cell(CELL_PADDLE, QColor::fromHsv(qrand() % 360, 192 + qrand() % 64, 192 + qrand() % 64))), + dell(nullptr) { for (int i = 0; i < size; ++i) { if (axis == AXIS_X) { field->setField(x + i, y, cell); @@ -67,17 +88,35 @@ struct Paddle } } } -}; - -const Cell empty(CELL_FREE, QColor()); -const Cell wall(CELL_WALL, QColor::fromRgb(80, 80, 90)); -const Cell snakebrick(CELL_SNAKEBRICK, QColor::fromRgb(200, 200, 200)); -const Cell brick(CELL_BRICK, QColor::fromRgb(255, 255, 255)); -const Cell food[] = { - Cell(CELL_FOOD, QColor::fromRgb(0, 255, 0)), - Cell(CELL_FOOD, QColor::fromRgb(0, 200, 0)), - Cell(CELL_FOOD, QColor::fromRgb(0, 160, 0)), - Cell(CELL_FOOD, QColor::fromRgb(0, 120, 0)), + bool shrink(GameCore *field) { + if (size <= 0) + return false; + size--; + if (axis == AXIS_X) { + field->setField(x + size, y, &empty); + } else { + field->setField(x, y + size, &empty); + } + return true; + } + bool grow(GameCore *field) { + if (axis == AXIS_X) { + if (x < 0 || x + size >= field->width()) + return false; + if (field->field(x + size, y)->isFree()) { + field->setField(x + size, y, cell); + } + } else { + if (y < 0 || y + size >= field->height()) + return false; + if (field->field(x, y + size)->isFree()) { + field->setField(x, y + size, cell); + } + } + size++; + return true; + } + virtual ~Paddle() { delete cell; delete dell; } }; class Snake @@ -102,6 +141,7 @@ GameCore::GameCore(QWidget *widget) : _widget(widget), _deaths(0), _lastMeal(QDateTime::currentMSecsSinceEpoch()), + _lastPaddle(QDateTime::currentMSecsSinceEpoch()), _field(nullptr) { qsrand((uint)_lastMeal); @@ -109,27 +149,31 @@ GameCore::GameCore(QWidget *widget) _height = widget->height() / SCALING; if (_width <= 0 || _height <= 0) return; - initField(); + int cellCount = _width * _height; + _field = (const Cell**)calloc(cellCount, sizeof(*_field)); + for (int i = 0; i < cellCount; ++i) { + _field[i] = ∅ + } // Should be either 4 player pong or two player + tetris // Bricks in the center also only make sense without tetris _paddles.append(new Paddle(1, _height - 2, this, AXIS_X)); _paddles.append(new Paddle(1, 1, this, AXIS_X)); _paddles.append(new Paddle(_width - 2, 1, this, AXIS_Y)); _paddles.append(new Paddle(1, 1, this, AXIS_Y)); - for (int y = 15; y < _height - 16; y += 3) { - for (int x = 15; x < _width - 16; x += 3) { - FIELD(x, y) = &brick; - FIELD(x + 1, y) = &brick; - FIELD(x, y + 1) = &brick; - FIELD(x + 1, y + 1) = &brick; - } + // Assign players to cells + for (int i = 0; i < _paddles.size(); ++i) { + _paddles[i]->dell = new Cell(CELL_PADDLE_BACKING, _paddles[i]->cell->color.color().darker(250)); + _paddles[i]->cell->player = _paddles[i]; + _paddles[i]->dell->player = _paddles[i]; } + drawPaddleBorders(); + addBreakoutBlocks(); qDebug() << "Field:" << _width << _height; addFood(); _t = new QTimer(widget); _t->start(15); // GAME - QTimer::connect(_t, &QTimer::timeout, [this]() { + QTimer::connect(_t, &QTimer::timeout, [this, cellCount]() { // static int tick = 0; ++tick; @@ -205,7 +249,16 @@ GameCore::GameCore(QWidget *widget) setField(p.x(), p.y(), &snakebrick); } } - addFood(); + int cnt = 0; + for (int i = 0; i < cellCount; ++i) { + if (_field[i]->isFood()) { + if (++cnt > 5) + break; + } + } + if (cnt <= 5) { + addFood(); + } } else { snake->len += 5; } @@ -219,11 +272,17 @@ GameCore::GameCore(QWidget *widget) _lastMeal = QDateTime::currentMSecsSinceEpoch(); addFood(); } + // Spawn more balls? + if (_lastPaddle + 60000 < QDateTime::currentMSecsSinceEpoch()) { + qDebug() << "No ball action!"; + _lastPaddle = QDateTime::currentMSecsSinceEpoch(); + addBall(); + } } // // Balls Ball *bbottom = nullptr, *btop = nullptr, *bleft = nullptr, *bright = nullptr; - for ( Ball* ball : _balls) { + for (Ball* ball : _balls) { const QPoint old(ball->x / SCALING, ball->y / SCALING); int nx = ball->x + ball->dx; int ny = ball->y + ball->dy; @@ -242,9 +301,11 @@ GameCore::GameCore(QWidget *widget) ny = _height * SCALING - 1; ball->dy = -qAbs(ball->dy); } - // Collision + // Collision check const QPoint noo(nx / SCALING, ny / SCALING); - if (old != noo && !FIELD(noo.x(), noo.y())->isFree()) { + const Cell *newCell = FIELD(noo.x(), noo.y()); + if (old != noo && !newCell->isFree()) { + // Yes, new cell occupied, see what should happen bool one = false; if (FIELD(old.x(), noo.y()) == ball->cell || FIELD(old.x(), noo.y())->isFree()) { one = true; @@ -266,13 +327,27 @@ GameCore::GameCore(QWidget *widget) ball->dx = -ball->dx; ball->dy = -ball->dy; } + auto *p = newCell->player; + if (p != nullptr) { + // Collision with player-cell: either paddle, or its backing wall + if (newCell->type == CELL_PADDLE_BACKING) { + p->shrink(this); + ball->lastPlayer = nullptr; + checkPongGameOver(); + } else { + _lastPaddle = QDateTime::currentMSecsSinceEpoch(); + ball->lastPlayer = p; + } + } // Destroy cell? - if (FIELD(noo.x(), noo.y())->ballWillDestroy()) { - if (FIELD(noo.x(), noo.y())->isFood()) { - // TODO: Make paddle that last touched the ball bigger + if (newCell->ballWillDestroy()) { + if (newCell->isFood()) { + if (ball->lastPlayer != nullptr) { + ball->lastPlayer->grow(this); + } addFood(); } - if (FIELD(noo.x(), noo.y())->isBrick()) { + if (newCell->isBrick()) { for (int y = -1; y <= 1; ++y) { for (int x = -1; x <= 1; ++x) { if (FIELD(noo.x() + x, noo.y() + y)->isBrick()) { @@ -285,7 +360,7 @@ GameCore::GameCore(QWidget *widget) } } } else { - // Free movevemnt + // No collision, free movement ball->gridPos = noo; ball->x = nx; ball->y = ny; @@ -312,6 +387,8 @@ GameCore::GameCore(QWidget *widget) // Paddles if (bbottom != nullptr) { for (Paddle *paddle : _paddles) { + if (paddle->size <= 0) + continue; if (paddle->axis == AXIS_X) { bool left = false, right = false; Ball *check; @@ -331,7 +408,7 @@ GameCore::GameCore(QWidget *widget) right = true; } if (left) { - if (FIELD(paddle->x - 1, paddle->y)->isFree()) { + if (FIELD(paddle->x - 1, paddle->y)->isPaddleFree()) { paddle->x--; setField(paddle->x, paddle->y, paddle->cell); if (FIELD(paddle->x + paddle->size, paddle->y) == paddle->cell) { @@ -339,7 +416,7 @@ GameCore::GameCore(QWidget *widget) } } } else if (right) { - if (FIELD(paddle->x + paddle->size, paddle->y)->isFree()) { + if (FIELD(paddle->x + paddle->size, paddle->y)->isPaddleFree()) { setField(paddle->x + paddle->size, paddle->y, paddle->cell); if (FIELD(paddle->x, paddle->y) == paddle->cell) { setField(paddle->x, paddle->y, &empty); @@ -367,7 +444,7 @@ GameCore::GameCore(QWidget *widget) bottom = true; } if (top) { - if (FIELD(paddle->x, paddle->y - 1)->isFree()) { + if (FIELD(paddle->x, paddle->y - 1)->isPaddleFree()) { paddle->y--; setField(paddle->x, paddle->y, paddle->cell); if (FIELD(paddle->x, paddle->y + paddle->size) == paddle->cell) { @@ -375,7 +452,7 @@ GameCore::GameCore(QWidget *widget) } } } else if (bottom) { - if (FIELD(paddle->x, paddle->y + paddle->size)->isFree()) { + if (FIELD(paddle->x, paddle->y + paddle->size)->isPaddleFree()) { setField(paddle->x, paddle->y + paddle->size, paddle->cell); if (FIELD(paddle->x, paddle->y) == paddle->cell) { setField(paddle->x, paddle->y, &empty); @@ -391,6 +468,9 @@ GameCore::GameCore(QWidget *widget) GameCore::~GameCore() { + for (Paddle *p: _paddles) { + delete p; + } free(_field); if(_t->isActive()) { _t->stop(); @@ -408,7 +488,7 @@ void GameCore::scanDir(Snake *snake, int dx, int dy, const Cell* &what, int &dis y += dy; dist++; if (x < 0 || x >= _width || y < 0 || y >= _height) { - what = &wall; + what = &snakebrick; return; } if (FIELD(x, y)->willKill() || FIELD(x, y)->isFood()) { @@ -436,27 +516,80 @@ void GameCore::addSnake() } } -void GameCore::initField() +void GameCore::addBall() { - if (_field == nullptr) { - _field = (const Cell**)calloc(_width * _height, sizeof(*_field)); + for (int i = 0; i < 100; ++i) { + int x = qrand() % _width; + int y = qrand() % _height; + if (FIELD(x, y)->isFree()) { + qDebug() << "Adding Ball at" << x << y; + Ball *b = new Ball(x * SCALING, y * SCALING); + if (b == nullptr) { + qDebug() << "NULLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL"; + } + _balls.append(b); + break; + } } - for (int i = 0; i < _width; ++i) { - FIELD(i, 0) = &wall; - FIELD(i, _height - 1) = &wall; +} + +void GameCore::drawPaddleBorders() +{ + // 0.. = bottom, top, right, left + for (int i = 1; i < _width - 1; ++i) { + FIELD(i, 0) = _paddles[1]->dell; + FIELD(i, _height - 1) = _paddles[0]->dell; } - for (int i = 0; i < _height; ++i) { - FIELD(0, i) = &wall; - FIELD(_width - 1, i) = &wall; + for (int i = 1; i < _height - 1; ++i) { + FIELD(0, i) = _paddles[3]->dell; + FIELD(_width - 1, i) = _paddles[2]->dell; } - for (int y = 1; y < _height - 1; ++y) { - for (int x = 1; x < _width - 1; ++x) { - FIELD(x, y) = ∅ + _widget->update(); +} + +void GameCore::addBreakoutBlocks() +{ + for (int y = 15; y < _height - 16; y += 3) { + for (int x = 15; x < _width - 16; x += 3) { + if (FIELD(x, y)->isPaddleFree()) { + FIELD(x, y) = &breakoutCenterBrick; + } + if (FIELD(x + 1, y)->isPaddleFree()) { + FIELD(x + 1, y) = &breakoutCenterBrick; + } + if (FIELD(x, y + 1)->isPaddleFree()) { + FIELD(x, y + 1) = &breakoutCenterBrick; + } + if (FIELD(x + 1, y + 1)->isPaddleFree()) { + FIELD(x + 1, y + 1) = &breakoutCenterBrick; + } } } _widget->update(); } +void GameCore::checkPongGameOver() +{ + int cnt = 0; + Paddle *last = nullptr; + for (Paddle *p : _paddles) { + if (p->size > 0) { + cnt++; + last = p; + } + } + if (cnt == 1) { + // Winrar + breakoutCenterBrick.color = QBrush(last->cell->color.color().lighter(250)); + for (Paddle *p : _paddles) { + while (p->size < 5 && p->grow(this)) { + // Nothing + } + } + addBreakoutBlocks(); + } +} + void GameCore::pauseAndResume() { if(_t->isActive()) { diff --git a/src/snake.h b/src/snake.h index 3adc301..538a944 100644 --- a/src/snake.h +++ b/src/snake.h @@ -25,6 +25,7 @@ private: QList _paddles; int _deaths; qint64 _lastMeal; + qint64 _lastPaddle; const Cell **_field; public: @@ -33,6 +34,10 @@ public: } void setField(int x, int y, const Cell *val); + int width() const { return _width; } + + int height() const { return _height; } + GameCore(QWidget *widget); virtual ~GameCore(); @@ -45,7 +50,13 @@ public: void addSnake(); - void initField(); + void addBall(); + + void drawPaddleBorders(); + + void addBreakoutBlocks(); + + void checkPongGameOver(); void pauseAndResume(); }; -- cgit v1.2.3-55-g7522