Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of SummonInheritanceFix v1.4.0
SummonInheritanceFix.dll
Decompiled 15 hours agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HG; using HarmonyLib; using Microsoft.CodeAnalysis; using RiskOfOptions; using RiskOfOptions.OptionConfigs; using RiskOfOptions.Options; using RoR2; using UnityEngine; using UnityEngine.Networking; [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("SummonInheritanceFix")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.3.0.0")] [assembly: AssemblyInformationalVersion("1.3.0")] [assembly: AssemblyProduct("SummonInheritanceFix")] [assembly: AssemblyTitle("SummonInheritanceFix")] [assembly: AssemblyVersion("1.3.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [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 SummonInheritanceFix { public static class ItemBehaviorScanner { private static Type itemBehaviorType; private static bool itemBehaviorLookedUp; private static Dictionary<string, ItemDef> normalizedItems; public static Type ItemBehaviorType { get { if (!itemBehaviorLookedUp) { itemBehaviorLookedUp = true; try { itemBehaviorType = typeof(CharacterBody).GetNestedType("ItemBehavior", BindingFlags.Public | BindingFlags.NonPublic); } catch { itemBehaviorType = null; } } return itemBehaviorType; } } public static void Run() { try { Type type = ItemBehaviorType; if (type == null) { SummonInheritancePlugin.Log.LogInfo((object)"ItemBehaviorScanner: CharacterBody.ItemBehavior not found in this game version, static scan skipped."); return; } Assembly assembly = typeof(CharacterBody).Assembly; string name = assembly.GetName().Name; EnsureNormalizedItems(); int num = 0; List<string> list = new List<string>(); List<string> list2 = new List<string>(); Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly2 in assemblies) { if (assembly2 == assembly || assembly2 == typeof(ItemBehaviorScanner).Assembly || assembly2.IsDynamic || !ReferencesAssembly(assembly2, name)) { continue; } foreach (Type item in SafeGetTypes(assembly2)) { if (item == null || item == type || item.IsAbstract || !type.IsAssignableFrom(item)) { continue; } num++; if (!TypeSpawnsAllies(item)) { continue; } ItemDef val = ResolveItemDef(item); if ((Object)(object)val != (Object)null) { if (!ModConfig.IsSummonItemName(((Object)val).name)) { ModConfig.AddAutoSummonItem(((Object)val).name); list.Add(((Object)val).name + " (" + item.FullName + ")"); } } else { list2.Add(item.FullName); } } } SummonInheritancePlugin.Log.LogInfo((object)("ItemBehaviorScanner: scanned " + num + " mod item-behaviour classes.")); if (list.Count > 0) { SummonInheritancePlugin.Log.LogInfo((object)("ItemBehaviorScanner: items detected as summoners (will not be inherited): " + string.Join(", ", list))); } if (list2.Count > 0) { SummonInheritancePlugin.Log.LogInfo((object)("ItemBehaviorScanner: these behaviour classes spawn allies but their item could not be resolved (add the item to ExtraSummonItems if needed): " + string.Join(", ", list2))); } } catch (Exception ex) { SummonInheritancePlugin.Log.LogWarning((object)("ItemBehaviorScanner failed: " + ex)); } } private static bool ReferencesAssembly(Assembly asm, string name) { try { AssemblyName[] referencedAssemblies = asm.GetReferencedAssemblies(); for (int i = 0; i < referencedAssemblies.Length; i++) { if (referencedAssemblies[i].Name == name) { return true; } } } catch { } return false; } private static IEnumerable<Type> SafeGetTypes(Assembly asm) { try { return asm.GetTypes(); } catch (ReflectionTypeLoadException ex) { return ex.Types; } catch { return new Type[0]; } } private static bool TypeSpawnsAllies(Type type) { try { MethodInfo[] methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); for (int i = 0; i < methods.Length; i++) { if (MethodCallsSpawn(methods[i])) { return true; } } ConstructorInfo[] constructors = type.GetConstructors(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); for (int i = 0; i < constructors.Length; i++) { if (MethodCallsSpawn(constructors[i])) { return true; } } Type[] nestedTypes = type.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic); for (int i = 0; i < nestedTypes.Length; i++) { if (TypeSpawnsAllies(nestedTypes[i])) { return true; } } } catch { } return false; } private static bool MethodCallsSpawn(MethodBase method) { try { MethodBody methodBody = method.GetMethodBody(); if (methodBody == null) { return false; } byte[] iLAsByteArray = methodBody.GetILAsByteArray(); if (iLAsByteArray == null) { return false; } Module module = method.Module; Type[] genericTypeArguments = ((method.DeclaringType != null && method.DeclaringType.IsGenericType) ? method.DeclaringType.GetGenericArguments() : null); Type[] genericMethodArguments = (method.IsGenericMethod ? method.GetGenericArguments() : null); for (int i = 0; i < iLAsByteArray.Length - 4; i++) { byte b = iLAsByteArray[i]; if (b != 40 && b != 111 && b != 115) { continue; } int num = BitConverter.ToInt32(iLAsByteArray, i + 1); int num2 = num >> 24; if (num2 != 6 && num2 != 10 && num2 != 43) { continue; } try { MethodBase methodBase = module.ResolveMethod(num, genericTypeArguments, genericMethodArguments); if (methodBase != null && IsSpawnCall(methodBase)) { return true; } } catch { } } } catch { } return false; } private static bool IsSpawnCall(MethodBase target) { Type declaringType = target.DeclaringType; if (declaringType == null) { return false; } string name = declaringType.Name; string name2 = target.Name; if (name == "DirectorCore" && name2 == "TrySpawnObject") { return true; } if (name == "MasterSummon") { return true; } if (name.EndsWith("SpawnCard") && (name2 == "DoSpawn" || name2 == "Spawn")) { return true; } return false; } public static ItemDef ResolveItemDef(Type behaviorType) { try { Type type = behaviorType; while (type != null) { object singleton = GetSingleton(type); if (singleton != null) { ItemDef val = FindItemDefMember(singleton); if ((Object)(object)val != (Object)null) { return val; } } type = type.DeclaringType; } EnsureNormalizedItems(); Type type2 = behaviorType; while (type2 != null) { string text = NormalizeTypeName(type2.Name); if (text.Length >= 4 && normalizedItems.TryGetValue(text, out var value)) { return value; } type2 = type2.DeclaringType; } } catch { } return null; } private static object GetSingleton(Type type) { Type type2 = type; while (type2 != null && type2 != typeof(object)) { try { PropertyInfo[] properties = type2.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (PropertyInfo propertyInfo in properties) { if (string.Equals(propertyInfo.Name, "instance", StringComparison.OrdinalIgnoreCase) && propertyInfo.GetIndexParameters().Length == 0 && propertyInfo.CanRead) { object value = propertyInfo.GetValue(null, null); if (value != null) { return value; } } } FieldInfo[] fields = type2.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (string.Equals(fieldInfo.Name, "instance", StringComparison.OrdinalIgnoreCase)) { object value2 = fieldInfo.GetValue(null); if (value2 != null) { return value2; } } } } catch { } type2 = type2.BaseType; } return null; } private static ItemDef FindItemDefMember(object instance) { Type type = instance.GetType(); try { FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (typeof(ItemDef).IsAssignableFrom(fieldInfo.FieldType)) { object? value = fieldInfo.GetValue(instance); ItemDef val = (ItemDef)((value is ItemDef) ? value : null); if ((Object)(object)val != (Object)null) { return val; } } } PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (PropertyInfo propertyInfo in properties) { if (propertyInfo.CanRead && propertyInfo.GetIndexParameters().Length == 0 && typeof(ItemDef).IsAssignableFrom(propertyInfo.PropertyType)) { object? value2 = propertyInfo.GetValue(instance, null); ItemDef val2 = (ItemDef)((value2 is ItemDef) ? value2 : null); if ((Object)(object)val2 != (Object)null) { return val2; } } } } catch { } return null; } private unsafe static void EnsureNormalizedItems() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) if (normalizedItems != null) { return; } normalizedItems = new Dictionary<string, ItemDef>(); GenericStaticEnumerable<ItemIndex, AllItemsEnumerator> allItems = ItemCatalog.allItems; AllItemsEnumerator enumerator = allItems.GetEnumerator(); try { while (((AllItemsEnumerator)(ref enumerator)).MoveNext()) { ItemDef itemDef = ItemCatalog.GetItemDef(((AllItemsEnumerator)(ref enumerator)).Current); if (!((Object)(object)itemDef == (Object)null) && !string.IsNullOrEmpty(((Object)itemDef).name)) { string text = NormalizeItemName(((Object)itemDef).name); if (text.Length > 0 && !normalizedItems.ContainsKey(text)) { normalizedItems[text] = itemDef; } } } } finally { ((IDisposable)(*(AllItemsEnumerator*)(&enumerator))/*cast due to .constrained prefix*/).Dispose(); } } private static string NormalizeItemName(string name) { string text = Letters(name); if (text.StartsWith("ITEM") && text.Length > 4) { text = text.Substring(4); } return text; } private static string NormalizeTypeName(string name) { string text = Letters(name); string[] array = new string[8] { "BODYBEHAVIOR", "BODYBEHAVIOUR", "BEHAVIOURSERVER", "BEHAVIORSERVER", "BEHAVIOUR", "BEHAVIOR", "SERVER", "ITEM" }; foreach (string text2 in array) { if (text.EndsWith(text2) && text.Length > text2.Length) { text = text.Substring(0, text.Length - text2.Length); break; } } return text; } private static string Letters(string value) { char[] array = new char[value.Length]; int length = 0; foreach (char c in value) { if (char.IsLetterOrDigit(c)) { array[length++] = char.ToUpperInvariant(c); } } return new string(array, 0, length); } } public static class ModConfig { private static readonly string[] BuiltinSummonItems = new string[10] { "BeetleGland", "RoboBallBuddy", "TitanGoldDuringTP", "Squid", "GhostOnKill", "Ghost", "MinorConstructOnKill", "VoidMegaCrabItem", "GummyClone", "MinionLeash" }; private static readonly string[] BuiltinSummonEquipment = new string[1] { "DroneBackup" }; private static readonly HashSet<string> summonItemSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static readonly HashSet<string> summonEquipmentSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); public static ConfigEntry<bool> BlockSummonItems = null; public static ConfigEntry<bool> BlockSummonEquipment = null; public static ConfigEntry<string> ExtraSummonItems = null; public static ConfigEntry<string> ExtraSummonEquipment = null; public static ConfigEntry<int> MaxInheritingMinionsPerOwner = null; public static ConfigEntry<bool> AutoDetectSummoners = null; public static ConfigEntry<bool> ProactiveBehaviorScan = null; private static readonly HashSet<string> autoSummonSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); public static ConfigEntry<string> LearnedSummonItems = null; private static readonly HashSet<string> learnedSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); public static ConfigEntry<bool> VerboseLogging = null; public static ConfigEntry<bool> DumpItemCodenames = null; public static ConfigEntry<bool> ModEnabled = null; public static ConfigEntry<bool> LiveSync = null; public static ConfigEntry<bool> InheritEquipment = null; public static ConfigEntry<float> ItemStackMultiplier = null; public static ConfigEntry<int> ItemCountCap = null; public static ConfigEntry<bool> InheritModdedOrUnknownTiers = null; public static readonly Dictionary<ItemTier, ConfigEntry<bool>> TierConfigs = new Dictionary<ItemTier, ConfigEntry<bool>>(); public static readonly Dictionary<string, ConfigEntry<bool>> TierDefNameConfigs = new Dictionary<string, ConfigEntry<bool>>(StringComparer.OrdinalIgnoreCase); public static ConfigEntry<bool> InheritDronesAndTurrets = null; public static ConfigEntry<bool> InheritBeetleGuards = null; public static ConfigEntry<bool> InheritAurelionite = null; public static ConfigEntry<bool> InheritSolusProbes = null; public static ConfigEntry<bool> InheritSquidTurrets = null; public static ConfigEntry<bool> InheritGhosts = null; public static ConfigEntry<bool> InheritBackupDrones = null; public static ConfigEntry<bool> InheritDevoted = null; public static ConfigEntry<bool> InheritOtherMinions = null; public static ConfigEntry<bool> InheritDios = null; public static ConfigEntry<string> BlacklistedItems = null; private static readonly HashSet<string> parsedBlacklist = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static ConfigFile configFile; private static bool riskOfOptionsInstalled; public static void Init(ConfigFile config) { configFile = config; riskOfOptionsInstalled = Chainloader.PluginInfos.ContainsKey("com.rune580.riskofoptions"); ModEnabled = config.Bind<bool>("1. General", "ModEnabled", true, "Enable or disable the SummonInheritance mod."); LiveSync = config.Bind<bool>("1. General", "LiveSync", true, "Dynamically update living summons' inventories whenever you pick up new items during a stage."); InheritEquipment = config.Bind<bool>("1. General", "InheritEquipment", true, "Whether summons also inherit the player's active equipment."); ItemStackMultiplier = config.Bind<float>("1. General", "ItemStackMultiplier", 1f, "Fraction of player's item count given to summons (1.0 = 100%, 0.5 = 50%)."); ItemCountCap = config.Bind<int>("1. General", "ItemCountCap", 0, "Maximum stacks of any single item granted to a summon (0 = uncapped)."); InheritModdedOrUnknownTiers = config.Bind<bool>("2. Item Tiers", "InheritModdedOrUnknownTiers", true, "Automatically inherit items belonging to custom modded or uncataloged tiers (e.g. Starstorm 2 Relics, DLC tiers)."); BindBaseTier(config, (ItemTier)0, "InheritTier_Tier1", "Common (White)"); BindBaseTier(config, (ItemTier)1, "InheritTier_Tier2", "Uncommon (Green)"); BindBaseTier(config, (ItemTier)2, "InheritTier_Tier3", "Legendary (Red)"); BindBaseTier(config, (ItemTier)4, "InheritTier_Boss", "Boss (Yellow)"); BindBaseTier(config, (ItemTier)3, "InheritTier_Lunar", "Lunar (Blue)"); BindBaseTier(config, (ItemTier)6, "InheritTier_VoidTier1", "Void Common"); BindBaseTier(config, (ItemTier)7, "InheritTier_VoidTier2", "Void Uncommon"); BindBaseTier(config, (ItemTier)8, "InheritTier_VoidTier3", "Void Legendary"); BindBaseTier(config, (ItemTier)9, "InheritTier_VoidBoss", "Void Boss"); InheritDronesAndTurrets = config.Bind<bool>("3. Summon Types", "InheritDronesAndTurrets", true, "Inherit items on purchaseable drones and gunner turrets."); InheritBeetleGuards = config.Bind<bool>("3. Summon Types", "InheritBeetleGuards", true, "Inherit items on Queen's Gland Beetle Guards."); InheritAurelionite = config.Bind<bool>("3. Summon Types", "InheritAurelionite", true, "Inherit items on Halcyon Seed Golden Titan (Aurelionite)."); InheritSolusProbes = config.Bind<bool>("3. Summon Types", "InheritSolusProbes", true, "Inherit items on Empathy Cores Solus Probes."); InheritSquidTurrets = config.Bind<bool>("3. Summon Types", "InheritSquidTurrets", true, "Inherit items on Squid Polyp turrets."); InheritGhosts = config.Bind<bool>("3. Summon Types", "InheritGhosts", true, "Inherit items on Happiest Mask ghosts."); InheritBackupDrones = config.Bind<bool>("3. Summon Types", "InheritBackupDrones", true, "Inherit items on The Back-up equipment strike drones."); InheritDevoted = config.Bind<bool>("3. Summon Types", "InheritDevoted", true, "Inherit items on Devoted Lemurians and Seekers of the Storm allies."); InheritOtherMinions = config.Bind<bool>("3. Summon Types", "InheritOtherMinions", true, "Inherit items on any other minion/creature belonging to your team (including modded summons)."); InheritDios = config.Bind<bool>("4. Safety & Blacklist", "InheritDios", false, "Allow summons to inherit ExtraLife (Dio's Best Friend). Disabled by default to prevent drone revival bugs."); BlacklistedItems = config.Bind<string>("4. Safety & Blacklist", "BlacklistedItems", "LunarPrimaryReplacement, LunarSecondaryReplacement, LunarUtilityReplacement, LunarSpecialReplacement, TitanGoldDuringTP, RoboBallBuddy, BeetleGland, MinionLeash, Ghost", "Comma-separated item codenames to blacklist from being given to summons."); BlockSummonItems = config.Bind<bool>("5. Loop Protection", "BlockSummonItems", true, "Never copy items that spawn extra drones/allies (Queen's Gland, Defiant Gouge, Zoea, Squid Polyp, Happiest Mask, ...) onto summons. Prevents infinite summon loops. Strongly recommended."); BlockSummonEquipment = config.Bind<bool>("5. Loop Protection", "BlockSummonEquipment", true, "Never copy equipment that spawns allies (The Back-up) onto summons. Prevents infinite summon loops. Strongly recommended."); ExtraSummonItems = config.Bind<string>("5. Loop Protection", "ExtraSummonItems", "", "Comma-separated extra item codenames that spawn allies (use this for modded items). Added on top of the built-in list."); ExtraSummonEquipment = config.Bind<string>("5. Loop Protection", "ExtraSummonEquipment", "", "Comma-separated extra equipment codenames that spawn allies (use this for modded equipment). Added on top of the built-in list."); MaxInheritingMinionsPerOwner = config.Bind<int>("5. Loop Protection", "MaxInheritingMinionsPerOwner", 40, "Safety net: maximum number of minions per player that may receive inherited items at the same time (0 = unlimited). Stops runaway spawn loops caused by unknown modded summon items."); AutoDetectSummoners = config.Bind<bool>("5. Loop Protection", "AutoDetectSummoners", true, "Behaviour-based detection: if an inheriting minion summons an ally, the new ally is blocked from inheriting and the responsible item is learned automatically (works for modded items, no names needed)."); ProactiveBehaviorScan = config.Bind<bool>("5. Loop Protection", "ProactiveBehaviorScan", true, "At startup, scan the item-behaviour classes of installed mods; items whose code spawns allies (DirectorCore.TrySpawnObject / MasterSummon) are never inherited, before anything spawns. Detected items are listed in the log."); LearnedSummonItems = config.Bind<string>("5. Loop Protection", "LearnedSummonItems", "", "Filled in automatically by AutoDetectSummoners. Comma-separated item codenames learned to spawn allies. You can edit or clear it."); VerboseLogging = config.Bind<bool>("5. Loop Protection", "VerboseLogging", false, "Log every item/equipment that gets blocked from inheritance. Useful for testing."); DumpItemCodenames = config.Bind<bool>("5. Loop Protection", "DumpItemCodenames", false, "On game load, print the codename of every item and equipment to the BepInEx log so you can find names for the Extra* lists."); RebuildSummonSets(); UpdateBlacklistCache(); BlacklistedItems.SettingChanged += delegate { UpdateBlacklistCache(); }; ExtraSummonItems.SettingChanged += delegate { RebuildSummonSets(); }; ExtraSummonEquipment.SettingChanged += delegate { RebuildSummonSets(); }; LearnedSummonItems.SettingChanged += delegate { RebuildSummonSets(); }; if (riskOfOptionsInstalled) { TryRegisterBaseRiskOfOptions(); } ((ResourceAvailability)(ref ItemTierCatalog.availability)).CallWhenAvailable((Action)OnItemTierCatalogAvailable); ((ResourceAvailability)(ref ItemCatalog.availability)).CallWhenAvailable((Action)OnItemCatalogAvailable); ((ResourceAvailability)(ref EquipmentCatalog.availability)).CallWhenAvailable((Action)OnEquipmentCatalogAvailable); } private static void RebuildSummonSets() { summonItemSet.Clear(); summonEquipmentSet.Clear(); string[] builtinSummonItems = BuiltinSummonItems; foreach (string item in builtinSummonItems) { summonItemSet.Add(item); } builtinSummonItems = BuiltinSummonEquipment; foreach (string item2 in builtinSummonItems) { summonEquipmentSet.Add(item2); } AddCsv((ExtraSummonItems != null) ? ExtraSummonItems.Value : null, summonItemSet); learnedSet.Clear(); AddCsv((LearnedSummonItems != null) ? LearnedSummonItems.Value : null, learnedSet); foreach (string item3 in learnedSet) { summonItemSet.Add(item3); } AddCsv((ExtraSummonEquipment != null) ? ExtraSummonEquipment.Value : null, summonEquipmentSet); } private static void AddCsv(string csv, HashSet<string> target) { if (string.IsNullOrWhiteSpace(csv)) { return; } string[] array = csv.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length > 0) { target.Add(text); } } } public static bool IsSummonItemName(string name) { if (!summonItemSet.Contains(name)) { return autoSummonSet.Contains(name); } return true; } public static void AddAutoSummonItem(string name) { if (!string.IsNullOrEmpty(name)) { autoSummonSet.Add(name); } } public static void LearnSummonItem(string name) { if (!string.IsNullOrEmpty(name) && !summonItemSet.Contains(name)) { summonItemSet.Add(name); learnedSet.Add(name); LearnedSummonItems.Value = string.Join(", ", learnedSet); } } public static bool IsSummonItem(ItemDef itemDef) { if ((Object)(object)itemDef == (Object)null || BlockSummonItems == null || !BlockSummonItems.Value) { return false; } if (!summonItemSet.Contains(((Object)itemDef).name)) { return autoSummonSet.Contains(((Object)itemDef).name); } return true; } public static bool IsSummonEquipment(EquipmentIndex index) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0019: Unknown result type (might be due to invalid IL or missing references) if ((int)index == -1 || BlockSummonEquipment == null || !BlockSummonEquipment.Value) { return false; } EquipmentDef equipmentDef = EquipmentCatalog.GetEquipmentDef(index); if ((Object)(object)equipmentDef != (Object)null) { return summonEquipmentSet.Contains(((Object)equipmentDef).name); } return false; } private unsafe static void OnItemCatalogAvailable() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Invalid comparison between Unknown and I4 //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: 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_00c0: Unknown result type (might be due to invalid IL or missing references) try { List<string> list = new List<string>(); foreach (string item in summonItemSet) { if ((int)ItemCatalog.FindItemIndex(item) == -1) { list.Add(item); } } if (list.Count > 0) { SummonInheritancePlugin.Log.LogInfo((object)("Loop protection: these summon-item names were not found in the item catalog (harmless, may be renamed or from a mod that is not installed): " + string.Join(", ", list))); } else { SummonInheritancePlugin.Log.LogInfo((object)("Loop protection: all " + summonItemSet.Count + " summon-item names resolved.")); } if (DumpItemCodenames.Value) { List<string> list2 = new List<string>(); GenericStaticEnumerable<ItemIndex, AllItemsEnumerator> allItems = ItemCatalog.allItems; AllItemsEnumerator enumerator2 = allItems.GetEnumerator(); try { while (((AllItemsEnumerator)(ref enumerator2)).MoveNext()) { ItemDef itemDef = ItemCatalog.GetItemDef(((AllItemsEnumerator)(ref enumerator2)).Current); if ((Object)(object)itemDef != (Object)null) { list2.Add(((Object)itemDef).name); } } } finally { ((IDisposable)(*(AllItemsEnumerator*)(&enumerator2))/*cast due to .constrained prefix*/).Dispose(); } SummonInheritancePlugin.Log.LogInfo((object)("ITEM CODENAMES: " + string.Join(", ", list2))); } if (ProactiveBehaviorScan.Value) { ItemBehaviorScanner.Run(); } } catch (Exception ex) { SummonInheritancePlugin.Log.LogWarning((object)("Item catalog check failed: " + ex)); } } private static void OnEquipmentCatalogAvailable() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Invalid comparison between Unknown and I4 try { List<string> list = new List<string>(); foreach (string item in summonEquipmentSet) { if ((int)EquipmentCatalog.FindEquipmentIndex(item) == -1) { list.Add(item); } } if (list.Count > 0) { SummonInheritancePlugin.Log.LogInfo((object)("Loop protection: these summon-equipment names were not found in the equipment catalog: " + string.Join(", ", list))); } else { SummonInheritancePlugin.Log.LogInfo((object)("Loop protection: all " + summonEquipmentSet.Count + " summon-equipment names resolved.")); } if (!DumpItemCodenames.Value) { return; } List<string> list2 = new List<string>(); for (int i = 0; i < EquipmentCatalog.equipmentCount; i++) { EquipmentDef equipmentDef = EquipmentCatalog.GetEquipmentDef((EquipmentIndex)i); if ((Object)(object)equipmentDef != (Object)null) { list2.Add(((Object)equipmentDef).name); } } SummonInheritancePlugin.Log.LogInfo((object)("EQUIPMENT CODENAMES: " + string.Join(", ", list2))); } catch (Exception ex) { SummonInheritancePlugin.Log.LogWarning((object)("Equipment catalog check failed: " + ex)); } } private static void BindBaseTier(ConfigFile config, ItemTier tier, string key, string name) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) ConfigEntry<bool> value = config.Bind<bool>("2. Item Tiers", key, true, "Inherit " + name + " items."); TierConfigs[tier] = value; TierDefNameConfigs[key] = value; } private static void OnItemTierCatalogAvailable() { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0011: 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_0111: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: 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_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Expected O, but got Unknown if (configFile == null) { return; } try { Enumerator<ItemTierDef> enumerator = ItemTierCatalog.allItemTierDefs.GetEnumerator(); try { while (enumerator.MoveNext()) { ItemTierDef current = enumerator.Current; if ((Object)(object)current == (Object)null) { continue; } string name = ((Object)current).name; if (string.IsNullOrEmpty(name)) { continue; } string text = name.Replace("Def", "").Trim(); string text2 = "InheritTier_" + text; ConfigEntry<bool> value; if (!TierDefNameConfigs.ContainsKey(name) && !TierDefNameConfigs.ContainsKey(text2)) { ConfigEntry<bool> val = configFile.Bind<bool>("2. Item Tiers", text2, true, "Dynamically discovered tier: Inherit " + text + " items."); TierConfigs[current.tier] = val; TierDefNameConfigs[name] = val; TierDefNameConfigs[text2] = val; if (riskOfOptionsInstalled) { TryRegisterOption((BaseOption)new CheckBoxOption(val)); } Debug.Log((object)$"[SummonInheritance] Dynamically registered tier: {text} ({current.tier})"); } else if (TierConfigs.TryGetValue(current.tier, out value)) { TierDefNameConfigs[name] = value; } } } finally { ((IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose(); } } catch (Exception arg) { Debug.LogWarning((object)$"[SummonInheritance] Error scanning ItemTierCatalog: {arg}"); } } public static bool IsTierAllowed(ItemDef itemDef) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Invalid comparison between Unknown and I4 //IL_0061: 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) if ((Object)(object)itemDef == (Object)null) { return false; } if ((int)itemDef.tier == 5) { return false; } try { ItemTierDef itemTierDef = ItemTierCatalog.GetItemTierDef(itemDef.tier); if ((Object)(object)itemTierDef != (Object)null && !string.IsNullOrEmpty(((Object)itemTierDef).name) && TierDefNameConfigs.TryGetValue(((Object)itemTierDef).name, out var value)) { return value.Value; } } catch { } if (TierConfigs.TryGetValue(itemDef.tier, out var value2)) { return value2.Value; } return InheritModdedOrUnknownTiers.Value; } private static void UpdateBlacklistCache() { parsedBlacklist.Clear(); if (!string.IsNullOrWhiteSpace(BlacklistedItems.Value)) { string[] array = BlacklistedItems.Value.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text in array) { parsedBlacklist.Add(text.Trim()); } } } public static bool IsItemBlacklisted(string itemName) { return parsedBlacklist.Contains(itemName); } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] private static void TryRegisterBaseRiskOfOptions() { //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Expected O, but got Unknown //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Expected O, but got Unknown //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Expected O, but got Unknown //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Expected O, but got Unknown //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Expected O, but got Unknown //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Expected O, but got Unknown //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Expected O, but got Unknown //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Expected O, but got Unknown //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected O, but got Unknown //IL_007e: 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_0179: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Expected O, but got Unknown //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Expected O, but got Unknown //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Expected O, but got Unknown //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Expected O, but got Unknown //IL_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Expected O, but got Unknown //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Expected O, but got Unknown //IL_01ec: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Expected O, but got Unknown //IL_01fb: Unknown result type (might be due to invalid IL or missing references) //IL_0205: Expected O, but got Unknown //IL_020a: Unknown result type (might be due to invalid IL or missing references) //IL_0214: Expected O, but got Unknown //IL_0219: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Expected O, but got Unknown //IL_0228: Unknown result type (might be due to invalid IL or missing references) //IL_0232: Expected O, but got Unknown //IL_0237: Unknown result type (might be due to invalid IL or missing references) //IL_0241: Expected O, but got Unknown //IL_0246: Unknown result type (might be due to invalid IL or missing references) //IL_0250: Expected O, but got Unknown //IL_0255: Unknown result type (might be due to invalid IL or missing references) //IL_025a: Unknown result type (might be due to invalid IL or missing references) //IL_0261: Unknown result type (might be due to invalid IL or missing references) //IL_026e: Expected O, but got Unknown //IL_0269: Unknown result type (might be due to invalid IL or missing references) //IL_0273: Expected O, but got Unknown //IL_0278: Unknown result type (might be due to invalid IL or missing references) //IL_0282: Expected O, but got Unknown try { ModSettingsManager.SetModDescription("Allows all summoned minions, drones, turrets, and allied creatures in your team to inherit your items and equipment.", "com.SummonInheritanceFix.Fork", "SummonInheritanceFix"); try { using Stream stream = typeof(ModConfig).Assembly.GetManifestResourceStream("SummonInheritanceFix.icon.png"); if (stream != null) { byte[] array = new byte[stream.Length]; stream.Read(array, 0, array.Length); Texture2D val = new Texture2D(256, 256, (TextureFormat)4, false); if (ImageConversion.LoadImage(val, array)) { ModSettingsManager.SetModIcon(Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f)), "com.SummonInheritanceFix.Fork", "SummonInheritanceFix"); } } } catch (Exception arg) { Debug.LogWarning((object)$"[SummonInheritance] Failed to load embedded icon: {arg}"); } ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(ModEnabled)); ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(LiveSync)); ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(InheritEquipment)); ModSettingsManager.AddOption((BaseOption)new StepSliderOption(ItemStackMultiplier, new StepSliderConfig { min = 0.1f, max = 2f, increment = 0.1f })); ModSettingsManager.AddOption((BaseOption)new IntSliderOption(ItemCountCap, new IntSliderConfig { min = 0, max = 50 })); ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(InheritModdedOrUnknownTiers)); foreach (KeyValuePair<ItemTier, ConfigEntry<bool>> tierConfig in TierConfigs) { ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(tierConfig.Value)); } ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(InheritDronesAndTurrets)); ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(InheritBeetleGuards)); ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(InheritAurelionite)); ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(InheritSolusProbes)); ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(InheritSquidTurrets)); ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(InheritGhosts)); ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(InheritBackupDrones)); ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(InheritDevoted)); ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(InheritOtherMinions)); ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(InheritDios)); ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(BlockSummonItems)); ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(BlockSummonEquipment)); ModSettingsManager.AddOption((BaseOption)new IntSliderOption(MaxInheritingMinionsPerOwner, new IntSliderConfig { min = 0, max = 100 })); ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(VerboseLogging)); } catch (Exception arg2) { Debug.LogWarning((object)$"[SummonInheritance] Failed to register base RiskOfOptions: {arg2}"); } } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] private static void TryRegisterOption(BaseOption option) { try { ModSettingsManager.AddOption(option); } catch (Exception arg) { Debug.LogWarning((object)$"[SummonInheritance] Failed to register dynamic option: {arg}"); } } } [BepInPlugin("com.SummonInheritanceFix.Fork", "SummonInheritanceFix", "1.3.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInIncompatibility("com.Antigravity.SummonInheritance")] public class SummonInheritancePlugin : BaseUnityPlugin { public const string PluginGUID = "com.SummonInheritanceFix.Fork"; public const string PluginName = "SummonInheritanceFix"; public const string PluginVersion = "1.3.0"; internal static ManualLogSource Log; private static readonly List<MinionInheritanceSync> ActiveMinions = new List<MinionInheritanceSync>(); private static float nextCapWarningTime; public static SummonInheritancePlugin Instance { get; private set; } private void Awake() { Instance = this; Log = ((BaseUnityPlugin)this).Logger; ModConfig.Init(((BaseUnityPlugin)this).Config); SummonWatcher.Install(); MinionOwnership.onMinionOwnerChangedGlobal += OnMinionOwnerChanged; CharacterMaster.onStartGlobal += OnMasterStart; Inventory.onInventoryChangedGlobal += OnInventoryChanged; Stage.onStageStartGlobal += OnStageStart; ((BaseUnityPlugin)this).Logger.LogInfo((object)"SummonInheritance v1.3.0 initialized (dynamic tiers + summon-loop protection)."); } private void OnDestroy() { MinionOwnership.onMinionOwnerChangedGlobal -= OnMinionOwnerChanged; CharacterMaster.onStartGlobal -= OnMasterStart; Inventory.onInventoryChangedGlobal -= OnInventoryChanged; Stage.onStageStartGlobal -= OnStageStart; } private void OnMasterStart(CharacterMaster master) { if (NetworkServer.active && ModConfig.ModEnabled.Value && (Object)(object)master != (Object)null) { ((MonoBehaviour)this).StartCoroutine(CheckAndAttachSync(master)); } } private void OnMinionOwnerChanged(MinionOwnership minionOwnership) { if (NetworkServer.active && ModConfig.ModEnabled.Value && (Object)(object)minionOwnership != (Object)null) { CharacterMaster component = ((Component)minionOwnership).GetComponent<CharacterMaster>(); if ((Object)(object)component != (Object)null) { ((MonoBehaviour)this).StartCoroutine(CheckAndAttachSync(component)); } } } private IEnumerator CheckAndAttachSync(CharacterMaster master) { yield return null; if ((Object)(object)master == (Object)null || (Object)(object)master.inventory == (Object)null || (Object)(object)((Component)master).GetComponent<MinionInheritanceSync>() != (Object)null || (Object)(object)((Component)master).GetComponent<NoInheritMarker>() != (Object)null || !ShouldInherit(master, out var ownerMaster) || (Object)(object)ownerMaster == (Object)null) { yield break; } int value = ModConfig.MaxInheritingMinionsPerOwner.Value; if (value > 0 && CountInheritingMinions(ownerMaster) >= value) { if (Time.unscaledTime >= nextCapWarningTime) { nextCapWarningTime = Time.unscaledTime + 10f; Log.LogWarning((object)("Inheriting-minion cap (" + value + ") reached for one player; new minions will not inherit items until some die. If this keeps happening, a modded item may be spawning allies: add its codename to ExtraSummonItems.")); } } else { ((Component)master).gameObject.AddComponent<MinionInheritanceSync>().Initialize(master, ownerMaster); } } public static void RequestSyncAll() { if ((Object)(object)Instance != (Object)null) { ((MonoBehaviour)Instance).StartCoroutine(SyncAllNextFrame()); } } private static IEnumerator SyncAllNextFrame() { yield return null; ActiveMinions.RemoveAll((MinionInheritanceSync m) => (Object)(object)m == (Object)null); MinionInheritanceSync[] array = ActiveMinions.ToArray(); foreach (MinionInheritanceSync minionInheritanceSync in array) { if ((Object)(object)minionInheritanceSync != (Object)null) { minionInheritanceSync.Sync(); } } } private static int CountInheritingMinions(CharacterMaster owner) { int num = 0; for (int i = 0; i < ActiveMinions.Count; i++) { MinionInheritanceSync minionInheritanceSync = ActiveMinions[i]; if ((Object)(object)minionInheritanceSync != (Object)null && (Object)(object)minionInheritanceSync.OwnerMaster == (Object)(object)owner) { num++; } } return num; } private static void OnStageStart(Stage stage) { if (!NetworkServer.active || !ModConfig.ModEnabled.Value) { return; } ActiveMinions.RemoveAll((MinionInheritanceSync m) => (Object)(object)m == (Object)null); MinionInheritanceSync[] array = ActiveMinions.ToArray(); foreach (MinionInheritanceSync minionInheritanceSync in array) { if ((Object)(object)minionInheritanceSync != (Object)null) { minionInheritanceSync.Sync(); } } } private static void OnInventoryChanged(Inventory inventory) { if (!NetworkServer.active || !ModConfig.ModEnabled.Value || !ModConfig.LiveSync.Value || (Object)(object)inventory == (Object)null) { return; } ActiveMinions.RemoveAll((MinionInheritanceSync m) => (Object)(object)m == (Object)null); MinionInheritanceSync[] array = ActiveMinions.ToArray(); foreach (MinionInheritanceSync minionInheritanceSync in array) { if ((Object)(object)minionInheritanceSync != (Object)null && (Object)(object)minionInheritanceSync.OwnerMaster != (Object)null && (Object)(object)minionInheritanceSync.OwnerMaster.inventory == (Object)(object)inventory) { minionInheritanceSync.Sync(); } } } public static void RegisterMinion(MinionInheritanceSync sync) { if (!ActiveMinions.Contains(sync)) { ActiveMinions.Add(sync); } } public static void UnregisterMinion(MinionInheritanceSync sync) { ActiveMinions.Remove(sync); } private static bool HasBodyFlag(CharacterBody body, long mask) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)body != (Object)null) { return ((ulong)body.bodyFlags & (ulong)mask) != 0; } return false; } private static bool HasItemByName(Inventory inventory, string codename) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Invalid comparison between Unknown and I4 //IL_0017: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)inventory == (Object)null) { return false; } ItemIndex val = ItemCatalog.FindItemIndex(codename); if ((int)val != -1) { return inventory.GetItemCountPermanent(val) > 0; } return false; } public static bool ShouldInherit(CharacterMaster minionMaster, out CharacterMaster ownerMaster) { ownerMaster = null; if (!ModConfig.ModEnabled.Value || (Object)(object)minionMaster == (Object)null || (Object)(object)minionMaster.inventory == (Object)null) { return false; } MinionOwnership minionOwnership = minionMaster.minionOwnership; if ((Object)(object)minionOwnership == (Object)null || (Object)(object)minionOwnership.ownerMaster == (Object)null) { return false; } ownerMaster = minionOwnership.ownerMaster; if ((Object)(object)ownerMaster == (Object)(object)minionMaster) { return false; } if ((Object)(object)ownerMaster.playerCharacterMasterController == (Object)null) { return false; } string text = (((Object)(object)minionMaster.bodyPrefab != (Object)null) ? ((Object)minionMaster.bodyPrefab).name : ""); CharacterBody body = minionMaster.GetBody(); if (string.IsNullOrEmpty(text) && (Object)(object)body != (Object)null) { text = ((Object)body).name; } string text2 = ((Object)minionMaster).name ?? ""; if (text.Contains("EngiTurret") || text.Contains("EngiWalkerTurret") || text2.Contains("EngiTurret") || text2.Contains("EngiWalkerTurret")) { return false; } if (text.Contains("DroneBackup") || text2.Contains("DroneBackup")) { return ModConfig.InheritBackupDrones.Value; } if (((Object)(object)body != (Object)null && (HasBodyFlag(body, 4194304L) || HasBodyFlag(body, 33554432L))) || text.Contains("Drone") || text.Contains("Turret") || text2.Contains("Drone") || text2.Contains("Turret")) { return ModConfig.InheritDronesAndTurrets.Value; } if (text.Contains("BeetleGuard") || text2.Contains("BeetleGuard")) { return ModConfig.InheritBeetleGuards.Value; } if (text.Contains("TitanGold") || text2.Contains("TitanGold")) { return ModConfig.InheritAurelionite.Value; } if (text.Contains("RoboBall") || text.Contains("Solus") || text2.Contains("RoboBall") || text2.Contains("Solus")) { return ModConfig.InheritSolusProbes.Value; } if (text.Contains("SquidTurret") || text2.Contains("SquidTurret")) { return ModConfig.InheritSquidTurrets.Value; } if (HasItemByName(minionMaster.inventory, "Ghost") || text.Contains("Ghost") || text2.Contains("Ghost")) { return ModConfig.InheritGhosts.Value; } if (((Object)(object)body != (Object)null && HasBodyFlag(body, 8192L)) || text.Contains("Devotion") || text2.Contains("Devotion") || text.Contains("LemurianBruiser")) { return ModConfig.InheritDevoted.Value; } return ModConfig.InheritOtherMinions.Value; } } public class MinionInheritanceSync : MonoBehaviour { private bool syncing; private readonly Dictionary<ItemIndex, int> given = new Dictionary<ItemIndex, int>(); public CharacterMaster MinionMaster { get; private set; } public CharacterMaster OwnerMaster { get; private set; } public HashSet<string> GetInheritedItemNames() { //IL_002d: Unknown result type (might be due to invalid IL or missing references) HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair<ItemIndex, int> item in given) { if (item.Value > 0) { ItemDef itemDef = ItemCatalog.GetItemDef(item.Key); if ((Object)(object)itemDef != (Object)null) { hashSet.Add(((Object)itemDef).name); } } } return hashSet; } public void Initialize(CharacterMaster minion, CharacterMaster owner) { MinionMaster = minion; OwnerMaster = owner; SummonInheritancePlugin.RegisterMinion(this); Sync(); } private void OnDestroy() { SummonInheritancePlugin.UnregisterMinion(this); } public unsafe void Sync() { //IL_00a3: 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_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: 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_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_021f: Unknown result type (might be due to invalid IL or missing references) //IL_0224: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Unknown result type (might be due to invalid IL or missing references) //IL_0229: Invalid comparison between Unknown and I4 //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0265: Unknown result type (might be due to invalid IL or missing references) //IL_0268: Invalid comparison between Unknown and I4 //IL_022b: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_0126: 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_024d: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_02bd: Unknown result type (might be due to invalid IL or missing references) //IL_02c2: Unknown result type (might be due to invalid IL or missing references) //IL_02b4: Unknown result type (might be due to invalid IL or missing references) //IL_02ba: Invalid comparison between Unknown and I4 //IL_02c7: Unknown result type (might be due to invalid IL or missing references) if (syncing || !NetworkServer.active || !ModConfig.ModEnabled.Value || (Object)(object)MinionMaster == (Object)null || (Object)(object)MinionMaster.inventory == (Object)null || (Object)(object)OwnerMaster == (Object)null || (Object)(object)OwnerMaster.inventory == (Object)null) { return; } syncing = true; try { Inventory inventory = OwnerMaster.inventory; Inventory inventory2 = MinionMaster.inventory; float num = Mathf.Clamp(ModConfig.ItemStackMultiplier.Value, 0.05f, 5f); int value = ModConfig.ItemCountCap.Value; GenericStaticEnumerable<ItemIndex, AllItemsEnumerator> allItems = ItemCatalog.allItems; AllItemsEnumerator enumerator = allItems.GetEnumerator(); try { while (((AllItemsEnumerator)(ref enumerator)).MoveNext()) { ItemIndex current = ((AllItemsEnumerator)(ref enumerator)).Current; ItemDef itemDef = ItemCatalog.GetItemDef(current); if ((Object)(object)itemDef == (Object)null) { continue; } if (IsBlacklisted(itemDef)) { if (given.TryGetValue(current, out var value2) && value2 > 0) { int itemCountPermanent = inventory2.GetItemCountPermanent(current); int num2 = Mathf.Min(value2, itemCountPermanent); if (num2 > 0) { inventory2.RemoveItemPermanent(current, num2); } given.Remove(current); } } else { if (!ModConfig.IsTierAllowed(itemDef)) { continue; } int itemCountPermanent2 = inventory.GetItemCountPermanent(current); int num3 = 0; if (itemCountPermanent2 > 0) { num3 = Mathf.Max(1, Mathf.FloorToInt((float)itemCountPermanent2 * num)); if (value > 0) { num3 = Mathf.Min(num3, value); } } int itemCountPermanent3 = inventory2.GetItemCountPermanent(current); if (itemCountPermanent3 < num3) { inventory2.GiveItemPermanent(current, num3 - itemCountPermanent3); given.TryGetValue(current, out var value3); given[current] = value3 + (num3 - itemCountPermanent3); } else if (itemCountPermanent3 > num3) { inventory2.RemoveItemPermanent(current, itemCountPermanent3 - num3); given.TryGetValue(current, out var value4); given[current] = Mathf.Max(0, value4 - (itemCountPermanent3 - num3)); } } } } finally { ((IDisposable)(*(AllItemsEnumerator*)(&enumerator))/*cast due to .constrained prefix*/).Dispose(); } if (!ModConfig.InheritEquipment.Value) { return; } EquipmentIndex currentEquipmentIndex = inventory.currentEquipmentIndex; if ((int)currentEquipmentIndex != -1 && ModConfig.IsSummonEquipment(currentEquipmentIndex)) { if (ModConfig.VerboseLogging.Value) { SummonInheritancePlugin.Log.LogInfo((object)("Blocked summon equipment from inheritance: " + ((Object)EquipmentCatalog.GetEquipmentDef(currentEquipmentIndex)).name)); } } else if ((int)currentEquipmentIndex != -1 && (((!((Object)(object)MinionMaster.bodyPrefab != (Object)null) || !((Object)MinionMaster.bodyPrefab).name.Contains("EquipmentDrone")) && !((Object)MinionMaster).name.Contains("EquipmentDrone")) || (int)inventory2.currentEquipmentIndex == -1) && inventory2.currentEquipmentIndex != currentEquipmentIndex) { inventory2.SetEquipmentIndex(currentEquipmentIndex); } } finally { syncing = false; } } private static bool IsBlacklisted(ItemDef itemDef) { if (itemDef.ContainsTag((ItemTag)13)) { return true; } if (ModConfig.IsSummonItem(itemDef)) { if (ModConfig.VerboseLogging.Value) { SummonInheritancePlugin.Log.LogInfo((object)("Blocked summon item from inheritance: " + ((Object)itemDef).name)); } return true; } if (((Object)itemDef).name == "LunarPrimaryReplacement" || ((Object)itemDef).name == "LunarSecondaryReplacement" || ((Object)itemDef).name == "LunarUtilityReplacement" || ((Object)itemDef).name == "LunarSpecialReplacement") { return true; } if ((((Object)itemDef).name == "ExtraLife" || ((Object)itemDef).name == "ExtraLifeVoid") && !ModConfig.InheritDios.Value) { return true; } if (((Object)itemDef).name == "Ghost") { return true; } if (ModConfig.IsItemBlacklisted(((Object)itemDef).name)) { return true; } return false; } } public class NoInheritMarker : MonoBehaviour { } public static class SummonWatcher { private static FieldInfo summonerBodyField; private const int MaxCulpritsToLearn = 4; private static float nextDiagTime; public static void Install() { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Expected O, but got Unknown try { MethodBase methodBase = AccessTools.Method(typeof(MasterSummon), "Perform", (Type[])null, (Type[])null); summonerBodyField = AccessTools.Field(typeof(MasterSummon), "summonerBodyObject"); if (methodBase == null || summonerBodyField == null) { SummonInheritancePlugin.Log.LogWarning((object)"SummonWatcher: MasterSummon.Perform / summonerBodyObject not found in this game version. Auto-detection disabled (name-based protection still active)."); return; } new Harmony("com.SummonInheritanceFix.Fork.watcher").Patch(methodBase, (HarmonyMethod)null, new HarmonyMethod(typeof(SummonWatcher), "PerformPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); SummonInheritancePlugin.Log.LogInfo((object)"SummonWatcher: patched MasterSummon.Perform."); } catch (Exception ex) { SummonInheritancePlugin.Log.LogWarning((object)("SummonWatcher install failed: " + ex)); } } private static void PerformPostfix(object __instance, CharacterMaster __result) { try { if (!ModConfig.ModEnabled.Value || !ModConfig.AutoDetectSummoners.Value || (Object)(object)__result == (Object)null || __instance == null) { return; } object? value = summonerBodyField.GetValue(__instance); GameObject val = (GameObject)((value is GameObject) ? value : null); if ((Object)(object)val == (Object)null) { return; } CharacterBody component = val.GetComponent<CharacterBody>(); CharacterMaster val2 = (((Object)(object)component != (Object)null) ? component.master : null); if ((Object)(object)val2 == (Object)null) { return; } MinionInheritanceSync component2 = ((Component)val2).GetComponent<MinionInheritanceSync>(); if ((Object)(object)component2 == (Object)null) { return; } if ((Object)(object)((Component)__result).GetComponent<NoInheritMarker>() == (Object)null) { ((Component)__result).gameObject.AddComponent<NoInheritMarker>(); } MinionInheritanceSync component3 = ((Component)__result).GetComponent<MinionInheritanceSync>(); if ((Object)(object)component3 != (Object)null) { Object.Destroy((Object)(object)component3); } HashSet<string> inheritedItemNames = component2.GetInheritedItemNames(); if (inheritedItemNames.Count == 0) { return; } List<string> list = new List<string>(); HashSet<string> hashSet = FindItemsReferencedByCaller(list); List<string> list2 = new List<string>(); foreach (string item in hashSet) { if (inheritedItemNames.Contains(item) && !ModConfig.IsSummonItemName(item)) { list2.Add(item); } } if (list2.Count == 0) { SummonInheritancePlugin.Log.LogInfo((object)"SummonWatcher: an inheriting minion summoned an ally, but no inherited item could be attributed. The new ally was blocked from inheriting."); if (!(Time.unscaledTime >= nextDiagTime)) { return; } nextDiagTime = Time.unscaledTime + 15f; SummonInheritancePlugin.Log.LogInfo((object)("SummonWatcher DIAG: spawned master = " + ((Object)__result).name + "; summoner master = " + ((Object)val2).name + "; inherited items = " + inheritedItemNames.Count + "; ItemDefs found in caller IL = [" + string.Join(", ", hashSet) + "]")); { foreach (string item2 in list) { SummonInheritancePlugin.Log.LogInfo((object)("SummonWatcher DIAG: frame " + item2)); } return; } } if (list2.Count > 4) { SummonInheritancePlugin.Log.LogInfo((object)("SummonWatcher: summon attribution ambiguous (" + list2.Count + " candidates), not learning. New ally was blocked from inheriting. Candidates: " + string.Join(", ", list2))); return; } foreach (string item3 in list2) { ModConfig.LearnSummonItem(item3); SummonInheritancePlugin.Log.LogInfo((object)("SummonWatcher: learned summon item '" + item3 + "' (it will no longer be inherited).")); } SummonInheritancePlugin.RequestSyncAll(); } catch (Exception ex) { SummonInheritancePlugin.Log.LogWarning((object)("SummonWatcher postfix error: " + ex)); } } private static HashSet<string> FindItemsReferencedByCaller(List<string> diag) { HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); StackTrace stackTrace = new StackTrace(fNeedFileInfo: false); int num = 0; for (int i = 0; i < stackTrace.FrameCount && i < 20; i++) { if (num >= 8) { break; } MethodBase method = stackTrace.GetFrame(i).GetMethod(); if (method == null || method.DeclaringType == null) { diag.Add("#" + i + " <unresolved>"); continue; } Assembly assembly = method.DeclaringType.Assembly; string name = assembly.GetName().Name; if (assembly == typeof(SummonWatcher).Assembly) { continue; } switch (name) { case "mscorlib": case "System": case "System.Core": case "0Harmony": case "MonoMod.Utils": case "MonoMod.RuntimeDetour": continue; } if (!name.StartsWith("UnityEngine") && !name.StartsWith("Unity.") && !name.StartsWith("DMDASM") && !name.StartsWith("DMD<") && !(method.DeclaringType == typeof(MasterSummon))) { num++; int count = hashSet.Count; CollectItemFields(method, hashSet); diag.Add("#" + i + " " + method.DeclaringType.FullName + "." + method.Name + " [" + name + "] new ItemDefs: " + (hashSet.Count - count)); } } Type itemBehaviorType = ItemBehaviorScanner.ItemBehaviorType; if (itemBehaviorType != null) { for (int j = 0; j < stackTrace.FrameCount && j < 40; j++) { MethodBase method2 = stackTrace.GetFrame(j).GetMethod(); Type type = ((method2 != null) ? method2.DeclaringType : null); while (type != null) { if (type != itemBehaviorType && itemBehaviorType.IsAssignableFrom(type)) { ItemDef val = ItemBehaviorScanner.ResolveItemDef(type); diag.Add("#" + j + " ItemBehavior " + type.FullName + " -> " + (((Object)(object)val != (Object)null) ? ((Object)val).name : "unresolved")); if ((Object)(object)val != (Object)null) { hashSet.Add(((Object)val).name); } break; } type = type.DeclaringType; } } } return hashSet; } private static void CollectItemFields(MethodBase method, HashSet<string> into) { try { MethodBody methodBody = method.GetMethodBody(); if (methodBody == null) { return; } byte[] iLAsByteArray = methodBody.GetILAsByteArray(); if (iLAsByteArray == null) { return; } Module module = method.Module; Type[] genericTypeArguments = ((method.DeclaringType != null && method.DeclaringType.IsGenericType) ? method.DeclaringType.GetGenericArguments() : null); Type[] genericMethodArguments = (method.IsGenericMethod ? method.GetGenericArguments() : null); for (int i = 0; i < iLAsByteArray.Length - 4; i++) { if (iLAsByteArray[i] != 126) { continue; } int num = BitConverter.ToInt32(iLAsByteArray, i + 1); if (num >> 24 != 4) { continue; } try { FieldInfo fieldInfo = module.ResolveField(num, genericTypeArguments, genericMethodArguments); if (fieldInfo != null && fieldInfo.IsStatic && typeof(ItemDef).IsAssignableFrom(fieldInfo.FieldType)) { object? value = fieldInfo.GetValue(null); ItemDef val = (ItemDef)((value is ItemDef) ? value : null); if ((Object)(object)val != (Object)null && !string.IsNullOrEmpty(((Object)val).name)) { into.Add(((Object)val).name); } } } catch { } } } catch { } } } }