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 FunctionalCouplerRework v1.0.4
plugins/FunctionalCouplerRework.dll
Decompiled a month agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Logging; using IL.RoR2; using Microsoft.CodeAnalysis; using Mono.Cecil; using Mono.Cecil.Cil; using MonoMod.Cil; using On.RoR2; using On.RoR2.Items; using RoR2; using RoR2.Orbs; 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("FunctionalCouplerRework")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("FunctionalCouplerRework")] [assembly: AssemblyTitle("FunctionalCouplerRework")] [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.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace FunctionalCouplerRework { public class Class1 { } internal sealed class PassiveEquipmentManager { private readonly Plugin plugin; private readonly ManualLogSource logger; private readonly Dictionary<CharacterBody, HashSet<BuffIndex>> manuallyAppliedBuffs = new Dictionary<CharacterBody, HashSet<BuffIndex>>(); private readonly HashSet<CharacterBody> pendingCollectiveRepairs = new HashSet<CharacterBody>(); private readonly HashSet<CharacterBody> pendingVoidRepairs = new HashSet<CharacterBody>(); private readonly HashSet<CharacterBody> pendingEarthRepairs = new HashSet<CharacterBody>(); private readonly HashSet<CharacterBody> pendingHauntedRepairs = new HashSet<CharacterBody>(); private readonly HashSet<CharacterBody> pendingBeadRepairs = new HashSet<CharacterBody>(); private readonly HashSet<CharacterBody> deferredInventoryChanges = new HashSet<CharacterBody>(); private int temporaryInventoryMutationDepth; internal PassiveEquipmentManager(Plugin plugin) { //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Expected O, but got Unknown //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Expected O, but got Unknown this.plugin = plugin; logger = plugin.Log; EquipmentSlot.MyFixedUpdate += new Manipulator(PatchWoodspriteFollowerRequirement); MultiShopCardUtils.OnPurchase += new hook_OnPurchase(MultiShopCardUtils_OnPurchase); } internal void OnInventoryChanged(CharacterBody body) { if (Object.op_Implicit((Object)(object)body)) { if (temporaryInventoryMutationDepth > 0) { deferredInventoryChanges.Add(body); return; } ReconcileInactiveEquipmentBuffs(body); ScheduleCollectiveRepairIfNeeded(body); ScheduleVoidRepairIfNeeded(body); ScheduleEarthRepairIfNeeded(body); ScheduleHauntedRepairIfNeeded(body); ScheduleBeadRepairIfNeeded(body); } } internal void BeginTemporaryInventoryMutation() { temporaryInventoryMutationDepth++; } internal void EndTemporaryInventoryMutation() { if (temporaryInventoryMutationDepth <= 0) { logger.LogWarning((object)"[FCR INVENTORY] Temporary inventory mutation depth underflow."); temporaryInventoryMutationDepth = 0; return; } temporaryInventoryMutationDepth--; if (temporaryInventoryMutationDepth > 0 || deferredInventoryChanges.Count == 0) { return; } CharacterBody[] array = (CharacterBody[])(object)new CharacterBody[deferredInventoryChanges.Count]; deferredInventoryChanges.CopyTo(array); deferredInventoryChanges.Clear(); foreach (CharacterBody val in array) { if (Object.op_Implicit((Object)(object)val)) { OnInventoryChanged(val); } } } internal bool TryFindStoredEquipment(Inventory inventory, EquipmentIndex targetEquipment, out uint foundSlot, out uint foundSet) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) foundSlot = 0u; foundSet = 0u; int equipmentSlotCount = inventory.GetEquipmentSlotCount(); for (uint num = 0u; num < equipmentSlotCount; num++) { int equipmentSetCount = inventory.GetEquipmentSetCount(num); for (uint num2 = 0u; num2 < equipmentSetCount; num2++) { if (inventory.GetEquipment(num, num2).equipmentIndex == targetEquipment) { foundSlot = num; foundSet = num2; return true; } } } return false; } internal void OnBodyDestroyed(CharacterBody body) { RemoveManuallyAppliedBuffs(body); pendingCollectiveRepairs.Remove(body); pendingVoidRepairs.Remove(body); pendingEarthRepairs.Remove(body); pendingHauntedRepairs.Remove(body); pendingBeadRepairs.Remove(body); deferredInventoryChanges.Remove(body); } private void ReconcileInactiveEquipmentBuffs(CharacterBody body) { //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_003e: 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_0048: 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_004d: Invalid comparison between Unknown and I4 //IL_004f: 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_0054: 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_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Invalid comparison between Unknown and I4 //IL_0080: 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_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0127: 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_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) Inventory inventory = body.inventory; if ((Object)(object)inventory == (Object)null) { return; } EquipmentIndex currentEquipmentIndex = inventory.currentEquipmentIndex; HashSet<BuffIndex> hashSet = new HashSet<BuffIndex>(); int equipmentSlotCount = inventory.GetEquipmentSlotCount(); for (uint num = 0u; num < equipmentSlotCount; num++) { int equipmentSetCount = inventory.GetEquipmentSetCount(num); for (uint num2 = 0u; num2 < equipmentSetCount; num2++) { EquipmentIndex equipmentIndex = inventory.GetEquipment(num, num2).equipmentIndex; if ((int)equipmentIndex != -1 && equipmentIndex != currentEquipmentIndex) { BuffDef val = EquipmentCatalog.GetEquipmentDef(equipmentIndex)?.passiveBuffDef; if (!((Object)(object)val == (Object)null) && (int)val.buffIndex != -1) { hashSet.Add(val.buffIndex); } } } } if (!manuallyAppliedBuffs.TryGetValue(body, out var value)) { value = new HashSet<BuffIndex>(); manuallyAppliedBuffs[body] = value; } List<BuffIndex> list = new List<BuffIndex>(); foreach (BuffIndex item in value) { if (!hashSet.Contains(item)) { list.Add(item); } } foreach (BuffIndex item2 in list) { if (body.HasBuff(item2)) { body.RemoveBuff(item2); } value.Remove(item2); } foreach (BuffIndex item3 in hashSet) { if (!value.Contains(item3)) { body.AddBuff(item3); value.Add(item3); } } if (value.Count == 0) { manuallyAppliedBuffs.Remove(body); } } private void ScheduleCollectiveRepairIfNeeded(CharacterBody body) { //IL_0015: 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_002c: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)body) && NetworkServer.active) { BuffIndex buffIndex = Buffs.EliteCollective.buffIndex; if (manuallyAppliedBuffs.TryGetValue(body, out var value) && value.Contains(buffIndex) && pendingCollectiveRepairs.Add(body)) { ((MonoBehaviour)plugin).StartCoroutine(RepairCollectiveBehaviorNextFrame(body)); } } } private IEnumerator RepairCollectiveBehaviorNextFrame(CharacterBody body) { yield return null; pendingCollectiveRepairs.Remove(body); if (!Object.op_Implicit((Object)(object)body) || !NetworkServer.active) { yield break; } Inventory inventory = body.inventory; if ((Object)(object)inventory == (Object)null || (int)Plugin.FunctionalCouplerIndex == -1 || inventory.GetItemCountEffective(Plugin.FunctionalCouplerIndex) <= 0) { yield break; } BuffIndex buffIndex = Buffs.EliteCollective.buffIndex; if (manuallyAppliedBuffs.TryGetValue(body, out var value) && value.Contains(buffIndex) && body.HasBuff(buffIndex) && !Object.op_Implicit((Object)(object)((Component)body).GetComponent<AffixCollectiveBehavior>())) { if (Object.op_Implicit((Object)(object)body.AddItemBehavior<AffixCollectiveBehavior>(1))) { logger.LogInfo((object)("[FCR COLLECTIVE] Restored missing AffixCollectiveBehavior on " + ((Object)body).name + " after deferred destruction.")); } else { logger.LogWarning((object)("[FCR COLLECTIVE] Tried to restore AffixCollectiveBehavior on " + ((Object)body).name + ", but vanilla returned null.")); } } } private void ScheduleVoidRepairIfNeeded(CharacterBody body) { //IL_0015: 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_002c: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)body) && NetworkServer.active) { BuffIndex buffIndex = Buffs.EliteVoid.buffIndex; if (manuallyAppliedBuffs.TryGetValue(body, out var value) && value.Contains(buffIndex) && pendingVoidRepairs.Add(body)) { ((MonoBehaviour)plugin).StartCoroutine(RepairVoidBehaviorNextFrame(body)); } } } private IEnumerator RepairVoidBehaviorNextFrame(CharacterBody body) { yield return null; pendingVoidRepairs.Remove(body); if (!Object.op_Implicit((Object)(object)body) || !NetworkServer.active) { yield break; } Inventory inventory = body.inventory; if ((Object)(object)inventory == (Object)null || (int)Plugin.FunctionalCouplerIndex == -1 || inventory.GetItemCountEffective(Plugin.FunctionalCouplerIndex) <= 0) { yield break; } BuffIndex buffIndex = Buffs.EliteVoid.buffIndex; if (manuallyAppliedBuffs.TryGetValue(body, out var value) && value.Contains(buffIndex) && body.HasBuff(buffIndex) && !Object.op_Implicit((Object)(object)((Component)body).GetComponent<AffixVoidBehavior>())) { if (Object.op_Implicit((Object)(object)body.AddItemBehavior<AffixVoidBehavior>(1))) { logger.LogInfo((object)("[FCR VOID] Restored missing AffixVoidBehavior on " + ((Object)body).name + " after deferred destruction.")); } else { logger.LogWarning((object)("[FCR VOID] Tried to restore AffixVoidBehavior on " + ((Object)body).name + ", but vanilla returned null.")); } } } private void ScheduleEarthRepairIfNeeded(CharacterBody body) { //IL_0015: 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_002c: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)body) && NetworkServer.active) { BuffIndex buffIndex = Buffs.EliteEarth.buffIndex; if (manuallyAppliedBuffs.TryGetValue(body, out var value) && value.Contains(buffIndex) && pendingEarthRepairs.Add(body)) { ((MonoBehaviour)plugin).StartCoroutine(RepairEarthBehaviorNextFrame(body)); } } } private IEnumerator RepairEarthBehaviorNextFrame(CharacterBody body) { yield return null; pendingEarthRepairs.Remove(body); if (!Object.op_Implicit((Object)(object)body) || !NetworkServer.active) { yield break; } Inventory inventory = body.inventory; if ((Object)(object)inventory == (Object)null || (int)Plugin.FunctionalCouplerIndex == -1 || inventory.GetItemCountEffective(Plugin.FunctionalCouplerIndex) <= 0) { yield break; } BuffIndex buffIndex = Buffs.EliteEarth.buffIndex; if (manuallyAppliedBuffs.TryGetValue(body, out var value) && value.Contains(buffIndex) && body.HasBuff(buffIndex) && !Object.op_Implicit((Object)(object)((Component)body).GetComponent<AffixEarthBehavior>())) { if (Object.op_Implicit((Object)(object)body.AddItemBehavior<AffixEarthBehavior>(1))) { logger.LogInfo((object)("[FCR EARTH] Restored missing AffixEarthBehavior on " + ((Object)body).name + " after deferred destruction.")); } else { logger.LogWarning((object)("[FCR EARTH] Tried to restore AffixEarthBehavior on " + ((Object)body).name + ", but vanilla returned null.")); } } } private void ScheduleHauntedRepairIfNeeded(CharacterBody body) { //IL_0015: 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_002c: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)body) && NetworkServer.active) { BuffIndex buffIndex = Buffs.AffixHaunted.buffIndex; if (manuallyAppliedBuffs.TryGetValue(body, out var value) && value.Contains(buffIndex) && pendingHauntedRepairs.Add(body)) { ((MonoBehaviour)plugin).StartCoroutine(RepairHauntedBehaviorNextFrame(body)); } } } private IEnumerator RepairHauntedBehaviorNextFrame(CharacterBody body) { yield return null; pendingHauntedRepairs.Remove(body); if (!Object.op_Implicit((Object)(object)body) || !NetworkServer.active) { yield break; } Inventory inventory = body.inventory; if ((Object)(object)inventory == (Object)null || (int)Plugin.FunctionalCouplerIndex == -1 || inventory.GetItemCountEffective(Plugin.FunctionalCouplerIndex) <= 0) { yield break; } BuffIndex buffIndex = Buffs.AffixHaunted.buffIndex; if (manuallyAppliedBuffs.TryGetValue(body, out var value) && value.Contains(buffIndex) && body.HasBuff(buffIndex) && !Object.op_Implicit((Object)(object)((Component)body).GetComponent<AffixHauntedBehavior>())) { if (Object.op_Implicit((Object)(object)body.AddItemBehavior<AffixHauntedBehavior>(1))) { logger.LogInfo((object)("[FCR HAUNTED] Restored missing AffixHauntedBehavior on " + ((Object)body).name + " after deferred destruction.")); } else { logger.LogWarning((object)("[FCR HAUNTED] Tried to restore AffixHauntedBehavior on " + ((Object)body).name + ", but vanilla returned null.")); } } } private void ScheduleBeadRepairIfNeeded(CharacterBody body) { //IL_0015: 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_002c: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)body) && NetworkServer.active) { BuffIndex buffIndex = Buffs.EliteBead.buffIndex; if (manuallyAppliedBuffs.TryGetValue(body, out var value) && value.Contains(buffIndex) && pendingBeadRepairs.Add(body)) { ((MonoBehaviour)plugin).StartCoroutine(RepairBeadBehaviorNextFrame(body)); } } } private IEnumerator RepairBeadBehaviorNextFrame(CharacterBody body) { yield return null; pendingBeadRepairs.Remove(body); if (!Object.op_Implicit((Object)(object)body) || !NetworkServer.active) { yield break; } Inventory inventory = body.inventory; if ((Object)(object)inventory == (Object)null || (int)Plugin.FunctionalCouplerIndex == -1 || inventory.GetItemCountEffective(Plugin.FunctionalCouplerIndex) <= 0) { yield break; } BuffIndex buffIndex = Buffs.EliteBead.buffIndex; if (manuallyAppliedBuffs.TryGetValue(body, out var value) && value.Contains(buffIndex) && body.HasBuff(buffIndex) && !Object.op_Implicit((Object)(object)((Component)body).GetComponent<AffixBeadBehavior>())) { if (Object.op_Implicit((Object)(object)body.AddItemBehavior<AffixBeadBehavior>(1))) { logger.LogInfo((object)("[FCR BEAD] Restored missing AffixBeadBehavior on " + ((Object)body).name + " after deferred destruction.")); } else { logger.LogWarning((object)("[FCR BEAD] Tried to restore AffixBeadBehavior on " + ((Object)body).name + ", but vanilla returned null.")); } } } private void MultiShopCardUtils_OnPurchase(orig_OnPurchase orig, PayCostContext context, int moneyCost) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Invalid comparison between Unknown and I4 //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_006a: 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_00d1: 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_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Expected O, but got Unknown //IL_01f7: Unknown result type (might be due to invalid IL or missing references) CharacterMaster activatorMaster = context.activatorMaster; Inventory val = (Object.op_Implicit((Object)(object)activatorMaster) ? activatorMaster.inventory : null); if ((Object)(object)val == (Object)null || val.currentEquipmentIndex == Equipment.MultiShopCard.equipmentIndex) { orig.Invoke(context, moneyCost); return; } if (!activatorMaster.hasBody || (int)Plugin.FunctionalCouplerIndex == -1 || val.GetItemCountEffective(Plugin.FunctionalCouplerIndex) <= 0 || val.GetEquipmentDisabled() || !TryFindStoredEquipment(val, Equipment.MultiShopCard.equipmentIndex, out var foundSlot, out var foundSet)) { orig.Invoke(context, moneyCost); return; } if (val.GetEquipment(foundSlot, foundSet).charges <= 0) { orig.Invoke(context, moneyCost); return; } CharacterBody body = activatorMaster.GetBody(); if (!Object.op_Implicit((Object)(object)body) || !Object.op_Implicit((Object)(object)body.equipmentSlot)) { orig.Invoke(context, moneyCost); return; } bool flag = false; if (moneyCost > 0) { flag = true; GoldOrb val2 = new GoldOrb(); GameObject purchasedObject = context.purchasedObject; Vector3? obj; if (purchasedObject == null) { obj = null; } else { Transform transform = purchasedObject.transform; obj = ((transform != null) ? new Vector3?(transform.position) : ((Vector3?)null)); } ((Orb)val2).origin = (Vector3)(((??)obj) ?? body.corePosition); ((Orb)val2).target = body.mainHurtBox; val2.goldAmount = (uint)(0.1f * (float)moneyCost); GoldOrb val3 = val2; OrbManager.instance.AddOrb((Orb)(object)val3); } GameObject purchasedObject2 = context.purchasedObject; ShopTerminalBehavior val4 = ((purchasedObject2 != null) ? purchasedObject2.GetComponent<ShopTerminalBehavior>() : null); if (Object.op_Implicit((Object)(object)val4) && Object.op_Implicit((Object)(object)val4.serverMultiShopController)) { flag = true; val4.serverMultiShopController.SetCloseOnTerminalPurchase(context.purchasedObject.GetComponent<PurchaseInteraction>(), false); } GameObject purchasedObject3 = context.purchasedObject; DroneVendorTerminalBehavior val5 = ((purchasedObject3 != null) ? purchasedObject3.GetComponent<DroneVendorTerminalBehavior>() : null); if (Object.op_Implicit((Object)(object)val5) && Object.op_Implicit((Object)(object)val5.serverMultiShopController)) { flag = true; val5.serverMultiShopController.SetCloseOnTerminalPurchase(context.purchasedObject.GetComponent<PurchaseInteraction>(), false); } if (flag) { body.equipmentSlot.OnEquipmentExecuted((byte)foundSlot, (byte)foundSet, Equipment.MultiShopCard.equipmentIndex); logger.LogInfo((object)$"[FCR CARD] Used stored Executive Card from slot {foundSlot}, set {foundSet} without touching the active equipment."); } } private void PatchWoodspriteFollowerRequirement(ILContext il) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown //IL_006c: Unknown result type (might be due to invalid IL or missing references) ILCursor val = new ILCursor(il); if (!val.TryGotoNext((MoveType)2, new Func<Instruction, bool>[2] { delegate(Instruction instruction) { //IL_0001: 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) if (instruction.OpCode == OpCodes.Ldsfld) { object operand = instruction.Operand; FieldReference val2 = (FieldReference)((operand is FieldReference) ? operand : null); if (val2 != null && ((MemberReference)val2).Name == "PassiveHealing") { return ((MemberReference)((MemberReference)val2).DeclaringType).FullName == "RoR2.RoR2Content/Equipment"; } } return false; }, (Instruction instruction) => ILPatternMatchingExt.MatchCallOrCallvirt<Object>(instruction, "op_Equality") })) { logger.LogError((object)"[FCR WOODSPRITE] Failed to locate the passive-healing follower requirement in EquipmentSlot.MyFixedUpdate. Stored Woodsprite support was not patched."); return; } val.Emit(OpCodes.Ldarg_0); val.EmitDelegate<Func<bool, EquipmentSlot, bool>>((Func<bool, EquipmentSlot, bool>)ShouldHavePassiveHealingFollower); logger.LogInfo((object)"[FCR WOODSPRITE] Patched EquipmentSlot.MyFixedUpdate so stored Woodsprite can use vanilla follower management."); } private bool ShouldHavePassiveHealingFollower(bool vanillaShouldHaveFollower, EquipmentSlot equipmentSlot) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Invalid comparison between Unknown and I4 //IL_0034: 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) if (vanillaShouldHaveFollower) { return true; } if (!Object.op_Implicit((Object)(object)equipmentSlot)) { return false; } CharacterBody characterBody = equipmentSlot.characterBody; Inventory val = ((characterBody != null) ? characterBody.inventory : null); if ((Object)(object)val == (Object)null || (int)Plugin.FunctionalCouplerIndex == -1 || val.GetItemCountEffective(Plugin.FunctionalCouplerIndex) <= 0) { return false; } uint foundSlot; uint foundSet; return TryFindStoredEquipment(val, Equipment.PassiveHealing.equipmentIndex, out foundSlot, out foundSet); } private void RemoveManuallyAppliedBuffs(CharacterBody body) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)body) || !manuallyAppliedBuffs.TryGetValue(body, out var value)) { return; } foreach (BuffIndex item in value) { if (body.HasBuff(item)) { body.RemoveBuff(item); } } manuallyAppliedBuffs.Remove(body); } } [BepInPlugin("thatguy119407.functionalcouplerrework", "Functional Coupler Rework", "1.0.4")] public class Plugin : BaseUnityPlugin { public const string PluginGUID = "thatguy119407.functionalcouplerrework"; public const string PluginName = "Functional Coupler Rework"; public const string PluginVersion = "1.0.4"; private static Plugin instance; private static ItemIndex functionalCouplerIndex = (ItemIndex)(-1); private PassiveEquipmentManager passiveEquipmentManager; private SeedReviveManager seedReviveManager; private FieldInfo itemDisplayRuleSetField; private MethodInfo instantiateDisplayRuleGroupMethod; private FieldInfo forceUpdateField; private FieldInfo collectiveFoundDisplaysField; private FieldInfo collectiveAnimatorsField; private FieldInfo collectiveHornPairsField; private bool checkedCatalog; private bool attemptedDisplayMethodResolution; private bool displayMethodsResolved; internal static Plugin Instance => instance; internal static ItemIndex FunctionalCouplerIndex => functionalCouplerIndex; internal ManualLogSource Log => ((BaseUnityPlugin)this).Logger; private void Awake() { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected O, but got Unknown //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected O, but got Unknown //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected O, but got Unknown //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Expected O, but got Unknown instance = this; passiveEquipmentManager = new PassiveEquipmentManager(this); seedReviveManager = new SeedReviveManager(passiveEquipmentManager, ((BaseUnityPlugin)this).Logger); CharacterBody.OnInventoryChanged += new hook_OnInventoryChanged(CharacterBody_OnInventoryChanged); CharacterBody.OnDestroy += new hook_OnDestroy(CharacterBody_OnDestroy); CharacterModel.OnInventoryChanged += new hook_OnInventoryChanged(CharacterModel_OnInventoryChanged); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Functional Coupler Rework v1.0.4 loaded."); CharacterMaster.OnBodyDeath += new hook_OnBodyDeath(CharacterMaster_OnBodyDeath); } private void OnDestroy() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown CharacterBody.OnInventoryChanged -= new hook_OnInventoryChanged(CharacterBody_OnInventoryChanged); CharacterBody.OnDestroy -= new hook_OnDestroy(CharacterBody_OnDestroy); CharacterModel.OnInventoryChanged -= new hook_OnInventoryChanged(CharacterModel_OnInventoryChanged); if ((Object)(object)instance == (Object)(object)this) { instance = null; } CharacterMaster.OnBodyDeath -= new hook_OnBodyDeath(CharacterMaster_OnBodyDeath); } private void CharacterBody_OnInventoryChanged(orig_OnInventoryChanged orig, CharacterBody self) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Invalid comparison between Unknown and I4 //IL_002a: Unknown result type (might be due to invalid IL or missing references) orig.Invoke(self); if (Object.op_Implicit((Object)(object)self)) { Inventory inventory = self.inventory; if (!((Object)(object)inventory == (Object)null) && (int)functionalCouplerIndex != -1 && inventory.GetItemCountEffective(functionalCouplerIndex) > 0) { passiveEquipmentManager.OnInventoryChanged(self); } } } private void CharacterModel_OnInventoryChanged(orig_OnInventoryChanged orig, CharacterModel self) { orig.Invoke(self); if (Object.op_Implicit((Object)(object)self) && Object.op_Implicit((Object)(object)self.body) && !((Object)(object)self.body.inventory == (Object)null)) { EnsureDisplayKeeper(self); } } private void EnsureDisplayKeeper(CharacterModel characterModel) { FunctionalCouplerDisplayKeeper functionalCouplerDisplayKeeper = ((Component)characterModel).GetComponent<FunctionalCouplerDisplayKeeper>(); if (!Object.op_Implicit((Object)(object)functionalCouplerDisplayKeeper)) { functionalCouplerDisplayKeeper = ((Component)characterModel).gameObject.AddComponent<FunctionalCouplerDisplayKeeper>(); } functionalCouplerDisplayKeeper.Initialize(characterModel); functionalCouplerDisplayKeeper.ReconcileImmediately(); } internal bool ShouldMaintainInactiveDisplays(CharacterModel model) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Invalid comparison between Unknown and I4 //IL_003f: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)model) || !Object.op_Implicit((Object)(object)model.body) || (Object)(object)model.body.inventory == (Object)null) { return false; } if ((int)functionalCouplerIndex == -1) { return false; } return model.body.inventory.GetItemCountEffective(functionalCouplerIndex) > 0; } internal void ReconcileInactiveDisplays(CharacterModel characterModel) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: 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_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Invalid comparison between Unknown and I4 //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: 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_00b8: 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_00f5: 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) if (!ShouldMaintainInactiveDisplays(characterModel) || !ResolveDisplayMethods()) { return; } Inventory inventory = characterModel.body.inventory; EquipmentIndex currentEquipmentIndex = inventory.currentEquipmentIndex; HashSet<EquipmentIndex> hashSet = new HashSet<EquipmentIndex>(); int equipmentSlotCount = inventory.GetEquipmentSlotCount(); for (uint num = 0u; num < equipmentSlotCount; num++) { int equipmentSetCount = inventory.GetEquipmentSetCount(num); for (uint num2 = 0u; num2 < equipmentSetCount; num2++) { EquipmentIndex equipmentIndex = inventory.GetEquipment(num, num2).equipmentIndex; if ((int)equipmentIndex != -1 && equipmentIndex != currentEquipmentIndex && !((Object)(object)EquipmentCatalog.GetEquipmentDef(equipmentIndex)?.passiveBuffDef == (Object)null)) { hashSet.Add(equipmentIndex); } } } bool flag = false; foreach (EquipmentIndex item in hashSet) { List<GameObject> equipmentDisplayObjects = characterModel.GetEquipmentDisplayObjects(item); bool flag2 = false; for (int i = 0; i < equipmentDisplayObjects.Count; i++) { if (Object.op_Implicit((Object)(object)equipmentDisplayObjects[i])) { flag2 = true; break; } } if (!flag2 && TryInstantiateEquipmentDisplay(characterModel, item)) { flag = true; } } if (flag) { ResetCollectiveDisplaySearch(characterModel.body); } } private bool ResolveDisplayMethods() { if (attemptedDisplayMethodResolution) { return displayMethodsResolved; } attemptedDisplayMethodResolution = true; BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; itemDisplayRuleSetField = typeof(CharacterModel).GetField("itemDisplayRuleSet", bindingAttr); forceUpdateField = typeof(CharacterModel).GetField("forceUpdate", bindingAttr); instantiateDisplayRuleGroupMethod = typeof(CharacterModel).GetMethod("InstantiateDisplayRuleGroup", bindingAttr, null, new Type[3] { typeof(DisplayRuleGroup), typeof(ItemIndex), typeof(EquipmentIndex) }, null); collectiveFoundDisplaysField = typeof(AffixCollectiveDisplayBehaviour).GetField("_foundDisplays", bindingAttr); collectiveAnimatorsField = typeof(AffixCollectiveDisplayBehaviour).GetField("_animatorsFromRings", bindingAttr); collectiveHornPairsField = typeof(AffixCollectiveDisplayBehaviour).GetField("_hornPairs", bindingAttr); if (itemDisplayRuleSetField == null || instantiateDisplayRuleGroupMethod == null || forceUpdateField == null) { ((BaseUnityPlugin)this).Logger.LogError((object)"[FCR DISPLAY] Could not resolve CharacterModel display members."); return false; } displayMethodsResolved = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)"[FCR DISPLAY] Successfully resolved display members."); return true; } private bool TryInstantiateEquipmentDisplay(CharacterModel characterModel, EquipmentIndex equipmentIndex) { //IL_0233: 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_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_020e: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_007d: 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_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) try { object? value = itemDisplayRuleSetField.GetValue(characterModel); ItemDisplayRuleSet val = (ItemDisplayRuleSet)((value is ItemDisplayRuleSet) ? value : null); if (!Object.op_Implicit((Object)(object)val)) { return false; } DisplayRuleGroup equipmentDisplayRuleGroup = val.GetEquipmentDisplayRuleGroup(equipmentIndex); if (((DisplayRuleGroup)(ref equipmentDisplayRuleGroup)).isEmpty) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[FCR DISPLAY] Empty display rule group for " + DescribeEquipment(equipmentIndex) + ".")); return false; } instantiateDisplayRuleGroupMethod.Invoke(characterModel, new object[3] { equipmentDisplayRuleGroup, (object)(ItemIndex)(-1), equipmentIndex }); List<GameObject> equipmentDisplayObjects = characterModel.GetEquipmentDisplayObjects(equipmentIndex); int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; for (int i = 0; i < equipmentDisplayObjects.Count; i++) { GameObject val2 = equipmentDisplayObjects[i]; if (!Object.op_Implicit((Object)(object)val2)) { continue; } num++; val2.SetActive(true); ItemDisplay component = val2.GetComponent<ItemDisplay>(); if (!Object.op_Implicit((Object)(object)component)) { continue; } num2++; component.SetVisibilityLevel(characterModel.visibility); component.RefreshRenderers(); if (component.rendererInfos == null) { continue; } for (int j = 0; j < component.rendererInfos.Length; j++) { Renderer renderer = component.rendererInfos[j].renderer; if (Object.op_Implicit((Object)(object)renderer)) { num3++; if (renderer.enabled) { num4++; } } } } forceUpdateField.SetValue(characterModel, true); ((BaseUnityPlugin)this).Logger.LogInfo((object)("[FCR DISPLAY] Added inactive display for " + DescribeEquipment(equipmentIndex) + "; " + $"objects={num}, ItemDisplays={num2}, " + $"renderers={num3}, enabledRenderers={num4}, " + $"modelVisibility={characterModel.visibility}.")); return num > 0; } catch (TargetInvocationException ex) { Exception arg = ex.InnerException ?? ex; ((BaseUnityPlugin)this).Logger.LogError((object)$"[FCR DISPLAY] Failed to instantiate {DescribeEquipment(equipmentIndex)}:\n{arg}"); return false; } catch (Exception arg2) { ((BaseUnityPlugin)this).Logger.LogError((object)$"[FCR DISPLAY] Failed to instantiate {DescribeEquipment(equipmentIndex)}:\n{arg2}"); return false; } } private void ResetCollectiveDisplaySearch(CharacterBody body) { if (!Object.op_Implicit((Object)(object)body) || collectiveFoundDisplaysField == null) { return; } AffixCollectiveDisplayBehaviour[] array = Object.FindObjectsOfType<AffixCollectiveDisplayBehaviour>(); foreach (AffixCollectiveDisplayBehaviour val in array) { if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val.affixCollectiveAttachmentBehaviour) && Object.op_Implicit((Object)(object)val.affixCollectiveAttachmentBehaviour.attachment) && !((Object)(object)val.affixCollectiveAttachmentBehaviour.attachment.attachedBody != (Object)(object)body)) { collectiveFoundDisplaysField.SetValue(val, false); object obj = collectiveAnimatorsField?.GetValue(val); obj?.GetType().GetMethod("Clear")?.Invoke(obj, null); object obj2 = collectiveHornPairsField?.GetValue(val); obj2?.GetType().GetMethod("Clear")?.Invoke(obj2, null); ((BaseUnityPlugin)this).Logger.LogInfo((object)"[FCR DISPLAY] Reset AffixCollectiveDisplayBehaviour so it can discover the newly added inactive displays."); } } } private string DescribeEquipment(EquipmentIndex equipmentIndex) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) EquipmentDef equipmentDef = EquipmentCatalog.GetEquipmentDef(equipmentIndex); if (!((Object)(object)equipmentDef == (Object)null)) { return $"{((Object)equipmentDef).name} [{equipmentIndex}]"; } return $"UnknownEquipment({equipmentIndex})"; } private void CharacterMaster_OnBodyDeath(orig_OnBodyDeath orig, CharacterMaster self, CharacterBody body) { if (seedReviveManager == null) { orig.Invoke(self, body); } else { seedReviveManager.OnBodyDeath(orig, self, body); } } private void CharacterBody_OnDestroy(orig_OnDestroy orig, CharacterBody self) { passiveEquipmentManager?.OnBodyDestroyed(self); orig.Invoke(self); } private void Update() { //IL_0015: 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_0025: Invalid comparison between Unknown and I4 //IL_003a: Unknown result type (might be due to invalid IL or missing references) if (!checkedCatalog && ItemCatalog.itemCount != 0) { functionalCouplerIndex = Items.ExtraEquipment.itemIndex; if ((int)functionalCouplerIndex != -1) { checkedCatalog = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Functional Coupler catalog index resolved: {functionalCouplerIndex}"); } } } } internal sealed class FunctionalCouplerDisplayKeeper : MonoBehaviour { private CharacterModel characterModel; private float nextReconcileTime; internal void Initialize(CharacterModel model) { characterModel = model; } internal void ReconcileImmediately() { nextReconcileTime = 0f; Reconcile(); } private void Update() { if (!(Time.unscaledTime < nextReconcileTime)) { Reconcile(); } } private void Reconcile() { nextReconcileTime = Time.unscaledTime + 0.5f; if (!Object.op_Implicit((Object)(object)characterModel)) { Object.Destroy((Object)(object)this); return; } Plugin instance = Plugin.Instance; if ((Object)(object)instance == (Object)null) { Object.Destroy((Object)(object)this); } else if (!instance.ShouldMaintainInactiveDisplays(characterModel)) { Object.Destroy((Object)(object)this); } else { instance.ReconcileInactiveDisplays(characterModel); } } } internal sealed class SeedReviveManager { private readonly PassiveEquipmentManager passiveEquipmentManager; private readonly ManualLogSource logger; internal SeedReviveManager(PassiveEquipmentManager passiveEquipmentManager, ManualLogSource logger) { this.passiveEquipmentManager = passiveEquipmentManager; this.logger = logger; } internal void OnBodyDeath(orig_OnBodyDeath orig, CharacterMaster master, CharacterBody body) { //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Invalid comparison between Unknown and I4 //IL_0034: 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_0054: 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_005f: 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_0066: 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_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: 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_0102: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0155: 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_015a: Unknown result type (might be due to invalid IL or missing references) Inventory val = (Object.op_Implicit((Object)(object)master) ? master.inventory : null); if (!NetworkServer.active || (Object)(object)val == (Object)null || (Object)(object)body == (Object)null || (int)Plugin.FunctionalCouplerIndex == -1 || val.GetItemCountEffective(Plugin.FunctionalCouplerIndex) <= 0) { orig.Invoke(master, body); return; } EquipmentIndex equipmentIndex = Equipment.HealAndRevive.equipmentIndex; EquipmentIndex equipmentIndex2 = Equipment.HealAndReviveConsumed.equipmentIndex; if (val.currentEquipmentIndex == equipmentIndex) { orig.Invoke(master, body); return; } if (!passiveEquipmentManager.TryFindStoredEquipment(val, equipmentIndex, out var foundSlot, out var foundSet)) { orig.Invoke(master, body); return; } uint activeEquipmentSlot = val.activeEquipmentSlot; if (val.activeEquipmentSet == null || val.activeEquipmentSet.Length <= activeEquipmentSlot) { logger.LogWarning((object)"[FCR SEED] Could not resolve the active equipment set. Vanilla death handling will run unchanged."); orig.Invoke(master, body); return; } uint num = val.activeEquipmentSet[activeEquipmentSlot]; if (foundSlot == activeEquipmentSlot && foundSet == num) { orig.Invoke(master, body); return; } EquipmentState equipment = val.GetEquipment(activeEquipmentSlot, num); EquipmentState equipment2 = val.GetEquipment(foundSlot, foundSet); if (equipment2.equipmentIndex != equipmentIndex) { orig.Invoke(master, body); return; } EquipmentState val2 = equipment2; bool flag = false; passiveEquipmentManager.BeginTemporaryInventoryMutation(); try { val.SetEquipment(equipment2, activeEquipmentSlot, num); val.SetEquipment(equipment, foundSlot, foundSet); orig.Invoke(master, body); flag = true; EquipmentState equipment3 = val.GetEquipment(activeEquipmentSlot, num); if (equipment3.equipmentIndex == equipmentIndex2) { val2 = equipment3; logger.LogInfo((object)("[FCR SEED] Vanilla consumed stored Seed of Life from " + $"slot {foundSlot}, set {foundSet} to revive {((Object)body).name}.")); } } finally { try { val.SetEquipment(equipment, activeEquipmentSlot, num); val.SetEquipment(val2, foundSlot, foundSet); } finally { passiveEquipmentManager.EndTemporaryInventoryMutation(); } } if (!flag) { logger.LogWarning((object)"[FCR SEED] Vanilla death handling did not complete normally; the temporary equipment swap was rolled back."); } } } }