home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / wxos2233.zip / wxOS2-2_3_3.zip / wxWindows-2.3.3 / demos / forty / game.cpp < prev    next >
C/C++ Source or Header  |  2002-03-06  |  24KB  |  1,001 lines

  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,v 1.5 2002/03/06 17:50:52 JS Exp $
  8. // Copyright:   (c) 1993-1998 Chris Breeze
  9. // Licence:       wxWindows licence
  10. //---------------------------------------------------------------------------
  11. // Last modified: 22nd July 1998 - ported to wxWindows 2.0
  12. /////////////////////////////////////////////////////////////////////////////
  13.  
  14. #ifdef __GNUG__
  15. #pragma implementation
  16. #pragma interface
  17. #endif
  18.  
  19. // For compilers that support precompilation, includes "wx/wx.h".
  20. #include "wx/wxprec.h"
  21.  
  22. #ifdef __BORLANDC__
  23. #pragma hdrstop
  24. #endif
  25.  
  26. #ifndef WX_PRECOMP
  27. #include "wx/wx.h"
  28. #endif
  29.  
  30. #include <stdlib.h>
  31. #include <stdio.h>
  32. #include <time.h>
  33. #include <string.h>
  34. #include "forty.h"
  35. #include "game.h"
  36.  
  37. Game::Game(int wins, int games, int score) :
  38.     m_inPlay(FALSE),
  39.     m_moveIndex(0),
  40.     m_redoIndex(0),
  41.     m_bmap(0),
  42.     m_bmapCard(0)
  43. {
  44.     int i;
  45.  
  46.     m_pack = new Pack(2, 2 + 4 * (CardHeight + 2));
  47.     srand(time(0));
  48.  
  49.     for (i = 0; i < 5; i++) m_pack->Shuffle();
  50.  
  51.     m_discard = new Discard(2, 2 + 5 * (CardHeight + 2));
  52.  
  53.     for (i = 0; i < 8; i++)
  54.     {
  55.         m_foundations[i] = new Foundation(2 + (i / 4) * (CardWidth + 2),
  56.                     2 + (i % 4) * (CardHeight + 2));
  57.     }
  58.  
  59.     for (i = 0; i < 10; i++)
  60.     {
  61.         m_bases[i] = new Base(8 + (i + 2) * (CardWidth + 2), 2);
  62.     }
  63.     Deal();
  64.     m_srcPile = 0;
  65.     m_liftedCard = 0;
  66.  
  67.     // copy the input parameters for future reference
  68.     m_numWins = wins;
  69.     m_numGames = games;
  70.     m_totalScore = score;
  71.     m_currentScore = 0;
  72. }
  73.  
  74.  
  75. 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("Game::DoMove() src == dest", "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("Game::DoMove() Undo buffer full", "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 = frame->GetChildren().First();
  197.             if (node) canvas = (wxWindow*)node->Data();
  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("Do you wish to play again?",
  207.             "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("Average score:m_x", &width, &height);
  245.         w = width;
  246.         h = height;
  247.     }
  248.     dc.DrawRectangle(x + w, y, 20, 4 * h);
  249.  
  250.     char str[80];
  251.     sprintf(str, "%d", m_currentScore);
  252.     dc.DrawText("Score:", x, y);
  253.     dc.DrawText(str, x + w, y);
  254.     y += h;
  255.  
  256.     sprintf(str, "%d", m_numGames);
  257.     dc.DrawText("Games played:", x, y);
  258.     dc.DrawText(str, x + w, y);
  259.     y += h;
  260.  
  261.     sprintf(str, "%d", m_numWins);
  262.     dc.DrawText("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.     sprintf(str, "%d", average);
  272.     dc.DrawText("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.     char str[10];
  802.     sprintf(str, "%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("Pack::AddCard() Undo error", "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. };
  1001.