Decompiled source of HighscoreAccuracy v1.4.0

HighscoreAccuracy.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BaboonAPI.Hooks.Tracks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using HighscoreAccuracy.Enums;
using Microsoft.CodeAnalysis;
using Microsoft.FSharp.Core;
using Newtonsoft.Json;
using TMPro;
using TrombLoader.CustomTracks;
using TrombLoader.Helpers;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("HighscoreAccuracy")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.4.0.0")]
[assembly: AssemblyInformationalVersion("1.4.0+f2ffb1a87524a6244d678f04b2393c064b8bafb5")]
[assembly: AssemblyProduct("Highscore Accuracy")]
[assembly: AssemblyTitle("HighscoreAccuracy")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.4.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace HighscoreAccuracy
{
	public static class FileHelper
	{
		public static readonly string FILE_PATH_TOOTTALLY_APPDATA = Path.Combine(Application.persistentDataPath, "TootTally");

		public static T LoadFromTootTallyAppData<T>(string fileName)
		{
			string text = Path.Combine(FILE_PATH_TOOTTALLY_APPDATA, fileName);
			if (!File.Exists(text))
			{
				Plugin.Log.LogError((object)("File " + text + " doesnt exist."));
				return default(T);
			}
			try
			{
				return JsonConvert.DeserializeObject<T>(File.ReadAllText(text));
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Couldn't deserialize object: " + ex.Message + " - " + ex.StackTrace));
			}
			return default(T);
		}

		public static void SaveToTootTallyAppData<T>(string fileName, T obj, bool saveToBackupIfExists = false)
		{
			string text = Path.Combine(FILE_PATH_TOOTTALLY_APPDATA, fileName);
			try
			{
				string contents = JsonConvert.SerializeObject((object)obj);
				if (File.Exists(text) && saveToBackupIfExists)
				{
					if (File.Exists(text + ".old"))
					{
						File.Delete(text + ".old");
					}
					File.Move(text, text + ".old");
				}
				File.WriteAllText(text, contents);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Couldn't serialize object: " + ex.Message + " - " + ex.StackTrace));
			}
		}
	}
	public static class HighscoreRegistry
	{
		public readonly struct HighscoreResult
		{
			public int Score { get; }

			public bool ShowAsterisk { get; }

			public HighscoreResult(int score, bool showAsterisk)
			{
				Score = score;
				ShowAsterisk = showAsterisk;
			}
		}

		private static Dictionary<string, Dictionary<string, Dictionary<int, int>>> _trackRefToHighscoreDict = new Dictionary<string, Dictionary<string, Dictionary<int, int>>>();

		public static void LoadHighscoresFromFile()
		{
			_trackRefToHighscoreDict = FileHelper.LoadFromTootTallyAppData<Dictionary<string, Dictionary<string, Dictionary<int, int>>>>("HighscoreAccuracyScores.json");
			if (_trackRefToHighscoreDict == null)
			{
				Plugin.Log.LogInfo((object)"Couldn't find highscores file, creating new one!");
				_trackRefToHighscoreDict = new Dictionary<string, Dictionary<string, Dictionary<int, int>>>();
				FileHelper.SaveToTootTallyAppData("HighscoreAccuracyScores.json", _trackRefToHighscoreDict, saveToBackupIfExists: true);
			}
		}

		public static void CheckNewScore(string trackref, int score, string mods, float gamespeed)
		{
			if (gamespeed != 0f)
			{
				mods = SortModsString(mods);
				int key = (int)Math.Round(gamespeed * 100f);
				if (!_trackRefToHighscoreDict.TryGetValue(trackref, out var value))
				{
					value = new Dictionary<string, Dictionary<int, int>>();
					_trackRefToHighscoreDict[trackref] = value;
				}
				if (!value.TryGetValue(mods, out var value2))
				{
					value2 = (value[mods] = new Dictionary<int, int>());
				}
				if (!value2.TryGetValue(key, out var value3) || score > value3)
				{
					value2[key] = score;
					FileHelper.SaveToTootTallyAppData("HighscoreAccuracyScores.json", _trackRefToHighscoreDict);
				}
			}
		}

		public static HighscoreResult GetHighscore(string trackref, string mods, float gamespeed)
		{
			if (Plugin.useModSpecificHighscores.Value == ModSpecificHighscoreMode.Global)
			{
				return new HighscoreResult(GetGlobalHighscore(), showAsterisk: false);
			}
			mods = SortModsString(mods);
			int key = (int)Math.Round(gamespeed * 100f);
			if (_trackRefToHighscoreDict.TryGetValue(trackref, out var value) && value.TryGetValue(mods, out var value2) && value2.TryGetValue(key, out var value3))
			{
				return new HighscoreResult(value3, showAsterisk: false);
			}
			if (Plugin.useModSpecificHighscores.Value == ModSpecificHighscoreMode.Hybrid)
			{
				return new HighscoreResult(GetGlobalHighscore(), showAsterisk: true);
			}
			return new HighscoreResult(0, showAsterisk: false);
		}

		private static int GetGlobalHighscore()
		{
			FSharpOption<SavedTrackScore> val = TrackLookup.lookupScore(GlobalVariables.chosen_track);
			return (val != null) ? ((IEnumerable<int>)val.Value.highScores).FirstOrDefault() : 0;
		}

		public static string SortModsString(string mods)
		{
			return string.Join(",", (from m in mods.Split(new char[1] { ',' })
				select m.Trim() into m
				orderby m
				select m).ToArray());
		}
	}
	public class OptionalTootTally
	{
		public static Type TootTallyGlobalVariables = GetType("TootTallyCore.Utils.TootTallyGlobals.TootTallyGlobalVariables", "TootTallyCore");

		private static Type GetType(string fullName, string assembly)
		{
			try
			{
				return Type.GetType(fullName + ", " + assembly);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Exception trying to get " + fullName + ". Reporting " + assembly + " as not found."));
				Plugin.Log.LogError((object)ex.Message);
				Plugin.Log.LogError((object)ex.StackTrace);
				return null;
			}
		}

		public static string GetModifiersString()
		{
			Type type = GetType("TootTallyGameModifiers.GameModifierManager", "TootTallyGameModifiers");
			if (type == null)
			{
				return "";
			}
			MethodInfo method = type.GetMethod("GetModifiersString");
			return (method == null) ? "" : ((string)method.Invoke(type, Array.Empty<object>()));
		}

		public static float GameSpeedMultiplier()
		{
			if (TootTallyGlobalVariables == null)
			{
				return GlobalVariables.turbomode ? 2f : 1f;
			}
			FieldInfo field = TootTallyGlobalVariables.GetField("gameSpeedMultiplier", BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy);
			return (field == null) ? 1f : ((float)field.GetValue(field));
		}

		public static object AddNewPage(string pageName, string headerText, float elementSpacing, Color bgColor)
		{
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			Type type = GetType("TootTallySettings.TootTallySettingsManager", "TootTallySettings");
			if (type == null)
			{
				Plugin.Log.LogDebug((object)"TootTallySettings not found");
				return null;
			}
			MethodInfo method = type.GetMethod("AddNewPage", new Type[4]
			{
				typeof(string),
				typeof(string),
				typeof(float),
				typeof(Color)
			});
			return (method == null) ? null : method.Invoke(type, new object[4] { pageName, headerText, elementSpacing, bgColor });
		}

		public static void AddToggle(object page, string name, ConfigEntry<bool> config)
		{
			MethodInfo method = page.GetType().GetMethod("AddToggle", new Type[3]
			{
				typeof(string),
				typeof(ConfigEntry<bool>),
				typeof(UnityAction<bool>)
			});
			if (method != null)
			{
				method.Invoke(page, new object[3] { name, config, null });
			}
		}

		public static void AddSlider(object page, string name, float min, float max, ConfigEntry<float> config, bool integerOnly)
		{
			MethodInfo method = page.GetType().GetMethod("AddSlider", new Type[5]
			{
				typeof(string),
				typeof(float),
				typeof(float),
				typeof(ConfigEntry<float>),
				typeof(bool)
			});
			if (method != null)
			{
				method.Invoke(page, new object[5] { name, min, max, config, integerOnly });
			}
		}

		public static void AddDropdown(object page, string name, ConfigEntryBase config)
		{
			MethodInfo method = page.GetType().GetMethod("AddDropdown", new Type[2]
			{
				typeof(string),
				typeof(ConfigEntryBase)
			});
			if (method != null)
			{
				method.Invoke(page, new object[2] { name, config });
			}
		}

		public static void AddLabel(object page, string label, int fontSize, TextAlignmentOptions textAlignment)
		{
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			MethodInfo method = page.GetType().GetMethod("AddLabel", new Type[5]
			{
				typeof(string),
				typeof(string),
				typeof(int),
				typeof(FontStyles),
				typeof(TextAlignmentOptions)
			});
			if (method != null)
			{
				method.Invoke(page, new object[5]
				{
					label,
					label,
					fontSize,
					(object)(FontStyles)0,
					textAlignment
				});
			}
		}
	}
	public class PercentCounter : MonoBehaviour
	{
		public static Action<int, int> scoreChanged;

		private Text foregroundText;

		private Text shadowText;

		private int pbScore;

		private float pbPercent;

		private int maxScore;

		private int[] scoreLeftover;

		private int[] scoreSums;

		private float targetAcc;

		private float currentAcc;

		private float updateTimer;

		private float timeSinceLastScore;

		public void Init(int maxScore, int[] scoreLeftover, int[] scoreSums, int pbScore, float pbPercent)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			this.maxScore = maxScore;
			this.scoreLeftover = scoreLeftover;
			this.scoreSums = scoreSums;
			this.pbScore = pbScore;
			this.pbPercent = pbPercent;
			((Component)this).transform.localScale = Vector3.one;
		}

		private void Start()
		{
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			foregroundText = ((Component)((Component)this).transform.Find("Score")).GetComponent<Text>();
			shadowText = ((Component)this).GetComponent<Text>();
			foregroundText.text = 100.FormatDecimals() + "%";
			shadowText.text = 100.FormatDecimals() + "%";
			foregroundText.supportRichText = true;
			shadowText.supportRichText = true;
			targetAcc = (currentAcc = 100f);
			updateTimer = (timeSinceLastScore = 0f);
			RectTransform component = ((Component)this).GetComponent<RectTransform>();
			component.anchoredPosition = new Vector2(component.anchoredPosition.x - 50f, component.anchoredPosition.y - 25f);
			scoreChanged = (Action<int, int>)Delegate.Combine(scoreChanged, new Action<int, int>(OnScoreChanged));
		}

		private void Update()
		{
			if (Plugin.animateCounter.Value)
			{
				updateTimer += Time.deltaTime;
				timeSinceLastScore += Time.deltaTime;
				if (updateTimer > 0.02f && currentAcc != targetAcc)
				{
					currentAcc = EaseValue(currentAcc, targetAcc - currentAcc, timeSinceLastScore, 0.6f);
					UpdateText(currentAcc);
					updateTimer = 0f;
				}
			}
		}

		private void OnDestroy()
		{
			scoreChanged = (Action<int, int>)Delegate.Remove(scoreChanged, new Action<int, int>(OnScoreChanged));
		}

		internal void OnScoreChanged(int totalScore, int noteIndex)
		{
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: 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)
			if (scoreLeftover == null || scoreSums == null || maxScore == 0)
			{
				return;
			}
			float percent = GetPercent(totalScore, noteIndex);
			if (Plugin.animateCounter.Value)
			{
				timeSinceLastScore = 0f;
				targetAcc = percent;
			}
			else
			{
				UpdateText(percent);
			}
			if (Plugin.colorBehavior.Value != ColorBehavior.PbPossibility && Plugin.colorBehavior.Value != ColorBehavior.Hybrid)
			{
				return;
			}
			if (totalScore > pbScore)
			{
				((Graphic)foregroundText).color = new Color(0f, 0.7f, 0f);
				return;
			}
			if (percent > pbPercent)
			{
				((Graphic)foregroundText).color = Color.green;
				return;
			}
			float num = scoreLeftover[noteIndex];
			if ((float)totalScore + num > (float)pbScore)
			{
				if (Plugin.colorBehavior.Value == ColorBehavior.Hybrid && percent < pbPercent - 10f)
				{
					((Graphic)foregroundText).color = new Color(1f, 0.5f, 0f);
				}
				else
				{
					((Graphic)foregroundText).color = Color.yellow;
				}
			}
			else
			{
				((Graphic)foregroundText).color = Color.red;
			}
		}

		internal void UpdateText(float percent)
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			string text = percent.FormatDecimals() + "%";
			foregroundText.text = text;
			shadowText.text = text;
			if (Plugin.colorBehavior.Value == ColorBehavior.Closeness)
			{
				if (percent > pbPercent)
				{
					((Graphic)foregroundText).color = Color.green;
				}
				else if (percent < pbPercent - 10f)
				{
					((Graphic)foregroundText).color = Color.red;
				}
				else
				{
					((Graphic)foregroundText).color = Color.yellow;
				}
			}
		}

		private float GetPercent(int totalScore, int noteIndex)
		{
			AccType value = Plugin.accType.Value;
			if (1 == 0)
			{
			}
			float result = value switch
			{
				AccType.Increasing => (float)totalScore / (float)maxScore * 100f, 
				AccType.Decreasing => (float)(totalScore + scoreLeftover[noteIndex]) / (float)maxScore * 100f, 
				_ => (float)totalScore / (float)scoreSums[noteIndex] * 100f, 
			};
			if (1 == 0)
			{
			}
			return result;
		}

		private float EaseValue(float current, float diff, float timeSum, float duration)
		{
			return Mathf.Max(diff * (0f - Mathf.Pow(2f, -10f * timeSum / duration) + 1f) * 1024f / 1023f + current, 0f);
		}
	}
	[HarmonyPatch]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInPlugin("com.hypersonicsharkz.highscoreaccuracy", "Highscore Accuracy", "1.4.0")]
	public class Plugin : BaseUnityPlugin
	{
		internal static Plugin Instance;

		internal static ManualLogSource Log;

		public const string FILE_HIGHSCORES_NAME = "HighscoreAccuracyScores.json";

		internal static ConfigEntry<AccType> accType;

		internal static ConfigEntry<ColorBehavior> colorBehavior;

		internal static ConfigEntry<float> decimals;

		internal static ConfigEntry<bool> showAccIngame;

		internal static ConfigEntry<bool> showLetterIngame;

		internal static ConfigEntry<bool> showPBIngame;

		internal static ConfigEntry<bool> animateCounter;

		internal static ConfigEntry<ModSpecificHighscoreMode> useModSpecificHighscores;

		private void Awake()
		{
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_0241: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			accType = ((BaseUnityPlugin)this).Config.Bind<AccType>("General", "Acc Type", AccType.Real, (ConfigDescription)null);
			colorBehavior = ((BaseUnityPlugin)this).Config.Bind<ColorBehavior>("General", "Color Behavior", ColorBehavior.Hybrid, (ConfigDescription)null);
			decimals = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Decimal Places", 2f, (ConfigDescription)null);
			showAccIngame = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Show acc in track", true, (ConfigDescription)null);
			showLetterIngame = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Show letter rank in track", false, (ConfigDescription)null);
			showPBIngame = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Show PB in track", true, (ConfigDescription)null);
			animateCounter = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Gradually increase the accuracy.", false, (ConfigDescription)null);
			useModSpecificHighscores = ((BaseUnityPlugin)this).Config.Bind<ModSpecificHighscoreMode>("General", "Use Modifier and Game Speed Specific High Scores", ModSpecificHighscoreMode.Hybrid, (ConfigDescription)null);
			object obj = OptionalTootTally.AddNewPage("Highscore Accuracy", "Highscore Accuracy", 40f, new Color(0.1f, 0.1f, 0.1f, 0.1f));
			if (obj != null)
			{
				OptionalTootTally.AddLabel(obj, "Accuracy Type", 24, (TextAlignmentOptions)1025);
				OptionalTootTally.AddDropdown(obj, "Accuracy Type", (ConfigEntryBase)(object)accType);
				OptionalTootTally.AddLabel(obj, "- Base Game: uses the internal calculations for the letter where >100% = S.\n\n- Real: calculates the actual maximum score for a track.\n\n- Decreasing: Uses real accuracy, but your % will always decrease or stay the same.\nFor example, ignoring multipliers, completely missing the first note of a 100 note song will give you 99%.\n\n- Increasing: Uses real accuracy, but your % will always increase or stay the same.\nFor example, ignoring multipliers, perfectly hitting the first note of a 100 note song will give you 1%.", 24, (TextAlignmentOptions)257);
				OptionalTootTally.AddLabel(obj, "Color Behavior", 24, (TextAlignmentOptions)1025);
				OptionalTootTally.AddDropdown(obj, "Color Behavior", (ConfigEntryBase)(object)colorBehavior);
				OptionalTootTally.AddLabel(obj, "- Closeness: Color depends on how close you are to your PB (old Highscore Accuracy behavior)\n    - Green: Above PB\n    - Yellow: Up to 10% below PB\n    - Red: More than 10% below PB\n\n- PB Possibility: Color depends on whether a PB is possible or not\n    - Dark green: Above PB and cannot avoid setting a new PB\n    - Green: Above PB\n    - Yellow: PB is still possible with the remaining notes\n    - Red: PB is impossible\n\n- Hybrid: A combination of the above two (adding orange if below 10% of your PB, but a PB is still possible)\n    - Dark green: Above PB and cannot avoid setting a new PB\n    - Green: Above PB\n    - Yellow: PB is still possible with the remaining notes\n    - Orange: PB is still possible with the remaining notes, but you're more than 10% below PB\n    - Red: PB is impossible", 24, (TextAlignmentOptions)257);
				OptionalTootTally.AddSlider(obj, "Decimal Places", 0f, 4f, decimals, integerOnly: true);
				OptionalTootTally.AddToggle(obj, "Show Acc Ingame", showAccIngame);
				OptionalTootTally.AddToggle(obj, "Show Letter Rank Ingame", showLetterIngame);
				OptionalTootTally.AddToggle(obj, "Show PB Ingame", showPBIngame);
				OptionalTootTally.AddToggle(obj, "Animate Counter", animateCounter);
				OptionalTootTally.AddLabel(obj, "Use Game Speed and Modifier Specific High Scores", 24, (TextAlignmentOptions)1025);
				OptionalTootTally.AddDropdown(obj, "Use Modifier and Game Speed Specific High Scores", (ConfigEntryBase)(object)useModSpecificHighscores);
				OptionalTootTally.AddLabel(obj, "Modifier and game speed specific high scores set without this version of the mod installed aren't saved and can't be shown.\n            \n- Global: The highest score across all modifiers and game speeds is shown. (old Highscore Accuracy behavior)\n\n- ModSpecific: The modifier and game speed specific high score is shown if found.\nOtherwise, no high score is shown.\n            \n- Hybrid: The modifier and game speed specific high score is shown if found.\nOtherwise, the highest score across all modifiers and game speeds is shown with an asterisk (*).", 24, (TextAlignmentOptions)257);
			}
			HighscoreRegistry.LoadHighscoresFromFile();
			new Harmony("com.hypersonicsharkz.highscoreaccuracy").PatchAll();
		}

		[HarmonyPatch(typeof(LevelSelectController), "populateScores")]
		private static void Postfix(LevelSelectController __instance, int ___songindex, List<SingleTrackData> ___alltrackslist)
		{
			string trackref = ___alltrackslist[___songindex].trackref;
			if (Utils.SkipHighscore(trackref))
			{
				return;
			}
			try
			{
				List<float[]> levelData = Utils.GetLevelData(trackref);
				int maxScore = Utils.GetMaxScore(AccType.BaseGame, levelData);
				int maxScore2 = Utils.GetMaxScore(accType.Value, levelData);
				for (int i = 0; i < 5; i++)
				{
					if (float.TryParse(__instance.topscores[i].text, out var result))
					{
						__instance.topscores[i].fontSize = 19;
						string text = Utils.ScoreLetter(result / (float)maxScore);
						float num = result / (float)maxScore2;
						__instance.topscores[i].text = __instance.topscores[i].text + " " + (100f * num).FormatDecimals() + "% " + text;
					}
				}
			}
			catch (Exception ex)
			{
				Debug.LogError((object)ex.Message);
			}
		}

		[HarmonyPatch(typeof(GameController), "tallyScore")]
		private static void Prefix(ref int ___previous_high_score)
		{
			___previous_high_score = int.MaxValue;
		}

		[HarmonyPatch(typeof(PointSceneController), "doCoins")]
		[HarmonyPriority(600)]
		private static void Postfix(PointSceneController __instance)
		{
			string trackref = GlobalVariables.chosen_track_data.trackref;
			if (!Utils.SkipHighscore(trackref))
			{
				List<float[]> levelData = Utils.GetLevelData(trackref);
				int maxScore = Utils.GetMaxScore(accType.Value, levelData);
				float number = (float)GlobalVariables.gameplay_scoretotal / (float)maxScore * 100f;
				float number2 = float.Parse(__instance.txt_prevhigh.text) / (float)maxScore * 100f;
				Text txt_score = __instance.txt_score;
				txt_score.text = txt_score.text + " " + number.FormatDecimals() + "%";
				__instance.txt_score.horizontalOverflow = (HorizontalWrapMode)1;
				Text txt_prevhigh = __instance.txt_prevhigh;
				txt_prevhigh.text = txt_prevhigh.text + " " + number2.FormatDecimals() + "%";
				__instance.txt_prevhigh.horizontalOverflow = (HorizontalWrapMode)1;
			}
		}

		[HarmonyPatch(typeof(GameController), "Start")]
		private static void Postfix(GameController __instance, List<float[]> ___leveldata)
		{
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			if (__instance.freeplay)
			{
				return;
			}
			float pbPercent = 0f;
			int pbScore = 0;
			GameObject val = GameObject.Find("ScoreShadow");
			int[] scoreSums = Utils.GetScoreSums(accType.Value, ___leveldata);
			int num = scoreSums[___leveldata.Count - 1];
			if (showPBIngame.Value)
			{
				HighscoreRegistry.HighscoreResult highscore = HighscoreRegistry.GetHighscore(GlobalVariables.chosen_track, OptionalTootTally.GetModifiersString(), OptionalTootTally.GameSpeedMultiplier());
				if (highscore.Score > 0)
				{
					GameObject val2 = Object.Instantiate<GameObject>(val, val.transform.parent);
					val2.transform.localScale = Vector3.one;
					RectTransform component = val2.GetComponent<RectTransform>();
					component.anchoredPosition = new Vector2(component.anchoredPosition.x, component.anchoredPosition.y - 50f);
					Text component2 = ((Component)val2.transform.Find("Score")).GetComponent<Text>();
					Text component3 = val2.GetComponent<Text>();
					float num2 = (float)highscore.Score / (float)num * 100f;
					component2.text = "PB: " + num2.FormatDecimals() + "%" + (highscore.ShowAsterisk ? "*" : "");
					component3.text = "PB: " + num2.FormatDecimals() + "%" + (highscore.ShowAsterisk ? "*" : "");
					pbPercent = num2;
					pbScore = highscore.Score;
				}
			}
			int[] array = new int[scoreSums.Length];
			for (int i = 0; i < scoreSums.Length; i++)
			{
				array[i] = num - scoreSums[i];
			}
			if (showAccIngame.Value)
			{
				PercentCounter percentCounter = Object.Instantiate<GameObject>(val, val.transform.parent).AddComponent<PercentCounter>();
				percentCounter.Init(num, array, scoreSums, pbScore, pbPercent);
			}
			if (showLetterIngame.Value)
			{
				ScoreCounter scoreCounter = Object.Instantiate<GameObject>(val, val.transform.parent).AddComponent<ScoreCounter>();
				scoreCounter.Init(Utils.GetScoreSums(AccType.BaseGame, ___leveldata));
			}
		}

		[HarmonyPatch(typeof(GameController), "getScoreAverage")]
		private static void Postfix(int ___totalscore, int ___currentnoteindex)
		{
			if (showAccIngame.Value)
			{
				PercentCounter.scoreChanged(___totalscore, ___currentnoteindex);
			}
			if (showLetterIngame.Value)
			{
				ScoreCounter.scoreChanged(___totalscore, ___currentnoteindex);
			}
		}

		[HarmonyPatch(typeof(SaverLoader), "checkForUpdatedScore")]
		private static void Prefix(SingleTrackData played_track, int newscore, string newletterscore)
		{
			HighscoreRegistry.CheckNewScore(played_track.trackref, newscore, OptionalTootTally.GetModifiersString(), OptionalTootTally.GameSpeedMultiplier());
		}
	}
	public class ScoreCounter : MonoBehaviour
	{
		public static Action<int, int> scoreChanged;

		private int[] scoreSums;

		private Text foregroundText;

		private Text shadowText;

		public void Init(int[] scoreSums)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			this.scoreSums = scoreSums;
			((Component)this).transform.localScale = Vector3.one;
		}

		private void Start()
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: 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)
			foregroundText = ((Component)((Component)this).transform.Find("Score")).GetComponent<Text>();
			shadowText = ((Component)this).GetComponent<Text>();
			foregroundText.text = "S";
			shadowText.text = "S";
			RectTransform component = ((Component)this).GetComponent<RectTransform>();
			component.anchoredPosition = new Vector2(component.anchoredPosition.x, component.anchoredPosition.y - 25f);
			scoreChanged = (Action<int, int>)Delegate.Combine(scoreChanged, new Action<int, int>(OnScoreChanged));
		}

		private void OnDestroy()
		{
			scoreChanged = (Action<int, int>)Delegate.Remove(scoreChanged, new Action<int, int>(OnScoreChanged));
		}

		internal void OnScoreChanged(int totalScore, int noteIndex)
		{
			if (scoreSums != null)
			{
				float num = (float)totalScore / (float)scoreSums[noteIndex];
				string text = Utils.ScoreLetter(num);
				foregroundText.text = text;
				shadowText.text = text;
			}
		}
	}
	public static class Utils
	{
		public static string FormatDecimals<T>(this T _number) where T : struct, IComparable, IComparable<T>, IConvertible, IEquatable<T>, IFormattable
		{
			return string.Format(new NumberFormatInfo
			{
				NumberDecimalDigits = (int)Plugin.decimals.Value
			}, "{0:F}", _number);
		}

		public static string ScoreLetter(float num)
		{
			return (!(num < 1f)) ? "S" : ((!(num < 0.8f)) ? "A" : ((!(num < 0.6f)) ? "B" : ((!(num < 0.4f)) ? "C" : ((num < 0.2f) ? "F" : "D"))));
		}

		public static int GetMaxScore(AccType accType, List<float[]> levelData)
		{
			return GetScoreSums(accType, levelData)[levelData.Count - 1];
		}

		public static int[] GetScoreSums(AccType accType, List<float[]> levelData)
		{
			int[] array = new int[levelData.Count];
			int num = 0;
			int num2 = 0;
			float num3 = 0.025f;
			for (int i = 0; i < levelData.Count; i++)
			{
				float num4 = levelData[i][1];
				for (; i + 1 < levelData.Count && levelData[i][0] + levelData[i][1] + num3 >= levelData[i + 1][0]; i++)
				{
					num4 += levelData[i + 1][1];
					array[i] = num;
				}
				int num5 = ((accType == AccType.BaseGame) ? GetGameMax(num4) : GetRealMax(num4, num2));
				num = (array[i] = num + num5);
				num2++;
			}
			return array;
		}

		private static int GetRealMax(float length, int noteIndex)
		{
			double num = ((noteIndex > 23) ? 1.5 : 0.0);
			double num2 = ((double)Math.Min(noteIndex, 10) + num) * 0.100000001490116 + 1.0;
			length = GetBaseScore(length);
			return (int)(Mathf.Floor((float)((double)length * 100.0 * num2)) * 10f);
		}

		private static int GetGameMax(float length)
		{
			return (int)Mathf.Floor(Mathf.Floor(GetBaseScore(length) * 100f * 1.315f) * 10f);
		}

		private static float GetBaseScore(float length)
		{
			return Mathf.Clamp(length, 0.2f, 5f) * 8f + 10f;
		}

		public static List<float[]> GetLevelData(string trackRef)
		{
			return TrackLookup.lookup(trackRef).LoadChart().savedleveldata;
		}

		public static bool SkipHighscore(string trackRef)
		{
			if (TrackLookup.tryLookup(trackRef) == null)
			{
				return true;
			}
			if (!(TrackLookup.lookup(trackRef) is CustomTrack))
			{
				return false;
			}
			TromboneTrack obj = TrackLookup.lookup(trackRef);
			CustomTrack val = (CustomTrack)(object)((obj is CustomTrack) ? obj : null);
			string text = Path.Combine(val.folderPath, Globals.defaultChartName);
			return !File.Exists(text) || new FileInfo(text).Length > 2000000;
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "com.hypersonicsharkz.highscoreaccuracy";

		public const string PLUGIN_NAME = "Highscore Accuracy";

		public const string PLUGIN_VERSION = "1.4.0";
	}
}
namespace HighscoreAccuracy.Enums
{
	public enum AccType
	{
		BaseGame,
		Real,
		Decreasing,
		Increasing
	}
	public enum ColorBehavior
	{
		Closeness,
		PbPossibility,
		Hybrid
	}
	public enum ModSpecificHighscoreMode
	{
		Global,
		ModSpecific,
		Hybrid
	}
}