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.

607 lines
16 KiB

  1. /***************************************************************************/
  2. /** Microsoft Windows **/
  3. /** Copyright(c) Microsoft Corp., 1991, 1992 **/
  4. /***************************************************************************/
  5. /****************************************************************************
  6. main2.cpp
  7. Aug 92, JimH
  8. May 93, JimH chico port
  9. Additional member functions for CMainWindow are here.
  10. ****************************************************************************/
  11. #include "hearts.h"
  12. #include "main.h"
  13. #include "resource.h"
  14. #include "debug.h"
  15. /****************************************************************************
  16. CMainWindow::Shuffle -- user requests shuffle from menu
  17. ****************************************************************************/
  18. void CMainWindow::Shuffle()
  19. {
  20. static int offset[MAXPLAYER] = { 1, 3, 2, 0 }; // passdir order
  21. // fill temp array with consecutive values
  22. int temp[52]; // array of card values
  23. for (int i = 0; i < 52; i++)
  24. temp[i] = i;
  25. // Sort cards
  26. int nLeft = 52;
  27. for (i = 0; i < 52; i++)
  28. {
  29. int j = ::rand() % nLeft;
  30. int id = i/13;
  31. int pos = Id2Pos(id); // convert id to position
  32. p[pos]->SetID(i%13, temp[j]);
  33. p[pos]->Select(i%13, FALSE);
  34. temp[j] = temp[--nLeft];
  35. }
  36. // display PASS button
  37. if (passdir != NOPASS)
  38. {
  39. CString text;
  40. text.LoadString(IDS_PASSLEFT + passdir);
  41. m_Button.SetWindowText(text);
  42. m_Button.EnableWindow(FALSE);
  43. m_Button.ShowWindow(SW_SHOW);
  44. }
  45. // set card locs and ask players to select cards to pass
  46. for (i = 0; i < MAXPLAYER; i++)
  47. {
  48. p[i]->ResetLoc();
  49. if (passdir != NOPASS)
  50. p[i]->SelectCardsToPass();
  51. }
  52. // Make sure everyone gets appropriate little white dots
  53. if (passdir != NOPASS)
  54. {
  55. if (role == PLAYER)
  56. ddeClient->Poke(hszPassUpdate, TEXT("")); // ask for update
  57. else
  58. ddeServer->PostAdvise(hszPass); // inform players
  59. }
  60. // Paint main window. This is done manually instead of just
  61. // invalidating the rectangle so that the cards are drawn in
  62. // order as if they are dealt, instead of a player at a time.
  63. CClientDC dc(this);
  64. #ifdef USE_MIRRORING
  65. SetLayout(dc.m_hDC, 0);
  66. SetLayout(dc.m_hAttribDC, 0);
  67. #endif
  68. CRect rect;
  69. GetClientRect(rect);
  70. dc.FillRect(&rect, &m_BgndBrush);
  71. for (SLOT s = 0; s < MAXSLOT; s++)
  72. for (i = 0; i < MAXPLAYER; i++)
  73. p[i]->Draw(dc, bCheating, s);
  74. for (i = 0; i < MAXPLAYER; i++)
  75. {
  76. if (passdir == NOPASS)
  77. p[i]->NotifyNewRound();
  78. else
  79. {
  80. p[i]->MarkSelectedCards(dc);
  81. CString sSelect;
  82. sSelect.LoadString(IDS_SELECT);
  83. CString sName;
  84. int passto = (i + offset[passdir]) % 4;
  85. sName = p[passto]->GetName();
  86. TCHAR string[100];
  87. wsprintf(string, sSelect, sName);
  88. p[i]->UpdateStatus(string);
  89. }
  90. }
  91. DoSort();
  92. }
  93. /****************************************************************************
  94. CMainWindow::HandlePassing()
  95. This function first checks to make sure each player is DONE_SELECTING,
  96. and then transfers the cards from hand to hand.
  97. This function is called by the gamemeister when he presses the pass
  98. button, or when notification arrives that a remote human has selected
  99. cards to pass.
  100. It returns FALSE if cards were not passed (because a remote human was
  101. still selecting) and TRUE if cards were successfully passed.
  102. ****************************************************************************/
  103. BOOL CMainWindow::HandlePassing()
  104. {
  105. int passto[MAXPLAYER];
  106. int temp[MAXPLAYER][3];
  107. static int offset[MAXPLAYER] = { 1, 3, 2, 0 };
  108. for (int pos = 0; pos < MAXPLAYER; pos++)
  109. if (p[pos]->GetMode() != DONE_SELECTING)
  110. return FALSE;
  111. for (pos = 0; pos < MAXPLAYER; pos++)
  112. {
  113. passto[pos] = ((pos + offset[passdir]) % 4);
  114. p[pos]->ReturnSelectedCards(temp[pos]);
  115. }
  116. for (pos = 0; pos < MAXPLAYER; pos++)
  117. p[passto[pos]]->ReceiveSelectedCards(temp[pos]);
  118. for (pos = 0; pos < MAXPLAYER; pos++)
  119. if (bCheating || (pos == 0))
  120. p[pos]->Sort();
  121. tricksleft = MAXSLOT;
  122. passdir++;
  123. if (passdir > NOPASS)
  124. passdir = LEFT;
  125. for (pos = 0; pos < MAXPLAYER; pos++)
  126. p[pos]->NotifyNewRound(); // notify players cards are passed
  127. CString s;
  128. s.LoadString(IDS_OK);
  129. m_Button.SetWindowText(s);
  130. OnShowButton();
  131. for (pos = 0; pos < MAXPLAYER; pos++)
  132. {
  133. CRect rect;
  134. if (pos == 0 || bCheating)
  135. p[pos]->GetCoverRect(rect);
  136. else
  137. p[pos]->GetMarkingRect(rect);
  138. InvalidateRect(&rect, TRUE);
  139. }
  140. UpdateWindow();
  141. return TRUE;
  142. }
  143. /****************************************************************************
  144. CMainWindow::FirstMove
  145. resets cardswon[] and tells owner of two of clubs to start hand
  146. ****************************************************************************/
  147. void CMainWindow::FirstMove()
  148. {
  149. for (int pos = 0; pos < MAXPLAYER; pos++)
  150. {
  151. p[pos]->SetMode(WAITING);
  152. p[pos]->ResetCardsWon();
  153. }
  154. for (pos = 0; pos < MAXPLAYER; pos++)
  155. {
  156. for (SLOT s = 0; s < MAXSLOT; s++)
  157. {
  158. if (p[pos]->GetID(s) == TWOCLUBS)
  159. {
  160. int id = Pos2Id(pos);
  161. ResetHandInfo(id);
  162. handinfo.bHeartsBroken = FALSE;
  163. handinfo.bQSPlayed = FALSE;
  164. handinfo.bShootingRisk = TRUE;
  165. handinfo.nMoonShooter = EMPTY;
  166. handinfo.bHumanShooter = FALSE;
  167. p[pos]->SelectCardToPlay(handinfo, bCheating);
  168. if (pos != 0)
  169. ((local_human *)p[0])->WaitMessage(p[pos]->GetName());
  170. return;
  171. }
  172. }
  173. }
  174. }
  175. /****************************************************************************
  176. CMainWindow::EndHand
  177. TimerDispatch
  178. CMainWindow::DispatchCards
  179. The Ref calls this routine at the end of each hand. It is logically
  180. a single routine, but is broken up so that there is a delay before the
  181. cards are zipped off the screen.
  182. EndHand() calculates who won the hand (trick) and starts a timer.
  183. TimerDispatch() receives the time message and calls DispatchCards().
  184. DispatchCards()
  185. ****************************************************************************/
  186. void CMainWindow::EndHand()
  187. {
  188. /* determine suit led */
  189. int playerled = handinfo.playerled;
  190. card *cardled = handinfo.cardplayed[playerled];
  191. int suitled = cardled->Suit();
  192. int value = cardled->Value2();
  193. trickwinner = playerled; // by default
  194. // Let players update tables, etc.
  195. for (int i = 0; i < 4; i++)
  196. p[i]->NotifyEndHand(handinfo);
  197. // check if anyone else played a higher card of the same suit
  198. for (i = playerled; i < (playerled+4); i++)
  199. {
  200. int j = i % 4;
  201. card *c = handinfo.cardplayed[j];
  202. if (c->Suit() == suitled)
  203. {
  204. int v = c->Value2();
  205. if (v > value)
  206. {
  207. value = v;
  208. trickwinner = j;
  209. }
  210. }
  211. }
  212. TRACE0("\n");
  213. // Update moonshoot portion of handinfo
  214. if (handinfo.bShootingRisk)
  215. {
  216. BOOL bPoints = FALSE; // point cards this hand?
  217. for (i = 0; i < 4; i++)
  218. {
  219. card *c = handinfo.cardplayed[i];
  220. if ((c->Suit() == HEARTS) || (c->ID() == BLACKLADY))
  221. bPoints = TRUE;
  222. }
  223. if (bPoints)
  224. {
  225. if (handinfo.nMoonShooter == EMPTY)
  226. {
  227. handinfo.nMoonShooter = trickwinner; // first points this round
  228. handinfo.bHumanShooter = p[trickwinner]->IsHuman();
  229. TRACE2("First points to p[%d] (%s)\n", trickwinner,
  230. handinfo.bHumanShooter ? TEXT("human") : TEXT("computer"));
  231. }
  232. else if (handinfo.nMoonShooter != trickwinner) // new point earner
  233. {
  234. handinfo.bShootingRisk = FALSE;
  235. TRACE0("Moon shot risk over\n");
  236. }
  237. }
  238. }
  239. // Start a timer so there is a delay between when the last card of
  240. // the trick is played, and when the cards are whisked off toward
  241. // the trick winner (dispatched.) If the timer fails, just call
  242. // DispatchCards() directly. The timer id is m_myid instead of a
  243. // constant so there's no conflict if you run multiple instances on
  244. // a single machine using local DDE, which is useful for testing.
  245. if (SetTimer(m_myid, 1000, TimerDispatch))
  246. bTimerOn = TRUE;
  247. else
  248. {
  249. bTimerOn = FALSE;
  250. DispatchCards();
  251. }
  252. }
  253. // for MFC1, this would return UINT and 3rd parameter would be int
  254. // for MFC2, this would return VOID and 3rd parameter would be UINT
  255. #if defined (MFC1)
  256. inline UINT FAR PASCAL EXPORT
  257. TimerDispatch(HWND hWnd, UINT nMsg, int nIDEvent, DWORD dwTime)
  258. {
  259. ::pMainWnd->DispatchCards(); // sneak back into a CMainWindow member func.
  260. return 0;
  261. }
  262. #else
  263. inline VOID FAR PASCAL EXPORT
  264. TimerDispatch(HWND hWnd, UINT nMsg, UINT_PTR nIDEvent, DWORD dwTime)
  265. {
  266. ::pMainWnd->DispatchCards(); // sneak back into a CMainWindow member func.
  267. }
  268. #endif
  269. void CMainWindow::DispatchCards()
  270. {
  271. KillTimer(m_myid);
  272. bTimerOn = FALSE;
  273. int score[MAXPLAYER];
  274. int poswinner = Id2Pos(trickwinner);
  275. // Determine who led so cards can be removed in reverse order.
  276. int playerled = handinfo.playerled;
  277. card *cardled = handinfo.cardplayed[playerled];
  278. // build up background bitmap for Glide()
  279. for (int i = (playerled + 3); i >= playerled; i--)
  280. {
  281. CDC *memdc = new CDC;
  282. CClientDC dc(this);
  283. #ifdef USE_MIRRORING
  284. SetLayout(dc.m_hDC, 0);
  285. SetLayout(dc.m_hAttribDC, 0);
  286. #endif
  287. memdc->CreateCompatibleDC(&dc);
  288. memdc->SelectObject(&card::m_bmBgnd);
  289. memdc->SelectObject(&m_BgndBrush);
  290. memdc->PatBlt(0, 0, card::dxCrd, card::dyCrd, PATCOPY);
  291. card *c = handinfo.cardplayed[i % 4];
  292. // If cards overlap, there is some extra work to do because the cards
  293. // still in player 0's or 2's hands may overlap cards that have been
  294. // played, so they have to get blted in first.
  295. if (TRUE) // bugbug should be able to check for overlap here
  296. {
  297. for (int pos = 0; pos < MAXPLAYER; pos += 2)
  298. {
  299. int mode = ((pos == 0 || bCheating) ? FACEUP : FACEDOWN);
  300. for (SLOT s = 0; s < MAXSLOT; s++)
  301. {
  302. card *c2 = p[pos]->Card(s);
  303. int x = c2->GetX() - c->GetX();
  304. int y = c2->GetY() - c->GetY();
  305. if (!c2->IsPlayed())
  306. c2->Draw(*memdc, x, y, mode, FALSE);
  307. }
  308. }
  309. }
  310. // Everyone needs to check for overlap of played cards.
  311. for (int j = playerled; j < i; j++)
  312. {
  313. card *c2 = handinfo.cardplayed[j % 4];
  314. int x = c2->GetX() - c->GetX();
  315. int y = c2->GetY() - c->GetY();
  316. c2->Draw(*memdc, x, y, FACEUP, FALSE);
  317. }
  318. delete memdc;
  319. p[poswinner]->WinCard(dc, c);
  320. c->Remove();
  321. }
  322. ResetHandInfo(trickwinner);
  323. // If there are more tricks left before we need to reshuffle,
  324. // ask the winner of this trick to start next hand, and we're done.
  325. if (--tricksleft)
  326. {
  327. p[poswinner]->SelectCardToPlay(handinfo, bCheating);
  328. if (poswinner != 0)
  329. ((local_human *)p[0])->WaitMessage(p[poswinner]->GetName());
  330. if (::cQdMoves > 0)
  331. {
  332. for (int i = 0; i < ::cQdMoves; i++)
  333. HandleMove(::moveq[i]);
  334. ::cQdMoves = 0;
  335. }
  336. return;
  337. }
  338. // Make sure sound buffer is freed up.
  339. HeartsPlaySound(OFF);
  340. // Display hearts (and queen of spades) next to whoever "won" them.
  341. int nMoonShot = EMPTY; // assume nobody shot moon
  342. for (i = 0; i < MAXPLAYER; i++)
  343. {
  344. BOOL bMoonShot;
  345. score[i] = p[i]->EvaluateScore(bMoonShot);
  346. if (bMoonShot)
  347. nMoonShot = i; // scores need to be adjusted
  348. CClientDC dc(this);
  349. #ifdef USE_MIRRORING
  350. SetLayout(dc.m_hDC, 0);
  351. SetLayout(dc.m_hAttribDC, 0);
  352. #endif
  353. p[i]->DisplayHeartsWon(dc);
  354. p[i]->SetMode(SCORING);
  355. }
  356. // adjust scores if someone collected all hearts AND queen of spades
  357. if (nMoonShot != EMPTY)
  358. {
  359. for (i = 0; i < MAXPLAYER; i++)
  360. {
  361. if (i == nMoonShot)
  362. score[i] -= 26;
  363. else
  364. score[i] += 26;
  365. p[i]->SetScore(score[i]); // adjust player score manually
  366. }
  367. }
  368. // Show score
  369. p[0]->UpdateStatus(IDS_SCORE);
  370. p[0]->SetMode(SCORING);
  371. CScoreDlg scoredlg(this, score, m_myid); // update scores in scoredlg
  372. player *pold = p[0];
  373. scoredlg.DoModal(); // display scores
  374. // If there has been a request to shut down while the score dialog
  375. // is displayed, m_FatalErrno will be non-zero.
  376. if (m_FatalErrno != 0)
  377. {
  378. p[0]->SetMode(PLAYING); // something other than SCORING...
  379. FatalError(m_FatalErrno); // so FatalError will accept it.
  380. return;
  381. }
  382. // It's possible for another player to have quit the game while
  383. // the score dialog was showing, so check that we're still
  384. // alive and well.
  385. if (p[0] != pold)
  386. return;
  387. // replace quit remote humans with computer players
  388. for (i = 1; i < MAXPLAYER; i++)
  389. {
  390. if (p[i]->HasQuit())
  391. {
  392. CString name = p[i]->GetName();
  393. int scoreLocal = p[i]->GetScore();
  394. delete p[i];
  395. p[i] = new computer(i); // check for failure
  396. CClientDC dc(this);
  397. p[i]->SetName(name, dc);
  398. p[i]->SetScore(scoreLocal);
  399. }
  400. }
  401. p[0]->SetMode(passdir == NOPASS ? DONE_SELECTING : SELECTING);
  402. if (scoredlg.IsGameOver())
  403. {
  404. GameOver();
  405. return;
  406. }
  407. Shuffle();
  408. // If there is no passing for upcoming round, we must make the changes
  409. // that HandlePassing() would normally do to start the next round.
  410. if (passdir == NOPASS)
  411. {
  412. for (i = 0; i < MAXPLAYER; i++) // everyone's DONE_SELECTING
  413. p[i]->SetMode(DONE_SELECTING);
  414. passdir = LEFT; // NEXT hand passes left
  415. tricksleft = MAXSLOT; // reset # of hands
  416. FirstMove(); // start next trick
  417. }
  418. for (i = 0; i < ::cQdMoves; i++)
  419. HandleMove(::moveq[i]);
  420. ::cQdMoves = 0;
  421. for (i = 0; i < ::cQdPasses; i++)
  422. HandlePass(::passq[i]);
  423. ::cQdPasses = 0;
  424. }
  425. /****************************************************************************
  426. CMainWindow::ResetHandInfo
  427. Note that handinfo.bHeartsBroken is not reset here -- it applies to
  428. the entire hand and is set only in FirstMove()
  429. Same with handinfo.bQSPlayed and moonshoot variables.
  430. ****************************************************************************/
  431. void CMainWindow::ResetHandInfo(int playernumber)
  432. {
  433. handinfo.playerled = playernumber;
  434. handinfo.turn = playernumber;
  435. for (int i = 0; i < MAXPLAYER; i++)
  436. handinfo.cardplayed[i] = NULL;
  437. }
  438. /****************************************************************************
  439. CMainWindow::CountClients()
  440. Count of number of clients active (including computer players)
  441. Only the GameMeister calls this, so potential clients are pos 1 to 3.
  442. ****************************************************************************/
  443. int CMainWindow::CountClients()
  444. {
  445. ASSERT(role == GAMEMEISTER);
  446. int cb = 0;
  447. for (int pos = 1; pos < MAXPLAYER; pos++)
  448. if (p[pos])
  449. cb++;
  450. return cb;
  451. }