diff --git a/OpenRA.Game/Game.cs b/OpenRA.Game/Game.cs index d570f0e5ed..b366bd4fd9 100755 --- a/OpenRA.Game/Game.cs +++ b/OpenRA.Game/Game.cs @@ -190,7 +190,8 @@ namespace OpenRA else if (orderManager.NetFrameNumber == 0) orderManager.LastTickTime = Environment.TickCount; - + + world.TickRender(worldRenderer); viewport.Tick(); } } diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj index 6f7b5cbd0c..19947ca9e6 100644 --- a/OpenRA.Game/OpenRA.Game.csproj +++ b/OpenRA.Game/OpenRA.Game.csproj @@ -217,6 +217,7 @@ + diff --git a/OpenRA.Game/Traits/TraitsInterfaces.cs b/OpenRA.Game/Traits/TraitsInterfaces.cs index 42b75205ab..d04f5d5bde 100755 --- a/OpenRA.Game/Traits/TraitsInterfaces.cs +++ b/OpenRA.Game/Traits/TraitsInterfaces.cs @@ -34,6 +34,7 @@ namespace OpenRA.Traits } public interface ITick { void Tick(Actor self); } + public interface ITickRender { void TickRender(WorldRenderer wr, Actor self); } public interface IRender { IEnumerable Render(Actor self, WorldRenderer wr); } public interface IAutoSelectionSize { int2 SelectionSize(Actor self); } diff --git a/OpenRA.Game/Widgets/MapPreviewWidget.cs b/OpenRA.Game/Widgets/MapPreviewWidget.cs index d68cf983d4..667343798c 100644 --- a/OpenRA.Game/Widgets/MapPreviewWidget.cs +++ b/OpenRA.Game/Widgets/MapPreviewWidget.cs @@ -12,31 +12,42 @@ using System; using System.Collections.Generic; using System.Drawing; using System.Linq; +using System.Threading; using OpenRA.FileFormats; using OpenRA.Graphics; +using OpenRA.Network; namespace OpenRA.Widgets { public class MapPreviewWidget : Widget { public Func Map = () => null; - public Func> SpawnColors = () => new Dictionary(); + public Func> SpawnClients = () => new Dictionary(); public Action OnMouseDown = _ => {}; public Action OnTooltip = (_, __) => { }; public bool IgnoreMouseInput = false; public bool ShowSpawnPoints = true; - static readonly Cache PreviewCache = new Cache(stub => Minimap.RenderMapPreview( new Map( stub.Path ))); + public readonly string TooltipContainer; + public readonly string TooltipTemplate = "SPAWN_TOOLTIP"; + Lazy tooltipContainer; + public int TooltipSpawnIndex = -1; - public MapPreviewWidget() : base() { } + public MapPreviewWidget() : base() + { + tooltipContainer = Lazy.New(() => Ui.Root.Get(TooltipContainer)); + } protected MapPreviewWidget(MapPreviewWidget other) : base(other) { lastMap = other.lastMap; Map = other.Map; - SpawnColors = other.SpawnColors; + SpawnClients = other.SpawnClients; ShowSpawnPoints = other.ShowSpawnPoints; + TooltipTemplate = other.TooltipTemplate; + TooltipContainer = other.TooltipContainer; + tooltipContainer = Lazy.New(() => Ui.Root.Get(TooltipContainer)); } public override Widget Clone() { return new MapPreviewWidget(this); } @@ -53,6 +64,18 @@ namespace OpenRA.Widgets return true; } + public override void MouseEntered() + { + if (TooltipContainer == null) return; + tooltipContainer.Value.SetTooltip(TooltipTemplate, new WidgetArgs() {{ "preview", this }}); + } + + public override void MouseExited() + { + if (TooltipContainer == null) return; + tooltipContainer.Value.RemoveTooltip(); + } + public int2 ConvertToPreview(int2 point) { var map = Map(); @@ -68,19 +91,30 @@ namespace OpenRA.Widgets public override void Draw() { var map = Map(); - if( map == null ) return; + if (map == null) + return; + + // Preview unavailable + if (!Loaded) + { + GeneratePreview(); + return; + } if (lastMap != map) { lastMap = map; // Update image data - var preview = PreviewCache[map]; - if( mapChooserSheet == null || mapChooserSheet.Size.Width != preview.Width || mapChooserSheet.Size.Height != preview.Height ) - mapChooserSheet = new Sheet(new Size( preview.Width, preview.Height ) ); + Bitmap preview; + lock (syncRoot) + preview = Previews[map.Uid]; - mapChooserSheet.Texture.SetData( preview ); - mapChooserSprite = new Sprite( mapChooserSheet, new Rectangle( 0, 0, map.Bounds.Width, map.Bounds.Height ), TextureChannel.Alpha ); + if (mapChooserSheet == null || mapChooserSheet.Size.Width != preview.Width || mapChooserSheet.Size.Height != preview.Height) + mapChooserSheet = new Sheet(new Size(preview.Width, preview.Height)); + + mapChooserSheet.Texture.SetData(preview); + mapChooserSprite = new Sprite(mapChooserSheet, new Rectangle(0, 0, map.Bounds.Width, map.Bounds.Height), TextureChannel.Alpha); } // Update map rect @@ -90,13 +124,14 @@ namespace OpenRA.Widgets var dh = (int)(PreviewScale * (size - map.Bounds.Height)) / 2; MapRect = new Rectangle(RenderBounds.X + dw, RenderBounds.Y + dh, (int)(map.Bounds.Width * PreviewScale), (int)(map.Bounds.Height * PreviewScale)); - Game.Renderer.RgbaSpriteRenderer.DrawSprite( mapChooserSprite, + Game.Renderer.RgbaSpriteRenderer.DrawSprite(mapChooserSprite, new float2(MapRect.Location), - new float2( MapRect.Size ) ); + new float2(MapRect.Size)); + TooltipSpawnIndex = -1; if (ShowSpawnPoints) { - var colors = SpawnColors(); + var colors = SpawnClients().ToDictionary(c => c.Key, c => c.Value.ColorRamp.GetColor(0)); var spawnPoints = map.GetSpawnPoints().ToList(); foreach (var p in spawnPoints) @@ -113,21 +148,82 @@ namespace OpenRA.Widgets if ((pos - Viewport.LastMousePos).LengthSquared < 64) { - OnTooltip(spawnPoints.IndexOf(p) + 1, pos); + TooltipSpawnIndex = spawnPoints.IndexOf(p) + 1; + + // Legacy tooltip behavior + if (TooltipContainer == null) + OnTooltip(TooltipSpawnIndex, pos); } } } } - /// - /// Forces loading the preview into the map cache. - /// - public Bitmap LoadMapPreview() - { - var map = Map(); - if( map == null ) return null; + // Async map preview generation bits + enum PreviewStatus { Invalid, Uncached, Generating, Cached } + static Thread previewLoaderThread; + static object syncRoot = new object(); + static Queue cacheUids = new Queue(); + static readonly Dictionary Previews = new Dictionary(); - return PreviewCache[map]; + void LoadAsyncInternal() + { + for (;;) + { + string uid; + lock (syncRoot) + { + if (cacheUids.Count == 0) + break; + uid = cacheUids.Peek(); + } + + var bitmap = Minimap.RenderMapPreview(Game.modData.AvailableMaps[uid]); + lock (syncRoot) + { + // TODO: We should add previews to a sheet here (with multiple previews per sheet) + Previews.Add(uid, bitmap); + cacheUids.Dequeue(); + } + + // Yuck... But this helps the UI Jank when opening the map selector significantly. + Thread.Sleep(50); + } } + + void GeneratePreview() + { + var m = Map(); + if (m == null) + return; + + var status = Status(m); + if (status == PreviewStatus.Uncached) + lock (syncRoot) + cacheUids.Enqueue(m.Uid); + + if (previewLoaderThread == null || !previewLoaderThread.IsAlive) + { + previewLoaderThread = new Thread(LoadAsyncInternal); + previewLoaderThread.Start(); + } + } + + static PreviewStatus Status(Map m) + { + if (m == null) + return PreviewStatus.Invalid; + + lock (syncRoot) + { + if (Previews.ContainsKey(m.Uid)) + return PreviewStatus.Cached; + + if (cacheUids.Contains(m.Uid)) + return PreviewStatus.Generating; + } + return PreviewStatus.Uncached; + } + + public bool Loaded { get { return Status(Map()) == PreviewStatus.Cached; } } } } diff --git a/OpenRA.Game/Widgets/ScrollItemWidget.cs b/OpenRA.Game/Widgets/ScrollItemWidget.cs index d5deb1ef09..99de7c2dc3 100644 --- a/OpenRA.Game/Widgets/ScrollItemWidget.cs +++ b/OpenRA.Game/Widgets/ScrollItemWidget.cs @@ -14,6 +14,8 @@ namespace OpenRA.Widgets { public class ScrollItemWidget : ButtonWidget { + public string ItemKey; + public ScrollItemWidget() : base() { @@ -28,6 +30,7 @@ namespace OpenRA.Widgets IsVisible = () => false; VisualHeight = 0; IgnoreChildMouseOver = true; + Key = other.Key; } public Func IsSelected = () => false; @@ -59,5 +62,12 @@ namespace OpenRA.Widgets w.OnDoubleClick = onDoubleClick; return w; } + + public static ScrollItemWidget Setup(string key, ScrollItemWidget template, Func isSelected, Action onClick) + { + var w = Setup(template, isSelected, onClick); + w.ItemKey = key; + return w; + } } } \ No newline at end of file diff --git a/OpenRA.Game/Widgets/ScrollPanelWidget.cs b/OpenRA.Game/Widgets/ScrollPanelWidget.cs index 185a41cfed..b227547bb2 100644 --- a/OpenRA.Game/Widgets/ScrollPanelWidget.cs +++ b/OpenRA.Game/Widgets/ScrollPanelWidget.cs @@ -10,6 +10,7 @@ using System; using System.Drawing; +using System.Linq; using OpenRA.Graphics; namespace OpenRA.Widgets @@ -143,6 +144,25 @@ namespace OpenRA.Widgets ListOffset = 0; } + public void ScrollToItem(string itemKey) + { + var item = Children.FirstOrDefault(c => + { + var si = c as ScrollItemWidget; + return si != null && si.ItemKey == itemKey; + }); + + if (item == null) + return; + + // Scroll the item to be visible + if (item.Bounds.Top + ListOffset < 0) + ListOffset = ItemSpacing - item.Bounds.Top; + + if (item.Bounds.Bottom + ListOffset > RenderBounds.Height) + ListOffset = RenderBounds.Height - item.Bounds.Bottom - ItemSpacing; + } + public override void Tick () { if (UpPressed) Scroll(1); diff --git a/OpenRA.Mods.Cnc/Widgets/TooltipContainerWidget.cs b/OpenRA.Game/Widgets/TooltipContainerWidget.cs similarity index 96% rename from OpenRA.Mods.Cnc/Widgets/TooltipContainerWidget.cs rename to OpenRA.Game/Widgets/TooltipContainerWidget.cs index 1901bfc5c8..5d0827a425 100755 --- a/OpenRA.Mods.Cnc/Widgets/TooltipContainerWidget.cs +++ b/OpenRA.Game/Widgets/TooltipContainerWidget.cs @@ -13,11 +13,10 @@ using System.Drawing; using System.Linq; using OpenRA.FileFormats; using OpenRA.Graphics; -using OpenRA.Mods.RA; using OpenRA.Widgets; using System; -namespace OpenRA.Mods.Cnc.Widgets +namespace OpenRA.Widgets { public class TooltipContainerWidget : Widget { diff --git a/OpenRA.Game/World.cs b/OpenRA.Game/World.cs index 833fc29427..58ad20884b 100644 --- a/OpenRA.Game/World.cs +++ b/OpenRA.Game/World.cs @@ -13,6 +13,7 @@ using System.Collections.Generic; using System.Linq; using OpenRA.Effects; using OpenRA.FileFormats; +using OpenRA.Graphics; using OpenRA.Network; using OpenRA.Orders; using OpenRA.Support; @@ -192,7 +193,12 @@ namespace OpenRA while (frameEndActions.Count != 0) frameEndActions.Dequeue()(this); - + } + + // For things that want to update their render state once per tick, ignoring pause state + public void TickRender(WorldRenderer wr) + { + ActorsWithTrait().Do(x => x.Trait.TickRender(wr, x.Actor)); } public IEnumerable Actors { get { return actors; } } diff --git a/OpenRA.Mods.Cnc/CncMenuPaletteEffect.cs b/OpenRA.Mods.Cnc/CncMenuPaletteEffect.cs index a351b80128..71ee66a307 100644 --- a/OpenRA.Mods.Cnc/CncMenuPaletteEffect.cs +++ b/OpenRA.Mods.Cnc/CncMenuPaletteEffect.cs @@ -11,6 +11,7 @@ using System.Collections.Generic; using System.Drawing; using OpenRA.FileFormats; +using OpenRA.Graphics; using OpenRA.Traits; namespace OpenRA.Mods.Cnc @@ -22,7 +23,7 @@ namespace OpenRA.Mods.Cnc public object Create(ActorInitializer init) { return new CncMenuPaletteEffect(this); } } - public class CncMenuPaletteEffect : IPaletteModifier, ITick + public class CncMenuPaletteEffect : IPaletteModifier, ITickRender { public enum EffectType { None, Black, Desaturated } public readonly CncMenuPaletteEffectInfo Info; @@ -40,7 +41,7 @@ namespace OpenRA.Mods.Cnc to = type; } - public void Tick(Actor self) + public void TickRender(WorldRenderer wr, Actor self) { if (remainingFrames > 0) remainingFrames--; diff --git a/OpenRA.Mods.Cnc/OpenRA.Mods.Cnc.csproj b/OpenRA.Mods.Cnc/OpenRA.Mods.Cnc.csproj index 067b709c02..687c921db7 100644 --- a/OpenRA.Mods.Cnc/OpenRA.Mods.Cnc.csproj +++ b/OpenRA.Mods.Cnc/OpenRA.Mods.Cnc.csproj @@ -111,10 +111,10 @@ - + diff --git a/OpenRA.Mods.Cnc/Widgets/Logic/SpawnSelectorTooltipLogic.cs b/OpenRA.Mods.Cnc/Widgets/Logic/SpawnSelectorTooltipLogic.cs new file mode 100644 index 0000000000..6ad7f65538 --- /dev/null +++ b/OpenRA.Mods.Cnc/Widgets/Logic/SpawnSelectorTooltipLogic.cs @@ -0,0 +1,79 @@ +#region Copyright & License Information +/* + * Copyright 2007-2013 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see COPYING. + */ +#endregion + +using System; +using System.Drawing; +using System.Linq; +using OpenRA.Widgets; +using OpenRA.Network; + +namespace OpenRA.Mods.Cnc.Widgets.Logic +{ + public class SpawnSelectorTooltipLogic + { + [ObjectCreator.UseCtor] + public SpawnSelectorTooltipLogic(Widget widget, TooltipContainerWidget tooltipContainer, MapPreviewWidget preview) + { + widget.IsVisible = () => preview.TooltipSpawnIndex != -1; + var label = widget.Get("LABEL"); + var flag = widget.Get("FLAG"); + var team = widget.Get("TEAM"); + + var ownerFont = Game.Renderer.Fonts[label.Font]; + var teamFont = Game.Renderer.Fonts[team.Font]; + var cachedWidth = 0; + var labelText = ""; + string playerCountry = null; + var playerTeam = -1; + + tooltipContainer.BeforeRender = () => + { + var client = preview.SpawnClients().Values.FirstOrDefault(c => c.SpawnPoint == preview.TooltipSpawnIndex); + + var teamWidth = 0; + if (client == null) + { + labelText = "Available spawn"; + playerCountry = null; + playerTeam = 0; + widget.Bounds.Height = 25; + } + else + { + labelText = client.Name; + playerCountry = client.Country; + playerTeam = client.Team; + widget.Bounds.Height = playerTeam > 0 ? 40 : 25; + teamWidth = teamFont.Measure(team.GetText()).X; + } + + label.Bounds.X = playerCountry != null ? flag.Bounds.Right + 5 : 5; + + var textWidth = ownerFont.Measure(labelText).X; + if (textWidth != cachedWidth) + { + label.Bounds.Width = textWidth; + widget.Bounds.Width = 2*label.Bounds.X + textWidth; + } + + widget.Bounds.Width = Math.Max(teamWidth + 10, label.Bounds.Right + 5); + team.Bounds.Width = widget.Bounds.Width; + }; + + label.GetText = () => labelText; + flag.IsVisible = () => playerCountry != null; + flag.GetImageCollection = () => "flags"; + flag.GetImageName = () => playerCountry; + team.GetText = () => "Team {0}".F(playerTeam); + team.IsVisible = () => playerTeam > 0; + } + } +} + diff --git a/OpenRA.Mods.RA/Widgets/Logic/LobbyLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/LobbyLogic.cs index 618653d372..9b142f6986 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/LobbyLogic.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/LobbyLogic.cs @@ -113,7 +113,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic mapPreview.Map = () => Map; mapPreview.OnMouseDown = mi => LobbyUtils.SelectSpawnPoint( orderManager, mapPreview, Map, mi ); mapPreview.OnTooltip = (spawnPoint, pos) => LobbyUtils.ShowSpawnPointTooltip(orderManager, spawnPoint, pos); - mapPreview.SpawnColors = () => LobbyUtils.GetSpawnColors(orderManager, Map); + mapPreview.SpawnClients = () => LobbyUtils.GetSpawnClients(orderManager, Map); var mapTitle = lobby.GetOrNull("MAP_TITLE"); if (mapTitle != null) @@ -122,6 +122,20 @@ namespace OpenRA.Mods.RA.Widgets.Logic mapTitle.GetText = () => Map.Title; } + var mapType = lobby.GetOrNull("MAP_TYPE"); + if (mapType != null) + { + mapType.IsVisible = () => Map != null; + mapType.GetText = () => Map.Type; + } + + var mapAuthor = lobby.GetOrNull("MAP_AUTHOR"); + if (mapAuthor != null) + { + mapAuthor.IsVisible = () => Map != null; + mapAuthor.GetText = () => "Created by {0}".F(Map.Author); + } + CountryNames = Rules.Info["world"].Traits.WithInterface() .Where(c => c.Selectable) .ToDictionary(a => a.Race, a => a.Name); diff --git a/OpenRA.Mods.RA/Widgets/Logic/LobbyUtils.cs b/OpenRA.Mods.RA/Widgets/Logic/LobbyUtils.cs index 58369cf830..fa04fad141 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/LobbyUtils.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/LobbyUtils.cs @@ -150,14 +150,14 @@ namespace OpenRA.Mods.RA.Widgets.Logic color.AttachPanel(colorChooser); } - public static Dictionary GetSpawnColors(OrderManager orderManager, Map map) + public static Dictionary GetSpawnClients(OrderManager orderManager, Map map) { var spawns = map.GetSpawnPoints(); return orderManager.LobbyInfo.Clients - .Where( c => c.SpawnPoint != 0) - .ToDictionary( - c => spawns[c.SpawnPoint - 1], - c => c.ColorRamp.GetColor(0)); + .Where(c => c.SpawnPoint != 0) + .ToDictionary( + c => spawns[c.SpawnPoint - 1], + c => c); } public static void SelectSpawnPoint(OrderManager orderManager, MapPreviewWidget mapPreview, Map map, MouseInput mi) diff --git a/OpenRA.Mods.RA/Widgets/Logic/MapChooserLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/MapChooserLogic.cs index f9fedb2082..df448c1701 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/MapChooserLogic.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/MapChooserLogic.cs @@ -9,6 +9,7 @@ #endregion using System; +using System.Collections.Generic; using System.Linq; using System.Threading; using OpenRA.FileFormats; @@ -19,10 +20,13 @@ namespace OpenRA.Mods.RA.Widgets.Logic public class MapChooserLogic { Map map; + + // May be a subset of available maps if a mode filter is active + Dictionary visibleMaps; + ScrollPanelWidget scrollpanel; ScrollItemWidget itemTemplate; string gameMode; - Thread mapLoaderThread; [ObjectCreator.UseCtor] internal MapChooserLogic(Widget widget, string initialMap, Action onExit, Action onSelect) @@ -34,6 +38,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic scrollpanel = widget.Get("MAP_LIST"); scrollpanel.ScrollVelocity = 40f; + scrollpanel.Layout = new GridLayout(scrollpanel); itemTemplate = scrollpanel.Get("MAP_TEMPLATE"); @@ -55,7 +60,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic { var item = ScrollItemWidget.Setup(template, () => gameMode == ii.First, - () => { gameMode = ii.First; EnumerateMapsAsync(); }); + () => { gameMode = ii.First; EnumerateMaps(); }); item.Get("LABEL").GetText = () => showItem(ii); return item; }; @@ -66,34 +71,34 @@ namespace OpenRA.Mods.RA.Widgets.Logic gameModeDropdown.GetText = () => showItem(gameModes.First(m => m.First == gameMode)); } - EnumerateMapsAsync(); - } + var randomMapButton = widget.GetOrNull("RANDOMMAP_BUTTON"); + if (randomMapButton != null) + { + randomMapButton.OnClick = () => + { + var kv = visibleMaps.Random(Game.CosmeticRandom); + map = kv.Value; + scrollpanel.ScrollToItem(kv.Key); + }; + randomMapButton.IsDisabled = () => visibleMaps == null || visibleMaps.Count == 0; + } - void EnumerateMapsAsync() - { - if (mapLoaderThread != null && mapLoaderThread.IsAlive) - mapLoaderThread.Abort(); // violent, but should be fine since we are not doing anything sensitive in this thread - - mapLoaderThread = new Thread(EnumerateMaps); - mapLoaderThread.Start(); + EnumerateMaps(); } void EnumerateMaps() { - Game.RunAfterTick(() => scrollpanel.RemoveChildren()); // queue removal in case another thread added any items to the game queue - scrollpanel.Layout = new GridLayout(scrollpanel); - scrollpanel.ScrollToTop(); - var maps = Game.modData.AvailableMaps .Where(kv => kv.Value.Selectable) .Where(kv => kv.Value.Type == gameMode || gameMode == null) .OrderBy(kv => kv.Value.PlayerCount) .ThenBy(kv => kv.Value.Title); + scrollpanel.RemoveChildren(); foreach (var kv in maps) { var m = kv.Value; - var item = ScrollItemWidget.Setup(itemTemplate, () => m == map, () => map = m); + var item = ScrollItemWidget.Setup(kv.Key, itemTemplate, () => m == map, () => map = m); var titleLabel = item.Get("TITLE"); titleLabel.GetText = () => m.Title; @@ -102,17 +107,21 @@ namespace OpenRA.Mods.RA.Widgets.Logic previewWidget.IgnoreMouseOver = true; previewWidget.IgnoreMouseInput = true; previewWidget.Map = () => m; - previewWidget.LoadMapPreview(); + previewWidget.IsVisible = () => previewWidget.RenderBounds.IntersectsWith(scrollpanel.RenderBounds); - var detailsWidget = item.Get("DETAILS"); + var previewLoadingWidget = item.GetOrNull("PREVIEW_PLACEHOLDER"); + if (previewLoadingWidget != null) + previewLoadingWidget.IsVisible = () => !previewWidget.Loaded; + + var detailsWidget = item.GetOrNull("DETAILS"); if (detailsWidget != null) - detailsWidget.GetText = () => "{0} ({1})".F(m.Type, m.PlayerCount); + detailsWidget.GetText = () => "{0} ({1} players)".F(m.Type, m.PlayerCount); - var authorWidget = item.Get("AUTHOR"); + var authorWidget = item.GetOrNull("AUTHOR"); if (authorWidget != null) - authorWidget.GetText = () => m.Author; + authorWidget.GetText = () => "Created by {0}".F(m.Author); - var sizeWidget = item.Get("SIZE"); + var sizeWidget = item.GetOrNull("SIZE"); if (sizeWidget != null) { var size = m.Bounds.Width + "x" + m.Bounds.Height; @@ -124,8 +133,12 @@ namespace OpenRA.Mods.RA.Widgets.Logic sizeWidget.GetText = () => size; } - Game.RunAfterTick(() => scrollpanel.AddChild(item)); + scrollpanel.AddChild(item); } + + visibleMaps = maps.ToDictionary(kv => kv.Key, kv => kv.Value); + if (visibleMaps.ContainsValue(map)) + scrollpanel.ScrollToItem(visibleMaps.First(m => m.Value == map).Key); } } } diff --git a/mods/cnc/chrome/lobby.yaml b/mods/cnc/chrome/lobby.yaml index 4a1d70e709..a040f5f7b7 100644 --- a/mods/cnc/chrome/lobby.yaml +++ b/mods/cnc/chrome/lobby.yaml @@ -32,13 +32,28 @@ Container@SERVER_LOBBY: Y:1 Width:192 Height:192 + TooltipContainer:TOOLTIP_CONTAINER Label@MAP_TITLE: X:PARENT_RIGHT-15-WIDTH - Y:225 + Y:227 Width:194 Height:25 Font:Bold Align:Center + Label@MAP_TYPE: + X:PARENT_RIGHT-15-WIDTH + Y:242 + Width:194 + Height:25 + Font:TinyBold + Align:Center + Label@MAP_AUTHOR: + X:PARENT_RIGHT-15-WIDTH + Y:255 + Width:194 + Height:25 + Font:Tiny + Align:Center ScrollPanel@PLAYERS: X:15 Y:30 @@ -329,13 +344,13 @@ Container@SERVER_LOBBY: Font:Bold Checkbox@ALLOWCHEATS_CHECKBOX: X:15 - Y:255 + Y:257 Width:130 Height:20 Text: Allow Cheats Checkbox@CRATES_CHECKBOX: X:160 - Y:255 + Y:257 Width:80 Height:20 Text: Crates @@ -347,13 +362,6 @@ Container@SERVER_LOBBY: Font:Bold Visible:false Text:Assign - Button@RANDOMMAP_BUTTON: - X:PARENT_RIGHT-120-15-40 - Y:255 - Width:120 - Height:25 - Text:Random Map - Font:Bold ScrollPanel@CHAT_DISPLAY: X:15 Y:285 @@ -396,6 +404,7 @@ Container@SERVER_LOBBY: Height:25 Align:Right Text:Chat: + TooltipContainer@TOOLTIP_CONTAINER: Button@DISCONNECT_BUTTON: X:0 Y:499 diff --git a/mods/cnc/chrome/mapchooser.yaml b/mods/cnc/chrome/mapchooser.yaml index e27de9ce1d..c78eb97746 100644 --- a/mods/cnc/chrome/mapchooser.yaml +++ b/mods/cnc/chrome/mapchooser.yaml @@ -6,62 +6,78 @@ Container@MAPCHOOSER_PANEL: Height:535 Children: Label@TITLE: - Width:790 + Width:PARENT_RIGHT Y:0-25 Font:BigBold Contrast:true Align:Center Text: Select Map Background@bg: - Width:790 + Width:PARENT_RIGHT Height:500 Background:panel-black Children: + Label@GAMEMODE_DESC: + X:PARENT_RIGHT - WIDTH - 220 + Y:10 + Width:200 + Height:25 + Font: Bold + Align: Right + Text:Filter: + DropDownButton@GAMEMODE_FILTER: + X:PARENT_RIGHT - WIDTH - 15 + Y:10 + Width:200 + Height:25 ScrollPanel@MAP_LIST: X:15 - Y:30 + Y:45 Width:PARENT_RIGHT - 30 - Height:455 + Height:440 Children: ScrollItem@MAP_TEMPLATE: - Width:180 - Height:208 + Width:168 + Height:217 X:2 Y:0 Visible:false Children: + Background@PREVIEW_PLACEHOLDER: + X:(PARENT_RIGHT - WIDTH)/2 + Y:4 + Width:158 + Height:158 + Background:panel-black + ClickThrough: false + MapPreview@PREVIEW: + X:(PARENT_RIGHT - WIDTH)/2 + Y:4 + Width:158 + Height:158 Label@TITLE: X:2 - Y:PARENT_BOTTOM-47 + Y:PARENT_BOTTOM-48 Width:PARENT_RIGHT-4 - Height:25 Align:Center Label@DETAILS: Width:PARENT_RIGHT-4 X:2 - Y:PARENT_BOTTOM-35 + Y:PARENT_BOTTOM-34 Align:Center - Height:25 Font:Tiny Label@AUTHOR: Width:PARENT_RIGHT-4 X:2 - Y:PARENT_BOTTOM-26 + Y:PARENT_BOTTOM-22 Align:Center - Height:25 Font:Tiny Label@SIZE: Width:PARENT_RIGHT-4 X:2 - Y:PARENT_BOTTOM-17 + Y:PARENT_BOTTOM-10 Align:Center - Height:25 Font:Tiny - MapPreview@PREVIEW: - X:(PARENT_RIGHT - WIDTH)/2 - Y:4 - Width:160 - Height:160 Button@BUTTON_CANCEL: Key:escape X:0 @@ -69,9 +85,16 @@ Container@MAPCHOOSER_PANEL: Width:140 Height:35 Text:Cancel + Button@RANDOMMAP_BUTTON: + Key:space + X:PARENT_RIGHT - 150 - WIDTH + Y:499 + Width:140 + Height:35 + Text:Random Button@BUTTON_OK: Key:return - X:790 - WIDTH + X:PARENT_RIGHT - WIDTH Y:499 Width:140 Height:35 diff --git a/mods/cnc/chrome/tooltips.yaml b/mods/cnc/chrome/tooltips.yaml index 6860027c99..ed35e8ddb3 100644 --- a/mods/cnc/chrome/tooltips.yaml +++ b/mods/cnc/chrome/tooltips.yaml @@ -99,3 +99,23 @@ Background@SUPPORT_POWER_TOOLTIP: Y:20 Font:TinyBold VAlign:Top + +Background@SPAWN_TOOLTIP: + Logic:SpawnSelectorTooltipLogic + Background:panel-black + Width:141 + Children: + Label@LABEL: + X:5 + Height:23 + Font:Bold + Image@FLAG: + X:5 + Y:5 + Width:32 + Height:16 + Label@TEAM: + Y:21 + Height:15 + Font:TinyBold + Align:center \ No newline at end of file diff --git a/mods/cnc/rules/infantry.yaml b/mods/cnc/rules/infantry.yaml index de191604b1..fdda163d5f 100644 --- a/mods/cnc/rules/infantry.yaml +++ b/mods/cnc/rules/infantry.yaml @@ -230,5 +230,6 @@ PVICE: DrawLineToTarget: Selectable: Voice: DinoVoice + SelectionDecorations: ActorLostNotification: Notification: unitlost.aud