Decompiled source of MoreMerchantSlots v0.1.0

plugins/MoreMerchantSlots.dll

Decompiled 3 hours ago
using System;
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 BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
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("MoreMerchantSlots")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0+01ef0b4c091a4bc41ca5ce31a504e08f9176a069")]
[assembly: AssemblyProduct("MoreMerchantSlots")]
[assembly: AssemblyTitle("MoreMerchantSlots")]
[assembly: AssemblyVersion("0.1.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 MoreMerchantSlots
{
	internal static class ExtraGoods
	{
		[HarmonyPatch(typeof(SaveManager), "GenerateMerchantGoodsAtDistance")]
		private static class ApplyOnce
		{
			private static void Prefix(SaveManager __instance, int distance, int branch, int index)
			{
				HashSet<string> hashSet = Names(Merchants).ToHashSet<string>(StringComparer.OrdinalIgnoreCase);
				foreach (MerchantData item in __instance.GetRunState().MerchantsAtDistance(distance, branch, index))
				{
					if (hashSet.Contains(((Object)item).name) && done.Add(item))
					{
						Append(item);
					}
				}
			}
		}

		private static ConfigEntry<string> Merchants = null;

		private static ConfigEntry<string> Pools = null;

		private static readonly HashSet<MerchantData> done = new HashSet<MerchantData>();

		public static void Init(ConfigFile config)
		{
			Merchants = config.Bind<string>("Goods", "Merchants", "UnitUpgradeMerchant,SpellUpgradeMerchant,SoulSavior_UpgradedUnitUpgradeMerchant,SoulSavior_UpgradedSpellUpgradeMerchant", "Asset names of the merchants to stock up, comma-separated: the Merchants of Steel and Magic and their Soul Savior upgrades by default. Takes effect on the next ring generated; the game must be restarted for a change to apply.");
			Pools = config.Bind<string>("Goods", "Pools", "UnitUpgradePoolCommon,UnitUpgradePoolRare,SpellUpgradePoolCommon,SpellUpgradePoolRare", "Asset names of the upgrade pools each merchant gets one more good of, comma-separated; a merchant only gets the pools it already sells from. The Soul Savior merchants' pools carry a SoulSavior_ prefix, matched too. Same timing as Merchants.");
		}

		private static IEnumerable<string> Names(ConfigEntry<string> entry)
		{
			return from n in entry.Value.Split(',', StringSplitOptions.RemoveEmptyEntries)
				select n.Trim();
		}

		private static IList<MerchantRewardData>? Rewards(MerchantData merchant)
		{
			return AccessTools.Field(typeof(MerchantData), "rewards").GetValue(merchant) as IList<MerchantRewardData>;
		}

		private static string? PoolName(MerchantRewardData reward)
		{
			RewardData rewardData = reward.RewardData;
			EnhancerPoolRewardData val = (EnhancerPoolRewardData)(object)((rewardData is EnhancerPoolRewardData) ? rewardData : null);
			if (val == null)
			{
				return null;
			}
			object? value = AccessTools.Field(typeof(EnhancerPoolRewardData), "relicPool").GetValue(val);
			object? obj = ((value is EnhancerPool) ? value : null);
			if (obj == null)
			{
				return null;
			}
			return ((Object)obj).name;
		}

		private static bool Draws(MerchantRewardData reward, string pool)
		{
			string text = PoolName(reward);
			if (text != null)
			{
				if (!text.Equals(pool, StringComparison.OrdinalIgnoreCase))
				{
					return text.Equals("SoulSavior_" + pool, StringComparison.OrdinalIgnoreCase);
				}
				return true;
			}
			return false;
		}

		private static void Append(MerchantData merchant)
		{
			IList<MerchantRewardData> rewards = Rewards(merchant);
			if (rewards == null)
			{
				Plugin.Logger.LogWarning((object)(((Object)merchant).name + ": no rewards list; left as is."));
				return;
			}
			List<MerchantRewardData> list = (from pool in Names(Pools)
				select ((IEnumerable<MerchantRewardData>)rewards).FirstOrDefault((Func<MerchantRewardData, bool>)((MerchantRewardData r) => (Object)(object)r.RequiredMutator == (Object)null && Draws(r, pool)))).OfType<MerchantRewardData>().ToList();
			if (list.Count == 0)
			{
				Plugin.Logger.LogWarning((object)(((Object)merchant).name + ": sells from none of the pools " + Pools.Value + "; left as is."));
				return;
			}
			foreach (MerchantRewardData item in list)
			{
				rewards.Add(item);
			}
			Plugin.Logger.LogInfo((object)string.Format("{0}: +{1} ({2} rewards).", ((Object)merchant).name, string.Join(", +", list.Select(PoolName)), rewards.Count));
		}
	}
	internal static class GoodsLayout
	{
		[HarmonyPatch(typeof(MerchantScreen), "Initialize")]
		private static class OnInitialize
		{
			private static void Postfix(MerchantScreen __instance)
			{
				Fit(__instance);
			}
		}

		[HarmonyPatch(typeof(MerchantScreen), "RerollItems")]
		private static class OnReroll
		{
			private static void Postfix(MerchantScreen __instance)
			{
				Fit(__instance);
			}
		}

		private const int Columns = 3;

		private const float Scale = 2f / 3f;

		private const float VanillaCellWidth = 596f;

		private const float VanillaBackgroundWidth = 608f;

		private const float VanillaIconWidth = 176f;

		private const float VanillaIconLeft = 20f;

		private const float BoxGap = -12f;

		private const float BoxRight = 40f;

		private const float VanillaFontMin = 20f;

		private const float VanillaFontMax = 32f;

		private static ConfigEntry<bool> Enabled = null;

		private static ConfigEntry<int> DescriptionFontSize = null;

		private static ConfigEntry<int> IconWidth = null;

		private static ConfigEntry<int> IconLeft = null;

		private static readonly FieldRef<MerchantScreen, MerchantScreenContent> Content = AccessTools.FieldRefAccess<MerchantScreen, MerchantScreenContent>("content");

		private static readonly FieldRef<MerchantScreen, List<MerchantGoodUIBase>> GoodUIs = AccessTools.FieldRefAccess<MerchantScreen, List<MerchantGoodUIBase>>("goodUIs");

		private static readonly FieldRef<MerchantGoodDetailsUI, RewardDetailsUI> RewardUI = AccessTools.FieldRefAccess<MerchantGoodDetailsUI, RewardDetailsUI>("rewardUI");

		private static readonly FieldRef<RewardDetailsUI, RelicInfoUI> RelicUI = AccessTools.FieldRefAccess<RewardDetailsUI, RelicInfoUI>("relicUI");

		private static readonly FieldRef<RelicInfoUI, TMP_Text> DescriptionLabel = AccessTools.FieldRefAccess<RelicInfoUI, TMP_Text>("descriptionLabel");

		private static MerchantScreen? current;

		public static void Init(ConfigFile config)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Expected O, but got Unknown
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Expected O, but got Unknown
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Expected O, but got Unknown
			Enabled = config.Bind<bool>("Layout", "FitGoods", true, "Lay the merchant screen's goods out in three narrower columns when there are more than four. Off leaves the extra goods overflowing the screen.");
			DescriptionFontSize = config.Bind<int>("Layout", "DescriptionFontSize", 26, new ConfigDescription("Largest font size of a good's description on a narrowed screen (the game's is 32); a description that still doesn't fit shrinks further, down to half of it.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 32), Array.Empty<object>()));
			IconWidth = config.Bind<int>("Layout", "IconWidth", 120, new ConfigDescription("Width kept for a good's icon on a narrowed screen (the game's is 176, the icon itself 104); the description box gets what is freed.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(104, 176), Array.Empty<object>()));
			IconLeft = config.Bind<int>("Layout", "IconLeft", 8, new ConfigDescription("Gap between a good's left edge and its icon's frame on a narrowed screen (the game's is 20); the description box moves along.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 40), Array.Empty<object>()));
			config.SettingChanged += delegate(object _, SettingChangedEventArgs e)
			{
				if (e.ChangedSetting.Definition.Section == "Layout" && (Object)(object)current != (Object)null && ((Component)current).gameObject.activeInHierarchy)
				{
					Fit(current);
				}
			};
		}

		private static void Fit(MerchantScreen screen)
		{
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			current = screen;
			MerchantScreenContent obj = GameObjectUtil.OrNull<MerchantScreenContent>(Content.Invoke(screen));
			LayoutGroup obj2 = ((obj != null) ? obj.GoodsContainer : null);
			GridLayoutGroup val = (GridLayoutGroup)(object)((obj2 is GridLayoutGroup) ? obj2 : null);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			List<MerchantGoodDetailsUI> list = (from g in GoodUIs.Invoke(screen).OfType<MerchantGoodDetailsUI>()
				where ((MerchantGoodUIBase)g).GoodState != null
				select g).ToList();
			bool flag = Enabled.Value && list.Count > 4;
			val.constraint = (Constraint)1;
			val.constraintCount = (flag ? 3 : 2);
			val.cellSize = new Vector2(flag ? 397.33334f : 596f, val.cellSize.y);
			foreach (MerchantGoodDetailsUI item in list)
			{
				RectTransform val2 = Background((MerchantGoodUIBase)(object)item);
				if ((Object)(object)val2 != (Object)null)
				{
					val2.sizeDelta = new Vector2(flag ? 405.33334f : 608f, val2.sizeDelta.y);
				}
				FitPanel(item, flag);
			}
		}

		private static void FitPanel(MerchantGoodDetailsUI good, bool narrow)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			//IL_00c4: 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_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			RelicInfoUI val = GameObjectUtil.OrNull<RelicInfoUI>(RelicUI.Invoke(GameObjectUtil.OrNull<RewardDetailsUI>(RewardUI.Invoke(good))));
			if (!((Object)(object)val == (Object)null))
			{
				RectTransform val2 = (RectTransform)((Component)val).transform;
				TMP_Text val3 = GameObjectUtil.OrNull<TMP_Text>(DescriptionLabel.Invoke(val));
				TMP_Text obj = GameObjectUtil.OrNull<TMP_Text>(val3);
				Transform obj2 = ((obj != null) ? obj.transform.parent : null);
				RectTransform val4 = (RectTransform)(object)((obj2 is RectTransform) ? obj2 : null);
				Transform obj3 = GameObjectUtil.OrNull<Transform>(val.IconRoot);
				Transform obj4 = ((obj3 != null) ? obj3.parent : null);
				RectTransform val5 = (RectTransform)(object)((obj4 is RectTransform) ? obj4 : null);
				float num = (narrow ? ((float)IconWidth.Value) : 176f);
				float num2 = (narrow ? ((float)IconLeft.Value) : 20f);
				val2.sizeDelta = new Vector2(narrow ? 397.33334f : 596f, val2.sizeDelta.y);
				if ((Object)(object)val5 != (Object)null)
				{
					val5.sizeDelta = new Vector2(num, val5.sizeDelta.y);
					val5.anchoredPosition = new Vector2(num2, val5.anchoredPosition.y);
				}
				if ((Object)(object)val4 != (Object)null)
				{
					val4.anchorMin = new Vector2(0f, val4.anchorMin.y);
					val4.anchorMax = new Vector2(1f, val4.anchorMax.y);
					val4.offsetMin = new Vector2(num2 + num + -12f, val4.offsetMin.y);
					val4.offsetMax = new Vector2(-40f, val4.offsetMax.y);
				}
				if ((Object)(object)val3 != (Object)null)
				{
					val3.enableAutoSizing = true;
					val3.fontSizeMax = (narrow ? ((float)DescriptionFontSize.Value) : 32f);
					val3.fontSizeMin = (narrow ? ((float)DescriptionFontSize.Value / 2f) : 20f);
				}
			}
		}

		private static RectTransform? Background(MerchantGoodUIBase good)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			//IL_001b: 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)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			foreach (RectTransform item in ((Component)good).transform)
			{
				RectTransform val = item;
				if (val.anchorMin.x == val.anchorMax.x && val.sizeDelta.x > 0f && (Object)(object)((Component)val).GetComponent<Image>() != (Object)null)
				{
					return val;
				}
			}
			return null;
		}
	}
	[BepInPlugin("SpecialCircumstances.MoreMerchantSlots", "MoreMerchantSlots", "0.1.0")]
	public class Plugin : BaseUnityPlugin
	{
		internal static ManualLogSource Logger = new ManualLogSource("SpecialCircumstances.MoreMerchantSlots");

		private const float ConfigPollSeconds = 1f;

		private DateTime configWriteTime;

		private float nextConfigPoll;

		public void Awake()
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			Logger = ((BaseUnityPlugin)this).Logger;
			ExtraGoods.Init(((BaseUnityPlugin)this).Config);
			GoodsLayout.Init(((BaseUnityPlugin)this).Config);
			new Harmony("SpecialCircumstances.MoreMerchantSlots").PatchAll();
			configWriteTime = ReadConfigWriteTime();
			Logger.LogInfo((object)"Plugin SpecialCircumstances.MoreMerchantSlots is loaded!");
		}

		public void Update()
		{
			PollConfig();
		}

		private void PollConfig()
		{
			if (Time.realtimeSinceStartup < nextConfigPoll)
			{
				return;
			}
			nextConfigPoll = Time.realtimeSinceStartup + 1f;
			DateTime dateTime = ReadConfigWriteTime();
			if (!(dateTime == configWriteTime))
			{
				configWriteTime = dateTime;
				try
				{
					((BaseUnityPlugin)this).Config.Reload();
				}
				catch (Exception ex)
				{
					Logger.LogWarning((object)("Could not reload the config file: " + ex.Message));
					return;
				}
				configWriteTime = ReadConfigWriteTime();
				Logger.LogDebug((object)"Config reloaded.");
			}
		}

		private DateTime ReadConfigWriteTime()
		{
			try
			{
				return File.GetLastWriteTimeUtc(((BaseUnityPlugin)this).Config.ConfigFilePath);
			}
			catch (Exception ex)
			{
				Logger.LogDebug((object)("Could not stat the config file: " + ex.Message));
				return configWriteTime;
			}
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "SpecialCircumstances.MoreMerchantSlots";

		public const string PLUGIN_NAME = "MoreMerchantSlots";

		public const string PLUGIN_VERSION = "0.1.0";
	}
}