Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of HKS Item Randomizer v1.0.0
HKSilksong_Randomizer.dll
Decompiled 9 months agousing 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"); } }