diff --git a/OpenRA.Mods.Cnc/OpenRA.Mods.Cnc.csproj b/OpenRA.Mods.Cnc/OpenRA.Mods.Cnc.csproj
index 5d88daefe5..d0b08c5c20 100644
--- a/OpenRA.Mods.Cnc/OpenRA.Mods.Cnc.csproj
+++ b/OpenRA.Mods.Cnc/OpenRA.Mods.Cnc.csproj
@@ -74,6 +74,7 @@
+
diff --git a/OpenRA.Mods.Cnc/Widgets/CncMenuLogic.cs b/OpenRA.Mods.Cnc/Widgets/CncMenuLogic.cs
index d0946b75cc..88589acc8f 100755
--- a/OpenRA.Mods.Cnc/Widgets/CncMenuLogic.cs
+++ b/OpenRA.Mods.Cnc/Widgets/CncMenuLogic.cs
@@ -40,6 +40,17 @@ namespace OpenRA.Mods.Cnc.Widgets
mainMenu.GetWidget("SOLO_BUTTON").OnClick = StartSkirmishGame;
mainMenu.GetWidget("MULTIPLAYER_BUTTON").OnClick = () => Menu = MenuType.Multiplayer;
+
+ mainMenu.GetWidget("REPLAYS_BUTTON").OnClick = () =>
+ {
+ Menu = MenuType.None;
+ Widget.OpenWindow("REPLAYBROWSER_PANEL", new Dictionary()
+ {
+ { "onExit", new Action(() => { Menu = MenuType.Main; Widget.CloseWindow(); }) },
+ { "onStart", new Action(RemoveShellmapUI) }
+ });
+ };
+
mainMenu.GetWidget("SETTINGS_BUTTON").OnClick = () => Menu = MenuType.Settings;
mainMenu.GetWidget("QUIT_BUTTON").OnClick = Game.Exit;
@@ -74,15 +85,14 @@ namespace OpenRA.Mods.Cnc.Widgets
settingsMenu.GetWidget("BACK_BUTTON").OnClick = () => Menu = MenuType.Main;
}
+ void RemoveShellmapUI()
+ {
+ Widget.CloseWindow();
+ Widget.RootWidget.RemoveChild(Widget.RootWidget.GetWidget("MENU_BACKGROUND"));
+ }
+
void OpenLobbyPanel()
{
- // Starting a game: remove all shellmap ui
- Action onStart = () =>
- {
- Widget.CloseWindow();
- Widget.RootWidget.RemoveChild(Widget.RootWidget.GetWidget("MENU_BACKGROUND"));
- };
-
// Quit the lobby: disconnect and restore menu
Action onLobbyClose = () =>
{
@@ -95,7 +105,7 @@ namespace OpenRA.Mods.Cnc.Widgets
Game.OpenWindow("SERVER_LOBBY", new Dictionary()
{
{ "onExit", onLobbyClose },
- { "onStart", onStart }
+ { "onStart", new Action(RemoveShellmapUI) }
});
}
diff --git a/OpenRA.Mods.Cnc/Widgets/CncReplayBrowserLogic.cs b/OpenRA.Mods.Cnc/Widgets/CncReplayBrowserLogic.cs
new file mode 100644
index 0000000000..76b759dc53
--- /dev/null
+++ b/OpenRA.Mods.Cnc/Widgets/CncReplayBrowserLogic.cs
@@ -0,0 +1,112 @@
+#region Copyright & License Information
+/*
+ * Copyright 2007-2011 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.IO;
+using System.Linq;
+using OpenRA.FileFormats;
+using OpenRA.Network;
+using OpenRA.Widgets;
+using OpenRA.Mods.RA.Widgets.Delegates;
+
+namespace OpenRA.Mods.Cnc.Widgets
+{
+ public class CncReplayBrowserLogic : IWidgetDelegate
+ {
+ Widget widget;
+
+ [ObjectCreator.UseCtor]
+ public CncReplayBrowserLogic([ObjectCreator.Param] Widget widget,
+ [ObjectCreator.Param] Action onExit,
+ [ObjectCreator.Param] Action onStart)
+ {
+ this.widget = widget;
+
+ widget.GetWidget("CANCEL_BUTTON").OnClick = onExit;
+
+ var rl = widget.GetWidget("REPLAY_LIST");
+ var replayDir = Path.Combine(Platform.SupportDir, "Replays");
+
+ var template = widget.GetWidget("REPLAY_TEMPLATE");
+ CurrentReplay = null;
+
+ rl.RemoveChildren();
+ if (Directory.Exists(replayDir))
+ foreach (var replayFile in Directory.GetFiles(replayDir, "*.rep").Reverse())
+ AddReplay(rl, replayFile, template);
+
+ var watch = widget.GetWidget("WATCH_BUTTON");
+ watch.IsDisabled = () => currentReplay == null;
+ watch.OnClick = () =>
+ {
+ if (currentReplay != null)
+ {
+ Game.JoinReplay(CurrentReplay);
+ onStart();
+ }
+ };
+
+ widget.GetWidget("REPLAY_INFO").IsVisible = () => currentReplay != null;
+ }
+
+ Map MapFromSummary(ReplaySummary rs)
+ {
+ if (rs.LobbyInfo == null)
+ return null;
+
+ var map = rs.LobbyInfo.GlobalSettings.Map;
+ if (!Game.modData.AvailableMaps.ContainsKey(map))
+ return null;
+
+ return Game.modData.AvailableMaps[map];
+ }
+
+ string currentReplay = null;
+ string CurrentReplay
+ {
+ get { return currentReplay; }
+ set
+ {
+ currentReplay = value;
+ if (currentReplay != null)
+ {
+ try
+ {
+ var summary = new ReplaySummary(currentReplay);
+ var mapStub = MapFromSummary(summary);
+
+ widget.GetWidget("DURATION").GetText =
+ () => WidgetUtils.FormatTime(summary.Duration * 3 /* todo: 3:1 ratio isnt always true. */);
+ widget.GetWidget("MAP_PREVIEW").Map = () => mapStub;
+ widget.GetWidget("MAP_TITLE").GetText =
+ () => mapStub != null ? mapStub.Title : "(Unknown Map)";
+ }
+ catch(Exception e)
+ {
+ Log.Write("debug", "Exception while parsing replay: {0}", e.ToString());
+ currentReplay = null;
+ }
+ }
+ }
+ }
+
+ void AddReplay(ScrollPanelWidget list, string filename, LabelWidget template)
+ {
+ var entry = template.Clone() as LabelWidget;
+ entry.Id = "REPLAY_";
+ entry.GetText = () => " {0}".F(Path.GetFileName(filename));
+ entry.GetBackground = () => (CurrentReplay == filename) ? "dialog2" : null;
+ entry.OnMouseDown = mi => { if (mi.Button != MouseButton.Left) return false; CurrentReplay = filename; return true; };
+ entry.IsVisible = () => true;
+ list.AddChild(entry);
+ }
+ }
+}
diff --git a/OpenRA.Mods.RA/Widgets/Delegates/ReplayBrowserDelegate.cs b/OpenRA.Mods.RA/Widgets/Delegates/ReplayBrowserDelegate.cs
index 56b197d226..8a8a3cd5c7 100644
--- a/OpenRA.Mods.RA/Widgets/Delegates/ReplayBrowserDelegate.cs
+++ b/OpenRA.Mods.RA/Widgets/Delegates/ReplayBrowserDelegate.cs
@@ -112,7 +112,7 @@ namespace OpenRA.Mods.RA.Widgets.Delegates
}
/* a maze of twisty little hacks,... */
- class ReplaySummary
+ public class ReplaySummary
{
public readonly int Duration;
public readonly Session LobbyInfo;
diff --git a/mods/cnc/chrome/mainmenu.yaml b/mods/cnc/chrome/mainmenu.yaml
index f6ad8e2cdc..5bd068cc48 100644
--- a/mods/cnc/chrome/mainmenu.yaml
+++ b/mods/cnc/chrome/mainmenu.yaml
@@ -55,8 +55,8 @@ Container@MENU_BACKGROUND:
Width:140
Height:35
Text:Settings
- CncMenuButton@REPLAY_BUTTON:
- Id:REPLAY_BUTTON
+ CncMenuButton@REPLAYS_BUTTON:
+ Id:REPLAYS_BUTTON
X:450
Y:0
Width:140
diff --git a/mods/cnc/chrome/replaybrowser.yaml b/mods/cnc/chrome/replaybrowser.yaml
new file mode 100644
index 0000000000..c06b101238
--- /dev/null
+++ b/mods/cnc/chrome/replaybrowser.yaml
@@ -0,0 +1,92 @@
+Container@REPLAYBROWSER_PANEL:
+ Id:REPLAYBROWSER_PANEL
+ Delegate:CncReplayBrowserLogic
+ X:(WINDOW_RIGHT - WIDTH)/2
+ Y:(WINDOW_BOTTOM - 500)/2
+ Width:740
+ Height:535
+ Children:
+ Label@TITLE:
+ Width:740
+ Y:0-25
+ Font:BigBold
+ Contrast:true
+ Align:Center
+ Text:Replay Viewer
+ Background@bg:
+ Width:740
+ Height:500
+ Background:panel-black
+ Children:
+ CncScrollPanel@REPLAY_LIST:
+ Id:REPLAY_LIST
+ X:15
+ Y:50
+ Width:390
+ Height:300
+ Children:
+ Label@REPLAY_TEMPLATE:
+ Id:REPLAY_TEMPLATE
+ Width:PARENT_RIGHT-28
+ Height:25
+ X:2
+ Y:0
+ Visible:false
+ Container@REPLAY_INFO:
+ Id:REPLAY_INFO
+ Width:PARENT_RIGHT
+ Height:PARENT_BOTTOM
+ Visible:false
+ Children:
+ MapPreview@MAP_PREVIEW:
+ Id:MAP_PREVIEW
+ X:PARENT_RIGHT-241
+ Y:30
+ Width:192
+ Height:192
+ Label@MAP_TITLE_LABEL:
+ Id:MAP_TITLE_LABEL
+ X:PARENT_RIGHT - 200 - WIDTH
+ Y:250
+ Align:Right
+ Width:70
+ Height:20
+ Text:Map:
+ Bold:True
+ Label@MAP_TITLE:
+ Id:MAP_TITLE
+ X:PARENT_RIGHT - 195
+ Y:250
+ Align:Left
+ Width:70
+ Height:20
+ Label@DURATION_LABEL:
+ Id:DURATION_LABEL
+ X:PARENT_RIGHT - 200 - WIDTH
+ Y:270
+ Align:Right
+ Width:70
+ Height:20
+ Text:Duration:
+ Bold:True
+ Label@DURATION:
+ Id:DURATION
+ X:PARENT_RIGHT - 195
+ Y:270
+ Align:Left
+ Width:70
+ Height:20
+ CncMenuButton@WATCH_BUTTON:
+ Id:WATCH_BUTTON
+ X:0
+ Y:499
+ Width:140
+ Height:35
+ Text:Watch
+ CncMenuButton@CANCEL_BUTTON:
+ Id:CANCEL_BUTTON
+ X:600
+ Y:499
+ Width:140
+ Height:35
+ Text:Back
\ No newline at end of file
diff --git a/mods/cnc/mod.yaml b/mods/cnc/mod.yaml
index 17e95cf281..4df3099a0c 100644
--- a/mods/cnc/mod.yaml
+++ b/mods/cnc/mod.yaml
@@ -65,6 +65,7 @@ ChromeLayout:
mods/cnc/chrome/directconnect.yaml
mods/cnc/chrome/lobby.yaml
mods/cnc/chrome/mapchooser.yaml
+ mods/cnc/chrome/replaybrowser.yaml
mods/cnc/chrome/ingame.yaml
Weapons: