Decompiled source of LethalVoice v1.1.0

com.github.zehsteam.LethalVoice.dll

Decompiled 3 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
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.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using LethalConfig;
using LethalConfig.ConfigItems;
using LethalConfig.ConfigItems.Options;
using Microsoft.CodeAnalysis;
using UnityEngine;
using VoiceRecognitionAPI;
using com.github.zehsteam.LethalVoice.Dependencies;
using com.github.zehsteam.LethalVoice.Helpers;
using com.github.zehsteam.MonsterHotkeys;
using com.github.zehsteam.MonsterHotkeys.Helpers;
using com.github.zehsteam.MonsterHotkeys.MonoBehaviours;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("com.github.zehsteam.LethalVoice")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyDescription("Spawn Monsters and Monster Plushies when you say specific words/phrases using the MonsterHotkeys mod. Highly Configurable.")]
[assembly: AssemblyFileVersion("1.1.0.0")]
[assembly: AssemblyInformationalVersion("1.1.0+90cc74c9e70bc5a090e414d236b01915a35937b0")]
[assembly: AssemblyProduct("LethalVoice")]
[assembly: AssemblyTitle("com.github.zehsteam.LethalVoice")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.0.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 com.github.zehsteam.LethalVoice
{
	internal class ConfigManager
	{
		public ConfigEntry<bool> General_DisableHotkeys;

		public ConfigEntry<bool> VoiceRecognition_Enabled;

		public ConfigEntry<float> VoiceRecognition_Confidence;

		public ConfigEntry<bool> Enemy_Enabled;

		public ConfigEntry<string> Enemy_WordsToSpawnRandomEnemies;

		public ConfigEntry<bool> Plushie_Enabled;

		public ConfigEntry<string> Plushie_WordsToSpawnRandomPlushies;

		public ConfigEntry<bool> Message_ShowRecognizedPlushiePhraseMessages;

		public string[] Enemy_WordsToSpawnRandomEnemiesArray
		{
			get
			{
				return Utils.StringToArray<string>(Enemy_WordsToSpawnRandomEnemies.Value);
			}
			set
			{
				Enemy_WordsToSpawnRandomEnemies.Value = Utils.ArrayToString(value);
			}
		}

		public string[] Plushie_WordsToSpawnRandomPlushiesArray
		{
			get
			{
				return Utils.StringToArray<string>(Plushie_WordsToSpawnRandomPlushies.Value);
			}
			set
			{
				Plushie_WordsToSpawnRandomPlushies.Value = Utils.ArrayToString(value);
			}
		}

		public ConfigManager()
		{
			BindConfigs();
			MigrateOldConfigSettings();
			ConfigHelper.ClearUnusedEntries();
		}

		private void BindConfigs()
		{
			ConfigHelper.SkipAutoGen();
			General_DisableHotkeys = ConfigHelper.Bind("General", "DisableHotkeys", defaultValue: true, "If enabled, the hotkeys from the MonsterHotkeys mod will be disabled.");
			General_DisableHotkeys.SettingChanged += Plugin.Instance.OnDisableHotkeysChanged;
			VoiceRecognition_Enabled = ConfigHelper.Bind("Voice Recognition", "Enabled", defaultValue: true, "If enabled, speech recognition will be active.");
			VoiceRecognition_Confidence = ConfigHelper.Bind("Voice Recognition", "Confidence", 0.7f, "How difficult it will be for the speech recognition to recognize your word/phrase.", requiresRestart: false, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f));
			Enemy_Enabled = ConfigHelper.Bind("Enemy", "Enabled", defaultValue: true, "If enabled, you will be able to spawn random monsters when specific words/phrases are said. (Requires VoiceRecognition to be enabled)");
			Enemy_WordsToSpawnRandomEnemies = ConfigHelper.Bind("Enemy", "WordsToSpawnRandomEnemies", "alright, fuck, garbage, god, hell, kill, mate, nuts, piece, shit, wait, what, why", "The list of words/phrases you want to trigger a random monster spawn. Each word/phrase should be separated by a comma.");
			Enemy_WordsToSpawnRandomEnemies.SettingChanged += Plugin.Instance.OnSpawnRandomMonsterWordsChanged;
			Plushie_Enabled = ConfigHelper.Bind("Plushie", "Enabled", defaultValue: true, "If enabled, you will be able to spawn random plushies when specific words/phrases are said. (Requires VoiceRecognition to be enabled)");
			Plushie_WordsToSpawnRandomPlushies = ConfigHelper.Bind("Plushie", "WordsToSpawnRandomPlushies", "ban, hate, like, messed, no, ok, rizz, stop, thank, this", "The list of words/phrases you want to trigger a random plushie spawn. Each word/phrase should be separated by a comma.");
			Plushie_WordsToSpawnRandomPlushies.SettingChanged += Plugin.Instance.OnSpawnRandomPlushieWordsChanged;
			Message_ShowRecognizedPlushiePhraseMessages = ConfigHelper.Bind("Message", "ShowRecognizedPlushiePhraseMessages", defaultValue: true, "If enabled, will show a message when a phrase is recognized for spawning plushies. See additional message setting in the MonsterHotkeys config file.");
		}

		private void MigrateOldConfigSettings()
		{
			foreach (KeyValuePair<ConfigDefinition, string> orphanedConfigEntry in ConfigHelper.GetOrphanedConfigEntries())
			{
				MigrateOldConfigSetting(orphanedConfigEntry.Key.Section, orphanedConfigEntry.Key.Key, orphanedConfigEntry.Value);
			}
		}

		private void MigrateOldConfigSetting(string section, string key, string value)
		{
			StringComparison comparisonType = StringComparison.OrdinalIgnoreCase;
			if (section.Equals("General Settings", comparisonType))
			{
				if (key.Equals("VoiceRecognitionEnabled", comparisonType))
				{
					ConfigHelper.SetConfigEntryValue<bool>(VoiceRecognition_Enabled, value);
					return;
				}
				if (key.Equals("VoiceConfidence", comparisonType))
				{
					ConfigHelper.SetConfigEntryValue<float>(VoiceRecognition_Confidence, value);
					return;
				}
				if (key.Equals("DisableHotkeys", comparisonType))
				{
					ConfigHelper.SetConfigEntryValue<bool>(General_DisableHotkeys, value);
					return;
				}
				if (key.Equals("ShowRecognizedPlushiePhraseMessages", comparisonType))
				{
					ConfigHelper.SetConfigEntryValue<bool>(Message_ShowRecognizedPlushiePhraseMessages, value);
					return;
				}
			}
			if (section.Equals("Monster Settings", comparisonType) && key.Equals("SpawnRandomMonsterWords", comparisonType))
			{
				ConfigHelper.SetConfigEntryValue<string>(Enemy_WordsToSpawnRandomEnemies, value);
			}
			else if (section.Equals("Plushie Settings", comparisonType) && key.Equals("SpawnRandomPlushieWords", comparisonType))
			{
				ConfigHelper.SetConfigEntryValue<string>(Plushie_WordsToSpawnRandomPlushies, value);
			}
		}
	}
	[BepInPlugin("com.github.zehsteam.LethalVoice", "LethalVoice", "1.1.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	internal class Plugin : BaseUnityPlugin
	{
		private string[] _activePhrases = Array.Empty<string>();

		internal static Plugin Instance { get; private set; }

		internal static ManualLogSource Logger { get; private set; }

		internal static ConfigManager ConfigManager { get; private set; }

		private void Awake()
		{
			if ((Object)(object)Instance == (Object)null)
			{
				Instance = this;
			}
			Logger = Logger.CreateLogSource("com.github.zehsteam.LethalVoice");
			Logger.LogInfo((object)"LethalVoice has awoken!");
			ConfigManager = new ConfigManager();
			RegisterVoiceListener();
			UpdateDisableHotkeys();
		}

		private void RegisterVoiceListener()
		{
			string[] enemy_WordsToSpawnRandomEnemiesArray = ConfigManager.Enemy_WordsToSpawnRandomEnemiesArray;
			string[] plushie_WordsToSpawnRandomPlushiesArray = ConfigManager.Plushie_WordsToSpawnRandomPlushiesArray;
			int num = 0;
			string[] array = new string[enemy_WordsToSpawnRandomEnemiesArray.Length + plushie_WordsToSpawnRandomPlushiesArray.Length];
			ReadOnlySpan<string> readOnlySpan = new ReadOnlySpan<string>(enemy_WordsToSpawnRandomEnemiesArray);
			readOnlySpan.CopyTo(new Span<string>(array).Slice(num, readOnlySpan.Length));
			num += readOnlySpan.Length;
			ReadOnlySpan<string> readOnlySpan2 = new ReadOnlySpan<string>(plushie_WordsToSpawnRandomPlushiesArray);
			readOnlySpan2.CopyTo(new Span<string>(array).Slice(num, readOnlySpan2.Length));
			num += readOnlySpan2.Length;
			_activePhrases = array;
			Voice.RegisterPhrases(_activePhrases);
			Voice.RegisterCustomHandler((EventHandler<VoiceRecognitionEventArgs>)HandleVoiceRecognitionPhrase);
		}

		private void HandleVoiceRecognitionPhrase(object obj, VoiceRecognitionEventArgs recognized)
		{
			if (ConfigManager.VoiceRecognition_Enabled.Value && !(recognized.Confidence < ConfigManager.VoiceRecognition_Confidence.Value))
			{
				string message = recognized.Message;
				if (ConfigManager.Enemy_Enabled.Value && Utils.ArrayContains(ConfigManager.Enemy_WordsToSpawnRandomEnemiesArray, message))
				{
					Logger.LogInfo((object)$"Recognized phrase \"{message}\" with a confidence of {recognized.Confidence}");
					SpawnRandomEnemy(message);
				}
				if (ConfigManager.Plushie_Enabled.Value && Utils.ArrayContains(ConfigManager.Plushie_WordsToSpawnRandomPlushiesArray, message))
				{
					Logger.LogInfo((object)$"Recognized phrase \"{message}\" with a confidence of {recognized.Confidence}");
					SpawnRandomPlushies(message);
				}
			}
		}

		private void SpawnRandomEnemy(string phrase)
		{
			try
			{
				EnemyHelper.SpawnRandomEnemy(phrase);
			}
			catch (Exception arg)
			{
				Logger.LogError((object)$"Failed to spawn random enemy. {arg}");
			}
		}

		private void SpawnRandomPlushies(string phrase)
		{
			try
			{
				PlushieManager instance = PlushieManager.Instance;
				if (instance != null)
				{
					instance.SpawnRandomPlushies();
				}
				ShowPlushiePhraseRecognizedMessage(phrase);
			}
			catch (Exception arg)
			{
				Logger.LogError((object)$"Failed to spawn random plushies. {arg}");
			}
		}

		public void OnSpawnRandomMonsterWordsChanged(object sender, EventArgs e)
		{
			string[] array = ConfigManager.Enemy_WordsToSpawnRandomEnemiesArray.Where((string x) => !Utils.ArrayContains(_activePhrases, x)).ToArray();
			if (array.Length != 0)
			{
				Voice.RegisterPhrases(array);
			}
		}

		public void OnSpawnRandomPlushieWordsChanged(object sender, EventArgs e)
		{
			string[] array = ConfigManager.Plushie_WordsToSpawnRandomPlushiesArray.Where((string x) => !Utils.ArrayContains(_activePhrases, x)).ToArray();
			if (array.Length != 0)
			{
				Voice.RegisterPhrases(array);
			}
		}

		private void ShowPlushiePhraseRecognizedMessage(string phrase)
		{
			if (!ConfigManager.Message_ShowRecognizedPlushiePhraseMessages.Value)
			{
				return;
			}
			try
			{
				if (!((Object)(object)PlushieManager.Instance == (Object)null) && PlushieManager.Instance.CanSpawnPlushies())
				{
					MessageCanvas instance = MessageCanvas.Instance;
					if (instance != null)
					{
						instance.ShowMessage_LocalClient("Recognized phrase \"" + phrase + "\"");
					}
				}
			}
			catch (Exception arg)
			{
				Logger.LogError((object)$"Failed to show plushie phrase recognized message on local client.\n\n{arg}");
			}
		}

		private void UpdateDisableHotkeys()
		{
			bool value = ConfigManager.General_DisableHotkeys.Value;
			try
			{
				HotkeyListener.DisableHotkeys = value;
				Logger.LogInfo((object)((value ? "Disabled" : "Enabled") + " MonsterHotkeys hotkeys."));
			}
			catch (Exception arg)
			{
				Logger.LogError((object)string.Format("Failed to {0} MonsterHotkeys hotkeys.\n\n{1}", value ? "disable" : "enable", arg));
			}
		}

		public void OnDisableHotkeysChanged(object sender, EventArgs e)
		{
			UpdateDisableHotkeys();
		}
	}
	internal static class Utils
	{
		public static T[] StringToArray<T>(string value)
		{
			if (string.IsNullOrEmpty(value))
			{
				return Array.Empty<T>();
			}
			try
			{
				return (from x in value.Split(',')
					select (T)Convert.ChangeType(x.Trim(), typeof(T))).ToArray();
			}
			catch (Exception arg)
			{
				Plugin.Logger.LogError((object)$"Failed to convert string to array of type {typeof(T)}. \"{value}\". {arg}");
			}
			return Array.Empty<T>();
		}

		public static string ArrayToString<T>(T[] value)
		{
			if (value == null || value.Length == 0)
			{
				return string.Empty;
			}
			return string.Join(", ", value.Select((T x) => x.ToString()));
		}

		public static bool ArrayContains(string[] array, string value, bool matchCase = false)
		{
			if (value == null || value.Length == 0)
			{
				return false;
			}
			StringComparison comparisonType = ((!matchCase) ? StringComparison.OrdinalIgnoreCase : StringComparison.CurrentCulture);
			return array.Any((string x) => x.Equals(value, comparisonType));
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "com.github.zehsteam.LethalVoice";

		public const string PLUGIN_NAME = "LethalVoice";

		public const string PLUGIN_VERSION = "1.1.0";
	}
}
namespace com.github.zehsteam.LethalVoice.Helpers
{
	internal static class ConfigHelper
	{
		public static void SkipAutoGen()
		{
			if (LethalConfigProxy.Enabled)
			{
				LethalConfigProxy.SkipAutoGen();
			}
		}

		public static void AddButton(string section, string name, string buttonText, string description, Action callback)
		{
			if (LethalConfigProxy.Enabled)
			{
				LethalConfigProxy.AddButton(section, name, buttonText, description, callback);
			}
		}

		public static ConfigEntry<T> Bind<T>(string section, string key, T defaultValue, string description, bool requiresRestart = false, AcceptableValueBase acceptableValues = null, Action<T> settingChanged = null, ConfigFile configFile = null)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Expected O, but got Unknown
			if (configFile == null)
			{
				configFile = ((BaseUnityPlugin)Plugin.Instance).Config;
			}
			ConfigEntry<T> configEntry = ((acceptableValues == null) ? configFile.Bind<T>(section, key, defaultValue, description) : configFile.Bind<T>(section, key, defaultValue, new ConfigDescription(description, acceptableValues, Array.Empty<object>())));
			if (settingChanged != null)
			{
				configEntry.SettingChanged += delegate
				{
					settingChanged?.Invoke(configEntry.Value);
				};
			}
			if (LethalConfigProxy.Enabled)
			{
				LethalConfigProxy.AddConfig<T>(configEntry, requiresRestart);
			}
			return configEntry;
		}

		public static Dictionary<ConfigDefinition, string> GetOrphanedConfigEntries(ConfigFile configFile = null)
		{
			if (configFile == null)
			{
				configFile = ((BaseUnityPlugin)Plugin.Instance).Config;
			}
			PropertyInfo property = ((object)configFile).GetType().GetProperty("OrphanedEntries", BindingFlags.Instance | BindingFlags.NonPublic);
			return (Dictionary<ConfigDefinition, string>)property.GetValue(configFile, null);
		}

		public static void SetConfigEntryValue<T>(ConfigEntry<T> configEntry, string value)
		{
			if (typeof(T) == typeof(int) && int.TryParse(value, out var result))
			{
				configEntry.Value = (T)(object)result;
				return;
			}
			if (typeof(T) == typeof(float) && float.TryParse(value, out var result2))
			{
				configEntry.Value = (T)(object)result2;
				return;
			}
			if (typeof(T) == typeof(double) && double.TryParse(value, out var result3))
			{
				configEntry.Value = (T)(object)result3;
				return;
			}
			if (typeof(T) == typeof(bool) && bool.TryParse(value, out var result4))
			{
				configEntry.Value = (T)(object)result4;
				return;
			}
			if (typeof(T) == typeof(string))
			{
				configEntry.Value = (T)(object)value;
				return;
			}
			throw new InvalidOperationException($"Unsupported type: {typeof(T)}");
		}

		public static void ClearUnusedEntries(ConfigFile configFile = null)
		{
			if (configFile == null)
			{
				configFile = ((BaseUnityPlugin)Plugin.Instance).Config;
			}
			Dictionary<ConfigDefinition, string> orphanedConfigEntries = GetOrphanedConfigEntries(configFile);
			if (orphanedConfigEntries != null)
			{
				orphanedConfigEntries.Clear();
				configFile.Save();
			}
		}
	}
}
namespace com.github.zehsteam.LethalVoice.Dependencies
{
	internal static class LethalConfigProxy
	{
		public const string PLUGIN_GUID = "ainavt.lc.lethalconfig";

		private static bool? _enabled;

		public static bool Enabled
		{
			get
			{
				bool valueOrDefault = _enabled.GetValueOrDefault();
				if (!_enabled.HasValue)
				{
					valueOrDefault = Chainloader.PluginInfos.ContainsKey("ainavt.lc.lethalconfig");
					_enabled = valueOrDefault;
				}
				return _enabled.Value;
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
		public static void SkipAutoGen()
		{
			LethalConfigManager.SkipAutoGen();
		}

		[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
		public static void AddConfig<T>(ConfigEntry<T> configEntry, bool requiresRestart = false)
		{
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Expected O, but got Unknown
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Expected O, but got Unknown
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Expected O, but got Unknown
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Expected O, but got Unknown
			AcceptableValueBase acceptableValues = ((ConfigEntryBase)configEntry).Description.AcceptableValues;
			if (acceptableValues != null)
			{
				if (acceptableValues is AcceptableValueRange<float> || acceptableValues is AcceptableValueRange<int>)
				{
					AddConfigSlider<T>(configEntry, requiresRestart);
					return;
				}
				if (acceptableValues is AcceptableValueList<string>)
				{
					AddConfigDropdown<T>(configEntry, requiresRestart);
					return;
				}
			}
			if (!(configEntry is ConfigEntry<string> val))
			{
				if (!(configEntry is ConfigEntry<bool> val2))
				{
					if (!(configEntry is ConfigEntry<float> val3))
					{
						if (!(configEntry is ConfigEntry<int> val4))
						{
							throw new NotSupportedException($"Unsupported type: {typeof(T)}");
						}
						LethalConfigManager.AddConfigItem((BaseConfigItem)new IntInputFieldConfigItem(val4, requiresRestart));
					}
					else
					{
						LethalConfigManager.AddConfigItem((BaseConfigItem)new FloatInputFieldConfigItem(val3, requiresRestart));
					}
				}
				else
				{
					LethalConfigManager.AddConfigItem((BaseConfigItem)new BoolCheckBoxConfigItem(val2, requiresRestart));
				}
			}
			else
			{
				LethalConfigManager.AddConfigItem((BaseConfigItem)new TextInputFieldConfigItem(val, requiresRestart));
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
		public static void AddConfigSlider<T>(ConfigEntry<T> configEntry, bool requiresRestart = false)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			if (!(configEntry is ConfigEntry<float> val))
			{
				if (!(configEntry is ConfigEntry<int> val2))
				{
					throw new NotSupportedException($"Slider not supported for type: {typeof(T)}");
				}
				LethalConfigManager.AddConfigItem((BaseConfigItem)new IntSliderConfigItem(val2, requiresRestart));
			}
			else
			{
				LethalConfigManager.AddConfigItem((BaseConfigItem)new FloatSliderConfigItem(val, requiresRestart));
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
		public static void AddConfigDropdown<T>(ConfigEntry<T> configEntry, bool requiresRestart = false)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			if (configEntry is ConfigEntry<string> val)
			{
				LethalConfigManager.AddConfigItem((BaseConfigItem)new TextDropDownConfigItem(val, requiresRestart));
				return;
			}
			throw new NotSupportedException($"Dropdown not supported for type: {typeof(T)}");
		}

		[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
		public static void AddButton(string section, string name, string buttonText, string description, Action callback)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected O, but got Unknown
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			LethalConfigManager.AddConfigItem((BaseConfigItem)new GenericButtonConfigItem(section, name, description, buttonText, (GenericButtonHandler)delegate
			{
				callback?.Invoke();
			}));
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}