Files
OpenRA/OpenRA.Mods.Common/Widgets/ProductionPaletteWidget.cs
penev92 2e04fb5ddd Rename Faction trait members
Rename Faction.Race to Faction.InternalName
2015-07-14 18:50:39 +03:00

422 lines
12 KiB
C#

#region Copyright & License Information
/*
* Copyright 2007-2015 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. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Orders;
using OpenRA.Mods.Common.Traits;
using OpenRA.Network;
using OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets
{
public class ProductionIcon
{
public ActorInfo Actor;
public string Name;
public Hotkey Hotkey;
public Sprite Sprite;
public PaletteReference Palette;
public float2 Pos;
public List<ProductionItem> Queued;
}
public class ProductionPaletteWidget : Widget
{
public enum ReadyTextStyleOptions { Solid, AlternatingColor, Blinking }
public readonly ReadyTextStyleOptions ReadyTextStyle = ReadyTextStyleOptions.AlternatingColor;
public readonly Color ReadyTextAltColor = Color.Gold;
public readonly int Columns = 3;
public readonly int2 IconSize = new int2(64, 48);
public readonly int2 IconMargin = int2.Zero;
public readonly int2 IconSpriteOffset = int2.Zero;
public readonly string TabClick = null;
public readonly string DisabledTabClick = null;
public readonly string TooltipContainer;
public readonly string TooltipTemplate = "PRODUCTION_TOOLTIP";
[Translate] public readonly string ReadyText = "";
[Translate] public readonly string HoldText = "";
public int DisplayedIconCount { get; private set; }
public int TotalIconCount { get; private set; }
public event Action<int, int> OnIconCountChanged = (a, b) => { };
public ProductionIcon TooltipIcon { get; private set; }
public readonly World World;
readonly OrderManager orderManager;
public int MinimumRows = 4;
public int MaximumRows = int.MaxValue;
public int IconRowOffset = 0;
public int MaxIconRowOffset = int.MaxValue;
Lazy<TooltipContainerWidget> tooltipContainer;
ProductionQueue currentQueue;
public ProductionQueue CurrentQueue
{
get { return currentQueue; }
set { currentQueue = value; RefreshIcons(); }
}
public override Rectangle EventBounds { get { return eventBounds; } }
Dictionary<Rectangle, ProductionIcon> icons = new Dictionary<Rectangle, ProductionIcon>();
Animation cantBuild, clock;
Rectangle eventBounds = Rectangle.Empty;
readonly WorldRenderer worldRenderer;
SpriteFont overlayFont;
float2 holdOffset, readyOffset, timeOffset, queuedOffset;
[ObjectCreator.UseCtor]
public ProductionPaletteWidget(OrderManager orderManager, World world, WorldRenderer worldRenderer)
{
this.orderManager = orderManager;
this.World = world;
this.worldRenderer = worldRenderer;
tooltipContainer = Exts.Lazy(() =>
Ui.Root.Get<TooltipContainerWidget>(TooltipContainer));
cantBuild = new Animation(world, "clock");
cantBuild.PlayFetchIndex("idle", () => 0);
clock = new Animation(world, "clock");
}
public void ScrollDown()
{
if (CanScrollDown)
IconRowOffset++;
}
public bool CanScrollDown
{
get
{
var totalRows = (TotalIconCount + Columns - 1) / Columns;
return IconRowOffset < totalRows - MaxIconRowOffset;
}
}
public void ScrollUp()
{
if (CanScrollUp)
IconRowOffset--;
}
public bool CanScrollUp
{
get { return IconRowOffset > 0; }
}
public void ScrollToTop()
{
IconRowOffset = 0;
}
public IEnumerable<ActorInfo> AllBuildables
{
get
{
if (CurrentQueue == null)
return Enumerable.Empty<ActorInfo>();
return CurrentQueue.AllItems().OrderBy(a => a.Traits.Get<BuildableInfo>().BuildPaletteOrder);
}
}
public override void Tick()
{
TotalIconCount = AllBuildables.Count();
if (CurrentQueue != null && !CurrentQueue.Actor.IsInWorld)
CurrentQueue = null;
if (CurrentQueue != null)
RefreshIcons();
}
public override void MouseEntered()
{
if (TooltipContainer != null)
tooltipContainer.Value.SetTooltip(TooltipTemplate,
new WidgetArgs() { { "palette", this } });
}
public override void MouseExited()
{
if (TooltipContainer != null)
tooltipContainer.Value.RemoveTooltip();
}
public override bool HandleMouseInput(MouseInput mi)
{
var icon = icons.Where(i => i.Key.Contains(mi.Location))
.Select(i => i.Value).FirstOrDefault();
if (mi.Event == MouseInputEvent.Move)
TooltipIcon = icon;
if (icon == null)
return false;
// Only support left and right clicks
if (mi.Button != MouseButton.Left && mi.Button != MouseButton.Right)
return false;
// Eat mouse-up events
if (mi.Event != MouseInputEvent.Down)
return true;
return HandleEvent(icon, mi.Button == MouseButton.Left, mi.Modifiers.HasModifier(Modifiers.Shift));
}
protected bool PickUpCompletedBuildingIcon(ProductionIcon icon, ProductionItem item)
{
var actor = World.Map.Rules.Actors[icon.Name];
if (item != null && item.Done && actor.Traits.Contains<BuildingInfo>())
{
World.OrderGenerator = new PlaceBuildingOrderGenerator(CurrentQueue, icon.Name);
return true;
}
return false;
}
public void PickUpCompletedBuilding()
{
foreach (var icon in icons.Values)
{
var item = icon.Queued.FirstOrDefault();
if (PickUpCompletedBuildingIcon(icon, item))
break;
}
}
bool HandleLeftClick(ProductionItem item, ProductionIcon icon, bool handleMultiple)
{
if (PickUpCompletedBuildingIcon(icon, item))
{
Sound.Play(TabClick);
return true;
}
if (item != null && item.Paused)
{
// Resume a paused item
Sound.Play(TabClick);
World.IssueOrder(Order.PauseProduction(CurrentQueue.Actor, icon.Name, false));
return true;
}
if (CurrentQueue.BuildableItems().Any(a => a.Name == icon.Name))
{
// Queue a new item
Sound.Play(TabClick);
Sound.PlayNotification(World.Map.Rules, World.LocalPlayer, "Speech", CurrentQueue.Info.QueuedAudio, World.LocalPlayer.Country.InternalName);
World.IssueOrder(Order.StartProduction(CurrentQueue.Actor, icon.Name,
handleMultiple ? 5 : 1));
return true;
}
return false;
}
bool HandleRightClick(ProductionItem item, ProductionIcon icon, bool handleMultiple)
{
if (item == null)
return false;
Sound.Play(TabClick);
if (item.Paused || item.Done || item.TotalCost == item.RemainingCost)
{
// Instant cancel of things we have not started yet and things that are finished
Sound.PlayNotification(World.Map.Rules, World.LocalPlayer, "Speech", CurrentQueue.Info.CancelledAudio, World.LocalPlayer.Country.InternalName);
World.IssueOrder(Order.CancelProduction(CurrentQueue.Actor, icon.Name,
handleMultiple ? 5 : 1));
}
else
{
// Pause an existing item
Sound.PlayNotification(World.Map.Rules, World.LocalPlayer, "Speech", CurrentQueue.Info.OnHoldAudio, World.LocalPlayer.Country.InternalName);
World.IssueOrder(Order.PauseProduction(CurrentQueue.Actor, icon.Name, true));
}
return true;
}
bool HandleEvent(ProductionIcon icon, bool isLeftClick, bool handleMultiple)
{
var item = icon.Queued.FirstOrDefault();
var handled = isLeftClick ? HandleLeftClick(item, icon, handleMultiple)
: HandleRightClick(item, icon, handleMultiple);
if (!handled)
Sound.Play(DisabledTabClick);
return true;
}
public override bool HandleKeyPress(KeyInput e)
{
if (e.Event == KeyInputEvent.Up || CurrentQueue == null)
return false;
var hotkey = Hotkey.FromKeyInput(e);
var toBuild = icons.Values.FirstOrDefault(i => i.Hotkey == hotkey);
return toBuild != null ? HandleEvent(toBuild, true, false) : false;
}
public void RefreshIcons()
{
icons = new Dictionary<Rectangle, ProductionIcon>();
var producer = CurrentQueue != null ? CurrentQueue.MostLikelyProducer() : default(TraitPair<Production>);
if (CurrentQueue == null || producer.Trait == null)
{
if (DisplayedIconCount != 0)
{
OnIconCountChanged(DisplayedIconCount, 0);
DisplayedIconCount = 0;
}
return;
}
var oldIconCount = DisplayedIconCount;
DisplayedIconCount = 0;
var ks = Game.Settings.Keys;
var rb = RenderBounds;
var race = producer.Trait.Race;
foreach (var item in AllBuildables.Skip(IconRowOffset * Columns).Take(MaxIconRowOffset * Columns))
{
var x = DisplayedIconCount % Columns;
var y = DisplayedIconCount / Columns;
var rect = new Rectangle(rb.X + x * (IconSize.X + IconMargin.X), rb.Y + y * (IconSize.Y + IconMargin.Y), IconSize.X, IconSize.Y);
var rsi = item.Traits.Get<RenderSpritesInfo>();
var icon = new Animation(World, rsi.GetImage(item, World.Map.SequenceProvider, race));
icon.Play(item.Traits.Get<TooltipInfo>().Icon);
var bi = item.Traits.Get<BuildableInfo>();
var pi = new ProductionIcon()
{
Actor = item,
Name = item.Name,
Hotkey = ks.GetProductionHotkey(DisplayedIconCount),
Sprite = icon.Image,
Palette = worldRenderer.Palette(bi.IconPalette),
Pos = new float2(rect.Location),
Queued = CurrentQueue.AllQueued().Where(a => a.Item == item.Name).ToList()
};
icons.Add(rect, pi);
DisplayedIconCount++;
}
eventBounds = icons.Any() ? icons.Keys.Aggregate(Rectangle.Union) : Rectangle.Empty;
if (oldIconCount != DisplayedIconCount)
OnIconCountChanged(oldIconCount, DisplayedIconCount);
}
public override void Draw()
{
var iconOffset = 0.5f * IconSize.ToFloat2() + IconSpriteOffset;
overlayFont = Game.Renderer.Fonts["TinyBold"];
timeOffset = iconOffset - overlayFont.Measure(WidgetUtils.FormatTime(0)) / 2;
queuedOffset = new float2(4, 2);
holdOffset = iconOffset - overlayFont.Measure(HoldText) / 2;
readyOffset = iconOffset - overlayFont.Measure(ReadyText) / 2;
if (CurrentQueue == null)
return;
var buildableItems = CurrentQueue.BuildableItems();
var pio = currentQueue.Actor.Owner.PlayerActor.TraitsImplementing<IProductionIconOverlay>().FirstOrDefault();
var pioOffset = pio != null ? pio.Offset(IconSize) : new float2(0, 0);
// Icons
foreach (var icon in icons.Values)
{
WidgetUtils.DrawSHPCentered(icon.Sprite, icon.Pos + iconOffset, icon.Palette);
// Draw the ProductionIconOverlay's sprite
if (pio != null && pio.IsOverlayActive(icon.Actor))
WidgetUtils.DrawSHPCentered(pio.Sprite(), icon.Pos + iconOffset + pioOffset, worldRenderer.Palette(pio.Palette()), pio.Scale());
// Build progress
if (icon.Queued.Count > 0)
{
var first = icon.Queued[0];
clock.PlayFetchIndex("idle",
() => (first.TotalTime - first.RemainingTime)
* (clock.CurrentSequence.Length - 1) / first.TotalTime);
clock.Tick();
WidgetUtils.DrawSHPCentered(clock.Image, icon.Pos + iconOffset, icon.Palette);
}
else if (!buildableItems.Any(a => a.Name == icon.Name))
WidgetUtils.DrawSHPCentered(cantBuild.Image, icon.Pos + iconOffset, icon.Palette);
}
// Overlays
foreach (var icon in icons.Values)
{
var total = icon.Queued.Count;
if (total > 0)
{
var first = icon.Queued[0];
var waiting = first != CurrentQueue.CurrentItem() && !first.Done;
if (first.Done)
{
if (ReadyTextStyle == ReadyTextStyleOptions.Solid || orderManager.LocalFrameNumber / 9 % 2 == 0)
overlayFont.DrawTextWithContrast(ReadyText, icon.Pos + readyOffset, Color.White, Color.Black, 1);
else if (ReadyTextStyle == ReadyTextStyleOptions.AlternatingColor)
overlayFont.DrawTextWithContrast(ReadyText, icon.Pos + readyOffset, ReadyTextAltColor, Color.Black, 1);
}
else if (first.Paused)
overlayFont.DrawTextWithContrast(HoldText,
icon.Pos + holdOffset,
Color.White, Color.Black, 1);
else if (!waiting)
overlayFont.DrawTextWithContrast(WidgetUtils.FormatTime(first.RemainingTimeActual),
icon.Pos + timeOffset,
Color.White, Color.Black, 1);
if (total > 1 || waiting)
overlayFont.DrawTextWithContrast(total.ToString(),
icon.Pos + queuedOffset,
Color.White, Color.Black, 1);
}
}
}
public override string GetCursor(int2 pos)
{
var icon = icons.Where(i => i.Key.Contains(pos))
.Select(i => i.Value).FirstOrDefault();
return icon != null ? base.GetCursor(pos) : null;
}
}
}