Leaked source code of windows server 2003
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.

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