|
|
/***************************************************************************/ /** Microsoft Windows **/ /** Copyright(c) Microsoft Corp., 1991, 1992 **/ /***************************************************************************/
/****************************************************************************
main.cpp
Aug 92, JimH May 93, JimH chico port
Main window callback functions Other CMainWindow member functions are in main2.cpp and welcome.cpp
****************************************************************************/
#include "hearts.h"
#include "main.h"
#include "resource.h"
#include "debug.h"
#include <regstr.h>
// declare static memberes
CBrush CMainWindow::m_BgndBrush; CRect CMainWindow::m_TableRect;
// declare globals
CMainWindow *pMainWnd;
MOVE move; // describes move for DDE transaction
int nStatusHeight; // height of status window
// Do not translate these registry strings
const TCHAR szRegPath[] = REGSTR_PATH_WINDOWSAPPLETS TEXT("\\Hearts"); const TCHAR regvalSound[] = TEXT("sound"); const TCHAR regvalName[] = TEXT("name"); const TCHAR regvalRole[] = TEXT("gamemeister"); const TCHAR regvalSpeed[] = TEXT("speed"); const TCHAR *regvalPName[3] = { TEXT("p1name"), TEXT("p2name"), TEXT("p3name") };
const TCHAR szHelpFileName[] = TEXT("mshearts.chm");
CTheApp theApp; // start Hearts and run it!
/****************************************************************************
CTheApp::InitInstance
****************************************************************************/
BOOL CTheApp::InitInstance() {
m_pMainWnd = new CMainWindow(m_lpCmdLine); m_pMainWnd->ShowWindow(SW_SHOW); // instead of m_nCmdShow
m_pMainWnd->UpdateWindow();
// Start the app off by posting Welcome dialog.
m_pMainWnd->PostMessage(WM_COMMAND, IDM_WELCOME);
return TRUE; }
BEGIN_MESSAGE_MAP( CMainWindow, CFrameWnd ) ON_COMMAND(IDM_ABOUT, OnAbout) ON_COMMAND(IDM_BOSSKEY, OnBossKey) ON_COMMAND(IDM_CHEAT, OnCheat) ON_COMMAND(IDM_EXIT, OnExit) ON_COMMAND(IDM_HELP, OnHelp) // ON_COMMAND(IDM_HELPONHELP, OnHelpOnHelp)
ON_COMMAND(IDM_HIDEBUTTON, OnHideButton) // ON_COMMAND(IDM_SEARCH, OnSearch)
ON_COMMAND(IDM_NEWGAME, OnNewGame) ON_COMMAND(IDM_OPTIONS, OnOptions) ON_COMMAND(IDM_QUOTE, OnQuote) ON_COMMAND(IDM_REF, OnRef) ON_COMMAND(IDM_SHOWBUTTON, OnShowButton) ON_COMMAND(IDM_SCORE, OnScore) ON_COMMAND(IDM_SOUND, OnSound) ON_COMMAND(IDM_WELCOME, OnWelcome)
ON_BN_CLICKED(IDM_BUTTON, OnPass)
ON_WM_CHAR() ON_MESSAGE(WM_PRINTCLIENT, OnPrintClient) ON_WM_CLOSE() ON_WM_CREATE() ON_WM_ERASEBKGND() ON_WM_LBUTTONDOWN() ON_WM_PAINT() END_MESSAGE_MAP()
/****************************************************************************
CMainWindow constructor
creates green background brush, and main hearts window
****************************************************************************/
CMainWindow::CMainWindow(LPTSTR lpCmdLine) : m_lpCmdLine(lpCmdLine), passdir(LEFT), bCheating(FALSE), bSoundOn(FALSE), bTimerOn(FALSE), bConstructed(TRUE), m_FatalErrno(0), bEnforceFirstBlood(TRUE) { #if !defined (MFC1)
m_bAutoMenuEnable = FALSE; // MFC 1.0 compatibility, required for MFC2
#endif
for (int i = 0; i < MAXPLAYER; i++) p[i] = NULL;
ResetHandInfo(-1); // set handinfo struct to default values
// Check for monochrome
CDC ic; ic.CreateIC(TEXT("DISPLAY"), NULL, NULL, NULL);
if (ic.GetDeviceCaps(NUMCOLORS) == 2) // if monochrome
m_bkgndcolor = RGB(255, 255, 255); // white background for mono
else m_bkgndcolor = RGB(0, 127, 0);
ic.DeleteDC();
m_BgndBrush.CreateSolidBrush(m_bkgndcolor); // destroyed in OnClose()
LoadAccelTable( TEXT("HeartsAccel") );
RECT rc; SystemParametersInfo(SPI_GETWORKAREA, 0, &rc, 0);
CRect rect; int dy = min(WINHEIGHT, (rc.bottom - rc.top));
int x, y; if (GetSystemMetrics(SM_CYSCREEN) <= 480) // VGA
{ x = (((rc.right - rc.left) - WINWIDTH) / 2) + rc.left; // centered
y = rc.top; } else { x = CW_USEDEFAULT; y = CW_USEDEFAULT; }
rect.SetRect(x, y, x+WINWIDTH, y+dy);
CString sAppname; sAppname.LoadString(IDS_APPNAME);
Create( NULL, // default class
sAppname, // window title
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_CLIPCHILDREN, // window style
rect, // size
NULL, // parent
TEXT("HeartsMenu")); // menu
}
/****************************************************************************
CMainWindow::OnAbout
displays about box
****************************************************************************/
//extern "C" int WINAPI ShellAbout(HWND, LPCSTR, LPCSTR, HICON);
void CMainWindow::OnAbout() { HICON hIcon = ::LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE(AFX_IDI_STD_FRAME));
CString s; s.LoadString(IDS_NETWORK); ShellAbout(m_hWnd, s, NULL, hIcon); }
/****************************************************************************
CMainWindow::OnQuote
displays quote box and plays quote.
****************************************************************************/
void CMainWindow::OnQuote() { CQuoteDlg quote(this); // HeartsPlaySound(SND_QUOTE);
quote.DoModal(); HeartsPlaySound(OFF); }
/****************************************************************************
CMainWindow::OnChar, looks space, plays first legal move, or pushes button
****************************************************************************/
void CMainWindow::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) { // We know the cast below is legal because position 0 is always
// the local human.
local_human *p0 = (local_human *)p[0];
int mode = p0->GetMode();
if ((nChar != (UINT)' ') || (p0->IsTimerOn())) return;
if (mode != PLAYING) return;
p0->SetMode(WAITING);
POINT loc;
for (SLOT s = 0; s < MAXSLOT; s++) { if (p0->GetCardLoc(s, loc)) { if (p0->PlayCard(loc.x, loc.y, handinfo, bCheating, FALSE)) { return; } } }
p0->SetMode(PLAYING); }
/****************************************************************************
CMainWindow::OnCheat -- toggles bCheating used to show all cards face up.
****************************************************************************/
void CMainWindow::OnCheat() { RegEntry Reg(szRegPath); const TCHAR val[] = TEXT("ZB"); TCHAR buf[20];
Reg.GetString(val, buf, sizeof(buf)); if (buf[0] != TEXT('4') || buf[1] != TEXT('2')) return;
bCheating = !bCheating; InvalidateRect(NULL, TRUE); // redraw main hearts window
CMenu *pMenu = GetMenu(); pMenu->CheckMenuItem(IDM_CHEAT, bCheating ? MF_CHECKED : MF_UNCHECKED); }
/****************************************************************************
CMainWindow::OnClose -- cleans up background brush, deletes players, etc.
****************************************************************************/
void CMainWindow::OnClose() { m_BgndBrush.DeleteObject();
for (int i = 0; i < 4; i++) { if (p[i]) { delete p[i]; p[i] = NULL; } }
::HtmlHelp(::GetDesktopWindow(), szHelpFileName, HH_CLOSE_ALL, 0);
{ RegEntry Reg(szRegPath); Reg.FlushKey(); }
DestroyWindow(); }
/****************************************************************************
CMainWindow::OnCreate -- creates pass button child window & player objects. also initializes some of the data members
****************************************************************************/
int CMainWindow::OnCreate(LPCREATESTRUCT lpCreateStruct) { ::pMainWnd = this;
if (!bConstructed) { FatalError(IDS_MEMORY); return -1; }
// Check for existence of cards.dll
SetErrorMode(SEM_NOOPENFILEERRORBOX); HINSTANCE hCardsDLL = LoadLibrary(TEXT("CARDS.DLL")); if (hCardsDLL < (HINSTANCE)HINSTANCE_ERROR) { FatalError(IDS_CARDSDLL); bConstructed = FALSE; return -1; } ::FreeLibrary(hCardsDLL);
CClientDC dc(this); TEXTMETRIC tm;
::srand((unsigned) ::time(NULL)); // set rand() seed
dc.GetTextMetrics(&tm); int nTextHeight = tm.tmHeight + tm.tmExternalLeading; m_StatusHeight = nTextHeight + 11; GetClientRect(m_TableRect);
m_TableRect.bottom -= m_StatusHeight;
bConstructed = TRUE;
// Player 0 is constructed as the gamemeister. This
// initializes lots of good stuff which is used later.
// If player 0 doesn't happen to be a real gamemeister,
// this gets fixed up in OnWelcome().
p[0] = new local_human(0); // display status bar
if (p[0] == NULL) { bConstructed = FALSE; return -1; }
// Construct pushbutton
int cxChar = tm.tmAveCharWidth; int cyChar = tm.tmHeight + tm.tmExternalLeading; int nWidth = (60 * cxChar) / 4; int nHeight = (14 * cyChar) / 8; int x = (m_TableRect.right / 2) - (nWidth / 2); int y = m_TableRect.bottom - card::dyCrd - (2 * POPSPACING) - nHeight; CRect rect; rect.SetRect(x, y, x+nWidth, y+nHeight);
if (!m_Button.Create(TEXT(""), WS_CHILD | BS_PUSHBUTTON, rect, this, IDM_BUTTON)) { bConstructed = FALSE; return -1; }
// check for sound capability
RegEntry Reg(szRegPath);
bHasSound = SoundInit(); if (bHasSound) { if (Reg.GetNumber(regvalSound, FALSE)) { CMenu *pMenu = GetMenu(); pMenu->CheckMenuItem(IDM_SOUND, MF_CHECKED); bSoundOn = TRUE; } } else { CMenu *pMenu = GetMenu(); pMenu->EnableMenuItem(IDM_SOUND, MF_GRAYED); }
card c; int nStepSize; DWORD dwSpeed = Reg.GetNumber(regvalSpeed, IDC_NORMAL);
if (dwSpeed == IDC_FAST) nStepSize = 60; else if (dwSpeed == IDC_SLOW) nStepSize = 5; else nStepSize = 15;
c.SetStepSize(nStepSize);
return (bConstructed ? 0 : -1); }
/****************************************************************************
CMainWindow::OnEraseBkgnd -- required to draw background green
****************************************************************************/
BOOL CMainWindow::OnEraseBkgnd(CDC *pDC) { if (!m_BgndBrush.m_hObject) // if background brush is not valid
return FALSE;
m_BgndBrush.UnrealizeObject(); CBrush *pOldBrush = pDC->SelectObject(&m_BgndBrush); pDC->PatBlt(0, 0, WINWIDTH, WINHEIGHT, PATCOPY); pDC->SelectObject(pOldBrush); return FALSE; }
/****************************************************************************
CMainWindow::OnLButtonDown
Handles human selecting card to play or pass.
****************************************************************************/
void CMainWindow::OnLButtonDown(UINT nFlags, CPoint point) { // We know the cast below is legal because position 0 is always
// the local human.
#ifdef USE_MIRRORING
CRect rect; DWORD ProcessDefaultLayout; if (GetProcessDefaultLayout(&ProcessDefaultLayout)) if (ProcessDefaultLayout == LAYOUT_RTL) { GetClientRect(&rect); point.x = rect.right - rect.left - point.x; } #endif
local_human *p0 = (local_human *)p[0];
if (p0->IsTimerOn()) // ignore mouse clicks if timer running
return;
modetype mode = p0->GetMode();
if (mode == SELECTING) { p0->PopCard(m_BgndBrush, point.x, point.y); return; } else if (mode != PLAYING) return;
p0->SetMode(WAITING); if (p0->PlayCard(point.x, point.y, handinfo, bCheating)) // valid card?
return;
// move wasn't legal, so back to PLAYING mode
p0->SetMode(PLAYING); }
/****************************************************************************
CMainWindow::OnNewGame
****************************************************************************/
void CMainWindow::OnNewGame() { passdir = LEFT; // each new game must start with LEFT
bAutostarted = FALSE; // means dealer has agreed to play at least
CMenu *pMenu = GetMenu(); pMenu->EnableMenuItem(IDM_NEWGAME, MF_GRAYED);
if (role == GAMEMEISTER) {
BOOL bNewPlayers = FALSE;
for (int i = 1; i < MAXPLAYER; i++) { if (!p[i]) { bNewPlayers = TRUE; p[i] = new computer(i); if (!p[i]) { bConstructed = FALSE; return; } } }
m_gamenumber = ::rand(); }
ResetHandInfo(-1);
::srand(m_gamenumber);
{ CScoreDlg score(this); score.ResetScore(); } // destruct score
TRACE1("\n\ngame number is %d\n\n", m_gamenumber); DUMP(); TRACE0("\n\n");
Shuffle(); }
/****************************************************************************
CMainWindow::OnOptions -- user requests options dialog from menu
****************************************************************************/
void CMainWindow::OnOptions() { COptionsDlg optionsdlg(this); optionsdlg.DoModal(); }
/****************************************************************************
CMainWindow::OnPaint
****************************************************************************/
void CMainWindow::OnPaint() { CPaintDC dc( this ); #ifdef USE_MIRRORING
SetLayout(dc.m_hDC, 0); SetLayout(dc.m_hAttribDC, 0); #endif
// players must be painted in order starting with playerled so that
// cards in centre overlap correctly
if (bConstructed) { int start = Id2Pos(handinfo.playerled % 4);
// check that someone has started
if (start >= 0) { for (int i = start; i < (MAXPLAYER+start); i++) { int pos = i % 4; if (p[pos]) { if (p[pos]->GetMode() == SCORING) { p[pos]->DisplayHeartsWon(dc); } else { p[pos]->Draw(dc, bCheating); p[pos]->MarkSelectedCards(dc); } } } } } }
/****************************************************************************
CMainWindow::OnPass
This function handles the local human pressing the button either to pass selected cards or to accept cards passed.
****************************************************************************/
void CMainWindow::OnPass() { if (p[0]->GetMode() == ACCEPTING) // OK (accepting passed cards)
{ m_Button.ShowWindow(SW_HIDE); m_Button.SetWindowText(TEXT("")); p[0]->SetMode(WAITING); // local human pushed the button
CRect rect; p[0]->GetCoverRect(rect);
for (SLOT s = 0; s < MAXSLOT; s++) p[0]->Select(s, FALSE);
InvalidateRect(&rect, TRUE); UpdateWindow();
FirstMove();
return; }
m_Button.EnableWindow(FALSE); p[0]->SetMode(DONE_SELECTING);
BOOL bReady = TRUE;
for (int i = 1; i < MAXPLAYER; i++) if (p[i]->GetMode() != DONE_SELECTING) bReady = FALSE;
if (!bReady) p[0]->UpdateStatus(IDS_PASSWAIT);
if (bReady) HandlePassing(); }
/****************************************************************************
CMainWindow::OnRef
After a human or a computer plays a card, they must PostMessage(WM_COMMAND, IDM_REF) which causes this routine (the referee) to be called.
Ref does the following: - updates handinfo data struct - calls HeartsPlaySound() if appropriate - determines if the hand is over or, if not, whose turn is next
****************************************************************************/
void CMainWindow::OnRef() { card *c = handinfo.cardplayed[handinfo.turn];
if (!handinfo.bHeartsBroken) { if (c->Suit() == HEARTS) { handinfo.bHeartsBroken = TRUE; HeartsPlaySound(SND_BREAK); } }
if (c->ID() == BLACKLADY) { handinfo.bQSPlayed = TRUE; HeartsPlaySound(SND_QUEEN); }
/* ------------------------------------------------
#if defined(_DEBUG)
TRACE("[%d] ", m_myid); TRACE("h.turn %d, ", handinfo.turn); TRACE("led %d, ", handinfo.playerled); for (int i = 0; i < 4; i++) { if (handinfo.cardplayed[i]) { CDNAME(handinfo.cardplayed[i]); } else { TRACE("-- "); } } TRACE("\n",); #endif
------------------------------------------------ */
int pos = Id2Pos(handinfo.turn); SLOT slot = p[pos]->GetSlot(handinfo.cardplayed[handinfo.turn]->ID());
#if defined(_DEBUG)
if (p[pos]->IsHuman()) ((human *)p[pos])->DebugMove(slot); #endif
p[pos]->GlideToCentre(slot, pos==0 ? TRUE : bCheating);
handinfo.turn++; handinfo.turn %= 4;
int newpos = Id2Pos(handinfo.turn);
if (handinfo.turn == handinfo.playerled) { EndHand(); } else { p[newpos]->SelectCardToPlay(handinfo, bCheating);
if (newpos != 0) ((local_human *)p[0])->WaitMessage(p[newpos]->GetName()); } }
/****************************************************************************
CMainWindow::OnScore -- user requests score dialog from menu
****************************************************************************/
void CMainWindow::OnScore() { CScoreDlg scoredlg(this); // this constructor does not add new info
scoredlg.DoModal(); }
/****************************************************************************
CMainWindow::DoSort
****************************************************************************/
void CMainWindow::DoSort() { for (int i = 0; i < (bCheating ? MAXPLAYER : 1); i++) { CRect rect; int id; // card in play for this player
if (handinfo.cardplayed[i] == NULL) id = EMPTY; else id = handinfo.cardplayed[i]->ID();
p[i]->Sort();
if (id != EMPTY) // if this player has a card in play, restore it
{ for (SLOT s = 0; s < MAXSLOT; s++) { if (p[i]->GetID(s) == id) { handinfo.cardplayed[i] = p[i]->Card(s); break; } } }
p[i]->GetCoverRect(rect); InvalidateRect(&rect, TRUE); } }
/****************************************************************************
CMainWindow::OnSound()
request sound on or off from menu.
****************************************************************************/
void CMainWindow::OnSound() { RegEntry Reg(szRegPath);
bSoundOn = !bSoundOn;
CMenu *pMenu = GetMenu(); pMenu->CheckMenuItem(IDM_SOUND, bSoundOn ? MF_CHECKED : MF_UNCHECKED);
if (bSoundOn) Reg.SetValue(regvalSound, 1); else Reg.DeleteValue(regvalSound); }
/****************************************************************************
CMainWindow::OnPrintClient()
Draw background into the specified HDC. This is used when drawing the "Pass" button in the Luna style.
****************************************************************************/
LRESULT CMainWindow::OnPrintClient(WPARAM wParam, LPARAM lParam) { CDC dc; CRect rect;
dc.Attach((HDC)wParam); GetClientRect(&rect); dc.FillRect(&rect, &m_BgndBrush); dc.Detach(); return 1; }
|