|
|
/***************************************************************************/ /** Microsoft Windows **/ /** Copyright(c) Microsoft Corp., 1991, 1992 **/ /***************************************************************************/
/****************************************************************************
human.cpp
Aug 92, JimH May 93, JimH chico port
local_human and remote_human member functions
****************************************************************************/
#include "hearts.h"
#include "main.h" // friendly access
#include "resource.h"
#include "debug.h"
#include <stdio.h>
#include <stdlib.h> // abs() prototype
static CRect rectCard; // used in timer callback
// declare static members
BOOL local_human::bTimerOn; CString local_human::m_StatusText;
/****************************************************************************
human constructor -- abstract class
****************************************************************************/
human::human(int n, int pos) : player(n, pos) {
}
/****************************************************************************
local_human::local_human()
This is the constructor that initializes player::hWnd and player::hInst. It also creates the stretch bitmap that covers a card plus its popped height extension.
****************************************************************************/
local_human::local_human(int n) : human(n, 0) { m_pStatusWnd = new CStatusBarCtrl(); m_StatusText.LoadString(IDS_INTRO);
CClientDC dc(::pMainWnd);
m_pStatusWnd->Create(WS_CHILD|WS_VISIBLE|CCS_BOTTOM, CRect(), ::pMainWnd, 0); m_pStatusWnd->SetSimple(); UpdateStatus();
bTimerOn = FALSE;
if (!m_bmStretchCard.CreateCompatibleBitmap(&dc, card::dxCrd, card::dyCrd + POPSPACING)) { ::pMainWnd->FatalError(IDS_MEMORY); return; } }
/****************************************************************************
local_human destructor
****************************************************************************/
local_human::~local_human() { m_bmStretchCard.DeleteObject(); delete m_pStatusWnd; m_pStatusWnd = NULL; }
/****************************************************************************
local_human::Draw()
This virtual function draws selected cards in the popped up position. ALL is not used for slot in this variant.
****************************************************************************/
void local_human::Draw(CDC &dc, BOOL bCheating, SLOT slot) { DisplayName(dc); SLOT start = (slot == ALL ? 0 : slot); SLOT stop = (slot == ALL ? MAXSLOT : slot+1);
SLOT playedslot = EMPTY; // must draw cards in play last for EGA
for (SLOT s = start; s < stop; s++) { if (cd[s].IsPlayed()) playedslot = s; else cd[s].PopDraw(dc); // pop up selected cards
}
if (playedslot != EMPTY) cd[playedslot].Draw(dc); }
/****************************************************************************
local_human::PopCard()
handles mouse button selection of card to pass
****************************************************************************/
void local_human::PopCard(CBrush &brush, int x, int y) { SLOT s = XYToCard(x, y); if (s == EMPTY) return;
// count selected cards
int c = 0; for (int i = 0; i < MAXSLOT; i++) if (cd[i].IsSelected()) c++;
if (cd[s].IsSelected() && (c == 3)) { ::pMainWnd->PostMessage(WM_COMMAND, IDM_HIDEBUTTON); } else if (!cd[s].IsSelected()) { if (c == 3) // only allow three selections
return; else if (c == 2) ::pMainWnd->PostMessage(WM_COMMAND, IDM_SHOWBUTTON); }
// toggle selection
BOOL bSelected = cd[s].IsSelected(); cd[s].Select(!bSelected);
CClientDC dc(::pMainWnd); #ifdef USE_MIRRORING
SetLayout(dc.m_hDC, 0); SetLayout(dc.m_hAttribDC, 0); #endif
CDC memDC; memDC.CreateCompatibleDC(&dc); memDC.SelectObject(&m_bmStretchCard); memDC.SelectObject(&brush); memDC.PatBlt(0, 0, card::dxCrd, card::dyCrd + POPSPACING, PATCOPY);
for (i = 0; i < MAXSLOT; i++) { if (abs(i - s) <= (card::dxCrd / HORZSPACING)) { cd[i].Draw(memDC, // cdc
(i - s) * HORZSPACING, // x
cd[i].IsSelected() ? 0 : POPSPACING, // y
FACEUP, // mode
FALSE); // update loc?
} }
dc.BitBlt(loc.x + (HORZSPACING * s), loc.y - POPSPACING, card::dxCrd, card::dyCrd + POPSPACING, &memDC, 0, 0, SRCCOPY); }
/****************************************************************************
local_human::PlayCard()
handles mouse button selection of card to play and ensures move is legal.
PlayCard starts a timer that calls StartTimer() which calls TimerBadMove(). Think of it as one long function with a timer delay half way through.
****************************************************************************/
BOOL local_human::PlayCard(int x, int y, handinfotype &h, BOOL bCheating, BOOL bFlash) { SLOT s = XYToCard(x, y); if (s == EMPTY) return FALSE;
card *cardled = h.cardplayed[h.playerled]; BOOL bFirstTrick = (cardled != NULL && cardled->ID() == TWOCLUBS);
/* check if selected card is valid */
if (h.playerled == id) // if local human is leading...
{ if (cd[s].ID() != TWOCLUBS) { for (int i = 0; i < MAXSLOT; i++) // is there a two of clubs?
{ if ((i != s) && (cd[i].ID() == TWOCLUBS)) { UpdateStatus(IDS_LEAD2C); if (bFlash) StartTimer(cd[s]);
return FALSE; } } } if ((cd[s].Suit() == HEARTS) && (!h.bHeartsBroken)) // if hearts led
{ for (int i = 0; i < MAXSLOT; i++) // are there any non-hearts?
{ if ((!cd[i].IsEmpty()) && (cd[i].Suit() != HEARTS)) { UpdateStatus(IDS_LEADHEARTS); if (bFlash) StartTimer(cd[s]);
return FALSE; } } } }
// if not following suit
else if (cardled != NULL && (cd[s].Suit() != cardled->Suit())) { // make sure we're following suit if possible
for (int i = 0; i < MAXSLOT; i++) { if ((!cd[i].IsEmpty()) && (cd[i].Suit()==cardled->Suit())) { CString s1, s2; s1.LoadString(IDS_BADMOVE); s2.LoadString(IDS_SUIT0+cardled->Suit()); TCHAR string[80]; wsprintf(string, s1, s2);
if (bFlash) { UpdateStatus(string); StartTimer(cd[s]); }
return FALSE; } }
// make sure we're not trying to break the First Blood rule
if (bFirstTrick && ::pMainWnd->IsFirstBloodEnforced()) { BOOL bPointCard = (cd[s].Suit() == HEARTS || cd[s].ID() == BLACKLADY);
BOOL bOthersAvailable = FALSE;
for (int i = 0; i < MAXSLOT; i++) if ((!cd[i].IsEmpty()) && (cd[i].Suit() != HEARTS)) if (cd[i].ID() != BLACKLADY) bOthersAvailable = TRUE;
if (bPointCard && bOthersAvailable) { UpdateStatus(IDS_BADBLOOD); if (bFlash) StartTimer(cd[s]);
return FALSE; } } }
SetMode(WAITING); cd[s].Play(); h.cardplayed[id] = &(cd[s]);
::move.playerid = id; ::move.cardid = cd[s].ID(); ::move.playerled = h.playerled; ::move.turn = h.turn;
::pMainWnd->OnRef();
return TRUE; }
void local_human::StartTimer(card &c) { CClientDC dc(::pMainWnd); #ifdef USE_MIRRORING
SetLayout(dc.m_hDC, 0); SetLayout(dc.m_hAttribDC, 0); #endif
c.Draw(dc, HILITE); // flash
c.GetRect(rectCard);
if (::pMainWnd->SetTimer(1, 250, TimerBadMove)) { bTimerOn = TRUE; } else { bTimerOn = FALSE; ::pMainWnd->InvalidateRect(&rectCard, FALSE); } }
// MFC2 changes same as SetTimer in main2.cpp
#if defined (MFC1)
UINT FAR PASCAL EXPORT TimerBadMove(HWND hWnd, UINT nMsg, int nIDEvent, DWORD dwTime) { ::KillTimer(hWnd, 1); local_human::bTimerOn = FALSE; ::InvalidateRect(hWnd, &rectCard, FALSE); return 0; }
#else
void FAR PASCAL EXPORT TimerBadMove(HWND hWnd, UINT nMsg, UINT_PTR nIDEvent, DWORD dwTime) { ::KillTimer(hWnd, 1); local_human::bTimerOn = FALSE; #ifdef USE_MIRRORING
CRect rect; int i; DWORD ProcessDefaultLayout; if (GetProcessDefaultLayout(&ProcessDefaultLayout)) if (ProcessDefaultLayout == LAYOUT_RTL) { GetClientRect(hWnd, &rect); rectCard.left = abs(rect.right - rect.left) - rectCard.left; rectCard.right = abs(rect.right - rect.left) - rectCard.right; i = rectCard.left; rectCard.left = rectCard.right; rectCard.right = i; } #endif
::InvalidateRect(hWnd, &rectCard, FALSE);
}
#endif
/****************************************************************************
local_human::XYToCard()
returns a card slot number (or EMPTY) given a mouse location
****************************************************************************/
int local_human::XYToCard(int x, int y) { // check that we are in the right general area on the screen
if (y < (loc.y - POPSPACING)) return EMPTY;
if (y > (loc.y + card::dyCrd)) return EMPTY;
if (x < loc.x) return EMPTY;
if (x > (loc.x + (12 * HORZSPACING) + card::dxCrd)) return EMPTY;
// Take first stab at card selected.
SLOT s = (x - loc.x) / HORZSPACING; if (s > 12) s = 12;
// If the click is ABOVE the top of the normal card location,
// check to see if this is a selected card.
if (y < loc.y) { // If the card is bSelected, then we have it. If not, it could
// be overhanging other cards.
if (!cd[s].IsSelected()) { for (;;) { if (s == 0) return EMPTY; s--;
// if this card doesn't extend as far as x, give up
if ((loc.x + (s * HORZSPACING) + card::dxCrd) < x) return EMPTY;
// if this card is selected, we've got it
if (cd[s].IsSelected()) break; } } }
// a similar check is used to make sure we pick a card not yet played
if (!cd[s].IsInHand()) { for (;;) { if (s == 0) return EMPTY; s--;
// if this card doesn't extend as far as x, give up
if ((loc.x + (s * HORZSPACING) + card::dxCrd) < x) return EMPTY;
// if this card is selected, we've got it
if (cd[s].IsInHand()) break; } }
return s; }
/****************************************************************************
local_human::SelectCardsToPass()
This virtual function allows mouse clicks to mean select a card to play.
****************************************************************************/
void local_human::SelectCardsToPass() { SetMode(SELECTING); }
/****************************************************************************
local_human::SelectCardToPlay
Computer versions of this virtual function actually do the card selection. This local_human version marks the player as ready to select a card to play with the mouse, and updates the status to reflect this.
****************************************************************************/
void local_human::SelectCardToPlay(handinfotype &h, BOOL bCheating) { SetMode(PLAYING); UpdateStatus(IDS_GO); }
/****************************************************************************
local_human::UpdateStatus
The status bar can be updated either by manually filling m_StatusText or by passing a string resource id.
****************************************************************************/
void local_human::UpdateStatus(void) { m_pStatusWnd->SetText(m_StatusText, 255, 0); }
void local_human::UpdateStatus(int stringid) { status = stringid; m_StatusText.LoadString(stringid); UpdateStatus(); }
void local_human::UpdateStatus(const TCHAR *string) { m_StatusText = string; UpdateStatus(); }
/****************************************************************************
local_human::ReceiveSelectedCards
The parameter c[] is an array of three cards being passed from another player.
****************************************************************************/
void local_human::ReceiveSelectedCards(int c[]) { for (int i = 0, j = 0; j < 3; i++) { if (cd[i].IsSelected()) cd[i].SetID(c[j++]);
ASSERT(i < MAXSLOT); }
SetMode(ACCEPTING); UpdateStatus(IDS_ACCEPT); }
/****************************************************************************
local_human::WaitMessage()
Makes and shows the "Waiting for %s to move..." message
****************************************************************************/
void local_human::WaitMessage(const TCHAR *name) { TCHAR buf[100]; CString s;
s.LoadString(IDS_WAIT); wsprintf(buf, s, name); UpdateStatus(buf); }
|