From e424883857fbadb060b7abca4c2b8c76dcdbba99 Mon Sep 17 00:00:00 2001 From: Scott_NZ Date: Tue, 27 Nov 2012 17:11:47 +1300 Subject: [PATCH] Improve graph. Add things like scales/axis labelling. --- OpenRA.Mods.RA/Player/PlayerStatistics.cs | 2 +- .../Widgets/Logic/ObserverStatsLogic.cs | 2 - .../Widgets/ObserverProductionIconsWidget.cs | 4 +- .../Widgets/ObserverStatsGraphWidget.cs | 124 +++++++++++++----- .../ObserverSupportPowerIconsWidget.cs | 4 +- mods/ra/chrome/ingame.yaml | 13 +- 6 files changed, 109 insertions(+), 40 deletions(-) diff --git a/OpenRA.Mods.RA/Player/PlayerStatistics.cs b/OpenRA.Mods.RA/Player/PlayerStatistics.cs index 2d12cb745c..894a47c814 100644 --- a/OpenRA.Mods.RA/Player/PlayerStatistics.cs +++ b/OpenRA.Mods.RA/Player/PlayerStatistics.cs @@ -75,7 +75,7 @@ namespace OpenRA.Mods.RA public void Tick(Actor self) { - if (self.World.FrameNumber % 1500 == 0) + if (self.World.FrameNumber % 1500 == 1) { UpdateEarnedThisMinute(); } diff --git a/OpenRA.Mods.RA/Widgets/Logic/ObserverStatsLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/ObserverStatsLogic.cs index 8e55229c54..d6a6dff840 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/ObserverStatsLogic.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/ObserverStatsLogic.cs @@ -153,8 +153,6 @@ namespace OpenRA.Mods.RA.Widgets.Logic var graph = template.Get("EARNED_THIS_MIN_GRAPH"); graph.GetDataSource = () => players.Select(p => Pair.New(p, p.PlayerActor.Trait().EarnedSamples.Select(s => (float)s))); - graph.GetDataScale = () => 1 / 100f; - graph.GetLastValueFormat = () => "${0}"; playerStatsPanel.AddChild(template); } diff --git a/OpenRA.Mods.RA/Widgets/ObserverProductionIconsWidget.cs b/OpenRA.Mods.RA/Widgets/ObserverProductionIconsWidget.cs index 3aa47af80a..ae598f6916 100644 --- a/OpenRA.Mods.RA/Widgets/ObserverProductionIconsWidget.cs +++ b/OpenRA.Mods.RA/Widgets/ObserverProductionIconsWidget.cs @@ -79,8 +79,8 @@ namespace OpenRA.Mods.RA.Widgets var clock = clocks[queue.Trait]; clock.PlayFetchIndex("idle", - () => (item.TotalTime - item.RemainingTime) - * (clock.CurrentSequence.Length - 1) / item.TotalTime); + () => item.TotalTime == 0 ? 0 : ((item.TotalTime - item.RemainingTime) + * (clock.CurrentSequence.Length - 1) / item.TotalTime)); clock.Tick(); WidgetUtils.DrawSHP(clock.Image, location, worldRenderer, size); diff --git a/OpenRA.Mods.RA/Widgets/ObserverStatsGraphWidget.cs b/OpenRA.Mods.RA/Widgets/ObserverStatsGraphWidget.cs index 1360a9217b..653fb96fa3 100644 --- a/OpenRA.Mods.RA/Widgets/ObserverStatsGraphWidget.cs +++ b/OpenRA.Mods.RA/Widgets/ObserverStatsGraphWidget.cs @@ -18,68 +18,132 @@ namespace OpenRA.Widgets { public class ObserverStatsGraphWidget : Widget { - public Func>>> GetDataSource = () => null; - public Func GetDataScale = () => 1.0f; - public Func GetLastValueFormat = () => "{0}"; - public Func GetNodeCount = () => 20; - public Func GetNodeStep = () => 5; + public Func>>> GetDataSource; + public Func GetDataScale; + public Func GetValueFormat; + public Func GetXAxisValueFormat; + public Func GetYAxisValueFormat; + public Func GetVisibleNodeCount; + public Func GetXAxisLabel; + public Func GetYAxisLabel; + public Func GetDisplayYAxisZero; + public string ValueFormat = "{0}"; + public string XAxisValueFormat = "{0}"; + public string YAxisValueFormat = "{0}"; + public int VisibleNodeCount = 10; + public string XAxisLabel = ""; + public string YAxisLabel = ""; + public bool DisplayYAxisZero = true; - public ObserverStatsGraphWidget() : base() { } + public ObserverStatsGraphWidget() + : base() + { + GetValueFormat = () => ValueFormat; + GetXAxisValueFormat = () => XAxisValueFormat; + GetYAxisValueFormat = () => YAxisValueFormat; + GetVisibleNodeCount = () => VisibleNodeCount; + GetXAxisLabel = () => XAxisLabel; + GetYAxisLabel = () => YAxisLabel; + GetDisplayYAxisZero = () => DisplayYAxisZero; + } protected ObserverStatsGraphWidget(ObserverStatsGraphWidget other) : base(other) { GetDataSource = other.GetDataSource; + GetValueFormat = other.GetValueFormat; + GetXAxisValueFormat = other.GetXAxisValueFormat; + GetYAxisValueFormat = other.GetYAxisValueFormat; + GetVisibleNodeCount = other.GetVisibleNodeCount; + GetXAxisLabel = other.GetXAxisLabel; + GetYAxisLabel = other.GetYAxisLabel; + GetDisplayYAxisZero = other.GetDisplayYAxisZero; + ValueFormat = other.ValueFormat; + XAxisValueFormat = other.XAxisValueFormat; + YAxisValueFormat = other.YAxisValueFormat; + VisibleNodeCount = other.VisibleNodeCount; + XAxisLabel = other.XAxisLabel; + YAxisLabel = other.YAxisLabel; + DisplayYAxisZero = other.DisplayYAxisZero; } public override void Draw() { + if (GetDataSource == null || !GetDataSource().Any()) + { + return; + } var rect = RenderBounds; var origin = new float2(rect.Left, rect.Bottom); - var basis = new float2(rect.Width / 100, rect.Height / 100); - Game.Renderer.LineRenderer.DrawLine(origin, origin + new float2(100, 0) * basis, Color.White, Color.White); - Game.Renderer.LineRenderer.DrawLine(origin, origin - new float2(0, 100) * basis, Color.White, Color.White); - Game.Renderer.LineRenderer.DrawLine(origin + new float2(100, 0) * basis, origin + new float2(100, -100) * basis, Color.White, Color.White); + var width = rect.Width; + var height = rect.Height; + + Game.Renderer.LineRenderer.DrawLine(origin, origin + new float2(width, 0), Color.White, Color.White); + Game.Renderer.LineRenderer.DrawLine(origin, origin + new float2(0, -height), Color.White, Color.White); + Game.Renderer.LineRenderer.DrawLine(origin + new float2(width, 0), origin + new float2(width, -height), Color.White, Color.White); + Game.Renderer.LineRenderer.DrawLine(origin + new float2(0, -height), origin + new float2(width, -height), Color.White, Color.White); var tinyBold = Game.Renderer.Fonts["TinyBold"]; + var bold = Game.Renderer.Fonts["Bold"]; - var i = 0; - foreach (var pair in GetDataSource()) + var visibleNodeCount = GetVisibleNodeCount(); + var visibleNodeStep = width / visibleNodeCount; + var actualNodeCount = GetDataSource().First().Second.Count(); + var visibleNodeStart = Math.Max(0, actualNodeCount - visibleNodeCount); + var visibleNodeEnd = Math.Max(actualNodeCount, visibleNodeCount); + + float maxValue = GetDataSource().Select(p => p.Second).SelectMany(d => d).Max(); + var scale = (100 / maxValue) * 3; + + //todo: make this stuff not draw outside of the RenderBounds + for (int n = visibleNodeStart, i = 0; n <= visibleNodeEnd; n++, i++) { - var player = pair.First; - var data = pair.Second.Reverse().Take(GetNodeCount()).Reverse(); + var x = i * visibleNodeStep; + Game.Renderer.LineRenderer.DrawLine(origin + new float2(x, 0), origin + new float2(x, -5), Color.White, Color.White); + tinyBold.DrawText(GetXAxisValueFormat().F(n), origin + new float2(x, 2), Color.White); + } + bold.DrawText(GetXAxisLabel(), origin + new float2(width / 2, 20), Color.White); + + for (var i = (GetDisplayYAxisZero() ? 0f : height / 10); i <= height; i += height / 10) + { + var value = i / scale; + Game.Renderer.LineRenderer.DrawLine(origin + new float2(width - 5, -i), origin + new float2(width, -i), Color.White, Color.White); + tinyBold.DrawText(GetYAxisValueFormat().F(value), origin + new float2(width + 2, -i), Color.White); + } + bold.DrawText(GetYAxisLabel(), origin + new float2(width + 40, -(height / 2)), Color.White); + + var playerNameOffset = 0; + foreach (var playerDataPair in GetDataSource()) + { + var player = playerDataPair.First; + var data = playerDataPair.Second; var color = player.ColorRamp.GetColor(0); if (data.Any()) { - var scale = GetDataScale(); + data = data.Reverse().Take(visibleNodeCount).Reverse(); var scaledData = data.Select(d => d * scale); - var n = 0; - var step = GetNodeStep(); + var x = 0f; scaledData.Aggregate((a, b) => { Game.Renderer.LineRenderer.DrawLine( - origin + new float2(n, -a) * basis, - origin + new float2(n + step, -b) * basis, + origin + new float2(x, -a), + origin + new float2(x + visibleNodeStep, -b), color, color); - n += step; + x += visibleNodeStep; return b; }); - var lastValue = data.Last(); - if (lastValue != 0) + var value = data.Last(); + if (value != 0) { - var scaledLastValue = lastValue * scale; - var lastValueFormat = GetLastValueFormat(); - if (lastValueFormat != null) - { - tinyBold.DrawText(lastValueFormat.F(lastValue), origin + new float2(n, -scaledLastValue - 2) * basis, color); - } + var scaledValue = value * scale; + tinyBold.DrawText(GetValueFormat().F(value), origin + new float2(x, -scaledValue - 2), color); } } - tinyBold.DrawText(player.PlayerName, new float2(rect.Left, rect.Top) + new float2(5, 10 * i - 3), color); - i++; + tinyBold.DrawText(player.PlayerName, new float2(rect.Left, rect.Top) + new float2(5, 10 * playerNameOffset + 3), color); + playerNameOffset++; } } diff --git a/OpenRA.Mods.RA/Widgets/ObserverSupportPowerIconsWidget.cs b/OpenRA.Mods.RA/Widgets/ObserverSupportPowerIconsWidget.cs index c20c8c69e6..ddfb359214 100644 --- a/OpenRA.Mods.RA/Widgets/ObserverSupportPowerIconsWidget.cs +++ b/OpenRA.Mods.RA/Widgets/ObserverSupportPowerIconsWidget.cs @@ -79,8 +79,8 @@ namespace OpenRA.Mods.RA.Widgets var clock = clocks[power.a.Key]; clock.PlayFetchIndex("idle", - () => (item.TotalTime - item.RemainingTime) - * (clock.CurrentSequence.Length - 1) / item.TotalTime); + () => item.TotalTime == 0 ? 0 : ((item.TotalTime - item.RemainingTime) + * (clock.CurrentSequence.Length - 1) / item.TotalTime)); clock.Tick(); WidgetUtils.DrawSHP(clock.Image, location, worldRenderer, size); diff --git a/mods/ra/chrome/ingame.yaml b/mods/ra/chrome/ingame.yaml index 2bdaf44fe2..71852df2ba 100644 --- a/mods/ra/chrome/ingame.yaml +++ b/mods/ra/chrome/ingame.yaml @@ -862,14 +862,21 @@ Container@OBSERVER_ROOT: Container@EARNED_THIS_MIN_GRAPH_TEMPLATE: X:0 Y:0 - Width:PARENT_RIGHT-35 - Height:300 + Width:PARENT_RIGHT-100 + Height:PARENT_BOTTOM-50 Children: ObserverStatsGraph@EARNED_THIS_MIN_GRAPH: X:0 Y:0 - Width:800 + Width:PARENT_RIGHT Height:PARENT_BOTTOM + ValueFormat:${0} + XAxisValueFormat:{0} + YAxisValueFormat:${0:F0} + VisibleNodeCount:20 + XAxisLabel:m + YAxisLabel:$ + DisplayYAxisZero:false Background@FMVPLAYER: Width:WINDOW_RIGHT Height:WINDOW_BOTTOM