Decompiled source of kossnikitaMatesMod v1.0.3

kossnikitaMatesMod.dll

Decompiled 8 months ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Audio;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("kossnikita")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.3.0")]
[assembly: AssemblyInformationalVersion("1.0.3+2405c4dd8ccd4accb5843eda27113403ef74f8d2")]
[assembly: AssemblyProduct("kossnikita mates mod")]
[assembly: AssemblyTitle("kossnikitaMatesMod")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.3.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace kossnikitaMates
{
	public class AudioSourceData
	{
		public AudioClip OriginalClip { get; set; }

		public AudioClip RealClip
		{
			get
			{
				using (new SpoofBypassContext())
				{
					return Source.clip;
				}
			}
			set
			{
				using (new SpoofBypassContext())
				{
					Source.clip = value;
				}
			}
		}

		public AudioSource Source { get; private set; }

		public float OriginalPitch { get; set; }

		public float? CustomPitch { get; set; }

		public bool ReplacementAttempted { get; set; }

		public AudioSourceData(AudioSource source)
		{
			Source = source;
			using (new SpoofBypassContext())
			{
				OriginalPitch = source.pitch;
			}
		}

		public static AudioSourceData GetOrCreateData(AudioSource audioSource)
		{
			if (Plugin.audioSourceData.TryGetValue(audioSource, out var value))
			{
				return value;
			}
			value = new AudioSourceData(audioSource);
			value.OriginalClip = value.RealClip;
			Plugin.audioSourceData.Add(audioSource, value);
			return value;
		}
	}
	public class SpoofBypassContext : IDisposable
	{
		public SpoofBypassContext()
		{
			AudioSourcePatch.bypassSpoofing = true;
		}

		public void Dispose()
		{
			AudioSourcePatch.bypassSpoofing = false;
		}
	}
	[HarmonyPatch(typeof(AudioSource))]
	public class AudioSourcePatch
	{
		public struct SpoofBypassContext : IDisposable
		{
			private bool previousState;

			public SpoofBypassContext()
			{
				previousState = bypassSpoofing;
				bypassSpoofing = true;
			}

			public void Dispose()
			{
				bypassSpoofing = previousState;
			}
		}

		private class AdditionalAudioBehaviour : MonoBehaviour
		{
			[CompilerGenerated]
			private sealed class <CleanupCoroutine>d__4 : IEnumerator<object>, IEnumerator, IDisposable
			{
				private int <>1__state;

				private object <>2__current;

				public float delay;

				public AdditionalAudioBehaviour <>4__this;

				object IEnumerator<object>.Current
				{
					[DebuggerHidden]
					get
					{
						return <>2__current;
					}
				}

				object IEnumerator.Current
				{
					[DebuggerHidden]
					get
					{
						return <>2__current;
					}
				}

				[DebuggerHidden]
				public <CleanupCoroutine>d__4(int <>1__state)
				{
					this.<>1__state = <>1__state;
				}

				[DebuggerHidden]
				void IDisposable.Dispose()
				{
					<>1__state = -2;
				}

				private bool MoveNext()
				{
					//IL_0025: Unknown result type (might be due to invalid IL or missing references)
					//IL_002f: Expected O, but got Unknown
					int num = <>1__state;
					AdditionalAudioBehaviour additionalAudioBehaviour = <>4__this;
					switch (num)
					{
					default:
						return false;
					case 0:
						<>1__state = -1;
						<>2__current = (object)new WaitForSeconds(delay);
						<>1__state = 1;
						return true;
					case 1:
						<>1__state = -1;
						if (additionalAudioBehaviour.unmuteOriginal && (Object)(object)additionalAudioBehaviour.original != (Object)null)
						{
							using (new SpoofBypassContext())
							{
								try
								{
									additionalAudioBehaviour.original.mute = false;
								}
								catch
								{
								}
							}
						}
						Object.Destroy((Object)(object)((Component)additionalAudioBehaviour).gameObject);
						return false;
					}
				}

				bool IEnumerator.MoveNext()
				{
					//ILSpy generated this explicit interface implementation from .override directive in MoveNext
					return this.MoveNext();
				}

				[DebuggerHidden]
				void IEnumerator.Reset()
				{
					throw new NotSupportedException();
				}
			}

			private AudioSource original;

			private AudioSource replacement;

			private bool unmuteOriginal;

			public void Init(AudioSource original, AudioSource replacement, bool unmuteOriginal, float lifespan)
			{
				this.original = original;
				this.replacement = replacement;
				this.unmuteOriginal = unmuteOriginal;
				((MonoBehaviour)this).StartCoroutine(CleanupCoroutine(lifespan));
			}

			[IteratorStateMachine(typeof(<CleanupCoroutine>d__4))]
			private IEnumerator CleanupCoroutine(float delay)
			{
				//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
				return new <CleanupCoroutine>d__4(0)
				{
					<>4__this = this,
					delay = delay
				};
			}
		}

		private class ReplacementDistanceBehaviour : MonoBehaviour
		{
			[CompilerGenerated]
			private sealed class <CleanupCoroutine>d__4 : IEnumerator<object>, IEnumerator, IDisposable
			{
				private int <>1__state;

				private object <>2__current;

				public float delay;

				public ReplacementDistanceBehaviour <>4__this;

				object IEnumerator<object>.Current
				{
					[DebuggerHidden]
					get
					{
						return <>2__current;
					}
				}

				object IEnumerator.Current
				{
					[DebuggerHidden]
					get
					{
						return <>2__current;
					}
				}

				[DebuggerHidden]
				public <CleanupCoroutine>d__4(int <>1__state)
				{
					this.<>1__state = <>1__state;
				}

				[DebuggerHidden]
				void IDisposable.Dispose()
				{
					<>1__state = -2;
				}

				private bool MoveNext()
				{
					//IL_0025: Unknown result type (might be due to invalid IL or missing references)
					//IL_002f: Expected O, but got Unknown
					int num = <>1__state;
					ReplacementDistanceBehaviour replacementDistanceBehaviour = <>4__this;
					switch (num)
					{
					default:
						return false;
					case 0:
						<>1__state = -1;
						<>2__current = (object)new WaitForSeconds(delay);
						<>1__state = 1;
						return true;
					case 1:
						<>1__state = -1;
						if ((Object)(object)replacementDistanceBehaviour.original != (Object)null)
						{
							using (new SpoofBypassContext())
							{
								try
								{
									replacementDistanceBehaviour.original.maxDistance = replacementDistanceBehaviour.originalMaxDistance;
								}
								catch
								{
								}
								try
								{
									replacementDistanceBehaviour.original.volume = replacementDistanceBehaviour.originalVolume;
								}
								catch
								{
								}
							}
						}
						Object.Destroy((Object)(object)replacementDistanceBehaviour);
						return false;
					}
				}

				bool IEnumerator.MoveNext()
				{
					//ILSpy generated this explicit interface implementation from .override directive in MoveNext
					return this.MoveNext();
				}

				[DebuggerHidden]
				void IEnumerator.Reset()
				{
					throw new NotSupportedException();
				}
			}

			private AudioSource original;

			private float originalMaxDistance;

			private float originalVolume;

			public void Init(AudioSource original, float originalMaxDistance, float originalVolume, float lifespan)
			{
				this.original = original;
				this.originalMaxDistance = originalMaxDistance;
				this.originalVolume = originalVolume;
				((MonoBehaviour)this).StartCoroutine(CleanupCoroutine(lifespan));
			}

			[IteratorStateMachine(typeof(<CleanupCoroutine>d__4))]
			private IEnumerator CleanupCoroutine(float delay)
			{
				//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
				return new <CleanupCoroutine>d__4(0)
				{
					<>4__this = this,
					delay = delay
				};
			}
		}

		[ThreadStatic]
		internal static bool bypassSpoofing;

		private static AudioMixerGroup[] cachedAudioMixerGroups;

		private static FieldInfo m_GroupsField;

		[HarmonyPrefix]
		[HarmonyPatch("Play", new Type[] { })]
		[HarmonyPatch("Play", new Type[] { typeof(ulong) })]
		[HarmonyPatch("PlayDelayed", new Type[] { typeof(float) })]
		[HarmonyPatch("PlayScheduled", new Type[] { typeof(double) })]
		private static bool PlayPrefix(AudioSource __instance)
		{
			if (bypassSpoofing)
			{
				return true;
			}
			if (!Plugin.ConfigSound.Value)
			{
				return true;
			}
			AudioSourceData orCreateData = AudioSourceData.GetOrCreateData(__instance);
			if (orCreateData.ReplacementAttempted)
			{
				return true;
			}
			try
			{
				string text = "<unknown>";
				try
				{
					using (new SpoofBypassContext())
					{
						AudioClip clip = __instance.clip;
						text = ((clip != null) ? ((Object)clip).name : null) ?? "<null>";
					}
				}
				catch
				{
				}
				AudioClip originalClip = orCreateData.OriginalClip;
				string text2 = ((originalClip != null) ? ((Object)originalClip).name : null) ?? "<null>";
				string text3 = null;
				try
				{
					AudioClip realClip = orCreateData.RealClip;
					text3 = ((realClip != null) ? ((Object)realClip).name : null) ?? "<null>";
				}
				catch
				{
					text3 = "<error>";
				}
				Plugin.DebugLogAudio($"PlayPrefix called: id={((Object)__instance).GetInstanceID()} go='{((Object)((Component)__instance).gameObject).name}' instClip='{text}' orig='{text2}' real='{text3}'");
			}
			catch
			{
			}
			if (TryReplaceAudioWithType(__instance, orCreateData.OriginalClip, out var replacement, out var replacementType))
			{
				try
				{
					AudioClip originalClip2 = orCreateData.OriginalClip;
					LogSpatialAudioParams(__instance, ((originalClip2 != null) ? ((Object)originalClip2).name : null) ?? "<unknown>", replacementType);
				}
				catch
				{
				}
				if ((Object)(object)replacement == (Object)null)
				{
					return false;
				}
				if (replacementType != null && replacementType.IsAdditional)
				{
					AdditionalSoundReplacement obj5 = replacementType as AdditionalSoundReplacement;
					string mixerGroupName = obj5?.MixerGroupName;
					float? replacementVolume = obj5?.Volume;
					float maxDistanceMultiplier = obj5?.MaxDistanceMultiplier ?? 1f;
					SpawnAdditionalAudioSource(__instance, replacement, replacementType, replacementType.ShouldMuteOriginal, replacementType.Is2D, mixerGroupName, replacementVolume, maxDistanceMultiplier);
					if (replacementType.ShouldMuteOriginal)
					{
						using (new SpoofBypassContext())
						{
							__instance.volume = 0f;
						}
					}
					orCreateData.ReplacementAttempted = true;
					string[] obj6 = new string[6]
					{
						"Applied additional replacement '",
						((Object)replacement).name,
						"' for original '",
						null,
						null,
						null
					};
					AudioClip originalClip3 = orCreateData.OriginalClip;
					obj6[3] = ((originalClip3 != null) ? ((Object)originalClip3).name : null) ?? "<null>";
					obj6[4] = "' on ";
					obj6[5] = ((Object)((Component)__instance).gameObject).name;
					Plugin.DebugLogAudio(string.Concat(obj6));
					return true;
				}
				float? customPitch = replacementType?.Pitch;
				orCreateData.CustomPitch = customPitch;
				try
				{
					float num = 0f;
					try
					{
						using (new SpoofBypassContext())
						{
							_ = __instance.isPlaying;
							num = __instance.time;
						}
					}
					catch
					{
					}
					orCreateData.RealClip = replacement;
					try
					{
						string text4 = "<null>";
						try
						{
							using (new SpoofBypassContext())
							{
								AudioClip clip2 = __instance.clip;
								text4 = ((clip2 != null) ? ((Object)clip2).name : null) ?? "<null>";
								_ = __instance.isPlaying;
							}
						}
						catch
						{
						}
						AudioClip originalClip4 = orCreateData.OriginalClip;
						string text5 = ((originalClip4 != null) ? ((Object)originalClip4).name : null) ?? "<null>";
						if (text4 == text5)
						{
							try
							{
								ManualLogSource logger = Plugin.Logger;
								if (logger != null)
								{
									logger.LogWarning((object)("[PlayPrefix][Fallback] Underlying clip still '" + text4 + "', applying Stop/Play fallback to enforce replacement '" + ((Object)replacement).name + "'"));
								}
								using (new SpoofBypassContext())
								{
									__instance.Stop();
									__instance.clip = replacement;
									if (orCreateData.CustomPitch.HasValue)
									{
										__instance.pitch = orCreateData.CustomPitch.Value;
									}
									__instance.time = num;
									__instance.Play();
								}
							}
							catch (Exception ex)
							{
								ManualLogSource logger2 = Plugin.Logger;
								if (logger2 != null)
								{
									logger2.LogWarning((object)("[PlayPrefix][Fallback] Failed Stop/Play fallback: " + ex.Message));
								}
							}
						}
					}
					catch
					{
					}
					if (orCreateData.CustomPitch.HasValue)
					{
						using (new SpoofBypassContext())
						{
							try
							{
								__instance.pitch = orCreateData.CustomPitch.Value;
							}
							catch
							{
							}
						}
					}
					try
					{
						using (new SpoofBypassContext())
						{
							__instance.time = num;
						}
					}
					catch
					{
					}
					orCreateData.ReplacementAttempted = true;
					Plugin.Logger.LogInfo((object)$"Replaced audio clip with '{((Object)replacement).name}' (preserved time={num})");
					object[] obj12 = new object[4]
					{
						((Object)replacement).name,
						null,
						null,
						null
					};
					AudioClip originalClip5 = orCreateData.OriginalClip;
					obj12[1] = ((originalClip5 != null) ? ((Object)originalClip5).name : null) ?? "<null>";
					obj12[2] = ((Object)((Component)__instance).gameObject).name;
					obj12[3] = num;
					Plugin.DebugLogAudio(string.Format("Applied standard replacement '{0}' for original '{1}' on {2} (time preserved={3})", obj12));
				}
				catch (Exception ex2)
				{
					Plugin.Logger.LogWarning((object)("Failed to apply replacement without Stop/Play: " + ex2.Message));
					orCreateData.ReplacementAttempted = true;
				}
				return true;
			}
			return true;
		}

		[HarmonyPostfix]
		[HarmonyPatch("Play", new Type[] { })]
		[HarmonyPatch("Play", new Type[] { typeof(ulong) })]
		[HarmonyPatch("PlayDelayed", new Type[] { typeof(float) })]
		[HarmonyPatch("PlayScheduled", new Type[] { typeof(double) })]
		private static void PlayPostfix(AudioSource __instance)
		{
			try
			{
				string text = "<null>";
				try
				{
					using (new SpoofBypassContext())
					{
						AudioClip clip = __instance.clip;
						text = ((clip != null) ? ((Object)clip).name : null) ?? "<null>";
					}
				}
				catch
				{
				}
				Plugin.DebugLogAudio($"PlayPostfix: AudioSource id={((Object)__instance).GetInstanceID()} go='{((Object)((Component)__instance).gameObject).name}' now has clip='{text}' isPlaying={__instance.isPlaying}");
			}
			catch
			{
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch("Stop", new Type[] { })]
		private static bool Stop(AudioSource __instance)
		{
			if (!Plugin.ConfigSound.Value)
			{
				return true;
			}
			AudioSourceData orCreateData = AudioSourceData.GetOrCreateData(__instance);
			try
			{
				if ((Object)(object)orCreateData.RealClip != (Object)null && (Object)(object)orCreateData.OriginalClip != (Object)null && (Object)(object)orCreateData.RealClip != (Object)(object)orCreateData.OriginalClip)
				{
					ISoundReplacement replacementType = Plugin.GetReplacementType(((Object)orCreateData.OriginalClip).name);
					if (replacementType != null && replacementType.PlayToEnd)
					{
						AudioClip realClip = orCreateData.RealClip;
						Plugin.DebugLogAudio("Stop intercepted: skipping Stop to allow replacement '" + (((realClip != null) ? ((Object)realClip).name : null) ?? "<null>") + "' to finish on " + ((Object)((Component)__instance).gameObject).name);
						return false;
					}
				}
			}
			catch
			{
			}
			orCreateData.ReplacementAttempted = false;
			try
			{
				orCreateData.RealClip = orCreateData.OriginalClip;
			}
			catch
			{
			}
			return true;
		}

		private static void SpawnAdditionalAudioSource(AudioSource original, AudioClip clip, ISoundReplacement replacementType, bool muteOriginal, bool is2D = false, string mixerGroupName = null, float? replacementVolume = null, float maxDistanceMultiplier = 1f)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected O, but got Unknown
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0292: Unknown result type (might be due to invalid IL or missing references)
			//IL_0284: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02df: Invalid comparison between Unknown and I4
			GameObject val = new GameObject("AdditionalAudio_" + ((Object)clip).name);
			val.transform.position = ((Component)original).transform.position;
			AudioSource val2 = val.AddComponent<AudioSource>();
			try
			{
				if (!string.IsNullOrEmpty(mixerGroupName))
				{
					AudioMixerGroup outputAudioMixerGroup = original.outputAudioMixerGroup;
					AudioMixerGroup[] array = FindAudioMixerGroups((outputAudioMixerGroup != null) ? outputAudioMixerGroup.audioMixer : null);
					if (array != null)
					{
						AudioMixerGroup val3 = Array.Find(array, (AudioMixerGroup g) => ((Object)g).name == mixerGroupName);
						if ((Object)(object)val3 != (Object)null)
						{
							val2.outputAudioMixerGroup = val3;
						}
						else
						{
							Plugin.Logger.LogWarning((object)("Mixer group '" + mixerGroupName + "' not found, using original"));
							val2.outputAudioMixerGroup = original.outputAudioMixerGroup;
						}
					}
				}
				else
				{
					val2.outputAudioMixerGroup = original.outputAudioMixerGroup;
				}
			}
			catch
			{
			}
			float? num = replacementVolume;
			if (num.HasValue)
			{
				val2.volume = Math.Max(0f, Math.Min(1f, num.Value));
			}
			else
			{
				try
				{
					using (new SpoofBypassContext())
					{
						val2.volume = original.volume;
					}
				}
				catch
				{
				}
			}
			float? num2 = replacementType?.Pitch;
			if (num2.HasValue)
			{
				ManualLogSource logger = Plugin.Logger;
				if (logger != null)
				{
					logger.LogDebug((object)$"[SpawnAdditionalAudioSource] Applying replacement pitch {num2.Value} to new source for clip '{((Object)clip).name}'");
				}
				val2.pitch = num2.Value;
			}
			else
			{
				using (new SpoofBypassContext())
				{
					float pitch = original.pitch;
					ManualLogSource logger2 = Plugin.Logger;
					if (logger2 != null)
					{
						logger2.LogDebug((object)$"[SpawnAdditionalAudioSource] Copying original pitch {pitch} to new source for clip '{((Object)clip).name}'");
					}
					val2.pitch = pitch;
				}
			}
			if (is2D)
			{
				val2.spatialBlend = 0f;
			}
			else
			{
				val2.spatialBlend = original.spatialBlend;
				if (replacementType.MinDistance.HasValue)
				{
					val2.minDistance = replacementType.MinDistance.Value;
				}
				else
				{
					val2.minDistance = original.minDistance;
				}
				if (replacementType.MaxDistance.HasValue)
				{
					val2.maxDistance = replacementType.MaxDistance.Value;
				}
				else
				{
					val2.maxDistance = original.maxDistance * Math.Max(0.01f, maxDistanceMultiplier);
				}
				if (replacementType.RolloffMode.HasValue)
				{
					val2.rolloffMode = replacementType.RolloffMode.Value;
				}
				else
				{
					val2.rolloffMode = original.rolloffMode;
				}
				val2.dopplerLevel = original.dopplerLevel;
				val2.spread = original.spread;
				val2.spatialize = original.spatialize;
				val2.spatializePostEffects = original.spatializePostEffects;
				val2.reverbZoneMix = original.reverbZoneMix;
				if ((int)original.rolloffMode == 2)
				{
					try
					{
						AnimationCurve customCurve = original.GetCustomCurve((AudioSourceCurveType)0);
						if (customCurve != null)
						{
							val2.SetCustomCurve((AudioSourceCurveType)0, customCurve);
						}
					}
					catch
					{
					}
				}
			}
			val2.loop = false;
			val2.playOnAwake = false;
			val2.clip = clip;
			if (replacementType != null && replacementType.ResetTimeOnPlay)
			{
				val2.time = 0f;
			}
			val2.Play();
			if (muteOriginal)
			{
				using (new SpoofBypassContext())
				{
					try
					{
						original.mute = true;
					}
					catch
					{
					}
				}
			}
			val.AddComponent<AdditionalAudioBehaviour>().Init(original, val2, muteOriginal, clip.length + 0.5f);
		}

		public static AudioMixerGroup[] FindAudioMixerGroupsPublic(AudioMixer mixer = null)
		{
			return FindAudioMixerGroups(mixer);
		}

		private static AudioMixerGroup[] FindAudioMixerGroups(AudioMixer mixer = null)
		{
			try
			{
				if (cachedAudioMixerGroups != null && cachedAudioMixerGroups.Length != 0)
				{
					return cachedAudioMixerGroups;
				}
				AudioMixerGroup[] array = Resources.FindObjectsOfTypeAll<AudioMixerGroup>();
				if (array != null && array.Length != 0)
				{
					cachedAudioMixerGroups = array;
					return array;
				}
				if ((Object)(object)mixer != (Object)null)
				{
					if (m_GroupsField == null)
					{
						m_GroupsField = AccessTools.Field(((object)mixer).GetType(), "m_Groups");
					}
					if (m_GroupsField != null && m_GroupsField.GetValue(mixer) is AudioMixerGroup[] array2 && array2.Length != 0)
					{
						cachedAudioMixerGroups = array2;
						return array2;
					}
				}
				AudioMixer[] array3 = Resources.FindObjectsOfTypeAll<AudioMixer>();
				if (array3 != null && array3.Length != 0)
				{
					AudioMixer[] array4 = array3;
					foreach (AudioMixer val in array4)
					{
						if (m_GroupsField == null)
						{
							m_GroupsField = AccessTools.Field(((object)val).GetType(), "m_Groups");
						}
						if (m_GroupsField != null && m_GroupsField.GetValue(val) is AudioMixerGroup[] array5 && array5.Length != 0)
						{
							cachedAudioMixerGroups = array5;
							return array5;
						}
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogWarning((object)("Failed to get AudioMixer groups: " + ex.Message));
			}
			return null;
		}

		[HarmonyPrefix]
		[HarmonyPatch("PlayOneShot", new Type[]
		{
			typeof(AudioClip),
			typeof(float)
		})]
		private static bool PlayOneShot(AudioSource __instance, ref AudioClip clip)
		{
			if (!Plugin.ConfigSound.Value)
			{
				return true;
			}
			try
			{
				ISoundReplacement soundReplacement = (((Object)(object)clip != (Object)null) ? Plugin.GetReplacementType(((Object)clip).name) : null);
				if (soundReplacement != null)
				{
					AudioClip obj = clip;
					LogSpatialAudioParams(__instance, ((obj != null) ? ((Object)obj).name : null) ?? "<unknown>", soundReplacement);
				}
			}
			catch
			{
			}
			if (TryReplaceAudio(__instance, clip, out var replacement))
			{
				if ((Object)(object)replacement == (Object)null)
				{
					return false;
				}
				clip = replacement;
			}
			return true;
		}

		[HarmonyPrefix]
		[HarmonyPatch("PlayOneShot", new Type[] { typeof(AudioClip) })]
		private static bool PlayOneShotSingleParam(AudioSource __instance, ref AudioClip clip)
		{
			return PlayOneShot(__instance, ref clip);
		}

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPrefix]
		private static bool ClipSetter(AudioSource __instance, AudioClip value)
		{
			if (bypassSpoofing)
			{
				return true;
			}
			AudioSourceData orCreateData = AudioSourceData.GetOrCreateData(__instance);
			if (orCreateData.ReplacementAttempted)
			{
				orCreateData.OriginalClip = value;
				Plugin.DebugLogAudio(string.Format("ClipSetter: stored OriginalClip while replacement active: '{0}' for AudioSource id={1} go='{2}'", ((value != null) ? ((Object)value).name : null) ?? "<null>", ((Object)__instance).GetInstanceID(), ((Object)((Component)__instance).gameObject).name));
				return false;
			}
			if ((Object)(object)value != (Object)(object)orCreateData.OriginalClip)
			{
				orCreateData.OriginalClip = value;
				Plugin.DebugLogAudio(string.Format("ClipSetter: stored OriginalClip='{0}' for AudioSource id={1} go='{2}'", ((value != null) ? ((Object)value).name : null) ?? "<null>", ((Object)__instance).GetInstanceID(), ((Object)((Component)__instance).gameObject).name));
			}
			return true;
		}

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPostfix]
		private static void SpoofAudioSourceClip(AudioSource __instance, ref AudioClip __result)
		{
			if (bypassSpoofing || !Plugin.audioSourceData.TryGetValue(__instance, out var value) || (Object)(object)value.OriginalClip == (Object)null)
			{
				return;
			}
			try
			{
				if ((Object)(object)value.RealClip != (Object)(object)value.OriginalClip)
				{
					object[] obj = new object[4]
					{
						((Object)__instance).GetInstanceID(),
						((Object)((Component)__instance).gameObject).name,
						null,
						null
					};
					AudioClip realClip = value.RealClip;
					obj[2] = ((realClip != null) ? ((Object)realClip).name : null) ?? "<null>";
					AudioClip originalClip = value.OriginalClip;
					obj[3] = ((originalClip != null) ? ((Object)originalClip).name : null) ?? "<null>";
					Plugin.DebugLogAudio(string.Format("SpoofAudioSourceClip: real != original, not spoofing for AudioSource id={0} go='{1}' real='{2}' orig='{3}'", obj));
					return;
				}
			}
			catch
			{
			}
			object arg = ((Object)__instance).GetInstanceID();
			string name = ((Object)((Component)__instance).gameObject).name;
			AudioClip originalClip2 = value.OriginalClip;
			Plugin.DebugLogAudio(string.Format("SpoofAudioSourceClip: spoofing clip for AudioSource id={0} go='{1}' to OriginalClip='{2}'", arg, name, ((originalClip2 != null) ? ((Object)originalClip2).name : null) ?? "<null>"));
			__result = value.OriginalClip;
		}

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPostfix]
		private static void SpoofIsPlaying(AudioSource __instance, ref bool __result)
		{
			if (bypassSpoofing || !Plugin.audioSourceData.TryGetValue(__instance, out var _))
			{
				return;
			}
			using (new SpoofBypassContext())
			{
				__result = __instance.isPlaying;
			}
		}

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPrefix]
		private static bool PitchSetter(AudioSource __instance, ref float value)
		{
			if (bypassSpoofing)
			{
				return true;
			}
			if ((Object)(object)__instance == (Object)null)
			{
				return true;
			}
			if (Plugin.audioSourceData.TryGetValue(__instance, out var value2) && (Object)(object)value2.RealClip != (Object)null && value2.CustomPitch.HasValue)
			{
				ManualLogSource logger = Plugin.Logger;
				if (logger != null)
				{
					logger.LogDebug((object)$"[PitchSetter] Overriding pitch on '{((Object)((Component)__instance).gameObject).name}' to {value2.CustomPitch.Value}");
				}
				value = value2.CustomPitch.Value;
			}
			return true;
		}

		private static bool TryReplaceAudio(AudioSource audioSource, AudioClip originalClip, out AudioClip replacement)
		{
			replacement = null;
			if ((Object)(object)originalClip == (Object)null)
			{
				return false;
			}
			ISoundReplacement replacementType = Plugin.GetReplacementType(((Object)originalClip).name);
			if (replacementType == null)
			{
				ManualLogSource logger = Plugin.Logger;
				if (logger != null)
				{
					logger.LogInfo((object)$"[TryReplaceAudio] No replacement type for '{((Object)originalClip).name}' on AudioSource id={((Object)audioSource).GetInstanceID()} name='{((Object)((Component)audioSource).gameObject).name}'");
				}
				return false;
			}
			ManualLogSource logger2 = Plugin.Logger;
			if (logger2 != null)
			{
				logger2.LogInfo((object)("[TryReplaceAudio] Replacement type '" + replacementType.GetType().Name + "' found for '" + ((Object)originalClip).name + "'"));
			}
			string replacementForAudioSource = replacementType.GetReplacementForAudioSource(((Object)originalClip).name, audioSource);
			if (replacementForAudioSource == null)
			{
				ManualLogSource logger3 = Plugin.Logger;
				if (logger3 != null)
				{
					logger3.LogInfo((object)$"[TryReplaceAudio] Replacement type returned null for '{((Object)originalClip).name}' on AudioSource id={((Object)audioSource).GetInstanceID()}");
				}
				return false;
			}
			replacement = Plugin.GetAudioClip(replacementForAudioSource);
			if ((Object)(object)replacement == (Object)null)
			{
				ManualLogSource logger4 = Plugin.Logger;
				if (logger4 != null)
				{
					logger4.LogWarning((object)("[TryReplaceAudio] Audio clip '" + replacementForAudioSource + "' not found in cache for original '" + ((Object)originalClip).name + "'"));
				}
				return false;
			}
			ManualLogSource logger5 = Plugin.Logger;
			if (logger5 != null)
			{
				logger5.LogInfo((object)$"[TryReplaceAudio] Will replace '{((Object)originalClip).name}' with '{replacementForAudioSource}' on AudioSource id={((Object)audioSource).GetInstanceID()} name='{((Object)((Component)audioSource).gameObject).name}'");
			}
			return true;
		}

		private static bool TryReplaceAudioWithType(AudioSource audioSource, AudioClip originalClip, out AudioClip replacement, out ISoundReplacement replacementType)
		{
			replacement = null;
			replacementType = null;
			try
			{
				ManualLogSource logger = Plugin.Logger;
				if (logger != null)
				{
					string arg = ((originalClip != null) ? ((Object)originalClip).name : null) ?? "<null>";
					object arg2 = ((audioSource != null) ? new int?(((Object)audioSource).GetInstanceID()) : null);
					object obj;
					if (audioSource == null)
					{
						obj = null;
					}
					else
					{
						GameObject gameObject = ((Component)audioSource).gameObject;
						obj = ((gameObject != null) ? ((Object)gameObject).name : null);
					}
					if (obj == null)
					{
						obj = "<null>";
					}
					logger.LogWarning((object)$"[TryReplaceAudioWithType] Called for clip='{arg}' on AudioSource id={arg2} name='{obj}'");
				}
			}
			catch
			{
			}
			if ((Object)(object)originalClip == (Object)null)
			{
				return false;
			}
			replacementType = Plugin.GetReplacementType(((Object)originalClip).name);
			if (replacementType == null)
			{
				ManualLogSource logger2 = Plugin.Logger;
				if (logger2 != null)
				{
					logger2.LogInfo((object)("[TryReplaceAudioWithType] No replacement type for '" + ((Object)originalClip).name + "'"));
				}
				return false;
			}
			string replacementForAudioSource = replacementType.GetReplacementForAudioSource(((Object)originalClip).name, audioSource);
			if (replacementForAudioSource == null)
			{
				ManualLogSource logger3 = Plugin.Logger;
				if (logger3 != null)
				{
					logger3.LogInfo((object)("[TryReplaceAudioWithType] Replacement type returned null for '" + ((Object)originalClip).name + "'"));
				}
				return false;
			}
			replacement = Plugin.GetAudioClip(replacementForAudioSource);
			if ((Object)(object)replacement == (Object)null)
			{
				ManualLogSource logger4 = Plugin.Logger;
				if (logger4 != null)
				{
					logger4.LogWarning((object)("[TryReplaceAudioWithType] Audio clip '" + replacementForAudioSource + "' not found in cache for original '" + ((Object)originalClip).name + "'"));
				}
				return false;
			}
			ManualLogSource logger5 = Plugin.Logger;
			if (logger5 != null)
			{
				logger5.LogInfo((object)("[TryReplaceAudioWithType] Replacing '" + ((Object)originalClip).name + "' with '" + replacementForAudioSource + "'"));
			}
			return true;
		}

		private static void LogSpatialAudioParams(AudioSource src, string originalClipName, ISoundReplacement replacementType)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: 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_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)src == (Object)null)
			{
				return;
			}
			try
			{
				using (new SpoofBypassContext())
				{
					Vector3 val = (((Object)(object)((Component)src).transform != (Object)null) ? ((Component)src).transform.position : Vector3.zero);
					float pitch = src.pitch;
					float minDistance = src.minDistance;
					float maxDistance = src.maxDistance;
					float spatialBlend = src.spatialBlend;
					AudioRolloffMode rolloffMode = src.rolloffMode;
					float dopplerLevel = src.dopplerLevel;
					float spread = src.spread;
					float reverbZoneMix = src.reverbZoneMix;
					bool spatialize = src.spatialize;
					bool spatializePostEffects = src.spatializePostEffects;
					string text = null;
					try
					{
						AudioMixerGroup outputAudioMixerGroup = src.outputAudioMixerGroup;
						text = ((outputAudioMixerGroup != null) ? ((Object)outputAudioMixerGroup).name : null);
					}
					catch
					{
						text = "<unknown>";
					}
					ManualLogSource logger = Plugin.Logger;
					if (logger != null)
					{
						logger.LogInfo((object)string.Format("[AudioSpatial] Clip='{0}' ReplacementType='{1}' Pos={2} Pitch={3} MinDist={4} MaxDist={5} SpatialBlend={6} Rolloff={7} Doppler={8} Spread={9} Reverb={10} Spatialize={11} SpatializePost={12} MixerGroup={13}", originalClipName, replacementType?.GetType().Name ?? "<null>", val, pitch, minDistance, maxDistance, spatialBlend, rolloffMode, dopplerLevel, spread, reverbZoneMix, spatialize, spatializePostEffects, text));
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logger2 = Plugin.Logger;
				if (logger2 != null)
				{
					logger2.LogWarning((object)("[AudioSpatial] Failed to log spatial params for '" + originalClipName + "': " + ex.Message));
				}
			}
		}
	}
	[HarmonyPatch(typeof(EnemySpinny), "GetAndSyncRandomRotationRPC")]
	public class PatchRouletteTargetAngle
	{
		private static void Prefix(EnemySpinny __instance, float _targetAngleDegrees)
		{
			Plugin.rouletteAngles.GetOrCreateValue(__instance).TargetAngle = _targetAngleDegrees;
			Plugin.LastRouletteTargetAngle = _targetAngleDegrees;
			ManualLogSource logger = Plugin.Logger;
			if (logger != null)
			{
				logger.LogDebug((object)$"Target angle for {((Object)((Component)__instance).gameObject).name}: {_targetAngleDegrees}°");
			}
		}
	}
	public interface ISoundReplacement
	{
		bool IsAdditional => false;

		bool ShouldMuteOriginal => true;

		bool Is2D => false;

		bool ResetTimeOnPlay => false;

		bool PlayToEnd => false;

		float? Pitch => null;

		float? Volume => null;

		float? MinDistance => null;

		float? MaxDistance => null;

		AudioRolloffMode? RolloffMode => null;

		string GetReplacement(string originalName);

		string GetReplacementForAudioSource(string originalName, AudioSource audioSource)
		{
			return GetReplacement(originalName);
		}
	}
	public static class PlayerDamageResponder
	{
		[HarmonyPatch(typeof(HurtCollider), "PlayerHurt")]
		private static class HurtColliderPlayerHurtPatch
		{
			[HarmonyPostfix]
			private static void Postfix(HurtCollider __instance)
			{
				//IL_00bc: 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_00e8: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
				//IL_011a: Unknown result type (might be due to invalid IL or missing references)
				//IL_011f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0147: Unknown result type (might be due to invalid IL or missing references)
				//IL_0155: Unknown result type (might be due to invalid IL or missing references)
				//IL_016c: Unknown result type (might be due to invalid IL or missing references)
				//IL_009b: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
				try
				{
					if ((Object)(object)__instance == (Object)null)
					{
						return;
					}
					if (Time.time - lastDiagnosticLogTime > 10f)
					{
						lastDiagnosticLogTime = Time.time;
						Plugin.Logger.LogDebug((object)"PlayerDamageResponder: HurtCollider.PlayerHurt hook is firing (diagnostic ping).");
					}
					SemiLaser componentInParent = ((Component)__instance).GetComponentInParent<SemiLaser>();
					if ((Object)(object)componentInParent == (Object)null)
					{
						return;
					}
					AudioSource audioSource = componentInParent.audioSource;
					if (!TryResolveBeamerForSemiLaser(componentInParent, out var beamer))
					{
						Plugin.Logger.LogDebug((object)"PlayerDamageResponder: Laser hurt detected but owning Beamer not resolved yet; skipping sound.");
						return;
					}
					Vector3 val;
					try
					{
						val = (((Object)(object)beamer.enemy != (Object)null && (Object)(object)beamer.enemy.CenterTransform != (Object)null) ? beamer.enemy.CenterTransform.position : ((Component)beamer).transform.position);
					}
					catch (Exception)
					{
						val = ((Component)beamer).transform.position;
					}
					int instanceID;
					try
					{
						instanceID = ((Object)componentInParent).GetInstanceID();
					}
					catch (Exception)
					{
						instanceID = ((Object)__instance).GetInstanceID();
					}
					if (!ShouldThrottle(instanceID))
					{
						Vector3 val2 = val;
						string text;
						try
						{
							text = ((Object)((Component)componentInParent).gameObject).name;
						}
						catch (Exception)
						{
							text = "<unknown>";
						}
						object obj;
						if (!((Object)(object)audioSource != (Object)null))
						{
							obj = "<no template>";
						}
						else
						{
							Vector3 position = ((Component)audioSource).transform.position;
							obj = ((object)(Vector3)(ref position)).ToString();
						}
						string text2 = (string)obj;
						Plugin.Logger.LogInfo((object)$"PlayerDamageResponder: Laser player hurt detected (src={text}). beamerPos={val}, templatePos={text2}, playingAt={val2}");
						Plugin.PlaySoundAtPosition("system/naiden", val2, 1f, null, audioSource, 2f);
					}
				}
				catch (Exception ex4)
				{
					Plugin.Logger.LogError((object)("PlayerDamageResponder.HurtColliderPlayerHurtPatch error: " + ex4.Message));
				}
			}
		}

		[HarmonyPatch(typeof(PlayerHealth), "Hurt")]
		private static class PlayerHealthHurtPatch
		{
			[HarmonyPostfix]
			private static void Postfix(object[] __args)
			{
				//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
				//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
				//IL_0105: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b6: 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_00c8: Unknown result type (might be due to invalid IL or missing references)
				try
				{
					if (__args == null)
					{
						return;
					}
					SemiLaser val = null;
					foreach (object obj in __args)
					{
						if (obj == null)
						{
							continue;
						}
						GameObject val2 = (GameObject)((obj is GameObject) ? obj : null);
						if (val2 != null)
						{
							val = val2.GetComponent<SemiLaser>();
						}
						else
						{
							SemiLaser val3 = (SemiLaser)((obj is SemiLaser) ? obj : null);
							if (val3 != null)
							{
								val = val3;
							}
						}
						if ((Object)(object)val != (Object)null)
						{
							break;
						}
					}
					if ((Object)(object)val == (Object)null)
					{
						return;
					}
					AudioSource audioSource = val.audioSource;
					if (!TryResolveBeamerForSemiLaser(val, out var beamer))
					{
						Plugin.Logger.LogDebug((object)"PlayerDamageResponder.PlayerHealthHurtPatch: Laser detected but owning Beamer not resolved; skipping sound.");
						return;
					}
					Vector3 position;
					try
					{
						position = (((Object)(object)beamer.enemy != (Object)null && (Object)(object)beamer.enemy.CenterTransform != (Object)null) ? beamer.enemy.CenterTransform.position : ((Component)beamer).transform.position);
					}
					catch (Exception)
					{
						position = ((Component)beamer).transform.position;
					}
					int instanceID;
					try
					{
						instanceID = ((Object)val).GetInstanceID();
					}
					catch (Exception)
					{
						instanceID = ((Object)beamer).GetInstanceID();
					}
					if (!ShouldThrottle(instanceID))
					{
						Plugin.PlaySoundAtPosition("system/naiden", position, 1f, null, audioSource, 2f);
					}
				}
				catch (Exception ex3)
				{
					Plugin.Logger.LogError((object)("PlayerDamageResponder.PlayerHealthHurtPatch error: " + ex3.Message));
				}
			}
		}

		private const float LaserHitSoundCooldownSeconds = 1f;

		private const float DiagnosticLogIntervalSeconds = 10f;

		private static float lastDiagnosticLogTime;

		private static readonly Dictionary<int, float> lastPlayedTimeBySourceId = new Dictionary<int, float>();

		private static readonly Dictionary<int, EnemyBeamer> beamerBySemiLaserId = new Dictionary<int, EnemyBeamer>();

		private static bool TryResolveBeamerForSemiLaser(SemiLaser semiLaser, out EnemyBeamer beamer)
		{
			beamer = null;
			if ((Object)(object)semiLaser == (Object)null)
			{
				return false;
			}
			int instanceID;
			try
			{
				instanceID = ((Object)semiLaser).GetInstanceID();
			}
			catch (Exception)
			{
				return false;
			}
			if (beamerBySemiLaserId.TryGetValue(instanceID, out var value) && (Object)(object)value != (Object)null)
			{
				beamer = value;
				return true;
			}
			try
			{
				EnemyBeamer[] array = Object.FindObjectsOfType<EnemyBeamer>();
				foreach (EnemyBeamer val in array)
				{
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					try
					{
						if ((Object)(object)val.laser != (Object)null && val.laser == semiLaser)
						{
							beamerBySemiLaserId[instanceID] = val;
							beamer = val;
							return true;
						}
					}
					catch (Exception ex2)
					{
						Plugin.Logger.LogDebug((object)("Error checking beamer laser: " + ex2.Message));
					}
				}
			}
			catch (Exception ex3)
			{
				Plugin.Logger.LogDebug((object)("Error resolving beamer for SemiLaser: " + ex3.Message));
			}
			return false;
		}

		private static bool ShouldThrottle(int sourceId)
		{
			float now = Time.time;
			if (lastPlayedTimeBySourceId.TryGetValue(sourceId, out var value) && now - value < 1f)
			{
				return true;
			}
			lastPlayedTimeBySourceId[sourceId] = now;
			if (lastPlayedTimeBySourceId.Count > 512)
			{
				int[] array = (from kvp in lastPlayedTimeBySourceId
					where now - kvp.Value > 60f
					select kvp.Key).ToArray();
				foreach (int key in array)
				{
					lastPlayedTimeBySourceId.Remove(key);
				}
			}
			return false;
		}

		public static void PatchPlayerDamage()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				new Harmony("kossnikitaMatesMod.playerdamage").PatchAll(typeof(PlayerDamageResponder));
				Plugin.Logger.LogInfo((object)"PlayerDamageResponder: Patches applied via attributes.");
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogError((object)("PlayerDamageResponder patching failed: " + ex.Message));
			}
		}
	}
	[BepInPlugin("kossnikitaMatesMod", "kossnikita mates mod", "1.0.3")]
	public class Plugin : BaseUnityPlugin
	{
		public class RouletteAngleData
		{
			public float TargetAngle { get; set; }
		}

		internal static ManualLogSource Logger;

		public static ConfigEntry<bool> ConfigSound = null;

		public static ConfigEntry<bool> DebugAudio = null;

		public static ConfigEntry<bool> ConfigEnableTextureReplacement = null;

		public static ConfigEntry<bool> ConfigEnableLoadingUIRevUp = null;

		public static ConfigEntry<bool> ConfigEnableSmeshariki = null;

		public static ConfigEntry<bool> ConfigEnableTrapMusicboxMusic = null;

		public static ConfigEntry<bool> ConfigEnableCrazyFrog = null;

		public static ConfigEntry<bool> ConfigEnableSystemPoiska = null;

		public static ConfigEntry<bool> ConfigEnableKazik = null;

		public static ConfigEntry<bool> ConfigEnableNapas = null;

		public static ConfigEntry<bool> ConfigEnableUpgrade = null;

		public static ConfigEntry<string> HunterShotType = null;

		private static Dictionary<string, Texture2D> textureCache = new Dictionary<string, Texture2D>();

		private AssetBundle assetBundle;

		private static Dictionary<string, AudioClip> audioClips = new Dictionary<string, AudioClip>();

		public static ConditionalWeakTable<AudioSource, AudioSourceData> audioSourceData = new ConditionalWeakTable<AudioSource, AudioSourceData>();

		private static Dictionary<string, ISoundReplacement> soundReplacements = new Dictionary<string, ISoundReplacement>
		{
			["loading ui rev up"] = new ConditionalSoundReplacement("misc/vstroiku", (int count) => count % 2 == 1, () => ConfigEnableLoadingUIRevUp, 1f),
			["headman chase begin"] = new SimpleSoundReplacement("smesharikibegin", () => ConfigEnableSmeshariki),
			["headman chase loop"] = new SequentialSoundReplacement(new string[2] { "smesharikiloop01", "smesharikiloop02" }, () => ConfigEnableSmeshariki),
			["trap musicbox music"] = new SequentialSoundReplacement(new string[9] { "musicbox/9mm", "musicbox/autist", "musicbox/daleko", "musicbox/daymne", "musicbox/podomnoy", "musicbox/sinyak", "musicbox/vodila", "musicbox/yapotyanu", "musicbox/zerkalo" }, () => ConfigEnableTrapMusicboxMusic),
			["enemy tumbler tumble loop global"] = new SimpleSoundReplacement("crazy-frog", () => ConfigEnableCrazyFrog),
			["enemy tumbler hurt01"] = new SimpleSoundReplacement("bing", () => ConfigEnableCrazyFrog),
			["enemy tumbler hurt02"] = new SimpleSoundReplacement("bing", () => ConfigEnableCrazyFrog),
			["enemy tumbler hurt03"] = new SimpleSoundReplacement("bing", () => ConfigEnableCrazyFrog),
			["enemy slow walker death"] = new AdditionalSoundReplacement("timenyaposlal"),
			["enemy beamer attack intro"] = new AdditionalSoundReplacement("system/sistemapoiska", () => ConfigEnableSystemPoiska, muteOriginal: false, is2D: false, null, 0.4f, 0.5f),
			["enemy beamer attack intro global"] = new AdditionalSoundReplacement("system/sistemapoiska1global", () => ConfigEnableSystemPoiska, muteOriginal: false),
			["enemy ceiling eye staring start"] = new AdditionalSoundReplacement("tvoya_popochka_vozbuzdaet", null, muteOriginal: false, is2D: false, "Sound Effects", 0.4f, 0.01f),
			["enemy spinny music"] = new RouletteColorSoundReplacement("kazino/jackpot", "kazino/esli_hochesh", () => ConfigEnableKazik),
			["enemy spinny music leaving"] = new RouletteColorSoundReplacement("kazino/jackpot_leaving", "kazino/esli_hochesh_leaving", () => ConfigEnableKazik),
			["enemy spinny music muffled"] = new SimpleSoundReplacement("kazino/esli_hochesh_muffled", () => ConfigEnableKazik),
			["gramophone music"] = new SimpleSoundReplacement("gramophone_napas", () => ConfigEnableNapas),
			["player upgrade"] = new SimpleSoundReplacement("amamamminecraft", () => ConfigEnableUpgrade),
			["enemy bowtie scream loop"] = new SimpleSoundReplacement("cheboksary"),
			["enemy bowtie scream loop global"] = new SimpleSoundReplacement("cheboksaryglobal"),
			["extraction point surplus spawn"] = new AdditionalSequentialSoundReplacement(new string[6] { "extraction/herecomes", "extraction/platina01", "extraction/abba", "extraction/platina02", "extraction/gimmetheloot", "extraction/instasamka" }),
			["enemy hidden man player pickup"] = new SimpleSoundReplacement("dejavu/dejavu", null, 1f),
			["enemy hidden man player release"] = new AdditionalSoundReplacement("dejavu/dejavuijustbeenthisplacebefore", null, muteOriginal: true, is2D: false, null, pitch: 1f, volume: 0.2f),
			["enemy hidden man footstep sprint01"] = new SimpleSoundReplacement("dejavu/dejavu1", null, 1f),
			["enemy hidden man footstep sprint02"] = new SimpleSoundReplacement("dejavu/dejavu2", null, 1f),
			["enemy hidden man footstep sprint03"] = new SimpleSoundReplacement("dejavu/dejavuaha", null, 1f),
			["enemy hidden man player move loop"] = new SimpleSoundReplacement("dejavu/dejavusection", null, 1f),
			["enemy upscream scream01"] = new SimpleSoundReplacement("goida/scream01"),
			["enemy upscream scream02"] = new SimpleSoundReplacement("goida/scream02"),
			["enemy upscream scream03"] = new SimpleSoundReplacement("goida/scream03"),
			["enemy upscream scream global01"] = new SimpleSoundReplacement("goida/global01"),
			["enemy upscream scream global02"] = new SimpleSoundReplacement("goida/global02"),
			["enemy upscream scream global03"] = new SimpleSoundReplacement("goida/global03"),
			["enemy hunter shoot"] = new DynamicSoundReplacement(delegate
			{
				string text2 = HunterShotType.Value.ToLowerInvariant();
				if (text2 == "kraber")
				{
					return "apex/kraber shot";
				}
				return (text2 == "sentinel") ? "apex/sentinel shot" : null;
			}, () => !string.Equals(HunterShotType.Value, "standard", StringComparison.OrdinalIgnoreCase)),
			["enemy hunter shoot global"] = new DynamicSoundReplacement(delegate
			{
				string text = HunterShotType.Value.ToLowerInvariant();
				if (text == "kraber")
				{
					return "apex/kraber shot global";
				}
				return (text == "sentinel") ? "apex/sentinel shot global" : null;
			}, () => !string.Equals(HunterShotType.Value, "standard", StringComparison.OrdinalIgnoreCase)),
			["ambience breaker global14"] = new SimpleSoundReplacement("arat"),
			["ambience breaker arctic01"] = new SimpleSoundReplacement("arat"),
			["ambience breaker wizard06"] = new SimpleSoundReplacement("arat"),
			["ambience breaker manor08"] = new SimpleSoundReplacement("binauralknocking"),
			["sledgehammer hit01"] = new WeightedRandomSoundReplacement(new WeightedSound[3]
			{
				new WeightedSound("armatura1", 40),
				new WeightedSound("armatura2", 30),
				new WeightedSound("armatura3", 30)
			}),
			["sledgehammer hit02"] = new WeightedRandomSoundReplacement(new WeightedSound[3]
			{
				new WeightedSound("armatura1", 40),
				new WeightedSound("armatura2", 30),
				new WeightedSound("armatura3", 30)
			}),
			["sledgehammer hit03"] = new WeightedRandomSoundReplacement(new WeightedSound[3]
			{
				new WeightedSound("armatura1", 40),
				new WeightedSound("armatura2", 30),
				new WeightedSound("armatura3", 30)
			}),
			["trap doll loop"] = new SimpleSoundReplacement("raspilimenya"),
			["player revive"] = new SimpleSoundReplacement("yarodilsa")
		};

		public static ConcurrentDictionary<string, int> playbackCounters = new ConcurrentDictionary<string, int>();

		public static float LastRouletteTargetAngle = 0f;

		public static ConditionalWeakTable<EnemySpinny, RouletteAngleData> rouletteAngles = new ConditionalWeakTable<EnemySpinny, RouletteAngleData>();

		private const string modResourcePath = "assets/mods/mates/";

		private void Awake()
		{
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0177: Expected O, but got Unknown
			//IL_021b: Unknown result type (might be due to invalid IL or missing references)
			Logger = ((BaseUnityPlugin)this).Logger;
			ConfigSound = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableSound", true, "Enable or disable sound effects in the plugin.");
			ConfigEnableLoadingUIRevUp = ((BaseUnityPlugin)this).Config.Bind<bool>("Sound Replacements", "LoadingTruck", true, "Enable replacement for 'loading ui rev up'");
			ConfigEnableSmeshariki = ((BaseUnityPlugin)this).Config.Bind<bool>("Sound Replacements", "Smeshariki", true, "Enable replacement for 'smeshariki'");
			ConfigEnableTrapMusicboxMusic = ((BaseUnityPlugin)this).Config.Bind<bool>("Sound Replacements", "TrapMusicboxMusic", true, "Enable replacement for 'trap musicbox music'");
			ConfigEnableCrazyFrog = ((BaseUnityPlugin)this).Config.Bind<bool>("Sound Replacements", "CrazyFrog", true, "Enable replacement for Crazy Frog");
			ConfigEnableSystemPoiska = ((BaseUnityPlugin)this).Config.Bind<bool>("Sound Replacements", "SystemPoiska", true, "Enable replacement for 'system poiska'");
			ConfigEnableKazik = ((BaseUnityPlugin)this).Config.Bind<bool>("Sound Replacements", "Kazik", true, "Enable replacement for 'kazik' sound");
			ConfigEnableNapas = ((BaseUnityPlugin)this).Config.Bind<bool>("Sound Replacements", "Napas", true, "Enable replacement for 'napas' sound");
			ConfigEnableUpgrade = ((BaseUnityPlugin)this).Config.Bind<bool>("Sound Replacements", "Upgrade", true, "Enable replacement for 'upgrade' sound");
			HunterShotType = ((BaseUnityPlugin)this).Config.Bind<string>("Sound Replacements", "HunterShotType", "standard", new ConfigDescription("Type of sound to play for Hunter shots (standard, kraber, sentinel)", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[3] { "standard", "kraber", "sentinel" }), Array.Empty<object>()));
			DebugAudio = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugAudio", false, "Enable verbose audio debug logs (very chatty)");
			assetBundle = LoadAssetBundleFromFile("mates");
			if ((Object)(object)assetBundle == (Object)null)
			{
				Logger.LogError((object)"Failed to load asset bundle.");
				return;
			}
			LoadAllAudioClips();
			if (DebugAudio != null && DebugAudio.Value)
			{
				try
				{
					DumpAudioClipKeys(200);
				}
				catch
				{
				}
			}
			Logger.LogInfo((object)$"Loaded {soundReplacements.Count} sound replacements");
			new Harmony("kossnikitaMatesMod").PatchAll();
			Logger.LogInfo((object)"Harmony patches applied successfully!");
			try
			{
				PlayerDamageResponder.PatchPlayerDamage();
			}
			catch (Exception ex)
			{
				Logger.LogError((object)("Failed to install PlayerDamageResponder: " + ex.Message));
			}
			Logger.LogInfo((object)"Plugin kossnikitaMatesMod is loaded!");
		}

		private AssetBundle LoadAssetBundleFromFile(string fileName)
		{
			try
			{
				string text = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? AppDomain.CurrentDomain.BaseDirectory, fileName);
				if (!File.Exists(text))
				{
					Logger.LogError((object)("AssetBundle file not found at '" + text + "'. Expected it next to the plugin DLL."));
					return null;
				}
				AssetBundle val = AssetBundle.LoadFromFile(text);
				if ((Object)(object)val != (Object)null)
				{
					Logger.LogDebug((object)("Successfully loaded AssetBundle from file: " + text));
					return val;
				}
				Logger.LogError((object)("Failed to load AssetBundle from file: " + text));
				return null;
			}
			catch (Exception ex)
			{
				Logger.LogError((object)("Failed to load AssetBundle from file: " + ex.Message));
				return null;
			}
		}

		private void LoadAllAudioClips()
		{
			string[] allAssetNames = assetBundle.GetAllAssetNames();
			foreach (string text in allAssetNames)
			{
				if (text.EndsWith(".mp3") || text.EndsWith(".wav") || text.EndsWith(".ogg"))
				{
					AudioClip val = assetBundle.LoadAsset<AudioClip>(text);
					if ((Object)(object)val != (Object)null)
					{
						string key = text.Replace(Path.GetExtension(text), "").Substring("assets/mods/mates/".Length).ToLowerInvariant();
						audioClips[key] = val;
					}
					else
					{
						Logger.LogWarning((object)("Failed to load audio clip: " + text));
					}
				}
			}
			Logger.LogDebug((object)$"Loaded {audioClips.Count} audio clips and {textureCache.Count} textures into cache");
			if (textureCache.Count > 0)
			{
				Logger.LogDebug((object)$"Loaded {textureCache.Count} textures into cache");
			}
		}

		public static AudioClip GetAudioClip(string clipName)
		{
			if (string.IsNullOrEmpty(clipName))
			{
				return null;
			}
			if (audioClips.TryGetValue(clipName, out var value))
			{
				return value;
			}
			Logger.LogError((object)("Audio clip not found in cache: " + clipName));
			return null;
		}

		private static string NormalizeLookupKey(string key)
		{
			if (string.IsNullOrEmpty(key))
			{
				return key;
			}
			string text = key.ToLowerInvariant().Trim();
			text = text.Replace('\\', '/');
			if (text.EndsWith(".mp3") || text.EndsWith(".wav") || text.EndsWith(".ogg"))
			{
				text = text.Substring(0, text.LastIndexOf('.'));
			}
			if (text.StartsWith("assets/mods/mates/"))
			{
				text = text.Substring("assets/mods/mates/".Length);
			}
			text = text.Replace('_', ' ').Replace('-', ' ');
			while (text.Contains("  "))
			{
				text = text.Replace("  ", " ");
			}
			return text;
		}

		public static Texture2D GetReplacedTexture(string textureName)
		{
			if (!ConfigEnableTextureReplacement.Value)
			{
				return null;
			}
			if (string.IsNullOrEmpty(textureName))
			{
				return null;
			}
			if (textureCache.TryGetValue(textureName, out var value))
			{
				Logger.LogDebug((object)("Found replacement texture for '" + textureName + "'"));
				return value;
			}
			return null;
		}

		public static AudioClip GetReplacedSound(string originalName)
		{
			if (soundReplacements.TryGetValue(originalName, out var value))
			{
				string replacement = value.GetReplacement(originalName);
				if (replacement != null)
				{
					AudioClip audioClip = GetAudioClip(replacement);
					if ((Object)(object)audioClip != (Object)null)
					{
						return audioClip;
					}
					Logger.LogWarning((object)("Replacement sound '" + replacement + "' not found for '" + originalName + "'"));
				}
			}
			return null;
		}

		public static void DebugLogAudio(string message)
		{
			try
			{
				if (DebugAudio != null && DebugAudio.Value)
				{
					Logger.LogDebug((object)("[DebugAudio] " + message));
				}
			}
			catch
			{
			}
		}

		public static void DumpAudioClipKeys(int max = 50)
		{
			List<string> list = new List<string>(audioClips.Keys);
			Logger.LogDebug((object)$"[DebugAudio] audioClips count={list.Count}");
			for (int i = 0; i < Math.Min(max, list.Count); i++)
			{
				Logger.LogDebug((object)$"[DebugAudio] clip[{i}] = {list[i]}");
			}
		}

		public static bool HasReplacedSound(string originalName)
		{
			return soundReplacements.ContainsKey(originalName);
		}

		public static ISoundReplacement GetReplacementType(string originalName)
		{
			if (string.IsNullOrEmpty(originalName))
			{
				return null;
			}
			if (soundReplacements.TryGetValue(originalName, out var value))
			{
				return value;
			}
			ManualLogSource logger = Logger;
			if (logger != null)
			{
				logger.LogDebug((object)("[GetReplacementType] No replacement found for '" + originalName + "'"));
			}
			return null;
		}

		public static void PlaySound(string clipName, float volume = 1f)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Expected O, but got Unknown
			if (ConfigSound.Value)
			{
				AudioClip audioClip = GetAudioClip(clipName);
				if ((Object)(object)audioClip != (Object)null)
				{
					GameObject val = new GameObject("Audio_" + clipName);
					AudioSource obj = val.AddComponent<AudioSource>();
					obj.clip = audioClip;
					obj.volume = volume;
					obj.Play();
					Object.Destroy((Object)val, audioClip.length);
				}
			}
		}

		public static void PlaySoundAtPosition(string clipName, Vector3 position, float volume = 1f, string mixerGroupName = null, AudioSource templateSource = null, float maxDistanceMultiplier = 1f)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Expected O, but got Unknown
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Invalid comparison between Unknown and I4
			if (!ConfigSound.Value)
			{
				return;
			}
			AudioClip audioClip = GetAudioClip(clipName);
			if ((Object)(object)audioClip == (Object)null)
			{
				Logger.LogError((object)("PlaySoundAtPosition: Audio clip '" + clipName + "' not found in cache!"));
				return;
			}
			GameObject val = new GameObject("Audio_" + clipName);
			val.transform.position = position;
			AudioSource val2 = val.AddComponent<AudioSource>();
			val2.volume = Math.Max(0f, volume);
			val2.pitch = 1f;
			val2.spatialBlend = 1f;
			val2.minDistance = 1f;
			val2.maxDistance = 500f * Math.Max(0.01f, maxDistanceMultiplier);
			val2.rolloffMode = (AudioRolloffMode)0;
			val2.loop = false;
			val2.playOnAwake = false;
			val2.clip = audioClip;
			if ((Object)(object)templateSource != (Object)null)
			{
				try
				{
					val2.outputAudioMixerGroup = templateSource.outputAudioMixerGroup;
					val2.spatialBlend = templateSource.spatialBlend;
					val2.minDistance = templateSource.minDistance;
					val2.maxDistance = templateSource.maxDistance * Math.Max(0.01f, maxDistanceMultiplier);
					val2.rolloffMode = templateSource.rolloffMode;
					val2.dopplerLevel = templateSource.dopplerLevel;
					val2.spread = templateSource.spread;
					if ((int)templateSource.rolloffMode == 2)
					{
						AnimationCurve customCurve = templateSource.GetCustomCurve((AudioSourceCurveType)0);
						if (customCurve != null)
						{
							val2.SetCustomCurve((AudioSourceCurveType)0, customCurve);
						}
					}
					val2.spatialize = templateSource.spatialize;
					val2.spatializePostEffects = templateSource.spatializePostEffects;
					val2.reverbZoneMix = templateSource.reverbZoneMix;
				}
				catch (Exception ex)
				{
					Logger.LogWarning((object)("PlaySoundAtPosition: Failed to copy template AudioSource settings: " + ex.Message));
				}
			}
			val2.Play();
			Object.Destroy((Object)(object)val, audioClip.length + 0.5f);
		}

		private static void LogAvailableAudioMixers()
		{
		}
	}
	public abstract class CounterBasedSoundReplacement : ISoundReplacement
	{
		protected readonly Func<ConfigEntry<bool>> configGetter;

		protected CounterBasedSoundReplacement(Func<ConfigEntry<bool>> configGetter)
		{
			this.configGetter = configGetter;
		}

		protected int GetAndIncrementCounter(string originalName)
		{
			return Plugin.playbackCounters.AddOrUpdate(originalName, 1, (string key, int oldValue) => oldValue + 1);
		}

		public abstract string GetReplacement(string originalName);
	}
	public class SimpleSoundReplacement : ISoundReplacement
	{
		private readonly string replacementName;

		private readonly Func<ConfigEntry<bool>> configGetter;

		private readonly float? pitch;

		private readonly float? volume;

		private readonly float? minDistance;

		private readonly float? maxDistance;

		private readonly AudioRolloffMode? rolloffMode;

		public float? Pitch => pitch;

		public float? Volume => volume;

		public float? MinDistance => minDistance;

		public float? MaxDistance => maxDistance;

		public AudioRolloffMode? RolloffMode => rolloffMode;

		public SimpleSoundReplacement(string replacementName, Func<ConfigEntry<bool>> configGetter = null, float? pitch = null, float? volume = null, float? minDistance = null, float? maxDistance = null, AudioRolloffMode? rolloffMode = null)
		{
			this.replacementName = replacementName;
			this.configGetter = configGetter;
			this.pitch = pitch;
			this.volume = volume;
			this.minDistance = minDistance;
			this.maxDistance = maxDistance;
			this.rolloffMode = rolloffMode;
		}

		public string GetReplacement(string originalName)
		{
			Func<ConfigEntry<bool>> func = configGetter;
			if (func != null && func()?.Value == false)
			{
				return null;
			}
			return replacementName;
		}
	}
	public class WeightedRandomSoundReplacement : ISoundReplacement
	{
		private readonly WeightedSound[] sounds;

		private readonly int totalWeight;

		private readonly Func<ConfigEntry<bool>> configGetter;

		private readonly float? volume;

		private readonly float? minDistance;

		private readonly float? maxDistance;

		private readonly AudioRolloffMode? rolloffMode;

		public float? Volume => volume;

		public float? MinDistance => minDistance;

		public float? MaxDistance => maxDistance;

		public AudioRolloffMode? RolloffMode => rolloffMode;

		public WeightedRandomSoundReplacement(WeightedSound[] sounds, Func<ConfigEntry<bool>> configGetter = null, float? volume = null, float? minDistance = null, float? maxDistance = null, AudioRolloffMode? rolloffMode = null)
		{
			this.sounds = sounds;
			totalWeight = sounds.Sum((WeightedSound s) => s.Weight);
			this.configGetter = configGetter;
			this.volume = volume;
			this.minDistance = minDistance;
			this.maxDistance = maxDistance;
			this.rolloffMode = rolloffMode;
		}

		public string GetReplacement(string originalName)
		{
			Func<ConfigEntry<bool>> func = configGetter;
			if (func != null && func()?.Value == false)
			{
				return null;
			}
			int num = Random.Range(0, totalWeight);
			int num2 = 0;
			WeightedSound[] array = sounds;
			for (int i = 0; i < array.Length; i++)
			{
				WeightedSound weightedSound = array[i];
				num2 += weightedSound.Weight;
				if (num < num2)
				{
					return weightedSound.Name;
				}
			}
			return sounds[0].Name;
		}
	}
	public struct WeightedSound
	{
		public string Name;

		public int Weight;

		public WeightedSound(string name, int weight)
		{
			Name = name;
			Weight = weight;
		}
	}
	public class ConditionalSoundReplacement : CounterBasedSoundReplacement
	{
		private readonly string replacementName;

		private readonly Func<int, bool> condition;

		private readonly float? pitch;

		private readonly float? volume;

		private readonly float? minDistance;

		private readonly float? maxDistance;

		private readonly AudioRolloffMode? rolloffMode;

		public float? Pitch => pitch;

		public float? Volume => volume;

		public float? MinDistance => minDistance;

		public float? MaxDistance => maxDistance;

		public AudioRolloffMode? RolloffMode => rolloffMode;

		public ConditionalSoundReplacement(string replacementName, Func<int, bool> condition, Func<ConfigEntry<bool>> configGetter = null, float? pitch = null, float? volume = null, float? minDistance = null, float? maxDistance = null, AudioRolloffMode? rolloffMode = null)
			: base(configGetter)
		{
			this.replacementName = replacementName;
			this.condition = condition;
			this.pitch = pitch;
			this.volume = volume;
			this.minDistance = minDistance;
			this.maxDistance = maxDistance;
			this.rolloffMode = rolloffMode;
		}

		public override string GetReplacement(string originalName)
		{
			Func<ConfigEntry<bool>> func = configGetter;
			if (func != null && func()?.Value == false)
			{
				return null;
			}
			int andIncrementCounter = GetAndIncrementCounter(originalName);
			if (condition(andIncrementCounter))
			{
				return replacementName;
			}
			return null;
		}
	}
	public class SequentialSoundReplacement : CounterBasedSoundReplacement
	{
		private readonly string[] sounds;

		private readonly float maxDistanceMultiplier;

		private readonly float? volume;

		private readonly float? minDistance;

		private readonly float? maxDistance;

		private readonly AudioRolloffMode? rolloffMode;

		private readonly bool playtoend;

		private readonly float? pitch;

		public bool PlayToEnd => playtoend;

		public float? Pitch => pitch;

		public float MaxDistanceMultiplier => maxDistanceMultiplier;

		public float? Volume => volume;

		public float? MinDistance => minDistance;

		public float? MaxDistance => maxDistance;

		public AudioRolloffMode? RolloffMode => rolloffMode;

		public SequentialSoundReplacement(string[] sounds, Func<ConfigEntry<bool>> configGetter = null, float maxDistanceMultiplier = 1f, float? volume = null, bool playtoend = false, float? pitch = null, float? minDistance = null, float? maxDistance = null, AudioRolloffMode? rolloffMode = null)
			: base(configGetter)
		{
			this.sounds = sounds;
			this.maxDistanceMultiplier = Math.Max(0.01f, maxDistanceMultiplier);
			this.volume = (volume.HasValue ? new float?(Math.Max(0f, volume.Value)) : null);
			this.playtoend = playtoend;
			this.pitch = pitch;
			this.minDistance = minDistance;
			this.maxDistance = maxDistance;
			this.rolloffMode = rolloffMode;
		}

		public override string GetReplacement(string originalName)
		{
			Func<ConfigEntry<bool>> func = configGetter;
			if (func != null && func()?.Value == false)
			{
				return null;
			}
			int num = (GetAndIncrementCounter(originalName) - 1) % sounds.Length;
			return sounds[num];
		}

		public string GetReplacementForAudioSource(string originalName, AudioSource audioSource)
		{
			return GetReplacement(originalName);
		}
	}
	public class AdditionalSoundReplacement : ISoundReplacement
	{
		private readonly string replacementName;

		private readonly Func<ConfigEntry<bool>> configGetter;

		private readonly bool muteOriginal;

		private readonly bool is2D;

		private readonly string mixerGroupName;

		private readonly float? volume;

		private readonly float maxDistanceMultiplier;

		private readonly float? minDistance;

		private readonly float? maxDistance;

		private readonly AudioRolloffMode? rolloffMode;

		private readonly float? pitch;

		public bool ShouldMuteOriginal => muteOriginal;

		public bool IsAdditional => true;

		public bool Is2D => is2D;

		public string MixerGroupName => mixerGroupName;

		public float? Volume => volume;

		public float MaxDistanceMultiplier => maxDistanceMultiplier;

		public float? MinDistance => minDistance;

		public float? MaxDistance => maxDistance;

		public AudioRolloffMode? RolloffMode => rolloffMode;

		public float? Pitch => pitch;

		public AdditionalSoundReplacement(string replacementName, Func<ConfigEntry<bool>> configGetter = null, bool muteOriginal = true, bool is2D = false, string mixerGroupName = null, float? volume = null, float maxDistanceMultiplier = 1f, float? pitch = null, float? minDistance = null, float? maxDistance = null, AudioRolloffMode? rolloffMode = null)
		{
			this.replacementName = replacementName;
			this.configGetter = configGetter;
			this.muteOriginal = muteOriginal;
			this.is2D = is2D;
			this.mixerGroupName = mixerGroupName;
			this.volume = (volume.HasValue ? new float?(Math.Max(0f, Math.Min(1f, volume.Value))) : null);
			this.maxDistanceMultiplier = Math.Max(0.01f, maxDistanceMultiplier);
			this.minDistance = minDistance;
			this.maxDistance = maxDistance;
			this.rolloffMode = rolloffMode;
			this.pitch = pitch;
		}

		public string GetReplacement(string originalName)
		{
			Func<ConfigEntry<bool>> func = configGetter;
			if (func != null && func()?.Value == false)
			{
				return null;
			}
			return replacementName;
		}
	}
	public class RouletteColorSoundReplacement : ISoundReplacement
	{
		private readonly string blackPrizeSound;

		private readonly string otherPrizeSound;

		private readonly Func<ConfigEntry<bool>> configGetter;

		private readonly bool resetTimeOnPlay;

		public bool ResetTimeOnPlay => resetTimeOnPlay;

		public RouletteColorSoundReplacement(string blackPrizeSound, string otherPrizeSound, Func<ConfigEntry<bool>> configGetter = null, bool resetTimeOnPlay = true)
		{
			this.blackPrizeSound = blackPrizeSound;
			this.otherPrizeSound = otherPrizeSound;
			this.configGetter = configGetter;
			this.resetTimeOnPlay = resetTimeOnPlay;
		}

		public string GetReplacement(string originalName)
		{
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Invalid comparison between Unknown and I4
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			Func<ConfigEntry<bool>> func = configGetter;
			if (func != null && func()?.Value == false)
			{
				return otherPrizeSound;
			}
			try
			{
				EnemySpinny val = Object.FindObjectOfType<EnemySpinny>();
				if ((Object)(object)val == (Object)null)
				{
					ManualLogSource logger = Plugin.Logger;
					if (logger != null)
					{
						logger.LogWarning((object)"[Roulette] EnemySpinny not found, using default sound");
					}
					return otherPrizeSound;
				}
				float num = Plugin.LastRouletteTargetAngle;
				if (Plugin.rouletteAngles.TryGetValue(val, out var value))
				{
					num = value.TargetAngle;
				}
				Colors currentColorbyAngle = val.GetCurrentColorbyAngle(num);
				ManualLogSource logger2 = Plugin.Logger;
				if (logger2 != null)
				{
					logger2.LogDebug((object)$"[Roulette] Angle: {num}°, Prize: {currentColorbyAngle}");
				}
				if ((int)currentColorbyAngle == 4)
				{
					ManualLogSource logger3 = Plugin.Logger;
					if (logger3 != null)
					{
						logger3.LogDebug((object)("[Roulette] BLACK PRIZE! Playing: " + blackPrizeSound));
					}
					return blackPrizeSound;
				}
				ManualLogSource logger4 = Plugin.Logger;
				if (logger4 != null)
				{
					logger4.LogDebug((object)$"[Roulette] Other prize: {currentColorbyAngle}. Playing: {otherPrizeSound}");
				}
				return otherPrizeSound;
			}
			catch (Exception ex)
			{
				ManualLogSource logger5 = Plugin.Logger;
				if (logger5 != null)
				{
					logger5.LogError((object)("[Roulette] Error determining prize: " + ex.Message));
				}
				return otherPrizeSound;
			}
		}
	}
	public class DynamicSoundReplacement : ISoundReplacement
	{
		private readonly Func<string> replacementProvider;

		private readonly Func<bool> enabledProvider;

		private readonly float? pitch;

		private readonly float? volume;

		private readonly float? minDistance;

		private readonly float? maxDistance;

		private readonly AudioRolloffMode? rolloffMode;

		public float? Pitch => pitch;

		public float? Volume => volume;

		public float? MinDistance => minDistance;

		public float? MaxDistance => maxDistance;

		public AudioRolloffMode? RolloffMode => rolloffMode;

		public DynamicSoundReplacement(Func<string> replacementProvider, Func<bool> enabledProvider = null, float? pitch = null, float? volume = null, float? minDistance = null, float? maxDistance = null, AudioRolloffMode? rolloffMode = null)
		{
			this.replacementProvider = replacementProvider ?? throw new ArgumentNullException("replacementProvider");
			this.enabledProvider = enabledProvider;
			this.pitch = pitch;
			this.volume = volume;
			this.minDistance = minDistance;
			this.maxDistance = maxDistance;
			this.rolloffMode = rolloffMode;
		}

		public string GetReplacement(string originalName)
		{
			if (enabledProvider != null && !enabledProvider())
			{
				return null;
			}
			try
			{
				string text = replacementProvider();
				if (string.IsNullOrEmpty(text))
				{
					return null;
				}
				return text;
			}
			catch
			{
				return null;
			}
		}
	}
	public class AdditionalSequentialSoundReplacement : CounterBasedSoundReplacement, ISoundReplacement
	{
		private readonly string[] sounds;

		private readonly bool muteOriginal;

		private readonly bool is2D;

		private readonly string mixerGroupName;

		private readonly float? volume;

		private readonly float maxDistanceMultiplier;

		private readonly float? pitch;

		private readonly float? minDistance;

		private readonly float? maxDistance;

		private readonly AudioRolloffMode? rolloffMode;

		private readonly bool playtoend;

		public bool ShouldMuteOriginal => muteOriginal;

		public bool IsAdditional => true;

		public bool Is2D => is2D;

		public string MixerGroupName => mixerGroupName;

		public float? Volume => volume;

		public float MaxDistanceMultiplier => maxDistanceMultiplier;

		public float? MinDistance => minDistance;

		public float? MaxDistance => maxDistance;

		public AudioRolloffMode? RolloffMode => rolloffMode;

		public float? Pitch => pitch;

		public bool PlayToEnd => playtoend;

		public AdditionalSequentialSoundReplacement(string[] sounds, Func<ConfigEntry<bool>> configGetter = null, bool muteOriginal = true, bool is2D = false, string mixerGroupName = null, float? volume = null, float maxDistanceMultiplier = 1f, float? pitch = null, float? minDistance = null, float? maxDistance = null, AudioRolloffMode? rolloffMode = null, bool playtoend = false)
			: base(configGetter)
		{
			this.sounds = sounds ?? throw new ArgumentNullException("sounds");
			this.muteOriginal = muteOriginal;
			this.is2D = is2D;
			this.mixerGroupName = mixerGroupName;
			this.volume = (volume.HasValue ? new float?(Math.Max(0f, Math.Min(1f, volume.Value))) : null);
			this.maxDistanceMultiplier = Math.Max(0.01f, maxDistanceMultiplier);
			this.pitch = pitch;
			this.minDistance = minDistance;
			this.maxDistance = maxDistance;
			this.rolloffMode = rolloffMode;
			this.playtoend = playtoend;
		}

		public override string GetReplacement(string originalName)
		{
			Func<ConfigEntry<bool>> func = configGetter;
			if (func != null && func()?.Value == false)
			{
				return null;
			}
			int num = (GetAndIncrementCounter(originalName) - 1) % sounds.Length;
			return sounds[num];
		}

		public string GetReplacementForAudioSource(string originalName, AudioSource audioSource)
		{
			return GetReplacement(originalName);
		}
	}
	public class TextureReplacer : MonoBehaviour
	{
		[CompilerGenerated]
		private sealed class <InitialScan>d__5 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public TextureReplacer <>4__this;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <InitialScan>d__5(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0024: Unknown result type (might be due to invalid IL or missing references)
				//IL_002e: Expected O, but got Unknown
				int num = <>1__state;
				TextureReplacer textureReplacer = <>4__this;
				switch (num)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = (object)new WaitForSeconds(1f);
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					textureReplacer.ScanAndReplaceTextures();
					return false;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

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

		private static readonly HashSet<int> processedMaterials = new HashSet<int>();

		private float scanInterval = 2f;

		private float lastScanTime;

		private void Start()
		{
			((MonoBehaviour)this).StartCoroutine(InitialScan());
		}

		[IteratorStateMachine(typeof(<InitialScan>d__5))]
		private IEnumerator InitialScan()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <InitialScan>d__5(0)
			{
				<>4__this = this
			};
		}

		private void Update()
		{
			if (Plugin.ConfigEnableTextureReplacement.Value && Time.time - lastScanTime >= scanInterval)
			{
				lastScanTime = Time.time;
				ScanAndReplaceTextures();
			}
		}

		private void ScanAndReplaceTextures()
		{
			if (!Plugin.ConfigEnableTextureReplacement.Value)
			{
				return;
			}
			Renderer[] array = Object.FindObjectsOfType<Renderer>();
			foreach (Renderer val in array)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				Material[] sharedMaterials = val.sharedMaterials;
				if (sharedMaterials == null)
				{
					continue;
				}
				Material[] array2 = sharedMaterials;
				foreach (Material val2 in array2)
				{
					if (!((Object)(object)val2 == (Object)null))
					{
						int instanceID = ((Object)val2).GetInstanceID();
						if (!processedMaterials.Contains(instanceID))
						{
							TryReplaceMaterialTextures(val2, ((Object)((Component)val).gameObject).name);
						}
					}
				}
			}
		}

		private void TryReplaceMaterialTextures(Material material, string objectName)
		{
			if ((Object)(object)material == (Object)null)
			{
				return;
			}
			int instanceID = ((Object)material).GetInstanceID();
			string[] obj = new string[6] { "_MainTex", "_EmissionMap", "_Emissive", "_BaseMap", "_BaseColorMap", "_AlbedoMap" };
			bool flag = false;
			string[] array = obj;
			foreach (string text in array)
			{
				if (!material.HasProperty(text))
				{
					continue;
				}
				Texture texture = material.GetTexture(text);
				if ((Object)(object)texture == (Object)null)
				{
					continue;
				}
				string text2 = ((Object)texture).name?.ToLowerInvariant() ?? "";
				if (textureNamesToReplace.Contains(text2))
				{
					Texture2D replacedTexture = Plugin.GetReplacedTexture(text2);
					if ((Object)(object)replacedTexture != (Object)null)
					{
						material.SetTexture(text, (Texture)(object)replacedTexture);
						flag = true;
					}
				}
			}
			if (flag || !HasRelevantTextures(material))
			{
				processedMaterials.Add(instanceID);
			}
		}

		private bool HasRelevantTextures(Material material)
		{
			return (((Object)material).name?.ToLowerInvariant() ?? "").Contains("valuable");
		}

		public static void ClearCache()
		{
			processedMaterials.Clear();
		}

		private void OnDestroy()
		{
			processedMaterials.Clear();
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "kossnikitaMatesMod";

		public const string PLUGIN_NAME = "kossnikita mates mod";

		public const string PLUGIN_VERSION = "1.0.3";
	}
}