1112 lines
30 KiB
C
1112 lines
30 KiB
C
#include <windows.h>
|
|
#include <port1632.h>
|
|
#include "cards.h"
|
|
#include "golf.h"
|
|
#include "cdt.h"
|
|
#include "stdlib.h"
|
|
|
|
#define ININAME "entpack.ini"
|
|
|
|
typedef INT X;
|
|
typedef INT Y;
|
|
typedef INT DX;
|
|
typedef INT DY;
|
|
|
|
// ReCt structure
|
|
typedef struct _rc
|
|
{
|
|
X xLeft;
|
|
Y yTop;
|
|
X xRight;
|
|
Y yBot;
|
|
} RC;
|
|
|
|
#define abs(x) (((x) < 0) ? (-(x)) : (x))
|
|
|
|
#define IDCARDBACK 65
|
|
|
|
#define APPTITLE "Golf"
|
|
|
|
LRESULT APIENTRY WndProc (HWND, UINT, WPARAM, LPARAM) ;
|
|
VOID Deal(VOID);
|
|
VOID InitBoard(VOID);
|
|
VOID DrawLayout(HDC hDC);
|
|
VOID DrawPile(HDC hDC);
|
|
VOID UpdateDeck(HDC hDC);
|
|
BOOL UpdateLayout(HDC hDC, INT column, BOOL bValidate);
|
|
VOID UndoMove(HDC hDC);
|
|
VOID DoWinEffects(HDC hDC);
|
|
VOID UpdateScore(HDC hDC);
|
|
VOID APIENTRY Help(HWND hWnd, UINT wCommand, ULONG_PTR lParam);
|
|
INT_PTR APIENTRY BackDlgProc(HANDLE hdlg, UINT wm, WPARAM wParam, LPARAM lParam);
|
|
BOOL FDrawFocus(HDC hdc, RC *prc, BOOL fFocus);
|
|
VOID ChangeBack(WORD wNewDeckBack);
|
|
VOID DoBacks(VOID);
|
|
VOID MyDrawText(HDC hDC, LPSTR lpBuf, INT w, LPRECT lpRect, WORD wFlags);
|
|
INT Message(HWND hWnd, WORD wId, WORD wFlags);
|
|
BOOL fDialog(INT id,HWND hwnd,DLGPROC fpfn);
|
|
|
|
BOOL APIENTRY cdtDrawExt(HDC hdc, INT x, INT y, INT dx, INT dy, INT cd, INT mode, DWORD rgbBgnd);
|
|
BOOL APIENTRY cdtAnimate(HDC hdc, INT cd, INT x, INT y, INT ispr);
|
|
VOID DrawAnimate(INT cd, MPOINT *ppt, INT iani);
|
|
BOOL DeckAnimate(INT iqsec);
|
|
VOID APIENTRY TimerProc(HWND hwnd, UINT wm, UINT_PTR id, DWORD dwTime);
|
|
VOID SaveState(VOID);
|
|
VOID RestoreState(VOID);
|
|
LPSTR lstrtok(LPSTR lpStr, LPSTR lpDelim);
|
|
static BOOL IsInString(CHAR c, LPSTR s);
|
|
VOID DrawGameOver(HDC hDC);
|
|
INT_PTR APIENTRY RecordDlgProc(HANDLE hdlg, UINT wm, WPARAM wParam, LPARAM lParam);
|
|
LRESULT APIENTRY ReadOnlyProc(HWND hwnd, UINT wMessage, WPARAM wParam, LPARAM lParam);
|
|
VOID MarkControlReadOnly(HWND hwndCtrl, BOOL bReadOnly);
|
|
|
|
BOOL CheckGameOver(HDC hDC);
|
|
|
|
INT layout[52], pile[52], col[7];
|
|
INT deckStart, deckEnd, pilePos, nCards;
|
|
INT xClient, yClient, xCard, yCard;
|
|
INT xPileInc, yPileInc;
|
|
INT nCardsLeft[7], nWins, nGames;
|
|
|
|
DWORD dwBkgnd;
|
|
RECT deckRect, cntRect, pileRect, colRect[7];
|
|
WORD wErrorMessages;
|
|
WORD wDeckBack = IDCARDBACK;
|
|
FARPROC lpfnTimerProc;
|
|
|
|
typedef struct tagUndoRec {
|
|
enum { Layout, Deck } origin;
|
|
INT column;
|
|
} UndoRec;
|
|
UndoRec undo[52];
|
|
INT undoPos;
|
|
|
|
HWND hWnd;
|
|
HANDLE hMyInstance;
|
|
CHAR szAppName[80];
|
|
CHAR szOOM[256];
|
|
CHAR szGameOver[80], szGameOverS[80], szRecordTitle[80];
|
|
BOOL bGameInProgress = FALSE;
|
|
|
|
MMain(hInstance, hPrevInstance, lpszCmdLine, nCmdShow)
|
|
/* { */
|
|
MSG msg ;
|
|
WNDCLASS wndclass ;
|
|
HANDLE hAccel;
|
|
|
|
if (!LoadString(hInstance, IDSOOM, szOOM, 256) ||
|
|
!LoadString(hInstance, IDSAppName, szAppName, 80) ||
|
|
!LoadString(hInstance, IDSGameOver, szGameOver, 80) ||
|
|
!LoadString(hInstance, IDSGameOverS, szGameOverS, 80) ||
|
|
!LoadString(hInstance, IDSRecordTitle, szRecordTitle, 80)
|
|
)
|
|
return FALSE;
|
|
|
|
if (hPrevInstance) {
|
|
hWnd = FindWindow(szAppName, NULL);
|
|
if (hWnd)
|
|
{
|
|
hWnd = GetLastActivePopup(hWnd);
|
|
BringWindowToTop(hWnd);
|
|
if (IsIconic(hWnd))
|
|
ShowWindow(hWnd, SW_RESTORE);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
|
|
wndclass.lpfnWndProc = WndProc ;
|
|
wndclass.cbClsExtra = 0 ;
|
|
wndclass.cbWndExtra = 0 ;
|
|
wndclass.hInstance = hInstance ;
|
|
wndclass.hIcon = LoadIcon (hInstance, "Golf") ;
|
|
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
|
|
wndclass.hbrBackground = CreateSolidBrush(dwBkgnd = RGB(0,130,0));
|
|
wndclass.lpszMenuName = szAppName ;
|
|
wndclass.lpszClassName = szAppName ;
|
|
|
|
if (!RegisterClass (&wndclass))
|
|
return FALSE ;
|
|
|
|
hWnd = CreateWindow (szAppName, APPTITLE,
|
|
WS_OVERLAPPEDWINDOW | WS_MAXIMIZE,
|
|
CW_USEDEFAULT, CW_USEDEFAULT,
|
|
CW_USEDEFAULT, CW_USEDEFAULT,
|
|
NULL, NULL, hInstance, NULL) ;
|
|
|
|
if (!hWnd)
|
|
return FALSE;
|
|
|
|
hAccel = LoadAccelerators(hInstance, szAppName);
|
|
|
|
if (!hAccel)
|
|
return FALSE;
|
|
|
|
if(SetTimer(hWnd, 666, 250, TimerProc) == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
RestoreState();
|
|
|
|
ShowWindow (hWnd, SW_SHOWMAXIMIZED) ;
|
|
UpdateWindow (hWnd) ;
|
|
|
|
hMyInstance = hInstance;
|
|
while (GetMessage (&msg, NULL, 0, 0))
|
|
{
|
|
if (!TranslateAccelerator(hWnd, hAccel, &msg)) {
|
|
TranslateMessage (&msg) ;
|
|
DispatchMessage (&msg) ;
|
|
}
|
|
}
|
|
return (INT) msg.wParam ;
|
|
}
|
|
|
|
VOID APIENTRY Help(HWND hWnd, UINT wCommand, ULONG_PTR lParam)
|
|
{
|
|
CHAR szHelpPath[100], *pPath;
|
|
|
|
pPath = szHelpPath
|
|
+ GetModuleFileName(hMyInstance, szHelpPath, 99);
|
|
|
|
if (pPath != szHelpPath)
|
|
{
|
|
while (*pPath-- != '.')
|
|
;
|
|
++pPath;
|
|
*++pPath = 'H';
|
|
*++pPath = 'L';
|
|
*++pPath = 'P';
|
|
*++pPath = '\0';
|
|
|
|
WinHelp(hWnd, szHelpPath, wCommand, lParam);
|
|
}
|
|
}
|
|
|
|
VOID MyDrawText(HDC hDC, LPSTR lpBuf, INT w, LPRECT lpRect, WORD wFlags)
|
|
{
|
|
DWORD dwOldBk, dwOldTextColor;
|
|
HBRUSH hBrush, hOldBrush;
|
|
|
|
dwOldBk = SetBkColor(hDC, dwBkgnd);
|
|
dwOldTextColor = SetTextColor(hDC, RGB(255,255,255));
|
|
if (hBrush = CreateSolidBrush(dwBkgnd)) {
|
|
if (hOldBrush = SelectObject(hDC, hBrush)) {
|
|
PatBlt(hDC, lpRect->left, lpRect->top, lpRect->right - lpRect->left,
|
|
lpRect->bottom - lpRect->top, PATCOPY);
|
|
SelectObject(hDC, hOldBrush);
|
|
}
|
|
DeleteObject(hBrush);
|
|
}
|
|
DrawText(hDC, lpBuf, w, lpRect, wFlags);
|
|
SetBkColor(hDC, dwOldBk);
|
|
SetTextColor(hDC, dwOldTextColor);
|
|
}
|
|
|
|
VOID UpdateScore(HDC hDC)
|
|
{
|
|
CHAR buffer[5];
|
|
|
|
if (deckEnd - deckStart + 1) {
|
|
wsprintf(buffer, "%2d", deckEnd - deckStart + 1);
|
|
MyDrawText(hDC, buffer, -1, &cntRect, DT_RIGHT | DT_NOCLIP);
|
|
}
|
|
}
|
|
|
|
VOID DisplayWins(VOID)
|
|
{
|
|
fDialog(2, hWnd, RecordDlgProc);
|
|
}
|
|
|
|
|
|
LRESULT APIENTRY WndProc (
|
|
HWND hWnd,
|
|
UINT iMessage,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
HDC hDC ;
|
|
HMENU hMenu;
|
|
PAINTSTRUCT ps ;
|
|
SHORT i, j ;
|
|
MPOINT pt;
|
|
POINT mpt;
|
|
static BOOL fBoard;
|
|
FARPROC lpAbout;
|
|
HANDLE hLib;
|
|
|
|
switch (iMessage)
|
|
{
|
|
case WM_CREATE:
|
|
cdtInit(&xCard, &yCard);
|
|
Deal();
|
|
fBoard = FALSE;
|
|
break;
|
|
|
|
case WM_SIZE:
|
|
xClient = LOWORD(lParam);
|
|
yClient = HIWORD(lParam);
|
|
break;
|
|
|
|
case WM_PAINT:
|
|
hDC = BeginPaint (hWnd, &ps) ;
|
|
if (!fBoard)
|
|
{
|
|
InitBoard();
|
|
fBoard = TRUE;
|
|
}
|
|
DrawLayout(hDC);
|
|
DrawPile(hDC);
|
|
EndPaint(hWnd, &ps);
|
|
break;
|
|
|
|
case WM_LBUTTONDOWN:
|
|
pt.x = LOWORD(lParam);
|
|
pt.y = HIWORD(lParam);
|
|
|
|
MPOINT2POINT(pt, mpt);
|
|
if (PtInRect(&deckRect, mpt))
|
|
{
|
|
SendMessage(hWnd, WM_KEYDOWN, VK_SPACE, 0L);
|
|
break;
|
|
}
|
|
|
|
i = 0;
|
|
for (j = 0; j < 7; ++j)
|
|
if (PtInRect(colRect + j, mpt))
|
|
{
|
|
i = (SHORT) (j + 1);
|
|
break;
|
|
}
|
|
if (i)
|
|
SendMessage(hWnd, WM_KEYDOWN, '0' + i, 0L);
|
|
|
|
break;
|
|
|
|
case WM_INITMENU:
|
|
hMenu = GetMenu(hWnd);
|
|
EnableMenuItem(hMenu, IDM_OPTIONSUNDO, MF_BYCOMMAND |
|
|
undoPos ? MF_ENABLED : MF_GRAYED);
|
|
CheckMenuItem(hMenu, IDM_OPTIONSERROR,
|
|
wErrorMessages ? MF_CHECKED : MF_UNCHECKED);
|
|
break;
|
|
|
|
case WM_RBUTTONDOWN:
|
|
SendMessage(hWnd, WM_KEYDOWN, VK_SPACE, 0L);
|
|
break;
|
|
|
|
case WM_KEYDOWN:
|
|
hDC = GetDC(hWnd);
|
|
|
|
switch (wParam)
|
|
{
|
|
case VK_ESCAPE:
|
|
ShowWindow(hWnd, SW_MINIMIZE);
|
|
break;
|
|
|
|
|
|
case VK_SPACE:
|
|
UpdateDeck(hDC);
|
|
break;
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
UpdateLayout(hDC, (CHAR) wParam - '1', FALSE);
|
|
break;
|
|
}
|
|
|
|
ReleaseDC(hWnd, hDC);
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
switch(GET_WM_COMMAND_ID(wParam, lParam))
|
|
{
|
|
case IDM_NEWGAME:
|
|
Deal();
|
|
fBoard = FALSE;
|
|
InvalidateRect(hWnd, NULL, TRUE);
|
|
break;
|
|
|
|
case IDM_EXIT:
|
|
DestroyWindow(hWnd);
|
|
break;
|
|
|
|
case IDM_OPTIONSDECK:
|
|
DoBacks();
|
|
break;
|
|
|
|
case IDM_GAMERECORD:
|
|
DisplayWins();
|
|
break;
|
|
|
|
case IDM_ABOUT:
|
|
hLib = MLoadLibrary("shell32.dll");
|
|
if (hLib < (HANDLE)32)
|
|
break;
|
|
|
|
lpAbout = GetProcAddress(hLib, (LPSTR)"ShellAboutA");
|
|
|
|
if (lpAbout) {
|
|
(*lpAbout)(hWnd, szAppName, "by Ken Sykes", LoadIcon(hMyInstance, szAppName));
|
|
}
|
|
|
|
FreeLibrary(hLib);
|
|
break;
|
|
|
|
case MENU_INDEX:
|
|
Help(hWnd, HELP_INDEX, 0L);
|
|
break;
|
|
|
|
case MENU_HOWTOPLAY:
|
|
Help(hWnd, HELP_CONTEXT, 1L);
|
|
break;
|
|
|
|
case MENU_COMMANDS:
|
|
Help(hWnd, HELP_CONTEXT, 2L);
|
|
break;
|
|
|
|
case MENU_USINGHELP:
|
|
Help(hWnd, HELP_HELPONHELP, 0L);
|
|
break;
|
|
|
|
case IDM_OPTIONSERROR:
|
|
wErrorMessages = (WORD) ~wErrorMessages;
|
|
break;
|
|
|
|
case IDM_OPTIONSUNDO:
|
|
hDC = GetDC(hWnd);
|
|
UndoMove(hDC);
|
|
ReleaseDC(hWnd, hDC);
|
|
break;
|
|
|
|
}
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
KillTimer(hWnd, 666);
|
|
FreeProcInstance(lpfnTimerProc);
|
|
cdtTerm();
|
|
Help(hWnd, HELP_QUIT, 0L);
|
|
SaveState();
|
|
PostQuitMessage (0) ;
|
|
break ;
|
|
|
|
default:
|
|
return DefWindowProc (hWnd, iMessage, wParam, lParam) ;
|
|
}
|
|
return 0L ;
|
|
}
|
|
|
|
VOID Deal(VOID)
|
|
{
|
|
INT i, p1, p2, tmp;
|
|
|
|
/* stuff cards into layout */
|
|
for (i = 0; i < 52; ++i)
|
|
layout[i] = i;
|
|
|
|
/* shuffle them around */
|
|
srand(LOWORD(GetTickCount()));
|
|
for (i = 0; i < 500; ++i)
|
|
{
|
|
p1 = rand() % 52;
|
|
p2 = rand() % 52;
|
|
tmp = layout[p1];
|
|
layout[p1] = layout[p2];
|
|
layout[p2] = tmp;
|
|
}
|
|
|
|
/* initialize column pointers */
|
|
for (i = 0; i < 7; ++i)
|
|
col[i] = i * 5 + 4;
|
|
deckStart = 35;
|
|
deckEnd = 51;
|
|
pilePos = 0;
|
|
|
|
/* turn over the first card */
|
|
pile[pilePos] = layout[deckEnd--];
|
|
nCards = 35;
|
|
|
|
bGameInProgress = TRUE;
|
|
}
|
|
|
|
VOID InitBoard(VOID)
|
|
{
|
|
INT xPos, yPos;
|
|
INT xStep, yStep, col;
|
|
|
|
xPos = 30;
|
|
yPos = yClient - yCard - 30;
|
|
deckRect.left = xPos;
|
|
deckRect.right = xPos + xCard;
|
|
deckRect.top = yPos;
|
|
deckRect.bottom = yPos + yCard;
|
|
|
|
cntRect.left = xPos;
|
|
cntRect.right = cntRect.left + xCard;
|
|
cntRect.top = yPos + yCard + 5;
|
|
cntRect.bottom = cntRect.top + 20;
|
|
|
|
xPos += xCard + 20;
|
|
pileRect.left = xPos;
|
|
pileRect.right = xPos + xCard;
|
|
pileRect.top = yPos;
|
|
pileRect.bottom = yPos + yCard;
|
|
xPileInc = (xClient - 2 * xCard - 50) / 52;
|
|
yPileInc = 18;
|
|
|
|
xStep = xCard + 10;
|
|
yStep = yPileInc;
|
|
|
|
for (col = 0, xPos = 30; col < 7; ++col, xPos += xStep)
|
|
{
|
|
yPos = 10 + 5 * yStep;
|
|
colRect[col].left = xPos;
|
|
colRect[col].right = xPos + xCard;
|
|
colRect[col].top = yPos - yStep;
|
|
colRect[col].bottom = yPos - yStep + yCard;
|
|
}
|
|
|
|
undoPos = 0;
|
|
|
|
}
|
|
|
|
VOID DrawLayout(HDC hDC)
|
|
{
|
|
INT xStep, yStep, column, i, *card;
|
|
RECT rect;
|
|
|
|
xStep = xCard + 10;
|
|
yStep = yPileInc;
|
|
|
|
for (column = 0; column < 7; ++column)
|
|
{
|
|
/* if column is empty skip it */
|
|
if (col[column] < 5 * column)
|
|
continue;
|
|
|
|
/* start rectangle at top of column */
|
|
rect = colRect[column];
|
|
OffsetRect(&rect, 0, -yStep * (col[column] - 5 * column));
|
|
|
|
card = layout + 5 * column;
|
|
while (rect.top <= colRect[column].top)
|
|
{
|
|
cdtDraw(hDC, rect.left, rect.top, *card++, faceup, dwBkgnd);
|
|
OffsetRect(&rect, 0, yStep);
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID DrawPile(HDC hDC)
|
|
{
|
|
INT i, xPos;
|
|
|
|
if (bGameInProgress)
|
|
cdtDraw(hDC, deckRect.left, deckRect.top, wDeckBack, facedown, dwBkgnd);
|
|
else
|
|
DrawGameOver(hDC);
|
|
|
|
UpdateScore(hDC);
|
|
|
|
for (i = 0, xPos = 50 + xCard; i <= pilePos; ++i, xPos += xPileInc)
|
|
cdtDraw(hDC, xPos, pileRect.top, pile[i], faceup, dwBkgnd);
|
|
}
|
|
|
|
VOID UpdateDeck(HDC hDC)
|
|
{
|
|
/* if deck is empty return */
|
|
if (deckEnd < deckStart)
|
|
return;
|
|
|
|
/* move card to pile */
|
|
pile[++pilePos] = layout[deckEnd--];
|
|
|
|
/* fill in undo buffer */
|
|
undo[undoPos++].origin = Deck;
|
|
|
|
/* draw new card */
|
|
OffsetRect(&pileRect, xPileInc, 0);
|
|
cdtDraw(hDC, pileRect.left, pileRect.top, pile[pilePos], faceup, dwBkgnd);
|
|
|
|
/* update counter */
|
|
UpdateScore(hDC);
|
|
|
|
/* if we turned up last card remove facedown bitmap */
|
|
CheckGameOver(hDC);
|
|
}
|
|
|
|
BOOL CheckGameOver(HDC hDC)
|
|
{
|
|
RECT rect;
|
|
INT i;
|
|
|
|
if (deckEnd >= deckStart)
|
|
return FALSE;
|
|
|
|
SetBkColor(hDC, dwBkgnd);
|
|
cdtDraw(hDC, 30, yClient - yCard - 30, 0, remove, dwBkgnd);
|
|
rect = cntRect;
|
|
MyDrawText(hDC, "", 0, &rect, DT_LEFT | DT_NOCLIP);
|
|
|
|
for (i = 0; i < 7; ++i)
|
|
if (UpdateLayout(hDC, i, TRUE))
|
|
return FALSE;
|
|
|
|
bGameInProgress = FALSE;
|
|
|
|
DrawGameOver(hDC);
|
|
|
|
++nCardsLeft[(nCards - 1) / 5];
|
|
++nGames;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID DrawGameOver(HDC hDC)
|
|
{
|
|
CHAR buffer[30];
|
|
RECT rect;
|
|
|
|
wsprintf(buffer, (nCards == 1) ? szGameOverS : szGameOver, nCards);
|
|
rect = cntRect;
|
|
rect.left = 0;
|
|
rect.right = xClient;
|
|
MyDrawText(hDC, buffer, -1, &rect, DT_CENTER | DT_NOCLIP);
|
|
}
|
|
|
|
BOOL UpdateLayout(HDC hDC, INT column, BOOL bValidate)
|
|
{
|
|
INT colpos, dist;
|
|
|
|
/* if column is empty, ignore */
|
|
colpos = col[column];
|
|
if (colpos < 0 || (colpos / 5) != column)
|
|
return FALSE;
|
|
|
|
/* if card on top of pile is a king, don't move card to pile */
|
|
if (CardRank(pile[pilePos]) == king)
|
|
{
|
|
if (wErrorMessages && !bValidate)
|
|
Message(hWnd, IDSNoCardOnKing, MB_OK);
|
|
return FALSE;
|
|
}
|
|
|
|
/* if card is not adjacent, don't move it to pile */
|
|
dist = IndexValue(pile[pilePos], ACELOW)
|
|
- IndexValue(layout[colpos], ACELOW);
|
|
if (abs(dist) != 1)
|
|
{
|
|
if (wErrorMessages && !bValidate)
|
|
Message(hWnd, IDSNotAdjacent, MB_OK);
|
|
return FALSE;
|
|
}
|
|
|
|
if (bValidate)
|
|
return TRUE;
|
|
|
|
/* move card to pile */
|
|
pile[++pilePos] = layout[colpos--];
|
|
col[column] = colpos;
|
|
|
|
/* fill in undo buffer */
|
|
undo[undoPos].origin = Layout;
|
|
undo[undoPos++].column = column;
|
|
|
|
/* remove card from layout */
|
|
SetBkColor(hDC, dwBkgnd);
|
|
cdtDraw(hDC, colRect[column].left, colRect[column].top,
|
|
pile[pilePos], remove, dwBkgnd);
|
|
|
|
if (colpos >= 5 * column)
|
|
{
|
|
OffsetRect(colRect + column, 0, -yPileInc);
|
|
cdtDraw(hDC, colRect[column].left, colRect[column].top,
|
|
layout[colpos], faceup, dwBkgnd);
|
|
}
|
|
|
|
/* draw card on pile */
|
|
OffsetRect(&pileRect, xPileInc, 0);
|
|
cdtDraw(hDC, pileRect.left, pileRect.top, pile[pilePos], faceup, dwBkgnd);
|
|
|
|
/* decrement # of cards */
|
|
--nCards;
|
|
|
|
if (!nCards)
|
|
DoWinEffects(hDC);
|
|
|
|
/* if deck is empty display game over message */
|
|
CheckGameOver(hDC);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID UndoMove(HDC hDC)
|
|
{
|
|
CHAR buffer[10];
|
|
RECT *pRect, rect;
|
|
INT column;
|
|
HBRUSH hBrush;
|
|
|
|
--undoPos;
|
|
|
|
/* erase the top card on the pile */
|
|
SetBkColor(hDC, dwBkgnd);
|
|
cdtDraw(hDC, pileRect.left, pileRect.top, pile[pilePos], remove, dwBkgnd);
|
|
OffsetRect(&pileRect, -xPileInc, 0);
|
|
|
|
/* redraw the card that will now be at top of pile */
|
|
if (pilePos)
|
|
cdtDraw(hDC, pileRect.left, pileRect.top,
|
|
pile[pilePos-1], faceup, dwBkgnd);
|
|
|
|
if (!bGameInProgress)
|
|
{
|
|
rect = cntRect;
|
|
rect.left = rect.right + 1;
|
|
rect.right = xClient;
|
|
rect.bottom = yClient;
|
|
hBrush = CreateSolidBrush(dwBkgnd);
|
|
if (hBrush) {
|
|
FillRect(hDC, &rect, hBrush);
|
|
DeleteObject(hBrush);
|
|
}
|
|
|
|
if (nCards)
|
|
--nCardsLeft[(nCards - 1) / 5];
|
|
else
|
|
--nWins;
|
|
--nGames;
|
|
|
|
bGameInProgress = TRUE;
|
|
}
|
|
|
|
/* move the card back where it belongs */
|
|
if (undo[undoPos].origin == Deck)
|
|
{
|
|
if (deckEnd < deckStart)
|
|
cdtDraw(hDC, 30, yClient - yCard - 30, wDeckBack, facedown, dwBkgnd);
|
|
|
|
layout[++deckEnd] = pile[pilePos--];
|
|
UpdateScore(hDC);
|
|
}
|
|
else
|
|
{
|
|
column = undo[undoPos].column;
|
|
pRect = colRect + column;
|
|
if (col[column] >= 5 * column)
|
|
OffsetRect(pRect, 0, yPileInc);
|
|
cdtDraw(hDC, pRect->left, pRect->top, pile[pilePos], faceup, dwBkgnd);
|
|
layout[++col[undo[undoPos].column]] = pile[pilePos--];
|
|
|
|
++nCards;
|
|
}
|
|
}
|
|
|
|
VOID DoWinEffects(HDC hDC)
|
|
{
|
|
Message(hWnd, IDSWinner, MB_OK);
|
|
++nWins;
|
|
++nGames;
|
|
|
|
undoPos = 0;
|
|
}
|
|
|
|
VOID DoBacks()
|
|
{
|
|
|
|
DialogBox(hMyInstance, MAKEINTRESOURCE(1), hWnd, BackDlgProc);
|
|
|
|
}
|
|
|
|
INT_PTR APIENTRY BackDlgProc(HANDLE hdlg, UINT wm, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
static INT modeNew;
|
|
INT iback;
|
|
MEASUREITEMSTRUCT FAR *lpmi;
|
|
DRAWITEMSTRUCT FAR *lpdi;
|
|
HBRUSH hbr;
|
|
RC rc, rcCrd;
|
|
HDC hdc;
|
|
INT i;
|
|
|
|
switch(wm)
|
|
{
|
|
case WM_INITDIALOG:
|
|
modeNew = wDeckBack;
|
|
SetFocus(GetDlgItem(hdlg, modeNew));
|
|
return FALSE;
|
|
|
|
case WM_COMMAND:
|
|
|
|
if(GET_WM_COMMAND_ID(wParam, lParam) >= IDFACEDOWNFIRST &&
|
|
GET_WM_COMMAND_ID(wParam, lParam) <= IDFACEDOWN12) {
|
|
modeNew = GET_WM_COMMAND_ID(wParam, lParam) ;
|
|
if (GET_WM_COMMAND_CMD(wParam, lParam) == BN_DOUBLECLICKED) {
|
|
ChangeBack((WORD)modeNew);
|
|
EndDialog(hdlg, 0);
|
|
}
|
|
} else
|
|
switch(wParam)
|
|
{
|
|
case IDOK:
|
|
ChangeBack((WORD)modeNew);
|
|
/* fall thru */
|
|
|
|
case IDCANCEL:
|
|
EndDialog(hdlg, 0);
|
|
break;
|
|
|
|
}
|
|
break;
|
|
|
|
case WM_MEASUREITEM:
|
|
lpmi = (MEASUREITEMSTRUCT FAR *)lParam;
|
|
lpmi->CtlType = ODT_BUTTON;
|
|
lpmi->itemWidth = xCard /* 32 */;
|
|
lpmi->itemHeight = yCard /* 54 */;
|
|
break;
|
|
case WM_DRAWITEM:
|
|
lpdi = (DRAWITEMSTRUCT FAR *)lParam;
|
|
|
|
CopyRect((LPRECT) &rc, &lpdi->rcItem);
|
|
rcCrd = rc;
|
|
InflateRect((LPRECT) &rcCrd, -3, -3);
|
|
hdc = lpdi->hDC;
|
|
|
|
if (lpdi->itemAction == ODA_DRAWENTIRE)
|
|
{
|
|
cdtDrawExt(hdc, rcCrd.xLeft, rcCrd.yTop,
|
|
rcCrd.xRight-rcCrd.xLeft, rcCrd.yBot-rcCrd.yTop,
|
|
lpdi->CtlID, FACEDOWN, 0L);
|
|
FDrawFocus(hdc, &rc, lpdi->itemState & ODS_FOCUS);
|
|
break;
|
|
}
|
|
if (lpdi->itemAction == ODA_SELECT)
|
|
InvertRect(hdc, (LPRECT)&rcCrd);
|
|
|
|
if (lpdi->itemAction == ODA_FOCUS)
|
|
FDrawFocus(hdc, &rc, lpdi->itemState & ODS_FOCUS);
|
|
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL FDrawFocus(HDC hdc, RC *prc, BOOL fFocus)
|
|
{
|
|
HBRUSH hbr;
|
|
RC rc;
|
|
hbr = CreateSolidBrush(GetSysColor(fFocus ? COLOR_HIGHLIGHT : COLOR_WINDOW));
|
|
if(hbr == NULL)
|
|
return FALSE;
|
|
rc = *prc;
|
|
FrameRect(hdc, (LPRECT) &rc, hbr);
|
|
InflateRect((LPRECT) &rc, -1, -1);
|
|
FrameRect(hdc, (LPRECT) &rc, hbr);
|
|
DeleteObject(hbr);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID ChangeBack(WORD wNewDeckBack)
|
|
{
|
|
HDC hDC;
|
|
|
|
wDeckBack = wNewDeckBack;
|
|
|
|
if (deckEnd < deckStart)
|
|
return;
|
|
|
|
hDC = GetDC(hWnd);
|
|
if (hDC) {
|
|
cdtDraw(hDC, deckRect.left, deckRect.top, wDeckBack, facedown, dwBkgnd);
|
|
ReleaseDC(hWnd, hDC);
|
|
}
|
|
}
|
|
|
|
INT Message(HWND hWnd, WORD wId, WORD wFlags)
|
|
{
|
|
static CHAR szBuf[256];
|
|
|
|
if (!LoadString(hMyInstance, wId, szBuf, 256) ||
|
|
wId == IDSOOM) {
|
|
lstrcpy(szBuf, szOOM);
|
|
wFlags = MB_ICONHAND | MB_SYSTEMMODAL;
|
|
}
|
|
|
|
if (!(wFlags & MB_SYSTEMMODAL))
|
|
wFlags |= MB_TASKMODAL;
|
|
|
|
if (!(wFlags & (MB_ICONHAND | MB_ICONEXCLAMATION | MB_ICONINFORMATION)))
|
|
wFlags |= MB_ICONEXCLAMATION;
|
|
|
|
return MessageBox(hWnd, szBuf, szAppName, wFlags);
|
|
}
|
|
|
|
|
|
VOID DrawAnimate(INT cd, MPOINT *ppt, INT iani)
|
|
{
|
|
HDC hDC;
|
|
|
|
if(!(hDC = GetDC(hWnd)))
|
|
return;
|
|
cdtAnimate(hDC, cd, ppt->x, ppt->y, iani);
|
|
ReleaseDC(hWnd, hDC);
|
|
}
|
|
|
|
BOOL DeckAnimate(INT iqsec)
|
|
{
|
|
INT iani;
|
|
MPOINT pt;
|
|
|
|
|
|
pt.x = (SHORT) deckRect.left;
|
|
pt.y = (SHORT) deckRect.top;
|
|
|
|
switch(wDeckBack) {
|
|
case IDFACEDOWN3:
|
|
DrawAnimate(IDFACEDOWN3, &pt, iqsec % 4);
|
|
break;
|
|
case IDFACEDOWN10: // krazy kastle
|
|
DrawAnimate(IDFACEDOWN10, &pt, iqsec % 2);
|
|
break;
|
|
|
|
case IDFACEDOWN11: // sanflipe
|
|
if((iani = (iqsec+4) % (50*4)) < 4)
|
|
DrawAnimate(IDFACEDOWN11, &pt, iani);
|
|
else
|
|
// if a menu overlapps an ani while it is ani'ing, leaves deck
|
|
// bitmap in inconsistent state...
|
|
if(iani % 6 == 0)
|
|
DrawAnimate(IDFACEDOWN11, &pt, 3);
|
|
break;
|
|
case IDFACEDOWN12: // SLIME
|
|
if((iani = (iqsec+4) % (15*4)) < 4)
|
|
DrawAnimate(IDFACEDOWN12, &pt, iani);
|
|
else
|
|
// if a menu overlapps an ani while it is ani'ing, leaves deck
|
|
// bitmap in inconsistent state...
|
|
if(iani % 6 == 0)
|
|
DrawAnimate(IDFACEDOWN12, &pt, 3);
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID APIENTRY TimerProc(HWND hwnd, UINT wm, UINT_PTR id, DWORD dwTime)
|
|
{
|
|
static INT x = 0;
|
|
|
|
if (deckEnd >= deckStart)
|
|
DeckAnimate(x++);
|
|
return;
|
|
}
|
|
|
|
VOID SaveState(VOID)
|
|
{
|
|
CHAR sz[80];
|
|
|
|
wsprintf(sz, "%d %d %d %d %d %d %d %d %d", nGames, nWins, nCardsLeft[0],
|
|
nCardsLeft[1], nCardsLeft[2], nCardsLeft[3],
|
|
nCardsLeft[4], nCardsLeft[5], nCardsLeft[6]);
|
|
WritePrivateProfileString(szAppName, "Stats", sz, ININAME);
|
|
|
|
wsprintf(sz, "%d %d", wErrorMessages, wDeckBack);
|
|
WritePrivateProfileString(szAppName, "MenuState", sz, ININAME);
|
|
}
|
|
|
|
VOID RestoreState(VOID)
|
|
{
|
|
CHAR sz[80], *psz;
|
|
INT col;
|
|
DWORD cchRead;
|
|
|
|
cchRead = GetPrivateProfileString(szAppName, "Stats", "0 0 0 0 0 0 0 0 0", sz,
|
|
sizeof(sz), ININAME);
|
|
|
|
psz = (cchRead > 0) ? lstrtok(sz, " ") : NULL;
|
|
if (psz) {
|
|
nGames = atoi(psz);
|
|
psz = lstrtok(NULL, " ");
|
|
nWins = psz ? atoi(psz) : 0;
|
|
} else
|
|
nGames = nWins = 0;
|
|
|
|
for (col = 0; col < 7 && psz; ++col)
|
|
nCardsLeft[col] = atoi(psz = lstrtok(NULL, " "));
|
|
|
|
for (; col < 7; ++col)
|
|
nCardsLeft[col] = 0;
|
|
|
|
cchRead = GetPrivateProfileString(szAppName, "MenuState", "0 65", sz,
|
|
sizeof(sz), ININAME);
|
|
|
|
psz = (cchRead > 0) ? lstrtok(sz, " ") : NULL;
|
|
if (psz) {
|
|
wErrorMessages = (WORD) atoi(psz);
|
|
psz = lstrtok(NULL, " ");
|
|
|
|
wDeckBack = (WORD) IDCARDBACK;
|
|
|
|
if (psz)
|
|
wDeckBack = (WORD) atoi(psz);
|
|
|
|
} else {
|
|
wErrorMessages = 0;
|
|
wDeckBack = IDCARDBACK;
|
|
}
|
|
}
|
|
|
|
static BOOL IsInString(CHAR c, LPSTR s)
|
|
{
|
|
while (*s && *s != c)
|
|
s = AnsiNext(s);
|
|
|
|
return *s;
|
|
}
|
|
|
|
/* write our own strtok to avoid pulling in entire string library ... */
|
|
LPSTR lstrtok(LPSTR lpStr, LPSTR lpDelim)
|
|
{
|
|
static LPSTR lpString;
|
|
LPSTR lpRetVal, lpTemp;
|
|
|
|
/* if we are passed new string skip leading delimiters */
|
|
if(lpStr) {
|
|
lpString = lpStr;
|
|
|
|
while (*lpString && IsInString(*lpString, lpDelim))
|
|
lpString = AnsiNext(lpString);
|
|
}
|
|
|
|
/* if there are no more tokens return NULL */
|
|
if(!*lpString)
|
|
return NULL;
|
|
|
|
/* save head of token */
|
|
lpRetVal = lpString;
|
|
|
|
/* find delimiter or end of string */
|
|
while(*lpString && !IsInString(*lpString, lpDelim))
|
|
lpString = AnsiNext(lpString);
|
|
|
|
/* if we found a delimiter insert string terminator and skip */
|
|
if(*lpString) {
|
|
lpTemp = AnsiNext(lpString);
|
|
*lpString = '\0';
|
|
lpString = lpTemp;
|
|
}
|
|
|
|
/* return token */
|
|
return(lpRetVal);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*\
|
|
| fDialog(id,hwnd,fpfn) |
|
|
| |
|
|
| Description: |
|
|
| This function displays a dialog box and returns the exit code. |
|
|
| the function passed will have a proc instance made for it. |
|
|
| |
|
|
| Arguments: |
|
|
| id resource id of dialog to display |
|
|
| hwnd parent window of dialog |
|
|
| fpfn dialog message function |
|
|
| |
|
|
| Returns: |
|
|
| exit code of dialog (what was passed to EndDialog) |
|
|
| |
|
|
\*----------------------------------------------------------------------------*/
|
|
BOOL fDialog(INT id,HWND hwnd,DLGPROC fpfn)
|
|
{
|
|
BOOL f;
|
|
HANDLE hInst;
|
|
|
|
hInst = (HANDLE) GetWindowLongPtr(hwnd,GWLP_HINSTANCE);
|
|
fpfn = MakeProcInstance(fpfn,hInst);
|
|
f = (BOOL) DialogBox(hInst,MAKEINTRESOURCE(id),hwnd, fpfn);
|
|
FreeProcInstance (fpfn);
|
|
return f;
|
|
}
|
|
|
|
INT_PTR APIENTRY RecordDlgProc(HANDLE hdlg, UINT wm, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
CHAR sz[80];
|
|
HWND hwndEdit;
|
|
INT i;
|
|
|
|
switch(wm) {
|
|
|
|
case WM_INITDIALOG:
|
|
hwndEdit = GetDlgItem(hdlg, IDD_RECORD);
|
|
SendMessage(hwndEdit, LB_ADDSTRING, 0, (LPARAM) (szRecordTitle));
|
|
|
|
wsprintf(sz, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d", nGames, nWins, nCardsLeft[0],
|
|
nCardsLeft[1], nCardsLeft[2], nCardsLeft[3],
|
|
nCardsLeft[4], nCardsLeft[5], nCardsLeft[6]);
|
|
SendMessage(hwndEdit, LB_ADDSTRING, 0, (LPARAM) (sz));
|
|
MarkControlReadOnly(hwndEdit, TRUE);
|
|
return TRUE;
|
|
|
|
case WM_COMMAND:
|
|
switch(GET_WM_COMMAND_ID(wParam, lParam)) {
|
|
case IDOK:
|
|
/* fall thru */
|
|
|
|
case IDCANCEL:
|
|
hwndEdit = GetDlgItem(hdlg, IDD_RECORD);
|
|
MarkControlReadOnly(hwndEdit, FALSE);
|
|
EndDialog(hdlg, GET_WM_COMMAND_ID(wParam, lParam) == IDOK);
|
|
|
|
break;
|
|
|
|
case IDD_CLEARSCORES:
|
|
nGames = nWins = 0;
|
|
for (i = 0; i < 7; ++i)
|
|
nCardsLeft[i] = 0;
|
|
lstrcpy(sz, "0\t0\t0\t0\t0\t0\t0\t0\t0");
|
|
hwndEdit = GetDlgItem(hdlg, IDD_RECORD);
|
|
SendMessage(hwndEdit, LB_DELETESTRING, 1, 0L);
|
|
SendMessage(hwndEdit, LB_ADDSTRING, 0, (LPARAM) (sz));
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static WNDPROC lpOldWP;
|
|
|
|
VOID MarkControlReadOnly(HWND hwndCtrl, BOOL bReadOnly)
|
|
{
|
|
if (bReadOnly)
|
|
lpOldWP = (WNDPROC) SetWindowLongPtr(hwndCtrl, GWLP_WNDPROC,
|
|
(LONG_PTR) ReadOnlyProc);
|
|
else
|
|
SetWindowLongPtr(hwndCtrl, GWLP_WNDPROC, (LONG_PTR)lpOldWP);
|
|
}
|
|
|
|
LRESULT APIENTRY ReadOnlyProc(HWND hwnd, UINT wMessage, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (wMessage) {
|
|
case WM_LBUTTONDOWN:
|
|
case WM_RBUTTONDOWN:
|
|
case WM_LBUTTONDBLCLK:
|
|
case WM_RBUTTONDBLCLK:
|
|
return 0L;
|
|
}
|
|
|
|
return CallWindowProc(lpOldWP, hwnd, wMessage, wParam, lParam);
|
|
}
|