windows-nt/Source/XPSP1/NT/shell/osshell/games/mshearts/card.cpp

427 lines
11 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/***************************************************************************/
/** 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);
}