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.

691 lines
17 KiB

  1. /***************************************************************************/
  2. /** Microsoft Windows **/
  3. /** Copyright(c) Microsoft Corp., 1991, 1992 **/
  4. /***************************************************************************/
  5. /****************************************************************************
  6. human.cpp
  7. Aug 92, JimH
  8. May 93, JimH chico port
  9. local_human and remote_human member functions
  10. ****************************************************************************/
  11. #include "hearts.h"
  12. #include "main.h" // friendly access
  13. #include "resource.h"
  14. #include "debug.h"
  15. #include <stdio.h>
  16. #include <stdlib.h> // abs() prototype
  17. static CRect rectCard; // used in timer callback
  18. // declare static members
  19. BOOL local_human::bTimerOn;
  20. CString local_human::m_StatusText;
  21. /****************************************************************************
  22. human constructor -- abstract class
  23. ****************************************************************************/
  24. human::human(int n, int pos) : player(n, pos)
  25. {
  26. }
  27. /****************************************************************************
  28. remote_human constructor
  29. ****************************************************************************/
  30. remote_human::remote_human(int n, int pos, HCONV hConv) : human(n, pos),
  31. m_hConv(hConv), bQuit(FALSE)
  32. {
  33. }
  34. /****************************************************************************
  35. remote_human::SelectCardToPlay()
  36. Under normal circumstances, all that is required is that mode be set
  37. to PLAYING. If the remote human has quit and the computer player has
  38. not yet taken over, this routine just picks the first legal card it
  39. can find and plays it.
  40. ****************************************************************************/
  41. void remote_human::SelectCardToPlay(handinfotype &h, BOOL bCheating)
  42. {
  43. if (!bQuit)
  44. {
  45. SetMode(PLAYING);
  46. return;
  47. }
  48. BOOL bFirst = (h.playerled == id); // am I leading?
  49. card *cardled = h.cardplayed[h.playerled];
  50. int nSuitLed = (cardled == NULL ? EMPTY : cardled->Suit());
  51. SLOT sLast[MAXSUIT]; // will contain some card of each suit
  52. SLOT s = EMPTY;
  53. for (int i = 0; i < MAXSUIT; i++)
  54. sLast[i] = EMPTY;
  55. // fill sLast array, and look for two of clubs while were are at it
  56. for (i = 0; i < MAXSLOT; i++)
  57. {
  58. if (cd[i].IsValid())
  59. {
  60. sLast[cd[i].Suit()] = i;
  61. if (cd[i].ID() == TWOCLUBS)
  62. s = i;
  63. }
  64. }
  65. if (s == EMPTY) // if two of clubs not found
  66. {
  67. if (sLast[CLUBS] != EMPTY)
  68. s = sLast[CLUBS];
  69. else if (sLast[DIAMONDS] != EMPTY)
  70. s = sLast[DIAMONDS];
  71. else if (sLast[SPADES] != EMPTY)
  72. s = sLast[SPADES];
  73. else
  74. s = sLast[HEARTS];
  75. if (!bFirst && (sLast[nSuitLed] != EMPTY))
  76. s = sLast[nSuitLed];
  77. }
  78. SetMode(WAITING);
  79. cd[s].Play(); // mark card as played
  80. h.cardplayed[id] = &(cd[s]); // update handinfo
  81. // inform other players
  82. ::move.playerid = id;
  83. ::move.cardid = cd[s].ID();
  84. ::move.playerled = h.playerled;
  85. ::move.turn = h.turn;
  86. ddeServer->PostAdvise(hszMove);
  87. // inform gamemeister
  88. ::pMainWnd->PostMessage(WM_COMMAND, IDM_REF);
  89. }
  90. /****************************************************************************
  91. remote_human::SelectCardsToPass()
  92. Under normal circumstances, all that is required is that mode be set to
  93. SELECTING. If the remote human has quit and the computer player has not
  94. yet taken over, just select the first three cards found.
  95. ****************************************************************************/
  96. void remote_human::SelectCardsToPass()
  97. {
  98. if (!bQuit)
  99. {
  100. SetMode(SELECTING);
  101. return;
  102. }
  103. cd[0].Select(TRUE);
  104. cd[1].Select(TRUE);
  105. cd[2].Select(TRUE);
  106. for (int i = 3; i < MAXSLOT; i++)
  107. cd[i].Select(FALSE);
  108. CClientDC dc(::pMainWnd);
  109. #ifdef USE_MIRRORING
  110. SetLayout(dc.m_hDC, 0);
  111. SetLayout(dc.m_hAttribDC, 0);
  112. #endif
  113. MarkSelectedCards(dc);
  114. SetMode(DONE_SELECTING);
  115. ddeServer->PostAdvise(hszPass); // let other players know
  116. }
  117. /****************************************************************************
  118. local_human::local_human()
  119. This is the constructor that initializes player::hWnd and player::hInst.
  120. It also creates the stretch bitmap that covers a card plus its popped
  121. height extension.
  122. ****************************************************************************/
  123. local_human::local_human(int n) : human(n, 0)
  124. {
  125. m_pStatusWnd = new CStatusBarCtrl();
  126. m_StatusText.LoadString(IDS_INTRO);
  127. CClientDC dc(::pMainWnd);
  128. m_pStatusWnd->Create(WS_CHILD|WS_VISIBLE|CCS_BOTTOM, CRect(), ::pMainWnd, 0);
  129. m_pStatusWnd->SetSimple();
  130. UpdateStatus();
  131. bTimerOn = FALSE;
  132. if (!m_bmStretchCard.CreateCompatibleBitmap(&dc, card::dxCrd,
  133. card::dyCrd + POPSPACING))
  134. {
  135. ::pMainWnd->FatalError(IDS_MEMORY);
  136. return;
  137. }
  138. }
  139. /****************************************************************************
  140. local_human destructor
  141. ****************************************************************************/
  142. local_human::~local_human()
  143. {
  144. m_bmStretchCard.DeleteObject();
  145. delete m_pStatusWnd;
  146. m_pStatusWnd = NULL;
  147. }
  148. /****************************************************************************
  149. local_human::Draw()
  150. This virtual function draws selected cards in the popped up position.
  151. ALL is not used for slot in this variant.
  152. ****************************************************************************/
  153. void local_human::Draw(CDC &dc, BOOL bCheating, SLOT slot)
  154. {
  155. DisplayName(dc);
  156. SLOT start = (slot == ALL ? 0 : slot);
  157. SLOT stop = (slot == ALL ? MAXSLOT : slot+1);
  158. SLOT playedslot = EMPTY; // must draw cards in play last for EGA
  159. for (SLOT s = start; s < stop; s++)
  160. {
  161. if (cd[s].IsPlayed())
  162. playedslot = s;
  163. else
  164. cd[s].PopDraw(dc); // pop up selected cards
  165. }
  166. if (playedslot != EMPTY)
  167. cd[playedslot].Draw(dc);
  168. }
  169. /****************************************************************************
  170. local_human::PopCard()
  171. handles mouse button selection of card to pass
  172. ****************************************************************************/
  173. void local_human::PopCard(CBrush &brush, int x, int y)
  174. {
  175. SLOT s = XYToCard(x, y);
  176. if (s == EMPTY)
  177. return;
  178. // count selected cards
  179. int c = 0;
  180. for (int i = 0; i < MAXSLOT; i++)
  181. if (cd[i].IsSelected())
  182. c++;
  183. if (cd[s].IsSelected() && (c == 3))
  184. {
  185. ::pMainWnd->PostMessage(WM_COMMAND, IDM_HIDEBUTTON);
  186. }
  187. else if (!cd[s].IsSelected())
  188. {
  189. if (c == 3) // only allow three selections
  190. return;
  191. else if (c == 2)
  192. ::pMainWnd->PostMessage(WM_COMMAND, IDM_SHOWBUTTON);
  193. }
  194. // toggle selection
  195. BOOL bSelected = cd[s].IsSelected();
  196. cd[s].Select(!bSelected);
  197. CClientDC dc(::pMainWnd);
  198. #ifdef USE_MIRRORING
  199. SetLayout(dc.m_hDC, 0);
  200. SetLayout(dc.m_hAttribDC, 0);
  201. #endif
  202. CDC memDC;
  203. memDC.CreateCompatibleDC(&dc);
  204. memDC.SelectObject(&m_bmStretchCard);
  205. memDC.SelectObject(&brush);
  206. memDC.PatBlt(0, 0, card::dxCrd, card::dyCrd + POPSPACING, PATCOPY);
  207. for (i = 0; i < MAXSLOT; i++)
  208. {
  209. if (abs(i - s) <= (card::dxCrd / HORZSPACING))
  210. {
  211. cd[i].Draw(memDC, // cdc
  212. (i - s) * HORZSPACING, // x
  213. cd[i].IsSelected() ? 0 : POPSPACING, // y
  214. FACEUP, // mode
  215. FALSE); // update loc?
  216. }
  217. }
  218. dc.BitBlt(loc.x + (HORZSPACING * s), loc.y - POPSPACING,
  219. card::dxCrd, card::dyCrd + POPSPACING,
  220. &memDC, 0, 0, SRCCOPY);
  221. }
  222. /****************************************************************************
  223. local_human::PlayCard()
  224. handles mouse button selection of card to play
  225. and ensures move is legal.
  226. PlayCard starts a timer that calls StartTimer() which calls TimerBadMove().
  227. Think of it as one long function with a timer delay half way through.
  228. ****************************************************************************/
  229. BOOL local_human::PlayCard(int x, int y, handinfotype &h, BOOL bCheating,
  230. BOOL bFlash)
  231. {
  232. SLOT s = XYToCard(x, y);
  233. if (s == EMPTY)
  234. return FALSE;
  235. card *cardled = h.cardplayed[h.playerled];
  236. BOOL bFirstTrick = (cardled != NULL && cardled->ID() == TWOCLUBS);
  237. /* check if selected card is valid */
  238. if (h.playerled == id) // if local human is leading...
  239. {
  240. if (cd[s].ID() != TWOCLUBS)
  241. {
  242. for (int i = 0; i < MAXSLOT; i++) // is there a two of clubs?
  243. {
  244. if ((i != s) && (cd[i].ID() == TWOCLUBS))
  245. {
  246. UpdateStatus(IDS_LEAD2C);
  247. if (bFlash)
  248. StartTimer(cd[s]);
  249. return FALSE;
  250. }
  251. }
  252. }
  253. if ((cd[s].Suit() == HEARTS) && (!h.bHeartsBroken)) // if hearts led
  254. {
  255. for (int i = 0; i < MAXSLOT; i++) // are there any non-hearts?
  256. {
  257. if ((!cd[i].IsEmpty()) && (cd[i].Suit() != HEARTS))
  258. {
  259. UpdateStatus(IDS_LEADHEARTS);
  260. if (bFlash)
  261. StartTimer(cd[s]);
  262. return FALSE;
  263. }
  264. }
  265. }
  266. }
  267. // if not following suit
  268. else if (cardled != NULL && (cd[s].Suit() != cardled->Suit()))
  269. {
  270. // make sure we're following suit if possible
  271. for (int i = 0; i < MAXSLOT; i++)
  272. {
  273. if ((!cd[i].IsEmpty()) && (cd[i].Suit()==cardled->Suit()))
  274. {
  275. CString s1, s2;
  276. s1.LoadString(IDS_BADMOVE);
  277. s2.LoadString(IDS_SUIT0+cardled->Suit());
  278. TCHAR string[80];
  279. wsprintf(string, s1, s2);
  280. if (bFlash)
  281. {
  282. UpdateStatus(string);
  283. StartTimer(cd[s]);
  284. }
  285. return FALSE;
  286. }
  287. }
  288. // make sure we're not trying to break the First Blood rule
  289. if (bFirstTrick && ::pMainWnd->IsFirstBloodEnforced())
  290. {
  291. BOOL bPointCard =
  292. (cd[s].Suit() == HEARTS || cd[s].ID() == BLACKLADY);
  293. BOOL bOthersAvailable = FALSE;
  294. for (int i = 0; i < MAXSLOT; i++)
  295. if ((!cd[i].IsEmpty()) && (cd[i].Suit() != HEARTS))
  296. if (cd[i].ID() != BLACKLADY)
  297. bOthersAvailable = TRUE;
  298. if (bPointCard && bOthersAvailable)
  299. {
  300. UpdateStatus(IDS_BADBLOOD);
  301. if (bFlash)
  302. StartTimer(cd[s]);
  303. return FALSE;
  304. }
  305. }
  306. }
  307. SetMode(WAITING);
  308. cd[s].Play();
  309. h.cardplayed[id] = &(cd[s]);
  310. ::move.playerid = id;
  311. ::move.cardid = cd[s].ID();
  312. ::move.playerled = h.playerled;
  313. ::move.turn = h.turn;
  314. if (id == 0) // if gamemeister
  315. ddeServer->PostAdvise(hszMove);
  316. else
  317. ddeClient->Poke(hszMove, &move, sizeof(move));
  318. ::pMainWnd->OnRef();
  319. return TRUE;
  320. }
  321. void local_human::StartTimer(card &c)
  322. {
  323. CClientDC dc(::pMainWnd);
  324. #ifdef USE_MIRRORING
  325. SetLayout(dc.m_hDC, 0);
  326. SetLayout(dc.m_hAttribDC, 0);
  327. #endif
  328. c.Draw(dc, HILITE); // flash
  329. c.GetRect(rectCard);
  330. if (::pMainWnd->SetTimer(1, 250, TimerBadMove))
  331. {
  332. bTimerOn = TRUE;
  333. }
  334. else
  335. {
  336. bTimerOn = FALSE;
  337. ::pMainWnd->InvalidateRect(&rectCard, FALSE);
  338. }
  339. }
  340. // MFC2 changes same as SetTimer in main2.cpp
  341. #if defined (MFC1)
  342. UINT FAR PASCAL EXPORT
  343. TimerBadMove(HWND hWnd, UINT nMsg, int nIDEvent, DWORD dwTime)
  344. {
  345. ::KillTimer(hWnd, 1);
  346. local_human::bTimerOn = FALSE;
  347. ::InvalidateRect(hWnd, &rectCard, FALSE);
  348. return 0;
  349. }
  350. #else
  351. void FAR PASCAL EXPORT
  352. TimerBadMove(HWND hWnd, UINT nMsg, UINT_PTR nIDEvent, DWORD dwTime)
  353. {
  354. ::KillTimer(hWnd, 1);
  355. local_human::bTimerOn = FALSE;
  356. #ifdef USE_MIRRORING
  357. CRect rect;
  358. int i;
  359. DWORD ProcessDefaultLayout;
  360. if (GetProcessDefaultLayout(&ProcessDefaultLayout))
  361. if (ProcessDefaultLayout == LAYOUT_RTL)
  362. {
  363. GetClientRect(hWnd, &rect);
  364. rectCard.left = abs(rect.right - rect.left) - rectCard.left;
  365. rectCard.right = abs(rect.right - rect.left) - rectCard.right;
  366. i = rectCard.left;
  367. rectCard.left = rectCard.right;
  368. rectCard.right = i;
  369. }
  370. #endif
  371. ::InvalidateRect(hWnd, &rectCard, FALSE);
  372. }
  373. #endif
  374. /****************************************************************************
  375. local_human::XYToCard()
  376. returns a card slot number (or EMPTY) given a mouse location
  377. ****************************************************************************/
  378. int local_human::XYToCard(int x, int y)
  379. {
  380. // check that we are in the right general area on the screen
  381. if (y < (loc.y - POPSPACING))
  382. return EMPTY;
  383. if (y > (loc.y + card::dyCrd))
  384. return EMPTY;
  385. if (x < loc.x)
  386. return EMPTY;
  387. if (x > (loc.x + (12 * HORZSPACING) + card::dxCrd))
  388. return EMPTY;
  389. // Take first stab at card selected.
  390. SLOT s = (x - loc.x) / HORZSPACING;
  391. if (s > 12)
  392. s = 12;
  393. // If the click is ABOVE the top of the normal card location,
  394. // check to see if this is a selected card.
  395. if (y < loc.y)
  396. {
  397. // If the card is bSelected, then we have it. If not, it could
  398. // be overhanging other cards.
  399. if (!cd[s].IsSelected())
  400. {
  401. for (;;)
  402. {
  403. if (s == 0)
  404. return EMPTY;
  405. s--;
  406. // if this card doesn't extend as far as x, give up
  407. if ((loc.x + (s * HORZSPACING) + card::dxCrd) < x)
  408. return EMPTY;
  409. // if this card is selected, we've got it
  410. if (cd[s].IsSelected())
  411. break;
  412. }
  413. }
  414. }
  415. // a similar check is used to make sure we pick a card not yet played
  416. if (!cd[s].IsInHand())
  417. {
  418. for (;;)
  419. {
  420. if (s == 0)
  421. return EMPTY;
  422. s--;
  423. // if this card doesn't extend as far as x, give up
  424. if ((loc.x + (s * HORZSPACING) + card::dxCrd) < x)
  425. return EMPTY;
  426. // if this card is selected, we've got it
  427. if (cd[s].IsInHand())
  428. break;
  429. }
  430. }
  431. return s;
  432. }
  433. /****************************************************************************
  434. local_human::SelectCardsToPass()
  435. This virtual function allows mouse clicks to mean select a card to play.
  436. ****************************************************************************/
  437. void local_human::SelectCardsToPass()
  438. {
  439. SetMode(SELECTING);
  440. }
  441. /****************************************************************************
  442. local_human::SelectCardToPlay
  443. Computer versions of this virtual function actually do the card selection.
  444. This local_human version marks the player as ready to select a card to
  445. play with the mouse, and updates the status to reflect this.
  446. ****************************************************************************/
  447. void local_human::SelectCardToPlay(handinfotype &h, BOOL bCheating)
  448. {
  449. SetMode(PLAYING);
  450. UpdateStatus(IDS_GO);
  451. }
  452. /****************************************************************************
  453. local_human::UpdateStatus
  454. The status bar can be updated either by manually filling m_StatusText
  455. or by passing a string resource id.
  456. ****************************************************************************/
  457. void local_human::UpdateStatus(void)
  458. {
  459. m_pStatusWnd->SetText(m_StatusText, 255, 0);
  460. }
  461. void local_human::UpdateStatus(int stringid)
  462. {
  463. status = stringid;
  464. m_StatusText.LoadString(stringid);
  465. UpdateStatus();
  466. }
  467. void local_human::UpdateStatus(const TCHAR *string)
  468. {
  469. m_StatusText = string;
  470. UpdateStatus();
  471. }
  472. /****************************************************************************
  473. local_human::ReceiveSelectedCards
  474. The parameter c[] is an array of three cards being passed from another
  475. player.
  476. ****************************************************************************/
  477. void local_human::ReceiveSelectedCards(int c[])
  478. {
  479. for (int i = 0, j = 0; j < 3; i++)
  480. {
  481. if (cd[i].IsSelected())
  482. cd[i].SetID(c[j++]);
  483. ASSERT(i < MAXSLOT);
  484. }
  485. SetMode(ACCEPTING);
  486. UpdateStatus(IDS_ACCEPT);
  487. }
  488. /****************************************************************************
  489. local_human::WaitMessage()
  490. Makes and shows the "Waiting for %s to move..." message
  491. ****************************************************************************/
  492. void local_human::WaitMessage(const TCHAR *name)
  493. {
  494. TCHAR buf[100];
  495. CString s;
  496. s.LoadString(IDS_WAIT);
  497. wsprintf(buf, s, name);
  498. UpdateStatus(buf);
  499. }