mirror of
https://github.com/D1GQ/BetterAmongUs.git
synced 2026-10-20 01:46:19 +00:00
340 lines
12 KiB
C#
340 lines
12 KiB
C#
using AmongUs.Data;
|
|
using AmongUs.GameOptions;
|
|
using BetterAmongUs.Data;
|
|
using BetterAmongUs.Helpers;
|
|
using BetterAmongUs.Items.Structs;
|
|
using BetterAmongUs.Modules;
|
|
using BetterAmongUs.Patches.Gameplay.UI.Settings;
|
|
using HarmonyLib;
|
|
using Il2CppInterop.Runtime.Attributes;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
using TMPro;
|
|
using UnityEngine;
|
|
|
|
namespace BetterAmongUs.Mono;
|
|
|
|
internal class PlayerInfoDisplay : MonoBehaviour
|
|
{
|
|
protected PlayerControl? _player;
|
|
protected TextMeshPro? _nameText;
|
|
protected TextMeshPro? _infoText;
|
|
protected TextMeshPro? _topText;
|
|
protected TextMeshPro? _bottomText;
|
|
|
|
private readonly StringBuilder _sbTag = new(256);
|
|
private readonly StringBuilder _sbTagTop = new(256);
|
|
private readonly StringBuilder _sbTagBottom = new(256);
|
|
private string _lastTopText = "", _lastBottomText = "", _lastInfoText = "";
|
|
private int _lastUpdateFrame;
|
|
private bool _lastPlayerState;
|
|
private const int UPDATE_COOLDOWN = 10;
|
|
|
|
// Cached regex patterns
|
|
private static readonly Regex _friendCodePattern = new(@"^[a-zA-Z0-9#]+$", RegexOptions.Compiled);
|
|
private static readonly Regex _hashtagPattern = new(@"^#[0-9]{4}$", RegexOptions.Compiled);
|
|
|
|
private CachedTranslations _cachedTranslations = new();
|
|
|
|
// Cached colors
|
|
private static readonly Dictionary<string, Color32> _cachedColors = new()
|
|
{
|
|
["#00f583"] = Utils.HexToColor32("#00f583"),
|
|
["#4f0000"] = Utils.HexToColor32("#4f0000"),
|
|
["#fc0000"] = Utils.HexToColor32("#fc0000"),
|
|
["#8731e7"] = Utils.HexToColor32("#8731e7")
|
|
};
|
|
|
|
// Cached translations
|
|
private class CachedTranslations
|
|
{
|
|
internal readonly string Loading = Translator.GetString("Player.Loading");
|
|
internal readonly string PlatformHidden = Translator.GetString("Player.PlatformHidden");
|
|
internal readonly string NoFriendCode = Translator.GetString("Player.NoFriendCode");
|
|
internal readonly string SickoUser = Translator.GetString("Player.SickoUser");
|
|
internal readonly string AUMUser = Translator.GetString("Player.AUMUser");
|
|
internal readonly string KNUser = Translator.GetString("Player.KNUser");
|
|
internal readonly string KnownCheater = Translator.GetString("Player.KnownCheater");
|
|
internal readonly string BetterUser = Translator.GetString("Player.BetterUser");
|
|
}
|
|
|
|
internal void Init(PlayerControl player)
|
|
{
|
|
_player = player;
|
|
|
|
var nameTextTransform = player.gameObject.transform.Find("Names/NameText_TMP");
|
|
_nameText = nameTextTransform?.GetComponent<TextMeshPro>();
|
|
|
|
_infoText = InstantiatePlayerInfoText("InfoText_Info_TMP", new Vector3(0f, 0.25f), nameTextTransform);
|
|
_topText = InstantiatePlayerInfoText("InfoText_T_TMP", new Vector3(0f, 0.15f), nameTextTransform);
|
|
_bottomText = InstantiatePlayerInfoText("InfoText_B_TMP", new Vector3(0f, -0.15f), nameTextTransform);
|
|
_infoText.fontSize = 1.3f;
|
|
_topText.fontSize = 1.3f;
|
|
_bottomText.fontSize = 1.3f;
|
|
}
|
|
|
|
protected TextMeshPro InstantiatePlayerInfoText(string name, Vector3 positionOffset, Transform parent)
|
|
{
|
|
var newTextObject = Instantiate(_nameText, parent);
|
|
newTextObject.name = name;
|
|
newTextObject.transform.DestroyChildren();
|
|
newTextObject.transform.position += positionOffset;
|
|
|
|
var textMesh = newTextObject.GetComponent<TextMeshPro>();
|
|
textMesh.text = string.Empty;
|
|
newTextObject.gameObject.SetActive(true);
|
|
|
|
return textMesh;
|
|
}
|
|
|
|
private void ResetText()
|
|
{
|
|
_infoText?.SetText(string.Empty);
|
|
_topText?.SetText(string.Empty);
|
|
_bottomText?.SetText(string.Empty);
|
|
}
|
|
|
|
protected virtual void LateUpdate()
|
|
{
|
|
if (Time.frameCount - _lastUpdateFrame < UPDATE_COOLDOWN)
|
|
return;
|
|
|
|
_sbTag.Clear();
|
|
_sbTagTop.Clear();
|
|
_sbTagBottom.Clear();
|
|
|
|
UpdatePlayerInfo();
|
|
UpdatePlayerHighlight();
|
|
UpdateColorBlindTextPosition();
|
|
_nameText.transform.parent.localPosition = new Vector3(0f, 0.8f, -0.5f);
|
|
|
|
_lastUpdateFrame = Time.frameCount;
|
|
}
|
|
|
|
private void UpdatePlayerInfo()
|
|
{
|
|
if (_player?.Data == null) return;
|
|
|
|
var betterData = _player.BetterData();
|
|
|
|
if (!_player.DataIsCollected())
|
|
{
|
|
_nameText.text = _cachedTranslations.Loading;
|
|
return;
|
|
}
|
|
|
|
if (!BAUPlugin.LobbyPlayerInfo.Value && GameState.IsLobby)
|
|
{
|
|
ResetText();
|
|
_player.RawSetName(_player.Data.PlayerName);
|
|
return;
|
|
}
|
|
|
|
string newName = _player.Data.PlayerName;
|
|
string hashPuid = Utils.GetHashPuid(_player);
|
|
string platform = Utils.GetPlatformName(_player, useTag: true);
|
|
|
|
string friendCode = ValidateFriendCode(out string friendCodeColor);
|
|
|
|
if (DataManager.Settings.Gameplay.StreamerMode)
|
|
{
|
|
platform = _cachedTranslations.PlatformHidden;
|
|
}
|
|
|
|
SetPlayerOutline(_sbTag);
|
|
|
|
if (GameState.IsInGame && GameState.IsLobby && !GameState.IsFreePlay)
|
|
{
|
|
SetLobbyInfo(ref newName, betterData, _sbTag);
|
|
_sbTagTop.Append($"<color=#9e9e9e>{platform}</color>+++")
|
|
.Append($"<color=#ffd829>Lv: {_player.Data.PlayerLevel + 1}</color>+++");
|
|
|
|
_sbTagBottom.Append($"<color={friendCodeColor}>{friendCode}</color>+++");
|
|
}
|
|
else if ((GameState.IsInGame || GameState.IsFreePlay) && !GameState.IsHideNSeek)
|
|
{
|
|
SetInGameInfo(_sbTagTop);
|
|
}
|
|
|
|
if (!_player.IsInShapeshift())
|
|
{
|
|
if (_player.IsImpostorTeammate())
|
|
newName = newName.ToColor(Colors.ImpostorRed);
|
|
_player.RawSetName(newName);
|
|
}
|
|
else
|
|
{
|
|
var targetData = Utils.PlayerDataFromPlayerId(_player.shapeshiftTargetPlayerId);
|
|
var name = targetData.BetterData()?.RealName ?? targetData.PlayerName;
|
|
if (_player.IsImpostorTeammate())
|
|
name = name.ToColor(Colors.ImpostorRed);
|
|
if (targetData != null) _player.RawSetName(name);
|
|
}
|
|
|
|
UpdateTextIfChanged(_topText, _sbTagTop, ref _lastTopText);
|
|
UpdateTextIfChanged(_bottomText, _sbTagBottom, ref _lastBottomText);
|
|
UpdateTextIfChanged(_infoText, _sbTag, ref _lastInfoText);
|
|
}
|
|
|
|
private static void UpdateTextIfChanged(TextMeshPro textMesh, StringBuilder sb, ref string lastValue)
|
|
{
|
|
if (textMesh == null) return;
|
|
|
|
string newText = Utils.FormatInfo(sb);
|
|
if (newText != lastValue)
|
|
{
|
|
textMesh?.SetText(newText);
|
|
lastValue = newText;
|
|
}
|
|
}
|
|
|
|
private string ValidateFriendCode(out string color)
|
|
{
|
|
color = "#FFFFFF";
|
|
if (_player?.Data == null) return string.Empty;
|
|
|
|
void TryKick()
|
|
{
|
|
if (GameState.IsHost && BetterGameSettings.InvalidFriendCode.GetBool())
|
|
{
|
|
string kickMessage = string.Format(Translator.GetString("AntiCheat.KickMessage"),
|
|
Translator.GetString("AntiCheat.ByAntiCheat"),
|
|
Translator.GetString("AntiCheat.Reason.InvalidFriendCode"));
|
|
_player.Kick(true, kickMessage, true);
|
|
}
|
|
}
|
|
|
|
string friendCode = _player.Data.FriendCode;
|
|
|
|
bool isValidFriendCode = !string.IsNullOrEmpty(friendCode) &&
|
|
_friendCodePattern.IsMatch(friendCode) &&
|
|
_hashtagPattern.IsMatch(friendCode) &&
|
|
!friendCode.Contains(' ') &&
|
|
Regex.Replace(friendCode, @"^#[0-9]{4}$", string.Empty).Length is > 10 or < 5;
|
|
|
|
color = isValidFriendCode ? "#ff0000" : "#00f7ff";
|
|
|
|
if (string.IsNullOrEmpty(friendCode))
|
|
{
|
|
friendCode = _cachedTranslations.NoFriendCode;
|
|
color = "#ff0000";
|
|
TryKick();
|
|
}
|
|
else if (!isValidFriendCode)
|
|
{
|
|
TryKick();
|
|
}
|
|
|
|
if (DataManager.Settings.Gameplay.StreamerMode)
|
|
{
|
|
friendCode = new string('*', friendCode.Length);
|
|
}
|
|
|
|
return friendCode.Trim();
|
|
}
|
|
|
|
[HideFromIl2Cpp]
|
|
private void SetPlayerOutline(StringBuilder sbTag)
|
|
{
|
|
if (_player?.Data == null) return;
|
|
|
|
string hashPuid = Utils.GetHashPuid(_player);
|
|
string friendCode = _player.Data.FriendCode;
|
|
|
|
var color = _player.cosmetics.currentBodySprite.BodySprite.material.GetColor("_OutlineColor");
|
|
|
|
if (ContainsPlayerData(BetterDataManager.BetterDataFile.SickoData, _player.Data))
|
|
{
|
|
sbTag.Append($"<color=#00f583>{_cachedTranslations.SickoUser}</color>+++");
|
|
_player.SetOutlineByHex(true, "#00f583");
|
|
}
|
|
else if (ContainsPlayerData(BetterDataManager.BetterDataFile.AUMData, _player.Data))
|
|
{
|
|
sbTag.Append($"<color=#4f0000>{_cachedTranslations.AUMUser}</color>+++");
|
|
_player.SetOutlineByHex(true, "#4f0000");
|
|
}
|
|
else if (ContainsPlayerData(BetterDataManager.BetterDataFile.KNData, _player.Data))
|
|
{
|
|
sbTag.Append($"<color=#8731e7>{_cachedTranslations.KNUser}</color>+++");
|
|
_player.SetOutlineByHex(true, "#8731e7");
|
|
}
|
|
else if (ContainsPlayerData(BetterDataManager.BetterDataFile.CheatData, _player.Data))
|
|
{
|
|
sbTag.Append($"<color=#fc0000>{_cachedTranslations.KnownCheater}</color>+++");
|
|
_player.SetOutlineByHex(true, "#fc0000");
|
|
}
|
|
else if (_cachedColors.Any(kvp => color == kvp.Value))
|
|
{
|
|
_player.SetOutline(false, null);
|
|
}
|
|
}
|
|
|
|
[HideFromIl2Cpp]
|
|
private static bool ContainsPlayerData(HashSet<UserInfo> dataList, NetworkedPlayerInfo playerData)
|
|
{
|
|
foreach (var info in dataList)
|
|
{
|
|
if (info.CheckPlayerData(playerData))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
[HideFromIl2Cpp]
|
|
private void SetLobbyInfo(ref string newName, ExtendedPlayerInfo betterData, StringBuilder sbTag)
|
|
{
|
|
if (betterData == null) return;
|
|
|
|
if (_player.IsHost() && BAUPlugin.LobbyPlayerInfo.Value)
|
|
newName = _player.GetPlayerNameAndColor();
|
|
|
|
if ((_player.IsLocalPlayer() || betterData.IsBetterUser) && !GameState.IsInGamePlay)
|
|
{
|
|
string verificationSymbol = betterData.IsVerifiedBetterUser || _player.IsLocalPlayer() ? "✓ " : "";
|
|
sbTag.AppendFormat("<color=#0dff00>{1}{0}</color>+++",
|
|
_cachedTranslations.BetterUser, verificationSymbol);
|
|
}
|
|
sbTag.Append($"<color=#b554ff>ID: {_player.PlayerId}</color>+++");
|
|
}
|
|
|
|
[HideFromIl2Cpp]
|
|
private void SetInGameInfo(StringBuilder sbTagTop)
|
|
{
|
|
if (_player.IsImpostorTeammate() || _player.IsLocalPlayer() ||
|
|
!PlayerControl.LocalPlayer.IsAlive() && !PlayerControl.LocalPlayer.Is(RoleTypes.GuardianAngel))
|
|
{
|
|
string roleInfo = _player.GetRoleName().ToColor(_player.Data.Role.TeamColor);
|
|
|
|
if (!_player.IsImpostorTeam() && _player.myTasks.Count > 0)
|
|
{
|
|
int completedTasks = 0;
|
|
foreach (var task in _player.Data.Tasks)
|
|
{
|
|
if (task.Complete) completedTasks++;
|
|
}
|
|
roleInfo += $" <color=#cbcbcb>({completedTasks}/{_player.Data.Tasks.Count})</color>";
|
|
}
|
|
|
|
sbTagTop.Append(roleInfo + "+++");
|
|
}
|
|
}
|
|
|
|
private void UpdatePlayerHighlight()
|
|
{
|
|
SetPlayerOutline(new StringBuilder(32));
|
|
}
|
|
|
|
private void UpdateColorBlindTextPosition()
|
|
{
|
|
var text = _player.cosmetics.colorBlindText;
|
|
if (!text.enabled) return;
|
|
if (!_player.onLadder && !_player.MyPhysics.Animations.IsPlayingAnyLadderAnimation())
|
|
{
|
|
text.transform.localPosition = new Vector3(0f, -1.3f, 0.4999f);
|
|
}
|
|
else
|
|
{
|
|
text.transform.localPosition = new Vector3(0f, -1.5f, 0.4999f);
|
|
}
|
|
}
|
|
} |