Decompiled source of Snatcher over Anima Harvester boss v1.0.1

HatKidSoulbound.dll

Decompiled 12 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HG.GeneralSerializer;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using R2API;
using RiskOfOptions;
using RiskOfOptions.OptionConfigs;
using RiskOfOptions.Options;
using RoR2;
using RoR2.ConVar;
using RoR2.Projectile;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.AddressableAssets.ResourceLocators;
using UnityEngine.Rendering;
using UnityEngine.Rendering.PostProcessing;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace HatKidSoulbound
{
	internal static class BossEffects
	{
		private const string BossFolder = "RoR2/DLC4/ReaperBoss/";

		private static readonly string[] ExtraPrefabs = new string[9] { "RoR2/DLC4/champrush/ReaperRevivalFX.prefab", "RoR2/DLC4/champrush/ChampRushReaperSpawnVFX.prefab", "RoR2/DLC4/MalachiteSpikeEffect.prefab", "RoR2/DLC4/MalachiteSpikeGroundEffect.prefab", "RoR2/DLC4/MalachiteSpikeGrower.prefab", "RoR2/DLC4/MalachiteSpikeGrowerProjectileInitialGhost.prefab", "RoR2/DLC4/MalachiteSpikePredictionEffect.prefab", "RoR2/DLC4/ReaperBossAssailSlashMalachiteProjectile.prefab", "RoR2/DLC4/ReaperBossAssailSlashMalachiteProjectileRightVariant.prefab" };

		private static readonly string[] BossBodies = new string[2] { "RoR2/Junk_DLC4/WightProjectile/WightProjectileReaper.prefab", "RoR2/DLC4/ReaperBoss/ReaperBossSoulBody.prefab" };

		private static readonly string[] SharedProjectiles = new string[2] { "RoR2/Base/ElitePoison/PoisonStakeProjectile.prefab", "RoR2/Base/ElitePoison/UrchinSeekingProjectile.prefab" };

		private const string StageFolder = "RoR2/DLC4/champrush/";

		private static readonly string[] StagePrefixes = new string[14]
		{
			"CR_", "ChampRushOrb", "ChamprushOrb", "ChampRushReaper", "ChampRushRitual", "ChampRushPurgatory", "ChamprushFireNado", "ChamprushPitFire", "ReaperRevivalFX", "TeleportOutBoom",
			"AcoWightSkullCS_VFX", "Champrush_DoorPortal", "PortalToUltima", "Holder_ Quadrant"
		};

		private static readonly Regex Guid = new Regex("^[0-9a-f]{32}$");

		private static readonly List<GameObject> held = new List<GameObject>();

		private static readonly Dictionary<GameObject, GameObject> copies = new Dictionary<GameObject, GameObject>();

		internal static readonly HashSet<string> sharedProjectileNames = new HashSet<string>();

		public static void Setup()
		{
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01db: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0386: Unknown result type (might be due to invalid IL or missing references)
			//IL_0396: Unknown result type (might be due to invalid IL or missing references)
			//IL_039b: Unknown result type (might be due to invalid IL or missing references)
			Stopwatch stopwatch = Stopwatch.StartNew();
			HashSet<string> hashSet = new HashSet<string>();
			foreach (IResourceLocator resourceLocator in Addressables.ResourceLocators)
			{
				foreach (object key in resourceLocator.Keys)
				{
					if (key is string text && text.StartsWith("RoR2/DLC4/"))
					{
						hashSet.Add(text);
					}
				}
			}
			HashSet<GameObject> hashSet2 = new HashSet<GameObject>();
			foreach (string item2 in from k in hashSet.Where((string k) => k.EndsWith(".prefab") && k.StartsWith("RoR2/DLC4/ReaperBoss/")).Concat(ExtraPrefabs)
				orderby k
				select k)
			{
				Add(hashSet2, TryLoad(item2));
			}
			foreach (string item3 in hashSet.Where((string k) => k.EndsWith(".asset") && (k.StartsWith("RoR2/DLC4/ReaperBoss/EntityStates.") || k.Contains("/EntityStates.ChampRushReaper."))))
			{
				EntityStateConfiguration val = null;
				try
				{
					val = Addressables.LoadAssetAsync<EntityStateConfiguration>((object)item3).WaitForCompletion();
				}
				catch
				{
				}
				if (!Object.op_Implicit((Object)(object)val))
				{
					continue;
				}
				SerializedField[] serializedFields = val.serializedFieldsCollection.serializedFields;
				foreach (SerializedField val2 in serializedFields)
				{
					Object objectValue = val2.fieldValue.objectValue;
					GameObject val3 = (GameObject)(object)((objectValue is GameObject) ? objectValue : null);
					if (val3 != null)
					{
						Add(hashSet2, val3);
					}
					else if (val2.fieldValue.stringValue != null && Guid.IsMatch(val2.fieldValue.stringValue))
					{
						Add(hashSet2, TryLoad(val2.fieldValue.stringValue));
					}
				}
			}
			int num2 = 0;
			foreach (GameObject item4 in hashSet2)
			{
				if (Purple.All(item4))
				{
					num2++;
				}
			}
			string[] bossBodies = BossBodies;
			for (int num = 0; num < bossBodies.Length; num++)
			{
				GameObject val4 = TryLoad(bossBodies[num]);
				if (Object.op_Implicit((Object)(object)val4))
				{
					held.Add(val4);
					PurpleBody(val4);
					num2++;
					if (Object.op_Implicit((Object)(object)val4.GetComponent<ProjectileController>()))
					{
						hashSet2.Add(val4);
					}
				}
			}
			int num3 = 0;
			foreach (string item5 in hashSet.Where((string k) => k.StartsWith("RoR2/DLC4/champrush/") && k.EndsWith(".prefab") && StagePrefixes.Any((string p) => k.Substring("RoR2/DLC4/champrush/".Length).StartsWith(p))))
			{
				GameObject val5 = TryLoad(item5);
				if (!Object.op_Implicit((Object)(object)val5) || hashSet2.Contains(val5))
				{
					continue;
				}
				held.Add(val5);
				Renderer[] componentsInChildren = val5.GetComponentsInChildren<Renderer>(true);
				foreach (Renderer r in componentsInChildren)
				{
					Purple.Renderer(r, !IsEffect(r));
				}
				ParticleSystem[] componentsInChildren2 = val5.GetComponentsInChildren<ParticleSystem>(true);
				for (int num = 0; num < componentsInChildren2.Length; num++)
				{
					Purple.Particles(componentsInChildren2[num]);
				}
				Light[] componentsInChildren3 = val5.GetComponentsInChildren<Light>(true);
				foreach (Light val6 in componentsInChildren3)
				{
					if (Purple.IsColoured(val6.color))
					{
						val6.color = Purple.Shift(val6.color);
					}
				}
				num3++;
			}
			int num4 = 0;
			foreach (GameObject item6 in hashSet2.Where((GameObject g) => Object.op_Implicit((Object)(object)g.GetComponent<ProjectileController>())).ToList())
			{
				foreach (var (obj2, fieldInfo, val7) in EffectFields(item6).ToList())
				{
					if (!hashSet2.Contains(val7) && !Object.op_Implicit((Object)(object)val7.GetComponent<ProjectileController>()) && !Object.op_Implicit((Object)(object)val7.GetComponent<CharacterBody>()))
					{
						GameObject val8 = Copy(val7);
						if ((Object)(object)val8 != (Object)(object)val7)
						{
							fieldInfo.SetValue(obj2, val8);
							num4++;
						}
					}
				}
				ProjectileController component = item6.GetComponent<ProjectileController>();
				GameObject val9 = Ghost(component);
				if (Object.op_Implicit((Object)(object)val9) && !hashSet2.Contains(val9))
				{
					GameObject val10 = Copy(val9);
					if ((Object)(object)val10 != (Object)(object)val9)
					{
						SetGhost(component, val10);
						num4++;
					}
				}
			}
			bossBodies = SharedProjectiles;
			for (int num = 0; num < bossBodies.Length; num++)
			{
				GameObject val11 = TryLoad(bossBodies[num]);
				ProjectileController val12 = (Object.op_Implicit((Object)(object)val11) ? val11.GetComponent<ProjectileController>() : null);
				if (!Object.op_Implicit((Object)(object)val12))
				{
					continue;
				}
				held.Add(val11);
				sharedProjectileNames.Add(((Object)val11).name);
				foreach (var item7 in EffectFields(val11).ToList())
				{
					GameObject item = item7.Item3;
					if (!Object.op_Implicit((Object)(object)item.GetComponent<ProjectileController>()) && !Object.op_Implicit((Object)(object)item.GetComponent<CharacterBody>()))
					{
						Copy(item);
					}
				}
				GameObject val13 = Ghost(val12);
				if (Object.op_Implicit((Object)(object)val13))
				{
					Copy(val13);
				}
			}
			HatKidSoulboundPlugin.Log.LogInfo((object)($"Purple boss effects: {hashSet2.Count} boss prefabs ({num2} recoloured), {num4} shared effects copied, " + $"{num3} stage effects, {sharedProjectileNames.Count} shared projectiles ({stopwatch.ElapsedMilliseconds} ms)"));
		}

		internal static bool IsEffect(Renderer r)
		{
			if (!(r is ParticleSystemRenderer) && !(r is TrailRenderer) && !(r is LineRenderer))
			{
				return r.sharedMaterials.Any((Material m) => Object.op_Implicit((Object)(object)m) && Object.op_Implicit((Object)(object)m.shader) && ((Object)m.shader).name.Contains("/FX/"));
			}
			return true;
		}

		private static IEnumerable<(Component, FieldInfo, GameObject)> EffectFields(GameObject go)
		{
			Component[] componentsInChildren = go.GetComponentsInChildren<Component>(true);
			foreach (Component c in componentsInChildren)
			{
				if (!Object.op_Implicit((Object)(object)c) || c is Transform || ((object)c).GetType().Namespace == null || !((object)c).GetType().Namespace.StartsWith("RoR2"))
				{
					continue;
				}
				FieldInfo[] fields = ((object)c).GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (FieldInfo fieldInfo in fields)
				{
					if (fieldInfo.FieldType != typeof(GameObject))
					{
						continue;
					}
					object? value = fieldInfo.GetValue(c);
					GameObject val = (GameObject)((value is GameObject) ? value : null);
					if (Object.op_Implicit((Object)(object)val))
					{
						Scene scene = val.scene;
						if (!((Scene)(ref scene)).IsValid() && !val.transform.IsChildOf(go.transform))
						{
							yield return (c, fieldInfo, val);
						}
					}
				}
			}
		}

		internal static void BeforeStart(ProjectileController pc)
		{
			foreach (var (obj, fieldInfo, val) in EffectFields(((Component)pc).gameObject).ToList())
			{
				if (copies.TryGetValue(val, out var value) && (Object)(object)value != (Object)(object)val)
				{
					fieldInfo.SetValue(obj, value);
				}
			}
			GameObject val2 = Ghost(pc);
			if (Object.op_Implicit((Object)(object)val2) && copies.TryGetValue(val2, out var value2) && (Object)(object)value2 != (Object)(object)val2)
			{
				SetGhost(pc, value2, log: false);
			}
		}

		internal static void AfterStart(ProjectileController pc)
		{
			Purple.All(((Component)pc).gameObject);
		}

		internal static void PurpleBody(GameObject body)
		{
			Purple.All(body);
			ColorParticlesWithBodyColor[] componentsInChildren = body.GetComponentsInChildren<ColorParticlesWithBodyColor>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				((Behaviour)componentsInChildren[i]).enabled = false;
			}
			ModelLocator component = body.GetComponent<ModelLocator>();
			Transform val = ((component != null) ? component.modelTransform : null);
			CharacterModel val2 = (Object.op_Implicit((Object)(object)val) ? ((Component)val).GetComponent<CharacterModel>() : null);
			if (!Object.op_Implicit((Object)(object)val2))
			{
				return;
			}
			for (int j = 0; j < val2.baseRendererInfos.Length; j++)
			{
				if (Object.op_Implicit((Object)(object)val2.baseRendererInfos[j].defaultMaterial))
				{
					val2.baseRendererInfos[j].defaultMaterial = Purple.Material(val2.baseRendererInfos[j].defaultMaterial);
				}
			}
		}

		private static void Add(HashSet<GameObject> set, GameObject go)
		{
			if (Object.op_Implicit((Object)(object)go) && !Object.op_Implicit((Object)(object)go.GetComponent<CharacterBody>()) && !Object.op_Implicit((Object)(object)go.GetComponent<CharacterMaster>()) && set.Add(go))
			{
				held.Add(go);
			}
		}

		private static GameObject TryLoad(string key)
		{
			//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)
			try
			{
				return Addressables.LoadAssetAsync<GameObject>((object)key).WaitForCompletion();
			}
			catch
			{
				return null;
			}
		}

		private static GameObject Copy(GameObject original)
		{
			if (copies.TryGetValue(original, out var value))
			{
				return value;
			}
			if (copies.ContainsValue(original))
			{
				return original;
			}
			GameObject val = original;
			GameObject val2 = PrefabAPI.InstantiateClone(original, ((Object)original).name + "HatKidPurple", false);
			if (Purple.All(val2) && (!Object.op_Implicit((Object)(object)val2.GetComponent<EffectComponent>()) || ContentAddition.AddEffect(val2)))
			{
				val = val2;
			}
			else
			{
				Object.Destroy((Object)(object)val2);
			}
			copies[original] = val;
			return val;
		}

		private static void SetGhost(ProjectileController pc, GameObject ghost, bool log = true)
		{
			pc.ghostPrefab = ghost;
			FieldInfo[] fields = typeof(ProjectileController).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (FieldInfo fieldInfo in fields)
			{
				if (fieldInfo.Name.IndexOf("ghost", StringComparison.OrdinalIgnoreCase) >= 0 && typeof(AssetReference).IsAssignableFrom(fieldInfo.FieldType))
				{
					fieldInfo.SetValue(pc, Activator.CreateInstance(fieldInfo.FieldType, ""));
				}
			}
			if (log)
			{
				HatKidSoulboundPlugin.Log.LogInfo((object)("Boss projectile " + ((Object)pc).name + ": purple copy of its shared ghost (" + ((Object)ghost).name + ")"));
			}
		}

		private static GameObject Ghost(ProjectileController pc)
		{
			if (Object.op_Implicit((Object)(object)pc.ghostPrefab))
			{
				return pc.ghostPrefab;
			}
			FieldInfo[] fields = typeof(ProjectileController).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (FieldInfo fieldInfo in fields)
			{
				if (fieldInfo.Name.IndexOf("ghost", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					object? value = fieldInfo.GetValue(pc);
					AssetReference val = (AssetReference)((value is AssetReference) ? value : null);
					if (val != null && val.RuntimeKeyIsValid())
					{
						return TryLoad(val.RuntimeKey.ToString());
					}
				}
			}
			return null;
		}
	}
	internal static class BossFight
	{
		private const string NameToken = "CHAZ_HATKIDSOULBOUND_BOSS_NAME";

		private const string SubtitleToken = "CHAZ_HATKIDSOULBOUND_BOSS_SUBTITLE";

		private const string MusicRtpc = "Volume_MSX";

		private const int BossTrack = 0;

		private const int TimePieceTrack = 1;

		private const float PhaseGap = 30f;

		private const float RiffLength = 7.2f;

		private const float BossFadeOut = 0.4f;

		private const float SongFadeOut = 3f;

		internal static byte[] spawnVoice;

		internal static byte[] deathRiff;

		private static readonly Dictionary<string, (string name, string subtitle)> originalTokens = new Dictionary<string, (string, string)>();

		private static float lastBossSeen = -999f;

		private static float killedAt = -1f;

		private static bool finalPhaseSeen;

		private static bool finalPhaseAlive;

		private static bool mutedGameMusic;

		private static bool ownCall;

		private static float reportAt = -1f;

		private static float gameMusicVolume = 1f;

		private static float dim = 1f;

		private static long framesAtStart;

		private static uint musicRtpcId;

		public static void Setup(Harmony harmony)
		{
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Expected O, but got Unknown
			LanguageAPI.Add("CHAZ_HATKIDSOULBOUND_BOSS_NAME", "The Snatcher");
			LanguageAPI.Add("CHAZ_HATKIDSOULBOUND_BOSS_SUBTITLE", "The Soulbound Contractor");
			CharacterBody.onBodyStartGlobal += OnBodyStart;
			SceneManager.activeSceneChanged += delegate
			{
				lastBossSeen = -999f;
				killedAt = -1f;
				finalPhaseSeen = (finalPhaseAlive = false);
			};
			if (LoadTrack(0, "your_contract_has_expired.snm") & LoadTrack(1, "timepiece_released.snm"))
			{
				MusicPlayer.Start();
			}
			HarmonyMethod val = new HarmonyMethod(typeof(BossFight).GetMethod("SetRtpcPrefix", BindingFlags.Static | BindingFlags.NonPublic));
			int num = 0;
			MethodInfo[] methods = typeof(AkSoundEngine).GetMethods(BindingFlags.Static | BindingFlags.Public);
			foreach (MethodInfo methodInfo in methods)
			{
				if (methodInfo.Name != "SetRTPCValue")
				{
					continue;
				}
				ParameterInfo[] parameters = methodInfo.GetParameters();
				if (parameters.Length >= 2 && !(parameters[1].Name != "in_value") && !(parameters[1].ParameterType != typeof(float)))
				{
					try
					{
						harmony.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						num++;
					}
					catch (Exception ex)
					{
						HatKidSoulboundPlugin.Log.LogWarning((object)("Couldn't hook a music volume call: " + ex.Message));
					}
				}
			}
			HatKidSoulboundPlugin.Log.LogInfo((object)$"Music: hooked {num} music volume calls");
		}

		private static bool LoadTrack(int slot, string resource)
		{
			using (Stream stream = typeof(Assets).Assembly.GetManifestResourceStream(resource))
			{
				using MemoryStream memoryStream = new MemoryStream();
				stream.CopyTo(memoryStream);
				if (MusicPlayer.Load(slot, memoryStream.ToArray()))
				{
					return true;
				}
			}
			HatKidSoulboundPlugin.Log.LogError((object)("Couldn't read " + resource));
			return false;
		}

		public static void Shutdown()
		{
			MusicPlayer.Wanted = false;
			MusicPlayer.Stop();
			RestoreGameMusic();
		}

		internal static bool IsBoss(CharacterBody b)
		{
			if (Object.op_Implicit((Object)(object)b) && ((Object)b).name.StartsWith("ReaperBoss"))
			{
				return !((Object)b).name.StartsWith("ReaperBossSoul");
			}
			return false;
		}

		private static void OnBodyStart(CharacterBody body)
		{
			if (IsBoss(body))
			{
				Title(body);
				if (((Object)body).name.StartsWith("ReaperBossBody") && HatKidSoulboundPlugin.SnatcherVoice)
				{
					Voice.Play(spawnVoice, HatKidSoulboundPlugin.FoolVolume * 1.035f);
				}
			}
		}

		private static void Title(CharacterBody body)
		{
			string key = ((Object)body).name.Replace("(Clone)", "");
			if (!originalTokens.TryGetValue(key, out (string, string) value))
			{
				if (body.baseNameToken == "CHAZ_HATKIDSOULBOUND_BOSS_NAME")
				{
					return;
				}
				value = (originalTokens[key] = (body.baseNameToken, body.subtitleNameToken));
			}
			bool replaceBossTitle = HatKidSoulboundPlugin.ReplaceBossTitle;
			object baseNameToken;
			if (!replaceBossTitle)
			{
				(baseNameToken, _) = value;
			}
			else
			{
				baseNameToken = "CHAZ_HATKIDSOULBOUND_BOSS_NAME";
			}
			body.baseNameToken = (string)baseNameToken;
			body.subtitleNameToken = (replaceBossTitle ? "CHAZ_HATKIDSOULBOUND_BOSS_SUBTITLE" : value.Item2);
		}

		public static void Update()
		{
			bool inSoulbound = BossRuntime.InSoulbound;
			bool flag = false;
			bool flag2 = false;
			if (inSoulbound)
			{
				foreach (CharacterBody readOnlyInstances in CharacterBody.readOnlyInstancesList)
				{
					if (IsBoss(readOnlyInstances) && Object.op_Implicit((Object)(object)readOnlyInstances.healthComponent) && readOnlyInstances.healthComponent.alive)
					{
						flag = true;
						if (((Object)readOnlyInstances).name.Contains("Malachite"))
						{
							finalPhaseSeen = true;
							flag2 = true;
						}
						Title(readOnlyInstances);
					}
				}
			}
			if (flag)
			{
				lastBossSeen = Time.time;
			}
			if (finalPhaseAlive && !flag2 && killedAt < 0f)
			{
				killedAt = Time.time;
				HatKidSoulboundPlugin.Log.LogInfo((object)"Snatcher defeated: boss-death riff");
				if (HatKidSoulboundPlugin.ReplaceMusic)
				{
					Voice.PlayAt(deathRiff, MusicVolume());
				}
			}
			finalPhaseAlive = flag2;
			bool flag3 = inSoulbound && killedAt < 0f && (flag || (!finalPhaseSeen && Time.time - lastBossSeen < 30f));
			bool flag4 = inSoulbound && killedAt >= 0f;
			bool replaceMusic = HatKidSoulboundPlugin.ReplaceMusic;
			bool flag5 = replaceMusic && (flag3 || (flag4 && Time.time >= killedAt + 7.2f));
			int num = (flag4 ? 1 : 0);
			if (flag5 != MusicPlayer.Wanted || (flag5 && num != MusicPlayer.WantedTrack))
			{
				HatKidSoulboundPlugin.Log.LogInfo((object)((!flag5) ? "Music: stopping" : ((num == 0) ? "Music: Your Contract Has Expired" : "Music: Time Piece")));
				if (flag5)
				{
					reportAt = Time.unscaledTime + 5f;
					framesAtStart = MusicPlayer.FramesWritten;
				}
			}
			if (reportAt > 0f && Time.unscaledTime >= reportAt)
			{
				reportAt = -1f;
				HatKidSoulboundPlugin.Log.LogInfo((object)($"Music: {(float)(MusicPlayer.FramesWritten - framesAtStart) / 44100f:0.0} s streamed, " + $"peak {MusicPlayer.LastPeak}, volume {MusicPlayer.Volume:0.00} (music setting {HatKidSoulboundPlugin.MusicVolume:0.00}, " + string.Format("master {0:0.00}, game music {1:0.00}, dimmed {2:0.00})", Voice.GameVolume("volume_master"), gameMusicVolume, dim)));
			}
			MusicPlayer.FadeOutSeconds = (flag4 ? 0.4f : 3f);
			MusicPlayer.WantedTrack = num;
			MusicPlayer.Wanted = flag5;
			MusicPlayer.Paused = PauseManager.isPaused;
			if (!mutedGameMusic)
			{
				gameMusicVolume = Voice.GameVolume("volume_music");
			}
			MusicPlayer.Volume = MusicVolume() * dim;
			if (MusicPlayer.Wanted || MusicPlayer.Playing || (replaceMusic && flag4))
			{
				if (!mutedGameMusic)
				{
					dim = 1f;
				}
				mutedGameMusic = true;
				SetMusicRtpc(0f);
			}
			else
			{
				RestoreGameMusic();
			}
		}

		private static float MusicVolume()
		{
			return HatKidSoulboundPlugin.MusicVolume * Voice.GameVolume("volume_master") * gameMusicVolume;
		}

		private static void RestoreGameMusic()
		{
			if (mutedGameMusic)
			{
				mutedGameMusic = false;
				dim = 1f;
				SetMusicRtpc(gameMusicVolume * 100f);
			}
		}

		private static void SetMusicRtpc(float value)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			ownCall = true;
			try
			{
				AkSoundEngine.SetRTPCValue("Volume_MSX", value);
			}
			finally
			{
				ownCall = false;
			}
		}

		private static void SetRtpcPrefix(object[] __args, ref float in_value)
		{
			if (ownCall || !mutedGameMusic)
			{
				return;
			}
			bool num2;
			if (!(__args[0] is string text))
			{
				if (!(__args[0] is uint num))
				{
					return;
				}
				num2 = num == ((musicRtpcId != 0) ? musicRtpcId : (musicRtpcId = AkSoundEngine.GetIDFromString("Volume_MSX")));
			}
			else
			{
				num2 = text == "Volume_MSX";
			}
			if (num2)
			{
				float num3 = ((gameMusicVolume > 0.001f) ? Mathf.Clamp01(in_value / (gameMusicVolume * 100f)) : 1f);
				if (Mathf.Abs(num3 - dim) > 0.01f)
				{
					HatKidSoulboundPlugin.Log.LogInfo((object)$"Music: another mod set the game's music to {in_value:0}; kept muted, song at {num3:0%}");
				}
				dim = num3;
				in_value = 0f;
			}
		}
	}
	internal static class BossRuntime
	{
		[HarmonyPatch(typeof(ProjectileController), "Start")]
		private static class ProjectilePatches
		{
			private static void Prefix(ProjectileController __instance, out bool __state)
			{
				__state = false;
				try
				{
					if (Object.op_Implicit((Object)(object)__instance) && Shared(__instance))
					{
						Once(string.Format("shared projectile {0} owner={1} bossSide={2}", ((Object)__instance).name, Object.op_Implicit((Object)(object)__instance.owner) ? ((Object)__instance.owner).name : "none", BossSide(__instance.owner)));
						if (BossSide(__instance.owner))
						{
							__state = true;
							BossEffects.BeforeStart(__instance);
						}
					}
				}
				catch (Exception ex)
				{
					HatKidSoulboundPlugin.Log.LogWarning((object)("Purple projectile: " + ex.Message));
				}
			}

			private static void Postfix(ProjectileController __instance, bool __state)
			{
				if (__state && Object.op_Implicit((Object)(object)__instance))
				{
					BossEffects.AfterStart(__instance);
				}
			}
		}

		private const string ScreamEvent = "Play_Champ_Reaper_Phase2Transistion";

		private const float ScreamDelay = 5f;

		internal const float VoiceGain = 1.035f;

		internal static byte[] foolVoice;

		private static float foolAt = -1f;

		private static readonly HashSet<string> logged = new HashSet<string>();

		internal static bool InSoulbound
		{
			get
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0005: Unknown result type (might be due to invalid IL or missing references)
				Scene activeScene = SceneManager.GetActiveScene();
				return ((Scene)(ref activeScene)).name == "champrush";
			}
		}

		public static void Setup(Harmony harmony)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			if (HatKidSoulboundPlugin.RecolouredEffects)
			{
				harmony.PatchAll(typeof(ProjectilePatches));
			}
			HarmonyMethod val = new HarmonyMethod(typeof(BossRuntime).GetMethod("PostEventPrefix", BindingFlags.Static | BindingFlags.NonPublic));
			int num = 0;
			MethodInfo[] methods = typeof(AkSoundEngine).GetMethods(BindingFlags.Static | BindingFlags.Public);
			foreach (MethodInfo methodInfo in methods)
			{
				if (!(methodInfo.Name != "PostEvent") && !(methodInfo.ReturnType != typeof(uint)))
				{
					ParameterInfo[] parameters = methodInfo.GetParameters();
					if (parameters.Length >= 2 && !(parameters[0].ParameterType != typeof(string)))
					{
						harmony.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						num++;
					}
				}
			}
			HatKidSoulboundPlugin.Log.LogInfo((object)$"Scream replacement: hooked {num} sound calls");
			if (HatKidSoulboundPlugin.RecolouredEffects)
			{
				CharacterBody.onBodyStartGlobal += OnBodyStart;
			}
		}

		private static bool PostEventPrefix(object[] __args, ref uint __result)
		{
			if (!(__args[0] is string a) || !string.Equals(a, "Play_Champ_Reaper_Phase2Transistion", StringComparison.OrdinalIgnoreCase) || !HatKidSoulboundPlugin.SnatcherVoice)
			{
				return true;
			}
			foolAt = Time.time + 5f;
			__result = 0u;
			return false;
		}

		public static void Update()
		{
			if (!(foolAt < 0f) && !(Time.time < foolAt))
			{
				foolAt = -1f;
				HatKidSoulboundPlugin.Log.LogInfo((object)"Phase 2: FOOOOOOOOL instead of the scream");
				Voice.Play(foolVoice, HatKidSoulboundPlugin.FoolVolume * 1.035f);
			}
		}

		private static bool BossAlive()
		{
			return CharacterBody.readOnlyInstancesList.Any((CharacterBody b) => Object.op_Implicit((Object)(object)b) && IsBoss(b) && Object.op_Implicit((Object)(object)b.healthComponent) && b.healthComponent.alive);
		}

		private static bool IsBoss(CharacterBody b)
		{
			if (((Object)b).name.StartsWith("ReaperBoss"))
			{
				return !((Object)b).name.StartsWith("ReaperBossSoul");
			}
			return false;
		}

		private static bool BossSide(GameObject owner)
		{
			CharacterBody val = (Object.op_Implicit((Object)(object)owner) ? owner.GetComponent<CharacterBody>() : null);
			if (Object.op_Implicit((Object)(object)val))
			{
				if (!IsBoss(val))
				{
					return Object.op_Implicit((Object)(object)((Component)val).GetComponent<PurpleMinion>());
				}
				return true;
			}
			return false;
		}

		private static void OnBodyStart(CharacterBody body)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			if (((Object)body).name.StartsWith("UrchinTurretBody"))
			{
				bool flag = InSoulbound && BossAlive() && !Object.op_Implicit((Object)(object)((Component)body).GetComponent<PurpleMinion>());
				string[] obj = new string[6]
				{
					"urchin ",
					flag.ToString(),
					" scene=",
					null,
					null,
					null
				};
				Scene activeScene = SceneManager.GetActiveScene();
				obj[3] = ((Scene)(ref activeScene)).name;
				obj[4] = " boss=";
				obj[5] = BossAlive().ToString();
				Once(string.Concat(obj));
				if (flag)
				{
					((Component)body).gameObject.AddComponent<PurpleMinion>();
				}
			}
		}

		private static void Once(string what)
		{
			if (logged.Add(what))
			{
				HatKidSoulboundPlugin.Log.LogInfo((object)("Purple: " + what));
			}
		}

		private static bool Shared(ProjectileController pc)
		{
			GameObject projectilePrefab = ProjectileCatalog.GetProjectilePrefab(pc.catalogIndex);
			if (Object.op_Implicit((Object)(object)projectilePrefab))
			{
				return BossEffects.sharedProjectileNames.Contains(((Object)projectilePrefab).name);
			}
			return false;
		}
	}
	[DefaultExecutionOrder(10000)]
	public class PurpleMinion : MonoBehaviour
	{
		private CharacterModel model;

		private MaterialPropertyBlock block;

		private float nextScan;

		private void Start()
		{
			CharacterBody component = ((Component)this).GetComponent<CharacterBody>();
			object obj;
			if (component == null)
			{
				obj = null;
			}
			else
			{
				ModelLocator modelLocator = component.modelLocator;
				obj = ((modelLocator != null) ? modelLocator.modelTransform : null);
			}
			Transform val = (Transform)obj;
			model = (Object.op_Implicit((Object)(object)val) ? ((Component)val).GetComponent<CharacterModel>() : null);
		}

		private void LateUpdate()
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)model))
			{
				RendererInfo[] baseRendererInfos = model.baseRendererInfos;
				for (int i = 0; i < baseRendererInfos.Length; i++)
				{
					if (Object.op_Implicit((Object)(object)baseRendererInfos[i].defaultMaterial))
					{
						baseRendererInfos[i].defaultMaterial = Purple.Material(baseRendererInfos[i].defaultMaterial);
					}
				}
			}
			block = (MaterialPropertyBlock)(((object)block) ?? ((object)new MaterialPropertyBlock()));
			Renderer[] componentsInChildren = ((Component)this).GetComponentsInChildren<Renderer>();
			foreach (Renderer val in componentsInChildren)
			{
				Purple.Renderer(val);
				if (val.HasPropertyBlock())
				{
					Purple.Block(val, block);
				}
			}
			if (!(Time.time >= nextScan))
			{
				return;
			}
			nextScan = Time.time + 0.5f;
			ParticleSystem[] componentsInChildren2 = ((Component)this).GetComponentsInChildren<ParticleSystem>(true);
			for (int j = 0; j < componentsInChildren2.Length; j++)
			{
				Purple.Particles(componentsInChildren2[j]);
			}
			Light[] componentsInChildren3 = ((Component)this).GetComponentsInChildren<Light>(true);
			foreach (Light val2 in componentsInChildren3)
			{
				if (Purple.IsColoured(val2.color))
				{
					val2.color = Purple.Shift(val2.color);
				}
			}
		}
	}
	[DefaultExecutionOrder(10000)]
	public class BossSnatcher : MonoBehaviour
	{
		internal static Material snatcherMaterial;

		internal static Mesh emptyMesh;

		private static readonly string[] hiddenParts = new string[11]
		{
			"cyrstals", "horns", "inner_cloak", "Harvester_Head_Main", "Harvester_Head_Main_Eyes", "Harvester_Head_False", "Harvester_Head_False_Eyes", "Harvester_Weapon", "Harvester_Weapon_blade", "Symbol",
			"ring"
		};

		public Mesh snatcherMesh;

		private CharacterModel characterModel;

		private SkinnedMeshRenderer body;

		private SkinnedMeshRenderer[] hidden;

		private MaterialPropertyBlock block;

		private float nextEffectScan;

		private readonly HashSet<int> recoloured = new HashSet<int>();

		private void Awake()
		{
			characterModel = ((Component)this).GetComponent<CharacterModel>();
			body = Find("Harvester_Body");
			hidden = (from r in hiddenParts.Select(Find)
				where Object.op_Implicit((Object)(object)r)
				select r).ToArray();
		}

		private SkinnedMeshRenderer Find(string path)
		{
			Transform val = ((Component)this).transform.Find(path);
			if (!Object.op_Implicit((Object)(object)val))
			{
				return null;
			}
			return ((Component)val).GetComponent<SkinnedMeshRenderer>();
		}

		private void LateUpdate()
		{
			if (!Object.op_Implicit((Object)(object)body))
			{
				return;
			}
			if (Object.op_Implicit((Object)(object)snatcherMesh) && (Object)(object)body.sharedMesh != (Object)(object)snatcherMesh)
			{
				body.sharedMesh = snatcherMesh;
			}
			SkinnedMeshRenderer[] array = hidden;
			foreach (SkinnedMeshRenderer val in array)
			{
				if ((Object)(object)val.sharedMesh != (Object)(object)emptyMesh)
				{
					val.sharedMesh = emptyMesh;
				}
			}
			RendererInfo[] array2 = (Object.op_Implicit((Object)(object)characterModel) ? characterModel.baseRendererInfos : null);
			if (array2 != null)
			{
				for (int j = 0; j < array2.Length; j++)
				{
					Renderer renderer = array2[j].renderer;
					if ((Object)(object)renderer == (Object)(object)body && (Object)(object)array2[j].defaultMaterial != (Object)(object)snatcherMaterial)
					{
						array2[j].defaultMaterial = snatcherMaterial;
						((Renderer)body).sharedMaterial = snatcherMaterial;
					}
					else if (HatKidSoulboundPlugin.RecolouredEffects && Object.op_Implicit((Object)(object)renderer) && (Object)(object)renderer != (Object)(object)body && !(renderer is SkinnedMeshRenderer) && Object.op_Implicit((Object)(object)array2[j].defaultMaterial))
					{
						array2[j].defaultMaterial = Purple.Material(array2[j].defaultMaterial);
					}
				}
			}
			if (HatKidSoulboundPlugin.RecolouredEffects)
			{
				KeepPurple((Renderer)(object)body);
				if (Time.time >= nextEffectScan)
				{
					nextEffectScan = Time.time + 0.2f;
					RecolourAttachedEffects();
				}
			}
		}

		private void KeepPurple(Renderer r)
		{
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			Material[] sharedMaterials = r.sharedMaterials;
			bool flag = false;
			for (int i = 0; i < sharedMaterials.Length; i++)
			{
				if (Object.op_Implicit((Object)(object)sharedMaterials[i]) && !((Object)(object)sharedMaterials[i] == (Object)(object)snatcherMaterial))
				{
					Material val = Purple.Material(sharedMaterials[i]);
					if ((Object)(object)val != (Object)(object)sharedMaterials[i])
					{
						sharedMaterials[i] = val;
						flag = true;
					}
				}
			}
			if (flag)
			{
				r.sharedMaterials = sharedMaterials;
			}
			if (r.HasPropertyBlock())
			{
				block = (MaterialPropertyBlock)(((object)block) ?? ((object)new MaterialPropertyBlock()));
				Purple.Block(r, block);
				r.GetPropertyBlock(block);
				if (block.HasFloat("_EliteIndex") && block.GetFloat("_EliteIndex") != 0f)
				{
					block.SetFloat("_EliteIndex", 0f);
					r.SetPropertyBlock(block);
				}
			}
		}

		private void RecolourAttachedEffects()
		{
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			CharacterBody val = (Object.op_Implicit((Object)(object)characterModel) ? characterModel.body : null);
			Transform val2 = (Object.op_Implicit((Object)(object)val) ? ((Component)val).transform : ((Component)this).transform);
			Renderer[] componentsInChildren = ((Component)val2).GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val3 in componentsInChildren)
			{
				if (recoloured.Add(((Object)val3).GetInstanceID()) && (val3 is ParticleSystemRenderer || val3 is TrailRenderer || val3 is LineRenderer || Object.op_Implicit((Object)(object)((Component)val3).GetComponentInParent<EffectComponent>()) || Object.op_Implicit((Object)(object)((Component)val3).GetComponentInParent<VFXAttributes>())))
				{
					Purple.All(((Component)val3).gameObject);
				}
			}
			Light[] componentsInChildren2 = ((Component)val2).GetComponentsInChildren<Light>(true);
			foreach (Light val4 in componentsInChildren2)
			{
				if (recoloured.Add(((Object)val4).GetInstanceID()) && Purple.IsColoured(val4.color))
				{
					val4.color = Purple.Shift(val4.color);
				}
			}
		}
	}
	[BepInPlugin("com.chaz.hatkidsoulbound", "Snatcher over Anima Harvester (boss)", "1.0.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class HatKidSoulboundPlugin : BaseUnityPlugin
	{
		public const string PluginGUID = "com.chaz.hatkidsoulbound";

		public const string PluginName = "Snatcher over Anima Harvester (boss)";

		public const string PluginVersion = "1.0.1";

		private const string StageScene = "champrush";

		private const float HatKidScale = 0.9f;

		private const float ChainedScale = 1f;

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> replaceMusic;

		internal static ConfigEntry<bool> replaceBossTitle;

		internal static ConfigEntry<bool> recolouredEffects;

		internal static ConfigEntry<bool> snatcherVoice;

		internal static ConfigEntry<float> musicVolume;

		internal static ConfigEntry<float> voiceVolume;

		private static Dictionary<string, Matrix4x4> acolyteBindposes;

		private static Dictionary<string, Matrix4x4> harvesterBindposes;

		private static Material bodyMat;

		private static Material faceMat;

		private static Material umbrellaMat;

		internal static bool ReplaceMusic => replaceMusic.Value;

		internal static bool ReplaceBossTitle => replaceBossTitle.Value;

		internal static bool SnatcherVoice => snatcherVoice.Value;

		internal static float MusicVolume => musicVolume.Value;

		internal static float FoolVolume => voiceVolume.Value;

		internal static bool RecolouredEffects { get; private set; }

		private void Awake()
		{
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Expected O, but got Unknown
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Expected O, but got Unknown
			//IL_027b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0280: Unknown result type (might be due to invalid IL or missing references)
			//IL_0286: Expected O, but got Unknown
			//IL_028b: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			replaceMusic = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Replace music", true, "Play A Hat in Time's \"Your Contract Has Expired\" during the boss fight instead of the normal boss music, the boss-death riff on the killing blow, and the Time Piece's calm music after the fight.");
			replaceBossTitle = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Replace boss title", true, "Show the boss as \"The Snatcher\" with the subtitle \"The Soulbound Contractor\" instead of \"Anima Harvester\" / \"Avatar of N'kuhana\".");
			recolouredEffects = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Recoloured effects", true, "The boss's attacks and the stage's ritual, cutscene, pit and door effects in Hat Kid's purple. Off: the usual green. Takes effect after restarting the game.");
			RecolouredEffects = recolouredEffects.Value;
			snatcherVoice = ((BaseUnityPlugin)this).Config.Bind<bool>("Audio", "Snatcher voice lines", true, "\"Ahahaa, FOOOOOL\" when the Snatcher appears, and \"FOOOOOOOOL\" instead of the Anima Harvester's scream when his second phase starts.");
			voiceVolume = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "Voice volume", 1f, new ConfigDescription("Volume of the Snatcher's lines (0-1), multiplied by the game's Master and SFX volume.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			musicVolume = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "Music volume", 1f, new ConfigDescription("Volume of the music (0-1), multiplied by the game's Master and Music volume.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			if (Chainloader.PluginInfos.ContainsKey("com.rune580.riskofoptions"))
			{
				try
				{
					ModSettings.Register();
				}
				catch (Exception ex)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Couldn't add the mod settings page: " + ex.Message));
				}
			}
			try
			{
				LoadHatKid();
				HatKidify(HatKidSoulboundPlugin.Load<GameObject>("RoR2/DLC4/champrush/UnlockableAcolyteInteractable.prefab").transform, "revivable Acolyte", 0.9f);
				SceneManager.sceneLoaded += OnSceneLoaded;
			}
			catch (Exception ex2)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Hat Kid NPC setup failed: " + ex2));
			}
			if (RecolouredEffects)
			{
				try
				{
					BossEffects.Setup();
				}
				catch (Exception ex3)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)("Boss effects setup failed: " + ex3));
				}
			}
			try
			{
				using (Stream s = typeof(Assets).Assembly.GetManifestResourceStream("snatcher_fool.wav"))
				{
					BossRuntime.foolVoice = Voice.Load(s);
				}
				using (Stream s2 = typeof(Assets).Assembly.GetManifestResourceStream("snatcher_ahahaa_fool.wav"))
				{
					BossFight.spawnVoice = Voice.Load(s2);
				}
				using (Stream s3 = typeof(Assets).Assembly.GetManifestResourceStream("boss_death_riff.wav"))
				{
					BossFight.deathRiff = Voice.Load(s3);
				}
				Harmony val = new Harmony("com.chaz.hatkidsoulbound");
				BossRuntime.Setup(val);
				BossFight.Setup(val);
			}
			catch (Exception ex4)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Boss runtime setup failed: " + ex4));
			}
			RoR2Application.onLoad = (Action)Delegate.Combine(RoR2Application.onLoad, (Action)delegate
			{
				try
				{
					SetUpBosses();
				}
				catch (Exception ex5)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)("Snatcher boss setup failed: " + ex5));
				}
			});
		}

		private void Update()
		{
			Voice.Cleanup();
			BossRuntime.Update();
			BossFight.Update();
		}

		private void OnDestroy()
		{
			Voice.StopAll();
			BossFight.Shutdown();
		}

		private void LoadHatKid()
		{
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Expected O, but got Unknown
			acolyteBindposes = Assets.ReadBindposes("bindposes.bin");
			harvesterBindposes = Assets.ReadBindposes("harvester_bindposes.bin");
			Material template = HatKidSoulboundPlugin.Load<Material>("RoR2/DLC4/Acolyte/matAcolyteBody.mat");
			bodyMat = Assets.MakeMaterial(template, "matHatKidBody", Assets.ReadTexture("hatkid_body.png"), cutout: false);
			faceMat = Assets.MakeMaterial(template, "matHatKidFace", Assets.ReadTexture("hatkid_face.png"), cutout: true);
			umbrellaMat = Assets.MakeMaterial(template, "matHatKidUmbrella", Assets.ReadTexture("umbrella.png"), cutout: false);
			Material val = Assets.MakeMaterial(template, "matSnatcher", Assets.ReadTexture("snatcher_albedo.png"), cutout: false);
			val.SetTexture("_EmTex", (Texture)(object)Assets.ReadTexture("snatcher_glow.png"));
			val.SetColor("_EmColor", Color.white);
			val.SetFloat("_EmPower", 2f);
			Texture texture = val.GetTexture("_FresnelRamp");
			if (Object.op_Implicit((Object)(object)texture))
			{
				val.SetTexture("_FresnelRamp", Purple.Texture(texture));
			}
			BossSnatcher.snatcherMaterial = val;
			BossSnatcher.emptyMesh = new Mesh
			{
				name = "HatKidSoulboundEmpty"
			};
		}

		private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			if (((Scene)(ref scene)).name != "champrush")
			{
				return;
			}
			int num = 0;
			GameObject[] rootGameObjects = ((Scene)(ref scene)).GetRootGameObjects();
			for (int i = 0; i < rootGameObjects.Length; i++)
			{
				Transform[] componentsInChildren = rootGameObjects[i].GetComponentsInChildren<Transform>(true);
				foreach (Transform val in componentsInChildren)
				{
					if (((Object)val).name == "mdlAcolyte" && !Object.op_Implicit((Object)(object)((Component)val).GetComponentInParent<CharacterModel>()) && !Object.op_Implicit((Object)(object)((Component)val).GetComponentInParent<CharacterBody>()))
					{
						Transform parent = val.parent;
						HatKidify(val, "chained Acolyte (" + ((parent != null) ? ((Object)parent).name : null) + ")", 1f);
						num++;
					}
				}
			}
			if (num == 0)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"No Acolyte found in Soulbound Threshold");
			}
			if (!RecolouredEffects)
			{
				return;
			}
			try
			{
				StagePurple.Apply(scene);
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Stage purple failed: " + ex));
			}
		}

		internal static void HatKidify(Transform mdlAcolyte, string what, float scale)
		{
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			if (((Object)mdlAcolyte).name != "mdlAcolyte")
			{
				mdlAcolyte = ((IEnumerable<Transform>)((Component)mdlAcolyte).GetComponentsInChildren<Transform>(true)).FirstOrDefault((Func<Transform, bool>)((Transform t) => ((Object)t).name == "mdlAcolyte"));
			}
			if (!Object.op_Implicit((Object)(object)mdlAcolyte))
			{
				throw new Exception("No mdlAcolyte in " + what);
			}
			if (Object.op_Implicit((Object)(object)((Component)mdlAcolyte).GetComponent<HatKidNpc>()))
			{
				return;
			}
			HatKidNpc hatKidNpc = ((Component)mdlAcolyte).gameObject.AddComponent<HatKidNpc>();
			hatKidNpc.scale = scale;
			SkinnedMeshRenderer[] componentsInChildren = ((Component)mdlAcolyte).GetComponentsInChildren<SkinnedMeshRenderer>(true);
			foreach (SkinnedMeshRenderer val in componentsInChildren)
			{
				if (((Object)val).name == "acolyte_geo")
				{
					hatKidNpc.Add(val, Assets.ReadSkinnedMesh("hatkid_body.hkm", val, acolyteBindposes), bodyMat, face: false);
				}
				else if (((Object)val).name.StartsWith("acolyte_eyes"))
				{
					hatKidNpc.Add(val, Assets.ReadSkinnedMesh("hatkid_face.hkm", val, acolyteBindposes), faceMat, face: true);
				}
				else if (((Object)val).name == "Acolyte_Weapon_geo")
				{
					hatKidNpc.Add(val, Assets.ReadSkinnedMesh("umbrella.hkm", val, acolyteBindposes), Object.op_Implicit((Object)(object)((Renderer)val).sharedMaterial) ? umbrellaMat : null, face: false);
				}
			}
			hatKidNpc.Apply();
			mdlAcolyte.localScale = Vector3.one * scale;
			Log.LogInfo((object)$"Hat Kid: {what} ({hatKidNpc.Count} parts)");
		}

		private void SetUpBosses()
		{
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			foreach (GameObject allBodyPrefab in BodyCatalog.allBodyPrefabs)
			{
				if (!Object.op_Implicit((Object)(object)allBodyPrefab) || !((Object)allBodyPrefab).name.StartsWith("ReaperBoss") || ((Object)allBodyPrefab).name.StartsWith("ReaperBossSoul"))
				{
					continue;
				}
				ModelLocator component = allBodyPrefab.GetComponent<ModelLocator>();
				Transform val = ((component != null) ? component.modelTransform : null);
				object obj;
				if (!Object.op_Implicit((Object)(object)val))
				{
					obj = null;
				}
				else
				{
					Transform obj2 = val.Find("Harvester_Body");
					obj = ((obj2 != null) ? ((Component)obj2).GetComponent<SkinnedMeshRenderer>() : null);
				}
				SkinnedMeshRenderer val2 = (SkinnedMeshRenderer)obj;
				if (Object.op_Implicit((Object)(object)val2))
				{
					if (!Object.op_Implicit((Object)(object)((Component)val).GetComponent<BossSnatcher>()))
					{
						((Component)val).gameObject.AddComponent<BossSnatcher>().snatcherMesh = Assets.ReadSkinnedMesh("snatcher.hkm", val2, harvesterBindposes);
					}
					num++;
					((BaseUnityPlugin)this).Logger.LogInfo((object)$"Snatcher boss: {((Object)allBodyPrefab).name} (model scale {val.localScale.x})");
				}
			}
			if (num == 0)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Soulbound Threshold boss body not found");
			}
		}

		internal static T Load<T>(string key) where T : Object
		{
			//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)
			T val = Addressables.LoadAssetAsync<T>((object)key).WaitForCompletion();
			if (!Object.op_Implicit((Object)(object)val))
			{
				throw new Exception("Missing addressable " + key);
			}
			return val;
		}
	}
	[DefaultExecutionOrder(10000)]
	public class HatKidNpc : MonoBehaviour
	{
		public float scale = 1f;

		private readonly List<(SkinnedMeshRenderer renderer, Mesh mesh, Material material, bool face)> parts = new List<(SkinnedMeshRenderer, Mesh, Material, bool)>();

		public int Count => parts.Count;

		private void Awake()
		{
			if (parts.Count > 0)
			{
				return;
			}
			SkinnedMeshRenderer[] componentsInChildren = ((Component)this).GetComponentsInChildren<SkinnedMeshRenderer>(true);
			foreach (SkinnedMeshRenderer val in componentsInChildren)
			{
				if (Object.op_Implicit((Object)(object)val.sharedMesh) && (((Object)val.sharedMesh).name.StartsWith("hatkid_") || ((Object)val.sharedMesh).name == "umbrella"))
				{
					Add(val, val.sharedMesh, ((Renderer)val).sharedMaterial, ((Object)val).name.StartsWith("acolyte_eyes"));
				}
			}
		}

		public void Add(SkinnedMeshRenderer r, Mesh mesh, Material material, bool face)
		{
			r.updateWhenOffscreen = true;
			parts.Add((r, mesh, material, face));
		}

		public void Apply()
		{
			foreach (var (val, val2, val3, flag) in parts)
			{
				if (Object.op_Implicit((Object)(object)val))
				{
					if ((Object)(object)val.sharedMesh != (Object)(object)val2)
					{
						val.sharedMesh = val2;
					}
					if (Object.op_Implicit((Object)(object)val3) && (((Renderer)val).sharedMaterials.Length != 1 || (Object)(object)((Renderer)val).sharedMaterial != (Object)(object)val3))
					{
						((Renderer)val).sharedMaterials = (Material[])(object)new Material[1] { val3 };
					}
					if (flag && ((Renderer)val).HasPropertyBlock())
					{
						((Renderer)val).SetPropertyBlock((MaterialPropertyBlock)null);
					}
				}
			}
		}

		private void LateUpdate()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			Apply();
			if (((Component)this).transform.localScale.x != scale)
			{
				((Component)this).transform.localScale = Vector3.one * scale;
			}
		}
	}
	internal static class Assets
	{
		private static readonly Dictionary<string, Mesh> meshes = new Dictionary<string, Mesh>();

		private static Stream Open(string name)
		{
			return typeof(Assets).Assembly.GetManifestResourceStream(name) ?? throw new FileNotFoundException("Embedded resource missing: " + name);
		}

		public static Dictionary<string, Matrix4x4> ReadBindposes(string name)
		{
			//IL_0027: 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)
			using BinaryReader binaryReader = new BinaryReader(Open(name));
			Dictionary<string, Matrix4x4> dictionary = new Dictionary<string, Matrix4x4>();
			int num = binaryReader.ReadInt32();
			for (int i = 0; i < num; i++)
			{
				string key = ReadString(binaryReader);
				Matrix4x4 value = default(Matrix4x4);
				for (int j = 0; j < 16; j++)
				{
					((Matrix4x4)(ref value))[j / 4, j % 4] = binaryReader.ReadSingle();
				}
				dictionary[key] = value;
			}
			return dictionary;
		}

		public static Mesh ReadSkinnedMesh(string name, SkinnedMeshRenderer target, Dictionary<string, Matrix4x4> bindposes)
		{
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_01db: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0208: Unknown result type (might be due to invalid IL or missing references)
			//IL_020d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0286: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0333: Unknown result type (might be due to invalid IL or missing references)
			//IL_0338: Unknown result type (might be due to invalid IL or missing references)
			//IL_0351: Expected O, but got Unknown
			Transform[] bones = target.bones;
			string key = name + "|" + string.Join(",", bones.Select((Transform b) => (!Object.op_Implicit((Object)(object)b)) ? "" : ((Object)b).name));
			if (meshes.TryGetValue(key, out var value))
			{
				return value;
			}
			using BinaryReader binaryReader = new BinaryReader(Open(name));
			if (new string(binaryReader.ReadChars(4)) != "HKM1")
			{
				throw new InvalidDataException(name);
			}
			int num = binaryReader.ReadInt32();
			string[] array = new string[num];
			for (int num2 = 0; num2 < num; num2++)
			{
				array[num2] = ReadString(binaryReader);
			}
			Dictionary<string, int> byName = new Dictionary<string, int>();
			for (int num3 = 0; num3 < bones.Length; num3++)
			{
				if (Object.op_Implicit((Object)(object)bones[num3]) && !byName.ContainsKey(((Object)bones[num3]).name))
				{
					byName[((Object)bones[num3]).name] = num3;
				}
			}
			Matrix4x4 value2;
			int[] array2 = array.Select(delegate(string b)
			{
				if (!byName.TryGetValue(b, out value2))
				{
					throw new Exception(name + ": bone " + b + " not on " + ((Object)target).name);
				}
				return value2;
			}).ToArray();
			int num4 = binaryReader.ReadInt32();
			Vector3[] array3 = (Vector3[])(object)new Vector3[num4];
			Vector3[] array4 = (Vector3[])(object)new Vector3[num4];
			Vector2[] array5 = (Vector2[])(object)new Vector2[num4];
			for (int num5 = 0; num5 < num4; num5++)
			{
				array3[num5] = new Vector3(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle());
			}
			for (int num6 = 0; num6 < num4; num6++)
			{
				array4[num6] = new Vector3(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle());
			}
			for (int num7 = 0; num7 < num4; num7++)
			{
				array5[num7] = new Vector2(binaryReader.ReadSingle(), binaryReader.ReadSingle());
			}
			int[] array6 = new int[num4 * 4];
			for (int num8 = 0; num8 < array6.Length; num8++)
			{
				array6[num8] = array2[binaryReader.ReadInt32()];
			}
			BoneWeight[] array7 = (BoneWeight[])(object)new BoneWeight[num4];
			for (int num9 = 0; num9 < num4; num9++)
			{
				float weight = binaryReader.ReadSingle();
				float weight2 = binaryReader.ReadSingle();
				float weight3 = binaryReader.ReadSingle();
				float weight4 = binaryReader.ReadSingle();
				int num10 = num9;
				BoneWeight val = default(BoneWeight);
				((BoneWeight)(ref val)).boneIndex0 = array6[num9 * 4];
				((BoneWeight)(ref val)).boneIndex1 = array6[num9 * 4 + 1];
				((BoneWeight)(ref val)).boneIndex2 = array6[num9 * 4 + 2];
				((BoneWeight)(ref val)).boneIndex3 = array6[num9 * 4 + 3];
				((BoneWeight)(ref val)).weight0 = weight;
				((BoneWeight)(ref val)).weight1 = weight2;
				((BoneWeight)(ref val)).weight2 = weight3;
				((BoneWeight)(ref val)).weight3 = weight4;
				array7[num10] = val;
			}
			int num11 = binaryReader.ReadInt32();
			int[] array8 = new int[num11];
			for (int num12 = 0; num12 < num11; num12++)
			{
				array8[num12] = binaryReader.ReadInt32();
			}
			Mesh val2 = new Mesh
			{
				name = Path.GetFileNameWithoutExtension(name)
			};
			if (num4 > 65535)
			{
				val2.indexFormat = (IndexFormat)1;
			}
			val2.vertices = array3;
			val2.normals = array4;
			val2.uv = array5;
			val2.SetIndices(array8, (MeshTopology)0, 0);
			val2.boneWeights = array7;
			val2.bindposes = bones.Select((Transform b) => (!Object.op_Implicit((Object)(object)b) || !bindposes.TryGetValue(((Object)b).name, out value2)) ? Matrix4x4.identity : value2).ToArray();
			val2.RecalculateTangents();
			val2.RecalculateBounds();
			meshes[key] = val2;
			return val2;
		}

		public static Texture2D ReadTexture(string name)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Expected O, but got Unknown
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Expected O, but got Unknown
			using Stream stream = Open(name);
			using MemoryStream memoryStream = new MemoryStream();
			stream.CopyTo(memoryStream);
			Texture2D val = new Texture2D(2, 2, (TextureFormat)4, true)
			{
				name = Path.GetFileNameWithoutExtension(name)
			};
			ImageConversion.LoadImage(val, memoryStream.ToArray());
			((Texture)val).wrapMode = (TextureWrapMode)1;
			((Texture)val).filterMode = (FilterMode)2;
			val.Apply(true, true);
			return val;
		}

		public static Material MakeMaterial(Material template, string name, Texture2D tex, bool cutout)
		{
			//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_000e: Expected O, but got Unknown
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			Material val = new Material(template)
			{
				name = name
			};
			val.SetTexture("_MainTex", (Texture)(object)tex);
			val.SetTexture("_NormalTex", (Texture)null);
			val.SetFloat("_NormalStrength", 0f);
			val.SetColor("_EmColor", Color.black);
			val.SetTexture("_EmTex", (Texture)null);
			val.SetFloat("_LimbRemovalOn", 0f);
			val.DisableKeyword("LIMBREMOVAL");
			val.SetFloat("_EnableCutout", cutout ? 1f : 0f);
			if (cutout)
			{
				val.EnableKeyword("CUTOUT");
			}
			else
			{
				val.DisableKeyword("CUTOUT");
			}
			return val;
		}

		private static string ReadString(BinaryReader r)
		{
			return Encoding.UTF8.GetString(r.ReadBytes(r.ReadInt32()));
		}
	}
	internal static class ModSettings
	{
		public const string RiskOfOptionsGUID = "com.rune580.riskofoptions";

		public static void Register()
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Expected O, but got Unknown
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Expected O, but got Unknown
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Expected O, but got Unknown
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Expected O, but got Unknown
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Expected O, but got Unknown
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: 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_00b7: Expected O, but got Unknown
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Expected O, but got Unknown
			ModSettingsManager.SetModDescription("Soulbound Threshold with Hat Kid and the Snatcher.");
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(HatKidSoulboundPlugin.replaceMusic));
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(HatKidSoulboundPlugin.replaceBossTitle));
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(HatKidSoulboundPlugin.recolouredEffects, new CheckBoxConfig
			{
				restartRequired = true
			}));
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(HatKidSoulboundPlugin.snatcherVoice));
			ModSettingsManager.AddOption((BaseOption)new SliderOption(HatKidSoulboundPlugin.voiceVolume, new SliderConfig
			{
				min = 0f,
				max = 1f,
				formatString = "{0:0%}"
			}));
			ModSettingsManager.AddOption((BaseOption)new SliderOption(HatKidSoulboundPlugin.musicVolume, new SliderConfig
			{
				min = 0f,
				max = 1f,
				formatString = "{0:0%}"
			}));
		}
	}
	internal static class MusicPlayer
	{
		private struct WaveFormat
		{
			public short wFormatTag;

			public short nChannels;

			public int nSamplesPerSec;

			public int nAvgBytesPerSec;

			public short nBlockAlign;

			public short wBitsPerSample;

			public short cbSize;
		}

		private struct WaveHeader
		{
			public IntPtr lpData;

			public int dwBufferLength;

			public int dwBytesRecorded;

			public IntPtr dwUser;

			public int dwFlags;

			public int dwLoops;

			public IntPtr lpNext;

			public IntPtr reserved;
		}

		private class Channel
		{
			public int start;

			public int value;

			public int index;

			public int startValue;

			public int startIndex;

			public void Reset()
			{
				value = startValue;
				index = startIndex;
			}

			public short Next(byte[] data, int frame)
			{
				int num = data[start + (frame >> 1)];
				int num2 = (((frame & 1) == 0) ? (num >> 4) : (num & 0xF));
				int num3 = Steps[index];
				index = Math.Min(88, Math.Max(0, index + IndexStep[num2]));
				int num4 = num3 >> 3;
				if ((num2 & 4) != 0)
				{
					num4 += num3;
				}
				if ((num2 & 2) != 0)
				{
					num4 += num3 >> 1;
				}
				if ((num2 & 1) != 0)
				{
					num4 += num3 >> 2;
				}
				value = (((num2 & 8) != 0) ? (value - num4) : (value + num4));
				value = Math.Max(-32768, Math.Min(32767, value));
				return (short)value;
			}
		}

		private class Track
		{
			public byte[] data;

			public int headFrames;

			public int loopFrames;

			public Channel headL;

			public Channel headR;

			public Channel loopL;

			public Channel loopR;
		}

		private const int BufferFrames = 2205;

		private const int BufferCount = 8;

		private const int WAVE_MAPPER = -1;

		private const int CALLBACK_EVENT = 327680;

		private const int WHDR_DONE = 1;

		private static readonly int headerSize = Marshal.SizeOf(typeof(WaveHeader));

		private static readonly int flagsOffset = (int)Marshal.OffsetOf(typeof(WaveHeader), "dwFlags");

		private static readonly short[] Steps = new short[89]
		{
			7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
			19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
			50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
			130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
			337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
			876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
			2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
			5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
			15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
		};

		private static readonly sbyte[] IndexStep = new sbyte[16]
		{
			-1, -1, -1, -1, 2, 4, 6, 8, -1, -1,
			-1, -1, 2, 4, 6, 8
		};

		public static volatile bool Wanted;

		public static volatile bool Paused;

		public static volatile int WantedTrack;

		public static volatile float Volume;

		public static volatile float FadeInSeconds = 0.5f;

		public static volatile float FadeOutSeconds = 3f;

		public static volatile bool Playing;

		public static long FramesWritten;

		public static volatile int LastPeak;

		public static volatile int OpenError;

		private static readonly Track[] tracks = new Track[4];

		private static int rate = 44100;

		private static Thread thread;

		private static volatile bool quit;

		[DllImport("winmm.dll")]
		private static extern int waveOutOpen(out IntPtr handle, int deviceId, ref WaveFormat format, IntPtr callback, IntPtr instance, int flags);

		[DllImport("winmm.dll")]
		private static extern int waveOutPrepareHeader(IntPtr handle, IntPtr header, int size);

		[DllImport("winmm.dll")]
		private static extern int waveOutWrite(IntPtr handle, IntPtr header, int size);

		[DllImport("winmm.dll")]
		private static extern int waveOutUnprepareHeader(IntPtr handle, IntPtr header, int size);

		[DllImport("winmm.dll")]
		private static extern int waveOutPause(IntPtr handle);

		[DllImport("winmm.dll")]
		private static extern int waveOutRestart(IntPtr handle);

		[DllImport("winmm.dll")]
		private static extern int waveOutReset(IntPtr handle);

		[DllImport("winmm.dll")]
		private static extern int waveOutClose(IntPtr handle);

		public static bool Load(int slot, byte[] snm)
		{
			if (snm == null || snm.Length < 32 || snm[0] != 83 || snm[1] != 78 || snm[2] != 77 || snm[3] != 52)
			{
				return false;
			}
			rate = BitConverter.ToInt32(snm, 4);
			short num = BitConverter.ToInt16(snm, 8);
			int num2 = BitConverter.ToInt32(snm, 18);
			int num3 = BitConverter.ToInt32(snm, 22);
			if (num != 2 || num3 <= 0 || snm.Length < 32 + 2 * num2 + 2 * num3)
			{
				return false;
			}
			tracks[slot] = new Track
			{
				data = snm,
				headFrames = Math.Min(BitConverter.ToInt32(snm, 10), num2 * 2),
				loopFrames = Math.Min(BitConverter.ToInt32(snm, 14), num3 * 2),
				headL = new Channel
				{
					start = 32
				},
				headR = new Channel
				{
					start = 32 + num2
				},
				loopL = new Channel
				{
					start = 32 + 2 * num2,
					startValue = BitConverter.ToInt16(snm, 26),
					startIndex = (sbyte)snm[28]
				},
				loopR = new Channel
				{
					start = 32 + 2 * num2 + num3,
					startValue = BitConverter.ToInt16(snm, 29),
					startIndex = (sbyte)snm[31]
				}
			};
			return true;
		}

		public static void Start()
		{
			if (thread == null)
			{
				quit = false;
				thread = new Thread(Run)
				{
					IsBackground = true,
					Name = "HatKidSoulboundMusic"
				};
				thread.Start();
			}
		}

		public static void Stop()
		{
			quit = true;
			thread?.Join(500);
			thread = null;
		}

		private static void Run()
		{
			AutoResetEvent autoResetEvent = new AutoResetEvent(initialState: false);
			IntPtr[] array = new IntPtr[8];
			IntPtr[] array2 = new IntPtr[8];
			bool[] array3 = new bool[8];
			byte[] array4 = new byte[8820];
			for (int i = 0; i < 8; i++)
			{
				array[i] = Marshal.AllocHGlobal(headerSize);
				array2[i] = Marshal.AllocHGlobal(array4.Length);
			}
			IntPtr handle = IntPtr.Zero;
			Track track = null;
			bool flag = false;
			bool flag2 = false;
			int num = 0;
			float num2 = 0f;
			float num3 = 0f;
			try
			{
				while (!quit)
				{
					autoResetEvent.WaitOne(20);
					Track track2 = (Wanted ? tracks[Math.Max(0, Math.Min(tracks.Length - 1, WantedTrack))] : null);
					bool flag3 = track2 != null && (handle == IntPtr.Zero || track2 == track);
					if (handle == IntPtr.Zero)
					{
						if (!flag3)
						{
							Playing = false;
							continue;
						}
						track = track2;
						WaveFormat format = new WaveFormat
						{
							wFormatTag = 1,
							nChannels = 2,
							nSamplesPerSec = rate,
							wBitsPerSample = 16,
							nBlockAlign = 4,
							nAvgBytesPerSec = rate * 4
						};
						int num4 = waveOutOpen(out handle, -1, ref format, autoResetEvent.SafeWaitHandle.DangerousGetHandle(), IntPtr.Zero, 327680);
						if (num4 != 0)
						{
							if (OpenError != num4)
							{
								HatKidSoulboundPlugin.Log.LogWarning((object)("Music: couldn't open the audio device (waveOut error " + num4 + ")"));
							}
							OpenError = num4;
							handle = IntPtr.Zero;
							Thread.Sleep(1000);
							continue;
						}
						for (int j = 0; j < 8; j++)
						{
							Marshal.StructureToPtr(new WaveHeader
							{
								lpData = array2[j],
								dwBufferLength = array4.Length
							}, array[j], fDeleteOld: false);
							waveOutPrepareHeader(handle, array[j], headerSize);
							array3[j] = false;
						}
						num = 0;
						flag2 = track.headFrames <= 0;
						track.headL.Reset();
						track.headR.Reset();
						track.loopL.Reset();
						track.loopR.Reset();
						num2 = (num3 = 0f);
						flag = false;
						Playing = true;
					}
					if (Paused != flag)
					{
						flag = Paused;
						if (flag)
						{
							waveOutPause(handle);
						}
						else
						{
							waveOutRestart(handle);
						}
					}
					bool flag4 = false;
					for (int k = 0; k < 8; k++)
					{
						if (array3[k] && (Marshal.ReadInt32(array[k], flagsOffset) & 1) == 0)
						{
							flag4 = true;
							continue;
						}
						array3[k] = false;
						if (!flag3 && num2 <= 0f)
						{
							continue;
						}
						float num5 = 2205f / (float)rate;
						num2 = (flag3 ? Math.Min(1f, num2 + num5 / Math.Max(0.01f, FadeInSeconds)) : Math.Max(0f, num2 - num5 / Math.Max(0.01f, FadeOutSeconds)));
						float num6 = Volume * num2 * num2;
						for (int l = 0; l < 2205; l++)
						{
							float num7 = num3 + (num6 - num3) * ((float)l + 1f) / 2205f;
							int val;
							int val2;
							if (!flag2)
							{
								val = (int)((float)track.headL.Next(track.data, num) * num7);
								val2 = (int)((float)track.headR.Next(track.data, num) * num7);
								if (++num >= track.headFrames)
								{
									num = 0;
									flag2 = true;
								}
							}
							else
							{
								val = (int)((float)track.loopL.Next(track.data, num) * num7);
								val2 = (int)((float)track.loopR.Next(track.data, num) * num7);
								if (++num >= track.loopFrames)
								{
									num = 0;
									track.loopL.Reset();
									track.loopR.Reset();
								}
							}
							val = Math.Max(-32768, Math.Min(32767, val));
							val2 = Math.Max(-32768, Math.Min(32767, val2));
							array4[l * 4] = (byte)val;
							array4[l * 4 + 1] = (byte)(val >> 8);
							array4[l * 4 + 2] = (byte)val2;
							array4[l * 4 + 3] = (byte)(val2 >> 8);
						}
						num3 = num6;
						int num8 = 0;
						for (int m = 0; m < array4.Length; m += 2)
						{
							num8 = Math.Max(num8, Math.Abs((int)(short)(array4[m] | (array4[m + 1] << 8))));
						}
						LastPeak = num8;
						FramesWritten += 2205L;
						Marshal.Copy(array4, 0, array2[k], array4.Length);
						waveOutWrite(handle, array[k], headerSize);
						flag4 = (array3[k] = true);
					}
					if (!flag3 && num2 <= 0f && !flag4)
					{
						Close(ref handle, array);
					}
				}
			}
			catch (Exception ex)
			{
				HatKidSoulboundPlugin.Log.LogWarning((object)("Music stream stopped: " + ex));
			}
			finally
			{
				Close(ref handle, array);
				for (int n = 0; n < 8; n++)
				{
					Marshal.FreeHGlobal(array[n]);
					Marshal.FreeHGlobal(array2[n]);
				}
				autoResetEvent.Close();
				Playing = false;
			}
		}

		private static void Close(ref IntPtr handle, IntPtr[] headers)
		{
			if (!(handle == IntPtr.Zero))
			{
				waveOutReset(handle);
				foreach (IntPtr header in headers)
				{
					waveOutUnprepareHeader(handle, header, headerSize);
				}
				waveOutClose(handle);
				handle = IntPtr.Zero;
				Playing = false;
			}
		}
	}
	internal static class Purple
	{
		private const float TargetHue = 0.78f;

		private static readonly Dictionary<Texture, Texture> textures = new Dictionary<Texture, Texture>();

		private static readonly Dictionary<Material, Material> materials = new Dictionary<Material, Material>();

		private static readonly Dictionary<Material, Material> glowMaterials = new Dictionary<Material, Material>();

		private const float SkyMinSaturation = 0.3f;

		private const float SkyMinShift = 0.04f;

		private static readonly Dictionary<Texture, Texture> skyTextures = new Dictionary<Texture, Texture>();

		private static readonly Dictionary<Material, Material> skyMaterials = new Dictionary<Material, Material>();

		private static readonly string[] blockColours = new string[7] { "_EmColor", "_Color", "_TintColor", "_FlashColor", "_EmissionColor", "_MainColor", "_GlowColor" };

		private static readonly string[] blockTextures = new string[4] { "_FresnelRamp", "_RemapTex", "_EmTex", "_MainTex" };

		public static bool IsColoured(Color c)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			float num = default(float);
			float num2 = default(float);
			float num3 = default(float);
			Color.RGBToHSV(c, ref num, ref num2, ref num3);
			if (num2 > 0.25f && num3 > 0.03f)
			{
				return Mathf.Abs(num - 0.78f) > 0.002f;
			}
			return false;
		}

		public static Color Shift(Color c)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: 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)
			if (!IsColoured(c))
			{
				return c;
			}
			float num = default(float);
			float num2 = default(float);
			float num3 = default(float);
			Color.RGBToHSV(c, ref num, ref num2, ref num3);
			Color result = Color.HSVToRGB(0.78f, num2, num3, true);
			result.a = c.a;
			return result;
		}

		public static Color Force(Color c, float minSaturation, float minSaturationToShift = 0f)
		{
			//IL_0000: 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_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			float num = default(float);
			float num2 = default(float);
			float num3 = default(float);
			Color.RGBToHSV(c, ref num, ref num2, ref num3);
			if (num2 < minSaturationToShift)
			{
				return c;
			}
			Color result = Color.HSVToRGB(0.78f, Mathf.Max(num2, minSaturation), num3, true);
			result.a = c.a;
			return result;
		}

		public static Gradient Shift(Gradient g)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Expected O, but got Unknown
			if (g == null)
			{
				return null;
			}
			GradientColorKey[] colorKeys = g.colorKeys;
			bool flag = false;
			for (int i = 0; i < colorKeys.Length; i++)
			{
				if (IsColoured(colorKeys[i].color))
				{
					colorKeys[i].color = Shift(colorKeys[i].color);
					flag = true;
				}
			}
			if (!flag)
			{
				return g;
			}
			Gradient val = new Gradient
			{
				mode = g.mode
			};
			val.SetKeys(colorKeys, g.alphaKeys);
			return val;
		}

		public static MinMaxGradient Shift(MinMaxGradient m)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: 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_0022: Expected I4, but got Unknown
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: 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_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: 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)
			//IL_007e: 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_00a1: Unknown result type (might be due to invalid IL or missing references)
			ParticleSystemGradientMode mode = ((MinMaxGradient)(ref m)).mode;
			switch ((int)mode)
			{
			case 0:
				return new MinMaxGradient(Shift(((MinMaxGradient)(ref m)).color));
			case 2:
				return new MinMaxGradient(Shift(((MinMaxGradient)(ref m)).colorMin), Shift(((MinMaxGradient)(ref m)).colorMax));
			case 1:
				return new MinMaxGradient(Shift(((MinMaxGradient)(ref m)).gradient));
			case 3:
				return new MinMaxGradient(Shift(((MinMaxGradient)(ref m)).gradientMin), Shift(((MinMaxGradient)(ref m)).gradientMax));
			case 4:
			{
				MinMaxGradient result = default(MinMaxGradient);
				((MinMaxGradient)(ref result))..ctor(Shift(((MinMaxGradient)(ref m)).gradient));
				((MinMaxGradient)(ref result)).mode = (ParticleSystemGradientMode)4;
				return result;
			}
			default:
				return m;
			}
		}

		public static Texture Texture(Texture t)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Invalid comparison between Unknown and I4
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: 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)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: 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_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_009a: 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_00b5: 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_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Expected O, but got Unknown
			if (!Object.op_Implicit((Object)(object)t) || !(t is Texture2D) || (int)t.dimension != 2)
			{
				return t;
			}
			if (textures.TryGetValue(t, out var value))
			{
				return value;
			}
			Texture val = t;
			try
			{
				int w;
				int h;
				Color32[] array = Read(t, out w, out h);
				int num = 0;
				for (int i = 0; i < array.Length; i++)
				{
					Color c = Color32.op_Implicit(array[i]);
					if (IsColoured(c))
					{
						array[i] = Color32.op_Implicit(Shift(c));
						num++;
					}
				}
				if (num > array.Length / 200)
				{
					Texture2D val2 = new Texture2D(w, h, (TextureFormat)4, t.mipmapCount > 1)
					{
						name = ((Object)t).name + "_Purple",
						wrapMode = t.wrapMode,
						filterMode = t.filterMode,
						anisoLevel = t.anisoLevel
					};
					val2.SetPixels32(array);
					val2.Apply(true, true);
					val = (Texture)val2;
				}
			}
			catch (Exception ex)
			{
				HatKidSoulboundPlugin.Log.LogWarning((object)("Couldn't recolour " + ((Object)t).name + ": " + ex.Message));
			}
			textures[t] = val;
			return val;
		}

		private static Color32[] Read(Texture t, out int w, out int h)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			Color32[] array = Blit(t, 64, 64);
			bool flag = false;
			Color32[] array2 = array;
			for (int i = 0; i < array2.Length; i++)
			{
				if (IsColoured(Color32.op_Implicit(array2[i])))
				{
					flag = true;
					break;
				}
			}
			if (!flag)
			{
				w = 64;
				h = 64;
				return (Color32[])(object)new Color32[0];
			}
			w = Mathf.Min(t.width, 1024);
			h = Mathf.Min(t.height, 1024);
			return Blit(t, w, h);
		}

		private static Color32[] Blit(Texture t, int w, int h)
		{
			//IL_0022: 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)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Expected O, but got Unknown
			RenderTexture temporary = RenderTexture.GetTemporary(w, h, 0, (RenderTextureFormat)0, (RenderTextureReadWrite)2);
			Graphics.Blit(t, temporary);
			RenderTexture active = RenderTexture.active;
			RenderTexture.active = temporary;
			Texture2D val = new Texture2D(w, h, (TextureFormat)4, false);
			val.ReadPixels(new Rect(0f, 0f, (float)w, (float)h), 0, 0);
			val.Apply();
			RenderTexture.active = active;
			RenderTexture.ReleaseTemporary(temporary);
			Color32[] pixels = val.GetPixels32();
			Object.Destroy((Object)val);
			return pixels;
		}

		public static Material Material(Material m, bool glowOnly = false)
		{
			//IL_0059: 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_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Invalid comparison between Unknown and I4
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: 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_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)m))
			{
				return m;
			}
			if (((Object)m).name.Contains("TeamAreaIndicator"))
			{
				return m;
			}
			Dictionary<Material, Material> dictionary = (glowOnly ? glowMaterials : materials);
			if (dictionary.TryGetValue(m, out var value))
			{
				return value;
			}
			Material val = null;
			Shader shader = m.shader;
			for (int i = 0; i < shader.GetPropertyCount(); i++)
			{
				string propertyName = shader.GetPropertyName(i);
				ShaderPropertyType propertyType = shader.GetPropertyType(i);
				if ((int)propertyType != 0)
				{
					if ((int)propertyType == 4 && !propertyName.Contains("Normal") && !propertyName.Contains("Bump") && (!glowOnly || propertyName.Contains("Ramp") || propertyName.Contains("Remap") || propertyName.Contains("Em") || propertyName.Contains("Glow")))
					{
						Texture texture = m.GetTexture(propertyName);
						Texture val2 = Texture(texture);
						if ((Object)(object)val2 != (Object)(object)texture)
						{
							val = (Material)(((object)val) ?? ((object)new Material(m)
							{
								name = ((Object)m).name + "_Purple"
							}));
							val.SetTexture(propertyName, val2);
						}
					}
				}
				else
				{
					Color color = m.GetColor(propertyName);
					if (IsColoured(color))
					{
						val = (Material)(((object)val) ?? ((object)new Material(m)
						{
							name = ((Object)m).name + "_Purple"
						}));
						val.SetColor(propertyName, Shift(color));
					}
				}
			}
			dictionary[m] = val ?? m;
			dictionary[val ?? m] = val ?? m;
			return val ?? m;
		}

		public static Color Sky(Color c)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			return Force(c, 0.3f, 0.04f);
		}

		private static Gradient Sky(Gradient g)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: 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_003c: 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_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Expected O, but got Unknown
			if (g == null)
			{
				return null;
			}
			GradientColorKey[] colorKeys = g.colorKeys;
			for (int i = 0; i < colorKeys.Length; i++)
			{
				colorKeys[i].color = Sky(colorKeys[i].color);
			}
			Gradient val = new Gradient
			{
				mode = g.mode
			};
			val.SetKeys(colorKeys, g.alphaKeys);
			return val;
		}

		private static MinMaxGradient Sky(MinMaxGradient m)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: 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_0022: Expected I4, but got Unknown
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: 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_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: 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)
			//IL_007e: 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_00a1: Unknown result type (might be due to invalid IL or missing references)
			ParticleSystemGradientMode mode = ((MinMaxGradient)(ref m)).mode;
			switch ((int)mode)
			{
			case 0:
				return new MinMaxGradient(Sky(((MinMaxGradient)(ref m)).color));
			case 2:
				return new MinMaxGradient(Sky(((MinMaxGradient)(ref m)).colorMin), Sky(((MinMaxGradient)(ref m)).colorMax));
			case 1:
				return new MinMaxGradient(Sky(((MinMaxGradient)(ref m)).gradient));
			case 3:
				return new MinMaxGradient(Sky(((MinMaxGradient)(ref m)).gradientMin), Sky(((MinMaxGradient)(ref m)).gradientMax));
			case 4:
			{
				MinMaxGradient result = default(MinMaxGradient);
				((MinMaxGradient)(ref result))..ctor(Sky(((MinMaxGradient)(ref m)).gradient));
				((MinMaxGradient)(ref result)).mode = (ParticleSystemGradientMode)4;
				return result;
			}
			default:
				return m;
			}
		}

		private static Texture SkyTexture(Texture t)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Invalid comparison between Unknown and I4
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: 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_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: 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_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: 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)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Expected O, but got Unknown
			if (!Object.op_Implicit((Object)(object)t) || !(t is Texture2D) || (int)t.dimension != 2)
			{
				return t;
			}
			if (skyTextures.TryGetValue(t, out var value))
			{
				return value;
			}
			Texture val = t;
			try
			{
				int num = Mathf.Min(t.width, 1024);
				int num2 = Mathf.Min(t.height, 1024);
				Color32[] array = Blit(t, num, num2);
				int num3 = 0;
				for (int i = 0; i < array.Length; i++)
				{
					Color val2 = Color32.op_Implicit(array[i]);
					Color val3 = Sky(val2);
					if (val3 != val2)
					{
						array[i] = Color32.op_Implicit(val3);
						num3++;
					}
				}
				if (num3 > array.Length / 200)
				{
					Texture2D val4 = new Texture2D(num, num2, (TextureFormat)4, t.mipmapCount > 1)
					{
						name = ((Object)t).name + "_SkyPurple",
						wrapMode = t.wrapMode,
						filterMode = t.filterMode,
						anisoLevel = t.anisoLevel
					};
					val4.SetPixels32(array);
					val4.Apply(true, true);
					val = (Texture)val4;
				}
			}
			catch (Exception ex)
			{
				HatKidSoulboundPlugin.Log.LogWarning((object)("Couldn't recolour " + ((Object)t).name + ": " + ex.Message));
			}
			skyTextures[t] = val;
			return val;
		}

		private static Material SkyMaterial(Material m)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: 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_005c: Invalid comparison between Unknown and I4
			if (!Object.op_Implicit((Object)(object)m))
			{
				return m;
			}
			if (skyMaterials.TryGetValue(m, out var value))
			{
				return value;
			}
			Material val = new Material(m)
			{
				name = ((Object)m).name + "_SkyPurple"
			};
			Shader shader = m.shader;
			for (int i = 0; i < shader.GetPropertyCount(); i++)
			{
				string propertyName = shader.GetPropertyName(i);
				ShaderPropertyType propertyType = shader.GetPropertyType(i);
				if ((int)propertyType != 0)
				{
					if ((int)propertyType == 4 && !propertyName.Contains("Normal") && !propertyName.Contains("Bump"))
					{
						val.SetTexture(propertyName, SkyTexture(m.GetTexture(propertyName)));
					}
				}
				else
				{
					val.SetColor(propertyName, Sky(m.GetColor(propertyName)));
				}
			}
			skyMaterials[m] = val;
			skyMaterials[val] = val;
			return val;
		}

		public static void SkyRenderer(Renderer r)
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: 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_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			Material[] sharedMaterials = r.sharedMaterials;
			for (int i = 0; i < sharedMaterials.Length; i++)
			{
				sharedMaterials[i] = SkyMaterial(sharedMaterials[i]);
			}
			r.sharedMaterials = sharedMaterials;
			ParticleSystemRenderer val = (ParticleSystemRenderer)(object)((r is ParticleSystemRenderer) ? r : null);
			if (val != null && Object.op_Implicit((Object)(object)val.trailMaterial))
			{
				val.trailMaterial = SkyMaterial(val.trailMaterial);
			}
			ParticleSystem component = ((Component)r).GetComponent<ParticleSystem>();
			if (Object.op_Implicit((Object)(object)component))
			{
				MainModule main = component.main;
				((MainModule)(ref main)).startColor = Sky(((MainModule)(ref main)).startColor);
				ColorOverLifetimeModule colorOverLifetime = component.colorOverLifetime;
				if (((ColorOverLifetimeModule)(ref colorOverLifetime)).enabled)
				{
					((ColorOverLifetimeModule)(ref colorOverLifetime)).color = Sky(((ColorOverLifetimeModule)(ref colorOverLifetime)).color);
				}
			}
		}

		public static bool All(GameObject root, bool glowOnly = false)
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			bool flag = false;
			Renderer[] componentsInChildren = root.GetComponentsInChildren<Renderer>(true);
			foreach (Renderer r in componentsInChildren)
			{
				flag |= Renderer(r, glowOnly);
			}
			ParticleSystem[] componentsInChildren2 = root.GetComponentsInChildren<ParticleSystem>(true);
			foreach (ParticleSystem ps in componentsInChildren2)
			{
				flag |= Particles(ps);
			}
			Light[] componentsInChildren3 = root.GetComponentsInChildren<Light>(true);
			foreach (Light val in componentsInChildren3)
			{
				if (IsColoured(val.color))
				{
					val.color = Shift(val.color);
					flag = true;
				}
			}
			return flag;
		}

		public static bool Renderer(Renderer r, bool glowOnly = false)
		{
			bool flag = false;
			Material[] sharedMaterials = r.sharedMaterials;
			for (int i = 0; i < sharedMaterials.Length; i++)
			{
				Material val = Material(sharedMaterials[i], glowOnly);
				if ((Object)(object)val != (Object)(object)sharedMaterials[i])
				{
					sharedMaterials[i] = val;
					flag = true;
				}
			}
			if (flag)
			{
				r.sharedMaterials = sharedMaterials;
			}
			ParticleSystemRenderer val2 = (ParticleSystemRenderer)(object)((r is ParticleSystemRenderer) ? r : null);
			if (val2 != null && Object.op_Implicit((Object)(object)val2.trailMaterial))
			{
				Material val3 = Material(val2.trailMaterial);
				if ((Object)(object)val3 != (Object)(object)val2.trailMaterial)
				{
					val2.trailMaterial = val3;
					flag = true;
				}
			}
			TrailRenderer val4 = (TrailRenderer)(object)((r is TrailRenderer) ? r : null);
			if (val4 != null)
			{
				Gradient val5 = Shift(val4.colorGradient);
				if (val5 != val4.colorGradient)
				{
					val4.colorGradient = val5;
					flag = true;
				}
			}
			LineRenderer val6 = (LineRenderer)(object)((r is LineRenderer) ? r : null);
			if (val6 != null)
			{
				Gradient val7 = Shift(val6.colorGradient);
				if (val7 != val6.colorGradient)
				{
					val6.colorGradient = val7;
					flag = true;
				}
			}
			return flag;
		}

		public static void Block(Renderer r, MaterialPropertyBlock block)
		{
			//IL_0022: 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)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			r.GetPropertyBlock(block);
			bool flag = false;
			string[] array = blockColours;
			foreach (string text in array)
			{
				if (block.HasColor(text))
				{
					Color color = block.GetColor(text);
					if (IsColoured(color))
					{
						block.SetColor(text, Shift(color));
						flag = true;
					}
				}
			}
			array = blockTextures;
			foreach (string text2 in array)
			{
				if (block.HasTexture(text2))
				{
					Texture texture = block.GetTexture(text2);
					Texture val = Texture(texture);
					if ((Object)(object)val != (Object)(object)texture)
					{
						block.SetTexture(text2, val);
						flag = true;
					}
				}
			}
			if (flag)
			{
				r.SetPropertyBlock(block);
			}
		}

		public static bool Particles(ParticleSystem ps)
		{
			//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_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: 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_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or