VanguardReMaster/client/Assets/Vanguard/Runtime/UI/PlayerTopBar.cs

275 lines
9.8 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 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)
{
_player = player;
var top = Surface(parent, RootName, new Color(0.012f, 0.03f, 0.08f, 0.94f), 0);
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(parent, config, onLoggedOut, onHistory, onLobby);
_player.Changed += UpdateTexts;
Loc.LanguageChanged += UpdateTexts;
UpdateTexts();
}
public bool IsMenuOpen => _menu != null && _menu.IsOpen;
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,
};
}
}
}