]> git.saurik.com Git - wxWidgets.git/blame - demos/forty/game.cpp
use MSWAlwaysDrawBg() to fix the problem with black background; rewrote/cleaned up...
[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
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
37Game::Game(int wins, int games, int score) :
010216e3
WS
38 m_inPlay(false),
39 m_moveIndex(0),
40 m_redoIndex(0),
41 m_bmap(0),
42 m_bmapCard(0)
63cafd27
JS
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 {
010216e3
WS
55 m_foundations[i] = new Foundation(2 + (i / 4) * (CardWidth + 2),
56 2 + (i % 4) * (CardHeight + 2));
63cafd27
JS
57 }
58
59 for (i = 0; i < 10; i++)
60 {
010216e3 61 m_bases[i] = new Base(8 + (i + 2) * (CardWidth + 2), 2);
63cafd27
JS
62 }
63 Deal();
64 m_srcPile = 0;
65 m_liftedCard = 0;
66
010216e3 67 // copy the input parameters for future reference
63cafd27
JS
68 m_numWins = wins;
69 m_numGames = games;
70 m_totalScore = score;
71 m_currentScore = 0;
72}
73
74
fc799548
JS
75void 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
63cafd27
JS
99// Make sure we delete all objects created by the game object
100Game::~Game()
101{
102 int i;
103
104 delete m_pack;
105 delete m_discard;
106 for (i = 0; i < 8; i++)
107 {
010216e3 108 delete m_foundations[i];
63cafd27
JS
109 }
110 for (i = 0; i < 10; i++)
111 {
010216e3 112 delete m_bases[i];
63cafd27 113 }
010216e3
WS
114 delete m_bmap;
115 delete m_bmapCard;
63cafd27
JS
116}
117
118/*
119Set the score for a new player.
120NB: call Deal() first if the new player is to start
121a new game
122*/
123void 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
132void Game::Undo(wxDC& dc)
133{
134 if (m_moveIndex > 0)
135 {
010216e3
WS
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);
63cafd27
JS
140 }
141}
142
143// Redo the last move
144void Game::Redo(wxDC& dc)
145{
146 if (m_moveIndex < m_redoIndex)
147 {
010216e3
WS
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++;
63cafd27
JS
157 }
158}
159
160void Game::DoMove(wxDC& dc, Pile* src, Pile* dest)
161{
162 if (m_moveIndex < MaxMoves)
163 {
010216e3
WS
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++;
63cafd27 172
010216e3
WS
173 // when we do a move any moves in redo buffer are discarded
174 m_redoIndex = m_moveIndex;
63cafd27
JS
175 }
176 else
177 {
010216e3
WS
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 {
9738cd31 196 wxWindowList::compatibility_iterator node = frame->GetChildren().GetFirst();
010216e3
WS
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 }
63cafd27
JS
218}
219
220
221void Game::DisplayScore(wxDC& dc)
222{
16553659 223 wxColour bgColour = FortyApp::BackgroundColour();
010216e3 224 wxPen* pen = wxThePenList->FindOrCreatePen(bgColour, 1, wxSOLID);
16553659
JS
225 dc.SetTextBackground(bgColour);
226 dc.SetTextForeground(FortyApp::TextColour());
010216e3
WS
227 dc.SetBrush(FortyApp::BackgroundBrush());
228 dc.SetPen(* pen);
63cafd27 229
010216e3 230 // count the number of cards in foundations
63cafd27
JS
231 m_currentScore = 0;
232 for (int i = 0; i < 8; i++)
233 {
010216e3 234 m_currentScore += m_foundations[i]->GetNumCards();
63cafd27
JS
235 }
236
237 int x, y;
238 m_pack->GetTopCardPos(x, y);
239 x += 12 * CardWidth - 105;
240
010216e3
WS
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);
63cafd27 249
f37c24e0
MB
250 wxString str;
251 str.Printf(_T("%d"), m_currentScore);
252 dc.DrawText(_T("Score:"), x, y);
63cafd27
JS
253 dc.DrawText(str, x + w, y);
254 y += h;
255
f37c24e0
MB
256 str.Printf(_T("%d"), m_numGames);
257 dc.DrawText(_T("Games played:"), x, y);
63cafd27
JS
258 dc.DrawText(str, x + w, y);
259 y += h;
260
f37c24e0
MB
261 str.Printf(_T("%d"), m_numWins);
262 dc.DrawText(_T("Games won:"), x, y);
63cafd27
JS
263 dc.DrawText(str, x + w, y);
264 y += h;
265
266 int average = 0;
010216e3
WS
267 if (m_numGames > 0)
268 {
269 average = (2 * (m_currentScore + m_totalScore) + m_numGames ) / (2 * m_numGames);
270 }
f37c24e0
MB
271 str.Printf(_T("%d"), average);
272 dc.DrawText(_T("Average score:"), x, y);
63cafd27
JS
273 dc.DrawText(str, x + w, y);
274}
275
276
277// Shuffle the m_pack and deal the cards
278void Game::Deal()
279{
280 int i, j;
281 Card* card;
282
010216e3 283 // Reset all the piles, the undo buffer and shuffle the m_pack
63cafd27
JS
284 m_moveIndex = 0;
285 m_pack->ResetPile();
286 for (i = 0; i < 5; i++)
010216e3
WS
287 {
288 m_pack->Shuffle();
289 }
63cafd27
JS
290 m_discard->ResetPile();
291 for (i = 0; i < 10; i++)
010216e3
WS
292 {
293 m_bases[i]->ResetPile();
294 }
63cafd27 295 for (i = 0; i < 8; i++)
010216e3
WS
296 {
297 m_foundations[i]->ResetPile();
298 }
63cafd27 299
010216e3 300 // Deal the initial 40 cards onto the bases
63cafd27
JS
301 for (i = 0; i < 10; i++)
302 {
010216e3
WS
303 for (j = 1; j <= 4; j++)
304 {
305 card = m_pack->RemoveTopCard();
306 card->TurnCard(faceup);
307 m_bases[i]->AddCard(card);
308 }
63cafd27
JS
309 }
310
311 if (m_inPlay)
010216e3
WS
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 }
63cafd27 317 m_currentScore = 0;
e0b5519a 318 m_inPlay = false;
63cafd27
JS
319}
320
321
322// Redraw the m_pack, discard pile, the bases and the foundations
323void Game::Redraw(wxDC& dc)
324{
010216e3
WS
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);
82ea63e6 346 memoryDC.SetPen( *wxTRANSPARENT_PEN );
010216e3
WS
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 }
63cafd27
JS
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
359Pile* Game::WhichPile(int x, int y)
360{
010216e3
WS
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;
63cafd27
JS
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()
397bool Game::LButtonDown(wxDC& dc, int x, int y)
398{
399 m_srcPile = WhichPile(x, y);
400 if (m_srcPile == m_pack)
401 {
010216e3
WS
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 }
63cafd27
JS
410 m_srcPile = 0;
411 }
412 else if (m_srcPile)
413 {
010216e3
WS
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 }
63cafd27
JS
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
443void Game::LButtonDblClk(wxDC& dc, int x, int y)
444{
445 Pile* pile = WhichPile(x, y);
446 if (!pile) return;
447
010216e3 448 // Double click on m_pack is the same as left button down
63cafd27
JS
449 if (pile == m_pack)
450 {
010216e3 451 LButtonDown(dc, x, y);
63cafd27
JS
452 }
453 else
454 {
010216e3 455 Card* card = pile->GetTopCard();
63cafd27 456
010216e3
WS
457 if (card)
458 {
459 int i;
63cafd27 460
010216e3
WS
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 )
63cafd27 469 {
010216e3 470 if (m_topCard->GetSuit() == card->GetSuit() &&
63cafd27 471 m_foundations[i + 4] != pile &&
010216e3
WS
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 }
63cafd27 479 }
010216e3 480 }
63cafd27 481 }
010216e3
WS
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 }
63cafd27 493 }
010216e3
WS
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 }
63cafd27 506 }
010216e3
WS
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 }
63cafd27
JS
519 }
520}
521
522
523// Test to see whether the game has been won:
524// i.e. m_pack, discard and bases are empty
525bool Game::HaveYouWon()
526{
e0b5519a
JS
527 if (m_pack->GetTopCard()) return false;
528 if (m_discard->GetTopCard()) return false;
63cafd27
JS
529 for(int i = 0; i < 10; i++)
530 {
010216e3 531 if (m_bases[i]->GetTopCard()) return false;
63cafd27
JS
532 }
533 m_numWins++;
534 m_totalScore += m_currentScore;
535 m_currentScore = 0;
e0b5519a 536 return true;
63cafd27
JS
537}
538
539
540// See whether the card under the cursor can be moved somewhere else
e0b5519a 541// Returns 'true' if it can be moved, 'false' otherwise
63cafd27
JS
542bool Game::CanYouGo(int x, int y)
543{
544 Pile* pile = WhichPile(x, y);
545 if (pile && pile != m_pack)
546 {
010216e3 547 Card* card = pile->GetTopCard();
63cafd27 548
010216e3
WS
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 {
e0b5519a 556 return true;
010216e3 557 }
63cafd27 558 }
010216e3
WS
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 }
63cafd27 567 }
010216e3 568 }
63cafd27 569 }
e0b5519a 570 return false;
63cafd27
JS
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.
578void Game::LButtonUp(wxDC& dc, int x, int y)
579{
580 if (m_srcPile)
581 {
010216e3
WS
582 // work out the position of the dragged card
583 x += m_xOffset;
63cafd27
JS
584 y += m_yOffset;
585
010216e3
WS
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];
63cafd27
JS
598 distance = nearestPile->CalcDistance(x, y);
599 }
600 }
010216e3
WS
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)
63cafd27 607 {
010216e3 608 nearestPile = m_bases[i];
63cafd27
JS
609 distance = nearestPile->CalcDistance(x, y);
610 }
611 }
010216e3
WS
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 }
63cafd27
JS
630 else
631 {
010216e3
WS
632 // Return card to src pile
633 m_srcPile->AddCard(dc, m_liftedCard);
634 }
635 m_srcPile = 0;
636 m_liftedCard = 0;
63cafd27
JS
637 }
638}
639
640
641
642
643bool Game::DropCard(int x, int y, Pile* pile, Card* card)
644{
e0b5519a 645 bool retval = false;
63cafd27
JS
646 if (pile->Overlap(x, y))
647 {
010216e3
WS
648 if (pile->AcceptCard(card))
649 {
650 retval = true;
63cafd27
JS
651 }
652 }
653 return retval;
654}
655
656
657void Game::MouseMove(wxDC& dc, int mx, int my)
658{
659 if (m_liftedCard)
660 {
010216e3
WS
661 wxMemoryDC memoryDC;
662 memoryDC.SelectObject(*m_bmap);
63cafd27 663
010216e3
WS
664 int dx = mx + m_xOffset - m_xPos;
665 int dy = my + m_yOffset - m_yPos;
63cafd27 666
010216e3 667 if (abs(dx) >= CardWidth || abs(dy) >= CardHeight)
63cafd27 668 {
010216e3
WS
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);
63cafd27
JS
736 }
737}
738
739
740
741//----------------------------------------------//
742// The Pack class: holds the two decks of cards //
743//----------------------------------------------//
744Pack::Pack(int x, int y) : Pile(x, y, 0, 0)
745{
746 for (m_topCard = 0; m_topCard < NumCards; m_topCard++)
747 {
010216e3 748 m_cards[m_topCard] = new Card(1 + m_topCard / 2, facedown);
63cafd27
JS
749 }
750 m_topCard = NumCards - 1;
751}
752
753
754void Pack::Shuffle()
755{
756 Card* temp[NumCards];
757 int i;
758
010216e3 759 // Don't try to shuffle an empty m_pack!
63cafd27
JS
760 if (m_topCard < 0) return;
761
010216e3
WS
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.
63cafd27
JS
765 for (i = 0; i <= m_topCard; i++)
766 {
010216e3 767 temp[i] = 0;
63cafd27
JS
768 }
769 for (i = 0; i <= m_topCard; i++)
770 {
010216e3
WS
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];
63cafd27
JS
779 m_cards[i] = 0;
780 }
781
010216e3
WS
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.
63cafd27
JS
785 for (i = 0; i <= m_topCard; i++)
786 {
010216e3
WS
787 int pos = rand() % (m_topCard + 1);
788 while (m_cards[pos])
789 {
790 pos++;
63cafd27 791 if (pos > m_topCard) pos = 0;
010216e3 792 }
63cafd27
JS
793 m_cards[pos] = temp[i];
794 }
795}
796
797void Pack::Redraw(wxDC& dc)
798{
799 Pile::Redraw(dc);
800
f37c24e0
MB
801 wxString str;
802 str.Printf(_T("%d "), m_topCard + 1);
63cafd27 803
82ea63e6 804 dc.SetBackgroundMode( wxSOLID );
010216e3
WS
805 dc.SetTextBackground(FortyApp::BackgroundColour());
806 dc.SetTextForeground(FortyApp::TextColour());
63cafd27
JS
807 dc.DrawText(str, m_x + CardWidth + 5, m_y + CardHeight / 2);
808
809}
810
811void Pack::AddCard(Card* card)
812{
813 if (card == m_cards[m_topCard + 1])
814 {
010216e3 815 m_topCard++;
63cafd27
JS
816 }
817 else
818 {
010216e3
WS
819 wxMessageBox(_T("Pack::AddCard() Undo error"), _T("Forty Thieves: Warning"),
820 wxOK | wxICON_EXCLAMATION);
63cafd27
JS
821 }
822 card->TurnCard(facedown);
823}
824
825
826Pack::~Pack()
827{
828 for (m_topCard = 0; m_topCard < NumCards; m_topCard++)
829 {
010216e3 830 delete m_cards[m_topCard];
63cafd27
JS
831 }
832};
833
834
835//------------------------------------------------------//
836// The Base class: holds the initial pile of four cards //
837//------------------------------------------------------//
838Base::Base(int x, int y) : Pile(x, y, 0, 12)
839{
840 m_topCard = -1;
841}
842
843
844bool Base::AcceptCard(Card* card)
845{
e0b5519a 846 bool retval = false;
63cafd27
JS
847
848 if (m_topCard >= 0)
849 {
010216e3
WS
850 if (m_cards[m_topCard]->GetSuit() == card->GetSuit() &&
851 m_cards[m_topCard]->GetPipValue() - 1 == card->GetPipValue())
852 {
e0b5519a 853 retval = true;
63cafd27
JS
854 }
855 }
856 else
857 {
010216e3 858 // pile is empty - ACCEPT
e0b5519a 859 retval = true;
63cafd27
JS
860 }
861 return retval;
862}
863
63cafd27
JS
864
865//----------------------------------------------------------------//
866// The Foundation class: holds the cards built up from the ace... //
867//----------------------------------------------------------------//
868Foundation::Foundation(int x, int y) : Pile(x, y, 0, 0)
869{
870 m_topCard = -1;
871}
872
873bool Foundation::AcceptCard(Card* card)
874{
e0b5519a 875 bool retval = false;
63cafd27
JS
876
877 if (m_topCard >= 0)
878 {
010216e3
WS
879 if (m_cards[m_topCard]->GetSuit() == card->GetSuit() &&
880 m_cards[m_topCard]->GetPipValue() + 1 == card->GetPipValue())
881 {
e0b5519a 882 retval = true;
63cafd27
JS
883 }
884 }
885 else if (card->GetPipValue() == 1)
886 {
010216e3 887 // It's an ace and the pile is empty - ACCEPT
e0b5519a 888 retval = true;
63cafd27
JS
889 }
890 return retval;
891}
892
63cafd27
JS
893
894//----------------------------------------------------//
895// The Discard class: holds cards dealt from the m_pack //
896//----------------------------------------------------//
897Discard::Discard(int x, int y) : Pile(x, y, 19, 0)
898{
899 m_topCard = -1;
900}
901
902void Discard::Redraw(wxDC& dc)
903{
904 if (m_topCard >= 0)
905 {
010216e3
WS
906 if (m_dx == 0 && m_dy == 0)
907 {
63cafd27 908 m_cards[m_topCard]->Draw(dc, m_x, m_y);
010216e3
WS
909 }
910 else
911 {
912 int x = m_x;
913 int y = m_y;
914 for (int i = 0; i <= m_topCard; i++)
915 {
916 m_cards[i]->Draw(dc, x, y);
917 x += m_dx;
918 y += m_dy;
919 if (i == 31)
920 {
921 x = m_x;
63cafd27
JS
922 y = m_y + CardHeight / 3;
923 }
924 }
925 }
926 }
927 else
928 {
010216e3 929 Card::DrawNullCard(dc, m_x, m_y);
63cafd27
JS
930 }
931}
932
933
934void Discard::GetTopCardPos(int& x, int& y)
935{
936 if (m_topCard < 0)
937 {
010216e3
WS
938 x = m_x;
939 y = m_y;
63cafd27
JS
940 }
941 else if (m_topCard > 31)
942 {
010216e3
WS
943 x = m_x + m_dx * (m_topCard - 32);
944 y = m_y + CardHeight / 3;
63cafd27
JS
945 }
946 else
947 {
010216e3
WS
948 x = m_x + m_dx * m_topCard;
949 y = m_y;
63cafd27
JS
950 }
951}
952
953
954Card* Discard::RemoveTopCard(wxDC& dc, int m_xOffset, int m_yOffset)
955{
956 Card* card;
957
958 if (m_topCard <= 31)
959 {
010216e3 960 card = Pile::RemoveTopCard(dc, m_xOffset, m_yOffset);
63cafd27
JS
961 }
962 else
963 {
010216e3
WS
964 int topX, topY, x, y;
965 GetTopCardPos(topX, topY);
966 card = Pile::RemoveTopCard();
967 card->Erase(dc, topX - m_xOffset, topY - m_yOffset);
968 GetTopCardPos(x, y);
969 dc.SetClippingRegion(topX - m_xOffset, topY - m_yOffset,
970 CardWidth, CardHeight);
971
972 for (int i = m_topCard - 31; i <= m_topCard - 31 + CardWidth / m_dx; i++)
973 {
254a2129 974 m_cards[i]->Draw(dc, m_x - m_xOffset + i * m_dx, m_y - m_yOffset);
010216e3
WS
975 }
976 if (m_topCard > 31)
977 {
978 m_cards[m_topCard]->Draw(dc, topX - m_xOffset - m_dx, topY - m_yOffset);
979 }
980 dc.DestroyClippingRegion();
63cafd27
JS
981 }
982
983 return card;
984}