diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj
index 9c41d37d09..ed0ad3a5c5 100644
--- a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj
+++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj
@@ -372,6 +372,7 @@
+
diff --git a/OpenRA.Mods.RA/Widgets/Logic/IngameObserverChromeLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/IngameObserverChromeLogic.cs
index 7fb99de8fe..888404372b 100644
--- a/OpenRA.Mods.RA/Widgets/Logic/IngameObserverChromeLogic.cs
+++ b/OpenRA.Mods.RA/Widgets/Logic/IngameObserverChromeLogic.cs
@@ -45,6 +45,12 @@ namespace OpenRA.Mods.RA.Widgets.Logic
optionsBG.Get("MUSIC").OnClick = () => Ui.OpenWindow("MUSIC_MENU");
optionsBG.Get("RESUME").OnClick = () => optionsBG.Visible = false;
optionsBG.Get("SURRENDER").IsVisible = () => false;
+
+ Ui.Root.Get("INGAME_STATS_BUTTON").OnClick = () =>
+ {
+ var stats = gameRoot.Get("OBSERVER_STATS");
+ stats.Visible = !stats.Visible;
+ };
}
void UnregisterEvents()
diff --git a/OpenRA.Mods.RA/Widgets/Logic/ObserverStatsLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/ObserverStatsLogic.cs
new file mode 100644
index 0000000000..be4d6062a3
--- /dev/null
+++ b/OpenRA.Mods.RA/Widgets/Logic/ObserverStatsLogic.cs
@@ -0,0 +1,100 @@
+#region Copyright & License Information
+/*
+ * Copyright 2007-2012 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.Linq;
+using OpenRA.Traits;
+using OpenRA.Widgets;
+using OpenRA.Network;
+using System.Collections.Generic;
+using OpenRA.Mods.RA.Buildings;
+using System.Drawing;
+using OpenRA.Graphics;
+using System;
+
+namespace OpenRA.Mods.RA.Widgets.Logic
+{
+ public class ObserverStatsLogic
+ {
+ ScrollPanelWidget playersPanel;
+ ScrollItemWidget playerTemplate;
+ ScrollItemWidget teamTemplate;
+
+ [ObjectCreator.UseCtor]
+ public ObserverStatsLogic(World world, Widget widget)
+ {
+ playersPanel = widget.Get("PLAYERS");
+ playerTemplate = playersPanel.Get("PLAYER_TEMPLATE");
+ teamTemplate = playersPanel.Get("TEAM_TEMPLATE");
+ playersPanel.RemoveChildren();
+ playersPanel.Layout = new GridLayout(playersPanel);
+
+ var players = world.Players.Where(p => !p.NonCombatant);
+
+ widget.Height = (200 + (Math.Min(8, players.Count()) * 25)).ToString();
+ var args = new WidgetArgs();
+ widget.Initialize(args);
+ widget.Get("BACKGROUND").Initialize(args);
+ widget.Get("PLAYERS").Initialize(args);
+
+ var teams = players.GroupBy(p => (world.LobbyInfo.ClientWithIndex(p.ClientIndex) ?? new Session.Client()).Team).OrderBy(g => g.Key);
+ foreach (var t in teams)
+ {
+ var team = t;
+ var tt = ScrollItemWidget.Setup(teamTemplate, () => false, () => { });
+ tt.IgnoreMouseOver = true;
+ tt.Get("TEAM").GetText = () => team.Key == 0 ? "No team" : "Team " + team.Key;
+ playersPanel.AddChild(tt);
+ foreach (var p in team)
+ {
+ var player = p;
+ var template = ScrollItemWidget.Setup(playerTemplate, () => false, null);
+ template.OnClick = () =>
+ {
+ var index = playersPanel.Children.Where(c => c.GetOrNull("PLAYER") != null).ToList().FindIndex(w => w == template);
+ var selectedPlayer = teams.SelectMany(x => x).ElementAt(index);
+ var playerBase = world.Actors.FirstOrDefault(a => !a.IsDead() && a.HasTrait() && a.Owner == selectedPlayer);
+ if (playerBase != null)
+ {
+ Game.MoveViewport(playerBase.Location.ToFloat2());
+ }
+ };
+
+ var flag = template.Get("FACTION_FLAG");
+ flag.GetImageName = () => player.Country.Race;
+ flag.GetImageCollection = () => "flags";
+
+ var playerName = template.Get("PLAYER");
+ playerName.GetText = () => player.PlayerName + (player.WinState == WinState.Undefined ? "" : " (" + player.WinState + ")");
+ playerName.GetColor = () => player.ColorRamp.GetColor(0);
+
+ var res = player.PlayerActor.Trait();
+ template.Get("CASH").GetText = () => "$" + (res.DisplayCash + res.DisplayOre);
+
+ var powerRes = player.PlayerActor.Trait();
+ var power = template.Get("POWER");
+ power.GetText = () => powerRes.PowerDrained + "/" + powerRes.PowerProvided;
+ power.GetColor = () => GetPowerColor(powerRes.PowerState);
+
+ template.Get("KILLS").GetText = () => player.Kills.ToString();
+ template.Get("DEATHS").GetText = () => player.Deaths.ToString();
+
+ playersPanel.AddChild(template);
+ }
+ }
+ }
+
+ static Color GetPowerColor(PowerState state)
+ {
+ if (state == PowerState.Critical) return Color.Red;
+ if (state == PowerState.Low) return Color.Orange;
+ return Color.LimeGreen;
+ }
+ }
+}
diff --git a/mods/ra/chrome/ingame.yaml b/mods/ra/chrome/ingame.yaml
index 57d5d0a6ea..464dcb8aa9 100644
--- a/mods/ra/chrome/ingame.yaml
+++ b/mods/ra/chrome/ingame.yaml
@@ -287,6 +287,13 @@ Container@OBSERVER_ROOT:
Text:Options
Font:Bold
Key:escape
+ Button@INGAME_STATS_BUTTON:
+ X:162
+ Y:0
+ Width:160
+ Height:25
+ Text:Players
+ Font:Bold
Background@RADAR_BG:
X:WINDOW_RIGHT-255
Y:5
@@ -384,6 +391,124 @@ Container@OBSERVER_ROOT:
Y:205
Width:170
Height:40
+ Container@OBSERVER_STATS:
+ Logic:ObserverStatsLogic
+ X:25
+ Y:50
+ Width:565
+ Height:400
+ Visible:false
+ Children:
+ Background@BACKGROUND:
+ Width:PARENT_RIGHT
+ Height:PARENT_BOTTOM
+ Background:dialog
+ Children:
+ Label@TITLE:
+ X:0
+ Y:15
+ Width:PARENT_RIGHT
+ Height:25
+ Font:Bold
+ Align:Center
+ Text:Players
+ Label@PLAYER_HEADER:
+ X:85
+ Y:40
+ Width:160
+ Height:25
+ Font:Bold
+ Text:Player
+ Label@CASH_HEADER:
+ X:245
+ Y:40
+ Width:80
+ Height:25
+ Font:Bold
+ Text:Cash
+ Label@POWER_HEADER
+ X:325
+ Y:40
+ Width:80
+ Height:25
+ Font:Bold
+ Text:Power
+ Label@KILLS_HEADER:
+ X:405
+ Y:40
+ Width:40
+ Height:25
+ Font:Bold
+ Text:Kills
+ Align:Right
+ Label@DEATHS_HEADER:
+ X:465
+ Y:40
+ Width:40
+ Height:25
+ Font:Bold
+ Text:Deaths
+ Align:Right
+ ScrollPanel@PLAYERS:
+ X:25
+ Y:70
+ Width:PARENT_RIGHT-50
+ Height:PARENT_BOTTOM-45-50
+ ItemSpacing:5
+ Children:
+ ScrollItem@TEAM_TEMPLATE:
+ X:0
+ Y:0
+ Width:PARENT_RIGHT-30
+ Height:25
+ Children:
+ Label@TEAM:
+ X:0
+ Y:0
+ Width:PARENT_RIGHT
+ Height:PARENT_BOTTOM
+ Font:Bold
+ ScrollItem@PLAYER_TEMPLATE:
+ X:0
+ Y:0
+ Width:PARENT_RIGHT-30
+ Height:25
+ Children:
+ Image@FACTION_FLAG:
+ X:15
+ Y:5
+ Width:40
+ Height:PARENT_BOTTOM
+ ImageName:random
+ ImageCollection:flags
+ Label@PLAYER:
+ X:55
+ Y:0
+ Width:160
+ Height:PARENT_BOTTOM
+ Font:Bold
+ Label@CASH:
+ X:215
+ Y:0
+ Width:80
+ Height:PARENT_BOTTOM
+ Label@POWER:
+ X:295
+ Y:0
+ Width:80
+ Height:PARENT_BOTTOM
+ Label@KILLS:
+ X:375
+ Y:0
+ Width:40
+ Height:PARENT_BOTTOM
+ Align:Right
+ Label@DEATHS:
+ X:415
+ Y:0
+ Width:40
+ Height:PARENT_BOTTOM
+ Align:Right
Background@FMVPLAYER:
Width:WINDOW_RIGHT
Height:WINDOW_BOTTOM