patch 102361 applied
[wxWidgets.git] / demos / forty / game.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: game.cpp
3 // Purpose: Forty Thieves patience game
4 // Author: Chris Breeze
5 // Modified by:
6 // Created: 21/07/97
7 // RCS-ID: $Id$
8 // Copyright: (c) 1993-1998 Chris Breeze
9 // Licence: wxWindows licence
10 //---------------------------------------------------------------------------
11 // Last modified: 22nd July 1998 - ported to wxWindows 2.0
12 /////////////////////////////////////////////////////////////////////////////
13
14 #ifdef __GNUG__
15 #pragma implementation
16 #pragma interface
17 #endif
18
19 // For compilers that support precompilation, includes "wx/wx.h".
20 #include "wx/wxprec.h"
21
22 #ifdef __BORLANDC__
23 #pragma hdrstop
24 #endif
25
26 #ifndef WX_PRECOMP
27 #include "wx/wx.h"
28 #endif
29
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <time.h>
33 #include <string.h>
34 #include "forty.h"
35 #include "game.h"
36
37 Game::Game(int wins, int games, int score) :
38 m_inPlay(FALSE),
39 m_moveIndex(0),
40 m_redoIndex(0),
41 m_bmap(0),
42 m_bmapCard(0)
43 {
44 int i;
45
46 m_pack = new Pack(2, 2 + 4 * (CardHeight + 2));
47 srand(time(0));
48
49 for (i = 0; i < 5; i++) m_pack->Shuffle();
50
51 m_discard = new Discard(2, 2 + 5 * (CardHeight + 2));
52
53 for (i = 0; i < 8; i++)
54 {
55 m_foundations[i] = new Foundation(2 + (i / 4) * (CardWidth + 2),
56 2 + (i % 4) * (CardHeight + 2));
57 }
58
59 for (i = 0; i < 10; i++)
60 {
61 m_bases[i] = new Base(8 + (i + 2) * (CardWidth + 2), 2);
62 }
63 Deal();
64 m_srcPile = 0;
65 m_liftedCard = 0;
66
67 // copy the input parameters for future reference
68 m_numWins = wins;
69 m_numGames = games;
70 m_totalScore = score;
71 m_currentScore = 0;
72 }
73
74
75 // Make sure we delete all objects created by the game object
76 Game::~Game()
77 {
78 int i;
79
80 delete m_pack;
81 delete m_discard;
82 for (i = 0; i < 8; i++)
83 {
84 delete m_foundations[i];
85 }
86 for (i = 0; i < 10; i++)
87 {
88 delete m_bases[i];
89 }
90 delete m_bmap;
91 delete m_bmapCard;
92 }
93
94 /*
95 Set the score for a new player.
96 NB: call Deal() first if the new player is to start
97 a new game
98 */
99 void Game::NewPlayer(int wins, int games, int score)
100 {
101 m_numWins = wins;
102 m_numGames = games;
103 m_totalScore = score;
104 m_currentScore = 0;
105 }
106
107 // Undo the last move
108 void Game::Undo(wxDC& dc)
109 {
110 if (m_moveIndex > 0)
111 {
112 m_moveIndex--;
113 Card* card = m_moves[m_moveIndex].dest->RemoveTopCard(dc);
114 m_moves[m_moveIndex].src->AddCard(dc, card);
115 DisplayScore(dc);
116 }
117 }
118
119 // Redo the last move
120 void Game::Redo(wxDC& dc)
121 {
122 if (m_moveIndex < m_redoIndex)
123 {
124 Card* card = m_moves[m_moveIndex].src->RemoveTopCard(dc);
125 if (m_moves[m_moveIndex].src == m_pack)
126 {
127 m_pack->Redraw(dc);
128 card->TurnCard(faceup);
129 }
130 m_moves[m_moveIndex].dest->AddCard(dc, card);
131 DisplayScore(dc);
132 m_moveIndex++;
133 }
134 }
135
136 void Game::DoMove(wxDC& dc, Pile* src, Pile* dest)
137 {
138 if (m_moveIndex < MaxMoves)
139 {
140 if (src == dest)
141 {
142 wxMessageBox("Game::DoMove() src == dest", "Debug message",
143 wxOK | wxICON_EXCLAMATION);
144 }
145 m_moves[m_moveIndex].src = src;
146 m_moves[m_moveIndex].dest = dest;
147 m_moveIndex++;
148
149 // when we do a move any moves in redo buffer are discarded
150 m_redoIndex = m_moveIndex;
151 }
152 else
153 {
154 wxMessageBox("Game::DoMove() Undo buffer full", "Debug message",
155 wxOK | wxICON_EXCLAMATION);
156 }
157
158 if (!m_inPlay)
159 {
160 m_inPlay = TRUE;
161 m_numGames++;
162 }
163 DisplayScore(dc);
164
165 if (HaveYouWon())
166 {
167 wxWindow *frame = wxTheApp->GetTopWindow();
168 wxWindow *canvas = (wxWindow *) NULL;
169
170 if (frame)
171 {
172 wxNode *node = frame->GetChildren().First();
173 if (node) canvas = (wxWindow*)node->Data();
174 }
175
176 // This game is over
177 m_inPlay = FALSE;
178
179 // Redraw the score box to update games won
180 DisplayScore(dc);
181
182 if (wxMessageBox("Do you wish to play again?",
183 "Well Done, You have won!", wxYES_NO | wxICON_QUESTION) == wxYES)
184 {
185 Deal();
186 canvas->Refresh();
187 }
188 else
189 {
190 // user cancelled the dialog - exit the app
191 ((wxFrame*)canvas->GetParent())->Close(TRUE);
192 }
193 }
194 }
195
196
197 void Game::DisplayScore(wxDC& dc)
198 {
199 wxColour bgColour = FortyApp::BackgroundColour();
200 wxPen* pen = wxThePenList->FindOrCreatePen(bgColour, 1, wxSOLID);
201 dc.SetTextBackground(bgColour);
202 dc.SetTextForeground(FortyApp::TextColour());
203 dc.SetBrush(FortyApp::BackgroundBrush());
204 dc.SetPen(* pen);
205
206 // count the number of cards in foundations
207 m_currentScore = 0;
208 for (int i = 0; i < 8; i++)
209 {
210 m_currentScore += m_foundations[i]->GetNumCards();
211 }
212
213 int x, y;
214 m_pack->GetTopCardPos(x, y);
215 x += 12 * CardWidth - 105;
216
217 int w, h;
218 {
219 long width, height;
220 dc.GetTextExtent("Average score:m_x", &width, &height);
221 w = width;
222 h = height;
223 }
224 dc.DrawRectangle(x + w, y, 20, 4 * h);
225
226 char str[80];
227 sprintf(str, "%d", m_currentScore);
228 dc.DrawText("Score:", x, y);
229 dc.DrawText(str, x + w, y);
230 y += h;
231
232 sprintf(str, "%d", m_numGames);
233 dc.DrawText("Games played:", x, y);
234 dc.DrawText(str, x + w, y);
235 y += h;
236
237 sprintf(str, "%d", m_numWins);
238 dc.DrawText("Games won:", x, y);
239 dc.DrawText(str, x + w, y);
240 y += h;
241
242 int average = 0;
243 if (m_numGames > 0)
244 {
245 average = (2 * (m_currentScore + m_totalScore) + m_numGames ) / (2 * m_numGames);
246 }
247 sprintf(str, "%d", average);
248 dc.DrawText("Average score:", x, y);
249 dc.DrawText(str, x + w, y);
250 }
251
252
253 // Shuffle the m_pack and deal the cards
254 void Game::Deal()
255 {
256 int i, j;
257 Card* card;
258
259 // Reset all the piles, the undo buffer and shuffle the m_pack
260 m_moveIndex = 0;
261 m_pack->ResetPile();
262 for (i = 0; i < 5; i++)
263 {
264 m_pack->Shuffle();
265 }
266 m_discard->ResetPile();
267 for (i = 0; i < 10; i++)
268 {
269 m_bases[i]->ResetPile();
270 }
271 for (i = 0; i < 8; i++)
272 {
273 m_foundations[i]->ResetPile();
274 }
275
276 // Deal the initial 40 cards onto the bases
277 for (i = 0; i < 10; i++)
278 {
279 for (j = 1; j <= 4; j++)
280 {
281 card = m_pack->RemoveTopCard();
282 card->TurnCard(faceup);
283 m_bases[i]->AddCard(card);
284 }
285 }
286
287 if (m_inPlay)
288 {
289 // player has started the game and then redealt
290 // and so we must add the score for this game to the total score
291 m_totalScore += m_currentScore;
292 }
293 m_currentScore = 0;
294 m_inPlay = FALSE;
295 }
296
297
298 // Redraw the m_pack, discard pile, the bases and the foundations
299 void Game::Redraw(wxDC& dc)
300 {
301 int i;
302 m_pack->Redraw(dc);
303 m_discard->Redraw(dc);
304 for (i = 0; i < 8; i++)
305 {
306 m_foundations[i]->Redraw(dc);
307 }
308 for (i = 0; i < 10; i++)
309 {
310 m_bases[i]->Redraw(dc);
311 }
312 DisplayScore(dc);
313
314 if (m_bmap == 0)
315 {
316 m_bmap = new wxBitmap(CardWidth, CardHeight);
317 m_bmapCard = new wxBitmap(CardWidth, CardHeight);
318
319 // Initialise the card bitmap to the background colour
320 wxMemoryDC memoryDC;
321 memoryDC.SelectObject(*m_bmapCard);
322 memoryDC.SetPen( *wxTRANSPARENT_PEN );
323 memoryDC.SetBrush(FortyApp::BackgroundBrush());
324 memoryDC.DrawRectangle(0, 0, CardWidth, CardHeight);
325 memoryDC.SelectObject(*m_bmap);
326 memoryDC.DrawRectangle(0, 0, CardWidth, CardHeight);
327 memoryDC.SelectObject(wxNullBitmap);
328 }
329 }
330
331
332 // Test to see if the point (x, y) is over the top card of one of the piles
333 // Returns pointer to the pile, or 0 if (x, y) is not over a pile
334 // or the pile is empty
335 Pile* Game::WhichPile(int x, int y)
336 {
337 if (m_pack->GetCard(x, y) &&
338 m_pack->GetCard(x, y) == m_pack->GetTopCard())
339 {
340 return m_pack;
341 }
342
343 if (m_discard->GetCard(x, y) &&
344 m_discard->GetCard(x, y) == m_discard->GetTopCard())
345 {
346 return m_discard;
347 }
348
349 int i;
350 for (i = 0; i < 8; i++)
351 {
352 if (m_foundations[i]->GetCard(x, y) &&
353 m_foundations[i]->GetCard(x, y) == m_foundations[i]->GetTopCard())
354 {
355 return m_foundations[i];
356 }
357 }
358
359 for (i = 0; i < 10; i++)
360 {
361 if (m_bases[i]->GetCard(x, y) &&
362 m_bases[i]->GetCard(x, y) == m_bases[i]->GetTopCard())
363 {
364 return m_bases[i];
365 }
366 }
367 return 0;
368 }
369
370
371 // Left button is pressed - if cursor is over the m_pack then deal a card
372 // otherwise if it is over a card pick it up ready to be dragged - see MouseMove()
373 bool Game::LButtonDown(wxDC& dc, int x, int y)
374 {
375 m_srcPile = WhichPile(x, y);
376 if (m_srcPile == m_pack)
377 {
378 Card* card = m_pack->RemoveTopCard();
379 if (card)
380 {
381 m_pack->Redraw(dc);
382 card->TurnCard(faceup);
383 m_discard->AddCard(dc, card);
384 DoMove(dc, m_pack, m_discard);
385 }
386 m_srcPile = 0;
387 }
388 else if (m_srcPile)
389 {
390 m_srcPile->GetTopCardPos(m_xPos, m_yPos);
391 m_xOffset = m_xPos - x;
392 m_yOffset = m_yPos - y;
393
394 // Copy the area under the card
395 // Initialise the card bitmap to the background colour
396 {
397 wxMemoryDC memoryDC;
398 memoryDC.SelectObject(*m_bmap);
399 m_liftedCard = m_srcPile->RemoveTopCard(memoryDC, m_xPos, m_yPos);
400 }
401
402 // Draw the card in card bitmap ready for blitting onto
403 // the screen
404 {
405 wxMemoryDC memoryDC;
406 memoryDC.SelectObject(*m_bmapCard);
407 m_liftedCard->Draw(memoryDC, 0, 0);
408 }
409 }
410 return m_srcPile != 0;
411 }
412
413 // Called when the left button is double clicked
414 // If a card is under the pointer and it can move elsewhere then move it.
415 // Move onto a foundation as first choice, a populated base as second and
416 // an empty base as third choice.
417 // NB Cards in the m_pack cannot be moved in this way - they aren't in play
418 // yet
419 void Game::LButtonDblClk(wxDC& dc, int x, int y)
420 {
421 Pile* pile = WhichPile(x, y);
422 if (!pile) return;
423
424 // Double click on m_pack is the same as left button down
425 if (pile == m_pack)
426 {
427 LButtonDown(dc, x, y);
428 }
429 else
430 {
431 Card* card = pile->GetTopCard();
432
433 if (card)
434 {
435 int i;
436
437 // if the card is an ace then try to place it next
438 // to an ace of the same suit
439 if (card->GetPipValue() == 1)
440 {
441 for(i = 0; i < 4; i++)
442 {
443 Card* m_topCard = m_foundations[i]->GetTopCard();
444 if ( m_topCard )
445 {
446 if (m_topCard->GetSuit() == card->GetSuit() &&
447 m_foundations[i + 4] != pile &&
448 m_foundations[i + 4]->GetTopCard() == 0)
449 {
450 pile->RemoveTopCard(dc);
451 m_foundations[i + 4]->AddCard(dc, card);
452 DoMove(dc, pile, m_foundations[i + 4]);
453 return;
454 }
455 }
456 }
457 }
458
459 // try to place the card on a foundation
460 for(i = 0; i < 8; i++)
461 {
462 if (m_foundations[i]->AcceptCard(card) && m_foundations[i] != pile)
463 {
464 pile->RemoveTopCard(dc);
465 m_foundations[i]->AddCard(dc, card);
466 DoMove(dc, pile, m_foundations[i]);
467 return;
468 }
469 }
470 // try to place the card on a populated base
471 for(i = 0; i < 10; i++)
472 {
473 if (m_bases[i]->AcceptCard(card) &&
474 m_bases[i] != pile &&
475 m_bases[i]->GetTopCard())
476 {
477 pile->RemoveTopCard(dc);
478 m_bases[i]->AddCard(dc, card);
479 DoMove(dc, pile, m_bases[i]);
480 return;
481 }
482 }
483 // try to place the card on any base
484 for(i = 0; i < 10; i++)
485 {
486 if (m_bases[i]->AcceptCard(card) && m_bases[i] != pile)
487 {
488 pile->RemoveTopCard(dc);
489 m_bases[i]->AddCard(dc, card);
490 DoMove(dc, pile, m_bases[i]);
491 return;
492 }
493 }
494 }
495 }
496 }
497
498
499 // Test to see whether the game has been won:
500 // i.e. m_pack, discard and bases are empty
501 bool Game::HaveYouWon()
502 {
503 if (m_pack->GetTopCard()) return FALSE;
504 if (m_discard->GetTopCard()) return FALSE;
505 for(int i = 0; i < 10; i++)
506 {
507 if (m_bases[i]->GetTopCard()) return FALSE;
508 }
509 m_numWins++;
510 m_totalScore += m_currentScore;
511 m_currentScore = 0;
512 return TRUE;
513 }
514
515
516 // See whether the card under the cursor can be moved somewhere else
517 // Returns TRUE if it can be moved, FALSE otherwise
518 bool Game::CanYouGo(int x, int y)
519 {
520 Pile* pile = WhichPile(x, y);
521 if (pile && pile != m_pack)
522 {
523 Card* card = pile->GetTopCard();
524
525 if (card)
526 {
527 int i;
528 for(i = 0; i < 8; i++)
529 {
530 if (m_foundations[i]->AcceptCard(card) && m_foundations[i] != pile)
531 {
532 return TRUE;
533 }
534 }
535 for(i = 0; i < 10; i++)
536 {
537 if (m_bases[i]->GetTopCard() &&
538 m_bases[i]->AcceptCard(card) &&
539 m_bases[i] != pile)
540 {
541 return TRUE;
542 }
543 }
544 }
545 }
546 return FALSE;
547 }
548
549
550 // Called when the left button is released after dragging a card
551 // Scan the piles to see if this card overlaps a pile and can be added
552 // to the pile. If the card overlaps more than one pile on which it can be placed
553 // then put it on the nearest pile.
554 void Game::LButtonUp(wxDC& dc, int x, int y)
555 {
556 if (m_srcPile)
557 {
558 // work out the position of the dragged card
559 x += m_xOffset;
560 y += m_yOffset;
561
562 Pile* nearestPile = 0;
563 int distance = (CardHeight + CardWidth) * (CardHeight + CardWidth);
564
565 // find the nearest pile which will accept the card
566 int i;
567 for (i = 0; i < 8; i++)
568 {
569 if (DropCard(x, y, m_foundations[i], m_liftedCard))
570 {
571 if (m_foundations[i]->CalcDistance(x, y) < distance)
572 {
573 nearestPile = m_foundations[i];
574 distance = nearestPile->CalcDistance(x, y);
575 }
576 }
577 }
578 for (i = 0; i < 10; i++)
579 {
580 if (DropCard(x, y, m_bases[i], m_liftedCard))
581 {
582 if (m_bases[i]->CalcDistance(x, y) < distance)
583 {
584 nearestPile = m_bases[i];
585 distance = nearestPile->CalcDistance(x, y);
586 }
587 }
588 }
589
590 // Restore the area under the card
591 wxMemoryDC memoryDC;
592 memoryDC.SelectObject(*m_bmap);
593 dc.Blit(m_xPos, m_yPos, CardWidth, CardHeight,
594 &memoryDC, 0, 0, wxCOPY);
595
596 // Draw the card in its new position
597 if (nearestPile)
598 {
599 // Add to new pile
600 nearestPile->AddCard(dc, m_liftedCard);
601 if (nearestPile != m_srcPile)
602 {
603 DoMove(dc, m_srcPile, nearestPile);
604 }
605 }
606 else
607 {
608 // Return card to src pile
609 m_srcPile->AddCard(dc, m_liftedCard);
610 }
611 m_srcPile = 0;
612 m_liftedCard = 0;
613 }
614 }
615
616
617
618
619 bool Game::DropCard(int x, int y, Pile* pile, Card* card)
620 {
621 bool retval = FALSE;
622 if (pile->Overlap(x, y))
623 {
624 if (pile->AcceptCard(card))
625 {
626 retval = TRUE;
627 }
628 }
629 return retval;
630 }
631
632
633 void Game::MouseMove(wxDC& dc, int mx, int my)
634 {
635 if (m_liftedCard)
636 {
637 wxMemoryDC memoryDC;
638 memoryDC.SelectObject(*m_bmap);
639
640 int dx = mx + m_xOffset - m_xPos;
641 int dy = my + m_yOffset - m_yPos;
642
643 if (abs(dx) >= CardWidth || abs(dy) >= CardHeight)
644 {
645 // Restore the area under the card
646 dc.Blit(m_xPos, m_yPos, CardWidth, CardHeight,
647 &memoryDC, 0, 0, wxCOPY);
648
649 // Copy the area under the card in the new position
650 memoryDC.Blit(0, 0, CardWidth, CardHeight,
651 &dc, m_xPos + dx, m_yPos + dy, wxCOPY);
652 }
653 else if (dx >= 0)
654 {
655 // dx >= 0
656 dc.Blit(m_xPos, m_yPos, dx, CardHeight, &memoryDC, 0, 0, wxCOPY);
657 if (dy >= 0)
658 {
659 // dy >= 0
660 dc.Blit(m_xPos + dx, m_yPos, CardWidth - dx, dy, &memoryDC, dx, 0, wxCOPY);
661 memoryDC.Blit(0, 0, CardWidth - dx, CardHeight - dy,
662 &memoryDC, dx, dy, wxCOPY);
663 memoryDC.Blit(0, CardHeight - dy, CardWidth - dx, dy,
664 &dc, m_xPos + dx, m_yPos + CardHeight, wxCOPY);
665 }
666 else
667 {
668 // dy < 0
669 dc.Blit(m_xPos + dx, m_yPos + dy + CardHeight, CardWidth - dx, -dy,
670 &memoryDC, dx, CardHeight + dy, wxCOPY);
671 memoryDC.Blit(0, -dy, CardWidth - dx, CardHeight + dy,
672 &memoryDC, dx, 0, wxCOPY);
673 memoryDC.Blit(0, 0, CardWidth - dx, -dy,
674 &dc, m_xPos + dx, m_yPos + dy, wxCOPY);
675 }
676 memoryDC.Blit(CardWidth - dx, 0, dx, CardHeight,
677 &dc, m_xPos + CardWidth, m_yPos + dy, wxCOPY);
678 }
679 else
680 {
681 // dx < 0
682 dc.Blit(m_xPos + CardWidth + dx, m_yPos, -dx, CardHeight,
683 &memoryDC, CardWidth + dx, 0, wxCOPY);
684 if (dy >= 0)
685 {
686 dc.Blit(m_xPos, m_yPos, CardWidth + dx, dy, &memoryDC, 0, 0, wxCOPY);
687 memoryDC.Blit(-dx, 0, CardWidth + dx, CardHeight - dy,
688 &memoryDC, 0, dy, wxCOPY);
689 memoryDC.Blit(-dx, CardHeight - dy, CardWidth + dx, dy,
690 &dc, m_xPos, m_yPos + CardHeight, wxCOPY);
691 }
692 else
693 {
694 // dy < 0
695 dc.Blit(m_xPos, m_yPos + CardHeight + dy, CardWidth + dx, -dy,
696 &memoryDC, 0, CardHeight + dy, wxCOPY);
697 memoryDC.Blit(-dx, -dy, CardWidth + dx, CardHeight + dy,
698 &memoryDC, 0, 0, wxCOPY);
699 memoryDC.Blit(-dx, 0, CardWidth + dx, -dy,
700 &dc, m_xPos, m_yPos + dy, wxCOPY);
701 }
702 memoryDC.Blit(0, 0, -dx, CardHeight,
703 &dc, m_xPos + dx, m_yPos + dy, wxCOPY);
704 }
705 m_xPos += dx;
706 m_yPos += dy;
707
708 // draw the card in its new position
709 memoryDC.SelectObject(*m_bmapCard);
710 dc.Blit(m_xPos, m_yPos, CardWidth, CardHeight,
711 &memoryDC, 0, 0, wxCOPY);
712 }
713 }
714
715
716
717 //----------------------------------------------//
718 // The Pack class: holds the two decks of cards //
719 //----------------------------------------------//
720 Pack::Pack(int x, int y) : Pile(x, y, 0, 0)
721 {
722 for (m_topCard = 0; m_topCard < NumCards; m_topCard++)
723 {
724 m_cards[m_topCard] = new Card(1 + m_topCard / 2, facedown);
725 }
726 m_topCard = NumCards - 1;
727 }
728
729
730 void Pack::Shuffle()
731 {
732 Card* temp[NumCards];
733 int i;
734
735 // Don't try to shuffle an empty m_pack!
736 if (m_topCard < 0) return;
737
738 // Copy the cards into a temporary array. Start by clearing
739 // the array and then copy the card into a random position.
740 // If the position is occupied then find the next lower position.
741 for (i = 0; i <= m_topCard; i++)
742 {
743 temp[i] = 0;
744 }
745 for (i = 0; i <= m_topCard; i++)
746 {
747 int pos = rand() % (m_topCard + 1);
748 while (temp[pos])
749 {
750 pos--;
751 if (pos < 0) pos = m_topCard;
752 }
753 m_cards[i]->TurnCard(facedown);
754 temp[pos] = m_cards[i];
755 m_cards[i] = 0;
756 }
757
758 // Copy each card back into the m_pack in a random
759 // position. If position is occupied then find nearest
760 // unoccupied position after the random position.
761 for (i = 0; i <= m_topCard; i++)
762 {
763 int pos = rand() % (m_topCard + 1);
764 while (m_cards[pos])
765 {
766 pos++;
767 if (pos > m_topCard) pos = 0;
768 }
769 m_cards[pos] = temp[i];
770 }
771 }
772
773 void Pack::Redraw(wxDC& dc)
774 {
775 Pile::Redraw(dc);
776
777 char str[10];
778 sprintf(str, "%d ", m_topCard + 1);
779
780 dc.SetBackgroundMode( wxSOLID );
781 dc.SetTextBackground(FortyApp::BackgroundColour());
782 dc.SetTextForeground(FortyApp::TextColour());
783 dc.DrawText(str, m_x + CardWidth + 5, m_y + CardHeight / 2);
784
785 }
786
787 void Pack::AddCard(Card* card)
788 {
789 if (card == m_cards[m_topCard + 1])
790 {
791 m_topCard++;
792 }
793 else
794 {
795 wxMessageBox("Pack::AddCard() Undo error", "Forty Thieves: Warning",
796 wxOK | wxICON_EXCLAMATION);
797 }
798 card->TurnCard(facedown);
799 }
800
801
802 Pack::~Pack()
803 {
804 for (m_topCard = 0; m_topCard < NumCards; m_topCard++)
805 {
806 delete m_cards[m_topCard];
807 }
808 };
809
810
811 //------------------------------------------------------//
812 // The Base class: holds the initial pile of four cards //
813 //------------------------------------------------------//
814 Base::Base(int x, int y) : Pile(x, y, 0, 12)
815 {
816 m_topCard = -1;
817 }
818
819
820 bool Base::AcceptCard(Card* card)
821 {
822 bool retval = FALSE;
823
824 if (m_topCard >= 0)
825 {
826 if (m_cards[m_topCard]->GetSuit() == card->GetSuit() &&
827 m_cards[m_topCard]->GetPipValue() - 1 == card->GetPipValue())
828 {
829 retval = TRUE;
830 }
831 }
832 else
833 {
834 // pile is empty - ACCEPT
835 retval = TRUE;
836 }
837 return retval;
838 }
839
840 Base::~Base()
841 {
842 // nothing special at the moment
843 };
844
845
846 //----------------------------------------------------------------//
847 // The Foundation class: holds the cards built up from the ace... //
848 //----------------------------------------------------------------//
849 Foundation::Foundation(int x, int y) : Pile(x, y, 0, 0)
850 {
851 m_topCard = -1;
852 }
853
854 bool Foundation::AcceptCard(Card* card)
855 {
856 bool retval = FALSE;
857
858 if (m_topCard >= 0)
859 {
860 if (m_cards[m_topCard]->GetSuit() == card->GetSuit() &&
861 m_cards[m_topCard]->GetPipValue() + 1 == card->GetPipValue())
862 {
863 retval = TRUE;
864 }
865 }
866 else if (card->GetPipValue() == 1)
867 {
868 // It's an ace and the pile is empty - ACCEPT
869 retval = TRUE;
870 }
871 return retval;
872 }
873
874 Foundation::~Foundation()
875 {
876 // nothing special at the moment
877 };
878
879
880 //----------------------------------------------------//
881 // The Discard class: holds cards dealt from the m_pack //
882 //----------------------------------------------------//
883 Discard::Discard(int x, int y) : Pile(x, y, 19, 0)
884 {
885 m_topCard = -1;
886 }
887
888 void Discard::Redraw(wxDC& dc)
889 {
890 if (m_topCard >= 0)
891 {
892 if (m_dx == 0 && m_dy == 0)
893 {
894 m_cards[m_topCard]->Draw(dc, m_x, m_y);
895 }
896 else
897 {
898 int x = m_x;
899 int y = m_y;
900 for (int i = 0; i <= m_topCard; i++)
901 {
902 m_cards[i]->Draw(dc, x, y);
903 x += m_dx;
904 y += m_dy;
905 if (i == 31)
906 {
907 x = m_x;
908 y = m_y + CardHeight / 3;
909 }
910 }
911 }
912 }
913 else
914 {
915 Card::DrawNullCard(dc, m_x, m_y);
916 }
917 }
918
919
920 void Discard::GetTopCardPos(int& x, int& y)
921 {
922 if (m_topCard < 0)
923 {
924 x = m_x;
925 y = m_y;
926 }
927 else if (m_topCard > 31)
928 {
929 x = m_x + m_dx * (m_topCard - 32);
930 y = m_y + CardHeight / 3;
931 }
932 else
933 {
934 x = m_x + m_dx * m_topCard;
935 y = m_y;
936 }
937 }
938
939
940 Card* Discard::RemoveTopCard(wxDC& dc, int m_xOffset, int m_yOffset)
941 {
942 Card* card;
943
944 if (m_topCard <= 31)
945 {
946 card = Pile::RemoveTopCard(dc, m_xOffset, m_yOffset);
947 }
948 else
949 {
950 int topX, topY, x, y;
951 GetTopCardPos(topX, topY);
952 card = Pile::RemoveTopCard();
953 card->Erase(dc, topX - m_xOffset, topY - m_yOffset);
954 GetTopCardPos(x, y);
955 dc.SetClippingRegion(topX - m_xOffset, topY - m_yOffset,
956 CardWidth, CardHeight);
957
958 for (int i = m_topCard - 31; i <= m_topCard - 31 + CardWidth / m_dx; i++)
959 {
960 m_cards[i]->Draw(dc, m_x - m_xOffset + i * m_dx, m_y - m_yOffset);
961 }
962 if (m_topCard > 31)
963 {
964 m_cards[m_topCard]->Draw(dc, topX - m_xOffset - m_dx, topY - m_yOffset);
965 }
966 dc.DestroyClippingRegion();
967 }
968
969 return card;
970 }
971
972
973 Discard::~Discard()
974 {
975 // nothing special at the moment
976 };