NT4/private/windows/ep/cruel/cruel.c
2020-09-30 17:12:29 +02:00

1431 lines
40 KiB
C

#include <windows.h>
#include <port1632.h>
#include "cards.h"
#include "cruel.h"
#include "cdt.h"
#include "stdlib.h"
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 IDCARDBACK 65
#define ININAME "entpack.ini"
#define APPTITLE "Cruel"
#define HI 0
#define LOW 1
#define SUM 2
#define TOTAL 3
#define WINS 4
#define abs(x) (((x) < 0) ? (-(x)) : (x))
#define NEXT_ROUND 4000
#define INVALID_POS 255
#define MAXUNDOSIZE 100
extern VOID APIENTRY AboutWEP(HWND hwnd, HICON hicon, LPSTR lpTitle, LPSTR lpCredit);
LONG APIENTRY WndProc (HWND, UINT, WPARAM, LONG) ;
VOID Deal(VOID);
VOID InitBoard(VOID);
VOID DrawLayout(HDC hDC);
VOID DrawFoundation(HDC hDC);
VOID UpdateDeck(HDC hDC);
VOID UpdateLayout(HDC hDC, INT column);
VOID DoWinEffects(HDC hDC);
BOOL MoveCard(HDC hDC, INT startRow, INT startColumn,
INT endRow, INT endColumn,
BOOL bValidate);
VOID RestoreLayout(HDC hDC);
VOID APIENTRY Help(HWND hWnd, WORD wCommand, LONG lParam);
VOID UndoMove(HDC hDC);
INT Message(HWND hWnd, WORD wId, WORD wFlags);
VOID MyDrawText(HDC hDC, LPSTR lpBuf, INT w, LPRECT lpRect, WORD wFlags);
BOOL CheckGameOver(VOID);
BOOL APIENTRY BackDlgProc(HANDLE hdlg, WORD wm, WPARAM wParam, LONG lParam);
BOOL FDrawFocus(HDC hdc, RC *prc, BOOL fFocus);
VOID ChangeBack(WORD wNewDeckBack);
VOID DoBacks(VOID);
VOID DrawGameOver(VOID);
BOOL fDialog(INT id,HWND hwnd,FARPROC 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);
WORD APIENTRY TimerProc(HWND hwnd, INT wm, INT id, DWORD dwTime);
BOOL APIENTRY RecordDlgProc(HANDLE hdlg, WORD wm, WPARAM wParam, LONG lParam);
INT APIENTRY ReadOnlyProc(HWND hwnd, WORD wMessage, WPARAM wParam, LONG lParam);
VOID MarkControlReadOnly(HWND hwndCtrl, BOOL bReadOnly);
VOID ShowStacks(VOID);
VOID SaveState(VOID);
VOID RestoreState(VOID);
LPSTR lstrtok(LPSTR lpStr, LPSTR lpDelim);
static BOOL IsInString(CHAR c, LPSTR s);
VOID DisplayStats(VOID);
INT sprintf();
#if 0
INT init[2][6] = { { 49, 33, 13, 46, 10, 47 },
{ 39, 31, 19, 44, 28, 8 }};
#endif
typedef struct tagCardRec {
INT card;
struct tagCardRec *next;
} CardRec;
CardRec *FreeList[52];
INT freePos;
CardRec deck[52];
CardRec *layout[2][6];
RECT layoutRect[2][6];
INT foundation[4];
RECT foundationRect[4];
INT nCards;
LONG nStats[5];
CHAR szAppName[80], szGameOver[80], szGameOverS[80];
CHAR szOOM[256], szRecordTitle[80];
WORD wDeckBack;
FARPROC lpfnTimerProc;
BOOL bGameInProgress = FALSE;
typedef struct tagUndoRec {
INT startRow, startColumn, endRow, endColumn;
} UndoRec;
UndoRec undo[MAXUNDOSIZE];
INT undoPos;
INT xClient, yClient, xCard, yCard;
INT xInc, yInc;
DWORD dwBkgnd;
WORD wErrorMessages;
HWND hWnd;
HANDLE hMyInstance;
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);
hWnd = GetLastActivePopup(hWnd);
BringWindowToTop(hWnd);
if (IsIconic(hWnd))
ShowWindow(hWnd, SW_RESTORE);
return FALSE;
}
wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (hInstance, szAppName) ;
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 ;
RestoreState();
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((lpfnTimerProc = MakeProcInstance((FARPROC) TimerProc, hInstance)) == NULL)
return FALSE;
if(SetTimer(hWnd, 666, 250, (TIMERPROC)lpfnTimerProc) == 0) {
FreeProcInstance(lpfnTimerProc);
return FALSE;
}
ShowWindow (hWnd, SW_SHOWMAXIMIZED) ;
UpdateWindow (hWnd) ;
hMyInstance = hInstance;
while (GetMessage (&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(hWnd, hAccel, &msg)) {
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
}
return msg.wParam ;
}
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 APIENTRY Help(HWND hWnd, WORD wCommand, LONG lParam)
{
CHAR szHelpPath[100], *pPath;
pPath = szHelpPath
+ GetModuleFileName(hMyInstance, szHelpPath, 99);
while (*pPath-- != '.')
;
++pPath;
*++pPath = 'H';
*++pPath = 'L';
*++pPath = 'P';
*++pPath = '\0';
WinHelp(hWnd, szHelpPath, wCommand, lParam);
}
BOOL CheckGameOver(VOID)
{
CardRec *pCard;
INT colStart, colEnd, rowStart, rowEnd;
BOOL bMoveFound;
HDC hDC;
RECT rect;
/* check to see if there is a card to play */
bMoveFound = FALSE;
for (rowStart = 0; rowStart < 2; ++rowStart)
for (colStart = 0; colStart < 6; ++colStart) {
if (!layout[rowStart][colStart])
continue;
for (rowEnd = -1; rowEnd < 2; ++rowEnd)
for (colEnd = 0; colEnd < ((rowEnd < 0) ? 4 : 6); ++colEnd) {
if (rowEnd == -1 && foundation[colEnd] == wDeckBack)
continue;
if (MoveCard(NULL, rowStart, colStart, rowEnd, colEnd, TRUE)) {
bMoveFound = TRUE;
goto endMoveSearch;
}
}
}
endMoveSearch:
/* if a move was found game ain't over! */
if (bMoveFound)
return FALSE;
/* count # of cards left */
nCards = 0;
for (rowStart = 0; rowStart < 2; ++rowStart)
for (colStart = 0; colStart < 6; ++colStart)
for (pCard = layout[rowStart][colStart]; pCard; pCard = pCard->next)
++nCards;
/* if no cards then we have a winner! */
if (!nCards) {
Message(hWnd, IDSWinner, MB_OK | MB_ICONEXCLAMATION);
undoPos = 0;
}
else
DrawGameOver();
if (!nCards)
++nStats[WINS];
if (nCards < nStats[LOW])
nStats[LOW] = nCards;
if (nCards > nStats[HI])
nStats[HI] = nCards;
++nStats[TOTAL];
nStats[SUM] += nCards;
bGameInProgress = FALSE;
return TRUE;
}
VOID DrawGameOver(VOID)
{
HDC hDC;
CHAR buffer[80];
RECT rect;
wsprintf(buffer, (nCards == 1) ? szGameOverS : szGameOver, nCards);
rect.bottom = yClient - 15;
rect.top = rect.bottom - 10;
rect.left = 0;
rect.right = xClient;
if (hDC = GetDC(hWnd)) {
MyDrawText(hDC, buffer, -1, &rect, DT_CENTER | DT_NOCLIP);
ReleaseDC(hWnd, hDC);
}
}
LONG APIENTRY WndProc (
HWND hWnd,
UINT iMessage,
WPARAM wParam,
LONG lParam)
{
HDC hDC ;
HMENU hMenu;
PAINTSTRUCT ps ;
SHORT i, j ;
POINT mpt;
MPOINT pt;
RECT tmpRect;
LONG Area, maxArea;
static BOOL fBoard = FALSE;
static HWND hWndButton = NULL;
INT row, column;
static BOOL fTracking = FALSE;
static INT startRow, startColumn, endRow, endColumn;
static RECT trackRect;
static HDC hDCTrack;
static HBRUSH hBrush, hOldBrush;
static HPEN hOldPen;
static MPOINT ptOldPos;
VOID ( APIENTRY *lpAbout)(HWND, LPSTR, LPSTR, HICON);
HANDLE hLib;
switch (iMessage)
{
case WM_CREATE:
cdtInit(&xCard, &yCard);
Deal();
startRow = startColumn = endRow = endColumn = INVALID_POS;
hBrush = CreateSolidBrush(dwBkgnd);
if (!hBrush)
return -1L;
CheckGameOver();
break;
case WM_SIZE:
xClient = LOWORD(lParam);
yClient = HIWORD(lParam);
break;
case WM_PAINT:
hDC = BeginPaint (hWnd, &ps) ;
SetBkColor(hDC, dwBkgnd);
if (!fBoard)
{
InitBoard();
if (!hWndButton)
hWndButton = CreateWindow("button", "Deal",
WS_CHILD |
WS_VISIBLE |
BS_DEFPUSHBUTTON,
20 + 5 * xInc,
10 + yCard / 4,
xCard,
yCard / 2,
hWnd, (HMENU)NEXT_ROUND,
hMyInstance, NULL);
fBoard = TRUE;
}
DrawLayout(hDC);
DrawFoundation(hDC);
if (!bGameInProgress)
DrawGameOver();
EndPaint(hWnd, &ps);
break;
case WM_LBUTTONDBLCLK:
pt = MAKEMPOINT(lParam);
for (row = 0; row < 2; ++row)
for (column = 0; column < 6; ++column)
{
MPOINT2POINT(pt, mpt);
if (!layout[row][column] ||
!PtInRect(&layoutRect[row][column], mpt))
continue;
i = CardSuit(layout[row][column]->card);
j = CardRank(layout[row][column]->card);
if (CardRank(foundation[i]) == j - 1) {
hDC = GetDC(hWnd);
if (hDC) {
MoveCard(hDC, row, column, -1, i, FALSE);
ReleaseDC(hWnd, hDC);
}
} else {
if (wErrorMessages)
Message(hWnd, IDSNotNextCard, MB_OK);
}
return 0L;
}
break;
case WM_LBUTTONDOWN:
pt = MAKEMPOINT(lParam);
for (row = 0; row < 2; ++row)
for (column = 0; column < 6; ++column)
{
MPOINT2POINT(pt, mpt);
if (!layout[row][column] ||
!PtInRect(&layoutRect[row][column], mpt))
continue;
fTracking = TRUE;
startRow = row;
startColumn = column;
trackRect = layoutRect[row][column];
hDCTrack = GetDC(hWnd);
hOldBrush = SelectObject(hDCTrack,
GetStockObject(NULL_BRUSH));
hOldPen = SelectObject(hDCTrack,
GetStockObject(WHITE_PEN));
SetROP2(hDCTrack, R2_XORPEN);
Rectangle(hDCTrack, trackRect.left, trackRect.top,
trackRect.right, trackRect.bottom);
ptOldPos = pt;
SetCapture(hWnd);
goto foundSource;
}
foundSource:
break;
case WM_MOUSEMOVE:
pt = MAKEMPOINT(lParam);
if (fTracking && !(wParam & MK_LBUTTON))
PostMessage(hWnd, WM_LBUTTONUP, wParam, lParam);
else if (!fTracking ||
((pt.x == ptOldPos.x) && (pt.y == ptOldPos.y)))
break;
Rectangle(hDCTrack, trackRect.left, trackRect.top,
trackRect.right, trackRect.bottom);
OffsetRect(&trackRect, pt.x - ptOldPos.x,
pt.y - ptOldPos.y);
ptOldPos = pt;
Rectangle(hDCTrack, trackRect.left, trackRect.top,
trackRect.right, trackRect.bottom);
break;
case WM_LBUTTONUP:
if (!fTracking)
break;
ReleaseCapture();
endRow = endColumn = INVALID_POS;
maxArea = 0;
for (row = 0; row < 2; ++row)
for (column = 0; column < 6; ++column)
{
if (!layout[row][column] ||
!IntersectRect(&tmpRect, &trackRect,
&layoutRect[row][column]))
continue;
Area = abs((tmpRect.right - tmpRect.left)
* (tmpRect.bottom - tmpRect.top));
if (Area > maxArea) {
endRow = row;
endColumn = column;
maxArea = Area;
}
}
if (maxArea)
goto foundTarget;
endRow = -1;
for (column = 0; column < 4; ++column)
if (IntersectRect(&tmpRect, &trackRect, &foundationRect[column]))
{
Area = abs((tmpRect.right - tmpRect.left)
* (tmpRect.bottom - tmpRect.top));
if (Area > maxArea) {
endColumn = column;
maxArea = Area;
}
}
foundTarget:
fTracking = FALSE;
Rectangle(hDCTrack, trackRect.left, trackRect.top,
trackRect.right, trackRect.bottom);
if (startRow != endRow || startColumn != endColumn) {
if ((endRow != INVALID_POS) && (endColumn != INVALID_POS))
MoveCard(hDCTrack, startRow, startColumn, endRow, endColumn, FALSE);
startRow = startColumn = endRow = endColumn = INVALID_POS;
}
SelectObject(hDCTrack, hOldBrush);
SelectObject(hDCTrack, hOldPen);
ReleaseDC(hWnd, hDCTrack);
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_COMMAND:
switch(GET_WM_COMMAND_ID(wParam, lParam))
{
case IDM_NEWGAME:
Deal();
fBoard = FALSE;
InvalidateRect(hWnd, NULL, TRUE);
CheckGameOver();
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
case IDM_OPTIONSDECK:
DoBacks();
break;
case IDM_ABOUT:
hLib = MLoadLibrary("shell32.dll");
if (hLib < (HANDLE)32)
break;
lpAbout = GetProcAddress(hLib, (LPSTR)"ShellAboutA");
if (lpAbout) {
(*lpAbout)(hWnd,
(LPSTR) szAppName, (LPSTR)"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 = ~wErrorMessages;
break;
case IDM_OPTIONSUNDO:
hDC = GetDC(hWnd);
UndoMove(hDC);
ReleaseDC(hWnd, hDC);
break;
case IDM_GAMERECORD:
DisplayStats();
break;
case IDM_DOMINIMIZE:
ShowWindow(hWnd, SW_MINIMIZE);
break;
case NEXT_ROUND:
if (!bGameInProgress)
break;
if (!undoPos) {
if (wErrorMessages)
Message(hWnd, IDSNoCardsMoved, MB_OK);
break;
}
UnionRect(&tmpRect, &layoutRect[0][0],
&layoutRect[1][5]);
hDC = GetDC(hWnd);
FillRect(hDC, &tmpRect, hBrush);
RestoreLayout(hDC);
DrawLayout(hDC);
ReleaseDC(hWnd, hDC);
undoPos = 0;
CheckGameOver();
break;
}
break;
case WM_DESTROY:
KillTimer(hWnd, 666);
FreeProcInstance(lpfnTimerProc);
cdtTerm();
Help(hWnd, HELP_QUIT, 0L);
DeleteObject(hBrush);
SaveState();
PostQuitMessage (0) ;
break ;
default:
return DefWindowProc (hWnd, iMessage, wParam, lParam) ;
}
return 0L ;
}
VOID Deal(VOID)
{
INT i, p1, p2;
INT row, column;
CardRec tmp, *pDeck;
cardSuit suit;
cardRank rank;
/* stuff cards into deck */
pDeck = deck;
for (i = 4; i < 52; ++i)
(pDeck++)->card = i;
/* shuffle them around */
srand(LOWORD(GetTickCount()));
for (p2 = 0; p2 < 7; ++p2) {
for (i = 47; i > 0; --i) {
p1 = rand() % i;
tmp = deck[p1];
deck[p1] = deck[i];
deck[i] = tmp;
}
}
/* establish layout links */
pDeck = deck;
for (row = 0; row < 2; ++row)
for (column = 0; column < 6; ++column)
{
layout[row][column] = pDeck;
for (i = 0; i < 3; ++i, ++pDeck)
pDeck->next = pDeck + 1;
(pDeck++)->next = NULL;
}
/* put aces in foundation */
for (i = 0; i < 4; ++i)
foundation[i] = i;
#if 0
{
INT i,j, *p;
for (i = 0; i < 2; ++i)
for (j = 0; j < 6; ++j)
layout[i][j]->card = init[i][j];
p = (INT *) &init[0][0];
j = *p;
for (i = 1; i < 12; ++i)
p[i-1] = p[i];
p[11] = j;
}
#endif
bGameInProgress = TRUE;
}
VOID InitBoard(VOID)
{
INT xPos, yPos;
INT row, column, i;
RECT *pRect;
/* compute x and y increments */
xInc = (xClient - 40) / 6;
yInc = (yClient - 20) / 3;
/* compute foundation rectangles */
yPos = 10;
pRect = foundationRect;
for (xPos = 20 + xInc, i = 0; i < 4; ++i, xPos += xInc, ++pRect)
{
pRect->left = xPos;
pRect->top = yPos;
pRect->right = xPos + xCard;
pRect->bottom = yPos + yCard;
}
/* compute layout rectangles */
pRect = &layoutRect[0][0];
for (row = 0, yPos = 10 + yInc; row < 2; ++row, yPos += yInc)
for (column = 0, xPos = 20; column < 6; ++column, xPos += xInc)
{
pRect->left = xPos;
pRect->top = yPos;
pRect->right = xPos + xCard;
(pRect++)->bottom = yPos + yCard;
}
undoPos = 0;
freePos = 0;
}
VOID DrawLayout(HDC hDC)
{
INT row, column;
for (row = 0; row < 2; ++row)
for (column = 0; column < 6; ++column)
{
if (!layout[row][column])
continue;
cdtDraw(hDC, layoutRect[row][column].left,
layoutRect[row][column].top,
layout[row][column]->card,
faceup, dwBkgnd);
}
}
VOID DrawFoundation(HDC hDC)
{
INT i;
for (i = 0; i < 4; ++i)
cdtDraw(hDC, foundationRect[i].left, foundationRect[i].top,
foundation[i],
(foundation[i] == wDeckBack) ? facedown : faceup, dwBkgnd);
}
BOOL MoveCard(HDC hDC, INT startRow, INT startColumn,
INT endRow, INT endColumn,
BOOL bValidate)
{
CardRec *pStart = layout[startRow][startColumn];
CardRec *pEnd = layout[endRow][endColumn];
INT startCard = pStart->card;
INT endCard;
cardMode drawmode;
BOOL bDone;
if (pEnd == (CardRec*)NULL) /* on NT, this causes exception */
endCard = (endRow < 0) ? foundation[endColumn] : 0;
else
endCard = (endRow < 0) ? foundation[endColumn] : pEnd->card;
/* make sure suits match */
if (CardSuit(startCard) != CardSuit(endCard))
{
if (!bValidate && wErrorMessages)
Message(hWnd, IDSWrongSuit, MB_OK);
return FALSE;
}
/* take action based on where card is moved ... */
if (endRow < 0)
{ /* card to foundation */
/* card must be one higher than top of foundation */
if (IndexValue(startCard, ACELOW) != IndexValue(endCard, ACELOW) + 1)
{
if (!bValidate && wErrorMessages)
Message(hWnd, IDSNotNextCard, MB_OK);
return FALSE;
}
if (bValidate)
return TRUE;
/* move card to foundation and draw it */
if (CardRank(startCard) == king) {
startCard = wDeckBack;
drawmode = facedown;
} else
drawmode = faceup;
foundation[endColumn] = startCard;
cdtDraw(hDC, foundationRect[endColumn].left,
foundationRect[endColumn].top,
startCard, drawmode, dwBkgnd);
layout[startRow][startColumn] = pStart->next;
FreeList[freePos++] = pStart;
}
else
{ /* card to another pile */
/* card must be one lower in rank */
if (IndexValue(startCard, ACELOW) != IndexValue(endCard, ACELOW) - 1)
{
if (!bValidate && wErrorMessages)
Message(hWnd, IDSWrongRank, MB_OK);
return FALSE;
}
if (bValidate)
return TRUE;
/* move card to new pile and display it */
layout[endRow][endColumn] = pStart;
layout[startRow][startColumn] = pStart->next;
pStart->next = pEnd;
cdtDraw(hDC, layoutRect[endRow][endColumn].left,
layoutRect[endRow][endColumn].top,
startCard, faceup, dwBkgnd);
}
/* erase old card and expose new card */
if (layout[startRow][startColumn])
cdtDraw(hDC, layoutRect[startRow][startColumn].left,
layoutRect[startRow][startColumn].top,
layout[startRow][startColumn]->card, faceup, dwBkgnd);
else
cdtDraw(hDC, layoutRect[startRow][startColumn].left,
layoutRect[startRow][startColumn].top,
0, remove, dwBkgnd);
if (undoPos == MAXUNDOSIZE) {
Message(hWnd, IDSUndoFull, MB_OK);
undoPos = 0;
}
undo[undoPos].startRow = startRow;
undo[undoPos].endRow = endRow;
undo[undoPos].startColumn = startColumn;
undo[undoPos].endColumn = endColumn;
++undoPos;
bDone = TRUE;
for (startCard = 0; startCard < 4; ++startCard)
if (foundation[startCard] != wDeckBack) {
bDone = FALSE;
break;
}
if (bDone)
CheckGameOver();
return TRUE;
}
VOID RestoreLayout(HDC hDC)
{
INT i, j;
CardRec *pCurPos, *pList;
/* stage 1: Chain cards together ... */
/* find last non-empty stack */
for (i = 0; i < 12; ++i)
if (layout[i / 6][i % 6])
break;
/* start the list here */
pList = pCurPos = layout[i / 6][i % 6];
/* work towards last stack */
for (; i < 11; ++i)
{
while (pCurPos->next)
pCurPos = pCurPos->next;
pCurPos->next = layout[(i+1) / 6][(i+1) % 6];
}
/* stage 2: deal them back to layout again ... */
for (i = 0; i < 12; ++i)
{
layout[i / 6][i % 6] = pList;
for (j = 0; j < 3; ++j)
if (pList && pList->next)
pList = pList->next;
else
break;
if (j != 3)
break;
else
{
pCurPos = pList->next;
pList->next = NULL;
pList = pCurPos;
}
}
/* rest of stacks are empty */
for (++i; i < 12; ++i)
layout[i / 6][i % 6] = NULL;
}
VOID UndoMove(HDC hDC)
{
CHAR buffer[10];
RECT *pRect, rect;
INT column, card;
HBRUSH hBrush;
CardRec *pCard;
INT endRow, endColumn, startRow, startColumn;
--undoPos;
endRow = undo[undoPos].endRow;
startRow = undo[undoPos].startRow;
endColumn = undo[undoPos].endColumn;
startColumn = undo[undoPos].startColumn;
if (endRow < 0) {
/* move card from foundation back to pile */
if (!freePos) {
Message(hWnd, IDSIntFreePos, MB_OK | MB_ICONHAND);
return;
}
pCard = FreeList[--freePos];
/* move card to pile */
if (foundation[endColumn] == wDeckBack)
foundation[endColumn] = 48 + endColumn;
// foundation[endColumn] = CardIndex(endColumn, king);
pCard->card = card = foundation[endColumn];
pCard->next = layout[startRow][startColumn];
layout[startRow][startColumn] = pCard;
/* decrement card on foundation */
foundation[endColumn] -= 4;
// foundation[endColumn] = CardIndex(CardSuit(card), CardRank(card)-1);
/* update the foundation */
cdtDraw(hDC, foundationRect[endColumn].left,
foundationRect[endColumn].top,
foundation[endColumn], faceup, dwBkgnd);
/* update the pile */
cdtDraw(hDC, layoutRect[startRow][startColumn].left,
layoutRect[startRow][startColumn].top,
pCard->card, faceup, dwBkgnd);
} else {
/* move card from one pile to the other */
pCard = layout[endRow][endColumn];
layout[endRow][endColumn] = pCard->next;
pCard->next = layout[startRow][startColumn];
layout[startRow][startColumn] = pCard;
/* update pile we moved card back to (start) */
cdtDraw(hDC, layoutRect[startRow][startColumn].left,
layoutRect[startRow][startColumn].top,
pCard->card, faceup, dwBkgnd);
/* update pile we moved card back from (end), which could be empty
** now.
*/
if (layout[endRow][endColumn])
cdtDraw(hDC, layoutRect[endRow][endColumn].left,
layoutRect[endRow][endColumn].top,
layout[endRow][endColumn]->card, faceup, dwBkgnd);
else
cdtDraw(hDC, layoutRect[endRow][endColumn].left,
layoutRect[endRow][endColumn].top,
0, remove, dwBkgnd);
}
}
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 DoBacks()
{
FARPROC lpprocBacks;
lpprocBacks = MakeProcInstance( (FARPROC)BackDlgProc, hMyInstance);
DialogBox(hMyInstance, MAKEINTRESOURCE(1), hWnd, (WNDPROC)lpprocBacks);
FreeProcInstance(lpprocBacks);
}
BOOL APIENTRY BackDlgProc(HANDLE hdlg, WORD wm, WPARAM wParam, LONG lParam)
{
WORD cmd;
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:
cmd = GET_WM_COMMAND_ID(wParam, lParam);
if(cmd >= IDFACEDOWNFIRST && cmd <= IDFACEDOWN12) {
modeNew = cmd;
if (GET_WM_COMMAND_CMD(wParam, lParam) == BN_DOUBLECLICKED) {
ChangeBack((WORD)modeNew);
EndDialog(hdlg, 0);
}
} else
switch(cmd)
{
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;
INT i;
hDC = GetDC(hWnd);
for (i = 0; i < 4; ++i) {
if (foundation[i] == wDeckBack) {
if (hDC)
cdtDraw(hDC, foundationRect[i].left, foundationRect[i].top, wNewDeckBack, facedown, dwBkgnd);
foundation[i] = wNewDeckBack;
}
}
if (hDC)
ReleaseDC(hWnd, hDC);
wDeckBack = wNewDeckBack;
return;
}
VOID DrawAnimate(INT cd, MPOINT *ppt, INT iani)
{
HDC hDC;
INT i;
if(!(hDC = GetDC(hWnd)))
return;
for (i = 0; i < 4; ++i)
if (foundation[i] == wDeckBack)
cdtAnimate(hDC, cd, foundationRect[i].left, foundationRect[i].top, iani);
ReleaseDC(hWnd, hDC);
}
BOOL DeckAnimate(INT iqsec)
{
INT iani;
MPOINT pt;
pt.x = pt.y = 0; /* not used! */
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;
}
WORD APIENTRY TimerProc(HWND hwnd, INT wm, INT id, DWORD dwTime)
{
static INT x = 0;
if (bGameInProgress)
DeckAnimate(x++);
return TRUE;
}
VOID SaveState(VOID)
{
CHAR sz[80];
wsprintf(sz, "%ld %ld %ld %ld %ld", nStats[0], nStats[1],
nStats[2], nStats[3], nStats[4]);
WritePrivateProfileString(szAppName, "Stats", sz, ININAME);
wsprintf(sz, "%d %d", wErrorMessages, wDeckBack);
WritePrivateProfileString(szAppName, "MenuState", sz, ININAME);
}
VOID DisplayStats(VOID)
{
CHAR sz[80];
fDialog(2, hWnd, (FARPROC)RecordDlgProc);
}
VOID RestoreState(VOID)
{
CHAR sz[80], *psz;
INT col;
GetPrivateProfileString(szAppName, "Stats", "0 52 0 0 0", sz,
sizeof(sz), ININAME);
psz = lstrtok(sz, " ");
col = 0;
if (psz) {
nStats[0] = atol(psz);
for (col = 1; col < 5 && psz; ++col)
nStats[col] = atol(psz = lstrtok(NULL, " "));
}
for (; col < 5; ++col)
nStats[col] = 0;
GetPrivateProfileString(szAppName, "MenuState", "0 65 4", sz,
sizeof(sz), ININAME);
psz = lstrtok(sz, " ");
if (psz) {
wErrorMessages = atoi(psz);
psz = lstrtok(NULL, " ");
wDeckBack = psz ? atoi(psz) : IDCARDBACK;
} 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 sucking 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,FARPROC fpfn)
{
BOOL f;
HANDLE hInst;
hInst = (HANDLE)GetWindowLong(hwnd,GWL_HINSTANCE);
fpfn = MakeProcInstance(fpfn,hInst);
f = DialogBox(hInst,MAKEINTRESOURCE(id),hwnd,(WNDPROC)fpfn);
FreeProcInstance ((FARPROC)fpfn);
return f;
}
BOOL APIENTRY RecordDlgProc(HANDLE hdlg, WORD wm, WPARAM wParam, LONG lParam)
{
CHAR sz[80];
HWND hwndEdit;
INT i;
switch(wm) {
case WM_INITDIALOG:
hwndEdit = GetDlgItem(hdlg, IDD_RECORD);
SendMessage(hwndEdit, LB_ADDSTRING, 0, (DWORD) ((LPSTR) szRecordTitle));
wsprintf(sz, "%ld\t%ld\t%ld\t%ld\t%ld", nStats[TOTAL], nStats[WINS],
nStats[HI], nStats[LOW], nStats[TOTAL] ? (nStats[SUM] / nStats[TOTAL]) : 0);
SendMessage(hwndEdit, LB_ADDSTRING, 0, (DWORD) ((LPSTR) 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:
for (i = 0; i < 5; ++i)
nStats[i] = 0;
nStats[LOW] = 52;
lstrcpy(sz, "0\t0\t0\t52\t0");
hwndEdit = GetDlgItem(hdlg, IDD_RECORD);
SendMessage(hwndEdit, LB_DELETESTRING, 1, 0L);
SendMessage(hwndEdit, LB_ADDSTRING, 0, (DWORD) ((LPSTR) sz));
break;
}
break;
}
return FALSE;
}
static FARPROC lpOldWP;
VOID MarkControlReadOnly(HWND hwndCtrl, BOOL bReadOnly)
{
if (bReadOnly)
lpOldWP = (FARPROC) SetWindowLong(hwndCtrl, GWL_WNDPROC,
(DWORD) ReadOnlyProc);
else
SetWindowLong(hwndCtrl, GWL_WNDPROC, (LONG)lpOldWP);
}
INT APIENTRY ReadOnlyProc(HWND hwnd, WORD wMessage, WPARAM wParam, LONG lParam)
{
switch (wMessage) {
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_LBUTTONDBLCLK:
case WM_RBUTTONDBLCLK:
return 0L;
}
return CallWindowProc((WNDPROC)lpOldWP, hwnd, wMessage, wParam, lParam);
}
#if 0
VOID ShowStacks(VOID)
{
CHAR szBuf[80];
wsprintf(szBuf,
"%02d %02d %02d %02d %02d %02d\n%02d %02d %02d %02d %02d %02d",
layout[0][0] ? layout[0][0]->card : -1,
layout[0][1] ? layout[0][1]->card : -1,
layout[0][2] ? layout[0][2]->card : -1,
layout[0][3] ? layout[0][3]->card : -1,
layout[0][4] ? layout[0][4]->card : -1,
layout[0][5] ? layout[0][5]->card : -1,
layout[1][0] ? layout[1][0]->card : -1,
layout[1][1] ? layout[1][1]->card : -1,
layout[1][2] ? layout[1][2]->card : -1,
layout[1][3] ? layout[1][3]->card : -1,
layout[1][4] ? layout[1][4]->card : -1,
layout[1][5] ? layout[1][5]->card : -1);
MessageBox(hWnd, szBuf, "Card Stacks", MB_OK);
wsprintf(szBuf,
"%02d %02d %02d %02d %02d %02d\n%02d %02d %02d %02d %02d %02d",
layout[0][0] ? IndexValue(layout[0][0]->card, ACELOW) : -1,
layout[0][1] ? IndexValue(layout[0][1]->card, ACELOW) : -1,
layout[0][2] ? IndexValue(layout[0][2]->card, ACELOW) : -1,
layout[0][3] ? IndexValue(layout[0][3]->card, ACELOW) : -1,
layout[0][4] ? IndexValue(layout[0][4]->card, ACELOW) : -1,
layout[0][5] ? IndexValue(layout[0][5]->card, ACELOW) : -1,
layout[1][0] ? IndexValue(layout[1][0]->card, ACELOW) : -1,
layout[1][1] ? IndexValue(layout[1][1]->card, ACELOW) : -1,
layout[1][2] ? IndexValue(layout[1][2]->card, ACELOW) : -1,
layout[1][3] ? IndexValue(layout[1][3]->card, ACELOW) : -1,
layout[1][4] ? IndexValue(layout[1][4]->card, ACELOW) : -1,
layout[1][5] ? IndexValue(layout[1][5]->card, ACELOW) : -1);
MessageBox(hWnd, szBuf, "IndexValue", MB_OK);
}
#endif