Rewrite production tabs (again): Avoid doing a pile of work every tick; support multiple queue types grouped under one category; Tab numbering is sticky.

This commit is contained in:
Paul Chote
2011-07-03 00:38:50 +12:00
parent b85fce3b41
commit c76811c5e1
5 changed files with 99 additions and 56 deletions

View File

@@ -22,6 +22,7 @@ namespace OpenRA.Mods.Cnc.Widgets.Logic
MenuType menu = MenuType.None; MenuType menu = MenuType.None;
Widget ingameRoot; Widget ingameRoot;
World world;
void AddChatLine(Color c, string from, string text) void AddChatLine(Color c, string from, string text)
{ {
@@ -32,6 +33,13 @@ namespace OpenRA.Mods.Cnc.Widgets.Logic
{ {
Game.AddChatLine -= AddChatLine; Game.AddChatLine -= AddChatLine;
Game.BeforeGameStart -= UnregisterEvents; Game.BeforeGameStart -= UnregisterEvents;
if (world.LocalPlayer != null)
{
var queueTabs = ingameRoot.GetWidget<ProductionTabsWidget>("PRODUCTION_TABS");
world.ActorAdded += queueTabs.ActorChanged;
world.ActorRemoved += queueTabs.ActorChanged;
}
} }
ProductionQueue QueueForType(World world, string type) ProductionQueue QueueForType(World world, string type)
@@ -45,6 +53,7 @@ namespace OpenRA.Mods.Cnc.Widgets.Logic
public CncIngameChromeLogic([ObjectCreator.Param] Widget widget, public CncIngameChromeLogic([ObjectCreator.Param] Widget widget,
[ObjectCreator.Param] World world ) [ObjectCreator.Param] World world )
{ {
this.world = world;
world.WorldActor.Trait<CncMenuPaletteEffect>() world.WorldActor.Trait<CncMenuPaletteEffect>()
.Fade(CncMenuPaletteEffect.EffectType.None); .Fade(CncMenuPaletteEffect.EffectType.None);
@@ -65,33 +74,31 @@ namespace OpenRA.Mods.Cnc.Widgets.Logic
var buildPalette = playerWidgets.GetWidget<ProductionPaletteWidget>("PRODUCTION_PALETTE"); var buildPalette = playerWidgets.GetWidget<ProductionPaletteWidget>("PRODUCTION_PALETTE");
var queueTabs = playerWidgets.GetWidget<ProductionTabsWidget>("PRODUCTION_TABS"); var queueTabs = playerWidgets.GetWidget<ProductionTabsWidget>("PRODUCTION_TABS");
world.ActorAdded += queueTabs.ActorChanged;
world.ActorRemoved += queueTabs.ActorChanged;
var queueTypes = sidebarRoot.GetWidget("PRODUCTION_TYPES"); var queueTypes = sidebarRoot.GetWidget("PRODUCTION_TYPES");
var buildingTab = queueTypes.GetWidget<ButtonWidget>("BUILDING"); var buildingTab = queueTypes.GetWidget<ButtonWidget>("BUILDING");
buildingTab.OnClick = () => queueTabs.QueueType = "Building"; buildingTab.OnClick = () => queueTabs.QueueType = "Building";
buildingTab.IsDisabled = () => !queueTabs.QueueCounts.ContainsKey("Building") buildingTab.IsDisabled = () => queueTabs.Groups["Building"].Tabs.Count == 0;
|| queueTabs.QueueCounts["Building"] == 0;
var defenseTab = queueTypes.GetWidget<ButtonWidget>("DEFENSE"); var defenseTab = queueTypes.GetWidget<ButtonWidget>("DEFENSE");
defenseTab.OnClick = () => queueTabs.QueueType = "Defense"; defenseTab.OnClick = () => queueTabs.QueueType = "Defense";
defenseTab.IsDisabled = () => !queueTabs.QueueCounts.ContainsKey("Defense") defenseTab.IsDisabled = () => queueTabs.Groups["Defense"].Tabs.Count == 0;
|| queueTabs.QueueCounts["Defense"] == 0;
var infantryTab = queueTypes.GetWidget<ButtonWidget>("INFANTRY"); var infantryTab = queueTypes.GetWidget<ButtonWidget>("INFANTRY");
infantryTab.OnClick = () => queueTabs.QueueType = "Infantry"; infantryTab.OnClick = () => queueTabs.QueueType = "Infantry";
infantryTab.IsDisabled = () => !queueTabs.QueueCounts.ContainsKey("Infantry") infantryTab.IsDisabled = () => queueTabs.Groups["Infantry"].Tabs.Count == 0;
|| queueTabs.QueueCounts["Infantry"] == 0;
var vehicleTab = queueTypes.GetWidget<ButtonWidget>("VEHICLE"); var vehicleTab = queueTypes.GetWidget<ButtonWidget>("VEHICLE");
vehicleTab.OnClick = () => queueTabs.QueueType = "Vehicle"; vehicleTab.OnClick = () => queueTabs.QueueType = "Vehicle";
vehicleTab.IsDisabled = () => !queueTabs.QueueCounts.ContainsKey("Vehicle") vehicleTab.IsDisabled = () => queueTabs.Groups["Vehicle"].Tabs.Count == 0;
|| queueTabs.QueueCounts["Vehicle"] == 0;
var aircraftTab = queueTypes.GetWidget<ButtonWidget>("AIRCRAFT"); var aircraftTab = queueTypes.GetWidget<ButtonWidget>("AIRCRAFT");
aircraftTab.OnClick = () => queueTabs.QueueType = "Aircraft"; aircraftTab.OnClick = () => queueTabs.QueueType = "Aircraft";
aircraftTab.IsDisabled = () => !queueTabs.QueueCounts.ContainsKey("Aircraft") aircraftTab.IsDisabled = () => queueTabs.Groups["Aircraft"].Tabs.Count == 0;
|| queueTabs.QueueCounts["Aircraft"] == 0;
} }
ingameRoot.GetWidget<ButtonWidget>("OPTIONS_BUTTON").OnClick = () => ingameRoot.GetWidget<ButtonWidget>("OPTIONS_BUTTON").OnClick = () =>
{ {

View File

@@ -19,6 +19,44 @@ using System;
namespace OpenRA.Mods.Cnc.Widgets namespace OpenRA.Mods.Cnc.Widgets
{ {
public class ProductionTab
{
public string Name;
public ProductionQueue Queue;
}
public class ProductionTabGroup
{
public List<ProductionTab> Tabs = new List<ProductionTab>();
public string Group;
public int CumulativeCount;
public void Update(IEnumerable<ProductionQueue> allQueues)
{
var queues = allQueues.Where(q => q.Info.Group == Group).ToList();
List<ProductionTab> tabs = new List<ProductionTab>();
// Remove stale queues
foreach (var t in Tabs)
{
if (!queues.Contains(t.Queue))
continue;
tabs.Add(t);
queues.Remove(t.Queue);
}
// Add new queues
foreach (var queue in queues)
tabs.Add(new ProductionTab()
{
Name = (++CumulativeCount).ToString(),
Queue = queue
});
Tabs = tabs;
}
}
class ProductionTabsWidget : Widget class ProductionTabsWidget : Widget
{ {
string queueType; string queueType;
@@ -32,9 +70,8 @@ namespace OpenRA.Mods.Cnc.Widgets
{ {
queueType = value; queueType = value;
ListOffset = 0; ListOffset = 0;
ResetButtons();
Widget.RootWidget.GetWidget<ProductionPaletteWidget>(PaletteWidget) Widget.RootWidget.GetWidget<ProductionPaletteWidget>(PaletteWidget)
.CurrentQueue = tabs.Keys.FirstOrDefault(); .CurrentQueue = Groups[queueType].Tabs[0].Queue;
} }
} }
@@ -42,10 +79,7 @@ namespace OpenRA.Mods.Cnc.Widgets
public float ScrollVelocity = 4f; public float ScrollVelocity = 4f;
public int TabWidth = 30; public int TabWidth = 30;
public int ArrowWidth = 20; public int ArrowWidth = 20;
public Dictionary<string, ProductionTabGroup> Groups;
public ProductionQueue[] AllQueues;
public Dictionary<string, int> QueueCounts = new Dictionary<string, int>();
Dictionary<ProductionQueue, Rectangle> tabs = new Dictionary<ProductionQueue, Rectangle>();
int ContentWidth = 0; int ContentWidth = 0;
float ListOffset = 0; float ListOffset = 0;
@@ -53,19 +87,19 @@ namespace OpenRA.Mods.Cnc.Widgets
bool rightPressed = false; bool rightPressed = false;
Rectangle leftButtonRect; Rectangle leftButtonRect;
Rectangle rightButtonRect; Rectangle rightButtonRect;
readonly World world; readonly World world;
[ObjectCreator.UseCtor] [ObjectCreator.UseCtor]
public ProductionTabsWidget( [ObjectCreator.Param] World world ) public ProductionTabsWidget( [ObjectCreator.Param] World world )
{ {
this.world = world; this.world = world;
Groups = Rules.Info.Values.SelectMany(a => a.Traits.WithInterface<ProductionQueueInfo>())
.Select(q => q.Group).Distinct().ToDictionary(g => g, g => new ProductionTabGroup() { Group = g });
} }
public override void DrawInner() public override void DrawInner()
{ {
var rb = RenderBounds; var rb = RenderBounds;
leftButtonRect = new Rectangle(rb.X, rb.Y, ArrowWidth, rb.Height); leftButtonRect = new Rectangle(rb.X, rb.Y, ArrowWidth, rb.Height);
rightButtonRect = new Rectangle(rb.Right - ArrowWidth, rb.Y, ArrowWidth, rb.Height); rightButtonRect = new Rectangle(rb.Right - ArrowWidth, rb.Y, ArrowWidth, rb.Height);
@@ -83,21 +117,24 @@ namespace OpenRA.Mods.Cnc.Widgets
WidgetUtils.DrawRGBA(ChromeProvider.GetImage("scrollbar", rightPressed || rightDisabled ? "down_pressed" : "down_arrow"), WidgetUtils.DrawRGBA(ChromeProvider.GetImage("scrollbar", rightPressed || rightDisabled ? "down_pressed" : "down_arrow"),
new float2(rightButtonRect.Left + 2, rightButtonRect.Top + 2)); new float2(rightButtonRect.Left + 2, rightButtonRect.Top + 2));
if (QueueType == null)
return;
// Draw tab buttons
Game.Renderer.EnableScissor(leftButtonRect.Right, rb.Y + 1, rightButtonRect.Left - leftButtonRect.Right - 1, rb.Height); Game.Renderer.EnableScissor(leftButtonRect.Right, rb.Y + 1, rightButtonRect.Left - leftButtonRect.Right - 1, rb.Height);
var palette = Widget.RootWidget.GetWidget<ProductionPaletteWidget>(PaletteWidget); var palette = Widget.RootWidget.GetWidget<ProductionPaletteWidget>(PaletteWidget);
// TODO: Draw children buttons var origin = new int2(leftButtonRect.Right - 1 + (int)ListOffset, leftButtonRect.Y);
var i = 1; SpriteFont font = Game.Renderer.Fonts["TinyBold"];
foreach (var tab in tabs) ContentWidth = 0;
foreach (var tab in Groups[QueueType].Tabs)
{ {
ButtonWidget.DrawBackground("button", tab.Value, false, tab.Key == palette.CurrentQueue, tab.Value.Contains(Viewport.LastMousePos)); var rect = new Rectangle(origin.X + ContentWidth, origin.Y, TabWidth, rb.Height);
ButtonWidget.DrawBackground("button", rect, false, tab.Queue == palette.CurrentQueue, rect.Contains(Viewport.LastMousePos));
ContentWidth += TabWidth - 1;
SpriteFont font = Game.Renderer.Fonts["TinyBold"]; int2 textSize = font.Measure(tab.Name);
var text = i.ToString(); int2 position = new int2(rect.X + (rect.Width - textSize.X)/2, rect.Y + (rect.Height - textSize.Y)/2);
int2 textSize = font.Measure(text); font.DrawTextWithContrast(tab.Name, position, Color.White, Color.Black, 1);
int2 position = new int2(tab.Value.X + (tab.Value.Width - textSize.X)/2, tab.Value.Y + (tab.Value.Height - textSize.Y)/2);
font.DrawTextWithContrast(text, position, Color.White, Color.Black, 1);
i++;
} }
Game.Renderer.DisableScissor(); Game.Renderer.DisableScissor();
@@ -109,18 +146,16 @@ namespace OpenRA.Mods.Cnc.Widgets
ListOffset = Math.Min(0,Math.Max(Bounds.Width - rightButtonRect.Width - leftButtonRect.Width - ContentWidth, ListOffset)); ListOffset = Math.Min(0,Math.Max(Bounds.Width - rightButtonRect.Width - leftButtonRect.Width - ContentWidth, ListOffset));
} }
public void ResetButtons() // Is added to world.ActorAdded by the SidebarLogic handler
public void ActorChanged(Actor a)
{ {
tabs.Clear(); if (a.HasTrait<ProductionQueue>())
ContentWidth = 0;
var rb = RenderBounds;
var origin = new int2(leftButtonRect.Right - 1 + (int)ListOffset, leftButtonRect.Y);
foreach (var queue in AllQueues.Where(q => q.Info.Type == QueueType))
{ {
var rect = new Rectangle(origin.X + ContentWidth, origin.Y, TabWidth, rb.Height); var allQueues = world.ActorsWithTrait<ProductionQueue>()
tabs.Add(queue, rect); .Where(p => p.Actor.Owner == world.LocalPlayer && p.Actor.IsInWorld)
ContentWidth += TabWidth - 1; .Select(p => p.Trait).ToArray();
foreach (var g in Groups.Values)
g.Update(allQueues);
} }
} }
@@ -128,15 +163,6 @@ namespace OpenRA.Mods.Cnc.Widgets
{ {
if (leftPressed) Scroll(1); if (leftPressed) Scroll(1);
if (rightPressed) Scroll(-1); if (rightPressed) Scroll(-1);
AllQueues = world.ActorsWithTrait<ProductionQueue>()
.Where(p => p.Actor.Owner == world.LocalPlayer)
.Select(p => p.Trait).ToArray();
QueueCounts = AllQueues.Select(q => q.Info.Type).Distinct()
.ToDictionary(t => t, t => AllQueues.Count(q => q.Info.Type == t));
ResetButtons();
base.Tick(); base.Tick();
} }
@@ -145,7 +171,7 @@ namespace OpenRA.Mods.Cnc.Widgets
leftPressed = rightPressed = false; leftPressed = rightPressed = false;
return base.LoseFocus(mi); return base.LoseFocus(mi);
} }
public override bool HandleMouseInput(MouseInput mi) public override bool HandleMouseInput(MouseInput mi)
{ {
if (mi.Button == MouseButton.WheelDown) if (mi.Button == MouseButton.WheelDown)
@@ -175,16 +201,16 @@ namespace OpenRA.Mods.Cnc.Widgets
leftPressed = leftButtonRect.Contains(mi.Location.X, mi.Location.Y); leftPressed = leftButtonRect.Contains(mi.Location.X, mi.Location.Y);
rightPressed = rightButtonRect.Contains(mi.Location.X, mi.Location.Y); rightPressed = rightButtonRect.Contains(mi.Location.X, mi.Location.Y);
var queue = tabs.Where(a => a.Value.Contains(mi.Location)) // Check production tabs
.Select(a => a.Key).FirstOrDefault(); var offsetloc = mi.Location - new int2(leftButtonRect.Right - 1 + (int)ListOffset, leftButtonRect.Y);
if (offsetloc.X > 0 && offsetloc.X <= ContentWidth)
if (queue != null)
{ {
var palette = Widget.RootWidget.GetWidget<ProductionPaletteWidget>(PaletteWidget); var palette = Widget.RootWidget.GetWidget<ProductionPaletteWidget>(PaletteWidget);
palette.CurrentQueue = queue; palette.CurrentQueue = Groups[QueueType].Tabs[offsetloc.X/(TabWidth - 1)].Queue;
return true;
} }
return (leftPressed || rightPressed || queue != null); return (leftPressed || rightPressed);
} }
} }
} }

View File

@@ -19,6 +19,8 @@ namespace OpenRA.Mods.RA
public class ProductionQueueInfo : ITraitInfo public class ProductionQueueInfo : ITraitInfo
{ {
public readonly string Type = null; public readonly string Type = null;
public readonly string Group = null;
public float BuildSpeed = 0.4f; public float BuildSpeed = 0.4f;
public readonly int LowPowerSlowdown = 3; public readonly int LowPowerSlowdown = 3;

View File

@@ -25,6 +25,7 @@ FACT:
Facing: 108 Facing: 108
ProductionQueue@Building: ProductionQueue@Building:
Type: Building Type: Building
Group: Building
BuildSpeed: .4 BuildSpeed: .4
LowPowerSlowdown: 3 LowPowerSlowdown: 3
QueuedAudio: bldging1.aud QueuedAudio: bldging1.aud
@@ -33,6 +34,7 @@ FACT:
CancelledAudio: cancel1.aud CancelledAudio: cancel1.aud
ProductionQueue@Defense: ProductionQueue@Defense:
Type: Defense Type: Defense
Group: Defense
BuildSpeed: .4 BuildSpeed: .4
LowPowerSlowdown: 3 LowPowerSlowdown: 3
QueuedAudio: bldging1.aud QueuedAudio: bldging1.aud
@@ -165,6 +167,7 @@ PYLE:
Produces: Infantry Produces: Infantry
ProductionQueue: ProductionQueue:
Type: Infantry Type: Infantry
Group: Infantry
BuildSpeed: .4 BuildSpeed: .4
LowPowerSlowdown: 3 LowPowerSlowdown: 3
QueuedAudio: bldging1.aud QueuedAudio: bldging1.aud
@@ -204,6 +207,7 @@ HAND:
Produces: Infantry Produces: Infantry
ProductionQueue: ProductionQueue:
Type: Infantry Type: Infantry
Group: Infantry
BuildSpeed: .4 BuildSpeed: .4
LowPowerSlowdown: 3 LowPowerSlowdown: 3
QueuedAudio: bldging1.aud QueuedAudio: bldging1.aud
@@ -245,6 +249,7 @@ AFLD:
Produces: Vehicle Produces: Vehicle
ProductionQueue: ProductionQueue:
Type: Vehicle Type: Vehicle
Group: Vehicle
BuildSpeed: .4 BuildSpeed: .4
LowPowerSlowdown: 3 LowPowerSlowdown: 3
QueuedAudio: bldging1.aud QueuedAudio: bldging1.aud
@@ -287,6 +292,7 @@ WEAP:
Produces: Vehicle Produces: Vehicle
ProductionQueue: ProductionQueue:
Type: Vehicle Type: Vehicle
Group: Vehicle
BuildSpeed: .4 BuildSpeed: .4
LowPowerSlowdown: 3 LowPowerSlowdown: 3
QueuedAudio: bldging1.aud QueuedAudio: bldging1.aud
@@ -413,6 +419,7 @@ HPAD:
RallyPoint: RallyPoint:
ProductionQueue: ProductionQueue:
Type: Plane Type: Plane
Group: Aircraft
BuildSpeed: .4 BuildSpeed: .4
LowPowerSlowdown: 3 LowPowerSlowdown: 3
QueuedAudio: bldging1.aud QueuedAudio: bldging1.aud

View File

@@ -53,6 +53,7 @@ BIO:
Produces: Biolab Produces: Biolab
ProductionQueue: ProductionQueue:
Type: Biolab Type: Biolab
Group: Infantry
BuildSpeed: .4 BuildSpeed: .4
LowPowerSlowdown: 3 LowPowerSlowdown: 3
QueuedAudio: bldging1.aud QueuedAudio: bldging1.aud