diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj
index 59ed96a34e..988c9c1d70 100644
--- a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj
+++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj
@@ -382,7 +382,8 @@
-
+
+
diff --git a/OpenRA.Mods.RA/Widgets/Logic/ObserverStatsLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/ObserverStatsLogic.cs
index 170eddab3b..8b53853b52 100644
--- a/OpenRA.Mods.RA/Widgets/Logic/ObserverStatsLogic.cs
+++ b/OpenRA.Mods.RA/Widgets/Logic/ObserverStatsLogic.cs
@@ -21,18 +21,13 @@ namespace OpenRA.Mods.RA.Widgets.Logic
{
public class ObserverStatsLogic
{
- class StatsDropDownOption
- {
- public string Title;
- public Func IsSelected;
- public Action OnClick;
- }
-
ContainerWidget basicStatsHeaders;
ContainerWidget economicStatsHeaders;
+ ContainerWidget supportStatsHeaders;
ScrollPanelWidget playerStatsPanel;
ScrollItemWidget basicPlayerTemplate;
ScrollItemWidget economicPlayerTemplate;
+ ScrollItemWidget supportPlayerTemplate;
ScrollItemWidget teamTemplate;
DropDownButtonWidget statsDropDown;
LabelWidget title;
@@ -49,12 +44,15 @@ namespace OpenRA.Mods.RA.Widgets.Logic
basicStatsHeaders = widget.Get("BASIC_STATS_HEADERS");
economicStatsHeaders = widget.Get("ECONOMIC_STATS_HEADERS");
+ supportStatsHeaders = widget.Get("SUPPORT_STATS_HEADERS");
playerStatsPanel = widget.Get("PLAYER_STATS_PANEL");
playerStatsPanel.Layout = new GridLayout(playerStatsPanel);
basicPlayerTemplate = playerStatsPanel.Get("BASIC_PLAYER_TEMPLATE");
economicPlayerTemplate = playerStatsPanel.Get("ECONOMIC_PLAYER_TEMPLATE");
+ supportPlayerTemplate = playerStatsPanel.Get("SUPPORT_PLAYER_TEMPLATE");
+
teamTemplate = playerStatsPanel.Get("TEAM_TEMPLATE");
statsDropDown = widget.Get("STATS_DROPDOWN");
@@ -84,6 +82,17 @@ namespace OpenRA.Mods.RA.Widgets.Logic
statsDropDown.GetText = () => "Economic";
LoadStats(EconomicStats);
}
+ },
+ new StatsDropDownOption
+ {
+ Title = "Support",
+ IsSelected = () => supportStatsHeaders.Visible,
+ OnClick = () =>
+ {
+ ClearStats();
+ statsDropDown.GetText = () => "Support";
+ LoadStats(SupportStats);
+ }
}
};
Func setupItem = (option, template) =>
@@ -107,9 +116,10 @@ namespace OpenRA.Mods.RA.Widgets.Logic
playerStatsPanel.Children.Clear();
basicStatsHeaders.Visible = false;
economicStatsHeaders.Visible = false;
+ supportStatsHeaders.Visible = false;
}
- void LoadStats(Action forEachPlayer)
+ void LoadStats(Func forEachPlayer)
{
var teams = players.GroupBy(p => (world.LobbyInfo.ClientWithIndex(p.ClientIndex) ?? new Session.Client()).Team).OrderBy(g => g.Key);
foreach (var t in teams)
@@ -122,23 +132,30 @@ namespace OpenRA.Mods.RA.Widgets.Logic
foreach (var p in team)
{
var player = p;
- forEachPlayer(player);
+ playerStatsPanel.AddChild(forEachPlayer(player));
}
}
}
- void EconomicStats(Player player)
+ ScrollItemWidget SupportStats(Player player)
+ {
+ supportStatsHeaders.Visible = true;
+ var template = SetupPlayerScrollItemWidget(supportPlayerTemplate, player);
+
+ AddPlayerFlagAndName(template, player);
+
+ var supportPowers = template.Get("SUPPORT_POWERS");
+ supportPowers.GetPlayer = () => player;
+
+ return template;
+ }
+
+ ScrollItemWidget EconomicStats(Player player)
{
economicStatsHeaders.Visible = true;
var template = SetupPlayerScrollItemWidget(economicPlayerTemplate, player);
- 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);
+ AddPlayerFlagAndName(template, player);
var res = player.PlayerActor.Trait();
template.Get("CASH").GetText = () => "$" + (res.DisplayCash + res.DisplayOre);
@@ -147,9 +164,10 @@ namespace OpenRA.Mods.RA.Widgets.Logic
change.GetText = () => Math.Round(res.IncomeChange * 100, 1, MidpointRounding.AwayFromZero) + "%";
change.GetColor = () =>
{
- if (res.IncomeChange < 0) return Color.Red;
- if (res.IncomeChange > 0) return Color.LimeGreen;
- else return Color.White;
+ var c = Math.Round(res.IncomeChange, 1, MidpointRounding.AwayFromZero);
+ if (c < 0) return Color.Red;
+ if (c > 0) return Color.LimeGreen;
+ return Color.White;
};
var assets = template.Get("TOTAL_ASSETS");
@@ -160,21 +178,15 @@ namespace OpenRA.Mods.RA.Widgets.Logic
var numHarvesters = template.Get("NUMBER_HARVESTERS");
numHarvesters.GetText = () => world.Actors.Count(a => a.Owner == player && !a.IsDead() && a.HasTrait()).ToString();
- playerStatsPanel.AddChild(template);
+ return template;
}
- void BasicStats(Player player)
+ ScrollItemWidget BasicStats(Player player)
{
basicStatsHeaders.Visible = true;
var template = SetupPlayerScrollItemWidget(basicPlayerTemplate, player);
- 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);
+ AddPlayerFlagAndName(template, player);
var res = player.PlayerActor.Trait();
template.Get("CASH").GetText = () => "$" + (res.DisplayCash + res.DisplayOre);
@@ -188,10 +200,10 @@ namespace OpenRA.Mods.RA.Widgets.Logic
template.Get("KILLS").GetText = () => player.Kills.ToString();
template.Get("DEATHS").GetText = () => player.Deaths.ToString();
- var production = template.Get("PRODUCTION_ICONS");
+ var production = template.Get("PRODUCTION_ICONS");
production.GetPlayer = () => player;
- playerStatsPanel.AddChild(template);
+ return template;
}
ScrollItemWidget SetupPlayerScrollItemWidget(ScrollItemWidget template, Player player)
@@ -206,6 +218,17 @@ namespace OpenRA.Mods.RA.Widgets.Logic
});
}
+ static void AddPlayerFlagAndName(ScrollItemWidget template, Player player)
+ {
+ var flag = template.Get("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);
+ }
+
static void InitializeWidgets(params Widget[] widgets)
{
var args = new WidgetArgs();
@@ -221,5 +244,12 @@ namespace OpenRA.Mods.RA.Widgets.Logic
if (state == PowerState.Low) return Color.Orange;
return Color.LimeGreen;
}
+
+ class StatsDropDownOption
+ {
+ public string Title;
+ public Func IsSelected;
+ public Action OnClick;
+ }
}
}
diff --git a/OpenRA.Mods.RA/Widgets/ObserverBuildIconsWidget.cs b/OpenRA.Mods.RA/Widgets/ObserverProductionIconsWidget.cs
similarity index 91%
rename from OpenRA.Mods.RA/Widgets/ObserverBuildIconsWidget.cs
rename to OpenRA.Mods.RA/Widgets/ObserverProductionIconsWidget.cs
index d66fa78714..68b78a7b20 100644
--- a/OpenRA.Mods.RA/Widgets/ObserverBuildIconsWidget.cs
+++ b/OpenRA.Mods.RA/Widgets/ObserverProductionIconsWidget.cs
@@ -17,7 +17,7 @@ using System.Drawing;
namespace OpenRA.Mods.RA.Widgets
{
- public class ObserverBuildIconsWidget : Widget
+ public class ObserverProductionIconsWidget : Widget
{
public Func GetPlayer;
Dictionary iconSprites;
@@ -26,7 +26,7 @@ namespace OpenRA.Mods.RA.Widgets
Dictionary clocks;
[ObjectCreator.UseCtor]
- public ObserverBuildIconsWidget(World world, WorldRenderer worldRenderer)
+ public ObserverProductionIconsWidget(World world, WorldRenderer worldRenderer)
: base()
{
iconSprites = Rules.Info.Values.Where(u => u.Traits.Contains() && u.Name[0] != '^')
@@ -38,7 +38,7 @@ namespace OpenRA.Mods.RA.Widgets
clocks = new Dictionary();
}
- protected ObserverBuildIconsWidget(ObserverBuildIconsWidget other)
+ protected ObserverProductionIconsWidget(ObserverProductionIconsWidget other)
: base(other)
{
GetPlayer = other.GetPlayer;
@@ -101,7 +101,7 @@ namespace OpenRA.Mods.RA.Widgets
public override Widget Clone()
{
- return new ObserverBuildIconsWidget(this);
+ return new ObserverProductionIconsWidget(this);
}
}
}
diff --git a/OpenRA.Mods.RA/Widgets/ObserverSupportPowerIconsWidget.cs b/OpenRA.Mods.RA/Widgets/ObserverSupportPowerIconsWidget.cs
new file mode 100644
index 0000000000..fba606a789
--- /dev/null
+++ b/OpenRA.Mods.RA/Widgets/ObserverSupportPowerIconsWidget.cs
@@ -0,0 +1,107 @@
+#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;
+using System.Collections.Generic;
+using System.Linq;
+using OpenRA.Graphics;
+using OpenRA.Widgets;
+using System.Drawing;
+
+namespace OpenRA.Mods.RA.Widgets
+{
+ public class ObserverSupportPowerIconsWidget : Widget
+ {
+ public Func GetPlayer;
+ Dictionary iconSprites;
+ World world;
+ WorldRenderer worldRenderer;
+ Dictionary clocks;
+
+ [ObjectCreator.UseCtor]
+ public ObserverSupportPowerIconsWidget(World world, WorldRenderer worldRenderer)
+ : base()
+ {
+ iconSprites = Rules.Info.Values.SelectMany(u => u.Traits.WithInterface())
+ .Select(u => u.Image).Distinct()
+ .ToDictionary(
+ u => u,
+ u => Game.modData.SpriteLoader.LoadAllSprites(u)[0]);
+ this.world = world;
+ this.worldRenderer = worldRenderer;
+ clocks = new Dictionary();
+ }
+
+ protected ObserverSupportPowerIconsWidget(ObserverSupportPowerIconsWidget other)
+ : base(other)
+ {
+ GetPlayer = other.GetPlayer;
+ iconSprites = other.iconSprites;
+ world = other.world;
+ worldRenderer = other.worldRenderer;
+ clocks = other.clocks;
+ }
+
+ public override void Draw()
+ {
+ var player = GetPlayer();
+ if (player == null)
+ {
+ return;
+ }
+ var powers = player.PlayerActor.Trait().Powers
+ .Select((a, i) => new { a, i });
+ foreach (var power in powers)
+ {
+ if (!clocks.ContainsKey(power.a.Key))
+ {
+ clocks.Add(power.a.Key, new Animation("clock"));
+ }
+ }
+ foreach (var power in powers)
+ {
+ var item = power.a.Value;
+ if (item == null)
+ {
+ continue;
+ }
+ var sprite = iconSprites[item.Info.Image];
+ var size = sprite.size / new float2(2, 2);
+ var location = new float2(RenderBounds.Location) + new float2(power.i * (int)size.Length, 0);
+ WidgetUtils.DrawSHP(sprite, location, worldRenderer, size);
+
+ var clock = clocks[power.a.Key];
+ clock.PlayFetchIndex("idle",
+ () => (item.TotalTime - item.RemainingTime)
+ * (clock.CurrentSequence.Length - 1) / item.TotalTime);
+ clock.Tick();
+ WidgetUtils.DrawSHP(clock.Image, location, worldRenderer, size);
+
+ var tiny = Game.Renderer.Fonts["Tiny"];
+ var text = GetOverlayForItem(item);
+ tiny.DrawTextWithContrast(text,
+ location + new float2(16, 16) - new float2(tiny.Measure(text).X / 2, 0),
+ Color.White, Color.Black, 1);
+ }
+ }
+
+ static string GetOverlayForItem(SupportPowerManager.SupportPowerInstance item)
+ {
+ if (item.Disabled) return "ON HOLD";
+ if (item.Ready) return "READY";
+ return WidgetUtils.FormatTime(item.RemainingTime);
+ }
+
+ public override Widget Clone()
+ {
+ return new ObserverSupportPowerIconsWidget(this);
+ }
+ }
+}
diff --git a/mods/ra/chrome/ingame.yaml b/mods/ra/chrome/ingame.yaml
index e7f855d18f..5f49aff40d 100644
--- a/mods/ra/chrome/ingame.yaml
+++ b/mods/ra/chrome/ingame.yaml
@@ -518,6 +518,27 @@ Container@OBSERVER_ROOT:
Font:Bold
Text:Harvesters
Align:Right
+ Container@SUPPORT_STATS_HEADERS:
+ X:0
+ Y:0
+ Width:PARENT_RIGHT
+ Height:PARENT_BOTTOM
+ Children:
+ Label@PLAYER_HEADER:
+ X:85
+ Y:40
+ Width:160
+ Height:25
+ Font:Bold
+ Text:Player
+ Label@SUPPORT_POWERS_HEADER:
+ X:245
+ Y:40
+ Width:300
+ Height:25
+ Font:Bold
+ Text:Support Powers
+ Align:Center
ScrollPanel@PLAYER_STATS_PANEL:
X:25
Y:70
@@ -543,7 +564,7 @@ Container@OBSERVER_ROOT:
Width:PARENT_RIGHT-35
Height:25
Children:
- Image@FACTION_FLAG:
+ Image@FLAG:
X:20
Y:5
Width:35
@@ -583,7 +604,7 @@ Container@OBSERVER_ROOT:
Width:40
Height:PARENT_BOTTOM
Align:Right
- ObserverBuildIcons@PRODUCTION_ICONS:
+ ObserverProductionIcons@PRODUCTION_ICONS:
X:575
Y:0
Width:240
@@ -594,7 +615,7 @@ Container@OBSERVER_ROOT:
Width:PARENT_RIGHT-35
Height:25
Children:
- Image@FACTION_FLAG:
+ Image@FLAG:
X:20
Y:5
Width:35
@@ -633,6 +654,30 @@ Container@OBSERVER_ROOT:
Width:60
Height:PARENT_BOTTOM
Align:Right
+ ScrollItem@SUPPORT_PLAYER_TEMPLATE:
+ X:0
+ Y:0
+ Width:PARENT_RIGHT-35
+ Height:25
+ Children:
+ Image@FLAG:
+ X:20
+ Y:5
+ Width:35
+ Height:PARENT_BOTTOM-5
+ ImageName:random
+ ImageCollection:flags
+ Label@PLAYER:
+ X:55
+ Y:0
+ Width:160
+ Height:PARENT_BOTTOM
+ Font:Bold
+ ObserverSupportPowerIcons@SUPPORT_POWERS:
+ X:215
+ Y:0
+ Width:300
+ Height:PARENT_BOTTOM
Background@FMVPLAYER:
Width:WINDOW_RIGHT
Height:WINDOW_BOTTOM