Add multi-resolution badge support.
This commit is contained in:
@@ -23,67 +23,98 @@ namespace OpenRA
|
|||||||
public class PlayerDatabase : IGlobalModData
|
public class PlayerDatabase : IGlobalModData
|
||||||
{
|
{
|
||||||
public readonly string Profile = "https://forum.openra.net/openra/info/";
|
public readonly string Profile = "https://forum.openra.net/openra/info/";
|
||||||
|
public readonly int IconSize = 24;
|
||||||
|
|
||||||
[FieldLoader.Ignore]
|
// 512x512 is large enough for 49 unique 72x72 badges
|
||||||
readonly object syncObject = new object();
|
// or 100 unique 42x42 badges
|
||||||
|
// or 441 unique 24x24 badges
|
||||||
[FieldLoader.Ignore]
|
// or some combination of the above if the DPI changes ingame
|
||||||
readonly Dictionary<string, Sprite> spriteCache = new Dictionary<string, Sprite>();
|
|
||||||
|
|
||||||
// 128x128 is large enough for 25 unique 24x24 sprites
|
|
||||||
[FieldLoader.Ignore]
|
[FieldLoader.Ignore]
|
||||||
SheetBuilder sheetBuilder;
|
SheetBuilder sheetBuilder;
|
||||||
|
|
||||||
|
[FieldLoader.Ignore]
|
||||||
|
Cache<Pair<PlayerBadge, int>, Sprite> iconCache;
|
||||||
|
|
||||||
|
Sprite LoadSprite(string url, int density)
|
||||||
|
{
|
||||||
|
var spriteSize = IconSize * density;
|
||||||
|
var sprite = sheetBuilder.Allocate(new Size(spriteSize, spriteSize), 1f / density);
|
||||||
|
|
||||||
|
Action<DownloadDataCompletedEventArgs> onComplete = i =>
|
||||||
|
{
|
||||||
|
if (i.Error != null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var icon = new Png(new MemoryStream(i.Result));
|
||||||
|
if (icon.Width == spriteSize && icon.Height == spriteSize)
|
||||||
|
{
|
||||||
|
Game.RunAfterTick(() =>
|
||||||
|
{
|
||||||
|
Util.FastCopyIntoSprite(sprite, icon);
|
||||||
|
sprite.Sheet.CommitBufferedData();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
};
|
||||||
|
|
||||||
|
new Download(url, _ => { }, onComplete);
|
||||||
|
|
||||||
|
return sprite;
|
||||||
|
}
|
||||||
|
|
||||||
|
Sheet CreateSheet()
|
||||||
|
{
|
||||||
|
var sheet = new Sheet(SheetType.BGRA, new Size(512, 512));
|
||||||
|
|
||||||
|
// We must manually force the buffer creation to avoid a crash
|
||||||
|
// that is indirectly triggered by rendering from a Sheet that
|
||||||
|
// has not yet been written to.
|
||||||
|
sheet.CreateBuffer();
|
||||||
|
sheet.GetTexture().ScaleFilter = TextureScaleFilter.Linear;
|
||||||
|
|
||||||
|
return sheet;
|
||||||
|
}
|
||||||
|
|
||||||
public PlayerBadge LoadBadge(MiniYaml yaml)
|
public PlayerBadge LoadBadge(MiniYaml yaml)
|
||||||
{
|
{
|
||||||
if (sheetBuilder == null)
|
if (sheetBuilder == null)
|
||||||
{
|
{
|
||||||
sheetBuilder = new SheetBuilder(SheetType.BGRA, 128);
|
sheetBuilder = new SheetBuilder(SheetType.BGRA, CreateSheet);
|
||||||
|
|
||||||
// We must manually force the buffer creation to avoid a crash
|
iconCache = new Cache<Pair<PlayerBadge, int>, Sprite>(p =>
|
||||||
// that is indirectly triggered by rendering from a Sheet that
|
{
|
||||||
// has not yet been written to.
|
if (p.Second > 2 && !string.IsNullOrEmpty(p.First.Icon3x))
|
||||||
sheetBuilder.Current.CreateBuffer();
|
return LoadSprite(p.First.Icon3x, 3);
|
||||||
|
|
||||||
|
if (p.Second > 1 && !string.IsNullOrEmpty(p.First.Icon2x))
|
||||||
|
return LoadSprite(p.First.Icon2x, 2);
|
||||||
|
|
||||||
|
return LoadSprite(p.First.Icon, 1);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var labelNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Label");
|
var labelNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Label");
|
||||||
var icon24Node = yaml.Nodes.FirstOrDefault(n => n.Key == "Icon24");
|
var icon24Node = yaml.Nodes.FirstOrDefault(n => n.Key == "Icon24");
|
||||||
if (labelNode == null || icon24Node == null)
|
var icon48Node = yaml.Nodes.FirstOrDefault(n => n.Key == "Icon48");
|
||||||
|
var icon72Node = yaml.Nodes.FirstOrDefault(n => n.Key == "Icon72");
|
||||||
|
if (labelNode == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
Sprite sprite;
|
return new PlayerBadge(
|
||||||
lock (syncObject)
|
labelNode.Value.Value,
|
||||||
{
|
icon24Node != null ? icon24Node.Value.Value : null,
|
||||||
if (!spriteCache.TryGetValue(icon24Node.Value.Value, out sprite))
|
icon48Node != null ? icon48Node.Value.Value : null,
|
||||||
{
|
icon72Node != null ? icon72Node.Value.Value : null);
|
||||||
sprite = spriteCache[icon24Node.Value.Value] = sheetBuilder.Allocate(new Size(24, 24));
|
}
|
||||||
sprite.Sheet.GetTexture().ScaleFilter = TextureScaleFilter.Linear;
|
|
||||||
|
|
||||||
Action<DownloadDataCompletedEventArgs> onComplete = i =>
|
public Sprite GetIcon(PlayerBadge badge)
|
||||||
{
|
{
|
||||||
if (i.Error != null)
|
var ws = Game.Renderer.WindowScale;
|
||||||
return;
|
var density = ws > 2 ? 3 : ws > 1 ? 2 : 1;
|
||||||
|
return iconCache[Pair.New(badge, density)];
|
||||||
try
|
|
||||||
{
|
|
||||||
var icon = new Png(new MemoryStream(i.Result));
|
|
||||||
if (icon.Width == 24 && icon.Height == 24)
|
|
||||||
{
|
|
||||||
Game.RunAfterTick(() =>
|
|
||||||
{
|
|
||||||
Util.FastCopyIntoSprite(sprite, icon);
|
|
||||||
sprite.Sheet.CommitBufferedData();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
};
|
|
||||||
|
|
||||||
new Download(icon24Node.Value.Value, _ => { }, onComplete);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new PlayerBadge(labelNode.Value.Value, sprite);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using OpenRA.Graphics;
|
|
||||||
|
|
||||||
namespace OpenRA
|
namespace OpenRA
|
||||||
{
|
{
|
||||||
@@ -59,12 +58,16 @@ namespace OpenRA
|
|||||||
public class PlayerBadge
|
public class PlayerBadge
|
||||||
{
|
{
|
||||||
public readonly string Label;
|
public readonly string Label;
|
||||||
public readonly Sprite Icon24;
|
public readonly string Icon;
|
||||||
|
public readonly string Icon2x;
|
||||||
|
public readonly string Icon3x;
|
||||||
|
|
||||||
public PlayerBadge(string label, Sprite icon24)
|
public PlayerBadge(string label, string icon, string icon2x, string icon3x)
|
||||||
{
|
{
|
||||||
Label = label;
|
Label = label;
|
||||||
Icon24 = icon24;
|
Icon = icon;
|
||||||
|
Icon2x = icon2x;
|
||||||
|
Icon3x = icon3x;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
46
OpenRA.Mods.Common/Widgets/BadgeWidget.cs
Normal file
46
OpenRA.Mods.Common/Widgets/BadgeWidget.cs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#region Copyright & License Information
|
||||||
|
/*
|
||||||
|
* Copyright 2007-2020 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, either version 3 of
|
||||||
|
* the License, or (at your option) any later version. For more
|
||||||
|
* information, see COPYING.
|
||||||
|
*/
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using OpenRA.Widgets;
|
||||||
|
|
||||||
|
namespace OpenRA.Mods.Common.Widgets
|
||||||
|
{
|
||||||
|
public class BadgeWidget : Widget
|
||||||
|
{
|
||||||
|
public PlayerBadge Badge;
|
||||||
|
readonly PlayerDatabase playerDatabase;
|
||||||
|
|
||||||
|
[ObjectCreator.UseCtor]
|
||||||
|
public BadgeWidget(ModData modData)
|
||||||
|
{
|
||||||
|
playerDatabase = modData.Manifest.Get<PlayerDatabase>();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected BadgeWidget(BadgeWidget other)
|
||||||
|
: base(other)
|
||||||
|
{
|
||||||
|
Badge = other.Badge;
|
||||||
|
playerDatabase = other.playerDatabase;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Widget Clone() { return new BadgeWidget(this); }
|
||||||
|
|
||||||
|
public override void Draw()
|
||||||
|
{
|
||||||
|
if (Badge == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var icon = playerDatabase.GetIcon(Badge);
|
||||||
|
if (icon != null)
|
||||||
|
Game.Renderer.RgbaSpriteRenderer.DrawSprite(icon, RenderOrigin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -270,7 +270,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
|||||||
|
|
||||||
// Negotiate the label length that the tooltip will allow
|
// Negotiate the label length that the tooltip will allow
|
||||||
var maxLabelWidth = 0;
|
var maxLabelWidth = 0;
|
||||||
var templateIcon = badgeTemplate.Get<SpriteWidget>("ICON");
|
var templateIcon = badgeTemplate.Get("ICON");
|
||||||
var templateLabel = badgeTemplate.Get<LabelWidget>("LABEL");
|
var templateLabel = badgeTemplate.Get<LabelWidget>("LABEL");
|
||||||
var templateLabelFont = Game.Renderer.Fonts[templateLabel.Font];
|
var templateLabelFont = Game.Renderer.Fonts[templateLabel.Font];
|
||||||
foreach (var badge in profile.Badges)
|
foreach (var badge in profile.Badges)
|
||||||
@@ -285,8 +285,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
|||||||
foreach (var badge in profile.Badges)
|
foreach (var badge in profile.Badges)
|
||||||
{
|
{
|
||||||
var b = badgeTemplate.Clone();
|
var b = badgeTemplate.Clone();
|
||||||
var icon = b.Get<SpriteWidget>("ICON");
|
var icon = b.Get<BadgeWidget>("ICON");
|
||||||
icon.GetSprite = () => badge.Icon24;
|
icon.Badge = badge;
|
||||||
|
|
||||||
var label = b.Get<LabelWidget>("LABEL");
|
var label = b.Get<LabelWidget>("LABEL");
|
||||||
var labelFont = Game.Renderer.Fonts[label.Font];
|
var labelFont = Game.Renderer.Fonts[label.Font];
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ Container@PLAYER_PROFILE_BADGES_INSERT:
|
|||||||
Width: PARENT_RIGHT
|
Width: PARENT_RIGHT
|
||||||
Height: 25
|
Height: 25
|
||||||
Children:
|
Children:
|
||||||
Sprite@ICON:
|
Badge@ICON:
|
||||||
X: 6
|
X: 6
|
||||||
Y: 1
|
Y: 1
|
||||||
Width: 24
|
Width: 24
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ Container@PLAYER_PROFILE_BADGES_INSERT:
|
|||||||
Width: PARENT_RIGHT
|
Width: PARENT_RIGHT
|
||||||
Height: 25
|
Height: 25
|
||||||
Children:
|
Children:
|
||||||
Sprite@ICON:
|
Badge@ICON:
|
||||||
X: 6
|
X: 6
|
||||||
Y: 1
|
Y: 1
|
||||||
Width: 24
|
Width: 24
|
||||||
|
|||||||
Reference in New Issue
Block a user