From 60fe3ca1b51160931483534ea178e80476d1449e Mon Sep 17 00:00:00 2001 From: 11EJDE11 Date: Sun, 12 Jan 2025 02:49:37 +1300 Subject: [PATCH] Add map thumbnail preview to multiplayer game lobby map list hover --- .../Multiplayer/GameLobby/GameLobbyBase.cs | 30 ++++ .../Multiplayer/GameLobby/MapPreviewPanel.cs | 168 ++++++++++++++++++ 2 files changed, 198 insertions(+) create mode 100644 DXMainClient/DXGUI/Multiplayer/GameLobby/MapPreviewPanel.cs diff --git a/DXMainClient/DXGUI/Multiplayer/GameLobby/GameLobbyBase.cs b/DXMainClient/DXGUI/Multiplayer/GameLobby/GameLobbyBase.cs index 1e0b6585d..bf806366d 100644 --- a/DXMainClient/DXGUI/Multiplayer/GameLobby/GameLobbyBase.cs +++ b/DXMainClient/DXGUI/Multiplayer/GameLobby/GameLobbyBase.cs @@ -17,6 +17,7 @@ using DTAClient.Online.EventArguments; using ClientCore.Extensions; using TextCopy; +using System.Reflection; namespace DTAClient.DXGUI.Multiplayer.GameLobby { @@ -101,6 +102,8 @@ protected GameModeMap GameModeMap } } + private MapPreviewPanel panelMapPreview; + protected Map Map => GameModeMap?.Map; protected GameMode GameMode => GameModeMap?.GameMode; @@ -213,6 +216,13 @@ public override void Initialize() ex.Message)); } + panelMapPreview = new MapPreviewPanel(WindowManager, this.MapLoader); + panelMapPreview.Initialize(); + panelMapPreview.ClearInfo(); + panelMapPreview.Disable(); + panelMapPreview.InputEnabled = false; + AddChild(panelMapPreview); + btnLeaveGame = FindChild(nameof(btnLeaveGame)); btnLeaveGame.LeftClick += BtnLeaveGame_LeftClick; @@ -715,6 +725,7 @@ private void LbGameModeMapList_HoveredIndexChanged(object sender, EventArgs e) if (lbGameModeMapList.HoveredIndex < 0 || lbGameModeMapList.HoveredIndex >= lbGameModeMapList.ItemCount) { mapListTooltip.Text = string.Empty; + panelMapPreview.Disable(); return; } @@ -724,6 +735,25 @@ private void LbGameModeMapList_HoveredIndexChanged(object sender, EventArgs e) mapListTooltip.Text = "Original name:".L10N("Client:Main:OriginalMapName") + " " + gmm.Map.UntranslatedName; else mapListTooltip.Text = string.Empty; + + ShowMapPreviewPanelForIndex(lbGameModeMapList.HoveredIndex); + } + private void ShowMapPreviewPanelForIndex(int index) + { + if (index < 0 || index > lbGameModeMapList.ItemCount) + { + panelMapPreview.Disable(); + return; + } + panelMapPreview.Enable(); + panelMapPreview.X = lbGameModeMapList.X + lbGameModeMapList.Width; + panelMapPreview.Y = lbGameModeMapList.Y; + + XNAListBoxItem item = lbGameModeMapList.GetItem(1, index); + + GameModeMap gameModeMap = (GameModeMap)item.Tag; + + panelMapPreview.SetInfo(gameModeMap); } private void PickRandomMap() diff --git a/DXMainClient/DXGUI/Multiplayer/GameLobby/MapPreviewPanel.cs b/DXMainClient/DXGUI/Multiplayer/GameLobby/MapPreviewPanel.cs new file mode 100644 index 000000000..2a1c4af99 --- /dev/null +++ b/DXMainClient/DXGUI/Multiplayer/GameLobby/MapPreviewPanel.cs @@ -0,0 +1,168 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using ClientCore.Extensions; +using Microsoft.Xna.Framework.Graphics.PackedVector; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +using Rampastring.XNAUI; +using Rampastring.XNAUI.XNAControls; + +using DTAClient.Domain.Multiplayer; + +namespace DTAClient.DXGUI.Multiplayer.GameLobby +{ + + /// + /// A UI panel that displays information about a hosted CnCNet or LAN game. + /// + public class MapPreviewPanel : XNAPanel + { + public MapPreviewPanel(WindowManager windowManager, MapLoader mapLoader) + : base(windowManager) + { + this.mapLoader = mapLoader; + DrawMode = ControlDrawMode.UNIQUE_RENDER_TARGET; + } + + private MapLoader mapLoader; + + private XNALabel lblMapPreview; + private XNALabel lblMapName; + private XNALabel lblMapMaxPlayers; + + private GameModeMap map = null; + + private bool disposeTextures = false; + private Texture2D mapTexture = null; + private Texture2D noMapPreviewTexture = null; + + private int mapPreviewPositionY = 0; + private const int initialPanelHeight = 200; + private const int initialPanelWidth = 235; + private const int maxPreviewHeight = 150; + private const int mapPreviewMargin = 1; //border + private const int padding = 6; + + public override void Initialize() + { + ClientRectangle = new Rectangle(0, 0, initialPanelWidth, initialPanelHeight); + BackgroundTexture = AssetLoader.CreateTexture(new Color(0, 0, 0, 255), 1, 1); + PanelBackgroundDrawMode = PanelBackgroundImageDrawMode.STRETCHED; + + lblMapPreview = new XNALabel(WindowManager); + lblMapPreview.FontIndex = 1; + lblMapPreview.Text = "MAP PREVIEW".L10N("Client:Main:MapPreview"); + AddChild(lblMapPreview); + + lblMapName = new XNALabel(WindowManager); + lblMapName.FontIndex = 1; + lblMapName.Text = ""; + AddChild(lblMapName); + + lblMapMaxPlayers = new XNALabel(WindowManager); + lblMapMaxPlayers.FontIndex = 1; + lblMapMaxPlayers.Text = ""; + AddChild(lblMapMaxPlayers); + + if (AssetLoader.AssetExists("noMapPreview.png")) + noMapPreviewTexture = AssetLoader.LoadTexture("noMapPreview.png"); + + lblMapPreview.CenterOnParent(); + lblMapPreview.ClientRectangle = new Rectangle(lblMapPreview.X, padding, + lblMapPreview.Width, lblMapPreview.Height); + lblMapName.ClientRectangle = new Rectangle(padding, lblMapPreview.Y + lblMapPreview.Height + padding, + lblMapName.Width, lblMapName.Height); + lblMapMaxPlayers.ClientRectangle = new Rectangle(padding, lblMapName.Y + lblMapName.Height + padding, + lblMapMaxPlayers.Width, lblMapMaxPlayers.Height); + + base.Initialize(); + } + + public void SetInfo(GameModeMap map) + { + ClearInfo(); + + this.map = map; + + lblMapPreview.Visible = true; + lblMapName.Text = map.Map.Name; + lblMapName.Visible = true; + lblMapMaxPlayers.Text = "Max players: ".L10N("Client:Main:MaxPlayers") + map.Map.MaxPlayers.ToString(); + lblMapMaxPlayers.Y = lblMapName.Y + lblMapName.Height + padding; + lblMapMaxPlayers.Visible = true; + + mapPreviewPositionY = lblMapMaxPlayers.Y + lblMapMaxPlayers.Height + padding; + this.Height = mapPreviewPositionY + maxPreviewHeight + padding; + if (mapLoader != null && map != null) + { + mapTexture = map.Map.IsPreviewTextureCached() ? map.Map.LoadPreviewTexture() : null; + if (mapTexture == null && noMapPreviewTexture != null) + { + Debug.Assert(!noMapPreviewTexture.IsDisposed, "noMapPreviewTexture should not be disposed."); + mapTexture = noMapPreviewTexture; + disposeTextures = false; + } + else + { + disposeTextures = true; + } + } + } + + public void ClearInfo() + { + lblMapPreview.Visible = false; + lblMapName.Visible = false; + lblMapMaxPlayers.Visible = false; + + if (mapTexture != null && disposeTextures) + { + Debug.Assert(!mapTexture.IsDisposed, "mapTexture should not be disposed."); + mapTexture.Dispose(); + mapTexture = null; + } + } + + public override void Draw(GameTime gameTime) + { + base.Draw(gameTime); + + if (map != null && mapTexture != null) + RenderMapPreview(); + } + + private void RenderMapPreview() + { + // Calculate map preview area + double xRatio = (ClientRectangle.Width - (mapPreviewMargin*2)) / (double)mapTexture.Width; + double yRatio = (ClientRectangle.Height - mapPreviewPositionY) / (double)mapTexture.Height; + + double ratio = Math.Min(xRatio, yRatio); // Choose the smaller ratio for scaling + int textureWidth = (int)(mapTexture.Width * ratio); + int textureHeight = (int)(mapTexture.Height * ratio); + + // Apply max height constraint + if (textureHeight > maxPreviewHeight) + { + ratio = maxPreviewHeight / (double)mapTexture.Height; + textureHeight = maxPreviewHeight; + textureWidth = (int)(mapTexture.Width * ratio); // Recalculate width to maintain aspect ratio + } + + int texturePositionX = mapPreviewMargin + ((ClientRectangle.Width- (mapPreviewMargin*2)) / 2 - (textureWidth/2)); + int texturePositionY = mapPreviewPositionY; + + DrawTexture( + mapTexture, + new Rectangle(texturePositionX, texturePositionY, textureWidth, textureHeight), + Color.White + ); + } + } +}