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
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