Decompiled source of LouderKnelly v1.0.2

LouderKnelly.dll

Decompiled 8 hours ago
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<TsbSpellSpawn>)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<ulong>(((TsbSpellSpawn)(ref spawn)).CasterClientId);
							((LogInfoHandler)(ref val)).AppendLiteral(" not in KnellyBellSet (count=");
							((LogInfoHandler)(ref val)).AppendFormatted<int>(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<ulong>(((TsbSpellSpawn)(ref spawn)).CasterClientId);
						((LogInfoHandler)(ref val)).AppendLiteral(", lifetime=");
						((LogInfoHandler)(ref val)).AppendFormatted<float>(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<ulong>(casterId);
					((LogInfoHandler)(ref val2)).AppendLiteral(" ");
					((LogInfoHandler)(ref val2)).AppendLiteral("(isEndingLifetime=");
					((LogInfoHandler)(ref val2)).AppendFormatted<bool>(val.isEndingLifetime);
					((LogInfoHandler)(ref val2)).AppendLiteral(", isTriggered=");
					((LogInfoHandler)(ref val2)).AppendFormatted<bool>(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<Scene, Scene>? _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<string>("Runtime");
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", ");
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("PinnedGameBuild=");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("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<UnityAction<Scene, Scene>>((Delegate)new Action<Scene, Scene>(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<SpellVariantData> 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<string>(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<string>(sceneName);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' — GetAll(ExplodeAtEnd) -> ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(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<int>(i);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("] Title='");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(((StoreItemData)val2).TitleEntry ?? "<null>");
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' ");
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Desc='");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(((StoreItemData)val2).DescriptionEntry ?? "<null>");
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' ");
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("RepeatAmount=");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(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<string>(sceneName);
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("' -> ");
					((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<Exception>(ex);
				}
				log.LogError(val3);
			}
		}
	}
	internal static class KnellyBellSet
	{
		private static readonly HashSet<ulong> _casterIds = new HashSet<ulong>();

		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<int>? _movementSpeedPercent;

		private static ConfigEntry<bool>? _enabled;

		private static ConfigEntry<bool>? _verboseLogging;

		internal static ConfigEntry<bool>? EnabledEntry => _enabled;

		internal static bool IsEnabled => _enabled?.Value ?? false;

		internal static bool VerboseLogging
		{
			get
			{
				ConfigEntry<bool>? 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>((TsbCharacterClass)4);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<TsbSkinId>((TsbSkinId)1);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(")");
			}
			log.LogInfo(val);
			Bootstrap.Run((BasePlugin)(object)this);
			_enabled = ((BasePlugin)this).Config.Bind<bool>("LouderKnelly", "Enabled", true, "Master toggle for the Louder Knelly mod. When OFF, Knelly plays vanilla.");
			_verboseLogging = ((BasePlugin)this).Config.Bind<bool>("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<int>("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<int>(0, 300), Array.Empty<object>()));
			TsbSettingsMenu.RegisterSection("Louder Knelly", (Action<SettingsSection>)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<TsbCharacterClass>(ctx.CharacterClass);
						((BepInExLogInterpolatedStringHandler)val5).AppendLiteral(", skin=");
						((BepInExLogInterpolatedStringHandler)val5).AppendFormatted<TsbSkinId>(ctx.SkinId);
						((BepInExLogInterpolatedStringHandler)val5).AppendLiteral(", ");
						((BepInExLogInterpolatedStringHandler)val5).AppendLiteral("identity=");
						((BepInExLogInterpolatedStringHandler)val5).AppendFormatted<string>((ctx.Identity == null) ? "null" : "present");
						((BepInExLogInterpolatedStringHandler)val5).AppendLiteral(", ");
						((BepInExLogInterpolatedStringHandler)val5).AppendLiteral("renderers=");
						((BepInExLogInterpolatedStringHandler)val5).AppendFormatted<int>(ctx.Renderers.Length);
					}
					log5.LogInfo(val5);
				}
			};
			CharacterBuild val2 = new CharacterBuild();
			val2.set_Custom((Action<BuildApplyContext>)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<bool>? 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<TsbCharacterClass>(ctx.CharacterClass);
							((BepInExLogInterpolatedStringHandler)val4).AppendLiteral(", ");
							((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<TsbSkinId>(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<TsbCharacterClass>(ctx.CharacterClass);
							((BepInExLogInterpolatedStringHandler)val4).AppendLiteral(", ");
							((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<TsbSkinId>(ctx.SkinId);
							((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("): ");
							((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("ownerClientId=");
							((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<ulong>(num);
							((BepInExLogInterpolatedStringHandler)val4).AppendLiteral(", ");
							((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("MovementSpeed +");
							((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<int>(num2);
							((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("% (passed ");
							((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<int>(num2);
							((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("f), ");
							((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("KnellyBellSet.Count=");
							((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<int>(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<string>(((TsbScene)(ref scene)).RawName);
							((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("': clearing KnellyBellSet (");
							((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<int>(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<SpellInfoVisualizedArgs>)OnBellSpellInfoVisualized);
			TsbInfoPanels.Subscribe((TsbCharacterClass)4, (Action<CharacterInfoVisualizedArgs>)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<GameObject>(statEntryPrefab, statEntryParent);
				((Object)obj).name = "LouderKnellyMoveSpeedBullet";
				obj.SetActive(true);
				TextMeshProUGUI componentInChildren = obj.GetComponentInChildren<TextMeshProUGUI>(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);
				}
			}
		}
	}
}