3f66f6a5b3
This keyword is not expanded by Git which means it's not replaced with the correct revision value in the releases made using git-based scripts and it's confusing to have lines with unexpanded "$Id$" in the released files. As expanding them with Git is not that simple (it could be done with git archive and export-subst attribute) and there are not many benefits in having them in the first place, just remove all these lines. If nothing else, this will make an eventual transition to Git simpler. Closes #14487. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@74602 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
979 lines
25 KiB
C++
979 lines
25 KiB
C++
/////////////////////////////////////////////////////////////////////////////
|
|
// Name: game.cpp
|
|
// Purpose: Forty Thieves patience game
|
|
// Author: Chris Breeze
|
|
// Modified by:
|
|
// Created: 21/07/97
|
|
// Copyright: (c) 1993-1998 Chris Breeze
|
|
// Licence: wxWindows licence
|
|
//---------------------------------------------------------------------------
|
|
// Last modified: 22nd July 1998 - ported to wxWidgets 2.0
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// For compilers that support precompilation, includes "wx/wx.h".
|
|
#include "wx/wxprec.h"
|
|
|
|
#ifdef __BORLANDC__
|
|
#pragma hdrstop
|
|
#endif
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include "wx/wx.h"
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <time.h>
|
|
#include <string.h>
|
|
#include "forty.h"
|
|
#include "game.h"
|
|
|
|
Game::Game(int wins, int games, int score) :
|
|
m_inPlay(false),
|
|
m_moveIndex(0),
|
|
m_redoIndex(0),
|
|
m_bmap(0),
|
|
m_bmapCard(0)
|
|
{
|
|
int i;
|
|
|
|
m_pack = new Pack(2, 2 + 4 * (CardHeight + 2));
|
|
srand(time(0));
|
|
|
|
for (i = 0; i < 5; i++) m_pack->Shuffle();
|
|
|
|
m_discard = new Discard(2, 2 + 5 * (CardHeight + 2));
|
|
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
m_foundations[i] = new Foundation(2 + (i / 4) * (CardWidth + 2),
|
|
2 + (i % 4) * (CardHeight + 2));
|
|
}
|
|
|
|
for (i = 0; i < 10; i++)
|
|
{
|
|
m_bases[i] = new Base(8 + (i + 2) * (CardWidth + 2), 2);
|
|
}
|
|
Deal();
|
|
m_srcPile = 0;
|
|
m_liftedCard = 0;
|
|
|
|
// copy the input parameters for future reference
|
|
m_numWins = wins;
|
|
m_numGames = games;
|
|
m_totalScore = score;
|
|
m_currentScore = 0;
|
|
}
|
|
|
|
|
|
void Game::Layout()
|
|
{
|
|
int i;
|
|
|
|
m_pack->SetPos(2, 2 + 4 * (CardHeight + 2));
|
|
|
|
m_discard->SetPos(2, 2 + 5 * (CardHeight + 2));
|
|
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
m_foundations[i]->SetPos(2 + (i / 4) * (CardWidth + 2),
|
|
2 + (i % 4) * (CardHeight + 2));
|
|
}
|
|
|
|
for (i = 0; i < 10; i++)
|
|
{
|
|
m_bases[i]->SetPos(8 + (i + 2) * (CardWidth + 2), 2);
|
|
}
|
|
delete m_bmap;
|
|
delete m_bmapCard;
|
|
m_bmap = 0;
|
|
m_bmapCard = 0;
|
|
}
|
|
|
|
// Make sure we delete all objects created by the game object
|
|
Game::~Game()
|
|
{
|
|
int i;
|
|
|
|
delete m_pack;
|
|
delete m_discard;
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
delete m_foundations[i];
|
|
}
|
|
for (i = 0; i < 10; i++)
|
|
{
|
|
delete m_bases[i];
|
|
}
|
|
delete m_bmap;
|
|
delete m_bmapCard;
|
|
}
|
|
|
|
/*
|
|
Set the score for a new player.
|
|
NB: call Deal() first if the new player is to start
|
|
a new game
|
|
*/
|
|
void Game::NewPlayer(int wins, int games, int score)
|
|
{
|
|
m_numWins = wins;
|
|
m_numGames = games;
|
|
m_totalScore = score;
|
|
m_currentScore = 0;
|
|
}
|
|
|
|
// Undo the last move
|
|
void Game::Undo(wxDC& dc)
|
|
{
|
|
if (m_moveIndex > 0)
|
|
{
|
|
m_moveIndex--;
|
|
Card* card = m_moves[m_moveIndex].dest->RemoveTopCard(dc);
|
|
m_moves[m_moveIndex].src->AddCard(dc, card);
|
|
DisplayScore(dc);
|
|
}
|
|
}
|
|
|
|
// Redo the last move
|
|
void Game::Redo(wxDC& dc)
|
|
{
|
|
if (m_moveIndex < m_redoIndex)
|
|
{
|
|
Card* card = m_moves[m_moveIndex].src->RemoveTopCard(dc);
|
|
if (m_moves[m_moveIndex].src == m_pack)
|
|
{
|
|
m_pack->Redraw(dc);
|
|
card->TurnCard(faceup);
|
|
}
|
|
m_moves[m_moveIndex].dest->AddCard(dc, card);
|
|
DisplayScore(dc);
|
|
m_moveIndex++;
|
|
}
|
|
}
|
|
|
|
void Game::DoMove(wxDC& dc, Pile* src, Pile* dest)
|
|
{
|
|
if (m_moveIndex < MaxMoves)
|
|
{
|
|
if (src == dest)
|
|
{
|
|
wxMessageBox(wxT("Game::DoMove() src == dest"), wxT("Debug message"),
|
|
wxOK | wxICON_EXCLAMATION);
|
|
}
|
|
m_moves[m_moveIndex].src = src;
|
|
m_moves[m_moveIndex].dest = dest;
|
|
m_moveIndex++;
|
|
|
|
// when we do a move any moves in redo buffer are discarded
|
|
m_redoIndex = m_moveIndex;
|
|
}
|
|
else
|
|
{
|
|
wxMessageBox(wxT("Game::DoMove() Undo buffer full"), wxT("Debug message"),
|
|
wxOK | wxICON_EXCLAMATION);
|
|
}
|
|
|
|
if (!m_inPlay)
|
|
{
|
|
m_inPlay = true;
|
|
m_numGames++;
|
|
}
|
|
DisplayScore(dc);
|
|
|
|
if (HaveYouWon())
|
|
{
|
|
wxWindow *frame = wxTheApp->GetTopWindow();
|
|
wxWindow *canvas = (wxWindow *) NULL;
|
|
|
|
if (frame)
|
|
{
|
|
wxWindowList::compatibility_iterator node = frame->GetChildren().GetFirst();
|
|
if (node) canvas = (wxWindow*)node->GetData();
|
|
}
|
|
|
|
// This game is over
|
|
m_inPlay = false;
|
|
|
|
// Redraw the score box to update games won
|
|
DisplayScore(dc);
|
|
|
|
if (wxMessageBox(wxT("Do you wish to play again?"),
|
|
wxT("Well Done, You have won!"), wxYES_NO | wxICON_QUESTION) == wxYES)
|
|
{
|
|
Deal();
|
|
canvas->Refresh();
|
|
}
|
|
else
|
|
{
|
|
// user cancelled the dialog - exit the app
|
|
((wxFrame*)canvas->GetParent())->Close(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void Game::DisplayScore(wxDC& dc)
|
|
{
|
|
wxColour bgColour = FortyApp::BackgroundColour();
|
|
wxPen* pen = wxThePenList->FindOrCreatePen(bgColour, 1, wxSOLID);
|
|
dc.SetTextBackground(bgColour);
|
|
dc.SetTextForeground(FortyApp::TextColour());
|
|
dc.SetBrush(FortyApp::BackgroundBrush());
|
|
dc.SetPen(* pen);
|
|
|
|
// count the number of cards in foundations
|
|
m_currentScore = 0;
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
m_currentScore += m_foundations[i]->GetNumCards();
|
|
}
|
|
|
|
int x, y;
|
|
m_pack->GetTopCardPos(x, y);
|
|
x += 12 * CardWidth - 105;
|
|
|
|
wxCoord w, h;
|
|
{
|
|
wxCoord width, height;
|
|
dc.GetTextExtent(wxT("Average score:m_x"), &width, &height);
|
|
w = width;
|
|
h = height;
|
|
}
|
|
dc.DrawRectangle(x + w, y, 20, 4 * h);
|
|
|
|
wxString str;
|
|
str.Printf(wxT("%d"), m_currentScore);
|
|
dc.DrawText(wxT("Score:"), x, y);
|
|
dc.DrawText(str, x + w, y);
|
|
y += h;
|
|
|
|
str.Printf(wxT("%d"), m_numGames);
|
|
dc.DrawText(wxT("Games played:"), x, y);
|
|
dc.DrawText(str, x + w, y);
|
|
y += h;
|
|
|
|
str.Printf(wxT("%d"), m_numWins);
|
|
dc.DrawText(wxT("Games won:"), x, y);
|
|
dc.DrawText(str, x + w, y);
|
|
y += h;
|
|
|
|
int average = 0;
|
|
if (m_numGames > 0)
|
|
{
|
|
average = (2 * (m_currentScore + m_totalScore) + m_numGames ) / (2 * m_numGames);
|
|
}
|
|
str.Printf(wxT("%d"), average);
|
|
dc.DrawText(wxT("Average score:"), x, y);
|
|
dc.DrawText(str, x + w, y);
|
|
}
|
|
|
|
|
|
// Shuffle the m_pack and deal the cards
|
|
void Game::Deal()
|
|
{
|
|
int i, j;
|
|
Card* card;
|
|
|
|
// Reset all the piles, the undo buffer and shuffle the m_pack
|
|
m_moveIndex = 0;
|
|
m_pack->ResetPile();
|
|
for (i = 0; i < 5; i++)
|
|
{
|
|
m_pack->Shuffle();
|
|
}
|
|
m_discard->ResetPile();
|
|
for (i = 0; i < 10; i++)
|
|
{
|
|
m_bases[i]->ResetPile();
|
|
}
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
m_foundations[i]->ResetPile();
|
|
}
|
|
|
|
// Deal the initial 40 cards onto the bases
|
|
for (i = 0; i < 10; i++)
|
|
{
|
|
for (j = 1; j <= 4; j++)
|
|
{
|
|
card = m_pack->RemoveTopCard();
|
|
card->TurnCard(faceup);
|
|
m_bases[i]->AddCard(card);
|
|
}
|
|
}
|
|
|
|
if (m_inPlay)
|
|
{
|
|
// player has started the game and then redealt
|
|
// and so we must add the score for this game to the total score
|
|
m_totalScore += m_currentScore;
|
|
}
|
|
m_currentScore = 0;
|
|
m_inPlay = false;
|
|
}
|
|
|
|
|
|
// Redraw the m_pack, discard pile, the bases and the foundations
|
|
void Game::Redraw(wxDC& dc)
|
|
{
|
|
int i;
|
|
m_pack->Redraw(dc);
|
|
m_discard->Redraw(dc);
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
m_foundations[i]->Redraw(dc);
|
|
}
|
|
for (i = 0; i < 10; i++)
|
|
{
|
|
m_bases[i]->Redraw(dc);
|
|
}
|
|
DisplayScore(dc);
|
|
|
|
if (m_bmap == 0)
|
|
{
|
|
m_bmap = new wxBitmap(CardWidth, CardHeight);
|
|
m_bmapCard = new wxBitmap(CardWidth, CardHeight);
|
|
|
|
// Initialise the card bitmap to the background colour
|
|
wxMemoryDC memoryDC;
|
|
memoryDC.SelectObject(*m_bmapCard);
|
|
memoryDC.SetPen( *wxTRANSPARENT_PEN );
|
|
memoryDC.SetBrush(FortyApp::BackgroundBrush());
|
|
memoryDC.DrawRectangle(0, 0, CardWidth, CardHeight);
|
|
memoryDC.SelectObject(*m_bmap);
|
|
memoryDC.DrawRectangle(0, 0, CardWidth, CardHeight);
|
|
memoryDC.SelectObject(wxNullBitmap);
|
|
}
|
|
}
|
|
|
|
|
|
// Test to see if the point (x, y) is over the top card of one of the piles
|
|
// Returns pointer to the pile, or 0 if (x, y) is not over a pile
|
|
// or the pile is empty
|
|
Pile* Game::WhichPile(int x, int y)
|
|
{
|
|
if (m_pack->GetCard(x, y) &&
|
|
m_pack->GetCard(x, y) == m_pack->GetTopCard())
|
|
{
|
|
return m_pack;
|
|
}
|
|
|
|
if (m_discard->GetCard(x, y) &&
|
|
m_discard->GetCard(x, y) == m_discard->GetTopCard())
|
|
{
|
|
return m_discard;
|
|
}
|
|
|
|
int i;
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
if (m_foundations[i]->GetCard(x, y) &&
|
|
m_foundations[i]->GetCard(x, y) == m_foundations[i]->GetTopCard())
|
|
{
|
|
return m_foundations[i];
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < 10; i++)
|
|
{
|
|
if (m_bases[i]->GetCard(x, y) &&
|
|
m_bases[i]->GetCard(x, y) == m_bases[i]->GetTopCard())
|
|
{
|
|
return m_bases[i];
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
// Left button is pressed - if cursor is over the m_pack then deal a card
|
|
// otherwise if it is over a card pick it up ready to be dragged - see MouseMove()
|
|
bool Game::LButtonDown(wxDC& dc, int x, int y)
|
|
{
|
|
m_srcPile = WhichPile(x, y);
|
|
if (m_srcPile == m_pack)
|
|
{
|
|
Card* card = m_pack->RemoveTopCard();
|
|
if (card)
|
|
{
|
|
m_pack->Redraw(dc);
|
|
card->TurnCard(faceup);
|
|
m_discard->AddCard(dc, card);
|
|
DoMove(dc, m_pack, m_discard);
|
|
}
|
|
m_srcPile = 0;
|
|
}
|
|
else if (m_srcPile)
|
|
{
|
|
m_srcPile->GetTopCardPos(m_xPos, m_yPos);
|
|
m_xOffset = m_xPos - x;
|
|
m_yOffset = m_yPos - y;
|
|
|
|
// Copy the area under the card
|
|
// Initialise the card bitmap to the background colour
|
|
{
|
|
wxMemoryDC memoryDC;
|
|
memoryDC.SelectObject(*m_bmap);
|
|
m_liftedCard = m_srcPile->RemoveTopCard(memoryDC, m_xPos, m_yPos);
|
|
}
|
|
|
|
// Draw the card in card bitmap ready for blitting onto
|
|
// the screen
|
|
{
|
|
wxMemoryDC memoryDC;
|
|
memoryDC.SelectObject(*m_bmapCard);
|
|
m_liftedCard->Draw(memoryDC, 0, 0);
|
|
}
|
|
}
|
|
return m_srcPile != 0;
|
|
}
|
|
|
|
// Called when the left button is double clicked
|
|
// If a card is under the pointer and it can move elsewhere then move it.
|
|
// Move onto a foundation as first choice, a populated base as second and
|
|
// an empty base as third choice.
|
|
// NB Cards in the m_pack cannot be moved in this way - they aren't in play
|
|
// yet
|
|
void Game::LButtonDblClk(wxDC& dc, int x, int y)
|
|
{
|
|
Pile* pile = WhichPile(x, y);
|
|
if (!pile) return;
|
|
|
|
// Double click on m_pack is the same as left button down
|
|
if (pile == m_pack)
|
|
{
|
|
LButtonDown(dc, x, y);
|
|
}
|
|
else
|
|
{
|
|
Card* card = pile->GetTopCard();
|
|
|
|
if (card)
|
|
{
|
|
int i;
|
|
|
|
// if the card is an ace then try to place it next
|
|
// to an ace of the same suit
|
|
if (card->GetPipValue() == 1)
|
|
{
|
|
for(i = 0; i < 4; i++)
|
|
{
|
|
Card* m_topCard = m_foundations[i]->GetTopCard();
|
|
if ( m_topCard )
|
|
{
|
|
if (m_topCard->GetSuit() == card->GetSuit() &&
|
|
m_foundations[i + 4] != pile &&
|
|
m_foundations[i + 4]->GetTopCard() == 0)
|
|
{
|
|
pile->RemoveTopCard(dc);
|
|
m_foundations[i + 4]->AddCard(dc, card);
|
|
DoMove(dc, pile, m_foundations[i + 4]);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// try to place the card on a foundation
|
|
for(i = 0; i < 8; i++)
|
|
{
|
|
if (m_foundations[i]->AcceptCard(card) && m_foundations[i] != pile)
|
|
{
|
|
pile->RemoveTopCard(dc);
|
|
m_foundations[i]->AddCard(dc, card);
|
|
DoMove(dc, pile, m_foundations[i]);
|
|
return;
|
|
}
|
|
}
|
|
// try to place the card on a populated base
|
|
for(i = 0; i < 10; i++)
|
|
{
|
|
if (m_bases[i]->AcceptCard(card) &&
|
|
m_bases[i] != pile &&
|
|
m_bases[i]->GetTopCard())
|
|
{
|
|
pile->RemoveTopCard(dc);
|
|
m_bases[i]->AddCard(dc, card);
|
|
DoMove(dc, pile, m_bases[i]);
|
|
return;
|
|
}
|
|
}
|
|
// try to place the card on any base
|
|
for(i = 0; i < 10; i++)
|
|
{
|
|
if (m_bases[i]->AcceptCard(card) && m_bases[i] != pile)
|
|
{
|
|
pile->RemoveTopCard(dc);
|
|
m_bases[i]->AddCard(dc, card);
|
|
DoMove(dc, pile, m_bases[i]);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Test to see whether the game has been won:
|
|
// i.e. m_pack, discard and bases are empty
|
|
bool Game::HaveYouWon()
|
|
{
|
|
if (m_pack->GetTopCard()) return false;
|
|
if (m_discard->GetTopCard()) return false;
|
|
for(int i = 0; i < 10; i++)
|
|
{
|
|
if (m_bases[i]->GetTopCard()) return false;
|
|
}
|
|
m_numWins++;
|
|
m_totalScore += m_currentScore;
|
|
m_currentScore = 0;
|
|
return true;
|
|
}
|
|
|
|
|
|
// See whether the card under the cursor can be moved somewhere else
|
|
// Returns 'true' if it can be moved, 'false' otherwise
|
|
bool Game::CanYouGo(int x, int y)
|
|
{
|
|
Pile* pile = WhichPile(x, y);
|
|
if (pile && pile != m_pack)
|
|
{
|
|
Card* card = pile->GetTopCard();
|
|
|
|
if (card)
|
|
{
|
|
int i;
|
|
for(i = 0; i < 8; i++)
|
|
{
|
|
if (m_foundations[i]->AcceptCard(card) && m_foundations[i] != pile)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
for(i = 0; i < 10; i++)
|
|
{
|
|
if (m_bases[i]->GetTopCard() &&
|
|
m_bases[i]->AcceptCard(card) &&
|
|
m_bases[i] != pile)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
// Called when the left button is released after dragging a card
|
|
// Scan the piles to see if this card overlaps a pile and can be added
|
|
// to the pile. If the card overlaps more than one pile on which it can be placed
|
|
// then put it on the nearest pile.
|
|
void Game::LButtonUp(wxDC& dc, int x, int y)
|
|
{
|
|
if (m_srcPile)
|
|
{
|
|
// work out the position of the dragged card
|
|
x += m_xOffset;
|
|
y += m_yOffset;
|
|
|
|
Pile* nearestPile = 0;
|
|
int distance = (CardHeight + CardWidth) * (CardHeight + CardWidth);
|
|
|
|
// find the nearest pile which will accept the card
|
|
int i;
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
if (DropCard(x, y, m_foundations[i], m_liftedCard))
|
|
{
|
|
if (m_foundations[i]->CalcDistance(x, y) < distance)
|
|
{
|
|
nearestPile = m_foundations[i];
|
|
distance = nearestPile->CalcDistance(x, y);
|
|
}
|
|
}
|
|
}
|
|
for (i = 0; i < 10; i++)
|
|
{
|
|
if (DropCard(x, y, m_bases[i], m_liftedCard))
|
|
{
|
|
if (m_bases[i]->CalcDistance(x, y) < distance)
|
|
{
|
|
nearestPile = m_bases[i];
|
|
distance = nearestPile->CalcDistance(x, y);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Restore the area under the card
|
|
wxMemoryDC memoryDC;
|
|
memoryDC.SelectObject(*m_bmap);
|
|
dc.Blit(m_xPos, m_yPos, CardWidth, CardHeight,
|
|
&memoryDC, 0, 0, wxCOPY);
|
|
|
|
// Draw the card in its new position
|
|
if (nearestPile)
|
|
{
|
|
// Add to new pile
|
|
nearestPile->AddCard(dc, m_liftedCard);
|
|
if (nearestPile != m_srcPile)
|
|
{
|
|
DoMove(dc, m_srcPile, nearestPile);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Return card to src pile
|
|
m_srcPile->AddCard(dc, m_liftedCard);
|
|
}
|
|
m_srcPile = 0;
|
|
m_liftedCard = 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Game::DropCard(int x, int y, Pile* pile, Card* card)
|
|
{
|
|
bool retval = false;
|
|
if (pile->Overlap(x, y))
|
|
{
|
|
if (pile->AcceptCard(card))
|
|
{
|
|
retval = true;
|
|
}
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
|
|
void Game::MouseMove(wxDC& dc, int mx, int my)
|
|
{
|
|
if (m_liftedCard)
|
|
{
|
|
wxMemoryDC memoryDC;
|
|
memoryDC.SelectObject(*m_bmap);
|
|
|
|
int dx = mx + m_xOffset - m_xPos;
|
|
int dy = my + m_yOffset - m_yPos;
|
|
|
|
if (abs(dx) >= CardWidth || abs(dy) >= CardHeight)
|
|
{
|
|
// Restore the area under the card
|
|
dc.Blit(m_xPos, m_yPos, CardWidth, CardHeight,
|
|
&memoryDC, 0, 0, wxCOPY);
|
|
|
|
// Copy the area under the card in the new position
|
|
memoryDC.Blit(0, 0, CardWidth, CardHeight,
|
|
&dc, m_xPos + dx, m_yPos + dy, wxCOPY);
|
|
}
|
|
else if (dx >= 0)
|
|
{
|
|
// dx >= 0
|
|
dc.Blit(m_xPos, m_yPos, dx, CardHeight, &memoryDC, 0, 0, wxCOPY);
|
|
if (dy >= 0)
|
|
{
|
|
// dy >= 0
|
|
dc.Blit(m_xPos + dx, m_yPos, CardWidth - dx, dy, &memoryDC, dx, 0, wxCOPY);
|
|
memoryDC.Blit(0, 0, CardWidth - dx, CardHeight - dy,
|
|
&memoryDC, dx, dy, wxCOPY);
|
|
memoryDC.Blit(0, CardHeight - dy, CardWidth - dx, dy,
|
|
&dc, m_xPos + dx, m_yPos + CardHeight, wxCOPY);
|
|
}
|
|
else
|
|
{
|
|
// dy < 0
|
|
dc.Blit(m_xPos + dx, m_yPos + dy + CardHeight, CardWidth - dx, -dy,
|
|
&memoryDC, dx, CardHeight + dy, wxCOPY);
|
|
memoryDC.Blit(0, -dy, CardWidth - dx, CardHeight + dy,
|
|
&memoryDC, dx, 0, wxCOPY);
|
|
memoryDC.Blit(0, 0, CardWidth - dx, -dy,
|
|
&dc, m_xPos + dx, m_yPos + dy, wxCOPY);
|
|
}
|
|
memoryDC.Blit(CardWidth - dx, 0, dx, CardHeight,
|
|
&dc, m_xPos + CardWidth, m_yPos + dy, wxCOPY);
|
|
}
|
|
else
|
|
{
|
|
// dx < 0
|
|
dc.Blit(m_xPos + CardWidth + dx, m_yPos, -dx, CardHeight,
|
|
&memoryDC, CardWidth + dx, 0, wxCOPY);
|
|
if (dy >= 0)
|
|
{
|
|
dc.Blit(m_xPos, m_yPos, CardWidth + dx, dy, &memoryDC, 0, 0, wxCOPY);
|
|
memoryDC.Blit(-dx, 0, CardWidth + dx, CardHeight - dy,
|
|
&memoryDC, 0, dy, wxCOPY);
|
|
memoryDC.Blit(-dx, CardHeight - dy, CardWidth + dx, dy,
|
|
&dc, m_xPos, m_yPos + CardHeight, wxCOPY);
|
|
}
|
|
else
|
|
{
|
|
// dy < 0
|
|
dc.Blit(m_xPos, m_yPos + CardHeight + dy, CardWidth + dx, -dy,
|
|
&memoryDC, 0, CardHeight + dy, wxCOPY);
|
|
memoryDC.Blit(-dx, -dy, CardWidth + dx, CardHeight + dy,
|
|
&memoryDC, 0, 0, wxCOPY);
|
|
memoryDC.Blit(-dx, 0, CardWidth + dx, -dy,
|
|
&dc, m_xPos, m_yPos + dy, wxCOPY);
|
|
}
|
|
memoryDC.Blit(0, 0, -dx, CardHeight,
|
|
&dc, m_xPos + dx, m_yPos + dy, wxCOPY);
|
|
}
|
|
m_xPos += dx;
|
|
m_yPos += dy;
|
|
|
|
// draw the card in its new position
|
|
memoryDC.SelectObject(*m_bmapCard);
|
|
dc.Blit(m_xPos, m_yPos, CardWidth, CardHeight,
|
|
&memoryDC, 0, 0, wxCOPY);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------//
|
|
// The Pack class: holds the two decks of cards //
|
|
//----------------------------------------------//
|
|
Pack::Pack(int x, int y) : Pile(x, y, 0, 0)
|
|
{
|
|
for (m_topCard = 0; m_topCard < NumCards; m_topCard++)
|
|
{
|
|
m_cards[m_topCard] = new Card(1 + m_topCard / 2, facedown);
|
|
}
|
|
m_topCard = NumCards - 1;
|
|
}
|
|
|
|
|
|
void Pack::Shuffle()
|
|
{
|
|
Card* temp[NumCards];
|
|
int i;
|
|
|
|
// Don't try to shuffle an empty m_pack!
|
|
if (m_topCard < 0) return;
|
|
|
|
// Copy the cards into a temporary array. Start by clearing
|
|
// the array and then copy the card into a random position.
|
|
// If the position is occupied then find the next lower position.
|
|
for (i = 0; i <= m_topCard; i++)
|
|
{
|
|
temp[i] = 0;
|
|
}
|
|
for (i = 0; i <= m_topCard; i++)
|
|
{
|
|
int pos = rand() % (m_topCard + 1);
|
|
while (temp[pos])
|
|
{
|
|
pos--;
|
|
if (pos < 0) pos = m_topCard;
|
|
}
|
|
m_cards[i]->TurnCard(facedown);
|
|
temp[pos] = m_cards[i];
|
|
m_cards[i] = 0;
|
|
}
|
|
|
|
// Copy each card back into the m_pack in a random
|
|
// position. If position is occupied then find nearest
|
|
// unoccupied position after the random position.
|
|
for (i = 0; i <= m_topCard; i++)
|
|
{
|
|
int pos = rand() % (m_topCard + 1);
|
|
while (m_cards[pos])
|
|
{
|
|
pos++;
|
|
if (pos > m_topCard) pos = 0;
|
|
}
|
|
m_cards[pos] = temp[i];
|
|
}
|
|
}
|
|
|
|
void Pack::Redraw(wxDC& dc)
|
|
{
|
|
Pile::Redraw(dc);
|
|
|
|
wxString str;
|
|
str.Printf(wxT("%d "), m_topCard + 1);
|
|
|
|
dc.SetBackgroundMode( wxSOLID );
|
|
dc.SetTextBackground(FortyApp::BackgroundColour());
|
|
dc.SetTextForeground(FortyApp::TextColour());
|
|
dc.DrawText(str, m_x + CardWidth + 5, m_y + CardHeight / 2);
|
|
|
|
}
|
|
|
|
void Pack::AddCard(Card* card)
|
|
{
|
|
if (card == m_cards[m_topCard + 1])
|
|
{
|
|
m_topCard++;
|
|
}
|
|
else
|
|
{
|
|
wxMessageBox(wxT("Pack::AddCard() Undo error"), wxT("Forty Thieves: Warning"),
|
|
wxOK | wxICON_EXCLAMATION);
|
|
}
|
|
card->TurnCard(facedown);
|
|
}
|
|
|
|
|
|
Pack::~Pack()
|
|
{
|
|
for (m_topCard = 0; m_topCard < NumCards; m_topCard++)
|
|
{
|
|
delete m_cards[m_topCard];
|
|
}
|
|
}
|
|
|
|
|
|
//------------------------------------------------------//
|
|
// The Base class: holds the initial pile of four cards //
|
|
//------------------------------------------------------//
|
|
Base::Base(int x, int y) : Pile(x, y, 0, 12)
|
|
{
|
|
m_topCard = -1;
|
|
}
|
|
|
|
|
|
bool Base::AcceptCard(Card* card)
|
|
{
|
|
bool retval = false;
|
|
|
|
if (m_topCard >= 0)
|
|
{
|
|
if (m_cards[m_topCard]->GetSuit() == card->GetSuit() &&
|
|
m_cards[m_topCard]->GetPipValue() - 1 == card->GetPipValue())
|
|
{
|
|
retval = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// pile is empty - ACCEPT
|
|
retval = true;
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------//
|
|
// The Foundation class: holds the cards built up from the ace... //
|
|
//----------------------------------------------------------------//
|
|
Foundation::Foundation(int x, int y) : Pile(x, y, 0, 0)
|
|
{
|
|
m_topCard = -1;
|
|
}
|
|
|
|
bool Foundation::AcceptCard(Card* card)
|
|
{
|
|
bool retval = false;
|
|
|
|
if (m_topCard >= 0)
|
|
{
|
|
if (m_cards[m_topCard]->GetSuit() == card->GetSuit() &&
|
|
m_cards[m_topCard]->GetPipValue() + 1 == card->GetPipValue())
|
|
{
|
|
retval = true;
|
|
}
|
|
}
|
|
else if (card->GetPipValue() == 1)
|
|
{
|
|
// It's an ace and the pile is empty - ACCEPT
|
|
retval = true;
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------//
|
|
// The Discard class: holds cards dealt from the m_pack //
|
|
//----------------------------------------------------//
|
|
Discard::Discard(int x, int y) : Pile(x, y, 19, 0)
|
|
{
|
|
m_topCard = -1;
|
|
}
|
|
|
|
void Discard::Redraw(wxDC& dc)
|
|
{
|
|
if (m_topCard >= 0)
|
|
{
|
|
if (m_dx == 0 && m_dy == 0)
|
|
{
|
|
m_cards[m_topCard]->Draw(dc, m_x, m_y);
|
|
}
|
|
else
|
|
{
|
|
int x = m_x;
|
|
int y = m_y;
|
|
for (int i = 0; i <= m_topCard; i++)
|
|
{
|
|
m_cards[i]->Draw(dc, x, y);
|
|
x += m_dx;
|
|
y += m_dy;
|
|
if (i == 31)
|
|
{
|
|
x = m_x;
|
|
y = m_y + CardHeight / 3;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Card::DrawNullCard(dc, m_x, m_y);
|
|
}
|
|
}
|
|
|
|
|
|
void Discard::GetTopCardPos(int& x, int& y)
|
|
{
|
|
if (m_topCard < 0)
|
|
{
|
|
x = m_x;
|
|
y = m_y;
|
|
}
|
|
else if (m_topCard > 31)
|
|
{
|
|
x = m_x + m_dx * (m_topCard - 32);
|
|
y = m_y + CardHeight / 3;
|
|
}
|
|
else
|
|
{
|
|
x = m_x + m_dx * m_topCard;
|
|
y = m_y;
|
|
}
|
|
}
|
|
|
|
|
|
Card* Discard::RemoveTopCard(wxDC& dc, int m_xOffset, int m_yOffset)
|
|
{
|
|
Card* card;
|
|
|
|
if (m_topCard <= 31)
|
|
{
|
|
card = Pile::RemoveTopCard(dc, m_xOffset, m_yOffset);
|
|
}
|
|
else
|
|
{
|
|
int topX, topY, x, y;
|
|
GetTopCardPos(topX, topY);
|
|
card = Pile::RemoveTopCard();
|
|
card->Erase(dc, topX - m_xOffset, topY - m_yOffset);
|
|
GetTopCardPos(x, y);
|
|
dc.SetClippingRegion(topX - m_xOffset, topY - m_yOffset,
|
|
CardWidth, CardHeight);
|
|
|
|
for (int i = m_topCard - 31; i <= m_topCard - 31 + CardWidth / m_dx; i++)
|
|
{
|
|
m_cards[i]->Draw(dc, m_x - m_xOffset + i * m_dx, m_y - m_yOffset);
|
|
}
|
|
if (m_topCard > 31)
|
|
{
|
|
m_cards[m_topCard]->Draw(dc, topX - m_xOffset - m_dx, topY - m_yOffset);
|
|
}
|
|
dc.DestroyClippingRegion();
|
|
}
|
|
|
|
return card;
|
|
}
|