Decompiled source of TimbnCore v1.3.0

Timbn.GraveyardKeeperTwo.Core/Timbn.GraveyardKeeperTwo.Core.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using LazyBearTechnology;
using Microsoft.CodeAnalysis;
using TMPro;
using Timbn.GraveyardKeeperTwo.Core.Framework;
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("gavinsteinhoff")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Shared framework for Graveyard Keeper 2 (BepInEx 5) mods. A plugin base class with auto patching and config, plus the Core mod that hosts it.")]
[assembly: AssemblyFileVersion("1.3.0.0")]
[assembly: AssemblyInformationalVersion("1.3.0+42577719540a953590d1b1b778a9af1bc1cbf13e")]
[assembly: AssemblyProduct("Timbn Core")]
[assembly: AssemblyTitle("Timbn.GraveyardKeeperTwo.Core")]
[assembly: AssemblyVersion("1.3.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 Timbn.GraveyardKeeperTwo.Core
{
	[BepInPlugin("Timbn.GraveyardKeeperTwo.Core", "Timbn Core", "1.3.0")]
	public class TimbnCorePlugin : TimbnFrameworkPlugin<TimbnCorePlugin>
	{
		public const string Guid = "Timbn.GraveyardKeeperTwo.Core";

		public const string Version = "1.3.0";

		public const string TestedGameVersion = "1.005";

		public const string TestedGameBuild = "70946fdd5c76";

		private bool _loadErrorsChecked;

		public static string GameBuild { get; } = typeof(MainGame).Assembly.ManifestModule.ModuleVersionId.ToString("N").Substring(0, 12);

		public static TimbnCorePlugin? Instance { get; private set; }

		protected override void OnAwake()
		{
			Instance = this;
			base.Subscriptions.Add(TimbnGameEvents.GameStarted(TimbnGame.OnGameStarted));
			base.Subscriptions.Add(TimbnGameEvents.GoToMainMenu(TimbnGame.OnLeftGame));
			base.Subscriptions.Add(TimbnGameEvents.GameStarted(TimbnDialog.OnGameStarted));
			base.Subscriptions.Add(TimbnGameEvents.GameStarted(OnGameStarted));
			base.Subscriptions.Add(TimbnGameEvents.GoToMainMenu(TimbnDialog.OnLeftGame));
			string text = "Core 1.3.0 started on Graveyard Keeper 2 " + Application.version + " (build " + GameBuild + ")";
			if (Application.version != "1.005")
			{
				TimbnFrameworkPlugin<TimbnCorePlugin>.Logger.LogWarning((object)(text + ", but these mods were tested on 1.005. Include this line when reporting a problem."));
				base.MainMenu.Popup("Untested game version", "Timbn mods were tested on version 1.005 and this is " + Application.version + ". Some mods may misbehave until they are updated.");
			}
			else if (GameBuild != "70946fdd5c76")
			{
				TimbnFrameworkPlugin<TimbnCorePlugin>.Logger.LogWarning((object)(text + ", but these mods were tested on build 70946fdd5c76 of that version, so this is likely a hotfix. Include this line when reporting a problem."));
				base.MainMenu.Popup("Untested game build", "Timbn mods were tested on an earlier build of " + Application.version + ", so this is likely a hotfix. Some mods may misbehave until they are updated.");
			}
			else
			{
				TimbnFrameworkPlugin<TimbnCorePlugin>.Logger.LogInfo((object)(text + "."));
			}
		}

		private void OnGameStarted()
		{
			TimbnQuests.OnGameStarted();
			base.SessionSubscriptions.Add(TimbnGameEvents.QuestStarted(TimbnQuests.OnQuestStarted));
			TimbnPotions.OnGameStarted(base.SessionSubscriptions);
		}

		protected override void OnUpdate()
		{
			if (!_loadErrorsChecked)
			{
				_loadErrorsChecked = true;
				string text = TimbnLoadErrors.Describe();
				if (text != null)
				{
					base.MainMenu.Popup("Some mods did not load", text);
				}
			}
			TimbnDialog.OnUpdate();
			TimbnPotions.OnUpdate();
			TimbnMainMenu.OnUpdate();
		}

		protected override void OnDestroyed()
		{
			Instance = null;
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "Timbn.GraveyardKeeperTwo.Core";

		public const string PLUGIN_NAME = "Timbn Core";

		public const string PLUGIN_VERSION = "1.3.0";
	}
}
namespace Timbn.GraveyardKeeperTwo.Core.Patches
{
	[HarmonyPatch(typeof(EasySpritesCollection))]
	internal static class EasySpritesCollectionPatch
	{
		[HarmonyPrefix]
		[HarmonyPatch("GetSprite")]
		private static bool GetSprite(string spriteName, ref Sprite __result)
		{
			if (!TimbnSprites.TryGet(spriteName, out Sprite sprite))
			{
				return true;
			}
			__result = sprite;
			return false;
		}

		[HarmonyPrefix]
		[HarmonyPatch("HasSprite")]
		private static bool HasSprite(string spriteName, ref bool __result)
		{
			if (!TimbnSprites.TryGet(spriteName, out Sprite _))
			{
				return true;
			}
			__result = true;
			return false;
		}
	}
	[HarmonyPatch(typeof(GameBalance), "LoadGameBalance")]
	internal static class GameBalancePatch
	{
		private static void Postfix()
		{
			GameBalance loaded = TimbnBalance.Loaded;
			if (loaded != null)
			{
				TimbnBalance.OnBalanceLoaded(loaded);
			}
			TimbnPotions.OnBalanceLoaded();
		}
	}
	[HarmonyPatch(typeof(LLBase), "InitHashDictionary")]
	internal static class LLBasePatch
	{
		private static void Postfix(LLBase __instance)
		{
			TimbnLocale.ApplyTo(__instance);
		}
	}
	[HarmonyPatch(typeof(QuestSystemData), "PrepareForGame")]
	internal static class QuestSystemDataPatch
	{
		private static void Prefix(QuestSystemData __instance)
		{
			TimbnQuests.StubOrphans(__instance);
		}

		private static void Postfix(QuestSystemData __instance)
		{
			TimbnQuests.OnQuestsPrepared(__instance);
		}
	}
	[HarmonyPatch(typeof(SaveSystem), "Save")]
	internal static class SaveSystemPatch
	{
		private static void Prefix(GameSave gameSave, out IDisposable[] __state)
		{
			try
			{
				__state = new IDisposable[2]
				{
					TimbnQuests.HideForSave(gameSave),
					TimbnDialog.RemoveForSave()
				};
			}
			catch (Exception arg)
			{
				TimbnFrameworkPlugin<TimbnCorePlugin>.Logger.LogError((object)string.Format("{0}|Could not strip mod state from the save: {1}", "SaveSystemPatch", arg));
				__state = Array.Empty<IDisposable>();
			}
		}

		private static void Finalizer(IDisposable[]? __state)
		{
			if (__state == null)
			{
				return;
			}
			foreach (IDisposable disposable in __state)
			{
				try
				{
					disposable.Dispose();
				}
				catch (Exception arg)
				{
					TimbnFrameworkPlugin<TimbnCorePlugin>.Logger.LogError((object)string.Format("{0}|Could not restore mod state after the save: {1}", "SaveSystemPatch", arg));
				}
			}
		}
	}
	[HarmonyPatch(typeof(UIMainMenuInfoPanel), "SetMenuOnlyLabelsVisible")]
	internal static class UIMainMenuInfoPanelPatch
	{
		private static void Postfix()
		{
			TimbnMainMenu.DrawLines();
		}
	}
	[HarmonyPatch(typeof(UIMainMenuWindow), "Open")]
	internal static class UIMainMenuWindowPatch
	{
		private static void Postfix(UIMainMenuWindow __instance)
		{
			TimbnMainMenu.OnMenuOpened(__instance);
		}
	}
	[HarmonyPatch(typeof(WgoData), "FireInteractionEvent")]
	internal static class WgoDataPatch
	{
		private static bool Prefix(WgoData __instance, ref bool __result)
		{
			if (!TimbnDialog.TryHandleInteraction(__instance))
			{
				return true;
			}
			__result = true;
			return false;
		}
	}
}
namespace Timbn.GraveyardKeeperTwo.Core.Framework
{
	public static class TimbnBalance
	{
		private sealed class Entry
		{
			public BalanceBaseObject Definition { get; }

			public Action<GameBalance>? Prepare { get; }

			public Entry(BalanceBaseObject definition, Action<GameBalance>? prepare)
			{
				Definition = definition;
				Prepare = prepare;
			}
		}

		private static readonly FieldRef<GameBalance?> _instance = AccessTools.StaticFieldRefAccess<GameBalance>(AccessTools.Field(typeof(GameBalance), "instance"));

		private static readonly FieldRef<GameBalanceBase, List<IList>> _datas = AccessTools.FieldRefAccess<GameBalanceBase, List<IList>>("datas");

		private static readonly FieldRef<GameBalanceBase, List<Type>> _types = AccessTools.FieldRefAccess<GameBalanceBase, List<Type>>("types");

		private static readonly FieldRef<GameBalanceBase, List<Dictionary<string, int>>> _cache = AccessTools.FieldRefAccess<GameBalanceBase, List<Dictionary<string, int>>>("cache");

		private static readonly Action<GameBalance> _createQuestsCache = AccessTools.MethodDelegate<Action<GameBalance>>(AccessTools.Method(typeof(GameBalance), "CreateQuestsCache", (Type[])null, (Type[])null), (object)null, true);

		private static readonly List<Entry> _entries = new List<Entry>();

		public static GameBalance? Loaded => _instance.Invoke();

		internal static IDisposable Add(BalanceBaseObject definition)
		{
			return Add(definition, null);
		}

		internal static IDisposable Add(BalanceBaseObject definition, Action<GameBalance>? prepare)
		{
			Entry entry = new Entry(definition, prepare);
			_entries.Add(entry);
			GameBalance loaded = Loaded;
			if (loaded != null)
			{
				Insert(loaded, entry);
				RefreshDerivedCaches(loaded, ((object)definition).GetType());
			}
			return new TimbnUndo(delegate
			{
				Remove(entry);
			});
		}

		internal static void OnBalanceLoaded(GameBalance balance)
		{
			HashSet<Type> hashSet = new HashSet<Type>();
			foreach (Entry entry in _entries)
			{
				if (Insert(balance, entry))
				{
					hashSet.Add(((object)entry.Definition).GetType());
				}
			}
			foreach (Type item in hashSet)
			{
				RefreshDerivedCaches(balance, item);
			}
		}

		internal static void RefreshQuestCaches(GameBalance balance)
		{
			_createQuestsCache(balance);
		}

		private static bool Insert(GameBalance balance, Entry entry)
		{
			BalanceBaseObject definition = entry.Definition;
			int num = _types.Invoke((GameBalanceBase)(object)balance).IndexOf(((object)definition).GetType());
			if (num == -1)
			{
				TimbnFrameworkPlugin<TimbnCorePlugin>.Logger.LogError((object)("TimbnBalance|The balance has no table for " + ((object)definition).GetType().Name + "."));
				return false;
			}
			IList list = _datas.Invoke((GameBalanceBase)(object)balance)[num];
			if (list.Contains(definition))
			{
				return false;
			}
			entry.Prepare?.Invoke(balance);
			int existing = FindById(list, definition.id);
			if (existing == -1)
			{
				list.Add(definition);
				_cache.Invoke((GameBalanceBase)(object)balance)[num][definition.id] = list.Count - 1;
				return true;
			}
			if (!_entries.Any((Entry e) => e != entry && e.Definition == list[existing]))
			{
				TimbnFrameworkPlugin<TimbnCorePlugin>.Logger.LogError((object)("TimbnBalance|The game already has a " + ((object)definition).GetType().Name + " with id '" + definition.id + "'."));
				return false;
			}
			list[existing] = definition;
			return true;
		}

		private static void Remove(Entry entry)
		{
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			_entries.Remove(entry);
			GameBalance loaded = Loaded;
			if (loaded == null)
			{
				return;
			}
			BalanceBaseObject definition = entry.Definition;
			int num = _types.Invoke((GameBalanceBase)(object)loaded).IndexOf(((object)definition).GetType());
			if (num == -1)
			{
				return;
			}
			IList list = _datas.Invoke((GameBalanceBase)(object)loaded)[num];
			int num2 = list.IndexOf(definition);
			if (num2 != -1)
			{
				list.RemoveAt(num2);
				Dictionary<string, int> dictionary = _cache.Invoke((GameBalanceBase)(object)loaded)[num];
				dictionary.Clear();
				for (int i = 0; i < list.Count; i++)
				{
					dictionary[((BalanceBaseObject)list[i]).id] = i;
				}
				RefreshDerivedCaches(loaded, ((object)definition).GetType());
			}
		}

		private static int FindById(IList list, string id)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < list.Count; i++)
			{
				if (((BalanceBaseObject)list[i]).id == id)
				{
					return i;
				}
			}
			return -1;
		}

		private static void RefreshDerivedCaches(GameBalance balance, Type type)
		{
			if (type == typeof(QuestDef))
			{
				_createQuestsCache(balance);
			}
		}
	}
	public sealed class TimbnPluginBalance
	{
		private readonly TimbnFrameworkPlugin _owner;

		internal TimbnPluginBalance(TimbnFrameworkPlugin owner)
		{
			_owner = owner;
		}

		public IDisposable Add(BalanceBaseObject definition)
		{
			return _owner.Subscriptions.Add(TimbnBalance.Add(definition));
		}

		public IDisposable Add(BalanceBaseObject definition, Action<GameBalance>? prepare)
		{
			return _owner.Subscriptions.Add(TimbnBalance.Add(definition, prepare));
		}
	}
	public sealed class TimbnConversation
	{
		private enum StepKind
		{
			Say,
			PlayerSay,
			Do,
			Ask
		}

		private sealed class Step(StepKind kind)
		{
			public StepKind Kind { get; } = kind;

			public string Key { get; set; } = "";

			public Action? Action { get; set; }

			public string[] Answers { get; set; } = Array.Empty<string>();

			public Dictionary<string, TimbnConversation> Branches { get; } = new Dictionary<string, TimbnConversation>();
		}

		private readonly List<Step> _steps = new List<Step>();

		private readonly string _talkId;

		public WgoData Npc { get; }

		internal TimbnConversation(WgoData npc, string talkId)
		{
			Npc = npc;
			_talkId = talkId;
		}

		public TimbnConversation Say(string key)
		{
			_steps.Add(new Step(StepKind.Say)
			{
				Key = key
			});
			return this;
		}

		public TimbnConversation PlayerSay(string key)
		{
			_steps.Add(new Step(StepKind.PlayerSay)
			{
				Key = key
			});
			return this;
		}

		public TimbnConversation Do(Action action)
		{
			_steps.Add(new Step(StepKind.Do)
			{
				Action = action
			});
			return this;
		}

		public TimbnConversation Ask(params string[] answers)
		{
			if (answers.Length == 0)
			{
				throw new ArgumentException("Ask needs at least one answer.", "answers");
			}
			_steps.Add(new Step(StepKind.Ask)
			{
				Answers = answers
			});
			return this;
		}

		public TimbnConversation If(string answer, Action<TimbnConversation> then)
		{
			if (_steps.Count != 0)
			{
				List<Step> steps = _steps;
				if (steps[steps.Count - 1].Kind == StepKind.Ask)
				{
					TimbnConversation timbnConversation = new TimbnConversation(Npc, _talkId);
					then(timbnConversation);
					List<Step> steps2 = _steps;
					steps2[steps2.Count - 1].Branches[answer] = timbnConversation;
					return this;
				}
			}
			throw new InvalidOperationException("If has to follow Ask.");
		}

		internal void Run(Action done)
		{
			RunFrom(0, done, done);
		}

		private void RunFrom(int index, Action next, Action end)
		{
			if (index >= _steps.Count)
			{
				next();
				return;
			}
			Step step = _steps[index];
			switch (step.Kind)
			{
			case StepKind.Say:
				if (!TimbnDialog.Say(Npc, step.Key, carryOn))
				{
					end();
				}
				break;
			case StepKind.PlayerSay:
				if (!TimbnDialog.PlayerSay(step.Key, carryOn))
				{
					end();
				}
				break;
			case StepKind.Do:
				try
				{
					step.Action();
				}
				catch (Exception arg)
				{
					TimbnFrameworkPlugin<TimbnCorePlugin>.Logger.LogError((object)string.Format("{0}|A step in talk '{1}' threw: {2}", "TimbnConversation", _talkId, arg));
					end();
					break;
				}
				carryOn();
				break;
			case StepKind.Ask:
				if (!TimbnDialog.Ask(Npc, step.Answers, delegate(string chosen)
				{
					if (step.Branches.TryGetValue(chosen, out TimbnConversation value))
					{
						value.RunFrom(0, carryOn, end);
					}
					else
					{
						carryOn();
					}
				}, end))
				{
					end();
				}
				break;
			}
			void carryOn()
			{
				RunFrom(index + 1, next, end);
			}
		}
	}
	internal static class TimbnDialog
	{
		private sealed class Talk
		{
			public string NpcId { get; }

			public string EventId { get; }

			public Func<bool> IsOffered { get; }

			public Action<TimbnConversation> OnTalk { get; }

			public bool Busy { get; set; }

			public Talk(string npcId, string eventId, Func<bool> isOffered, Action<TimbnConversation> onTalk)
			{
				NpcId = npcId;
				EventId = eventId;
				IsOffered = isOffered;
				OnTalk = onTalk;
			}
		}

		private const string LeaveKey = "common_leave";

		private static readonly List<Talk> _talks = new List<Talk>();

		private static float _nextRefresh;

		private static bool _holdsControl;

		internal static IDisposable AddTalk(string npcId, string eventId, Func<bool> isOffered, Action<TimbnConversation> onTalk)
		{
			Talk talk = new Talk(npcId, eventId, isOffered, onTalk);
			_talks.RemoveAll((Talk t) => t.NpcId == npcId && t.EventId == eventId);
			_talks.Add(talk);
			Refresh();
			return new TimbnUndo(delegate
			{
				if (_talks.Remove(talk) && !_talks.Any((Talk t) => t.NpcId == npcId && t.EventId == eventId) && TimbnGame.IsInGame)
				{
					WgoData wgoData = MainGame.WorldData.GetWgoData(npcId);
					if (wgoData != null)
					{
						wgoData.RemoveInteractionEvent(eventId);
					}
				}
			});
		}

		internal static bool Say(WgoData npc, string key, Action then)
		{
			return Show(key, delegate
			{
				//IL_001c: Unknown result type (might be due to invalid IL or missing references)
				Bubble.Talk(new PhraseData(false, npc, key, then, (SpeechBubblePreset)null, (SpeechBubbleType)1, (ForceCornerPosition)0, 0f, false));
			});
		}

		internal static bool PlayerSay(string key, Action then)
		{
			return Show(key, delegate
			{
				//IL_0017: Unknown result type (might be due to invalid IL or missing references)
				Bubble.Talk(new PhraseData(true, (WgoData)null, key, then, (SpeechBubblePreset)null, (SpeechBubbleType)1, (ForceCornerPosition)0, 0f, false));
			});
		}

		internal static AnswerVisualData Answer(string key)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Expected O, but got Unknown
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Expected O, but got Unknown
			//IL_0055: Expected O, but got Unknown
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			AnswerData val = null;
			if (GameBalance.Me.questDefByReqPhrase.TryGetValue(key, out var value))
			{
				val = value.finishCheck.GetAnswerDataByReqs();
				int num = TimbnQuests.MoneyRewardOf(value);
				if (num > 0)
				{
					object obj = val;
					if (obj == null)
					{
						AnswerData val2 = new AnswerData();
						val = val2;
						obj = (object)val2;
					}
					((AnswerData)obj).AddRewardRes(new SmartRes
					{
						gameRes = new GameRes("money", (float)num)
					});
				}
			}
			return new AnswerVisualData
			{
				id = key,
				answerData = val
			};
		}

		internal static bool Ask(WgoData npc, string[] answers, Action<string> chosen, Action dismissed)
		{
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Expected O, but got Unknown
			List<AnswerVisualData> visuals = answers.Select(Answer).ToList();
			bool addedLeave = visuals.All((AnswerVisualData v) => v.answerData != null);
			if (addedLeave)
			{
				visuals.Add(new AnswerVisualData
				{
					id = "common_leave"
				});
			}
			bool picked = false;
			return Show(string.Join(", ", answers), delegate
			{
				Bubble.ShowMultiAnswer(visuals, MainGame.PlayerController.BubblePoint, npc, (Action<string>)delegate(string id)
				{
					picked = true;
					if (addedLeave && id == "common_leave")
					{
						dismissed();
					}
					else
					{
						GlobalEventsSystem.FireTrigger((Type)15, id);
						chosen(id);
					}
				}, (Action)delegate
				{
					if (!picked)
					{
						dismissed();
					}
				}, false);
			});
		}

		private static bool Show(string what, Action show)
		{
			try
			{
				show();
				return true;
			}
			catch (Exception arg)
			{
				TimbnFrameworkPlugin<TimbnCorePlugin>.Logger.LogError((object)string.Format("{0}|Showing '{1}' threw: {2}", "TimbnDialog", what, arg));
				return false;
			}
		}

		internal static void OnGameStarted()
		{
			_holdsControl = false;
			Refresh();
		}

		internal static void OnLeftGame()
		{
			_holdsControl = false;
			foreach (Talk talk in _talks)
			{
				talk.Busy = false;
			}
		}

		internal static void OnUpdate()
		{
			if (TimbnGame.IsInGame && !(Time.unscaledTime < _nextRefresh))
			{
				_nextRefresh = Time.unscaledTime + 1f;
				Refresh();
			}
		}

		internal static bool TryHandleInteraction(WgoData npc)
		{
			InteractionEvent next = npc.PeekFirstAddedEvent();
			if (next == null)
			{
				return false;
			}
			Talk talk = _talks.Find((Talk t) => t.NpcId == ((ObjectLinkedToDefinition<WGODef>)(object)npc).id && t.EventId == next.str);
			if (talk == null)
			{
				return false;
			}
			npc.RemoveInteractionEvent(talk.EventId);
			talk.Busy = true;
			TakeControl();
			TimbnConversation timbnConversation = new TimbnConversation(npc, talk.EventId);
			try
			{
				talk.OnTalk(timbnConversation);
			}
			catch (Exception ex)
			{
				TimbnFrameworkPlugin<TimbnCorePlugin>.Logger.LogError((object)string.Format("{0}|Talk '{1}' on {2} threw: {3}", "TimbnDialog", talk.EventId, talk.NpcId, ex));
				done();
				return true;
			}
			timbnConversation.Run(done);
			return true;
			void done()
			{
				if (talk.Busy)
				{
					talk.Busy = false;
					ReleaseControl();
					Refresh();
				}
			}
		}

		internal static IDisposable RemoveForSave()
		{
			if (!TimbnGame.IsInGame)
			{
				return new TimbnUndo(delegate
				{
				});
			}
			foreach (Talk talk in _talks)
			{
				WgoData wgoData = MainGame.WorldData.GetWgoData(talk.NpcId);
				if (wgoData != null)
				{
					wgoData.RemoveInteractionEvent(talk.EventId);
				}
			}
			return new TimbnUndo(Refresh);
		}

		private static void TakeControl()
		{
			if (!_holdsControl && TimbnGame.IsInGame && !((Object)(object)MainGame.PlayerController == (Object)null))
			{
				_holdsControl = true;
				MainGame.PlayerController.SetControlTakenType((TakenControlType)3, false);
			}
		}

		private static void ReleaseControl()
		{
			if (_holdsControl)
			{
				_holdsControl = false;
				if (TimbnGame.IsInGame && (Object)(object)MainGame.PlayerController != (Object)null)
				{
					MainGame.PlayerController.SetControlTakenType((TakenControlType)3, true);
				}
			}
		}

		private static void Refresh()
		{
			if (!TimbnGame.IsInGame)
			{
				return;
			}
			foreach (Talk talk in _talks)
			{
				WgoData wgoData = MainGame.WorldData.GetWgoData(talk.NpcId);
				if (wgoData != null)
				{
					bool flag = wgoData.Events.Any((InteractionEvent e) => e.str == talk.EventId);
					bool flag2 = !talk.Busy && IsOffered(talk);
					if (flag2 && !flag)
					{
						wgoData.AddInteractionEvent(talk.EventId, false);
					}
					else if (!flag2 && flag)
					{
						wgoData.RemoveInteractionEvent(talk.EventId);
					}
				}
			}
		}

		private static bool IsOffered(Talk talk)
		{
			try
			{
				return talk.IsOffered();
			}
			catch (Exception arg)
			{
				TimbnFrameworkPlugin<TimbnCorePlugin>.Logger.LogError((object)string.Format("{0}|isOffered for '{1}' threw: {2}", "TimbnDialog", talk.EventId, arg));
				return false;
			}
		}
	}
	public sealed class TimbnPluginDialog
	{
		private readonly TimbnFrameworkPlugin _owner;

		internal TimbnPluginDialog(TimbnFrameworkPlugin owner)
		{
			_owner = owner;
		}

		public IDisposable AddTalk(string npcId, string eventId, Func<bool> isOffered, Action<TimbnConversation> talk)
		{
			return _owner.Subscriptions.Add(TimbnDialog.AddTalk(npcId, eventId, isOffered, talk));
		}
	}
	internal static class TimbnGameEvents
	{
		internal static IDisposable GameStarted(Action handler)
		{
			return On(handler, delegate(Action h)
			{
				MainGame.OnGameStarted = (Action)Delegate.Combine(MainGame.OnGameStarted, h);
			}, delegate(Action h)
			{
				MainGame.OnGameStarted = (Action)Delegate.Remove(MainGame.OnGameStarted, h);
			});
		}

		internal static IDisposable GoToMainMenu(Action handler)
		{
			return On(handler, delegate(Action h)
			{
				MainGame.OnGoToMainMenu = (Action)Delegate.Combine(MainGame.OnGoToMainMenu, h);
			}, delegate(Action h)
			{
				MainGame.OnGoToMainMenu = (Action)Delegate.Remove(MainGame.OnGoToMainMenu, h);
			});
		}

		internal static IDisposable GamePaused(Action handler)
		{
			return On(handler, delegate(Action h)
			{
				MainGame.OnGamePaused = (Action)Delegate.Combine(MainGame.OnGamePaused, h);
			}, delegate(Action h)
			{
				MainGame.OnGamePaused = (Action)Delegate.Remove(MainGame.OnGamePaused, h);
			});
		}

		internal static IDisposable GameUnpaused(Action handler)
		{
			return On(handler, delegate(Action h)
			{
				MainGame.OnGameUnpaused = (Action)Delegate.Combine(MainGame.OnGameUnpaused, h);
			}, delegate(Action h)
			{
				MainGame.OnGameUnpaused = (Action)Delegate.Remove(MainGame.OnGameUnpaused, h);
			});
		}

		internal static IDisposable NewDayStarted(Action<int> handler)
		{
			return On(handler, delegate(Action<int> h)
			{
				EnvironmentEngine.OnNewDayStarted += h;
			}, delegate(Action<int> h)
			{
				EnvironmentEngine.OnNewDayStarted -= h;
			});
		}

		internal static IDisposable NewWeekdayStarted(Action<int> handler)
		{
			return On(handler, delegate(Action<int> h)
			{
				EnvironmentEngine.OnNewDayStartedWithDayNumber += h;
			}, delegate(Action<int> h)
			{
				EnvironmentEngine.OnNewDayStartedWithDayNumber -= h;
			});
		}

		internal static IDisposable TimeOfDayChanged(Action<float, bool> handler)
		{
			return On(handler, delegate(Action<float, bool> h)
			{
				EnvironmentEngine.OnTimeOfDayChangedEvent += h;
			}, delegate(Action<float, bool> h)
			{
				EnvironmentEngine.OnTimeOfDayChangedEvent -= h;
			});
		}

		internal static IDisposable SaveWriteStarted(Action handler)
		{
			return On(handler, delegate(Action h)
			{
				SaveSystem.OnSaveWriteStarted += h;
			}, delegate(Action h)
			{
				SaveSystem.OnSaveWriteStarted -= h;
			});
		}

		internal static IDisposable SaveWriteEnded(Action handler)
		{
			return On(handler, delegate(Action h)
			{
				SaveSystem.OnSaveWriteEnded += h;
			}, delegate(Action h)
			{
				SaveSystem.OnSaveWriteEnded -= h;
			});
		}

		internal static IDisposable SaveLoadingStarted(Action handler)
		{
			return On(handler, delegate(Action h)
			{
				SaveSystem.OnSaveLoadingStarted += h;
			}, delegate(Action h)
			{
				SaveSystem.OnSaveLoadingStarted -= h;
			});
		}

		internal static IDisposable SaveLoadingEnded(Action handler)
		{
			return On(handler, delegate(Action h)
			{
				SaveSystem.OnSaveLoadingEnded += h;
			}, delegate(Action h)
			{
				SaveSystem.OnSaveLoadingEnded -= h;
			});
		}

		internal static IDisposable PlayerTeleported(Action handler)
		{
			return On(handler, delegate(Action h)
			{
				PlayerController.OnPlayerTeleported += h;
			}, delegate(Action h)
			{
				PlayerController.OnPlayerTeleported -= h;
			});
		}

		internal static IDisposable WorldObjectSpawned(Action<Wgo> handler)
		{
			return On(handler, delegate(Action<Wgo> h)
			{
				Wgo.OnWgoSpawn += h;
			}, delegate(Action<Wgo> h)
			{
				Wgo.OnWgoSpawn -= h;
			});
		}

		internal static IDisposable WorldObjectDestroyed(Action<Wgo> handler)
		{
			return On(handler, delegate(Action<Wgo> h)
			{
				Wgo.OnWgoDestroy += h;
			}, delegate(Action<Wgo> h)
			{
				Wgo.OnWgoDestroy -= h;
			});
		}

		internal static IDisposable QuestStarted(Action<QuestData> handler)
		{
			QuestSystemData quests = MainGame.Instance.GameSave.questSystemData;
			return On(handler, delegate(Action<QuestData> h)
			{
				quests.OnQuestStarted += h;
			}, delegate(Action<QuestData> h)
			{
				quests.OnQuestStarted -= h;
			});
		}

		internal static IDisposable QuestCompleted(Action<QuestData> handler)
		{
			QuestSystemData quests = MainGame.Instance.GameSave.questSystemData;
			return On(handler, delegate(Action<QuestData> h)
			{
				quests.OnQuestCompleted += h;
			}, delegate(Action<QuestData> h)
			{
				quests.OnQuestCompleted -= h;
			});
		}

		internal static IDisposable QuestCanceled(Action<QuestData> handler)
		{
			QuestSystemData quests = MainGame.Instance.GameSave.questSystemData;
			return On(handler, delegate(Action<QuestData> h)
			{
				quests.OnQuestCanceled += h;
			}, delegate(Action<QuestData> h)
			{
				quests.OnQuestCanceled -= h;
			});
		}

		internal static IDisposable PlayerResourceChanged(Action handler)
		{
			PlayerData player = MainGame.PlayerData;
			return On(handler, delegate(Action h)
			{
				player.OnGameResChanged += h;
			}, delegate(Action h)
			{
				player.OnGameResChanged -= h;
			});
		}

		internal static IDisposable PlayerDropCollected(Action<List<Item>> handler)
		{
			PlayerData player = MainGame.PlayerData;
			return On(handler, delegate(Action<List<Item>> h)
			{
				player.OnDropCollected += h;
			}, delegate(Action<List<Item>> h)
			{
				player.OnDropCollected -= h;
			});
		}

		internal static IDisposable PlayerItemUsed(Action<Item> handler)
		{
			PlayerData player = MainGame.PlayerData;
			return On(handler, delegate(Action<Item> h)
			{
				player.OnItemUsed += h;
			}, delegate(Action<Item> h)
			{
				player.OnItemUsed -= h;
			});
		}

		internal static IDisposable PlayerItemsAdded(Action<List<Item>> handler)
		{
			Inventory inventory = MainGame.PlayerData.inventory;
			return On(handler, delegate(Action<List<Item>> h)
			{
				inventory.OnItemsAdd += h;
			}, delegate(Action<List<Item>> h)
			{
				inventory.OnItemsAdd -= h;
			});
		}

		internal static IDisposable PlayerItemsRemoved(Action<List<Item>> handler)
		{
			Inventory inventory = MainGame.PlayerData.inventory;
			return On(handler, delegate(Action<List<Item>> h)
			{
				inventory.OnItemsRemove += h;
			}, delegate(Action<List<Item>> h)
			{
				inventory.OnItemsRemove -= h;
			});
		}

		internal static IDisposable On(Action handler, Action<Action> add, Action<Action> remove)
		{
			add(safe);
			return new TimbnUndo(delegate
			{
				remove(safe);
			});
			void safe()
			{
				try
				{
					handler();
				}
				catch (Exception ex)
				{
					LogHandlerError(handler, ex);
				}
			}
		}

		internal static IDisposable On<T>(Action<T> handler, Action<Action<T>> add, Action<Action<T>> remove)
		{
			add(safe);
			return new TimbnUndo(delegate
			{
				remove(safe);
			});
			void safe(T arg)
			{
				try
				{
					handler(arg);
				}
				catch (Exception ex)
				{
					LogHandlerError(handler, ex);
				}
			}
		}

		internal static IDisposable On<T1, T2>(Action<T1, T2> handler, Action<Action<T1, T2>> add, Action<Action<T1, T2>> remove)
		{
			add(safe);
			return new TimbnUndo(delegate
			{
				remove(safe);
			});
			void safe(T1 first, T2 second)
			{
				try
				{
					handler(first, second);
				}
				catch (Exception ex)
				{
					LogHandlerError(handler, ex);
				}
			}
		}

		private static void LogHandlerError(Delegate handler, Exception ex)
		{
			TimbnFrameworkPlugin<TimbnCorePlugin>.Logger.LogError((object)string.Format("{0}|{1}.{2} threw: {3}", "TimbnGameEvents", handler.Method.DeclaringType?.FullName, handler.Method.Name, ex));
		}
	}
	public sealed class TimbnPluginEvents
	{
		private readonly TimbnFrameworkPlugin _owner;

		private readonly List<Func<IDisposable>> _perSave = new List<Func<IDisposable>>();

		internal TimbnPluginEvents(TimbnFrameworkPlugin owner)
		{
			_owner = owner;
		}

		public IDisposable GameStarted(Action handler)
		{
			return Own(TimbnGameEvents.GameStarted(handler));
		}

		public IDisposable GoToMainMenu(Action handler)
		{
			return Own(TimbnGameEvents.GoToMainMenu(handler));
		}

		public IDisposable GamePaused(Action handler)
		{
			return Own(TimbnGameEvents.GamePaused(handler));
		}

		public IDisposable GameUnpaused(Action handler)
		{
			return Own(TimbnGameEvents.GameUnpaused(handler));
		}

		public IDisposable NewDayStarted(Action<int> handler)
		{
			return Own(TimbnGameEvents.NewDayStarted(handler));
		}

		public IDisposable NewWeekdayStarted(Action<int> handler)
		{
			return Own(TimbnGameEvents.NewWeekdayStarted(handler));
		}

		public IDisposable TimeOfDayChanged(Action<float, bool> handler)
		{
			return Own(TimbnGameEvents.TimeOfDayChanged(handler));
		}

		public IDisposable SaveWriteStarted(Action handler)
		{
			return Own(TimbnGameEvents.SaveWriteStarted(handler));
		}

		public IDisposable SaveWriteEnded(Action handler)
		{
			return Own(TimbnGameEvents.SaveWriteEnded(handler));
		}

		public IDisposable SaveLoadingStarted(Action handler)
		{
			return Own(TimbnGameEvents.SaveLoadingStarted(handler));
		}

		public IDisposable SaveLoadingEnded(Action handler)
		{
			return Own(TimbnGameEvents.SaveLoadingEnded(handler));
		}

		public IDisposable PlayerTeleported(Action handler)
		{
			return Own(TimbnGameEvents.PlayerTeleported(handler));
		}

		public IDisposable WorldObjectSpawned(Action<Wgo> handler)
		{
			return Own(TimbnGameEvents.WorldObjectSpawned(handler));
		}

		public IDisposable WorldObjectDestroyed(Action<Wgo> handler)
		{
			return Own(TimbnGameEvents.WorldObjectDestroyed(handler));
		}

		public IDisposable QuestStarted(Action<QuestData> handler)
		{
			return PerSave(() => TimbnGameEvents.QuestStarted(handler));
		}

		public IDisposable QuestCompleted(Action<QuestData> handler)
		{
			return PerSave(() => TimbnGameEvents.QuestCompleted(handler));
		}

		public IDisposable QuestCanceled(Action<QuestData> handler)
		{
			return PerSave(() => TimbnGameEvents.QuestCanceled(handler));
		}

		public IDisposable PlayerResourceChanged(Action handler)
		{
			return PerSave(() => TimbnGameEvents.PlayerResourceChanged(handler));
		}

		public IDisposable PlayerDropCollected(Action<List<Item>> handler)
		{
			return PerSave(() => TimbnGameEvents.PlayerDropCollected(handler));
		}

		public IDisposable PlayerItemUsed(Action<Item> handler)
		{
			return PerSave(() => TimbnGameEvents.PlayerItemUsed(handler));
		}

		public IDisposable PlayerItemsAdded(Action<List<Item>> handler)
		{
			return PerSave(() => TimbnGameEvents.PlayerItemsAdded(handler));
		}

		public IDisposable PlayerItemsRemoved(Action<List<Item>> handler)
		{
			return PerSave(() => TimbnGameEvents.PlayerItemsRemoved(handler));
		}

		public IDisposable StaticEvent(Action handler, Action<Action> add, Action<Action> remove)
		{
			return Own(TimbnGameEvents.On(handler, add, remove));
		}

		public IDisposable StaticEvent<T>(Action<T> handler, Action<Action<T>> add, Action<Action<T>> remove)
		{
			return Own(TimbnGameEvents.On(handler, add, remove));
		}

		public IDisposable StaticEvent<T1, T2>(Action<T1, T2> handler, Action<Action<T1, T2>> add, Action<Action<T1, T2>> remove)
		{
			return Own(TimbnGameEvents.On(handler, add, remove));
		}

		internal void AttachToSave()
		{
			foreach (Func<IDisposable> item in _perSave.ToList())
			{
				item();
			}
		}

		private IDisposable Own(IDisposable subscription)
		{
			return _owner.Subscriptions.Add(subscription);
		}

		private IDisposable PerSave(Func<IDisposable> subscribe)
		{
			IDisposable current = null;
			Func<IDisposable> entry = attach;
			_perSave.Add(entry);
			if (TimbnGame.IsInGame)
			{
				attach();
			}
			return _owner.Subscriptions.Add(new TimbnUndo(delegate
			{
				_perSave.Remove(entry);
				current?.Dispose();
			}));
			IDisposable attach()
			{
				return current = _owner.SessionSubscriptions.Add(subscribe());
			}
		}
	}
	public static class TimbnInput
	{
		public static bool CanUseHotkeys
		{
			get
			{
				if (TimbnGame.IsInGame && LazyInput.IsInitialized && LazyInput.IsInputActive())
				{
					return (Object)(object)LazyWindowsStackController.ActiveWindow == (Object)null;
				}
				return false;
			}
		}

		public static bool PlayerHasControl
		{
			get
			{
				if (CanUseHotkeys && (Object)(object)MainGame.PlayerController != (Object)null)
				{
					return MainGame.PlayerController.IsControlsEnabled;
				}
				return false;
			}
		}

		public static bool IsDownInGame(this KeyboardShortcut shortcut)
		{
			if (CanUseHotkeys)
			{
				return ((KeyboardShortcut)(ref shortcut)).IsDown();
			}
			return false;
		}

		public static bool IsDownWithControl(this KeyboardShortcut shortcut)
		{
			if (PlayerHasControl)
			{
				return ((KeyboardShortcut)(ref shortcut)).IsDown();
			}
			return false;
		}
	}
	internal static class TimbnMainMenu
	{
		private sealed class Line
		{
			public string Label { get; }

			public string Value { get; }

			public TextMeshProUGUI? Text { get; set; }

			public Line(string label, string value)
			{
				Label = label;
				Value = value;
			}
		}

		private sealed class Popup
		{
			public string Header { get; }

			public string Text { get; }

			public Popup(string header, string text)
			{
				Header = header;
				Text = text;
			}
		}

		private static readonly FieldRef<MainGame, UIMainMenuInfoPanel> _infoPanel = AccessTools.FieldRefAccess<MainGame, UIMainMenuInfoPanel>("uiMainMenuInfoPanel");

		private static readonly FieldRef<UIMainMenuInfoPanel, TextMeshProUGUI> _developerLabel = AccessTools.FieldRefAccess<UIMainMenuInfoPanel, TextMeshProUGUI>("developerNameLabel");

		private static readonly FieldRef<UIMainMenuInfoPanel, TextMeshProUGUI> _publisherLabel = AccessTools.FieldRefAccess<UIMainMenuInfoPanel, TextMeshProUGUI>("publisherNameLabel");

		private static readonly FieldRef<UIMainMenuInfoPanel, TextStyle> _valueStyle = AccessTools.FieldRefAccess<UIMainMenuInfoPanel, TextStyle>("rightSideStyle");

		private static readonly List<Line> _lines = new List<Line>();

		private static readonly List<Popup> _popups = new List<Popup>();

		private static UIMainMenuWindow? _menu;

		internal static IDisposable AddLine(string label, string value)
		{
			Line line = new Line(label, value);
			_lines.Add(line);
			DrawLines();
			return new TimbnUndo(delegate
			{
				_lines.Remove(line);
				if ((Object)(object)line.Text != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)line.Text).gameObject);
				}
				DrawLines();
			});
		}

		internal static IDisposable AddPopup(string header, string text)
		{
			Popup popup = new Popup(header, text);
			_popups.Add(popup);
			return new TimbnUndo(delegate
			{
				_popups.Remove(popup);
			});
		}

		internal static void OnMenuOpened(UIMainMenuWindow menu)
		{
			_menu = menu;
		}

		internal static void DrawLines()
		{
			//IL_004b: 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_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//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)
			if ((Object)(object)MainGame.Instance == (Object)null)
			{
				return;
			}
			UIMainMenuInfoPanel val = _infoPanel.Invoke(MainGame.Instance);
			if (val == null || (Object)(object)val == (Object)null)
			{
				return;
			}
			TextMeshProUGUI val2 = _developerLabel.Invoke(val);
			TextMeshProUGUI val3 = _publisherLabel.Invoke(val);
			Vector2 val4 = ((TMP_Text)val2).rectTransform.anchoredPosition - ((TMP_Text)val3).rectTransform.anchoredPosition;
			for (int i = 0; i < _lines.Count; i++)
			{
				Line line = _lines[i];
				if ((Object)(object)line.Text == (Object)null)
				{
					line.Text = Object.Instantiate<TextMeshProUGUI>(val3, ((TMP_Text)val3).transform.parent);
					((Object)line.Text).name = "TimbnLine";
				}
				((TMP_Text)line.Text).rectTransform.anchoredPosition = ((TMP_Text)val2).rectTransform.anchoredPosition + val4 * (float)(_lines.Count - i + 1);
				((TMP_Text)line.Text).text = line.Label + _valueStyle.Invoke(val).ApplyStyleToString(line.Value, true, true);
				((Component)line.Text).gameObject.SetActive(((Component)val2).gameObject.activeSelf);
			}
		}

		internal static void OnUpdate()
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Expected O, but got Unknown
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Expected O, but got Unknown
			if (_popups.Count != 0 && !((Object)(object)_menu == (Object)null) && ((LazyWindow<LazyWidgetDataBase>)(object)_menu).IsShownAndTop)
			{
				Popup popup = _popups[0];
				_popups.RemoveAt(0);
				UIDialogWindow window = LazyUI.GetWindow<UIDialogWindow>();
				ButtonData val = new ButtonData((Action)((LazyWindow<UIDialogWindowData>)(object)window).Close, LLBase.L("btn_ok"), (Func<bool>)null, true, GameKey.Select, "");
				((LazyWindow<UIDialogWindowData>)(object)window).Open(new UIDialogWindowData(popup.Header, popup.Text, val));
			}
		}
	}
	public sealed class TimbnPluginMainMenu
	{
		private readonly TimbnFrameworkPlugin _owner;

		internal TimbnPluginMainMenu(TimbnFrameworkPlugin owner)
		{
			_owner = owner;
		}

		public IDisposable AddLine(string label, string value)
		{
			return _owner.Subscriptions.Add(TimbnMainMenu.AddLine(label, value));
		}

		public IDisposable Popup(string header, string text)
		{
			return _owner.Subscriptions.Add(TimbnMainMenu.AddPopup(header, text));
		}
	}
	public abstract class TimbnFrameworkPlugin : BaseUnityPlugin
	{
		private static readonly FieldRef<BaseUnityPlugin, PluginInfo?> _infoField = AccessTools.FieldRefAccess<BaseUnityPlugin, PluginInfo>("<Info>k__BackingField");

		private static int _harmonyInstances;

		private Harmony? _harmony;

		protected BepInPlugin Metadata { get; }

		public TimbnPluginEvents Events { get; }

		public TimbnPluginPotions Potions { get; }

		public TimbnPluginQuests Quests { get; }

		public TimbnPluginDialog Dialog { get; }

		public TimbnPluginText Text { get; }

		public TimbnPluginSprites Sprites { get; }

		public TimbnPluginBalance Balance { get; }

		public TimbnPluginMainMenu MainMenu { get; }

		internal TimbnSubscriptions Subscriptions { get; }

		internal TimbnSubscriptions SessionSubscriptions { get; }

		private protected virtual bool IsEnabled => true;

		protected TimbnFrameworkPlugin()
		{
			Metadata = MetadataHelper.GetMetadata((object)this);
			RepairPluginInfo();
			Subscriptions = new TimbnSubscriptions(((BaseUnityPlugin)this).Logger);
			SessionSubscriptions = new TimbnSubscriptions(((BaseUnityPlugin)this).Logger);
			Events = new TimbnPluginEvents(this);
			Potions = new TimbnPluginPotions(this);
			Quests = new TimbnPluginQuests(this);
			Dialog = new TimbnPluginDialog(this);
			Text = new TimbnPluginText(this);
			Sprites = new TimbnPluginSprites(this);
			Balance = new TimbnPluginBalance(this);
			MainMenu = new TimbnPluginMainMenu(this);
		}

		private void RepairPluginInfo()
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Expected O, but got Unknown
			if (((BaseUnityPlugin)this).Info == null)
			{
				PluginInfo val = new PluginInfo();
				Traverse obj = Traverse.Create((object)val);
				obj.Property<BepInPlugin>("Metadata", (object[])null).Value = Metadata;
				obj.Property<BaseUnityPlugin>("Instance", (object[])null).Value = (BaseUnityPlugin)(object)this;
				obj.Property<IEnumerable<BepInDependency>>("Dependencies", (object[])null).Value = MetadataHelper.GetDependencies(((object)this).GetType());
				obj.Property<IEnumerable<BepInProcess>>("Processes", (object[])null).Value = MetadataHelper.GetAttributes<BepInProcess>(((object)this).GetType());
				obj.Property<IEnumerable<BepInIncompatibility>>("Incompatibilities", (object[])null).Value = MetadataHelper.GetAttributes<BepInIncompatibility>(((object)this).GetType());
				obj.Property<string>("Location", (object[])null).Value = ((object)this).GetType().Assembly.Location;
				Chainloader.PluginInfos[Metadata.GUID] = val;
				_infoField.Invoke((BaseUnityPlugin)(object)this) = val;
			}
		}

		private void Awake()
		{
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Expected O, but got Unknown
			InitConfig();
			if (!IsEnabled)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Plugin " + Metadata.GUID + " is disabled in config (its own or Core's Enabled toggle); skipping."));
				return;
			}
			if (!(this is TimbnCorePlugin) && TimbnCorePlugin.Instance == null)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Plugin " + Metadata.GUID + " not started because Timbn Core did not start. See Core's log lines above."));
				return;
			}
			string text = $"{Metadata.GUID}.{++_harmonyInstances}";
			Harmony harmony = new Harmony(text);
			if (TryPatchAll(harmony))
			{
				_harmony = harmony;
				Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
				((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
				Subscriptions.Add(TimbnGameEvents.GameStarted(Events.AttachToSave));
				Subscriptions.Add(TimbnGameEvents.GoToMainMenu(SessionSubscriptions.Dispose));
				Subscriptions.Add(TimbnMainMenu.AddLine(Metadata.Name + ": ", Metadata.Version.ToString()));
				((BaseUnityPlugin)this).Logger.LogMessage((object)$"Plugin {Metadata.GUID} v{Metadata.Version} loaded, patches under '{text}'.");
				OnAwake();
			}
		}

		private bool TryPatchAll(Harmony harmony)
		{
			List<string> list = new List<string>();
			Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(((object)this).GetType().Assembly);
			foreach (Type type in typesFromAssembly)
			{
				try
				{
					harmony.CreateClassProcessor(type).Patch();
				}
				catch (Exception ex)
				{
					list.Add(type.FullName + ": " + (ex.InnerException ?? ex).Message);
				}
			}
			if (list.Count == 0)
			{
				return true;
			}
			harmony.UnpatchSelf();
			((BaseUnityPlugin)this).Logger.LogError((object)($"Plugin {Metadata.GUID} v{Metadata.Version} not started. {list.Count} patch class(es) failed on game build {Application.version}, " + "most likely a renamed or removed target. Nothing was left patched." + Environment.NewLine + "  " + string.Join(Environment.NewLine + "  ", list)));
			TimbnLoadErrors.AddStartFailure($"{Metadata.Name} {Metadata.Version}");
			return false;
		}

		private void Update()
		{
			if (_harmony != null && IsEnabled)
			{
				OnUpdate();
			}
		}

		private void OnDestroy()
		{
			if (_harmony != null)
			{
				try
				{
					OnDestroyed();
				}
				catch (Exception arg)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)string.Format("{0} threw: {1}", "OnDestroyed", arg));
				}
				SessionSubscriptions.Dispose();
				Subscriptions.Dispose();
				_harmony.UnpatchSelf();
				_harmony = null;
				((BaseUnityPlugin)this).Logger.LogMessage((object)("Plugin " + Metadata.GUID + " unloaded, subscriptions and patches removed."));
			}
		}

		private protected virtual void InitConfig()
		{
		}

		protected virtual void OnAwake()
		{
		}

		protected virtual void OnUpdate()
		{
		}

		protected virtual void OnDestroyed()
		{
		}
	}
	public abstract class TimbnFrameworkPlugin<T> : TimbnFrameworkPlugin where T : TimbnFrameworkPlugin<T>
	{
		public static ManualLogSource Logger { get; private set; }

		public static ConfigEntry<bool> Enabled { get; private set; }

		private protected sealed override bool IsEnabled
		{
			get
			{
				if (Enabled.Value)
				{
					return TimbnFrameworkPlugin<TimbnCorePlugin>.Enabled.Value;
				}
				return false;
			}
		}

		protected TimbnFrameworkPlugin()
		{
			Logger = ((BaseUnityPlugin)this).Logger;
		}

		private protected sealed override void InitConfig()
		{
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master toggle for " + base.Metadata.Name + ".");
			BindConfig(((BaseUnityPlugin)this).Config);
		}

		protected virtual void BindConfig(ConfigFile config)
		{
		}
	}
	public static class TimbnGame
	{
		private static bool _started;

		public static bool IsInGame
		{
			get
			{
				//IL_0019: Unknown result type (might be due to invalid IL or missing references)
				//IL_001f: Invalid comparison between Unknown and I4
				if (_started && (Object)(object)MainGame.Instance != (Object)null)
				{
					return (int)MainGame.Instance.gameState == 1;
				}
				return false;
			}
		}

		internal static void OnGameStarted()
		{
			_started = true;
		}

		internal static void OnLeftGame()
		{
			_started = false;
		}
	}
	internal static class TimbnLoadErrors
	{
		private const int _maxLines = 6;

		private static readonly Regex _missing = new Regex("^Could not load \\[(.+)\\] because it has missing dependencies: (.+)$");

		private static readonly Regex _skipped = new Regex("^Skipping \\[(.+)\\] because it has a dependency that was not loaded");

		private static readonly Regex _incompatible = new Regex("^Could not load \\[(.+)\\] because it is incompatible with: (.+)$");

		private static readonly Regex _wrongBepInEx = new Regex("^Plugin \\[.+\\] targets a wrong version of BepInEx");

		private static readonly Regex _dependency = new Regex("^(\\S+)(?: \\(v(.+) or newer\\))?$");

		private static readonly List<string> _startFailures = new List<string>();

		internal static void AddStartFailure(string plugin)
		{
			_startFailures.Add(plugin);
		}

		internal static string? Describe()
		{
			List<string> list = new List<string>();
			foreach (string dependencyError in Chainloader.DependencyErrors)
			{
				string text = Describe(dependencyError);
				if (text != null)
				{
					list.Add(text);
				}
			}
			foreach (string startFailure in _startFailures)
			{
				list.Add(startFailure + " is off because a game update changed something it needs.");
			}
			if (list.Count == 0)
			{
				return null;
			}
			List<string> list2 = list.Take(6).ToList();
			if (list.Count > 6)
			{
				list2.Add($"And {list.Count - 6} more.");
			}
			list2.Add("BepInEx/LogOutput.log has the details.");
			return string.Join("\n\n", list2);
		}

		private static string? Describe(string error)
		{
			if (_wrongBepInEx.IsMatch(error))
			{
				return null;
			}
			Match match = _missing.Match(error);
			if (match.Success)
			{
				IEnumerable<string> values = match.Groups[2].Value.Split(new string[1] { ", " }, StringSplitOptions.RemoveEmptyEntries).Select(DescribeDependency);
				return match.Groups[1].Value + " needs " + string.Join(" and ", values) + ".";
			}
			match = _skipped.Match(error);
			if (match.Success)
			{
				return match.Groups[1].Value + " is off because a mod it needs did not load.";
			}
			match = _incompatible.Match(error);
			if (match.Success)
			{
				IEnumerable<string> values2 = match.Groups[2].Value.Split(new string[1] { ", " }, StringSplitOptions.RemoveEmptyEntries).Select(NameOf);
				return match.Groups[1].Value + " can't run alongside " + string.Join(" and ", values2) + ".";
			}
			return error;
		}

		private static string DescribeDependency(string entry)
		{
			Match match = _dependency.Match(entry);
			if (!match.Success)
			{
				return entry;
			}
			string value = match.Groups[1].Value;
			string text = (match.Groups[2].Success ? match.Groups[2].Value : null);
			if (Chainloader.PluginInfos.TryGetValue(value, out var value2))
			{
				Version version = value2.Metadata.Version;
				if (text != null)
				{
					return $"{value2.Metadata.Name} {text} or newer (you have {version})";
				}
				return value2.Metadata.Name;
			}
			if (text != null)
			{
				return value + " " + text + " or newer, which is missing";
			}
			return value + ", which is missing";
		}

		private static string NameOf(string guid)
		{
			if (!Chainloader.PluginInfos.TryGetValue(guid, out var value))
			{
				return guid;
			}
			return value.Metadata.Name;
		}
	}
	internal sealed class TimbnSubscriptions : IDisposable
	{
		private readonly List<IDisposable> _items = new List<IDisposable>();

		private readonly ManualLogSource _logger;

		public int Count => _items.Count;

		internal TimbnSubscriptions(ManualLogSource logger)
		{
			_logger = logger;
		}

		public T Add<T>(T subscription) where T : IDisposable
		{
			_items.Add(subscription);
			return subscription;
		}

		public IDisposable Add(Action undo)
		{
			return Add(new TimbnUndo(undo));
		}

		public void Dispose()
		{
			for (int num = _items.Count - 1; num >= 0; num--)
			{
				try
				{
					_items[num].Dispose();
				}
				catch (Exception arg)
				{
					_logger.LogError((object)string.Format("{0}|An undo threw: {1}", "TimbnSubscriptions", arg));
				}
			}
			_items.Clear();
		}
	}
	internal sealed class TimbnUndo : IDisposable
	{
		private Action? _undo;

		public TimbnUndo(Action undo)
		{
			_undo = undo;
		}

		public void Dispose()
		{
			Action? undo = _undo;
			_undo = null;
			undo?.Invoke();
		}
	}
	public sealed class TimbnPluginPotions
	{
		private readonly TimbnFrameworkPlugin _owner;

		internal TimbnPluginPotions(TimbnFrameworkPlugin owner)
		{
			_owner = owner;
		}

		public IDisposable Register(TimbnPotion potion)
		{
			return _owner.Subscriptions.Add(TimbnPotions.Register(potion));
		}
	}
	public sealed class TimbnPotion
	{
		public string Id { get; set; } = "";

		public string Name { get; set; } = "";

		public string Description { get; set; } = "";

		public string TemplateItemId { get; set; } = "third_eye_salt";

		public string IconId { get; set; } = "";

		public int Price { get; set; } = 100;

		public Vector3Int Runes { get; set; }

		public AlchemyFormulaTab Tab { get; set; } = (AlchemyFormulaTab)3;

		public bool HiddenAtStart { get; set; }

		public TimbnPotionBuff Buff { get; set; } = new TimbnPotionBuff();

		public string BuffId => Id + "_buff";
	}
	public sealed class TimbnPotionBuff
	{
		public string Name { get; set; } = "";

		public string Description { get; set; } = "";

		public Func<float> Seconds { get; set; } = () => 60f;

		public Func<string> GlowColor { get; set; } = () => "ltyellow";

		public string IconId { get; set; } = "b_speed";

		public List<string> OnAddExpressions { get; set; } = new List<string>();

		public List<string> OnRemoveExpressions { get; set; } = new List<string>();

		public Action? OnStart { get; set; }

		public Action? WhileActive { get; set; }

		public Action? OnEnd { get; set; }
	}
	internal static class TimbnPotionBalance
	{
		private const string _potionDrinkerInspiration = "AddInspiration(\"insp_potion_drinker\", 1)";

		private static readonly Action<GameBalanceBase> _createIdsCache = AccessTools.MethodDelegate<Action<GameBalanceBase>>(AccessTools.Method(typeof(GameBalanceBase), "CreateIDsCache", (Type[])null, (Type[])null), (object)null, true);

		private static readonly FieldRef<ItemDef, string> _itemCustomIcon = AccessTools.FieldRefAccess<ItemDef, string>("customIcon");

		private static readonly FieldRef<PerkDef, string> _perkCustomIcon = AccessTools.FieldRefAccess<PerkDef, string>("customIcon");

		private static readonly string[] _alchemyStations = new string[2] { "alchemy_mix", "alchemy_mix_2" };

		private static readonly List<ItemDef> _items = new List<ItemDef>();

		private static readonly List<PerkDef> _perks = new List<PerkDef>();

		private static readonly List<AlchemyFormulaDef> _formulas = new List<AlchemyFormulaDef>();

		private static readonly List<AlchemyMixSourceDef> _mixes = new List<AlchemyMixSourceDef>();

		private static GameBalance? _balance;

		public static void Apply(IReadOnlyList<TimbnPotion> potions)
		{
			Remove();
			GameBalance loaded = TimbnBalance.Loaded;
			if (loaded == null || potions.Count == 0)
			{
				return;
			}
			foreach (TimbnPotion potion in potions)
			{
				if (!TryAdd(loaded, potion, out string reason))
				{
					TimbnFrameworkPlugin<TimbnCorePlugin>.Logger.LogWarning((object)("TimbnPotions|Skipped " + potion.Id + ". " + reason));
				}
			}
			_balance = loaded;
			_createIdsCache((GameBalanceBase)(object)loaded);
			foreach (ItemDef item in _items)
			{
				AddToGroupCache(loaded, item);
			}
			foreach (AlchemyMixSourceDef mix in _mixes)
			{
				loaded.alchemyMixSourcesByIdCache[mix.mixId] = mix;
			}
			LLBase.AddAliases(_mixes.Select((AlchemyMixSourceDef m) => m.mixId).ToList(), _mixes.Select((AlchemyMixSourceDef m) => m.formulaId).ToList());
			TimbnFrameworkPlugin<TimbnCorePlugin>.Logger.LogInfo((object)string.Format("{0}|Added {1} potions with {2} ingredient mixes.", "TimbnPotions", _items.Count, _mixes.Count));
		}

		public static void RefreshBuffs(IReadOnlyList<TimbnPotion> potions)
		{
			foreach (PerkDef perk in _perks)
			{
				TimbnPotion timbnPotion = potions.FirstOrDefault((TimbnPotion p) => p.BuffId == ((BalanceBaseObject)perk).id);
				if (timbnPotion != null)
				{
					ConfigureBuff(perk, timbnPotion.Buff);
				}
			}
		}

		private static void Remove()
		{
			GameBalance balance = _balance;
			_balance = null;
			if ((Object)(object)balance == (Object)null)
			{
				return;
			}
			foreach (ItemDef item in _items)
			{
				balance.itemDefs.Remove(item);
				foreach (string itemGroupId in item.itemGroupIds)
				{
					if (balance.groupItemsCache.TryGetValue(itemGroupId, out var value))
					{
						value.Remove(item);
					}
				}
			}
			foreach (PerkDef perk in _perks)
			{
				balance.perkDefs.Remove(perk);
			}
			foreach (AlchemyFormulaDef formula in _formulas)
			{
				balance.alchemyFormulaDefs.Remove(formula);
			}
			foreach (AlchemyMixSourceDef mix in _mixes)
			{
				balance.alchemyMixSourceDefs.Remove(mix);
				balance.alchemyMixSourcesByIdCache.Remove(mix.mixId);
				balance.alchemyMixDefsCache.Remove(mix.mixId);
				balance.runtimeCraftDefsCacheAlchemy.Remove(mix.mixId);
			}
			_createIdsCache((GameBalanceBase)(object)balance);
			_items.Clear();
			_perks.Clear();
			_formulas.Clear();
			_mixes.Clear();
		}

		private static bool TryAdd(GameBalance balance, TimbnPotion potion, out string reason)
		{
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ab: Expected O, but got Unknown
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Expected O, but got Unknown
			//IL_0215: Unknown result type (might be due to invalid IL or missing references)
			//IL_021a: Unknown result type (might be due to invalid IL or missing references)
			//IL_022b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0232: Unknown result type (might be due to invalid IL or missing references)
			//IL_0237: Unknown result type (might be due to invalid IL or missing references)
			//IL_0245: Unknown result type (might be due to invalid IL or missing references)
			//IL_024c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0251: Unknown result type (might be due to invalid IL or missing references)
			//IL_025f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0266: Unknown result type (might be due to invalid IL or missing references)
			//IL_026b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0279: Unknown result type (might be due to invalid IL or missing references)
			//IL_0280: Unknown result type (might be due to invalid IL or missing references)
			//IL_0285: Unknown result type (might be due to invalid IL or missing references)
			//IL_028a: Unknown result type (might be due to invalid IL or missing references)
			//IL_029b: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ca: Expected O, but got Unknown
			ItemDef val = balance.itemDefs.Find((ItemDef i) => ((BalanceBaseObject)i).id == potion.TemplateItemId);
			if (val == null)
			{
				reason = "Template item " + potion.TemplateItemId + " does not exist.";
				return false;
			}
			if (balance.itemDefs.Exists((ItemDef i) => ((BalanceBaseObject)i).id == potion.Id))
			{
				reason = "An item with that id already exists.";
				return false;
			}
			AlchemyFormulaDef val2 = balance.alchemyFormulaDefs.Find((AlchemyFormulaDef f) => f.GetRunesAsVector3Int() == potion.Runes);
			if (val2 != null)
			{
				reason = $"Runes {potion.Runes} already belong to {((BalanceBaseObject)val2).id}, so no mix would brew it.";
				return false;
			}
			PerkDef val3 = balance.perkDefs.Find((PerkDef p) => p.worldFxPrefabId?.StartsWith("buff_char_") ?? false);
			if (val3 == null)
			{
				reason = "No existing buff with a glow to copy.";
				return false;
			}
			PerkDef val4 = Copy<PerkDef>(val3);
			((BalanceBaseObject)val4).id = potion.BuffId;
			ConfigureBuff(val4, potion.Buff);
			balance.perkDefs.Add(val4);
			_perks.Add(val4);
			ItemDef val5 = Copy<ItemDef>(val);
			((BalanceBaseObject)val5).id = potion.Id;
			if (!string.IsNullOrEmpty(potion.IconId))
			{
				val5.iconId = potion.IconId;
				_itemCustomIcon.Invoke(val5) = potion.IconId;
			}
			val5.basePrice = potion.Price;
			val5.canBeUsedInAlchemy = false;
			val5.onUseExpressions = new List<LazyExpression>(2)
			{
				new LazyExpression("AddInspiration(\"insp_potion_drinker\", 1)"),
				new LazyExpression("AddPerk(\"" + ((BalanceBaseObject)val4).id + "\")")
			};
			val5.sortOrder = balance.itemDefs.Count;
			balance.itemDefs.Add(val5);
			_items.Add(val5);
			AlchemyFormulaDef val6 = balance.alchemyFormulaDefs.Find((AlchemyFormulaDef f) => ((BalanceBaseObject)f).id == potion.TemplateItemId);
			AlchemyFormulaDef val7 = new AlchemyFormulaDef
			{
				id = potion.Id
			};
			Vector3Int runes = potion.Runes;
			val7.runesRed = ((Vector3Int)(ref runes)).x;
			runes = potion.Runes;
			val7.runesGreen = ((Vector3Int)(ref runes)).y;
			runes = potion.Runes;
			val7.runesBlue = ((Vector3Int)(ref runes)).z;
			val7.tab = potion.Tab;
			val7.hiddenAtStart = potion.HiddenAtStart;
			val7.craftsIn = _alchemyStations.ToList();
			val7.onCraftEndExpressions = ((val6 != null) ? Copy<AlchemyFormulaDef>(val6).onCraftEndExpressions : new List<LazyExpression>());
			AlchemyFormulaDef val8 = val7;
			balance.alchemyFormulaDefs.Add(val8);
			_formulas.Add(val8);
			AddMixes(balance, val8);
			reason = "";
			return true;
		}

		private static void ConfigureBuff(PerkDef perk, TimbnPotionBuff buff)
		{
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Expected O, but got Unknown
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Expected O, but got Unknown
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Expected O, but got Unknown
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Expected O, but got Unknown
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Expected O, but got Unknown
			string text = buff.GlowColor();
			_perkCustomIcon.Invoke(perk) = buff.IconId;
			perk.duration = buff.Seconds();
			perk.worldFxPrefabId = "buff_char_" + text;
			perk.hudFxPrefabId = "buff_hud_" + text;
			perk.onAddExpressions = ((IEnumerable<string>)buff.OnAddExpressions).Select((Func<string, LazyExpression>)((string e) => new LazyExpression(e))).ToList();
			perk.onRemoveExpressions = ((IEnumerable<string>)buff.OnRemoveExpressions).Select((Func<string, LazyExpression>)((string e) => new LazyExpression(e))).ToList();
			perk.setGameResOnAdd = new GameRes();
			perk.addGameResOnAdd = new GameRes();
			perk.setGameResOnRemove = new GameRes();
			perk.addGameResOnRemove = new GameRes();
			perk.addGameResPerTick = new GameRes();
		}

		private static void AddMixes(GameBalance balance, AlchemyFormulaDef formula)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			Vector3Int target = formula.GetRunesAsVector3Int();
			List<ItemDef> list = balance.itemDefs.Where((ItemDef i) => i.canBeUsedInAlchemy && i.GetRunesAsVector3Int() != Vector3Int.zero).ToList();
			List<CraftDef> boosts = balance.craftDefs.Where((CraftDef c) => ((BalanceBaseObject)c).id.EndsWith("_boost")).ToList();
			List<Vector3Int> list2 = list.Select((ItemDef i) => i.GetRunesAsVector3Int()).ToList();
			List<Vector3Int> boostRunes = boosts.Select((CraftDef b) => b.GetBoostRunesAsVector3Int()).ToList();
			for (int num = 0; num < list.Count; num++)
			{
				Consider(list2[num], (ItemDef[])(object)new ItemDef[1] { list[num] });
				for (int num2 = 0; num2 < list.Count; num2++)
				{
					Vector3Int val = list2[num] + list2[num2];
					Consider(val, (ItemDef[])(object)new ItemDef[2]
					{
						list[num],
						list[num2]
					});
					for (int num3 = 0; num3 < list.Count; num3++)
					{
						Consider(val + list2[num3], (ItemDef[])(object)new ItemDef[3]
						{
							list[num],
							list[num2],
							list[num3]
						});
					}
				}
			}
			void Consider(Vector3Int sum, params ItemDef[] parts)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//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_002d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0032: Unknown result type (might be due to invalid IL or missing references)
				//IL_0038: Unknown result type (might be due to invalid IL or missing references)
				if (sum == target)
				{
					TryAddMix(balance, formula, parts, null);
				}
				for (int i = 0; i < boosts.Count; i++)
				{
					if (sum + boostRunes[i] == target)
					{
						TryAddMix(balance, formula, parts, boosts[i]);
					}
				}
			}
		}

		private static void TryAddMix(GameBalance balance, AlchemyFormulaDef formula, ItemDef[] parts, CraftDef? boost)
		{
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Expected O, but got Unknown
			string mixId = AlchemyMixDef.MixId(parts.Select((ItemDef p) => ((BalanceBaseObject)p).id).ToArray(), boost);
			if (!balance.alchemyMixSourcesByIdCache.ContainsKey(mixId) && !_mixes.Exists((AlchemyMixSourceDef m) => m.mixId == mixId))
			{
				AlchemyMixSourceDef item = new AlchemyMixSourceDef
				{
					mixId = mixId,
					formulaId = ((BalanceBaseObject)formula).id,
					ingredient1 = ((parts.Length != 0) ? ((BalanceBaseObject)parts[0]).id : ""),
					ingredient2 = ((parts.Length > 1) ? ((BalanceBaseObject)parts[1]).id : ""),
					ingredient3 = ((parts.Length > 2) ? ((BalanceBaseObject)parts[2]).id : "")
				};
				balance.alchemyMixSourceDefs.Add(item);
				_mixes.Add(item);
			}
		}

		private static void AddToGroupCache(GameBalance balance, ItemDef item)
		{
			foreach (string itemGroupId in item.itemGroupIds)
			{
				if (!balance.groupItemsCache.TryGetValue(itemGroupId, out var value))
				{
					value = (balance.groupItemsCache[itemGroupId] = new List<ItemDef>());
				}
				if (!value.Contains(item))
				{
					value.Add(item);
				}
			}
		}

		private static T Copy<T>(T source)
		{
			return JsonUtility.FromJson<T>(JsonUtility.ToJson((object)source));
		}
	}
	public static class TimbnPotions
	{
		private static readonly List<TimbnPotion> _potions = new List<TimbnPotion>();

		private static readonly List<TimbnPotion> _active = new List<TimbnPotion>();

		internal static IDisposable Register(TimbnPotion potion)
		{
			TimbnPotion timbnPotion = _potions.Find((TimbnPotion p) => p.Id == potion.Id);
			if (timbnPotion != null)
			{
				Unregister(timbnPotion);
			}
			_potions.Add(potion);
			IDisposable text = TimbnLocale.Add(new Dictionary<string, string>
			{
				[potion.Id] = potion.Name,
				[potion.Id + "_d"] = potion.Description,
				[potion.BuffId] = potion.Buff.Name,
				[potion.BuffId + "_d"] = potion.Buff.Description
			});
			TimbnPotionBalance.Apply(_potions);
			if (TimbnGame.IsInGame)
			{
				GameSave gameSave = MainGame.Instance.GameSave;
				if (gameSave != null)
				{
					PerkSystemData perkSystemData = gameSave.perkSystemData;
					if (((perkSystemData != null) ? new bool?(perkSystemData.HasPerk(potion.BuffId)) : ((bool?)null)) == true)
					{
						Start(potion);
					}
				}
			}
			return new TimbnUndo(delegate
			{
				Unregister(potion);
				text.Dispose();
			});
		}

		internal static void OnBalanceLoaded()
		{
			TimbnPotionBalance.Apply(_potions);
		}

		internal static void OnGameStarted(TimbnSubscriptions session)
		{
			TimbnPotionBalance.RefreshBuffs(_potions);
			PerkSystemData perks = MainGame.Instance.GameSave.perkSystemData;
			session.Add(TimbnGameEvents.On(OnPerkAdded, delegate(Action<PerkData> h)
			{
				perks.OnPerkAdded += h;
			}, delegate(Action<PerkData> h)
			{
				perks.OnPerkAdded -= h;
			}));
			session.Add(TimbnGameEvents.On(OnPerkRemoved, delegate(Action<PerkData> h)
			{
				perks.OnPerkRemoved += h;
			}, delegate(Action<PerkData> h)
			{
				perks.OnPerkRemoved -= h;
			}));
			session.Add(EndAll);
			foreach (PerkData activePerk in perks.activePerks)
			{
				OnPerkAdded(activePerk);
			}
		}

		internal static void OnUpdate()
		{
			for (int i = 0; i < _active.Count; i++)
			{
				Run(_active[i], "WhileActive", _active[i].Buff.WhileActive);
			}
		}

		private static void Unregister(TimbnPotion potion)
		{
			if (_potions.Remove(potion))
			{
				End(potion);
				TimbnPotionBalance.Apply(_potions);
			}
		}

		private static void OnPerkAdded(PerkData perk)
		{
			TimbnPotion timbnPotion = _potions.Find((TimbnPotion p) => p.BuffId == ((ObjectLinkedToDefinition<PerkDef>)(object)perk).id);
			if (timbnPotion != null)
			{
				Start(timbnPotion);
			}
		}

		private static void OnPerkRemoved(PerkData perk)
		{
			TimbnPotion timbnPotion = _active.Find((TimbnPotion p) => p.BuffId == ((ObjectLinkedToDefinition<PerkDef>)(object)perk).id);
			if (timbnPotion != null)
			{
				End(timbnPotion);
			}
		}

		private static void Start(TimbnPotion potion)
		{
			if (!_active.Contains(potion))
			{
				_active.Add(potion);
				Run(potion, "OnStart", potion.Buff.OnStart);
			}
		}

		private static void End(TimbnPotion potion)
		{
			if (_active.Remove(potion))
			{
				Run(potion, "OnEnd", potion.Buff.OnEnd);
			}
		}

		private static void EndAll()
		{
			foreach (TimbnPotion item in _active.ToList())
			{
				End(item);
			}
		}

		private static void Run(TimbnPotion potion, string hook, Action? action)
		{
			if (action == null)
			{
				return;
			}
			try
			{
				action();
			}
			catch (Exception ex)
			{
				TimbnFrameworkPlugin<TimbnCorePlugin>.Logger.LogError((object)string.Format("{0}|{1} for {2} threw: {3}", "TimbnPotions", hook, potion.Id, ex));
			}
		}
	}
	public sealed class TimbnPluginQuests
	{
		private readonly TimbnFrameworkPlugin _owner;

		internal TimbnPluginQuests(TimbnFrameworkPlugin owner)
		{
			_owner = owner;
		}

		public IDisposable Add(TimbnQuest quest)
		{
			quest.Validate();
			IDisposable text = TimbnLocale.Add(quest.Texts());
			IDisposable registered = TimbnQuests.Register(quest.ToDefinition());
			IDisposable talk = TimbnDialog.AddTalk(quest.Npc, quest.Id + "_talk", quest.IsOffered, quest.Script);
			return _owner.Subscriptions.Add(new TimbnUndo(delegate
			{
				talk.Dispose();
				registered.Dispose();
				text.Dispose();
			}));
		}

		public IDisposable Register(QuestDef definition)
		{
			return _owner.Subscriptions.Add(TimbnQuests.Register(definition));
		}
	}
	public enum TimbnQuestCompletion
	{
		Fetch,
		Custom
	}
	public sealed class TimbnQuest
	{
		public string Id { get; set; } = "";

		public string Npc { get; set; } = "";

		public string Icon { get; set; } = "";

		public string Name { get; set; } = "";

		public string Description { get; set; } = "";

		public string CompletedDescription { get; set; } = "";

		public TimbnQuestCompletion Completion { get; set; }

		public ItemCount[] Wants { get; set; } = Array.Empty<ItemCount>();

		public int RewardMoney { get; set; }

		public string After { get; set; } = "";

		public Func<bool>? AvailableWhen { get; set; }

		public string[] Offer { get; set; } = Array.Empty<string>();

		public string Accept { get; set; } = "";

		public string Decline { get; set; } = "";

		public string[] Accepted { get; set; } = Array.Empty<string>();

		public string[] Declined { get; set; } = Array.Empty<string>();

		public string[] Remind { get; set; } = Array.Empty<string>();

		public string Give { get; set; } = "";

		public string Later { get; set; } = "";

		public string[] Thanks { get; set; } = Array.Empty<string>();

		public QuestStatus? Status => TimbnQuests.StatusOf(Id);

		internal string GiveKey => Id + "_give";

		public void Complete()
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Invalid comparison between Unknown and I4
			if ((int)Status.GetValueOrDefault() == 2)
			{
				TimbnQuests.Complete(Id);
				if (RewardMoney > 0)
				{
					MainGame.PlayerData.AddRes("money", (float)RewardMoney);
				}
			}
		}

		internal void Validate()
		{
			if (string.IsNullOrEmpty(Id) || string.IsNullOrEmpty(Npc))
			{
				throw new ArgumentException("A TimbnQuest needs an Id and an Npc.");
			}
			if (string.IsNullOrEmpty(Accept))
			{
				throw new ArgumentException("Quest " + Id + " needs an Accept answer.");
			}
			if (Completion == TimbnQuestCompletion.Fetch && (Wants.Length == 0 || string.IsNullOrEmpty(Give)))
			{
				throw new ArgumentException("Fetch quest " + Id + " needs Wants and a Give answer.");
			}
		}

		internal QuestDef ToDefinition()
		{
			QuestDef val = TimbnQuests.CreateDefinition(Id);
			if (Completion == TimbnQuestCompletion.Fetch)
			{
				val.FinishOnAnswer(GiveKey, Wants).RewardMoney(RewardMoney);
			}
			if (!string.IsNullOrEmpty(After))
			{
				val.After(After);
			}
			val.wgoNpcId = Npc;
			val.iconId = Icon;
			return val;
		}

		internal Dictionary<string, string> Texts()
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>
			{
				[Id] = Name,
				["quest_open_" + Id + "_d"] = Description,
				["quest_closed_" + Id + "_d"] = (string.IsNullOrEmpty(CompletedDescription) ? Description : CompletedDescription),
				[Id + "_accept"] = Accept,
				[Id + "_decline"] = Decline,
				[GiveKey] = Give,
				[Id + "_later"] = Later
			};
			AddLines(dictionary, "offer", Offer);
			AddLines(dictionary, "accepted", Accepted);
			AddLines(dictionary, "declined", Declined);
			AddLines(dictionary, "remind", Remind);
			AddLines(dictionary, "thanks", Thanks);
			return dictionary;
		}

		internal bool IsOffered()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: 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_001a: Invalid comparison between Unknown and I4
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Invalid comparison between Unknown and I4
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Invalid comparison between Unknown and I4
			QuestStatus? status = Status;
			if (status.HasValue)
			{
				QuestStatus valueOrDefault = status.GetValueOrDefault();
				if ((int)valueOrDefault == 2)
				{
					if (Completion != TimbnQuestCompletion.Fetch)
					{
						return Remind.Length != 0;
					}
					return TimbnQuests.CanFinish(Id);
				}
				if (valueOrDefault - 3 > 1)
				{
					if (string.IsNullOrEmpty(After) || (int)TimbnQuests.StatusOf(After).GetValueOrDefault() == 3)
					{
						return AvailableWhen?.Invoke() ?? true;
					}
					return false;
				}
			}
			return false;
		}

		internal void Script(TimbnConversation talk)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Invalid comparison between Unknown and I4
			if ((int)Status.GetValueOrDefault() == 2)
			{
				SayLines(talk, "remind", Remind);
				if (Completion == TimbnQuestCompletion.Fetch)
				{
					talk.Ask(Answers(GiveKey, Id + "_later", Later)).If(GiveKey, delegate(TimbnConversation then)
					{
						SayLines(then, "thanks", Thanks);
					});
				}
				return;
			}
			SayLines(talk, "offer", Offer);
			talk.Ask(Answers(Id + "_accept", Id + "_decline", Decline)).If(Id + "_accept", delegate(TimbnConversation then)
			{
				SayLines(then.Do(delegate
				{
					TimbnQuests.Start(Id);
				}), "accepted", Accepted);
			}).If(Id + "_decline", delegate(TimbnConversation then)
			{
				SayLines(then, "declined", Declined);
			});
		}

		private static string[] Answers(string first, string secondKey, string secondText)
		{
			if (string.IsNullOrEmpty(secondText))
			{
				return new string[1] { first };
			}
			return new string[2] { first, secondKey };
		}

		private void AddLines(Dictionary<string, string> texts, string part, string[] lines)
		{
			for (int i = 0; i < lines.Length; i++)
			{
				texts[$"{Id}_{part}_{i + 1}"] = lines[i];
			}
		}

		private void SayLines(TimbnConversation talk, string part, string[] lines)
		{
			for (int i = 0; i < lines.Length; i++)
			{
				talk.Say($"{Id}_{part}_{i + 1}");
			}
		}
	}
	public static class TimbnQuests
	{
		private static readonly FieldRef<QuestDef, int> _posX = AccessTools.FieldRefAccess<QuestDef, int>("posX");

		private static readonly FieldRef<QuestDef, int> _posY = AccessTools.FieldRefAccess<QuestDef, int>("posY");

		private static readonly FieldRef<ObjectLinkedToDefinition<QuestDef>, string> _cachedId = AccessTools.FieldRefAccess<ObjectLinkedToDefinition<QuestDef>, string>("cachedId");

		private static readonly Action<QuestDef> _linkToParents = AccessTools.MethodDelegate<Action<QuestDef>>(AccessTools.Method(typeof(QuestDef), "InitParentsAndChildren", (Type[])null, (Type[])null), (object)null, true);

		private static readonly Dictionary<string, QuestDef> _quests = new Dictionary<string, QuestDef>();

		private static readonly Dictionary<string, int> _registeredAt = new Dictionary<string, int>();

		private static int _registrations;

		private static readonly Dictionary<string, IDisposable> _stubs = new Dictionary<string, IDisposable>();

		private static readonly Dictionary<QuestDef, int> _gameY = new Dictionary<QuestDef, int>();

		private static readonly ConditionalWeakTable<QuestDef, StrongBox<int>> _moneyRewards = new ConditionalWeakTable<QuestDef, StrongBox<int>>();

		private static QuestSystemData? LoadedQuests
		{
			get
			{
				MainGame instance = MainGame.Instance;
				QuestSystemData val = ((instance == null) ? null : instance.GameSave?.questSystemData);
				if (val?.questCollection?.questsCache != null)
				{
					return val;
				}
				return null;
			}
		}

		public static QuestDef CreateDefinition(string id)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected O, but got Unknown
			//IL_002c: 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_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Expected O, but got Unknown
			//IL_003f: Expected O, but got Unknown
			return new QuestDef
			{
				id = id,
				isHidden = true,
				hasPosInBalance = true,
				startCheck = new QuestCheck
				{
					questId = id
				},
				finishCheck = new QuestFinishCheck
				{
					questId = id
				}
			};
		}

		public static QuestDef After(this QuestDef definition, string parentId)
		{
			definition.parents.Add(parentId);
			return definition;
		}

		public static QuestDef FinishOnAnswer(this QuestDef definition, string answerId, params ItemCount[] price)
		{
			//IL_000a: 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_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Expected O, but got Unknown
			QuestFinishCheck finishCheck = definition.finishCheck;
			((QuestCheck)finishCheck).triggerType = (Type)15;
			((QuestCheck)finishCheck).triggerId = answerId;
			((QuestCheck)finishCheck).hasTrigger = true;
			finishCheck.phrase = answerId;
			foreach (ItemCount itemCount in price)
			{
				finishCheck.phraseReqs.Add(new QuestPhraseRequirement
				{
					requirement = (Requirement)0,
					entity = (Entity)0,
					itemCount = itemCount
				});
			}
			return definition;
		}

		public static ItemCount Item(string itemId, int count)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Expected O, but got Unknown
			return new ItemCount
			{
				itemId = itemId,
				count = count
			};
		}

		public static QuestDef RewardMoney(this QuestDef definition, int amount)
		{
			_moneyRewards.Remove(definition);
			_moneyRewards.Add(definition, new StrongBox<int>(amount));
			return definition;
		}

		internal static int MoneyRewardOf(QuestDef definition)
		{
			if (!_moneyRewards.TryGetValue(definition, out StrongBox<int> value))
			{
				return 0;
			}
			return value.Value;
		}

		internal static IDisposable Register(QuestDef definition)
		{
			FillBlanks(definition);
			if (!_registeredAt.ContainsKey(((BalanceBaseObject)definition).id))
			{
				_registeredAt[((BalanceBaseObject)definition).id] = _registrations++;
			}
			IDisposable balance = TimbnBalance.Add((BalanceBaseObject)(object)definition, delegate(GameBalance b)
			{
				Relayout(b, definition);
				_linkToParents(definition);
			});
			_quests[((BalanceBaseObject)definition).id] = definition;
			if (_stubs.TryGetValue(((BalanceBaseObject)definition).id, out IDisposable value))
			{
				_stubs.Remove(((BalanceBaseObject)definition).id);
				value.Dispose();
			}
			AttachToLoadedSave(definition);
			RelayoutIfLoaded();
			return new TimbnUndo(delegate
			{
				QuestDef value2;
				bool flag = _quests.TryGetValue(((BalanceBaseObject)definition).id, out value2) && value2 != definition;
				if (!flag)
				{
					_quests.Remove(((BalanceBaseObject)definition).id);
				}
				DetachFromLoadedSave(definition, flag);
				Unlink(definition);
				balance.Dispose();
				RelayoutIfLoaded();
			});
		}

		private static void RelayoutIfLoaded()
		{
			GameBalance loaded = TimbnBalance.Loaded;
			if (loaded != null)
			{
				Relayout(loaded, null);
			}
		}

		internal static void OnQuestStarted(QuestData quest)
		{
			if (_quests.ContainsKey(((ObjectLinkedToDefinition<QuestDef>)(object)quest).id))
			{
				RelayoutIfLoaded();
			}
		}

		public static QuestStatus? StatusOf(string id)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			QuestSystemData? loadedQuests = LoadedQuests;
			if (loadedQuests == null || !loadedQuests.questCollection.questsCache.TryGetValue(id, out var value))
			{
				return null;
			}
			return value.status;
		}

		public static void Start(string id)
		{
			QuestSystemData? loadedQuests = LoadedQuests;
			if (loadedQuests != null)
			{
				loadedQuests.StartQuest(id, 0f);
			}
		}

		internal static void Complete(string id)
		{
			QuestSystemData? loadedQuests = LoadedQuests;
			if (loadedQuests != null)
			{
				loadedQuests.CompleteQuest(id, 0f);
			}
		}

		public static bool CanFinish(string id)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Invalid comparison between Unknown and I4
			if ((int)StatusOf(id).GetValueOrDefault() == 2)
			{
				QuestDef dataOrNull = ((GameBalanceBase)GameBalance.Me).GetDataOrNull<QuestDef>(id);
				if (dataOrNull == null)
				{
					return false;
				}
				return dataOrNull.finishCheck.IsReadyToFinish();
			}
			return false;
		}

		private static void FillBlanks(QuestDef definition)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Expected O, but got Unknown
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Expected O, but got Unknown
			QuestDef val = definition;
			if (val.iconId == null)
			{
				val.iconId = string.Empty;
			}
			val = definition;
			if (val.wgoNpcId == null)
			{
				val.wgoNpcId = string.Empty;
			}
			val = definition;
			if (val.repVisualisationRes == null)
			{
				val.repVisualisationRes = new GameRes();
			}
			val = definition;
			if (val.startCheck == null)
			{
				val.startCheck = new QuestCheck
				{
					questId = ((BalanceBaseObject)definition).id
				};
			}
			val = definition;
			if (val.finishCheck == null)
			{
				val.finishCheck = new QuestFinishCheck
				{
					questId = ((BalanceBaseObject)definition).id
				};
			}
			QuestCheck[] array = (QuestCheck[])(object)new QuestCheck[2]
			{
				definition.startCheck,
				(QuestCheck)definition.finishCheck
			};
			foreach (QuestCheck val2 in array)
			{
				if (val2.triggerId == null)
				{
					val2.triggerId = string.Empty;
				}
			}
			QuestFinishCheck finishCheck = definition.finishCheck;
			if (finishCheck.phrase == null)
			{
				finishCheck.phrase = string.Empty;
			}
		}

		private static void Relayout(GameBalance balance, QuestDef? adding, Dictionary<string, QuestData>? saved = null)
		{
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_042f: Unknown result type (might be due to invalid IL or missing references)
			//IL_053d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0465: Unknown result type (might be due to invalid IL or missing references)
			//IL_046a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0475: Unknown result type (might be due to invalid IL or missing references)
			//IL_047a: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0514: Unknown result type (might be due to invalid IL or missing references)
			//IL_0519: Unknown result type (might be due to invalid IL or missing references)
			//IL_0526: Unknown result type (might be due to invalid IL or missing references)
			//IL_052b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0534: Unknown result type (might be due to invalid IL or missing references)
			//IL_0539: Unknown result type (might be due to invalid IL or missing references)
			List<QuestDef> list = _quests.Values.Where((QuestDef q) => ((BalanceBaseObject)q).id != ((BalanceBaseObject)(adding?)).id).ToList();
			if (adding != null)
			{
				list.Add(adding);
			}
			Dictionary<string, QuestDef> oursById = list.ToDictionary((QuestDef q) => ((BalanceBaseObject)q).id);
			List<QuestDef> list2 = balance.questDefs.Where((QuestDef q) => !oursById.ContainsKey(((BalanceBaseObject)q).id)).ToList();
			Vector2Int treePos;
			foreach (QuestDef item3 in list2)
			{
				if (!_gameY.ContainsKey(item3))
				{
					Dictionary<QuestDef, int> gameY = _gameY;
					treePos = item3.TreePos;
					gameY[item3] = ((Vector2Int)(ref treePos)).y;
				}
			}
			Dictionary<string, QuestDef> dictionary = new Dictionary<string, QuestDef>();
			foreach (QuestDef item4 in list2)
			{
				dictionary[((BalanceBaseObject)item4).id] = item4;
			}
			Dictionary<string, QuestData> cache = saved ?? LoadedQuests?.questCollection.questsCache;
			int num = 0;
			Dictionary<QuestDef, List<QuestDef>> dictionary2 = new Dictionary<QuestDef, List<QuestDef>>();
			List<QuestDef> list3 = new List<QuestDef>();
			foreach (QuestDef item5 in list.OrderBy(Order))
			{
				if (item5.parents.Count > 0 && dictionary.TryGetValue(item5.parents[0], out var value))
				{
					LinkToCell(item5, value, list2);
				}
				if (!shown(item5))
				{
					SetPosition(item5, new Vector2Int(0, -1 - num++));
					continue;
				}
				QuestDef val = parentOf(item5);
				if (val == null)
				{
					list3.Add(item5);
					continue;
				}
				if (!dictionary2.TryGetValue(val, out var value2))
				{
					value2 = (dictionary2[val] = new List<QuestDef>());
				}
				value2.Add(item5);
			}
			int width = list2.Where((QuestDef q) => q.hasPosInBalance).Select(delegate(QuestDef q)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				Vector2Int treePos2 = q.TreePos;
				return ((Vector2Int)(ref treePos2)).x;
			}).DefaultIfEmpty(0)
				.Max() + 1;
			HashSet<Vector2Int> hashSet = new HashSet<Vector2Int>();
			List<(QuestDef, QuestDef)> list5 = new List<(QuestDef, QuestDef)>();
			foreach (QuestDef item6 in list3)
			{
				if (item6.parents.Count > 0 && dictionary.TryGetValue(item6.parents[0], out var value3))
				{
					list5.Add((item6, value3));
					continue;
				}
				int width2;
				Dictionary<QuestDef, Vector2Int> shape = Shape(item6, dictionary2, out width2);
				Place(shape, FindSpot(shape, width2, hashSet, width, 0, 0), hashSet);
			}
			int num2 = ((hashSet.Count != 0) ? (hashSet.Max((Vector2Int c) => ((Vector2Int)(ref c)).y) + 2) : 0);
			foreach (QuestDef item7 in list2)
			{
				_posY.Invoke(item7) = _gameY[item7] + num2;
			}
			List<QuestDef> list6 = new List<QuestDef>();
			foreach (var item8 in list5.OrderBy<(QuestDef, QuestDef), int>(delegate((QuestDef Root, QuestDef Anchor) a)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_000b: Unknown result type (might be due to invalid IL or missing references)
				Vector2Int treePos2 = a.Anchor.TreePos;
				return ((Vector2Int)(ref treePos2)).y;
			}))
			{
				QuestDef item = item8.Item1;
				QuestDef item2 = item8.Item2;
				int width3;
				Dictionary<QuestDef, Vector2Int> dictionary3 = Shape(item, dictionary2, out width3);
				int num3 = dictionary3.Values.Max((Vector2Int c) => ((Vector2Int)(ref c)).y) + 1;
				if (!TryNextTo(dictionary3, width3, item2.TreePos, 2, list2, dictionary, hashSet, list6, width, out var origin))
				{
					foreach (QuestDef item9 in list2.Concat(list6))
					{
						treePos = item9.TreePos;
						int y = ((Vector2Int)(ref treePos)).y;
						treePos = item2.TreePos;
						if (y > ((Vector2Int)(ref treePos)).y)
						{
							_posY.Invoke(item9) += num3;
						}
					}
					if (!TryNextTo(dictionary3, width3, item2.TreePos, 1, list2, dictionary, hashSet, list6, width, out origin))
					{
						HashSet<Vector2Int> hashSet2 = BlockedCells(list2, dictionary);
						hashSet2.UnionWith(hashSet);
						hashSet2.UnionWith(list6.Select((QuestDef q) => q.TreePos));
						int shapeWidth = width3;
						treePos = item2.TreePos;
						int startY = ((Vector2Int)(ref treePos)).y + 1;
						treePos = item2.TreePos;
						origin = FindSpot(dictionary3, shapeWidth, hashSet2, width, startY, ((Vector2Int)(ref treePos)).x);
					}
				}
				Place(dictionary3, origin, new HashSet<Vector2Int>());
				list6.AddRange(dictionary3.Keys);
			}
			TimbnBalance.RefreshQuestCaches(balance);
			QuestDef? parentOf(QuestDef q)
			{
				if (q.parents.Count <= 0 || !oursById.TryGetValue(q.parents[0], out var value4) || value4 == q || !shown(value4))
				{
					return null;
				}
				return value4;
			}
			bool shown(QuestDef q)
			{
				if (cache != null && cache.TryGetValue(((BalanceBaseObject)q).id, out QuestData value4))
				{
					return !value4.isHidden;
				}
				return false;
			}
		}

		private static void LinkToCell(QuestDef root, QuestDef anchor, List<QuestDef> game)
		{
			//IL_001a: 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)
			foreach (QuestDef item in game)
			{
				if (item.hasPosInBalance && item.TreePos == anchor.TreePos && !root.parents.Contains(((BalanceBaseObject)item).id))
				{
					root.parents.Add(((BalanceBaseObject)item).id);
				}
			}
		}

		private static bool TryNextTo(Dictionary<QuestDef, Vector2Int> shape, int shapeWidth, Vector2Int parent, int maxRows, List<QuestDef> game, Dictionary<string, QuestDef> gameById, HashSet<Vector2Int> band, List<QuestDef> placed, int width, out Vector2Int origin)
		{
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			HashSet<Vector2Int> hashSet = new HashSet<Vector2Int>(game.Select((QuestDef q) => q.TreePos));
			hashSet.UnionWith(band);
			hashSet.UnionWith(placed.Select((QuestDef q) => q.TreePos));
			HashSet<Vector2Int> blocked = BlockedCells(game, gameById);
			blocked.UnionWith(hashSet);
			for (int num = 1; num <= maxRows && (num <= 1 || !hashSet.Contains(new Vector2Int(((Vector2Int)(ref parent)).x, ((Vector2Int)(ref parent)).y + num - 1))); num++)
			{
				int[] array = new int[3] { 0, 1, -1 };
				foreach (int num3 in array)
				{
					Vector2Int start = new Vector2Int(((Vector2Int)(ref parent)).x + num3, ((Vector2Int)(ref parent)).y + num);
					if (((Vector2Int)(ref start)).x >= 0 && ((Vector2Int)(ref start)).x + shapeWidth <= width && !shape.Values.Any((Vector2Int cell) => blocked.Contains(start + cell)))
					{
						origin = start;
						return true;
					}
				}
			}
			origin = default(Vector2Int);
			return false;
		}

		private static Dictionary<QuestDef, Vector2Int> Shape(QuestDef root, Dictionary<QuestDef, List<QuestDef>> kids, out int width)
		{
			Dictionary<QuestDef, Vector2Int> shape = new Dictionary<QuestDef, Vector2Int>();
			int next = 0;
			layout(root, 0);
			width = next;
			return shape;
			void layout(QuestDef node, int depth)
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_0061: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b6: 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_00c5: Unknown result type (might be due to invalid IL or missing references)
				shape[node] = Vector2Int.zero;
				List<QuestDef> value;
				List<QuestDef> list = (kids.TryGetValue(node, out value) ? value.Where((QuestDef k) => !shape.ContainsKey(k)).ToList() : new List<QuestDef>());
				if (list.Count == 0)
				{
					shape[node] = new Vector2Int(next++, depth);
				}
				else
				{
					foreach (QuestDef item in list)
					{
						layout(item, depth + 1);
					}
					Dictionary<QuestDef, Vector2Int> dictionary = shape;
					Vector2Int val = shape[list[0]];
					dictionary[node] = new Vector2Int(((Vector2Int)(ref val)).x, depth);
				}
			}
		}

		private static Vector2Int FindSpot(Dictionary<QuestDef, Vector2Int> shape, int shapeWidth, HashSet<Vector2Int> blocked, int width, int startY, int preferX)
		{
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			List<int> list = (from x in Enumerable.Range(0, Math.Max(1, width - shapeWidth + 1))
				orderby Math.Abs(x - preferX), x
				select x).ToList();
			int num = startY;
			while (true)
			{
				foreach (int item in list)
				{
					Vector2Int origin = new Vector2Int(item, num);
					if (!shape.Values.Any((Vector2Int cell) => blocked.Contains(origin + cell)))
					{
						return origin;
					}
				}
				num++;
			}
		}

		private static void Place(Dictionary<QuestDef, Vector2Int> shape, Vector2Int origin, HashSet<Vector2Int> taken)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			foreach (KeyValuePair<QuestDef, Vector2Int> item in shape)
			{
				SetPosition(item.Key, origin + item.Value);
				taken.Add(origin + item.Value);
			}
		}

		private static int Order(QuestDef definition)
		{
			if (!_registeredAt.TryGetValue(((BalanceBaseObject)definition).id, out var value))
			{
				return int.MaxValue;
			}
			return value;
		}

		private static HashSet<Vector2Int> BlockedCells(List<QuestDef> quests, Dictionary<string, QuestDef> byId)
		{
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing refe