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