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