1 #region Copyright & License Information 2 /* 3 * Copyright 2007-2020 The OpenRA Developers (see AUTHORS) 4 * This file is part of OpenRA, which is free software. It is made 5 * available to you under the terms of the GNU General Public License 6 * as published by the Free Software Foundation, either version 3 of 7 * the License, or (at your option) any later version. For more 8 * information, see COPYING. 9 */ 10 #endregion 11 12 using System; 13 using System.Linq; 14 using OpenRA.Mods.Common.Traits; 15 using OpenRA.Network; 16 using OpenRA.Widgets; 17 18 namespace OpenRA.Mods.Common.Widgets.Logic 19 { 20 public class ClassicProductionLogic : ChromeLogic 21 { 22 readonly ProductionPaletteWidget palette; 23 readonly World world; 24 SetupProductionGroupButton(OrderManager orderManager, ProductionTypeButtonWidget button)25 void SetupProductionGroupButton(OrderManager orderManager, ProductionTypeButtonWidget button) 26 { 27 if (button == null) 28 return; 29 30 // Classic production queues are initialized at game start, and then never change. 31 var queues = world.LocalPlayer.PlayerActor.TraitsImplementing<ProductionQueue>() 32 .Where(q => (q.Info.Group ?? q.Info.Type) == button.ProductionGroup) 33 .ToArray(); 34 35 Action<bool> selectTab = reverse => 36 { 37 palette.CurrentQueue = queues.FirstOrDefault(q => q.Enabled); 38 39 // When a tab is selected, scroll to the top because the current row position may be invalid for the new tab 40 palette.ScrollToTop(); 41 42 // Attempt to pick up a completed building (if there is one) so it can be placed 43 palette.PickUpCompletedBuilding(); 44 }; 45 46 button.IsDisabled = () => !queues.Any(q => q.BuildableItems().Any()); 47 button.OnMouseUp = mi => selectTab(mi.Modifiers.HasModifier(Modifiers.Shift)); 48 button.OnKeyPress = e => selectTab(e.Modifiers.HasModifier(Modifiers.Shift)); 49 button.OnClick = () => selectTab(false); 50 button.IsHighlighted = () => queues.Contains(palette.CurrentQueue); 51 52 var chromeName = button.ProductionGroup.ToLowerInvariant(); 53 var icon = button.Get<ImageWidget>("ICON"); 54 icon.GetImageName = () => button.IsDisabled() ? chromeName + "-disabled" : 55 queues.Any(q => q.AllQueued().Any(i => i.Done)) ? chromeName + "-alert" : chromeName; 56 } 57 58 [ObjectCreator.UseCtor] ClassicProductionLogic(Widget widget, OrderManager orderManager, World world)59 public ClassicProductionLogic(Widget widget, OrderManager orderManager, World world) 60 { 61 this.world = world; 62 palette = widget.Get<ProductionPaletteWidget>("PRODUCTION_PALETTE"); 63 64 var background = widget.GetOrNull("PALETTE_BACKGROUND"); 65 var foreground = widget.GetOrNull("PALETTE_FOREGROUND"); 66 if (background != null || foreground != null) 67 { 68 Widget backgroundTemplate = null; 69 Widget backgroundBottom = null; 70 Widget foregroundTemplate = null; 71 72 if (background != null) 73 { 74 backgroundTemplate = background.Get("ROW_TEMPLATE"); 75 backgroundBottom = background.GetOrNull("BOTTOM_CAP"); 76 } 77 78 if (foreground != null) 79 foregroundTemplate = foreground.Get("ROW_TEMPLATE"); 80 81 Action<int, int> updateBackground = (_, icons) => 82 { 83 var rows = Math.Max(palette.MinimumRows, (icons + palette.Columns - 1) / palette.Columns); 84 rows = Math.Min(rows, palette.MaximumRows); 85 86 if (background != null) 87 { 88 background.RemoveChildren(); 89 90 var rowHeight = backgroundTemplate.Bounds.Height; 91 for (var i = 0; i < rows; i++) 92 { 93 var row = backgroundTemplate.Clone(); 94 row.Bounds.Y = i * rowHeight; 95 background.AddChild(row); 96 } 97 98 if (backgroundBottom == null) 99 return; 100 101 backgroundBottom.Bounds.Y = rows * rowHeight; 102 background.AddChild(backgroundBottom); 103 } 104 105 if (foreground != null) 106 { 107 foreground.RemoveChildren(); 108 109 var rowHeight = foregroundTemplate.Bounds.Height; 110 for (var i = 0; i < rows; i++) 111 { 112 var row = foregroundTemplate.Clone(); 113 row.Bounds.Y = i * rowHeight; 114 foreground.AddChild(row); 115 } 116 } 117 }; 118 119 palette.OnIconCountChanged += updateBackground; 120 121 // Set the initial palette state 122 updateBackground(0, 0); 123 } 124 125 var typesContainer = widget.Get("PRODUCTION_TYPES"); 126 foreach (var i in typesContainer.Children) 127 SetupProductionGroupButton(orderManager, i as ProductionTypeButtonWidget); 128 129 var ticker = widget.Get<LogicTickerWidget>("PRODUCTION_TICKER"); 130 ticker.OnTick = () => 131 { 132 if (palette.CurrentQueue == null || palette.DisplayedIconCount == 0) 133 { 134 // Select the first active tab 135 foreach (var b in typesContainer.Children) 136 { 137 var button = b as ProductionTypeButtonWidget; 138 if (button == null || button.IsDisabled()) 139 continue; 140 141 button.OnClick(); 142 break; 143 } 144 } 145 }; 146 147 // Hook up scroll up and down buttons on the palette 148 var scrollDown = widget.GetOrNull<ButtonWidget>("SCROLL_DOWN_BUTTON"); 149 150 if (scrollDown != null) 151 { 152 scrollDown.OnClick = palette.ScrollDown; 153 scrollDown.IsVisible = () => palette.TotalIconCount > (palette.MaxIconRowOffset * palette.Columns); 154 scrollDown.IsDisabled = () => !palette.CanScrollDown; 155 } 156 157 var scrollUp = widget.GetOrNull<ButtonWidget>("SCROLL_UP_BUTTON"); 158 159 if (scrollUp != null) 160 { 161 scrollUp.OnClick = palette.ScrollUp; 162 scrollUp.IsVisible = () => palette.TotalIconCount > (palette.MaxIconRowOffset * palette.Columns); 163 scrollUp.IsDisabled = () => !palette.CanScrollUp; 164 } 165 166 SetMaximumVisibleRows(palette); 167 } 168 SetMaximumVisibleRows(ProductionPaletteWidget productionPalette)169 static void SetMaximumVisibleRows(ProductionPaletteWidget productionPalette) 170 { 171 var screenHeight = Game.Renderer.Resolution.Height; 172 173 // Get height of currently displayed icons 174 var containerWidget = Ui.Root.GetOrNull<ContainerWidget>("SIDEBAR_PRODUCTION"); 175 176 if (containerWidget == null) 177 return; 178 179 var sidebarProductionHeight = containerWidget.Bounds.Y; 180 181 // Check if icon heights exceed y resolution 182 var maxItemsHeight = screenHeight - sidebarProductionHeight; 183 184 var maxIconRowOffest = (maxItemsHeight / productionPalette.IconSize.Y) - 1; 185 productionPalette.MaxIconRowOffset = Math.Min(maxIconRowOffest, productionPalette.MaximumRows); 186 } 187 } 188 } 189