File indexing completed on 2024-04-21 04:05:23

0001 /*
0002     This file is part of the KDE games lskat 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 "display_two.h"
0009 
0010 // Qt includes
0011 #include <QPoint>
0012 #include <QTimer>
0013 
0014 // KF includes
0015 #include <KLocalizedString>
0016 #include <KConfigGroup>
0017 
0018 // Local includes
0019 #include "abstractinput.h"
0020 #include "cardsprite.h"
0021 #include "textsprite.h"
0022 #include "pixmapsprite.h"
0023 #include "scoresprite.h"
0024 
0025 // Display defines
0026 #define TIME_DELAY_SHUFFLE       100 /* Delay time in shuffling in [ms] */
0027 #define TIME_DELAY_AFTER_SHUFFLE 500 /* Extra delay after shuffling [ms] */
0028 
0029 // Constructor for the engine
0030 DisplayTwo::DisplayTwo(Deck *deck, QGraphicsScene *theScene, ThemeManager *theme,
0031                        int advancePeriod, QGraphicsView *parent)
0032           : Themable(QStringLiteral("display_two"), theme), AbstractDisplay(deck, theScene, theme, advancePeriod, parent)
0033 
0034 {
0035     // Choose a background color
0036     scene()->setBackgroundBrush(QColor(0, 0, 128));
0037 
0038     mSprites.reserve(10);
0039 
0040     // Create move icon
0041     mMoveSprites[0] = new PixmapSprite(QStringLiteral("moveicon0"), mTheme, mAdvancePeriod, 0, scene());
0042     if (!mMoveSprites[0]) qCCritical(LSKAT_LOG) << "Cannot load sprite" << "moveicon";
0043     mSprites.append(mMoveSprites[0]);
0044 
0045     mMoveSprites[1] = new PixmapSprite(QStringLiteral("moveicon1"), mTheme, mAdvancePeriod, 1, scene());
0046     if (!mMoveSprites[1]) qCCritical(LSKAT_LOG) << "Cannot load sprite" << "moveicon";
0047     mSprites.append(mMoveSprites[1]);
0048 
0049     // Create score board
0050     mScoreBoard[0] = new ScoreSprite(QStringLiteral("scoreboard0"), mTheme, mAdvancePeriod, 0, scene());
0051     if (!mScoreBoard[0]) qCCritical(LSKAT_LOG) << "Cannot load sprite" << "scoreboard0";
0052     mSprites.append(mScoreBoard[0]);
0053 
0054     mScoreBoard[1] = new ScoreSprite(QStringLiteral("scoreboard1"), mTheme, mAdvancePeriod, 1, scene());
0055     if (!mScoreBoard[1]) qCCritical(LSKAT_LOG) << "Cannot load sprite" << "scoreboard0";
0056     mSprites.append(mScoreBoard[1]);
0057 
0058     // Create card area
0059     mCardArea[0] = new PixmapSprite(QStringLiteral("cardarea0"), mTheme, mAdvancePeriod, 0, scene());
0060     if (!mCardArea[0]) qCCritical(LSKAT_LOG) << "Cannot load sprite" << "cardarea0";
0061     mSprites.append(mCardArea[0]);
0062 
0063     mCardArea[1] = new PixmapSprite(QStringLiteral("cardarea1"), mTheme, mAdvancePeriod, 1, scene());
0064     if (!mCardArea[1]) qCCritical(LSKAT_LOG) << "Cannot load sprite" << "cardarea1";
0065     mSprites.append(mCardArea[1]);
0066 
0067     // Create play area
0068     mPlayArea = new PixmapSprite(QStringLiteral("playarea"), mTheme, mAdvancePeriod, 0, scene());
0069     if (!mPlayArea) qCCritical(LSKAT_LOG) << "Cannot load sprite" << "playarea";
0070     mSprites.append(mPlayArea);
0071 
0072     // Create text sprites
0073     mText[0] = new TextSprite(QStringLiteral("scoretext0"), mTheme, scene());
0074     if (!mText[0]) qCCritical(LSKAT_LOG) << "Cannot load sprite" << "scoretext0";
0075     mSprites.append(mText[0]);
0076 
0077     mText[1] = new TextSprite(QStringLiteral("scoretext1"), mTheme, scene());
0078     if (!mText[1]) qCCritical(LSKAT_LOG) << "Cannot load sprite" << "scoretext1";
0079     mSprites.append(mText[1]);
0080 
0081     mText[2] = new TextSprite(QStringLiteral("resulttext"), mTheme, scene());
0082     if (!mText[2]) qCCritical(LSKAT_LOG) << "Cannot load sprite" << "resulttext";
0083     mSprites.append(mText[2]);
0084 
0085     // Redraw
0086     if (theme) theme->updateTheme(this);
0087 }
0088 
0089 // Called by thememanager when theme or theme geometry changes. Redraw and
0090 // resize this display.
0091 void DisplayTwo::changeTheme()
0092 {
0093     // Retrieve theme data
0094     KConfigGroup config = thememanager()->config(id());
0095 
0096     // Retrieve background pixmap
0097     QString bgsvgid = config.readEntry("background-svgid");
0098     QPixmap pixmap  = thememanager()->getPixmap(bgsvgid, scene()->sceneRect().size().toSize());
0099     scene()->setBackgroundBrush(pixmap);
0100     mView->update();
0101 }
0102 
0103 // Start display
0104 void DisplayTwo::start()
0105 {
0106     // Stop all card sprites
0107     for (CardSprite *sprite : std::as_const(mCards)) {
0108         sprite->stop();
0109     }
0110 
0111     // Hide or show sprites
0112     mMoveSprites[0]->hide();
0113     mMoveSprites[1]->hide();
0114     mScoreBoard[0]->show();
0115     mScoreBoard[1]->show();
0116     mCardArea[0]->show();
0117     mCardArea[1]->show();
0118     mPlayArea->show();
0119     mText[0]->hide();
0120     mText[1]->hide();
0121     mText[2]->hide();
0122 }
0123 
0124 // Connect a player with the score widget
0125 void DisplayTwo::updatePlayer(Player *player)
0126 {
0127     int id = player->id();
0128     mScoreBoard[id]->setPlayerName(player->name());
0129     mScoreBoard[id]->setPoints(player->points());
0130     mScoreBoard[id]->setScore(player->score());
0131     mScoreBoard[id]->setGames(player->wonGames(), player->games());
0132     mScoreBoard[id]->setInput(player->input()->type());
0133     mScoreBoard[id]->setTrump(player->trump());
0134 }
0135 
0136 // Init a player on a given screen/board position (0,1)
0137 void DisplayTwo::deal(Player *player, int position)
0138 {
0139     if (position != 0 && position != 1)
0140     {
0141         qCCritical(LSKAT_LOG) << "Wrong player position" << position;
0142         return;
0143     }
0144     if (!player)
0145     {
0146         qCCritical(LSKAT_LOG) << "No player given";
0147         return;
0148     }
0149 
0150     KConfigGroup config = thememanager()->config(id());
0151     QPointF deck_pos    = config.readEntry("deck-pos", QPointF(1.0, 1.0));
0152 
0153     // Start offset for display
0154     QPointF board_pos   = config.readEntry("board-pos1", QPointF(1.0, 1.0));
0155     QPointF board_sep   = config.readEntry("board-sep", QPointF(1.0, 1.0));
0156     QPointF board_shift = config.readEntry("board-shift", QPointF(1.0, 1.0));
0157     // Offset for second player
0158     if (position == 1)
0159     {
0160         board_pos  = config.readEntry("board-pos2", QPointF(1.0, 1.0));
0161     }
0162 
0163     // Two height level of cards
0164     for (int h = 0; h < 2; h++)
0165     {
0166         // Two rows of cards
0167         for (int y = 0; y < 2; y++)
0168         {
0169             // Four columns of cards
0170             for (int x = 0; x < 4; x++)
0171             {
0172                 // Get playerNumber on game board (0 1 2 3)
0173                 //                                (4 5 6 7)
0174                 int cardPos = x + 4 * y + 8 * h;
0175                 // Get card of player belonging to this playerNumber
0176                 int cardNo  = player->getCard(cardPos);
0177                 // Create sprite with card correct card image
0178                 CardSprite *sprite = mCards[cardNo];
0179                 // Move sprite to correct board playerNumber
0180                 QPointF pos = board_pos + QPointF(x * board_sep.x(), y * board_sep.y());
0181                 // Add shift for stacked cards
0182                 pos += h * board_shift;
0183                 sprite->setZValue(50 - 10 * h);
0184                 sprite->setPosition(deck_pos);
0185                 sprite->show();
0186                 double delay = position + 2 * x + 8 * y + 16 * (1 - h);
0187                 delay *= TIME_DELAY_SHUFFLE; // [ms]
0188                 // Move to the target position. The setPos is started with
0189                 // a little delay and depending on the last argument the
0190                 // backside or frontside is shown after the setPos
0191                 sprite->setShuffleMove(pos, delay, h == 0);
0192                 // Store sprite
0193             }// next x
0194         }// next y
0195     }// next h
0196 
0197     // Check dealing for only one player to avoid double timer events
0198     if (position == 1)
0199     {
0200         QTimer::singleShot(100, this, &DisplayTwo::checkShuffle);
0201     }
0202 }
0203 
0204 // Check whether all cardsprites are idle
0205 void DisplayTwo::checkShuffle()
0206 {
0207     bool idle = true;
0208     // Check whether the sprites are idle
0209     for (int i = 0; i < 32; i++)
0210     {
0211         CardSprite *sprite = mCards[i];
0212         if (!sprite->isIdle())
0213         {
0214             idle = false;
0215             break;
0216         }
0217     }
0218 
0219     // If sprites are not idle repeat check otherwise emit 'done' signal
0220     if (!idle)
0221     {
0222         QTimer::singleShot(100, this, &DisplayTwo::checkShuffle);
0223     }
0224     else
0225     {
0226         Q_EMIT dealingDone();
0227     }
0228 }
0229 
0230 // Convert a mouse coordinate back to card numbers
0231 void DisplayTwo::convertMousePress(const QPoint &mouse, int &playerNumber, int &cardNumber)
0232 {
0233     double scale = thememanager()->getScale();
0234     double x = mouse.x() / scale;
0235     double y = mouse.y() / scale;
0236 
0237     // Check play area 1
0238     KConfigGroup config0 = thememanager()->config(QStringLiteral("cardarea0"));
0239     QPointF pos0         = config0.readEntry("pos", QPointF(1.0, 1.0));
0240     double  width0       = config0.readEntry("width", 1.0);
0241     double  height0      = config0.readEntry("height", 1.0);
0242 
0243     double x0 = (x - pos0.x()) / width0;
0244     double y0 = (y - pos0.y()) / height0;
0245 
0246     // Check play area 2
0247     KConfigGroup config1 = thememanager()->config(QStringLiteral("cardarea1"));
0248     QPointF pos1         = config1.readEntry("pos", QPointF(1.0, 1.0));
0249     double  width1       = config1.readEntry("width", 1.0);
0250     double  height1      = config1.readEntry("height", 1.0);
0251 
0252     double x1 = (x - pos1.x()) / width1;
0253     double y1 = (y - pos1.y()) / height1;
0254 
0255     // Check in area 1
0256     if (x0 >= 0.0 && x0 < 1.0 && y0 >= 0.0 && y0 < 1.0)
0257     {
0258         int dx = int(x0 * 4.0);
0259         int dy = int(y0 * 2.0);
0260         playerNumber = 0;
0261         cardNumber = dx + 4 * dy;
0262         return;
0263     }
0264 
0265     // Check in area 2
0266     if (x1 >= 0.0 && x1 < 1.0 && y1 >= 0.0 && y1 < 1.0)
0267     {
0268         int dx = int(x1 * 4.0);
0269         int dy = int(y1 * 2.0);
0270         playerNumber = 1;
0271         cardNumber = dx + 4 * dy;
0272         return;
0273     }
0274 
0275     playerNumber = -1;
0276     return;
0277 }
0278 
0279 // Get x (0-3) y(0-1) board coordinates from card number (0-7)
0280 void DisplayTwo::calcXYFromNumber(int cardNumber, int &x, int &y)
0281 {
0282     x = cardNumber % 4;
0283     y = cardNumber / 4;
0284 }
0285 
0286 // Get a cardsprite given the card value
0287 CardSprite *DisplayTwo::getCardSprite(int cardValue)
0288 {
0289     CardSprite *sprite = mCards[cardValue];
0290     if (!sprite)
0291     {
0292         qCCritical(LSKAT_LOG) << "Could not find cardsprite for card value" << cardValue
0293                 << "Stored are" << mCards.size() << "sprites";
0294         return nullptr;
0295     }
0296     return sprite;
0297 }
0298 
0299 // Play a frontside card to the play area
0300 void DisplayTwo::play(int cardNumber, int playerNumber, int phase)
0301 {
0302     KConfigGroup config = thememanager()->config(id());
0303     QPointF play_pos_1     = config.readEntry("play-pos1", QPointF(1.0, 1.0));
0304     QPointF play_pos_2     = config.readEntry("play-pos2", QPointF(1.0, 1.0));
0305     double deal_move_time  = config.readEntry("deal-move-time", 1.0);
0306 
0307     CardSprite *sprite = getCardSprite(cardNumber);
0308     // Set z coordinate depending on setPos phase, that is latter cards will be
0309     // put on top
0310     sprite->setZValue(200 + 5 * phase);
0311     if (playerNumber == 0)
0312     {
0313         sprite->setMove(play_pos_1, deal_move_time);
0314     }
0315     else
0316     {
0317         sprite->setMove(play_pos_2, deal_move_time);
0318     }
0319 }
0320 
0321 // Turn a backside card to the front
0322 void DisplayTwo::turn(int cardNumber)
0323 {
0324     CardSprite *sprite = getCardSprite(cardNumber);
0325     sprite->setTurning(true);
0326 }
0327 
0328 // Remove the given card from the display.
0329 void DisplayTwo::remove(int winnerPosition, int cardNumber, int delta)
0330 {
0331     KConfigGroup config = thememanager()->config(id());
0332     QPointF stack_pos1  = config.readEntry("stack-pos1", QPointF(1.0, 1.0));
0333     QPointF stack_pos2  = config.readEntry("stack-pos2", QPointF(1.0, 1.0));
0334     QPointF stack_shift = config.readEntry("stack-shift", QPointF(1.0, 1.0));
0335     double remove_time  = config.readEntry("remove-time", 1.0);
0336     CardSprite *sprite  = getCardSprite(cardNumber);
0337     // Pile cards on top of each other
0338     sprite->setZValue(100 + delta);
0339     QPointF pos;
0340     if (winnerPosition == 0)
0341     {
0342         pos = QPointF(stack_pos1.x() + delta * stack_shift.x(),
0343                     stack_pos1.y() + delta * stack_shift.y());
0344     }
0345     else
0346     {
0347         pos = QPointF(stack_pos2.x() + delta * stack_shift.x(),
0348                     stack_pos2.y() + delta * stack_shift.y());
0349     }
0350     sprite->setRemove(pos, remove_time);
0351 }
0352 
0353 // Display a text on the game board.
0354 void DisplayTwo::showText(const QString &s)
0355 {
0356     mText[2]->setText(s);
0357     mText[2]->show();
0358 }
0359 
0360 // Display the score on the game board
0361 void DisplayTwo::showScore(int position, int score)
0362 {
0363     if (position < 0 || position > 1)
0364     {
0365         qCCritical(LSKAT_LOG) << "Wrong position (0,1) for showScore =" << position;
0366     }
0367     if (score == 0)
0368         mText[position]->setText(i18nc("Resulting score of a game with no point", "no point"));
0369     else
0370         mText[position]->setText(i18ncp("Resulting score of a game between 1 and 4", "%1 point", "%1 points", score));
0371     mText[position]->show();
0372 }
0373 
0374 // Show the setPos icon for the given player
0375 void DisplayTwo::showMove(int no)
0376 {
0377     QHashIterator<int, PixmapSprite *> it(mMoveSprites);
0378     while (it.hasNext())
0379     {
0380         it.next();
0381         PixmapSprite *sprite = it.value();
0382         sprite->hide();
0383     }
0384 
0385     if (no >= 0)
0386     {
0387         mMoveSprites[no]->show();
0388     }
0389 }
0390 
0391 #include "moc_display_two.cpp"