287 lines
10 KiB
C#
287 lines
10 KiB
C#
using System;
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
using Vanguard.App;
|
|
using Vanguard.State;
|
|
|
|
namespace Vanguard.UI
|
|
{
|
|
/// <summary>
|
|
/// Верхняя панель игрока, общая для Lobby и DeckBuilder: аватар-инициалы,
|
|
/// ник, MMR, сезон, Rating/Community и гамбургер-меню.
|
|
/// </summary>
|
|
public sealed class PlayerTopBar : IDisposable
|
|
{
|
|
public const string RootName = "PlayerTopBar";
|
|
|
|
private const float Margin = 24f;
|
|
private const float Height = 96f;
|
|
|
|
private static readonly Color Border = new Color(0.18f, 0.49f, 1f, 0.55f);
|
|
private static readonly Color BorderStrong = new Color(0.34f, 0.72f, 1f, 0.9f);
|
|
private static readonly Color TextPrimary = new Color(0.88f, 0.93f, 1f);
|
|
private static readonly Color TextMuted = new Color(0.58f, 0.69f, 0.88f);
|
|
private static readonly Color TextDim = new Color(0.44f, 0.55f, 0.75f);
|
|
private static readonly Color Accent = new Color(0.37f, 0.69f, 1f);
|
|
|
|
private readonly PlayerState _player;
|
|
private readonly HamburgerMenu _menu;
|
|
private readonly RectTransform _root;
|
|
private Text _avatar;
|
|
private Text _playerName;
|
|
private Text _mmr;
|
|
private bool _disposed;
|
|
|
|
public PlayerTopBar(
|
|
Transform parent,
|
|
AppConfig config,
|
|
PlayerState player,
|
|
Action onLoggedOut,
|
|
Action onHistory,
|
|
Action onRating,
|
|
Action onCommunity,
|
|
Action onLobby = null,
|
|
Transform overlayParent = null)
|
|
{
|
|
_player = player;
|
|
|
|
var top = Surface(parent, RootName, new Color(0.012f, 0.03f, 0.08f, 0.94f), 0);
|
|
_root = top;
|
|
top.anchorMin = new Vector2(0f, 1f);
|
|
top.anchorMax = new Vector2(1f, 1f);
|
|
top.pivot = new Vector2(0.5f, 1f);
|
|
top.offsetMin = new Vector2(Margin, -Height);
|
|
top.offsetMax = new Vector2(-Margin, 0f);
|
|
|
|
var avatarRect = Surface(
|
|
top,
|
|
"Avatar",
|
|
new Color(0.05f, 0.13f, 0.3f, 0.95f),
|
|
14);
|
|
avatarRect.anchorMin = new Vector2(0f, 0.5f);
|
|
avatarRect.anchorMax = new Vector2(0f, 0.5f);
|
|
avatarRect.pivot = new Vector2(0f, 0.5f);
|
|
avatarRect.sizeDelta = new Vector2(64f, 64f);
|
|
avatarRect.anchoredPosition = new Vector2(20f, 0f);
|
|
|
|
_avatar = UiKit.CreateText(avatarRect, "Initials", "?", 26, TextPrimary);
|
|
_avatar.fontStyle = FontStyle.Bold;
|
|
UiKit.Stretch(_avatar.rectTransform);
|
|
UiKit.MakeLiteral(_avatar);
|
|
|
|
_playerName = UiKit.CreateText(
|
|
top,
|
|
"PlayerName",
|
|
"…",
|
|
22,
|
|
TextPrimary,
|
|
TextAnchor.MiddleLeft);
|
|
_playerName.fontStyle = FontStyle.Bold;
|
|
var nameRect = _playerName.rectTransform;
|
|
nameRect.anchorMin = new Vector2(0f, 0.5f);
|
|
nameRect.anchorMax = new Vector2(0f, 0.5f);
|
|
nameRect.pivot = new Vector2(0f, 0.5f);
|
|
nameRect.sizeDelta = new Vector2(420f, 30f);
|
|
nameRect.anchoredPosition = new Vector2(104f, 14f);
|
|
UiKit.MakeLiteral(_playerName);
|
|
|
|
_mmr = UiKit.CreateText(
|
|
top,
|
|
"Mmr",
|
|
string.Empty,
|
|
14,
|
|
TextMuted,
|
|
TextAnchor.MiddleLeft);
|
|
var mmrRect = _mmr.rectTransform;
|
|
mmrRect.anchorMin = new Vector2(0f, 0.5f);
|
|
mmrRect.anchorMax = new Vector2(0f, 0.5f);
|
|
mmrRect.pivot = new Vector2(0f, 0.5f);
|
|
mmrRect.sizeDelta = new Vector2(460f, 22f);
|
|
mmrRect.anchoredPosition = new Vector2(104f, -16f);
|
|
UiKit.MakeLiteral(_mmr);
|
|
|
|
var season = UiKit.CreateText(
|
|
top,
|
|
"Season",
|
|
"Сезон — скоро",
|
|
14,
|
|
TextDim,
|
|
TextAnchor.MiddleCenter);
|
|
var seasonRect = season.rectTransform;
|
|
seasonRect.anchorMin = new Vector2(0.5f, 0.5f);
|
|
seasonRect.anchorMax = new Vector2(0.5f, 0.5f);
|
|
seasonRect.sizeDelta = new Vector2(280f, 30f);
|
|
seasonRect.anchoredPosition = Vector2.zero;
|
|
|
|
AddAction(top, "Rating", "Рейтинг", -312f, 180f, onRating, LobbyIcon.Chart);
|
|
AddAction(top, "Community", "Сообщество", -96f, 200f, onCommunity, LobbyIcon.Friends);
|
|
|
|
_menu = new HamburgerMenu(
|
|
top,
|
|
config,
|
|
onLoggedOut,
|
|
onHistory,
|
|
onLobby,
|
|
"Меню",
|
|
overlayParent);
|
|
|
|
_player.Changed += UpdateTexts;
|
|
Loc.LanguageChanged += UpdateTexts;
|
|
UpdateTexts();
|
|
}
|
|
|
|
public bool IsMenuOpen => _menu != null && _menu.IsOpen;
|
|
|
|
public RectTransform Root => _root;
|
|
|
|
public void CloseMenu()
|
|
{
|
|
if (_menu != null)
|
|
_menu.Close();
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (_disposed)
|
|
return;
|
|
|
|
_disposed = true;
|
|
_player.Changed -= UpdateTexts;
|
|
Loc.LanguageChanged -= UpdateTexts;
|
|
}
|
|
|
|
private void UpdateTexts()
|
|
{
|
|
if (_disposed)
|
|
return;
|
|
|
|
var name = string.IsNullOrEmpty(_player.UserName) ? "…" : _player.UserName;
|
|
_playerName.text = name;
|
|
_avatar.text = Initial(name);
|
|
_mmr.text = "MMR " + _player.RankedMmr
|
|
+ " · " + Loc.Translate("матчей") + " " + _player.Matches
|
|
+ " · " + Loc.Translate("побед") + " " + WinsPercent();
|
|
}
|
|
|
|
private string WinsPercent()
|
|
{
|
|
if (_player.Matches <= 0)
|
|
return "0%";
|
|
|
|
return Mathf.RoundToInt(_player.Wins * 100f / _player.Matches) + "%";
|
|
}
|
|
|
|
private static string Initial(string name)
|
|
{
|
|
foreach (var character in name)
|
|
{
|
|
if (char.IsLetterOrDigit(character))
|
|
return char.ToUpperInvariant(character).ToString();
|
|
}
|
|
|
|
return "?";
|
|
}
|
|
|
|
private static void AddAction(
|
|
RectTransform parent,
|
|
string name,
|
|
string label,
|
|
float rightOffset,
|
|
float width,
|
|
Action onClick,
|
|
Action<RectTransform, Color> icon)
|
|
{
|
|
var buttonRect = Surface(parent, name, new Color(0.035f, 0.09f, 0.21f, 0.85f), 12);
|
|
buttonRect.anchorMin = new Vector2(1f, 0.5f);
|
|
buttonRect.anchorMax = new Vector2(1f, 0.5f);
|
|
buttonRect.pivot = new Vector2(1f, 0.5f);
|
|
buttonRect.sizeDelta = new Vector2(width, 52f);
|
|
buttonRect.anchoredPosition = new Vector2(rightOffset, 0f);
|
|
|
|
var image = buttonRect.GetComponent<Image>();
|
|
image.raycastTarget = true;
|
|
var button = buttonRect.gameObject.AddComponent<Button>();
|
|
button.targetGraphic = image;
|
|
button.transition = Selectable.Transition.ColorTint;
|
|
button.colors = HoverColors();
|
|
if (onClick != null)
|
|
button.onClick.AddListener(() => onClick());
|
|
else
|
|
button.interactable = false;
|
|
|
|
var iconColor = onClick != null
|
|
? Accent
|
|
: new Color(Accent.r, Accent.g, Accent.b, 0.35f);
|
|
var iconRect = Icon(buttonRect, new Vector2(30f, 30f), new Vector2(-width * 0.5f + 30f, 0f), 0.6f);
|
|
icon(iconRect, iconColor);
|
|
|
|
var text = UiKit.CreateText(
|
|
buttonRect,
|
|
"Label",
|
|
label,
|
|
17,
|
|
onClick != null ? TextPrimary : new Color(TextPrimary.r, TextPrimary.g, TextPrimary.b, 0.4f),
|
|
TextAnchor.MiddleLeft);
|
|
var textRect = text.rectTransform;
|
|
textRect.anchorMin = new Vector2(0f, 0f);
|
|
textRect.anchorMax = new Vector2(1f, 1f);
|
|
textRect.offsetMin = new Vector2(52f, 0f);
|
|
textRect.offsetMax = new Vector2(-14f, 0f);
|
|
}
|
|
|
|
private static RectTransform Surface(
|
|
Transform parent,
|
|
string name,
|
|
Color fill,
|
|
int radius)
|
|
{
|
|
var go = new GameObject(name, typeof(RectTransform), typeof(Image));
|
|
var rect = (RectTransform)go.transform;
|
|
rect.SetParent(parent, false);
|
|
|
|
var image = go.GetComponent<Image>();
|
|
image.sprite = NeonUi.BodySliced(
|
|
radius,
|
|
fill,
|
|
radius > 0 ? Border : new Color(Border.r, Border.g, Border.b, 0.35f),
|
|
1);
|
|
image.type = Image.Type.Sliced;
|
|
image.color = Color.white;
|
|
image.raycastTarget = false;
|
|
return rect;
|
|
}
|
|
|
|
private static RectTransform Icon(
|
|
Transform parent,
|
|
Vector2 size,
|
|
Vector2 position,
|
|
float scale)
|
|
{
|
|
var go = new GameObject("Icon", typeof(RectTransform));
|
|
var rect = (RectTransform)go.transform;
|
|
rect.SetParent(parent, false);
|
|
rect.anchorMin = new Vector2(0.5f, 0.5f);
|
|
rect.anchorMax = new Vector2(0.5f, 0.5f);
|
|
rect.pivot = new Vector2(0.5f, 0.5f);
|
|
rect.sizeDelta = size;
|
|
rect.anchoredPosition = position;
|
|
rect.localScale = Vector3.one * scale;
|
|
return rect;
|
|
}
|
|
|
|
private static ColorBlock HoverColors()
|
|
{
|
|
return new ColorBlock
|
|
{
|
|
normalColor = Color.white,
|
|
highlightedColor = new Color(1.16f, 1.16f, 1.16f, 1f),
|
|
pressedColor = new Color(0.9f, 0.9f, 0.9f, 1f),
|
|
selectedColor = Color.white,
|
|
disabledColor = new Color(0.55f, 0.55f, 0.55f, 0.6f),
|
|
colorMultiplier = 1f,
|
|
fadeDuration = 0.08f,
|
|
};
|
|
}
|
|
}
|
|
}
|