Decompiled source of HKS Item Randomizer v1.0.0

HKSilksong_Randomizer.dll

Decompiled 2 weeks 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 BepInEx;
using BepInEx.Logging;
using GlobalSettings;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.SceneManagement;

[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.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("0.0.0.0")]
[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 HKSilksong_Randomizer
{
	[BepInPlugin("com.yourname.mysilksongmod", "HKSilksong_Randomizer", "0.5.5")]
	public class HKS_Randomizer : BaseUnityPlugin
	{
		private class PickupMessage
		{
			public string Text;

			public float Time;

			public PickupMessage(string t, float time)
			{
				Text = t;
				Time = time;
			}
		}

		[CompilerGenerated]
		private sealed class <CheckAndApplyMaskShardsIfReset>d__38 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public HKS_Randomizer <>4__this;

			private int <tries>5__1;

			private PlayerData <pd>5__2;

			private int <shards>5__3;

			private int <expectedHealthUpgrades>5__4;

			private int <expectedHeartPieces>5__5;

			private int <expectedMaxHealth>5__6;

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

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

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

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

			private bool MoveNext()
			{
				//IL_0088: Unknown result type (might be due to invalid IL or missing references)
				//IL_0092: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<tries>5__1 = 300;
					goto IL_0051;
				case 1:
					<>1__state = -1;
					goto IL_0051;
				case 2:
					{
						<>1__state = -1;
						<pd>5__2 = PlayerData.instance;
						if (<pd>5__2.maxHealth == 5 && <pd>5__2.heartPieces == 0 && upgradesGivenCount.TryGetValue("Mask Shard", out <shards>5__3) && <shards>5__3 > 0)
						{
							<expectedHealthUpgrades>5__4 = <shards>5__3 / 4;
							<expectedHeartPieces>5__5 = <shards>5__3 % 4;
							<expectedMaxHealth>5__6 = 5 + <expectedHealthUpgrades>5__4;
							<pd>5__2.maxHealth = <expectedMaxHealth>5__6;
							<pd>5__2.maxHealthBase = <expectedMaxHealth>5__6;
							<pd>5__2.health = Mathf.Min(<pd>5__2.health, <pd>5__2.maxHealth);
							<pd>5__2.heartPieces = <expectedHeartPieces>5__5;
							ToolItemManager.SendEquippedChangedEvent(true);
							Log.LogInfo((object)$"[MASK PERSIST] Detected reset stats (5/0), reapplied {<shards>5__3} mask shards: maxHealth={<pd>5__2.maxHealth}, heartPieces={<pd>5__2.heartPieces}");
						}
						return false;
					}
					IL_0051:
					if (PlayerData.instance == null && <tries>5__1-- > 0)
					{
						<>2__current = null;
						<>1__state = 1;
						return true;
					}
					if (PlayerData.instance == null)
					{
						return false;
					}
					<>2__current = (object)new WaitForSeconds(0.5f);
					<>1__state = 2;
					return true;
				}
			}

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

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

		[CompilerGenerated]
		private sealed class <RefreshSilkUI>d__53 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public HKS_Randomizer <>4__this;

			private SilkSpool <silkSpool>5__1;

			private Exception <e>5__2;

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

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

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

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<silkSpool>5__1 = null;
				<e>5__2 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = null;
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					try
					{
						<silkSpool>5__1 = SilkSpool.Instance ?? GameCameras.instance?.silkSpool;
						if ((Object)(object)<silkSpool>5__1 != (Object)null)
						{
							<silkSpool>5__1.DrawSpool();
							Log.LogInfo((object)"Refreshed silk spool HUD (delayed)");
						}
						else
						{
							Log.LogWarning((object)"SilkSpool instance not found - silk UI may not refresh");
						}
						<silkSpool>5__1 = null;
					}
					catch (Exception ex)
					{
						<e>5__2 = ex;
						Log.LogError((object)("Error refreshing silk UI: " + <e>5__2.Message));
					}
					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();
			}
		}

		private static ManualLogSource Log;

		private static CollectableItem[] memorizedCollectables;

		private static ToolItem[] memorizedTools;

		private static ToolCrest[] memorizedCrests;

		internal static ShopOwner[] memorizedShopOwners;

		private static string[] availablePowerups = new string[7] { "hasDoubleJump", "hasChargeSlash", "hasSuperJump", "hasWalljump", "hasBrolly", "hasDash", "hasNeedolin" };

		private static string[] availableMaps = new string[28]
		{
			"HasMossGrottoMap", "HasWildsMap", "HasBoneforestMap", "HasDocksMap", "HasGreymoorMap", "HasBellhartMap", "HasShellwoodMap", "HasCrawlMap", "HasHuntersNestMap", "HasJudgeStepsMap",
			"HasDustpensMap", "HasSlabMap", "HasPeakMap", "HasCitadelUnderstoreMap", "HasCoralMap", "HasSwampMap", "HasCloverMap", "HasAbyssMap", "HasHangMap", "HasSongGateMap",
			"HasHallsMap", "HasWardMap", "HasCogMap", "HasLibraryMap", "HasCradleMap", "HasArboriumMap", "HasAqueductMap", "HasWeavehomeMap"
		};

		private static Dictionary<string, int> availableUpgrades = new Dictionary<string, int>
		{
			{ "Mask Shard", 20 },
			{ "Spool Fragment", 18 },
			{ "Silk Heart", 3 }
		};

		private static Dictionary<string, int> upgradesGivenCount = new Dictionary<string, int>();

		public static bool itemsMemorized = false;

		private static HashSet<string> givenItems = new HashSet<string>();

		private static List<string> givenItemsInOrder = new List<string>();

		private static string givenItemsFilePath;

		private static string randomizedShopsFilePath;

		internal static Dictionary<string, List<(string itemName, int originalCost)>> randomizedShops = new Dictionary<string, List<(string, int)>>();

		internal static Dictionary<string, int> shopItemPool = new Dictionary<string, int>();

		private static Dictionary<string, int> bulkItemQuantities = new Dictionary<string, int>
		{
			{ "Song Pilgrim Cloak", 99 },
			{ "Enemy Morsel Speared", 99 },
			{ "Pins", 99 },
			{ "Shell Flower", 99 },
			{ "Enemy Morsel Seared", 99 },
			{ "Roach Corpse Item", 99 },
			{ "Enemy Morsel Shredded", 99 },
			{ "Fine Pin", 99 },
			{ "Pilgrim Rag", 99 },
			{ "Rock Roller Item", 99 },
			{ "Common Spine", 99 },
			{ "Crow Feather", 99 },
			{ "Silver Bellclapper", 99 },
			{ "Plasmium Blood", 99 },
			{ "Plasmium", 99 },
			{ "Simple Key", 4 },
			{ "Rosary_Set_Large", 5 },
			{ "Cog Heart Pieces", 3 },
			{ "Rosary_Set_Huge_White", 5 },
			{ "Crest Socket Unlocker", 20 },
			{ "Tool Pouch&Kit Inv", 8 },
			{ "Shard Pouch", 4 },
			{ "Pale_Oil", 4 },
			{ "Rosary_Set_Frayed", 5 },
			{ "Rosary_Set_Small", 5 },
			{ "Rosary_Set_Medium", 5 },
			{ "Tool Metal", 8 },
			{ "Silk Grub", 10 },
			{ "Slab Key", 1 },
			{ "R Seal chit", 4 },
			{ "R Weaver Totem", 3 },
			{ "R Bone Record", 4 },
			{ "R Weaver Record", 3 },
			{ "R Ancient Egg", 1 },
			{ "R Psalm Cylinder", 5 },
			{ "R Librarian Melody Cylinder", 1 },
			{ "Silk Snare", 1 },
			{ "Snare Soul Swamp Bug", 1 }
		};

		private static Dictionary<string, int> bulkItemGivenCount = new Dictionary<string, int>();

		private static Dictionary<string, int> bulkItemFailedAttempts = new Dictionary<string, int>();

		private static Dictionary<string, int> bulkItemPityThreshold = new Dictionary<string, int>
		{
			{ "Common Spine", 9 },
			{ "Pilgrim Rag", 14 },
			{ "Rock Roller Item", 19 },
			{ "Slab Key", 23 },
			{ "Silver Bellclapper", 24 },
			{ "Shell Flower", 27 },
			{ "Crow Feather", 29 },
			{ "Simple Key", 31 },
			{ "Roach Corpse Item", 33 },
			{ "Plasmium", 35 },
			{ "Fine Pin", 41 },
			{ "R Librarian Melody Cylinder", 44 },
			{ "Song Pilgrim Cloak", 47 },
			{ "Enemy Morsel Speared", 49 },
			{ "Enemy Morsel Shredded", 54 },
			{ "Enemy Morsel Seared", 59 },
			{ "White Key", 64 },
			{ "Snare Soul Swamp Bug", 67 },
			{ "Silk Snare", 70 },
			{ "Plasmium Blood", 71 }
		};

		private List<PickupMessage> _recentPickupMessages = new List<PickupMessage>();

		private const int kMaxPickupMessages = 10;

		private const float kPickupMessageDuration = 10f;

		public static HKS_Randomizer Instance { get; private set; }

		private void ShowInGamePickup(string text)
		{
			try
			{
				_recentPickupMessages.Add(new PickupMessage(text, Time.time));
				if (_recentPickupMessages.Count > 10)
				{
					_recentPickupMessages.RemoveAt(0);
				}
			}
			catch
			{
			}
		}

		private void SyncCrossCategoryOnGive(int givenItemType, string name)
		{
			try
			{
				if (givenItemType == 0 && memorizedTools != null && memorizedTools.Any((ToolItem t) => (Object)(object)t != (Object)null && t.name == name) && !givenItems.Contains("ToolItem: " + name))
				{
					givenItems.Add("ToolItem: " + name);
					givenItemsInOrder.Add("ToolItem: " + name);
					Log.LogInfo((object)("Sync: marked ToolItem '" + name + "' as given because collectable was given"));
				}
				if (givenItemType == 1 && memorizedCollectables != null && memorizedCollectables.Any((CollectableItem c) => (Object)(object)c != (Object)null && ((Object)c).name == name) && !givenItems.Contains("CollectableItem: " + name))
				{
					givenItems.Add("CollectableItem: " + name);
					givenItemsInOrder.Add("CollectableItem: " + name);
					Log.LogInfo((object)("Sync: marked CollectableItem '" + name + "' as given because Tool was given"));
				}
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Error in SyncCrossCategoryOnGive for '" + name + "': " + ex.Message));
			}
		}

		private void OnGUI()
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Expected O, but got Unknown
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Expected O, but got Unknown
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_0200: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Expected O, but got Unknown
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (_recentPickupMessages == null || _recentPickupMessages.Count == 0)
				{
					return;
				}
				int num = 420;
				int num2 = 12;
				float num3 = num2;
				GUIStyle val = new GUIStyle(GUI.skin.box);
				GUIStyle val2 = new GUIStyle(GUI.skin.label);
				val2.fontSize = 20;
				val2.fontStyle = (FontStyle)1;
				val2.alignment = (TextAnchor)5;
				val2.normal.textColor = Color.white;
				val2.wordWrap = true;
				val2.richText = true;
				Color color = GUI.color;
				Color color2 = default(Color);
				((Color)(ref color2))..ctor(0f, 0f, 0f, 0.45f);
				Color color3 = default(Color);
				((Color)(ref color3))..ctor(0f, 0f, 0f, 0.75f);
				Rect val4 = default(Rect);
				Rect val5 = default(Rect);
				Rect val6 = default(Rect);
				for (int num4 = _recentPickupMessages.Count - 1; num4 >= 0; num4--)
				{
					PickupMessage pickupMessage = _recentPickupMessages[num4];
					if (Time.time - pickupMessage.Time > 10f)
					{
						_recentPickupMessages.RemoveAt(num4);
					}
					else
					{
						GUIContent val3 = new GUIContent(pickupMessage.Text);
						float num5 = val2.CalcHeight(val3, (float)num);
						float num6 = 8f;
						((Rect)(ref val4))..ctor((float)(Screen.width - num - num2 - 8), num3 - 6f, (float)(num + 16), num5 + num6);
						((Rect)(ref val5))..ctor((float)(Screen.width - num - num2 + 1), num3 + 1f, (float)num, num5);
						((Rect)(ref val6))..ctor((float)(Screen.width - num - num2), num3, (float)num, num5);
						GUI.color = color2;
						GUI.Box(val4, GUIContent.none, val);
						GUI.color = color3;
						GUI.Label(val5, val3, val2);
						GUI.color = Color.white;
						GUI.Label(val6, val3, val2);
						num3 += num5 + 8f;
					}
				}
				GUI.color = color;
			}
			catch
			{
			}
		}

		private bool CheckForPityItems(ref int itemType, ref string selectedName)
		{
			try
			{
				foreach (KeyValuePair<string, int> item in bulkItemPityThreshold)
				{
					string key = item.Key;
					int value = item.Value;
					if (!bulkItemQuantities.ContainsKey(key))
					{
						continue;
					}
					int num = (bulkItemGivenCount.ContainsKey(key) ? bulkItemGivenCount[key] : 0);
					if (num >= bulkItemQuantities[key])
					{
						continue;
					}
					int num2 = (bulkItemFailedAttempts.ContainsKey(key) ? bulkItemFailedAttempts[key] : 0);
					if (num2 >= value)
					{
						Log.LogInfo((object)$"Pity timer activated! Forcing {key} after {num2 + 1} attempts");
						int num3 = DetermineItemType(key);
						if (num3 != -1)
						{
							itemType = num3;
							selectedName = key;
							bulkItemFailedAttempts[key] = 0;
							return true;
						}
						Log.LogWarning((object)("Could not determine type for pity item: " + key));
					}
				}
				return false;
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Error in pity timer check: " + ex.Message));
				return false;
			}
		}

		private int DetermineItemType(string itemName)
		{
			try
			{
				if (memorizedCollectables != null)
				{
					CollectableItem[] array = memorizedCollectables;
					foreach (CollectableItem val in array)
					{
						if ((Object)(object)val != (Object)null && ((Object)val).name == itemName)
						{
							return 0;
						}
					}
				}
				if (memorizedTools != null)
				{
					ToolItem[] array2 = memorizedTools;
					foreach (ToolItem val2 in array2)
					{
						if ((Object)(object)val2 != (Object)null && val2.name == itemName)
						{
							return 1;
						}
					}
				}
				if (memorizedCrests != null)
				{
					ToolCrest[] array3 = memorizedCrests;
					foreach (ToolCrest val3 in array3)
					{
						if ((Object)(object)val3 != (Object)null && val3.name == itemName)
						{
							return 2;
						}
					}
				}
				return -1;
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Error determining item type for " + itemName + ": " + ex.Message));
				return -1;
			}
		}

		private void UpdateFailedAttempts(int itemType, string selectedName)
		{
			try
			{
				foreach (KeyValuePair<string, int> item in bulkItemPityThreshold)
				{
					string key = item.Key;
					if (!(selectedName == key) && bulkItemQuantities.ContainsKey(key))
					{
						int num = (bulkItemGivenCount.ContainsKey(key) ? bulkItemGivenCount[key] : 0);
						if (num < bulkItemQuantities[key])
						{
							int num2 = (bulkItemFailedAttempts.ContainsKey(key) ? bulkItemFailedAttempts[key] : 0);
							bulkItemFailedAttempts[key] = num2 + 1;
						}
					}
				}
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Error updating failed attempts: " + ex.Message));
			}
		}

		private void Testing()
		{
		}

		private void Awake()
		{
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			Log.LogInfo((object)"HKS Rando Awake()");
			string text = Path.Combine(Paths.GameRootPath, "SilksongRandomizerData");
			if (!Directory.Exists(text))
			{
				Directory.CreateDirectory(text);
			}
			givenItemsFilePath = Path.Combine(text, "given_items.txt");
			randomizedShopsFilePath = Path.Combine(text, "randomized_shops.txt");
			LoadGivenItems();
			LoadRandomizedShops();
			LoadShopItemPool();
			Harmony val = new Harmony("com.yourname.mysilksongmod");
			val.PatchAll();
			SceneManager.sceneLoaded += OnSceneLoaded;
		}

		private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			((MonoBehaviour)this).StartCoroutine(CheckAndApplyMaskShardsIfReset());
		}

		private void OnDestroy()
		{
			SceneManager.sceneLoaded -= OnSceneLoaded;
		}

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

		private void Update()
		{
			try
			{
				if (Input.GetKeyDown((KeyCode)288))
				{
					Log.LogInfo((object)"F7 pressed - Getting random item!");
					GetRandomItem();
				}
				if (Input.GetKeyDown((KeyCode)289))
				{
					Log.LogInfo((object)"F8 pressed - Memorizing items and toggling crest!");
					if (!itemsMemorized)
					{
						MemorizeAllItems();
					}
					ToggleCloaklessHunterCrest();
				}
				if (Input.GetKeyDown((KeyCode)290))
				{
					Log.LogInfo((object)"F9 pressed - Toggling silk soar crest!");
					ToggleSilkSoar();
				}
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Error in Update(): " + ex.Message));
			}
		}

		private void getRelicWithSameName(string relicDisplayName)
		{
			if (memorizedCollectables == null)
			{
				Log.LogWarning((object)"getRelicWithSameName: no memorizedCollectables");
				return;
			}
			Log.LogInfo((object)("getRelicWithSameName: searching for '" + relicDisplayName + "'"));
			CollectableItem[] array = memorizedCollectables;
			foreach (CollectableItem val in array)
			{
				CollectableItemRelicType val2 = (CollectableItemRelicType)(object)((val is CollectableItemRelicType) ? val : null);
				if (val2 == null || val2.Relics == null)
				{
					continue;
				}
				foreach (CollectableRelic relic in val2.Relics)
				{
					if ((Object)(object)relic == (Object)null)
					{
						continue;
					}
					try
					{
						if (!relic.IsInInventory && relic.DisplayName == relicDisplayName)
						{
							((SavedItem)relic).Get(true);
							Log.LogInfo((object)("getRelicWithSameName: gave " + (relic.DisplayName ?? ((Object)relic).name)));
							return;
						}
					}
					catch (Exception ex)
					{
						Log.LogWarning((object)("getRelicWithSameName: inspect/give failed for '" + (((relic != null) ? ((Object)relic).name : null) ?? "<null>") + "': " + ex.Message));
					}
				}
			}
			givenItems.Add("CollectableItem: " + relicDisplayName);
			givenItemsInOrder.Add("CollectableItem: " + relicDisplayName);
		}

		public void MemorizeAllItems()
		{
			try
			{
				memorizedCollectables = (CollectableItem[])(((object)Resources.FindObjectsOfTypeAll<CollectableItem>()?.Where((CollectableItem item) => (Object)(object)item != (Object)null && !string.IsNullOrEmpty(((Object)item).name)).ToArray()) ?? ((object)new CollectableItem[0]));
				memorizedTools = (ToolItem[])(((object)Resources.FindObjectsOfTypeAll<ToolItem>()?.Where((ToolItem tool) => (Object)(object)tool != (Object)null && !string.IsNullOrEmpty(tool.name)).ToArray()) ?? ((object)new ToolItem[0]));
				memorizedCrests = (ToolCrest[])(((object)Resources.FindObjectsOfTypeAll<ToolCrest>()?.Where((ToolCrest crest) => (Object)(object)crest != (Object)null && !string.IsNullOrEmpty(crest.name)).ToArray()) ?? ((object)new ToolCrest[0]));
				memorizedShopOwners = (ShopOwner[])(((object)Resources.FindObjectsOfTypeAll<ShopOwner>()?.Where((ShopOwner owner) => (Object)(object)owner != (Object)null && ((ShopOwnerBase)owner).Stock != null && ((ShopOwnerBase)owner).Stock.Length != 0).ToArray()) ?? ((object)new ShopOwner[0]));
				itemsMemorized = true;
				ManualLogSource log = Log;
				object[] array = new object[5];
				CollectableItem[] array2 = memorizedCollectables;
				array[0] = ((array2 != null) ? array2.Length : 0);
				ToolItem[] array3 = memorizedTools;
				array[1] = ((array3 != null) ? array3.Length : 0);
				ToolCrest[] array4 = memorizedCrests;
				array[2] = ((array4 != null) ? array4.Length : 0);
				ShopOwner[] array5 = memorizedShopOwners;
				array[3] = ((array5 != null) ? array5.Length : 0);
				string[] array6 = availablePowerups;
				array[4] = ((array6 != null) ? array6.Length : 0);
				log.LogInfo((object)string.Format("Memorized {0} CollectableItems, {1} ToolItems, {2} ToolCrests, {3} ShopOwners, {4} powerups", array));
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Error memorizing items: " + ex.Message));
				itemsMemorized = false;
			}
		}

		public void GetRandomItem()
		{
			try
			{
				if (!itemsMemorized)
				{
					MemorizeAllItems();
					if (!itemsMemorized)
					{
						Log.LogError((object)"Failed to memorize items!");
						return;
					}
				}
				if (memorizedCollectables == null || memorizedTools == null || memorizedCrests == null)
				{
					Log.LogError((object)"Item arrays are null! Try pressing F8 again.");
					return;
				}
				if (memorizedCollectables.Length == 0 && memorizedTools.Length == 0 && memorizedCrests.Length == 0)
				{
					Log.LogWarning((object)"No items found in memorized arrays! Giving currency instead.");
					CurrencyManager.AddCurrency(100, (CurrencyType)0, true);
					CurrencyManager.AddCurrency(100, (CurrencyType)1, true);
					ShowInGamePickup("Text not intended to be shown");
					return;
				}
				List<string> list = new List<string>();
				List<string> list2 = new List<string>();
				List<string> list3 = new List<string>();
				List<string> list4 = new List<string>();
				List<string> list5 = new List<string>();
				Log.LogInfo((object)"=== BUILDING ITEM POOL ===");
				Log.LogInfo((object)$"bulkItemGivenCount has {bulkItemGivenCount.Count} entries:");
				foreach (KeyValuePair<string, int> item in bulkItemGivenCount)
				{
					Log.LogInfo((object)$"  {item.Key}: {item.Value} given");
				}
				CollectableItem[] array = memorizedCollectables;
				foreach (CollectableItem val in array)
				{
					if (!((Object)(object)val != (Object)null))
					{
						continue;
					}
					if (bulkItemQuantities.ContainsKey(((Object)val).name))
					{
						int num = (bulkItemGivenCount.ContainsKey(((Object)val).name) ? bulkItemGivenCount[((Object)val).name] : 0);
						int num2 = bulkItemQuantities[((Object)val).name] - num;
						Log.LogInfo((object)$"[BULK CHECK] {((Object)val).name}: maxQty={bulkItemQuantities[((Object)val).name]}, givenCount={num}, remaining={num2}");
						if (num2 <= 0)
						{
							continue;
						}
						int num3 = bulkItemQuantities[((Object)val).name];
						if (num3 >= 50)
						{
							list.Add(((Object)val).name);
							Log.LogInfo((object)$"[BULK DEBUG] {((Object)val).name}: maxQty={num3}, given={num}, remaining={num2}, added 1x (bulk>=50)");
							continue;
						}
						for (int j = 0; j < num2; j++)
						{
							list.Add(((Object)val).name);
						}
						Log.LogInfo((object)$"[BULK DEBUG] {((Object)val).name}: maxQty={num3}, given={num}, remaining={num2}, added {num2}x (remaining*5)");
					}
					else if (!givenItems.Contains("CollectableItem: " + ((Object)val).name))
					{
						list.Add(((Object)val).name);
					}
				}
				ToolItem[] array2 = memorizedTools;
				foreach (ToolItem val2 in array2)
				{
					if ((Object)(object)val2 != (Object)null && !givenItems.Contains("ToolItem: " + val2.name))
					{
						list2.Add(val2.name);
					}
				}
				ToolCrest[] array3 = memorizedCrests;
				foreach (ToolCrest val3 in array3)
				{
					if ((Object)(object)val3 != (Object)null && !givenItems.Contains("ToolCrest: " + val3.name))
					{
						list3.Add(val3.name);
					}
				}
				string[] array4 = availablePowerups;
				foreach (string text in array4)
				{
					if (!givenItems.Contains("Powerup: " + text))
					{
						PlayerData instance = PlayerData.instance;
						if (instance != null && !instance.GetBool(text))
						{
							list4.Add(text);
						}
					}
				}
				string[] array5 = availableMaps;
				foreach (string text2 in array5)
				{
					if (!givenItems.Contains("Map: " + text2))
					{
						PlayerData instance2 = PlayerData.instance;
						if (instance2 != null && !instance2.GetBool(text2))
						{
							list5.Add(text2);
						}
					}
				}
				List<string> list6 = new List<string>();
				foreach (KeyValuePair<string, int> availableUpgrade in availableUpgrades)
				{
					string key = availableUpgrade.Key;
					int value = availableUpgrade.Value;
					int num4 = (upgradesGivenCount.ContainsKey(key) ? upgradesGivenCount[key] : 0);
					if (num4 < value)
					{
						int num5 = value - num4;
						int num6 = Mathf.Min(num5, 41);
						for (int num7 = 0; num7 < num6; num7++)
						{
							list6.Add(key);
						}
					}
				}
				if (list.Count + list2.Count + list3.Count + list4.Count + list5.Count + list6.Count == 0)
				{
					CurrencyManager.AddCurrency(100, (CurrencyType)0, true);
					CurrencyManager.AddCurrency(100, (CurrencyType)1, true);
					Log.LogInfo((object)"All items given! Added 100 rosaries and 100 shards!");
					ShowInGamePickup("Got all items! Here's some currency instead.");
					return;
				}
				List<(int, string)> list7 = new List<(int, string)>();
				foreach (string item2 in list)
				{
					list7.Add((0, item2));
				}
				foreach (string item3 in list2)
				{
					list7.Add((1, item3));
				}
				foreach (string item4 in list3)
				{
					list7.Add((2, item4));
				}
				foreach (string item5 in list4)
				{
					list7.Add((3, item5));
				}
				foreach (string item6 in list5)
				{
					list7.Add((4, item6));
				}
				foreach (string item7 in list6)
				{
					list7.Add((5, item7));
				}
				if (list7.Count == 0)
				{
					Log.LogWarning((object)"No available items found! This should not happen after totalAvailable check.");
					CurrencyManager.AddCurrency(50, (CurrencyType)0, true);
					CurrencyManager.AddCurrency(50, (CurrencyType)1, true);
					Log.LogInfo((object)"Added 50 rosaries and 50 shards as fallback!");
					ShowInGamePickup("Got all items! Here's some currency instead. Also this text isn't really supposed to be seen but it is what it is");
					return;
				}
				int itemType = -1;
				string selectedName = "";
				if (!CheckForPityItems(ref itemType, ref selectedName))
				{
					(itemType, selectedName) = list7[Random.Range(0, list7.Count)];
				}
				UpdateFailedAttempts(itemType, selectedName);
				switch (itemType)
				{
				case 0:
				{
					string collectableName = selectedName;
					if (collectableName == "Invalid Item Template" || collectableName == "Blue Goop Jar" || collectableName == "Dock Demo Key")
					{
						Log.LogInfo((object)("Got CollectableItem: " + collectableName + " - giving currency instead!"));
						CurrencyManager.AddCurrency(200, (CurrencyType)0, true);
						CurrencyManager.AddCurrency(200, (CurrencyType)1, true);
						Log.LogInfo((object)("Added 200 rosaries and 200 shards for " + collectableName + "."));
						ShowInGamePickup("You found " + collectableName + "!\n not sure what that is...\n but here's some currency instead.");
						givenItems.Add("CollectableItem: " + collectableName);
						givenItemsInOrder.Add("CollectableItem: " + collectableName);
						SaveGivenItems();
						break;
					}
					CollectableItem val6 = memorizedCollectables?.FirstOrDefault((Func<CollectableItem, bool>)((CollectableItem item) => (Object)(object)item != (Object)null && ((Object)item).name == collectableName));
					if ((Object)(object)val6 != (Object)null)
					{
						if (collectableName == "Quill")
						{
							PlayerData instance8 = PlayerData.instance;
							instance8.hasQuill = true;
							instance8.QuillState = 3;
							givenItems.Add("CollectableItem: " + collectableName);
							givenItemsInOrder.Add("CollectableItem: " + collectableName);
							SaveGivenItems();
							Log.LogInfo((object)"Gave Quill!");
							ShowInGamePickup("Quill");
							GetRandomItem();
							break;
						}
						if (collectableName == "Tool Pouch&Kit Inv")
						{
							PlayerData instance9 = PlayerData.instance;
							if (instance9 != null)
							{
								string text9;
								if (Random.Range(0, 2) == 0)
								{
									instance9.ToolPouchUpgrades++;
									text9 = "Tool Pouch";
									Log.LogInfo((object)$"Gave Tool Pouch upgrade! Total: {instance9.ToolPouchUpgrades}");
								}
								else
								{
									instance9.ToolKitUpgrades++;
									text9 = "Tool Kit";
									Log.LogInfo((object)$"Gave Tool Kit upgrade! Total: {instance9.ToolKitUpgrades}");
								}
								int num10 = bulkItemQuantities[collectableName];
								int num11 = (bulkItemGivenCount.ContainsKey(collectableName) ? bulkItemGivenCount[collectableName] : 0);
								bulkItemGivenCount[collectableName] = num11 + 1;
								givenItemsInOrder.Add($"BULK: {collectableName}: {bulkItemGivenCount[collectableName]}/{num10}");
								SaveGivenItems();
								ShowInGamePickup(text9);
								Log.LogInfo((object)$"[BULK UPDATE] Updated bulkItemGivenCount[{collectableName}] = {bulkItemGivenCount[collectableName]}");
							}
							break;
						}
						if (bulkItemQuantities.ContainsKey(collectableName))
						{
							int num12 = bulkItemQuantities[collectableName];
							if (num12 >= 50)
							{
								val6.Collect(num12, true);
								bulkItemGivenCount[collectableName] = num12;
								givenItemsInOrder.Add($"BULK: {collectableName}: {num12}");
								Log.LogInfo((object)$"Gave bulk CollectableItem: {collectableName} x{num12}");
								ShowInGamePickup($"{collectableName} x {num12}");
							}
							else
							{
								if (((object)val6).GetType() == typeof(CollectableItemRelicType))
								{
									getRelicWithSameName(val6.GetDisplayName((ReadSource)4));
									ShowInGamePickup(val6.GetDisplayName((ReadSource)4) ?? "");
								}
								else if (((Object)val6).name == "Slab Key")
								{
									PlayerData instance10 = PlayerData.instance;
									instance10.HasSlabKeyA = true;
									instance10.HasSlabKeyB = true;
									instance10.HasSlabKeyC = true;
									Log.LogInfo((object)"Gave Slab Key - all three keys granted!");
									ShowInGamePickup("A slab key sneaked into your pocket!");
									GetRandomItem();
								}
								else
								{
									val6.Collect(1, true);
									ShowInGamePickup(collectableName ?? "");
								}
								int num13 = (bulkItemGivenCount.ContainsKey(collectableName) ? bulkItemGivenCount[collectableName] : 0);
								bulkItemGivenCount[collectableName] = num13 + 1;
								givenItemsInOrder.Add($"BULK: {collectableName}: {bulkItemGivenCount[collectableName]}/{num12}");
								Log.LogInfo((object)$"Gave CollectableItem: {collectableName} ({bulkItemGivenCount[collectableName]}/{num12})");
								Log.LogInfo((object)$"[BULK UPDATE] Updated bulkItemGivenCount[{collectableName}] = {bulkItemGivenCount[collectableName]}");
							}
						}
						else
						{
							val6.Collect(1, true);
							givenItems.Add("CollectableItem: " + collectableName);
							givenItemsInOrder.Add("CollectableItem: " + collectableName);
							Log.LogInfo((object)$"Gave new CollectableItem: {collectableName} ({givenItems.Count} total given)");
							ShowInGamePickup(collectableName ?? "");
						}
						SaveGivenItems();
					}
					else
					{
						Log.LogWarning((object)("Could not find CollectableItem: " + collectableName + " in memorized items! Giving currency instead."));
						CurrencyManager.AddCurrency(100, (CurrencyType)0, true);
						Log.LogInfo((object)"Added 100 rosaries as fallback for missing collectable item.");
						ShowInGamePickup("Didn't find this item: " + collectableName + ", how are you seeing this? Here's some rosaries instead.");
					}
					break;
				}
				case 1:
				{
					string toolName = selectedName;
					ToolItem val4 = memorizedTools?.FirstOrDefault((Func<ToolItem, bool>)((ToolItem item) => (Object)(object)item != (Object)null && item.name == toolName));
					if ((Object)(object)val4 != (Object)null)
					{
						((SavedItem)val4).Get(true);
						givenItems.Add("ToolItem: " + toolName);
						givenItemsInOrder.Add("ToolItem: " + toolName);
						SaveGivenItems();
						Log.LogInfo((object)$"Gave new ToolItem: {toolName} ({givenItems.Count} total given)");
						ShowInGamePickup(toolName ?? "");
					}
					else
					{
						Log.LogWarning((object)("Could not find ToolItem: " + toolName + " in memorized items! Giving currency instead."));
						CurrencyManager.AddCurrency(25, (CurrencyType)1, true);
						Log.LogInfo((object)"Added 25 shards as fallback for missing tool item.");
						ShowInGamePickup("Didn't find this item: " + toolName + ", how are you seeing this? Here's some rosaries instead.");
					}
					break;
				}
				case 2:
				{
					string crestName = selectedName;
					if (crestName == "Hunter")
					{
						Log.LogInfo((object)"Got ToolCrest: Hunter - giving currency instead!");
						CurrencyManager.AddCurrency(150, (CurrencyType)0, true);
						CurrencyManager.AddCurrency(150, (CurrencyType)1, true);
						Log.LogInfo((object)"Added 150 rosaries and 150 shards for Hunter crest.");
						givenItems.Add("ToolCrest: " + crestName);
						givenItemsInOrder.Add("ToolCrest: " + crestName);
						ShowInGamePickup("You found Hunter's crest!\n Oh wait, you already have that...\n Here's some currency instead.");
						break;
					}
					ToolCrest val5 = memorizedCrests?.FirstOrDefault((Func<ToolCrest, bool>)((ToolCrest item) => (Object)(object)item != (Object)null && item.name == crestName));
					if ((Object)(object)val5 != (Object)null)
					{
						val5.Unlock();
						try
						{
							ToolItemManager.SetEquippedCrest(crestName);
							Log.LogInfo((object)("Equipped ToolCrest: " + crestName));
							AutoRest();
						}
						catch (Exception ex)
						{
							Log.LogWarning((object)("Could not equip crest " + crestName + ": " + ex.Message));
						}
						givenItems.Add("ToolCrest: " + crestName);
						givenItemsInOrder.Add("ToolCrest: " + crestName);
						ShowInGamePickup(crestName ?? "");
						if (crestName == "Cursed")
						{
							PlayerData instance7 = PlayerData.instance;
							if (instance7 != null)
							{
								instance7.SetBool("IsAnyCursed", true);
								Log.LogInfo((object)"Set IsAnyCursed to true due to Cursed ToolCrest!");
							}
						}
						ToolCrest cloaklessCrest = Gameplay.CloaklessCrest;
						if ((Object)(object)cloaklessCrest != (Object)null && crestName == "cloakless")
						{
							Log.LogInfo((object)"Got Cloakless crest - triggering crest toggle!");
							ToggleCloaklessHunterCrest();
						}
						SaveGivenItems();
						Log.LogInfo((object)$"Gave and equipped new ToolCrest: {crestName} ({givenItems.Count} total given)");
					}
					else
					{
						Log.LogWarning((object)("Could not find ToolCrest: " + crestName + " in memorized items! Giving currency instead."));
						CurrencyManager.AddCurrency(100, (CurrencyType)0, true);
						CurrencyManager.AddCurrency(100, (CurrencyType)1, true);
						Log.LogInfo((object)"Added 100 rosaries and 100 shards as fallback for missing crest.");
						ShowInGamePickup("Could not find ToolCrest: " + crestName + ", you're not supposed to see this text, oh well. Here's some currency instead.");
					}
					break;
				}
				case 3:
				{
					string text7 = selectedName;
					PlayerData instance6 = PlayerData.instance;
					if (instance6 != null)
					{
						instance6.SetBool(text7, true);
						givenItems.Add("Powerup: " + text7);
						givenItemsInOrder.Add("Powerup: " + text7);
						SaveGivenItems();
						Log.LogInfo((object)$"Gave new Powerup: {text7} ({givenItems.Count} total given)");
						string text8 = text7.Replace("has", "").Replace("_", " ").Trim();
						ShowInGamePickup(text8 ?? "");
						Log.LogInfo((object)("You now have the " + text7 + " ability!"));
					}
					else
					{
						Log.LogWarning((object)"PlayerData not available for powerup! Giving currency instead.");
						CurrencyManager.AddCurrency(100, (CurrencyType)0, true);
						Log.LogInfo((object)"Added 100 rosaries as fallback for powerup.");
						ShowInGamePickup("Powerup " + text7 + " not found? Here's some rosaries instead.");
					}
					break;
				}
				case 4:
				{
					string text4 = selectedName;
					PlayerData instance5 = PlayerData.instance;
					if (instance5 != null)
					{
						if (instance5.GetBool(text4))
						{
							Log.LogInfo((object)("Player already has map " + text4 + "! Rerolling for new item..."));
							string text5 = text4.Replace("Has", "").Replace("_", " ").Trim();
							ShowInGamePickup("You already have " + text5 + "!\nRerolling for new item...");
							GetRandomItem();
						}
						else
						{
							instance5.SetBool(text4, true);
							givenItems.Add("Map: " + text4);
							givenItemsInOrder.Add("Map: " + text4);
							SaveGivenItems();
							Log.LogInfo((object)$"Gave map flag: {text4} ({givenItems.Count} total given)");
							string text6 = text4.Replace("Has", "").Replace("_", " ").Trim();
							ShowInGamePickup(text6 ?? "");
						}
					}
					else
					{
						Log.LogWarning((object)("PlayerData not available for map " + text4 + "! Giving currency instead."));
						CurrencyManager.AddCurrency(100, (CurrencyType)0, true);
						ShowInGamePickup("Map " + text4 + " not found? Here's some rosaries instead.");
					}
					break;
				}
				case 5:
				{
					string text3 = selectedName;
					PlayerData instance3 = PlayerData.instance;
					HeroController instance4 = HeroController.instance;
					if (instance3 == null || (Object)(object)instance4 == (Object)null)
					{
						Log.LogWarning((object)"PlayerData or HeroController not available for upgrade!");
						CurrencyManager.AddCurrency(50, (CurrencyType)0, true);
						ShowInGamePickup("Upgrade failed! Here's some currency.");
						break;
					}
					int num8 = (upgradesGivenCount.ContainsKey(text3) ? upgradesGivenCount[text3] : 0);
					upgradesGivenCount[text3] = num8 + 1;
					int num9 = availableUpgrades[text3];
					givenItemsInOrder.Add($"UPGRADE: {text3}: {upgradesGivenCount[text3]}/{num9}");
					switch (text3)
					{
					case "Mask Shard":
						instance3.heartPieces++;
						if (instance3.heartPieces % 4 == 0)
						{
							instance3.maxHealth++;
							instance3.health = instance3.maxHealth;
							instance3.heartPieces = 0;
							Log.LogInfo((object)$"Mask Shard collected! Total shards given: {upgradesGivenCount[text3]}/20. HEALTH UPGRADE! New max: {instance3.maxHealth}");
							ShowInGamePickup($"Mask Shard ({upgradesGivenCount[text3]}/20)\nMax Health Increased!\n0/4 pieces");
							ToolItemManager.SendEquippedChangedEvent(true);
						}
						else
						{
							Log.LogInfo((object)$"Mask Shard collected! Pieces: {instance3.heartPieces}/4. Total given: {upgradesGivenCount[text3]}/20");
							ShowInGamePickup($"Mask Shard ({upgradesGivenCount[text3]}/20)\n{instance3.heartPieces}/4 pieces");
						}
						break;
					case "Spool Fragment":
						instance3.silkSpoolParts++;
						if (instance3.silkSpoolParts % 2 == 0)
						{
							instance3.IsSilkSpoolBroken = false;
							instance4.AddToMaxSilk(1);
							instance4.MaxRegenSilkInstant();
							instance3.silkSpoolParts = 0;
							Log.LogInfo((object)$"Spool Fragment collected! Total fragments given: {upgradesGivenCount[text3]}/18. SILK UPGRADE! New max: {instance3.silkMax}");
							ShowInGamePickup($"Spool Fragment ({upgradesGivenCount[text3]}/18)\nMax Silk Increased!\n0/2 pieces");
							((MonoBehaviour)this).StartCoroutine(RefreshSilkUI());
						}
						else
						{
							Log.LogInfo((object)$"Spool Fragment collected! Parts: {instance3.silkSpoolParts}/2. Total given: {upgradesGivenCount[text3]}/18");
							ShowInGamePickup($"Spool Fragment ({upgradesGivenCount[text3]}/18)\n{instance3.silkSpoolParts}/2 pieces");
						}
						break;
					case "Silk Heart":
						instance3.silkRegenMax++;
						instance3.HasSeenSilkHearts = true;
						Log.LogInfo((object)$"Silk Heart collected! Total: {upgradesGivenCount[text3]}/3. Silk regen max: {instance3.silkRegenMax}");
						ShowInGamePickup($"Silk Heart ({upgradesGivenCount[text3]}/3)\nSilk Regen Increased!");
						ToolItemManager.SendEquippedChangedEvent(true);
						break;
					}
					SaveGivenItems();
					break;
				}
				}
			}
			catch (Exception ex2)
			{
				Log.LogError((object)("Error giving random item: " + ex2.Message));
			}
		}

		private void LoadGivenItems()
		{
			try
			{
				if (File.Exists(givenItemsFilePath))
				{
					string[] array = File.ReadAllLines(givenItemsFilePath);
					givenItems.Clear();
					bulkItemGivenCount.Clear();
					bulkItemFailedAttempts.Clear();
					upgradesGivenCount.Clear();
					givenItemsInOrder.Clear();
					for (int num = array.Length - 1; num >= 0; num--)
					{
						string text = array[num];
						if (!string.IsNullOrWhiteSpace(text))
						{
							string text2 = text.Trim();
							givenItemsInOrder.Add(text2);
							if (text2.StartsWith("BULK: "))
							{
								string[] array2 = text2.Split(new string[1] { ": " }, StringSplitOptions.None);
								if (array2.Length >= 3)
								{
									string key = array2[1];
									string text3 = array2[2];
									int result2;
									if (text3.Contains("/"))
									{
										string[] array3 = text3.Split(new char[1] { '/' });
										if (array3.Length == 2 && int.TryParse(array3[0], out var result))
										{
											bulkItemGivenCount[key] = result;
										}
									}
									else if (int.TryParse(text3, out result2))
									{
										bulkItemGivenCount[key] = result2;
									}
								}
							}
							else if (text2.StartsWith("PITY: "))
							{
								string[] array4 = text2.Split(new string[1] { ": " }, StringSplitOptions.None);
								if (array4.Length >= 3)
								{
									string key2 = array4[1];
									string text4 = array4[2];
									if (text4.Contains("/"))
									{
										string[] array5 = text4.Split(new char[1] { '/' });
										if (array5.Length >= 1 && int.TryParse(array5[0], out var result3))
										{
											bulkItemFailedAttempts[key2] = result3;
										}
									}
								}
							}
							else if (text2.StartsWith("FAILED: "))
							{
								string[] array6 = text2.Split(new string[1] { ": " }, StringSplitOptions.None);
								if (array6.Length >= 3)
								{
									string key3 = array6[1];
									if (int.TryParse(array6[2], out var result4))
									{
										bulkItemFailedAttempts[key3] = result4;
									}
								}
							}
							else if (text2.StartsWith("UPGRADE: "))
							{
								string[] array7 = text2.Split(new string[1] { ": " }, StringSplitOptions.None);
								if (array7.Length >= 3)
								{
									string key4 = array7[1];
									string text5 = array7[2];
									int result6;
									if (text5.Contains("/"))
									{
										string[] array8 = text5.Split(new char[1] { '/' });
										if (array8.Length == 2 && int.TryParse(array8[0], out var result5))
										{
											upgradesGivenCount[key4] = result5;
										}
									}
									else if (int.TryParse(text5, out result6))
									{
										upgradesGivenCount[key4] = result6;
									}
								}
							}
							else
							{
								givenItems.Add(text2);
							}
						}
					}
					Log.LogInfo((object)$"Loaded {givenItems.Count} previously given items, {bulkItemGivenCount.Count} bulk counters, {upgradesGivenCount.Count} upgrades, and {bulkItemFailedAttempts.Count} pity timers from file");
				}
				else
				{
					Log.LogInfo((object)"No previous given items file found - starting fresh");
				}
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Error loading given items: " + ex.Message));
			}
		}

		private void LoadRandomizedShops()
		{
			try
			{
				randomizedShops.Clear();
				if (!File.Exists(randomizedShopsFilePath))
				{
					Log.LogInfo((object)"No randomized_shops.txt file found, starting fresh");
					return;
				}
				string[] array = File.ReadAllLines(randomizedShopsFilePath);
				string[] array2 = array;
				foreach (string text in array2)
				{
					if (string.IsNullOrWhiteSpace(text))
					{
						continue;
					}
					string text2 = text.Trim();
					int num = text2.IndexOf('=');
					if (num >= 0)
					{
						string key = text2.Substring(0, num);
						string text3 = text2.Substring(num + 1);
						string[] array3 = text3.Split(new char[1] { '|' }, StringSplitOptions.None);
						List<(string, int)> list = new List<(string, int)>();
						string[] array4 = array3;
						foreach (string text4 in array4)
						{
							if (string.IsNullOrEmpty(text4))
							{
								list.Add(("", 0));
								continue;
							}
							int num2 = text4.LastIndexOf(':');
							if (num2 >= 0)
							{
								string item = text4.Substring(0, num2);
								string s = text4.Substring(num2 + 1);
								if (int.TryParse(s, out var result))
								{
									list.Add((item, result));
									continue;
								}
								Log.LogWarning((object)("Invalid cost format in shop data: '" + text4 + "'"));
								list.Add((item, 0));
							}
							else
							{
								list.Add((text4, 0));
							}
						}
						randomizedShops[key] = list;
					}
					else
					{
						randomizedShops[text2] = null;
					}
				}
				Log.LogInfo((object)$"Loaded {randomizedShops.Count} randomized shops from file (per-shop assignments: {randomizedShops.Values.Count((List<(string itemName, int originalCost)> v) => v != null)})");
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Error loading randomized shops: " + ex.Message));
			}
		}

		private void SaveGivenItems()
		{
			try
			{
				try
				{
					if (givenItemsInOrder != null && givenItemsInOrder.Count > 0)
					{
						string text = givenItemsInOrder[givenItemsInOrder.Count - 1];
						int num = -1;
						string text2 = null;
						if (text.StartsWith("CollectableItem: "))
						{
							num = 0;
							text2 = text.Substring("CollectableItem: ".Length);
						}
						else if (text.StartsWith("ToolItem: "))
						{
							num = 1;
							text2 = text.Substring("ToolItem: ".Length);
						}
						else if (text.StartsWith("ToolCrest: "))
						{
							num = 2;
							text2 = text.Substring("ToolCrest: ".Length);
						}
						else if (text.StartsWith("Powerup: "))
						{
							num = 3;
							text2 = text.Substring("Powerup: ".Length);
						}
						else if (text.StartsWith("BULK: "))
						{
							string[] array = text.Split(new string[1] { ": " }, StringSplitOptions.None);
							if (array.Length >= 2)
							{
								num = 0;
								text2 = array[1];
							}
						}
						if (num != -1 && !string.IsNullOrEmpty(text2))
						{
							SyncCrossCategoryOnGive(num, text2);
						}
					}
				}
				catch (Exception ex)
				{
					Log.LogError((object)("Error syncing cross-category after save: " + ex.Message));
				}
				List<string> list = new List<string>();
				List<string> collection = (from line in givenItemsInOrder.AsEnumerable().Reverse()
					where !line.StartsWith("=== PITY TIMER STATUS ===") && !line.Trim().Equals("")
					select line).ToList();
				list.AddRange(collection);
				if (bulkItemFailedAttempts.Count > 0)
				{
					list.Add("");
					list.Add("=== PITY TIMER STATUS ===");
					foreach (KeyValuePair<string, int> item in bulkItemFailedAttempts.OrderBy((KeyValuePair<string, int> x) => x.Key))
					{
						if (item.Value > 0 && bulkItemPityThreshold.ContainsKey(item.Key))
						{
							int num2 = bulkItemPityThreshold[item.Key];
							int num3 = num2 - item.Value + 1;
							list.Add($"PITY: {item.Key}: {item.Value}/{num2} failed ({num3} more attempts until guaranteed)");
						}
					}
				}
				File.WriteAllLines(givenItemsFilePath, list);
				Log.LogInfo((object)$"Saved {givenItems.Count} given items, {bulkItemGivenCount.Count} bulk counters, and {bulkItemFailedAttempts.Count} pity timers to file");
			}
			catch (Exception ex2)
			{
				Log.LogError((object)("Error saving given items: " + ex2.Message));
			}
		}

		internal void SaveRandomizedShops()
		{
			try
			{
				List<string> list = new List<string>();
				foreach (KeyValuePair<string, List<(string, int)>> randomizedShop in randomizedShops)
				{
					if (randomizedShop.Value == null)
					{
						list.Add(randomizedShop.Key);
						continue;
					}
					string[] value = randomizedShop.Value.Select<(string, int), string>(((string itemName, int originalCost) tuple) => string.IsNullOrEmpty(tuple.itemName) ? "" : $"{tuple.itemName}:{tuple.originalCost}").ToArray();
					list.Add(randomizedShop.Key + "=" + string.Join("|", value));
				}
				File.WriteAllLines(randomizedShopsFilePath, list);
				Log.LogInfo((object)$"Saved {randomizedShops.Count} randomized shops (with assignments: {list.Count((string l) => Enumerable.Contains(l, '='))})");
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Failed to save randomized shops: " + ex.Message));
			}
		}

		internal void SaveShopItemPool()
		{
			try
			{
				List<string> list = new List<string>();
				foreach (KeyValuePair<string, int> item in shopItemPool)
				{
					list.Add($"{item.Key}:{item.Value}");
				}
				string path = Path.Combine(Path.GetDirectoryName(randomizedShopsFilePath), "shop_item_pool.txt");
				File.WriteAllLines(path, list);
				Log.LogInfo((object)$"Saved shop item pool with {shopItemPool.Count} entries");
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Failed to save shop item pool: " + ex.Message));
			}
		}

		internal void LoadShopItemPool()
		{
			try
			{
				string path = Path.Combine(Path.GetDirectoryName(randomizedShopsFilePath), "shop_item_pool.txt");
				if (!File.Exists(path))
				{
					Log.LogInfo((object)"No shop item pool file found, starting with empty pool");
					return;
				}
				string[] array = File.ReadAllLines(path);
				shopItemPool.Clear();
				string[] array2 = array;
				foreach (string text in array2)
				{
					if (string.IsNullOrWhiteSpace(text))
					{
						continue;
					}
					string[] array3 = text.Split(new char[1] { ':' });
					if (array3.Length == 2)
					{
						string key = array3[0];
						if (int.TryParse(array3[1], out var result))
						{
							shopItemPool[key] = result;
						}
					}
				}
				Log.LogInfo((object)$"Loaded shop item pool with {shopItemPool.Count} entries");
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Failed to load shop item pool: " + ex.Message));
			}
		}

		internal static List<CollectableItem> GetAvailableCollectablesForShops()
		{
			List<CollectableItem> list = new List<CollectableItem>();
			if (memorizedCollectables == null)
			{
				return list;
			}
			CollectableItem[] array = memorizedCollectables;
			foreach (CollectableItem val in array)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				string name = ((Object)val).name;
				switch (name)
				{
				case "Invalid Item Template":
				case "Blue Goop Jar":
				case "Dock Demo Key":
					continue;
				}
				if (name == "Tool Pouch&Kit Inv")
				{
					continue;
				}
				switch (name)
				{
				case "R Seal chit":
				case "R Weaver Totem":
				case "R Bone Record":
				case "R Weaver Record":
				case "R Ancient Egg":
				case "R Psalm Cylinder":
				case "R Librarian Melody Cylinder":
					continue;
				}
				if (name == "Pins" || bulkItemPityThreshold.ContainsKey(name))
				{
					continue;
				}
				if (bulkItemQuantities.ContainsKey(name))
				{
					int num = bulkItemQuantities[name];
					if (num < 50)
					{
						int num2 = (shopItemPool.ContainsKey(name) ? shopItemPool[name] : 0);
						int num3 = num - num2;
						for (int j = 0; j < Mathf.Min(num3, 20); j++)
						{
							list.Add(val);
						}
					}
				}
				else if (!shopItemPool.ContainsKey(name) || shopItemPool[name] == 0)
				{
					list.Add(val);
				}
			}
			return list;
		}

		private void ToggleCloaklessHunterCrest()
		{
			try
			{
				PlayerData instance = PlayerData.instance;
				if (instance == null)
				{
					Log.LogWarning((object)"PlayerData not available!");
					return;
				}
				string currentCrestID = instance.CurrentCrestID;
				Log.LogInfo((object)("Current crest: " + (currentCrestID ?? "none")));
				ToolCrest cloaklessCrest = Gameplay.CloaklessCrest;
				bool flag = (Object)(object)cloaklessCrest != (Object)null && currentCrestID == cloaklessCrest.name;
				bool flag2 = currentCrestID == "Cursed";
				if (flag || flag2)
				{
					if (memorizedCrests != null)
					{
						ToolCrest val = ((IEnumerable<ToolCrest>)memorizedCrests).FirstOrDefault((Func<ToolCrest, bool>)((ToolCrest c) => (Object)(object)c != (Object)null && c.name == "Hunter"));
						if ((Object)(object)val != (Object)null)
						{
							val.Unlock();
							ToolItemManager.SetEquippedCrest(val.name);
							string text = (flag ? "cloakless" : "cursed");
							Log.LogInfo((object)("Switched from " + text + " to Hunter crest: " + val.name));
							AutoRest();
						}
						else
						{
							Log.LogWarning((object)"Hunter crest not found in memorized crests!");
						}
					}
					else
					{
						Log.LogWarning((object)"Crests not memorized yet! Press F7 first.");
					}
				}
				else if ((Object)(object)cloaklessCrest != (Object)null)
				{
					cloaklessCrest.Unlock();
					ToolItemManager.SetEquippedCrest(cloaklessCrest.name);
					Log.LogInfo((object)("Switched to cloakless crest: " + cloaklessCrest.name));
					AutoRest();
				}
				else
				{
					Log.LogWarning((object)"CloaklessCrest not found!");
				}
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Error toggling crest: " + ex.Message));
			}
		}

		private void ToggleSilkSoar()
		{
			try
			{
				PlayerData instance = PlayerData.instance;
				if (instance == null)
				{
					Log.LogWarning((object)"PlayerData not available!");
				}
				else if (instance.GetBool("hasSuperJump"))
				{
					instance.SetBool("hasSuperJump", false);
					Log.LogInfo((object)"Disabled Silk Soar ability");
				}
				else
				{
					instance.SetBool("hasSuperJump", true);
					Log.LogInfo((object)"Enabled Silk Soar ability");
				}
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Error toggling Silk Soar: " + ex.Message));
			}
		}

		public static void AutoRest()
		{
			try
			{
				bool flag = ToolItemManager.TryReplenishTools(true, (ReplenishMethod)0);
				Log.LogInfo((object)(flag ? "Auto-rested! Tools replenished." : "Auto-rest complete (no replenishment needed)."));
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Error during auto-rest: " + ex.Message));
			}
		}

		[IteratorStateMachine(typeof(<RefreshSilkUI>d__53))]
		private IEnumerator RefreshSilkUI()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <RefreshSilkUI>d__53(0)
			{
				<>4__this = this
			};
		}
	}
	[HarmonyPatch(typeof(ShopMenuStock), "BuildItemList")]
	public static class ShopMenuStock_BuildItemList_Patch
	{
		private static ManualLogSource Log = Logger.CreateLogSource("ShopRandomizer");

		[HarmonyPrefix]
		public static void Prefix(ShopMenuStock __instance)
		{
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_042e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0435: Expected I4, but got Unknown
			try
			{
				if (!HKS_Randomizer.itemsMemorized)
				{
					Log.LogInfo((object)"[SHOP] Items not memorized yet, memorizing now...");
					HKS_Randomizer.Instance?.MemorizeAllItems();
					if (!HKS_Randomizer.itemsMemorized)
					{
						Log.LogError((object)"[SHOP] Failed to memorize items, cannot randomize shops");
						return;
					}
				}
				ShopOwner[] array = Resources.FindObjectsOfTypeAll<ShopOwner>();
				Log.LogInfo((object)$"[SHOP] Found {array.Length} ShopOwners in scene");
				ShopOwner[] array2 = array;
				foreach (ShopOwner val in array2)
				{
					if ((Object)(object)val == (Object)null || ((ShopOwnerBase)val).Stock == null || ((ShopOwnerBase)val).Stock.Length == 0)
					{
						continue;
					}
					object obj;
					if (!((Object)((Component)val).gameObject).name.Contains("Mapper"))
					{
						Scene activeScene = SceneManager.GetActiveScene();
						obj = ((Scene)(ref activeScene)).name + "_" + ((Object)((Component)val).gameObject).name;
					}
					else
					{
						obj = "Mapper";
					}
					string text = (string)obj;
					if (HKS_Randomizer.randomizedShops.ContainsKey(text))
					{
						List<(string, int)> list = HKS_Randomizer.randomizedShops[text];
						if (list != null && list.Count == ((ShopOwnerBase)val).Stock.Length)
						{
							Log.LogInfo((object)("[SHOP LOAD] Reapplying saved assignments to " + text));
							for (int j = 0; j < ((ShopOwnerBase)val).Stock.Length; j++)
							{
								ShopItem val2 = ((ShopOwnerBase)val).Stock[j];
								if ((Object)(object)val2 == (Object)null)
								{
									continue;
								}
								var (itemName, num) = list[j];
								if (string.IsNullOrEmpty(itemName))
								{
									FieldInfo field = typeof(ShopItem).GetField("cost", BindingFlags.Instance | BindingFlags.NonPublic);
									if (field != null)
									{
										int num2 = (int)field.GetValue(val2);
										int num3 = Mathf.Max(1, num2 / 2);
										field.SetValue(val2, num3);
									}
									continue;
								}
								CollectableItem[] array3 = typeof(HKS_Randomizer).GetField("memorizedCollectables", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null) as CollectableItem[];
								CollectableItem val3 = null;
								if (array3 != null)
								{
									val3 = Array.Find(array3, (CollectableItem c) => (Object)(object)c != (Object)null && ((Object)c).name == itemName);
								}
								if ((Object)(object)val3 != (Object)null)
								{
									FieldInfo field2 = typeof(ShopItem).GetField("savedItem", BindingFlags.Instance | BindingFlags.NonPublic);
									if (field2 != null)
									{
										field2.SetValue(val2, val3);
										FieldInfo field3 = typeof(ShopItem).GetField("cost", BindingFlags.Instance | BindingFlags.NonPublic);
										if (field3 != null)
										{
											int num4 = Mathf.Max(1, num / 2);
											field3.SetValue(val2, num4);
											Log.LogInfo((object)$"[SHOP LOAD]   Slot {j}: {itemName} (original: {num}, halved: {num4})");
										}
									}
								}
								else
								{
									Log.LogWarning((object)$"[SHOP LOAD]   Slot {j}: Could not find item '{itemName}'");
								}
							}
							Log.LogInfo((object)("[SHOP LOAD] " + text + " assignments reapplied"));
							continue;
						}
						Log.LogWarning((object)("[SHOP] " + text + " has invalid/mismatched assignments, re-randomizing"));
					}
					Log.LogInfo((object)$"[SHOP] Randomizing shop: {text} with {((ShopOwnerBase)val).Stock.Length} slots");
					List<CollectableItem> availableCollectablesForShops = HKS_Randomizer.GetAvailableCollectablesForShops();
					int num5 = 0;
					List<(string, int)> list2 = new List<(string, int)>(new(string, int)[((ShopOwnerBase)val).Stock.Length]);
					for (int k = 0; k < ((ShopOwnerBase)val).Stock.Length; k++)
					{
						ShopItem val4 = ((ShopOwnerBase)val).Stock[k];
						if ((Object)(object)val4 == (Object)null)
						{
							continue;
						}
						int num6 = (int)val4.GetTypeFlags();
						if (num6 == 0 || num6 == 1)
						{
							if (availableCollectablesForShops.Count == 0)
							{
								Log.LogWarning((object)"[SHOP] Pool exhausted");
								break;
							}
							int index = Random.Range(0, availableCollectablesForShops.Count);
							CollectableItem val5 = availableCollectablesForShops[index];
							availableCollectablesForShops.RemoveAt(index);
							FieldInfo field4 = typeof(ShopItem).GetField("savedItem", BindingFlags.Instance | BindingFlags.NonPublic);
							FieldInfo field5 = typeof(ShopItem).GetField("cost", BindingFlags.Instance | BindingFlags.NonPublic);
							if (field4 != null && field5 != null)
							{
								field4.SetValue(val4, val5);
								int num7 = (int)field5.GetValue(val4);
								int num8 = Mathf.Max(1, num7 / 2);
								field5.SetValue(val4, num8);
								string name = ((Object)val5).name;
								int num9 = (HKS_Randomizer.shopItemPool.ContainsKey(name) ? HKS_Randomizer.shopItemPool[name] : 0);
								HKS_Randomizer.shopItemPool[name] = num9 + 1;
								Log.LogInfo((object)$"[SHOP]   Slot {k}: {name} (pool: {num9 + 1}, cost: {num8})");
								list2[k] = (name, num7);
								num5++;
							}
						}
						else
						{
							FieldInfo field6 = typeof(ShopItem).GetField("cost", BindingFlags.Instance | BindingFlags.NonPublic);
							if (field6 != null)
							{
								int num10 = (int)field6.GetValue(val4);
								int num11 = Mathf.Max(1, num10 / 2);
								field6.SetValue(val4, num11);
								list2[k] = ("", 0);
							}
						}
					}
					Log.LogInfo((object)$"[SHOP SAVE] {text} complete: {num5} items replaced");
					HKS_Randomizer.randomizedShops[text] = list2;
					((object)HKS_Randomizer.Instance).GetType().GetMethod("SaveRandomizedShops", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(HKS_Randomizer.Instance, null);
					((object)HKS_Randomizer.Instance).GetType().GetMethod("SaveShopItemPool", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(HKS_Randomizer.Instance, null);
				}
			}
			catch (Exception ex)
			{
				Log.LogError((object)("[SHOP] Error in BuildItemList patch: " + ex.Message + "\n" + ex.StackTrace));
			}
		}
	}
	[HarmonyPatch(typeof(CollectableItemPickup), "Invoke")]
	public static class CollectableItemPickup_DoPickup_Patch
	{
		[HarmonyPatch(typeof(SavedItem), "TryGet")]
		public static class SavedItem_TryGet_Patch
		{
			private static ManualLogSource Log = Logger.CreateLogSource("InstantPickupPatches");

			private static bool hasInitialized = false;

			[HarmonyPrefix]
			public static bool Prefix(SavedItem __instance, ref bool __result, bool breakIfAtMax, bool showPopup)
			{
				HKS_Randomizer hKS_Randomizer = Object.FindFirstObjectByType<HKS_Randomizer>();
				if ((Object)(object)hKS_Randomizer != (Object)null)
				{
					if (!hasInitialized)
					{
						Log.LogInfo((object)"First walking pickup - automatically initializing items");
						hKS_Randomizer.MemorizeAllItems();
						hasInitialized = true;
						if (HKS_Randomizer.itemsMemorized)
						{
							hKS_Randomizer.GetRandomItem();
						}
					}
					else if (HKS_Randomizer.itemsMemorized)
					{
						hKS_Randomizer.GetRandomItem();
					}
				}
				if (__instance is CollectableItem)
				{
					__result = true;
					return false;
				}
				return true;
			}
		}

		private static ManualLogSource patchLog = Logger.CreateLogSource("PickupPatches");
	}
}