|
|
/***************************************************************************/ /** Microsoft Windows **/ /** Copyright(c) Microsoft Corp., 1991, 1992 **/ /***************************************************************************/
/****************************************************************************
card.cpp
Nov 91, JimH
Methods for card objects
****************************************************************************/
#include "hearts.h"
#include <stdlib.h> // for labs() prototype
#include "card.h"
// Declare (and initialize) static members
HINSTANCE card::hCardsDLL; INITPROC card::lpcdtInit; DRAWPROC card::lpcdtDraw; FARPROC card::lpcdtTerm; CBitmap card::m_bmFgnd; CBitmap card::m_bmBgnd2; CDC card::m_MemB; CDC card::m_MemB2; CRgn card::m_Rgn1; CRgn card::m_Rgn2; CRgn card::m_Rgn; DWORD card::dwPixel[12];
BOOL card::bConstructed; int card::dxCrd; int card::dyCrd; CBitmap card::m_bmBgnd;
int card::count = 0; int card::stepsize = 15; // bigger stepsize -> faster glide
/****************************************************************************
card::card
If this is the first card being constructed, links to cards.dll are set up, along with the bitmaps and regions required for glide()
****************************************************************************/
card::card(int n) : id(n), state(NORMAL) { loc.x = 0; loc.y = 0; if (count == 0) { bConstructed = FALSE;
hCardsDLL = LoadLibrary(TEXT("CARDS.DLL")); if (hCardsDLL < (HINSTANCE)HINSTANCE_ERROR) return;
lpcdtInit = (INITPROC) GetProcAddress(hCardsDLL, "cdtInit"); lpcdtDraw = (DRAWPROC) GetProcAddress(hCardsDLL, "cdtDraw"); lpcdtTerm = (FARPROC) GetProcAddress(hCardsDLL, "cdtTerm");
BOOL bResult = FALSE; if(lpcdtInit) { bResult = (*lpcdtInit)(&dxCrd, &dyCrd); } if (!bResult) return;
bConstructed = TRUE;
CDC ic; ic.CreateIC(TEXT("DISPLAY"), NULL, NULL, NULL);
if (!m_bmBgnd.CreateCompatibleBitmap(&ic, dxCrd, dyCrd) || !m_bmFgnd.CreateCompatibleBitmap(&ic, dxCrd, dyCrd) || !m_bmBgnd2.CreateCompatibleBitmap(&ic, dxCrd, dyCrd)) bConstructed = FALSE;
ic.DeleteDC();
if (!m_Rgn1.CreateRectRgn(1, 1, 2, 2) || // dummy sizes
!m_Rgn2.CreateRectRgn(1, 1, 2, 2) || !m_Rgn.CreateRectRgn(1, 1, 2, 2)) bConstructed = FALSE; } count++; }
/****************************************************************************
card::~card
If this is the last card being destroyed, cards.dll is freed and the bitmaps and regions created for glide() are deleted.
****************************************************************************/ card::~card() { count--; if (count == 0) { (*lpcdtTerm)(); FreeLibrary(hCardsDLL); m_bmBgnd.DeleteObject(); m_bmFgnd.DeleteObject(); m_bmBgnd2.DeleteObject(); m_Rgn.DeleteObject(); m_Rgn1.DeleteObject(); m_Rgn2.DeleteObject(); } }
/****************************************************************************
card::Draw
wrapper for cards.cdtDraw() EMPTY cards are not passed through
****************************************************************************/
BOOL card::Draw(CDC &dc, int x, int y, int mode, BOOL bUpdateLoc) { if (bUpdateLoc) { loc.x = x; // update current location
loc.y = y; }
if (id == EMPTY) return FALSE;
return (*lpcdtDraw)(dc.m_hDC, x, y, mode == FACEDOWN ? CARDBACK : id, mode, 255); }
/****************************************************************************
card::CleanDraw
Same as Draw except corners are cleaned up before bitmap is blted. It's slower than normal draw, but there won't be a white flash in the corners.
****************************************************************************/
BOOL card::CleanDraw(CDC &dc) { if (id == EMPTY) return FALSE;
SaveCorners(dc, loc.x, loc.y); CDC memDC; memDC.CreateCompatibleDC(&dc); CBitmap bitmap; if (!bitmap.CreateCompatibleBitmap(&dc, dxCrd, dyCrd)) return FALSE;
CBitmap *oldbitmap = memDC.SelectObject(&bitmap); BOOL bResult = (*lpcdtDraw)(memDC.m_hDC, 0, 0, id, FACEUP, 0); if (!bResult) return FALSE;
RestoreCorners(memDC, 0, 0); dc.BitBlt(loc.x, loc.y, dxCrd, dyCrd, &memDC, 0, 0, SRCCOPY);
memDC.SelectObject(oldbitmap); bitmap.DeleteObject();
return TRUE; }
/****************************************************************************
card::PopDraw
Version of Draw intended for local humans. Selected cards are popped up.
****************************************************************************/
BOOL card::PopDraw(CDC &dc) { if (id == EMPTY) return FALSE;
int y = loc.y; if (state == SELECTED) y -= POPSPACING;
return (*lpcdtDraw)(dc.m_hDC, loc.x, y, id, FACEUP, 0); }
/****************************************************************************
card::Draw
This routine glides a card from its current position to the specified end position.
NOTE: before Glide() is called, the client must load card::m_bmBgnd with a bitmap of what should be displayed as being underneath the original card location. card::m_bmBgnd is created when the first card is constructed, and destroyed when the last card is destructed. Note also that card::m_bmBgnd must NOT be selected in any DC when Glide() is called.
****************************************************************************/
VOID card::Glide(CDC &dc, int xEnd, int yEnd) { int x1, y1, x2, y2; // each step is x1,y1 to x2,y2
if (!m_MemB.CreateCompatibleDC(&dc) || // memory DCs
!m_MemB2.CreateCompatibleDC(&dc)) return;
m_MemB2.SelectObject(&m_bmBgnd2); m_MemB.SelectObject(&m_bmFgnd);
// draw card into fgnd bitmap
(*lpcdtDraw)(m_MemB.m_hDC, 0, 0, id, FACEUP, 0);
m_MemB.SelectObject(&m_bmBgnd); // associate memDCs with bitmaps
SaveCorners(dc, loc.x, loc.y); RestoreCorners(m_MemB, 0, 0);
long dx = xEnd - loc.x; long dy = yEnd - loc.y; int distance = IntSqrt(dx*dx + dy*dy); // int approx. of dist. to travel
int steps = distance / stepsize; // determine # of intermediate steps
// Ensure that GlideStep gets called an even number of times so
// the background bitmap will get set properly for multi-glide moves
if ((steps % 2) == 1) steps++;
x1 = loc.x; y1 = loc.y; for (int i = 1; i < steps; i++) { x2 = loc.x + (int) (((long)i * dx) / (long)steps); y2 = loc.y + (int) (((long)i * dy) / (long)steps); GlideStep(dc, x1, y1, x2, y2); x1 = x2; y1 = y2; }
// do last step manually so it lands exactly on xEnd, yEnd
GlideStep(dc, x1, y1, xEnd, yEnd);
// reset clip region for entire screen
m_Rgn.SetRectRgn(0, 0, 30000, 30000); // really big region
dc.SelectObject(&m_Rgn);
loc.x = xEnd; loc.y = yEnd;
m_MemB.DeleteDC(); // clean up memory DCs
m_MemB2.DeleteDC(); }
/******************************************************************************
GlideStep
This routine gets called once for each step in the glide animation. On input, it needs the screen under the source in m_MemB, and the card to be moved in m_bmFgnd. It calculates the screen under the destination itself and blts it into m_MemB2. At the end of the animation, it moves m_MemB2 into m_MemB so it can be called again immediately with new coordinates.
******************************************************************************/
VOID card::GlideStep(CDC &dc, int x1, int y1, int x2, int y2) { m_Rgn1.SetRectRgn(x1, y1, x1+dxCrd, y1+dyCrd); m_Rgn2.SetRectRgn(x2, y2, x2+dxCrd, y2+dyCrd);
/* create background of new location by combing screen background
plus overlap from old background */
m_MemB2.BitBlt(0, 0, dxCrd, dyCrd, &dc, x2, y2, SRCCOPY); m_MemB2.BitBlt(x1-x2, y1-y2, dxCrd, dyCrd, &m_MemB, 0, 0, SRCCOPY); SaveCorners(m_MemB2, 0, 0);
/* Draw old background and then draw card */
m_Rgn.CombineRgn(&m_Rgn1, &m_Rgn2, RGN_DIFF); // part of hRgn1 not in hRgn2
dc.SelectObject(&m_Rgn); dc.BitBlt(x1, y1, dxCrd, dyCrd, &m_MemB, 0, 0, SRCCOPY); dc.SelectObject(&m_Rgn2); CBitmap *oldbitmap = m_MemB.SelectObject(&m_bmFgnd); // temp
RestoreCorners(m_MemB, 0, 0); dc.BitBlt(x2, y2, dxCrd, dyCrd, &m_MemB, 0, 0, SRCCOPY); m_MemB.SelectObject(oldbitmap); // restore
/* copy new background to old background, or rather, accomplish the
same effect by swapping the associated memory device contexts. */
HDC temp = m_MemB.Detach(); // detach the hDC from the CDC
m_MemB.Attach(m_MemB2.Detach()); // move the hDC from B2 to B
m_MemB2.Attach(temp); // finish the swap
}
/******************************************************************************
IntSqrt
Newton's method to find a quick close-enough square root without pulling in the floating point libraries.
f(x) = x*x - square = 0 f'(x) = 2x
******************************************************************************/
int card::IntSqrt(long square) { long lastguess = square; long guess = min(square / 2L, 1024L);
while (labs(guess-lastguess) > 3L) // 3 is close enough
{ lastguess = guess; guess -= ((guess * guess) - square) / (2L * guess); }
return (int) guess; }
/******************************************************************************
SaveCorners RestoreCorners
based on similar routines in cards.dll
******************************************************************************/
VOID card::SaveCorners(CDC &dc, int x, int y) { // Upper Left
dwPixel[0] = dc.GetPixel(x, y); dwPixel[1] = dc.GetPixel(x+1, y); dwPixel[2] = dc.GetPixel(x, y+1);
// Upper Right
x += dxCrd -1; dwPixel[3] = dc.GetPixel(x, y); dwPixel[4] = dc.GetPixel(x-1, y); dwPixel[5] = dc.GetPixel(x, y+1);
// Lower Right
y += dyCrd-1; dwPixel[6] = dc.GetPixel(x, y); dwPixel[7] = dc.GetPixel(x, y-1); dwPixel[8] = dc.GetPixel(x-1, y);
// Lower Left
x -= dxCrd-1; dwPixel[9] = dc.GetPixel(x, y); dwPixel[10] = dc.GetPixel(x+1, y); dwPixel[11] = dc.GetPixel(x, y-1); }
VOID card::RestoreCorners(CDC &dc, int x, int y) { // Upper Left
dc.SetPixel(x, y, dwPixel[0]); dc.SetPixel(x+1, y, dwPixel[1]); dc.SetPixel(x, y+1, dwPixel[2]);
// Upper Right
x += dxCrd-1; dc.SetPixel(x, y, dwPixel[3]); dc.SetPixel(x-1, y, dwPixel[4]); dc.SetPixel(x, y+1, dwPixel[5]);
// Lower Right
y += dyCrd-1; dc.SetPixel(x, y, dwPixel[6]); dc.SetPixel(x, y-1, dwPixel[7]); dc.SetPixel(x-1, y, dwPixel[8]);
// Lower Left
x -= dxCrd-1; dc.SetPixel(x, y, dwPixel[9]); dc.SetPixel(x+1, y, dwPixel[10]); dc.SetPixel(x, y-1, dwPixel[11]); }
/******************************************************************************
GetRect()
sets and returns a rect that covers the card
******************************************************************************/
CRect &card::GetRect(CRect &rect) { rect.SetRect(loc.x, loc.y, loc.x+dxCrd, loc.y+dyCrd); return(rect); }
|