CLIENT.CPP

/*========================================================================== 
*
* Copyright (C) 1997 Microsoft Corporation. All Rights Reserved.
*
* File: client.cpp
* Content:Slot machine client using DirectPlay.
*
***************************************************************************/

#include <windows.h>
#include <windowsx.h>
#include <stdio.h>

#include "dpslots.h"
#include "resource.h"

// constants
const DWORD SLOTWIDTH= 110;// width of slot
const DWORD SLOTHEIGHT= 119;// height of slot
const DWORD SLOTBORDER= 9;// space between slots
const DWORD REVSPERSECOND= 1;// no. revolutions per second
const DWORD PIXELSPERSLOT= SLOTHEIGHT - SLOTBORDER;// no. vertical pixels per slot
const DWORD PIXELSPERREV= PIXELSPERSLOT * SLOTSPERWHEEL;// no. vertical pixels drawn per revolution
const DWORD PIXELSPERSECOND= PIXELSPERREV * REVSPERSECOND;// no. vertical pixels drawn per second
const UINTTIMERID= 1;// timer ID to use
const UINTTIMERINTERVAL= 50;// timer interval
const UINTMAXSTRING= 200;// max size of a string

// window messages
const UINTWM_USER_UPDATEBALANCE = WM_USER + BALANCERESPONSE;
const UINTWM_USER_STARTSPINNING = WM_USER + SPINRESPONSE;

// structures
typedef struct {
DWORDdwIndex;// index of wheel slot to show
DWORDdwStartTicks;// time wheel started spinning
DWORDdwDuration;// duration wheel should spin
} WHEELINFO, *LPWHEELINFO;

// globals
HWNDghClientWnd = NULL;// main window

// prototypes
HRESULTSendBalanceRequest(LPDPLAYINFO lpDPInfo);
HRESULTSendSpinRequest(LPDPLAYINFO lpDPInfo, DWORD dwAmountBet);
voidDrawWheels(LPWHEELINFO lpWheels, HBITMAP hWheelBitmap,
HDC hDC, LPRECT lpBoundsRect);
voidStartSpinning(LPWHEELINFO lpWheels);
BOOLSpinWheels(HWND hWnd, LPWHEELINFO lpWheels, HBITMAP hWheelBitmap);

BOOL CALLBACK ClientWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static LPDPLAYINFOlpDPInfo = NULL;
static UINTidTimer = 0;
static HBITMAPhWheelBitmap = NULL;
static WHEELINFOWheelInfo[NUMWHEELS];
static MSG_SPINRESPONSESpinResponse;
CHARszStr[MAXSTRING];
DWORDi;
DWORDdwAmountBet;

switch(uMsg)
{
case WM_INITDIALOG:
// save the connection info pointer
lpDPInfo = (LPDPLAYINFO) lParam;

// store global window
ghClientWnd = hWnd;

// get slots bitmap
hWheelBitmap = LoadBitmap(ghInstance, MAKEINTRESOURCE(IDB_SLOTSBITMAP));

// initialize slots
for (i = 0; i < NUMWHEELS; i++)
WheelInfo[i].dwIndex = ((DWORD)rand()) % SLOTSPERWHEEL;

// get starting balance
SendBalanceRequest(lpDPInfo);
break;

case WM_DESTROY:
// stop the timer
if (idTimer)
{
KillTimer(hWnd, idTimer);
idTimer = 0;
}

// free the bitmap handle
if (hWheelBitmap)
{
DeleteObject(hWheelBitmap);
hWheelBitmap = NULL;
}
ghClientWnd = NULL;
break;

case WM_USER_UPDATEBALANCE:

// balance is in lParam
sprintf(szStr, "$%4d", lParam);

// display new balance
SetDlgItemText(hWnd, IDC_EDIT_BALANCE, szStr);
break;

case WM_USER_STARTSPINNING:

// copy spin response message from lParam
SpinResponse = *((LPMSG_SPINRESPONSE) lParam);
GlobalFreePtr((LPVOID) lParam);// free memory

// check for valid spin
if FAILED(SpinResponse.hr)
{
SetDlgItemText(hWnd, IDC_RESULTEDIT, "You don't have enough money!");
}
else
{
// copy slot settings specified by server
for (i = 0; i < NUMWHEELS; i++)
WheelInfo[i].dwIndex = SpinResponse.dwIndex[i];

// clear win or lose
SetDlgItemText(hWnd, IDC_RESULTEDIT, "");

// start things spinning
StartSpinning(WheelInfo);
idTimer = SetTimer(hWnd, TIMERID, TIMERINTERVAL, NULL);

// disable spin button while spinning
EnableDlgButton(hWnd, IDC_SPINBUTTON, FALSE);
}
break;

case WM_TIMER:
// readraw any spinning wheels
if (!SpinWheels(hWnd, WheelInfo, hWheelBitmap))
{
KillTimer(hWnd, idTimer);
idTimer = 0;

// display amount won or lost
if (SpinResponse.dwAmountWonOrLost > 0)
{
sprintf(szStr,"You win $%d!", SpinResponse.dwAmountWonOrLost);
PlaySound(MAKEINTRESOURCE(IDR_WINWAVE), ghInstance, SND_ASYNC | SND_RESOURCE);
}
else if (SpinResponse.dwAmountWonOrLost < 0)
{
sprintf(szStr,"You lose $%d!", -SpinResponse.dwAmountWonOrLost);
PlaySound(MAKEINTRESOURCE(IDR_LOSEWAVE), ghInstance, SND_ASYNC | SND_RESOURCE);
}
else
{
strcpy(szStr, "");
}

// display win or loss
SetDlgItemText(hWnd, IDC_RESULTEDIT, szStr);

PostMessage(hWnd, WM_USER_UPDATEBALANCE, (WPARAM) 0, (LPARAM) SpinResponse.dwBalance);

// enable spin button again
EnableDlgButton(hWnd, IDC_SPINBUTTON, TRUE);
}
break;

case WM_DRAWITEM:
{
DRAWITEMSTRUCT*diInfo;

diInfo = (DRAWITEMSTRUCT *) lParam;

switch (diInfo->CtlID)
{
case IDC_SLOTS:
if (diInfo->itemAction == ODA_DRAWENTIRE)
{
DrawWheels(WheelInfo, hWheelBitmap, diInfo->hDC, &diInfo->rcItem);
}
break;
}
}
break;

case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDC_SPINBUTTON:
// find out how much was bet
dwAmountBet = 0;

// one dollar
if (DlgItemIsChecked(hWnd, IDC_BET1CHECK))
dwAmountBet += 1;

// five dollars
if (DlgItemIsChecked(hWnd, IDC_BET2CHECK))
dwAmountBet += 5;

// ten dollars
if (DlgItemIsChecked(hWnd, IDC_BET3CHECK))
dwAmountBet += 10;

// ask server for results of spin using this bet
SendSpinRequest(lpDPInfo, dwAmountBet);
break;

case IDCANCEL:
EndDialog(hWnd, FALSE);
break;
}
break;
}

// Allow for default processing
return FALSE;
}

void ClientApplicationMessage(LPDPLAYINFO lpDPInfo, LPDPMSG_GENERIC lpMsg, DWORD dwMsgSize,
DPID idFrom, DPID idTo)
{
switch (lpMsg->dwType)
{
case BALANCERESPONSE:
{
LPMSG_BALANCERESPONSE lpBalance = (LPMSG_BALANCERESPONSE)lpMsg;

PostMessage(ghClientWnd, WM_USER_UPDATEBALANCE, (WPARAM) 0, (LPARAM) lpBalance->dwBalance);
}
break;

case SPINRESPONSE:
{
LPMSG_SPINRESPONSE lpSpin = (LPMSG_SPINRESPONSE)lpMsg;
LPMSG_SPINRESPONSE lpSpinCopy;

// make a copy of the message so we can pass it to the wndproc
lpSpinCopy = (LPMSG_SPINRESPONSE) GlobalAllocPtr(GHND, sizeof(MSG_SPINRESPONSE));
if (lpSpinCopy == NULL)
break;

*lpSpinCopy = *lpSpin;

PostMessage(ghClientWnd, WM_USER_STARTSPINNING, (WPARAM) 0, (LPARAM) lpSpinCopy);
}
break;

default:
break;
}
}

void ClientSystemMessage(LPDPLAYINFO lpDPInfo, LPDPMSG_GENERIC lpMsg, DWORD dwMsgSize,
DPID idFrom, DPID idTo)
{
// The body of each case is there so you can set a breakpoint and examine
// the contents of the message received.
switch (lpMsg->dwType)
{
case DPSYS_CREATEPLAYERORGROUP:
{
LPDPMSG_CREATEPLAYERORGROUP lp = (LPDPMSG_CREATEPLAYERORGROUP) lpMsg;
}
break;

case DPSYS_DESTROYPLAYERORGROUP:
{
LPDPMSG_DESTROYPLAYERORGROUP lp = (LPDPMSG_DESTROYPLAYERORGROUP)lpMsg;
}
break;

case DPSYS_ADDPLAYERTOGROUP:
{
LPDPMSG_ADDPLAYERTOGROUP lp = (LPDPMSG_ADDPLAYERTOGROUP)lpMsg;
}
break;

case DPSYS_DELETEPLAYERFROMGROUP:
{
LPDPMSG_DELETEPLAYERFROMGROUP lp = (LPDPMSG_DELETEPLAYERFROMGROUP)lpMsg;
}
break;

case DPSYS_SESSIONLOST:
{
LPDPMSG_SESSIONLOST lp = (LPDPMSG_SESSIONLOST)lpMsg;
}
break;

case DPSYS_HOST:
{
LPDPMSG_HOSTlp = (LPDPMSG_HOST)lpMsg;
}
break;

case DPSYS_SETPLAYERORGROUPDATA:
{
LPDPMSG_SETPLAYERORGROUPDATA lp = (LPDPMSG_SETPLAYERORGROUPDATA)lpMsg;
}
break;

case DPSYS_SETPLAYERORGROUPNAME:
{
LPDPMSG_SETPLAYERORGROUPNAME lp = (LPDPMSG_SETPLAYERORGROUPNAME)lpMsg;
}
break;

case DPSYS_SECUREMESSAGE:
{
LPDPMSG_SECUREMESSAGE lp = (LPDPMSG_SECUREMESSAGE)lpMsg;

ClientApplicationMessage(lpDPInfo, (LPDPMSG_GENERIC) lp->lpData, lp->dwDataSize,
lp->dpIdFrom, idTo);

}
break;
}
}

HRESULT SendBalanceRequest(LPDPLAYINFO lpDPInfo)
{
MSG_BALANCEREQUESTMsg;

ZeroMemory(&Msg, sizeof(MSG_BALANCEREQUEST));
Msg.dwType = BALANCEREQUEST;

return (lpDPInfo->lpDirectPlay3A->Send(lpDPInfo->dpidPlayer,
DPID_SERVERPLAYER, SENDFLAGS(lpDPInfo->bIsSecure),
&Msg, sizeof(MSG_BALANCEREQUEST)));
}

HRESULT SendSpinRequest(LPDPLAYINFO lpDPInfo, DWORD dwAmountBet)
{
MSG_SPINREQUESTMsg;

ZeroMemory(&Msg, sizeof(MSG_SPINREQUEST));
Msg.dwType = SPINREQUEST;
Msg.dwAmountBet = dwAmountBet;

return (lpDPInfo->lpDirectPlay3A->Send(lpDPInfo->dpidPlayer,
DPID_SERVERPLAYER, SENDFLAGS(lpDPInfo->bIsSecure),
&Msg, sizeof(MSG_SPINREQUEST)));
}

#define RECTWIDTH(lpRect) ((lpRect)->right - (lpRect)->left)
#define RECTHEIGHT(lpRect) ((lpRect)->bottom - (lpRect)->top)

BOOL PaintBitmap(HDC hDC, LPRECT lpDCRect, HBITMAP hDDB, LPRECT lpDDBRect)
{
HDC hMemDC; // Handle to memory DC
HBITMAP hOldBitmap; // Handle to previous bitmap
BOOL bSuccess = FALSE; // Success/fail flag

// Create a memory DC

hMemDC = CreateCompatibleDC(hDC);

// If this failed, return FALSE

if (!hMemDC)
return FALSE;

// Select bitmap into the memory DC

hOldBitmap = (HBITMAP) SelectObject (hMemDC, hDDB);

// Make sure to use the stretching mode best for color pictures

SetStretchBltMode (hDC, COLORONCOLOR);

// Determine whether to call StretchBlt() or BitBlt()
if ((RECTWIDTH(lpDCRect) == RECTWIDTH(lpDDBRect)) &&
(RECTHEIGHT(lpDCRect) == RECTHEIGHT(lpDDBRect)))
bSuccess = BitBlt(hDC, lpDCRect->left, lpDCRect->top,
lpDCRect->right - lpDCRect->left,
lpDCRect->bottom - lpDCRect->top, hMemDC, lpDDBRect->left,
lpDDBRect->top, SRCCOPY);
else
bSuccess = StretchBlt(hDC, lpDCRect->left, lpDCRect->top,
lpDCRect->right - lpDCRect->left,
lpDCRect->bottom - lpDCRect->top, hMemDC, lpDDBRect->left,
lpDDBRect->top, lpDDBRect->right - lpDDBRect->left,
lpDDBRect->bottom - lpDDBRect->top, SRCCOPY);

// Clean up

SelectObject(hMemDC, hOldBitmap);

DeleteDC(hMemDC);

// Return with success/fail flag

return bSuccess;
}

void DrawWheels(LPWHEELINFO lpWheels, HBITMAP hWheelBitmap, HDC hDC, LPRECT lpBoundsRect)
{
RECTrectDC, rectSlot;
DWORDdwWidth, dwHeight, dwXOffset, dwYOffset;
DWORDi;

if (hWheelBitmap == NULL)
return;

dwWidth = lpBoundsRect->right - lpBoundsRect->left;
dwHeight = lpBoundsRect->bottom - lpBoundsRect->top;

dwXOffset = (dwWidth - (SLOTWIDTH * NUMWHEELS)) / (NUMWHEELS + 1);
dwYOffset = (dwHeight - SLOTHEIGHT) / 2;

SetRect(&rectDC, dwXOffset, dwYOffset,
dwXOffset + SLOTWIDTH, dwYOffset + SLOTHEIGHT);

for (i = 0; i < NUMWHEELS; i++)
{
SetRect(&rectSlot, 0, 0, SLOTWIDTH, SLOTHEIGHT);
OffsetRect(&rectSlot, 0, lpWheels[i].dwIndex * PIXELSPERSLOT);

PaintBitmap(hDC, &rectDC, hWheelBitmap, &rectSlot);

OffsetRect(&rectDC, SLOTWIDTH + dwXOffset, 0);
}
}

void StartSpinning(LPWHEELINFO lpWheels)
{
DWORDi;

for (i = 0; i < NUMWHEELS; i++)
{
lpWheels[i].dwStartTicks = GetTickCount();
lpWheels[i].dwStartTicks -= lpWheels[i].dwIndex * PIXELSPERSLOT * 1000 / PIXELSPERREV;
lpWheels[i].dwDuration = 1000 * (i + 1) + 1000;
}
}

BOOL SpinWheels(HWND hWnd, LPWHEELINFO lpWheels, HBITMAP hWheelBitmap)
{
HDChDC;
RECTrectBounds;
LPRECTlpBoundsRect;
RECTrectDC, rectSlot;
DWORDdwWidth, dwHeight, dwXOffset, dwYOffset, dwYStart;
DWORDi, dwTicks, dwStoppedCount;

if (hWheelBitmap == NULL)
return (FALSE);

hDC = GetWindowDC(GetDlgItem(hWnd, IDC_SLOTS));
if (hDC == NULL)
return (FALSE);

if (!GetWindowRect(GetDlgItem(hWnd, IDC_SLOTS), &rectBounds))
return (FALSE);

lpBoundsRect = &rectBounds;

dwWidth = lpBoundsRect->right - lpBoundsRect->left;
dwHeight = lpBoundsRect->bottom - lpBoundsRect->top;

dwXOffset = (dwWidth - (SLOTWIDTH * NUMWHEELS)) / (NUMWHEELS + 1);
dwYOffset = (dwHeight - SLOTHEIGHT) / 2;

SetRect(&rectDC, dwXOffset, dwYOffset,
dwXOffset + SLOTWIDTH, dwYOffset + SLOTHEIGHT);

dwStoppedCount = 0;
for (i = 0; i < NUMWHEELS; i++)
{
if (lpWheels[i].dwDuration == 0)
{
dwStoppedCount++;
}
else
{
dwTicks = GetTickCount() - lpWheels[i].dwStartTicks;
dwYStart = (dwTicks * PIXELSPERSECOND) / 1000;
dwYStart %= PIXELSPERREV;

if (dwTicks >= lpWheels[i].dwDuration)
{
//lpWheels[i].value = ((dwYStart + (PIXELSPERSLOT - 1)) / PIXELSPERSLOT) % SLOTSPERWHEEL;

SetRect(&rectSlot, 0, 0, SLOTWIDTH, SLOTHEIGHT);
OffsetRect(&rectSlot, 0, lpWheels[i].dwIndex * PIXELSPERSLOT);
PaintBitmap(hDC, &rectDC, hWheelBitmap, &rectSlot);

lpWheels[i].dwDuration = 0;
if (dwStoppedCount == (NUMWHEELS - 1))
PlaySound(MAKEINTRESOURCE(IDR_STOPWAVE), ghInstance, SND_RESOURCE);
else
PlaySound(MAKEINTRESOURCE(IDR_STOPWAVE), ghInstance, SND_ASYNC | SND_RESOURCE);
}
else
{
SetRect(&rectSlot, 0, 0, SLOTWIDTH, SLOTHEIGHT);
OffsetRect(&rectSlot, 0, dwYStart);
if (rectSlot.bottom > PIXELSPERREV)
{
RECTrectSlotTmp, rectDCTmp;
DWORDheight;

// copy from bottom end of bitmap
height = PIXELSPERREV - rectSlot.top;
rectSlotTmp = rectSlot;
rectSlotTmp.bottom = rectSlotTmp.top + height;
rectDCTmp = rectDC;
rectDCTmp.bottom = rectDCTmp.top + height;
PaintBitmap(hDC, &rectDCTmp, hWheelBitmap, &rectSlotTmp);

height = rectSlot.bottom - PIXELSPERREV;
rectSlotTmp = rectSlot;
rectSlotTmp.top = 0;
rectSlotTmp.bottom = height;
rectDCTmp = rectDC;
rectDCTmp.top = rectDCTmp.bottom - height;
PaintBitmap(hDC, &rectDCTmp, hWheelBitmap, &rectSlotTmp);
}
else
PaintBitmap(hDC, &rectDC, hWheelBitmap, &rectSlot);
}
}
OffsetRect(&rectDC, SLOTWIDTH + dwXOffset, 0);
}

return (dwStoppedCount != NUMWHEELS);
}