2 // gamewidget.cpp: Main game window widget
5 // (C) 2013 Underground Software
7 // JLH = James Hammons <jlhamm@acm.org>
10 // --- ---------- ------------------------------------------------------------
11 // JLH 03/01/2014 Created this file
14 #include "gamewidget.h"
15 #include <unistd.h> // for usleep()
16 #include "gameboard.h"
19 GameWidget::GameWidget(QWidget * parent/*= 0*/): QWidget(parent),
20 level(1), gameBoard(new GameBoard(level)),
21 animating(false), boxMoving(false)
25 // score->setTextFormat(Qt::PlainText);
26 // setFixedSize(580, 520);
27 // gameBoard = new GameBoard(1);
31 GameWidget::~GameWidget(void)
36 void GameWidget::paintEvent(QPaintEvent * /*event*/)
39 // Optimizations: Create a member variable that has a bitmap in it,
40 // tile it with the felt in OnCreate(), blit as background instead of
41 // FillRect(r, brWhite). Also need to keep track of client rect.
42 // Possibly have 2 bitmaps as member vars, so keep bm construction to a minimum...
44 QPainter painter(this);
45 // QRect rcUpdate = dc.m_ps.rcPaint; // Update rect...
46 // QRect rcUpdate = QRect(0, 0, 580, 520); // Update rect?
48 painter.translate(QPoint(offsetX, offsetY));
52 for(int y=0; y<gameBoard->height; y++)
54 for(int x=0; x<gameBoard->width; x++)
56 int tile = gameBoard->board[ptr++];
57 painter.setPen(QPen(Qt::black, 2.0, Qt::SolidLine));
58 painter.setBrush(QBrush(Qt::black));
59 int tileType = tile & (~GTBoxSpot);
61 if (tileType == GTWall)
63 painter.setBrush(QBrush(Qt::white));
64 painter.drawRect(x * maxLength, y * maxLength, maxLength, maxLength);
66 else if ((tileType == GTBox)
67 && (!(boxMoving && (movingBoxPositionX == x) && (movingBoxPositionY == y))))
69 painter.setBrush(QBrush(tile & GTBoxSpot ? Qt::green : Qt::red));
70 painter.drawRect(x * maxLength, y * maxLength, maxLength, maxLength);
73 else if (tile == GTBoxSpot)
75 painter.setBrush(QBrush(Qt::magenta));
76 // painter.drawRect((x * maxLength) + 20, (y * maxLength) + 20, maxLength - 40, maxLength - 40);
77 painter.drawRect((x * maxLength) + (int)(20.0/60.0*(float)maxLength), (y * maxLength) + (int)(20.0/60.0*(float)maxLength), maxLength - (int)(40.0/60.0*(float)maxLength), maxLength - (int)(40.0/60.0*(float)maxLength));
79 else if (tile == (GTBox | GTBoxSpot))
81 if (!(boxMoving && (movingBoxPositionX == x) && (movingBoxPositionY == y)))
83 painter.setBrush(QBrush(Qt::green));
84 painter.drawRect(x * maxLength, y * maxLength, maxLength, maxLength);
88 painter.setBrush(QBrush(Qt::magenta));
89 // painter.drawRect((x * maxLength) + 20, (y * maxLength) + 20, maxLength - 40, maxLength - 40);
90 painter.drawRect((x * maxLength) + (int)(20.0/60.0*(float)maxLength), (y * maxLength) + (int)(20.0/60.0*(float)maxLength), maxLength - (int)(40.0/60.0*(float)maxLength), maxLength - (int)(40.0/60.0*(float)maxLength));
94 else if ((tileType == GTSpace)
95 || ((tileType == GTBox) && (boxMoving && (movingBoxPositionX == x) && (movingBoxPositionY == y))))
97 painter.setBrush(QBrush(QPixmap(":/bg_marble_g.bmp")));
98 painter.drawRect(x * maxLength, y * maxLength, maxLength, maxLength);
100 if (tile & GTBoxSpot)
102 painter.setBrush(QBrush(Qt::magenta));
103 // painter.drawRect((x * maxLength) + 20, (y * maxLength) + 20, maxLength - 40, maxLength - 40);
104 painter.drawRect((x * maxLength) + (int)(20.0/60.0*(float)maxLength), (y * maxLength) + (int)(20.0/60.0*(float)maxLength), maxLength - (int)(40.0/60.0*(float)maxLength), maxLength - (int)(40.0/60.0*(float)maxLength));
108 if ((gameBoard->playerX == x) && (gameBoard->playerY == y) && !animating)
110 painter.setBrush(QBrush(Qt::yellow));
111 // painter.drawEllipse((x * maxLength) + 10, (y * maxLength) + 10, maxLength - 20, maxLength - 20);
112 painter.drawEllipse((x * maxLength) + (int)(10.0/60.0*(float)maxLength), (y * maxLength) + (int)(10.0/60.0*(float)maxLength), maxLength - (int)(20.0/60.0*(float)maxLength), maxLength - (int)(20.0/60.0*(float)maxLength));
118 painter.setBrush(QBrush(Qt::yellow));
119 // painter.drawEllipse(playerX + 10, playerY + 10, maxLength - 20, maxLength - 20);
120 painter.drawEllipse(playerX + (int)(10.0/60.0*(float)maxLength), playerY + (int)(10.0/60.0*(float)maxLength), maxLength - (int)(20.0/60.0*(float)maxLength), maxLength - (int)(20.0/60.0*(float)maxLength));
125 painter.setBrush(QBrush(Qt::red));
126 painter.drawRect(boxX, boxY, maxLength, maxLength);
132 void GameWidget::mousePressEvent(QMouseEvent * event)
134 if (event->button() == Qt::LeftButton)
141 void GameWidget::mouseMoveEvent(QMouseEvent * event)
143 if (event->buttons() & Qt::LeftButton)
150 void GameWidget::mouseReleaseEvent(QMouseEvent * event)
152 if (event->button() == Qt::LeftButton)
159 void GameWidget::mouseDoubleClickEvent(QMouseEvent * event)
161 if (event->button() == Qt::LeftButton)
168 void GameWidget::keyPressEvent(QKeyEvent * event)
170 int key = event->key();
171 float deltaX = 0, deltaY = 0;
172 float px = (float)(gameBoard->playerX * maxLength);
173 float py = (float)(gameBoard->playerY * maxLength);
174 float bx = px, by = py;
176 if (key == Qt::Key_Up)
178 boxMoving = gameBoard->IsBoxNOfPlayer();
179 int moveType = gameBoard->MovePlayerN();
181 if (moveType == PMInvalid)
186 else if (key == Qt::Key_Down)
188 boxMoving = gameBoard->IsBoxSOfPlayer();
190 if (gameBoard->MovePlayerS() == PMInvalid)
195 else if (key == Qt::Key_Left)
197 boxMoving = gameBoard->IsBoxWOfPlayer();
199 if (gameBoard->MovePlayerW() == PMInvalid)
204 else if (key == Qt::Key_Right)
206 boxMoving = gameBoard->IsBoxEOfPlayer();
208 if (gameBoard->MovePlayerE() == PMInvalid)
221 movingBoxPositionX = gameBoard->playerX + (int)deltaX;
222 movingBoxPositionY = gameBoard->playerY + (int)deltaY;
223 bx += deltaX * (float)maxLength;
224 by += deltaY * (float)maxLength;
228 float stepSize = (float)maxLength / (float)steps;
229 deltaX *= stepSize, deltaY *= stepSize;
231 for(int i=0; i<steps; i++)
245 animating = boxMoving = false;
247 // Only update if a key we recognize has been pressed!
250 if (gameBoard->GameHasBeenWon())
255 void GameWidget::keyReleaseEvent(QKeyEvent * /*event*/)
260 void GameWidget::resizeEvent(QResizeEvent * /*event*/)
262 // QSize s = event->size();
264 //printf("Size of window is: %i x %i\n", s.width(), s.height());
265 //printf("Size of game grid is: %i x %i\n", gameBoard->width, gameBoard->height);
267 // Find the constraints
268 float boxSizeX = s.width() / gameBoard->width;
269 float boxSizeY = s.height() / gameBoard->height;
271 maxLength = (int)(boxSizeX > boxSizeY ? boxSizeY : boxSizeX);
278 void GameWidget::CreateBackground(void)
281 char BGRes[27][64] = {
282 ":/res/grfttile.bmp",
284 ":/res/bg_tech_3.bmp",
285 ":/res/bg_tech_2.bmp",
286 ":/res/bg_tech_1.bmp",
287 ":/res/bg_weave_3.bmp",
288 ":/res/bg_weave_2.bmp",
289 ":/res/bg_clouds_2.bmp",
290 ":/res/bg_floor_plate.bmp",
291 ":/res/bg_marble_b.bmp",
292 ":/res/bg_marble_g.bmp",
293 ":/res/bg_marble_p.bmp",
294 ":/res/bg_marble_r.bmp",
295 ":/res/bg_marble_rb.bmp",
296 ":/res/bg_money_1.bmp",
297 ":/res/bg_pinstripe2.bmp",
298 ":/res/bg_pinstripe7.bmp",
299 ":/res/bg_raindrops_large.bmp",
300 ":/res/bg_raindrops_small.bmp",
301 ":/res/bg_stucco.bmp",
302 ":/res/bg_wood_w.bmp",
303 ":/res/bg_wood_b1.bmp",
304 ":/res/bg_wood_d.bmp",
305 ":/res/bg_wood_f.bmp",
306 ":/res/bg_wood_mh.bmp",
307 ":/res/bg_wood_mv.bmp",
308 ":/res/bg_wood_ro.bmp"
311 QPalette pal = palette();
312 pal.setBrush(backgroundRole(), QBrush(QPixmap(BGRes[m_nBackground])));
313 setAutoFillBackground(true);
316 return true; // Ignore errors for now...
318 // QPalette pal = palette();
319 // pal.setBrush(backgroundRole(), QBrush(QPixmap(":/bg_marble_g.bmp")));
320 // setAutoFillBackground(true);
326 void GameWidget::NextLevel(void)
330 gameBoard = new GameBoard(level);
336 void GameWidget::ResetLevel(void)
338 gameBoard->ResetGame();
343 void GameWidget::UndoLastMove(void)
347 float deltaX = 0, deltaY = 0;
348 float px = (float)(gameBoard->playerX * maxLength);
349 float py = (float)(gameBoard->playerY * maxLength);
350 float bx = px, by = py;
352 // Return if nothing to undo
353 if (!gameBoard->UndoLastMove(dx, dy, type))
367 // movingBoxPositionX = gameBoard->playerX + (int)deltaX;
368 // movingBoxPositionY = gameBoard->playerY + (int)deltaY;
369 movingBoxPositionX = gameBoard->playerX + dx;
370 movingBoxPositionY = gameBoard->playerY + dy;
371 // bx += deltaX * (float)maxLength;
372 // by += deltaY * (float)maxLength;
373 bx += (float)(dx * maxLength);
374 by += (float)(dy * maxLength);
378 float stepSize = (float)maxLength / (float)steps;
379 deltaX *= stepSize, deltaY *= stepSize;
381 for(int i=0; i<steps; i++)
395 animating = boxMoving = false;
401 void GameWidget::DrawBoard(QPainter * painter, QRect r)
403 // Use "r" as clipping rect--only draw what's necessary
405 // pDC->BitBlt(r.left, r.top, r.Width(), r.Height(), pDC2, r.left, r.top, SRCCOPY);
407 // Draw the solitaire board...
409 for(int i=0; i<7; i++)
411 int y = 126; //130-4;
413 for(int j=0; j<19; j++)
415 int card = solBoard[i][j];
417 if (card != -1 && !(hitStack && (i == stackStart.x()) && (j >= stackStart.y())))
419 int nLinesToDraw = 0; // Default--draws whole card
421 if (solBoard[i][j + 1] != -1 && !(hitStack && (i == stackStart.x()) && (j + 1 >= stackStart.y())))
425 if (myDeck.IsFaceUp(card))
429 int x = 10 + (i * (CARD_WIDTH + 8));//, y = 130 + (j * 4);
434 if (myDeck.IsFaceUp(solBoard[i][j - 1])) // Previous card...
438 //wil wok??? mebbe... YESH!!!!!!!!!!!!!
439 QRect cardRect(x, y, CARD_WIDTH, (nLinesToDraw ? nLinesToDraw : CARD_HEIGHT));
441 if (r.intersects(cardRect))
442 cdtDraw(painter, x, y, (myDeck.IsFaceUp(card) ? myDeck.GetCardAtPos(card)
443 : nCardBack), FACEUP, nLinesToDraw);
448 for(int i=0; i<4; i++)
450 if (nHitAce == i + 1)
452 if (((nAce[i] - 1) % 13) != 0)
453 cdtDraw(painter, 200 + ((CARD_WIDTH + 4) * i), 7, nAce[i] - 1, FACEUP);
455 cdtDraw(painter, 200 + ((CARD_WIDTH + 4) * i), 7, 0, INVISIBLEGHOST);
460 cdtDraw(painter, 200 + ((CARD_WIDTH + 4) * i), 7, nAce[i], FACEUP);
462 cdtDraw(painter, 200 + ((CARD_WIDTH + 4) * i), 7, 0, INVISIBLEGHOST);
467 cdtDraw(painter, 5, 7, nCardBack, FACEUP);
472 cdtDraw(painter, 5, 7, 0, DECKX);
474 cdtDraw(painter, 5, 7, 0, DECKO);
476 cdtDraw(painter, 5, 7, 0, (m_bShowX ? DECKX : DECKO));
484 for(int k=0; k<m_nNumToSplay; k++)
485 cdtDraw(painter, 5 + CARD_WIDTH + 6 + (k * 12), 7 + (k * 2),
486 myDeck.GetCardAtPos(stack2[stack2p - (m_nNumToSplay - k - 1)]), FACEUP);
492 if (m_nNumToSplay == 1)
493 cdtDraw(painter, 5 + CARD_WIDTH + 6, 7, myDeck.GetCardAtPos(stack2[stack2p - 1]), FACEUP);
495 for(int k=0; k<m_nNumToSplay-1; k++)
496 cdtDraw(painter, 5 + CARD_WIDTH + 6 + (k * 12), 7 + (k * 2),
497 myDeck.GetCardAtPos(stack2[stack2p - (m_nNumToSplay - k - 1)]), FACEUP);
504 void GameWidget::OnLButtonDown(QPoint point)
506 dcHitStack = dcHitDiscard = false; // For double clicking...
507 m_bTouchedGame = true; // Game has been attempted!
515 for(int i=0; i<7; i++)
516 for(int j=0; j<20; j++)
517 solBoardUndo[i][j] = solBoard[i][j];
519 for(int i=0; i<24; i++)
520 stack1Undo[i] = stack1[i], stack2Undo[i] = stack2[i];
522 for(int i=0; i<4; i++)
523 nAceUndo[i] = nAce[i];
525 stack1pUndo = stack1p, stack2pUndo = stack2p;
526 m_nScoreUndo = m_nScore;
527 m_nExhaustedDeckUndo = m_nExhaustedDeck;
528 m_nNumToSplayUndo = m_nNumToSplay;
529 m_bShowXUndo = m_bShowX;
532 // Check for a hit on the tableaux...
534 for(int i=0; i<7; i++)
537 r.setLeft(10 + (i * (CARD_WIDTH + 8)));
538 r.setRight(r.left() + CARD_WIDTH - 1);
542 for(int j=0; j<19; j++)
544 if (solBoard[i][j] != -1)
546 if (myDeck.IsFaceUp(solBoard[i][j]))
548 if ((j == 18) || (solBoard[i][j + 1] == -1))
549 r.setBottom(r.top() + CARD_HEIGHT - 1);
551 r.setBottom(r.top() + 17);
553 if (r.contains(point))
555 hitStack = dcHitStack = true;
556 stackStart = QPoint(i, j);
557 mouseOffset = QPoint(point.x() - r.left(), point.y() - r.top());
558 stackPos = QPoint(point.x() - mouseOffset.x(), point.y() - mouseOffset.y());
559 QRect rcUpdate(r.left(), r.top(), r.width(), CARD_HEIGHT - 1); //wil wok?
563 r.setTop(r.top() + 18);
569 if (solBoard[i][j + 1] == -1)
571 r.setBottom(r.top() + CARD_HEIGHT - 1);
573 if (r.contains(point))
575 myDeck.ToggleCardFacing(solBoard[i][j]); // Turn over that last card
579 PlaySound(IDW_CARDFLIP);
582 update(r); //wil wok?
588 emit UpdateScore(m_nScore);
596 r.setTop(r.top() + 4);
604 // Check for hit on discard stack
606 int nCX = 5 + CARD_WIDTH + 6 + ((m_nNumToSplay - 1) * 12);
607 int nCY = 7 + ((m_nNumToSplay - 1) * 2);
609 if (QRect(nCX, nCY, CARD_WIDTH - 1, CARD_HEIGHT - 1).contains(point))
613 hitDiscard = dcHitDiscard = true;
614 mouseOffset = QPoint(point.x() - (5 + CARD_WIDTH + 6 + ((m_nNumToSplay - 1) * 12)),
615 point.y() - (7 + ((m_nNumToSplay - 1) * 2)));
616 stackPos = QPoint(point.x() - mouseOffset.x(), point.y() - mouseOffset.y());
621 // Check for hit on remaining deck
623 if (QRect(5, 7, 5 + CARD_WIDTH - 1, 7 + CARD_HEIGHT - 1).contains(point))
627 if (stack1p == -1) // I.e. it's empty...
630 if ((m_bDrawThree && (m_nExhaustedDeck == 3))
631 || (!m_bDrawThree && (m_nExhaustedDeck == 1)))
635 m_nExhaustedDeck = 0;
636 m_nScore -= (m_bDrawThree ? 20 : 100);
641 emit UpdateScore(m_nScore);
644 if (!m_bVegasStyle || (m_bVegasStyle && ((m_bDrawThree && (m_nExhaustedDeck < 3))
645 || (!m_bDrawThree && (m_nExhaustedDeck < 1)))))
647 for(; stack2p != -1; stack2p--)
650 stack1[stack1p] = stack2[stack2p];
655 PlaySound(IDW_CARDKNOCK);
663 for(int i=0; i<(m_bDrawThree ? 3 : 1); i++)
665 if (stack1p != -1) // I.e., there are cards left...
667 stack2[++stack2p] = stack1[stack1p--];
672 PlaySound(IDW_CARDFLIP);
682 if (stack1p == -1) // This is done here because it's right!
686 if (m_bVegasStyle && ((m_bDrawThree && (m_nExhaustedDeck == 3))
687 || (!m_bDrawThree && (m_nExhaustedDeck == 1))))
692 mouseOffset = QPoint(point.x() - 5, point.y() - 7);
694 QRect inv1(5, 7, CARD_WIDTH, CARD_HEIGHT),
695 inv2(5 + CARD_WIDTH + 6, 7, (2 * 12) + CARD_WIDTH, (2 * 2) + CARD_HEIGHT);
700 // Check for hit on ace piles
702 for(int i=0; i<4; i++)
704 QRect aceRect(200 + ((CARD_WIDTH + 4) * i), 7, CARD_WIDTH - 1, CARD_HEIGHT - 1);
706 if (aceRect.contains(point) && (nAce[i] > -1))
709 // mouseOffset = QPoint(point.x() - (200 + ((CARD_WIDTH + 4) * i)), point.y() - 7);
710 mouseOffset = QPoint(point.x() - aceRect.x(), point.y() - aceRect.y());
711 stackPos = QPoint(point.x() - mouseOffset.x(), point.y() - mouseOffset.y());
718 void GameWidget::OnLButtonUp(QPoint point)
723 point -= mouseOffset; // ?? bad form!
725 if (hitStack || hitDiscard || (nHitAce != 0))
729 for(int i=0; i<7; i++) // Compute valid rects at bottoms of stacks...
731 tabl[i].setLeft(10 + (i * (CARD_WIDTH + 8)));
732 tabl[i].setRight(tabl[i].left() + CARD_WIDTH - 1);
735 for(int j=0; j<19; j++)
737 if ((solBoard[i][j] == -1)
738 || (hitStack && (i == stackStart.x()) && (j == stackStart.y())))
742 for(int k=0; k<j-1; k++)
744 if (!myDeck.IsFaceUp(solBoard[i][k]))
745 tabl[i].setTop(tabl[i].top() + 4);
747 tabl[i].setTop(tabl[i].top() + 17);
751 tabl[i].setBottom(tabl[i].top() + CARD_HEIGHT - 1);
757 // stackPos = QPoint(point.x() - mouseOffset.x(), point.y() - mouseOffset.y());
758 // QRect c(point.x(), point.y(), CARD_WIDTH - 1, CARD_HEIGHT - 1);
759 // QRect c(point.x() - mouseOffset.x(), point.y() - mouseOffset.y(), CARD_WIDTH - 1, CARD_HEIGHT - 1);
760 QRect c(stackPos.x(), stackPos.y(), CARD_WIDTH - 1, CARD_HEIGHT - 1);
762 a[0] = QRect(200, 7, CARD_WIDTH - 1, CARD_HEIGHT - 1);
763 a[1] = QRect(200 + ((CARD_WIDTH + 4) * 1), 7, CARD_WIDTH - 1, CARD_HEIGHT - 1);
764 a[2] = QRect(200 + ((CARD_WIDTH + 4) * 2), 7, CARD_WIDTH - 1, CARD_HEIGHT - 1);
765 a[3] = QRect(200 + ((CARD_WIDTH + 4) * 3), 7, CARD_WIDTH - 1, CARD_HEIGHT - 1);
768 bHitAce[0] = c.intersects(a[0]);
769 bHitAce[1] = c.intersects(a[1]);
770 bHitAce[2] = c.intersects(a[2]);
771 bHitAce[3] = c.intersects(a[3]);
773 // Check to see if hit more than one ace & pick the one that's closer
774 for(int i=0; i<3; i++)
776 if (bHitAce[i] && bHitAce[i + 1] && (nAce[i] == -1) && (nAce[i + 1] == -1))
778 if (c.intersected(a[i]).width() > c.intersected(a[i + 1]).width())
779 bHitAce[i + 1] = false;
787 bool bMovedToAce = false;
789 for(int i=0; i<4; i++)
791 if (bHitAce[i] && IsValidMoveToAce(nAce[i]))
796 nAce[i] = myDeck.GetCardAtPos(solBoard[stackStart.x()][stackStart.y()]),
797 solBoard[stackStart.x()][stackStart.y()] = -1;
801 nAce[i] = myDeck.GetCardAtPos(stack2[stack2p--]);
803 if (m_nNumToSplay != 1)
809 nAce[i] = nAce[nHitAce - 1];
810 int nCard = nAce[nHitAce - 1];
811 nAce[nHitAce - 1] = ((nCard - 1) % 13 == 0 ? -1 : nCard - 1);
817 PlaySound(IDW_CARDPLACE);
829 m_nScore += (m_bVegasStyle ? 5 : 10);
832 emit UpdateScore(m_nScore);
837 if (!bMovedToAce) // No aces, so check for stacks
839 for(int i=0; i<7; i++)
841 if (c.intersects(tabl[i]) && IsValidMoveToTableaux(i))
847 PlaySound(IDW_CARDPLACE);
850 if (hitStack && (i != stackStart.x()))
852 int cnt = stackStart.y();
854 for(int k=0; k<19; k++)
856 if (solBoard[i][k] == -1)
858 solBoard[i][k] = solBoard[stackStart.x()][cnt];
859 solBoard[stackStart.x()][cnt] = -1;
863 if (solBoard[stackStart.x()][cnt] == -1) // Done copying?
872 for(int k=0; k<19; k++)
874 if (solBoard[i][k] == -1)
876 solBoard[i][k] = stack2[stack2p--];
877 myDeck.SetCardFaceUp(solBoard[i][k]);
883 emit UpdateScore(m_nScore);
886 if (m_nNumToSplay != 1)
898 // handle moving to tableaux here...
899 for(int k=0; k<19; k++)
901 if (solBoard[i][k] == -1)
903 int nCard = nAce[nHitAce - 1];
905 for(int t=0; t<52; t++) // We have to find da damn thing...
907 if (myDeck.GetCardAtPos(t) == nCard)
914 nAce[nHitAce - 1] = ((nCard - 1) % 13 == 0 ? -1 : nCard - 1);
915 myDeck.SetCardFaceUp(solBoard[i][k]);
924 m_nScore -= (m_bVegasStyle ? 5 : 15);
927 emit UpdateScore(m_nScore);
941 mouseDown = hitStack = hitDiscard = false;
949 emit UpdateScore(m_nScore);
951 m_bWonLastGame = true;
957 QString id[] = { IDW_WINNER1, IDW_WINNER2, IDW_WINNER3, IDW_WINNER4, IDW_WINNER5 };
958 srand((unsigned)time(NULL));
959 int nWaveNum = rand() % 5;
960 PlaySound(id[nWaveNum]);
964 QMessageBox::information(this, "MAC Solitaire", "Congratulations!\nYou won!");
965 allowUserInput = false;
970 void GameWidget::OnMouseMove(QPoint point)
972 if (mouseDown && (hitStack || hitDiscard || (nHitAce != 0)))
978 for(int i=stackStart.y(); i<19; i++)
979 if (solBoard[stackStart.x()][i] != -1)
985 nHeight += CARD_HEIGHT - 18;
987 QRect rcOld(stackPos.x(), stackPos.y(), CARD_WIDTH,
988 nHeight); // Find old position for repainting...
990 stackPos = QPoint(point.x() - mouseOffset.x(), point.y() - mouseOffset.y());
992 QRect rcNew(stackPos.x(), stackPos.y(), CARD_WIDTH,
993 nHeight); // Find new position
994 QRect rcUpdate = rcOld.united(rcNew);
995 update(rcUpdate); //wil wok? YESH!
1000 void GameWidget::OnLButtonDblClk(QPoint point)
1002 if (dcHitStack || dcHitDiscard)
1004 hitStack = dcHitStack, hitDiscard = dcHitDiscard;
1006 for(int i=0; i<4; i++)
1008 if (IsValidMoveToAce(nAce[i]))
1012 nAce[i] = myDeck.GetCardAtPos(solBoard[stackStart.x()][stackStart.y()]);
1013 solBoard[stackStart.x()][stackStart.y()] = -1;
1017 nAce[i] = myDeck.GetCardAtPos(stack2[stack2p--]);
1019 if (m_nNumToSplay != 1)
1037 m_nScore += (m_bVegasStyle ? 5 : 10);
1040 emit UpdateScore(m_nScore);
1045 dcHitStack = hitStack = dcHitDiscard = hitDiscard = false;
1048 OnLButtonDown(point); // Either was on draw pile or somewhere else...
1052 // It's behaving strangely, especially with kings in the ace piles...
1053 // It's becuase of the mod 13 function: kings(13) get whacked to 0 this way...
1054 void GameWidget::HandleAutoRemove(void)
1062 int nLowestAce = (nAce[0] == -1 ? 0 : ((nAce[0] - 1) % 13) + 1); // Aces are 1-based, cards 1-based
1064 for(int i=1; i<4; i++)
1066 int nTemp = (nAce[i] == -1 ? 0 : ((nAce[i] - 1) % 13) + 1);
1068 if (nTemp < nLowestAce)
1072 for(int i=0; i<7; i++) // Compare cards that *can* go
1074 for(int j=18; j>=0; j--)
1076 if (solBoard[i][j] != -1)
1078 if (myDeck.IsFaceUp(solBoard[i][j]))
1080 int nCard = myDeck.GetCardAtPos(solBoard[i][j]);
1082 for(int k=0; k<4; k++)
1084 if (IsValidMoveToAce(nAce[k], nCard))
1086 // Ranking/suiting doesn't work if nAce == -1... FIX!
1087 // Should be fixed now...
1088 // Figure out some way to simplify this tortuous logic, for cryin' out loud!
1089 int nSuit[4], nRank[4];
1091 for(int t=0; t<4; t++)
1094 nSuit[t] = ((nAce[t] > 13) && (nAce[t] < 40) ? 0 : 1);
1098 nRank[t] = ((nAce[t] - 1) % 13) + 1;
1101 int nCardSuit = ((nCard > 13) && (nCard < 40) ? 0 : 1),
1102 nCardRank = ((nCard - 1) % 13) + 1;
1103 bool bSpecial = false;
1104 int nCR[2], nCnt = 0;
1106 for(int t=0; t<4; t++)
1107 if ((nCardSuit != nSuit[t]) && (t != k) && (nSuit[t] != -1))
1108 nCR[nCnt++] = nRank[t];
1110 if ((nCnt == 2) && (nCR[0] == nCR[1])
1111 && (nCR[0] - nLowestAce == 2) && (nCardRank - nLowestAce == 3))
1114 if (((((nCard - 1) % 13) + 1) - nLowestAce) <= 2 || bSpecial)
1115 // OR difference is 3 and both opposite color are at 2
1116 // if (((nCard%13) - nLowestAce) <= 2)
1118 solBoard[i][j] = -1;
1120 if (m_bAnimationsOn)
1121 AnimateCards(nCard, k, i, j);
1133 m_nScore += (m_bVegasStyle ? 5 : 10);
1135 emit UpdateScore(m_nScore);
1149 int nCard = myDeck.GetCardAtPos(stack2[stack2p]);
1151 for(int k=0; k<4; k++)
1153 if (IsValidMoveToAce(nAce[k], nCard))
1155 // Ranking/suiting doesn't work if nAce == -1... FIX!
1156 // Should be fixed now...
1157 // Figure out some way to simplify this tortuous logic, for cryin' out loud!
1158 int nSuit[4], nRank[4];
1160 for(int t=0; t<4; t++)
1163 nSuit[t] = ((nAce[t] > 13) && (nAce[t] < 40) ? 0 : 1);
1167 nRank[t] = ((nAce[t] - 1) % 13) + 1;
1170 int nCardSuit = ((nCard > 13) && (nCard < 40) ? 0 : 1),
1171 nCardRank = ((nCard - 1) % 13) + 1;
1172 bool bSpecial = false;
1173 int nCR[2], nCnt = 0;
1175 for(int t=0; t<4; t++)
1177 if ((nCardSuit != nSuit[t]) && (t != k) && (nSuit[t] != -1))
1178 nCR[nCnt++] = nRank[t];
1181 if ((nCnt == 2) && (nCR[0] == nCR[1])
1182 && (nCR[0] - nLowestAce == 2) && (nCardRank - nLowestAce == 3))
1185 if (((((nCard - 1) % 13) + 1) - nLowestAce) <= 2 || bSpecial)
1186 // OR difference is 3 and both opposite color are at 2 (tortuously checked above)
1190 if (m_nNumToSplay != 1)
1193 if (m_bAnimationsOn)
1194 AnimateCards(nCard, k, -1, -1);
1206 m_nScore += (m_bVegasStyle ? 5 : 10);
1209 emit UpdateScore(m_nScore);
1219 // Have two routines? One to initiate, the other to handle card movements?
1220 // Good idea! Implement it, you schmendrick!
1221 void GameWidget::AnimateCards(int nCard, int nAce, int nTabX, int nTabY)
1223 //no mas--jeff no likee CWaitCursor wc; // Automagically reverts when it goes out of scope
1224 int nCX, nCY, nDX, nDY;
1226 // Step 1: figure out where card started from
1229 // nCX = 5+CARD_WIDTH+6, nCY = 5;
1230 nCX = 5 + CARD_WIDTH + 6 + ((m_nNumToSplay - 1) * 12);
1231 nCY = 7 + ((m_nNumToSplay - 1) * 2);
1235 nCX = 10 + (nTabX * (CARD_WIDTH + 8)), nCY = 130;
1237 for(int j=0; j<nTabY; j++)
1239 if (solBoard[nTabX][j] != -1)
1241 if (myDeck.IsFaceUp(solBoard[nTabX][j]))
1248 // Step 2: figure out where card is going
1249 nDX = 200 + ((CARD_WIDTH + 4) * nAce), nDY = 7;
1250 // Step 3: animate card flying from start to destination
1251 // Use Bresenham's algorithm to figure path...
1252 // (ideas for movement: increasing acceleration, curved paths, more than one at a time)
1254 m_pFirstCard = new SCardInfo(nCard);
1255 int nDeltaX = nDX - nCX, nDeltaY = nDY - nCY;
1258 update(); //will do it??? Seems to...
1259 //Doing this causes an image of the card to be drawn at (0, 0) :-/
1261 QRect rcOld(nCX, nCY, CARD_WIDTH, CARD_HEIGHT), rcNew, rcUpdate;
1263 if (abs(nDeltaX) > abs(nDeltaY))
1265 for(int i=0; i<abs(nDeltaX); i++)
1267 // Put card draw stuff into OnDraw? (probably best way to do it...)
1268 nError += abs(nDeltaY);
1270 if (nError > abs(nDeltaX))
1271 nCY += (nDeltaY < 0 ? -1 : +1), nError -= abs(nDeltaX);
1273 m_pFirstCard->nXPos = nCX + (nDeltaX < 0 ? -i : +i), m_pFirstCard->nYPos = nCY;
1279 rcNew = QRect(m_pFirstCard->nXPos, m_pFirstCard->nYPos,
1280 CARD_WIDTH, CARD_HEIGHT);
1281 rcUpdate = rcOld.united(rcNew);
1288 // calc new positions
1291 m_pFirstCard->nXPos = nDX, m_pFirstCard->nYPos = nDY;
1292 rcNew = QRect(nDX, nDY, CARD_WIDTH, CARD_HEIGHT);
1293 rcUpdate = rcOld.united(rcNew);
1298 for(int i=0; i<abs(nDeltaY); i++)
1300 // Put card draw stuff into OnDraw? (probably best way to do it...)
1301 nError += abs(nDeltaX);
1303 if (nError > abs(nDeltaY))
1304 nCX += (nDeltaX < 0 ? -1 : +1), nError -= abs(nDeltaY);
1306 m_pFirstCard->nXPos = nCX, m_pFirstCard->nYPos = nCY + (nDeltaY < 0 ? -i : +i);
1312 rcNew = QRect(m_pFirstCard->nXPos, m_pFirstCard->nYPos,
1313 CARD_WIDTH, CARD_HEIGHT);
1314 rcUpdate = rcOld.united(rcNew);
1316 repaint(rcUpdate);//this is crappy. should only update what's needed!
1321 // calc new positions
1324 m_pFirstCard->nXPos = nDX, m_pFirstCard->nYPos = nDY;
1325 rcNew = QRect(nDX, nDY, CARD_WIDTH, CARD_HEIGHT);
1326 rcUpdate = rcOld.united(rcNew);
1330 m_bFreeCard = false;
1331 delete m_pFirstCard;
1335 bool GameWidget::IsValidMoveToAce(int nAceM)
1339 for(int k=stackStart.y(); k<19; k++)
1341 if (solBoard[stackStart.x()][k] != -1)
1345 if ((hitStack && (nCards == 1)) || hitDiscard)
1347 int nCardHit = (hitStack ? myDeck.GetCardAtPos(solBoard[stackStart.x()][stackStart.y()])
1348 : myDeck.GetCardAtPos(stack2[stack2p]));
1349 return IsValidMoveToAce(nAceM, nCardHit);
1351 else if (nHitAce != 0)
1353 return IsValidMoveToAce(nAceM, nAce[nHitAce - 1]);
1360 bool GameWidget::IsValidMoveToAce(int nAce, int nCard)
1362 if (((nAce == -1) && ((nCard % 13) == 1))
1363 || ((nAce == (nCard - 1)) && ((nAce % 13) != 0)))
1370 bool GameWidget::IsValidMoveToTableaux(int nStack)
1372 int nBottomCard = -1, nTopCard;
1374 for(int i=0; i<19; i++)
1376 if ((solBoard[nStack][i] != -1) && (myDeck.IsFaceUp(solBoard[nStack][i])))
1377 nBottomCard = myDeck.GetCardAtPos(solBoard[nStack][i]);
1381 nTopCard = myDeck.GetCardAtPos(solBoard[stackStart.x()][stackStart.y()]);
1382 else if (hitDiscard)
1383 nTopCard = myDeck.GetCardAtPos(stack2[stack2p]);
1384 else if (nHitAce != 0)
1385 nTopCard = nAce[nHitAce - 1];
1387 int colorBC = ((nBottomCard > 13) && (nBottomCard < 40) ? 0 : 1);
1388 int colorTC = ((nTopCard > 13) && (nTopCard < 40) ? 0 : 1);
1389 int rankBC = (nBottomCard - 1) % 13;
1390 int rankTC = (nTopCard - 1) % 13;
1392 if (((rankBC == (rankTC + 1)) && (colorBC != colorTC))
1393 || ((nBottomCard == -1) && (rankTC == 12)))
1400 bool GameWidget::PlayerWon(void)
1402 for(int i=0; i<7; i++)
1403 if (solBoard[i][0] != -1)
1406 if ((stack1p != -1) || (stack2p != -1))
1413 void GameWidget::HandleStatistics(void)
1428 if (!m_bWonLastGame)
1434 if (m_nStreakC < 0) // Beat largest losing streak?
1436 if (abs(m_nStreakC) > m_nStreakL)
1437 m_nStreakL = abs(m_nStreakC);
1439 else // Check for longest winning streak
1441 if (m_nStreakC > m_nStreakW)
1442 m_nStreakW = m_nStreakC;
1445 if (m_bVegasStyle) // Check for high score
1447 if (m_nScore > m_nVHiScore)
1448 m_nVHiScore = m_nScore;
1452 if (m_nScore > m_nHiScore)
1453 m_nHiScore = m_nScore;
1459 void GameWidget::ResizeGrid(void)
1463 // Find the constraints
1464 float boxSizeX = s.width() / gameBoard->width;
1465 float boxSizeY = s.height() / gameBoard->height;
1467 maxLength = (int)(boxSizeX > boxSizeY ? boxSizeY : boxSizeX);
1469 offsetX = (s.width() - (maxLength * gameBoard->width)) / 2;
1470 offsetY = (s.height() - (maxLength * gameBoard->height)) / 2;
1475 // Halt processing for 'count' milliseconds
1477 void GameWidget::Pause(int count)
1479 // DWORD endCount = GetTickCount() + count;
1480 // while (GetTickCount() < endCount) {} // Still crude, but better control
1482 usleep(count * 1000);
1484 // This causes it to lock up randomly. :-/
1488 while (time.msec() < count)