Add player badges.
This commit is contained in:
@@ -9,10 +9,79 @@
|
|||||||
*/
|
*/
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using OpenRA.Graphics;
|
||||||
|
|
||||||
namespace OpenRA
|
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/";
|
||||||
|
|
||||||
|
[FieldLoader.Ignore]
|
||||||
|
readonly object syncObject = new object();
|
||||||
|
|
||||||
|
[FieldLoader.Ignore]
|
||||||
|
readonly Dictionary<string, Sprite> spriteCache = new Dictionary<string, Sprite>();
|
||||||
|
|
||||||
|
// 128x128 is large enough for 25 unique 24x24 sprites
|
||||||
|
[FieldLoader.Ignore]
|
||||||
|
SheetBuilder sheetBuilder;
|
||||||
|
|
||||||
|
public PlayerBadge LoadBadge(MiniYaml yaml)
|
||||||
|
{
|
||||||
|
if (sheetBuilder == null)
|
||||||
|
{
|
||||||
|
sheetBuilder = new SheetBuilder(SheetType.BGRA, 128);
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
sheetBuilder.Current.CreateBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
var labelNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Label");
|
||||||
|
var icon24Node = yaml.Nodes.FirstOrDefault(n => n.Key == "Icon24");
|
||||||
|
if (labelNode == null || icon24Node == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
Sprite sprite;
|
||||||
|
lock (syncObject)
|
||||||
|
{
|
||||||
|
if (!spriteCache.TryGetValue(icon24Node.Value.Value, out sprite))
|
||||||
|
{
|
||||||
|
sprite = spriteCache[icon24Node.Value.Value] = sheetBuilder.Allocate(new Size(24, 24));
|
||||||
|
|
||||||
|
Action<DownloadDataCompletedEventArgs> onComplete = i =>
|
||||||
|
{
|
||||||
|
if (i.Error != null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var icon = new Bitmap(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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,10 @@
|
|||||||
*/
|
*/
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using OpenRA.Graphics;
|
||||||
|
|
||||||
namespace OpenRA
|
namespace OpenRA
|
||||||
{
|
{
|
||||||
public class PlayerProfile
|
public class PlayerProfile
|
||||||
@@ -20,5 +24,46 @@ namespace OpenRA
|
|||||||
public readonly int ProfileID;
|
public readonly int ProfileID;
|
||||||
public readonly string ProfileName;
|
public readonly string ProfileName;
|
||||||
public readonly string ProfileRank = "Registered Player";
|
public readonly string ProfileRank = "Registered Player";
|
||||||
|
|
||||||
|
[FieldLoader.LoadUsing("LoadBadges")]
|
||||||
|
public readonly List<PlayerBadge> Badges;
|
||||||
|
|
||||||
|
static object LoadBadges(MiniYaml yaml)
|
||||||
|
{
|
||||||
|
var badges = new List<PlayerBadge>();
|
||||||
|
|
||||||
|
var badgesNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Badges");
|
||||||
|
if (badgesNode != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var playerDatabase = Game.ModData.Manifest.Get<PlayerDatabase>();
|
||||||
|
foreach (var badgeNode in badgesNode.Value.Nodes)
|
||||||
|
{
|
||||||
|
var badge = playerDatabase.LoadBadge(badgeNode.Value);
|
||||||
|
if (badge != null)
|
||||||
|
badges.Add(badge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Discard badges on error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return badges;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PlayerBadge
|
||||||
|
{
|
||||||
|
public readonly string Label;
|
||||||
|
public readonly Sprite Icon24;
|
||||||
|
|
||||||
|
public PlayerBadge(string label, Sprite icon24)
|
||||||
|
{
|
||||||
|
Label = label;
|
||||||
|
Icon24 = icon24;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,12 +21,18 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
|||||||
{
|
{
|
||||||
public class LocalProfileLogic : ChromeLogic
|
public class LocalProfileLogic : ChromeLogic
|
||||||
{
|
{
|
||||||
|
readonly WorldRenderer worldRenderer;
|
||||||
readonly LocalPlayerProfile localProfile;
|
readonly LocalPlayerProfile localProfile;
|
||||||
|
readonly Widget badgeContainer;
|
||||||
|
readonly Widget widget;
|
||||||
bool notFound;
|
bool notFound;
|
||||||
|
bool badgesVisible;
|
||||||
|
|
||||||
[ObjectCreator.UseCtor]
|
[ObjectCreator.UseCtor]
|
||||||
public LocalProfileLogic(Widget widget, WorldRenderer worldRenderer, Func<bool> minimalProfile)
|
public LocalProfileLogic(Widget widget, WorldRenderer worldRenderer, Func<bool> minimalProfile)
|
||||||
{
|
{
|
||||||
|
this.worldRenderer = worldRenderer;
|
||||||
|
this.widget = widget;
|
||||||
localProfile = Game.LocalPlayerProfile;
|
localProfile = Game.LocalPlayerProfile;
|
||||||
|
|
||||||
// Key registration
|
// Key registration
|
||||||
@@ -78,6 +84,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
|||||||
destroyKey.OnClick = localProfile.DeleteKeypair;
|
destroyKey.OnClick = localProfile.DeleteKeypair;
|
||||||
destroyKey.IsDisabled = minimalProfile;
|
destroyKey.IsDisabled = minimalProfile;
|
||||||
|
|
||||||
|
badgeContainer = widget.Get("BADGES_CONTAINER");
|
||||||
|
badgeContainer.IsVisible = () => badgesVisible && !minimalProfile()
|
||||||
|
&& localProfile.State == LocalPlayerProfile.LinkState.Linked;
|
||||||
|
|
||||||
localProfile.RefreshPlayerData(() => RefreshComplete(false));
|
localProfile.RefreshPlayerData(() => RefreshComplete(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +96,36 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
|||||||
if (updateNotFound)
|
if (updateNotFound)
|
||||||
notFound = localProfile.State == LocalPlayerProfile.LinkState.Unlinked;
|
notFound = localProfile.State == LocalPlayerProfile.LinkState.Unlinked;
|
||||||
|
|
||||||
Game.RunAfterTick(Ui.ResetTooltips);
|
Game.RunAfterTick(() =>
|
||||||
|
{
|
||||||
|
badgesVisible = false;
|
||||||
|
|
||||||
|
if (localProfile.State == LocalPlayerProfile.LinkState.Linked)
|
||||||
|
{
|
||||||
|
if (localProfile.ProfileData.Badges.Any())
|
||||||
|
{
|
||||||
|
Func<int, int> negotiateWidth = _ => widget.Get("PROFILE_HEADER").Bounds.Width;
|
||||||
|
|
||||||
|
// Remove any stale badges that may be left over from a previous session
|
||||||
|
badgeContainer.RemoveChildren();
|
||||||
|
|
||||||
|
var badges = Ui.LoadWidget("PLAYER_PROFILE_BADGES_INSERT", badgeContainer, new WidgetArgs()
|
||||||
|
{
|
||||||
|
{ "worldRenderer", worldRenderer },
|
||||||
|
{ "profile", localProfile.ProfileData },
|
||||||
|
{ "negotiateWidth", negotiateWidth }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (badges.Bounds.Height > 0)
|
||||||
|
{
|
||||||
|
badgeContainer.Bounds.Height = badges.Bounds.Height;
|
||||||
|
badgesVisible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ui.ResetTooltips();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,6 +141,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
|||||||
playerDatabase = modData.Manifest.Get<PlayerDatabase>();
|
playerDatabase = modData.Manifest.Get<PlayerDatabase>();
|
||||||
|
|
||||||
var header = widget.Get("HEADER");
|
var header = widget.Get("HEADER");
|
||||||
|
var badgeContainer = widget.Get("BADGES_CONTAINER");
|
||||||
|
var badgeSeparator = badgeContainer.GetOrNull("SEPARATOR");
|
||||||
|
|
||||||
var profileHeader = header.Get("PROFILE_HEADER");
|
var profileHeader = header.Get("PROFILE_HEADER");
|
||||||
var messageHeader = header.Get("MESSAGE_HEADER");
|
var messageHeader = header.Get("MESSAGE_HEADER");
|
||||||
@@ -145,6 +186,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
|||||||
profileWidth = Math.Max(profileWidth, rankFont.Measure(profile.ProfileRank).X + 2 * rankLabel.Bounds.Left);
|
profileWidth = Math.Max(profileWidth, rankFont.Measure(profile.ProfileRank).X + 2 * rankLabel.Bounds.Left);
|
||||||
|
|
||||||
header.Bounds.Height += headerSizeOffset;
|
header.Bounds.Height += headerSizeOffset;
|
||||||
|
badgeContainer.Bounds.Y += header.Bounds.Height;
|
||||||
if (client.IsAdmin)
|
if (client.IsAdmin)
|
||||||
{
|
{
|
||||||
profileWidth = Math.Max(profileWidth, adminFont.Measure(adminLabel.Text).X + 2 * adminLabel.Bounds.Left);
|
profileWidth = Math.Max(profileWidth, adminFont.Measure(adminLabel.Text).X + 2 * adminLabel.Bounds.Left);
|
||||||
@@ -152,11 +194,37 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
|||||||
adminContainer.IsVisible = () => true;
|
adminContainer.IsVisible = () => true;
|
||||||
profileHeader.Bounds.Height += adminLabel.Bounds.Height;
|
profileHeader.Bounds.Height += adminLabel.Bounds.Height;
|
||||||
header.Bounds.Height += adminLabel.Bounds.Height;
|
header.Bounds.Height += adminLabel.Bounds.Height;
|
||||||
|
badgeContainer.Bounds.Y += adminLabel.Bounds.Height;
|
||||||
|
}
|
||||||
|
|
||||||
|
Func<int, int> negotiateWidth = badgeWidth =>
|
||||||
|
{
|
||||||
|
profileWidth = Math.Min(Math.Max(badgeWidth, profileWidth), widget.Bounds.Width);
|
||||||
|
return profileWidth;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (profile.Badges.Any())
|
||||||
|
{
|
||||||
|
var badges = Ui.LoadWidget("PLAYER_PROFILE_BADGES_INSERT", badgeContainer, new WidgetArgs()
|
||||||
|
{
|
||||||
|
{ "worldRenderer", worldRenderer },
|
||||||
|
{ "profile", profile },
|
||||||
|
{ "negotiateWidth", negotiateWidth }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (badges.Bounds.Height > 0)
|
||||||
|
{
|
||||||
|
badgeContainer.Bounds.Height = badges.Bounds.Height;
|
||||||
|
badgeContainer.IsVisible = () => true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
profileWidth = Math.Min(profileWidth, widget.Bounds.Width);
|
profileWidth = Math.Min(profileWidth, widget.Bounds.Width);
|
||||||
header.Bounds.Width = widget.Bounds.Width = profileWidth;
|
header.Bounds.Width = widget.Bounds.Width = badgeContainer.Bounds.Width = profileWidth;
|
||||||
widget.Bounds.Height = header.Bounds.Height;
|
widget.Bounds.Height = header.Bounds.Height + badgeContainer.Bounds.Height;
|
||||||
|
|
||||||
|
if (badgeSeparator != null)
|
||||||
|
badgeSeparator.Bounds.Width = profileWidth - 2 * badgeSeparator.Bounds.X;
|
||||||
|
|
||||||
profileLoaded = true;
|
profileLoaded = true;
|
||||||
});
|
});
|
||||||
@@ -182,11 +250,53 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
|||||||
header.Bounds.Height += messageHeader.Bounds.Height;
|
header.Bounds.Height += messageHeader.Bounds.Height;
|
||||||
header.Bounds.Width = widget.Bounds.Width = messageWidth;
|
header.Bounds.Width = widget.Bounds.Width = messageWidth;
|
||||||
widget.Bounds.Height = header.Bounds.Height;
|
widget.Bounds.Height = header.Bounds.Height;
|
||||||
|
badgeContainer.Visible = false;
|
||||||
|
|
||||||
new Download(playerDatabase.Profile + client.Fingerprint, _ => { }, onQueryComplete);
|
new Download(playerDatabase.Profile + client.Fingerprint, _ => { }, onQueryComplete);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class PlayerProfileBadgesLogic : ChromeLogic
|
||||||
|
{
|
||||||
|
[ObjectCreator.UseCtor]
|
||||||
|
public PlayerProfileBadgesLogic(Widget widget, PlayerProfile profile, Func<int, int> negotiateWidth)
|
||||||
|
{
|
||||||
|
var showBadges = profile.Badges.Any();
|
||||||
|
widget.IsVisible = () => showBadges;
|
||||||
|
|
||||||
|
var badgeTemplate = widget.Get("BADGE_TEMPLATE");
|
||||||
|
widget.RemoveChild(badgeTemplate);
|
||||||
|
|
||||||
|
var width = 0;
|
||||||
|
var badgeOffset = badgeTemplate.Bounds.Y;
|
||||||
|
foreach (var badge in profile.Badges)
|
||||||
|
{
|
||||||
|
var b = badgeTemplate.Clone();
|
||||||
|
var icon = b.Get<SpriteWidget>("ICON");
|
||||||
|
icon.GetSprite = () => badge.Icon24;
|
||||||
|
|
||||||
|
var label = b.Get<LabelWidget>("LABEL");
|
||||||
|
var labelFont = Game.Renderer.Fonts[label.Font];
|
||||||
|
|
||||||
|
var labelText = WidgetUtils.TruncateText(badge.Label, label.Bounds.Width, labelFont);
|
||||||
|
label.GetText = () => labelText;
|
||||||
|
|
||||||
|
width = Math.Max(width, label.Bounds.Left + labelFont.Measure(labelText).X + icon.Bounds.X);
|
||||||
|
|
||||||
|
b.Bounds.Y = badgeOffset;
|
||||||
|
widget.AddChild(b);
|
||||||
|
|
||||||
|
badgeOffset += badgeTemplate.Bounds.Height;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (badgeOffset > badgeTemplate.Bounds.Y)
|
||||||
|
badgeOffset += 5;
|
||||||
|
|
||||||
|
widget.Bounds.Width = negotiateWidth(width);
|
||||||
|
widget.Bounds.Height = badgeOffset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class AnonymousProfileTooltipLogic : ChromeLogic
|
public class AnonymousProfileTooltipLogic : ChromeLogic
|
||||||
{
|
{
|
||||||
[ObjectCreator.UseCtor]
|
[ObjectCreator.UseCtor]
|
||||||
|
|||||||
@@ -28,6 +28,11 @@ Container@LOCAL_PROFILE_PANEL:
|
|||||||
Font: TinyBold
|
Font: TinyBold
|
||||||
BaseLine: 1
|
BaseLine: 1
|
||||||
Text: Logout
|
Text: Logout
|
||||||
|
Background@BADGES_CONTAINER:
|
||||||
|
Width: PARENT_RIGHT
|
||||||
|
Y: 48
|
||||||
|
Visible: false
|
||||||
|
Background: panel-black
|
||||||
Background@GENERATE_KEYS:
|
Background@GENERATE_KEYS:
|
||||||
Width: PARENT_RIGHT
|
Width: PARENT_RIGHT
|
||||||
Height: PARENT_BOTTOM
|
Height: PARENT_BOTTOM
|
||||||
@@ -208,3 +213,22 @@ Container@LOCAL_PROFILE_PANEL:
|
|||||||
BaseLine: 1
|
BaseLine: 1
|
||||||
Font: TinyBold
|
Font: TinyBold
|
||||||
Text: Retry
|
Text: Retry
|
||||||
|
|
||||||
|
Container@PLAYER_PROFILE_BADGES_INSERT:
|
||||||
|
Logic: PlayerProfileBadgesLogic
|
||||||
|
Width: PARENT_RIGHT
|
||||||
|
Children:
|
||||||
|
Container@BADGE_TEMPLATE:
|
||||||
|
Width: PARENT_RIGHT
|
||||||
|
Height: 25
|
||||||
|
Children:
|
||||||
|
Sprite@ICON:
|
||||||
|
X: 6
|
||||||
|
Y: 1
|
||||||
|
Width: 24
|
||||||
|
Height: 24
|
||||||
|
Label@LABEL:
|
||||||
|
X: 36
|
||||||
|
Width: PARENT_RIGHT - 60
|
||||||
|
Height: 24
|
||||||
|
Font: Bold
|
||||||
|
|||||||
@@ -293,3 +293,8 @@ Container@REGISTERED_PLAYER_TOOLTIP:
|
|||||||
Width: PARENT_RIGHT - 20
|
Width: PARENT_RIGHT - 20
|
||||||
Height: 23
|
Height: 23
|
||||||
Font: Bold
|
Font: Bold
|
||||||
|
Background@BADGES_CONTAINER:
|
||||||
|
Width: PARENT_RIGHT
|
||||||
|
Y: 0-1
|
||||||
|
Visible: false
|
||||||
|
Background: panel-black
|
||||||
|
|||||||
@@ -28,6 +28,11 @@ Container@LOCAL_PROFILE_PANEL:
|
|||||||
Font: TinyBold
|
Font: TinyBold
|
||||||
BaseLine: 1
|
BaseLine: 1
|
||||||
Text: Logout
|
Text: Logout
|
||||||
|
Background@BADGES_CONTAINER:
|
||||||
|
Width: PARENT_RIGHT
|
||||||
|
Y: 48
|
||||||
|
Visible: false
|
||||||
|
Background: dialog3
|
||||||
Background@GENERATE_KEYS:
|
Background@GENERATE_KEYS:
|
||||||
Width: PARENT_RIGHT
|
Width: PARENT_RIGHT
|
||||||
Height: PARENT_BOTTOM
|
Height: PARENT_BOTTOM
|
||||||
@@ -208,3 +213,23 @@ Container@LOCAL_PROFILE_PANEL:
|
|||||||
BaseLine: 1
|
BaseLine: 1
|
||||||
Font: TinyBold
|
Font: TinyBold
|
||||||
Text: Retry
|
Text: Retry
|
||||||
|
|
||||||
|
Container@PLAYER_PROFILE_BADGES_INSERT:
|
||||||
|
Logic: PlayerProfileBadgesLogic
|
||||||
|
Width: PARENT_RIGHT
|
||||||
|
Height: 110
|
||||||
|
Children:
|
||||||
|
Container@BADGE_TEMPLATE:
|
||||||
|
Width: PARENT_RIGHT
|
||||||
|
Height: 25
|
||||||
|
Children:
|
||||||
|
Sprite@ICON:
|
||||||
|
X: 6
|
||||||
|
Y: 1
|
||||||
|
Width: 24
|
||||||
|
Height: 24
|
||||||
|
Label@LABEL:
|
||||||
|
X: 36
|
||||||
|
Width: PARENT_RIGHT - 60
|
||||||
|
Height: 24
|
||||||
|
Font: Bold
|
||||||
|
|||||||
@@ -218,6 +218,14 @@ Background@REGISTERED_PLAYER_TOOLTIP:
|
|||||||
Width: PARENT_RIGHT - 14
|
Width: PARENT_RIGHT - 14
|
||||||
Height: 23
|
Height: 23
|
||||||
Font: Bold
|
Font: Bold
|
||||||
|
Container@BADGES_CONTAINER:
|
||||||
|
Width: PARENT_RIGHT
|
||||||
|
Visible: false
|
||||||
|
Children:
|
||||||
|
Background@SEPARATOR:
|
||||||
|
X: 10
|
||||||
|
Height: 1
|
||||||
|
Background: tooltip-separator
|
||||||
|
|
||||||
Background@PRODUCTION_TOOLTIP:
|
Background@PRODUCTION_TOOLTIP:
|
||||||
Logic: ProductionTooltipLogic
|
Logic: ProductionTooltipLogic
|
||||||
|
|||||||
@@ -192,6 +192,9 @@ dialog3: dialog.png
|
|||||||
corner-bl: 640,127,1,1
|
corner-bl: 640,127,1,1
|
||||||
corner-br: 767,127,1,1
|
corner-br: 767,127,1,1
|
||||||
|
|
||||||
|
tooltip-separator: dialog.png
|
||||||
|
border-t: 641,0,126,1
|
||||||
|
|
||||||
# Same as the half transparent frame used in the Asset Browser
|
# Same as the half transparent frame used in the Asset Browser
|
||||||
dialog4: dialog.png
|
dialog4: dialog.png
|
||||||
background: 517,392,54,54
|
background: 517,392,54,54
|
||||||
|
|||||||
@@ -221,6 +221,14 @@ Background@REGISTERED_PLAYER_TOOLTIP:
|
|||||||
Width: PARENT_RIGHT - 14
|
Width: PARENT_RIGHT - 14
|
||||||
Height: 23
|
Height: 23
|
||||||
Font: Bold
|
Font: Bold
|
||||||
|
Container@BADGES_CONTAINER:
|
||||||
|
Width: PARENT_RIGHT
|
||||||
|
Visible: false
|
||||||
|
Children:
|
||||||
|
Background@SEPARATOR:
|
||||||
|
X: 10
|
||||||
|
Height: 1
|
||||||
|
Background: tooltip-separator
|
||||||
|
|
||||||
Background@PRODUCTION_TOOLTIP:
|
Background@PRODUCTION_TOOLTIP:
|
||||||
Logic: ProductionTooltipLogic
|
Logic: ProductionTooltipLogic
|
||||||
|
|||||||
@@ -510,6 +510,9 @@ dialog4: dialog.png
|
|||||||
corner-bl: 512,446,6,6
|
corner-bl: 512,446,6,6
|
||||||
corner-br: 571,446,6,6
|
corner-br: 571,446,6,6
|
||||||
|
|
||||||
|
tooltip-separator: dialog.png
|
||||||
|
border-t: 517,387,54,1
|
||||||
|
|
||||||
# completely black tile
|
# completely black tile
|
||||||
dialog5: dialog.png
|
dialog5: dialog.png
|
||||||
background: 579,387,64,64
|
background: 579,387,64,64
|
||||||
|
|||||||
@@ -464,6 +464,9 @@ dialog4: dialog.png
|
|||||||
corner-bl: 512,446,6,6
|
corner-bl: 512,446,6,6
|
||||||
corner-br: 571,446,6,6
|
corner-br: 571,446,6,6
|
||||||
|
|
||||||
|
tooltip-separator: dialog.png
|
||||||
|
border-t: 517,387,54,1
|
||||||
|
|
||||||
# A copy of dialog3 (pressed button)
|
# A copy of dialog3 (pressed button)
|
||||||
progressbar-bg: dialog.png
|
progressbar-bg: dialog.png
|
||||||
background: 641,1,126,126
|
background: 641,1,126,126
|
||||||
|
|||||||
Reference in New Issue
Block a user