Decompiled source of CosmeticTokenPriorityUse v4.0.0

CosmeticTokenPriorityUse.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: IgnoresAccessChecksTo("")]
[assembly: AssemblyCompany("REPOJP")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("zabuMod")]
[assembly: AssemblyTitle("zabuMod")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.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 REPOJP.CosmeticTokenPriorityUse
{
	[BepInPlugin("REPOJP.CosmeticTokenPriorityUse", "Cosmetic Token Priority Use", "1.0.0")]
	public sealed class CosmeticTokenPriorityUsePlugin : BaseUnityPlugin
	{
		public enum CompletedFallbackOrder
		{
			Descending,
			Ascending
		}

		[HarmonyPatch(typeof(CosmeticShopMachine), "Interact")]
		private static class CosmeticShopMachineInteractPatch
		{
			private static bool Prefix()
			{
				TryPrepareNextTokenForGacha();
				return true;
			}
		}

		public const string PluginGuid = "REPOJP.CosmeticTokenPriorityUse";

		public const string PluginName = "Cosmetic Token Priority Use";

		public const string PluginVersion = "1.0.0";

		private static readonly Rarity[] DescendingRarityOrder;

		private static readonly Rarity[] AscendingRarityOrder;

		private static ManualLogSource log;

		private static ConfigEntry<bool> enabledConfig;

		private static ConfigEntry<CompletedFallbackOrder> allCosmeticsUnlockedOrderConfig;

		private static ConfigEntry<bool> refreshTokenUiAfterReorderConfig;

		private static ConfigEntry<bool> debugLogSelectedTokenConfig;

		private static FieldInfo cosmeticTokensField;

		private static FieldInfo cosmeticUnlocksField;

		private static FieldInfo tokenObjectsField;

		private static FieldInfo tokenElementIndexField;

		private static FieldInfo tokenElementRarityField;

		private static FieldInfo tokenElementPositionTargetNextField;

		private Harmony harmony;

		private void Awake()
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Expected O, but got Unknown
			try
			{
				((Component)this).transform.parent = null;
				((Object)((Component)this).gameObject).hideFlags = (HideFlags)52;
				Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
				log = ((BaseUnityPlugin)this).Logger;
				BindConfig();
				CacheReflectionMembers();
				harmony = new Harmony("REPOJP.CosmeticTokenPriorityUse");
				harmony.PatchAll();
				log.LogInfo((object)"Cosmetic Token Priority Use v1.0.0 loaded.");
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Failure: Failed to load Cosmetic Token Priority Use\n" + ex));
			}
		}

		private void OnDestroy()
		{
			try
			{
				if (harmony != null)
				{
					harmony.UnpatchSelf();
					harmony = null;
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Failure: Failed to unload Cosmetic Token Priority Use\n" + ex));
			}
		}

		private void BindConfig()
		{
			enabledConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enables this mod.このMODを有効化します。");
			allCosmeticsUnlockedOrderConfig = ((BaseUnityPlugin)this).Config.Bind<CompletedFallbackOrder>("General", "AllCosmeticsUnlockedOrder", CompletedFallbackOrder.Descending, "Token order used when all cosmetics are already unlocked.全コスメ所持済み時のトークン使用順です。");
			refreshTokenUiAfterReorderConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "RefreshTokenUIAfterReorder", true, "Updates token UI after internally moving the selected token.選択トークンを内部移動した後にトークンUIを更新します。");
			debugLogSelectedTokenConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugLogSelectedToken", false, "Outputs selected token rarity to the BepInEx log.選択されたトークンレアリティをBepInExログに出力します。");
		}

		private static void CacheReflectionMembers()
		{
			cosmeticTokensField = AccessTools.Field(typeof(MetaManager), "cosmeticTokens");
			cosmeticUnlocksField = AccessTools.Field(typeof(MetaManager), "cosmeticUnlocks");
			tokenObjectsField = AccessTools.Field(typeof(CosmeticTokenUI), "tokenObjects");
			tokenElementIndexField = AccessTools.Field(typeof(CosmeticTokenUIElement), "index");
			tokenElementRarityField = AccessTools.Field(typeof(CosmeticTokenUIElement), "rarity");
			tokenElementPositionTargetNextField = AccessTools.Field(typeof(CosmeticTokenUIElement), "positionTargetNext");
		}

		internal static void TryPrepareNextTokenForGacha()
		{
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (enabledConfig == null || !enabledConfig.Value)
				{
					return;
				}
				if (!HasRequiredReflectionMembers())
				{
					WriteWarning("Failure: Required fields were not found.");
					return;
				}
				MetaManager instance = MetaManager.instance;
				if ((Object)(object)instance == (Object)null)
				{
					return;
				}
				List<int> cosmeticTokens = GetCosmeticTokens(instance);
				if (cosmeticTokens == null || cosmeticTokens.Count <= 0)
				{
					return;
				}
				List<CosmeticAsset> cosmeticAssets = instance.cosmeticAssets;
				if (cosmeticAssets == null || cosmeticAssets.Count <= 0)
				{
					return;
				}
				List<int> cosmeticUnlocks = GetCosmeticUnlocks(instance);
				if (cosmeticUnlocks == null)
				{
					return;
				}
				HashSet<int> unlockedSet = new HashSet<int>(cosmeticUnlocks);
				string reason;
				int num = SelectTokenIndex(cosmeticTokens, cosmeticAssets, unlockedSet, out reason);
				if (num < 0 || num >= cosmeticTokens.Count)
				{
					WriteDebug("No eligible cosmetic token was selected.");
					return;
				}
				Rarity val = (Rarity)cosmeticTokens[num];
				if (num != cosmeticTokens.Count - 1)
				{
					MoveTokenToEnd(cosmeticTokens, num);
					RefreshTokenUIAfterReorder(num, cosmeticTokens);
				}
				WriteDebug("Selected token: " + ((object)(Rarity)(ref val)).ToString() + " / Reason: " + reason);
			}
			catch (Exception ex)
			{
				WriteWarning("Failure: Failed to prepare cosmetic token priority.\n" + ex);
			}
		}

		private static bool HasRequiredReflectionMembers()
		{
			return cosmeticTokensField != null && cosmeticUnlocksField != null;
		}

		private static List<int> GetCosmeticTokens(MetaManager metaManager)
		{
			object value = cosmeticTokensField.GetValue(metaManager);
			return value as List<int>;
		}

		private static List<int> GetCosmeticUnlocks(MetaManager metaManager)
		{
			object value = cosmeticUnlocksField.GetValue(metaManager);
			return value as List<int>;
		}

		private static int SelectTokenIndex(List<int> tokens, List<CosmeticAsset> assets, HashSet<int> unlockedSet, out string reason)
		{
			int num = SelectTokenIndexForLockedCosmetic(tokens, assets, unlockedSet, DescendingRarityOrder, out reason);
			if (num >= 0)
			{
				return num;
			}
			if (AreAllValidCosmeticsUnlocked(assets, unlockedSet))
			{
				Rarity[] completedFallbackOrder = GetCompletedFallbackOrder();
				int num2 = SelectTokenIndexByOwnedTokenOnly(tokens, completedFallbackOrder);
				if (num2 >= 0)
				{
					reason = "All cosmetics unlocked fallback";
					return num2;
				}
			}
			reason = "No eligible token";
			return -1;
		}

		private static int SelectTokenIndexForLockedCosmetic(List<int> tokens, List<CosmeticAsset> assets, HashSet<int> unlockedSet, Rarity[] order, out string reason)
		{
			//IL_0009: 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_001d: Unknown result type (might be due to invalid IL or missing references)
			foreach (Rarity rarity in order)
			{
				if (HasLockedCosmeticForRarity(assets, unlockedSet, rarity))
				{
					int num = FindLatestTokenIndexForRarity(tokens, rarity);
					if (num >= 0)
					{
						reason = "Locked cosmetic exists";
						return num;
					}
				}
			}
			reason = "No locked cosmetic token match";
			return -1;
		}

		private static int SelectTokenIndexByOwnedTokenOnly(List<int> tokens, Rarity[] order)
		{
			for (int i = 0; i < order.Length; i++)
			{
				int num = FindLatestTokenIndexForRarity(tokens, order[i]);
				if (num >= 0)
				{
					return num;
				}
			}
			return -1;
		}

		private static Rarity[] GetCompletedFallbackOrder()
		{
			if (allCosmeticsUnlockedOrderConfig != null && allCosmeticsUnlockedOrderConfig.Value == CompletedFallbackOrder.Ascending)
			{
				return AscendingRarityOrder;
			}
			return DescendingRarityOrder;
		}

		private static int FindLatestTokenIndexForRarity(List<int> tokens, Rarity rarity)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Expected I4, but got Unknown
			int num = (int)rarity;
			for (int num2 = tokens.Count - 1; num2 >= 0; num2--)
			{
				if (tokens[num2] == num)
				{
					return num2;
				}
			}
			return -1;
		}

		private static bool HasLockedCosmeticForRarity(List<CosmeticAsset> assets, HashSet<int> unlockedSet, Rarity rarity)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < assets.Count; i++)
			{
				CosmeticAsset val = assets[i];
				if (IsValidCosmeticAsset(val) && val.rarity == rarity && !unlockedSet.Contains(i))
				{
					return true;
				}
			}
			return false;
		}

		private static bool AreAllValidCosmeticsUnlocked(List<CosmeticAsset> assets, HashSet<int> unlockedSet)
		{
			bool result = false;
			for (int i = 0; i < assets.Count; i++)
			{
				CosmeticAsset asset = assets[i];
				if (IsValidCosmeticAsset(asset))
				{
					result = true;
					if (!unlockedSet.Contains(i))
					{
						return false;
					}
				}
			}
			return result;
		}

		private static bool IsValidCosmeticAsset(CosmeticAsset asset)
		{
			try
			{
				return (Object)(object)asset != (Object)null && asset.prefab != null && asset.prefab.IsValid();
			}
			catch
			{
				return false;
			}
		}

		private static void MoveTokenToEnd(List<int> tokens, int selectedIndex)
		{
			int item = tokens[selectedIndex];
			tokens.RemoveAt(selectedIndex);
			tokens.Add(item);
		}

		private static void RefreshTokenUIAfterReorder(int movedIndex, List<int> tokens)
		{
			try
			{
				if (refreshTokenUiAfterReorderConfig == null || !refreshTokenUiAfterReorderConfig.Value)
				{
					return;
				}
				CosmeticTokenUI instance = CosmeticTokenUI.instance;
				if ((Object)(object)instance == (Object)null || tokenObjectsField == null)
				{
					return;
				}
				object value = tokenObjectsField.GetValue(instance);
				if (!(value is List<CosmeticTokenUIElement> list) || list.Count != tokens.Count)
				{
					instance.Setup();
					return;
				}
				if (movedIndex >= 0 && movedIndex < list.Count && movedIndex != list.Count - 1)
				{
					CosmeticTokenUIElement item = list[movedIndex];
					list.RemoveAt(movedIndex);
					list.Add(item);
				}
				for (int i = 0; i < list.Count; i++)
				{
					CosmeticTokenUIElement val = list[i];
					if (!((Object)(object)val == (Object)null))
					{
						if (tokenElementIndexField != null)
						{
							tokenElementIndexField.SetValue(val, i);
						}
						if (tokenElementRarityField != null)
						{
							tokenElementRarityField.SetValue(val, (object)(Rarity)tokens[i]);
						}
						if (tokenElementPositionTargetNextField != null)
						{
							tokenElementPositionTargetNextField.SetValue(val, null);
						}
						CosmeticTokenUIElement val2 = null;
						if (i > 0)
						{
							val2 = list[i - 1];
						}
						val.Setup(val2);
					}
				}
				if (list.Count > 0)
				{
					CosmeticTokenUIElement val3 = list[list.Count - 1];
					if ((Object)(object)val3 != (Object)null)
					{
						val3.JumpSet(8f);
						val3.SheenSet(0.05f);
					}
				}
			}
			catch (Exception ex)
			{
				WriteWarning("Failure: Failed to refresh cosmetic token UI after reorder.\n" + ex);
			}
		}

		private static void WriteDebug(string message)
		{
			if (debugLogSelectedTokenConfig != null && debugLogSelectedTokenConfig.Value && log != null)
			{
				log.LogInfo((object)message);
			}
		}

		private static void WriteWarning(string message)
		{
			if (log != null)
			{
				log.LogWarning((object)message);
			}
		}

		static CosmeticTokenPriorityUsePlugin()
		{
			Rarity[] array = new Rarity[4];
			RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			DescendingRarityOrder = (Rarity[])(object)array;
			Rarity[] array2 = new Rarity[4];
			RuntimeHelpers.InitializeArray(array2, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			AscendingRarityOrder = (Rarity[])(object)array2;
		}
	}
}