294 lines
10 KiB
C#
294 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);
|
||
if (onLobby != null)
|
||
AddAction(top, "ToLobby", "← В лобби", -508f, 160f, onLobby, null);
|
||
|
||
_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 textLeft = 18f;
|
||
if (icon != null)
|
||
{
|
||
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);
|
||
textLeft = 52f;
|
||
}
|
||
|
||
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(textLeft, 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,
|
||
};
|
||
}
|
||
}
|
||
}
|