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)
24 // score->setTextFormat(Qt::PlainText);
25 setFixedSize(580, 520);
26 // gameBoard = new GameBoard(1);
30 GameWidget::~GameWidget(void)
35 void GameWidget::paintEvent(QPaintEvent * /*event*/)
38 // Optimizations: Create a member variable that has a bitmap in it,
39 // tile it with the felt in OnCreate(), blit as background instead of
40 // FillRect(r, brWhite). Also need to keep track of client rect.
41 // Possibly have 2 bitmaps as member vars, so keep bm construction to a minimum...
43 QPainter painter(this);
44 // QRect rcUpdate = dc.m_ps.rcPaint; // Update rect...
45 // QRect rcUpdate = QRect(0, 0, 580, 520); // Update rect?
50 for(int y=0; y<gameBoard->height; y++)
52 for(int x=0; x<gameBoard->width; x++)
54 int tile = gameBoard->board[ptr++];
55 painter.setPen(QPen(Qt::black, 2.0, Qt::SolidLine));
56 painter.setBrush(QBrush(Qt::black));
60 painter.setBrush(QBrush(Qt::white));
61 painter.drawRect(x * maxLength, y * maxLength, maxLength, maxLength);
63 else if (tile == GTBox)
65 if (!(boxMoving && (movingBoxPositionX == x) && (movingBoxPositionY == y)))
67 painter.setBrush(QBrush(Qt::red));
68 painter.drawRect(x * maxLength, y * maxLength, maxLength, maxLength);
71 else if (tile == GTBoxSpot)
73 painter.setBrush(QBrush(Qt::magenta));
74 painter.drawRect((x * maxLength) + 20, (y * maxLength) + 20, maxLength - 40, maxLength - 40);
76 else if (tile == (GTBox | GTBoxSpot))
78 if (!(boxMoving && (movingBoxPositionX == x) && (movingBoxPositionY == y)))
80 painter.setBrush(QBrush(Qt::green));
81 painter.drawRect(x * maxLength, y * maxLength, maxLength, maxLength);
85 painter.setBrush(QBrush(Qt::magenta));
86 painter.drawRect((x * maxLength) + 20, (y * maxLength) + 20, maxLength - 40, maxLength - 40);
90 if ((gameBoard->playerX == x) && (gameBoard->playerY == y) && !animating)
92 painter.setBrush(QBrush(Qt::yellow));
93 painter.drawEllipse((x * maxLength) + 10, (y * maxLength) + 10, maxLength - 20, maxLength - 20);
99 painter.setBrush(QBrush(Qt::yellow));
100 painter.drawEllipse(playerX + 10, playerY + 10, maxLength - 20, maxLength - 20);
105 painter.setBrush(QBrush(Qt::red));
106 painter.drawRect(boxX, boxY, maxLength, maxLength);
112 DrawBoard(&painter, rcUpdate);
114 // Draw the "loose" cards....
118 for(int i=stackStart.y(); i<19; i++)
120 int card = solBoard[stackStart.x()][i];
124 if (solBoard[stackStart.x()][i + 1] == -1) // Draw full card
125 cdtDraw(&painter, stackPos.x(), stackPos.y() + ((i - stackStart.y()) * 18),
126 myDeck.GetCardAtPos(card), FACEUP);
127 else // Draw partial card
128 cdtDraw(&painter, stackPos.x(), stackPos.y() + ((i - stackStart.y()) * 18),
129 myDeck.GetCardAtPos(card), FACEUP, 20);
135 cdtDraw(&painter, stackPos.x(), stackPos.y(), myDeck.GetCardAtPos(stack2[stack2p]),
139 cdtDraw(&painter, stackPos.x(), stackPos.y(), nAce[nHitAce - 1], FACEUP);
141 if (m_bFreeCard) // For card animations...
143 for(SCardInfo * pCard=m_pFirstCard; pCard; pCard=pCard->pNext)
144 cdtDraw(&painter, pCard->nXPos, pCard->nYPos, pCard->nCard, FACEUP);
150 void GameWidget::mousePressEvent(QMouseEvent * event)
152 if (event->button() == Qt::LeftButton)
154 // OnLButtonDown(event->pos());
160 void GameWidget::mouseMoveEvent(QMouseEvent * event)
162 if (event->buttons() & Qt::LeftButton)
164 // OnMouseMove(event->pos());
170 void GameWidget::mouseReleaseEvent(QMouseEvent * event)
172 if (event->button() == Qt::LeftButton)
174 // OnLButtonUp(event->pos());
180 void GameWidget::mouseDoubleClickEvent(QMouseEvent * event)
182 if (event->button() == Qt::LeftButton)
184 // OnLButtonDblClk(event->pos());
190 void GameWidget::keyPressEvent(QKeyEvent * event)
192 int key = event->key();
193 float deltaX = 0, deltaY = 0;
194 float px = (float)(gameBoard->playerX * maxLength);
195 float py = (float)(gameBoard->playerY * maxLength);
196 float bx = px, by = py;
198 if (key == Qt::Key_Up)
200 boxMoving = gameBoard->IsBoxNOfPlayer();
201 int moveType = gameBoard->MovePlayerN();
203 if (moveType == PMInvalid)
208 else if (key == Qt::Key_Down)
210 boxMoving = gameBoard->IsBoxSOfPlayer();
212 if (gameBoard->MovePlayerS() == PMInvalid)
217 else if (key == Qt::Key_Left)
219 boxMoving = gameBoard->IsBoxWOfPlayer();
221 if (gameBoard->MovePlayerW() == PMInvalid)
226 else if (key == Qt::Key_Right)
228 boxMoving = gameBoard->IsBoxEOfPlayer();
230 if (gameBoard->MovePlayerE() == PMInvalid)
243 movingBoxPositionX = gameBoard->playerX + (int)deltaX;
244 movingBoxPositionY = gameBoard->playerY + (int)deltaY;
245 bx += deltaX * (float)maxLength;
246 by += deltaY * (float)maxLength;
250 float stepSize = (float)maxLength / (float)steps;
251 deltaX *= stepSize, deltaY *= stepSize;
253 for(int i=0; i<steps; i++)
267 animating = boxMoving = false;
269 // Only update if a key we recognize has been pressed!
272 if (gameBoard->GameHasBeenWon())
277 void GameWidget::keyReleaseEvent(QKeyEvent * event)
282 void GameWidget::NextLevel(void)
286 gameBoard = new GameBoard(level);
291 void GameWidget::ResetLevel(void)
293 gameBoard->ResetGame();
299 bool GameWidget::CreateBackground(void)
301 char BGRes[27][64] = {
302 ":/res/grfttile.bmp",
304 ":/res/bg_tech_3.bmp",
305 ":/res/bg_tech_2.bmp",
306 ":/res/bg_tech_1.bmp",
307 ":/res/bg_weave_3.bmp",
308 ":/res/bg_weave_2.bmp",
309 ":/res/bg_clouds_2.bmp",
310 ":/res/bg_floor_plate.bmp",
311 ":/res/bg_marble_b.bmp",
312 ":/res/bg_marble_g.bmp",
313 ":/res/bg_marble_p.bmp",
314 ":/res/bg_marble_r.bmp",
315 ":/res/bg_marble_rb.bmp",
316 ":/res/bg_money_1.bmp",
317 ":/res/bg_pinstripe2.bmp",
318 ":/res/bg_pinstripe7.bmp",
319 ":/res/bg_raindrops_large.bmp",
320 ":/res/bg_raindrops_small.bmp",
321 ":/res/bg_stucco.bmp",
322 ":/res/bg_wood_w.bmp",
323 ":/res/bg_wood_b1.bmp",
324 ":/res/bg_wood_d.bmp",
325 ":/res/bg_wood_f.bmp",
326 ":/res/bg_wood_mh.bmp",
327 ":/res/bg_wood_mv.bmp",
328 ":/res/bg_wood_ro.bmp"
331 QPalette pal = palette();
332 pal.setBrush(backgroundRole(), QBrush(QPixmap(BGRes[m_nBackground])));
333 setAutoFillBackground(true);
336 return true; // Ignore errors for now...
340 void GameWidget::DrawBoard(QPainter * painter, QRect r)
342 // Use "r" as clipping rect--only draw what's necessary
344 // pDC->BitBlt(r.left, r.top, r.Width(), r.Height(), pDC2, r.left, r.top, SRCCOPY);
346 // Draw the solitaire board...
348 for(int i=0; i<7; i++)
350 int y = 126; //130-4;
352 for(int j=0; j<19; j++)
354 int card = solBoard[i][j];
356 if (card != -1 && !(hitStack && (i == stackStart.x()) && (j >= stackStart.y())))
358 int nLinesToDraw = 0; // Default--draws whole card
360 if (solBoard[i][j + 1] != -1 && !(hitStack && (i == stackStart.x()) && (j + 1 >= stackStart.y())))
364 if (myDeck.IsFaceUp(card))
368 int x = 10 + (i * (CARD_WIDTH + 8));//, y = 130 + (j * 4);
373 if (myDeck.IsFaceUp(solBoard[i][j - 1])) // Previous card...
377 //wil wok??? mebbe... YESH!!!!!!!!!!!!!
378 QRect cardRect(x, y, CARD_WIDTH, (nLinesToDraw ? nLinesToDraw : CARD_HEIGHT));
380 if (r.intersects(cardRect))
381 cdtDraw(painter, x, y, (myDeck.IsFaceUp(card) ? myDeck.GetCardAtPos(card)
382 : nCardBack), FACEUP, nLinesToDraw);
387 for(int i=0; i<4; i++)
389 if (nHitAce == i + 1)
391 if (((nAce[i] - 1) % 13) != 0)
392 cdtDraw(painter, 200 + ((CARD_WIDTH + 4) * i), 7, nAce[i] - 1, FACEUP);
394 cdtDraw(painter, 200 + ((CARD_WIDTH + 4) * i), 7, 0, INVISIBLEGHOST);
399 cdtDraw(painter, 200 + ((CARD_WIDTH + 4) * i), 7, nAce[i], FACEUP);
401 cdtDraw(painter, 200 + ((CARD_WIDTH + 4) * i), 7, 0, INVISIBLEGHOST);
406 cdtDraw(painter, 5, 7, nCardBack, FACEUP);
411 cdtDraw(painter, 5, 7, 0, DECKX);
413 cdtDraw(painter, 5, 7, 0, DECKO);
415 cdtDraw(painter, 5, 7, 0, (m_bShowX ? DECKX : DECKO));
423 for(int k=0; k<m_nNumToSplay; k++)
424 cdtDraw(painter, 5 + CARD_WIDTH + 6 + (k * 12), 7 + (k * 2),
425 myDeck.GetCardAtPos(stack2[stack2p - (m_nNumToSplay - k - 1)]), FACEUP);
431 if (m_nNumToSplay == 1)
432 cdtDraw(painter, 5 + CARD_WIDTH + 6, 7, myDeck.GetCardAtPos(stack2[stack2p - 1]), FACEUP);
434 for(int k=0; k<m_nNumToSplay-1; k++)
435 cdtDraw(painter, 5 + CARD_WIDTH + 6 + (k * 12), 7 + (k * 2),
436 myDeck.GetCardAtPos(stack2[stack2p - (m_nNumToSplay - k - 1)]), FACEUP);
443 void GameWidget::OnLButtonDown(QPoint point)
445 dcHitStack = dcHitDiscard = false; // For double clicking...
446 m_bTouchedGame = true; // Game has been attempted!
454 for(int i=0; i<7; i++)
455 for(int j=0; j<20; j++)
456 solBoardUndo[i][j] = solBoard[i][j];
458 for(int i=0; i<24; i++)
459 stack1Undo[i] = stack1[i], stack2Undo[i] = stack2[i];
461 for(int i=0; i<4; i++)
462 nAceUndo[i] = nAce[i];
464 stack1pUndo = stack1p, stack2pUndo = stack2p;
465 m_nScoreUndo = m_nScore;
466 m_nExhaustedDeckUndo = m_nExhaustedDeck;
467 m_nNumToSplayUndo = m_nNumToSplay;
468 m_bShowXUndo = m_bShowX;
471 // Check for a hit on the tableaux...
473 for(int i=0; i<7; i++)
476 r.setLeft(10 + (i * (CARD_WIDTH + 8)));
477 r.setRight(r.left() + CARD_WIDTH - 1);
481 for(int j=0; j<19; j++)
483 if (solBoard[i][j] != -1)
485 if (myDeck.IsFaceUp(solBoard[i][j]))
487 if ((j == 18) || (solBoard[i][j + 1] == -1))
488 r.setBottom(r.top() + CARD_HEIGHT - 1);
490 r.setBottom(r.top() + 17);
492 if (r.contains(point))
494 hitStack = dcHitStack = true;
495 stackStart = QPoint(i, j);
496 mouseOffset = QPoint(point.x() - r.left(), point.y() - r.top());
497 stackPos = QPoint(point.x() - mouseOffset.x(), point.y() - mouseOffset.y());
498 QRect rcUpdate(r.left(), r.top(), r.width(), CARD_HEIGHT - 1); //wil wok?
502 r.setTop(r.top() + 18);
508 if (solBoard[i][j + 1] == -1)
510 r.setBottom(r.top() + CARD_HEIGHT - 1);
512 if (r.contains(point))
514 myDeck.ToggleCardFacing(solBoard[i][j]); // Turn over that last card
518 PlaySound(IDW_CARDFLIP);
521 update(r); //wil wok?
527 emit UpdateScore(m_nScore);
535 r.setTop(r.top() + 4);
543 // Check for hit on discard stack
545 int nCX = 5 + CARD_WIDTH + 6 + ((m_nNumToSplay - 1) * 12);
546 int nCY = 7 + ((m_nNumToSplay - 1) * 2);
548 if (QRect(nCX, nCY, CARD_WIDTH - 1, CARD_HEIGHT - 1).contains(point))
552 hitDiscard = dcHitDiscard = true;
553 mouseOffset = QPoint(point.x() - (5 + CARD_WIDTH + 6 + ((m_nNumToSplay - 1) * 12)),
554 point.y() - (7 + ((m_nNumToSplay - 1) * 2)));
555 stackPos = QPoint(point.x() - mouseOffset.x(), point.y() - mouseOffset.y());
560 // Check for hit on remaining deck
562 if (QRect(5, 7, 5 + CARD_WIDTH - 1, 7 + CARD_HEIGHT - 1).contains(point))
566 if (stack1p == -1) // I.e. it's empty...
569 if ((m_bDrawThree && (m_nExhaustedDeck == 3))
570 || (!m_bDrawThree && (m_nExhaustedDeck == 1)))
574 m_nExhaustedDeck = 0;
575 m_nScore -= (m_bDrawThree ? 20 : 100);
580 emit UpdateScore(m_nScore);
583 if (!m_bVegasStyle || (m_bVegasStyle && ((m_bDrawThree && (m_nExhaustedDeck < 3))
584 || (!m_bDrawThree && (m_nExhaustedDeck < 1)))))
586 for(; stack2p != -1; stack2p--)
589 stack1[stack1p] = stack2[stack2p];
594 PlaySound(IDW_CARDKNOCK);
602 for(int i=0; i<(m_bDrawThree ? 3 : 1); i++)
604 if (stack1p != -1) // I.e., there are cards left...
606 stack2[++stack2p] = stack1[stack1p--];
611 PlaySound(IDW_CARDFLIP);
621 if (stack1p == -1) // This is done here because it's right!
625 if (m_bVegasStyle && ((m_bDrawThree && (m_nExhaustedDeck == 3))
626 || (!m_bDrawThree && (m_nExhaustedDeck == 1))))
631 mouseOffset = QPoint(point.x() - 5, point.y() - 7);
633 QRect inv1(5, 7, CARD_WIDTH, CARD_HEIGHT),
634 inv2(5 + CARD_WIDTH + 6, 7, (2 * 12) + CARD_WIDTH, (2 * 2) + CARD_HEIGHT);
639 // Check for hit on ace piles
641 for(int i=0; i<4; i++)
643 QRect aceRect(200 + ((CARD_WIDTH + 4) * i), 7, CARD_WIDTH - 1, CARD_HEIGHT - 1);
645 if (aceRect.contains(point) && (nAce[i] > -1))
648 // mouseOffset = QPoint(point.x() - (200 + ((CARD_WIDTH + 4) * i)), point.y() - 7);
649 mouseOffset = QPoint(point.x() - aceRect.x(), point.y() - aceRect.y());
650 stackPos = QPoint(point.x() - mouseOffset.x(), point.y() - mouseOffset.y());
657 void GameWidget::OnLButtonUp(QPoint point)
662 point -= mouseOffset; // ?? bad form!
664 if (hitStack || hitDiscard || (nHitAce != 0))
668 for(int i=0; i<7; i++) // Compute valid rects at bottoms of stacks...
670 tabl[i].setLeft(10 + (i * (CARD_WIDTH + 8)));
671 tabl[i].setRight(tabl[i].left() + CARD_WIDTH - 1);
674 for(int j=0; j<19; j++)
676 if ((solBoard[i][j] == -1)
677 || (hitStack && (i == stackStart.x()) && (j == stackStart.y())))
681 for(int k=0; k<j-1; k++)
683 if (!myDeck.IsFaceUp(solBoard[i][k]))
684 tabl[i].setTop(tabl[i].top() + 4);
686 tabl[i].setTop(tabl[i].top() + 17);
690 tabl[i].setBottom(tabl[i].top() + CARD_HEIGHT - 1);
696 // stackPos = QPoint(point.x() - mouseOffset.x(), point.y() - mouseOffset.y());
697 // QRect c(point.x(), point.y(), CARD_WIDTH - 1, CARD_HEIGHT - 1);
698 // QRect c(point.x() - mouseOffset.x(), point.y() - mouseOffset.y(), CARD_WIDTH - 1, CARD_HEIGHT - 1);
699 QRect c(stackPos.x(), stackPos.y(), CARD_WIDTH - 1, CARD_HEIGHT - 1);
701 a[0] = QRect(200, 7, CARD_WIDTH - 1, CARD_HEIGHT - 1);
702 a[1] = QRect(200 + ((CARD_WIDTH + 4) * 1), 7, CARD_WIDTH - 1, CARD_HEIGHT - 1);
703 a[2] = QRect(200 + ((CARD_WIDTH + 4) * 2), 7, CARD_WIDTH - 1, CARD_HEIGHT - 1);
704 a[3] = QRect(200 + ((CARD_WIDTH + 4) * 3), 7, CARD_WIDTH - 1, CARD_HEIGHT - 1);
707 bHitAce[0] = c.intersects(a[0]);
708 bHitAce[1] = c.intersects(a[1]);
709 bHitAce[2] = c.intersects(a[2]);
710 bHitAce[3] = c.intersects(a[3]);
712 // Check to see if hit more than one ace & pick the one that's closer
713 for(int i=0; i<3; i++)
715 if (bHitAce[i] && bHitAce[i + 1] && (nAce[i] == -1) && (nAce[i + 1] == -1))
717 if (c.intersected(a[i]).width() > c.intersected(a[i + 1]).width())
718 bHitAce[i + 1] = false;
726 bool bMovedToAce = false;
728 for(int i=0; i<4; i++)
730 if (bHitAce[i] && IsValidMoveToAce(nAce[i]))
735 nAce[i] = myDeck.GetCardAtPos(solBoard[stackStart.x()][stackStart.y()]),
736 solBoard[stackStart.x()][stackStart.y()] = -1;
740 nAce[i] = myDeck.GetCardAtPos(stack2[stack2p--]);
742 if (m_nNumToSplay != 1)
748 nAce[i] = nAce[nHitAce - 1];
749 int nCard = nAce[nHitAce - 1];
750 nAce[nHitAce - 1] = ((nCard - 1) % 13 == 0 ? -1 : nCard - 1);
756 PlaySound(IDW_CARDPLACE);
768 m_nScore += (m_bVegasStyle ? 5 : 10);
771 emit UpdateScore(m_nScore);
776 if (!bMovedToAce) // No aces, so check for stacks
778 for(int i=0; i<7; i++)
780 if (c.intersects(tabl[i]) && IsValidMoveToTableaux(i))
786 PlaySound(IDW_CARDPLACE);
789 if (hitStack && (i != stackStart.x()))
791 int cnt = stackStart.y();
793 for(int k=0; k<19; k++)
795 if (solBoard[i][k] == -1)
797 solBoard[i][k] = solBoard[stackStart.x()][cnt];
798 solBoard[stackStart.x()][cnt] = -1;
802 if (solBoard[stackStart.x()][cnt] == -1) // Done copying?
811 for(int k=0; k<19; k++)
813 if (solBoard[i][k] == -1)
815 solBoard[i][k] = stack2[stack2p--];
816 myDeck.SetCardFaceUp(solBoard[i][k]);
822 emit UpdateScore(m_nScore);
825 if (m_nNumToSplay != 1)
837 // handle moving to tableaux here...
838 for(int k=0; k<19; k++)
840 if (solBoard[i][k] == -1)
842 int nCard = nAce[nHitAce - 1];
844 for(int t=0; t<52; t++) // We have to find da damn thing...
846 if (myDeck.GetCardAtPos(t) == nCard)
853 nAce[nHitAce - 1] = ((nCard - 1) % 13 == 0 ? -1 : nCard - 1);
854 myDeck.SetCardFaceUp(solBoard[i][k]);
863 m_nScore -= (m_bVegasStyle ? 5 : 15);
866 emit UpdateScore(m_nScore);
880 mouseDown = hitStack = hitDiscard = false;
888 emit UpdateScore(m_nScore);
890 m_bWonLastGame = true;
896 QString id[] = { IDW_WINNER1, IDW_WINNER2, IDW_WINNER3, IDW_WINNER4, IDW_WINNER5 };
897 srand((unsigned)time(NULL));
898 int nWaveNum = rand() % 5;
899 PlaySound(id[nWaveNum]);
903 QMessageBox::information(this, "MAC Solitaire", "Congratulations!\nYou won!");
904 allowUserInput = false;
909 void GameWidget::OnMouseMove(QPoint point)
911 if (mouseDown && (hitStack || hitDiscard || (nHitAce != 0)))
917 for(int i=stackStart.y(); i<19; i++)
918 if (solBoard[stackStart.x()][i] != -1)
924 nHeight += CARD_HEIGHT - 18;
926 QRect rcOld(stackPos.x(), stackPos.y(), CARD_WIDTH,
927 nHeight); // Find old position for repainting...
929 stackPos = QPoint(point.x() - mouseOffset.x(), point.y() - mouseOffset.y());
931 QRect rcNew(stackPos.x(), stackPos.y(), CARD_WIDTH,
932 nHeight); // Find new position
933 QRect rcUpdate = rcOld.united(rcNew);
934 update(rcUpdate); //wil wok? YESH!
939 void GameWidget::OnLButtonDblClk(QPoint point)
941 if (dcHitStack || dcHitDiscard)
943 hitStack = dcHitStack, hitDiscard = dcHitDiscard;
945 for(int i=0; i<4; i++)
947 if (IsValidMoveToAce(nAce[i]))
951 nAce[i] = myDeck.GetCardAtPos(solBoard[stackStart.x()][stackStart.y()]);
952 solBoard[stackStart.x()][stackStart.y()] = -1;
956 nAce[i] = myDeck.GetCardAtPos(stack2[stack2p--]);
958 if (m_nNumToSplay != 1)
976 m_nScore += (m_bVegasStyle ? 5 : 10);
979 emit UpdateScore(m_nScore);
984 dcHitStack = hitStack = dcHitDiscard = hitDiscard = false;
987 OnLButtonDown(point); // Either was on draw pile or somewhere else...
991 // It's behaving strangely, especially with kings in the ace piles...
992 // It's becuase of the mod 13 function: kings(13) get whacked to 0 this way...
993 void GameWidget::HandleAutoRemove(void)
1001 int nLowestAce = (nAce[0] == -1 ? 0 : ((nAce[0] - 1) % 13) + 1); // Aces are 1-based, cards 1-based
1003 for(int i=1; i<4; i++)
1005 int nTemp = (nAce[i] == -1 ? 0 : ((nAce[i] - 1) % 13) + 1);
1007 if (nTemp < nLowestAce)
1011 for(int i=0; i<7; i++) // Compare cards that *can* go
1013 for(int j=18; j>=0; j--)
1015 if (solBoard[i][j] != -1)
1017 if (myDeck.IsFaceUp(solBoard[i][j]))
1019 int nCard = myDeck.GetCardAtPos(solBoard[i][j]);
1021 for(int k=0; k<4; k++)
1023 if (IsValidMoveToAce(nAce[k], nCard))
1025 // Ranking/suiting doesn't work if nAce == -1... FIX!
1026 // Should be fixed now...
1027 // Figure out some way to simplify this tortuous logic, for cryin' out loud!
1028 int nSuit[4], nRank[4];
1030 for(int t=0; t<4; t++)
1033 nSuit[t] = ((nAce[t] > 13) && (nAce[t] < 40) ? 0 : 1);
1037 nRank[t] = ((nAce[t] - 1) % 13) + 1;
1040 int nCardSuit = ((nCard > 13) && (nCard < 40) ? 0 : 1),
1041 nCardRank = ((nCard - 1) % 13) + 1;
1042 bool bSpecial = false;
1043 int nCR[2], nCnt = 0;
1045 for(int t=0; t<4; t++)
1046 if ((nCardSuit != nSuit[t]) && (t != k) && (nSuit[t] != -1))
1047 nCR[nCnt++] = nRank[t];
1049 if ((nCnt == 2) && (nCR[0] == nCR[1])
1050 && (nCR[0] - nLowestAce == 2) && (nCardRank - nLowestAce == 3))
1053 if (((((nCard - 1) % 13) + 1) - nLowestAce) <= 2 || bSpecial)
1054 // OR difference is 3 and both opposite color are at 2
1055 // if (((nCard%13) - nLowestAce) <= 2)
1057 solBoard[i][j] = -1;
1059 if (m_bAnimationsOn)
1060 AnimateCards(nCard, k, i, j);
1072 m_nScore += (m_bVegasStyle ? 5 : 10);
1074 emit UpdateScore(m_nScore);
1088 int nCard = myDeck.GetCardAtPos(stack2[stack2p]);
1090 for(int k=0; k<4; k++)
1092 if (IsValidMoveToAce(nAce[k], nCard))
1094 // Ranking/suiting doesn't work if nAce == -1... FIX!
1095 // Should be fixed now...
1096 // Figure out some way to simplify this tortuous logic, for cryin' out loud!
1097 int nSuit[4], nRank[4];
1099 for(int t=0; t<4; t++)
1102 nSuit[t] = ((nAce[t] > 13) && (nAce[t] < 40) ? 0 : 1);
1106 nRank[t] = ((nAce[t] - 1) % 13) + 1;
1109 int nCardSuit = ((nCard > 13) && (nCard < 40) ? 0 : 1),
1110 nCardRank = ((nCard - 1) % 13) + 1;
1111 bool bSpecial = false;
1112 int nCR[2], nCnt = 0;
1114 for(int t=0; t<4; t++)
1116 if ((nCardSuit != nSuit[t]) && (t != k) && (nSuit[t] != -1))
1117 nCR[nCnt++] = nRank[t];
1120 if ((nCnt == 2) && (nCR[0] == nCR[1])
1121 && (nCR[0] - nLowestAce == 2) && (nCardRank - nLowestAce == 3))
1124 if (((((nCard - 1) % 13) + 1) - nLowestAce) <= 2 || bSpecial)
1125 // OR difference is 3 and both opposite color are at 2 (tortuously checked above)
1129 if (m_nNumToSplay != 1)
1132 if (m_bAnimationsOn)
1133 AnimateCards(nCard, k, -1, -1);
1145 m_nScore += (m_bVegasStyle ? 5 : 10);
1148 emit UpdateScore(m_nScore);
1158 // Have two routines? One to initiate, the other to handle card movements?
1159 // Good idea! Implement it, you schmendrick!
1160 void GameWidget::AnimateCards(int nCard, int nAce, int nTabX, int nTabY)
1162 //no mas--jeff no likee CWaitCursor wc; // Automagically reverts when it goes out of scope
1163 int nCX, nCY, nDX, nDY;
1165 // Step 1: figure out where card started from
1168 // nCX = 5+CARD_WIDTH+6, nCY = 5;
1169 nCX = 5 + CARD_WIDTH + 6 + ((m_nNumToSplay - 1) * 12);
1170 nCY = 7 + ((m_nNumToSplay - 1) * 2);
1174 nCX = 10 + (nTabX * (CARD_WIDTH + 8)), nCY = 130;
1176 for(int j=0; j<nTabY; j++)
1178 if (solBoard[nTabX][j] != -1)
1180 if (myDeck.IsFaceUp(solBoard[nTabX][j]))
1187 // Step 2: figure out where card is going
1188 nDX = 200 + ((CARD_WIDTH + 4) * nAce), nDY = 7;
1189 // Step 3: animate card flying from start to destination
1190 // Use Bresenham's algorithm to figure path...
1191 // (ideas for movement: increasing acceleration, curved paths, more than one at a time)
1193 m_pFirstCard = new SCardInfo(nCard);
1194 int nDeltaX = nDX - nCX, nDeltaY = nDY - nCY;
1197 update(); //will do it??? Seems to...
1198 //Doing this causes an image of the card to be drawn at (0, 0) :-/
1200 QRect rcOld(nCX, nCY, CARD_WIDTH, CARD_HEIGHT), rcNew, rcUpdate;
1202 if (abs(nDeltaX) > abs(nDeltaY))
1204 for(int i=0; i<abs(nDeltaX); i++)
1206 // Put card draw stuff into OnDraw? (probably best way to do it...)
1207 nError += abs(nDeltaY);
1209 if (nError > abs(nDeltaX))
1210 nCY += (nDeltaY < 0 ? -1 : +1), nError -= abs(nDeltaX);
1212 m_pFirstCard->nXPos = nCX + (nDeltaX < 0 ? -i : +i), m_pFirstCard->nYPos = nCY;
1218 rcNew = QRect(m_pFirstCard->nXPos, m_pFirstCard->nYPos,
1219 CARD_WIDTH, CARD_HEIGHT);
1220 rcUpdate = rcOld.united(rcNew);
1227 // calc new positions
1230 m_pFirstCard->nXPos = nDX, m_pFirstCard->nYPos = nDY;
1231 rcNew = QRect(nDX, nDY, CARD_WIDTH, CARD_HEIGHT);
1232 rcUpdate = rcOld.united(rcNew);
1237 for(int i=0; i<abs(nDeltaY); i++)
1239 // Put card draw stuff into OnDraw? (probably best way to do it...)
1240 nError += abs(nDeltaX);
1242 if (nError > abs(nDeltaY))
1243 nCX += (nDeltaX < 0 ? -1 : +1), nError -= abs(nDeltaY);
1245 m_pFirstCard->nXPos = nCX, m_pFirstCard->nYPos = nCY + (nDeltaY < 0 ? -i : +i);
1251 rcNew = QRect(m_pFirstCard->nXPos, m_pFirstCard->nYPos,
1252 CARD_WIDTH, CARD_HEIGHT);
1253 rcUpdate = rcOld.united(rcNew);
1255 repaint(rcUpdate);//this is crappy. should only update what's needed!
1260 // calc new positions
1263 m_pFirstCard->nXPos = nDX, m_pFirstCard->nYPos = nDY;
1264 rcNew = QRect(nDX, nDY, CARD_WIDTH, CARD_HEIGHT);
1265 rcUpdate = rcOld.united(rcNew);
1269 m_bFreeCard = false;
1270 delete m_pFirstCard;
1274 bool GameWidget::IsValidMoveToAce(int nAceM)
1278 for(int k=stackStart.y(); k<19; k++)
1280 if (solBoard[stackStart.x()][k] != -1)
1284 if ((hitStack && (nCards == 1)) || hitDiscard)
1286 int nCardHit = (hitStack ? myDeck.GetCardAtPos(solBoard[stackStart.x()][stackStart.y()])
1287 : myDeck.GetCardAtPos(stack2[stack2p]));
1288 return IsValidMoveToAce(nAceM, nCardHit);
1290 else if (nHitAce != 0)
1292 return IsValidMoveToAce(nAceM, nAce[nHitAce - 1]);
1299 bool GameWidget::IsValidMoveToAce(int nAce, int nCard)
1301 if (((nAce == -1) && ((nCard % 13) == 1))
1302 || ((nAce == (nCard - 1)) && ((nAce % 13) != 0)))
1309 bool GameWidget::IsValidMoveToTableaux(int nStack)
1311 int nBottomCard = -1, nTopCard;
1313 for(int i=0; i<19; i++)
1315 if ((solBoard[nStack][i] != -1) && (myDeck.IsFaceUp(solBoard[nStack][i])))
1316 nBottomCard = myDeck.GetCardAtPos(solBoard[nStack][i]);
1320 nTopCard = myDeck.GetCardAtPos(solBoard[stackStart.x()][stackStart.y()]);
1321 else if (hitDiscard)
1322 nTopCard = myDeck.GetCardAtPos(stack2[stack2p]);
1323 else if (nHitAce != 0)
1324 nTopCard = nAce[nHitAce - 1];
1326 int colorBC = ((nBottomCard > 13) && (nBottomCard < 40) ? 0 : 1);
1327 int colorTC = ((nTopCard > 13) && (nTopCard < 40) ? 0 : 1);
1328 int rankBC = (nBottomCard - 1) % 13;
1329 int rankTC = (nTopCard - 1) % 13;
1331 if (((rankBC == (rankTC + 1)) && (colorBC != colorTC))
1332 || ((nBottomCard == -1) && (rankTC == 12)))
1339 bool GameWidget::PlayerWon(void)
1341 for(int i=0; i<7; i++)
1342 if (solBoard[i][0] != -1)
1345 if ((stack1p != -1) || (stack2p != -1))
1352 void GameWidget::HandleStatistics(void)
1367 if (!m_bWonLastGame)
1373 if (m_nStreakC < 0) // Beat largest losing streak?
1375 if (abs(m_nStreakC) > m_nStreakL)
1376 m_nStreakL = abs(m_nStreakC);
1378 else // Check for longest winning streak
1380 if (m_nStreakC > m_nStreakW)
1381 m_nStreakW = m_nStreakC;
1384 if (m_bVegasStyle) // Check for high score
1386 if (m_nScore > m_nVHiScore)
1387 m_nVHiScore = m_nScore;
1391 if (m_nScore > m_nHiScore)
1392 m_nHiScore = m_nScore;
1399 // Halt processing for 'count' milliseconds
1401 void GameWidget::Pause(int count)
1403 // DWORD endCount = GetTickCount() + count;
1404 // while (GetTickCount() < endCount) {} // Still crude, but better control
1406 usleep(count * 1000);
1408 // This causes it to lock up randomly. :-/
1412 while (time.msec() < count)