Decompiled source of ClockDaddyPlus v0.1.1

plugins/ClockDaddyPlus/ClockDaddyPlus.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[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("Laoch")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright (c) Laoch")]
[assembly: AssemblyFileVersion("0.1.1.0")]
[assembly: AssemblyInformationalVersion("0.1.1+4722fa699af23d00d746ac781356a0dc72fd86cd")]
[assembly: AssemblyProduct("ClockDaddyPlus")]
[assembly: AssemblyTitle("ClockDaddyPlus")]
[assembly: InternalsVisibleTo("ClockDaddyPlus.Harness")]
[assembly: AssemblyVersion("0.1.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ClockDaddyPlus
{
	public enum PingSound
	{
		None,
		Chime,
		Bell,
		Beeps,
		Ding,
		Rising,
		Westminster,
		Marimba,
		Gong,
		Pulse,
		MusicBox
	}
	internal static class Chime
	{
		private const int Rate = 44100;

		private static readonly Dictionary<PingSound, AudioClip> Clips = new Dictionary<PingSound, AudioClip>();

		public static AudioClip Get(PingSound sound)
		{
			if (Clips.TryGetValue(sound, out var value))
			{
				return value;
			}
			float[] array = Samples(sound);
			value = ((array == null) ? null : Build("ClockDaddy_" + sound, array));
			Clips[sound] = value;
			return value;
		}

		private static float[] Samples(PingSound sound)
		{
			return sound switch
			{
				PingSound.Chime => ChimeSamples(), 
				PingSound.Bell => BellSamples(), 
				PingSound.Beeps => BeepSamples(), 
				PingSound.Ding => DingSamples(), 
				PingSound.Rising => RisingSamples(), 
				PingSound.Westminster => WestminsterSamples(), 
				PingSound.Marimba => MarimbaSamples(), 
				PingSound.Gong => GongSamples(), 
				PingSound.Pulse => PulseSamples(), 
				PingSound.MusicBox => MusicBoxSamples(), 
				_ => null, 
			};
		}

		private static AudioClip Build(string name, float[] samples)
		{
			AudioClip obj = AudioClip.Create(name, samples.Length, 1, 44100, false);
			obj.SetData(samples, 0);
			return obj;
		}

		private static float[] ChimeSamples()
		{
			float[] buf = new float[70560];
			AddBell(buf, 0f, 1318.5f, 0.9f, 3.5f);
			AddBell(buf, 0.45f, 1046.5f, 1.1f, 3f);
			return Normalize(buf, 0.8f);
		}

		private static float[] BellSamples()
		{
			float[] buf = new float[105840];
			AddBell(buf, 0f, 523.25f, 2.3f, 1.8f);
			return Normalize(buf, 0.8f);
		}

		private static float[] BeepSamples()
		{
			float[] array = new float[39690];
			for (int i = 0; i < 3; i++)
			{
				int num = (int)((float)(44100 * i) * 0.28f);
				int num2 = 6174;
				for (int j = 0; j < num2 && num + j < array.Length; j++)
				{
					float num3 = (float)j / 44100f;
					float num4 = Mathf.Min(1f, (float)Mathf.Min(j, num2 - j) / 220.5f);
					float num5 = Mathf.Sin(MathF.PI * 3520f * num3) + 0.3f * Mathf.Sin(MathF.PI * 7040f * num3);
					array[num + j] += num5 * num4;
				}
			}
			return Normalize(array, 0.6f);
		}

		private static float[] DingSamples()
		{
			float[] buf = new float[44100];
			AddBell(buf, 0f, 1760f, 1f, 4.5f);
			return Normalize(buf, 0.7f);
		}

		private static float[] RisingSamples()
		{
			float[] buf = new float[66150];
			float[] array = new float[4] { 1046.5f, 1318.51f, 1567.98f, 2093f };
			for (int i = 0; i < array.Length; i++)
			{
				AddBell(buf, (float)i * 0.14f, array[i], 1f, 4f);
			}
			return Normalize(buf, 0.75f);
		}

		private static float[] WestminsterSamples()
		{
			float[] buf = new float[132300];
			float[] array = new float[4] { 659.25f, 523.25f, 587.33f, 392f };
			for (int i = 0; i < array.Length; i++)
			{
				AddBell(buf, (float)i * 0.55f, array[i], (i == array.Length - 1) ? 1.35f : 1.2f, 2.2f);
			}
			return Normalize(buf, 0.8f);
		}

		private static float[] MarimbaSamples()
		{
			float[] buf = new float[39690];
			AddMallet(buf, 0f, 783.99f);
			AddMallet(buf, 0.16f, 1046.5f);
			AddMallet(buf, 0.32f, 1046.5f);
			return Normalize(buf, 0.75f);
		}

		private static float[] GongSamples()
		{
			float[] array = new float[154350];
			float[] array2 = new float[6] { 1f, 1.52f, 1.53f, 2.31f, 3.17f, 4.4f };
			float[] array3 = new float[6] { 1f, 0.6f, 0.5f, 0.4f, 0.25f, 0.12f };
			for (int i = 0; i < array.Length; i++)
			{
				float num = (float)i / 44100f;
				float num2 = Mathf.Min(1f, num / 0.06f) * Mathf.Exp((0f - num) * 1.1f);
				float num3 = 0f;
				for (int j = 0; j < array2.Length; j++)
				{
					num3 += array3[j] * Mathf.Sin(MathF.PI * 220f * array2[j] * num) * Mathf.Exp((0f - num) * 0.4f * (float)j);
				}
				array[i] = num3 * num2;
			}
			return Normalize(array, 0.85f);
		}

		private static float[] PulseSamples()
		{
			float[] buf = new float[52920];
			for (int i = 0; i < 2; i++)
			{
				for (int j = 0; j < 4; j++)
				{
					AddBeep(buf, (float)i * 0.6f + (float)j * 0.09f, 0.05f, 2637f);
				}
			}
			return Normalize(buf, 0.55f);
		}

		private static float[] MusicBoxSamples()
		{
			float[] buf = new float[97020];
			float[] array = new float[6] { 1567.98f, 1318.51f, 1046.5f, 1174.66f, 1567.98f, 2093f };
			float[] array2 = new float[6] { 0f, 0.18f, 0.36f, 0.54f, 0.78f, 1.02f };
			for (int i = 0; i < array.Length; i++)
			{
				AddTine(buf, array2[i], array[i], (i == array.Length - 1) ? 1.2f : 0.7f);
			}
			return Normalize(buf, 0.7f);
		}

		private static void AddBeep(float[] buf, float startSec, float lengthSec, float freq)
		{
			int num = (int)(startSec * 44100f);
			int num2 = (int)(lengthSec * 44100f);
			for (int i = 0; i < num2 && num + i < buf.Length; i++)
			{
				float num3 = (float)i / 44100f;
				float num4 = Mathf.Min(1f, (float)Mathf.Min(i, num2 - i) / 176.40001f);
				buf[num + i] += Mathf.Sin(MathF.PI * 2f * freq * num3) * num4;
			}
		}

		private static void AddMallet(float[] buf, float startSec, float freq)
		{
			int num = (int)(startSec * 44100f);
			int num2 = 22050;
			for (int i = 0; i < num2 && num + i < buf.Length; i++)
			{
				float num3 = (float)i / 44100f;
				float num4 = Mathf.Min(1f, num3 / 0.002f);
				float num5 = Mathf.Sin(MathF.PI * 2f * freq * num3) * Mathf.Exp((0f - num3) * 9f) + 0.35f * Mathf.Sin(MathF.PI * 2f * freq * 4f * num3) * Mathf.Exp((0f - num3) * 40f) + 0.1f * Mathf.Sin(MathF.PI * 2f * freq * 10f * num3) * Mathf.Exp((0f - num3) * 80f);
				buf[num + i] += num5 * num4;
			}
		}

		private static void AddTine(float[] buf, float startSec, float freq, float lengthSec)
		{
			int num = (int)(startSec * 44100f);
			int num2 = (int)(lengthSec * 44100f);
			for (int i = 0; i < num2 && num + i < buf.Length; i++)
			{
				float num3 = (float)i / 44100f;
				float num4 = Mathf.Min(1f, num3 / 0.002f);
				float num5 = Mathf.Sin(MathF.PI * 2f * freq * num3) * Mathf.Exp((0f - num3) * 4f) + 0.3f * Mathf.Sin(MathF.PI * 2f * freq * 3.01f * num3) * Mathf.Exp((0f - num3) * 12f) + 0.12f * Mathf.Sin(MathF.PI * 2f * freq * 5.4f * num3) * Mathf.Exp((0f - num3) * 25f);
				buf[num + i] += num5 * num4;
			}
		}

		private static void AddBell(float[] buf, float startSec, float freq, float lengthSec, float decay)
		{
			float[] array = new float[4] { 1f, 2f, 2.76f, 5.4f };
			float[] array2 = new float[4] { 1f, 0.5f, 0.25f, 0.1f };
			int num = (int)(startSec * 44100f);
			int num2 = (int)(lengthSec * 44100f);
			for (int i = 0; i < num2 && num + i < buf.Length; i++)
			{
				float num3 = (float)i / 44100f;
				float num4 = Mathf.Min(1f, num3 / 0.004f);
				float num5 = 0f;
				for (int j = 0; j < array.Length; j++)
				{
					num5 += array2[j] * Mathf.Sin(MathF.PI * 2f * freq * array[j] * num3) * Mathf.Exp((0f - num3) * decay * (1f + (float)j));
				}
				buf[num + i] += num5 * num4;
			}
		}

		private static float[] Normalize(float[] buf, float peak)
		{
			float num = 0f;
			foreach (float num2 in buf)
			{
				num = Mathf.Max(num, Mathf.Abs(num2));
			}
			if (num > 0f)
			{
				for (int j = 0; j < buf.Length; j++)
				{
					buf[j] *= peak / num;
				}
			}
			return buf;
		}
	}
	[BepInPlugin("com.laoch.clockdaddyplus", "ClockDaddyPlus", "0.1.1")]
	public class ClockDaddyPlusPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.laoch.clockdaddyplus";

		public const string PluginName = "ClockDaddyPlus";

		public const string PluginVersion = "0.1.1";

		private GUIStyle _style;

		private Font _font;

		private float _nextFontSearch;

		private readonly GUIContent _content = new GUIContent();

		private readonly StringBuilder _sb = new StringBuilder();

		internal static ClockDaddyPlusPlugin Instance { get; private set; }

		internal static ManualLogSource Log { get; private set; }

		internal Timers Timers { get; private set; }

		private void Awake()
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			Settings.Bind(((BaseUnityPlugin)this).Config);
			new Harmony("com.laoch.clockdaddyplus").PatchAll(typeof(ClockDaddyPlusPlugin).Assembly);
			AudioSource val = ((Component)this).gameObject.AddComponent<AudioSource>();
			val.playOnAwake = false;
			val.spatialBlend = 0f;
			val.ignoreListenerPause = true;
			Timers = new Timers(val, Path.Combine(Paths.ConfigPath, "com.laoch.clockdaddyplus.timers.txt"));
			((BaseUnityPlugin)this).Logger.LogInfo((object)"ClockDaddyPlus 0.1.1 loaded. Set timers up in F1; Shift + / - to pick, Shift+T to start.");
		}

		private void Update()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: 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_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			if (!IsTyping())
			{
				KeyboardShortcut value = Settings.NextKey.Value;
				if (((KeyboardShortcut)(ref value)).IsDown())
				{
					Timers.Cycle(1);
				}
				value = Settings.PrevKey.Value;
				if (((KeyboardShortcut)(ref value)).IsDown())
				{
					Timers.Cycle(-1);
				}
				value = Settings.StartStopKey.Value;
				if (((KeyboardShortcut)(ref value)).IsDown())
				{
					Timers.StartOrStopSelected();
				}
				value = Settings.StopAllKey.Value;
				if (((KeyboardShortcut)(ref value)).IsDown())
				{
					Timers.StopAll();
				}
			}
			Timers.Update();
		}

		private static bool IsTyping()
		{
			if ((!((Object)(object)Chat.instance != (Object)null) || !Chat.instance.HasFocus()) && !Console.IsVisible() && !TextInput.IsVisible())
			{
				return InputGuard.TypingInConfigManager;
			}
			return true;
		}

		private static bool HudAllowed()
		{
			if ((Object)(object)Player.m_localPlayer == (Object)null)
			{
				return false;
			}
			if (Hud.IsUserHidden())
			{
				return false;
			}
			if (Settings.HideInMenus.Value && (Minimap.IsOpen() || InventoryGui.IsVisible() || Menu.IsVisible() || StoreGui.IsVisible()))
			{
				return false;
			}
			return true;
		}

		private void OnGUI()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Invalid comparison between Unknown and I4
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			if ((int)Event.current.type != 7 || !HudAllowed())
			{
				return;
			}
			GUIStyle style = GetStyle();
			style.fontSize = Settings.FontSize.Value;
			float x = (float)Screen.width * Settings.PositionX.Value / 100f;
			float y = (float)Screen.height * Settings.PositionY.Value / 100f;
			float lineHeight = style.lineHeight + 4f;
			if (Time.realtimeSinceStartup < Timers.PickerUntil)
			{
				DrawLine(ref y, x, lineHeight, PickerText(), Settings.SelectColor.Value);
			}
			if (!Settings.ShowTimers.Value)
			{
				return;
			}
			bool flag = Mathf.Repeat(Time.unscaledTime * 1.5f, 1f) < 0.5f;
			foreach (RunningTimer item in Timers.Running)
			{
				if (Timers.IsLive(item))
				{
					if (item.IsDone)
					{
						DrawLine(ref y, x, lineHeight, item.Name + "  DONE", flag ? Settings.DoneColor.Value : Settings.TextColor.Value);
					}
					else
					{
						DrawLine(ref y, x, lineHeight, item.Name + "  " + Durations.Format(item.Remaining()), Settings.TextColor.Value);
					}
				}
			}
		}

		private string PickerText()
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			int selected = Timers.Selected;
			if (selected < 0)
			{
				return "No timers enabled (F1)";
			}
			SlotSettings slotSettings = Settings.Slots[selected];
			string value = KeyText(Settings.StartStopKey.Value);
			_sb.Length = 0;
			_sb.Append("<  ").Append(slotSettings.Label).Append("  ");
			if (Timers.TryGet(selected, out var timer))
			{
				_sb.Append(timer.IsDone ? "done" : (Durations.Format(timer.Remaining()) + " left")).Append("  >   ").Append(value)
					.Append(timer.IsDone ? " clear" : " stop");
			}
			else
			{
				_sb.Append(Durations.Format(slotSettings.Length)).Append((slotSettings.Clock.Value == TimerClock.World) ? " (world)" : "").Append("  >   ")
					.Append(value)
					.Append(" start");
			}
			return _sb.ToString();
		}

		internal static string KeyText(KeyboardShortcut k)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			if ((int)((KeyboardShortcut)(ref k)).MainKey == 0)
			{
				return "(no key)";
			}
			StringBuilder stringBuilder = new StringBuilder();
			foreach (KeyCode modifier in ((KeyboardShortcut)(ref k)).Modifiers)
			{
				stringBuilder.Append(KeyName(modifier)).Append('+');
			}
			return stringBuilder.Append(KeyName(((KeyboardShortcut)(ref k)).MainKey)).ToString();
		}

		private unsafe static string KeyName(KeyCode c)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Invalid comparison between Unknown and I4
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Invalid comparison between Unknown and I4
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Invalid comparison between Unknown and I4
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Invalid comparison between Unknown and I4
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Expected I4, but got Unknown
			if ((int)c <= 61)
			{
				if ((int)c == 45)
				{
					return "-";
				}
				if ((int)c == 61)
				{
					return "=";
				}
			}
			else
			{
				if ((int)c == 269)
				{
					return "Num-";
				}
				if ((int)c == 270)
				{
					return "Num+";
				}
				switch (c - 303)
				{
				case 0:
				case 1:
					return "Shift";
				case 2:
				case 3:
					return "Ctrl";
				case 4:
				case 5:
					return "Alt";
				}
			}
			string text = ((object)(*(KeyCode*)(&c))/*cast due to .constrained prefix*/).ToString();
			if (!text.StartsWith("Alpha"))
			{
				return text;
			}
			return text.Substring(5);
		}

		private void DrawLine(ref float y, float x, float lineHeight, string text, Color color)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: 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_00c2: Unknown result type (might be due to invalid IL or missing references)
			GUIStyle style = _style;
			_content.text = text;
			Vector2 val = style.CalcSize(_content);
			Rect val2 = default(Rect);
			((Rect)(ref val2))..ctor(Mathf.Clamp(x, 0f, Mathf.Max(0f, (float)Screen.width - val.x)), y, val.x, val.y);
			style.normal.textColor = new Color(0f, 0f, 0f, 0.8f);
			GUI.Label(new Rect(((Rect)(ref val2)).x + 1f, ((Rect)(ref val2)).y + 1f, ((Rect)(ref val2)).width, ((Rect)(ref val2)).height), _content, style);
			style.normal.textColor = color;
			GUI.Label(val2, _content, style);
			y += lineHeight;
		}

		private GUIStyle GetStyle()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Expected O, but got Unknown
			if (_style == null)
			{
				_style = new GUIStyle(GUI.skin.label)
				{
					richText = false,
					wordWrap = false,
					clipping = (TextClipping)0
				};
			}
			if ((Object)(object)_font == (Object)null && Time.unscaledTime >= _nextFontSearch)
			{
				_nextFontSearch = Time.unscaledTime + 5f;
				Font[] array = Resources.FindObjectsOfTypeAll<Font>();
				foreach (Font val in array)
				{
					if (((Object)val).name == "AveriaSerifLibre-Bold")
					{
						_font = val;
						break;
					}
				}
				if ((Object)(object)_font != (Object)null)
				{
					_style.font = _font;
				}
			}
			_style.fontStyle = (FontStyle)(Settings.BoldText.Value ? 1 : 0);
			return _style;
		}
	}
	internal sealed class ConfigurationManagerAttributes
	{
		public int? Order;

		public Action<ConfigEntryBase> CustomDrawer;

		public bool? HideDefaultButton;

		public bool? HideSettingName;

		public bool? IsAdvanced;
	}
	internal static class Durations
	{
		public static TimeSpan Parse(string text, TimeSpan fallback)
		{
			if (string.IsNullOrWhiteSpace(text))
			{
				return fallback;
			}
			string[] array = text.Trim().Split(':');
			try
			{
				TimeSpan timeSpan;
				switch (array.Length)
				{
				case 1:
					timeSpan = TimeSpan.FromMinutes(double.Parse(array[0], NumberStyles.Float, CultureInfo.InvariantCulture));
					break;
				case 2:
					timeSpan = new TimeSpan(0, int.Parse(array[0], CultureInfo.InvariantCulture), int.Parse(array[1], CultureInfo.InvariantCulture));
					break;
				case 3:
					timeSpan = new TimeSpan(int.Parse(array[0], CultureInfo.InvariantCulture), int.Parse(array[1], CultureInfo.InvariantCulture), int.Parse(array[2], CultureInfo.InvariantCulture));
					break;
				default:
					return fallback;
				}
				return (timeSpan > TimeSpan.Zero) ? timeSpan : fallback;
			}
			catch (FormatException)
			{
				return fallback;
			}
			catch (OverflowException)
			{
				return fallback;
			}
		}

		public static string Format(TimeSpan t)
		{
			if (t < TimeSpan.Zero)
			{
				t = TimeSpan.Zero;
			}
			int num = (int)Math.Ceiling(t.TotalSeconds);
			int num2 = num / 3600;
			int num3 = num / 60 % 60;
			int num4 = num % 60;
			if (num2 <= 0)
			{
				return $"{num3}:{num4:00}";
			}
			return $"{num2}:{num3:00}:{num4:00}";
		}
	}
	internal static class InputGuard
	{
		[HarmonyPatch(typeof(TextInput), "IsVisible")]
		private static class TextInputIsVisible
		{
			private static void Postfix(ref bool __result)
			{
				if (!__result && TypingInConfigManager)
				{
					__result = true;
				}
			}
		}

		private const string ConfigManagerGuid = "com.bepis.bepinex.configurationmanager";

		private static object _cm;

		private static PropertyInfo _displaying;

		private static bool _looked;

		public static bool TypingInConfigManager
		{
			get
			{
				if (GUIUtility.keyboardControl == 0)
				{
					return false;
				}
				if (!_looked)
				{
					_looked = true;
					if (Chainloader.PluginInfos.TryGetValue("com.bepis.bepinex.configurationmanager", out var value) && (Object)(object)value.Instance != (Object)null)
					{
						_cm = value.Instance;
						_displaying = _cm.GetType().GetProperty("DisplayingWindow", BindingFlags.Instance | BindingFlags.Public);
					}
				}
				if (_cm == null || _displaying == null)
				{
					return false;
				}
				try
				{
					return (bool)_displaying.GetValue(_cm, null);
				}
				catch
				{
					return false;
				}
			}
		}
	}
	public enum TimerClock
	{
		Real,
		World
	}
	public enum AlertMessage
	{
		None,
		Center,
		TopLeft
	}
	internal sealed class SlotSettings
	{
		public int Index;

		public ConfigEntry<bool> Enabled;

		public ConfigEntry<string> Name;

		public ConfigEntry<string> Duration;

		public ConfigEntry<TimerClock> Clock;

		public TimeSpan Length => Durations.Parse(Duration.Value, TimeSpan.FromMinutes(5.0));

		public string Label
		{
			get
			{
				if (!string.IsNullOrWhiteSpace(Name.Value))
				{
					return Name.Value.Trim();
				}
				return $"Timer {Index + 1}";
			}
		}
	}
	internal static class Settings
	{
		public const int SlotCount = 6;

		private const string SKeys = "1 - Hotkeys";

		private const string SDisplay = "2 - Display";

		private const string SAlert = "3 - Alerts";

		public static ConfigEntry<KeyboardShortcut> NextKey;

		public static ConfigEntry<KeyboardShortcut> PrevKey;

		public static ConfigEntry<KeyboardShortcut> StartStopKey;

		public static ConfigEntry<KeyboardShortcut> StopAllKey;

		public static ConfigEntry<bool> ShowTimers;

		public static ConfigEntry<float> PositionX;

		public static ConfigEntry<float> PositionY;

		public static ConfigEntry<int> FontSize;

		public static ConfigEntry<bool> BoldText;

		public static ConfigEntry<Color> TextColor;

		public static ConfigEntry<Color> SelectColor;

		public static ConfigEntry<Color> DoneColor;

		public static ConfigEntry<float> SelectorSeconds;

		public static ConfigEntry<bool> HideInMenus;

		public static ConfigEntry<PingSound> Sound;

		public static ConfigEntry<float> Volume;

		public static ConfigEntry<AlertMessage> MessageStyle;

		public static ConfigEntry<string> DoneMessage;

		public static ConfigEntry<int> KeepDoneSeconds;

		public static ConfigEntry<int> RepeatSeconds;

		public static readonly SlotSettings[] Slots = new SlotSettings[6];

		private static ConfigFile _cfg;

		private static int _order;

		private static readonly (string name, string duration, TimerClock clock, bool on)[] Defaults = new(string, string, TimerClock, bool)[6]
		{
			("Puff farm", "1:00:00", TimerClock.World, true),
			("Mistwalker", "20:00", TimerClock.Real, true),
			("Food", "25:00", TimerClock.Real, true),
			("Fermenter", "40:00", TimerClock.World, true),
			("Timer 5", "5:00", TimerClock.Real, false),
			("Timer 6", "15:00", TimerClock.Real, false)
		};

		public static void Bind(ConfigFile cfg)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0206: Unknown result type (might be due to invalid IL or missing references)
			_cfg = cfg;
			_order = 1000;
			NextKey = B<KeyboardShortcut>("1 - Hotkeys", "Next Timer", new KeyboardShortcut((KeyCode)61, (KeyCode[])(object)new KeyCode[1] { (KeyCode)304 }), "Select the next timer (Shift and the + / = key by default). The pick shows on screen for a moment.");
			PrevKey = B<KeyboardShortcut>("1 - Hotkeys", "Previous Timer", new KeyboardShortcut((KeyCode)45, (KeyCode[])(object)new KeyCode[1] { (KeyCode)304 }), "Select the previous timer.");
			StartStopKey = B<KeyboardShortcut>("1 - Hotkeys", "Start / Stop Timer", new KeyboardShortcut((KeyCode)116, (KeyCode[])(object)new KeyCode[1] { (KeyCode)304 }), "Start the selected timer, or stop it if it's already running.");
			StopAllKey = B<KeyboardShortcut>("1 - Hotkeys", "Stop All Timers", new KeyboardShortcut((KeyCode)0, Array.Empty<KeyCode>()), "Stop every running timer. Unset by default.");
			_order = 1000;
			ShowTimers = B("2 - Display", "Show Timers", value: true, "List running timers on screen with their countdown.");
			PositionX = B("2 - Display", "Position X (%)", 1f, "Left edge of the timer list, 0 = left of screen, 100 = right.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f));
			PositionY = B("2 - Display", "Position Y (%)", 22f, "Top of the timer list, 0 = top of screen, 100 = bottom.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f));
			FontSize = B("2 - Display", "Font Size", 20, "Text size.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 48));
			BoldText = B("2 - Display", "Bold Text", value: true, "Draw the timers in bold (matches ClockDaddy).");
			TextColor = B<Color>("2 - Display", "Text Color", Color.white, "Running timers.");
			SelectColor = B<Color>("2 - Display", "Selected Color", new Color(1f, 0.84f, 0f, 1f), "The timer picker line.");
			DoneColor = B<Color>("2 - Display", "Done Color", new Color(1f, 0.35f, 0.2f, 1f), "Finished timers (they flash).");
			SelectorSeconds = B("2 - Display", "Picker Shows For (seconds)", 4f, "How long the picker stays up after Next / Previous.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 15f));
			HideInMenus = B("2 - Display", "Hide In Menus", value: false, "Hide the list in the inventory, Esc menu and big map.");
			_order = 1000;
			Sound = B("3 - Alerts", "Sound", PingSound.Chime, "Sound when a timer finishes. Use Test Sound to hear it.");
			Volume = B("3 - Alerts", "Volume", 0.7f, "Alert volume.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f));
			B("3 - Alerts", "Test Sound", value: false, "Play the chosen sound at the chosen volume.", null, delegate
			{
				DrawTestSoundButton();
			}, hideName: true);
			MessageStyle = B("3 - Alerts", "Message Style", AlertMessage.Center, "Where the \"done\" message pops up.");
			DoneMessage = B("3 - Alerts", "Done Message", "{name} is done!", "Text when a timer finishes. {name} is the timer's name.");
			KeepDoneSeconds = B("3 - Alerts", "Keep Finished For (seconds)", 60, "How long a finished timer stays in the list, flashing. Start/Stop on it clears it sooner.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 600));
			RepeatSeconds = B("3 - Alerts", "Repeat Alert Every (seconds)", 0, "Ping again this often while a finished timer is still listed. 0 = once.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 120));
			for (int num = 0; num < 6; num++)
			{
				(string, string, TimerClock, bool) tuple = Defaults[num];
				string section = $"{4 + num} - Timer {num + 1}";
				_order = 1000;
				SlotSettings slotSettings = new SlotSettings
				{
					Index = num
				};
				slotSettings.Enabled = B(section, "Enabled", tuple.Item4, "Include this timer when cycling with Next / Previous.");
				slotSettings.Name = B(section, "Name", tuple.Item1, "What it's called on screen and in the done message.");
				slotSettings.Duration = B(section, "Duration", tuple.Item2, "How long it runs: h:mm:ss, mm:ss, or plain minutes (e.g. 1:30:00, 20:00, 45).");
				slotSettings.Clock = B(section, "Clock", tuple.Item3, "Real = wall-clock time (tool durability, food). World = the world's clock, which also jumps ahead when everyone sleeps (crops, fermenters).");
				int captured = num;
				B(section, "Start / Stop", value: false, "Start or stop this timer now.", null, delegate
				{
					DrawSlotButton(captured);
				}, hideName: true);
				Slots[num] = slotSettings;
			}
		}

		private static ConfigEntry<T> B<T>(string section, string key, T value, string description, AcceptableValueBase acceptable = null, Action<ConfigEntryBase> drawer = null, bool hideName = false)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Expected O, but got Unknown
			ConfigurationManagerAttributes configurationManagerAttributes = new ConfigurationManagerAttributes
			{
				Order = _order--
			};
			if (drawer != null)
			{
				configurationManagerAttributes.CustomDrawer = drawer;
				configurationManagerAttributes.HideDefaultButton = true;
				configurationManagerAttributes.HideSettingName = hideName;
			}
			return _cfg.Bind<T>(section, key, value, new ConfigDescription(description, acceptable, new object[1] { configurationManagerAttributes }));
		}

		private static void DrawTestSoundButton()
		{
			Timers timers = ClockDaddyPlusPlugin.Instance?.Timers;
			if (timers != null && GUILayout.Button($"Test Sound  ({Sound.Value})", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }))
			{
				timers.PlaySound();
			}
		}

		private static void DrawSlotButton(int index)
		{
			Timers timers = ClockDaddyPlusPlugin.Instance?.Timers;
			if (timers != null)
			{
				RunningTimer timer;
				string text = ((!timers.TryGet(index, out timer)) ? ("Start  (" + Durations.Format(Slots[index].Length) + ")") : (timer.IsDone ? "Clear (done)" : ("Stop  (" + Durations.Format(timer.Remaining()) + " left)")));
				if (GUILayout.Button(text, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }))
				{
					timers.StartOrStop(index);
				}
			}
		}
	}
	internal sealed class RunningTimer
	{
		public int Slot;

		public string Name;

		public TimerClock Clock;

		public DateTime EndUtc;

		public double EndWorld;

		public string World;

		public bool IsDone;

		public float DoneAt;

		public float LastAlert;

		public TimeSpan Remaining()
		{
			if (Clock == TimerClock.Real)
			{
				return EndUtc - DateTime.UtcNow;
			}
			ZNet instance = ZNet.instance;
			if (!((Object)(object)instance != (Object)null))
			{
				return TimeSpan.MaxValue;
			}
			return TimeSpan.FromSeconds(EndWorld - instance.GetTimeSeconds());
		}
	}
	internal sealed class Timers
	{
		private readonly List<RunningTimer> _running = new List<RunningTimer>();

		private readonly AudioSource _audio;

		private readonly string _saveFile;

		public int Selected { get; private set; }

		public float PickerUntil { get; private set; }

		internal int AlertCount { get; private set; }

		public IReadOnlyList<RunningTimer> Running => _running;

		private static bool InGame
		{
			get
			{
				if ((Object)(object)Player.m_localPlayer != (Object)null)
				{
					return (Object)(object)ZNet.instance != (Object)null;
				}
				return false;
			}
		}

		private static string WorldName
		{
			get
			{
				if (!((Object)(object)ZNet.instance != (Object)null))
				{
					return null;
				}
				return ZNet.instance.GetWorldName();
			}
		}

		public Timers(AudioSource audio, string saveFile)
		{
			_audio = audio;
			_saveFile = saveFile;
			Load();
			Selected = FirstEnabled(0, 1);
		}

		public bool TryGet(int slot, out RunningTimer timer)
		{
			timer = _running.FirstOrDefault((RunningTimer t) => t.Slot == slot);
			return timer != null;
		}

		public static bool IsLive(RunningTimer t)
		{
			if (t.Clock != TimerClock.Real)
			{
				if ((Object)(object)ZNet.instance != (Object)null)
				{
					return t.World == WorldName;
				}
				return false;
			}
			return true;
		}

		public void Cycle(int direction)
		{
			int num = FirstEnabled(Selected + direction, direction);
			if (num < 0)
			{
				Notify("ClockDaddy+: no timers enabled (turn some on in F1)");
				return;
			}
			Selected = num;
			ShowPicker();
		}

		public void ShowPicker()
		{
			PickerUntil = Time.realtimeSinceStartup + Settings.SelectorSeconds.Value;
		}

		private static int FirstEnabled(int start, int step)
		{
			int num = 6;
			for (int i = 0; i < num; i++)
			{
				int num2 = ((start + i * step) % num + num) % num;
				if (Settings.Slots[num2].Enabled.Value)
				{
					return num2;
				}
			}
			return -1;
		}

		public void StartOrStopSelected()
		{
			if (Selected < 0 || !Settings.Slots[Selected].Enabled.Value)
			{
				Selected = FirstEnabled(Math.Max(Selected, 0), 1);
				if (Selected < 0)
				{
					Notify("ClockDaddy+: no timers enabled (turn some on in F1)");
					return;
				}
			}
			StartOrStop(Selected);
			ShowPicker();
		}

		public void StartOrStop(int slot)
		{
			if (TryGet(slot, out var timer))
			{
				_running.Remove(timer);
				Save();
				Notify(timer.IsDone ? (timer.Name + " cleared") : (timer.Name + " stopped"));
				return;
			}
			SlotSettings slotSettings = Settings.Slots[slot];
			RunningTimer runningTimer = new RunningTimer
			{
				Slot = slot,
				Name = slotSettings.Label,
				Clock = slotSettings.Clock.Value
			};
			if (runningTimer.Clock == TimerClock.Real)
			{
				runningTimer.EndUtc = DateTime.UtcNow + slotSettings.Length;
			}
			else
			{
				if (!InGame)
				{
					Notify(runningTimer.Name + ": World timers can only start in a world");
					return;
				}
				runningTimer.EndWorld = ZNet.instance.GetTimeSeconds() + slotSettings.Length.TotalSeconds;
				runningTimer.World = WorldName;
			}
			_running.Add(runningTimer);
			Save();
			Notify(runningTimer.Name + " started (" + Durations.Format(slotSettings.Length) + ")");
		}

		public void StopAll()
		{
			if (_running.Count != 0)
			{
				_running.Clear();
				Save();
				Notify("All timers stopped");
			}
		}

		public void Update()
		{
			if (!InGame)
			{
				return;
			}
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			bool flag = false;
			for (int num = _running.Count - 1; num >= 0; num--)
			{
				RunningTimer runningTimer = _running[num];
				if (!IsLive(runningTimer))
				{
					continue;
				}
				if (!runningTimer.IsDone)
				{
					if (runningTimer.Remaining() > TimeSpan.Zero)
					{
						continue;
					}
					runningTimer.IsDone = true;
					runningTimer.DoneAt = realtimeSinceStartup;
					Alert(runningTimer);
					flag = true;
				}
				int value = Settings.KeepDoneSeconds.Value;
				if (realtimeSinceStartup - runningTimer.DoneAt >= (float)value)
				{
					_running.RemoveAt(num);
					flag = true;
					continue;
				}
				int value2 = Settings.RepeatSeconds.Value;
				if (value2 > 0 && realtimeSinceStartup - runningTimer.LastAlert >= (float)value2)
				{
					Alert(runningTimer);
				}
			}
			if (flag)
			{
				Save();
			}
		}

		private void Alert(RunningTimer t)
		{
			t.LastAlert = Time.realtimeSinceStartup;
			AlertCount++;
			PlaySound();
			AlertMessage value = Settings.MessageStyle.Value;
			if (value != AlertMessage.None && !((Object)(object)MessageHud.instance == (Object)null))
			{
				string text = (Settings.DoneMessage.Value ?? "").Replace("{name}", t.Name);
				if (text.Length != 0)
				{
					MessageHud.instance.ShowMessage((MessageType)((value == AlertMessage.TopLeft) ? 1 : 2), text, 0, (Sprite)null, true, true);
				}
			}
		}

		public void PlaySound()
		{
			AudioClip val = Chime.Get(Settings.Sound.Value);
			if ((Object)(object)val != (Object)null && (Object)(object)_audio != (Object)null)
			{
				_audio.PlayOneShot(val, Settings.Volume.Value);
			}
		}

		private static void Notify(string text)
		{
			if ((Object)(object)MessageHud.instance != (Object)null && InGame)
			{
				MessageHud.instance.ShowMessage((MessageType)1, text, 0, (Sprite)null, false, true);
			}
		}

		private void Save()
		{
			try
			{
				IEnumerable<string> contents = from t in _running
					where !t.IsDone
					select string.Join("|", t.Slot.ToString(CultureInfo.InvariantCulture), t.Clock.ToString(), t.EndUtc.Ticks.ToString(CultureInfo.InvariantCulture), t.EndWorld.ToString("R", CultureInfo.InvariantCulture), (t.World ?? "").Replace("|", "/"), (t.Name ?? "").Replace("|", "/"));
				File.WriteAllLines(_saveFile, contents);
			}
			catch (Exception ex)
			{
				ManualLogSource log = ClockDaddyPlusPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("Couldn't save timers: " + ex.Message));
				}
			}
		}

		private void Load()
		{
			try
			{
				if (!File.Exists(_saveFile))
				{
					return;
				}
				string[] array = File.ReadAllLines(_saveFile);
				foreach (string obj in array)
				{
					string[] array2 = obj.Split('|');
					if (array2.Length >= 6 && int.TryParse(array2[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out var slot) && slot >= 0 && slot < 6 && !_running.Any((RunningTimer t) => t.Slot == slot) && Enum.TryParse<TimerClock>(array2[1], out var result))
					{
						long.TryParse(array2[2], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2);
						double.TryParse(array2[3], NumberStyles.Float, CultureInfo.InvariantCulture, out var result3);
						_running.Add(new RunningTimer
						{
							Slot = slot,
							Clock = result,
							EndUtc = new DateTime(result2, DateTimeKind.Utc),
							EndWorld = result3,
							World = ((array2[4].Length > 0) ? array2[4] : null),
							Name = array2[5]
						});
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = ClockDaddyPlusPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("Couldn't load saved timers: " + ex.Message));
				}
			}
		}
	}
}