/***************************************************************************/ /** Microsoft Windows **/ /** Copyright(c) Microsoft Corp., 1991, 1992 **/ /***************************************************************************/ /**************************************************************************** player.cpp Aug 92, JimH May 93, JimH chico port Methods for player objects ****************************************************************************/ #include "hearts.h" #include "main.h" // friendly access #include "resource.h" #include "debug.h" #include // qsort() prototype #include extern "C" { // compare routine for qsort() int __cdecl CompareCards(card *c1, card *c2); } /**************************************************************************** player::player ****************************************************************************/ player::player(int n, int pos) : id(n), position(pos) { // Set up font BYTE charset = 0; int fontsize = 0; CString fontname, charsetstr, fontsizestr; fontname.LoadString(IDS_FONTFACE); charsetstr.LoadString(IDS_CHARSET); fontsizestr.LoadString(IDS_FONTSIZE); charset = (BYTE)_ttoi(charsetstr); fontsize = _ttoi(fontsizestr); font.CreateFont(fontsize, 0, 0, 0, 700, 0, 0, 0, charset, 0, 0, 0, 0, fontname); CRect rect = CMainWindow::m_TableRect; POINT centre; const int offset = 30; // offset from centre for playloc mode = STARTING; centre.x = (rect.right / 2) - (card::dxCrd / 2); centre.y = (rect.bottom / 2) - (card::dyCrd / 2); playloc = centre; score = 0; CClientDC dc(::pMainWnd); TEXTMETRIC tm; dc.GetTextMetrics(&tm); int nTextHeight = tm.tmHeight + tm.tmExternalLeading; switch (position) { case 0: loc.x = (rect.right - (12 * HORZSPACING + card::dxCrd)) / 2; loc.y = rect.bottom - card::dyCrd - IDGE; dx = HORZSPACING; dy = 0; playloc.x -= 5; playloc.y += offset; dotloc.x = loc.x + (HORZSPACING / 2); dotloc.y = loc.y - IDGE; homeloc.x = playloc.x; homeloc.y = rect.bottom + card::dyCrd; nameloc.x = loc.x + card::dxCrd + IDGE; nameloc.y = rect.bottom - nTextHeight - IDGE; break; case 1: loc.x = 3 * IDGE; loc.y = (rect.bottom - (12 * VERTSPACING + card::dyCrd)) / 2; dx = 0; dy = VERTSPACING; playloc.x -= offset; playloc.y -= 5; dotloc.x = loc.x + card::dxCrd + IDGE; dotloc.y = loc.y + (VERTSPACING / 2); homeloc.x = -card::dxCrd; homeloc.y = playloc.y; nameloc.x = loc.x + 2; nameloc.y = loc.y - nTextHeight; break; case 2: loc.x = ((rect.right - (12 * HORZSPACING + card::dxCrd)) / 2) + (12 * HORZSPACING); loc.y = IDGE; dx = -HORZSPACING; dy = 0; playloc.x += 5; playloc.y -= offset; dotloc.x = loc.x + card::dxCrd - (HORZSPACING / 2); dotloc.y = loc.y + card::dyCrd + IDGE; homeloc.x = playloc.x; homeloc.y = -card::dyCrd; nameloc.x = ((rect.right - (12 * HORZSPACING + card::dxCrd)) / 2) + (12 * HORZSPACING) + card::dxCrd + IDGE; nameloc.y = IDGE; break; case 3: loc.x = rect.right - (card::dxCrd + (3 * IDGE)); loc.y = ((rect.bottom - (12 * VERTSPACING + card::dyCrd)) / 2) + (12 * VERTSPACING); dx = 0; dy = -VERTSPACING; playloc.x += offset; playloc.y += 5; dotloc.x = loc.x - IDGE; dotloc.y = loc.y + card::dyCrd - (VERTSPACING / 2); homeloc.x = rect.right; homeloc.y = playloc.y; nameloc.x = ((rect.right - (12 * HORZSPACING + card::dxCrd)) / 2) - IDGE - 2; nameloc.y = ((rect.bottom - (12 * VERTSPACING + card::dyCrd)) / 2) + (12 * VERTSPACING) + card::dyCrd; break; } ResetLoc(); } /**************************************************************************** player::ResetLoc This routine puts cards in locations based on their slot number. It is used to initialize their x,y locs, or after cards have been sorted. ****************************************************************************/ void player::ResetLoc() { int x = loc.x; int y = loc.y; for (SLOT s = 0; s < MAXSLOT; s++) { if (cd[s].IsInHand()) cd[s].SetLoc(x, y); x += dx; y += dy; } } /**************************************************************************** player::Sort ****************************************************************************/ void player::Sort() { qsort( (void *)cd, MAXSLOT, sizeof(card), (int (__cdecl *)(const void *, const void *))CompareCards ); ResetLoc(); } /**************************************************************************** CompareCards This is the compare function for player::Sort. Aces are high, cards not in hand sort high, and order of suits is clubs, diamonds, spades, hearts (alternating colours) ****************************************************************************/ int __cdecl CompareCards(card *c1, card *c2) { int v1 = c1->Value2(); int v2 = c2->Value2(); int s1 = c1->Suit(); int s2 = c2->Suit(); if (!(c1->IsInHand())) v1 = EMPTY; if (!(c2->IsInHand())) v2 = EMPTY; if (v1 == EMPTY || v2 == EMPTY) { if (v1 == v2) // they're both EMPTY return 0; else if (v1 == EMPTY) return 1; else return -1; } if (s1 != s2) // different suits? { if (s1 == HEARTS && s2 == SPADES) // these two suits reversed return 1; else if (s1 == SPADES && s2 == HEARTS) return -1; else return (s1 - s2); } return (v1 - v2); } /**************************************************************************** player::GetSlot converts a card id to a slot number ****************************************************************************/ SLOT player::GetSlot(int id) { SLOT s = EMPTY; for (int num = 0; num < MAXSLOT; num++) { if (GetID(num) == id) { s = num; break; } } ASSERT(s != EMPTY); return s; } /**************************************************************************** player::GetCardLoc Loc gets location of upper-left corner of specified card slot. Returns true if slot s is valid. ****************************************************************************/ BOOL player::GetCardLoc(SLOT s, POINT& loc) { if (!cd[s].IsValid()) return FALSE; loc.x = cd[s].GetX(); loc.y = cd[s].GetY(); return TRUE; } /**************************************************************************** player::GetCoverRect returns a rect that covers all cards in hand ****************************************************************************/ CRect &player::GetCoverRect(CRect& rect) { rect.left = (dx < 0 ? loc.x + 12 * dx : loc.x); rect.right = rect.left + (dx != 0 ? card::dxCrd + 12 * abs(dx) : card::dxCrd); rect.top = (dy < 0 ? loc.y + 12 * dy : loc.y); rect.bottom = rect.top + (dy != 0 ? card::dyCrd + 12 * abs(dy) : card::dyCrd); // expand rect to include selection indicators if (position == 0) rect.top -= POPSPACING; else if (position == 1) rect.right += 2 * IDGE; else if (position == 2) rect.bottom += 2 * IDGE; else rect.left -= 2 * IDGE; return rect; } /**************************************************************************** rect::GetMarkingRect returns a rect that covers all selection marking dots ****************************************************************************/ CRect &player::GetMarkingRect(CRect& rect) { rect.left = (dx < 0 ? dotloc.x + (12 * dx) : dotloc.x); rect.right = (dx < 0 ? dotloc.x + 2 : dotloc.x + (12 * dx) + 2); rect.top = (dy < 0 ? dotloc.y + (12 * dy) : dotloc.y); rect.bottom = (dy < 0 ? dotloc.y + 2 : dotloc.y + (12 * dy) + 2); return rect; } /**************************************************************************** player::Draw Draws all the cards belonging to this player. bCheating defaults to FALSE, and SLOT defaults to ALL. ****************************************************************************/ void player::Draw(CDC &dc, BOOL bCheating, SLOT slot) { DisplayName(dc); SLOT start = (slot == ALL ? 0 : slot); SLOT stop = (slot == ALL ? MAXSLOT : slot+1); SLOT playedslot = EMPTY; // must draw cards in play last for EGA for (SLOT s = start; s < stop; s++) { if (cd[s].IsPlayed()) playedslot = s; // save and draw later else if (bCheating) cd[s].Draw(dc); else cd[s].Draw(dc, FACEDOWN); } if (playedslot != EMPTY) cd[playedslot].Draw(dc); } void player::DisplayName(CDC &dc) { CFont *oldfont = dc.SelectObject(&font); dc.SetBkColor(::pMainWnd->GetBkColor()); dc.TextOut(nameloc.x, nameloc.y, name, name.GetLength()); dc.SelectObject(oldfont); } void player::SetName(CString& newname, CDC& dc) { static RECT rect; // client rect of main window static BOOL bFirst = TRUE; // first time through this routine? if (bFirst) ::pMainWnd->GetClientRect(&rect); if (rect.right > 100) // app started non-iconic bFirst = FALSE; name = newname; CFont *oldfont = dc.SelectObject(&font); if (position == 0) { CSize size = dc.GetTextExtent(name, name.GetLength()); nameloc.x = ((rect.right - (12 * HORZSPACING + card::dxCrd)) / 2) - IDGE - size.cx; } else if (position == 3) { CSize size = dc.GetTextExtent(name, name.GetLength()); nameloc.x = rect.right - size.cx - (3*IDGE) - 2; } dc.SelectObject(oldfont); } /**************************************************************************** player::ReturnSelectedCards player::ReceiveSelectedCards When cards are passed from player to player, the first function is used to return the selected cards. The second is used to pass another player's selections in. ****************************************************************************/ void player::ReturnSelectedCards(int c[]) { c[0] = EMPTY; // default c[1] = EMPTY; c[2] = EMPTY; if (mode == STARTING || mode == SELECTING) return; for (int i = 0, j = 0; j < 3; i++) { if (cd[i].IsSelected()) c[j++] = cd[i].ID(); if (i >= MAXSLOT) { ASSERT(i < MAXSLOT); } } } void player::ReceiveSelectedCards(int c[]) { for (int i = 0, j = 0; j < 3; i++) { if (cd[i].IsSelected()) { cd[i].SetID(c[j++]); cd[i].Select(FALSE); } ASSERT(i < MAXSLOT); } SetMode(WAITING); } /**************************************************************************** player::MarkSelectedCards This virtual function puts white dots beside selected cards for all non-local_human players. ****************************************************************************/ void player::MarkSelectedCards(CDC &dc) { COLORREF color = RGB(255, 255, 255); for (int s = 0; s < MAXSLOT; s++) { if (cd[s].IsSelected()) { int x = dotloc.x + (s * dx); int y = dotloc.y + (s * dy); dc.SetPixel(x, y, color); dc.SetPixel(x+1, y, color); dc.SetPixel(x, y+1, color); dc.SetPixel(x+1, y+1, color); } } } /**************************************************************************** player::GlideToCentre This function takes a selected card and glides it to its play location. The other normal cards (cards still in hand) are each checked to see if the card is to be moved. If so, their image is drawn into the background bitmap. ****************************************************************************/ void player::GlideToCentre(SLOT s, BOOL bFaceup) { CRect rectCard, rectSrc, rectDummy; CClientDC dc(::pMainWnd); #ifdef USE_MIRRORING SetLayout(dc.m_hDC, 0); SetLayout(dc.m_hAttribDC, 0); #endif CDC *memdc = new CDC; memdc->CreateCompatibleDC(&dc); memdc->SelectObject(&card::m_bmBgnd); memdc->SelectObject(&CMainWindow::m_BgndBrush); memdc->PatBlt(0, 0, card::dxCrd, card::dyCrd, PATCOPY); cd[s].GetRect(rectCard); for (SLOT i = 0; i < MAXSLOT; i++) { if (cd[i].IsNormal() && (i != s)) { cd[i].GetRect(rectSrc); if (IntersectRect(&rectDummy, &rectSrc, &rectCard)) { cd[i].Draw(*memdc, // CDC rectSrc.left-rectCard.left, // x rectSrc.top-rectCard.top, // y bFaceup ? FACEUP : FACEDOWN, // mode FALSE); // don't update loc } } } delete memdc; // must delete before Glide() called cd[s].CleanDraw(dc); cd[s].Glide(dc, playloc.x, playloc.y); // glide to play location cd[s].Play(); // mark card as played SetMode(WAITING); } /**************************************************************************** player::ResetCardsWon cardswon[] keeps track of point cards won this hand. This function clears this data for a new hand. ****************************************************************************/ void player::ResetCardsWon() { for (int i = 0; i < MAXCARDSWON; i++) cardswon[i] = EMPTY; numcardswon = 0; } /**************************************************************************** player::WinCard Cards won in tricks are passed in. If they are point cards (hearts or queen on spades) the id is saved in cardswon[]. ****************************************************************************/ void player::WinCard(CDC &dc, card *c) { if ((c->IsHeart()) || (c->ID() == BLACKLADY)) cardswon[numcardswon++] = c->ID(); RegEntry Reg(szRegPath); DWORD dwSpeed = Reg.GetNumber(regvalSpeed, IDC_NORMAL); int oldstep = c->SetStepSize(dwSpeed == IDC_SLOW ? 5 : 30); c->Glide(dc, homeloc.x, homeloc.y); c->SetStepSize(oldstep); } /**************************************************************************** player::EvaluateScore Points stored in cardswon[] are added to players total score. ****************************************************************************/ int player::EvaluateScore(BOOL &bMoonShot) { for (int i = 0; i < MAXCARDSWON; i++) { if (cardswon[i] == BLACKLADY) score += 13; else if (cardswon[i] != EMPTY) score++; } if (cardswon[MAXCARDSWON-1] != EMPTY) // if player got ALL point cards bMoonShot = TRUE; else bMoonShot = FALSE; return score; } /**************************************************************************** player::DisplayHeartsWon ****************************************************************************/ void player::DisplayHeartsWon(CDC &dc) { card c; int x = loc.x; int y = loc.y; x += ((MAXCARDSWON - numcardswon) / 2) * dx; y += ((MAXCARDSWON - numcardswon) / 2) * dy; for (int i = 0; i < numcardswon; i++) { c.SetID(cardswon[i]); c.SetLoc(x, y); c.Draw(dc); x += dx; y += dy; } DisplayName(dc); }