Decompiled source of UltraAchievements Library v2.0.0

plugins/UltraAchievements-Library/UltraAchievementsRevamped.Core.dll

Decompiled 2 months ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
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 BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UltraAchievementsRevamped.Core.Achievements;
using UltraAchievementsRevamped.Core.UI;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("UltraAchievementsRevamped.Core")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Core library for UltraAchievements")]
[assembly: AssemblyFileVersion("2.0.0.0")]
[assembly: AssemblyInformationalVersion("2.0.0+53de7905e782c38379c5254ecd06ea62b5c9c4ec")]
[assembly: AssemblyProduct("UltraAchievementsRevamped.Core")]
[assembly: AssemblyTitle("UltraAchievementsRevamped.Core")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("2.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 UltraAchievementsRevamped.Core
{
	internal static class Assets
	{
		internal static AchievementPopUp AchievementPopUpPrefab;

		internal static AchievementPanel AchievementPanelPrefab;

		public static string AssetPath => Path.Combine(ModFolder, "Assets");

		private static string ModFolder => Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

		private static string CatalogPath => Path.Combine(AssetPath, "catalog_core.json");

		internal static void LoadAssets()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			Addressables.LoadContentCatalogAsync(CatalogPath, true, (string)null).WaitForCompletion();
			AchievementPopUpPrefab = Addressables.LoadAssetAsync<GameObject>((object)"UltraAchievementsCore/Achievement Overlay.prefab").WaitForCompletion().GetComponent<AchievementPopUp>();
			AchievementPanelPrefab = Addressables.LoadAssetAsync<GameObject>((object)"UltraAchievementsCore/Achievement Panel.prefab").WaitForCompletion().GetComponent<AchievementPanel>();
		}
	}
	[BepInPlugin("protract.ultrakill.ultra_achievements_core", "UltraAchievementsRevamped.Core", "2.0.0")]
	public class Plugin : BaseUnityPlugin
	{
		public static class PluginInfo
		{
			public const string Name = "UltraAchievementsRevamped.Core";

			public const string Guid = "protract.ultrakill.ultra_achievements_core";

			public const string Version = "2.0.0";
		}

		internal static ManualLogSource Logger;

		private void Awake()
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			Logger = ((BaseUnityPlugin)this).Logger;
			Logger.LogInfo((object)"UltraAchievementsRevamped.Core 2.0.0 has loaded!");
			new Harmony("protract.ultrakill.ultra_achievements_core").PatchAll();
			Assets.LoadAssets();
		}
	}
}
namespace UltraAchievementsRevamped.Core.UI
{
	[HarmonyPatch]
	public class AchievementPanel : MonoBehaviour
	{
		[SerializeField]
		private GameObject modButtonTemplate;

		[SerializeField]
		private GameObject modPanelTemplate;

		[SerializeField]
		private GameObject achievementTemplate;

		[SerializeField]
		private ShopButton backButton;

		[SerializeField]
		private int itemsPerPage;

		private readonly List<GameObject> modButtons = new List<GameObject>();

		private readonly List<Button> pageButtons = new List<Button>();

		private readonly List<GameObject> modPanels = new List<GameObject>();

		private void Start()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Expected O, but got Unknown
			foreach (Transform item in ((Component)this).transform.Find("Left Panel/Buttons/Page Buttons"))
			{
				Button component = ((Component)item).GetComponent<Button>();
				if ((Object)(object)component != (Object)null)
				{
					pageButtons.Add(component);
				}
			}
			for (int i = 0; i < pageButtons.Count; i++)
			{
				int pageIndex = i;
				((UnityEvent)pageButtons[i].onClick).AddListener((UnityAction)delegate
				{
					GoToPage(pageIndex);
				});
			}
			foreach (var (modName, achievements) in AchievementManager.ModNameToAchInfo)
			{
				CreateModButton(modName, achievements);
			}
			GoToPage(0);
		}

		private void CreateModButton(string modName, List<AchievementInfo> achievements)
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Expected O, but got Unknown
			Transform val = ((Component)this).transform.Find("Left Panel/Buttons/Mod Buttons");
			GameObject val2 = Object.Instantiate<GameObject>(modButtonTemplate, val);
			val2.GetComponentInChildren<TMP_Text>().text = modName;
			int panelIndex = modButtons.Count;
			((UnityEvent)val2.GetComponent<Button>().onClick).AddListener((UnityAction)delegate
			{
				SelectModPanel(panelIndex);
			});
			CreateModPanel(modName, achievements);
			modButtons.Add(val2);
		}

		private void CreateModPanel(string modName, List<AchievementInfo> achievements)
		{
			GameObject val = Object.Instantiate<GameObject>(modPanelTemplate, modPanelTemplate.transform.parent);
			((Component)val.transform.Find("Title")).GetComponent<TMP_Text>().text = modName;
			val.SetActive(false);
			Transform val2 = val.transform.Find("Achievement List/Viewport/Content");
			foreach (AchievementInfo achievement in achievements)
			{
				if (!achievement.IsHidden || achievement.IsComplete)
				{
					GameObject val3 = Object.Instantiate<GameObject>(achievementTemplate, val2);
					val3.SetActive(true);
					((Component)val3.transform.Find("Name")).GetComponent<TMP_Text>().text = achievement.DisplayName;
					((Component)val3.transform.Find("Description")).GetComponent<TMP_Text>().text = achievement.Description;
					if (achievement.IsComplete)
					{
						((Component)val3.transform.Find("Icon")).GetComponent<Image>().sprite = achievement.Icon;
					}
				}
			}
			modPanels.Add(val);
		}

		private void SelectModPanel(int index)
		{
			for (int i = 0; i < modPanels.Count; i++)
			{
				modPanels[i].SetActive(i == index);
			}
		}

		private void GoToPage(int pageIndex)
		{
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			if (itemsPerPage <= 0)
			{
				Plugin.Logger.LogError((object)"itemsPerPage on AchievementPanel must be greater than zero");
				return;
			}
			int num = pageIndex * itemsPerPage;
			int num2 = num + itemsPerPage;
			for (int i = 0; i < modButtons.Count; i++)
			{
				modButtons[i].SetActive(i >= num && i < num2);
			}
			int num3 = Mathf.CeilToInt((float)modButtons.Count / (float)itemsPerPage);
			for (int j = 0; j < pageButtons.Count; j++)
			{
				if (j >= num3)
				{
					((Selectable)pageButtons[j]).interactable = false;
					((Graphic)((Component)pageButtons[j]).GetComponent<Image>()).color = Color.white;
				}
				else if (j == pageIndex)
				{
					((Selectable)pageButtons[j]).interactable = true;
					((Graphic)((Component)pageButtons[j]).GetComponent<Image>()).color = Color.red;
				}
				else
				{
					((Selectable)pageButtons[j]).interactable = true;
					((Graphic)((Component)pageButtons[j]).GetComponent<Image>()).color = Color.white;
				}
			}
		}

		[HarmonyPatch(typeof(ShopZone), "Start")]
		[HarmonyPostfix]
		private static void TerminalSpawnPatch(ShopZone __instance)
		{
			if (!(((Object)__instance).name != "Shop"))
			{
				GameObject gameObject = ((Component)((Component)__instance).transform.Find("Canvas/Background/Main Panel")).gameObject;
				AchievementPanel achievementPanel = Object.Instantiate<AchievementPanel>(Assets.AchievementPanelPrefab, gameObject.transform);
				GameObject gameObject2 = ((Component)((Component)__instance).transform.Find("Canvas/Background/Icon")).gameObject;
				((Selectable)gameObject2.AddComponent<Button>()).interactable = true;
				((Graphic)gameObject2.GetComponent<Image>()).raycastTarget = true;
				ShopButton val = gameObject2.AddComponent<ShopButton>();
				val.toActivate = (GameObject[])(object)new GameObject[1] { ((Component)achievementPanel).gameObject };
				val.toDeactivate = Array.Empty<GameObject>();
				val.clickSound = achievementPanel.backButton.clickSound;
			}
		}
	}
	public class AchievementPopUp : MonoBehaviour
	{
		[CompilerGenerated]
		private sealed class <WaitAndDestroy>d__9 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public AchievementPopUp <>4__this;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <WaitAndDestroy>d__9(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_002f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0039: Expected O, but got Unknown
				//IL_005f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0069: Expected O, but got Unknown
				int num = <>1__state;
				AchievementPopUp achievementPopUp = <>4__this;
				switch (num)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = (object)new WaitForSeconds(10f);
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					achievementPopUp.hudOpenEffect.Reverse(30f);
					<>2__current = (object)new WaitForSeconds(1f);
					<>1__state = 2;
					return true;
				case 2:
					<>1__state = -1;
					Object.Destroy((Object)(object)((Component)achievementPopUp).gameObject);
					return false;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		[SerializeField]
		private TMP_Text titleText;

		[SerializeField]
		private TMP_Text descriptionText;

		[SerializeField]
		private Image achievementIcon;

		[SerializeField]
		private AudioClip achievementSound;

		[SerializeField]
		private HudOpenEffect hudOpenEffect;

		private const float DestroyDelay = 10f;

		private const float HUDReverseSpeed = 30f;

		private const float HUDReverseTime = 1f;

		private void Start()
		{
			NewMovement instance = MonoSingleton<NewMovement>.Instance;
			if (instance != null)
			{
				AudioSource component = ((Component)instance).GetComponent<AudioSource>();
				if (component != null)
				{
					component.PlayOneShot(achievementSound);
				}
			}
			((MonoBehaviour)this).StartCoroutine(WaitAndDestroy());
		}

		[IteratorStateMachine(typeof(<WaitAndDestroy>d__9))]
		private IEnumerator WaitAndDestroy()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <WaitAndDestroy>d__9(0)
			{
				<>4__this = this
			};
		}

		internal static void CreateInstance(AchievementInfo achievementInfo, Transform parent)
		{
			AchievementPopUp achievementPopUp = Object.Instantiate<AchievementPopUp>(Assets.AchievementPopUpPrefab, parent);
			achievementPopUp.titleText.text = achievementInfo.DisplayName;
			achievementPopUp.descriptionText.text = achievementInfo.Description;
			achievementPopUp.achievementIcon.sprite = achievementInfo.Icon;
		}
	}
}
namespace UltraAchievementsRevamped.Core.Achievements
{
	public class AchievementInfo : ScriptableObject
	{
		[SerializeField]
		private string id;

		[SerializeField]
		private string sourceMod;

		[SerializeField]
		private Sprite icon;

		[SerializeField]
		private string displayName;

		[SerializeField]
		[TextArea]
		private string description;

		[SerializeField]
		private bool isHidden;

		[NonSerialized]
		internal bool IsComplete;

		public string Id => id;

		public string SourceMod => sourceMod;

		public Sprite Icon => icon;

		public string DisplayName => displayName;

		public string Description => description;

		public bool IsHidden => isHidden;

		public static T Create<T>(string id, string sourceMod, Sprite icon, string displayName, string description, bool isHidden) where T : AchievementInfo
		{
			T val = ScriptableObject.CreateInstance<T>();
			val.id = id;
			val.sourceMod = sourceMod;
			val.icon = icon;
			val.displayName = displayName;
			val.description = description;
			val.isHidden = isHidden;
			return val;
		}
	}
	public static class AchievementManager
	{
		private const int SaveFormatVersion = 1;

		private static Dictionary<string, (bool isComplete, int? progress)> _saveDataCache;

		private static readonly Dictionary<string, AchievementInfo> IdToAchInfo = new Dictionary<string, AchievementInfo>();

		private static string SavePath => Path.Combine(Application.persistentDataPath, "achievements.dat");

		internal static Dictionary<string, List<AchievementInfo>> ModNameToAchInfo => (from info in IdToAchInfo.Values
			group info by info.SourceMod).ToDictionary((IGrouping<string, AchievementInfo> grouping) => grouping.Key, (IGrouping<string, AchievementInfo> grouping) => grouping.ToList());

		public static void RegisterAchievementInfos(IEnumerable<AchievementInfo> infos)
		{
			foreach (AchievementInfo info2 in infos)
			{
				if ((Object)(object)info2 == (Object)null)
				{
					Plugin.Logger.LogError((object)"Cannot register null achievement info");
					continue;
				}
				if (IdToAchInfo.ContainsKey(info2.Id))
				{
					Plugin.Logger.LogError((object)("Achievement with id " + info2.Id + " has already been registered"));
					continue;
				}
				if (info2 is ProgressiveAchievementInfo info)
				{
					RegisterProgressiveInfo(info);
				}
				else
				{
					RegisterInfo(info2);
				}
				Plugin.Logger.LogInfo((object)("Registered achievement with id " + info2.Id));
			}
		}

		public static AchievementInfo GetAchievementInfo(string id)
		{
			if (!IdToAchInfo.TryGetValue(id, out var value))
			{
				Plugin.Logger.LogError((object)("Achievement " + id + " has not been registered"));
			}
			return value;
		}

		public static void MarkAchievementComplete(string id)
		{
			AchievementInfo achievementInfo = GetAchievementInfo(id);
			if (!((Object)(object)achievementInfo == (Object)null) && !achievementInfo.IsComplete)
			{
				achievementInfo.IsComplete = true;
				AchievementPopUp.CreateInstance(achievementInfo, null);
				Plugin.Logger.LogInfo((object)("Marked achievement " + achievementInfo.Id + " as complete"));
				SaveAchievementProgress();
			}
		}

		public static void MarkAchievementComplete(AchievementInfo achievementInfo)
		{
			if ((Object)(object)achievementInfo == (Object)null)
			{
				Plugin.Logger.LogError((object)"Cannot mark null achievement info as complete");
			}
			else if (!achievementInfo.IsComplete)
			{
				achievementInfo.IsComplete = true;
				AchievementPopUp.CreateInstance(achievementInfo, null);
				Plugin.Logger.LogInfo((object)("Marked achievement " + achievementInfo.Id + " as complete"));
				SaveAchievementProgress();
			}
		}

		public static void AddProgressToAchievement(string id, int amount)
		{
			AchievementInfo achievementInfo = GetAchievementInfo(id);
			if ((Object)(object)achievementInfo == (Object)null)
			{
				return;
			}
			if (achievementInfo is ProgressiveAchievementInfo progressiveAchievementInfo)
			{
				progressiveAchievementInfo.CurrentProgress += amount;
				if (progressiveAchievementInfo.CurrentProgress >= progressiveAchievementInfo.MaxProgress)
				{
					MarkAchievementComplete(progressiveAchievementInfo);
				}
				else
				{
					SaveAchievementProgress();
				}
			}
			else
			{
				Plugin.Logger.LogError((object)("Achievement " + id + " is not a progressive achievement"));
			}
		}

		public static void AddProgressToAchievement(AchievementInfo achievementInfo, int amount)
		{
			if ((Object)(object)achievementInfo == (Object)null)
			{
				Plugin.Logger.LogError((object)"Cannot add progress to null progressive achievement info");
			}
			else if (achievementInfo is ProgressiveAchievementInfo progressiveAchievementInfo)
			{
				progressiveAchievementInfo.CurrentProgress += amount;
				if (progressiveAchievementInfo.CurrentProgress >= progressiveAchievementInfo.MaxProgress)
				{
					MarkAchievementComplete(progressiveAchievementInfo);
				}
				else
				{
					SaveAchievementProgress();
				}
			}
			else
			{
				Plugin.Logger.LogError((object)("Achievement " + achievementInfo.Id + " is not a progressive achievement"));
			}
		}

		private static void RegisterInfo(AchievementInfo info)
		{
			if (_saveDataCache == null)
			{
				_saveDataCache = LoadAchievementProgress();
			}
			if (_saveDataCache.TryGetValue(info.Id, out (bool, int?) value))
			{
				(info.IsComplete, _) = value;
			}
			IdToAchInfo.Add(info.Id, info);
		}

		private static void RegisterProgressiveInfo(ProgressiveAchievementInfo info)
		{
			if (_saveDataCache == null)
			{
				_saveDataCache = LoadAchievementProgress();
			}
			if (_saveDataCache.TryGetValue(info.Id, out (bool, int?) value))
			{
				(info.IsComplete, _) = value;
				if (value.Item2.HasValue)
				{
					info.CurrentProgress = value.Item2.Value;
				}
			}
			IdToAchInfo.Add(info.Id, info);
		}

		private static void SaveAchievementProgress()
		{
			try
			{
				using BinaryWriter binaryWriter = new BinaryWriter(File.Open(SavePath, FileMode.Create));
				binaryWriter.Write(1);
				binaryWriter.Write(IdToAchInfo.Count);
				foreach (var (value, achievementInfo2) in IdToAchInfo)
				{
					binaryWriter.Write(value);
					binaryWriter.Write(achievementInfo2.IsComplete);
					binaryWriter.Write(achievementInfo2 is ProgressiveAchievementInfo);
					if (achievementInfo2 is ProgressiveAchievementInfo progressiveAchievementInfo)
					{
						binaryWriter.Write(progressiveAchievementInfo.CurrentProgress);
					}
				}
				Plugin.Logger.LogInfo((object)$"Achievements saved at {Time.time}");
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogError((object)("Achievements could not be saved to " + SavePath + ": " + ex.Message));
			}
		}

		private static Dictionary<string, (bool isComplete, int? progress)> LoadAchievementProgress()
		{
			Dictionary<string, (bool, int?)> dictionary = new Dictionary<string, (bool, int?)>();
			if (!File.Exists(SavePath))
			{
				return dictionary;
			}
			try
			{
				using BinaryReader binaryReader = new BinaryReader(File.OpenRead(SavePath));
				int num = binaryReader.ReadInt32();
				if (num != 1)
				{
					Plugin.Logger.LogError((object)$"Achievements could not be loaded due to mismatched format versions: {num} vs {1}");
					return dictionary;
				}
				int num2 = binaryReader.ReadInt32();
				for (int i = 0; i < num2; i++)
				{
					string key = binaryReader.ReadString();
					bool item = binaryReader.ReadBoolean();
					int? item2 = (binaryReader.ReadBoolean() ? new int?(binaryReader.ReadInt32()) : null);
					dictionary[key] = (item, item2);
				}
				return dictionary;
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogError((object)("Achievements could not be loaded from " + SavePath + ": " + ex.Message));
				return dictionary;
			}
		}
	}
	public class ProgressiveAchievementInfo : AchievementInfo
	{
		[SerializeField]
		private int maxProgress;

		[NonSerialized]
		internal int CurrentProgress;

		public int MaxProgress => maxProgress;

		public static ProgressiveAchievementInfo Create(string id, string sourceMod, Sprite icon, string displayName, string description, bool isHidden, int maxProgress)
		{
			ProgressiveAchievementInfo progressiveAchievementInfo = AchievementInfo.Create<ProgressiveAchievementInfo>(id, sourceMod, icon, displayName, description, isHidden);
			progressiveAchievementInfo.maxProgress = maxProgress;
			return progressiveAchievementInfo;
		}
	}
}