Decompiled source of AchievementPeek v1.0.0

AchievementPeek.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Koendertjh.Shared;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("AchievementPeek")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+a45c570d17c512d77d75166ff42bc1dee72ef243")]
[assembly: AssemblyProduct("AchievementPeek")]
[assembly: AssemblyTitle("AchievementPeek")]
[assembly: AssemblyVersion("1.0.0.0")]
[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 Koendertjh.Shared
{
	internal enum Boss
	{
		None,
		Eikthyr,
		Elder,
		Bonemass,
		Moder,
		Yagluth,
		Queen,
		Fader,
		Kall
	}
	internal static class BossProgress
	{
		private static readonly string[] EnemyNames = new string[9] { null, "$enemy_eikthyr", "$enemy_gdking", "$enemy_bonemass", "$enemy_dragon", "$enemy_goblinking", "$enemy_seekerqueen", "$enemy_fader", "$enemy_frozenking_p3" };

		private static Dictionary<string, string> s_defeatKeys;

		private static ZNetScene s_keysScene;

		internal static string EnemyName(Boss boss)
		{
			return EnemyNames[(int)boss];
		}

		internal static bool IsDefeated(Boss boss)
		{
			if (boss == Boss.None)
			{
				return true;
			}
			string enemyName = EnemyName(boss);
			if (HasKilled(enemyName))
			{
				return true;
			}
			string text = DefeatKey(enemyName);
			if (string.IsNullOrEmpty(text))
			{
				return false;
			}
			if (!((Object)(object)ZoneSystem.instance != (Object)null) || !ZoneSystem.instance.GetGlobalKey(text))
			{
				if ((Object)(object)Player.m_localPlayer != (Object)null)
				{
					return ((Humanoid)Player.m_localPlayer).HaveUniqueKey(text);
				}
				return false;
			}
			return true;
		}

		internal static bool HasKilled(string enemyName)
		{
			Game instance = Game.instance;
			PlayerProfile val = ((instance != null) ? instance.GetPlayerProfile() : null);
			if (val == null)
			{
				return false;
			}
			PlayerStats[] playerStats = val.m_playerStats;
			foreach (PlayerStats val2 in playerStats)
			{
				if (val2?.m_enemyStats == null)
				{
					continue;
				}
				Dictionary<string, float>[] enemyStats = val2.m_enemyStats;
				foreach (Dictionary<string, float> dictionary in enemyStats)
				{
					if (dictionary != null && dictionary.TryGetValue(enemyName, out var value) && value > 0f)
					{
						return true;
					}
				}
			}
			return false;
		}

		private static string DefeatKey(string enemyName)
		{
			if ((Object)(object)ZNetScene.instance == (Object)null)
			{
				return null;
			}
			if (s_defeatKeys == null || (Object)(object)s_keysScene != (Object)(object)ZNetScene.instance)
			{
				s_defeatKeys = new Dictionary<string, string>();
				s_keysScene = ZNetScene.instance;
				foreach (GameObject prefab in ZNetScene.instance.m_prefabs)
				{
					Character val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<Character>() : null);
					if ((Object)(object)val != (Object)null && !string.IsNullOrEmpty(val.m_defeatSetGlobalKey))
					{
						s_defeatKeys[val.m_name] = val.m_defeatSetGlobalKey;
					}
				}
			}
			s_defeatKeys.TryGetValue(enemyName, out var value);
			return value;
		}
	}
}
namespace AchievementPeek
{
	[HarmonyPatch(typeof(AchievementsGui))]
	internal static class DetailRows
	{
		private static readonly Dictionary<string, string> StatNames = new Dictionary<string, string>
		{
			{ "BossKillMultiplayer", "Bosses killed together with a friend" },
			{ "BossKillSolo", "Bosses killed alone" },
			{ "BuildClusterArchitecture", "Architecture pieces in one building" },
			{ "BuildClusterBuilding", "Buildings in one village" },
			{ "BuildClusterDecor", "Decorations in one building" },
			{ "BuildClusterFloor", "Floors in one building" },
			{ "BuildClusterFurniture", "Furniture in one building" },
			{ "BuildClusterLighting", "Lights in one building" },
			{ "BuildClusterRoof", "Roof pieces in one building" },
			{ "BuildClusterStorage", "Storage in one building" },
			{ "BuildClusterWall", "Walls in one building" },
			{ "DeathByAshlandsLava", "Death by lava" },
			{ "Deaths", "Deaths" },
			{ "FishCaughtTier4", "Four star fish caught" },
			{ "LeviathanSink", "Leviathans awakened" },
			{ "PlayerSpawn", "Times spawned" }
		};

		private static Transform s_odinRow;

		private static int s_hidden;

		private static int s_missing;

		[HarmonyPrefix]
		[HarmonyPatch("OnOpenAchievementDetails")]
		private static void BeforeOpen(AchievementsGui __instance, out bool __state)
		{
			__state = __instance.m_achievementDetails.activeSelf;
			if (!__state)
			{
				s_odinRow = null;
				s_hidden = 0;
				s_missing = 0;
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch("OnOpenAchievementDetails")]
		private static void AfterOpen(Achievement achievement, bool clickable, bool __state, AchievementDetailUnlockCondition ___m_achievementDetailsElementPrefab, RectTransform ___m_achievementDetailsListRoot)
		{
			if (__state || !clickable || !AchievementPeekPlugin.Active)
			{
				return;
			}
			foreach (Achievement otherAchievementTrigger in achievement.m_otherAchievementTriggers)
			{
				if (!((Object)(object)otherAchievementTrigger == (Object)null))
				{
					AddRow(___m_achievementDetailsElementPrefab, ___m_achievementDetailsListRoot, Localization.instance.Localize(otherAchievementTrigger.m_name), otherAchievementTrigger.m_unlocked ? 1f : 0f, 1f);
				}
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch("CreateStatRow")]
		private static bool CreateStatRow(string statKey, float currentAmount, float totalAmount, AchievementDetailUnlockCondition ___m_achievementDetailsElementPrefab, RectTransform ___m_achievementDetailsListRoot)
		{
			RowMode value = AchievementPeekPlugin.MissingRows.Value;
			if (currentAmount >= totalAmount || value == RowMode.Off || !AchievementPeekPlugin.Active)
			{
				return true;
			}
			if (value == RowMode.Discovered && !Discovery.IsDiscovered(statKey))
			{
				s_hidden++;
				UpdateOdinRow(___m_achievementDetailsElementPrefab, ___m_achievementDetailsListRoot);
				return false;
			}
			AddRow(___m_achievementDetailsElementPrefab, ___m_achievementDetailsListRoot, RowName(statKey), currentAmount, totalAmount);
			return false;
		}

		[HarmonyPostfix]
		[HarmonyPatch("CreateStatRow")]
		private static void KeepOdinRowLast()
		{
			if ((Object)(object)s_odinRow != (Object)null)
			{
				s_odinRow.SetAsLastSibling();
			}
		}

		private static void AddRow(AchievementDetailUnlockCondition prefab, RectTransform root, string name, float current, float total)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			bool flag = current >= total;
			AchievementDetailUnlockCondition val = NewRow(prefab, root, flag ? Color.green : Color.gray);
			((TMP_Text)val.StatName).text = name;
			((TMP_Text)val.Progress).text = Format(current) + " / " + Format(total);
			if (!flag && AchievementPeekPlugin.MissingFirst.Value)
			{
				((Component)val).transform.SetSiblingIndex(s_missing++);
			}
			KeepOdinRowLast();
		}

		private static void UpdateOdinRow(AchievementDetailUnlockCondition prefab, RectTransform root)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)s_odinRow == (Object)null)
			{
				AchievementDetailUnlockCondition obj = NewRow(prefab, root, Color.gray);
				TextMeshProUGUI statName = obj.StatName;
				((TMP_Text)statName).fontStyle = (FontStyles)(((TMP_Text)statName).fontStyle | 2);
				((TMP_Text)obj.Progress).text = "";
				s_odinRow = ((Component)obj).transform;
			}
			((TMP_Text)((Component)s_odinRow).GetComponent<AchievementDetailUnlockCondition>().StatName).text = $"Only Odin knows what else is hidden here ({s_hidden})";
			s_odinRow.SetAsLastSibling();
		}

		private static AchievementDetailUnlockCondition NewRow(AchievementDetailUnlockCondition prefab, RectTransform root, Color color)
		{
			//IL_0019: 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)
			AchievementDetailUnlockCondition obj = Object.Instantiate<AchievementDetailUnlockCondition>(prefab, (Transform)(object)root);
			((Component)obj).gameObject.SetActive(true);
			((Graphic)obj.StatName).color = color;
			((Graphic)obj.Progress).color = color;
			return obj;
		}

		private static string RowName(string statKey)
		{
			if (!Discovery.IsStat(statKey))
			{
				return Localized(statKey) ?? statKey;
			}
			if (StatNames.TryGetValue(statKey, out var value))
			{
				return value;
			}
			string text = Localized("$stat_" + statKey);
			if (text != null)
			{
				return text;
			}
			string text2 = Regex.Replace(statKey, "(?<=[a-z])(?=[A-Z])", " ");
			return text2.Substring(0, 1) + text2.Substring(1).ToLowerInvariant();
		}

		private static string Localized(string key)
		{
			string text = Localization.instance.Localize(key);
			if (string.IsNullOrEmpty(text) || text == key || text.StartsWith("[", StringComparison.Ordinal))
			{
				return null;
			}
			return text;
		}

		private static string Format(float amount)
		{
			return Mathf.Floor(amount).ToString("N0", CultureInfo.InvariantCulture);
		}
	}
	internal static class Discovery
	{
		private static Dictionary<string, HashSet<string>> s_creatureDrops;

		private static Dictionary<string, string> s_fishItems;

		private static ZNetScene s_scene;

		internal static bool IsStat(string key)
		{
			PlayerStatType result;
			return Enum.TryParse<PlayerStatType>(key, out result);
		}

		internal static bool IsDiscovered(string key)
		{
			if (IsStat(key))
			{
				return true;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return false;
			}
			Build();
			if (s_creatureDrops.TryGetValue(key, out var value))
			{
				if (BossProgress.HasKilled(key))
				{
					return true;
				}
				foreach (string item in value)
				{
					if (localPlayer.IsMaterialKnown(item))
					{
						return true;
					}
				}
				return false;
			}
			if (s_fishItems.TryGetValue(key, out var value2) && localPlayer.IsMaterialKnown(value2))
			{
				return true;
			}
			if (!localPlayer.IsRecipeKnown(key))
			{
				return localPlayer.IsMaterialKnown(key);
			}
			return true;
		}

		private static void Build()
		{
			if (s_creatureDrops != null && (Object)(object)s_scene == (Object)(object)ZNetScene.instance)
			{
				return;
			}
			s_creatureDrops = new Dictionary<string, HashSet<string>>();
			s_fishItems = new Dictionary<string, string>();
			s_scene = ZNetScene.instance;
			if ((Object)(object)s_scene == (Object)null)
			{
				return;
			}
			foreach (GameObject prefab in s_scene.m_prefabs)
			{
				if ((Object)(object)prefab == (Object)null)
				{
					continue;
				}
				Character component = prefab.GetComponent<Character>();
				if ((Object)(object)component != (Object)null)
				{
					if (!s_creatureDrops.TryGetValue(component.m_name, out var value))
					{
						value = (s_creatureDrops[component.m_name] = new HashSet<string>());
					}
					CharacterDrop component2 = prefab.GetComponent<CharacterDrop>();
					if ((Object)(object)component2 != (Object)null)
					{
						foreach (Drop drop in component2.m_drops)
						{
							ItemDrop val = (((Object)(object)drop.m_prefab != (Object)null) ? drop.m_prefab.GetComponent<ItemDrop>() : null);
							if ((Object)(object)val != (Object)null)
							{
								value.Add(val.m_itemData.m_shared.m_name);
							}
						}
					}
				}
				Fish component3 = prefab.GetComponent<Fish>();
				ItemDrop component4 = prefab.GetComponent<ItemDrop>();
				if ((Object)(object)component3 != (Object)null && (Object)(object)component4 != (Object)null)
				{
					s_fishItems[component3.m_name] = component4.m_itemData.m_shared.m_name;
				}
			}
		}
	}
	internal enum RowMode
	{
		Off,
		Discovered,
		All
	}
	internal enum SecretMode
	{
		Hidden,
		Progressive,
		All
	}
	[BepInPlugin("com.koendertjh.achievementpeek", "AchievementPeek", "1.0.0")]
	public class AchievementPeekPlugin : BaseUnityPlugin
	{
		public const string ModGuid = "com.koendertjh.achievementpeek";

		public const string ModName = "AchievementPeek";

		public const string ModVersion = "1.0.0";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<Boss> RequiredBoss;

		internal static ConfigEntry<RowMode> MissingRows;

		internal static ConfigEntry<SecretMode> SecretAchievements;

		internal static ConfigEntry<bool> MissingFirst;

		private Harmony _harmony;

		internal static bool Active
		{
			get
			{
				if (Enabled.Value)
				{
					return BossProgress.IsDefeated(RequiredBoss.Value);
				}
				return false;
			}
		}

		private void Awake()
		{
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Enabled", true, "Master switch.");
			RequiredBoss = ((BaseUnityPlugin)this).Config.Bind<Boss>("2 - Spoilers", "RequiredBoss", Boss.None, "The mod stays off (the achievement screen looks like vanilla) until you have defeated this boss.");
			MissingRows = ((BaseUnityPlugin)this).Config.Bind<RowMode>("2 - Spoilers", "MissingRows", RowMode.Discovered, "What the details of an achievement show for conditions you haven't met yet. Off = vanilla '???'. Discovered = only things you have come across (known items, recipes and pieces, creatures you killed or whose drops you picked up); the rest is counted in an 'Only Odin knows' row. All = everything.");
			SecretAchievements = ((BaseUnityPlugin)this).Config.Bind<SecretMode>("2 - Spoilers", "SecretAchievements", SecretMode.Progressive, "Hidden = vanilla. Progressive = a boss achievement shows once you defeated the boss before it, Ashlands secrets after the Queen; the other secrets show right away. All = every secret shows.");
			MissingFirst = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - Display", "MissingFirst", true, "List the conditions you haven't met yet above the ones you have.");
			_harmony = new Harmony("com.koendertjh.achievementpeek");
			_harmony.PatchAll(typeof(AchievementPeekPlugin).Assembly);
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	[HarmonyPatch]
	internal static class SecretAchievements
	{
		private static readonly Dictionary<string, Boss> RevealAfter = new Dictionary<string, Boss>
		{
			{
				"Boss2Elder",
				Boss.Eikthyr
			},
			{
				"Boss3Bonemass",
				Boss.Elder
			},
			{
				"Boss4Moder",
				Boss.Bonemass
			},
			{
				"Boss5Yagluth",
				Boss.Moder
			},
			{
				"Boss6Queen",
				Boss.Yagluth
			},
			{
				"Boss7Fader",
				Boss.Queen
			},
			{
				"Boss8FrozenKing",
				Boss.Fader
			},
			{
				"DeathByObliterator",
				Boss.Queen
			},
			{
				"BuildDyrnwyn",
				Boss.Queen
			}
		};

		private static readonly Color SecretTint = new Color(0.78f, 0.7f, 0.95f);

		private static bool IsRevealed(Achievement achievement)
		{
			switch (AchievementPeekPlugin.SecretAchievements.Value)
			{
			case SecretMode.All:
				return true;
			case SecretMode.Progressive:
			{
				if (RevealAfter.TryGetValue(achievement.m_id, out var value))
				{
					return BossProgress.IsDefeated(value);
				}
				return true;
			}
			default:
				return false;
			}
		}

		private static bool ShouldUnhide(Achievement achievement)
		{
			if ((Object)(object)achievement != (Object)null && achievement.m_isSecret && !achievement.m_unlocked)
			{
				return IsRevealed(achievement);
			}
			return false;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(InventoryGui), "UpdateAchievementsList")]
		private static void BeforeList(out List<Achievement> __state)
		{
			__state = new List<Achievement>();
			if (!AchievementPeekPlugin.Active || (Object)(object)Achievements.m_instance == (Object)null)
			{
				return;
			}
			foreach (AchievementList achievementList in Achievements.m_instance.m_achievementLists)
			{
				foreach (Achievement achievement in achievementList.m_achievements)
				{
					if (ShouldUnhide(achievement))
					{
						achievement.m_isSecret = false;
						__state.Add(achievement);
					}
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(InventoryGui), "UpdateAchievementsList")]
		private static void TintSecrets(List<Achievement> __state, AchievementsGui ___m_achievementsPanel)
		{
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			if (__state.Count == 0)
			{
				return;
			}
			HashSet<string> hashSet = new HashSet<string>();
			foreach (Achievement item in __state)
			{
				hashSet.Add(Localization.instance.Localize(item.m_name));
			}
			foreach (GameObject achievements in ___m_achievementsPanel.m_achievementsList)
			{
				Transform obj = achievements.transform.Find("name");
				TMP_Text val = ((obj != null) ? ((Component)obj).GetComponent<TMP_Text>() : null);
				if ((Object)(object)val != (Object)null && hashSet.Contains(val.text))
				{
					((Graphic)val).color = SecretTint;
				}
			}
		}

		[HarmonyFinalizer]
		[HarmonyPatch(typeof(InventoryGui), "UpdateAchievementsList")]
		private static void AfterList(List<Achievement> __state)
		{
			Rehide(__state);
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(AchievementsGui), "OnOpenAchievementDetails")]
		private static void BeforeDetails(Achievement achievement, out List<Achievement> __state)
		{
			__state = new List<Achievement>();
			if (AchievementPeekPlugin.Active && ShouldUnhide(achievement))
			{
				achievement.m_isSecret = false;
				__state.Add(achievement);
			}
		}

		[HarmonyFinalizer]
		[HarmonyPatch(typeof(AchievementsGui), "OnOpenAchievementDetails")]
		private static void AfterDetails(List<Achievement> __state)
		{
			Rehide(__state);
		}

		private static void Rehide(List<Achievement> unhidden)
		{
			if (unhidden == null)
			{
				return;
			}
			foreach (Achievement item in unhidden)
			{
				item.m_isSecret = true;
			}
		}
	}
	internal static class PluginInfo
	{
		public const string Version = "1.0.0";
	}
}