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 Microsoft.FSharp.Core;
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.3.8.0")]
[assembly: AssemblyInformationalVersion("1.3.8")]
[assembly: AssemblyProduct("Highscore Accuracy")]
[assembly: AssemblyTitle("HighscoreAccuracy")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.3.8.0")]
[module: UnverifiableCode]
namespace HighscoreAccuracy;
public enum AccType
{
BaseGame,
Real,
Decreasing,
Increasing
}
public enum ColorBehavior
{
Closeness,
PbPossibility,
Hybrid
}
public class OptionalTootTallySettings
{
public static object AddNewPage(string pageName, string headerText, float elementSpacing, Color bgColor)
{
//IL_0091: Unknown result type (might be due to invalid IL or missing references)
try
{
Type 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.Invoke(type, new object[4] { pageName, headerText, elementSpacing, bgColor });
}
catch (Exception ex)
{
Plugin.Log.LogError((object)"Exception trying to get config page. Reporting TootTallySettings as not found.");
Plugin.Log.LogError((object)ex.Message);
Plugin.Log.LogError((object)ex.StackTrace);
return null;
}
}
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.*/)]
[BepInPlugin("com.hypersonicsharkz.highscoreaccuracy", "Highscore Accuracy", "1.3.8")]
public class Plugin : BaseUnityPlugin
{
internal static Plugin Instance;
internal static ManualLogSource Log;
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;
private void Awake()
{
//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
//IL_01fb: 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);
object obj = OptionalTootTallySettings.AddNewPage("Highscore Accuracy", "Highscore Accuracy", 40f, new Color(0.1f, 0.1f, 0.1f, 0.1f));
if (obj != null)
{
OptionalTootTallySettings.AddLabel(obj, "Accuracy Type", 24, (TextAlignmentOptions)1025);
OptionalTootTallySettings.AddDropdown(obj, "Accuracy Type", (ConfigEntryBase)(object)accType);
OptionalTootTallySettings.AddLabel(obj, "- Base Game: uses the internal calculations for the letter where >100% = S.\r\n\r\n- Real: calculates the actual maximum score for a track.\r\n\r\n- Decreasing: Uses real accuracy, but your % will always decrease or stay the same.\r\nFor example, ignoring multipliers, completely missing the first note of a 100 note song will give you 99%.\r\n\r\n- Increasing: Uses real accuracy, but your % will always increase or stay the same.\r\nFor example, ignoring multipliers, perfectly hitting the first note of a 100 note song will give you 1%.", 24, (TextAlignmentOptions)257);
OptionalTootTallySettings.AddLabel(obj, "Color Behavior", 24, (TextAlignmentOptions)1025);
OptionalTootTallySettings.AddDropdown(obj, "Color Behavior", (ConfigEntryBase)(object)colorBehavior);
OptionalTootTallySettings.AddLabel(obj, "- Closeness: Color depends on how close you are to your PB (old Highscore Accuracy behavior)\r\n - Green: Above PB\r\n - Yellow: Up to 10% below PB\r\n - Red: More than 10% below PB\r\n\r\n- PB Possibility: Color depends on whether a PB is possible or not\r\n - Dark green: Above PB and cannot avoid setting a new PB\r\n - Green: Above PB\r\n - Yellow: PB is still possible with the remaining notes\r\n - Red: PB is impossible\r\n\r\n- Hybrid: A combination of the above two (adding orange if below 10% of your PB, but a PB is still possible)\r\n - Dark green: Above PB and cannot avoid setting a new PB\r\n - Green: Above PB\r\n - Yellow: PB is still possible with the remaining notes\r\n - Orange: PB is still possible with the remaining notes, but you're more than 10% below PB\r\n - Red: PB is impossible", 24, (TextAlignmentOptions)257);
OptionalTootTallySettings.AddSlider(obj, "Decimal Places", 0f, 4f, decimals, integerOnly: true);
OptionalTootTallySettings.AddToggle(obj, "Show Acc Ingame", showAccIngame);
OptionalTootTallySettings.AddToggle(obj, "Show Letter Rank Ingame", showLetterIngame);
OptionalTootTallySettings.AddToggle(obj, "Show PB Ingame", showPBIngame);
OptionalTootTallySettings.AddToggle(obj, "Animate Counter", animateCounter);
OptionalTootTallySettings.AddLabel(obj, "If the dropdowns aren't showing up, update TootTally.\r\nYou can still update accuracy type through the config file, as usual.", 24, (TextAlignmentOptions)257);
}
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;
}
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++)
{
try
{
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_00a5: Unknown result type (might be due to invalid IL or missing references)
//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
//IL_00d9: 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)
{
FSharpOption<SavedTrackScore> val2 = TrackLookup.lookupScore(GlobalVariables.chosen_track);
int num2 = ((val2 != null) ? ((IEnumerable<int>)val2.Value.highScores).FirstOrDefault() : 0);
if (num2 > 0)
{
GameObject val3 = Object.Instantiate<GameObject>(val, val.transform.parent);
val3.transform.localScale = Vector3.one;
RectTransform component = val3.GetComponent<RectTransform>();
component.anchoredPosition = new Vector2(component.anchoredPosition.x, component.anchoredPosition.y - 50f);
Text component2 = ((Component)val3.transform.Find("Score")).GetComponent<Text>();
Text component3 = val3.GetComponent<Text>();
float num3 = (float)num2 / (float)num * 100f;
component2.text = "PB: " + num3.FormatDecimals() + "%";
component3.text = "PB: " + num3.FormatDecimals() + "%";
pbPercent = num3;
pbScore = num2;
}
}
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);
}
}
}
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)
{
TromboneTrack obj = TrackLookup.lookup(trackRef);
CustomTrack val = (CustomTrack)(object)((obj is CustomTrack) ? obj : null);
return val != null && new FileInfo(Path.Combine(val.folderPath, Globals.defaultChartName)).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.3.8";
}