Decompiled source of HeroStats v1.0.1

HeroStats.dll

Decompiled 2 hours ago
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using RoR2;
using RoR2.Stats;
using RoR2.UI;
using TMPro;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace HeroStats;

[BepInPlugin("com.proigryfedorov.herostats", "HeroStats", "1.0.1")]
public class HeroStatsPlugin : BaseUnityPlugin
{
	internal enum PanelAnchor
	{
		Coins,
		Objectives,
		Screen
	}

	public const string PluginGuid = "com.proigryfedorov.herostats";

	public const string PluginName = "HeroStats";

	public const string PluginVersion = "1.0.1";

	internal static HeroStatsPlugin Instance;

	internal static ConfigEntry<bool> ShowPanel;

	internal static ConfigEntry<KeyCode> ToggleKey;

	internal static ConfigEntry<bool> English;

	internal static ConfigEntry<float> PositionX;

	internal static ConfigEntry<float> PositionY;

	internal static ConfigEntry<PanelAnchor> Anchor;

	internal static ConfigEntry<float> OffsetX;

	internal static ConfigEntry<float> OffsetY;

	internal static ConfigEntry<float> FontSize;

	internal static ConfigEntry<float> DpsWindow;

	internal static ConfigEntry<bool> IncludeMinions;

	internal static ConfigEntry<bool> StackTotals;

	private static readonly string[,] LineSettings = new string[17, 2]
	{
		{ "Title", "Заголовок «Статистика»" },
		{ "Damage", "Урон" },
		{ "AttackSpeed", "Скорость атаки" },
		{ "Crit", "Шанс и множитель крита" },
		{ "Bleed", "Шанс кровотечения (только если больше 0)" },
		{ "Health", "Здоровье и щит" },
		{ "Regen", "Реген" },
		{ "Armor", "Броня и снижение урона" },
		{ "Speed", "Скорость передвижения" },
		{ "Jumps", "Прыжки" },
		{ "Level", "Уровень" },
		{ "Gold", "Золото (вместе с банком)" },
		{ "Luck", "Удача (только если не 0)" },
		{ "Kills", "Убийства" },
		{ "Dps", "DPS" },
		{ "MaxHit", "Максимальный урон одним ударом" },
		{ "ItemCounts", "Общее количество предметов" }
	};

	private static readonly Dictionary<string, ConfigEntry<bool>> lines = new Dictionary<string, ConfigEntry<bool>>();

	private readonly Queue<KeyValuePair<float, float>> recentDamage = new Queue<KeyValuePair<float, float>>();

	private float recentDamageSum;

	internal float maxHit;

	private static readonly Dictionary<string, StackMode> NonLinearItems = new Dictionary<string, StackMode>
	{
		{
			"Bear",
			StackMode.Hyperbolic
		},
		{
			"ExecuteLowHealthElite",
			StackMode.Hyperbolic
		},
		{
			"StunChanceOnHit",
			StackMode.Hyperbolic
		},
		{
			"AlienHead",
			StackMode.Exponential
		},
		{
			"EquipmentMagazine",
			StackMode.Exponential
		},
		{
			"AutoCastEquipment",
			StackMode.Exponential
		},
		{
			"HalfAttackSpeedHalfCooldowns",
			StackMode.Exponential
		},
		{
			"LunarDagger",
			StackMode.Compound
		}
	};

	internal static bool Shown(string key)
	{
		if (lines.TryGetValue(key, out var value))
		{
			return value.Value;
		}
		return true;
	}

	private void Awake()
	{
		//IL_0279: Unknown result type (might be due to invalid IL or missing references)
		//IL_027f: Expected O, but got Unknown
		//IL_02a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b4: Expected O, but got Unknown
		//IL_02f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_02fe: Expected O, but got Unknown
		//IL_0325: Unknown result type (might be due to invalid IL or missing references)
		//IL_0332: Expected O, but got Unknown
		Instance = this;
		ShowPanel = ((BaseUnityPlugin)this).Config.Bind<bool>("Panel", "Show", true, "Показывать панель статистики.");
		ToggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Panel", "ToggleKey", (KeyCode)283, "Клавиша, которая показывает/прячет панель.");
		English = ((BaseUnityPlugin)this).Config.Bind<bool>("Panel", "English", false, "Подписи на английском вместо русского.");
		PositionX = ((BaseUnityPlugin)this).Config.Bind<float>("Panel", "PositionX", 0.01f, "При Anchor = Screen: положение верхнего левого угла панели по горизонтали (0 — левый край, 1 — правый).");
		PositionY = ((BaseUnityPlugin)this).Config.Bind<float>("Panel", "PositionY", 0.62f, "При Anchor = Screen: положение верхнего левого угла по вертикали (0 — низ, 1 — верх).");
		FontSize = ((BaseUnityPlugin)this).Config.Bind<float>("Panel", "FontSize", 18f, "Размер шрифта панели.");
		DpsWindow = ((BaseUnityPlugin)this).Config.Bind<float>("Stats", "DpsWindow", 5f, "За сколько последних секунд считать DPS.");
		IncludeMinions = ((BaseUnityPlugin)this).Config.Bind<bool>("Stats", "IncludeMinions", false, "Учитывать урон дронов и других миньонов в DPS и максимальном ударе.");
		for (int i = 0; i < LineSettings.GetLength(0); i++)
		{
			lines[LineSettings[i, 0]] = ((BaseUnityPlugin)this).Config.Bind<bool>("Lines", LineSettings[i, 0], true, "Показывать в панели: " + LineSettings[i, 1] + ".");
		}
		lines["ItemTiers"] = ((BaseUnityPlugin)this).Config.Bind<bool>("Lines", "ItemTiers", false, "Показывать в панели количество предметов по каждой редкости (подробно).");
		Anchor = ((BaseUnityPlugin)this).Config.Bind<PanelAnchor>("Panel", "Anchor", PanelAnchor.Coins, "К чему привязана панель: Coins — слева под золотом и лунными монетами; Objectives — справа под списком целей (зарядка телепорта и т.д.); Screen — по PositionX/PositionY.");
		OffsetX = ((BaseUnityPlugin)this).Config.Bind<float>("Panel", "OffsetX", 0f, "Сдвиг панели от точки привязки вправо (отрицательное — влево).");
		OffsetY = ((BaseUnityPlugin)this).Config.Bind<float>("Panel", "OffsetY", 6f, "Сдвиг панели от точки привязки вниз (отрицательное — вверх).");
		StackTotals = ((BaseUnityPlugin)this).Config.Bind<bool>("Tooltips", "StackTotals", true, "Показывать в подсказке полное описание с итоговыми числами за все стаки предмета.");
		Run.onRunStartGlobal += delegate
		{
			ResetRunStats();
			GoldBank.Reset();
		};
		Stage.onStageStartGlobal += delegate
		{
			GoldBank.RegisterClientHandler();
		};
		GlobalEventManager.onClientDamageNotified += OnClientDamageNotified;
		Harmony val = new Harmony("com.proigryfedorov.herostats");
		val.Patch((MethodBase)AccessTools.Method(typeof(HUD), "Awake", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(HeroStatsPlugin), "HudAwakePostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		val.Patch((MethodBase)AccessTools.Method(typeof(ItemIcon), "SetItemIndex", new Type[1] { typeof(SetItemIndexArgs) }, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(HeroStatsPlugin), "ItemIconPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		val.Patch((MethodBase)AccessTools.PropertyGetter(typeof(TooltipProvider), "bodyText"), (HarmonyMethod)null, new HarmonyMethod(typeof(HeroStatsPlugin), "BodyTextPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		GoldBank.Patch(val);
		((BaseUnityPlugin)this).Logger.LogInfo((object)"HeroStats 1.0.1 loaded");
	}

	private void ResetRunStats()
	{
		recentDamage.Clear();
		recentDamageSum = 0f;
		maxHit = 0f;
	}

	private void Update()
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		if (Input.GetKeyDown(ToggleKey.Value))
		{
			ShowPanel.Value = !ShowPanel.Value;
		}
	}

	private void OnClientDamageNotified(DamageDealtMessage msg)
	{
		if (msg == null || (Object)(object)msg.attacker == (Object)null || msg.damage <= 0f)
		{
			return;
		}
		CharacterMaster val = LocalMaster();
		if (!((Object)(object)val == (Object)null) && IsMine(msg.attacker, val))
		{
			float time = Time.time;
			recentDamage.Enqueue(new KeyValuePair<float, float>(time, msg.damage));
			recentDamageSum += msg.damage;
			if (msg.damage > maxHit)
			{
				maxHit = msg.damage;
			}
		}
	}

	private static bool IsMine(GameObject attacker, CharacterMaster local)
	{
		CharacterBody component = attacker.GetComponent<CharacterBody>();
		if ((Object)(object)component == (Object)null || (Object)(object)component.master == (Object)null)
		{
			return false;
		}
		if ((Object)(object)component.master == (Object)(object)local)
		{
			return true;
		}
		if (!IncludeMinions.Value)
		{
			return false;
		}
		MinionOwnership minionOwnership = component.master.minionOwnership;
		if ((Object)(object)minionOwnership != (Object)null)
		{
			return (Object)(object)minionOwnership.ownerMaster == (Object)(object)local;
		}
		return false;
	}

	internal float Dps()
	{
		float num = Mathf.Max(1f, DpsWindow.Value);
		float num2 = Time.time - num;
		while (recentDamage.Count > 0 && recentDamage.Peek().Key < num2)
		{
			recentDamageSum -= recentDamage.Dequeue().Value;
		}
		if (recentDamage.Count == 0)
		{
			recentDamageSum = 0f;
		}
		return recentDamageSum / num;
	}

	internal static CharacterMaster LocalMaster()
	{
		LocalUser firstLocalUser = LocalUserManager.GetFirstLocalUser();
		if (firstLocalUser == null)
		{
			return null;
		}
		return firstLocalUser.cachedMaster;
	}

	internal static string Num(double value)
	{
		return StackText.Short(value, English.Value);
	}

	internal static string L(string ru, string en)
	{
		if (!English.Value)
		{
			return ru;
		}
		return en;
	}

	internal static string TierName(ItemTier tier)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Expected I4, but got Unknown
		//IL_0125: Unknown result type (might be due to invalid IL or missing references)
		//IL_0192: Unknown result type (might be due to invalid IL or missing references)
		//IL_0198: Expected I4, but got Unknown
		switch ((int)tier)
		{
		case 0:
			return L("Обычные", "Common");
		case 1:
			return L("Необычные", "Uncommon");
		case 2:
			return L("Легендарные", "Legendary");
		case 4:
			return L("Босс", "Boss");
		case 3:
			return L("Лунные", "Lunar");
		case 6:
			return L("Пустота: обычные", "Void common");
		case 7:
			return L("Пустота: необычные", "Void uncommon");
		case 8:
			return L("Пустота: легендарные", "Void legendary");
		case 9:
			return L("Пустота: босс", "Void boss");
		case 10:
			return L("Еда", "Food");
		case 11:
			return L("Проклятые", "Cursed");
		case 12:
			return L("Страницы", "Pages");
		case 13:
			return L("Порча", "Hex");
		case 5:
			return L("Без редкости", "No tier");
		default:
		{
			ItemTierDef itemTierDef = ItemTierCatalog.GetItemTierDef(tier);
			string text = (((Object)(object)itemTierDef != (Object)null) ? ((Object)itemTierDef).name.Replace("TierDef", "").Replace("Def", "").Trim() : "");
			if (text.Length <= 0)
			{
				return L("Особые", "Other") + " (" + (int)tier + ")";
			}
			return text;
		}
		}
	}

	internal static Color TierColor(ItemTier tier)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		ItemTierDef itemTierDef = ItemTierCatalog.GetItemTierDef(tier);
		if (!((Object)(object)itemTierDef != (Object)null))
		{
			return Color.white;
		}
		return Color32.op_Implicit(ColorCatalog.GetColor(itemTierDef.colorIndex));
	}

	internal static string Hex(Color c)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		return ColorUtility.ToHtmlStringRGB(c);
	}

	private static void HudAwakePostfix(HUD __instance)
	{
		try
		{
			StatsPanel.Create(__instance);
		}
		catch (Exception ex)
		{
			if ((Object)(object)Instance != (Object)null)
			{
				((BaseUnityPlugin)Instance).Logger.LogError((object)("Не удалось создать панель статистики: " + ex));
			}
		}
	}

	private static void ItemIconPostfix(ItemIcon __instance, int ___itemCount)
	{
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		if (!StackTotals.Value || (Object)(object)__instance.tooltipProvider == (Object)null)
		{
			return;
		}
		ItemDef itemDef = ItemCatalog.GetItemDef(__instance.itemIndex);
		if (!((Object)(object)itemDef == (Object)null))
		{
			TooltipProvider tooltipProvider = __instance.tooltipProvider;
			TooltipMark tooltipMark = ((Component)tooltipProvider).GetComponent<TooltipMark>();
			if ((Object)(object)tooltipMark == (Object)null)
			{
				tooltipMark = ((Component)tooltipProvider).gameObject.AddComponent<TooltipMark>();
			}
			string text = ((tooltipMark.ours != null && tooltipProvider.overrideBodyText == tooltipMark.ours) ? null : tooltipProvider.bodyText);
			tooltipMark.baseText = text ?? tooltipMark.baseText ?? "";
			tooltipMark.item = itemDef;
			tooltipMark.count = ___itemCount;
			BuildTooltip(tooltipProvider, tooltipMark);
		}
	}

	private static float CurrentLuck()
	{
		CharacterMaster val = LocalMaster();
		if (!((Object)(object)val != (Object)null))
		{
			return 0f;
		}
		return val.luck;
	}

	private static void BuildTooltip(TooltipProvider tooltip, TooltipMark mark)
	{
		ItemDef item = mark.item;
		int count = mark.count;
		float num = CurrentLuck();
		StringBuilder stringBuilder = new StringBuilder(mark.baseText);
		string text;
		if (((Object)item).name == "LowerPricedChests")
		{
			text = StackText.SaleStar(count, num, English.Value);
		}
		else if (count > 1 || num != 0f)
		{
			if (!NonLinearItems.TryGetValue(((Object)item).name, out var value))
			{
				value = StackMode.Linear;
			}
			text = StackText.Totals(Language.GetString(item.descriptionToken), count, value, num, English.Value);
		}
		else
		{
			text = null;
		}
		if (text != null)
		{
			stringBuilder.Append("\n\n<b>").Append(L("Всего за ", "Total for ")).Append(count)
				.Append(L(" шт.", ""));
			if (num != 0f)
			{
				stringBuilder.Append(L(", удача ", ", luck ")).Append(num.ToString("+0;-0", CultureInfo.InvariantCulture));
			}
			stringBuilder.Append(":</b>\n").Append(text);
		}
		mark.luck = num;
		mark.ours = stringBuilder.ToString();
		tooltip.overrideBodyText = mark.ours;
	}

	private static void BodyTextPostfix(TooltipProvider __instance, ref string __result)
	{
		if (StackTotals.Value)
		{
			TooltipMark component = ((Component)__instance).GetComponent<TooltipMark>();
			if (!((Object)(object)component == (Object)null) && !((Object)(object)component.item == (Object)null) && !(__result != component.ours) && component.luck != CurrentLuck())
			{
				BuildTooltip(__instance, component);
				__result = component.ours;
			}
		}
	}
}
internal static class GoldBank
{
	private const uint MoneyCap = 2000000000u;

	private const uint RefillBelow = 1000000000u;

	private const short MsgBank = 18800;

	private static readonly Dictionary<CharacterMaster, double> bank = new Dictionary<CharacterMaster, double>();

	private static double clientBank;

	private static bool adjusting;

	public static void Patch(Harmony harmony)
	{
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_0016: Expected O, but got Unknown
		//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c2: Expected O, but got Unknown
		//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f8: Expected O, but got Unknown
		HarmonyMethod val = new HarmonyMethod(typeof(GoldBank), "GiveMoneyPrefix", (Type[])null);
		harmony.Patch((MethodBase)AccessTools.Method(typeof(CharacterMaster), "GiveMoney", new Type[2]
		{
			typeof(uint),
			typeof(bool)
		}, (Type[])null), val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		harmony.Patch((MethodBase)AccessTools.Method(typeof(CharacterMaster), "GiveMoneyWithOnLevelUpFreeUnlock", new Type[1] { typeof(uint) }, (Type[])null), val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		harmony.Patch((MethodBase)AccessTools.PropertySetter(typeof(CharacterMaster), "money"), (HarmonyMethod)null, new HarmonyMethod(typeof(GoldBank), "SetMoneyPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		harmony.Patch((MethodBase)AccessTools.Method(typeof(MoneyText), "Update", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(GoldBank), "MoneyTextPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
	}

	public static void Reset()
	{
		bank.Clear();
		clientBank = 0.0;
		RegisterClientHandler();
	}

	public static void RegisterClientHandler()
	{
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0035: Expected O, but got Unknown
		NetworkClient val = (((Object)(object)NetworkManager.singleton != (Object)null) ? NetworkManager.singleton.client : null);
		if (val != null)
		{
			val.RegisterHandler((short)18800, new NetworkMessageDelegate(OnClientBank));
		}
	}

	public static double Total(CharacterMaster master)
	{
		if ((Object)(object)master == (Object)null)
		{
			return 0.0;
		}
		double value = 0.0;
		if (NetworkServer.active)
		{
			bank.TryGetValue(master, out value);
		}
		else if ((Object)(object)master == (Object)(object)HeroStatsPlugin.LocalMaster())
		{
			value = clientBank;
		}
		return (double)master.money + value;
	}

	private static void GiveMoneyPrefix(CharacterMaster __instance, ref uint amount)
	{
		if (NetworkServer.active && !adjusting)
		{
			uint num = __instance.money;
			if (num > 2000000000)
			{
				AddBank(__instance, num - 2000000000);
				SetWallet(__instance, 2000000000u);
				num = 2000000000u;
			}
			ulong num2 = (ulong)num + (ulong)amount;
			if (num2 > 2000000000)
			{
				AddBank(__instance, num2 - 2000000000);
				amount = 2000000000 - num;
			}
		}
	}

	private static void SetMoneyPostfix(CharacterMaster __instance)
	{
		if (NetworkServer.active && !adjusting && bank.TryGetValue(__instance, out var value) && !(value < 1.0))
		{
			uint money = __instance.money;
			if (money < 1000000000)
			{
				uint num = (uint)Math.Min(value, 2000000000 - money);
				SetWallet(__instance, money + num);
				AddBank(__instance, 0.0 - (double)num);
			}
		}
	}

	private static void SetWallet(CharacterMaster master, uint value)
	{
		adjusting = true;
		try
		{
			master.money = value;
		}
		finally
		{
			adjusting = false;
		}
	}

	private static void AddBank(CharacterMaster master, double delta)
	{
		bank.TryGetValue(master, out var value);
		value = Math.Max(0.0, value + delta);
		bank[master] = value;
		SendBank(master, value);
	}

	private static void SendBank(CharacterMaster master, double value)
	{
		PlayerCharacterMasterController playerCharacterMasterController = master.playerCharacterMasterController;
		NetworkUser val = (((Object)(object)playerCharacterMasterController != (Object)null) ? playerCharacterMasterController.networkUser : null);
		if (!((Object)(object)val == (Object)null) && ((NetworkBehaviour)val).connectionToClient != null)
		{
			BankMessage bankMessage = new BankMessage();
			bankMessage.value = value;
			((NetworkBehaviour)val).connectionToClient.Send((short)18800, (MessageBase)(object)bankMessage);
		}
	}

	private static void OnClientBank(NetworkMessage netMsg)
	{
		clientBank = netMsg.ReadMessage<BankMessage>().value;
	}

	private static void MoneyTextPostfix(MoneyText __instance)
	{
		if ((Object)(object)__instance.targetText == (Object)null)
		{
			return;
		}
		foreach (HUD readOnlyInstance in HUD.readOnlyInstanceList)
		{
			if ((Object)(object)readOnlyInstance.moneyText != (Object)(object)__instance)
			{
				continue;
			}
			CharacterMaster targetMaster = readOnlyInstance.targetMaster;
			if (!((Object)(object)targetMaster == (Object)null))
			{
				string text = HeroStatsPlugin.Num(Total(targetMaster));
				if (((TMP_Text)__instance.targetText).text != text)
				{
					((TMP_Text)__instance.targetText).text = text;
				}
			}
			break;
		}
	}
}
internal class TooltipMark : MonoBehaviour
{
	public string baseText;

	public string ours;

	public ItemDef item;

	public int count;

	public float luck;
}
internal class BankMessage : MessageBase
{
	public double value;

	public override void Serialize(NetworkWriter writer)
	{
		writer.Write(value);
	}

	public override void Deserialize(NetworkReader reader)
	{
		value = reader.ReadDouble();
	}
}
internal class StatsPanel : MonoBehaviour
{
	private HUD hud;

	private RectTransform rect;

	private Image background;

	private TextMeshProUGUI text;

	private float nextRefresh;

	private static readonly FieldInfo LunarCoinField = AccessTools.Field(typeof(HUD), "lunarCoinContainer");

	private static readonly FieldInfo VoidCoinField = AccessTools.Field(typeof(HUD), "voidCoinContainer");

	private readonly Vector3[] corners = (Vector3[])(object)new Vector3[4];

	private ObjectivePanelController objectives;

	public static void Create(HUD hud)
	{
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0042: Expected O, but got Unknown
		Transform val = (((Object)(object)hud.mainContainer != (Object)null) ? hud.mainContainer.transform : ((Component)hud).transform);
		GameObject val2 = new GameObject("HeroStatsPanel", new Type[1] { typeof(RectTransform) });
		val2.transform.SetParent(val, false);
		StatsPanel statsPanel = val2.AddComponent<StatsPanel>();
		statsPanel.hud = hud;
		statsPanel.Build();
	}

	private void Place()
	{
		//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
		//IL_011d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0122: Unknown result type (might be due to invalid IL or missing references)
		//IL_0123: Unknown result type (might be due to invalid IL or missing references)
		//IL_012a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0137: Unknown result type (might be due to invalid IL or missing references)
		//IL_0048: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: Unknown result type (might be due to invalid IL or missing references)
		//IL_0065: Unknown result type (might be due to invalid IL or missing references)
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		//IL_006b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
		Vector2 val = default(Vector2);
		((Vector2)(ref val))..ctor(HeroStatsPlugin.OffsetX.Value, 0f - HeroStatsPlugin.OffsetY.Value);
		switch (HeroStatsPlugin.Anchor.Value)
		{
		case HeroStatsPlugin.PanelAnchor.Objectives:
		{
			if (ObjectivesBottomRight(out var point))
			{
				rect.pivot = new Vector2(1f, 1f);
				((Transform)rect).position = point;
				RectTransform obj2 = rect;
				obj2.anchoredPosition += val;
				return;
			}
			break;
		}
		case HeroStatsPlugin.PanelAnchor.Coins:
		{
			RectTransform val2 = LowestCurrency();
			if ((Object)(object)val2 != (Object)null)
			{
				val2.GetWorldCorners(corners);
				rect.pivot = new Vector2(0f, 1f);
				((Transform)rect).position = corners[0];
				RectTransform obj = rect;
				obj.anchoredPosition += val;
				return;
			}
			break;
		}
		}
		rect.pivot = new Vector2(0f, 1f);
		RectTransform obj3 = rect;
		Vector2 anchorMin = (rect.anchorMax = new Vector2(HeroStatsPlugin.PositionX.Value, HeroStatsPlugin.PositionY.Value));
		obj3.anchorMin = anchorMin;
		rect.anchoredPosition = val;
	}

	private bool ObjectivesBottomRight(out Vector3 point)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: Unknown result type (might be due to invalid IL or missing references)
		//IL_0142: Unknown result type (might be due to invalid IL or missing references)
		//IL_0147: Unknown result type (might be due to invalid IL or missing references)
		point = Vector3.zero;
		if ((Object)(object)objectives == (Object)null)
		{
			objectives = ((Component)hud).GetComponentInChildren<ObjectivePanelController>(true);
		}
		if ((Object)(object)objectives == (Object)null || !((Component)objectives).gameObject.activeInHierarchy)
		{
			return false;
		}
		((RectTransform)((Component)objectives).transform).GetWorldCorners(corners);
		float x = corners[3].x;
		float num = float.MaxValue;
		RectTransform objectiveTrackerContainer = objectives.objectiveTrackerContainer;
		if ((Object)(object)objectiveTrackerContainer != (Object)null)
		{
			for (int i = 0; i < ((Transform)objectiveTrackerContainer).childCount; i++)
			{
				Transform child = ((Transform)objectiveTrackerContainer).GetChild(i);
				RectTransform val = (RectTransform)(object)((child is RectTransform) ? child : null);
				if (!((Object)(object)val == (Object)null) && ((Component)val).gameObject.activeInHierarchy)
				{
					val.GetWorldCorners(corners);
					num = Mathf.Min(num, corners[0].y);
				}
			}
			if (num == float.MaxValue)
			{
				objectiveTrackerContainer.GetWorldCorners(corners);
				num = corners[0].y;
			}
		}
		else
		{
			num = corners[3].y;
		}
		point = new Vector3(x, num, corners[3].z);
		return true;
	}

	private RectTransform LowestCurrency()
	{
		RectTransform result = null;
		float num = float.MaxValue;
		GameObject[] array = (GameObject[])(object)new GameObject[3]
		{
			((Object)(object)hud.moneyText != (Object)null) ? ((Component)hud.moneyText).gameObject : null,
			(GameObject)((LunarCoinField != null) ? /*isinst with value type is only supported in some contexts*/: null),
			(GameObject)((VoidCoinField != null) ? /*isinst with value type is only supported in some contexts*/: null)
		};
		GameObject[] array2 = array;
		foreach (GameObject val in array2)
		{
			if ((Object)(object)val == (Object)null || !val.activeInHierarchy)
			{
				continue;
			}
			Transform transform = val.transform;
			RectTransform val2 = (RectTransform)(object)((transform is RectTransform) ? transform : null);
			if (!((Object)(object)val2 == (Object)null))
			{
				val2.GetWorldCorners(corners);
				if (corners[0].y < num)
				{
					num = corners[0].y;
					result = val2;
				}
			}
		}
		return result;
	}

	private void Build()
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Expected O, but got Unknown
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0056: Unknown result type (might be due to invalid IL or missing references)
		//IL_0080: Unknown result type (might be due to invalid IL or missing references)
		//IL_008a: Expected O, but got Unknown
		//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d5: Expected O, but got Unknown
		rect = (RectTransform)((Component)this).transform;
		rect.pivot = new Vector2(0f, 1f);
		background = ((Component)this).gameObject.AddComponent<Image>();
		((Graphic)background).color = new Color(0f, 0f, 0f, 0.45f);
		((Graphic)background).raycastTarget = false;
		VerticalLayoutGroup val = ((Component)this).gameObject.AddComponent<VerticalLayoutGroup>();
		((LayoutGroup)val).padding = new RectOffset(12, 14, 8, 10);
		((HorizontalOrVerticalLayoutGroup)val).childControlWidth = true;
		((HorizontalOrVerticalLayoutGroup)val).childControlHeight = true;
		ContentSizeFitter val2 = ((Component)this).gameObject.AddComponent<ContentSizeFitter>();
		val2.horizontalFit = (FitMode)2;
		val2.verticalFit = (FitMode)2;
		GameObject val3 = new GameObject("Text", new Type[1] { typeof(RectTransform) });
		val3.transform.SetParent(((Component)this).transform, false);
		text = val3.AddComponent<TextMeshProUGUI>();
		TextMeshProUGUI componentInChildren = ((Component)hud).GetComponentInChildren<TextMeshProUGUI>(true);
		if ((Object)(object)componentInChildren != (Object)null)
		{
			((TMP_Text)text).font = ((TMP_Text)componentInChildren).font;
		}
		((TMP_Text)text).richText = true;
		((Graphic)text).raycastTarget = false;
		((TMP_Text)text).enableWordWrapping = false;
		((TMP_Text)text).alignment = (TextAlignmentOptions)257;
	}

	private void Update()
	{
		bool flag = HeroStatsPlugin.ShowPanel.Value && (Object)(object)hud.targetBodyObject != (Object)null;
		if (((Component)text).gameObject.activeSelf != flag)
		{
			((Component)text).gameObject.SetActive(flag);
			((Behaviour)background).enabled = flag;
		}
		if (flag && !(Time.unscaledTime < nextRefresh))
		{
			nextRefresh = Time.unscaledTime + 0.25f;
			Place();
			((TMP_Text)text).fontSize = HeroStatsPlugin.FontSize.Value;
			CharacterBody component = hud.targetBodyObject.GetComponent<CharacterBody>();
			if ((Object)(object)component != (Object)null)
			{
				((TMP_Text)text).text = Compose(component);
				((Behaviour)background).enabled = ((TMP_Text)text).text.Length > 0;
			}
		}
	}

	private static void Line(StringBuilder sb, string key, string label, string color, string value)
	{
		if (HeroStatsPlugin.Shown(key))
		{
			sb.Append(label).Append(": <color=#").Append(color)
				.Append(">")
				.Append(value)
				.Append("</color>\n");
		}
	}

	private static string Compose(CharacterBody body)
	{
		HeroStatsPlugin instance = HeroStatsPlugin.Instance;
		StringBuilder stringBuilder = new StringBuilder();
		if (HeroStatsPlugin.Shown("Title"))
		{
			stringBuilder.Append("<b>").Append(HeroStatsPlugin.L("Статистика", "Stats")).Append("</b>\n");
		}
		Line(stringBuilder, "Damage", HeroStatsPlugin.L("Урон", "Damage"), "f2c46b", HeroStatsPlugin.Num(body.damage));
		Line(stringBuilder, "AttackSpeed", HeroStatsPlugin.L("Скорость атаки", "Attack speed"), "f2c46b", body.attackSpeed.ToString("0.##", CultureInfo.InvariantCulture));
		Line(stringBuilder, "Crit", HeroStatsPlugin.L("Шанс крита", "Crit chance"), "f2c46b", body.crit.ToString("0.#", CultureInfo.InvariantCulture) + "%  (x" + body.critMultiplier.ToString("0.##", CultureInfo.InvariantCulture) + ")");
		if (body.bleedChance > 0f)
		{
			Line(stringBuilder, "Bleed", HeroStatsPlugin.L("Кровотечение", "Bleed chance"), "e8473a", body.bleedChance.ToString("0.#", CultureInfo.InvariantCulture) + "%");
		}
		HealthComponent healthComponent = body.healthComponent;
		if ((Object)(object)healthComponent != (Object)null)
		{
			string text = HeroStatsPlugin.Num(healthComponent.health) + " / " + HeroStatsPlugin.Num(body.maxHealth);
			if (body.maxShield > 0f)
			{
				string text2 = text;
				text = text2 + "  (+" + HeroStatsPlugin.Num(healthComponent.shield) + HeroStatsPlugin.L(" щит", " shield") + ")";
			}
			Line(stringBuilder, "Health", HeroStatsPlugin.L("Здоровье", "Health"), "77d14a", text);
		}
		Line(stringBuilder, "Regen", HeroStatsPlugin.L("Реген", "Regen"), "77d14a", HeroStatsPlugin.Num(body.regen) + HeroStatsPlugin.L("/с", "/s"));
		float armor = body.armor;
		float num = ((armor >= 0f) ? (100f / (100f + armor)) : (2f - 100f / (100f - armor)));
		Line(stringBuilder, "Armor", HeroStatsPlugin.L("Броня", "Armor"), "77d14a", HeroStatsPlugin.Num(armor) + " | " + ((1f - num) * 100f).ToString("0.##", CultureInfo.InvariantCulture) + "%");
		Line(stringBuilder, "Speed", HeroStatsPlugin.L("Скорость", "Speed"), "6fb4e8", body.moveSpeed.ToString("0.##", CultureInfo.InvariantCulture) + HeroStatsPlugin.L(" м/с", " m/s"));
		if ((Object)(object)body.characterMotor != (Object)null)
		{
			int num2 = Mathf.Max(0, body.maxJumpCount - body.characterMotor.jumpCount);
			Line(stringBuilder, "Jumps", HeroStatsPlugin.L("Прыжки", "Jumps"), "6fb4e8", num2 + " / " + body.maxJumpCount);
		}
		Line(stringBuilder, "Level", HeroStatsPlugin.L("Уровень", "Level"), "dddddd", ((int)body.level).ToString());
		CharacterMaster master = body.master;
		if ((Object)(object)master != (Object)null)
		{
			Line(stringBuilder, "Gold", HeroStatsPlugin.L("Золото", "Gold"), "f2d24b", HeroStatsPlugin.Num(GoldBank.Total(master)));
			if (master.luck != 0f)
			{
				Line(stringBuilder, "Luck", HeroStatsPlugin.L("Удача", "Luck"), "6fe8c4", master.luck.ToString("0.##", CultureInfo.InvariantCulture));
			}
			ulong num3 = 0uL;
			if ((Object)(object)master.playerStatsComponent != (Object)null && master.playerStatsComponent.currentStats != null)
			{
				num3 = master.playerStatsComponent.currentStats.GetStatValueULong(StatDef.totalKills);
			}
			Line(stringBuilder, "Kills", HeroStatsPlugin.L("Убийства", "Kills"), "f2925a", HeroStatsPlugin.Num(num3));
		}
		Line(stringBuilder, "Dps", "DPS", "f2c46b", HeroStatsPlugin.Num(instance.Dps()));
		Line(stringBuilder, "MaxHit", HeroStatsPlugin.L("Макс. удар", "Max hit"), "ff6060", HeroStatsPlugin.Num(instance.maxHit));
		if ((HeroStatsPlugin.Shown("ItemCounts") || HeroStatsPlugin.Shown("ItemTiers")) && (Object)(object)master != (Object)null && (Object)(object)master.inventory != (Object)null)
		{
			AppendItemCounts(stringBuilder, master.inventory);
		}
		return stringBuilder.ToString().TrimEnd('\n');
	}

	private static void AppendItemCounts(StringBuilder sb, Inventory inv)
	{
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		//IL_007e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0083: Unknown result type (might be due to invalid IL or missing references)
		//IL_0084: Unknown result type (might be due to invalid IL or missing references)
		//IL_0088: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		//IL_006e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0127: Unknown result type (might be due to invalid IL or missing references)
		//IL_012c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0139: Unknown result type (might be due to invalid IL or missing references)
		//IL_013b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0154: Unknown result type (might be due to invalid IL or missing references)
		//IL_016b: Unknown result type (might be due to invalid IL or missing references)
		Dictionary<ItemTier, int> dictionary = new Dictionary<ItemTier, int>();
		List<ItemTier> list = new List<ItemTier>();
		int num = 0;
		foreach (ItemIndex item in inv.itemAcquisitionOrder)
		{
			ItemDef itemDef = ItemCatalog.GetItemDef(item);
			if ((Object)(object)itemDef == (Object)null || itemDef.hidden)
			{
				continue;
			}
			int itemCount = inv.GetItemCount(item);
			if (itemCount > 0)
			{
				if (!dictionary.ContainsKey(itemDef.tier))
				{
					dictionary[itemDef.tier] = 0;
					list.Add(itemDef.tier);
				}
				dictionary[itemDef.tier] += itemCount;
				num += itemCount;
			}
		}
		if (num == 0)
		{
			return;
		}
		list.Sort();
		if (HeroStatsPlugin.Shown("ItemCounts"))
		{
			sb.Append("<b>").Append(HeroStatsPlugin.L("Предметы", "Items")).Append(": ")
				.Append(HeroStatsPlugin.Num(num))
				.Append("</b>\n");
		}
		if (!HeroStatsPlugin.Shown("ItemTiers"))
		{
			return;
		}
		foreach (ItemTier item2 in list)
		{
			sb.Append("  <color=#").Append(HeroStatsPlugin.Hex(HeroStatsPlugin.TierColor(item2))).Append(">")
				.Append(HeroStatsPlugin.TierName(item2))
				.Append(": ")
				.Append(HeroStatsPlugin.Num(dictionary[item2]))
				.Append("</color>\n");
		}
	}
}
internal enum StackMode
{
	Linear,
	Hyperbolic,
	Exponential,
	Compound
}
internal static class StackText
{
	private static readonly Regex StackNote = new Regex("<style=cStack>(?<pre>[^(<]*)\\(\\s*(?<sign>[+\\-−])\\s*\\$?(?<per>\\d+(?:[.,]\\d+)?)(?<perPct>%?)[^)]*\\)(?<post>[^<]*)</style>", RegexOptions.Compiled);

	private static readonly Regex SentenceNote = new Regex("<style=cStack>(?<text>[^(<]*?)(?<num>\\d+(?:[.,]\\d+)?)%(?<rest>[^(<]*)</style>", RegexOptions.Compiled);

	private static readonly Regex NumberPattern = new Regex("[+\\-]?\\$?\\d+(?:[.,]\\d+)?%?", RegexOptions.Compiled);

	private static readonly Regex TagPattern = new Regex("<[^>]*>", RegexOptions.Compiled);

	private static readonly Regex DoubleDot = new Regex("\\.(</color>)\\.", RegexOptions.Compiled);

	private static readonly Regex IgnoresLuck = new Regex("Unaffected by luck|удачи не учитывается", RegexOptions.IgnoreCase | RegexOptions.Compiled);

	private static readonly Regex ChanceBefore = new Regex("(вероятност\\w*|шанс\\w*|chance)( (в|с|of|a|из))? ?$", RegexOptions.IgnoreCase | RegexOptions.Compiled);

	private static readonly Regex ChanceAfter = new Regex("^ ?(вероятност|шанс|chance)", RegexOptions.IgnoreCase | RegexOptions.Compiled);

	private static readonly Regex Spaces = new Regex("\\s+", RegexOptions.Compiled);

	private static readonly Regex PercentPattern = new Regex("\\d+(?:[.,]\\d+)?%", RegexOptions.Compiled);

	private static readonly Regex ReductionWord = new Regex("сниж|уменьш|reduc|decreas", RegexOptions.IgnoreCase | RegexOptions.Compiled);

	private static readonly string[] SuffixRu = new string[9] { "", " тыс.", " млн", " млрд", " трлн", " квадрлн", " квинтлн", " секстлн", " септлн" };

	private static readonly string[] SuffixEn = new string[9] { "", "K", "M", "B", "T", "Qa", "Qi", "Sx", "Sp" };

	public static string Totals(string description, int count, StackMode mode, float luck, bool english)
	{
		if (string.IsNullOrEmpty(description) || count < 1)
		{
			return null;
		}
		bool flag = luck != 0f && !IgnoresLuck.IsMatch(description);
		StringBuilder stringBuilder = new StringBuilder();
		int num = 0;
		bool found = false;
		foreach (Match item in StackNote.Matches(description))
		{
			if (!TryParse(item.Groups["per"].Value, out var value))
			{
				continue;
			}
			found = true;
			double sign = ((item.Groups["sign"].Value == "+") ? 1.0 : (-1.0));
			bool flag2 = item.Groups["perPct"].Value == "%";
			string text = description.Substring(num, item.Index - num);
			string text2 = TagPattern.Replace(text, (Match t) => new string(' ', t.Length));
			MatchCollection matchCollection = NumberPattern.Matches(text2);
			if (matchCollection.Count > 0)
			{
				Match match2 = matchCollection[matchCollection.Count - 1];
				string value2 = match2.Value;
				bool flag3 = value2.EndsWith("%");
				TryParse(value2.Trim('+', '-', '$', '%'), out var value3);
				if (value2.StartsWith("-"))
				{
					value3 = 0.0 - value3;
				}
				string text3 = text2.Substring(0, match2.Index);
				string input = text3.Substring(text3.LastIndexOf('.') + 1);
				bool flag4 = ReductionWord.IsMatch(input);
				double num2 = Total(value3, value, sign, count, flag3, flag2, mode, flag4);
				string text4 = text.Substring(0, match2.Index);
				stringBuilder.Append(flag ? AnnotateChances(text4, luck, english) : text4);
				stringBuilder.Append(text, match2.Index, text.Length - match2.Index);
				stringBuilder.Append(item.Value);
				stringBuilder.Append(" <color=#ffffff>= ");
				if (value2.StartsWith("+") && num2 >= 0.0)
				{
					stringBuilder.Append('+');
				}
				if (value2.Contains("$"))
				{
					stringBuilder.Append('$');
				}
				stringBuilder.Append(FullNumber(num2, english));
				stringBuilder.Append(flag3 ? "%" : UnitAfter(text2, match2.Index + match2.Length));
				if (mode == StackMode.Compound && flag3)
				{
					stringBuilder.Append(" (x").Append(FullNumber(flag4 ? (1.0 - num2 / 100.0) : (1.0 + num2 / 100.0), english)).Append(')');
				}
				stringBuilder.Append("</color>");
				string text5 = TagPattern.Replace(description.Substring(item.Index + item.Length), "");
				if (flag && flag3 && IsChance(text2.Substring(0, match2.Index), text2.Substring(match2.Index + match2.Length) + text5))
				{
					AppendLuck(stringBuilder, num2, luck, english);
				}
			}
			else
			{
				stringBuilder.Append(flag ? AnnotateChances(text, luck, english) : text);
				stringBuilder.Append(item.Value);
				stringBuilder.Append(" <color=#ffffff>= ").Append(FullNumber(value * (double)count, english));
				if (flag2)
				{
					stringBuilder.Append('%');
				}
				stringBuilder.Append("</color>");
			}
			num = item.Index + item.Length;
		}
		string text6 = description.Substring(num);
		stringBuilder.Append(flag ? AnnotateChances(text6, luck, english) : text6);
		if (flag && stringBuilder.ToString() != description)
		{
			found = true;
		}
		string input2 = SentenceNote.Replace(stringBuilder.ToString(), delegate(Match m)
		{
			if (!TryParse(m.Groups["num"].Value, out var value4))
			{
				return m.Value;
			}
			found = true;
			return m.Value + " <color=#ffffff>= " + FullNumber(value4 * (double)(count - 1), english) + "%</color>";
		});
		if (!found)
		{
			return null;
		}
		return DoubleDot.Replace(input2, ".$1");
	}

	private static bool IsChance(string before, string after)
	{
		if (!ChanceBefore.IsMatch(Spaces.Replace(before, " ").TrimEnd()))
		{
			return ChanceAfter.IsMatch(Spaces.Replace(after, " "));
		}
		return true;
	}

	private static string AnnotateChances(string text, float luck, bool english)
	{
		string text2 = TagPattern.Replace(text, (Match t) => new string(' ', t.Length));
		MatchCollection matchCollection = PercentPattern.Matches(text2);
		if (matchCollection.Count == 0)
		{
			return text;
		}
		StringBuilder stringBuilder = new StringBuilder(text);
		for (int num = matchCollection.Count - 1; num >= 0; num--)
		{
			Match match = matchCollection[num];
			int num2 = match.Index + match.Length;
			if (IsChance(text2.Substring(0, match.Index), text2.Substring(num2)))
			{
				int num3 = num2;
				Match match2 = Regex.Match(text.Substring(num2), "^(</[^>]*>)+");
				if (match2.Success)
				{
					num3 += match2.Length;
				}
				TryParse(match.Value.TrimEnd('%'), out var value);
				StringBuilder stringBuilder2 = new StringBuilder();
				AppendLuck(stringBuilder2, value, luck, english);
				stringBuilder.Insert(num3, stringBuilder2.ToString());
			}
		}
		return stringBuilder.ToString();
	}

	private static string UnitAfter(string maskedRegion, int afterNumber)
	{
		string text = maskedRegion.Substring(afterNumber);
		string text2 = text.TrimStart();
		int num = text2.IndexOfAny(new char[3] { ' ', '\u00a0', '\n' });
		string text3 = ((num < 0) ? text2 : text2.Substring(0, num));
		text3 = text3.TrimEnd(',', ';', ':');
		if (text3.Length == 0 || text3.Length > 7 || char.IsDigit(text3[0]))
		{
			return "";
		}
		return ((text.Length > 0 && char.IsWhiteSpace(text[0])) ? " " : "") + text3;
	}

	public static double LuckyChance(double percent, float luck)
	{
		double num = Math.Min(Math.Max(percent, 0.0), 100.0) / 100.0;
		int num2 = 1 + (int)Math.Ceiling(Math.Abs(luck));
		double num3 = ((luck > 0f) ? (1.0 - Math.Pow(1.0 - num, num2)) : Math.Pow(num, num2));
		return 100.0 * num3;
	}

	private static void AppendLuck(StringBuilder sb, double percent, float luck, bool english)
	{
		sb.Append(" <color=#6fe8c4>(").Append(english ? "with luck " : "с удачей ").Append(FullNumber(LuckyChance(percent, luck), english))
			.Append("%)</color>");
	}

	public static string SaleStar(int stars, float luck, bool english)
	{
		StringBuilder stringBuilder = new StringBuilder();
		stringBuilder.Append(english ? "First chest of a stage drops " : "Первый сундук на уровне даёт ").Append("<color=#ffffff>2</color>").Append(english ? " items." : " предмета.");
		if (stars < 2)
		{
			stringBuilder.Append(english ? "\nWith 2+ stars it can drop up to 5." : "\nС 2+ звёздами может дать до 5.");
			return stringBuilder.ToString();
		}
		double num = ((stars >= 3) ? (100.0 * (1.0 - 1.0 / ((double)stars * 0.05 + 1.0))) : 0.0);
		double[] array = new double[3] { 30.0, 15.0, 1.0 };
		double num2 = 1.0;
		double num3 = 2.0;
		for (int i = 0; i < array.Length; i++)
		{
			double num4 = LuckyChance(array[i] + num, luck) / 100.0;
			num2 *= num4;
			num3 += num2;
			stringBuilder.Append('\n').Append(english ? "Item " : "").Append(i + 3)
				.Append(english ? ": " : "-й предмет: ")
				.Append("<color=#ffffff>")
				.Append(FullNumber(num2 * 100.0, english))
				.Append("%</color>");
		}
		stringBuilder.Append('\n').Append(english ? "Expected items: " : "В среднем предметов: ").Append("<color=#ffffff>")
			.Append(FullNumber(num3, english))
			.Append("</color>");
		return stringBuilder.ToString();
	}

	private static double Total(double baseValue, double per, double sign, int count, bool basePct, bool perPct, StackMode mode, bool reduction)
	{
		if (basePct && perPct && mode == StackMode.Compound)
		{
			if (!reduction)
			{
				return 100.0 * (Math.Pow(1.0 + per / 100.0, count) - 1.0);
			}
			return 100.0 * (1.0 - Math.Pow(1.0 - per / 100.0, count));
		}
		if (perPct && !basePct)
		{
			if (!(sign < 0.0))
			{
				return baseValue * (1.0 + per / 100.0 * (double)(count - 1));
			}
			return baseValue * Math.Pow(1.0 - per / 100.0, count - 1);
		}
		if (basePct && perPct && mode == StackMode.Hyperbolic)
		{
			return 100.0 * (1.0 - 1.0 / (1.0 + per / 100.0 * (double)count));
		}
		if (basePct && perPct && mode == StackMode.Exponential)
		{
			return 100.0 * (1.0 - (1.0 - baseValue / 100.0) * Math.Pow(1.0 - per / 100.0, count - 1));
		}
		return baseValue + sign * per * (double)(count - 1);
	}

	public static string FullNumber(double value, bool english)
	{
		if (Math.Abs(value) >= 1000000000000000.0)
		{
			return Short(value, english);
		}
		NumberFormatInfo numberFormatInfo = (NumberFormatInfo)CultureInfo.InvariantCulture.NumberFormat.Clone();
		numberFormatInfo.NumberGroupSeparator = (english ? "," : "\u00a0");
		numberFormatInfo.NumberDecimalSeparator = (english ? "." : ",");
		bool flag = Math.Abs(value - Math.Round(value)) < 0.005;
		return value.ToString(flag ? "#,0" : "#,0.##", numberFormatInfo);
	}

	public static string Short(double value, bool english)
	{
		string[] array = (english ? SuffixEn : SuffixRu);
		double num = Math.Abs(value);
		int num2 = 0;
		while (num >= 1000.0 && num2 < array.Length - 1)
		{
			num /= 1000.0;
			num2++;
		}
		return ((double)Math.Sign(value) * num).ToString((num2 == 0) ? "0.##" : "0.00", CultureInfo.InvariantCulture) + array[num2];
	}

	private static bool TryParse(string text, out double value)
	{
		return double.TryParse(text.Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out value);
	}
}