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.

490 lines
13 KiB

  1. /****************************************************************************
  2. Transfer.c
  3. June 91, JimH initial code
  4. Oct 91, JimH port to Win32
  5. Routines for transfering cards and queing cards for transfer are here.
  6. ****************************************************************************/
  7. #include "freecell.h"
  8. #include "freecons.h"
  9. #include <assert.h>
  10. #include <memory.h>
  11. /****************************************************************************
  12. Transfer
  13. This function actually moves the cards. It both updates the card array,
  14. and draws the bitmaps. Note that it moves only one card per call.
  15. ****************************************************************************/
  16. VOID Transfer(HWND hWnd, UINT fcol, UINT fpos, UINT tcol, UINT tpos)
  17. {
  18. CARD c;
  19. HDC hDC;
  20. DEBUGMSG(TEXT("Transfer request from (%u, "), fcol);
  21. DEBUGMSG(TEXT("%u) to ("), fpos);
  22. DEBUGMSG(TEXT("%u, "), tcol);
  23. DEBUGMSG(TEXT("%u)\r\n"), tpos);
  24. assert(fpos < MAXPOS);
  25. assert(tpos < MAXPOS);
  26. UpdateWindow(hWnd); // ensure cards are drawn before animation starts
  27. if (fcol == TOPROW) // can't transfer FROM home cells
  28. {
  29. if ((fpos > 3) || (card[TOPROW][fpos] == IDGHOST))
  30. return;
  31. }
  32. else
  33. {
  34. if ((fpos = FindLastPos(fcol)) == EMPTY) // or from empty column
  35. return;
  36. if (fcol == tcol) // click and release on same column
  37. {
  38. hDC = GetDC(hWnd);
  39. DrawCard(hDC, fcol, fpos, card[fcol][fpos], FACEUP);
  40. ReleaseDC(hWnd, hDC);
  41. return;
  42. }
  43. }
  44. if (tcol == TOPROW)
  45. {
  46. if (tpos > 3) // if move to home cell
  47. {
  48. wCardCount--;
  49. DisplayCardCount(hWnd); // update display
  50. c = card[fcol][fpos];
  51. home[SUIT(c)] = VALUE(c); // new card at top of home[suit]
  52. }
  53. }
  54. else
  55. tpos = FindLastPos(tcol) + 1; // bottom of column
  56. Glide(hWnd, fcol, fpos, tcol, tpos); // send the card on its way
  57. c = card[fcol][fpos];
  58. card[fcol][fpos] = EMPTY;
  59. card[tcol][tpos] = c;
  60. /* If ACE being moved to home cell, update homesuit array. */
  61. if (VALUE(c) == ACE && tcol == TOPROW && tpos > 3)
  62. homesuit[SUIT(c)] = tpos;
  63. if (tcol == TOPROW)
  64. {
  65. hDC = GetDC(hWnd);
  66. DrawKing(hDC, tpos < 4 ? LEFT : RIGHT, TRUE);
  67. ReleaseDC(hWnd, hDC);
  68. }
  69. }
  70. /******************************************************************************
  71. MoveCol
  72. User has requested a multi-card move to an empty column
  73. ******************************************************************************/
  74. VOID MoveCol(UINT fcol, UINT tcol)
  75. {
  76. UINT freecells; // number of free cells
  77. CARD free[4]; // locations of free cells
  78. UINT trans; // number to transfer
  79. INT i; // counter
  80. assert(fcol != TOPROW);
  81. assert(tcol != TOPROW);
  82. assert(card[fcol][0] != EMPTY);
  83. /* Count number of free cells and put locations in free[] */
  84. freecells = 0;
  85. for (i = 0; i < 4; i++)
  86. {
  87. if (card[TOPROW][i] == EMPTY)
  88. {
  89. free[freecells] = i;
  90. freecells++;
  91. }
  92. }
  93. /* Find number of cards to transfer */
  94. if (fcol == TOPROW || tcol == TOPROW)
  95. trans = 1;
  96. else
  97. trans = NumberToTransfer(fcol, tcol);
  98. if (trans > (freecells+1)) // don't transfer too many
  99. trans = freecells+1;
  100. /* Move to free cells */
  101. trans--;
  102. for (i = 0; i < (INT)trans; i++)
  103. QueueTransfer(fcol, 0, TOPROW, free[i]);
  104. /* Transfer last card directly */
  105. QueueTransfer(fcol, 0, tcol, 0);
  106. /* transfer from free cells to column */
  107. for (i = trans-1; i >= 0; i--)
  108. QueueTransfer(TOPROW, free[i], tcol, 0);
  109. }
  110. /******************************************************************************
  111. MultiMove
  112. User has chosen to move from one non-empty column to another.
  113. ******************************************************************************/
  114. VOID MultiMove(UINT fcol, UINT tcol)
  115. {
  116. CARD free[4]; // locations of free cells
  117. UINT freecol[MAXCOL]; // locations of free columns
  118. UINT freecells; // number of free cells
  119. UINT trans; // number to transfer
  120. UINT col, pos;
  121. INT i; // counter
  122. assert(fcol != TOPROW);
  123. assert(tcol != TOPROW);
  124. assert(card[fcol][0] != EMPTY);
  125. /* Count number of free cells and put locations in free[] */
  126. freecells = 0;
  127. for (pos = 0; pos < 4; pos++)
  128. {
  129. if (card[TOPROW][pos] == EMPTY)
  130. {
  131. free[freecells] = pos;
  132. freecells++;
  133. }
  134. }
  135. /* Find the number of cards to move. If the number is too big to
  136. move all at once, push partial results into available columns. */
  137. trans = NumberToTransfer(fcol, tcol);
  138. if (trans > (freecells+1))
  139. {
  140. i = 0;
  141. for (col = 1; col < MAXCOL; col++)
  142. if (card[col][0] == EMPTY)
  143. freecol[i++] = col;
  144. /* transfer into free columns until direct transfer can be made */
  145. i = 0;
  146. while (trans > (freecells + 1))
  147. {
  148. MoveCol(fcol, freecol[i]);
  149. trans -= (freecells + 1);
  150. i++;
  151. }
  152. MoveCol(fcol, tcol); // do last transfer directly
  153. for (i--; i >= 0; i--) // gather cards in free cells
  154. MoveCol(freecol[i], tcol);
  155. }
  156. else // else all one MoveCol()
  157. {
  158. MoveCol(fcol, tcol);
  159. }
  160. }
  161. /****************************************************************************
  162. QueueTransfer
  163. In order that multi-card moves happen slowly enough for the user to
  164. watch, they are not moved as soon as they are calculated. Instead,
  165. they first are queued using this function into the movelist array.
  166. After the request is queued, the card array is updated to reflect the
  167. request. This is ok because the card array is saved away in shadow
  168. temporarily. The same logic as in Transfer() is used to update card.
  169. ****************************************************************************/
  170. VOID QueueTransfer(UINT fcol, UINT fpos, UINT tcol, UINT tpos)
  171. {
  172. CARD c;
  173. MOVE move;
  174. assert(moveindex < MAXMOVELIST);
  175. assert(fpos < MAXPOS);
  176. assert(tpos < MAXPOS);
  177. move.fcol = fcol; // package move request into MOVE type
  178. move.fpos = fpos;
  179. move.tcol = tcol;
  180. move.tpos = tpos;
  181. movelist[moveindex++] = move; // store request in array and update index
  182. /* Now update card array if necessary. */
  183. if (fcol == TOPROW)
  184. {
  185. if ((fpos > 3) || (card[TOPROW][fpos] == IDGHOST))
  186. return;
  187. }
  188. else
  189. {
  190. if ((fpos = FindLastPos(fcol)) == EMPTY)
  191. return;
  192. if (fcol == tcol) // click and release on same column
  193. return;
  194. }
  195. if (tcol == TOPROW)
  196. {
  197. if (tpos > 3)
  198. {
  199. c = card[fcol][fpos];
  200. home[SUIT(c)] = VALUE(c);
  201. }
  202. }
  203. else
  204. tpos = FindLastPos(tcol) + 1;
  205. c = card[fcol][fpos];
  206. card[fcol][fpos] = EMPTY;
  207. card[tcol][tpos] = c;
  208. if (VALUE(c) == ACE && tcol == TOPROW && tpos > 3)
  209. homesuit[SUIT(c)] = tpos;
  210. }
  211. /******************************************************************************
  212. MoveCards
  213. If there are queued transfer requests, this function moves them.
  214. ******************************************************************************/
  215. VOID MoveCards(HWND hWnd)
  216. {
  217. UINT i;
  218. if (moveindex == 0) // if there are no queued requests
  219. return;
  220. /* restore card to its state before requests got queued. */
  221. memcpy(&(card[0][0]), &(shadow[0][0]), sizeof(card));
  222. SetCursor(LoadCursor(NULL, IDC_WAIT)); // set cursor to hourglass
  223. SetCapture(hWnd);
  224. ShowCursor(TRUE);
  225. for (i = 0; i < moveindex; i++)
  226. Transfer(hWnd, movelist[i].fcol, movelist[i].fpos,
  227. movelist[i].tcol, movelist[i].tpos);
  228. if ((moveindex > 1) || (movelist[0].fcol != movelist[0].tcol))
  229. {
  230. cUndo = moveindex;
  231. EnableMenuItem(GetMenu(hWnd), IDM_UNDO, MF_ENABLED);
  232. }
  233. else
  234. {
  235. cUndo = 0;
  236. EnableMenuItem(GetMenu(hWnd), IDM_UNDO, MF_GRAYED);
  237. }
  238. moveindex = 0; // no cards left to move
  239. ShowCursor(FALSE);
  240. SetCursor(LoadCursor(NULL, IDC_ARROW));
  241. ReleaseCapture();
  242. if (wCardCount == 0) // if game is won
  243. {
  244. INT cLifetimeWins; // wins including .ini stats
  245. INT wStreak, wSType; // streak length and type
  246. INT wWins; // record win streak
  247. INT_PTR nResponse; // dialog box response
  248. HDC hDC;
  249. LONG lRegResult;
  250. cUndo = 0;
  251. EnableMenuItem(GetMenu(hWnd), IDM_UNDO, MF_GRAYED);
  252. lRegResult = REGOPEN
  253. if (ERROR_SUCCESS == lRegResult)
  254. {
  255. bGameInProgress = FALSE;
  256. bCheating = FALSE;
  257. cLifetimeWins = GetInt(pszWon, 0);
  258. if (gamenumber != oldgamenumber) // repeats don't count
  259. {
  260. cLifetimeWins++;
  261. cWins++;
  262. cGames++;
  263. SetInt(pszWon, cLifetimeWins);
  264. wSType = GetInt(pszSType, LOST);
  265. if (wSType == LOST)
  266. {
  267. SetInt(pszSType, WON);
  268. wStreak = 1;
  269. SetInt(pszStreak, 1);
  270. }
  271. else
  272. {
  273. wStreak = GetInt(pszStreak, 0);
  274. wStreak++;
  275. SetInt(pszStreak, wStreak);
  276. }
  277. wWins = GetInt(pszWins, 0);
  278. if (wWins < wStreak) // if new record
  279. {
  280. wWins = wStreak;
  281. SetInt(pszWins, wWins);
  282. }
  283. }
  284. REGCLOSE
  285. }
  286. hDC = GetDC(hWnd);
  287. Payoff(hDC);
  288. ReleaseDC(hWnd, hDC);
  289. bWonState = TRUE;
  290. nResponse = DialogBox(hInst, TEXT("YouWin"), hWnd, YouWinDlg);
  291. if (nResponse == IDYES)
  292. PostMessage(hWnd, WM_COMMAND,
  293. bSelecting ? IDM_SELECT : IDM_NEWGAME, 0);
  294. oldgamenumber = gamenumber;
  295. gamenumber = 0; // turn off mouse handling
  296. }
  297. else
  298. IsGameLost(hWnd); // check for game lost
  299. }
  300. /******************************************************************************
  301. SetCursorShape
  302. This function is called in response to WM_MOUSEMOVE. If the current pointer
  303. position represents a legal move, the cursor shape changes to indicate this.
  304. ******************************************************************************/
  305. VOID SetCursorShape(HWND hWnd, UINT x, UINT y)
  306. {
  307. UINT tcol = 0, tpos = 0;
  308. UINT trans; // number of cards required to transfer
  309. BOOL bFound; // is cursor over card?
  310. HDC hDC;
  311. /* If we're flipping, cursor is always an hourglass. */
  312. if (bFlipping)
  313. {
  314. SetCursor(LoadCursor(NULL, IDC_WAIT));
  315. return;
  316. }
  317. bFound = Point2Card(x, y, &tcol, &tpos);
  318. if (bFound && tcol == TOPROW)
  319. {
  320. hDC = GetDC(hWnd);
  321. if (tpos < 4)
  322. DrawKing(hDC, LEFT, TRUE);
  323. else
  324. DrawKing(hDC, RIGHT, TRUE);
  325. ReleaseDC(hWnd, hDC);
  326. }
  327. /* Unless we're chosing a move target, cursor is just an arrow. */
  328. if (wMouseMode != TO)
  329. {
  330. SetCursor(LoadCursor(NULL, IDC_ARROW));
  331. return;
  332. }
  333. /* If we're not on a card, check if we're pointing to an empty
  334. column (up arrow), otherwise arrow. */
  335. if (!bFound)
  336. {
  337. if ((tcol > 0 && tcol < 9) && (card[tcol][0] == EMPTY))
  338. SetCursor(LoadCursor(NULL, IDC_UPARROW));
  339. else
  340. SetCursor(LoadCursor(NULL, IDC_ARROW));
  341. return;
  342. }
  343. if (tcol != TOPROW)
  344. tpos = FindLastPos(tcol);
  345. /* Check for cancel request. */
  346. if (wFromCol == tcol && wFromPos == tpos)
  347. {
  348. SetCursor(LoadCursor(NULL, IDC_ARROW));
  349. return;
  350. }
  351. /* Check moves from or to the top row. */
  352. if (wFromCol == TOPROW || tcol == TOPROW)
  353. {
  354. if (IsValidMove(hWnd, tcol, tpos))
  355. {
  356. if (tcol == TOPROW)
  357. SetCursor(LoadCursor(NULL, IDC_UPARROW));
  358. else
  359. SetCursor(LoadCursor(hInst, TEXT("DownArrow")));
  360. }
  361. else
  362. SetCursor(LoadCursor(NULL, IDC_ARROW));
  363. return;
  364. }
  365. /* Check moves between columns. */
  366. trans = NumberToTransfer(wFromCol, tcol); // how many required?
  367. if ((trans > 0) && (trans <= MaxTransfer()))
  368. SetCursor(LoadCursor(hInst, TEXT("DownArrow")));
  369. else
  370. SetCursor(LoadCursor(NULL, IDC_ARROW));
  371. }