]> Shamusworld >> Repos - warehouse-man-deluxe/blob - src/gamewidget.cpp
837edd8b55aea62fab3b5d8dcfc16d44a145b28c
[warehouse-man-deluxe] / src / gamewidget.cpp
1 //
2 // gamewidget.cpp: Main game window widget
3 //
4 // by James Hammons
5 // (C) 2013 Underground Software
6 //
7 // JLH = James Hammons <jlhamm@acm.org>
8 //
9 // Who  When        What
10 // ---  ----------  ------------------------------------------------------------
11 // JLH  03/01/2014  Created this file
12 //
13
14 #include "gamewidget.h"
15 #include <unistd.h>             // for usleep()
16 #include "gameboard.h"
17
18
19 GameWidget::GameWidget(QWidget * parent/*= 0*/): QWidget(parent)//,
20 //      score(new QLabel)
21 {
22 //      score->setTextFormat(Qt::PlainText);
23         setFixedSize(580, 520);
24         gameBoard = new GameBoard(1);
25 }
26
27
28 GameWidget::~GameWidget(void)
29 {
30 }
31
32
33 void GameWidget::paintEvent(QPaintEvent * /*event*/)
34 {
35         // TO DO:
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...
40
41         QPainter painter(this);
42 //      QRect rcUpdate = dc.m_ps.rcPaint; // Update rect...
43 //      QRect rcUpdate = QRect(0, 0, 580, 520);                 // Update rect?
44
45         int maxLength = 60;
46         int ptr = 0;
47
48         for(int y=0; y<gameBoard->height; y++)
49         {
50                 for(int x=0; x<gameBoard->width; x++)
51                 {
52                         int tile = gameBoard->board[ptr++];
53                         painter.setPen(QPen(Qt::black, 2.0, Qt::SolidLine));
54                         painter.setBrush(QBrush(Qt::black));
55
56                         if (tile == GTWall)
57                         {
58                                 painter.setBrush(QBrush(Qt::white));
59                                 painter.drawRect(x * maxLength, y * maxLength, maxLength, maxLength);
60                         }
61                         else if (tile == GTBox)
62                         {
63                                 painter.setBrush(QBrush(Qt::red));
64                                 painter.drawRect(x * maxLength, y * maxLength, maxLength, maxLength);
65                         }
66                         else if (tile == GTBoxSpot)
67                         {
68                                 painter.setBrush(QBrush(Qt::magenta));
69                                 painter.drawRect((x * maxLength) + 20, (y * maxLength) + 20, maxLength - 40, maxLength - 40);
70                         }
71                         else if (tile == (GTBox | GTBoxSpot))
72                         {
73                                 painter.setBrush(QBrush(Qt::green));
74                                 painter.drawRect(x * maxLength, y * maxLength, maxLength, maxLength);
75                         }
76
77                         if ((gameBoard->playerX == x) && (gameBoard->playerY == y))
78                         {
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);
82                         }
83                 }
84         }
85
86
87 #if 0
88         DrawBoard(&painter, rcUpdate);
89
90         // Draw the "loose" cards....
91
92         if (hitStack)
93         {
94                 for(int i=stackStart.y(); i<19; i++)
95                 {
96                         int card = solBoard[stackStart.x()][i];
97
98                         if (card != -1)
99                         {
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);
106                         }
107                 }
108         }
109
110         if (hitDiscard)
111                 cdtDraw(&painter, stackPos.x(), stackPos.y(), myDeck.GetCardAtPos(stack2[stack2p]),
112                         FACEUP);
113
114         if (nHitAce != 0)
115                 cdtDraw(&painter, stackPos.x(), stackPos.y(), nAce[nHitAce - 1], FACEUP);
116
117         if (m_bFreeCard)  // For card animations...
118         {
119                 for(SCardInfo * pCard=m_pFirstCard; pCard; pCard=pCard->pNext)
120                         cdtDraw(&painter, pCard->nXPos, pCard->nYPos, pCard->nCard, FACEUP);
121         }
122 #endif
123 }
124
125
126 void GameWidget::mousePressEvent(QMouseEvent * event)
127 {
128         if (event->button() == Qt::LeftButton)
129         {
130 //              OnLButtonDown(event->pos());
131                 event->accept();
132         }
133 }
134
135
136 void GameWidget::mouseMoveEvent(QMouseEvent * event)
137 {
138         if (event->buttons() & Qt::LeftButton)
139         {
140 //              OnMouseMove(event->pos());
141                 event->accept();
142         }
143 }
144
145
146 void GameWidget::mouseReleaseEvent(QMouseEvent * event)
147 {
148         if (event->button() == Qt::LeftButton)
149         {
150 //              OnLButtonUp(event->pos());
151                 event->accept();
152         }
153 }
154
155
156 void GameWidget::mouseDoubleClickEvent(QMouseEvent * event)
157 {
158         if (event->button() == Qt::LeftButton)
159         {
160 //              OnLButtonDblClk(event->pos());
161                 event->accept();
162         }
163 }
164
165
166 void GameWidget::keyPressEvent(QKeyEvent * event)
167 {
168         int key = event->key();
169
170         if (key == Qt::Key_Up)
171         {
172                 if (gameBoard->MovePlayerN() == PMInvalid)
173                         return;
174         }
175         else if (key == Qt::Key_Down)
176         {
177                 if (gameBoard->MovePlayerS() == PMInvalid)
178                         return;
179         }
180         else if (key == Qt::Key_Left)
181         {
182                 if (gameBoard->MovePlayerW() == PMInvalid)
183                         return;
184         }
185         else if (key == Qt::Key_Right)
186         {
187                 if (gameBoard->MovePlayerE() == PMInvalid)
188                         return;
189         }
190         else
191                 return;
192
193         // Only update if a key we recognize has been pressed!
194         update();
195
196         if (gameBoard->GameHasBeenWon())
197                 emit GameWasWon();
198 }
199
200
201 void GameWidget::keyReleaseEvent(QKeyEvent * event)
202 {
203 }
204
205
206 void GameWidget::NextLevel(void)
207 {
208         level++;
209         delete gameBoard;
210         gameBoard = new GameBoard(level);
211         update();
212 }
213
214
215 void GameWidget::ResetLevel(void)
216 {
217         gameBoard->ResetGame();
218         update();
219 }
220
221
222 #if 0
223 bool GameWidget::CreateBackground(void)
224 {
225         char BGRes[27][64] = {
226                 ":/res/grfttile.bmp",
227                 ":/res/cloth_6.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"
253         };
254
255         QPalette pal = palette();
256         pal.setBrush(backgroundRole(), QBrush(QPixmap(BGRes[m_nBackground])));
257         setAutoFillBackground(true);
258         setPalette(pal);
259
260         return true; // Ignore errors for now...
261 }
262
263
264 void GameWidget::DrawBoard(QPainter * painter, QRect r)
265 {
266         // Use "r" as clipping rect--only draw what's necessary
267
268 //      pDC->BitBlt(r.left, r.top, r.Width(), r.Height(), pDC2, r.left, r.top, SRCCOPY);
269
270         // Draw the solitaire board...
271
272         for(int i=0; i<7; i++)
273         {
274                 int y = 126; //130-4;
275
276                 for(int j=0; j<19; j++)
277                 {
278                         int card = solBoard[i][j];
279
280                         if (card != -1 && !(hitStack && (i == stackStart.x()) && (j >= stackStart.y())))
281                         {
282                                 int nLinesToDraw = 0; // Default--draws whole card
283
284                                 if (solBoard[i][j + 1] != -1 && !(hitStack && (i == stackStart.x()) && (j + 1 >= stackStart.y())))
285                                 {
286                                         nLinesToDraw = 6;
287
288                                         if (myDeck.IsFaceUp(card))
289                                                 nLinesToDraw += 14;
290                                 }
291
292                                 int x = 10 + (i * (CARD_WIDTH + 8));//, y = 130 + (j * 4);
293                                 y += 4;
294
295                                 if (j > 0)
296                                 {
297                                         if (myDeck.IsFaceUp(solBoard[i][j - 1]))  // Previous card...
298                                                 y += 14;
299                                 }
300
301                                 //wil wok??? mebbe... YESH!!!!!!!!!!!!!
302                                 QRect cardRect(x, y, CARD_WIDTH, (nLinesToDraw ? nLinesToDraw : CARD_HEIGHT));
303
304                                 if (r.intersects(cardRect))
305                                         cdtDraw(painter, x, y, (myDeck.IsFaceUp(card) ? myDeck.GetCardAtPos(card)
306                                                 : nCardBack), FACEUP, nLinesToDraw);
307                         }
308                 }
309         }
310
311         for(int i=0; i<4; i++)
312         {
313                 if (nHitAce == i + 1)
314                 {
315                         if (((nAce[i] - 1) % 13) != 0)
316                                 cdtDraw(painter, 200 + ((CARD_WIDTH + 4) * i), 7, nAce[i] - 1, FACEUP);
317                         else
318                                 cdtDraw(painter, 200 + ((CARD_WIDTH + 4) * i), 7, 0, INVISIBLEGHOST);
319                 }
320                 else
321                 {
322                         if (nAce[i] != -1)
323                                 cdtDraw(painter, 200 + ((CARD_WIDTH + 4) * i), 7, nAce[i], FACEUP);
324                         else
325                                 cdtDraw(painter, 200 + ((CARD_WIDTH + 4) * i), 7, 0, INVISIBLEGHOST);
326                 }
327         }
328         
329         if (stack1p != -1)
330                 cdtDraw(painter, 5, 7, nCardBack, FACEUP);
331         else
332         {
333 #if 0
334                 if (m_bShowX)
335                         cdtDraw(painter, 5, 7, 0, DECKX);
336                 else
337                         cdtDraw(painter, 5, 7, 0, DECKO);
338 #else
339                 cdtDraw(painter, 5, 7, 0, (m_bShowX ? DECKX : DECKO));
340 #endif
341         }
342
343         if (stack2p != -1)
344         {
345                 if (!hitDiscard)
346                 {
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);
350                 }
351                 else
352                 {
353                         if (stack2p != 0)
354                         {
355                                 if (m_nNumToSplay == 1)
356                                         cdtDraw(painter, 5 + CARD_WIDTH + 6, 7, myDeck.GetCardAtPos(stack2[stack2p - 1]), FACEUP);
357                                 else
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);
361                         }
362                 }
363         }
364 }
365
366
367 void GameWidget::OnLButtonDown(QPoint point) 
368 {
369         dcHitStack = dcHitDiscard = false;  // For double clicking...
370         m_bTouchedGame = true;              // Game has been attempted!
371
372         if (!allowUserInput)
373                 return;
374
375         mouseDown = true;
376
377         // Set undo up...
378         for(int i=0; i<7; i++)
379                 for(int j=0; j<20; j++)
380                         solBoardUndo[i][j] = solBoard[i][j];
381
382         for(int i=0; i<24; i++)
383                 stack1Undo[i] = stack1[i], stack2Undo[i] = stack2[i];
384
385         for(int i=0; i<4; i++)
386                 nAceUndo[i] = nAce[i];
387
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;
393         m_bCanUndo = false;
394
395         // Check for a hit on the tableaux...
396
397         for(int i=0; i<7; i++)
398         {
399                 QRect r;
400                 r.setLeft(10 + (i * (CARD_WIDTH + 8)));
401                 r.setRight(r.left() + CARD_WIDTH - 1);
402                 r.setTop(130);
403                 int nCards = 0;
404
405                 for(int j=0; j<19; j++)
406                 {
407                         if (solBoard[i][j] != -1)
408                         {
409                                 if (myDeck.IsFaceUp(solBoard[i][j]))
410                                 {
411                                         if ((j == 18) || (solBoard[i][j + 1] == -1))
412                                                 r.setBottom(r.top() + CARD_HEIGHT - 1);
413                                         else
414                                                 r.setBottom(r.top() + 17);
415
416                                         if (r.contains(point))
417                                         {
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?
423                                                 return;
424                                         }
425
426                                         r.setTop(r.top() + 18);
427                                 }
428                                 else
429                                 {
430                                         if (j != 18)
431                                         {
432                                                 if (solBoard[i][j + 1] == -1)
433                                                 {
434                                                         r.setBottom(r.top() + CARD_HEIGHT - 1);
435
436                                                         if (r.contains(point))
437                                                         {
438                                                                 myDeck.ToggleCardFacing(solBoard[i][j]);  // Turn over that last card
439
440 #if HAVE_SOUND
441                                                                 if (bSoundOn)
442                                                                         PlaySound(IDW_CARDFLIP);
443 #endif
444
445                                                                 update(r); //wil wok?
446
447                                                                 // Scoring...
448                                                                 if (!m_bVegasStyle)
449                                                                 {
450                                                                         m_nScore += 5;
451                                                                         emit UpdateScore(m_nScore);
452                                                                 }
453
454                                                                 return;
455                                                         }
456                                                 }
457                                         }
458
459                                         r.setTop(r.top() + 4);
460                                 }
461
462                                 nCards++;
463                         }
464                 }
465         }
466
467         // Check for hit on discard stack
468
469         int nCX = 5 + CARD_WIDTH + 6 + ((m_nNumToSplay - 1) * 12);
470         int nCY = 7 + ((m_nNumToSplay - 1) * 2);
471
472         if (QRect(nCX, nCY, CARD_WIDTH - 1, CARD_HEIGHT - 1).contains(point))
473         {
474                 if (stack2p != -1)
475                 {
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());
480                         return;
481                 }
482         }
483
484         // Check for hit on remaining deck
485
486         if (QRect(5, 7, 5 + CARD_WIDTH - 1, 7 + CARD_HEIGHT - 1).contains(point))
487         {
488                 m_bCanUndo = true;
489
490                 if (stack1p == -1)  // I.e. it's empty...
491                 {
492                         // Scoring...
493                         if ((m_bDrawThree && (m_nExhaustedDeck == 3))
494                                 || (!m_bDrawThree && (m_nExhaustedDeck == 1)))
495                         {
496                                 if (!m_bVegasStyle)
497                                 {
498                                         m_nExhaustedDeck = 0;
499                                         m_nScore -= (m_bDrawThree ? 20 : 100);
500
501                                         if (m_nScore < 0)
502                                                 m_nScore = 0;
503
504                                         emit UpdateScore(m_nScore);
505                                 }
506                         }
507                         if (!m_bVegasStyle || (m_bVegasStyle && ((m_bDrawThree && (m_nExhaustedDeck < 3))
508                                         || (!m_bDrawThree && (m_nExhaustedDeck < 1)))))
509                         {
510                                 for(; stack2p != -1; stack2p--)
511                                 {
512                                         stack1p++;
513                                         stack1[stack1p] = stack2[stack2p];
514                                 }
515
516 #if HAVE_SOUND
517                                 if (bSoundOn)
518                                         PlaySound(IDW_CARDKNOCK);
519 #endif
520                         }
521                 }
522                 else
523                 {
524                         m_nNumToSplay = 0;
525
526                         for(int i=0; i<(m_bDrawThree ? 3 : 1); i++)
527                         {
528                                 if (stack1p != -1)  // I.e., there are cards left...
529                                 {
530                                         stack2[++stack2p] = stack1[stack1p--];
531
532 #if HAVE_SOUND
533                                         if (bSoundOn)
534                                         {
535                                                 PlaySound(IDW_CARDFLIP);
536                                                 // Pause
537                                                 Pause(55);
538                                         }
539 #endif
540
541                                         m_nNumToSplay++;
542                                 }
543                         }
544
545                         if (stack1p == -1)  // This is done here because it's right!
546                         {
547                                 m_nExhaustedDeck++;
548
549                                 if (m_bVegasStyle && ((m_bDrawThree && (m_nExhaustedDeck == 3))
550                                         || (!m_bDrawThree && (m_nExhaustedDeck == 1))))
551                                         m_bShowX = true;
552                         }
553                 }
554
555                 mouseOffset = QPoint(point.x() - 5, point.y() - 7);
556
557                 QRect inv1(5, 7, CARD_WIDTH, CARD_HEIGHT),
558                         inv2(5 + CARD_WIDTH + 6, 7, (2 * 12) + CARD_WIDTH, (2 * 2) + CARD_HEIGHT);
559                 update(inv1);
560                 update(inv2);
561         }
562
563         // Check for hit on ace piles
564
565         for(int i=0; i<4; i++)
566         {
567                 QRect aceRect(200 + ((CARD_WIDTH + 4) * i), 7, CARD_WIDTH - 1, CARD_HEIGHT - 1);
568
569                 if (aceRect.contains(point) && (nAce[i] > -1))
570                 {
571                         nHitAce = 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());
575                         return;
576                 }
577         }
578 }
579
580
581 void GameWidget::OnLButtonUp(QPoint point) 
582 {
583         if (!allowUserInput)
584                 return;
585
586         point -= mouseOffset; // ?? bad form!
587
588         if (hitStack || hitDiscard || (nHitAce != 0))
589         {
590                 QRect tabl[7];
591
592                 for(int i=0; i<7; i++)  // Compute valid rects at bottoms of stacks...
593                 {
594                         tabl[i].setLeft(10 + (i * (CARD_WIDTH + 8)));
595                         tabl[i].setRight(tabl[i].left() + CARD_WIDTH - 1);
596                         tabl[i].setTop(130);
597
598                         for(int j=0; j<19; j++)
599                         {
600                                 if ((solBoard[i][j] == -1)
601                                         || (hitStack && (i == stackStart.x()) && (j == stackStart.y())))
602                                 {
603                                         if (j > 0)
604                                         {
605                                                 for(int k=0; k<j-1; k++)
606                                                 {
607                                                         if (!myDeck.IsFaceUp(solBoard[i][k]))
608                                                                 tabl[i].setTop(tabl[i].top() + 4);
609                                                         else
610                                                                 tabl[i].setTop(tabl[i].top() + 17);
611                                                 }
612                                         }
613
614                                         tabl[i].setBottom(tabl[i].top() + CARD_HEIGHT - 1);
615                                         break;
616                                 }
617                         }
618                 }
619
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);
624                 QRect a[4];
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);
629
630                 bool bHitAce[4];
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]);
635
636                 // Check to see if hit more than one ace & pick the one that's closer
637                 for(int i=0; i<3; i++)
638                 {
639                         if (bHitAce[i] && bHitAce[i + 1] && (nAce[i] == -1) && (nAce[i + 1] == -1))
640                         {
641                                 if (c.intersected(a[i]).width() > c.intersected(a[i + 1]).width())
642                                         bHitAce[i + 1] = false;
643                                 else
644                                         bHitAce[i] = false;
645
646                                 break;
647                         }
648                 }
649
650                 bool bMovedToAce = false;
651
652                 for(int i=0; i<4; i++)
653                 {
654                         if (bHitAce[i] && IsValidMoveToAce(nAce[i]))
655                         {
656                                 m_bCanUndo = true;
657
658                                 if (hitStack)
659                                         nAce[i] = myDeck.GetCardAtPos(solBoard[stackStart.x()][stackStart.y()]),
660                                         solBoard[stackStart.x()][stackStart.y()] = -1;
661
662                                 if (hitDiscard)
663                                 {
664                                         nAce[i] = myDeck.GetCardAtPos(stack2[stack2p--]);
665
666                                         if (m_nNumToSplay != 1)
667                                                 m_nNumToSplay--;
668                                 }
669
670                                 if (nHitAce != 0)
671                                 {
672                                         nAce[i] = nAce[nHitAce - 1];
673                                         int nCard = nAce[nHitAce - 1];
674                                         nAce[nHitAce - 1] = ((nCard - 1) % 13 == 0 ? -1 : nCard - 1);
675                                         break;
676                                 }
677
678 #if HAVE_SOUND
679                                 if (bSoundOn)
680                                         PlaySound(IDW_CARDPLACE);
681 #endif
682
683                                 bMovedToAce = true;
684
685                                 // Scoring...
686 #if 0
687                                 if (m_bVegasStyle)
688                                         m_nScore += 5;
689                                 else
690                                         m_nScore += 10;
691 #else
692                                 m_nScore += (m_bVegasStyle ? 5 : 10);
693 #endif
694
695                                 emit UpdateScore(m_nScore);
696                                 break;
697                         }
698                 }
699
700                 if (!bMovedToAce) // No aces, so check for stacks
701                 {
702                         for(int i=0; i<7; i++)
703                         {
704                                 if (c.intersects(tabl[i]) && IsValidMoveToTableaux(i))
705                                 {
706                                         m_bCanUndo = true;
707
708 #if HAVE_SOUND
709                                         if (bSoundOn)
710                                                 PlaySound(IDW_CARDPLACE);
711 #endif
712
713                                         if (hitStack && (i != stackStart.x()))
714                                         {
715                                                 int cnt = stackStart.y();
716
717                                                 for(int k=0; k<19; k++)
718                                                 {
719                                                         if (solBoard[i][k] == -1)
720                                                         {
721                                                                 solBoard[i][k] = solBoard[stackStart.x()][cnt];
722                                                                 solBoard[stackStart.x()][cnt] = -1;
723                                                                 cnt++;
724                                                         }
725
726                                                         if (solBoard[stackStart.x()][cnt] == -1) // Done copying?
727                                                                 break;
728                                                 }
729
730                                                 break;
731                                         }
732
733                                         if (hitDiscard)
734                                         {
735                                                 for(int k=0; k<19; k++)
736                                                 {
737                                                         if (solBoard[i][k] == -1)
738                                                         {
739                                                                 solBoard[i][k] = stack2[stack2p--];
740                                                                 myDeck.SetCardFaceUp(solBoard[i][k]);
741
742                                                                 // Scoring...
743                                                                 if (!m_bVegasStyle)
744                                                                 {
745                                                                         m_nScore += 5;
746                                                                         emit UpdateScore(m_nScore);
747                                                                 }
748
749                                                                 if (m_nNumToSplay != 1)
750                                                                         m_nNumToSplay--;
751
752                                                                 break;
753                                                         }
754                                                 }
755
756                                                 break;
757                                         }
758
759                                         if (nHitAce != 0)
760                                         {
761                                                 // handle moving to tableaux here...
762                                                 for(int k=0; k<19; k++)
763                                                 {
764                                                         if (solBoard[i][k] == -1)
765                                                         {
766                                                                 int nCard = nAce[nHitAce - 1];
767
768                                                                 for(int t=0; t<52; t++)  // We have to find da damn thing...
769                                                                 {
770                                                                         if (myDeck.GetCardAtPos(t) == nCard)
771                                                                         {
772                                                                                 solBoard[i][k] = t;
773                                                                                 break;
774                                                                         }
775                                                                 }
776
777                                                                 nAce[nHitAce - 1] = ((nCard - 1) % 13 == 0 ? -1 : nCard - 1);
778                                                                 myDeck.SetCardFaceUp(solBoard[i][k]);
779
780                                                                 // Scoring...
781 #if 0
782                                                                 if (!m_bVegasStyle)
783                                                                         m_nScore -= 15;
784                                                                 else
785                                                                         m_nScore -= 5;
786 #else
787                                                                 m_nScore -= (m_bVegasStyle ? 5 : 15);
788 #endif
789
790                                                                 emit UpdateScore(m_nScore);
791                                                                 break;
792                                                         }
793                                                 }
794
795                                                 break;
796                                         }
797                                 }
798                         }
799                 }
800
801                 update();
802         }
803
804         mouseDown = hitStack = hitDiscard = false;
805         nHitAce = 0;
806
807         if (bAutoRemove)
808                 HandleAutoRemove();
809
810         if (PlayerWon())
811         {
812                 emit UpdateScore(m_nScore);
813                 HandleStatistics();
814                 m_bWonLastGame = true;
815                 m_bCanUndo = false;
816
817 #if HAVE_SOUND
818                 if (bSoundOn)
819                 {
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]);
824                 }
825 #endif
826
827                 QMessageBox::information(this, "MAC Solitaire", "Congratulations!\nYou won!");
828                 allowUserInput = false;
829         }
830 }
831
832
833 void GameWidget::OnMouseMove(QPoint point) 
834 {
835         if (mouseDown && (hitStack || hitDiscard || (nHitAce != 0)))
836         {
837                 int nHeight = 0;
838
839                 if (hitStack)
840                 {
841                         for(int i=stackStart.y(); i<19; i++)
842                                 if (solBoard[stackStart.x()][i] != -1)
843                                         nHeight += 18;
844                 }
845                 else
846                         nHeight = 18;
847
848                 nHeight += CARD_HEIGHT - 18;
849
850                 QRect rcOld(stackPos.x(), stackPos.y(), CARD_WIDTH,
851                         nHeight); // Find old position for repainting...
852
853                 stackPos = QPoint(point.x() - mouseOffset.x(), point.y() - mouseOffset.y());
854
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!
859         }
860 }
861
862
863 void GameWidget::OnLButtonDblClk(QPoint point) 
864 {
865         if (dcHitStack || dcHitDiscard)
866         {
867                 hitStack = dcHitStack, hitDiscard = dcHitDiscard;
868
869                 for(int i=0; i<4; i++)
870                 {
871                         if (IsValidMoveToAce(nAce[i]))
872                         {
873                                 if (hitStack)
874                                 {
875                                         nAce[i] = myDeck.GetCardAtPos(solBoard[stackStart.x()][stackStart.y()]);
876                                         solBoard[stackStart.x()][stackStart.y()] = -1;
877                                 }
878                                 else
879                                 {
880                                         nAce[i] = myDeck.GetCardAtPos(stack2[stack2p--]);
881
882                                         if (m_nNumToSplay != 1)
883                                                 m_nNumToSplay--;
884                                 }
885
886                                 update();
887
888 #if HAVE_SOUND
889                                 if (bSoundOn)
890                                         PlaySound(IDW_ZIP);
891 #endif
892
893                                 // Scoring...
894 #if 0
895                                 if (m_bVegasStyle)
896                                         m_nScore += 5;
897                                 else
898                                         m_nScore += 10;
899 #else
900                                 m_nScore += (m_bVegasStyle ? 5 : 10);
901 #endif
902
903                                 emit UpdateScore(m_nScore);
904                                 break;
905                         }
906                 }
907                 
908                 dcHitStack = hitStack = dcHitDiscard = hitDiscard = false;
909         }
910         else
911                 OnLButtonDown(point); // Either was on draw pile or somewhere else...
912 }
913
914
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)
918 {
919         bool bCheck = true;
920
921         while (bCheck)
922         {
923                 bCheck = false;
924
925                 int nLowestAce = (nAce[0] == -1 ? 0 : ((nAce[0] - 1) % 13) + 1); // Aces are 1-based, cards 1-based
926
927                 for(int i=1; i<4; i++)
928                 {
929                         int nTemp = (nAce[i] == -1 ? 0 : ((nAce[i] - 1) % 13) + 1);
930
931                         if (nTemp < nLowestAce)
932                                 nLowestAce = nTemp;
933                 }
934
935                 for(int i=0; i<7; i++)  // Compare cards that *can* go
936                 {
937                         for(int j=18; j>=0; j--)
938                         {
939                                 if (solBoard[i][j] != -1)
940                                 {
941                                         if (myDeck.IsFaceUp(solBoard[i][j]))
942                                         {
943                                                 int nCard = myDeck.GetCardAtPos(solBoard[i][j]);
944
945                                                 for(int k=0; k<4; k++)
946                                                 {
947                                                         if (IsValidMoveToAce(nAce[k], nCard))
948                                                         {
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];
953
954                                                                 for(int t=0; t<4; t++)
955                                                                 {
956                                                                         if (nAce[t] != -1)
957                                                                                 nSuit[t] = ((nAce[t] > 13) && (nAce[t] < 40) ? 0 : 1);
958                                                                         else
959                                                                                 nSuit[t] = -1;
960
961                                                                         nRank[t] = ((nAce[t] - 1) % 13) + 1;
962                                                                 }
963
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;
968
969                                                                 for(int t=0; t<4; t++)
970                                                                         if ((nCardSuit != nSuit[t]) && (t != k) && (nSuit[t] != -1))
971                                                                                 nCR[nCnt++] = nRank[t];
972
973                                                                 if ((nCnt == 2) && (nCR[0] == nCR[1])
974                                                                         && (nCR[0] - nLowestAce == 2) && (nCardRank - nLowestAce == 3))
975                                                                         bSpecial = true;
976
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)
980                                                                 {
981                                                                         solBoard[i][j] = -1;
982
983                                                                         if (m_bAnimationsOn)
984                                                                                 AnimateCards(nCard, k, i, j);
985
986                                                                         nAce[k] = nCard;
987                                                                         bCheck = true;
988
989                                                                         // Scoring...
990 #if 0
991                                                                         if (m_bVegasStyle)
992                                                                                 m_nScore += 5;
993                                                                         else
994                                                                                 m_nScore += 10;
995 #else
996                                                                         m_nScore += (m_bVegasStyle ? 5 : 10);
997 #endif
998                                                                         emit UpdateScore(m_nScore);
999                                                                         break;
1000                                                                 }
1001                                                         }
1002                                                 }
1003                                         }
1004
1005                                         break;
1006                                 }
1007                         }
1008                 }
1009
1010                 if (stack2p != -1)
1011                 {
1012                         int nCard = myDeck.GetCardAtPos(stack2[stack2p]);
1013
1014                         for(int k=0; k<4; k++)
1015                         {
1016                                 if (IsValidMoveToAce(nAce[k], nCard))
1017                                 {
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];
1022
1023                                         for(int t=0; t<4; t++)
1024                                         {
1025                                                 if (nAce[t] != -1)
1026                                                         nSuit[t] = ((nAce[t] > 13) && (nAce[t] < 40) ? 0 : 1);
1027                                                 else
1028                                                         nSuit[t] = -1;
1029
1030                                                 nRank[t] = ((nAce[t] - 1) % 13) + 1;
1031                                         }
1032
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;
1037
1038                                         for(int t=0; t<4; t++)
1039                                         {
1040                                                 if ((nCardSuit != nSuit[t]) && (t != k) && (nSuit[t] != -1))
1041                                                         nCR[nCnt++] = nRank[t];
1042                                         }
1043
1044                                         if ((nCnt == 2) && (nCR[0] == nCR[1])
1045                                                 && (nCR[0] - nLowestAce == 2) && (nCardRank - nLowestAce == 3))
1046                                                 bSpecial = true;
1047
1048                                         if (((((nCard - 1) % 13) + 1) - nLowestAce) <= 2 || bSpecial)
1049                                         // OR difference is 3 and both opposite color are at 2 (tortuously checked above)
1050                                         {
1051                                                 stack2p--;
1052
1053                                                 if (m_nNumToSplay != 1)
1054                                                         m_nNumToSplay--;
1055
1056                                                 if (m_bAnimationsOn)
1057                                                         AnimateCards(nCard, k, -1, -1);
1058
1059                                                 nAce[k] = nCard;
1060                                                 bCheck = true;
1061
1062                                                 // Scoring...
1063 #if 0
1064                                                 if (m_bVegasStyle)
1065                                                         m_nScore += 5;
1066                                                 else
1067                                                         m_nScore += 10;
1068 #else
1069                                                 m_nScore += (m_bVegasStyle ? 5 : 10);
1070 #endif
1071
1072                                                 emit UpdateScore(m_nScore);
1073                                                 break;
1074                                         }
1075                                 }
1076                         }
1077                 }
1078         }
1079 }
1080
1081
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)
1085 {
1086 //no mas--jeff no likee CWaitCursor wc;   // Automagically reverts when it goes out of scope
1087         int nCX, nCY, nDX, nDY;
1088
1089         // Step 1: figure out where card started from
1090         if (nTabX == -1)
1091         {
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);
1095         }
1096         else
1097         {
1098                 nCX = 10 + (nTabX * (CARD_WIDTH + 8)), nCY = 130;
1099
1100                 for(int j=0; j<nTabY; j++)
1101                 {
1102                         if (solBoard[nTabX][j] != -1)
1103                         {
1104                                 if (myDeck.IsFaceUp(solBoard[nTabX][j]))
1105                                         nCY += 18;
1106                                 else
1107                                         nCY += 4;
1108                         }
1109                 }
1110         }
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)
1116         m_bFreeCard = true;
1117         m_pFirstCard = new SCardInfo(nCard);
1118         int nDeltaX = nDX - nCX, nDeltaY = nDY - nCY;
1119         int nError = 0;
1120         int nUpdate = 0;
1121         update(); //will do it??? Seems to...
1122 //Doing this causes an image of the card to be drawn at (0, 0) :-/
1123 //      repaint();
1124         QRect rcOld(nCX, nCY, CARD_WIDTH, CARD_HEIGHT), rcNew, rcUpdate;
1125
1126         if (abs(nDeltaX) > abs(nDeltaY))
1127         {
1128                 for(int i=0; i<abs(nDeltaX); i++)
1129                 {
1130                         // Put card draw stuff into OnDraw? (probably best way to do it...)
1131                         nError += abs(nDeltaY);
1132
1133                         if (nError > abs(nDeltaX))
1134                                 nCY += (nDeltaY < 0 ? -1 : +1), nError -= abs(nDeltaX);
1135
1136                         m_pFirstCard->nXPos = nCX + (nDeltaX < 0 ? -i : +i), m_pFirstCard->nYPos = nCY;
1137                         // Draw card
1138                         nUpdate++;
1139
1140                         if (nUpdate == 10)
1141                         {
1142                                 rcNew = QRect(m_pFirstCard->nXPos, m_pFirstCard->nYPos,
1143                                         CARD_WIDTH, CARD_HEIGHT);
1144                                 rcUpdate = rcOld.united(rcNew);
1145                                 nUpdate = 0;
1146                                 repaint(rcUpdate);
1147                                 rcOld = rcNew;
1148                                 // Pause
1149                                 Pause(3);
1150                         }
1151                         // calc new positions
1152                 }
1153
1154                 m_pFirstCard->nXPos = nDX, m_pFirstCard->nYPos = nDY;
1155                 rcNew = QRect(nDX, nDY, CARD_WIDTH, CARD_HEIGHT);
1156                 rcUpdate = rcOld.united(rcNew);
1157                 repaint(rcUpdate);
1158         }
1159         else
1160         {
1161                 for(int i=0; i<abs(nDeltaY); i++)
1162                 {
1163                         // Put card draw stuff into OnDraw? (probably best way to do it...)
1164                         nError += abs(nDeltaX);
1165
1166                         if (nError > abs(nDeltaY))
1167                                 nCX += (nDeltaX < 0 ? -1 : +1), nError -= abs(nDeltaY);
1168
1169                         m_pFirstCard->nXPos = nCX, m_pFirstCard->nYPos = nCY + (nDeltaY < 0 ? -i : +i);
1170                         // Draw card
1171                         nUpdate++;
1172
1173                         if (nUpdate == 10)
1174                         {
1175                                 rcNew = QRect(m_pFirstCard->nXPos, m_pFirstCard->nYPos,
1176                                         CARD_WIDTH, CARD_HEIGHT);
1177                                 rcUpdate = rcOld.united(rcNew);
1178                                 nUpdate = 0;
1179                                 repaint(rcUpdate);//this is crappy. should only update what's needed!
1180                                 rcOld = rcNew;
1181                                 // Pause
1182                                 Pause(3);
1183                         }
1184                         // calc new positions
1185                 }
1186
1187                 m_pFirstCard->nXPos = nDX, m_pFirstCard->nYPos = nDY;
1188                 rcNew = QRect(nDX, nDY, CARD_WIDTH, CARD_HEIGHT);
1189                 rcUpdate = rcOld.united(rcNew);
1190                 repaint(rcUpdate);
1191         }
1192
1193         m_bFreeCard = false;
1194         delete m_pFirstCard;
1195 }
1196
1197
1198 bool GameWidget::IsValidMoveToAce(int nAceM)
1199 {
1200         int nCards = 0;
1201
1202         for(int k=stackStart.y(); k<19; k++)
1203         {
1204                 if (solBoard[stackStart.x()][k] != -1)
1205                         nCards++;
1206         }
1207
1208         if ((hitStack && (nCards == 1)) || hitDiscard)
1209         {
1210                 int nCardHit = (hitStack ? myDeck.GetCardAtPos(solBoard[stackStart.x()][stackStart.y()])
1211                         : myDeck.GetCardAtPos(stack2[stack2p]));
1212                 return IsValidMoveToAce(nAceM, nCardHit);
1213         }
1214         else if (nHitAce != 0)
1215         {
1216                 return IsValidMoveToAce(nAceM, nAce[nHitAce - 1]);
1217         }
1218
1219         return false;
1220 }
1221
1222
1223 bool GameWidget::IsValidMoveToAce(int nAce, int nCard)
1224 {
1225         if (((nAce == -1) && ((nCard % 13) == 1))
1226                 || ((nAce == (nCard - 1)) && ((nAce % 13) != 0)))
1227                 return true;
1228
1229         return false;
1230 }
1231
1232
1233 bool GameWidget::IsValidMoveToTableaux(int nStack)
1234 {
1235         int nBottomCard = -1, nTopCard;
1236
1237         for(int i=0; i<19; i++)
1238         {
1239                 if ((solBoard[nStack][i] != -1) && (myDeck.IsFaceUp(solBoard[nStack][i])))
1240                         nBottomCard = myDeck.GetCardAtPos(solBoard[nStack][i]);
1241         }
1242
1243         if (hitStack)
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];
1249
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;
1254
1255         if (((rankBC == (rankTC + 1)) && (colorBC != colorTC))
1256                 || ((nBottomCard == -1) && (rankTC == 12)))
1257                 return true;
1258         
1259         return false;
1260 }
1261
1262
1263 bool GameWidget::PlayerWon(void)
1264 {
1265         for(int i=0; i<7; i++)
1266                 if (solBoard[i][0] != -1)
1267                   return false;
1268
1269         if ((stack1p != -1) || (stack2p != -1))
1270                 return false;
1271
1272         return true;
1273 }
1274
1275
1276 void GameWidget::HandleStatistics(void)
1277 {
1278         if (PlayerWon())
1279         {
1280                 m_nWins++;
1281
1282                 if (m_bWonLastGame)
1283                         m_nStreakC++;
1284                 else
1285                         m_nStreakC = 1;
1286         }
1287         else
1288         {
1289                 m_nLosses++;
1290
1291                 if (!m_bWonLastGame)
1292                         m_nStreakC--;
1293                 else
1294                         m_nStreakC = -1;
1295         }
1296
1297         if (m_nStreakC < 0)    // Beat largest losing streak?
1298         {
1299                 if (abs(m_nStreakC) > m_nStreakL)
1300                         m_nStreakL = abs(m_nStreakC);
1301         }
1302         else                   // Check for longest winning streak
1303         {
1304                 if (m_nStreakC > m_nStreakW)
1305                         m_nStreakW = m_nStreakC;
1306         }
1307
1308         if (m_bVegasStyle)     // Check for high score
1309         {
1310                 if (m_nScore > m_nVHiScore)
1311                         m_nVHiScore = m_nScore;
1312         }
1313         else
1314         {
1315                 if (m_nScore > m_nHiScore)
1316                         m_nHiScore = m_nScore;
1317         }
1318 }
1319
1320
1321 //
1322 // Halt processing for 'count' milliseconds
1323 //
1324 void GameWidget::Pause(int count)
1325 {
1326 //      DWORD endCount = GetTickCount() + count;
1327 //      while (GetTickCount() < endCount) {}     // Still crude, but better control
1328 #if 1
1329         usleep(count * 1000);
1330 #else
1331         // This causes it to lock up randomly. :-/
1332         QTime time;
1333         time.start();
1334
1335         while (time.msec() < count)
1336                 ; // Do nothing...
1337 #endif
1338 }
1339 #endif
1340