#include "sol.h" VSZASSERT /* Klondike init stuff */ LRESULT KlondGmProc(GM *pgm, INT msgg, WPARAM wp1, LPARAM wp2); LRESULT DeckColProc(COL *pcol, INT msgc, WPARAM wp1, LPARAM wp2); LRESULT DiscardColProc(COL *pcol, INT msgc, WPARAM wp1, LPARAM wp2); LRESULT TabColProc(COL *pcol, INT msgc, WPARAM wp1, LPARAM wp2); LRESULT FoundColProc(COL *pcol, INT msgc, WPARAM wp1, LPARAM wp2); // Imported from Win3.1 BOOL FInitKlondGm() { COLCLS *pcolcls; GM *pgm; DX dxCrdOffUp; DX dyCrdOffUp; DX dyCrdOffDn; int icol; int icrdMax; /* KLUDGE to get klondike going */ FreeGm(pgmCur); if((pgm = pgmCur = PAlloc(sizeof(GM)+(13-1)*sizeof(COL *))) == NULL) return fFalse; pgm->lpfnGmProc = KlondGmProc; SendGmMsg(pgm, msggInit, fTrue, 0); pgm->icolMax = 13; pgm->dqsecScore = 10*4; if(!FInitUndo(&pgm->udr)) goto OOM; /* Initialize all the column types but don't position yet */ for(icol = 0; icol < pgm->icolMax; icol++) { switch(icol) { case icolDeck: pcolcls = PcolclsCreate(tclsDeck, (COLCLSCREATEFUNC)DeckColProc, 0, 0, 2, 1, 1, 10); icrdMax = icrdDeckMax; break; case icolDiscard: dxCrdOffUp = dxCrd / 5; pcolcls = PcolclsCreate(tclsDiscard, (COLCLSCREATEFUNC)DiscardColProc, dxCrdOffUp, 1, 2, 1, 1, 10); icrdMax = icrdDiscardMax; break; case icolFoundFirst: pcolcls = PcolclsCreate(tclsFound, (COLCLSCREATEFUNC)FoundColProc, 2, 1, 0, 0, 4, 1); Assert(icol - 1 == icolDiscard); icrdMax = icrdFoundMax; break; case icolTabFirst: Assert(fHalfCards == 1 || fHalfCards == 0); dyCrdOffUp = dyCrd * 4 / 25 - fHalfCards; dyCrdOffDn = dyCrd / 25; pgm->dyDragMax = dyCrd + 12 * dyCrdOffUp; pcolcls = PcolclsCreate(tclsTab, (COLCLSCREATEFUNC)TabColProc, 0, dyCrdOffUp, 0, dyCrdOffDn, 1, 1); icrdMax = icrdTabMax; break; } if(pcolcls == NULL) { OOM: OOM(); FreeGm(pgmCur); Assert(pgmCur == NULL); return fFalse; } if((pgm->rgpcol[icol] = PcolCreate(pcolcls, 0, 0, 0, 0, icrdMax)) == NULL) { FreeP(pcolcls); goto OOM; } pgm->icolMac++; } /* Return without positioning the cards. This will be done at * WM_SIZE message time. */ return TRUE; } /* PositionCols * Positions the card columns. Note that this has been revised to * allow card positioning at times other than at the start of the * game. */ BOOL PositionCols(void) { DX dxMarg; DY dyMarg; DX dx; X xLeft; X xRight; Y yTop; Y yBot; int icol; DY dyCrdOffUp; DY dyCrdOffDn; COL *pcol; GM *pgm; WORD i; /* The game we're using is always the current one */ pgm = pgmCur; /* Before doing the column classes, replace all card X coordinates with * offsets from the column class. */ for (icol = 0 ; icol < 13 ; ++icol) { /* Get a pointer to this COL structure */ pcol = pgm->rgpcol[icol]; /* Loop through all the cards in this column */ for (i = 0 ; i < pcol->icrdMax ; ++i) pcol->rgcrd[i].pt.x -= pcol->rc.xLeft; } /* Set the card margins. Note that xCardMargin is computed in SOL.C * at the time the original window is created and is changed on * WM_SIZE messages. */ dxMarg = xCardMargin; dyMarg = MulDiv(dyCrd, 5, 100); /* Loop through all column types */ for(icol = 0 ; icol < 13 ; icol++) { switch(icol) { case icolDeck: xLeft = dxMarg; yTop = dyMarg; xRight = xLeft + dxCrd + icrdDeckMax / 10 * 2; yBot = yTop + dyCrd + icrdDeckMax / 10; dx = 0; break; case icolDiscard: xLeft += dxMarg + dxCrd; xRight = xLeft + 7 * dxCrd / 5 + icrdDiscardMax / 10 * 2; break; case icolFoundFirst: xLeft = 4 * dxMarg + 3 * dxCrd; xRight = xLeft + dxCrd + icrdFoundMax / 4 * 2; dx = dxMarg + dxCrd; break; case icolTabFirst: dyCrdOffUp = dyCrd * 4 / 25 - fHalfCards; dyCrdOffDn = dyCrd / 25; xLeft = dxMarg; xRight = xLeft + dxCrd; yTop = yBot + 1; yBot = yTop + 12 * dyCrdOffUp + dyCrd + 6 * dyCrdOffDn; break; } /* Set this information into the structure */ pcol = pgm->rgpcol[icol]; pcol->rc.xLeft = xLeft; pcol->rc.yTop = yTop; pcol->rc.xRight = xRight; pcol->rc.yBot = yBot; /* Prepare for the next loop */ xLeft += dx; xRight += dx; } /* Now that the column offsets are correct, move the cards back */ for (icol = 0 ; icol < 13 ; ++icol) { /* Get a pointer to this COL structure */ pcol = pgm->rgpcol[icol]; /* Loop through all the cards in this column */ for (i = 0 ; i < pcol->icrdMax ; ++i) pcol->rgcrd[i].pt.x += pcol->rc.xLeft; } return TRUE; } /* TABLEAU col Proc stuff */ BOOL FTabValidMove(COL *pcolDest, COL *pcolSrc) { RA raSrc, raDest; SU suSrc, suDest; INT icrdSel; CD cd; Assert(pcolSrc->pmove != NULL); icrdSel = pcolSrc->pmove->icrdSel; Assert(icrdSel < pcolSrc->icrdMac); Assert(pcolSrc->icrdMac > 0); cd = pcolSrc->rgcrd[icrdSel].cd; raSrc = RaFromCd(cd); suSrc = SuFromCd(cd); if(raSrc == raKing) return (pcolDest->icrdMac == 0); if(pcolDest->icrdMac == 0) return fFalse; if(!pcolDest->rgcrd[pcolDest->icrdMac-1].fUp) return fFalse; cd = pcolDest->rgcrd[pcolDest->icrdMac-1].cd; raDest = RaFromCd(cd); suDest = SuFromCd(cd); /* invalid moves */ Assert((suClub ^ suSpade) == 0x03); Assert((suHeart ^ suDiamond) == 0x03); /* valid moves */ Assert((suClub ^ suDiamond) < 0x03); Assert((suClub ^ suHeart) < 0x03); Assert((suSpade ^ suDiamond) < 0x03); Assert((suSpade ^ suHeart) < 0x03); return (((suSrc ^ suDest) < 0x03) && suSrc != suDest && raSrc+1 == raDest); } INT TabHit(COL *pcol, PT *ppt, INT icrdMin) { CRD *pcrd; if(pcol->icrdMac > 0 && !(pcrd=&pcol->rgcrd[pcol->icrdMac-1])->fUp && FPtInCrd(pcrd, *ppt)) { SendGmMsg(pgmCur, msggKillUndo, 0, 0); SendColMsg(pcol, msgcSel, icrdEnd, 1); SendColMsg(pcol, msgcFlip, fTrue, 0); SendColMsg(pcol, msgcComputeCrdPos, pcol->icrdMac-1, fFalse); SendColMsg(pcol, msgcRender, pcol->icrdMac-1, icrdToEnd); SendGmMsg(pgmCur, msggChangeScore, csKlondTabFlip, 0); SendColMsg(pcol, msgcEndSel, fFalse, 0); /* should I return this? */ return icrdNil; } return DefColProc(pcol, msgcHit, (INT_PTR) ppt, icrdMin); } BOOL TabDiscardDblClk(COL *pcol, PT *ppt, INT icol) { CRD *pcrd; INT icolDest; COL *pcolDest; BOOL fResult; fResult = fFalse; if(pcol->icrdMac > 0 && (pcrd=&pcol->rgcrd[pcol->icrdMac-1])->fUp && FPtInCrd(pcrd, *ppt)) { if(pcol->pmove == NULL) SendColMsg(pcol, msgcSel, icrdEnd, ccrdToEnd); Assert(pcol->pmove != NULL); for(icolDest = icolFoundFirst; icolDest < icolFoundFirst+ccolFound; icolDest++) { pcolDest = pgmCur->rgpcol[icolDest]; if(SendColMsg(pcolDest, msgcValidMove, (INT_PTR)pcol, 0)) { SendGmMsg(pgmCur, msggSaveUndo, icolDest, icol); fResult = SendColMsg(pcolDest, msgcMove, (INT_PTR) pcol, icrdToEnd) && (fOutlineDrag || SendColMsg(pcol, msgcRender, pcol->icrdMac-1, icrdToEnd)) && SendGmMsg(pgmCur, msggScore, (INT_PTR) pcolDest, (INT_PTR) pcol); if(SendGmMsg(pgmCur, msggIsWinner, 0, 0)) SendGmMsg(pgmCur, msggWinner, 0, 0); goto Return; } } SendColMsg(pcol, msgcEndSel, fFalse, 0); } Return: return fResult; } LRESULT TabColProc(COL *pcol, INT msgc, WPARAM wp1, LPARAM wp2) { switch(msgc) { case msgcHit: /* should this go in DefProc? */ return TabHit(pcol, (PT *)wp1, (INT)wp2); case msgcDblClk: return TabDiscardDblClk(pcol, (PT *)wp1, (INT)wp2); case msgcValidMove: return FTabValidMove(pcol, (COL *) wp1); } return DefColProc(pcol, msgc, wp1, wp2); } BOOL FFoundRender(COL *pcol, INT icrdFirst, INT icrdLast) { #define dxFoundDn 2 #define dyFoundDn 1 if(pcol->icrdMac == 0 || icrdLast == 0) { if(!FGetHdc()) return fFalse; DrawCardExt((PT *)(&pcol->rc.xLeft), 0, GHOST); DrawBackExcl(pcol, (PT *) &pcol->rc); ReleaseHdc(); return fTrue; } else return DefColProc(pcol, msgcRender, icrdFirst, icrdLast); } BOOL FFoundValidMove(COL *pcolDest, COL *pcolSrc) { RA raSrc; SU suSrc; INT icrdSel; Assert(pcolSrc->pmove != NULL); icrdSel = pcolSrc->pmove->icrdSel; Assert(icrdSel < pcolSrc->icrdMac); Assert(pcolSrc->icrdMac > 0); if(pcolSrc->pmove->ccrdSel != 1) return fFalse; raSrc = RaFromCd(pcolSrc->rgcrd[icrdSel].cd); suSrc = SuFromCd(pcolSrc->rgcrd[icrdSel].cd); if(pcolDest->icrdMac == 0) return(raSrc == raAce); return (raSrc == RaFromCd(pcolDest->rgcrd[pcolDest->icrdMac-1].cd)+1 && suSrc == SuFromCd(pcolDest->rgcrd[pcolDest->icrdMac-1].cd)); } /* Foundation stuff */ LRESULT FoundColProc(COL *pcol, INT msgc, WPARAM wp1, LPARAM wp2) { switch(msgc) { case msgcValidMove: return FFoundValidMove(pcol, (COL *) wp1); case msgcRender: return FFoundRender(pcol, (INT)wp1, (INT)wp2); } return DefColProc(pcol, msgc, wp1, wp2); } /* DeckStuff */ BOOL DeckInit(COL *pcol) { CRD *pcrd; INT icrd; Assert(pcol->icrdMax == icrdDeckMax); for(icrd = 0; icrd < icrdDeckMax; icrd++) { pcrd = &pcol->rgcrd[icrd]; pcrd->cd = (unsigned short)icrd; pcrd->pt = *(PT *)&pcol->rc; pcrd->fUp = fFalse; } pcol->icrdMac = icrdDeckMax; SendColMsg(pcol, msgcShuffle, 0, 0); SendColMsg(pcol, msgcComputeCrdPos, 0, fFalse); return fTrue; } INT DeckHit(COL *pcol, PT *ppt, INT icrdMin) { RC rc; INT ccrd; if(pcol->icrdMac == 0) { CrdRcFromPt((PT *) &pcol->rc, &rc); if(PtInRect((LPRECT) &rc, *(POINT *)ppt)) return icrdEmpty; else return icrdNil; } else if(!FPtInCrd(&pcol->rgcrd[pcol->icrdMac-1], *ppt)) return icrdNil; ccrd = ((GetKeyState(VK_SHIFT) & GetKeyState(VK_CONTROL) & GetKeyState(VK_MENU)) < 0) ? 1 : pgmCur->ccrdDeal; move.icrdSel = WMax(pcol->icrdMac-ccrd, 0); move.ccrdSel = pcol->icrdMac - move.icrdSel; Assert(pcol->pmove == NULL); pcol->pmove = &move; return move.icrdSel; } BOOL FDeckRender(COL *pcol, INT icrdFirst, INT icrdLast) { INT mode; BOOL f; PT pt; /* to avoid redrawing the deck multiple times during dealing */ if(!pgmCur->fDealt && pcol->icrdMac%10 != 9) return fTrue; if(!FGetHdc()) return fFalse; if(pcol->icrdMac == 0) { mode = (smd == smdVegas && pgmCur->irep == ccrdDeal-1) ? DECKX : DECKO; DrawCardExt((PT *) &pcol->rc, 0, mode); DrawBackExcl(pcol, (PT *) &pcol->rc); f = fTrue; } else { f = DefColProc(pcol, msgcRender, icrdFirst, icrdLast); if((icrdLast == pcol->icrdMac || icrdLast == icrdToEnd) && !fHalfCards) { pt.x = pcol->rgcrd[pcol->icrdMac-1].pt.x+dxCrd-1; pt.y = pcol->rgcrd[pcol->icrdMac-1].pt.y+dyCrd-1; SetPixel(hdcCur, pt.x-xOrgCur, pt.y-yOrgCur, rgbTable); SetPixel(hdcCur, pt.x-1-xOrgCur, pt.y-yOrgCur, rgbTable); SetPixel(hdcCur, pt.x-xOrgCur, pt.y-1-yOrgCur, rgbTable); } } ReleaseHdc(); return f; } VOID DrawAnimate(INT cd, PT *ppt, INT iani) { if(!FGetHdc()) return; cdtAnimate(hdcCur, cd, ppt->x, ppt->y, iani); ReleaseHdc(); } BOOL DeckAnimate(COL *pcol, INT iqsec) { INT iani; PT pt; // we removed the older card decks that required Animation. The new // card deck doesn't involve any animation. #ifdef UNUSEDCODE if(pcol->icrdMac > 0 && !fHalfCards) { pt = pcol->rgcrd[pcol->icrdMac-1].pt; switch(modeFaceDown) { 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; } } #endif return fTrue; } LRESULT DeckColProc(COL *pcol, INT msgc, WPARAM wp1, LPARAM wp2) { switch(msgc) { case msgcInit: return DeckInit(pcol); case msgcValidMove: case msgcDrawOutline: return fFalse; case msgcValidMovePt: return icrdNil; case msgcHit: return DeckHit(pcol, (PT *) wp1, (INT)wp2); case msgcRender: return FDeckRender(pcol, (INT) wp1, (INT) wp2); case msgcValidKbdColSel: return !wp1; case msgcValidKbdCrdSel: return pcol->icrdMac == 0 || wp1 == (WPARAM) pcol->icrdMac-1; case msgcAnimate: return DeckAnimate(pcol, (INT)wp1); } return DefColProc(pcol, msgc, wp1, wp2); } BOOL DiscardRemove(COL *pcol, COL *pcolDest, LPARAM wp2) { return DefColProc(pcol, msgcRemove, (INT_PTR) pcolDest, wp2); } BOOL DiscardMove(COL *pcolDest, COL *pcolSrc, INT icrd) { BOOL fResult; SendColMsg(pcolDest, msgcComputeCrdPos, WMax(0, pcolDest->icrdMac-3), fTrue); /* YUCK: Default ComputeCrdPos doesn't quite work for discard because up cards are handled specially for Discard piles. To keep code size down we have this global hack variable which DefComputeCrdPos uses. */ fMegaDiscardHack = fTrue; fResult = DefColProc(pcolDest, msgcMove, (INT_PTR) pcolSrc, icrd); fMegaDiscardHack = fFalse; return fResult; } INT DiscardHit(COL *pcol, PT *ppt, INT icrdMin) { return DefColProc(pcol, msgcHit, (INT_PTR) ppt, WMax(0, pcol->icrdMac-1)); } BOOL DiscardRender(COL *pcol, INT icrdFirst, INT icrdLast) { PT pt; INT icrd; COLCLS *pcolcls; if(DefColProc(pcol, msgcRender, icrdFirst, icrdLast)) { if(FGetHdc()) { pcolcls = pcol->pcolcls; for(icrd = pcol->icrdMac-1; icrd >= 0 && icrd >= pcol->icrdMac-2; icrd--) { pt = pcol->rgcrd[icrd].pt; /* 3 is a kludge value here */ DrawBackground(pt.x+dxCrd-pcolcls->dxUp, pt.y-pcolcls->dyUp*3, pt.x+dxCrd, pt.y); } ReleaseHdc(); } return fTrue; } return fFalse; } /* Discard Stuff */ LRESULT DiscardColProc(COL *pcol, INT msgc, WPARAM wp1, LPARAM wp2) { switch(msgc) { case msgcDblClk: return TabDiscardDblClk(pcol, (PT *)wp1, (INT)wp2); case msgcHit: return DiscardHit(pcol, (PT *)wp1, (INT)wp2); case msgcMove: return DiscardMove(pcol, (COL *) wp1, (INT)wp2); case msgcRemove: return DiscardRemove(pcol, (COL *) wp1, wp2); case msgcValidMovePt: return icrdNil; case msgcValidKbdColSel: return !wp1; case msgcRender: return DiscardRender(pcol, (INT)wp1, (INT)wp2); case msgcValidKbdCrdSel: return pcol->icrdMac == 0 || wp1 == (WPARAM) pcol->icrdMac-1; } return DefColProc(pcol, msgc, wp1, wp2); } /* GAME stuff */ BOOL KlondDeal(GM *pgm, BOOL fZeroScore) { INT icrdSel; INT icol; INT irw; COL *pcolDeck; VOID StatString(); if(!FGetHdc()) { OOM(); return fFalse; } EraseScreen(); for(icol = 0; icol < pgm->icolMac; icol++) SendColMsg(pgm->rgpcol[icol], msgcClearCol, 0, 0); pcolDeck = pgm->rgpcol[icolDeck]; SendColMsg(pcolDeck, msgcInit, 0, 0); SendGmMsg(pgm, msggKillUndo, 0, 0); SendGmMsg(pgm, msggInit, !(smd == smdVegas && fKeepScore) || fZeroScore, 0); StatString(idsNil); pgm->fDealt = fTrue; SendGmMsg(pgm, msggChangeScore, csKlondDeal, 0); SendColMsg(pcolDeck, msgcRender, 0, icrdToEnd); pgm->fDealt = fFalse; for(icol = icolFoundFirst; icol < icolFoundFirst+ccolFound; icol++) SendColMsg(pgm->rgpcol[icol], msgcRender, 0, icrdToEnd); // BabakJ: What the %@!&$* is this?! irw is always less than irw + ccolTab!!! // Note: ccolTab if #ifdef'ed as 7 // for(irw = 0; irw < irw+ccolTab; irw++) for(irw = 0; irw < ccolTab; irw++) for(icol = irw; icol < ccolTab; icol++) { icrdSel = SendColMsg(pcolDeck, msgcSel, icrdEnd, 0); if(icol == irw) SendColMsg(pcolDeck, msgcFlip, fTrue, 0); SendColMsg(pgm->rgpcol[icol+icolTabFirst], msgcMove, (INT_PTR) pcolDeck, icrdToEnd); SendColMsg(pcolDeck, msgcRender, icrdSel-1, icrdToEnd); } NewKbdColAbs(pgm, 0); pgm->fDealt = fTrue; ReleaseHdc(); return fTrue; } BOOL KlondMouseDown(GM *pgm, PT *ppt) { INT icrdSel; INT icrd; COL *pcolDeck, *pcolDiscard; /* Kbd sel already in effect */ if(FSelOfGm(pgm) || !pgm->fDealt) return fFalse; /* place the next cards on discard pile */ if((icrd = SendColMsg(pgm->rgpcol[icolDeck], msgcHit, (INT_PTR) ppt, 0)) != icrdNil) { pgm->fInput = fTrue; pcolDeck = pgm->rgpcol[icolDeck]; pcolDiscard = pgm->rgpcol[icolDiscard]; if(icrd == icrdEmpty) { /* repeat */ if(SendColMsg(pcolDiscard, msgcNumCards, 0, 0) == 0) { /* both deck and discard are empty */ Assert(pcolDeck->pmove == NULL); return fFalse; } if(smd == smdVegas && pgm->irep == ccrdDeal-1) return fFalse; pgm->irep++; pgm->udr.fEndDeck = TRUE; return SendGmMsg(pgm, msggSaveUndo, icolDiscard, icolDeck) && SendColMsg(pcolDiscard, msgcSel, 0, ccrdToEnd) != icrdNil && SendColMsg(pcolDiscard, msgcFlip, fFalse, 0) && SendColMsg(pcolDiscard, msgcInvert, 0, 0) && SendGmMsg(pgm, msggScore, (INT_PTR) pcolDeck, (INT_PTR) pcolDiscard) && SendColMsg(pcolDeck, msgcMove, (INT_PTR) pcolDiscard, icrdToEnd) && SendColMsg(pcolDiscard, msgcRender, 0, icrdToEnd); } else { icrdSel = pcolDeck->pmove->icrdSel-1; /* deal next cards to discard */ return SendGmMsg(pgm, msggSaveUndo, icolDiscard, icolDeck) && SendColMsg(pcolDeck, msgcFlip, fTrue, 0) && SendColMsg(pcolDeck, msgcInvert, 0, 0) && SendColMsg(pcolDiscard, msgcMove, (INT_PTR)pcolDeck, icrdToEnd) && SendColMsg(pcolDeck, msgcRender, icrdSel, icrdToEnd); } } return DefGmProc(pgm, msggMouseDown, (INT_PTR) ppt, icolDiscard); } BOOL KlondIsWinner(GM *pgm) { INT icol; for(icol = icolFoundFirst; icol < icolFoundFirst+ccolFound; icol++) if(pgm->rgpcol[icol]->icrdMac != icrdFoundMax) return fFalse; return fTrue; } BOOL FAbort() { MSG msg; if (MsgWaitForMultipleObjects(0, NULL, FALSE, 5, QS_ALLINPUT) != WAIT_OBJECT_0) return FALSE; if(PeekMessage(&msg, hwndApp, 0, 0, PM_NOREMOVE)) { switch(msg.message) { case WM_LBUTTONDOWN: case WM_MBUTTONDOWN: case WM_RBUTTONDOWN: case WM_KEYDOWN: case WM_SYSKEYDOWN: case WM_MENUSELECT: case WM_NCLBUTTONDOWN: case WM_NCMBUTTONDOWN: case WM_NCRBUTTONDOWN: return fTrue; } PeekMessage(&msg, hwndApp, 0, 0, PM_REMOVE); TranslateMessage((LPMSG)&msg); DispatchMessage((LPMSG)&msg); } return fFalse; } // Hack for making winning animation faster: // At cascading time we have: KlondWinner -> DrawCardPt ->cdtDrawExt // so we set a flag so cdtDrawExt knows it is cascading and does not need // to round up corners. BOOL fKlondWinner = FALSE; BOOL KlondWinner(GM *pgm) { INT icol; INT icrd; CRD *pcrd; PT pt; PT ptV; INT dxp; INT dyp; RC rcT; INT dsco; TCHAR *pch; TCHAR szBonus[84]; VOID StatString(); UINT cchUsed, cchTmp; fKlondWinner = TRUE; dsco = (INT)SendGmMsg(pgmCur, msggChangeScore, csKlondWin, 0); pgm->udr.fAvail = fFalse; pgm->fDealt = fFalse; pgm->fWon = fTrue; if(smd == smdStandard) { cchUsed = CchString(szBonus, idsBonus, ARRAYSIZE(szBonus)); pch = &szBonus[cchUsed]; cchTmp = CchDecodeInt(pch, dsco); pch += cchTmp; cchUsed += cchTmp; *pch++ = TEXT(' '); cchUsed++; *pch++ = TEXT(' '); cchUsed++; } else { pch = szBonus; cchUsed = 0; } if (cchUsed < ARRAYSIZE(szBonus)) { CchString(pch, idsEndWinner, ARRAYSIZE(szBonus) - cchUsed); } StatStringSz(szBonus); if(!FGetHdc()) goto ByeNoRel; Assert(xOrgCur == 0); Assert(yOrgCur == 0); GetClientRect(hwndApp, (RECT *)&rcT); dxp = rcT.xRight; dyp = rcT.yBot - dyCrd; for(icrd = icrdFoundMax-1; icrd >= 0; icrd--) { for(icol = icolFoundFirst; icol < icolFoundFirst+ccolFound; icol++) { ptV.x = rand() % 110 - 65; /* favor up and to left */ if(abs(ptV.x) < 15) /* kludge so doesn't bounce forever */ ptV.x = -20; ptV.y = rand() % 110 - 75; pt = (pcrd = &pgm->rgpcol[icol]->rgcrd[icrd])->pt; while(pt.x > -dxCrd && pt.x < dxp) { DrawCardPt(pcrd, &pt); pt.x += ptV.x/10; pt.y += ptV.y/10; ptV.y+= 3; if(pt.y > dyp && ptV.y > 0) ptV.y = -(ptV.y*8)/10; if(FAbort()) goto ByeBye; } } } ByeBye: ReleaseHdc(); ByeNoRel: StatString(idsNil); EraseScreen(); fKlondWinner = FALSE; return DefGmProc(pgm, msggWinner, 0, 0); } BOOL KlondForceWin(GM *pgm) { INT icol; CRD *pcrd; COL *pcol; RA ra; SU su; for(icol = 0; icol < pgm->icolMac; icol++) SendColMsg(pgm->rgpcol[icol], msgcClearCol, 0, 0); for(su = suFirst, icol = icolFoundFirst; icol < icolFoundFirst+ccolFound; icol++, su++) { Assert(raFirst == 0); for(ra = raFirst; ra < raMax; ra++) { pcol = pgm->rgpcol[icol]; pcrd = &pcol->rgcrd[ra]; pcrd->cd = Cd(ra, su); pcrd->pt.x = pcol->rc.xLeft; pcrd->pt.y = pcol->rc.yTop; pcrd->fUp = fTrue; } pgm->rgpcol[icol]->icrdMac = icrdFoundMax; } Assert(SendGmMsg(pgm, msggIsWinner, 0, 0)); return (BOOL)SendGmMsg(pgm, msggWinner, 0, 0); } /* Note: assumes is called once a second */ /* if pcolDest == pcolSrc == NULL, then is a timer msg */ BOOL KlondScore(GM *pgm, COL *pcolDest, COL *pcolSrc) { INT cs; INT tclsSrc, tclsDest; if(smd == smdNone) return fTrue; cs = csNil; Assert(FValidCol(pcolSrc)); Assert(FValidCol(pcolDest)); tclsSrc = pcolSrc->pcolcls->tcls; tclsDest = pcolDest->pcolcls->tcls; switch(tclsDest) { default: return fTrue; case tclsDeck: if(tclsSrc == tclsDiscard) cs = csKlondDeckFlip; break; case tclsFound: switch(tclsSrc) { default: return fTrue; case tclsDiscard: case tclsTab: cs = csKlondFound; break; } break; case tclsTab: switch(tclsSrc) { default: return fTrue; case tclsDiscard: cs = csKlondTab; break; case tclsFound: cs = csKlondFoundTab; break; } break; } SendGmMsg(pgm, msggChangeScore, cs, 0); return fTrue; } INT mpcsdscoStd[] = { -2, -20, 10, 5, 5, -15, 0, 0}; INT mpcsdscoVegas[] = {0, 0, 5, 0, 0, -5, -52, 0}; BOOL KlondChangeScore(GM *pgm, INT cs, INT sco) { INT dsco; INT csNew; INT *pmpcsdsco; INT ret; if(cs < 0) return DefGmProc(pgm, msggChangeScore, cs, sco); Assert(FInRange(cs, 0, csKlondMax-1)); switch(smd) { default: Assert(smd == smdNone); return fTrue; case smdVegas: pmpcsdsco = mpcsdscoVegas; break; case smdStandard: pmpcsdsco = mpcsdscoStd; if(cs == csKlondWin && fTimedGame) { #ifdef DEBUG pgm->iqsecScore = WMax(120, pgm->iqsecScore); #endif /* check if timer set properly */ if(pgm->iqsecScore >= 120) dsco = (20000/(pgm->iqsecScore>>2))*(350/10); else dsco = 0; goto DoScore; } if(cs == csKlondDeckFlip) { if(ccrdDeal == 1 && pgm->irep >= 1) { dsco = -100; goto DoScore; } else if(ccrdDeal == 3 && pgm->irep > 3) break; else return fTrue; } break; } dsco = pmpcsdsco[cs]; DoScore: csNew = smd == smdVegas ? csDel : csDelPos; ret = DefGmProc(pgm, msggChangeScore, csNew, dsco); if(cs == csKlondWin) return dsco; else return ret; } BOOL KlondTimer(GM *pgm, INT wp1, INT wp2) { if(fTimedGame && pgm->fDealt && pgm->fInput && !fIconic) { pgm->iqsecScore = WMin(pgm->iqsecScore+1, 0x7ffe); if(pgm->icolSel == icolNil) SendColMsg(pgm->rgpcol[icolDeck], msgcAnimate, pgm->iqsecScore, 0); if(pgm->dqsecScore != 0 && (pgm->iqsecScore)%pgm->dqsecScore == 0) { SendGmMsg(pgm, msggChangeScore, csKlondTime, 0); } else { /* update status bar once as second */ if(~(pgm->iqsecScore & 0x03)) StatUpdate(); return fTrue; } } return fFalse; } BOOL KlondDrawStatus(GM *pgm, RC *prc) { TCHAR *pch; TCHAR sz[80]; RC rc; LONG rgb; BOOL fNegSco; SIZE iSize; extern INT iCurrency; extern TCHAR szCurrency[]; HFONT hFontOld = NULL; HFONT hStatusFont = NULL; // store the old font and replace the status font by MS Shell Dlg // as it supports FE characters as well as euro characters. hStatusFont = CreateFont(-MulDiv(9, GetDeviceCaps(hdcCur, LOGPIXELSY), 72), 0, 0, 0, FW_BOLD, 0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, TEXT("MS Shell Dlg")); if (hStatusFont && hdcCur) hFontOld = SelectObject(hdcCur, hStatusFont); pch = sz; if(fTimedGame) { pch += CchString(pch, idsTime, ARRAYSIZE(sz)); pch += CchDecodeInt(pch, (pgm->iqsecScore>>2)); } #ifdef DEBUG if(!fScreenShots) { *pch++ = TEXT(' '); pch = PszCopy(TEXT("Game # "), pch); pch += CchDecodeInt(pch, igmCur); } #endif if(pch != sz) { DrawText(hdcCur, sz, (INT)(pch-sz), (LPRECT) prc, DT_RIGHT|DT_NOCLIP|DT_SINGLELINE); } if(smd != smdNone) { rc = *prc; GetTextExtentPoint32(hdcCur, sz, (INT)(pch-sz), &iSize); rc.xRight -= iSize.cx; pch = sz; if(fNegSco = pgm->sco < 0) *pch++ = TEXT('-'); if(smd == smdVegas) { if(!(iCurrency&1)) { pch = PszCopy(szCurrency, pch); if(iCurrency == 2) *pch++ = TEXT(' '); } } pch += CchDecodeInt(pch, fNegSco ? -pgm->sco : pgm->sco); if(smd == smdVegas) { if(iCurrency&1) { if(iCurrency == 3) *pch++ = TEXT(' '); pch = PszCopy(szCurrency, pch); } } *pch++ = TEXT(' '); rgb = SetTextColor(hdcCur, (!fBW && fNegSco) ? RGB(0xff, 0, 0) : RGB(0, 0, 0)); DrawText(hdcCur, sz, (INT)(pch-sz), (LPRECT) &rc, DT_RIGHT|DT_NOCLIP|DT_SINGLELINE); SetTextColor(hdcCur, rgb); GetTextExtentPoint32(hdcCur, sz, (INT)(pch-sz), &iSize); rc.xRight -= iSize.cx; pch = PszCopy(szScore, sz); DrawText(hdcCur, sz, (INT)(pch-sz), (LPRECT) &rc, DT_RIGHT|DT_NOCLIP|DT_SINGLELINE); GetTextExtentPoint32(hdcCur, sz, (INT)(pch-sz), &iSize); rc.xRight -= iSize.cx; rc.xLeft = rc.xRight - 4 * dxChar; PatBlt(hdcCur, rc.xLeft, rc.yTop, rc.xRight-rc.xLeft, rc.yBot-rc.yTop, PATCOPY); } // restore the font if (hFontOld) SelectObject(hdcCur, hFontOld); // close the created font handle if (hStatusFont) DeleteObject(hStatusFont); return fTrue; } LRESULT KlondGmProc(GM *pgm, INT msgg, WPARAM wp1, LPARAM wp2) { switch(msgg) { case msggMouseDblClk: if(DefGmProc(pgm, msggMouseDblClk, wp1, wp2)) return fTrue; /* fall thru so works for deck double clicks */ case msggMouseDown: return KlondMouseDown(pgm, (PT *)wp1); case msggDeal: return KlondDeal(pgm, (BOOL)wp1); case msggIsWinner: return KlondIsWinner(pgm); case msggWinner: return KlondWinner(pgm); case msggForceWin: return KlondForceWin(pgm); case msggScore: return KlondScore(pgm, (COL *) wp1, (COL *)wp2); case msggChangeScore: return KlondChangeScore(pgm, (INT)wp1, (INT)wp2); case msggTimer: return KlondTimer(pgm, (INT)wp1, (INT)wp2); case msggDrawStatus: return KlondDrawStatus(pgm, (RC *) wp1); } return DefGmProc(pgm, msgg, wp1, wp2); }