using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Net; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Threading; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using REPOForge.Compat; using REPOForge.Network; using REPOForge.Shop; using UnityEngine; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("REPOForge")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("REPOForge")] [assembly: AssemblyTitle("REPOForge")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace REPOForge { public static class Forge { public static class Bus { public static void Subscribe(Action handler) { PluginBus.Subscribe(handler); } public static void Unsubscribe(Action handler) { PluginBus.Unsubscribe(handler); } public static void Publish(T evt) { PluginBus.Publish(evt); } } public static class Library { public static int Count => ((IReadOnlyCollection)ModLibrary.Entries).Count; public static bool ShopSkip(string itemName) { return ModLibrary.ShopSkip(itemName); } public static bool SelfSpawns(string itemName) { return ModLibrary.SelfSpawns(itemName); } } public static class Shop { public static string? LastManifestHash => ShopCoordinator.LastHash; public static void Register(ShopIntent intent) { ShopCoordinator.Register(intent); } public static void Unregister(string itemName) { ShopCoordinator.Unregister(itemName); } } public static class Compat { public static string Fingerprint => DiscrepancyGuard.LocalFingerprint; public static void Declare(CompatManifest manifest) { DiscrepancyGuard.Declare(manifest); } } public static class Network { public static bool RegisterPrefab(string path, GameObject prefab) { return PrefabRegistry.Register(path, prefab); } public static GameObject? SpawnItem(object item, Vector3 position, Quaternion rotation) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) return PrefabRegistry.SpawnItem(item, position, rotation); } } public static class Harmony { public static void Claim(string method, HarmonyLane lane, string owner) { HarmonyArbiter.Claim(method, lane, owner); } } } public static class PluginBus { private static readonly Dictionary> _subs = new Dictionary>(); public static void Subscribe(Action handler) { Type typeFromHandle = typeof(T); List value = null; if (!_subs.TryGetValue(typeFromHandle, out value)) { value = new List(); _subs[typeFromHandle] = value; } value.Add(handler); } public static void Unsubscribe(Action handler) { List value = null; if (_subs.TryGetValue(typeof(T), out value)) { value.Remove(handler); } } public static void Publish(T evt) { List value = null; if (evt == null || !_subs.TryGetValue(typeof(T), out value)) { return; } Delegate[] array = value.ToArray(); foreach (Delegate obj in array) { try { ((Action)obj)(evt); } catch (Exception arg) { Plugin.Log.LogError((object)$"Bus handler for {typeof(T).Name} threw: {arg}"); } } } } public sealed class ShopReadyEvent { public string ManifestHash { get; set; } = ""; public int Count { get; set; } public int Seed { get; set; } } public sealed class DriftReportEvent { public bool HardMismatch { get; set; } public string HostFingerprint { get; set; } = ""; public string ClientFingerprint { get; set; } = ""; public List Findings { get; set; } = new List(); } public enum HarmonyLane { Core = 0, Forge = 100, Content = 400, Override = 800 } public enum ItemVolumeKind { Small, Medium, Large, Large_wide, Power_crystal, Large_high, Rubber_duck, Health_pack, Large_plus } public enum ShopCategoryKind { Items, Consumables, Upgrades, Health, Secret } public sealed class ShopIntent { public string ItemName { get; set; } = ""; public string PrefabPath { get; set; } = ""; public float Weight { get; set; } = 10f; public int MaxInShop { get; set; } = 1; public ItemVolumeKind Volume { get; set; } = ItemVolumeKind.Medium; public ShopCategoryKind Category { get; set; } public int PriceMin { get; set; } = 4000; public int PriceMax { get; set; } = 8000; public object? Item { get; set; } } public sealed class CompatManifest { public string Guid { get; set; } = ""; public string Version { get; set; } = ""; public string DisplayName { get; set; } = ""; public bool HostRequired { get; set; } = true; public List PrefabPaths { get; } = new List(); public List PatchClaims { get; } = new List(); } internal static class GameAccess { private const BindingFlags Flags = BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; private static readonly Dictionary<(Type, string), MemberInfo?> Cache = new Dictionary<(Type, string), MemberInfo>(); private static readonly HashSet Dumped = new HashSet(); private static bool _roleLogged; internal static Type? Type(string name) { return AccessTools.TypeByName(name) ?? AccessTools.TypeByName(name + ", Assembly-CSharp"); } internal static object? Instance(string typeName) { Type type = Type(typeName); if (type == null) { return null; } return GetStatic(type, "instance") ?? GetStatic(type, "Instance") ?? GetStatic(type, "Singleton"); } internal static bool IsMaster() { bool? flag = PhotonIsMaster(); if (flag.HasValue) { LogRoleOnce(flag.Value); return flag.Value; } try { Type type = Type("SemiFunc"); MethodInfo methodInfo = ((type != null) ? type.GetMethod("IsMasterClientOrSingleplayer", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) : null) ?? ((type != null) ? type.GetMethod("IsMasterClient", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) : null); if (methodInfo != null) { bool num = (bool)methodInfo.Invoke(null, null); LogRoleOnce(num); return num; } } catch (Exception) { } return !IsMultiplayer(); } private static bool? PhotonIsMaster() { try { Type type = Type("Photon.Pun.PhotonNetwork") ?? Type("PhotonNetwork"); if (type == null) { return null; } bool? flag = StaticBool(type, "IsMasterClient") ?? StaticBool(type, "isMasterClient"); bool? flag2 = StaticBool(type, "InRoom") ?? StaticBool(type, "inRoom"); if (flag2 == true && flag.HasValue) { return flag.Value; } if ((StaticBool(type, "OfflineMode") ?? StaticBool(type, "offlineMode")) == true) { return true; } return (flag2 == false) | flag; } catch (Exception) { return null; } } private static void LogRoleOnce(bool master) { if (_roleLogged) { return; } _roleLogged = true; try { Plugin.Log.LogInfo((object)(master ? "Co-op: this machine is the host. Shop items spawn here; others receive them." : "Co-op: this machine is a client. Shop spawn is skipped here so it does not desync.")); } catch (Exception) { } } internal static void ResetRoleLog() { _roleLogged = false; } internal static bool IsMultiplayer() { try { Type type = Type("Photon.Pun.PhotonNetwork") ?? Type("PhotonNetwork"); if (type != null) { bool? flag = StaticBool(type, "InRoom") ?? StaticBool(type, "inRoom"); bool? flag2 = StaticBool(type, "OfflineMode") ?? StaticBool(type, "offlineMode"); if (flag == true && flag2 != true) { return true; } object obj = GetStatic(type, "CountOfPlayersInRooms") ?? GetStatic(type, "CountOfPlayers"); if (obj is int && (int)obj > 1) { return true; } } } catch (Exception) { } try { Type type2 = Type("SemiFunc") ?? Type("GameManager"); MethodInfo methodInfo = ((type2 != null) ? type2.GetMethod("IsMultiplayer", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) : null) ?? ((type2 != null) ? type2.GetMethod("Multiplayer", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) : null); if (methodInfo != null) { return (bool)methodInfo.Invoke(null, null); } } catch (Exception) { } return false; } private static bool? StaticBool(Type t, string name) { object obj = GetStatic(t, name); if (obj is bool) { return (bool)obj; } return null; } internal static object? PhotonLocalPlayer() { Type type = Type("Photon.Pun.PhotonNetwork") ?? Type("PhotonNetwork"); object obj; if (!(type == null)) { obj = GetStatic(type, "LocalPlayer"); if (obj == null) { return GetStatic(type, "localPlayer"); } } else { obj = null; } return obj; } internal static IEnumerable PhotonPlayers() { Type type = Type("Photon.Pun.PhotonNetwork") ?? Type("PhotonNetwork"); object obj = ((type == null) ? null : (GetStatic(type, "PlayerList") ?? GetStatic(type, "playerList"))); IEnumerable enumerable = (IEnumerable)((obj is IEnumerable) ? obj : null); if (enumerable == null) { yield break; } IEnumerator enumerator = enumerable.GetEnumerator(); try { while (enumerator.MoveNext()) { object current = enumerator.Current; if (current != null) { yield return current; } } } finally { ((IDisposable)((enumerator is IDisposable) ? enumerator : null))?.Dispose(); } } internal static bool IsNotMaster() { return !IsMaster(); } internal static bool PhotonInRoom() { Type type = Type("Photon.Pun.PhotonNetwork") ?? Type("PhotonNetwork"); if (type == null) { return false; } try { if (!((GetStatic(type, "InRoom") ?? GetStatic(type, "inRoom")) is bool result)) { object obj = GetStatic(type, "IsConnectedAndReady"); bool flag = false; int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } return (byte)(num & (flag ? 1 : 0)) != 0; } return result; } catch (Exception) { return false; } } internal static IList? ListField(object obj, string name) { object obj2 = Read(obj, name); return (IList)((obj2 is IList) ? obj2 : null); } internal static IEnumerable AllStatItems() { object stats = Instance("StatsManager"); if (stats == null) { yield break; } DumpType(stats.GetType()); Type itemType = Type("Item"); HashSet seen = new HashSet(); FieldInfo[] fields = stats.GetType().GetFields(BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); FieldInfo[] array = fields; foreach (FieldInfo fieldInfo in array) { object value; try { value = fieldInfo.GetValue(stats); } catch (Exception) { continue; } IDictionary dictionary = (IDictionary)((value is IDictionary) ? value : null); if (dictionary != null) { IDictionaryEnumerator enumerator = dictionary.GetEnumerator(); try { while (enumerator.MoveNext()) { object value2 = ((DictionaryEntry)enumerator.Current).Value; if (value2 != null && (!(itemType != null) || itemType.IsInstanceOfType(value2)) && seen.Add(value2.GetHashCode())) { yield return value2; } } } finally { ((IDisposable)((enumerator is IDisposable) ? enumerator : null))?.Dispose(); } continue; } IList list = (IList)((value is IList) ? value : null); if (list == null) { continue; } IEnumerator enumerator2 = list.GetEnumerator(); try { while (enumerator2.MoveNext()) { object current = enumerator2.Current; if (current != null && (!(itemType != null) || itemType.IsInstanceOfType(current)) && seen.Add(current.GetHashCode())) { yield return current; } } } finally { ((IDisposable)((enumerator2 is IDisposable) ? enumerator2 : null))?.Dispose(); } } } internal static GameObject? PrefabObject(object? item) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown if (item == null) { return null; } object member = GetMember(item, "prefab"); return (GameObject)(((member is GameObject) ? member : null) ?? null); } internal static void SetEnum(object obj, string name, string value) { if (obj == null || string.IsNullOrEmpty(value)) { return; } FieldInfo field = obj.GetType().GetField(name, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (field == null || !field.FieldType.IsEnum) { return; } try { field.SetValue(obj, Enum.Parse(field.FieldType, value, ignoreCase: true)); } catch (Exception) { } } internal static IList? NewItemList(object item) { Type type = Type("Item"); if (type == null || item == null) { return null; } try { IList obj = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(type)); obj.Add(item); return obj; } catch (Exception) { return null; } } internal static void SetField(object obj, string name, object? value) { FieldInfo field = obj.GetType().GetField(name, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { field.SetValue(obj, value); } } internal static void DumpType(Type t) { DumpOnce(t); } internal static int ReadInt(object obj, string name, int fallback = 0) { object obj2 = Read(obj, name); if (obj2 is int) { return (int)obj2; } if (obj2 is float) { return (int)(float)obj2; } return fallback; } internal static void RaiseInt(object obj, string name, int atLeast) { if (obj != null && atLeast > 0) { int num = ReadInt(obj, name); if (atLeast > num) { SetField(obj, name, atLeast); } } } internal static object[] FindAll(string typeName, bool includeInactive = false) { //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Expected O, but got Unknown //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) Type type = Type(typeName); if (type == null) { return Array.Empty(); } try { MethodInfo methodInfo = AccessTools.Method(typeof(Resources), "FindObjectsOfTypeAll", new Type[1] { typeof(Type) }, (Type[])null); if (methodInfo != null && methodInfo.Invoke(null, new object[1] { type }) is Array { Length: >0 } array) { List list = new List(array.Length); for (int i = 0; i < array.Length; i++) { object value = array.GetValue(i); if (value == null || (Object)((value is Object) ? value : null) == (Object)null) { continue; } try { Component val = (Component)((value is Component) ? value : null); if ((Object)(object)val != (Object)null) { if ((int)((Object)val).hideFlags != 0 || ((Object)(object)val.transform != (Object)null && val.transform.position.y < -500f)) { continue; } Scene scene = val.gameObject.scene; if (!((Scene)(ref scene)).IsValid() || (((Scene)(ref scene)).name ?? "").IndexOf("DontDestroyOnLoad", StringComparison.OrdinalIgnoreCase) >= 0) { continue; } } } catch (Exception) { } list.Add(value); } if (list.Count > 0) { return list.ToArray(); } } } catch (Exception) { } try { object[] array2 = InvokeFind(typeof(Object), "FindObjectsOfType", type, includeInactive: true); if (array2 != null && array2.Length != 0) { return array2; } return InvokeFind(typeof(Object), "FindObjectsOfType", type, includeInactive); } catch (Exception) { return Array.Empty(); } } private static object[] InvokeFind(Type host, string method, Type target, bool includeInactive = false) { MethodInfo[] methods = host.GetMethods(BindingFlags.Static | BindingFlags.Public); foreach (MethodInfo methodInfo in methods) { if (methodInfo.Name != method) { continue; } ParameterInfo[] parameters = methodInfo.GetParameters(); object[] array = null; if (parameters.Length == 2 && parameters[0].ParameterType == typeof(Type) && parameters[1].ParameterType == typeof(bool)) { array = new object[2] { target, includeInactive }; } else if (!includeInactive && parameters.Length == 1 && parameters[0].ParameterType == typeof(Type)) { array = new object[1] { target }; } if (array != null) { object obj = methodInfo.Invoke(null, array); Array array2 = (Array)((obj is Array) ? obj : null); if (array2 != null && array2.Length > 0) { object[] array3 = new object[array2.Length]; array2.CopyTo(array3, 0); return array3; } } } return Array.Empty(); } internal static object? GetMember(object obj, string name) { return Read(obj, name); } internal static string ItemName(object item) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) object obj = Read(item, "itemName"); object obj2 = ((obj is string) ? obj : null); if (obj2 == null) { object obj3 = Read(item, "name"); obj2 = ((obj3 is string) ? obj3 : null); if (obj2 == null) { object obj4 = ((item is Object) ? item : null); obj2 = ((obj4 != null) ? ((Object)obj4).name : null) ?? item.ToString(); } } return (string)obj2; } internal static string PrefabPath(object item) { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Expected O, but got Unknown //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Expected O, but got Unknown DumpOnce(item.GetType()); object obj = Read(item, "prefab"); if (obj != null) { DumpOnce(obj.GetType()); object obj2 = Read(obj, "resourcePath"); string text = (string)(((obj2 is string) ? obj2 : null) ?? null); if (!string.IsNullOrEmpty(text)) { return text; } GameObject val = (GameObject)(((obj is GameObject) ? obj : null) ?? null); if ((Object)val != (Object)null) { return "Items/" + ((Object)val).name; } } return "Items/" + ItemName(item); } internal static string ItemVolume(object item) { return (Read(item, "itemVolume") ?? Read(item, "volume") ?? Read(item, "itemType"))?.ToString() ?? "Medium"; } internal static int MaxInShop(object item) { if ((Read(item, "maxAmountInShop") ?? Read(item, "maxAmount") ?? Read(item, "maxPurchaseAmount")) is int val) { return Math.Max(1, val); } return 1; } internal static int AveragePrice(object item) { object obj = Read(item, "value") ?? Read(item, "valuePreset") ?? Read(item, "ValuePreset"); if (obj != null) { DumpOnce(obj.GetType()); } object obj2 = obj ?? item; int num = CoerceInt(Read(obj2, "valueMin") ?? Read(obj2, "min") ?? Read(obj2, "from") ?? Read(obj2, "x")); int num2 = CoerceInt(Read(obj2, "valueMax") ?? Read(obj2, "max") ?? Read(obj2, "to") ?? Read(obj2, "y")); if (num <= 0 && num2 <= 0) { num = CoerceInt(Read(obj2, "value")); } if (num <= 0) { num = 8000; } if (num2 <= 0) { num2 = num; } return Math.Max(1, (num + num2) / 2); } private static int CoerceInt(object? v) { if (v is int) { return (int)v; } if (v is float) { return (int)(float)v; } if (v is double) { return (int)(double)v; } if (v is short) { return (short)v; } return 0; } private static object? GetStatic(Type t, string name) { MemberInfo memberInfo = Resolve(t, name); try { FieldInfo fieldInfo = (FieldInfo)((memberInfo is FieldInfo) ? memberInfo : null); if (fieldInfo != null) { return fieldInfo.GetValue(null); } PropertyInfo propertyInfo = (PropertyInfo)((memberInfo is PropertyInfo) ? memberInfo : null); if (propertyInfo != null && propertyInfo.CanRead) { return propertyInfo.GetValue(null); } } catch (Exception) { } return null; } private static object? Read(object obj, string name) { if (obj == null) { return null; } MemberInfo memberInfo = Resolve(obj.GetType(), name); try { FieldInfo fieldInfo = (FieldInfo)((memberInfo is FieldInfo) ? memberInfo : null); if (fieldInfo != null) { return fieldInfo.GetValue(obj); } PropertyInfo propertyInfo = (PropertyInfo)((memberInfo is PropertyInfo) ? memberInfo : null); if (propertyInfo != null && propertyInfo.CanRead) { return propertyInfo.GetValue(obj); } } catch (Exception) { } return null; } private static MemberInfo? Resolve(Type t, string name) { (Type, string) tuple = default((Type, string)); tuple = (t, name); MemberInfo value = null; if (Cache.TryGetValue(tuple, out value)) { return value; } MemberInfo memberInfo = t.GetField(name, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (memberInfo == null) { memberInfo = t.GetProperty(name, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); } Cache[tuple] = memberInfo; return memberInfo; } private static void DumpOnce(Type t) { if (t == null || !Dumped.Add(t.FullName ?? t.Name)) { return; } try { List list = new List(); FieldInfo[] fields = t.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { list.Add(fieldInfo.FieldType.Name + " " + fieldInfo.Name); } PropertyInfo[] properties = t.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (PropertyInfo propertyInfo in properties) { if (propertyInfo.GetIndexParameters().Length == 0) { list.Add(propertyInfo.PropertyType.Name + " " + propertyInfo.Name + "{get}"); } } Plugin.Log.LogInfo((object)("[reflect] " + t.Name + ": " + ((list.Count == 0) ? "(no instance members)" : string.Join(", ", list)))); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[reflect] " + t.Name + " dump failed: " + ex.Message)); } } } [BepInPlugin("com.repoforge.core", "REPOForge", "1.5.9")] public sealed class Plugin : BaseUnityPlugin { [Serializable] [CompilerGenerated] private sealed class __c { public static readonly __c __9 = new __c(); public static Action __9__28_0; public static Action __9__28_1; public static Action __9__28_2; public static Action __9__28_3; internal void Awake_b__28_0() { HarmonyArbiter.Install(Harmony); } internal void Awake_b__28_1() { PrefabRegistry.Install(Harmony); } internal void Awake_b__28_2() { ShopCoordinator.Install(Harmony); } internal void Awake_b__28_3() { DiscrepancyGuard.Install(Harmony); } } internal static ConfigEntry Enabled; internal static ConfigEntry Handshake; internal static ConfigEntry KickOnHardMismatch; internal static ConfigEntry PlaceholderMissingPrefabs; internal static ConfigEntry VerboseLogging; internal static ConfigEntry ShopPipeline; internal static ConfigEntry PriceWeight; internal static ConfigEntry ExtraShelves; internal static ConfigEntry ItemSpawnTargetAmount; internal static ConfigEntry FillEmptyVolumes; internal static ConfigEntry DiversifyPool; internal static ConfigEntry PreferModItems; internal static ConfigEntry MaxCopiesPerItem; internal static ConfigEntry RemapSmallToMedium; internal static ConfigEntry AdoptUnlistedMods; internal static ConfigEntry RefreshLibrary; private static bool _deferredBootDone; private static bool _deferredBootScheduled; private static bool _deferredBootHooked; private static bool _deferredUpdateLogged; private static long _deferredBootStartTicks; private const double DeferredMinWaitSec = 1.0; private const double DeferredMaxWaitSec = 12.0; internal static Plugin Instance { get; private set; } internal static ManualLogSource Log { get; private set; } internal static Harmony Harmony { get; private set; } private void Awake() { //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Expected O, but got Unknown //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Expected O, but got Unknown //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Expected O, but got Unknown //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Expected O, but got Unknown //IL_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_01fd: Expected O, but got Unknown //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Expected O, but got Unknown //IL_0322: Unknown result type (might be due to invalid IL or missing references) //IL_032c: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; Log.LogInfo((object)"REPOForge 1.5.9 waking."); Enabled = ((BaseUnityPlugin)this).Config.Bind("General", "Enabled", true, "Master switch."); Handshake = ((BaseUnityPlugin)this).Config.Bind("Compat", "Handshake", false, "In co-op, compare loaded mods with other players and log a mismatch. Default OFF — avoids Photon hang when not connected."); KickOnHardMismatch = ((BaseUnityPlugin)this).Config.Bind("Compat", "KickOnHardMismatch", false, "Kick clients with hard mismatches. Default is warn-only."); PlaceholderMissingPrefabs = ((BaseUnityPlugin)this).Config.Bind("Compat", "PlaceholderMissingPrefabs", true, "Spawn a placeholder instead of crashing when a prefab is missing on a client."); VerboseLogging = ((BaseUnityPlugin)this).Config.Bind("Compat", "VerboseLogging", false, "Extra log lines for patches and shop rolls."); ShopPipeline = ((BaseUnityPlugin)this).Config.Bind("Shop", "Pipeline", true, "Intercept shop item lists and spawn through the host manifest."); PriceWeight = ((BaseUnityPlugin)this).Config.Bind("Shop", "UseShopPriceForItemSelection", false, "Reorder the pool so cheaper items spawn more often. Off by default — vanilla/MSI already weight by duplicate entries."); ExtraShelves = ((BaseUnityPlugin)this).Config.Bind("Shop", "ExtraShelves", 0, new ConfigDescription("Unused. Kept so old configs still load.", (AcceptableValueBase)new AcceptableValueRange(0, 6), Array.Empty())); ItemSpawnTargetAmount = ((BaseUnityPlugin)this).Config.Bind("Shop", "ItemSpawnTargetAmount", 0, new ConfigDescription("Hard cap. Ignored while FillEmptyVolumes is on. 0 = do not override.", (AcceptableValueBase)new AcceptableValueRange(0, 256), Array.Empty())); FillEmptyVolumes = ((BaseUnityPlugin)this).Config.Bind("Shop", "FillEmptyVolumes", true, "Raise spawn budgets to match empty shelf slots. Never lowers More Shop Items."); DiversifyPool = ((BaseUnityPlugin)this).Config.Bind("Shop", "DiversifyPool", true, "Round-robin unique items so one type cannot fill every slot (e.g. only medium health packs)."); PreferModItems = ((BaseUnityPlugin)this).Config.Bind("Shop", "PreferModItems", true, "Give non-vanilla (REPOLib / other mods) items a guaranteed shelf slot before vanilla repeats."); MaxCopiesPerItem = ((BaseUnityPlugin)this).Config.Bind("Shop", "MaxCopiesPerItem", 2, new ConfigDescription("Max copies of one prefab on the shelves. 1 = all unique, 2 = at most a pair.", (AcceptableValueBase)new AcceptableValueRange(1, 16), Array.Empty())); RemapSmallToMedium = ((BaseUnityPlugin)this).Config.Bind("Shop", "RemapSmallItemsOntoMediumVolumes", true, "If no unused item matches the shelf volume, place a different unused item instead of a duplicate."); AdoptUnlistedMods = ((BaseUnityPlugin)this).Config.Bind("Shop", "AdoptUnlistedMods", false, "Off: only items already in the shop roll (REPOLib RegisterItem / vanilla GetAll). On: also pull extra StatsManager items that look like shop gear (never valuables or secret attic loot)."); RefreshLibrary = ((BaseUnityPlugin)this).Config.Bind("Compat", "RefreshLibrary", false, "On Start, scan BepInEx/plugins manifests and optionally fetch the Thunderstore R.E.P.O. package index. Default OFF — full catalog (+31585) froze lobby join."); if (AdoptUnlistedMods.Value) { Log.LogWarning((object)"Shop.AdoptUnlistedMods was on (1.2.0 stuffed level loot into the shop). Turning off. Future shop mods still appear via REPOLib RegisterItem."); AdoptUnlistedMods.Value = false; } if (FillEmptyVolumes.Value && ItemSpawnTargetAmount.Value > 0 && ItemSpawnTargetAmount.Value <= 28) { Log.LogWarning((object)$"Shop.ItemSpawnTargetAmount was {ItemSpawnTargetAmount.Value} (old cap). Ignored — FillEmptyVolumes fills every shelf. Set FillEmptyVolumes=false if you want a hard cap."); ItemSpawnTargetAmount.Value = 0; } if (MaxCopiesPerItem.Value >= 6) { Log.LogWarning((object)$"Shop.MaxCopiesPerItem was {MaxCopiesPerItem.Value} (old default). Reset to 2 so shelves are not walls of the same crystal/pan."); MaxCopiesPerItem.Value = 2; } Harmony = new Harmony("com.repoforge.core"); if (!Enabled.Value) { Log.LogWarning((object)"REPOForge is disabled in config."); return; } Log.LogInfo((object)"REPOForge 1.5.9 config bound; Harmony patches deferred until after Steam/Photon auth (Lobby / ConnectedToMaster)."); try { ScheduleDeferredBoot(); } catch (Exception ex) { Log.LogWarning((object)("Deferred boot schedule from Awake failed: " + ex.Message)); } } private void Start() { if (!Enabled.Value) { return; } try { try { string path = Path.Combine(Paths.ConfigPath, "com.repoforge.library.cache.json"); if (File.Exists(path) && !RefreshLibrary.Value) { File.Delete(path); Log.LogInfo((object)"Deleted com.repoforge.library.cache.json (RefreshLibrary=false)."); } } catch (Exception ex) { Log.LogDebug((object)("Cache cleanup: " + ex.Message)); } } catch (Exception ex2) { Log.LogWarning((object)("Cache cleanup skipped: " + ex2.Message)); } ScheduleDeferredBoot(); } private static bool IsPastNetworkAuth() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) try { if (GameAccess.PhotonInRoom()) { return true; } } catch (Exception) { } try { Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; if (!string.IsNullOrEmpty(name) && (name.IndexOf("Lobby", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("Menu", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("Shop", StringComparison.OrdinalIgnoreCase) >= 0 || name.Equals("Main", StringComparison.OrdinalIgnoreCase))) { return true; } } catch (Exception) { } try { Type type = GameAccess.Type("Photon.Pun.PhotonNetwork") ?? GameAccess.Type("PhotonNetwork"); if (type == null) { return false; } PropertyInfo propertyInfo = AccessTools.Property(type, "InRoom"); if (propertyInfo != null) { object value = propertyInfo.GetValue(null, null); bool flag = default(bool); int num; if (value is bool) { flag = (bool)value; num = 1; } else { num = 0; } if (((uint)num & (flag ? 1u : 0u)) != 0) { return true; } } PropertyInfo propertyInfo2 = AccessTools.Property(type, "CloudRegion"); if (!string.IsNullOrEmpty(((propertyInfo2 != null) ? propertyInfo2.GetValue(null, null) : null) as string)) { return true; } PropertyInfo propertyInfo3 = AccessTools.Property(type, "NetworkClientState"); if (propertyInfo3 != null) { object value2 = propertyInfo3.GetValue(null, null); string text = ((value2 != null) ? value2.ToString() : ""); if (!string.IsNullOrEmpty(text) && (text.IndexOf("ConnectedToMaster", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("JoinedLobby", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("Joined", StringComparison.OrdinalIgnoreCase) >= 0)) { return true; } } } catch (Exception) { } return false; } private static double DeferredElapsedSec() { return (double)(DateTime.UtcNow.Ticks - _deferredBootStartTicks) / 10000000.0; } private void ScheduleDeferredBoot() { if (!_deferredBootDone && Enabled.Value) { if (!_deferredBootScheduled) { _deferredBootScheduled = true; _deferredBootStartTicks = DateTime.UtcNow.Ticks; Log.LogInfo((object)"Deferred boot armed (Update + sceneLoaded; DateTime clock, no Unity Time)."); } HookDeferredSceneLoaded(); TryFinishDeferredBoot("schedule"); } } private void HookDeferredSceneLoaded() { if (_deferredBootHooked) { return; } _deferredBootHooked = true; try { SceneManager.sceneLoaded += OnDeferredSceneLoaded; } catch (Exception ex) { Log.LogWarning((object)("sceneLoaded hook failed: " + ex.Message)); } } private void OnDeferredSceneLoaded(Scene scene, LoadSceneMode mode) { try { string text = ((Scene)(ref scene)).name ?? ""; Log.LogInfo((object)("Deferred boot saw scene: " + text)); TryFinishDeferredBoot("scene:" + text); } catch (Exception ex) { Log.LogWarning((object)("OnDeferredSceneLoaded: " + ex.Message)); } } private void Update() { if (!_deferredBootScheduled || _deferredBootDone) { return; } if (!_deferredUpdateLogged) { _deferredUpdateLogged = true; try { Log.LogInfo((object)"Deferred boot Update() is running."); } catch (Exception) { } } TryFinishDeferredBoot("update"); } private void TryFinishDeferredBoot(string reason) { //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) if (_deferredBootDone || !_deferredBootScheduled || !Enabled.Value) { return; } double num = DeferredElapsedSec(); if (num < 0.0) { num = 0.0; } bool flag = false; bool flag2 = false; try { flag = num >= 1.0 && IsPastNetworkAuth(); } catch (Exception ex) { Log.LogWarning((object)("IsPastNetworkAuth threw: " + ex.Message)); } if (!flag && num >= 3.0) { try { Scene activeScene = SceneManager.GetActiveScene(); string text = ((Scene)(ref activeScene)).name ?? ""; if (text.IndexOf("Main", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("Lobby", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("Shop", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("Menu", StringComparison.OrdinalIgnoreCase) >= 0) { flag = true; } } catch (Exception) { } } if (!flag && num >= 12.0) { flag2 = true; flag = true; } if (!flag) { return; } _deferredBootDone = true; try { if (_deferredBootHooked) { SceneManager.sceneLoaded -= OnDeferredSceneLoaded; _deferredBootHooked = false; } } catch (Exception) { } if (flag2) { Log.LogWarning((object)$"Deferred boot timed out after {num:0.0}s ({reason}) — applying patches anyway."); } else { Log.LogInfo((object)$"Past network auth after {num:0.0}s ({reason}) — applying deferred Harmony installs."); } try { ApplyDeferredInstalls(); } catch (Exception ex4) { Log.LogError((object)("ApplyDeferredInstalls failed: " + ex4.ToString())); } } private void ApplyDeferredInstalls() { //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Expected O, but got Unknown object obj = (Action)delegate { HarmonyArbiter.Install(Harmony); }; SafeInstall("HarmonyArbiter", (Action)obj); object obj2 = (Action)delegate { PrefabRegistry.Install(Harmony); }; SafeInstall("PrefabRegistry", (Action)obj2); object obj3 = (Action)delegate { ShopCoordinator.Install(Harmony); }; SafeInstall("ShopCoordinator", (Action)obj3); object obj4 = (Action)delegate { DiscrepancyGuard.Install(Harmony); }; SafeInstall("DiscrepancyGuard", (Action)obj4); Log.LogInfo((object)"REPOForge 1.5.9 loaded (deferred). Duplicate copies of this GUID are skipped by BepInEx — keep one folder."); try { ModLibrary.Boot(); } catch (Exception ex) { Log.LogWarning((object)("Library boot skipped: " + ex.Message)); } try { DiscrepancyGuard.DiscoverLoadedPlugins(); } catch (Exception ex2) { Log.LogWarning((object)("Auto-discover skipped: " + ex2.Message)); } try { DiscrepancyGuard.BeginWatch((MonoBehaviour)this); } catch (Exception ex3) { Log.LogWarning((object)("Handshake watch skipped: " + ex3.Message)); } } private void OnDestroy() { Harmony harmony = Harmony; if (harmony != null) { harmony.UnpatchSelf(); } } private static void SafeInstall(string name, Action fn) { try { fn(); } catch (Exception ex) { Log.LogError((object)(name + " install failed (continuing): " + ex.GetBaseException().Message)); } } } public static class MyPluginInfo { public const string GUID = "com.repoforge.core"; public const string Name = "REPOForge"; public const string Version = "1.5.9"; } } namespace REPOForge.Compat { internal static class DiscrepancyGuard { [CompilerGenerated] private sealed class __c__DisplayClass10_0 { public CompatManifest manifest; internal bool Declare_b__0(CompatManifest m) { return m.Guid == manifest.Guid; } } private static readonly List _manifests = new List(); private static readonly HashSet _seenPeers = new HashSet(StringComparer.OrdinalIgnoreCase); private static string _cached = ""; private static bool _dirty = true; internal static string LocalFingerprint { get { if (_dirty) { _cached = Compute(); } return _cached; } } internal static void Install(Harmony _harmony) { Plugin.Log.LogInfo((object)"Handshake is poll-only. Photon join is not patched (that hung co-op loading in 1.5.0)."); } internal static void BeginWatch(MonoBehaviour host) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown if (!((Object)host == (Object)null)) { if (!Plugin.Handshake.Value) { Plugin.Log.LogInfo((object)"Handshake disabled — WatchRoom not started (co-op stability)."); } else { host.StartCoroutine(WatchRoom()); } } } private static IEnumerator WatchRoom() { bool wasIn = false; WaitForSecondsRealtime wait = new WaitForSecondsRealtime(0.75f); while (true) { yield return wait; bool flag; try { flag = GameAccess.PhotonInRoom(); } catch (Exception) { flag = false; } if (flag && !wasIn) { try { GameAccess.ResetRoleLog(); if (Plugin.Enabled.Value && Plugin.Handshake.Value) { Plugin.Log.LogInfo((object)$"[OnJoinedRoom] fingerprint {LocalFingerprint} ({_manifests.Count} mods) host={GameAccess.IsMaster()} coop={GameAccess.IsMultiplayer()}. Compare this line with other players."); ScanRoom("join"); } } catch (Exception ex2) { Plugin.Log.LogWarning((object)("Handshake failed: " + ex2.Message)); } } if (!flag && wasIn) { _seenPeers.Clear(); GameAccess.ResetRoleLog(); } wasIn = flag; } } internal static void DiscoverLoadedPlugins() { try { foreach (PluginInfo value in Chainloader.PluginInfos.Values) { BepInPlugin metadata = value.Metadata; if (metadata != null) { CompatManifest obj = new CompatManifest { Guid = metadata.GUID }; Version version = metadata.Version; obj.Version = ((version != null) ? version.ToString() : null) ?? "0"; obj.DisplayName = metadata.Name ?? metadata.GUID; obj.HostRequired = metadata.GUID != "com.repoforge.core"; Declare(obj); } } Plugin.Log.LogInfo((object)$"Auto-discovered {_manifests.Count} plugins. Fingerprint {LocalFingerprint}."); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Plugin auto-discover skipped: " + ex.Message)); } } internal static void Declare(CompatManifest manifest) { __c__DisplayClass10_0 CS__8__locals7 = new __c__DisplayClass10_0(); CS__8__locals7.manifest = manifest; if (CS__8__locals7.manifest != null && !string.IsNullOrEmpty(CS__8__locals7.manifest.Guid)) { _manifests.RemoveAll((CompatManifest m) => m.Guid == CS__8__locals7.manifest.Guid); _manifests.Add(CS__8__locals7.manifest); _dirty = true; if (Plugin.VerboseLogging.Value) { Plugin.Log.LogInfo((object)("Compat declared " + CS__8__locals7.manifest.Guid + "@" + CS__8__locals7.manifest.Version)); } } } private static void ScanRoom(string reason) { try { foreach (object item in GameAccess.PhotonPlayers()) { ReadPlayer(item, reason); } } catch (Exception) { } } private static void ReadPlayer(object? player, string reason) { if (player == null) { return; } try { object member = GameAccess.GetMember(player, "NickName"); string text = (string)(((member is string) ? member : null) ?? "player"); object obj = GameAccess.PhotonLocalPlayer(); if (obj == null || obj != player) { object obj2 = GameAccess.GetMember(player, "CustomProperties") ?? GameAccess.GetMember(player, "customProperties"); string text2 = null; IDictionary dictionary = (IDictionary)((obj2 is IDictionary) ? obj2 : null); if (dictionary != null && dictionary.Contains("rf")) { text2 = dictionary["rf"]?.ToString(); } if (!string.IsNullOrEmpty(text2)) { ParsePayload(text2, text + "/" + reason); } } } catch (Exception) { } } private static void ParsePayload(string payload, string nick) { if (!string.IsNullOrEmpty(payload)) { string[] array = payload.Split('|'); string remote = array[0]; string version = ((array.Length > 1) ? array[1] : "?"); OnRemoteFingerprint(remote, version, nick); } } internal static void OnRemoteFingerprint(string remote, string version, string nick) { if (string.IsNullOrEmpty(remote)) { return; } string item = nick + "|" + remote; if (_seenPeers.Add(item)) { string localFingerprint = LocalFingerprint; List list = new List(); bool flag = localFingerprint != remote; if (flag) { list.Add("fingerprint " + localFingerprint + " vs " + remote); Plugin.Log.LogWarning((object)("Co-op mismatch with " + nick + ": this machine " + localFingerprint + ", theirs " + remote + " (Forge " + version + "). Use the same mods or the shop will desync.")); } else { Plugin.Log.LogInfo((object)("Co-op aligned with " + nick + " (" + remote + ")")); } PluginBus.Publish(new DriftReportEvent { HardMismatch = flag, HostFingerprint = (GameAccess.IsMaster() ? localFingerprint : remote), ClientFingerprint = (GameAccess.IsMaster() ? remote : localFingerprint), Findings = list }); if (flag && Plugin.KickOnHardMismatch.Value && GameAccess.IsMaster()) { Plugin.Log.LogWarning((object)("KickOnHardMismatch is on — host should kick " + nick + ".")); } } } private static string Compute() { _dirty = false; IOrderedEnumerable orderedEnumerable = Chainloader.PluginInfos.Values.Select((PluginInfo p) => $"{p.Metadata.GUID}@{p.Metadata.Version}").OrderBy((string result) => result, StringComparer.OrdinalIgnoreCase); IOrderedEnumerable orderedEnumerable2 = _manifests.SelectMany((CompatManifest m) => m.PrefabPaths).Distinct(StringComparer.OrdinalIgnoreCase).OrderBy((string result) => result, StringComparer.OrdinalIgnoreCase); string s = string.Join(";", orderedEnumerable) + "|" + string.Join(";", orderedEnumerable2); using SHA256 sHA = SHA256.Create(); return BitConverter.ToString(sHA.ComputeHash(Encoding.UTF8.GetBytes(s))).Replace("-", "").Substring(0, 12) .ToLowerInvariant(); } } internal static class HarmonyArbiter { [CompilerGenerated] private sealed class __c__DisplayClass2_0 { public string owner; internal bool Claim_b__0((string, HarmonyLane) c) { return c.Item1 == owner; } } private static readonly Dictionary> _claims = new Dictionary>(StringComparer.OrdinalIgnoreCase); private static bool _dumped; internal static void Install(Harmony harmony) { SceneManager.sceneLoaded += delegate { DumpIfNeeded(); }; } internal static void Claim(string method, HarmonyLane lane, string owner) { __c__DisplayClass2_0 CS__8__locals4 = new __c__DisplayClass2_0(); CS__8__locals4.owner = owner; if (string.IsNullOrEmpty(method) || string.IsNullOrEmpty(CS__8__locals4.owner)) { return; } List<(string, HarmonyLane)> value = null; if (!_claims.TryGetValue(method, out value)) { value = new List<(string, HarmonyLane)>(); _claims[method] = value; } value.RemoveAll(((string, HarmonyLane) c) => c.Item1 == CS__8__locals4.owner); value.Add((CS__8__locals4.owner, lane)); if (value.Count > 1) { string text = string.Join(", ", value.ConvertAll(((string, HarmonyLane) c) => $"{c.Item1}[{c.Item2}]")); bool flag = value.Exists(((string, HarmonyLane) c) => c.Item2 == HarmonyLane.Override) && value.Exists(((string, HarmonyLane) c) => c.Item2 != HarmonyLane.Override); Plugin.Log.LogWarning((object)("Harmony claim on " + method + ": " + text + (flag ? " — Override vs others, last skip-original Prefix will win unless they use Forge.Shop." : ""))); } } private static void DumpIfNeeded() { if (_dumped) { return; } _dumped = true; foreach (KeyValuePair> claim in _claims) { if (claim.Value.Count >= 2) { Plugin.Log.LogInfo((object)$"[arbiter] {claim.Key} owned by {claim.Value.Count} mods"); } } } } internal sealed class ModEntry { public string Guid = ""; public string Name = ""; public string Thunderstore = ""; public string Kind = "content"; public bool HostRequired = true; public readonly HashSet Policy = new HashSet(StringComparer.OrdinalIgnoreCase); public readonly List Markers = new List(); public readonly List Patches = new List(); public string Version = ""; public bool Installed; public bool Baked; } internal static class ModLibrary { [CompilerGenerated] private static class __O { public static ThreadStart _0__RefreshThunderstore; } [CompilerGenerated] private sealed class __c__DisplayClass12_0 { public string m; internal bool Upsert_b__0(string x) { return string.Equals(x, m, StringComparison.OrdinalIgnoreCase); } } [CompilerGenerated] private sealed class __c__DisplayClass12_1 { public string p; internal bool Upsert_b__1(string x) { return string.Equals(x, p, StringComparison.OrdinalIgnoreCase); } } private static readonly object Gate = new object(); private static readonly Dictionary ByGuid = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly List All = new List(); private static bool _booted; private static readonly string[][] Baked = new string[25][] { new string[7] { "com.repoforge.core", "REPOForge", "REPOForge-REPOForge", "library", "core", "", "ShopManager.GetAllItemsFromStatsManager|PunManager.SpawnShopItem" }, new string[7] { "Zehs.REPOLib", "REPOLib", "Zehs-REPOLib", "library", "content_api", "", "StatsManager.AddItem" }, new string[7] { "Jettcodey.MoreShopItems", "More Shop Items", "Jettcodey-MoreShopItems", "shop", "raise_budgets|keep_weights", "", "ShopManager.GetAllItemsFromStatsManager" }, new string[7] { "HeroHanex.NoItemSpawnLimit", "NoItemSpawnLimit", "HeroHanex-NoItemSpawnLimit", "shop", "raise_caps", "", "" }, new string[7] { "MEDVAC.HealerDrone", "MEDVAC Healer Drone", "cherdak-MEDVAC_Healer_Drone", "content", "self_spawns|shop_skip", "MEDVAC", "" }, new string[7] { "uz.cherdak.repo.cargobackpack", "C.A.R.G.O. Backpack", "cherdak-CARGO_Backpack_Mod", "content", "shop_pin", "C.A.R.G.O|CARGO Backpack", "" }, new string[7] { "uz.cherdak.repo.featherguarddrone", "Featherguard Drone", "cherdak-FeatherguardDrone", "content", "shop_pin", "Featherguard|Feather Guard", "" }, new string[7] { "uz.cherdak.repo.echomannequin", "Echo Mannequin", "cherdak-EchoMannequin", "content", "shop_skip", "Echo Mannequin|Item Echo", "" }, new string[7] { "com.github.zehsteam.LethalCompanyValuables", "LethalCompanyValuables", "Zehs-LethalCompanyValuables", "valuable", "valuables", "Valuable", "" }, new string[7] { "Rangerbb275.REPOing_Valuables", "REPOing Valuables", "Rangerbb275-REPOing_Valuables", "valuable", "valuables", "Valuable", "" }, new string[7] { "Roemi.Blackbox", "Blackbox", "Roemi-Blackbox", "qol", "", "", "" }, new string[7] { "MinecraftStrongholdLevel", "Minecraft Stronghold Level", "AriIcedT-MinecraftStrongholdLevel", "level", "level_loot", "MC Item", "" }, new string[7] { "DirtyGames.REPOGambling", "REPOGambling", "DirtyGames-REPOGambling", "shop", "shop_module", "", "" }, new string[7] { "Tolga.ShoppingCart", "ShoppingCart", "Tolga-ShoppingCart", "qol", "shop_module", "", "" }, new string[7] { "HVG.ShopSpawnByType", "ShopSpawnByType", "HVG_Solutions-ShopSpawnByType", "shop", "shop_list", "", "PunManager.SpawnShopItem|ShopManager.GetAllItemsFromStatsManager" }, new string[7] { "Zichen.ShopPlus", "ShopPlus", "Zichen-ShopPlus", "shop", "shop_list", "", "ShopManager.ShopInitialize|PunManager.SpawnShopItem" }, new string[7] { "SeroRonin.ItemBundles", "ItemBundles", "SeroRonin-ItemBundles", "shop", "shop_pin", "Bundle", "ShopManager.GetAllItemsFromStatsManager" }, new string[7] { "itsUndefined.Shop_Items_Spawn_in_Level", "Shop Items Spawn in Level", "itsUndefined-Shop_Items_Spawn_in_Level", "shop", "level_loot", "", "ValuableDirector.Spawn" }, new string[7] { "papucsevo.All_Shop_Items_In_Level", "All Shop Items In Level", "papucsevo-All_Shop_Items_In_Level", "shop", "level_loot", "", "ValuableDirector.Spawn" }, new string[7] { "BULLETBOT.MoreUpgrades", "MoreUpgrades", "BULLETBOT-MoreUpgrades", "content", "shop_pin", "", "" }, new string[7] { "nickklmao.repoconfig", "REPOConfig", "nickklmao-REPOConfig", "qol", "client_ok", "", "" }, new string[7] { "flipf17.DeadTTS", "DeadTTS", "flipf17-DeadTTS", "qol", "client_ok", "", "" }, new string[7] { "com.empress.blackboxfixer", "Empress Blackbox Fixer", "Empress-BlackboxFixer", "fix", "client_ok", "", "" }, new string[7] { "WesleysEnemies", "WesleysEnemies", "Wesley-WesleysEnemies", "content", "enemies", "", "" }, new string[7] { "WesleysLevels", "WesleysLevels", "Wesley-WesleysLevels", "level", "level_loot", "", "" } }; internal static IReadOnlyList Entries { get { lock (Gate) { return All.ToArray(); } } } internal static void Boot() { if (_booted) { return; } _booted = true; LoadBaked(); LoadJsonBesideDll(); ScanInstalledManifests(); BindChainloader(); ApplyHarmonyClaims(); int num = 0; int num2 = 0; foreach (ModEntry item in All) { if (item.Installed) { num++; } if (item.Baked) { num2++; } } Plugin.Log.LogInfo((object)$"Library: {num2} known REPO mods, {num} installed, {All.Count} total. Unknown packages are treated as shop-pin if they register via REPOLib."); if (Plugin.RefreshLibrary.Value) { object obj = new ThreadStart(RefreshThunderstore); Thread thread = new Thread((ThreadStart)obj); thread.IsBackground = true; thread.Name = "REPOForge.Library"; thread.Start(); } } internal static bool ShopSkip(string? name) { if (string.IsNullOrEmpty(name)) { return false; } if (name.IndexOf("Echo Mannequin", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("Item Echo", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } if (!MarkerHits(name, "shop_skip") && !MarkerHits(name, "self_spawns")) { return MarkerHits(name, "valuables"); } return true; } internal static bool SelfSpawns(string? name) { if (!string.IsNullOrEmpty(name)) { if (name.IndexOf("MEDVAC", StringComparison.OrdinalIgnoreCase) < 0) { return MarkerHits(name, "self_spawns"); } return true; } return false; } internal static bool LooksLikeValuable(string? blob) { if (!string.IsNullOrEmpty(blob)) { if (blob.IndexOf("valuable", StringComparison.OrdinalIgnoreCase) < 0) { return MarkerHits(blob, "valuables"); } return true; } return false; } internal static bool ShopPin(string? name) { if (!string.IsNullOrEmpty(name)) { return MarkerHits(name, "shop_pin"); } return false; } private static bool MarkerHits(string text, string policy) { lock (Gate) { foreach (ModEntry item in All) { if (!item.Policy.Contains(policy)) { continue; } foreach (string marker in item.Markers) { if (marker.Length > 0 && text.IndexOf(marker, StringComparison.OrdinalIgnoreCase) >= 0) { return true; } } } } return false; } private static void Upsert(ModEntry incoming) { if (string.IsNullOrEmpty(incoming.Guid) && string.IsNullOrEmpty(incoming.Thunderstore)) { return; } string text = ((!string.IsNullOrEmpty(incoming.Guid)) ? incoming.Guid : incoming.Thunderstore); ModEntry value = null; if (ByGuid.TryGetValue(text, out value)) { if (!string.IsNullOrEmpty(incoming.Name)) { value.Name = incoming.Name; } if (!string.IsNullOrEmpty(incoming.Version)) { value.Version = incoming.Version; } if (!string.IsNullOrEmpty(incoming.Thunderstore)) { value.Thunderstore = incoming.Thunderstore; } if (incoming.Installed) { value.Installed = true; } if (incoming.Baked) { value.Baked = true; } foreach (string item in incoming.Policy) { value.Policy.Add(item); } using (List.Enumerator enumerator2 = incoming.Markers.GetEnumerator()) { while (enumerator2.MoveNext()) { __c__DisplayClass12_0 CS__8__locals6 = new __c__DisplayClass12_0(); CS__8__locals6.m = enumerator2.Current; if (!value.Markers.Exists((string x) => string.Equals(x, CS__8__locals6.m, StringComparison.OrdinalIgnoreCase))) { value.Markers.Add(CS__8__locals6.m); } } } using (List.Enumerator enumerator2 = incoming.Patches.GetEnumerator()) { while (enumerator2.MoveNext()) { __c__DisplayClass12_1 CS__8__locals7 = new __c__DisplayClass12_1(); CS__8__locals7.p = enumerator2.Current; if (!value.Patches.Exists((string x) => string.Equals(x, CS__8__locals7.p, StringComparison.OrdinalIgnoreCase))) { value.Patches.Add(CS__8__locals7.p); } } } if (!string.IsNullOrEmpty(incoming.Guid) && !ByGuid.ContainsKey(incoming.Guid)) { ByGuid[incoming.Guid] = value; } } else { ByGuid[text] = incoming; if (!string.IsNullOrEmpty(incoming.Guid) && incoming.Guid != text) { ByGuid[incoming.Guid] = incoming; } All.Add(incoming); } } private static void LoadBaked() { lock (Gate) { string[][] baked = Baked; for (int i = 0; i < baked.Length; i++) { ModEntry modEntry = ParseRow(baked[i]); modEntry.Baked = true; Upsert(modEntry); } } } private static void LoadJsonBesideDll() { try { string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); if (!string.IsNullOrEmpty(directoryName)) { string text = Path.Combine(directoryName, "library.json"); if (File.Exists(text)) { MergeJson(File.ReadAllText(text), baked: true); Plugin.Log.LogInfo((object)("Library loaded " + text)); } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Library json skipped: " + ex.Message)); } } private static void ScanInstalledManifests() { try { string pluginPath = Paths.PluginPath; if (string.IsNullOrEmpty(pluginPath) || !Directory.Exists(pluginPath)) { return; } string[] files = Directory.GetFiles(pluginPath, "manifest.json", SearchOption.AllDirectories); foreach (string path in files) { try { string json = File.ReadAllText(path); string text = JsonStr(json, "name"); string version = JsonStr(json, "version_number"); string fileName = Path.GetFileName(Path.GetDirectoryName(path) ?? ""); string text2 = fileName; int num = fileName.IndexOf('-'); if (num > 0) { string text3 = fileName.Substring(num + 1); int num2 = text3.LastIndexOf('-'); if (num2 > 0 && char.IsDigit(text3[num2 + 1])) { text2 = fileName.Substring(0, num + 1 + num2); } } Upsert(new ModEntry { Name = (string.IsNullOrEmpty(text) ? fileName : text), Version = version, Thunderstore = text2, Guid = text2, Installed = true, Kind = "content" }); } catch (Exception) { } } } catch (Exception ex2) { Plugin.Log.LogWarning((object)("Library scan skipped: " + ex2.Message)); } } private static void BindChainloader() { try { foreach (PluginInfo value in Chainloader.PluginInfos.Values) { BepInPlugin metadata = value.Metadata; if (metadata != null) { ModEntry obj = new ModEntry { Guid = metadata.GUID, Name = (metadata.Name ?? metadata.GUID) }; Version version = metadata.Version; obj.Version = ((version != null) ? version.ToString() : null) ?? ""; obj.Installed = true; ModEntry modEntry = obj; lock (Gate) { Upsert(modEntry); } DiscrepancyGuard.Declare(new CompatManifest { Guid = metadata.GUID, Version = modEntry.Version, DisplayName = modEntry.Name, HostRequired = true }); } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Library chainloader bind skipped: " + ex.Message)); } } private static void ApplyHarmonyClaims() { lock (Gate) { foreach (ModEntry item in All) { if (!item.Installed) { continue; } foreach (string patch in item.Patches) { HarmonyArbiter.Claim(patch, HarmonyLane.Content, item.Guid); } } } } private static void RefreshThunderstore() { try { ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12; using WebClient webClient = new WebClient { Encoding = Encoding.UTF8 }; webClient.Headers[HttpRequestHeader.UserAgent] = "REPOForge/1.5.4"; string[] obj = new string[2] { "https://thunderstore.io/c/repo/api/v1/package/", "https://thunderstore.io/api/experimental/community/repo/packages/" }; string text = null; string[] array = obj; foreach (string address in array) { try { text = webClient.DownloadString(address); if (!string.IsNullOrEmpty(text)) { break; } } catch (Exception) { } } if (string.IsNullOrEmpty(text)) { Plugin.Log.LogWarning((object)"Library Thunderstore refresh skipped: no catalog endpoint answered."); return; } int num = MergeThunderstorePage(text); if (num > 500) { Plugin.Log.LogWarning((object)$"Library Thunderstore refresh aborted: +{num} packages would bloat cache (co-op hang). Keep RefreshLibrary=false."); return; } Plugin.Log.LogInfo((object)$"Library Thunderstore refresh: +{num} packages."); try { File.WriteAllText(Path.Combine(Paths.ConfigPath, "com.repoforge.library.cache.json"), text); } catch (Exception) { } } catch (Exception ex3) { Plugin.Log.LogWarning((object)("Library Thunderstore refresh skipped: " + ex3.Message)); } } private static int MergeThunderstorePage(string json) { int num = 0; int i = 0; while (true) { string text = ExtractAfter(json, "\"full_name\":", ref i); if (text == null) { break; } string name = ExtractAfter(json, "\"name\":", ref i) ?? text; ModEntry incoming = new ModEntry { Guid = text, Name = name, Thunderstore = text, Kind = "content" }; lock (Gate) { int count = All.Count; Upsert(incoming); if (All.Count > count) { num++; } } } return num; } private static void MergeJson(string json, bool baked) { int num = json.IndexOf("\"mods\"", StringComparison.Ordinal); if (num < 0) { return; } int i = json.IndexOf('[', num); if (i < 0) { return; } int num2 = 0; int num3 = -1; for (; i < json.Length; i++) { switch (json[i]) { case '{': if (num2 == 0) { num3 = i; } num2++; break; case '}': num2--; if (num2 == 0 && num3 >= 0) { ModEntry modEntry = FromJsonObject(json.Substring(num3, i - num3 + 1)); modEntry.Baked = baked; lock (Gate) { Upsert(modEntry); } num3 = -1; } break; } } } private static ModEntry FromJsonObject(string obj) { ModEntry modEntry = new ModEntry { Guid = JsonStr(obj, "guid"), Name = JsonStr(obj, "name"), Thunderstore = JsonStr(obj, "thunderstore"), Kind = JsonStr(obj, "kind") }; if (string.IsNullOrEmpty(modEntry.Kind)) { modEntry.Kind = "content"; } string text = JsonStr(obj, "hostRequired"); modEntry.HostRequired = text != "false"; foreach (string item in JsonArr(obj, "policy")) { modEntry.Policy.Add(item); } modEntry.Markers.AddRange(JsonArr(obj, "markers")); modEntry.Patches.AddRange(JsonArr(obj, "patches")); return modEntry; } private static string JsonStr(string json, string key) { string value = "\"" + key + "\""; int num = json.IndexOf(value, StringComparison.OrdinalIgnoreCase); if (num < 0) { return ""; } num = json.IndexOf(':', num); if (num < 0) { return ""; } for (num++; num < json.Length && char.IsWhiteSpace(json[num]); num++) { } if (num >= json.Length) { return ""; } if (json[num] == '"') { num++; int num2 = json.IndexOf('"', num); if (num2 >= 0) { return json.Substring(num, num2 - num); } return ""; } int i; for (i = num; i < json.Length && json[i] != ',' && json[i] != '}' && json[i] != ']'; i++) { } return json.Substring(num, i - num).Trim(); } private static List JsonArr(string json, string key) { List list = new List(); string value = "\"" + key + "\""; int num = json.IndexOf(value, StringComparison.OrdinalIgnoreCase); if (num < 0) { return list; } num = json.IndexOf('[', num); if (num < 0) { return list; } int num2 = json.IndexOf(']', num); if (num2 < 0) { return list; } string text = json.Substring(num, num2 - num); int startIndex = 0; while (true) { int num3 = text.IndexOf('"', startIndex); if (num3 < 0) { break; } int num4 = text.IndexOf('"', num3 + 1); if (num4 < 0) { break; } list.Add(text.Substring(num3 + 1, num4 - num3 - 1)); startIndex = num4 + 1; } return list; } private static string? ExtractAfter(string json, string key, ref int i) { int num = json.IndexOf(key, i, StringComparison.Ordinal); if (num < 0) { return null; } int num2 = json.IndexOf('"', num + key.Length); if (num2 < 0) { return null; } int num3 = json.IndexOf('"', num2 + 1); if (num3 < 0) { return null; } i = num3 + 1; return json.Substring(num2 + 1, num3 - num2 - 1); } private static ModEntry ParseRow(string[] r) { ModEntry modEntry = new ModEntry { Guid = r[0], Name = r[1], Thunderstore = r[2], Kind = r[3] }; string[] array = r[4].Split(new char[1] { '|' }, StringSplitOptions.RemoveEmptyEntries); foreach (string item in array) { modEntry.Policy.Add(item); } array = r[5].Split(new char[1] { '|' }, StringSplitOptions.RemoveEmptyEntries); foreach (string item2 in array) { modEntry.Markers.Add(item2); } array = r[6].Split(new char[1] { '|' }, StringSplitOptions.RemoveEmptyEntries); foreach (string item3 in array) { modEntry.Patches.Add(item3); } return modEntry; } } } namespace REPOForge.Network { internal static class PrefabRegistry { private static readonly Dictionary _local = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary _owners = new Dictionary(StringComparer.OrdinalIgnoreCase); internal static void Install(Harmony harmony) { //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Expected O, but got Unknown Type type = GameAccess.Type("Photon.Pun.PhotonNetwork") ?? GameAccess.Type("PhotonNetwork"); if (!(type == null)) { MethodInfo methodInfo = AccessTools.Method(type, "InstantiateRoomObject", new Type[5] { typeof(string), typeof(Vector3), typeof(Quaternion), typeof(byte), typeof(object[]) }, (Type[])null) ?? AccessTools.Method(type, "InstantiateRoomObject", (Type[])null, (Type[])null); if (!(methodInfo == null)) { harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(PrefabRegistry), "InstantiateFinalizer", (Type[])null), (HarmonyMethod)null); } } } internal static bool Register(string path, GameObject prefab) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected O, but got Unknown if (string.IsNullOrEmpty(path) || (Object)prefab == (Object)null) { return false; } if (_local.ContainsKey(path)) { Plugin.Log.LogWarning((object)("Prefab '" + path + "' already registered. First registrant (" + _owners[path] + ") wins.")); return false; } _local[path] = prefab; _owners[path] = "com.repoforge.core"; if (Plugin.VerboseLogging.Value) { Plugin.Log.LogInfo((object)("Prefab registered " + path)); } return true; } internal static GameObject? SpawnItem(object item, Vector3 position, Quaternion rotation) { //IL_03c3: Unknown result type (might be due to invalid IL or missing references) //IL_03c4: Unknown result type (might be due to invalid IL or missing references) //IL_03a6: Unknown result type (might be due to invalid IL or missing references) //IL_03b1: Expected O, but got Unknown //IL_03b4: Unknown result type (might be due to invalid IL or missing references) //IL_03b5: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Expected O, but got Unknown //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected O, but got Unknown //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Expected O, but got Unknown //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Expected O, but got Unknown //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Expected O, but got Unknown //IL_02ab: Unknown result type (might be due to invalid IL or missing references) //IL_02b6: Expected O, but got Unknown //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0303: Unknown result type (might be due to invalid IL or missing references) //IL_0304: Unknown result type (might be due to invalid IL or missing references) //IL_02b9: Unknown result type (might be due to invalid IL or missing references) //IL_02ba: Unknown result type (might be due to invalid IL or missing references) //IL_02d4: Unknown result type (might be due to invalid IL or missing references) //IL_02df: Expected O, but got Unknown //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Expected O, but got Unknown //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_031b: Unknown result type (might be due to invalid IL or missing references) //IL_0326: Expected O, but got Unknown //IL_0310: Unknown result type (might be due to invalid IL or missing references) //IL_0311: Unknown result type (might be due to invalid IL or missing references) //IL_0251: Unknown result type (might be due to invalid IL or missing references) //IL_025c: Expected O, but got Unknown //IL_036b: Unknown result type (might be due to invalid IL or missing references) //IL_036c: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: 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_01eb: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Expected O, but got Unknown if (!GameAccess.IsMaster()) { Plugin.Log.LogError((object)"SpawnItem is host-only."); return null; } string text = GameAccess.PrefabPath(item); string text2 = GameAccess.ItemName(item); object member = GameAccess.GetMember(item, "prefab"); GameObject val = (GameObject)(((member is GameObject) ? member : null) ?? null); if ((Object)val == (Object)null && member != null) { object obj = GameAccess.GetMember(member, "Prefab") ?? GameAccess.GetMember(member, "prefab"); val = (GameObject)(((obj is GameObject) ? obj : null) ?? null); } if ((Object)val == (Object)null && !string.IsNullOrEmpty(text) && _local.TryGetValue(text, out var value)) { val = value; } object obj2 = GameAccess.GetMember(member, "PrefabName") ?? GameAccess.GetMember(member, "prefabName"); string text3 = (string)(((obj2 is string) ? obj2 : null) ?? null); Plugin.Log.LogInfo((object)string.Format("[spawn] PrefabRegistry {0} path={1} prefabName={2} prefab={3} mp={4} pos={5}", text2, text, text3, ((Object)val != (Object)null) ? ((Object)val).name : "NULL", GameAccess.IsMultiplayer(), ((Vector3)(ref position)).ToString("F2"))); try { if (GameAccess.IsMultiplayer()) { EnsurePhotonCache(text, val); EnsurePhotonCache(text3, val); GameObject val2 = PhotonInstantiate(text, position, rotation) ?? PhotonInstantiate(text3, position, rotation); if ((Object)val2 == (Object)null) { string[] array = new string[6] { "Items/Item Drone Heal", "Items/Item Drone Feather", "Items/Item Drone Battery", "Items/Item Drone Indestructible", "Item Drone Heal", "Item Drone Feather" }; foreach (string text4 in array) { val2 = PhotonInstantiate(text4, position, rotation); if ((Object)val2 != (Object)null) { Plugin.Log.LogInfo((object)("[spawn] PrefabRegistry photon-fallback " + text2 + " via " + text4 + " → " + ((Object)val2).name)); return val2; } } } if ((Object)val2 != (Object)null) { Plugin.Log.LogInfo((object)("[spawn] PrefabRegistry photon " + text2 + " → " + ((Object)val2).name)); return val2; } Plugin.Log.LogWarning((object)("[spawn] Co-op: '" + text2 + "' did not replicate. Not spawning it only on the host — guests would not see it.")); return null; } if ((Object)val != (Object)null) { GameObject val3 = Object.Instantiate(val, position, rotation); Plugin.Log.LogInfo((object)("[spawn] PrefabRegistry Instantiate " + text2 + " → " + (((Object)val3 != (Object)null) ? ((Object)val3).name : "NULL"))); return val3; } GameObject val4 = PhotonInstantiate(text, position, rotation) ?? PhotonInstantiate(text3, position, rotation); if ((Object)val4 != (Object)null) { Plugin.Log.LogInfo((object)("[spawn] PrefabRegistry photon-sp " + text2 + " → " + ((Object)val4).name)); return val4; } Plugin.Log.LogWarning((object)("[spawn] PrefabRegistry FAIL " + text2 + " no Prefab GameObject, photon null. path=" + text)); return Placeholder(text, position, rotation); } catch (Exception ex) { Plugin.Log.LogError((object)("SpawnItem(" + text + ") failed: " + ex.Message)); if (GameAccess.IsMultiplayer()) { return null; } if ((Object)val != (Object)null) { try { return Object.Instantiate(val, position, rotation); } catch (Exception) { } } return Placeholder(text, position, rotation); } } private static void EnsurePhotonCache(string path, GameObject prefab) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected O, but got Unknown if (string.IsNullOrEmpty(path) || (Object)prefab == (Object)null) { return; } try { if (!_local.ContainsKey(path)) { _local[path] = prefab; } Type type = GameAccess.Type("Photon.Pun.PhotonNetwork") ?? GameAccess.Type("PhotonNetwork"); if (type == null) { return; } PropertyInfo propertyInfo = AccessTools.Property(type, "PrefabPool"); object obj = ((propertyInfo != null) ? propertyInfo.GetValue(null, null) : null); if (obj != null) { FieldInfo fieldInfo = AccessTools.Field(obj.GetType(), "ResourceCache") ?? AccessTools.Field(obj.GetType(), "resourceCache"); if (!(fieldInfo == null) && fieldInfo.GetValue(obj) is IDictionary dictionary) { dictionary[path] = prefab; Plugin.Log.LogInfo((object)("[spawn] seeded Photon ResourceCache " + path)); } } } catch (Exception ex) { Plugin.Log.LogDebug((object)("EnsurePhotonCache: " + ex.Message)); } } private static GameObject? PhotonInstantiate(string? path, Vector3 position, Quaternion rotation) { //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00af: 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_00dc: Expected O, but got Unknown if (string.IsNullOrEmpty(path)) { return null; } Type type = GameAccess.Type("Photon.Pun.PhotonNetwork") ?? GameAccess.Type("PhotonNetwork"); MethodInfo methodInfo = AccessTools.Method(type, "InstantiateRoomObject", new Type[5] { typeof(string), typeof(Vector3), typeof(Quaternion), typeof(byte), typeof(object[]) }, (Type[])null) ?? AccessTools.Method(type, "InstantiateRoomObject", (Type[])null, (Type[])null); if (methodInfo == null) { return null; } object obj = methodInfo.Invoke(null, new object[5] { path, position, rotation, (byte)0, null }); return (GameObject)((obj is GameObject) ? obj : null); } private static Exception? InstantiateFinalizer(Exception? __exception, string __0, Vector3 __1, Quaternion __2, ref GameObject? __result) { //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) if (__exception == null) { return null; } if (!Plugin.Enabled.Value || !Plugin.PlaceholderMissingPrefabs.Value) { return __exception; } string text = __0 ?? ""; if (text.IndexOf("Item", StringComparison.OrdinalIgnoreCase) < 0 && !text.StartsWith("Items/", StringComparison.OrdinalIgnoreCase)) { return __exception; } Plugin.Log.LogWarning((object)("Photon instantiate failed for '" + text + "': " + __exception.Message + ". Spawning placeholder.")); __result = Placeholder(text, __1, __2); return null; } private static GameObject? Placeholder(string path, Vector3 position, Quaternion rotation) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) try { GameObject obj = GameObject.CreatePrimitive((PrimitiveType)3); ((Object)obj).name = "REPOForge_Placeholder_" + (path ?? "unknown"); obj.transform.position = position; obj.transform.rotation = rotation; obj.transform.localScale = Vector3.one * 0.35f; return obj; } catch (Exception ex) { Plugin.Log.LogWarning((object)("Placeholder spawn failed: " + ex.Message)); return null; } } } } namespace REPOForge.Shop { internal static class ShopCoordinator { private sealed class Group { internal string Path = ""; internal string Name = ""; internal object Item; internal int Count; internal bool Mod; } [CompilerGenerated] private sealed class __c__DisplayClass14_0 { public ShopIntent intent; internal bool Register_b__0(ShopIntent i) { return i.ItemName == intent.ItemName; } } [CompilerGenerated] private sealed class __c__DisplayClass15_0 { public string itemName; internal bool Unregister_b__0(ShopIntent i) { return i.ItemName == itemName; } } private static readonly List _intents = new List(); private static readonly List _reservedMods = new List(); private static readonly HashSet _placedThisVisit = new HashSet(StringComparer.OrdinalIgnoreCase); private static readonly object _gate = new object(); private static readonly Dictionary _spawned = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary _homeVol = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly HashSet _usedSlots = new HashSet(StringComparer.OrdinalIgnoreCase); private static string? _lastAttempt; internal static string? LastHash { get; private set; } private static void SpawnLog(string msg) { Plugin.Log.LogInfo((object)("[spawn] " + msg)); } internal static void Install(Harmony harmony) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected O, but got Unknown //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Expected O, but got Unknown //IL_0124: Expected O, but got Unknown //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Expected O, but got Unknown //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Expected O, but got Unknown //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_01ec: Expected O, but got Unknown Type? type = GameAccess.Type("ShopManager"); MethodInfo methodInfo = AccessTools.Method(type, "GetAllItemsFromStatsManager", (Type[])null, (Type[])null); if (methodInfo != null) { harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(ShopCoordinator), "GetAllPostfix", (Type[])null) { priority = 0 }, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)"Patched ShopManager.GetAllItemsFromStatsManager (postfix last)."); } else { Plugin.Log.LogWarning((object)"ShopManager.GetAllItemsFromStatsManager not found."); } Type? type2 = GameAccess.Type("PunManager"); MethodInfo methodInfo2 = AccessTools.Method(type2, "SpawnShopItem", (Type[])null, (Type[])null); if (methodInfo2 != null) { ParameterInfo[] parameters = methodInfo2.GetParameters(); Plugin.Log.LogInfo((object)("Patched PunManager.SpawnShopItem (" + string.Join(", ", Array.ConvertAll(parameters, (ParameterInfo p) => p.ParameterType.Name + " " + p.Name)) + ").")); harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(ShopCoordinator), "SpawnPrefix", (Type[])null) { priority = 100 }, new HarmonyMethod(typeof(ShopCoordinator), "SpawnPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } else { Plugin.Log.LogWarning((object)"PunManager.SpawnShopItem not found — volume matching still runs via GetAll postfix."); } MethodInfo methodInfo3 = AccessTools.Method(type2, "ShopPopulateItemVolumes", (Type[])null, (Type[])null); if (methodInfo3 != null) { harmony.Patch((MethodBase)methodInfo3, new HarmonyMethod(typeof(ShopCoordinator), "PopulatePrefix", (Type[])null) { priority = 800 }, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)methodInfo3, (HarmonyMethod)null, new HarmonyMethod(typeof(ShopCoordinator), "PopulatePostfix", (Type[])null) { priority = 0 }, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } MethodInfo methodInfo4 = AccessTools.Method(type, "GetAllItemVolumesInScene", (Type[])null, (Type[])null); if (methodInfo4 != null) { harmony.Patch((MethodBase)methodInfo4, (HarmonyMethod)null, new HarmonyMethod(typeof(ShopCoordinator), "VolumesPostfix", (Type[])null) { priority = 0 }, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } internal static void Register(ShopIntent intent) { __c__DisplayClass14_0 CS__8__locals10 = new __c__DisplayClass14_0(); CS__8__locals10.intent = intent; if (CS__8__locals10.intent == null || string.IsNullOrEmpty(CS__8__locals10.intent.ItemName)) { return; } lock (_gate) { _intents.RemoveAll((ShopIntent i) => i.ItemName == CS__8__locals10.intent.ItemName); _intents.Add(CS__8__locals10.intent); if (CS__8__locals10.intent.Item != null) { GameAccess.SetEnum(CS__8__locals10.intent.Item, "itemVolume", CS__8__locals10.intent.Volume.ToString()); } } if (Plugin.VerboseLogging.Value) { Plugin.Log.LogInfo((object)("Shop intent registered: " + CS__8__locals10.intent.ItemName + " (" + CS__8__locals10.intent.PrefabPath + ")")); } } internal static void Unregister(string itemName) { __c__DisplayClass15_0 CS__8__locals2 = new __c__DisplayClass15_0(); CS__8__locals2.itemName = itemName; lock (_gate) { _intents.RemoveAll((ShopIntent i) => i.ItemName == CS__8__locals2.itemName); } } private static void GetAllPostfix(object __instance) { if (!Plugin.Enabled.Value || !Plugin.ShopPipeline.Value) { return; } if (GameAccess.IsNotMaster()) { Plugin.Log.LogInfo((object)"Shop mix skipped — client. Host fills the shelves; you receive them."); return; } try { _spawned.Clear(); _reservedMods.Clear(); _placedThisVisit.Clear(); _homeVol.Clear(); _usedSlots.Clear(); ReconcileLists(__instance); string text = (LastHash = ComputeHash(__instance)); int count = CountItems(__instance); PluginBus.Publish(new ShopReadyEvent { ManifestHash = text, Count = count, Seed = Environment.TickCount }); int num = GameAccess.ListField(__instance, "potentialItems")?.Count ?? 0; int num2 = GameAccess.ListField(__instance, "potentialItemConsumables")?.Count ?? 0; int num3 = GameAccess.ListField(__instance, "potentialItemUpgrades")?.Count ?? 0; int num4 = GameAccess.ListField(__instance, "potentialItemHealthPacks")?.Count ?? 0; object member = GameAccess.GetMember(__instance, "itemSpawnTargetAmount"); Plugin.Log.LogInfo((object)$"Shop pool items={num} consumables={num2} upgrades={num3} health={num4} target={member} unique={text}"); } catch (Exception arg) { Plugin.Log.LogError((object)$"Shop reconcile failed: {arg}"); } } private static void PopulatePrefix() { if (Plugin.Enabled.Value && Plugin.ShopPipeline.Value && !GameAccess.IsNotMaster()) { _spawned.Clear(); _placedThisVisit.Clear(); _homeVol.Clear(); _usedSlots.Clear(); object obj = GameAccess.Instance("ShopManager"); if (obj != null) { ApplyBudgets(obj); } } } private static void VolumesPostfix(object __instance) { if (Plugin.Enabled.Value && Plugin.ShopPipeline.Value && !GameAccess.IsNotMaster()) { ApplyBudgets(__instance); } } private static void PopulatePostfix() { if (!Plugin.Enabled.Value || !Plugin.ShopPipeline.Value || GameAccess.IsNotMaster()) { return; } try { ForcePlaceMods(); } catch (Exception arg) { Plugin.Log.LogError((object)$"Force-place mods failed: {arg}"); } } private static bool SpawnPrefix(object itemVolume, IList itemList, ref int spawnCount, bool isSecret, ref bool __result) { _lastAttempt = null; if (!Plugin.Enabled.Value || !Plugin.ShopPipeline.Value) { return true; } if (GameAccess.IsNotMaster()) { return true; } if (itemList == null || itemList.Count == 0) { SpawnLog($"skip empty-list vol={VolKind(itemVolume)} secret={isSecret} count={spawnCount}"); return true; } try { string text = DescribeTail(itemList); if (itemVolume != null) { PreferMatchingVolume(itemVolume, itemList); } int num = 0; while (itemList.Count > 0) { object obj = itemList[itemList.Count - 1]; if (obj == null) { SpawnLog($"skip null-tail vol={VolKind(itemVolume)} secret={isSecret}"); return true; } string text2 = GameAccess.ItemName(obj); string text3 = GameAccess.ItemVolume(obj); if (!IsModItem(obj)) { _lastAttempt = text2; SpawnLog($"vanilla-path {text2}[{text3}] onto {VolName(itemVolume)} secret={isSecret} count={spawnCount} list={itemList.Count} (was {text})"); return true; } if (IsMedvac(text2) || IsShopExcluded(text2) || AlreadyPlaced(text2)) { SpawnLog($"strip {text2} medvac={IsMedvac(text2)} excluded={IsShopExcluded(text2)} already={AlreadyPlaced(text2)} vol={VolName(itemVolume)}"); itemList.RemoveAt(itemList.Count - 1); if (itemVolume != null && itemList.Count > 0) { PreferMatchingVolume(itemVolume, itemList); } continue; } if (!VolCompatible(itemVolume, obj)) { SpawnLog("defer " + text2 + "[" + text3 + "] off " + VolName(itemVolume)); itemList.RemoveAt(itemList.Count - 1); itemList.Insert(0, obj); if (itemVolume != null && itemList.Count > 0) { PreferMatchingVolume(itemVolume, itemList); } if (++num <= itemList.Count) { continue; } return true; } _lastAttempt = text2; SpawnLog($"mod-path {text2}[{text3}] onto {VolName(itemVolume)} secret={isSecret} count={spawnCount} list={itemList.Count} — leaving for vanilla SpawnShopItem"); return true; } SpawnLog($"list exhausted after strip vol={VolName(itemVolume)} secret={isSecret} count={spawnCount}"); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Spawn remap skipped: " + ex.Message)); } return true; } private static void SpawnPostfix(object itemVolume, IList itemList, ref int spawnCount, bool isSecret, bool __result) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Expected O, but got Unknown if (Plugin.Enabled.Value && Plugin.ShopPipeline.Value) { string lastAttempt = _lastAttempt; GameObject val = ((!string.IsNullOrEmpty(lastAttempt)) ? FindClone(lastAttempt) : null); if (__result && (Object)val != (Object)null && itemVolume != null && !string.IsNullOrEmpty(lastAttempt)) { _homeVol[lastAttempt] = itemVolume; RememberSlot(itemVolume); } object obj; if (!((Object)val != (Object)null)) { obj = "-"; } else { Vector3 position = val.transform.position; obj = ((Vector3)(ref position)).ToString("F2"); } string text = (string)obj; SpawnLog(string.Format("result ok={0} item={1} secret={2} count={3} remaining={4} clone={5} pos={6} vol={7}", __result, lastAttempt ?? "(none)", isSecret, spawnCount, itemList?.Count ?? 0, ((Object)val != (Object)null) ? ((Object)val).name : "MISSING", text, VolName(itemVolume))); } } private static bool VolCompatible(object? volume, object item) { string text = EffectiveVolume(item); string text2 = VolKind(volume); if (string.IsNullOrEmpty(text) || string.IsNullOrEmpty(text2)) { return true; } if (string.Equals(text, text2, StringComparison.OrdinalIgnoreCase)) { return true; } if (Plugin.RemapSmallToMedium.Value && Contains(text, "small") && Contains(text2, "medium")) { return true; } if (Contains(text, "large") && (Contains(text2, "large") || Contains(text2, "wide") || Contains(text2, "plus"))) { return true; } return false; } private static string EffectiveVolume(object item) { return GameAccess.ItemVolume(item); } private static bool IsSecretItem(object item) { string text = GameAccess.GetMember(item, "itemSecretShopType")?.ToString() ?? ""; if (string.IsNullOrEmpty(text)) { return false; } if (!Contains(text, "none")) { return text != "0"; } return false; } private static bool IsValuable(object item) { string text = GameAccess.ItemName(item); string text2 = GameAccess.PrefabPath(item); string a = GameAccess.GetMember(item, "itemType")?.ToString() ?? ""; if (!Contains(text, "valuable") && !Contains(text2, "valuable") && !Contains(a, "valuable") && !ModLibrary.LooksLikeValuable(text)) { return ModLibrary.LooksLikeValuable(text2); } return true; } private static bool IsShopEligible(object item) { if (item == null) { return false; } string text = GameAccess.ItemName(item); if (IsMedvac(text) || IsShopExcluded(text)) { return false; } if (!IsModItem(item)) { return false; } if (IsValuable(item) || IsSecretItem(item)) { return false; } if (GameAccess.ReadInt(item, "maxAmountInShop") <= 0) { return false; } if (Contains(GameAccess.PrefabPath(item), "MC Item") && !Contains(text, "Firework")) { return false; } return true; } private static void AdoptMods(object shop) { if (!Plugin.AdoptUnlistedMods.Value) { return; } HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); Collect(GameAccess.ListField(shop, "potentialItems"), hashSet); Collect(GameAccess.ListField(shop, "potentialItemConsumables"), hashSet); Collect(GameAccess.ListField(shop, "potentialItemUpgrades"), hashSet); Collect(GameAccess.ListField(shop, "potentialItemHealthPacks"), hashSet); int num = 0; foreach (object item in GameAccess.AllStatItems()) { if (!IsShopEligible(item)) { continue; } string text = GameAccess.PrefabPath(item); if (!string.IsNullOrEmpty(text) && hashSet.Add(text)) { IList list = ListFor(shop, ShopCategoryKind.Items); string text2 = GameAccess.ItemVolume(item); if (Contains(text2, "health")) { list = GameAccess.ListField(shop, "potentialItemHealthPacks"); } else if (Contains(text2, "upgrade")) { list = GameAccess.ListField(shop, "potentialItemUpgrades"); } list?.Add(item); RememberMod(item); num++; Plugin.Log.LogInfo((object)("Shop adopt " + GameAccess.ItemName(item) + " [" + text2 + "] " + text)); } } if (num > 0) { Plugin.Log.LogInfo((object)$"Shop adopted {num} unlisted shop item(s)."); } } private static string DescribeTail(IList itemList) { if (itemList == null || itemList.Count == 0) { return "(empty)"; } object obj = itemList[itemList.Count - 1]; if (obj != null) { return GameAccess.ItemName(obj) + "[" + GameAccess.ItemVolume(obj) + "]"; } return "(null)"; } private static void PreferMatchingVolume(object itemVolume, IList itemList) { if (itemList.Count < 2) { return; } string text = GameAccess.GetMember(itemVolume, "itemVolume")?.ToString() ?? ""; int num = Math.Max(1, Plugin.MaxCopiesPerItem.Value); int num2 = -1; int num3 = -1; int value = 0; for (int num4 = itemList.Count - 1; num4 >= 0; num4--) { object obj = itemList[num4]; if (obj != null && (string.Equals(EffectiveVolume(obj), text, StringComparison.OrdinalIgnoreCase) || (Plugin.RemapSmallToMedium.Value && Contains(EffectiveVolume(obj), "small") && Contains(text, "medium")))) { if (num3 < 0) { num3 = num4; } string key = GameAccess.PrefabPath(obj); if ((_spawned.TryGetValue(key, out value) ? value : 0) < num) { num2 = num4; break; } } } int num5 = ((num2 >= 0) ? num2 : num3); if (num5 >= 0) { object obj2 = itemList[num5]; if (num5 != itemList.Count - 1) { itemList.RemoveAt(num5); itemList.Add(obj2); } string key2 = GameAccess.PrefabPath(obj2); int value2 = 0; _spawned[key2] = (_spawned.TryGetValue(key2, out value2) ? value2 : 0) + 1; } } private static void ReconcileLists(object shop) { HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); Collect(GameAccess.ListField(shop, "potentialItems"), hashSet); Collect(GameAccess.ListField(shop, "potentialItemConsumables"), hashSet); Collect(GameAccess.ListField(shop, "potentialItemUpgrades"), hashSet); Collect(GameAccess.ListField(shop, "potentialItemHealthPacks"), hashSet); lock (_gate) { foreach (ShopIntent intent in _intents) { if (intent.Item == null) { continue; } string text = (string.IsNullOrEmpty(intent.PrefabPath) ? GameAccess.PrefabPath(intent.Item) : intent.PrefabPath); if (!hashSet.Add(text)) { if (Plugin.VerboseLogging.Value) { Plugin.Log.LogInfo((object)("Shop intent '" + intent.ItemName + "' already in pool (" + text + ").")); } } else { ListFor(shop, intent.Category)?.Add(intent.Item); } } } EnsureCoverage(shop); if (Plugin.AdoptUnlistedMods.Value) { AdoptMods(shop); } if (Plugin.DiversifyPool.Value) { Mix(GameAccess.ListField(shop, "potentialItems"), "items", reserve: true); Mix(GameAccess.ListField(shop, "potentialItemConsumables"), "consumables", reserve: true); Mix(GameAccess.ListField(shop, "potentialItemUpgrades"), "upgrades", reserve: true); Mix(GameAccess.ListField(shop, "potentialItemHealthPacks"), "health", reserve: true); MixSecrets(shop); } else if (Plugin.PriceWeight.Value) { WeightByPrice(GameAccess.ListField(shop, "potentialItems")); WeightByPrice(GameAccess.ListField(shop, "potentialItemConsumables")); WeightByPrice(GameAccess.ListField(shop, "potentialItemUpgrades")); WeightByPrice(GameAccess.ListField(shop, "potentialItemHealthPacks")); } ApplyBudgets(shop); } private static void Mix(IList? list, string label, bool reserve) { if (list == null || list.Count == 0) { return; } List list2 = new List(); Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); IEnumerator enumerator = list.GetEnumerator(); try { int value = 0; while (enumerator.MoveNext()) { object current = enumerator.Current; if (current != null && !IsShopExcluded(GameAccess.ItemName(current))) { string text = GameAccess.PrefabPath(current); if (dictionary.TryGetValue(text, out value)) { list2[value].Count++; continue; } dictionary[text] = list2.Count; list2.Add(new Group { Path = text, Item = current, Count = 1, Name = GameAccess.ItemName(current), Mod = (Plugin.PreferModItems.Value && !VanillaCatalog.IsVanilla(text, GameAccess.ItemName(current))) }); } } } finally { ((IDisposable)((enumerator is IDisposable) ? enumerator : null))?.Dispose(); } int num = 1; foreach (Group item in list2) { if (item.Count > num) { num = item.Count; } } List list3 = new List(); List list4 = new List(); foreach (Group item2 in list2) { (item2.Mod ? list4 : list3).Add(item2); } foreach (Group item3 in list4) { item3.Count = 1; } list.Clear(); for (int num2 = num - 1; num2 >= 0; num2--) { AppendRoundRobin(list, list3, num2); } AppendRoundRobin(list, list4, 0); string text2 = ((list4.Count == 0) ? "(none)" : string.Join(", ", list4.ConvertAll((Group m) => m.Name + "[" + GameAccess.ItemVolume(m.Item) + "]"))); Plugin.Log.LogInfo((object)$"Shop mix {label}: unique={list2.Count} kept={list.Count} mods={list4.Count} pinned={text2}"); if (!reserve) { return; } foreach (Group item4 in list4) { RememberMod(item4.Item); } } private static void EnsureCoverage(object shop) { IList list = GameAccess.ListField(shop, "potentialItems"); if (list == null) { return; } IList dest = GameAccess.ListField(shop, "potentialItemConsumables"); IList dest2 = GameAccess.ListField(shop, "potentialItemUpgrades"); IList dest3 = GameAccess.ListField(shop, "potentialItemHealthPacks"); IEnumerator enumerator = list.GetEnumerator(); try { while (enumerator.MoveNext()) { object current = enumerator.Current; if (current != null) { string a = GameAccess.ItemVolume(current); if (Contains(a, "health")) { Offer(dest3, current); } else if (Contains(a, "upgrade") || Contains(a, "rubber") || Contains(a, "duck")) { Offer(dest2, current); } else if (Contains(a, "crystal") || Contains(a, "power")) { Offer(dest, current); } } } } finally { ((IDisposable)((enumerator is IDisposable) ? enumerator : null))?.Dispose(); } } private static void Offer(IList? dest, object item) { if (dest == null) { return; } string b = GameAccess.PrefabPath(item); IEnumerator enumerator = dest.GetEnumerator(); try { while (enumerator.MoveNext()) { object current = enumerator.Current; if (current != null && string.Equals(GameAccess.PrefabPath(current), b, StringComparison.OrdinalIgnoreCase)) { return; } } } finally { ((IDisposable)((enumerator is IDisposable) ? enumerator : null))?.Dispose(); } dest.Add(item); } private static bool Contains(string a, string b) { return a.IndexOf(b, StringComparison.OrdinalIgnoreCase) >= 0; } private static void MixSecrets(object shop) { object member = GameAccess.GetMember(shop, "potentialSecretItems"); IDictionary dictionary = (IDictionary)((member is IDictionary) ? member : null); if (dictionary == null) { return; } IDictionaryEnumerator enumerator = dictionary.GetEnumerator(); try { while (enumerator.MoveNext()) { DictionaryEntry dictionaryEntry = (DictionaryEntry)enumerator.Current; object value = dictionaryEntry.Value; IList list = (IList)((value is IList) ? value : null); if (list != null) { Mix(list, "secret:" + dictionaryEntry.Key, reserve: false); } } } finally { ((IDisposable)((enumerator is IDisposable) ? enumerator : null))?.Dispose(); } } private static bool IsModItem(object item) { if (item == null) { return false; } return !VanillaCatalog.IsVanilla(GameAccess.PrefabPath(item), GameAccess.ItemName(item)); } private static void RememberMod(object item) { if (item == null) { return; } string name = GameAccess.ItemName(item); if (IsMedvac(name) || IsShopExcluded(name) || IsSecretItem(item) || IsValuable(item)) { return; } string b = GameAccess.PrefabPath(item); foreach (object reservedMod in _reservedMods) { if (string.Equals(GameAccess.PrefabPath(reservedMod), b, StringComparison.OrdinalIgnoreCase)) { return; } } _reservedMods.Add(item); } private static bool IsMedvac(string name) { return ModLibrary.SelfSpawns(name); } private static bool IsShopExcluded(string name) { return ModLibrary.ShopSkip(name); } private static bool AlreadyPlaced(string name) { if (!string.IsNullOrEmpty(name)) { return _placedThisVisit.Contains(name); } return false; } private static void MarkPlaced(string name) { if (!string.IsNullOrEmpty(name)) { _placedThisVisit.Add(name); } } private static void ForcePlaceMods() { //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Expected O, but got Unknown //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_0214: Unknown result type (might be due to invalid IL or missing references) //IL_021f: Expected O, but got Unknown //IL_0242: Unknown result type (might be due to invalid IL or missing references) //IL_035e: Unknown result type (might be due to invalid IL or missing references) //IL_0369: Expected O, but got Unknown //IL_02a2: Unknown result type (might be due to invalid IL or missing references) //IL_02a7: Unknown result type (might be due to invalid IL or missing references) //IL_03fb: Unknown result type (might be due to invalid IL or missing references) //IL_0406: Expected O, but got Unknown //IL_0441: Unknown result type (might be due to invalid IL or missing references) //IL_044c: Expected O, but got Unknown //IL_04af: Unknown result type (might be due to invalid IL or missing references) //IL_04ba: Expected O, but got Unknown //IL_04e4: Unknown result type (might be due to invalid IL or missing references) //IL_04e9: Unknown result type (might be due to invalid IL or missing references) if (_reservedMods.Count == 0) { return; } List list = CollectShopVolumes(); List list2 = list.FindAll((object v) => VolumeEmpty(v) && !_usedSlots.Contains(SlotKey(v))); Plugin.Log.LogInfo((object)string.Format("Force-place: reserved={0} empty={1} volumes={2} already={3} placed=[{4}]", _reservedMods.Count, list2.Count, list.Count, _placedThisVisit.Count, string.Join(",", _placedThisVisit))); PurgeExcluded(); using List.Enumerator enumerator = _reservedMods.GetEnumerator(); object value = null; while (enumerator.MoveNext()) { object current = enumerator.Current; string text = GameAccess.ItemName(current); string text2 = GameAccess.PrefabPath(current); GameObject val = FindClone(text); object[] array = new object[6] { text, text2, IsMedvac(text), IsShopExcluded(text), AlreadyPlaced(text), null }; object obj; Vector3 position; if (!((Object)val != (Object)null)) { obj = "none"; } else { string[] obj2 = new string[5] { ((Object)val).name, " active=", val.activeInHierarchy.ToString(), " pos=", null }; position = val.transform.position; obj2[4] = ((Vector3)(ref position)).ToString("F2"); obj = string.Concat(obj2); } array[5] = obj; SpawnLog(string.Format("force consider {0} path={1} medvac={2} excluded={3} already={4} clone={5}", array)); if (IsMedvac(text) || IsShopExcluded(text) || AlreadyPlaced(text)) { Plugin.Log.LogInfo((object)("Force-place skip " + text + " — " + (IsShopExcluded(text) ? "excluded from shop" : "already handled"))); continue; } string text3 = EffectiveVolume(current); if ((Object)val != (Object)null) { _homeVol.TryGetValue(text, out value); SitOnVolume(val, value, current, value != null && val.transform.position.y < ShelfTop(value) - 0.05f); val.SetActive(true); Freeze(val); MarkPlaced(text); ManualLogSource log = Plugin.Log; object[] array2 = new object[5] { text, text3, ((Object)val).name, null, null }; position = val.transform.position; array2[3] = ((Vector3)(ref position)).ToString("F2"); array2[4] = val.activeInHierarchy; log.LogInfo((object)string.Format("Force-place {0} [{1}] via in-place → {2} {3} active={4}", array2)); continue; } GameObject spawned = null; string text4 = ""; object obj3 = TakeVolume(list2, text3) ?? ((list2.Count > 0) ? TakeAt(list2, 0) : null); SpawnLog($"force {text}[{text3}] slot={VolName(obj3)} emptyLeft={list2.Count}"); string replaced; if (obj3 != null) { spawned = VanillaSpawnOn(obj3, current); if ((Object)spawned != (Object)null) { text4 = "vanilla-empty " + VolName(obj3); } else { spawned = PlaceOnVolume(current, obj3); text4 = "fallback-empty " + VolName(obj3); } } else if (TrySwapVanilla(current, text3, list, out spawned, out replaced)) { text4 = "swap " + replaced + " on " + text3; } else { object obj4 = LargestVolume(list, text3) ?? ((list.Count > 0) ? list[0] : null); spawned = VanillaSpawnOn(obj4, current); if ((Object)spawned != (Object)null) { text4 = "vanilla-anchor " + VolName(obj4); } else { spawned = PlaceNeighbor(current, obj4); text4 = "neighbor of " + VolName(obj4); } obj3 = obj4; } if ((Object)spawned != (Object)null) { SitOnVolume(spawned, obj3, current, move: true); Freeze(spawned); MarkPlaced(text); RememberSlot(obj3); } ManualLogSource log2 = Plugin.Log; string[] array3 = new string[8] { "Force-place ", text, " [", text3, "] via ", text4, " → ", null }; object obj5; if (!((Object)spawned != (Object)null)) { obj5 = "FAILED"; } else { string[] obj6 = new string[5] { ((Object)spawned).name, " ", null, null, null }; position = spawned.transform.position; obj6[2] = ((Vector3)(ref position)).ToString("F2"); obj6[3] = " active="; obj6[4] = spawned.activeInHierarchy.ToString(); obj5 = string.Concat(obj6); } array3[7] = (string)obj5; log2.LogInfo((object)string.Concat(array3)); } } private static void PurgeExcluded() { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Expected O, but got Unknown //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) string[] array = new string[3] { "PhysGrabObject", "ItemAttributes", "ItemEquippable" }; for (int i = 0; i < array.Length; i++) { object[] array2 = GameAccess.FindAll(array[i], includeInactive: true); foreach (object obj in array2) { object obj2 = ((obj is Component) ? obj : null); GameObject val = ((obj2 != null) ? ((Component)obj2).gameObject : null); if (!((Object)val == (Object)null) && ((Object)val).name.IndexOf("(Clone)", StringComparison.OrdinalIgnoreCase) >= 0 && IsShopExcluded(((Object)val).name)) { string name = ((Object)val).name; Vector3 position = val.transform.position; SpawnLog("purge excluded shop clone " + name + " at " + ((Vector3)(ref position)).ToString("F2")); DestroyShopItem(val); } } } } private static List CollectShopVolumes() { List volumes = new List(); HashSet seen = new HashSet(); object obj = GameAccess.Instance("ShopManager"); IList list = ((obj != null) ? GameAccess.ListField(obj, "itemVolumes") : null); if (list != null) { IEnumerator enumerator = list.GetEnumerator(); try { while (enumerator.MoveNext()) { add(enumerator.Current); } } finally { ((IDisposable)((enumerator is IDisposable) ? enumerator : null))?.Dispose(); } } if (volumes.Count == 0) { object[] array = GameAccess.FindAll("ItemVolume"); for (int i = 0; i < array.Length; i++) { add(array[i]); } } return volumes; void add(object? v) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown Component val = (Component)((v is Component) ? v : null); if ((Object)(object)val != (Object)null && val.gameObject.activeInHierarchy) { string item = SlotKey(v); if (seen.Add(item)) { volumes.Add(v); } } } } private static string SlotKey(object? vol) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0079: 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) Component val = (Component)((vol is Component) ? vol : null); if ((Object)(object)val == (Object)null) { object obj = vol?.GetHashCode().ToString(); if (obj == null) { obj = ""; } return (string)obj; } Vector3 position = val.transform.position; return $"{Mathf.Round(position.x * 4f) / 4f:0.##},{Mathf.Round(position.y * 4f) / 4f:0.##},{Mathf.Round(position.z * 4f) / 4f:0.##}:{VolKind(vol)}"; } private static void RememberSlot(object? vol) { if (vol != null) { _usedSlots.Add(SlotKey(vol)); } } private static object? TakeVolume(List empty, string want) { for (int i = 0; i < empty.Count; i++) { if (string.Equals(VolKind(empty[i]), want, StringComparison.OrdinalIgnoreCase)) { return TakeAt(empty, i); } } return null; } private static object TakeAt(List list, int i) { object result = list[i]; list.RemoveAt(i); return result; } private static string VolKind(object? vol) { object obj; if (vol != null) { obj = GameAccess.GetMember(vol, "itemVolume")?.ToString(); if (obj == null) { return ""; } } else { obj = ""; } return (string)obj; } private static string VolName(object? vol) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) if (vol == null) { return "(none)"; } object obj = ((vol is Object) ? vol : null); return (((obj != null) ? ((Object)obj).name : null) ?? vol.GetType().Name) + "[" + VolKind(vol) + "]"; } private static bool TrySwapVanilla(object item, string want, List volumes, out GameObject? spawned, out string replaced) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected O, but got Unknown //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Expected O, but got Unknown spawned = null; replaced = ""; foreach (object volume in volumes) { if (!string.Equals(VolKind(volume), want, StringComparison.OrdinalIgnoreCase) && !IsLarge(VolKind(volume))) { continue; } GameObject val = Occupier(volume); if (!((Object)val == (Object)null)) { string name = ((Object)val).name; if (!IsModName(name)) { DestroyShopItem(val); spawned = VanillaSpawnOn(volume, item) ?? PlaceOnVolume(item, volume); replaced = name; return (Object)spawned != (Object)null; } } } return false; } private static bool IsLarge(string kind) { string text = kind ?? ""; if (text.IndexOf("large", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("wide", StringComparison.OrdinalIgnoreCase) < 0) { return text.IndexOf("plus", StringComparison.OrdinalIgnoreCase) >= 0; } return true; } private static bool IsModName(string name) { if (string.IsNullOrEmpty(name)) { return false; } foreach (object reservedMod in _reservedMods) { if (name.IndexOf(GameAccess.ItemName(reservedMod), StringComparison.OrdinalIgnoreCase) >= 0) { return true; } } return !VanillaCatalog.IsVanilla(name, name); } private static object? LargestVolume(List volumes, string want) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected O, but got Unknown //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Expected O, but got Unknown //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: 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) object obj = null; object obj2 = null; object obj3 = null; float num = -1f; float num2 = -1f; float num3 = -1f; foreach (object volume in volumes) { Component val = (Component)((volume is Component) ? volume : null); if (!((Object)val == (Object)null)) { Collider component = val.GetComponent(); float num4; if (!((Object)component != (Object)null)) { num4 = 0.2f; } else { Bounds bounds = component.bounds; float x = ((Bounds)(ref bounds)).size.x; bounds = component.bounds; num4 = x * ((Bounds)(ref bounds)).size.z; } float num5 = num4; string text = VolKind(volume); if (num5 > num3) { num3 = num5; obj3 = volume; } if (IsLarge(text) && num5 > num2) { num2 = num5; obj2 = volume; } if (string.Equals(text, want, StringComparison.OrdinalIgnoreCase) && num5 > num) { num = num5; obj = volume; } } } return obj2 ?? obj ?? obj3; } private static GameObject? Occupier(object vol) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected O, but got Unknown //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0091: 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_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Expected O, but got Unknown //IL_00ca: Expected O, but got Unknown //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Expected O, but got Unknown Component val = (Component)((vol is Component) ? vol : null); if ((Object)val == (Object)null) { return null; } Collider component = val.GetComponent(); Vector3 val2; Bounds bounds; if (!((Object)component != (Object)null)) { val2 = val.transform.position; } else { bounds = component.bounds; val2 = ((Bounds)(ref bounds)).center; } Vector3 val3; if (!((Object)component != (Object)null)) { val3 = Vector3.one * 0.35f; } else { bounds = component.bounds; val3 = ((Bounds)(ref bounds)).extents * 0.9f; } Vector3 val4 = val3; Collider[] array = Physics.OverlapBox(val2, val4, Quaternion.identity); foreach (Collider val5 in array) { if (!((Object)((Component)val5).transform == (Object)val.transform) && !((Component)val5).transform.IsChildOf(val.transform)) { GameObject val6 = ItemRoot(((Component)val5).gameObject); if ((Object)val6 != (Object)null) { return val6; } } } return null; } private static GameObject? ItemRoot(GameObject go) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected O, but got Unknown //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown Transform val = go.transform; Transform val2 = null; while ((Object)val != (Object)null) { string name = ((Object)val).name; if (name.IndexOf("Item ", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("(Clone)", StringComparison.OrdinalIgnoreCase) >= 0) { val2 = val; } val = val.parent; } if (!((Object)val2 != (Object)null)) { return null; } return ((Component)val2).gameObject; } private static void DestroyShopItem(GameObject go) { //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Expected O, but got Unknown try { Type type = GameAccess.Type("Photon.Pun.PhotonNetwork") ?? GameAccess.Type("PhotonNetwork"); MethodInfo methodInfo = AccessTools.Method(type, "Destroy", new Type[1] { typeof(GameObject) }, (Type[])null) ?? AccessTools.Method(type, "Destroy", new Type[1] { typeof(object) }, (Type[])null); if (GameAccess.IsMultiplayer() && methodInfo != null) { methodInfo.Invoke(null, new object[1] { go }); return; } } catch (Exception) { } Object.Destroy((Object)go); } private static GameObject? PlaceNeighbor(object item, object? volume) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0053: 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) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: 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_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0158: 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_0176: Unknown result type (might be due to invalid IL or missing references) //IL_017b: 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_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Expected O, but got Unknown //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Unknown result type (might be due to invalid IL or missing references) Component val = (Component)((volume is Component) ? volume : null); if ((Object)(object)val == (Object)null) { return PrefabRegistry.SpawnItem(item, Vector3.zero, Quaternion.identity); } Collider component = val.GetComponent(); float num; if (!((Object)component != (Object)null)) { num = 0.65f; } else { Bounds bounds = component.bounds; float x = ((Bounds)(ref bounds)).extents.x; bounds = component.bounds; num = Mathf.Max(0.4f, Mathf.Max(x, ((Bounds)(ref bounds)).extents.z) * 2.15f); } float num2 = num; Transform transform = val.transform; Quaternion rotation = RotationFor(item, val); Vector3[] array = (Vector3[])(object)new Vector3[5] { transform.position + transform.right * num2, transform.position - transform.right * num2, transform.position + transform.forward * num2, transform.position - transform.forward * num2, transform.position + transform.right * num2 * 0.5f + Vector3.up * 0.12f }; Vector3[] array2 = array; foreach (Vector3 val2 in array2) { if (!Occupied(val2, 0.28f) || !(val2 != array[0]) || !(val2 != array[^1])) { GameObject val3 = PrefabRegistry.SpawnItem(item, val2, rotation); if ((Object)val3 != (Object)null) { return val3; } } } return PrefabRegistry.SpawnItem(item, transform.position + transform.right * num2, rotation); } private static bool Occupied(Vector3 pos, float radius) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) Collider[] array = Physics.OverlapSphere(pos, radius); for (int i = 0; i < array.Length; i++) { string name = ((Object)((Component)array[i]).gameObject).name; if (name.IndexOf("Item ", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } if (name.IndexOf("(Clone)", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } } return false; } private static bool InScene(string name) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown return (Object)FindClone(name) != (Object)null; } private static bool VolumeEmpty(object vol) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0048: 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_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005b: 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) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Expected O, but got Unknown //IL_00b5: Expected O, but got Unknown Component val = (Component)((vol is Component) ? vol : null); if ((Object)val == (Object)null) { return false; } Collider component = val.GetComponent(); Vector3 val2; Vector3 val3; if ((Object)component != (Object)null) { Bounds bounds = component.bounds; val2 = ((Bounds)(ref bounds)).center; bounds = component.bounds; val3 = ((Bounds)(ref bounds)).extents * 0.85f; } else { val2 = val.transform.position; val3 = Vector3.one * 0.35f; } Collider[] array = Physics.OverlapBox(val2, val3, Quaternion.identity); foreach (Collider val4 in array) { if (!((Object)((Component)val4).transform == (Object)val.transform) && !((Component)val4).transform.IsChildOf(val.transform)) { string name = ((Object)((Component)val4).gameObject).name; if (name.IndexOf("Item ", StringComparison.OrdinalIgnoreCase) >= 0) { return false; } if (name.IndexOf("(Clone)", StringComparison.OrdinalIgnoreCase) >= 0) { return false; } } } return true; } private static GameObject? FindClone(string name) { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Expected O, but got Unknown if (string.IsNullOrEmpty(name)) { return null; } string[] array = new string[3] { "PhysGrabObject", "ItemAttributes", "ItemEquippable" }; for (int i = 0; i < array.Length; i++) { object[] array2 = GameAccess.FindAll(array[i], includeInactive: true); foreach (object obj in array2) { object obj2 = ((obj is Component) ? obj : null); GameObject val = ((obj2 != null) ? ((Component)obj2).gameObject : null); if (!((Object)val == (Object)null)) { string name2 = ((Object)val).name; if (name2.IndexOf("(Clone)", StringComparison.OrdinalIgnoreCase) >= 0 && name2.IndexOf(name, StringComparison.OrdinalIgnoreCase) >= 0) { return val; } } } } return null; } private static GameObject? VanillaSpawnOn(object? volume, object item) { //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Expected O, but got Unknown //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Expected O, but got Unknown //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Expected O, but got Unknown if (volume == null) { return null; } string text = GameAccess.ItemName(item); try { object obj = GameAccess.Instance("PunManager"); MethodInfo methodInfo = AccessTools.Method(GameAccess.Type("PunManager"), "SpawnShopItem", (Type[])null, (Type[])null); IList list = GameAccess.NewItemList(item); if (obj == null || methodInfo == null || list == null) { SpawnLog($"vanilla-call {text} aborted pun={obj != null} method={methodInfo != null} list={list != null}"); return null; } object[] parameters = new object[4] { volume, list, 0, false }; SpawnLog("vanilla-call " + text + "[" + GameAccess.ItemVolume(item) + "] vol=" + VolName(volume)); object obj2 = methodInfo.Invoke(obj, parameters); GameObject val = FindClone(text); if ((Object)val == (Object)null) { GameObject val2 = Occupier(volume); if ((Object)val2 != (Object)null && ((Object)val2).name.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0) { val = val2; } } object[] array = new object[4] { text, obj2, list.Count, null }; object obj3; if (!((Object)val != (Object)null)) { obj3 = "MISSING"; } else { string name = ((Object)val).name; obj3 = name + " active=" + val.activeInHierarchy; } array[3] = obj3; SpawnLog(string.Format("vanilla-return {0} result={1} leftover={2} clone={3}", array)); return val; } catch (Exception ex) { Plugin.Log.LogWarning((object)("Vanilla SpawnShopItem failed: " + ex.Message)); SpawnLog($"vanilla-call {text} EXCEPTION {ex}"); return null; } } private static void SitOnVolume(GameObject go, object? volume, object item, bool move) { //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003f: 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_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) try { go.SetActive(true); if (move) { Component val = (Component)((volume is Component) ? volume : null); if ((Object)(object)val != (Object)null) { float num = ShelfTop(volume); Vector3 position = val.transform.position; go.transform.position = new Vector3(position.x, num + 0.08f, position.z); go.transform.rotation = RotationFor(item, val); } } object[] array = new object[4] { ((Object)go).name, move, null, null }; Vector3 position2 = go.transform.position; array[2] = ((Vector3)(ref position2)).ToString("F2"); array[3] = VolName(volume); SpawnLog(string.Format("sit {0} move={1} pos={2} vol={3}", array)); } catch (Exception ex) { SpawnLog("sit failed " + ((Object)go).name + ": " + ex.Message); } } private static float ShelfTop(object? volume) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown //IL_0048: 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_0050: 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) Component val = (Component)((volume is Component) ? volume : null); if ((Object)(object)val == (Object)null) { return 0f; } Collider component = val.GetComponent(); if (!((Object)component != (Object)null)) { return val.transform.position.y; } Bounds bounds = component.bounds; return ((Bounds)(ref bounds)).max.y; } private static void Freeze(GameObject go) { Rigidbody[] componentsInChildren = go.GetComponentsInChildren(true); foreach (Rigidbody obj in componentsInChildren) { obj.isKinematic = true; obj.detectCollisions = true; } } private static GameObject? PlaceOnVolume(object item, object? volume) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Expected O, but got Unknown //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) Vector3 zero = Vector3.zero; Quaternion rotation = Quaternion.identity; Component val = (Component)((volume is Component) ? volume : null); if ((Object)(object)val != (Object)null) { ((Vector3)(ref zero))..ctor(val.transform.position.x, ShelfTop(volume) + 0.08f, val.transform.position.z); rotation = RotationFor(item, val); } string text = GameAccess.ItemName(item); SpawnLog("fallback-instantiate " + text + " at " + ((Vector3)(ref zero)).ToString("F2") + " vol=" + VolName(volume)); GameObject val2 = PrefabRegistry.SpawnItem(item, zero, rotation); if ((Object)val2 != (Object)null) { SitOnVolume(val2, volume, item, move: true); Freeze(val2); object[] array = new object[4] { text, ((Object)val2).name, val2.activeInHierarchy, null }; Vector3 position = val2.transform.position; array[3] = ((Vector3)(ref position)).ToString("F2"); SpawnLog(string.Format("fallback-ok {0} → {1} active={2} pos={3}", array)); } else { SpawnLog("fallback-FAIL " + text + " PrefabRegistry returned null"); } return val2; } private static Quaternion RotationFor(object item, Component volume) { //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) try { object obj = GameAccess.Instance("ShopManager"); object member = GameAccess.GetMember(obj, "itemRotateHelper"); Transform val = (Transform)((member is Transform) ? member : null); object member2 = GameAccess.GetMember(item, "spawnRotationOffset"); if ((Object)val != (Object)null) { Component val2 = (Component)((obj is Component) ? obj : null); if ((Object)(object)val2 != (Object)null) { val.SetParent(volume.transform, false); if (member2 is Quaternion localRotation) { val.localRotation = localRotation; } Quaternion rotation = val.rotation; val.SetParent(val2.transform, true); return rotation; } } } catch (Exception) { } return volume.transform.rotation; } private static void AppendRoundRobin(IList list, List groups, int n) { foreach (Group group in groups) { if (n < group.Count) { list.Add(group.Item); } } } private static void ApplyBudgets(object shop) { if (shop == null) { return; } GameAccess.DumpType(shop.GetType()); int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; object[] array = GameAccess.FindAll("ItemVolume"); object[] array2 = array; foreach (object obj in array2) { if (obj != null) { string text = GameAccess.GetMember(obj, "itemVolume")?.ToString() ?? ""; if (text.IndexOf("health", StringComparison.OrdinalIgnoreCase) >= 0) { num3++; } else if (text.IndexOf("upgrade", StringComparison.OrdinalIgnoreCase) >= 0) { num2++; } else if (text.IndexOf("crystal", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("power", StringComparison.OrdinalIgnoreCase) >= 0) { num4++; } else { num++; } } } int num5 = GameAccess.ReadInt(shop, "itemSpawnTargetAmount"); if (Plugin.FillEmptyVolumes.Value) { GameAccess.RaiseInt(shop, "itemSpawnTargetAmount", num); GameAccess.RaiseInt(shop, "itemUpgradesAmount", num2); GameAccess.RaiseInt(shop, "itemHealthPacksAmount", num3); GameAccess.RaiseInt(shop, "itemConsumablesAmount", num4); } else if (Plugin.ItemSpawnTargetAmount.Value > 0) { GameAccess.SetField(shop, "itemSpawnTargetAmount", Plugin.ItemSpawnTargetAmount.Value); } int num6 = GameAccess.ReadInt(shop, "itemSpawnTargetAmount"); Plugin.Log.LogInfo((object)($"Shop budgets items={num6} (was {num5}, general slots={num}) " + string.Format("upgrades={0} (slots={1}) ", GameAccess.ReadInt(shop, "itemUpgradesAmount"), num2) + string.Format("health={0} (slots={1}) ", GameAccess.ReadInt(shop, "itemHealthPacksAmount"), num3) + string.Format("consumables={0} (crystal slots={1}) ", GameAccess.ReadInt(shop, "itemConsumablesAmount"), num4) + $"volumes={array.Length}")); } private static IList? ListFor(object shop, ShopCategoryKind cat) { return cat switch { ShopCategoryKind.Upgrades => GameAccess.ListField(shop, "potentialItemUpgrades"), ShopCategoryKind.Health => GameAccess.ListField(shop, "potentialItemHealthPacks"), ShopCategoryKind.Consumables => GameAccess.ListField(shop, "potentialItemConsumables"), _ => GameAccess.ListField(shop, "potentialItems"), }; } private static void Collect(IList? list, HashSet seen) { if (list == null) { return; } IEnumerator enumerator = list.GetEnumerator(); try { while (enumerator.MoveNext()) { object current = enumerator.Current; if (current != null) { seen.Add(GameAccess.PrefabPath(current)); } } } finally { ((IDisposable)((enumerator is IDisposable) ? enumerator : null))?.Dispose(); } } private static void WeightByPrice(IList? list) { if (list == null || list.Count < 2) { return; } List list2 = new List(); IEnumerator enumerator = list.GetEnumerator(); try { while (enumerator.MoveNext()) { object current = enumerator.Current; if (current != null) { list2.Add(current); } } } finally { ((IDisposable)((enumerator is IDisposable) ? enumerator : null))?.Dispose(); } list2.Sort((object a, object b) => AveragePrice(b).CompareTo(AveragePrice(a))); list.Clear(); foreach (object item in list2) { list.Add(item); } } private static int AveragePrice(object item) { return GameAccess.AveragePrice(item); } private static int CountItems(object shop) { return (GameAccess.ListField(shop, "potentialItems")?.Count ?? 0) + (GameAccess.ListField(shop, "potentialItemConsumables")?.Count ?? 0) + (GameAccess.ListField(shop, "potentialItemUpgrades")?.Count ?? 0) + (GameAccess.ListField(shop, "potentialItemHealthPacks")?.Count ?? 0); } private static string ComputeHash(object shop) { StringBuilder sb = new StringBuilder(); eat(GameAccess.ListField(shop, "potentialItems")); eat(GameAccess.ListField(shop, "potentialItemConsumables")); eat(GameAccess.ListField(shop, "potentialItemUpgrades")); eat(GameAccess.ListField(shop, "potentialItemHealthPacks")); using (SHA256 sHA = SHA256.Create()) { return BitConverter.ToString(sHA.ComputeHash(Encoding.UTF8.GetBytes(sb.ToString()))).Replace("-", "").Substring(0, 12) .ToLowerInvariant(); } void eat(IList? list) { if (list == null) { return; } List list2 = new List(); IEnumerator enumerator = list.GetEnumerator(); try { while (enumerator.MoveNext()) { object current = enumerator.Current; if (current != null) { list2.Add(GameAccess.PrefabPath(current)); } } } finally { ((IDisposable)((enumerator is IDisposable) ? enumerator : null))?.Dispose(); } list2.Sort(StringComparer.OrdinalIgnoreCase); foreach (string item in list2) { sb.Append(item).Append(';'); } } } } internal static class VanillaCatalog { private static readonly string[] ModMarkers; private static readonly HashSet Paths; internal static bool IsVanilla(string path, string name) { string text = (path + " " + name).ToLowerInvariant(); string[] modMarkers = ModMarkers; foreach (string value in modMarkers) { if (text.IndexOf(value, StringComparison.Ordinal) >= 0) { return false; } } string text2 = (path ?? "").Replace('\\', '/').ToLowerInvariant(); int num = text2.LastIndexOf('/'); string item = ((num >= 0) ? text2.Substring(num + 1) : text2); if (Paths.Contains(item)) { return true; } foreach (string path2 in Paths) { if (text2.IndexOf(path2, StringComparison.Ordinal) >= 0) { return true; } } return false; } static VanillaCatalog() { ModMarkers = new string[7] { "medvac", "featherguard", "feather guard", "echo mannequin", "c.a.r.g.o", "cargo backpack", "mannequin" }; Paths = new HashSet(StringComparer.OrdinalIgnoreCase) { "item gun handgun", "item gun shotgun", "item gun tranq", "item gun laser", "item gun shockwave", "item gun stun", "item melee baseball bat", "item melee sledge hammer", "item melee frying pan", "item melee sword", "item melee inflatable hammer", "item melee stun baton", "item grenade explosive", "item grenade stun", "item grenade shockwave", "item mine explosive", "item mine shockwave", "item mine stun", "item drone zero gravity", "item drone feather", "item drone indestructible", "item drone battery", "item drone torque", "item staff void", "item staff zero gravity", "item staff torque", "item orb zero gravity", "item cart", "item cart small", "item cart laser", "item cart cannon", "item pocket cart", "item phase bridge", "item rubber duck", "item power crystal", "item leaf blower", "item reviveitem", "item valuable tracker", "item extraction tracker", "item walkietalkiebox", "item vehicle semiscooter", "item vehicle semiscooter small", "item health pack small", "item health pack medium", "item health pack large", "item upgrade player health", "item upgrade player energy", "item upgrade player extra jump", "item upgrade player sprint speed", "item upgrade player grab range", "item upgrade player grab strength", "item upgrade player tumble launch", "item upgrade player tumble climb", "item upgrade player tumble wings", "item upgrade player crouch rest" }; } } }