Merge pull request #6632 from steelphase-forks/common-widgets

Mods.Common Widgets
This commit is contained in:
Paul Chote
2014-10-04 10:06:05 +13:00
47 changed files with 91 additions and 80 deletions

View File

@@ -12,7 +12,7 @@ using System;
using System.Drawing;
using OpenRA.Graphics;
namespace OpenRA.Mods.RA.Graphics
namespace OpenRA.Mods.Common.Graphics
{
public struct TextRenderable : IRenderable
{

View File

@@ -46,6 +46,8 @@
<Compile Include="Graphics\ContrailRenderable.cs" />
<Compile Include="Graphics\RangeCircleRenderable.cs" />
<Compile Include="ModChooserLoadScreen.cs" />
<Compile Include="PaletteFromFile.cs" />
<Compile Include="Traits\ProvidesRadar.cs" />
<Compile Include="ServerTraits\ColorValidator.cs" />
<Compile Include="ServerTraits\MasterServerPinger.cs" />
<Compile Include="ServerTraits\PlayerPinger.cs" />
@@ -69,8 +71,24 @@
<Compile Include="Graphics\TextRenderable.cs" />
<Compile Include="Graphics\VoxelActorPreview.cs" />
<Compile Include="Graphics\VoxelRenderable.cs" />
<Compile Include="Widgets\ConfirmationDialogs.cs" />
<Compile Include="Widgets\HueSliderWidget.cs" />
<Compile Include="Widgets\LabelWithTooltipWidget.cs" />
<Compile Include="Widgets\LogicKeyListenerWidget.cs" />
<Compile Include="Widgets\Logic\AssetBrowserLogic.cs" />
<Compile Include="Widgets\ColorMixerWidget.cs" />
<Compile Include="Widgets\LogicTickerWidget.cs" />
<Compile Include="Widgets\Logic\ButtonTooltipLogic.cs" />
<Compile Include="Widgets\Logic\ColorPickerLogic.cs" />
<Compile Include="Widgets\ColorPreviewManagerWidget.cs" />
<Compile Include="Widgets\Logic\DisconnectWatcherLogic.cs" />
<Compile Include="Widgets\Logic\Ingame\IngameRadarDisplayLogic.cs" />
<Compile Include="Widgets\Logic\Ingame\LoadIngamePlayerOrObserverUILogic.cs" />
<Compile Include="Widgets\Logic\ModBrowserLogic.cs" />
<Compile Include="Widgets\MenuButtonWidget.cs" />
<Compile Include="Widgets\RadarWidget.cs" />
<Compile Include="Widgets\ResourceBarWidget.cs" />
<Compile Include="World\RadarPings.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>

View File

@@ -0,0 +1,58 @@
#region Copyright & License Information
/*
* Copyright 2007-2014 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 OpenRA.FileSystem;
using OpenRA.Graphics;
using OpenRA.Traits;
namespace OpenRA.Mods.Common
{
class PaletteFromFileInfo : ITraitInfo
{
[Desc("internal palette name")]
public readonly string Name = null;
[Desc("If defined, load the palette only for this tileset.")]
public readonly string Tileset = null;
[Desc("filename to load")]
public readonly string Filename = null;
[Desc("Map listed indices to shadow. Ignores previous color.")]
public readonly int[] ShadowIndex = { };
public readonly bool AllowModifiers = true;
public object Create(ActorInitializer init) { return new PaletteFromFile(init.world, this); }
}
class PaletteFromFile : ILoadsPalettes
{
readonly World world;
readonly PaletteFromFileInfo info;
public PaletteFromFile(World world, PaletteFromFileInfo info)
{
this.world = world;
this.info = info;
}
public void LoadPalettes(WorldRenderer wr)
{
if (info.Tileset == null || info.Tileset.ToLowerInvariant() == world.Map.Tileset.ToLowerInvariant())
wr.AddPalette(info.Name, new ImmutablePalette(GlobalFileSystem.Open(info.Filename), info.ShadowIndex), info.AllowModifiers);
}
public string Filename
{
get { return info.Filename; }
}
public string Name
{
get { return info.Name; }
}
}
}

View File

@@ -0,0 +1,45 @@
#region Copyright & License Information
/*
* Copyright 2007-2014 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.Linq;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[Desc("This actor enables the radar minimap.")]
public class ProvidesRadarInfo : TraitInfo<ProvidesRadar> { }
public class ProvidesRadar : ITick
{
public bool IsActive { get; private set; }
public void Tick(Actor self) { IsActive = UpdateActive(self); }
static bool UpdateActive(Actor self)
{
// Check if powered
if (self.IsDisabled()) return false;
var isJammed = self.World.ActorsWithTrait<JamsRadar>().Any(a => a.Actor.Owner.Stances[self.Owner] != Stance.Ally
&& (self.Location - a.Actor.Location).Length <= a.Actor.Info.Traits.Get<JamsRadarInfo>().Range);
return !isJammed;
}
}
[Desc("When an actor with this trait is in range of an actor with ProvidesRadar, it will temporarily disable the radar minimap for the enemy player.")]
public class JamsRadarInfo : TraitInfo<JamsRadar>
{
[Desc("Range for jamming.")]
public readonly int Range = 0;
}
public class JamsRadar { }
}

View File

@@ -0,0 +1,224 @@
#region Copyright & License Information
/*
* Copyright 2007-2014 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.Drawing;
using System.Threading;
using OpenRA.Graphics;
using OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets
{
public class ColorMixerWidget : Widget
{
public float[] SRange = {0.0f, 1.0f};
public float[] VRange = {0.2f, 1.0f};
public event Action OnChange = () => {};
float H, S, V;
byte[] front, back;
Sprite mixerSprite;
bool isMoving;
bool update;
object syncWorker = new object();
Thread workerThread;
bool workerAlive;
public ColorMixerWidget() {}
public ColorMixerWidget(ColorMixerWidget other)
: base(other)
{
OnChange = other.OnChange;
H = other.H;
S = other.S;
V = other.V;
}
public override void Initialize(WidgetArgs args)
{
base.Initialize(args);
// Bitmap data is generated in a background thread and then flipped
front = new byte[4*256*256];
back = new byte[4*256*256];
var rect = new Rectangle((int)(255*SRange[0]), (int)(255*(1 - VRange[1])), (int)(255*(SRange[1] - SRange[0]))+1, (int)(255*(VRange[1] - VRange[0])) + 1);
var mixerSheet = new Sheet(new Size(256, 256), false);
mixerSheet.Texture.SetData(front, 256, 256);
mixerSprite = new Sprite(mixerSheet, rect, TextureChannel.Alpha);
}
void GenerateBitmap()
{
// Generating the selection bitmap is slow,
// so we do it in a background thread
lock (syncWorker)
{
update = true;
if (workerThread == null || !workerAlive)
{
workerThread = new Thread(GenerateBitmapWorker);
workerThread.Start();
}
}
}
void GenerateBitmapWorker()
{
lock (syncWorker)
workerAlive = true;
for (;;)
{
float hue;
lock (syncWorker)
{
if (!update)
{
workerAlive = false;
break;
}
update = false;
// Take a local copy of the hue to generate to avoid tearing
hue = H;
}
lock (back)
{
unsafe
{
// Generate palette in HSV
fixed (byte* cc = &back[0])
{
var c = (int*)cc;
for (var v = 0; v < 256; v++)
for (var s = 0; s < 256; s++)
*(c + (v * 256) + s) = HSLColor.FromHSV(hue, s / 255f, (255 - v) / 255f).RGB.ToArgb();
}
}
lock (front)
{
var swap = front;
front = back;
back = swap;
}
}
}
}
public override void Draw()
{
if (Monitor.TryEnter(front))
{
try
{
mixerSprite.sheet.Texture.SetData(front, 256, 256);
}
finally
{
Monitor.Exit(front);
}
}
Game.Renderer.RgbaSpriteRenderer.DrawSprite(mixerSprite, RenderOrigin, new float2(RenderBounds.Size));
var sprite = ChromeProvider.GetImage("lobby-bits", "colorpicker");
var pos = RenderOrigin + PxFromValue() - new int2(sprite.bounds.Width, sprite.bounds.Height) / 2;
WidgetUtils.FillEllipseWithColor(new Rectangle(pos.X + 1, pos.Y + 1, sprite.bounds.Width - 2, sprite.bounds.Height - 2), Color.RGB);
Game.Renderer.RgbaSpriteRenderer.DrawSprite(sprite, pos);
}
void SetValueFromPx(int2 xy)
{
var rb = RenderBounds;
var s = SRange[0] + xy.X*(SRange[1] - SRange[0])/rb.Width;
var v = SRange[1] - xy.Y*(VRange[1] - VRange[0])/rb.Height;
S = s.Clamp(SRange[0], SRange[1]);
V = v.Clamp(VRange[0], VRange[1]);
}
int2 PxFromValue()
{
var rb = RenderBounds;
var x = RenderBounds.Width*(S - SRange[0])/(SRange[1] - SRange[0]);
var y = RenderBounds.Height*(1 - (V - VRange[0])/(VRange[1] - VRange[0]));
return new int2((int)x.Clamp(0, rb.Width), (int)y.Clamp(0, rb.Height));
}
public override bool HandleMouseInput(MouseInput mi)
{
if (mi.Button != MouseButton.Left)
return false;
if (mi.Event == MouseInputEvent.Down && !TakeMouseFocus(mi))
return false;
if (!HasMouseFocus)
return false;
switch (mi.Event)
{
case MouseInputEvent.Up:
isMoving = false;
YieldMouseFocus(mi);
break;
case MouseInputEvent.Down:
isMoving = true;
SetValueFromPx(mi.Location - RenderOrigin);
OnChange();
break;
case MouseInputEvent.Move:
if (isMoving)
{
SetValueFromPx(mi.Location - RenderOrigin);
OnChange();
}
break;
}
return true;
}
public HSLColor Color { get { return HSLColor.FromHSV(H, S, V); } }
public void Set(float hue)
{
if (H != hue)
{
H = hue;
GenerateBitmap();
OnChange();
}
}
public void Set(HSLColor color)
{
float h,s,v;
color.ToHSV(out h, out s, out v);
if (H != h || S != s || V != v)
{
if (H != h)
{
H = h;
GenerateBitmap();
}
S = s.Clamp(SRange[0], SRange[1]);
V = v.Clamp(VRange[0], VRange[1]);
OnChange();
}
}
}
}

View File

@@ -0,0 +1,51 @@
#region Copyright & License Information
/*
* Copyright 2007-2014 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 OpenRA.Graphics;
using OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets
{
public class ColorPreviewManagerWidget : Widget
{
public readonly string PaletteName = "colorpicker";
public readonly int[] RemapIndices = ChromeMetrics.Get<int[]>("ColorPickerRemapIndices");
public readonly float Ramp = 0.05f;
public HSLColor Color;
HSLColor cachedColor;
WorldRenderer worldRenderer;
IPalette preview;
[ObjectCreator.UseCtor]
public ColorPreviewManagerWidget(WorldRenderer worldRenderer)
{
this.worldRenderer = worldRenderer;
}
public override void Initialize(WidgetArgs args)
{
base.Initialize(args);
preview = worldRenderer.Palette(PaletteName).Palette;
}
public override void Tick()
{
if (cachedColor == Color)
return;
cachedColor = Color;
var newPalette = new MutablePalette(preview);
newPalette.ApplyRemap(new PlayerColorRemap(RemapIndices, Color, Ramp));
worldRenderer.ReplacePalette(PaletteName, newPalette);
}
}
}

View File

@@ -0,0 +1,138 @@
#region Copyright & License Information
/*
* Copyright 2007-2014 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 OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets
{
public static class ConfirmationDialogs
{
public static void PromptConfirmAction(string title, string text, Action onConfirm, Action onCancel = null, string confirmText = null, string cancelText = null)
{
var prompt = Ui.OpenWindow("CONFIRM_PROMPT");
prompt.Get<LabelWidget>("PROMPT_TITLE").GetText = () => title;
prompt.Get<LabelWidget>("PROMPT_TEXT").GetText = () => text;
if (!string.IsNullOrEmpty(confirmText))
prompt.Get<ButtonWidget>("CONFIRM_BUTTON").GetText = () => confirmText;
if (!string.IsNullOrEmpty(cancelText))
prompt.Get<ButtonWidget>("CANCEL_BUTTON").GetText = () => cancelText;
prompt.Get<ButtonWidget>("CONFIRM_BUTTON").OnClick = () =>
{
Ui.CloseWindow();
onConfirm();
};
prompt.Get<ButtonWidget>("CANCEL_BUTTON").OnClick = () =>
{
Ui.CloseWindow();
if (onCancel != null)
onCancel();
};
}
public static void TextInputPrompt(
string title, string prompt, string initialText,
Action<string> onAccept, Action onCancel = null,
string acceptText = null, string cancelText = null,
Func<string, bool> inputValidator = null)
{
var panel = Ui.OpenWindow("TEXT_INPUT_PROMPT");
Func<bool> doValidate = null;
ButtonWidget acceptButton = null, cancelButton = null;
//
// Title
//
panel.Get<LabelWidget>("PROMPT_TITLE").GetText = () => title;
//
// Prompt
//
panel.Get<LabelWidget>("PROMPT_TEXT").GetText = () => prompt;
//
// Text input
//
var input = panel.Get<TextFieldWidget>("INPUT_TEXT");
var isValid = false;
input.Text = initialText;
input.IsValid = () => isValid;
input.OnEnterKey = () =>
{
if (acceptButton.IsDisabled())
return false;
acceptButton.OnClick();
return true;
};
input.OnEscKey = () =>
{
if (cancelButton.IsDisabled())
return false;
cancelButton.OnClick();
return true;
};
input.TakeKeyboardFocus();
input.CursorPosition = input.Text.Length;
input.OnTextEdited = () => doValidate();
//
// Buttons
//
acceptButton = panel.Get<ButtonWidget>("ACCEPT_BUTTON");
if (!string.IsNullOrEmpty(acceptText))
acceptButton.GetText = () => acceptText;
acceptButton.OnClick = () =>
{
if (!doValidate())
return;
Ui.CloseWindow();
onAccept(input.Text);
};
cancelButton = panel.Get<ButtonWidget>("CANCEL_BUTTON");
if (!string.IsNullOrEmpty(cancelText))
cancelButton.GetText = () => cancelText;
cancelButton.OnClick = () =>
{
Ui.CloseWindow();
if (onCancel != null)
onCancel();
};
//
// Validation
//
doValidate = () =>
{
if (inputValidator == null)
return true;
isValid = inputValidator(input.Text);
if (isValid)
{
acceptButton.Disabled = false;
return true;
}
acceptButton.Disabled = true;
return false;
};
doValidate();
}
}
}

View File

@@ -0,0 +1,61 @@
#region Copyright & License Information
/*
* Copyright 2007-2014 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.Drawing;
using System.Drawing.Imaging;
using OpenRA.Graphics;
using OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets
{
public class HueSliderWidget : SliderWidget
{
Sprite hueSprite;
public HueSliderWidget() { }
public HueSliderWidget(HueSliderWidget other) : base(other) { }
public override void Initialize(WidgetArgs args)
{
base.Initialize(args);
using (var hueBitmap = new Bitmap(256, 256))
{
var hueSheet = new Sheet(new Size(256, 256), false);
hueSprite = new Sprite(hueSheet, new Rectangle(0, 0, 256, 1), TextureChannel.Alpha);
var bitmapData = hueBitmap.LockBits(hueBitmap.Bounds(),
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
unsafe
{
var c = (int*)bitmapData.Scan0;
for (var h = 0; h < 256; h++)
*(c + h) = HSLColor.FromHSV(h / 255f, 1, 1).RGB.ToArgb();
}
hueBitmap.UnlockBits(bitmapData);
hueSheet.Texture.SetData(hueBitmap);
}
}
public override void Draw()
{
if (!IsVisible())
return;
var ro = RenderOrigin;
var rb = RenderBounds;
Game.Renderer.RgbaSpriteRenderer.DrawSprite(hueSprite, ro, new float2(rb.Size));
var sprite = ChromeProvider.GetImage("lobby-bits", "huepicker");
var pos = RenderOrigin + new int2(PxFromValue(Value).Clamp(0, rb.Width - 1) - sprite.bounds.Width / 2, (rb.Height - sprite.bounds.Height) / 2);
Game.Renderer.RgbaSpriteRenderer.DrawSprite(sprite, pos);
}
}
}

View File

@@ -0,0 +1,62 @@
#region Copyright & License Information
/*
* Copyright 2007-2014 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 OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets
{
public class LabelWithTooltipWidget : LabelWidget
{
public readonly string TooltipTemplate;
public readonly string TooltipContainer;
Lazy<TooltipContainerWidget> tooltipContainer;
public Func<string> GetTooltipText = () => "";
[ObjectCreator.UseCtor]
public LabelWithTooltipWidget(World world)
: base()
{
tooltipContainer = Exts.Lazy(() =>
Ui.Root.Get<TooltipContainerWidget>(TooltipContainer));
}
protected LabelWithTooltipWidget(LabelWithTooltipWidget other)
: base(other)
{
TooltipTemplate = other.TooltipTemplate;
TooltipContainer = other.TooltipContainer;
tooltipContainer = Exts.Lazy(() =>
Ui.Root.Get<TooltipContainerWidget>(TooltipContainer));
GetTooltipText = other.GetTooltipText;
}
public override Widget Clone() { return new LabelWithTooltipWidget(this); }
public override void MouseEntered()
{
if (TooltipContainer == null)
return;
tooltipContainer.Value.SetTooltip(TooltipTemplate, new WidgetArgs() {{ "getText", GetTooltipText }});
}
public override void MouseExited()
{
if (TooltipContainer == null)
return;
tooltipContainer.Value.RemoveTooltip();
}
}
}

View File

@@ -0,0 +1,320 @@
#region Copyright & License Information
/*
* Copyright 2007-2014 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.Globalization;
using System.IO;
using System.Linq;
using OpenRA.FileSystem;
using OpenRA.Graphics;
using OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets.Logic
{
public class AssetBrowserLogic
{
Widget panel;
TextFieldWidget filenameInput;
SliderWidget frameSlider;
ScrollPanelWidget assetList;
ScrollItemWidget template;
IFolder assetSource = null;
List<string> availableShps = new List<string>();
bool animateFrames = false;
string currentPalette;
string currentFilename;
Sprite[] currentSprites;
int currentFrame;
readonly World world;
static readonly string[] AllowedExtensions = { ".shp", ".r8", "tmp", ".tem", ".des", ".sno", ".int", ".jun" };
[ObjectCreator.UseCtor]
public AssetBrowserLogic(Widget widget, Action onExit, World world)
{
this.world = world;
panel = widget;
assetSource = GlobalFileSystem.MountedFolders.First();
var ticker = panel.GetOrNull<LogicTickerWidget>("ANIMATION_TICKER");
if (ticker != null)
{
ticker.OnTick = () =>
{
if (animateFrames)
SelectNextFrame();
};
}
var sourceDropdown = panel.GetOrNull<DropDownButtonWidget>("SOURCE_SELECTOR");
if (sourceDropdown != null)
{
sourceDropdown.OnMouseDown = _ => ShowSourceDropdown(sourceDropdown);
sourceDropdown.GetText = () =>
{
var name = assetSource != null ? assetSource.Name.Replace(Platform.SupportDir, "^") : "All Packages";
if (name.Length > 15)
name = "..." + name.Substring(name.Length - 15);
return name;
};
}
var spriteWidget = panel.GetOrNull<SpriteWidget>("SPRITE");
if (spriteWidget != null)
{
spriteWidget.GetSprite = () => currentSprites != null ? currentSprites[currentFrame] : null;
currentPalette = spriteWidget.Palette;
spriteWidget.GetPalette = () => currentPalette;
}
var paletteDropDown = panel.GetOrNull<DropDownButtonWidget>("PALETTE_SELECTOR");
if (paletteDropDown != null)
{
paletteDropDown.OnMouseDown = _ => ShowPaletteDropdown(paletteDropDown, world);
paletteDropDown.GetText = () => currentPalette;
}
var colorPreview = panel.GetOrNull<ColorPreviewManagerWidget>("COLOR_MANAGER");
if (colorPreview != null)
colorPreview.Color = Game.Settings.Player.Color;
var colorDropdown = panel.GetOrNull<DropDownButtonWidget>("COLOR");
if (colorDropdown != null)
{
colorDropdown.IsDisabled = () => currentPalette != colorPreview.PaletteName;
colorDropdown.OnMouseDown = _ => ShowColorDropDown(colorDropdown, colorPreview, world);
panel.Get<ColorBlockWidget>("COLORBLOCK").GetColor = () => Game.Settings.Player.Color.RGB;
}
filenameInput = panel.Get<TextFieldWidget>("FILENAME_INPUT");
filenameInput.OnTextEdited = () => ApplyFilter(filenameInput.Text);
var frameContainer = panel.GetOrNull("FRAME_SELECTOR");
if (frameContainer != null)
frameContainer.IsVisible = () => currentSprites != null && currentSprites.Length > 1;
frameSlider = panel.Get<SliderWidget>("FRAME_SLIDER");
frameSlider.OnChange += x => { currentFrame = (int)Math.Round(x); };
frameSlider.GetValue = () => currentFrame;
var frameText = panel.GetOrNull<LabelWidget>("FRAME_COUNT");
if (frameText != null)
frameText.GetText = () => "{0} / {1}".F(currentFrame + 1, currentSprites.Length);
var playButton = panel.GetOrNull<ButtonWidget>("BUTTON_PLAY");
if (playButton != null)
{
playButton.OnClick = () => animateFrames = true;
playButton.IsVisible = () => !animateFrames;
}
var pauseButton = panel.GetOrNull<ButtonWidget>("BUTTON_PAUSE");
if (pauseButton != null)
{
pauseButton.OnClick = () => animateFrames = false;
pauseButton.IsVisible = () => animateFrames;
}
var stopButton = panel.GetOrNull<ButtonWidget>("BUTTON_STOP");
if (stopButton != null)
{
stopButton.OnClick = () =>
{
frameSlider.Value = 0;
currentFrame = 0;
animateFrames = false;
};
}
var nextButton = panel.GetOrNull<ButtonWidget>("BUTTON_NEXT");
if (nextButton != null)
nextButton.OnClick = SelectNextFrame;
var prevButton = panel.GetOrNull<ButtonWidget>("BUTTON_PREV");
if (prevButton != null)
prevButton.OnClick = SelectPreviousFrame;
assetList = panel.Get<ScrollPanelWidget>("ASSET_LIST");
template = panel.Get<ScrollItemWidget>("ASSET_TEMPLATE");
PopulateAssetList();
var closeButton = panel.GetOrNull<ButtonWidget>("CLOSE_BUTTON");
if (closeButton != null)
closeButton.OnClick = () => { Ui.CloseWindow(); onExit(); };
}
void SelectNextFrame()
{
currentFrame++;
if (currentFrame >= currentSprites.Length)
currentFrame = 0;
}
void SelectPreviousFrame()
{
currentFrame--;
if (currentFrame < 0)
currentFrame = currentSprites.Length - 1;
}
Dictionary<string, bool> assetVisByName = new Dictionary<string, bool>();
bool FilterAsset(string filename)
{
var filter = filenameInput.Text;
if (string.IsNullOrWhiteSpace(filter))
return true;
if (filename.IndexOf(filter, StringComparison.OrdinalIgnoreCase) >= 0)
return true;
return false;
}
void ApplyFilter(string filename)
{
assetVisByName.Clear();
assetList.Layout.AdjustChildren();
assetList.ScrollToTop();
// Select the first visible
var firstVisible = assetVisByName.FirstOrDefault(kvp => kvp.Value);
if (firstVisible.Key != null)
LoadAsset(firstVisible.Key);
}
void AddAsset(ScrollPanelWidget list, string filepath, ScrollItemWidget template)
{
var filename = Path.GetFileName(filepath);
var item = ScrollItemWidget.Setup(template,
() => currentFilename == filename,
() => { LoadAsset(filename); });
item.Get<LabelWidget>("TITLE").GetText = () => filepath;
item.IsVisible = () =>
{
bool visible;
if (assetVisByName.TryGetValue(filepath, out visible))
return visible;
visible = FilterAsset(filepath);
assetVisByName.Add(filepath, visible);
return visible;
};
list.AddChild(item);
}
bool LoadAsset(string filename)
{
if (string.IsNullOrEmpty(filename))
return false;
if (!GlobalFileSystem.Exists(filename))
return false;
currentFilename = filename;
currentSprites = world.Map.SequenceProvider.SpriteLoader.LoadAllSprites(filename);
currentFrame = 0;
frameSlider.MaximumValue = (float)currentSprites.Length - 1;
frameSlider.Ticks = currentSprites.Length;
return true;
}
bool ShowSourceDropdown(DropDownButtonWidget dropdown)
{
Func<IFolder, ScrollItemWidget, ScrollItemWidget> setupItem = (source, itemTemplate) =>
{
var item = ScrollItemWidget.Setup(itemTemplate,
() => assetSource == source,
() => { assetSource = source; PopulateAssetList(); });
item.Get<LabelWidget>("LABEL").GetText = () => source != null ? source.Name.Replace(Platform.SupportDir, "^") : "All Packages";
return item;
};
// TODO: Re-enable "All Packages" once list generation is done in a background thread
// var sources = new[] { (IFolder)null }.Concat(GlobalFileSystem.MountedFolders);
var sources = GlobalFileSystem.MountedFolders;
dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 280, sources, setupItem);
return true;
}
void PopulateAssetList()
{
assetList.RemoveChildren();
availableShps.Clear();
// TODO: This is too slow to run in the main thread
// var files = AssetSource != null ? AssetSource.AllFileNames() :
// GlobalFileSystem.MountedFolders.SelectMany(f => f.AllFileNames());
if (assetSource == null)
return;
var files = assetSource.AllFileNames().OrderBy(s => s);
foreach (var file in files)
{
if (AllowedExtensions.Any(ext => file.EndsWith(ext, true, CultureInfo.InvariantCulture)))
{
AddAsset(assetList, file, template);
availableShps.Add(file);
}
}
}
bool ShowPaletteDropdown(DropDownButtonWidget dropdown, World world)
{
Func<PaletteFromFile, ScrollItemWidget, ScrollItemWidget> setupItem = (palette, itemTemplate) =>
{
var name = palette.Name;
var item = ScrollItemWidget.Setup(itemTemplate,
() => currentPalette == name,
() => currentPalette = name);
item.Get<LabelWidget>("LABEL").GetText = () => name;
return item;
};
var palettes = world.WorldActor.TraitsImplementing<PaletteFromFile>();
dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 280, palettes, setupItem);
return true;
}
static void ShowColorDropDown(DropDownButtonWidget color, ColorPreviewManagerWidget preview, World world)
{
Action onExit = () =>
{
Game.Settings.Player.Color = preview.Color;
Game.Settings.Save();
};
color.RemovePanel();
Action<HSLColor> onChange = c => preview.Color = c;
var colorChooser = Game.LoadWidget(world, "COLOR_CHOOSER", null, new WidgetArgs()
{
{ "onChange", onChange },
{ "initialColor", Game.Settings.Player.Color }
});
color.AttachPanel(colorChooser, onExit);
}
}
}

View File

@@ -0,0 +1,57 @@
#region Copyright & License Information
/*
* Copyright 2007-2014 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 OpenRA.Graphics;
using OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets.Logic
{
public class ColorPickerLogic
{
[ObjectCreator.UseCtor]
public ColorPickerLogic(Widget widget, HSLColor initialColor, Action<HSLColor> onChange, WorldRenderer worldRenderer)
{
var ticker = widget.GetOrNull<LogicTickerWidget>("ANIMATE_PREVIEW");
if (ticker != null)
{
var preview = widget.Get<SpriteSequenceWidget>("PREVIEW");
var anim = preview.GetAnimation();
anim.PlayRepeating(anim.CurrentSequence.Name);
ticker.OnTick = anim.Tick;
}
var hueSlider = widget.Get<SliderWidget>("HUE");
var mixer = widget.Get<ColorMixerWidget>("MIXER");
var randomButton = widget.GetOrNull<ButtonWidget>("RANDOM_BUTTON");
hueSlider.OnChange += _ => mixer.Set(hueSlider.Value);
mixer.OnChange += () => onChange(mixer.Color);
if (randomButton != null)
randomButton.OnClick = () =>
{
// Avoid colors with low sat or lum
var hue = (byte)Game.CosmeticRandom.Next(255);
var sat = (byte)Game.CosmeticRandom.Next(70, 255);
var lum = (byte)Game.CosmeticRandom.Next(70, 255);
mixer.Set(new HSLColor(hue, sat, lum));
hueSlider.Value = hue / 255f;
};
// Set the initial state
mixer.Set(initialColor);
hueSlider.Value = initialColor.H / 255f;
onChange(mixer.Color);
}
}
}

View File

@@ -0,0 +1,37 @@
#region Copyright & License Information
/*
* Copyright 2007-2014 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 OpenRA.Network;
using OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets.Logic
{
public class DisconnectWatcherLogic
{
[ObjectCreator.UseCtor]
public DisconnectWatcherLogic(Widget widget, OrderManager orderManager)
{
var disconnected = false;
widget.Get<LogicTickerWidget>("DISCONNECT_WATCHER").OnTick = () =>
{
if (disconnected || orderManager.Connection.ConnectionState != ConnectionState.NotConnected)
return;
Game.RunAfterTick(() => Ui.OpenWindow("CONNECTIONFAILED_PANEL", new WidgetArgs {
{ "orderManager", orderManager },
{ "onAbort", null },
{ "onRetry", null }
}));
disconnected = true;
};
}
}
}

View File

@@ -0,0 +1,49 @@
#region Copyright & License Information
/*
* Copyright 2007-2014 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.Drawing;
using System.Linq;
using OpenRA.Mods.Common.Traits;
using OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets.Logic
{
public class IngameRadarDisplayLogic
{
[ObjectCreator.UseCtor]
public IngameRadarDisplayLogic(Widget widget, World world)
{
var radarEnabled = false;
var cachedRadarEnabled = false;
var blockColor = Color.Transparent;
var radar = widget.Get<RadarWidget>("RADAR_MINIMAP");
radar.IsEnabled = () => radarEnabled;
var ticker = widget.Get<LogicTickerWidget>("RADAR_TICKER");
ticker.OnTick = () =>
{
radarEnabled = world.ActorsWithTrait<ProvidesRadar>()
.Any(a => a.Actor.Owner == world.LocalPlayer && a.Trait.IsActive);
if (radarEnabled != cachedRadarEnabled)
Sound.PlayNotification(world.Map.Rules, null, "Sounds", radarEnabled ? "RadarUp" : "RadarDown", null);
cachedRadarEnabled = radarEnabled;
};
var block = widget.GetOrNull<ColorBlockWidget>("RADAR_FADETOBLACK");
if (block != null)
{
radar.Animating = x => blockColor = Color.FromArgb((int)(255 * x), Color.Black);
block.IsVisible = () => blockColor.A != 0;
block.GetColor = () => blockColor;
}
}
}
}

View File

@@ -0,0 +1,53 @@
#region Copyright & License Information
/*
* Copyright 2007-2014 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 OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets.Logic
{
public class LoadIngamePlayerOrObserverUILogic
{
[ObjectCreator.UseCtor]
public LoadIngamePlayerOrObserverUILogic(Widget widget, World world)
{
var ingameRoot = widget.Get("INGAME_ROOT");
var playerRoot = ingameRoot.Get("PLAYER_ROOT");
if (world.LocalPlayer == null)
Game.LoadWidget(world, "OBSERVER_WIDGETS", playerRoot, new WidgetArgs());
else
{
var playerWidgets = Game.LoadWidget(world, "PLAYER_WIDGETS", playerRoot, new WidgetArgs());
var sidebarTicker = playerWidgets.Get<LogicTickerWidget>("SIDEBAR_TICKER");
sidebarTicker.OnTick = () =>
{
// Switch to observer mode after win/loss
if (world.ObserveAfterWinOrLose && world.LocalPlayer.WinState != WinState.Undefined)
Game.RunAfterTick(() =>
{
playerRoot.RemoveChildren();
Game.LoadWidget(world, "OBSERVER_WIDGETS", playerRoot, new WidgetArgs());
});
};
}
Game.LoadWidget(world, "CHAT_PANEL", ingameRoot, new WidgetArgs());
Action showLeaveMapWidget = () =>
{
ingameRoot.RemoveChildren();
Game.LoadWidget(world, "LEAVE_MAP_WIDGET", Ui.Root, new WidgetArgs());
};
world.GameOver += showLeaveMapWidget;
}
}
}

View File

@@ -0,0 +1,25 @@
#region Copyright & License Information
/*
* Copyright 2007-2014 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 OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets
{
public class LogicKeyListenerWidget : Widget
{
public Func<KeyInput, bool> OnKeyPress = _ => false;
public override bool HandleKeyPress(KeyInput e)
{
return OnKeyPress(e);
}
}
}

View File

@@ -0,0 +1,21 @@
#region Copyright & License Information
/*
* Copyright 2007-2014 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 OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets
{
public class LogicTickerWidget : Widget
{
public Action OnTick = () => { };
public override void Tick() { OnTick(); }
}
}

View File

@@ -0,0 +1,33 @@
#region Copyright & License Information
/*
* Copyright 2007-2014 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 OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets
{
public class MenuButtonWidget : ButtonWidget
{
public readonly string MenuContainer = "INGAME_MENU";
public readonly bool Pause = true;
public readonly bool HideIngameUI = true;
[ObjectCreator.UseCtor]
public MenuButtonWidget(Ruleset modRules)
: base(modRules) { }
protected MenuButtonWidget(MenuButtonWidget other)
: base(other)
{
MenuContainer = other.MenuContainer;
Pause = other.Pause;
HideIngameUI = other.HideIngameUI;
}
}
}

View File

@@ -0,0 +1,263 @@
#region Copyright & License Information
/*
* Copyright 2007-2014 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.Drawing;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets
{
public class RadarWidget : Widget
{
public string WorldInteractionController = null;
public int AnimationLength = 5;
public string RadarOnlineSound = null;
public string RadarOfflineSound = null;
public Func<bool> IsEnabled = () => true;
public Action AfterOpen = () => { };
public Action AfterClose = () => { };
public Action<float> Animating = _ => {};
float radarMinimapHeight;
int frame;
bool hasRadar;
bool cachedEnabled;
int updateTicks;
float previewScale = 0;
int2 previewOrigin = int2.Zero;
Rectangle mapRect = Rectangle.Empty;
Sprite terrainSprite;
Sprite customTerrainSprite;
Sprite actorSprite;
Sprite shroudSprite;
readonly World world;
readonly WorldRenderer worldRenderer;
readonly RadarPings radarPings;
[ObjectCreator.UseCtor]
public RadarWidget(World world, WorldRenderer worldRenderer)
{
this.world = world;
this.worldRenderer = worldRenderer;
radarPings = world.WorldActor.TraitOrDefault<RadarPings>();
}
public override void Initialize(WidgetArgs args)
{
base.Initialize(args);
var width = world.Map.Bounds.Width;
var height = world.Map.Bounds.Height;
var size = Math.Max(width, height);
var rb = RenderBounds;
previewScale = Math.Min(rb.Width * 1f / width, rb.Height * 1f / height);
previewOrigin = new int2((int)(previewScale * (size - width) / 2), (int)(previewScale * (size - height) / 2));
mapRect = new Rectangle(previewOrigin.X, previewOrigin.Y, (int)(previewScale * width), (int)(previewScale * height));
// Only needs to be done once
using (var terrainBitmap = Minimap.TerrainBitmap(world.Map.Rules.TileSets[world.Map.Tileset], world.Map))
{
var r = new Rectangle(0, 0, width, height);
var s = new Size(terrainBitmap.Width, terrainBitmap.Height);
var terrainSheet = new Sheet(s, false);
terrainSheet.Texture.SetData(terrainBitmap);
terrainSprite = new Sprite(terrainSheet, r, TextureChannel.Alpha);
// Data is set in Tick()
customTerrainSprite = new Sprite(new Sheet(s, false), r, TextureChannel.Alpha);
actorSprite = new Sprite(new Sheet(s, false), r, TextureChannel.Alpha);
shroudSprite = new Sprite(new Sheet(s, false), r, TextureChannel.Alpha);
}
}
public override string GetCursor(int2 pos)
{
if (world == null || !hasRadar)
return null;
var cell = MinimapPixelToCell(pos);
var location = worldRenderer.Viewport.WorldToViewPx(worldRenderer.ScreenPxPosition(world.Map.CenterOfCell(cell)));
var mi = new MouseInput
{
Location = location,
Button = MouseButton.Right,
Modifiers = Game.GetModifierKeys()
};
var cursor = world.OrderGenerator.GetCursor(world, cell, mi);
if (cursor == null)
return "default";
return Game.modData.CursorProvider.HasCursorSequence(cursor + "-minimap") ? cursor + "-minimap" : cursor;
}
public override bool HandleMouseInput(MouseInput mi)
{
if (!mapRect.Contains(mi.Location))
return false;
if (!hasRadar)
return true;
var cell = MinimapPixelToCell(mi.Location);
var pos = world.Map.CenterOfCell(cell);
if ((mi.Event == MouseInputEvent.Down || mi.Event == MouseInputEvent.Move) && mi.Button == MouseButton.Left)
worldRenderer.Viewport.Center(pos);
if (mi.Event == MouseInputEvent.Down && mi.Button == MouseButton.Right)
{
// fake a mousedown/mouseup here
var location = worldRenderer.Viewport.WorldToViewPx(worldRenderer.ScreenPxPosition(pos));
var fakemi = new MouseInput
{
Event = MouseInputEvent.Down,
Button = MouseButton.Right,
Modifiers = mi.Modifiers,
Location = location
};
if (WorldInteractionController != null)
{
var controller = Ui.Root.Get<WorldInteractionControllerWidget>(WorldInteractionController);
controller.HandleMouseInput(fakemi);
fakemi.Event = MouseInputEvent.Up;
controller.HandleMouseInput(fakemi);
}
}
return true;
}
public override void Draw()
{
if (world == null)
return;
var o = new float2(mapRect.Location.X, mapRect.Location.Y + world.Map.Bounds.Height * previewScale * (1 - radarMinimapHeight) / 2);
var s = new float2(mapRect.Size.Width, mapRect.Size.Height * radarMinimapHeight);
var rsr = Game.Renderer.RgbaSpriteRenderer;
rsr.DrawSprite(terrainSprite, o, s);
rsr.DrawSprite(customTerrainSprite, o, s);
rsr.DrawSprite(actorSprite, o, s);
rsr.DrawSprite(shroudSprite, o, s);
// Draw viewport rect
if (hasRadar)
{
var tl = CellToMinimapPixel(world.Map.CellContaining(worldRenderer.Position(worldRenderer.Viewport.TopLeft)));
var br = CellToMinimapPixel(world.Map.CellContaining(worldRenderer.Position(worldRenderer.Viewport.BottomRight)));
Game.Renderer.EnableScissor(mapRect);
DrawRadarPings();
Game.Renderer.LineRenderer.DrawRect(tl, br, Color.White);
Game.Renderer.DisableScissor();
}
}
void DrawRadarPings()
{
if (radarPings == null)
return;
var lr = Game.Renderer.LineRenderer;
var oldWidth = lr.LineWidth;
lr.LineWidth = 2;
foreach (var radarPing in radarPings.Pings.Where(e => e.IsVisible()))
{
var c = radarPing.Color;
var pingCell = world.Map.CellContaining(radarPing.Position);
var points = radarPing.Points(CellToMinimapPixel(pingCell)).ToArray();
lr.DrawLine(points[0], points[1], c, c);
lr.DrawLine(points[1], points[2], c, c);
lr.DrawLine(points[2], points[0], c, c);
}
lr.LineWidth = oldWidth;
}
public override void Tick()
{
// Update the radar animation even when its closed
// This avoids obviously stale data from being shown when first opened.
// TODO: This delayed updating is a giant hack
--updateTicks;
if (updateTicks <= 0)
{
updateTicks = 12;
using (var bitmap = Minimap.CustomTerrainBitmap(world))
customTerrainSprite.sheet.Texture.SetData(bitmap);
}
if (updateTicks == 8)
using (var bitmap = Minimap.ActorsBitmap(world))
actorSprite.sheet.Texture.SetData(bitmap);
if (updateTicks == 4)
using (var bitmap = Minimap.ShroudBitmap(world))
shroudSprite.sheet.Texture.SetData(bitmap);
// Enable/Disable the radar
var enabled = IsEnabled();
if (enabled != cachedEnabled)
Sound.Play(enabled ? RadarOnlineSound : RadarOfflineSound);
cachedEnabled = enabled;
var targetFrame = enabled ? AnimationLength : 0;
hasRadar = enabled && frame == AnimationLength;
if (frame == targetFrame)
return;
frame += enabled ? 1 : -1;
radarMinimapHeight = float2.Lerp(0, 1, (float)frame / AnimationLength);
Animating(frame * 1f / AnimationLength);
// Update map rectangle for event handling
var ro = RenderOrigin;
mapRect = new Rectangle(previewOrigin.X + ro.X, previewOrigin.Y + ro.Y, mapRect.Width, mapRect.Height);
// Animation is complete
if (frame == targetFrame)
{
if (enabled)
AfterOpen();
else
AfterClose();
}
}
int2 CellToMinimapPixel(CPos p)
{
var mapOrigin = new CVec(world.Map.Bounds.Left, world.Map.Bounds.Top);
var mapOffset = Map.CellToMap(world.Map.TileShape, p) - mapOrigin;
return new int2(mapRect.X, mapRect.Y) + (previewScale * new float2(mapOffset.X, mapOffset.Y)).ToInt2();
}
CPos MinimapPixelToCell(int2 p)
{
var viewOrigin = new float2(mapRect.X, mapRect.Y);
var mapOrigin = new float2(world.Map.Bounds.Left, world.Map.Bounds.Top);
var fcell = mapOrigin + (1f / previewScale) * (p - viewOrigin);
return Map.MapToCell(world.Map.TileShape, new CPos((int)fcell.X, (int)fcell.Y));
}
}
}

View File

@@ -0,0 +1,136 @@
#region Copyright & License Information
/*
* Copyright 2007-2014 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.Drawing;
using OpenRA.Graphics;
using OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets
{
public enum ResourceBarOrientation { Vertical, Horizontal }
public enum ResourceBarStyle { Flat, Bevelled }
public class ResourceBarWidget : Widget
{
public readonly string TooltipTemplate;
public readonly string TooltipContainer;
Lazy<TooltipContainerWidget> tooltipContainer;
public string TooltipFormat = "";
public ResourceBarOrientation Orientation = ResourceBarOrientation.Vertical;
public ResourceBarStyle Style = ResourceBarStyle.Flat;
public string IndicatorCollection = "sidebar-bits";
public string IndicatorImage = "indicator";
public Func<float> GetProvided = () => 0;
public Func<float> GetUsed = () => 0;
public Func<Color> GetBarColor = () => Color.White;
EWMA providedLerp = new EWMA(0.3f);
EWMA usedLerp = new EWMA(0.3f);
[ObjectCreator.UseCtor]
public ResourceBarWidget(World world)
{
tooltipContainer = Exts.Lazy(() =>
Ui.Root.Get<TooltipContainerWidget>(TooltipContainer));
}
public override void MouseEntered()
{
if (TooltipContainer == null)
return;
Func<string> getText = () => TooltipFormat.F(GetUsed(), GetProvided());
tooltipContainer.Value.SetTooltip(TooltipTemplate, new WidgetArgs() {{ "getText", getText }});
}
public override void MouseExited()
{
if (TooltipContainer == null)
return;
tooltipContainer.Value.RemoveTooltip();
}
public override void Draw()
{
var scaleBy = 100.0f;
var provided = GetProvided();
var used = GetUsed();
var max = Math.Max(provided, used);
while (max >= scaleBy)
scaleBy *= 2;
var providedFrac = providedLerp.Update(provided/scaleBy);
var usedFrac = usedLerp.Update(used/scaleBy);
var b = RenderBounds;
var indicator = ChromeProvider.GetImage(IndicatorCollection, IndicatorImage);
var color = GetBarColor();
if (Orientation == ResourceBarOrientation.Vertical)
{
if (Style == ResourceBarStyle.Bevelled)
{
var colorDark = Exts.ColorLerp(0.25f, color, Color.Black);
for (var i = 0; i < b.Height; i++)
{
color = (i - 1 < b.Height / 2) ? color : colorDark;
var bottom = new float2(b.Left + i, b.Bottom);
var top = new float2(b.Left + i, b.Bottom + providedFrac*b.Height);
// Indent corners
if ((i == 0 || i == b.Width - 1) && providedFrac*b.Height > 1)
{
bottom.Y += 1;
top.Y -= 1;
}
Game.Renderer.LineRenderer.DrawLine(bottom, top, color, color);
}
}
else
Game.Renderer.LineRenderer.FillRect(new Rectangle(b.X, (int)float2.Lerp(b.Bottom, b.Top, providedFrac),
b.Width, (int)(providedFrac*b.Height)), color);
var x = (b.Left + b.Right - indicator.size.X) / 2;
var y = float2.Lerp(b.Bottom, b.Top, usedFrac) - indicator.size.Y / 2;
Game.Renderer.RgbaSpriteRenderer.DrawSprite(indicator, new float2(x, y));
}
else
{
if (Style == ResourceBarStyle.Bevelled)
{
var colorDark = Exts.ColorLerp(0.25f, color, Color.Black);
for (var i = 0; i < b.Height; i++)
{
color = (i - 1 < b.Height / 2) ? color : colorDark;
var left = new float2(b.Left, b.Top + i);
var right = new float2(b.Left + providedFrac*b.Width, b.Top + i);
// Indent corners
if ((i == 0 || i == b.Height - 1) && providedFrac*b.Width > 1)
{
left.X += 1;
right.X -= 1;
}
Game.Renderer.LineRenderer.DrawLine(left, right, color, color);
}
}
else
Game.Renderer.LineRenderer.FillRect(new Rectangle(b.X, b.Y, (int)(providedFrac*b.Width), b.Height), color);
var x = float2.Lerp(b.Left, b.Right, usedFrac) - indicator.size.X / 2;
var y = (b.Bottom + b.Top - indicator.size.Y) / 2;
Game.Renderer.RgbaSpriteRenderer.DrawSprite(indicator, new float2(x, y));
}
}
}
}

View File

@@ -0,0 +1,113 @@
#region Copyright & License Information
/*
* Copyright 2007-2014 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 OpenRA.Traits;
namespace OpenRA.Mods.Common
{
public class RadarPingsInfo : ITraitInfo
{
public readonly int FromRadius = 200;
public readonly int ToRadius = 15;
public readonly int ShrinkSpeed = 4;
public readonly float RotationSpeed = 0.12f;
public object Create(ActorInitializer init) { return new RadarPings(this); }
}
public class RadarPings : ITick
{
public readonly List<RadarPing> Pings = new List<RadarPing>();
readonly RadarPingsInfo info;
public WPos? LastPingPosition;
public RadarPings(RadarPingsInfo info)
{
this.info = info;
}
public void Tick(Actor self)
{
foreach (var ping in Pings.ToArray())
if (!ping.Tick())
Pings.Remove(ping);
}
public RadarPing Add(Func<bool> isVisible, WPos position, Color color, int duration)
{
var ping = new RadarPing(isVisible, position, color, duration,
info.FromRadius, info.ToRadius, info.ShrinkSpeed, info.RotationSpeed);
if (ping.IsVisible())
LastPingPosition = ping.Position;
Pings.Add(ping);
return ping;
}
public void Remove(RadarPing ping)
{
Pings.Remove(ping);
}
}
public class RadarPing
{
public Func<bool> IsVisible;
public WPos Position;
public Color Color;
public int Duration;
public int FromRadius;
public int ToRadius;
public int ShrinkSpeed;
public float RotationSpeed;
int radius;
float angle;
int tick;
public RadarPing(Func<bool> isVisible, WPos position, Color color, int duration,
int fromRadius, int toRadius, int shrinkSpeed, float rotationSpeed)
{
IsVisible = isVisible;
Position = position;
Color = color;
Duration = duration;
FromRadius = fromRadius;
ToRadius = toRadius;
ShrinkSpeed = shrinkSpeed;
RotationSpeed = rotationSpeed;
radius = fromRadius;
}
public bool Tick()
{
if (++tick == Duration)
return false;
radius = Math.Max(radius - ShrinkSpeed, ToRadius);
angle -= RotationSpeed;
return true;
}
public IEnumerable<float2> Points(float2 center)
{
yield return center + radius * float2.FromAngle(angle);
yield return center + radius * float2.FromAngle((float)(angle + 2 * Math.PI / 3));
yield return center + radius * float2.FromAngle((float)(angle + 4 * Math.PI / 3));
}
}
}