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 Void Crest Compatibility v1.0.0
VoidCrestMovesetCompat.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GlobalEnums; using GlobalSettings; using HarmonyLib; using HutongGames.PlayMaker; using HutongGames.PlayMaker.Actions; using Microsoft.CodeAnalysis; using Needleforge; using Needleforge.Attacks; using Needleforge.Data; using Silksong.ModMenu.Elements; using Silksong.ModMenu.Models; using Silksong.ModMenu.Plugin; using UnityEngine; using VoidCrest; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("ManthisCo")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright © Erymanthis 2026")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("VoidCrestMovesetCompat")] [assembly: AssemblyTitle("VoidCrestMovesetCompat")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace VoidCrestMovesetCompat { [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("io.github.erymanthis.voidcrestmovesetcompat", "VoidCrestMovesetCompat", "1.0.0")] public sealed class Plugin : BaseUnityPlugin { private sealed class ReaperOrbSpawnState { public readonly List<GameObject> SpawnedObjects; public readonly int StartIndex; public readonly bool ShouldVoidify; public ReaperOrbSpawnState(List<GameObject> spawnedObjects, int startIndex, bool shouldVoidify) { SpawnedObjects = spawnedObjects; StartIndex = startIndex; ShouldVoidify = shouldVoidify; } } [HarmonyPatch(typeof(HeroController), "AddHealth", new Type[] { typeof(int) })] private static class ShamanNormalBindHealingPatch { [HarmonyPrefix] private static bool Prefix() { return !shamanNormalBindInProgress; } } [HarmonyPatch] private static class MovesetMakerInitializePatch { private static MethodBase? TargetMethod() { return AccessTools.Method(AccessTools.TypeByName("Needleforge.Makers.MovesetMaker"), "InitializeMoveset", (Type[])null, (Type[])null); } [HarmonyPostfix] private static void Postfix(MovesetData moveset) { try { ApplyBaseMoveset(moveset); } catch (Exception arg) { Log.Error($"Failed to apply VoidCrest moveset compatibility patch: {arg}"); } } } [HarmonyPatch(typeof(HeroController), "Start")] private static class HeroControllerStartPreparePatch { [HarmonyPrefix] private static void Prefix() { try { PrepareVoidHeroConfigForStart(); } catch (Exception arg) { Log.Error($"Failed preparing VoidCrest hero config before HeroController.Start: {arg}"); } } } [HarmonyPatch(typeof(HeroController), "Start")] private static class HeroControllerStartLogPatch { [HarmonyPostfix] private static void Postfix(HeroController __instance) { if (ShouldApplyCompat()) { string activeBaseCrest = GetActiveBaseCrest(); LogHeroState(__instance, activeBaseCrest); } } } [HarmonyPatch(typeof(HeroController), "ResetAllCrestState", new Type[] { typeof(bool) })] private static class HeroControllerResetAllCrestStatePatch { [HarmonyPostfix] private static void Postfix(HeroController __instance) { try { ReapplyVoidConfigAfterReset(__instance); } catch (Exception arg) { Log.Error($"Failed reapplying VoidCrest config after ResetAllCrestState: {arg}"); } } } [HarmonyPatch(typeof(HeroController), "UpdateConfig")] private static class HeroControllerUpdateConfigPatch { [HarmonyPostfix] private static void Postfix(HeroController __instance) { try { ForceResolvedBaseConfigGroup(__instance); } catch (Exception arg) { Log.Error($"Failed resolving the selected Void base crest after UpdateConfig: {arg}"); } } } [HarmonyPatch] private static class VanillaBaseCrestFunctionPatch { private static IEnumerable<MethodBase> TargetMethods() { MethodBase[] array = new MethodBase[13] { AccessTools.PropertyGetter(typeof(HeroController), "IsWandererLucky"), AccessTools.Method(typeof(HeroController), "Attack", new Type[1] { typeof(AttackDirection) }, (Type[])null), AccessTools.Method(typeof(HeroController), "NailHitEnemy", (Type[])null, (Type[])null), AccessTools.Method(typeof(HeroController), "BindCompleted", (Type[])null, (Type[])null), AccessTools.Method(typeof(HeroController), "Recoil", new Type[2] { typeof(bool), typeof(bool) }, (Type[])null), AccessTools.Method(typeof(HeroController), "RecoilDown", (Type[])null, (Type[])null), AccessTools.Method(typeof(HeroController), "FallCheck", (Type[])null, (Type[])null), AccessTools.Method(typeof(DamageEnemies), "DoDamage", new Type[2] { typeof(GameObject), typeof(bool) }, (Type[])null), AccessTools.Method(typeof(HealthManager), "TakeDamage", new Type[1] { typeof(HitInstance) }, (Type[])null), AccessTools.Method(typeof(HeroAnimationController), "UpdateToolEquipFlags", (Type[])null, (Type[])null), AccessTools.Method(typeof(SurfaceWaterRegion), "OnTriggerEnter2D", new Type[1] { typeof(Collider2D) }, (Type[])null), AccessTools.Method(typeof(BouncePod), "WillRespond", new Type[1] { typeof(HitInstance) }, (Type[])null), AccessTools.Method(typeof(BouncePod), "Hit", new Type[1] { typeof(HitInstance) }, (Type[])null) }; MethodBase?[] array2 = array; foreach (MethodBase methodBase in array2) { if (methodBase != null) { yield return methodBase; } } } private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, MethodBase __originalMethod) { MethodInfo isEquippedGetter = AccessTools.PropertyGetter(typeof(ToolBase), "IsEquipped"); MethodInfo crestProxy = AccessTools.Method(typeof(Plugin), "IsBaseCrestFunctionallyEquipped", (Type[])null, (Type[])null); MethodInfo stringEquality = AccessTools.Method(typeof(string), "op_Equality", new Type[2] { typeof(string), typeof(string) }, (Type[])null); MethodInfo stringProxy = AccessTools.Method(typeof(Plugin), "BaseAwareStringEquals", (Type[])null, (Type[])null); MethodInfo wandererCritChanceGetter = AccessTools.PropertyGetter(typeof(Gameplay), "WandererCritChance"); MethodInfo wandererCritChanceProxy = AccessTools.Method(typeof(Plugin), "GetWandererCritChance", (Type[])null, (Type[])null); MethodInfo floatRandomRange = AccessTools.Method(typeof(Random), "Range", new Type[2] { typeof(float), typeof(float) }, (Type[])null); MethodInfo trackedRandomRangeProxy = AccessTools.Method(typeof(Plugin), "GetTrackedWandererRandomRange", (Type[])null, (Type[])null); int crestChecksReplaced = 0; int stringChecksReplaced = 0; int critChanceChecksReplaced = 0; int floatRandomCallsSeen = 0; foreach (CodeInstruction instruction in instructions) { if (isEquippedGetter != null && CodeInstructionExtensions.Calls(instruction, isEquippedGetter)) { crestChecksReplaced++; yield return CodeInstructionExtensions.MoveBlocksFrom(CodeInstructionExtensions.MoveLabelsFrom(new CodeInstruction(OpCodes.Call, (object)crestProxy), instruction), instruction); } else if (CodeInstructionExtensions.Calls(instruction, stringEquality)) { stringChecksReplaced++; yield return CodeInstructionExtensions.MoveBlocksFrom(CodeInstructionExtensions.MoveLabelsFrom(new CodeInstruction(OpCodes.Call, (object)stringProxy), instruction), instruction); } else if (__originalMethod.DeclaringType == typeof(DamageEnemies) && wandererCritChanceGetter != null && CodeInstructionExtensions.Calls(instruction, wandererCritChanceGetter)) { critChanceChecksReplaced++; yield return CodeInstructionExtensions.MoveBlocksFrom(CodeInstructionExtensions.MoveLabelsFrom(new CodeInstruction(OpCodes.Call, (object)wandererCritChanceProxy), instruction), instruction); } else if (__originalMethod.DeclaringType == typeof(DamageEnemies) && floatRandomRange != null && CodeInstructionExtensions.Calls(instruction, floatRandomRange) && floatRandomCallsSeen++ == 0) { yield return CodeInstructionExtensions.MoveBlocksFrom(CodeInstructionExtensions.MoveLabelsFrom(new CodeInstruction(OpCodes.Call, (object)trackedRandomRangeProxy), instruction), instruction); } else { yield return instruction; } } Log.Info("Restored vanilla base-crest checks in " + __originalMethod.DeclaringType?.Name + "." + __originalMethod.Name + ": " + $"equipped={crestChecksReplaced}, id={stringChecksReplaced}, wandererChance={critChanceChecksReplaced}."); } } [HarmonyPatch(typeof(DamageEnemies), "DoDamage", new Type[] { typeof(GameObject), typeof(bool) })] private static class CrestDamageCompatibilityPatch { [HarmonyPrefix] private static void Prefix(DamageEnemies __instance, GameObject target, ref float __state) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Invalid comparison between Unknown and I4 __state = __instance.DamageMultiplier; ToolItem representingTool = __instance.RepresentingTool; if ((Object)(object)representingTool != (Object)null && (int)representingTool.Type == 3 && ShouldApplyShamanSpellDamageBonus()) { __instance.DamageMultiplier = __state * Gameplay.SpellCrestRuneDamageMult; } if (ShouldApplyHunterVoidmassDamage(__instance)) { float num = ((PlayerData.instance.health <= 2) ? 0.35f : 0.25f); float num2 = 1f + (float)GetCurrentVoidmass() * num; if (IsNamedBoss(target)) { float currentHunterFocusMultiplier = GetCurrentHunterFocusMultiplier(); num2 = Mathf.Min(num2, 3f / Mathf.Max(1f, currentHunterFocusMultiplier)); } __instance.DamageMultiplier *= num2; } } [HarmonyPostfix] private static void Postfix(DamageEnemies __instance, float __state) { __instance.DamageMultiplier = __state; } [HarmonyFinalizer] private static Exception? Finalizer(DamageEnemies __instance, float __state, Exception? __exception) { __instance.DamageMultiplier = __state; return __exception; } } [HarmonyPatch(typeof(CallMethodProper), "OnEnter")] private static class ReaperBundlePickupPatch { [HarmonyPrefix] private static bool Prefix(CallMethodProper __instance) { if (!(PlayerData.instance?.CurrentCrestID != "Void") && IsSelectedBaseCrest("Reaper")) { FsmString methodName = __instance.methodName; if (string.Equals((methodName != null) ? methodName.Value : null, "AddSilkParts", StringComparison.Ordinal)) { FsmString behaviour = __instance.behaviour; if (string.Equals((behaviour != null) ? behaviour.Value : null, "HeroController", StringComparison.Ordinal) && IsReaperBundleObject(((FsmStateAction)__instance).Owner)) { AwardReaperOrbVoidmass(); ((FsmStateAction)__instance).Finish(); return false; } } } return true; } } [HarmonyPatch(typeof(FlingUtils), "SpawnAndFling", new Type[] { typeof(Config), typeof(Transform), typeof(Vector3), typeof(List<GameObject>), typeof(float) })] private static class ReaperBundleAppearancePatch { [HarmonyPrefix] private static void Prefix(Config config, ref List<GameObject>? addToList, out ReaperOrbSpawnState? __state) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) GameObject reaperBundlePrefab = Gameplay.ReaperBundlePrefab; if (!Object.op_Implicit((Object)(object)reaperBundlePrefab) || config.Prefab != reaperBundlePrefab) { __state = null; return; } if (addToList == null) { addToList = new List<GameObject>(); } bool shouldVoidify = PlayerData.instance?.CurrentCrestID == "Void" && IsSelectedBaseCrest("Reaper"); __state = new ReaperOrbSpawnState(addToList, addToList.Count, shouldVoidify); } [HarmonyPostfix] private static void Postfix(ReaperOrbSpawnState? __state) { if (__state == null) { return; } for (int i = __state.StartIndex; i < __state.SpawnedObjects.Count; i++) { GameObject val = __state.SpawnedObjects[i]; if (Object.op_Implicit((Object)(object)val)) { if (__state.ShouldVoidify) { VoidCrestColorUtil.Voidify(val); } else { VoidCrestColorUtil.UnvoidifyRoot(val); } } } } } [HarmonyPatch(typeof(HeroController), "TakeDamage", new Type[] { typeof(GameObject), typeof(CollisionSide), typeof(int), typeof(HazardType), typeof(DamagePropertyFlags) })] [HarmonyBefore(new string[] { "io.dervoce.voidcrest" })] [HarmonyPriority(800)] private static class ReaperRetaliationVoidmassGuardPatch { [HarmonyPrefix] private static void Prefix(HeroController __instance, out bool __state) { __state = suppressReaperRetaliationVoidmassGain; suppressReaperRetaliationVoidmassGain = PlayerData.instance?.CurrentCrestID == "Void" && IsSelectedBaseCrest("Reaper") && __instance.cState.isBinding; } [HarmonyFinalizer] private static Exception? Finalizer(bool __state, Exception? __exception) { suppressReaperRetaliationVoidmassGain = __state; return __exception; } } [HarmonyPatch] private static class VoidmassResetClearsReaperFractionPatch { private static MethodBase TargetMethod() { return VoidMassProperty?.GetSetMethod(nonPublic: true) ?? throw new MissingMethodException("VoidCrest.voidspool.voidMass setter was not found."); } [HarmonyPrefix] private static void Prefix(ref int value) { if (suppressReaperRetaliationVoidmassGain) { int currentVoidmass = GetCurrentVoidmass(); if (value > currentVoidmass) { value = currentVoidmass; } suppressReaperRetaliationVoidmassGain = false; } } [HarmonyPostfix] private static void Postfix(int value) { if (value <= 0 || value >= 10) { ResetReaperFractionalVoidmass((value <= 0) ? "Voidmass reset" : "Voidmass cap reached"); } } } [HarmonyPatch(typeof(SilkSpool), "ChangeSilk")] [HarmonyAfter(new string[] { "io.dervoce.voidcrest" })] [HarmonyPriority(0)] private static class ReaperFractionalVoidmassSpoolPatch { [HarmonyPrefix] private static void Prefix(SilkSpool __instance, ref int silk, ref int silkParts) { if (IsVoidmassSpool(__instance) && ShouldDisplayReaperHalfVoidmass()) { silk = Math.Min(GetCurrentVoidmass() + 1, 10); silkParts = 0; } } [HarmonyPostfix] private static void Postfix(SilkSpool __instance) { if (IsVoidmassSpool(__instance)) { ApplyReaperFractionalPipOpacity(__instance, ShouldDisplayReaperHalfVoidmass()); } } } [HarmonyPatch] private static class VanillaBaseCrestQueryPatch { private static IEnumerable<MethodBase> TargetMethods() { yield return AccessTools.Method(typeof(HeroController), "IsHunterCrestEquipped", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(HeroController), "IsArchitectCrestEquipped", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(HeroController), "IsShamanCrestEquipped", (Type[])null, (Type[])null); } [HarmonyPostfix] private static void Postfix(MethodBase __originalMethod, ref bool __result) { if (!__result && !(PlayerData.instance?.CurrentCrestID != "Void")) { __result = __originalMethod.Name switch { "IsHunterCrestEquipped" => IsHunterBaseSelected(), "IsArchitectCrestEquipped" => IsSelectedBaseCrest("Architect", "Toolmaster"), "IsShamanCrestEquipped" => IsSelectedBaseCrest("Shaman", "Spell"), _ => false, }; } } } [HarmonyPatch(typeof(CheckIfCrestEquipped), "get_IsTrue")] private static class CrestAttackAudioFsmCheckPatch { [HarmonyPostfix] private static void Postfix(CheckIfCrestEquipped __instance, ref bool __result) { if (__result || PlayerData.instance?.CurrentCrestID != "Void") { return; } HeroController instance = HeroController.instance; FsmObject crest = __instance.Crest; Object obj = ((crest != null) ? crest.Value : null); ToolCrest val = (ToolCrest)(object)((obj is ToolCrest) ? obj : null); if ((Object)(object)instance == (Object)null || (Object)(object)val == (Object)null || !DoesSelectedBaseRepresentCrest(val.name)) { return; } Fsm fsm = ((FsmStateAction)__instance).Fsm; PlayMakerFSM spellControl = instance.spellControl; if (fsm == ((spellControl != null) ? spellControl.Fsm : null) && string.Equals(val.name, "Spell", StringComparison.Ordinal) && IsSelectedBaseCrest("Spell", "Shaman") && !VoidCrestPlugin.doingSpecialAttack) { __result = true; FsmState state = ((FsmStateAction)__instance).State; string text = ((state != null) ? state.Name : null) ?? "<unknown>"; string item = "ShamanBindFSM:" + text; if (LoggedDashAliases.Add(item)) { Log.Info("Restored Shaman grounded Bind branch in FSM 'Spell Control', state '" + text + "'."); } return; } PlayMakerFSM crestAttacksFSM = instance.crestAttacksFSM; if (fsm != ((crestAttacksFSM != null) ? crestAttacksFSM.Fsm : null)) { PlayMakerFSM silkSpecialFSM = instance.silkSpecialFSM; if (fsm != ((silkSpecialFSM != null) ? silkSpecialFSM.Fsm : null) && !string.Equals((fsm != null) ? fsm.Name : null, "Nail Arts", StringComparison.Ordinal)) { return; } } __result = true; string text2 = ((fsm != null) ? fsm.Name : null) ?? "<unknown>"; FsmState state2 = ((FsmStateAction)__instance).State; string text3 = ((state2 != null) ? state2.Name : null) ?? "<unknown>"; string item2 = "AudioFSM:" + text2 + ":" + text3 + ":" + val.name; if (LoggedDashAliases.Add(item2)) { Log.Info("Restored selected crest audio branch in FSM '" + text2 + "', state '" + text3 + "', crest '" + val.name + "'."); } } } [HarmonyPatch(typeof(HeroAnimationController), "GetClip")] private static class HeroAnimationControllerGetClipPatch { [HarmonyPrefix] private static bool Prefix(HeroAnimationController __instance, string clipName, ref tk2dSpriteAnimationClip __result) { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Expected O, but got Unknown if (PlayerData.instance?.CurrentCrestID != "Void") { return true; } string text = ((clipName == "Shadow Dash") ? "Dash" : ((!(clipName == "Shadow Dash Down")) ? null : "Dash Down")); string text2 = text; if (text2 == null) { return true; } tk2dSpriteAnimationClip clip = __instance.GetClip(text2); if (clip == null) { return true; } __result = new tk2dSpriteAnimationClip(clip) { name = clipName }; if (LoggedDashAliases.Add(clipName)) { Log.Info("Aliased missing animation '" + clipName + "' to '" + text2 + "' while preserving clip playback state."); } return false; } } [HarmonyPatch(typeof(NailSlash), "StartSlash")] private static class NailSlashStartPatch { [HarmonyPostfix] private static void Postfix(NailSlash __instance) { try { RefreshLiveSlashVisuals((Component?)(object)__instance); } catch (Exception arg) { Log.Error($"Failed refreshing NailSlash visuals: {arg}"); } } } [HarmonyPatch(typeof(NailAttackBase), "OnSlashStarting")] private static class NailAttackBaseSlashStartingPatch { [HarmonyPostfix] private static void Postfix(NailAttackBase __instance) { try { RefreshLiveSlashVisuals((Component?)(object)__instance); } catch (Exception arg) { Log.Error($"Failed refreshing base nail attack visuals: {arg}"); } } } [HarmonyPatch(typeof(NailAttackBase), "SetNailImbuement")] private static class NailAttackBaseImbuementPatch { [HarmonyPostfix] private static void Postfix(NailAttackBase __instance) { try { RefreshLiveSlashVisuals((Component?)(object)__instance); } catch (Exception arg) { Log.Error($"Failed hijacking nail imbuement tint: {arg}"); } } } [HarmonyPatch(typeof(Downspike), "StartSlash")] private static class DownspikeStartPatch { [HarmonyPostfix] private static void Postfix(Downspike __instance) { try { RefreshLiveSlashVisuals((Component?)(object)__instance); } catch (Exception arg) { Log.Error($"Failed refreshing Downspike visuals: {arg}"); } } } [HarmonyPatch(typeof(HeroExtraNailSlash), "OnEnable")] private static class HeroExtraNailSlashEnablePatch { [HarmonyPostfix] private static void Postfix(HeroExtraNailSlash __instance) { try { LogHeroExtraNailSlashEnable(__instance); RefreshLiveSlashVisuals((Component?)(object)__instance); } catch (Exception arg) { Log.Error($"Failed refreshing HeroExtraNailSlash visuals: {arg}"); } } } [HarmonyPatch(typeof(HeroExtraNailSlash), "OnEnable")] private static class HeroExtraNailSlashChargeLogResetPatch { [HarmonyPostfix] private static void Postfix(HeroExtraNailSlash __instance) { try { if ((Object)(object)__instance != (Object)null && IsChargeSlashObject(((Component)__instance).gameObject)) { liveSlashRetintLogCount = 0; Log.Info("Reset retint target logging for ChargeSlash."); } } catch (Exception arg) { Log.Error($"Failed resetting ChargeSlash retint log window: {arg}"); } } } private sealed class ManualLogSourceAdapter { private readonly ManualLogSource source; public ManualLogSourceAdapter(ManualLogSource source) { this.source = source; } public void Info(string message) { source.LogInfo((object)message); } public void Warning(string message) { source.LogWarning((object)message); } public void Error(string message) { source.LogError((object)message); } } private sealed class LiveSlashVisualRetint : MonoBehaviour { private readonly List<tk2dBaseSprite> tk2dSprites = new List<tk2dBaseSprite>(); private readonly List<SpriteRenderer> spriteRenderers = new List<SpriteRenderer>(); private int framesRemaining; private Color targetColor = Color.white; public void Configure(Color color, int frames) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) targetColor = color; framesRemaining = Mathf.Max(framesRemaining, frames); CacheTargets(); Apply(); } private void LateUpdate() { if (framesRemaining <= 0 || !((Behaviour)this).isActiveAndEnabled) { Object.Destroy((Object)(object)this); return; } Apply(); framesRemaining--; } private void CacheTargets() { tk2dSprites.Clear(); spriteRenderers.Clear(); tk2dBaseSprite[] componentsInChildren = ((Component)this).GetComponentsInChildren<tk2dBaseSprite>(true); foreach (tk2dBaseSprite item in componentsInChildren) { tk2dSprites.Add(item); } SpriteRenderer[] componentsInChildren2 = ((Component)this).GetComponentsInChildren<SpriteRenderer>(true); foreach (SpriteRenderer item2 in componentsInChildren2) { spriteRenderers.Add(item2); } } private void Apply() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) foreach (tk2dBaseSprite tk2dSprite in tk2dSprites) { if (Object.op_Implicit((Object)(object)tk2dSprite)) { LogLiveRetintTarget(tk2dSprite); tk2dSprite.color = targetColor; } } foreach (SpriteRenderer spriteRenderer in spriteRenderers) { if (Object.op_Implicit((Object)(object)spriteRenderer)) { spriteRenderer.color = targetColor; } } } } [CompilerGenerated] private static class <>O { public static EventHandler <0>__OnOrangeHornetShaderChanged; public static MenuElementGenerator <1>__GenerateVoidCrestBaseCrestMenuElement; public static Func<char, bool> <2>__IsLetterOrDigit; public static Func<char, char> <3>__ToLowerInvariant; } private static readonly BindingFlags InstanceFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; private static readonly BindingFlags StaticFlags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; private const string VoidCrestConfigFileName = "io.dervoce.voidcrest.cfg"; private const string VoidCrestBaseCrestSection = "General"; private const string VoidCrestBaseCrestKey = "Base Crest"; private static readonly FieldInfo? HeroConfigsField = typeof(HeroController).GetField("configs", InstanceFlags); private static readonly FieldInfo? ToolCrestHeroConfigField = typeof(ToolCrest).GetField("heroConfig", InstanceFlags); private static readonly FieldInfo? CrestConfigField = typeof(HeroController).GetField("crestConfig", InstanceFlags); private static readonly FieldInfo? DashFsmEditField = typeof(HeroConfigNeedleforge).GetField("_dashFsm", InstanceFlags); private static readonly FieldInfo? DownSlashTypeField = typeof(HeroControllerConfig).GetField("downSlashType", InstanceFlags); private static readonly FieldInfo? DownSlashEventField = typeof(HeroControllerConfig).GetField("downSlashEvent", InstanceFlags); private static readonly PropertyInfo? CurrentConfigGroupProperty = typeof(HeroController).GetProperty("CurrentConfigGroup", InstanceFlags); private static readonly MethodInfo? UpdateConfigMethod = typeof(HeroController).GetMethod("UpdateConfig", InstanceFlags); private static readonly MethodInfo? SetConfigGroupMethod = typeof(HeroController).GetMethod("SetConfigGroup", InstanceFlags); private static readonly Type? VoidSpoolType = AccessTools.TypeByName("VoidCrest.voidspool"); private static readonly FieldInfo? VoidMassField = VoidSpoolType?.GetField("_voidMass", StaticFlags); private static readonly PropertyInfo? VoidMassProperty = VoidSpoolType?.GetProperty("voidMass", StaticFlags); private static readonly FieldInfo? VoidSpoolCloneField = VoidSpoolType?.GetField("spoolClone", StaticFlags); private static readonly FieldInfo? SilkChunksField = typeof(SilkSpool).GetField("silkChunks", InstanceFlags); private static readonly FieldInfo? HunterUpgStateField = typeof(HeroController).GetField("hunterUpgState", InstanceFlags); private static readonly FieldInfo? NormalSlashField = typeof(HeroController).GetField("normalSlash", InstanceFlags); private static readonly FieldInfo? DownSlashField = typeof(HeroController).GetField("downSlash", InstanceFlags); private static readonly FieldInfo? DownSpikeField = typeof(HeroController).GetField("downSpike", InstanceFlags); private static readonly FieldInfo? WallSlashField = typeof(HeroController).GetField("wallSlash", InstanceFlags); private static readonly FieldInfo? ToolCrestSpriteField = typeof(ToolCrest).GetField("crestSprite", InstanceFlags); private static readonly FieldInfo? ToolCrestSilhouetteField = typeof(ToolCrest).GetField("crestSilhouette", InstanceFlags); private static readonly FieldInfo? ToolCrestGlowField = typeof(ToolCrest).GetField("crestGlow", InstanceFlags); private static readonly FieldInfo? ToolCrestSlotsField = typeof(ToolCrest).GetField("slots", InstanceFlags); private const string VoidCrestArtworkResource = "VoidCrestMovesetCompat.Assets.Void_Crest_Master_Thick.png"; private const float VoidCrestDisplayScale = 1.2f; private static readonly Vector2[] VoidCrestSlotPositions = (Vector2[])(object)new Vector2[6] { new Vector2(-0.007f, 2.189f), new Vector2(0f, -0.294f), new Vector2(-0.075f, -2.017f), new Vector2(-2.005f, 0.768f), new Vector2(2.114f, 0.732f), new Vector2(1.42f, -0.258f) }; private static readonly string[] ConfigGroupObjectFields = new string[9] { "NormalSlashObject", "AlternateSlashObject", "UpSlashObject", "AltUpSlashObject", "DownSlashObject", "AltDownSlashObject", "WallSlashObject", "ChargeSlash", "TauntSlash" }; private static readonly Dictionary<string, string[]> ConfigAliases = new Dictionary<string, string[]>(StringComparer.Ordinal) { ["Hunter"] = new string[1] { "Default" }, ["Hunter_v2"] = new string[1] { "Default" }, ["Hunter_v3"] = new string[1] { "Default" }, ["Hunter_V2"] = new string[1] { "Default" }, ["Hunter_V3"] = new string[1] { "Default" }, ["Beast"] = new string[2] { "Warrior", "Beast" }, ["Warrior"] = new string[2] { "Warrior", "Beast" }, ["Architect"] = new string[1] { "Toolmaster" }, ["Toolmaster"] = new string[1] { "Toolmaster" }, ["Witch"] = new string[2] { "Witch", "Whip" }, ["Whip"] = new string[2] { "Witch", "Whip" }, ["Shaman"] = new string[2] { "Spell", "Shaman" }, ["Spell"] = new string[2] { "Spell", "Shaman" }, ["Reaper"] = new string[1] { "Reaper" }, ["Wanderer"] = new string[1] { "Wanderer" }, ["Cloakless"] = new string[1] { "Cloakless" }, ["Cursed"] = new string[1] { "Cursed" } }; private static readonly HashSet<string> ApprovedBaseCrests = new HashSet<string>(StringComparer.Ordinal) { "Hunter", "Beast", "Warrior", "Architect", "Toolmaster", "Witch", "Whip", "Shaman", "Spell", "Reaper", "Wanderer", "Cloakless", "Cursed" }; private static readonly string[] OrderedBaseCrests = new string[8] { "Hunter", "Wanderer", "Reaper", "Toolmaster", "Spell", "Warrior", "Cursed", "Cloakless" }; private static ManualLogSourceAdapter Log = null; private static Harmony Harmony = null; private static ConfigEntry<bool> TintHijacking = null; private static ConfigEntry<bool> WandererFiftyPercentCritChance = null; private static ConfigEntry<bool> WandererLuckTracker = null; private static ConfigEntry<int> SetVoidmassCount = null; private static ConfigEntry<bool> SetMasksToCriticalDamage = null; private static ConfigEntry<string> ActivateHunterFocusLevel = null; private static ConfigEntry<bool> ShamanSpellDamageBonus = null; private static bool resettingMaskDebugAction; private static bool resettingHunterFocusSelection; private static int liveSlashRetintLogCount; private static int heroExtraNailSlashLogCount; private static readonly HashSet<string> LoggedDashAliases = new HashSet<string>(StringComparer.Ordinal); private static readonly HashSet<int> ConfirmedNamedBosses = new HashSet<int>(); private static readonly HashSet<string> BossOverhaulObjectNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "First Weaver", "Lost Lace Boss", "Silk Boss" }; private static readonly HashSet<string> BossOverhaulComponentTypes = new HashSet<string>(StringComparer.Ordinal) { "LostSinner.Behaviours.Sinner", "Awakened_Grand_Mother_Silk.Source.Behaviours.SilkBoss", "LostAndChained.Components.LaceBossScene" }; private static readonly string[] CommunityBossAliases = new string[54] { "Bell Beast", "Bell Eater", "Broodmother", "Clover Dancers", "Cogwork Dancers", "Cog Dancers", "Crawfather", "Crust King Khann", "Disgraced Chef Lugoli", "Father of the Flame", "First Sinner", "Forebrothers", "Fourth Chorus", "Grand Mother Silk", "Great Conchflies", "Groal the Great", "Gurr the Outcast", "Lace", "Last Judge", "Lost Garmond", "Moorwing", "Moss Mother", "Nyleth", "Palestag", "Phantom", "Pinstress", "Plasmified Zango", "Raging Conchfly", "Raging Vonchfly", "Savage Beastfly", "Second Sentinel", "Shrine Guardian Seth", "Sister Splinter", "Skarrsinger Karmelita", "Skull Tyrant", "Summoned Saviour", "The Unravelled", "Tormented Trobbio", "Trobbio", "Voltvyrm", "Watcher at the Edge", "Widow", "Shakra", "Garmond", "Zaza", "Coral King", "Ant Queen", "Flower Queen", "Crow Court", "Grey Warrior", "Bone Flyer Giant", "Song Golem", "Ward Boss", "Zap Core Enemy" }; private static string activeBaseCrest = string.Empty; private static string lastReportedHunterVersion = string.Empty; private string? observedBaseCrest; private string? pendingBaseCrest; private bool wasAtBench; private bool applyPendingOnBenchEnter; private bool applyPendingOnBenchExit; private bool wasVoidBinding; private bool sawVoidSpecialBinding; private static bool shamanNormalBindInProgress; private static bool shamanBindInvincibilityApplied; private static readonly object ShamanRecoveryInvincibilitySource = new object(); private static bool reaperHalfVoidmassPending; private static SilkChunk? reaperFractionalDisplayChunk; private static bool suppressReaperRetaliationVoidmassGain; private static int wandererTrackedRolls; private static int wandererTrackedCrits; private static float wandererLastRoll = -1f; private static float wandererLastChance; private static bool wandererLastWasCrit; private GUIStyle? wandererTrackerStyle; private static Sprite? customVoidCrestSprite; private static Sprite? customVoidCrestSilhouette; private static Sprite? customVoidCrestGlow; private static string lastAppliedHunterFocusSelection = "Off"; private void Awake() { //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Expected O, but got Unknown //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: Expected O, but got Unknown //IL_0290: Unknown result type (might be due to invalid IL or missing references) //IL_029a: Expected O, but got Unknown Log = new ManualLogSourceAdapter(((BaseUnityPlugin)this).Logger); CanonicalizeHunterConfig(); TintHijacking = ((BaseUnityPlugin)this).Config.Bind<bool>("Visuals", "Tint Hijacking", true, "On: Void tint shader colors slashes. Off: vanilla or skin visuals."); TintHijacking.SettingChanged += delegate { Log.Info(string.Format("Tint Hijacking set to {0}; Void slash tint shader is {1}.", TintHijacking.Value, TintHijacking.Value ? "enabled" : "disabled")); RefreshCurrentChargeSlashTintPolicy(); }; WandererFiftyPercentCritChance = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Wanderer 50 Percent Crit Chance", false, "On: while Void uses Wanderer, set critical-hit chance to 50% for testing. Off: use Wanderer's vanilla critical-hit chance. Vanilla crests are untouched."); WandererFiftyPercentCritChance.SettingChanged += delegate { Log.Info("Wanderer 50% crit debug override is " + (WandererFiftyPercentCritChance.Value ? "enabled" : "disabled") + "."); }; WandererLuckTracker = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Wanderer Luck Tracker", false, "Show an in-game panel with live Wanderer crit chance, RNG rolls, crit results, and observed rate."); WandererLuckTracker.SettingChanged += delegate { ResetWandererLuckTracker(); Log.Info("Wanderer Luck Tracker is " + (WandererLuckTracker.Value ? "enabled" : "disabled") + "."); }; SetVoidmassCount = ((BaseUnityPlugin)this).Config.Bind<int>("Debug", "Set Voidmass Count", 0, new ConfigDescription("During gameplay, selecting a value immediately sets the live Voidmass count. This is a one-time debug action, not a persistent override.", (AcceptableValueBase)(object)new AcceptableValueList<int>(Enumerable.Range(0, 11).ToArray()), Array.Empty<object>())); SetVoidmassCount.SettingChanged += delegate { SetCurrentVoidmassForDebug(SetVoidmassCount.Value); }; SetMasksToCriticalDamage = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Set Masks to Critical Damage", false, "One-shot Hunter test action. On sets current health to exactly 2 Masks, then resets itself to Off. Does nothing unless Void is using Hunter."); SetMasksToCriticalDamage.SettingChanged += delegate { SetMasksForHunterCriticalDamageDebug(); }; ActivateHunterFocusLevel = ((BaseUnityPlugin)this).Config.Bind<string>("Debug", "Activate Focus Level", "Off", new ConfigDescription("Sets Hunter's real combo meter for testing. Focus 1 requires Hunter v2 or v3; Focus 2 requires Hunter v3. Locked levels do nothing.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[3] { "Off", "Focus 1", "Focus 2" }), Array.Empty<object>())); ActivateHunterFocusLevel.SettingChanged += delegate { ActivateHunterFocusLevelDebug(ActivateHunterFocusLevel.Value); }; ShamanSpellDamageBonus = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Shaman Spell Damage Bonus", true, "On: while Void uses Spell, enable Shaman's vanilla Silk Skill damage bonus. Off: disable the bonus for testing. Vanilla crests are untouched."); ShamanSpellDamageBonus.SettingChanged += delegate { Log.Info("Shaman/Spell damage bonus is " + (ShamanSpellDamageBonus.Value ? "enabled" : "disabled") + " for Void Crest."); RefreshLoadedShamanRuneEffects(); }; VoidCrestPlugin.enableOrangeHornet.SettingChanged += OnOrangeHornetShaderChanged; RegisterModMenuConfigOverrides(); Harmony = new Harmony("io.github.erymanthis.voidcrestmovesetcompat"); Harmony.PatchAll(); observedBaseCrest = GetConfiguredBaseCrest(); activeBaseCrest = observedBaseCrest; ApplyVoidCrestArtworkAndSlots(); ((MonoBehaviour)this).StartCoroutine(ApplyVoidCrestRuntimeWhenReady()); } private static void ApplyVoidCrestArtworkAndSlots() { try { CrestData voidCrestData = VoidCrestPlugin.voidCrestData; if (voidCrestData == null) { Log.Warning("Void crest data was unavailable while applying the custom crest artwork."); return; } EnsureVoidCrestSpritesLoaded(); voidCrestData.RealSprite = customVoidCrestSprite; voidCrestData.Silhouette = customVoidCrestSilhouette; voidCrestData.CrestGlow = customVoidCrestGlow; ApplyVoidCrestSlotPositions(voidCrestData.slots); voidCrestData.ApplyAutoSlotNavigation(false, 60f, (Vector2?)null); Log.Info("Applied the custom Void crest artwork and six-slot placement plan."); } catch (Exception arg) { Log.Error($"Failed to apply the custom Void crest artwork: {arg}"); } } private static void EnsureVoidCrestSpritesLoaded() { if ((Object)(object)customVoidCrestSprite != (Object)null && (Object)(object)customVoidCrestSilhouette != (Object)null && (Object)(object)customVoidCrestGlow != (Object)null) { return; } using Stream stream = typeof(Plugin).Assembly.GetManifestResourceStream("VoidCrestMovesetCompat.Assets.Void_Crest_Master_Thick.png"); if (stream == null) { throw new InvalidOperationException("Embedded artwork 'VoidCrestMovesetCompat.Assets.Void_Crest_Master_Thick.png' was not found."); } byte[] array = new byte[stream.Length]; int num; for (int i = 0; i < array.Length; i += num) { num = stream.Read(array, i, array.Length - i); if (num <= 0) { throw new EndOfStreamException("The embedded Void crest artwork ended unexpectedly."); } } Sprite source = VoidCrestSprites.LoadSpriteFromBytes(array, false); Sprite source2 = VoidCrestSprites.LoadSpriteFromBytes(array, true); customVoidCrestSprite = ScaleSpriteDisplay(source, 1.2f); customVoidCrestSilhouette = ScaleSpriteDisplay(source2, 1.2f); customVoidCrestGlow = ScaleSpriteDisplay(VoidCrestSprites.MatchSpriteSize(customVoidCrestSilhouette), 1.2f); ((Object)customVoidCrestSprite).name = "Void Crest Master"; ((Object)customVoidCrestSilhouette).name = "Void Crest Master Silhouette"; ((Object)customVoidCrestGlow).name = "Void Crest Master Glow"; VoidCrestSprites.crestSprite = customVoidCrestSprite; VoidCrestSprites.crestSilhouette = customVoidCrestSilhouette; } private static Sprite ScaleSpriteDisplay(Sprite source, float displayScale) { //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) //IL_0008: 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) //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_002c: 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_0039: Unknown result type (might be due to invalid IL or missing references) Rect rect = source.rect; Vector2 val = source.pivot / new Vector2(((Rect)(ref rect)).width, ((Rect)(ref rect)).height); return Sprite.Create(source.texture, rect, val, source.pixelsPerUnit / displayScale, 0u, (SpriteMeshType)0, source.border); } private static void ApplyVoidCrestSlotPositions(List<SlotInfo> slots) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0047: 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_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: 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_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: 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_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) if (slots.Count != VoidCrestSlotPositions.Length) { Log.Warning($"Expected six Void crest slots but found {slots.Count}; leaving slot placement unchanged."); return; } for (int i = 0; i < slots.Count; i++) { SlotInfo value = slots[i]; value.Position = VoidCrestSlotPositions[i] * 1.2f; slots[i] = value; } SlotInfo value2 = slots[0]; value2.AttackBinding = (AttackToolBinding)1; slots[0] = value2; SlotInfo value3 = slots[1]; value3.AttackBinding = (AttackToolBinding)0; slots[1] = value3; SlotInfo value4 = slots[2]; value4.AttackBinding = (AttackToolBinding)2; slots[2] = value4; SlotInfo value5 = slots[4]; value5.AttackBinding = (AttackToolBinding)0; slots[4] = value5; } private static IEnumerator ApplyVoidCrestRuntimeWhenReady() { for (int frame = 0; frame < 240; frame++) { CrestData voidCrestData = VoidCrestPlugin.voidCrestData; ToolCrest val = ((voidCrestData != null) ? voidCrestData.ToolCrest : null); if (voidCrestData != null && Object.op_Implicit((Object)(object)val)) { ToolCrestSpriteField?.SetValue(val, customVoidCrestSprite); ToolCrestSilhouetteField?.SetValue(val, customVoidCrestSilhouette); ToolCrestGlowField?.SetValue(val, customVoidCrestGlow); ToolCrestSlotsField?.SetValue(val, voidCrestData.slots.ToArray()); Log.Info("Applied the custom Void crest artwork and slots to the live ToolCrest."); yield break; } yield return null; } Log.Warning("The live Void ToolCrest was not created within 240 frames; data-layer artwork remains applied."); } private static void RegisterModMenuConfigOverrides() { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Expected O, but got Unknown //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Expected O, but got Unknown try { if (!(typeof(ConfigEntryFactory).GetField("defaultGenerators", StaticFlags)?.GetValue(null) is IList<MenuElementGenerator> list)) { Log.Warning("Could not access ModMenu default config generators; Base Crest will stay a text field."); return; } object obj = <>O.<1>__GenerateVoidCrestBaseCrestMenuElement; if (obj == null) { MenuElementGenerator val = GenerateVoidCrestBaseCrestMenuElement; <>O.<1>__GenerateVoidCrestBaseCrestMenuElement = val; obj = (object)val; } if (!list.Contains((MenuElementGenerator)obj)) { int index = list.Select((MenuElementGenerator generator, int index2) => new { generator = generator, index = index2 }).FirstOrDefault(x => ((Delegate)(object)x.generator).Method.Name == "GenerateStringElement")?.index ?? list.Count; object obj2 = <>O.<1>__GenerateVoidCrestBaseCrestMenuElement; if (obj2 == null) { MenuElementGenerator val2 = GenerateVoidCrestBaseCrestMenuElement; <>O.<1>__GenerateVoidCrestBaseCrestMenuElement = val2; obj2 = (object)val2; } list.Insert(index, (MenuElementGenerator)obj2); } } catch (Exception arg) { Log.Warning($"Failed registering VoidCrest ModMenu config override: {arg}"); } } private void Update() { TrackBenchDeferredBaseCrest(); RestoreBaseBindCompletionIfNeeded(); if (reaperHalfVoidmassPending && (PlayerData.instance?.CurrentCrestID != "Void" || !IsSelectedBaseCrest("Reaper"))) { ResetReaperFractionalVoidmass("Reaper branch inactive"); } } private void LateUpdate() { if (Object.op_Implicit((Object)(object)reaperFractionalDisplayChunk) && ShouldDisplayReaperHalfVoidmass()) { SetSilkChunkOpacity(reaperFractionalDisplayChunk, 0.5f); } } private void OnDestroy() { if (VoidCrestPlugin.enableOrangeHornet != null) { VoidCrestPlugin.enableOrangeHornet.SettingChanged -= OnOrangeHornetShaderChanged; } HeroInvincibilitySource.Remove(ShamanRecoveryInvincibilitySource); ResetReaperFractionalVoidmass("plugin unload"); } private static void OnOrangeHornetShaderChanged(object sender, EventArgs args) { try { HeroController instance = HeroController.instance; CrestData voidCrestData = VoidCrestPlugin.voidCrestData; ConfigEntry<bool> enableOrangeHornet = VoidCrestPlugin.enableOrangeHornet; if ((Object)(object)instance == (Object)null || voidCrestData == null || !voidCrestData.IsEquipped || enableOrangeHornet == null) { return; } bool value = enableOrangeHornet.Value; if (value) { VoidCrestColorUtil.Voidify(((Component)instance).gameObject); } else { VoidCrestColorUtil.UnvoidifyRoot(((Component)instance).gameObject); ConfigEntry<bool> enableOrangeAura = VoidCrestPlugin.enableOrangeAura; if (enableOrangeAura != null && enableOrangeAura.Value) { Transform val = ((Component)instance).transform.Find("HeroLight"); if ((Object)(object)val != (Object)null) { VoidCrestColorUtil.Voidify(((Component)val).gameObject); } } } Log.Info("Applied Orange Hornet Model Shader setting live: " + (value ? "On" : "Off") + "."); } catch (Exception arg) { Log.Error($"Failed applying Orange Hornet Model Shader setting live: {arg}"); } } private void OnGUI() { //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: 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_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Expected O, but got Unknown //IL_0228: Unknown result type (might be due to invalid IL or missing references) //IL_0258: Unknown result type (might be due to invalid IL or missing references) HeroController instance = HeroController.instance; if (WandererLuckTracker != null && WandererLuckTracker.Value && !((Object)(object)instance == (Object)null) && !instance.IsPaused() && !(PlayerData.instance?.CurrentCrestID != "Void") && IsSelectedBaseCrest("Wanderer")) { if (wandererTrackerStyle == null) { wandererTrackerStyle = new GUIStyle(GUI.skin.label) { fontSize = 16, alignment = (TextAnchor)0, wordWrap = false }; } wandererTrackerStyle.normal.textColor = Color.white; int currentVoidmass = GetCurrentVoidmass(); float wandererCritChance = GetWandererCritChance(); float luckModifier = instance.GetLuckModifier(); float num = Mathf.Clamp01(wandererCritChance * luckModifier); float num2 = ((wandererTrackedRolls > 0) ? ((float)wandererTrackedCrits / (float)wandererTrackedRolls) : 0f); string text = ((wandererLastRoll >= 0f) ? string.Format("{0:P2} / {1:P2} {2}", wandererLastRoll, wandererLastChance, wandererLastWasCrit ? "CRIT" : "normal") : "none"); string text2 = "WANDERER LUCK TRACKER\n" + string.Format("Voidmass: {0} Ready: {1}\n", currentVoidmass, instance.IsWandererLucky ? "YES" : "NO (needs 9 Silk)") + $"Raw chance: {wandererCritChance:P2}\n" + $"Luck modifier: x{luckModifier:0.00}\n" + $"Final chance: {num:P2}\n" + $"RNG rolls: {wandererTrackedRolls} Crits: {wandererTrackedCrits}\n" + "Observed: " + ((wandererTrackedRolls > 0) ? num2.ToString("P2") : "n/a") + "\nLast roll: " + text; float num3 = Mathf.Max(12f, (float)Screen.width - 390f - 12f); Rect val = default(Rect); ((Rect)(ref val))..ctor(num3, 12f, 390f, 184f); GUI.Box(val, GUIContent.none); GUI.Label(new Rect(((Rect)(ref val)).x + 12f, ((Rect)(ref val)).y + 8f, 366f, 168f), text2, wandererTrackerStyle); } } private void RestoreBaseBindCompletionIfNeeded() { //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) HeroController instance = HeroController.instance; if ((Object)(object)instance == (Object)null || PlayerData.instance?.CurrentCrestID != "Void") { if ((Object)(object)instance != (Object)null && shamanBindInvincibilityApplied) { HeroInvincibilityManager.RestoreHero(instance); } shamanNormalBindInProgress = false; shamanBindInvincibilityApplied = false; wasVoidBinding = false; sawVoidSpecialBinding = false; return; } bool isBinding = instance.cState.isBinding; if (isBinding && VoidCrestPlugin.doingSpecialAttack) { sawVoidSpecialBinding = true; } if (!wasVoidBinding && isBinding && !VoidCrestPlugin.doingSpecialAttack && IsSelectedBaseCrest("Spell", "Shaman")) { shamanNormalBindInProgress = true; HeroInvincibilityManager.MakeHeroFucked(instance); shamanBindInvincibilityApplied = true; Log.Info("Applied Void invincibility to Shaman's grounded normal Bind."); } if (wasVoidBinding && !isBinding && !sawVoidSpecialBinding && IsSelectedBaseCrest("Warrior", "Beast") && !instance.WarriorState.IsInRageMode) { instance.BindCompleted(); Log.Info($"Restored Beast Fury after normal Void Bind; active={instance.WarriorState.IsInRageMode}."); } else if (wasVoidBinding && !isBinding && !sawVoidSpecialBinding && IsSelectedBaseCrest("Reaper") && !instance.ReaperState.IsInReaperMode) { instance.BindCompleted(); Log.Info($"Restored Reaper harvest state after normal Void Bind; active={instance.ReaperState.IsInReaperMode}."); } if (wasVoidBinding && !isBinding && shamanNormalBindInProgress) { shamanNormalBindInProgress = false; if (shamanBindInvincibilityApplied) { HeroInvincibilityManager.RestoreHero(instance); shamanBindInvincibilityApplied = false; } ((MonoBehaviour)this).StartCoroutine(ShamanBindRecoveryInvincibility()); Log.Info("Completed Shaman grounded Void Bind with healing suppressed."); } if (!isBinding) { sawVoidSpecialBinding = false; } wasVoidBinding = isBinding; } private static IEnumerator ShamanBindRecoveryInvincibility() { HeroInvincibilitySource.Add(ShamanRecoveryInvincibilitySource); for (int frame = 0; frame < 8; frame++) { yield return null; } HeroInvincibilitySource.Remove(ShamanRecoveryInvincibilitySource); Log.Info("Ended Shaman Bind's 8-frame non-retaliating recovery invincibility."); } private static bool ApplyBaseMoveset(MovesetData moveset) { if (moveset != null) { CrestData voidCrestData = VoidCrestPlugin.voidCrestData; if (moveset == ((voidCrestData != null) ? voidCrestData.Moveset : null)) { HeroController instance = HeroController.instance; CrestData voidCrestData2 = VoidCrestPlugin.voidCrestData; if ((Object)(object)instance == (Object)null || ((voidCrestData2 != null) ? voidCrestData2.Moveset : null) == null || voidCrestData2.Moveset.ConfigGroup == null || !ShouldApplyCompat()) { return false; } string text = GetActiveBaseCrest(); ToolCrest crestByName = ToolItemManager.GetCrestByName(text); if (!Object.op_Implicit((Object)(object)crestByName)) { Log.Warning("Base crest '" + text + "' was not found at gameplay start."); return false; } ConfigGroup val = ResolveSourceConfigGroup(instance, text, crestByName); if (val == null) { Log.Warning("No live ConfigGroup was found for base crest '" + text + "'."); return false; } MovesetData moveset2 = voidCrestData2.Moveset; ConfigGroup configGroup = moveset2.ConfigGroup; HeroConfigNeedleforge heroConfig = moveset2.HeroConfig; FsmEdit edit = ((heroConfig != null) ? heroConfig.DashSlashFsmEdit : null); HeroConfigNeedleforge val2 = CloneHeroConfig(val.Config); ((Object)val2).name = voidCrestData2.name; SetDashSlashFsmEditSilently(val2, edit); moveset2.HeroConfig = val2; LogConfigGroupDetails("Source", val); ReplaceConfigGroupRoot(configGroup, val, preserveDashStab: true); RestoreTransplantedAttackAudio(val, configGroup); NormalizeTransplantedDownslashConfig(val2, configGroup.DownSlashObject); if (moveset2.DashSlash != null) { configGroup.DashStab = EnsureDashRoot(((GameObjectProxy)moveset2.DashSlash).CreateGameObject(configGroup.ActiveRoot, instance), configGroup.ActiveRoot); } Log.Info("Downslash config for '" + text + "': source=" + DescribeDownslashConfig(val.Config) + ", replacement=" + DescribeDownslashConfig((HeroControllerConfig?)(object)val2) + ", target=" + DescribeDownslashConfig(configGroup.Config)); configGroup.Setup(); ApplyChargeSlashTintPolicy(text, val.ChargeSlash, configGroup.ChargeSlash); LogConfigGroupDetails("Target", configGroup); LogChargeSlashDetails("Source", val.ChargeSlash); LogChargeSlashDetails("Target", configGroup.ChargeSlash); Log.Info("Applied base crest '" + text + "' attack objects to VoidCrest."); return true; } } return false; } private static bool GenerateVoidCrestBaseCrestMenuElement(ConfigEntryBase entry, out MenuElement menuElement) { menuElement = null; if (!(entry is ConfigEntry<string> val) || !string.Equals(entry.Definition.Section, "General", StringComparison.Ordinal) || !string.Equals(entry.Definition.Key, "Base Crest", StringComparison.Ordinal)) { return false; } ConfigFile configFile = entry.ConfigFile; if (!string.Equals(Path.GetFileName(((configFile != null) ? configFile.ConfigFilePath : null) ?? string.Empty), "io.dervoce.voidcrest.cfg", StringComparison.OrdinalIgnoreCase)) { return false; } List<string> list = new List<string>(OrderedBaseCrests); string text = CanonicalizeConfiguredBaseCrest(val.Value?.Trim() ?? string.Empty); if (!string.IsNullOrWhiteSpace(text) && !list.Contains<string>(text, StringComparer.Ordinal)) { list.Insert(0, text); } ChoiceElement<string> val2 = new ChoiceElement<string>(LocalizedText.op_Implicit(ConfigEntryFactoryExtensions.LabelName(entry)), (IChoiceModel<string>)(object)ChoiceModels.ForValues<string>(list), LocalizedText.op_Implicit(ConfigEntryFactoryExtensions.DescriptionLine(entry))); ConfigEntryFactoryExtensions.SynchronizeWith<string>((SelectableValueElement<string>)(object)val2, val); menuElement = (MenuElement)(object)val2; return true; } private void TrackBenchDeferredBaseCrest() { string text = NormalizeQueuedBaseCrest(GetConfiguredBaseCrest()); if (text == null) { return; } if (!string.Equals(text, observedBaseCrest, StringComparison.Ordinal)) { observedBaseCrest = text; pendingBaseCrest = text; PlayerData instance = PlayerData.instance; if (instance != null && instance.atBench) { applyPendingOnBenchExit = true; applyPendingOnBenchEnter = false; Log.Info("Queued base crest '" + pendingBaseCrest + "' to apply when leaving the bench."); } else { applyPendingOnBenchEnter = true; applyPendingOnBenchExit = false; Log.Info("Queued base crest '" + pendingBaseCrest + "' to apply on the next bench rest."); } } bool flag = PlayerData.instance?.atBench ?? false; if (!wasAtBench && flag && applyPendingOnBenchEnter) { ApplyPendingBenchChange("bench enter"); } else if (wasAtBench && !flag && applyPendingOnBenchExit) { ApplyPendingBenchChange("bench exit"); } wasAtBench = flag; } private void ApplyPendingBenchChange(string trigger) { string text = pendingBaseCrest ?? activeBaseCrest; string a = activeBaseCrest; activeBaseCrest = text; Log.Info("Applying queued base crest '" + text + "' on " + trigger + "."); if (TryApplyCurrentBaseCrest()) { if (string.Equals(a, "Reaper", StringComparison.Ordinal) && !string.Equals(text, "Reaper", StringComparison.Ordinal)) { ResetReaperFractionalVoidmass("base crest changed"); } pendingBaseCrest = null; applyPendingOnBenchEnter = false; applyPendingOnBenchExit = false; } else { activeBaseCrest = a; Log.Error("Base crest switch to '" + text + "' failed safely; the previous moveset remains active and the change stays queued."); } } private static bool TryApplyCurrentBaseCrest() { CrestData voidCrestData = VoidCrestPlugin.voidCrestData; MovesetData val = ((voidCrestData != null) ? voidCrestData.Moveset : null); HeroController instance = HeroController.instance; if (val == null || (Object)(object)instance == (Object)null) { return false; } try { if (!ApplyBaseMoveset(val)) { return false; } if (PlayerData.instance?.CurrentCrestID == "Void") { ReapplyVoidConfigAfterReset(instance); ForceResolvedBaseConfigGroup(instance); } return true; } catch (Exception arg) { Log.Error($"Failed applying live Void base crest switch: {arg}"); return false; } } private static string GetConfiguredBaseCrest() { return CanonicalizeConfiguredBaseCrest(VoidCrestPlugin.BaseCrest?.Value?.Trim() ?? string.Empty); } private static string GetActiveBaseCrest() { if (!string.Equals(activeBaseCrest, "Hunter", StringComparison.Ordinal)) { return activeBaseCrest; } return ResolvePlayerHunterVersion(); } private static string? NormalizeQueuedBaseCrest(string? value) { if (string.IsNullOrWhiteSpace(value)) { return null; } string text = CanonicalizeConfiguredBaseCrest(value.Trim()); if (!ApprovedBaseCrests.Contains(text)) { return null; } return text; } private static void CanonicalizeHunterConfig() { ConfigEntry<string> baseCrest = VoidCrestPlugin.BaseCrest; if (baseCrest != null) { string text = baseCrest.Value?.Trim() ?? string.Empty; string text2 = CanonicalizeConfiguredBaseCrest(text); if (!string.Equals(text, text2, StringComparison.Ordinal)) { baseCrest.Value = text2; Log.Info("Normalized Void Base Crest '" + text + "' to '" + text2 + "'. Hunter follows the player's vanilla evolution."); } } } private static string CanonicalizeConfiguredBaseCrest(string value) { if (!IsHunterVariantName(value)) { return value; } return "Hunter"; } private static bool IsHunterVariantName(string value) { if (!string.Equals(value, "Hunter_V2", StringComparison.Ordinal) && !string.Equals(value, "Hunter_V3", StringComparison.Ordinal) && !string.Equals(value, "Hunter_v2", StringComparison.Ordinal)) { return string.Equals(value, "Hunter_v3", StringComparison.Ordinal); } return true; } private static bool IsHunterBaseSelected() { return string.Equals(activeBaseCrest, "Hunter", StringComparison.Ordinal); } private static bool ShouldApplyHunterVoidmassDamage(DamageEnemies damage) { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Invalid comparison between Unknown and I4 if (PlayerData.instance?.CurrentCrestID != "Void" || !IsHunterBaseSelected() || (Object)(object)damage.RepresentingTool != (Object)null || VoidCrestPlugin.doingSpecialAttack || !IsCurrentHunterFocusActive()) { return false; } if ((int)damage.attackType != 0 && (int)damage.attackType != 7) { return DamageEnemies.IsNailAttackObject(((Component)damage).gameObject); } return true; } private static bool IsCurrentHunterFocusActive() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) HeroController instance = HeroController.instance; if ((Object)(object)instance == (Object)null) { return false; } int currentMeterHits = instance.HunterUpgState.CurrentMeterHits; string a = ResolvePlayerHunterVersion(); if (string.Equals(a, "Hunter_v3", StringComparison.Ordinal)) { return currentMeterHits >= Gameplay.HunterCombo2Hits; } if (string.Equals(a, "Hunter_v2", StringComparison.Ordinal)) { return currentMeterHits >= Gameplay.HunterComboHits; } return false; } private static float GetCurrentHunterFocusMultiplier() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) HeroController instance = HeroController.instance; if ((Object)(object)instance == (Object)null) { return 1f; } int currentMeterHits = instance.HunterUpgState.CurrentMeterHits; string a = ResolvePlayerHunterVersion(); if (string.Equals(a, "Hunter_v3", StringComparison.Ordinal)) { if (currentMeterHits >= Gameplay.HunterCombo2Hits + Gameplay.HunterCombo2ExtraHits) { return Gameplay.HunterCombo2ExtraDamageMult; } if (currentMeterHits >= Gameplay.HunterCombo2Hits) { return Gameplay.HunterCombo2DamageMult; } } else if (string.Equals(a, "Hunter_v2", StringComparison.Ordinal) && currentMeterHits >= Gameplay.HunterComboHits) { return Gameplay.HunterComboDamageMult; } return 1f; } private static bool IsNamedBoss(GameObject target) { if (!Object.op_Implicit((Object)(object)target)) { return false; } HealthManager val = target.GetComponent<HealthManager>() ?? target.GetComponentInParent<HealthManager>(); if (!Object.op_Implicit((Object)(object)val)) { return false; } int instanceID = ((Object)val).GetInstanceID(); if (ConfirmedNamedBosses.Contains(instanceID)) { return true; } if (BossOverhaulObjectNames.Contains(((Object)((Component)val).gameObject).name)) { ConfirmNamedBoss(val, "boss overhaul object '" + ((Object)((Component)val).gameObject).name + "'"); return true; } foreach (MonoBehaviour item in ((Component)val).GetComponentsInParent<MonoBehaviour>(true).Concat(((Component)val).GetComponentsInChildren<MonoBehaviour>(true)).Distinct()) { string text = (((Object)(object)item != (Object)null) ? (((object)item).GetType().FullName ?? string.Empty) : string.Empty); if (BossOverhaulComponentTypes.Contains(text)) { ConfirmNamedBoss(val, "boss overhaul component '" + text + "'"); return true; } } BossSceneController instance = BossSceneController.Instance; if (Object.op_Implicit((Object)(object)instance) && (instance.BossHealthLookup.ContainsKey(val) || (instance.bosses != null && instance.bosses.Contains(val)))) { ConfirmNamedBoss(val, "BossSceneController"); return true; } foreach (PlayMakerFSM item2 in ((Component)val).GetComponentsInParent<PlayMakerFSM>(true).Concat(((Component)val).GetComponentsInChildren<PlayMakerFSM>(true)).Distinct()) { FsmState[] fsmStates = item2.FsmStates; for (int i = 0; i < fsmStates.Length; i++) { FsmStateAction[] actions = fsmStates[i].Actions; foreach (FsmStateAction val2 in actions) { if (val2 is DisplayBossTitle) { ConfirmNamedBoss(val, "boss title FSM '" + item2.FsmName + "'"); return true; } RecordJournalKill val3 = (RecordJournalKill)(object)((val2 is RecordJournalKill) ? val2 : null); EnemyJournalRecord val5; if (val3 == null) { RecordJournalKillV2 val4 = (RecordJournalKillV2)(object)((val2 is RecordJournalKillV2) ? val2 : null); if (val4 != null) { FsmObject record = val4.Record; Object obj = ((record != null) ? record.Value : null); val5 = (EnemyJournalRecord)(object)((obj is EnemyJournalRecord) ? obj : null); } else { val5 = null; } } else { FsmObject record2 = val3.Record; Object obj2 = ((record2 != null) ? record2.Value : null); val5 = (EnemyJournalRecord)(object)((obj2 is EnemyJournalRecord) ? obj2 : null); } EnemyJournalRecord val6 = val5; if (Object.op_Implicit((Object)(object)val6) && val6.KillsRequired <= 1) { ConfirmNamedBoss(val, "one-kill journal record '" + ((Object)val6).name + "'"); return true; } } } } string text2 = NormalizeBossIdentity(GetObjectPath(((Component)val).transform)); string[] communityBossAliases = CommunityBossAliases; foreach (string text3 in communityBossAliases) { if (text2.Contains(NormalizeBossIdentity(text3))) { ConfirmNamedBoss(val, "community boss alias '" + text3 + "'"); return true; } } return false; } private static void ConfirmNamedBoss(HealthManager health, string reason) { if (ConfirmedNamedBosses.Add(((Object)health).GetInstanceID())) { Log.Info("Hunter boss cap recognized '" + GetObjectPath(((Component)health).transform) + "' via " + reason + "."); } } private static string NormalizeBossIdentity(string value) { return new string(value.Where(char.IsLetterOrDigit).Select(char.ToLowerInvariant).ToArray()); } private static void SetMasksForHunterCriticalDamageDebug() { if (resettingMaskDebugAction || SetMasksToCriticalDamage == null || !SetMasksToCriticalDamage.Value) { return; } try { HeroController instance = HeroController.instance; PlayerData instance2 = PlayerData.instance; if ((Object)(object)instance != (Object)null && instance2 != null && instance2.CurrentCrestID == "Void" && IsHunterBaseSelected()) { int num = Mathf.Min(2, instance2.CurrentMaxHealth); if (instance2.health > num) { instance.TakeHealth(instance2.health - num + instance2.healthBlue); } else if (instance2.health < num) { instance.AddHealth(num - instance2.health); } Log.Info($"Hunter debug set current health to {instance2.health} Masks (critical coefficient active at 2 or less)."); } else { Log.Info("Hunter critical-Masks debug action ignored because Void Hunter is not active."); } } finally { resettingMaskDebugAction = true; SetMasksToCriticalDamage.Value = false; resettingMaskDebugAction = false; } } private static void ActivateHunterFocusLevelDebug(string requestedLevel) { //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) if (!resettingHunterFocusSelection && ActivateHunterFocusLevel != null) { HeroController instance = HeroController.instance; bool flag = (Object)(object)instance != (Object)null && PlayerData.instance?.CurrentCrestID == "Void" && IsHunterBaseSelected(); ToolCrest crestByName = ToolItemManager.GetCrestByName("Hunter_v2"); ToolCrest crestByName2 = ToolItemManager.GetCrestByName("Hunter_v3"); bool flag2 = Object.op_Implicit((Object)(object)crestByName2) && crestByName2.IsUnlocked; bool flag3 = flag2 || (Object.op_Implicit((Object)(object)crestByName) && crestByName.IsUnlocked); if (requestedLevel switch { "Off" => flag ? 1 : 0, "Focus 1" => (flag && flag3) ? 1 : 0, "Focus 2" => (flag && flag2) ? 1 : 0, _ => 0, } == 0 || (Object)(object)instance == (Object)null || HunterUpgStateField == null) { Log.Info("Hunter debug '" + requestedLevel + "' ignored: the active Hunter evolution does not support that Focus level."); resettingHunterFocusSelection = true; ActivateHunterFocusLevel.Value = lastAppliedHunterFocusSelection; resettingHunterFocusSelection = false; } else { int num = ((!(requestedLevel == "Focus 1")) ? ((requestedLevel == "Focus 2") ? (Gameplay.HunterCombo2Hits + Gameplay.HunterCombo2ExtraHits) : 0) : ((!flag2) ? Gameplay.HunterComboHits : Gameplay.HunterCombo2Hits)); int num2 = num; HunterUpgCrestStateInfo hunterUpgState = instance.HunterUpgState; hunterUpgState.CurrentMeterHits = num2; HunterUpgStateField.SetValue(instance, hunterUpgState); lastAppliedHunterFocusSelection = requestedLevel; Log.Info($"Hunter debug set {requestedLevel} using vanilla combo meter value {num2}."); } } } private static string ResolvePlayerHunterVersion() { string text = "Hunter"; if (PlayerData.instance != null) { ToolCrest crestByName = ToolItemManager.GetCrestByName("Hunter_v3"); ToolCrest crestByName2 = ToolItemManager.GetCrestByName("Hunter_v2"); if (Object.op_Implicit((Object)(object)crestByName) && crestByName.IsUnlocked) { text = crestByName.name; } else if (Object.op_Implicit((Object)(object)crestByName2) && crestByName2.IsUnlocked) { text = crestByName2.name; } } if (!string.Equals(text, lastReportedHunterVersion, StringComparison.Ordinal)) { lastReportedHunterVersion = text; Log.Info("Resolved canonical Hunter base crest to vanilla progression asset '" + text + "'."); } return text; } private static void PrepareVoidHeroConfigForStart() { if (!ShouldApplyCompat() || PlayerData.instance?.CurrentCrestID != "Void") { return; } CrestData voidCrestData = VoidCrestPlugin.voidCrestData; ToolCrest val = ((voidCrestData != null) ? voidCrestData.ToolCrest : null); if (Object.op_Implicit((Object)(object)val)) { ToolCrest crestByName = ToolItemManager.GetCrestByName(GetActiveBaseCrest()); if (Object.op_Implicit((Object)(object)crestByName) && !((Object)(object)crestByName.HeroConfig == (Object)null)) { ToolCrestHeroConfigField?.SetValue(val, crestByName.HeroConfig); } } } private static void ReapplyVoidConfigAfterReset(HeroController hero) { if (ShouldApplyCompat() && !(PlayerData.instance?.CurrentCrestID != "Void")) { CrestData voidCrestData = VoidCrestPlugin.voidCrestData; object obj; if (voidCrestData == null) { obj = null; } else { MovesetData moveset = voidCrestData.Moveset; obj = ((moveset != null) ? moveset.HeroConfig : null); } HeroConfigNeedleforge val = (HeroConfigNeedleforge)obj; if (!((Object)(object)val == (Object)null)) { CrestConfigField?.SetValue(hero, val); UpdateConfigMethod?.Invoke(hero, Array.Empty<object>()); Log.Info("Reapplied Void compat config after ResetAllCrestState: " + DescribeDownslashConfig((HeroControllerConfig?)(object)val)); } } } private static void ForceResolvedBaseConfigGroup(HeroController hero) { if (!ShouldApplyCompat() || PlayerData.instance?.CurrentCrestID != "Void") { return; } string text = GetActiveBaseCrest(); ToolCrest crestByName = ToolItemManager.GetCrestByName(text); if (Object.op_Implicit((Object)(object)crestByName)) { ConfigGroup val = ResolveSourceConfigGroup(hero, text, crestByName); object? obj = CurrentConfigGroupProperty?.GetValue(hero); ConfigGroup val2 = (ConfigGroup)((obj is ConfigGroup) ? obj : null); if (val != null && val != val2) { SetConfigGroupMethod?.Invoke(hero, new object[2] { val, null }); ManualLogSourceAdapter log = Log; string[] obj2 = new string[5] { "Resolved active config group to '", null, null, null, null }; HeroControllerConfig config = val.Config; obj2[1] = ((config != null) ? ((Object)config).name : null) ?? "<null>"; obj2[2] = "' for Void base crest '"; obj2[3] = text; obj2[4] = "'."; log.Info(string.Concat(obj2)); } } } private static void SetDashSlashFsmEditSilently(HeroConfigNeedleforge config, FsmEdit? edit) { if (DashFsmEditField != null) { DashFsmEditField.SetValue(config, edit); } else { config.DashSlashFsmEdit = edit; } } private static bool ShouldApplyCompat() { if (string.IsNullOrWhiteSpace(GetActiveBaseCrest())) { return false; } return true; } private static bool IsBaseCrestFunctionallyEquipped(ToolBase tool) { if ((Object)(object)tool == (Object)null) { return false; } if (tool.IsEquipped) { return true; } ToolCrest val = (ToolCrest)(object)((tool is ToolCrest) ? tool : null); if (PlayerData.instance?.CurrentCrestID == "Void" && (Object)(object)val != (Object)null) { return IsSelectedBaseCrest(val.name); } return false; } private static bool ShouldApplyShamanSpellDamageBonus() { if (PlayerData.instance?.CurrentCrestID == "Void" && IsSelectedBaseCrest("Spell", "Shaman")) { if (ShamanSpellDamageBonus != null) { return ShamanSpellDamageBonus.Value; } return true; } return false; } private static float GetWandererCritChance() { if (PlayerData.instance?.CurrentCrestID == "Void" && IsSelectedBaseCrest("Wanderer")) { HeroController instance = HeroController.instance; if (WandererFiftyPercentCritChance != null && WandererFiftyPercentCritChance.Value) { float num = (((Object)(object)instance != (Object)null) ? instance.GetLuckModifier() : 1f); if (!(num > Mathf.Epsilon)) { return 0.5f; } return 0.5f / num; } return Gameplay.WandererCritChance + (float)GetCurrentVoidmass() * 0.031f; } return Gameplay.WandererCritChance; } private static int GetCurrentVoidmass() { try { return (VoidMassField?.GetValue(null) is int val) ? Math.Max(0, val) : 0; } catch (Exception ex) { string item = "VoidmassRead:" + ex.GetType().FullName; if (LoggedDashAliases.Add(item)) { Log.Warning("Could not read Voidmass for branch compatibility: " + ex.Message); } return 0; } } private static bool IsVoidmassSpool(SilkSpool? spool) { if (!Object.op_Implicit((Object)(object)spool)) { return false; } object? obj = VoidSpoolCloneField?.GetValue(null); GameObject val = (GameObject)((obj is GameObject) ? obj : null); if (!Object.op_Implicit((Object)(object)val) || ((Component)spool).gameObject != val) { return string.Equals(((Object)((Component)spool).gameObject).name, "Spool_VoidVersion", StringComparison.Ordinal); } return true; } private static bool ShouldDisplayReaperHalfVoidmass() { if (reaperHalfVoidmassPending && GetCurrentVoidmass() < 10 && PlayerData.instance?.CurrentCrestID == "Void") { return IsSelectedBaseCrest("Reaper"); } return false; } private static void RefreshReaperFractionalVoidmassDisplay() { try { object? obj = VoidSpoolCloneField?.GetValue(null); GameObject val = (GameObject)((obj is GameObject) ? obj : null); SilkSpool val2 = (Object.op_Implicit((Object)(object)val) ? val.GetComponent<SilkSpool>() : null); if (Object.op_Implicit((Object)(object)val2)) { val2.ChangeSilk(GetCurrentVoidmass(), 0, (SilkAddSource)0, (SilkTakeSource)0); } } catch (Exception ex) { string item = "ReaperFractionDisplay:" + ex.GetType().FullName; if (LoggedDashAliases.Add(item)) { Log.Warning("Could not refresh Reaper's half-Voidmass pip: " + ex.GetBaseException().Message); } } } private static void ApplyReaperFractionalPipOpacity(SilkSpool spool, bool showHalf) { SilkChunk val = null; if (showHalf && SilkChunksField?.GetValue(spool) is IList { Count: >0 } list) { object? obj = list[list.Count - 1]; val = (SilkChunk)((obj is SilkChunk) ? obj : null); } if (Object.op_Implicit((Object)(object)reaperFractionalDisplayChunk) && reaperFractionalDisplayChunk != val) { SetSilkChunkOpacity(reaperFractionalDisplayChunk, 1f); } reaperFractionalDisplayChunk = val; if (Object.op_Implicit((Object)(object)val)) { SetSilkChunkOpacity(val, 0.5f); } } private static void SetSilkChunkOpacity(SilkChunk chunk, float opacity) { //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_0020: Unknown result type (might be due to invalid IL or missing references) tk2dSprite component = ((Component)chunk).GetComponent<tk2dSprite>(); if (Object.op_Implicit((Object)(object)component)) { Color color = ((tk2dBaseSprite)component).color; color.a = opacity; ((tk2dBaseSprite)component).color = color; } } private static bool IsReaperBundleObject(GameObject? actionOwner) { GameObject reaperBundlePrefab = Gameplay.ReaperBundlePrefab; if (!Object.op_Implicit((Object)(object)actionOwner) || !Object.op_Implicit((Object)(object)reaperBundlePrefab)) { return false; } string b = RemoveCloneSuffix(((Object)reaperBundlePrefab).name); Transform val = actionOwner.transform; while ((Object)(object)val != (Object)null) { if (((Component)val).gameObject == reaperBundlePrefab || string.Equals(RemoveCloneSuffix(((Object)((Component)val).gameObject).name), b, StringComparison.Ordinal)) { return true; } val = val.parent; } return false; } private static string RemoveCloneSuffix(string objectName) { if (!objectName.EndsWith("(Clone)", StringComparison.Ordinal)) { return objectName; } return objectName.Substring(0, objectName.Length - "(Clone)".Length).TrimEnd(Array.Empty<char>()); } private static void AwardReaperOrbVoidmass() { int num = Mathf.Clamp(GetCurrentVoidmass(), 0, 10); if (num >= 10) { ResetReaperFractionalVoidmass("Voidmass cap reached"); Log.Info("Collected Reaper Voidmass orb at the 10-Voidmass cap; no Silk was awarded."); return; } if (!reaperHalfVoidmassPending) { reaperHalfVoidmassPending = true; RefreshReaperFractionalVoidmassDisplay(); Log.Info($"Collected Reaper Voidmass orb: effective Voidmass {(float)num + 0.5f:0.0}/10 (half-unit banked)."); return; } reaperHalfVoidmassPending = false; int num2 = Math.Min(num + 1, 10); try { PropertyInfo? voidMassProperty = VoidMassProperty; if ((object)voidMassProperty != null && voidMassProperty.CanWrite) { VoidMassProperty.SetValue(null, num2, null); } else { if (!(VoidMassField != null)) { Log.Warning("Reaper orb conversion could not find Void Crest's live Voidmass storage."); return; } VoidMassField.SetValue(null, num2); } Log.Info($"Collected Reaper Voidmass orb: Voidmass {num2}/10 (two half-units completed)."); } catch (Exception ex) { try { VoidMassField?.SetValue(null, num2); } catch { } Log.Warning($"Reaper orb raised Voidmass to {num2}, but its HUD refresh failed: {ex.GetBaseException().Message}"); } } private static void ResetReaperFractionalVoidmass(string reason) { if (reaperHalfVoidmassPending) { reaperHalfVoidmassPending = false; RefreshReaperFractionalVoidmassDisplay(); Log.Info("Cleared Reaper's banked half Voidmass: " + reason + "."); } } private static void SetCurrentVoidmassForDebug(int requestedValue) { int num = Mathf.Clamp(requestedValue, 0, 10); ResetReaperFractionalVoidmass("debug Voidmass count set"); try { PropertyInfo? voidMassProperty = VoidMassProperty; if ((object)voidMassProperty != null && voidMassProperty.CanWrite) { VoidMassProperty.SetValue(null, num, null); } else { if (!(VoidMassField != null)) { Log.Warning("Set Voidmass Count could not find Void Crest's live Voidmass storage."); return; } VoidMassField.SetValue(null, num); } ResetWandererLuckTracker(); Log.Info($"Debug set live Voidmass count to {num}."); } catch (Exception ex) { try { VoidMassField?.SetValue(null, num); } catch { } ResetWandererLuckTracker(); Log.Warning($"Set live Voidmass count to {num}, but its HUD refresh failed: {ex.GetBaseException().Message}"); } } private static float GetTrackedWandererRandomRange(float minimum, float maximum) { float num = Random.Range(minimum, maximum); if (WandererLuckTracker != null && WandererLuckTracker.Value && PlayerData.instance?.CurrentCrestID == "Void" && IsSelectedBaseCrest("Wanderer")) { HeroController instance = HeroController.instance; float num2 = Mathf.Clamp01(GetWandererCritChance() * (((Object)(object)instance != (Object)null) ? instance.GetLuckModifier() : 1f)); wandererTrackedRolls++; wandererLastRoll = num; wandererLastChance = num2; wandererLastWasCrit = num <= num2; if (wandererLastWasCrit) { wandererTrackedCrits++; } } return num; } private static void ResetWandererLuckTracker() { wandererTrackedRolls = 0; wandererTrackedCrits = 0; wandererLastRoll = -1f; wandererLastChance = 0f; wandererLastWasCrit = false; } private static void RefreshLoadedShamanRuneEffects() { HeroShamanRuneEffect[] array = Object.FindObjectsByType<HeroShamanRuneEffect>((FindObjectsInactive)1, (FindObjectsSortMode)0); for (int i = 0; i < array.Length; i++) { array[i].Refresh(); } } private static bool BaseAwareStringEquals(string? left, string? right) { if (string.Equals(left, right, StringComparison.Ordinal)) { return true; } if (PlayerData.instance?.CurrentCrestID != "Void") { return false; } if (string.Equals(left, "Void", StringComparison.Ordinal) && right != null) { return IsSelectedBaseCrest(right); } if (string.Equals(right, "Void", StringComparison.Ordinal) && left != null) { return IsSelectedBaseCrest(left); } return false; } private static bool IsSelectedBaseCrest(params string[] names) { string a = GetActiveBaseCrest(); foreach (string b in names) { if (string.Equals(a, b, StringComparison.Ordinal)) { return true; } } return false; } private static bool DoesSelectedBaseRepresentCrest(string crestName) { string text = GetActiveBaseCrest(); if (string.Equals(text, crestName, StringComparison.Ordinal)) { return true; } if (ConfigAliases.TryGetValue(text, out string[] value) && value.Contains<string>(crestName, StringComparer.Ordinal)) { return true; } if (string.Equals(text, "Cursed", StringComparison.Ordinal)) { if (!string.Equals(crestName, "Whip", StringComparison.Ordinal) && !string.Equals(crestName, "Witch", StringComparison.Ordinal)) { return string.Equals(crestName, "Cursed", StringComparison.Ordinal); } return true; } return false; } private static bool ShouldRefreshLiveSlashVisuals() { if (TintHijacking != null && !TintHijacking.Value && (Object)(object)HeroController.instance != (Object)null) { return PlayerData.instance?.CurrentCrestID == "Void"; } return false; } private static void RefreshLiveSlashVisuals(Component? source) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)source == (Object)null) && ShouldRefreshLiveSlashVisuals()) { GameObject gameObject = source.gameObject; if (!((Object)(object)gameObject.GetComponent<HeroController>() != (Object)null) && !((Object)(object)gameObject == (Object)(object)((Component)HeroController.instance).gameObject)) { Color desiredSlashColor = GetDesiredSlashColor(); (gameObject.GetComponent<LiveSlashVisualRetint>() ?? gameObject.AddComponent<LiveSlashVisualRetint>()).Configure(desiredSlashColor, 12); } } } private static void RefreshCurrentChargeSlashTintPolicy() { HeroController instance = HeroController.instance; CrestData voidCrestData = VoidCrestPlugin.voidCrestData; if (!((Object)(object)instance == (Object)null)) { object obj; if (voidCrestData == null) { obj = null; } else { MovesetData moveset = voidCrestData.Moveset; obj = ((moveset != null) ? moveset.ConfigGroup : null); } if (obj != null) { string text = GetActiveBaseCrest(); ToolCrest crestByName = ToolItemManager.GetCrestByName(text); ConfigGroup val = (Object.op_Implicit((Object)(object)crestByName) ? ResolveSourceConfigGroup(instance, text, crestByName) : null); ApplyChargeSlashTintPolicy(text, val?.ChargeSlash, voidCrestData.Moveset.ConfigGroup.ChargeSlash); } } } private static void ApplyChargeSlashTintPolicy(string baseCrestName, params GameObject?[] chargeSlashRoots) { VoidCrestColorUtil.ExemptObjects = RemoveDestroyedObjects(VoidCrestColorUtil.ExemptObjects); VoidCrestColorUtil.ExemptSprites = RemoveDestroyedObjects(VoidCrestColorUtil.ExemptSprites); bool flag = TintHijacking != null && TintHijacking.Value && (string.Equals(baseCrestName, "Spell", StringComparison.Ordinal) || string.Equals(baseCrestName, "Shaman", StringComparison.Ordinal)); foreach (GameObject val in chargeSlashRoots) { if (Object.op_Implicit((Object)(object)val)) { if (flag) { VoidCrestColorUtil.ExemptObjects = RemoveObject(VoidCrestColorUtil.ExemptObjects, val); VoidCrestColorUtil.ExemptSprites = RemoveObject(VoidCrestColorUtil.ExemptSprites, val); Log.Info("Allowed Spell ChargeSlash visual root '" + GetObjectPath(val.transform) + "' through VoidCrest's tint pass."); } else { VoidCrestColorUtil.ExemptObjects = AddUniqueObject(VoidCrestColorUtil.ExemptObjects, val); VoidCrestColorUtil.ExemptSprites = AddUniqueObject(VoidCrestColorUtil.ExemptSprites, val); Log.Info("Excluded ChargeSlash visual root '" + GetObjectPath(val.transform) + "' from VoidCrest's continuous recolor pass."); } } } } private static GameObject[] RemoveObject(GameObject[]? objects, GameObject value) { return (from item in RemoveDestroyedObjects(objects) where item != value select item).ToArray(); } private static GameObject[] RemoveDestroyedObjects(GameObject[]? objects) { if (objects == null || objects.Length == 0) { return Array.Empty<GameObject>(); } return objects.Where((GameObject item) => Object.op_Implicit((Object)(object)item)).ToArray(); } private static GameObject[] AddUniqueObject(GameObject[]? objects, GameObject value) { GameObject[] array = RemoveDestroyedObjects(objects); if (array.Any((GameObject item) => item == value)) { return array; } return array.Concat((IEnumerable<GameObject>)(object)new GameObject[1] { value }).ToArray(); } private static Color GetDesiredSlashColor() { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) HeroController instance = HeroController.instance; object obj; if (instance == null) { obj = null; } else { HeroNailImbuement nailImbuement = instance.NailImbuement; obj = ((nailImbuement != null) ? nailImbuement.CurrentImbuement : null); } NailImbuementConfig val = (NailImbuementConfig)obj; if (!((Object)(object)val != (Object)null)) { return Color.white; } return val.NailTintColor; } private static ConfigGroup? ResolveSourceConfigGroup(HeroController hero, string baseCrestName, ToolCrest baseCrest) { CrestData val = FindNeedleforgeCrest(baseCrestName); object obj; if (val == null) { obj = null; } else { MovesetData moveset = val.Moveset; obj = ((moveset != null) ? moveset.ConfigGroup : null); } if (obj != null) { return val.Moveset.ConfigGroup; } string text = (Object.op_Implicit((Object)(object)baseCrest.HeroConfig) ? ((Object)baseCrest.HeroConfig).name : "<null>"); List<string> list = new List<string>(); HashSet<string> candidateConfigNames = GetCandidateConfigNames(baseCrestName, text); foreach (ConfigGroup heroConfig in GetHeroConfigs(hero)) { if (!((Object)(object)heroConfig?.Config == (Object)null)) { list.Add(((Object)heroConfig.Config).name ?? "<unnamed>"); if (heroConfig.Config == baseCrest.HeroConfig) { return heroConfig; } string name = ((Object)heroConfig.Config).name; if (!string.IsNullOrWhiteSpace(name) && candidateConfigNames.Contains(name)) { return heroConfig; } } } Log.Warning("Could not match base crest '" + baseCrestName + "' (hero config name '" + text + "'). Available config groups: " + string.Join(", ", list)); return null; } private static HashSet<string> GetCandidateConfigNames(string baseCrestName, string heroConfigName) { HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); hashSet.Add(baseCrestName); if (!string.IsNullOrWhiteSpace(heroConfigName) && heroConfigName != "<null>") { hashSet.Add(heroConfigName); } if (ConfigAliases.TryGetValue(baseCrestName, out string[] value)) { string[] array = value; foreach (string item in array) { hashSet.Add(item); } } return hashSet; } private static IEnumerable<ConfigGroup> GetHeroConfigs(HeroController hero) { if (HeroConfigsField?.GetValue(hero) is ConfigGroup[] result) { return result; } return Array.Empty<ConfigGroup>(); } private static CrestData? FindNeedleforgeCrest(string crestName) { if (!(typeof(NeedleforgePlugin).GetField("newCrestData", StaticFlags)?.GetValue(null) is IEnumerable enumerable)) { return null; } foreach (object item in enumerable) { CrestData val = (CrestData)((item is CrestData) ? item : null); if (val != null && string.Equals(val.name, crestName, StringComparison.Ordinal)) { return val; } } return null; } private static HeroConfigNeedleforge CloneHeroConfig(HeroControllerConfig source) { HeroConfigNeedleforge val = (HeroConfigNeedleforge)(object)((source is HeroConfigNeedleforge) ? source : null); if (val != null) { HeroConfigNeedleforge obj = Object.Instantiate<HeroConfigNeedleforge>(val); ((Object)obj).name = ((Object)val).name; return obj; } HeroConfigNeedleforge val2 = ScriptableObject.CreateInstance<HeroConfigNeedleforge>(); CopyMatchingFields(source, val2); ((Object)val2).name = ((Object)source).name; return val2; } private static void CopyMatchingFields(object source, object target) { Dictionary<string, FieldInfo> dictionary = GetAllInstanceFields(target.GetType()).GroupBy<FieldInfo, string>((FieldInfo field) => field.Name, StringComparer.Ordinal).ToDictionary<IGrouping<string, FieldInfo>, string, FieldInfo>((IGrouping<string, FieldInfo> group) => group.Key, (IGrouping<string, FieldInfo> group) => group.First(), StringComparer.Ordinal); foreach (FieldInfo allInstanceField in GetAllInstanceFields(source.GetType())) { if (dictionary.TryGetValue(allInstanceField.Name, out var value) && (value.FieldType.IsAssignableFrom(allInstanceField.FieldType) || allInstanceField.FieldType.IsAssignableFrom(value.FieldType))) { value.SetValue(target, allInstanceField.GetValue(source)); } } } private static IEnumerable<FieldInfo> GetAllInstanceFields(Type type) { Type current = type; while (current != null) { FieldInfo[] fields = current.GetFields(InstanceFlags | BindingFlags.DeclaredOnly); foreach (FieldInfo fieldInfo in fields) { if (!fieldInfo.Name.StartsWith("m_", StringComparison.Ordinal)) { yield return fieldInfo; } } current = current.BaseType; } } private static void ReplaceConfigGroupRoot(ConfigGroup target, ConfigGroup source, bool preserveDashStab) { GameObject activeRoot = target.ActiveRoot; HeroController instance = HeroController.instance; bool active = Object.op_Implicit((Object)(object)activeRoot) && activeRoot.activeSelf; if ((Object)(object)instance != (Object)null && CurrentConfigGroupProperty?.GetValue(instance) == target) { active = true; } GameObject val = Object.Instantiate<GameObject>(source.ActiveRoot, source.ActiveRoot.transform.parent); ((Object)val).name = ((Object)source.ActiveRoot).name; val.SetActive(false); target.ActiveRoot = val; string[] configGroupObjectFields = ConfigGroupObjectFields; foreach (string fieldName in configGroupObjectFields) { RemapObjectField(target, source, fieldName, val); } if (!preserveDashStab) { RemapObjectField(target, source, "DashStab", val); } val.SetActive(active); if (Object.op_Implicit((Object)(object)activeRoot) && (Object)(object)activeRoot != (Object)(object)source.ActiveRoot) { activeRoot.SetActive(false); Object.Destroy((Object)(object)activeRoot); } } private static void RestoreTransplantedAttackAudio(ConfigGroup source, ConfigGroup target) { int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; HashSet<int> hashSet = new HashSet<int>(); string[] configGroupObjectFields = ConfigGroupObjectFields; foreach (string text in configGroupObjectFields) { FieldInfo? field = typeof(ConfigGroup).GetField(text, InstanceFlags); object? obj = field?.GetValue(source); GameObject val = (GameObject)((obj is GameObject) ? obj : null); object? obj2 = field?.GetValue(target); GameObject val2 = (GameObject)((obj2 is GameObject) ? obj2 : null); if (!Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)val2) || !hashSet.Add(((Object)val2).GetInstanceID())) { continue; } num++; AudioSource[] componentsInChildren = val.GetComponentsInChildren<AudioSource>(true); num2 += componentsInChildren.Length; AudioSource[] array = componentsInChildren; foreach (AudioSource val3 in array) { string relativePath = GetRelativePath(val.transform, ((Component)val3).transform); Transform val4 = ((relativePath == null) ? null : ((!(relativePath == "")) ? val2.transform.Find(relativePath) : val2.transform)); Transform val5 = val4; if ((Object)(object)val5 == (Object)null) { num4++; Log.Warning("Audio restore could not find '" + text + "/" + (relativePath ?? "<external>") + "' in the transplanted attack tree."); } else { int num5 = Array.IndexOf(((Component)((Component)val3).transform).GetComponents<AudioSource>(), val3); AudioSource[] components = ((Component)val5).GetComponents<AudioSource>(); if (num5 < 0 || num5 >= components.Length) { num4++; Log.Warning($"Audio restore found no matching AudioSource #{num5} at '{text}/{relativePath}'."); } else { CopyAudioSourceSettings(val3, components[num5]); num3++; } } } } Log.Info($"Restored base-crest attack audio: crest={GetActiveBaseCrest()}, objects={num}, " + $"sources={num2}, copied={num3}, missing={num4}; Void Dash/Bind audio preserved."); } private static void CopyAudioSourceSettings(AudioSource source, AudioSource target) { //IL_00da: Unknown result type (might be due to invalid IL or missing references) target.clip = source.clip; target.outputAudioMixerGroup = source.outputAudioMixerGroup; target.mute = source.mute; target.bypassEffects = source.bypassEffects; target.bypassListenerEffects = source.bypassListenerEffects; target.bypassReverbZones = source.bypassReverbZones; target.playOnAwake = source.playOnAwake; target.loop = source.loop; target.priority = source.priority; target.volume = source.volume; target.pitch = source.pitch; target.panStereo = source.panStereo; target.spatialBlend = source.spatialBlend; target.reverbZoneMix = source.reverbZoneMix; target.dopplerLevel = source.dopplerLevel; target.spread = source.spread; target.minDistance = source.minDistance; target.maxDistance = source.maxDistance; target.rolloffMode = source.rolloffMode; target.SetCustomCurve((AudioSourceCurveType)0, source.GetCustomCurve((AudioSourceCurveType)0)); target.SetCustomCurve((AudioSourceCurveType)1, source.GetCustomCurve((AudioSourceCurveType)1)); target.SetCustomCurve((AudioSourceCurveType)2, source.GetCustomCurve((AudioSourceCurveType)2)); target.SetCustomCurve((AudioSourceCurveType)3, source.GetCustomCurve((AudioSourceCurveType)3)); } private static void NormalizeTransplantedDownslashConfig(HeroConfigNeedleforge replacementConfig, GameObject? downSlashObject) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 if ((int)((HeroControllerConfig)replacementConfig).DownSlashType == 2 && !((Object)(object)downSlashObject == (Object)null)) { if ((Object)(object)downSlashObject.GetComponent<Downspike>() != (Object)null) { DownSlashTypeField?.S