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: game.cpp 44498 2007-02-13 23:20:12Z VZ $
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// 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) :
33    m_inPlay(false),
34    m_moveIndex(0),
35    m_redoIndex(0),
36    m_bmap(0),
37    m_bmapCard(0)
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    {
50        m_foundations[i] = new Foundation(2 + (i / 4) * (CardWidth + 2),
51                    2 + (i % 4) * (CardHeight + 2));
52    }
53
54    for (i = 0; i < 10; i++)
55    {
56        m_bases[i] = new Base(8 + (i + 2) * (CardWidth + 2), 2);
57    }
58    Deal();
59    m_srcPile = 0;
60    m_liftedCard = 0;
61
62    // copy the input parameters for future reference
63    m_numWins = wins;
64    m_numGames = games;
65    m_totalScore = score;
66    m_currentScore = 0;
67}
68
69
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
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    {
103        delete m_foundations[i];
104    }
105    for (i = 0; i < 10; i++)
106    {
107        delete m_bases[i];
108    }
109    delete m_bmap;
110    delete m_bmapCard;
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    {
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);
135    }
136}
137
138// Redo the last move
139void Game::Redo(wxDC& dc)
140{
141    if (m_moveIndex < m_redoIndex)
142    {
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++;
152    }
153}
154
155void Game::DoMove(wxDC& dc, Pile* src, Pile* dest)
156{
157    if (m_moveIndex < MaxMoves)
158    {
159        if (src == dest)
160        {
161            wxMessageBox(_T("Game::DoMove() src == dest"), _T("Debug message"),
162                   wxOK | wxICON_EXCLAMATION);
163        }
164        m_moves[m_moveIndex].src = src;
165        m_moves[m_moveIndex].dest = dest;
166        m_moveIndex++;
167
168        // when we do a move any moves in redo buffer are discarded
169        m_redoIndex = m_moveIndex;
170    }
171    else
172    {
173        wxMessageBox(_T("Game::DoMove() Undo buffer full"), _T("Debug message"),
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        {
191            wxWindowList::compatibility_iterator node = frame->GetChildren().GetFirst();
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
201        if (wxMessageBox(_T("Do you wish to play again?"),
202            _T("Well Done, You have won!"), wxYES_NO | wxICON_QUESTION) == wxYES)
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    }
213}
214
215
216void Game::DisplayScore(wxDC& dc)
217{
218    wxColour bgColour = FortyApp::BackgroundColour();
219    wxPen* pen = wxThePenList->FindOrCreatePen(bgColour, 1, wxSOLID);
220    dc.SetTextBackground(bgColour);
221    dc.SetTextForeground(FortyApp::TextColour());
222    dc.SetBrush(FortyApp::BackgroundBrush());
223    dc.SetPen(* pen);
224
225    // count the number of cards in foundations
226    m_currentScore = 0;
227    for (int i = 0; i < 8; i++)
228    {
229        m_currentScore += m_foundations[i]->GetNumCards();
230    }
231
232    int x, y;
233    m_pack->GetTopCardPos(x, y);
234    x += 12 * CardWidth - 105;
235
236    int w, h;
237    {
238        long width, height;
239        dc.GetTextExtent(_T("Average score:m_x"), &width, &height);
240        w = width;
241        h = height;
242    }
243    dc.DrawRectangle(x + w, y, 20, 4 * h);
244
245    wxString str;
246    str.Printf(_T("%d"), m_currentScore);
247    dc.DrawText(_T("Score:"), x, y);
248    dc.DrawText(str, x + w, y);
249    y += h;
250
251    str.Printf(_T("%d"), m_numGames);
252    dc.DrawText(_T("Games played:"), x, y);
253    dc.DrawText(str, x + w, y);
254    y += h;
255
256    str.Printf(_T("%d"), m_numWins);
257    dc.DrawText(_T("Games won:"), x, y);
258    dc.DrawText(str, x + w, y);
259    y += h;
260
261    int average = 0;
262    if (m_numGames > 0)
263    {
264        average = (2 * (m_currentScore + m_totalScore) + m_numGames ) / (2 * m_numGames);
265    }
266    str.Printf(_T("%d"), average);
267    dc.DrawText(_T("Average score:"), x, y);
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
278    // Reset all the piles, the undo buffer and shuffle the m_pack
279    m_moveIndex = 0;
280    m_pack->ResetPile();
281    for (i = 0; i < 5; i++)
282    {
283        m_pack->Shuffle();
284    }
285    m_discard->ResetPile();
286    for (i = 0; i < 10; i++)
287    {
288        m_bases[i]->ResetPile();
289    }
290    for (i = 0; i <  8; i++)
291    {
292        m_foundations[i]->ResetPile();
293    }
294
295    // Deal the initial 40 cards onto the bases
296    for (i = 0; i < 10; i++)
297    {
298        for (j = 1; j <= 4; j++)
299        {
300            card = m_pack->RemoveTopCard();
301            card->TurnCard(faceup);
302            m_bases[i]->AddCard(card);
303        }
304    }
305
306    if (m_inPlay)
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    }
312    m_currentScore = 0;
313    m_inPlay = false;
314}
315
316
317// Redraw the m_pack, discard pile, the bases and the foundations
318void Game::Redraw(wxDC& dc)
319{
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);
341        memoryDC.SetPen( *wxTRANSPARENT_PEN );
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    }
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{
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;
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    {
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        }
405        m_srcPile = 0;
406    }
407    else if (m_srcPile)
408    {
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        }
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
443    // Double click on m_pack is the same as left button down
444    if (pile == m_pack)
445    {
446        LButtonDown(dc, x, y);
447    }
448    else
449    {
450        Card* card = pile->GetTopCard();
451
452        if (card)
453        {
454            int i;
455
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 )
464                    {
465                        if (m_topCard->GetSuit() == card->GetSuit() &&
466                            m_foundations[i + 4] != pile &&
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                        }
474                    }
475                }
476            }
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                }
488            }
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                }
501            }
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        }
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{
522    if (m_pack->GetTopCard()) return false;
523    if (m_discard->GetTopCard()) return false;
524    for(int i = 0; i < 10; i++)
525    {
526        if (m_bases[i]->GetTopCard()) return false;
527    }
528    m_numWins++;
529    m_totalScore += m_currentScore;
530    m_currentScore = 0;
531    return true;
532}
533
534
535// See whether the card under the cursor can be moved somewhere else
536// Returns 'true' if it can be moved, 'false' otherwise
537bool Game::CanYouGo(int x, int y)
538{
539    Pile* pile = WhichPile(x, y);
540    if (pile && pile != m_pack)
541    {
542        Card* card = pile->GetTopCard();
543
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                {
551                    return true;
552                }
553            }
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                }
562            }
563        }
564    }
565    return false;
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    {
577        // work out the position of the dragged card
578        x += m_xOffset;
579        y += m_yOffset;
580
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];
593                    distance = nearestPile->CalcDistance(x, y);
594                }
595            }
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)
602                {
603                    nearestPile = m_bases[i];
604                    distance = nearestPile->CalcDistance(x, y);
605                }
606            }
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        }
625        else
626        {
627            // Return card to src pile
628            m_srcPile->AddCard(dc, m_liftedCard);
629        }
630        m_srcPile = 0;
631        m_liftedCard = 0;
632    }
633}
634
635
636
637
638bool Game::DropCard(int x, int y, Pile* pile, Card* card)
639{
640    bool retval = false;
641    if (pile->Overlap(x, y))
642    {
643        if (pile->AcceptCard(card))
644        {
645            retval = true;
646        }
647    }
648    return retval;
649}
650
651
652void Game::MouseMove(wxDC& dc, int mx, int my)
653{
654    if (m_liftedCard)
655    {
656        wxMemoryDC memoryDC;
657        memoryDC.SelectObject(*m_bmap);
658
659        int dx = mx + m_xOffset - m_xPos;
660        int dy = my + m_yOffset - m_yPos;
661
662        if (abs(dx) >= CardWidth || abs(dy) >= CardHeight)
663        {
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);
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    {
743        m_cards[m_topCard] = new Card(1 + m_topCard / 2, facedown);
744    }
745    m_topCard = NumCards - 1;
746}
747
748
749void Pack::Shuffle()
750{
751    Card* temp[NumCards];
752    int i;
753
754    // Don't try to shuffle an empty m_pack!
755    if (m_topCard < 0) return;
756
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.
760    for (i = 0; i <= m_topCard; i++)
761    {
762        temp[i] = 0;
763    }
764    for (i = 0; i <= m_topCard; i++)
765    {
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];
774        m_cards[i] = 0;
775    }
776
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.
780    for (i = 0; i <= m_topCard; i++)
781    {
782        int pos = rand() % (m_topCard + 1);
783        while (m_cards[pos])
784        {
785            pos++;
786            if (pos > m_topCard) pos = 0;
787        }
788        m_cards[pos] = temp[i];
789    }
790}
791
792void Pack::Redraw(wxDC& dc)
793{
794    Pile::Redraw(dc);
795
796    wxString str;
797    str.Printf(_T("%d  "), m_topCard + 1);
798
799    dc.SetBackgroundMode( wxSOLID );
800    dc.SetTextBackground(FortyApp::BackgroundColour());
801    dc.SetTextForeground(FortyApp::TextColour());
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    {
810        m_topCard++;
811    }
812    else
813    {
814        wxMessageBox(_T("Pack::AddCard() Undo error"), _T("Forty Thieves: Warning"),
815           wxOK | wxICON_EXCLAMATION);
816    }
817    card->TurnCard(facedown);
818}
819
820
821Pack::~Pack()
822{
823    for (m_topCard = 0; m_topCard < NumCards; m_topCard++)
824    {
825        delete m_cards[m_topCard];
826    }
827}
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{
841    bool retval = false;
842
843    if (m_topCard >= 0)
844    {
845        if (m_cards[m_topCard]->GetSuit() == card->GetSuit() &&
846            m_cards[m_topCard]->GetPipValue() - 1 == card->GetPipValue())
847        {
848            retval = true;
849        }
850    }
851    else
852    {
853        // pile is empty - ACCEPT
854        retval = true;
855    }
856    return retval;
857}
858
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{
870    bool retval = false;
871
872    if (m_topCard >= 0)
873    {
874        if (m_cards[m_topCard]->GetSuit() == card->GetSuit() &&
875            m_cards[m_topCard]->GetPipValue() + 1 == card->GetPipValue())
876        {
877            retval = true;
878        }
879    }
880    else if (card->GetPipValue() == 1)
881    {
882        // It's an ace and the pile is empty - ACCEPT
883        retval = true;
884    }
885    return retval;
886}
887
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    {
901        if (m_dx == 0 && m_dy == 0)
902        {
903            m_cards[m_topCard]->Draw(dc, m_x, m_y);
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;
917                    y = m_y + CardHeight / 3;
918                }
919            }
920        }
921    }
922    else
923    {
924        Card::DrawNullCard(dc, m_x, m_y);
925    }
926}
927
928
929void Discard::GetTopCardPos(int& x, int& y)
930{
931    if (m_topCard < 0)
932    {
933        x = m_x;
934        y = m_y;
935    }
936    else if (m_topCard > 31)
937    {
938        x = m_x + m_dx * (m_topCard - 32);
939        y = m_y + CardHeight / 3;
940    }
941    else
942    {
943        x = m_x + m_dx * m_topCard;
944        y = m_y;
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    {
955        card = Pile::RemoveTopCard(dc, m_xOffset, m_yOffset);
956    }
957    else
958    {
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        {
969            m_cards[i]->Draw(dc, m_x - m_xOffset + i * m_dx, m_y - m_yOffset);
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();
976    }
977
978    return card;
979}
980