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