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)//,
22 // score->setTextFormat(Qt::PlainText);
23 setFixedSize(580, 520);
24 gameBoard = new GameBoard(1);
28 GameWidget::~GameWidget(void)
33 void GameWidget::paintEvent(QPaintEvent * /*event*/)
36 // Optimizations: Create a member variable that has a bitmap in it,
37 // tile it with the felt in OnCreate(), blit as background instead of
38 // FillRect(r, brWhite). Also need to keep track of client rect.
39 // Possibly have 2 bitmaps as member vars, so keep bm construction to a minimum...
41 QPainter painter(this);
42 // QRect rcUpdate = dc.m_ps.rcPaint; // Update rect...
43 // QRect rcUpdate = QRect(0, 0, 580, 520); // Update rect?
48 for(int y=0; y<gameBoard->height; y++)
50 for(int x=0; x<gameBoard->width; x++)
52 int tile = gameBoard->board[ptr++];
53 painter.setPen(QPen(Qt::black, 2.0, Qt::SolidLine));
54 painter.setBrush(QBrush(Qt::black));
58 painter.setBrush(QBrush(Qt::white));
59 painter.drawRect(x * maxLength, y * maxLength, maxLength, maxLength);
61 else if (tile == GTBox)
63 painter.setBrush(QBrush(Qt::red));
64 painter.drawRect(x * maxLength, y * maxLength, maxLength, maxLength);
66 else if (tile == GTBoxSpot)
68 painter.setBrush(QBrush(Qt::magenta));
69 painter.drawRect((x * maxLength) + 20, (y * maxLength) + 20, maxLength - 40, maxLength - 40);
71 else if (tile == (GTBox | GTBoxSpot))
73 painter.setBrush(QBrush(Qt::green));
74 painter.drawRect(x * maxLength, y * maxLength, maxLength, maxLength);
77 if ((gameBoard->playerX == x) && (gameBoard->playerY == y))
79 painter.setBrush(QBrush(Qt::yellow));
80 painter.drawEllipse((x * maxLength) + 10, (y * maxLength) + 10, maxLength - 20, maxLength - 20);
81 // painter.drawRect(x * maxLength, y * maxLength, maxLength, maxLength);
88 DrawBoard(&painter, rcUpdate);
90 // Draw the "loose" cards....
94 for(int i=stackStart.y(); i<19; i++)
96 int card = solBoard[stackStart.x()][i];
100 if (solBoard[stackStart.x()][i + 1] == -1) // Draw full card
101 cdtDraw(&painter, stackPos.x(), stackPos.y() + ((i - stackStart.y()) * 18),
102 myDeck.GetCardAtPos(card), FACEUP);
103 else // Draw partial card
104 cdtDraw(&painter, stackPos.x(), stackPos.y() + ((i - stackStart.y()) * 18),
105 myDeck.GetCardAtPos(card), FACEUP, 20);
111 cdtDraw(&painter, stackPos.x(), stackPos.y(), myDeck.GetCardAtPos(stack2[stack2p]),
115 cdtDraw(&painter, stackPos.x(), stackPos.y(), nAce[nHitAce - 1], FACEUP);
117 if (m_bFreeCard) // For card animations...
119 for(SCardInfo * pCard=m_pFirstCard; pCard; pCard=pCard->pNext)
120 cdtDraw(&painter, pCard->nXPos, pCard->nYPos, pCard->nCard, FACEUP);
126 void GameWidget::mousePressEvent(QMouseEvent * event)
128 if (event->button() == Qt::LeftButton)
130 // OnLButtonDown(event->pos());
136 void GameWidget::mouseMoveEvent(QMouseEvent * event)
138 if (event->buttons() & Qt::LeftButton)
140 // OnMouseMove(event->pos());
146 void GameWidget::mouseReleaseEvent(QMouseEvent * event)
148 if (event->button() == Qt::LeftButton)
150 // OnLButtonUp(event->pos());
156 void GameWidget::mouseDoubleClickEvent(QMouseEvent * event)
158 if (event->button() == Qt::LeftButton)
160 // OnLButtonDblClk(event->pos());
166 void GameWidget::keyPressEvent(QKeyEvent * event)
168 int key = event->key();
170 if (key == Qt::Key_Up)
172 if (gameBoard->MovePlayerN() == PMInvalid)
175 else if (key == Qt::Key_Down)
177 if (gameBoard->MovePlayerS() == PMInvalid)
180 else if (key == Qt::Key_Left)
182 if (gameBoard->MovePlayerW() == PMInvalid)
185 else if (key == Qt::Key_Right)
187 if (gameBoard->MovePlayerE() == PMInvalid)
193 // Only update if a key we recognize has been pressed!
196 if (gameBoard->GameHasBeenWon())
201 void GameWidget::keyReleaseEvent(QKeyEvent * event)
206 void GameWidget::NextLevel(void)
210 gameBoard = new GameBoard(level);
215 void GameWidget::ResetLevel(void)
217 gameBoard->ResetGame();
223 bool GameWidget::CreateBackground(void)
225 char BGRes[27][64] = {
226 ":/res/grfttile.bmp",
228 ":/res/bg_tech_3.bmp",
229 ":/res/bg_tech_2.bmp",
230 ":/res/bg_tech_1.bmp",
231 ":/res/bg_weave_3.bmp",
232 ":/res/bg_weave_2.bmp",
233 ":/res/bg_clouds_2.bmp",
234 ":/res/bg_floor_plate.bmp",
235 ":/res/bg_marble_b.bmp",
236 ":/res/bg_marble_g.bmp",
237 ":/res/bg_marble_p.bmp",
238 ":/res/bg_marble_r.bmp",
239 ":/res/bg_marble_rb.bmp",
240 ":/res/bg_money_1.bmp",
241 ":/res/bg_pinstripe2.bmp",
242 ":/res/bg_pinstripe7.bmp",
243 ":/res/bg_raindrops_large.bmp",
244 ":/res/bg_raindrops_small.bmp",
245 ":/res/bg_stucco.bmp",
246 ":/res/bg_wood_w.bmp",
247 ":/res/bg_wood_b1.bmp",
248 ":/res/bg_wood_d.bmp",
249 ":/res/bg_wood_f.bmp",
250 ":/res/bg_wood_mh.bmp",
251 ":/res/bg_wood_mv.bmp",
252 ":/res/bg_wood_ro.bmp"
255 QPalette pal = palette();
256 pal.setBrush(backgroundRole(), QBrush(QPixmap(BGRes[m_nBackground])));
257 setAutoFillBackground(true);
260 return true; // Ignore errors for now...
264 void GameWidget::DrawBoard(QPainter * painter, QRect r)
266 // Use "r" as clipping rect--only draw what's necessary
268 // pDC->BitBlt(r.left, r.top, r.Width(), r.Height(), pDC2, r.left, r.top, SRCCOPY);
270 // Draw the solitaire board...
272 for(int i=0; i<7; i++)
274 int y = 126; //130-4;
276 for(int j=0; j<19; j++)
278 int card = solBoard[i][j];
280 if (card != -1 && !(hitStack && (i == stackStart.x()) && (j >= stackStart.y())))
282 int nLinesToDraw = 0; // Default--draws whole card
284 if (solBoard[i][j + 1] != -1 && !(hitStack && (i == stackStart.x()) && (j + 1 >= stackStart.y())))
288 if (myDeck.IsFaceUp(card))
292 int x = 10 + (i * (CARD_WIDTH + 8));//, y = 130 + (j * 4);
297 if (myDeck.IsFaceUp(solBoard[i][j - 1])) // Previous card...
301 //wil wok??? mebbe... YESH!!!!!!!!!!!!!
302 QRect cardRect(x, y, CARD_WIDTH, (nLinesToDraw ? nLinesToDraw : CARD_HEIGHT));
304 if (r.intersects(cardRect))
305 cdtDraw(painter, x, y, (myDeck.IsFaceUp(card) ? myDeck.GetCardAtPos(card)
306 : nCardBack), FACEUP, nLinesToDraw);
311 for(int i=0; i<4; i++)
313 if (nHitAce == i + 1)
315 if (((nAce[i] - 1) % 13) != 0)
316 cdtDraw(painter, 200 + ((CARD_WIDTH + 4) * i), 7, nAce[i] - 1, FACEUP);
318 cdtDraw(painter, 200 + ((CARD_WIDTH + 4) * i), 7, 0, INVISIBLEGHOST);
323 cdtDraw(painter, 200 + ((CARD_WIDTH + 4) * i), 7, nAce[i], FACEUP);
325 cdtDraw(painter, 200 + ((CARD_WIDTH + 4) * i), 7, 0, INVISIBLEGHOST);
330 cdtDraw(painter, 5, 7, nCardBack, FACEUP);
335 cdtDraw(painter, 5, 7, 0, DECKX);
337 cdtDraw(painter, 5, 7, 0, DECKO);
339 cdtDraw(painter, 5, 7, 0, (m_bShowX ? DECKX : DECKO));
347 for(int k=0; k<m_nNumToSplay; k++)
348 cdtDraw(painter, 5 + CARD_WIDTH + 6 + (k * 12), 7 + (k * 2),
349 myDeck.GetCardAtPos(stack2[stack2p - (m_nNumToSplay - k - 1)]), FACEUP);
355 if (m_nNumToSplay == 1)
356 cdtDraw(painter, 5 + CARD_WIDTH + 6, 7, myDeck.GetCardAtPos(stack2[stack2p - 1]), FACEUP);
358 for(int k=0; k<m_nNumToSplay-1; k++)
359 cdtDraw(painter, 5 + CARD_WIDTH + 6 + (k * 12), 7 + (k * 2),
360 myDeck.GetCardAtPos(stack2[stack2p - (m_nNumToSplay - k - 1)]), FACEUP);
367 void GameWidget::OnLButtonDown(QPoint point)
369 dcHitStack = dcHitDiscard = false; // For double clicking...
370 m_bTouchedGame = true; // Game has been attempted!
378 for(int i=0; i<7; i++)
379 for(int j=0; j<20; j++)
380 solBoardUndo[i][j] = solBoard[i][j];
382 for(int i=0; i<24; i++)
383 stack1Undo[i] = stack1[i], stack2Undo[i] = stack2[i];
385 for(int i=0; i<4; i++)
386 nAceUndo[i] = nAce[i];
388 stack1pUndo = stack1p, stack2pUndo = stack2p;
389 m_nScoreUndo = m_nScore;
390 m_nExhaustedDeckUndo = m_nExhaustedDeck;
391 m_nNumToSplayUndo = m_nNumToSplay;
392 m_bShowXUndo = m_bShowX;
395 // Check for a hit on the tableaux...
397 for(int i=0; i<7; i++)
400 r.setLeft(10 + (i * (CARD_WIDTH + 8)));
401 r.setRight(r.left() + CARD_WIDTH - 1);
405 for(int j=0; j<19; j++)
407 if (solBoard[i][j] != -1)
409 if (myDeck.IsFaceUp(solBoard[i][j]))
411 if ((j == 18) || (solBoard[i][j + 1] == -1))
412 r.setBottom(r.top() + CARD_HEIGHT - 1);
414 r.setBottom(r.top() + 17);
416 if (r.contains(point))
418 hitStack = dcHitStack = true;
419 stackStart = QPoint(i, j);
420 mouseOffset = QPoint(point.x() - r.left(), point.y() - r.top());
421 stackPos = QPoint(point.x() - mouseOffset.x(), point.y() - mouseOffset.y());
422 QRect rcUpdate(r.left(), r.top(), r.width(), CARD_HEIGHT - 1); //wil wok?
426 r.setTop(r.top() + 18);
432 if (solBoard[i][j + 1] == -1)
434 r.setBottom(r.top() + CARD_HEIGHT - 1);
436 if (r.contains(point))
438 myDeck.ToggleCardFacing(solBoard[i][j]); // Turn over that last card
442 PlaySound(IDW_CARDFLIP);
445 update(r); //wil wok?
451 emit UpdateScore(m_nScore);
459 r.setTop(r.top() + 4);
467 // Check for hit on discard stack
469 int nCX = 5 + CARD_WIDTH + 6 + ((m_nNumToSplay - 1) * 12);
470 int nCY = 7 + ((m_nNumToSplay - 1) * 2);
472 if (QRect(nCX, nCY, CARD_WIDTH - 1, CARD_HEIGHT - 1).contains(point))
476 hitDiscard = dcHitDiscard = true;
477 mouseOffset = QPoint(point.x() - (5 + CARD_WIDTH + 6 + ((m_nNumToSplay - 1) * 12)),
478 point.y() - (7 + ((m_nNumToSplay - 1) * 2)));
479 stackPos = QPoint(point.x() - mouseOffset.x(), point.y() - mouseOffset.y());
484 // Check for hit on remaining deck
486 if (QRect(5, 7, 5 + CARD_WIDTH - 1, 7 + CARD_HEIGHT - 1).contains(point))
490 if (stack1p == -1) // I.e. it's empty...
493 if ((m_bDrawThree && (m_nExhaustedDeck == 3))
494 || (!m_bDrawThree && (m_nExhaustedDeck == 1)))
498 m_nExhaustedDeck = 0;
499 m_nScore -= (m_bDrawThree ? 20 : 100);
504 emit UpdateScore(m_nScore);
507 if (!m_bVegasStyle || (m_bVegasStyle && ((m_bDrawThree && (m_nExhaustedDeck < 3))
508 || (!m_bDrawThree && (m_nExhaustedDeck < 1)))))
510 for(; stack2p != -1; stack2p--)
513 stack1[stack1p] = stack2[stack2p];
518 PlaySound(IDW_CARDKNOCK);
526 for(int i=0; i<(m_bDrawThree ? 3 : 1); i++)
528 if (stack1p != -1) // I.e., there are cards left...
530 stack2[++stack2p] = stack1[stack1p--];
535 PlaySound(IDW_CARDFLIP);
545 if (stack1p == -1) // This is done here because it's right!
549 if (m_bVegasStyle && ((m_bDrawThree && (m_nExhaustedDeck == 3))
550 || (!m_bDrawThree && (m_nExhaustedDeck == 1))))
555 mouseOffset = QPoint(point.x() - 5, point.y() - 7);
557 QRect inv1(5, 7, CARD_WIDTH, CARD_HEIGHT),
558 inv2(5 + CARD_WIDTH + 6, 7, (2 * 12) + CARD_WIDTH, (2 * 2) + CARD_HEIGHT);
563 // Check for hit on ace piles
565 for(int i=0; i<4; i++)
567 QRect aceRect(200 + ((CARD_WIDTH + 4) * i), 7, CARD_WIDTH - 1, CARD_HEIGHT - 1);
569 if (aceRect.contains(point) && (nAce[i] > -1))
572 // mouseOffset = QPoint(point.x() - (200 + ((CARD_WIDTH + 4) * i)), point.y() - 7);
573 mouseOffset = QPoint(point.x() - aceRect.x(), point.y() - aceRect.y());
574 stackPos = QPoint(point.x() - mouseOffset.x(), point.y() - mouseOffset.y());
581 void GameWidget::OnLButtonUp(QPoint point)
586 point -= mouseOffset; // ?? bad form!
588 if (hitStack || hitDiscard || (nHitAce != 0))
592 for(int i=0; i<7; i++) // Compute valid rects at bottoms of stacks...
594 tabl[i].setLeft(10 + (i * (CARD_WIDTH + 8)));
595 tabl[i].setRight(tabl[i].left() + CARD_WIDTH - 1);
598 for(int j=0; j<19; j++)
600 if ((solBoard[i][j] == -1)
601 || (hitStack && (i == stackStart.x()) && (j == stackStart.y())))
605 for(int k=0; k<j-1; k++)
607 if (!myDeck.IsFaceUp(solBoard[i][k]))
608 tabl[i].setTop(tabl[i].top() + 4);
610 tabl[i].setTop(tabl[i].top() + 17);
614 tabl[i].setBottom(tabl[i].top() + CARD_HEIGHT - 1);
620 // stackPos = QPoint(point.x() - mouseOffset.x(), point.y() - mouseOffset.y());
621 // QRect c(point.x(), point.y(), CARD_WIDTH - 1, CARD_HEIGHT - 1);
622 // QRect c(point.x() - mouseOffset.x(), point.y() - mouseOffset.y(), CARD_WIDTH - 1, CARD_HEIGHT - 1);
623 QRect c(stackPos.x(), stackPos.y(), CARD_WIDTH - 1, CARD_HEIGHT - 1);
625 a[0] = QRect(200, 7, CARD_WIDTH - 1, CARD_HEIGHT - 1);
626 a[1] = QRect(200 + ((CARD_WIDTH + 4) * 1), 7, CARD_WIDTH - 1, CARD_HEIGHT - 1);
627 a[2] = QRect(200 + ((CARD_WIDTH + 4) * 2), 7, CARD_WIDTH - 1, CARD_HEIGHT - 1);
628 a[3] = QRect(200 + ((CARD_WIDTH + 4) * 3), 7, CARD_WIDTH - 1, CARD_HEIGHT - 1);
631 bHitAce[0] = c.intersects(a[0]);
632 bHitAce[1] = c.intersects(a[1]);
633 bHitAce[2] = c.intersects(a[2]);
634 bHitAce[3] = c.intersects(a[3]);
636 // Check to see if hit more than one ace & pick the one that's closer
637 for(int i=0; i<3; i++)
639 if (bHitAce[i] && bHitAce[i + 1] && (nAce[i] == -1) && (nAce[i + 1] == -1))
641 if (c.intersected(a[i]).width() > c.intersected(a[i + 1]).width())
642 bHitAce[i + 1] = false;
650 bool bMovedToAce = false;
652 for(int i=0; i<4; i++)
654 if (bHitAce[i] && IsValidMoveToAce(nAce[i]))
659 nAce[i] = myDeck.GetCardAtPos(solBoard[stackStart.x()][stackStart.y()]),
660 solBoard[stackStart.x()][stackStart.y()] = -1;
664 nAce[i] = myDeck.GetCardAtPos(stack2[stack2p--]);
666 if (m_nNumToSplay != 1)
672 nAce[i] = nAce[nHitAce - 1];
673 int nCard = nAce[nHitAce - 1];
674 nAce[nHitAce - 1] = ((nCard - 1) % 13 == 0 ? -1 : nCard - 1);
680 PlaySound(IDW_CARDPLACE);
692 m_nScore += (m_bVegasStyle ? 5 : 10);
695 emit UpdateScore(m_nScore);
700 if (!bMovedToAce) // No aces, so check for stacks
702 for(int i=0; i<7; i++)
704 if (c.intersects(tabl[i]) && IsValidMoveToTableaux(i))
710 PlaySound(IDW_CARDPLACE);
713 if (hitStack && (i != stackStart.x()))
715 int cnt = stackStart.y();
717 for(int k=0; k<19; k++)
719 if (solBoard[i][k] == -1)
721 solBoard[i][k] = solBoard[stackStart.x()][cnt];
722 solBoard[stackStart.x()][cnt] = -1;
726 if (solBoard[stackStart.x()][cnt] == -1) // Done copying?
735 for(int k=0; k<19; k++)
737 if (solBoard[i][k] == -1)
739 solBoard[i][k] = stack2[stack2p--];
740 myDeck.SetCardFaceUp(solBoard[i][k]);
746 emit UpdateScore(m_nScore);
749 if (m_nNumToSplay != 1)
761 // handle moving to tableaux here...
762 for(int k=0; k<19; k++)
764 if (solBoard[i][k] == -1)
766 int nCard = nAce[nHitAce - 1];
768 for(int t=0; t<52; t++) // We have to find da damn thing...
770 if (myDeck.GetCardAtPos(t) == nCard)
777 nAce[nHitAce - 1] = ((nCard - 1) % 13 == 0 ? -1 : nCard - 1);
778 myDeck.SetCardFaceUp(solBoard[i][k]);
787 m_nScore -= (m_bVegasStyle ? 5 : 15);
790 emit UpdateScore(m_nScore);
804 mouseDown = hitStack = hitDiscard = false;
812 emit UpdateScore(m_nScore);
814 m_bWonLastGame = true;
820 QString id[] = { IDW_WINNER1, IDW_WINNER2, IDW_WINNER3, IDW_WINNER4, IDW_WINNER5 };
821 srand((unsigned)time(NULL));
822 int nWaveNum = rand() % 5;
823 PlaySound(id[nWaveNum]);
827 QMessageBox::information(this, "MAC Solitaire", "Congratulations!\nYou won!");
828 allowUserInput = false;
833 void GameWidget::OnMouseMove(QPoint point)
835 if (mouseDown && (hitStack || hitDiscard || (nHitAce != 0)))
841 for(int i=stackStart.y(); i<19; i++)
842 if (solBoard[stackStart.x()][i] != -1)
848 nHeight += CARD_HEIGHT - 18;
850 QRect rcOld(stackPos.x(), stackPos.y(), CARD_WIDTH,
851 nHeight); // Find old position for repainting...
853 stackPos = QPoint(point.x() - mouseOffset.x(), point.y() - mouseOffset.y());
855 QRect rcNew(stackPos.x(), stackPos.y(), CARD_WIDTH,
856 nHeight); // Find new position
857 QRect rcUpdate = rcOld.united(rcNew);
858 update(rcUpdate); //wil wok? YESH!
863 void GameWidget::OnLButtonDblClk(QPoint point)
865 if (dcHitStack || dcHitDiscard)
867 hitStack = dcHitStack, hitDiscard = dcHitDiscard;
869 for(int i=0; i<4; i++)
871 if (IsValidMoveToAce(nAce[i]))
875 nAce[i] = myDeck.GetCardAtPos(solBoard[stackStart.x()][stackStart.y()]);
876 solBoard[stackStart.x()][stackStart.y()] = -1;
880 nAce[i] = myDeck.GetCardAtPos(stack2[stack2p--]);
882 if (m_nNumToSplay != 1)
900 m_nScore += (m_bVegasStyle ? 5 : 10);
903 emit UpdateScore(m_nScore);
908 dcHitStack = hitStack = dcHitDiscard = hitDiscard = false;
911 OnLButtonDown(point); // Either was on draw pile or somewhere else...
915 // It's behaving strangely, especially with kings in the ace piles...
916 // It's becuase of the mod 13 function: kings(13) get whacked to 0 this way...
917 void GameWidget::HandleAutoRemove(void)
925 int nLowestAce = (nAce[0] == -1 ? 0 : ((nAce[0] - 1) % 13) + 1); // Aces are 1-based, cards 1-based
927 for(int i=1; i<4; i++)
929 int nTemp = (nAce[i] == -1 ? 0 : ((nAce[i] - 1) % 13) + 1);
931 if (nTemp < nLowestAce)
935 for(int i=0; i<7; i++) // Compare cards that *can* go
937 for(int j=18; j>=0; j--)
939 if (solBoard[i][j] != -1)
941 if (myDeck.IsFaceUp(solBoard[i][j]))
943 int nCard = myDeck.GetCardAtPos(solBoard[i][j]);
945 for(int k=0; k<4; k++)
947 if (IsValidMoveToAce(nAce[k], nCard))
949 // Ranking/suiting doesn't work if nAce == -1... FIX!
950 // Should be fixed now...
951 // Figure out some way to simplify this tortuous logic, for cryin' out loud!
952 int nSuit[4], nRank[4];
954 for(int t=0; t<4; t++)
957 nSuit[t] = ((nAce[t] > 13) && (nAce[t] < 40) ? 0 : 1);
961 nRank[t] = ((nAce[t] - 1) % 13) + 1;
964 int nCardSuit = ((nCard > 13) && (nCard < 40) ? 0 : 1),
965 nCardRank = ((nCard - 1) % 13) + 1;
966 bool bSpecial = false;
967 int nCR[2], nCnt = 0;
969 for(int t=0; t<4; t++)
970 if ((nCardSuit != nSuit[t]) && (t != k) && (nSuit[t] != -1))
971 nCR[nCnt++] = nRank[t];
973 if ((nCnt == 2) && (nCR[0] == nCR[1])
974 && (nCR[0] - nLowestAce == 2) && (nCardRank - nLowestAce == 3))
977 if (((((nCard - 1) % 13) + 1) - nLowestAce) <= 2 || bSpecial)
978 // OR difference is 3 and both opposite color are at 2
979 // if (((nCard%13) - nLowestAce) <= 2)
984 AnimateCards(nCard, k, i, j);
996 m_nScore += (m_bVegasStyle ? 5 : 10);
998 emit UpdateScore(m_nScore);
1012 int nCard = myDeck.GetCardAtPos(stack2[stack2p]);
1014 for(int k=0; k<4; k++)
1016 if (IsValidMoveToAce(nAce[k], nCard))
1018 // Ranking/suiting doesn't work if nAce == -1... FIX!
1019 // Should be fixed now...
1020 // Figure out some way to simplify this tortuous logic, for cryin' out loud!
1021 int nSuit[4], nRank[4];
1023 for(int t=0; t<4; t++)
1026 nSuit[t] = ((nAce[t] > 13) && (nAce[t] < 40) ? 0 : 1);
1030 nRank[t] = ((nAce[t] - 1) % 13) + 1;
1033 int nCardSuit = ((nCard > 13) && (nCard < 40) ? 0 : 1),
1034 nCardRank = ((nCard - 1) % 13) + 1;
1035 bool bSpecial = false;
1036 int nCR[2], nCnt = 0;
1038 for(int t=0; t<4; t++)
1040 if ((nCardSuit != nSuit[t]) && (t != k) && (nSuit[t] != -1))
1041 nCR[nCnt++] = nRank[t];
1044 if ((nCnt == 2) && (nCR[0] == nCR[1])
1045 && (nCR[0] - nLowestAce == 2) && (nCardRank - nLowestAce == 3))
1048 if (((((nCard - 1) % 13) + 1) - nLowestAce) <= 2 || bSpecial)
1049 // OR difference is 3 and both opposite color are at 2 (tortuously checked above)
1053 if (m_nNumToSplay != 1)
1056 if (m_bAnimationsOn)
1057 AnimateCards(nCard, k, -1, -1);
1069 m_nScore += (m_bVegasStyle ? 5 : 10);
1072 emit UpdateScore(m_nScore);
1082 // Have two routines? One to initiate, the other to handle card movements?
1083 // Good idea! Implement it, you schmendrick!
1084 void GameWidget::AnimateCards(int nCard, int nAce, int nTabX, int nTabY)
1086 //no mas--jeff no likee CWaitCursor wc; // Automagically reverts when it goes out of scope
1087 int nCX, nCY, nDX, nDY;
1089 // Step 1: figure out where card started from
1092 // nCX = 5+CARD_WIDTH+6, nCY = 5;
1093 nCX = 5 + CARD_WIDTH + 6 + ((m_nNumToSplay - 1) * 12);
1094 nCY = 7 + ((m_nNumToSplay - 1) * 2);
1098 nCX = 10 + (nTabX * (CARD_WIDTH + 8)), nCY = 130;
1100 for(int j=0; j<nTabY; j++)
1102 if (solBoard[nTabX][j] != -1)
1104 if (myDeck.IsFaceUp(solBoard[nTabX][j]))
1111 // Step 2: figure out where card is going
1112 nDX = 200 + ((CARD_WIDTH + 4) * nAce), nDY = 7;
1113 // Step 3: animate card flying from start to destination
1114 // Use Bresenham's algorithm to figure path...
1115 // (ideas for movement: increasing acceleration, curved paths, more than one at a time)
1117 m_pFirstCard = new SCardInfo(nCard);
1118 int nDeltaX = nDX - nCX, nDeltaY = nDY - nCY;
1121 update(); //will do it??? Seems to...
1122 //Doing this causes an image of the card to be drawn at (0, 0) :-/
1124 QRect rcOld(nCX, nCY, CARD_WIDTH, CARD_HEIGHT), rcNew, rcUpdate;
1126 if (abs(nDeltaX) > abs(nDeltaY))
1128 for(int i=0; i<abs(nDeltaX); i++)
1130 // Put card draw stuff into OnDraw? (probably best way to do it...)
1131 nError += abs(nDeltaY);
1133 if (nError > abs(nDeltaX))
1134 nCY += (nDeltaY < 0 ? -1 : +1), nError -= abs(nDeltaX);
1136 m_pFirstCard->nXPos = nCX + (nDeltaX < 0 ? -i : +i), m_pFirstCard->nYPos = nCY;
1142 rcNew = QRect(m_pFirstCard->nXPos, m_pFirstCard->nYPos,
1143 CARD_WIDTH, CARD_HEIGHT);
1144 rcUpdate = rcOld.united(rcNew);
1151 // calc new positions
1154 m_pFirstCard->nXPos = nDX, m_pFirstCard->nYPos = nDY;
1155 rcNew = QRect(nDX, nDY, CARD_WIDTH, CARD_HEIGHT);
1156 rcUpdate = rcOld.united(rcNew);
1161 for(int i=0; i<abs(nDeltaY); i++)
1163 // Put card draw stuff into OnDraw? (probably best way to do it...)
1164 nError += abs(nDeltaX);
1166 if (nError > abs(nDeltaY))
1167 nCX += (nDeltaX < 0 ? -1 : +1), nError -= abs(nDeltaY);
1169 m_pFirstCard->nXPos = nCX, m_pFirstCard->nYPos = nCY + (nDeltaY < 0 ? -i : +i);
1175 rcNew = QRect(m_pFirstCard->nXPos, m_pFirstCard->nYPos,
1176 CARD_WIDTH, CARD_HEIGHT);
1177 rcUpdate = rcOld.united(rcNew);
1179 repaint(rcUpdate);//this is crappy. should only update what's needed!
1184 // calc new positions
1187 m_pFirstCard->nXPos = nDX, m_pFirstCard->nYPos = nDY;
1188 rcNew = QRect(nDX, nDY, CARD_WIDTH, CARD_HEIGHT);
1189 rcUpdate = rcOld.united(rcNew);
1193 m_bFreeCard = false;
1194 delete m_pFirstCard;
1198 bool GameWidget::IsValidMoveToAce(int nAceM)
1202 for(int k=stackStart.y(); k<19; k++)
1204 if (solBoard[stackStart.x()][k] != -1)
1208 if ((hitStack && (nCards == 1)) || hitDiscard)
1210 int nCardHit = (hitStack ? myDeck.GetCardAtPos(solBoard[stackStart.x()][stackStart.y()])
1211 : myDeck.GetCardAtPos(stack2[stack2p]));
1212 return IsValidMoveToAce(nAceM, nCardHit);
1214 else if (nHitAce != 0)
1216 return IsValidMoveToAce(nAceM, nAce[nHitAce - 1]);
1223 bool GameWidget::IsValidMoveToAce(int nAce, int nCard)
1225 if (((nAce == -1) && ((nCard % 13) == 1))
1226 || ((nAce == (nCard - 1)) && ((nAce % 13) != 0)))
1233 bool GameWidget::IsValidMoveToTableaux(int nStack)
1235 int nBottomCard = -1, nTopCard;
1237 for(int i=0; i<19; i++)
1239 if ((solBoard[nStack][i] != -1) && (myDeck.IsFaceUp(solBoard[nStack][i])))
1240 nBottomCard = myDeck.GetCardAtPos(solBoard[nStack][i]);
1244 nTopCard = myDeck.GetCardAtPos(solBoard[stackStart.x()][stackStart.y()]);
1245 else if (hitDiscard)
1246 nTopCard = myDeck.GetCardAtPos(stack2[stack2p]);
1247 else if (nHitAce != 0)
1248 nTopCard = nAce[nHitAce - 1];
1250 int colorBC = ((nBottomCard > 13) && (nBottomCard < 40) ? 0 : 1);
1251 int colorTC = ((nTopCard > 13) && (nTopCard < 40) ? 0 : 1);
1252 int rankBC = (nBottomCard - 1) % 13;
1253 int rankTC = (nTopCard - 1) % 13;
1255 if (((rankBC == (rankTC + 1)) && (colorBC != colorTC))
1256 || ((nBottomCard == -1) && (rankTC == 12)))
1263 bool GameWidget::PlayerWon(void)
1265 for(int i=0; i<7; i++)
1266 if (solBoard[i][0] != -1)
1269 if ((stack1p != -1) || (stack2p != -1))
1276 void GameWidget::HandleStatistics(void)
1291 if (!m_bWonLastGame)
1297 if (m_nStreakC < 0) // Beat largest losing streak?
1299 if (abs(m_nStreakC) > m_nStreakL)
1300 m_nStreakL = abs(m_nStreakC);
1302 else // Check for longest winning streak
1304 if (m_nStreakC > m_nStreakW)
1305 m_nStreakW = m_nStreakC;
1308 if (m_bVegasStyle) // Check for high score
1310 if (m_nScore > m_nVHiScore)
1311 m_nVHiScore = m_nScore;
1315 if (m_nScore > m_nHiScore)
1316 m_nHiScore = m_nScore;
1322 // Halt processing for 'count' milliseconds
1324 void GameWidget::Pause(int count)
1326 // DWORD endCount = GetTickCount() + count;
1327 // while (GetTickCount() < endCount) {} // Still crude, but better control
1329 usleep(count * 1000);
1331 // This causes it to lock up randomly. :-/
1335 while (time.msec() < count)