using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Photon.Pun; using UnityEngine; using Zorro.Core; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("peak-mod")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("peak-mod")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("ca4fd68f-ad66-42be-952a-50365774478d")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.0.0")] namespace PeakItemRandomizer; [HarmonyPatch(typeof(BerryBush), "SpawnItems")] internal static class BerryBushPatch { private static bool Prefix(BerryBush __instance, List spawnSpots, ref List __result) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Unknown result type (might be due to invalid IL or missing references) //IL_0216: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) if (!PeakItemRandomizer.ModEnabled.Value || !PeakItemRandomizer.RandomizeNature.Value) { return true; } __result = new List(); if (!PhotonNetwork.IsMasterClient) { return false; } if (spawnSpots == null || spawnSpots.Count == 0) { return false; } GameObject randomAllowedPrefab = RandomizerCore.GetRandomAllowedPrefab(((Spawner)__instance).spawnPool); if ((Object)(object)randomAllowedPrefab == (Object)null) { ModLog.Warn("BerryBush had no prefab to spawn."); return false; } List list = new List(spawnSpots); float num = Mathf.Pow(Random.value, __instance.randomPow); int num2 = Mathf.RoundToInt(Mathf.Lerp(__instance.possibleBerries.x, __instance.possibleBerries.y, num)); num2 = Mathf.Clamp(num2, 0, spawnSpots.Count); for (int i = 0; i < num2; i++) { int index = Random.Range(0, list.Count); Transform val = list[index]; list.RemoveAt(index); Item component = PhotonNetwork.InstantiateItemRoom(((Object)randomAllowedPrefab).name, val.position, Quaternion.identity).GetComponent(); __result.Add(((Component)component).GetComponent()); if ((Object)(object)((Spawner)__instance).spawnUpTowardsTarget != (Object)null) { Transform transform = ((Component)component).transform; Vector3 val2 = ((Spawner)__instance).spawnUpTowardsTarget.position - ((Component)component).transform.position; transform.up = ((Vector3)(ref val2)).normalized; ((Component)component).transform.Rotate(Vector3.up, Random.Range(0f, 360f), (Space)1); } if (((Spawner)__instance).spawnTransformIsSpawnerTransform) { ((Component)component).transform.rotation = val.rotation; ((Component)component).transform.Rotate(Vector3.up, Random.Range(0f, 360f), (Space)1); } ((Component)component).GetComponent().RPC("SetKinematicRPC", (RpcTarget)3, new object[3] { true, ((Component)component).transform.position, ((Component)component).transform.rotation }); } ModLog.Info($"BerryBush spawned {num2}x {((Object)randomAllowedPrefab).name}"); return false; } } [HarmonyPatch(typeof(BerryVine), "SpawnItems")] internal static class BerryVinePatch { private static bool Prefix(BerryVine __instance, List spawnSpots, ref List __result) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) if (!PeakItemRandomizer.ModEnabled.Value || !PeakItemRandomizer.RandomizeNature.Value) { return true; } __result = new List(); if (!PhotonNetwork.IsMasterClient) { return false; } if (spawnSpots == null || spawnSpots.Count == 0) { return false; } GameObject randomAllowedPrefab = RandomizerCore.GetRandomAllowedPrefab(((Spawner)__instance).spawnPool); if ((Object)(object)randomAllowedPrefab == (Object)null) { ModLog.Warn("BerryVine had no prefab to spawn."); return false; } List list = new List(spawnSpots); float num = Mathf.Pow(Random.value, __instance.randomPow); int num2 = Mathf.RoundToInt(Mathf.Lerp(__instance.possibleBerries.x, __instance.possibleBerries.y, num)); num2 = Mathf.Clamp(num2, 0, spawnSpots.Count); for (int i = 0; i < num2; i++) { int index = Random.Range(0, list.Count); Transform val = list[index]; list.RemoveAt(index); Item component = PhotonNetwork.InstantiateItemRoom(((Object)randomAllowedPrefab).name, val.position + __instance.spawnOffsetWorldSpace, Quaternion.identity).GetComponent(); __result.Add(((Component)component).GetComponent()); if ((Object)(object)((Spawner)__instance).spawnUpTowardsTarget != (Object)null) { Transform transform = ((Component)component).transform; Vector3 val2 = ((Spawner)__instance).spawnUpTowardsTarget.position - ((Component)component).transform.position; transform.up = ((Vector3)(ref val2)).normalized; } ((Component)component).transform.rotation = Quaternion.Euler(0f, (float)Random.Range(0, 360), 0f); ((Component)component).GetComponent().RPC("SetKinematicRPC", (RpcTarget)3, new object[3] { true, ((Component)component).transform.position, ((Component)component).transform.rotation }); } ModLog.Info($"BerryVine spawned {num2}x {((Object)randomAllowedPrefab).name}"); return false; } } internal static class ItemSpawnConfig { private static readonly Dictionary> ItemToggles = new Dictionary>(); public static void BuildItemConfig(ConfigFile config) { ItemToggles.Clear(); if ((Object)(object)SingletonAsset.Instance == (Object)null) { ModLog.Warn("ItemDatabase was not ready. Could not build item toggle config."); return; } foreach (KeyValuePair item in SingletonAsset.Instance.itemLookup) { Item value = item.Value; if (!((Object)(object)value == (Object)null)) { LootData component = ((Component)value).GetComponent(); if (!((Object)(object)component == (Object)null)) { string safeConfigName = GetSafeConfigName(value); ConfigEntry value2 = config.Bind("Items - Spawn Toggles", safeConfigName, true, $"Allow this item to spawn. Prefab: {((Object)((Component)value).gameObject).name} | Item ID: {item.Key}"); ItemToggles[item.Key] = value2; } } } ModLog.Info($"Created {ItemToggles.Count} in-game item spawn toggles."); } public static bool IsItemEnabled(ushort itemId, Item item) { if (ItemToggles.Count == 0 && (Object)(object)PeakItemRandomizer.Instance != (Object)null) { BuildItemConfig(((BaseUnityPlugin)PeakItemRandomizer.Instance).Config); } if (ItemToggles.TryGetValue(itemId, out var value)) { return value.Value; } ModLog.Warn($"No toggle found for item ID {itemId}. Blocking by default."); return false; } private static string GetSafeConfigName(Item item) { string text = ((Object)((Component)item).gameObject).name ?? "UnknownItem"; string text2 = ((item.UIData != null) ? (item.UIData.itemName ?? "") : ""); string input = (string.IsNullOrWhiteSpace(text2) ? text : (text2 + " (" + text + ")")); input = Regex.Replace(input, "[^a-zA-Z0-9_\\-() ]", ""); return Regex.Replace(input, "\\s+", " "); } } [HarmonyPatch(typeof(LootData), "PopulateLootData")] internal static class LootDataPatch { private static void Postfix() { try { RandomizerCore.RebuildRandomizedPools(); } catch (Exception ex) { ModLog.Error("LootData patch failed: " + ex); } } } internal static class ModLog { internal static ManualLogSource Log; internal static void Init(ManualLogSource source) { Log = source; Info("Logger initialized."); } internal static void Info(string msg) { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)msg); } } internal static void Warn(string msg) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)msg); } } internal static void Error(string msg) { ManualLogSource log = Log; if (log != null) { log.LogError((object)msg); } } internal static void Debug(string msg) { ManualLogSource log = Log; if (log != null) { log.LogDebug((object)msg); } } } [BepInPlugin("com.yourname.peakitemrandomizer", "PeakItemRandomizer", "1.1.0")] public sealed class PeakItemRandomizer : BaseUnityPlugin { public const string PluginGuid = "com.yourname.peakitemrandomizer"; public const string PluginName = "PeakItemRandomizer"; public const string PluginVersion = "1.1.0"; internal static PeakItemRandomizer Instance; private Harmony harmony; internal static ConfigEntry ModEnabled; internal static ConfigEntry EqualChance; internal static ConfigEntry RandomizeLuggage; internal static ConfigEntry RandomizeNature; internal static ConfigEntry KeepRespawnStatuesVanilla; internal static ConfigEntry ExtraBlockedNames; internal static ConfigEntry AllowedNamesOnly; private void Awake() { //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Expected O, but got Unknown Instance = this; ModLog.Init(((BaseUnityPlugin)this).Logger); ModLog.Info("Starting PeakItemRandomizer..."); ModEnabled = ((BaseUnityPlugin)this).Config.Bind("General", "Enabled", true, "Master toggle for the item randomizer."); EqualChance = ((BaseUnityPlugin)this).Config.Bind("General", "EqualChance", true, "Every allowed item gets equal spawn chance."); RandomizeLuggage = ((BaseUnityPlugin)this).Config.Bind("Pools", "RandomizeLuggage", true, "Randomize luggage pools."); RandomizeNature = ((BaseUnityPlugin)this).Config.Bind("Pools", "RandomizeNature", true, "Randomize bushes, vines, trees, and nature pools."); KeepRespawnStatuesVanilla = ((BaseUnityPlugin)this).Config.Bind("Pools", "KeepRespawnStatuesVanilla", true, "Do not randomize respawn chests/statues/coffins."); ExtraBlockedNames = ((BaseUnityPlugin)this).Config.Bind("Filters", "BlockedNameContains", "chess,prop,cheat,cheater,debug,test,guidebook, stick, Infinite, Pawn, Stone, Queen, King, Rook, Bishop, Knight", "Comma-separated item name fragments to block from spawning."); AllowedNamesOnly = ((BaseUnityPlugin)this).Config.Bind("Filters", "AllowedNamesOnly", "", "Optional whitelist. Leave blank to allow every valid item except blocked ones."); harmony = new Harmony("com.yourname.peakitemrandomizer"); harmony.PatchAll(); ModLog.Info("Harmony patches applied."); } private void OnDestroy() { Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } ModLog.Warn("PeakItemRandomizer unloaded."); } internal static HashSet SplitConfig(ConfigEntry entry) { return (from s in entry.Value.Split(new char[3] { ',', ';', '|' }, StringSplitOptions.RemoveEmptyEntries) select s.Trim().ToLowerInvariant() into s where !string.IsNullOrWhiteSpace(s) select s).ToHashSet(); } } internal static class RandomizerCore { private static readonly SpawnPool[] NaturePools; private static readonly SpawnPool[] LuggagePools; public static void RebuildRandomizedPools() { //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) if (!PeakItemRandomizer.ModEnabled.Value) { ModLog.Warn("Mod disabled. Skipping pool rebuild."); return; } if (LootData.AllSpawnWeightData == null) { ModLog.Warn("LootData.AllSpawnWeightData was null. Skipping pool rebuild."); return; } List allowedItemIds = GetAllowedItemIds(); if (allowedItemIds.Count == 0) { ModLog.Error("No allowed items found. Randomizer will not change pools."); return; } int itemWeight = (PeakItemRandomizer.EqualChance.Value ? 1 : 10); Dictionary dictionary = allowedItemIds.ToDictionary((ushort id) => id, (ushort _) => itemWeight); foreach (SpawnPool item in GetPoolsToRandomize()) { LootData.AllSpawnWeightData[item] = new Dictionary(dictionary); ModLog.Info($"Randomized pool: {item} | items: {dictionary.Count} | weight each: {itemWeight}"); } LootData.AllSpawnWeightData[(SpawnPool)134217728] = new Dictionary(dictionary); ModLog.Info($"SpawnPool.All rebuilt with {dictionary.Count} allowed items."); } private static List GetAllowedItemIds() { HashSet source = PeakItemRandomizer.SplitConfig(PeakItemRandomizer.ExtraBlockedNames); HashSet hashSet = PeakItemRandomizer.SplitConfig(PeakItemRandomizer.AllowedNamesOnly); List list = new List(); foreach (KeyValuePair item in SingletonAsset.Instance.itemLookup) { Item value = item.Value; if ((Object)(object)value == (Object)null) { continue; } LootData component = ((Component)value).GetComponent(); if ((Object)(object)component == (Object)null) { continue; } if (!value.IsValidToSpawn()) { ModLog.Debug("Skipped invalid item: " + ((Object)((Component)value).gameObject).name); continue; } string text = ((Object)((Component)value).gameObject).name ?? ""; string text2 = ((value.UIData != null) ? (value.UIData.itemName ?? "") : ""); string combinedName = (text + " " + text2).ToLowerInvariant(); if (source.Any((string blockedText) => combinedName.Contains(blockedText))) { ModLog.Warn("Blocked item: " + text); } else if (hashSet.Count > 0 && !hashSet.Any((string allowedText) => combinedName.Contains(allowedText))) { ModLog.Debug("Skipped because not whitelisted: " + text); } else { list.Add(item.Key); } } list = list.Distinct().ToList(); ModLog.Info($"Allowed item count: {list.Count}"); return list; } private static HashSet GetPoolsToRandomize() { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) HashSet hashSet = new HashSet(); if (PeakItemRandomizer.RandomizeNature.Value) { SpawnPool[] naturePools = NaturePools; foreach (SpawnPool item in naturePools) { hashSet.Add(item); } } if (PeakItemRandomizer.RandomizeLuggage.Value) { SpawnPool[] luggagePools = LuggagePools; foreach (SpawnPool item2 in luggagePools) { hashSet.Add(item2); } } if (PeakItemRandomizer.KeepRespawnStatuesVanilla.Value) { hashSet.Remove((SpawnPool)131072); } return hashSet; } public static GameObject GetRandomAllowedPrefab(SpawnPool pool) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) if (LootData.AllSpawnWeightData == null) { LootData.PopulateLootData(); } if (!LootData.AllSpawnWeightData.TryGetValue(pool, out var value) || value.Count == 0) { ModLog.Warn($"Pool {pool} was empty. Falling back to vanilla GetRandomItem."); return LootData.GetRandomItem(pool, false); } List list = value.Keys.ToList(); ushort num = list[Random.Range(0, list.Count)]; Item val = default(Item); if (ItemDatabase.TryGetItem(num, ref val) && (Object)(object)val != (Object)null) { ModLog.Debug($"Selected item from {pool}: {((Object)((Component)val).gameObject).name}"); return ((Component)val).gameObject; } ModLog.Warn($"Failed to resolve item ID {num}. Falling back to vanilla GetRandomItem."); return LootData.GetRandomItem(pool, false); } static RandomizerCore() { SpawnPool[] array = new SpawnPool[11]; RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/); NaturePools = (SpawnPool[])(object)array; SpawnPool[] array2 = new SpawnPool[9]; RuntimeHelpers.InitializeArray(array2, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/); LuggagePools = (SpawnPool[])(object)array2; } } [HarmonyPatch(typeof(RespawnChest), "SpawnItems")] internal static class RespawnChestPatch { private static bool Prefix() { if (PeakItemRandomizer.KeepRespawnStatuesVanilla.Value) { ModLog.Debug("RespawnChest left vanilla."); } return true; } }