BlackjackCardGame.cs 63 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664
  1. #region File Description
  2. //-----------------------------------------------------------------------------
  3. // BlackjackGame.cs
  4. //
  5. // Microsoft XNA Community Game Platform
  6. // Copyright (C) Microsoft Corporation. All rights reserved.
  7. //-----------------------------------------------------------------------------
  8. #endregion
  9. #region Using Statements
  10. using System;
  11. using System.Collections.Generic;
  12. using System.Text;
  13. using CardsFramework;
  14. using Microsoft.Xna.Framework;
  15. using System.Threading;
  16. using Microsoft.Xna.Framework.Input;
  17. using Microsoft.Xna.Framework.Graphics;
  18. using GameStateManagement;
  19. using System.Reflection;
  20. #endregion
  21. namespace Blackjack
  22. {
  23. /// <summary>
  24. /// The various possible game states.
  25. /// </summary>
  26. public enum BlackjackGameState
  27. {
  28. Shuffling,
  29. Betting,
  30. Playing,
  31. Dealing,
  32. RoundEnd,
  33. GameOver,
  34. }
  35. class BlackjackCardGame : CardsGame
  36. {
  37. #region Fields and Properties
  38. Dictionary<Player, string> playerHandValueTexts =
  39. new Dictionary<Player, string>();
  40. Dictionary<Player, string> playerSecondHandValueTexts =
  41. new Dictionary<Player, string>();
  42. private Hand deadCards = new Hand(); // stores used cards
  43. private BlackjackPlayer dealerPlayer;
  44. bool[] turnFinishedByPlayer;
  45. TimeSpan dealDuration = TimeSpan.FromMilliseconds(500);
  46. AnimatedHandGameComponent[] animatedHands;
  47. // An additional list for managing hands created when performing a split.
  48. AnimatedHandGameComponent[] animatedSecondHands;
  49. BetGameComponent betGameComponent;
  50. AnimatedHandGameComponent dealerHandComponent;
  51. Dictionary<string, Button> buttons = new Dictionary<string, Button>();
  52. Button newGame;
  53. bool showInsurance;
  54. // An offset used for drawing the second hand which appears after a split in
  55. // the correct location.
  56. Vector2 secondHandOffset =
  57. new Vector2(100 * BlackjackGame.WidthScale, 25 * BlackjackGame.HeightScale);
  58. static Vector2 ringOffset = new Vector2(0, 110);
  59. #if WINDOWS_PHONE || IOS || ANDROID
  60. Vector2 frameSize = new Vector2(162, 162);
  61. #else
  62. Vector2 frameSize = new Vector2(180, 180);
  63. #endif
  64. public BlackjackGameState State { get; set; }
  65. ScreenManager screenManager;
  66. const int maxPlayers = 3;
  67. const int minPlayers = 1;
  68. #endregion
  69. #region Initializations
  70. /// <summary>
  71. /// Creates a new instance of the <see cref="BlackjackCardGame"/> class.
  72. /// </summary>
  73. /// <param name="tableBounds">The table bounds. These serves as the bounds for
  74. /// the game's main area.</param>
  75. /// <param name="dealerPosition">Position for the dealer's deck.</param>
  76. /// <param name="placeOrder">A method that translate a player index into the
  77. /// position of his deck on the game table.</param>
  78. /// <param name="screenManager">The games <see cref="ScreenManager"/>.</param>
  79. /// <param name="theme">The game's deck theme name.</param>
  80. public BlackjackCardGame(Rectangle tableBounds, Vector2 dealerPosition,
  81. Func<int, Vector2> placeOrder, ScreenManager screenManager, string theme)
  82. : base(6, 0, CardSuit.AllSuits, CardsFramework.CardValue.NonJokers,
  83. minPlayers, maxPlayers, new BlackJackTable(ringOffset, tableBounds,
  84. dealerPosition, maxPlayers, placeOrder, theme, screenManager.Game),
  85. theme, screenManager.Game)
  86. {
  87. dealerPlayer = new BlackjackPlayer("Dealer", this);
  88. turnFinishedByPlayer = new bool[MaximumPlayers];
  89. this.screenManager = screenManager;
  90. if (animatedHands == null)
  91. {
  92. animatedHands = new AnimatedHandGameComponent[maxPlayers];
  93. }
  94. if (animatedSecondHands == null)
  95. {
  96. animatedSecondHands = new AnimatedHandGameComponent[maxPlayers];
  97. }
  98. }
  99. /// <summary>
  100. /// Performs necessary initializations.
  101. /// </summary>
  102. public void Initialize()
  103. {
  104. base.LoadContent();
  105. // Initialize a new bet component
  106. betGameComponent =
  107. new BetGameComponent(players, screenManager.input, Theme, this);
  108. Game.Components.Add(betGameComponent);
  109. // Initialize the game buttons
  110. string[] buttonsText = { "Hit", "Stand", "Double", "Split", "Insurance" };
  111. for (int buttonIndex = 0; buttonIndex < buttonsText.Length; buttonIndex++)
  112. {
  113. Button button = new Button("ButtonRegular", "ButtonPressed",
  114. screenManager.input, this)
  115. {
  116. Text = buttonsText[buttonIndex],
  117. Bounds = new Rectangle(screenManager.SafeArea.Left + 10 + buttonIndex * 110,
  118. screenManager.SafeArea.Bottom - 60,
  119. 100, 50),
  120. Font = this.Font,
  121. Visible = false,
  122. Enabled = false
  123. };
  124. buttons.Add(buttonsText[buttonIndex], button);
  125. Game.Components.Add(button);
  126. }
  127. newGame = new Button("ButtonRegular", "ButtonPressed",
  128. screenManager.input, this)
  129. {
  130. Text = "New Hand",
  131. Bounds = new Rectangle(screenManager.SafeArea.Left + 10,
  132. screenManager.SafeArea.Bottom - 60, 200, 50),
  133. Font = this.Font,
  134. Visible = false,
  135. Enabled = false
  136. };
  137. // Alter the insurance button's bounds as it is considerably larger than
  138. // the other buttons
  139. Rectangle insuranceBounds = buttons["Insurance"].Bounds;
  140. insuranceBounds.Width = 200;
  141. buttons["Insurance"].Bounds = insuranceBounds;
  142. newGame.Click += newGame_Click;
  143. Game.Components.Add(newGame);
  144. // Register to click event
  145. buttons["Hit"].Click += Hit_Click;
  146. buttons["Stand"].Click += Stand_Click;
  147. buttons["Double"].Click += Double_Click;
  148. buttons["Split"].Click += Split_Click;
  149. buttons["Insurance"].Click += Insurance_Click;
  150. }
  151. #endregion
  152. #region Update and Render
  153. /// <summary>
  154. /// Perform the game's update logic.
  155. /// </summary>
  156. /// <param name="gameTime">Time elapsed since the last call to
  157. /// this method.</param>
  158. public void Update(GameTime gameTime)
  159. {
  160. switch (State)
  161. {
  162. case BlackjackGameState.Shuffling:
  163. {
  164. ShowShuffleAnimation();
  165. } break;
  166. case BlackjackGameState.Betting:
  167. {
  168. EnableButtons(false);
  169. } break;
  170. case BlackjackGameState.Dealing:
  171. {
  172. // Deal 2 cards and start playing
  173. State = BlackjackGameState.Playing;
  174. Deal();
  175. StartPlaying();
  176. } break;
  177. case BlackjackGameState.Playing:
  178. {
  179. // Calculate players' current hand values
  180. for (int playerIndex = 0; playerIndex < players.Count; playerIndex++)
  181. {
  182. ((BlackjackPlayer)players[playerIndex]).CalculateValues();
  183. }
  184. dealerPlayer.CalculateValues();
  185. // Make sure no animations are running
  186. if (!CheckForRunningAnimations<AnimatedCardsGameComponent>())
  187. {
  188. BlackjackPlayer player =
  189. (BlackjackPlayer)GetCurrentPlayer();
  190. // If the current player is an AI player, make it play
  191. if (player is BlackjackAIPlayer)
  192. {
  193. ((BlackjackAIPlayer)player).AIPlay();
  194. }
  195. CheckRules();
  196. // If all players have finished playing, the
  197. // current round ends
  198. if (State == BlackjackGameState.Playing &&
  199. GetCurrentPlayer() == null)
  200. {
  201. EndRound();
  202. }
  203. // Update button availability according to player options
  204. SetButtonAvailability();
  205. }
  206. else
  207. EnableButtons(false);
  208. } break;
  209. case BlackjackGameState.RoundEnd:
  210. {
  211. if (dealerHandComponent.EstimatedTimeForAnimationsCompletion() == TimeSpan.Zero)
  212. {
  213. betGameComponent.CalculateBalance(dealerPlayer);
  214. // Check if there is enough money to play
  215. // then show new game option or tell the player he has lost
  216. if (((BlackjackPlayer)players[0]).Balance < 5)
  217. {
  218. EndGame();
  219. }
  220. else
  221. {
  222. newGame.Enabled = true;
  223. newGame.Visible = true;
  224. }
  225. }
  226. } break;
  227. case BlackjackGameState.GameOver:
  228. {
  229. } break;
  230. default: break;
  231. }
  232. }
  233. /// <summary>
  234. /// Shows the card shuffling animation.
  235. /// </summary>
  236. private void ShowShuffleAnimation()
  237. {
  238. // Add shuffling animation
  239. AnimatedGameComponent animationComponent = new AnimatedGameComponent(this.Game)
  240. {
  241. CurrentPosition = GameTable.DealerPosition,
  242. Visible = false
  243. };
  244. Game.Components.Add(animationComponent);
  245. animationComponent.AddAnimation(
  246. new FramesetGameComponentAnimation(cardsAssets["shuffle_" + Theme], 32, 11, frameSize)
  247. {
  248. Duration = TimeSpan.FromSeconds(1.5f),
  249. PerformBeforeStart = ShowComponent,
  250. PerformBeforSartArgs = animationComponent,
  251. PerformWhenDone = PlayShuffleAndRemoveComponent,
  252. PerformWhenDoneArgs = animationComponent
  253. });
  254. State = BlackjackGameState.Betting;
  255. }
  256. /// <summary>
  257. /// Helper method to show component
  258. /// </summary>
  259. /// <param name="obj"></param>
  260. void ShowComponent(object obj)
  261. {
  262. ((AnimatedGameComponent)obj).Visible = true;
  263. }
  264. /// <summary>
  265. /// Helper method to play shuffle sound and remove component
  266. /// </summary>
  267. /// <param name="obj"></param>
  268. void PlayShuffleAndRemoveComponent(object obj)
  269. {
  270. AudioManager.PlaySound("Shuffle");
  271. Game.Components.Remove((AnimatedGameComponent)obj);
  272. }
  273. /// <summary>
  274. /// Renders the visual elements for which the game itself is responsible.
  275. /// </summary>
  276. /// <param name="gameTime">Time passed since the last call to
  277. /// this method.</param>
  278. public void Draw(GameTime gameTime)
  279. {
  280. SpriteBatch.Begin();
  281. switch (State)
  282. {
  283. case BlackjackGameState.Playing:
  284. {
  285. ShowPlayerValues();
  286. } break;
  287. case BlackjackGameState.GameOver:
  288. {
  289. } break;
  290. case BlackjackGameState.RoundEnd:
  291. {
  292. if (dealerHandComponent.EstimatedTimeForAnimationsCompletion() == TimeSpan.Zero)
  293. {
  294. ShowDealerValue();
  295. }
  296. ShowPlayerValues();
  297. } break;
  298. default: break;
  299. }
  300. SpriteBatch.End();
  301. }
  302. /// <summary>
  303. /// Draws the dealer's hand value on the screen.
  304. /// </summary>
  305. private void ShowDealerValue()
  306. {
  307. // Calculate the value to display
  308. string dealerValue = dealerPlayer.FirstValue.ToString();
  309. if (dealerPlayer.FirstValueConsiderAce)
  310. {
  311. if (dealerPlayer.FirstValue + 10 == 21)
  312. {
  313. dealerValue = "21";
  314. }
  315. else
  316. {
  317. dealerValue += @"\" + (dealerPlayer.FirstValue + 10).ToString();
  318. }
  319. }
  320. // Draw the value
  321. Vector2 measure = Font.MeasureString(dealerValue);
  322. Vector2 position = GameTable.DealerPosition - new Vector2(measure.X + 20, 0);
  323. SpriteBatch.Draw(screenManager.BlankTexture,
  324. new Rectangle((int)position.X - 4, (int)position.Y,
  325. (int)measure.X + 8, (int)measure.Y), Color.Black);
  326. SpriteBatch.DrawString(Font, dealerValue,
  327. position, Color.White);
  328. }
  329. /// <summary>
  330. /// Draws the players' hand value on the screen.
  331. /// </summary>
  332. private void ShowPlayerValues()
  333. {
  334. Color color = Color.Black;
  335. Player currentPlayer = GetCurrentPlayer();
  336. for (int playerIndex = 0; playerIndex < players.Count; playerIndex++)
  337. {
  338. BlackjackPlayer player = (BlackjackPlayer)players[playerIndex];
  339. // The current player's hand value will be red to serve as a visual
  340. // prompt for who the active player is
  341. if (player == currentPlayer)
  342. {
  343. color = Color.Red;
  344. }
  345. else
  346. {
  347. color = Color.White;
  348. }
  349. // Calculate the values to draw
  350. string playerHandValueText;
  351. string playerSecondHandValueText = null;
  352. if (!animatedHands[playerIndex].IsAnimating)
  353. {
  354. if (player.FirstValue > 0)
  355. {
  356. playerHandValueText = player.FirstValue.ToString();
  357. // Take the fact that an ace is wither 1 or 11 into
  358. // consideration when calculating the value to display
  359. // Since the ace already counts as 1, we add 10 to get
  360. // the alternate value
  361. if (player.FirstValueConsiderAce)
  362. {
  363. if (player.FirstValue + 10 == 21)
  364. {
  365. playerHandValueText = "21";
  366. }
  367. else
  368. {
  369. playerHandValueText += @"\" +
  370. (player.FirstValue + 10).ToString();
  371. }
  372. }
  373. playerHandValueTexts[player] = playerHandValueText;
  374. }
  375. else
  376. {
  377. playerHandValueText = null;
  378. }
  379. if (player.IsSplit)
  380. {
  381. // If the player has performed a split, he has an additional
  382. // hand with its own value
  383. if (player.SecondValue > 0)
  384. {
  385. playerSecondHandValueText = player.SecondValue.ToString();
  386. if (player.SecondValueConsiderAce)
  387. {
  388. if (player.SecondValue + 10 == 21)
  389. {
  390. playerSecondHandValueText = "21";
  391. }
  392. else
  393. {
  394. playerSecondHandValueText +=
  395. @"\" + (player.SecondValue + 10).ToString();
  396. }
  397. }
  398. playerSecondHandValueTexts[player] =
  399. playerSecondHandValueText;
  400. }
  401. else
  402. {
  403. playerSecondHandValueText = null;
  404. }
  405. }
  406. }
  407. else
  408. {
  409. playerHandValueTexts.TryGetValue(player, out playerHandValueText);
  410. playerSecondHandValueTexts.TryGetValue(
  411. player, out playerSecondHandValueText);
  412. }
  413. if (player.IsSplit)
  414. {
  415. // If the player has performed a split, mark the active hand alone
  416. // with a red value
  417. color = player.CurrentHandType == HandTypes.First &&
  418. player == currentPlayer ? Color.Red : Color.White;
  419. if (playerHandValueText != null)
  420. {
  421. DrawValue(animatedHands[playerIndex], playerIndex, playerHandValueText, color);
  422. }
  423. color = player.CurrentHandType == HandTypes.Second &&
  424. player == currentPlayer ? Color.Red : Color.White;
  425. if (playerSecondHandValueText != null)
  426. {
  427. DrawValue(animatedSecondHands[playerIndex], playerIndex, playerSecondHandValueText,
  428. color);
  429. }
  430. }
  431. else
  432. {
  433. // If there is a value to draw, draw it
  434. if (playerHandValueText != null)
  435. {
  436. DrawValue(animatedHands[playerIndex], playerIndex, playerHandValueText, color);
  437. }
  438. }
  439. }
  440. }
  441. /// <summary>
  442. /// Draws the value of a player's hand above his top card.
  443. /// The value will be drawn over a black background.
  444. /// </summary>
  445. /// <param name="animatedHand">The player's hand.</param>
  446. /// <param name="place">A number representing the player's position on the
  447. /// game table.</param>
  448. /// <param name="value">The value to draw.</param>
  449. /// <param name="valueColor">The color in which to draw the value.</param>
  450. private void DrawValue(AnimatedHandGameComponent animatedHand, int place,
  451. string value, Color valueColor)
  452. {
  453. Hand hand = animatedHand.Hand;
  454. Vector2 position = GameTable.PlaceOrder(place) +
  455. animatedHand.GetCardRelativePosition(hand.Count - 1);
  456. Vector2 measure = Font.MeasureString(value);
  457. position.X += (cardsAssets["CardBack_" + Theme].Bounds.Width - measure.X) / 2;
  458. position.Y -= measure.Y + 5;
  459. SpriteBatch.Draw(screenManager.BlankTexture,
  460. new Rectangle((int)position.X - 4, (int)position.Y,
  461. (int)measure.X + 8, (int)measure.Y), Color.Black);
  462. SpriteBatch.DrawString(Font, value, position, valueColor);
  463. }
  464. #endregion
  465. #region Override Methods
  466. /// <summary>
  467. /// Adds a player to the game.
  468. /// </summary>
  469. /// <param name="player">The player to add.</param>
  470. public override void AddPlayer(Player player)
  471. {
  472. if (player is BlackjackPlayer && players.Count < MaximumPlayers)
  473. {
  474. players.Add(player);
  475. }
  476. }
  477. /// <summary>
  478. /// Gets the active player.
  479. /// </summary>
  480. /// <returns>The first payer who has placed a bet and has not
  481. /// finish playing.</returns>
  482. public override Player GetCurrentPlayer()
  483. {
  484. for (int playerIndex = 0; playerIndex < players.Count; playerIndex++)
  485. {
  486. if (((BlackjackPlayer)players[playerIndex]).MadeBet && turnFinishedByPlayer[playerIndex] == false)
  487. {
  488. return players[playerIndex];
  489. }
  490. }
  491. return null;
  492. }
  493. /// <summary>
  494. /// Calculate the value of a blackjack card.
  495. /// </summary>
  496. /// <param name="card">The card to calculate the value for.</param>
  497. /// <returns>The card's value. All card values are equal to their face number,
  498. /// except for jack/queen/king which value at 10.</returns>
  499. /// <remarks>An ace's value will be 1. Game logic will treat it as 11 where
  500. /// appropriate.</remarks>
  501. public override int CardValue(TraditionalCard card)
  502. {
  503. return Math.Min(base.CardValue(card), 10);
  504. }
  505. /// <summary>
  506. /// Deals 2 cards to each player including the dealer and adds the appropriate
  507. /// animations.
  508. /// </summary>
  509. public override void Deal()
  510. {
  511. if (State == BlackjackGameState.Playing)
  512. {
  513. TraditionalCard card;
  514. for (int dealIndex = 0; dealIndex < 2; dealIndex++)
  515. {
  516. for (int playerIndex = 0; playerIndex < players.Count; playerIndex++)
  517. {
  518. if (((BlackjackPlayer)players[playerIndex]).MadeBet)
  519. {
  520. // Deal a card to one of the players
  521. card = dealer.DealCardToHand(players[playerIndex].Hand);
  522. AddDealAnimation(card, animatedHands[playerIndex], true, dealDuration,
  523. DateTime.Now + TimeSpan.FromSeconds(
  524. dealDuration.TotalSeconds * (dealIndex * players.Count + playerIndex)));
  525. }
  526. }
  527. // Deal a card to the dealer
  528. card = dealer.DealCardToHand(dealerPlayer.Hand);
  529. AddDealAnimation(card, dealerHandComponent, dealIndex == 0, dealDuration, DateTime.Now);
  530. }
  531. }
  532. }
  533. /// <summary>
  534. /// Performs necessary initializations needed after dealing the cards in order
  535. /// to start playing.
  536. /// </summary>
  537. public override void StartPlaying()
  538. {
  539. // Check that there are enough players to start playing
  540. if ((MinimumPlayers <= players.Count && players.Count <= MaximumPlayers))
  541. {
  542. // Set up and register to gameplay events
  543. GameRule gameRule = new BustRule(players);
  544. rules.Add(gameRule);
  545. gameRule.RuleMatch += BustGameRule;
  546. gameRule = new BlackJackRule(players);
  547. rules.Add(gameRule);
  548. gameRule.RuleMatch += BlackJackGameRule;
  549. gameRule = new InsuranceRule(dealerPlayer.Hand);
  550. rules.Add(gameRule);
  551. gameRule.RuleMatch += InsuranceGameRule;
  552. // Display the hands participating in the game
  553. for (int playerIndex = 0; playerIndex < players.Count; playerIndex++)
  554. {
  555. if (((BlackjackPlayer)players[playerIndex]).MadeBet)
  556. {
  557. animatedHands[playerIndex].Visible = false;
  558. }
  559. else
  560. {
  561. animatedHands[playerIndex].Visible = true;
  562. }
  563. }
  564. }
  565. }
  566. #endregion
  567. #region Helper Methods
  568. /// <summary>
  569. /// Display an animation when a card is dealt.
  570. /// </summary>
  571. /// <param name="card">The card being dealt.</param>
  572. /// <param name="animatedHand">The animated hand into which the card
  573. /// is dealt.</param>
  574. /// <param name="flipCard">Should the card be flipped after dealing it.</param>
  575. /// <param name="duration">The animations desired duration.</param>
  576. /// <param name="startTime">The time at which the animation should
  577. /// start.</param>
  578. public void AddDealAnimation(TraditionalCard card, AnimatedHandGameComponent
  579. animatedHand, bool flipCard, TimeSpan duration, DateTime startTime)
  580. {
  581. // Get the card location and card component
  582. int cardLocationInHand = animatedHand.GetCardLocationInHand(card);
  583. AnimatedCardsGameComponent cardComponent = animatedHand.GetCardGameComponent(cardLocationInHand);
  584. // Add the transition animation
  585. cardComponent.AddAnimation(
  586. new TransitionGameComponentAnimation(GameTable.DealerPosition,
  587. animatedHand.CurrentPosition +
  588. animatedHand.GetCardRelativePosition(cardLocationInHand))
  589. {
  590. StartTime = startTime,
  591. PerformBeforeStart = ShowComponent,
  592. PerformBeforSartArgs = cardComponent,
  593. PerformWhenDone = PlayDealSound
  594. });
  595. if (flipCard)
  596. {
  597. // Add the flip animation
  598. cardComponent.AddAnimation(new FlipGameComponentAnimation
  599. {
  600. IsFromFaceDownToFaceUp = true,
  601. Duration = duration,
  602. StartTime = startTime + duration,
  603. PerformWhenDone = PlayFlipSound
  604. });
  605. }
  606. }
  607. /// <summary>
  608. /// Helper method to play deal sound
  609. /// </summary>
  610. /// <param name="obj"></param>
  611. void PlayDealSound(object obj)
  612. {
  613. AudioManager.PlaySound("Deal");
  614. }
  615. /// <summary>
  616. /// Helper method to play flip sound
  617. /// </summary>
  618. /// <param name="obj"></param>
  619. void PlayFlipSound(object obj)
  620. {
  621. AudioManager.PlaySound("Flip");
  622. }
  623. /// <summary>
  624. /// Adds an animation which displays an asset over a player's hand. The asset
  625. /// will appear above the hand and appear to "fall" on top of it.
  626. /// </summary>
  627. /// <param name="player">The player over the hand of which to place the
  628. /// animation.</param>
  629. /// <param name="assetName">Name of the asset to display above the hand.</param>
  630. /// <param name="animationHand">Which hand to put cue over.</param>
  631. /// <param name="waitForHand">Start the cue animation when the animation
  632. /// of this hand over null of the animation of the currentHand</param>
  633. void CueOverPlayerHand(BlackjackPlayer player, string assetName,
  634. HandTypes animationHand, AnimatedHandGameComponent waitForHand)
  635. {
  636. // Get the position of the relevant hand
  637. int playerIndex = players.IndexOf(player);
  638. AnimatedHandGameComponent currentAnimatedHand;
  639. Vector2 currentPosition;
  640. if (playerIndex >= 0)
  641. {
  642. switch (animationHand)
  643. {
  644. case HandTypes.First:
  645. currentAnimatedHand = animatedHands[playerIndex];
  646. currentPosition = currentAnimatedHand.CurrentPosition;
  647. break;
  648. case HandTypes.Second:
  649. currentAnimatedHand = animatedSecondHands[playerIndex];
  650. currentPosition = currentAnimatedHand.CurrentPosition +
  651. secondHandOffset;
  652. break;
  653. default:
  654. throw new Exception(
  655. "Player has an unsupported hand type.");
  656. }
  657. }
  658. else
  659. {
  660. currentAnimatedHand = dealerHandComponent;
  661. currentPosition = currentAnimatedHand.CurrentPosition;
  662. }
  663. // Add the animation component
  664. AnimatedGameComponent animationComponent =
  665. new AnimatedGameComponent(this, cardsAssets[assetName])
  666. {
  667. CurrentPosition = currentPosition,
  668. Visible = false
  669. };
  670. Game.Components.Add(animationComponent);
  671. // Calculate when to start the animation. The animation will only begin
  672. // after all hand cards finish animating
  673. TimeSpan estimatedTimeToCompleteAnimations;
  674. if (waitForHand != null)
  675. {
  676. estimatedTimeToCompleteAnimations = waitForHand.EstimatedTimeForAnimationsCompletion();
  677. }
  678. else
  679. {
  680. estimatedTimeToCompleteAnimations = currentAnimatedHand.EstimatedTimeForAnimationsCompletion();
  681. }
  682. // Add a scale effect animation
  683. animationComponent.AddAnimation(new ScaleGameComponentAnimation(2.0f, 1.0f)
  684. {
  685. StartTime =
  686. DateTime.Now + estimatedTimeToCompleteAnimations,
  687. Duration = TimeSpan.FromSeconds(1f),
  688. PerformBeforeStart = ShowComponent,
  689. PerformBeforSartArgs = animationComponent
  690. });
  691. }
  692. /// <summary>
  693. /// Ends the current round.
  694. /// </summary>
  695. private void EndRound()
  696. {
  697. RevealDealerFirstCard();
  698. DealerAI();
  699. ShowResults();
  700. State = BlackjackGameState.RoundEnd;
  701. }
  702. /// <summary>
  703. /// Causes the dealer's hand to be displayed.
  704. /// </summary>
  705. private void ShowDealerHand()
  706. {
  707. dealerHandComponent =
  708. new BlackjackAnimatedDealerHandComponent(-1, dealerPlayer.Hand, this);
  709. Game.Components.Add(dealerHandComponent);
  710. }
  711. /// <summary>
  712. /// Reveal's the dealer's hidden card.
  713. /// </summary>
  714. private void RevealDealerFirstCard()
  715. {
  716. // Iterate over all dealer cards expect for the last
  717. AnimatedCardsGameComponent cardComponent = dealerHandComponent.GetCardGameComponent(1);
  718. cardComponent.AddAnimation(new FlipGameComponentAnimation()
  719. {
  720. Duration = TimeSpan.FromSeconds(0.5),
  721. StartTime = DateTime.Now
  722. });
  723. }
  724. /// <summary>
  725. /// Present visual indication as to how the players fared in the current round.
  726. /// </summary>
  727. private void ShowResults()
  728. {
  729. // Calculate the dealer's hand value
  730. int dealerValue = dealerPlayer.FirstValue;
  731. if (dealerPlayer.FirstValueConsiderAce)
  732. {
  733. dealerValue += 10;
  734. }
  735. // Show each player's result
  736. for (int playerIndex = 0; playerIndex < players.Count; playerIndex++)
  737. {
  738. ShowResultForPlayer((BlackjackPlayer)players[playerIndex], dealerValue, HandTypes.First);
  739. if (((BlackjackPlayer)players[playerIndex]).IsSplit)
  740. {
  741. ShowResultForPlayer((BlackjackPlayer)players[playerIndex], dealerValue, HandTypes.Second);
  742. }
  743. }
  744. }
  745. /// <summary>
  746. /// Display's a player's status after the turn has ended.
  747. /// </summary>
  748. /// <param name="player">The player for which to display the status.</param>
  749. /// <param name="dealerValue">The dealer's hand value.</param>
  750. /// <param name="currentHandType">The player's hand to take into
  751. /// account.</param>
  752. private void ShowResultForPlayer(BlackjackPlayer player, int dealerValue,
  753. HandTypes currentHandType)
  754. {
  755. // Calculate the player's hand value and check his state (blackjack/bust)
  756. bool blackjack, bust;
  757. int playerValue;
  758. switch (currentHandType)
  759. {
  760. case HandTypes.First:
  761. blackjack = player.BlackJack;
  762. bust = player.Bust;
  763. playerValue = player.FirstValue;
  764. if (player.FirstValueConsiderAce)
  765. {
  766. playerValue += 10;
  767. }
  768. break;
  769. case HandTypes.Second:
  770. blackjack = player.SecondBlackJack;
  771. bust = player.SecondBust;
  772. playerValue = player.SecondValue;
  773. if (player.SecondValueConsiderAce)
  774. {
  775. playerValue += 10;
  776. }
  777. break;
  778. default:
  779. throw new Exception(
  780. "Player has an unsupported hand type.");
  781. }
  782. // The bust or blackjack state are animated independently of this method,
  783. // so only trigger different outcome indications
  784. if (player.MadeBet &&
  785. (!blackjack || (dealerPlayer.BlackJack && blackjack)) && !bust)
  786. {
  787. string assetName = GetResultAsset(player, dealerValue, playerValue);
  788. CueOverPlayerHand(player, assetName, currentHandType, dealerHandComponent);
  789. }
  790. }
  791. /// <summary>
  792. /// Return the asset name according to the result.
  793. /// </summary>
  794. /// <param name="player">The player for which to return the asset name.</param>
  795. /// <param name="dealerValue">The dealer's hand value.</param>
  796. /// <param name="playerValue">The player's hand value.</param>
  797. /// <returns>The asset name</returns>
  798. private string GetResultAsset(BlackjackPlayer player, int dealerValue, int playerValue)
  799. {
  800. string assetName;
  801. if (dealerPlayer.Bust)
  802. {
  803. assetName = "win";
  804. }
  805. else if (dealerPlayer.BlackJack)
  806. {
  807. if (player.BlackJack)
  808. {
  809. assetName = "push";
  810. }
  811. else
  812. {
  813. assetName = "lose";
  814. }
  815. }
  816. else if (playerValue < dealerValue)
  817. {
  818. assetName = "lose";
  819. }
  820. else if (playerValue > dealerValue)
  821. {
  822. assetName = "win";
  823. }
  824. else
  825. {
  826. assetName = "push";
  827. }
  828. return assetName;
  829. }
  830. /// <summary>
  831. /// Have the dealer play. The dealer hits until reaching 17+ and then
  832. /// stands.
  833. /// </summary>
  834. private void DealerAI()
  835. {
  836. // The dealer may have not need to draw additional cards after his first
  837. // two. Check if this is the case and if so end the dealer's play.
  838. dealerPlayer.CalculateValues();
  839. int dealerValue = dealerPlayer.FirstValue;
  840. if (dealerPlayer.FirstValueConsiderAce)
  841. {
  842. dealerValue += 10;
  843. }
  844. if (dealerValue > 21)
  845. {
  846. dealerPlayer.Bust = true;
  847. CueOverPlayerHand(dealerPlayer, "bust", HandTypes.First, dealerHandComponent);
  848. }
  849. else if (dealerValue == 21)
  850. {
  851. dealerPlayer.BlackJack = true;
  852. CueOverPlayerHand(dealerPlayer, "blackjack", HandTypes.First, dealerHandComponent);
  853. }
  854. if (dealerPlayer.BlackJack || dealerPlayer.Bust)
  855. {
  856. return;
  857. }
  858. // Draw cards until 17 is reached, or the dealer gets a blackjack or busts
  859. int cardsDealed = 0;
  860. while (dealerValue <= 17)
  861. {
  862. TraditionalCard card = dealer.DealCardToHand(dealerPlayer.Hand);
  863. AddDealAnimation(card, dealerHandComponent, true, dealDuration,
  864. DateTime.Now.AddMilliseconds(1000 * (cardsDealed + 1)));
  865. cardsDealed++;
  866. dealerPlayer.CalculateValues();
  867. dealerValue = dealerPlayer.FirstValue;
  868. if (dealerPlayer.FirstValueConsiderAce)
  869. {
  870. dealerValue += 10;
  871. }
  872. if (dealerValue > 21)
  873. {
  874. dealerPlayer.Bust = true;
  875. CueOverPlayerHand(dealerPlayer, "bust", HandTypes.First, dealerHandComponent);
  876. }
  877. }
  878. }
  879. /// <summary>
  880. /// Displays the hands currently in play.
  881. /// </summary>
  882. private void DisplayPlayingHands()
  883. {
  884. for (int playerIndex = 0; playerIndex < players.Count; playerIndex++)
  885. {
  886. AnimatedHandGameComponent animatedHandGameComponent =
  887. new BlackjackAnimatedPlayerHandComponent(playerIndex, players[playerIndex].Hand, this);
  888. Game.Components.Add(animatedHandGameComponent);
  889. animatedHands[playerIndex] = animatedHandGameComponent;
  890. }
  891. ShowDealerHand();
  892. }
  893. /// <summary>
  894. /// Starts a new game round.
  895. /// </summary>
  896. public void StartRound()
  897. {
  898. playerHandValueTexts.Clear();
  899. AudioManager.PlaySound("Shuffle");
  900. dealer.Shuffle();
  901. DisplayPlayingHands();
  902. State = BlackjackGameState.Shuffling;
  903. }
  904. /// <summary>
  905. /// Sets the button availability according to the options available to the
  906. /// current player.
  907. /// </summary>
  908. private void SetButtonAvailability()
  909. {
  910. BlackjackPlayer player = (BlackjackPlayer)GetCurrentPlayer();
  911. // Hide all buttons if no player is in play or the player is an AI player
  912. if (player == null || player is BlackjackAIPlayer)
  913. {
  914. EnableButtons(false);
  915. ChangeButtonsVisiblility(false);
  916. return;
  917. }
  918. // Show all buttons
  919. EnableButtons(true);
  920. ChangeButtonsVisiblility(true);
  921. // Set insurance button availability
  922. buttons["Insurance"].Visible = showInsurance;
  923. buttons["Insurance"].Enabled = showInsurance;
  924. if (player.IsSplit == false)
  925. {
  926. // Remember that the bet amount was already reduced from the balance,
  927. // so we only need to check if the player has more money than the
  928. // current bet when trying to double/split
  929. // Set double button availability
  930. if (player.BetAmount > player.Balance || player.Hand.Count != 2)
  931. {
  932. buttons["Double"].Visible = false;
  933. buttons["Double"].Enabled = false;
  934. }
  935. if (player.Hand.Count != 2 ||
  936. player.Hand[0].Value != player.Hand[1].Value ||
  937. player.BetAmount > player.Balance)
  938. {
  939. buttons["Split"].Visible = false;
  940. buttons["Split"].Enabled = false;
  941. }
  942. }
  943. else
  944. {
  945. // We've performed a split. Get the initial bet amount to check whether
  946. // or not we can double the current bet.
  947. float initialBet = player.BetAmount /
  948. ((player.Double ? 2f : 1f) + (player.SecondDouble ? 2f : 1f));
  949. // Set double button availability.
  950. if (initialBet > player.Balance || player.CurrentHand.Count != 2)
  951. {
  952. buttons["Double"].Visible = false;
  953. buttons["Double"].Enabled = false;
  954. }
  955. // Once you've split, you can't split again
  956. buttons["Split"].Visible = false;
  957. buttons["Split"].Enabled = false;
  958. }
  959. }
  960. /// <summary>
  961. /// Checks for running animations.
  962. /// </summary>
  963. /// <typeparam name="T">The type of animation to look for.</typeparam>
  964. /// <returns>True if a running animation of the desired type is found and
  965. /// false otherwise.</returns>
  966. internal bool CheckForRunningAnimations<T>() where T : AnimatedGameComponent
  967. {
  968. T animationComponent;
  969. for (int componentIndex = 0; componentIndex < Game.Components.Count; componentIndex++)
  970. {
  971. animationComponent = Game.Components[componentIndex] as T;
  972. if (animationComponent != null)
  973. {
  974. if (animationComponent.IsAnimating)
  975. return true;
  976. }
  977. }
  978. return false;
  979. }
  980. /// <summary>
  981. /// Ends the game.
  982. /// </summary>
  983. private void EndGame()
  984. {
  985. // Calculate the estimated time for all playing animations to end
  986. long estimatedTime = 0;
  987. AnimatedGameComponent animationComponent;
  988. for (int componentIndex = 0; componentIndex < Game.Components.Count; componentIndex++)
  989. {
  990. animationComponent = Game.Components[componentIndex] as AnimatedGameComponent;
  991. if (animationComponent != null)
  992. {
  993. estimatedTime = Math.Max(estimatedTime,
  994. animationComponent.EstimatedTimeForAnimationsCompletion().Ticks);
  995. }
  996. }
  997. // Add a component for an empty stalling animation. This actually acts
  998. // as a timer.
  999. Texture2D texture = this.Game.Content.Load<Texture2D>(@"Images\youlose");
  1000. animationComponent = new AnimatedGameComponent(this, texture)
  1001. {
  1002. CurrentPosition = new Vector2(
  1003. this.Game.GraphicsDevice.Viewport.Bounds.Center.X - texture.Width / 2,
  1004. this.Game.GraphicsDevice.Viewport.Bounds.Center.Y - texture.Height / 2),
  1005. Visible = false
  1006. };
  1007. this.Game.Components.Add(animationComponent);
  1008. // Add a button to return to the main menu
  1009. Rectangle bounds = this.Game.GraphicsDevice.Viewport.Bounds;
  1010. Vector2 center = new Vector2(bounds.Center.X, bounds.Center.Y);
  1011. Button backButton = new Button("ButtonRegular", "ButtonPressed",
  1012. screenManager.input, this)
  1013. {
  1014. Bounds = new Rectangle((int)center.X - 100, (int)center.Y + 80, 200, 50),
  1015. Font = this.Font,
  1016. Text = "Main Menu",
  1017. Visible = false,
  1018. Enabled = true,
  1019. };
  1020. backButton.Click += backButton_Click;
  1021. // Add stalling animation
  1022. animationComponent.AddAnimation(new AnimatedGameComponentAnimation()
  1023. {
  1024. Duration = TimeSpan.FromTicks(estimatedTime) + TimeSpan.FromSeconds(1),
  1025. PerformWhenDone = ResetGame,
  1026. PerformWhenDoneArgs = new object[] { animationComponent, backButton }
  1027. });
  1028. Game.Components.Add(backButton);
  1029. }
  1030. /// <summary>
  1031. /// Helper method to reset the game
  1032. /// </summary>
  1033. /// <param name="obj"></param>
  1034. void ResetGame(object obj)
  1035. {
  1036. object[] arr = (object[])obj;
  1037. State = BlackjackGameState.GameOver;
  1038. ((AnimatedGameComponent)arr[0]).Visible = true;
  1039. ((Button)arr[1]).Visible = true;
  1040. // Remove all unnecessary game components
  1041. for (int compontneIndex = 0; compontneIndex < Game.Components.Count; )
  1042. {
  1043. if ((Game.Components[compontneIndex] != ((AnimatedGameComponent)arr[0]) &&
  1044. Game.Components[compontneIndex] != ((Button)arr[1])) &&
  1045. (Game.Components[compontneIndex] is BetGameComponent ||
  1046. Game.Components[compontneIndex] is AnimatedGameComponent ||
  1047. Game.Components[compontneIndex] is Button))
  1048. {
  1049. Game.Components.RemoveAt(compontneIndex);
  1050. }
  1051. else
  1052. compontneIndex++;
  1053. }
  1054. }
  1055. /// <summary>
  1056. /// Finishes the current turn.
  1057. /// </summary>
  1058. private void FinishTurn()
  1059. {
  1060. // Remove all unnecessary components
  1061. for (int componentIndex = 0; componentIndex < Game.Components.Count; componentIndex++)
  1062. {
  1063. if (!(Game.Components[componentIndex] is GameTable ||
  1064. Game.Components[componentIndex] is BlackjackCardGame ||
  1065. Game.Components[componentIndex] is BetGameComponent ||
  1066. Game.Components[componentIndex] is Button ||
  1067. Game.Components[componentIndex] is ScreenManager ||
  1068. Game.Components[componentIndex] is InputHelper))
  1069. {
  1070. if (Game.Components[componentIndex] is AnimatedCardsGameComponent)
  1071. {
  1072. AnimatedCardsGameComponent animatedCard =
  1073. (Game.Components[componentIndex] as AnimatedCardsGameComponent);
  1074. animatedCard.AddAnimation(
  1075. new TransitionGameComponentAnimation(animatedCard.CurrentPosition,
  1076. new Vector2(animatedCard.CurrentPosition.X, this.Game.GraphicsDevice.Viewport.Height))
  1077. {
  1078. Duration = TimeSpan.FromSeconds(0.40),
  1079. PerformWhenDone = RemoveComponent,
  1080. PerformWhenDoneArgs = animatedCard
  1081. });
  1082. }
  1083. else
  1084. {
  1085. Game.Components.RemoveAt(componentIndex);
  1086. componentIndex--;
  1087. }
  1088. }
  1089. }
  1090. // Reset player values
  1091. for (int playerIndex = 0; playerIndex < players.Count; playerIndex++)
  1092. {
  1093. (players[playerIndex] as BlackjackPlayer).ResetValues();
  1094. players[playerIndex].Hand.DealCardsToHand(deadCards, players[playerIndex].Hand.Count);
  1095. turnFinishedByPlayer[playerIndex] = false;
  1096. animatedHands[playerIndex] = null;
  1097. animatedSecondHands[playerIndex] = null;
  1098. }
  1099. // Reset the bet component
  1100. betGameComponent.Reset();
  1101. betGameComponent.Enabled = true;
  1102. // Reset dealer
  1103. dealerPlayer.Hand.DealCardsToHand(deadCards, dealerPlayer.Hand.Count);
  1104. dealerPlayer.ResetValues();
  1105. // Reset rules
  1106. rules.Clear();
  1107. }
  1108. /// <summary>
  1109. /// Helper method to remove component
  1110. /// </summary>
  1111. /// <param name="obj"></param>
  1112. void RemoveComponent(object obj)
  1113. {
  1114. Game.Components.Remove((AnimatedGameComponent)obj);
  1115. }
  1116. /// <summary>
  1117. /// Performs the "Stand" move for the current player.
  1118. /// </summary>
  1119. public void Stand()
  1120. {
  1121. BlackjackPlayer player = (BlackjackPlayer)GetCurrentPlayer();
  1122. if (player == null)
  1123. return;
  1124. // If the player only has one hand, his turn ends. Otherwise, he now plays
  1125. // using his next hand
  1126. if (player.IsSplit == false)
  1127. {
  1128. turnFinishedByPlayer[players.IndexOf(player)] = true;
  1129. }
  1130. else
  1131. {
  1132. switch (player.CurrentHandType)
  1133. {
  1134. case HandTypes.First:
  1135. if (player.SecondBlackJack)
  1136. {
  1137. turnFinishedByPlayer[players.IndexOf(player)] = true;
  1138. }
  1139. else
  1140. {
  1141. player.CurrentHandType = HandTypes.Second;
  1142. }
  1143. break;
  1144. case HandTypes.Second:
  1145. turnFinishedByPlayer[players.IndexOf(player)] = true;
  1146. break;
  1147. default:
  1148. throw new Exception(
  1149. "Player has an unsupported hand type.");
  1150. }
  1151. }
  1152. }
  1153. /// <summary>
  1154. /// Performs the "Split" move for the current player.
  1155. /// This includes adding the animations which shows the first hand splitting
  1156. /// into two.
  1157. /// </summary>
  1158. public void Split()
  1159. {
  1160. BlackjackPlayer player = (BlackjackPlayer)GetCurrentPlayer();
  1161. int playerIndex = players.IndexOf(player);
  1162. player.InitializeSecondHand();
  1163. Vector2 sourcePosition = animatedHands[playerIndex].GetCardGameComponent(1).CurrentPosition;
  1164. Vector2 targetPosition = animatedHands[playerIndex].GetCardGameComponent(0).CurrentPosition +
  1165. secondHandOffset;
  1166. // Create an animation moving the top card to the second hand location
  1167. AnimatedGameComponentAnimation animation = new TransitionGameComponentAnimation(sourcePosition,
  1168. targetPosition)
  1169. {
  1170. StartTime = DateTime.Now,
  1171. Duration = TimeSpan.FromSeconds(0.5f)
  1172. };
  1173. // Actually perform the split
  1174. player.SplitHand();
  1175. // Add additional chip stack for the second hand
  1176. betGameComponent.AddChips(playerIndex, player.BetAmount,
  1177. false, true);
  1178. // Initialize visual representation of the second hand
  1179. animatedSecondHands[playerIndex] =
  1180. new BlackjackAnimatedPlayerHandComponent(playerIndex, secondHandOffset,
  1181. player.SecondHand, this);
  1182. Game.Components.Add(animatedSecondHands[playerIndex]);
  1183. AnimatedCardsGameComponent animatedGameComponet = animatedSecondHands[playerIndex].GetCardGameComponent(0);
  1184. animatedGameComponet.IsFaceDown = false;
  1185. animatedGameComponet.AddAnimation(animation);
  1186. // Deal an additional cards to each of the new hands
  1187. TraditionalCard card = dealer.DealCardToHand(player.Hand);
  1188. AddDealAnimation(card, animatedHands[playerIndex], true, dealDuration,
  1189. DateTime.Now + animation.EstimatedTimeForAnimationCompletion);
  1190. card = dealer.DealCardToHand(player.SecondHand);
  1191. AddDealAnimation(card, animatedSecondHands[playerIndex], true, dealDuration,
  1192. DateTime.Now + animation.EstimatedTimeForAnimationCompletion +
  1193. dealDuration);
  1194. }
  1195. /// <summary>
  1196. /// Performs the "Double" move for the current player.
  1197. /// </summary>
  1198. public void Double()
  1199. {
  1200. BlackjackPlayer player = (BlackjackPlayer)GetCurrentPlayer();
  1201. int playerIndex = players.IndexOf(player);
  1202. switch (player.CurrentHandType)
  1203. {
  1204. case HandTypes.First:
  1205. player.Double = true;
  1206. float betAmount = player.BetAmount;
  1207. if (player.IsSplit)
  1208. {
  1209. betAmount /= 2f;
  1210. }
  1211. betGameComponent.AddChips(playerIndex, betAmount, false, false);
  1212. break;
  1213. case HandTypes.Second:
  1214. player.SecondDouble = true;
  1215. if (player.Double == false)
  1216. {
  1217. // The bet is evenly spread between both hands, add one half
  1218. betGameComponent.AddChips(playerIndex, player.BetAmount / 2f,
  1219. false, true);
  1220. }
  1221. else
  1222. {
  1223. // The first hand's bet is double, add one third of the total
  1224. betGameComponent.AddChips(playerIndex, player.BetAmount / 3f,
  1225. false, true);
  1226. }
  1227. break;
  1228. default:
  1229. throw new Exception(
  1230. "Player has an unsupported hand type.");
  1231. }
  1232. Hit();
  1233. Stand();
  1234. }
  1235. /// <summary>
  1236. /// Performs the "Hit" move for the current player.
  1237. /// </summary>
  1238. public void Hit()
  1239. {
  1240. BlackjackPlayer player = (BlackjackPlayer)GetCurrentPlayer();
  1241. if (player == null)
  1242. return;
  1243. int playerIndex = players.IndexOf(player);
  1244. // Draw a card to the appropriate hand
  1245. switch (player.CurrentHandType)
  1246. {
  1247. case HandTypes.First:
  1248. TraditionalCard card = dealer.DealCardToHand(player.Hand);
  1249. AddDealAnimation(card, animatedHands[playerIndex], true,
  1250. dealDuration, DateTime.Now);
  1251. break;
  1252. case HandTypes.Second:
  1253. card = dealer.DealCardToHand(player.SecondHand);
  1254. AddDealAnimation(card, animatedSecondHands[playerIndex], true,
  1255. dealDuration, DateTime.Now);
  1256. break;
  1257. default:
  1258. throw new Exception(
  1259. "Player has an unsupported hand type.");
  1260. }
  1261. }
  1262. /// <summary>
  1263. /// Changes the visiblility of most game buttons.
  1264. /// </summary>
  1265. /// <param name="visible">True to make the buttons visible, false to make
  1266. /// them invisible.</param>
  1267. void ChangeButtonsVisiblility(bool visible)
  1268. {
  1269. buttons["Hit"].Visible = visible;
  1270. buttons["Stand"].Visible = visible;
  1271. buttons["Double"].Visible = visible;
  1272. buttons["Split"].Visible = visible;
  1273. buttons["Insurance"].Visible = visible;
  1274. }
  1275. /// <summary>
  1276. /// Enables or disable most game buttons.
  1277. /// </summary>
  1278. /// <param name="enabled">True to enable the buttons , false to
  1279. /// disable them.</param>
  1280. void EnableButtons(bool enabled)
  1281. {
  1282. buttons["Hit"].Enabled = enabled;
  1283. buttons["Stand"].Enabled = enabled;
  1284. buttons["Double"].Enabled = enabled;
  1285. buttons["Split"].Enabled = enabled;
  1286. buttons["Insurance"].Enabled = enabled;
  1287. }
  1288. /// <summary>
  1289. /// Add an indication that the player has passed on the current round.
  1290. /// </summary>
  1291. /// <param name="indexPlayer">The player's index.</param>
  1292. public void ShowPlayerPass(int indexPlayer)
  1293. {
  1294. // Add animation component
  1295. AnimatedGameComponent passComponent = new AnimatedGameComponent(this, cardsAssets["pass"])
  1296. {
  1297. CurrentPosition = GameTable.PlaceOrder(indexPlayer),
  1298. Visible = false
  1299. };
  1300. Game.Components.Add(passComponent);
  1301. // Hide insurance button only when the first payer passes
  1302. Action<object> performWhenDone = null;
  1303. if (indexPlayer == 0)
  1304. {
  1305. performWhenDone = HideInshurance;
  1306. }
  1307. // Add scale animation for the pass "card"
  1308. passComponent.AddAnimation(new ScaleGameComponentAnimation(2.0f, 1.0f)
  1309. {
  1310. AnimationCycles = 1,
  1311. PerformBeforeStart = ShowComponent,
  1312. PerformBeforSartArgs = passComponent,
  1313. StartTime = DateTime.Now,
  1314. Duration = TimeSpan.FromSeconds(1),
  1315. PerformWhenDone = performWhenDone
  1316. });
  1317. }
  1318. /// <summary>
  1319. /// Helper method to hide insurance
  1320. /// </summary>
  1321. /// <param name="obj"></param>
  1322. void HideInshurance(object obj)
  1323. {
  1324. showInsurance = false;
  1325. }
  1326. #endregion
  1327. #region Event Handlers
  1328. /// <summary>
  1329. /// Shows the insurance button if the first player can afford insurance.
  1330. /// </summary>
  1331. /// <param name="sender">The sender.</param>
  1332. /// <param name="e">The <see cref="System.EventArgs"/> instance containing
  1333. /// the event data.</param>
  1334. void InsuranceGameRule(object sender, EventArgs e)
  1335. {
  1336. BlackjackPlayer player = (BlackjackPlayer)players[0];
  1337. if (player.Balance >= player.BetAmount / 2)
  1338. {
  1339. showInsurance = true;
  1340. }
  1341. }
  1342. /// <summary>
  1343. /// Shows the bust visual cue after the bust rule has been matched.
  1344. /// </summary>
  1345. /// <param name="sender">The sender.</param>
  1346. /// <param name="e">The <see cref="System.EventArgs"/> instance containing
  1347. /// the event data.</param>
  1348. void BustGameRule(object sender, EventArgs e)
  1349. {
  1350. showInsurance = false;
  1351. BlackjackGameEventArgs args = (e as BlackjackGameEventArgs);
  1352. BlackjackPlayer player = (BlackjackPlayer)args.Player;
  1353. CueOverPlayerHand(player, "bust", args.Hand, null);
  1354. switch (args.Hand)
  1355. {
  1356. case HandTypes.First:
  1357. player.Bust = true;
  1358. if (player.IsSplit && !player.SecondBlackJack)
  1359. {
  1360. player.CurrentHandType = HandTypes.Second;
  1361. }
  1362. else
  1363. {
  1364. turnFinishedByPlayer[players.IndexOf(player)] = true;
  1365. }
  1366. break;
  1367. case HandTypes.Second:
  1368. player.SecondBust = true;
  1369. turnFinishedByPlayer[players.IndexOf(player)] = true;
  1370. break;
  1371. default:
  1372. throw new Exception(
  1373. "Player has an unsupported hand type.");
  1374. }
  1375. }
  1376. /// <summary>
  1377. /// Shows the blackjack visual cue after the blackjack rule has been matched.
  1378. /// </summary>
  1379. /// <param name="sender">The sender.</param>
  1380. /// <param name="e">The <see cref="System.EventArgs"/> instance containing
  1381. /// the event data.</param>
  1382. void BlackJackGameRule(object sender, EventArgs e)
  1383. {
  1384. showInsurance = false;
  1385. BlackjackGameEventArgs args = (e as BlackjackGameEventArgs);
  1386. BlackjackPlayer player = (BlackjackPlayer)args.Player;
  1387. CueOverPlayerHand(player, "blackjack", args.Hand, null);
  1388. switch (args.Hand)
  1389. {
  1390. case HandTypes.First:
  1391. player.BlackJack = true;
  1392. if (player.IsSplit)
  1393. {
  1394. player.CurrentHandType = HandTypes.Second;
  1395. }
  1396. else
  1397. {
  1398. turnFinishedByPlayer[players.IndexOf(player)] = true;
  1399. }
  1400. break;
  1401. case HandTypes.Second:
  1402. player.SecondBlackJack = true;
  1403. if (player.CurrentHandType == HandTypes.Second)
  1404. {
  1405. turnFinishedByPlayer[players.IndexOf(player)] = true;
  1406. }
  1407. break;
  1408. default:
  1409. throw new Exception(
  1410. "Player has an unsupported hand type.");
  1411. }
  1412. }
  1413. /// <summary>
  1414. /// Handles the Click event of the insurance button.
  1415. /// </summary>
  1416. /// <param name="sender">The source of the event.</param>
  1417. /// <param name="e">The
  1418. /// <see cref="System.EventArgs"/> instance containing the event data.</param>
  1419. void Insurance_Click(object sender, EventArgs e)
  1420. {
  1421. BlackjackPlayer player = (BlackjackPlayer)GetCurrentPlayer();
  1422. if (player == null)
  1423. return;
  1424. player.IsInsurance = true;
  1425. player.Balance -= player.BetAmount / 2f;
  1426. betGameComponent.AddChips(players.IndexOf(player), player.BetAmount / 2, true, false);
  1427. showInsurance = false;
  1428. }
  1429. /// <summary>
  1430. /// Handles the Click event of the new game button.
  1431. /// </summary>
  1432. /// <param name="sender">The source of the event.</param>
  1433. /// <param name="e">The
  1434. /// <see cref="System.EventArgs"/> instance containing the event data.</param>
  1435. void newGame_Click(object sender, EventArgs e)
  1436. {
  1437. FinishTurn();
  1438. StartRound();
  1439. newGame.Enabled = false;
  1440. newGame.Visible = false;
  1441. }
  1442. /// <summary>
  1443. /// Handles the Click event of the hit button.
  1444. /// </summary>
  1445. /// <param name="sender">The source of the event.</param>
  1446. /// <param name="e">The
  1447. /// <see cref="System.EventArgs"/> instance containing the event data.</param>
  1448. void Hit_Click(object sender, EventArgs e)
  1449. {
  1450. Hit();
  1451. showInsurance = false;
  1452. }
  1453. /// <summary>
  1454. /// Handles the Click event of the stand button.
  1455. /// </summary>
  1456. /// <param name="sender">The source of the event.</param>
  1457. /// <param name="e">The
  1458. /// <see cref="System.EventArgs"/> instance containing the event data.</param>
  1459. void Stand_Click(object sender, EventArgs e)
  1460. {
  1461. Stand();
  1462. showInsurance = false;
  1463. }
  1464. /// <summary>
  1465. /// Handles the Click event of the double button.
  1466. /// </summary>
  1467. /// <param name="sender">The source of the event.</param>
  1468. /// <param name="e">The
  1469. /// <see cref="System.EventArgs"/> instance containing the event data.</param>
  1470. void Double_Click(object sender, EventArgs e)
  1471. {
  1472. Double();
  1473. showInsurance = false;
  1474. }
  1475. /// <summary>
  1476. /// Handles the Click event of the split button.
  1477. /// </summary>
  1478. /// <param name="sender">The source of the event.</param>
  1479. /// <param name="e">The
  1480. /// <see cref="System.EventArgs"/> instance containing the event data.</param>
  1481. void Split_Click(object sender, EventArgs e)
  1482. {
  1483. Split();
  1484. showInsurance = false;
  1485. }
  1486. /// <summary>
  1487. /// Handles the Click event of the back button.
  1488. /// </summary>
  1489. /// <param name="sender">The source of the event.</param>
  1490. /// <param name="e">>The
  1491. /// <see cref="System.EventArgs"/> instance containing the event data.</param>
  1492. void backButton_Click(object sender, EventArgs e)
  1493. {
  1494. // Remove all unnecessary components
  1495. for (int componentIndex = 0; componentIndex < Game.Components.Count; componentIndex++)
  1496. {
  1497. if (!(Game.Components[componentIndex] is ScreenManager))
  1498. {
  1499. Game.Components.RemoveAt(componentIndex);
  1500. componentIndex--;
  1501. }
  1502. }
  1503. foreach (GameScreen screen in screenManager.GetScreens())
  1504. screen.ExitScreen();
  1505. screenManager.AddScreen(new BackgroundScreen(), null);
  1506. screenManager.AddScreen(new MainMenuScreen(), null);
  1507. }
  1508. #endregion
  1509. }
  1510. }