using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Core.Logging.Interpolation; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using BepInEx.Unity.IL2CPP.Utils.Collections; using Grimoire.Builds; using Grimoire.Events; using Grimoire.Generated; using Grimoire.Reskins; using Grimoire.Spells; using Grimoire.Types; using Grimoire.Ui; using Il2CppInterop.Runtime; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyVersion("0.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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; } } [AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte b) { NullableFlags = new byte[1] { b }; } public NullableAttribute(byte[] b) { NullableFlags = b; } } [AttributeUsage(AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte b) { Flag = b; } } } namespace Grimoire.LouderKnelly { internal static class BellExplodeOnExpirePatch { internal static void Subscribe() { TsbSpellSpawnRegistry.Subscribe((TsbSpellType)4, (Action)OnBellSpawned); } private static void OnBellSpawned(TsbSpellSpawn spawn) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.IsEnabled) { return; } try { LogInfoHandler val; bool flag = default(bool); if (!KnellyBellSet.Contains(((TsbSpellSpawn)(ref spawn)).CasterClientId)) { if (Plugin.VerboseLogging && Log.Source != null) { GrimoireLogSource source = Log.Source; GrimoireLogSource obj = source; val = new LogInfoHandler(80, 2, source, ref flag); if (flag) { ((LogInfoHandler)(ref val)).AppendLiteral("[LouderKnelly] Bell spawned: casterId="); ((LogInfoHandler)(ref val)).AppendFormatted(((TsbSpellSpawn)(ref spawn)).CasterClientId); ((LogInfoHandler)(ref val)).AppendLiteral(" not in KnellyBellSet (count="); ((LogInfoHandler)(ref val)).AppendFormatted(KnellyBellSet.Count); ((LogInfoHandler)(ref val)).AppendLiteral("), skipping"); } obj.LogInfo(val); } return; } Component spawnedInstance = ((TsbSpellSpawn)(ref spawn)).SpawnedInstance; Bell val2 = (Bell)(object)((spawnedInstance is Bell) ? spawnedInstance : null); if ((Object)(object)val2 == (Object)null) { return; } float lifetime = Bell.op_Implicit(val2).lifetime; if (Plugin.VerboseLogging && Log.Source != null) { GrimoireLogSource source = Log.Source; GrimoireLogSource obj2 = source; val = new LogInfoHandler(85, 2, source, ref flag); if (flag) { ((LogInfoHandler)(ref val)).AppendLiteral("[LouderKnelly] Bell spawned: scheduling explode-on-expire for casterId="); ((LogInfoHandler)(ref val)).AppendFormatted(((TsbSpellSpawn)(ref spawn)).CasterClientId); ((LogInfoHandler)(ref val)).AppendLiteral(", lifetime="); ((LogInfoHandler)(ref val)).AppendFormatted(lifetime); ((LogInfoHandler)(ref val)).AppendLiteral("s"); } obj2.LogInfo(val); } ((MonoBehaviour)val2).StartCoroutine(CollectionExtensions.WrapToIl2Cpp(WatchForExpiry(val2, lifetime, ((TsbSpellSpawn)(ref spawn)).CasterClientId))); } catch (Exception ex) { GrimoireLogSource source2 = Log.Source; if (source2 != null) { source2.LogError((object)("[LouderKnelly] BellExplodeOnExpirePatch handler threw: " + ex.Message)); } } } private static IEnumerator WatchForExpiry(Bell bell, float lifetime, ulong casterId) { float num = Mathf.Max(0f, lifetime - 0.1f); yield return (object)new WaitForSeconds(num); if ((Object)(object)bell == (Object)null) { if (Plugin.VerboseLogging) { GrimoireLogSource source = Log.Source; if (source != null) { source.LogInfo((object)"[LouderKnelly] WatchForExpiry: bell destroyed before watcher woke, no-op"); } } yield break; } Bell val = Bell.op_Implicit(bell); if (val.isTriggered) { if (Plugin.VerboseLogging) { GrimoireLogSource source2 = Log.Source; if (source2 != null) { source2.LogInfo((object)"[LouderKnelly] WatchForExpiry: bell was triggered by player, no-op"); } } yield break; } if (Plugin.VerboseLogging && Log.Source != null) { GrimoireLogSource source3 = Log.Source; bool flag = default(bool); LogInfoHandler val2 = new LogInfoHandler(106, 3, source3, ref flag); if (flag) { ((LogInfoHandler)(ref val2)).AppendLiteral("[LouderKnelly] WatchForExpiry: firing ExplosionCoroutine for casterId="); ((LogInfoHandler)(ref val2)).AppendFormatted(casterId); ((LogInfoHandler)(ref val2)).AppendLiteral(" "); ((LogInfoHandler)(ref val2)).AppendLiteral("(isEndingLifetime="); ((LogInfoHandler)(ref val2)).AppendFormatted(val.isEndingLifetime); ((LogInfoHandler)(ref val2)).AppendLiteral(", isTriggered="); ((LogInfoHandler)(ref val2)).AppendFormatted(val.isTriggered); ((LogInfoHandler)(ref val2)).AppendLiteral(")"); } source3.LogInfo(val2); } ((MonoBehaviour)bell).StartCoroutine(val.ExplosionCoroutine(casterId)); } } internal static class BringUpProbe { private static bool _explodeAtEndProbeRan; private static UnityAction? _sceneChangedRaw; private static ManualLogSource? _capturedLog; public static void Run(ManualLogSource log) { log.LogInfo((object)"[BringUpProbe] -- start --"); ProbeBellSurface(log); DeferExplodeAtEndProbe(log); log.LogInfo((object)"[BringUpProbe] -- end (B deferred to first scene) --"); } private static void ProbeBellSurface(ManualLogSource log) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(97, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[BringUpProbe] A: Grimoire.Generated.Bell wrapper compiled OK. "); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("InteropAssembly="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted("Runtime"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", "); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("PinnedGameBuild="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted("1.0.2.16727"); } log.LogInfo(val); log.LogInfo((object)"[BringUpProbe] A: typed surface available -> casterId, ExplosionCoroutine(ulong), EndLifetimeCoroutine()"); } private static void DeferExplodeAtEndProbe(ManualLogSource log) { //IL_0027: 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) _capturedLog = log; _sceneChangedRaw = DelegateSupport.ConvertDelegate>((Delegate)new Action(OnSceneChanged)); SceneManager.activeSceneChanged += _sceneChangedRaw; Scene activeScene = SceneManager.GetActiveScene(); TryProbeOnce(log, ((Scene)(ref activeScene)).name); } private static void OnSceneChanged(Scene previous, Scene next) { if (_capturedLog != null && ((Scene)(ref next)).IsValid()) { TryProbeOnce(_capturedLog, ((Scene)(ref next)).name); } } private static void TryProbeOnce(ManualLogSource log, string sceneName) { //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Expected O, but got Unknown //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Expected O, but got Unknown if (_explodeAtEndProbeRan) { return; } bool flag = default(bool); try { IReadOnlyList all = SpellVariantDataRepository.GetAll((SpellVariant)13); BepInExInfoLogInterpolatedStringHandler val; if (all.Count == 0) { val = new BepInExInfoLogInterpolatedStringHandler(87, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[BringUpProbe] B: scene='"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(sceneName); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' — GetAll(ExplodeAtEnd) still empty, will retry on next scene"); } log.LogInfo(val); return; } _explodeAtEndProbeRan = true; val = new BepInExInfoLogInterpolatedStringHandler(63, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[BringUpProbe] B: scene='"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(sceneName); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' — GetAll(ExplodeAtEnd) -> "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(all.Count); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" match(es)"); } log.LogInfo(val); for (int i = 0; i < all.Count; i++) { SpellVariantData val2 = all[i].Unwrap(); val = new BepInExInfoLogInterpolatedStringHandler(53, 4, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[BringUpProbe] B: ["); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(i); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("] Title='"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(((StoreItemData)val2).TitleEntry ?? ""); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' "); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Desc='"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(((StoreItemData)val2).DescriptionEntry ?? ""); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' "); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("RepeatAmount="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(all[i].RepeatAmount); } log.LogInfo(val); } } catch (Exception ex) { BepInExErrorLogInterpolatedStringHandler val3 = new BepInExErrorLogInterpolatedStringHandler(55, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("[BringUpProbe] B: deferred lookup threw on scene='"); ((BepInExLogInterpolatedStringHandler)val3).AppendFormatted(sceneName); ((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("' -> "); ((BepInExLogInterpolatedStringHandler)val3).AppendFormatted(ex); } log.LogError(val3); } } } internal static class KnellyBellSet { private static readonly HashSet _casterIds = new HashSet(); public static int Count => _casterIds.Count; public static void Add(ulong casterId) { _casterIds.Add(casterId); } public static bool Contains(ulong casterId) { return _casterIds.Contains(casterId); } public static void Clear() { _casterIds.Clear(); } } [BepInPlugin("com.commkicks.grimoire.louderknelly", "Louder Knelly", "1.0.2")] public sealed class Plugin : BasePlugin { public const string PluginId = "com.commkicks.grimoire.louderknelly"; private const TsbCharacterClass MOD_CLASS = 4; private const TsbSkinId MOD_SKIN = 1; private const int defaultValue = 3; private static ConfigEntry? _movementSpeedPercent; private static ConfigEntry? _enabled; private static ConfigEntry? _verboseLogging; internal static ConfigEntry? EnabledEntry => _enabled; internal static bool IsEnabled => _enabled?.Value ?? false; internal static bool VerboseLogging { get { ConfigEntry? verboseLogging = _verboseLogging; if (verboseLogging == null) { return false; } return !verboseLogging.Value; } } internal static int MovementSpeedPercent => _movementSpeedPercent?.Value ?? 3; public override void Load() { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Expected O, but got Unknown //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Expected O, but got Unknown ManualLogSource log = ((BasePlugin)this).Log; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(48, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[LouderKnelly] Load — registering build for ("); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted((TsbCharacterClass)4); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted((TsbSkinId)1); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(")"); } log.LogInfo(val); Bootstrap.Run((BasePlugin)(object)this); _enabled = ((BasePlugin)this).Config.Bind("LouderKnelly", "Enabled", true, "Master toggle for the Louder Knelly mod. When OFF, Knelly plays vanilla."); _verboseLogging = ((BasePlugin)this).Config.Bind("LouderKnelly", "VerboseLogging", false, "Per-spawn diagnostic logging. Off for player installs. Turn on when debugging why a bell didn't explode or why the build callback didn't fire."); _movementSpeedPercent = ((BasePlugin)this).Config.Bind("LouderKnelly", "MovementSpeedPercent", 3, new ConfigDescription("Bonus movement speed granted to BellMage (Knelly) on spawn. Reads as percent-points on the in-run movement-speed tracker. 0 = vanilla, 3 = +3% (default), 100 = +100% (effectively 2x), 300 = +300% (4x). Changes take effect on the next spawn — die or start a new run to feel a new value.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 300), Array.Empty())); TsbSettingsMenu.RegisterSection("Louder Knelly", (Action)delegate(SettingsSection section) { section.AddToggle(_enabled, "Louder Knelly").AddSlider(_movementSpeedPercent, "Bonus Move Speed (%)").DependsOn(_enabled); }); _enabled.SettingChanged += delegate { SpellCardDescriptionPatch.RefreshLiveVisualizers(); }; _movementSpeedPercent.SettingChanged += delegate { SpellCardDescriptionPatch.RefreshLiveVisualizers(); }; BellExplodeOnExpirePatch.Subscribe(); SpellCardDescriptionPatch.Subscribe(); BringUpProbe.Run(((BasePlugin)this).Log); Players.Spawned += delegate(PlayerReskinContext ctx) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) if (VerboseLogging) { ManualLogSource log5 = ((BasePlugin)this).Log; bool flag4 = default(bool); BepInExInfoLogInterpolatedStringHandler val5 = new BepInExInfoLogInterpolatedStringHandler(74, 4, ref flag4); if (flag4) { ((BepInExLogInterpolatedStringHandler)val5).AppendLiteral("[LouderKnelly] Players.Spawned fired: "); ((BepInExLogInterpolatedStringHandler)val5).AppendLiteral("class="); ((BepInExLogInterpolatedStringHandler)val5).AppendFormatted(ctx.CharacterClass); ((BepInExLogInterpolatedStringHandler)val5).AppendLiteral(", skin="); ((BepInExLogInterpolatedStringHandler)val5).AppendFormatted(ctx.SkinId); ((BepInExLogInterpolatedStringHandler)val5).AppendLiteral(", "); ((BepInExLogInterpolatedStringHandler)val5).AppendLiteral("identity="); ((BepInExLogInterpolatedStringHandler)val5).AppendFormatted((ctx.Identity == null) ? "null" : "present"); ((BepInExLogInterpolatedStringHandler)val5).AppendLiteral(", "); ((BepInExLogInterpolatedStringHandler)val5).AppendLiteral("renderers="); ((BepInExLogInterpolatedStringHandler)val5).AppendFormatted(ctx.Renderers.Length); } log5.LogInfo(val5); } }; CharacterBuild val2 = new CharacterBuild(); val2.set_Custom((Action)delegate(BuildApplyContext ctx) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Expected O, but got Unknown //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) ConfigEntry? enabled = _enabled; bool flag3 = default(bool); if (enabled == null || !enabled.Value) { if (VerboseLogging) { ManualLogSource log3 = ((BasePlugin)this).Log; BepInExInfoLogInterpolatedStringHandler val4 = new BepInExInfoLogInterpolatedStringHandler(77, 2, ref flag3); if (flag3) { ((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("[LouderKnelly] Custom callback fired for "); ((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("("); ((BepInExLogInterpolatedStringHandler)val4).AppendFormatted(ctx.CharacterClass); ((BepInExLogInterpolatedStringHandler)val4).AppendLiteral(", "); ((BepInExLogInterpolatedStringHandler)val4).AppendFormatted(ctx.SkinId); ((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("): disabled, skipping all effects"); } log3.LogInfo(val4); } } else { ulong num = ctx.Identity.IGameplayPlayerIdentity_get_OwnerClientId(); KnellyBellSet.Add(num); int num2 = _movementSpeedPercent?.Value ?? 4; if (num2 > 0) { ctx.Stats.AddCharacterImprovement((StatType)7, (float)num2, (StatModifiersType)0); } if (VerboseLogging) { ManualLogSource log4 = ((BasePlugin)this).Log; BepInExInfoLogInterpolatedStringHandler val4 = new BepInExInfoLogInterpolatedStringHandler(112, 6, ref flag3); if (flag3) { ((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("[LouderKnelly] Custom callback fired for "); ((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("("); ((BepInExLogInterpolatedStringHandler)val4).AppendFormatted(ctx.CharacterClass); ((BepInExLogInterpolatedStringHandler)val4).AppendLiteral(", "); ((BepInExLogInterpolatedStringHandler)val4).AppendFormatted(ctx.SkinId); ((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("): "); ((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("ownerClientId="); ((BepInExLogInterpolatedStringHandler)val4).AppendFormatted(num); ((BepInExLogInterpolatedStringHandler)val4).AppendLiteral(", "); ((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("MovementSpeed +"); ((BepInExLogInterpolatedStringHandler)val4).AppendFormatted(num2); ((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("% (passed "); ((BepInExLogInterpolatedStringHandler)val4).AppendFormatted(num2); ((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("f), "); ((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("KnellyBellSet.Count="); ((BepInExLogInterpolatedStringHandler)val4).AppendFormatted(KnellyBellSet.Count); } log4.LogInfo(val4); } } }); BuildRegistry.Register((TsbCharacterClass)4, (TsbSkinId)1, val2); TsbEvents.SceneChanged += delegate(TsbScene scene) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown if (KnellyBellSet.Count > 0) { if (VerboseLogging) { ManualLogSource log2 = ((BasePlugin)this).Log; bool flag2 = default(bool); BepInExInfoLogInterpolatedStringHandler val3 = new BepInExInfoLogInterpolatedStringHandler(58, 2, ref flag2); if (flag2) { ((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("[LouderKnelly] scene='"); ((BepInExLogInterpolatedStringHandler)val3).AppendFormatted(((TsbScene)(ref scene)).RawName); ((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("': clearing KnellyBellSet ("); ((BepInExLogInterpolatedStringHandler)val3).AppendFormatted(KnellyBellSet.Count); ((BepInExLogInterpolatedStringHandler)val3).AppendLiteral(" entries)"); } log2.LogInfo(val3); } KnellyBellSet.Clear(); } }; ((BasePlugin)this).Log.LogInfo((object)"[LouderKnelly] Build registered. Pick Knelly to verify."); } } internal static class SpellCardDescriptionPatch { private const string ExpireBulletText = "Spell explodes when its lifetime expires."; private const string LocTable = "Localization_Strings"; private const string ExpireMarker = "LouderKnellyExplodeOnExpireBullet"; private const string MoveSpeedMarker = "LouderKnellyMoveSpeedBullet"; private static CharacterUIVisualizer? _liveCharacterVisualizer; private static SpellUIVisualizer? _liveSpellVisualizer; internal static void Subscribe() { TsbInfoPanels.SpellInfoSkipVote += OnSpellInfoSkipVote; TsbInfoPanels.Subscribe((TsbSpellType)4, (Action)OnBellSpellInfoVisualized); TsbInfoPanels.Subscribe((TsbCharacterClass)4, (Action)OnBellMageCharacterInfoVisualized); } private static void OnSpellInfoSkipVote(SpellInfoSkipVoteArgs args) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Invalid comparison between Unknown and I4 if (args.IsCharacterInfoPanelSurface && (int)args.SpellType == 4 && (Object)(object)args.Visualizer.CurrentSpellData == (Object)(object)SpellData.op_Implicit(args.SpellData)) { args.SkipVanillaRebuild = true; } } private static void OnBellSpellInfoVisualized(SpellInfoVisualizedArgs args) { if (args.IsCharacterInfoPanelSurface) { _liveSpellVisualizer = args.Visualizer.Unwrap(); if (Plugin.IsEnabled) { ApplySpellBullets(args.Visualizer); } } } private static void OnBellMageCharacterInfoVisualized(CharacterInfoVisualizedArgs args) { _liveCharacterVisualizer = args.Visualizer.Unwrap(); if (Plugin.IsEnabled) { ApplyTraitBullets(args.Visualizer); } } public static void RefreshLiveVisualizers() { try { if ((Object)(object)_liveCharacterVisualizer == (Object)null) { _liveCharacterVisualizer = null; } else if (Plugin.IsEnabled) { ApplyTraitBullets(CharacterUIVisualizer.op_Implicit(_liveCharacterVisualizer)); } else { DestroyMarkers(CharacterUIVisualizer.op_Implicit(_liveCharacterVisualizer).statEntryParent); } if ((Object)(object)_liveSpellVisualizer == (Object)null) { _liveSpellVisualizer = null; } else if (Plugin.IsEnabled) { ApplySpellBullets(SpellUIVisualizer.op_Implicit(_liveSpellVisualizer)); } else { DestroyMarkers(SpellUIVisualizer.op_Implicit(_liveSpellVisualizer).statEntryParent); } } catch (Exception ex) { GrimoireLogSource source = Log.Source; if (source != null) { source.LogError((object)("[LouderKnelly] RefreshLiveVisualizers threw: " + ex.Message)); } } } private static void ApplyTraitBullets(CharacterUIVisualizer wrapped) { Transform statEntryParent = wrapped.statEntryParent; if ((Object)(object)statEntryParent == (Object)null) { return; } DestroyMarkersByName(statEntryParent, "LouderKnellyMoveSpeedBullet"); int movementSpeedPercent = Plugin.MovementSpeedPercent; if (movementSpeedPercent <= 0) { return; } GameObject statEntryPrefab = wrapped.statEntryPrefab; if (!((Object)(object)statEntryPrefab == (Object)null)) { GameObject obj = Object.Instantiate(statEntryPrefab, statEntryParent); ((Object)obj).name = "LouderKnellyMoveSpeedBullet"; obj.SetActive(true); TextMeshProUGUI componentInChildren = obj.GetComponentInChildren(true); if ((Object)(object)componentInChildren != (Object)null) { string value = TsbLoc.Resolve("Localization_Strings", "CharacterProperty_Increased_MovementSpeed", "Movement speed"); ((TMP_Text)componentInChildren).text = $"+{movementSpeedPercent}% {value}"; } } } private static void ApplySpellBullets(SpellUIVisualizer wrapped) { Transform statEntryParent = wrapped.statEntryParent; if ((Object)(object)statEntryParent == (Object)null) { return; } DestroyMarkersByName(statEntryParent, "LouderKnellyExplodeOnExpireBullet"); wrapped.CreateStatEntry("Spell explodes when its lifetime expires."); if (statEntryParent.childCount > 0) { Transform child = statEntryParent.GetChild(statEntryParent.childCount - 1); if ((Object)(object)child != (Object)null) { ((Object)((Component)child).gameObject).name = "LouderKnellyExplodeOnExpireBullet"; } } } private static void DestroyMarkers(Transform statParent) { if (!((Object)(object)statParent == (Object)null)) { DestroyMarkersByName(statParent, "LouderKnellyMoveSpeedBullet"); DestroyMarkersByName(statParent, "LouderKnellyExplodeOnExpireBullet"); } } private static void DestroyMarkersByName(Transform statParent, string markerName) { for (int num = statParent.childCount - 1; num >= 0; num--) { Transform child = statParent.GetChild(num); if ((Object)(object)child != (Object)null && ((Object)child).name == markerName) { Object.Destroy((Object)(object)((Component)child).gameObject); } } } } }