File indexing completed on 2024-04-21 04:02:14

0001 /*
0002     This file is part of the KDE games kwin4 program
0003     SPDX-FileCopyrightText: 2006 Martin Heni <kde@heni-online.de>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "displaygame.h"
0009 
0010 // own
0011 #include "kwin4doc.h"
0012 #include "kwin4view.h"
0013 #include "piecesprite.h"
0014 #include "pixmapsprite.h"
0015 #include "reflectiongraphicsscene.h"
0016 #include "scoresprite.h"
0017 #include "spritenotify.h"
0018 // KF
0019 #include "kfourinline_debug.h"
0020 // Qt
0021 #include <QColor>
0022 #include <QPixmap>
0023 #include <QPoint>
0024 // Std
0025 #include <cassert>
0026 #include <cmath>
0027 
0028 // Constructor for the display
0029 DisplayGame::DisplayGame(ReflectionGraphicsScene *scene, ThemeManager *theme, QGraphicsView *parent)
0030     : Themeable(QStringLiteral("gamedisplay"), theme)
0031     , QObject(parent)
0032 {
0033     // Store arguments as attributes
0034     mScene = scene;
0035     mView = parent;
0036     mTheme = theme;
0037 
0038     // Choose a background color
0039     scene->setBackgroundBrush(QColor(0, 0, 128));
0040 
0041     // Clear storage of all sprites
0042     mSprites.clear();
0043 
0044     // Create piece sprites
0045     for (int i = 0; i < 42; i++) {
0046         PieceSprite *sprite = new PieceSprite(QStringLiteral("piece"), mTheme, i, mScene);
0047         if (!sprite)
0048             qCCritical(KFOURINLINE_LOG) << "Cannot load sprite"
0049                                         << "piece";
0050         mSprites.append(sprite);
0051         mPieces.append(sprite);
0052         sprite->hide();
0053     }
0054 
0055     // Create stars
0056     for (int i = 0; i < 4; i++) {
0057         PixmapSprite *sprite = new PixmapSprite(QStringLiteral("star"), mTheme, i, mScene);
0058         if (!sprite)
0059             qCCritical(KFOURINLINE_LOG) << "Cannot load sprite"
0060                                         << "star";
0061         mSprites.append(sprite);
0062         mStars.append(sprite);
0063         sprite->hide();
0064     }
0065 
0066     // Create board
0067     mBoard = new PixmapSprite(QStringLiteral("board"), mTheme, 0, mScene);
0068     if (!mBoard)
0069         qCCritical(KFOURINLINE_LOG) << "Cannot load sprite"
0070                                     << "board";
0071     mSprites.append(mBoard);
0072     mBoard->hide();
0073 
0074     // Create hint
0075     mHint = new PixmapSprite(QStringLiteral("hint"), mTheme, 0, mScene);
0076     if (!mHint)
0077         qCCritical(KFOURINLINE_LOG) << "Cannot load sprite"
0078                                     << "hint";
0079     mSprites.append(mHint);
0080     mHint->hide();
0081 
0082     // Create Game Over
0083     // mGameOver = new PixmapSprite("gameover", mTheme, 0, mScene);
0084     // if (!mGameOver) qCCritical(KFOURINLINE_LOG) << "Cannot load sprite" << "gameover";
0085     // mSprites.append(mGameOver);
0086     // mGameOver->hide();
0087 
0088     // Create score board
0089     mScoreBoard = new ScoreSprite(QStringLiteral("scoreboard"), mTheme, 0, mScene);
0090     if (!mScoreBoard)
0091         qCCritical(KFOURINLINE_LOG) << "Cannot load sprite"
0092                                     << "scoreboard";
0093     mSprites.append(mScoreBoard);
0094     mScoreBoard->hide();
0095 
0096     // Create movement indication arrows
0097     for (int i = 0; i < 7; i++) {
0098         PixmapSprite *arrow = new PixmapSprite(QStringLiteral("arrow"), mTheme, i, mScene);
0099         if (!arrow)
0100             qCCritical(KFOURINLINE_LOG) << "Cannot load sprite"
0101                                         << "arrow";
0102         mSprites.append(arrow);
0103         mArrows.append(arrow);
0104         arrow->hide();
0105     }
0106 
0107     // Static decoration
0108     KConfigGroup config = thememanager()->config(id());
0109     QStringList deco = config.readEntry("decoration", QStringList());
0110     for (int i = 0; i < deco.size(); i++) {
0111         PixmapSprite *sprite = new PixmapSprite(deco.at(i), mTheme, i, mScene);
0112         if (!sprite)
0113             qCCritical(KFOURINLINE_LOG) << "Cannot load sprite" << deco.at(i);
0114         mSprites.append(sprite);
0115         sprite->show();
0116     }
0117 
0118     // Animation timer
0119     mTimer = new QTimer(this);
0120     connect(mTimer, &QTimer::timeout, this, &DisplayGame::advance);
0121 
0122     // Redraw
0123     if (theme)
0124         theme->updateTheme(this);
0125 }
0126 
0127 // Destructor: clean up
0128 DisplayGame::~DisplayGame()
0129 {
0130     delete mTimer;
0131     qDeleteAll(mSprites);
0132 }
0133 
0134 // Called by thememanager when theme or theme geometry changes. Redraw and resize
0135 // this display.
0136 void DisplayGame::changeTheme()
0137 {
0138     // Retrieve theme data
0139     KConfigGroup config = thememanager()->config(id());
0140     QPointF board_pos = config.readEntry("board-pos", QPointF(1.0, 1.0));
0141     QPointF arrow_pos = config.readEntry("arrow-pos", QPointF(1.0, 1.0));
0142     QPointF board_spread = config.readEntry("board-spread", QPointF(1.0, 1.0));
0143 
0144     // Retrieve background pixmap
0145     QString bgsvgid = config.readEntry("background-svgid");
0146     QPixmap pixmap = thememanager()->getPixmap(bgsvgid, mScene->sceneRect().size().toSize());
0147     mScene->setBackgroundBrush(pixmap);
0148     QRectF pos = mBoard->sceneBoundingRect();
0149 
0150     // Move movement arrows
0151     for (int i = 0; i < 7; i++) {
0152         PixmapSprite *arrow = mArrows.value(i);
0153         QPointF to = QPointF(board_spread.x() * i + arrow_pos.x(), board_spread.y() * 0 + arrow_pos.y());
0154         arrow->setPosition(to);
0155     }
0156 
0157     // Move piece sprites
0158     for (int i = 0; i < 42; i++) {
0159         PieceSprite *piece = mPieces.value(i);
0160         int x = piece->logicalPos().x();
0161         int y = piece->logicalPos().y();
0162         QPointF to = QPointF(board_spread.x() * x + board_pos.x(), board_spread.y() * y + board_pos.y());
0163         piece->setPosition(to);
0164     }
0165 
0166     // Move hint
0167     {
0168         int x = mHint->logicalPos().x();
0169         int y = mHint->logicalPos().y();
0170         QPointF to = QPointF(board_spread.x() * x + board_pos.x(), board_spread.y() * y + board_pos.y());
0171         mHint->setPosition(to);
0172     }
0173 
0174     // Move stars
0175     for (int i = 0; i < 4; i++) {
0176         PixmapSprite *star = mStars.value(i);
0177         int x = star->logicalPos().x();
0178         int y = star->logicalPos().y();
0179         QPointF to = QPointF(board_spread.x() * x + board_pos.x(), board_spread.y() * y + board_pos.y());
0180         star->setPosition(to);
0181     }
0182 
0183     // Check whether the theme uses reflection handling
0184     if (config.readEntry("use-reflection", false)) {
0185         dynamic_cast<KWin4View *>(mView)->setReflection((int)pos.x(),
0186                                                         (int)(pos.y() + pos.height()),
0187                                                         (int)(mScene->sceneRect().width() - pos.x()),
0188                                                         (int)(pos.height() * 0.3));
0189     } else {
0190         // Zero width disables the reflections
0191         dynamic_cast<KWin4View *>(mView)->setReflection(0, 0, 0, 0);
0192     }
0193 
0194     mView->update();
0195 }
0196 
0197 // Start a new game. Initialize graphics.
0198 void DisplayGame::start()
0199 {
0200     // Run timer (unused)
0201     // mTimer->setSingleShot(true);
0202     // mTimer->start(0);
0203 
0204     // Show decoration
0205     mBoard->show();
0206     mScoreBoard->show();
0207 
0208     // Show movement arrows
0209     for (int i = 0; i < 7; i++) {
0210         mArrows.value(i)->setFrame(0);
0211         mArrows.value(i)->show();
0212     }
0213 
0214     // Hide piece sprites
0215     for (int i = 0; i < 42; i++) {
0216         mPieces.value(i)->hide();
0217     }
0218 
0219     // Hide stars
0220     for (int i = 0; i < 4; i++) {
0221         mStars.value(i)->setAnimation(false);
0222         mStars.value(i)->hide();
0223     }
0224 }
0225 
0226 // Run game animation
0227 void DisplayGame::advance()
0228 {
0229     // Currently unused
0230 }
0231 
0232 // Display end game sprite
0233 void DisplayGame::displayEnd()
0234 {
0235     // assert(mGameOver != 0);
0236     // mGameOver->show();
0237 }
0238 
0239 // Set the movement indicator arrows above the game board
0240 void DisplayGame::displayArrow(int x, int color)
0241 {
0242     // Set all arrows back to frame 0
0243     for (int i = 0; i < 7; i++) {
0244         mArrows.value(i)->setFrame(0);
0245     }
0246 
0247     // Check for no color
0248     if (color == Nobody) {
0249         return;
0250     }
0251 
0252     // Make sure the frames are chosen properly
0253     if (color == Yellow)
0254         mArrows.value(x)->setFrame(1);
0255     else
0256         mArrows.value(x)->setFrame(2);
0257 }
0258 
0259 // Set a game HINT sprite
0260 void DisplayGame::displayHint(int x, int y, bool show)
0261 {
0262     // Invert height
0263     y = 5 - y;
0264 
0265     // Check for removal of the sprite
0266     if (!show) {
0267         mHint->hide();
0268         return;
0269     }
0270 
0271     // Retrieve theme data
0272     KConfigGroup config = thememanager()->config(id());
0273     QPointF board_pos = config.readEntry("board-pos", QPointF(1.0, 1.0));
0274     QPointF board_spread = config.readEntry("board-spread", QPointF(1.0, 1.0));
0275 
0276     QPointF to = QPointF(board_spread.x() * x + board_pos.x(), board_spread.y() * y + board_pos.y());
0277     mHint->setLogicalPos(QPoint(x, y));
0278     mHint->setPosition(to);
0279     mHint->show();
0280 }
0281 
0282 // Set a game piece, red or yellow or hidden depending on 'color'
0283 SpriteNotify *DisplayGame::displayPiece(int x, int y, int color, int no, bool animation)
0284 {
0285     // Invert height
0286     y = 5 - y;
0287 
0288     // Get piece
0289     PieceSprite *sprite = mPieces.value(no);
0290     assert(sprite != nullptr);
0291 
0292     // Check for removal of sprite
0293     if (color == Nobody) {
0294         sprite->hide();
0295         return nullptr;
0296     }
0297 
0298     // Retrieve theme data
0299     KConfigGroup config = thememanager()->config(id());
0300     QPointF board_pos = config.readEntry("board-pos", QPointF(1.0, 1.0));
0301     QPointF board_spread = config.readEntry("board-spread", QPointF(1.0, 1.0));
0302     double velocity = config.readEntry("move-velocity", 0.8);
0303 
0304     // Make sure the frames are ok
0305     int frame;
0306     if (color == Yellow)
0307         frame = 0;
0308     else
0309         frame = 1;
0310 
0311     // Just draw the sprites or show an movement animation?
0312     if (animation) {
0313         QPointF from = QPointF(board_spread.x() * x + board_pos.x(), board_spread.y() * (-1) + board_pos.y());
0314         QPointF to = QPointF(board_spread.x() * x + board_pos.x(), board_spread.y() * y + board_pos.y());
0315         sprite->setFrame(frame);
0316         sprite->startLinear(from, to, velocity);
0317         // For theme store (x,y) board coordinate
0318         sprite->setLogicalPos(QPoint(x, y));
0319     } else {
0320         QPointF to = QPointF(board_spread.x() * x + board_pos.x(), board_spread.y() * y + board_pos.y());
0321         sprite->setFrame(frame);
0322         sprite->setPosition(to);
0323         // For theme store (x,y) board coordinate
0324         sprite->setLogicalPos(QPoint(x, y));
0325     }
0326 
0327     sprite->show();
0328     return sprite->notify();
0329 }
0330 
0331 // Draw Star Sprites as winning indicator
0332 void DisplayGame::displayStar(int x, int y, int no)
0333 {
0334     // Invert height
0335     y = 5 - y;
0336     PixmapSprite *star = mStars.value(no - 1);
0337     assert(star != nullptr);
0338 
0339     // Retrieve theme data
0340     KConfigGroup config = thememanager()->config(id());
0341     QPointF board_pos = config.readEntry("board-pos", QPointF(1.0, 1.0));
0342     QPointF board_spread = config.readEntry("board-spread", QPointF(1.0, 1.0));
0343 
0344     QPointF pos = QPointF(board_spread.x() * x + board_pos.x(), board_spread.y() * y + board_pos.y());
0345     star->setAnimation(true);
0346     star->setLogicalPos(QPoint(x, y));
0347     star->setPosition(pos);
0348     star->show();
0349 }
0350 
0351 // Return the mouse mapped to the board or bar item so that a
0352 // move 0..6 is generated. -1 means an illegal position.
0353 int DisplayGame::mapMouseToMove(const QPoint &pos)
0354 {
0355     // Error?
0356     if (!mBoard)
0357         return -1;
0358 
0359     // Find which arrow the mouse is closest to. This way
0360     // all board scaling become irrelevant. An alternative
0361     // would be to calculate the position using board_pos and
0362     // board_spread.
0363     for (int i = 0; i < 7; i++) {
0364         PixmapSprite *arrow = mArrows.value(i);
0365         int width = int(arrow->boundingRect().width());
0366         int relPos = int(arrow->mapFromParent(QPointF(pos)).x());
0367         // Found matching arrow
0368         if (relPos > 0 && relPos <= width) {
0369             return i;
0370         }
0371     }
0372 
0373     // Nothing found
0374     return -1;
0375 }
0376 
0377 // Retrieve the score sprite.
0378 ScoreSprite *DisplayGame::score()
0379 {
0380     return mScoreBoard;
0381 }
0382 
0383 #include "moc_displaygame.cpp"