diff --git a/OpenRA.Mods.Common/Traits/Player/PlayerStatistics.cs b/OpenRA.Mods.Common/Traits/Player/PlayerStatistics.cs index 56a4d77a6d..2725e4a7c8 100644 --- a/OpenRA.Mods.Common/Traits/Player/PlayerStatistics.cs +++ b/OpenRA.Mods.Common/Traits/Player/PlayerStatistics.cs @@ -10,7 +10,11 @@ #endregion using System.Collections.Generic; +using System.Linq; +using OpenRA; using OpenRA.Graphics; +using OpenRA.Mods.Common.Traits.Render; +using OpenRA.Primitives; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -64,8 +68,12 @@ namespace OpenRA.Mods.Common.Traits bool armyGraphDisabled; bool incomeGraphDisabled; + public readonly Cache Units; - public PlayerStatistics(Actor self) { } + public PlayerStatistics(Actor self) + { + Units = new Cache(name => new ArmyUnit(self.World.Map.Rules.Actors[name], self.Owner)); + } void INotifyCreated.Created(Actor self) { @@ -151,28 +159,74 @@ namespace OpenRA.Mods.Common.Traits } } + public class ArmyUnit + { + public readonly ActorInfo ActorInfo; + public readonly Animation Icon; + public readonly string IconPalette; + public readonly int ProductionQueueOrder; + public readonly int BuildPaletteOrder; + public readonly TooltipInfo TooltipInfo; + public readonly BuildableInfo BuildableInfo; + + public int Count { get; set; } + + public ArmyUnit(ActorInfo actorInfo, Player owner) + { + ActorInfo = actorInfo; + + var queues = owner.World.Map.Rules.Actors.Values + .SelectMany(a => a.TraitInfos()); + + BuildableInfo = actorInfo.TraitInfoOrDefault(); + TooltipInfo = actorInfo.TraitInfos().FirstOrDefault(info => info.EnabledByDefault); + + ProductionQueueOrder = queues.Where(q => BuildableInfo.Queue.Contains(q.Type)) + .Select(q => q.DisplayOrder) + .MinByOrDefault(o => o); + + var rsi = actorInfo.TraitInfoOrDefault(); + + if (BuildableInfo != null && rsi != null) + { + var image = rsi.GetImage(actorInfo, owner.World.Map.Rules.Sequences, owner.Faction.Name); + Icon = new Animation(owner.World, image); + Icon.Play(BuildableInfo.Icon); + IconPalette = BuildableInfo.IconPalette; + BuildPaletteOrder = BuildableInfo.BuildPaletteOrder; + } + } + } + [Desc("Attach this to a unit to update observer stats.")] public class UpdatesPlayerStatisticsInfo : ITraitInfo { [Desc("Add to army value in statistics")] public bool AddToArmyValue = false; + [ActorReference] + [Desc("Count this actor as a different type in the spectator army display.")] + public string OverrideActor = null; + public object Create(ActorInitializer init) { return new UpdatesPlayerStatistics(this, init.Self); } } public class UpdatesPlayerStatistics : INotifyKilled, INotifyCreated, INotifyOwnerChanged, INotifyActorDisposing { - UpdatesPlayerStatisticsInfo info; + readonly UpdatesPlayerStatisticsInfo info; + readonly string actorName; + readonly int cost = 0; + PlayerStatistics playerStats; - int cost = 0; bool includedInArmyValue = false; public UpdatesPlayerStatistics(UpdatesPlayerStatisticsInfo info, Actor self) { this.info = info; - if (self.Info.HasTraitInfo()) - cost = self.Info.TraitInfo().Cost; + var valuedInfo = self.Info.TraitInfoOrDefault(); + cost = valuedInfo != null ? valuedInfo.Cost : 0; playerStats = self.Owner.PlayerActor.Trait(); + actorName = info.OverrideActor ?? self.Info.Name; } void INotifyKilled.Killed(Actor self, AttackInfo e) @@ -199,14 +253,19 @@ namespace OpenRA.Mods.Common.Traits { defenderStats.ArmyValue -= cost; includedInArmyValue = false; + playerStats.Units[actorName].Count--; } } void INotifyCreated.Created(Actor self) { includedInArmyValue = info.AddToArmyValue; + if (includedInArmyValue) + { playerStats.ArmyValue += cost; + playerStats.Units[actorName].Count++; + } } void INotifyOwnerChanged.OnOwnerChanged(Actor self, Player oldOwner, Player newOwner) @@ -216,6 +275,8 @@ namespace OpenRA.Mods.Common.Traits { playerStats.ArmyValue -= cost; newOwnerStats.ArmyValue += cost; + playerStats.Units[actorName].Count--; + newOwnerStats.Units[actorName].Count++; } playerStats = newOwnerStats; @@ -227,6 +288,7 @@ namespace OpenRA.Mods.Common.Traits { playerStats.ArmyValue -= cost; includedInArmyValue = false; + playerStats.Units[actorName].Count--; } } } diff --git a/OpenRA.Mods.Common/Widgets/Logic/Ingame/ArmyTooltipLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Ingame/ArmyTooltipLogic.cs new file mode 100644 index 0000000000..c0ba67dcae --- /dev/null +++ b/OpenRA.Mods.Common/Widgets/Logic/Ingame/ArmyTooltipLogic.cs @@ -0,0 +1,67 @@ +#region Copyright & License Information +/* + * Copyright 2007-2020 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, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using System; +using System.Linq; +using OpenRA.Mods.Common.Traits; +using OpenRA.Widgets; + +namespace OpenRA.Mods.Common.Widgets.Logic +{ + public class ArmyTooltipLogic : ChromeLogic + { + [ObjectCreator.UseCtor] + public ArmyTooltipLogic(Widget widget, TooltipContainerWidget tooltipContainer, Func getTooltipUnit) + { + widget.IsVisible = () => getTooltipUnit() != null; + var nameLabel = widget.Get("NAME"); + var descLabel = widget.Get("DESC"); + + var font = Game.Renderer.Fonts[nameLabel.Font]; + var descFont = Game.Renderer.Fonts[descLabel.Font]; + + ArmyUnit lastArmyUnit = null; + var descLabelPadding = descLabel.Bounds.Height; + + tooltipContainer.BeforeRender = () => + { + var armyUnit = getTooltipUnit(); + + if (armyUnit == null || armyUnit == lastArmyUnit) + return; + + var tooltip = armyUnit.TooltipInfo; + var name = tooltip != null ? tooltip.Name : armyUnit.ActorInfo.Name; + var buildable = armyUnit.BuildableInfo; + + nameLabel.Text = name; + + var nameSize = font.Measure(name); + + descLabel.Text = buildable.Description.Replace("\\n", "\n"); + var descSize = descFont.Measure(descLabel.Text); + descLabel.Bounds.Width = descSize.X; + descLabel.Bounds.Height = descSize.Y + descLabelPadding; + + var leftWidth = Math.Max(nameSize.X, descSize.X); + + widget.Bounds.Width = leftWidth + 2 * nameLabel.Bounds.X; + + // Set the bottom margin to match the left margin + var leftHeight = descLabel.Bounds.Bottom + descLabel.Bounds.X; + + widget.Bounds.Height = leftHeight; + + lastArmyUnit = armyUnit; + }; + } + } +} diff --git a/OpenRA.Mods.Common/Widgets/Logic/Ingame/ObserverStatsLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Ingame/ObserverStatsLogic.cs index 9fbb57753c..49061965e8 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Ingame/ObserverStatsLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Ingame/ObserverStatsLogic.cs @@ -21,9 +21,9 @@ using OpenRA.Widgets; namespace OpenRA.Mods.Common.Widgets.Logic { - public enum ObserverStatsPanel { None, Basic, Economy, Production, SupportPowers, Combat, Graph, ArmyGraph } + public enum ObserverStatsPanel { None, Basic, Economy, Production, SupportPowers, Combat, Army, Graph, ArmyGraph } - [ChromeLogicArgsHotkeys("StatisticsBasicKey", "StatisticsEconomyKey", "StatisticsProductionKey", "StatisticsSupportPowersKey", "StatisticsCombatKey", "StatisticsGraphKey", + [ChromeLogicArgsHotkeys("StatisticsBasicKey", "StatisticsEconomyKey", "StatisticsProductionKey", "StatisticsSupportPowersKey", "StatisticsCombatKey", "StatisticsArmyKey", "StatisticsGraphKey", "StatisticsArmyGraphKey")] public class ObserverStatsLogic : ChromeLogic { @@ -32,11 +32,13 @@ namespace OpenRA.Mods.Common.Widgets.Logic readonly ContainerWidget productionStatsHeaders; readonly ContainerWidget supportPowerStatsHeaders; readonly ContainerWidget combatStatsHeaders; + readonly ContainerWidget armyHeaders; readonly ScrollPanelWidget playerStatsPanel; readonly ScrollItemWidget basicPlayerTemplate; readonly ScrollItemWidget economyPlayerTemplate; readonly ScrollItemWidget productionPlayerTemplate; readonly ScrollItemWidget supportPowersPlayerTemplate; + readonly ScrollItemWidget armyPlayerTemplate; readonly ScrollItemWidget combatPlayerTemplate; readonly ContainerWidget incomeGraphContainer; readonly ContainerWidget armyValueGraphContainer; @@ -72,6 +74,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic economyStatsHeaders = widget.Get("ECONOMY_STATS_HEADERS"); productionStatsHeaders = widget.Get("PRODUCTION_STATS_HEADERS"); supportPowerStatsHeaders = widget.Get("SUPPORT_POWERS_HEADERS"); + armyHeaders = widget.Get("ARMY_HEADERS"); combatStatsHeaders = widget.Get("COMBAT_STATS_HEADERS"); playerStatsPanel = widget.Get("PLAYER_STATS_PANEL"); @@ -87,12 +90,14 @@ namespace OpenRA.Mods.Common.Widgets.Logic AdjustHeader(productionStatsHeaders); AdjustHeader(supportPowerStatsHeaders); AdjustHeader(combatStatsHeaders); + AdjustHeader(armyHeaders); } basicPlayerTemplate = playerStatsPanel.Get("BASIC_PLAYER_TEMPLATE"); economyPlayerTemplate = playerStatsPanel.Get("ECONOMY_PLAYER_TEMPLATE"); productionPlayerTemplate = playerStatsPanel.Get("PRODUCTION_PLAYER_TEMPLATE"); supportPowersPlayerTemplate = playerStatsPanel.Get("SUPPORT_POWERS_PLAYER_TEMPLATE"); + armyPlayerTemplate = playerStatsPanel.Get("ARMY_PLAYER_TEMPLATE"); combatPlayerTemplate = playerStatsPanel.Get("COMBAT_PLAYER_TEMPLATE"); incomeGraphContainer = widget.Get("INCOME_GRAPH_CONTAINER"); @@ -144,6 +149,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic createStatsOption("Production", ObserverStatsPanel.Production, productionPlayerTemplate, () => DisplayStats(ProductionStats)), createStatsOption("Support Powers", ObserverStatsPanel.SupportPowers, supportPowersPlayerTemplate, () => DisplayStats(SupportPowerStats)), createStatsOption("Combat", ObserverStatsPanel.Combat, combatPlayerTemplate, () => DisplayStats(CombatStats)), + createStatsOption("Army", ObserverStatsPanel.Army, armyPlayerTemplate, () => DisplayStats(ArmyStats)), createStatsOption("Earnings (graph)", ObserverStatsPanel.Graph, null, () => IncomeGraph()), createStatsOption("Army (graph)", ObserverStatsPanel.ArmyGraph, null, () => ArmyValueGraph()), }; @@ -157,7 +163,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic var statsDropDownPanelTemplate = logicArgs.TryGetValue("StatsDropDownPanelTemplate", out yaml) ? yaml.Value : "LABEL_DROPDOWN_TEMPLATE"; - statsDropDown.OnMouseDown = _ => statsDropDown.ShowDropDown(statsDropDownPanelTemplate, 205, statsDropDownOptions, setupItem); + statsDropDown.OnMouseDown = _ => statsDropDown.ShowDropDown(statsDropDownPanelTemplate, 230, statsDropDownOptions, setupItem); statsDropDownOptions[0].OnClick(); var keyListener = statsDropDown.Get("STATS_DROPDOWN_KEYHANDLER"); @@ -190,6 +196,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic economyStatsHeaders.Visible = false; productionStatsHeaders.Visible = false; supportPowerStatsHeaders.Visible = false; + armyHeaders.Visible = false; combatStatsHeaders.Visible = false; incomeGraphContainer.Visible = false; @@ -343,6 +350,27 @@ namespace OpenRA.Mods.Common.Widgets.Logic return template; } + ScrollItemWidget ArmyStats(Player player) + { + armyHeaders.Visible = true; + var template = SetupPlayerScrollItemWidget(armyPlayerTemplate, player); + + AddPlayerFlagAndName(template, player); + + var playerName = template.Get("PLAYER"); + playerName.GetColor = () => Color.White; + + var playerColor = template.Get("PLAYER_COLOR"); + var playerGradient = template.Get("PLAYER_GRADIENT"); + + SetupPlayerColor(player, template, playerColor, playerGradient); + + template.Get("ARMY_ICONS").GetPlayer = () => player; + template.IgnoreChildMouseOver = false; + + return template; + } + ScrollItemWidget EconomyStats(Player player) { economyStatsHeaders.Visible = true; diff --git a/OpenRA.Mods.Common/Widgets/ObserverArmyIconsWidget.cs b/OpenRA.Mods.Common/Widgets/ObserverArmyIconsWidget.cs new file mode 100644 index 0000000000..c992d8a4b4 --- /dev/null +++ b/OpenRA.Mods.Common/Widgets/ObserverArmyIconsWidget.cs @@ -0,0 +1,209 @@ +#region Copyright & License Information +/* + * Copyright 2007-2020 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, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using OpenRA.Graphics; +using OpenRA.Mods.Common.Traits; +using OpenRA.Primitives; +using OpenRA.Widgets; + +namespace OpenRA.Mods.Common.Widgets +{ + public class ObserverArmyIconsWidget : Widget + { + public Func GetPlayer; + readonly World world; + readonly WorldRenderer worldRenderer; + + public int IconWidth = 32; + public int IconHeight = 24; + public int IconSpacing = 1; + + float2 iconSize; + public int MinWidth = 240; + + public ArmyUnit TooltipUnit { get; private set; } + public Func GetTooltipUnit; + + public readonly string TooltipTemplate = "ARMY_TOOLTIP"; + public readonly string TooltipContainer; + + readonly Lazy tooltipContainer; + readonly List armyIcons = new List(); + + readonly CachedTransform stats = new CachedTransform(player => player.PlayerActor.TraitOrDefault()); + + int lastIconIdx; + Rectangle currentIconBounds; + + [ObjectCreator.UseCtor] + public ObserverArmyIconsWidget(World world, WorldRenderer worldRenderer) + { + this.world = world; + this.worldRenderer = worldRenderer; + + GetTooltipUnit = () => TooltipUnit; + tooltipContainer = Exts.Lazy(() => + Ui.Root.Get(TooltipContainer)); + } + + protected ObserverArmyIconsWidget(ObserverArmyIconsWidget other) + : base(other) + { + GetPlayer = other.GetPlayer; + world = other.world; + worldRenderer = other.worldRenderer; + + IconWidth = other.IconWidth; + IconHeight = other.IconHeight; + IconSpacing = other.IconSpacing; + iconSize = new float2(IconWidth, IconHeight); + + MinWidth = other.MinWidth; + + TooltipUnit = other.TooltipUnit; + GetTooltipUnit = () => TooltipUnit; + + TooltipTemplate = other.TooltipTemplate; + TooltipContainer = other.TooltipContainer; + + tooltipContainer = Exts.Lazy(() => + Ui.Root.Get(TooltipContainer)); + } + + public override void Draw() + { + armyIcons.Clear(); + + var player = GetPlayer(); + if (player == null) + return; + + var playerStatistics = stats.Update(player); + + var items = playerStatistics.Units.Values + .Where(u => u.Count > 0 && u.Icon != null) + .OrderBy(u => u.ProductionQueueOrder) + .ThenBy(u => u.BuildPaletteOrder); + + Game.Renderer.EnableAntialiasingFilter(); + + var queueCol = 0; + foreach (var unit in items) + { + var icon = unit.Icon; + var topLeftOffset = new int2(queueCol * (IconWidth + IconSpacing), 0); + + var iconTopLeft = RenderOrigin + topLeftOffset; + var centerPosition = iconTopLeft; + + WidgetUtils.DrawSHPCentered(icon.Image, centerPosition + 0.5f * iconSize, worldRenderer.Palette(unit.IconPalette), 0.5f); + + armyIcons.Add(new ArmyIcon + { + Bounds = new Rectangle(iconTopLeft.X, iconTopLeft.Y, (int)iconSize.X, (int)iconSize.Y), + Unit = unit + }); + + queueCol++; + } + + Bounds.Width = queueCol * (IconWidth + IconSpacing); + + Game.Renderer.DisableAntialiasingFilter(); + + var bold = Game.Renderer.Fonts["TinyBold"]; + foreach (var armyIcon in armyIcons) + { + var text = armyIcon.Unit.Count.ToString(); + bold.DrawTextWithContrast(text, armyIcon.Bounds.Location + new float2(iconSize.X, 0) - new float2(bold.Measure(text).X, bold.TopOffset), + Color.White, Color.Black, 1); + } + + var parentWidth = Bounds.X + Bounds.Width; + Parent.Bounds.Width = parentWidth; + + var gradient = Parent.Get("PLAYER_GRADIENT"); + + var offset = gradient.Bounds.X - Bounds.X; + var gradientWidth = Math.Max(MinWidth - offset, queueCol * (IconWidth + IconSpacing)); + + gradient.Bounds.Width = gradientWidth; + var widestChildWidth = Parent.Parent.Children.Max(x => x.Bounds.Width); + + Parent.Parent.Bounds.Width = Math.Max(25 + widestChildWidth, Bounds.Left + MinWidth); + } + + public override Widget Clone() + { + return new ObserverArmyIconsWidget(this); + } + + public override void MouseEntered() + { + if (TooltipContainer == null) + return; + + foreach (var armyIcon in armyIcons) + { + if (!armyIcon.Bounds.Contains(Viewport.LastMousePos)) + continue; + + TooltipUnit = armyIcon.Unit; + break; + } + + tooltipContainer.Value.SetTooltip(TooltipTemplate, new WidgetArgs { { "getTooltipUnit", GetTooltipUnit } }); + } + + public override void MouseExited() + { + if (TooltipContainer == null) + return; + + tooltipContainer.Value.RemoveTooltip(); + } + + public override void Tick() + { + if (lastIconIdx >= armyIcons.Count) + { + TooltipUnit = null; + return; + } + + if (TooltipUnit != null && currentIconBounds.Contains(Viewport.LastMousePos)) + return; + + for (var i = 0; i < armyIcons.Count; i++) + { + var armyIcon = armyIcons[i]; + if (!armyIcon.Bounds.Contains(Viewport.LastMousePos)) + continue; + + lastIconIdx = i; + currentIconBounds = armyIcon.Bounds; + TooltipUnit = armyIcon.Unit; + return; + } + + TooltipUnit = null; + } + + class ArmyIcon + { + public Rectangle Bounds { get; set; } + public ArmyUnit Unit { get; set; } + } + } +} diff --git a/mods/cnc/chrome/ingame.yaml b/mods/cnc/chrome/ingame.yaml index 015968b76a..0c8b5b4b4a 100644 --- a/mods/cnc/chrome/ingame.yaml +++ b/mods/cnc/chrome/ingame.yaml @@ -267,6 +267,7 @@ Container@OBSERVER_WIDGETS: StatisticsProductionKey: StatisticsProduction StatisticsSupportPowersKey: StatisticsSupportPowers StatisticsCombatKey: StatisticsCombat + StatisticsArmyKey: StatisticsArmy StatisticsGraphKey: StatisticsGraph StatisticsArmyGraphKey: StatisticsArmyGraph X: 5 @@ -542,6 +543,42 @@ Container@OBSERVER_WIDGETS: Font: Bold Text: Support Powers Shadow: True + Container@ARMY_HEADERS: + X: 0 + Y: 0 + Width: 400 + Height: PARENT_BOTTOM + Children: + ColorBlock@HEADER_COLOR: + X: 0 + Y: 0 + Color: 00000090 + Width: PARENT_RIGHT - 200 + Height: PARENT_BOTTOM + GradientColorBlock@HEADER_GRADIENT: + X: PARENT_RIGHT - 200 + Y: 0 + TopLeftColor: 00000090 + BottomLeftColor: 00000090 + Width: 200 + Height: PARENT_BOTTOM + Label@PLAYER_HEADER: + X: 40 + Y: 0 + Width: 120 + Height: PARENT_BOTTOM + Font: Bold + Text: Player + Align: Left + Shadow: True + Label@ARMY_HEADER: + X: 160 + Y: 0 + Width: 100 + Height: PARENT_BOTTOM + Font: Bold + Text: Army + Shadow: True Container@COMBAT_STATS_HEADERS: X: 0 Y: 0 @@ -911,6 +948,43 @@ Container@OBSERVER_WIDGETS: Width: 0 Height: PARENT_BOTTOM TooltipContainer: TOOLTIP_CONTAINER + ScrollItem@ARMY_PLAYER_TEMPLATE: + X: 0 + Y: 0 + Width: 400 + Height: 24 + BaseName: scrollitem-nohover + Children: + ColorBlock@PLAYER_COLOR: + X: 0 + Y: 0 + Width: PARENT_RIGHT - 200 + Height: PARENT_BOTTOM + GradientColorBlock@PLAYER_GRADIENT: + X: PARENT_RIGHT - 200 + Y: 0 + Width: 200 + Height: PARENT_BOTTOM + Image@FLAG: + X: 5 + Y: 4 + Width: 35 + Height: PARENT_BOTTOM - 4 + ImageName: random + ImageCollection: flags + Label@PLAYER: + X: 40 + Y: 0 + Width: 120 + Height: PARENT_BOTTOM + Font: Bold + Shadow: True + ObserverArmyIcons@ARMY_ICONS: + X: 160 + Y: 0 + Width: 0 + Height: PARENT_BOTTOM + TooltipContainer: TOOLTIP_CONTAINER ScrollItem@COMBAT_PLAYER_TEMPLATE: X: 0 Y: 0 diff --git a/mods/cnc/chrome/tooltips.yaml b/mods/cnc/chrome/tooltips.yaml index a4ed6bb304..add358bb38 100644 --- a/mods/cnc/chrome/tooltips.yaml +++ b/mods/cnc/chrome/tooltips.yaml @@ -353,6 +353,24 @@ Background@SUPPORT_POWER_TOOLTIP_FACTIONSUFFIX: Font: TinyBold VAlign: Top +Background@ARMY_TOOLTIP: + Logic: ArmyTooltipLogic + Background: panel-black + Width: 200 + Height: 65 + Children: + Label@NAME: + X: 5 + Y: 1 + Height: 23 + Font: Bold + Label@DESC: + X: 5 + Y: 20 + Height: 5 + Font: TinyBold + VAlign: Top + Background@SPAWN_TOOLTIP: Logic: SpawnSelectorTooltipLogic Background: panel-black diff --git a/mods/common/chrome/ingame-observer.yaml b/mods/common/chrome/ingame-observer.yaml index fc7a4ed821..325593b2bf 100644 --- a/mods/common/chrome/ingame-observer.yaml +++ b/mods/common/chrome/ingame-observer.yaml @@ -167,6 +167,7 @@ Container@OBSERVER_WIDGETS: StatisticsProductionKey: StatisticsProduction StatisticsSupportPowersKey: StatisticsSupportPowers StatisticsCombatKey: StatisticsCombat + StatisticsArmyKey: StatisticsArmy StatisticsGraphKey: StatisticsGraph StatisticsArmyGraphKey: StatisticsArmyGraph X: 5 @@ -434,6 +435,42 @@ Container@OBSERVER_WIDGETS: Font: Bold Text: Support Powers Shadow: True + Container@ARMY_HEADERS: + X: 0 + Y: 0 + Width: 400 + Height: PARENT_BOTTOM + Children: + ColorBlock@HEADER_COLOR: + X: 0 + Y: 0 + Color: 00000090 + Width: PARENT_RIGHT - 200 + Height: PARENT_BOTTOM + GradientColorBlock@HEADER_GRADIENT: + X: PARENT_RIGHT - 200 + Y: 0 + TopLeftColor: 00000090 + BottomLeftColor: 00000090 + Width: 200 + Height: PARENT_BOTTOM + Label@PLAYER_HEADER: + X: 40 + Y: 0 + Width: 120 + Height: PARENT_BOTTOM + Font: Bold + Text: Player + Align: Left + Shadow: True + Label@ARMY_HEADER: + X: 160 + Y: 0 + Width: 100 + Height: PARENT_BOTTOM + Font: Bold + Text: Army + Shadow: True Container@COMBAT_STATS_HEADERS: X: 0 Y: 0 @@ -788,6 +825,43 @@ Container@OBSERVER_WIDGETS: Width: 0 Height: PARENT_BOTTOM TooltipContainer: TOOLTIP_CONTAINER + ScrollItem@ARMY_PLAYER_TEMPLATE: + X: 0 + Y: 0 + Width: 400 + Height: 25 + BaseName: scrollitem-nohover + Children: + ColorBlock@PLAYER_COLOR: + X: 0 + Y: 0 + Width: PARENT_RIGHT - 200 + Height: PARENT_BOTTOM + GradientColorBlock@PLAYER_GRADIENT: + X: PARENT_RIGHT - 200 + Y: 0 + Width: 200 + Height: PARENT_BOTTOM + Image@FLAG: + X: 5 + Y: 4 + Width: 35 + Height: PARENT_BOTTOM - 4 + ImageName: random + ImageCollection: flags + Label@PLAYER: + X: 35 + Y: 0 + Width: 120 + Height: PARENT_BOTTOM + Font: Bold + Shadow: True + ObserverArmyIcons@ARMY_ICONS: + X: 155 + Y: 0 + Width: 0 + Height: PARENT_BOTTOM + TooltipContainer: TOOLTIP_CONTAINER ScrollItem@COMBAT_PLAYER_TEMPLATE: X: 0 Y: 0 diff --git a/mods/common/chrome/tooltips.yaml b/mods/common/chrome/tooltips.yaml index f54c32ebe7..b9e016851f 100644 --- a/mods/common/chrome/tooltips.yaml +++ b/mods/common/chrome/tooltips.yaml @@ -319,3 +319,21 @@ Background@SUPPORT_POWER_TOOLTIP: Y: 24 Font: TinyBold VAlign: Top + +Background@ARMY_TOOLTIP: + Logic: ArmyTooltipLogic + Background: dialog4 + Width: 200 + Height: 65 + Children: + Label@NAME: + X: 7 + Y: 3 + Height: 23 + Font: Bold + Label@DESC: + X: 7 + Y: 27 + Height: 2 + Font: TinyBold + VAlign: Top diff --git a/mods/common/hotkeys/observer.yaml b/mods/common/hotkeys/observer.yaml index 604aa83437..6754641ad3 100644 --- a/mods/common/hotkeys/observer.yaml +++ b/mods/common/hotkeys/observer.yaml @@ -46,10 +46,14 @@ StatisticsCombat: F6 Description: Combat statistics Types: Observer, Replay, Spectator -StatisticsGraph: F7 +StatisticsArmy: F7 + Description: Army statistics + Types: Observer, Replay, Spectator + +StatisticsGraph: Description: Statistics graph Types: Observer, Replay, Spectator -StatisticsArmyGraph: F8 +StatisticsArmyGraph: Description: Army value graph Types: Observer, Replay, Spectator diff --git a/mods/d2k/chrome/tooltips.yaml b/mods/d2k/chrome/tooltips.yaml index 0ab2f896d4..ba43ecbc30 100644 --- a/mods/d2k/chrome/tooltips.yaml +++ b/mods/d2k/chrome/tooltips.yaml @@ -321,3 +321,21 @@ Background@SUPPORT_POWER_TOOLTIP: Y: 30 Font: TinyBold VAlign: Top + +Background@ARMY_TOOLTIP: + Logic: ArmyTooltipLogic + Background: dialog3 + Width: 200 + Height: 65 + Children: + Label@NAME: + X: 7 + Y: 3 + Height: 23 + Font: Bold + Label@DESC: + X: 7 + Y: 23 + Height: 3 + Font: TinyBold + VAlign: Top diff --git a/mods/d2k/rules/starport.yaml b/mods/d2k/rules/starport.yaml index fc3d6260c2..6ea4efa3a8 100644 --- a/mods/d2k/rules/starport.yaml +++ b/mods/d2k/rules/starport.yaml @@ -8,6 +8,8 @@ mcv.starport: -MapEditorData: RenderSprites: Image: mcv + UpdatesPlayerStatistics: + OverrideActor: mcv harvester.starport: Inherits: harvester @@ -19,6 +21,8 @@ harvester.starport: -MapEditorData: RenderSprites: Image: harvester + UpdatesPlayerStatistics: + OverrideActor: harvester trike.starport: Inherits: trike @@ -30,6 +34,8 @@ trike.starport: -MapEditorData: RenderSprites: Image: trike + UpdatesPlayerStatistics: + OverrideActor: trike quad.starport: Inherits: quad @@ -41,6 +47,8 @@ quad.starport: -MapEditorData: RenderSprites: Image: quad + UpdatesPlayerStatistics: + OverrideActor: quad siege_tank.starport: Inherits: siege_tank @@ -52,6 +60,8 @@ siege_tank.starport: -MapEditorData: RenderSprites: Image: siege_tank + UpdatesPlayerStatistics: + OverrideActor: siege_tank missile_tank.starport: Inherits: missile_tank @@ -63,6 +73,8 @@ missile_tank.starport: -MapEditorData: RenderSprites: Image: missile_tank + UpdatesPlayerStatistics: + OverrideActor: missile_tank combat_tank_a.starport: Inherits: combat_tank_a @@ -74,6 +86,8 @@ combat_tank_a.starport: -MapEditorData: RenderSprites: Image: combat_tank_a + UpdatesPlayerStatistics: + OverrideActor: combat_tank_a combat_tank_h.starport: Inherits: combat_tank_h @@ -85,6 +99,8 @@ combat_tank_h.starport: -MapEditorData: RenderSprites: Image: combat_tank_h + UpdatesPlayerStatistics: + OverrideActor: combat_tank_h combat_tank_o.starport: Inherits: combat_tank_o @@ -96,6 +112,8 @@ combat_tank_o.starport: -MapEditorData: RenderSprites: Image: combat_tank_o + UpdatesPlayerStatistics: + OverrideActor: combat_tank_o carryall.starport: Inherits: carryall @@ -105,3 +123,5 @@ carryall.starport: Valued: Cost: 1500 -MapEditorData: + UpdatesPlayerStatistics: + OverrideActor: carryall diff --git a/mods/ra/chrome/ingame-observer.yaml b/mods/ra/chrome/ingame-observer.yaml index 847f308164..1c83d8c29c 100644 --- a/mods/ra/chrome/ingame-observer.yaml +++ b/mods/ra/chrome/ingame-observer.yaml @@ -203,6 +203,7 @@ Container@OBSERVER_WIDGETS: StatisticsProductionKey: StatisticsProduction StatisticsSupportPowersKey: StatisticsSupportPowers StatisticsCombatKey: StatisticsCombat + StatisticsArmyKey: StatisticsArmy StatisticsGraphKey: StatisticsGraph StatisticsArmyGraphKey: StatisticsArmyGraph StatsDropDownPanelTemplate: SPECTATOR_LABEL_DROPDOWN_TEMPLATE @@ -481,6 +482,42 @@ Container@OBSERVER_WIDGETS: Font: Bold Text: Support Powers Shadow: True + Container@ARMY_HEADERS: + X: 0 + Y: 0 + Width: 400 + Height: PARENT_BOTTOM + Children: + ColorBlock@HEADER_COLOR: + X: 0 + Y: 0 + Color: 00000090 + Width: PARENT_RIGHT - 200 + Height: PARENT_BOTTOM + GradientColorBlock@HEADER_GRADIENT: + X: PARENT_RIGHT - 200 + Y: 0 + TopLeftColor: 00000090 + BottomLeftColor: 00000090 + Width: 200 + Height: PARENT_BOTTOM + Label@PLAYER_HEADER: + X: 40 + Y: 0 + Width: 120 + Height: PARENT_BOTTOM + Font: Bold + Text: Player + Align: Left + Shadow: True + Label@ARMY_HEADER: + X: 160 + Y: 0 + Width: 100 + Height: PARENT_BOTTOM + Font: Bold + Text: Army + Shadow: True Container@COMBAT_STATS_HEADERS: X: 0 Y: 0 @@ -852,6 +889,43 @@ Container@OBSERVER_WIDGETS: Width: 0 Height: PARENT_BOTTOM TooltipContainer: TOOLTIP_CONTAINER + ScrollItem@ARMY_PLAYER_TEMPLATE: + X: 0 + Y: 0 + Width: 400 + Height: 24 + BaseName: scrollitem-nohover + Children: + ColorBlock@PLAYER_COLOR: + X: 0 + Y: 0 + Width: PARENT_RIGHT - 200 + Height: PARENT_BOTTOM + GradientColorBlock@PLAYER_GRADIENT: + X: PARENT_RIGHT - 200 + Y: 0 + Width: 200 + Height: PARENT_BOTTOM + Image@FLAG: + X: 5 + Y: 4 + Width: 35 + Height: PARENT_BOTTOM - 4 + ImageName: random + ImageCollection: flags + Label@PLAYER: + X: 40 + Y: 0 + Width: 120 + Height: PARENT_BOTTOM + Font: Bold + Shadow: True + ObserverArmyIcons@ARMY_ICONS: + X: 160 + Y: 0 + Width: 0 + Height: PARENT_BOTTOM + TooltipContainer: TOOLTIP_CONTAINER ScrollItem@COMBAT_PLAYER_TEMPLATE: X: 0 Y: 0 diff --git a/mods/ra/rules/infantry.yaml b/mods/ra/rules/infantry.yaml index 77a59eaacd..3ce6a30488 100644 --- a/mods/ra/rules/infantry.yaml +++ b/mods/ra/rules/infantry.yaml @@ -106,6 +106,8 @@ E1R1: ProducibleWithLevel: Prerequisites: techlevel.infonly InitialLevels: 1 + UpdatesPlayerStatistics: + OverrideActor: e1 -Buildable: E2: @@ -199,6 +201,8 @@ E3R1: ProducibleWithLevel: Prerequisites: techlevel.infonly InitialLevels: 1 + UpdatesPlayerStatistics: + OverrideActor: e3 -Buildable: E4: diff --git a/mods/ts/chrome/ingame-observer.yaml b/mods/ts/chrome/ingame-observer.yaml index b05b7e5b79..ef7ebf4b74 100644 --- a/mods/ts/chrome/ingame-observer.yaml +++ b/mods/ts/chrome/ingame-observer.yaml @@ -167,6 +167,7 @@ Container@OBSERVER_WIDGETS: StatisticsProductionKey: StatisticsProduction StatisticsSupportPowersKey: StatisticsSupportPowers StatisticsCombatKey: StatisticsCombat + StatisticsArmyKey: StatisticsArmy StatisticsGraphKey: StatisticsGraph StatisticsArmyGraphKey: StatisticsArmyGraph X: 5 @@ -434,6 +435,42 @@ Container@OBSERVER_WIDGETS: Font: Bold Text: Support Powers Shadow: True + Container@ARMY_HEADERS: + X: 0 + Y: 0 + Width: 400 + Height: PARENT_BOTTOM + Children: + ColorBlock@HEADER_COLOR: + X: 0 + Y: 0 + Color: 00000090 + Width: PARENT_RIGHT - 200 + Height: PARENT_BOTTOM + GradientColorBlock@HEADER_GRADIENT: + X: PARENT_RIGHT - 200 + Y: 0 + TopLeftColor: 00000090 + BottomLeftColor: 00000090 + Width: 200 + Height: PARENT_BOTTOM + Label@PLAYER_HEADER: + X: 40 + Y: 0 + Width: 120 + Height: PARENT_BOTTOM + Font: Bold + Text: Player + Align: Left + Shadow: True + Label@ARMY_HEADER: + X: 160 + Y: 0 + Width: 100 + Height: PARENT_BOTTOM + Font: Bold + Text: Army + Shadow: True Container@COMBAT_STATS_HEADERS: X: 0 Y: 0 @@ -798,6 +835,43 @@ Container@OBSERVER_WIDGETS: Height: PARENT_BOTTOM ClockPalette: iconclock TooltipContainer: TOOLTIP_CONTAINER + ScrollItem@ARMY_PLAYER_TEMPLATE: + X: 0 + Y: 0 + Width: 400 + Height: 24 + BaseName: scrollitem-nohover + Children: + ColorBlock@PLAYER_COLOR: + X: 0 + Y: 0 + Width: PARENT_RIGHT - 200 + Height: PARENT_BOTTOM + GradientColorBlock@PLAYER_GRADIENT: + X: PARENT_RIGHT - 200 + Y: 0 + Width: 200 + Height: PARENT_BOTTOM + Image@FLAG: + X: 5 + Y: 4 + Width: 35 + Height: PARENT_BOTTOM - 4 + ImageName: random + ImageCollection: flags + Label@PLAYER: + X: 40 + Y: 0 + Width: 120 + Height: PARENT_BOTTOM + Font: Bold + Shadow: True + ObserverArmyIcons@ARMY_ICONS: + X: 160 + Y: 0 + Width: 0 + Height: PARENT_BOTTOM + TooltipContainer: TOOLTIP_CONTAINER ScrollItem@COMBAT_PLAYER_TEMPLATE: X: 0 Y: 0