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<Transform> spawnSpots, ref List<PhotonView> __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<PhotonView>();
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<Transform> list = new List<Transform>(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<Item>();
__result.Add(((Component)component).GetComponent<PhotonView>());
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<PhotonView>().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<Transform> spawnSpots, ref List<PhotonView> __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<PhotonView>();
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<Transform> list = new List<Transform>(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<Item>();
__result.Add(((Component)component).GetComponent<PhotonView>());
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<PhotonView>().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<ushort, ConfigEntry<bool>> ItemToggles = new Dictionary<ushort, ConfigEntry<bool>>();
public static void BuildItemConfig(ConfigFile config)
{
ItemToggles.Clear();
if ((Object)(object)SingletonAsset<ItemDatabase>.Instance == (Object)null)
{
ModLog.Warn("ItemDatabase was not ready. Could not build item toggle config.");
return;
}
foreach (KeyValuePair<ushort, Item> item in SingletonAsset<ItemDatabase>.Instance.itemLookup)
{
Item value = item.Value;
if (!((Object)(object)value == (Object)null))
{
LootData component = ((Component)value).GetComponent<LootData>();
if (!((Object)(object)component == (Object)null))
{
string safeConfigName = GetSafeConfigName(value);
ConfigEntry<bool> value2 = config.Bind<bool>("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<bool> ModEnabled;
internal static ConfigEntry<bool> EqualChance;
internal static ConfigEntry<bool> RandomizeLuggage;
internal static ConfigEntry<bool> RandomizeNature;
internal static ConfigEntry<bool> KeepRespawnStatuesVanilla;
internal static ConfigEntry<string> ExtraBlockedNames;
internal static ConfigEntry<string> 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<bool>("General", "Enabled", true, "Master toggle for the item randomizer.");
EqualChance = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EqualChance", true, "Every allowed item gets equal spawn chance.");
RandomizeLuggage = ((BaseUnityPlugin)this).Config.Bind<bool>("Pools", "RandomizeLuggage", true, "Randomize luggage pools.");
RandomizeNature = ((BaseUnityPlugin)this).Config.Bind<bool>("Pools", "RandomizeNature", true, "Randomize bushes, vines, trees, and nature pools.");
KeepRespawnStatuesVanilla = ((BaseUnityPlugin)this).Config.Bind<bool>("Pools", "KeepRespawnStatuesVanilla", true, "Do not randomize respawn chests/statues/coffins.");
ExtraBlockedNames = ((BaseUnityPlugin)this).Config.Bind<string>("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<string>("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<string> SplitConfig(ConfigEntry<string> 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<ushort> 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<ushort, int> dictionary = allowedItemIds.ToDictionary((ushort id) => id, (ushort _) => itemWeight);
foreach (SpawnPool item in GetPoolsToRandomize())
{
LootData.AllSpawnWeightData[item] = new Dictionary<ushort, int>(dictionary);
ModLog.Info($"Randomized pool: {item} | items: {dictionary.Count} | weight each: {itemWeight}");
}
LootData.AllSpawnWeightData[(SpawnPool)134217728] = new Dictionary<ushort, int>(dictionary);
ModLog.Info($"SpawnPool.All rebuilt with {dictionary.Count} allowed items.");
}
private static List<ushort> GetAllowedItemIds()
{
HashSet<string> source = PeakItemRandomizer.SplitConfig(PeakItemRandomizer.ExtraBlockedNames);
HashSet<string> hashSet = PeakItemRandomizer.SplitConfig(PeakItemRandomizer.AllowedNamesOnly);
List<ushort> list = new List<ushort>();
foreach (KeyValuePair<ushort, Item> item in SingletonAsset<ItemDatabase>.Instance.itemLookup)
{
Item value = item.Value;
if ((Object)(object)value == (Object)null)
{
continue;
}
LootData component = ((Component)value).GetComponent<LootData>();
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<SpawnPool> 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<SpawnPool> hashSet = new HashSet<SpawnPool>();
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<ushort> 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;
}
}