Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

709 lines
19 KiB

  1. /****************************************************************************
  2. Display.c
  3. June 91, JimH initial code
  4. Oct 91, JimH port to Win32
  5. Contains routines dealing with pixels and card shuffling.
  6. ****************************************************************************/
  7. #include "freecell.h"
  8. #include "freecons.h"
  9. #include <assert.h>
  10. #include <stdlib.h> // rand() prototype
  11. #include <time.h>
  12. // This static data has to do with positions of the cards on the screen.
  13. // See CalcOffsets() below for descriptions. Note that wOffset[TOPROW]
  14. // is the left edge of the leftmose home cell.
  15. static UINT wOffset[MAXCOL]; // left edge of column n (n from 1 to 8)
  16. static UINT wIconOffset; // left edge of icon (btwn home & free)
  17. static UINT wVSpace; // vert space between home and columns
  18. static UINT wUpdateCol, wUpdatePos; // card user chose to transfer FROM
  19. static BOOL bCardRevealed; // right mouse button show a card?
  20. #define BGND (255) // used for cdtDrawExt
  21. #define ICONY ((dyCrd - ICONHEIGHT) / 3)
  22. /****************************************************************************
  23. CalcOffsets
  24. This function determines the locations where cards are drawn.
  25. ****************************************************************************/
  26. VOID CalcOffsets(HWND hWnd)
  27. {
  28. RECT rect;
  29. UINT i;
  30. UINT leftedge;
  31. BOOL bEGAmode = FALSE;
  32. if (GetSystemMetrics(SM_CYSCREEN) <= 350) // EGA
  33. bEGAmode = TRUE;
  34. GetClientRect(hWnd, &rect);
  35. wOffset[TOPROW] = rect.right - (4 * dxCrd); // home cells
  36. leftedge = (rect.right - ((MAXCOL-1) * dxCrd)) / MAXCOL;
  37. for (i = 1; i < MAXCOL; i++)
  38. wOffset[i] = leftedge + (((i-1) * (rect.right-leftedge)) / (MAXCOL-1));
  39. /* place icon half way between free cells and home cells */
  40. wIconOffset = (rect.right-ICONWIDTH) / 2;
  41. if (bEGAmode)
  42. wVSpace = 4;
  43. else
  44. wVSpace = 10;
  45. /* dyTops is the vertical space between stacked cards. To fit the
  46. theoretical maximum, the formula is dyTops = (dyCrd * 9) / 50.
  47. A compromise is used to make the cards easier to see. It is possible,
  48. therefore, that some stacks could get long enough for the bottom
  49. cards no to be visible. The situation for EGA is worse, as cards
  50. are both closer together, and more likely to fall off the bottom.
  51. An alternative is to squish the bitmaps dyCrd = (35 * dyCrd) / 48. */
  52. dyTops = (dyCrd * 9) / 46; // space between tops of cards in columns
  53. if (bEGAmode)
  54. dyTops = (dyTops * 4) / 5;
  55. }
  56. /****************************************************************************
  57. ShuffleDeck
  58. If seed is non-zero, that number is used as rand() seed to shuffle
  59. deck. Otherwise, a seed is generated and presented to the user who
  60. may change it in a dialog box.
  61. ****************************************************************************/
  62. VOID ShuffleDeck(HWND hWnd, UINT_PTR seed)
  63. {
  64. UINT i, j; // generic counters
  65. UINT col, pos;
  66. UINT wLeft = 52; // cards left to be chosen in shuffle
  67. CARD deck[52]; // deck of 52 unique cards
  68. if (seed == 0) // if user must select seed
  69. {
  70. gamenumber = GenerateRandomGameNum();
  71. /* Keep calling GameNumDlg until valid number chosen. */
  72. while (!DialogBox(hInst, TEXT("GameNum"), hWnd, GameNumDlg))
  73. {
  74. }
  75. if (gamenumber == CANCELGAME) // if user chose CANCEL button
  76. return;
  77. }
  78. else
  79. {
  80. gamenumber = (INT) seed;
  81. }
  82. LoadString(hInst, IDS_APPNAME2, bigbuf, BIG);
  83. wsprintf(smallbuf, bigbuf, gamenumber);
  84. SetWindowText(hWnd, smallbuf);
  85. for (col = 0; col < MAXCOL; col++) // clear the deck
  86. {
  87. for (pos = 0; pos < MAXPOS; pos++)
  88. {
  89. card[col][pos] = EMPTY;
  90. }
  91. }
  92. /* shuffle cards */
  93. for (i = 0; i < 52; i++) // put unique card in each deck loc.
  94. {
  95. deck[i] = i;
  96. }
  97. if (gamenumber == -1) // special unwinnable shuffle
  98. {
  99. i = 0;
  100. for (pos = 0; pos < 7; pos++)
  101. {
  102. for (col = 1; col < 5; col++)
  103. {
  104. card[col][pos] = i++;
  105. }
  106. i+= 4;
  107. }
  108. for (pos = 0; pos < 6; pos++)
  109. {
  110. i -= 12;
  111. for (col = 5; col < 9; col++)
  112. {
  113. card[col][pos] = i++;
  114. }
  115. }
  116. }
  117. else if (gamenumber == -2)
  118. {
  119. i = 3;
  120. for (col = 1; col < 5; col++)
  121. {
  122. card[col][0] = i--;
  123. }
  124. i = 51;
  125. for (pos = 1; pos < 7; pos++)
  126. {
  127. for (col = 1; col < 5; col++)
  128. {
  129. card[col][pos] = i--;
  130. }
  131. }
  132. for (pos = 0; pos < 6; pos++)
  133. {
  134. for (col = 5; col < 9; col++)
  135. {
  136. card[col][pos] = i--;
  137. }
  138. }
  139. }
  140. else
  141. {
  142. //
  143. // Caution:
  144. // This shuffle algorithm has been published to people all around. The intention
  145. // was to let people track the games by game numbers. So far all the games between
  146. // 1 and 32767 except one have been proved to have a winning solution. Do not change
  147. // the shuffling algorithm else you will incur the wrath of people who have invested
  148. // a huge amount of time solving these games.
  149. //
  150. // The game number can now be upto a million as the srand takes an integer but the
  151. // the random value generated by rand() is only from 0 to 32767.
  152. //
  153. srand(gamenumber);
  154. for (i = 0; i < 52; i++)
  155. {
  156. j = rand() % wLeft;
  157. wLeft --;
  158. card[(i%8)+1][i/8] = deck[j];
  159. deck[j] = deck[wLeft];
  160. }
  161. }
  162. }
  163. /****************************************************************************
  164. PaintMainWindow
  165. This function is called in response to WM_PAINT.
  166. ****************************************************************************/
  167. VOID PaintMainWindow(HWND hWnd)
  168. {
  169. PAINTSTRUCT ps;
  170. UINT col, pos;
  171. UINT y; // y location of icon
  172. CARD c;
  173. INT mode; // mode to draw card (FACEUP or HILITE)
  174. HCURSOR hCursor; // original cursor
  175. HPEN hOldPen;
  176. BeginPaint(hWnd, &ps);
  177. /* Draw icon with 3D box around it. */
  178. y = ICONY;
  179. hOldPen = SelectObject(ps.hdc, hBrightPen);
  180. MoveToEx(ps.hdc, wIconOffset-3, y + ICONHEIGHT + 1, NULL);
  181. LineTo(ps.hdc, wIconOffset-3, y-3);
  182. LineTo(ps.hdc, wIconOffset+ICONWIDTH + 2, y-3);
  183. SelectObject(ps.hdc, GetStockObject(BLACK_PEN));
  184. MoveToEx(ps.hdc, wIconOffset + ICONWIDTH + 2, y-2, NULL);
  185. LineTo(ps.hdc, wIconOffset + ICONWIDTH + 2, y + ICONHEIGHT + 2);
  186. LineTo(ps.hdc, wIconOffset - 3, y + ICONHEIGHT + 2);
  187. DrawKing(ps.hdc, SAME, TRUE);
  188. hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
  189. ShowCursor(TRUE);
  190. /* Top row first */
  191. for (pos = 0; pos < 8; pos++)
  192. {
  193. mode = FACEUP;
  194. if ((c = card[TOPROW][pos]) == EMPTY)
  195. c = IDGHOST;
  196. else if (wMouseMode == TO && pos == wFromPos && TOPROW == wFromCol)
  197. mode = HILITE;
  198. DrawCard(ps.hdc, TOPROW, pos, c, mode);
  199. }
  200. /* Then, the 8 columns */
  201. for (col = 1; col < MAXCOL; col++)
  202. {
  203. for (pos = 0; pos < MAXPOS; pos++)
  204. {
  205. if ((c = card[col][pos]) == EMPTY)
  206. break;
  207. if (wMouseMode == TO && pos == wFromPos && col == wFromCol)
  208. mode = HILITE;
  209. else
  210. mode = FACEUP;
  211. DrawCard(ps.hdc, col, pos, c, mode);
  212. }
  213. }
  214. if (bWonState)
  215. Payoff(ps.hdc);
  216. ShowCursor(FALSE);
  217. SetCursor(hCursor);
  218. SelectObject(ps.hdc, hOldPen);
  219. EndPaint(hWnd, &ps);
  220. DisplayCardCount(hWnd);
  221. }
  222. /****************************************************************************
  223. DrawCard
  224. This function takes a card value and position (in col and pos),
  225. converts it to x and y, and displays it in the specified mode.
  226. ****************************************************************************/
  227. VOID DrawCard(HDC hDC, UINT col, UINT pos, CARD card, INT mode)
  228. {
  229. UINT x, y;
  230. HDC hMemDC;
  231. HBITMAP hOldBitmap;
  232. Card2Point(col, pos, &x, &y);
  233. if (card == IDGHOST && hBM_Ghost)
  234. {
  235. hMemDC = CreateCompatibleDC(hDC);
  236. if (hMemDC)
  237. {
  238. hOldBitmap = SelectObject(hMemDC, hBM_Ghost);
  239. BitBlt(hDC, x, y, dxCrd, dyCrd, hMemDC, 0, 0, SRCCOPY);
  240. SelectObject(hMemDC, hOldBitmap);
  241. DeleteDC(hMemDC);
  242. }
  243. }
  244. else
  245. cdtDrawExt(hDC, x, y, dxCrd, dyCrd, card, mode, BGND);
  246. }
  247. VOID DrawCardMem(HDC hMemDC, CARD card, INT mode)
  248. {
  249. cdtDrawExt(hMemDC, 0, 0-dyTops, dxCrd, dyCrd, card, mode, BGND);
  250. }
  251. /****************************************************************************
  252. RevealCard
  253. When the user chooses a hidden card by clicking the right mouse button,
  254. this function displays the entire card bitmap.
  255. ****************************************************************************/
  256. VOID RevealCard(HWND hWnd, UINT x, UINT y)
  257. {
  258. UINT col, pos;
  259. HDC hDC;
  260. bCardRevealed = FALSE; // no card revealed yet
  261. if (Point2Card(x, y, &col, &pos))
  262. {
  263. wUpdateCol = col; // save for RestoreColumn()
  264. wUpdatePos = pos;
  265. }
  266. else
  267. return; // not a card
  268. if (col == 0 || pos == (MAXPOS-1))
  269. return;
  270. if (card[col][pos+1] == EMPTY)
  271. return;
  272. hDC = GetDC(hWnd);
  273. DrawCard(hDC, col, pos, card[col][pos], FACEUP);
  274. ReleaseDC(hWnd, hDC);
  275. bCardRevealed = TRUE; // ok, card has been revealed
  276. }
  277. /****************************************************************************
  278. RestoreColumn
  279. After RevealCard has messed up a column by revealing a hidden card,
  280. this routine patches it up again by redisplaying all the cards from
  281. the revealed card down to the bottom of the column. If the bottom card
  282. is selected for a move, it is correctly shown hilighted.
  283. ****************************************************************************/
  284. VOID RestoreColumn(HWND hWnd)
  285. {
  286. HDC hDC;
  287. UINT pos;
  288. UINT lastpos = EMPTY; // last pos in column (for HILITE mode)
  289. INT mode; // HILITE or FACEUP
  290. if (!bCardRevealed)
  291. return;
  292. if (wMouseMode == TO)
  293. lastpos = FindLastPos(wUpdateCol);
  294. hDC = GetDC(hWnd);
  295. mode = FACEUP;
  296. for (pos = (wUpdatePos + 1); pos < MAXPOS; pos++)
  297. {
  298. if (card[wUpdateCol][pos] == EMPTY)
  299. break;
  300. if (wMouseMode == TO && pos == lastpos && wUpdateCol == wFromCol)
  301. mode = HILITE;
  302. DrawCard(hDC, wUpdateCol, pos, card[wUpdateCol][pos], mode);
  303. }
  304. ReleaseDC(hWnd, hDC);
  305. }
  306. /****************************************************************************
  307. Point2Card
  308. Given an x,y location (typically a mouse click) this function returns
  309. the column and position of that card through pointers. The function
  310. return value is TRUE if it found a card, and FALSE otherwise.
  311. ****************************************************************************/
  312. BOOL Point2Card(UINT x, UINT y, UINT *col, UINT *pos)
  313. {
  314. if (y < dyCrd) // TOPROW
  315. {
  316. if (x < (UINT) (4 * dxCrd)) // free cells
  317. {
  318. *col = TOPROW;
  319. *pos = x / dxCrd;
  320. return TRUE;
  321. }
  322. else if (x < wOffset[TOPROW]) // between free cells & home cells
  323. return FALSE;
  324. x -= wOffset[TOPROW];
  325. if (x < (UINT) (4 * dxCrd)) // home cells
  326. {
  327. *col = TOPROW;
  328. *pos = (x / dxCrd) + 4;
  329. return TRUE;
  330. }
  331. else // right of home cells
  332. return FALSE;
  333. }
  334. if (y < (dyCrd + wVSpace)) // above column cards
  335. return FALSE;
  336. if (x < wOffset[1]) // left of column 1
  337. return FALSE;
  338. *col = (x - wOffset[1]) / (wOffset[2] - wOffset[1]);
  339. (*col)++;
  340. if (x > (wOffset[*col] + dxCrd))
  341. return FALSE; // between columns
  342. y -= (dyCrd + wVSpace);
  343. *pos = min((y / dyTops), MAXPOS);
  344. if (card[*col][0] == EMPTY)
  345. return FALSE; // empty column
  346. if (*pos < (MAXPOS-1))
  347. {
  348. if (card[*col][(*pos)+1] != EMPTY) // if partially hidden card...
  349. return TRUE; // we're done
  350. }
  351. while (card[*col][*pos] == EMPTY)
  352. (*pos)--;
  353. if (y > ((*pos * dyTops) + dyCrd))
  354. return FALSE; // below last card in column
  355. else
  356. return TRUE;
  357. }
  358. /****************************************************************************
  359. Card2Point
  360. Given a column and position, this function returns the x and y pixel
  361. location of the upper left hand corner of the card.
  362. ****************************************************************************/
  363. VOID Card2Point(UINT col, UINT pos, UINT *x, UINT *y)
  364. {
  365. assert(col <= MAXCOL);
  366. assert(pos <= MAXPOS);
  367. if (col == TOPROW) // column 0 is really the top row of 8 slots
  368. {
  369. *y = 0;
  370. *x = pos * dxCrd;
  371. if (pos > 3)
  372. *x += wOffset[TOPROW] - (4 * dxCrd);
  373. }
  374. else
  375. {
  376. *x = wOffset[col];
  377. *y = dyCrd + wVSpace + (pos * dyTops);
  378. }
  379. }
  380. /****************************************************************************
  381. DisplayCardCount
  382. This function displays wCardCount (the number of cards not in home cells)
  383. at the right edge of the menu bar. If necessary, the old value is erased.
  384. ****************************************************************************/
  385. VOID DisplayCardCount(HWND hWnd)
  386. {
  387. RECT rect; // client rect
  388. HDC hDC;
  389. TCHAR buffer[25]; // current value in ASCII stored here
  390. TCHAR oldbuffer[25]; // previous value in ASCII
  391. UINT xLoc; // x pixel loction for count
  392. UINT wCount; // temp wCardCount holder
  393. static UINT yLoc = 0; // y pixel location for count
  394. static UINT wOldCount = 0; // previous count value
  395. HFONT hOldFont = NULL;
  396. SIZE size;
  397. if (IsIconic(hWnd)) // don't draw on icon!
  398. return;
  399. hDC = GetWindowDC(hWnd); // get DC for entire window
  400. if (!hDC)
  401. return;
  402. SetBkColor(hDC, GetSysColor(COLOR_MENU));
  403. if (hMenuFont)
  404. hOldFont = SelectObject(hDC, hMenuFont);
  405. wCount = wCardCount;
  406. if (wCount == 0xFFFF) // decremented past 0?
  407. wCount = 0;
  408. LoadString(hInst, IDS_CARDSLEFT, smallbuf, SMALL);
  409. wsprintf(buffer, smallbuf, wCount);
  410. if (yLoc == 0) // needs to be set only once
  411. {
  412. TEXTMETRIC tm;
  413. int offset;
  414. GetTextMetrics(hDC, &tm);
  415. offset = (GetSystemMetrics(SM_CYMENU) - tm.tmHeight) / 2;
  416. yLoc = GetSystemMetrics(SM_CYFRAME) // sizing frame
  417. + GetSystemMetrics(SM_CYCAPTION) // height of caption
  418. + offset;
  419. }
  420. GetClientRect(hWnd, &rect);
  421. GetTextExtentPoint32(hDC, buffer, lstrlen(buffer), &size);
  422. xLoc = rect.right - size.cx;
  423. if (xLoc > xOldLoc) // need to erase old score?
  424. {
  425. SetTextColor(hDC, GetSysColor(COLOR_MENU)); // background colour
  426. wsprintf(oldbuffer, smallbuf, wOldCount);
  427. TextOut(hDC, xOldLoc, yLoc, oldbuffer, lstrlen(buffer));
  428. }
  429. SetTextColor(hDC, GetSysColor(COLOR_MENUTEXT));
  430. TextOut(hDC, xLoc, yLoc, buffer, lstrlen(buffer));
  431. xOldLoc = xLoc;
  432. wOldCount = wCount;
  433. if (hMenuFont)
  434. SelectObject(hDC, hOldFont);
  435. ReleaseDC(hWnd, hDC);
  436. }
  437. /****************************************************************************
  438. Payoff
  439. Draws the Big King when you win the game.
  440. ****************************************************************************/
  441. VOID Payoff(HDC hDC)
  442. {
  443. HDC hMemDC; // bitmap memory DC
  444. HBITMAP hBitmap;
  445. HBITMAP hOldBitmap;
  446. INT xStretch = 320; // stretched size of bitmap
  447. INT yStretch = 320;
  448. if (GetSystemMetrics(SM_CYSCREEN) <= 350) // EGA
  449. {
  450. xStretch = 32 * 8;
  451. yStretch = 32 * 6;
  452. }
  453. DrawKing(hDC, NONE, TRUE);
  454. hMemDC = CreateCompatibleDC(hDC);
  455. if (!hMemDC)
  456. return;
  457. hBitmap = LoadBitmap(hInst, TEXT("KingSmile"));
  458. if (hBitmap)
  459. {
  460. hOldBitmap = SelectObject(hMemDC, hBitmap);
  461. StretchBlt(hDC, 10, dyCrd + 10, xStretch, yStretch, hMemDC, 0, 0,
  462. BMWIDTH, BMHEIGHT, SRCCOPY);
  463. SelectObject(hMemDC, hOldBitmap);
  464. DeleteObject(hBitmap);
  465. }
  466. DeleteDC(hMemDC);
  467. }
  468. /****************************************************************************
  469. DrawKing
  470. Draws the small king in the box between the free and home cells.
  471. If state is SAME, the previous bitmap is displayed. If bDraw is
  472. FALSE, oldstate is updated, but the bitmap is not displayed.
  473. ****************************************************************************/
  474. VOID DrawKing(HDC hDC, UINT state, BOOL bDraw)
  475. {
  476. HDC hMemDC; // bitmap memory DC
  477. HBITMAP hBitmap;
  478. HBITMAP hOldBitmap;
  479. static UINT oldstate = RIGHT; // previous state -- RIGHT is default
  480. HBRUSH hOldBrush;
  481. if (state == oldstate)
  482. return;
  483. if (state == SAME)
  484. state = oldstate;
  485. if (!bDraw)
  486. {
  487. oldstate = state;
  488. return;
  489. }
  490. hMemDC = CreateCompatibleDC(hDC);
  491. if (!hMemDC)
  492. return;
  493. if (state == RIGHT)
  494. hBitmap = LoadBitmap(hInst, TEXT("KingBitmap"));
  495. else if (state == LEFT)
  496. hBitmap = LoadBitmap(hInst, TEXT("KingLeft"));
  497. else if (state == SMILE)
  498. hBitmap = LoadBitmap(hInst, TEXT("KingSmile"));
  499. else // NONE
  500. hBitmap = CreateCompatibleBitmap(hDC, BMWIDTH, BMHEIGHT);
  501. if (hBitmap)
  502. {
  503. hOldBitmap = SelectObject(hMemDC, hBitmap);
  504. if (state == NONE)
  505. {
  506. hOldBrush = SelectObject(hMemDC, hBgndBrush);
  507. PatBlt(hMemDC, 0, 0, BMWIDTH, BMHEIGHT, PATCOPY);
  508. SelectObject(hMemDC, hOldBrush);
  509. }
  510. BitBlt(hDC,wIconOffset,ICONY,BMWIDTH,BMHEIGHT,hMemDC,0,0,SRCCOPY);
  511. SelectObject(hMemDC, hOldBitmap);
  512. DeleteObject(hBitmap);
  513. }
  514. DeleteDC(hMemDC);
  515. oldstate = state;
  516. }
  517. /****************************************************************************
  518. GenerateRandomGameNum
  519. returns a UINT from 1 to MAXGAMENUBMER
  520. ****************************************************************************/
  521. UINT GenerateRandomGameNum()
  522. {
  523. UINT wGameNum;
  524. srand((unsigned int)time(NULL));
  525. rand();
  526. rand();
  527. wGameNum = rand();
  528. while (wGameNum < 1 || wGameNum > MAXGAMENUMBER)
  529. wGameNum = rand();
  530. return wGameNum;
  531. }