SessionBrowserScreen.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. //-----------------------------------------------------------------------------
  2. // SessionBrowserScreen.cs
  3. //
  4. // Shows available sessions and allows hosting a new game.
  5. //-----------------------------------------------------------------------------
  6. using System;
  7. using System.Collections.Generic;
  8. using Microsoft.Xna.Framework;
  9. using Microsoft.Xna.Framework.Net;
  10. using Microsoft.Xna.Framework.Graphics;
  11. using Microsoft.Xna.Framework.Input;
  12. using Microsoft.Xna.Framework.Input.Touch;
  13. using GameStateManagement;
  14. using CardsFramework;
  15. namespace Blackjack
  16. {
  17. class SessionBrowserScreen : MenuScreen
  18. {
  19. MenuEntry hostGameMenuEntry;
  20. MenuEntry refreshMenuEntry;
  21. MenuEntry backMenuEntry;
  22. List<MenuEntry> sessionEntries = new List<MenuEntry>();
  23. List<AvailableNetworkSession> availableSessions = new List<AvailableNetworkSession>();
  24. TimeSpan timeSinceLastSearch = TimeSpan.Zero;
  25. const float AutoRefreshInterval = 5.0f; // Auto-refresh every 5 seconds
  26. bool isSearching = false;
  27. public SessionBrowserScreen()
  28. : base(Resources.JoinOrHostGame)
  29. {
  30. // Enable tap gestures for mobile input
  31. EnabledGestures = GestureType.Tap;
  32. }
  33. public override void LoadContent()
  34. {
  35. hostGameMenuEntry = new MenuEntry(Resources.HostNewGame);
  36. refreshMenuEntry = new MenuEntry(Resources.Refresh);
  37. backMenuEntry = new MenuEntry(Resources.Back);
  38. hostGameMenuEntry.Selected += HostGameMenuEntrySelected;
  39. refreshMenuEntry.Selected += RefreshMenuEntrySelected;
  40. backMenuEntry.Selected += OnCancel;
  41. MenuEntries.Add(hostGameMenuEntry);
  42. MenuEntries.Add(refreshMenuEntry);
  43. MenuEntries.Add(backMenuEntry);
  44. // Start async session discovery
  45. BeginFindSessions();
  46. base.LoadContent();
  47. }
  48. void RefreshMenuEntrySelected(object sender, EventArgs e)
  49. {
  50. // Manually trigger a refresh
  51. if (!isSearching)
  52. {
  53. BeginFindSessions();
  54. timeSinceLastSearch = TimeSpan.Zero;
  55. }
  56. }
  57. void HostGameMenuEntrySelected(object sender, EventArgs e)
  58. {
  59. // Host a new session and go to lobby
  60. // Use SystemLink for local network testing (PlayerMatch requires online services)
  61. var asyncResult = NetworkSession.CreateAsync(
  62. NetworkSessionType.SystemLink,
  63. BlackjackConstants.MinPlayers, // local gamers
  64. BlackjackConstants.MaxPlayers, // max gamers
  65. 0, // private slots
  66. null);
  67. var busyScreen = new NetworkBusyScreen<NetworkSession>(asyncResult);
  68. busyScreen.OperationCompleted += (s, evt) =>
  69. {
  70. var networkSession = evt.Result as NetworkSession;
  71. if (networkSession != null)
  72. {
  73. NetworkSessionComponent.Create(ScreenManager, networkSession);
  74. ScreenManager.AddScreen(new BlackjackLobbyScreen(networkSession), null);
  75. }
  76. else
  77. {
  78. ScreenManager.AddScreen(new MessageBoxScreen(Resources.FailedToCreateSession), null);
  79. }
  80. };
  81. ScreenManager.AddScreen(busyScreen, null);
  82. }
  83. void JoinSessionMenuEntrySelected(object sender, EventArgs e)
  84. {
  85. var entry = sender as AvailableSessionMenuEntry;
  86. var availableSession = entry?.AvailableSession;
  87. if (availableSession != null)
  88. {
  89. var asyncResult = NetworkSession.JoinAsync(availableSession);
  90. var busyScreen = new NetworkBusyScreen<NetworkSession>(asyncResult);
  91. busyScreen.OperationCompleted += (s, evt) =>
  92. {
  93. var networkSession = evt.Result as NetworkSession;
  94. if (networkSession != null)
  95. {
  96. NetworkSessionComponent.Create(ScreenManager, networkSession);
  97. ScreenManager.AddScreen(new BlackjackLobbyScreen(networkSession), null);
  98. }
  99. else
  100. {
  101. ScreenManager.AddScreen(new MessageBoxScreen(Resources.FailedToJoinSession), null);
  102. }
  103. };
  104. ScreenManager.AddScreen(busyScreen, null);
  105. }
  106. }
  107. void RefreshSessionList()
  108. {
  109. // Remove all existing session entries from MenuEntries
  110. foreach (var entry in sessionEntries)
  111. {
  112. MenuEntries.Remove(entry);
  113. }
  114. sessionEntries.Clear();
  115. // Don't add session entries to MenuEntries - we'll draw them separately
  116. // Just create the entries for the available sessions
  117. foreach (var session in availableSessions)
  118. {
  119. var entry = new AvailableSessionMenuEntry(session);
  120. entry.Selected += JoinSessionMenuEntrySelected;
  121. sessionEntries.Add(entry);
  122. }
  123. }
  124. public override void Update(GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)
  125. {
  126. base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
  127. // Auto-refresh session list periodically
  128. if (!isSearching && !coveredByOtherScreen)
  129. {
  130. timeSinceLastSearch += gameTime.ElapsedGameTime;
  131. if (timeSinceLastSearch.TotalSeconds >= AutoRefreshInterval)
  132. {
  133. BeginFindSessions();
  134. timeSinceLastSearch = TimeSpan.Zero;
  135. }
  136. }
  137. // Update refresh button text to show status
  138. if (refreshMenuEntry != null)
  139. {
  140. refreshMenuEntry.Text = isSearching ? Resources.NetworkBusy : Resources.Refresh;
  141. }
  142. }
  143. public override void HandleInput(InputState inputState)
  144. {
  145. // Handle input for session entries using platform-specific input handling
  146. bool isClicked = false;
  147. Vector2 clickPosition = Vector2.Zero;
  148. // Use transformed cursor location for all input types (handles scaling/letterboxing)
  149. Vector2 transformedCursorPos = inputState.CurrentCursorLocation;
  150. if (UIUtility.IsDesktop)
  151. {
  152. // Handle mouse click (button was pressed last frame, released this frame)
  153. if (inputState.IsLeftMouseButtonClicked())
  154. {
  155. isClicked = true;
  156. clickPosition = transformedCursorPos;
  157. }
  158. }
  159. else if (UIUtility.IsMobile)
  160. {
  161. // Handle touch input with gestures
  162. if (inputState.Gestures.Count > 0 && inputState.Gestures[0].GestureType == Microsoft.Xna.Framework.Input.Touch.GestureType.Tap)
  163. {
  164. isClicked = true;
  165. // Use transformed cursor position (already set by InputState)
  166. clickPosition = transformedCursorPos;
  167. }
  168. }
  169. // Handle session selection if clicked
  170. if (isClicked && availableSessions.Count > 0)
  171. {
  172. SpriteFont font = ScreenManager.Font;
  173. Vector2 headerPosition = new Vector2(ScreenManager.SafeArea.Left + 100, ScreenManager.SafeArea.Top + 150);
  174. Vector2 sessionPosition = new Vector2(ScreenManager.SafeArea.Left + 120, headerPosition.Y + font.LineSpacing * 1.5f);
  175. float scale = 0.85f;
  176. for (int i = 0; i < sessionEntries.Count; i++)
  177. {
  178. var sessionEntry = sessionEntries[i];
  179. Vector2 textSize = font.MeasureString(sessionEntry.Text) * scale;
  180. Rectangle hitBox = new Rectangle(
  181. (int)sessionPosition.X,
  182. (int)sessionPosition.Y,
  183. (int)textSize.X,
  184. (int)(font.LineSpacing * scale)
  185. );
  186. if (hitBox.Contains((int)clickPosition.X, (int)clickPosition.Y))
  187. {
  188. // Clicked on this session - join it
  189. JoinSessionMenuEntrySelected(sessionEntry, EventArgs.Empty);
  190. return;
  191. }
  192. sessionPosition.Y += font.LineSpacing * scale + 10;
  193. }
  194. }
  195. // Let base class handle button input
  196. base.HandleInput(inputState);
  197. }
  198. protected override void OnCancel(PlayerIndex playerIndex)
  199. {
  200. // Exit all screens and return to main menu (without BackgroundScreen to avoid logo)
  201. foreach (GameScreen screen in ScreenManager.GetScreens())
  202. screen.ExitScreen();
  203. ScreenManager.AddScreen(new BackgroundScreen(), null);
  204. ScreenManager.AddScreen(new MainMenuScreen(), null);
  205. }
  206. public override void Draw(GameTime gameTime)
  207. {
  208. // Draw solid background to cover any BackgroundScreen logo
  209. ScreenManager.GraphicsDevice.Clear(new Color(50, 20, 20)); // Dark red background
  210. // Draw menu entries (bottom buttons) and title from base class
  211. base.Draw(gameTime);
  212. SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
  213. SpriteFont font = ScreenManager.Font;
  214. spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, ScreenManager.GlobalTransformation);
  215. // Draw "Available Games:" section header and session list
  216. if (availableSessions.Count > 0)
  217. {
  218. // Draw header
  219. Vector2 headerPosition = new Vector2(ScreenManager.SafeArea.Left + 100, ScreenManager.SafeArea.Top + 150);
  220. spriteBatch.DrawString(font, Resources.AvailableGames, headerPosition, Color.Yellow, 0f, Vector2.Zero, 0.9f, SpriteEffects.None, 0f);
  221. // Draw session entries in a vertical list below the header
  222. Vector2 sessionPosition = new Vector2(ScreenManager.SafeArea.Left + 120, headerPosition.Y + font.LineSpacing * 1.5f);
  223. for (int i = 0; i < sessionEntries.Count; i++)
  224. {
  225. var sessionEntry = sessionEntries[i];
  226. Color color = Color.White;
  227. float scale = 0.85f;
  228. // Draw the session entry text
  229. string sessionText = sessionEntry.Text;
  230. spriteBatch.DrawString(font, sessionText, sessionPosition, color, 0f, Vector2.Zero, scale, SpriteEffects.None, 0f);
  231. // Move down for next entry
  232. sessionPosition.Y += font.LineSpacing * scale + 10; // Add some padding
  233. }
  234. }
  235. // Draw status at bottom left
  236. Vector2 statusPosition = new Vector2(ScreenManager.SafeArea.Left + 50, ScreenManager.SafeArea.Bottom - 80);
  237. var pluralGames = availableSessions.Count == 0 || availableSessions.Count > 1 ? "s" : "";
  238. string statusText = isSearching
  239. ? Resources.SearchingForGames
  240. : string.Format(Resources.FoundGames, availableSessions.Count, pluralGames);
  241. spriteBatch.DrawString(font, statusText, statusPosition, Color.LightGreen, 0f, Vector2.Zero, 0.8f, SpriteEffects.None, 0f);
  242. // Show auto-refresh timer
  243. if (!isSearching)
  244. {
  245. int secondsUntilRefresh = (int)(AutoRefreshInterval - timeSinceLastSearch.TotalSeconds);
  246. string timerText = string.Format(Resources.AutoRefreshIn, secondsUntilRefresh);
  247. statusPosition.Y += font.LineSpacing;
  248. spriteBatch.DrawString(font, timerText, statusPosition, Color.Gray, 0f, Vector2.Zero, 0.7f, SpriteEffects.None, 0f);
  249. }
  250. spriteBatch.End();
  251. }
  252. // Helper class for session info
  253. // Deprecated: AvailableSession replaced by AvailableNetworkSession
  254. void BeginFindSessions()
  255. {
  256. if (isSearching)
  257. return; // Already searching
  258. isSearching = true;
  259. var asyncResult = NetworkSession.FindAsync(
  260. NetworkSessionType.SystemLink,
  261. 1, // local gamers
  262. null);
  263. var busyScreen = new NetworkBusyScreen<AvailableNetworkSessionCollection>(asyncResult);
  264. busyScreen.OperationCompleted += (s, evt) =>
  265. {
  266. isSearching = false;
  267. var foundSessions = evt.Result as AvailableNetworkSessionCollection;
  268. availableSessions.Clear();
  269. if (foundSessions != null)
  270. {
  271. foreach (var session in foundSessions)
  272. availableSessions.Add(session);
  273. }
  274. RefreshSessionList();
  275. };
  276. ScreenManager.AddScreen(busyScreen, null);
  277. }
  278. }
  279. }