File indexing completed on 2023-10-03 10:41:41
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 // Create move icon 0039 mMoveSprites[0] = new PixmapSprite(QStringLiteral("moveicon0"), mTheme, mAdvancePeriod, 0, scene()); 0040 if (!mMoveSprites[0]) qCCritical(LSKAT_LOG) << "Cannot load sprite" << "moveicon"; 0041 mSprites.append(mMoveSprites[0]); 0042 0043 mMoveSprites[1] = new PixmapSprite(QStringLiteral("moveicon1"), mTheme, mAdvancePeriod, 1, scene()); 0044 if (!mMoveSprites[1]) qCCritical(LSKAT_LOG) << "Cannot load sprite" << "moveicon"; 0045 mSprites.append(mMoveSprites[1]); 0046 0047 // Create score board 0048 mScoreBoard[0] = new ScoreSprite(QStringLiteral("scoreboard0"), mTheme, mAdvancePeriod, 0, scene()); 0049 if (!mScoreBoard[0]) qCCritical(LSKAT_LOG) << "Cannot load sprite" << "scoreboard0"; 0050 mSprites.append(mScoreBoard[0]); 0051 0052 mScoreBoard[1] = new ScoreSprite(QStringLiteral("scoreboard1"), mTheme, mAdvancePeriod, 1, scene()); 0053 if (!mScoreBoard[1]) qCCritical(LSKAT_LOG) << "Cannot load sprite" << "scoreboard0"; 0054 mSprites.append(mScoreBoard[1]); 0055 0056 // Create card area 0057 mCardArea[0] = new PixmapSprite(QStringLiteral("cardarea0"), mTheme, mAdvancePeriod, 0, scene()); 0058 if (!mCardArea[0]) qCCritical(LSKAT_LOG) << "Cannot load sprite" << "cardarea0"; 0059 mSprites.append(mCardArea[0]); 0060 0061 mCardArea[1] = new PixmapSprite(QStringLiteral("cardarea1"), mTheme, mAdvancePeriod, 1, scene()); 0062 if (!mCardArea[1]) qCCritical(LSKAT_LOG) << "Cannot load sprite" << "cardarea1"; 0063 mSprites.append(mCardArea[1]); 0064 0065 // Create play area 0066 mPlayArea = new PixmapSprite(QStringLiteral("playarea"), mTheme, mAdvancePeriod, 0, scene()); 0067 if (!mPlayArea) qCCritical(LSKAT_LOG) << "Cannot load sprite" << "playarea"; 0068 mSprites.append(mPlayArea); 0069 0070 // Create text sprites 0071 mText[0] = new TextSprite(QStringLiteral("scoretext0"), mTheme, scene()); 0072 if (!mText[0]) qCCritical(LSKAT_LOG) << "Cannot load sprite" << "scoretext0"; 0073 mSprites.append(mText[0]); 0074 0075 mText[1] = new TextSprite(QStringLiteral("scoretext1"), mTheme, scene()); 0076 if (!mText[1]) qCCritical(LSKAT_LOG) << "Cannot load sprite" << "scoretext1"; 0077 mSprites.append(mText[1]); 0078 0079 mText[2] = new TextSprite(QStringLiteral("resulttext"), mTheme, scene()); 0080 if (!mText[2]) qCCritical(LSKAT_LOG) << "Cannot load sprite" << "resulttext"; 0081 mSprites.append(mText[2]); 0082 0083 // Redraw 0084 if (theme) theme->updateTheme(this); 0085 } 0086 0087 // Called by thememanager when theme or theme geometry changes. Redraw and 0088 // resize this display. 0089 void DisplayTwo::changeTheme() 0090 { 0091 // Retrieve theme data 0092 KConfigGroup config = thememanager()->config(id()); 0093 0094 // Retrieve background pixmap 0095 QString bgsvgid = config.readEntry("background-svgid"); 0096 QPixmap pixmap = thememanager()->getPixmap(bgsvgid, scene()->sceneRect().size().toSize()); 0097 scene()->setBackgroundBrush(pixmap); 0098 mView->update(); 0099 } 0100 0101 // Start display 0102 void DisplayTwo::start() 0103 { 0104 // Stop all card sprites 0105 for (int i = 0; i < mCards.size(); i++) 0106 { 0107 CardSprite *sprite = mCards[i]; 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"